@embassys/ambassador 0.2.8 → 0.2.10

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 (53) hide show
  1. package/README.md +37 -8
  2. package/dist/agent-capabilities.d.ts +7 -2
  3. package/dist/agent-capabilities.js +73 -25
  4. package/dist/agent-capabilities.js.map +1 -1
  5. package/dist/ambassador-options.d.ts +4 -0
  6. package/dist/ambassador-options.js +7 -1
  7. package/dist/ambassador-options.js.map +1 -1
  8. package/dist/central-enrollment.js +1 -5
  9. package/dist/central-enrollment.js.map +1 -1
  10. package/dist/cli.js +17 -2
  11. package/dist/cli.js.map +1 -1
  12. package/dist/credential-store.d.ts +4 -4
  13. package/dist/credential-store.js +6 -98
  14. package/dist/credential-store.js.map +1 -1
  15. package/dist/delivery-profile.d.ts +10 -7
  16. package/dist/delivery-profile.js +62 -44
  17. package/dist/delivery-profile.js.map +1 -1
  18. package/dist/direct-delivery.d.ts +3 -1
  19. package/dist/direct-delivery.js +116 -15
  20. package/dist/direct-delivery.js.map +1 -1
  21. package/dist/gateway-application.d.ts +4 -0
  22. package/dist/gateway-application.js +14 -6
  23. package/dist/gateway-application.js.map +1 -1
  24. package/dist/gateway-paths.d.ts +2 -0
  25. package/dist/gateway-paths.js +2 -0
  26. package/dist/gateway-paths.js.map +1 -1
  27. package/dist/guided-registration.d.ts +2 -1
  28. package/dist/guided-registration.js +15 -7
  29. package/dist/guided-registration.js.map +1 -1
  30. package/dist/process-lock.js +1 -0
  31. package/dist/process-lock.js.map +1 -1
  32. package/dist/sqlite-artifact.d.ts +9 -1
  33. package/dist/sqlite-artifact.js +23 -8
  34. package/dist/sqlite-artifact.js.map +1 -1
  35. package/dist/webhook-secret-store.d.ts +16 -0
  36. package/dist/webhook-secret-store.js +49 -0
  37. package/dist/webhook-secret-store.js.map +1 -0
  38. package/dist/windows-access-control.d.ts +6 -0
  39. package/dist/windows-access-control.js +161 -0
  40. package/dist/windows-access-control.js.map +1 -0
  41. package/docs/development-reset.md +27 -0
  42. package/docs/getting-started-claude.md +8 -6
  43. package/docs/getting-started-codex.md +7 -6
  44. package/docs/getting-started-gemini.md +5 -4
  45. package/docs/getting-started-hermes.md +78 -27
  46. package/docs/getting-started-openclaw.md +87 -24
  47. package/docs/live-qualification.md +89 -16
  48. package/integrations/openclaw-ambassador/index.mjs +171 -0
  49. package/integrations/openclaw-ambassador/openclaw.plugin.json +73 -0
  50. package/integrations/openclaw-ambassador/package.json +11 -0
  51. package/integrations/openclaw-ambassador/receiver.d.mts +43 -0
  52. package/integrations/openclaw-ambassador/receiver.mjs +164 -0
  53. package/package.json +3 -1
@@ -1,6 +1,6 @@
1
1
  import { chmodSync, closeSync, constants, fchmodSync, fstatSync, lstatSync, openSync, } from "node:fs";
2
2
  import { dirname } from "node:path";
3
- const POSIX = process.platform !== "win32";
3
+ import { secureWindowsArtifactSync } from "./windows-access-control.js";
4
4
  function errorCode(error) {
5
5
  return error !== null && typeof error === "object" && "code" in error
6
6
  ? String(error.code)
@@ -9,7 +9,7 @@ function errorCode(error) {
9
9
  function sameArtifact(left, right) {
10
10
  return left.dev === right.dev && left.ino === right.ino;
11
11
  }
12
- function secureExistingFile(path, invalidArtifact) {
12
+ function secureExistingFile(path, invalidArtifact, platform, windowsAccessControl) {
13
13
  let pathStats;
14
14
  try {
15
15
  pathStats = lstatSync(path, { bigint: true });
@@ -21,7 +21,9 @@ function secureExistingFile(path, invalidArtifact) {
21
21
  }
22
22
  if (!pathStats.isFile() || pathStats.nlink !== 1n)
23
23
  throw invalidArtifact();
24
- if (POSIX)
24
+ if (platform === "win32")
25
+ windowsAccessControl?.secure(path, "file");
26
+ else
25
27
  chmodSync(path, 0o600);
26
28
  const currentStats = lstatSync(path, { bigint: true });
27
29
  if (!currentStats.isFile() ||
@@ -30,15 +32,21 @@ function secureExistingFile(path, invalidArtifact) {
30
32
  throw invalidArtifact();
31
33
  }
32
34
  }
33
- export function preparePrivateSqliteArtifact(path, invalidArtifact) {
35
+ export function preparePrivateSqliteArtifact(path, invalidArtifact, options = {}) {
36
+ const platform = options.platform ?? process.platform;
37
+ const windowsAccessControl = platform === "win32"
38
+ ? (options.windowsAccessControl ?? { secure: secureWindowsArtifactSync })
39
+ : undefined;
34
40
  const directoryPath = dirname(path);
35
41
  const initialDirectoryStats = lstatSync(directoryPath, { bigint: true });
36
42
  if (!initialDirectoryStats.isDirectory())
37
43
  throw invalidArtifact();
44
+ if (platform === "win32")
45
+ windowsAccessControl?.secure(directoryPath, "directory");
38
46
  let directoryDescriptor;
39
47
  let fileDescriptor;
40
48
  try {
41
- if (POSIX) {
49
+ if (platform !== "win32") {
42
50
  directoryDescriptor = openSync(directoryPath, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
43
51
  const descriptorStats = fstatSync(directoryDescriptor, { bigint: true });
44
52
  const pathStats = lstatSync(directoryPath, { bigint: true });
@@ -53,10 +61,12 @@ export function preparePrivateSqliteArtifact(path, invalidArtifact) {
53
61
  fchmodSync(directoryDescriptor, 0o700);
54
62
  }
55
63
  for (const suffix of ["-wal", "-shm", "-journal"]) {
56
- secureExistingFile(`${path}${suffix}`, invalidArtifact);
64
+ secureExistingFile(`${path}${suffix}`, invalidArtifact, platform, windowsAccessControl);
57
65
  }
66
+ let created = false;
58
67
  try {
59
68
  fileDescriptor = openSync(path, constants.O_CREAT | constants.O_EXCL | constants.O_RDWR, 0o600);
69
+ created = true;
60
70
  }
61
71
  catch (error) {
62
72
  if (errorCode(error) !== "EEXIST")
@@ -64,8 +74,13 @@ export function preparePrivateSqliteArtifact(path, invalidArtifact) {
64
74
  const pathStats = lstatSync(path, { bigint: true });
65
75
  if (!pathStats.isFile() || pathStats.nlink !== 1n)
66
76
  throw invalidArtifact();
67
- fileDescriptor = openSync(path, constants.O_RDWR | constants.O_NOFOLLOW);
77
+ if (platform === "win32")
78
+ windowsAccessControl?.secure(path, "file");
79
+ const noFollow = platform === "win32" ? 0 : constants.O_NOFOLLOW;
80
+ fileDescriptor = openSync(path, constants.O_RDWR | noFollow);
68
81
  }
82
+ if (created && platform === "win32")
83
+ windowsAccessControl?.secure(path, "file");
69
84
  const validateDirectory = () => {
70
85
  const currentDirectoryStats = lstatSync(directoryPath, { bigint: true });
71
86
  const expectedDirectoryStats = directoryDescriptor === undefined
@@ -95,7 +110,7 @@ export function preparePrivateSqliteArtifact(path, invalidArtifact) {
95
110
  }
96
111
  };
97
112
  validate();
98
- if (POSIX)
113
+ if (platform !== "win32")
99
114
  fchmodSync(fileDescriptor, 0o600);
100
115
  return {
101
116
  validate,
@@ -1 +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"}
1
+ {"version":3,"file":"sqlite-artifact.js","sourceRoot":"","sources":["../src/sqlite-artifact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type EncryptedFileCredentialStoreOptions } from "./credential-store.js";
2
+ export interface WebhookSecretStore {
3
+ load(): Promise<string | undefined>;
4
+ createOrLoad(): Promise<string>;
5
+ }
6
+ export interface EncryptedFileWebhookSecretStoreOptions {
7
+ readonly platform?: NodeJS.Platform;
8
+ readonly windowsAccessControl?: EncryptedFileCredentialStoreOptions["windowsAccessControl"];
9
+ readonly scope?: string;
10
+ }
11
+ export declare class EncryptedFileWebhookSecretStore implements WebhookSecretStore {
12
+ #private;
13
+ constructor(path: string, keyPath: string, options?: EncryptedFileWebhookSecretStoreOptions);
14
+ load(): Promise<string | undefined>;
15
+ createOrLoad(): Promise<string>;
16
+ }
@@ -0,0 +1,49 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ import { EncryptedFileCredentialStore, } from "./credential-store.js";
4
+ const SECRET = /^[a-f0-9]{48}$/u;
5
+ const DEFAULT_SCOPE = '{"kind":"ambassador-webhook-secret","version":1}';
6
+ const creations = new Map();
7
+ function validateWebhookSecret(value) {
8
+ if (!SECRET.test(value))
9
+ throw new Error("The webhook secret store is invalid");
10
+ }
11
+ export class EncryptedFileWebhookSecretStore {
12
+ #path;
13
+ #store;
14
+ constructor(path, keyPath, options = {}) {
15
+ this.#path = resolve(path);
16
+ this.#store = new EncryptedFileCredentialStore(path, keyPath, options.scope ?? DEFAULT_SCOPE, {
17
+ ...(options.platform === undefined ? {} : { platform: options.platform }),
18
+ ...(options.windowsAccessControl === undefined
19
+ ? {}
20
+ : { windowsAccessControl: options.windowsAccessControl }),
21
+ validatePlaintext: validateWebhookSecret,
22
+ });
23
+ }
24
+ async load() {
25
+ return this.#store.load();
26
+ }
27
+ async createOrLoad() {
28
+ const existing = creations.get(this.#path);
29
+ if (existing !== undefined)
30
+ return existing;
31
+ const creation = this.#createOrLoad();
32
+ creations.set(this.#path, creation);
33
+ try {
34
+ return await creation;
35
+ }
36
+ finally {
37
+ creations.delete(this.#path);
38
+ }
39
+ }
40
+ async #createOrLoad() {
41
+ const stored = await this.#store.load();
42
+ if (stored !== undefined)
43
+ return stored;
44
+ const created = randomBytes(24).toString("hex");
45
+ await this.#store.save(created);
46
+ return created;
47
+ }
48
+ }
49
+ //# sourceMappingURL=webhook-secret-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-secret-store.js","sourceRoot":"","sources":["../src/webhook-secret-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EACL,4BAA4B,GAE7B,MAAM,uBAAuB,CAAC;AAE/B,MAAM,MAAM,GAAG,iBAAiB,CAAC;AACjC,MAAM,aAAa,GAAG,kDAAkD,CAAC;AACzE,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;AAarD,SAAS,qBAAqB,CAAC,KAAa;IAC1C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,OAAO,+BAA+B;IACjC,KAAK,CAAS;IACd,MAAM,CAA+B;IAE9C,YAAY,IAAY,EAAE,OAAe,EAAE,OAAO,GAA2C,EAAE;QAC7F,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,IAAI,4BAA4B,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,aAAa,EAAE;YAC5F,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;YACzE,GAAG,CAAC,OAAO,CAAC,oBAAoB,KAAK,SAAS;gBAC5C,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAC3D,iBAAiB,EAAE,qBAAqB;SACzC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,QAAQ,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACtC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC;QACxB,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QACxC,MAAM,OAAO,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ export type WindowsArtifactKind = "directory" | "file";
2
+ export interface WindowsAccessControl {
3
+ secure(path: string, kind: WindowsArtifactKind): Promise<void>;
4
+ }
5
+ export declare function secureWindowsArtifact(path: string, kind: WindowsArtifactKind): Promise<void>;
6
+ export declare function secureWindowsArtifactSync(path: string, kind: WindowsArtifactKind): void;
@@ -0,0 +1,161 @@
1
+ import { execFile, execFileSync } from "node:child_process";
2
+ import { win32 } from "node:path";
3
+ const SYSTEM_SID = "S-1-5-18";
4
+ const POWERSHELL_TIMEOUT_MS = 30_000;
5
+ const WINDOWS_HELPER_ENVIRONMENT_NAMES = [
6
+ "SystemRoot",
7
+ "WINDIR",
8
+ "ComSpec",
9
+ "PATHEXT",
10
+ "PATH",
11
+ "TEMP",
12
+ "TMP",
13
+ "USERPROFILE",
14
+ "LOCALAPPDATA",
15
+ "APPDATA",
16
+ "PROGRAMDATA",
17
+ ];
18
+ const WINDOWS_ACL_SCRIPT = `
19
+ $ErrorActionPreference = 'Stop'
20
+ if ($args.Count -ne 0) { exit 41 }
21
+ $target = [Environment]::GetEnvironmentVariable('AMBASSADOR_ACL_PATH', 'Process')
22
+ $kind = [Environment]::GetEnvironmentVariable('AMBASSADOR_ACL_KIND', 'Process')
23
+ if ([String]::IsNullOrEmpty($target)) { exit 42 }
24
+ $attributes = [System.IO.File]::GetAttributes($target)
25
+ if (($attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) { exit 43 }
26
+ $isDirectory = ($attributes -band [System.IO.FileAttributes]::Directory) -ne 0
27
+ if (($kind -eq 'directory') -ne $isDirectory) { exit 44 }
28
+ if ($kind -ne 'directory' -and $kind -ne 'file') { exit 45 }
29
+ $userIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
30
+ $userSid = $userIdentity.Value
31
+ $artifact = if ($isDirectory) {
32
+ [System.IO.DirectoryInfo]::new($target)
33
+ } else {
34
+ [System.IO.FileInfo]::new($target)
35
+ }
36
+ $security = if ($isDirectory) {
37
+ [System.Security.AccessControl.DirectorySecurity]::new()
38
+ } else {
39
+ [System.Security.AccessControl.FileSecurity]::new()
40
+ }
41
+ $security.SetOwner($userIdentity)
42
+ $security.SetAccessRuleProtection($true, $false)
43
+ $inheritance = if ($isDirectory) {
44
+ [System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit'
45
+ } else {
46
+ [System.Security.AccessControl.InheritanceFlags]::None
47
+ }
48
+ $expected = [System.Collections.Generic.HashSet[string]]::new(
49
+ [System.StringComparer]::OrdinalIgnoreCase
50
+ )
51
+ [void]$expected.Add($userSid)
52
+ [void]$expected.Add('${SYSTEM_SID}')
53
+ foreach ($sid in $expected) {
54
+ $identity = [System.Security.Principal.SecurityIdentifier]::new($sid)
55
+ $rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
56
+ $identity,
57
+ [System.Security.AccessControl.FileSystemRights]::FullControl,
58
+ $inheritance,
59
+ [System.Security.AccessControl.PropagationFlags]::None,
60
+ [System.Security.AccessControl.AccessControlType]::Allow
61
+ )
62
+ [void]$security.AddAccessRule($rule)
63
+ }
64
+ $artifact.SetAccessControl($security)
65
+ $actual = $artifact.GetAccessControl(
66
+ [System.Security.AccessControl.AccessControlSections]'Access,Owner'
67
+ )
68
+ if (-not $actual.AreAccessRulesProtected) { exit 46 }
69
+ if ($actual.GetOwner([System.Security.Principal.SecurityIdentifier]).Value -ne $userSid) {
70
+ exit 47
71
+ }
72
+ $rules = @($actual.GetAccessRules(
73
+ $true,
74
+ $true,
75
+ [System.Security.Principal.SecurityIdentifier]
76
+ ))
77
+ if ($rules.Count -ne $expected.Count) { exit 48 }
78
+ foreach ($rule in $rules) {
79
+ if (-not $expected.Contains($rule.IdentityReference.Value)) { exit 49 }
80
+ if ($rule.IsInherited) { exit 50 }
81
+ if ($rule.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow) {
82
+ exit 51
83
+ }
84
+ if ([int]$rule.FileSystemRights -ne [int][System.Security.AccessControl.FileSystemRights]::FullControl) {
85
+ exit 52
86
+ }
87
+ if ($rule.InheritanceFlags -ne $inheritance) { exit 53 }
88
+ if ($rule.PropagationFlags -ne [System.Security.AccessControl.PropagationFlags]::None) {
89
+ exit 54
90
+ }
91
+ }
92
+ [Console]::Out.Write('AMBASSADOR_ACL_OK')
93
+ `;
94
+ function powershellExecutable() {
95
+ const systemRoot = process.env.SystemRoot;
96
+ if (systemRoot === undefined ||
97
+ !win32.isAbsolute(systemRoot) ||
98
+ systemRoot.includes("\u0000") ||
99
+ systemRoot.includes("\r") ||
100
+ systemRoot.includes("\n")) {
101
+ throw new Error("Windows state access control failed");
102
+ }
103
+ return win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
104
+ }
105
+ function helperEnvironment(path, kind) {
106
+ if (path.length < 1 || path.length > 32_768 || path.includes("\u0000")) {
107
+ throw new Error("Windows state access control failed");
108
+ }
109
+ const environment = {};
110
+ for (const name of WINDOWS_HELPER_ENVIRONMENT_NAMES) {
111
+ const value = process.env[name];
112
+ if (value !== undefined)
113
+ environment[name] = value;
114
+ }
115
+ environment.AMBASSADOR_ACL_PATH = path;
116
+ environment.AMBASSADOR_ACL_KIND = kind;
117
+ return environment;
118
+ }
119
+ function arguments_() {
120
+ return [
121
+ "-NoLogo",
122
+ "-NoProfile",
123
+ "-NonInteractive",
124
+ "-EncodedCommand",
125
+ Buffer.from(WINDOWS_ACL_SCRIPT, "utf16le").toString("base64"),
126
+ ];
127
+ }
128
+ function options(path, kind) {
129
+ return {
130
+ encoding: "utf8",
131
+ env: helperEnvironment(path, kind),
132
+ maxBuffer: 32 * 1024,
133
+ timeout: POWERSHELL_TIMEOUT_MS,
134
+ windowsHide: true,
135
+ };
136
+ }
137
+ export async function secureWindowsArtifact(path, kind) {
138
+ const output = await new Promise((resolve, reject) => {
139
+ execFile(powershellExecutable(), arguments_(), options(path, kind), (error, stdout, stderr) => {
140
+ if (error !== null || stderr.length !== 0 || stdout !== "AMBASSADOR_ACL_OK") {
141
+ reject(new Error("Windows state access control failed"));
142
+ return;
143
+ }
144
+ resolve(stdout);
145
+ });
146
+ });
147
+ if (output !== "AMBASSADOR_ACL_OK")
148
+ throw new Error("Windows state access control failed");
149
+ }
150
+ export function secureWindowsArtifactSync(path, kind) {
151
+ let output;
152
+ try {
153
+ output = execFileSync(powershellExecutable(), arguments_(), options(path, kind));
154
+ }
155
+ catch {
156
+ throw new Error("Windows state access control failed");
157
+ }
158
+ if (output !== "AMBASSADOR_ACL_OK")
159
+ throw new Error("Windows state access control failed");
160
+ }
161
+ //# sourceMappingURL=windows-access-control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows-access-control.js","sourceRoot":"","sources":["../src/windows-access-control.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ # Reset local test state
2
+
3
+ This removes only local Ambassador test residue. It does not call the central
4
+ service or delete a registered central identity.
5
+
6
+ 1. Stop `ambassador start`.
7
+ 2. Delete the complete Ambassador state directory for the test account:
8
+
9
+ | Platform | Default state directory |
10
+ | --- | --- |
11
+ | macOS | `~/Library/Application Support/ambassador` |
12
+ | Linux | `${XDG_STATE_HOME:-~/.local/state}/ambassador` |
13
+ | Windows | `%LOCALAPPDATA%\ambassador` |
14
+
15
+ The directory contains the encrypted central credential and key, encrypted
16
+ webhook secret and key, delivery profile, ID-only notification journal, and
17
+ process lock. Delete the directory only after checking the exact path.
18
+
19
+ 3. Remove any test-only Ambassador MCP entry, Hermes webhook route, or
20
+ OpenClaw plugin configuration that you created for the run. Do not remove
21
+ normal provider credentials.
22
+ 4. Start Ambassador again from the working directory you want the new direct
23
+ profile to use.
24
+
25
+ Because this is a local-only reset, central still owns the old email identity.
26
+ Use a new disposable email when repeating registration. A `409` for the old
27
+ address is expected and is not fixed by deleting local files.
@@ -3,9 +3,9 @@
3
3
  ## Before you start
4
4
 
5
5
  - Install Node.js `>=24.19.0 <25`.
6
- - Use Claude Code `2.1.257` or `2.1.258`.
7
- - For direct delivery, install `@agentclientprotocol/claude-agent-acp` `0.73.0`
8
- so `claude-agent-acp` is on `PATH`.
6
+ - Install Claude Code.
7
+ - For direct delivery, install `@agentclientprotocol/claude-agent-acp` so
8
+ `claude-agent-acp` is on `PATH`.
9
9
  - Sign in to Claude Code normally. Ambassador never receives your Claude
10
10
  credential.
11
11
 
@@ -28,7 +28,9 @@
28
28
  Ambassador will launch `claude-agent-acp` when a central message arrives. That
29
29
  is a new gateway-managed session, not the chat used for registration.
30
30
 
31
- The qualification probe records the installed version without rejecting it.
32
- Production still checks the exact MCP client and ACP identities in the compiled
33
- profile, so a new incompatible release fails closed. See
31
+ Ambassador selects this profile by the exact known MCP client name, then tries
32
+ the fixed `claude-agent-acp` ACP v1 contract. Reported client and adapter
33
+ versions are diagnostic only. An incompatible release fails at startup, ACP
34
+ initialization, session creation, or delivery instead of being rejected by a
35
+ version list. See
34
36
  [Qualification](qualification.md) for compatibility evidence.
@@ -3,9 +3,9 @@
3
3
  ## Before you start
4
4
 
5
5
  - Install Node.js `>=24.19.0 <25`.
6
- - Use Codex `0.149.0` or `0.152.1`.
7
- - For direct delivery, install `@agentclientprotocol/codex-acp` `1.8.0` so
8
- `codex-acp` is on `PATH`.
6
+ - Install Codex.
7
+ - For direct delivery, install `@agentclientprotocol/codex-acp` so `codex-acp`
8
+ is on `PATH`.
9
9
  - Sign in to Codex normally. Ambassador never receives your Codex credential.
10
10
 
11
11
  ## Set up direct delivery
@@ -31,7 +31,8 @@
31
31
  Ambassador will launch `codex-acp` when a central message arrives. That is a
32
32
  new gateway-managed session, not the chat used for registration.
33
33
 
34
- The qualification probe records the installed version without rejecting it.
35
- Production still checks the exact MCP client and ACP identities in the compiled
36
- profile, so a new incompatible release fails closed. See
34
+ Ambassador selects this profile by the exact known MCP client name, then tries
35
+ the fixed `codex-acp` ACP v1 contract. Reported client and adapter versions are
36
+ diagnostic only. An incompatible release fails at startup, ACP initialization,
37
+ session creation, or delivery instead of being rejected by a version list. See
37
38
  [Qualification](qualification.md) for compatibility evidence.
@@ -3,7 +3,7 @@
3
3
  ## Before you start
4
4
 
5
5
  - Install Node.js `>=24.19.0 <25`.
6
- - Install and sign in to Gemini CLI `0.58.0`.
6
+ - Install and sign in to Gemini CLI.
7
7
  - Gemini supplies native ACP through `gemini --acp`; no adapter is needed.
8
8
  - Ambassador never receives your Gemini or Google credential.
9
9
 
@@ -26,7 +26,8 @@
26
26
  Ambassador will launch `gemini --acp` when a central message arrives. That is
27
27
  a new gateway-managed session, not the chat used for registration.
28
28
 
29
- The qualification probe records the installed version without rejecting it.
30
- Production still checks the exact MCP client and ACP identities in the compiled
31
- profile, so a new incompatible release fails closed. See
29
+ Ambassador selects this profile by the exact known MCP client name, then tries
30
+ the fixed `gemini --acp` ACP v1 contract. Reported client and agent versions are
31
+ diagnostic only. An incompatible release fails at startup, ACP initialization,
32
+ session creation, or delivery instead of being rejected by a version list. See
32
33
  [Qualification](qualification.md) for compatibility evidence.
@@ -3,50 +3,101 @@
3
3
  ## Before you start
4
4
 
5
5
  - Install Node.js `>=24.19.0 <25`.
6
- - Install and authenticate Hermes Agent `0.20.5` or `0.21.0`.
6
+ - Install and authenticate Hermes Agent.
7
7
  - Make sure `hermes-acp` is on `PATH` for direct delivery.
8
8
  - Ambassador never receives your provider credential.
9
9
 
10
- Hermes Agent `0.20.5` passed both live delivery modes and is included in
11
- Ambassador 0.2.8's direct profile. The qualification probe records the installed
12
- version without treating the observation itself as a failure.
13
-
14
10
  ## Set up direct delivery
15
11
 
16
- 1. From the directory Hermes may access, keep Ambassador running:
12
+ 1. From the directory Hermes may access, keep the latest Ambassador running:
17
13
 
18
14
  ```sh
19
15
  npx --yes @embassys/ambassador@latest start
20
16
  ```
21
17
 
22
- 2. Add `http://127.0.0.1:8787/mcp` as a Streamable HTTP MCP server in Hermes's
23
- normal MCP configuration. Do not configure authentication.
18
+ 2. Add the endpoint printed by Ambassador as an unauthenticated Streamable
19
+ HTTP MCP server:
20
+
21
+ ```sh
22
+ hermes mcp add ambassador \
23
+ --url http://127.0.0.1:8787/mcp \
24
+ --connect-timeout 15
25
+ ```
26
+
27
+ Replace the URL if Ambassador printed a different loopback port. Do not
28
+ configure authentication.
24
29
  3. Start or restart Hermes so it sees the MCP server.
25
- 4. Ask Hermes to register your email; it calls Ambassador's `register_agent`
30
+ 4. Ask Hermes to register your email. It calls Ambassador's `register_agent`
26
31
  tool.
27
32
  5. Choose **Send directly to this Hermes agent**.
28
33
  6. Enter the six-digit code sent to your email.
29
34
 
30
- Ambassador will launch `hermes-acp` when a central message arrives. That is a
31
- new gateway-managed session, not the chat used for registration.
35
+ Ambassador launches `hermes-acp` when a central message arrives. That is a new
36
+ gateway-managed session, not the chat used for registration. Reported versions
37
+ are diagnostic only: Ambassador tries the fixed ACP v1 command and exact
38
+ `hermes-agent` identity, then reports a bounded startup, initialization,
39
+ session, or delivery failure if they are incompatible.
40
+
41
+ ## Set up webhook delivery
42
+
43
+ Hermes has a native generic webhook receiver. Configure one owner-controlled
44
+ route that uses the same value for Ambassador's bearer and HMAC V2 contract:
45
+
46
+ 1. With Ambassador running, choose **Send to a webhook** during registration.
47
+ Ambassador responds with:
48
+
49
+ ```sh
50
+ npx --yes @embassys/ambassador@latest webhook-secret
51
+ ```
52
+
53
+ Ambassador creates the secret, encrypts it in its own owner-only state, and
54
+ displays it. Repeating the command displays the same value; it does not
55
+ rotate it.
32
56
 
33
- ## Use a webhook instead
57
+ 2. Enable Hermes webhooks with `WEBHOOK_ENABLED=true` and your chosen
58
+ `WEBHOOK_PORT` in Hermes's owner-only `.hermes/.env`. Add this route to
59
+ `.hermes/webhook_subscriptions.json`, replacing both placeholders with the
60
+ displayed value and preserving any existing routes:
61
+
62
+ ```json
63
+ {
64
+ "embassys": {
65
+ "description": "Embassys Ambassador",
66
+ "events": [],
67
+ "filters": [
68
+ {
69
+ "field": "headers.Authorization",
70
+ "equals": "Bearer PASTE_AMBASSADOR_SECRET_HERE"
71
+ }
72
+ ],
73
+ "prompt": "",
74
+ "skills": [],
75
+ "deliver": "log",
76
+ "secret": "PASTE_AMBASSADOR_SECRET_HERE"
77
+ }
78
+ }
79
+ ```
80
+
81
+ Keep the file mode `0600`. An empty prompt passes the complete canonical
82
+ JSON to the model. The route uses Hermes's normal tool configuration, so
83
+ keep the Ambassador MCP server enabled there.
84
+
85
+ 3. Start or restart `hermes gateway run`. The local receiver URL is normally:
86
+
87
+ ```text
88
+ http://127.0.0.1:8644/webhooks/embassys
89
+ ```
34
90
 
35
- - Before step 1, set the receiver secret in the same shell:
91
+ Use the actual configured port. A non-loopback receiver must use an HTTPS
92
+ URL.
36
93
 
37
- ```sh
38
- export AMBASSADOR_WEBHOOK_SECRET="$(
39
- node -e "process.stdout.write(require('node:crypto').randomBytes(24).toString('hex'))"
40
- )"
41
- ```
94
+ 4. Retry `register_agent` with webhook selected and that URL. MCP carries only
95
+ `delivery.mode` and `delivery.url`; it never carries the secret or a secret
96
+ name.
42
97
 
43
- - During registration, choose **Send to a webhook**.
44
- - Give Hermes the HTTPS webhook URL and the variable name
45
- `AMBASSADOR_WEBHOOK_SECRET`.
46
- - Hermes supplies `delivery.url` and `delivery.secret_env`; it never receives
47
- the secret value.
98
+ Hermes validates the bearer filter and HMAC V2 timestamp/signature before its
99
+ model runs. A webhook `2xx` proves custody only. Keep both Hermes and
100
+ Ambassador running until the model calls `respond_to_permission` or
101
+ `submit_action_result` and the requester receives the correlated response.
48
102
 
49
- Production still checks the exact MCP client and ACP identities in the compiled
50
- profile, so a new incompatible release fails closed. See
51
- [Qualification](qualification.md) for the artifact-specific compatibility
52
- evidence.
103
+ For local reruns, see [Reset local test state](development-reset.md).