@timurproko/a1 0.2.7-dev.1228 → 0.2.7-dev.1231

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/README.md CHANGED
@@ -47,6 +47,8 @@ Exact preview:
47
47
  npm x -y -- @timurproko/a1-install --develop 0.1.8-dev.107
48
48
  ```
49
49
 
50
+ The installer needs Node.js 22.19 or later. When npm's global folder is not writable, as with the nodejs.org installer on macOS, it installs under `~/.local` and adds `~/.local/bin` to your shell's PATH; open a new terminal to use `a1`.
51
+
50
52
  <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/assets/readme/separator-dark.svg"><img src="docs/assets/readme/separator.svg" alt="" width="280"></picture></p>
51
53
 
52
54
  ## Launch
@@ -250,6 +250,11 @@ async function installedTargetIsCallable(value) {
250
250
  for (const path of value.launchers) {
251
251
  try {
252
252
  const metadata = await lstat(path);
253
+ // Platform: npm links Unix launchers; one is callable only when it resolves to the exact target entry.
254
+ if (metadata.isSymbolicLink() && process.platform !== "win32") {
255
+ if (await realpath(path) !== await realpath(resolve(value.packageRoot, "bin", "cli.js"))) return false;
256
+ continue;
257
+ }
253
258
  if (!metadata.isFile() || metadata.isSymbolicLink()) return false;
254
259
  if (process.platform !== "win32" && (metadata.mode & 0o111) === 0) return false;
255
260
  const normalized = (await readFile(path, "utf8")).replaceAll("\\", "/");
@@ -1,4 +1,4 @@
1
- import { unwatchFile, watchFile } from "node:fs";
1
+ import { statSync } from "node:fs";
2
2
  import { resolveLaunchProfilePaths } from "../features/launch/profile-paths.js";
3
3
  import { resolveProductPaths } from "../foundation/lifecycle/paths.js";
4
4
  import { PRODUCT_IDENTITY } from "../product-identity.js";
@@ -19,18 +19,20 @@ export function createOwnedSettingsManager(options) {
19
19
  agentSettingLabelOverrides: { fullscreenCopyOnSelect: "Copy on select" },
20
20
  });
21
21
  }
22
- const SHARED_SETTINGS_POLL_MS = 250;
23
22
  /** Rereads the shared settings document whenever its stat changes; returns the stop. */
24
23
  export function watchSharedSettings(settings) {
25
- const file = settings.file;
26
- const listener = () => {
24
+ let last = "";
25
+ const poll = () => {
27
26
  try {
28
- settings.refresh();
27
+ const s = statSync(settings.file);
28
+ if (last !== (last = `${s.ino}:${s.mtimeMs}`))
29
+ settings.refresh();
29
30
  }
30
31
  catch {
31
32
  // Compatibility: an unreadable peer write resolves on the next stat change.
32
33
  }
33
34
  };
34
- watchFile(file, { interval: SHARED_SETTINGS_POLL_MS, persistent: false }, listener);
35
- return () => unwatchFile(file, listener);
35
+ poll();
36
+ const timer = setInterval(poll, 250).unref();
37
+ return () => clearInterval(timer);
36
38
  }
@@ -78,7 +78,7 @@ export declare function prepareUpdateRecoveryCapsule(options: ProtectedPackageRe
78
78
  /** Execute global replacement behind a detached owner and coordinate terminal cancellation. */
79
79
  export declare function runProtectedPackageReplacement(options: ProtectedPackageReplacementOptions): Promise<ProtectedPackageReplacementResult>;
80
80
  export declare function removeUpdateRecoveryCapsule(dataDir: string, transactionId: string): Promise<void>;
81
- /** Classify a complete launcher set without following linked or mixed launcher files. */
81
+ /** Classify a complete launcher set; only an npm Unix link to the exact target entry is followed. */
82
82
  export declare function inspectUpdateLauncherSet(capsule: UpdateRecoveryCapsule, platform?: NodeJS.Platform): Promise<"target" | "recovery" | "unavailable">;
83
83
  /** Remove terminal recovery capsules only after no canonical launcher references them. */
84
84
  export declare function cleanupUpdateRecoveryCapsules(dataDir: string): Promise<void>;
@@ -258,12 +258,20 @@ export async function removeUpdateRecoveryCapsule(dataDir, transactionId) {
258
258
  throw new Error("invalid update recovery transaction identity");
259
259
  await rm(resolve(dataDir, "update-recovery", transactionId), { recursive: true, force: true });
260
260
  }
261
- /** Classify a complete launcher set without following linked or mixed launcher files. */
261
+ /** Classify a complete launcher set; only an npm Unix link to the exact target entry is followed. */
262
262
  export async function inspectUpdateLauncherSet(capsule, platform = process.platform) {
263
263
  let disposition = null;
264
264
  const targetToken = `node_modules/${capsule.packageName}/bin/cli.js`;
265
265
  for (const path of capsule.launchers) {
266
266
  const metadata = await lstat(path).catch(() => null);
267
+ if (metadata?.isSymbolicLink() && platform !== "win32") {
268
+ const linked = await realpath(path).catch(() => null);
269
+ const entry = await realpath(resolve(capsule.packageRoot, "bin", "cli.js")).catch(() => null);
270
+ if (linked === null || linked !== entry || disposition === "recovery")
271
+ return "unavailable";
272
+ disposition = "target";
273
+ continue;
274
+ }
267
275
  if (!metadata?.isFile() || metadata.isSymbolicLink() || (platform !== "win32" && (metadata.mode & 0o111) === 0))
268
276
  return "unavailable";
269
277
  const source = (await readFile(path, "utf8")).replaceAll("\\", "/");
@@ -469,6 +469,13 @@ async function launchersOwnPackage(globalRoot, fileSystem) {
469
469
  try {
470
470
  for (const launcher of updateLauncherPaths(globalRoot)) {
471
471
  const metadata = await (fileSystem.lstat ?? lstat)(launcher);
472
+ // Platform: npm links Unix launchers; one owns the package only when it resolves to the exact entry.
473
+ if (metadata.isSymbolicLink() && process.platform !== "win32") {
474
+ const entry = resolve(globalRoot, ...PRODUCT_PACKAGE.split("/"), "bin", "cli.js");
475
+ if (!samePath(await fileSystem.realpath(launcher), await fileSystem.realpath(entry)))
476
+ return false;
477
+ continue;
478
+ }
472
479
  if (!metadata.isFile() || metadata.isSymbolicLink() || (process.platform !== "win32" && (metadata.mode & 0o111) === 0))
473
480
  return false;
474
481
  if (!(await fileSystem.readFile(launcher)).replaceAll("\\", "/").includes(token))
@@ -7,7 +7,7 @@
7
7
  "platform": "darwin",
8
8
  "architecture": "arm64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-30T07:58:45.715Z",
10
+ "builtAt": "2026-10-31T18:04:55.424Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
13
  "sha256": "a87eaaffcd986b624c8b044b756446cc1bdaf4334a1775b97820542fe34f9fb7",
@@ -9,7 +9,7 @@
9
9
  "architecture": "arm64",
10
10
  "target": "aarch64-apple-darwin",
11
11
  "capability": "supported",
12
- "builtAt": "2026-10-30T08:01:30.327Z",
12
+ "builtAt": "2026-10-31T18:08:37.997Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
15
  "sha256": "4e35835f3af4c0d9ae9582886f3a1f66d8a8bd306014d24af29e979ca504aed6",
@@ -7,7 +7,7 @@
7
7
  "platform": "darwin",
8
8
  "architecture": "x64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-30T07:58:54.109Z",
10
+ "builtAt": "2026-10-31T18:05:02.327Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
13
  "sha256": "783a65f0de287a1c02cd5429dbe97bc8633f4cc8a7b92e3e961180b9bdcff814",
@@ -9,7 +9,7 @@
9
9
  "architecture": "x64",
10
10
  "target": "x86_64-apple-darwin",
11
11
  "capability": "supported",
12
- "builtAt": "2026-10-30T08:03:39.902Z",
12
+ "builtAt": "2026-10-31T18:11:47.607Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
15
  "sha256": "7161d3bde5cf9d1caf2978fe623b6d26dbdc257093a48b2870d510804563a1b3",
@@ -7,7 +7,7 @@
7
7
  "platform": "linux",
8
8
  "architecture": "x64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-30T07:58:30.937Z",
10
+ "builtAt": "2026-10-31T18:04:50.100Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
13
  "sha256": "118e5954e3864672bd8482ab7edf419cb27dcdf2533720316979b7f5cc5a53a5",
@@ -9,7 +9,7 @@
9
9
  "architecture": "x64",
10
10
  "target": "x86_64-unknown-linux-gnu",
11
11
  "capability": "supported",
12
- "builtAt": "2026-10-30T08:01:43.730Z",
12
+ "builtAt": "2026-10-31T18:08:05.383Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
15
  "sha256": "41212856c811ddef6e918e96b49dc957b2d49946d7ef5494962db8d70937d760",
@@ -7,10 +7,10 @@
7
7
  "platform": "win32",
8
8
  "architecture": "x64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-30T07:59:01.471Z",
10
+ "builtAt": "2026-10-31T18:05:42.004Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
- "sha256": "875865ad5b3bd80793a394573029d0388f54becf6a8ecb225b9e67845684b693",
13
+ "sha256": "e67e64206f795842ae72fd9ef77437165a0a7a8892c0256b51658f19aee4375e",
14
14
  "size": 402432
15
15
  },
16
16
  "provenance": {
@@ -9,10 +9,10 @@
9
9
  "architecture": "x64",
10
10
  "target": "x86_64-pc-windows-msvc",
11
11
  "capability": "supported",
12
- "builtAt": "2026-10-30T08:02:29.690Z",
12
+ "builtAt": "2026-10-31T18:08:25.857Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "c25f492b5e735f453e0207bdfd6f43fd36ade4ba7cf86d8e69c2184cf751a873",
15
+ "sha256": "e22f344176b06f44427a2855d21b271a0d2a780aa89003cd90948d9bbcb6b9a3",
16
16
  "size": 6397440
17
17
  },
18
18
  "entry": {