iterate 0.2.7 → 0.3.0
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 +86 -76
- package/THIRD_PARTY_NOTICES.md +55 -0
- package/bin/iterate.js +11 -3
- package/dist/api-url-B6404M82.mjs +17 -0
- package/dist/api-url-B6404M82.mjs.map +1 -0
- package/dist/app-ref-BipL0feU.mjs +35 -0
- package/dist/app-ref-BipL0feU.mjs.map +1 -0
- package/dist/app-ref-C1CrgXqX.mjs +7 -0
- package/dist/app-ref-C1CrgXqX.mjs.map +1 -0
- package/dist/app-ref-DYai_om1.mjs +7 -0
- package/dist/app-ref-DYai_om1.mjs.map +1 -0
- package/dist/cli-D0c-pDL_.mjs +1010 -0
- package/dist/cli-D0c-pDL_.mjs.map +1 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.mjs +4 -0
- package/dist/cloudflare-BTm90gQ4.mjs +951 -0
- package/dist/cloudflare-BTm90gQ4.mjs.map +1 -0
- package/dist/contract-s4FW4eES.mjs +309 -0
- package/dist/contract-s4FW4eES.mjs.map +1 -0
- package/dist/document-review/index.d.ts +5 -0
- package/dist/document-review/types.d.ts +107 -0
- package/dist/document-review.mjs +7015 -0
- package/dist/document-review.mjs.map +1 -0
- package/dist/durable-object-processor-durability-CNsTjAJS.mjs +205 -0
- package/dist/durable-object-processor-durability-CNsTjAJS.mjs.map +1 -0
- package/dist/idempotency-DleloJNt.mjs +28 -0
- package/dist/idempotency-DleloJNt.mjs.map +1 -0
- package/dist/index.mjs +1 -1
- package/dist/itx/api-url.d.ts +6 -0
- package/dist/itx/itx-node-client.d.ts +65 -0
- package/dist/itx/itx-session.d.ts +215 -0
- package/dist/itx/owned-rpc-session.d.ts +14 -0
- package/dist/itx/query-client.d.ts +10 -0
- package/dist/itx-api.generated.d.ts +6195 -0
- package/dist/itx-session-sjud8GiT.mjs +534 -0
- package/dist/itx-session-sjud8GiT.mjs.map +1 -0
- package/dist/live-state-BJNqOwFw.mjs +299 -0
- package/dist/live-state-BJNqOwFw.mjs.map +1 -0
- package/dist/next/api.d.ts +479 -0
- package/dist/next/api.mjs +0 -0
- package/dist/next/app-server.d.ts +44 -0
- package/dist/next/app-server.mjs +479 -0
- package/dist/next/app-server.mjs.map +1 -0
- package/dist/next/app-session.d.ts +49 -0
- package/dist/next/app-session.mjs +238 -0
- package/dist/next/app-session.mjs.map +1 -0
- package/dist/next/app.d.ts +29 -0
- package/dist/next/app.mjs +141 -0
- package/dist/next/app.mjs.map +1 -0
- package/dist/next/client/live-state.d.ts +63 -0
- package/dist/next/client/oauth.d.ts +12 -0
- package/dist/next/client/react.d.ts +109 -0
- package/dist/next/client/socket.d.ts +6 -0
- package/dist/next/client.mjs +156 -0
- package/dist/next/client.mjs.map +1 -0
- package/dist/next/expression.d.ts +146 -0
- package/dist/next/expression.mjs +399 -0
- package/dist/next/expression.mjs.map +1 -0
- package/dist/next/lib.d.ts +56 -0
- package/dist/next/lib.mjs +199 -0
- package/dist/next/lib.mjs.map +1 -0
- package/dist/next/oauth-scopes.d.ts +32 -0
- package/dist/next/oauth-scopes.mjs +40 -0
- package/dist/next/oauth-scopes.mjs.map +1 -0
- package/dist/next/oauth.mjs +29 -0
- package/dist/next/oauth.mjs.map +1 -0
- package/dist/next/principal.d.ts +64 -0
- package/dist/next/principal.mjs +98 -0
- package/dist/next/principal.mjs.map +1 -0
- package/dist/next/project-ingress.d.ts +37 -0
- package/dist/next/project-ingress.mjs +75 -0
- package/dist/next/project-ingress.mjs.map +1 -0
- package/dist/next/react.mjs +285 -0
- package/dist/next/react.mjs.map +1 -0
- package/dist/next/sdk/auth.d.ts +5 -0
- package/dist/next/sdk/index.d.ts +112 -0
- package/dist/next/sdk.mjs +139 -0
- package/dist/next/sdk.mjs.map +1 -0
- package/dist/next/stream/processor.d.ts +378 -0
- package/dist/next/stream/processor.mjs +582 -0
- package/dist/next/stream/processor.mjs.map +1 -0
- package/dist/next/stream/run.d.ts +58 -0
- package/dist/next/stream/run.mjs +40 -0
- package/dist/next/stream/run.mjs.map +1 -0
- package/dist/next-node.d.ts +15 -0
- package/dist/next-node.mjs +51 -0
- package/dist/next-node.mjs.map +1 -0
- package/dist/node.d.ts +3 -0
- package/dist/node.mjs +185 -0
- package/dist/node.mjs.map +1 -0
- package/dist/processor-host-capabilities-BMFH3KTM.mjs +56 -0
- package/dist/processor-host-capabilities-BMFH3KTM.mjs.map +1 -0
- package/dist/processors/cloudflare.d.ts +3 -0
- package/dist/processors/durable-object-processor-durability.d.ts +79 -0
- package/dist/processors/event-consumption-metrics.d.ts +82 -0
- package/dist/processors/idempotency.d.ts +13 -0
- package/dist/processors/index.d.ts +12 -0
- package/dist/processors/processor-contracts.d.ts +342 -0
- package/dist/processors/processor-facet.d.ts +186 -0
- package/dist/processors/processor-host-capabilities.d.ts +60 -0
- package/dist/processors/prompt-sections.d.ts +17 -0
- package/dist/processors/rpc-types.d.ts +515 -0
- package/dist/processors/schemas.d.ts +102 -0
- package/dist/processors/stream-handle.d.ts +45 -0
- package/dist/processors/stream-processor-keepalive.d.ts +95 -0
- package/dist/processors/stream-processor-registry.d.ts +233 -0
- package/dist/processors/stream-processor-runner.d.ts +289 -0
- package/dist/processors/stream-processor.d.ts +339 -0
- package/dist/processors/stream-runtime-metrics.d.ts +107 -0
- package/dist/processors/testing.d.ts +302 -0
- package/dist/processors-BoNyeBfQ.mjs +10 -0
- package/dist/processors-BoNyeBfQ.mjs.map +1 -0
- package/dist/processors-cloudflare.mjs +3 -0
- package/dist/processors-testing.mjs +435 -0
- package/dist/processors-testing.mjs.map +1 -0
- package/dist/processors.mjs +52 -0
- package/dist/processors.mjs.map +1 -0
- package/dist/protocol-DnK_f2m6.mjs +251 -0
- package/dist/protocol-DnK_f2m6.mjs.map +1 -0
- package/dist/sdk/capnweb/index.d.ts +2 -0
- package/dist/sdk/capnweb/live-state/compact.d.ts +5 -0
- package/dist/sdk/capnweb/live-state/diff.d.ts +41 -0
- package/dist/sdk/capnweb/live-state/engine.d.ts +44 -0
- package/dist/sdk/capnweb/live-state/index.d.ts +41 -0
- package/dist/sdk/capnweb/live-state/protocol.d.ts +87 -0
- package/dist/sdk/capnweb/live-state/retain.d.ts +23 -0
- package/dist/sdk/capnweb/live-state/store.d.ts +20 -0
- package/dist/sdk/capnweb/live-state/types.d.ts +11 -0
- package/dist/sdk/capnweb/react.d.ts +45 -0
- package/dist/sdk/capnweb/react.mjs +316 -0
- package/dist/sdk/capnweb/react.mjs.map +1 -0
- package/dist/sdk/capnweb.mjs +4 -0
- package/dist/sdk/itx/react.d.ts +191 -0
- package/dist/sdk/itx/react.mjs +383 -0
- package/dist/sdk/itx/react.mjs.map +1 -0
- package/dist/sdk-DMB-IM11.mjs +933 -0
- package/dist/sdk-DMB-IM11.mjs.map +1 -0
- package/dist/sdk.d.ts +339 -0
- package/dist/sdk.mjs +2 -0
- package/dist/serve-itx.d.ts +46 -0
- package/dist/starter-apps/flake-dashboard/app-ref.d.ts +31 -0
- package/dist/starter-apps/flake-dashboard/configured-worker.mjs +1055 -0
- package/dist/starter-apps/flake-dashboard/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/flake-dashboard/contract.d.ts +4839 -0
- package/dist/starter-apps/flake-dashboard/contract.mjs +2 -0
- package/dist/starter-apps/flake-dashboard/index.d.ts +17 -0
- package/dist/starter-apps/flake-dashboard/index.mjs +56 -0
- package/dist/starter-apps/flake-dashboard/index.mjs.map +1 -0
- package/dist/starter-apps/flake-dashboard/worker.d.ts +4607 -0
- package/dist/starter-apps/github-ai-linter/ai-linter.d.ts +8914 -0
- package/dist/starter-apps/github-ai-linter/configured-worker.mjs +17987 -0
- package/dist/starter-apps/github-ai-linter/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/github-ai-linter/contract.d.ts +9193 -0
- package/dist/starter-apps/github-ai-linter/index.d.ts +10 -0
- package/dist/starter-apps/github-ai-linter/index.mjs +36 -0
- package/dist/starter-apps/github-ai-linter/index.mjs.map +1 -0
- package/dist/starter-apps/github-ai-linter/prompt.d.ts +13 -0
- package/dist/starter-apps/github-ai-linter/review-bot.d.ts +808 -0
- package/dist/starter-apps/github-ai-linter/rules.d.ts +34 -0
- package/dist/starter-apps/github-ai-linter/worker-ref.d.ts +19 -0
- package/dist/starter-apps/github-ai-linter/worker.d.ts +19 -0
- package/dist/starter-apps/github-ai-linter/worker.mjs +947 -0
- package/dist/starter-apps/github-ai-linter/worker.mjs.map +1 -0
- package/dist/starter-apps/guestbook/app-ref.d.ts +27 -0
- package/dist/starter-apps/guestbook/client.d.ts +7 -0
- package/dist/starter-apps/guestbook/client.mjs +59 -0
- package/dist/starter-apps/guestbook/configured-worker.mjs +205 -0
- package/dist/starter-apps/guestbook/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/guestbook/index.d.ts +9 -0
- package/dist/starter-apps/guestbook/index.mjs +31 -0
- package/dist/starter-apps/guestbook/index.mjs.map +1 -0
- package/dist/starter-apps/guestbook/processor.d.ts +2267 -0
- package/dist/starter-apps/guestbook/worker.d.ts +26 -0
- package/dist/starter-apps/guestbook/worker.mjs +191 -0
- package/dist/starter-apps/guestbook/worker.mjs.map +1 -0
- package/dist/starter-apps/media/configured-worker.mjs +577 -0
- package/dist/starter-apps/media/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/media/index.mjs +36 -0
- package/dist/starter-apps/media/index.mjs.map +1 -0
- package/dist/starter-apps/media/ref.mjs +20 -0
- package/dist/starter-apps/media/ref.mjs.map +1 -0
- package/dist/starter-apps/media/worker.mjs +579 -0
- package/dist/starter-apps/media/worker.mjs.map +1 -0
- package/dist/starter-apps/notes/configured-worker.mjs +6134 -0
- package/dist/starter-apps/notes/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/notes/index.mjs +23 -0
- package/dist/starter-apps/notes/index.mjs.map +1 -0
- package/dist/starter-apps/notes/ref.mjs +21 -0
- package/dist/starter-apps/notes/ref.mjs.map +1 -0
- package/dist/starter-apps/notes/worker.mjs +427 -0
- package/dist/starter-apps/notes/worker.mjs.map +1 -0
- package/dist/starter-apps/todo/client.mjs +59 -0
- package/dist/starter-apps/todo/configured-worker.mjs +2864 -0
- package/dist/starter-apps/todo/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/todo/index.d.ts +8 -0
- package/dist/starter-apps/todo/index.mjs +29 -0
- package/dist/starter-apps/todo/index.mjs.map +1 -0
- package/dist/stream-processor-keepalive-DAQTP6m3.mjs +2082 -0
- package/dist/stream-processor-keepalive-DAQTP6m3.mjs.map +1 -0
- package/dist/usingCtx-inzbY1Qz.mjs +57 -0
- package/dist/usingCtx-mZx5nsAW.mjs +11800 -0
- package/dist/usingCtx-mZx5nsAW.mjs.map +1 -0
- package/dist/worker-ref-DZxPDmb_.mjs +390 -0
- package/dist/worker-ref-DZxPDmb_.mjs.map +1 -0
- package/menubar/Iterate.entitlements +12 -0
- package/menubar/Iterate.swift +914 -0
- package/menubar/IterateIcon.swift +145 -0
- package/menubar/README.md +28 -0
- package/menubar/build-menubar-app.sh +59 -0
- package/package.json +235 -18
- package/dist/cli-DMS4kJph.mjs +0 -868
- package/dist/cli-DMS4kJph.mjs.map +0 -1
- package/dist/config-DtnR7Lv7.mjs +0 -170
- package/dist/config-DtnR7Lv7.mjs.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/stream-tui/agent-chat-terminal.d.mts +0 -1
- package/dist/stream-tui/agent-chat-terminal.mjs +0 -933
- package/dist/stream-tui/agent-chat-terminal.mjs.map +0 -1
|
@@ -0,0 +1,1010 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import process$1 from "node:process";
|
|
6
|
+
import repl from "node:repl";
|
|
7
|
+
import { RpcTarget, newWebSocketRpcSession } from "capnweb";
|
|
8
|
+
import * as prompts from "@clack/prompts";
|
|
9
|
+
import { os } from "@orpc/server";
|
|
10
|
+
import { createCli, yamlTableConsoleLogger } from "trpc-cli";
|
|
11
|
+
import { z } from "zod/v4";
|
|
12
|
+
import WebSocket from "ws";
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { homedir, hostname } from "node:os";
|
|
15
|
+
import { spawn } from "node:child_process";
|
|
16
|
+
//#region src/next-node.ts
|
|
17
|
+
async function connectOsNext(input) {
|
|
18
|
+
const url = new URL("/api", input.baseUrl);
|
|
19
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") throw new Error("OS Next URL must use http or https.");
|
|
20
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
21
|
+
const socket = new WebSocket(url.href, { handshakeTimeout: 15e3 });
|
|
22
|
+
const root = newWebSocketRpcSession(socket);
|
|
23
|
+
const closed = new Promise((resolve) => {
|
|
24
|
+
socket.once("close", (code, reason) => resolve({
|
|
25
|
+
code,
|
|
26
|
+
reason: reason.toString()
|
|
27
|
+
}));
|
|
28
|
+
});
|
|
29
|
+
socket.on("error", () => {});
|
|
30
|
+
let timeout;
|
|
31
|
+
try {
|
|
32
|
+
const session = await Promise.race([root.authenticate(input.auth), new Promise((_, reject) => {
|
|
33
|
+
timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("OS Next authentication timed out")), 2e4);
|
|
34
|
+
})]);
|
|
35
|
+
return {
|
|
36
|
+
session,
|
|
37
|
+
closed,
|
|
38
|
+
[Symbol.dispose]() {
|
|
39
|
+
try {
|
|
40
|
+
session[Symbol.dispose]();
|
|
41
|
+
} finally {
|
|
42
|
+
try {
|
|
43
|
+
root[Symbol.dispose]();
|
|
44
|
+
} finally {
|
|
45
|
+
socket.close();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
try {
|
|
52
|
+
root[Symbol.dispose]();
|
|
53
|
+
} finally {
|
|
54
|
+
socket.terminate();
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
} finally {
|
|
58
|
+
clearTimeout(timeout);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/run-command.ts
|
|
63
|
+
/** Spawn a command, optionally feed it stdin, and collect its result. */
|
|
64
|
+
function run(command, args, stdin) {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const child = spawn(command, args);
|
|
67
|
+
let stdout = "";
|
|
68
|
+
let stderr = "";
|
|
69
|
+
child.stdout.on("data", (d) => stdout += d);
|
|
70
|
+
child.stderr.on("data", (d) => stderr += d);
|
|
71
|
+
child.on("error", reject);
|
|
72
|
+
child.on("close", (code) => resolve({
|
|
73
|
+
stdout,
|
|
74
|
+
stderr,
|
|
75
|
+
exitCode: code ?? 1
|
|
76
|
+
}));
|
|
77
|
+
if (stdin !== void 0) child.stdin.end(stdin);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/menubar-app.ts
|
|
82
|
+
const CONFIG_DIR = join(process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config"), "iterate");
|
|
83
|
+
const BUILD_DIR = join(CONFIG_DIR, "menubar-build");
|
|
84
|
+
const APP_PATH = join(BUILD_DIR, "Iterate.app");
|
|
85
|
+
const SOURCES = [
|
|
86
|
+
"Iterate.swift",
|
|
87
|
+
"IterateIcon.swift",
|
|
88
|
+
"build-menubar-app.sh",
|
|
89
|
+
"Iterate.entitlements"
|
|
90
|
+
];
|
|
91
|
+
/** Compile-if-needed and launch the menu-bar app for one project/config. */
|
|
92
|
+
async function launchMenubarApp(input) {
|
|
93
|
+
if (process.platform !== "darwin") throw new Error("The menu-bar app is macOS-only.");
|
|
94
|
+
const log = input.log || (() => {});
|
|
95
|
+
const menubarDir = join(import.meta.dirname, "..", "menubar");
|
|
96
|
+
const binPath = join(import.meta.dirname, "..", "bin", "iterate.js");
|
|
97
|
+
const contents = await Promise.all(SOURCES.map((name) => readFile(join(menubarDir, name), "utf8")));
|
|
98
|
+
const hash = createHash("sha256").update(contents.join("\0")).digest("hex").slice(0, 12);
|
|
99
|
+
const marker = join(BUILD_DIR, `.built-${hash}`);
|
|
100
|
+
if (!existsSync(marker) || !existsSync(APP_PATH)) {
|
|
101
|
+
log("Building the menu-bar app (swiftc)…");
|
|
102
|
+
await mkdir(BUILD_DIR, { recursive: true });
|
|
103
|
+
const build = await run("bash", [join(menubarDir, "build-menubar-app.sh"), BUILD_DIR]);
|
|
104
|
+
if (build.exitCode !== 0) throw new Error(`Menu-bar build failed — install the Xcode command-line tools (xcode-select --install)?\n${build.stderr.trim() || build.stdout.trim()}`);
|
|
105
|
+
await writeFile(marker, hash);
|
|
106
|
+
}
|
|
107
|
+
await writeFile(join(CONFIG_DIR, "menubar.json"), `${JSON.stringify({
|
|
108
|
+
command: process.execPath,
|
|
109
|
+
args: [binPath],
|
|
110
|
+
config: input.configName,
|
|
111
|
+
project: input.project,
|
|
112
|
+
xdgConfigHome: process.env.XDG_CONFIG_HOME
|
|
113
|
+
}, null, 2)}\n`);
|
|
114
|
+
const open = await run("open", [
|
|
115
|
+
"-a",
|
|
116
|
+
APP_PATH,
|
|
117
|
+
join(CONFIG_DIR, "menubar.json")
|
|
118
|
+
]);
|
|
119
|
+
if (open.exitCode !== 0) throw new Error(`Could not launch the app: ${open.stderr.trim()}`);
|
|
120
|
+
log(`Launched Iterate for project "${input.project}" (config ${input.configName}).`);
|
|
121
|
+
log("It lives in your menu bar — click the 𝑖 to sign in and share your computer with the project's agents.");
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region \0@oxc-project+runtime@0.134.0/helpers/esm/usingCtx.js
|
|
125
|
+
function _usingCtx() {
|
|
126
|
+
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
127
|
+
var n = Error();
|
|
128
|
+
return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
|
|
129
|
+
}, e = {}, n = [];
|
|
130
|
+
function using(r, e) {
|
|
131
|
+
if (null != e) {
|
|
132
|
+
if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
|
|
133
|
+
if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
|
|
134
|
+
if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
|
|
135
|
+
if ("function" != typeof o) throw new TypeError("Object is not disposable.");
|
|
136
|
+
t && (o = function o() {
|
|
137
|
+
try {
|
|
138
|
+
t.call(e);
|
|
139
|
+
} catch (r) {
|
|
140
|
+
return Promise.reject(r);
|
|
141
|
+
}
|
|
142
|
+
}), n.push({
|
|
143
|
+
v: e,
|
|
144
|
+
d: o,
|
|
145
|
+
a: r
|
|
146
|
+
});
|
|
147
|
+
} else r && n.push({
|
|
148
|
+
d: e,
|
|
149
|
+
a: r
|
|
150
|
+
});
|
|
151
|
+
return e;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
e,
|
|
155
|
+
u: using.bind(null, !1),
|
|
156
|
+
a: using.bind(null, !0),
|
|
157
|
+
d: function d() {
|
|
158
|
+
var o, t = this.e, s = 0;
|
|
159
|
+
function next() {
|
|
160
|
+
for (; o = n.pop();) try {
|
|
161
|
+
if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
|
|
162
|
+
if (o.d) {
|
|
163
|
+
var r = o.d.call(o.v);
|
|
164
|
+
if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
|
|
165
|
+
} else s |= 1;
|
|
166
|
+
} catch (r) {
|
|
167
|
+
return err(r);
|
|
168
|
+
}
|
|
169
|
+
if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
|
|
170
|
+
if (t !== e) throw t;
|
|
171
|
+
}
|
|
172
|
+
function err(n) {
|
|
173
|
+
return t = t !== e ? new r(n, t) : n, next();
|
|
174
|
+
}
|
|
175
|
+
return next();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/use-my-computer.ts
|
|
181
|
+
/** Methods run locally with the authority of the person sharing this Mac. */
|
|
182
|
+
var MyComputer = class extends RpcTarget {
|
|
183
|
+
#emit;
|
|
184
|
+
#nextCall = 0;
|
|
185
|
+
constructor(emit = () => {}) {
|
|
186
|
+
super();
|
|
187
|
+
this.#emit = emit;
|
|
188
|
+
}
|
|
189
|
+
async #call(method, summary, operation) {
|
|
190
|
+
const id = ++this.#nextCall;
|
|
191
|
+
this.#emit({
|
|
192
|
+
type: "call",
|
|
193
|
+
id,
|
|
194
|
+
method,
|
|
195
|
+
summary: summary.slice(0, 160)
|
|
196
|
+
});
|
|
197
|
+
let ok = false;
|
|
198
|
+
try {
|
|
199
|
+
const result = await operation();
|
|
200
|
+
ok = true;
|
|
201
|
+
return result;
|
|
202
|
+
} finally {
|
|
203
|
+
this.#emit({
|
|
204
|
+
type: "call-done",
|
|
205
|
+
id,
|
|
206
|
+
ok
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/** Pop a native dialog on screen and return which button the human clicked. */
|
|
211
|
+
async ask({ question, buttons = ["No", "Yes"] }) {
|
|
212
|
+
return this.#call("ask", question, async () => {
|
|
213
|
+
if (buttons.length < 1 || buttons.length > 3) throw new Error("ask() needs 1–3 buttons (AppleScript dialogs cap at three).");
|
|
214
|
+
const buttonList = buttons.map((b) => `"${escapeForAppleScript(b)}"`).join(", ");
|
|
215
|
+
const { stdout } = await osascript(`display dialog "${escapeForAppleScript(question)}" buttons {${buttonList}} default button "${escapeForAppleScript(buttons[0])}" with title "iterate · myComputer"`);
|
|
216
|
+
return { answer: stdout.trim().replace(/^button returned:/, "") };
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/** Show a desktop notification. */
|
|
220
|
+
async notify({ message, title = "iterate" }) {
|
|
221
|
+
return this.#call("notify", message, async () => {
|
|
222
|
+
await osascript(`display notification "${escapeForAppleScript(message)}" with title "${escapeForAppleScript(title)}"`);
|
|
223
|
+
return { ok: true };
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/** Run arbitrary Swift and return its output — full power, when an agent needs it. */
|
|
227
|
+
async runSwift({ code }) {
|
|
228
|
+
return await this.#call("runSwift", "Swift script", () => run("swift", ["-"], code));
|
|
229
|
+
}
|
|
230
|
+
__describe() {
|
|
231
|
+
return {
|
|
232
|
+
instructions: "A live Mac shared by its owner. Ask before destructive actions. Methods: ask({ question, buttons? }), notify({ message, title? }), runSwift({ code }). Swift has full local access.",
|
|
233
|
+
types: "ask(input: { question: string; buttons?: string[] }): Promise<{ answer: string }>; notify(input: { message: string; title?: string }): Promise<{ ok: true }>; runSwift(input: { code: string }): Promise<{ stdout: string; stderr: string; exitCode: number }>;"
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
async function askComputerName() {
|
|
238
|
+
const proposed = proposeComputerName();
|
|
239
|
+
if (!process.stdin.isTTY) return proposed;
|
|
240
|
+
const answer = await prompts.text({
|
|
241
|
+
message: "What should agents call this computer? (camelCase — it becomes the itx.<name> path)",
|
|
242
|
+
placeholder: proposed,
|
|
243
|
+
defaultValue: proposed,
|
|
244
|
+
validate: (value) => /^[a-zA-Z][a-zA-Z0-9]*$/.test((value ?? "").trim()) ? void 0 : "Use a camelCase name: letters and digits, starting with a letter (e.g. jonasComputer)."
|
|
245
|
+
});
|
|
246
|
+
if (prompts.isCancel(answer)) {
|
|
247
|
+
prompts.cancel("Cancelled.");
|
|
248
|
+
process.exit(0);
|
|
249
|
+
}
|
|
250
|
+
return answer.trim();
|
|
251
|
+
}
|
|
252
|
+
/** "Jonas’s-MacBook-Pro.local" → "jonasComputer". A friendly default, always editable. */
|
|
253
|
+
function proposeComputerName() {
|
|
254
|
+
const cleaned = hostname().replace(/\.local$/i, "").split(/[^a-zA-Z0-9]+/).filter(Boolean)[0]?.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
255
|
+
return cleaned ? `${cleaned}Computer` : "myComputer";
|
|
256
|
+
}
|
|
257
|
+
/** Provision belongs to this connection; signals release it before closing the transport.
|
|
258
|
+
* A disconnect ends sharing visibly. The caller explicitly starts each new share. */
|
|
259
|
+
async function shareMyComputer(input) {
|
|
260
|
+
try {
|
|
261
|
+
var _usingCtx$2 = _usingCtx();
|
|
262
|
+
const name = input.name || await askComputerName();
|
|
263
|
+
const project = _usingCtx$2.u(await input.connection.session.projects.get(input.project));
|
|
264
|
+
const emit = (event) => {
|
|
265
|
+
if (input.json) process.stdout.write(`${JSON.stringify(event)}\n`);
|
|
266
|
+
};
|
|
267
|
+
_usingCtx$2.u(await project.provide(`itx.${name}`, new MyComputer(emit)));
|
|
268
|
+
emit({
|
|
269
|
+
type: "status",
|
|
270
|
+
loggedIn: true,
|
|
271
|
+
name
|
|
272
|
+
});
|
|
273
|
+
console.error(`itx.${name} is live for project ${input.project}. Press Ctrl-C to stop sharing.`);
|
|
274
|
+
console.error(`Tell your agent to call itx.${name}.__describe() to learn how to use this Mac.`);
|
|
275
|
+
let stop = () => {};
|
|
276
|
+
const stopped = new Promise((resolve) => {
|
|
277
|
+
stop = () => resolve("stopped");
|
|
278
|
+
});
|
|
279
|
+
process.once("SIGINT", stop);
|
|
280
|
+
process.once("SIGTERM", stop);
|
|
281
|
+
if (input.json) {
|
|
282
|
+
process.stdin.once("end", stop);
|
|
283
|
+
process.stdin.resume();
|
|
284
|
+
if (process.stdin.readableEnded) stop();
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
const outcome = await Promise.race([stopped, input.connection.closed]);
|
|
288
|
+
if (outcome !== "stopped") throw new Error(`Computer sharing disconnected (${outcome.code}: ${outcome.reason || "connection closed"}). Run iterate use-my-computer again to reconnect.`);
|
|
289
|
+
} finally {
|
|
290
|
+
process.removeListener("SIGINT", stop);
|
|
291
|
+
process.removeListener("SIGTERM", stop);
|
|
292
|
+
if (input.json) {
|
|
293
|
+
process.stdin.removeListener("end", stop);
|
|
294
|
+
process.stdin.pause();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
} catch (_) {
|
|
298
|
+
_usingCtx$2.e = _;
|
|
299
|
+
} finally {
|
|
300
|
+
_usingCtx$2.d();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/** Run an AppleScript snippet, throwing if osascript reports failure (e.g. the human cancels). */
|
|
304
|
+
async function osascript(script) {
|
|
305
|
+
const result = await run("osascript", ["-e", script]);
|
|
306
|
+
if (result.exitCode !== 0) throw new Error(`osascript failed (exit ${result.exitCode}): ${result.stderr.trim() || "no output"}`);
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
/** Escape a string for embedding in an AppleScript double-quoted literal. */
|
|
310
|
+
const escapeForAppleScript = (text) => text.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
311
|
+
const CONFIG_PATH = join(join(process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config"), "iterate"), "config.json");
|
|
312
|
+
/** Stored session (lives inside a config entry) */
|
|
313
|
+
const StoredSession = z.object({
|
|
314
|
+
token: z.string().optional(),
|
|
315
|
+
refreshToken: z.string().optional(),
|
|
316
|
+
clientId: z.string().optional(),
|
|
317
|
+
scope: z.string().optional(),
|
|
318
|
+
tokenType: z.string().optional(),
|
|
319
|
+
expiresAt: z.string().optional()
|
|
320
|
+
});
|
|
321
|
+
/** A named config — describes which server to talk to and how to authenticate. */
|
|
322
|
+
const Config = z.object({
|
|
323
|
+
defaultProject: z.string().optional(),
|
|
324
|
+
osBaseUrl: z.string().optional().default("https://os.iterate2.com"),
|
|
325
|
+
session: StoredSession.optional()
|
|
326
|
+
});
|
|
327
|
+
/** The config file on disk (~/.config/iterate/config.json) */
|
|
328
|
+
const ConfigFile = z.object({
|
|
329
|
+
configs: z.record(z.string(), Config).optional(),
|
|
330
|
+
default: z.string().optional(),
|
|
331
|
+
/** Maps absolute directory path to a config name */
|
|
332
|
+
workspaces: z.record(z.string(), z.string()).optional()
|
|
333
|
+
});
|
|
334
|
+
const normalizeConfig = (config) => ({
|
|
335
|
+
...config,
|
|
336
|
+
osBaseUrl: config.osBaseUrl.replace(/\/+$/, "")
|
|
337
|
+
});
|
|
338
|
+
const readConfigFile = () => {
|
|
339
|
+
if (!existsSync(CONFIG_PATH)) return {};
|
|
340
|
+
const rawText = readFileSync(CONFIG_PATH, "utf8");
|
|
341
|
+
try {
|
|
342
|
+
return ConfigFile.parse(JSON.parse(rawText));
|
|
343
|
+
} catch (error) {
|
|
344
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
345
|
+
throw new Error(`Invalid JSON in ${CONFIG_PATH}: ${detail}`);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const writeConfigFile = (configFile) => {
|
|
349
|
+
const parsed = ConfigFile.safeParse(configFile);
|
|
350
|
+
if (!parsed.success) throw new Error(`Invalid config file: ${z.prettifyError(parsed.error)}`);
|
|
351
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
352
|
+
writeFileSync(CONFIG_PATH, `${JSON.stringify(parsed.data, null, 2)}\n`, { mode: 384 });
|
|
353
|
+
};
|
|
354
|
+
function readConfig(name, options) {
|
|
355
|
+
const result = (() => {
|
|
356
|
+
const raw = readConfigFile().configs?.[name] ?? (name === "prd" ? {} : void 0);
|
|
357
|
+
if (!raw) return /* @__PURE__ */ new Error(`Config "${name}" not found in ${CONFIG_PATH}`);
|
|
358
|
+
const parsed = Config.safeParse(raw);
|
|
359
|
+
if (!parsed.success) return /* @__PURE__ */ new Error(`Invalid config "${name}" in ${CONFIG_PATH}:\n${z.prettifyError(parsed.error)}`);
|
|
360
|
+
return normalizeConfig(parsed.data);
|
|
361
|
+
})();
|
|
362
|
+
if (result instanceof Error && options?.throw) throw result;
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Merge session fields into the named config's stored session. Extra
|
|
367
|
+
* runtime-only keys on `session` are stripped by the schema on write.
|
|
368
|
+
*/
|
|
369
|
+
const updateConfigSession = (configName, session) => {
|
|
370
|
+
const configFile = readConfigFile();
|
|
371
|
+
configFile.configs ||= {};
|
|
372
|
+
const entry = configFile.configs[configName] ||= normalizeConfig(Config.parse({}));
|
|
373
|
+
entry.session = {
|
|
374
|
+
...entry.session,
|
|
375
|
+
...session
|
|
376
|
+
};
|
|
377
|
+
writeConfigFile(configFile);
|
|
378
|
+
};
|
|
379
|
+
const removeConfigSession = (configName) => {
|
|
380
|
+
const configFile = readConfigFile();
|
|
381
|
+
const entry = configFile.configs?.[configName];
|
|
382
|
+
if (!entry?.session) return;
|
|
383
|
+
delete entry.session;
|
|
384
|
+
writeConfigFile(configFile);
|
|
385
|
+
};
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/cli.ts
|
|
388
|
+
const OAUTH_REFRESH_SKEW_MS = 6e4;
|
|
389
|
+
const isAgent = process$1.env.AGENT === "1" || process$1.env.OPENCODE === "1" || Boolean(process$1.env.OPENCODE_SESSION) || Boolean(process$1.env.CLAUDE_CODE);
|
|
390
|
+
let configFlagOverride;
|
|
391
|
+
const consumeCliStringFlag = (flagName) => {
|
|
392
|
+
const args = process$1.argv.slice(2);
|
|
393
|
+
const flagIndex = args.indexOf(flagName);
|
|
394
|
+
if (flagIndex === -1) return void 0;
|
|
395
|
+
const value = args[flagIndex + 1];
|
|
396
|
+
if (!value || value.startsWith("-")) throw new Error(`${flagName} requires a value`);
|
|
397
|
+
process$1.argv.splice(flagIndex + 2, 2);
|
|
398
|
+
return value;
|
|
399
|
+
};
|
|
400
|
+
const hasConfig = (configFile, name) => name === "prd" || Boolean(configFile.configs?.[name]);
|
|
401
|
+
/**
|
|
402
|
+
* Resolve which config name to use.
|
|
403
|
+
* Priority: --config flag > workspace match (walk up from cwd) > default > single-config auto > built-in prd
|
|
404
|
+
*/
|
|
405
|
+
const resolveConfigName = (workspacePath) => {
|
|
406
|
+
const configFile = readConfigFile();
|
|
407
|
+
if (configFlagOverride) {
|
|
408
|
+
if (!hasConfig(configFile, configFlagOverride)) return /* @__PURE__ */ new Error(`Config "${configFlagOverride}" not found. Available: ${Object.keys(configFile.configs || {}).join(", ") || "(none)"}`);
|
|
409
|
+
return configFlagOverride;
|
|
410
|
+
}
|
|
411
|
+
let dir = workspacePath;
|
|
412
|
+
while (dir && dir !== "/") {
|
|
413
|
+
const match = configFile.workspaces?.[dir];
|
|
414
|
+
if (match) {
|
|
415
|
+
if (!hasConfig(configFile, match)) return /* @__PURE__ */ new Error(`Workspace "${dir}" maps to config "${match}" which doesn't exist.`);
|
|
416
|
+
return match;
|
|
417
|
+
}
|
|
418
|
+
dir = dirname(dir);
|
|
419
|
+
}
|
|
420
|
+
if (configFile.default) {
|
|
421
|
+
if (!hasConfig(configFile, configFile.default)) return /* @__PURE__ */ new Error(`Default config "${configFile.default}" doesn't exist. Available: ${Object.keys(configFile.configs || {}).join(", ") || "(none)"}`);
|
|
422
|
+
return configFile.default;
|
|
423
|
+
}
|
|
424
|
+
const configNames = Object.keys(configFile.configs || {});
|
|
425
|
+
if (configNames.length === 1) return configNames[0];
|
|
426
|
+
return "prd";
|
|
427
|
+
};
|
|
428
|
+
function resolveConfig(workspacePath, options) {
|
|
429
|
+
const result = (() => {
|
|
430
|
+
const name = resolveConfigName(workspacePath);
|
|
431
|
+
if (name instanceof Error) return name;
|
|
432
|
+
const config = readConfig(name);
|
|
433
|
+
if (config instanceof Error) return config;
|
|
434
|
+
return {
|
|
435
|
+
name,
|
|
436
|
+
config
|
|
437
|
+
};
|
|
438
|
+
})();
|
|
439
|
+
if (result instanceof Error && options?.throw) throw result;
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Resolve the config's OAuth credentials before connecting.
|
|
444
|
+
* OAuth sessions are refreshed when possible.
|
|
445
|
+
*/
|
|
446
|
+
const storedCredentials = async (config, configName) => {
|
|
447
|
+
let session = config.session;
|
|
448
|
+
if (!session) throw new Error(`Not logged in to ${config.osBaseUrl}. Run \`iterate login\` first.`);
|
|
449
|
+
if (sessionNeedsRefresh(session)) if (session.refreshToken && session.clientId) session = await refreshOAuthSession({
|
|
450
|
+
config,
|
|
451
|
+
configName,
|
|
452
|
+
session
|
|
453
|
+
});
|
|
454
|
+
else throw new Error(`Session expired for ${config.osBaseUrl}. Run \`iterate login\` again.`);
|
|
455
|
+
if (session.token) return {
|
|
456
|
+
type: "bearer",
|
|
457
|
+
token: session.token
|
|
458
|
+
};
|
|
459
|
+
throw new Error(`No bearer token for ${config.osBaseUrl}. Run \`iterate login\` again.`);
|
|
460
|
+
};
|
|
461
|
+
const sessionNeedsRefresh = (session) => {
|
|
462
|
+
if (!session.expiresAt) return false;
|
|
463
|
+
const expiresAt = Date.parse(session.expiresAt);
|
|
464
|
+
return Number.isFinite(expiresAt) && expiresAt <= Date.now() + OAUTH_REFRESH_SKEW_MS;
|
|
465
|
+
};
|
|
466
|
+
const credentialsForConfig = async (config, name) => {
|
|
467
|
+
const secret = process$1.env.APP_CONFIG_ADMIN_API_SECRET?.trim();
|
|
468
|
+
if (secret) return {
|
|
469
|
+
type: "admin-secret",
|
|
470
|
+
secret
|
|
471
|
+
};
|
|
472
|
+
const token = process$1.env.ITERATE_BEARER_TOKEN?.trim();
|
|
473
|
+
if (token) return {
|
|
474
|
+
type: "bearer",
|
|
475
|
+
token
|
|
476
|
+
};
|
|
477
|
+
return await storedCredentials(config, name);
|
|
478
|
+
};
|
|
479
|
+
const connectConfigured = async () => {
|
|
480
|
+
const resolved = resolveConfig(process$1.cwd(), { throw: true });
|
|
481
|
+
return {
|
|
482
|
+
resolved,
|
|
483
|
+
connection: await connectOsNext({
|
|
484
|
+
baseUrl: resolved.config.osBaseUrl,
|
|
485
|
+
auth: await credentialsForConfig(resolved.config, resolved.name)
|
|
486
|
+
})
|
|
487
|
+
};
|
|
488
|
+
};
|
|
489
|
+
const selectProject = async (connection, configured) => {
|
|
490
|
+
if (configured) return configured;
|
|
491
|
+
const projects = await connection.session.projects.list();
|
|
492
|
+
if (projects.length === 1) return projects[0].id;
|
|
493
|
+
throw new Error(`Pass --project or set defaultProject in ${CONFIG_PATH}. Accessible projects: ${projects.map((p) => `${p.slug} (${p.id})`).join(", ") || "none"}.`);
|
|
494
|
+
};
|
|
495
|
+
const OAUTH_SCOPE = "iterate";
|
|
496
|
+
const LOOPBACK_HOST = "localhost";
|
|
497
|
+
const LOOPBACK_CALLBACK_PATH = "/callback";
|
|
498
|
+
const OAUTH_CALLBACK_TIMEOUT_MS = 300 * 1e3;
|
|
499
|
+
const OAuthTokenResponse = z.object({
|
|
500
|
+
access_token: z.string().min(1),
|
|
501
|
+
refresh_token: z.string().optional(),
|
|
502
|
+
expires_in: z.number().positive().optional(),
|
|
503
|
+
expires_at: z.number().positive().optional(),
|
|
504
|
+
token_type: z.string().optional(),
|
|
505
|
+
scope: z.string().optional()
|
|
506
|
+
});
|
|
507
|
+
const base64Url = (buffer) => buffer.toString("base64url");
|
|
508
|
+
const randomBase64Url = (byteLength = 32) => base64Url(randomBytes(byteLength));
|
|
509
|
+
const oauthResourceForOsBaseUrl = (osBaseUrl) => {
|
|
510
|
+
return new URL("/api", osBaseUrl).href;
|
|
511
|
+
};
|
|
512
|
+
const openUrlInBrowser = async (url) => {
|
|
513
|
+
const { execFile } = await import("node:child_process");
|
|
514
|
+
const { command, args } = process$1.platform === "darwin" ? {
|
|
515
|
+
command: "open",
|
|
516
|
+
args: [url]
|
|
517
|
+
} : process$1.platform === "win32" ? {
|
|
518
|
+
command: "cmd",
|
|
519
|
+
args: [
|
|
520
|
+
"/c",
|
|
521
|
+
"start",
|
|
522
|
+
"",
|
|
523
|
+
url
|
|
524
|
+
]
|
|
525
|
+
} : {
|
|
526
|
+
command: "xdg-open",
|
|
527
|
+
args: [url]
|
|
528
|
+
};
|
|
529
|
+
execFile(command, args, (error) => {
|
|
530
|
+
if (error) console.error(`Could not open a browser: ${error.message}. Open the URL above manually.`);
|
|
531
|
+
});
|
|
532
|
+
};
|
|
533
|
+
const readErrorBody = async (response) => {
|
|
534
|
+
const text = await response.text();
|
|
535
|
+
return text.length > 300 ? `${text.slice(0, 300)}...` : text;
|
|
536
|
+
};
|
|
537
|
+
const registerOAuthClient = async (input) => {
|
|
538
|
+
const response = await fetch(`${input.authBaseUrl}/oauth2/register`, {
|
|
539
|
+
method: "POST",
|
|
540
|
+
signal: AbortSignal.timeout(3e4),
|
|
541
|
+
headers: {
|
|
542
|
+
"content-type": "application/json",
|
|
543
|
+
origin: input.authBaseUrl
|
|
544
|
+
},
|
|
545
|
+
body: JSON.stringify({
|
|
546
|
+
client_name: "Iterate CLI",
|
|
547
|
+
redirect_uris: [input.redirectUri],
|
|
548
|
+
token_endpoint_auth_method: "none",
|
|
549
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
550
|
+
response_types: ["code"],
|
|
551
|
+
scope: OAUTH_SCOPE,
|
|
552
|
+
type: "native",
|
|
553
|
+
require_pkce: true
|
|
554
|
+
})
|
|
555
|
+
});
|
|
556
|
+
if (!response.ok) throw new Error(`OAuth client registration failed (${response.status}): ${await readErrorBody(response)}`);
|
|
557
|
+
const client = z.object({ client_id: z.string().min(1) }).parse(await response.json());
|
|
558
|
+
if (!client.client_id) throw new Error("OAuth client registration did not return client_id.");
|
|
559
|
+
return client.client_id;
|
|
560
|
+
};
|
|
561
|
+
const startOAuthCallbackServer = async () => {
|
|
562
|
+
let settled = false;
|
|
563
|
+
let resolveCallback;
|
|
564
|
+
let rejectCallback;
|
|
565
|
+
const callbackPromise = new Promise((resolve, reject) => {
|
|
566
|
+
resolveCallback = resolve;
|
|
567
|
+
rejectCallback = reject;
|
|
568
|
+
});
|
|
569
|
+
callbackPromise.catch(() => {});
|
|
570
|
+
const server = createServer((request, response) => {
|
|
571
|
+
if (settled) {
|
|
572
|
+
response.writeHead(409, { "content-type": "text/plain; charset=utf-8" });
|
|
573
|
+
response.end("OAuth callback already received.");
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const url = new URL(request.url ?? "/", `http://${LOOPBACK_HOST}`);
|
|
577
|
+
if (url.pathname !== LOOPBACK_CALLBACK_PATH) {
|
|
578
|
+
response.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
579
|
+
response.end("Not found.");
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const error = url.searchParams.get("error");
|
|
583
|
+
if (error) {
|
|
584
|
+
settled = true;
|
|
585
|
+
response.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
586
|
+
response.end("<h1>Iterate login failed</h1><p>You can return to the terminal.</p>");
|
|
587
|
+
rejectCallback?.(/* @__PURE__ */ new Error(`OAuth authorization failed: ${error}${url.searchParams.get("error_description") ? ` (${url.searchParams.get("error_description")})` : ""}`));
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
const code = url.searchParams.get("code");
|
|
591
|
+
const state = url.searchParams.get("state");
|
|
592
|
+
if (!code || !state) {
|
|
593
|
+
settled = true;
|
|
594
|
+
response.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
595
|
+
response.end("<h1>Iterate login failed</h1><p>Missing code or state.</p>");
|
|
596
|
+
rejectCallback?.(/* @__PURE__ */ new Error("OAuth callback was missing code or state."));
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
settled = true;
|
|
600
|
+
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
601
|
+
response.end("<h1>Iterate login complete</h1><p>You can close this tab.</p>");
|
|
602
|
+
const address = server.address();
|
|
603
|
+
const port = typeof address === "object" && address ? address.port : 0;
|
|
604
|
+
resolveCallback?.({
|
|
605
|
+
code,
|
|
606
|
+
state,
|
|
607
|
+
redirectUri: `http://${LOOPBACK_HOST}:${port}${LOOPBACK_CALLBACK_PATH}`
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
await new Promise((resolve, reject) => {
|
|
611
|
+
server.once("error", reject);
|
|
612
|
+
server.listen(0, LOOPBACK_HOST, () => resolve());
|
|
613
|
+
});
|
|
614
|
+
const timeout = setTimeout(() => {
|
|
615
|
+
if (!settled) {
|
|
616
|
+
settled = true;
|
|
617
|
+
rejectCallback?.(/* @__PURE__ */ new Error("Timed out waiting for OAuth callback."));
|
|
618
|
+
}
|
|
619
|
+
}, OAUTH_CALLBACK_TIMEOUT_MS);
|
|
620
|
+
timeout.unref();
|
|
621
|
+
const address = server.address();
|
|
622
|
+
return {
|
|
623
|
+
redirectUri: `http://${LOOPBACK_HOST}:${typeof address === "object" && address ? address.port : 0}${LOOPBACK_CALLBACK_PATH}`,
|
|
624
|
+
wait: () => callbackPromise.finally(() => clearTimeout(timeout)),
|
|
625
|
+
close: () => {
|
|
626
|
+
clearTimeout(timeout);
|
|
627
|
+
return new Promise((resolve) => server.close(() => resolve()));
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
};
|
|
631
|
+
const exchangeOAuthCode = async (input) => {
|
|
632
|
+
const body = new URLSearchParams({
|
|
633
|
+
grant_type: "authorization_code",
|
|
634
|
+
client_id: input.clientId,
|
|
635
|
+
code: input.code,
|
|
636
|
+
redirect_uri: input.redirectUri,
|
|
637
|
+
code_verifier: input.codeVerifier,
|
|
638
|
+
resource: input.resource
|
|
639
|
+
});
|
|
640
|
+
const response = await fetch(`${input.authBaseUrl}/oauth2/token`, {
|
|
641
|
+
method: "POST",
|
|
642
|
+
signal: AbortSignal.timeout(3e4),
|
|
643
|
+
headers: {
|
|
644
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
645
|
+
origin: input.authBaseUrl
|
|
646
|
+
},
|
|
647
|
+
body
|
|
648
|
+
});
|
|
649
|
+
if (!response.ok) throw new Error(`OAuth token exchange failed (${response.status}): ${await readErrorBody(response)}`);
|
|
650
|
+
const token = OAuthTokenResponse.parse(await response.json());
|
|
651
|
+
if (!token.access_token) throw new Error("OAuth token exchange did not return access_token.");
|
|
652
|
+
return token;
|
|
653
|
+
};
|
|
654
|
+
const oauthTokenToSession = (token, existing) => {
|
|
655
|
+
const expiresAtMs = token.expires_at ? token.expires_at * 1e3 : token.expires_in ? Date.now() + token.expires_in * 1e3 : void 0;
|
|
656
|
+
return {
|
|
657
|
+
token: token.access_token,
|
|
658
|
+
refreshToken: token.refresh_token ?? existing?.refreshToken,
|
|
659
|
+
clientId: existing?.clientId,
|
|
660
|
+
scope: token.scope,
|
|
661
|
+
tokenType: token.token_type,
|
|
662
|
+
expiresAt: expiresAtMs ? new Date(expiresAtMs).toISOString() : void 0
|
|
663
|
+
};
|
|
664
|
+
};
|
|
665
|
+
const refreshOAuthSession = async (input) => {
|
|
666
|
+
if (!input.session.refreshToken || !input.session.clientId) throw new Error(`Session expired for ${input.config.osBaseUrl}. Run \`iterate login\` again.`);
|
|
667
|
+
const authBaseUrl = input.config.osBaseUrl;
|
|
668
|
+
const body = new URLSearchParams({
|
|
669
|
+
grant_type: "refresh_token",
|
|
670
|
+
client_id: input.session.clientId,
|
|
671
|
+
refresh_token: input.session.refreshToken,
|
|
672
|
+
resource: oauthResourceForOsBaseUrl(input.config.osBaseUrl)
|
|
673
|
+
});
|
|
674
|
+
if (input.session.scope) body.set("scope", input.session.scope);
|
|
675
|
+
const response = await fetch(`${authBaseUrl}/oauth2/token`, {
|
|
676
|
+
method: "POST",
|
|
677
|
+
signal: AbortSignal.timeout(3e4),
|
|
678
|
+
headers: {
|
|
679
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
680
|
+
origin: authBaseUrl
|
|
681
|
+
},
|
|
682
|
+
body
|
|
683
|
+
});
|
|
684
|
+
if (!response.ok) throw new Error(`OAuth refresh failed (${response.status}). Run \`iterate login\` again.`);
|
|
685
|
+
const refreshedSession = oauthTokenToSession(OAuthTokenResponse.parse(await response.json()), input.session);
|
|
686
|
+
refreshedSession.clientId = input.session.clientId;
|
|
687
|
+
input.config.session = refreshedSession;
|
|
688
|
+
if (input.configName) updateConfigSession(input.configName, refreshedSession);
|
|
689
|
+
return refreshedSession;
|
|
690
|
+
};
|
|
691
|
+
const oauthLogin = async (config) => {
|
|
692
|
+
const authBaseUrl = config.osBaseUrl;
|
|
693
|
+
const resource = oauthResourceForOsBaseUrl(config.osBaseUrl);
|
|
694
|
+
const codeVerifier = randomBase64Url(48);
|
|
695
|
+
const state = randomBase64Url(32);
|
|
696
|
+
const callback = await startOAuthCallbackServer();
|
|
697
|
+
try {
|
|
698
|
+
const clientId = await registerOAuthClient({
|
|
699
|
+
authBaseUrl,
|
|
700
|
+
redirectUri: callback.redirectUri
|
|
701
|
+
});
|
|
702
|
+
const authorizeUrl = new URL(`${authBaseUrl}/oauth2/auth`);
|
|
703
|
+
authorizeUrl.searchParams.set("response_type", "code");
|
|
704
|
+
authorizeUrl.searchParams.set("client_id", clientId);
|
|
705
|
+
authorizeUrl.searchParams.set("redirect_uri", callback.redirectUri);
|
|
706
|
+
authorizeUrl.searchParams.set("scope", OAUTH_SCOPE);
|
|
707
|
+
authorizeUrl.searchParams.set("resource", resource);
|
|
708
|
+
authorizeUrl.searchParams.set("state", state);
|
|
709
|
+
authorizeUrl.searchParams.set("code_challenge", base64Url(createHash("sha256").update(codeVerifier).digest()));
|
|
710
|
+
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
711
|
+
console.error(`\nOpening browser to authenticate with Iterate:\n`);
|
|
712
|
+
console.error(` ${authorizeUrl.href}\n`);
|
|
713
|
+
if (!isAgent && process$1.env.ITERATE_SKIP_BROWSER_OPEN !== "1") await openUrlInBrowser(authorizeUrl.href);
|
|
714
|
+
const callbackResult = await callback.wait();
|
|
715
|
+
if (callbackResult.state !== state) throw new Error("OAuth callback state did not match. Please try again.");
|
|
716
|
+
const session = oauthTokenToSession(await exchangeOAuthCode({
|
|
717
|
+
authBaseUrl,
|
|
718
|
+
clientId,
|
|
719
|
+
code: callbackResult.code,
|
|
720
|
+
codeVerifier,
|
|
721
|
+
redirectUri: callbackResult.redirectUri,
|
|
722
|
+
resource
|
|
723
|
+
}), {
|
|
724
|
+
clientId,
|
|
725
|
+
refreshToken: void 0
|
|
726
|
+
});
|
|
727
|
+
session.clientId = clientId;
|
|
728
|
+
return session;
|
|
729
|
+
} finally {
|
|
730
|
+
await callback.close();
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
const loginToResolvedConfig = async (resolved) => {
|
|
734
|
+
try {
|
|
735
|
+
var _usingCtx$1 = _usingCtx();
|
|
736
|
+
const { config } = resolved;
|
|
737
|
+
console.error(`Logging in to ${config.osBaseUrl}...`);
|
|
738
|
+
const oauthResult = await oauthLogin(config);
|
|
739
|
+
config.session = oauthResult;
|
|
740
|
+
await _usingCtx$1.u(await connectOsNext({
|
|
741
|
+
baseUrl: config.osBaseUrl,
|
|
742
|
+
auth: {
|
|
743
|
+
type: "bearer",
|
|
744
|
+
token: oauthResult.token
|
|
745
|
+
}
|
|
746
|
+
})).session.whoami();
|
|
747
|
+
updateConfigSession(resolved.name, oauthResult);
|
|
748
|
+
return oauthResult;
|
|
749
|
+
} catch (_) {
|
|
750
|
+
_usingCtx$1.e = _;
|
|
751
|
+
} finally {
|
|
752
|
+
_usingCtx$1.d();
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
const launcherProcedures = {
|
|
756
|
+
ping: os.input(z.object({})).handler(async () => {
|
|
757
|
+
try {
|
|
758
|
+
var _usingCtx3 = _usingCtx();
|
|
759
|
+
const { connection } = await connectConfigured();
|
|
760
|
+
return {
|
|
761
|
+
message: "OS Next session valid",
|
|
762
|
+
principal: await _usingCtx3.u(connection).session.whoami()
|
|
763
|
+
};
|
|
764
|
+
} catch (_) {
|
|
765
|
+
_usingCtx3.e = _;
|
|
766
|
+
} finally {
|
|
767
|
+
_usingCtx3.d();
|
|
768
|
+
}
|
|
769
|
+
}),
|
|
770
|
+
login: os.input(z.object({})).meta({ description: "Authenticate with OS Next via browser OAuth" }).handler(async () => {
|
|
771
|
+
const session = await loginToResolvedConfig(resolveConfig(process$1.cwd(), { throw: true }));
|
|
772
|
+
return {
|
|
773
|
+
message: "Logged in successfully",
|
|
774
|
+
expiresAt: session.expiresAt,
|
|
775
|
+
scope: session.scope
|
|
776
|
+
};
|
|
777
|
+
}),
|
|
778
|
+
logout: os.input(z.object({})).meta({ description: "Remove the current config's stored session" }).handler(async () => {
|
|
779
|
+
const resolved = resolveConfig(process$1.cwd(), { throw: true });
|
|
780
|
+
removeConfigSession(resolved.name);
|
|
781
|
+
return { message: `Logged out from ${resolved.name}` };
|
|
782
|
+
}),
|
|
783
|
+
orgs: { list: os.input(z.object({})).handler(async () => {
|
|
784
|
+
try {
|
|
785
|
+
var _usingCtx4 = _usingCtx();
|
|
786
|
+
const { connection } = await connectConfigured();
|
|
787
|
+
return await _usingCtx4.u(connection).session.orgs();
|
|
788
|
+
} catch (_) {
|
|
789
|
+
_usingCtx4.e = _;
|
|
790
|
+
} finally {
|
|
791
|
+
_usingCtx4.d();
|
|
792
|
+
}
|
|
793
|
+
}) },
|
|
794
|
+
projects: { list: os.input(z.object({})).handler(async () => {
|
|
795
|
+
try {
|
|
796
|
+
var _usingCtx5 = _usingCtx();
|
|
797
|
+
const { connection } = await connectConfigured();
|
|
798
|
+
return await _usingCtx5.u(connection).session.projects.list();
|
|
799
|
+
} catch (_) {
|
|
800
|
+
_usingCtx5.e = _;
|
|
801
|
+
} finally {
|
|
802
|
+
_usingCtx5.d();
|
|
803
|
+
}
|
|
804
|
+
}) },
|
|
805
|
+
repl: os.input(z.object({
|
|
806
|
+
project: z.string().optional().describe("Project id or slug"),
|
|
807
|
+
context: z.string().default("/").describe("Context path within the project")
|
|
808
|
+
})).meta({ description: "Open a local Node REPL with itx and RpcTarget in scope" }).handler(async ({ input }) => {
|
|
809
|
+
try {
|
|
810
|
+
var _usingCtx6 = _usingCtx();
|
|
811
|
+
const { resolved, connection } = await connectConfigured();
|
|
812
|
+
const owned = _usingCtx6.u(connection);
|
|
813
|
+
const project = input.project || resolved.config.defaultProject;
|
|
814
|
+
if (!project && input.context !== "/") throw new Error("--context requires --project or a configured defaultProject.");
|
|
815
|
+
const root = _usingCtx6.u(project ? await owned.session.projects.get(project) : null);
|
|
816
|
+
const context = _usingCtx6.u(root ? await root.cd(input.context) : null);
|
|
817
|
+
console.error(`Connected to ${resolved.config.osBaseUrl}, ${project ? `project ${project}, context ${input.context}` : "session"}. Use .exit to quit.`);
|
|
818
|
+
const server = repl.start({
|
|
819
|
+
prompt: "itx> ",
|
|
820
|
+
useGlobal: true
|
|
821
|
+
});
|
|
822
|
+
const initialize = () => {
|
|
823
|
+
server.context.itx = context || owned.session;
|
|
824
|
+
server.context.RpcTarget = RpcTarget;
|
|
825
|
+
};
|
|
826
|
+
initialize();
|
|
827
|
+
server.on("reset", initialize);
|
|
828
|
+
try {
|
|
829
|
+
const outcome = await Promise.race([new Promise((resolve) => server.once("exit", () => resolve("exit"))), owned.closed]);
|
|
830
|
+
if (outcome !== "exit") throw new Error(`REPL disconnected (${outcome.code}: ${outcome.reason || "connection closed"}). Start a new REPL to reconnect.`);
|
|
831
|
+
} finally {
|
|
832
|
+
server.close();
|
|
833
|
+
}
|
|
834
|
+
} catch (_) {
|
|
835
|
+
_usingCtx6.e = _;
|
|
836
|
+
} finally {
|
|
837
|
+
_usingCtx6.d();
|
|
838
|
+
}
|
|
839
|
+
}),
|
|
840
|
+
itx: { run: os.input(z.object({
|
|
841
|
+
project: z.string().optional().describe("Project id or slug; defaults to config.defaultProject"),
|
|
842
|
+
context: z.string().default("/").describe("Context path within the project"),
|
|
843
|
+
eval: z.string().optional().describe("Script body with itx in scope; use return for the result"),
|
|
844
|
+
file: z.string().optional().describe("Read the script from a UTF-8 file; - reads stdin")
|
|
845
|
+
}).refine((input) => Boolean(input.eval) !== Boolean(input.file), "Specify exactly one of --eval or --file")).meta({ description: "Run an itx script once on OS Next" }).handler(async ({ input }) => {
|
|
846
|
+
try {
|
|
847
|
+
var _usingCtx7 = _usingCtx();
|
|
848
|
+
let script = input.eval;
|
|
849
|
+
if (input.file === "-") {
|
|
850
|
+
const chunks = [];
|
|
851
|
+
for await (const chunk of process$1.stdin) chunks.push(Buffer.from(chunk));
|
|
852
|
+
script = Buffer.concat(chunks).toString("utf8");
|
|
853
|
+
} else if (input.file) script = await readFile(input.file, "utf8");
|
|
854
|
+
const { resolved, connection } = await connectConfigured();
|
|
855
|
+
const owned = _usingCtx7.u(connection);
|
|
856
|
+
const project = await selectProject(owned, input.project || resolved.config.defaultProject);
|
|
857
|
+
const root = _usingCtx7.u(await owned.session.projects.get(project));
|
|
858
|
+
return await _usingCtx7.u(await root.cd(input.context)).run(`async (itx) => {\n${script}\n}`);
|
|
859
|
+
} catch (_) {
|
|
860
|
+
_usingCtx7.e = _;
|
|
861
|
+
} finally {
|
|
862
|
+
_usingCtx7.d();
|
|
863
|
+
}
|
|
864
|
+
}) },
|
|
865
|
+
menubar: os.input(z.object({ project: z.string().optional().describe("Project id or slug") })).meta({ description: "Launch the macOS menu bar for sign-in and computer sharing" }).handler(async ({ input }) => {
|
|
866
|
+
const resolved = resolveConfig(process$1.cwd(), { throw: true });
|
|
867
|
+
const project = input.project || resolved.config.defaultProject;
|
|
868
|
+
if (!project) throw new Error("menubar needs --project or a configured defaultProject.");
|
|
869
|
+
await launchMenubarApp({
|
|
870
|
+
configName: resolved.name,
|
|
871
|
+
project,
|
|
872
|
+
log: console.error
|
|
873
|
+
});
|
|
874
|
+
}),
|
|
875
|
+
useMyComputer: os.input(z.object({
|
|
876
|
+
project: z.string().optional().describe("Project id or slug"),
|
|
877
|
+
json: z.boolean().optional().describe("Emit menu-bar events as NDJSON; stop on stdin EOF"),
|
|
878
|
+
name: z.string().regex(/^[a-zA-Z][a-zA-Z0-9]*$/).optional().describe("Computer capability name, e.g. jonasComputer")
|
|
879
|
+
})).meta({ description: "Share this Mac with a project until Ctrl-C" }).handler(async ({ input }) => {
|
|
880
|
+
try {
|
|
881
|
+
var _usingCtx8 = _usingCtx();
|
|
882
|
+
if (process$1.platform !== "darwin") throw new Error("use-my-computer requires macOS (AppleScript and Swift).");
|
|
883
|
+
const { resolved, connection } = await connectConfigured();
|
|
884
|
+
const owned = _usingCtx8.u(connection);
|
|
885
|
+
await shareMyComputer({
|
|
886
|
+
connection: owned,
|
|
887
|
+
project: await selectProject(owned, input.project || resolved.config.defaultProject),
|
|
888
|
+
name: input.name,
|
|
889
|
+
json: input.json
|
|
890
|
+
});
|
|
891
|
+
} catch (_) {
|
|
892
|
+
_usingCtx8.e = _;
|
|
893
|
+
} finally {
|
|
894
|
+
_usingCtx8.d();
|
|
895
|
+
}
|
|
896
|
+
}),
|
|
897
|
+
config: {
|
|
898
|
+
get: os.input(z.object({})).meta({
|
|
899
|
+
default: true,
|
|
900
|
+
description: "Show config, resolved target, and session status"
|
|
901
|
+
}).handler(async () => {
|
|
902
|
+
const configFile = readConfigFile();
|
|
903
|
+
const resolved = resolveConfig(process$1.cwd());
|
|
904
|
+
const configs = configFile.configs || {};
|
|
905
|
+
const sessions = Object.fromEntries(Object.entries(configs).map(([name, cfg]) => {
|
|
906
|
+
if (!cfg.session) return [name, null];
|
|
907
|
+
return [name, {
|
|
908
|
+
hasToken: Boolean(cfg.session?.token),
|
|
909
|
+
expiresAt: cfg.session?.expiresAt,
|
|
910
|
+
expired: cfg.session?.expiresAt ? new Date(cfg.session.expiresAt) < /* @__PURE__ */ new Date() : false
|
|
911
|
+
}];
|
|
912
|
+
}));
|
|
913
|
+
if (resolved instanceof Error) return {
|
|
914
|
+
configPath: CONFIG_PATH,
|
|
915
|
+
error: resolved.message
|
|
916
|
+
};
|
|
917
|
+
return {
|
|
918
|
+
configPath: CONFIG_PATH,
|
|
919
|
+
config: resolved.name,
|
|
920
|
+
...resolved.config,
|
|
921
|
+
session: sessions[resolved.name]
|
|
922
|
+
};
|
|
923
|
+
}),
|
|
924
|
+
list: os.input(z.object({})).meta({ description: "List all named configs" }).handler(async () => {
|
|
925
|
+
const configFile = readConfigFile();
|
|
926
|
+
const currentName = resolveConfigName(process$1.cwd());
|
|
927
|
+
const configs = {
|
|
928
|
+
["prd"]: {},
|
|
929
|
+
...configFile.configs || {}
|
|
930
|
+
};
|
|
931
|
+
return {
|
|
932
|
+
configs: Object.fromEntries(Object.entries(configs).map(([name, cfg]) => [name, {
|
|
933
|
+
osBaseUrl: Config.parse(cfg).osBaseUrl,
|
|
934
|
+
active: name === currentName ? true : void 0
|
|
935
|
+
}])),
|
|
936
|
+
default: configFile.default
|
|
937
|
+
};
|
|
938
|
+
}),
|
|
939
|
+
set: os.input(z.object({
|
|
940
|
+
name: z.string().describe("Config name (e.g. dev, prd, preview)"),
|
|
941
|
+
osBaseUrl: z.string().optional().describe("Base URL for OS API (e.g. https://os.iterate2.com)"),
|
|
942
|
+
defaultProject: z.string().optional().describe("Default project id or slug"),
|
|
943
|
+
setDefault: z.boolean().optional().describe("Set as the default config"),
|
|
944
|
+
setWorkspace: z.boolean().optional().describe("Map current directory to this config")
|
|
945
|
+
})).meta({ description: "Create or update a named config" }).handler(async ({ input }) => {
|
|
946
|
+
const configFile = readConfigFile();
|
|
947
|
+
configFile.configs ||= {};
|
|
948
|
+
configFile.configs[input.name] ||= Config.parse({});
|
|
949
|
+
if (input.osBaseUrl && input.osBaseUrl !== configFile.configs[input.name].osBaseUrl) {
|
|
950
|
+
configFile.configs[input.name].osBaseUrl = input.osBaseUrl;
|
|
951
|
+
delete configFile.configs[input.name].session;
|
|
952
|
+
}
|
|
953
|
+
if (input.defaultProject) configFile.configs[input.name].defaultProject = input.defaultProject;
|
|
954
|
+
if (input.setDefault) configFile.default = input.name;
|
|
955
|
+
if (input.setWorkspace) {
|
|
956
|
+
configFile.workspaces ||= {};
|
|
957
|
+
configFile.workspaces[process$1.cwd()] = input.name;
|
|
958
|
+
}
|
|
959
|
+
writeConfigFile(configFile);
|
|
960
|
+
return {
|
|
961
|
+
configPath: CONFIG_PATH,
|
|
962
|
+
config: {
|
|
963
|
+
...configFile.configs[input.name],
|
|
964
|
+
session: void 0
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
}),
|
|
968
|
+
use: os.input(z.object({ name: z.string().meta({ positional: true }).describe("Config name to set as default") })).meta({ description: "Set the default config" }).handler(async ({ input }) => {
|
|
969
|
+
const configFile = readConfigFile();
|
|
970
|
+
if (input.name !== "prd" && !configFile.configs?.[input.name]) throw new Error(`Config "${input.name}" not found. Available: ${Object.keys(configFile.configs || {}).join(", ") || "(none)"}`);
|
|
971
|
+
configFile.default = input.name;
|
|
972
|
+
writeConfigFile(configFile);
|
|
973
|
+
return { default: input.name };
|
|
974
|
+
}),
|
|
975
|
+
current: os.input(z.object({})).meta({ description: "Show which config is active and why" }).handler(async () => {
|
|
976
|
+
const resolved = resolveConfig(process$1.cwd(), { throw: true });
|
|
977
|
+
return {
|
|
978
|
+
name: resolved.name,
|
|
979
|
+
config: {
|
|
980
|
+
...resolved.config,
|
|
981
|
+
session: resolved.config.session ? { loggedIn: true } : void 0
|
|
982
|
+
},
|
|
983
|
+
resolvedVia: configFlagOverride ? "--config flag" : "workspace mapping or default"
|
|
984
|
+
};
|
|
985
|
+
})
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
const getCli = async () => {
|
|
989
|
+
configFlagOverride = consumeCliStringFlag("--config");
|
|
990
|
+
if (process$1.argv.length === 2) process$1.argv.push("--help");
|
|
991
|
+
return {
|
|
992
|
+
cli: createCli({
|
|
993
|
+
router: launcherProcedures,
|
|
994
|
+
name: "iterate",
|
|
995
|
+
description: "Iterate CLI for OS Next. Run itx scripts, authenticate, and share your computer."
|
|
996
|
+
}),
|
|
997
|
+
prompts: isAgent || !process$1.stdin.isTTY || !process$1.stdout.isTTY ? void 0 : prompts
|
|
998
|
+
};
|
|
999
|
+
};
|
|
1000
|
+
const runCli = async () => {
|
|
1001
|
+
const { cli, prompts: cliPrompts } = await getCli();
|
|
1002
|
+
await cli.run({
|
|
1003
|
+
prompts: cliPrompts,
|
|
1004
|
+
logger: yamlTableConsoleLogger
|
|
1005
|
+
});
|
|
1006
|
+
};
|
|
1007
|
+
//#endregion
|
|
1008
|
+
export { runCli };
|
|
1009
|
+
|
|
1010
|
+
//# sourceMappingURL=cli-D0c-pDL_.mjs.map
|