@geteigen/cli 0.0.0-stage → 3.0.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 +154 -2
- package/dist/agent.d.ts +32 -0
- package/dist/agent.js +246 -0
- package/dist/auth.d.ts +5 -0
- package/dist/auth.js +33 -0
- package/dist/cli/src/agent.d.ts +24 -0
- package/dist/cli/src/agent.js +260 -0
- package/dist/cli/src/agentTools.d.ts +10 -0
- package/dist/cli/src/agentTools.js +70 -0
- package/dist/cli/src/auth.d.ts +5 -0
- package/dist/cli/src/auth.js +33 -0
- package/dist/cli/src/config.d.ts +11 -0
- package/dist/cli/src/config.js +51 -0
- package/dist/cli/src/cursorAuth.d.ts +10 -0
- package/dist/cli/src/cursorAuth.js +92 -0
- package/dist/cli/src/index.d.ts +2 -0
- package/dist/cli/src/index.js +244 -0
- package/dist/cli/src/listModels.d.ts +14 -0
- package/dist/cli/src/listModels.js +196 -0
- package/dist/cli/src/listSessions.d.ts +9 -0
- package/dist/cli/src/listSessions.js +37 -0
- package/dist/cli/src/machineConfig.d.ts +20 -0
- package/dist/cli/src/machineConfig.js +66 -0
- package/dist/cli/src/pickFolder.d.ts +5 -0
- package/dist/cli/src/pickFolder.js +65 -0
- package/dist/cli/src/proc.d.ts +23 -0
- package/dist/cli/src/proc.js +77 -0
- package/dist/cli/src/service.d.ts +17 -0
- package/dist/cli/src/service.js +246 -0
- package/dist/cli/src/workRoots.d.ts +30 -0
- package/dist/cli/src/workRoots.js +221 -0
- package/dist/cli/src/worker.d.ts +8 -0
- package/dist/cli/src/worker.js +51 -0
- package/dist/cli/src/workerCore.d.ts +47 -0
- package/dist/cli/src/workerCore.js +702 -0
- package/dist/cli/src/workspace.d.ts +8 -0
- package/dist/cli/src/workspace.js +269 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +51 -0
- package/dist/cursorAuth.d.ts +10 -0
- package/dist/cursorAuth.js +95 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +108 -0
- package/dist/listModels.d.ts +6 -0
- package/dist/listModels.js +62 -0
- package/dist/listSessions.d.ts +9 -0
- package/dist/listSessions.js +37 -0
- package/dist/pickFolder.d.ts +5 -0
- package/dist/pickFolder.js +65 -0
- package/dist/shared/actionDrafts.d.ts +83 -0
- package/dist/shared/actionDrafts.js +169 -0
- package/dist/shared/agentArtifacts.d.ts +49 -0
- package/dist/shared/agentArtifacts.js +169 -0
- package/dist/shared/agentLabel.d.ts +18 -0
- package/dist/shared/agentLabel.js +22 -0
- package/dist/shared/agentMentions.d.ts +38 -0
- package/dist/shared/agentMentions.js +110 -0
- package/dist/shared/agentRepos.d.ts +28 -0
- package/dist/shared/agentRepos.js +60 -0
- package/dist/shared/agentRunStatus.d.ts +12 -0
- package/dist/shared/agentRunStatus.js +17 -0
- package/dist/shared/agentUsage.d.ts +24 -0
- package/dist/shared/agentUsage.js +139 -0
- package/dist/shared/approvals.d.ts +44 -0
- package/dist/shared/approvals.js +97 -0
- package/dist/shared/askEigen.d.ts +190 -0
- package/dist/shared/askEigen.js +76 -0
- package/dist/shared/attribution.d.ts +25 -0
- package/dist/shared/attribution.js +63 -0
- package/dist/shared/automations.d.ts +136 -0
- package/dist/shared/automations.js +546 -0
- package/dist/shared/backends/claudeCode.d.ts +42 -0
- package/dist/shared/backends/claudeCode.js +540 -0
- package/dist/shared/backends/codex.d.ts +15 -0
- package/dist/shared/backends/codex.js +287 -0
- package/dist/shared/backends/cursor.d.ts +102 -0
- package/dist/shared/backends/cursor.js +1197 -0
- package/dist/shared/backends/index.d.ts +18 -0
- package/dist/shared/backends/index.js +58 -0
- package/dist/shared/backends/opencode.d.ts +40 -0
- package/dist/shared/backends/opencode.js +397 -0
- package/dist/shared/backends/types.d.ts +171 -0
- package/dist/shared/backends/types.js +19 -0
- package/dist/shared/backends/usage.d.ts +8 -0
- package/dist/shared/backends/usage.js +71 -0
- package/dist/shared/billingPlans.d.ts +60 -0
- package/dist/shared/billingPlans.js +262 -0
- package/dist/shared/brain.d.ts +353 -0
- package/dist/shared/brain.js +487 -0
- package/dist/shared/clarifyingQuestions.d.ts +13 -0
- package/dist/shared/clarifyingQuestions.js +207 -0
- package/dist/shared/claudeModels.d.ts +10 -0
- package/dist/shared/claudeModels.js +52 -0
- package/dist/shared/cloudBackends.d.ts +21 -0
- package/dist/shared/cloudBackends.js +29 -0
- package/dist/shared/codexModels.d.ts +10 -0
- package/dist/shared/codexModels.js +28 -0
- package/dist/shared/contextWindows.d.ts +2 -0
- package/dist/shared/contextWindows.js +30 -0
- package/dist/shared/cursorMcpConfig.d.ts +14 -0
- package/dist/shared/cursorMcpConfig.js +43 -0
- package/dist/shared/cursorProjectMcp.d.ts +10 -0
- package/dist/shared/cursorProjectMcp.js +42 -0
- package/dist/shared/docs.d.ts +59 -0
- package/dist/shared/docs.js +87 -0
- package/dist/shared/events.d.ts +929 -0
- package/dist/shared/events.js +59 -0
- package/dist/shared/integrations.d.ts +266 -0
- package/dist/shared/integrations.js +449 -0
- package/dist/shared/issueTranscript.d.ts +64 -0
- package/dist/shared/issueTranscript.js +387 -0
- package/dist/shared/issues.d.ts +139 -0
- package/dist/shared/issues.js +217 -0
- package/dist/shared/machines.d.ts +115 -0
- package/dist/shared/machines.js +58 -0
- package/dist/shared/markdownMedia.d.ts +13 -0
- package/dist/shared/markdownMedia.js +41 -0
- package/dist/shared/notificationText.d.ts +6 -0
- package/dist/shared/notificationText.js +38 -0
- package/dist/shared/opencodeModels.d.ts +19 -0
- package/dist/shared/opencodeModels.js +62 -0
- package/dist/shared/opencodeSession.d.ts +7 -0
- package/dist/shared/opencodeSession.js +30 -0
- package/dist/shared/orchestration.d.ts +58 -0
- package/dist/shared/orchestration.js +12 -0
- package/dist/shared/orgs.d.ts +47 -0
- package/dist/shared/orgs.js +49 -0
- package/dist/shared/personalOrg.d.ts +5 -0
- package/dist/shared/personalOrg.js +12 -0
- package/dist/shared/plans.d.ts +3 -0
- package/dist/shared/plans.js +19 -0
- package/dist/shared/providerModels.d.ts +40 -0
- package/dist/shared/providerModels.js +503 -0
- package/dist/shared/roomAttention.d.ts +17 -0
- package/dist/shared/roomAttention.js +36 -0
- package/dist/shared/roomContext.d.ts +120 -0
- package/dist/shared/roomContext.js +133 -0
- package/dist/shared/roomPermissions.d.ts +56 -0
- package/dist/shared/roomPermissions.js +133 -0
- package/dist/shared/sessionSeats.d.ts +29 -0
- package/dist/shared/sessionSeats.js +57 -0
- package/dist/shared/slashCommands.d.ts +39 -0
- package/dist/shared/slashCommands.js +166 -0
- package/dist/shared/space.d.ts +33 -0
- package/dist/shared/space.js +54 -0
- package/dist/shared/spaceFeed.d.ts +78 -0
- package/dist/shared/spaceFeed.js +194 -0
- package/dist/shared/stringifyUnknown.d.ts +1 -0
- package/dist/shared/stringifyUnknown.js +1 -0
- package/dist/shared/swarm.d.ts +320 -0
- package/dist/shared/swarm.js +328 -0
- package/dist/shared/swarmVisual.d.ts +79 -0
- package/dist/shared/swarmVisual.js +212 -0
- package/dist/shared/terminalFilter.d.ts +13 -0
- package/dist/shared/terminalFilter.js +46 -0
- package/dist/shared/toolIntegration.d.ts +56 -0
- package/dist/shared/toolIntegration.js +180 -0
- package/dist/shared/typing.d.ts +9 -0
- package/dist/shared/typing.js +37 -0
- package/dist/shared/uploads.d.ts +31 -0
- package/dist/shared/uploads.js +65 -0
- package/dist/shared/voiceApprovals.d.ts +36 -0
- package/dist/shared/voiceApprovals.js +229 -0
- package/dist/shared/workspace.d.ts +47 -0
- package/dist/shared/workspace.js +40 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +266 -0
- package/package.json +51 -4
package/README.md
CHANGED
|
@@ -1,3 +1,155 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @geteigen/cli
|
|
2
2
|
|
|
3
|
-
|
|
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
|
package/dist/agent.d.ts
ADDED
|
@@ -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
|
+
}>;
|