{"_id":"@buzzni/saycode-extension-sdk","_rev":"4-cc6a7f113e5ef70e158c89c85f20732e","name":"@buzzni/saycode-extension-sdk","dist-tags":{"latest":"0.4.1"},"versions":{"0.1.0":{"name":"@buzzni/saycode-extension-sdk","version":"0.1.0","keywords":["saycode","extension","sdk","desktop"],"license":"MIT","_id":"@buzzni/saycode-extension-sdk@0.1.0","maintainers":[{"name":"namsangboy","email":"namsangboy@gmail.com"}],"bin":{"saycode-extension":"dist/cli.js"},"dist":{"shasum":"e5c939ba5d20bd11c6869ecc31ad0d28ae8cca13","tarball":"https://registry.npmjs.org/@buzzni/saycode-extension-sdk/-/saycode-extension-sdk-0.1.0.tgz","fileCount":9,"integrity":"sha512-X54QJdp43Fe3sJ66wwyJ6yYdIkIivPLxJrIV8j6Nlw7qGsRx4ZxrdUaP6Lu26qWlZO0LC8MxIqEm4jJodRTqEA==","signatures":[{"sig":"MEUCIA37XO80uhX0kt6xNpoSp1llmpCfYipOnJ8tsjbqfs2FAiEAqtEjPykGfl2DjgpngniJE0FhXxftuznJFNBCRRWAm+Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27024},"type":"module","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js"}},"gitHead":"4386c011286374ae9629085abd48fd586d8c7ec2","scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"namsangboy","email":"namsangboy@gmail.com"},"_npmVersion":"10.9.8","description":"Contracts and CLI for building Saycode Desktop extensions.","directories":{},"_nodeVersion":"22.23.2","dependencies":{"jszip":"^3.10.1","esbuild":"^0.28.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/saycode-extension-sdk_0.1.0_1787134382030_0.7631381247785305","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@buzzni/saycode-extension-sdk","version":"0.3.0","keywords":["saycode","extension","sdk","desktop"],"license":"MIT","_id":"@buzzni/saycode-extension-sdk@0.3.0","maintainers":[{"name":"namsangboy","email":"namsangboy@gmail.com"}],"bin":{"saycode-extension":"dist/cli.js"},"dist":{"shasum":"0b9744a17a86deddc54a20a29330a174e43f2981","tarball":"https://registry.npmjs.org/@buzzni/saycode-extension-sdk/-/saycode-extension-sdk-0.3.0.tgz","fileCount":9,"integrity":"sha512-/PwMsCMTmcKbHUArfZBzybgEgtB9YiJxHW5AzXqgXpOqMvDcaTR0MXkW4XiSlftrt03o2ShSHMkLXZ35RpQE5g==","signatures":[{"sig":"MEUCIQDR5pLLYLWznqkE+oaCNmocOSH3YDu/p3pPDUjQLJLrbwIgAWFBAZhCdkj38/JckO75xKJhSH5W1Ho3q74jGeLKPnQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":35165},"type":"module","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js"}},"gitHead":"e60f29320ee33a03975ce4c96ee3393286bb2552","scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"namsangboy","email":"namsangboy@gmail.com"},"_npmVersion":"10.9.8","description":"Contracts and CLI for building Saycode Desktop extensions.","directories":{},"_nodeVersion":"22.23.2","dependencies":{"jszip":"^3.10.1","esbuild":"^0.28.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/saycode-extension-sdk_0.3.0_1787981802078_0.3824036888966651","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@buzzni/saycode-extension-sdk","version":"0.4.0","keywords":["saycode","extension","sdk","desktop"],"license":"MIT","_id":"@buzzni/saycode-extension-sdk@0.4.0","maintainers":[{"name":"namsangboy","email":"namsangboy@gmail.com"}],"homepage":"https://github.com/buzzni/saycode-extensions#readme","bugs":{"url":"https://github.com/buzzni/saycode-extensions/issues"},"bin":{"saycode-extension":"dist/cli.js"},"dist":{"shasum":"61b578878d36e54d9702ec55ffb7072b112e1d0f","tarball":"https://registry.npmjs.org/@buzzni/saycode-extension-sdk/-/saycode-extension-sdk-0.4.0.tgz","fileCount":9,"integrity":"sha512-pPLhABdDvt2NZMkdHWg7c1dJvvb48Tbb5IzKmCj4TQdgYjkk4oKFTBfp5W+bssWtkrtoKSZ4AxesFn5tCsgxWA==","signatures":[{"sig":"MEYCIQCiO1YN9rscwKMlyCUq4/xlpqKPhaLUAQZwOYLeWRpu9wIhAKAkZ841EYVWsEyzrKXdkYsHdm0qs6YxI4fsK93RnrnZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIHwkb5bAZOrcX7MnirpcIInJ10p2UQmNcTsKSgLMZ3d4AiEApaadCuuUI0wPCYOyPw9UOWs04vo7fMJ1CFVt1kiQy2A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50330},"type":"module","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js"}},"gitHead":"557a316be5d14069309573a724e25204eeb371ff","scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"namsangboy","email":"namsangboy@gmail.com"},"repository":{"url":"git+https://github.com/buzzni/saycode-extensions.git","type":"git"},"_npmVersion":"10.9.8","description":"Contracts and CLI for building Saycode Desktop extensions.","directories":{},"_nodeVersion":"22.23.2","dependencies":{"jszip":"^3.10.1","esbuild":"^0.28.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/saycode-extension-sdk_0.4.0_1789717858369_0.1492798720389792","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"_id":"@buzzni/saycode-extension-sdk@0.4.1","bin":{"saycode-extension":"dist/cli.js"},"bugs":{"url":"https://github.com/buzzni/saycode-extensions/issues"},"dist":{"shasum":"c515d7fd3353069c1949c1b615f82a154b3fc4fd","tarball":"https://registry.npmjs.org/@buzzni/saycode-extension-sdk/-/saycode-extension-sdk-0.4.1.tgz","fileCount":9,"integrity":"sha512-Qs/r+i7iVvnwRa5MzyFryks2JwcqpIkqJIVCOhYtD5Al8KGs93QKrePfYBcazTSx62QeSbu6h+b7K+i9SRXXxA==","signatures":[{"sig":"MEQCIHRpHDPDzzXcQ9kdTm92qEMELPCin4/fePM9VO/maZR4AiArRkfupgvY5LL2ZKN6RAiDyPBkaXq5iI84dD/IYH2pFQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCGO2N+kHK2w3nVooUtQnMhywKhCYCQo59Ll5HzUpXuTAIgXvoRQd97NiwFtI6tuMyFor1AxEw9gjlFm9GZoCcivtU="}],"unpackedSize":53277},"name":"@buzzni/saycode-extension-sdk","type":"module","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js"}},"gitHead":"417b7ebf897161689ffc04b3be002a9c971e9c64","license":"MIT","scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"version":"0.4.1","_npmUser":{"name":"namsangboy","email":"namsangboy@gmail.com"},"homepage":"https://github.com/buzzni/saycode-extensions#readme","keywords":["saycode","extension","sdk","desktop"],"repository":{"url":"git+https://github.com/buzzni/saycode-extensions.git","type":"git"},"_npmVersion":"10.9.8","description":"Contracts and CLI for building Saycode Desktop extensions.","directories":{},"maintainers":[{"name":"namsangboy","email":"namsangboy@gmail.com"}],"_nodeVersion":"22.23.2","dependencies":{"jszip":"^3.10.2","esbuild":"^0.28.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/saycode-extension-sdk_0.4.1_1790771484146_0.1047136065198433"}}},"time":{"created":"2026-08-19T10:13:01.471Z","modified":"2026-09-30T12:31:24.751Z","0.1.0":"2026-08-19T10:13:02.178Z","0.3.0":"2026-08-29T05:36:42.203Z","0.4.0":"2026-09-18T07:50:58.504Z","0.4.1":"2026-09-30T12:31:24.233Z"},"bugs":{"url":"https://github.com/buzzni/saycode-extensions/issues"},"license":"MIT","homepage":"https://github.com/buzzni/saycode-extensions#readme","keywords":["saycode","extension","sdk","desktop"],"repository":{"url":"git+https://github.com/buzzni/saycode-extensions.git","type":"git"},"description":"Contracts and CLI for building Saycode Desktop extensions.","maintainers":[{"name":"namsangboy","email":"namsangboy@gmail.com"}],"readme":"# @buzzni/saycode-extension-sdk\n\nContracts and CLI for building extensions for Saycode Desktop.\n\nExtensions run in an isolated host, outside the Saycode renderer. This package gives you the typed contracts to write\none and the CLI to validate, bundle, and package it. Desktop internals, Electron, Node built-ins, credentials, and\nunrestricted network/filesystem access are intentionally absent — the security boundary lives in Desktop, not here.\n\n## Quick Start\n\n`scaffold` needs no prior install — `npx` fetches the CLI for you:\n\n```bash\nnpx @buzzni/saycode-extension-sdk scaffold hello-extension --id com.example.hello\ncd hello-extension\nnpm install\nnpm run validate\nnpm run dev -- --once\nnpm run pack\n```\n\nThe result is `com.example.hello-1.0.0.saycode-extension`, installable from **Settings → Extensions** in Saycode\nDesktop. The scaffolded command is lazy: Desktop activates the extension only when `com.example.hello.hello` is first\ninvoked.\n\n## Install\n\n`scaffold` already adds the SDK to the generated project. To add it to an existing project:\n\n```bash\nnpm i -D @buzzni/saycode-extension-sdk\n```\n\nAlways a **devDependency**. `pack` inlines the contracts into your extension bundle, so nothing resolves this package\nat runtime — and declaring it as a runtime dependency would drag the CLI's `esbuild` and `jszip` into your tree.\n\nRequires Node.js 22 or newer.\n\n## Public API\n\nOnly these imports are stable:\n\n```ts\nimport { defineExtension, type ExtensionContext, type JsonValue } from '@buzzni/saycode-extension-sdk'\nimport { parseExtensionManifest } from '@buzzni/saycode-extension-sdk/manifest'\n```\n\n```ts\nexport default defineExtension({\n  activate(context) {\n    context.commands.register('com.example.hello.hello', (name) => `Hello ${String(name)}`)\n  },\n})\n```\n\n`defineExtension` accepts `activate(context)` and an optional `deactivate()`. `context.commands.register(id, handler)`\nregisters a namespaced command. Arguments and results must be JSON values: null, finite numbers, booleans, strings,\narrays, or plain objects composed from those values.\n\nImporting undocumented package subpaths is rejected by package exports. Importing Saycode Desktop source,\n`@buzzni/saycode-core`, Electron, VS Code, or Node built-ins is forbidden and fails at bundle time.\n\n## Manifest\n\n`extension.json` is the package contract:\n\n```json\n{\n  \"id\": \"com.example.hello\",\n  \"version\": \"1.0.0\",\n  \"apiVersion\": 3,\n  \"engines\": { \"saycode\": \"^1.0.0\" },\n  \"entrypoint\": \"index.js\",\n  \"permissions\": [],\n  \"activationEvents\": [\"onCommand:com.example.hello.hello\"],\n  \"contributes\": {\n    \"commands\": [{ \"id\": \"com.example.hello.hello\", \"title\": \"Hello\" }]\n  }\n}\n```\n\nIds are lowercase stable namespaces. Versions use semantic versioning. Paths are relative, forward-slash paths without\nempty, `.`, or `..` segments. API versions and permissions are closed sets; an unknown value is rejected before\nexecution. API v2 supports `commands`, typed `settings`, isolated `panels`, `projectTemplates`, and contextual\n`machineActions`. API v3 additionally supports declarative `artifactActions`; the current SDK accepts only the\nDesktop support window `[2,3]`.\n\nA project template keeps the v1 `id`, `title`, and `assetsRoot` fields and may add UI metadata:\n\n```json\n{\n  \"projectTemplates\": [{\n    \"id\": \"com.example.templates.dashboard\",\n    \"title\": \"Dashboard\",\n    \"description\": \"Dashboard starter\",\n    \"stack\": \"React\",\n    \"firstPrompt\": \"Build a dashboard\",\n    \"devServerCommand\": \"npm run dev\",\n    \"assetsRoot\": \"templates/dashboard\",\n    \"localizations\": {\n      \"ko\": { \"title\": \"대시보드\", \"description\": \"대시보드 시작점\", \"firstPrompt\": \"대시보드를 만들어줘\" }\n    }\n  }]\n}\n```\n\n`description`, `stack`, `firstPrompt`, and `devServerCommand` remain optional for compatibility, but Desktop lists only\ntemplates that provide all four. `localizations` may override `title`, `description`, and `firstPrompt`; lookup falls\nback from an exact locale to its base language and then to the default fields. Asset paths stay relative to\n`assetsRoot`; Desktop reads them through bounded host APIs rather than exposing installation paths.\n\n## Lifecycle and permissions\n\nInstallation validates and stores an extension but leaves it disabled. Enablement exposes contributions without loading\nextension code. An activation event loads the browser bundle in the isolated host. Disablement removes contributions and\ndeactivates the host. Three consecutive crashes quarantine the extension until manual recovery. Updates require an\ninactive extension and preserve the last-known-good version for rollback.\n\nProtected work must go through a declared capability. Both a manifest declaration and current user approval are\nrequired. Extensions never receive raw auth tokens, E2EE secrets, sync credentials, unrestricted Electron/Node objects,\nor ambient filesystem, network, or process access.\n\n## UI contributions\n\nExtensions cannot import React components into the Saycode renderer. `panels` name a packaged HTML entrypoint rendered\nin an isolated surface with origin and schema checks and no raw credential bridge. Settings and project templates are\ndeclarative descriptors. Keep contribution ids namespaced by the extension id; collisions disable registration.\n\n## Testing and debugging\n\n`saycode-extension validate .` checks the manifest before you build. `saycode-extension dev . --once` produces a\ndeterministic browser bundle. The bundler targets a browser and deliberately fails imports such as `node:fs`,\n`node:net`, `child_process`, or `electron`.\n\nRuntime exceptions and timeouts appear in **Settings → Extensions** in Desktop; repeated crashes enter quarantine.\n\n## Packaging and release\n\n`saycode-extension pack .` validates the manifest, creates a browser ESM bundle, and writes a deterministic package\ncontaining `extension.json` and `index.js`. Distribute that file directly; users install it from\n**Settings → Extensions**.\n\nA marketplace, remote catalog, automatic updates, artifact signing, and revocation are not part of this release.\n\n## Versioning and compatibility\n\nThis package is `0.x`: minor versions may contain breaking changes while the extension API settles. Pin an exact\nversion if you need stability.\n\nUse semantic versions for your own extension releases. `apiVersion` is the wire schema version and changes only for\nincompatible contract changes. `engines.saycode` states the compatible Desktop line. Desktop tests the current and\nprevious supported API fixtures and rejects unsupported versions with an explanatory message. Do not deep-import SDK\ninternals to work around that gate.\n\n## Security rules\n\n- Never request or log credentials, cookies, encryption keys, or raw authorization headers.\n- Never hide a required permission behind a generic label.\n- Treat command arguments, panel messages, stored values, and remote content as untrusted input.\n- Do not add postinstall scripts, symlinks, absolute paths, path traversal, dynamic Node imports, or native binaries.\n- Keep network, file, machine, notification, and storage operations behind the smallest declared capability.\n- Report a suspected sandbox or capability bypass privately, never in a public forum. Use\n  https://github.com/buzzni/saycode-extensions/security/advisories/new — any GitHub account can file there\n  and only the Saycode maintainers see it. Include the affected SDK and API version, a minimal extension package,\n  and reproduction steps without real credentials.\n\n## License\n\nMIT\n\n## Asset workflow (SDK 0.4.1 / host engine 1.1.0)\n\nAPI 3 remains current, with API 2 still supported. New asset extensions declare `engines.saycode: \"^1.1.0\"`; older hosts fail closed. The CLI includes a package's optional `assets/` directory using the same traversal/symlink checks as project-template files. Do not put private user originals or credentials in an artifact.\n\nThe four permissions are separate grants: `assets.read` (list/describe/open/readChunk/close), `assets.write` (create/update/remove and begin/writeChunk/seal for instruction text), `files.select` (native pick/readChunk/close), and `drafts.prepare` (context/prepare/commit/cancel). Call `context.invokeCapability(permission, action, {version: 1, ...args})`. Core supplies the authenticated caller; no user ID, URL, credential, arbitrary machine command or absolute path is accepted. `assets.read/open` accepts either an artifact-relative `path`, or private `id`, `revision`, `role` (source/instructions/preview). `readChunk` accepts handle/offset and returns base64, nextOffset and done.\n\nOriginals are limited to 10 MiB each and 20 MiB per extension/principal scope. Chunks are 24 KiB, handles expire after 5 minutes of inactivity, and calls are bounded to 120 seconds. Editing uses contiguous byte offsets then a SHA-256 seal. Create takes name/sourceHandle/preserve; update takes id/expectedRevision and name/sourceHandle/instructionHandle; remove takes id/expectedRevision. Core shows a native confirmation and rechecks the revision. A failed or timed-out write may have reached the server: reload before retrying, never automatically repeat a create.\n\n`context` returns targetId/revision/private for the composer that opened the panel. `prepare` takes targetId/revision/text/handles and returns preparationId. `commit` confirms then appends to that same unedited draft; it never sends a message or creates a session. Private originals cannot enter project or organizational drafts. Cancellation, navigation, account changes, permission revocation and host stop invalidate pending work. After applying, users review and send using the normal composer.\n\nThe panel must use `window.saycodePanel.ready` and `invokeCommand`, not install a competing MessagePort listener. It has no network or Node access. Renderer migration is outside this release: the document template package uses retained public previews and authenticated existing Web previews.\n\nPanels run in an opaque-origin iframe framed with `sandbox=\"allow-scripts\"`: forms never submit and `crypto.subtle` is unavailable. Save from button click handlers and hash in plain JavaScript; test panels in a harness that removes both.\n","readmeFilename":"README.md"}