@datename/fastcode-termux 0.0.0-stage → 0.1.6-beta
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 +21 -0
- package/README.md +418 -2
- package/bin/fastcode.mjs +59 -0
- package/dist/agent/approval.d.ts +50 -0
- package/dist/agent/approval.js +7 -0
- package/dist/agent/compaction.d.ts +39 -0
- package/dist/agent/compaction.js +227 -0
- package/dist/agent/context.d.ts +29 -0
- package/dist/agent/context.js +62 -0
- package/dist/agent/guards.d.ts +43 -0
- package/dist/agent/guards.js +269 -0
- package/dist/agent/ignore.d.ts +11 -0
- package/dist/agent/ignore.js +129 -0
- package/dist/agent/limits.d.ts +24 -0
- package/dist/agent/limits.js +71 -0
- package/dist/agent/process-tree.d.ts +2 -0
- package/dist/agent/process-tree.js +31 -0
- package/dist/agent/protocol-mode.d.ts +6 -0
- package/dist/agent/protocol-mode.js +51 -0
- package/dist/agent/protocol.d.ts +4 -0
- package/dist/agent/protocol.js +129 -0
- package/dist/agent/registry.d.ts +17 -0
- package/dist/agent/registry.js +90 -0
- package/dist/agent/runner.d.ts +106 -0
- package/dist/agent/runner.js +352 -0
- package/dist/agent/shell-session.d.ts +37 -0
- package/dist/agent/shell-session.js +400 -0
- package/dist/agent/stream.d.ts +29 -0
- package/dist/agent/stream.js +103 -0
- package/dist/agent/tool-schemas.d.ts +2 -0
- package/dist/agent/tool-schemas.js +63 -0
- package/dist/agent/tools/append_file.d.ts +2 -0
- package/dist/agent/tools/append_file.js +26 -0
- package/dist/agent/tools/apply_patch.d.ts +10 -0
- package/dist/agent/tools/apply_patch.js +52 -0
- package/dist/agent/tools/copy_file.d.ts +2 -0
- package/dist/agent/tools/copy_file.js +30 -0
- package/dist/agent/tools/delete_file.d.ts +2 -0
- package/dist/agent/tools/delete_file.js +24 -0
- package/dist/agent/tools/grep.d.ts +2 -0
- package/dist/agent/tools/grep.js +23 -0
- package/dist/agent/tools/index.d.ts +2 -0
- package/dist/agent/tools/index.js +32 -0
- package/dist/agent/tools/list_files.d.ts +2 -0
- package/dist/agent/tools/list_files.js +17 -0
- package/dist/agent/tools/make_dir.d.ts +2 -0
- package/dist/agent/tools/make_dir.js +24 -0
- package/dist/agent/tools/move_path.d.ts +2 -0
- package/dist/agent/tools/move_path.js +47 -0
- package/dist/agent/tools/paths.d.ts +7 -0
- package/dist/agent/tools/paths.js +84 -0
- package/dist/agent/tools/pwd.d.ts +2 -0
- package/dist/agent/tools/pwd.js +10 -0
- package/dist/agent/tools/read_file.d.ts +2 -0
- package/dist/agent/tools/read_file.js +21 -0
- package/dist/agent/tools/read_many.d.ts +2 -0
- package/dist/agent/tools/read_many.js +25 -0
- package/dist/agent/tools/readers.d.ts +6 -0
- package/dist/agent/tools/readers.js +216 -0
- package/dist/agent/tools/replace_in_file.d.ts +2 -0
- package/dist/agent/tools/replace_in_file.js +30 -0
- package/dist/agent/tools/shell.d.ts +2 -0
- package/dist/agent/tools/shell.js +48 -0
- package/dist/agent/tools/stat_path.d.ts +2 -0
- package/dist/agent/tools/stat_path.js +31 -0
- package/dist/agent/tools/types.d.ts +39 -0
- package/dist/agent/tools/types.js +1 -0
- package/dist/agent/tools/validate.d.ts +6 -0
- package/dist/agent/tools/validate.js +29 -0
- package/dist/agent/tools/write_file.d.ts +2 -0
- package/dist/agent/tools/write_file.js +26 -0
- package/dist/agent/tools.d.ts +3 -0
- package/dist/agent/tools.js +2 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.js +708 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +569 -0
- package/dist/commands/about.d.ts +8 -0
- package/dist/commands/about.js +16 -0
- package/dist/commands/agent.d.ts +11 -0
- package/dist/commands/agent.js +18 -0
- package/dist/commands/attach.d.ts +9 -0
- package/dist/commands/attach.js +15 -0
- package/dist/commands/bg.d.ts +7 -0
- package/dist/commands/bg.js +9 -0
- package/dist/commands/cancel.d.ts +10 -0
- package/dist/commands/cancel.js +10 -0
- package/dist/commands/clear.d.ts +11 -0
- package/dist/commands/clear.js +9 -0
- package/dist/commands/compact.d.ts +10 -0
- package/dist/commands/compact.js +22 -0
- package/dist/commands/config.d.ts +7 -0
- package/dist/commands/config.js +24 -0
- package/dist/commands/context.d.ts +7 -0
- package/dist/commands/context.js +24 -0
- package/dist/commands/cost.d.ts +9 -0
- package/dist/commands/cost.js +13 -0
- package/dist/commands/cwd.d.ts +7 -0
- package/dist/commands/cwd.js +7 -0
- package/dist/commands/edit.d.ts +10 -0
- package/dist/commands/edit.js +36 -0
- package/dist/commands/effort.d.ts +25 -0
- package/dist/commands/effort.js +48 -0
- package/dist/commands/exit.d.ts +9 -0
- package/dist/commands/exit.js +8 -0
- package/dist/commands/expand.d.ts +10 -0
- package/dist/commands/expand.js +8 -0
- package/dist/commands/help.d.ts +6 -0
- package/dist/commands/help.js +18 -0
- package/dist/commands/index.d.ts +9 -0
- package/dist/commands/index.js +80 -0
- package/dist/commands/jobs.d.ts +7 -0
- package/dist/commands/jobs.js +16 -0
- package/dist/commands/kill.d.ts +7 -0
- package/dist/commands/kill.js +11 -0
- package/dist/commands/login.d.ts +18 -0
- package/dist/commands/login.js +63 -0
- package/dist/commands/logout.d.ts +9 -0
- package/dist/commands/logout.js +45 -0
- package/dist/commands/model.d.ts +24 -0
- package/dist/commands/model.js +50 -0
- package/dist/commands/plugin.d.ts +11 -0
- package/dist/commands/plugin.js +74 -0
- package/dist/commands/provider.d.ts +24 -0
- package/dist/commands/provider.js +39 -0
- package/dist/commands/reset.d.ts +9 -0
- package/dist/commands/reset.js +13 -0
- package/dist/commands/skills.d.ts +11 -0
- package/dist/commands/skills.js +106 -0
- package/dist/commands/steer.d.ts +11 -0
- package/dist/commands/steer.js +13 -0
- package/dist/commands/system.d.ts +11 -0
- package/dist/commands/system.js +20 -0
- package/dist/commands/theme.d.ts +11 -0
- package/dist/commands/theme.js +22 -0
- package/dist/commands/tools.d.ts +7 -0
- package/dist/commands/tools.js +29 -0
- package/dist/commands/whoami.d.ts +9 -0
- package/dist/commands/whoami.js +52 -0
- package/dist/config/paths.d.ts +4 -0
- package/dist/config/paths.js +59 -0
- package/dist/config/store.d.ts +89 -0
- package/dist/config/store.js +260 -0
- package/dist/config/themes.d.ts +73 -0
- package/dist/config/themes.js +77 -0
- package/dist/config/trust.d.ts +22 -0
- package/dist/config/trust.js +153 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +31 -0
- package/dist/lib/diagnostics.d.ts +66 -0
- package/dist/lib/diagnostics.js +195 -0
- package/dist/lib/http.d.ts +23 -0
- package/dist/lib/http.js +128 -0
- package/dist/lib/markdown.d.ts +1 -0
- package/dist/lib/markdown.js +32 -0
- package/dist/lib/model-meta.d.ts +19 -0
- package/dist/lib/model-meta.js +113 -0
- package/dist/lib/native-tools.d.ts +12 -0
- package/dist/lib/native-tools.js +21 -0
- package/dist/lib/net.d.ts +33 -0
- package/dist/lib/net.js +168 -0
- package/dist/lib/pkg.d.ts +5 -0
- package/dist/lib/pkg.js +27 -0
- package/dist/lib/platform.d.ts +7 -0
- package/dist/lib/platform.js +57 -0
- package/dist/lib/startup.d.ts +6 -0
- package/dist/lib/startup.js +25 -0
- package/dist/lib/uiMode.d.ts +2 -0
- package/dist/lib/uiMode.js +7 -0
- package/dist/patch/apply.d.ts +15 -0
- package/dist/patch/apply.js +172 -0
- package/dist/patch/parse.d.ts +29 -0
- package/dist/patch/parse.js +112 -0
- package/dist/patch/plan.d.ts +49 -0
- package/dist/patch/plan.js +273 -0
- package/dist/plugins/manager.d.ts +23 -0
- package/dist/plugins/manager.js +101 -0
- package/dist/providers/anthropic.d.ts +22 -0
- package/dist/providers/anthropic.js +119 -0
- package/dist/providers/custom.d.ts +33 -0
- package/dist/providers/custom.js +25 -0
- package/dist/providers/deepseek.d.ts +34 -0
- package/dist/providers/deepseek.js +16 -0
- package/dist/providers/google.d.ts +33 -0
- package/dist/providers/google.js +19 -0
- package/dist/providers/groq.d.ts +34 -0
- package/dist/providers/groq.js +21 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.js +67 -0
- package/dist/providers/mistral.d.ts +34 -0
- package/dist/providers/mistral.js +20 -0
- package/dist/providers/moonshot.d.ts +34 -0
- package/dist/providers/moonshot.js +23 -0
- package/dist/providers/ollama.d.ts +33 -0
- package/dist/providers/ollama.js +22 -0
- package/dist/providers/openai.d.ts +34 -0
- package/dist/providers/openai.js +26 -0
- package/dist/providers/openaiCompatible.d.ts +26 -0
- package/dist/providers/openaiCompatible.js +158 -0
- package/dist/providers/openrouter.d.ts +34 -0
- package/dist/providers/openrouter.js +27 -0
- package/dist/providers/qwen.d.ts +34 -0
- package/dist/providers/qwen.js +24 -0
- package/dist/providers/xai.d.ts +34 -0
- package/dist/providers/xai.js +19 -0
- package/dist/providers/zhipu.d.ts +34 -0
- package/dist/providers/zhipu.js +24 -0
- package/dist/session/log.d.ts +52 -0
- package/dist/session/log.js +331 -0
- package/dist/session/projection.d.ts +43 -0
- package/dist/session/projection.js +257 -0
- package/dist/skills/manager.d.ts +18 -0
- package/dist/skills/manager.js +201 -0
- package/dist/skills/parser.d.ts +18 -0
- package/dist/skills/parser.js +65 -0
- package/dist/types.d.ts +205 -0
- package/dist/types.js +1 -0
- package/dist/ui/Banner.d.ts +5 -0
- package/dist/ui/Banner.js +65 -0
- package/dist/ui/Message.d.ts +1 -0
- package/dist/ui/Message.js +24 -0
- package/dist/ui/Picker.d.ts +8 -0
- package/dist/ui/Picker.js +24 -0
- package/dist/ui/Prompt.d.ts +9 -0
- package/dist/ui/Prompt.js +38 -0
- package/dist/ui/Spinner.d.ts +5 -0
- package/dist/ui/Spinner.js +39 -0
- package/dist/ui/StatusBar.d.ts +11 -0
- package/dist/ui/StatusBar.js +20 -0
- package/dist/ui/TrustGate.d.ts +5 -0
- package/dist/ui/TrustGate.js +26 -0
- package/package.json +96 -4
- package/src/agent/approval.ts +47 -0
- package/src/agent/compaction.ts +276 -0
- package/src/agent/context.ts +97 -0
- package/src/agent/guards.ts +270 -0
- package/src/agent/ignore.ts +147 -0
- package/src/agent/limits.ts +71 -0
- package/src/agent/process-tree.ts +32 -0
- package/src/agent/protocol-mode.ts +59 -0
- package/src/agent/protocol.ts +129 -0
- package/src/agent/registry.ts +98 -0
- package/src/agent/runner.ts +443 -0
- package/src/agent/shell-session.ts +449 -0
- package/src/agent/stream.ts +143 -0
- package/src/agent/tool-schemas.ts +67 -0
- package/src/agent/tools/append_file.ts +28 -0
- package/src/agent/tools/apply_patch.ts +54 -0
- package/src/agent/tools/copy_file.ts +32 -0
- package/src/agent/tools/delete_file.ts +26 -0
- package/src/agent/tools/grep.ts +25 -0
- package/src/agent/tools/index.ts +34 -0
- package/src/agent/tools/list_files.ts +19 -0
- package/src/agent/tools/make_dir.ts +26 -0
- package/src/agent/tools/move_path.ts +50 -0
- package/src/agent/tools/paths.ts +92 -0
- package/src/agent/tools/pwd.ts +12 -0
- package/src/agent/tools/read_file.ts +23 -0
- package/src/agent/tools/read_many.ts +27 -0
- package/src/agent/tools/readers.ts +228 -0
- package/src/agent/tools/replace_in_file.ts +32 -0
- package/src/agent/tools/shell.ts +53 -0
- package/src/agent/tools/stat_path.ts +33 -0
- package/src/agent/tools/types.ts +42 -0
- package/src/agent/tools/validate.ts +35 -0
- package/src/agent/tools/write_file.ts +28 -0
- package/src/agent/tools.ts +3 -0
- package/src/app.ts +708 -0
- package/src/cli.ts +335 -0
- package/src/commands/about.ts +16 -0
- package/src/commands/agent.ts +18 -0
- package/src/commands/attach.ts +16 -0
- package/src/commands/bg.ts +9 -0
- package/src/commands/cancel.ts +10 -0
- package/src/commands/clear.ts +9 -0
- package/src/commands/compact.ts +23 -0
- package/src/commands/config.ts +24 -0
- package/src/commands/context.ts +25 -0
- package/src/commands/cost.ts +13 -0
- package/src/commands/cwd.ts +7 -0
- package/src/commands/edit.ts +37 -0
- package/src/commands/effort.ts +49 -0
- package/src/commands/exit.ts +8 -0
- package/src/commands/expand.ts +8 -0
- package/src/commands/help.ts +18 -0
- package/src/commands/index.ts +80 -0
- package/src/commands/jobs.ts +17 -0
- package/src/commands/kill.ts +12 -0
- package/src/commands/login.ts +63 -0
- package/src/commands/logout.ts +45 -0
- package/src/commands/model.ts +50 -0
- package/src/commands/plugin.ts +74 -0
- package/src/commands/provider.ts +39 -0
- package/src/commands/reset.ts +13 -0
- package/src/commands/skills.ts +106 -0
- package/src/commands/steer.ts +13 -0
- package/src/commands/system.ts +20 -0
- package/src/commands/theme.ts +22 -0
- package/src/commands/tools.ts +29 -0
- package/src/commands/whoami.ts +52 -0
- package/src/config/paths.ts +59 -0
- package/src/config/store.ts +293 -0
- package/src/config/themes.ts +77 -0
- package/src/config/trust.ts +183 -0
- package/src/index.ts +31 -0
- package/src/lib/diagnostics.ts +195 -0
- package/src/lib/http.ts +132 -0
- package/src/lib/markdown.ts +32 -0
- package/src/lib/model-meta.ts +138 -0
- package/src/lib/native-tools.ts +32 -0
- package/src/lib/net.ts +204 -0
- package/src/lib/pkg.ts +35 -0
- package/src/lib/platform.ts +57 -0
- package/src/lib/startup.ts +25 -0
- package/src/lib/uiMode.ts +7 -0
- package/src/patch/apply.ts +194 -0
- package/src/patch/parse.ts +131 -0
- package/src/patch/plan.ts +317 -0
- package/src/plugins/manager.ts +101 -0
- package/src/providers/anthropic.ts +119 -0
- package/src/providers/custom.ts +25 -0
- package/src/providers/deepseek.ts +16 -0
- package/src/providers/google.ts +19 -0
- package/src/providers/groq.ts +21 -0
- package/src/providers/index.ts +68 -0
- package/src/providers/mistral.ts +20 -0
- package/src/providers/moonshot.ts +23 -0
- package/src/providers/ollama.ts +22 -0
- package/src/providers/openai.ts +26 -0
- package/src/providers/openaiCompatible.ts +158 -0
- package/src/providers/openrouter.ts +27 -0
- package/src/providers/qwen.ts +24 -0
- package/src/providers/xai.ts +19 -0
- package/src/providers/zhipu.ts +24 -0
- package/src/session/log.ts +374 -0
- package/src/session/projection.ts +308 -0
- package/src/skills/manager.ts +201 -0
- package/src/skills/parser.ts +65 -0
- package/src/types.ts +205 -0
- package/src/ui/Banner.ts +68 -0
- package/src/ui/Message.ts +24 -0
- package/src/ui/Picker.ts +24 -0
- package/src/ui/Prompt.ts +38 -0
- package/src/ui/Spinner.ts +39 -0
- package/src/ui/StatusBar.ts +20 -0
- package/src/ui/TrustGate.ts +26 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
export const UNKNOWN_MODEL_META = {
|
|
5
|
+
contextWindow: 32_768,
|
|
6
|
+
supportsTools: false,
|
|
7
|
+
charsPerToken: 2.5,
|
|
8
|
+
};
|
|
9
|
+
const PROVIDER_META = {
|
|
10
|
+
anthropic: { contextWindow: 1_000_000, supportsTools: true, charsPerToken: 3.8, maxOutputTokens: 64_000 },
|
|
11
|
+
openai: { contextWindow: 400_000, supportsTools: true, charsPerToken: 3.8, maxOutputTokens: 32_000 },
|
|
12
|
+
google: { contextWindow: 1_048_576, supportsTools: true, charsPerToken: 3.6, maxOutputTokens: 65_536 },
|
|
13
|
+
deepseek: { contextWindow: 128_000, supportsTools: true, charsPerToken: 1.7, maxOutputTokens: 8_192 },
|
|
14
|
+
openrouter: { contextWindow: 128_000, supportsTools: true, charsPerToken: 2.5, maxOutputTokens: 16_384 },
|
|
15
|
+
groq: { contextWindow: 128_000, supportsTools: true, charsPerToken: 3.8, maxOutputTokens: 8_192 },
|
|
16
|
+
mistral: { contextWindow: 128_000, supportsTools: true, charsPerToken: 3.8, maxOutputTokens: 8_192 },
|
|
17
|
+
xai: { contextWindow: 256_000, supportsTools: true, charsPerToken: 3.8, maxOutputTokens: 16_384 },
|
|
18
|
+
moonshot: { contextWindow: 256_000, supportsTools: true, charsPerToken: 2.0, maxOutputTokens: 16_384 },
|
|
19
|
+
zhipu: { contextWindow: 128_000, supportsTools: true, charsPerToken: 1.7, maxOutputTokens: 8_192 },
|
|
20
|
+
qwen: { contextWindow: 128_000, supportsTools: true, charsPerToken: 1.7, maxOutputTokens: 8_192 },
|
|
21
|
+
ollama: { contextWindow: 32_768, supportsTools: true, charsPerToken: 2.5, maxOutputTokens: 4_096 },
|
|
22
|
+
custom: { contextWindow: 32_768, supportsTools: false, charsPerToken: 2.5 },
|
|
23
|
+
};
|
|
24
|
+
const MODEL_OVERRIDES = {
|
|
25
|
+
"claude-haiku-5.5": { contextWindow: 200_000 },
|
|
26
|
+
"claude-haiku-4.5": { contextWindow: 200_000 },
|
|
27
|
+
"claude-sonnet-5": { contextWindow: 1_000_000 },
|
|
28
|
+
"claude-opus-5": { contextWindow: 1_000_000 },
|
|
29
|
+
"claude-sonnet-4.6": { contextWindow: 1_000_000 },
|
|
30
|
+
"claude-opus-4.6": { contextWindow: 1_000_000 },
|
|
31
|
+
"gpt-4o": { contextWindow: 128_000 },
|
|
32
|
+
"gpt-4o-mini": { contextWindow: 128_000 },
|
|
33
|
+
"gpt-4.1": { contextWindow: 1_000_000 },
|
|
34
|
+
o3: { contextWindow: 200_000 },
|
|
35
|
+
"o4-mini": { contextWindow: 200_000 },
|
|
36
|
+
"gemini-3.5-flash-lite": { contextWindow: 1_048_576 },
|
|
37
|
+
"gemini-3.1-flash-lite": { contextWindow: 1_048_576 },
|
|
38
|
+
"deepseek-v4-pro": { contextWindow: 128_000, charsPerToken: 1.7 },
|
|
39
|
+
"deepseek-flash": { contextWindow: 128_000, charsPerToken: 1.7 },
|
|
40
|
+
"glm-5.3": { contextWindow: 128_000, charsPerToken: 1.7 },
|
|
41
|
+
"glm-5.2": { contextWindow: 128_000, charsPerToken: 1.7 },
|
|
42
|
+
"qwen3.8-max": { contextWindow: 128_000, charsPerToken: 1.7 },
|
|
43
|
+
"qwen3.8-flash": { contextWindow: 128_000, charsPerToken: 1.7 },
|
|
44
|
+
"kimi-k3": { contextWindow: 256_000, charsPerToken: 2.0 },
|
|
45
|
+
"grok-4.7": { contextWindow: 256_000, charsPerToken: 3.8 },
|
|
46
|
+
"mistral-large-latest": { contextWindow: 128_000, charsPerToken: 3.8 },
|
|
47
|
+
};
|
|
48
|
+
let overrideCache;
|
|
49
|
+
export function modelMetaOverridePath() {
|
|
50
|
+
const root = process.env["FASTCODE_HOME"] ?? join(homedir(), ".fastcode");
|
|
51
|
+
return join(root, "model-meta.json");
|
|
52
|
+
}
|
|
53
|
+
export function clearModelMetaCache() {
|
|
54
|
+
overrideCache = undefined;
|
|
55
|
+
}
|
|
56
|
+
function loadOverrides() {
|
|
57
|
+
if (overrideCache)
|
|
58
|
+
return overrideCache;
|
|
59
|
+
const file = modelMetaOverridePath();
|
|
60
|
+
if (!existsSync(file)) {
|
|
61
|
+
overrideCache = {};
|
|
62
|
+
return overrideCache;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
66
|
+
overrideCache = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
overrideCache = {};
|
|
70
|
+
}
|
|
71
|
+
return overrideCache;
|
|
72
|
+
}
|
|
73
|
+
function usable(meta) {
|
|
74
|
+
return Boolean(meta) && typeof meta === "object";
|
|
75
|
+
}
|
|
76
|
+
function apply(base, ...layers) {
|
|
77
|
+
const next = { ...base };
|
|
78
|
+
for (const layer of layers) {
|
|
79
|
+
if (!usable(layer))
|
|
80
|
+
continue;
|
|
81
|
+
if (typeof layer.contextWindow === "number" && layer.contextWindow > 0)
|
|
82
|
+
next.contextWindow = Math.floor(layer.contextWindow);
|
|
83
|
+
if (typeof layer.charsPerToken === "number" && layer.charsPerToken > 0)
|
|
84
|
+
next.charsPerToken = layer.charsPerToken;
|
|
85
|
+
if (typeof layer.supportsTools === "boolean")
|
|
86
|
+
next.supportsTools = layer.supportsTools;
|
|
87
|
+
if (typeof layer.maxOutputTokens === "number" && layer.maxOutputTokens > 0)
|
|
88
|
+
next.maxOutputTokens = Math.floor(layer.maxOutputTokens);
|
|
89
|
+
}
|
|
90
|
+
return next;
|
|
91
|
+
}
|
|
92
|
+
export function getModelMeta(providerId, model) {
|
|
93
|
+
const provider = providerId && PROVIDER_META[providerId] ? PROVIDER_META[providerId] : UNKNOWN_MODEL_META;
|
|
94
|
+
const overrides = loadOverrides();
|
|
95
|
+
const keys = [
|
|
96
|
+
providerId && model ? `${providerId}/${model}` : undefined,
|
|
97
|
+
providerId && model ? `${providerId}::${model}` : undefined,
|
|
98
|
+
model,
|
|
99
|
+
].filter((key) => Boolean(key));
|
|
100
|
+
const layers = keys.map((key) => overrides[key]);
|
|
101
|
+
return apply(provider, model ? MODEL_OVERRIDES[model] : undefined, ...layers);
|
|
102
|
+
}
|
|
103
|
+
export function estTokens(text, options = {}) {
|
|
104
|
+
if (!text)
|
|
105
|
+
return 0;
|
|
106
|
+
const ratio = typeof options === "number"
|
|
107
|
+
? options
|
|
108
|
+
: options.charsPerToken ?? getModelMeta(options.providerId, options.model).charsPerToken;
|
|
109
|
+
return Math.ceil(text.length / Math.max(0.1, ratio));
|
|
110
|
+
}
|
|
111
|
+
export function estMessagesTokens(messages, options = {}) {
|
|
112
|
+
return messages.reduce((sum, message) => sum + estTokens(message.content ?? "", options), 0);
|
|
113
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NativeToolCall } from "../types.js";
|
|
2
|
+
export type ToolCallAccumulator = Map<number, NativeToolCall>;
|
|
3
|
+
export interface ToolCallDelta {
|
|
4
|
+
index?: number;
|
|
5
|
+
id?: string;
|
|
6
|
+
function?: {
|
|
7
|
+
name?: string;
|
|
8
|
+
arguments?: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare function mergeToolCallDelta(accumulator: ToolCallAccumulator, delta: ToolCallDelta): void;
|
|
12
|
+
export declare function finalizeToolCalls(accumulator: ToolCallAccumulator): NativeToolCall[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function mergeToolCallDelta(accumulator, delta) {
|
|
2
|
+
const index = typeof delta.index === "number" && Number.isFinite(delta.index) ? delta.index : accumulator.size;
|
|
3
|
+
const entry = accumulator.get(index) ?? { id: "", name: "", arguments: "" };
|
|
4
|
+
if (typeof delta.id === "string" && delta.id)
|
|
5
|
+
entry.id = delta.id;
|
|
6
|
+
if (typeof delta.function?.name === "string" && delta.function.name)
|
|
7
|
+
entry.name = delta.function.name;
|
|
8
|
+
if (typeof delta.function?.arguments === "string")
|
|
9
|
+
entry.arguments += delta.function.arguments;
|
|
10
|
+
accumulator.set(index, entry);
|
|
11
|
+
}
|
|
12
|
+
export function finalizeToolCalls(accumulator) {
|
|
13
|
+
return Array.from(accumulator.entries())
|
|
14
|
+
.sort((a, b) => a[0] - b[0])
|
|
15
|
+
.filter(([, call]) => Boolean(call.name))
|
|
16
|
+
.map(([index, call]) => ({
|
|
17
|
+
id: call.id || `call_${index}`,
|
|
18
|
+
name: call.name,
|
|
19
|
+
arguments: call.arguments || "{}",
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ChatChunk } from "../types.js";
|
|
2
|
+
export declare class QuotaError extends Error {
|
|
3
|
+
readonly code = "QUOTA";
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class IncompleteStreamError extends Error {
|
|
7
|
+
readonly partialText: string;
|
|
8
|
+
constructor(message: string, partialText: string);
|
|
9
|
+
}
|
|
10
|
+
export interface RetryRequestOptions {
|
|
11
|
+
retries?: number;
|
|
12
|
+
backoffMs?: number[];
|
|
13
|
+
retryOn?: (error: Error) => boolean;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
export interface RetryStreamOptions {
|
|
17
|
+
clientRequestId?: string;
|
|
18
|
+
lastAcc?: string;
|
|
19
|
+
retries?: number;
|
|
20
|
+
backoffMs?: number[];
|
|
21
|
+
retryOn?: (error: Error) => boolean;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
onRetry?: (error: Error, attempt: number) => void;
|
|
24
|
+
onReset?: () => void;
|
|
25
|
+
}
|
|
26
|
+
export interface StreamChunk extends ChatChunk {
|
|
27
|
+
reset?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function isRetryableError(error: Error): boolean;
|
|
30
|
+
export declare function retryRequest<T>(fn: (attempt: number) => Promise<T>, options?: RetryRequestOptions): Promise<T>;
|
|
31
|
+
export declare function retryStream(send: (options: {
|
|
32
|
+
clientRequestId: string;
|
|
33
|
+
}) => AsyncGenerator<ChatChunk>, options?: RetryStreamOptions): AsyncGenerator<StreamChunk>;
|
package/dist/lib/net.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
export class QuotaError extends Error {
|
|
3
|
+
code = "QUOTA";
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "QuotaError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class IncompleteStreamError extends Error {
|
|
10
|
+
partialText;
|
|
11
|
+
constructor(message, partialText) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "IncompleteStreamError";
|
|
14
|
+
this.partialText = partialText;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const DEFAULT_BACKOFF = [1000, 2000, 4000];
|
|
18
|
+
const STREAM_BACKOFF = [400, 800, 1600];
|
|
19
|
+
export function isRetryableError(error) {
|
|
20
|
+
const message = String(error?.message ?? error);
|
|
21
|
+
if (/\b(401|402|403|404)\b/.test(message))
|
|
22
|
+
return false;
|
|
23
|
+
if (/timeout|timed? ?out|ECONNRESET|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|EPIPE|fetch failed|network|socket hang up|terminated|other side closed|stream ended before done/i.test(message))
|
|
24
|
+
return true;
|
|
25
|
+
return /\b(429|5\d\d)\b/.test(message);
|
|
26
|
+
}
|
|
27
|
+
function isQuotaError(error) {
|
|
28
|
+
return /\b429\b|quota|insufficient|rate limit|billing|balance/i.test(String(error?.message ?? error));
|
|
29
|
+
}
|
|
30
|
+
export async function retryRequest(fn, options = {}) {
|
|
31
|
+
const attempts = Math.max(1, Math.floor(options.retries ?? 3));
|
|
32
|
+
const backoff = options.backoffMs ?? DEFAULT_BACKOFF;
|
|
33
|
+
const retryOn = options.retryOn ?? isRetryableError;
|
|
34
|
+
let lastError;
|
|
35
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
36
|
+
if (options.signal?.aborted)
|
|
37
|
+
throw abortError();
|
|
38
|
+
try {
|
|
39
|
+
return await fn(attempt);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
lastError = toError(err);
|
|
43
|
+
if (attempt >= attempts - 1 || !retryOn(lastError))
|
|
44
|
+
break;
|
|
45
|
+
await delay(backoff[Math.min(attempt, backoff.length - 1)], options.signal);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (lastError && isQuotaError(lastError))
|
|
49
|
+
throw new QuotaError(lastError.message);
|
|
50
|
+
throw lastError ?? new Error("retryRequest failed");
|
|
51
|
+
}
|
|
52
|
+
export async function* retryStream(send, options = {}) {
|
|
53
|
+
const clientRequestId = options.clientRequestId ?? randomUUID();
|
|
54
|
+
const attempts = Math.max(1, Math.floor(options.retries ?? 1) + 1);
|
|
55
|
+
const backoff = options.backoffMs ?? STREAM_BACKOFF;
|
|
56
|
+
const retryOn = options.retryOn ?? isRetryableError;
|
|
57
|
+
let delivered = options.lastAcc ?? "";
|
|
58
|
+
let lastError;
|
|
59
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
60
|
+
if (options.signal?.aborted)
|
|
61
|
+
throw abortError();
|
|
62
|
+
let buffering = delivered.length > 0;
|
|
63
|
+
let pending = "";
|
|
64
|
+
let doneSeen = false;
|
|
65
|
+
let usage;
|
|
66
|
+
let toolCalls;
|
|
67
|
+
try {
|
|
68
|
+
for await (const chunk of send({ clientRequestId })) {
|
|
69
|
+
if (options.signal?.aborted)
|
|
70
|
+
throw abortError();
|
|
71
|
+
if (chunk.usage)
|
|
72
|
+
usage = chunk.usage;
|
|
73
|
+
if (chunk.toolCalls)
|
|
74
|
+
toolCalls = chunk.toolCalls;
|
|
75
|
+
const delta = chunk.delta ?? "";
|
|
76
|
+
if (!delta) {
|
|
77
|
+
if (!buffering && !chunk.done)
|
|
78
|
+
yield chunk;
|
|
79
|
+
if (chunk.done)
|
|
80
|
+
doneSeen = true;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (buffering) {
|
|
84
|
+
pending += delta;
|
|
85
|
+
const matchedPrefix = delivered.startsWith(pending) || pending.startsWith(delivered);
|
|
86
|
+
if (delivered.startsWith(pending)) {
|
|
87
|
+
if (pending.length === delivered.length) {
|
|
88
|
+
buffering = false;
|
|
89
|
+
pending = "";
|
|
90
|
+
}
|
|
91
|
+
if (chunk.done)
|
|
92
|
+
doneSeen = true;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (matchedPrefix) {
|
|
96
|
+
const extra = pending.slice(delivered.length);
|
|
97
|
+
buffering = false;
|
|
98
|
+
pending = "";
|
|
99
|
+
if (extra) {
|
|
100
|
+
delivered += extra;
|
|
101
|
+
yield { ...chunk, delta: extra, done: false };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
buffering = false;
|
|
106
|
+
delivered = pending;
|
|
107
|
+
pending = "";
|
|
108
|
+
options.onReset?.();
|
|
109
|
+
yield { ...chunk, delta: delivered, reset: true, done: false };
|
|
110
|
+
}
|
|
111
|
+
if (chunk.done)
|
|
112
|
+
doneSeen = true;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
delivered += delta;
|
|
116
|
+
yield { ...chunk, done: false };
|
|
117
|
+
if (chunk.done)
|
|
118
|
+
doneSeen = true;
|
|
119
|
+
}
|
|
120
|
+
if (!doneSeen)
|
|
121
|
+
throw new IncompleteStreamError("stream ended before done", delivered);
|
|
122
|
+
yield {
|
|
123
|
+
delta: "",
|
|
124
|
+
done: true,
|
|
125
|
+
...(usage ? { usage } : {}),
|
|
126
|
+
...(toolCalls ? { toolCalls } : {}),
|
|
127
|
+
};
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
lastError = toError(err);
|
|
132
|
+
if (options.signal?.aborted)
|
|
133
|
+
throw abortError();
|
|
134
|
+
if (attempt >= attempts - 1 || !retryOn(lastError)) {
|
|
135
|
+
lastError.partialText = delivered;
|
|
136
|
+
throw lastError;
|
|
137
|
+
}
|
|
138
|
+
options.onRetry?.(lastError, attempt + 1);
|
|
139
|
+
await delay(backoff[Math.min(attempt, backoff.length - 1)], options.signal);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
throw lastError ?? new Error("retryStream failed");
|
|
143
|
+
}
|
|
144
|
+
function delay(ms, signal) {
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
if (signal?.aborted) {
|
|
147
|
+
reject(abortError());
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const timer = setTimeout(() => {
|
|
151
|
+
signal?.removeEventListener("abort", onAbort);
|
|
152
|
+
resolve();
|
|
153
|
+
}, ms);
|
|
154
|
+
const onAbort = () => {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
reject(abortError());
|
|
157
|
+
};
|
|
158
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function abortError() {
|
|
162
|
+
const error = new Error("Aborted");
|
|
163
|
+
error.name = "AbortError";
|
|
164
|
+
return error;
|
|
165
|
+
}
|
|
166
|
+
function toError(value) {
|
|
167
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
168
|
+
}
|
package/dist/lib/pkg.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read this package's package.json (name + version) from any layout.
|
|
3
|
+
*
|
|
4
|
+
* The same source runs three ways, so `import.meta.url` points at different
|
|
5
|
+
* depths; try every plausible relative path and fall back gracefully.
|
|
6
|
+
*
|
|
7
|
+
* - tsx / dev: src/lib/pkg.ts -> ../../package.json
|
|
8
|
+
* - tsc output: dist/lib/pkg.js -> ../../package.json
|
|
9
|
+
* - esbuild bundle: dist/cli.js -> ../package.json
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
const FALLBACK = { name: "@datename/fastcode-termux", version: "0.0.0" };
|
|
13
|
+
const CANDIDATES = ["../package.json", "../../package.json", "../../../package.json"];
|
|
14
|
+
export function readPackageMeta() {
|
|
15
|
+
for (const rel of CANDIDATES) {
|
|
16
|
+
try {
|
|
17
|
+
const meta = JSON.parse(readFileSync(new URL(rel, import.meta.url), "utf8"));
|
|
18
|
+
if (meta && typeof meta.version === "string") {
|
|
19
|
+
return { name: meta.name ?? FALLBACK.name, version: meta.version };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
/* try the next candidate */
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return FALLBACK;
|
|
27
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function detectPlatform(): "darwin" | "linux" | "win32" | "termux" | "other";
|
|
2
|
+
export declare const isTermux: () => boolean;
|
|
3
|
+
export declare const isWindows: () => boolean;
|
|
4
|
+
export declare const isMac: () => boolean;
|
|
5
|
+
/** Human-readable label for the status bar. */
|
|
6
|
+
export declare function platformLabel(): NodeJS.Platform | "Termux" | "Linux" | "macOS" | "Windows";
|
|
7
|
+
export declare function safeHome(): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform detection and small helpers.
|
|
3
|
+
*
|
|
4
|
+
* FastCode targets Termux, Linux, macOS, and Windows. Most of our
|
|
5
|
+
* environment-aware behavior keys off these flags rather than scattering
|
|
6
|
+
* `process.platform === "..."` checks across the codebase.
|
|
7
|
+
*/
|
|
8
|
+
import { platform, homedir } from "node:os";
|
|
9
|
+
export function detectPlatform() {
|
|
10
|
+
// Termux exposes PREFIX=/data/data/com.termux/files/usr. The extra check
|
|
11
|
+
// makes us robust to users who manually export PREFIX for other reasons.
|
|
12
|
+
const prefix = process.env["PREFIX"];
|
|
13
|
+
// Termux exports TERMUX_VERSION; PREFIX=/data/data/com.termux/files/usr is a
|
|
14
|
+
// strong secondary signal. Neither requires the directory to exist yet.
|
|
15
|
+
if (process.platform === "android" ||
|
|
16
|
+
Boolean(process.env["TERMUX_VERSION"]) ||
|
|
17
|
+
(prefix && prefix.includes("com.termux"))) {
|
|
18
|
+
return "termux";
|
|
19
|
+
}
|
|
20
|
+
switch (process.platform) {
|
|
21
|
+
case "linux":
|
|
22
|
+
case "darwin":
|
|
23
|
+
case "win32":
|
|
24
|
+
return process.platform;
|
|
25
|
+
default:
|
|
26
|
+
return "other";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export const isTermux = () => detectPlatform() === "termux";
|
|
30
|
+
export const isWindows = () => process.platform === "win32";
|
|
31
|
+
export const isMac = () => process.platform === "darwin";
|
|
32
|
+
/** Human-readable label for the status bar. */
|
|
33
|
+
export function platformLabel() {
|
|
34
|
+
const p = detectPlatform();
|
|
35
|
+
switch (p) {
|
|
36
|
+
case "termux":
|
|
37
|
+
return "Termux";
|
|
38
|
+
case "linux":
|
|
39
|
+
return "Linux";
|
|
40
|
+
case "darwin":
|
|
41
|
+
return "macOS";
|
|
42
|
+
case "win32":
|
|
43
|
+
return "Windows";
|
|
44
|
+
default:
|
|
45
|
+
return platform();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function safeHome() {
|
|
49
|
+
// os.homedir() can return undefined on weird CI containers; fall back
|
|
50
|
+
// to $HOME / %USERPROFILE% / cwd in that order.
|
|
51
|
+
const fromOs = homedir();
|
|
52
|
+
if (fromOs)
|
|
53
|
+
return fromOs;
|
|
54
|
+
return (process.env["HOME"] ??
|
|
55
|
+
process.env["USERPROFILE"] ??
|
|
56
|
+
process.cwd());
|
|
57
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { resolveApiKey } from "../config/store.js";
|
|
2
|
+
import { getProviderOrThrow } from "../providers/index.js";
|
|
3
|
+
export function createStartupTranscript(config) {
|
|
4
|
+
const messages = [
|
|
5
|
+
`Welcome to FastCode. Provider: **${config.providerId}** · model: **${config.model}**.\n\nType **/help** to list slash commands, or just start chatting.`,
|
|
6
|
+
];
|
|
7
|
+
const provider = getProviderOrThrow(config.providerId);
|
|
8
|
+
if (provider.authEnv.length > 0) {
|
|
9
|
+
const lookup = resolveApiKey(provider.id, config.model, provider.authEnv);
|
|
10
|
+
if (lookup.status === "unbound") {
|
|
11
|
+
messages.push(`Stored ${provider.label} key isn't bound to a model, so it won't be used. ` +
|
|
12
|
+
`Re-bind it with \`/login ${provider.id} ${config.model} <key>\` (or \`/logout ${provider.id}\` to discard it).`);
|
|
13
|
+
}
|
|
14
|
+
else if (lookup.status === "missing") {
|
|
15
|
+
messages.push(`No API key for ${provider.label} · ${config.model}. ` +
|
|
16
|
+
`Run \`/login ${provider.id} ${config.model} <key>\`, or set ${provider.authEnv[0]} (provider-wide, any model).`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return messages.map((content, index) => ({
|
|
20
|
+
id: index + 1,
|
|
21
|
+
role: "system",
|
|
22
|
+
content,
|
|
23
|
+
markdown: true,
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { detectPlatform } from "./platform.js";
|
|
2
|
+
export function shouldUseStableTerminalUi(platform = detectPlatform(), env = process.env) {
|
|
3
|
+
return platform === "termux" || env.FASTCODE_STABLE_UI === "1";
|
|
4
|
+
}
|
|
5
|
+
export function shouldUseLiveStreamingUi(platform = detectPlatform(), env = process.env) {
|
|
6
|
+
return !shouldUseStableTerminalUi(platform, env);
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PatchPlan, PlannedFile } from "./plan.js";
|
|
2
|
+
export interface AppliedFile {
|
|
3
|
+
path: string;
|
|
4
|
+
kind: PlannedFile["kind"];
|
|
5
|
+
diff: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ApplyResult {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
output: string;
|
|
10
|
+
files: AppliedFile[];
|
|
11
|
+
touched: string[];
|
|
12
|
+
removed: string[];
|
|
13
|
+
warnings: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function applyPatchPlan(plan: PatchPlan): ApplyResult;
|