@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.
- 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 +263 -0
- package/dist/cli/src/agentTools.d.ts +6 -0
- package/dist/cli/src/agentTools.js +104 -0
- package/dist/cli/src/auth.d.ts +5 -0
- package/dist/cli/src/auth.js +33 -0
- package/dist/cli/src/cliEnv.d.ts +6 -0
- package/dist/cli/src/cliEnv.js +26 -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/gitAuthEnv.d.ts +5 -0
- package/dist/cli/src/gitAuthEnv.js +35 -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 +31 -0
- package/dist/cli/src/proc.js +97 -0
- package/dist/cli/src/service.d.ts +17 -0
- package/dist/cli/src/service.js +255 -0
- package/dist/cli/src/workRoots.d.ts +30 -0
- package/dist/cli/src/workRoots.js +228 -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 +721 -0
- package/dist/cli/src/workspace.d.ts +10 -0
- package/dist/cli/src/workspace.js +329 -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 +135 -0
- package/dist/shared/automations.js +553 -0
- package/dist/shared/backends/claudeCode.d.ts +44 -0
- package/dist/shared/backends/claudeCode.js +548 -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 +111 -0
- package/dist/shared/backends/cursor.js +1216 -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 +43 -0
- package/dist/shared/backends/opencode.js +415 -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 +932 -0
- package/dist/shared/events.js +57 -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 +119 -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 +49 -0
- package/dist/shared/workspace.js +53 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +266 -0
- package/package.json +51 -4
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { AgentTodoItem, ClarifyingQuestion } from "../events.js";
|
|
2
|
+
import type { ToolIntegrationRef } from "../toolIntegration.js";
|
|
3
|
+
import type { CliCommandCatalog } from "../slashCommands.js";
|
|
4
|
+
/** Backend kind for a room-level agent process. */
|
|
5
|
+
export type AgentBackendKind = "cursor" | "claude-code" | "codex" | "opencode";
|
|
6
|
+
/** Claude Code, Codex, and OpenCode share the local-CLI + Blaxel-sandbox path. */
|
|
7
|
+
export declare const CLI_SANDBOX_BACKENDS: readonly ["claude-code", "codex", "opencode"];
|
|
8
|
+
export type CliSandboxBackendKind = (typeof CLI_SANDBOX_BACKENDS)[number];
|
|
9
|
+
export declare function isCliSandboxBackend(kind: string | null | undefined): kind is CliSandboxBackendKind;
|
|
10
|
+
export declare function parseAgentBackendKind(raw: unknown): AgentBackendKind;
|
|
11
|
+
export declare function backendShortLabel(kind: AgentBackendKind): string;
|
|
12
|
+
/** Git / PR metadata attached to a completed cloud agent run. */
|
|
13
|
+
export interface RunGitInfo {
|
|
14
|
+
branches: Array<{
|
|
15
|
+
repoUrl: string;
|
|
16
|
+
branch?: string;
|
|
17
|
+
prUrl?: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
/** Token use (and cost, when the CLI reports it) for one finished turn. */
|
|
21
|
+
export interface RunUsage {
|
|
22
|
+
inputTokens: number;
|
|
23
|
+
outputTokens: number;
|
|
24
|
+
cacheReadTokens: number;
|
|
25
|
+
cacheWriteTokens: number;
|
|
26
|
+
/** Reported by the CLI (Claude Code); absent when it has to be estimated. */
|
|
27
|
+
costUsd?: number;
|
|
28
|
+
}
|
|
29
|
+
export type { AgentTodoItem, ClarifyingQuestion };
|
|
30
|
+
/**
|
|
31
|
+
* Normalized agent stream events used internally by Steer.
|
|
32
|
+
* Subagent events from within a single agent carry `subagent_nested`
|
|
33
|
+
* and a parent tool-call id — they do NOT create a new Agent row.
|
|
34
|
+
*/
|
|
35
|
+
export type NormalizedAgentEvent = {
|
|
36
|
+
kind: "session";
|
|
37
|
+
sessionId: string;
|
|
38
|
+
} | {
|
|
39
|
+
kind: "assistant_delta";
|
|
40
|
+
text: string;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "assistant_final";
|
|
43
|
+
text: string;
|
|
44
|
+
} | {
|
|
45
|
+
kind: "thinking_delta";
|
|
46
|
+
text: string;
|
|
47
|
+
} | {
|
|
48
|
+
kind: "thinking_final";
|
|
49
|
+
text: string;
|
|
50
|
+
} | {
|
|
51
|
+
kind: "tool_start";
|
|
52
|
+
callId: string;
|
|
53
|
+
name: string;
|
|
54
|
+
detail: string;
|
|
55
|
+
path?: string;
|
|
56
|
+
todos?: AgentTodoItem[];
|
|
57
|
+
questions?: ClarifyingQuestion[];
|
|
58
|
+
/** Set for MCP calls: which server / integration / tool ran. */
|
|
59
|
+
integration?: ToolIntegrationRef;
|
|
60
|
+
} | {
|
|
61
|
+
kind: "tool_done";
|
|
62
|
+
callId: string;
|
|
63
|
+
name: string;
|
|
64
|
+
detail: string;
|
|
65
|
+
path?: string;
|
|
66
|
+
/** The tool failed / was rejected; `detail` carries the error. */
|
|
67
|
+
isError?: boolean;
|
|
68
|
+
diffPatch?: string;
|
|
69
|
+
todos?: AgentTodoItem[];
|
|
70
|
+
questions?: ClarifyingQuestion[];
|
|
71
|
+
/** Set for MCP calls: which server / integration / tool ran. */
|
|
72
|
+
integration?: ToolIntegrationRef;
|
|
73
|
+
} | {
|
|
74
|
+
kind: "subagent_nested";
|
|
75
|
+
parentCallId: string;
|
|
76
|
+
callId: string;
|
|
77
|
+
name: string;
|
|
78
|
+
detail: string;
|
|
79
|
+
path?: string;
|
|
80
|
+
status: "started" | "completed";
|
|
81
|
+
} | {
|
|
82
|
+
kind: "error";
|
|
83
|
+
message: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The agent's reply and tools are finished; the runner may still be doing
|
|
87
|
+
* post-run work (e.g. pushing the branch) before `done` arrives.
|
|
88
|
+
*/
|
|
89
|
+
| {
|
|
90
|
+
kind: "turn_complete";
|
|
91
|
+
detail?: string;
|
|
92
|
+
}
|
|
93
|
+
/** Runner progress before/around the agent (e.g. "Starting sandbox…"); empty clears it. */
|
|
94
|
+
| {
|
|
95
|
+
kind: "status";
|
|
96
|
+
detail?: string;
|
|
97
|
+
}
|
|
98
|
+
/** Slash commands the CLI reported at startup (drives the composer menu). */
|
|
99
|
+
| {
|
|
100
|
+
kind: "cli_commands";
|
|
101
|
+
catalog: CliCommandCatalog;
|
|
102
|
+
} | {
|
|
103
|
+
kind: "done";
|
|
104
|
+
result: string;
|
|
105
|
+
git?: RunGitInfo;
|
|
106
|
+
usage?: RunUsage;
|
|
107
|
+
};
|
|
108
|
+
export interface BuildArgsOptions {
|
|
109
|
+
prompt: string;
|
|
110
|
+
modelId: string;
|
|
111
|
+
sessionId?: string | null;
|
|
112
|
+
/** Cursor `--mode plan` / Claude `--permission-mode plan`. */
|
|
113
|
+
mode?: "agent" | "plan";
|
|
114
|
+
/** Working directory for Codex `--cd` (repo root in Blaxel). */
|
|
115
|
+
cwd?: string | null;
|
|
116
|
+
/** Streamable-HTTP MCP servers to attach for this invocation. */
|
|
117
|
+
mcpServers?: Record<string, {
|
|
118
|
+
type: "http";
|
|
119
|
+
url: string;
|
|
120
|
+
headers: Record<string, string>;
|
|
121
|
+
}>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Abstraction over headless coding-agent CLIs.
|
|
125
|
+
* Implementations spawn the process and parse NDJSON into NormalizedAgentEvent.
|
|
126
|
+
*/
|
|
127
|
+
export interface WorkerBackend {
|
|
128
|
+
kind: AgentBackendKind;
|
|
129
|
+
/** False until the backend is fully wired and tested. */
|
|
130
|
+
available: boolean;
|
|
131
|
+
/** Executable name (`cursor`, `claude`, …). */
|
|
132
|
+
command: string;
|
|
133
|
+
buildArgs(opts: BuildArgsOptions): string[];
|
|
134
|
+
/** Extra env for this invocation (e.g. OpenCode inline MCP config). */
|
|
135
|
+
buildEnv?(opts: BuildArgsOptions): Record<string, string>;
|
|
136
|
+
/**
|
|
137
|
+
* Parse one JSON object from a stream-json line into zero or more
|
|
138
|
+
* normalized events. Stateful parsers may hold buffers on the instance;
|
|
139
|
+
* callers that need isolation should create a fresh backend per run.
|
|
140
|
+
*/
|
|
141
|
+
parseLine(json: unknown, ctx?: ParseLineContext): NormalizedAgentEvent[];
|
|
142
|
+
/**
|
|
143
|
+
* Emit a terminal event the parser is holding back. OpenCode reports a
|
|
144
|
+
* `stop` on the step that *triggers* compaction too, so its `done` is
|
|
145
|
+
* deferred until the stream stays quiet (a new step cancels it) or the
|
|
146
|
+
* process exits. Call periodically and once on process close.
|
|
147
|
+
*/
|
|
148
|
+
flush?(ctx?: ParseLineContext): NormalizedAgentEvent[];
|
|
149
|
+
}
|
|
150
|
+
export interface ParseLineContext {
|
|
151
|
+
/** Accumulated assistant text so far (mutated by the parser). */
|
|
152
|
+
assistantBuf: {
|
|
153
|
+
value: string;
|
|
154
|
+
};
|
|
155
|
+
/** Accumulated model thinking / reasoning for the current turn. */
|
|
156
|
+
thinkingBuf?: {
|
|
157
|
+
value: string;
|
|
158
|
+
};
|
|
159
|
+
/** Set true when a terminal result/done/error event is emitted. */
|
|
160
|
+
gotTerminalEvent: {
|
|
161
|
+
value: boolean;
|
|
162
|
+
};
|
|
163
|
+
/** Optional stderr buffer used for error messages. */
|
|
164
|
+
stderr?: string;
|
|
165
|
+
/**
|
|
166
|
+
* When true, only emit tool/session events (no assistant text / done / error).
|
|
167
|
+
* Used to re-parse Blaxel process stdout after streamLogs may have missed
|
|
168
|
+
* mid-run tool lines while still delivering the terminal result.
|
|
169
|
+
*/
|
|
170
|
+
toolsOnly?: boolean;
|
|
171
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Claude Code, Codex, and OpenCode share the local-CLI + Blaxel-sandbox path. */
|
|
2
|
+
export const CLI_SANDBOX_BACKENDS = ["claude-code", "codex", "opencode"];
|
|
3
|
+
export function isCliSandboxBackend(kind) {
|
|
4
|
+
return kind === "claude-code" || kind === "codex" || kind === "opencode";
|
|
5
|
+
}
|
|
6
|
+
export function parseAgentBackendKind(raw) {
|
|
7
|
+
if (raw === "claude-code" || raw === "codex" || raw === "opencode")
|
|
8
|
+
return raw;
|
|
9
|
+
return "cursor";
|
|
10
|
+
}
|
|
11
|
+
export function backendShortLabel(kind) {
|
|
12
|
+
if (kind === "claude-code")
|
|
13
|
+
return "Claude";
|
|
14
|
+
if (kind === "codex")
|
|
15
|
+
return "Codex";
|
|
16
|
+
if (kind === "opencode")
|
|
17
|
+
return "OpenCode";
|
|
18
|
+
return "Cursor";
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentBackendKind, RunUsage } from "./types.js";
|
|
2
|
+
/** Claude Code `result` line: `usage` plus `total_cost_usd`. */
|
|
3
|
+
export declare function claudeResultUsage(ev: Record<string, unknown>): RunUsage | undefined;
|
|
4
|
+
/** Codex `turn.completed`: `usage` with input / cached / output tokens. */
|
|
5
|
+
export declare function codexTurnUsage(ev: Record<string, unknown>): RunUsage | undefined;
|
|
6
|
+
export declare function runUsageCostUsd(backend: AgentBackendKind, usage: RunUsage): number;
|
|
7
|
+
export declare function addRunUsage(total: RunUsage, next: RunUsage, backend: AgentBackendKind): RunUsage;
|
|
8
|
+
export declare const EMPTY_RUN_USAGE: RunUsage;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
function num(value) {
|
|
2
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
3
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
4
|
+
}
|
|
5
|
+
function record(value) {
|
|
6
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
7
|
+
? value
|
|
8
|
+
: null;
|
|
9
|
+
}
|
|
10
|
+
/** Claude Code `result` line: `usage` plus `total_cost_usd`. */
|
|
11
|
+
export function claudeResultUsage(ev) {
|
|
12
|
+
const usage = record(ev.usage);
|
|
13
|
+
const cost = num(ev.total_cost_usd);
|
|
14
|
+
if (!usage && !cost)
|
|
15
|
+
return undefined;
|
|
16
|
+
return {
|
|
17
|
+
inputTokens: num(usage?.input_tokens),
|
|
18
|
+
outputTokens: num(usage?.output_tokens),
|
|
19
|
+
cacheReadTokens: num(usage?.cache_read_input_tokens),
|
|
20
|
+
cacheWriteTokens: num(usage?.cache_creation_input_tokens),
|
|
21
|
+
...(cost ? { costUsd: cost } : {}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Codex `turn.completed`: `usage` with input / cached / output tokens. */
|
|
25
|
+
export function codexTurnUsage(ev) {
|
|
26
|
+
const usage = record(ev.usage);
|
|
27
|
+
if (!usage)
|
|
28
|
+
return undefined;
|
|
29
|
+
const cached = num(usage.cached_input_tokens);
|
|
30
|
+
return {
|
|
31
|
+
// OpenAI counts cached tokens inside input_tokens.
|
|
32
|
+
inputTokens: Math.max(0, num(usage.input_tokens) - cached),
|
|
33
|
+
outputTokens: num(usage.output_tokens),
|
|
34
|
+
cacheReadTokens: cached,
|
|
35
|
+
cacheWriteTokens: 0,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** USD per million tokens, used only when the CLI doesn't report cost. */
|
|
39
|
+
const ESTIMATE_RATES = {
|
|
40
|
+
"claude-code": { input: 3, cached: 0.3, output: 15 },
|
|
41
|
+
codex: { input: 1.25, cached: 0.125, output: 10 },
|
|
42
|
+
// OpenCode reports cost directly; model-specific fallback rates would be wrong.
|
|
43
|
+
opencode: { input: 0, cached: 0, output: 0 },
|
|
44
|
+
};
|
|
45
|
+
export function runUsageCostUsd(backend, usage) {
|
|
46
|
+
if (usage.costUsd != null)
|
|
47
|
+
return usage.costUsd;
|
|
48
|
+
if (backend === "cursor")
|
|
49
|
+
return 0;
|
|
50
|
+
const rate = ESTIMATE_RATES[backend];
|
|
51
|
+
return ((usage.inputTokens * rate.input +
|
|
52
|
+
(usage.cacheReadTokens + usage.cacheWriteTokens) * rate.cached +
|
|
53
|
+
usage.outputTokens * rate.output) /
|
|
54
|
+
1_000_000);
|
|
55
|
+
}
|
|
56
|
+
export function addRunUsage(total, next, backend) {
|
|
57
|
+
return {
|
|
58
|
+
inputTokens: total.inputTokens + next.inputTokens,
|
|
59
|
+
outputTokens: total.outputTokens + next.outputTokens,
|
|
60
|
+
cacheReadTokens: total.cacheReadTokens + next.cacheReadTokens,
|
|
61
|
+
cacheWriteTokens: total.cacheWriteTokens + next.cacheWriteTokens,
|
|
62
|
+
costUsd: (total.costUsd ?? 0) + runUsageCostUsd(backend, next),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export const EMPTY_RUN_USAGE = {
|
|
66
|
+
inputTokens: 0,
|
|
67
|
+
outputTokens: 0,
|
|
68
|
+
cacheReadTokens: 0,
|
|
69
|
+
cacheWriteTokens: 0,
|
|
70
|
+
costUsd: 0,
|
|
71
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace billing plans. `null` on a numeric limit means unlimited.
|
|
3
|
+
* Effective limits are PLAN_DEFAULTS, then BILLING_PLAN_OVERRIDES, then a
|
|
4
|
+
* per-organization override stored by an admin.
|
|
5
|
+
*/
|
|
6
|
+
export type PlanId = "free" | "team" | "business";
|
|
7
|
+
export declare const PLAN_IDS: PlanId[];
|
|
8
|
+
export declare const PLAN_RANK: Record<PlanId, number>;
|
|
9
|
+
export declare const PLAN_LABELS: Record<PlanId, string>;
|
|
10
|
+
/** Runner safety cap when a plan leaves concurrent issues unlimited. */
|
|
11
|
+
export declare const ABSOLUTE_MAX_CONCURRENT_ISSUES = 64;
|
|
12
|
+
export interface PlanLimits {
|
|
13
|
+
maxWorkspaces: number | null;
|
|
14
|
+
maxDrivers: number | null;
|
|
15
|
+
maxViewers: number | null;
|
|
16
|
+
maxConcurrentRooms: number | null;
|
|
17
|
+
maxConcurrentIssues: number | null;
|
|
18
|
+
maxQueuedIssues: number | null;
|
|
19
|
+
issuesPerDay: number | null;
|
|
20
|
+
swarmsPerWeek: number | null;
|
|
21
|
+
maxSwarmAgents: number | null;
|
|
22
|
+
swarmBudgetUsd: number | null;
|
|
23
|
+
maxIntegrations: number | null;
|
|
24
|
+
/**
|
|
25
|
+
* Included hosted-tool spend per calendar month, in USD.
|
|
26
|
+
* `0` blocks hosted calls. `null` means unlimited. Not per seat.
|
|
27
|
+
*/
|
|
28
|
+
hostedToolsUsdPerMonth: number | null;
|
|
29
|
+
/** Included hosted minutes per calendar month. Team multiplies by driver seats. */
|
|
30
|
+
sandboxMinutesPerMonth: number | null;
|
|
31
|
+
sandboxMinutesPerSeat: boolean;
|
|
32
|
+
maxConcurrentSandboxes: number | null;
|
|
33
|
+
sandboxMaxRunMinutes: number | null;
|
|
34
|
+
sandboxMemoryMb: number;
|
|
35
|
+
allowOverage: boolean;
|
|
36
|
+
policy: boolean;
|
|
37
|
+
/** Days of audit-log history kept and shown. `null` keeps everything. */
|
|
38
|
+
auditRetentionDays: number | null;
|
|
39
|
+
auditExport: boolean;
|
|
40
|
+
sso: boolean;
|
|
41
|
+
customMcpAllowlist: boolean;
|
|
42
|
+
hostedSandbox: boolean;
|
|
43
|
+
}
|
|
44
|
+
export type PlanLimitKey = keyof PlanLimits;
|
|
45
|
+
export declare const PLAN_DEFAULTS: Record<PlanId, PlanLimits>;
|
|
46
|
+
export declare const LIMIT_LABELS: Record<PlanLimitKey, string>;
|
|
47
|
+
export declare function isPlanId(value: unknown): value is PlanId;
|
|
48
|
+
export declare function higherPlan(a: PlanId, b: PlanId): PlanId;
|
|
49
|
+
export declare function parsePlanId(value: unknown, fallback?: PlanId): PlanId;
|
|
50
|
+
/** Keep only known limit fields. `null` is preserved and means unlimited. */
|
|
51
|
+
export declare function parseLimitsOverride(raw: unknown): Partial<PlanLimits> | null;
|
|
52
|
+
export declare function mergePlanLimits(base: PlanLimits, ...overrides: Array<Partial<PlanLimits> | null | undefined>): PlanLimits;
|
|
53
|
+
export declare function envPlanOverrides(): Partial<Record<PlanId, Partial<PlanLimits>>>;
|
|
54
|
+
export declare function limitsForPlan(plan: PlanId, orgOverride?: Partial<PlanLimits> | null): PlanLimits;
|
|
55
|
+
/** Oldest audit row a plan keeps/shows (ms epoch), or null for full history. */
|
|
56
|
+
export declare function auditCutoffMs(limits: PlanLimits, now?: number): number | null;
|
|
57
|
+
export declare function monthStartMs(now?: number): number;
|
|
58
|
+
export declare function dayStartMs(now?: number): number;
|
|
59
|
+
export declare function weekStartMs(now?: number): number;
|
|
60
|
+
export declare function includedSandboxSeconds(limits: PlanLimits, driverSeats: number): number | null;
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace billing plans. `null` on a numeric limit means unlimited.
|
|
3
|
+
* Effective limits are PLAN_DEFAULTS, then BILLING_PLAN_OVERRIDES, then a
|
|
4
|
+
* per-organization override stored by an admin.
|
|
5
|
+
*/
|
|
6
|
+
export const PLAN_IDS = ["free", "team", "business"];
|
|
7
|
+
export const PLAN_RANK = {
|
|
8
|
+
free: 0,
|
|
9
|
+
team: 1,
|
|
10
|
+
business: 2,
|
|
11
|
+
};
|
|
12
|
+
export const PLAN_LABELS = {
|
|
13
|
+
free: "Free",
|
|
14
|
+
team: "Team",
|
|
15
|
+
business: "Business",
|
|
16
|
+
};
|
|
17
|
+
/** Runner safety cap when a plan leaves concurrent issues unlimited. */
|
|
18
|
+
export const ABSOLUTE_MAX_CONCURRENT_ISSUES = 64;
|
|
19
|
+
export const PLAN_DEFAULTS = {
|
|
20
|
+
free: {
|
|
21
|
+
maxWorkspaces: 2,
|
|
22
|
+
maxDrivers: 3,
|
|
23
|
+
maxViewers: 5,
|
|
24
|
+
maxConcurrentRooms: 3,
|
|
25
|
+
maxConcurrentIssues: 3,
|
|
26
|
+
maxQueuedIssues: 25,
|
|
27
|
+
issuesPerDay: 20,
|
|
28
|
+
swarmsPerWeek: 1,
|
|
29
|
+
maxSwarmAgents: 8,
|
|
30
|
+
swarmBudgetUsd: 25,
|
|
31
|
+
maxIntegrations: 10,
|
|
32
|
+
hostedToolsUsdPerMonth: 0,
|
|
33
|
+
sandboxMinutesPerMonth: 60,
|
|
34
|
+
sandboxMinutesPerSeat: false,
|
|
35
|
+
maxConcurrentSandboxes: 1,
|
|
36
|
+
sandboxMaxRunMinutes: 20,
|
|
37
|
+
sandboxMemoryMb: 2048,
|
|
38
|
+
allowOverage: false,
|
|
39
|
+
policy: false,
|
|
40
|
+
auditRetentionDays: 7,
|
|
41
|
+
auditExport: false,
|
|
42
|
+
sso: false,
|
|
43
|
+
customMcpAllowlist: false,
|
|
44
|
+
hostedSandbox: true,
|
|
45
|
+
},
|
|
46
|
+
team: {
|
|
47
|
+
maxWorkspaces: 10,
|
|
48
|
+
maxDrivers: null,
|
|
49
|
+
maxViewers: null,
|
|
50
|
+
maxConcurrentRooms: 25,
|
|
51
|
+
maxConcurrentIssues: 15,
|
|
52
|
+
maxQueuedIssues: 200,
|
|
53
|
+
issuesPerDay: null,
|
|
54
|
+
swarmsPerWeek: 20,
|
|
55
|
+
maxSwarmAgents: 20,
|
|
56
|
+
swarmBudgetUsd: 100,
|
|
57
|
+
maxIntegrations: null,
|
|
58
|
+
hostedToolsUsdPerMonth: 0,
|
|
59
|
+
sandboxMinutesPerMonth: 600,
|
|
60
|
+
sandboxMinutesPerSeat: true,
|
|
61
|
+
maxConcurrentSandboxes: 10,
|
|
62
|
+
sandboxMaxRunMinutes: 120,
|
|
63
|
+
sandboxMemoryMb: 4096,
|
|
64
|
+
allowOverage: true,
|
|
65
|
+
policy: true,
|
|
66
|
+
auditRetentionDays: 90,
|
|
67
|
+
auditExport: false,
|
|
68
|
+
sso: false,
|
|
69
|
+
customMcpAllowlist: false,
|
|
70
|
+
hostedSandbox: true,
|
|
71
|
+
},
|
|
72
|
+
business: {
|
|
73
|
+
maxWorkspaces: null,
|
|
74
|
+
maxDrivers: null,
|
|
75
|
+
maxViewers: null,
|
|
76
|
+
maxConcurrentRooms: null,
|
|
77
|
+
maxConcurrentIssues: null,
|
|
78
|
+
maxQueuedIssues: null,
|
|
79
|
+
issuesPerDay: null,
|
|
80
|
+
swarmsPerWeek: null,
|
|
81
|
+
maxSwarmAgents: null,
|
|
82
|
+
swarmBudgetUsd: null,
|
|
83
|
+
maxIntegrations: null,
|
|
84
|
+
hostedToolsUsdPerMonth: 0,
|
|
85
|
+
sandboxMinutesPerMonth: null,
|
|
86
|
+
sandboxMinutesPerSeat: false,
|
|
87
|
+
maxConcurrentSandboxes: null,
|
|
88
|
+
sandboxMaxRunMinutes: 240,
|
|
89
|
+
sandboxMemoryMb: 8192,
|
|
90
|
+
allowOverage: true,
|
|
91
|
+
policy: true,
|
|
92
|
+
auditRetentionDays: null,
|
|
93
|
+
auditExport: true,
|
|
94
|
+
sso: true,
|
|
95
|
+
customMcpAllowlist: true,
|
|
96
|
+
hostedSandbox: true,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
const NUMERIC_KEYS = [
|
|
100
|
+
"maxWorkspaces",
|
|
101
|
+
"maxDrivers",
|
|
102
|
+
"maxViewers",
|
|
103
|
+
"maxConcurrentRooms",
|
|
104
|
+
"maxConcurrentIssues",
|
|
105
|
+
"maxQueuedIssues",
|
|
106
|
+
"issuesPerDay",
|
|
107
|
+
"swarmsPerWeek",
|
|
108
|
+
"maxSwarmAgents",
|
|
109
|
+
"swarmBudgetUsd",
|
|
110
|
+
"maxIntegrations",
|
|
111
|
+
"hostedToolsUsdPerMonth",
|
|
112
|
+
"sandboxMinutesPerMonth",
|
|
113
|
+
"maxConcurrentSandboxes",
|
|
114
|
+
"sandboxMaxRunMinutes",
|
|
115
|
+
"sandboxMemoryMb",
|
|
116
|
+
"auditRetentionDays",
|
|
117
|
+
];
|
|
118
|
+
const BOOLEAN_KEYS = [
|
|
119
|
+
"sandboxMinutesPerSeat",
|
|
120
|
+
"allowOverage",
|
|
121
|
+
"policy",
|
|
122
|
+
"auditExport",
|
|
123
|
+
"sso",
|
|
124
|
+
"customMcpAllowlist",
|
|
125
|
+
"hostedSandbox",
|
|
126
|
+
];
|
|
127
|
+
export const LIMIT_LABELS = {
|
|
128
|
+
maxWorkspaces: "workspaces",
|
|
129
|
+
maxDrivers: "drivers",
|
|
130
|
+
maxViewers: "viewers",
|
|
131
|
+
maxConcurrentRooms: "concurrent rooms",
|
|
132
|
+
maxConcurrentIssues: "concurrent issues",
|
|
133
|
+
maxQueuedIssues: "queued issues",
|
|
134
|
+
issuesPerDay: "issue runs per day",
|
|
135
|
+
swarmsPerWeek: "swarms per week",
|
|
136
|
+
maxSwarmAgents: "swarm agents",
|
|
137
|
+
swarmBudgetUsd: "swarm budget",
|
|
138
|
+
maxIntegrations: "integrations",
|
|
139
|
+
hostedToolsUsdPerMonth: "hosted tools",
|
|
140
|
+
sandboxMinutesPerMonth: "hosted sandbox minutes",
|
|
141
|
+
sandboxMinutesPerSeat: "sandbox minutes per seat",
|
|
142
|
+
maxConcurrentSandboxes: "concurrent sandboxes",
|
|
143
|
+
sandboxMaxRunMinutes: "sandbox run length",
|
|
144
|
+
sandboxMemoryMb: "sandbox memory",
|
|
145
|
+
allowOverage: "sandbox overage",
|
|
146
|
+
policy: "integration policy",
|
|
147
|
+
auditRetentionDays: "audit log history",
|
|
148
|
+
auditExport: "audit export",
|
|
149
|
+
sso: "SSO",
|
|
150
|
+
customMcpAllowlist: "custom MCP allowlist",
|
|
151
|
+
hostedSandbox: "hosted sandboxes",
|
|
152
|
+
};
|
|
153
|
+
export function isPlanId(value) {
|
|
154
|
+
return value === "free" || value === "team" || value === "business";
|
|
155
|
+
}
|
|
156
|
+
export function higherPlan(a, b) {
|
|
157
|
+
return PLAN_RANK[a] >= PLAN_RANK[b] ? a : b;
|
|
158
|
+
}
|
|
159
|
+
export function parsePlanId(value, fallback = "free") {
|
|
160
|
+
return isPlanId(value) ? value : fallback;
|
|
161
|
+
}
|
|
162
|
+
function asLimitNumber(value) {
|
|
163
|
+
if (value === null)
|
|
164
|
+
return null;
|
|
165
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
if (typeof value === "string" && value.trim()) {
|
|
169
|
+
const n = Number(value);
|
|
170
|
+
if (Number.isFinite(n) && n >= 0)
|
|
171
|
+
return n;
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
/** Keep only known limit fields. `null` is preserved and means unlimited. */
|
|
176
|
+
export function parseLimitsOverride(raw) {
|
|
177
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
178
|
+
return null;
|
|
179
|
+
const src = raw;
|
|
180
|
+
const out = {};
|
|
181
|
+
let any = false;
|
|
182
|
+
for (const key of NUMERIC_KEYS) {
|
|
183
|
+
if (!(key in src))
|
|
184
|
+
continue;
|
|
185
|
+
const n = asLimitNumber(src[key]);
|
|
186
|
+
if (n === undefined)
|
|
187
|
+
continue;
|
|
188
|
+
out[key] = n;
|
|
189
|
+
any = true;
|
|
190
|
+
}
|
|
191
|
+
for (const key of BOOLEAN_KEYS) {
|
|
192
|
+
if (typeof src[key] === "boolean") {
|
|
193
|
+
out[key] = src[key];
|
|
194
|
+
any = true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return any ? out : null;
|
|
198
|
+
}
|
|
199
|
+
export function mergePlanLimits(base, ...overrides) {
|
|
200
|
+
const out = { ...base };
|
|
201
|
+
for (const over of overrides) {
|
|
202
|
+
if (!over)
|
|
203
|
+
continue;
|
|
204
|
+
for (const key of NUMERIC_KEYS) {
|
|
205
|
+
if (over[key] !== undefined) {
|
|
206
|
+
out[key] = over[key];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (const key of BOOLEAN_KEYS) {
|
|
210
|
+
if (over[key] !== undefined)
|
|
211
|
+
out[key] = over[key];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (!Number.isFinite(out.sandboxMemoryMb) || out.sandboxMemoryMb < 512) {
|
|
215
|
+
out.sandboxMemoryMb = base.sandboxMemoryMb;
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
export function envPlanOverrides() {
|
|
220
|
+
const raw = process.env.BILLING_PLAN_OVERRIDES?.trim();
|
|
221
|
+
if (!raw)
|
|
222
|
+
return {};
|
|
223
|
+
try {
|
|
224
|
+
const parsed = JSON.parse(raw);
|
|
225
|
+
const out = {};
|
|
226
|
+
for (const id of PLAN_IDS) {
|
|
227
|
+
const patch = parseLimitsOverride(parsed[id]);
|
|
228
|
+
if (patch)
|
|
229
|
+
out[id] = patch;
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return {};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
export function limitsForPlan(plan, orgOverride) {
|
|
238
|
+
return mergePlanLimits(PLAN_DEFAULTS[plan], envPlanOverrides()[plan], orgOverride);
|
|
239
|
+
}
|
|
240
|
+
/** Oldest audit row a plan keeps/shows (ms epoch), or null for full history. */
|
|
241
|
+
export function auditCutoffMs(limits, now = Date.now()) {
|
|
242
|
+
if (limits.auditRetentionDays == null)
|
|
243
|
+
return null;
|
|
244
|
+
return now - limits.auditRetentionDays * 24 * 60 * 60 * 1000;
|
|
245
|
+
}
|
|
246
|
+
export function monthStartMs(now = Date.now()) {
|
|
247
|
+
const d = new Date(now);
|
|
248
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1);
|
|
249
|
+
}
|
|
250
|
+
export function dayStartMs(now = Date.now()) {
|
|
251
|
+
const d = new Date(now);
|
|
252
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
|
253
|
+
}
|
|
254
|
+
export function weekStartMs(now = Date.now()) {
|
|
255
|
+
return now - 7 * 24 * 60 * 60 * 1000;
|
|
256
|
+
}
|
|
257
|
+
export function includedSandboxSeconds(limits, driverSeats) {
|
|
258
|
+
if (limits.sandboxMinutesPerMonth == null)
|
|
259
|
+
return null;
|
|
260
|
+
const seats = limits.sandboxMinutesPerSeat ? Math.max(1, driverSeats) : 1;
|
|
261
|
+
return Math.round(limits.sandboxMinutesPerMonth * seats * 60);
|
|
262
|
+
}
|