@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
package/dist/config/trust.d.ts
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
1
|
+
export interface TrustPathEntry {
|
|
2
|
+
grantedAt: string;
|
|
3
|
+
lastUsed: string;
|
|
4
|
+
}
|
|
5
|
+
export interface TrustToolEntry extends TrustPathEntry {
|
|
6
|
+
catalogHash: string;
|
|
7
|
+
}
|
|
8
|
+
export interface TrustStore {
|
|
9
|
+
version: 1;
|
|
10
|
+
tools: Record<string, TrustToolEntry>;
|
|
11
|
+
paths: Record<string, TrustPathEntry>;
|
|
12
|
+
}
|
|
13
|
+
export declare function normalizeProjectPath(dir: string): string;
|
|
14
|
+
export declare function loadTrust(): TrustStore;
|
|
5
15
|
export declare function trustAllEnabled(): boolean;
|
|
6
|
-
export declare function isTrusted(dir:
|
|
7
|
-
export declare function trustProject(dir:
|
|
8
|
-
export declare function untrustProject(dir:
|
|
9
|
-
export declare function listTrusted():
|
|
16
|
+
export declare function isTrusted(dir: string): boolean;
|
|
17
|
+
export declare function trustProject(dir: string): string;
|
|
18
|
+
export declare function untrustProject(dir: string): boolean;
|
|
19
|
+
export declare function listTrusted(): Array<{
|
|
20
|
+
path: string;
|
|
21
|
+
addedAt: string;
|
|
22
|
+
}>;
|
|
23
|
+
/** Check a tool/fingerprint approval bound to the current tool catalog. */
|
|
24
|
+
export declare function isToolApproved(key: string, catalogHash: string): boolean;
|
|
25
|
+
export declare function rememberToolApproval(key: string, catalogHash: string): void;
|
|
26
|
+
export declare function forgetToolApproval(key: string): boolean;
|
|
27
|
+
export declare function listToolApprovals(): Array<{
|
|
28
|
+
key: string;
|
|
29
|
+
grantedAt: string;
|
|
30
|
+
catalogHash: string;
|
|
31
|
+
}>;
|
package/dist/config/trust.js
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* FASTCODE_TRUST_ALL=1 marks every directory trusted (scripts, CI).
|
|
9
9
|
*/
|
|
10
|
-
import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { existsSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { resolve } from "node:path";
|
|
12
12
|
import { ensureDir, getPaths } from "./paths.js";
|
|
13
13
|
const TRUTHY = ["1", "true", "yes", "on"];
|
|
14
|
+
let cache;
|
|
14
15
|
export function normalizeProjectPath(dir) {
|
|
15
16
|
const absolute = resolve(dir);
|
|
16
17
|
try {
|
|
@@ -20,22 +21,117 @@ export function normalizeProjectPath(dir) {
|
|
|
20
21
|
return absolute;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
24
|
+
function emptyStore() {
|
|
25
|
+
return { version: 1, tools: {}, paths: {} };
|
|
26
|
+
}
|
|
27
|
+
function normalizePaths(raw) {
|
|
28
|
+
const paths = {};
|
|
29
|
+
if (!raw || typeof raw !== "object")
|
|
30
|
+
return paths;
|
|
31
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
32
|
+
if (!key || !value || typeof value !== "object")
|
|
33
|
+
continue;
|
|
34
|
+
paths[key] = {
|
|
35
|
+
grantedAt: typeof value.grantedAt === "string" ? value.grantedAt : "",
|
|
36
|
+
lastUsed: typeof value.lastUsed === "string" ? value.lastUsed : "",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return paths;
|
|
40
|
+
}
|
|
41
|
+
function normalizeTools(raw) {
|
|
42
|
+
const tools = {};
|
|
43
|
+
if (!raw || typeof raw !== "object")
|
|
44
|
+
return tools;
|
|
45
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
46
|
+
if (!key || !value || typeof value !== "object" || typeof value.catalogHash !== "string")
|
|
47
|
+
continue;
|
|
48
|
+
tools[key] = {
|
|
49
|
+
grantedAt: typeof value.grantedAt === "string" ? value.grantedAt : "",
|
|
50
|
+
lastUsed: typeof value.lastUsed === "string" ? value.lastUsed : "",
|
|
51
|
+
catalogHash: value.catalogHash,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return tools;
|
|
55
|
+
}
|
|
56
|
+
function migrateLegacy(raw) {
|
|
57
|
+
if (!raw || typeof raw !== "object" || !Array.isArray(raw.trusted))
|
|
58
|
+
return undefined;
|
|
59
|
+
const store = emptyStore();
|
|
60
|
+
for (const entry of raw.trusted) {
|
|
61
|
+
if (!entry || typeof entry.path !== "string" || !entry.path)
|
|
62
|
+
continue;
|
|
63
|
+
const at = typeof entry.addedAt === "string" ? entry.addedAt : new Date().toISOString();
|
|
64
|
+
store.paths[entry.path] = { grantedAt: at, lastUsed: at };
|
|
65
|
+
}
|
|
66
|
+
return store;
|
|
67
|
+
}
|
|
68
|
+
function readStore(file) {
|
|
69
|
+
if (!existsSync(file))
|
|
70
|
+
return emptyStore();
|
|
71
|
+
try {
|
|
72
|
+
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
73
|
+
if (raw && raw.version === 1) {
|
|
74
|
+
return {
|
|
75
|
+
version: 1,
|
|
76
|
+
tools: normalizeTools(raw.tools),
|
|
77
|
+
paths: normalizePaths(raw.paths),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const migrated = migrateLegacy(raw);
|
|
81
|
+
if (!migrated)
|
|
82
|
+
return emptyStore();
|
|
83
|
+
try {
|
|
84
|
+
writeFileSync(file, JSON.stringify(migrated, null, 2), "utf8");
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// A read-only home still gets the migrated view in memory.
|
|
88
|
+
}
|
|
89
|
+
return migrated;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return emptyStore();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
23
95
|
export function loadTrust() {
|
|
24
96
|
const { trust } = getPaths();
|
|
25
|
-
|
|
26
|
-
return { trusted: [] };
|
|
97
|
+
let key = "missing";
|
|
27
98
|
try {
|
|
28
|
-
const
|
|
29
|
-
|
|
99
|
+
const info = statSync(trust);
|
|
100
|
+
key = `${info.mtimeMs}:${info.size}`;
|
|
30
101
|
}
|
|
31
102
|
catch {
|
|
32
|
-
|
|
103
|
+
// Missing file: cache the empty store until it appears.
|
|
33
104
|
}
|
|
105
|
+
if (cache && cache.file === trust && cache.key === key)
|
|
106
|
+
return cache.state;
|
|
107
|
+
const state = readStore(trust);
|
|
108
|
+
cache = { file: trust, key, state };
|
|
109
|
+
return state;
|
|
34
110
|
}
|
|
35
111
|
function saveTrust(state) {
|
|
36
112
|
const paths = getPaths();
|
|
37
113
|
ensureDir(paths.root);
|
|
38
|
-
|
|
114
|
+
const tmp = paths.trust + "." + process.pid + ".tmp";
|
|
115
|
+
try {
|
|
116
|
+
writeFileSync(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
117
|
+
renameSync(tmp, paths.trust);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
try {
|
|
121
|
+
rmSync(tmp, { force: true });
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// best effort
|
|
125
|
+
}
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const info = statSync(paths.trust);
|
|
130
|
+
cache = { file: paths.trust, key: `${info.mtimeMs}:${info.size}`, state };
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
cache = undefined;
|
|
134
|
+
}
|
|
39
135
|
}
|
|
40
136
|
export function trustAllEnabled() {
|
|
41
137
|
return TRUTHY.includes((process.env["FASTCODE_TRUST_ALL"] ?? "").trim().toLowerCase());
|
|
@@ -44,13 +140,14 @@ export function isTrusted(dir) {
|
|
|
44
140
|
if (trustAllEnabled())
|
|
45
141
|
return true;
|
|
46
142
|
const target = normalizeProjectPath(dir);
|
|
47
|
-
return loadTrust().
|
|
143
|
+
return Boolean(loadTrust().paths[target]);
|
|
48
144
|
}
|
|
49
145
|
export function trustProject(dir) {
|
|
50
146
|
const target = normalizeProjectPath(dir);
|
|
51
147
|
const state = loadTrust();
|
|
52
|
-
if (!state.
|
|
53
|
-
|
|
148
|
+
if (!state.paths[target]) {
|
|
149
|
+
const at = new Date().toISOString();
|
|
150
|
+
state.paths[target] = { grantedAt: at, lastUsed: at };
|
|
54
151
|
saveTrust(state);
|
|
55
152
|
}
|
|
56
153
|
return target;
|
|
@@ -58,10 +155,38 @@ export function trustProject(dir) {
|
|
|
58
155
|
export function untrustProject(dir) {
|
|
59
156
|
const target = normalizeProjectPath(dir);
|
|
60
157
|
const state = loadTrust();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
158
|
+
if (!state.paths[target])
|
|
159
|
+
return false;
|
|
160
|
+
delete state.paths[target];
|
|
161
|
+
saveTrust(state);
|
|
162
|
+
return true;
|
|
64
163
|
}
|
|
65
164
|
export function listTrusted() {
|
|
66
|
-
return loadTrust().
|
|
165
|
+
return Object.entries(loadTrust().paths).map(([path, entry]) => ({ path, addedAt: entry.grantedAt }));
|
|
166
|
+
}
|
|
167
|
+
/** Check a tool/fingerprint approval bound to the current tool catalog. */
|
|
168
|
+
export function isToolApproved(key, catalogHash) {
|
|
169
|
+
const entry = loadTrust().tools[key];
|
|
170
|
+
return Boolean(entry && entry.catalogHash === catalogHash);
|
|
171
|
+
}
|
|
172
|
+
export function rememberToolApproval(key, catalogHash) {
|
|
173
|
+
const state = loadTrust();
|
|
174
|
+
const at = new Date().toISOString();
|
|
175
|
+
state.tools[key] = { grantedAt: at, lastUsed: at, catalogHash };
|
|
176
|
+
saveTrust(state);
|
|
177
|
+
}
|
|
178
|
+
export function forgetToolApproval(key) {
|
|
179
|
+
const state = loadTrust();
|
|
180
|
+
if (!state.tools[key])
|
|
181
|
+
return false;
|
|
182
|
+
delete state.tools[key];
|
|
183
|
+
saveTrust(state);
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
export function listToolApprovals() {
|
|
187
|
+
return Object.entries(loadTrust().tools).map(([key, entry]) => ({
|
|
188
|
+
key,
|
|
189
|
+
grantedAt: entry.grantedAt,
|
|
190
|
+
catalogHash: entry.catalogHash,
|
|
191
|
+
}));
|
|
67
192
|
}
|
package/dist/headless.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-Ink headless runner for `--ui json` / non-TTY `--ui plain`.
|
|
3
|
+
*
|
|
4
|
+
* stdout is reserved for the protocol/answer; diagnostics go to stderr.
|
|
5
|
+
*/
|
|
6
|
+
import readline from "node:readline";
|
|
7
|
+
import { AGENT_SYSTEM_PROMPT } from "./agent/protocol.js";
|
|
8
|
+
import { compactionConfigFromEnv } from "./agent/compaction.js";
|
|
9
|
+
import { resolveToolProtocol } from "./agent/protocol-mode.js";
|
|
10
|
+
import { runAgent } from "./agent/runner.js";
|
|
11
|
+
import { setShellPoolSize } from "./agent/shell-session.js";
|
|
12
|
+
import { toolSchemas } from "./agent/tool-schemas.js";
|
|
13
|
+
import { encodeGateAsk, parseGateDecision } from "./agent/approval-json.js";
|
|
14
|
+
import { decisionForOption, parseApprovalChoice } from "./agent/approval.js";
|
|
15
|
+
import { buildPluginsPrompt } from "./plugins/manager.js";
|
|
16
|
+
import { buildSkillsPrompt } from "./skills/manager.js";
|
|
17
|
+
import { loadConfig, resolveApiKey } from "./config/store.js";
|
|
18
|
+
import { getProviderOrThrow } from "./providers/index.js";
|
|
19
|
+
import { resumeSession } from "./session/projection.js";
|
|
20
|
+
let eventSeq = 0;
|
|
21
|
+
function writeEvent(value) {
|
|
22
|
+
eventSeq += 1;
|
|
23
|
+
process.stdout.write(JSON.stringify({ v: 1, seq: eventSeq, ts: new Date().toISOString(), ...value }) + "\n");
|
|
24
|
+
}
|
|
25
|
+
function optionalBudget(name) {
|
|
26
|
+
const value = Number(process.env[name]);
|
|
27
|
+
return Number.isFinite(value) && value > 0 ? value : undefined;
|
|
28
|
+
}
|
|
29
|
+
export async function runHeadless(options) {
|
|
30
|
+
const config = loadConfig();
|
|
31
|
+
setShellPoolSize(config.shellPoolSize);
|
|
32
|
+
const provider = getProviderOrThrow(config.providerId);
|
|
33
|
+
let apiKey;
|
|
34
|
+
if (provider.authEnv.length > 0) {
|
|
35
|
+
const lookup = resolveApiKey(provider.id, config.model, provider.authEnv);
|
|
36
|
+
if (lookup.status !== "ok")
|
|
37
|
+
throw new Error(`No usable API key for ${provider.id}.`);
|
|
38
|
+
apiKey = lookup.key;
|
|
39
|
+
}
|
|
40
|
+
const history = options.resume ? resumeSession(options.resume, process.cwd()).messages : [];
|
|
41
|
+
const system = [
|
|
42
|
+
config.systemPrompt,
|
|
43
|
+
buildSkillsPrompt(config.enabledSkills),
|
|
44
|
+
buildPluginsPrompt(config.enabledPlugins),
|
|
45
|
+
config.agentMode ? AGENT_SYSTEM_PROMPT : "",
|
|
46
|
+
].filter((part) => part.trim()).join("\n\n");
|
|
47
|
+
const protocol = config.agentMode ? resolveToolProtocol(config, config.providerId, config.model) : "legacy";
|
|
48
|
+
const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
49
|
+
const queued = [];
|
|
50
|
+
const waiters = [];
|
|
51
|
+
let eof = false;
|
|
52
|
+
rl.on("line", (line) => {
|
|
53
|
+
const waiter = waiters.shift();
|
|
54
|
+
if (waiter)
|
|
55
|
+
waiter(line);
|
|
56
|
+
else
|
|
57
|
+
queued.push(line);
|
|
58
|
+
});
|
|
59
|
+
rl.on("close", () => {
|
|
60
|
+
eof = true;
|
|
61
|
+
while (waiters.length > 0)
|
|
62
|
+
waiters.shift()(undefined);
|
|
63
|
+
});
|
|
64
|
+
const nextLine = (timeoutMs) => {
|
|
65
|
+
if (queued.length > 0)
|
|
66
|
+
return Promise.resolve(queued.shift());
|
|
67
|
+
if (eof)
|
|
68
|
+
return Promise.resolve(undefined);
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const waiter = (line) => {
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
resolve(line);
|
|
73
|
+
};
|
|
74
|
+
const timer = setTimeout(() => {
|
|
75
|
+
const index = waiters.indexOf(waiter);
|
|
76
|
+
if (index >= 0)
|
|
77
|
+
waiters.splice(index, 1);
|
|
78
|
+
resolve(undefined);
|
|
79
|
+
}, Math.max(1000, timeoutMs));
|
|
80
|
+
timer.unref?.();
|
|
81
|
+
waiters.push(waiter);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
const requestApproval = async (request) => {
|
|
85
|
+
if (options.ui === "json") {
|
|
86
|
+
writeEvent(encodeGateAsk(request));
|
|
87
|
+
const line = await nextLine(request.timeoutMs ?? 60_000);
|
|
88
|
+
if (line === undefined)
|
|
89
|
+
return { kind: "deny", reason: "approval timeout/EOF" };
|
|
90
|
+
const parsed = parseGateDecision(request, line);
|
|
91
|
+
if (parsed.error) {
|
|
92
|
+
writeEvent({ event: "gate:error", version: 1, id: request.id, error: parsed.error });
|
|
93
|
+
return parsed.decision;
|
|
94
|
+
}
|
|
95
|
+
return parsed.decision;
|
|
96
|
+
}
|
|
97
|
+
process.stderr.write(`${request.prompt ?? "Approval required"}\n`);
|
|
98
|
+
request.options.forEach((option, index) => process.stderr.write(`${index + 1}. ${option.label}\n`));
|
|
99
|
+
process.stderr.write(`Choose 1-${request.options.length}: `);
|
|
100
|
+
const line = await nextLine(request.timeoutMs ?? 60_000);
|
|
101
|
+
const option = line === undefined ? undefined : parseApprovalChoice(request, line);
|
|
102
|
+
if (!option)
|
|
103
|
+
return { kind: "deny", reason: "approval cancelled/invalid/timeout" };
|
|
104
|
+
if (option.kind === "feedback") {
|
|
105
|
+
process.stderr.write(`${option.prompt || "Why should this be denied?"}\n> `);
|
|
106
|
+
const feedbackLine = await nextLine(request.timeoutMs ?? 60_000);
|
|
107
|
+
const feedback = feedbackLine?.trim();
|
|
108
|
+
return feedback
|
|
109
|
+
? { kind: "feedback", text: feedback }
|
|
110
|
+
: { kind: "deny", reason: "feedback cancelled/empty (fail closed)" };
|
|
111
|
+
}
|
|
112
|
+
return decisionForOption(option);
|
|
113
|
+
};
|
|
114
|
+
const abort = new AbortController();
|
|
115
|
+
const onSigint = () => abort.abort();
|
|
116
|
+
const onTerminate = () => abort.abort();
|
|
117
|
+
process.once("SIGINT", onSigint);
|
|
118
|
+
process.once("SIGTERM", onTerminate);
|
|
119
|
+
process.once("SIGHUP", onTerminate);
|
|
120
|
+
try {
|
|
121
|
+
for await (const event of runAgent({
|
|
122
|
+
provider,
|
|
123
|
+
apiKey,
|
|
124
|
+
model: config.model,
|
|
125
|
+
messages: history,
|
|
126
|
+
prompt: options.prompt,
|
|
127
|
+
system,
|
|
128
|
+
maxTurns: config.agentMode ? Math.max(1, Math.floor(Number(config.maxAgentTurns) || 24)) : 1,
|
|
129
|
+
allowTools: config.agentMode,
|
|
130
|
+
tokenBudget: optionalBudget("FASTCODE_TOKEN_BUDGET"),
|
|
131
|
+
costBudgetUsd: optionalBudget("FASTCODE_COST_BUDGET_USD"),
|
|
132
|
+
signal: abort.signal,
|
|
133
|
+
cwd: process.cwd(),
|
|
134
|
+
idleTimeoutMs: config.requestIdleTimeoutMs,
|
|
135
|
+
compaction: compactionConfigFromEnv(),
|
|
136
|
+
reasoningEffort: config.reasoningEffort,
|
|
137
|
+
protocol,
|
|
138
|
+
tools: protocol === "native" ? toolSchemas() : undefined,
|
|
139
|
+
requestApproval,
|
|
140
|
+
})) {
|
|
141
|
+
if (options.ui === "json") {
|
|
142
|
+
if (event.type === "assistant_text")
|
|
143
|
+
writeEvent({ event: "assistant", text: event.text });
|
|
144
|
+
else if (event.type === "tool_call")
|
|
145
|
+
writeEvent({
|
|
146
|
+
event: "tool_call",
|
|
147
|
+
id: event.id,
|
|
148
|
+
tool: event.call.tool,
|
|
149
|
+
args: event.call,
|
|
150
|
+
...(event.native ? { native: true } : {}),
|
|
151
|
+
});
|
|
152
|
+
else if (event.type === "egress_denied")
|
|
153
|
+
writeEvent({
|
|
154
|
+
event: "egress_denied",
|
|
155
|
+
outlet: event.outlet,
|
|
156
|
+
...(event.target ? { target: event.target } : {}),
|
|
157
|
+
rule: event.rule,
|
|
158
|
+
mode: event.mode,
|
|
159
|
+
would_block: event.wouldBlock,
|
|
160
|
+
code: event.code,
|
|
161
|
+
reason: event.reason,
|
|
162
|
+
});
|
|
163
|
+
else if (event.type === "tool_result")
|
|
164
|
+
writeEvent({
|
|
165
|
+
event: "tool_result",
|
|
166
|
+
id: event.id,
|
|
167
|
+
tool: event.call.tool,
|
|
168
|
+
ok: event.ok,
|
|
169
|
+
output: event.output,
|
|
170
|
+
...(event.sideEffects ? { side_effects: event.sideEffects } : {}),
|
|
171
|
+
...(event.code ? { code: event.code } : {}),
|
|
172
|
+
...(event.hint ? { hint: event.hint } : {}),
|
|
173
|
+
...(event.truncated !== undefined ? { truncated: event.truncated } : {}),
|
|
174
|
+
...(event.totalLines !== undefined ? { total_lines: event.totalLines } : {}),
|
|
175
|
+
...(event.returnedLines !== undefined ? { returned_lines: event.returnedLines } : {}),
|
|
176
|
+
...(event.totalBytes !== undefined ? { total_bytes: event.totalBytes } : {}),
|
|
177
|
+
...(event.returnedBytes !== undefined ? { returned_bytes: event.returnedBytes } : {}),
|
|
178
|
+
...(event.filesRead !== undefined ? { files_read: event.filesRead } : {}),
|
|
179
|
+
...(event.filesSkipped !== undefined ? { files_skipped: event.filesSkipped } : {}),
|
|
180
|
+
...(event.matches !== undefined ? { matches: event.matches } : {}),
|
|
181
|
+
});
|
|
182
|
+
else if (event.type === "usage")
|
|
183
|
+
writeEvent({
|
|
184
|
+
event: "usage",
|
|
185
|
+
input_tokens: event.usage.inputTokens ?? 0,
|
|
186
|
+
output_tokens: event.usage.outputTokens ?? 0,
|
|
187
|
+
cost_usd: event.usage.costUsd ?? 0,
|
|
188
|
+
});
|
|
189
|
+
else if (event.type === "notice")
|
|
190
|
+
writeEvent({ event: "notice", text: event.text });
|
|
191
|
+
else if (event.type === "error")
|
|
192
|
+
writeEvent({ event: "error", kind: event.kind, code: event.kind, message: event.message });
|
|
193
|
+
else if (event.type === "done")
|
|
194
|
+
writeEvent({ event: "done", stopReason: event.stopReason, finalText: event.finalText });
|
|
195
|
+
}
|
|
196
|
+
else if (event.type === "assistant_text") {
|
|
197
|
+
process.stdout.write(event.text);
|
|
198
|
+
}
|
|
199
|
+
else if (event.type === "notice") {
|
|
200
|
+
process.stderr.write(`[fastcode] ${event.text}\n`);
|
|
201
|
+
}
|
|
202
|
+
else if (event.type === "done" && event.finalText) {
|
|
203
|
+
process.stdout.write(event.finalText + "\n");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
process.removeListener("SIGINT", onSigint);
|
|
209
|
+
process.removeListener("SIGTERM", onTerminate);
|
|
210
|
+
process.removeListener("SIGHUP", onTerminate);
|
|
211
|
+
rl.close();
|
|
212
|
+
}
|
|
213
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -5,13 +5,29 @@
|
|
|
5
5
|
* Ink-based TUI. We keep this file tiny so the slow imports (Ink, React)
|
|
6
6
|
* are not loaded for `fastcode skills list`.
|
|
7
7
|
*/
|
|
8
|
-
import { parseCli } from "./cli.js";
|
|
8
|
+
import { parseCli } from "./cli-main.js";
|
|
9
|
+
import { installProcessGuards } from "./lib/lifecycle.js";
|
|
9
10
|
async function main() {
|
|
11
|
+
installProcessGuards();
|
|
10
12
|
const cli = await parseCli();
|
|
11
13
|
if (cli.done)
|
|
12
14
|
return;
|
|
15
|
+
if (cli.ui === "json") {
|
|
16
|
+
if (!cli.prompt) {
|
|
17
|
+
console.error("--ui json requires a prompt: fastcode --ui json \"<prompt>\"");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const { runHeadless } = await import("./headless.js");
|
|
21
|
+
await runHeadless({ prompt: cli.prompt, ui: "json", resume: cli.resume });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
13
24
|
if (!process.stdout.isTTY) {
|
|
14
|
-
|
|
25
|
+
if (cli.ui === "plain" && cli.prompt) {
|
|
26
|
+
const { runHeadless } = await import("./headless.js");
|
|
27
|
+
await runHeadless({ prompt: cli.prompt, ui: "plain", resume: cli.resume });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.error("FastCode TUI requires a TTY. Use `fastcode --ui plain|json \"<prompt>\"` for pipe mode.");
|
|
15
31
|
process.exit(1);
|
|
16
32
|
}
|
|
17
33
|
// Lazy-load Ink so subcommands don't pay the React boot cost.
|
|
@@ -23,7 +39,7 @@ async function main() {
|
|
|
23
39
|
// React.createElement avoids needing a separate .tsx loader for this file.
|
|
24
40
|
// exitOnCtrlC: false lets the app own Ctrl+C so the documented
|
|
25
41
|
// "press Ctrl+C twice to quit" behavior actually works.
|
|
26
|
-
render(React.createElement(App, { initialPrompt: cli.prompt, resume: cli.resume }), { exitOnCtrlC: false });
|
|
42
|
+
render(React.createElement(App, { initialPrompt: cli.prompt, resume: cli.resume, ui: cli.ui ?? "tty" }), { exitOnCtrlC: false });
|
|
27
43
|
}
|
|
28
44
|
main().catch((err) => {
|
|
29
45
|
console.error(err);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal debug logging.
|
|
3
|
+
*
|
|
4
|
+
* There is no logging framework in this project: normal output goes through
|
|
5
|
+
* Ink and lifecycle warnings go straight to stderr. `FASTCODE_DEBUG=1` opts
|
|
6
|
+
* into low-volume stderr diagnostics (for example timeout clamping or config
|
|
7
|
+
* cache invalidation) without changing the default experience.
|
|
8
|
+
*/
|
|
9
|
+
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
10
|
+
export function debugEnabled() {
|
|
11
|
+
return TRUTHY.has((process.env["FASTCODE_DEBUG"] ?? "").trim().toLowerCase());
|
|
12
|
+
}
|
|
13
|
+
export function debugLog(scope, message) {
|
|
14
|
+
if (!debugEnabled())
|
|
15
|
+
return;
|
|
16
|
+
try {
|
|
17
|
+
process.stderr.write(`[fastcode:${scope}] ${message}\n`);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Debug logging must never break the operation it observes.
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -12,29 +12,73 @@ export declare function collectDiagnostics(): {
|
|
|
12
12
|
configRoot: any;
|
|
13
13
|
configFile: any;
|
|
14
14
|
authFile: any;
|
|
15
|
+
authMode: string;
|
|
16
|
+
tmpdir: string;
|
|
17
|
+
memTotalMb: number;
|
|
18
|
+
memFreeMb: number;
|
|
15
19
|
providerId: any;
|
|
16
20
|
model: any;
|
|
17
21
|
keyPresent: boolean;
|
|
18
|
-
keyStatus: "ok" | "
|
|
19
|
-
keyEnv:
|
|
22
|
+
keyStatus: "ok" | "missing" | "unbound";
|
|
23
|
+
keyEnv: string;
|
|
20
24
|
skillsInstalled: number;
|
|
25
|
+
skillsRegistry: string;
|
|
26
|
+
debug: boolean;
|
|
27
|
+
configCache: boolean;
|
|
21
28
|
pluginsInstalled: number;
|
|
22
29
|
enabledSkills: any;
|
|
23
30
|
enabledPlugins: any;
|
|
24
31
|
sessionLogEnabled: boolean;
|
|
25
32
|
sessionCacheMb: number;
|
|
33
|
+
sessionFsync: "auto" | "off" | "on";
|
|
34
|
+
unclosedSessions: number;
|
|
26
35
|
sessionsDir: any;
|
|
27
36
|
sessionsCount: number;
|
|
28
37
|
sessionsBytes: number;
|
|
29
38
|
trustFile: any;
|
|
30
|
-
trusted:
|
|
39
|
+
trusted: boolean;
|
|
31
40
|
shellDenyPatterns: number;
|
|
32
41
|
allowDangerous: boolean;
|
|
33
42
|
toolOutputLimit: number;
|
|
43
|
+
readMaxBytes: number;
|
|
44
|
+
readScanMaxBytes: number;
|
|
45
|
+
readManyMaxBytes: number;
|
|
46
|
+
grepMaxBytes: number;
|
|
47
|
+
egress: {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
mode: import("../agent/policy/defaults.js").EgressMode;
|
|
50
|
+
maxScanBytes: number;
|
|
51
|
+
denyPaths: number;
|
|
52
|
+
denyGlobs: number;
|
|
53
|
+
denyContentMarkers: number;
|
|
54
|
+
scanToolOutput: boolean;
|
|
55
|
+
};
|
|
56
|
+
tokenBudget: number;
|
|
57
|
+
costBudgetUsd: number;
|
|
58
|
+
compactionEnabled: boolean;
|
|
59
|
+
compactionRatio: number;
|
|
60
|
+
compactionKeepTurns: number;
|
|
61
|
+
compactionSummaryTokens: number;
|
|
62
|
+
compactionHeapMb: number;
|
|
63
|
+
compactionHeapUsedMb: number;
|
|
64
|
+
compactionForced: boolean;
|
|
65
|
+
serverCompactionAware: boolean;
|
|
66
|
+
shellActive: number;
|
|
67
|
+
shellPool: number;
|
|
68
|
+
shellPoolMax: number;
|
|
69
|
+
shellSpawns: number;
|
|
70
|
+
shellMaxSpawn: number;
|
|
71
|
+
sseIdleMs: number;
|
|
72
|
+
toolProtocol: import("../agent/protocol-mode.js").ToolProtocol;
|
|
73
|
+
toolProtocolMode: string;
|
|
74
|
+
toolCapability: any;
|
|
75
|
+
loopExempt: string[];
|
|
76
|
+
modelMetaFile: string;
|
|
77
|
+
modelMeta: import("./model-meta.js").ModelMeta;
|
|
34
78
|
providers: {
|
|
35
79
|
id: string;
|
|
36
80
|
label: string;
|
|
37
|
-
env:
|
|
81
|
+
env: string[];
|
|
38
82
|
keyPresent: boolean;
|
|
39
83
|
}[];
|
|
40
84
|
};
|