@vincemakes/kiso-tools-node 0.1.28 → 0.1.30

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/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  * explicit timeout and an output cap so a runaway command cannot flood the
12
12
  * context.
13
13
  *
14
- * Token 轮: reads are RANGEABLE (read_file offset/limit, default head 200
14
+ * the token round: reads are RANGEABLE (read_file offset/limit, default head 200
15
15
  * lines) and search/list are capped (50 / 200) — every truncation carries
16
16
  * an actionable continuation note (deterministic per file state), so the
17
17
  * model always has a path to the full content.
@@ -37,7 +37,7 @@ export declare class PathEscapeError extends Error {
37
37
  }
38
38
  export declare function resolveWithinRoot(root: string, input: string): string;
39
39
  /**
40
- * 十: the canonical path a tool will actually touch, as an ABSOLUTE path.
40
+ * round 10: the canonical path a tool will actually touch, as an ABSOLUTE path.
41
41
  * Symlinks in the deepest EXISTING ancestor are resolved — a file to be
42
42
  * created under a symlinked directory lands in the TARGET, not in the
43
43
  * link — and the not-yet-existing tail is re-appended. Shared by the
@@ -49,7 +49,7 @@ export interface WorkspaceToolsOptions {
49
49
  /** The workspace the tools may touch; everything else is refused. */
50
50
  readonly workspaceRoot: string;
51
51
  /**
52
- * 自举 #3 (发现#7): "inherit" keeps kiso's own provider credentials in
52
+ * bootstrap #3 (finding #7): "inherit" keeps kiso's own provider credentials in
53
53
  * the shell child's environment. DEFAULT (absent): the credentials are
54
54
  * STRIPPED — a shell command must not inherit the agent's API keys (a
55
55
  * nested kiso would hit the REAL provider and blow up faux e2e runs;
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@
11
11
  * explicit timeout and an output cap so a runaway command cannot flood the
12
12
  * context.
13
13
  *
14
- * Token 轮: reads are RANGEABLE (read_file offset/limit, default head 200
14
+ * the token round: reads are RANGEABLE (read_file offset/limit, default head 200
15
15
  * lines) and search/list are capped (50 / 200) — every truncation carries
16
16
  * an actionable continuation note (deterministic per file state), so the
17
17
  * model always has a path to the full content.
@@ -22,7 +22,7 @@ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:pat
22
22
  import { defineTool } from "@vincemakes/kiso-core";
23
23
  const OUTPUT_CAP = 100_000; // chars of output a tool result may carry
24
24
  const DEFAULT_SHELL_TIMEOUT_MS = 30_000;
25
- // Token 轮: the scoped-read defaults — read_file shows the head 200 lines
25
+ // the token round: the scoped-read defaults — read_file shows the head 200 lines
26
26
  // of a large file (with an actionable continuation note, never a silent
27
27
  // drop), search_text caps at 50 excerpts, list_dir at 200 entries. The
28
28
  // red line: every truncation names its continuation — the model always
@@ -88,7 +88,7 @@ export function resolveWithinRoot(root, input) {
88
88
  return canonical;
89
89
  }
90
90
  /**
91
- * 十: the canonical path a tool will actually touch, as an ABSOLUTE path.
91
+ * round 10: the canonical path a tool will actually touch, as an ABSOLUTE path.
92
92
  * Symlinks in the deepest EXISTING ancestor are resolved — a file to be
93
93
  * created under a symlinked directory lands in the TARGET, not in the
94
94
  * link — and the not-yet-existing tail is re-appended. Shared by the
@@ -116,7 +116,7 @@ export function canonicalTargetPath(input) {
116
116
  return missingTail.length > 0 ? join(ancestorReal, ...missingTail) : ancestorReal;
117
117
  }
118
118
  /**
119
- * The inode-boundary policy for READS (): a hard link inside the workspace
119
+ * The inode-boundary policy for READS (round 8): a hard link inside the workspace
120
120
  * may point at an inode whose OTHER links live outside (e.g. /etc/passwd) —
121
121
  * reading it would silently exfiltrate external content. Policy:
122
122
  * - regular, single-link files: read;
@@ -132,13 +132,13 @@ function inodeReadPolicy(root, full) {
132
132
  return `not a regular file — refusing to read (${full})`;
133
133
  if (st.nlink <= 1)
134
134
  return null;
135
- // 第四轮: the link count is verified STRUCTURALLY, never by counting
135
+ // round 4: the link count is verified STRUCTURALLY, never by counting
136
136
  // newline-split text. `find -print0` emits NUL-separated paths — a file
137
137
  // named "inside\nspoof" is ONE path, not two — and every match is then
138
138
  // re-statted and checked for the EXACT dev+ino pair (an inode number
139
139
  // alone is not identity across devices). Any failure to verify every
140
140
  // link is fail-closed: the file is refused.
141
- // 第五轮(P2-3): the workspace root is CANONICALIZED before the scan —
141
+ // round 5(P2-3): the workspace root is CANONICALIZED before the scan —
142
142
  // find on a symlinked root would not follow the symlink into the real
143
143
  // tree, undercounting the in-workspace links and misjudging a legal
144
144
  // hard link as an external escape.
@@ -335,8 +335,8 @@ export function searchTextTool(opts) {
335
335
  }
336
336
  else if (entry.isFile()) {
337
337
  try {
338
- // 八: same inode boundary as read_file — a hard link
339
- // to an external inode is not searched. 第四轮(对抗):
338
+ // round 8: same inode boundary as read_file — a hard link
339
+ // to an external inode is not searched. round 4 (adversarial):
340
340
  // the link count is verified against the WORKSPACE
341
341
  // root, not the search subroot — a link that lives
342
342
  // inside the workspace but outside the search dir is
@@ -397,12 +397,12 @@ export function writeFileTool(opts) {
397
397
  const tmp = `${full}.kiso-tmp-${process.pid}-${crypto.randomUUID()}`;
398
398
  let preservedMode;
399
399
  try {
400
- // E 组: SAFE REPLACEMENT — write a temp file next to the
400
+ // E group: SAFE REPLACEMENT — write a temp file next to the
401
401
  // target and rename it over the directory entry. A hard link
402
402
  // inside the workspace that shares an EXTERNAL inode is
403
403
  // therefore never overwritten: rename replaces the entry,
404
404
  // not the shared inode.
405
- // 八: an existing file keeps its mode — a 0755 script stays
405
+ // round 8: an existing file keeps its mode — a 0755 script stays
406
406
  // 0755 after replacement (rename drops the temp's default
407
407
  // mode, so it is copied onto the temp first).
408
408
  if (existsSync(full))
@@ -422,7 +422,7 @@ export function writeFileTool(opts) {
422
422
  return { content: `wrote ${path} (${content.length} chars)`, isError: false };
423
423
  }
424
424
  catch (err) {
425
- // 八: a failed write never leaves a temp file with the FULL
425
+ // round 8: a failed write never leaves a temp file with the FULL
426
426
  // content behind — it is unlinked in every failure path.
427
427
  try {
428
428
  unlinkSync(tmp);
@@ -468,8 +468,8 @@ export function editFileTool(opts) {
468
468
  if (index === -1) {
469
469
  return { content: `edit_file: pattern not found in ${path}`, isError: true, errorKind: "invalid_input" };
470
470
  }
471
- // E 组: safe replacement — never rewrite a shared external inode via a hard link.
472
- // 八: the edited file keeps its mode.
471
+ // E group: safe replacement — never rewrite a shared external inode via a hard link.
472
+ // round 8: the edited file keeps its mode.
473
473
  preservedMode = statSync(full).mode & 0o7777;
474
474
  writeFileSync(tmp, text.slice(0, index) + replace + text.slice(index + search.length), "utf8");
475
475
  chmodSync(tmp, preservedMode);
@@ -481,7 +481,7 @@ export function editFileTool(opts) {
481
481
  return { content: `edited ${path}`, isError: false };
482
482
  }
483
483
  catch (err) {
484
- // 八: a failed edit never leaves a temp file behind.
484
+ // round 8: a failed edit never leaves a temp file behind.
485
485
  try {
486
486
  unlinkSync(tmp);
487
487
  }
@@ -494,7 +494,7 @@ export function editFileTool(opts) {
494
494
  });
495
495
  }
496
496
  /**
497
- * 自举 #3 (发现#7): the explicit credential list stripped from shell
497
+ * bootstrap #3 (finding #7): the explicit credential list stripped from shell
498
498
  * children — the agent's own provider surface (both families' keys, base
499
499
  * URLs, and model choices) plus the generic API-key / auth-token patterns
500
500
  * that cover other providers. Everything else in the environment passes
@@ -534,7 +534,7 @@ export function shellTool(opts) {
534
534
  },
535
535
  execute: async ({ command, timeoutMs }, ctx) => {
536
536
  const timeout = timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
537
- // E 组: a PRE-aborted signal never spawns the command.
537
+ // E group: a PRE-aborted signal never spawns the command.
538
538
  if (ctx.signal.aborted) {
539
539
  return { content: "shell aborted before start", isError: true, errorKind: "fatal" };
540
540
  }
@@ -542,7 +542,7 @@ export function shellTool(opts) {
542
542
  // detached: the command gets its OWN process group, so a
543
543
  // timeout/abort can kill the WHOLE TREE (children included),
544
544
  // not just the outer shell (Area 4). cwd is the workspace.
545
- // 自举 #3 (发现#7): the shell child NEVER inherits kiso's own
545
+ // bootstrap #3 (finding #7): the shell child NEVER inherits kiso's own
546
546
  // provider credentials by default — only the explicit
547
547
  // shellEnv: "inherit" opt-in keeps them.
548
548
  const child = spawn(command, {
@@ -562,13 +562,13 @@ export function shellTool(opts) {
562
562
  return;
563
563
  settled = true;
564
564
  clearTimeout(timer);
565
- // E 组: the abort listener is removed once settled — it
565
+ // E group: the abort listener is removed once settled — it
566
566
  // must not accumulate across runs.
567
567
  ctx.signal.removeEventListener("abort", onAbort);
568
568
  resolvePromise(result);
569
569
  };
570
570
  /**
571
- * Kill the whole tree and CONFIRM it exited (八/十一):
571
+ * Kill the whole tree and CONFIRM it exited (rounds 8/11):
572
572
  *
573
573
  * 1. FREEZE the root (SIGSTOP) FIRST — a stopped shell cannot
574
574
  * fork new descendants while we enumerate;
@@ -586,7 +586,7 @@ export function shellTool(opts) {
586
586
  */
587
587
  const killTree = () => new Promise((resolveKill) => {
588
588
  const tracked = new Set();
589
- // 十一(对抗): the ROOT itself is tracked too — the
589
+ // round 11 (adversarial): the ROOT itself is tracked too — the
590
590
  // verdict must not read "aborted" while the root
591
591
  // survives. DOCUMENTED LIMITS: (1) a process that
592
592
  // forks between SIGSTOP delivery and the next scan,
@@ -628,7 +628,7 @@ export function shellTool(opts) {
628
628
  }
629
629
  previous = current;
630
630
  }
631
- // The process group (E 组: never kill an undefined/0
631
+ // The process group (E group: never kill an undefined/0
632
632
  // pid), which also takes the frozen root down.
633
633
  if (child.pid !== undefined && child.pid > 0) {
634
634
  try {
@@ -683,7 +683,7 @@ export function shellTool(opts) {
683
683
  : { content: `exit ${code}: ${combined}`, isError: true, errorKind: "fatal" });
684
684
  });
685
685
  // The kernel's abort reaches the command AND its whole tree.
686
- // The listener is removed by settle (E ).
686
+ // The listener is removed by settle (E group).
687
687
  const uncertainVerdict = (unconfirmed) => unconfirmed.length > 0
688
688
  ? `could not confirm ${unconfirmed.length} descendant(s) exited (pids ${unconfirmed.join(", ")}) — treat the side effect as UNCERTAIN`
689
689
  : "";
@@ -711,7 +711,7 @@ export function shellTool(opts) {
711
711
  }
712
712
  /**
713
713
  * All live pids whose ancestor chain includes `pid`, from the pid table
714
- * (八: `ps -axo pid=,ppid=` — the ONLY way to see a setsid()-escaped
714
+ * (round 8: `ps -axo pid=,ppid=` — the ONLY way to see a setsid()-escaped
715
715
  * process, which is in its own group and invisible to a group kill).
716
716
  */
717
717
  function descendantsOf(pid) {
@@ -749,7 +749,7 @@ function descendantsOf(pid) {
749
749
  /**
750
750
  * Poll the pid table until NONE of the tracked pids is alive (bounded).
751
751
  * Returns the pids still alive at the deadline — the caller MUST NOT
752
- * report "aborted"/"timed out" while any tracked pid survives (十一).
752
+ * report "aborted"/"timed out" while any tracked pid survives (round 11).
753
753
  */
754
754
  function waitAllDead(pids) {
755
755
  if (pids.length === 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tools-node",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "kiso coding tools for Node hosts — read file, list directory, search text, write/edit file, shell command.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,7 +21,7 @@
21
21
  "test": "vitest run"
22
22
  },
23
23
  "dependencies": {
24
- "@vincemakes/kiso-core": "0.1.28"
24
+ "@vincemakes/kiso-core": "0.1.29"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^26.1.2",