@timurproko/a1 0.1.1-dev.7 → 0.1.1-dev.9

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 (49) hide show
  1. package/README.md +11 -74
  2. package/bin/{a1-guardian.js → guardian.js} +1 -1
  3. package/bin/{a1-ui.js → ui.js} +1 -8
  4. package/dist/native/darwin-arm64/manifest.json +3 -3
  5. package/dist/native/darwin-arm64/{a1-process-guardian → process-guardian} +0 -0
  6. package/dist/native/linux-x64/manifest.json +3 -3
  7. package/dist/native/linux-x64/{a1-process-guardian → process-guardian} +0 -0
  8. package/dist/native/win32-x64/manifest.json +3 -3
  9. package/dist/native/win32-x64/process-guardian.exe +0 -0
  10. package/dist/src/composition/index.js +7 -1
  11. package/dist/src/features/launch/initialize-profile.js +23 -2
  12. package/dist/src/features/launch/profiles.d.ts +1 -1
  13. package/dist/src/features/launch/profiles.js +2 -2
  14. package/dist/src/features/launch/runtime-selection.d.ts +13 -9
  15. package/dist/src/features/launch/runtime-selection.js +6 -6
  16. package/dist/src/features/owned-ui/settings-app.js +6 -4
  17. package/dist/src/foundation/pi-component-adapter/index.d.ts +1 -1
  18. package/dist/src/foundation/pi-component-adapter/index.js +1 -1
  19. package/dist/src/foundation/pi-component-adapter/upstream/theme/theme.d.ts +8 -0
  20. package/dist/src/foundation/pi-component-adapter/upstream/theme/theme.js +12 -0
  21. package/dist/src/foundation/pi-engine-adapter/adapter.d.ts +5 -0
  22. package/dist/src/foundation/pi-engine-adapter/adapter.js +7 -0
  23. package/dist/src/foundation/pi-engine-adapter/settings-integration.d.ts +0 -2
  24. package/dist/src/foundation/pi-engine-adapter/settings-integration.js +24 -37
  25. package/dist/src/foundation/release/bootstrap.js +2 -2
  26. package/dist/src/foundation/ui-components/list-view.js +4 -4
  27. package/dist/src/foundation/ui-components/theme.d.ts +6 -0
  28. package/dist/src/foundation/ui-components/theme.js +1 -0
  29. package/dist/src/product-identity.json +7 -7
  30. package/docs/architecture/process-guardian-provenance.md +5 -5
  31. package/docs/architecture/toolchain.md +3 -3
  32. package/docs/ci-release-runbook.md +10 -2
  33. package/docs/manual-terminal-colour-check.md +55 -0
  34. package/package.json +9 -7
  35. package/dist/native/win32-x64/a1-process-guardian.exe +0 -0
  36. package/dist/src/composition/transparent-runtime.d.ts +0 -6
  37. package/dist/src/composition/transparent-runtime.js +0 -19
  38. package/dist/src/foundation/transparent-terminal/command-resolution.d.ts +0 -13
  39. package/dist/src/foundation/transparent-terminal/command-resolution.js +0 -72
  40. package/dist/src/foundation/transparent-terminal/foreground-broker.d.ts +0 -24
  41. package/dist/src/foundation/transparent-terminal/foreground-broker.js +0 -30
  42. package/dist/src/foundation/transparent-terminal/index.d.ts +0 -4
  43. package/dist/src/foundation/transparent-terminal/index.js +0 -4
  44. package/dist/src/foundation/transparent-terminal/main.d.ts +0 -9
  45. package/dist/src/foundation/transparent-terminal/main.js +0 -87
  46. package/dist/src/foundation/transparent-terminal/native-launcher.d.ts +0 -25
  47. package/dist/src/foundation/transparent-terminal/native-launcher.js +0 -146
  48. /package/bin/{a1.js → cli.js} +0 -0
  49. /package/bin/{a1-supervisor.js → supervisor.js} +0 -0
@@ -86,13 +86,13 @@
86
86
  "piEventFrameParitySchema": "a1-pi-event-frame-parity-v1"
87
87
  },
88
88
  "artifacts": {
89
- "cliEntry": "bin/a1.js",
90
- "supervisorEntry": "bin/a1-supervisor.js",
91
- "guardianEntry": "bin/a1-guardian.js",
92
- "uiEntry": "bin/a1-ui.js",
93
- "nativeExecutable": "a1-terminal-host",
94
- "nativeCrate": "a1-terminal-host",
95
- "processGuardianExecutable": "a1-process-guardian",
89
+ "cliEntry": "bin/cli.js",
90
+ "supervisorEntry": "bin/supervisor.js",
91
+ "guardianEntry": "bin/guardian.js",
92
+ "uiEntry": "bin/ui.js",
93
+ "nativeExecutable": "terminal-host",
94
+ "nativeCrate": "terminal-host",
95
+ "processGuardianExecutable": "process-guardian",
96
96
  "releaseTarballStem": "timurproko-a1",
97
97
  "diagnosticStem": "a1"
98
98
  }
@@ -1,6 +1,6 @@
1
1
  # Process guardian provenance
2
2
 
3
- A1 launch-instance containment uses a standalone native executable named `a1-process-guardian`. The approved implementation is a separate Rust crate at `native/process-guardian`; it is not a Node-API addon and does not reuse or resume the held composed terminal-host implementation.
3
+ A1 launch-instance containment uses a standalone native executable named `process-guardian`. The approved implementation is a separate Rust crate at `native/process-guardian`; it is not a Node-API addon and does not reuse or resume the held composed terminal-host implementation.
4
4
 
5
5
  ## Boundary
6
6
 
@@ -13,10 +13,10 @@ On Windows it uses `windows-sys 0.61.2` to create a Job Object, enables `JOB_OBJ
13
13
  Release builds place the exact executable at:
14
14
 
15
15
  ```text
16
- dist/native/win32-x64/a1-process-guardian.exe
17
- dist/native/linux-x64/a1-process-guardian
18
- dist/native/darwin-x64/a1-process-guardian
19
- dist/native/darwin-arm64/a1-process-guardian
16
+ dist/native/win32-x64/process-guardian.exe
17
+ dist/native/linux-x64/process-guardian
18
+ dist/native/darwin-x64/process-guardian
19
+ dist/native/darwin-arm64/process-guardian
20
20
  ```
21
21
 
22
22
  Each supported artifact must be named in the release integrity manifest with target triple, crate version, guardian protocol version, SHA-256, byte size, build provenance, and signature/attestation status. A missing, altered, incompatible, or wrong-platform artifact is a concise unsupported-containment launch error; A1 never silently falls back to PID-only cleanup.
@@ -65,15 +65,15 @@ This mandatory candidate workflow checks documented package-root exports and the
65
65
  npm run sync:pi-ui
66
66
  ```
67
67
 
68
- This optional, mutating presentation-maintenance workflow regenerates component and event-frame parity evidence when maintainers deliberately adopt upstream UI changes. Review generated diffs, attribution, source-ledger records, and terminal parity before committing them. It is never an engine candidate acceptance gate and is not run by `check`, `prepack`, or release publication.
68
+ This optional, mutating presentation-maintenance workflow regenerates component and event-frame parity evidence when maintainers deliberately adopt upstream UI changes. Review generated diffs, attribution, and source-ledger records before committing them. It is never an engine candidate acceptance gate and is not run by `check`, `prepack`, or release publication.
69
69
 
70
- `npm run test:pi-terminal-parity` remains an explicit presentation acceptance command. `node scripts/check-pinned-pi-source-ledger.mjs` validates accepted provenance; its `--engine-only` mode validates the ownership partition without comparing private upstream source.
70
+ Presentation acceptance is the reader comparing `a1 pi` with pinned Pi. `node scripts/check-pinned-pi-source-ledger.mjs` validates accepted provenance; its `--engine-only` mode validates the ownership partition without comparing private upstream source.
71
71
 
72
72
  ## Preview publication
73
73
 
74
74
  A preview candidate must use a unique `-dev.N` version and exact manually accepted bytes. Publication packs once, binds evidence to source commit/version/integrity, runs applicable non-desktop gates, publishes under npm `next`, and verifies registry identity. It must keep `latest` unchanged and record physical/cross-platform certification as deferred.
75
75
 
76
- The GitHub trusted-publishing workflow is `.github/workflows/publish-next.yml`. Stable publication remains a separate release process from a clean tagged `master` commit after all mandatory platform gates pass.
76
+ The GitHub trusted-publishing workflow is `.github/workflows/npm-publish.yml`. Stable publication remains a separate release process from a clean tagged `master` commit after all mandatory platform gates pass.
77
77
 
78
78
  ## A1 state paths
79
79
 
@@ -22,10 +22,18 @@ Need more coverage for a risky preview? Dispatch the candidate with `full: true`
22
22
 
23
23
  ## Publish a preview to npm `next`
24
24
 
25
+ One command from a clean `develop` checkout runs the whole chain — candidate build, publish, registry verification:
26
+
27
+ ```sh
28
+ npm run release:next
29
+ ```
30
+
31
+ It dispatches the same trusted workflows below and never publishes local bytes. The manual steps remain the reference path:
32
+
25
33
  1. Make sure `Development validation required` is green on the `develop` tip.
26
34
  2. Dispatch **Build npm next candidate** with the exact commit and `confirm_candidate=build-uncertified-next-candidate`.
27
35
  3. Check the resulting `candidate-evidence.json`: gates passed, package integrity matches.
28
- 4. Approve the `npm-next` environment and dispatch **Publish npm next** with the candidate run id.
36
+ 4. Approve the `npm-publish` environment and dispatch **Publish npm next** with the candidate run id.
29
37
 
30
38
  The publisher uploads the exact validated tarball — it never rebuilds. Candidates expire after 14 days; an expired or mismatched artifact means building a new candidate, not patching the old one.
31
39
 
@@ -35,7 +43,7 @@ The publisher uploads the exact validated tarball — it never rebuilds. Candida
35
43
  2. Dispatch **Build stable candidate** — it packs once and validates the same bytes on all three platforms.
36
44
  3. Dispatch **Certify stable physical platforms** on the dedicated isolated workers (they set `PHYSICAL_WORKER_ISOLATED=true` and run under the `stable-physical` environment). Never run physical host probes on a developer workstation.
37
45
  4. Dispatch **Certify stable candidate** with both run ids. `Stable candidate required` passes only when every verdict binds the same commit, version, and digest.
38
- 5. Merge that exact commit to `master`, tag it `v<version>`, and dispatch **Publish npm stable**, then approve `npm-stable`.
46
+ 5. Merge that exact commit to `master`, tag it `v<version>`, and dispatch **Publish npm stable**, then approve `npm-publish`.
39
47
 
40
48
  Stable artifacts expire after 30 days. The same rule as previews applies: publication still requires exact certified bytes. Never upload locally rebuilt bytes, and never route around certification by rebuilding inside a publisher.
41
49
 
@@ -0,0 +1,55 @@
1
+ # Terminal colour check
2
+
3
+ ## What can go wrong
4
+
5
+ Every colour the interface draws is 24-bit: the pinned dark theme's accent is
6
+ `#8abeb7`, its border `#5f87ff`, its headings `#f0c674`. On Windows, Node decides
7
+ once at startup whether it may send those sequences to the terminal at all. It
8
+ asks the console to enable virtual terminal processing; when that fails it renders
9
+ ANSI itself, and its renderer knows only the sixteen SGR colours. Each 24-bit
10
+ colour is then replaced by the terminal's nearest palette entry, so the whole
11
+ interface shifts at once — accent to the terminal's cyan, headings to its yellow —
12
+ while the application still writes exactly the bytes it always wrote.
13
+
14
+ That decision is made per process, from the console handle the process was given,
15
+ and every process it launches inherits the result. It is a property of how the
16
+ session was started, not of what is drawn.
17
+
18
+ ## Launching so colour survives
19
+
20
+ The installed command is an npm shim — a shell script that `exec`s Node — and that
21
+ shape keeps 24-bit colour. So does `pi`, for the same reason.
22
+
23
+ - Installed: `a1`, `a1 pi`
24
+ - From a checkout: `scripts/dev` and `scripts/dev pi`
25
+
26
+ `npm start` and `npm run start:pi` build first and then launch through the same
27
+ entry, but a package manager on Windows hands the script to `cmd.exe`, which can
28
+ leave the launch without a terminal on its input; the launcher says so and stops
29
+ rather than opening a session that closes as it starts. Running `scripts/dev`
30
+ from the shell is the direct path.
31
+
32
+ A directly launched `node scripts/start-local.mjs` under Git Bash does not, and
33
+ neither does a directly launched `node …/pi/dist/cli.js`: the collapse belongs to
34
+ the launch, not to either application.
35
+
36
+ ## Checking a terminal
37
+
38
+ ```
39
+ node scripts/check-terminal-colour.mjs
40
+ ```
41
+
42
+ It prints the same colour as 24-bit, as a 256-colour index, and as the terminal's
43
+ own palette entry. When the first block is indistinguishable from the third, this
44
+ launch collapses colour and any comparison made in it is a comparison of two wrong
45
+ screens.
46
+
47
+ ## What the gates hold
48
+
49
+ - `test/features/launch/terminal-colour-fidelity.test.ts` holds the emitted bytes:
50
+ the pinned palette resolves to 24-bit sequences, a 256-colour terminal stays on
51
+ indices, and the launch chain keeps inheriting the terminal it was started from
52
+ rather than opening a console of its own.
53
+ - The reader compares `a1 pi` with pinned Pi in the same terminal. Run the colour
54
+ check first: a launch that collapses colour makes both sessions equally wrong, so
55
+ they would agree with each other and disagree with what a user sees.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.1.1-dev.7",
3
+ "version": "0.1.1-dev.9",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.13.0",
7
7
  "bin": {
8
- "a1": "bin/a1.js"
8
+ "a1": "bin/cli.js"
9
9
  },
10
10
  "files": [
11
11
  "bin",
@@ -21,32 +21,33 @@
21
21
  "clean": "node scripts/clean.mjs",
22
22
  "build": "npm run clean && tsc -p tsconfig.build.json && node scripts/build-process-guardian.mjs",
23
23
  "build:process-guardian": "node scripts/build-process-guardian.mjs",
24
- "typecheck": "tsc -p tsconfig.json --noEmit",
24
+ "typecheck": "tsgo -p tsconfig.json --noEmit",
25
25
  "check:architecture": "node scripts/check-architecture.mjs && node scripts/product-identifier-policy.mjs --check && node scripts/check-product-identity-boundaries.mjs && node scripts/check-package-identity.mjs && node scripts/check-pinned-pi-source-ledger.mjs && node scripts/check-terminal-host-provenance.mjs",
26
26
  "check:customization-ready": "node scripts/check-owned-ui-customization-prerequisites.mjs",
27
27
  "check:deprecated": "node scripts/check-deprecated-dependencies.mjs",
28
28
  "branches:prune": "node scripts/prune-merged-branches.mjs",
29
29
  "check": "npm run test:fast",
30
30
  "publish:next": "tsx scripts/publish-next.ts",
31
+ "release:next": "node scripts/release-next.mjs",
31
32
  "test": "npm run test:fast",
32
33
  "test:fast": "node scripts/run-validation-tier.mjs typecheck fast",
33
34
  "test:scope": "node scripts/run-validation-tier.mjs",
34
35
  "test:full": "node scripts/run-validation-tier.mjs full-release",
35
36
  "test:release": "node scripts/run-release-gates.mjs",
36
37
  "test:terminal-host": "node scripts/run-terminal-host-probe.mjs",
37
- "test:pi-terminal-parity": "npm run build --silent && node scripts/run-pi-terminal-parity.mjs",
38
38
  "test:pi-engine-conformance": "vitest run test/foundation/pi-engine-adapter/conformance.test.ts test/foundation/pi-engine-adapter/runtime-integration.test.ts test/foundation/pi-engine-adapter/session-integration.test.ts",
39
39
  "report:pi-engine-conformance": "npm run build --silent && node scripts/run-pi-engine-conformance.mjs",
40
40
  "sync:pi-ui": "npm run update:pi-component-parity && npm run update:pi-event-frame-parity && npm run update:pi-settings-metadata",
41
41
  "update:pi-component-parity": "tsx scripts/update-pi-component-parity.ts",
42
42
  "update:pi-event-frame-parity": "tsx scripts/update-pi-event-frame-parity.ts",
43
43
  "proof:terminal-host": "node scripts/run-terminal-host.mjs",
44
- "start": "npm run build && node scripts/start-local.mjs",
44
+ "start": "npm run build && node scripts/dev-launch.mjs",
45
+ "start:pi": "npm run build && node scripts/dev-launch.mjs pi",
46
+ "start:sandbox": "npm run build && node scripts/dev-launch.mjs sandbox",
45
47
  "prepack": "node scripts/prepack-gate.mjs",
46
48
  "prepublishOnly": "npm run test:release",
47
49
  "prepare": "npm run build",
48
- "update:pi-settings-metadata": "node scripts/update-pi-settings-metadata.mjs",
49
- "clean:worktrees": "node scripts/clean-worktrees.mjs"
50
+ "update:pi-settings-metadata": "node scripts/update-pi-settings-metadata.mjs"
50
51
  },
51
52
  "dependencies": {
52
53
  "@earendil-works/pi-coding-agent": "0.84.2",
@@ -59,6 +60,7 @@
59
60
  "@types/cross-spawn": "6.0.6",
60
61
  "@types/node": "24.2.1",
61
62
  "@types/semver": "7.8.0",
63
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
62
64
  "@xterm/headless": "6.0.0",
63
65
  "node-pty": "1.1.0",
64
66
  "tsx": "4.20.3",
@@ -1,6 +0,0 @@
1
- import type { LaunchProfileId } from "../foundation/lifecycle/index.js";
2
- export declare function selectTransparentChild(environment?: NodeJS.ProcessEnv, executable?: string, compositionUrl?: string): {
3
- readonly executable: string;
4
- readonly arguments: readonly string[];
5
- };
6
- export declare function runSelectedTransparentRuntime(profileId: Exclude<LaunchProfileId, "a1">, environment?: NodeJS.ProcessEnv): Promise<number>;
@@ -1,19 +0,0 @@
1
- import { fileURLToPath } from "node:url";
2
- import { runTransparentForeground } from "../foundation/transparent-terminal/index.js";
3
- export function selectTransparentChild(environment = process.env, executable = process.execPath, compositionUrl = import.meta.url) {
4
- const entry = fileURLToPath(new URL("../foundation/pi-engine-adapter/public-main-entry.js", compositionUrl));
5
- return { executable, arguments: [entry, ...parseSelectedArguments(environment.A1_LAUNCH_ARGUMENTS_JSON)] };
6
- }
7
- export async function runSelectedTransparentRuntime(profileId, environment = process.env) {
8
- const child = selectTransparentChild(environment);
9
- return await runTransparentForeground({ profileId, environment, ...child });
10
- }
11
- function parseSelectedArguments(source) {
12
- if (!source)
13
- return [];
14
- const value = JSON.parse(source);
15
- if (!Array.isArray(value) || value.some(item => typeof item !== "string")) {
16
- throw new TypeError("A1_LAUNCH_ARGUMENTS_JSON must be a JSON array of strings");
17
- }
18
- return value;
19
- }
@@ -1,13 +0,0 @@
1
- export interface ResolvedTransparentCommand {
2
- readonly executable: string;
3
- readonly arguments: readonly string[];
4
- readonly source: "exact" | "path" | "npm-windows-shim";
5
- }
6
- export interface TransparentCommandResolutionOptions {
7
- readonly platform?: NodeJS.Platform;
8
- readonly cwd: string;
9
- readonly environment: Readonly<Record<string, string>>;
10
- readonly nodeExecutable?: string;
11
- }
12
- /** Resolve a command without invoking a command interpreter. */
13
- export declare function resolveTransparentCommand(executable: string, arguments_: readonly string[], options: TransparentCommandResolutionOptions): Promise<ResolvedTransparentCommand>;
@@ -1,72 +0,0 @@
1
- import { access, readFile } from "node:fs/promises";
2
- import { extname, isAbsolute, resolve } from "node:path";
3
- /** Resolve a command without invoking a command interpreter. */
4
- export async function resolveTransparentCommand(executable, arguments_, options) {
5
- const platform = options.platform ?? process.platform;
6
- return platform === "win32"
7
- ? await resolveWindowsCommand(executable, arguments_, options)
8
- : { executable, arguments: [...arguments_], source: "exact" };
9
- }
10
- async function resolveWindowsCommand(command, commandArguments, options) {
11
- const resolved = await resolveWindowsPathCommand(command, options.cwd, options.environment);
12
- const selected = resolved ?? throwCommandNotFound(command);
13
- if (extname(selected).toLowerCase() !== ".cmd") {
14
- return { executable: selected, arguments: [...commandArguments], source: "path" };
15
- }
16
- return await unwrapNpmWindowsShim(selected, commandArguments, options.nodeExecutable ?? process.execPath);
17
- }
18
- async function resolveWindowsPathCommand(executable, cwd, environment) {
19
- const hasPath = isAbsolute(executable) || executable.includes("/") || executable.includes("\\");
20
- const roots = hasPath ? [cwd] : windowsPath(environment).split(";").filter(Boolean);
21
- const extension = extname(executable);
22
- const extensions = extension ? [""] : windowsExecutableExtensions(environment);
23
- for (const root of roots) {
24
- const base = hasPath ? resolve(cwd, executable) : resolve(root.replace(/^"|"$/g, ""), executable);
25
- for (const suffix of extensions) {
26
- const candidate = `${base}${suffix}`;
27
- if (await isFile(candidate))
28
- return candidate;
29
- }
30
- }
31
- return null;
32
- }
33
- async function unwrapNpmWindowsShim(shimPath, arguments_, fallbackNodeExecutable) {
34
- const source = (await readFile(shimPath, "utf8")).replace(/^\uFEFF/, "");
35
- const targetMatch = source.match(/"%_prog%"\s+"%dp0%\\([^"\r\n]+)"\s+%\*/i);
36
- if (!targetMatch?.[1]) {
37
- throw Object.assign(new Error(`transparent launch refuses arbitrary Windows command script ${shimPath}; configure an executable binary instead`), { code: "UNSUPPORTED_WINDOWS_COMMAND_SHIM" });
38
- }
39
- const shimRoot = resolve(shimPath, "..");
40
- const target = resolve(shimRoot, targetMatch[1].replaceAll("\\", "/"));
41
- if (!await isFile(target))
42
- throw Object.assign(new Error(`npm command shim target is missing: ${target}`), { code: "ENOENT" });
43
- const localNode = resolve(shimRoot, "node.exe");
44
- const nodeExecutable = await isFile(localNode) ? localNode : fallbackNodeExecutable;
45
- return {
46
- executable: nodeExecutable,
47
- arguments: [target, ...arguments_],
48
- source: "npm-windows-shim",
49
- };
50
- }
51
- function throwCommandNotFound(command) {
52
- throw Object.assign(new Error(`spawn ${command} ENOENT`), { code: "ENOENT" });
53
- }
54
- function windowsPath(environment) {
55
- return environment.Path ?? environment.PATH ?? "";
56
- }
57
- function windowsExecutableExtensions(environment) {
58
- const source = environment.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
59
- return [...new Set(source.split(";").filter(Boolean).flatMap(value => {
60
- const extension = value.startsWith(".") ? value : `.${value}`;
61
- return [extension, extension.toLowerCase()];
62
- }))];
63
- }
64
- async function isFile(path) {
65
- try {
66
- await access(path);
67
- return true;
68
- }
69
- catch {
70
- return false;
71
- }
72
- }
@@ -1,24 +0,0 @@
1
- import type { NativeProcessIdentity, TransparentTerminalLaunchProfile, TransparentTerminalLifecycleOutcome } from "../lifecycle/index.js";
2
- export type TransparentStopReason = "owner-disconnect" | "user-request" | "update";
3
- export interface TransparentChildHandle {
4
- readonly processIdentity: NativeProcessIdentity;
5
- readonly outcome: Promise<TransparentTerminalLifecycleOutcome>;
6
- stop(reason: TransparentStopReason): Promise<TransparentTerminalLifecycleOutcome>;
7
- }
8
- export interface TransparentNativeLauncher {
9
- launch(profile: TransparentTerminalLaunchProfile): Promise<TransparentChildHandle>;
10
- }
11
- export interface ForegroundBrokerRequest {
12
- readonly profile: TransparentTerminalLaunchProfile;
13
- readonly stopRequested?: Promise<TransparentStopReason>;
14
- }
15
- export interface ForegroundBrokerResult {
16
- readonly processIdentity: NativeProcessIdentity | null;
17
- readonly outcome: TransparentTerminalLifecycleOutcome;
18
- }
19
- /**
20
- * Waits for one directly attached child. Launch-instance registration and
21
- * process-tree containment belong to the outer profile-neutral guardian.
22
- * Ordinary terminal data never enters this object.
23
- */
24
- export declare function runForegroundBroker(request: ForegroundBrokerRequest, launcher: TransparentNativeLauncher): Promise<ForegroundBrokerResult>;
@@ -1,30 +0,0 @@
1
- import { assertNativeProcessIdentity, assertTransparentTerminalLaunchProfile } from "../lifecycle/index.js";
2
- /**
3
- * Waits for one directly attached child. Launch-instance registration and
4
- * process-tree containment belong to the outer profile-neutral guardian.
5
- * Ordinary terminal data never enters this object.
6
- */
7
- export async function runForegroundBroker(request, launcher) {
8
- assertTransparentTerminalLaunchProfile(request.profile);
9
- let handle;
10
- try {
11
- handle = await launcher.launch(request.profile);
12
- assertNativeProcessIdentity(handle.processIdentity);
13
- }
14
- catch (error) {
15
- return {
16
- processIdentity: null,
17
- outcome: { kind: "spawn-error", message: errorMessage(error), code: errorCode(error) },
18
- };
19
- }
20
- const outcome = request.stopRequested
21
- ? await Promise.race([handle.outcome, request.stopRequested.then(async (reason) => await handle.stop(reason))])
22
- : await handle.outcome;
23
- return { processIdentity: handle.processIdentity, outcome };
24
- }
25
- function errorMessage(error) {
26
- return error instanceof Error ? error.message : String(error);
27
- }
28
- function errorCode(error) {
29
- return error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : null;
30
- }
@@ -1,4 +0,0 @@
1
- export * from "./command-resolution.js";
2
- export * from "./foreground-broker.js";
3
- export * from "./main.js";
4
- export * from "./native-launcher.js";
@@ -1,4 +0,0 @@
1
- export * from "./command-resolution.js";
2
- export * from "./foreground-broker.js";
3
- export * from "./main.js";
4
- export * from "./native-launcher.js";
@@ -1,9 +0,0 @@
1
- import { type LaunchProfileId } from "../lifecycle/index.js";
2
- export interface TransparentForegroundOptions {
3
- readonly environment?: NodeJS.ProcessEnv;
4
- readonly profileId?: LaunchProfileId;
5
- readonly cwd?: string;
6
- readonly executable: string;
7
- readonly arguments: readonly string[];
8
- }
9
- export declare function runTransparentForeground(options: TransparentForegroundOptions): Promise<number>;
@@ -1,87 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { realpath } from "node:fs/promises";
3
- import { resolve } from "node:path";
4
- import { assertLaunchProfileId } from "../lifecycle/index.js";
5
- import { runForegroundBroker } from "./foreground-broker.js";
6
- import { createPlatformTransparentLauncher } from "./native-launcher.js";
7
- export async function runTransparentForeground(options) {
8
- const environment = { ...(options.environment ?? process.env) };
9
- const profileId = options.profileId ?? environment.A1_LAUNCH_PROFILE ?? "a1";
10
- assertLaunchProfileId(profileId);
11
- const profile = await launchProfile(environment, options, profileId);
12
- const stop = stopSignal();
13
- try {
14
- const result = await runForegroundBroker({ profile, stopRequested: stop.requested }, createPlatformTransparentLauncher());
15
- if (result.outcome.kind === "exited")
16
- return result.outcome.exitCode;
17
- if (result.outcome.kind === "signaled")
18
- return 1;
19
- if (result.outcome.kind === "stopped" || result.outcome.kind === "detached")
20
- return 0;
21
- throw Object.assign(new Error(result.outcome.message), { code: result.outcome.code ?? undefined });
22
- }
23
- finally {
24
- stop.dispose();
25
- }
26
- }
27
- async function launchProfile(environment, options, profileId) {
28
- const executable = options.executable;
29
- const arguments_ = options.arguments;
30
- const cwd = await realpath(options.cwd ?? process.cwd());
31
- const terminalType = environment.TERM?.trim() || "xterm-256color";
32
- return {
33
- id: `${profileId}-transparent-${randomUUID()}`,
34
- terminalCapability: "transparent",
35
- executable,
36
- arguments: arguments_,
37
- cwd,
38
- environment: selectedChildEnvironment(environment),
39
- terminalType,
40
- dimensions: terminalDimensions(environment),
41
- ownerDisconnect: "stop",
42
- recovery: "none",
43
- surface: "none",
44
- visualReconnection: "none",
45
- };
46
- }
47
- function selectedChildEnvironment(environment) {
48
- const selected = {};
49
- for (const [name, value] of Object.entries(environment)) {
50
- if (value !== undefined && !name.startsWith("A1_"))
51
- selected[name] = value;
52
- }
53
- return selected;
54
- }
55
- function terminalDimensions(environment) {
56
- const columns = positiveInteger(environment.COLUMNS) ?? 80;
57
- const rows = positiveInteger(environment.LINES) ?? 24;
58
- return { columns: Math.min(500, Math.max(2, columns)), rows: Math.min(300, Math.max(1, rows)) };
59
- }
60
- function positiveInteger(value) {
61
- const parsed = Number(value);
62
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
63
- }
64
- function stopSignal() {
65
- let resolveStop;
66
- let settled = false;
67
- const requested = new Promise(resolve => { resolveStop = resolve; });
68
- const request = (reason) => {
69
- if (settled)
70
- return;
71
- settled = true;
72
- resolveStop(reason);
73
- };
74
- const onSigterm = () => request("user-request");
75
- // The attached child shares foreground signal delivery. Keep the broker alive
76
- // without translating or forwarding Ctrl+C.
77
- const onSigint = () => undefined;
78
- process.once("SIGTERM", onSigterm);
79
- process.on("SIGINT", onSigint);
80
- return {
81
- requested,
82
- dispose() {
83
- process.off("SIGTERM", onSigterm);
84
- process.off("SIGINT", onSigint);
85
- },
86
- };
87
- }
@@ -1,25 +0,0 @@
1
- import { type ChildProcess, type SpawnOptions } from "node:child_process";
2
- import type { TransparentTerminalLaunchProfile } from "../lifecycle/index.js";
3
- import type { TransparentChildHandle, TransparentNativeLauncher } from "./foreground-broker.js";
4
- export interface NativeSpawnAdapter {
5
- resolveCommand(profile: TransparentTerminalLaunchProfile): Promise<{
6
- readonly executable: string;
7
- readonly arguments: readonly string[];
8
- }>;
9
- spawn(executable: string, arguments_: readonly string[], options: SpawnOptions): ChildProcess;
10
- observeStartIdentity(child: ChildProcess): Promise<string>;
11
- identityMatches(child: ChildProcess, expectedStartIdentity: string): Promise<boolean>;
12
- stop(child: ChildProcess, force: boolean): Promise<void>;
13
- waitForExit(child: ChildProcess, timeoutMs: number): Promise<boolean>;
14
- }
15
- export declare function createPlatformTransparentLauncher(platform?: NodeJS.Platform, adapter?: NativeSpawnAdapter): TransparentNativeLauncher;
16
- export declare class WindowsTransparentLauncher implements TransparentNativeLauncher {
17
- private readonly adapter;
18
- constructor(adapter?: NativeSpawnAdapter);
19
- launch(profile: TransparentTerminalLaunchProfile): Promise<TransparentChildHandle>;
20
- }
21
- export declare class UnixTransparentLauncher implements TransparentNativeLauncher {
22
- private readonly adapter;
23
- constructor(adapter?: NativeSpawnAdapter);
24
- launch(profile: TransparentTerminalLaunchProfile): Promise<TransparentChildHandle>;
25
- }