@datename/fastcode-termux 0.1.6-beta → 0.1.8-beta.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/LICENSE +4 -0
- package/README.md +50 -17
- package/dist/agent/approval-json.d.ts +43 -0
- package/dist/agent/approval-json.js +69 -0
- package/dist/agent/approval-native.d.ts +3 -0
- package/dist/agent/approval-native.js +165 -0
- package/dist/agent/approval-store.d.ts +16 -0
- package/dist/agent/approval-store.js +21 -0
- package/dist/agent/approval.d.ts +33 -0
- package/dist/agent/approval.js +40 -5
- package/dist/agent/compaction.d.ts +2 -0
- package/dist/agent/compaction.js +60 -17
- package/dist/agent/guards.d.ts +16 -16
- package/dist/agent/guards.js +978 -24
- package/dist/agent/policy/defaults.d.ts +16 -0
- package/dist/agent/policy/defaults.js +17 -0
- package/dist/agent/policy/egress.d.ts +32 -0
- package/dist/agent/policy/egress.js +280 -0
- package/dist/agent/process-tree.d.ts +20 -0
- package/dist/agent/process-tree.js +81 -10
- package/dist/agent/protocol.d.ts +20 -1
- package/dist/agent/protocol.js +165 -38
- package/dist/agent/registry.d.ts +3 -1
- package/dist/agent/registry.js +168 -11
- package/dist/agent/runner.d.ts +21 -0
- package/dist/agent/runner.js +44 -5
- package/dist/agent/shell-ast.d.ts +11 -0
- package/dist/agent/shell-ast.js +204 -0
- package/dist/agent/shell-session.d.ts +14 -0
- package/dist/agent/shell-session.js +251 -40
- package/dist/agent/tool-schemas.js +26 -7
- package/dist/agent/tools/append_file.js +3 -1
- package/dist/agent/tools/apply_patch.js +44 -4
- package/dist/agent/tools/copy_file.js +26 -3
- package/dist/agent/tools/delete_file.js +22 -3
- package/dist/agent/tools/grep.js +35 -4
- package/dist/agent/tools/make_dir.js +3 -1
- package/dist/agent/tools/move_path.d.ts +7 -0
- package/dist/agent/tools/move_path.js +31 -5
- package/dist/agent/tools/read_file.js +63 -3
- package/dist/agent/tools/read_many.js +21 -3
- package/dist/agent/tools/readers.d.ts +53 -1
- package/dist/agent/tools/readers.js +236 -26
- package/dist/agent/tools/replace_in_file.js +38 -5
- package/dist/agent/tools/shell.js +2 -2
- package/dist/agent/tools/types.d.ts +26 -0
- package/dist/agent/tools/validate.d.ts +15 -2
- package/dist/agent/tools/validate.js +66 -3
- package/dist/agent/tools/write_file.js +3 -1
- package/dist/app.d.ts +2 -1
- package/dist/app.js +526 -85
- package/dist/cli-main.js +363 -0
- package/dist/cli.js +397 -337
- package/dist/commands/about.js +1 -1
- package/dist/commands/clear.d.ts +1 -0
- package/dist/commands/clear.js +1 -1
- package/dist/commands/compact.d.ts +12 -0
- package/dist/commands/compact.js +14 -2
- package/dist/commands/context.js +5 -4
- package/dist/commands/index.js +2 -0
- package/dist/commands/jobs.js +3 -2
- package/dist/commands/kill.js +3 -1
- package/dist/commands/login.d.ts +2 -2
- package/dist/commands/login.js +15 -15
- package/dist/commands/model.d.ts +1 -13
- package/dist/commands/model.js +6 -38
- package/dist/commands/provider.d.ts +1 -15
- package/dist/commands/provider.js +4 -22
- package/dist/commands/skills.js +34 -4
- package/dist/commands/threads.d.ts +9 -0
- package/dist/commands/threads.js +26 -0
- package/dist/config/store.d.ts +21 -0
- package/dist/config/store.js +47 -9
- package/dist/config/trust.d.ts +9 -0
- package/dist/config/trust.js +41 -2
- package/dist/headless.d.ts +7 -0
- package/dist/headless.js +213 -0
- package/dist/index.js +19 -3
- package/dist/lib/debug.d.ts +2 -0
- package/dist/lib/debug.js +22 -0
- package/dist/lib/diagnostics.d.ts +19 -0
- package/dist/lib/diagnostics.js +32 -6
- package/dist/lib/highlight-subset.d.ts +21 -0
- package/dist/lib/highlight-subset.js +88 -0
- package/dist/lib/lifecycle.d.ts +5 -0
- package/dist/lib/lifecycle.js +62 -0
- package/dist/lib/markdown.js +22 -4
- package/dist/providers/deepseek.js +1 -1
- package/dist/providers/openaiCompatible.d.ts +24 -2
- package/dist/providers/openaiCompatible.js +20 -6
- package/dist/providers/spec.d.ts +12 -0
- package/dist/providers/spec.js +29 -0
- package/dist/session/log.d.ts +14 -2
- package/dist/session/log.js +56 -27
- package/dist/session/projection.d.ts +34 -1
- package/dist/session/projection.js +315 -46
- package/dist/skills/manager.d.ts +15 -0
- package/dist/skills/manager.js +135 -18
- package/dist/types.d.ts +15 -0
- package/dist/ui/StatusBar.js +1 -1
- package/package.json +3 -2
- package/src/agent/approval-json.ts +103 -0
- package/src/agent/approval-native.ts +168 -0
- package/src/agent/approval-store.ts +38 -0
- package/src/agent/approval.ts +58 -11
- package/src/agent/compaction.ts +62 -17
- package/src/agent/guards.ts +1049 -25
- package/src/agent/policy/defaults.ts +34 -0
- package/src/agent/policy/egress.ts +335 -0
- package/src/agent/process-tree.ts +105 -23
- package/src/agent/protocol.ts +187 -39
- package/src/agent/registry.ts +186 -14
- package/src/agent/runner.ts +50 -7
- package/src/agent/shell-ast.ts +227 -0
- package/src/agent/shell-session.ts +265 -41
- package/src/agent/tool-schemas.ts +26 -7
- package/src/agent/tools/append_file.ts +3 -1
- package/src/agent/tools/apply_patch.ts +45 -4
- package/src/agent/tools/copy_file.ts +27 -3
- package/src/agent/tools/delete_file.ts +22 -3
- package/src/agent/tools/grep.ts +36 -4
- package/src/agent/tools/make_dir.ts +3 -1
- package/src/agent/tools/move_path.ts +38 -5
- package/src/agent/tools/read_file.ts +64 -3
- package/src/agent/tools/read_many.ts +21 -3
- package/src/agent/tools/readers.ts +289 -26
- package/src/agent/tools/replace_in_file.ts +39 -5
- package/src/agent/tools/shell.ts +2 -2
- package/src/agent/tools/types.ts +43 -1
- package/src/agent/tools/validate.ts +73 -4
- package/src/agent/tools/write_file.ts +3 -1
- package/src/app.ts +533 -82
- package/src/{cli.ts → cli-main.ts} +34 -6
- package/src/commands/about.ts +1 -1
- package/src/commands/clear.ts +1 -1
- package/src/commands/compact.ts +14 -2
- package/src/commands/context.ts +5 -4
- package/src/commands/index.ts +2 -0
- package/src/commands/jobs.ts +3 -2
- package/src/commands/kill.ts +3 -1
- package/src/commands/login.ts +15 -15
- package/src/commands/model.ts +6 -38
- package/src/commands/provider.ts +4 -22
- package/src/commands/skills.ts +34 -4
- package/src/commands/threads.ts +28 -0
- package/src/config/store.ts +47 -9
- package/src/config/trust.ts +45 -2
- package/src/headless.ts +225 -0
- package/src/index.ts +19 -3
- package/src/lib/debug.ts +24 -0
- package/src/lib/diagnostics.ts +32 -6
- package/src/lib/highlight-subset.ts +93 -0
- package/src/lib/lifecycle.ts +69 -0
- package/src/lib/markdown.ts +22 -4
- package/src/providers/deepseek.ts +1 -1
- package/src/providers/openaiCompatible.ts +47 -10
- package/src/providers/spec.ts +42 -0
- package/src/session/log.ts +70 -27
- package/src/session/projection.ts +360 -47
- package/src/skills/manager.ts +140 -18
- package/src/types/highlight-shims.d.ts +14 -0
- package/src/types.ts +16 -1
- package/src/ui/StatusBar.ts +1 -1
- package/tsconfig.json +1 -0
- /package/dist/{cli.d.ts → cli-main.d.ts} +0 -0
package/src/headless.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-Ink headless runner for `--ui json` / non-TTY `--ui plain`.
|
|
3
|
+
*
|
|
4
|
+
* stdout is reserved for the protocol/answer; diagnostics go to stderr.
|
|
5
|
+
*/
|
|
6
|
+
import readline from "node:readline";
|
|
7
|
+
import { AGENT_SYSTEM_PROMPT } from "./agent/protocol.js";
|
|
8
|
+
import { compactionConfigFromEnv } from "./agent/compaction.js";
|
|
9
|
+
import { resolveToolProtocol } from "./agent/protocol-mode.js";
|
|
10
|
+
import { runAgent } from "./agent/runner.js";
|
|
11
|
+
import { setShellPoolSize } from "./agent/shell-session.js";
|
|
12
|
+
import { toolSchemas } from "./agent/tool-schemas.js";
|
|
13
|
+
import { encodeGateAsk, parseGateDecision } from "./agent/approval-json.js";
|
|
14
|
+
import { decisionForOption, parseApprovalChoice, type ApprovalDecision, type ApprovalRequest } from "./agent/approval.js";
|
|
15
|
+
import { buildPluginsPrompt } from "./plugins/manager.js";
|
|
16
|
+
import { buildSkillsPrompt } from "./skills/manager.js";
|
|
17
|
+
import { loadConfig, resolveApiKey } from "./config/store.js";
|
|
18
|
+
import { getProviderOrThrow } from "./providers/index.js";
|
|
19
|
+
import { resumeSession } from "./session/projection.js";
|
|
20
|
+
|
|
21
|
+
interface HeadlessOptions {
|
|
22
|
+
prompt: string;
|
|
23
|
+
ui: "json" | "plain";
|
|
24
|
+
resume?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let eventSeq = 0;
|
|
28
|
+
|
|
29
|
+
function writeEvent(value: Record<string, any>): void {
|
|
30
|
+
eventSeq += 1;
|
|
31
|
+
process.stdout.write(JSON.stringify({ v: 1, seq: eventSeq, ts: new Date().toISOString(), ...value }) + "\n");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function optionalBudget(name: string): number | undefined {
|
|
35
|
+
const value = Number(process.env[name]);
|
|
36
|
+
return Number.isFinite(value) && value > 0 ? value : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function runHeadless(options: HeadlessOptions): Promise<void> {
|
|
40
|
+
const config = loadConfig();
|
|
41
|
+
setShellPoolSize(config.shellPoolSize);
|
|
42
|
+
const provider = getProviderOrThrow(config.providerId);
|
|
43
|
+
let apiKey: string | undefined;
|
|
44
|
+
if (provider.authEnv.length > 0) {
|
|
45
|
+
const lookup = resolveApiKey(provider.id, config.model, provider.authEnv);
|
|
46
|
+
if (lookup.status !== "ok")
|
|
47
|
+
throw new Error(`No usable API key for ${provider.id}.`);
|
|
48
|
+
apiKey = lookup.key;
|
|
49
|
+
}
|
|
50
|
+
const history = options.resume ? resumeSession(options.resume, process.cwd()).messages : [];
|
|
51
|
+
const system = [
|
|
52
|
+
config.systemPrompt,
|
|
53
|
+
buildSkillsPrompt(config.enabledSkills),
|
|
54
|
+
buildPluginsPrompt(config.enabledPlugins),
|
|
55
|
+
config.agentMode ? AGENT_SYSTEM_PROMPT : "",
|
|
56
|
+
].filter((part) => part.trim()).join("\n\n");
|
|
57
|
+
const protocol = config.agentMode ? resolveToolProtocol(config, config.providerId, config.model) : "legacy";
|
|
58
|
+
const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
59
|
+
const queued: string[] = [];
|
|
60
|
+
const waiters: Array<(line: string | undefined) => void> = [];
|
|
61
|
+
let eof = false;
|
|
62
|
+
rl.on("line", (line) => {
|
|
63
|
+
const waiter = waiters.shift();
|
|
64
|
+
if (waiter)
|
|
65
|
+
waiter(line);
|
|
66
|
+
else
|
|
67
|
+
queued.push(line);
|
|
68
|
+
});
|
|
69
|
+
rl.on("close", () => {
|
|
70
|
+
eof = true;
|
|
71
|
+
while (waiters.length > 0)
|
|
72
|
+
waiters.shift()!(undefined);
|
|
73
|
+
});
|
|
74
|
+
const nextLine = (timeoutMs: number): Promise<string | undefined> => {
|
|
75
|
+
if (queued.length > 0)
|
|
76
|
+
return Promise.resolve(queued.shift());
|
|
77
|
+
if (eof)
|
|
78
|
+
return Promise.resolve(undefined);
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
const waiter = (line: string | undefined) => {
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
resolve(line);
|
|
83
|
+
};
|
|
84
|
+
const timer = setTimeout(() => {
|
|
85
|
+
const index = waiters.indexOf(waiter);
|
|
86
|
+
if (index >= 0)
|
|
87
|
+
waiters.splice(index, 1);
|
|
88
|
+
resolve(undefined);
|
|
89
|
+
}, Math.max(1000, timeoutMs));
|
|
90
|
+
timer.unref?.();
|
|
91
|
+
waiters.push(waiter);
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const requestApproval = async (request: ApprovalRequest): Promise<ApprovalDecision> => {
|
|
96
|
+
if (options.ui === "json") {
|
|
97
|
+
writeEvent(encodeGateAsk(request));
|
|
98
|
+
const line = await nextLine(request.timeoutMs ?? 60_000);
|
|
99
|
+
if (line === undefined)
|
|
100
|
+
return { kind: "deny", reason: "approval timeout/EOF" };
|
|
101
|
+
const parsed = parseGateDecision(request, line);
|
|
102
|
+
if (parsed.error) {
|
|
103
|
+
writeEvent({ event: "gate:error", version: 1, id: request.id, error: parsed.error });
|
|
104
|
+
return parsed.decision;
|
|
105
|
+
}
|
|
106
|
+
return parsed.decision;
|
|
107
|
+
}
|
|
108
|
+
process.stderr.write(`${request.prompt ?? "Approval required"}\n`);
|
|
109
|
+
request.options.forEach((option, index) => process.stderr.write(`${index + 1}. ${option.label}\n`));
|
|
110
|
+
process.stderr.write(`Choose 1-${request.options.length}: `);
|
|
111
|
+
const line = await nextLine(request.timeoutMs ?? 60_000);
|
|
112
|
+
const option = line === undefined ? undefined : parseApprovalChoice(request, line);
|
|
113
|
+
if (!option)
|
|
114
|
+
return { kind: "deny", reason: "approval cancelled/invalid/timeout" };
|
|
115
|
+
if (option.kind === "feedback") {
|
|
116
|
+
process.stderr.write(`${option.prompt || "Why should this be denied?"}\n> `);
|
|
117
|
+
const feedbackLine = await nextLine(request.timeoutMs ?? 60_000);
|
|
118
|
+
const feedback = feedbackLine?.trim();
|
|
119
|
+
return feedback
|
|
120
|
+
? { kind: "feedback", text: feedback }
|
|
121
|
+
: { kind: "deny", reason: "feedback cancelled/empty (fail closed)" };
|
|
122
|
+
}
|
|
123
|
+
return decisionForOption(option);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const abort = new AbortController();
|
|
127
|
+
const onSigint = () => abort.abort();
|
|
128
|
+
const onTerminate = () => abort.abort();
|
|
129
|
+
process.once("SIGINT", onSigint);
|
|
130
|
+
process.once("SIGTERM", onTerminate);
|
|
131
|
+
process.once("SIGHUP", onTerminate);
|
|
132
|
+
try {
|
|
133
|
+
for await (const event of runAgent({
|
|
134
|
+
provider,
|
|
135
|
+
apiKey,
|
|
136
|
+
model: config.model,
|
|
137
|
+
messages: history,
|
|
138
|
+
prompt: options.prompt,
|
|
139
|
+
system,
|
|
140
|
+
maxTurns: config.agentMode ? Math.max(1, Math.floor(Number(config.maxAgentTurns) || 24)) : 1,
|
|
141
|
+
allowTools: config.agentMode,
|
|
142
|
+
tokenBudget: optionalBudget("FASTCODE_TOKEN_BUDGET"),
|
|
143
|
+
costBudgetUsd: optionalBudget("FASTCODE_COST_BUDGET_USD"),
|
|
144
|
+
signal: abort.signal,
|
|
145
|
+
cwd: process.cwd(),
|
|
146
|
+
idleTimeoutMs: config.requestIdleTimeoutMs,
|
|
147
|
+
compaction: compactionConfigFromEnv(),
|
|
148
|
+
reasoningEffort: config.reasoningEffort,
|
|
149
|
+
protocol,
|
|
150
|
+
tools: protocol === "native" ? toolSchemas() : undefined,
|
|
151
|
+
requestApproval,
|
|
152
|
+
})) {
|
|
153
|
+
if (options.ui === "json") {
|
|
154
|
+
if (event.type === "assistant_text")
|
|
155
|
+
writeEvent({ event: "assistant", text: event.text });
|
|
156
|
+
else if (event.type === "tool_call")
|
|
157
|
+
writeEvent({
|
|
158
|
+
event: "tool_call",
|
|
159
|
+
id: event.id,
|
|
160
|
+
tool: event.call.tool,
|
|
161
|
+
args: event.call,
|
|
162
|
+
...(event.native ? { native: true } : {}),
|
|
163
|
+
});
|
|
164
|
+
else if (event.type === "egress_denied")
|
|
165
|
+
writeEvent({
|
|
166
|
+
event: "egress_denied",
|
|
167
|
+
outlet: event.outlet,
|
|
168
|
+
...(event.target ? { target: event.target } : {}),
|
|
169
|
+
rule: event.rule,
|
|
170
|
+
mode: event.mode,
|
|
171
|
+
would_block: event.wouldBlock,
|
|
172
|
+
code: event.code,
|
|
173
|
+
reason: event.reason,
|
|
174
|
+
});
|
|
175
|
+
else if (event.type === "tool_result")
|
|
176
|
+
writeEvent({
|
|
177
|
+
event: "tool_result",
|
|
178
|
+
id: event.id,
|
|
179
|
+
tool: event.call.tool,
|
|
180
|
+
ok: event.ok,
|
|
181
|
+
output: event.output,
|
|
182
|
+
...(event.sideEffects ? { side_effects: event.sideEffects } : {}),
|
|
183
|
+
...(event.code ? { code: event.code } : {}),
|
|
184
|
+
...(event.hint ? { hint: event.hint } : {}),
|
|
185
|
+
...(event.truncated !== undefined ? { truncated: event.truncated } : {}),
|
|
186
|
+
...(event.totalLines !== undefined ? { total_lines: event.totalLines } : {}),
|
|
187
|
+
...(event.returnedLines !== undefined ? { returned_lines: event.returnedLines } : {}),
|
|
188
|
+
...(event.totalBytes !== undefined ? { total_bytes: event.totalBytes } : {}),
|
|
189
|
+
...(event.returnedBytes !== undefined ? { returned_bytes: event.returnedBytes } : {}),
|
|
190
|
+
...(event.filesRead !== undefined ? { files_read: event.filesRead } : {}),
|
|
191
|
+
...(event.filesSkipped !== undefined ? { files_skipped: event.filesSkipped } : {}),
|
|
192
|
+
...(event.matches !== undefined ? { matches: event.matches } : {}),
|
|
193
|
+
});
|
|
194
|
+
else if (event.type === "usage")
|
|
195
|
+
writeEvent({
|
|
196
|
+
event: "usage",
|
|
197
|
+
input_tokens: event.usage.inputTokens ?? 0,
|
|
198
|
+
output_tokens: event.usage.outputTokens ?? 0,
|
|
199
|
+
cost_usd: event.usage.costUsd ?? 0,
|
|
200
|
+
});
|
|
201
|
+
else if (event.type === "notice")
|
|
202
|
+
writeEvent({ event: "notice", text: event.text });
|
|
203
|
+
else if (event.type === "error")
|
|
204
|
+
writeEvent({ event: "error", kind: event.kind, code: event.kind, message: event.message });
|
|
205
|
+
else if (event.type === "done")
|
|
206
|
+
writeEvent({ event: "done", stopReason: event.stopReason, finalText: event.finalText });
|
|
207
|
+
}
|
|
208
|
+
else if (event.type === "assistant_text") {
|
|
209
|
+
process.stdout.write(event.text);
|
|
210
|
+
}
|
|
211
|
+
else if (event.type === "notice") {
|
|
212
|
+
process.stderr.write(`[fastcode] ${event.text}\n`);
|
|
213
|
+
}
|
|
214
|
+
else if (event.type === "done" && event.finalText) {
|
|
215
|
+
process.stdout.write(event.finalText + "\n");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
process.removeListener("SIGINT", onSigint);
|
|
221
|
+
process.removeListener("SIGTERM", onTerminate);
|
|
222
|
+
process.removeListener("SIGHUP", onTerminate);
|
|
223
|
+
rl.close();
|
|
224
|
+
}
|
|
225
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,13 +5,29 @@
|
|
|
5
5
|
* Ink-based TUI. We keep this file tiny so the slow imports (Ink, React)
|
|
6
6
|
* are not loaded for `fastcode skills list`.
|
|
7
7
|
*/
|
|
8
|
-
import { parseCli } from "./cli.js";
|
|
8
|
+
import { parseCli } from "./cli-main.js";
|
|
9
|
+
import { installProcessGuards } from "./lib/lifecycle.js";
|
|
9
10
|
async function main() {
|
|
11
|
+
installProcessGuards();
|
|
10
12
|
const cli = await parseCli();
|
|
11
13
|
if (cli.done)
|
|
12
14
|
return;
|
|
15
|
+
if (cli.ui === "json") {
|
|
16
|
+
if (!cli.prompt) {
|
|
17
|
+
console.error("--ui json requires a prompt: fastcode --ui json \"<prompt>\"");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const { runHeadless } = await import("./headless.js");
|
|
21
|
+
await runHeadless({ prompt: cli.prompt, ui: "json", resume: cli.resume });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
13
24
|
if (!process.stdout.isTTY) {
|
|
14
|
-
|
|
25
|
+
if (cli.ui === "plain" && cli.prompt) {
|
|
26
|
+
const { runHeadless } = await import("./headless.js");
|
|
27
|
+
await runHeadless({ prompt: cli.prompt, ui: "plain", resume: cli.resume });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.error("FastCode TUI requires a TTY. Use `fastcode --ui plain|json \"<prompt>\"` for pipe mode.");
|
|
15
31
|
process.exit(1);
|
|
16
32
|
}
|
|
17
33
|
// Lazy-load Ink so subcommands don't pay the React boot cost.
|
|
@@ -23,7 +39,7 @@ async function main() {
|
|
|
23
39
|
// React.createElement avoids needing a separate .tsx loader for this file.
|
|
24
40
|
// exitOnCtrlC: false lets the app own Ctrl+C so the documented
|
|
25
41
|
// "press Ctrl+C twice to quit" behavior actually works.
|
|
26
|
-
render(React.createElement(App, { initialPrompt: cli.prompt, resume: cli.resume }), { exitOnCtrlC: false });
|
|
42
|
+
render(React.createElement(App, { initialPrompt: cli.prompt, resume: cli.resume, ui: cli.ui ?? "tty" }), { exitOnCtrlC: false });
|
|
27
43
|
}
|
|
28
44
|
main().catch((err) => {
|
|
29
45
|
console.error(err);
|
package/src/lib/debug.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal debug logging.
|
|
3
|
+
*
|
|
4
|
+
* There is no logging framework in this project: normal output goes through
|
|
5
|
+
* Ink and lifecycle warnings go straight to stderr. `FASTCODE_DEBUG=1` opts
|
|
6
|
+
* into low-volume stderr diagnostics (for example timeout clamping or config
|
|
7
|
+
* cache invalidation) without changing the default experience.
|
|
8
|
+
*/
|
|
9
|
+
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
10
|
+
|
|
11
|
+
export function debugEnabled(): boolean {
|
|
12
|
+
return TRUTHY.has((process.env["FASTCODE_DEBUG"] ?? "").trim().toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function debugLog(scope: string, message: string): void {
|
|
16
|
+
if (!debugEnabled())
|
|
17
|
+
return;
|
|
18
|
+
try {
|
|
19
|
+
process.stderr.write(`[fastcode:${scope}] ${message}\n`);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Debug logging must never break the operation it observes.
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/lib/diagnostics.ts
CHANGED
|
@@ -11,14 +11,17 @@ import { detectPlatform, platformLabel } from "./platform.js";
|
|
|
11
11
|
import { readPackageMeta } from "./pkg.js";
|
|
12
12
|
import { shouldUseStableTerminalUi } from "./uiMode.js";
|
|
13
13
|
import { getPaths } from "../config/paths.js";
|
|
14
|
-
import { loadConfig, resolveApiKey, resolveProviderKey } from "../config/store.js";
|
|
14
|
+
import { configCacheStatus, loadConfig, resolveApiKey, resolveProviderKey } from "../config/store.js";
|
|
15
|
+
import { debugEnabled } from "./debug.js";
|
|
15
16
|
import { PROVIDERS } from "../providers/index.js";
|
|
16
|
-
import { listInstalledSkills } from "../skills/manager.js";
|
|
17
|
+
import { listInstalledSkills, resolveRegistry } from "../skills/manager.js";
|
|
17
18
|
import { listInstalledPlugins } from "../plugins/manager.js";
|
|
18
19
|
import { findUnclosedSessions, fsyncMode, loggingEnabled, listSessions, sessionCacheLimitBytes } from "../session/log.js";
|
|
19
20
|
import { isTrusted } from "../config/trust.js";
|
|
20
21
|
import { DEFAULT_DENY_PATTERNS, loopExemptTools } from "../agent/guards.js";
|
|
21
22
|
import { toolOutputLimit } from "../agent/limits.js";
|
|
23
|
+
import { grepMaxBytes, readManyMaxBytes, readMaxBytes, readScanMaxBytes } from "../agent/tools/readers.js";
|
|
24
|
+
import { normalizeEgressConfig } from "../agent/policy/egress.js";
|
|
22
25
|
import { compactionConfigFromEnv, heapExceeded } from "../agent/compaction.js";
|
|
23
26
|
import { shellPoolStats } from "../agent/shell-session.js";
|
|
24
27
|
import { resolveToolProtocol, toolCapabilityKey } from "../agent/protocol-mode.js";
|
|
@@ -34,6 +37,7 @@ function nodeIsSupported() {
|
|
|
34
37
|
}
|
|
35
38
|
export function collectDiagnostics() {
|
|
36
39
|
const config = loadConfig();
|
|
40
|
+
const egress = normalizeEgressConfig(config.egress);
|
|
37
41
|
let paths;
|
|
38
42
|
try {
|
|
39
43
|
paths = getPaths();
|
|
@@ -95,6 +99,9 @@ export function collectDiagnostics() {
|
|
|
95
99
|
keyStatus: provider ? keyLookup.status : "missing",
|
|
96
100
|
keyEnv: provider?.authEnv?.[0],
|
|
97
101
|
skillsInstalled,
|
|
102
|
+
skillsRegistry: resolveRegistry(process.env["FASTCODE_SKILLS_REGISTRY"], config.skillsRegistry),
|
|
103
|
+
debug: debugEnabled(),
|
|
104
|
+
configCache: configCacheStatus().cached,
|
|
98
105
|
pluginsInstalled,
|
|
99
106
|
enabledSkills: (config.enabledSkills ?? []).length,
|
|
100
107
|
enabledPlugins: (config.enabledPlugins ?? []).length,
|
|
@@ -110,16 +117,32 @@ export function collectDiagnostics() {
|
|
|
110
117
|
shellDenyPatterns: DEFAULT_DENY_PATTERNS.length,
|
|
111
118
|
allowDangerous: ["1", "true", "yes", "on"].includes((process.env["FASTCODE_ALLOW_DANGEROUS"] ?? "").trim().toLowerCase()),
|
|
112
119
|
toolOutputLimit: toolOutputLimit(),
|
|
120
|
+
readMaxBytes: readMaxBytes(),
|
|
121
|
+
readScanMaxBytes: readScanMaxBytes(),
|
|
122
|
+
readManyMaxBytes: readManyMaxBytes(),
|
|
123
|
+
grepMaxBytes: grepMaxBytes(),
|
|
124
|
+
egress: {
|
|
125
|
+
enabled: egress.enabled,
|
|
126
|
+
mode: egress.mode,
|
|
127
|
+
maxScanBytes: egress.maxScanBytes,
|
|
128
|
+
denyPaths: egress.denyPaths.length,
|
|
129
|
+
denyGlobs: egress.denyGlobs.length,
|
|
130
|
+
denyContentMarkers: egress.denyContentMarkers.length,
|
|
131
|
+
scanToolOutput: egress.scanToolOutput,
|
|
132
|
+
},
|
|
113
133
|
tokenBudget: positiveEnv("FASTCODE_TOKEN_BUDGET"),
|
|
114
134
|
costBudgetUsd: positiveEnv("FASTCODE_COST_BUDGET_USD"),
|
|
115
135
|
compactionEnabled: compaction.enabled,
|
|
116
136
|
compactionRatio: compaction.localThresholdRatio,
|
|
137
|
+
compactionKeepTurns: compaction.keepTurns,
|
|
138
|
+
compactionSummaryTokens: compaction.maxSummaryTokens,
|
|
117
139
|
compactionHeapMb: compaction.heapLimitMb,
|
|
118
140
|
compactionHeapUsedMb: process.memoryUsage().heapUsed / 1024 / 1024,
|
|
119
141
|
compactionForced: heapExceeded(compaction),
|
|
120
142
|
serverCompactionAware: compaction.serverCompactionAware,
|
|
121
143
|
shellActive: shells.active,
|
|
122
144
|
shellPool: shells.pool,
|
|
145
|
+
shellPoolMax: shells.maxPool,
|
|
123
146
|
shellSpawns: shells.spawns,
|
|
124
147
|
shellMaxSpawn: shells.maxSpawnPerTurn,
|
|
125
148
|
sseIdleMs: positiveEnv("FASTCODE_SSE_IDLE_MS") || 25_000,
|
|
@@ -157,16 +180,19 @@ export function formatDiagnostics(info) {
|
|
|
157
180
|
lines.push(" trusted cwd " + (info.trusted ? "yes" : "NO - agent tools ask first or stay off") + " (" + (info.trustFile ?? "?") + ")");
|
|
158
181
|
lines.push(" session log " + (info.sessionLogEnabled ? "on" : "off") + " cache cap " + info.sessionCacheMb + "MB, " + info.sessionsCount + " file(s), " + (info.sessionsBytes / 1024 / 1024).toFixed(2) + "MB used");
|
|
159
182
|
lines.push(" session fsync " + info.sessionFsync + " unclosed " + info.unclosedSessions + " " + (info.sessionsDir ?? "(unavailable)"));
|
|
160
|
-
lines.push(" shell guard " + info.shellDenyPatterns + "
|
|
183
|
+
lines.push(" shell guard " + info.shellDenyPatterns + " legacy soft patterns" + (info.allowDangerous ? " [FASTCODE_ALLOW_DANGEROUS is ON]" : ""));
|
|
161
184
|
lines.push(" tool output " + info.toolOutputLimit + " chars max (streaming cap)");
|
|
185
|
+
lines.push(` read limits read ${info.readMaxBytes}B · scan ${info.readScanMaxBytes}B · read_many ${info.readManyMaxBytes}B · grep ${info.grepMaxBytes}B`);
|
|
186
|
+
lines.push(` egress ${info.egress.enabled ? "enforce" : "audit-only"} · mode ${info.egress.mode} · scan ${info.egress.maxScanBytes}B · paths ${info.egress.denyPaths} · globs ${info.egress.denyGlobs} · markers ${info.egress.denyContentMarkers}`);
|
|
162
187
|
lines.push(` budgets ${info.tokenBudget > 0 ? "tokens ≤ " + info.tokenBudget : "token budget off"} · ${info.costBudgetUsd > 0 ? "$" + info.costBudgetUsd : "cost budget off"}`);
|
|
163
|
-
lines.push(` compaction ${info.compactionEnabled ? "on" : "off"} · ${Math.round(info.compactionRatio * 100)}% · heap ${info.compactionHeapUsedMb.toFixed(1)}/${info.compactionHeapMb}MB${info.compactionForced ? " [FORCED]" : ""}${info.serverCompactionAware ? " · server-aware" : ""}`);
|
|
164
|
-
lines.push(` shell pool ${info.shellActive} active · ${info.shellPool} resident · ${info.shellSpawns} spawns · cap ${info.shellMaxSpawn}/turn`);
|
|
188
|
+
lines.push(` compaction ${info.compactionEnabled ? "on" : "off"} · ${Math.round(info.compactionRatio * 100)}% · keep ${info.compactionKeepTurns} turns · summary ${info.compactionSummaryTokens} tok · heap ${info.compactionHeapUsedMb.toFixed(1)}/${info.compactionHeapMb}MB${info.compactionForced ? " [FORCED]" : ""}${info.serverCompactionAware ? " · server-aware" : ""}`);
|
|
189
|
+
lines.push(` shell pool ${info.shellActive} active · ${info.shellPool}/${info.shellPoolMax} resident · ${info.shellSpawns} spawns · cap ${info.shellMaxSpawn}/turn`);
|
|
165
190
|
lines.push(` loop exempt ${info.loopExempt.length > 0 ? info.loopExempt.join(", ") : "(none)"}`);
|
|
166
191
|
lines.push(` model meta ctx ${info.modelMeta.contextWindow} · ~${info.modelMeta.charsPerToken} chars/token · ${info.modelMeta.supportsTools ? "tools" : "no tools"} · ${info.modelMetaFile}`);
|
|
167
192
|
lines.push(` sse timeout ${info.sseIdleMs}ms idle`);
|
|
168
193
|
lines.push(` tool protocol ${info.toolProtocol} (${info.toolProtocolMode}) · cached ${info.toolCapability}`);
|
|
169
194
|
lines.push(` provider ${info.providerId} · model ${info.model}`);
|
|
195
|
+
lines.push(` config cache ${info.configCache ? "warm" : "cold"} · debug ${info.debug ? "on" : "off"}`);
|
|
170
196
|
let apiKeyStatus;
|
|
171
197
|
if (info.keyPresent)
|
|
172
198
|
apiKeyStatus = "present";
|
|
@@ -175,7 +201,7 @@ export function formatDiagnostics(info) {
|
|
|
175
201
|
else
|
|
176
202
|
apiKeyStatus = `MISSING — set ${info.keyEnv ?? "the provider env var"} or /login ${info.providerId} ${info.model} <key>`;
|
|
177
203
|
lines.push(` api key ${apiKeyStatus}`);
|
|
178
|
-
lines.push(` skills ${info.skillsInstalled} installed, ${info.enabledSkills} enabled`);
|
|
204
|
+
lines.push(` skills ${info.skillsInstalled} installed, ${info.enabledSkills} enabled (${info.skillsRegistry || "no registry configured"})`);
|
|
179
205
|
lines.push(` plugins ${info.pluginsInstalled} installed, ${info.enabledPlugins} enabled`);
|
|
180
206
|
lines.push("");
|
|
181
207
|
lines.push(" provider keys:");
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trimmed highlight.js registry.
|
|
3
|
+
*
|
|
4
|
+
* cli-highlight imports the full `highlight.js` package, which registers every
|
|
5
|
+
* language and adds roughly a megabyte of language definitions to the bundle
|
|
6
|
+
* for a TUI that mostly renders JS/TS/JSON/sh/Python. The esbuild plugin in
|
|
7
|
+
* scripts/esbuild-plugins.mjs aliases the bare `highlight.js` import to this
|
|
8
|
+
* module so only the common set below is bundled.
|
|
9
|
+
*
|
|
10
|
+
* Unknown languages still work: cli-highlight throws, marked-terminal catches
|
|
11
|
+
* and renders the fence as plain styled text. `renderMarkdown` adds a short
|
|
12
|
+
* dimmed hint instead of failing or blanking the message.
|
|
13
|
+
*/
|
|
14
|
+
import hljs from "highlight.js/lib/core.js";
|
|
15
|
+
import javascript from "highlight.js/lib/languages/javascript.js";
|
|
16
|
+
import typescript from "highlight.js/lib/languages/typescript.js";
|
|
17
|
+
import json from "highlight.js/lib/languages/json.js";
|
|
18
|
+
import bash from "highlight.js/lib/languages/bash.js";
|
|
19
|
+
import python from "highlight.js/lib/languages/python.js";
|
|
20
|
+
import go from "highlight.js/lib/languages/go.js";
|
|
21
|
+
import rust from "highlight.js/lib/languages/rust.js";
|
|
22
|
+
import java from "highlight.js/lib/languages/java.js";
|
|
23
|
+
import c from "highlight.js/lib/languages/c.js";
|
|
24
|
+
import cpp from "highlight.js/lib/languages/cpp.js";
|
|
25
|
+
import csharp from "highlight.js/lib/languages/csharp.js";
|
|
26
|
+
import css from "highlight.js/lib/languages/css.js";
|
|
27
|
+
import xml from "highlight.js/lib/languages/xml.js";
|
|
28
|
+
import yaml from "highlight.js/lib/languages/yaml.js";
|
|
29
|
+
import markdown from "highlight.js/lib/languages/markdown.js";
|
|
30
|
+
import sql from "highlight.js/lib/languages/sql.js";
|
|
31
|
+
import diff from "highlight.js/lib/languages/diff.js";
|
|
32
|
+
import dockerfile from "highlight.js/lib/languages/dockerfile.js";
|
|
33
|
+
import powershell from "highlight.js/lib/languages/powershell.js";
|
|
34
|
+
import kotlin from "highlight.js/lib/languages/kotlin.js";
|
|
35
|
+
import swift from "highlight.js/lib/languages/swift.js";
|
|
36
|
+
import ruby from "highlight.js/lib/languages/ruby.js";
|
|
37
|
+
import php from "highlight.js/lib/languages/php.js";
|
|
38
|
+
import lua from "highlight.js/lib/languages/lua.js";
|
|
39
|
+
import r from "highlight.js/lib/languages/r.js";
|
|
40
|
+
import scala from "highlight.js/lib/languages/scala.js";
|
|
41
|
+
import ini from "highlight.js/lib/languages/ini.js";
|
|
42
|
+
import makefile from "highlight.js/lib/languages/makefile.js";
|
|
43
|
+
import plaintext from "highlight.js/lib/languages/plaintext.js";
|
|
44
|
+
|
|
45
|
+
const LANGUAGES: Record<string, any> = {
|
|
46
|
+
javascript,
|
|
47
|
+
typescript,
|
|
48
|
+
json,
|
|
49
|
+
bash,
|
|
50
|
+
python,
|
|
51
|
+
go,
|
|
52
|
+
rust,
|
|
53
|
+
java,
|
|
54
|
+
c,
|
|
55
|
+
cpp,
|
|
56
|
+
csharp,
|
|
57
|
+
css,
|
|
58
|
+
xml,
|
|
59
|
+
yaml,
|
|
60
|
+
markdown,
|
|
61
|
+
sql,
|
|
62
|
+
diff,
|
|
63
|
+
dockerfile,
|
|
64
|
+
powershell,
|
|
65
|
+
kotlin,
|
|
66
|
+
swift,
|
|
67
|
+
ruby,
|
|
68
|
+
php,
|
|
69
|
+
lua,
|
|
70
|
+
r,
|
|
71
|
+
scala,
|
|
72
|
+
ini,
|
|
73
|
+
makefile,
|
|
74
|
+
plaintext,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
for (const [name, definition] of Object.entries(LANGUAGES))
|
|
78
|
+
hljs.registerLanguage(name, definition);
|
|
79
|
+
|
|
80
|
+
export const COMMON_HIGHLIGHT_LANGUAGES = Object.keys(LANGUAGES);
|
|
81
|
+
|
|
82
|
+
export function supportsHighlightLanguage(language: string | undefined): boolean {
|
|
83
|
+
if (!language)
|
|
84
|
+
return true;
|
|
85
|
+
const name = language.trim().toLowerCase().split(/\s+/)[0] ?? "";
|
|
86
|
+
return Boolean(name) && Boolean(hljs.getLanguage(name));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const highlight = hljs.highlight.bind(hljs);
|
|
90
|
+
export const highlightAuto = hljs.highlightAuto.bind(hljs);
|
|
91
|
+
export const listLanguages = hljs.listLanguages.bind(hljs);
|
|
92
|
+
export const getLanguage = hljs.getLanguage.bind(hljs);
|
|
93
|
+
export default hljs;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-level shutdown guards.
|
|
3
|
+
*
|
|
4
|
+
* The TUI already calls shutdownShells() from its React cleanup, but fatal
|
|
5
|
+
* errors and SIGTERM/SIGHUP can bypass that path. These guards are best-effort
|
|
6
|
+
* and idempotent; they do not turn FastCode into a kernel-level sandbox.
|
|
7
|
+
*/
|
|
8
|
+
import { shutdownShells } from "../agent/shell-session.js";
|
|
9
|
+
|
|
10
|
+
let installed = false;
|
|
11
|
+
let cleaned = false;
|
|
12
|
+
|
|
13
|
+
export function cleanupProcesses(): void {
|
|
14
|
+
if (cleaned)
|
|
15
|
+
return;
|
|
16
|
+
cleaned = true;
|
|
17
|
+
try {
|
|
18
|
+
shutdownShells();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Best effort: never mask the original failure.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ProcessGuardOptions {
|
|
26
|
+
onFatal?: (error: unknown) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function installProcessGuards(options: ProcessGuardOptions = {}): () => void {
|
|
30
|
+
if (installed)
|
|
31
|
+
return () => undefined;
|
|
32
|
+
installed = true;
|
|
33
|
+
const onSignal = (signal: NodeJS.Signals) => {
|
|
34
|
+
process.stderr.write(`[fastcode] ${signal} received; shutting down.\n`);
|
|
35
|
+
cleanupProcesses();
|
|
36
|
+
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
37
|
+
};
|
|
38
|
+
const onFatal = (error: unknown) => {
|
|
39
|
+
const detail = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
40
|
+
process.stderr.write(`[fastcode] fatal: ${detail}\n`);
|
|
41
|
+
cleanupProcesses();
|
|
42
|
+
options.onFatal?.(error);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
};
|
|
45
|
+
const signals: NodeJS.Signals[] = ["SIGTERM", "SIGHUP"];
|
|
46
|
+
for (const signal of signals) {
|
|
47
|
+
try {
|
|
48
|
+
process.on(signal, onSignal);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Some platforms do not expose every signal.
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
process.on("uncaughtException", onFatal);
|
|
55
|
+
process.on("unhandledRejection", onFatal);
|
|
56
|
+
return () => {
|
|
57
|
+
for (const signal of signals) {
|
|
58
|
+
try {
|
|
59
|
+
process.removeListener(signal, onSignal);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Ignore unsupported signals.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
process.removeListener("uncaughtException", onFatal);
|
|
66
|
+
process.removeListener("unhandledRejection", onFatal);
|
|
67
|
+
installed = false;
|
|
68
|
+
};
|
|
69
|
+
}
|
package/src/lib/markdown.ts
CHANGED
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
* markdown parser. The result is a fully-formatted ANSI string we can
|
|
7
7
|
* print directly inside an Ink <Text>.
|
|
8
8
|
*/
|
|
9
|
+
import chalk from "chalk";
|
|
9
10
|
import { marked } from "marked";
|
|
10
11
|
import { markedTerminal } from "marked-terminal";
|
|
12
|
+
import { supportsHighlightLanguage } from "./highlight-subset.js";
|
|
11
13
|
let configured = false;
|
|
12
14
|
function configure() {
|
|
13
15
|
if (configured)
|
|
@@ -15,16 +17,32 @@ function configure() {
|
|
|
15
17
|
marked.use(markedTerminal({
|
|
16
18
|
reflowText: false,
|
|
17
19
|
tab: 2,
|
|
20
|
+
// marked-terminal calls this only when highlighting fails; keep the
|
|
21
|
+
// cold-language fallback as plain text instead of colored source.
|
|
22
|
+
code: (text: string) => text,
|
|
18
23
|
}) as any);
|
|
19
24
|
configured = true;
|
|
20
25
|
}
|
|
26
|
+
function unsupportedFenceLanguages(md: string): string[] {
|
|
27
|
+
const missing = new Set<string>();
|
|
28
|
+
for (const match of md.matchAll(/(?:^|\n)\s*(?:`{3,}|~{3,})([^\n]*)/g)) {
|
|
29
|
+
const info = (match[1] ?? "").trim();
|
|
30
|
+
if (!info)
|
|
31
|
+
continue;
|
|
32
|
+
const language = info.split(/\s+/)[0] ?? "";
|
|
33
|
+
if (language && !supportsHighlightLanguage(language))
|
|
34
|
+
missing.add(language);
|
|
35
|
+
}
|
|
36
|
+
return Array.from(missing);
|
|
37
|
+
}
|
|
21
38
|
export function renderMarkdown(md) {
|
|
22
39
|
configure();
|
|
23
40
|
try {
|
|
24
|
-
const out = marked.parse(md) as string;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
const out = (marked.parse(md) as string).replace(/\s+$/, "");
|
|
42
|
+
const missing = unsupportedFenceLanguages(String(md));
|
|
43
|
+
if (missing.length === 0)
|
|
44
|
+
return out;
|
|
45
|
+
return out + "\n\n" + chalk.dim(`[fastcode] no syntax highlighting for ${missing.join(", ")}; rendered as plain text.`);
|
|
28
46
|
}
|
|
29
47
|
catch {
|
|
30
48
|
return md;
|
|
@@ -9,7 +9,7 @@ export const deepseekProvider = {
|
|
|
9
9
|
label: "DeepSeek",
|
|
10
10
|
authEnv: ["DEEPSEEK_API_KEY"],
|
|
11
11
|
baseUrl: BASE,
|
|
12
|
-
defaultModel: "deepseek-
|
|
12
|
+
defaultModel: "deepseek-flash",
|
|
13
13
|
listModels: (apiKey) => listModelsHttp({ baseUrl: BASE, apiKey, staticModels: STATIC }),
|
|
14
14
|
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: BASE, apiKey, staticModels: STATIC, reasoning: "openai" }),
|
|
15
15
|
supportsReasoning: () => true,
|