@nimbus-dev/sdk 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -0
  3. package/dist/audit-logger.d.ts +6 -0
  4. package/dist/audit-logger.d.ts.map +1 -0
  5. package/dist/audit-logger.js +18 -0
  6. package/dist/audit-logger.js.map +1 -0
  7. package/dist/contract-tests.d.ts +45 -0
  8. package/dist/contract-tests.d.ts.map +1 -0
  9. package/dist/contract-tests.js +191 -0
  10. package/dist/contract-tests.js.map +1 -0
  11. package/dist/crypto/app-store-connect-jwt.d.ts +19 -0
  12. package/dist/crypto/app-store-connect-jwt.d.ts.map +1 -0
  13. package/dist/crypto/app-store-connect-jwt.js +30 -0
  14. package/dist/crypto/app-store-connect-jwt.js.map +1 -0
  15. package/dist/crypto/canonical-json.d.ts +36 -0
  16. package/dist/crypto/canonical-json.d.ts.map +1 -0
  17. package/dist/crypto/canonical-json.js +75 -0
  18. package/dist/crypto/canonical-json.js.map +1 -0
  19. package/dist/crypto/jwt.d.ts +30 -0
  20. package/dist/crypto/jwt.d.ts.map +1 -0
  21. package/dist/crypto/jwt.js +30 -0
  22. package/dist/crypto/jwt.js.map +1 -0
  23. package/dist/crypto/service-account-token.d.ts +36 -0
  24. package/dist/crypto/service-account-token.d.ts.map +1 -0
  25. package/dist/crypto/service-account-token.js +96 -0
  26. package/dist/crypto/service-account-token.js.map +1 -0
  27. package/dist/crypto/verify-signature.d.ts +57 -0
  28. package/dist/crypto/verify-signature.d.ts.map +1 -0
  29. package/dist/crypto/verify-signature.js +102 -0
  30. package/dist/crypto/verify-signature.js.map +1 -0
  31. package/dist/distribution-channel.d.ts +34 -0
  32. package/dist/distribution-channel.d.ts.map +1 -0
  33. package/dist/distribution-channel.js +73 -0
  34. package/dist/distribution-channel.js.map +1 -0
  35. package/dist/hitl-request.d.ts +7 -0
  36. package/dist/hitl-request.d.ts.map +1 -0
  37. package/dist/hitl-request.js +15 -0
  38. package/dist/hitl-request.js.map +1 -0
  39. package/dist/index.d.ts +23 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +19 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/ipc/index.d.ts +2 -0
  44. package/dist/ipc/index.d.ts.map +1 -0
  45. package/dist/ipc/index.js +2 -0
  46. package/dist/ipc/index.js.map +1 -0
  47. package/dist/ipc/ndjson-line-reader.d.ts +20 -0
  48. package/dist/ipc/ndjson-line-reader.d.ts.map +1 -0
  49. package/dist/ipc/ndjson-line-reader.js +56 -0
  50. package/dist/ipc/ndjson-line-reader.js.map +1 -0
  51. package/dist/server.d.ts +29 -0
  52. package/dist/server.d.ts.map +1 -0
  53. package/dist/server.js +23 -0
  54. package/dist/server.js.map +1 -0
  55. package/dist/testing/index.d.ts +15 -0
  56. package/dist/testing/index.d.ts.map +1 -0
  57. package/dist/testing/index.js +17 -0
  58. package/dist/testing/index.js.map +1 -0
  59. package/dist/testing/sandbox-contract.d.ts +83 -0
  60. package/dist/testing/sandbox-contract.d.ts.map +1 -0
  61. package/dist/testing/sandbox-contract.js +105 -0
  62. package/dist/testing/sandbox-contract.js.map +1 -0
  63. package/dist/testing/sandbox-probe.d.ts +23 -0
  64. package/dist/testing/sandbox-probe.d.ts.map +1 -0
  65. package/dist/testing/sandbox-probe.js +78 -0
  66. package/dist/testing/sandbox-probe.js.map +1 -0
  67. package/dist/types.d.ts +41 -0
  68. package/dist/types.d.ts.map +1 -0
  69. package/dist/types.js +5 -0
  70. package/dist/types.js.map +1 -0
  71. package/package.json +55 -0
  72. package/src/audit-logger.test.ts +33 -0
  73. package/src/audit-logger.ts +23 -0
  74. package/src/contract-tests.test.ts +203 -0
  75. package/src/contract-tests.ts +220 -0
  76. package/src/crypto/app-store-connect-jwt.test.ts +80 -0
  77. package/src/crypto/app-store-connect-jwt.ts +42 -0
  78. package/src/crypto/canonical-json.test.ts +121 -0
  79. package/src/crypto/canonical-json.ts +73 -0
  80. package/src/crypto/jwt.test.ts +62 -0
  81. package/src/crypto/jwt.ts +45 -0
  82. package/src/crypto/service-account-token.test.ts +128 -0
  83. package/src/crypto/service-account-token.ts +116 -0
  84. package/src/crypto/verify-signature.test.ts +118 -0
  85. package/src/crypto/verify-signature.ts +138 -0
  86. package/src/distribution-channel.test.ts +107 -0
  87. package/src/distribution-channel.ts +105 -0
  88. package/src/hitl-request.ts +22 -0
  89. package/src/index.ts +59 -0
  90. package/src/ipc/index.ts +5 -0
  91. package/src/ipc/ndjson-line-reader.test.ts +64 -0
  92. package/src/ipc/ndjson-line-reader.ts +70 -0
  93. package/src/plugin-api-v1.test.ts +50 -0
  94. package/src/sdk.test.ts +23 -0
  95. package/src/server.test.ts +96 -0
  96. package/src/server.ts +39 -0
  97. package/src/testing/index.ts +18 -0
  98. package/src/testing/sandbox-contract.test.ts +146 -0
  99. package/src/testing/sandbox-contract.ts +155 -0
  100. package/src/testing/sandbox-probe.ts +87 -0
  101. package/src/types.ts +42 -0
package/dist/server.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * NimbusExtensionServer — base class for all Nimbus MCP extension servers
3
+ *
4
+ * Usage:
5
+ * const server = new NimbusExtensionServer({ manifest, onAuth });
6
+ * server.registerTool("search", { description, inputSchema, handler });
7
+ * server.start();
8
+ */
9
+ export class NimbusExtensionServer {
10
+ _options;
11
+ constructor(options) {
12
+ this._options = options;
13
+ }
14
+ registerTool(_name, _definition) {
15
+ // Roadmap Q3: register tool with MCP server
16
+ }
17
+ start() {
18
+ if (this._options.manifest.id.length === 0) {
19
+ throw new Error("NimbusExtensionServer: manifest.id is required");
20
+ }
21
+ }
22
+ }
23
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,MAAM,OAAO,qBAAqB;IACf,QAAQ,CAAkC;IAE3D,YAAY,OAAwC;QAClD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,YAAY,CAAS,KAAa,EAAE,WAA4C;QAC9E,4CAA4C;IAC9C,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `@nimbus-dev/sdk/testing` — utilities for extension authors and the
3
+ * gateway's own connector tests.
4
+ *
5
+ * Exports:
6
+ * - `MockGateway` — mock Gateway IPC for unit tests (Phase 4).
7
+ * - `runSandboxContractTests(manifestPath)` — fork the probe binary and
8
+ * verify the runtime sandbox enforces the manifest's declared
9
+ * `permissions.network` + `permissions.filesystem` (Phase 5 T2 PR 1).
10
+ */
11
+ export { runSandboxContractTests } from "./sandbox-contract";
12
+ export declare class MockGateway {
13
+ callTool(_toolName: string, _input: Record<string, unknown>): Promise<unknown>;
14
+ }
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,qBAAa,WAAW;IAChB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAGrF"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@nimbus-dev/sdk/testing` — utilities for extension authors and the
3
+ * gateway's own connector tests.
4
+ *
5
+ * Exports:
6
+ * - `MockGateway` — mock Gateway IPC for unit tests (Phase 4).
7
+ * - `runSandboxContractTests(manifestPath)` — fork the probe binary and
8
+ * verify the runtime sandbox enforces the manifest's declared
9
+ * `permissions.network` + `permissions.filesystem` (Phase 5 T2 PR 1).
10
+ */
11
+ export { runSandboxContractTests } from "./sandbox-contract";
12
+ export class MockGateway {
13
+ async callTool(_toolName, _input) {
14
+ return {};
15
+ }
16
+ }
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,OAAO,WAAW;IACtB,KAAK,CAAC,QAAQ,CAAC,SAAiB,EAAE,MAA+B;QAC/D,OAAO,EAAE,CAAC;IACZ,CAAC;CACF"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * `runSandboxContractTests` — verifies the declared sandbox permissions in a
3
+ * manifest match the runtime enforcement seen by a forked probe.
4
+ *
5
+ * Phase 5 T2 PR 1.
6
+ *
7
+ * Three probes run sequentially:
8
+ *
9
+ * 1. **network-listed** — for the first declared host in
10
+ * `permissions.network`, a HEAD fetch must succeed (2xx–4xx).
11
+ * Skipped if no hosts are declared.
12
+ *
13
+ * 2. **network-unlisted** — a fetch to `192.0.2.1` (TEST-NET-1, never
14
+ * routable) must fail with ECONNREFUSED / EPERM / EHOSTUNREACH /
15
+ * ENETUNREACH. Skipped on Windows because AppContainer network
16
+ * filtering is host-allow + deny-by-default at a different layer — the
17
+ * probe sees a generic socket failure that is indistinguishable from
18
+ * the unsandboxed case. See `docs/sandbox.md#platform-asymmetry`.
19
+ * Skipped on every platform if no hosts are declared (the
20
+ * sandbox-runner would deny network entirely in that case, and the
21
+ * probe's expectation collapses to "no network at all", which the
22
+ * first probe doesn't exercise).
23
+ *
24
+ * 3. **fs-denied** — a read of a known-protected path
25
+ * (`/etc/passwd` POSIX, `C:\Windows\System32\config\SAM` Windows)
26
+ * must fail with EACCES / EPERM. This probe always runs.
27
+ *
28
+ * Each probe is invoked via `child_process.spawnSync(process.execPath, …)`
29
+ * directly. **The probe is *not* sandbox-wrapped by the SDK harness alone.**
30
+ * For the SDK to assert real enforcement, a wrapping helper (typically
31
+ * `packages/gateway/test/helpers/sandbox-harness.ts`) needs to fork
32
+ * `runSandboxContractTests` inside a process that is itself sandbox-wrapped,
33
+ * or substitute a sandboxed `execPath`.
34
+ *
35
+ * Used by:
36
+ * - First-party connector contract tests via the gateway test harness.
37
+ * - Third-party extension authors invoking
38
+ * `import { runSandboxContractTests } from "@nimbus-dev/sdk/testing"`.
39
+ *
40
+ * When invoked **outside** a sandboxed harness on Linux/macOS, probes 2 and
41
+ * 3 will either succeed (return exit 2) — which the harness reports as a
42
+ * test failure — or fail with codes other than 10/11. Either way the harness
43
+ * throws. That's the third-party UX: "your contract test failed because the
44
+ * sandbox isn't enforcing the manifest", which is the correct signal even
45
+ * though the UX could be friendlier.
46
+ */
47
+ export interface ProbeResult {
48
+ status: number;
49
+ stderr: string;
50
+ stdout: string;
51
+ }
52
+ /** Probe-runner shape — exposed for testability (see `__defaultRunProbe`). */
53
+ export type ProbeRunner = (probe: string, arg: string) => ProbeResult;
54
+ export interface RunSandboxContractTestsOptions {
55
+ /**
56
+ * Override the probe runner. Default is `__defaultRunProbe` (a
57
+ * `child_process.spawnSync` wrapper around the bundled probe binary).
58
+ * Tests inject a stub here; production callers leave this undefined.
59
+ */
60
+ runProbe?: ProbeRunner;
61
+ /**
62
+ * Override `process.platform` for testability of the Windows skip
63
+ * branch. Default is the live `process.platform`.
64
+ */
65
+ platform?: NodeJS.Platform;
66
+ }
67
+ /**
68
+ * Read the manifest at `manifestPath`, fork the probe binary for each
69
+ * declared capability + the FS-denied negative case, and throw if the
70
+ * observed enforcement does not match.
71
+ *
72
+ * Throws on first failure with a message that names the probe, the
73
+ * observed exit code, and the probe's stderr.
74
+ */
75
+ export declare function runSandboxContractTests(manifestPath: string, opts?: RunSandboxContractTestsOptions): Promise<void>;
76
+ /**
77
+ * Default probe runner — forks the bundled probe binary via
78
+ * `child_process.spawnSync`. Exported for direct use in test scaffolding
79
+ * that wants to assert the production wiring without re-implementing the
80
+ * `spawnSync` envelope.
81
+ */
82
+ export declare function __defaultRunProbe(probe: string, arg: string): ProbeResult;
83
+ //# sourceMappingURL=sandbox-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-contract.d.ts","sourceRoot":"","sources":["../../src/testing/sandbox-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAmBH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,8EAA8E;AAC9E,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,WAAW,CAAC;AAEtE,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,MAAM,EACpB,IAAI,GAAE,8BAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CAuCf;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CASzE"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * `runSandboxContractTests` — verifies the declared sandbox permissions in a
3
+ * manifest match the runtime enforcement seen by a forked probe.
4
+ *
5
+ * Phase 5 T2 PR 1.
6
+ *
7
+ * Three probes run sequentially:
8
+ *
9
+ * 1. **network-listed** — for the first declared host in
10
+ * `permissions.network`, a HEAD fetch must succeed (2xx–4xx).
11
+ * Skipped if no hosts are declared.
12
+ *
13
+ * 2. **network-unlisted** — a fetch to `192.0.2.1` (TEST-NET-1, never
14
+ * routable) must fail with ECONNREFUSED / EPERM / EHOSTUNREACH /
15
+ * ENETUNREACH. Skipped on Windows because AppContainer network
16
+ * filtering is host-allow + deny-by-default at a different layer — the
17
+ * probe sees a generic socket failure that is indistinguishable from
18
+ * the unsandboxed case. See `docs/sandbox.md#platform-asymmetry`.
19
+ * Skipped on every platform if no hosts are declared (the
20
+ * sandbox-runner would deny network entirely in that case, and the
21
+ * probe's expectation collapses to "no network at all", which the
22
+ * first probe doesn't exercise).
23
+ *
24
+ * 3. **fs-denied** — a read of a known-protected path
25
+ * (`/etc/passwd` POSIX, `C:\Windows\System32\config\SAM` Windows)
26
+ * must fail with EACCES / EPERM. This probe always runs.
27
+ *
28
+ * Each probe is invoked via `child_process.spawnSync(process.execPath, …)`
29
+ * directly. **The probe is *not* sandbox-wrapped by the SDK harness alone.**
30
+ * For the SDK to assert real enforcement, a wrapping helper (typically
31
+ * `packages/gateway/test/helpers/sandbox-harness.ts`) needs to fork
32
+ * `runSandboxContractTests` inside a process that is itself sandbox-wrapped,
33
+ * or substitute a sandboxed `execPath`.
34
+ *
35
+ * Used by:
36
+ * - First-party connector contract tests via the gateway test harness.
37
+ * - Third-party extension authors invoking
38
+ * `import { runSandboxContractTests } from "@nimbus-dev/sdk/testing"`.
39
+ *
40
+ * When invoked **outside** a sandboxed harness on Linux/macOS, probes 2 and
41
+ * 3 will either succeed (return exit 2) — which the harness reports as a
42
+ * test failure — or fail with codes other than 10/11. Either way the harness
43
+ * throws. That's the third-party UX: "your contract test failed because the
44
+ * sandbox isn't enforcing the manifest", which is the correct signal even
45
+ * though the UX could be friendlier.
46
+ */
47
+ import { spawnSync } from "node:child_process";
48
+ import { readFile } from "node:fs/promises";
49
+ import { dirname, resolve } from "node:path";
50
+ import { fileURLToPath } from "node:url";
51
+ const PROBE_PATH = resolve(dirname(fileURLToPath(import.meta.url)), "sandbox-probe.ts");
52
+ /**
53
+ * Read the manifest at `manifestPath`, fork the probe binary for each
54
+ * declared capability + the FS-denied negative case, and throw if the
55
+ * observed enforcement does not match.
56
+ *
57
+ * Throws on first failure with a message that names the probe, the
58
+ * observed exit code, and the probe's stderr.
59
+ */
60
+ export async function runSandboxContractTests(manifestPath, opts = {}) {
61
+ const runProbe = opts.runProbe ?? __defaultRunProbe;
62
+ const platform = opts.platform ?? process.platform;
63
+ const raw = await readFile(manifestPath, "utf8");
64
+ const manifest = JSON.parse(raw);
65
+ const perms = manifest.permissions;
66
+ const objectForm = perms && typeof perms === "object" && !Array.isArray(perms) ? perms : null;
67
+ const hosts = objectForm?.network ?? [];
68
+ const firstHost = hosts[0];
69
+ if (firstHost !== undefined) {
70
+ const r = runProbe("network-listed", firstHost);
71
+ if (r.status !== 0) {
72
+ throw new Error(`network-listed probe failed for ${firstHost}: exit ${r.status}; stderr: ${r.stderr.trim()}`);
73
+ }
74
+ }
75
+ if (platform !== "win32" && hosts.length > 0) {
76
+ const r = runProbe("network-unlisted", "");
77
+ if (r.status !== 11) {
78
+ throw new Error(`network-unlisted probe should have failed with ECONNREFUSED/EPERM/EHOSTUNREACH/ENETUNREACH; ` +
79
+ `got exit ${r.status}; stderr: ${r.stderr.trim()}. ` +
80
+ `See docs/sandbox.md#platform-asymmetry.`);
81
+ }
82
+ }
83
+ const r3 = runProbe("fs-denied", "");
84
+ if (r3.status !== 10) {
85
+ throw new Error(`fs-denied probe should have returned EACCES (exit 10); got exit ${r3.status}; ` +
86
+ `stderr: ${r3.stderr.trim()}.`);
87
+ }
88
+ }
89
+ /**
90
+ * Default probe runner — forks the bundled probe binary via
91
+ * `child_process.spawnSync`. Exported for direct use in test scaffolding
92
+ * that wants to assert the production wiring without re-implementing the
93
+ * `spawnSync` envelope.
94
+ */
95
+ export function __defaultRunProbe(probe, arg) {
96
+ const result = spawnSync(process.execPath, [PROBE_PATH, `--probe=${probe}`, `--arg=${arg}`], {
97
+ encoding: "utf8",
98
+ });
99
+ return {
100
+ status: result.status ?? -1,
101
+ stderr: result.stderr ?? "",
102
+ stdout: result.stdout ?? "",
103
+ };
104
+ }
105
+ //# sourceMappingURL=sandbox-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-contract.js","sourceRoot":"","sources":["../../src/testing/sandbox-contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Sandbox probe binary — invoked by `runSandboxContractTests`.
3
+ *
4
+ * The probe is a tiny standalone program that the contract harness forks
5
+ * (typically wrapped by the gateway's sandbox runner) to exercise one
6
+ * specific capability and report the outcome via process exit code.
7
+ *
8
+ * Exit codes:
9
+ * 0 — expected pass (network reach to a listed host succeeded)
10
+ * 2 — unexpected outcome (test fails)
11
+ * 10 — expected EACCES/EPERM on filesystem read (sandbox enforced)
12
+ * 11 — expected ECONNREFUSED/EPERM/EHOSTUNREACH/ENETUNREACH on network
13
+ *
14
+ * Invocation:
15
+ * bun sandbox-probe.ts --probe=<name> --arg=<value>
16
+ *
17
+ * Probes:
18
+ * network-listed — HEAD-fetch https://<arg>/ ; succeed if 2xx-4xx
19
+ * network-unlisted — fetch http://192.0.2.1 (TEST-NET-1); succeed if blocked
20
+ * fs-denied — read a known-protected path; succeed if EACCES
21
+ */
22
+ export {};
23
+ //# sourceMappingURL=sandbox-probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-probe.d.ts","sourceRoot":"","sources":["../../src/testing/sandbox-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAgEH,OAAO,EAAE,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Sandbox probe binary — invoked by `runSandboxContractTests`.
3
+ *
4
+ * The probe is a tiny standalone program that the contract harness forks
5
+ * (typically wrapped by the gateway's sandbox runner) to exercise one
6
+ * specific capability and report the outcome via process exit code.
7
+ *
8
+ * Exit codes:
9
+ * 0 — expected pass (network reach to a listed host succeeded)
10
+ * 2 — unexpected outcome (test fails)
11
+ * 10 — expected EACCES/EPERM on filesystem read (sandbox enforced)
12
+ * 11 — expected ECONNREFUSED/EPERM/EHOSTUNREACH/ENETUNREACH on network
13
+ *
14
+ * Invocation:
15
+ * bun sandbox-probe.ts --probe=<name> --arg=<value>
16
+ *
17
+ * Probes:
18
+ * network-listed — HEAD-fetch https://<arg>/ ; succeed if 2xx-4xx
19
+ * network-unlisted — fetch http://192.0.2.1 (TEST-NET-1); succeed if blocked
20
+ * fs-denied — read a known-protected path; succeed if EACCES
21
+ */
22
+ const probe = process.argv.find((a) => a.startsWith("--probe="))?.slice(8);
23
+ const arg = process.argv.find((a) => a.startsWith("--arg="))?.slice(6);
24
+ function errorCode(e) {
25
+ return (e.code ??
26
+ e.cause?.code);
27
+ }
28
+ async function probeNetworkListed() {
29
+ const url = `https://${arg ?? ""}/`;
30
+ try {
31
+ const res = await fetch(url, { method: "HEAD" });
32
+ return res.status >= 200 && res.status < 500 ? 0 : 2;
33
+ }
34
+ catch {
35
+ return 2;
36
+ }
37
+ }
38
+ async function probeNetworkUnlisted() {
39
+ try {
40
+ await fetch("http://192.0.2.1");
41
+ return 2;
42
+ }
43
+ catch (e) {
44
+ const code = errorCode(e);
45
+ if (code === "ECONNREFUSED" ||
46
+ code === "EPERM" ||
47
+ code === "EHOSTUNREACH" ||
48
+ code === "ENETUNREACH") {
49
+ return 11;
50
+ }
51
+ return 2;
52
+ }
53
+ }
54
+ async function probeFsDenied() {
55
+ const path = process.platform === "win32" ? String.raw `C:\Windows\System32\config\SAM` : "/etc/passwd";
56
+ try {
57
+ await Bun.file(path).text();
58
+ return 2;
59
+ }
60
+ catch (e) {
61
+ const code = e.code;
62
+ if (code === "EACCES" || code === "EPERM" || code === "EBUSY")
63
+ return 10;
64
+ return 2;
65
+ }
66
+ }
67
+ async function main() {
68
+ if (probe === "network-listed")
69
+ process.exit(await probeNetworkListed());
70
+ if (probe === "network-unlisted")
71
+ process.exit(await probeNetworkUnlisted());
72
+ if (probe === "fs-denied")
73
+ process.exit(await probeFsDenied());
74
+ process.exit(2);
75
+ }
76
+ await main();
77
+ export {};
78
+ //# sourceMappingURL=sandbox-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-probe.js","sourceRoot":"","sources":["../../src/testing/sandbox-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AAC3E,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AAEvE,SAAS,SAAS,CAAC,CAAU;IAC3B,OAAO,CACJ,CAAkD,CAAC,IAAI;QACvD,CAAmC,CAAC,KAAK,EAAE,IAAI,CACjD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,MAAM,GAAG,GAAG,WAAW,GAAG,IAAI,EAAE,GAAG,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACjD,OAAO,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB;IACjC,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC1B,IACE,IAAI,KAAK,cAAc;YACvB,IAAI,KAAK,OAAO;YAChB,IAAI,KAAK,cAAc;YACvB,IAAI,KAAK,aAAa,EACtB,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa;IAC1B,MAAM,IAAI,GACR,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAA,gCAAgC,CAAC,CAAC,CAAC,aAAa,CAAC;IAC5F,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,IAAI,GAAI,CAAuB,CAAC,IAAI,CAAC;QAC3C,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO;YAAE,OAAO,EAAE,CAAC;QACzE,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,IAAI,KAAK,KAAK,gBAAgB;QAAE,OAAO,CAAC,IAAI,CAAC,MAAM,kBAAkB,EAAE,CAAC,CAAC;IACzE,IAAI,KAAK,KAAK,kBAAkB;QAAE,OAAO,CAAC,IAAI,CAAC,MAAM,oBAAoB,EAAE,CAAC,CAAC;IAC7E,IAAI,KAAK,KAAK,WAAW;QAAE,OAAO,CAAC,IAAI,CAAC,MAAM,aAAa,EAAE,CAAC,CAAC;IAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAQD,MAAM,IAAI,EAAE,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Shared types for Nimbus extensions
3
+ */
4
+ export interface NimbusItem {
5
+ id: string;
6
+ service: string;
7
+ itemType: "file" | "folder" | "email" | "event" | "photo" | "task";
8
+ name: string;
9
+ mimeType?: string;
10
+ sizeBytes?: number;
11
+ createdAt?: number;
12
+ modifiedAt?: number;
13
+ url?: string;
14
+ parentId?: string;
15
+ rawMeta?: Record<string, unknown>;
16
+ }
17
+ export interface ExtensionManifest {
18
+ $schema?: string;
19
+ id: string;
20
+ displayName: string;
21
+ version: string;
22
+ description: string;
23
+ author: string;
24
+ homepage?: string;
25
+ icon?: string;
26
+ entrypoint: string;
27
+ runtime: "bun" | "node";
28
+ permissions: Array<"read" | "write" | "delete">;
29
+ hitlRequired: Array<"write" | "delete">;
30
+ oauth?: {
31
+ provider: string;
32
+ scopes: string[];
33
+ authUrl: string;
34
+ tokenUrl: string;
35
+ pkce: boolean;
36
+ };
37
+ syncInterval?: number;
38
+ tags?: string[];
39
+ minNimbusVersion: string;
40
+ }
41
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,WAAW,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC;IAChD,YAAY,EAAE,KAAK,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,OAAO,CAAC;KACf,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared types for Nimbus extensions
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@nimbus-dev/sdk",
3
+ "version": "1.1.2",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/nimbus-agent/Nimbus.git",
8
+ "directory": "packages/sdk"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "bun": "./src/index.ts",
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "./testing": {
21
+ "bun": "./src/testing/index.ts",
22
+ "types": "./dist/testing/index.d.ts",
23
+ "import": "./dist/testing/index.js",
24
+ "default": "./dist/testing/index.js"
25
+ },
26
+ "./ipc": {
27
+ "bun": "./src/ipc/index.ts",
28
+ "types": "./dist/ipc/index.d.ts",
29
+ "import": "./dist/ipc/index.js",
30
+ "default": "./dist/ipc/index.js"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "src"
36
+ ],
37
+ "scripts": {
38
+ "dev": "bun run --watch src/index.ts",
39
+ "build": "tsc --project tsconfig.build.json",
40
+ "typecheck": "tsc --noEmit",
41
+ "lint": "biome check src/",
42
+ "test": "bun test",
43
+ "clean": "rm -rf dist",
44
+ "prepublishOnly": "bun run build && bun run typecheck",
45
+ "test:coverage:sdk": "bun test --coverage src"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public",
49
+ "registry": "https://registry.npmjs.org/"
50
+ },
51
+ "devDependencies": {
52
+ "@types/bun": "latest",
53
+ "typescript": "^6.0.3"
54
+ }
55
+ }
@@ -0,0 +1,33 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { createScopedAuditLogger } from "./audit-logger.ts";
3
+
4
+ describe("createScopedAuditLogger", () => {
5
+ test("prefixes action with extension ID", async () => {
6
+ const calls: Array<{ action: string; payload: Record<string, unknown> }> = [];
7
+ const emit = async (action: string, payload: Record<string, unknown>): Promise<void> => {
8
+ calls.push({ action, payload });
9
+ };
10
+ const logger = createScopedAuditLogger("ext.my-connector", emit);
11
+ await logger.log("sync.completed", { items: 42 });
12
+ expect(calls).toHaveLength(1);
13
+ expect(calls[0]?.action).toBe("ext.my-connector:sync.completed");
14
+ expect(calls[0]?.payload).toEqual({ items: 42 });
15
+ });
16
+
17
+ test("rejects action IDs that already contain a colon", async () => {
18
+ const logger = createScopedAuditLogger("ext.foo", async () => {});
19
+ await expect(logger.log("already:scoped", {})).rejects.toThrow(/colon/);
20
+ });
21
+
22
+ test("rejects empty action ID", async () => {
23
+ const logger = createScopedAuditLogger("ext.foo", async () => {});
24
+ await expect(logger.log("", {})).rejects.toThrow(/empty/);
25
+ });
26
+
27
+ test("propagates emit errors unchanged", async () => {
28
+ const logger = createScopedAuditLogger("ext.foo", async () => {
29
+ throw new Error("downstream");
30
+ });
31
+ await expect(logger.log("x", {})).rejects.toThrow("downstream");
32
+ });
33
+ });
@@ -0,0 +1,23 @@
1
+ export type AuditEmit = (action: string, payload: Record<string, unknown>) => Promise<void>;
2
+
3
+ export interface AuditLogger {
4
+ log(action: string, payload: Record<string, unknown>): Promise<void>;
5
+ }
6
+
7
+ export function createScopedAuditLogger(extensionId: string, emit: AuditEmit): AuditLogger {
8
+ if (!extensionId || extensionId.trim().length === 0) {
9
+ throw new Error("extensionId must be non-empty");
10
+ }
11
+ return {
12
+ async log(action, payload) {
13
+ if (!action || action.length === 0) {
14
+ throw new Error("action must be non-empty");
15
+ }
16
+ if (action.includes(":")) {
17
+ throw new Error("action must not contain a colon (scoping prefix is added automatically)");
18
+ }
19
+ const scoped = `${extensionId}:${action}`;
20
+ await emit(scoped, payload);
21
+ },
22
+ };
23
+ }