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.
- package/README.md +9 -2
- package/dist/cli.js +116 -20
- 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.
|
|
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.
|
|
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
|
-
|
|
23041
|
-
|
|
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 =
|
|
26899
|
-
|
|
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
|
|
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
|
-
|
|
26919
|
-
homes[
|
|
26920
|
-
defaultCollectorDirectory(
|
|
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 !==
|
|
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
|
|
26967
|
-
|
|
26968
|
-
|
|
26969
|
-
|
|
26970
|
-
|
|
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 =
|
|
26987
|
-
|
|
26988
|
-
|
|
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: {
|