@datename/fastcode-termux 0.1.6-beta → 0.1.6
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 +6 -24
- package/dist/agent/guards.d.ts +0 -8
- package/dist/agent/guards.js +0 -195
- package/dist/agent/protocol.js +1 -4
- package/dist/agent/tools.d.ts +1 -3
- package/dist/agent/tools.js +377 -2
- package/dist/app.js +103 -335
- package/dist/cli.js +311 -362
- package/dist/commands/index.js +0 -22
- package/dist/commands/login.d.ts +2 -4
- package/dist/commands/login.js +10 -12
- package/dist/commands/logout.js +16 -8
- package/dist/commands/model.d.ts +0 -13
- package/dist/commands/model.js +22 -42
- package/dist/commands/provider.d.ts +0 -13
- package/dist/commands/provider.js +9 -20
- package/dist/commands/whoami.js +5 -6
- package/dist/config/store.d.ts +12 -32
- package/dist/config/store.js +65 -117
- package/dist/config/trust.d.ts +8 -21
- package/dist/config/trust.js +13 -99
- package/dist/lib/diagnostics.d.ts +4 -29
- package/dist/lib/diagnostics.js +4 -60
- package/dist/lib/http.d.ts +1 -11
- package/dist/lib/http.js +4 -62
- package/dist/providers/anthropic.d.ts +0 -1
- package/dist/providers/anthropic.js +0 -12
- package/dist/providers/custom.d.ts +0 -9
- package/dist/providers/deepseek.d.ts +0 -10
- package/dist/providers/deepseek.js +1 -2
- package/dist/providers/google.d.ts +0 -9
- package/dist/providers/groq.d.ts +0 -10
- package/dist/providers/groq.js +1 -2
- package/dist/providers/index.d.ts +201 -10
- package/dist/providers/index.js +0 -30
- package/dist/providers/mistral.d.ts +0 -10
- package/dist/providers/mistral.js +1 -2
- package/dist/providers/moonshot.d.ts +0 -10
- package/dist/providers/moonshot.js +1 -2
- package/dist/providers/ollama.d.ts +0 -9
- package/dist/providers/openai.d.ts +0 -10
- package/dist/providers/openai.js +1 -2
- package/dist/providers/openaiCompatible.d.ts +0 -9
- package/dist/providers/openaiCompatible.js +10 -58
- package/dist/providers/openrouter.d.ts +0 -10
- package/dist/providers/openrouter.js +1 -2
- package/dist/providers/qwen.d.ts +0 -10
- package/dist/providers/qwen.js +1 -2
- package/dist/providers/xai.d.ts +0 -10
- package/dist/providers/xai.js +1 -2
- package/dist/providers/zhipu.d.ts +0 -10
- package/dist/providers/zhipu.js +1 -2
- package/dist/session/log.d.ts +12 -35
- package/dist/session/log.js +10 -206
- package/dist/types.d.ts +1 -60
- package/dist/ui/Message.d.ts +7 -1
- package/dist/ui/Message.js +2 -13
- package/dist/ui/Prompt.d.ts +2 -1
- package/dist/ui/Prompt.js +2 -2
- package/dist/ui/Spinner.js +5 -11
- package/dist/ui/StatusBar.d.ts +1 -2
- package/dist/ui/StatusBar.js +5 -6
- package/package.json +1 -1
- package/src/agent/guards.ts +0 -196
- package/src/agent/protocol.ts +1 -4
- package/src/agent/tools.ts +377 -3
- package/src/app.ts +103 -335
- package/src/cli.ts +3 -30
- package/src/commands/index.ts +0 -22
- package/src/commands/login.ts +10 -12
- package/src/commands/logout.ts +16 -8
- package/src/commands/model.ts +22 -42
- package/src/commands/provider.ts +9 -20
- package/src/commands/whoami.ts +5 -6
- package/src/config/store.ts +69 -122
- package/src/config/trust.ts +21 -137
- package/src/lib/diagnostics.ts +4 -60
- package/src/lib/http.ts +4 -66
- package/src/providers/anthropic.ts +1 -13
- package/src/providers/deepseek.ts +1 -2
- package/src/providers/groq.ts +1 -2
- package/src/providers/index.ts +1 -32
- package/src/providers/mistral.ts +1 -2
- package/src/providers/moonshot.ts +1 -2
- package/src/providers/openai.ts +1 -2
- package/src/providers/openaiCompatible.ts +10 -58
- package/src/providers/openrouter.ts +1 -2
- package/src/providers/qwen.ts +1 -2
- package/src/providers/xai.ts +1 -2
- package/src/providers/zhipu.ts +1 -2
- package/src/session/log.ts +28 -267
- package/src/types.ts +1 -63
- package/src/ui/Message.ts +2 -13
- package/src/ui/Prompt.ts +2 -2
- package/src/ui/Spinner.ts +5 -11
- package/src/ui/StatusBar.ts +5 -6
- package/dist/agent/approval.d.ts +0 -50
- package/dist/agent/approval.js +0 -7
- package/dist/agent/compaction.d.ts +0 -39
- package/dist/agent/compaction.js +0 -227
- package/dist/agent/context.d.ts +0 -29
- package/dist/agent/context.js +0 -62
- package/dist/agent/ignore.d.ts +0 -11
- package/dist/agent/ignore.js +0 -129
- package/dist/agent/process-tree.d.ts +0 -2
- package/dist/agent/process-tree.js +0 -31
- package/dist/agent/protocol-mode.d.ts +0 -6
- package/dist/agent/protocol-mode.js +0 -51
- package/dist/agent/registry.d.ts +0 -17
- package/dist/agent/registry.js +0 -90
- package/dist/agent/runner.d.ts +0 -106
- package/dist/agent/runner.js +0 -352
- package/dist/agent/shell-session.d.ts +0 -37
- package/dist/agent/shell-session.js +0 -400
- package/dist/agent/stream.d.ts +0 -29
- package/dist/agent/stream.js +0 -103
- package/dist/agent/tool-schemas.d.ts +0 -2
- package/dist/agent/tool-schemas.js +0 -63
- package/dist/agent/tools/append_file.d.ts +0 -2
- package/dist/agent/tools/append_file.js +0 -26
- package/dist/agent/tools/apply_patch.d.ts +0 -10
- package/dist/agent/tools/apply_patch.js +0 -52
- package/dist/agent/tools/copy_file.d.ts +0 -2
- package/dist/agent/tools/copy_file.js +0 -30
- package/dist/agent/tools/delete_file.d.ts +0 -2
- package/dist/agent/tools/delete_file.js +0 -24
- package/dist/agent/tools/grep.d.ts +0 -2
- package/dist/agent/tools/grep.js +0 -23
- package/dist/agent/tools/index.d.ts +0 -2
- package/dist/agent/tools/index.js +0 -32
- package/dist/agent/tools/list_files.d.ts +0 -2
- package/dist/agent/tools/list_files.js +0 -17
- package/dist/agent/tools/make_dir.d.ts +0 -2
- package/dist/agent/tools/make_dir.js +0 -24
- package/dist/agent/tools/move_path.d.ts +0 -2
- package/dist/agent/tools/move_path.js +0 -47
- package/dist/agent/tools/paths.d.ts +0 -7
- package/dist/agent/tools/paths.js +0 -84
- package/dist/agent/tools/pwd.d.ts +0 -2
- package/dist/agent/tools/pwd.js +0 -10
- package/dist/agent/tools/read_file.d.ts +0 -2
- package/dist/agent/tools/read_file.js +0 -21
- package/dist/agent/tools/read_many.d.ts +0 -2
- package/dist/agent/tools/read_many.js +0 -25
- package/dist/agent/tools/readers.d.ts +0 -6
- package/dist/agent/tools/readers.js +0 -216
- package/dist/agent/tools/replace_in_file.d.ts +0 -2
- package/dist/agent/tools/replace_in_file.js +0 -30
- package/dist/agent/tools/shell.d.ts +0 -2
- package/dist/agent/tools/shell.js +0 -48
- package/dist/agent/tools/stat_path.d.ts +0 -2
- package/dist/agent/tools/stat_path.js +0 -31
- package/dist/agent/tools/types.d.ts +0 -39
- package/dist/agent/tools/types.js +0 -1
- package/dist/agent/tools/validate.d.ts +0 -6
- package/dist/agent/tools/validate.js +0 -29
- package/dist/agent/tools/write_file.d.ts +0 -2
- package/dist/agent/tools/write_file.js +0 -26
- package/dist/commands/attach.d.ts +0 -9
- package/dist/commands/attach.js +0 -15
- package/dist/commands/bg.d.ts +0 -7
- package/dist/commands/bg.js +0 -9
- package/dist/commands/cancel.d.ts +0 -10
- package/dist/commands/cancel.js +0 -10
- package/dist/commands/compact.d.ts +0 -10
- package/dist/commands/compact.js +0 -22
- package/dist/commands/context.d.ts +0 -7
- package/dist/commands/context.js +0 -24
- package/dist/commands/edit.d.ts +0 -10
- package/dist/commands/edit.js +0 -36
- package/dist/commands/effort.d.ts +0 -25
- package/dist/commands/effort.js +0 -48
- package/dist/commands/expand.d.ts +0 -10
- package/dist/commands/expand.js +0 -8
- package/dist/commands/jobs.d.ts +0 -7
- package/dist/commands/jobs.js +0 -16
- package/dist/commands/kill.d.ts +0 -7
- package/dist/commands/kill.js +0 -11
- package/dist/commands/steer.d.ts +0 -11
- package/dist/commands/steer.js +0 -13
- package/dist/lib/model-meta.d.ts +0 -19
- package/dist/lib/model-meta.js +0 -113
- package/dist/lib/native-tools.d.ts +0 -12
- package/dist/lib/native-tools.js +0 -21
- package/dist/lib/net.d.ts +0 -33
- package/dist/lib/net.js +0 -168
- package/dist/patch/apply.d.ts +0 -15
- package/dist/patch/apply.js +0 -172
- package/dist/patch/parse.d.ts +0 -29
- package/dist/patch/parse.js +0 -112
- package/dist/patch/plan.d.ts +0 -49
- package/dist/patch/plan.js +0 -273
- package/dist/session/projection.d.ts +0 -43
- package/dist/session/projection.js +0 -257
- package/dist/ui/Picker.d.ts +0 -8
- package/dist/ui/Picker.js +0 -24
- package/src/agent/approval.ts +0 -47
- package/src/agent/compaction.ts +0 -276
- package/src/agent/context.ts +0 -97
- package/src/agent/ignore.ts +0 -147
- package/src/agent/process-tree.ts +0 -32
- package/src/agent/protocol-mode.ts +0 -59
- package/src/agent/registry.ts +0 -98
- package/src/agent/runner.ts +0 -443
- package/src/agent/shell-session.ts +0 -449
- package/src/agent/stream.ts +0 -143
- package/src/agent/tool-schemas.ts +0 -67
- package/src/agent/tools/append_file.ts +0 -28
- package/src/agent/tools/apply_patch.ts +0 -54
- package/src/agent/tools/copy_file.ts +0 -32
- package/src/agent/tools/delete_file.ts +0 -26
- package/src/agent/tools/grep.ts +0 -25
- package/src/agent/tools/index.ts +0 -34
- package/src/agent/tools/list_files.ts +0 -19
- package/src/agent/tools/make_dir.ts +0 -26
- package/src/agent/tools/move_path.ts +0 -50
- package/src/agent/tools/paths.ts +0 -92
- package/src/agent/tools/pwd.ts +0 -12
- package/src/agent/tools/read_file.ts +0 -23
- package/src/agent/tools/read_many.ts +0 -27
- package/src/agent/tools/readers.ts +0 -228
- package/src/agent/tools/replace_in_file.ts +0 -32
- package/src/agent/tools/shell.ts +0 -53
- package/src/agent/tools/stat_path.ts +0 -33
- package/src/agent/tools/types.ts +0 -42
- package/src/agent/tools/validate.ts +0 -35
- package/src/agent/tools/write_file.ts +0 -28
- package/src/commands/attach.ts +0 -16
- package/src/commands/bg.ts +0 -9
- package/src/commands/cancel.ts +0 -10
- package/src/commands/compact.ts +0 -23
- package/src/commands/context.ts +0 -25
- package/src/commands/edit.ts +0 -37
- package/src/commands/effort.ts +0 -49
- package/src/commands/expand.ts +0 -8
- package/src/commands/jobs.ts +0 -17
- package/src/commands/kill.ts +0 -12
- package/src/commands/steer.ts +0 -13
- package/src/lib/model-meta.ts +0 -138
- package/src/lib/native-tools.ts +0 -32
- package/src/lib/net.ts +0 -204
- package/src/patch/apply.ts +0 -194
- package/src/patch/parse.ts +0 -131
- package/src/patch/plan.ts +0 -317
- package/src/session/projection.ts +0 -308
- package/src/ui/Picker.ts +0 -24
package/src/config/trust.ts
CHANGED
|
@@ -7,30 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* FASTCODE_TRUST_ALL=1 marks every directory trusted (scripts, CI).
|
|
9
9
|
*/
|
|
10
|
-
import { existsSync, readFileSync, realpathSync,
|
|
10
|
+
import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { resolve } from "node:path";
|
|
12
12
|
import { ensureDir, getPaths } from "./paths.js";
|
|
13
|
-
|
|
14
13
|
const TRUTHY = ["1", "true", "yes", "on"];
|
|
15
|
-
|
|
16
|
-
export interface TrustPathEntry {
|
|
17
|
-
grantedAt: string;
|
|
18
|
-
lastUsed: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface TrustToolEntry extends TrustPathEntry {
|
|
22
|
-
catalogHash: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface TrustStore {
|
|
26
|
-
version: 1;
|
|
27
|
-
tools: Record<string, TrustToolEntry>;
|
|
28
|
-
paths: Record<string, TrustPathEntry>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
let cache: { file: string; key: string; state: TrustStore } | undefined;
|
|
32
|
-
|
|
33
|
-
export function normalizeProjectPath(dir: string): string {
|
|
14
|
+
export function normalizeProjectPath(dir) {
|
|
34
15
|
const absolute = resolve(dir);
|
|
35
16
|
try {
|
|
36
17
|
return realpathSync(absolute);
|
|
@@ -39,145 +20,48 @@ export function normalizeProjectPath(dir: string): string {
|
|
|
39
20
|
return absolute;
|
|
40
21
|
}
|
|
41
22
|
}
|
|
42
|
-
|
|
43
|
-
function emptyStore(): TrustStore {
|
|
44
|
-
return { version: 1, tools: {}, paths: {} };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function normalizePaths(raw: any): Record<string, TrustPathEntry> {
|
|
48
|
-
const paths: Record<string, TrustPathEntry> = {};
|
|
49
|
-
if (!raw || typeof raw !== "object")
|
|
50
|
-
return paths;
|
|
51
|
-
for (const [key, value] of Object.entries<any>(raw)) {
|
|
52
|
-
if (!key || !value || typeof value !== "object")
|
|
53
|
-
continue;
|
|
54
|
-
paths[key] = {
|
|
55
|
-
grantedAt: typeof value.grantedAt === "string" ? value.grantedAt : "",
|
|
56
|
-
lastUsed: typeof value.lastUsed === "string" ? value.lastUsed : "",
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
return paths;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function normalizeTools(raw: any): Record<string, TrustToolEntry> {
|
|
63
|
-
const tools: Record<string, TrustToolEntry> = {};
|
|
64
|
-
if (!raw || typeof raw !== "object")
|
|
65
|
-
return tools;
|
|
66
|
-
for (const [key, value] of Object.entries<any>(raw)) {
|
|
67
|
-
if (!key || !value || typeof value !== "object" || typeof value.catalogHash !== "string")
|
|
68
|
-
continue;
|
|
69
|
-
tools[key] = {
|
|
70
|
-
grantedAt: typeof value.grantedAt === "string" ? value.grantedAt : "",
|
|
71
|
-
lastUsed: typeof value.lastUsed === "string" ? value.lastUsed : "",
|
|
72
|
-
catalogHash: value.catalogHash,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
return tools;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function migrateLegacy(raw: any): TrustStore | undefined {
|
|
79
|
-
if (!raw || typeof raw !== "object" || !Array.isArray(raw.trusted))
|
|
80
|
-
return undefined;
|
|
81
|
-
const store = emptyStore();
|
|
82
|
-
for (const entry of raw.trusted) {
|
|
83
|
-
if (!entry || typeof entry.path !== "string" || !entry.path)
|
|
84
|
-
continue;
|
|
85
|
-
const at = typeof entry.addedAt === "string" ? entry.addedAt : new Date().toISOString();
|
|
86
|
-
store.paths[entry.path] = { grantedAt: at, lastUsed: at };
|
|
87
|
-
}
|
|
88
|
-
return store;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function readStore(file: string): TrustStore {
|
|
92
|
-
if (!existsSync(file))
|
|
93
|
-
return emptyStore();
|
|
94
|
-
try {
|
|
95
|
-
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
96
|
-
if (raw && raw.version === 1) {
|
|
97
|
-
return {
|
|
98
|
-
version: 1,
|
|
99
|
-
tools: normalizeTools(raw.tools),
|
|
100
|
-
paths: normalizePaths(raw.paths),
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
const migrated = migrateLegacy(raw);
|
|
104
|
-
if (!migrated)
|
|
105
|
-
return emptyStore();
|
|
106
|
-
try {
|
|
107
|
-
writeFileSync(file, JSON.stringify(migrated, null, 2), "utf8");
|
|
108
|
-
}
|
|
109
|
-
catch {
|
|
110
|
-
// A read-only home still gets the migrated view in memory.
|
|
111
|
-
}
|
|
112
|
-
return migrated;
|
|
113
|
-
}
|
|
114
|
-
catch {
|
|
115
|
-
return emptyStore();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function loadTrust(): TrustStore {
|
|
23
|
+
export function loadTrust() {
|
|
120
24
|
const { trust } = getPaths();
|
|
121
|
-
|
|
25
|
+
if (!existsSync(trust))
|
|
26
|
+
return { trusted: [] };
|
|
122
27
|
try {
|
|
123
|
-
const
|
|
124
|
-
|
|
28
|
+
const raw = JSON.parse(readFileSync(trust, "utf8"));
|
|
29
|
+
return { trusted: Array.isArray(raw.trusted) ? raw.trusted.filter((entry) => entry && typeof entry.path === "string") : [] };
|
|
125
30
|
}
|
|
126
31
|
catch {
|
|
127
|
-
|
|
32
|
+
return { trusted: [] };
|
|
128
33
|
}
|
|
129
|
-
if (cache && cache.file === trust && cache.key === key)
|
|
130
|
-
return cache.state;
|
|
131
|
-
const state = readStore(trust);
|
|
132
|
-
cache = { file: trust, key, state };
|
|
133
|
-
return state;
|
|
134
34
|
}
|
|
135
|
-
|
|
136
|
-
function saveTrust(state: TrustStore): void {
|
|
35
|
+
function saveTrust(state) {
|
|
137
36
|
const paths = getPaths();
|
|
138
37
|
ensureDir(paths.root);
|
|
139
38
|
writeFileSync(paths.trust, JSON.stringify(state, null, 2), "utf8");
|
|
140
|
-
try {
|
|
141
|
-
const info = statSync(paths.trust);
|
|
142
|
-
cache = { file: paths.trust, key: `${info.mtimeMs}:${info.size}`, state };
|
|
143
|
-
}
|
|
144
|
-
catch {
|
|
145
|
-
cache = undefined;
|
|
146
|
-
}
|
|
147
39
|
}
|
|
148
|
-
|
|
149
|
-
export function trustAllEnabled(): boolean {
|
|
40
|
+
export function trustAllEnabled() {
|
|
150
41
|
return TRUTHY.includes((process.env["FASTCODE_TRUST_ALL"] ?? "").trim().toLowerCase());
|
|
151
42
|
}
|
|
152
|
-
|
|
153
|
-
export function isTrusted(dir: string): boolean {
|
|
43
|
+
export function isTrusted(dir) {
|
|
154
44
|
if (trustAllEnabled())
|
|
155
45
|
return true;
|
|
156
46
|
const target = normalizeProjectPath(dir);
|
|
157
|
-
return
|
|
47
|
+
return loadTrust().trusted.some((entry) => entry.path === target);
|
|
158
48
|
}
|
|
159
|
-
|
|
160
|
-
export function trustProject(dir: string): string {
|
|
49
|
+
export function trustProject(dir) {
|
|
161
50
|
const target = normalizeProjectPath(dir);
|
|
162
51
|
const state = loadTrust();
|
|
163
|
-
if (!state.
|
|
164
|
-
|
|
165
|
-
state.paths[target] = { grantedAt: at, lastUsed: at };
|
|
52
|
+
if (!state.trusted.some((entry) => entry.path === target)) {
|
|
53
|
+
state.trusted.push({ path: target, addedAt: new Date().toISOString() });
|
|
166
54
|
saveTrust(state);
|
|
167
55
|
}
|
|
168
56
|
return target;
|
|
169
57
|
}
|
|
170
|
-
|
|
171
|
-
export function untrustProject(dir: string): boolean {
|
|
58
|
+
export function untrustProject(dir) {
|
|
172
59
|
const target = normalizeProjectPath(dir);
|
|
173
60
|
const state = loadTrust();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
saveTrust(state);
|
|
178
|
-
return true;
|
|
61
|
+
const next = state.trusted.filter((entry) => entry.path !== target);
|
|
62
|
+
saveTrust({ trusted: next });
|
|
63
|
+
return state.trusted.length !== next.length;
|
|
179
64
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return Object.entries(loadTrust().paths).map(([path, entry]) => ({ path, addedAt: entry.grantedAt }));
|
|
65
|
+
export function listTrusted() {
|
|
66
|
+
return loadTrust().trusted;
|
|
183
67
|
}
|
package/src/lib/diagnostics.ts
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* diagnosed without opening the TUI: Node version, Termux detection, whether
|
|
6
6
|
* stdout is a real terminal, the config root, and which provider keys exist.
|
|
7
7
|
*/
|
|
8
|
-
import { existsSync, statSync } from "node:fs";
|
|
9
|
-
import { totalmem, freemem, tmpdir as osTmpdir } from "node:os";
|
|
10
8
|
import { detectPlatform, platformLabel } from "./platform.js";
|
|
11
9
|
import { readPackageMeta } from "./pkg.js";
|
|
12
10
|
import { shouldUseStableTerminalUi } from "./uiMode.js";
|
|
@@ -15,14 +13,10 @@ import { loadConfig, resolveApiKey, resolveProviderKey } from "../config/store.j
|
|
|
15
13
|
import { PROVIDERS } from "../providers/index.js";
|
|
16
14
|
import { listInstalledSkills } from "../skills/manager.js";
|
|
17
15
|
import { listInstalledPlugins } from "../plugins/manager.js";
|
|
18
|
-
import {
|
|
16
|
+
import { loggingEnabled, listSessions, sessionCacheLimitBytes } from "../session/log.js";
|
|
19
17
|
import { isTrusted } from "../config/trust.js";
|
|
20
|
-
import { DEFAULT_DENY_PATTERNS
|
|
18
|
+
import { DEFAULT_DENY_PATTERNS } from "../agent/guards.js";
|
|
21
19
|
import { toolOutputLimit } from "../agent/limits.js";
|
|
22
|
-
import { compactionConfigFromEnv, heapExceeded } from "../agent/compaction.js";
|
|
23
|
-
import { shellPoolStats } from "../agent/shell-session.js";
|
|
24
|
-
import { resolveToolProtocol, toolCapabilityKey } from "../agent/protocol-mode.js";
|
|
25
|
-
import { getModelMeta, modelMetaOverridePath } from "./model-meta.js";
|
|
26
20
|
const NODE_REQUIRED = "18.17";
|
|
27
21
|
function readVersion() {
|
|
28
22
|
return readPackageMeta().version;
|
|
@@ -61,16 +55,6 @@ export function collectDiagnostics() {
|
|
|
61
55
|
// Same for plugins.
|
|
62
56
|
}
|
|
63
57
|
const platform = detectPlatform();
|
|
64
|
-
const compaction = compactionConfigFromEnv();
|
|
65
|
-
const shells = shellPoolStats();
|
|
66
|
-
let authMode = "missing";
|
|
67
|
-
try {
|
|
68
|
-
if (paths?.auth && existsSync(paths.auth))
|
|
69
|
-
authMode = (statSync(paths.auth).mode & 0o777).toString(8).padStart(4, "0");
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
authMode = "unreadable";
|
|
73
|
-
}
|
|
74
58
|
return {
|
|
75
59
|
version: readVersion(),
|
|
76
60
|
node: process.version,
|
|
@@ -85,10 +69,6 @@ export function collectDiagnostics() {
|
|
|
85
69
|
configRoot: paths?.root,
|
|
86
70
|
configFile: paths?.config,
|
|
87
71
|
authFile: paths?.auth,
|
|
88
|
-
authMode,
|
|
89
|
-
tmpdir: process.env["TMPDIR"] || osTmpdir(),
|
|
90
|
-
memTotalMb: totalmem() / 1024 / 1024,
|
|
91
|
-
memFreeMb: freemem() / 1024 / 1024,
|
|
92
72
|
providerId: config.providerId,
|
|
93
73
|
model: config.model,
|
|
94
74
|
keyPresent,
|
|
@@ -100,8 +80,6 @@ export function collectDiagnostics() {
|
|
|
100
80
|
enabledPlugins: (config.enabledPlugins ?? []).length,
|
|
101
81
|
sessionLogEnabled: loggingEnabled(),
|
|
102
82
|
sessionCacheMb: Math.round(sessionCacheLimitBytes() / (1024 * 1024)),
|
|
103
|
-
sessionFsync: fsyncMode(),
|
|
104
|
-
unclosedSessions: findUnclosedSessions().length,
|
|
105
83
|
sessionsDir: paths?.sessions,
|
|
106
84
|
sessionsCount: listSessions().length,
|
|
107
85
|
sessionsBytes: listSessions().reduce((sum, entry) => sum + entry.bytes, 0),
|
|
@@ -110,25 +88,6 @@ export function collectDiagnostics() {
|
|
|
110
88
|
shellDenyPatterns: DEFAULT_DENY_PATTERNS.length,
|
|
111
89
|
allowDangerous: ["1", "true", "yes", "on"].includes((process.env["FASTCODE_ALLOW_DANGEROUS"] ?? "").trim().toLowerCase()),
|
|
112
90
|
toolOutputLimit: toolOutputLimit(),
|
|
113
|
-
tokenBudget: positiveEnv("FASTCODE_TOKEN_BUDGET"),
|
|
114
|
-
costBudgetUsd: positiveEnv("FASTCODE_COST_BUDGET_USD"),
|
|
115
|
-
compactionEnabled: compaction.enabled,
|
|
116
|
-
compactionRatio: compaction.localThresholdRatio,
|
|
117
|
-
compactionHeapMb: compaction.heapLimitMb,
|
|
118
|
-
compactionHeapUsedMb: process.memoryUsage().heapUsed / 1024 / 1024,
|
|
119
|
-
compactionForced: heapExceeded(compaction),
|
|
120
|
-
serverCompactionAware: compaction.serverCompactionAware,
|
|
121
|
-
shellActive: shells.active,
|
|
122
|
-
shellPool: shells.pool,
|
|
123
|
-
shellSpawns: shells.spawns,
|
|
124
|
-
shellMaxSpawn: shells.maxSpawnPerTurn,
|
|
125
|
-
sseIdleMs: positiveEnv("FASTCODE_SSE_IDLE_MS") || 25_000,
|
|
126
|
-
toolProtocol: resolveToolProtocol(config, config.providerId, config.model),
|
|
127
|
-
toolProtocolMode: (process.env["FASTCODE_TOOL_PROTOCOL"] ?? "legacy").trim().toLowerCase(),
|
|
128
|
-
toolCapability: config.capabilities?.toolCalling?.[toolCapabilityKey(config.providerId, config.model)] ?? "unknown",
|
|
129
|
-
loopExempt: Array.from(loopExemptTools()),
|
|
130
|
-
modelMetaFile: modelMetaOverridePath(),
|
|
131
|
-
modelMeta: getModelMeta(config.providerId, config.model),
|
|
132
91
|
providers: PROVIDERS.map((p) => ({
|
|
133
92
|
id: p.id,
|
|
134
93
|
label: p.label,
|
|
@@ -137,10 +96,6 @@ export function collectDiagnostics() {
|
|
|
137
96
|
})),
|
|
138
97
|
};
|
|
139
98
|
}
|
|
140
|
-
function positiveEnv(name) {
|
|
141
|
-
const value = Number(process.env[name]);
|
|
142
|
-
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
143
|
-
}
|
|
144
99
|
export function formatDiagnostics(info) {
|
|
145
100
|
const lines = [];
|
|
146
101
|
lines.push(`FastCode ${info.version} — environment check`);
|
|
@@ -151,21 +106,12 @@ export function formatDiagnostics(info) {
|
|
|
151
106
|
lines.push(` stable UI ${info.stableUi ? "on (anti-flicker)" : "off"}`);
|
|
152
107
|
lines.push(" config root " + (info.configRoot ?? "(unavailable)"));
|
|
153
108
|
lines.push(" config file " + (info.configFile ?? "(unavailable)"));
|
|
154
|
-
lines.push(" keys file " + (info.authFile ?? "(unavailable)") + "
|
|
155
|
-
lines.push(" tmpdir " + info.tmpdir + (info.termux && !String(info.tmpdir).startsWith("/data/data/com.termux") ? " [WARN: use $PREFIX/tmp]" : ""));
|
|
156
|
-
lines.push(" memory " + info.memFreeMb.toFixed(0) + "MB free / " + info.memTotalMb.toFixed(0) + "MB total");
|
|
109
|
+
lines.push(" keys file " + (info.authFile ?? "(unavailable)") + " (separate, chmod 0600)");
|
|
157
110
|
lines.push(" trusted cwd " + (info.trusted ? "yes" : "NO - agent tools ask first or stay off") + " (" + (info.trustFile ?? "?") + ")");
|
|
158
111
|
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
|
-
lines.push(" session
|
|
112
|
+
lines.push(" session dir " + (info.sessionsDir ?? "(unavailable)"));
|
|
160
113
|
lines.push(" shell guard " + info.shellDenyPatterns + " deny patterns" + (info.allowDangerous ? " [FASTCODE_ALLOW_DANGEROUS is ON]" : ""));
|
|
161
114
|
lines.push(" tool output " + info.toolOutputLimit + " chars max (streaming cap)");
|
|
162
|
-
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`);
|
|
165
|
-
lines.push(` loop exempt ${info.loopExempt.length > 0 ? info.loopExempt.join(", ") : "(none)"}`);
|
|
166
|
-
lines.push(` model meta ctx ${info.modelMeta.contextWindow} · ~${info.modelMeta.charsPerToken} chars/token · ${info.modelMeta.supportsTools ? "tools" : "no tools"} · ${info.modelMetaFile}`);
|
|
167
|
-
lines.push(` sse timeout ${info.sseIdleMs}ms idle`);
|
|
168
|
-
lines.push(` tool protocol ${info.toolProtocol} (${info.toolProtocolMode}) · cached ${info.toolCapability}`);
|
|
169
115
|
lines.push(` provider ${info.providerId} · model ${info.model}`);
|
|
170
116
|
let apiKeyStatus;
|
|
171
117
|
if (info.keyPresent)
|
|
@@ -188,8 +134,6 @@ export function formatDiagnostics(info) {
|
|
|
188
134
|
lines.push(" - pkg install nodejs git");
|
|
189
135
|
lines.push(" - keep the session alive with: termux-wake-lock");
|
|
190
136
|
lines.push(" - add an extra-keys row for Esc/Ctrl in ~/.termux/termux.properties");
|
|
191
|
-
lines.push(" - long tasks: export TMPDIR=$PREFIX/tmp and use termux-wake-lock");
|
|
192
|
-
lines.push(` - Android 12+ caps phantom processes (~32); background shells are pooled (${info.shellPool} resident, cap ${info.shellMaxSpawn}/turn)`);
|
|
193
137
|
}
|
|
194
138
|
return lines.join("\n");
|
|
195
139
|
}
|
package/src/lib/http.ts
CHANGED
|
@@ -16,21 +16,8 @@ export async function fetchJson(url: string, init: any = {}): Promise<any> {
|
|
|
16
16
|
}
|
|
17
17
|
return (await res.json());
|
|
18
18
|
}
|
|
19
|
-
export class NetworkStallError extends Error {
|
|
20
|
-
readonly code = "NETWORK_STALL";
|
|
21
|
-
readonly bytesReceived: number;
|
|
22
|
-
constructor(message: string, bytesReceived = 0) {
|
|
23
|
-
super(message);
|
|
24
|
-
this.name = "NetworkStallError";
|
|
25
|
-
this.bytesReceived = bytesReceived;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export interface StreamSseOptions {
|
|
29
|
-
idleTimeoutMs?: number;
|
|
30
|
-
totalTimeoutMs?: number;
|
|
31
|
-
}
|
|
32
19
|
/** Async iterator over SSE events from a streaming HTTP response. */
|
|
33
|
-
export async function* streamSSE(res
|
|
20
|
+
export async function* streamSSE(res) {
|
|
34
21
|
if (!res.ok) {
|
|
35
22
|
const body = await res.text().catch(() => "");
|
|
36
23
|
throw new Error(`${res.status} ${res.statusText}: ${body.slice(0, 500)}`);
|
|
@@ -39,50 +26,19 @@ export async function* streamSSE(res, options: StreamSseOptions = {}) {
|
|
|
39
26
|
throw new Error("Empty response body");
|
|
40
27
|
const reader = res.body.getReader();
|
|
41
28
|
const decoder = new TextDecoder("utf-8");
|
|
42
|
-
const envIdle = Number(process.env["FASTCODE_SSE_IDLE_MS"]);
|
|
43
|
-
const idleMs = positive(options.idleTimeoutMs, positive(envIdle, 25_000));
|
|
44
|
-
const totalMs = positive(options.totalTimeoutMs, 0);
|
|
45
|
-
const startedAt = Date.now();
|
|
46
|
-
let bytesReceived = 0;
|
|
47
29
|
let buffer = "";
|
|
48
30
|
while (true) {
|
|
49
|
-
const
|
|
50
|
-
if (remaining <= 0)
|
|
51
|
-
throw new NetworkStallError(`SSE total timeout after ${totalMs}ms`, bytesReceived);
|
|
52
|
-
const boundedByTotal = remaining <= idleMs;
|
|
53
|
-
const waitMs = boundedByTotal ? Math.max(1, remaining) : idleMs;
|
|
54
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
55
|
-
const timeout = new Promise<never>((_resolve, reject) => {
|
|
56
|
-
timer = setTimeout(() => {
|
|
57
|
-
reject(new NetworkStallError(boundedByTotal
|
|
58
|
-
? `SSE total timeout after ${totalMs}ms`
|
|
59
|
-
: `SSE idle timeout after ${idleMs}ms (no data received; set FASTCODE_SSE_IDLE_MS to wait longer)`, bytesReceived));
|
|
60
|
-
}, waitMs);
|
|
61
|
-
});
|
|
62
|
-
let chunk;
|
|
63
|
-
try {
|
|
64
|
-
chunk = await Promise.race([reader.read(), timeout]);
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
await reader.cancel().catch(() => undefined);
|
|
68
|
-
throw err;
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
if (timer)
|
|
72
|
-
clearTimeout(timer);
|
|
73
|
-
}
|
|
74
|
-
const { value, done } = chunk;
|
|
31
|
+
const { value, done } = await reader.read();
|
|
75
32
|
if (done)
|
|
76
33
|
break;
|
|
77
|
-
bytesReceived += value?.byteLength ?? 0;
|
|
78
34
|
// Normalize CR/LF so both "\n\n" and "\r\n\r\n" frame separators
|
|
79
35
|
// are detected, including a CR/LF split across two chunks.
|
|
80
36
|
buffer = (buffer + decoder.decode(value, { stream: true })).replace(/\r\n?/g, "\n");
|
|
81
37
|
let sep = buffer.indexOf("\n\n");
|
|
82
38
|
while (sep !== -1) {
|
|
83
|
-
const
|
|
39
|
+
const chunk = buffer.slice(0, sep);
|
|
84
40
|
buffer = buffer.slice(sep + 2);
|
|
85
|
-
const ev = parseSseChunk(
|
|
41
|
+
const ev = parseSseChunk(chunk);
|
|
86
42
|
if (ev)
|
|
87
43
|
yield ev;
|
|
88
44
|
sep = buffer.indexOf("\n\n");
|
|
@@ -96,24 +52,6 @@ export async function* streamSSE(res, options: StreamSseOptions = {}) {
|
|
|
96
52
|
yield ev;
|
|
97
53
|
}
|
|
98
54
|
}
|
|
99
|
-
export async function probeConnectivity(url: string, timeoutMs = 3000): Promise<boolean> {
|
|
100
|
-
const controller = new AbortController();
|
|
101
|
-
const timer = setTimeout(() => controller.abort(), positive(timeoutMs, 3000));
|
|
102
|
-
try {
|
|
103
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
104
|
-
await res.body?.cancel().catch(() => undefined);
|
|
105
|
-
return res.ok;
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
finally {
|
|
111
|
-
clearTimeout(timer);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function positive(value, fallback) {
|
|
115
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
116
|
-
}
|
|
117
55
|
function parseSseChunk(chunk) {
|
|
118
56
|
let event;
|
|
119
57
|
const dataLines = [];
|
|
@@ -10,16 +10,12 @@ const STATIC_MODELS = [
|
|
|
10
10
|
{ id: "claude-haiku-4.5", label: "Claude Haiku 4.5" },
|
|
11
11
|
];
|
|
12
12
|
const BASE = "https://api.anthropic.com/v1";
|
|
13
|
-
const THINKING_BUDGETS = { low: 1024, medium: 4096, high: 8192, max: 16000 };
|
|
14
13
|
export const anthropicProvider = {
|
|
15
14
|
id: "anthropic",
|
|
16
15
|
label: "Anthropic (Claude)",
|
|
17
16
|
authEnv: ["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"],
|
|
18
17
|
baseUrl: BASE,
|
|
19
18
|
defaultModel: "claude-sonnet-5.5",
|
|
20
|
-
supportsReasoning() {
|
|
21
|
-
return true;
|
|
22
|
-
},
|
|
23
19
|
async listModels(apiKey) {
|
|
24
20
|
// Query the live model list; fall back to the built-in list offline.
|
|
25
21
|
if (apiKey) {
|
|
@@ -49,7 +45,7 @@ export const anthropicProvider = {
|
|
|
49
45
|
throw new Error("Missing ANTHROPIC_API_KEY. Bind one with /login anthropic <model> <key>, or set the env var.");
|
|
50
46
|
}
|
|
51
47
|
const messages = req.messages.map((m) => ({ role: m.role, content: m.content }));
|
|
52
|
-
const body
|
|
48
|
+
const body = {
|
|
53
49
|
model: req.model,
|
|
54
50
|
messages,
|
|
55
51
|
...(req.system ? { system: req.system } : {}),
|
|
@@ -57,14 +53,6 @@ export const anthropicProvider = {
|
|
|
57
53
|
max_tokens: req.maxOutputTokens ?? 4096,
|
|
58
54
|
...(req.temperature !== undefined ? { temperature: req.temperature } : {}),
|
|
59
55
|
};
|
|
60
|
-
if (req.reasoningEffort && req.reasoningEffort !== "auto") {
|
|
61
|
-
const budget = THINKING_BUDGETS[req.reasoningEffort] ?? THINKING_BUDGETS.medium;
|
|
62
|
-
body.thinking = { type: "enabled", budget_tokens: budget };
|
|
63
|
-
// max_tokens must exceed the thinking budget.
|
|
64
|
-
body.max_tokens = Math.max(body.max_tokens, budget + 1024);
|
|
65
|
-
// Extended thinking is incompatible with a custom temperature.
|
|
66
|
-
delete body.temperature;
|
|
67
|
-
}
|
|
68
56
|
const res = await fetch(BASE + "/messages", {
|
|
69
57
|
method: "POST",
|
|
70
58
|
headers: {
|
|
@@ -11,6 +11,5 @@ export const deepseekProvider = {
|
|
|
11
11
|
baseUrl: BASE,
|
|
12
12
|
defaultModel: "deepseek-v4-pro",
|
|
13
13
|
listModels: (apiKey) => listModelsHttp({ baseUrl: BASE, apiKey, staticModels: STATIC }),
|
|
14
|
-
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: BASE, apiKey, staticModels: STATIC
|
|
15
|
-
supportsReasoning: () => true,
|
|
14
|
+
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: BASE, apiKey, staticModels: STATIC }),
|
|
16
15
|
};
|
package/src/providers/groq.ts
CHANGED
|
@@ -16,6 +16,5 @@ export const groqProvider = {
|
|
|
16
16
|
baseUrl: BASE,
|
|
17
17
|
defaultModel: "llama-3.3-70b-versatile",
|
|
18
18
|
listModels: (apiKey) => listModelsHttp({ baseUrl: BASE, apiKey, staticModels: STATIC }),
|
|
19
|
-
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: BASE, apiKey, staticModels: STATIC
|
|
20
|
-
supportsReasoning: () => true,
|
|
19
|
+
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: BASE, apiKey, staticModels: STATIC }),
|
|
21
20
|
};
|
package/src/providers/index.ts
CHANGED
|
@@ -11,8 +11,7 @@ import { moonshotProvider } from "./moonshot.js";
|
|
|
11
11
|
import { zhipuProvider } from "./zhipu.js";
|
|
12
12
|
import { qwenProvider } from "./qwen.js";
|
|
13
13
|
import { customProvider } from "./custom.js";
|
|
14
|
-
|
|
15
|
-
export const PROVIDERS: Provider[] = [
|
|
14
|
+
export const PROVIDERS = [
|
|
16
15
|
anthropicProvider,
|
|
17
16
|
openaiProvider,
|
|
18
17
|
googleProvider,
|
|
@@ -36,33 +35,3 @@ export function getProviderOrThrow(id) {
|
|
|
36
35
|
throw new Error("Unknown provider: " + id);
|
|
37
36
|
return p;
|
|
38
37
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Cheap connectivity check used by the model picker to label a list "offline".
|
|
41
|
-
* `listModels()` silently falls back to the static list, so without this probe
|
|
42
|
-
* the UI cannot tell a live catalog from a cached one.
|
|
43
|
-
*/
|
|
44
|
-
export async function probeProvider(provider, apiKey, timeoutMs = 4000) {
|
|
45
|
-
const controller = new AbortController();
|
|
46
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
47
|
-
try {
|
|
48
|
-
const base = String(provider.baseUrl).replace(/[/]+$/, "");
|
|
49
|
-
const headers = { accept: "application/json" };
|
|
50
|
-
if (apiKey) {
|
|
51
|
-
if (provider.id === "anthropic") {
|
|
52
|
-
headers["x-api-key"] = apiKey;
|
|
53
|
-
headers["anthropic-version"] = "2023-06-01";
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
headers["authorization"] = "Bearer " + apiKey;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const res = await fetch(base + "/models", { headers, signal: controller.signal });
|
|
60
|
-
return res.ok;
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
clearTimeout(timer);
|
|
67
|
-
}
|
|
68
|
-
}
|
package/src/providers/mistral.ts
CHANGED
|
@@ -15,6 +15,5 @@ export const mistralProvider = {
|
|
|
15
15
|
baseUrl: BASE,
|
|
16
16
|
defaultModel: "mistral-small-latest",
|
|
17
17
|
listModels: (apiKey) => listModelsHttp({ baseUrl: BASE, apiKey, staticModels: STATIC }),
|
|
18
|
-
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: BASE, apiKey, staticModels: STATIC
|
|
19
|
-
supportsReasoning: () => true,
|
|
18
|
+
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: BASE, apiKey, staticModels: STATIC }),
|
|
20
19
|
};
|
|
@@ -18,6 +18,5 @@ export const moonshotProvider = {
|
|
|
18
18
|
},
|
|
19
19
|
defaultModel: "kimi-k3",
|
|
20
20
|
listModels: (apiKey) => listModelsHttp({ baseUrl: baseUrl(), apiKey, staticModels: STATIC }),
|
|
21
|
-
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: baseUrl(), apiKey, staticModels: STATIC
|
|
22
|
-
supportsReasoning: () => true,
|
|
21
|
+
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: baseUrl(), apiKey, staticModels: STATIC }),
|
|
23
22
|
};
|
package/src/providers/openai.ts
CHANGED
|
@@ -21,6 +21,5 @@ export const openaiProvider = {
|
|
|
21
21
|
baseUrl: BASE,
|
|
22
22
|
defaultModel: "gpt-6-luna",
|
|
23
23
|
listModels: (apiKey) => listModelsHttp({ baseUrl: BASE, apiKey, staticModels: STATIC }),
|
|
24
|
-
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: BASE, apiKey, staticModels: STATIC
|
|
25
|
-
supportsReasoning: (model) => /^(o\d|gpt-5|gpt-6)/i.test(model),
|
|
24
|
+
stream: (req, apiKey) => streamChatCompletion(req, { baseUrl: BASE, apiKey, staticModels: STATIC }),
|
|
26
25
|
};
|