iteration-cli 0.19.0 → 0.19.1

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.
Files changed (3) hide show
  1. package/README.md +9 -2
  2. package/dist/cli.js +116 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,11 +1,18 @@
1
1
  # Iteration CLI (private source)
2
2
 
3
- This package owns the `iteration-cli@0.19.0` product executable. Clerk sign-in,
3
+ This package owns the `iteration-cli@0.19.1` product executable. Clerk sign-in,
4
4
  workspace enrollment and automatic expansion, the default Iteration endpoint,
5
5
  commands/status UI, history progress, friction summaries, Codex/Cursor billing
6
6
  clients and `iteration mcp`, which gives connected agents the workspace's tools,
7
7
  live here, not in the public Trace Vault repository.
8
8
 
9
+ Running `iteration connect` again reuses the computer's saved workspace and agent
10
+ credentials and reopens project choices. Several connected workspaces require a
11
+ choice in the terminal, or an explicit `--workspace` in automation. Existing
12
+ source connections are verified and reused; only missing sources are expanded.
13
+ Server failures stop setup without starting sign-in, and a revoked source needs
14
+ its own explicit sign-in. Reconnecting preserves history and sharing choices.
15
+
9
16
  The bundled collector requires Node.js 22.22.0 or later. Keep collector data on a
10
17
  local filesystem; the shared API owns its
11
18
  [single-writer lock and recovery contract](../trace-vault/packages/cli/README.md).
@@ -39,7 +46,7 @@ From the private repository root after workspace initialization:
39
46
  ```sh
40
47
  pnpm --filter "iteration-cli..." --recursive --workspace-concurrency=4 run build
41
48
  pnpm --filter iteration-cli pack --pack-destination /tmp/iteration-cli-pack
42
- node packages/cli/smoke.mjs /tmp/iteration-cli-pack/iteration-cli-0.19.0.tgz
49
+ node packages/cli/smoke.mjs /tmp/iteration-cli-pack/iteration-cli-0.19.1.tgz
43
50
  ```
44
51
 
45
52
  The package smoke installs the tarball offline under a disposable home and checks
package/dist/cli.js CHANGED
@@ -23025,6 +23025,16 @@ function sharingConnection(registry3, organizationId, source) {
23025
23025
  }
23026
23026
 
23027
23027
  // src/sharing.ts
23028
+ var SharingSnapshotError = class extends Error {
23029
+ status;
23030
+ constructor(status2) {
23031
+ super(
23032
+ `Could not check project sharing (HTTP ${status2}). Collection is waiting.`
23033
+ );
23034
+ this.name = "SharingSnapshotError";
23035
+ this.status = status2;
23036
+ }
23037
+ };
23028
23038
  async function fetchSharingSnapshot(config3, request2 = collectorFetch, signal) {
23029
23039
  const response = await request2(
23030
23040
  new URL("./collector-sharing", config3.endpoint),
@@ -23036,10 +23046,10 @@ async function fetchSharingSnapshot(config3, request2 = collectorFetch, signal)
23036
23046
  ])
23037
23047
  }
23038
23048
  );
23039
- if (!response.ok)
23040
- throw new Error(
23041
- `Could not check project sharing (HTTP ${response.status}). Collection is waiting.`
23042
- );
23049
+ if (!response.ok) {
23050
+ await response.body?.cancel();
23051
+ throw new SharingSnapshotError(response.status);
23052
+ }
23043
23053
  return collectorSharingSnapshotSchema.parse(await response.json());
23044
23054
  }
23045
23055
  function workspaceForSource(snapshot, location, source) {
@@ -26820,6 +26830,19 @@ async function startSource(config3) {
26820
26830
  );
26821
26831
  }
26822
26832
  }
26833
+ async function prepareConnectedSource(source, home, directory, connection, previous) {
26834
+ const config3 = previous === null ? sourceConfig(source, home, directory, connection.endpoint, connection) : {
26835
+ ...previous,
26836
+ destination: "host",
26837
+ ...previous.organizationId === connection.organizationId ? {
26838
+ token: connection.token,
26839
+ organizationName: connection.organizationName
26840
+ } : {}
26841
+ };
26842
+ if (previous === null || previous.destination !== "host" || previous.token !== config3.token)
26843
+ await prepareSource(config3, previous);
26844
+ return config3;
26845
+ }
26823
26846
  async function localConnectionOptions(options, selectedSource) {
26824
26847
  const existing = await Promise.all(
26825
26848
  COLLECTOR_SOURCES2.map(
@@ -26861,6 +26884,61 @@ function assertSharingOwner(ownerId, nextOwnerId) {
26861
26884
  "This computer is connected to another Iteration account. Run iteration logout before switching accounts."
26862
26885
  );
26863
26886
  }
26887
+ async function connectedWorkspace(registry3, requested, endpoint) {
26888
+ if (requested !== null) return requested;
26889
+ const workspaces = [
26890
+ ...new Map(
26891
+ registry3.connections.filter((connection) => connection.endpoint === endpoint).map((connection) => [connection.organizationId, connection])
26892
+ ).values()
26893
+ ];
26894
+ if (workspaces.length < 2) return workspaces[0]?.organizationId ?? null;
26895
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
26896
+ throw new Error(
26897
+ "This computer has several workspaces. Run iteration connect interactively to choose one, or pass --workspace ID."
26898
+ );
26899
+ process.stdout.write(
26900
+ "\nChoose a connected workspace:\n" + workspaces.map(
26901
+ (workspace, index) => ` ${index + 1}. ${safeWorkspaceName(workspace.organizationName)}`
26902
+ ).join("\n") + "\n"
26903
+ );
26904
+ for (let attempt = 0; attempt < 3; attempt++) {
26905
+ const answer = await ask(terminalFor(process.stdout), "Workspace number:");
26906
+ if (answer === null) throw new Error("Connection canceled.");
26907
+ const selected = /^\d+$/.test(answer.trim()) ? workspaces[Number(answer.trim()) - 1] : void 0;
26908
+ if (selected) return selected.organizationId;
26909
+ }
26910
+ throw new Error("Choose one of the listed workspaces to connect.");
26911
+ }
26912
+ async function verifySavedConnection(connection, registry3) {
26913
+ const snapshot = await fetchSharingSnapshot(connection);
26914
+ if (snapshot.computerId !== registry3.computerId)
26915
+ throw new Error(
26916
+ "The saved connection belongs to a different computer. Sign in again before connecting it."
26917
+ );
26918
+ assertSharingOwner(registry3.ownerId, snapshot.ownerId);
26919
+ return connection;
26920
+ }
26921
+ async function connectionForSource(registry3, anchor3, anchorConfig, source) {
26922
+ if (source === anchor3.source) return anchor3;
26923
+ const saved = sharingConnection(registry3, anchor3.organizationId, source);
26924
+ if (saved !== null) {
26925
+ try {
26926
+ return await verifySavedConnection(saved, registry3);
26927
+ } catch (error3) {
26928
+ if (error3 instanceof SharingSnapshotError && [401, 403].includes(error3.status))
26929
+ throw new Error(
26930
+ `${collectorSourceLabel(source)} needs sign-in again. Run iteration connect ${source} --workspace ${anchor3.organizationId}.`,
26931
+ { cause: error3 }
26932
+ );
26933
+ throw error3;
26934
+ }
26935
+ }
26936
+ return enrolledSharingConnection(
26937
+ await expandSource(anchorConfig, source, anchor3.organizationId),
26938
+ source,
26939
+ anchor3.endpoint
26940
+ );
26941
+ }
26864
26942
  async function waitForSharing(anchor3, alreadyApproved, signal) {
26865
26943
  const deadline = Date.now() + 15 * 60 * 1e3;
26866
26944
  let approved = alreadyApproved;
@@ -26894,13 +26972,27 @@ async function connectWorkspace(options, selectedSource) {
26894
26972
  const prepared = await withSharingRegistry(async (initial, save) => {
26895
26973
  let registry3 = initial;
26896
26974
  const { existing, homes, sources, endpoint } = local;
26975
+ if (registry3.connections.some(
26976
+ (connection) => connection.endpoint !== endpoint
26977
+ ))
26978
+ throw new Error(
26979
+ "This computer is connected to another Iteration server. Sign out before changing servers."
26980
+ );
26897
26981
  const first = sources[0];
26898
- const requested = textOption(options, "workspace");
26899
- let anchor3 = requested === null ? null : sharingConnection(registry3, requested, first);
26982
+ const requested = await connectedWorkspace(
26983
+ registry3,
26984
+ textOption(options, "workspace"),
26985
+ endpoint
26986
+ );
26987
+ let anchor3 = requested === null ? null : sharingConnection(registry3, requested, first) ?? registry3.connections.find(
26988
+ (connection) => connection.organizationId === requested && connection.endpoint === endpoint
26989
+ ) ?? null;
26900
26990
  if (anchor3 !== null) {
26901
26991
  try {
26902
- await fetchSharingSnapshot(anchor3);
26903
- } catch {
26992
+ await verifySavedConnection(anchor3, registry3);
26993
+ } catch (error3) {
26994
+ if (!(error3 instanceof SharingSnapshotError && [401, 403].includes(error3.status)))
26995
+ throw error3;
26904
26996
  anchor3 = null;
26905
26997
  }
26906
26998
  }
@@ -26915,9 +27007,9 @@ async function connectWorkspace(options, selectedSource) {
26915
27007
  anchor3 = enrolledSharingConnection(enrollment, first, endpoint);
26916
27008
  }
26917
27009
  const anchorConfig = sourceConfig(
26918
- first,
26919
- homes[first],
26920
- defaultCollectorDirectory(first),
27010
+ anchor3.source,
27011
+ homes[anchor3.source],
27012
+ defaultCollectorDirectory(anchor3.source),
26921
27013
  endpoint,
26922
27014
  anchor3
26923
27015
  );
@@ -26950,7 +27042,7 @@ async function connectWorkspace(options, selectedSource) {
26950
27042
  sourceHomes: homes,
26951
27043
  connections: [
26952
27044
  ...registry3.connections.filter(
26953
- (connection) => connection.organizationId !== anchor3.organizationId || connection.source !== first
27045
+ (connection) => connection.organizationId !== anchor3.organizationId || connection.source !== anchor3.source
26954
27046
  ),
26955
27047
  anchor3
26956
27048
  ]
@@ -26963,11 +27055,11 @@ async function connectWorkspace(options, selectedSource) {
26963
27055
  }
26964
27056
  const configs = [];
26965
27057
  for (const source of sources) {
26966
- const enrollment = source === first ? anchor3 : await expandSource(anchorConfig, source, anchor3.organizationId);
26967
- const connection = enrolledSharingConnection(
26968
- enrollment,
26969
- source,
26970
- endpoint
27058
+ const connection = await connectionForSource(
27059
+ registry3,
27060
+ anchor3,
27061
+ anchorConfig,
27062
+ source
26971
27063
  );
26972
27064
  registry3 = {
26973
27065
  ...registry3,
@@ -26983,9 +27075,13 @@ async function connectWorkspace(options, selectedSource) {
26983
27075
  const directory = selectedSource ? path30.resolve(
26984
27076
  textOption(options, "data-directory") ?? defaultCollectorDirectory(source)
26985
27077
  ) : defaultCollectorDirectory(source);
26986
- const config3 = previous === null ? sourceConfig(source, homes[source], directory, endpoint, enrollment) : { ...previous, destination: "host" };
26987
- if (previous === null || previous.destination !== "host")
26988
- await prepareSource(config3, previous);
27078
+ const config3 = await prepareConnectedSource(
27079
+ source,
27080
+ homes[source],
27081
+ directory,
27082
+ connection,
27083
+ previous
27084
+ );
26989
27085
  registry3 = {
26990
27086
  ...registry3,
26991
27087
  directories: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iteration-cli",
3
- "version": "0.19.0",
3
+ "version": "0.19.1",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",