@hellolynk-ai/codex-plugin 0.0.0-stage → 0.2.99

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 (123) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/LICENSE +92 -0
  3. package/README.md +86 -2
  4. package/bin/hellolynk-client-setup.mjs +2748 -0
  5. package/bin/hellolynk-mcp-config-shape.mjs +23 -0
  6. package/hellolynk-client-pack-manifest.json +619 -0
  7. package/hellolynk-client-pack-pin.json +4 -0
  8. package/package.json +58 -4
  9. package/plugins/hellolynk-codex/.app.json +3 -0
  10. package/plugins/hellolynk-codex/.codex-plugin/plugin.json +38 -0
  11. package/plugins/hellolynk-codex/.mcp.json +19 -0
  12. package/plugins/hellolynk-codex/assets/hellolynk-mark.png +0 -0
  13. package/plugins/hellolynk-codex/package.json +11 -0
  14. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-account.mjs +180 -0
  15. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-core.mjs +770 -0
  16. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-profile.mjs +191 -0
  17. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-auth.mjs +347 -0
  18. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-protected-store.mjs +2721 -0
  19. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-local-process-lock.mjs +157 -0
  20. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-neutral-host-installation-store.mjs +765 -0
  21. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-platform-credential-store.mjs +773 -0
  22. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-runtime-safety.cjs +471 -0
  23. package/plugins/hellolynk-codex/runtime/bootstrap/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
  24. package/plugins/hellolynk-codex/runtime/connect-activate.mjs +837 -0
  25. package/plugins/hellolynk-codex/runtime/core/hellolynk-agent-configurator.mjs +701 -0
  26. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-account.mjs +180 -0
  27. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-core.mjs +770 -0
  28. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-profile.mjs +191 -0
  29. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-entry.mjs +31 -0
  30. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-runtime.mjs +600 -0
  31. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update.mjs +453 -0
  32. package/plugins/hellolynk-codex/runtime/core/hellolynk-contact-request-e2ee.mjs +867 -0
  33. package/plugins/hellolynk-codex/runtime/core/hellolynk-device-enrollment.mjs +435 -0
  34. package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee-client.mjs +1964 -0
  35. package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee.mjs +1207 -0
  36. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-auth.mjs +347 -0
  37. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-protected-store.mjs +2721 -0
  38. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-transport-client.mjs +946 -0
  39. package/plugins/hellolynk-codex/runtime/core/hellolynk-host.mjs +2374 -0
  40. package/plugins/hellolynk-codex/runtime/core/hellolynk-human-confirmation-ledger.mjs +256 -0
  41. package/plugins/hellolynk-codex/runtime/core/hellolynk-human-relationship-controller.mjs +1234 -0
  42. package/plugins/hellolynk-codex/runtime/core/hellolynk-inbox-read-state.mjs +11 -0
  43. package/plugins/hellolynk-codex/runtime/core/hellolynk-indexed-message-store.mjs +1558 -0
  44. package/plugins/hellolynk-codex/runtime/core/hellolynk-instance-conversation.mjs +185 -0
  45. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contact-requests.mjs +1733 -0
  46. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contacts.mjs +1228 -0
  47. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-migration.mjs +766 -0
  48. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-protection.mjs +747 -0
  49. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-identity-trust.mjs +274 -0
  50. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-messaging.mjs +3043 -0
  51. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-peer-transport.mjs +103 -0
  52. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-process-lock.mjs +157 -0
  53. package/plugins/hellolynk-codex/runtime/core/hellolynk-model-inbox.mjs +230 -0
  54. package/plugins/hellolynk-codex/runtime/core/hellolynk-model-notices.cjs +60 -0
  55. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-addressing-tools.cjs +231 -0
  56. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-conversations.mjs +280 -0
  57. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-human-confirmation-broker.mjs +1036 -0
  58. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-inbox-runtime.mjs +553 -0
  59. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-notice.mjs +50 -0
  60. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-reply-routes.mjs +96 -0
  61. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-worker-lease.mjs +84 -0
  62. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-assembly.mjs +955 -0
  63. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-installation-store.mjs +765 -0
  64. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-launcher.mjs +665 -0
  65. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-process.mjs +4374 -0
  66. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-store-adapters.mjs +548 -0
  67. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-workbench-read-model.mjs +1365 -0
  68. package/plugins/hellolynk-codex/runtime/core/hellolynk-platform-credential-store.mjs +773 -0
  69. package/plugins/hellolynk-codex/runtime/core/hellolynk-relay-client-cli.mjs +232 -0
  70. package/plugins/hellolynk-codex/runtime/core/hellolynk-release-retention.mjs +160 -0
  71. package/plugins/hellolynk-codex/runtime/core/hellolynk-runtime-safety.cjs +471 -0
  72. package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-action-grants.mjs +457 -0
  73. package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-connection.mjs +234 -0
  74. package/plugins/hellolynk-codex/runtime/neutral-host-bootstrap.mjs +64 -0
  75. package/plugins/hellolynk-codex/runtime/neutral-host-release-entry.mjs +70 -0
  76. package/plugins/hellolynk-codex/runtime/neutral-host-release-loader.mjs +156 -0
  77. package/plugins/hellolynk-codex/runtime/neutral-host-release-manifest.json +358 -0
  78. package/plugins/hellolynk-codex/runtime/neutral-host-release-pin.json +4 -0
  79. package/plugins/hellolynk-codex/runtime/platform/windows/configure-agent.ps1 +281 -0
  80. package/plugins/hellolynk-codex/runtime/platform/windows/credential-store.ps1 +844 -0
  81. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
  82. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-human-confirmation-ledger.ps1 +155 -0
  83. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-local-data-key.ps1 +300 -0
  84. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-native-human-confirmation-broker.ps1 +216 -0
  85. package/plugins/hellolynk-codex/runtime/workbench/candidate-bootstrap.mjs +54 -0
  86. package/plugins/hellolynk-codex/runtime/workbench/contract.mjs +1314 -0
  87. package/plugins/hellolynk-codex/runtime/workbench/hellolynk-workbench-build.json +215 -0
  88. package/plugins/hellolynk-codex/runtime/workbench/host-capabilities.mjs +80 -0
  89. package/plugins/hellolynk-codex/runtime/workbench/i18n.mjs +1127 -0
  90. package/plugins/hellolynk-codex/runtime/workbench/loopback-surface.mjs +740 -0
  91. package/plugins/hellolynk-codex/runtime/workbench/mcp-app-bundle.mjs +97 -0
  92. package/plugins/hellolynk-codex/runtime/workbench/service.mjs +485 -0
  93. package/plugins/hellolynk-codex/runtime/workbench/state.mjs +421 -0
  94. package/plugins/hellolynk-codex/runtime/workbench/transports.mjs +171 -0
  95. package/plugins/hellolynk-codex/runtime/workbench/ui/app.mjs +6120 -0
  96. package/plugins/hellolynk-codex/runtime/workbench/ui/connection-form.mjs +155 -0
  97. package/plugins/hellolynk-codex/runtime/workbench/ui/hellolynk-mark.png +0 -0
  98. package/plugins/hellolynk-codex/runtime/workbench/ui/http-transport.mjs +247 -0
  99. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/arrow-left.svg +3 -0
  100. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/close.svg +3 -0
  101. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/contacts.svg +3 -0
  102. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/inbox.svg +3 -0
  103. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/refresh.svg +3 -0
  104. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/search.svg +3 -0
  105. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/security.svg +3 -0
  106. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/settings.svg +3 -0
  107. package/plugins/hellolynk-codex/runtime/workbench/ui/index.html +139 -0
  108. package/plugins/hellolynk-codex/runtime/workbench/ui/loopback-entry.mjs +26 -0
  109. package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-client.mjs +151 -0
  110. package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-entry.mjs +452 -0
  111. package/plugins/hellolynk-codex/runtime/workbench/ui/styles.css +686 -0
  112. package/plugins/hellolynk-codex/server/codex-neutral-production.mjs +520 -0
  113. package/plugins/hellolynk-codex/server/mcp-proxy.mjs +306 -0
  114. package/plugins/hellolynk-codex/server/mcp-server.mjs +1193 -0
  115. package/plugins/hellolynk-codex/server/native-conversation-directory.mjs +33 -0
  116. package/plugins/hellolynk-codex/server/native-reply-routes.mjs +32 -0
  117. package/plugins/hellolynk-codex/server/native-task-client.mjs +382 -0
  118. package/plugins/hellolynk-codex/server/native-task-runtime.mjs +8 -0
  119. package/plugins/hellolynk-codex/server/neutral-host-adapter.mjs +649 -0
  120. package/plugins/hellolynk-codex/server/workbench-runtime.mjs +180 -0
  121. package/plugins/hellolynk-codex/skills/hellolynk-open/SKILL.md +11 -0
  122. package/plugins/hellolynk-codex/skills/hellolynk-open/agents/openai.yaml +4 -0
  123. package/tools/hellolynk-activate-neutral-host-clients.mjs +837 -0
@@ -0,0 +1,2721 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { lstat, mkdir } from "node:fs/promises";
3
+ import { createConnection } from "node:net";
4
+ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
5
+ import { spawn } from "node:child_process";
6
+ import { fileURLToPath } from "node:url";
7
+ import runtimeSafety from "./hellolynk-runtime-safety.cjs";
8
+ import { readClientProfile } from "./hellolynk-client-profile.mjs";
9
+ import { assertClientAccountCurrent, clientAccountBaseRoot, resolveClientAccountRoot } from "./hellolynk-client-account.mjs";
10
+ export { resolveClientAccountRoot } from "./hellolynk-client-account.mjs";
11
+ import {
12
+ assertCapabilityRecord,
13
+ assertIdentifier,
14
+ HELLOLYNK_HOST_CAPABILITY_CONTRACT,
15
+ } from "./hellolynk-host-auth.mjs";
16
+ import {
17
+ createNeutralHostInstallationStore,
18
+ defaultNeutralHostStoreRoot,
19
+ } from "./hellolynk-neutral-host-installation-store.mjs";
20
+
21
+ const { sanitizeHelloLynkChildEnvironment } = runtimeSafety;
22
+
23
+ export const HELLOLYNK_HOST_PROTECTED_STORE_CONTRACT = "hellolynk-host-protected-store-v1";
24
+ export const HELLOLYNK_HOST_TRANSPORT_CREDENTIAL_CONTRACT = "hellolynk-host-transport-credential-v1";
25
+ export const HELLOLYNK_HOST_TRANSPORT_SESSION_CONTRACT = "hellolynk-host-transport-session-v1";
26
+ export const HELLOLYNK_CODEX_NEUTRAL_BINDING_CONTRACT = "hellolynk-codex-neutral-binding-v1";
27
+ export const HELLOLYNK_OPENCLAW_NEUTRAL_BINDING_CONTRACT = "hellolynk-openclaw-neutral-binding-v1";
28
+ export const HELLOLYNK_TRAE_WORK_NEUTRAL_BINDING_CONTRACT = "hellolynk-trae-work-neutral-binding-v1";
29
+ export const HELLOLYNK_WORKBUDDY_NEUTRAL_BINDING_CONTRACT = "hellolynk-workbuddy-neutral-binding-v1";
30
+ export const HELLOLYNK_CLAUDE_CODE_NEUTRAL_BINDING_CONTRACT = "hellolynk-claude-code-neutral-binding-v1";
31
+ export const HELLOLYNK_CURSOR_NEUTRAL_BINDING_CONTRACT = "hellolynk-cursor-neutral-binding-v1";
32
+ export const HELLOLYNK_DSH_NEUTRAL_BINDING_CONTRACT = "hellolynk-dsh-neutral-binding-v1";
33
+ export const HELLOLYNK_CODEX_NEUTRAL_EXACT_SCOPES = Object.freeze([
34
+ "app-data:authorized-messaging",
35
+ "app-data:changes-read",
36
+ "app-data:contact-requests-read",
37
+ "app-data:contacts-manage",
38
+ "app-data:contacts-read",
39
+ "app-data:human-send-request",
40
+ "app-data:inbox-manage",
41
+ "app-data:inbox-read",
42
+ "app-data:outbox-read",
43
+ "app-data:outbox-retry",
44
+ "attachments:manage",
45
+ "surface:human-ui",
46
+ "ui:launch",
47
+ ]);
48
+ export const HELLOLYNK_OPENCLAW_NEUTRAL_EXACT_SCOPES = Object.freeze([
49
+ ...HELLOLYNK_CODEX_NEUTRAL_EXACT_SCOPES,
50
+ ]);
51
+ const HELLOLYNK_CODEX_NEUTRAL_PREVIOUS_SCOPES = Object.freeze(
52
+ HELLOLYNK_CODEX_NEUTRAL_EXACT_SCOPES.filter((scope) => scope !== "app-data:authorized-messaging"),
53
+ );
54
+ const HELLOLYNK_OPENCLAW_NEUTRAL_PREVIOUS_SCOPES = Object.freeze([
55
+ ...HELLOLYNK_CODEX_NEUTRAL_PREVIOUS_SCOPES,
56
+ ]);
57
+ export const HELLOLYNK_TRAE_WORK_NEUTRAL_EXACT_SCOPES = Object.freeze([
58
+ "app-data:authorized-messaging",
59
+ "app-data:changes-read",
60
+ "app-data:contact-requests-read",
61
+ "app-data:contacts-manage",
62
+ "app-data:contacts-read",
63
+ "app-data:human-send-request",
64
+ "app-data:inbox-manage",
65
+ "app-data:inbox-read",
66
+ "app-data:outbox-read",
67
+ "app-data:outbox-retry",
68
+ "attachments:manage",
69
+ "status:read",
70
+ "surface:human-ui",
71
+ "ui:launch",
72
+ ]);
73
+ export const HELLOLYNK_WORKBUDDY_NEUTRAL_EXACT_SCOPES = Object.freeze([
74
+ ...HELLOLYNK_TRAE_WORK_NEUTRAL_EXACT_SCOPES,
75
+ ]);
76
+ export const HELLOLYNK_CLAUDE_CODE_NEUTRAL_EXACT_SCOPES = Object.freeze([
77
+ ...HELLOLYNK_WORKBUDDY_NEUTRAL_EXACT_SCOPES,
78
+ ]);
79
+ export const HELLOLYNK_CURSOR_NEUTRAL_EXACT_SCOPES = Object.freeze([
80
+ "app-data:authorized-messaging",
81
+ "app-data:changes-read",
82
+ "app-data:contact-requests-read",
83
+ "app-data:contacts-manage",
84
+ "app-data:contacts-read",
85
+ "app-data:human-send-request",
86
+ "app-data:inbox-manage",
87
+ "app-data:inbox-read",
88
+ "app-data:outbox-read",
89
+ "app-data:outbox-retry",
90
+ "attachments:manage",
91
+ "status:read",
92
+ "surface:human-ui",
93
+ "ui:launch",
94
+ ]);
95
+ // DSH's native assistant may read/reply only through contact-authorized routes.
96
+ // Human Workbench commands still require independently issued single-use grants.
97
+ const HELLOLYNK_CURSOR_NEUTRAL_PREVIOUS_SCOPES = Object.freeze(
98
+ HELLOLYNK_CURSOR_NEUTRAL_EXACT_SCOPES.filter((scope) => scope !== "app-data:authorized-messaging"),
99
+ );
100
+ export const HELLOLYNK_DSH_NEUTRAL_EXACT_SCOPES = Object.freeze([...HELLOLYNK_CURSOR_NEUTRAL_EXACT_SCOPES]);
101
+ const HELLOLYNK_DSH_NEUTRAL_PREVIOUS_SCOPES = Object.freeze([...HELLOLYNK_CURSOR_NEUTRAL_PREVIOUS_SCOPES]);
102
+ const HELLOLYNK_TRAE_WORK_NEUTRAL_PREVIOUS_SCOPES = Object.freeze(
103
+ HELLOLYNK_TRAE_WORK_NEUTRAL_EXACT_SCOPES.filter((scope) => scope !== "app-data:authorized-messaging"),
104
+ );
105
+ const HELLOLYNK_WORKBUDDY_NEUTRAL_PREVIOUS_SCOPES = Object.freeze([
106
+ ...HELLOLYNK_TRAE_WORK_NEUTRAL_PREVIOUS_SCOPES,
107
+ ]);
108
+ const HELLOLYNK_TRAE_WORK_NEUTRAL_LEGACY_SCOPES = Object.freeze([
109
+ "app-data:human-send-request",
110
+ "attachments:manage",
111
+ "status:read",
112
+ "surface:human-ui",
113
+ ]);
114
+ const HELLOLYNK_WORKBUDDY_NEUTRAL_LEGACY_SCOPES = Object.freeze([
115
+ ...HELLOLYNK_TRAE_WORK_NEUTRAL_LEGACY_SCOPES,
116
+ ]);
117
+ const CAPABILITY_ID_PATTERN = /^cap_[A-Za-z0-9_-]{32}$/;
118
+ const TRANSPORT_CREDENTIAL_PATTERN = /^hellolynk_host_transport_v1_([A-Za-z0-9_-]{3,127})\.([A-Za-z0-9_-]{32,})$/;
119
+ const TRANSPORT_SESSION_ID_PATTERN = /^hosttransportsession_[A-Za-z0-9_-]{16,}$/;
120
+ const TRANSPORT_SESSION_SECRET_PATTERN = /^hostsession_[a-f0-9]{48}$/;
121
+ const SCRIPT_PATH = join(
122
+ dirname(fileURLToPath(import.meta.url)),
123
+ "..",
124
+ "platform",
125
+ "windows",
126
+ "hellolynk-host-protected-store.ps1",
127
+ );
128
+ const ROOT_INVOKE_QUEUES = new Map();
129
+ const NEUTRAL_BINDING_READS = new Map();
130
+ const MAX_NEUTRAL_BINDING_READS = 256;
131
+ const NEUTRAL_BINDING_READ = /^(Resolve|Verify)(Codex|OpenClaw|TraeWork|WorkBuddy|ClaudeCode|Cursor|Dsh)NeutralBinding$/;
132
+ const PERSISTENT_CREDENTIAL_EXPIRES_AT = 253402300799999;
133
+ const LEGACY_CREDENTIAL_LIFETIME_MS = 30 * 24 * 60 * 60 * 1000;
134
+ const READ_ONLY_COMMANDS = new Set([
135
+ "LoadDeviceEnrollment",
136
+ "Status", "VerifyCodexNeutralBinding", "VerifyOpenClawNeutralBinding",
137
+ "VerifyTraeWorkNeutralBinding", "VerifyWorkBuddyNeutralBinding",
138
+ "VerifyClaudeCodeNeutralBinding",
139
+ "VerifyCursorNeutralBinding", "VerifyDshNeutralBinding", "VerifyTransport", "VerifyTransportCredentialLifetimeMigration",
140
+ "ResolveCodexNeutralBinding", "ResolveOpenClawNeutralBinding", "ResolveTraeWorkNeutralBinding",
141
+ "ResolveWorkBuddyNeutralBinding", "ResolveClaudeCodeNeutralBinding", "ResolveCursorNeutralBinding", "ResolveDshNeutralBinding",
142
+ ]);
143
+
144
+ export function defaultClaudeCodeStoreRoot(env = process.env) {
145
+ return defaultNeutralHostStoreRoot("win32", env);
146
+ }
147
+
148
+ function assertClaudeCodeStoreRoot(root) {
149
+ const base = clientAccountBaseRoot(root);
150
+ if (basename(base) !== "host") {
151
+ throw codeError("neutral_claude_code_store_root_invalid");
152
+ }
153
+ }
154
+
155
+ export function createHostProtectedStore(options = {}) {
156
+ const platform = options.platform || process.platform;
157
+ if (platform !== "win32") throw codeError("host_protected_store_platform_unsupported");
158
+ const root = assertRoot(options.root || defaultNeutralHostStoreRoot(platform, options.env));
159
+ const helperPath = resolve(options.helperPath || SCRIPT_PATH);
160
+ const clock = normalizeClock(options.clock);
161
+ const random = typeof options.randomBytes === "function" ? options.randomBytes : randomBytes;
162
+ const readOnly = options.readOnly === true;
163
+ const installationStore = options.installationStore || createNeutralHostInstallationStore(readOnly ? {
164
+ root,
165
+ readOnly: true,
166
+ verifyPath: ({ path, directory }) => verifyHostProtectedStorePath({
167
+ root,
168
+ path,
169
+ directory,
170
+ helperPath,
171
+ signal: options.signal,
172
+ }),
173
+ } : { root });
174
+ const nativeInvoke = (command, invokeOptions) => invokeWindowsStore({
175
+ command, root, helperPath, verifyOnly: readOnly, ...(readOnly ? { signal: options.signal } : {}), ...invokeOptions,
176
+ });
177
+ const baseInvoke = options.invoke || ((command, invokeOptions) => serializeRootInvoke(root, () => nativeInvoke(command, invokeOptions)));
178
+ const invoke = async (command, invokeOptions = {}) => {
179
+ if (readOnly && !READ_ONLY_COMMANDS.has(command)) throw codeError("host_protected_store_read_only");
180
+ // Each peer lookup creates a read-only store. Reuse its successful DPAPI
181
+ // result across those instances, while checking live paths and the file
182
+ // on every use. Registration/account checks remain in the public methods.
183
+ if (readOnly && !options.invoke && NEUTRAL_BINDING_READ.test(command)) {
184
+ return readNeutralBinding(command, invokeOptions);
185
+ }
186
+ try {
187
+ return await baseInvoke(command, invokeOptions);
188
+ } finally {
189
+ if (!READ_ONLY_COMMANDS.has(command)) {
190
+ for (const [key, entry] of NEUTRAL_BINDING_READS) {
191
+ if (entry.root === root) NEUTRAL_BINDING_READS.delete(key);
192
+ }
193
+ }
194
+ }
195
+ };
196
+ const capabilityStorePath = join(root, "capabilities-v1.dpapi.clixml");
197
+ const capabilityCache = new Map();
198
+ const capabilityLoadFlights = new Map();
199
+ let capabilityCacheFingerprint = null;
200
+
201
+ async function readNeutralBinding(command, invokeOptions, queued = false) {
202
+ const key = JSON.stringify([root, helperPath, command.replace(/^Verify/, "Resolve")]);
203
+ const signal = options.signal;
204
+ for (let attempt = 0; attempt < 2; attempt += 1) {
205
+ signal?.throwIfAborted();
206
+ // A cold native read already verifies root and credential-file ACLs,
207
+ // with the file check under its data mutex. Reuse that result only while
208
+ // protected paths remain intact, without a second VerifyPath process.
209
+ const before = await protectedPathFingerprint(root, capabilityStorePath, helperPath, false, signal);
210
+ const cached = NEUTRAL_BINDING_READS.get(key);
211
+ if (before && cached?.fingerprint === before
212
+ && await neutralBindingPathsPresent(cached.result.binding, signal)) {
213
+ signal?.throwIfAborted();
214
+ const result = structuredClone(cached.result);
215
+ // Verify keeps its original public-only contract. Resolve's caller
216
+ // decodes a fresh Buffer, so wiping it cannot damage the cached copy.
217
+ if (command.startsWith("Verify")) delete result.record.secret;
218
+ return result;
219
+ }
220
+ // Keep warm reads independent of unrelated native work. On a miss,
221
+ // recheck after entering the existing root queue and publish the result
222
+ // before releasing it, so concurrent callers do not repeat the helper.
223
+ if (!queued) return serializeRootInvoke(root, () => readNeutralBinding(command, invokeOptions, true));
224
+ NEUTRAL_BINDING_READS.delete(key);
225
+ const result = await nativeInvoke(command, invokeOptions);
226
+ const after = await protectedPathFingerprint(root, capabilityStorePath, helperPath, false, signal);
227
+ signal?.throwIfAborted();
228
+ if (before !== after) continue;
229
+ if (before && command.startsWith("Resolve")) {
230
+ if (NEUTRAL_BINDING_READS.size >= MAX_NEUTRAL_BINDING_READS) {
231
+ NEUTRAL_BINDING_READS.delete(NEUTRAL_BINDING_READS.keys().next().value);
232
+ }
233
+ NEUTRAL_BINDING_READS.set(key, { root, fingerprint: after, result: structuredClone(result) });
234
+ }
235
+ return result;
236
+ }
237
+ throw codeError("host_protected_store_changed_during_read");
238
+ }
239
+
240
+ function accountStore(client) {
241
+ if (options.followClientAccount === false) return null;
242
+ const selected = resolveClientAccountRoot(client, root);
243
+ if (selected === root) return null;
244
+ return createHostProtectedStore({ ...options, root: selected, installationStore: undefined });
245
+ }
246
+ function currentCapability(record) {
247
+ assertClientAccountCurrent(record.adapterId, root);
248
+ return record;
249
+ }
250
+
251
+ function invalidateCapabilityCache() {
252
+ capabilityCacheFingerprint = null;
253
+ capabilityCache.clear();
254
+ }
255
+
256
+ async function readCapabilityStoreFingerprint() {
257
+ try {
258
+ const metadata = await lstat(capabilityStorePath, { bigint: true });
259
+ return [
260
+ metadata.dev,
261
+ metadata.ino,
262
+ metadata.size,
263
+ metadata.mtimeNs,
264
+ metadata.birthtimeNs,
265
+ ].join(":");
266
+ } catch (error) {
267
+ if (error?.code === "ENOENT") return "missing";
268
+ throw error;
269
+ }
270
+ }
271
+
272
+ async function resolveCapability(capabilityId) {
273
+ const id = assertCapabilityId(capabilityId);
274
+ for (let attempt = 0; attempt < 2; attempt += 1) {
275
+ const fingerprint = await readCapabilityStoreFingerprint();
276
+ if (fingerprint !== capabilityCacheFingerprint) {
277
+ capabilityCacheFingerprint = fingerprint;
278
+ capabilityCache.clear();
279
+ }
280
+ if (capabilityCache.has(id)) return currentCapability(capabilityCache.get(id));
281
+ const existingFlight = capabilityLoadFlights.get(id);
282
+ const flight = existingFlight?.fingerprint === fingerprint
283
+ ? existingFlight
284
+ : {
285
+ fingerprint,
286
+ promise: load(id),
287
+ };
288
+ if (flight !== existingFlight) capabilityLoadFlights.set(id, flight);
289
+ try {
290
+ const record = await flight.promise;
291
+ const currentFingerprint = await readCapabilityStoreFingerprint();
292
+ if (currentFingerprint !== fingerprint) {
293
+ capabilityCacheFingerprint = currentFingerprint;
294
+ capabilityCache.clear();
295
+ continue;
296
+ }
297
+ capabilityCache.set(id, record);
298
+ return currentCapability(record);
299
+ } finally {
300
+ if (capabilityLoadFlights.get(id) === flight) capabilityLoadFlights.delete(id);
301
+ }
302
+ }
303
+ throw codeError("host_protected_store_changed_during_read");
304
+ }
305
+
306
+ async function issue(input = {}) {
307
+ const record = createRecord(input, { clock, random });
308
+ let result;
309
+ try {
310
+ result = await invoke("Issue", { stdin: serializeRecord(record) });
311
+ } finally {
312
+ invalidateCapabilityCache();
313
+ }
314
+ return deserializeRecord(result?.record);
315
+ }
316
+
317
+ async function load(capabilityId) {
318
+ const id = assertCapabilityId(capabilityId);
319
+ const result = await invoke("Load", { capabilityId: id });
320
+ return deserializeRecord(result?.record);
321
+ }
322
+
323
+ async function revoke(capabilityId) {
324
+ const id = assertCapabilityId(capabilityId);
325
+ let result;
326
+ try {
327
+ result = await invoke("Revoke", { capabilityId: id });
328
+ } finally {
329
+ invalidateCapabilityCache();
330
+ }
331
+ return Object.freeze({
332
+ capabilityId: assertCapabilityId(result?.capabilityId),
333
+ state: result?.state === "revoked" ? "revoked" : fail("host_capability_state_invalid"),
334
+ revokedAt: assertTimestamp(result?.revokedAt),
335
+ });
336
+ }
337
+
338
+ async function rotate(capabilityId, input = {}) {
339
+ const oldId = assertCapabilityId(capabilityId);
340
+ const record = createRecord(input, { clock, random });
341
+ if (record.capabilityId === oldId) throw codeError("host_capability_rotation_id_reused");
342
+ let result;
343
+ try {
344
+ result = await invoke("Rotate", {
345
+ capabilityId: oldId,
346
+ stdin: serializeRecord(record),
347
+ });
348
+ } finally {
349
+ invalidateCapabilityCache();
350
+ }
351
+ if (result?.replacedCapabilityId !== oldId) throw codeError("host_capability_rotation_invalid");
352
+ return deserializeRecord(result.record);
353
+ }
354
+
355
+ async function status() {
356
+ const result = await invoke("Status", {});
357
+ if (result?.contract !== HELLOLYNK_HOST_PROTECTED_STORE_CONTRACT || result?.protected !== true
358
+ || Object.keys(result).sort().join(",") !== "contract,protected") {
359
+ throw codeError("host_protected_store_status_invalid");
360
+ }
361
+ return Object.freeze({ contract: result.contract, protected: true });
362
+ }
363
+
364
+ async function saveCodexNeutralBinding(input = {}) {
365
+ const installation = await resolveActiveInstallation(installationStore, input);
366
+ const delegateBinding = await resolveCodexInstallationBinding(installationStore, input, installation);
367
+ const descriptor = codexNeutralBindingDescriptor(input, installation, delegateBinding);
368
+ const result = await invoke("SaveCodexNeutralBinding", { stdin: JSON.stringify(descriptor) });
369
+ return assertPersistentNeutralBinding(publicCodexNeutralBinding(result?.binding), root);
370
+ }
371
+
372
+ async function resolveCodexNeutralBinding() {
373
+ const selected = accountStore("codex");
374
+ if (selected) return selected.resolveCodexNeutralBinding();
375
+ const installationBefore = await resolveActiveInstallation(installationStore, {});
376
+ const result = await invoke("ResolveCodexNeutralBinding", {});
377
+ const binding = assertPersistentNeutralBinding(publicCodexNeutralBinding(result?.binding), root);
378
+ const capability = deserializeRecord(result?.record);
379
+ await assertCurrentCodexInstallationBinding(
380
+ installationStore,
381
+ installationBefore,
382
+ binding,
383
+ capability,
384
+ );
385
+ return Object.freeze({ ...binding, capability });
386
+ }
387
+
388
+ async function verifyCodexNeutralBinding() {
389
+ const selected = accountStore("codex");
390
+ if (selected) return selected.verifyCodexNeutralBinding();
391
+ const installationBefore = await resolveActiveInstallation(installationStore, {});
392
+ const result = await invoke("VerifyCodexNeutralBinding", {});
393
+ const binding = assertPersistentNeutralBinding(publicCodexNeutralBinding(result?.binding), root);
394
+ const capability = publicRecord(result?.record);
395
+ await assertCurrentCodexInstallationBinding(
396
+ installationStore,
397
+ installationBefore,
398
+ binding,
399
+ capability,
400
+ );
401
+ return Object.freeze({ ...binding, capability });
402
+ }
403
+
404
+ async function removeCodexNeutralBinding(input = {}) {
405
+ const installation = await resolveActiveInstallation(installationStore, input);
406
+ const delegateBinding = await resolveCodexInstallationBinding(installationStore, input, installation);
407
+ const expected = publicCodexNeutralBinding(input);
408
+ if (expected.region !== installation.region
409
+ || expected.principalId !== installation.principalId
410
+ || expected.hostInstallationId !== installation.hostInstallationId
411
+ || expected.profileId !== delegateBinding.profileId
412
+ || expected.delegateId !== delegateBinding.delegateId) {
413
+ throw codeError("neutral_codex_capability_binding_mismatch");
414
+ }
415
+ const result = await invoke("RemoveCodexNeutralBinding", {
416
+ stdin: JSON.stringify(expected),
417
+ });
418
+ if (result?.removed !== true) throw codeError("neutral_codex_binding_configuration_required");
419
+ return Object.freeze({ removed: true });
420
+ }
421
+
422
+ async function replaceCodexNeutralBinding(input = {}) {
423
+ const installation = await resolveActiveInstallation(installationStore, input.replacementBinding);
424
+ const delegateBinding = await resolveCodexInstallationBinding(
425
+ installationStore,
426
+ input.replacementBinding,
427
+ installation,
428
+ );
429
+ const previousBinding = publicCodexNeutralBinding(input.previousBinding);
430
+ const replacementBinding = codexNeutralBindingDescriptor(
431
+ input.replacementBinding,
432
+ installation,
433
+ delegateBinding,
434
+ );
435
+ const result = await invoke("ReplaceCodexNeutralBinding", {
436
+ stdin: JSON.stringify({ previousBinding, replacementBinding }),
437
+ });
438
+ return publicCodexNeutralBinding(result?.binding);
439
+ }
440
+
441
+ async function migrateCodexNeutralBinding(input = {}) {
442
+ return migrateNeutralBinding({
443
+ command: "MigrateCodexNeutralBinding",
444
+ input,
445
+ adapterId: "codex",
446
+ bindingCode: "neutral_codex_capability_binding_mismatch",
447
+ descriptor: codexNeutralBindingDescriptor,
448
+ publicBinding: publicCodexNeutralBinding,
449
+ resolveInstallationBinding: resolveCodexInstallationBinding,
450
+ assertCurrent: assertCurrentCodexInstallationBinding,
451
+ });
452
+ }
453
+
454
+ async function saveOpenClawNeutralBinding(input = {}) {
455
+ const installation = await resolveActiveInstallation(installationStore, input);
456
+ const delegateBinding = await resolveOpenClawInstallationBinding(installationStore, input, installation);
457
+ const descriptor = openClawNeutralBindingDescriptor(input, installation, delegateBinding);
458
+ const result = await invoke("SaveOpenClawNeutralBinding", { stdin: JSON.stringify(descriptor) });
459
+ return assertPersistentNeutralBinding(publicOpenClawNeutralBinding(result?.binding), root);
460
+ }
461
+
462
+ async function resolveOpenClawNeutralBinding(input = {}) {
463
+ const selected = accountStore("openclaw");
464
+ if (selected) return selected.resolveOpenClawNeutralBinding(input);
465
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
466
+ const result = await invoke("ResolveOpenClawNeutralBinding", {});
467
+ const binding = assertPersistentNeutralBinding(publicOpenClawNeutralBinding(result?.binding), root);
468
+ const capability = deserializeRecord(result?.record);
469
+ await assertCurrentOpenClawInstallationBinding(
470
+ installationStore,
471
+ installationBefore,
472
+ binding,
473
+ capability,
474
+ input,
475
+ );
476
+ return Object.freeze({ ...binding, capability });
477
+ }
478
+
479
+ async function verifyOpenClawNeutralBinding(input = {}) {
480
+ const selected = accountStore("openclaw");
481
+ if (selected) return selected.verifyOpenClawNeutralBinding(input);
482
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
483
+ const result = await invoke("VerifyOpenClawNeutralBinding", {});
484
+ const binding = assertPersistentNeutralBinding(publicOpenClawNeutralBinding(result?.binding), root);
485
+ const capability = publicRecord(result?.record);
486
+ await assertCurrentOpenClawInstallationBinding(
487
+ installationStore,
488
+ installationBefore,
489
+ binding,
490
+ capability,
491
+ input,
492
+ );
493
+ return Object.freeze({ ...binding, capability });
494
+ }
495
+
496
+ async function removeOpenClawNeutralBinding(input = {}) {
497
+ const installation = await resolveActiveInstallation(installationStore, input);
498
+ const delegateBinding = await resolveOpenClawInstallationBinding(installationStore, input, installation);
499
+ const expected = publicOpenClawNeutralBinding(input);
500
+ if (expected.region !== installation.region
501
+ || expected.principalId !== installation.principalId
502
+ || expected.hostInstallationId !== installation.hostInstallationId
503
+ || expected.profileId !== delegateBinding.profileId
504
+ || expected.delegateId !== delegateBinding.delegateId) {
505
+ throw codeError("neutral_openclaw_capability_binding_mismatch");
506
+ }
507
+ const result = await invoke("RemoveOpenClawNeutralBinding", {
508
+ stdin: JSON.stringify(expected),
509
+ });
510
+ if (result?.removed !== true) throw codeError("neutral_openclaw_binding_configuration_required");
511
+ return Object.freeze({ removed: true });
512
+ }
513
+
514
+ async function migrateOpenClawNeutralBinding(input = {}) {
515
+ return migrateNeutralBinding({
516
+ command: "MigrateOpenClawNeutralBinding",
517
+ input,
518
+ adapterId: "openclaw",
519
+ bindingCode: "neutral_openclaw_capability_binding_mismatch",
520
+ descriptor: openClawNeutralBindingDescriptor,
521
+ publicBinding: publicOpenClawNeutralBinding,
522
+ resolveInstallationBinding: resolveOpenClawInstallationBinding,
523
+ assertCurrent: assertCurrentOpenClawInstallationBinding,
524
+ });
525
+ }
526
+
527
+ async function saveTraeWorkNeutralBinding(input = {}) {
528
+ const installation = await resolveActiveInstallation(installationStore, input);
529
+ const delegateBinding = await resolveTraeWorkInstallationBinding(installationStore, input, installation);
530
+ const descriptor = traeWorkNeutralBindingDescriptor(input, installation, delegateBinding);
531
+ const result = await invoke("SaveTraeWorkNeutralBinding", { stdin: JSON.stringify(descriptor) });
532
+ return assertPersistentNeutralBinding(publicTraeWorkNeutralBinding(result?.binding), root);
533
+ }
534
+
535
+ async function resolveTraeWorkNeutralBinding(input = {}) {
536
+ const selected = accountStore("trae-work");
537
+ if (selected) return selected.resolveTraeWorkNeutralBinding(input);
538
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
539
+ const result = await invoke("ResolveTraeWorkNeutralBinding", {});
540
+ const binding = assertPersistentNeutralBinding(publicTraeWorkNeutralBinding(result?.binding), root);
541
+ const capability = deserializeRecord(result?.record);
542
+ await assertCurrentTraeWorkInstallationBinding(
543
+ installationStore,
544
+ installationBefore,
545
+ binding,
546
+ capability,
547
+ input,
548
+ );
549
+ return Object.freeze({ ...binding, capability });
550
+ }
551
+
552
+ async function verifyTraeWorkNeutralBinding(input = {}) {
553
+ const selected = accountStore("trae-work");
554
+ if (selected) return selected.verifyTraeWorkNeutralBinding(input);
555
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
556
+ const result = await invoke("VerifyTraeWorkNeutralBinding", {});
557
+ const binding = assertPersistentNeutralBinding(publicTraeWorkNeutralBinding(result?.binding), root);
558
+ const capability = publicRecord(result?.record);
559
+ await assertCurrentTraeWorkInstallationBinding(
560
+ installationStore,
561
+ installationBefore,
562
+ binding,
563
+ capability,
564
+ input,
565
+ );
566
+ return Object.freeze({ ...binding, capability });
567
+ }
568
+
569
+ async function removeTraeWorkNeutralBinding(input = {}) {
570
+ const installation = await resolveActiveInstallation(installationStore, input);
571
+ const delegateBinding = await resolveTraeWorkInstallationBinding(installationStore, input, installation);
572
+ const expected = publicTraeWorkNeutralBinding(input);
573
+ if (expected.region !== installation.region
574
+ || expected.principalId !== installation.principalId
575
+ || expected.hostInstallationId !== installation.hostInstallationId
576
+ || expected.profileId !== delegateBinding.profileId
577
+ || expected.delegateId !== delegateBinding.delegateId) {
578
+ throw codeError("neutral_trae_work_capability_binding_mismatch");
579
+ }
580
+ const result = await invoke("RemoveTraeWorkNeutralBinding", {
581
+ stdin: JSON.stringify(expected),
582
+ });
583
+ if (result?.removed !== true) throw codeError("neutral_trae_work_binding_configuration_required");
584
+ return Object.freeze({ removed: true });
585
+ }
586
+
587
+ async function migrateTraeWorkNeutralBinding(input = {}) {
588
+ return migrateNeutralBinding({
589
+ command: "MigrateTraeWorkNeutralBinding",
590
+ input,
591
+ adapterId: "trae-work",
592
+ bindingCode: "neutral_trae_work_capability_binding_mismatch",
593
+ descriptor: traeWorkNeutralBindingDescriptor,
594
+ publicBinding: publicTraeWorkNeutralBinding,
595
+ resolveInstallationBinding: resolveTraeWorkInstallationBinding,
596
+ assertCurrent: assertCurrentTraeWorkInstallationBinding,
597
+ });
598
+ }
599
+
600
+ async function saveWorkBuddyNeutralBinding(input = {}) {
601
+ const installation = await resolveActiveInstallation(installationStore, input);
602
+ const delegateBinding = await resolveWorkBuddyInstallationBinding(installationStore, input, installation);
603
+ const descriptor = workBuddyNeutralBindingDescriptor(input, installation, delegateBinding);
604
+ const result = await invoke("SaveWorkBuddyNeutralBinding", { stdin: JSON.stringify(descriptor) });
605
+ return assertPersistentNeutralBinding(publicWorkBuddyNeutralBinding(result?.binding), root);
606
+ }
607
+
608
+ async function resolveWorkBuddyNeutralBinding(input = {}) {
609
+ const selected = accountStore("workbuddy");
610
+ if (selected) return selected.resolveWorkBuddyNeutralBinding(input);
611
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
612
+ const result = await invoke("ResolveWorkBuddyNeutralBinding", {});
613
+ const binding = assertPersistentNeutralBinding(publicWorkBuddyNeutralBinding(result?.binding), root);
614
+ const capability = deserializeRecord(result?.record);
615
+ await assertCurrentWorkBuddyInstallationBinding(
616
+ installationStore,
617
+ installationBefore,
618
+ binding,
619
+ capability,
620
+ input,
621
+ );
622
+ return Object.freeze({ ...binding, capability });
623
+ }
624
+
625
+ async function verifyWorkBuddyNeutralBinding(input = {}) {
626
+ const selected = accountStore("workbuddy");
627
+ if (selected) return selected.verifyWorkBuddyNeutralBinding(input);
628
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
629
+ const result = await invoke("VerifyWorkBuddyNeutralBinding", {});
630
+ const binding = assertPersistentNeutralBinding(publicWorkBuddyNeutralBinding(result?.binding), root);
631
+ const capability = publicRecord(result?.record);
632
+ await assertCurrentWorkBuddyInstallationBinding(
633
+ installationStore,
634
+ installationBefore,
635
+ binding,
636
+ capability,
637
+ input,
638
+ );
639
+ return Object.freeze({ ...binding, capability });
640
+ }
641
+
642
+ async function removeWorkBuddyNeutralBinding(input = {}) {
643
+ const installation = await resolveActiveInstallation(installationStore, input);
644
+ const delegateBinding = await resolveWorkBuddyInstallationBinding(installationStore, input, installation);
645
+ const expected = publicWorkBuddyNeutralBinding(input);
646
+ if (expected.region !== installation.region
647
+ || expected.principalId !== installation.principalId
648
+ || expected.hostInstallationId !== installation.hostInstallationId
649
+ || expected.profileId !== delegateBinding.profileId
650
+ || expected.delegateId !== delegateBinding.delegateId) {
651
+ throw codeError("neutral_workbuddy_capability_binding_mismatch");
652
+ }
653
+ const result = await invoke("RemoveWorkBuddyNeutralBinding", {
654
+ stdin: JSON.stringify(expected),
655
+ });
656
+ if (result?.removed !== true) throw codeError("neutral_workbuddy_binding_configuration_required");
657
+ return Object.freeze({ removed: true });
658
+ }
659
+
660
+ async function migrateWorkBuddyNeutralBinding(input = {}) {
661
+ return migrateNeutralBinding({
662
+ command: "MigrateWorkBuddyNeutralBinding",
663
+ input,
664
+ adapterId: "workbuddy",
665
+ bindingCode: "neutral_workbuddy_capability_binding_mismatch",
666
+ descriptor: workBuddyNeutralBindingDescriptor,
667
+ publicBinding: publicWorkBuddyNeutralBinding,
668
+ resolveInstallationBinding: resolveWorkBuddyInstallationBinding,
669
+ assertCurrent: assertCurrentWorkBuddyInstallationBinding,
670
+ });
671
+ }
672
+
673
+ async function saveClaudeCodeNeutralBinding(input = {}) {
674
+ assertClaudeCodeStoreRoot(root);
675
+ assertClientAccountCurrent("claude-code", root);
676
+ const installation = await resolveActiveInstallation(installationStore, input);
677
+ const delegateBinding = await resolveClaudeCodeInstallationBinding(installationStore, input, installation);
678
+ const descriptor = claudeCodeNeutralBindingDescriptor(input, installation, delegateBinding);
679
+ const result = await invoke("SaveClaudeCodeNeutralBinding", { stdin: JSON.stringify(descriptor) });
680
+ return assertPersistentNeutralBinding(publicClaudeCodeNeutralBinding(result?.binding), root);
681
+ }
682
+
683
+ async function resolveClaudeCodeNeutralBinding(input = {}) {
684
+ assertClaudeCodeStoreRoot(root);
685
+ const selected = accountStore("claude-code");
686
+ if (selected) return selected.resolveClaudeCodeNeutralBinding(input);
687
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
688
+ const result = await invoke("ResolveClaudeCodeNeutralBinding", {});
689
+ const binding = assertPersistentNeutralBinding(publicClaudeCodeNeutralBinding(result?.binding), root);
690
+ const capability = deserializeRecord(result?.record);
691
+ await assertCurrentClaudeCodeInstallationBinding(
692
+ installationStore,
693
+ installationBefore,
694
+ binding,
695
+ capability,
696
+ input,
697
+ );
698
+ return Object.freeze({ ...binding, capability });
699
+ }
700
+
701
+ async function verifyClaudeCodeNeutralBinding(input = {}) {
702
+ assertClaudeCodeStoreRoot(root);
703
+ const selected = accountStore("claude-code");
704
+ if (selected) return selected.verifyClaudeCodeNeutralBinding(input);
705
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
706
+ const result = await invoke("VerifyClaudeCodeNeutralBinding", {});
707
+ const binding = assertPersistentNeutralBinding(publicClaudeCodeNeutralBinding(result?.binding), root);
708
+ const capability = publicRecord(result?.record);
709
+ await assertCurrentClaudeCodeInstallationBinding(
710
+ installationStore,
711
+ installationBefore,
712
+ binding,
713
+ capability,
714
+ input,
715
+ );
716
+ return Object.freeze({ ...binding, capability });
717
+ }
718
+
719
+ async function removeClaudeCodeNeutralBinding(input = {}) {
720
+ assertClaudeCodeStoreRoot(root);
721
+ assertClientAccountCurrent("claude-code", root);
722
+ const installation = await resolveActiveInstallation(installationStore, input);
723
+ const delegateBinding = await resolveClaudeCodeInstallationBinding(installationStore, input, installation);
724
+ const expected = publicClaudeCodeNeutralBinding(input);
725
+ if (expected.region !== installation.region
726
+ || expected.principalId !== installation.principalId
727
+ || expected.hostInstallationId !== installation.hostInstallationId
728
+ || expected.profileId !== delegateBinding.profileId
729
+ || expected.delegateId !== delegateBinding.delegateId) {
730
+ throw codeError("neutral_claude_code_capability_binding_mismatch");
731
+ }
732
+ const result = await invoke("RemoveClaudeCodeNeutralBinding", {
733
+ stdin: JSON.stringify(expected),
734
+ });
735
+ if (result?.removed !== true) throw codeError("neutral_claude_code_binding_configuration_required");
736
+ return Object.freeze({ removed: true });
737
+ }
738
+
739
+ async function migrateClaudeCodeNeutralBinding(input = {}) {
740
+ assertClaudeCodeStoreRoot(root);
741
+ assertClientAccountCurrent("claude-code", root);
742
+ return migrateNeutralBinding({
743
+ command: "MigrateClaudeCodeNeutralBinding",
744
+ input,
745
+ adapterId: "claude-code",
746
+ bindingCode: "neutral_claude_code_capability_binding_mismatch",
747
+ descriptor: claudeCodeNeutralBindingDescriptor,
748
+ publicBinding: publicClaudeCodeNeutralBinding,
749
+ resolveInstallationBinding: resolveClaudeCodeInstallationBinding,
750
+ assertCurrent: assertCurrentClaudeCodeInstallationBinding,
751
+ });
752
+ }
753
+
754
+ async function saveCursorNeutralBinding(input = {}) {
755
+ const installation = await resolveActiveInstallation(installationStore, input);
756
+ const delegateBinding = await resolveCursorInstallationBinding(installationStore, input, installation);
757
+ const descriptor = cursorNeutralBindingDescriptor(input, installation, delegateBinding);
758
+ const result = await invoke("SaveCursorNeutralBinding", { stdin: JSON.stringify(descriptor) });
759
+ return assertPersistentNeutralBinding(publicCursorNeutralBinding(result?.binding), root);
760
+ }
761
+
762
+ async function resolveCursorNeutralBinding(input = {}) {
763
+ const selected = accountStore("cursor");
764
+ if (selected) return selected.resolveCursorNeutralBinding(input);
765
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
766
+ const result = await invoke("ResolveCursorNeutralBinding", {});
767
+ const binding = assertPersistentNeutralBinding(publicCursorNeutralBinding(result?.binding), root);
768
+ const capability = deserializeRecord(result?.record);
769
+ await assertCurrentCursorInstallationBinding(
770
+ installationStore,
771
+ installationBefore,
772
+ binding,
773
+ capability,
774
+ input,
775
+ );
776
+ return Object.freeze({ ...binding, capability });
777
+ }
778
+
779
+ async function verifyCursorNeutralBinding(input = {}) {
780
+ const selected = accountStore("cursor");
781
+ if (selected) return selected.verifyCursorNeutralBinding(input);
782
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
783
+ const result = await invoke("VerifyCursorNeutralBinding", {});
784
+ const binding = assertPersistentNeutralBinding(publicCursorNeutralBinding(result?.binding), root);
785
+ const capability = publicRecord(result?.record);
786
+ await assertCurrentCursorInstallationBinding(
787
+ installationStore,
788
+ installationBefore,
789
+ binding,
790
+ capability,
791
+ input,
792
+ );
793
+ return Object.freeze({ ...binding, capability });
794
+ }
795
+
796
+ async function removeCursorNeutralBinding(input = {}) {
797
+ const installation = await resolveActiveInstallation(installationStore, input);
798
+ const delegateBinding = await resolveCursorInstallationBinding(installationStore, input, installation);
799
+ const expected = publicCursorNeutralBinding(input);
800
+ if (expected.region !== installation.region
801
+ || expected.principalId !== installation.principalId
802
+ || expected.hostInstallationId !== installation.hostInstallationId
803
+ || expected.profileId !== delegateBinding.profileId
804
+ || expected.delegateId !== delegateBinding.delegateId) {
805
+ throw codeError("neutral_cursor_capability_binding_mismatch");
806
+ }
807
+ const result = await invoke("RemoveCursorNeutralBinding", {
808
+ stdin: JSON.stringify(expected),
809
+ });
810
+ if (result?.removed !== true) throw codeError("neutral_cursor_binding_configuration_required");
811
+ return Object.freeze({ removed: true });
812
+ }
813
+
814
+ async function migrateCursorNeutralBinding(input = {}) {
815
+ return migrateNeutralBinding({
816
+ command: "MigrateCursorNeutralBinding",
817
+ input,
818
+ adapterId: "cursor",
819
+ bindingCode: "neutral_cursor_capability_binding_mismatch",
820
+ descriptor: cursorNeutralBindingDescriptor,
821
+ publicBinding: publicCursorNeutralBinding,
822
+ resolveInstallationBinding: resolveCursorInstallationBinding,
823
+ assertCurrent: assertCurrentCursorInstallationBinding,
824
+ });
825
+ }
826
+
827
+ async function saveDshNeutralBinding(input = {}) {
828
+ const installation = await resolveActiveInstallation(installationStore, input);
829
+ const delegateBinding = await resolveDshInstallationBinding(installationStore, input, installation);
830
+ const descriptor = dshNeutralBindingDescriptor(input, installation, delegateBinding);
831
+ const result = await invoke("SaveDshNeutralBinding", { stdin: JSON.stringify(descriptor) });
832
+ return assertPersistentNeutralBinding(publicDshNeutralBinding(result?.binding), root);
833
+ }
834
+
835
+ async function resolveDshNeutralBinding(input = {}) {
836
+ const selected = accountStore("dsh");
837
+ if (selected) return selected.resolveDshNeutralBinding(input);
838
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
839
+ const result = await invoke("ResolveDshNeutralBinding", {});
840
+ const binding = assertPersistentNeutralBinding(publicDshNeutralBinding(result?.binding), root);
841
+ const capability = deserializeRecord(result?.record);
842
+ await assertCurrentDshInstallationBinding(installationStore, installationBefore, binding, capability, input);
843
+ return Object.freeze({ ...binding, capability });
844
+ }
845
+
846
+ async function verifyDshNeutralBinding(input = {}) {
847
+ const selected = accountStore("dsh");
848
+ if (selected) return selected.verifyDshNeutralBinding(input);
849
+ const installationBefore = await resolveActiveInstallation(installationStore, input);
850
+ const result = await invoke("VerifyDshNeutralBinding", {});
851
+ const binding = assertPersistentNeutralBinding(publicDshNeutralBinding(result?.binding), root);
852
+ const capability = publicRecord(result?.record);
853
+ await assertCurrentDshInstallationBinding(installationStore, installationBefore, binding, capability, input);
854
+ return Object.freeze({ ...binding, capability });
855
+ }
856
+
857
+ async function removeDshNeutralBinding(input = {}) {
858
+ const installation = await resolveActiveInstallation(installationStore, input);
859
+ const delegateBinding = await resolveDshInstallationBinding(installationStore, input, installation);
860
+ const expected = publicDshNeutralBinding(input);
861
+ if (expected.region !== installation.region || expected.principalId !== installation.principalId
862
+ || expected.hostInstallationId !== installation.hostInstallationId
863
+ || expected.profileId !== delegateBinding.profileId || expected.delegateId !== delegateBinding.delegateId) {
864
+ throw codeError("neutral_dsh_capability_binding_mismatch");
865
+ }
866
+ const result = await invoke("RemoveDshNeutralBinding", { stdin: JSON.stringify(expected) });
867
+ if (result?.removed !== true) throw codeError("neutral_dsh_binding_configuration_required");
868
+ return Object.freeze({ removed: true });
869
+ }
870
+
871
+ async function migrateDshNeutralBinding(input = {}) {
872
+ return migrateNeutralBinding({
873
+ command: "MigrateDshNeutralBinding", input, adapterId: "dsh",
874
+ bindingCode: "neutral_dsh_capability_binding_mismatch",
875
+ descriptor: dshNeutralBindingDescriptor, publicBinding: publicDshNeutralBinding,
876
+ resolveInstallationBinding: resolveDshInstallationBinding,
877
+ assertCurrent: assertCurrentDshInstallationBinding,
878
+ });
879
+ }
880
+
881
+ // Explicit recovery to the one supported prior DSH scope contract. Normal
882
+ // save/migrate still require current scopes; this cannot select arbitrary scopes.
883
+ async function restoreDshNeutralBinding(input = {}) {
884
+ if (!Array.isArray(input?.replacementBinding?.scopes)
885
+ || !sameStrings(input.replacementBinding.scopes, HELLOLYNK_DSH_NEUTRAL_PREVIOUS_SCOPES)) {
886
+ throw codeError("neutral_dsh_capability_binding_mismatch");
887
+ }
888
+ return migrateNeutralBinding({
889
+ command: "RestoreDshNeutralBinding", input, adapterId: "dsh",
890
+ bindingCode: "neutral_dsh_capability_binding_mismatch",
891
+ descriptor: (value, installation, delegate) => dshNeutralBindingDescriptor(value, installation, delegate, true),
892
+ publicBinding: publicDshNeutralBinding,
893
+ resolveInstallationBinding: resolveDshInstallationBinding,
894
+ assertCurrent: assertCurrentDshInstallationBinding,
895
+ });
896
+ }
897
+
898
+ async function migrateNeutralBinding(spec) {
899
+ const previousCapabilityId = assertCapabilityId(spec.input?.previousCapabilityId);
900
+ const installation = await resolveActiveInstallation(
901
+ installationStore,
902
+ spec.input?.replacementBinding,
903
+ );
904
+ const delegateBinding = await spec.resolveInstallationBinding(
905
+ installationStore,
906
+ spec.input?.replacementBinding,
907
+ installation,
908
+ );
909
+ const previousBinding = spec.publicBinding(spec.input?.previousBinding);
910
+ if (previousBinding.region !== installation.region
911
+ || previousBinding.principalId !== installation.principalId
912
+ || previousBinding.hostInstallationId !== installation.hostInstallationId
913
+ || previousBinding.profileId !== delegateBinding.profileId
914
+ || previousBinding.delegateId !== delegateBinding.delegateId) {
915
+ throw codeError(spec.bindingCode);
916
+ }
917
+ const replacementBinding = spec.descriptor(
918
+ spec.input?.replacementBinding,
919
+ installation,
920
+ delegateBinding,
921
+ );
922
+ const replacementRecord = createRecord({
923
+ region: replacementBinding.region,
924
+ principalId: replacementBinding.principalId,
925
+ delegateId: replacementBinding.delegateId,
926
+ hostInstallationId: replacementBinding.hostInstallationId,
927
+ adapterId: spec.adapterId,
928
+ version: replacementBinding.version,
929
+ scopes: replacementBinding.scopes,
930
+ }, { clock, random });
931
+ if (replacementRecord.capabilityId === previousCapabilityId) {
932
+ replacementRecord.secretBuffer.fill(0);
933
+ throw codeError("host_capability_rotation_id_reused");
934
+ }
935
+ let result;
936
+ try {
937
+ result = await invoke(spec.command, {
938
+ capabilityId: previousCapabilityId,
939
+ stdin: JSON.stringify({
940
+ previousBinding,
941
+ replacementBinding,
942
+ replacementRecord: JSON.parse(serializeRecord(replacementRecord)),
943
+ }),
944
+ });
945
+ } finally {
946
+ replacementRecord.secretBuffer.fill(0);
947
+ invalidateCapabilityCache();
948
+ }
949
+ if (result?.replacedCapabilityId !== previousCapabilityId) {
950
+ throw codeError("host_capability_rotation_invalid");
951
+ }
952
+ let capability;
953
+ try {
954
+ const binding = spec.publicBinding(result?.binding);
955
+ capability = deserializeRecord(result?.record);
956
+ await spec.assertCurrent(installationStore, installation, binding, capability, binding);
957
+ return Object.freeze({ ...binding, capability });
958
+ } catch (error) {
959
+ capability?.secretBuffer?.fill(0);
960
+ throw error;
961
+ }
962
+ }
963
+
964
+ async function saveTransportCredential(input = {}, installationInput = {}) {
965
+ const installation = await resolveActiveInstallation(installationStore, installationInput);
966
+ const record = transportCredentialRecord(input, installation);
967
+ if (record.expiresAt <= assertTimestamp(clock())) throw codeError("host_transport_credential_expired");
968
+ const result = await invoke("SaveTransportCredential", { stdin: JSON.stringify(record) });
969
+ return deserializeTransportCredential(result?.record, installation, clock(), { allowExpired: false });
970
+ }
971
+
972
+ async function disconnectLocalClient(client) {
973
+ if (!["codex", "openclaw", "trae-work", "workbuddy", "cursor", "dsh", "claude-code"].includes(client)) throw codeError("workbench_account_client_invalid");
974
+ if (client === "claude-code") assertClaudeCodeStoreRoot(root);
975
+ try {
976
+ const result = await invoke("DisconnectLocalClient", { stdin: JSON.stringify({ client }) });
977
+ if (result?.disconnected !== true || result.client !== client) throw codeError("workbench_disconnect_failed");
978
+ return Object.freeze({ disconnected: true });
979
+ } finally { invalidateCapabilityCache(); }
980
+ }
981
+
982
+ async function saveEnrollmentTransportCredential(input = {}, installationInput = {}) {
983
+ const installation = await resolveActiveInstallation(installationStore, installationInput);
984
+ const record = transportCredentialRecord(input, installation);
985
+ if (record.expiresAt <= assertTimestamp(clock())) throw codeError("host_transport_credential_expired");
986
+ // The native transaction verifies the durable enrollment approval before
987
+ // replacing an older transport credential. Ordinary saves stay immutable.
988
+ const result = await invoke("SaveEnrollmentTransportCredential", { stdin: JSON.stringify(record) });
989
+ return deserializeTransportCredential(result?.record, installation, clock(), { allowExpired: false });
990
+ }
991
+
992
+ async function loadDeviceEnrollment() {
993
+ return validateDeviceEnrollmentResult(await invoke("LoadDeviceEnrollment"));
994
+ }
995
+
996
+ async function saveDeviceEnrollment(input = {}) {
997
+ if (!Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0
998
+ || !input.value || typeof input.value !== "object" || Array.isArray(input.value)) {
999
+ throw codeError("device_enrollment_store_invalid");
1000
+ }
1001
+ const stdin = JSON.stringify({ expectedRevision: input.expectedRevision, value: input.value });
1002
+ if (Buffer.byteLength(stdin, "utf8") > 60 * 1024) throw codeError("device_enrollment_store_invalid");
1003
+ return validateDeviceEnrollmentResult(await invoke("SaveDeviceEnrollment", { stdin }));
1004
+ }
1005
+
1006
+ async function loadTransportCredential(credentialId, installationInput = {}) {
1007
+ const installation = await resolveActiveInstallation(installationStore, installationInput);
1008
+ const result = await invoke("LoadTransportCredential", {
1009
+ credentialId: assertIdentifier(credentialId, "host_transport_credential_id_invalid"),
1010
+ principalId: installation.principalId,
1011
+ hostInstallationId: installation.hostInstallationId,
1012
+ timestamp: assertTimestamp(clock()),
1013
+ });
1014
+ return deserializeTransportCredential(result?.record, installation, clock(), { allowExpired: false });
1015
+ }
1016
+
1017
+ // Explicit maintenance only. Ordinary load/save must continue rejecting expired
1018
+ // credentials and immutable changes. Never return candidate or session secrets.
1019
+ async function migrateTransportCredentialLifetime(input = {}) {
1020
+ if (input.apply === true && readOnly) throw codeError("host_protected_store_read_only");
1021
+ const installation = await resolveActiveInstallation(installationStore, input);
1022
+ const profile = await readClientProfile(input.profileFile);
1023
+ if (profile.principalId !== installation.principalId || profile.hostId !== installation.hostInstallationId
1024
+ || profile.region !== installation.region
1025
+ || !installation.delegateBindings?.some((binding) => binding.profileId === profile.profileId
1026
+ && binding.delegateId === profile.delegateId && binding.authorizationVersion === profile.authorizationVersion)) {
1027
+ throw codeError("host_transport_migration_profile_mismatch");
1028
+ }
1029
+ const relay = new URL(profile.baseUrl.replace(/\/api\/v1\/?$/, ""));
1030
+ if (relay.protocol !== "https:" || relay.username || relay.password || relay.search || relay.hash
1031
+ || relay.pathname !== "/") throw codeError("host_transport_migration_https_required");
1032
+ const query = { principalId: installation.principalId, hostInstallationId: installation.hostInstallationId };
1033
+ const result = await invoke("VerifyTransportCredentialLifetimeMigration", { ...query, verifyOnly: true });
1034
+ const previous = deserializeTransportCredential(result?.record, installation, clock(), { allowExpired: true });
1035
+ const permanent = previous.expiresAt === PERSISTENT_CREDENTIAL_EXPIRES_AT;
1036
+ if (!permanent && previous.expiresAt - previous.issuedAt !== LEGACY_CREDENTIAL_LIFETIME_MS) {
1037
+ throw codeError("host_transport_migration_lifetime_invalid");
1038
+ }
1039
+ const summary = { credentialId: previous.credentialId, principalId: previous.principalId,
1040
+ hostId: previous.hostInstallationId, previousExpiresAt: previous.expiresAt,
1041
+ expiresAt: permanent ? previous.expiresAt : PERSISTENT_CREDENTIAL_EXPIRES_AT };
1042
+ if (permanent || input.apply !== true) return Object.freeze({ ...summary, status: permanent ? "already_permanent" : "eligible" });
1043
+ let response;
1044
+ try {
1045
+ response = await (options.migrationFetchImpl || globalThis.fetch)(`${relay.origin}/api/v2/host-transport/sessions`, {
1046
+ method: "POST", redirect: "error", signal: AbortSignal.timeout(8_000),
1047
+ headers: { "content-type": "application/json", "x-hellolynk-host-transport-credential": previous.secret },
1048
+ body: "{}",
1049
+ });
1050
+ } catch { throw codeError("host_transport_migration_http_failed"); }
1051
+ if (!response.ok) throw codeError("host_transport_migration_http_failed");
1052
+ let proof;
1053
+ try { proof = await response.json(); } catch { throw codeError("host_transport_migration_proof_invalid"); }
1054
+ if (!proof || typeof proof !== "object" || Array.isArray(proof)
1055
+ || proof.credentialId !== previous.credentialId || proof.principalId !== previous.principalId
1056
+ || proof.hostId !== previous.hostInstallationId || Date.parse(proof.credentialCreatedAt) !== previous.issuedAt
1057
+ || Date.parse(proof.credentialExpiresAt) !== PERSISTENT_CREDENTIAL_EXPIRES_AT
1058
+ || !TRANSPORT_SESSION_ID_PATTERN.test(proof.hostTransportSessionId)
1059
+ || !TRANSPORT_SESSION_SECRET_PATTERN.test(proof.hostTransportSessionSecret)
1060
+ || !(Date.parse(proof.expiresAt) > clock()) || Date.parse(proof.expiresAt) >= PERSISTENT_CREDENTIAL_EXPIRES_AT) {
1061
+ throw codeError("host_transport_migration_proof_invalid");
1062
+ }
1063
+ // Recheck owner/profile after network I/O; the native helper compares every
1064
+ // original credential field under its per-root mutex before the only write.
1065
+ await resolveActiveInstallation(installationStore, installation);
1066
+ const currentProfile = await readClientProfile(input.profileFile);
1067
+ if (JSON.stringify(currentProfile) !== JSON.stringify(profile)) throw codeError("host_transport_migration_profile_changed");
1068
+ const migrated = await invoke("MigrateTransportCredentialLifetime", { ...query, stdin: JSON.stringify({
1069
+ previous, proof: { credentialId: proof.credentialId, principalId: proof.principalId,
1070
+ hostId: proof.hostId, credentialCreatedAt: proof.credentialCreatedAt, credentialExpiresAt: proof.credentialExpiresAt },
1071
+ }) });
1072
+ if (migrated?.credentialId !== previous.credentialId || migrated?.expiresAt !== PERSISTENT_CREDENTIAL_EXPIRES_AT
1073
+ || migrated?.status !== "migrated") throw codeError("host_transport_migration_result_invalid");
1074
+ return Object.freeze({ ...summary, status: "migrated" });
1075
+ }
1076
+
1077
+ async function revokeTransportCredential(credentialId, installationInput = {}) {
1078
+ const installation = await resolveActiveInstallation(installationStore, installationInput);
1079
+ const result = await invoke("RevokeTransportCredential", {
1080
+ credentialId: assertIdentifier(credentialId, "host_transport_credential_id_invalid"),
1081
+ principalId: installation.principalId,
1082
+ hostInstallationId: installation.hostInstallationId,
1083
+ timestamp: assertTimestamp(clock()),
1084
+ });
1085
+ assertExactKeys(result, ["cascadedSessionCount", "credentialId", "revokedAt", "state"]);
1086
+ if (result.credentialId !== credentialId || result.state !== "revoked") {
1087
+ throw codeError("host_transport_credential_revoke_invalid");
1088
+ }
1089
+ return Object.freeze({
1090
+ credentialId: result.credentialId,
1091
+ state: result.state,
1092
+ revokedAt: assertTimestamp(result.revokedAt),
1093
+ cascadedSessionCount: assertNonNegativeInteger(
1094
+ result.cascadedSessionCount,
1095
+ "host_transport_credential_revoke_invalid",
1096
+ ),
1097
+ });
1098
+ }
1099
+
1100
+ async function saveTransportSession(input = {}, installationInput = {}) {
1101
+ return writeTransportSession("SaveTransportSession", input, installationInput);
1102
+ }
1103
+
1104
+ async function replaceTransportSession(input = {}, installationInput = {}) {
1105
+ return writeTransportSession("ReplaceTransportSession", input, installationInput);
1106
+ }
1107
+
1108
+ async function writeTransportSession(command, input, installationInput) {
1109
+ const installation = await resolveActiveInstallation(installationStore, installationInput);
1110
+ const record = transportSessionRecord(input, installation);
1111
+ const now = assertTimestamp(clock());
1112
+ if (record.expiresAt <= now) throw codeError("host_transport_session_expired");
1113
+ const result = await invoke(command, { stdin: JSON.stringify(record), timestamp: now });
1114
+ return deserializeTransportSession(result?.record, installation, null, now);
1115
+ }
1116
+
1117
+ async function loadTransportSession(sessionId, installationInput = {}) {
1118
+ const installation = await resolveActiveInstallation(installationStore, installationInput);
1119
+ const result = await invoke("LoadTransportSession", {
1120
+ sessionId: assertTransportSessionId(sessionId),
1121
+ principalId: installation.principalId,
1122
+ hostInstallationId: installation.hostInstallationId,
1123
+ timestamp: assertTimestamp(clock()),
1124
+ });
1125
+ return deserializeTransportSession(result?.record, installation, null, clock());
1126
+ }
1127
+
1128
+ async function removeTransportSession(sessionId, installationInput = {}) {
1129
+ const installation = await resolveActiveInstallation(installationStore, installationInput);
1130
+ const result = await invoke("RemoveTransportSession", {
1131
+ sessionId: assertTransportSessionId(sessionId),
1132
+ principalId: installation.principalId,
1133
+ hostInstallationId: installation.hostInstallationId,
1134
+ });
1135
+ assertExactKeys(result, ["removed", "sessionId"]);
1136
+ if (result.sessionId !== sessionId || typeof result.removed !== "boolean") {
1137
+ throw codeError("host_transport_session_remove_invalid");
1138
+ }
1139
+ return Object.freeze({ sessionId: result.sessionId, removed: result.removed });
1140
+ }
1141
+
1142
+ async function resolveTransport(installationInput = {}) {
1143
+ const installation = await resolveActiveInstallation(installationStore, installationInput);
1144
+ const now = assertTimestamp(clock());
1145
+ const result = await invoke("ResolveTransport", {
1146
+ principalId: installation.principalId,
1147
+ hostInstallationId: installation.hostInstallationId,
1148
+ timestamp: now,
1149
+ });
1150
+ assertExactKeys(result, ["credential", "session"]);
1151
+ const credential = deserializeTransportCredential(result.credential, installation, now, { allowExpired: false });
1152
+ const session = result.session === null
1153
+ ? null
1154
+ : deserializeTransportSession(result.session, installation, credential.credentialId, now);
1155
+ return Object.freeze({ credential, session });
1156
+ }
1157
+
1158
+ async function verifyTransport(installationInput = {}) {
1159
+ const installation = await resolveActiveInstallation(installationStore, installationInput);
1160
+ const now = assertTimestamp(clock());
1161
+ const result = await invoke("VerifyTransport", {
1162
+ principalId: installation.principalId,
1163
+ hostInstallationId: installation.hostInstallationId,
1164
+ timestamp: now,
1165
+ });
1166
+ assertExactKeys(result, ["credential", "session"]);
1167
+ assertExactKeys(result.credential, [
1168
+ "credentialId", "expiresAt", "hostInstallationId", "principalId",
1169
+ ]);
1170
+ const credential = Object.freeze({
1171
+ credentialId: assertIdentifier(result.credential.credentialId, "host_transport_credential_id_invalid"),
1172
+ principalId: assertIdentifier(result.credential.principalId, "host_principal_id_invalid"),
1173
+ hostInstallationId: assertIdentifier(result.credential.hostInstallationId, "host_installation_id_invalid"),
1174
+ expiresAt: assertTimestamp(result.credential.expiresAt),
1175
+ });
1176
+ if (credential.principalId !== installation.principalId
1177
+ || credential.hostInstallationId !== installation.hostInstallationId
1178
+ || credential.expiresAt <= now) throw codeError("host_transport_credential_owner_mismatch");
1179
+ let session = null;
1180
+ if (result.session !== null) {
1181
+ assertExactKeys(result.session, [
1182
+ "credentialId", "expiresAt", "hostInstallationId", "principalId", "sessionId",
1183
+ ]);
1184
+ session = Object.freeze({
1185
+ sessionId: assertTransportSessionId(result.session.sessionId),
1186
+ credentialId: assertIdentifier(result.session.credentialId, "host_transport_credential_id_invalid"),
1187
+ principalId: assertIdentifier(result.session.principalId, "host_principal_id_invalid"),
1188
+ hostInstallationId: assertIdentifier(result.session.hostInstallationId, "host_installation_id_invalid"),
1189
+ expiresAt: assertTimestamp(result.session.expiresAt),
1190
+ });
1191
+ if (session.credentialId !== credential.credentialId
1192
+ || session.principalId !== installation.principalId
1193
+ || session.hostInstallationId !== installation.hostInstallationId
1194
+ || session.expiresAt <= now) throw codeError("host_transport_session_owner_mismatch");
1195
+ }
1196
+ return Object.freeze({ credential, session });
1197
+ }
1198
+
1199
+ const hostTransportCredentialProvider = Object.freeze({
1200
+ async get(installationInput = {}) {
1201
+ return (await resolveTransport(installationInput)).credential.secret;
1202
+ },
1203
+ });
1204
+
1205
+ const hostTransportSessionProvider = Object.freeze({
1206
+ async get(installationInput = {}) {
1207
+ const resolved = await resolveTransport(installationInput);
1208
+ if (!resolved.session) throw codeError("host_transport_session_invalid");
1209
+ return clientTransportSession(resolved.session);
1210
+ },
1211
+ async save(value, installationInput = {}) {
1212
+ const resolved = await resolveTransport(installationInput);
1213
+ const session = await replaceTransportSession({
1214
+ ...value,
1215
+ credentialId: resolved.credential.credentialId,
1216
+ hostInstallationId: value?.hostInstallationId ?? value?.hostId,
1217
+ issuedAt: value?.issuedAt ?? clock(),
1218
+ }, installationInput);
1219
+ return clientTransportSession(session);
1220
+ },
1221
+ async remove(installationInput = {}) {
1222
+ const resolved = await resolveTransport(installationInput);
1223
+ if (!resolved.session) return Object.freeze({ sessionId: null, removed: false });
1224
+ return removeTransportSession(resolved.session.sessionId, installationInput);
1225
+ },
1226
+ });
1227
+
1228
+ return Object.freeze({
1229
+ root,
1230
+ issue,
1231
+ load,
1232
+ revoke,
1233
+ disconnectLocalClient,
1234
+ rotate,
1235
+ status,
1236
+ capabilityResolver: resolveCapability,
1237
+ saveCodexNeutralBinding,
1238
+ resolveCodexNeutralBinding,
1239
+ verifyCodexNeutralBinding,
1240
+ removeCodexNeutralBinding,
1241
+ replaceCodexNeutralBinding,
1242
+ migrateCodexNeutralBinding,
1243
+ saveOpenClawNeutralBinding,
1244
+ resolveOpenClawNeutralBinding,
1245
+ verifyOpenClawNeutralBinding,
1246
+ removeOpenClawNeutralBinding,
1247
+ migrateOpenClawNeutralBinding,
1248
+ saveTraeWorkNeutralBinding,
1249
+ resolveTraeWorkNeutralBinding,
1250
+ verifyTraeWorkNeutralBinding,
1251
+ removeTraeWorkNeutralBinding,
1252
+ migrateTraeWorkNeutralBinding,
1253
+ saveWorkBuddyNeutralBinding,
1254
+ resolveWorkBuddyNeutralBinding,
1255
+ verifyWorkBuddyNeutralBinding,
1256
+ removeWorkBuddyNeutralBinding,
1257
+ migrateWorkBuddyNeutralBinding,
1258
+ saveClaudeCodeNeutralBinding,
1259
+ resolveClaudeCodeNeutralBinding,
1260
+ verifyClaudeCodeNeutralBinding,
1261
+ removeClaudeCodeNeutralBinding,
1262
+ migrateClaudeCodeNeutralBinding,
1263
+ saveCursorNeutralBinding,
1264
+ resolveCursorNeutralBinding,
1265
+ verifyCursorNeutralBinding,
1266
+ removeCursorNeutralBinding,
1267
+ migrateCursorNeutralBinding,
1268
+ saveDshNeutralBinding,
1269
+ resolveDshNeutralBinding,
1270
+ verifyDshNeutralBinding,
1271
+ removeDshNeutralBinding,
1272
+ migrateDshNeutralBinding,
1273
+ restoreDshNeutralBinding,
1274
+ saveTransportCredential,
1275
+ loadDeviceEnrollment,
1276
+ saveDeviceEnrollment,
1277
+ saveEnrollmentTransportCredential,
1278
+ loadTransportCredential,
1279
+ migrateTransportCredentialLifetime,
1280
+ revokeTransportCredential,
1281
+ saveTransportSession,
1282
+ loadTransportSession,
1283
+ removeTransportSession,
1284
+ replaceTransportSession,
1285
+ resolveTransport,
1286
+ verifyTransport,
1287
+ hostTransportCredentialProvider,
1288
+ hostTransportSessionProvider,
1289
+ });
1290
+ }
1291
+
1292
+ // The native resolver also checks launch paths outside the credential file.
1293
+ // Check those current paths even on a cache hit; never hide a removed runner
1294
+ // or a directory replaced with a junction behind an unchanged credential.
1295
+ async function neutralBindingPathsPresent(binding, signal) {
1296
+ const paths = [[binding.runtimeRoot, true, 0]];
1297
+ if (binding.adapterId === "codex") paths.push(
1298
+ [binding.releaseRoot, true, 0],
1299
+ [binding.profileManifestPath, false, 65536],
1300
+ [resolve(binding.releaseRoot, binding.runnerRelativePath), false, 1048576],
1301
+ );
1302
+ for (const [target, directory, maxBytes] of paths) {
1303
+ for (let current = target; ; current = dirname(current)) {
1304
+ signal?.throwIfAborted();
1305
+ const stat = await lstat(current).catch(() => null);
1306
+ if (!stat || stat.isSymbolicLink()) return false;
1307
+ if (current === target && !directory) {
1308
+ if (!stat.isFile() || stat.size > maxBytes) return false;
1309
+ } else if (!stat.isDirectory()) return false;
1310
+ if (dirname(current) === current) break;
1311
+ }
1312
+ }
1313
+ return true;
1314
+ }
1315
+
1316
+ // Reuse a successful ACL/path check while files and directory identities stay
1317
+ // unchanged. Files include ctime: Windows ACL edits need not change mtime.
1318
+ // Directories omit timestamps so ordinary writes do not invalidate the cache.
1319
+ // Per the user's 2026-10-12 decision, in-place directory ACL changes can remain
1320
+ // cached until Host restart; cold checks and replacement/junction checks remain.
1321
+ // Credentials, bindings and authorization state are still read separately.
1322
+ const VERIFIED_PATHS = new Map();
1323
+ const MAX_VERIFIED_PATHS = 256;
1324
+
1325
+ async function protectedPathFingerprint(root, target, helperPath, directory, signal) {
1326
+ const components = new Set();
1327
+ for (const leaf of [root, target, helperPath]) {
1328
+ for (let cursor = leaf; ; cursor = dirname(cursor)) {
1329
+ components.add(cursor);
1330
+ if (dirname(cursor) === cursor) break;
1331
+ }
1332
+ }
1333
+ const parts = [];
1334
+ for (const component of components) {
1335
+ signal?.throwIfAborted();
1336
+ let stat;
1337
+ try { stat = await lstat(component, { bigint: true }); }
1338
+ catch { return null; } // Let the existing verifier report the path error.
1339
+ const isFile = component === helperPath || (component === target && !directory);
1340
+ if (stat.isSymbolicLink() || (isFile ? !stat.isFile() : !stat.isDirectory())) return null;
1341
+ const identity = [component, stat.dev, stat.ino, stat.mode, stat.birthtimeNs];
1342
+ parts.push((isFile
1343
+ ? [...identity, stat.nlink, stat.size, stat.ctimeNs, stat.mtimeNs] : identity).join(":"));
1344
+ }
1345
+ signal?.throwIfAborted();
1346
+ return parts.join("\n");
1347
+ }
1348
+
1349
+ export async function verifyHostProtectedStorePath(options = {}) {
1350
+ const root = assertRoot(options.root || defaultNeutralHostStoreRoot("win32", options.env));
1351
+ const target = resolve(String(options.path || ""));
1352
+ const relativeTarget = relative(root, target);
1353
+ if (!isAbsolute(String(options.path || ""))
1354
+ || (relativeTarget !== "" && (isAbsolute(relativeTarget) || relativeTarget === ".." || relativeTarget.startsWith(`..\\`)))) {
1355
+ throw codeError("host_protected_store_path_invalid");
1356
+ }
1357
+ const helperPath = resolve(options.helperPath || SCRIPT_PATH);
1358
+ const directory = options.directory === true;
1359
+ const key = JSON.stringify([root, target, helperPath, directory]);
1360
+ const before = await protectedPathFingerprint(root, target, helperPath, directory, options.signal);
1361
+ options.signal?.throwIfAborted();
1362
+ if (before && VERIFIED_PATHS.get(key) === before) return Object.freeze({ verified: true });
1363
+ VERIFIED_PATHS.delete(key);
1364
+ const result = await invokeWindowsStore({
1365
+ command: "VerifyPath",
1366
+ root,
1367
+ helperPath,
1368
+ verifyOnly: true,
1369
+ signal: options.signal,
1370
+ pathToVerify: target,
1371
+ verifyDirectory: directory,
1372
+ });
1373
+ assertExactKeys(result, ["verified"]);
1374
+ if (result.verified !== true) throw codeError("host_protected_store_path_invalid");
1375
+ const after = await protectedPathFingerprint(root, target, helperPath, directory, options.signal);
1376
+ if (before && after === before) {
1377
+ if (VERIFIED_PATHS.size >= MAX_VERIFIED_PATHS) VERIFIED_PATHS.delete(VERIFIED_PATHS.keys().next().value);
1378
+ VERIFIED_PATHS.set(key, after);
1379
+ }
1380
+ return Object.freeze({ verified: true });
1381
+ }
1382
+
1383
+ function serializeRootInvoke(root, callback) {
1384
+ const key = process.platform === "win32"
1385
+ ? root.replace(/[\\/]+$/, "").toUpperCase()
1386
+ : root;
1387
+ const previous = ROOT_INVOKE_QUEUES.get(key) || Promise.resolve();
1388
+ const current = previous.catch(() => {}).then(callback);
1389
+ ROOT_INVOKE_QUEUES.set(key, current);
1390
+ return current.finally(() => {
1391
+ if (ROOT_INVOKE_QUEUES.get(key) === current) ROOT_INVOKE_QUEUES.delete(key);
1392
+ });
1393
+ }
1394
+
1395
+ async function resolveOpenClawInstallationBinding(store, input, installation) {
1396
+ const profileId = assertIdentifier(input?.profileId, "neutral_openclaw_binding_invalid");
1397
+ const delegateId = input?.delegateId == null
1398
+ ? null
1399
+ : assertIdentifier(input.delegateId, "neutral_openclaw_binding_invalid");
1400
+ let binding;
1401
+ try {
1402
+ binding = await store.resolveBinding({ profileId, delegateId });
1403
+ } catch {
1404
+ throw codeError("neutral_openclaw_capability_binding_mismatch");
1405
+ }
1406
+ if (binding.region !== installation.region
1407
+ || binding.principalId !== installation.principalId
1408
+ || binding.hostInstallationId !== installation.hostInstallationId
1409
+ || !binding.adapters.includes("openclaw")) {
1410
+ throw codeError("neutral_openclaw_capability_binding_mismatch");
1411
+ }
1412
+ return binding;
1413
+ }
1414
+
1415
+ async function resolveTraeWorkInstallationBinding(store, input, installation) {
1416
+ const profileId = assertIdentifier(input?.profileId, "neutral_trae_work_binding_invalid");
1417
+ const delegateId = input?.delegateId == null
1418
+ ? null
1419
+ : assertIdentifier(input.delegateId, "neutral_trae_work_binding_invalid");
1420
+ let binding;
1421
+ try {
1422
+ binding = await store.resolveBinding({ profileId, delegateId });
1423
+ } catch {
1424
+ throw codeError("neutral_trae_work_capability_binding_mismatch");
1425
+ }
1426
+ if (binding.region !== installation.region
1427
+ || binding.principalId !== installation.principalId
1428
+ || binding.hostInstallationId !== installation.hostInstallationId
1429
+ || !binding.adapters.includes("trae-work")) {
1430
+ throw codeError("neutral_trae_work_capability_binding_mismatch");
1431
+ }
1432
+ return binding;
1433
+ }
1434
+
1435
+ function traeWorkNeutralBindingDescriptor(input, installation, delegateBinding) {
1436
+ const scopes = canonicalScopes(input?.scopes || HELLOLYNK_TRAE_WORK_NEUTRAL_EXACT_SCOPES);
1437
+ const acceptedScopes = sameStrings(scopes, HELLOLYNK_TRAE_WORK_NEUTRAL_EXACT_SCOPES)
1438
+ || (input?.allowLegacyScopes === true
1439
+ && (sameStrings(scopes, HELLOLYNK_TRAE_WORK_NEUTRAL_PREVIOUS_SCOPES)
1440
+ || sameStrings(scopes, HELLOLYNK_TRAE_WORK_NEUTRAL_LEGACY_SCOPES)));
1441
+ if (!acceptedScopes
1442
+ || (input?.adapterId ?? "trae-work") !== "trae-work"
1443
+ || Number(input?.version ?? 2) !== 2) {
1444
+ throw codeError("neutral_trae_work_capability_binding_mismatch");
1445
+ }
1446
+ const release = optionalTraeWorkReleaseBinding(input);
1447
+ return Object.freeze({
1448
+ contract: HELLOLYNK_TRAE_WORK_NEUTRAL_BINDING_CONTRACT,
1449
+ region: installation.region,
1450
+ principalId: installation.principalId,
1451
+ delegateId: delegateBinding.delegateId,
1452
+ hostInstallationId: installation.hostInstallationId,
1453
+ profileId: delegateBinding.profileId,
1454
+ adapterId: "trae-work",
1455
+ version: 2,
1456
+ scopes,
1457
+ runtimeRoot: assertAbsolutePath(input?.runtimeRoot, "neutral_trae_work_binding_invalid"),
1458
+ ...release,
1459
+ });
1460
+ }
1461
+
1462
+ function publicTraeWorkNeutralBinding(value) {
1463
+ const releaseKeys = [
1464
+ "profileManifestPath", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
1465
+ ];
1466
+ const releaseCount = releaseKeys.filter((key) => Object.hasOwn(value || {}, key)).length;
1467
+ assertExactKeysWithCode(value, [
1468
+ "adapterId", "contract", "delegateId", "hostInstallationId", "principalId",
1469
+ "profileId", "region", "runtimeRoot", "scopes", "version",
1470
+ ...(releaseCount ? releaseKeys : []),
1471
+ ], "neutral_trae_work_binding_invalid");
1472
+ if (releaseCount !== 0 && releaseCount !== releaseKeys.length) {
1473
+ throw codeError("neutral_trae_work_binding_invalid");
1474
+ }
1475
+ if (value.contract !== HELLOLYNK_TRAE_WORK_NEUTRAL_BINDING_CONTRACT
1476
+ || value.adapterId !== "trae-work" || Number(value.version) !== 2) {
1477
+ throw codeError("neutral_trae_work_binding_invalid");
1478
+ }
1479
+ const scopes = canonicalScopes(value.scopes);
1480
+ if (!sameStrings(scopes, HELLOLYNK_TRAE_WORK_NEUTRAL_EXACT_SCOPES)
1481
+ && !sameStrings(scopes, HELLOLYNK_TRAE_WORK_NEUTRAL_PREVIOUS_SCOPES)
1482
+ && !sameStrings(scopes, HELLOLYNK_TRAE_WORK_NEUTRAL_LEGACY_SCOPES)) {
1483
+ throw codeError("neutral_trae_work_binding_invalid");
1484
+ }
1485
+ return Object.freeze({
1486
+ contract: value.contract,
1487
+ region: assertIdentifier(value.region, "neutral_trae_work_binding_invalid"),
1488
+ principalId: assertIdentifier(value.principalId, "neutral_trae_work_binding_invalid"),
1489
+ delegateId: assertIdentifier(value.delegateId, "neutral_trae_work_binding_invalid"),
1490
+ hostInstallationId: assertIdentifier(value.hostInstallationId, "neutral_trae_work_binding_invalid"),
1491
+ profileId: assertIdentifier(value.profileId, "neutral_trae_work_binding_invalid"),
1492
+ adapterId: "trae-work",
1493
+ version: 2,
1494
+ scopes: Object.freeze(scopes),
1495
+ runtimeRoot: assertAbsolutePath(value.runtimeRoot, "neutral_trae_work_binding_invalid"),
1496
+ ...(releaseCount === 0 ? {} : optionalTraeWorkReleaseBinding(value)),
1497
+ });
1498
+ }
1499
+
1500
+ function optionalTraeWorkReleaseBinding(value) {
1501
+ const keys = [
1502
+ "profileManifestPath", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
1503
+ ];
1504
+ const count = keys.filter((key) => Object.hasOwn(value || {}, key)).length;
1505
+ if (count === 0) return Object.freeze({});
1506
+ if (count !== keys.length) throw codeError("neutral_trae_work_launch_configuration_invalid");
1507
+ return Object.freeze({
1508
+ releaseRoot: assertAbsolutePath(value.releaseRoot, "neutral_trae_work_launch_configuration_invalid"),
1509
+ releaseManifestSha256: assertSha256(value.releaseManifestSha256),
1510
+ runnerRelativePath: assertRunnerReference(value.runnerRelativePath),
1511
+ profileManifestPath: assertAbsolutePath(
1512
+ value.profileManifestPath,
1513
+ "neutral_trae_work_launch_configuration_invalid",
1514
+ ),
1515
+ });
1516
+ }
1517
+
1518
+ async function assertCurrentTraeWorkInstallationBinding(store, before, binding, capability, input) {
1519
+ const after = await resolveActiveInstallation(store, input);
1520
+ if (before.region !== after.region || before.principalId !== after.principalId
1521
+ || before.hostInstallationId !== after.hostInstallationId
1522
+ || binding.region !== after.region || binding.principalId !== after.principalId
1523
+ || binding.hostInstallationId !== after.hostInstallationId) {
1524
+ throw codeError("neutral_trae_work_capability_binding_mismatch");
1525
+ }
1526
+ const delegateBinding = await resolveTraeWorkInstallationBinding(store, binding, after);
1527
+ if (delegateBinding.profileId !== binding.profileId || delegateBinding.delegateId !== binding.delegateId
1528
+ || capability.region !== binding.region || capability.principalId !== binding.principalId
1529
+ || capability.delegateId !== binding.delegateId
1530
+ || capability.hostInstallationId !== binding.hostInstallationId
1531
+ || capability.adapterId !== "trae-work" || capability.version !== 2
1532
+ || capability.state !== "active" || !sameStrings(capability.scopes, binding.scopes)) {
1533
+ throw codeError("neutral_trae_work_capability_binding_mismatch");
1534
+ }
1535
+ }
1536
+
1537
+ async function resolveWorkBuddyInstallationBinding(store, input, installation) {
1538
+ const profileId = assertIdentifier(input?.profileId, "neutral_workbuddy_binding_invalid");
1539
+ const delegateId = input?.delegateId == null
1540
+ ? null
1541
+ : assertIdentifier(input.delegateId, "neutral_workbuddy_binding_invalid");
1542
+ let binding;
1543
+ try {
1544
+ binding = await store.resolveBinding({ profileId, delegateId });
1545
+ } catch {
1546
+ throw codeError("neutral_workbuddy_capability_binding_mismatch");
1547
+ }
1548
+ if (binding.region !== installation.region
1549
+ || binding.principalId !== installation.principalId
1550
+ || binding.hostInstallationId !== installation.hostInstallationId
1551
+ || !binding.adapters.includes("workbuddy")) {
1552
+ throw codeError("neutral_workbuddy_capability_binding_mismatch");
1553
+ }
1554
+ return binding;
1555
+ }
1556
+
1557
+ function workBuddyNeutralBindingDescriptor(input, installation, delegateBinding) {
1558
+ const scopes = canonicalScopes(input?.scopes || HELLOLYNK_WORKBUDDY_NEUTRAL_EXACT_SCOPES);
1559
+ const acceptedScopes = sameStrings(scopes, HELLOLYNK_WORKBUDDY_NEUTRAL_EXACT_SCOPES)
1560
+ || (input?.allowLegacyScopes === true
1561
+ && (sameStrings(scopes, HELLOLYNK_WORKBUDDY_NEUTRAL_PREVIOUS_SCOPES)
1562
+ || sameStrings(scopes, HELLOLYNK_WORKBUDDY_NEUTRAL_LEGACY_SCOPES)));
1563
+ if (!acceptedScopes
1564
+ || (input?.adapterId ?? "workbuddy") !== "workbuddy"
1565
+ || Number(input?.version ?? 2) !== 2) {
1566
+ throw codeError("neutral_workbuddy_capability_binding_mismatch");
1567
+ }
1568
+ const release = optionalWorkBuddyReleaseBinding(input);
1569
+ return Object.freeze({
1570
+ contract: HELLOLYNK_WORKBUDDY_NEUTRAL_BINDING_CONTRACT,
1571
+ region: installation.region,
1572
+ principalId: installation.principalId,
1573
+ delegateId: delegateBinding.delegateId,
1574
+ hostInstallationId: installation.hostInstallationId,
1575
+ profileId: delegateBinding.profileId,
1576
+ adapterId: "workbuddy",
1577
+ version: 2,
1578
+ scopes,
1579
+ runtimeRoot: assertAbsolutePath(input?.runtimeRoot, "neutral_workbuddy_binding_invalid"),
1580
+ ...release,
1581
+ });
1582
+ }
1583
+
1584
+ function publicWorkBuddyNeutralBinding(value) {
1585
+ const releaseKeys = [
1586
+ "profileManifestPath", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
1587
+ ];
1588
+ const releaseCount = releaseKeys.filter((key) => Object.hasOwn(value || {}, key)).length;
1589
+ assertExactKeysWithCode(value, [
1590
+ "adapterId", "contract", "delegateId", "hostInstallationId", "principalId",
1591
+ "profileId", "region", "runtimeRoot", "scopes", "version",
1592
+ ...(releaseCount ? releaseKeys : []),
1593
+ ], "neutral_workbuddy_binding_invalid");
1594
+ if (releaseCount !== 0 && releaseCount !== releaseKeys.length) {
1595
+ throw codeError("neutral_workbuddy_binding_invalid");
1596
+ }
1597
+ if (value.contract !== HELLOLYNK_WORKBUDDY_NEUTRAL_BINDING_CONTRACT
1598
+ || value.adapterId !== "workbuddy" || Number(value.version) !== 2) {
1599
+ throw codeError("neutral_workbuddy_binding_invalid");
1600
+ }
1601
+ const scopes = canonicalScopes(value.scopes);
1602
+ if (!sameStrings(scopes, HELLOLYNK_WORKBUDDY_NEUTRAL_EXACT_SCOPES)
1603
+ && !sameStrings(scopes, HELLOLYNK_WORKBUDDY_NEUTRAL_PREVIOUS_SCOPES)
1604
+ && !sameStrings(scopes, HELLOLYNK_WORKBUDDY_NEUTRAL_LEGACY_SCOPES)) {
1605
+ throw codeError("neutral_workbuddy_binding_invalid");
1606
+ }
1607
+ return Object.freeze({
1608
+ contract: value.contract,
1609
+ region: assertIdentifier(value.region, "neutral_workbuddy_binding_invalid"),
1610
+ principalId: assertIdentifier(value.principalId, "neutral_workbuddy_binding_invalid"),
1611
+ delegateId: assertIdentifier(value.delegateId, "neutral_workbuddy_binding_invalid"),
1612
+ hostInstallationId: assertIdentifier(value.hostInstallationId, "neutral_workbuddy_binding_invalid"),
1613
+ profileId: assertIdentifier(value.profileId, "neutral_workbuddy_binding_invalid"),
1614
+ adapterId: "workbuddy",
1615
+ version: 2,
1616
+ scopes: Object.freeze(scopes),
1617
+ runtimeRoot: assertAbsolutePath(value.runtimeRoot, "neutral_workbuddy_binding_invalid"),
1618
+ ...(releaseCount === 0 ? {} : optionalWorkBuddyReleaseBinding(value)),
1619
+ });
1620
+ }
1621
+
1622
+ function optionalWorkBuddyReleaseBinding(value) {
1623
+ const keys = [
1624
+ "profileManifestPath", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
1625
+ ];
1626
+ const count = keys.filter((key) => Object.hasOwn(value || {}, key)).length;
1627
+ if (count === 0) return Object.freeze({});
1628
+ if (count !== keys.length) throw codeError("neutral_workbuddy_launch_configuration_invalid");
1629
+ return Object.freeze({
1630
+ releaseRoot: assertAbsolutePath(value.releaseRoot, "neutral_workbuddy_launch_configuration_invalid"),
1631
+ releaseManifestSha256: assertSha256(value.releaseManifestSha256),
1632
+ runnerRelativePath: assertRunnerReference(value.runnerRelativePath),
1633
+ profileManifestPath: assertAbsolutePath(
1634
+ value.profileManifestPath,
1635
+ "neutral_workbuddy_launch_configuration_invalid",
1636
+ ),
1637
+ });
1638
+ }
1639
+
1640
+ async function assertCurrentWorkBuddyInstallationBinding(store, before, binding, capability, input) {
1641
+ const after = await resolveActiveInstallation(store, input);
1642
+ if (before.region !== after.region || before.principalId !== after.principalId
1643
+ || before.hostInstallationId !== after.hostInstallationId
1644
+ || binding.region !== after.region || binding.principalId !== after.principalId
1645
+ || binding.hostInstallationId !== after.hostInstallationId) {
1646
+ throw codeError("neutral_workbuddy_capability_binding_mismatch");
1647
+ }
1648
+ const delegateBinding = await resolveWorkBuddyInstallationBinding(store, binding, after);
1649
+ if (delegateBinding.profileId !== binding.profileId || delegateBinding.delegateId !== binding.delegateId
1650
+ || capability.region !== binding.region || capability.principalId !== binding.principalId
1651
+ || capability.delegateId !== binding.delegateId
1652
+ || capability.hostInstallationId !== binding.hostInstallationId
1653
+ || capability.adapterId !== "workbuddy" || capability.version !== 2
1654
+ || capability.state !== "active" || !sameStrings(capability.scopes, binding.scopes)) {
1655
+ throw codeError("neutral_workbuddy_capability_binding_mismatch");
1656
+ }
1657
+ }
1658
+
1659
+ async function resolveClaudeCodeInstallationBinding(store, input, installation) {
1660
+ const profileId = assertIdentifier(input?.profileId, "neutral_claude_code_binding_invalid");
1661
+ const delegateId = input?.delegateId == null
1662
+ ? null
1663
+ : assertIdentifier(input.delegateId, "neutral_claude_code_binding_invalid");
1664
+ let binding;
1665
+ try {
1666
+ binding = await store.resolveBinding({ profileId, delegateId });
1667
+ } catch {
1668
+ throw codeError("neutral_claude_code_capability_binding_mismatch");
1669
+ }
1670
+ if (binding.region !== installation.region
1671
+ || binding.principalId !== installation.principalId
1672
+ || binding.hostInstallationId !== installation.hostInstallationId
1673
+ || !binding.adapters.includes("claude-code")) {
1674
+ throw codeError("neutral_claude_code_capability_binding_mismatch");
1675
+ }
1676
+ return binding;
1677
+ }
1678
+
1679
+ function claudeCodeNeutralBindingDescriptor(input, installation, delegateBinding) {
1680
+ const scopes = canonicalScopes(input?.scopes || HELLOLYNK_CLAUDE_CODE_NEUTRAL_EXACT_SCOPES);
1681
+ const acceptedScopes = sameStrings(scopes, HELLOLYNK_CLAUDE_CODE_NEUTRAL_EXACT_SCOPES);
1682
+ if (!acceptedScopes
1683
+ || (input?.adapterId ?? "claude-code") !== "claude-code"
1684
+ || Number(input?.version ?? 2) !== 2) {
1685
+ throw codeError("neutral_claude_code_capability_binding_mismatch");
1686
+ }
1687
+ const release = optionalClaudeCodeReleaseBinding(input);
1688
+ return Object.freeze({
1689
+ contract: HELLOLYNK_CLAUDE_CODE_NEUTRAL_BINDING_CONTRACT,
1690
+ region: installation.region,
1691
+ principalId: installation.principalId,
1692
+ delegateId: delegateBinding.delegateId,
1693
+ hostInstallationId: installation.hostInstallationId,
1694
+ profileId: delegateBinding.profileId,
1695
+ adapterId: "claude-code",
1696
+ version: 2,
1697
+ scopes,
1698
+ runtimeRoot: assertAbsolutePath(input?.runtimeRoot, "neutral_claude_code_binding_invalid"),
1699
+ ...release,
1700
+ });
1701
+ }
1702
+
1703
+ function publicClaudeCodeNeutralBinding(value) {
1704
+ const releaseKeys = [
1705
+ "profileManifestPath", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
1706
+ ];
1707
+ const releaseCount = releaseKeys.filter((key) => Object.hasOwn(value || {}, key)).length;
1708
+ assertExactKeysWithCode(value, [
1709
+ "adapterId", "contract", "delegateId", "hostInstallationId", "principalId",
1710
+ "profileId", "region", "runtimeRoot", "scopes", "version",
1711
+ ...(releaseCount ? releaseKeys : []),
1712
+ ], "neutral_claude_code_binding_invalid");
1713
+ if (releaseCount !== 0 && releaseCount !== releaseKeys.length) {
1714
+ throw codeError("neutral_claude_code_binding_invalid");
1715
+ }
1716
+ if (value.contract !== HELLOLYNK_CLAUDE_CODE_NEUTRAL_BINDING_CONTRACT
1717
+ || value.adapterId !== "claude-code" || Number(value.version) !== 2) {
1718
+ throw codeError("neutral_claude_code_binding_invalid");
1719
+ }
1720
+ const scopes = canonicalScopes(value.scopes);
1721
+ if (!sameStrings(scopes, HELLOLYNK_CLAUDE_CODE_NEUTRAL_EXACT_SCOPES)) {
1722
+ throw codeError("neutral_claude_code_binding_invalid");
1723
+ }
1724
+ return Object.freeze({
1725
+ contract: value.contract,
1726
+ region: assertIdentifier(value.region, "neutral_claude_code_binding_invalid"),
1727
+ principalId: assertIdentifier(value.principalId, "neutral_claude_code_binding_invalid"),
1728
+ delegateId: assertIdentifier(value.delegateId, "neutral_claude_code_binding_invalid"),
1729
+ hostInstallationId: assertIdentifier(value.hostInstallationId, "neutral_claude_code_binding_invalid"),
1730
+ profileId: assertIdentifier(value.profileId, "neutral_claude_code_binding_invalid"),
1731
+ adapterId: "claude-code",
1732
+ version: 2,
1733
+ scopes: Object.freeze(scopes),
1734
+ runtimeRoot: assertAbsolutePath(value.runtimeRoot, "neutral_claude_code_binding_invalid"),
1735
+ ...(releaseCount === 0 ? {} : optionalClaudeCodeReleaseBinding(value)),
1736
+ });
1737
+ }
1738
+
1739
+ function optionalClaudeCodeReleaseBinding(value) {
1740
+ const keys = [
1741
+ "profileManifestPath", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
1742
+ ];
1743
+ const count = keys.filter((key) => Object.hasOwn(value || {}, key)).length;
1744
+ if (count === 0) return Object.freeze({});
1745
+ if (count !== keys.length) throw codeError("neutral_claude_code_launch_configuration_invalid");
1746
+ return Object.freeze({
1747
+ releaseRoot: assertAbsolutePath(value.releaseRoot, "neutral_claude_code_launch_configuration_invalid"),
1748
+ releaseManifestSha256: assertSha256(value.releaseManifestSha256),
1749
+ runnerRelativePath: assertRunnerReference(value.runnerRelativePath),
1750
+ profileManifestPath: assertAbsolutePath(
1751
+ value.profileManifestPath,
1752
+ "neutral_claude_code_launch_configuration_invalid",
1753
+ ),
1754
+ });
1755
+ }
1756
+
1757
+ async function assertCurrentClaudeCodeInstallationBinding(store, before, binding, capability, input) {
1758
+ const after = await resolveActiveInstallation(store, input);
1759
+ if (before.region !== after.region || before.principalId !== after.principalId
1760
+ || before.hostInstallationId !== after.hostInstallationId
1761
+ || binding.region !== after.region || binding.principalId !== after.principalId
1762
+ || binding.hostInstallationId !== after.hostInstallationId) {
1763
+ throw codeError("neutral_claude_code_capability_binding_mismatch");
1764
+ }
1765
+ const delegateBinding = await resolveClaudeCodeInstallationBinding(store, binding, after);
1766
+ if (delegateBinding.profileId !== binding.profileId || delegateBinding.delegateId !== binding.delegateId
1767
+ || capability.region !== binding.region || capability.principalId !== binding.principalId
1768
+ || capability.delegateId !== binding.delegateId
1769
+ || capability.hostInstallationId !== binding.hostInstallationId
1770
+ || capability.adapterId !== "claude-code" || capability.version !== 2
1771
+ || capability.state !== "active" || !sameStrings(capability.scopes, binding.scopes)) {
1772
+ throw codeError("neutral_claude_code_capability_binding_mismatch");
1773
+ }
1774
+ }
1775
+
1776
+ async function resolveCursorInstallationBinding(store, input, installation) {
1777
+ const profileId = assertIdentifier(input?.profileId, "neutral_cursor_binding_invalid");
1778
+ const delegateId = input?.delegateId == null
1779
+ ? null
1780
+ : assertIdentifier(input.delegateId, "neutral_cursor_binding_invalid");
1781
+ let binding;
1782
+ try {
1783
+ binding = await store.resolveBinding({ profileId, delegateId });
1784
+ } catch {
1785
+ throw codeError("neutral_cursor_capability_binding_mismatch");
1786
+ }
1787
+ if (binding.region !== installation.region
1788
+ || binding.principalId !== installation.principalId
1789
+ || binding.hostInstallationId !== installation.hostInstallationId
1790
+ || !binding.adapters.includes("cursor")) {
1791
+ throw codeError("neutral_cursor_capability_binding_mismatch");
1792
+ }
1793
+ return binding;
1794
+ }
1795
+
1796
+ function cursorNeutralBindingDescriptor(input, installation, delegateBinding) {
1797
+ const scopes = canonicalScopes(input?.scopes || HELLOLYNK_CURSOR_NEUTRAL_EXACT_SCOPES);
1798
+ if (!sameStrings(scopes, HELLOLYNK_CURSOR_NEUTRAL_EXACT_SCOPES)
1799
+ || (input?.adapterId ?? "cursor") !== "cursor"
1800
+ || Number(input?.version ?? 2) !== 2) {
1801
+ throw codeError("neutral_cursor_capability_binding_mismatch");
1802
+ }
1803
+ const release = optionalCursorReleaseBinding(input);
1804
+ return Object.freeze({
1805
+ contract: HELLOLYNK_CURSOR_NEUTRAL_BINDING_CONTRACT,
1806
+ region: installation.region,
1807
+ principalId: installation.principalId,
1808
+ delegateId: delegateBinding.delegateId,
1809
+ hostInstallationId: installation.hostInstallationId,
1810
+ profileId: delegateBinding.profileId,
1811
+ adapterId: "cursor",
1812
+ version: 2,
1813
+ scopes,
1814
+ runtimeRoot: assertAbsolutePath(input?.runtimeRoot, "neutral_cursor_binding_invalid"),
1815
+ ...release,
1816
+ });
1817
+ }
1818
+
1819
+ function publicCursorNeutralBinding(value) {
1820
+ const releaseKeys = [
1821
+ "profileManifestPath", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
1822
+ ];
1823
+ const releaseCount = releaseKeys.filter((key) => Object.hasOwn(value || {}, key)).length;
1824
+ assertExactKeysWithCode(value, [
1825
+ "adapterId", "contract", "delegateId", "hostInstallationId", "principalId",
1826
+ "profileId", "region", "runtimeRoot", "scopes", "version",
1827
+ ...(releaseCount ? releaseKeys : []),
1828
+ ], "neutral_cursor_binding_invalid");
1829
+ if (releaseCount !== 0 && releaseCount !== releaseKeys.length) {
1830
+ throw codeError("neutral_cursor_binding_invalid");
1831
+ }
1832
+ if (value.contract !== HELLOLYNK_CURSOR_NEUTRAL_BINDING_CONTRACT
1833
+ || value.adapterId !== "cursor" || Number(value.version) !== 2) {
1834
+ throw codeError("neutral_cursor_binding_invalid");
1835
+ }
1836
+ const scopes = canonicalScopes(value.scopes);
1837
+ if (!sameStrings(scopes, HELLOLYNK_CURSOR_NEUTRAL_EXACT_SCOPES)
1838
+ && !sameStrings(scopes, HELLOLYNK_CURSOR_NEUTRAL_PREVIOUS_SCOPES)) {
1839
+ throw codeError("neutral_cursor_binding_invalid");
1840
+ }
1841
+ return Object.freeze({
1842
+ contract: value.contract,
1843
+ region: assertIdentifier(value.region, "neutral_cursor_binding_invalid"),
1844
+ principalId: assertIdentifier(value.principalId, "neutral_cursor_binding_invalid"),
1845
+ delegateId: assertIdentifier(value.delegateId, "neutral_cursor_binding_invalid"),
1846
+ hostInstallationId: assertIdentifier(value.hostInstallationId, "neutral_cursor_binding_invalid"),
1847
+ profileId: assertIdentifier(value.profileId, "neutral_cursor_binding_invalid"),
1848
+ adapterId: "cursor",
1849
+ version: 2,
1850
+ scopes: Object.freeze(scopes),
1851
+ runtimeRoot: assertAbsolutePath(value.runtimeRoot, "neutral_cursor_binding_invalid"),
1852
+ ...(releaseCount === 0 ? {} : optionalCursorReleaseBinding(value)),
1853
+ });
1854
+ }
1855
+
1856
+ function optionalCursorReleaseBinding(value) {
1857
+ const keys = [
1858
+ "profileManifestPath", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
1859
+ ];
1860
+ const count = keys.filter((key) => Object.hasOwn(value || {}, key)).length;
1861
+ if (count === 0) return Object.freeze({});
1862
+ if (count !== keys.length) throw codeError("neutral_cursor_launch_configuration_invalid");
1863
+ return Object.freeze({
1864
+ releaseRoot: assertAbsolutePath(value.releaseRoot, "neutral_cursor_launch_configuration_invalid"),
1865
+ releaseManifestSha256: assertSha256(
1866
+ value.releaseManifestSha256,
1867
+ "neutral_cursor_launch_configuration_invalid",
1868
+ ),
1869
+ runnerRelativePath: assertRunnerReference(
1870
+ value.runnerRelativePath,
1871
+ "neutral_cursor_launch_configuration_invalid",
1872
+ ),
1873
+ profileManifestPath: assertAbsolutePath(
1874
+ value.profileManifestPath,
1875
+ "neutral_cursor_launch_configuration_invalid",
1876
+ ),
1877
+ });
1878
+ }
1879
+
1880
+ async function assertCurrentCursorInstallationBinding(store, before, binding, capability, input) {
1881
+ const after = await resolveActiveInstallation(store, input);
1882
+ if (before.region !== after.region || before.principalId !== after.principalId
1883
+ || before.hostInstallationId !== after.hostInstallationId
1884
+ || binding.region !== after.region || binding.principalId !== after.principalId
1885
+ || binding.hostInstallationId !== after.hostInstallationId) {
1886
+ throw codeError("neutral_cursor_capability_binding_mismatch");
1887
+ }
1888
+ const delegateBinding = await resolveCursorInstallationBinding(store, binding, after);
1889
+ if (delegateBinding.profileId !== binding.profileId || delegateBinding.delegateId !== binding.delegateId
1890
+ || capability.region !== binding.region || capability.principalId !== binding.principalId
1891
+ || capability.delegateId !== binding.delegateId
1892
+ || capability.hostInstallationId !== binding.hostInstallationId
1893
+ || capability.adapterId !== "cursor" || capability.version !== 2
1894
+ || capability.state !== "active" || !sameStrings(capability.scopes, binding.scopes)) {
1895
+ throw codeError("neutral_cursor_capability_binding_mismatch");
1896
+ }
1897
+ }
1898
+
1899
+ async function resolveDshInstallationBinding(store, input, installation) {
1900
+ const profileId = assertIdentifier(input?.profileId, "neutral_dsh_binding_invalid");
1901
+ const delegateId = input?.delegateId == null ? null : assertIdentifier(input.delegateId, "neutral_dsh_binding_invalid");
1902
+ let binding;
1903
+ try { binding = await store.resolveBinding({ profileId, delegateId }); } catch {
1904
+ throw codeError("neutral_dsh_capability_binding_mismatch");
1905
+ }
1906
+ if (binding.region !== installation.region || binding.principalId !== installation.principalId
1907
+ || binding.hostInstallationId !== installation.hostInstallationId || !binding.adapters.includes("dsh")) {
1908
+ throw codeError("neutral_dsh_capability_binding_mismatch");
1909
+ }
1910
+ return binding;
1911
+ }
1912
+
1913
+ function dshNeutralBindingDescriptor(input, installation, delegateBinding, allowPrevious = false) {
1914
+ const scopes = canonicalScopes(input?.scopes || HELLOLYNK_DSH_NEUTRAL_EXACT_SCOPES);
1915
+ if (!(sameStrings(scopes, HELLOLYNK_DSH_NEUTRAL_EXACT_SCOPES)
1916
+ || (allowPrevious && sameStrings(scopes, HELLOLYNK_DSH_NEUTRAL_PREVIOUS_SCOPES)))
1917
+ || (input?.adapterId ?? "dsh") !== "dsh" || Number(input?.version ?? 2) !== 2) {
1918
+ throw codeError("neutral_dsh_capability_binding_mismatch");
1919
+ }
1920
+ return Object.freeze({
1921
+ contract: HELLOLYNK_DSH_NEUTRAL_BINDING_CONTRACT, region: installation.region,
1922
+ principalId: installation.principalId, delegateId: delegateBinding.delegateId,
1923
+ hostInstallationId: installation.hostInstallationId, profileId: delegateBinding.profileId,
1924
+ adapterId: "dsh", version: 2, scopes,
1925
+ runtimeRoot: assertAbsolutePath(input?.runtimeRoot, "neutral_dsh_binding_invalid"),
1926
+ ...optionalDshReleaseBinding(input),
1927
+ });
1928
+ }
1929
+
1930
+ function publicDshNeutralBinding(value) {
1931
+ assertExactKeysWithCode(value, [
1932
+ "adapterId", "contract", "delegateId", "hostInstallationId", "principalId",
1933
+ "profileId", "profileManifestPath", "region", "releaseManifestSha256", "releaseRoot",
1934
+ "runnerRelativePath", "runtimeRoot", "scopes", "version",
1935
+ ], "neutral_dsh_binding_invalid");
1936
+ if (value.contract !== HELLOLYNK_DSH_NEUTRAL_BINDING_CONTRACT || value.adapterId !== "dsh"
1937
+ || Number(value.version) !== 2) throw codeError("neutral_dsh_binding_invalid");
1938
+ const scopes = canonicalScopes(value.scopes);
1939
+ if (!sameStrings(scopes, HELLOLYNK_DSH_NEUTRAL_EXACT_SCOPES)
1940
+ && !sameStrings(scopes, HELLOLYNK_DSH_NEUTRAL_PREVIOUS_SCOPES)) throw codeError("neutral_dsh_binding_invalid");
1941
+ return Object.freeze({
1942
+ contract: value.contract, region: assertIdentifier(value.region, "neutral_dsh_binding_invalid"),
1943
+ principalId: assertIdentifier(value.principalId, "neutral_dsh_binding_invalid"),
1944
+ delegateId: assertIdentifier(value.delegateId, "neutral_dsh_binding_invalid"),
1945
+ hostInstallationId: assertIdentifier(value.hostInstallationId, "neutral_dsh_binding_invalid"),
1946
+ profileId: assertIdentifier(value.profileId, "neutral_dsh_binding_invalid"), adapterId: "dsh",
1947
+ version: 2, scopes: Object.freeze(scopes),
1948
+ runtimeRoot: assertAbsolutePath(value.runtimeRoot, "neutral_dsh_binding_invalid"),
1949
+ ...optionalDshReleaseBinding(value),
1950
+ });
1951
+ }
1952
+
1953
+ function optionalDshReleaseBinding(value) {
1954
+ return Object.freeze({
1955
+ releaseRoot: assertAbsolutePath(value?.releaseRoot, "neutral_dsh_launch_configuration_invalid"),
1956
+ releaseManifestSha256: assertSha256(value?.releaseManifestSha256, "neutral_dsh_launch_configuration_invalid"),
1957
+ runnerRelativePath: assertRunnerReference(value?.runnerRelativePath, "neutral_dsh_launch_configuration_invalid"),
1958
+ profileManifestPath: assertAbsolutePath(value?.profileManifestPath, "neutral_dsh_launch_configuration_invalid"),
1959
+ });
1960
+ }
1961
+
1962
+ async function assertCurrentDshInstallationBinding(store, before, binding, capability, input) {
1963
+ const after = await resolveActiveInstallation(store, input);
1964
+ if (before.region !== after.region || before.principalId !== after.principalId
1965
+ || before.hostInstallationId !== after.hostInstallationId || binding.region !== after.region
1966
+ || binding.principalId !== after.principalId || binding.hostInstallationId !== after.hostInstallationId) {
1967
+ throw codeError("neutral_dsh_capability_binding_mismatch");
1968
+ }
1969
+ const delegateBinding = await resolveDshInstallationBinding(store, binding, after);
1970
+ if (delegateBinding.profileId !== binding.profileId || delegateBinding.delegateId !== binding.delegateId
1971
+ || capability.region !== binding.region || capability.principalId !== binding.principalId
1972
+ || capability.delegateId !== binding.delegateId || capability.hostInstallationId !== binding.hostInstallationId
1973
+ || capability.adapterId !== "dsh" || capability.version !== 2 || capability.state !== "active"
1974
+ || !sameStrings(capability.scopes, binding.scopes)) throw codeError("neutral_dsh_capability_binding_mismatch");
1975
+ }
1976
+
1977
+ function openClawNeutralBindingDescriptor(input, installation, delegateBinding) {
1978
+ const scopes = canonicalScopes(input?.scopes || HELLOLYNK_OPENCLAW_NEUTRAL_EXACT_SCOPES);
1979
+ if (!(sameStrings(scopes, HELLOLYNK_OPENCLAW_NEUTRAL_EXACT_SCOPES)
1980
+ || (input?.allowLegacyScopes === true
1981
+ && sameStrings(scopes, HELLOLYNK_OPENCLAW_NEUTRAL_PREVIOUS_SCOPES)))
1982
+ || (input?.adapterId ?? "openclaw") !== "openclaw"
1983
+ || Number(input?.version ?? 2) !== 2) {
1984
+ throw codeError("neutral_openclaw_capability_binding_mismatch");
1985
+ }
1986
+ return Object.freeze({
1987
+ contract: HELLOLYNK_OPENCLAW_NEUTRAL_BINDING_CONTRACT,
1988
+ region: installation.region,
1989
+ principalId: installation.principalId,
1990
+ delegateId: delegateBinding.delegateId,
1991
+ hostInstallationId: installation.hostInstallationId,
1992
+ profileId: delegateBinding.profileId,
1993
+ adapterId: "openclaw",
1994
+ version: 2,
1995
+ scopes,
1996
+ runtimeRoot: assertAbsolutePath(input?.runtimeRoot, "neutral_openclaw_binding_invalid"),
1997
+ releaseRoot: assertAbsolutePath(input?.releaseRoot, "neutral_openclaw_binding_invalid"),
1998
+ releaseManifestSha256: assertSha256(input?.releaseManifestSha256),
1999
+ runnerRelativePath: assertRunnerReference(input?.runnerRelativePath),
2000
+ profileManifestPath: assertAbsolutePath(
2001
+ input?.profileManifestPath,
2002
+ "neutral_openclaw_binding_invalid",
2003
+ ),
2004
+ });
2005
+ }
2006
+
2007
+ function publicOpenClawNeutralBinding(value) {
2008
+ const legacyKeys = [
2009
+ "adapterId", "contract", "delegateId", "hostInstallationId", "principalId",
2010
+ "profileId", "region", "runtimeRoot", "scopes", "version",
2011
+ ];
2012
+ const currentKeys = [
2013
+ ...legacyKeys,
2014
+ "profileManifestPath", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
2015
+ ];
2016
+ const actualKeys = Object.keys(value || {}).sort();
2017
+ const isLegacy = sameStrings(actualKeys, [...legacyKeys].sort());
2018
+ if (!isLegacy && !sameStrings(actualKeys, [...currentKeys].sort())) {
2019
+ throw codeError("neutral_openclaw_binding_invalid");
2020
+ }
2021
+ if (value.contract !== HELLOLYNK_OPENCLAW_NEUTRAL_BINDING_CONTRACT
2022
+ || value.adapterId !== "openclaw" || Number(value.version) !== 2) {
2023
+ throw codeError("neutral_openclaw_binding_invalid");
2024
+ }
2025
+ const scopes = canonicalScopes(value.scopes);
2026
+ if (!sameStrings(scopes, HELLOLYNK_OPENCLAW_NEUTRAL_EXACT_SCOPES)
2027
+ && !sameStrings(scopes, HELLOLYNK_OPENCLAW_NEUTRAL_PREVIOUS_SCOPES)) {
2028
+ throw codeError("neutral_openclaw_binding_invalid");
2029
+ }
2030
+ return Object.freeze({
2031
+ contract: value.contract,
2032
+ region: assertIdentifier(value.region, "neutral_openclaw_binding_invalid"),
2033
+ principalId: assertIdentifier(value.principalId, "neutral_openclaw_binding_invalid"),
2034
+ delegateId: assertIdentifier(value.delegateId, "neutral_openclaw_binding_invalid"),
2035
+ hostInstallationId: assertIdentifier(value.hostInstallationId, "neutral_openclaw_binding_invalid"),
2036
+ profileId: assertIdentifier(value.profileId, "neutral_openclaw_binding_invalid"),
2037
+ adapterId: "openclaw",
2038
+ version: 2,
2039
+ scopes: Object.freeze(scopes),
2040
+ runtimeRoot: assertAbsolutePath(value.runtimeRoot, "neutral_openclaw_binding_invalid"),
2041
+ ...(!isLegacy ? {
2042
+ releaseRoot: assertAbsolutePath(value.releaseRoot, "neutral_openclaw_binding_invalid"),
2043
+ releaseManifestSha256: assertSha256(value.releaseManifestSha256),
2044
+ runnerRelativePath: assertRunnerReference(value.runnerRelativePath),
2045
+ profileManifestPath: assertAbsolutePath(value.profileManifestPath, "neutral_openclaw_binding_invalid"),
2046
+ } : {}),
2047
+ });
2048
+ }
2049
+
2050
+ async function assertCurrentOpenClawInstallationBinding(store, before, binding, capability, input) {
2051
+ const after = await resolveActiveInstallation(store, input);
2052
+ if (before.region !== after.region || before.principalId !== after.principalId
2053
+ || before.hostInstallationId !== after.hostInstallationId
2054
+ || binding.region !== after.region || binding.principalId !== after.principalId
2055
+ || binding.hostInstallationId !== after.hostInstallationId) {
2056
+ throw codeError("neutral_openclaw_capability_binding_mismatch");
2057
+ }
2058
+ const delegateBinding = await resolveOpenClawInstallationBinding(store, binding, after);
2059
+ if (delegateBinding.profileId !== binding.profileId || delegateBinding.delegateId !== binding.delegateId
2060
+ || capability.region !== binding.region || capability.principalId !== binding.principalId
2061
+ || capability.delegateId !== binding.delegateId
2062
+ || capability.hostInstallationId !== binding.hostInstallationId
2063
+ || capability.adapterId !== "openclaw" || capability.version !== 2
2064
+ || capability.state !== "active" || !sameStrings(capability.scopes, binding.scopes)) {
2065
+ throw codeError("neutral_openclaw_capability_binding_mismatch");
2066
+ }
2067
+ }
2068
+
2069
+ async function resolveCodexInstallationBinding(store, input, installation) {
2070
+ const profileId = assertIdentifier(input?.profileId, "neutral_codex_binding_invalid");
2071
+ const delegateId = input?.delegateId == null
2072
+ ? null
2073
+ : assertIdentifier(input.delegateId, "neutral_codex_binding_invalid");
2074
+ let binding;
2075
+ try {
2076
+ binding = await store.resolveBinding({ profileId, delegateId });
2077
+ } catch {
2078
+ throw codeError("neutral_codex_capability_binding_mismatch");
2079
+ }
2080
+ if (binding.region !== installation.region
2081
+ || binding.principalId !== installation.principalId
2082
+ || binding.hostInstallationId !== installation.hostInstallationId
2083
+ || !binding.adapters.includes("codex")) {
2084
+ throw codeError("neutral_codex_capability_binding_mismatch");
2085
+ }
2086
+ return binding;
2087
+ }
2088
+
2089
+ function codexNeutralBindingDescriptor(input, installation, delegateBinding) {
2090
+ const scopes = canonicalScopes(input?.scopes || HELLOLYNK_CODEX_NEUTRAL_EXACT_SCOPES);
2091
+ if (!(sameStrings(scopes, HELLOLYNK_CODEX_NEUTRAL_EXACT_SCOPES)
2092
+ || (input?.allowLegacyScopes === true
2093
+ && sameStrings(scopes, HELLOLYNK_CODEX_NEUTRAL_PREVIOUS_SCOPES)))) {
2094
+ throw codeError("neutral_codex_capability_binding_mismatch");
2095
+ }
2096
+ if ((input?.adapterId ?? "codex") !== "codex" || Number(input?.version ?? 2) !== 2) {
2097
+ throw codeError("neutral_codex_capability_binding_mismatch");
2098
+ }
2099
+ return Object.freeze({
2100
+ contract: HELLOLYNK_CODEX_NEUTRAL_BINDING_CONTRACT,
2101
+ region: installation.region,
2102
+ principalId: installation.principalId,
2103
+ delegateId: delegateBinding.delegateId,
2104
+ hostInstallationId: installation.hostInstallationId,
2105
+ profileId: delegateBinding.profileId,
2106
+ adapterId: "codex",
2107
+ version: 2,
2108
+ scopes,
2109
+ runtimeRoot: assertAbsolutePath(input?.runtimeRoot, "neutral_codex_binding_invalid"),
2110
+ releaseRoot: assertAbsolutePath(input?.releaseRoot, "neutral_codex_binding_invalid"),
2111
+ releaseManifestSha256: assertSha256(input?.releaseManifestSha256),
2112
+ runnerRelativePath: assertRunnerReference(input?.runnerRelativePath),
2113
+ profileManifestPath: assertAbsolutePath(
2114
+ input?.profileManifestPath,
2115
+ "neutral_codex_launch_configuration_required",
2116
+ ),
2117
+ });
2118
+ }
2119
+
2120
+ function publicCodexNeutralBinding(value) {
2121
+ assertExactKeysWithCode(value, [
2122
+ "adapterId", "contract", "delegateId", "hostInstallationId", "principalId",
2123
+ "profileId", "profileManifestPath", "region", "releaseManifestSha256", "releaseRoot", "runnerRelativePath",
2124
+ "runtimeRoot", "scopes", "version",
2125
+ ], "neutral_codex_binding_invalid");
2126
+ if (value.contract !== HELLOLYNK_CODEX_NEUTRAL_BINDING_CONTRACT
2127
+ || value.adapterId !== "codex" || Number(value.version) !== 2) {
2128
+ throw codeError("neutral_codex_binding_invalid");
2129
+ }
2130
+ const scopes = canonicalScopes(value.scopes);
2131
+ if (!sameStrings(scopes, HELLOLYNK_CODEX_NEUTRAL_EXACT_SCOPES)
2132
+ && !sameStrings(scopes, HELLOLYNK_CODEX_NEUTRAL_PREVIOUS_SCOPES)) {
2133
+ throw codeError("neutral_codex_binding_invalid");
2134
+ }
2135
+ return Object.freeze({
2136
+ contract: value.contract,
2137
+ region: assertIdentifier(value.region, "neutral_codex_binding_invalid"),
2138
+ principalId: assertIdentifier(value.principalId, "neutral_codex_binding_invalid"),
2139
+ delegateId: assertIdentifier(value.delegateId, "neutral_codex_binding_invalid"),
2140
+ hostInstallationId: assertIdentifier(value.hostInstallationId, "neutral_codex_binding_invalid"),
2141
+ profileId: assertIdentifier(value.profileId, "neutral_codex_binding_invalid"),
2142
+ adapterId: "codex",
2143
+ version: 2,
2144
+ scopes: Object.freeze(scopes),
2145
+ runtimeRoot: assertAbsolutePath(value.runtimeRoot, "neutral_codex_binding_invalid"),
2146
+ releaseRoot: assertAbsolutePath(value.releaseRoot, "neutral_codex_binding_invalid"),
2147
+ releaseManifestSha256: assertSha256(value.releaseManifestSha256),
2148
+ runnerRelativePath: assertRunnerReference(value.runnerRelativePath),
2149
+ profileManifestPath: assertAbsolutePath(
2150
+ value.profileManifestPath,
2151
+ "neutral_codex_binding_invalid",
2152
+ ),
2153
+ });
2154
+ }
2155
+
2156
+ async function assertCurrentCodexInstallationBinding(store, before, binding, capability) {
2157
+ const after = await resolveActiveInstallation(store, {});
2158
+ if (before.region !== after.region || before.principalId !== after.principalId
2159
+ || before.hostInstallationId !== after.hostInstallationId
2160
+ || binding.region !== after.region || binding.principalId !== after.principalId
2161
+ || binding.hostInstallationId !== after.hostInstallationId) {
2162
+ throw codeError("neutral_codex_capability_binding_mismatch");
2163
+ }
2164
+ // Use the delegate in the complete post-DPAPI registry snapshot. Reading
2165
+ // the registry a third time repeats path checks and can mix two
2166
+ // different snapshots; the post-read still rejects removed/rebound delegates.
2167
+ const matches = after.delegateBindings.filter((item) => item.profileId === binding.profileId
2168
+ && item.delegateId === binding.delegateId && item.adapters.includes("codex"));
2169
+ if (matches.length !== 1) throw codeError("neutral_codex_capability_binding_mismatch");
2170
+ const delegateBinding = matches[0];
2171
+ if (delegateBinding.profileId !== binding.profileId || delegateBinding.delegateId !== binding.delegateId
2172
+ || capability.region !== binding.region || capability.principalId !== binding.principalId
2173
+ || capability.delegateId !== binding.delegateId
2174
+ || capability.hostInstallationId !== binding.hostInstallationId
2175
+ || capability.adapterId !== "codex" || capability.version !== 2
2176
+ || capability.state !== "active" || !sameStrings(capability.scopes, binding.scopes)) {
2177
+ throw codeError("neutral_codex_capability_binding_mismatch");
2178
+ }
2179
+ }
2180
+
2181
+ function assertAbsolutePath(value, code) {
2182
+ const text = String(value || "");
2183
+ if (!text || !isAbsolute(text) || resolve(text) !== text) throw codeError(code);
2184
+ return text;
2185
+ }
2186
+
2187
+ function assertPersistentNeutralBinding(binding, storeRoot) {
2188
+ const pathFields = ["runtimeRoot", "releaseRoot", "profileManifestPath"];
2189
+ for (const field of pathFields) {
2190
+ if (!Object.hasOwn(binding, field)) continue;
2191
+ const normalized = resolve(String(binding[field] || "")).replaceAll("\\", "/").toLowerCase();
2192
+ if (normalized.includes("/.codex/worktrees/")
2193
+ || normalized.endsWith("/.codex/worktrees")
2194
+ || /(?:^|\/)[^/]*hellolynk[^/]*stage[^/]*(?:\/|$)/u.test(normalized)) {
2195
+ throw codeError("neutral_host_persistent_binding_path_invalid");
2196
+ }
2197
+ }
2198
+ if (basename(storeRoot).toLowerCase() !== "host") return binding;
2199
+ if (pathFields.some((field) => !Object.hasOwn(binding, field))) {
2200
+ throw codeError("neutral_host_persistent_binding_path_invalid");
2201
+ }
2202
+ const finalRoot = dirname(clientAccountBaseRoot(storeRoot));
2203
+ const releasesRoot = join(finalRoot, "releases");
2204
+ const releaseRelative = relative(releasesRoot, resolve(binding.releaseRoot));
2205
+ if (resolve(binding.runtimeRoot) !== join(storeRoot, "runtime")
2206
+ || resolve(binding.profileManifestPath) !== join(storeRoot, "profile-manifest-v1.json")
2207
+ || releaseRelative === "" || releaseRelative.startsWith("..") || isAbsolute(releaseRelative)) {
2208
+ throw codeError("neutral_host_persistent_binding_path_invalid");
2209
+ }
2210
+ return binding;
2211
+ }
2212
+
2213
+ function assertRunnerReference(value, code = "neutral_codex_runner_integrity_invalid") {
2214
+ const text = String(value || "");
2215
+ if (!/^(?:[A-Za-z0-9][A-Za-z0-9._-]*\/)*[A-Za-z0-9][A-Za-z0-9._-]*\.mjs$/.test(
2216
+ text.replaceAll("\\", "/"),
2217
+ )) throw codeError(code);
2218
+ if (text.includes("\\") || text.split("/").includes("..")) {
2219
+ throw codeError(code);
2220
+ }
2221
+ return text;
2222
+ }
2223
+
2224
+ function assertSha256(value, code = "neutral_codex_runner_integrity_invalid") {
2225
+ const text = String(value || "");
2226
+ if (!/^[a-f0-9]{64}$/.test(text)) throw codeError(code);
2227
+ return text;
2228
+ }
2229
+
2230
+ function sameStrings(left, right) {
2231
+ return left.length === right.length && left.every((value, index) => value === right[index]);
2232
+ }
2233
+
2234
+ function createRecord(input, dependencies) {
2235
+ const scopes = canonicalScopes(input.scopes);
2236
+ const secretBuffer = Buffer.from(dependencies.random(32));
2237
+ if (secretBuffer.length !== 32) throw codeError("host_capability_random_invalid");
2238
+ const capabilityId = createCapabilityId(dependencies.random);
2239
+ return assertCapabilityRecord({
2240
+ contract: HELLOLYNK_HOST_CAPABILITY_CONTRACT,
2241
+ capabilityId,
2242
+ region: assertIdentifier(input.region, "host_capability_region_invalid"),
2243
+ principalId: assertIdentifier(input.principalId, "host_principal_id_invalid"),
2244
+ delegateId: assertIdentifier(input.delegateId, "host_delegate_id_invalid"),
2245
+ hostInstallationId: assertIdentifier(input.hostInstallationId, "host_installation_id_invalid"),
2246
+ adapterId: assertIdentifier(input.adapterId, "host_adapter_id_invalid"),
2247
+ version: assertPositiveInteger(input.version ?? 1, "host_capability_version_invalid"),
2248
+ state: "active",
2249
+ scopes,
2250
+ issuedAt: assertTimestamp(dependencies.clock()),
2251
+ revokedAt: null,
2252
+ secretBuffer,
2253
+ });
2254
+ }
2255
+
2256
+ function createCapabilityId(random) {
2257
+ for (let attempt = 0; attempt < 64; attempt += 1) {
2258
+ const bytes = Buffer.from(random(24));
2259
+ if (bytes.length !== 24) throw codeError("host_capability_random_invalid");
2260
+ const candidate = `cap_${bytes.toString("base64url")}`;
2261
+ if (CAPABILITY_ID_PATTERN.test(candidate) && /[A-Za-z0-9]$/.test(candidate)) return candidate;
2262
+ }
2263
+ throw codeError("host_capability_random_invalid");
2264
+ }
2265
+
2266
+ function serializeRecord(record) {
2267
+ return JSON.stringify({
2268
+ contract: record.contract,
2269
+ capabilityId: record.capabilityId,
2270
+ region: record.region,
2271
+ principalId: record.principalId,
2272
+ delegateId: record.delegateId,
2273
+ hostInstallationId: record.hostInstallationId,
2274
+ adapterId: record.adapterId,
2275
+ version: record.version,
2276
+ state: record.state,
2277
+ scopes: [...record.scopes],
2278
+ issuedAt: record.issuedAt,
2279
+ revokedAt: null,
2280
+ secret: record.secretBuffer.toString("base64url"),
2281
+ });
2282
+ }
2283
+
2284
+ function deserializeRecord(value) {
2285
+ assertExactKeys(value, [
2286
+ "adapterId", "capabilityId", "contract", "delegateId", "hostInstallationId",
2287
+ "issuedAt", "principalId", "region", "revokedAt", "scopes", "secret", "state", "version",
2288
+ ]);
2289
+ if (typeof value.secret !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(value.secret)) {
2290
+ throw codeError("host_capability_secret_invalid");
2291
+ }
2292
+ const { secret, ...publicFields } = value;
2293
+ return assertCapabilityRecord({
2294
+ ...publicFields,
2295
+ secretBuffer: Buffer.from(value.secret, "base64url"),
2296
+ });
2297
+ }
2298
+
2299
+ function publicRecord(value) {
2300
+ assertExactKeys(value, [
2301
+ "adapterId", "capabilityId", "contract", "delegateId", "hostInstallationId",
2302
+ "issuedAt", "principalId", "region", "revokedAt", "scopes", "state", "version",
2303
+ ]);
2304
+ if (value.contract !== HELLOLYNK_HOST_CAPABILITY_CONTRACT) throw codeError("host_capability_contract_invalid");
2305
+ const state = value.state;
2306
+ if (!['active', 'revoked'].includes(state)) throw codeError("host_capability_state_invalid");
2307
+ const issuedAt = assertTimestamp(value.issuedAt);
2308
+ const revokedAt = state === "active" && value.revokedAt === null
2309
+ ? null : assertTimestamp(value.revokedAt);
2310
+ if (state === "revoked" && revokedAt < issuedAt) throw codeError("host_capability_state_invalid");
2311
+ return Object.freeze({
2312
+ contract: value.contract,
2313
+ capabilityId: assertCapabilityId(value.capabilityId),
2314
+ region: assertIdentifier(value.region, "host_capability_region_invalid"),
2315
+ principalId: assertIdentifier(value.principalId, "host_principal_id_invalid"),
2316
+ delegateId: assertIdentifier(value.delegateId, "host_delegate_id_invalid"),
2317
+ hostInstallationId: assertIdentifier(value.hostInstallationId, "host_installation_id_invalid"),
2318
+ adapterId: assertIdentifier(value.adapterId, "host_adapter_id_invalid"),
2319
+ version: assertPositiveInteger(value.version, "host_capability_version_invalid"),
2320
+ state,
2321
+ scopes: Object.freeze(canonicalScopes(value.scopes)),
2322
+ issuedAt,
2323
+ revokedAt,
2324
+ });
2325
+ }
2326
+
2327
+ async function resolveActiveInstallation(store, input) {
2328
+ const status = await store.status();
2329
+ const active = status.installations.filter((item) => item.state === "active");
2330
+ if (active.length !== 1) {
2331
+ throw codeError(active.length ? "host_installation_ambiguous" : "host_installation_missing");
2332
+ }
2333
+ const installation = active[0];
2334
+ const requestedPrincipalId = input?.principalId == null
2335
+ ? installation.principalId
2336
+ : assertIdentifier(input.principalId, "host_principal_id_invalid");
2337
+ const requestedHostId = input?.hostInstallationId ?? input?.hostId ?? installation.hostInstallationId;
2338
+ if (requestedPrincipalId !== installation.principalId
2339
+ || assertIdentifier(requestedHostId, "host_installation_id_invalid") !== installation.hostInstallationId) {
2340
+ throw codeError("host_transport_installation_owner_mismatch");
2341
+ }
2342
+ return installation;
2343
+ }
2344
+
2345
+ function transportCredentialRecord(input, installation) {
2346
+ const credentialId = assertIdentifier(input?.credentialId, "host_transport_credential_id_invalid");
2347
+ const secret = String(input?.secret ?? input?.credential ?? "");
2348
+ const match = TRANSPORT_CREDENTIAL_PATTERN.exec(secret);
2349
+ if (!match || match[1] !== credentialId) throw codeError("host_transport_credential_secret_invalid");
2350
+ const principalId = assertIdentifier(input?.principalId, "host_principal_id_invalid");
2351
+ const hostInstallationId = assertIdentifier(
2352
+ input?.hostInstallationId ?? input?.hostId,
2353
+ "host_installation_id_invalid",
2354
+ );
2355
+ if (principalId !== installation.principalId
2356
+ || hostInstallationId !== installation.hostInstallationId) {
2357
+ throw codeError("host_transport_credential_owner_mismatch");
2358
+ }
2359
+ const issuedAt = normalizeTransportTimestamp(input?.issuedAt ?? input?.createdAt);
2360
+ const expiresAt = normalizeTransportTimestamp(input?.expiresAt);
2361
+ if (expiresAt <= issuedAt) throw codeError("host_transport_credential_expiry_invalid");
2362
+ if ((input?.state ?? "active") !== "active" || (input?.revokedAt ?? null) !== null) {
2363
+ throw codeError("host_transport_credential_state_invalid");
2364
+ }
2365
+ return Object.freeze({
2366
+ contract: HELLOLYNK_HOST_TRANSPORT_CREDENTIAL_CONTRACT,
2367
+ credentialId,
2368
+ principalId,
2369
+ hostInstallationId,
2370
+ state: "active",
2371
+ secret,
2372
+ issuedAt,
2373
+ expiresAt,
2374
+ revokedAt: null,
2375
+ });
2376
+ }
2377
+
2378
+ function deserializeTransportCredential(value, installation, now, options = {}) {
2379
+ assertExactKeys(value, [
2380
+ "contract", "credentialId", "expiresAt", "hostInstallationId", "issuedAt",
2381
+ "principalId", "revokedAt", "secret", "state",
2382
+ ]);
2383
+ if (value.contract !== HELLOLYNK_HOST_TRANSPORT_CREDENTIAL_CONTRACT) {
2384
+ throw codeError("host_transport_credential_contract_invalid");
2385
+ }
2386
+ const credentialId = assertIdentifier(value.credentialId, "host_transport_credential_id_invalid");
2387
+ const secret = String(value.secret || "");
2388
+ const match = TRANSPORT_CREDENTIAL_PATTERN.exec(secret);
2389
+ if (!match || match[1] !== credentialId) throw codeError("host_transport_credential_secret_invalid");
2390
+ const principalId = assertIdentifier(value.principalId, "host_principal_id_invalid");
2391
+ const hostInstallationId = assertIdentifier(value.hostInstallationId, "host_installation_id_invalid");
2392
+ if (principalId !== installation.principalId
2393
+ || hostInstallationId !== installation.hostInstallationId) {
2394
+ throw codeError("host_transport_credential_owner_mismatch");
2395
+ }
2396
+ const issuedAt = assertTimestamp(value.issuedAt);
2397
+ const expiresAt = assertTimestamp(value.expiresAt);
2398
+ if (expiresAt <= issuedAt) throw codeError("host_transport_credential_expiry_invalid");
2399
+ const state = value.state;
2400
+ const revokedAt = value.revokedAt === null ? null : assertTimestamp(value.revokedAt);
2401
+ if (!['active', 'revoked'].includes(state)
2402
+ || (state === "active" && revokedAt !== null)
2403
+ || (state === "revoked" && (revokedAt === null || revokedAt < issuedAt))) {
2404
+ throw codeError("host_transport_credential_state_invalid");
2405
+ }
2406
+ if (state !== "active") throw codeError("host_transport_credential_inactive");
2407
+ if (!options.allowExpired && expiresAt <= assertTimestamp(now)) {
2408
+ throw codeError("host_transport_credential_expired");
2409
+ }
2410
+ return Object.freeze({
2411
+ contract: value.contract,
2412
+ credentialId,
2413
+ principalId,
2414
+ hostInstallationId,
2415
+ state,
2416
+ secret,
2417
+ issuedAt,
2418
+ expiresAt,
2419
+ revokedAt,
2420
+ });
2421
+ }
2422
+
2423
+ function transportSessionRecord(input, installation) {
2424
+ const sessionId = assertTransportSessionId(input?.sessionId);
2425
+ const sessionSecret = String(input?.sessionSecret || "");
2426
+ if (!TRANSPORT_SESSION_SECRET_PATTERN.test(sessionSecret)) {
2427
+ throw codeError("host_transport_session_secret_invalid");
2428
+ }
2429
+ const credentialId = assertIdentifier(input?.credentialId, "host_transport_credential_id_invalid");
2430
+ const principalId = assertIdentifier(input?.principalId, "host_principal_id_invalid");
2431
+ const hostInstallationId = assertIdentifier(
2432
+ input?.hostInstallationId ?? input?.hostId,
2433
+ "host_installation_id_invalid",
2434
+ );
2435
+ if (principalId !== installation.principalId
2436
+ || hostInstallationId !== installation.hostInstallationId) {
2437
+ throw codeError("host_transport_session_owner_mismatch");
2438
+ }
2439
+ const issuedAt = normalizeTransportTimestamp(input?.issuedAt);
2440
+ const expiresAt = normalizeTransportTimestamp(input?.expiresAt);
2441
+ if (expiresAt <= issuedAt) throw codeError("host_transport_session_expiry_invalid");
2442
+ return Object.freeze({
2443
+ contract: HELLOLYNK_HOST_TRANSPORT_SESSION_CONTRACT,
2444
+ sessionId,
2445
+ sessionSecret,
2446
+ credentialId,
2447
+ principalId,
2448
+ hostInstallationId,
2449
+ issuedAt,
2450
+ expiresAt,
2451
+ });
2452
+ }
2453
+
2454
+ function deserializeTransportSession(value, installation, credentialId, now) {
2455
+ assertExactKeys(value, [
2456
+ "contract", "credentialId", "expiresAt", "hostInstallationId", "issuedAt",
2457
+ "principalId", "sessionId", "sessionSecret",
2458
+ ]);
2459
+ if (value.contract !== HELLOLYNK_HOST_TRANSPORT_SESSION_CONTRACT) {
2460
+ throw codeError("host_transport_session_contract_invalid");
2461
+ }
2462
+ const record = transportSessionRecord(value, installation);
2463
+ if (credentialId && record.credentialId !== credentialId) {
2464
+ throw codeError("host_transport_session_credential_mismatch");
2465
+ }
2466
+ if (record.expiresAt <= assertTimestamp(now)) throw codeError("host_transport_session_expired");
2467
+ return record;
2468
+ }
2469
+
2470
+ function clientTransportSession(value) {
2471
+ return Object.freeze({
2472
+ sessionId: value.sessionId,
2473
+ sessionSecret: value.sessionSecret,
2474
+ principalId: value.principalId,
2475
+ hostId: value.hostInstallationId,
2476
+ expiresAt: new Date(value.expiresAt).toISOString(),
2477
+ });
2478
+ }
2479
+
2480
+ function normalizeTransportTimestamp(value) {
2481
+ if (typeof value === "string") {
2482
+ const parsed = Date.parse(value);
2483
+ if (!Number.isFinite(parsed)) throw codeError("host_timestamp_invalid");
2484
+ return assertTimestamp(parsed);
2485
+ }
2486
+ return assertTimestamp(value);
2487
+ }
2488
+
2489
+ function assertTransportSessionId(value) {
2490
+ const sessionId = String(value || "");
2491
+ if (!TRANSPORT_SESSION_ID_PATTERN.test(sessionId)) {
2492
+ throw codeError("host_transport_session_id_invalid");
2493
+ }
2494
+ return sessionId;
2495
+ }
2496
+
2497
+ function validateDeviceEnrollmentResult(value) {
2498
+ assertExactKeys(value, ["contract", "revision", "value"]);
2499
+ if (value.contract !== "hellolynk-device-enrollment-v1"
2500
+ || !Number.isSafeInteger(value.revision) || value.revision < 0
2501
+ || (value.value !== null && (typeof value.value !== "object" || Array.isArray(value.value)))) {
2502
+ throw codeError("device_enrollment_store_invalid");
2503
+ }
2504
+ return value;
2505
+ }
2506
+
2507
+ async function invokeWindowsStore(input) {
2508
+ input.signal?.throwIfAborted();
2509
+ const helperMetadata = await lstat(input.helperPath);
2510
+ if (!helperMetadata.isFile() || helperMetadata.isSymbolicLink()) throw codeError("host_protected_helper_invalid");
2511
+ if (!input.verifyOnly) await mkdir(input.root, { recursive: true, mode: 0o700 });
2512
+ const rootMetadata = await lstat(input.root);
2513
+ if (!rootMetadata.isDirectory() || rootMetadata.isSymbolicLink()) throw codeError("host_protected_store_root_invalid");
2514
+
2515
+ const pipeName = `HelloLynkHostProtected-${process.pid}-${randomBytes(18).toString("base64url")}`;
2516
+ const pipePath = `\\\\.\\pipe\\${pipeName}`;
2517
+ const args = [
2518
+ "-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", input.helperPath,
2519
+ "-Command", input.command,
2520
+ "-StoreRoot", input.root,
2521
+ "-ResultPipeName", pipeName,
2522
+ ];
2523
+ if (input.capabilityId) args.push("-CapabilityId", assertCapabilityId(input.capabilityId));
2524
+ if (input.credentialId) args.push(
2525
+ "-CredentialId",
2526
+ assertIdentifier(input.credentialId, "host_transport_credential_id_invalid"),
2527
+ );
2528
+ if (input.sessionId) args.push("-SessionId", assertTransportSessionId(input.sessionId));
2529
+ if (input.principalId) args.push(
2530
+ "-PrincipalId",
2531
+ assertIdentifier(input.principalId, "host_principal_id_invalid"),
2532
+ );
2533
+ if (input.hostInstallationId) args.push(
2534
+ "-HostInstallationId",
2535
+ assertIdentifier(input.hostInstallationId, "host_installation_id_invalid"),
2536
+ );
2537
+ if (input.timestamp != null) args.push("-Timestamp", String(assertTimestamp(input.timestamp)));
2538
+ if (input.pathToVerify) args.push("-PathToVerify", resolve(input.pathToVerify));
2539
+ if (input.verifyDirectory) args.push("-VerifyDirectory");
2540
+ if (input.verifyOnly) args.push("-VerifyOnly");
2541
+ input.signal?.throwIfAborted();
2542
+ const child = spawn("pwsh.exe", args, {
2543
+ windowsHide: true,
2544
+ stdio: ["pipe", "pipe", "pipe"],
2545
+ env: sanitizeHelloLynkChildEnvironment(process.env),
2546
+ });
2547
+ const controller = new AbortController();
2548
+ const stdout = [];
2549
+ const stderr = [];
2550
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
2551
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
2552
+ const exitPromise = new Promise((resolveExit, rejectExit) => {
2553
+ child.on("error", rejectExit);
2554
+ child.on("close", resolveExit);
2555
+ // An early helper failure can close stdin while the parent is still writing.
2556
+ for (const stream of [child.stdin, child.stdout, child.stderr]) stream.on("error", rejectExit);
2557
+ if (input.stdin) child.stdin.end(input.stdin, "utf8");
2558
+ else child.stdin.end();
2559
+ });
2560
+ let timeout;
2561
+ let abort;
2562
+ try {
2563
+ // One budget covers helper exit AND the entire pipe read, including a
2564
+ // connected peer that never ends. Retain the existing 30s allowance for
2565
+ // PowerShell startup, the full 10s SID mutex wait and local protected I/O.
2566
+ const timeoutPromise = new Promise((_, rejectTimeout) => {
2567
+ timeout = setTimeout(() => rejectTimeout(codeError("host_protected_helper_timeout")), 30_000);
2568
+ timeout.unref?.();
2569
+ });
2570
+ const resultPromise = readCurrentUserPipe(pipePath, controller.signal);
2571
+ const completed = Promise.all([
2572
+ exitPromise.then((exitCode) => {
2573
+ if (Buffer.concat(stdout).length) throw codeError("host_protected_helper_stdout_forbidden");
2574
+ if (exitCode !== 0) throw codeError(publicErrorCode(Buffer.concat(stderr).toString("utf8").trim()));
2575
+ }),
2576
+ resultPromise,
2577
+ ]);
2578
+ const cancelled = new Promise((_, reject) => {
2579
+ abort = () => reject(codeError("host_protected_result_cancelled"));
2580
+ if (input.signal?.aborted) abort();
2581
+ else input.signal?.addEventListener("abort", abort, { once: true });
2582
+ });
2583
+ const outcome = Promise.race([completed, timeoutPromise, cancelled]);
2584
+ const [, raw] = await outcome;
2585
+ try { return JSON.parse(raw); } catch { throw codeError("host_protected_result_invalid"); }
2586
+ } catch (error) {
2587
+ // A timed-out write may already have committed. Never retry it here.
2588
+ if (child.exitCode == null && child.signalCode == null) child.kill();
2589
+ throw error;
2590
+ } finally {
2591
+ clearTimeout(timeout);
2592
+ input.signal?.removeEventListener("abort", abort);
2593
+ controller.abort();
2594
+ for (const stream of [child.stdin, child.stdout, child.stderr]) stream.destroy();
2595
+ }
2596
+ }
2597
+
2598
+ async function readCurrentUserPipe(pipePath, signal) {
2599
+ while (true) {
2600
+ if (signal.aborted) throw codeError("host_protected_result_cancelled");
2601
+ try {
2602
+ return await new Promise((resolvePipe, rejectPipe) => {
2603
+ const chunks = [];
2604
+ let bytes = 0;
2605
+ let settled = false;
2606
+ const socket = createConnection(pipePath);
2607
+ const finish = (error, result) => {
2608
+ if (settled) return;
2609
+ settled = true;
2610
+ signal.removeEventListener("abort", abort);
2611
+ socket.destroy();
2612
+ if (error) rejectPipe(error);
2613
+ else resolvePipe(result);
2614
+ };
2615
+ const abort = () => finish(codeError("host_protected_result_cancelled"));
2616
+ signal.addEventListener("abort", abort, { once: true });
2617
+ socket.on("data", (chunk) => {
2618
+ if (settled) return;
2619
+ bytes += chunk.length;
2620
+ if (bytes > 2 * 1024 * 1024) {
2621
+ finish(codeError("host_protected_result_too_large"));
2622
+ return;
2623
+ }
2624
+ chunks.push(chunk);
2625
+ });
2626
+ socket.on("error", (error) => finish(error));
2627
+ socket.on("end", () => finish(null, Buffer.concat(chunks).toString("utf8")));
2628
+ socket.on("close", () => finish(codeError("host_protected_result_incomplete")));
2629
+ });
2630
+ } catch (error) {
2631
+ if (signal.aborted) throw codeError("host_protected_result_cancelled");
2632
+ if (error?.code !== "ENOENT") throw error;
2633
+ await new Promise((resolveWait) => {
2634
+ const finish = () => {
2635
+ clearTimeout(retry);
2636
+ signal.removeEventListener("abort", finish);
2637
+ resolveWait();
2638
+ };
2639
+ const retry = setTimeout(finish, 10);
2640
+ signal.addEventListener("abort", finish, { once: true });
2641
+ });
2642
+ }
2643
+ }
2644
+ }
2645
+
2646
+ function canonicalScopes(value) {
2647
+ if (!Array.isArray(value) || !value.length || value.length > 64) throw codeError("host_capability_scopes_invalid");
2648
+ const scopes = value.map((scope) => assertIdentifier(scope, "host_capability_scope_invalid"));
2649
+ const canonical = [...new Set(scopes)].sort();
2650
+ if (canonical.length !== scopes.length || canonical.some((scope, index) => scope !== scopes[index])) {
2651
+ throw codeError("host_capability_scopes_noncanonical");
2652
+ }
2653
+ return canonical;
2654
+ }
2655
+
2656
+ function assertCapabilityId(value) {
2657
+ const id = String(value || "");
2658
+ if (!CAPABILITY_ID_PATTERN.test(id)) throw codeError("host_capability_id_invalid");
2659
+ return id;
2660
+ }
2661
+
2662
+ function assertRoot(value) {
2663
+ const input = String(value || "");
2664
+ if (process.platform === "win32" && (!/^[A-Za-z]:[\\/]/.test(input) || input.startsWith("\\\\?\\")
2665
+ || input.startsWith("\\\\") || input.includes("\0"))) {
2666
+ throw codeError("host_protected_store_root_invalid");
2667
+ }
2668
+ const root = resolve(input);
2669
+ if (!isAbsolute(root) || root.length < 4) throw codeError("host_protected_store_root_invalid");
2670
+ return root;
2671
+ }
2672
+
2673
+ function assertExactKeys(value, expected) {
2674
+ return assertExactKeysWithCode(value, expected, "host_capability_fields_invalid");
2675
+ }
2676
+
2677
+ function assertExactKeysWithCode(value, expected, code) {
2678
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw codeError(code);
2679
+ const actual = Object.keys(value).sort();
2680
+ const wanted = [...expected].sort();
2681
+ if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) {
2682
+ throw codeError(code);
2683
+ }
2684
+ }
2685
+
2686
+ function assertTimestamp(value) {
2687
+ const number = Number(value);
2688
+ if (!Number.isSafeInteger(number) || number < 0) throw codeError("host_timestamp_invalid");
2689
+ return number;
2690
+ }
2691
+
2692
+ function assertPositiveInteger(value, code) {
2693
+ const number = Number(value);
2694
+ if (!Number.isSafeInteger(number) || number < 1) throw codeError(code);
2695
+ return number;
2696
+ }
2697
+
2698
+ function assertNonNegativeInteger(value, code) {
2699
+ const number = Number(value);
2700
+ if (!Number.isSafeInteger(number) || number < 0) throw codeError(code);
2701
+ return number;
2702
+ }
2703
+
2704
+ function publicErrorCode(value) {
2705
+ const matches = String(value || "").match(/[a-z][a-z0-9_]{2,127}/g);
2706
+ return matches?.at(-1) || "host_protected_store_operation_failed";
2707
+ }
2708
+
2709
+ function normalizeClock(value) {
2710
+ if (value == null) return () => Date.now();
2711
+ if (typeof value === "function") return value;
2712
+ if (typeof value?.now === "function") return () => value.now();
2713
+ throw codeError("host_clock_invalid");
2714
+ }
2715
+
2716
+ function fail(code) { throw codeError(code); }
2717
+ function codeError(code) {
2718
+ const error = new Error(code);
2719
+ error.code = code;
2720
+ return error;
2721
+ }