ข้ามไปยังเนื้อหา

ระบบปลั๊กอิน Ogma ​

ปลั๊กอิน Ogma ขยายความสามารถของเครื่องมือด้วยลอจิกแบ็กเอนด์ที่กำหนดเอง แผง UI ฟรอนต์เอนด์ และขั้นตอนเวิร์กโฟลว์ ปลั๊กอินติดตั้งภายในเครื่องจากไดเรกทอรีบนดิสก์ เปิดใช้งานแยกตามโครงการ และทำงานในสภาพแวดล้อมแซนด์บ็อกซ์

เอกสารนี้เป็นข้อมูลอ้างอิงหลักสำหรับผู้พัฒนาปลั๊กอิน

หากต้องการเริ่มต้นให้เร็วที่สุด ให้ใช้ คู่มือเริ่มต้นใช้งานปลั๊กอินอย่างรวดเร็ว


เริ่มต้นอย่างรวดเร็ว ​

ปลั๊กอินแบ็กเอนด์แบบเรียบง่าย ​

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; ใช้คำสั่ง import ได้เมื่อใช้เครื่องมือรวมบันเดิล):

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) ​

หากคุณเขียนด้วย 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.json อยู่ในไดเรกทอรีรากของแพ็กเกจ ทุกฟิลด์แยกแยะตัวพิมพ์ใหญ่และเล็ก

ฟิลด์ระดับบน ​

ฟิลด์จำเป็นชนิดข้อมูลหมายเหตุ
idใช่สตริงใช้ได้เฉพาะตัวอักษรละตินพิมพ์เล็ก ตัวเลข และยัติภังค์ สูงสุด 64 อักขระ ต้องไม่ซ้ำกับปลั๊กอินอื่นที่ติดตั้งไว้
versionใช่สตริงการกำหนดเวอร์ชันเชิงความหมาย: MAJOR.MINOR.PATCH
nameไม่สตริงชื่อที่แสดงใน UI ค่าเริ่มต้นคือ id
descriptionไม่สตริงสรุปในหนึ่งบรรทัด
authorไม่ออบเจ็กต์{ "name": "...", "email": "...", "url": "..." }
homepageไม่สตริงURL ของที่เก็บซอร์สโค้ดหรือเอกสาร
pluginsใช่อาร์เรย์รายการองค์ประกอบปลั๊กอินหนึ่งรายการขึ้นไป (ดูด้านล่าง)
permissionsไม่อาร์เรย์รายการชื่อสิทธิ์ที่จำเป็น (ดู สิทธิ์)

รายการองค์ประกอบปลั๊กอิน ​

แต่ละออบเจ็กต์ในอาร์เรย์ 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ไม่ไฟล์ CSS ที่โหลดใน iframe ของปลั๊กอิน
assetsไม่ไดเรกทอรีของแอสเซ็ตแบบคงที่ที่ให้บริการภายใต้ /plugins/{id}/assets/
backend.idไม่เชื่อมองค์ประกอบฟรอนต์เอนด์กับองค์ประกอบแบ็กเอนด์ของตนเพื่อเรียก RPC ผ่าน sdk.backend.*
runtimeไม่ (เฉพาะแบ็กเอนด์)"javascript" (ค่าเริ่มต้นและค่าเดียวที่รองรับ)

API ปลั๊กอินแบ็กเอนด์ (sdk) ​

ออบเจ็กต์ sdk ของแบ็กเอนด์ถูกส่งให้ฟังก์ชัน init(sdk) ของคุณ ทุกเมธอดทำงานแบบซิงโครนัส เว้นแต่จะระบุว่าเป็น async

sdk.console ​

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

เขียนลงในบัฟเฟอร์บันทึกของปลั๊กอิน (ดูได้ในแท็บบันทึก) เก็บไว้สูงสุด 500 รายการ แต่ละข้อความถูกตัดทอนที่ 1 KB

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 ​

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 ​

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 เหล่านี้ทำงานแบบซิงโครนัสในรันไทม์ปลั๊กอิน ไม่ใช่โมดูล fs เต็มรูปแบบของ Node.js การเข้าถึงไฟล์ของปลั๊กอินต้องมีสิทธิ์ plugin_storage และอยู่ภายในไดเรกทอรีข้อมูลส่วนตัวของปลั๊กอิน JavaScript ของเวิร์กโฟลว์มีบริบทระบบไฟล์ต่างออกไป ดู การเข้าถึงไฟล์ของเวิร์กโฟลว์

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 ​

ลงทะเบียนคอลแบ็กสำหรับเหตุการณ์ของ 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 ​

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 ในมานิเฟสต์และได้รับอนุมัติจากผู้ใช้ ดู สิทธิ์

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 ​

ลงทะเบียนฟังก์ชัน 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 ปลั๊กอินฟรอนต์เอนด์

sdk.api.send ส่งเหตุการณ์เข้าคิวแยกตามปลั๊กอิน (สูงสุด 200 รายการ) ฟรอนต์เอนด์โพลคิวนี้ผ่าน sdk.backend.onEvent

sdk.replay, sdk.projects, sdk.scope, sdk.workflows, sdk.matchReplace ​

เหล่านี้เป็นเนมสเปซสำหรับค้นข้อมูลแบบอ่านอย่างเดียว ดู เอกสารอ้างอิง SDK แบ็กเอนด์ สำหรับซิกเนเจอร์เมธอดฉบับเต็ม

คลาสคำขอ ​

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) ​

โค้ดปลั๊กอินฟรอนต์เอนด์ทำงานใน iframe ที่อยู่ในแซนด์บ็อกซ์และโหลดจาก /plugins/{id}/ui iframe ใช้ postMessage เพื่อสื่อสารกับโฮสต์ Ogma ซึ่งทำหน้าที่ส่งต่อการเรียกไปยังแบ็กเอนด์

SDK พร้อมใช้งานผ่าน window.ogmaSDK เรียก ogmaSDK.ready(cb) เพื่อรับ SDK ที่พร้อมทำงานเมื่อสร้างบริดจ์โฮสต์แล้ว:

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 ​

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

sdk.meta ​

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

sdk.requests ​

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 ​

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

ต้องมีสิทธิ์ read_findings (ได้รับอนุมัติโดยอัตโนมัติ)

sdk.scope ​

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

sdk.projects ​

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

sdk.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 ​

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

ลงทะเบียนหน้านำทาง ปัจจุบันโฮสต์ตอบรับแล้ว การผสานรวมกับเราเตอร์อย่างเต็มรูปแบบยังอยู่ระหว่างดำเนินการ

sdk.sidebar ​

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

ลงทะเบียนรายการแถบด้านข้าง ปัจจุบันมีผลเฉพาะในแผง UI ของปลั๊กอิน การเชื่อมเข้ากับตำแหน่งแถบด้านข้างส่วนกลางยังอยู่ระหว่างดำเนินการ

sdk.commands ​

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

โฮสต์ตอบรับแล้ว การผสานรวมกับแผงคำสั่งยังอยู่ระหว่างดำเนินการ

sdk.menu ​

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

โฮสต์ตอบรับแล้ว การแทรกรายการในเมนูบริบทยังอยู่ระหว่างดำเนินการ

sdk.window ​

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

แสดงการแจ้งเตือนแบบโทสต์ในแผงปลั๊กอิน ชนิดที่ใช้ได้: info, success, warning, error

sdk.ui ​

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

สิทธิ์ ​

ประกาศสิทธิ์ใน manifest.json:

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

สิทธิ์ที่อนุมัติโดยอัตโนมัติ (ไม่ต้องได้รับการอนุมัติจากผู้ใช้) ​

สิทธิ์เหล่านี้จะได้รับอนุมัติเสมอสำหรับปลั๊กอินที่ติดตั้งไว้ทุกตัว:

สิทธิ์สิ่งที่อนุญาตให้ทำ
read_http_historysdk.requests.get, sdk.requests.search
read_findingssdk.findings.get, sdk.findings.list
read_scopesdk.scope.getActive
read_projectssdk.projects.getCurrent, sdk.projects.list
plugin_storagesdk.storage, sdk.fs, sdk.path

สิทธิ์ที่มีการป้องกัน (ต้องได้รับการอนุมัติจากผู้ใช้) ​

ต้องประกาศสิทธิ์เหล่านี้ในมานิเฟสต์ และผู้ใช้ต้องอนุมัติอย่างชัดเจนจากแท็บสิทธิ์:

สิทธิ์สิ่งที่อนุญาตให้ทำ
send_requestssdk.requests.send - ส่งคำขอ HTTP ขาออก
write_findingssdk.findings.create, sdk.findings.update

ผู้ใช้จะเห็นข้อความขออนุมัติเมื่อเปิดใช้งานปลั๊กอินที่ประกาศสิทธิ์ที่มีการป้องกัน นอกจากนี้ยังอนุมัติหรือเพิกถอนสิทธิ์ได้ทุกเมื่อจากแท็บสิทธิ์


โครงสร้างแพ็กเกจปลั๊กอิน ​

แพ็กเกจปลั๊กอินติดตั้งจากไดเรกทอรีภายในเครื่องได้ และเมื่อใช้งานผ่านเบราว์เซอร์ก็ติดตั้งจากไฟล์ส่งออก .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.)

ข้อกำหนดสคริปต์แบ็กเอนด์ ​

  • ต้องเป็นไฟล์ JS เดียวที่ทำงานได้ด้วยตนเอง
  • ไม่รองรับ require() และ import() แบบไดนามิก
  • ต้องส่งออกฟังก์ชัน init(sdk) (หรือกำหนดให้เป็นฟังก์ชันโกลบอล)
  • ชุดย่อยของ ES2020 ที่ QuickJS รองรับ: async/await, Promise, Map, Set, Symbol, Proxy, Date, RegExp, JSON ไม่มี fetch และไม่มี Buffer
  • import แบบคงที่ที่รองรับจะถูกแก้ไขการอ้างอิงระหว่างการประมวลผลปลั๊กอินล่วงหน้า: @ogma/sdk, crypto, fs, path
  • ขนาดไฟล์สูงสุด: 256 KB

ข้อกำหนดสคริปต์ฟรอนต์เอนด์ ​

  • ทำงานภายใน iframe ที่อยู่ในแซนด์บ็อกซ์ อนุญาต connect-src: 'self' เพื่อให้ปลั๊กอินส่ง POST ไปยัง /plugins/{id}/api/* และโพล /plugins/{id}/events/poll ได้
  • CSP: default-src 'none'; script-src 'nonce-...'; style-src 'self'; img-src data: blob: 'self'; connect-src 'self'
  • ไม่มี allow-same-origin ในแซนด์บ็อกซ์ iframe ปลั๊กอินจึงเข้าถึง DOM ของหน้าหลักของ Ogma หรือคุกกี้ไม่ได้
  • ใช้ ogmaSDK.ready(cb) เพื่อเข้าถึง SDK อย่าเรียกเมธอด SDK ก่อนคอลแบ็กทำงาน

การสร้างปลั๊กอินสำหรับ 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 อยู่ที่ราก


การติดตั้งปลั๊กอิน ​

  1. เปิด ปลั๊กอิน ในแถบด้านข้างซ้าย
  2. คลิก ติดตั้ง (ด้านบนของแท็บ ติดตั้งแล้ว)
  3. ในแอปเดสก์ท็อป คลิก เรียกดู เพื่อเปิดตัวเลือกโฟลเดอร์ของระบบ ในเบราว์เซอร์ ให้พิมพ์พาธเต็มฝั่งเซิร์ฟเวอร์ของไดเรกทอรีปลั๊กอิน
  4. คลิก ตรวจสอบ เพื่อตรวจสอบมานิเฟสต์และรายการไฟล์
  5. คลิก ติดตั้ง หากการตรวจสอบผ่าน
  6. เลือกปลั๊กอินในรายการแล้วคลิก เปิดใช้งาน
  7. หากปลั๊กอินประกาศสิทธิ์ที่มีการป้องกัน ให้ตรวจสอบและอนุมัติจากแท็บ สิทธิ์ ก่อนเปิดใช้งาน

การแก้ไขปัญหา ​

ปลั๊กอินเริ่มทำงานไม่สำเร็จโดยไม่มีข้อความผิดพลาด: ตรวจสอบแท็บ บันทึก สาเหตุที่พบบ่อยที่สุด:

  • เรียก sdk.meta.path() แล้วแต่สร้างไดเรกทอรีข้อมูลไม่ได้
  • ข้อยกเว้นที่ไม่ได้จัดการใน init()
  • การเรียก sdk.* ขาดหายไปหรือสะกดผิด

ฟรอนต์เอนด์แสดงหน้าว่าง: ตรวจสอบคอนโซลเบราว์เซอร์ว่ามีการละเมิด CSP หรือไม่ ตรวจสอบให้แน่ใจว่าสคริปต์ฟรอนต์เอนด์เรียก ogmaSDK.ready(cb) ก่อนเข้าถึงเมธอด SDK ใด ๆ

sdk.requests.send แจ้งข้อผิดพลาด Permission denied: ต้องประกาศสิทธิ์ send_requests ในมานิเฟสต์ และผู้ใช้ต้องอนุมัติจากแท็บสิทธิ์ด้วย

เรียกฟังก์ชัน sdk.api.register จากฟรอนต์เอนด์ไม่ได้: ต้องเปิดใช้งานแบ็กเอนด์ (ไม่ใช่เพียงติดตั้งไว้) ชื่อฟังก์ชันต้องตรงกับชื่อที่ฟรอนต์เอนด์ส่งให้ sdk.backend.call ทุกตัวอักษร (แยกแยะตัวพิมพ์ใหญ่และเล็ก)

คำเตือนความเข้ากันได้แสดงเป็นข้อผิดพลาด: คำเตือนเหล่านี้ไม่ขัดขวางการทำงาน แต่บ่งชี้ส่วนที่ API ยังรองรับไม่ครบ ดูตารางการแมป SDK ข้างต้นสำหรับรายละเอียดความครอบคลุมของ API

ซอฟต์แวร์กรรมสิทธิ์ สงวนลิขสิทธิ์