@embassys/ambassador 0.0.0 → 0.2.6

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 (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +68 -2
  3. package/dist/agent-capabilities.d.ts +35 -0
  4. package/dist/agent-capabilities.js +221 -0
  5. package/dist/agent-capabilities.js.map +1 -0
  6. package/dist/ambassador-options.d.ts +9 -0
  7. package/dist/ambassador-options.js +31 -0
  8. package/dist/ambassador-options.js.map +1 -0
  9. package/dist/central-credential.d.ts +41 -0
  10. package/dist/central-credential.js +355 -0
  11. package/dist/central-credential.js.map +1 -0
  12. package/dist/central-enrollment.d.ts +31 -0
  13. package/dist/central-enrollment.js +278 -0
  14. package/dist/central-enrollment.js.map +1 -0
  15. package/dist/central-json.d.ts +8 -0
  16. package/dist/central-json.js +259 -0
  17. package/dist/central-json.js.map +1 -0
  18. package/dist/central-protected-transport.d.ts +21 -0
  19. package/dist/central-protected-transport.js +217 -0
  20. package/dist/central-protected-transport.js.map +1 -0
  21. package/dist/central-rest.d.ts +50 -0
  22. package/dist/central-rest.js +401 -0
  23. package/dist/central-rest.js.map +1 -0
  24. package/dist/cli.d.ts +26 -0
  25. package/dist/cli.js +131 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/credential-store.d.ts +18 -0
  28. package/dist/credential-store.js +517 -0
  29. package/dist/credential-store.js.map +1 -0
  30. package/dist/delivery-profile.d.ts +43 -0
  31. package/dist/delivery-profile.js +253 -0
  32. package/dist/delivery-profile.js.map +1 -0
  33. package/dist/direct-delivery.d.ts +38 -0
  34. package/dist/direct-delivery.js +315 -0
  35. package/dist/direct-delivery.js.map +1 -0
  36. package/dist/dpop.d.ts +28 -0
  37. package/dist/dpop.js +119 -0
  38. package/dist/dpop.js.map +1 -0
  39. package/dist/errors.d.ts +5 -0
  40. package/dist/errors.js +11 -0
  41. package/dist/errors.js.map +1 -0
  42. package/dist/gateway-application.d.ts +32 -0
  43. package/dist/gateway-application.js +256 -0
  44. package/dist/gateway-application.js.map +1 -0
  45. package/dist/gateway-paths.d.ts +10 -0
  46. package/dist/gateway-paths.js +28 -0
  47. package/dist/gateway-paths.js.map +1 -0
  48. package/dist/guided-registration.d.ts +23 -0
  49. package/dist/guided-registration.js +117 -0
  50. package/dist/guided-registration.js.map +1 -0
  51. package/dist/identity.d.ts +20 -0
  52. package/dist/identity.js +54 -0
  53. package/dist/identity.js.map +1 -0
  54. package/dist/local-mcp.d.ts +28 -0
  55. package/dist/local-mcp.js +420 -0
  56. package/dist/local-mcp.js.map +1 -0
  57. package/dist/local-tool-result.d.ts +4 -0
  58. package/dist/local-tool-result.js +17 -0
  59. package/dist/local-tool-result.js.map +1 -0
  60. package/dist/mcp-contract.d.ts +10 -0
  61. package/dist/mcp-contract.js +63 -0
  62. package/dist/mcp-contract.js.map +1 -0
  63. package/dist/notification-journal.d.ts +24 -0
  64. package/dist/notification-journal.js +194 -0
  65. package/dist/notification-journal.js.map +1 -0
  66. package/dist/notification-relay.d.ts +31 -0
  67. package/dist/notification-relay.js +203 -0
  68. package/dist/notification-relay.js.map +1 -0
  69. package/dist/process-lock.d.ts +8 -0
  70. package/dist/process-lock.js +122 -0
  71. package/dist/process-lock.js.map +1 -0
  72. package/dist/sqlite-artifact.d.ts +7 -0
  73. package/dist/sqlite-artifact.js +121 -0
  74. package/dist/sqlite-artifact.js.map +1 -0
  75. package/dist/webhook-delivery.d.ts +23 -0
  76. package/dist/webhook-delivery.js +122 -0
  77. package/dist/webhook-delivery.js.map +1 -0
  78. package/docs/getting-started-claude.md +94 -0
  79. package/docs/getting-started-codex.md +103 -0
  80. package/docs/getting-started-gemini.md +87 -0
  81. package/docs/getting-started-hermes.md +84 -0
  82. package/docs/getting-started-openclaw.md +83 -0
  83. package/docs/live-qualification.md +174 -0
  84. package/package.json +48 -7
  85. package/index.js +0 -1
@@ -0,0 +1,122 @@
1
+ import { lstatSync } from "node:fs";
2
+ import { mkdir } from "node:fs/promises";
3
+ import { basename, dirname, resolve } from "node:path";
4
+ import Database from "better-sqlite3";
5
+ import { GatewayError } from "./errors.js";
6
+ import { preparePrivateSqliteArtifact } from "./sqlite-artifact.js";
7
+ const LOCK_HANDOFF_TIMEOUT_MS = 1_000;
8
+ const activeLockKeys = new Set();
9
+ const pendingLockPaths = new Set();
10
+ function errorCode(error) {
11
+ if (error !== null && typeof error === "object" && "code" in error) {
12
+ return typeof error.code === "string" ? error.code : undefined;
13
+ }
14
+ return undefined;
15
+ }
16
+ function invalidLockArtifact() {
17
+ return new GatewayError("lock_invalid", "The Ambassador lock artifact is invalid", 7);
18
+ }
19
+ function daemonRunning() {
20
+ return new GatewayError("daemon_running", "Ambassador is already running", 7);
21
+ }
22
+ function pathKey(path) {
23
+ return process.platform === "win32" ? path.toLowerCase() : path;
24
+ }
25
+ function filesystemLockKey(path) {
26
+ try {
27
+ const stats = lstatSync(path, { bigint: true });
28
+ return `artifact:${stats.dev}:${stats.ino}`;
29
+ }
30
+ catch (error) {
31
+ if (errorCode(error) !== "ENOENT")
32
+ throw error;
33
+ const parentStats = lstatSync(dirname(path), { bigint: true });
34
+ return `new:${parentStats.dev}:${parentStats.ino}:${pathKey(basename(path))}`;
35
+ }
36
+ }
37
+ export class ProcessLock {
38
+ database;
39
+ keys;
40
+ released = false;
41
+ constructor(database, keys) {
42
+ this.database = database;
43
+ this.keys = keys;
44
+ }
45
+ static async acquire(path) {
46
+ const artifactPath = resolve(path);
47
+ const pendingPath = pathKey(artifactPath);
48
+ if (pendingLockPaths.has(pendingPath))
49
+ throw daemonRunning();
50
+ pendingLockPaths.add(pendingPath);
51
+ const keys = [];
52
+ try {
53
+ await mkdir(dirname(artifactPath), { recursive: true, mode: 0o700 });
54
+ const initialKey = filesystemLockKey(artifactPath);
55
+ if (activeLockKeys.has(initialKey))
56
+ throw daemonRunning();
57
+ activeLockKeys.add(initialKey);
58
+ keys.push(initialKey);
59
+ pendingLockPaths.delete(pendingPath);
60
+ const artifact = preparePrivateSqliteArtifact(artifactPath, invalidLockArtifact);
61
+ let database;
62
+ try {
63
+ const artifactKey = filesystemLockKey(artifactPath);
64
+ if (artifactKey !== initialKey) {
65
+ if (activeLockKeys.has(artifactKey))
66
+ throw daemonRunning();
67
+ activeLockKeys.add(artifactKey);
68
+ keys.push(artifactKey);
69
+ }
70
+ database = new Database(artifactPath, { timeout: LOCK_HANDOFF_TIMEOUT_MS });
71
+ artifact.validate();
72
+ // Closing another descriptor for this inode after BEGIN can release POSIX process locks.
73
+ artifact.releaseFile();
74
+ database.pragma(`busy_timeout = ${LOCK_HANDOFF_TIMEOUT_MS}`);
75
+ database.pragma("trusted_schema = OFF");
76
+ database.exec("BEGIN EXCLUSIVE");
77
+ artifact.validateDirectory();
78
+ return new ProcessLock(database, keys);
79
+ }
80
+ catch (error) {
81
+ database?.close();
82
+ const code = errorCode(error);
83
+ if (code?.startsWith("SQLITE_BUSY"))
84
+ throw daemonRunning();
85
+ if (code === "SQLITE_NOTADB" || code === "SQLITE_FORMAT" || code === "SQLITE_CORRUPT") {
86
+ throw invalidLockArtifact();
87
+ }
88
+ throw error;
89
+ }
90
+ finally {
91
+ artifact.close();
92
+ }
93
+ }
94
+ catch (error) {
95
+ pendingLockPaths.delete(pendingPath);
96
+ for (const key of keys)
97
+ activeLockKeys.delete(key);
98
+ if (errorCode(error) === "ENOENT" && keys.some((key) => key.startsWith("artifact:"))) {
99
+ throw daemonRunning();
100
+ }
101
+ throw error;
102
+ }
103
+ }
104
+ async release() {
105
+ if (this.released)
106
+ return;
107
+ this.released = true;
108
+ try {
109
+ this.database.exec("ROLLBACK");
110
+ }
111
+ finally {
112
+ try {
113
+ this.database.close();
114
+ }
115
+ finally {
116
+ for (const key of this.keys)
117
+ activeLockKeys.delete(key);
118
+ }
119
+ }
120
+ }
121
+ }
122
+ //# sourceMappingURL=process-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-lock.js","sourceRoot":"","sources":["../src/process-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface PreparedSqliteArtifact {
2
+ validate: () => void;
3
+ validateDirectory: () => void;
4
+ releaseFile: () => void;
5
+ close: () => void;
6
+ }
7
+ export declare function preparePrivateSqliteArtifact(path: string, invalidArtifact: () => Error): PreparedSqliteArtifact;
@@ -0,0 +1,121 @@
1
+ import { chmodSync, closeSync, constants, fchmodSync, fstatSync, lstatSync, openSync, } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ const POSIX = process.platform !== "win32";
4
+ function errorCode(error) {
5
+ return error !== null && typeof error === "object" && "code" in error
6
+ ? String(error.code)
7
+ : undefined;
8
+ }
9
+ function sameArtifact(left, right) {
10
+ return left.dev === right.dev && left.ino === right.ino;
11
+ }
12
+ function secureExistingFile(path, invalidArtifact) {
13
+ let pathStats;
14
+ try {
15
+ pathStats = lstatSync(path, { bigint: true });
16
+ }
17
+ catch (error) {
18
+ if (errorCode(error) === "ENOENT")
19
+ return;
20
+ throw error;
21
+ }
22
+ if (!pathStats.isFile() || pathStats.nlink !== 1n)
23
+ throw invalidArtifact();
24
+ if (POSIX)
25
+ chmodSync(path, 0o600);
26
+ const currentStats = lstatSync(path, { bigint: true });
27
+ if (!currentStats.isFile() ||
28
+ currentStats.nlink !== 1n ||
29
+ !sameArtifact(pathStats, currentStats)) {
30
+ throw invalidArtifact();
31
+ }
32
+ }
33
+ export function preparePrivateSqliteArtifact(path, invalidArtifact) {
34
+ const directoryPath = dirname(path);
35
+ const initialDirectoryStats = lstatSync(directoryPath, { bigint: true });
36
+ if (!initialDirectoryStats.isDirectory())
37
+ throw invalidArtifact();
38
+ let directoryDescriptor;
39
+ let fileDescriptor;
40
+ try {
41
+ if (POSIX) {
42
+ directoryDescriptor = openSync(directoryPath, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
43
+ const descriptorStats = fstatSync(directoryDescriptor, { bigint: true });
44
+ const pathStats = lstatSync(directoryPath, { bigint: true });
45
+ if (!descriptorStats.isDirectory() || !pathStats.isDirectory())
46
+ throw invalidArtifact();
47
+ if (!sameArtifact(descriptorStats, pathStats))
48
+ throw invalidArtifact();
49
+ if (typeof process.getuid === "function" &&
50
+ descriptorStats.uid !== BigInt(process.getuid())) {
51
+ throw invalidArtifact();
52
+ }
53
+ fchmodSync(directoryDescriptor, 0o700);
54
+ }
55
+ for (const suffix of ["-wal", "-shm", "-journal"]) {
56
+ secureExistingFile(`${path}${suffix}`, invalidArtifact);
57
+ }
58
+ try {
59
+ fileDescriptor = openSync(path, constants.O_CREAT | constants.O_EXCL | constants.O_RDWR, 0o600);
60
+ }
61
+ catch (error) {
62
+ if (errorCode(error) !== "EEXIST")
63
+ throw error;
64
+ const pathStats = lstatSync(path, { bigint: true });
65
+ if (!pathStats.isFile() || pathStats.nlink !== 1n)
66
+ throw invalidArtifact();
67
+ fileDescriptor = openSync(path, constants.O_RDWR | constants.O_NOFOLLOW);
68
+ }
69
+ const validateDirectory = () => {
70
+ const currentDirectoryStats = lstatSync(directoryPath, { bigint: true });
71
+ const expectedDirectoryStats = directoryDescriptor === undefined
72
+ ? initialDirectoryStats
73
+ : fstatSync(directoryDescriptor, { bigint: true });
74
+ if (!currentDirectoryStats.isDirectory() ||
75
+ !sameArtifact(expectedDirectoryStats, currentDirectoryStats)) {
76
+ throw invalidArtifact();
77
+ }
78
+ };
79
+ const validate = () => {
80
+ const descriptorStats = fstatSync(fileDescriptor, { bigint: true });
81
+ const pathStats = lstatSync(path, { bigint: true });
82
+ if (!descriptorStats.isFile() ||
83
+ !pathStats.isFile() ||
84
+ descriptorStats.nlink !== 1n ||
85
+ pathStats.nlink !== 1n ||
86
+ !sameArtifact(descriptorStats, pathStats)) {
87
+ throw invalidArtifact();
88
+ }
89
+ validateDirectory();
90
+ };
91
+ const releaseFile = () => {
92
+ if (fileDescriptor !== undefined) {
93
+ closeSync(fileDescriptor);
94
+ fileDescriptor = undefined;
95
+ }
96
+ };
97
+ validate();
98
+ if (POSIX)
99
+ fchmodSync(fileDescriptor, 0o600);
100
+ return {
101
+ validate,
102
+ validateDirectory,
103
+ releaseFile,
104
+ close: () => {
105
+ releaseFile();
106
+ if (directoryDescriptor !== undefined) {
107
+ closeSync(directoryDescriptor);
108
+ directoryDescriptor = undefined;
109
+ }
110
+ },
111
+ };
112
+ }
113
+ catch (error) {
114
+ if (fileDescriptor !== undefined)
115
+ closeSync(fileDescriptor);
116
+ if (directoryDescriptor !== undefined)
117
+ closeSync(directoryDescriptor);
118
+ throw error;
119
+ }
120
+ }
121
+ //# sourceMappingURL=sqlite-artifact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-artifact.js","sourceRoot":"","sources":["../src/sqlite-artifact.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EACT,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AAS3C,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK;QACnE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QACpB,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CAAC,IAAiB,EAAE,KAAkB;IACzD,OAAO,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,CAAC;AAC1D,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY,EAAE,eAA4B;IACpE,IAAI,SAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,QAAQ;YAAE,OAAO;QAC1C,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,SAAS,CAAC,KAAK,KAAK,EAAE;QAAE,MAAM,eAAe,EAAE,CAAC;IAE3E,IAAI,KAAK;QAAE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClC,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,IACE,CAAC,YAAY,CAAC,MAAM,EAAE;QACtB,YAAY,CAAC,KAAK,KAAK,EAAE;QACzB,CAAC,YAAY,CAAC,SAAS,EAAE,YAAY,CAAC,EACtC,CAAC;QACD,MAAM,eAAe,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,IAAY,EACZ,eAA4B;IAE5B,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,qBAAqB,GAAG,SAAS,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACzE,IAAI,CAAC,qBAAqB,CAAC,WAAW,EAAE;QAAE,MAAM,eAAe,EAAE,CAAC;IAElE,IAAI,mBAAuC,CAAC;IAC5C,IAAI,cAAkC,CAAC;IACvC,IAAI,CAAC;QACH,IAAI,KAAK,EAAE,CAAC;YACV,mBAAmB,GAAG,QAAQ,CAC5B,aAAa,EACb,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,WAAW,GAAG,SAAS,CAAC,UAAU,CAClE,CAAC;YACF,MAAM,eAAe,GAAG,SAAS,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACzE,MAAM,SAAS,GAAG,SAAS,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7D,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE;gBAAE,MAAM,eAAe,EAAE,CAAC;YACxF,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,SAAS,CAAC;gBAAE,MAAM,eAAe,EAAE,CAAC;YACvE,IACE,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU;gBACpC,eAAe,CAAC,GAAG,KAAK,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAChD,CAAC;gBACD,MAAM,eAAe,EAAE,CAAC;YAC1B,CAAC;YACD,UAAU,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;QACzC,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC;YAClD,kBAAkB,CAAC,GAAG,IAAI,GAAG,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,CAAC;YACH,cAAc,GAAG,QAAQ,CACvB,IAAI,EACJ,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,EACvD,KAAK,CACN,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YAC/C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,SAAS,CAAC,KAAK,KAAK,EAAE;gBAAE,MAAM,eAAe,EAAE,CAAC;YAC3E,cAAc,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,iBAAiB,GAAG,GAAG,EAAE;YAC7B,MAAM,qBAAqB,GAAG,SAAS,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACzE,MAAM,sBAAsB,GAC1B,mBAAmB,KAAK,SAAS;gBAC/B,CAAC,CAAC,qBAAqB;gBACvB,CAAC,CAAC,SAAS,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACvD,IACE,CAAC,qBAAqB,CAAC,WAAW,EAAE;gBACpC,CAAC,YAAY,CAAC,sBAAsB,EAAE,qBAAqB,CAAC,EAC5D,CAAC;gBACD,MAAM,eAAe,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC;QACF,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,MAAM,eAAe,GAAG,SAAS,CAAC,cAAwB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9E,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,IACE,CAAC,eAAe,CAAC,MAAM,EAAE;gBACzB,CAAC,SAAS,CAAC,MAAM,EAAE;gBACnB,eAAe,CAAC,KAAK,KAAK,EAAE;gBAC5B,SAAS,CAAC,KAAK,KAAK,EAAE;gBACtB,CAAC,YAAY,CAAC,eAAe,EAAE,SAAS,CAAC,EACzC,CAAC;gBACD,MAAM,eAAe,EAAE,CAAC;YAC1B,CAAC;YACD,iBAAiB,EAAE,CAAC;QACtB,CAAC,CAAC;QACF,MAAM,WAAW,GAAG,GAAG,EAAE;YACvB,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;gBACjC,SAAS,CAAC,cAAc,CAAC,CAAC;gBAC1B,cAAc,GAAG,SAAS,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC;QAEF,QAAQ,EAAE,CAAC;QACX,IAAI,KAAK;YAAE,UAAU,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;QAE7C,OAAO;YACL,QAAQ;YACR,iBAAiB;YACjB,WAAW;YACX,KAAK,EAAE,GAAG,EAAE;gBACV,WAAW,EAAE,CAAC;gBACd,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;oBACtC,SAAS,CAAC,mBAAmB,CAAC,CAAC;oBAC/B,mBAAmB,GAAG,SAAS,CAAC;gBAClC,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,cAAc,KAAK,SAAS;YAAE,SAAS,CAAC,cAAc,CAAC,CAAC;QAC5D,IAAI,mBAAmB,KAAK,SAAS;YAAE,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACtE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { CentralMessage } from "./central-rest.js";
2
+ export type WebhookDeliveryErrorCode = "delivery_failed" | "invalid_configuration";
3
+ export declare class WebhookDeliveryError extends Error {
4
+ readonly code: WebhookDeliveryErrorCode;
5
+ constructor(code: WebhookDeliveryErrorCode);
6
+ }
7
+ export interface WebhookDeliveryTargetOptions {
8
+ readonly url: string;
9
+ readonly secret: string;
10
+ readonly fetch?: typeof fetch;
11
+ readonly now?: () => number;
12
+ readonly deadlineMs?: number;
13
+ readonly maximumAttempts?: number;
14
+ readonly retryDelayMs?: number;
15
+ }
16
+ export declare class WebhookDeliveryTarget {
17
+ #private;
18
+ constructor(options: WebhookDeliveryTargetOptions);
19
+ deliver(message: CentralMessage, signal: AbortSignal): Promise<{
20
+ readonly status: "accepted";
21
+ }>;
22
+ close(): Promise<void>;
23
+ }
@@ -0,0 +1,122 @@
1
+ import { createHmac, randomUUID } from "node:crypto";
2
+ import { setTimeout as delay } from "node:timers/promises";
3
+ import { canonicalWebhookUrl } from "./delivery-profile.js";
4
+ const DEFAULT_DEADLINE_MS = 10_000;
5
+ const DEFAULT_MAXIMUM_ATTEMPTS = 3;
6
+ const DEFAULT_RETRY_DELAY_MS = 250;
7
+ const MAX_MESSAGE_BYTES = 512 * 1024;
8
+ export class WebhookDeliveryError extends Error {
9
+ code;
10
+ constructor(code) {
11
+ super("Webhook delivery failed");
12
+ this.code = code;
13
+ this.name = "WebhookDeliveryError";
14
+ }
15
+ }
16
+ function positiveInteger(value) {
17
+ return Number.isSafeInteger(value) && value > 0;
18
+ }
19
+ async function cancel(response) {
20
+ await response.body?.cancel().catch(() => undefined);
21
+ }
22
+ export class WebhookDeliveryTarget {
23
+ #url;
24
+ #secret;
25
+ #authorization;
26
+ #fetch;
27
+ #now;
28
+ #deadlineMs;
29
+ #maximumAttempts;
30
+ #retryDelayMs;
31
+ #lifetime = new AbortController();
32
+ constructor(options) {
33
+ try {
34
+ this.#url = canonicalWebhookUrl(options.url);
35
+ }
36
+ catch {
37
+ throw new WebhookDeliveryError("invalid_configuration");
38
+ }
39
+ if (!/^[\x21-\x7e]{32,256}$/u.test(options.secret)) {
40
+ throw new WebhookDeliveryError("invalid_configuration");
41
+ }
42
+ this.#secret = options.secret;
43
+ this.#authorization = `Bearer ${options.secret}`;
44
+ this.#fetch = options.fetch ?? globalThis.fetch;
45
+ this.#now = options.now ?? Date.now;
46
+ this.#deadlineMs = options.deadlineMs ?? DEFAULT_DEADLINE_MS;
47
+ this.#maximumAttempts = options.maximumAttempts ?? DEFAULT_MAXIMUM_ATTEMPTS;
48
+ this.#retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
49
+ if (!positiveInteger(this.#deadlineMs) ||
50
+ !positiveInteger(this.#maximumAttempts) ||
51
+ !positiveInteger(this.#retryDelayMs)) {
52
+ throw new WebhookDeliveryError("invalid_configuration");
53
+ }
54
+ }
55
+ async deliver(message, signal) {
56
+ let body;
57
+ try {
58
+ body = JSON.stringify(message);
59
+ }
60
+ catch {
61
+ throw new WebhookDeliveryError("delivery_failed");
62
+ }
63
+ if (Buffer.byteLength(body, "utf8") > MAX_MESSAGE_BYTES) {
64
+ throw new WebhookDeliveryError("delivery_failed");
65
+ }
66
+ const requestId = message.id ?? randomUUID();
67
+ const deadline = AbortSignal.timeout(this.#deadlineMs);
68
+ const requestSignal = AbortSignal.any([signal, deadline, this.#lifetime.signal]);
69
+ for (let attempt = 1; attempt <= this.#maximumAttempts; attempt += 1) {
70
+ if (requestSignal.aborted)
71
+ throw new WebhookDeliveryError("delivery_failed");
72
+ const now = this.#now();
73
+ if (!Number.isSafeInteger(now) || now < 0) {
74
+ throw new WebhookDeliveryError("invalid_configuration");
75
+ }
76
+ const timestamp = String(Math.floor(now / 1_000));
77
+ const signature = createHmac("sha256", this.#secret)
78
+ .update(timestamp, "ascii")
79
+ .update(".", "ascii")
80
+ .update(body, "utf8")
81
+ .digest("hex");
82
+ try {
83
+ const response = await this.#fetch(this.#url, {
84
+ method: "POST",
85
+ headers: {
86
+ authorization: this.#authorization,
87
+ "content-type": "application/json",
88
+ "idempotency-key": requestId,
89
+ "x-request-id": requestId,
90
+ "x-webhook-timestamp": timestamp,
91
+ "x-webhook-signature-v2": signature,
92
+ },
93
+ body,
94
+ credentials: "omit",
95
+ redirect: "manual",
96
+ signal: requestSignal,
97
+ });
98
+ const accepted = response.status >= 200 && response.status < 300;
99
+ await cancel(response);
100
+ if (accepted)
101
+ return { status: "accepted" };
102
+ }
103
+ catch {
104
+ if (requestSignal.aborted)
105
+ throw new WebhookDeliveryError("delivery_failed");
106
+ }
107
+ if (attempt < this.#maximumAttempts) {
108
+ try {
109
+ await delay(this.#retryDelayMs, undefined, { signal: requestSignal });
110
+ }
111
+ catch {
112
+ throw new WebhookDeliveryError("delivery_failed");
113
+ }
114
+ }
115
+ }
116
+ throw new WebhookDeliveryError("delivery_failed");
117
+ }
118
+ async close() {
119
+ this.#lifetime.abort();
120
+ }
121
+ }
122
+ //# sourceMappingURL=webhook-delivery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-delivery.js","sourceRoot":"","sources":["../src/webhook-delivery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ # Get started with Claude Code
2
+
3
+ Release target: `@embassys/ambassador@0.2.6`. The profile is source-reviewed
4
+ against Claude Code 2.1.257 and 2.1.258 plus
5
+ `@agentclientprotocol/claude-agent-acp` 0.73.0, and its exact ACP initialization
6
+ contract passed. Real-agent direct and webhook qualification remain open under
7
+ the one-release qualification exception in ADR 0015.
8
+
9
+ Ambassador enables Claude Code only for this exact contract:
10
+
11
+ | Field | Value |
12
+ | --- | --- |
13
+ | MCP `clientInfo` | `claude-code` / `2.1.257` or `2.1.258` |
14
+ | Delivery modes | direct and webhook |
15
+ | Direct command | `claude-agent-acp` |
16
+ | ACP adapter | `@agentclientprotocol/claude-agent-acp` / `0.73.0` |
17
+ | Accepted ACP `agentInfo` | `@agentclientprotocol/claude-agent-acp` / `0.73.0` |
18
+ | Ambassador MCP in the direct session | ACP HTTP MCP injection |
19
+
20
+ Ambassador uses the existing
21
+ [`agentclientprotocol/claude-agent-acp`](https://github.com/agentclientprotocol/claude-agent-acp)
22
+ adapter. It does not contain a Claude adapter. The reviewed release is
23
+ [`v0.73.0`](https://github.com/agentclientprotocol/claude-agent-acp/tree/v0.73.0).
24
+ Its
25
+ [`package.json`](https://github.com/agentclientprotocol/claude-agent-acp/blob/v0.73.0/package.json)
26
+ defines the `claude-agent-acp` command, pins `@agentclientprotocol/sdk` 1.4.0,
27
+ and pins `@anthropic-ai/claude-agent-sdk` 0.3.257. Its
28
+ [`acp-agent.ts`](https://github.com/agentclientprotocol/claude-agent-acp/blob/v0.73.0/src/acp-agent.ts)
29
+ returns the adapter identity and maps HTTP MCP session configuration into the
30
+ Claude Agent SDK.
31
+
32
+ The selected SDK contains Claude Code 2.1.257. The separately reviewed current
33
+ Claude Code package is 2.1.258. Both exact versions use the `claude-code` MCP
34
+ identity and map to the same fixed profile. Other Claude Code or adapter
35
+ versions fail closed until the registry is reviewed and updated.
36
+
37
+ ## Setup
38
+
39
+ 1. Install Node.js `>=24.19.0 <25` and
40
+ `@agentclientprotocol/claude-agent-acp` 0.73.0 through its normal package
41
+ installation process. Ambassador never installs or updates the adapter.
42
+ 2. Authenticate Claude through normal Claude Code setup, or set one approved
43
+ adapter authentication variable outside chat: `ANTHROPIC_API_KEY`,
44
+ `ANTHROPIC_AUTH_TOKEN`, or `CLAUDE_CODE_OAUTH_TOKEN`.
45
+ 3. Generate the local MCP token without putting its value in chat or a command
46
+ argument:
47
+
48
+ ```sh
49
+ export AMBASSADOR_LOCAL_TOKEN="$(
50
+ node -e "process.stdout.write(require('node:crypto').randomBytes(24).toString('hex'))"
51
+ )"
52
+ ```
53
+
54
+ 4. If you may choose webhook delivery, create its secret in the same shell
55
+ before starting Ambassador:
56
+
57
+ ```sh
58
+ export AMBASSADOR_WEBHOOK_SECRET="$(
59
+ node -e "process.stdout.write(require('node:crypto').randomBytes(24).toString('hex'))"
60
+ )"
61
+ ```
62
+
63
+ 5. From the directory the direct agent may access, start the exact release and
64
+ keep it running in the foreground:
65
+
66
+ ```sh
67
+ npx --yes @embassys/ambassador@0.2.6 start \
68
+ --local-token-env=AMBASSADOR_LOCAL_TOKEN
69
+ ```
70
+
71
+ 6. Configure Claude Code's MCP client to use the loopback endpoint printed by
72
+ Ambassador, with a bearer token read from `AMBASSADOR_LOCAL_TOKEN`. Use the
73
+ provider's normal MCP configuration mechanism; do not copy the token value
74
+ into chat.
75
+ 7. Ask Claude Code to register with your email and optional display name. The
76
+ first `register_agent` call contains `email` and, if wanted, `display_name`.
77
+ Ambassador recognizes the Claude Code profile and asks direct versus
78
+ webhook, with direct as the default.
79
+ 8. For direct mode, choose direct. The follow-up repeats the same `email` and
80
+ optional `display_name` and adds `delivery: {"mode":"direct"}`. Ambassador
81
+ starts `claude-agent-acp` and injects its authenticated HTTP MCP server into
82
+ the new ACP session.
83
+ 9. For webhook mode, choose webhook and provide the HTTPS receiver URL plus
84
+ the environment-variable name `AMBASSADOR_WEBHOOK_SECRET`. The follow-up
85
+ repeats the registration fields and adds `delivery.mode`, `delivery.url`,
86
+ and `delivery.secret_env`; it never sends the secret value.
87
+ 10. Enter the email verification code when Claude Code asks for it. Claude
88
+ Code calls `verify_email` with that `email` and six-digit `code`; the
89
+ central token and DPoP key stay inside Ambassador.
90
+
91
+ Ambassador does not pass `CLAUDE_CODE_EXECUTABLE` or another executable
92
+ override. It never receives Claude account credentials, the central token, or
93
+ the DPoP private key. Never put the local token or a provider credential in
94
+ chat or registration arguments.
@@ -0,0 +1,103 @@
1
+ # Get started with Codex
2
+
3
+ Release target: `@embassys/ambassador@0.2.6`. The profile is source-reviewed
4
+ against Codex 0.149.0 and 0.152.1 plus
5
+ `@agentclientprotocol/codex-acp` 1.8.0. The exact ACP initialization contract
6
+ and direct live-central flow passed. Webhook qualification and a repeat of the
7
+ direct case on supported Node remain open under the one-release qualification
8
+ exception in ADR 0015.
9
+
10
+ Ambassador enables Codex only for this exact contract:
11
+
12
+ | Field | Value |
13
+ | --- | --- |
14
+ | MCP `clientInfo` | `codex-mcp-client` / `0.149.0` or `0.152.1` |
15
+ | Delivery modes | direct and webhook |
16
+ | Direct command | `codex-acp` |
17
+ | ACP adapter | `@agentclientprotocol/codex-acp` / `1.8.0` |
18
+ | Accepted ACP `agentInfo` | `@agentclientprotocol/codex-acp` / `1.8.0` |
19
+ | Ambassador MCP in the direct session | ACP HTTP MCP injection |
20
+
21
+ Ambassador uses the existing
22
+ [`agentclientprotocol/codex-acp`](https://github.com/agentclientprotocol/codex-acp)
23
+ adapter. It does not contain a Codex adapter. The reviewed adapter revision is
24
+ [`87997e2627e8fa246a49de533c612f6196c4004e`](https://github.com/agentclientprotocol/codex-acp/tree/87997e2627e8fa246a49de533c612f6196c4004e).
25
+ Its
26
+ [`package.json`](https://github.com/agentclientprotocol/codex-acp/blob/87997e2627e8fa246a49de533c612f6196c4004e/package.json)
27
+ defines the `codex-acp` command and exact adapter version. Its
28
+ [`CodexAcpServer.ts`](https://github.com/agentclientprotocol/codex-acp/blob/87997e2627e8fa246a49de533c612f6196c4004e/src/CodexAcpServer.ts)
29
+ returns the ACP identity, and
30
+ [`CodexAcpClient.ts`](https://github.com/agentclientprotocol/codex-acp/blob/87997e2627e8fa246a49de533c612f6196c4004e/src/CodexAcpClient.ts)
31
+ maps session MCP configuration into Codex App Server.
32
+
33
+ The Codex MCP alias comes from
34
+ [`rmcp_client.rs`](https://github.com/openai/codex/blob/dcfcb570b2cd0a2500b1d47a7b04a7cb1b0a0bd2/codex-rs/codex-mcp/src/rmcp_client.rs).
35
+ OpenAI documents App Server as the protocol for embedding Codex in another
36
+ product in the
37
+ [`Codex App Server` documentation](https://learn.chatgpt.com/docs/app-server).
38
+ Later Codex or adapter versions fail closed until the registry is reviewed and
39
+ updated.
40
+
41
+ ## Setup
42
+
43
+ 1. Install Node.js `>=24.19.0 <25` and
44
+ `@agentclientprotocol/codex-acp` 1.8.0 through its normal package
45
+ installation process. Ambassador never installs or updates the adapter.
46
+ 2. Authenticate Codex through the adapter's normal ChatGPT login, or set
47
+ `CODEX_API_KEY` or `OPENAI_API_KEY` outside chat. The adapter owns provider
48
+ authentication.
49
+ 3. Generate the local MCP token without putting its value in chat or a command
50
+ argument:
51
+
52
+ ```sh
53
+ export AMBASSADOR_LOCAL_TOKEN="$(
54
+ node -e "process.stdout.write(require('node:crypto').randomBytes(24).toString('hex'))"
55
+ )"
56
+ ```
57
+
58
+ 4. If you may choose webhook delivery, create its secret in the same shell
59
+ before starting Ambassador:
60
+
61
+ ```sh
62
+ export AMBASSADOR_WEBHOOK_SECRET="$(
63
+ node -e "process.stdout.write(require('node:crypto').randomBytes(24).toString('hex'))"
64
+ )"
65
+ ```
66
+
67
+ 5. From the directory the direct agent may access, start the exact release and
68
+ keep it running in the foreground:
69
+
70
+ ```sh
71
+ npx --yes @embassys/ambassador@0.2.6 start \
72
+ --local-token-env=AMBASSADOR_LOCAL_TOKEN
73
+ ```
74
+
75
+ 6. In a shell where `AMBASSADOR_LOCAL_TOKEN` is available, configure Codex to
76
+ use the endpoint printed by Ambassador:
77
+
78
+ ```sh
79
+ codex mcp add ambassador \
80
+ --url http://127.0.0.1:8787/mcp \
81
+ --bearer-token-env-var AMBASSADOR_LOCAL_TOKEN
82
+ ```
83
+
84
+ 7. Ask Codex to register with your email and optional display name. The first
85
+ `register_agent` call contains `email` and, if wanted, `display_name`.
86
+ Ambassador recognizes the Codex MCP profile and asks direct versus webhook,
87
+ with direct as the default.
88
+ 8. For direct mode, choose direct. The follow-up repeats the same `email` and
89
+ optional `display_name` and adds `delivery: {"mode":"direct"}`. Ambassador
90
+ starts `codex-acp` and injects its authenticated HTTP MCP server into the new
91
+ ACP session.
92
+ 9. For webhook mode, choose webhook and provide the HTTPS receiver URL plus
93
+ the environment-variable name `AMBASSADOR_WEBHOOK_SECRET`. The follow-up
94
+ repeats the registration fields and adds `delivery.mode`, `delivery.url`,
95
+ and `delivery.secret_env`; it never sends the secret value.
96
+ 10. Enter the email verification code when Codex asks for it. Codex calls
97
+ `verify_email` with that `email` and six-digit `code`; the central token and
98
+ DPoP key stay inside Ambassador.
99
+
100
+ Ambassador does not pass `CODEX_PATH`, `CODEX_CONFIG`, or another executable or
101
+ session override. It never receives Codex account credentials, the central
102
+ token, or the DPoP private key. Never put the local token or a provider API key
103
+ in chat or registration arguments.