@hellolynk-ai/workbuddy-mcp 0.0.0-stage → 0.3.96

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 (176) hide show
  1. package/LICENSE +92 -0
  2. package/README.md +88 -2
  3. package/bin/hellolynk-client-setup.mjs +2748 -0
  4. package/bin/hellolynk-mcp-config-shape.mjs +23 -0
  5. package/clients/core/hellolynk-client-account.mjs +180 -0
  6. package/clients/core/hellolynk-client-core.mjs +770 -0
  7. package/clients/core/hellolynk-client-profile.mjs +191 -0
  8. package/clients/core/hellolynk-client-update-entry.mjs +31 -0
  9. package/clients/core/hellolynk-client-update-runtime.mjs +600 -0
  10. package/clients/core/hellolynk-client-update.mjs +453 -0
  11. package/clients/core/hellolynk-contact-request-e2ee.mjs +867 -0
  12. package/clients/core/hellolynk-device-enrollment.mjs +435 -0
  13. package/clients/core/hellolynk-direct-text-e2ee-client.mjs +1964 -0
  14. package/clients/core/hellolynk-direct-text-e2ee.mjs +1207 -0
  15. package/clients/core/hellolynk-host-auth.mjs +347 -0
  16. package/clients/core/hellolynk-host-protected-store.mjs +2721 -0
  17. package/clients/core/hellolynk-host-transport-client.mjs +946 -0
  18. package/clients/core/hellolynk-host.mjs +2374 -0
  19. package/clients/core/hellolynk-human-confirmation-ledger.mjs +256 -0
  20. package/clients/core/hellolynk-human-relationship-controller.mjs +1234 -0
  21. package/clients/core/hellolynk-inbox-read-state.mjs +11 -0
  22. package/clients/core/hellolynk-indexed-message-store.mjs +1558 -0
  23. package/clients/core/hellolynk-instance-conversation.mjs +185 -0
  24. package/clients/core/hellolynk-local-contact-requests.mjs +1733 -0
  25. package/clients/core/hellolynk-local-contacts.mjs +1228 -0
  26. package/clients/core/hellolynk-local-data-migration.mjs +766 -0
  27. package/clients/core/hellolynk-local-data-protection.mjs +747 -0
  28. package/clients/core/hellolynk-local-identity-trust.mjs +274 -0
  29. package/clients/core/hellolynk-local-messaging.mjs +3043 -0
  30. package/clients/core/hellolynk-local-peer-transport.mjs +103 -0
  31. package/clients/core/hellolynk-local-process-lock.mjs +157 -0
  32. package/clients/core/hellolynk-model-inbox.mjs +230 -0
  33. package/clients/core/hellolynk-model-notices.cjs +60 -0
  34. package/clients/core/hellolynk-native-addressing-tools.cjs +231 -0
  35. package/clients/core/hellolynk-native-conversations.mjs +280 -0
  36. package/clients/core/hellolynk-native-human-confirmation-broker.mjs +1036 -0
  37. package/clients/core/hellolynk-native-inbox-runtime.mjs +553 -0
  38. package/clients/core/hellolynk-native-notice.mjs +50 -0
  39. package/clients/core/hellolynk-native-reply-routes.mjs +96 -0
  40. package/clients/core/hellolynk-native-task-index.cjs +305 -0
  41. package/clients/core/hellolynk-native-worker-lease.mjs +84 -0
  42. package/clients/core/hellolynk-neutral-host-assembly.mjs +955 -0
  43. package/clients/core/hellolynk-neutral-host-installation-store.mjs +765 -0
  44. package/clients/core/hellolynk-neutral-host-launcher.mjs +665 -0
  45. package/clients/core/hellolynk-neutral-host-process.mjs +4374 -0
  46. package/clients/core/hellolynk-neutral-host-store-adapters.mjs +548 -0
  47. package/clients/core/hellolynk-neutral-host-workbench-read-model.mjs +1365 -0
  48. package/clients/core/hellolynk-platform-credential-store.mjs +773 -0
  49. package/clients/core/hellolynk-relay-client-cli.mjs +232 -0
  50. package/clients/core/hellolynk-release-retention.mjs +160 -0
  51. package/clients/core/hellolynk-runtime-safety.cjs +471 -0
  52. package/clients/core/hellolynk-workbench-action-grants.mjs +457 -0
  53. package/clients/core/hellolynk-workbench-connection.mjs +234 -0
  54. package/clients/platform/windows/credential-store.ps1 +844 -0
  55. package/clients/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
  56. package/clients/platform/windows/hellolynk-human-confirmation-ledger.ps1 +155 -0
  57. package/clients/platform/windows/hellolynk-local-data-key.ps1 +300 -0
  58. package/clients/platform/windows/hellolynk-native-human-confirmation-broker.ps1 +216 -0
  59. package/clients/workbench/contract.mjs +1314 -0
  60. package/clients/workbench/service.mjs +485 -0
  61. package/hellolynk-client-pack-manifest.json +884 -0
  62. package/hellolynk-client-pack-pin.json +4 -0
  63. package/integrations/workbuddy/package.json +15 -0
  64. package/integrations/workbuddy/server/host-lifecycle.mjs +249 -0
  65. package/integrations/workbuddy/server/mcp-proxy.mjs +258 -0
  66. package/integrations/workbuddy/server/mcp-server.mjs +357 -0
  67. package/integrations/workbuddy/server/model-call-origin.mjs +87 -0
  68. package/integrations/workbuddy/server/native-task-cdp.mjs +175 -0
  69. package/integrations/workbuddy/server/native-task-debug-startup.mjs +117 -0
  70. package/integrations/workbuddy/server/native-task-debug-startup.ps1 +40 -0
  71. package/integrations/workbuddy/server/native-task-mcp-registration.mjs +30 -0
  72. package/integrations/workbuddy/server/native-task-service-registration.mjs +115 -0
  73. package/integrations/workbuddy/server/native-task-worker.mjs +127 -0
  74. package/integrations/workbuddy/server/neutral-host-adapter.mjs +639 -0
  75. package/integrations/workbuddy/server/origin-hook.mjs +111 -0
  76. package/integrations/workbuddy/server/origin-routes.mjs +211 -0
  77. package/integrations/workbuddy/server/package-bootstrap.mjs +111 -0
  78. package/integrations/workbuddy/server/workbench-candidate.mjs +202 -0
  79. package/integrations/workbuddy/server/workbench-hidden-launcher.vbs +46 -0
  80. package/integrations/workbuddy/server/workbench-process.mjs +373 -0
  81. package/integrations/workbuddy/server/workbench-task.ps1 +196 -0
  82. package/integrations/workbuddy/skills/hellolynk-inbox/SKILL.md +9 -0
  83. package/integrations/workbuddy/skills/hellolynk-inbox/workbuddy.json +6 -0
  84. package/package.json +65 -4
  85. package/runtime/connect-activate.mjs +837 -0
  86. package/runtime/core/hellolynk-agent-configurator.mjs +701 -0
  87. package/runtime/core/hellolynk-client-account.mjs +180 -0
  88. package/runtime/core/hellolynk-client-core.mjs +770 -0
  89. package/runtime/core/hellolynk-client-profile.mjs +191 -0
  90. package/runtime/core/hellolynk-client-update-entry.mjs +31 -0
  91. package/runtime/core/hellolynk-client-update-runtime.mjs +600 -0
  92. package/runtime/core/hellolynk-client-update.mjs +453 -0
  93. package/runtime/core/hellolynk-contact-request-e2ee.mjs +867 -0
  94. package/runtime/core/hellolynk-device-enrollment.mjs +435 -0
  95. package/runtime/core/hellolynk-direct-text-e2ee-client.mjs +1964 -0
  96. package/runtime/core/hellolynk-direct-text-e2ee.mjs +1207 -0
  97. package/runtime/core/hellolynk-host-auth.mjs +347 -0
  98. package/runtime/core/hellolynk-host-protected-store.mjs +2721 -0
  99. package/runtime/core/hellolynk-host-transport-client.mjs +946 -0
  100. package/runtime/core/hellolynk-host.mjs +2374 -0
  101. package/runtime/core/hellolynk-human-confirmation-ledger.mjs +256 -0
  102. package/runtime/core/hellolynk-human-relationship-controller.mjs +1234 -0
  103. package/runtime/core/hellolynk-inbox-read-state.mjs +11 -0
  104. package/runtime/core/hellolynk-indexed-message-store.mjs +1558 -0
  105. package/runtime/core/hellolynk-instance-conversation.mjs +185 -0
  106. package/runtime/core/hellolynk-local-contact-requests.mjs +1733 -0
  107. package/runtime/core/hellolynk-local-contacts.mjs +1228 -0
  108. package/runtime/core/hellolynk-local-data-migration.mjs +766 -0
  109. package/runtime/core/hellolynk-local-data-protection.mjs +747 -0
  110. package/runtime/core/hellolynk-local-identity-trust.mjs +274 -0
  111. package/runtime/core/hellolynk-local-messaging.mjs +3043 -0
  112. package/runtime/core/hellolynk-local-peer-transport.mjs +103 -0
  113. package/runtime/core/hellolynk-local-process-lock.mjs +157 -0
  114. package/runtime/core/hellolynk-model-inbox.mjs +230 -0
  115. package/runtime/core/hellolynk-model-notices.cjs +60 -0
  116. package/runtime/core/hellolynk-native-addressing-tools.cjs +231 -0
  117. package/runtime/core/hellolynk-native-conversations.mjs +280 -0
  118. package/runtime/core/hellolynk-native-human-confirmation-broker.mjs +1036 -0
  119. package/runtime/core/hellolynk-native-inbox-runtime.mjs +553 -0
  120. package/runtime/core/hellolynk-native-notice.mjs +50 -0
  121. package/runtime/core/hellolynk-native-reply-routes.mjs +96 -0
  122. package/runtime/core/hellolynk-native-worker-lease.mjs +84 -0
  123. package/runtime/core/hellolynk-neutral-host-assembly.mjs +955 -0
  124. package/runtime/core/hellolynk-neutral-host-installation-store.mjs +765 -0
  125. package/runtime/core/hellolynk-neutral-host-launcher.mjs +665 -0
  126. package/runtime/core/hellolynk-neutral-host-process.mjs +4374 -0
  127. package/runtime/core/hellolynk-neutral-host-store-adapters.mjs +548 -0
  128. package/runtime/core/hellolynk-neutral-host-workbench-read-model.mjs +1365 -0
  129. package/runtime/core/hellolynk-platform-credential-store.mjs +773 -0
  130. package/runtime/core/hellolynk-relay-client-cli.mjs +232 -0
  131. package/runtime/core/hellolynk-release-retention.mjs +160 -0
  132. package/runtime/core/hellolynk-runtime-safety.cjs +471 -0
  133. package/runtime/core/hellolynk-workbench-action-grants.mjs +457 -0
  134. package/runtime/core/hellolynk-workbench-connection.mjs +234 -0
  135. package/runtime/neutral-host-bootstrap.mjs +57 -0
  136. package/runtime/neutral-host-release-entry.mjs +81 -0
  137. package/runtime/neutral-host-release-loader.mjs +156 -0
  138. package/runtime/neutral-host-release-manifest.json +302 -0
  139. package/runtime/neutral-host-release-pin.json +4 -0
  140. package/runtime/platform/windows/configure-agent.ps1 +281 -0
  141. package/runtime/platform/windows/credential-store.ps1 +844 -0
  142. package/runtime/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
  143. package/runtime/platform/windows/hellolynk-human-confirmation-ledger.ps1 +155 -0
  144. package/runtime/platform/windows/hellolynk-local-data-key.ps1 +300 -0
  145. package/runtime/platform/windows/hellolynk-native-human-confirmation-broker.ps1 +216 -0
  146. package/runtime/workbench/candidate-bootstrap.mjs +54 -0
  147. package/runtime/workbench/contract.mjs +1314 -0
  148. package/runtime/workbench/hellolynk-workbench-build.json +215 -0
  149. package/runtime/workbench/host-capabilities.mjs +80 -0
  150. package/runtime/workbench/i18n.mjs +1127 -0
  151. package/runtime/workbench/loopback-surface.mjs +740 -0
  152. package/runtime/workbench/mcp-app-bundle.mjs +97 -0
  153. package/runtime/workbench/service.mjs +485 -0
  154. package/runtime/workbench/state.mjs +421 -0
  155. package/runtime/workbench/transports.mjs +171 -0
  156. package/runtime/workbench/ui/app.mjs +6120 -0
  157. package/runtime/workbench/ui/connection-form.mjs +155 -0
  158. package/runtime/workbench/ui/hellolynk-mark.png +0 -0
  159. package/runtime/workbench/ui/http-transport.mjs +247 -0
  160. package/runtime/workbench/ui/icons/arrow-left.svg +3 -0
  161. package/runtime/workbench/ui/icons/close.svg +3 -0
  162. package/runtime/workbench/ui/icons/contacts.svg +3 -0
  163. package/runtime/workbench/ui/icons/inbox.svg +3 -0
  164. package/runtime/workbench/ui/icons/refresh.svg +3 -0
  165. package/runtime/workbench/ui/icons/search.svg +3 -0
  166. package/runtime/workbench/ui/icons/security.svg +3 -0
  167. package/runtime/workbench/ui/icons/settings.svg +3 -0
  168. package/runtime/workbench/ui/index.html +139 -0
  169. package/runtime/workbench/ui/loopback-entry.mjs +26 -0
  170. package/runtime/workbench/ui/mcp-app-client.mjs +151 -0
  171. package/runtime/workbench/ui/mcp-app-entry.mjs +452 -0
  172. package/runtime/workbench/ui/styles.css +686 -0
  173. package/tools/hellolynk-activate-neutral-host-clients.mjs +837 -0
  174. package/tools/hellolynk-install-client-skills.mjs +1211 -0
  175. package/workbuddy-package-manifest.json +1 -0
  176. package/workbuddy-package-pin.json +1 -0
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+
6
+ import { loadNeutralHostRelease } from "./neutral-host-release-loader.mjs";
7
+
8
+ export async function runNeutralHostBootstrap(argv = process.argv.slice(2)) {
9
+ const root = parse(argv);
10
+ const release = await loadNeutralHostRelease();
11
+ if (typeof release?.run !== "function") throw codeError("neutral_runtime_integrity_invalid");
12
+ return release.run({ root });
13
+ }
14
+
15
+ function parse(argv) {
16
+ if (argv.length !== 3 || argv[0] !== "run" || argv[1] !== "--root"
17
+ || !path.isAbsolute(String(argv[2] || ""))) {
18
+ throw codeError("neutral_runtime_launch_configuration_required");
19
+ }
20
+ return path.resolve(argv[2]);
21
+ }
22
+
23
+ function codeError(code) { const error = new Error(code); error.code = code; return error; }
24
+
25
+ // Start failures and a failed Host both end here. A failed Host keeps its lock until this
26
+ // process exits on its own: a closed stderr pipe (the detached launcher may be gone) must
27
+ // not crash it before admitted work finishes.
28
+ function exitWithFailure(code) {
29
+ process.stderr.on("error", () => {});
30
+ process.stderr.write(`${JSON.stringify({ ok: false, code })}\n`);
31
+ process.exitCode = 1;
32
+ }
33
+
34
+ // A stray rejection must not take the Host down: log a stable code and keep serving. A synchronous
35
+ // uncaught exception leaves state unknown: stop the Host (releases its lock), then exit; 15 s bounds a stuck stop.
36
+ function guardHostProcess(result) {
37
+ process.on("unhandledRejection", (reason) => writeFailure("neutral_runtime_unhandled_rejection", reason));
38
+ process.on("uncaughtException", (error) => {
39
+ writeFailure("neutral_runtime_uncaught_exception", error);
40
+ process.exitCode = 1;
41
+ const exit = () => process.exit(1);
42
+ setTimeout(exit, 15_000).unref?.();
43
+ Promise.resolve().then(() => result?.stop?.("uncaught_exception")).then(exit, exit);
44
+ });
45
+ return result;
46
+ }
47
+
48
+ function writeFailure(code, cause) {
49
+ if (!process.stderr.listenerCount("error")) process.stderr.on("error", () => {});
50
+ process.stderr.write(`${JSON.stringify({ ok: false, code, cause: String(cause?.code || cause?.name || "").slice(0, 80) })}\n`);
51
+ }
52
+
53
+ if (import.meta.url === pathToFileURL(process.argv[1] || "").href) {
54
+ runNeutralHostBootstrap()
55
+ .then((result) => guardHostProcess(result)?.waitUntilStopped?.() || new Promise(() => {}))
56
+ .catch((error) => exitWithFailure(String(error?.code || "neutral_runtime_launch_failed")));
57
+ }
@@ -0,0 +1,81 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { createHostProtectedStore } from "./core/hellolynk-host-protected-store.mjs";
6
+ import {
7
+ createNeutralHostInstallationStore,
8
+ defaultNeutralHostStoreRoot,
9
+ } from "./core/hellolynk-neutral-host-installation-store.mjs";
10
+ import { startHelloLynkNeutralHostAssembly } from "./core/hellolynk-neutral-host-assembly.mjs";
11
+
12
+ export async function createNeutralHostReleaseRuntime() {
13
+ return Object.freeze({
14
+ defaultStoreRoot: (env) => defaultNeutralHostStoreRoot("win32", env),
15
+ createInstallationStore: (options) => createNeutralHostInstallationStore(options),
16
+ createProtectedStore: (options) => createHostProtectedStore(options),
17
+ run: async ({ root }) => runProductionHost(root),
18
+ });
19
+ }
20
+
21
+ async function runProductionHost(rootInput) {
22
+ const rootText = String(rootInput || "");
23
+ if (!path.isAbsolute(rootText) || path.resolve(rootText) !== rootText || rootText.length < 4) {
24
+ throw codeError("neutral_runtime_launch_configuration_required");
25
+ }
26
+ const root = rootText;
27
+ const installationStore = createNeutralHostInstallationStore({ root });
28
+ const protectedStore = createHostProtectedStore({ root, installationStore });
29
+ const binding = await resolveInstalledBinding(protectedStore);
30
+ const profiles = await readProfileManifest(binding.profileManifestPath);
31
+ const assembly = await startHelloLynkNeutralHostAssembly({
32
+ root,
33
+ runtimeRoot: binding.runtimeRoot,
34
+ profiles: profiles.map((profileFile) => ({ profileFile })),
35
+ }, { installationStore, protectedStore });
36
+ return Object.freeze({
37
+ stop: assembly.stop,
38
+ waitUntilStopped: async () => {
39
+ while (assembly.host.status !== "stopped") {
40
+ // A failed Host never reaches "stopped"; end the wait so the bootstrap exits with its code.
41
+ if (assembly.host.status === "failed") throw codeError(assembly.host.failureCode || "neutral_host_stop_failed");
42
+ await new Promise((resolve) => setTimeout(resolve, 50));
43
+ }
44
+ },
45
+ });
46
+ }
47
+
48
+ async function resolveInstalledBinding(store) {
49
+ const releaseRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
50
+ const candidates = [];
51
+ for (const resolveBinding of [
52
+ () => store.resolveWorkBuddyNeutralBinding(),
53
+ () => store.resolveOpenClawNeutralBinding(),
54
+ ]) {
55
+ try {
56
+ const binding = await resolveBinding();
57
+ if (path.resolve(binding.releaseRoot || "") === releaseRoot) candidates.push(binding);
58
+ } catch {}
59
+ }
60
+ if (candidates.length !== 1) throw codeError("neutral_runtime_binding_invalid");
61
+ return candidates[0];
62
+ }
63
+
64
+ async function readProfileManifest(manifestPath) {
65
+ let manifest;
66
+ try { manifest = JSON.parse(await fs.readFile(manifestPath, "utf8")); }
67
+ catch { throw codeError("neutral_runtime_profile_manifest_invalid"); }
68
+ if (!manifest || manifest.contract !== "hellolynk-neutral-host-profile-manifest-v1"
69
+ || !Array.isArray(manifest.profiles) || !manifest.profiles.length || manifest.profiles.length > 16
70
+ || Object.keys(manifest).sort().join(",") !== "contract,profiles") {
71
+ throw codeError("neutral_runtime_profile_manifest_invalid");
72
+ }
73
+ const profiles = manifest.profiles.map((value) => String(value || ""));
74
+ if (profiles.some((value, index) => !path.isAbsolute(value)
75
+ || path.resolve(value) !== value || profiles.indexOf(value) !== index)) {
76
+ throw codeError("neutral_runtime_profile_manifest_invalid");
77
+ }
78
+ return Object.freeze(profiles);
79
+ }
80
+
81
+ function codeError(code) { const error = new Error(code); error.code = code; return error; }
@@ -0,0 +1,156 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+
6
+ const ROOT = path.dirname(fileURLToPath(import.meta.url));
7
+ const PIN_PATH = path.join(ROOT, "neutral-host-release-pin.json");
8
+ const MANIFEST_PATH = path.join(ROOT, "neutral-host-release-manifest.json");
9
+ const SAFE_RUNTIME_RELATIVE = /^(?:[A-Za-z0-9][A-Za-z0-9._-]*\/)*[A-Za-z0-9][A-Za-z0-9._-]*\.(?:cjs|mjs|ps1)$/;
10
+ const SAFE_RELEASE_RELATIVE = /^release\/(?:[A-Za-z0-9][A-Za-z0-9._-]*\/)*[A-Za-z0-9][A-Za-z0-9._-]*\.(?:mjs|ps1|md|html|js|css|json)$/;
11
+
12
+ export async function inspectNeutralHostRelease() {
13
+ await assertNoReparseAncestors(ROOT);
14
+ const pinArtifact = await readArtifact(PIN_PATH, 16 * 1024);
15
+ const pin = parseJson(pinArtifact.bytes);
16
+ exact(pin, ["contract", "manifestSha256"]);
17
+ if (pin.contract !== "hellolynk-neutral-host-release-pin-v1" || !HASH.test(String(pin.manifestSha256 || ""))) fail();
18
+ const manifestArtifact = await readArtifact(MANIFEST_PATH, 256 * 1024);
19
+ if (sha256(manifestArtifact.bytes) !== pin.manifestSha256) fail();
20
+ let manifest;
21
+ try { manifest = JSON.parse(manifestArtifact.bytes); } catch { fail(); }
22
+ exact(manifest, ["contract", "entry", "files"]);
23
+ if (manifest.contract !== "hellolynk-neutral-host-release-manifest-v1"
24
+ || !Array.isArray(manifest.files) || manifest.files.length < 3 || manifest.files.length > 128) fail();
25
+ const files = [];
26
+ const seen = new Set();
27
+ for (const item of manifest.files) {
28
+ exact(item, ["path", "sha256"]);
29
+ const relative = safeRelative(item.path);
30
+ if (seen.has(relative) || !HASH.test(String(item.sha256 || ""))) fail();
31
+ seen.add(relative);
32
+ const artifact = await readArtifact(inside(relative), 2 * 1024 * 1024);
33
+ if (sha256(artifact.bytes) !== item.sha256) fail();
34
+ files.push(Object.freeze({ relative, sha256: item.sha256, identity: artifact.identity }));
35
+ }
36
+ const entry = safeRelative(manifest.entry);
37
+ if (!seen.has(entry) || !seen.has("neutral-host-bootstrap.mjs")
38
+ || !seen.has("neutral-host-release-loader.mjs")) fail();
39
+ return Object.freeze({
40
+ contract: "hellolynk-neutral-host-release-inspection-v1",
41
+ releaseRoot: path.dirname(ROOT),
42
+ manifestSha256: pin.manifestSha256,
43
+ entry,
44
+ controls: Object.freeze([
45
+ Object.freeze({ relative: "neutral-host-release-pin.json", sha256: sha256(pinArtifact.bytes), identity: pinArtifact.identity }),
46
+ Object.freeze({ relative: "neutral-host-release-manifest.json", sha256: pin.manifestSha256, identity: manifestArtifact.identity }),
47
+ ]),
48
+ files: Object.freeze(files),
49
+ });
50
+ }
51
+
52
+ export async function activateNeutralHostRelease(inspection) {
53
+ if (!inspection || inspection.contract !== "hellolynk-neutral-host-release-inspection-v1"
54
+ || inspection.releaseRoot !== path.dirname(ROOT) || !Array.isArray(inspection.controls)
55
+ || inspection.controls.length !== 2 || !Array.isArray(inspection.files)) fail();
56
+ await assertNoReparseAncestors(ROOT);
57
+ for (const expected of inspection.controls) {
58
+ const control = CONTROL_PATHS.get(expected.relative);
59
+ if (!control) fail();
60
+ const artifact = await readArtifact(control, 256 * 1024);
61
+ if (sha256(artifact.bytes) !== expected.sha256 || !sameIdentity(artifact.identity, expected.identity)) fail();
62
+ }
63
+ for (const expected of inspection.files) {
64
+ const artifact = await readArtifact(inside(safeRelative(expected.relative)), 2 * 1024 * 1024);
65
+ if (sha256(artifact.bytes) !== expected.sha256 || !sameIdentity(artifact.identity, expected.identity)) fail();
66
+ }
67
+ const entryFile = inside(safeRelative(inspection.entry));
68
+ let module;
69
+ try { module = await import(`${pathToFileURL(entryFile).href}?verified=${inspection.manifestSha256}`); }
70
+ catch { throw codeError("neutral_runtime_runner_integrity_invalid"); }
71
+ if (typeof module.createNeutralHostReleaseRuntime !== "function") fail();
72
+ const runtime = await module.createNeutralHostReleaseRuntime();
73
+ if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) fail();
74
+ return Object.freeze({ ...runtime, releaseManifestSha256: inspection.manifestSha256 });
75
+ }
76
+
77
+ export async function loadNeutralHostRelease() {
78
+ return activateNeutralHostRelease(await inspectNeutralHostRelease());
79
+ }
80
+
81
+ async function readArtifact(file, maximum) {
82
+ await assertNoReparseAncestors(file);
83
+ let handle;
84
+ try { handle = await fs.open(file, "r"); }
85
+ catch (error) {
86
+ if (error?.code === "ENOENT") throw codeError("neutral_runtime_runner_unavailable");
87
+ throw codeError("neutral_runtime_runner_integrity_invalid");
88
+ }
89
+ try {
90
+ const before = await handle.stat({ bigint: true });
91
+ if (!before.isFile() || before.nlink !== 1n || before.size > BigInt(maximum)) fail();
92
+ const bytes = await handle.readFile();
93
+ const after = await handle.stat({ bigint: true });
94
+ if (!sameIdentity(identity(before), identity(after))) fail();
95
+ return Object.freeze({ bytes, identity: identity(after) });
96
+ } catch (error) {
97
+ if (/^neutral_runtime_/.test(String(error?.code || ""))) throw error;
98
+ throw codeError("neutral_runtime_runner_integrity_invalid");
99
+ } finally { await handle?.close(); }
100
+ }
101
+
102
+ async function assertNoReparseAncestors(target) {
103
+ const resolved = path.resolve(target);
104
+ const parsed = path.parse(resolved);
105
+ let cursor = parsed.root;
106
+ for (const segment of path.relative(parsed.root, resolved).split(path.sep).filter(Boolean)) {
107
+ cursor = path.join(cursor, segment);
108
+ let stat;
109
+ try { stat = await fs.lstat(cursor); }
110
+ catch (error) {
111
+ if (error?.code === "ENOENT") throw codeError("neutral_runtime_runner_unavailable");
112
+ throw codeError("neutral_runtime_runner_integrity_invalid");
113
+ }
114
+ if (stat.isSymbolicLink()) fail();
115
+ }
116
+ }
117
+
118
+ function identity(stat) {
119
+ return Object.freeze({
120
+ dev: String(stat.dev), ino: String(stat.ino), size: String(stat.size),
121
+ mtimeNs: String(stat.mtimeNs), nlink: String(stat.nlink),
122
+ });
123
+ }
124
+ function sameIdentity(left, right) {
125
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size
126
+ && left.mtimeNs === right.mtimeNs && left.nlink === right.nlink;
127
+ }
128
+ function parseJson(bytes) { try { return JSON.parse(bytes); } catch { fail(); } }
129
+ function safeRelative(value) {
130
+ const text = String(value || "");
131
+ const allowed = text.startsWith("release/") ? SAFE_RELEASE_RELATIVE : SAFE_RUNTIME_RELATIVE;
132
+ if (!allowed.test(text) || text.split("/").includes("..")) fail();
133
+ return text;
134
+ }
135
+ function inside(relative) {
136
+ const releaseRoot = path.dirname(ROOT);
137
+ const result = relative.startsWith("release/")
138
+ ? path.resolve(releaseRoot, relative.slice("release/".length))
139
+ : path.resolve(ROOT, relative);
140
+ const base = relative.startsWith("release/") ? releaseRoot : ROOT;
141
+ if (!result.startsWith(`${base}${path.sep}`)) fail();
142
+ return result;
143
+ }
144
+ function exact(value, expected) {
145
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail();
146
+ const actual = Object.keys(value).sort(); const wanted = [...expected].sort();
147
+ if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) fail();
148
+ }
149
+ const HASH = /^[a-f0-9]{64}$/;
150
+ const CONTROL_PATHS = new Map([
151
+ ["neutral-host-release-pin.json", PIN_PATH],
152
+ ["neutral-host-release-manifest.json", MANIFEST_PATH],
153
+ ]);
154
+ function sha256(value) { return crypto.createHash("sha256").update(value).digest("hex"); }
155
+ function fail() { throw codeError("neutral_runtime_runner_integrity_invalid"); }
156
+ function codeError(code) { const error = new Error(code); error.code = code; return error; }
@@ -0,0 +1,302 @@
1
+ {
2
+ "contract": "hellolynk-neutral-host-release-manifest-v1",
3
+ "entry": "neutral-host-release-entry.mjs",
4
+ "files": [
5
+ {
6
+ "path": "neutral-host-bootstrap.mjs",
7
+ "sha256": "db0ffd88a5fbb9091f16ceab4e391ac6919ba2640cdbc358574034779f848d7e"
8
+ },
9
+ {
10
+ "path": "neutral-host-release-loader.mjs",
11
+ "sha256": "00b226c65732bcd4d18d2b1b4ebc5092b7073fac52ee500d5d746b01217bfa65"
12
+ },
13
+ {
14
+ "path": "neutral-host-release-entry.mjs",
15
+ "sha256": "d434d392c85728228772369e9a890cef34598833ab2433dcc7b963c17bf5c146"
16
+ },
17
+ {
18
+ "path": "connect-activate.mjs",
19
+ "sha256": "50ec8b0a58ac5b099a653722b0946acb23d0a29a054bfb3856dc963db6f84bee"
20
+ },
21
+ {
22
+ "path": "core/hellolynk-agent-configurator.mjs",
23
+ "sha256": "505b010ffd2c3fb614f2a4c6eae92a9fc5b640b8e48dff39e563dcbaabfd344b"
24
+ },
25
+ {
26
+ "path": "core/hellolynk-client-account.mjs",
27
+ "sha256": "ab7ec5dda92e510e7deb14f900fd2df4c6c3373fbeb4af4fb2b6c1a72aa41a99"
28
+ },
29
+ {
30
+ "path": "core/hellolynk-client-core.mjs",
31
+ "sha256": "94bbad1d0ccee969bbffed176449e427188f1b892512a64167ab31f63d258e2b"
32
+ },
33
+ {
34
+ "path": "core/hellolynk-client-profile.mjs",
35
+ "sha256": "4b45a78b4d6c4794a1f494bd95777579c32c56a68321a1d3b484048942ae4742"
36
+ },
37
+ {
38
+ "path": "core/hellolynk-client-update-entry.mjs",
39
+ "sha256": "b2740406d79d0fa129c3bd1b5e6a07c3f52eed5e35a91ffea40b099ddc708936"
40
+ },
41
+ {
42
+ "path": "core/hellolynk-client-update-runtime.mjs",
43
+ "sha256": "3ed31f0cea704c3ad26481126a78a7d39c21df69a5c52d1074a096a2b7b3e32f"
44
+ },
45
+ {
46
+ "path": "core/hellolynk-client-update.mjs",
47
+ "sha256": "8f0d6f54313c91bd2ffb4643859a91dd1e122bb5f1da9143fe2851a3370d7fbf"
48
+ },
49
+ {
50
+ "path": "core/hellolynk-release-retention.mjs",
51
+ "sha256": "68321383ecf73adf55d842c7215f5e0b0fe72851aac292885740be99743e1bd0"
52
+ },
53
+ {
54
+ "path": "core/hellolynk-contact-request-e2ee.mjs",
55
+ "sha256": "657664c6250d2738061e1ec2680eda6b36063c357d9e2d9c743e4218ece50895"
56
+ },
57
+ {
58
+ "path": "core/hellolynk-device-enrollment.mjs",
59
+ "sha256": "f3e99ff2bdfa76e5a830a518792a1b9e5e705d7a8a01eca725dd7c5c8391d4ef"
60
+ },
61
+ {
62
+ "path": "core/hellolynk-direct-text-e2ee-client.mjs",
63
+ "sha256": "4007bf5e6c91df00adf10384cc1f0d0d28491b8ded518365dddbac717f3ac5be"
64
+ },
65
+ {
66
+ "path": "core/hellolynk-direct-text-e2ee.mjs",
67
+ "sha256": "f0abc3714cfad007669140ead4bfbb3f5bd56edd24d4389c3addee3f42887a01"
68
+ },
69
+ {
70
+ "path": "core/hellolynk-host-auth.mjs",
71
+ "sha256": "db1ec56f4e60e6450fba50188eecf89748f39f5d7a75c2456c30a6053233555d"
72
+ },
73
+ {
74
+ "path": "core/hellolynk-host-protected-store.mjs",
75
+ "sha256": "89b6e6c4ce9c6ba2984f8c301de4251da6004bdd402cc44d578a4505c21b75b1"
76
+ },
77
+ {
78
+ "path": "core/hellolynk-host-transport-client.mjs",
79
+ "sha256": "9f27d3e44ce8333413e712eb38b970a335b64081d7e3be6882ff5ca921668859"
80
+ },
81
+ {
82
+ "path": "core/hellolynk-host.mjs",
83
+ "sha256": "6cbe0ba6ec7aa81059a4a2d0e247fbedb6731687ec21ded4aaefa0e79be9e493"
84
+ },
85
+ {
86
+ "path": "core/hellolynk-model-notices.cjs",
87
+ "sha256": "c1b0d9071d3750c87755bc9fdf969d89c7fa62a0dede4c37b8cb2954b81163b0"
88
+ },
89
+ {
90
+ "path": "core/hellolynk-model-inbox.mjs",
91
+ "sha256": "516c846340ee7a2fd624eaedaa51ded81988ef875b8a7eeb15c2e296774bfa63"
92
+ },
93
+ {
94
+ "path": "core/hellolynk-native-inbox-runtime.mjs",
95
+ "sha256": "039ba7fe8273357d178556fc57e751c469e2bf0056bcaee5029e7fe376099c72"
96
+ },
97
+ {
98
+ "path": "core/hellolynk-native-reply-routes.mjs",
99
+ "sha256": "8ed07580f2b6fe6bff9953951ea79f6c7c1c806887cd0bf21d4893ba8343ebe2"
100
+ },
101
+ {
102
+ "path": "core/hellolynk-native-worker-lease.mjs",
103
+ "sha256": "e4e6864ff047b1d3511c363eb2bc7a3a3841a453f5d9d40b94bde55cc38cebc5"
104
+ },
105
+ {
106
+ "path": "core/hellolynk-human-confirmation-ledger.mjs",
107
+ "sha256": "63bcbbb0870ad83fc031636d74ce77fc070d5f4d1d4c10297f8b3ec9ad85bbf6"
108
+ },
109
+ {
110
+ "path": "core/hellolynk-human-relationship-controller.mjs",
111
+ "sha256": "7ef584c30febf0e142f621b719e433c0a9f5d976bbb917ab15f6cc1abb39ad52"
112
+ },
113
+ {
114
+ "path": "core/hellolynk-indexed-message-store.mjs",
115
+ "sha256": "61a9d642fb0453aa949c6e634a30ae3f8562a81d3a666580a2f32d414c9f80ad"
116
+ },
117
+ {
118
+ "path": "core/hellolynk-inbox-read-state.mjs",
119
+ "sha256": "bf2353075f0a10cc3492c0d7309f8d3fef4a4bda1996b878cfbae8515beaeb34"
120
+ },
121
+ {
122
+ "path": "core/hellolynk-instance-conversation.mjs",
123
+ "sha256": "f00161cbdab48b33f0be1a809389145c167d8b8bdfab32d8c929db3bc167b4a1"
124
+ },
125
+ {
126
+ "path": "core/hellolynk-local-contact-requests.mjs",
127
+ "sha256": "9724729b28c4c979696fcd53531e89eddb18a3045e32f602ecfb002af1c44b4c"
128
+ },
129
+ {
130
+ "path": "core/hellolynk-local-contacts.mjs",
131
+ "sha256": "b7c722f0c300f951ed390ea82aec04975b226f2ea4d5c873764ab36ef9a8bc2c"
132
+ },
133
+ {
134
+ "path": "core/hellolynk-local-data-migration.mjs",
135
+ "sha256": "1e791114633770fe3955842725965798dfd749e1dbf513f08efc4e01b9ed1756"
136
+ },
137
+ {
138
+ "path": "core/hellolynk-local-data-protection.mjs",
139
+ "sha256": "1bda879aa0bdf46700a63374e82cc7bc639385656b1463a75a49e8510971066c"
140
+ },
141
+ {
142
+ "path": "core/hellolynk-local-identity-trust.mjs",
143
+ "sha256": "b6d22715c627820c0ea92ca543cf52bbfeee5d5f416d242c2e21230640310420"
144
+ },
145
+ {
146
+ "path": "core/hellolynk-local-messaging.mjs",
147
+ "sha256": "23e701818b18b96ba36ad1dfc3c2489d80fec8dafa2f433cf29947175786ed07"
148
+ },
149
+ {
150
+ "path": "core/hellolynk-local-peer-transport.mjs",
151
+ "sha256": "9c32b2fa5f716f1ff36e09b3f2cd697479acc2f3c6bcb6e96045f5ca65e3b1c6"
152
+ },
153
+ {
154
+ "path": "core/hellolynk-local-process-lock.mjs",
155
+ "sha256": "5212990a98b9ad2eb83fa2d5fb8a123d687c5ef6daf6cba49a4b6d04a7fe251e"
156
+ },
157
+ {
158
+ "path": "core/hellolynk-native-conversations.mjs",
159
+ "sha256": "671030938a5259f6cce0c1eb4e61997f7f89232c677c9ba26062daea91b6f8c6"
160
+ },
161
+ {
162
+ "path": "core/hellolynk-native-notice.mjs",
163
+ "sha256": "6a5fe50fbccac465d690612839eaf7c10a5622cd84151277b0bf4507eccbe087"
164
+ },
165
+ {
166
+ "path": "core/hellolynk-native-addressing-tools.cjs",
167
+ "sha256": "252251de855c3fe9c5ebdd54a97ba976a8009f4ac57ec44f492bed8c7fb903de"
168
+ },
169
+ {
170
+ "path": "core/hellolynk-native-human-confirmation-broker.mjs",
171
+ "sha256": "2fb2d3b40869362be3e40a1ec55947021319af5315cbf8fdc5ed59df717cceb3"
172
+ },
173
+ {
174
+ "path": "core/hellolynk-neutral-host-assembly.mjs",
175
+ "sha256": "d0ac9c697bf8a85263d99a0739da557c62f52c72f4afb105eb97b3aac2ae1f3b"
176
+ },
177
+ {
178
+ "path": "core/hellolynk-neutral-host-installation-store.mjs",
179
+ "sha256": "b5f6288732af6f9b9c0c750f6002eae657dde9513fc97674f6e59200f3cc9dc6"
180
+ },
181
+ {
182
+ "path": "core/hellolynk-neutral-host-launcher.mjs",
183
+ "sha256": "789b2531cdbde6e6d8f91f2a40746372e903e9e48e8aa6846b9abf2d28150780"
184
+ },
185
+ {
186
+ "path": "core/hellolynk-neutral-host-process.mjs",
187
+ "sha256": "6d1913e3fb6e35d9a090afde39bf4a1521b17d1620fb09420e507d60af77cce4"
188
+ },
189
+ {
190
+ "path": "core/hellolynk-neutral-host-store-adapters.mjs",
191
+ "sha256": "3e425ca2cc111e750f55e94be2561cae8a3b308e473fb549e42da3443b67fd34"
192
+ },
193
+ {
194
+ "path": "core/hellolynk-neutral-host-workbench-read-model.mjs",
195
+ "sha256": "65ca217dbd5be05f4cf38fd977b0d536526f79463a402fae90a7329dac375670"
196
+ },
197
+ {
198
+ "path": "core/hellolynk-platform-credential-store.mjs",
199
+ "sha256": "f27f5f377cc4d6d2b5664212cf8fcc4c98d910064c1677ff9c1f776df1a59dba"
200
+ },
201
+ {
202
+ "path": "core/hellolynk-relay-client-cli.mjs",
203
+ "sha256": "29707c4e0d91d5db6f46408b4c1101b9ab30b117500a9e315da8802e71a43010"
204
+ },
205
+ {
206
+ "path": "core/hellolynk-runtime-safety.cjs",
207
+ "sha256": "43d576d9f774409f4a735793c1503a6f397e787b10c59a4ac9cc9257299f5efe"
208
+ },
209
+ {
210
+ "path": "core/hellolynk-workbench-action-grants.mjs",
211
+ "sha256": "076d1bb848c85c06695440f33daebd853a9f49147a85cf582cc3e2964d95198a"
212
+ },
213
+ {
214
+ "path": "core/hellolynk-workbench-connection.mjs",
215
+ "sha256": "d3396350fd75af78e60772e021cf6dfb643d9eee41894c5461c959a56650cb60"
216
+ },
217
+ {
218
+ "path": "workbench/candidate-bootstrap.mjs",
219
+ "sha256": "891b02f750693e5b4836e716922c2d632f6736b2a5b3b7417f161499459f3f56"
220
+ },
221
+ {
222
+ "path": "workbench/contract.mjs",
223
+ "sha256": "20648c3ed8e20b984b698010855414f8d4acaf0e2ade53b997b5582a85d90a08"
224
+ },
225
+ {
226
+ "path": "workbench/host-capabilities.mjs",
227
+ "sha256": "4e1a82e6cb46dd775ba39e2d34ff21eb1c293523883d18e607084ca91cfffb87"
228
+ },
229
+ {
230
+ "path": "workbench/i18n.mjs",
231
+ "sha256": "272917746c06cabc1b87b94341f2deca5591f0bea2d7a0e07b2d959d8e1f8c74"
232
+ },
233
+ {
234
+ "path": "workbench/loopback-surface.mjs",
235
+ "sha256": "6a550034c5dd558dfd22f9cb589fd76df40a99e976538e3b5d01371d212c9aa6"
236
+ },
237
+ {
238
+ "path": "workbench/mcp-app-bundle.mjs",
239
+ "sha256": "48fb8224cbaa7f78a386a79b2dba761f4de6c4b83b16032f7f96bd9783e172e4"
240
+ },
241
+ {
242
+ "path": "workbench/service.mjs",
243
+ "sha256": "3bf82a0076e9fe3b62105c626fe0d622092224f28d3905c62eafd65bf5385dcc"
244
+ },
245
+ {
246
+ "path": "workbench/state.mjs",
247
+ "sha256": "770d10f3bf76236772c87e86f5da346c013da387fb40c08360f895a7052cf7b5"
248
+ },
249
+ {
250
+ "path": "workbench/transports.mjs",
251
+ "sha256": "0c9f42ca9c9a4b4cc29b1bdcc0a0488d16a9ad06c235c642dff9cd4fc6f0d528"
252
+ },
253
+ {
254
+ "path": "workbench/ui/app.mjs",
255
+ "sha256": "643a5ac8d303f936ee87dd7c0c851aa1988e99ea9a9a45c6c89da28bdc76e799"
256
+ },
257
+ {
258
+ "path": "workbench/ui/connection-form.mjs",
259
+ "sha256": "700ca793f5eddca1dfea5f220c73732efc51ea4e1f8b8fc36da2bb67136240f0"
260
+ },
261
+ {
262
+ "path": "workbench/ui/http-transport.mjs",
263
+ "sha256": "b2fedbc2c34bcf065e0fc23c8c5fb37d358c03daa2c82ba46530b8dfb8fdc571"
264
+ },
265
+ {
266
+ "path": "workbench/ui/loopback-entry.mjs",
267
+ "sha256": "0e5e5d259ab6e2d8408c6d2e089551e6f279dcb1267e9db19f9d9dded0956fd4"
268
+ },
269
+ {
270
+ "path": "workbench/ui/mcp-app-client.mjs",
271
+ "sha256": "91b1e6e84a12434c2be7614845febfa44afa341d5d7f580bc1bfc106ed37f8f8"
272
+ },
273
+ {
274
+ "path": "workbench/ui/mcp-app-entry.mjs",
275
+ "sha256": "ba82ac55c4efcf7701bda33b6c6e0df4d59da542b1d6c33c96ba7f2391c4773a"
276
+ },
277
+ {
278
+ "path": "platform/windows/configure-agent.ps1",
279
+ "sha256": "7dc7c75bf0f7e45060b041a08d502993afe84581040f165e7f4437bf490708fa"
280
+ },
281
+ {
282
+ "path": "platform/windows/credential-store.ps1",
283
+ "sha256": "d00dae4af08205fffa52c4956d1c87d5c3423e8f20982ccfc1b76e3424fe442e"
284
+ },
285
+ {
286
+ "path": "platform/windows/hellolynk-host-protected-store.ps1",
287
+ "sha256": "24357e8ce54090b4ec68e457f02c9e834a80f3273ef3c03a8c0fa7258797fb49"
288
+ },
289
+ {
290
+ "path": "platform/windows/hellolynk-human-confirmation-ledger.ps1",
291
+ "sha256": "4612561e2128dfd96c018f2c2d9ae41016f35bb6df8536314e9f0fb428165a82"
292
+ },
293
+ {
294
+ "path": "platform/windows/hellolynk-local-data-key.ps1",
295
+ "sha256": "0976a0ecdd724e767b87e65743a230569c3fcb30e2449cf38bb718eb1604c15c"
296
+ },
297
+ {
298
+ "path": "platform/windows/hellolynk-native-human-confirmation-broker.ps1",
299
+ "sha256": "757a731a3e8761d5d473db19ecfc330783f2826663a0057c4d6896e679d7a388"
300
+ }
301
+ ]
302
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "contract": "hellolynk-neutral-host-release-pin-v1",
3
+ "manifestSha256": "2f11bc431837eadac3a49baa4d31f7a4fc303476eeb586df0cf28d0766e204e5"
4
+ }