archive-labs 1.0.5 → 1.0.7

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/LICENSE CHANGED
@@ -1,17 +1,17 @@
1
- Apache License
2
- Version 2.0, January 2004
3
- https://www.apache.org/licenses/
4
-
5
- Copyright 2026 Archive Labs
6
-
7
- Licensed under the Apache License, Version 2.0 (the "License");
8
- you may not use this file except in compliance with the License.
9
- You may obtain a copy of the License at
10
-
11
- https://www.apache.org/licenses/LICENSE-2.0
12
-
13
- Unless required by applicable law or agreed to in writing, software
14
- distributed under the License is distributed on an "AS IS" BASIS,
15
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- See the License for the specific language governing permissions and
17
- limitations under the License.
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ https://www.apache.org/licenses/
4
+
5
+ Copyright 2026 Archive Labs
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ https://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
package/README.md CHANGED
@@ -1,86 +1,87 @@
1
- # Archive Labs CLI
2
-
3
- <p align="center">
4
- <img src="https://cdn.jsdelivr.net/npm/archive-labs/logo.png" alt="Archive Labs" width="96" />
5
- </p>
6
-
7
- Archive Labs CLI is a terminal client for Archive. It signs in, reads repository state, runs sync and checks, and supports text or JSON output.
8
-
9
- ## Install
10
-
11
- ```bash
12
- npm install -g archive-labs
13
- archive
14
- ```
15
-
16
- No global install:
17
-
18
- ```bash
19
- npx archive-labs
20
- ```
21
-
22
- ## Commands
23
-
24
- ### Auth and setup
25
-
26
- - `archive init` Set the API and app targets for this workspace.
27
- - `archive login` Sign in and store credentials for later commands.
28
- - `archive logout` Remove stored credentials from this machine.
29
-
30
- ### Repository checks
31
-
32
- - `archive status` Show repository health, readiness, and missing signals.
33
- - `archive sync` Refresh the repository index and derived metadata.
34
- - `archive check` Run the default check for the current context.
35
- - `archive check pr <number>` Review a pull request for merge readiness, impact, ownership, and blockers.
36
- - `archive check diff` Review the current working tree before commit or push.
37
- - `archive events` Review recent repository events.
38
- - `archive recent` Show recently used commands.
39
-
40
- ### Impact and release
41
-
42
- - `archive impact file <path>` Show what a file change affects across services and downstream paths.
43
- - `archive impact diff` Summarize the impact of the current working tree.
44
- - `archive release summarize <tag>` Summarize what is included in a release tag.
45
- - `archive release risk <tag>` Review release risk before shipping.
46
- - `archive ask <question>` Query the codebase in natural language.
47
- - `archive why <path>` Show file or folder context from history and evidence.
48
-
49
- ### Utilities
50
-
51
- - `archive doctor` Inspect local config, auth, and runtime state.
52
- - `archive ping` Check API connectivity.
53
- - `archive help` Show the command list and usage.
54
- - `archive version` Print the installed CLI version.
55
-
56
- Example usage:
57
-
58
- ```bash
59
- archive login
60
- archive status
61
- archive sync
62
- archive check pr 182 --repository acme/payments --ci --json --strict
63
- archive impact diff --json
64
- archive release risk v1.2.0
65
- ```
66
-
67
- ## Output
68
-
69
- Use `--json` when you need machine-readable output. Text output stays short and terminal-friendly.
70
-
71
- ## Config
72
-
73
- CLI config is stored under `~/.archive-labs/config.json`.
74
-
75
- Environment variables:
76
-
77
- - `ARCHIVE_TOKEN`
78
- - `ARCHIVE_LABS_TOKEN`
79
- - `ARCHIVE_GITHUB_TOKEN`
80
-
81
- ## Publish
82
-
83
- ```bash
84
- npm pack
85
- npm publish
86
- ```
1
+ # Archive Labs CLI
2
+
3
+ <p align="center">
4
+ <img src="https://cdn.jsdelivr.net/npm/archive-labs/logo.png" alt="Archive Labs" width="96" />
5
+ </p>
6
+
7
+ Archive Labs CLI is a terminal client for Archive. It signs in, reads repository state, runs sync and checks, and supports text or JSON output.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install -g archive-labs
13
+ archive
14
+ ```
15
+
16
+ No global install:
17
+
18
+ ```bash
19
+ npx archive-labs
20
+ ```
21
+
22
+ ## Commands
23
+
24
+ ### Auth and setup
25
+
26
+ - `archive init` Set the API and app targets for this workspace.
27
+ - `archive login` Sign in and store credentials for later commands.
28
+ - `archive auth login` Alias for browser-based sign-in from onboarding flows.
29
+ - `archive logout` Remove stored credentials from this machine.
30
+
31
+ ### Repository checks
32
+
33
+ - `archive status` Show repository health, readiness, and missing signals.
34
+ - `archive sync` Refresh the repository index and derived metadata.
35
+ - `archive check` Run the default check for the current context.
36
+ - `archive check pr <number>` Review a pull request for merge readiness, impact, ownership, and blockers.
37
+ - `archive check diff` Review the current working tree before commit or push.
38
+ - `archive events` Review recent repository events.
39
+ - `archive recent` Show recently used commands.
40
+
41
+ ### Impact and release
42
+
43
+ - `archive impact file <path>` Show what a file change affects across services and downstream paths.
44
+ - `archive impact diff` Summarize the impact of the current working tree.
45
+ - `archive release summarize <tag>` Summarize what is included in a release tag.
46
+ - `archive release risk <tag>` Review release risk before shipping.
47
+ - `archive ask <question>` Query the codebase in natural language.
48
+ - `archive why <path>` Show file or folder context from history and evidence.
49
+
50
+ ### Utilities
51
+
52
+ - `archive doctor` Inspect local config, auth, and runtime state.
53
+ - `archive ping` Check API connectivity.
54
+ - `archive help` Show the command list and usage.
55
+ - `archive version` Print the installed CLI version.
56
+
57
+ Example usage:
58
+
59
+ ```bash
60
+ archive login
61
+ archive status
62
+ archive sync
63
+ archive check pr 182 --repository acme/payments --ci --json --strict
64
+ archive impact diff --json
65
+ archive release risk v1.2.0
66
+ ```
67
+
68
+ ## Output
69
+
70
+ Use `--json` when you need machine-readable output. Text output stays short and terminal-friendly.
71
+
72
+ ## Config
73
+
74
+ CLI config is stored under `~/.archive-labs/config.json`.
75
+
76
+ Environment variables:
77
+
78
+ - `ARCHIVE_TOKEN`
79
+ - `ARCHIVE_LABS_TOKEN`
80
+ - `ARCHIVE_GITHUB_TOKEN`
81
+
82
+ ## Publish
83
+
84
+ ```bash
85
+ npm pack
86
+ npm publish
87
+ ```
package/dist/cli.js CHANGED
@@ -52,6 +52,7 @@ const systemCheckStaleSyncThresholdMs = 72 * 60 * 60 * 1000;
52
52
  const impactPollIntervalMs = 1500;
53
53
  const defaultApiUrl = "https://api.archivelabs.dev";
54
54
  const defaultAppUrl = "https://app.archivelabs.dev";
55
+ const defaultLocalAppUrl = "http://localhost:8080";
55
56
  const legacyApiUrl = "http://localhost:3000";
56
57
  const legacyAppUrl = "http://localhost:5173";
57
58
  const jsonSchemaVersion = 1;
@@ -170,6 +171,10 @@ const utilityHelpEntries = [
170
171
  command: "archive login",
171
172
  description: "Authenticate with GitHub, Google, Microsoft, or email.",
172
173
  },
174
+ {
175
+ command: "archive auth login",
176
+ description: "Alias for archive login; opens browser sign-in.",
177
+ },
173
178
  {
174
179
  command: "archive logout",
175
180
  description: "Remove stored Archive login credentials from this machine.",
@@ -423,6 +428,19 @@ const matchesNormalizedUrl = (value, expected) => {
423
428
  return false;
424
429
  }
425
430
  };
431
+ const isLocalhostUrl = (value) => {
432
+ if (!value?.trim()) {
433
+ return false;
434
+ }
435
+ try {
436
+ const parsed = new URL(normalizeBaseUrl(value));
437
+ const hostname = parsed.hostname.toLowerCase();
438
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
439
+ }
440
+ catch {
441
+ return false;
442
+ }
443
+ };
426
444
  const renderLocalInitFixCommand = () => `${primaryCommand} init --api-url ${defaultApiUrl} --app-url ${defaultAppUrl} -y`;
427
445
  const getLegacyConfigWarnings = (config) => {
428
446
  const warnings = [];
@@ -471,9 +489,12 @@ const joinCommandInput = (value, label) => {
471
489
  return normalized;
472
490
  };
473
491
  let repoEnvPromise = null;
492
+ const repoEnvFileNames = [".env", ".env.local", ".env.development", ".env.development.local"];
474
493
  const getRepoEnvPaths = () => [
475
- path.join(process.cwd(), ".env"),
476
- path.join(process.cwd(), "backend", ".env"),
494
+ ...repoEnvFileNames.map((fileName) => path.join(process.cwd(), fileName)),
495
+ ...repoEnvFileNames.map((fileName) => path.join(process.cwd(), "backend", fileName)),
496
+ ...repoEnvFileNames.map((fileName) => path.join(process.cwd(), "apps", "backend", fileName)),
497
+ ...repoEnvFileNames.map((fileName) => path.join(process.cwd(), "apps", "frontend", fileName)),
477
498
  ];
478
499
  const readRepoEnv = async () => {
479
500
  if (!repoEnvPromise) {
@@ -501,7 +522,7 @@ const resolveBaseUrl = (explicit, config) => {
501
522
  return normalizeBaseUrl(config.apiUrl);
502
523
  return defaultApiUrl;
503
524
  };
504
- const resolveAppUrl = async (explicit, config) => {
525
+ const resolveAppUrl = async (explicit, config, apiUrl) => {
505
526
  if (explicit?.trim())
506
527
  return normalizeBaseUrl(explicit);
507
528
  if (process.env.ARCHIVE_LABS_APP_URL?.trim()) {
@@ -524,6 +545,9 @@ const resolveAppUrl = async (explicit, config) => {
524
545
  if (repoCandidate?.trim()) {
525
546
  return normalizeBaseUrl(repoCandidate);
526
547
  }
548
+ if (isLocalhostUrl(apiUrl)) {
549
+ return defaultLocalAppUrl;
550
+ }
527
551
  return defaultAppUrl;
528
552
  };
529
553
  const resolveHealthUrl = (explicit, config) => {
@@ -1670,7 +1694,7 @@ const serializeSummaryAction = (action) => action?.kind === "command"
1670
1694
  : null;
1671
1695
  const buildSetupReadinessSummary = async (config) => {
1672
1696
  const accessContext = await loadRepositoryAccessContext(config);
1673
- const appUrl = await resolveAppUrl(undefined, config);
1697
+ const appUrl = await resolveAppUrl(undefined, config, resolveBaseUrl(undefined, config));
1674
1698
  const selectionPlan = planRepositorySelection({
1675
1699
  canPrompt: false,
1676
1700
  explicitRepositoryFullName: null,
@@ -5961,7 +5985,15 @@ const createCliCallbackServer = async (expectedState, allowedOrigin, apiUrl) =>
5961
5985
  try {
5962
5986
  return await new Promise((resolve, reject) => {
5963
5987
  timer = setTimeout(() => {
5964
- reject(new Error("Timed out waiting for browser login to complete."));
5988
+ reject(createCliError({
5989
+ category: "timeout",
5990
+ code: "timeout",
5991
+ command: primaryCommand,
5992
+ exitCode: exitCodeTimeout,
5993
+ hint: "Open the login URL again, or increase --timeout if browser sign-in needs longer.",
5994
+ message: "Archive timed out waiting for browser login to complete.",
5995
+ retryable: true,
5996
+ }));
5965
5997
  }, timeoutMs);
5966
5998
  void result.then(resolve, reject);
5967
5999
  });
@@ -5977,7 +6009,7 @@ const createCliCallbackServer = async (expectedState, allowedOrigin, apiUrl) =>
5977
6009
  };
5978
6010
  const runOAuthLogin = async (provider, options, existingConfig) => {
5979
6011
  const apiUrl = resolveBaseUrl(options.apiUrl, existingConfig);
5980
- const appUrl = await resolveAppUrl(options.appUrl, existingConfig);
6012
+ const appUrl = await resolveAppUrl(options.appUrl, existingConfig, apiUrl);
5981
6013
  const state = crypto.randomBytes(20).toString("hex");
5982
6014
  const callbackServer = await createCliCallbackServer(state, new URL(appUrl).origin, apiUrl);
5983
6015
  const loginUrl = new URL("/auth", `${appUrl}/`);
@@ -6241,7 +6273,6 @@ const runInit = async (options = {}) => {
6241
6273
  clack.intro(`${primaryCommand} init`);
6242
6274
  }
6243
6275
  const resolvedApiUrl = resolveBaseUrl(undefined, existingConfig);
6244
- const resolvedAppUrl = await resolveAppUrl(undefined, existingConfig);
6245
6276
  let apiUrl = options.apiUrl?.trim() || (options.yes ? resolvedApiUrl : "");
6246
6277
  if (!apiUrl) {
6247
6278
  if (!allowPrompts) {
@@ -6273,6 +6304,7 @@ const runInit = async (options = {}) => {
6273
6304
  apiUrl = String(response);
6274
6305
  }
6275
6306
  const normalizedApiUrl = normalizeBaseUrl(apiUrl);
6307
+ const resolvedAppUrl = await resolveAppUrl(undefined, existingConfig, normalizedApiUrl);
6276
6308
  let appUrl = options.appUrl?.trim() || (options.yes ? resolvedAppUrl : "");
6277
6309
  if (!appUrl) {
6278
6310
  if (!allowPrompts) {
@@ -6391,7 +6423,7 @@ const runDoctor = async (options = {}) => {
6391
6423
  {
6392
6424
  title: "Resolve app target",
6393
6425
  task: async () => {
6394
- appUrl = await resolveAppUrl(undefined, config);
6426
+ appUrl = await resolveAppUrl(undefined, config, apiBaseUrl);
6395
6427
  },
6396
6428
  },
6397
6429
  ], {
@@ -6723,7 +6755,7 @@ const runSync = async (options = {}) => {
6723
6755
  const repositoryState = getSyncRepositoryState(repository);
6724
6756
  const lastIndexedAt = repository.lastSyncedAt ?? null;
6725
6757
  if (!repository.appInstalled || !repository.installationId) {
6726
- const appUrl = await resolveAppUrl(undefined, nextConfig);
6758
+ const appUrl = await resolveAppUrl(undefined, nextConfig, resolveBaseUrl(undefined, nextConfig));
6727
6759
  throw createSyncCliError({
6728
6760
  code: "app_install_required",
6729
6761
  hint: `${appUrl}/dashboard?flow=setup&provider=github`,
@@ -8643,6 +8675,42 @@ const buildProgram = async () => {
8643
8675
  .action(withCommandHandling("archive release risk", async (tag, options) => {
8644
8676
  await runReleaseRisk(tag, options);
8645
8677
  }));
8678
+ const authCommand = program
8679
+ .command("auth")
8680
+ .description("Manage Archive authentication");
8681
+ authCommand.helpInformation = () => renderGroupHelp(version, "Auth", [
8682
+ {
8683
+ command: "archive auth login",
8684
+ description: "Authenticate with GitHub, Google, Microsoft, or email.",
8685
+ },
8686
+ {
8687
+ command: "archive auth logout",
8688
+ description: "Remove stored Archive login credentials from this machine.",
8689
+ },
8690
+ ]);
8691
+ authCommand.action(withCommandHandling("archive auth", async () => {
8692
+ authCommand.outputHelp();
8693
+ }));
8694
+ authCommand
8695
+ .command("login")
8696
+ .description("Sign in with GitHub, Google, Microsoft, or email")
8697
+ .option("--provider <provider>", "Login method (github, google, azure, microsoft, or email)", parseLoginProvider)
8698
+ .option("--email <email>", "Email address for email login")
8699
+ .option("--api-url <url>", "Archive Labs API URL for email login")
8700
+ .option("--app-url <url>", "Archive Labs app URL for browser login")
8701
+ .option("--json", "Output login state as JSON")
8702
+ .option("--timeout <ms>", "Login timeout in milliseconds", parseTimeout, defaultLoginTimeoutMs)
8703
+ .option("--no-browser", "Print the browser sign-in URL instead of opening it automatically")
8704
+ .action(withCommandHandling("archive auth login", async (options) => {
8705
+ await runLogin(options);
8706
+ }));
8707
+ authCommand
8708
+ .command("logout")
8709
+ .description("Remove stored Archive login credentials from this machine")
8710
+ .option("--json", "Output logout state as JSON")
8711
+ .action(withCommandHandling("archive auth logout", async (options) => {
8712
+ await runLogout(options);
8713
+ }));
8646
8714
  program
8647
8715
  .command("login")
8648
8716
  .description("Sign in with GitHub, Google, Microsoft, or email")
@@ -99,6 +99,9 @@ export const inferCommandGroup = (argv) => {
99
99
  if (head === "release") {
100
100
  return "release";
101
101
  }
102
+ if (head === "auth" && (next === "login" || next === "logout")) {
103
+ return "utility";
104
+ }
102
105
  if (head === "login" || head === "doctor" || head === "ping" || head === "help" || head === "version" || head === "recent") {
103
106
  return "utility";
104
107
  }
package/package.json CHANGED
@@ -1,56 +1,56 @@
1
- {
2
- "name": "archive-labs",
3
- "version": "1.0.5",
4
- "description": "Terminal CLI for Archive that manages login, repository status, sync, checks, impact analysis, and release risk.",
5
- "license": "Apache-2.0",
6
- "preferGlobal": true,
7
- "packageManager": "pnpm@10.33.0",
8
- "type": "module",
9
- "bin": {
10
- "archive": "./dist/cli.js",
11
- "archive-labs": "./dist/cli.js"
12
- },
13
- "files": [
14
- "dist",
15
- "logo.png",
16
- "README.md",
17
- "LICENSE"
18
- ],
19
- "engines": {
20
- "node": ">=22.13.0"
21
- },
22
- "scripts": {
23
- "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
24
- "build": "tsc -p tsconfig.json",
25
- "dev": "tsx watch src/cli.ts",
26
- "start": "node dist/cli.js",
27
- "smoke": "node dist/cli.js version && node dist/cli.js help",
28
- "pack:verify": "node scripts/verify-pack.mjs",
29
- "test:contract": "node scripts/test-contract.mjs",
30
- "lint": "npm run typecheck",
31
- "typecheck": "tsc -p tsconfig.json --noEmit",
32
- "test:integration": "npm run build && node scripts/run-node-tests.mjs",
33
- "test": "npm run test:integration",
34
- "ci": "npm run clean && npm run typecheck && npm run build && npm run smoke && npm run test",
35
- "prepack": "npm run build",
36
- "prepublishOnly": "npm run ci && npm run pack:verify"
37
- },
38
- "keywords": [
39
- "archive-labs",
40
- "archive",
41
- "cli"
42
- ],
43
- "devDependencies": {
44
- "@types/node": "^22.16.5",
45
- "tsx": "^4.20.3",
46
- "typescript": "^5.9.3"
47
- },
48
- "dependencies": {
49
- "@clack/prompts": "^1.2.0",
50
- "@napi-rs/keyring": "^1.3.0",
51
- "chalk": "^5.6.2",
52
- "commander": "^14.0.3",
53
- "listr2": "^10.2.1",
54
- "ora": "^9.3.0"
55
- }
56
- }
1
+ {
2
+ "name": "archive-labs",
3
+ "version": "1.0.7",
4
+ "description": "Terminal CLI for Archive that manages login, repository status, sync, checks, impact analysis, and release risk.",
5
+ "license": "Apache-2.0",
6
+ "preferGlobal": true,
7
+ "packageManager": "pnpm@10.33.0",
8
+ "type": "module",
9
+ "bin": {
10
+ "archive": "./dist/cli.js",
11
+ "archive-labs": "./dist/cli.js"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "logo.png",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "engines": {
20
+ "node": ">=22.13.0"
21
+ },
22
+ "scripts": {
23
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
24
+ "build": "tsc -p tsconfig.json",
25
+ "dev": "tsx watch src/cli.ts",
26
+ "start": "node dist/cli.js",
27
+ "smoke": "node dist/cli.js version && node dist/cli.js help",
28
+ "pack:verify": "node scripts/verify-pack.mjs",
29
+ "test:contract": "node scripts/test-contract.mjs",
30
+ "lint": "npm run typecheck",
31
+ "typecheck": "tsc -p tsconfig.json --noEmit",
32
+ "test:integration": "npm run build && node scripts/run-node-tests.mjs",
33
+ "test": "npm run test:integration",
34
+ "ci": "npm run clean && npm run typecheck && npm run build && npm run smoke && npm run test",
35
+ "prepack": "npm run build",
36
+ "prepublishOnly": "npm run ci && npm run pack:verify"
37
+ },
38
+ "keywords": [
39
+ "archive-labs",
40
+ "archive",
41
+ "cli"
42
+ ],
43
+ "devDependencies": {
44
+ "@types/node": "^22.16.5",
45
+ "tsx": "^4.20.3",
46
+ "typescript": "^5.9.3"
47
+ },
48
+ "dependencies": {
49
+ "@clack/prompts": "^1.2.0",
50
+ "@napi-rs/keyring": "^1.3.0",
51
+ "chalk": "^5.6.2",
52
+ "commander": "^14.0.3",
53
+ "listr2": "^10.2.1",
54
+ "ora": "^9.3.0"
55
+ }
56
+ }