luckerr 0.41.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 +267 -0
- package/README.zh-CN.md +237 -0
- package/dashboard/app.css +3022 -0
- package/dashboard/dist/app.js +30137 -0
- package/dashboard/dist/app.js.map +1 -0
- package/dashboard/dist/vendor-hljs.css +10 -0
- package/dashboard/dist/vendor-uplot.css +1 -0
- package/dashboard/index.html +19 -0
- package/data/deepseek-tokenizer.json.gz +0 -0
- package/dist/cli/acp-EOOAI4F5.js +712 -0
- package/dist/cli/acp-EOOAI4F5.js.map +1 -0
- package/dist/cli/chat-7J6GJXL2.js +51 -0
- package/dist/cli/chat-7J6GJXL2.js.map +1 -0
- package/dist/cli/chunk-2425HK6U.js +54 -0
- package/dist/cli/chunk-2425HK6U.js.map +1 -0
- package/dist/cli/chunk-25T6CVUP.js +172 -0
- package/dist/cli/chunk-25T6CVUP.js.map +1 -0
- package/dist/cli/chunk-2UQP6H6T.js +31 -0
- package/dist/cli/chunk-2UQP6H6T.js.map +1 -0
- package/dist/cli/chunk-56OAJILV.js +47 -0
- package/dist/cli/chunk-56OAJILV.js.map +1 -0
- package/dist/cli/chunk-5FTI4KXH.js +150 -0
- package/dist/cli/chunk-5FTI4KXH.js.map +1 -0
- package/dist/cli/chunk-5TWQD73O.js +2846 -0
- package/dist/cli/chunk-5TWQD73O.js.map +1 -0
- package/dist/cli/chunk-653BOCMK.js +40 -0
- package/dist/cli/chunk-653BOCMK.js.map +1 -0
- package/dist/cli/chunk-6ALJTWWQ.js +2663 -0
- package/dist/cli/chunk-6ALJTWWQ.js.map +1 -0
- package/dist/cli/chunk-6DRKA2IL.js +341 -0
- package/dist/cli/chunk-6DRKA2IL.js.map +1 -0
- package/dist/cli/chunk-6LV63NJV.js +634 -0
- package/dist/cli/chunk-6LV63NJV.js.map +1 -0
- package/dist/cli/chunk-74EX7SUH.js +25293 -0
- package/dist/cli/chunk-74EX7SUH.js.map +1 -0
- package/dist/cli/chunk-74U5RKTX.js +60611 -0
- package/dist/cli/chunk-74U5RKTX.js.map +1 -0
- package/dist/cli/chunk-ANJSUESV.js +143 -0
- package/dist/cli/chunk-ANJSUESV.js.map +1 -0
- package/dist/cli/chunk-DB2Z3DKZ.js +54 -0
- package/dist/cli/chunk-DB2Z3DKZ.js.map +1 -0
- package/dist/cli/chunk-DDIH3ZAA.js +400 -0
- package/dist/cli/chunk-DDIH3ZAA.js.map +1 -0
- package/dist/cli/chunk-ELN3Z3B2.js +621 -0
- package/dist/cli/chunk-ELN3Z3B2.js.map +1 -0
- package/dist/cli/chunk-F6BSQJGV.js +200 -0
- package/dist/cli/chunk-F6BSQJGV.js.map +1 -0
- package/dist/cli/chunk-FET2UAG5.js +246 -0
- package/dist/cli/chunk-FET2UAG5.js.map +1 -0
- package/dist/cli/chunk-FFJ342IJ.js +190 -0
- package/dist/cli/chunk-FFJ342IJ.js.map +1 -0
- package/dist/cli/chunk-GB3247B6.js +130 -0
- package/dist/cli/chunk-GB3247B6.js.map +1 -0
- package/dist/cli/chunk-HC2J4U3G.js +373 -0
- package/dist/cli/chunk-HC2J4U3G.js.map +1 -0
- package/dist/cli/chunk-HRUZAIHQ.js +42 -0
- package/dist/cli/chunk-HRUZAIHQ.js.map +1 -0
- package/dist/cli/chunk-J3ZJFUDL.js +308 -0
- package/dist/cli/chunk-J3ZJFUDL.js.map +1 -0
- package/dist/cli/chunk-J5XJHLWM.js +55 -0
- package/dist/cli/chunk-J5XJHLWM.js.map +1 -0
- package/dist/cli/chunk-JFGLMRZ6.js +160 -0
- package/dist/cli/chunk-JFGLMRZ6.js.map +1 -0
- package/dist/cli/chunk-JMBMLOBP.js +26 -0
- package/dist/cli/chunk-JMBMLOBP.js.map +1 -0
- package/dist/cli/chunk-JMWHXZEL.js +551 -0
- package/dist/cli/chunk-JMWHXZEL.js.map +1 -0
- package/dist/cli/chunk-KEQGPJBO.js +209 -0
- package/dist/cli/chunk-KEQGPJBO.js.map +1 -0
- package/dist/cli/chunk-M4K6U37F.js +232 -0
- package/dist/cli/chunk-M4K6U37F.js.map +1 -0
- package/dist/cli/chunk-MIJI2WMN.js +95 -0
- package/dist/cli/chunk-MIJI2WMN.js.map +1 -0
- package/dist/cli/chunk-MPAO3JNR.js +128 -0
- package/dist/cli/chunk-MPAO3JNR.js.map +1 -0
- package/dist/cli/chunk-PZOFBEDC.js +873 -0
- package/dist/cli/chunk-PZOFBEDC.js.map +1 -0
- package/dist/cli/chunk-RAILYQLN.js +46 -0
- package/dist/cli/chunk-RAILYQLN.js.map +1 -0
- package/dist/cli/chunk-RR35VQVT.js +90 -0
- package/dist/cli/chunk-RR35VQVT.js.map +1 -0
- package/dist/cli/chunk-RRA7VPW4.js +417 -0
- package/dist/cli/chunk-RRA7VPW4.js.map +1 -0
- package/dist/cli/chunk-RU36QVN3.js +452 -0
- package/dist/cli/chunk-RU36QVN3.js.map +1 -0
- package/dist/cli/chunk-RUBIINXR.js +1819 -0
- package/dist/cli/chunk-RUBIINXR.js.map +1 -0
- package/dist/cli/chunk-S4XVGLRW.js +499 -0
- package/dist/cli/chunk-S4XVGLRW.js.map +1 -0
- package/dist/cli/chunk-TUK7OWJA.js +51 -0
- package/dist/cli/chunk-TUK7OWJA.js.map +1 -0
- package/dist/cli/chunk-VALDDV76.js +580 -0
- package/dist/cli/chunk-VALDDV76.js.map +1 -0
- package/dist/cli/chunk-WQOGPYGN.js +11390 -0
- package/dist/cli/chunk-WQOGPYGN.js.map +1 -0
- package/dist/cli/chunk-WREKDFXT.js +34320 -0
- package/dist/cli/chunk-WREKDFXT.js.map +1 -0
- package/dist/cli/chunk-Y7XQU2EL.js +270 -0
- package/dist/cli/chunk-Y7XQU2EL.js.map +1 -0
- package/dist/cli/chunk-YBVCZJU4.js +54 -0
- package/dist/cli/chunk-YBVCZJU4.js.map +1 -0
- package/dist/cli/chunk-YLIHDXUQ.js +749 -0
- package/dist/cli/chunk-YLIHDXUQ.js.map +1 -0
- package/dist/cli/chunk-YV5XXFD7.js +767 -0
- package/dist/cli/chunk-YV5XXFD7.js.map +1 -0
- package/dist/cli/chunk-ZRCNIYRQ.js +101 -0
- package/dist/cli/chunk-ZRCNIYRQ.js.map +1 -0
- package/dist/cli/code-CRKVCMFZ.js +155 -0
- package/dist/cli/code-CRKVCMFZ.js.map +1 -0
- package/dist/cli/commands-QLMD3T7B.js +356 -0
- package/dist/cli/commands-QLMD3T7B.js.map +1 -0
- package/dist/cli/commit-53PP32NC.js +293 -0
- package/dist/cli/commit-53PP32NC.js.map +1 -0
- package/dist/cli/desktop-R6W5CLJ5.js +1046 -0
- package/dist/cli/desktop-R6W5CLJ5.js.map +1 -0
- package/dist/cli/devtools-YECO25QO.js +3719 -0
- package/dist/cli/devtools-YECO25QO.js.map +1 -0
- package/dist/cli/diff-LYNRCJZE.js +166 -0
- package/dist/cli/diff-LYNRCJZE.js.map +1 -0
- package/dist/cli/doctor-5IBP4R5J.js +28 -0
- package/dist/cli/doctor-5IBP4R5J.js.map +1 -0
- package/dist/cli/events-QN6KLN2V.js +340 -0
- package/dist/cli/events-QN6KLN2V.js.map +1 -0
- package/dist/cli/index.js +3500 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/mcp-FGKEH7RG.js +277 -0
- package/dist/cli/mcp-FGKEH7RG.js.map +1 -0
- package/dist/cli/mcp-browse-YCND4NWT.js +178 -0
- package/dist/cli/mcp-browse-YCND4NWT.js.map +1 -0
- package/dist/cli/mcp-inspect-V34J3VX5.js +143 -0
- package/dist/cli/mcp-inspect-V34J3VX5.js.map +1 -0
- package/dist/cli/package.json +3 -0
- package/dist/cli/prompt-I775PNKT.js +16 -0
- package/dist/cli/prompt-I775PNKT.js.map +1 -0
- package/dist/cli/prune-sessions-KGIIYD3P.js +44 -0
- package/dist/cli/prune-sessions-KGIIYD3P.js.map +1 -0
- package/dist/cli/replay-RDXLUAOE.js +292 -0
- package/dist/cli/replay-RDXLUAOE.js.map +1 -0
- package/dist/cli/run-RCAC2RYW.js +223 -0
- package/dist/cli/run-RCAC2RYW.js.map +1 -0
- package/dist/cli/server-FFU6TLYJ.js +3658 -0
- package/dist/cli/server-FFU6TLYJ.js.map +1 -0
- package/dist/cli/sessions-QT26MQAE.js +107 -0
- package/dist/cli/sessions-QT26MQAE.js.map +1 -0
- package/dist/cli/setup-VV4WKXHV.js +767 -0
- package/dist/cli/setup-VV4WKXHV.js.map +1 -0
- package/dist/cli/stats-JVZPQWAN.js +15 -0
- package/dist/cli/stats-JVZPQWAN.js.map +1 -0
- package/dist/cli/update-KYI3OVJP.js +15 -0
- package/dist/cli/update-KYI3OVJP.js.map +1 -0
- package/dist/cli/version-ANYORXTI.js +34 -0
- package/dist/cli/version-ANYORXTI.js.map +1 -0
- package/dist/index.d.ts +2557 -0
- package/dist/index.js +15000 -0
- package/dist/index.js.map +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
|
|
3
|
+
import {
|
|
4
|
+
formatMcpLifecycleEvent,
|
|
5
|
+
formatMcpSlowToast
|
|
6
|
+
} from "./chunk-RAILYQLN.js";
|
|
7
|
+
import {
|
|
8
|
+
buildTransportFromSpec,
|
|
9
|
+
preflightStdioSpec
|
|
10
|
+
} from "./chunk-HRUZAIHQ.js";
|
|
11
|
+
import {
|
|
12
|
+
CacheFirstLoop,
|
|
13
|
+
ImmutablePrefix,
|
|
14
|
+
ToolRegistry,
|
|
15
|
+
bridgeMcpTools
|
|
16
|
+
} from "./chunk-WQOGPYGN.js";
|
|
17
|
+
import "./chunk-J3ZJFUDL.js";
|
|
18
|
+
import {
|
|
19
|
+
openTranscriptFile,
|
|
20
|
+
recordFromLoopEvent,
|
|
21
|
+
writeRecord
|
|
22
|
+
} from "./chunk-FFJ342IJ.js";
|
|
23
|
+
import {
|
|
24
|
+
McpClient,
|
|
25
|
+
parseMcpSpec
|
|
26
|
+
} from "./chunk-YV5XXFD7.js";
|
|
27
|
+
import "./chunk-6LV63NJV.js";
|
|
28
|
+
import "./chunk-F6BSQJGV.js";
|
|
29
|
+
import {
|
|
30
|
+
loadDotenv
|
|
31
|
+
} from "./chunk-2UQP6H6T.js";
|
|
32
|
+
import "./chunk-RUBIINXR.js";
|
|
33
|
+
import "./chunk-VALDDV76.js";
|
|
34
|
+
import "./chunk-KEQGPJBO.js";
|
|
35
|
+
import "./chunk-S4XVGLRW.js";
|
|
36
|
+
import {
|
|
37
|
+
DeepSeekClient
|
|
38
|
+
} from "./chunk-DDIH3ZAA.js";
|
|
39
|
+
import "./chunk-25T6CVUP.js";
|
|
40
|
+
import "./chunk-Y7XQU2EL.js";
|
|
41
|
+
import "./chunk-5TWQD73O.js";
|
|
42
|
+
import {
|
|
43
|
+
defaultConfigPath,
|
|
44
|
+
isPlausibleKey,
|
|
45
|
+
loadApiKey,
|
|
46
|
+
loadBaseUrl,
|
|
47
|
+
mcpEnvFor,
|
|
48
|
+
readConfig,
|
|
49
|
+
saveApiKey
|
|
50
|
+
} from "./chunk-6ALJTWWQ.js";
|
|
51
|
+
import {
|
|
52
|
+
appendUsage
|
|
53
|
+
} from "./chunk-M4K6U37F.js";
|
|
54
|
+
import "./chunk-ANJSUESV.js";
|
|
55
|
+
import "./chunk-JMWHXZEL.js";
|
|
56
|
+
import "./chunk-MPAO3JNR.js";
|
|
57
|
+
import "./chunk-TUK7OWJA.js";
|
|
58
|
+
|
|
59
|
+
// src/cli/commands/run.ts
|
|
60
|
+
import { stdin, stdout } from "process";
|
|
61
|
+
import { createInterface } from "readline/promises";
|
|
62
|
+
async function ensureApiKey() {
|
|
63
|
+
const existing = loadApiKey();
|
|
64
|
+
if (existing) return existing;
|
|
65
|
+
if (!stdin.isTTY) {
|
|
66
|
+
process.stderr.write(
|
|
67
|
+
"DEEPSEEK_API_KEY is not set and stdin is not a TTY (cannot prompt).\nSet the env var, or run `luckerr chat` once interactively to save a key.\n"
|
|
68
|
+
);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
process.stdout.write(
|
|
72
|
+
"DeepSeek API key not configured.\nGet one at https://platform.deepseek.com/api_keys\n"
|
|
73
|
+
);
|
|
74
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
75
|
+
try {
|
|
76
|
+
while (true) {
|
|
77
|
+
const answer = (await rl.question("API key \u203A ")).trim();
|
|
78
|
+
if (!answer) continue;
|
|
79
|
+
if (!isPlausibleKey(answer)) {
|
|
80
|
+
process.stdout.write("Key looks too short. Paste the full token (16+ chars, no spaces).\n");
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
saveApiKey(answer);
|
|
84
|
+
process.stdout.write(`Saved to ${defaultConfigPath()}
|
|
85
|
+
|
|
86
|
+
`);
|
|
87
|
+
return answer;
|
|
88
|
+
}
|
|
89
|
+
} finally {
|
|
90
|
+
rl.close();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function runCommand(opts) {
|
|
94
|
+
loadDotenv();
|
|
95
|
+
const apiKey = await ensureApiKey();
|
|
96
|
+
process.env.DEEPSEEK_API_KEY = apiKey;
|
|
97
|
+
const requestedSpecs = opts.mcp ?? [];
|
|
98
|
+
const clients = [];
|
|
99
|
+
let tools;
|
|
100
|
+
let successCount = 0;
|
|
101
|
+
const disabledNames = new Set(readConfig().mcpDisabled ?? []);
|
|
102
|
+
if (requestedSpecs.length > 0) {
|
|
103
|
+
tools = new ToolRegistry();
|
|
104
|
+
for (const raw of requestedSpecs) {
|
|
105
|
+
let label = "anon";
|
|
106
|
+
let mcp;
|
|
107
|
+
try {
|
|
108
|
+
const spec = parseMcpSpec(raw);
|
|
109
|
+
label = spec.name ?? "anon";
|
|
110
|
+
if (spec.name && disabledNames.has(spec.name)) {
|
|
111
|
+
process.stderr.write(`${formatMcpLifecycleEvent({ state: "disabled", name: label })}
|
|
112
|
+
`);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
process.stderr.write(`${formatMcpLifecycleEvent({ state: "handshake", name: label })}
|
|
116
|
+
`);
|
|
117
|
+
const t0 = Date.now();
|
|
118
|
+
const prefix2 = spec.name ? `${spec.name}_` : requestedSpecs.length === 1 && opts.mcpPrefix ? opts.mcpPrefix : "";
|
|
119
|
+
if (spec.transport === "stdio") preflightStdioSpec(spec);
|
|
120
|
+
const transport = buildTransportFromSpec(spec, {
|
|
121
|
+
env: mcpEnvFor(spec.name, readConfig())
|
|
122
|
+
});
|
|
123
|
+
mcp = new McpClient({ transport });
|
|
124
|
+
await mcp.initialize();
|
|
125
|
+
const bridge = await bridgeMcpTools(mcp, {
|
|
126
|
+
registry: tools,
|
|
127
|
+
namePrefix: prefix2,
|
|
128
|
+
serverName: label,
|
|
129
|
+
onSlow: (info) => process.stderr.write(
|
|
130
|
+
`${formatMcpSlowToast({ name: info.serverName, p95Ms: info.p95Ms, sampleSize: info.sampleSize })}
|
|
131
|
+
`
|
|
132
|
+
)
|
|
133
|
+
});
|
|
134
|
+
process.stderr.write(
|
|
135
|
+
`${formatMcpLifecycleEvent({
|
|
136
|
+
state: "connected",
|
|
137
|
+
name: label,
|
|
138
|
+
tools: bridge.registeredNames.length,
|
|
139
|
+
ms: Date.now() - t0
|
|
140
|
+
})}
|
|
141
|
+
`
|
|
142
|
+
);
|
|
143
|
+
clients.push(mcp);
|
|
144
|
+
successCount++;
|
|
145
|
+
} catch (err) {
|
|
146
|
+
await mcp?.close().catch(() => void 0);
|
|
147
|
+
process.stderr.write(
|
|
148
|
+
`${formatMcpLifecycleEvent({ state: "failed", name: label, reason: err.message })}
|
|
149
|
+
\u2192 run \`luckerr setup\` to remove broken entries from your saved config.
|
|
150
|
+
`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (successCount === 0) tools = void 0;
|
|
155
|
+
}
|
|
156
|
+
const client = new DeepSeekClient({ baseUrl: loadBaseUrl() });
|
|
157
|
+
const prefix = new ImmutablePrefix({
|
|
158
|
+
system: opts.system,
|
|
159
|
+
toolSpecs: tools?.specs()
|
|
160
|
+
});
|
|
161
|
+
const loop = new CacheFirstLoop({
|
|
162
|
+
client,
|
|
163
|
+
prefix,
|
|
164
|
+
tools,
|
|
165
|
+
model: opts.model,
|
|
166
|
+
budgetUsd: opts.budgetUsd,
|
|
167
|
+
failureThreshold: opts.failureThreshold
|
|
168
|
+
});
|
|
169
|
+
const prefixHash = prefix.fingerprint;
|
|
170
|
+
let transcriptStream = null;
|
|
171
|
+
if (opts.transcript) {
|
|
172
|
+
transcriptStream = openTranscriptFile(opts.transcript, {
|
|
173
|
+
version: 1,
|
|
174
|
+
source: "luckerr run",
|
|
175
|
+
model: opts.model,
|
|
176
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
177
|
+
});
|
|
178
|
+
writeRecord(transcriptStream, {
|
|
179
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
180
|
+
turn: 1,
|
|
181
|
+
role: "user",
|
|
182
|
+
content: opts.task
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
for await (const ev of loop.step(opts.task)) {
|
|
187
|
+
if (ev.role === "assistant_delta" && ev.content) process.stdout.write(ev.content);
|
|
188
|
+
if (ev.role === "tool") process.stdout.write(`
|
|
189
|
+
[tool ${ev.toolName}] ${ev.content}
|
|
190
|
+
`);
|
|
191
|
+
if (ev.role === "error") process.stderr.write(`
|
|
192
|
+
[error] ${ev.error}
|
|
193
|
+
`);
|
|
194
|
+
if (ev.role === "done") process.stdout.write("\n");
|
|
195
|
+
if (ev.role === "assistant_final" && ev.stats?.usage) {
|
|
196
|
+
appendUsage({ session: null, model: ev.stats.model, usage: ev.stats.usage });
|
|
197
|
+
}
|
|
198
|
+
if (transcriptStream && ev.role !== "assistant_delta") {
|
|
199
|
+
writeRecord(transcriptStream, recordFromLoopEvent(ev, { model: opts.model, prefixHash }));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} finally {
|
|
203
|
+
transcriptStream?.end();
|
|
204
|
+
}
|
|
205
|
+
const s = loop.stats.summary();
|
|
206
|
+
process.stdout.write(
|
|
207
|
+
`
|
|
208
|
+
\u2014 turns:${s.turns} cache:${(s.cacheHitRatio * 100).toFixed(1)}% cost:$${s.totalCostUsd.toFixed(6)} save-vs-claude:${s.savingsVsClaudePct.toFixed(1)}%
|
|
209
|
+
`
|
|
210
|
+
);
|
|
211
|
+
if (opts.transcript) {
|
|
212
|
+
process.stdout.write(`
|
|
213
|
+
transcript: ${opts.transcript}
|
|
214
|
+
`);
|
|
215
|
+
process.stdout.write(` \u2192 npx luckerr replay ${opts.transcript}
|
|
216
|
+
`);
|
|
217
|
+
}
|
|
218
|
+
for (const c of clients) await c.close();
|
|
219
|
+
}
|
|
220
|
+
export {
|
|
221
|
+
runCommand
|
|
222
|
+
};
|
|
223
|
+
//# sourceMappingURL=run-RCAC2RYW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/commands/run.ts"],"sourcesContent":["import type { WriteStream } from \"node:fs\";\nimport { stdin, stdout } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport {\n defaultConfigPath,\n isPlausibleKey,\n loadApiKey,\n loadBaseUrl,\n mcpEnvFor,\n readConfig,\n saveApiKey,\n} from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\nimport { CacheFirstLoop, DeepSeekClient, ImmutablePrefix } from \"../../index.js\";\nimport { McpClient } from \"../../mcp/client.js\";\nimport { preflightStdioSpec } from \"../../mcp/preflight.js\";\nimport { bridgeMcpTools } from \"../../mcp/registry.js\";\nimport { parseMcpSpec } from \"../../mcp/spec.js\";\nimport { buildTransportFromSpec } from \"../../mcp/transport-from-spec.js\";\nimport { appendUsage } from \"../../telemetry/usage.js\";\nimport { ToolRegistry } from \"../../tools.js\";\nimport { openTranscriptFile, recordFromLoopEvent, writeRecord } from \"../../transcript/log.js\";\nimport { formatMcpLifecycleEvent } from \"../ui/mcp-lifecycle.js\";\nimport { formatMcpSlowToast } from \"../ui/mcp-toast.js\";\n\nexport interface RunOptions {\n task: string;\n model: string;\n system: string;\n budgetUsd?: number;\n /** Per-turn repair-signal count required to escalate flash→pro. Undefined → loop default (3). */\n failureThreshold?: number;\n /** JSONL transcript path — lets `luckerr replay` / `diff` audit this run. */\n transcript?: string;\n /** Zero or more MCP server specs. Each: `\"name=cmd args...\"` or `\"cmd args...\"`. */\n mcp?: string[];\n /** Global prefix — only honored when a single anonymous server is given. */\n mcpPrefix?: string;\n}\n\nasync function ensureApiKey(): Promise<string> {\n const existing = loadApiKey();\n if (existing) return existing;\n\n if (!stdin.isTTY) {\n process.stderr.write(\n \"DEEPSEEK_API_KEY is not set and stdin is not a TTY (cannot prompt).\\n\" +\n \"Set the env var, or run `luckerr chat` once interactively to save a key.\\n\",\n );\n process.exit(1);\n }\n\n process.stdout.write(\n \"DeepSeek API key not configured.\\nGet one at https://platform.deepseek.com/api_keys\\n\",\n );\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n while (true) {\n const answer = (await rl.question(\"API key › \")).trim();\n if (!answer) continue;\n if (!isPlausibleKey(answer)) {\n process.stdout.write(\"Key looks too short. Paste the full token (16+ chars, no spaces).\\n\");\n continue;\n }\n saveApiKey(answer);\n process.stdout.write(`Saved to ${defaultConfigPath()}\\n\\n`);\n return answer;\n }\n } finally {\n rl.close();\n }\n}\n\nexport async function runCommand(opts: RunOptions): Promise<void> {\n loadDotenv();\n const apiKey = await ensureApiKey();\n process.env.DEEPSEEK_API_KEY = apiKey;\n\n // Optional MCP setup — mirrors chat's flow. Must happen before loop\n // construction so the tools make it into the prefix.\n const requestedSpecs = opts.mcp ?? [];\n const clients: McpClient[] = [];\n let tools: ToolRegistry | undefined;\n let successCount = 0;\n const disabledNames = new Set(readConfig().mcpDisabled ?? []);\n if (requestedSpecs.length > 0) {\n tools = new ToolRegistry();\n for (const raw of requestedSpecs) {\n let label = \"anon\";\n let mcp: McpClient | undefined;\n try {\n const spec = parseMcpSpec(raw);\n label = spec.name ?? \"anon\";\n if (spec.name && disabledNames.has(spec.name)) {\n process.stderr.write(`${formatMcpLifecycleEvent({ state: \"disabled\", name: label })}\\n`);\n continue;\n }\n process.stderr.write(`${formatMcpLifecycleEvent({ state: \"handshake\", name: label })}\\n`);\n const t0 = Date.now();\n const prefix = spec.name\n ? `${spec.name}_`\n : requestedSpecs.length === 1 && opts.mcpPrefix\n ? opts.mcpPrefix\n : \"\";\n if (spec.transport === \"stdio\") preflightStdioSpec(spec);\n const transport = buildTransportFromSpec(spec, {\n env: mcpEnvFor(spec.name, readConfig()),\n });\n mcp = new McpClient({ transport });\n await mcp.initialize();\n const bridge = await bridgeMcpTools(mcp, {\n registry: tools,\n namePrefix: prefix,\n serverName: label,\n onSlow: (info) =>\n process.stderr.write(\n `${formatMcpSlowToast({ name: info.serverName, p95Ms: info.p95Ms, sampleSize: info.sampleSize })}\\n`,\n ),\n });\n process.stderr.write(\n `${formatMcpLifecycleEvent({\n state: \"connected\",\n name: label,\n tools: bridge.registeredNames.length,\n ms: Date.now() - t0,\n })}\\n`,\n );\n clients.push(mcp);\n successCount++;\n } catch (err) {\n // Non-fatal — skip and continue, same as `luckerr chat`. A\n // one-shot `run` invocation with a broken MCP server otherwise\n // fails the whole run over a side-concern tool the task might\n // not even touch.\n await mcp?.close().catch(() => undefined);\n process.stderr.write(\n `${formatMcpLifecycleEvent({ state: \"failed\", name: label, reason: (err as Error).message })}\\n → run \\`luckerr setup\\` to remove broken entries from your saved config.\\n`,\n );\n }\n }\n if (successCount === 0) tools = undefined;\n }\n\n const client = new DeepSeekClient({ baseUrl: loadBaseUrl() });\n const prefix = new ImmutablePrefix({\n system: opts.system,\n toolSpecs: tools?.specs(),\n });\n const loop = new CacheFirstLoop({\n client,\n prefix,\n tools,\n model: opts.model,\n budgetUsd: opts.budgetUsd,\n failureThreshold: opts.failureThreshold,\n });\n const prefixHash = prefix.fingerprint;\n\n let transcriptStream: WriteStream | null = null;\n if (opts.transcript) {\n transcriptStream = openTranscriptFile(opts.transcript, {\n version: 1,\n source: \"luckerr run\",\n model: opts.model,\n startedAt: new Date().toISOString(),\n });\n // Also persist the user turn itself (the loop's event stream starts with\n // assistant output, not the prompt we're about to send).\n writeRecord(transcriptStream, {\n ts: new Date().toISOString(),\n turn: 1,\n role: \"user\",\n content: opts.task,\n });\n }\n\n try {\n for await (const ev of loop.step(opts.task)) {\n if (ev.role === \"assistant_delta\" && ev.content) process.stdout.write(ev.content);\n if (ev.role === \"tool\") process.stdout.write(`\\n[tool ${ev.toolName}] ${ev.content}\\n`);\n if (ev.role === \"error\") process.stderr.write(`\\n[error] ${ev.error}\\n`);\n if (ev.role === \"done\") process.stdout.write(\"\\n\");\n if (ev.role === \"assistant_final\" && ev.stats?.usage) {\n // `luckerr run` is often used in CI / scripting — we want\n // those turns to show up in `luckerr stats` too so the\n // dashboard reflects all DeepSeek spend, not just TUI sessions.\n appendUsage({ session: null, model: ev.stats.model, usage: ev.stats.usage });\n }\n // Persist every non-streaming event — deltas would flood the file and\n // aren't useful for replay (replay renders final content, not keystrokes).\n if (transcriptStream && ev.role !== \"assistant_delta\") {\n writeRecord(transcriptStream, recordFromLoopEvent(ev, { model: opts.model, prefixHash }));\n }\n }\n } finally {\n transcriptStream?.end();\n }\n\n const s = loop.stats.summary();\n process.stdout.write(\n `\\n— turns:${s.turns} cache:${(s.cacheHitRatio * 100).toFixed(1)}% ` +\n `cost:$${s.totalCostUsd.toFixed(6)} save-vs-claude:${s.savingsVsClaudePct.toFixed(1)}%\\n`,\n );\n if (opts.transcript) {\n process.stdout.write(`\\ntranscript: ${opts.transcript}\\n`);\n process.stdout.write(` → npx luckerr replay ${opts.transcript}\\n`);\n }\n\n for (const c of clients) await c.close();\n}\n"],"mappings":"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","names":["prefix"]}
|