@datename/fastcode-termux 0.1.6 → 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 +67 -16
- 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 +83 -0
- package/dist/agent/approval.js +42 -0
- package/dist/agent/compaction.d.ts +41 -0
- package/dist/agent/compaction.js +270 -0
- package/dist/agent/context.d.ts +29 -0
- package/dist/agent/context.js +62 -0
- package/dist/agent/guards.d.ts +24 -16
- package/dist/agent/guards.js +1167 -18
- package/dist/agent/ignore.d.ts +11 -0
- package/dist/agent/ignore.js +129 -0
- 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 +22 -0
- package/dist/agent/process-tree.js +102 -0
- package/dist/agent/protocol-mode.d.ts +6 -0
- package/dist/agent/protocol-mode.js +51 -0
- package/dist/agent/protocol.d.ts +20 -1
- package/dist/agent/protocol.js +169 -39
- package/dist/agent/registry.d.ts +19 -0
- package/dist/agent/registry.js +247 -0
- package/dist/agent/runner.d.ts +127 -0
- package/dist/agent/runner.js +391 -0
- package/dist/agent/shell-ast.d.ts +11 -0
- package/dist/agent/shell-ast.js +204 -0
- package/dist/agent/shell-session.d.ts +51 -0
- package/dist/agent/shell-session.js +611 -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 +82 -0
- package/dist/agent/tools/append_file.d.ts +2 -0
- package/dist/agent/tools/append_file.js +28 -0
- package/dist/agent/tools/apply_patch.d.ts +10 -0
- package/dist/agent/tools/apply_patch.js +92 -0
- package/dist/agent/tools/copy_file.d.ts +2 -0
- package/dist/agent/tools/copy_file.js +53 -0
- package/dist/agent/tools/delete_file.d.ts +2 -0
- package/dist/agent/tools/delete_file.js +43 -0
- package/dist/agent/tools/grep.d.ts +2 -0
- package/dist/agent/tools/grep.js +54 -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 +26 -0
- package/dist/agent/tools/move_path.d.ts +9 -0
- package/dist/agent/tools/move_path.js +73 -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 +81 -0
- package/dist/agent/tools/read_many.d.ts +2 -0
- package/dist/agent/tools/read_many.js +43 -0
- package/dist/agent/tools/readers.d.ts +58 -0
- package/dist/agent/tools/readers.js +426 -0
- package/dist/agent/tools/replace_in_file.d.ts +2 -0
- package/dist/agent/tools/replace_in_file.js +63 -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 +65 -0
- package/dist/agent/tools/types.js +1 -0
- package/dist/agent/tools/validate.d.ts +19 -0
- package/dist/agent/tools/validate.js +92 -0
- package/dist/agent/tools/write_file.d.ts +2 -0
- package/dist/agent/tools/write_file.js +28 -0
- package/dist/agent/tools.d.ts +3 -1
- package/dist/agent/tools.js +2 -377
- package/dist/app.d.ts +2 -1
- package/dist/app.js +846 -173
- package/dist/cli-main.js +363 -0
- package/dist/cli.js +444 -333
- package/dist/commands/about.js +1 -1
- 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 +1 -0
- package/dist/commands/clear.js +1 -1
- package/dist/commands/compact.d.ts +22 -0
- package/dist/commands/compact.js +34 -0
- package/dist/commands/context.d.ts +7 -0
- package/dist/commands/context.js +25 -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/expand.d.ts +10 -0
- package/dist/commands/expand.js +8 -0
- package/dist/commands/index.js +24 -0
- package/dist/commands/jobs.d.ts +7 -0
- package/dist/commands/jobs.js +17 -0
- package/dist/commands/kill.d.ts +7 -0
- package/dist/commands/kill.js +13 -0
- package/dist/commands/login.d.ts +6 -4
- package/dist/commands/login.js +24 -22
- package/dist/commands/logout.js +8 -16
- package/dist/commands/model.d.ts +1 -0
- package/dist/commands/model.js +10 -22
- package/dist/commands/provider.d.ts +1 -2
- package/dist/commands/provider.js +4 -11
- package/dist/commands/skills.js +34 -4
- package/dist/commands/steer.d.ts +11 -0
- package/dist/commands/steer.js +13 -0
- package/dist/commands/threads.d.ts +9 -0
- package/dist/commands/threads.js +26 -0
- package/dist/commands/whoami.js +6 -5
- package/dist/config/store.d.ts +53 -12
- package/dist/config/store.js +163 -73
- package/dist/config/trust.d.ts +30 -8
- package/dist/config/trust.js +139 -14
- 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 +48 -4
- package/dist/lib/diagnostics.js +90 -8
- package/dist/lib/highlight-subset.d.ts +21 -0
- package/dist/lib/highlight-subset.js +88 -0
- package/dist/lib/http.d.ts +11 -1
- package/dist/lib/http.js +62 -4
- package/dist/lib/lifecycle.d.ts +5 -0
- package/dist/lib/lifecycle.js +62 -0
- package/dist/lib/markdown.js +22 -4
- 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/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/providers/anthropic.d.ts +1 -0
- package/dist/providers/anthropic.js +12 -0
- package/dist/providers/custom.d.ts +9 -0
- package/dist/providers/deepseek.d.ts +10 -0
- package/dist/providers/deepseek.js +3 -2
- package/dist/providers/google.d.ts +9 -0
- package/dist/providers/groq.d.ts +10 -0
- package/dist/providers/groq.js +2 -1
- package/dist/providers/index.d.ts +10 -201
- package/dist/providers/index.js +30 -0
- package/dist/providers/mistral.d.ts +10 -0
- package/dist/providers/mistral.js +2 -1
- package/dist/providers/moonshot.d.ts +10 -0
- package/dist/providers/moonshot.js +2 -1
- package/dist/providers/ollama.d.ts +9 -0
- package/dist/providers/openai.d.ts +10 -0
- package/dist/providers/openai.js +2 -1
- package/dist/providers/openaiCompatible.d.ts +33 -2
- package/dist/providers/openaiCompatible.js +78 -16
- package/dist/providers/openrouter.d.ts +10 -0
- package/dist/providers/openrouter.js +2 -1
- package/dist/providers/qwen.d.ts +10 -0
- package/dist/providers/qwen.js +2 -1
- package/dist/providers/spec.d.ts +12 -0
- package/dist/providers/spec.js +29 -0
- package/dist/providers/xai.d.ts +10 -0
- package/dist/providers/xai.js +2 -1
- package/dist/providers/zhipu.d.ts +10 -0
- package/dist/providers/zhipu.js +2 -1
- package/dist/session/log.d.ts +49 -14
- package/dist/session/log.js +236 -11
- package/dist/session/projection.d.ts +76 -0
- package/dist/session/projection.js +526 -0
- package/dist/skills/manager.d.ts +15 -0
- package/dist/skills/manager.js +135 -18
- package/dist/types.d.ts +75 -1
- package/dist/ui/Message.d.ts +1 -7
- package/dist/ui/Message.js +13 -2
- package/dist/ui/Picker.d.ts +8 -0
- package/dist/ui/Picker.js +24 -0
- package/dist/ui/Prompt.d.ts +1 -2
- package/dist/ui/Prompt.js +2 -2
- package/dist/ui/Spinner.js +11 -5
- package/dist/ui/StatusBar.d.ts +2 -1
- package/dist/ui/StatusBar.js +6 -5
- 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 +94 -0
- package/src/agent/compaction.ts +321 -0
- package/src/agent/context.ts +97 -0
- package/src/agent/guards.ts +1239 -19
- package/src/agent/ignore.ts +147 -0
- package/src/agent/policy/defaults.ts +34 -0
- package/src/agent/policy/egress.ts +335 -0
- package/src/agent/process-tree.ts +114 -0
- package/src/agent/protocol-mode.ts +59 -0
- package/src/agent/protocol.ts +191 -40
- package/src/agent/registry.ts +270 -0
- package/src/agent/runner.ts +486 -0
- package/src/agent/shell-ast.ts +227 -0
- package/src/agent/shell-session.ts +673 -0
- package/src/agent/stream.ts +143 -0
- package/src/agent/tool-schemas.ts +86 -0
- package/src/agent/tools/append_file.ts +30 -0
- package/src/agent/tools/apply_patch.ts +95 -0
- package/src/agent/tools/copy_file.ts +56 -0
- package/src/agent/tools/delete_file.ts +45 -0
- package/src/agent/tools/grep.ts +57 -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 +28 -0
- package/src/agent/tools/move_path.ts +83 -0
- package/src/agent/tools/paths.ts +92 -0
- package/src/agent/tools/pwd.ts +12 -0
- package/src/agent/tools/read_file.ts +84 -0
- package/src/agent/tools/read_many.ts +45 -0
- package/src/agent/tools/readers.ts +491 -0
- package/src/agent/tools/replace_in_file.ts +66 -0
- package/src/agent/tools/shell.ts +53 -0
- package/src/agent/tools/stat_path.ts +33 -0
- package/src/agent/tools/types.ts +84 -0
- package/src/agent/tools/validate.ts +104 -0
- package/src/agent/tools/write_file.ts +30 -0
- package/src/agent/tools.ts +3 -377
- package/src/app.ts +853 -170
- package/src/{cli.ts → cli-main.ts} +64 -9
- package/src/commands/about.ts +1 -1
- 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 +1 -1
- package/src/commands/compact.ts +35 -0
- package/src/commands/context.ts +26 -0
- package/src/commands/edit.ts +37 -0
- package/src/commands/effort.ts +49 -0
- package/src/commands/expand.ts +8 -0
- package/src/commands/index.ts +24 -0
- package/src/commands/jobs.ts +18 -0
- package/src/commands/kill.ts +14 -0
- package/src/commands/login.ts +24 -22
- package/src/commands/logout.ts +8 -16
- package/src/commands/model.ts +10 -22
- package/src/commands/provider.ts +4 -11
- package/src/commands/skills.ts +34 -4
- package/src/commands/steer.ts +13 -0
- package/src/commands/threads.ts +28 -0
- package/src/commands/whoami.ts +6 -5
- package/src/config/store.ts +168 -77
- package/src/config/trust.ts +181 -22
- package/src/headless.ts +225 -0
- package/src/index.ts +19 -3
- package/src/lib/debug.ts +24 -0
- package/src/lib/diagnostics.ts +90 -8
- package/src/lib/highlight-subset.ts +93 -0
- package/src/lib/http.ts +66 -4
- package/src/lib/lifecycle.ts +69 -0
- package/src/lib/markdown.ts +22 -4
- 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/patch/apply.ts +194 -0
- package/src/patch/parse.ts +131 -0
- package/src/patch/plan.ts +317 -0
- package/src/providers/anthropic.ts +13 -1
- package/src/providers/deepseek.ts +3 -2
- package/src/providers/groq.ts +2 -1
- package/src/providers/index.ts +32 -1
- package/src/providers/mistral.ts +2 -1
- package/src/providers/moonshot.ts +2 -1
- package/src/providers/openai.ts +2 -1
- package/src/providers/openaiCompatible.ts +104 -19
- package/src/providers/openrouter.ts +2 -1
- package/src/providers/qwen.ts +2 -1
- package/src/providers/spec.ts +42 -0
- package/src/providers/xai.ts +2 -1
- package/src/providers/zhipu.ts +2 -1
- package/src/session/log.ts +311 -29
- package/src/session/projection.ts +621 -0
- package/src/skills/manager.ts +140 -18
- package/src/types/highlight-shims.d.ts +14 -0
- package/src/types.ts +79 -2
- package/src/ui/Message.ts +13 -2
- package/src/ui/Picker.ts +24 -0
- package/src/ui/Prompt.ts +2 -2
- package/src/ui/Spinner.ts +11 -5
- package/src/ui/StatusBar.ts +6 -5
- package/tsconfig.json +1 -0
- /package/dist/{cli.d.ts → cli-main.d.ts} +0 -0
|
@@ -12,8 +12,11 @@ import { loadConfig, saveConfig, resolveProviderKey } from "./config/store.js";
|
|
|
12
12
|
import { getPaths } from "./config/paths.js";
|
|
13
13
|
import { PROVIDERS, getProvider } from "./providers/index.js";
|
|
14
14
|
import { collectDiagnostics, formatDiagnostics } from "./lib/diagnostics.js";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { probeConnectivity } from "./lib/http.js";
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
17
|
+
import { latestSessionFile, listSessions, projectionPath, readSession, resolveSessionFile } from "./session/log.js";
|
|
18
|
+
import { readProjection, sessionMetaFromFile } from "./session/projection.js";
|
|
19
|
+
import { forgetToolApproval, listToolApprovals, listTrusted, trustProject, untrustProject } from "./config/trust.js";
|
|
17
20
|
const NL = String.fromCharCode(10);
|
|
18
21
|
const HELP = [
|
|
19
22
|
"",
|
|
@@ -37,7 +40,8 @@ const HELP = [
|
|
|
37
40
|
" Trust (first-run safety)",
|
|
38
41
|
" $ fastcode trust [dir] Trust a folder (enables agent tools)",
|
|
39
42
|
" $ fastcode untrust [dir] Revoke trust for a folder",
|
|
40
|
-
" $ fastcode trusted List trusted folders",
|
|
43
|
+
" $ fastcode trusted List trusted folders and remembered approvals",
|
|
44
|
+
" $ fastcode trust --forget <key> Revoke one remembered approval",
|
|
41
45
|
"",
|
|
42
46
|
" Skills & maintenance",
|
|
43
47
|
" $ fastcode skills list Print installed skills",
|
|
@@ -45,6 +49,7 @@ const HELP = [
|
|
|
45
49
|
" $ fastcode skills uninstall <id> Remove an installed skill",
|
|
46
50
|
" $ fastcode skills search <q> Search the registry",
|
|
47
51
|
" $ fastcode doctor Environment diagnostics (Termux/mobile)",
|
|
52
|
+
" $ fastcode doctor --net Also probe registry connectivity",
|
|
48
53
|
" $ fastcode update Check for a newer release",
|
|
49
54
|
"",
|
|
50
55
|
" Options",
|
|
@@ -61,16 +66,26 @@ const HELP = [
|
|
|
61
66
|
" DASHSCOPE_API_KEY) or from /login inside the TUI.",
|
|
62
67
|
" Override the data directory with FASTCODE_HOME.",
|
|
63
68
|
"",
|
|
69
|
+
" Approval UI: --ui tty|native|plain|json (default tty)",
|
|
70
|
+
"",
|
|
64
71
|
].join(NL);
|
|
65
72
|
export async function parseCli(): Promise<any> {
|
|
66
73
|
const cli = meow(HELP, {
|
|
67
74
|
importMeta: import.meta,
|
|
68
75
|
flags: {
|
|
69
76
|
version: { type: "boolean", shortFlag: "v" },
|
|
77
|
+
ui: { type: "string", default: "tty" },
|
|
78
|
+
forget: { type: "string" },
|
|
79
|
+
net: { type: "boolean", default: false },
|
|
70
80
|
},
|
|
71
81
|
autoHelp: true,
|
|
72
82
|
autoVersion: true,
|
|
73
83
|
});
|
|
84
|
+
const uiMode = String(cli.flags.ui ?? "tty").toLowerCase();
|
|
85
|
+
if (!["tty", "native", "plain", "json"].includes(uiMode)) {
|
|
86
|
+
console.error("Unknown --ui mode: " + uiMode + " (use tty, native, plain, or json)");
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
74
89
|
// First positional dictates the sub-command. Anything else is a prompt.
|
|
75
90
|
const [first, ...rest] = cli.input;
|
|
76
91
|
if (first === "provider") {
|
|
@@ -156,13 +171,26 @@ export async function parseCli(): Promise<any> {
|
|
|
156
171
|
return { done: true };
|
|
157
172
|
}
|
|
158
173
|
if (rest[0] === "show") {
|
|
159
|
-
const
|
|
174
|
+
const idOrFile = rest[1] ?? all[0].id;
|
|
175
|
+
const entries = readSession(idOrFile);
|
|
160
176
|
for (const entry of entries) {
|
|
161
177
|
if (entry.type === "message")
|
|
162
178
|
console.log("[" + entry.role + "] " + entry.content);
|
|
179
|
+
else if (entry.type === "user" || entry.type === "assistant_raw")
|
|
180
|
+
console.log("[" + entry.type + "] " + String(entry.content ?? "").slice(0, 300));
|
|
163
181
|
else
|
|
164
182
|
console.log("[" + entry.type + "]");
|
|
165
183
|
}
|
|
184
|
+
const file = resolveSessionFile(idOrFile);
|
|
185
|
+
const projection = readProjection(file);
|
|
186
|
+
console.log("");
|
|
187
|
+
if (projection) {
|
|
188
|
+
console.log("[projection] " + projectionPath(file));
|
|
189
|
+
console.log(" messages: " + projection.messages.length + " · activeFiles: " + projection.activeFiles.length + " · lastFactSeq: " + projection.lastFactSeq);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
console.log("[projection] (none)");
|
|
193
|
+
}
|
|
166
194
|
return { done: true };
|
|
167
195
|
}
|
|
168
196
|
console.log("Sessions (newest first):");
|
|
@@ -177,22 +205,44 @@ export async function parseCli(): Promise<any> {
|
|
|
177
205
|
if (first === "resume") {
|
|
178
206
|
const target = rest[0];
|
|
179
207
|
const file = !target || target === "latest" ? latestSessionFile() : resolveSessionFile(target);
|
|
180
|
-
if (!file) {
|
|
208
|
+
if (!file || !existsSync(file)) {
|
|
181
209
|
console.error("No saved session to resume. Start one with: fastcode");
|
|
182
210
|
process.exit(1);
|
|
183
211
|
}
|
|
212
|
+
const meta = sessionMetaFromFile(file);
|
|
213
|
+
if (meta.cwd) {
|
|
214
|
+
if (existsSync(meta.cwd)) {
|
|
215
|
+
process.chdir(meta.cwd);
|
|
216
|
+
console.log("Working directory: " + meta.cwd);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
console.log("Session directory no longer exists: " + meta.cwd + " (staying in " + process.cwd() + ")");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (meta.providerId)
|
|
223
|
+
console.log("Session model: " + meta.providerId + "::" + meta.model);
|
|
184
224
|
console.log("Resuming " + file);
|
|
185
225
|
return { done: false, resume: file };
|
|
186
226
|
}
|
|
187
227
|
if (first === "trust" || first === "untrust" || first === "trusted") {
|
|
188
228
|
if (first === "trusted") {
|
|
189
229
|
const all = listTrusted();
|
|
190
|
-
if (all.length === 0)
|
|
230
|
+
if (all.length === 0)
|
|
191
231
|
console.log("No trusted folders yet. Run: fastcode trust");
|
|
192
|
-
return { done: true };
|
|
193
|
-
}
|
|
194
232
|
for (const entry of all)
|
|
195
233
|
console.log(" " + entry.path + " (" + entry.addedAt + ")");
|
|
234
|
+
const approvals = listToolApprovals();
|
|
235
|
+
if (approvals.length > 0) {
|
|
236
|
+
console.log("");
|
|
237
|
+
console.log("Remembered tool approvals:");
|
|
238
|
+
for (const entry of approvals)
|
|
239
|
+
console.log(" " + entry.key + " catalog=" + entry.catalogHash);
|
|
240
|
+
}
|
|
241
|
+
return { done: true };
|
|
242
|
+
}
|
|
243
|
+
if (first === "trust" && typeof cli.flags.forget === "string") {
|
|
244
|
+
const key = String(cli.flags.forget);
|
|
245
|
+
console.log(forgetToolApproval(key) ? "Forgot approval: " + key : "No such approval: " + key);
|
|
196
246
|
return { done: true };
|
|
197
247
|
}
|
|
198
248
|
const dir = rest[0] ?? process.cwd();
|
|
@@ -258,6 +308,11 @@ export async function parseCli(): Promise<any> {
|
|
|
258
308
|
}
|
|
259
309
|
if (first === "doctor") {
|
|
260
310
|
console.log(formatDiagnostics(collectDiagnostics()));
|
|
311
|
+
if (cli.flags.net === true) {
|
|
312
|
+
const url = "https://registry.npmjs.org/";
|
|
313
|
+
const reachable = await probeConnectivity(url);
|
|
314
|
+
console.log(` network ${reachable ? "reachable" : "unreachable"} (${url})`);
|
|
315
|
+
}
|
|
261
316
|
return { done: true };
|
|
262
317
|
}
|
|
263
318
|
if (first === "update") {
|
|
@@ -266,7 +321,7 @@ export async function parseCli(): Promise<any> {
|
|
|
266
321
|
}
|
|
267
322
|
// No sub-command → treat all positionals as the initial prompt.
|
|
268
323
|
const prompt = cli.input.length > 0 ? cli.input.join(" ") : undefined;
|
|
269
|
-
return prompt ? { prompt, done: false } : { done: false };
|
|
324
|
+
return prompt ? { prompt, done: false, ui: uiMode } : { done: false, ui: uiMode };
|
|
270
325
|
}
|
|
271
326
|
function compareVersions(a, b) {
|
|
272
327
|
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
package/src/commands/about.ts
CHANGED
|
@@ -9,7 +9,7 @@ export const aboutCommand = {
|
|
|
9
9
|
`Platform: ${platformLabel()} • Node ${process.version}`,
|
|
10
10
|
``,
|
|
11
11
|
`GitHub: https://github.com/dateName/fastcode-termux`,
|
|
12
|
-
`Skills:
|
|
12
|
+
`Skills: configure a registry with /skills registry <url>`,
|
|
13
13
|
];
|
|
14
14
|
return { message: lines.join("\n") };
|
|
15
15
|
},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { pollShellSession } from "../agent/shell-session.js";
|
|
2
|
+
|
|
3
|
+
export const attachCommand = {
|
|
4
|
+
name: "attach",
|
|
5
|
+
description: "Show new output from a background shell session (/attach <id>)",
|
|
6
|
+
async run({ args }) {
|
|
7
|
+
const id = args[0];
|
|
8
|
+
if (!id)
|
|
9
|
+
return { message: "Usage: /attach <session-id> (see /jobs)" };
|
|
10
|
+
const result = await pollShellSession(id, 0);
|
|
11
|
+
if (!result)
|
|
12
|
+
return { message: `Unknown session: ${id}` };
|
|
13
|
+
const status = result.status === "running" ? "running" : `${result.status} (exit ${result.exitCode ?? "?"})`;
|
|
14
|
+
return { message: `[session ${id}] ${status}\n${result.output || "(no new output)"}` };
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const bgCommand = {
|
|
2
|
+
name: "bg",
|
|
3
|
+
description: "Explain backgrounding (long shell commands auto-background after ~2s)",
|
|
4
|
+
run() {
|
|
5
|
+
return {
|
|
6
|
+
message: "Long shell commands already return a session id after ~2s; use /jobs to list them, /attach <id> for new output, /kill <id> to stop.",
|
|
7
|
+
};
|
|
8
|
+
},
|
|
9
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const cancelCommand = {
|
|
2
|
+
name: "cancel",
|
|
3
|
+
description: "Cancel the active agent run (soft keyboard alternative to Esc)",
|
|
4
|
+
run({ state, actions }) {
|
|
5
|
+
if (!state.busy)
|
|
6
|
+
return { message: "Nothing is running." };
|
|
7
|
+
actions.cancelRun();
|
|
8
|
+
return { message: "Cancelling the active run…" };
|
|
9
|
+
},
|
|
10
|
+
};
|
package/src/commands/clear.ts
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { compactMessages, compactionConfigFromEnv } from "../agent/compaction.js";
|
|
2
|
+
import { getModelMeta } from "../lib/model-meta.js";
|
|
3
|
+
|
|
4
|
+
export const compactCommand = {
|
|
5
|
+
name: "compact",
|
|
6
|
+
description: "Force local context compaction now",
|
|
7
|
+
run({ state, actions }) {
|
|
8
|
+
const config = compactionConfigFromEnv();
|
|
9
|
+
if (!config.enabled)
|
|
10
|
+
return { message: "Compaction is disabled by FASTCODE_COMPACTION." };
|
|
11
|
+
const meta = getModelMeta(state.providerId, state.model);
|
|
12
|
+
const result = compactMessages(state.history, { ...config, localThresholdRatio: 0 }, {
|
|
13
|
+
contextWindow: meta.contextWindow,
|
|
14
|
+
providerId: state.providerId,
|
|
15
|
+
model: state.model,
|
|
16
|
+
force: true,
|
|
17
|
+
});
|
|
18
|
+
if (!result.compacted)
|
|
19
|
+
return { message: "Nothing to compact (history is already small)." };
|
|
20
|
+
actions.replaceHistory(result.messages);
|
|
21
|
+
return {
|
|
22
|
+
message: `[context compacted] ${result.beforeTokens} → ${result.afterTokens} tokens (${result.droppedMessages} messages summarized).` +
|
|
23
|
+
(result.summaryFile ? `\nSummary: ${result.summaryFile}` : ""),
|
|
24
|
+
compacted: {
|
|
25
|
+
summaryRef: result.summaryFile,
|
|
26
|
+
source: "local",
|
|
27
|
+
beforeTokens: result.beforeTokens,
|
|
28
|
+
afterTokens: result.afterTokens,
|
|
29
|
+
droppedMessages: result.droppedMessages,
|
|
30
|
+
keepMessages: result.keptMessages,
|
|
31
|
+
reason: "manual",
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { compactionConfigFromEnv, heapExceeded, localThresholdTokens } from "../agent/compaction.js";
|
|
2
|
+
import { estMessagesTokens, getModelMeta } from "../lib/model-meta.js";
|
|
3
|
+
|
|
4
|
+
export const contextCommand = {
|
|
5
|
+
name: "context",
|
|
6
|
+
description: "Show estimated context tokens, compaction threshold, and heap usage",
|
|
7
|
+
run(ctx) {
|
|
8
|
+
const meta = getModelMeta(ctx.state.providerId, ctx.state.model);
|
|
9
|
+
const config = compactionConfigFromEnv();
|
|
10
|
+
const tokens = estMessagesTokens(ctx.state.history, {
|
|
11
|
+
providerId: ctx.state.providerId,
|
|
12
|
+
model: ctx.state.model,
|
|
13
|
+
});
|
|
14
|
+
const threshold = localThresholdTokens(config, meta.contextWindow);
|
|
15
|
+
const percent = meta.contextWindow > 0 ? (tokens / meta.contextWindow) * 100 : 0;
|
|
16
|
+
const heapMb = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
17
|
+
return {
|
|
18
|
+
message: [
|
|
19
|
+
`Context: ~${tokens.toLocaleString()} / ${meta.contextWindow.toLocaleString()} tokens (${percent.toFixed(1)}%)`,
|
|
20
|
+
`Compaction threshold: ${threshold.toLocaleString()} tokens (${Math.round(config.localThresholdRatio * 100)}%)${config.enabled ? "" : " [disabled]"}`,
|
|
21
|
+
`Heap: ${heapMb.toFixed(1)} MB / ${config.heapLimitMb} MB${heapExceeded(config) ? " [forced compaction]" : ""}`,
|
|
22
|
+
`Messages: ${ctx.state.history.length} · chars/token: ~${meta.charsPerToken}`,
|
|
23
|
+
].join("\n"),
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
export const editCommand = {
|
|
7
|
+
name: "edit",
|
|
8
|
+
description: "Edit the prompt in $EDITOR (works around Ink IME issues on Termux)",
|
|
9
|
+
run({ state, actions }) {
|
|
10
|
+
const file = join(tmpdir(), `fastcode-edit-${Date.now()}.md`);
|
|
11
|
+
writeFileSync(file, state.draft ?? "", "utf8");
|
|
12
|
+
const editor = process.env["EDITOR"] || process.env["VISUAL"] || "nano";
|
|
13
|
+
const wasRaw = Boolean(process.stdin.isTTY && process.stdin.isRaw);
|
|
14
|
+
try {
|
|
15
|
+
if (wasRaw)
|
|
16
|
+
process.stdin.setRawMode(false);
|
|
17
|
+
const result = spawnSync(editor, [file], { stdio: "inherit" });
|
|
18
|
+
if (result.error)
|
|
19
|
+
return { message: `Could not launch ${editor}: ${result.error.message}` };
|
|
20
|
+
const text = readFileSync(file, "utf8").trim();
|
|
21
|
+
if (!text)
|
|
22
|
+
return { message: "Editor closed with no text." };
|
|
23
|
+
actions.setInput(text);
|
|
24
|
+
return { message: "Draft loaded into the prompt; press Enter to send." };
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
if (wasRaw)
|
|
28
|
+
process.stdin.setRawMode(true);
|
|
29
|
+
try {
|
|
30
|
+
unlinkSync(file);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// temp file may already be gone
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { getProvider } from "../providers/index.js";
|
|
2
|
+
import type { ReasoningEffort } from "../types.js";
|
|
3
|
+
const EFFORTS: ReasoningEffort[] = ["auto", "low", "medium", "high", "max"];
|
|
4
|
+
const HINTS: Record<ReasoningEffort, string> = {
|
|
5
|
+
auto: "provider default (send nothing)",
|
|
6
|
+
low: "fast, minimal thinking",
|
|
7
|
+
medium: "balanced",
|
|
8
|
+
high: "deeper reasoning, slower",
|
|
9
|
+
max: "maximum budget where supported",
|
|
10
|
+
};
|
|
11
|
+
export const effortCommand = {
|
|
12
|
+
name: "effort",
|
|
13
|
+
description: "Set thinking effort (auto/low/medium/high/max). Usage: /effort [level]",
|
|
14
|
+
run({ args, state, actions }) {
|
|
15
|
+
const provider = getProvider(state.providerId);
|
|
16
|
+
const supported = provider?.supportsReasoning?.(state.model) ?? false;
|
|
17
|
+
const current = (state.reasoningEffort ?? "auto");
|
|
18
|
+
const apply = (value: ReasoningEffort) => {
|
|
19
|
+
actions.setReasoningEffort(value);
|
|
20
|
+
return describe(value, provider, supported);
|
|
21
|
+
};
|
|
22
|
+
if (args.length > 0) {
|
|
23
|
+
const value = args[0].toLowerCase();
|
|
24
|
+
if (!EFFORTS.includes(value as ReasoningEffort)) {
|
|
25
|
+
return { message: `Unknown effort "${args[0]}". Choose: ${EFFORTS.join(", ")}.` };
|
|
26
|
+
}
|
|
27
|
+
return { message: apply(value as ReasoningEffort) };
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
message: `Thinking effort: ${current}${supported ? "" : " (this provider has no thinking parameter)"}`,
|
|
31
|
+
picker: {
|
|
32
|
+
title: `Thinking effort · ${provider?.label ?? state.providerId}`,
|
|
33
|
+
current,
|
|
34
|
+
items: EFFORTS.map((value) => ({ value, label: value, hint: HINTS[value] })),
|
|
35
|
+
onSelect: (value) => actions.pushSystemMessage(apply(value as ReasoningEffort)),
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
function describe(value, provider, supported) {
|
|
41
|
+
if (value === "auto") {
|
|
42
|
+
return "Thinking effort: auto — the provider's default is used (no thinking parameter is sent).";
|
|
43
|
+
}
|
|
44
|
+
if (!supported) {
|
|
45
|
+
return `Thinking effort saved as ${value}, but ${provider?.label ?? "this provider"} does not expose a thinking parameter; ` +
|
|
46
|
+
"requests will omit it. Switch to a reasoning-capable model/provider to use it.";
|
|
47
|
+
}
|
|
48
|
+
return `Thinking effort: ${value}. The next request will carry the provider-specific thinking parameter.`;
|
|
49
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const expandCommand = {
|
|
2
|
+
name: "expand",
|
|
3
|
+
description: "Toggle full tool output in the transcript (stable UI defaults to collapsed)",
|
|
4
|
+
run({ state, actions }) {
|
|
5
|
+
actions.setExpanded(!state.expanded);
|
|
6
|
+
return { message: state.expanded ? "Tool output: collapsed." : "Tool output: expanded (new output only)." };
|
|
7
|
+
},
|
|
8
|
+
};
|
package/src/commands/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { clearCommand } from "./clear.js";
|
|
|
7
7
|
import { exitCommand } from "./exit.js";
|
|
8
8
|
import { modelCommand } from "./model.js";
|
|
9
9
|
import { providerCommand } from "./provider.js";
|
|
10
|
+
import { effortCommand } from "./effort.js";
|
|
10
11
|
import { loginCommand } from "./login.js";
|
|
11
12
|
import { logoutCommand } from "./logout.js";
|
|
12
13
|
import { skillsCommand } from "./skills.js";
|
|
@@ -17,6 +18,17 @@ import { systemCommand } from "./system.js";
|
|
|
17
18
|
import { aboutCommand } from "./about.js";
|
|
18
19
|
import { configCommand } from "./config.js";
|
|
19
20
|
import { whoamiCommand } from "./whoami.js";
|
|
21
|
+
import { contextCommand } from "./context.js";
|
|
22
|
+
import { jobsCommand } from "./jobs.js";
|
|
23
|
+
import { killCommand } from "./kill.js";
|
|
24
|
+
import { cancelCommand } from "./cancel.js";
|
|
25
|
+
import { steerCommand } from "./steer.js";
|
|
26
|
+
import { compactCommand } from "./compact.js";
|
|
27
|
+
import { expandCommand } from "./expand.js";
|
|
28
|
+
import { attachCommand } from "./attach.js";
|
|
29
|
+
import { bgCommand } from "./bg.js";
|
|
30
|
+
import { editCommand } from "./edit.js";
|
|
31
|
+
import { threadsCommand } from "./threads.js";
|
|
20
32
|
export const COMMANDS = [
|
|
21
33
|
helpCommand,
|
|
22
34
|
agentCommand,
|
|
@@ -27,6 +39,7 @@ export const COMMANDS = [
|
|
|
27
39
|
exitCommand,
|
|
28
40
|
modelCommand,
|
|
29
41
|
providerCommand,
|
|
42
|
+
effortCommand,
|
|
30
43
|
loginCommand,
|
|
31
44
|
logoutCommand,
|
|
32
45
|
skillsCommand,
|
|
@@ -37,6 +50,17 @@ export const COMMANDS = [
|
|
|
37
50
|
aboutCommand,
|
|
38
51
|
configCommand,
|
|
39
52
|
whoamiCommand,
|
|
53
|
+
contextCommand,
|
|
54
|
+
jobsCommand,
|
|
55
|
+
killCommand,
|
|
56
|
+
cancelCommand,
|
|
57
|
+
steerCommand,
|
|
58
|
+
compactCommand,
|
|
59
|
+
expandCommand,
|
|
60
|
+
attachCommand,
|
|
61
|
+
bgCommand,
|
|
62
|
+
editCommand,
|
|
63
|
+
threadsCommand,
|
|
40
64
|
];
|
|
41
65
|
export function findCommand(name) {
|
|
42
66
|
const lower = name.toLowerCase();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { listShellSessions } from "../agent/shell-session.js";
|
|
2
|
+
|
|
3
|
+
export const jobsCommand = {
|
|
4
|
+
name: "jobs",
|
|
5
|
+
description: "List background shell sessions",
|
|
6
|
+
run() {
|
|
7
|
+
const jobs = listShellSessions();
|
|
8
|
+
if (jobs.length === 0)
|
|
9
|
+
return { message: "No shell sessions. Long commands return a session id automatically." };
|
|
10
|
+
const lines = jobs.map((job) => {
|
|
11
|
+
const exit = job.exitCode === undefined || job.exitCode === null ? "" : ` exit=${job.exitCode}`;
|
|
12
|
+
const age = Math.max(0, Math.round((Date.now() - job.startedAt) / 1000));
|
|
13
|
+
const command = job.command.length > 80 ? job.command.slice(0, 77) + "…" : job.command;
|
|
14
|
+
return `${job.id} ${job.status}${exit} ${age}s ${(job.bytes / 1024).toFixed(1)}KB ${command}`;
|
|
15
|
+
});
|
|
16
|
+
return { message: ["Shell sessions:", ...lines, "", "Kill one with: /kill <id>", "Completed sessions are pruned after 10 minutes; resident pool cap is 2."].join("\n") };
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { killShellSession } from "../agent/shell-session.js";
|
|
2
|
+
|
|
3
|
+
export const killCommand = {
|
|
4
|
+
name: "kill",
|
|
5
|
+
description: "Kill a background shell session (/kill <id>)",
|
|
6
|
+
run(ctx) {
|
|
7
|
+
const id = ctx.args[0];
|
|
8
|
+
if (!id)
|
|
9
|
+
return { message: "Usage: /kill <session-id> (see /jobs)" };
|
|
10
|
+
return { message: killShellSession(id)
|
|
11
|
+
? `Killed ${id} (process group killed; setsid descendants may escape).`
|
|
12
|
+
: `No running session named ${id}.` };
|
|
13
|
+
},
|
|
14
|
+
};
|
package/src/commands/login.ts
CHANGED
|
@@ -2,8 +2,10 @@ import { getProvider, PROVIDERS } from "../providers/index.js";
|
|
|
2
2
|
import { maskKey, setCredential } from "../config/store.js";
|
|
3
3
|
import { getPaths } from "../config/paths.js";
|
|
4
4
|
/**
|
|
5
|
-
* Save a
|
|
6
|
-
*
|
|
5
|
+
* Save a provider-wide key and describe it for the terminal. `model` is kept
|
|
6
|
+
* for the `/login provider model key` one-liner and only recorded as the
|
|
7
|
+
* credential's origin — the key itself is reused by every model of that
|
|
8
|
+
* provider. Shared by the `/login` command and the guided flow in the app.
|
|
7
9
|
*/
|
|
8
10
|
export function saveScopedKey(providerId, providerLabel, model, key) {
|
|
9
11
|
const trimmed = key.trim();
|
|
@@ -12,20 +14,32 @@ export function saveScopedKey(providerId, providerLabel, model, key) {
|
|
|
12
14
|
return [
|
|
13
15
|
"Saved API key",
|
|
14
16
|
` provider ${providerLabel} (${providerId})`,
|
|
15
|
-
`
|
|
17
|
+
` scope all ${providerId} models (saved from ${model || "unknown"})`,
|
|
16
18
|
` key ${maskKey(trimmed)}`,
|
|
17
19
|
` stored ${paths.auth} (mode 0600)`,
|
|
18
20
|
"",
|
|
19
|
-
`Remove with /logout ${providerId}
|
|
21
|
+
`Remove with /logout ${providerId}`,
|
|
20
22
|
].join("\n");
|
|
21
23
|
}
|
|
24
|
+
export function maskLoginEcho(args) {
|
|
25
|
+
const parts = ["/login", args[0] ?? ""];
|
|
26
|
+
if (args.length === 2) {
|
|
27
|
+
parts.push("********");
|
|
28
|
+
}
|
|
29
|
+
else if (args.length >= 3) {
|
|
30
|
+
parts.push(args[1]);
|
|
31
|
+
for (let i = 2; i < args.length; i += 1)
|
|
32
|
+
parts.push("********");
|
|
33
|
+
}
|
|
34
|
+
return parts.filter((part) => part !== "").join(" ");
|
|
35
|
+
}
|
|
22
36
|
export const loginCommand = {
|
|
23
37
|
name: "login",
|
|
24
|
-
description: "Bind
|
|
25
|
-
run({ args,
|
|
38
|
+
description: "Bind a provider-wide API key. Usage: /login [provider] [model] [key]",
|
|
39
|
+
run({ args, actions }) {
|
|
26
40
|
if (args.length === 0) {
|
|
27
|
-
actions.startLogin({});
|
|
28
|
-
return { message:
|
|
41
|
+
actions.startLogin({ flow: "login" });
|
|
42
|
+
return { message: "Configure a provider: choose a provider and model, then enter its API key." };
|
|
29
43
|
}
|
|
30
44
|
const provider = getProvider(args[0]);
|
|
31
45
|
if (!provider) {
|
|
@@ -36,26 +50,14 @@ export const loginCommand = {
|
|
|
36
50
|
}
|
|
37
51
|
if (args.length === 1) {
|
|
38
52
|
actions.startLogin({ provider: provider.id });
|
|
39
|
-
return { message: `Provider: ${provider.label} (${provider.id}).\
|
|
53
|
+
return { message: `Provider: ${provider.label} (${provider.id}).\nThe key will apply to every model of this provider.` };
|
|
40
54
|
}
|
|
41
55
|
const model = args[1];
|
|
42
56
|
if (args.length === 2) {
|
|
43
57
|
actions.startLogin({ provider: provider.id, model });
|
|
44
|
-
return { message: `Provider: ${provider.id} · model: ${model}.\nNow paste the API key (input is masked).` };
|
|
58
|
+
return { message: `Provider: ${provider.id} · saved from model: ${model}.\nNow paste the API key (input is masked).` };
|
|
45
59
|
}
|
|
46
60
|
const key = args.slice(2).join(" ");
|
|
47
61
|
return { message: saveScopedKey(provider.id, provider.label, model, key) };
|
|
48
62
|
},
|
|
49
|
-
};
|
|
50
|
-
function introText(state) {
|
|
51
|
-
const env = getProvider(state.providerId)?.authEnv?.[0] ?? "PROVIDER_API_KEY";
|
|
52
|
-
return [
|
|
53
|
-
"Bind an API key to a specific provider + model.",
|
|
54
|
-
"",
|
|
55
|
-
` current scope: ${state.providerId} · ${state.model}`,
|
|
56
|
-
"",
|
|
57
|
-
"FastCode will ask for the provider, then the model, then the key.",
|
|
58
|
-
" one-liner: /login <provider> <model> <key>",
|
|
59
|
-
` or set: export ${env}=<key> (provider-wide, any model)`,
|
|
60
|
-
].join("\n");
|
|
61
63
|
}
|
package/src/commands/logout.ts
CHANGED
|
@@ -8,11 +8,11 @@ export const logoutCommand = {
|
|
|
8
8
|
const credentials = listCredentials();
|
|
9
9
|
if (credentials.length === 0)
|
|
10
10
|
return { message: "No stored keys. Use /login to add one." };
|
|
11
|
-
const lines = ["Stored keys (provider
|
|
11
|
+
const lines = ["Stored keys (provider-wide):"];
|
|
12
12
|
for (const c of credentials)
|
|
13
|
-
lines.push(` ${c.provider}
|
|
13
|
+
lines.push(` ${c.provider} ${maskKey(c.key)}`);
|
|
14
14
|
lines.push("");
|
|
15
|
-
lines.push("Remove: /logout <provider>
|
|
15
|
+
lines.push("Remove: /logout <provider>");
|
|
16
16
|
lines.push(" /logout <provider> --all");
|
|
17
17
|
return { message: lines.join("\n") };
|
|
18
18
|
}
|
|
@@ -29,25 +29,17 @@ export const logoutCommand = {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
if (args.length >= 2) {
|
|
32
|
-
const
|
|
33
|
-
const ok = removeCredential(provider.id, model);
|
|
32
|
+
const ok = removeCredential(provider.id);
|
|
34
33
|
return {
|
|
35
34
|
message: ok
|
|
36
|
-
? `Removed stored key for ${provider.id}
|
|
37
|
-
: `No stored key for ${provider.id}
|
|
35
|
+
? `Removed the stored key for ${provider.id} (applies to all its models).`
|
|
36
|
+
: `No stored key for ${provider.id}.`,
|
|
38
37
|
};
|
|
39
38
|
}
|
|
40
39
|
const forProvider = listCredentials().filter((c) => c.provider === provider.id);
|
|
41
40
|
if (forProvider.length === 0)
|
|
42
41
|
return { message: `No stored key for ${provider.label}.` };
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return { message: `Removed stored key for ${forProvider[0].provider}::${forProvider[0].model}.` };
|
|
46
|
-
}
|
|
47
|
-
const lines = [`${provider.label} has ${forProvider.length} stored keys — pick one to remove:`];
|
|
48
|
-
for (const c of forProvider)
|
|
49
|
-
lines.push(` /logout ${c.provider} ${c.model} (${maskKey(c.key)})`);
|
|
50
|
-
lines.push(` /logout ${provider.id} --all (remove all)`);
|
|
51
|
-
return { message: lines.join("\n") };
|
|
42
|
+
removeCredential(provider.id);
|
|
43
|
+
return { message: `Removed the stored key for ${provider.label} (all models).` };
|
|
52
44
|
},
|
|
53
45
|
};
|
package/src/commands/model.ts
CHANGED
|
@@ -1,30 +1,18 @@
|
|
|
1
1
|
import { getProviderOrThrow } from "../providers/index.js";
|
|
2
|
-
|
|
2
|
+
export function isValidModelId(value) {
|
|
3
|
+
return /^[A-Za-z0-9][A-Za-z0-9._:/+-]{0,199}$/.test(value);
|
|
4
|
+
}
|
|
3
5
|
export const modelCommand = {
|
|
4
6
|
name: "model",
|
|
5
|
-
description: "
|
|
7
|
+
description: "Pick a model interactively, or switch with /model <id>",
|
|
6
8
|
async run({ args, state, actions }) {
|
|
7
9
|
const provider = getProviderOrThrow(state.providerId);
|
|
8
|
-
if (args.length
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return { message: `No models available from ${provider.label}.` };
|
|
13
|
-
}
|
|
14
|
-
const lines = [`Models for ${provider.label}:`];
|
|
15
|
-
for (const m of models.slice(0, 50)) {
|
|
16
|
-
const marker = m.id === state.model ? "● " : " ";
|
|
17
|
-
const label = m.label ? ` — ${m.label}` : "";
|
|
18
|
-
lines.push(`${marker}${m.id}${label}`);
|
|
19
|
-
}
|
|
20
|
-
if (models.length > 50)
|
|
21
|
-
lines.push(` … and ${models.length - 50} more.`);
|
|
22
|
-
lines.push("");
|
|
23
|
-
lines.push("Switch with `/model <id>`.");
|
|
24
|
-
return { message: lines.join("\n") };
|
|
10
|
+
if (args.length > 0) {
|
|
11
|
+
const id = args.join(" ");
|
|
12
|
+
actions.setModel(id);
|
|
13
|
+
return { message: `Model set to ${id}.` };
|
|
25
14
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return { message: `Model set to ${id}.` };
|
|
15
|
+
actions.startLogin({ flow: "model", provider: provider.id, model: state.model });
|
|
16
|
+
return { message: `Configure ${provider.label}: choose a model, then enter its API key.` };
|
|
29
17
|
},
|
|
30
18
|
};
|
package/src/commands/provider.ts
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getProvider } from "../providers/index.js";
|
|
2
2
|
export const providerCommand = {
|
|
3
3
|
name: "provider",
|
|
4
4
|
description: "List or switch the active LLM provider. Usage: /provider [id]",
|
|
5
|
-
run({ args, actions
|
|
5
|
+
run({ args, actions }) {
|
|
6
6
|
if (args.length === 0) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const marker = p.id === state.providerId ? "● " : " ";
|
|
10
|
-
const auth = p.authEnv.length === 0 ? "(no key needed)" : `[${p.authEnv.join(", ")}]`;
|
|
11
|
-
lines.push(`${marker}${p.id.padEnd(12)} ${p.label} ${auth}`);
|
|
12
|
-
}
|
|
13
|
-
lines.push("");
|
|
14
|
-
lines.push("Switch with `/provider <id>` (e.g. `/provider openrouter`).");
|
|
15
|
-
return { message: lines.join("\n") };
|
|
7
|
+
actions.startLogin({ flow: "provider" });
|
|
8
|
+
return { message: "Configure a provider: choose a provider and model, then enter its API key." };
|
|
16
9
|
}
|
|
17
10
|
const target = args[0];
|
|
18
11
|
const next = getProvider(target);
|