@timurproko/a1 0.2.7-dev.792 → 0.2.7-dev.806
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/dist/app/session-shell/clipboard-diagnostics.js +1 -1
- package/dist/app/session-shell/response-copy-helper.js +21 -6
- package/dist/app/session-shell/response-copy-protocol.d.ts +1 -1
- package/dist/app/session-shell/response-copy-transport.d.ts +3 -6
- package/dist/app/session-shell/response-copy-transport.js +60 -17
- package/dist/app/session-shell/session-shell-root.d.ts +2 -0
- package/dist/app/session-shell/session-shell-root.js +6 -1
- package/dist/app/session-shell/session-shell.d.ts +1 -0
- package/dist/app/session-shell/session-shell.js +22 -19
- package/dist/cli/tabs.js +1 -1
- package/dist/composition/owned-ui.d.ts +1 -0
- package/dist/composition/owned-ui.js +16 -3
- package/dist/features/resident-tab/shortcut-declarations.d.ts +1 -1
- package/dist/features/resident-tab/shortcut-declarations.js +1 -0
- package/dist/foundation/launch-guardian/exit-watcher.d.ts +14 -0
- package/dist/foundation/launch-guardian/exit-watcher.js +94 -0
- package/dist/foundation/launch-guardian/main.d.ts +2 -1
- package/dist/foundation/launch-guardian/main.js +58 -6
- package/dist/foundation/process-containment/{native-guardian-containment.d.ts → addon-containment.d.ts} +7 -5
- package/dist/foundation/process-containment/addon-containment.js +139 -0
- package/dist/foundation/process-containment/addon-process-inspector.d.ts +11 -0
- package/dist/foundation/process-containment/addon-process-inspector.js +36 -0
- package/dist/foundation/process-containment/addon.d.ts +42 -0
- package/dist/foundation/process-containment/addon.js +57 -0
- package/dist/foundation/process-containment/artifact.d.ts +1 -1
- package/dist/foundation/process-containment/artifact.js +6 -5
- package/dist/foundation/process-containment/contracts.d.ts +2 -0
- package/dist/foundation/process-containment/index.d.ts +4 -4
- package/dist/foundation/process-containment/index.js +3 -3
- package/dist/foundation/process-containment/native-process-inspector.d.ts +2 -1
- package/dist/foundation/process-containment/native-process-inspector.js +10 -10
- package/dist/foundation/release/bootstrap.js +53 -16
- package/dist/foundation/release/index.d.ts +1 -1
- package/dist/foundation/release/index.js +1 -1
- package/dist/foundation/release/release-store.d.ts +12 -0
- package/dist/foundation/release/release-store.js +124 -2
- package/dist/foundation/resident-tabs/bounded-host-command.d.ts +3 -1
- package/dist/foundation/resident-tabs/bounded-host-command.js +6 -2
- package/dist/foundation/resident-tabs/bridge-client.d.ts +2 -0
- package/dist/foundation/resident-tabs/bridge-client.js +13 -0
- package/dist/foundation/resident-tabs/doctor.d.ts +0 -1
- package/dist/foundation/resident-tabs/doctor.js +4 -4
- package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +7 -2
- package/dist/foundation/resident-tabs/ensure-terminal-host.js +15 -11
- package/dist/foundation/resident-tabs/host-entry.d.ts +1 -0
- package/dist/foundation/resident-tabs/host-entry.js +17 -0
- package/dist/foundation/resident-tabs/index.d.ts +2 -2
- package/dist/foundation/resident-tabs/index.js +1 -1
- package/dist/foundation/resident-tabs/maintenance.d.ts +5 -4
- package/dist/foundation/resident-tabs/maintenance.js +32 -17
- package/dist/foundation/resident-tabs/release-holds.d.ts +3 -2
- package/dist/foundation/resident-tabs/release-holds.js +16 -7
- package/dist/foundation/resident-tabs/settings.d.ts +0 -1
- package/dist/foundation/resident-tabs/settings.js +1 -7
- package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +11 -15
- package/dist/foundation/resident-tabs/terminal-host-artifact.js +57 -32
- package/dist/foundation/session-writer-lock/index.d.ts +1 -3
- package/dist/foundation/session-writer-lock/index.js +8 -6
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +2 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +2 -0
- package/dist/native/darwin-arm64/manifest.json +8 -6
- package/dist/native/darwin-arm64/process-guardian.node +0 -0
- package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/native/darwin-arm64/terminal-host.manifest.json +14 -13
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +9 -7
- package/dist/native/darwin-x64/process-guardian.node +0 -0
- package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/native/darwin-x64/terminal-host.manifest.json +15 -14
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +8 -6
- package/dist/native/linux-x64/process-guardian.node +0 -0
- package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/native/linux-x64/terminal-host.manifest.json +14 -13
- package/dist/native/linux-x64/{terminal-host → terminal-host.node} +0 -0
- package/dist/native/win32-x64/manifest.json +8 -6
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/native/win32-x64/terminal-host.manifest.json +14 -13
- package/dist/native/win32-x64/{terminal-host.exe → terminal-host.node} +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +3 -2
- package/dist/runtime-payload-inventory.json +1 -0
- package/dist/ui/settings/declarations.d.ts +0 -12
- package/dist/ui/settings/declarations.js +0 -10
- package/docs/architecture/boundaries.md +10 -9
- package/docs/architecture/process-guardian-provenance.md +17 -11
- package/docs/architecture/resident-tabs.md +36 -34
- package/docs/architecture/terminal-host-provenance.md +5 -3
- package/docs/architecture/toolchain.md +8 -6
- package/docs/ci-release-runbook.md +1 -1
- package/docs/openspec-archive-automation.md +23 -9
- package/docs/validation.md +6 -2
- package/package.json +5 -3
- package/dist/foundation/process-containment/darwin-process-inspector.d.ts +0 -5
- package/dist/foundation/process-containment/darwin-process-inspector.js +0 -7
- package/dist/foundation/process-containment/native-guardian-containment.js +0 -152
- package/dist/foundation/process-containment/windows-process-inspector.d.ts +0 -19
- package/dist/foundation/process-containment/windows-process-inspector.js +0 -56
- package/dist/native/darwin-arm64/a1_session_lock.node +0 -0
- package/dist/native/darwin-arm64/process-guardian +0 -0
- package/dist/native/darwin-arm64/terminal-host +0 -0
- package/dist/native/darwin-x64/a1_session_lock.node +0 -0
- package/dist/native/darwin-x64/process-guardian +0 -0
- package/dist/native/darwin-x64/terminal-host +0 -0
- package/dist/native/linux-x64/a1_session_lock.node +0 -0
- package/dist/native/linux-x64/process-guardian +0 -0
- package/dist/native/win32-x64/a1_session_lock.node +0 -0
- package/dist/native/win32-x64/process-guardian.exe +0 -0
|
@@ -2,37 +2,38 @@ import { constants } from "node:fs";
|
|
|
2
2
|
import { access } from "node:fs/promises";
|
|
3
3
|
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
4
4
|
import { runBoundedHostCommand } from "./bounded-host-command.js";
|
|
5
|
-
import { resolveTerminalHostPath } from "./ensure-terminal-host.js";
|
|
5
|
+
import { resolveTerminalHostEntryPath, resolveTerminalHostPath } from "./ensure-terminal-host.js";
|
|
6
6
|
import { verifyTerminalHostArtifact } from "./terminal-host-artifact.js";
|
|
7
7
|
const INCOMPATIBLE_EXIT = 4;
|
|
8
8
|
/**
|
|
9
9
|
* Runs one maintenance form through a verified terminal host. When the running host speaks
|
|
10
|
-
* another protocol generation, it retries once with the
|
|
11
|
-
* recorded, after verifying that release's manifest; it never
|
|
10
|
+
* another protocol generation, it retries once with the addon from the release that host
|
|
11
|
+
* recorded, after verifying that release's manifest; it never loads an unverified addon.
|
|
12
12
|
*/
|
|
13
13
|
export async function runTerminalHostMaintenance(options) {
|
|
14
14
|
const platform = options.platform ?? process.platform;
|
|
15
15
|
const architecture = options.architecture ?? process.arch;
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
16
|
+
const command = await firstVerifiedHost(options, options.releaseRoots, platform, architecture);
|
|
17
|
+
if (command === undefined)
|
|
18
18
|
return unavailable(options.action, "no verified terminal host is installed");
|
|
19
|
-
const report = await invoke(options,
|
|
19
|
+
const report = await invoke(options, command);
|
|
20
20
|
if (report.exitCode !== INCOMPATIBLE_EXIT || typeof report.releaseRoot !== "string")
|
|
21
21
|
return report;
|
|
22
22
|
const cohort = await firstVerifiedHost({ ...options, environment: {} }, [report.releaseRoot], platform, architecture);
|
|
23
|
-
if (cohort === undefined || cohort ===
|
|
23
|
+
if (cohort === undefined || cohort.entry === command.entry) {
|
|
24
24
|
return { ...report, message: "the running tabs use another version whose terminal host cannot be verified" };
|
|
25
25
|
}
|
|
26
|
-
return await invoke(options, cohort);
|
|
26
|
+
return await invoke({ ...options, environment: installedEnvironment(options.environment) }, cohort);
|
|
27
27
|
}
|
|
28
28
|
function arguments_(options) {
|
|
29
29
|
const command = options.action === "stop" ? ["stop", options.tabId ?? ""] : [options.action];
|
|
30
30
|
return ["tabs", ...command, "--profile", options.profileRoot, "--data-dir", options.dataDir, "--runtime-dir", options.runtimeDir];
|
|
31
31
|
}
|
|
32
|
-
async function invoke(options,
|
|
32
|
+
async function invoke(options, command) {
|
|
33
33
|
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
34
34
|
try {
|
|
35
|
-
const
|
|
35
|
+
const run = options.run ?? ((host, argumentsValue, limit) => runHost(host, argumentsValue, limit, options.environment));
|
|
36
|
+
const result = await run(command, arguments_(options), timeoutMs);
|
|
36
37
|
const line = result.stdout.trim().split(/\r?\n/u).at(-1) ?? "";
|
|
37
38
|
const parsed = JSON.parse(line);
|
|
38
39
|
if (parsed.schema !== "a1-tabs-maintenance-v1" || typeof parsed.host !== "string")
|
|
@@ -48,19 +49,22 @@ async function firstVerifiedHost(options, roots, platform, architecture) {
|
|
|
48
49
|
? options.environment[PRODUCT_IDENTITY.environment.terminalHostPath]?.trim()
|
|
49
50
|
: undefined;
|
|
50
51
|
if (override) {
|
|
52
|
+
const entry = roots[0] === undefined ? undefined : resolveTerminalHostEntryPath(roots[0]);
|
|
53
|
+
if (entry === undefined)
|
|
54
|
+
return undefined;
|
|
51
55
|
try {
|
|
52
|
-
await access(override, constants.R_OK
|
|
53
|
-
return
|
|
56
|
+
await Promise.all([access(override, constants.R_OK), access(entry, constants.R_OK)]);
|
|
57
|
+
return { executable: process.execPath, entry };
|
|
54
58
|
}
|
|
55
59
|
catch {
|
|
56
60
|
return undefined;
|
|
57
61
|
}
|
|
58
62
|
}
|
|
59
63
|
for (const root of roots) {
|
|
60
|
-
const
|
|
64
|
+
const addon = resolveTerminalHostPath(root, {}, platform, architecture);
|
|
61
65
|
try {
|
|
62
|
-
await (options.verify ?? verifyTerminalHostArtifact)(
|
|
63
|
-
return
|
|
66
|
+
await (options.verify ?? verifyTerminalHostArtifact)(addon, platform, architecture);
|
|
67
|
+
return { executable: process.execPath, entry: resolveTerminalHostEntryPath(root) };
|
|
64
68
|
}
|
|
65
69
|
catch {
|
|
66
70
|
// Rationale: an older retained release may predate the packaged host; try the next one.
|
|
@@ -68,11 +72,22 @@ async function firstVerifiedHost(options, roots, platform, architecture) {
|
|
|
68
72
|
}
|
|
69
73
|
return undefined;
|
|
70
74
|
}
|
|
75
|
+
function installedEnvironment(environment) {
|
|
76
|
+
const sanitized = { ...environment };
|
|
77
|
+
delete sanitized[PRODUCT_IDENTITY.environment.developmentInstanceId];
|
|
78
|
+
delete sanitized[PRODUCT_IDENTITY.environment.terminalHostPath];
|
|
79
|
+
return sanitized;
|
|
80
|
+
}
|
|
71
81
|
function unavailable(action, message) {
|
|
72
82
|
return { schema: "a1-tabs-maintenance-v1", command: action, host: "unavailable", message, exitCode: 3 };
|
|
73
83
|
}
|
|
74
84
|
/** The largest maintenance report accepted from the terminal host. */
|
|
75
85
|
export const MAX_MAINTENANCE_REPORT_BYTES = 4 * 1024 * 1024;
|
|
76
|
-
async function runHost(
|
|
77
|
-
return await runBoundedHostCommand(
|
|
86
|
+
async function runHost(command, arguments_, timeoutMs, environment) {
|
|
87
|
+
return await runBoundedHostCommand(command, arguments_, {
|
|
88
|
+
environment,
|
|
89
|
+
maxStdoutBytes: MAX_MAINTENANCE_REPORT_BYTES,
|
|
90
|
+
timeoutMs,
|
|
91
|
+
label: "terminal host maintenance",
|
|
92
|
+
});
|
|
78
93
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type TerminalHostCommand } from "./ensure-terminal-host.js";
|
|
1
2
|
export interface ResidentReleaseHoldReport {
|
|
2
3
|
/** False when any resident record or identity could not be verified; callers then keep every release. */
|
|
3
4
|
readonly complete: boolean;
|
|
@@ -11,8 +12,8 @@ export interface ResidentReleaseHoldOptions {
|
|
|
11
12
|
readonly platform?: NodeJS.Platform;
|
|
12
13
|
readonly architecture?: string;
|
|
13
14
|
readonly timeoutMs?: number;
|
|
14
|
-
readonly verify?: (
|
|
15
|
-
readonly run?: (
|
|
15
|
+
readonly verify?: (addon: string, platform: NodeJS.Platform, architecture: string) => Promise<unknown>;
|
|
16
|
+
readonly run?: (command: TerminalHostCommand, arguments_: readonly string[], timeoutMs: number) => Promise<{
|
|
16
17
|
readonly code: number;
|
|
17
18
|
readonly stdout: string;
|
|
18
19
|
}>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { readdir, stat } from "node:fs/promises";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
+
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
3
4
|
import { runBoundedHostCommand } from "./bounded-host-command.js";
|
|
4
|
-
import { resolveTerminalHostPath } from "./ensure-terminal-host.js";
|
|
5
|
+
import { resolveTerminalHostEntryPath, resolveTerminalHostPath } from "./ensure-terminal-host.js";
|
|
5
6
|
import { verifyTerminalHostArtifact } from "./terminal-host-artifact.js";
|
|
6
7
|
const COMPLETE_EMPTY = Object.freeze({ complete: true, releaseRoots: Object.freeze([]), uncertain: Object.freeze([]) });
|
|
7
8
|
/**
|
|
@@ -15,22 +16,22 @@ export async function readResidentReleaseHolds(options) {
|
|
|
15
16
|
const platform = options.platform ?? process.platform;
|
|
16
17
|
const architecture = options.architecture ?? process.arch;
|
|
17
18
|
const verify = options.verify ?? verifyTerminalHostArtifact;
|
|
18
|
-
let
|
|
19
|
+
let command;
|
|
19
20
|
for (const root of options.releaseRoots) {
|
|
20
21
|
const candidate = resolveTerminalHostPath(root, {}, platform, architecture);
|
|
21
22
|
try {
|
|
22
23
|
await verify(candidate, platform, architecture);
|
|
23
|
-
|
|
24
|
+
command = { executable: process.execPath, entry: resolveTerminalHostEntryPath(root) };
|
|
24
25
|
break;
|
|
25
26
|
}
|
|
26
27
|
catch {
|
|
27
28
|
// Rationale: an older release may predate the packaged host; another retained release may still carry it.
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
|
-
if (!
|
|
31
|
+
if (!command)
|
|
31
32
|
return incomplete("no verified terminal host is installed to inspect resident records");
|
|
32
33
|
try {
|
|
33
|
-
const result = await (options.run ?? runHost)(
|
|
34
|
+
const result = await (options.run ?? runHost)(command, ["release-holds", "--data-dir", options.dataDir], options.timeoutMs ?? 5_000);
|
|
34
35
|
const report = JSON.parse(result.stdout.trim().split(/\r?\n/u).at(-1) ?? "");
|
|
35
36
|
if (result.code !== 0 || report.schema !== "a1-resident-release-holds-v1" || typeof report.complete !== "boolean"
|
|
36
37
|
|| !isStringArray(report.releaseRoots) || !isStringArray(report.uncertain)) {
|
|
@@ -72,6 +73,14 @@ function isStringArray(value) {
|
|
|
72
73
|
}
|
|
73
74
|
/** The largest release-hold report accepted from the terminal host. */
|
|
74
75
|
export const MAX_RELEASE_HOLD_REPORT_BYTES = 1024 * 1024;
|
|
75
|
-
async function runHost(
|
|
76
|
-
|
|
76
|
+
async function runHost(command, arguments_, timeoutMs) {
|
|
77
|
+
const environment = { ...process.env };
|
|
78
|
+
delete environment[PRODUCT_IDENTITY.environment.developmentInstanceId];
|
|
79
|
+
delete environment[PRODUCT_IDENTITY.environment.terminalHostPath];
|
|
80
|
+
return await runBoundedHostCommand(command, arguments_, {
|
|
81
|
+
environment,
|
|
82
|
+
maxStdoutBytes: MAX_RELEASE_HOLD_REPORT_BYTES,
|
|
83
|
+
timeoutMs,
|
|
84
|
+
label: "terminal host release-hold inspection",
|
|
85
|
+
});
|
|
77
86
|
}
|
|
@@ -3,7 +3,6 @@ export declare const RESIDENT_TABS_MAX_CONCURRENT_STARTS_CAP = 8;
|
|
|
3
3
|
export declare const RESIDENT_TABS_PREWARM_COUNT = 1;
|
|
4
4
|
export declare const RESIDENT_TABS_PREWARM_CAP = 1;
|
|
5
5
|
export interface ResidentTabLaunchSettings {
|
|
6
|
-
readonly tabsMax: number;
|
|
7
6
|
readonly tabsAutoName: boolean;
|
|
8
7
|
readonly maxConcurrentStarts: number;
|
|
9
8
|
readonly prewarmCount: number;
|
|
@@ -7,7 +7,6 @@ export const RESIDENT_TABS_MAX_CONCURRENT_STARTS_CAP = 8;
|
|
|
7
7
|
export const RESIDENT_TABS_PREWARM_COUNT = 1;
|
|
8
8
|
export const RESIDENT_TABS_PREWARM_CAP = 1;
|
|
9
9
|
const DEFAULTS = Object.freeze({
|
|
10
|
-
tabsMax: 10,
|
|
11
10
|
tabsAutoName: true,
|
|
12
11
|
maxConcurrentStarts: RESIDENT_TABS_MAX_CONCURRENT_STARTS,
|
|
13
12
|
prewarmCount: RESIDENT_TABS_PREWARM_COUNT,
|
|
@@ -46,17 +45,12 @@ export async function readResidentTabLaunchSettings(profileDir, namespace = "a1"
|
|
|
46
45
|
}
|
|
47
46
|
function resolveLaunchValues(document, migrations) {
|
|
48
47
|
const resolution = resolveOwnedUiSettings({
|
|
49
|
-
declarations: [
|
|
50
|
-
OWNED_SETTING_DECLARATIONS.tabsMax,
|
|
51
|
-
OWNED_SETTING_DECLARATIONS.tabsAutoName,
|
|
52
|
-
],
|
|
48
|
+
declarations: [OWNED_SETTING_DECLARATIONS.tabsAutoName],
|
|
53
49
|
migrations,
|
|
54
50
|
document,
|
|
55
51
|
currentVersion: OWNED_UI_SETTINGS_VERSION,
|
|
56
52
|
});
|
|
57
|
-
const tabsMax = document.values?.["tabsMax"] ?? settingValue(resolution, "tabsMax");
|
|
58
53
|
return {
|
|
59
|
-
tabsMax: typeof tabsMax === "number" && Number.isFinite(tabsMax) ? Math.max(1, Math.min(50, Math.trunc(tabsMax))) : DEFAULTS.tabsMax,
|
|
60
54
|
tabsAutoName: settingValue(resolution, "tabsAutoName") === true,
|
|
61
55
|
maxConcurrentStarts: RESIDENT_TABS_MAX_CONCURRENT_STARTS,
|
|
62
56
|
prewarmCount: RESIDENT_TABS_PREWARM_COUNT,
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
export declare const TERMINAL_HOST_MANIFEST_FILENAME = "terminal-host.manifest.json";
|
|
2
|
-
export
|
|
3
|
-
export type TerminalHostArtifactCode = "TERMINAL_HOST_ARTIFACT_MISSING" | "TERMINAL_HOST_ARTIFACT_INCOMPATIBLE" | "TERMINAL_HOST_ARTIFACT_UNSUPPORTED" | "TERMINAL_HOST_ARTIFACT_TAMPERED";
|
|
4
|
-
export interface VerifiedTerminalHostArtifact {
|
|
5
|
-
readonly executable: string;
|
|
6
|
-
readonly sessionLock: string;
|
|
7
|
-
readonly generation: number;
|
|
8
|
-
}
|
|
2
|
+
export type TerminalHostArtifactErrorCode = "TERMINAL_HOST_ARTIFACT_MISSING" | "TERMINAL_HOST_ARTIFACT_TAMPERED" | "TERMINAL_HOST_ARTIFACT_INCOMPATIBLE" | "TERMINAL_HOST_ARTIFACT_UNSUPPORTED";
|
|
9
3
|
export declare class TerminalHostArtifactError extends Error {
|
|
10
|
-
readonly code:
|
|
11
|
-
|
|
4
|
+
readonly code: TerminalHostArtifactErrorCode;
|
|
5
|
+
readonly manifestPath: string;
|
|
6
|
+
constructor(code: TerminalHostArtifactErrorCode, manifestPath: string, message: string);
|
|
7
|
+
}
|
|
8
|
+
export interface VerifiedTerminalHostArtifact {
|
|
9
|
+
readonly addon: string;
|
|
10
|
+
readonly entry: string;
|
|
11
|
+
readonly generation: 1;
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
* anything executes or loads them: schema, generation, platform, architecture, file names, sizes,
|
|
16
|
-
* and SHA-256 digests.
|
|
17
|
-
*/
|
|
18
|
-
export declare function verifyTerminalHostArtifact(executable: string, platform?: NodeJS.Platform, architecture?: string, generation?: number): Promise<VerifiedTerminalHostArtifact>;
|
|
13
|
+
/** Verifies every runtime file certified by the terminal-host v2 manifest before Node loads it. */
|
|
14
|
+
export declare function verifyTerminalHostArtifact(addonPath: string, platform?: NodeJS.Platform, architecture?: string): Promise<VerifiedTerminalHostArtifact>;
|
|
@@ -1,53 +1,78 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFile, stat } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, resolve } from "node:path";
|
|
4
|
+
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
4
5
|
export const TERMINAL_HOST_MANIFEST_FILENAME = "terminal-host.manifest.json";
|
|
5
|
-
|
|
6
|
+
const SCHEMA = "a1-terminal-host-artifact-v2";
|
|
7
|
+
const GENERATION = 1;
|
|
8
|
+
const NODE_API = 9;
|
|
9
|
+
const NOTICES_NAME = "terminal-host-THIRD-PARTY-NOTICES.txt";
|
|
6
10
|
export class TerminalHostArtifactError extends Error {
|
|
7
11
|
code;
|
|
8
|
-
|
|
12
|
+
manifestPath;
|
|
13
|
+
constructor(code, manifestPath, message) {
|
|
9
14
|
super(message);
|
|
10
15
|
this.name = "TerminalHostArtifactError";
|
|
11
16
|
this.code = code;
|
|
17
|
+
this.manifestPath = manifestPath;
|
|
12
18
|
}
|
|
13
19
|
}
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export async function verifyTerminalHostArtifact(executable, platform = process.platform, architecture = process.arch, generation = TERMINAL_HOST_GENERATION) {
|
|
20
|
-
const directory = dirname(executable);
|
|
20
|
+
/** Verifies every runtime file certified by the terminal-host v2 manifest before Node loads it. */
|
|
21
|
+
export async function verifyTerminalHostArtifact(addonPath, platform = process.platform, architecture = process.arch) {
|
|
22
|
+
const directory = dirname(addonPath);
|
|
23
|
+
const releaseRoot = resolve(directory, "../../..");
|
|
24
|
+
const manifestPath = resolve(directory, TERMINAL_HOST_MANIFEST_FILENAME);
|
|
21
25
|
let manifest;
|
|
22
26
|
try {
|
|
23
|
-
|
|
27
|
+
const parsed = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
28
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
29
|
+
throw new TypeError("terminal host manifest is not an object");
|
|
30
|
+
manifest = parsed;
|
|
24
31
|
}
|
|
25
32
|
catch (error) {
|
|
26
|
-
|
|
33
|
+
const code = error instanceof Error && "code" in error && error.code === "ENOENT"
|
|
34
|
+
? "TERMINAL_HOST_ARTIFACT_MISSING"
|
|
35
|
+
: "TERMINAL_HOST_ARTIFACT_INCOMPATIBLE";
|
|
36
|
+
throw new TerminalHostArtifactError(code, manifestPath, "terminal host manifest is missing or invalid");
|
|
27
37
|
}
|
|
28
|
-
if (manifest.
|
|
29
|
-
|
|
30
|
-
|| manifest.artifact?.filename !== basename(executable) || typeof manifest.sessionLock?.filename !== "string"
|
|
31
|
-
|| basename(manifest.sessionLock.filename) !== manifest.sessionLock.filename) {
|
|
32
|
-
throw new TerminalHostArtifactError("terminal host manifest is incompatible with this platform or generation", "TERMINAL_HOST_ARTIFACT_INCOMPATIBLE");
|
|
38
|
+
if (manifest.capability === "unsupported") {
|
|
39
|
+
throw new TerminalHostArtifactError("TERMINAL_HOST_ARTIFACT_UNSUPPORTED", manifestPath, "terminal host is unsupported on this platform");
|
|
33
40
|
}
|
|
34
|
-
if (manifest.
|
|
35
|
-
|
|
41
|
+
if (manifest.schema !== SCHEMA || manifest.kind !== "node-addon" || manifest.nodeApi !== NODE_API
|
|
42
|
+
|| manifest.protocolVersion !== 1 || manifest.generation !== GENERATION
|
|
43
|
+
|| manifest.platform !== platform || manifest.architecture !== architecture || manifest.capability !== "supported"
|
|
44
|
+
|| !validRecord(manifest.artifact) || !validRecord(manifest.entry) || !validRecord(manifest.notices)
|
|
45
|
+
|| manifest.artifact.filename !== `${PRODUCT_IDENTITY.artifacts.terminalHostAddon}.node`
|
|
46
|
+
|| manifest.entry.filename !== `dist/foundation/resident-tabs/${PRODUCT_IDENTITY.artifacts.terminalHostEntry}.js`
|
|
47
|
+
|| manifest.notices.filename !== NOTICES_NAME
|
|
48
|
+
|| basename(addonPath) !== manifest.artifact.filename) {
|
|
49
|
+
throw new TerminalHostArtifactError("TERMINAL_HOST_ARTIFACT_INCOMPATIBLE", manifestPath, "terminal host manifest is incompatible");
|
|
36
50
|
}
|
|
37
|
-
const
|
|
38
|
-
await verifyFile(
|
|
39
|
-
await verifyFile(
|
|
40
|
-
|
|
51
|
+
const entry = resolve(releaseRoot, manifest.entry.filename);
|
|
52
|
+
await verifyFile(addonPath, manifest.artifact, manifestPath);
|
|
53
|
+
await verifyFile(entry, manifest.entry, manifestPath);
|
|
54
|
+
await verifyFile(resolve(directory, manifest.notices.filename), manifest.notices, manifestPath);
|
|
55
|
+
return { addon: addonPath, entry, generation: 1 };
|
|
41
56
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (metadata.size !== expected.size || digest !== expected.sha256) {
|
|
48
|
-
throw new TerminalHostArtifactError(`${label} artifact integrity verification failed`, "TERMINAL_HOST_ARTIFACT_TAMPERED");
|
|
49
|
-
}
|
|
57
|
+
function validRecord(value) {
|
|
58
|
+
return typeof value === "object" && value !== null
|
|
59
|
+
&& typeof value.filename === "string"
|
|
60
|
+
&& /^[a-f0-9]{64}$/u.test(value.sha256)
|
|
61
|
+
&& Number.isSafeInteger(value.size) && value.size >= 0;
|
|
50
62
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
63
|
+
async function verifyFile(path, record, manifestPath) {
|
|
64
|
+
let bytes;
|
|
65
|
+
let metadata;
|
|
66
|
+
try {
|
|
67
|
+
[bytes, metadata] = await Promise.all([readFile(path), stat(path)]);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
71
|
+
throw new TerminalHostArtifactError("TERMINAL_HOST_ARTIFACT_MISSING", manifestPath, `certified terminal host file is missing: ${record.filename}`);
|
|
72
|
+
}
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
if (!metadata.isFile() || bytes.byteLength !== record.size || createHash("sha256").update(bytes).digest("hex") !== record.sha256) {
|
|
76
|
+
throw new TerminalHostArtifactError("TERMINAL_HOST_ARTIFACT_TAMPERED", manifestPath, `certified terminal host file failed verification: ${record.filename}`);
|
|
77
|
+
}
|
|
53
78
|
}
|
|
@@ -19,9 +19,7 @@ export interface SessionWriterHandle {
|
|
|
19
19
|
bind(createdPath?: string): void;
|
|
20
20
|
release(): void;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
export declare const SESSION_LOCK_OVERRIDE_ENVIRONMENT = "A1_SESSION_LOCK_PATH";
|
|
24
|
-
/** Security: a release that carries its own lock never loads an addon chosen by the environment or cwd. */
|
|
22
|
+
/** Security: a release that carries its own terminal-host addon never loads one chosen by cwd. */
|
|
25
23
|
export declare function nativeCandidates(moduleUrl?: string, env?: NodeJS.ProcessEnv, cwd?: string, exists?: typeof existsSync): string[];
|
|
26
24
|
export declare function acquireExisting(path: string): SessionWriterHandle;
|
|
27
25
|
export declare function reserveNew(parent: string, name: string): SessionWriterHandle;
|
|
@@ -18,15 +18,17 @@ export class SessionWriterConflict extends Error {
|
|
|
18
18
|
}
|
|
19
19
|
let backendOverride;
|
|
20
20
|
let cachedBackend;
|
|
21
|
-
/**
|
|
22
|
-
export const SESSION_LOCK_OVERRIDE_ENVIRONMENT = "A1_SESSION_LOCK_PATH";
|
|
23
|
-
/** Security: a release that carries its own lock never loads an addon chosen by the environment or cwd. */
|
|
21
|
+
/** Security: a release that carries its own terminal-host addon never loads one chosen by cwd. */
|
|
24
22
|
export function nativeCandidates(moduleUrl = import.meta.url, env = process.env, cwd = process.cwd(), exists = existsSync) {
|
|
25
|
-
const addon =
|
|
23
|
+
const addon = `${PRODUCT_IDENTITY.artifacts.terminalHostAddon}.node`;
|
|
26
24
|
// Invariant: this module sits at dist/foundation/session-writer-lock/ inside the release root.
|
|
27
25
|
const own = resolve(fileURLToPath(new URL("../../../", moduleUrl)), "dist", "native", `${process.platform}-${process.arch}`, addon);
|
|
28
|
-
const override = env[PRODUCT_IDENTITY.environment.developmentInstanceId]
|
|
29
|
-
|
|
26
|
+
const override = env[PRODUCT_IDENTITY.environment.developmentInstanceId]
|
|
27
|
+
? env[PRODUCT_IDENTITY.environment.terminalHostPath]?.trim() || undefined
|
|
28
|
+
: undefined;
|
|
29
|
+
const local = exists(own)
|
|
30
|
+
? [own]
|
|
31
|
+
: [resolve(cwd, "dist", "native", `${process.platform}-${process.arch}`, addon), resolve(cwd, ".artifacts", "terminal-host", addon)];
|
|
30
32
|
return override ? [override, ...local] : local;
|
|
31
33
|
}
|
|
32
34
|
function loadNativeBackend() {
|
|
@@ -143,7 +143,8 @@ export function createPiShellEditor(options) {
|
|
|
143
143
|
tui.requestRender();
|
|
144
144
|
};
|
|
145
145
|
let autocompleteProvider;
|
|
146
|
-
const builtInCatalog = options.keybindingProfile === "a1" ? OWNED_BUILTIN_SLASH_COMMANDS : PINNED_PI_BUILTIN_SLASH_COMMANDS
|
|
146
|
+
const builtInCatalog = (options.keybindingProfile === "a1" ? OWNED_BUILTIN_SLASH_COMMANDS : PINNED_PI_BUILTIN_SLASH_COMMANDS)
|
|
147
|
+
.filter(command => !options.hideSettingsCommand || command.name !== "settings");
|
|
147
148
|
const setAutocompleteCommands = (installed) => {
|
|
148
149
|
// Invariant: collapse is a bare-A1 presentation; the comparison profile installs the pinned list.
|
|
149
150
|
const collapsed = options.keybindingProfile === "a1" && options.skillsPresentation?.() === "collapse"
|
|
@@ -164,6 +164,8 @@ export interface PiShellEditorOptions {
|
|
|
164
164
|
readonly historyEditor?: HistoryEditorConstructor;
|
|
165
165
|
/** Bare A1 adds ergonomic aliases while comparison profiles retain Pi defaults. */
|
|
166
166
|
readonly keybindingProfile?: "pi" | "a1";
|
|
167
|
+
/** Resident Agents expose Settings through the global tab instead of a slash command. */
|
|
168
|
+
readonly hideSettingsCommand?: boolean;
|
|
167
169
|
/** The process's shared keybindings; its profile must match `keybindingProfile`. */
|
|
168
170
|
readonly keybindings?: PiKeybindingsHost;
|
|
169
171
|
readonly getColumns: () => number;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schema": "a1-process-guardian-artifact-
|
|
3
|
-
"
|
|
2
|
+
"schema": "a1-process-guardian-artifact-v2",
|
|
3
|
+
"kind": "node-addon",
|
|
4
|
+
"nodeApi": 9,
|
|
5
|
+
"protocolVersion": 2,
|
|
4
6
|
"crateVersion": "0.1.0-dev",
|
|
5
7
|
"platform": "darwin",
|
|
6
8
|
"architecture": "arm64",
|
|
7
9
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-10-
|
|
10
|
+
"builtAt": "2026-10-19T04:50:42.711Z",
|
|
9
11
|
"artifact": {
|
|
10
|
-
"filename": "process-guardian",
|
|
11
|
-
"sha256": "
|
|
12
|
-
"size":
|
|
12
|
+
"filename": "process-guardian.node",
|
|
13
|
+
"sha256": "a87eaaffcd986b624c8b044b756446cc1bdaf4334a1775b97820542fe34f9fb7",
|
|
14
|
+
"size": 456080
|
|
13
15
|
},
|
|
14
16
|
"provenance": {
|
|
15
17
|
"language": "Rust",
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Third-party notices for the A1 terminal host
|
|
2
2
|
|
|
3
|
-
The terminal-host
|
|
3
|
+
The terminal-host Node-API addon includes the following components.
|
|
4
4
|
|
|
5
5
|
==============================================================================
|
|
6
6
|
anyhow 1.0.104
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schema": "a1-terminal-host-artifact-
|
|
2
|
+
"schema": "a1-terminal-host-artifact-v2",
|
|
3
|
+
"kind": "node-addon",
|
|
4
|
+
"nodeApi": 9,
|
|
3
5
|
"protocolVersion": 1,
|
|
4
6
|
"generation": 1,
|
|
5
7
|
"crateVersion": "0.1.0-dev",
|
|
@@ -7,21 +9,21 @@
|
|
|
7
9
|
"architecture": "arm64",
|
|
8
10
|
"target": "aarch64-apple-darwin",
|
|
9
11
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-10-
|
|
12
|
+
"builtAt": "2026-10-19T04:52:31.538Z",
|
|
11
13
|
"artifact": {
|
|
12
|
-
"filename": "terminal-host",
|
|
13
|
-
"sha256": "
|
|
14
|
-
"size":
|
|
14
|
+
"filename": "terminal-host.node",
|
|
15
|
+
"sha256": "af7b9fee2efa8478c728465031e73fb316b6b7012d11de0364130b0acd1b6e62",
|
|
16
|
+
"size": 5173904
|
|
15
17
|
},
|
|
16
|
-
"
|
|
17
|
-
"filename": "
|
|
18
|
-
"sha256": "
|
|
19
|
-
"size":
|
|
18
|
+
"entry": {
|
|
19
|
+
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
20
|
+
"sha256": "4b1f702cc9980ffa531cf3e9963b884eaa35120d04d5c195ea3308f501d8d557",
|
|
21
|
+
"size": 1315
|
|
20
22
|
},
|
|
21
23
|
"notices": {
|
|
22
24
|
"filename": "terminal-host-THIRD-PARTY-NOTICES.txt",
|
|
23
|
-
"sha256": "
|
|
24
|
-
"size":
|
|
25
|
+
"sha256": "5f18fd5d1863f7282c9c3e249b9a25df1b454d26cc737b92b3360ca877e50120",
|
|
26
|
+
"size": 540046
|
|
25
27
|
},
|
|
26
28
|
"provenance": {
|
|
27
29
|
"language": "Rust",
|
|
@@ -34,8 +36,7 @@
|
|
|
34
36
|
"zigVersion": "0.15.2",
|
|
35
37
|
"rustVersion": "rustc 1.99.1 (58cf8f9c3 2026-10-13)",
|
|
36
38
|
"features": {
|
|
37
|
-
"
|
|
38
|
-
"sessionLock": [
|
|
39
|
+
"addon": [
|
|
39
40
|
"node-addon"
|
|
40
41
|
]
|
|
41
42
|
},
|
|
Binary file
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schema": "a1-process-guardian-artifact-
|
|
3
|
-
"
|
|
2
|
+
"schema": "a1-process-guardian-artifact-v2",
|
|
3
|
+
"kind": "node-addon",
|
|
4
|
+
"nodeApi": 9,
|
|
5
|
+
"protocolVersion": 2,
|
|
4
6
|
"crateVersion": "0.1.0-dev",
|
|
5
7
|
"platform": "darwin",
|
|
6
8
|
"architecture": "x64",
|
|
7
9
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-10-
|
|
10
|
+
"builtAt": "2026-10-19T04:50:48.870Z",
|
|
9
11
|
"artifact": {
|
|
10
|
-
"filename": "process-guardian",
|
|
11
|
-
"sha256": "
|
|
12
|
-
"size":
|
|
12
|
+
"filename": "process-guardian.node",
|
|
13
|
+
"sha256": "783a65f0de287a1c02cd5429dbe97bc8633f4cc8a7b92e3e961180b9bdcff814",
|
|
14
|
+
"size": 444248
|
|
13
15
|
},
|
|
14
16
|
"provenance": {
|
|
15
17
|
"language": "Rust",
|
|
16
18
|
"cargoLock": "native/process-guardian/Cargo.lock",
|
|
17
19
|
"sourceRoot": "native/process-guardian",
|
|
18
20
|
"signatureStatus": "ci-attestation-required",
|
|
19
|
-
"identityCheck": "
|
|
21
|
+
"identityCheck": "mach-o-header"
|
|
20
22
|
}
|
|
21
23
|
}
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Third-party notices for the A1 terminal host
|
|
2
2
|
|
|
3
|
-
The terminal-host
|
|
3
|
+
The terminal-host Node-API addon includes the following components.
|
|
4
4
|
|
|
5
5
|
==============================================================================
|
|
6
6
|
anyhow 1.0.104
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schema": "a1-terminal-host-artifact-
|
|
2
|
+
"schema": "a1-terminal-host-artifact-v2",
|
|
3
|
+
"kind": "node-addon",
|
|
4
|
+
"nodeApi": 9,
|
|
3
5
|
"protocolVersion": 1,
|
|
4
6
|
"generation": 1,
|
|
5
7
|
"crateVersion": "0.1.0-dev",
|
|
@@ -7,21 +9,21 @@
|
|
|
7
9
|
"architecture": "x64",
|
|
8
10
|
"target": "x86_64-apple-darwin",
|
|
9
11
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-10-
|
|
12
|
+
"builtAt": "2026-10-19T04:54:11.822Z",
|
|
11
13
|
"artifact": {
|
|
12
|
-
"filename": "terminal-host",
|
|
13
|
-
"sha256": "
|
|
14
|
-
"size":
|
|
14
|
+
"filename": "terminal-host.node",
|
|
15
|
+
"sha256": "1a5465ea12ecfe76d5f0c8fd5c11dee99597b4206a7e9ece01b37fc5c477c211",
|
|
16
|
+
"size": 5656912
|
|
15
17
|
},
|
|
16
|
-
"
|
|
17
|
-
"filename": "
|
|
18
|
-
"sha256": "
|
|
19
|
-
"size":
|
|
18
|
+
"entry": {
|
|
19
|
+
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
20
|
+
"sha256": "4b1f702cc9980ffa531cf3e9963b884eaa35120d04d5c195ea3308f501d8d557",
|
|
21
|
+
"size": 1315
|
|
20
22
|
},
|
|
21
23
|
"notices": {
|
|
22
24
|
"filename": "terminal-host-THIRD-PARTY-NOTICES.txt",
|
|
23
|
-
"sha256": "
|
|
24
|
-
"size":
|
|
25
|
+
"sha256": "ebddf265d324c1393ba2f52b5fa411709ae2179bfe1aa7bb5bd84f0adc4ccef0",
|
|
26
|
+
"size": 552220
|
|
25
27
|
},
|
|
26
28
|
"provenance": {
|
|
27
29
|
"language": "Rust",
|
|
@@ -34,12 +36,11 @@
|
|
|
34
36
|
"zigVersion": "0.15.2",
|
|
35
37
|
"rustVersion": "rustc 1.99.1 (58cf8f9c3 2026-10-13)",
|
|
36
38
|
"features": {
|
|
37
|
-
"
|
|
38
|
-
"sessionLock": [
|
|
39
|
+
"addon": [
|
|
39
40
|
"node-addon"
|
|
40
41
|
]
|
|
41
42
|
},
|
|
42
43
|
"signatureStatus": "ci-attestation-required",
|
|
43
|
-
"identityCheck": "
|
|
44
|
+
"identityCheck": "mach-o-header"
|
|
44
45
|
}
|
|
45
46
|
}
|
|
Binary file
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schema": "a1-process-guardian-artifact-
|
|
3
|
-
"
|
|
2
|
+
"schema": "a1-process-guardian-artifact-v2",
|
|
3
|
+
"kind": "node-addon",
|
|
4
|
+
"nodeApi": 9,
|
|
5
|
+
"protocolVersion": 2,
|
|
4
6
|
"crateVersion": "0.1.0-dev",
|
|
5
7
|
"platform": "linux",
|
|
6
8
|
"architecture": "x64",
|
|
7
9
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-10-
|
|
10
|
+
"builtAt": "2026-10-19T04:50:20.956Z",
|
|
9
11
|
"artifact": {
|
|
10
|
-
"filename": "process-guardian",
|
|
11
|
-
"sha256": "
|
|
12
|
-
"size":
|
|
12
|
+
"filename": "process-guardian.node",
|
|
13
|
+
"sha256": "118e5954e3864672bd8482ab7edf419cb27dcdf2533720316979b7f5cc5a53a5",
|
|
14
|
+
"size": 491520
|
|
13
15
|
},
|
|
14
16
|
"provenance": {
|
|
15
17
|
"language": "Rust",
|