---
url: https://docs.ogmabox.com/hi/plugins/README.md
description: >-
  बैकएंड लॉजिक, फ्रंटएंड पैनल, कमांड, अनुमतियों और मार्केटप्लेस मेटाडेटा के साथ
  Ogma प्लगइन बनाएँ, पैकेज करें, इंस्टॉल करें, चालू करें और वितरित करें।
---

# Ogma प्लगइन प्रणाली {#ogma-plugin-system}

Ogma प्लगइन कस्टम बैकएंड लॉजिक, फ्रंटएंड UI पैनल और कार्यप्रवाह के चरण जोड़कर टूल का विस्तार करते हैं। प्लगइन डिस्क की डायरेक्टरी से स्थानीय रूप से इंस्टॉल होते हैं, हर प्रोजेक्ट के लिए अलग से चालू किए जाते हैं और सैंडबॉक्स परिवेश में चलते हैं।

यह दस्तावेज़ प्लगइन डेवलपर के लिए मुख्य संदर्भ है।

सबसे तेज़ शुरुआत के लिए [प्लगइन के लिए त्वरित शुरुआत](/hi/plugins/quickstart) का उपयोग करें।

***

## त्वरित शुरुआत {#quick-start}

### न्यूनतम बैकएंड प्लगइन {#minimal-backend-plugin}

```
my-plugin/
  manifest.json
  backend/script.js
```

`manifest.json`:

```json
{
  "id": "my-plugin",
  "name": "My Plugin",
  "version": "1.0.0",
  "plugins": [
    {
      "kind": "backend",
      "id": "my-plugin-backend",
      "entrypoint": "backend/script.js"
    }
  ]
}
```

`backend/script.js` (ES2020; बंडलर का उपयोग करते समय इम्पोर्ट का उपयोग किया जा सकता है):

```js
async function init(sdk) {
  sdk.console.log("my-plugin started");

  sdk.events.onInterceptResponse(function(req, res) {
    if (res.getCode() === 403) {
      sdk.console.warn("403 on " + req.getUrl());
    }
  });
}
```

केवल बैकएंड वाला कार्यशील प्लगइन बनाने के लिए इतना पर्याप्त है। इसे `backend/script.js` में एक ही फ़ाइल के रूप में रखें और `manifest.json` को सीधे उस फ़ाइल की ओर इंगित करें।

### एक मिनट की बिल्ड प्रक्रिया (TypeScript स्रोत) {#one-minute-build-flow-typescript-source}

यदि आप TypeScript में लिख रहे हैं, तो इस संरचना का उपयोग करें:

```text
my-plugin/
  manifest.json
  backend/
    src/index.ts
```

बिल्ड:

```bash
pnpm add -D @ogmabox/ogma-sdk esbuild typescript
pnpm exec esbuild backend/src/index.ts --bundle --format=iife --platform=neutral --external:@ogma/sdk --external:@ogmabox/ogma-sdk --outfile=backend/script.js
```

इंस्टॉलेशन: **प्लगइन > इंस्टॉल करें**, `my-plugin/` डायरेक्टरी चुनें। फिर प्लगइन चालू करें।

***

## मैनिफ़ेस्ट संदर्भ {#manifest-reference}

`manifest.json` पैकेज की रूट डायरेक्टरी में रहता है। सभी फ़ील्ड में बड़े और छोटे अक्षरों का अंतर माना जाता है।

### शीर्ष-स्तरीय फ़ील्ड {#top-level-fields}

| फ़ील्ड | आवश्यक | प्रकार | टिप्पणियाँ |
|-------|----------|------|-------|
| `id` | हाँ | स्ट्रिंग | केवल छोटे लैटिन अक्षर, अंक और हाइफ़न। अधिकतम 64 वर्ण। इंस्टॉल किए गए प्लगइन में अद्वितीय। |
| `version` | हाँ | स्ट्रिंग | सिमैंटिक वर्ज़निंग: `MAJOR.MINOR.PATCH` |
| `name` | नहीं | स्ट्रिंग | UI में दिखाया जाने वाला नाम। डिफ़ॉल्ट `id` है। |
| `description` | नहीं | स्ट्रिंग | एक पंक्ति का सारांश। |
| `author` | नहीं | ऑब्जेक्ट | `{ "name": "...", "email": "...", "url": "..." }` |
| `homepage` | नहीं | स्ट्रिंग | स्रोत रिपॉज़िटरी या दस्तावेज़ का URL। |
| `plugins` | हाँ | ऐरे | प्लगइन घटक की एक या अधिक एंट्री (नीचे देखें)। |
| `permissions` | नहीं | ऐरे | आवश्यक अनुमति नामों की सूची ([अनुमतियाँ](#permissions) देखें)। |

### प्लगइन घटक की एंट्री {#plugin-component-entry}

`plugins` ऐरे का हर ऑब्जेक्ट एक घटक का विवरण देता है।

**बैकएंड घटक:**

```json
{
  "kind": "backend",
  "id": "my-plugin-backend",
  "entrypoint": "backend/script.js",
  "runtime": "javascript",
  "assets": "backend/assets"
}
```

**फ्रंटएंड घटक:**

```json
{
  "kind": "frontend",
  "id": "my-plugin-frontend",
  "entrypoint": "frontend/script.js",
  "style": "frontend/style.css",
  "assets": "frontend/assets",
  "backend": { "id": "my-plugin-backend" }
}
```

| फ़ील्ड | आवश्यक | टिप्पणियाँ |
|-------|----------|-------|
| `kind` | हाँ | `"backend"` या `"frontend"` |
| `id` | हाँ | मैनिफ़ेस्ट में अद्वितीय। छोटे अक्षर और हाइफ़न। |
| `entrypoint` | हाँ | JS प्रवेश फ़ाइल का सापेक्ष पथ। |
| `style` | नहीं | प्लगइन iframe में लोड होने वाली CSS फ़ाइल। |
| `assets` | नहीं | `/plugins/{id}/assets/` के अंतर्गत उपलब्ध कराए जाने वाले स्थिर एसेट की डायरेक्टरी। |
| `backend.id` | नहीं | `sdk.backend.*` RPC के लिए फ्रंटएंड घटक को उसके बैकएंड घटक से जोड़ता है। |
| `runtime` | नहीं (केवल बैकएंड) | `"javascript"` (डिफ़ॉल्ट और एकमात्र समर्थित मान)। |

***

## बैकएंड प्लगइन API (sdk) {#backend-plugin-api-sdk}

बैकएंड `sdk` ऑब्जेक्ट आपके `init(sdk)` फ़ंक्शन को दिया जाता है। सभी मेथड सिंक्रोनस हैं, जब तक उन्हें `async` के रूप में चिह्नित न किया गया हो।

### `sdk.console` {#sdk-console}

```js
sdk.console.log("message")
sdk.console.warn("message")
sdk.console.error("message")
```

प्लगइन के लॉग बफ़र में लिखता है (लॉग टैब में दिखाई देता है)। अधिकतम 500 एंट्री रखी जाती हैं। हर संदेश 1 KB पर काट दिया जाता है।

### `sdk.meta` {#sdk-meta}

```js
sdk.meta.id()          // > string: plugin id (e.g. "my-plugin")
sdk.meta.packageId()   // > string: same as id
sdk.meta.version()     // > string: semver (e.g. "1.0.0")
sdk.meta.path()        // > string: writable data directory for this plugin
```

`sdk.meta.path()` प्लगइन की लिखने योग्य निजी डेटा डायरेक्टरी की ओर इंगित करता है, जैसे `~/.local/share/ogma/plugins/my-plugin/data`। डायरेक्टरी अपने-आप बनती है और रीस्टार्ट के बाद भी प्लगइन डेटा बनाए रखने के लिए इसका उपयोग किया जा सकता है।

स्टेट और फ़ाइल सहायक उपकरणों के लिए `sdk.storage`, `sdk.path` और `sdk.fs` भी उपलब्ध हैं।

### `sdk.storage` {#sdk-storage}

```js
sdk.storage.get("key")      // > string | null
sdk.storage.set("key", "value")
sdk.storage.delete("key")
sdk.storage.clear()
sdk.storage.keys()          // > string[]
```

`sdk.storage` का दायरा प्लगइन ID तक सीमित है और इसका डेटा प्लगइन के रीस्टार्ट के बाद भी बना रहता है।

### `sdk.fs` {#sdk-fs}

```js
sdk.fs.readFile("relative/file.txt")      // > string
sdk.fs.writeFile("relative/file.txt", "text")
sdk.fs.appendFile("relative/file.txt", "more")
sdk.fs.exists("relative/file.txt")        // > boolean
sdk.fs.existsSync("relative/file.txt")    // > boolean
sdk.fs.list("relative/dir")              // > string[]
sdk.fs.mkdir("relative/dir")
```

`sdk.fs` केवल `sdk.meta.path()` के अंतर्गत आने वाली फ़ाइलों तक सीमित है।

`read` और `write`, संगतता के लिए `readFile` और `writeFile` के वैकल्पिक नाम बने रहते हैं। ऐसी फ़ाइल बनाने से पहले `exists` या `existsSync` का उपयोग करें जिसे आप ओवरराइट नहीं करना चाहते। ये API प्लगइन रनटाइम में सिंक्रोनस रूप से काम करते हैं; ये Node.js का पूरा `fs` मॉड्यूल नहीं हैं। प्लगइन की फ़ाइल पहुँच के लिए `plugin_storage` अनुमति आवश्यक है और यह प्लगइन की निजी डेटा डायरेक्टरी के भीतर रहती है। कार्यप्रवाह JavaScript का फ़ाइल सिस्टम संदर्भ अलग है; [कार्यप्रवाह की फ़ाइल पहुँच](../app/workflows.md#javascript-and-files) देखें।

### `sdk.path` {#sdk-path}

```js
sdk.path.join("a", "b", "c")
sdk.path.basename("/tmp/file.txt")
sdk.path.dirname("/tmp/file.txt")
sdk.path.extname("file.txt")
sdk.path.resolve("/a", "b")
sdk.path.isAbsolute("/tmp/file.txt")
sdk.path.sep
```

### `sdk.events` {#sdk-events}

Ogma इवेंट के लिए कॉलबैक रजिस्टर करें। सभी कॉलबैक QuickJS सैंडबॉक्स के भीतर सिंक्रोनस रूप से कॉल किए जाते हैं।

```js
sdk.events.onInterceptRequest(function(req) {
  // req: RequestSpecRaw
  // Return a modified RequestSpecRaw to mutate the request.
  // Return null/undefined to pass through unchanged.
});

sdk.events.onInterceptResponse(function(req, res) {
  // req: Request (read-only), res: Response (read-only)
  // Return value is ignored.
});

sdk.events.onProjectChange(function() {
  // no callback args
});

sdk.events.onFindingCreated(function(finding) {
  // finding: { id, title, reporter }
});
```

### `sdk.requests` {#sdk-requests}

```js
// Get a single HTTP entry by id
var entry = sdk.requests.get("entry-id");
// entry: { id, method, host, path, query, tls, ... } or null

// Search HTTP history
var results = sdk.requests.search({ limit: 20, offset: 0 });
// results: { entries: [...], total: N }

// Send an HTTP request (requires send_requests permission)
var response = await sdk.requests.send(spec);
// spec: RequestSpecRaw (see below)
// response: Response
```

`sdk.requests.send` के लिए मैनिफ़ेस्ट में `send_requests` अनुमति घोषित होना और उपयोगकर्ता द्वारा उसका प्रदान किया जाना आवश्यक है। [अनुमतियाँ](#permissions) देखें।

### `sdk.findings` {#sdk-findings}

```js
// Create a finding (requires write_findings permission)
sdk.findings.create({
  title: "SSRF via redirect",
  reporter: "my-plugin",
  dedupeKey: "ssrf-" + request.getId(),
  request: { id: request.getId() }
});

// Check if a finding already exists (dedup check)
var exists = sdk.findings.exists({ dedupeKey: "ssrf-abc" });

// List findings
var page = sdk.findings.list({ limit: 20, offset: 0 });

// Get a single finding
var finding = sdk.findings.get("finding-id");
```

`sdk.findings.create` की दर सीमाएँ: प्रति मिनट 10, प्रति प्लगइन सत्र 500, प्रति इवेंट कॉलबैक 3।

### `sdk.api` {#sdk-api}

ऐसे बैकएंड RPC फ़ंक्शन रजिस्टर करें जिन्हें फ्रंटएंड `sdk.backend.*` के ज़रिए कॉल कर सके:

```js
// In backend init:
sdk.api.register("getScans", function(scanId) {
  return { scans: [] };
});

// Emit an event to connected frontends:
sdk.api.send("scan:complete", { scanId: 1, status: "ok" });
```

हैंडलर को फ्रंटएंड से दिए गए आर्ग्युमेंट मिलते हैं (कोई अतिरिक्त `sdk` आर्ग्युमेंट इंजेक्ट नहीं होता)। लौटाए गए मान JSON में सीरियलाइज़ करके कॉल करने वाले को वापस भेजे जाते हैं।

फ्रंटएंड इन्हें `sdk.backend.getScans(scanId)` के ज़रिए कॉल करता है - [फ्रंटएंड प्लगइन API](#frontend-plugin-api-sdk) देखें।

`sdk.api.send` हर प्लगइन की अपनी कतार में इवेंट भेजता है (अधिकतम 200 एंट्री)। फ्रंटएंड इस कतार को `sdk.backend.onEvent` के ज़रिए पोल करते हैं।

### `sdk.replay`, `sdk.projects`, `sdk.scope`, `sdk.workflows`, `sdk.matchReplace` {#sdk-replay-sdk-projects-sdk-scope-sdk-workflows-sdk-matchreplace}

ये केवल पढ़ने वाली क्वेरी के नेमस्पेस हैं। पूरे मेथड सिग्नेचर के लिए [बैकएंड SDK संदर्भ](./backend-sdk.md) देखें।

### अनुरोध क्लास {#request-classes}

**`RequestSpecRaw`** - इंटरसेप्ट किए गए अनुरोध का प्रतिनिधित्व करता है। यह आपको `onInterceptRequest` में मिलता है।

```js
spec.getMethod()          // > string
spec.setMethod("POST")
spec.getHost()            // > string
spec.setHost("example.com")
spec.getPort()            // > number
spec.getPath()            // > string
spec.setPath("/new/path")
spec.getQuery()           // > string
spec.getTls()             // > boolean
spec.getHeaders()         // > Record<string, string[]>
spec.setHeader("X-Foo", "bar")
spec.getBody()            // > Body | null
spec.setBody("new body")
spec.getRaw()             // > Uint8Array (raw bytes) or []
spec.setRaw(bytes)        // set raw bytes

// Create a new spec from a URL string:
var spec = new RequestSpecRaw("https://example.com/path?q=1");
```

**`Request`** - कैप्चर किया गया केवल पढ़ने योग्य अनुरोध (`sdk.requests.get` से)।

```js
req.getId()
req.getMethod()
req.getHost()
req.getPort()
req.getTls()
req.getPath()
req.getQuery()
req.getUrl()           // > full URL string
req.getHeaders()       // > Record<string, string>
req.getHeader("name")
req.getBody()          // > Body | null
req.getCreatedAt()     // > Date
req.toSpec()           // > RequestSpec (mutable copy)
```

**`Response`** - कैप्चर की गई केवल पढ़ने योग्य प्रतिक्रिया।

```js
res.getCode()            // > number (HTTP status)
res.getHeaders()         // > Record<string, string>
res.getHeader("name")
res.getBody()            // > Body | null
res.getRoundtripTime()   // > number (ms)
res.getCreatedAt()       // > Date
```

**`Body`**:

```js
body.toText()    // > string
body.toJson()    // > parsed object or null
body.toRaw()     // > Uint8Array
body.length      // > number (original size, may differ from toText() if truncated)
```

***

## फ्रंटएंड प्लगइन API (sdk) {#frontend-plugin-api-sdk}

फ्रंटएंड प्लगइन कोड `/plugins/{id}/ui` से लोड किए गए सैंडबॉक्स iframe में चलता है। iframe, `postMessage` का उपयोग करके Ogma होस्ट से संवाद करता है, जो कॉल को बैकएंड तक पहुँचाता है।

SDK, `window.ogmaSDK` के ज़रिए उपलब्ध है। होस्ट ब्रिज स्थापित होने के बाद तैयार SDK प्राप्त करने के लिए `ogmaSDK.ready(cb)` कॉल करें:

```js
ogmaSDK.ready(function(sdk) {
  // sdk is the live SDK - safe to call any method here
  sdk.log.info("frontend ready");
});
```

सभी SDK मेथड Promise लौटाते हैं।

### `sdk.log` {#sdk-log}

```js
sdk.log.info("message")
sdk.log.warn("message")
sdk.log.error("message")
```

### `sdk.meta` {#sdk-meta-1}

```js
var meta = await sdk.meta.get();
// { pluginId, packageId, name, version, ogmaVersion }
```

### `sdk.requests` {#sdk-requests-1}

```js
var entry = await sdk.requests.get({ id: "entry-id" });
var result = await sdk.requests.search({ limit: 20, offset: 0, query: "host:example.com" });
```

`read_http_history` अनुमति आवश्यक है (अपने-आप प्रदान की जाती है; उपयोगकर्ता की स्वीकृति आवश्यक नहीं)।

### `sdk.findings` {#sdk-findings-1}

```js
var page = await sdk.findings.list({ limit: 20, offset: 0 });
```

`read_findings` अनुमति आवश्यक है (अपने-आप प्रदान की जाती है)।

### `sdk.scope` {#sdk-scope}

```js
var scope = await sdk.scope.getActive();
```

### `sdk.projects` {#sdk-projects}

```js
var project = await sdk.projects.getCurrent();
```

### `sdk.backend` - बैकएंड RPC {#sdk-backend-backend-rpc}

बैकएंड पर `sdk.api.register` से रजिस्टर किए गए फ़ंक्शन कॉल करें:

```js
// Call a named backend function
var result = await sdk.backend.call("getScans", [scanId]);

// Poll for backend-emitted events (sdk.api.send on the backend side)
var { events, next_since } = await sdk.backend.poll(since);
// events: [{ event: "scan:complete", args: [...] }]

// Register an event listener (uses polling internally)
sdk.backend.onEvent("scan:complete", function(data) {
  console.log("scan done", data);
});
```

`sdk.backend.onEvent` अंदरूनी रूप से 2 सेकंड की पोलिंग लूप का उपयोग करता है। लौटाए गए अनसब्सक्राइब फ़ंक्शन को कॉल करके सुनना बंद करें:

```js
var unsub = sdk.backend.onEvent("scan:complete", handler);
// later:
unsub();
```

### `sdk.navigation` {#sdk-navigation}

```js
await sdk.navigation.addPage("/my-plugin", { title: "My Plugin" });
```

नेविगेशन पेज रजिस्टर करता है। अभी होस्ट इसकी स्वीकृति देता है। पूरे राउटर एकीकरण पर काम चल रहा है।

### `sdk.sidebar` {#sdk-sidebar}

```js
await sdk.sidebar.registerItem("My Plugin", "/my-plugin", { icon: "puzzle" });
```

साइडबार एंट्री रजिस्टर करता है। अभी यह प्लगइन UI पैनल तक सीमित है - ग्लोबल साइडबार स्लॉट से जोड़ने पर काम चल रहा है।

### `sdk.commands` {#sdk-commands}

```js
await sdk.commands.register("my-plugin:scan", {
  name: "Scan with My Plugin",
  handler: function(context) { /* ... */ }
});
```

होस्ट इसकी स्वीकृति देता है। कमांड पैलेट एकीकरण पर काम चल रहा है।

### `sdk.menu` {#sdk-menu}

```js
await sdk.menu.registerItem({
  type: "Request",
  commandId: "my-plugin:scan",
  leadingIcon: "shield"
});
```

होस्ट इसकी स्वीकृति देता है। संदर्भ मेनू में जोड़ने पर काम चल रहा है।

### `sdk.window` {#sdk-window}

```js
sdk.window.showToast("Scan complete", { variant: "success", duration: 3000 });
```

प्लगइन पैनल में टोस्ट सूचना दिखाता है। प्रकार: `info`, `success`, `warning`, `error`।

### `sdk.ui` {#sdk-ui}

```js
sdk.ui.resize(600);                              // request iframe height change
sdk.ui.sidebar.registerItem("name", "/path");    // alias for sdk.sidebar.registerItem
```

***

## अनुमतियाँ {#permissions}

`manifest.json` में अनुमतियाँ घोषित करें:

```json
{
  "permissions": ["send_requests", "write_findings"]
}
```

### अपने-आप मिलने वाली अनुमतियाँ (उपयोगकर्ता की स्वीकृति आवश्यक नहीं) {#auto-granted-permissions-no-user-approval-needed}

ये अनुमतियाँ हर इंस्टॉल किए गए प्लगइन को हमेशा प्रदान की जाती हैं:

| अनुमति | क्या करने देती है |
|------------|----------------|
| `read_http_history` | `sdk.requests.get`, `sdk.requests.search` |
| `read_findings` | `sdk.findings.get`, `sdk.findings.list` |
| `read_scope` | `sdk.scope.getActive` |
| `read_projects` | `sdk.projects.getCurrent`, `sdk.projects.list` |
| `plugin_storage` | `sdk.storage`, `sdk.fs`, `sdk.path` |

### संरक्षित अनुमतियाँ (उपयोगकर्ता की स्वीकृति आवश्यक) {#protected-permissions-require-user-approval}

इन्हें मैनिफ़ेस्ट में घोषित करना और उपयोगकर्ता द्वारा अनुमतियाँ टैब से स्पष्ट रूप से प्रदान करना आवश्यक है:

| अनुमति | क्या करने देती है |
|------------|----------------|
| `send_requests` | `sdk.requests.send` - बाहरी HTTP अनुरोध भेजना |
| `write_findings` | `sdk.findings.create`, `sdk.findings.update` |

संरक्षित अनुमतियाँ घोषित करने वाले प्लगइन को चालू करते समय उपयोगकर्ता को अनुमति माँगने वाला संदेश दिखता है। वे अनुमतियाँ टैब से कभी भी अनुमतियाँ दे या वापस ले सकते हैं।

***

## प्लगइन पैकेज संरचना {#plugin-package-structure}

प्लगइन पैकेज स्थानीय डायरेक्टरी से इंस्टॉल किया जा सकता है और ब्राउज़र के ज़रिए काम करते समय `.zip` निर्यात से भी इंस्टॉल किया जा सकता है।

```
my-plugin/
  manifest.json           - required
  backend/
    script.js             - bundled backend JS (ES2020)
  frontend/
    script.js             - bundled frontend JS
    style.css             - optional CSS
    assets/               - static assets (images, fonts, etc.)
```

### बैकएंड स्क्रिप्ट की आवश्यकताएँ {#backend-script-requirements}

* एक ही स्व-सम्पूर्ण JS फ़ाइल होनी चाहिए।
* `require()` और डायनेमिक `import()` समर्थित नहीं हैं।
* `init(sdk)` फ़ंक्शन एक्सपोर्ट करना आवश्यक है (या उसे ग्लोबल के रूप में परिभाषित करें)।
* QuickJS द्वारा समर्थित ES2020 उपसमुच्चय: `async/await`, `Promise`, `Map`, `Set`, `Symbol`, `Proxy`, `Date`, `RegExp`, `JSON`। `fetch` नहीं, `Buffer` नहीं।
* समर्थित स्टैटिक इम्पोर्ट प्लगइन की प्रीप्रोसेसिंग में हल किए जाते हैं: `@ogma/sdk`, `crypto`, `fs`, `path`।
* अधिकतम फ़ाइल आकार: 256 KB।

### फ्रंटएंड स्क्रिप्ट की आवश्यकताएँ {#frontend-script-requirements}

* सैंडबॉक्स iframe के भीतर चलता है। `connect-src: 'self'` की अनुमति है ताकि प्लगइन `/plugins/{id}/api/*` पर POST कर सके और `/plugins/{id}/events/poll` को पोल कर सके।
* CSP: `default-src 'none'; script-src 'nonce-...'; style-src 'self'; img-src data: blob: 'self'; connect-src 'self'`।
* iframe सैंडबॉक्स में `allow-same-origin` नहीं है - प्लगइन Ogma के पैरेंट DOM या कुकी तक नहीं पहुँच सकता।
* SDK तक पहुँचने के लिए `ogmaSDK.ready(cb)` का उपयोग करें; कॉलबैक चलने से पहले SDK मेथड कॉल न करें।

***

## Ogma के लिए प्लगइन बनाना {#building-a-plugin-for-ogma}

बैकएंड एक ही बंडल की गई JS फ़ाइल होना चाहिए, इसलिए इंस्टॉल करने से पहले अपने TypeScript/ES मॉड्यूल स्रोत को बंडल करना आवश्यक है।

अनुशंसित टूलचेन:

```bash
# Install dependencies
pnpm install

# Bundle backend (outputs a single CJS/IIFE file):
esbuild packages/backend/src/index.ts \
  --bundle \
  --platform=neutral \
  --format=iife \
  --global-name=_plugin \
  --outfile=dist/backend/script.js \
  --external:@ogma/sdk --external:@ogmabox/ogma-sdk

# Bundle frontend:
vite build packages/frontend --outDir ../../dist/frontend
```

यदि आप Caido डेवलपमेंट टूलचेन (`@caido-community/dev`) का उपयोग कर रहे हैं, तो `caido-dev build` चलाएँ और फिर आउटपुट को Ogma-संगत संरचना में कॉपी करें, जिसमें रूट पर `manifest.json` हो।

***

## प्लगइन इंस्टॉल करना {#installing-a-plugin}

1. बाएँ साइडबार में **प्लगइन** खोलें।
2. **इंस्टॉल करें** पर क्लिक करें (**इंस्टॉल किए गए** टैब के ऊपर)।
3. डेस्कटॉप ऐप में, नेटिव फ़ोल्डर चयनकर्ता खोलने के लिए **ब्राउज़ करें** पर क्लिक करें। ब्राउज़र में, प्लगइन डायरेक्टरी का सर्वर पर मौजूद पूरा पथ लिखें।
4. मैनिफ़ेस्ट और फ़ाइल सूची जाँचने के लिए **मान्यता जाँचें** पर क्लिक करें।
5. सत्यापन सफल होने पर **इंस्टॉल करें** पर क्लिक करें।
6. सूची में प्लगइन चुनें और **चालू करें** पर क्लिक करें।
7. यदि प्लगइन संरक्षित अनुमतियाँ घोषित करता है, तो उसे चालू करने से पहले **अनुमतियाँ** टैब से उनकी समीक्षा करके उन्हें प्रदान करें।

***

## समस्या निवारण {#troubleshooting}

**प्लगइन का आरंभ बिना त्रुटि संदेश के विफल होता है:** **लॉग** टैब जाँचें। सबसे आम कारण:

* `sdk.meta.path()` कॉल किया गया लेकिन डेटा डायरेक्टरी नहीं बन सकी।
* `init()` में हैंडल नहीं किया गया अपवाद।
* `sdk.*` कॉल मौजूद नहीं है या गलत लिखा गया है।

**फ्रंटएंड खाली दिखता है:** CSP उल्लंघन के लिए ब्राउज़र कंसोल जाँचें। सुनिश्चित करें कि आपकी फ्रंटएंड स्क्रिप्ट किसी भी SDK मेथड का उपयोग करने से पहले `ogmaSDK.ready(cb)` कॉल करती है।

**`sdk.requests.send`, `Permission denied` त्रुटि देता है:** `send_requests` अनुमति मैनिफ़ेस्ट में घोषित होना और उपयोगकर्ता द्वारा अनुमतियाँ टैब में प्रदान किया जाना, दोनों आवश्यक हैं।

**फ्रंटएंड से `sdk.api.register` फ़ंक्शन कॉल नहीं हो रहे:** बैकएंड चालू होना चाहिए (केवल इंस्टॉल किया गया नहीं)। फ़ंक्शन नाम ठीक उसी से मेल खाना चाहिए जो फ्रंटएंड `sdk.backend.call` को देता है (बड़े और छोटे अक्षरों का अंतर माना जाता है)।

**संगतता चेतावनियाँ त्रुटि के रूप में दिखती हैं:** ये संचालन नहीं रोकतीं, लेकिन API कवरेज की कमियाँ दर्शाती हैं। API कवरेज के लिए ऊपर दी गई SDK मैपिंग टेबल देखें।
