Basic plugin structure

The following is the basic structure of all Agry plugins; please adhere to best practices to develop plugins that are functional and compliant for publication.

(async () => {
    const EXTERNAL_URL = 'https://jsonplaceholder.typicode.com/todos/1';
    const POST_URL = 'https://jsonplaceholder.typicode.com/posts';

    const setResult = async (message, type = 'info') => {
        await Agry.log(message, type);
        await Agry.dom.setHTML(
            'sdk-test-result',
            `<strong>Status:</strong> ${message}`
        );
    };

    // Interface: Ribbon button and side widget.
    await Agry.addRibbonButton('SDK Test', '🧪', async () => {
        await Agry.toast('SDK test plugin active.', 'success');
    });

    await Agry.addWidget(
        'sdk-test-widget',
        'SDK Diagnostics',
        '🧪',
        '<p>Test plugin loaded successfully.</p><small>Open the diagnostic window from the Ribbon button.</small>'
    );

    // Interface: plugin window.
    await Agry.createWindow(
        'sdk-test-window',
        'Agry SDK Diagnostics',
        `
            <p>This plugin tests capabilities without modifying the logbook, planner, or inventory.</p>

            <div id="sdk-test-result">
                <strong>Status:</strong> ready for tests.
            </div>

            <p>
                <button id="sdk-test-storage">Storage test</button>
                <button id="sdk-test-network">External GET + POST</button>
            </p>

            <p>
                <button id="sdk-test-project">Read Agry fields</button>
                <button id="sdk-test-account">Read account profile</button>
            </p>

            <p>
                <button id="sdk-test-devices">Read devices</button>
                <button id="sdk-test-map">Position + marker</button>
            </p>

            <p>
                <button id="sdk-test-dialog">Test confirmation window</button>
                <button id="sdk-test-download">Download report</button>
            </p>
        `,
        480,
        'auto'
    );

    // Isolated storage for this plugin only.
    const startedAt = new Date().toISOString();
    await Agry.storage.set('last-sdk-test', { startedAt });
    const savedTest = await Agry.storage.get('last-sdk-test');
    await Agry.log(`Initial storage OK: ${savedTest ? 'data saved' : 'data not found'}`, 'success');

    // Window events: no inline onclick, everything passes through the protected API.
    await Agry.events.add('click', async (event) => {
        const targetId = Agry.events.getTargetId(event);

        try {
            if (targetId === 'sdk-test-storage') {
                const keys = await Agry.storage.getAllKeys();
                await setResult(`Storage OK: ${keys.length} keys available.`, 'success');
            }

            if (targetId === 'sdk-test-network') {
                await setResult('External GET request in progress…');

                // On the first run, permission for the domain will be requested.
                const getData = await Agry.httpGet(EXTERNAL_URL);
                await Agry.log(`External GET OK: ${JSON.stringify(getData)}`, 'success');

                await setResult('External POST request in progress…');
                const postData = await Agry.httpPost(POST_URL, {
                    title: 'Agry SDK Test',
                    completed: false,
                    source: 'plugin-studio'
                });

                await Agry.log(`External POST OK: ${JSON.stringify(postData)}`, 'success');
                await setResult('Network OK: GET and POST completed.', 'success');
                await Agry.toast('Network test passed.', 'success');
            }

            if (targetId === 'sdk-test-project') {
                const fields = await Agry.fields.list();
                await Agry.log(`Fields read: ${fields.length}`, 'success');
                await setResult(`Project data OK: found ${fields.length} fields.`, 'success');
            }

            if (targetId === 'sdk-test-account') {
                const profile = await MyMicroeden.account.getProfile();
                await Agry.log('Account profile read successfully.', 'success');

                // Does not show personal data in the UI: only verifies the response.
                await setResult(
                    profile && profile.success !== false
                        ? 'Account OK: profile available.'
                        : 'Profile received, but no data available.',
                    'success'
                );
            }

            if (targetId === 'sdk-test-devices') {
                const devices = await MyMicroeden.devices.list();
                const count = Array.isArray(devices?.data)
                    ? devices.data.length
                    : Array.isArray(devices)
                        ? devices.length
                        : 0;

                await Agry.log(`Devices read: ${count}`, 'success');
                await setResult(`Devices OK: ${count} devices detected.`, 'success');
            }

            if (targetId === 'sdk-test-map') {
                await setResult('Position request in progress…');

                const position = await Agry.map.getUserPosition({
                    centerMap: false,
                    addMarker: false
                });

                await Agry.addMarker(
                    position.lat,
                    position.lng,
                    'Marker created by the SDK test',
                    '🧪',
                    'normal'
                );

                await setResult('Map OK: position read and marker created.', 'success');
                await Agry.toast('Test marker added.', 'success');
            }

            if (targetId === 'sdk-test-dialog') {
                await Agry.ui.confirm(
                    'The Agry windows test works. Confirm?',
                    async () => {
                        await setResult('Confirmation window OK.', 'success');
                        await Agry.toast('Confirmation received.', 'success');
                    }
                );
            }

            if (targetId === 'sdk-test-download') {
                const report = {
                    plugin: 'SDK Test',
                    executedAt: new Date().toISOString(),
                    storage: savedTest ? 'ok' : 'not available'
                };

                await Agry.file.download(
                    JSON.stringify(report, null, 2),
                    'agry-sdk-test-report.json',
                    'application/json'
                );

                await setResult('Download OK: report generated.', 'success');
            }
        } catch (error) {
            await Agry.log(`Test failed: ${error.message}`, 'error');
            await setResult(`Error: ${error.message}`, 'error');
            await Agry.toast('A test requires authorization or has failed.', 'error');
        }
    });

    await Agry.onStop(async () => {
        await Agry.log('Test plugin stopped: plugin resources removed.', 'info');
    });

    await Agry.toast('Diagnostic plugin ready.', 'success');
    await Agry.log('SDK Diagnostics ready. Use the window to test capabilities.', 'success');

    Agry.addWidget('id', 'Title', '📊', '<div>Content</div>');

})();