@geteigen/cli 0.0.0-stage → 3.1.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.
Files changed (172) hide show
  1. package/README.md +154 -2
  2. package/dist/agent.d.ts +32 -0
  3. package/dist/agent.js +246 -0
  4. package/dist/auth.d.ts +5 -0
  5. package/dist/auth.js +33 -0
  6. package/dist/cli/src/agent.d.ts +24 -0
  7. package/dist/cli/src/agent.js +263 -0
  8. package/dist/cli/src/agentTools.d.ts +6 -0
  9. package/dist/cli/src/agentTools.js +104 -0
  10. package/dist/cli/src/auth.d.ts +5 -0
  11. package/dist/cli/src/auth.js +33 -0
  12. package/dist/cli/src/cliEnv.d.ts +6 -0
  13. package/dist/cli/src/cliEnv.js +26 -0
  14. package/dist/cli/src/config.d.ts +11 -0
  15. package/dist/cli/src/config.js +51 -0
  16. package/dist/cli/src/cursorAuth.d.ts +10 -0
  17. package/dist/cli/src/cursorAuth.js +92 -0
  18. package/dist/cli/src/gitAuthEnv.d.ts +5 -0
  19. package/dist/cli/src/gitAuthEnv.js +35 -0
  20. package/dist/cli/src/index.d.ts +2 -0
  21. package/dist/cli/src/index.js +244 -0
  22. package/dist/cli/src/listModels.d.ts +14 -0
  23. package/dist/cli/src/listModels.js +196 -0
  24. package/dist/cli/src/listSessions.d.ts +9 -0
  25. package/dist/cli/src/listSessions.js +37 -0
  26. package/dist/cli/src/machineConfig.d.ts +20 -0
  27. package/dist/cli/src/machineConfig.js +66 -0
  28. package/dist/cli/src/pickFolder.d.ts +5 -0
  29. package/dist/cli/src/pickFolder.js +65 -0
  30. package/dist/cli/src/proc.d.ts +31 -0
  31. package/dist/cli/src/proc.js +97 -0
  32. package/dist/cli/src/service.d.ts +17 -0
  33. package/dist/cli/src/service.js +255 -0
  34. package/dist/cli/src/workRoots.d.ts +30 -0
  35. package/dist/cli/src/workRoots.js +228 -0
  36. package/dist/cli/src/worker.d.ts +8 -0
  37. package/dist/cli/src/worker.js +51 -0
  38. package/dist/cli/src/workerCore.d.ts +47 -0
  39. package/dist/cli/src/workerCore.js +721 -0
  40. package/dist/cli/src/workspace.d.ts +10 -0
  41. package/dist/cli/src/workspace.js +329 -0
  42. package/dist/config.d.ts +11 -0
  43. package/dist/config.js +51 -0
  44. package/dist/cursorAuth.d.ts +10 -0
  45. package/dist/cursorAuth.js +95 -0
  46. package/dist/index.d.ts +2 -0
  47. package/dist/index.js +108 -0
  48. package/dist/listModels.d.ts +6 -0
  49. package/dist/listModels.js +62 -0
  50. package/dist/listSessions.d.ts +9 -0
  51. package/dist/listSessions.js +37 -0
  52. package/dist/pickFolder.d.ts +5 -0
  53. package/dist/pickFolder.js +65 -0
  54. package/dist/shared/actionDrafts.d.ts +83 -0
  55. package/dist/shared/actionDrafts.js +169 -0
  56. package/dist/shared/agentArtifacts.d.ts +49 -0
  57. package/dist/shared/agentArtifacts.js +169 -0
  58. package/dist/shared/agentLabel.d.ts +18 -0
  59. package/dist/shared/agentLabel.js +22 -0
  60. package/dist/shared/agentMentions.d.ts +38 -0
  61. package/dist/shared/agentMentions.js +110 -0
  62. package/dist/shared/agentRepos.d.ts +28 -0
  63. package/dist/shared/agentRepos.js +60 -0
  64. package/dist/shared/agentRunStatus.d.ts +12 -0
  65. package/dist/shared/agentRunStatus.js +17 -0
  66. package/dist/shared/agentUsage.d.ts +24 -0
  67. package/dist/shared/agentUsage.js +139 -0
  68. package/dist/shared/approvals.d.ts +44 -0
  69. package/dist/shared/approvals.js +97 -0
  70. package/dist/shared/askEigen.d.ts +190 -0
  71. package/dist/shared/askEigen.js +76 -0
  72. package/dist/shared/attribution.d.ts +25 -0
  73. package/dist/shared/attribution.js +63 -0
  74. package/dist/shared/automations.d.ts +135 -0
  75. package/dist/shared/automations.js +553 -0
  76. package/dist/shared/backends/claudeCode.d.ts +44 -0
  77. package/dist/shared/backends/claudeCode.js +548 -0
  78. package/dist/shared/backends/codex.d.ts +15 -0
  79. package/dist/shared/backends/codex.js +287 -0
  80. package/dist/shared/backends/cursor.d.ts +111 -0
  81. package/dist/shared/backends/cursor.js +1216 -0
  82. package/dist/shared/backends/index.d.ts +18 -0
  83. package/dist/shared/backends/index.js +58 -0
  84. package/dist/shared/backends/opencode.d.ts +43 -0
  85. package/dist/shared/backends/opencode.js +415 -0
  86. package/dist/shared/backends/types.d.ts +171 -0
  87. package/dist/shared/backends/types.js +19 -0
  88. package/dist/shared/backends/usage.d.ts +8 -0
  89. package/dist/shared/backends/usage.js +71 -0
  90. package/dist/shared/billingPlans.d.ts +60 -0
  91. package/dist/shared/billingPlans.js +262 -0
  92. package/dist/shared/brain.d.ts +353 -0
  93. package/dist/shared/brain.js +487 -0
  94. package/dist/shared/clarifyingQuestions.d.ts +13 -0
  95. package/dist/shared/clarifyingQuestions.js +207 -0
  96. package/dist/shared/claudeModels.d.ts +10 -0
  97. package/dist/shared/claudeModels.js +52 -0
  98. package/dist/shared/cloudBackends.d.ts +21 -0
  99. package/dist/shared/cloudBackends.js +29 -0
  100. package/dist/shared/codexModels.d.ts +10 -0
  101. package/dist/shared/codexModels.js +28 -0
  102. package/dist/shared/contextWindows.d.ts +2 -0
  103. package/dist/shared/contextWindows.js +30 -0
  104. package/dist/shared/cursorMcpConfig.d.ts +14 -0
  105. package/dist/shared/cursorMcpConfig.js +43 -0
  106. package/dist/shared/cursorProjectMcp.d.ts +10 -0
  107. package/dist/shared/cursorProjectMcp.js +42 -0
  108. package/dist/shared/docs.d.ts +59 -0
  109. package/dist/shared/docs.js +87 -0
  110. package/dist/shared/events.d.ts +932 -0
  111. package/dist/shared/events.js +57 -0
  112. package/dist/shared/integrations.d.ts +266 -0
  113. package/dist/shared/integrations.js +449 -0
  114. package/dist/shared/issueTranscript.d.ts +64 -0
  115. package/dist/shared/issueTranscript.js +387 -0
  116. package/dist/shared/issues.d.ts +139 -0
  117. package/dist/shared/issues.js +217 -0
  118. package/dist/shared/machines.d.ts +119 -0
  119. package/dist/shared/machines.js +58 -0
  120. package/dist/shared/markdownMedia.d.ts +13 -0
  121. package/dist/shared/markdownMedia.js +41 -0
  122. package/dist/shared/notificationText.d.ts +6 -0
  123. package/dist/shared/notificationText.js +38 -0
  124. package/dist/shared/opencodeModels.d.ts +19 -0
  125. package/dist/shared/opencodeModels.js +62 -0
  126. package/dist/shared/opencodeSession.d.ts +7 -0
  127. package/dist/shared/opencodeSession.js +30 -0
  128. package/dist/shared/orchestration.d.ts +58 -0
  129. package/dist/shared/orchestration.js +12 -0
  130. package/dist/shared/orgs.d.ts +47 -0
  131. package/dist/shared/orgs.js +49 -0
  132. package/dist/shared/personalOrg.d.ts +5 -0
  133. package/dist/shared/personalOrg.js +12 -0
  134. package/dist/shared/plans.d.ts +3 -0
  135. package/dist/shared/plans.js +19 -0
  136. package/dist/shared/providerModels.d.ts +40 -0
  137. package/dist/shared/providerModels.js +503 -0
  138. package/dist/shared/roomAttention.d.ts +17 -0
  139. package/dist/shared/roomAttention.js +36 -0
  140. package/dist/shared/roomContext.d.ts +120 -0
  141. package/dist/shared/roomContext.js +133 -0
  142. package/dist/shared/roomPermissions.d.ts +56 -0
  143. package/dist/shared/roomPermissions.js +133 -0
  144. package/dist/shared/sessionSeats.d.ts +29 -0
  145. package/dist/shared/sessionSeats.js +57 -0
  146. package/dist/shared/slashCommands.d.ts +39 -0
  147. package/dist/shared/slashCommands.js +166 -0
  148. package/dist/shared/space.d.ts +33 -0
  149. package/dist/shared/space.js +54 -0
  150. package/dist/shared/spaceFeed.d.ts +78 -0
  151. package/dist/shared/spaceFeed.js +194 -0
  152. package/dist/shared/stringifyUnknown.d.ts +1 -0
  153. package/dist/shared/stringifyUnknown.js +1 -0
  154. package/dist/shared/swarm.d.ts +320 -0
  155. package/dist/shared/swarm.js +328 -0
  156. package/dist/shared/swarmVisual.d.ts +79 -0
  157. package/dist/shared/swarmVisual.js +212 -0
  158. package/dist/shared/terminalFilter.d.ts +13 -0
  159. package/dist/shared/terminalFilter.js +46 -0
  160. package/dist/shared/toolIntegration.d.ts +56 -0
  161. package/dist/shared/toolIntegration.js +180 -0
  162. package/dist/shared/typing.d.ts +9 -0
  163. package/dist/shared/typing.js +37 -0
  164. package/dist/shared/uploads.d.ts +31 -0
  165. package/dist/shared/uploads.js +65 -0
  166. package/dist/shared/voiceApprovals.d.ts +36 -0
  167. package/dist/shared/voiceApprovals.js +229 -0
  168. package/dist/shared/workspace.d.ts +49 -0
  169. package/dist/shared/workspace.js +53 -0
  170. package/dist/worker.d.ts +1 -0
  171. package/dist/worker.js +266 -0
  172. package/package.json +51 -4
package/README.md CHANGED
@@ -1,3 +1,155 @@
1
- # Temporary Holding Version
1
+ # @geteigen/cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ CLI worker for **Eigen** — runs local **Cursor**, **Claude Code**, **Codex**, and **OpenCode** agents on your machine for multiplayer sessions hosted in the web app.
4
+
5
+ When you create a **Local** session in Eigen, prompts are relayed to this worker, which executes `cursor agent`, `claude`, `codex`, or `opencode` against a folder on your machine.
6
+
7
+ **Protocol 10** is required for OpenCode, **Protocol 5** for Codex, and **Protocol 4** to forward chat images/files into the local workspace. Install/update:
8
+
9
+ ```bash
10
+ npm i -g @geteigen/cli@latest
11
+ ```
12
+
13
+ ## Requirements
14
+
15
+ - Node.js 18+
16
+ - [Cursor CLI](https://cursor.com) on your `PATH` (`cursor` command) for Cursor agents
17
+ - [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) (`claude`) for Claude Code agents
18
+ - [Codex CLI](https://github.com/openai/codex) (`codex`) for Codex agents
19
+ - [OpenCode](https://opencode.ai) (`opencode`) for OpenCode agents
20
+ - A Eigen account (sign in on the web app)
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ npm install -g @geteigen/cli
26
+ ```
27
+
28
+ Or from this repo:
29
+
30
+ ```bash
31
+ cd cli
32
+ pnpm install
33
+ pnpm build
34
+ npm link
35
+ ```
36
+
37
+ ## Quick start
38
+
39
+ 1. Sign in on the Eigen web app and open **Pair CLI** (`/cli-pair`).
40
+ 2. Generate a pairing code.
41
+ 3. On your machine:
42
+
43
+ ```bash
44
+ eigen login
45
+ # Server URL defaults to https://cursor-multiplayer-agent.onrender.com — press Enter
46
+ # Paste the pairing code
47
+
48
+ cursor agent login # if not already logged into Cursor CLI
49
+ # For Claude Code / OpenCode local agents, ensure `claude` / `opencode` work in your shell
50
+
51
+ eigen start
52
+ ```
53
+
54
+ 4. Keep `eigen start` running while you use Local sessions in the browser.
55
+
56
+ Credentials are stored at `~/.config/steer/config.json`.
57
+
58
+ ## Commands
59
+
60
+ | Command | Description |
61
+ |----------------|-------------|
62
+ | `eigen login` | Pair with your Eigen account via a one-time web code |
63
+ | `eigen logout` | Clear stored credentials |
64
+ | `eigen status` | Show logged-in user, server URL, and your rooms |
65
+ | `eigen start` | Start the worker (connects to the API over WebSocket) |
66
+ | `eigen start --repo <path>` | Same, but force every prompt to use this repo path |
67
+
68
+ ## Server URL
69
+
70
+ `eigen login` defaults to:
71
+
72
+ ```
73
+ https://cursor-multiplayer-agent.onrender.com
74
+ ```
75
+
76
+ Override per login at the prompt, or with:
77
+
78
+ ```bash
79
+ export STEER_SERVER_URL=http://localhost:3000
80
+ eigen login
81
+ ```
82
+
83
+ Use the local URL when developing against a local API.
84
+
85
+ ## Run agents on a server or VM
86
+
87
+ Headless machines connect with a **setup token** instead of a browser login.
88
+ In Eigen, open **Machines → Add machine**, choose personal or team (team
89
+ machines can only be added by workspace owners/admins), and copy one of the
90
+ commands it shows. Tokens are single-use by default and expire after 7 days.
91
+
92
+ **One-line script** (Linux/macOS — needs Node 20+ and git):
93
+
94
+ ```bash
95
+ curl -fsSL https://www.geteigen.space/install.sh | sh -s -- --token mt_…
96
+ # optional: --agents claude,codex,opencode,cursor --name build-vm --no-service
97
+ ```
98
+
99
+ **Manual:**
100
+
101
+ ```bash
102
+ npm install -g @geteigen/cli
103
+ eigen connect --token mt_… # trades the token for this machine's credential
104
+ eigen service install # systemd (Linux) / launchd (macOS); starts at boot
105
+ ```
106
+
107
+ **Docker** (Claude Code, Codex, OpenCode, and Cursor CLIs preinstalled):
108
+
109
+ ```bash
110
+ docker run -d --name eigen-worker --restart=always \
111
+ -e EIGEN_MACHINE_TOKEN=mt_… \
112
+ -v eigen-worker:/root/.config/steer -v eigen-work:/work \
113
+ ghcr.io/adityaraj-09/eigen-worker:latest
114
+ ```
115
+
116
+ Agents still need provider access on the machine: set `ANTHROPIC_API_KEY` /
117
+ `OPENAI_API_KEY` / `CURSOR_API_KEY` (or sign the CLIs in), or — on team
118
+ machines — keep the "use team keys" policy and Eigen sends the workspace's
119
+ keys with each run without storing them.
120
+
121
+ **Managing it**
122
+
123
+ - `eigen service status` / `eigen service logs` / `eigen service uninstall`
124
+ - `eigen start --daemon` runs in the background without a service
125
+ - **Work roots:** sessions clone repos under the work root (`--root <dir>`,
126
+ `EIGEN_WORK_ROOT`, `/work` in Docker). Admins can also limit agents to
127
+ allowed folders in Eigen → Machines; the worker refuses anything outside them.
128
+ - **Revoking:** remove the machine in Eigen → Machines. It disconnects at once
129
+ and its credential stops working; reconnecting needs a new setup token.
130
+
131
+ ## What the worker does
132
+
133
+ While `eigen start` is connected, the API can ask your machine to:
134
+
135
+ - List models for each installed agent (`cursor agent --list-models`, `opencode models`, …)
136
+ - Open a native folder picker for Local session create
137
+ - Run **Cursor**, **Claude Code**, **Codex**, or **OpenCode** prompts in the selected repo and stream events back to the room
138
+ - Stream tool results (including file paths and edit diffs) as each tool finishes so the web chat can group and inspect them
139
+ - **Protocol 4+**: write chat images/files into `.steer-uploads/` in the repo so local agents can Read them
140
+ - **Protocol 3+**: Claude Code backend + exclusive file locks before edit tools run (multi-agent conflict resolution)
141
+ - **Protocol 10+**: OpenCode backend (Anthropic / OpenAI provider keys or local CLI login)
142
+
143
+ ## Development
144
+
145
+ ```bash
146
+ pnpm install
147
+ pnpm dev # run via tsx without building
148
+ pnpm build # compile to dist/
149
+ ```
150
+
151
+ The published binary is `dist/index.js` (`bin`: `steer`).
152
+
153
+ ## License
154
+
155
+ MIT
@@ -0,0 +1,32 @@
1
+ export type AgentStreamEvent = {
2
+ kind: "session";
3
+ sessionId: string;
4
+ } | {
5
+ kind: "assistant_delta";
6
+ text: string;
7
+ } | {
8
+ kind: "assistant_final";
9
+ text: string;
10
+ } | {
11
+ kind: "tool_start";
12
+ callId: string;
13
+ name: string;
14
+ detail: string;
15
+ path?: string;
16
+ } | {
17
+ kind: "tool_done";
18
+ callId: string;
19
+ name: string;
20
+ detail: string;
21
+ path?: string;
22
+ } | {
23
+ kind: "error";
24
+ message: string;
25
+ } | {
26
+ kind: "done";
27
+ result: string;
28
+ };
29
+ export declare function isEditTool(name: string): boolean;
30
+ export declare function abortAgent(): void;
31
+ export declare function runAgent(repoPath: string, prompt: string, modelId: string, onEvent: (event: AgentStreamEvent) => void, sessionId?: string | null): Promise<void>;
32
+ export declare function getFileDiff(repoPath: string, filePath: string): Promise<string>;
package/dist/agent.js ADDED
@@ -0,0 +1,246 @@
1
+ import { spawn } from "child_process";
2
+ import { createInterface } from "readline";
3
+ import { existsSync } from "fs";
4
+ import { resolve, relative, isAbsolute } from "path";
5
+ import { execFile } from "child_process";
6
+ import { promisify } from "util";
7
+ const execFileAsync = promisify(execFile);
8
+ // ── Helpers (from server/agentRunner.ts) ─────────────────────────────
9
+ function extractText(message) {
10
+ if (!message || typeof message !== "object")
11
+ return "";
12
+ const content = message.content;
13
+ if (typeof content === "string")
14
+ return content;
15
+ if (!Array.isArray(content))
16
+ return "";
17
+ return content
18
+ .map((part) => {
19
+ if (part && typeof part === "object" && "text" in part) {
20
+ return String(part.text ?? "");
21
+ }
22
+ return "";
23
+ })
24
+ .join("");
25
+ }
26
+ function toolInfo(toolCall) {
27
+ if (!toolCall)
28
+ return { name: "tool", detail: "" };
29
+ const key = Object.keys(toolCall).find((k) => k.endsWith("ToolCall") ||
30
+ (!["hookAdditionalContexts", "toolCallId", "startedAtMs", "completedAtMs"].includes(k) &&
31
+ typeof toolCall[k] === "object"));
32
+ const name = key ? key.replace(/ToolCall$/, "") : "tool";
33
+ const body = key ? toolCall[key] : undefined;
34
+ const args = body?.args && typeof body.args === "object"
35
+ ? body.args
36
+ : undefined;
37
+ let detail = "";
38
+ let path;
39
+ if (args) {
40
+ for (const k of ["path", "filePath", "file_path", "target_file", "targetFile"]) {
41
+ if (typeof args[k] === "string" && String(args[k]).trim()) {
42
+ path = String(args[k]).trim();
43
+ break;
44
+ }
45
+ }
46
+ detail =
47
+ String(args.command ?? args.globPattern ?? path ?? args.targetDirectory ?? "") ||
48
+ JSON.stringify(args).slice(0, 120);
49
+ }
50
+ return { name, detail, path };
51
+ }
52
+ // ── Edit-tool detection (from server/gitDiff.ts) ─────────────────────
53
+ const EDIT_TOOL_RE = /^(write|edit|strreplace|searchreplace|delete|applypatch|editnotebook|create|updatefile|deletefile|writefile)/i;
54
+ export function isEditTool(name) {
55
+ return EDIT_TOOL_RE.test(name.replace(/ToolCall$/i, ""));
56
+ }
57
+ // ── Agent runner ─────────────────────────────────────────────────────
58
+ let activeChild = null;
59
+ export function abortAgent() {
60
+ activeChild?.kill("SIGTERM");
61
+ activeChild = null;
62
+ }
63
+ export function runAgent(repoPath, prompt, modelId, onEvent, sessionId) {
64
+ return new Promise((resolve, reject) => {
65
+ const args = [
66
+ "agent",
67
+ "--print",
68
+ "--output-format",
69
+ "stream-json",
70
+ "--stream-partial-output",
71
+ "--force",
72
+ "--trust",
73
+ ];
74
+ if (modelId && modelId !== "auto") {
75
+ args.push("--model", modelId);
76
+ }
77
+ if (sessionId) {
78
+ args.push("--resume", sessionId);
79
+ }
80
+ args.push(prompt);
81
+ const child = spawn("cursor", args, {
82
+ cwd: repoPath,
83
+ env: process.env,
84
+ stdio: ["ignore", "pipe", "pipe"],
85
+ });
86
+ activeChild = child;
87
+ let stderr = "";
88
+ let settled = false;
89
+ let assistantBuf = "";
90
+ let gotTerminalEvent = false;
91
+ const finish = (err) => {
92
+ if (settled)
93
+ return;
94
+ settled = true;
95
+ activeChild = null;
96
+ if (err)
97
+ reject(err);
98
+ else
99
+ resolve();
100
+ };
101
+ child.stderr.on("data", (chunk) => {
102
+ stderr += chunk.toString();
103
+ });
104
+ const rl = createInterface({ input: child.stdout });
105
+ rl.on("line", (line) => {
106
+ const trimmed = line.trim();
107
+ if (!trimmed)
108
+ return;
109
+ let ev;
110
+ try {
111
+ ev = JSON.parse(trimmed);
112
+ }
113
+ catch {
114
+ return;
115
+ }
116
+ const type = ev.type;
117
+ if (type === "system" && ev.subtype === "init" && ev.session_id) {
118
+ onEvent({ kind: "session", sessionId: String(ev.session_id) });
119
+ return;
120
+ }
121
+ if (type === "assistant") {
122
+ const text = extractText(ev.message);
123
+ if (!text)
124
+ return;
125
+ if ("timestamp_ms" in ev) {
126
+ assistantBuf += text;
127
+ onEvent({ kind: "assistant_delta", text: assistantBuf });
128
+ }
129
+ else {
130
+ assistantBuf = text;
131
+ onEvent({ kind: "assistant_final", text });
132
+ }
133
+ return;
134
+ }
135
+ if (type === "tool_call") {
136
+ const callId = String(ev.call_id ?? "");
137
+ const info = toolInfo(ev.tool_call);
138
+ if (ev.subtype === "started") {
139
+ onEvent({
140
+ kind: "tool_start",
141
+ callId,
142
+ name: info.name,
143
+ detail: info.detail,
144
+ path: info.path,
145
+ });
146
+ }
147
+ else if (ev.subtype === "completed") {
148
+ onEvent({
149
+ kind: "tool_done",
150
+ callId,
151
+ name: info.name,
152
+ detail: info.detail,
153
+ path: info.path,
154
+ });
155
+ }
156
+ return;
157
+ }
158
+ if (type === "result") {
159
+ gotTerminalEvent = true;
160
+ if (ev.session_id) {
161
+ onEvent({ kind: "session", sessionId: String(ev.session_id) });
162
+ }
163
+ if (ev.is_error) {
164
+ const msg = String(ev.result != null && ev.result !== "" ? ev.result : stderr || "Agent error");
165
+ onEvent({ kind: "error", message: msg });
166
+ }
167
+ else {
168
+ const result = String(ev.result ?? assistantBuf);
169
+ onEvent({ kind: "done", result });
170
+ }
171
+ }
172
+ });
173
+ child.on("error", (err) => {
174
+ if (!gotTerminalEvent) {
175
+ onEvent({ kind: "error", message: err.message });
176
+ }
177
+ finish(err);
178
+ });
179
+ child.on("close", (code) => {
180
+ if (settled)
181
+ return;
182
+ if (code === 0) {
183
+ if (!gotTerminalEvent && assistantBuf) {
184
+ onEvent({ kind: "done", result: assistantBuf });
185
+ }
186
+ finish();
187
+ }
188
+ else {
189
+ const msg = stderr.trim() || `Agent exited with code ${code ?? "unknown"}`;
190
+ if (!gotTerminalEvent) {
191
+ onEvent({ kind: "error", message: msg });
192
+ }
193
+ finish(new Error(msg));
194
+ }
195
+ });
196
+ });
197
+ }
198
+ // ── File diff (from server/gitDiff.ts) ───────────────────────────────
199
+ function resolveInRepo(repoPath, filePath) {
200
+ const abs = isAbsolute(filePath) ? filePath : resolve(repoPath, filePath);
201
+ const rel = relative(repoPath, abs);
202
+ if (rel.startsWith("..") || rel === "")
203
+ return null;
204
+ return rel.replace(/\\/g, "/");
205
+ }
206
+ async function runGit(repoPath, args) {
207
+ try {
208
+ const { stdout } = await execFileAsync("git", args, {
209
+ cwd: repoPath,
210
+ maxBuffer: 8 * 1024 * 1024,
211
+ encoding: "utf8",
212
+ });
213
+ return stdout;
214
+ }
215
+ catch (err) {
216
+ const e = err;
217
+ return e.stdout || "";
218
+ }
219
+ }
220
+ async function isTracked(repoPath, rel) {
221
+ try {
222
+ await execFileAsync("git", ["ls-files", "--error-unmatch", "--", rel], {
223
+ cwd: repoPath,
224
+ encoding: "utf8",
225
+ });
226
+ return true;
227
+ }
228
+ catch {
229
+ return false;
230
+ }
231
+ }
232
+ export async function getFileDiff(repoPath, filePath) {
233
+ const rel = resolveInRepo(repoPath, filePath);
234
+ if (!rel)
235
+ return "";
236
+ const abs = resolve(repoPath, rel);
237
+ let patch = await runGit(repoPath, ["diff", "HEAD", "--", rel]);
238
+ if (!patch)
239
+ patch = await runGit(repoPath, ["diff", "--", rel]);
240
+ if (!patch)
241
+ patch = await runGit(repoPath, ["diff", "--cached", "--", rel]);
242
+ if (!patch && existsSync(abs) && !(await isTracked(repoPath, rel))) {
243
+ patch = await runGit(repoPath, ["diff", "--no-index", "--", "/dev/null", rel]);
244
+ }
245
+ return patch.trim();
246
+ }
package/dist/auth.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { type Config } from "./config.js";
2
+ /** Pair CLI with a Clerk-authenticated web account via one-time code. */
3
+ export declare function loginWithPairingCode(serverUrl: string, code: string): Promise<Config>;
4
+ export declare function logout(): void;
5
+ export declare function getAuthHeaders(): Record<string, string>;
package/dist/auth.js ADDED
@@ -0,0 +1,33 @@
1
+ import { saveConfig, clearConfig, loadConfig } from "./config.js";
2
+ /** Pair CLI with a Clerk-authenticated web account via one-time code. */
3
+ export async function loginWithPairingCode(serverUrl, code) {
4
+ const base = serverUrl.replace(/\/+$/, "");
5
+ const normalized = code.trim().toUpperCase().replace(/\s+/g, "");
6
+ const res = await fetch(`${base}/api/auth/pairing/claim`, {
7
+ method: "POST",
8
+ headers: { "Content-Type": "application/json" },
9
+ body: JSON.stringify({ code: normalized }),
10
+ });
11
+ if (!res.ok) {
12
+ const err = (await res.json().catch(() => ({})));
13
+ throw new Error(err.error || `Pairing failed (${res.status})`);
14
+ }
15
+ const data = (await res.json());
16
+ const config = {
17
+ serverUrl: base,
18
+ token: data.token,
19
+ email: data.user.email,
20
+ };
21
+ saveConfig(config);
22
+ return config;
23
+ }
24
+ export function logout() {
25
+ clearConfig();
26
+ }
27
+ export function getAuthHeaders() {
28
+ const config = loadConfig();
29
+ if (!config) {
30
+ throw new Error("Not logged in. Run `steer login` first.");
31
+ }
32
+ return { Authorization: `Bearer ${config.token}` };
33
+ }
@@ -0,0 +1,24 @@
1
+ import { isEditTool, type AgentBackendKind, type NormalizedAgentEvent } from "../../shared/backends/index.js";
2
+ import type { BuildArgsOptions } from "../../shared/backends/types.js";
3
+ export type AgentStreamEvent = NormalizedAgentEvent;
4
+ export { isEditTool };
5
+ export declare function abortRun(runKey: string): void;
6
+ export declare function abortAll(): void;
7
+ /** @deprecated Use abortRun(runKey) or abortAll(). */
8
+ export declare function abortAgent(runKey?: string): void;
9
+ export interface RunHandle {
10
+ runKey: string;
11
+ abort: () => void;
12
+ promise: Promise<void>;
13
+ }
14
+ export declare function runAgent(repoPath: string, prompt: string, modelId: string, onEvent: (event: AgentStreamEvent) => void, sessionId?: string | null, runKey?: string, backendKind?: AgentBackendKind, mode?: "agent" | "plan"): Promise<void>;
15
+ export declare function runAgentWithHandle(repoPath: string, prompt: string, modelId: string, onEvent: (event: AgentStreamEvent) => void, sessionId?: string | null, runKey?: string, backendKind?: AgentBackendKind, mode?: "agent" | "plan",
16
+ /** Claude Code / Codex / OpenCode: MCP servers attached for this run (Cursor reads its own mcp.json). */
17
+ mcpServers?: BuildArgsOptions["mcpServers"],
18
+ /** Extra env for this run only (team machines: workspace provider keys). Never logged or stored. */
19
+ runEnv?: Record<string, string>): RunHandle;
20
+ export declare function getFileDiff(repoPath: string, filePath: string): Promise<string>;
21
+ export declare function revertFiles(repoPath: string, filePaths: string[]): Promise<{
22
+ reverted: string[];
23
+ errors: string[];
24
+ }>;