@timurproko/a1 0.1.8-dev.820b4a4 → 0.1.8-dev.b449b5c
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 +8 -2
- package/bin/cli.js +2 -2
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/src/cli/dispatch.d.ts +2 -1
- package/dist/src/cli/dispatch.js +22 -3
- package/dist/src/foundation/release/update.d.ts +2 -0
- package/dist/src/foundation/release/update.js +57 -8
- package/docs/ci-release-runbook.md +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npm install --global @timurproko/a1@latest
|
|
|
12
12
|
a1 # A1-owned UI and profile: ~/.a1/agent
|
|
13
13
|
a1 version # show Installed, Release (latest), and Next versions
|
|
14
14
|
a1 update # update to npm latest
|
|
15
|
-
a1 update:next # update to npm next
|
|
15
|
+
a1 update:next # update to npm next (or a1 update:<commit> for a specific preview)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
Prerelease builds — what `a1 update:next` installs — add two development profiles
|
|
@@ -68,9 +68,15 @@ time and never committed, so previews cost no commits and need no command, and a
|
|
|
68
68
|
installed preview says exactly which source produced it.
|
|
69
69
|
|
|
70
70
|
```sh
|
|
71
|
-
a1 update:next
|
|
71
|
+
a1 update:next # install the newest preview
|
|
72
|
+
a1 update:7eabe9e # install the preview built from that commit
|
|
73
|
+
a1 update:0.1.8-dev.7eabe9e # or name the version outright
|
|
72
74
|
```
|
|
73
75
|
|
|
76
|
+
Naming a commit is what the version suffix is for: read it off `a1 version`, a
|
|
77
|
+
pull request, or a commit list, and install exactly that build. A commit that was
|
|
78
|
+
never published is refused rather than guessed at.
|
|
79
|
+
|
|
74
80
|
### Stable — npm `latest`
|
|
75
81
|
|
|
76
82
|
One command, from a clean `develop` that matches its remote:
|
package/bin/cli.js
CHANGED
|
@@ -22,9 +22,9 @@ process.exitCode = await dispatchCli(process.argv.slice(2), {
|
|
|
22
22
|
const { runVersionStats } = await import("../dist/src/cli/index.js");
|
|
23
23
|
return await runVersionStats({ packageRoot: fileURLToPath(packageRoot) });
|
|
24
24
|
},
|
|
25
|
-
update: async channel => {
|
|
25
|
+
update: async (channel, target) => {
|
|
26
26
|
const { runSelfUpdate } = await import("../dist/src/foundation/release/index.js");
|
|
27
|
-
return await runSelfUpdate({ packageRoot: fileURLToPath(packageRoot), channel });
|
|
27
|
+
return await runSelfUpdate({ packageRoot: fileURLToPath(packageRoot), channel, ...(target === undefined ? {} : { target }) });
|
|
28
28
|
},
|
|
29
29
|
packages: async request => {
|
|
30
30
|
const [{ runPackageCommand }, { createPiPackagesPort }] = await Promise.all([
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
7
|
"capability": "unsupported",
|
|
8
|
-
"builtAt": "2026-08-24T06:
|
|
8
|
+
"builtAt": "2026-08-24T06:35:58.522Z",
|
|
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-24T06:
|
|
8
|
+
"builtAt": "2026-08-24T06:35:49.229Z",
|
|
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-24T06:
|
|
8
|
+
"builtAt": "2026-08-24T06:36:28.685Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "1d535ec647852d21736769e0d418b80f9a99d02a9721609a13f251300986b9b9",
|
|
12
12
|
"size": 172544
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -5,7 +5,7 @@ export type UpdateChannel = "stable" | "next";
|
|
|
5
5
|
export interface CliHandlers {
|
|
6
6
|
readonly launch: (intent: InteractiveLaunchIntent) => Promise<number>;
|
|
7
7
|
readonly version: () => Promise<number>;
|
|
8
|
-
readonly update: (channel: UpdateChannel) => Promise<number>;
|
|
8
|
+
readonly update: (channel: UpdateChannel, target?: string) => Promise<number>;
|
|
9
9
|
readonly packages: (request: PackageCommandRequest) => Promise<number>;
|
|
10
10
|
}
|
|
11
11
|
export interface CliOutput {
|
|
@@ -21,6 +21,7 @@ export type CliCommand = {
|
|
|
21
21
|
} | {
|
|
22
22
|
readonly kind: "update";
|
|
23
23
|
readonly channel: UpdateChannel;
|
|
24
|
+
readonly target?: string;
|
|
24
25
|
} | {
|
|
25
26
|
readonly kind: "packages";
|
|
26
27
|
readonly request: PackageCommandRequest;
|
package/dist/src/cli/dispatch.js
CHANGED
|
@@ -7,6 +7,7 @@ export function cliUsage(capabilities) {
|
|
|
7
7
|
"version",
|
|
8
8
|
"update [self|<source>|--extensions|--models]",
|
|
9
9
|
"update:next",
|
|
10
|
+
"update:<commit>",
|
|
10
11
|
"install <source>",
|
|
11
12
|
"remove <source>",
|
|
12
13
|
"list",
|
|
@@ -25,7 +26,7 @@ export async function dispatchCli(arguments_, handlers, output, capabilities) {
|
|
|
25
26
|
return await handlers.version();
|
|
26
27
|
if (command.kind === "packages")
|
|
27
28
|
return await handlers.packages(command.request);
|
|
28
|
-
return await handlers.update(command.channel);
|
|
29
|
+
return await handlers.update(command.channel, command.target);
|
|
29
30
|
}
|
|
30
31
|
export function parseCliCommand(arguments_, capabilities) {
|
|
31
32
|
if (arguments_.length === 0)
|
|
@@ -36,8 +37,8 @@ export function parseCliCommand(arguments_, capabilities) {
|
|
|
36
37
|
}
|
|
37
38
|
if (command === "version")
|
|
38
39
|
return withoutArguments(rest, { kind: "version" });
|
|
39
|
-
if (command
|
|
40
|
-
return
|
|
40
|
+
if (command !== undefined && command.startsWith("update:"))
|
|
41
|
+
return parseColonUpdate(command.slice("update:".length), rest);
|
|
41
42
|
if (command === "update")
|
|
42
43
|
return parseUpdate(rest);
|
|
43
44
|
if (command === "install" || command === "remove" || command === "uninstall") {
|
|
@@ -51,6 +52,24 @@ export function parseCliCommand(arguments_, capabilities) {
|
|
|
51
52
|
return { kind: "error", message: `Bare ${PRODUCT_TEXT.commandName} is the ${PRODUCT_TEXT.displayName} agent experience; there is no agent subcommand.` };
|
|
52
53
|
return { kind: "error", message: PRODUCT_TEXT.diagnostic(`received an unknown command: ${command ?? ""}`) };
|
|
53
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* What follows the colon says which build to move to. `next` is the newest
|
|
57
|
+
* preview; anything else names one outright, by the commit it was built from or by
|
|
58
|
+
* its full version — a preview is published as `<version>-dev.<commit>`, so the
|
|
59
|
+
* commit alone is enough to find it.
|
|
60
|
+
*/
|
|
61
|
+
function parseColonUpdate(suffix, rest) {
|
|
62
|
+
if (rest.length > 0)
|
|
63
|
+
return { kind: "error", message: PRODUCT_TEXT.diagnostic("update takes what to move to after the colon, and nothing else.") };
|
|
64
|
+
if (suffix === "next")
|
|
65
|
+
return { kind: "update", channel: "next" };
|
|
66
|
+
if (suffix.length === 0)
|
|
67
|
+
return { kind: "error", message: PRODUCT_TEXT.diagnostic(`update: needs a preview after the colon, as in ${PRODUCT_TEXT.commandName} update:next.`) };
|
|
68
|
+
if (!/^[0-9a-z][0-9a-z.+-]*$/i.test(suffix)) {
|
|
69
|
+
return { kind: "error", message: PRODUCT_TEXT.diagnostic(`received an unusable preview: ${suffix}`) };
|
|
70
|
+
}
|
|
71
|
+
return { kind: "update", channel: "next", target: suffix };
|
|
72
|
+
}
|
|
54
73
|
/**
|
|
55
74
|
* `update` carries both meanings pinned Pi gives it: itself by default, and the
|
|
56
75
|
* profile's packages when a target says so. Pi is refused as a target because A1
|
|
@@ -33,6 +33,8 @@ export interface UpdatePerformanceEvidence {
|
|
|
33
33
|
export interface SelfUpdateOptions {
|
|
34
34
|
packageRoot: string;
|
|
35
35
|
channel?: UpdateChannel;
|
|
36
|
+
/** A specific preview to install, named by its commit or its full version. */
|
|
37
|
+
target?: string;
|
|
36
38
|
environment?: NodeJS.ProcessEnv;
|
|
37
39
|
fileSystem?: UpdateFileSystem;
|
|
38
40
|
output?: UpdateOutput;
|
|
@@ -221,6 +221,56 @@ function createUpdateProgress(output, enabled) {
|
|
|
221
221
|
},
|
|
222
222
|
};
|
|
223
223
|
}
|
|
224
|
+
/** The newest version the channel points at, which is what an unqualified update takes. */
|
|
225
|
+
async function resolveChannelHead(runner, distTag, output) {
|
|
226
|
+
const lookup = await runNpm(runner, ["view", `${PRODUCT_PACKAGE}@${distTag}`, "version"], true, output, `query the npm ${distTag} channel`);
|
|
227
|
+
if (lookup.result === null)
|
|
228
|
+
return { version: null, exitCode: lookup.exitCode };
|
|
229
|
+
const version = validSemver(lookup.result.stdout.trim());
|
|
230
|
+
if (version === null) {
|
|
231
|
+
output.stderr(`${PRODUCT_TEXT.diagnostic(`received a malformed ${distTag} version from npm: ${JSON.stringify(lookup.result.stdout.trim())}.`)}\n`);
|
|
232
|
+
return { version: null, exitCode: 1 };
|
|
233
|
+
}
|
|
234
|
+
return { version, exitCode: 0 };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Resolve a preview the caller named.
|
|
238
|
+
*
|
|
239
|
+
* A preview is published as `<version>-dev.<commit>`, so its commit is enough to
|
|
240
|
+
* say which one is wanted — the version in front of it is not something anyone
|
|
241
|
+
* should have to remember. A full version is accepted too, for anyone reading one
|
|
242
|
+
* back from `a1 version` or a changelog.
|
|
243
|
+
*
|
|
244
|
+
* The published list is the authority: naming a commit that was never published,
|
|
245
|
+
* or one published more than once under different versions, is an error rather
|
|
246
|
+
* than a guess.
|
|
247
|
+
*/
|
|
248
|
+
async function resolveRequestedPreview(runner, requested, output) {
|
|
249
|
+
const lookup = await runNpm(runner, ["view", PRODUCT_PACKAGE, "versions", "--json"], true, output, "list the published versions");
|
|
250
|
+
if (lookup.result === null)
|
|
251
|
+
return { version: null, exitCode: lookup.exitCode };
|
|
252
|
+
let published;
|
|
253
|
+
try {
|
|
254
|
+
published = JSON.parse(lookup.result.stdout.trim() || "[]");
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
output.stderr(`${PRODUCT_TEXT.diagnostic(`received a malformed version list from npm: ${JSON.stringify(lookup.result.stdout.trim())}.`)}\n`);
|
|
258
|
+
return { version: null, exitCode: 1 };
|
|
259
|
+
}
|
|
260
|
+
const versions = (Array.isArray(published) ? published : [published]).filter((value) => typeof value === "string");
|
|
261
|
+
const exact = versions.find(version => version === requested);
|
|
262
|
+
if (exact !== undefined)
|
|
263
|
+
return { version: exact, exitCode: 0 };
|
|
264
|
+
const matches = versions.filter(version => version.endsWith(`-dev.${requested}`));
|
|
265
|
+
if (matches.length === 1)
|
|
266
|
+
return { version: matches[0], exitCode: 0 };
|
|
267
|
+
if (matches.length > 1) {
|
|
268
|
+
output.stderr(`${PRODUCT_TEXT.diagnostic(`found more than one preview for ${requested}: ${matches.join(", ")}. Name the version instead.`)}\n`);
|
|
269
|
+
return { version: null, exitCode: 1 };
|
|
270
|
+
}
|
|
271
|
+
output.stderr(`${PRODUCT_TEXT.diagnostic(`published no preview for ${requested}.`)}\n`);
|
|
272
|
+
return { version: null, exitCode: 1 };
|
|
273
|
+
}
|
|
224
274
|
export async function runSelfUpdate(options) {
|
|
225
275
|
const fileSystem = options.fileSystem ?? defaultFileSystem;
|
|
226
276
|
const output = options.output ?? defaultOutput;
|
|
@@ -249,14 +299,13 @@ export async function runSelfUpdate(options) {
|
|
|
249
299
|
output.stderr(`${PRODUCT_TEXT.diagnostic(`could not read its running package version: ${errorMessage(error)}`)}\n`);
|
|
250
300
|
return 1;
|
|
251
301
|
}
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
302
|
+
const requested = options.target?.trim();
|
|
303
|
+
const resolved = await measure("target-resolution", async () => requested === undefined || requested.length === 0
|
|
304
|
+
? await resolveChannelHead(runner, distTag, output)
|
|
305
|
+
: await resolveRequestedPreview(runner, requested, output));
|
|
306
|
+
if (resolved.version === null)
|
|
307
|
+
return resolved.exitCode;
|
|
308
|
+
const targetVersion = resolved.version;
|
|
260
309
|
output.stdout(`${PRODUCT_TEXT.commandName} update (${UPDATE_CHANNEL_LABELS[channel]}): ${runningVersion} → ${targetVersion}.\n`);
|
|
261
310
|
const progress = createUpdateProgress(output, options.progress ?? (options.output === undefined && process.stdout.isTTY === true));
|
|
262
311
|
const rootLookup = await measure("global-root", async () => await runNpm(runner, ["root", "--global"], true, output, "resolve npm's global package root"));
|
|
@@ -37,7 +37,9 @@ The version is stamped at publish time — `<major.minor.patch>-dev.<short commi
|
|
|
37
37
|
the base taken from whatever `package.json` declares and the suffix from the commit
|
|
38
38
|
being published — and is never written back to the repository. An installed preview
|
|
39
39
|
therefore names the exact source it came from, and rebuilding a commit produces the
|
|
40
|
-
same version rather than a new one.
|
|
40
|
+
same version rather than a new one. That suffix is also how a specific preview is
|
|
41
|
+
installed: `a1 update:<commit>` resolves it against the published list and
|
|
42
|
+
refuses a commit that was never published. `develop` therefore carries one open prerelease version between
|
|
41
43
|
releases, and no commit is ever spent on a preview.
|
|
42
44
|
|
|
43
45
|
One consequence worth knowing: a push that would republish an existing version
|
package/package.json
CHANGED