@timurproko/a1 0.1.8-dev.127 → 0.1.8-dev.132

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
@@ -6,11 +6,25 @@
6
6
  npm install --global @timurproko/a1@latest
7
7
  ```
8
8
 
9
+ Any channel can also be installed or updated directly with npm, without the
10
+ `a1 update` commands:
11
+
12
+ ```sh
13
+ # dev channel (next tag)
14
+ npm install -g @timurproko/a1@next
15
+
16
+ # stable release
17
+ npm install -g @timurproko/a1@latest
18
+
19
+ # exact version
20
+ npm install -g @timurproko/a1@0.1.8-dev.107
21
+ ```
22
+
9
23
  ## Use
10
24
 
11
25
  ```sh
12
26
  a1 # launch A1 (profile: ~/.a1/agent)
13
- a1 version # show Installed, Release, and Develop versions
27
+ a1 version # show Current, Develop, and Release versions
14
28
  a1 update # install the newest stable release
15
29
  a1 update:develop # install the current development preview
16
30
  a1 update:107 # install numbered preview 107
@@ -14,18 +14,20 @@
14
14
  * chrome silently disappears and routed input dead-ends, with no error.
15
15
  *
16
16
  * A1 therefore does not import the specifier at all. Its package declares the
17
- * subpath import `#pi-tui`, resolving to pinned Pi's copy first and to the
18
- * hoisted one only when that is absent which is exactly the case where there
19
- * is one copy and both sides agree anyway. Every A1 module imports `#pi-tui`,
20
- * so which copy A1 uses is stated in package.json and enforced by Node at
21
- * resolution, rather than arranged by rewriting an installed tree.
17
+ * subpath import `#pi-tui`, resolving to bin/pi-tui.js a proxy that
18
+ * re-exports pinned Pi's nested copy. The hop through the proxy is load-bearing:
19
+ * Node rejects package-imports targets containing a `node_modules` path segment
20
+ * (Invalid Package Target) and silently falls through to any fallback, which is
21
+ * exactly how an earlier alias that named the nested path directly reintroduced
22
+ * the split while appearing to declare the opposite. A plain import specifier
23
+ * inside a module carries no such restriction.
22
24
  *
23
- * What remains here is the check that it worked. The alias names one path
24
- * inside pinned Pi; if a future layout moved that copy, resolution would fall
25
- * back to A1's own and the split would returnsilently, which is what made
26
- * this expensive the first time. So launch compares the two resolutions and
27
- * says so when they differ, loudly and once, instead of leaving a user to
28
- * discover it as missing extension UI.
25
+ * What remains here is the check that it worked. The alias is resolved by
26
+ * asking Node itself never by reimplementing resolution, which is how the
27
+ * earlier check reported "unified" for a target Node had rejected and the
28
+ * proxy hop is followed to the module it re-exports. When that disagrees with
29
+ * what pinned Pi resolves, launch says so, loudly and once, instead of leaving
30
+ * a user to discover it as missing extension UI.
29
31
  *
30
32
  * This lives in bin/ (shipped, plain JS) because it inspects dependency
31
33
  * resolution, which the Pi API boundary policy rightly forbids ordinary
@@ -39,32 +41,41 @@ import { pathToFileURL } from "node:url";
39
41
  /**
40
42
  * What A1's own modules resolve `#pi-tui` to, as a real path.
41
43
  *
42
- * The alias is read from the manifest and its entries tried in order, which is
43
- * what Node does for a subpath import: a relative target is a file within the
44
- * package, and a bare one goes through ordinary resolution. Asking Node
45
- * directly is not an option here `import.meta.resolve` ignores the parent it
46
- * is given unless an experimental flag is set, so it would always answer for
47
- * the running process rather than for the installation being inspected.
44
+ * Node answers for the alias itself (require.resolve honors package imports for
45
+ * the package the parent belongs to). When the answer is A1's proxy file, the
46
+ * module it re-exports is what A1 actually renders with, so the proxy's one
47
+ * static `export * from` specifier is followed to its target.
48
48
  */
49
49
  function resolveOwnPiTui(packageRoot) {
50
- const manifest = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
51
- const alias = manifest.imports?.["#pi-tui"];
52
- const targets = Array.isArray(alias) ? alias : alias === undefined ? [] : [alias];
53
- if (targets.length === 0) throw new Error("package.json declares no #pi-tui alias");
54
- for (const target of targets) {
55
- if (typeof target !== "string") continue;
56
- if (target.startsWith("./") || target.startsWith("../")) {
57
- const candidate = join(packageRoot, target);
58
- if (existsSync(candidate)) return canonical(candidate);
59
- continue;
60
- }
61
- try {
62
- return canonical(createRequire(pathToFileURL(join(packageRoot, "package.json")).href).resolve(target));
63
- } catch {
64
- continue;
65
- }
50
+ const requireFromRoot = createRequire(pathToFileURL(join(packageRoot, "package.json")).href);
51
+ let resolved;
52
+ try {
53
+ resolved = requireFromRoot.resolve("#pi-tui");
54
+ } catch (error) {
55
+ throw new Error(`#pi-tui does not resolve: ${message(error)}`);
56
+ }
57
+ return canonical(followProxyReExport(resolved));
58
+ }
59
+
60
+ /**
61
+ * Follow A1's proxy hop: one relative `export * from "..."` per file, at most
62
+ * one hop. A resolution that is not the proxy (or any file without such a
63
+ * re-export) is returned as-is.
64
+ */
65
+ function followProxyReExport(resolvedPath) {
66
+ let source;
67
+ try {
68
+ source = readFileSync(resolvedPath, "utf8");
69
+ } catch {
70
+ return resolvedPath;
71
+ }
72
+ const reExport = source.match(/^export \* from "(\.\.?\/[^"]+)";?$/m);
73
+ if (!reExport) return resolvedPath;
74
+ const target = join(dirname(resolvedPath), reExport[1]);
75
+ if (!existsSync(target)) {
76
+ throw new Error(`#pi-tui proxy ${resolvedPath} re-exports a missing file: ${target}`);
66
77
  }
67
- throw new Error(`no #pi-tui target resolves: ${targets.join(", ")}`);
78
+ return target;
68
79
  }
69
80
 
70
81
  /**
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Types for the `#pi-tui` proxy (see ./pi-tui.js): the alias resolves to that
3
+ * file, so TypeScript reads this declaration and follows it to pinned Pi's
4
+ * nested pi-tui copy — the same module the proxy re-exports at runtime.
5
+ */
6
+ export * from "../node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui/dist/index.js";
package/bin/pi-tui.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The one pi-tui module A1 uses: pinned Pi's own copy.
3
+ *
4
+ * A1's `#pi-tui` alias resolves here, and this file re-exports the copy nested
5
+ * inside pinned Pi — the same one Pi's extension loader hands to extensions, so
6
+ * prototype patches and `instanceof` checks land on the classes A1 renders with.
7
+ *
8
+ * The alias cannot name that copy directly: Node rejects any package-imports
9
+ * target containing a `node_modules` path segment (Invalid Package Target) and
10
+ * silently falls through to the next entry, which resolves the hoisted root
11
+ * copy and reintroduces the two-identity split this file exists to prevent. A
12
+ * plain import specifier in a module carries no such restriction, so the hop
13
+ * through this file is what makes the nested copy nameable from the manifest.
14
+ *
15
+ * When the nested copy is missing (a layout pinned Pi's shrinkwrap does not
16
+ * produce), this import fails loudly at launch instead of silently rendering
17
+ * without extension chrome. bin/module-identity.js reports the same condition
18
+ * before the composition loads.
19
+ *
20
+ * This lives in bin/ (shipped, plain JS) because it names a path inside
21
+ * node_modules, which the Pi API boundary policy rightly forbids ordinary
22
+ * production code from doing.
23
+ */
24
+ export * from "../node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui/dist/index.js";
@@ -21,7 +21,7 @@ export async function runVersionStats(options) {
21
21
  return 1;
22
22
  }
23
23
  const remote = await queryDistTags(runner);
24
- output.stdout(`Installed: ${installed}\nRelease: ${remote.release ?? "unavailable"}\nDevelop: ${remote.develop ?? "unavailable"}\n`);
24
+ output.stdout(`Current: ${installed}\nDevelop: ${remote.develop ?? "unavailable"}\nRelease: ${remote.release ?? "unavailable"}\n`);
25
25
  if (remote.error)
26
26
  output.stderr(`${PRODUCT_TEXT.diagnostic(`could not resolve npm dist-tags: ${remote.error}`)}\n`);
27
27
  return 0;
@@ -37,7 +37,9 @@ async function queryDistTags(runner) {
37
37
  if (result.code !== 0)
38
38
  return unavailable(`npm exited with status ${result.code ?? "unknown"}`);
39
39
  try {
40
- const metadata = JSON.parse(result.stdout);
40
+ const parsed = JSON.parse(result.stdout);
41
+ // npm 12 wraps `npm view <pkg> dist-tags --json` output in a one-element array; older npm returns the bare object.
42
+ const metadata = Array.isArray(parsed) && parsed.length === 1 ? parsed[0] : parsed;
41
43
  if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata))
42
44
  throw new TypeError("npm returned a non-object dist-tags value");
43
45
  const tags = metadata;
@@ -5,7 +5,7 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "arm64",
7
7
  "capability": "unsupported",
8
- "builtAt": "2026-08-25T16:32:02.507Z",
8
+ "builtAt": "2026-08-25T17:36:44.842Z",
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-25T16:32:02.752Z",
8
+ "builtAt": "2026-08-25T17:37:08.874Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "29e22fe29de2828982bc67ef418c4adcaab1490281477b7bea592ec6fe621bcd",
@@ -5,10 +5,10 @@
5
5
  "platform": "win32",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-08-25T16:33:25.671Z",
8
+ "builtAt": "2026-08-25T17:37:16.993Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "eeceadd71235bbb8b39caec42a55216b6b48f3822b8cc9ac94fe4932d74bb5de",
11
+ "sha256": "b9edc03a251b1c0e851ff55227385f016241fc4f33aa0f5ee5f0415443d6a927",
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.127",
3
+ "version": "0.1.8-dev.132",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.13.0",
@@ -8,10 +8,7 @@
8
8
  "a1": "bin/cli.js"
9
9
  },
10
10
  "imports": {
11
- "#pi-tui": [
12
- "./node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui/dist/index.js",
13
- "@earendil-works/pi-tui"
14
- ]
11
+ "#pi-tui": "./bin/pi-tui.js"
15
12
  },
16
13
  "files": [
17
14
  "bin",