@datename/fastcode-termux 0.1.6-beta → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +6 -24
  2. package/dist/agent/guards.d.ts +0 -8
  3. package/dist/agent/guards.js +0 -195
  4. package/dist/agent/protocol.js +1 -4
  5. package/dist/agent/tools.d.ts +1 -3
  6. package/dist/agent/tools.js +377 -2
  7. package/dist/app.js +103 -335
  8. package/dist/cli.js +311 -362
  9. package/dist/commands/index.js +0 -22
  10. package/dist/commands/login.d.ts +2 -4
  11. package/dist/commands/login.js +10 -12
  12. package/dist/commands/logout.js +16 -8
  13. package/dist/commands/model.d.ts +0 -13
  14. package/dist/commands/model.js +22 -42
  15. package/dist/commands/provider.d.ts +0 -13
  16. package/dist/commands/provider.js +9 -20
  17. package/dist/commands/whoami.js +5 -6
  18. package/dist/config/store.d.ts +12 -32
  19. package/dist/config/store.js +65 -117
  20. package/dist/config/trust.d.ts +8 -21
  21. package/dist/config/trust.js +13 -99
  22. package/dist/lib/diagnostics.d.ts +4 -29
  23. package/dist/lib/diagnostics.js +4 -60
  24. package/dist/lib/http.d.ts +1 -11
  25. package/dist/lib/http.js +4 -62
  26. package/dist/providers/anthropic.d.ts +0 -1
  27. package/dist/providers/anthropic.js +0 -12
  28. package/dist/providers/custom.d.ts +0 -9
  29. package/dist/providers/deepseek.d.ts +0 -10
  30. package/dist/providers/deepseek.js +1 -2
  31. package/dist/providers/google.d.ts +0 -9
  32. package/dist/providers/groq.d.ts +0 -10
  33. package/dist/providers/groq.js +1 -2
  34. package/dist/providers/index.d.ts +201 -10
  35. package/dist/providers/index.js +0 -30
  36. package/dist/providers/mistral.d.ts +0 -10
  37. package/dist/providers/mistral.js +1 -2
  38. package/dist/providers/moonshot.d.ts +0 -10
  39. package/dist/providers/moonshot.js +1 -2
  40. package/dist/providers/ollama.d.ts +0 -9
  41. package/dist/providers/openai.d.ts +0 -10
  42. package/dist/providers/openai.js +1 -2
  43. package/dist/providers/openaiCompatible.d.ts +0 -9
  44. package/dist/providers/openaiCompatible.js +10 -58
  45. package/dist/providers/openrouter.d.ts +0 -10
  46. package/dist/providers/openrouter.js +1 -2
  47. package/dist/providers/qwen.d.ts +0 -10
  48. package/dist/providers/qwen.js +1 -2
  49. package/dist/providers/xai.d.ts +0 -10
  50. package/dist/providers/xai.js +1 -2
  51. package/dist/providers/zhipu.d.ts +0 -10
  52. package/dist/providers/zhipu.js +1 -2
  53. package/dist/session/log.d.ts +12 -35
  54. package/dist/session/log.js +10 -206
  55. package/dist/types.d.ts +1 -60
  56. package/dist/ui/Message.d.ts +7 -1
  57. package/dist/ui/Message.js +2 -13
  58. package/dist/ui/Prompt.d.ts +2 -1
  59. package/dist/ui/Prompt.js +2 -2
  60. package/dist/ui/Spinner.js +5 -11
  61. package/dist/ui/StatusBar.d.ts +1 -2
  62. package/dist/ui/StatusBar.js +5 -6
  63. package/package.json +1 -1
  64. package/src/agent/guards.ts +0 -196
  65. package/src/agent/protocol.ts +1 -4
  66. package/src/agent/tools.ts +377 -3
  67. package/src/app.ts +103 -335
  68. package/src/cli.ts +3 -30
  69. package/src/commands/index.ts +0 -22
  70. package/src/commands/login.ts +10 -12
  71. package/src/commands/logout.ts +16 -8
  72. package/src/commands/model.ts +22 -42
  73. package/src/commands/provider.ts +9 -20
  74. package/src/commands/whoami.ts +5 -6
  75. package/src/config/store.ts +69 -122
  76. package/src/config/trust.ts +21 -137
  77. package/src/lib/diagnostics.ts +4 -60
  78. package/src/lib/http.ts +4 -66
  79. package/src/providers/anthropic.ts +1 -13
  80. package/src/providers/deepseek.ts +1 -2
  81. package/src/providers/groq.ts +1 -2
  82. package/src/providers/index.ts +1 -32
  83. package/src/providers/mistral.ts +1 -2
  84. package/src/providers/moonshot.ts +1 -2
  85. package/src/providers/openai.ts +1 -2
  86. package/src/providers/openaiCompatible.ts +10 -58
  87. package/src/providers/openrouter.ts +1 -2
  88. package/src/providers/qwen.ts +1 -2
  89. package/src/providers/xai.ts +1 -2
  90. package/src/providers/zhipu.ts +1 -2
  91. package/src/session/log.ts +28 -267
  92. package/src/types.ts +1 -63
  93. package/src/ui/Message.ts +2 -13
  94. package/src/ui/Prompt.ts +2 -2
  95. package/src/ui/Spinner.ts +5 -11
  96. package/src/ui/StatusBar.ts +5 -6
  97. package/dist/agent/approval.d.ts +0 -50
  98. package/dist/agent/approval.js +0 -7
  99. package/dist/agent/compaction.d.ts +0 -39
  100. package/dist/agent/compaction.js +0 -227
  101. package/dist/agent/context.d.ts +0 -29
  102. package/dist/agent/context.js +0 -62
  103. package/dist/agent/ignore.d.ts +0 -11
  104. package/dist/agent/ignore.js +0 -129
  105. package/dist/agent/process-tree.d.ts +0 -2
  106. package/dist/agent/process-tree.js +0 -31
  107. package/dist/agent/protocol-mode.d.ts +0 -6
  108. package/dist/agent/protocol-mode.js +0 -51
  109. package/dist/agent/registry.d.ts +0 -17
  110. package/dist/agent/registry.js +0 -90
  111. package/dist/agent/runner.d.ts +0 -106
  112. package/dist/agent/runner.js +0 -352
  113. package/dist/agent/shell-session.d.ts +0 -37
  114. package/dist/agent/shell-session.js +0 -400
  115. package/dist/agent/stream.d.ts +0 -29
  116. package/dist/agent/stream.js +0 -103
  117. package/dist/agent/tool-schemas.d.ts +0 -2
  118. package/dist/agent/tool-schemas.js +0 -63
  119. package/dist/agent/tools/append_file.d.ts +0 -2
  120. package/dist/agent/tools/append_file.js +0 -26
  121. package/dist/agent/tools/apply_patch.d.ts +0 -10
  122. package/dist/agent/tools/apply_patch.js +0 -52
  123. package/dist/agent/tools/copy_file.d.ts +0 -2
  124. package/dist/agent/tools/copy_file.js +0 -30
  125. package/dist/agent/tools/delete_file.d.ts +0 -2
  126. package/dist/agent/tools/delete_file.js +0 -24
  127. package/dist/agent/tools/grep.d.ts +0 -2
  128. package/dist/agent/tools/grep.js +0 -23
  129. package/dist/agent/tools/index.d.ts +0 -2
  130. package/dist/agent/tools/index.js +0 -32
  131. package/dist/agent/tools/list_files.d.ts +0 -2
  132. package/dist/agent/tools/list_files.js +0 -17
  133. package/dist/agent/tools/make_dir.d.ts +0 -2
  134. package/dist/agent/tools/make_dir.js +0 -24
  135. package/dist/agent/tools/move_path.d.ts +0 -2
  136. package/dist/agent/tools/move_path.js +0 -47
  137. package/dist/agent/tools/paths.d.ts +0 -7
  138. package/dist/agent/tools/paths.js +0 -84
  139. package/dist/agent/tools/pwd.d.ts +0 -2
  140. package/dist/agent/tools/pwd.js +0 -10
  141. package/dist/agent/tools/read_file.d.ts +0 -2
  142. package/dist/agent/tools/read_file.js +0 -21
  143. package/dist/agent/tools/read_many.d.ts +0 -2
  144. package/dist/agent/tools/read_many.js +0 -25
  145. package/dist/agent/tools/readers.d.ts +0 -6
  146. package/dist/agent/tools/readers.js +0 -216
  147. package/dist/agent/tools/replace_in_file.d.ts +0 -2
  148. package/dist/agent/tools/replace_in_file.js +0 -30
  149. package/dist/agent/tools/shell.d.ts +0 -2
  150. package/dist/agent/tools/shell.js +0 -48
  151. package/dist/agent/tools/stat_path.d.ts +0 -2
  152. package/dist/agent/tools/stat_path.js +0 -31
  153. package/dist/agent/tools/types.d.ts +0 -39
  154. package/dist/agent/tools/types.js +0 -1
  155. package/dist/agent/tools/validate.d.ts +0 -6
  156. package/dist/agent/tools/validate.js +0 -29
  157. package/dist/agent/tools/write_file.d.ts +0 -2
  158. package/dist/agent/tools/write_file.js +0 -26
  159. package/dist/commands/attach.d.ts +0 -9
  160. package/dist/commands/attach.js +0 -15
  161. package/dist/commands/bg.d.ts +0 -7
  162. package/dist/commands/bg.js +0 -9
  163. package/dist/commands/cancel.d.ts +0 -10
  164. package/dist/commands/cancel.js +0 -10
  165. package/dist/commands/compact.d.ts +0 -10
  166. package/dist/commands/compact.js +0 -22
  167. package/dist/commands/context.d.ts +0 -7
  168. package/dist/commands/context.js +0 -24
  169. package/dist/commands/edit.d.ts +0 -10
  170. package/dist/commands/edit.js +0 -36
  171. package/dist/commands/effort.d.ts +0 -25
  172. package/dist/commands/effort.js +0 -48
  173. package/dist/commands/expand.d.ts +0 -10
  174. package/dist/commands/expand.js +0 -8
  175. package/dist/commands/jobs.d.ts +0 -7
  176. package/dist/commands/jobs.js +0 -16
  177. package/dist/commands/kill.d.ts +0 -7
  178. package/dist/commands/kill.js +0 -11
  179. package/dist/commands/steer.d.ts +0 -11
  180. package/dist/commands/steer.js +0 -13
  181. package/dist/lib/model-meta.d.ts +0 -19
  182. package/dist/lib/model-meta.js +0 -113
  183. package/dist/lib/native-tools.d.ts +0 -12
  184. package/dist/lib/native-tools.js +0 -21
  185. package/dist/lib/net.d.ts +0 -33
  186. package/dist/lib/net.js +0 -168
  187. package/dist/patch/apply.d.ts +0 -15
  188. package/dist/patch/apply.js +0 -172
  189. package/dist/patch/parse.d.ts +0 -29
  190. package/dist/patch/parse.js +0 -112
  191. package/dist/patch/plan.d.ts +0 -49
  192. package/dist/patch/plan.js +0 -273
  193. package/dist/session/projection.d.ts +0 -43
  194. package/dist/session/projection.js +0 -257
  195. package/dist/ui/Picker.d.ts +0 -8
  196. package/dist/ui/Picker.js +0 -24
  197. package/src/agent/approval.ts +0 -47
  198. package/src/agent/compaction.ts +0 -276
  199. package/src/agent/context.ts +0 -97
  200. package/src/agent/ignore.ts +0 -147
  201. package/src/agent/process-tree.ts +0 -32
  202. package/src/agent/protocol-mode.ts +0 -59
  203. package/src/agent/registry.ts +0 -98
  204. package/src/agent/runner.ts +0 -443
  205. package/src/agent/shell-session.ts +0 -449
  206. package/src/agent/stream.ts +0 -143
  207. package/src/agent/tool-schemas.ts +0 -67
  208. package/src/agent/tools/append_file.ts +0 -28
  209. package/src/agent/tools/apply_patch.ts +0 -54
  210. package/src/agent/tools/copy_file.ts +0 -32
  211. package/src/agent/tools/delete_file.ts +0 -26
  212. package/src/agent/tools/grep.ts +0 -25
  213. package/src/agent/tools/index.ts +0 -34
  214. package/src/agent/tools/list_files.ts +0 -19
  215. package/src/agent/tools/make_dir.ts +0 -26
  216. package/src/agent/tools/move_path.ts +0 -50
  217. package/src/agent/tools/paths.ts +0 -92
  218. package/src/agent/tools/pwd.ts +0 -12
  219. package/src/agent/tools/read_file.ts +0 -23
  220. package/src/agent/tools/read_many.ts +0 -27
  221. package/src/agent/tools/readers.ts +0 -228
  222. package/src/agent/tools/replace_in_file.ts +0 -32
  223. package/src/agent/tools/shell.ts +0 -53
  224. package/src/agent/tools/stat_path.ts +0 -33
  225. package/src/agent/tools/types.ts +0 -42
  226. package/src/agent/tools/validate.ts +0 -35
  227. package/src/agent/tools/write_file.ts +0 -28
  228. package/src/commands/attach.ts +0 -16
  229. package/src/commands/bg.ts +0 -9
  230. package/src/commands/cancel.ts +0 -10
  231. package/src/commands/compact.ts +0 -23
  232. package/src/commands/context.ts +0 -25
  233. package/src/commands/edit.ts +0 -37
  234. package/src/commands/effort.ts +0 -49
  235. package/src/commands/expand.ts +0 -8
  236. package/src/commands/jobs.ts +0 -17
  237. package/src/commands/kill.ts +0 -12
  238. package/src/commands/steer.ts +0 -13
  239. package/src/lib/model-meta.ts +0 -138
  240. package/src/lib/native-tools.ts +0 -32
  241. package/src/lib/net.ts +0 -204
  242. package/src/patch/apply.ts +0 -194
  243. package/src/patch/parse.ts +0 -131
  244. package/src/patch/plan.ts +0 -317
  245. package/src/session/projection.ts +0 -308
  246. package/src/ui/Picker.ts +0 -24
@@ -1,54 +0,0 @@
1
- import { parsePatch } from "../../patch/parse.js";
2
- import { planPatch, reviewPatchPlan } from "../../patch/plan.js";
3
- import { applyPatchPlan } from "../../patch/apply.js";
4
- import type { Tool } from "./types.js";
5
- import { invalid, requiredString } from "./validate.js";
6
-
7
- /**
8
- * Atomic multi-file patch.
9
- *
10
- * Applies Add / Delete / Update / Move only after every file plan succeeds.
11
- * Symbolic links are refused, CRLF and mode bits are preserved, and per-file
12
- * diffs are returned. xattr / ACL are NOT preserved and the inode changes
13
- * (tmp+rename); restart file watchers if they miss the change.
14
- */
15
- export const applyPatchTool: Tool = {
16
- name: "apply_patch",
17
- requiresTrust: true,
18
- stateScope: "turn",
19
- revalidate: ["trust", "sandboxRoots"],
20
- approvalHints: { mayRequireApproval: true },
21
- prepare(call) {
22
- let patch: string;
23
- try {
24
- patch = requiredString(call, "patch");
25
- }
26
- catch (err: any) {
27
- return invalid(err.message);
28
- }
29
- const parsed = parsePatch(patch);
30
- return parsed.ok === false ? invalid(parsed.output) : { ok: true };
31
- },
32
- async execute(call, ctx) {
33
- const patch = requiredString(call, "patch");
34
- const planned = planPatch(patch, ctx.cwd);
35
- if (planned.ok === false)
36
- return { ok: false, output: planned.output };
37
- const review = reviewPatchPlan(planned.plan);
38
- if (review.kind === "deny")
39
- return { ok: false, output: `patch denied: ${review.reason}` };
40
- if (review.kind === "pending") {
41
- return {
42
- ok: false,
43
- output: `patch needs approval (${review.reason}), but no approval resolver is registered; nothing was written.`,
44
- };
45
- }
46
- const applied = applyPatchPlan(planned.plan);
47
- const warnings = applied.warnings.length > 0 ? "\n\nwarnings:\n" + applied.warnings.map((line) => "- " + line).join("\n") : "";
48
- return {
49
- ok: applied.ok,
50
- output: applied.output + warnings,
51
- sideEffects: { touched: applied.touched, removed: applied.removed },
52
- };
53
- },
54
- };
@@ -1,32 +0,0 @@
1
- import { copyFile, mkdir } from "node:fs/promises";
2
- import { dirname } from "node:path";
3
- import { sandboxPath } from "./paths.js";
4
- import type { Tool } from "./types.js";
5
- import { invalid, preparePath, requiredString } from "./validate.js";
6
-
7
- export const copyFileTool: Tool = {
8
- name: "copy_file",
9
- requiresTrust: true,
10
- stateScope: "turn",
11
- revalidate: ["trust", "sandboxRoots"],
12
- prepare(call, ctx) {
13
- try {
14
- const from = requiredString(call, "from");
15
- const to = requiredString(call, "to");
16
- const checkedFrom = preparePath(ctx.cwd, from);
17
- if (!checkedFrom.ok)
18
- return checkedFrom;
19
- return preparePath(ctx.cwd, to);
20
- }
21
- catch (err: any) {
22
- return invalid(err.message);
23
- }
24
- },
25
- async execute(call, ctx) {
26
- const from = sandboxPath(ctx.cwd, requiredString(call, "from"));
27
- const to = sandboxPath(ctx.cwd, requiredString(call, "to"));
28
- await mkdir(dirname(to), { recursive: true });
29
- await copyFile(from, to);
30
- return { ok: true, output: `copied ${from} to ${to}`, sideEffects: { touched: [to] } };
31
- },
32
- };
@@ -1,26 +0,0 @@
1
- import { rm } from "node:fs/promises";
2
- import { resolvePath } from "./paths.js";
3
- import type { Tool } from "./types.js";
4
- import { invalid, preparePath, requiredString } from "./validate.js";
5
-
6
- export const deleteFileTool: Tool = {
7
- name: "delete_file",
8
- requiresTrust: true,
9
- stateScope: "turn",
10
- revalidate: ["trust", "sandboxRoots"],
11
- prepare(call, ctx) {
12
- let path: string;
13
- try {
14
- path = requiredString(call, "path");
15
- }
16
- catch (err: any) {
17
- return invalid(err.message);
18
- }
19
- return preparePath(ctx.cwd, path);
20
- },
21
- async execute(call, ctx) {
22
- const file = resolvePath(ctx.cwd, call);
23
- await rm(file, { recursive: true, force: true });
24
- return { ok: true, output: `deleted ${file}`, sideEffects: { removed: [file] } };
25
- },
26
- };
@@ -1,25 +0,0 @@
1
- import { grepFiles } from "./readers.js";
2
- import { resolvePath } from "./paths.js";
3
- import type { Tool } from "./types.js";
4
- import { invalid, numberOption, preparePath, requiredString } from "./validate.js";
5
-
6
- export const grepTool: Tool = {
7
- name: "grep",
8
- stateScope: "call",
9
- revalidate: ["sandboxRoots"],
10
- prepare(call, ctx) {
11
- try {
12
- requiredString(call, "pattern");
13
- }
14
- catch (err: any) {
15
- return invalid(err.message);
16
- }
17
- return preparePath(ctx.cwd, typeof call.path === "string" ? call.path : ".");
18
- },
19
- async execute(call, ctx) {
20
- return {
21
- ok: true,
22
- output: await grepFiles(ctx.cwd, resolvePath(ctx.cwd, call, "."), requiredString(call, "pattern"), numberOption(call.maxDepth, 4)),
23
- };
24
- },
25
- };
@@ -1,34 +0,0 @@
1
- import type { Tool } from "./types.js";
2
- import { shellTool } from "./shell.js";
3
- import { pwdTool } from "./pwd.js";
4
- import { readFileTool } from "./read_file.js";
5
- import { readManyTool } from "./read_many.js";
6
- import { writeFileTool } from "./write_file.js";
7
- import { appendFileTool } from "./append_file.js";
8
- import { applyPatchTool } from "./apply_patch.js";
9
- import { copyFileTool } from "./copy_file.js";
10
- import { deleteFileTool } from "./delete_file.js";
11
- import { listFilesTool } from "./list_files.js";
12
- import { makeDirTool } from "./make_dir.js";
13
- import { movePathTool } from "./move_path.js";
14
- import { grepTool } from "./grep.js";
15
- import { replaceInFileTool } from "./replace_in_file.js";
16
- import { statPathTool } from "./stat_path.js";
17
-
18
- export const TOOLS: Tool[] = [
19
- shellTool,
20
- pwdTool,
21
- readFileTool,
22
- readManyTool,
23
- writeFileTool,
24
- appendFileTool,
25
- applyPatchTool,
26
- copyFileTool,
27
- deleteFileTool,
28
- listFilesTool,
29
- makeDirTool,
30
- movePathTool,
31
- grepTool,
32
- replaceInFileTool,
33
- statPathTool,
34
- ];
@@ -1,19 +0,0 @@
1
- import { listFiles } from "./readers.js";
2
- import { resolvePath } from "./paths.js";
3
- import type { Tool } from "./types.js";
4
- import { numberOption, preparePath } from "./validate.js";
5
-
6
- export const listFilesTool: Tool = {
7
- name: "list_files",
8
- stateScope: "call",
9
- revalidate: ["sandboxRoots"],
10
- prepare(call, ctx) {
11
- return preparePath(ctx.cwd, typeof call.path === "string" ? call.path : ".");
12
- },
13
- async execute(call, ctx) {
14
- return {
15
- ok: true,
16
- output: await listFiles(ctx.cwd, resolvePath(ctx.cwd, call, "."), numberOption(call.maxDepth, 2), call.detail === true),
17
- };
18
- },
19
- };
@@ -1,26 +0,0 @@
1
- import { mkdir } from "node:fs/promises";
2
- import { resolvePath } from "./paths.js";
3
- import type { Tool } from "./types.js";
4
- import { invalid, preparePath, requiredString } from "./validate.js";
5
-
6
- export const makeDirTool: Tool = {
7
- name: "make_dir",
8
- requiresTrust: true,
9
- stateScope: "turn",
10
- revalidate: ["trust", "sandboxRoots"],
11
- prepare(call, ctx) {
12
- let path: string;
13
- try {
14
- path = requiredString(call, "path");
15
- }
16
- catch (err: any) {
17
- return invalid(err.message);
18
- }
19
- return preparePath(ctx.cwd, path);
20
- },
21
- async execute(call, ctx) {
22
- const dir = resolvePath(ctx.cwd, call);
23
- await mkdir(dir, { recursive: true });
24
- return { ok: true, output: `created ${dir}`, sideEffects: { created: [dir] } };
25
- },
26
- };
@@ -1,50 +0,0 @@
1
- import { mkdir, rename } from "node:fs/promises";
2
- import { lstatSync } from "node:fs";
3
- import { dirname } from "node:path";
4
- import { sandboxPath } from "./paths.js";
5
- import type { Tool } from "./types.js";
6
- import { invalid, preparePath, requiredString } from "./validate.js";
7
-
8
- function exists(path: string): boolean {
9
- try {
10
- lstatSync(path);
11
- return true;
12
- }
13
- catch {
14
- return false;
15
- }
16
- }
17
-
18
- export const movePathTool: Tool = {
19
- name: "move_path",
20
- requiresTrust: true,
21
- stateScope: "turn",
22
- revalidate: ["trust", "sandboxRoots"],
23
- prepare(call, ctx) {
24
- try {
25
- const from = requiredString(call, "from");
26
- const to = requiredString(call, "to");
27
- const checkedFrom = preparePath(ctx.cwd, from);
28
- if (!checkedFrom.ok)
29
- return checkedFrom;
30
- const checkedTo = preparePath(ctx.cwd, to);
31
- if (!checkedTo.ok)
32
- return checkedTo;
33
- if (exists(to))
34
- return invalid(`move target exists: ${to}`);
35
- return { ok: true };
36
- }
37
- catch (err: any) {
38
- return invalid(err.message);
39
- }
40
- },
41
- async execute(call, ctx) {
42
- const from = sandboxPath(ctx.cwd, requiredString(call, "from"));
43
- const to = sandboxPath(ctx.cwd, requiredString(call, "to"));
44
- if (exists(to))
45
- return { ok: false, output: `move target exists: ${to}` };
46
- await mkdir(dirname(to), { recursive: true });
47
- await rename(from, to);
48
- return { ok: true, output: `moved ${from} to ${to}`, sideEffects: { removed: [from], touched: [to] } };
49
- },
50
- };
@@ -1,92 +0,0 @@
1
- import { existsSync, realpathSync } from "node:fs";
2
- import { basename, delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path";
3
- import { detectPlatform, safeHome } from "../../lib/platform.js";
4
- import { loadConfig } from "../../config/store.js";
5
- import type { ToolCall } from "./types.js";
6
-
7
- export function allowedRoots(cwd: string): string[] {
8
- const roots = [resolve(cwd)];
9
- const configured = (process.env["FASTCODE_ALLOW_PATHS"] ?? "")
10
- .split(delimiter)
11
- .map((entry) => entry.trim())
12
- .filter(Boolean);
13
- for (const entry of configured)
14
- roots.push(resolve(entry));
15
- const sharedDirs = loadConfig().sharedDirs;
16
- if (Array.isArray(sharedDirs)) {
17
- for (const entry of sharedDirs) {
18
- if (typeof entry !== "string" || !entry.trim())
19
- continue;
20
- roots.push(resolveSharedDir(cwd, entry.trim()));
21
- }
22
- }
23
- if (detectPlatform() === "termux" && !isDisabled(process.env["FASTCODE_ALLOW_SHARED_STORAGE"])) {
24
- const home = process.env["HOME"];
25
- if (home) {
26
- const shared = join(home, "storage", "shared");
27
- if (existsSync(shared))
28
- roots.push(shared);
29
- }
30
- }
31
- return roots;
32
- }
33
-
34
- export function resolveSharedDir(cwd: string, entry: string): string {
35
- const expanded = entry === "~" || entry.startsWith("~/") || entry.startsWith("~\\")
36
- ? join(safeHome(), entry.slice(1))
37
- : entry;
38
- return isAbsolute(expanded) ? resolve(expanded) : resolve(cwd, expanded);
39
- }
40
-
41
- function isDisabled(value: string | undefined): boolean {
42
- if (value === undefined)
43
- return false;
44
- return ["0", "false", "no", "off"].includes(value.trim().toLowerCase());
45
- }
46
-
47
- export function isInsideAny(child: string, roots: string[]): boolean {
48
- return roots.some((root) => {
49
- const rel = relative(root, child);
50
- return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
51
- });
52
- }
53
-
54
- export function resolvePath(cwd: string, call: ToolCall, fallback = ""): string {
55
- const raw = typeof call.path === "string" ? call.path : fallback;
56
- return sandboxPath(cwd, raw);
57
- }
58
-
59
- export function sandboxPath(cwd: string, raw: string): string {
60
- const target = resolve(cwd, raw);
61
- const roots = allowedRoots(cwd);
62
- const realRoots = roots.map((root) => realpathOf(root)).filter(Boolean) as string[];
63
- const realTarget = realpathOf(target);
64
- if (realTarget) {
65
- if (isInsideAny(realTarget, realRoots))
66
- return target;
67
- throw new Error("path outside sandbox: " + raw);
68
- }
69
- if (isInsideAny(target, roots))
70
- return target;
71
- throw new Error("path outside sandbox: " + raw);
72
- }
73
-
74
- export function realpathOf(input: string): string | undefined {
75
- let current = resolve(input);
76
- const missing: string[] = [];
77
- for (;;) {
78
- if (existsSync(current)) {
79
- try {
80
- return join(realpathSync(current), ...missing.slice().reverse());
81
- }
82
- catch {
83
- return undefined;
84
- }
85
- }
86
- const parent = dirname(current);
87
- if (parent === current)
88
- return undefined;
89
- missing.push(basename(current));
90
- current = parent;
91
- }
92
- }
@@ -1,12 +0,0 @@
1
- import type { Tool } from "./types.js";
2
-
3
- export const pwdTool: Tool = {
4
- name: "pwd",
5
- stateScope: "call",
6
- prepare() {
7
- return { ok: true };
8
- },
9
- async execute(_call, ctx) {
10
- return { ok: true, output: ctx.cwd };
11
- },
12
- };
@@ -1,23 +0,0 @@
1
- import { readTextFile } from "./readers.js";
2
- import { resolvePath } from "./paths.js";
3
- import type { Tool } from "./types.js";
4
- import { invalid, preparePath, requiredString } from "./validate.js";
5
-
6
- export const readFileTool: Tool = {
7
- name: "read_file",
8
- stateScope: "call",
9
- revalidate: ["sandboxRoots"],
10
- prepare(call, ctx) {
11
- let path: string;
12
- try {
13
- path = requiredString(call, "path");
14
- }
15
- catch (err: any) {
16
- return invalid(err.message);
17
- }
18
- return preparePath(ctx.cwd, path);
19
- },
20
- async execute(call, ctx) {
21
- return { ok: true, output: await readTextFile(resolvePath(ctx.cwd, call)) };
22
- },
23
- };
@@ -1,27 +0,0 @@
1
- import { readMany } from "./readers.js";
2
- import type { Tool } from "./types.js";
3
- import { invalid, preparePath, stringArray } from "./validate.js";
4
-
5
- export const readManyTool: Tool = {
6
- name: "read_many",
7
- stateScope: "call",
8
- revalidate: ["sandboxRoots"],
9
- prepare(call, ctx) {
10
- let paths: string[];
11
- try {
12
- paths = stringArray(call, "paths");
13
- }
14
- catch (err: any) {
15
- return invalid(err.message);
16
- }
17
- for (const path of paths) {
18
- const checked = preparePath(ctx.cwd, path);
19
- if (!checked.ok)
20
- return checked;
21
- }
22
- return { ok: true };
23
- },
24
- async execute(call, ctx) {
25
- return { ok: true, output: await readMany(ctx.cwd, stringArray(call, "paths")) };
26
- },
27
- };
@@ -1,228 +0,0 @@
1
- import { open, readdir, stat } from "node:fs/promises";
2
- import { basename, extname, join } from "node:path";
3
- import { createIgnore, isBinaryName } from "../ignore.js";
4
- import { createOutputBuffer } from "../limits.js";
5
- import { allowedRoots, isInsideAny, realpathOf, sandboxPath } from "./paths.js";
6
-
7
- export async function readMany(cwd: string, paths: string[]): Promise<string> {
8
- const blocks: string[] = [];
9
- for (const path of paths) {
10
- const file = sandboxPath(cwd, path);
11
- blocks.push(`### ${file}\n${await readTextFile(file)}`);
12
- }
13
- return blocks.join("\n\n");
14
- }
15
-
16
- const CODE_EXT = new Set([
17
- ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".java",
18
- ".rb", ".php", ".cs", ".c", ".h", ".cpp", ".hpp", ".kt", ".swift", ".scala",
19
- ".sh", ".lua", ".ex", ".exs", ".dart", ".vue", ".svelte",
20
- ]);
21
-
22
- export async function listFiles(sandbox: string, root: string, maxDepth: number, detail: boolean): Promise<string> {
23
- const out = createOutputBuffer();
24
- const ignore = createIgnore(sandbox);
25
- if (detail) {
26
- await walkWithin(sandbox, root, maxDepth, async (file, info) => {
27
- out.line((info.isDirectory() ? "dir " : "file") + " " + file);
28
- return !out.full;
29
- });
30
- return out.value();
31
- }
32
- async function visitDir(dir: string, depth: number, label: string): Promise<void> {
33
- let entries;
34
- try {
35
- entries = await readdir(dir, { withFileTypes: true });
36
- }
37
- catch {
38
- out.line(`${indent(depth)}${label}/ (unreadable)`);
39
- return;
40
- }
41
- entries = entries.filter((entry) => !ignore.ignored(entry.name, join(dir, entry.name), entry.isDirectory()));
42
- entries.sort((a, b) => Number(b.isDirectory()) - Number(a.isDirectory()) || a.name.localeCompare(b.name));
43
- const dirs = entries.filter((entry) => entry.isDirectory());
44
- const files = entries.filter((entry) => entry.isFile());
45
- out.line(`${indent(depth)}${label}/ (${files.length} file${files.length === 1 ? "" : "s"}, ${dirs.length} dir${dirs.length === 1 ? "" : "s"})`);
46
- for (const entry of files) {
47
- const full = join(dir, entry.name);
48
- const info = await stat(full).catch(() => undefined);
49
- if (!info)
50
- continue;
51
- let line = `${indent(depth + 1)}${entry.name} (${formatSize(info.size)})`;
52
- const symbols = await fileSymbols(full, entry.name, info.size);
53
- if (symbols)
54
- line += ` — ${symbols}`;
55
- out.line(line);
56
- if (out.full)
57
- return;
58
- }
59
- if (depth >= maxDepth)
60
- return;
61
- for (const entry of dirs) {
62
- await visitDir(join(dir, entry.name), depth + 1, entry.name);
63
- if (out.full)
64
- return;
65
- }
66
- }
67
- await visitDir(root, 0, basename(root) || ".");
68
- return out.value();
69
- }
70
-
71
- async function fileSymbols(file: string, name: string, size: number): Promise<string> {
72
- if (size > 256 * 1024 || !CODE_EXT.has(extname(name).toLowerCase()))
73
- return "";
74
- try {
75
- const text = await readTextFile(file, 64 * 1024);
76
- const names = new Set<string>();
77
- const patterns = [
78
- /(?:^|\n)\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?(?:function|class|interface|type|enum|struct|trait|impl|def)\s+([A-Za-z_$][\w$]*)/g,
79
- /(?:^|\n)\s*(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:\(|function)/g,
80
- ];
81
- for (const pattern of patterns) {
82
- for (const match of text.matchAll(pattern)) {
83
- names.add(match[1]);
84
- if (names.size >= 8)
85
- break;
86
- }
87
- }
88
- return Array.from(names).slice(0, 8).join(", ");
89
- }
90
- catch {
91
- return "";
92
- }
93
- }
94
-
95
- function indent(depth: number): string {
96
- return " ".repeat(depth);
97
- }
98
-
99
- export function formatSize(bytes: number): string {
100
- if (bytes < 1024)
101
- return `${bytes} B`;
102
- if (bytes < 1024 * 1024)
103
- return `${(bytes / 1024).toFixed(1)} KB`;
104
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
105
- }
106
-
107
- export async function grepFiles(sandbox: string, root: string, pattern: string, maxDepth: number): Promise<string> {
108
- const re = new RegExp(pattern, "i");
109
- const out = createOutputBuffer();
110
- const maxBytes = grepMaxBytes();
111
- await walkWithin(sandbox, root, maxDepth, async (file, info) => {
112
- if (!info.isFile())
113
- return;
114
- if (isBinaryName(basename(file)))
115
- return;
116
- if (info.size > maxBytes) {
117
- out.line(`${file}: skipped (${formatSize(info.size)} exceeds ${formatSize(maxBytes)})`);
118
- return !out.full;
119
- }
120
- try {
121
- const text = await readTextFile(file, maxBytes);
122
- const lines = text.split(/\r?\n/);
123
- lines.forEach((line, index) => {
124
- if (re.test(line))
125
- out.line(file + ":" + (index + 1) + ": " + line);
126
- });
127
- }
128
- catch {
129
- // Binary or unreadable files are skipped.
130
- }
131
- return !out.full;
132
- });
133
- return out.value();
134
- }
135
-
136
- export async function readTextFile(file: string, limit = readMaxBytes()): Promise<string> {
137
- const info = await stat(file);
138
- if (!info.isFile())
139
- throw new Error(`not a file: ${file}`);
140
- const length = Math.min(info.size, limit);
141
- const handle = await open(file, "r");
142
- try {
143
- const buffer = Buffer.alloc(length);
144
- const { bytesRead } = await handle.read(buffer, 0, length, 0);
145
- const slice = buffer.subarray(0, bytesRead);
146
- if (slice.subarray(0, Math.min(8192, slice.length)).includes(0))
147
- return `[binary file omitted: ${file} (${info.size} bytes)]`;
148
- const text = slice.toString("utf8");
149
- return info.size > length
150
- ? `${text}\n[... truncated at ${length} of ${info.size} bytes; use grep or read a narrower file ...]`
151
- : text;
152
- }
153
- finally {
154
- await handle.close();
155
- }
156
- }
157
-
158
- export function readMaxBytes(): number {
159
- const raw = Number(process.env["FASTCODE_READ_MAX_BYTES"]);
160
- return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 200_000;
161
- }
162
-
163
- function grepMaxBytes(): number {
164
- const raw = Number(process.env["FASTCODE_GREP_MAX_BYTES"]);
165
- return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 1_000_000;
166
- }
167
-
168
- async function walkWithin(sandbox: string, root: string, maxDepth: number, visit: (file: string, info: any) => Promise<boolean | void>): Promise<void> {
169
- const roots = allowedRoots(sandbox).map((entry) => realpathOf(entry)).filter(Boolean) as string[];
170
- const ignore = createIgnore(sandbox);
171
- await walk(root, maxDepth, visit, 0, { roots, visited: 0, maxVisited: 5000, ignore }, []);
172
- }
173
-
174
- async function walk(node: string, maxDepth: number, visit: (file: string, info: any) => Promise<boolean | void>, depth = 0, state: any, chain: string[] = []): Promise<boolean | void> {
175
- if (state.visited >= state.maxVisited)
176
- return;
177
- let info;
178
- try {
179
- info = await stat(node);
180
- }
181
- catch {
182
- return;
183
- }
184
- let real;
185
- try {
186
- real = await realpathOf(node) ?? node;
187
- }
188
- catch {
189
- real = node;
190
- }
191
- if (chain.includes(real))
192
- return;
193
- state.visited += 1;
194
- const insideSandbox = !state.roots || state.roots.length === 0 || isInsideAny(real, state.roots);
195
- let keepGoing: boolean | void = true;
196
- try {
197
- keepGoing = await visit(node, info);
198
- }
199
- catch {
200
- // A visitor failure must not abort the surrounding walk.
201
- }
202
- if (keepGoing === false)
203
- return false;
204
- if (!info.isDirectory() || depth >= maxDepth || !insideSandbox)
205
- return;
206
- let entries;
207
- try {
208
- entries = await readdir(node, { withFileTypes: true });
209
- }
210
- catch {
211
- return;
212
- }
213
- const nextChain = chain.concat(real);
214
- for (const entry of entries) {
215
- if (state.ignore?.ignored(entry.name, join(node, entry.name), entry.isDirectory()))
216
- continue;
217
- if (state.visited >= state.maxVisited)
218
- return;
219
- try {
220
- const keepWalking = await walk(join(node, entry.name), maxDepth, visit, depth + 1, state, nextChain);
221
- if (keepWalking === false)
222
- return false;
223
- }
224
- catch {
225
- // Keep walking siblings.
226
- }
227
- }
228
- }