reasonix 0.26.0 → 0.27.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/README.md +113 -67
- package/README.zh-CN.md +115 -69
- package/dashboard/app.css +14 -3
- package/dashboard/dist/app.js +315 -21
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/index.js +2229 -905
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +68 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -446,6 +446,8 @@ declare class ImmutablePrefix {
|
|
|
446
446
|
toMessages(): ChatMessage[];
|
|
447
447
|
tools(): ToolSpec[];
|
|
448
448
|
addTool(spec: ToolSpec): boolean;
|
|
449
|
+
/** Mirror of addTool for MCP hot-unbridge. Same cache-miss cost — prefix changes shape. */
|
|
450
|
+
removeTool(name: string): boolean;
|
|
449
451
|
get fingerprint(): string;
|
|
450
452
|
/** Dev/test only — throws on cache drift, which always means a non-`addTool` mutation slipped in. */
|
|
451
453
|
verifyFingerprint(): string;
|
|
@@ -619,6 +621,8 @@ declare class ToolRegistry {
|
|
|
619
621
|
/** At most one interceptor active; calling twice replaces. */
|
|
620
622
|
setToolInterceptor(fn: ToolInterceptor | null): void;
|
|
621
623
|
register<A, R>(def: ToolDefinition<A, R>): this;
|
|
624
|
+
/** Drop a registered tool. Returns true if the name was present. Used by MCP hot-unbridge. */
|
|
625
|
+
unregister(name: string): boolean;
|
|
622
626
|
has(name: string): boolean;
|
|
623
627
|
get(name: string): ToolDefinition | undefined;
|
|
624
628
|
get size(): number;
|
|
@@ -1191,7 +1195,7 @@ interface ShellToolsOptions {
|
|
|
1191
1195
|
declare function tokenizeCommand(cmd: string): string[];
|
|
1192
1196
|
/** Up-front detection — without it, `dir | findstr foo` quotes `|` literal and pipe silently fails. */
|
|
1193
1197
|
declare function detectShellOperator(cmd: string): string | null;
|
|
1194
|
-
/**
|
|
1198
|
+
/** Allowlist match on leading argv tokens; demoted by `RISKY_ARGS` when a destructive flag appears in the tail. */
|
|
1195
1199
|
declare function isAllowed(cmd: string, extra?: readonly string[]): boolean;
|
|
1196
1200
|
interface RunCommandResult {
|
|
1197
1201
|
exitCode: number | null;
|
package/dist/index.js
CHANGED
|
@@ -981,6 +981,10 @@ var ToolRegistry = class {
|
|
|
981
981
|
this._tools.set(def.name, internal);
|
|
982
982
|
return this;
|
|
983
983
|
}
|
|
984
|
+
/** Drop a registered tool. Returns true if the name was present. Used by MCP hot-unbridge. */
|
|
985
|
+
unregister(name) {
|
|
986
|
+
return this._tools.delete(name);
|
|
987
|
+
}
|
|
984
988
|
has(name) {
|
|
985
989
|
return this._tools.has(name);
|
|
986
990
|
}
|
|
@@ -1645,6 +1649,14 @@ var ImmutablePrefix = class {
|
|
|
1645
1649
|
this._fingerprintCache = null;
|
|
1646
1650
|
return true;
|
|
1647
1651
|
}
|
|
1652
|
+
/** Mirror of addTool for MCP hot-unbridge. Same cache-miss cost — prefix changes shape. */
|
|
1653
|
+
removeTool(name) {
|
|
1654
|
+
const idx = this._toolSpecs.findIndex((t) => t.function?.name === name);
|
|
1655
|
+
if (idx < 0) return false;
|
|
1656
|
+
this._toolSpecs.splice(idx, 1);
|
|
1657
|
+
this._fingerprintCache = null;
|
|
1658
|
+
return true;
|
|
1659
|
+
}
|
|
1648
1660
|
get fingerprint() {
|
|
1649
1661
|
if (this._fingerprintCache !== null) return this._fingerprintCache;
|
|
1650
1662
|
this._fingerprintCache = this.computeFingerprint();
|
|
@@ -6406,12 +6418,65 @@ function detectShellOperator(cmd) {
|
|
|
6406
6418
|
if (quote) return null;
|
|
6407
6419
|
return check();
|
|
6408
6420
|
}
|
|
6421
|
+
var RISKY_ARGS = {
|
|
6422
|
+
// Branch / remote mutation
|
|
6423
|
+
"git branch": ["-d", "-D", "--delete", "-m", "-M", "--move", "-c", "-C", "--copy", "--force"],
|
|
6424
|
+
"git remote": ["add", "remove", "rm", "rename", "set-url", "set-head", "prune"],
|
|
6425
|
+
// `--output` writes to an arbitrary path; `--ext-diff` invokes user-config'd external programs.
|
|
6426
|
+
"git diff": ["--output", "--ext-diff"],
|
|
6427
|
+
"git log": ["--output"],
|
|
6428
|
+
"git show": ["--output"],
|
|
6429
|
+
// `-exec*` / `-ok*` are RCE; `-delete` and `-fprint*` / `-fls` write to arbitrary paths.
|
|
6430
|
+
find: [
|
|
6431
|
+
"-delete",
|
|
6432
|
+
"-exec",
|
|
6433
|
+
"-execdir",
|
|
6434
|
+
"-ok",
|
|
6435
|
+
"-okdir",
|
|
6436
|
+
"-fprint",
|
|
6437
|
+
"-fprint0",
|
|
6438
|
+
"-fprintf",
|
|
6439
|
+
"-fls"
|
|
6440
|
+
],
|
|
6441
|
+
// `-o FILE` writes the tree to an arbitrary path.
|
|
6442
|
+
tree: ["-o"],
|
|
6443
|
+
// Auto-fix mutates source files.
|
|
6444
|
+
"npx eslint": ["--fix", "--fix-dry-run"],
|
|
6445
|
+
"npx biome check": ["--write", "--apply", "--apply-unsafe"],
|
|
6446
|
+
ruff: ["--fix", "--unsafe-fixes", "format"]
|
|
6447
|
+
};
|
|
6448
|
+
function tailHasRisky(tail, risky) {
|
|
6449
|
+
for (const a of tail) {
|
|
6450
|
+
for (const r of risky) {
|
|
6451
|
+
if (a === r) return true;
|
|
6452
|
+
if (a.startsWith(`${r}=`)) return true;
|
|
6453
|
+
}
|
|
6454
|
+
}
|
|
6455
|
+
return false;
|
|
6456
|
+
}
|
|
6409
6457
|
function isAllowed(cmd, extra = []) {
|
|
6410
|
-
|
|
6458
|
+
let argv;
|
|
6459
|
+
try {
|
|
6460
|
+
argv = tokenizeCommand(cmd);
|
|
6461
|
+
} catch {
|
|
6462
|
+
return false;
|
|
6463
|
+
}
|
|
6464
|
+
if (argv.length === 0) return false;
|
|
6411
6465
|
const allowlist = [...BUILTIN_ALLOWLIST, ...extra];
|
|
6412
6466
|
for (const prefix of allowlist) {
|
|
6413
|
-
|
|
6414
|
-
if (
|
|
6467
|
+
const prefixTokens = prefix.split(" ");
|
|
6468
|
+
if (argv.length < prefixTokens.length) continue;
|
|
6469
|
+
let match = true;
|
|
6470
|
+
for (let i = 0; i < prefixTokens.length; i++) {
|
|
6471
|
+
if (argv[i] !== prefixTokens[i]) {
|
|
6472
|
+
match = false;
|
|
6473
|
+
break;
|
|
6474
|
+
}
|
|
6475
|
+
}
|
|
6476
|
+
if (!match) continue;
|
|
6477
|
+
const risky = RISKY_ARGS[prefix];
|
|
6478
|
+
if (risky && tailHasRisky(argv.slice(prefixTokens.length), risky)) return false;
|
|
6479
|
+
return true;
|
|
6415
6480
|
}
|
|
6416
6481
|
return false;
|
|
6417
6482
|
}
|