@timurproko/a1 0.1.8-dev.134 → 0.1.8-dev.137

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/bin/cli.js CHANGED
@@ -11,12 +11,23 @@ const capabilities = cliCapabilities(JSON.parse(await readFile(new URL("package.
11
11
 
12
12
  process.exitCode = await dispatchCli(process.argv.slice(2), {
13
13
  launch: async intent => {
14
- const [{ prepareInteractiveLaunch }, { runBootstrap }] = await Promise.all([
14
+ const [{ prepareInteractiveLaunch }, { runBootstrap }, { healModuleIdentityAtLaunch, releaseCopyIsLaunchable }] = await Promise.all([
15
15
  import("../dist/features/launch/index.js"),
16
16
  import("../dist/foundation/release/index.js"),
17
+ import("./module-identity.js"),
17
18
  ]);
19
+ // Self-heal the installed tree before the release store copies it: npm 12
20
+ // blocks install scripts by default, so the postinstall that normally
21
+ // repairs module identity may never have run (see bin/module-identity.js).
22
+ const packageRootPath = fileURLToPath(packageRoot);
23
+ await healModuleIdentityAtLaunch(packageRootPath, message => process.stderr.write(message));
18
24
  const prepared = await prepareInteractiveLaunch(intent);
19
- return await runBootstrap({ packageRoot: fileURLToPath(packageRoot), launchIntent: intent, environment: prepared.environment });
25
+ return await runBootstrap({
26
+ packageRoot: packageRootPath,
27
+ launchIntent: intent,
28
+ environment: prepared.environment,
29
+ releaseIsLaunchable: releaseCopyIsLaunchable,
30
+ });
20
31
  },
21
32
  version: async () => {
22
33
  const { runVersionStats } = await import("../dist/cli/index.js");
@@ -132,6 +132,29 @@ function message(error) {
132
132
  return error instanceof Error ? error.message : String(error);
133
133
  }
134
134
 
135
+ /**
136
+ * Launch-entry self-heal: when A1 and pinned Pi disagree on the terminal
137
+ * module, rewrite the proxy to the copy Pi resolves before anything copies
138
+ * this tree. npm 12 blocks install scripts unless allowScripts covers the
139
+ * package, so the postinstall that normally rewrites the proxy may never
140
+ * have run and the installed tree may still carry the published placeholder
141
+ * path. Named neutrally so the launch entries that call it stay free of
142
+ * terminal implementation identifiers, as the bootstrap boundary requires.
143
+ */
144
+ export async function healModuleIdentityAtLaunch(packageRoot, warn) {
145
+ if (inspectPiTuiModuleIdentity(packageRoot).kind === "unified") return;
146
+ const { syncPiTuiProxy } = await import("./sync-pi-tui-proxy.js");
147
+ const outcome = syncPiTuiProxy(packageRoot);
148
+ if (outcome.kind === "unresolved") {
149
+ warn(`a1: could not point the terminal module proxy at pinned Pi's copy (${outcome.message}); extension UI may not render.\n`);
150
+ }
151
+ }
152
+
153
+ /** Whether a materialized release copy resolves one terminal module for both A1 and Pi. */
154
+ export function releaseCopyIsLaunchable(releaseRoot) {
155
+ return inspectPiTuiModuleIdentity(releaseRoot).kind === "unified";
156
+ }
157
+
135
158
  /** Launch-entry wrapper: warn on stderr when the two sides disagree. */
136
159
  export function assertSinglePiTuiModuleAtLaunch(packageRoot, warn) {
137
160
  const outcome = inspectPiTuiModuleIdentity(packageRoot);
@@ -13,6 +13,16 @@ export interface BootstrapOptions {
13
13
  };
14
14
  readonly environment?: NodeJS.ProcessEnv;
15
15
  readonly output?: Pick<NodeJS.WriteStream, "write">;
16
+ /**
17
+ * Whether a materialized release copy is fit to launch. The active release
18
+ * is normally reused while its version matches the installation, but a copy
19
+ * can be broken in ways a version cannot see — materialized from a tree a
20
+ * blocked postinstall never finished repairing. The probe is injected from
21
+ * the bin entry because deciding it requires inspecting dependency
22
+ * resolution, which stays out of production code. Absent means every
23
+ * release is fit.
24
+ */
25
+ readonly releaseIsLaunchable?: (releaseRoot: string) => boolean;
16
26
  }
17
27
  export declare function runBootstrap(options: BootstrapOptions): Promise<number>;
18
28
  export declare function certifyMaterializedRelease(release: MaterializedRelease, dataDir: string, verification?: VerifyMaterializedReleaseOptions): Promise<string>;
@@ -46,7 +46,8 @@ export async function runBootstrap(options) {
46
46
  const installedVersion = await readInstalledVersion(options.packageRoot);
47
47
  const activeId = state.references.active;
48
48
  const active = activeId === null ? undefined : state.releases[activeId];
49
- if (active?.approval === "approved" && active.packageVersion === installedVersion) {
49
+ const activeIsLaunchable = active === undefined || (options.releaseIsLaunchable?.(active.releaseRoot) ?? true);
50
+ if (activeIsLaunchable && active?.approval === "approved" && active.packageVersion === installedVersion) {
50
51
  const endpointMatches = endpoint?.releaseId === active.releaseId
51
52
  && endpoint.releaseRoot === active.releaseRoot
52
53
  && endpoint.contentDigest === active.contentDigest;
@@ -73,6 +74,17 @@ export async function runBootstrap(options) {
73
74
  await stateStore.activate(candidate.releaseId);
74
75
  state = await stateStore.read();
75
76
  }
77
+ else if (!activeIsLaunchable && candidate.releaseId !== activeId
78
+ && state.releases[candidate.releaseId]?.approval !== "approved") {
79
+ // The active reference points at a copy that cannot launch, so reusing it
80
+ // is off the table — but an unapproved candidate would lose the selection
81
+ // below to that same broken active (`start-active`). Approving the healed
82
+ // candidate here lets ordinary cohort selection activate it, while a live
83
+ // busy cohort still wins the endpoint checks and keeps its sessions.
84
+ const diagnosticsPath = await certifyMaterializedRelease(candidate, paths.dataDir);
85
+ await stateStore.approve(candidate.releaseId, diagnosticsPath);
86
+ state = await stateStore.read();
87
+ }
76
88
  // The candidate's own endpoint decides whether this launch attaches or starts a supervisor.
77
89
  // A cohort other than this one is not in the way: it listens somewhere else.
78
90
  endpointPaths = resolveCohortEndpoint(paths, candidate.releaseId, environment);
@@ -403,6 +403,19 @@ export async function runSelfUpdate(options) {
403
403
  }
404
404
  transaction = await transactionStore.advance("package-installed");
405
405
  }
406
+ // npm 12 blocks install scripts unless allowScripts covers the package, so
407
+ // the postinstall that points the #pi-tui proxy at the tree npm just built
408
+ // may never have run. Run the shipped script directly: the proxy must be
409
+ // correct before the release store copies this tree. A failure is reported
410
+ // and left to the launch-time self-heal rather than failing the update.
411
+ try {
412
+ const proxySync = await runner(process.execPath, [resolve(packageRoot, "bin", "sync-pi-tui-proxy.js")], { captureStdout: true });
413
+ if (proxySync.code !== 0)
414
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`could not point the #pi-tui proxy at the installed tree (exited ${formatExitCode(proxySync.code)}).`)}\n`);
415
+ }
416
+ catch (error) {
417
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`could not point the #pi-tui proxy at the installed tree: ${errorMessage(error)}`)}\n`);
418
+ }
406
419
  progress.set(70, 75);
407
420
  // Ownership can be reacquired after an interrupted installation (for
408
421
  // example, if bare A1 is launched before the update is resumed). Recheck
@@ -5,7 +5,7 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "arm64",
7
7
  "capability": "unsupported",
8
- "builtAt": "2026-08-25T18:22:19.065Z",
8
+ "builtAt": "2026-08-26T06:22:58.678Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "7524bf568992517f50ed53196c861c5edeba0d7275633bb4735ab45bd5963a28",
@@ -5,7 +5,7 @@
5
5
  "platform": "linux",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-08-25T18:22:33.863Z",
8
+ "builtAt": "2026-08-26T06:22:53.598Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "ee8a00eaaf79c707459bbbfb52518e9739314967049fbe5fa625f36ce33db9ee",
@@ -5,10 +5,10 @@
5
5
  "platform": "win32",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-08-25T18:23:18.046Z",
8
+ "builtAt": "2026-08-26T06:23:46.974Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "2f13a4c73ed082a13cf9947be6f363c3c800eaf40e22ea9ae4674299d7f51cdd",
11
+ "sha256": "7b6350b01d875ee3d0db81a1ed5126df5e4a796b93c9ea146e9f24d1f1ab5a2e",
12
12
  "size": 172544
13
13
  },
14
14
  "provenance": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.1.8-dev.134",
3
+ "version": "0.1.8-dev.137",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.13.0",