@vascend/talos 0.1.0 → 0.1.1

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 (66) hide show
  1. package/README.md +7 -3
  2. package/dist/backend/asset-events.js +9 -9
  3. package/dist/core/agent.js +3 -3
  4. package/dist/core/cli-args.js +50 -50
  5. package/dist/core/cli-installation.d.ts +4 -0
  6. package/dist/core/cli-installation.js +55 -0
  7. package/dist/core/compaction.js +26 -26
  8. package/dist/core/config.js +101 -101
  9. package/dist/core/execution-guidance.js +21 -21
  10. package/dist/core/package-update.d.ts +3 -1
  11. package/dist/core/package-update.js +19 -6
  12. package/dist/core/self-update.d.ts +1 -1
  13. package/dist/core/signed-release.d.ts +33 -0
  14. package/dist/core/signed-release.js +121 -0
  15. package/dist/core/update-check.d.ts +1 -1
  16. package/dist/core/update-check.js +15 -14
  17. package/dist/core/vibe-modes.js +13 -13
  18. package/dist/distribution/install.d.ts +1 -0
  19. package/dist/distribution/install.js +31 -0
  20. package/dist/install.mjs +271 -0
  21. package/dist/node-runtime.json +13 -0
  22. package/dist/providers/oauth-callback-server.js +32 -32
  23. package/dist/tools/capabilities.js +30 -30
  24. package/dist/tools/computer-capture.js +50 -50
  25. package/dist/tools/program.js +82 -82
  26. package/dist/tui/i18n-catalog.js +16 -0
  27. package/dist/tui/quick-help.js +31 -31
  28. package/dist/tui/repl.js +5 -5
  29. package/dist/tui/update-prompt.js +1 -0
  30. package/dist/vascend/code-events.js +9 -9
  31. package/dist/vascend/completeness.js +31 -31
  32. package/dist/vascend/context-fabric.js +16 -16
  33. package/dist/vascend/contract-memory.js +30 -30
  34. package/dist/vascend/council.js +82 -82
  35. package/dist/vascend/decision-promotion.js +23 -23
  36. package/dist/vascend/granular-kingdom.js +17 -17
  37. package/dist/vascend/granular-launcher.js +2 -2
  38. package/dist/vascend/kingdom-kpi.js +19 -19
  39. package/dist/vascend/kingdom-run.js +6 -6
  40. package/dist/vascend/kingdom.js +56 -56
  41. package/dist/vascend/mandate.js +21 -21
  42. package/dist/vascend/objective-revision.js +34 -34
  43. package/dist/vascend/patch-plan.js +22 -22
  44. package/dist/vascend/requirements.js +29 -29
  45. package/dist/vascend/reviewer.js +26 -26
  46. package/dist/vascend/room-memory.js +25 -25
  47. package/dist/vascend/runtime.js +44 -44
  48. package/dist/vascend/self-improve.js +39 -39
  49. package/dist/vascend/test-failure-memory.js +9 -9
  50. package/dist/vascend/vision.js +18 -18
  51. package/dist/vendor/computer-use/README.md +7 -7
  52. package/dist/vendor/computer-use/computer-desktop.js +211 -211
  53. package/dist/vendor/computer-use/computer-observe.js +315 -315
  54. package/dist/vendor/computer-use/computer-scripts.js +1230 -1230
  55. package/dist/vendor/computer-use/computer-tools.js +2136 -2136
  56. package/dist/vendor/computer-use/package.json +1 -1
  57. package/dist/vendor/computer-use/rac/atomic-input.js +91 -91
  58. package/dist/vendor/computer-use/rac/grammatica.js +101 -101
  59. package/dist/vendor/computer-use/rac/parser.js +185 -185
  60. package/dist/vendor/computer-use/rac/protocol.js +136 -136
  61. package/install.ps1 +374 -30
  62. package/install.sh +356 -43
  63. package/package.json +2 -2
  64. package/dist/core/kingdoms-mode-state.d.ts +0 -24
  65. package/dist/core/kingdoms-mode-state.js +0 -139
  66. package/dist/text-sanitize.js +0 -28
@@ -2,6 +2,8 @@ import { existsSync, readFileSync, realpathSync } from "node:fs";
2
2
  import { delimiter, dirname, join } from "node:path";
3
3
  import { homeDirFromEnv } from "./env-paths.js";
4
4
  import { runUpdateCommand } from "./update-process.js";
5
+ import { downloadVerifiedPackage } from "./signed-release.js";
6
+ import { finalizeCliInstallation } from "./cli-installation.js";
5
7
  const PACKAGE = "@vascend/talos";
6
8
  const REGISTRY = "https://registry.npmjs.org";
7
9
  export function managerCommand(manager, env) {
@@ -56,13 +58,13 @@ function npmPrefix(root) {
56
58
  return null;
57
59
  }
58
60
  }
59
- export function packageUpdateArgs(manager, version, prefix) {
60
- const target = `${PACKAGE}@${version}`;
61
+ export function packageUpdateArgs(manager, target, prefix) {
62
+ const security = ["--ignore-scripts", `--registry=${REGISTRY}`];
61
63
  if (manager === "npm")
62
- return ["install", "--global", target, `--registry=${REGISTRY}`, ...(prefix ? ["--prefix", prefix] : [])];
64
+ return ["install", "--global", target, ...security, "--no-audit", "--no-fund", ...(prefix ? ["--prefix", prefix] : [])];
63
65
  if (manager === "yarn")
64
- return ["global", "add", target, `--registry=${REGISTRY}`];
65
- return ["add", "--global", target, `--registry=${REGISTRY}`];
66
+ return ["global", "add", target, ...security];
67
+ return ["add", "--global", target, ...security];
66
68
  }
67
69
  export async function installPackageUpdate(update, options) {
68
70
  const { root, env } = options;
@@ -109,13 +111,24 @@ export async function installPackageUpdate(update, options) {
109
111
  }
110
112
  if (!matches)
111
113
  continue;
114
+ let artifact;
112
115
  try {
113
- await run(command(packageUpdateArgs(manager, update.version, prefix), true));
116
+ artifact = await (options.download ?? downloadVerifiedPackage)(update.version);
117
+ }
118
+ catch {
119
+ return { ok: false, reason: "verification", changed: false };
120
+ }
121
+ try {
122
+ await run(command(packageUpdateArgs(manager, artifact.path, prefix), true));
123
+ finalizeCliInstallation(root, update.version);
114
124
  return { ok: true };
115
125
  }
116
126
  catch {
117
127
  return { ok: false, reason: "install", changed: true };
118
128
  }
129
+ finally {
130
+ await artifact.dispose();
131
+ }
119
132
  }
120
133
  return { ok: false, reason: "unsupported", changed: false };
121
134
  }
@@ -1,7 +1,7 @@
1
1
  import { type TalosUpdate } from "./update-check.js";
2
2
  import { type UpdateRunner } from "./update-process.js";
3
3
  export declare const TALOS_INSTALL_ROOT: string;
4
- export type UpdateFailure = "unsupported" | "modified" | "diverged" | "version" | "download" | "build" | "install";
4
+ export type UpdateFailure = "unsupported" | "modified" | "diverged" | "version" | "download" | "build" | "install" | "verification";
5
5
  export type UpdateResult = {
6
6
  readonly ok: true;
7
7
  } | {
@@ -0,0 +1,33 @@
1
+ export declare const TALOS_DOWNLOAD_ORIGIN = "https://vascend.it";
2
+ export declare const STABLE_VERSION: RegExp;
3
+ export interface SignedRelease {
4
+ readonly name: "@vascend/talos";
5
+ readonly version: string;
6
+ readonly dist: {
7
+ readonly integrity: string;
8
+ readonly signatures: readonly {
9
+ keyid: string;
10
+ sig: string;
11
+ }[];
12
+ };
13
+ }
14
+ export declare function stableVersion(value: unknown): value is string;
15
+ export declare function verifyRelease(bytes: Uint8Array, registryKeys: unknown, options?: {
16
+ now?: number;
17
+ version?: string;
18
+ }): SignedRelease;
19
+ export declare function verifyPackage(bytes: Uint8Array, release: SignedRelease): void;
20
+ export declare function fetchReleaseBytes(url: string, limit: number, options?: {
21
+ fetch?: typeof fetch;
22
+ timeoutMs?: number;
23
+ }): Promise<Buffer>;
24
+ export declare function fetchSignedRelease(version?: string, options?: {
25
+ fetch?: typeof fetch;
26
+ timeoutMs?: number;
27
+ }): Promise<SignedRelease>;
28
+ export declare function downloadVerifiedPackage(version: string, options?: {
29
+ fetch?: typeof fetch;
30
+ }): Promise<{
31
+ path: string;
32
+ dispose: () => Promise<void>;
33
+ }>;
@@ -0,0 +1,121 @@
1
+ import { createHash, createPublicKey, verify } from "node:crypto";
2
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ export const TALOS_DOWNLOAD_ORIGIN = "https://vascend.it";
6
+ export const STABLE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
7
+ export function stableVersion(value) {
8
+ return typeof value === "string" && value.length <= 40 && STABLE_VERSION.exec(value)?.[0] === value;
9
+ }
10
+ export function verifyRelease(bytes, registryKeys, options = {}) {
11
+ if (bytes.length > 64 * 1024)
12
+ throw new Error("Release metadata is too large.");
13
+ const data = JSON.parse(Buffer.from(bytes).toString("utf8"));
14
+ if (!data || data.name !== "@vascend/talos" || !stableVersion(data.version)
15
+ || (options.version !== undefined && data.version !== options.version))
16
+ throw new Error("Unexpected release version.");
17
+ if (typeof data.dist?.integrity !== "string" || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(data.dist.integrity)
18
+ || data.dist.integrity.length !== 95 || !Array.isArray(data.dist.signatures) || data.dist.signatures.length > 10)
19
+ throw new Error("Invalid npm release signature.");
20
+ const keys = registryKeys?.keys;
21
+ if (!Array.isArray(keys) || keys.length > 30)
22
+ throw new Error("Invalid npm signing keys.");
23
+ const payload = Buffer.from(`${data.name}@${data.version}:${data.dist.integrity}`);
24
+ for (const signature of data.dist.signatures) {
25
+ if (typeof signature?.sig !== "string" || signature.sig.length > 256 || typeof signature.keyid !== "string")
26
+ continue;
27
+ for (const trusted of keys) {
28
+ if (!trusted || trusted.keyid !== signature.keyid || trusted.keytype !== "ecdsa-sha2-nistp256"
29
+ || trusted.scheme !== "ecdsa-sha2-nistp256" || typeof trusted.key !== "string" || trusted.key.length > 512
30
+ || (trusted.expires !== null && !(Date.parse(trusted.expires) > (options.now ?? Date.now()))))
31
+ continue;
32
+ try {
33
+ const key = createPublicKey({ key: Buffer.from(trusted.key, "base64"), format: "der", type: "spki" });
34
+ if (key.asymmetricKeyType === "ec" && key.asymmetricKeyDetails?.namedCurve === "prime256v1"
35
+ && verify("sha256", payload, key, Buffer.from(signature.sig, "base64")))
36
+ return data;
37
+ }
38
+ catch { /* Try another current registry key; never accept an unverified release. */ }
39
+ }
40
+ }
41
+ throw new Error("The npm registry signature is invalid.");
42
+ }
43
+ export function verifyPackage(bytes, release) {
44
+ if (`sha512-${createHash("sha512").update(bytes).digest("base64")}` !== release.dist.integrity) {
45
+ throw new Error("Release checksum does not match. Installation refused.");
46
+ }
47
+ }
48
+ export async function fetchReleaseBytes(url, limit, options = {}) {
49
+ const controller = new AbortController();
50
+ const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 20_000);
51
+ try {
52
+ const response = await (options.fetch ?? fetch)(url, { redirect: "error", signal: controller.signal, headers: { Accept: "application/octet-stream" } });
53
+ if (!response.ok || !response.body) {
54
+ await response.body?.cancel();
55
+ throw new Error("Release download is unavailable.");
56
+ }
57
+ if (Number(response.headers.get("content-length")) > limit) {
58
+ await response.body.cancel();
59
+ throw new Error("Release download is too large.");
60
+ }
61
+ const reader = response.body.getReader();
62
+ const chunks = [];
63
+ let length = 0;
64
+ try {
65
+ for (;;) {
66
+ const { done, value } = await reader.read();
67
+ if (done)
68
+ break;
69
+ length += value.length;
70
+ if (length > limit)
71
+ throw new Error("Release download is too large.");
72
+ chunks.push(value);
73
+ }
74
+ }
75
+ finally {
76
+ await reader.cancel();
77
+ }
78
+ return Buffer.concat(chunks);
79
+ }
80
+ finally {
81
+ clearTimeout(timer);
82
+ }
83
+ }
84
+ export async function fetchSignedRelease(version = "latest", options = {}) {
85
+ if (version !== "latest" && !stableVersion(version))
86
+ throw new Error("Invalid Talos version.");
87
+ // npm is the authority for both freshness and signatures. A stale/compromised
88
+ // mirror cannot hide a release by replaying older, correctly signed metadata.
89
+ const [metadata, keys] = await Promise.all([
90
+ fetchReleaseBytes(`https://registry.npmjs.org/@vascend%2ftalos/${version}`, 64 * 1024, options),
91
+ fetchReleaseBytes("https://registry.npmjs.org/-/npm/v1/keys", 64 * 1024, options),
92
+ ]);
93
+ return verifyRelease(metadata, JSON.parse(keys.toString("utf8")), version === "latest" ? {} : { version });
94
+ }
95
+ export async function downloadVerifiedPackage(version, options = {}) {
96
+ const release = await fetchSignedRelease(version, options);
97
+ const filename = `vascend-talos-${release.version}.tgz`;
98
+ let bytes;
99
+ try {
100
+ bytes = await fetchReleaseBytes(`${TALOS_DOWNLOAD_ORIGIN}/talos/releases/${release.version}/${filename}`, 32 * 1024 * 1024, options);
101
+ verifyPackage(bytes, release);
102
+ }
103
+ catch {
104
+ // This fixed npm URL also supports historical versions and a mirror that is
105
+ // still deploying. Never follow a tarball URL supplied by remote metadata.
106
+ bytes = await fetchReleaseBytes(`https://registry.npmjs.org/@vascend/talos/-/talos-${release.version}.tgz`, 32 * 1024 * 1024, options);
107
+ verifyPackage(bytes, release);
108
+ }
109
+ // npm receives only a private, verified local archive. No scripts or remote tarball URLs.
110
+ const directory = await mkdtemp(join(tmpdir(), "talos-verified-"));
111
+ const dispose = () => rm(directory, { recursive: true, force: true });
112
+ try {
113
+ const path = join(directory, filename);
114
+ await writeFile(path, bytes, { flag: "wx", mode: 0o600 });
115
+ return { path, dispose };
116
+ }
117
+ catch (error) {
118
+ await dispose();
119
+ throw error;
120
+ }
121
+ }
@@ -7,7 +7,7 @@ export interface TalosUpdate {
7
7
  readonly version: string;
8
8
  readonly tag: string;
9
9
  }
10
- export type UpdateSource = "github" | "npm";
10
+ export type UpdateSource = "github" | "npm" | "vascend";
11
11
  export declare function parseUpdateVersion(value: unknown): {
12
12
  version: string;
13
13
  core: readonly bigint[];
@@ -1,4 +1,5 @@
1
1
  import { runUpdateCommand } from "./update-process.js";
2
+ import { fetchSignedRelease } from "./signed-release.js";
2
3
  export const TALOS_REPOSITORY = "VascendTechnologies/vascend-talos";
3
4
  export const TALOS_RELEASES_URL = `https://github.com/${TALOS_REPOSITORY}/releases`;
4
5
  const RELEASE_ENDPOINT = `https://api.github.com/repos/${TALOS_REPOSITORY}/releases/latest`;
@@ -43,27 +44,29 @@ export function shouldCheckForUpdate(args, env, inputIsTTY, outputIsTTY) {
43
44
  export async function checkForTalosUpdate(options) {
44
45
  if (!parseUpdateVersion(options.currentVersion))
45
46
  return null;
47
+ if (options.source === "vascend" || options.source === "npm") {
48
+ try {
49
+ const release = await fetchSignedRelease("latest", { fetch: options.fetch, timeoutMs: options.timeoutMs ?? 2_500 });
50
+ return updateFromRelease(options.currentVersion, { tag_name: `v${release.version}`, draft: false, prerelease: false });
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
46
56
  const budget = options.timeoutMs ?? 2_500;
47
57
  const deadline = Date.now() + budget;
48
- const fromNpm = options.source === "npm";
49
- const token = fromNpm ? undefined : options.env.GH_TOKEN || options.env.GITHUB_TOKEN;
58
+ const token = options.env.GH_TOKEN || options.env.GITHUB_TOKEN;
50
59
  const controller = new AbortController();
51
- const timer = setTimeout(() => controller.abort(), Math.max(1, Math.floor(budget / (fromNpm ? 1 : 2))));
60
+ const timer = setTimeout(() => controller.abort(), Math.max(1, Math.floor(budget / 2)));
52
61
  try {
53
- const response = await (options.fetch ?? fetch)(fromNpm ? "https://registry.npmjs.org/@vascend%2ftalos/latest" : RELEASE_ENDPOINT, {
54
- headers: { Accept: fromNpm ? "application/json" : "application/vnd.github+json", "User-Agent": "vascend-talos-update",
62
+ const response = await (options.fetch ?? fetch)(RELEASE_ENDPOINT, {
63
+ headers: { Accept: "application/vnd.github+json", "User-Agent": "vascend-talos-update",
55
64
  ...(token ? { Authorization: `Bearer ${token}` } : {}) },
56
65
  redirect: "error",
57
66
  signal: controller.signal,
58
67
  });
59
68
  if (response.ok) {
60
- const data = await response.json();
61
- if (!fromNpm)
62
- return updateFromRelease(options.currentVersion, data);
63
- const pkg = data;
64
- return pkg?.name === "@vascend/talos"
65
- ? updateFromRelease(options.currentVersion, { tag_name: pkg.version, draft: false, prerelease: false })
66
- : null;
69
+ return updateFromRelease(options.currentVersion, await response.json());
67
70
  }
68
71
  await response.body?.cancel();
69
72
  }
@@ -71,8 +74,6 @@ export async function checkForTalosUpdate(options) {
71
74
  finally {
72
75
  clearTimeout(timer);
73
76
  }
74
- if (fromNpm)
75
- return null;
76
77
  const remaining = deadline - Date.now();
77
78
  if (remaining <= 0)
78
79
  return null;
@@ -363,19 +363,19 @@ export function renderVibeModeBlock(route) {
363
363
  const spec = MODES[route.resolved];
364
364
  const rules = spec.operatingRules.map((rule, index) => `@regola[${index + 1}]: "${escapeValue(rule)}"`);
365
365
  const gates = spec.completionGates.map((gate, index) => `@gate[${index + 1}]: "${escapeValue(gate)}"`);
366
- return `[VIBE_MODE]
367
- INDICE
368
- 1 = profilo
369
- 2 = processo
370
- 3 = gate
371
-
372
- DEFINIZIONI
373
- @profilo[attivo]: id=${spec.id}, nome="${escapeValue(spec.label)}", confidence=${route.confidence.toFixed(2)}
374
- @profilo[scopo]: "${escapeValue(spec.summary)}"
375
- @azione[prima]: "${escapeValue(spec.firstMove)}"
376
- ${rules.join("\n")}
377
- ${gates.join("\n")}
378
-
366
+ return `[VIBE_MODE]
367
+ INDICE
368
+ 1 = profilo
369
+ 2 = processo
370
+ 3 = gate
371
+
372
+ DEFINIZIONI
373
+ @profilo[attivo]: id=${spec.id}, nome="${escapeValue(spec.label)}", confidence=${route.confidence.toFixed(2)}
374
+ @profilo[scopo]: "${escapeValue(spec.summary)}"
375
+ @azione[prima]: "${escapeValue(spec.firstMove)}"
376
+ ${rules.join("\n")}
377
+ ${gates.join("\n")}
378
+
379
379
  OUTPUT: applica davvero processo e gate del profilo; non limitarti a citarne il nome.`;
380
380
  }
381
381
  function escapeValue(value) {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { join } from "node:path";
2
+ import { homedir } from "node:os";
3
+ import { downloadVerifiedPackage, fetchSignedRelease } from "../core/signed-release.js";
4
+ import { managerCommand, packageUpdateArgs } from "../core/package-update.js";
5
+ import { runUpdateCommand } from "../core/update-process.js";
6
+ import { finalizeCliInstallation, installationPrefix } from "../core/cli-installation.js";
7
+ async function install() {
8
+ if (Number(process.versions.node.split(".")[0]) < 20)
9
+ throw new Error("Talos requires Node.js 20 or newer, including npm: https://nodejs.org");
10
+ const npm = managerCommand("npm", process.env);
11
+ if (!npm)
12
+ throw new Error("npm is missing. Install Node.js with npm from https://nodejs.org");
13
+ const version = process.env.TALOS_VERSION || "latest";
14
+ const release = await fetchSignedRelease(version);
15
+ const prefix = installationPrefix(process.env.TALOS_INSTALL_DIR || (process.platform === "win32"
16
+ ? join(process.env.LOCALAPPDATA || join(homedir(), "AppData", "Local"), "Programs", "Talos") : join(homedir(), ".local")));
17
+ console.log(`Installing Talos ${release.version} in ${prefix}`);
18
+ const artifact = await downloadVerifiedPackage(release.version);
19
+ try {
20
+ await runUpdateCommand({ ...npm, args: [...npm.args, ...packageUpdateArgs("npm", artifact.path, prefix)],
21
+ cwd: homedir(), env: process.env, inherit: true, timeoutMs: 300_000 });
22
+ const entry = join(prefix, ...(process.platform === "win32" ? [] : ["lib"]), "node_modules", "@vascend", "talos", "dist", "index.js");
23
+ finalizeCliInstallation(join(prefix, ...(process.platform === "win32" ? [] : ["lib"]), "node_modules", "@vascend", "talos"), release.version);
24
+ await runUpdateCommand({ command: process.execPath, args: [entry, "--version"], cwd: homedir(), env: process.env, inherit: true, timeoutMs: 10_000 });
25
+ console.log("Talos installed. npm registry signature and SHA-512 verified.");
26
+ }
27
+ finally {
28
+ await artifact.dispose();
29
+ }
30
+ }
31
+ install().catch(error => { console.error(error instanceof Error ? error.message : "Installation failed."); process.exitCode = 1; });
@@ -0,0 +1,271 @@
1
+ // src/distribution/install.ts
2
+ import { join as join4 } from "node:path";
3
+ import { homedir } from "node:os";
4
+
5
+ // src/core/signed-release.ts
6
+ import { createHash, createPublicKey, verify } from "node:crypto";
7
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ var TALOS_DOWNLOAD_ORIGIN = "https://vascend.it";
11
+ var STABLE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
12
+ function stableVersion(value) {
13
+ return typeof value === "string" && value.length <= 40 && STABLE_VERSION.exec(value)?.[0] === value;
14
+ }
15
+ function verifyRelease(bytes, registryKeys, options = {}) {
16
+ if (bytes.length > 64 * 1024)
17
+ throw new Error("Release metadata is too large.");
18
+ const data = JSON.parse(Buffer.from(bytes).toString("utf8"));
19
+ if (!data || data.name !== "@vascend/talos" || !stableVersion(data.version) || options.version !== undefined && data.version !== options.version)
20
+ throw new Error("Unexpected release version.");
21
+ if (typeof data.dist?.integrity !== "string" || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(data.dist.integrity) || data.dist.integrity.length !== 95 || !Array.isArray(data.dist.signatures) || data.dist.signatures.length > 10)
22
+ throw new Error("Invalid npm release signature.");
23
+ const keys = registryKeys?.keys;
24
+ if (!Array.isArray(keys) || keys.length > 30)
25
+ throw new Error("Invalid npm signing keys.");
26
+ const payload = Buffer.from(`${data.name}@${data.version}:${data.dist.integrity}`);
27
+ for (const signature of data.dist.signatures) {
28
+ if (typeof signature?.sig !== "string" || signature.sig.length > 256 || typeof signature.keyid !== "string")
29
+ continue;
30
+ for (const trusted of keys) {
31
+ if (!trusted || trusted.keyid !== signature.keyid || trusted.keytype !== "ecdsa-sha2-nistp256" || trusted.scheme !== "ecdsa-sha2-nistp256" || typeof trusted.key !== "string" || trusted.key.length > 512 || trusted.expires !== null && !(Date.parse(trusted.expires) > (options.now ?? Date.now())))
32
+ continue;
33
+ try {
34
+ const key = createPublicKey({ key: Buffer.from(trusted.key, "base64"), format: "der", type: "spki" });
35
+ if (key.asymmetricKeyType === "ec" && key.asymmetricKeyDetails?.namedCurve === "prime256v1" && verify("sha256", payload, key, Buffer.from(signature.sig, "base64")))
36
+ return data;
37
+ } catch {}
38
+ }
39
+ }
40
+ throw new Error("The npm registry signature is invalid.");
41
+ }
42
+ function verifyPackage(bytes, release) {
43
+ if (`sha512-${createHash("sha512").update(bytes).digest("base64")}` !== release.dist.integrity) {
44
+ throw new Error("Release checksum does not match. Installation refused.");
45
+ }
46
+ }
47
+ async function fetchReleaseBytes(url, limit, options = {}) {
48
+ const controller = new AbortController;
49
+ const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 20000);
50
+ try {
51
+ const response = await (options.fetch ?? fetch)(url, { redirect: "error", signal: controller.signal, headers: { Accept: "application/octet-stream" } });
52
+ if (!response.ok || !response.body) {
53
+ await response.body?.cancel();
54
+ throw new Error("Release download is unavailable.");
55
+ }
56
+ if (Number(response.headers.get("content-length")) > limit) {
57
+ await response.body.cancel();
58
+ throw new Error("Release download is too large.");
59
+ }
60
+ const reader = response.body.getReader();
61
+ const chunks = [];
62
+ let length = 0;
63
+ try {
64
+ for (;; ) {
65
+ const { done, value } = await reader.read();
66
+ if (done)
67
+ break;
68
+ length += value.length;
69
+ if (length > limit)
70
+ throw new Error("Release download is too large.");
71
+ chunks.push(value);
72
+ }
73
+ } finally {
74
+ await reader.cancel();
75
+ }
76
+ return Buffer.concat(chunks);
77
+ } finally {
78
+ clearTimeout(timer);
79
+ }
80
+ }
81
+ async function fetchSignedRelease(version = "latest", options = {}) {
82
+ if (version !== "latest" && !stableVersion(version))
83
+ throw new Error("Invalid Talos version.");
84
+ const [metadata, keys] = await Promise.all([
85
+ fetchReleaseBytes(`https://registry.npmjs.org/@vascend%2ftalos/${version}`, 64 * 1024, options),
86
+ fetchReleaseBytes("https://registry.npmjs.org/-/npm/v1/keys", 64 * 1024, options)
87
+ ]);
88
+ return verifyRelease(metadata, JSON.parse(keys.toString("utf8")), version === "latest" ? {} : { version });
89
+ }
90
+ async function downloadVerifiedPackage(version, options = {}) {
91
+ const release = await fetchSignedRelease(version, options);
92
+ const filename = `vascend-talos-${release.version}.tgz`;
93
+ let bytes;
94
+ try {
95
+ bytes = await fetchReleaseBytes(`${TALOS_DOWNLOAD_ORIGIN}/talos/releases/${release.version}/${filename}`, 32 * 1024 * 1024, options);
96
+ verifyPackage(bytes, release);
97
+ } catch {
98
+ bytes = await fetchReleaseBytes(`https://registry.npmjs.org/@vascend/talos/-/talos-${release.version}.tgz`, 32 * 1024 * 1024, options);
99
+ verifyPackage(bytes, release);
100
+ }
101
+ const directory = await mkdtemp(join(tmpdir(), "talos-verified-"));
102
+ const dispose = () => rm(directory, { recursive: true, force: true });
103
+ try {
104
+ const path = join(directory, filename);
105
+ await writeFile(path, bytes, { flag: "wx", mode: 384 });
106
+ return { path, dispose };
107
+ } catch (error) {
108
+ await dispose();
109
+ throw error;
110
+ }
111
+ }
112
+
113
+ // src/core/package-update.ts
114
+ import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
115
+ import { delimiter, dirname as dirname2, join as join3 } from "node:path";
116
+
117
+ // src/core/update-process.ts
118
+ import { spawn } from "node:child_process";
119
+ var runUpdateCommand = (command) => new Promise((resolve, reject) => {
120
+ const child = spawn(command.command, [...command.args], {
121
+ cwd: command.cwd,
122
+ env: command.env,
123
+ shell: false,
124
+ windowsHide: true,
125
+ stdio: command.inherit ? "inherit" : ["ignore", "pipe", "pipe"],
126
+ timeout: command.timeoutMs
127
+ });
128
+ let output = "";
129
+ let failed = false;
130
+ const collect = (data) => {
131
+ output += data.toString();
132
+ if (output.length > 2000000) {
133
+ failed = true;
134
+ child.kill();
135
+ reject(new Error("Command output exceeded limit"));
136
+ }
137
+ };
138
+ child.stdout?.on("data", collect);
139
+ child.stderr?.resume();
140
+ child.on("error", reject);
141
+ child.on("close", (code, signal) => {
142
+ if (failed)
143
+ return;
144
+ if (code === 0 && !signal)
145
+ resolve(output.trim());
146
+ else
147
+ reject(new Error(`${command.command} exited with ${signal ?? code}`));
148
+ });
149
+ });
150
+
151
+ // src/core/cli-installation.ts
152
+ import { existsSync, lstatSync, readFileSync, realpathSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
153
+ import { dirname, join as join2, resolve } from "node:path";
154
+ function installationPrefix(value) {
155
+ const prefix = resolve(value);
156
+ if (/[\x00-\x1f\x7f]/.test(prefix) || prefix.includes(process.platform === "win32" ? ";" : ":")) {
157
+ throw new Error("The installation directory cannot be represented safely in PATH.");
158
+ }
159
+ return prefix;
160
+ }
161
+ function windowsCliShim() {
162
+ return `@echo off\r
163
+ "%~dp0.talos-runtime\\node.exe" "%~dp0node_modules\\@vascend\\talos\\dist\\index.js" %*\r
164
+ `;
165
+ }
166
+ function finalizeCliInstallation(root, version) {
167
+ const pkg = JSON.parse(readFileSync(join2(root, "package.json"), "utf8"));
168
+ if (pkg.name !== "@vascend/talos" || pkg.version !== version)
169
+ throw new Error("Installed Talos version differs from the requested release.");
170
+ if (process.platform !== "win32")
171
+ return;
172
+ const modules = dirname(dirname(root));
173
+ if (dirname(root).split(/[\\/]/).pop() !== "@vascend" || modules.split(/[\\/]/).pop() !== "node_modules")
174
+ return;
175
+ const prefix = dirname(modules);
176
+ const belongsToTalos = (file) => {
177
+ if (!existsSync(file))
178
+ return false;
179
+ const info = lstatSync(file);
180
+ return info.isFile() && !info.isSymbolicLink() && info.size < 16384 && /node_modules[\\/]@vascend[\\/]talos[\\/]dist[\\/]index\.js/i.test(readFileSync(file, "utf8"));
181
+ };
182
+ for (const alias of ["talos", "horizon"]) {
183
+ const cmd = join2(prefix, `${alias}.cmd`);
184
+ if (!belongsToTalos(cmd))
185
+ continue;
186
+ if (!process.versions.bun) {
187
+ const runtime = join2(prefix, ".talos-runtime");
188
+ if (existsSync(runtime)) {
189
+ if (!lstatSync(runtime).isSymbolicLink())
190
+ throw new Error("The Talos runtime link is occupied by a regular directory.");
191
+ if (realpathSync(runtime).toLowerCase() !== realpathSync(dirname(process.execPath)).toLowerCase())
192
+ unlinkSync(runtime);
193
+ }
194
+ if (!existsSync(runtime))
195
+ symlinkSync(dirname(process.execPath), runtime, "junction");
196
+ writeFileSync(cmd, windowsCliShim());
197
+ }
198
+ const ps1 = join2(prefix, `${alias}.ps1`);
199
+ if (belongsToTalos(ps1))
200
+ unlinkSync(ps1);
201
+ }
202
+ }
203
+
204
+ // src/core/package-update.ts
205
+ var REGISTRY = "https://registry.npmjs.org";
206
+ function managerCommand(manager, env) {
207
+ if (process.platform !== "win32")
208
+ return { command: manager, args: [] };
209
+ const path = Object.entries(env).find(([key]) => key.toLowerCase() === "path")?.[1] ?? "";
210
+ const directories = path.split(delimiter).filter(Boolean).map((directory) => directory.replace(/^"|"$/g, ""));
211
+ const node = process.versions.bun ? directories.map((directory) => join3(directory, "node.exe")).find((file) => existsSync2(file)) : process.execPath;
212
+ for (const directory of directories) {
213
+ const exe = join3(directory, `${manager}.exe`);
214
+ if (existsSync2(exe))
215
+ return { command: exe, args: [] };
216
+ const scripts = {
217
+ npm: ["npm/bin/npm-cli.js"],
218
+ pnpm: ["pnpm/bin/pnpm.cjs", "corepack/dist/pnpm.js"],
219
+ yarn: ["yarn/bin/yarn.js", "corepack/dist/yarn.js"]
220
+ };
221
+ if (manager !== "bun")
222
+ for (const script of scripts[manager]) {
223
+ const file = join3(directory, "node_modules", script);
224
+ if (node && existsSync2(file))
225
+ return { command: node, args: [file] };
226
+ }
227
+ }
228
+ return null;
229
+ }
230
+ function packageUpdateArgs(manager, target, prefix) {
231
+ const security = ["--ignore-scripts", `--registry=${REGISTRY}`];
232
+ if (manager === "npm")
233
+ return ["install", "--global", target, ...security, "--no-audit", "--no-fund", ...prefix ? ["--prefix", prefix] : []];
234
+ if (manager === "yarn")
235
+ return ["global", "add", target, ...security];
236
+ return ["add", "--global", target, ...security];
237
+ }
238
+
239
+ // src/distribution/install.ts
240
+ async function install() {
241
+ if (Number(process.versions.node.split(".")[0]) < 20)
242
+ throw new Error("Talos requires Node.js 20 or newer, including npm: https://nodejs.org");
243
+ const npm = managerCommand("npm", process.env);
244
+ if (!npm)
245
+ throw new Error("npm is missing. Install Node.js with npm from https://nodejs.org");
246
+ const version = process.env.TALOS_VERSION || "latest";
247
+ const release = await fetchSignedRelease(version);
248
+ const prefix = installationPrefix(process.env.TALOS_INSTALL_DIR || (process.platform === "win32" ? join4(process.env.LOCALAPPDATA || join4(homedir(), "AppData", "Local"), "Programs", "Talos") : join4(homedir(), ".local")));
249
+ console.log(`Installing Talos ${release.version} in ${prefix}`);
250
+ const artifact = await downloadVerifiedPackage(release.version);
251
+ try {
252
+ await runUpdateCommand({
253
+ ...npm,
254
+ args: [...npm.args, ...packageUpdateArgs("npm", artifact.path, prefix)],
255
+ cwd: homedir(),
256
+ env: process.env,
257
+ inherit: true,
258
+ timeoutMs: 300000
259
+ });
260
+ const entry = join4(prefix, ...process.platform === "win32" ? [] : ["lib"], "node_modules", "@vascend", "talos", "dist", "index.js");
261
+ finalizeCliInstallation(join4(prefix, ...process.platform === "win32" ? [] : ["lib"], "node_modules", "@vascend", "talos"), release.version);
262
+ await runUpdateCommand({ command: process.execPath, args: [entry, "--version"], cwd: homedir(), env: process.env, inherit: true, timeoutMs: 1e4 });
263
+ console.log("Talos installed. npm registry signature and SHA-512 verified.");
264
+ } finally {
265
+ await artifact.dispose();
266
+ }
267
+ }
268
+ install().catch((error) => {
269
+ console.error(error instanceof Error ? error.message : "Installation failed.");
270
+ process.exitCode = 1;
271
+ });
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "24.21.0",
3
+ "source": "https://nodejs.org/dist/v24.21.0/SHASUMS256.txt",
4
+ "sha256": {
5
+ "darwin-arm64": "bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057",
6
+ "darwin-x64": "1462cb3b3046b815cf8ea436d3da450ec1a9f11dac7e5a46b0ada5305d7e8097",
7
+ "linux-arm64": "724282c3b43aec998aa9527380465b45d229e021b58035f5f4f63095eabfe5d5",
8
+ "linux-x64": "6e1db87ef58b8819e5d5402eff1536491b18edd8eb7bee5ef7897876e88dc5ff",
9
+ "linux-x64-musl": "3d63405fc65a0d2d2976c1f0bc2fd27bb0bd07212469e705aac3f03ae5ab4c9c",
10
+ "win-arm64": "8779b1bde1d39f8d420e3b57aa657b39891af434d3de44a919044cec06785921",
11
+ "win-x64": "158f7685b44de51f6c0df1d153526cbcd3e1bc739a8dfc607721cef75de9e541"
12
+ }
13
+ }