@amalgm/shell 0.1.112 → 0.1.114
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.
- package/PURPOSE.md +75 -6
- package/README.md +36 -7
- package/dist/authorization/device-key.d.ts +19 -0
- package/dist/authorization/device-key.js +81 -0
- package/dist/authorization/device-key.js.map +1 -0
- package/dist/authorization/index.d.ts +65 -0
- package/dist/authorization/index.js +63 -0
- package/dist/authorization/index.js.map +1 -0
- package/dist/authorization/issuer.d.ts +11 -0
- package/dist/authorization/issuer.js +76 -0
- package/dist/authorization/issuer.js.map +1 -0
- package/dist/authorization/key-store.d.ts +15 -0
- package/dist/authorization/key-store.js +148 -0
- package/dist/authorization/key-store.js.map +1 -0
- package/dist/authorization/recovery-client.d.ts +19 -0
- package/dist/authorization/recovery-client.js +102 -0
- package/dist/authorization/recovery-client.js.map +1 -0
- package/dist/authorization/recovery-host.d.ts +28 -0
- package/dist/authorization/recovery-host.js +93 -0
- package/dist/authorization/recovery-host.js.map +1 -0
- package/dist/authorization/recovery.d.ts +13 -0
- package/dist/authorization/recovery.js +27 -0
- package/dist/authorization/recovery.js.map +1 -0
- package/dist/authorization/secret-process.d.ts +2 -0
- package/dist/authorization/secret-process.js +32 -0
- package/dist/authorization/secret-process.js.map +1 -0
- package/dist/boot.d.ts +19 -11
- package/dist/boot.js +34 -25
- package/dist/boot.js.map +1 -1
- package/dist/builtin-agents.d.ts +3 -0
- package/dist/builtin-agents.js +15 -0
- package/dist/builtin-agents.js.map +1 -0
- package/dist/chat-composition.d.ts +19 -0
- package/dist/chat-composition.js +83 -0
- package/dist/chat-composition.js.map +1 -0
- package/dist/chat-host.d.ts +9 -25
- package/dist/chat-host.js +12 -158
- package/dist/chat-host.js.map +1 -1
- package/dist/cli.js +21 -13
- package/dist/cli.js.map +1 -1
- package/dist/content-cache-host.d.ts +7 -0
- package/dist/content-cache-host.js +23 -1
- package/dist/content-cache-host.js.map +1 -1
- package/dist/credentials-host.d.ts +1 -5
- package/dist/credentials-host.js +1 -5
- package/dist/credentials-host.js.map +1 -1
- package/dist/entity-record-store.d.ts +2 -0
- package/dist/entity-record-store.js +15 -0
- package/dist/entity-record-store.js.map +1 -1
- package/dist/forward.js +1 -1
- package/dist/forward.js.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/machine-host.d.ts +29 -0
- package/dist/machine-host.js +240 -10
- package/dist/machine-host.js.map +1 -1
- package/dist/parity.d.ts +1 -5
- package/dist/parity.js +4 -41
- package/dist/parity.js.map +1 -1
- package/dist/registry-graph.d.ts +61 -0
- package/dist/registry-graph.js +156 -0
- package/dist/registry-graph.js.map +1 -0
- package/dist/release-catalog.js +7 -4
- package/dist/release-catalog.js.map +1 -1
- package/dist/runtime-command-host.d.ts +11 -0
- package/dist/runtime-command-host.js +65 -0
- package/dist/runtime-command-host.js.map +1 -1
- package/dist/user-ground-host.d.ts +13 -0
- package/dist/user-ground-host.js +449 -205
- package/dist/user-ground-host.js.map +1 -1
- package/dist/wire-client.js +1 -1
- package/package.json +15 -6
- package/dist/chat-payloads-host.d.ts +0 -8
- package/dist/chat-payloads-host.js +0 -76
- package/dist/chat-payloads-host.js.map +0 -1
- package/dist/chat-relay-host.d.ts +0 -24
- package/dist/chat-relay-host.js +0 -90
- package/dist/chat-relay-host.js.map +0 -1
- package/dist/drivers/claude-code.d.ts +0 -27
- package/dist/drivers/claude-code.js +0 -72
- package/dist/drivers/claude-code.js.map +0 -1
- package/dist/paged-mutation-submit.d.ts +0 -28
- package/dist/paged-mutation-submit.js +0 -61
- package/dist/paged-mutation-submit.js.map +0 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { atomicJson } from "../filesystem.js";
|
|
5
|
+
import { runSecretProcess } from "./secret-process.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const KEYCHAIN_SERVICE = "com.amalgm.shell.authorization";
|
|
8
|
+
export function createDeviceKeyStore(input) {
|
|
9
|
+
const file = fileStore(input.file);
|
|
10
|
+
const platform = input.platform ?? process.platform;
|
|
11
|
+
if (platform === "darwin")
|
|
12
|
+
return preferredStore(keychainStore(input.account), file);
|
|
13
|
+
if (platform === "win32")
|
|
14
|
+
return preferredStore(dpapiStore(input.file), file);
|
|
15
|
+
return file;
|
|
16
|
+
}
|
|
17
|
+
function fileStore(file) {
|
|
18
|
+
return {
|
|
19
|
+
load: async () => readStoredJson(file),
|
|
20
|
+
save: async (key) => atomicJson(file, key),
|
|
21
|
+
remove: async () => rmSync(file, { force: true }),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function keychainStore(account) {
|
|
25
|
+
return {
|
|
26
|
+
load: async () => {
|
|
27
|
+
try {
|
|
28
|
+
const { stdout } = await execFileAsync("/usr/bin/security", [
|
|
29
|
+
"find-generic-password", "-a", account, "-s", KEYCHAIN_SERVICE, "-w",
|
|
30
|
+
], { encoding: "utf8", maxBuffer: 64 * 1024 });
|
|
31
|
+
return JSON.parse(stdout.trim());
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
const message = errorText(error);
|
|
35
|
+
if (message.includes("could not be found") || message.includes("SecKeychainSearchCopyNext: -25300")) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
save: async (key) => {
|
|
42
|
+
await runSecretProcess("/usr/bin/security", [
|
|
43
|
+
"add-generic-password", "-U", "-a", account, "-s", KEYCHAIN_SERVICE,
|
|
44
|
+
"-w",
|
|
45
|
+
], `${JSON.stringify(key)}\n`);
|
|
46
|
+
},
|
|
47
|
+
remove: async () => {
|
|
48
|
+
try {
|
|
49
|
+
await execFileAsync("/usr/bin/security", [
|
|
50
|
+
"delete-generic-password", "-a", account, "-s", KEYCHAIN_SERVICE,
|
|
51
|
+
], { encoding: "utf8", maxBuffer: 64 * 1024 });
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const message = errorText(error);
|
|
55
|
+
if (!message.includes("could not be found") && !message.includes("SecKeychainSearchCopyNext: -25300")) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function dpapiStore(file) {
|
|
63
|
+
const protect = [
|
|
64
|
+
"$ErrorActionPreference='Stop'",
|
|
65
|
+
"$p=[Text.Encoding]::UTF8.GetBytes([Console]::In.ReadToEnd())",
|
|
66
|
+
"$b=[Security.Cryptography.ProtectedData]::Protect($p,$null,[Security.Cryptography.DataProtectionScope]::CurrentUser)",
|
|
67
|
+
"[Console]::Out.Write([Convert]::ToBase64String($b))",
|
|
68
|
+
].join(";");
|
|
69
|
+
const unprotect = [
|
|
70
|
+
"$ErrorActionPreference='Stop'",
|
|
71
|
+
"$b=[Convert]::FromBase64String([Console]::In.ReadToEnd().Trim())",
|
|
72
|
+
"$p=[Security.Cryptography.ProtectedData]::Unprotect($b,$null,[Security.Cryptography.DataProtectionScope]::CurrentUser)",
|
|
73
|
+
"[Console]::Out.Write([Text.Encoding]::UTF8.GetString($p))",
|
|
74
|
+
].join(";");
|
|
75
|
+
return {
|
|
76
|
+
load: async () => {
|
|
77
|
+
const stored = readStoredJson(file);
|
|
78
|
+
if (stored?.storage !== "windows-dpapi" || !stored.ciphertext)
|
|
79
|
+
return null;
|
|
80
|
+
const value = await runSecretProcess("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", unprotect], stored.ciphertext);
|
|
81
|
+
return JSON.parse(value);
|
|
82
|
+
},
|
|
83
|
+
save: async (key) => {
|
|
84
|
+
const ciphertext = await runSecretProcess("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", protect], JSON.stringify(key));
|
|
85
|
+
atomicJson(file, { storage: "windows-dpapi", ciphertext });
|
|
86
|
+
},
|
|
87
|
+
remove: async () => rmSync(file, { force: true }),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function preferredStore(primary, fallback) {
|
|
91
|
+
let selected = null;
|
|
92
|
+
return {
|
|
93
|
+
load: async () => {
|
|
94
|
+
const primaryValue = await primary.load();
|
|
95
|
+
if (primaryValue) {
|
|
96
|
+
selected = "primary";
|
|
97
|
+
return primaryValue;
|
|
98
|
+
}
|
|
99
|
+
const fallbackValue = await fallback.load();
|
|
100
|
+
if (fallbackValue)
|
|
101
|
+
selected = "fallback";
|
|
102
|
+
return fallbackValue;
|
|
103
|
+
},
|
|
104
|
+
save: async (key) => {
|
|
105
|
+
if (selected === "primary")
|
|
106
|
+
return primary.save(key);
|
|
107
|
+
if (selected === "fallback")
|
|
108
|
+
return fallback.save(key);
|
|
109
|
+
try {
|
|
110
|
+
await primary.save(key);
|
|
111
|
+
selected = "primary";
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
await fallback.save(key);
|
|
115
|
+
selected = "fallback";
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
remove: async () => {
|
|
119
|
+
const errors = [];
|
|
120
|
+
await primary.remove().catch((error) => errors.push(error));
|
|
121
|
+
await fallback.remove().catch((error) => errors.push(error));
|
|
122
|
+
if (errors.length > 0)
|
|
123
|
+
throw errors[0];
|
|
124
|
+
selected = null;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function readStoredJson(file) {
|
|
129
|
+
try {
|
|
130
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
throw Object.assign(new Error("Stored authorization key is unreadable or corrupt"), {
|
|
137
|
+
code: "authorization_key_corrupt",
|
|
138
|
+
cause: error,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function errorText(error) {
|
|
143
|
+
if (!error || typeof error !== "object")
|
|
144
|
+
return String(error);
|
|
145
|
+
const value = error;
|
|
146
|
+
return `${String(value.message ?? "")} ${String(value.stderr ?? "")}`;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=key-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-store.js","sourceRoot":"","sources":["../../src/authorization/key-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DeviceKey } from "./device-key.js";
|
|
2
|
+
type Dict = Record<string, unknown>;
|
|
3
|
+
export interface AuthorizationRecoveryClientOptions {
|
|
4
|
+
readonly appUrl: string;
|
|
5
|
+
readonly body: Dict;
|
|
6
|
+
readonly signer: DeviceKey;
|
|
7
|
+
readonly setupCode?: string;
|
|
8
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
9
|
+
readonly nowMs?: () => number;
|
|
10
|
+
readonly sleep?: (milliseconds: number) => Promise<void>;
|
|
11
|
+
readonly showSetup?: (input: {
|
|
12
|
+
readonly verificationUrl: string;
|
|
13
|
+
readonly setupCode: string;
|
|
14
|
+
readonly keyThumbprint: string;
|
|
15
|
+
}) => Promise<void> | void;
|
|
16
|
+
}
|
|
17
|
+
/** Browser-approved recovery with proof of the staged candidate private key. */
|
|
18
|
+
export declare function requestAuthorizationKeyRecovery(options: AuthorizationRecoveryClientOptions): Promise<Dict>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { assertSetupCode } from "@amalgm/core/identity";
|
|
2
|
+
/** Browser-approved recovery with proof of the staged candidate private key. */
|
|
3
|
+
export async function requestAuthorizationKeyRecovery(options) {
|
|
4
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
5
|
+
const nowMs = options.nowMs ?? (() => Date.now());
|
|
6
|
+
const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
7
|
+
const origin = recoveryOrigin(options.appUrl);
|
|
8
|
+
const registrationToken = options.setupCode
|
|
9
|
+
? text((await postJson(fetchImpl, origin, "/api/auth/computer-setup/redeem", {
|
|
10
|
+
...options.body,
|
|
11
|
+
setup_code: assertSetupCode(options.setupCode),
|
|
12
|
+
})).computer_registration_token)
|
|
13
|
+
: await waitForApproval(options, fetchImpl, origin, nowMs, sleep);
|
|
14
|
+
if (!registrationToken)
|
|
15
|
+
throw recoveryError("authorization_recovery_token_missing");
|
|
16
|
+
const registerUrl = new URL("/api/computers/register", origin).toString();
|
|
17
|
+
const proof = await options.signer.signProof({ method: "POST", url: registerUrl });
|
|
18
|
+
return postJson(fetchImpl, origin, "/api/computers/register", options.body, {
|
|
19
|
+
authorization: `Bearer ${registrationToken}`,
|
|
20
|
+
dpop: proof,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async function waitForApproval(options, fetchImpl, origin, nowMs, sleep) {
|
|
24
|
+
const started = await postJson(fetchImpl, origin, "/api/auth/computer-setup/cli/start", options.body);
|
|
25
|
+
const deviceCode = text(started.device_code);
|
|
26
|
+
const setupCode = text(started.setup_code);
|
|
27
|
+
const verificationUrl = text(started.verification_url);
|
|
28
|
+
if (!deviceCode || !setupCode || !verificationUrl) {
|
|
29
|
+
throw recoveryError("authorization_recovery_start_invalid");
|
|
30
|
+
}
|
|
31
|
+
await options.showSetup?.({
|
|
32
|
+
verificationUrl,
|
|
33
|
+
setupCode,
|
|
34
|
+
keyThumbprint: options.signer.keyThumbprint,
|
|
35
|
+
});
|
|
36
|
+
const deadline = nowMs() + Math.max(60, number(started.expires_in, 180)) * 1000;
|
|
37
|
+
let intervalMs = Math.max(1, number(started.interval, 2)) * 1000;
|
|
38
|
+
while (nowMs() < deadline) {
|
|
39
|
+
const polled = await postJson(fetchImpl, origin, "/api/auth/computer-setup/cli/poll", {
|
|
40
|
+
...options.body,
|
|
41
|
+
device_code: deviceCode,
|
|
42
|
+
});
|
|
43
|
+
const status = text(polled.status);
|
|
44
|
+
if (status === "approved")
|
|
45
|
+
return text(polled.computer_registration_token);
|
|
46
|
+
if (status !== "pending")
|
|
47
|
+
throw recoveryError(`authorization_recovery_${status || "invalid"}`);
|
|
48
|
+
intervalMs = Math.max(1, number(polled.interval, intervalMs / 1000)) * 1000;
|
|
49
|
+
await sleep(intervalMs);
|
|
50
|
+
}
|
|
51
|
+
throw recoveryError("authorization_recovery_timeout");
|
|
52
|
+
}
|
|
53
|
+
function recoveryOrigin(value) {
|
|
54
|
+
const url = new URL(value);
|
|
55
|
+
const loopback = url.hostname === "127.0.0.1" || url.hostname === "localhost" || url.hostname === "::1";
|
|
56
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
|
|
57
|
+
throw recoveryError("authorization_recovery_origin_insecure");
|
|
58
|
+
}
|
|
59
|
+
url.pathname = "/";
|
|
60
|
+
url.search = "";
|
|
61
|
+
url.hash = "";
|
|
62
|
+
return url.toString();
|
|
63
|
+
}
|
|
64
|
+
async function postJson(fetchImpl, origin, path, body, headers = {}) {
|
|
65
|
+
const response = await fetchImpl(new URL(path, origin), {
|
|
66
|
+
method: "POST",
|
|
67
|
+
redirect: "error",
|
|
68
|
+
headers: { accept: "application/json", "content-type": "application/json", ...headers },
|
|
69
|
+
body: JSON.stringify(body),
|
|
70
|
+
});
|
|
71
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
72
|
+
if (bytes.byteLength > 64 * 1024)
|
|
73
|
+
throw recoveryError("authorization_recovery_response_too_large");
|
|
74
|
+
let payload = {};
|
|
75
|
+
try {
|
|
76
|
+
payload = bytes.byteLength > 0
|
|
77
|
+
? JSON.parse(Buffer.from(bytes).toString("utf8"))
|
|
78
|
+
: {};
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
throw recoveryError("authorization_recovery_response_invalid");
|
|
82
|
+
}
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
throw Object.assign(new Error(`${response.status} ${text(payload.error) || response.statusText}`), {
|
|
85
|
+
code: `http_${response.status}`,
|
|
86
|
+
status: response.status,
|
|
87
|
+
payload,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return payload;
|
|
91
|
+
}
|
|
92
|
+
function number(value, fallback) {
|
|
93
|
+
const parsed = Number(value);
|
|
94
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
95
|
+
}
|
|
96
|
+
function text(value) {
|
|
97
|
+
return typeof value === "string" ? value.trim() : "";
|
|
98
|
+
}
|
|
99
|
+
function recoveryError(code) {
|
|
100
|
+
return Object.assign(new Error(code), { code });
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=recovery-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery-client.js","sourceRoot":"","sources":["../../src/authorization/recovery-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type StoredMachine } from "@amalgm/core/identity";
|
|
2
|
+
import type { DeviceKeyStore } from "./key-store.js";
|
|
3
|
+
export interface AuthorizationKeyRecoveryResult {
|
|
4
|
+
readonly status: "healthy" | "recovered";
|
|
5
|
+
readonly deviceId: string;
|
|
6
|
+
readonly computerId: string;
|
|
7
|
+
readonly keyThumbprint: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function recoverMachineAuthorization(options: Readonly<{
|
|
10
|
+
machine: StoredMachine;
|
|
11
|
+
appUrl: string;
|
|
12
|
+
runtimeVersion: string;
|
|
13
|
+
runtimeLabel: string;
|
|
14
|
+
branch: string;
|
|
15
|
+
fallbackName: string;
|
|
16
|
+
fallbackPlatform: string;
|
|
17
|
+
stateDir: string;
|
|
18
|
+
platform?: NodeJS.Platform;
|
|
19
|
+
keyStore?: DeviceKeyStore;
|
|
20
|
+
recoveryKeyStore?: DeviceKeyStore;
|
|
21
|
+
setupCode?: string;
|
|
22
|
+
showSetup?: (input: {
|
|
23
|
+
verificationUrl: string;
|
|
24
|
+
setupCode: string;
|
|
25
|
+
keyThumbprint?: string;
|
|
26
|
+
}) => Promise<void> | void;
|
|
27
|
+
save(machine: StoredMachine): Promise<void>;
|
|
28
|
+
}>): Promise<AuthorizationKeyRecoveryResult>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { authFromRecord, mergeRecordAuth, publicComputerRecord, } from "@amalgm/core/identity";
|
|
2
|
+
import { deviceKeyFromStored, installationDeviceKeyStores, promoteInstallationDeviceKeyRecovery, requireInstallationDeviceKey, stageInstallationDeviceKeyRecovery, } from "./index.js";
|
|
3
|
+
import { requestAuthorizationKeyRecovery } from "./recovery-client.js";
|
|
4
|
+
export async function recoverMachineAuthorization(options) {
|
|
5
|
+
const record = mergeRecordAuth(options.machine.publicRecord, options.machine.secretAuth) ?? {};
|
|
6
|
+
const deviceId = text(record.device_id);
|
|
7
|
+
const computerId = text(record.computer_id);
|
|
8
|
+
const userId = text(record.user_id);
|
|
9
|
+
const expectedLocalKey = text(record.authorization_key_thumbprint);
|
|
10
|
+
if (!deviceId || !computerId || !userId)
|
|
11
|
+
throw recoveryError("authorization_recovery_identity_incomplete");
|
|
12
|
+
const keyOptions = {
|
|
13
|
+
stateDir: options.stateDir,
|
|
14
|
+
deviceId,
|
|
15
|
+
...(options.platform ? { platform: options.platform } : {}),
|
|
16
|
+
...(options.keyStore ? { keyStore: options.keyStore } : {}),
|
|
17
|
+
...(options.recoveryKeyStore ? { recoveryKeyStore: options.recoveryKeyStore } : {}),
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
const active = await requireInstallationDeviceKey(keyOptions);
|
|
21
|
+
if (!expectedLocalKey || active.keyThumbprint === expectedLocalKey) {
|
|
22
|
+
return result("healthy", deviceId, computerId, active.keyThumbprint);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
const code = text(error?.code);
|
|
27
|
+
if (code !== "authorization_key_missing" && code !== "authorization_key_corrupt")
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
const pending = await installationDeviceKeyStores(keyOptions).pending.load();
|
|
31
|
+
if (pending) {
|
|
32
|
+
const staged = deviceKeyFromStored(pending, {});
|
|
33
|
+
if (expectedLocalKey && staged.keyThumbprint === expectedLocalKey) {
|
|
34
|
+
await promoteInstallationDeviceKeyRecovery({
|
|
35
|
+
...keyOptions,
|
|
36
|
+
acceptedKeyThumbprint: staged.keyThumbprint,
|
|
37
|
+
});
|
|
38
|
+
return result("recovered", deviceId, computerId, staged.keyThumbprint);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const candidate = await stageInstallationDeviceKeyRecovery(keyOptions);
|
|
42
|
+
const response = await requestAuthorizationKeyRecovery({
|
|
43
|
+
appUrl: options.appUrl,
|
|
44
|
+
body: recoveryBody(options, record, deviceId, candidate.keyThumbprint),
|
|
45
|
+
signer: candidate,
|
|
46
|
+
...(options.setupCode ? { setupCode: options.setupCode } : {}),
|
|
47
|
+
...(options.showSetup ? { showSetup: options.showSetup } : {}),
|
|
48
|
+
});
|
|
49
|
+
assertRecoveryResponse(response, { deviceId, computerId, userId, key: candidate.keyThumbprint });
|
|
50
|
+
const merged = { ...record, ...response, app_url: options.appUrl };
|
|
51
|
+
await options.save({
|
|
52
|
+
publicRecord: publicComputerRecord(merged),
|
|
53
|
+
secretAuth: authFromRecord(merged, options.machine.secretAuth, { nowIso: () => new Date().toISOString() }),
|
|
54
|
+
installation: options.machine.installation,
|
|
55
|
+
ready: options.machine.ready,
|
|
56
|
+
});
|
|
57
|
+
await promoteInstallationDeviceKeyRecovery({
|
|
58
|
+
...keyOptions,
|
|
59
|
+
acceptedKeyThumbprint: candidate.keyThumbprint,
|
|
60
|
+
});
|
|
61
|
+
return result("recovered", deviceId, computerId, candidate.keyThumbprint);
|
|
62
|
+
}
|
|
63
|
+
function recoveryBody(options, record, deviceId, key) {
|
|
64
|
+
return {
|
|
65
|
+
device_id: deviceId,
|
|
66
|
+
name: text(record.name) || options.fallbackName,
|
|
67
|
+
platform: text(record.platform) || options.fallbackPlatform,
|
|
68
|
+
app_version: options.runtimeVersion,
|
|
69
|
+
runtime_label: options.runtimeLabel,
|
|
70
|
+
branch: options.branch,
|
|
71
|
+
purpose: "authorization_key_recovery",
|
|
72
|
+
authorization_key_thumbprint: key,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function assertRecoveryResponse(response, expected) {
|
|
76
|
+
if (text(response.device_id) !== expected.deviceId
|
|
77
|
+
|| text(response.computer_id) !== expected.computerId
|
|
78
|
+
|| text(response.user_id) !== expected.userId
|
|
79
|
+
|| text(response.authorization_key_thumbprint) !== expected.key
|
|
80
|
+
|| !text(response.computer_auth_token)
|
|
81
|
+
|| !text(response.tunnel_token))
|
|
82
|
+
throw recoveryError("authorization_recovery_response_mismatch");
|
|
83
|
+
}
|
|
84
|
+
function result(status, deviceId, computerId, keyThumbprint) {
|
|
85
|
+
return Object.freeze({ status, deviceId, computerId, keyThumbprint });
|
|
86
|
+
}
|
|
87
|
+
function recoveryError(code) {
|
|
88
|
+
return Object.assign(new Error(code), { code });
|
|
89
|
+
}
|
|
90
|
+
function text(value) {
|
|
91
|
+
return typeof value === "string" ? value.trim() : "";
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=recovery-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery-host.js","sourceRoot":"","sources":["../../src/authorization/recovery-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DeviceKey } from "./device-key.js";
|
|
2
|
+
import type { DeviceKeyStore } from "./key-store.js";
|
|
3
|
+
export interface DeviceKeyRecoveryStores {
|
|
4
|
+
readonly active: DeviceKeyStore;
|
|
5
|
+
readonly pending: DeviceKeyStore;
|
|
6
|
+
}
|
|
7
|
+
/** Reuse one pending candidate so a lost server response is safe to retry. */
|
|
8
|
+
export declare function stageDeviceKeyRecovery(stores: DeviceKeyRecoveryStores, ports?: Readonly<{
|
|
9
|
+
nowMs?: () => number;
|
|
10
|
+
randomUuid?: () => string;
|
|
11
|
+
}>): Promise<DeviceKey>;
|
|
12
|
+
/** Promote only the exact server-accepted candidate, then erase staging. */
|
|
13
|
+
export declare function promoteDeviceKeyRecovery(stores: DeviceKeyRecoveryStores, acceptedKeyThumbprint: string): Promise<DeviceKey>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { deviceKeyFromStored, generateStoredDeviceKey, } from "./device-key.js";
|
|
2
|
+
/** Reuse one pending candidate so a lost server response is safe to retry. */
|
|
3
|
+
export async function stageDeviceKeyRecovery(stores, ports = {}) {
|
|
4
|
+
let candidate = await stores.pending.load();
|
|
5
|
+
if (!candidate) {
|
|
6
|
+
candidate = generateStoredDeviceKey();
|
|
7
|
+
await stores.pending.save(candidate);
|
|
8
|
+
}
|
|
9
|
+
return deviceKeyFromStored(candidate, ports);
|
|
10
|
+
}
|
|
11
|
+
/** Promote only the exact server-accepted candidate, then erase staging. */
|
|
12
|
+
export async function promoteDeviceKeyRecovery(stores, acceptedKeyThumbprint) {
|
|
13
|
+
const candidate = await stores.pending.load();
|
|
14
|
+
if (!candidate)
|
|
15
|
+
throw recoveryError("authorization_recovery_candidate_missing");
|
|
16
|
+
const signer = deviceKeyFromStored(candidate, {});
|
|
17
|
+
if (signer.keyThumbprint !== acceptedKeyThumbprint) {
|
|
18
|
+
throw recoveryError("authorization_recovery_candidate_mismatch");
|
|
19
|
+
}
|
|
20
|
+
await stores.active.save(candidate);
|
|
21
|
+
await stores.pending.remove();
|
|
22
|
+
return signer;
|
|
23
|
+
}
|
|
24
|
+
function recoveryError(code) {
|
|
25
|
+
return Object.assign(new Error(code), { code });
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/authorization/recovery.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAQzB,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAA+B,EAC/B,QAAuE,EAAE;IAEzE,IAAI,SAAS,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IAC5C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,uBAAuB,EAAE,CAAC;QACtC,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,MAA+B,EAC/B,qBAA6B;IAE7B,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IAC9C,IAAI,CAAC,SAAS;QAAE,MAAM,aAAa,CAAC,0CAA0C,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAClD,IAAI,MAAM,CAAC,aAAa,KAAK,qBAAqB,EAAE,CAAC;QACnD,MAAM,aAAa,CAAC,2CAA2C,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
const OUTPUT_LIMIT = 64 * 1024;
|
|
3
|
+
/** Run a local credential helper without placing secret input in argv. */
|
|
4
|
+
export function runSecretProcess(file, args, input) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const child = spawn(file, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
7
|
+
const stdout = [];
|
|
8
|
+
const stderr = [];
|
|
9
|
+
let outputBytes = 0;
|
|
10
|
+
const collect = (target) => (chunk) => {
|
|
11
|
+
outputBytes += chunk.byteLength;
|
|
12
|
+
if (outputBytes > OUTPUT_LIMIT)
|
|
13
|
+
child.kill();
|
|
14
|
+
else
|
|
15
|
+
target.push(Buffer.from(chunk));
|
|
16
|
+
};
|
|
17
|
+
child.stdout.on("data", collect(stdout));
|
|
18
|
+
child.stderr.on("data", collect(stderr));
|
|
19
|
+
child.once("error", reject);
|
|
20
|
+
child.once("close", (code) => {
|
|
21
|
+
if (outputBytes > OUTPUT_LIMIT)
|
|
22
|
+
return reject(new Error("credential helper output exceeded limit"));
|
|
23
|
+
if (code === 0)
|
|
24
|
+
return resolve(Buffer.concat(stdout).toString("utf8"));
|
|
25
|
+
const error = new Error(`credential helper exited with code ${code ?? "unknown"}`);
|
|
26
|
+
error.stderr = Buffer.concat(stderr).toString("utf8");
|
|
27
|
+
reject(error);
|
|
28
|
+
});
|
|
29
|
+
child.stdin.end(input);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=secret-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-process.js","sourceRoot":"","sources":["../../src/authorization/secret-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,CAAC;AAE/B,0EAA0E;AAC1E,MAAM,UAAU,gBAAgB,CAC9B,IAAY,EACZ,IAAuB,EACvB,KAAa;IAEb,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACrE,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,MAAM,OAAO,GAAG,CAAC,MAAgB,EAAE,EAAE,CAAC,CAAC,KAAiB,EAAE,EAAE;YAC1D,WAAW,IAAI,KAAK,CAAC,UAAU,CAAC;YAChC,IAAI,WAAW,GAAG,YAAY;gBAAE,KAAK,CAAC,IAAI,EAAE,CAAC;;gBACxC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC;QACF,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QACzC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QACzC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAC3B,IAAI,WAAW,GAAG,YAAY;gBAAE,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC,CAAC;YACpG,IAAI,IAAI,KAAK,CAAC;gBAAE,OAAO,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YACvE,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,sCAAsC,IAAI,IAAI,SAAS,EAAE,CAEhF,CAAC;YACF,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtD,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/boot.d.ts
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import type { Server } from "node:http";
|
|
2
2
|
import { Agents } from "@amalgm/agents";
|
|
3
3
|
import type { Apps } from "@amalgm/apps";
|
|
4
|
+
import { type BrowserAuthorization, type BrowserDriver, type BrowserEventSink, type BrowserRuntimeSelector, type BrowserService } from "@amalgm/browser";
|
|
5
|
+
import type { ChatRuntimePort } from "@amalgm/chat";
|
|
6
|
+
import type { PlatformCredentialBroker } from "@amalgm/chat/host";
|
|
4
7
|
import { StateRail } from "@amalgm/core/state";
|
|
5
8
|
import { Notifications } from "@amalgm/tools";
|
|
6
9
|
import type { EmailDelivery, Toolbox } from "@amalgm/tools";
|
|
7
10
|
import type { PtySessionManager } from "@amalgm/core/pty";
|
|
8
11
|
import { WorkspaceRegistry } from "@amalgm/live/workspace";
|
|
9
12
|
import type { AutomationsHostConfig } from "./automations-host.js";
|
|
10
|
-
import { ChatHost } from "./chat-host.js";
|
|
11
|
-
import type { ChatDriver } from "./chat-host.js";
|
|
12
|
-
import type { ChatRelayHostConfig } from "./chat-relay-host.js";
|
|
13
|
+
import type { ChatHost } from "./chat-host.js";
|
|
13
14
|
import type { ShellConfig } from "./compose.js";
|
|
14
15
|
import type { AutomationEventsHostConfig } from "./events-host.js";
|
|
15
16
|
import type { BrickMount } from "./gateway.js";
|
|
16
17
|
export interface BootConfig extends ShellConfig {
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
|
|
18
|
+
/** Optional runtime injection for tests or non-native hosts. */
|
|
19
|
+
chatRuntime?: ChatRuntimePort<any, any>;
|
|
20
|
+
/** Live platform grant capability. Required only for platform-auth Chat. */
|
|
21
|
+
credentialBroker?: PlatformCredentialBroker;
|
|
21
22
|
/** Automations storage capability; when present /automations mounts. */
|
|
22
23
|
automations?: AutomationsHostConfig;
|
|
23
24
|
/** Automations delivery capability; when present /events mounts. */
|
|
@@ -26,16 +27,24 @@ export interface BootConfig extends ShellConfig {
|
|
|
26
27
|
notifications?: {
|
|
27
28
|
email?: EmailDelivery;
|
|
28
29
|
};
|
|
30
|
+
/** Optional host capabilities for Shell's one Browser service. */
|
|
31
|
+
browser?: {
|
|
32
|
+
electronDriver?: BrowserDriver;
|
|
33
|
+
runtimeSelector?: BrowserRuntimeSelector;
|
|
34
|
+
eventSink?: BrowserEventSink;
|
|
35
|
+
authorization?: BrowserAuthorization;
|
|
36
|
+
};
|
|
29
37
|
}
|
|
30
38
|
export interface BootedShell {
|
|
31
39
|
readonly gateway: Server;
|
|
32
40
|
readonly stateDirs: Record<string, string>;
|
|
33
41
|
readonly mounts: readonly BrickMount[];
|
|
34
|
-
readonly chat: ChatHost
|
|
42
|
+
readonly chat: ChatHost;
|
|
35
43
|
readonly agents: Agents;
|
|
36
44
|
readonly apps: Apps;
|
|
37
45
|
readonly toolbox: Toolbox;
|
|
38
46
|
readonly notifications: Notifications;
|
|
47
|
+
readonly browser: BrowserService;
|
|
39
48
|
readonly state: StateRail;
|
|
40
49
|
readonly workspace: WorkspaceRegistry;
|
|
41
50
|
readonly pty: PtySessionManager;
|
|
@@ -44,10 +53,9 @@ export interface BootedShell {
|
|
|
44
53
|
}
|
|
45
54
|
/**
|
|
46
55
|
* Boot the shell: identity law hands every brick its state dir, each
|
|
47
|
-
* brick's surface mounts on its
|
|
56
|
+
* brick's surface mounts on its Shell prefix, and kernel law guards
|
|
48
57
|
* the door. Bricks that ship their own HTTP server (agents, browser)
|
|
49
|
-
* run on loopback and are forwarded
|
|
50
|
-
* prefix-to-service-port architecture, applied in-process.
|
|
58
|
+
* run on loopback and are forwarded through the same gateway.
|
|
51
59
|
* Composition only — no brick behavior is defined here.
|
|
52
60
|
*/
|
|
53
61
|
export declare function bootShell(config?: BootConfig): Promise<BootedShell>;
|