@timurproko/a1 0.1.8-dev.820b4a4 → 0.1.8-dev.8b3a3ab

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
@@ -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,16 @@ 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 # install the newest preview
71
+ a1 update:next # install the newest preview
72
+ a1 update:7eabe9e # install the preview built from that commit
72
73
  ```
73
74
 
75
+ Naming a commit is what the version suffix is for: read it off `a1 version`, a
76
+ pull request, or a commit list, and install exactly that build — you never need to
77
+ know which version it went out under. A commit that was never published is refused
78
+ rather than guessed at. A full preview version works in the same place, so a string
79
+ pasted back from `a1 version` is understood too.
80
+
74
81
  ### Stable — npm `latest`
75
82
 
76
83
  One command, from a clean `develop` that matches its remote:
@@ -86,7 +93,7 @@ It lands the version on `develop` through a pull request that merges itself.
86
93
  **Landing it is what publishes** — the workflow builds, validates the packed release
87
94
  on Windows, Linux, and macOS, publishes to npm `latest` with provenance, and only
88
95
  then writes the `v<version>` tag and records the GitHub Release. The command waits
89
- for that to succeed, then lands the next `-dev.0` version so previews resume
96
+ for that to succeed, then opens the next `-dev` line so previews resume
90
97
  immediately.
91
98
 
92
99
  ```sh
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:21:14.306Z",
8
+ "builtAt": "2026-08-24T08:17:00.366Z",
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:21:16.988Z",
8
+ "builtAt": "2026-08-24T08:17:02.999Z",
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:21:59.348Z",
8
+ "builtAt": "2026-08-24T08:17:34.680Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "a6130ee1d6a5008de29c91aba4ed592381aa9e192faa27a4b7d07b817555fbac",
11
+ "sha256": "aa0e9865017b1b9affda68e22f3e075a17d599c345ffdc59926f5e9b103b250c",
12
12
  "size": 172544
13
13
  },
14
14
  "provenance": {
@@ -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;
@@ -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 === "update:next")
40
- return withoutArguments(rest, { kind: "update", channel: "next" });
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
@@ -1,7 +1,6 @@
1
1
  export * from "./bootstrap.js";
2
2
  export * from "./cohort-selection.js";
3
3
  export * from "./cohort-state.js";
4
- export * from "./development-preview-release.js";
5
4
  export * from "./process-cleanup.js";
6
5
  export * from "./release.js";
7
6
  export * from "./release-gc.js";
@@ -1,7 +1,6 @@
1
1
  export * from "./bootstrap.js";
2
2
  export * from "./cohort-selection.js";
3
3
  export * from "./cohort-state.js";
4
- export * from "./development-preview-release.js";
5
4
  export * from "./process-cleanup.js";
6
5
  export * from "./release.js";
7
6
  export * from "./release-gc.js";
@@ -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,64 @@ 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
+ // Naming a release here would install it through the preview path, which is a
264
+ // different command with a different meaning. The commit form cannot express
265
+ // one, so only the fuller spelling of a preview reaches this.
266
+ if (!exact.includes("-dev.")) {
267
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`${exact} is a release, not a preview; run ${PRODUCT_TEXT.commandName} update to move to the current release.`)}\n`);
268
+ return { version: null, exitCode: 1 };
269
+ }
270
+ return { version: exact, exitCode: 0 };
271
+ }
272
+ const matches = versions.filter(version => version.endsWith(`-dev.${requested}`));
273
+ if (matches.length === 1)
274
+ return { version: matches[0], exitCode: 0 };
275
+ if (matches.length > 1) {
276
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`found more than one preview for ${requested}: ${matches.join(", ")}. Name the version instead.`)}\n`);
277
+ return { version: null, exitCode: 1 };
278
+ }
279
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`published no preview for ${requested}.`)}\n`);
280
+ return { version: null, exitCode: 1 };
281
+ }
224
282
  export async function runSelfUpdate(options) {
225
283
  const fileSystem = options.fileSystem ?? defaultFileSystem;
226
284
  const output = options.output ?? defaultOutput;
@@ -249,15 +307,16 @@ export async function runSelfUpdate(options) {
249
307
  output.stderr(`${PRODUCT_TEXT.diagnostic(`could not read its running package version: ${errorMessage(error)}`)}\n`);
250
308
  return 1;
251
309
  }
252
- const targetLookup = await measure("target-resolution", async () => await runNpm(runner, ["view", `${PRODUCT_PACKAGE}@${distTag}`, "version"], true, output, `query the npm ${distTag} channel`));
253
- if (targetLookup.result === null)
254
- return targetLookup.exitCode;
255
- const targetVersion = validSemver(targetLookup.result.stdout.trim());
256
- if (targetVersion === null) {
257
- output.stderr(`${PRODUCT_TEXT.diagnostic(`received a malformed ${distTag} version from npm: ${JSON.stringify(targetLookup.result.stdout.trim())}.`)}\n`);
258
- return 1;
259
- }
260
- output.stdout(`${PRODUCT_TEXT.commandName} update (${UPDATE_CHANNEL_LABELS[channel]}): ${runningVersion} ${targetVersion}.\n`);
310
+ const requested = options.target?.trim();
311
+ const resolved = await measure("target-resolution", async () => requested === undefined || requested.length === 0
312
+ ? await resolveChannelHead(runner, distTag, output)
313
+ : await resolveRequestedPreview(runner, requested, output));
314
+ if (resolved.version === null)
315
+ return resolved.exitCode;
316
+ const targetVersion = resolved.version;
317
+ // No full stop after a version: it already ends in a dot-separated identifier,
318
+ // and a trailing one reads as part of the version rather than as punctuation.
319
+ output.stdout(`${PRODUCT_TEXT.commandName} update (${UPDATE_CHANNEL_LABELS[channel]}): ${runningVersion} → ${targetVersion}\n`);
261
320
  const progress = createUpdateProgress(output, options.progress ?? (options.output === undefined && process.stdout.isTTY === true));
262
321
  const rootLookup = await measure("global-root", async () => await runNpm(runner, ["root", "--global"], true, output, "resolve npm's global package root"));
263
322
  if (rootLookup.result === null)
@@ -346,7 +405,7 @@ export async function runSelfUpdate(options) {
346
405
  await transactionStore.clearCompleted();
347
406
  options.onPhaseTiming?.({ phase: "transaction-complete", durationMs: Math.max(0, now() - transactionStartedAt) });
348
407
  progress.finish();
349
- output.stdout(`${PRODUCT_TEXT.commandName} updated successfully: ${targetVersion}.\n`);
408
+ output.stdout(`${PRODUCT_TEXT.commandName} updated successfully: ${targetVersion}\n`);
350
409
  return 0;
351
410
  }
352
411
  catch (error) {
@@ -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. `develop` therefore carries one open prerelease version between
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
@@ -52,7 +54,7 @@ npm run release -- patch # or minor, major, or an exact x.y.z
52
54
  ```
53
55
 
54
56
  It lands `x.y.z` on `develop` through a pull request that merges itself, waits for
55
- that publication to succeed, and then lands `x.y.(z+1)-dev.0` so previews resume
57
+ that publication to succeed, and then lands `x.y.(z+1)-dev` so previews resume
56
58
  immediately. It publishes nothing itself and creates no tag.
57
59
 
58
60
  Landing the stable version is what publishes. The same pipeline sees a commit
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.1.8-dev.820b4a4",
3
+ "version": "0.1.8-dev.8b3a3ab",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.13.0",
@@ -1,49 +0,0 @@
1
- export declare const PREVIEW_RELEASE_SCHEMA: string;
2
- export interface DevelopmentPreviewCandidate {
3
- readonly version: string;
4
- readonly requiresVersionCommit: boolean;
5
- }
6
- export interface DevelopmentPreviewRegistryState {
7
- readonly published: boolean;
8
- readonly nextVersion: string | null;
9
- }
10
- export interface DevelopmentPreviewVerificationOptions {
11
- readonly attempts?: number;
12
- readonly delayMs?: number;
13
- readonly delay?: (milliseconds: number) => Promise<void>;
14
- }
15
- export interface DevelopmentPreviewPublishResult {
16
- readonly published: boolean;
17
- readonly recoveredPublishError: unknown | null;
18
- }
19
- export interface UncertifiedDevelopmentPreviewEvidenceInput {
20
- readonly packageName: string;
21
- readonly version: string;
22
- readonly commit: string;
23
- readonly tarball: string;
24
- readonly integrity: string;
25
- readonly shasum: string;
26
- readonly platform: NodeJS.Platform;
27
- readonly architecture: string;
28
- readonly recordedAt: string;
29
- }
30
- export interface UncertifiedDevelopmentPreviewEvidence extends UncertifiedDevelopmentPreviewEvidenceInput {
31
- readonly schema: typeof PREVIEW_RELEASE_SCHEMA;
32
- readonly channel: "next";
33
- readonly certificationStatus: "uncertified-development-preview";
34
- readonly terminalCapability: "owned-ui";
35
- readonly manualAcceptance: "accepted";
36
- readonly physicalHostCertification: "deferred";
37
- readonly crossPlatformCertification: "deferred";
38
- readonly stableReleaseEligible: false;
39
- }
40
- export declare function createUncertifiedDevelopmentPreviewEvidence(input: UncertifiedDevelopmentPreviewEvidenceInput): UncertifiedDevelopmentPreviewEvidence;
41
- export declare function requireManuallyAcceptedDevelopmentPreview(version: string, acceptedVersion: string): void;
42
- export declare function selectDevelopmentPreviewCandidate(currentVersion: string, publishedVersions: readonly string[]): DevelopmentPreviewCandidate;
43
- /**
44
- * Treats npm's process result as provisional: browser-auth completion can fail
45
- * after the immutable upload succeeds. Registry identity remains authoritative.
46
- */
47
- export declare function publishDevelopmentPreviewWithRecovery(publish: () => Promise<void>, verify: () => Promise<void>): Promise<DevelopmentPreviewPublishResult>;
48
- export declare function verifyDevelopmentPreviewRegistry(version: string, observe: () => Promise<DevelopmentPreviewRegistryState>, repairNextTag: () => Promise<void>, options?: DevelopmentPreviewVerificationOptions): Promise<void>;
49
- export declare function developmentPreviewTarballName(packageName: string, version: string): string;
@@ -1,114 +0,0 @@
1
- import { compare, inc, prerelease, valid } from "semver";
2
- import { PRODUCT_IDENTITY } from "../../product-identity.js";
3
- export const PREVIEW_RELEASE_SCHEMA = PRODUCT_IDENTITY.evidence.previewReleaseSchema;
4
- export function createUncertifiedDevelopmentPreviewEvidence(input) {
5
- const prereleaseParts = prerelease(input.version);
6
- if (valid(input.version) === null || prereleaseParts?.[0] !== "dev") {
7
- throw new Error(`uncertified preview requires a development prerelease: ${input.version}`);
8
- }
9
- return {
10
- schema: PREVIEW_RELEASE_SCHEMA,
11
- channel: "next",
12
- certificationStatus: "uncertified-development-preview",
13
- terminalCapability: "owned-ui",
14
- manualAcceptance: "accepted",
15
- physicalHostCertification: "deferred",
16
- crossPlatformCertification: "deferred",
17
- stableReleaseEligible: false,
18
- ...input,
19
- };
20
- }
21
- export function requireManuallyAcceptedDevelopmentPreview(version, acceptedVersion) {
22
- const acceptedPrerelease = prerelease(acceptedVersion);
23
- if (valid(acceptedVersion) === null || acceptedPrerelease?.[0] !== "dev") {
24
- throw new Error(`invalid manually accepted development preview: ${acceptedVersion}`);
25
- }
26
- if (version !== acceptedVersion) {
27
- throw new Error(`development preview ${version} has no exact manual acceptance; accepted version is ${acceptedVersion}`);
28
- }
29
- }
30
- export function selectDevelopmentPreviewCandidate(currentVersion, publishedVersions) {
31
- if (valid(currentVersion) === null)
32
- throw new Error(`invalid current package version: ${currentVersion}`);
33
- const published = publishedVersions.map(version => {
34
- if (valid(version) === null)
35
- throw new Error(`invalid published package version: ${version}`);
36
- return version;
37
- });
38
- const publishedSet = new Set(published);
39
- const highestPublished = published.reduce((highest, version) => highest === null || compare(version, highest) > 0 ? version : highest, null);
40
- const currentPrerelease = prerelease(currentVersion);
41
- const currentIsUnpublishedLeadingDev = currentPrerelease?.[0] === "dev"
42
- && !publishedSet.has(currentVersion)
43
- && (highestPublished === null || compare(currentVersion, highestPublished) > 0);
44
- if (currentIsUnpublishedLeadingDev)
45
- return { version: currentVersion, requiresVersionCommit: false };
46
- const base = highestPublished === null || compare(currentVersion, highestPublished) > 0
47
- ? currentVersion
48
- : highestPublished;
49
- const basePrerelease = prerelease(base);
50
- let candidate = basePrerelease?.[0] === "dev"
51
- ? inc(base, "prerelease", "dev")
52
- : inc(base, "prepatch", "dev");
53
- if (candidate === null)
54
- throw new Error(`could not increment development preview from ${base}`);
55
- while (publishedSet.has(candidate)) {
56
- candidate = inc(candidate, "prerelease", "dev");
57
- if (candidate === null)
58
- throw new Error(`could not increment development preview from ${base}`);
59
- }
60
- return { version: candidate, requiresVersionCommit: candidate !== currentVersion };
61
- }
62
- /**
63
- * Treats npm's process result as provisional: browser-auth completion can fail
64
- * after the immutable upload succeeds. Registry identity remains authoritative.
65
- */
66
- export async function publishDevelopmentPreviewWithRecovery(publish, verify) {
67
- let publishError = null;
68
- try {
69
- await publish();
70
- }
71
- catch (error) {
72
- publishError = error;
73
- }
74
- try {
75
- await verify();
76
- return { published: true, recoveredPublishError: publishError };
77
- }
78
- catch (verificationError) {
79
- if (publishError !== null)
80
- throw new AggregateError([publishError, verificationError], "npm publish failed and the exact version could not be verified in the registry");
81
- throw verificationError;
82
- }
83
- }
84
- export async function verifyDevelopmentPreviewRegistry(version, observe, repairNextTag, options = {}) {
85
- const attempts = options.attempts ?? 12;
86
- const delayMs = options.delayMs ?? 2_000;
87
- const delay = options.delay ?? (async (milliseconds) => await new Promise(resolvePromise => setTimeout(resolvePromise, milliseconds)));
88
- if (!Number.isInteger(attempts) || attempts < 1)
89
- throw new Error(`invalid registry verification attempts: ${attempts}`);
90
- let repaired = false;
91
- let last = { published: false, nextVersion: null };
92
- for (let attempt = 1; attempt <= attempts; attempt += 1) {
93
- last = await observe();
94
- if (last.published && last.nextVersion === version)
95
- return;
96
- if (last.published && !repaired) {
97
- await repairNextTag();
98
- repaired = true;
99
- }
100
- if (attempt < attempts)
101
- await delay(delayMs);
102
- }
103
- if (!last.published)
104
- throw new Error(`npm registry did not expose published version ${version} after ${attempts} attempts`);
105
- throw new Error(`npm next resolved ${last.nextVersion ?? "nothing"}; expected ${version} after ${attempts} attempts`);
106
- }
107
- export function developmentPreviewTarballName(packageName, version) {
108
- if (valid(version) === null)
109
- throw new Error(`invalid development preview version: ${version}`);
110
- const unscopedName = packageName.startsWith("@") ? packageName.slice(1).replace("/", "-") : packageName;
111
- if (!/^[a-z0-9._-]+$/i.test(unscopedName))
112
- throw new Error(`invalid package name: ${packageName}`);
113
- return `${unscopedName}-${version}.tgz`;
114
- }