@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
@@ -0,0 +1,217 @@
1
+ export const ISSUE_STATUSES = [
2
+ "draft",
3
+ "queued",
4
+ "running",
5
+ "needs_input",
6
+ "in_review",
7
+ "done",
8
+ "failed",
9
+ "cancelled",
10
+ ];
11
+ export const ISSUE_PRIORITIES = ["low", "medium", "high", "urgent"];
12
+ export const DEFAULT_PICKUP_DELAY_MS = 10 * 60 * 1000;
13
+ export const MIN_PICKUP_DELAY_MS = 0;
14
+ export const MAX_PICKUP_DELAY_MS = 24 * 60 * 60 * 1000;
15
+ export const DEFAULT_MAX_CONCURRENT_ISSUES = 2;
16
+ export const MAX_CONCURRENT_ISSUES_CAP = 8;
17
+ export const LINEAR_PROVIDER = "linear";
18
+ export const DEFAULT_ALLOW_AGENT_SPAWN = false;
19
+ export const DEFAULT_MAX_SPAWN_PER_BATCH = 10;
20
+ export const MAX_SPAWN_PER_BATCH_CAP = 20;
21
+ export const MAX_LINEAR_IMPORT_ISSUES = 20;
22
+ export const ISSUE_LEASE_MS = 12 * 60 * 1000;
23
+ export const ISSUE_POLL_MS = 15_000;
24
+ export const ISSUE_HEARTBEAT_MS = 30_000;
25
+ export const MAX_ISSUE_TITLE = 200;
26
+ export const MAX_ISSUE_DESCRIPTION = 20_000;
27
+ export const MAX_WRITEUP_CHARS = 80_000;
28
+ export const MAX_ISSUE_ATTACHMENTS = 8;
29
+ export const MAX_ISSUE_ATTACHMENT_BYTES = 8 * 1024 * 1024;
30
+ export const MAX_ISSUE_ATTEMPTS = 3;
31
+ export function isIssueStatus(value) {
32
+ return (typeof value === "string" &&
33
+ ISSUE_STATUSES.includes(value));
34
+ }
35
+ export function isIssuePriority(value) {
36
+ return (typeof value === "string" &&
37
+ ISSUE_PRIORITIES.includes(value));
38
+ }
39
+ export function issueSettingsScopeKey(input) {
40
+ const orgId = input.orgId?.trim();
41
+ if (orgId && orgId !== "personal")
42
+ return `org:${orgId}`;
43
+ return `user:${input.userId}`;
44
+ }
45
+ export function clampPickupDelayMs(raw, fallback = DEFAULT_PICKUP_DELAY_MS) {
46
+ const n = typeof raw === "number" ? raw : Number(raw);
47
+ if (!Number.isFinite(n))
48
+ return fallback;
49
+ return Math.min(MAX_PICKUP_DELAY_MS, Math.max(MIN_PICKUP_DELAY_MS, Math.round(n)));
50
+ }
51
+ export function clampMaxConcurrent(raw, ceiling = MAX_CONCURRENT_ISSUES_CAP) {
52
+ const n = typeof raw === "number" ? raw : Number(raw);
53
+ const cap = Number.isFinite(ceiling) && ceiling > 0 ? ceiling : MAX_CONCURRENT_ISSUES_CAP;
54
+ if (!Number.isFinite(n))
55
+ return Math.min(cap, DEFAULT_MAX_CONCURRENT_ISSUES);
56
+ return Math.min(cap, Math.max(1, Math.round(n)));
57
+ }
58
+ export function clampMaxSpawnPerBatch(raw) {
59
+ const n = typeof raw === "number" ? raw : Number(raw);
60
+ if (!Number.isFinite(n))
61
+ return DEFAULT_MAX_SPAWN_PER_BATCH;
62
+ return Math.min(MAX_SPAWN_PER_BATCH_CAP, Math.max(1, Math.round(n)));
63
+ }
64
+ /** `PER-9` / Linear URL tail → uppercase identifier, or null. */
65
+ export function parseLinearIdentifier(raw) {
66
+ if (typeof raw !== "string")
67
+ return null;
68
+ const m = raw.trim().match(/\b([A-Z][A-Z0-9]*-\d+)\b/i);
69
+ return m ? m[1].toUpperCase() : null;
70
+ }
71
+ export function mapLinearPriority(raw) {
72
+ if (typeof raw === "number" && Number.isFinite(raw)) {
73
+ if (raw === 1)
74
+ return "urgent";
75
+ if (raw === 2)
76
+ return "high";
77
+ if (raw === 4)
78
+ return "low";
79
+ return "medium";
80
+ }
81
+ if (raw && typeof raw === "object" && "name" in raw) {
82
+ return mapLinearPriority(raw.name);
83
+ }
84
+ if (typeof raw === "string") {
85
+ const s = raw.trim().toLowerCase();
86
+ if (s === "urgent" || s === "1")
87
+ return "urgent";
88
+ if (s === "high" || s === "2")
89
+ return "high";
90
+ if (s === "low" || s === "4")
91
+ return "low";
92
+ }
93
+ return "medium";
94
+ }
95
+ export function parseGithubHttpsRepo(raw) {
96
+ const trimmed = raw.trim().replace(/\.git$/i, "");
97
+ const m = trimmed.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)\/?$/i);
98
+ if (!m)
99
+ return null;
100
+ return `https://github.com/${m[1]}/${m[2]}`;
101
+ }
102
+ export function issueStatusLabel(status) {
103
+ switch (status) {
104
+ case "draft":
105
+ return "Draft";
106
+ case "queued":
107
+ return "Queued";
108
+ case "running":
109
+ return "Running";
110
+ case "needs_input":
111
+ return "Needs input";
112
+ case "in_review":
113
+ return "In review";
114
+ case "done":
115
+ return "Done";
116
+ case "failed":
117
+ return "Failed";
118
+ case "cancelled":
119
+ return "Cancelled";
120
+ }
121
+ }
122
+ export function canEditIssue(issue, actor) {
123
+ if (issue.creatorId === actor.userId)
124
+ return true;
125
+ return actor.orgRole === "owner" || actor.orgRole === "admin";
126
+ }
127
+ export function canQueueIssue(status) {
128
+ return status === "draft" || status === "failed" || status === "cancelled";
129
+ }
130
+ export function canCancelIssue(status) {
131
+ return status === "draft" || status === "queued" || status === "running";
132
+ }
133
+ export function canRetryIssue(status) {
134
+ return status === "failed" || status === "cancelled" || status === "needs_input";
135
+ }
136
+ /** True while the issue page should refresh itself (pickup / agent still moving). */
137
+ export function issueStatusIsActive(status) {
138
+ return status === "queued" || status === "running" || status === "needs_input";
139
+ }
140
+ /** Unwrap a full-document markdown fence the model sometimes wraps around writeups. */
141
+ export function sanitizeIssueWriteup(raw) {
142
+ let text = raw.replace(/\u0000/g, "").trim();
143
+ if (!text)
144
+ return "";
145
+ const fenced = text.match(/^```(?:markdown|md)?\s*\r?\n([\s\S]*?)\r?\n```\s*$/i)
146
+ || text.match(/^```(?:markdown|md)?\s*([\s\S]*?)```\s*$/i);
147
+ if (fenced?.[1])
148
+ text = fenced[1].trim();
149
+ if (text.length > MAX_WRITEUP_CHARS) {
150
+ text = text.slice(0, MAX_WRITEUP_CHARS).trimEnd();
151
+ }
152
+ return text;
153
+ }
154
+ export function looksLikeIssueWriteup(text) {
155
+ const trimmed = text.trim();
156
+ if (trimmed.length >= 80)
157
+ return true;
158
+ return /^#{1,3}\s+\S+/m.test(trimmed);
159
+ }
160
+ export function buildFallbackIssueWriteup(input) {
161
+ const desc = input.description?.trim();
162
+ const pr = input.prUrl?.trim();
163
+ return [
164
+ "## Summary",
165
+ "",
166
+ `The agent finished working on **${input.title}** but did not return a markdown writeup.`,
167
+ "",
168
+ desc ? `### Issue\n\n${desc}` : null,
169
+ pr
170
+ ? `### Pull request\n\n${pr}`
171
+ : "### Pull request\n\nNo pull request URL was captured.",
172
+ ]
173
+ .filter((part) => part !== null)
174
+ .join("\n");
175
+ }
176
+ export function buildIssueFixPrompt(input) {
177
+ const desc = input.description.trim() || "(no additional description)";
178
+ const attachments = input.attachmentNotes?.trim()
179
+ ? `\n\n${input.attachmentNotes.trim()}`
180
+ : "";
181
+ return [
182
+ `You are fixing Eigen issue ${input.issueId} in ${input.repoUrl} (base branch: ${input.startingRef}).`,
183
+ "",
184
+ `Title: ${input.title}`,
185
+ "",
186
+ "Description:",
187
+ desc,
188
+ attachments,
189
+ "",
190
+ "Rules:",
191
+ "- Implement the fix on a feature branch and open a pull request to the base branch.",
192
+ "- Pull request title/body may be short. Do not put a long writeup in the PR or in any repository file.",
193
+ "- Do not create or edit markdown/docs files for this issue (no ISSUE.md, WRITEUP.md, architecture notes, etc.).",
194
+ "- Do not ask clarifying questions. If something is ambiguous, pick the safest reasonable default and continue.",
195
+ "- Do not wait for a human. Finish the change and the pull request.",
196
+ ...(input.artifactRules?.trim() ? ["", input.artifactRules.trim()] : []),
197
+ ].join("\n");
198
+ }
199
+ export function buildIssueWriteupPrompt(input) {
200
+ const pr = input.prUrl?.trim()
201
+ ? `A pull request is at ${input.prUrl.trim()}.`
202
+ : "If a pull request was opened, mention it in one line; do not invent a URL.";
203
+ return [
204
+ `The code change for Eigen issue ${input.issueId} ("${input.title}") is done. ${pr}`,
205
+ "",
206
+ "Write a markdown note for the issue tracker only. Your entire reply must be that note.",
207
+ "Do not create or edit any files. Do not run more implementation work. Do not use tools.",
208
+ "Do not say you will write it later. Start with a ## heading.",
209
+ "",
210
+ "Cover:",
211
+ "- What the issue was",
212
+ "- The cause",
213
+ "- What you changed",
214
+ "",
215
+ "Markdown only. No JSON. No tool calls.",
216
+ ].join("\n");
217
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Machines: computers and VMs that run local agents (Cursor, Claude Code,
3
+ * Codex, OpenCode). Personal machines belong to one person (the desktop app and
4
+ * `eigen start` register them automatically; headless VMs connect with a
5
+ * setup token). Team machines belong to a workspace: owners/admins add them
6
+ * and choose who may run agents on them. Sessions pin agents to one machine.
7
+ */
8
+ export type MachineAgentId = "cursor" | "claude" | "codex" | "opencode";
9
+ export interface MachineAgentTool {
10
+ id: MachineAgentId;
11
+ label: string;
12
+ installed: boolean;
13
+ version?: string;
14
+ /** Where it was found (home shortened to ~). */
15
+ path?: string;
16
+ }
17
+ export type MachineOs = "darwin" | "linux" | "win32" | "other";
18
+ /** What connected it: the desktop app or the CLI worker. */
19
+ export type MachineClient = "desktop" | "cli";
20
+ export type MachineKind = "personal" | "team";
21
+ /** Team machines: who may run agents on it. Admins always can. */
22
+ export type MachineVisibility = "everyone" | "admins" | "selected";
23
+ /**
24
+ * Where agents on the machine get provider keys: the machine's own env / CLI
25
+ * sign-in, or the team's keys sent with each run (never written to disk).
26
+ */
27
+ export type MachineKeyPolicy = "machine" | "team_keys";
28
+ export interface MachineInfo {
29
+ id: string;
30
+ kind: MachineKind;
31
+ /** Team machines: the workspace that owns it. */
32
+ orgId: string | null;
33
+ /** Personal machines: whose it is. */
34
+ ownerUserId: string | null;
35
+ ownerName?: string | null;
36
+ /** Connected with a setup token (servers / VMs) rather than a signed-in app. */
37
+ headless: boolean;
38
+ visibility: MachineVisibility;
39
+ /** Selected-people visibility: who (only sent to people who can manage it). */
40
+ memberIds: string[];
41
+ keyPolicy: MachineKeyPolicy;
42
+ /** Folders agents may work in (headless/team machines). Empty = no limit. */
43
+ allowedRoots: string[];
44
+ /** The viewer can rename, change access, and revoke it. */
45
+ canManage: boolean;
46
+ name: string;
47
+ hostname: string;
48
+ os: MachineOs;
49
+ arch: string;
50
+ client: MachineClient;
51
+ online: boolean;
52
+ /** Agents running on it right now. */
53
+ activeRuns: number;
54
+ maxConcurrent: number;
55
+ agents: MachineAgentTool[];
56
+ lastSeenAt: number;
57
+ createdAt: number;
58
+ }
59
+ /** Room agents map backends onto the CLI each one needs on the machine. */
60
+ export declare function machineToolForBackend(backend: string | null | undefined): MachineAgentId;
61
+ export declare function machineHasBackend(machine: Pick<MachineInfo, "agents">, backend: string | null | undefined): boolean;
62
+ export declare function parseMachineOs(value: unknown): MachineOs;
63
+ export declare function machineOsLabel(os: MachineOs): string;
64
+ /** A one-time setup token for connecting a headless machine (shown once). */
65
+ export interface MachineSetupTokenInfo {
66
+ id: string;
67
+ name: string;
68
+ kind: MachineKind;
69
+ orgId: string | null;
70
+ visibility: MachineVisibility;
71
+ memberIds: string[];
72
+ keyPolicy: MachineKeyPolicy;
73
+ maxUses: number;
74
+ uses: number;
75
+ expiresAt: number;
76
+ createdAt: number;
77
+ createdBy: string;
78
+ }
79
+ export interface MachineSetupStep {
80
+ title: string;
81
+ command: string;
82
+ }
83
+ export interface MachineSetupCommands {
84
+ /** curl … | sh one-liner (installs agent CLIs + Eigen, connects, service). */
85
+ script: string;
86
+ /** Manual steps: PATH, agent CLIs, Eigen CLI, connect, service. */
87
+ manual: MachineSetupStep[];
88
+ /** docker run … */
89
+ docker: string;
90
+ }
91
+ export interface CreatedMachineToken {
92
+ token: string;
93
+ info: MachineSetupTokenInfo;
94
+ commands: MachineSetupCommands;
95
+ }
96
+ /** A folder on a machine, for the remote folder browser. */
97
+ export interface MachineDirEntry {
98
+ name: string;
99
+ path: string;
100
+ isGitRepo: boolean;
101
+ }
102
+ export interface MachineDirListing {
103
+ path: string;
104
+ parent: string | null;
105
+ entries: MachineDirEntry[];
106
+ /** Allowed roots (empty = whole home directory). */
107
+ roots: string[];
108
+ }
109
+ export declare function machineVisibilityLabel(v: MachineVisibility, memberCount?: number): string;
110
+ export declare function parseMachineVisibility(value: unknown): MachineVisibility;
111
+ export declare function parseMachineKeyPolicy(value: unknown, kind: MachineKind): MachineKeyPolicy;
112
+ /** Worker protocol that understands machine credentials, folder browsing, repo prep, and per-run env. */
113
+ export declare const MACHINE_PROTOCOL = 9;
114
+ /** First worker protocol that can run OpenCode agents and list their models. */
115
+ export declare const OPENCODE_WORKER_PROTOCOL = 10;
116
+ /** First worker protocol that can read a local workspace's git state and files. */
117
+ export declare const WORKSPACE_WORKER_PROTOCOL = 11;
118
+ /** First worker protocol that can commit, push, and switch branches from the git panel. */
119
+ export declare const GIT_PANEL_WORKER_PROTOCOL = 12;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Machines: computers and VMs that run local agents (Cursor, Claude Code,
3
+ * Codex, OpenCode). Personal machines belong to one person (the desktop app and
4
+ * `eigen start` register them automatically; headless VMs connect with a
5
+ * setup token). Team machines belong to a workspace: owners/admins add them
6
+ * and choose who may run agents on them. Sessions pin agents to one machine.
7
+ */
8
+ /** Room agents map backends onto the CLI each one needs on the machine. */
9
+ export function machineToolForBackend(backend) {
10
+ if (backend === "claude-code")
11
+ return "claude";
12
+ if (backend === "codex")
13
+ return "codex";
14
+ if (backend === "opencode")
15
+ return "opencode";
16
+ return "cursor";
17
+ }
18
+ export function machineHasBackend(machine, backend) {
19
+ const tool = machineToolForBackend(backend);
20
+ const found = machine.agents.find((a) => a.id === tool);
21
+ // Older workers don't report tools — don't block them.
22
+ return machine.agents.length === 0 || Boolean(found?.installed);
23
+ }
24
+ export function parseMachineOs(value) {
25
+ return value === "darwin" || value === "linux" || value === "win32" ? value : "other";
26
+ }
27
+ export function machineOsLabel(os) {
28
+ if (os === "darwin")
29
+ return "macOS";
30
+ if (os === "linux")
31
+ return "Linux";
32
+ if (os === "win32")
33
+ return "Windows";
34
+ return "Unknown OS";
35
+ }
36
+ export function machineVisibilityLabel(v, memberCount = 0) {
37
+ if (v === "admins")
38
+ return "Admins only";
39
+ if (v === "selected")
40
+ return memberCount === 1 ? "1 person" : `${memberCount} people`;
41
+ return "Everyone in the workspace";
42
+ }
43
+ export function parseMachineVisibility(value) {
44
+ return value === "admins" || value === "selected" ? value : "everyone";
45
+ }
46
+ export function parseMachineKeyPolicy(value, kind) {
47
+ if (value === "machine" || value === "team_keys")
48
+ return value;
49
+ return kind === "team" ? "team_keys" : "machine";
50
+ }
51
+ /** Worker protocol that understands machine credentials, folder browsing, repo prep, and per-run env. */
52
+ export const MACHINE_PROTOCOL = 9;
53
+ /** First worker protocol that can run OpenCode agents and list their models. */
54
+ export const OPENCODE_WORKER_PROTOCOL = 10;
55
+ /** First worker protocol that can read a local workspace's git state and files. */
56
+ export const WORKSPACE_WORKER_PROTOCOL = 11;
57
+ /** First worker protocol that can commit, push, and switch branches from the git panel. */
58
+ export const GIT_PANEL_WORKER_PROTOCOL = 12;
@@ -0,0 +1,13 @@
1
+ export type MediaPart = {
2
+ kind: "md";
3
+ text: string;
4
+ } | {
5
+ kind: "img";
6
+ src: string;
7
+ alt: string;
8
+ } | {
9
+ kind: "video";
10
+ src: string;
11
+ };
12
+ /** Pull raw HTML <img>/<video> tags out so react-markdown can still render prose. */
13
+ export declare function splitMediaHtml(content: string): MediaPart[];
@@ -0,0 +1,41 @@
1
+ /** Pull raw HTML <img>/<video> tags out so react-markdown can still render prose. */
2
+ export function splitMediaHtml(content) {
3
+ if (!content)
4
+ return [];
5
+ const parts = [];
6
+ const re = /<video\b([^>]*)>(?:[\s\S]*?<\/video>)?|<img\b([^>]*)\/?>/gi;
7
+ let last = 0;
8
+ let m;
9
+ while ((m = re.exec(content)) !== null) {
10
+ if (m.index > last) {
11
+ parts.push({ kind: "md", text: content.slice(last, m.index) });
12
+ }
13
+ const attrs = m[1] ?? m[2] ?? "";
14
+ const src = readAttr(attrs, "src");
15
+ if (src) {
16
+ if (m[0].toLowerCase().startsWith("<video")) {
17
+ parts.push({ kind: "video", src });
18
+ }
19
+ else {
20
+ parts.push({
21
+ kind: "img",
22
+ src,
23
+ alt: readAttr(attrs, "alt") || "",
24
+ });
25
+ }
26
+ }
27
+ else {
28
+ parts.push({ kind: "md", text: m[0] });
29
+ }
30
+ last = m.index + m[0].length;
31
+ }
32
+ if (last < content.length) {
33
+ parts.push({ kind: "md", text: content.slice(last) });
34
+ }
35
+ return parts.length ? parts : [{ kind: "md", text: content }];
36
+ }
37
+ function readAttr(attrs, name) {
38
+ const re = new RegExp(`${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i");
39
+ const m = attrs.match(re);
40
+ return (m?.[1] ?? m?.[2] ?? m?.[3] ?? "").trim();
41
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * One-line plain-text preview of an agent's markdown reply, for notification
3
+ * bodies. Notifications render as plain text and are truncated, so raw
4
+ * markdown shows up as `##`, `**` and table pipes.
5
+ */
6
+ export declare function notificationPreview(text: string | null | undefined, max?: number): string;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * One-line plain-text preview of an agent's markdown reply, for notification
3
+ * bodies. Notifications render as plain text and are truncated, so raw
4
+ * markdown shows up as `##`, `**` and table pipes.
5
+ */
6
+ export function notificationPreview(text, max = 200) {
7
+ if (!text)
8
+ return "";
9
+ const lines = text
10
+ .replace(/```[\s\S]*?```/g, " ")
11
+ .split(/\r?\n/)
12
+ .map((line) => line.trim())
13
+ // Tables read as noise in one line; keep the prose around them.
14
+ .filter((line) => line && !line.startsWith("|") && !/^[-*_]{3,}$/.test(line))
15
+ .map((line) => line
16
+ .replace(/^#{1,6}\s+/, "")
17
+ .replace(/^>\s?/, "")
18
+ .replace(/^(?:[-*+]|\d+[.)])\s+/, "")
19
+ .replace(/^\[[ xX]\]\s+/, ""));
20
+ const plain = lines
21
+ .join(" ")
22
+ .replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1")
23
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
24
+ .replace(/<[^>]+>/g, "")
25
+ .replace(/(\*\*|__)(.+?)\1/g, "$2")
26
+ .replace(/(^|[\s(])\*(\S(?:.*?\S)?)\*(?=[\s).,:;!?]|$)/g, "$1$2")
27
+ .replace(/~~(.+?)~~/g, "$1")
28
+ .replace(/`([^`]*)`/g, "$1")
29
+ // Leftovers from markdown cut off mid-span by an earlier truncation.
30
+ .replace(/\*\*|`/g, "")
31
+ // Already-flattened text (older notifications): a table became "| a | b |…".
32
+ .replace(/(?:^|\s)\|[^|]*\|.*$/, "")
33
+ .replace(/\s+/g, " ")
34
+ .trim();
35
+ if (plain.length <= max)
36
+ return plain;
37
+ return `${plain.slice(0, max - 1).trimEnd()}…`;
38
+ }
@@ -0,0 +1,19 @@
1
+ import type { ModelInfo } from "./events.js";
2
+ /**
3
+ * OpenCode model ids are `provider/model` pairs. This is the offline fallback;
4
+ * local workers replace it with `opencode models` and the server can add live
5
+ * Anthropic / OpenAI catalog entries for the same providers.
6
+ */
7
+ export declare const OPENCODE_MODELS: ModelInfo[];
8
+ /** Cloud sandboxes need a concrete provider/model instead of `auto`. */
9
+ export declare const DEFAULT_OPENCODE_MODEL = "anthropic/claude-sonnet-4-5";
10
+ /** Keyless default on OpenCode's own gateway. */
11
+ export declare const DEFAULT_OPENCODE_FREE_MODEL = "opencode/glm-5.3-flash-free";
12
+ export declare function isOpencodeModelId(id: string): boolean;
13
+ /** Pick a default cloud model that matches whichever provider key exists. */
14
+ export declare function defaultOpencodeModelForKeys(keys: {
15
+ anthropic?: string;
16
+ openai?: string;
17
+ }): string;
18
+ /** Provider id encoded in an OpenCode model id. */
19
+ export declare function opencodeModelProvider(id: string | null | undefined): "anthropic" | "openai" | null;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * OpenCode model ids are `provider/model` pairs. This is the offline fallback;
3
+ * local workers replace it with `opencode models` and the server can add live
4
+ * Anthropic / OpenAI catalog entries for the same providers.
5
+ */
6
+ export const OPENCODE_MODELS = [
7
+ {
8
+ id: "auto",
9
+ displayName: "Auto",
10
+ description: "Use the CLI's configured default model",
11
+ },
12
+ {
13
+ id: "anthropic/claude-sonnet-4-5",
14
+ displayName: "Claude Sonnet 4.5",
15
+ description: "Anthropic — default OpenCode coding model",
16
+ },
17
+ {
18
+ id: "anthropic/claude-opus-4-1",
19
+ displayName: "Claude Opus 4.1",
20
+ description: "Anthropic — strongest model for autonomous coding",
21
+ },
22
+ {
23
+ id: "openai/gpt-5-codex",
24
+ displayName: "GPT-5 Codex",
25
+ description: "OpenAI — Codex coding model",
26
+ },
27
+ {
28
+ id: "openai/gpt-5",
29
+ displayName: "GPT-5",
30
+ description: "OpenAI — general reasoning and coding",
31
+ },
32
+ ];
33
+ /** Cloud sandboxes need a concrete provider/model instead of `auto`. */
34
+ export const DEFAULT_OPENCODE_MODEL = "anthropic/claude-sonnet-4-5";
35
+ /** Keyless default on OpenCode's own gateway. */
36
+ export const DEFAULT_OPENCODE_FREE_MODEL = "opencode/glm-5.3-flash-free";
37
+ export function isOpencodeModelId(id) {
38
+ if (id === "auto")
39
+ return true;
40
+ const slash = id.indexOf("/");
41
+ if (slash <= 0 || slash === id.length - 1)
42
+ return false;
43
+ const provider = id.slice(0, slash);
44
+ const model = id.slice(slash + 1);
45
+ return (/^[a-z0-9][a-z0-9._-]*$/i.test(provider) &&
46
+ !/\s/.test(model));
47
+ }
48
+ /** Pick a default cloud model that matches whichever provider key exists. */
49
+ export function defaultOpencodeModelForKeys(keys) {
50
+ if (keys.anthropic?.trim())
51
+ return "anthropic/claude-sonnet-4-5";
52
+ if (keys.openai?.trim())
53
+ return "openai/gpt-5-codex";
54
+ return DEFAULT_OPENCODE_FREE_MODEL;
55
+ }
56
+ /** Provider id encoded in an OpenCode model id. */
57
+ export function opencodeModelProvider(id) {
58
+ const provider = (id || "").trim().split("/")[0]?.toLowerCase();
59
+ if (provider === "anthropic" || provider === "openai")
60
+ return provider;
61
+ return null;
62
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * OpenCode scopes sessions to the project directory, and
3
+ * `opencode run --session <id>` for a session from another project never
4
+ * prints or exits. Returns false only when the list loaded and lacks the id,
5
+ * so a slow or broken CLI still resumes as before.
6
+ */
7
+ export declare function opencodeSessionInDirectory(cwd: string, sessionId: string, env: NodeJS.ProcessEnv): Promise<boolean>;
@@ -0,0 +1,30 @@
1
+ import { execFile } from "child_process";
2
+ const SESSION_LIST_TIMEOUT_MS = 15_000;
3
+ /**
4
+ * OpenCode scopes sessions to the project directory, and
5
+ * `opencode run --session <id>` for a session from another project never
6
+ * prints or exits. Returns false only when the list loaded and lacks the id,
7
+ * so a slow or broken CLI still resumes as before.
8
+ */
9
+ export function opencodeSessionInDirectory(cwd, sessionId, env) {
10
+ return new Promise((resolve) => {
11
+ execFile("opencode", ["session", "list", "--format", "json", "-n", "500"], {
12
+ cwd,
13
+ env: { ...env, PWD: cwd },
14
+ timeout: SESSION_LIST_TIMEOUT_MS,
15
+ maxBuffer: 16 * 1024 * 1024,
16
+ }, (err, stdout) => {
17
+ if (err)
18
+ return resolve(true);
19
+ try {
20
+ const list = JSON.parse(String(stdout));
21
+ if (!Array.isArray(list))
22
+ return resolve(true);
23
+ resolve(list.some((s) => s?.id === sessionId));
24
+ }
25
+ catch {
26
+ resolve(true);
27
+ }
28
+ });
29
+ });
30
+ }
@@ -0,0 +1,58 @@
1
+ import type { IssueInfo } from "./issues.js";
2
+ export declare const ORCHESTRATION_BATCH_KINDS: readonly ["linear_import", "spawn_issue", "spawn_room_agent"];
3
+ export type OrchestrationBatchKind = (typeof ORCHESTRATION_BATCH_KINDS)[number];
4
+ export declare const ORCHESTRATION_ITEM_OUTCOMES: readonly ["created", "skipped", "spawned", "assigned", "error"];
5
+ export type OrchestrationItemOutcome = (typeof ORCHESTRATION_ITEM_OUTCOMES)[number];
6
+ export interface OrchestrationBatchItemInfo {
7
+ id: string;
8
+ batchId: string;
9
+ issueId?: string | null;
10
+ agentId?: string | null;
11
+ externalProvider?: string | null;
12
+ externalId?: string | null;
13
+ externalIdentifier?: string | null;
14
+ externalUrl?: string | null;
15
+ title: string;
16
+ outcome: OrchestrationItemOutcome;
17
+ error?: string | null;
18
+ createdAt: number;
19
+ }
20
+ export interface OrchestrationBatchInfo {
21
+ id: string;
22
+ roomId?: string | null;
23
+ orgId?: string | null;
24
+ creatorId: string;
25
+ title: string;
26
+ kind: OrchestrationBatchKind;
27
+ status: string;
28
+ createdAt: number;
29
+ updatedAt: number;
30
+ items: OrchestrationBatchItemInfo[];
31
+ }
32
+ export interface LinearIssueSnapshot {
33
+ id?: string;
34
+ identifier?: string;
35
+ title?: string;
36
+ description?: string;
37
+ url?: string;
38
+ priority?: unknown;
39
+ }
40
+ export interface LinearImportResult {
41
+ batch: OrchestrationBatchInfo;
42
+ created: IssueInfo[];
43
+ skipped: IssueInfo[];
44
+ errors: Array<{
45
+ ref: string;
46
+ error: string;
47
+ }>;
48
+ }
49
+ export interface LinearIssuePreview {
50
+ id?: string;
51
+ identifier?: string;
52
+ title: string;
53
+ description?: string;
54
+ url?: string;
55
+ priority?: string;
56
+ alreadyImported?: boolean;
57
+ existingIssueId?: string;
58
+ }