@tbox.cn/tboxcode-dsh-tools 0.1.0-rc.3 → 0.1.0-rc.5
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/lib/auto-commit/index.d.ts +2 -0
- package/lib/auto-commit/index.js +1 -0
- package/lib/auto-commit-FhjBfiMx.js +1 -0
- package/lib/{index-9ZM90CvT.d.ts → index-BNOJI4FD.d.ts} +13 -4
- package/lib/index-CNU9S13j.d.ts +52 -0
- package/lib/index-bkf64gGn.d.ts +34 -0
- package/lib/index.d.ts +7 -6
- package/lib/index.js +1 -1
- package/lib/pre-execute/index.d.ts +2 -2
- package/lib/pre-execute/index.js +1 -1
- package/lib/pre-execute-AkMe7eQo.js +1 -0
- package/lib/restrict/index.d.ts +1 -1
- package/lib/scratch/index.d.ts +2 -2
- package/lib/scratch/index.js +1 -1
- package/lib/scratch-ak4Wn8Xe.js +2 -0
- package/lib/skill-provider/index.d.ts +1 -1
- package/lib/webfetch/index.d.ts +1 -1
- package/package.json +6 -1
- package/lib/index-DHJdScQt.d.ts +0 -21
- package/lib/pre-execute-CSOIc_CR.js +0 -1
- package/lib/scratch-9LkFiTBl.js +0 -2
- /package/lib/{index-DjflX-te.d.ts → index-Bb9goxWJ.d.ts} +0 -0
- /package/lib/{index-NdtUHa7X.d.ts → index-C_e95eAG.d.ts} +0 -0
- /package/lib/{index-CSv9cWMb.d.ts → index-D8NxTQT6.d.ts} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as RunAutoCommitOptions, c as name, d as shouldSkip, i as GitRunner, l as pushOnly, n as Config, o as apply, r as GitResult, s as defaultGitRunner, t as AutoCommitOutcome, u as runAutoCommit } from "../index-CNU9S13j.js";
|
|
2
|
+
export { AutoCommitOutcome, Config, GitResult, GitRunner, RunAutoCommitOptions, apply, defaultGitRunner, name, pushOnly, runAutoCommit, shouldSkip };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,i as t,n,o as r,r as i,t as a}from"../auto-commit-FhjBfiMx.js";export{a as apply,n as defaultGitRunner,i as name,t as pushOnly,e as runAutoCommit,r as shouldSkip};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z as e}from"zod";import{join as t}from"node:path";import{existsSync as n}from"node:fs";import{execFile as r}from"node:child_process";import{promisify as i}from"node:util";const a=i(r),o=`tboxcode-auto-commit`,s=e.object({enabled:e.boolean().optional(),push:e.boolean().optional(),remote:e.string().optional(),branch:e.string().optional(),commitMessage:e.string().optional(),pendingRetryMs:e.number().int().positive().optional(),authorName:e.string().optional(),authorEmail:e.string().optional(),maxAttempts:e.number().int().min(1).max(10).optional()}),c=async(e,t,n)=>{let r=await a(`git`,[...e],{cwd:t,env:{...process.env,...n},maxBuffer:16*1024*1024});return{stdout:r.stdout,stderr:r.stderr}};function l(e){return{GIT_AUTHOR_NAME:e.authorName??process.env.TBOX_GIT_AUTHOR_NAME??`CodingBox`,GIT_AUTHOR_EMAIL:e.authorEmail??process.env.TBOX_GIT_AUTHOR_EMAIL??`codingbox@tbox.cn`,GIT_COMMITTER_NAME:e.authorName??process.env.TBOX_GIT_AUTHOR_NAME??`CodingBox`,GIT_COMMITTER_EMAIL:e.authorEmail??process.env.TBOX_GIT_AUTHOR_EMAIL??`codingbox@tbox.cn`}}function u(e,r){if(e===void 0||e.length===0)return{kind:`skipped`,reason:`no-cwd`};if(r.enabled!==!0)return{kind:`skipped`,reason:`disabled`};if(n(t(e,`.git`,`TBOX_INIT_IN_PROGRESS`)))return{kind:`skipped`,reason:`init-in-progress`}}async function d(e,t,n,r){let i=n.remote??`origin`,a=n.branch,o=n.push===!1?[]:a===void 0?[`push`]:[`push`,i,a];if(o.length!==0)try{await e(o,t,r);return}catch{await e([`push`,i,`HEAD`],t,r)}}async function f(e){let{cwd:t,config:n}=e;await d(e.git??c,t,n,l(n))}async function p(e){let{cwd:t,config:n}=e,r=u(t,n);if(r!==void 0)return r;let i=e.git??c,a=l(n);if((await i([`status`,`--porcelain`],t,a)).stdout.trim().length===0)return{kind:`noop`};await i([`add`,`-A`],t,a);let o=n.commitMessage??`chore: auto-commit at ${(e.now??(()=>new Date))().toISOString()}`;await i([`commit`,`--no-verify`,`-m`,o],t,a);try{return await d(i,t,n,a),{kind:`committed`,pushed:n.push!==!1,commit:o}}catch(e){return{kind:`push-failed`,commit:o,error:e instanceof Error?e.message:String(e)}}}function m(e,t={}){let n=s.parse(t),r=n.maxAttempts??3,i=n.pendingRetryMs??3e4,a=new Map,o=new Set,c=e.logger,l=async t=>{let a=!1,s;for(let l=1;l<=r;l++)try{if(a){await f({cwd:t,config:n}),o.delete(t),e.emit?.(`tboxcode/git_push`,{cwd:t,ok:!0,...s===void 0?{}:{commit:s},attempt:l});return}let u=await p({cwd:t,config:n});if(u.kind===`push-failed`){if(o.add(t),s=u.commit,a=!0,e.emit?.(`tboxcode/git_push`,{cwd:t,ok:!1,error:u.error,attempt:l}),c?.warn(`[tboxcode-auto-commit] push failed (${t}): ${u.error}`),l<r){await new Promise(e=>setTimeout(e,l*i));continue}return}o.delete(t),u.kind===`committed`&&e.emit?.(`tboxcode/git_push`,{cwd:t,ok:!0,commit:u.commit});return}catch(e){if(c?.warn(`[tboxcode-auto-commit] attempt ${l} failed: ${e instanceof Error?e.message:String(e)}`),l>=r)return;await new Promise(e=>setTimeout(e,l*i))}},d=e=>{if(a.has(e))return;let t=l(e).catch(e=>c?.warn(`[tboxcode-auto-commit] ${e instanceof Error?e.message:String(e)}`)).finally(()=>a.delete(e));a.set(e,t)};if(e.on(`session/event`,(e,t)=>{if(t.type!==`turn/end`)return;let r=e.header;if(r?.origin===`subagent`||r?.parentSession!==void 0)return;let i=r?.cwd;i!==void 0&&u(i,n)===void 0&&d(i)}),n.enabled===!0)for(let e of o)d(e);return()=>{a.clear(),o.clear()}}export{p as a,f as i,c as n,u as o,o as r,m as t};
|
|
@@ -9,6 +9,8 @@ interface Config {
|
|
|
9
9
|
permission?: Record<string, 'allow' | 'ask' | 'deny'>;
|
|
10
10
|
/** plan 档 MCP 写类模式表(后缀或完整名;缺省内置写类动词表)。 */
|
|
11
11
|
mcpDenyPatterns?: string[];
|
|
12
|
+
/** 拒绝访问的绝对/相对路径前缀(pre-execute 最佳努力;非 per-path sandbox)。 */
|
|
13
|
+
denyPaths?: string[];
|
|
12
14
|
}
|
|
13
15
|
/** 内置 MCP 写类模式表(保守后缀匹配——误伤面最小化,Revisions 维护义务)。 */
|
|
14
16
|
declare const DEFAULT_MCP_DENY_PATTERNS: readonly string[];
|
|
@@ -16,10 +18,17 @@ declare const DEFAULT_MCP_DENY_PATTERNS: readonly string[];
|
|
|
16
18
|
* dsh 侧无 sideEffecting/defaultPermission 面——无匹配返回 undefined 透传):
|
|
17
19
|
* exact → 最长 `prefix_*` 通配 → 组精确/通配 → `*` 全局。 */
|
|
18
20
|
declare function resolveToolPermission(toolName: string, permission: Record<string, 'allow' | 'ask' | 'deny'> | undefined, permissionGroups?: readonly string[]): 'allow' | 'ask' | 'deny' | undefined;
|
|
19
|
-
/** MCP
|
|
20
|
-
*
|
|
21
|
-
*
|
|
21
|
+
/** MCP 写类命中:只认 dsh mcp-client 的 `mcp__<server>__<tool>` 命名空间;
|
|
22
|
+
* `_` 分段精确匹配(模式词剥前导 `_`;如 `mcp__github__create_issue` 的
|
|
23
|
+
* [mcp, github, create, issue] 含 'create')或完整名匹配。分段制覆盖写类
|
|
24
|
+
* 动词位于任意段位的 MCP sanitized 名形态。原生下划线工具(scratch_write /
|
|
25
|
+
* todo_write / str_replace_editor 等)不进入此判定——命名空间前缀是唯一
|
|
26
|
+
* 可靠区分面(ToolExecution 不携带工具来源元数据)。 */
|
|
22
27
|
declare function matchesMcpDenyPattern(toolName: string, patterns: readonly string[]): boolean;
|
|
28
|
+
/** 从 exec.arguments 收集路径(已知键 + bash command token)。 */
|
|
29
|
+
declare function collectExecutionPaths(name: string, args: unknown): string[];
|
|
30
|
+
/** 命中任一 denyPaths 前缀即返回命中的配置项。 */
|
|
31
|
+
declare function matchDenyPath(candidate: string, denyPaths: readonly string[], cwd: string): string | undefined;
|
|
23
32
|
declare function apply(ctx: Context, config?: Config): () => void;
|
|
24
33
|
//#endregion
|
|
25
|
-
export {
|
|
34
|
+
export { inject as a, name as c, collectExecutionPaths as i, resolveToolPermission as l, DEFAULT_MCP_DENY_PATTERNS as n, matchDenyPath as o, apply as r, matchesMcpDenyPattern as s, Config as t };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
|
|
3
|
+
//#region src/auto-commit/index.d.ts
|
|
4
|
+
declare const name = "tboxcode-auto-commit";
|
|
5
|
+
interface Config {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
push?: boolean;
|
|
8
|
+
remote?: string;
|
|
9
|
+
branch?: string;
|
|
10
|
+
commitMessage?: string;
|
|
11
|
+
pendingRetryMs?: number;
|
|
12
|
+
authorName?: string;
|
|
13
|
+
authorEmail?: string;
|
|
14
|
+
maxAttempts?: number;
|
|
15
|
+
}
|
|
16
|
+
interface GitResult {
|
|
17
|
+
stdout: string;
|
|
18
|
+
stderr: string;
|
|
19
|
+
}
|
|
20
|
+
type GitRunner = (args: readonly string[], cwd: string, env?: NodeJS.ProcessEnv) => Promise<GitResult>;
|
|
21
|
+
declare const defaultGitRunner: GitRunner;
|
|
22
|
+
type AutoCommitOutcome = {
|
|
23
|
+
kind: 'skipped';
|
|
24
|
+
reason: 'no-cwd' | 'subagent' | 'init-in-progress' | 'disabled';
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'noop';
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'committed';
|
|
29
|
+
pushed: boolean;
|
|
30
|
+
commit: string;
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'push-failed';
|
|
33
|
+
commit: string;
|
|
34
|
+
error: string;
|
|
35
|
+
};
|
|
36
|
+
interface RunAutoCommitOptions {
|
|
37
|
+
cwd: string;
|
|
38
|
+
config: Config;
|
|
39
|
+
git?: GitRunner;
|
|
40
|
+
now?: () => Date;
|
|
41
|
+
}
|
|
42
|
+
/** 初始化标记 / 非 git 目录 / 无 cwd 的跳过判定。 */
|
|
43
|
+
declare function shouldSkip(cwd: string | undefined, config: Config): AutoCommitOutcome | undefined;
|
|
44
|
+
/** 仅重试 push(commit 已落的 pending 补推;不伪造新 commit)。 */
|
|
45
|
+
declare function pushOnly(options: Omit<RunAutoCommitOptions, 'config'> & {
|
|
46
|
+
config: Config;
|
|
47
|
+
}): Promise<void>;
|
|
48
|
+
/** 一次 turn/end 的自动提交(纯函数;测试注入 git runner)。 */
|
|
49
|
+
declare function runAutoCommit(options: RunAutoCommitOptions): Promise<AutoCommitOutcome>;
|
|
50
|
+
declare function apply(ctx: Context, config?: Config): () => void;
|
|
51
|
+
//#endregion
|
|
52
|
+
export { RunAutoCommitOptions as a, name as c, shouldSkip as d, GitRunner as i, pushOnly as l, Config as n, apply as o, GitResult as r, defaultGitRunner as s, AutoCommitOutcome as t, runAutoCommit as u };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
|
|
3
|
+
//#region src/scratch/index.d.ts
|
|
4
|
+
declare const name = "tboxcode-scratch";
|
|
5
|
+
declare const inject: string[];
|
|
6
|
+
/** 结构面(dsh 类型子集——零 peer 膨胀)。 */
|
|
7
|
+
interface SandboxExecutionPolicy {
|
|
8
|
+
mode: string;
|
|
9
|
+
workspaceRoot: string;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
}
|
|
12
|
+
/** 会话私有 scratch 目录(布局单源约定:与 bundle/prompts.ts 的 scratchDirFor 保持一致)。 */
|
|
13
|
+
declare function scratchDirFor(sessionId: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* session cwd(历史导出保留——旧解析语义;scratch 内部自 2026-11-03 起不再
|
|
16
|
+
* 使用,路径根 = scratch 目录,决策 [plan-mode-sandbox-composition] Revisions)。
|
|
17
|
+
*/
|
|
18
|
+
declare function resolveWithSessionCwd(modelPath: string | undefined, cwd: string | undefined): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* scratch 根解析:相对 → scratch 目录(node:path.resolve 归一);绝对原样;
|
|
21
|
+
* undefined → scratch 目录。
|
|
22
|
+
*/
|
|
23
|
+
declare function resolveScratchPath(modelPath: string | undefined, scratchDir: string): string;
|
|
24
|
+
/** per-call 特赦 policy 构造(工作区 = scratch 目录——/tmp 系由 workspace-write
|
|
25
|
+
* 档 writableRoots 原生覆盖)。 */
|
|
26
|
+
declare function scratchPolicy(sessionId: string): SandboxExecutionPolicy;
|
|
27
|
+
/**
|
|
28
|
+
* 幂等 ensure:根与会话目录都必须是真实目录。
|
|
29
|
+
* 竞态(ENOTDIR / EEXIST / ENOENT)有限重试;最终失败带路径与底层 cause。
|
|
30
|
+
*/
|
|
31
|
+
declare function ensureScratchDir(dir: string): Promise<void>;
|
|
32
|
+
declare function apply(ctx: Context): () => void;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { resolveScratchPath as a, scratchPolicy as c, name as i, ensureScratchDir as n, resolveWithSessionCwd as o, inject as r, scratchDirFor as s, apply as t };
|
package/lib/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as
|
|
3
|
-
import { i as name$
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { c as name, d as shouldSkip, l as pushOnly, n as Config, o as apply, t as AutoCommitOutcome, u as runAutoCommit } from "./index-CNU9S13j.js";
|
|
2
|
+
import { i as name$5, n as apply$5 } from "./index-Bb9goxWJ.js";
|
|
3
|
+
import { i as collectCwdUpSkillRoots, n as TBOXCODE_SKILL_RANK, o as name$4, r as apply$4 } from "./index-C_e95eAG.js";
|
|
4
|
+
import { i as name$2, n as apply$2 } from "./index-D8NxTQT6.js";
|
|
5
|
+
import { c as name$1, i as collectExecutionPaths, l as resolveToolPermission, n as DEFAULT_MCP_DENY_PATTERNS, o as matchDenyPath, r as apply$1, s as matchesMcpDenyPattern } from "./index-BNOJI4FD.js";
|
|
6
|
+
import { a as resolveScratchPath, c as scratchPolicy, i as name$3, o as resolveWithSessionCwd, s as scratchDirFor, t as apply$3 } from "./index-bkf64gGn.js";
|
|
6
7
|
import { ToolRunContext } from "@deepseek-ai/dsh-tools";
|
|
7
8
|
|
|
8
9
|
//#region src/webfetch/webfetch-core.d.ts
|
|
@@ -70,4 +71,4 @@ declare const MAX_SKILL_DESC = 1024;
|
|
|
70
71
|
/** 折行 → 截断至 MAX_SKILL_DESC 码点(确定性——截断点外变更不改 hash)。 */
|
|
71
72
|
declare function formatSkillDesc(desc: string | undefined): string;
|
|
72
73
|
//#endregion
|
|
73
|
-
export { DEFAULT_MCP_DENY_PATTERNS, MAX_SKILL_DESC, TBOXCODE_SKILL_RANK, TRUNCATE_MAX_BYTES, TRUNCATE_MAX_LINES, type TruncateOptions, type TruncateSink, type WebfetchDeps, type WebfetchInput, collectCwdUpSkillRoots, detectRedirect, executeWebfetch, extractTextFromHTML, formatSkillDesc, matchesMcpDenyPattern, parseSkillFile, apply as preExecuteApply, name as preExecuteName, readLimitedResponse, resolveToolPermission, resolveWithSessionCwd, apply$
|
|
74
|
+
export { type Config as AutoCommitConfig, type AutoCommitOutcome, DEFAULT_MCP_DENY_PATTERNS, MAX_SKILL_DESC, TBOXCODE_SKILL_RANK, TRUNCATE_MAX_BYTES, TRUNCATE_MAX_LINES, type TruncateOptions, type TruncateSink, type WebfetchDeps, type WebfetchInput, apply as autoCommitApply, name as autoCommitName, collectCwdUpSkillRoots, collectExecutionPaths, detectRedirect, executeWebfetch, extractTextFromHTML, formatSkillDesc, matchDenyPath, matchesMcpDenyPattern, parseSkillFile, apply$1 as preExecuteApply, name$1 as preExecuteName, pushOnly, readLimitedResponse, resolveScratchPath, resolveToolPermission, resolveWithSessionCwd, apply$2 as restrictApply, name$2 as restrictName, runAutoCommit, apply$3 as scratchApply, scratchDirFor, name$3 as scratchName, scratchPolicy, shouldSkip, apply$4 as skillProviderApply, name$4 as skillProviderName, truncateOutput, apply$5 as webfetchApply, name$5 as webfetchName };
|
package/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,c as t,i as n,l as r,o as i,r as a,s as o,t as s,u as c}from"./webfetch-CXvjRbDU.js";import{a as l,c as u,n as d,o as f,r as p,s as m,t as h}from"./skill-provider-DenqO0Yu.js";import{r as g,t as _}from"./restrict-CldLToKG.js";import{a as v,
|
|
1
|
+
import{a as e,c as t,i as n,l as r,o as i,r as a,s as o,t as s,u as c}from"./webfetch-CXvjRbDU.js";import{a as l,c as u,n as d,o as f,r as p,s as m,t as h}from"./skill-provider-DenqO0Yu.js";import{r as g,t as _}from"./restrict-CldLToKG.js";import{a as v,c as y,n as b,o as x,r as S,s as C,t as w}from"./pre-execute-AkMe7eQo.js";import{a as T,i as E,o as D,r as O,t as k}from"./auto-commit-FhjBfiMx.js";import{a as A,c as j,i as M,o as N,s as P,t as F}from"./scratch-ak4Wn8Xe.js";export{w as DEFAULT_MCP_DENY_PATTERNS,f as MAX_SKILL_DESC,h as TBOXCODE_SKILL_RANK,n as TRUNCATE_MAX_BYTES,e as TRUNCATE_MAX_LINES,k as autoCommitApply,O as autoCommitName,p as collectCwdUpSkillRoots,S as collectExecutionPaths,i as detectRedirect,o as executeWebfetch,t as extractTextFromHTML,m as formatSkillDesc,v as matchDenyPath,x as matchesMcpDenyPattern,u as parseSkillFile,b as preExecuteApply,C as preExecuteName,E as pushOnly,r as readLimitedResponse,A as resolveScratchPath,y as resolveToolPermission,N as resolveWithSessionCwd,_ as restrictApply,g as restrictName,T as runAutoCommit,F as scratchApply,P as scratchDirFor,M as scratchName,j as scratchPolicy,D as shouldSkip,d as skillProviderApply,l as skillProviderName,c as truncateOutput,s as webfetchApply,a as webfetchName};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { Config, DEFAULT_MCP_DENY_PATTERNS, apply, inject, matchesMcpDenyPattern, name, resolveToolPermission };
|
|
1
|
+
import { a as inject, c as name, i as collectExecutionPaths, l as resolveToolPermission, n as DEFAULT_MCP_DENY_PATTERNS, o as matchDenyPath, r as apply, s as matchesMcpDenyPattern, t as Config } from "../index-BNOJI4FD.js";
|
|
2
|
+
export { Config, DEFAULT_MCP_DENY_PATTERNS, apply, collectExecutionPaths, inject, matchDenyPath, matchesMcpDenyPattern, name, resolveToolPermission };
|
package/lib/pre-execute/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,
|
|
1
|
+
import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s}from"../pre-execute-AkMe7eQo.js";export{s as DEFAULT_MCP_DENY_PATTERNS,r as apply,a as collectExecutionPaths,n as inject,e as matchDenyPath,i as matchesMcpDenyPattern,o as name,t as resolveToolPermission};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z as e}from"zod";import{homedir as t}from"node:os";import{isAbsolute as n,join as r,resolve as i,sep as a}from"node:path";import{existsSync as o,realpathSync as s}from"node:fs";const c=`tboxcode-pre-execute`,l=[`tools`],u=e.enum([`allow`,`ask`,`deny`]),d=e.object({permission:e.record(e.string(),u).optional(),mcpDenyPatterns:e.array(e.string()).optional(),denyPaths:e.array(e.string()).optional()}),f=[`_write`,`_create`,`_update`,`_delete`,`_remove`,`_patch`,`_modify`,`_rename`,`_move`,`_copy`,`_upload`,`_send`,`_deploy`,`_publish`,`_commit`,`_push`,`_post`,`_put`,`_insert`,`_replace`];function p(e,t,n){if(t===void 0)return;let r=t[e];if(r!==void 0)return r;let i=-1,a;for(let n of Object.keys(t))if(n.endsWith(`_*`)){let r=n.slice(0,-2);e.startsWith(`${r}_`)&&r.length>i&&(i=r.length,a=t[n])}if(a!==void 0)return a;if(n!==void 0&&n.length>0){for(let e of n){let n=t[e];if(n!==void 0)return n}for(let e of Object.keys(t))if(e.endsWith(`:*`)){let r=e.slice(0,-2);for(let i of n)if(i.startsWith(`${r}:`))return t[e]}}return t[`*`]}function m(e,t){if(!e.startsWith(`mcp__`))return!1;let n=new Set(e.split(`_`));for(let r of t){if(r===e)return!0;let t=r.startsWith(`_`)?r.slice(1):r;if(t.length>0&&n.has(t))return!0}return!1}const h=new Set([`path`,`paths`,`file`,`files`,`file_path`,`filePath`,`target`,`targetPath`,`workdir`,`cwd`,`directory`,`dir`,`location`,`source`,`destination`]);function g(e,a){let c=e.trim();if(c.length===0)return;let l=c===`~`?t():c.startsWith(`~/`)?r(t(),c.slice(2)):c,u=n(l)?i(l):i(a,l);try{return o(u)?s(u):u}catch{return u}}function _(e){let t=e.replace(/\/+$/,``)||`/`;if(t===`/`)return e=>e.startsWith(`/`);if(!t.includes(`*`))return e=>e===t||e.startsWith(t+a);let n=t.replace(/[.+?^${}()|[\]\\]/g,`\\$&`),r=RegExp(`^${n.replace(/\*\*/g,`.*`).replace(/\*/g,`[^/]*`)}(?:/|$)`);return e=>r.test(e)}function v(e,t){let n=[],r=(t,i,a)=>{if(!(a>6||t==null)){if(typeof t==`string`){i!==void 0&&h.has(i)&&n.push(t);return}if(Array.isArray(t)){for(let e of t)r(e,i,a+1);return}if(typeof t==`object`)for(let[i,o]of Object.entries(t)){if(i===`command`&&typeof o==`string`&&(e===`bash`||e===`scratch_bash`)){for(let e of o.split(/\s+/)){let t=e.replace(/^['"]|['"]$/g,``);(t.startsWith(`~`)||t.includes(`/`))&&n.push(t)}continue}r(o,i,a+1)}}};return r(t,void 0,0),n}function y(e,t,n){let r=g(e,n);if(r!==void 0)for(let e of t){let t=g(e,n);if(t!==void 0&&_(t)(r))return e}}function b(e){let t=e.agent?.session?.header;return typeof t?.cwd==`string`&&t.cwd.length>0?t.cwd:process.cwd()}function x(e,t={}){let n=d.parse(t),r=n.permission,i=n.denyPaths??[],a=n.mcpDenyPatterns??f,o=e.get(`sessionProjections`);return e.on(`tools/pre-execute`,async(e,t)=>{if(i.length>0){let t=b(e);for(let n of v(e.name,e.arguments)){let e=y(n,i,t);if(e!==void 0)return{kind:`deny`,reason:`Path '${n}' is denied by the security configuration (matched '${e}'). Do not retry this path; use an allowed location or ask the user.`}}}if(p(e.name,r)===`deny`)return{kind:`deny`,reason:`Tool '${e.name}' is denied by the permission configuration. Do not retry this tool; use an alternative approach or ask the user.`};if(o!==void 0&&e.agent!==void 0)try{if(o.stateOf(e.agent.session,`sandboxMode`)===`read-only`&&m(e.name,a))return{kind:`deny`,reason:`Plan mode is active (read-only): the MCP tool '${e.name}' may modify external systems, so it is blocked. Record the intended action in the plan instead, or ask the user to switch to build mode to execute it.`}}catch{}return t()}),()=>{}}export{y as a,p as c,l as i,x as n,m as o,v as r,c as s,f as t};
|
package/lib/restrict/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as name, n as apply, r as inject, t as Config } from "../index-
|
|
1
|
+
import { i as name, n as apply, r as inject, t as Config } from "../index-D8NxTQT6.js";
|
|
2
2
|
export { Config, apply, inject, name };
|
package/lib/scratch/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { apply, inject, name, resolveWithSessionCwd, scratchDirFor, scratchPolicy };
|
|
1
|
+
import { a as resolveScratchPath, c as scratchPolicy, i as name, n as ensureScratchDir, o as resolveWithSessionCwd, r as inject, s as scratchDirFor, t as apply } from "../index-bkf64gGn.js";
|
|
2
|
+
export { apply, ensureScratchDir, inject, name, resolveScratchPath, resolveWithSessionCwd, scratchDirFor, scratchPolicy };
|
package/lib/scratch/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,
|
|
1
|
+
import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s}from"../scratch-ak4Wn8Xe.js";export{s as apply,r as ensureScratchDir,a as inject,n as name,e as resolveScratchPath,i as resolveWithSessionCwd,o as scratchDirFor,t as scratchPolicy};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{defineTool as e}from"@deepseek-ai/dsh-tools";import{lstat as t,mkdir as n,rename as r}from"node:fs/promises";import{tmpdir as i}from"node:os";import{basename as a,dirname as o,isAbsolute as s,join as c,resolve as l,sep as u}from"node:path";import{randomUUID as d}from"node:crypto";const f=`tboxcode-scratch`,p=[`tools`,`shell`,`fs`],m=c(i(),`tboxcode-scratch`);function h(e){return c(m,e)}function g(e,t){if(e===void 0)return t;if(s(e))return e;if(t!==void 0)return`${t}${u}${e}`}function _(e,t){return e===void 0?t:s(e)?e:l(t,e)}function v(e){return{mode:`workspace-write`,workspaceRoot:h(e),sessionId:e}}async function y(e){try{return await t(e)}catch(e){let t=e.code;if(t===`ENOENT`||t===`ENOTDIR`)return;throw e}}async function b(e){let t=c(o(e),`.${a(e)}.displaced-${Date.now()}-${d().slice(0,8)}`);try{await r(e,t)}catch(e){if(e.code!==`ENOENT`)throw e}}async function x(e){let t=await y(e);t?.isDirectory()||(t!==void 0&&await b(e),await n(e,{recursive:!0,mode:448}))}async function S(e){let t=o(e),n;for(let r=0;r<3;r++)try{await x(t),await x(e);return}catch(e){n=e;let t=e.code;if(t!==`ENOTDIR`&&t!==`EEXIST`&&t!==`ENOENT`)break}let r=n instanceof Error?n.message:String(n);throw Error(`scratch directory "${e}" could not be prepared: ${r}`)}function C(e){return e===void 0?``:e.truncated===!0?`${e.text}\n[output truncated; full output: ${e.spillPath??`(unavailable)`}]`:e.text}function w(e,t){let n=[`cwd: ${t}`],r=C(e.stdout),i=C(e.stderr);return n.push(`exitCode: ${e.exitCode??`null`}${e.timedOut?` (timed out)`:``}${e.aborted?` (aborted)`:``}`),e.signal&&n.push(`signal: ${e.signal}`),r.trim().length>0&&n.push(`stdout:\n${r}`),i.trim().length>0&&n.push(`stderr:\n${i}`),e.sandbox?.runnerFailed?n.push(`[sandbox: runner failed under ${e.sandbox.mode} mode]`):e.sandbox?.denied&&n.push(`[sandbox: file access denied under ${e.sandbox.mode} mode]`),n.join(`
|
|
2
|
+
`)}function T(e){return typeof e==`object`&&!!e&&e.code===`FS_SANDBOX_DENIED`}function E(e,t,n){return n?`cannot write "${e}": plan (read-only) mode allows writes only inside ${t} (system temp). Record workspace changes in the plan, or ask the user to switch to build mode.`:`cannot write "${e}": use write/edit for workspace files (scratch tools default to ${t}).`}const D={schema:{type:`object`,additionalProperties:!1,properties:{path:{type:`string`,required:!0,description:`Absolute path written.`},operation:{type:`string`,required:!0,enum:[`create`,`update`]},before:{required:!0,oneOf:[{type:`string`},{type:`null`}]},after:{type:`string`,required:!0},message:{type:`string`,required:!0}}},render:(e,t)=>[{type:`text`,text:t.message}],presentationMeta:(e,t)=>({path:t.path,operation:t.operation,diffs:[{path:t.path,oldText:t.before,newText:t.after}]})};function O(t){let n=t.shell,r=t.fs,i=async e=>{let n;try{if(n=e?.session?.header?.id,n===void 0)return;await S(h(n))}catch(e){try{t.logger?.warn?.(`scratch: pre-create failed for ${n??`unknown`}: ${e instanceof Error?e.message:String(e)}`)}catch{}}},a=t.on(`agent/created`,({agent:e})=>i(e)),o=e=>{let t=e.agent?.session?.header?.id??`adhoc-${d()}`;return{sessionId:t,dir:h(t)}},s=e=>{try{let n=t.get?.(`sandboxPolicy`),r=e.agent?.session;return n===void 0||r===void 0?void 0:n.resolve({session:r})}catch{return}},c=(e,t)=>t===void 0||t.mode===`read-only`?v(e):t,l=[t.tools.register(e({name:`scratch_bash`,description:`Run a bash command in the session scratch directory (system temp), not the project workspace. The working directory and relative paths resolve inside the scratch directory; use absolute paths or cd to inspect the workspace. In plan (read-only) mode this is the writable exception (scratch and system temp only); in build mode it follows the session policy like bash.`,parameters:{command:{type:`string`,required:!0,description:`The bash command to execute inside the scratch directory.`},description:{type:`string`,required:!0,description:`Clear, concise description of what this command does, 5-10 words.`},timeoutMs:{type:`number`,description:`Timeout in milliseconds; the executor applies its configured default and cap.`}},output:{schema:{type:`string`},render:(e,t)=>[{type:`text`,text:String(t)}]},async execute(e,t){let{sessionId:r,dir:i}=o(t),a=c(r,s(t));await S(i);let l={command:e.command,workdir:i,...e.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs},signal:t.signal,sandboxPolicy:a},u=n.resolve(l);return w(await(await n.execute(u)).result(),i)}})),t.tools.register(e({name:`scratch_write`,description:`Create or overwrite a file in the session scratch directory (system temp). Relative paths resolve inside the scratch directory, not the workspace; absolute paths follow the session policy. Prefer write for workspace files. In plan (read-only) mode only the scratch directory and system temp are writable.`,parameters:{filePath:{type:`string`,required:!0,description:`Target file path; relative paths resolve inside the scratch directory.`},content:{type:`string`,required:!0,description:`Full file content to write.`}},output:D,async execute(e,t){let{sessionId:n,dir:i}=o(t),a=s(t),l=c(n,a);await S(i);let u=_(e.filePath,i),d=await r.resolve(u,{signal:t.signal}),f;try{f=await r.writeText(d,e.content,void 0,t.signal,l)}catch(e){throw T(e)?Error(E(u,i,a?.mode===`read-only`)):e}return{path:u,operation:f.operation??`update`,before:f.before??null,after:f.after??e.content,message:`written: ${u} (version ${String(f.version)})`}}})),t.tools.register(e({name:`scratch_edit`,description:`Edit a file in the session scratch directory (system temp) by exact literal replacement. Relative paths resolve inside the scratch directory, not the workspace; absolute paths follow the session policy. Prefer edit for workspace files. In plan (read-only) mode only the scratch directory and system temp are writable.`,parameters:{filePath:{type:`string`,required:!0,description:`Target file path; relative paths resolve inside the scratch directory.`},oldString:{type:`string`,required:!0,description:`Literal text to replace; must match exactly.`},newString:{type:`string`,required:!0,description:`Replacement text (empty string deletes the match).`},replaceAll:{type:`boolean`,description:`Replace every match instead of requiring exactly one (default false).`}},output:D,async execute(e,t){let{sessionId:n,dir:i}=o(t),a=s(t),l=c(n,a);await S(i);let u=_(e.filePath,i),d=await r.resolve(u,{signal:t.signal}),f;try{f=await r.editText(d,{oldString:e.oldString,newString:e.newString,replaceAll:e.replaceAll??!1},void 0,t.signal,l)}catch(e){throw T(e)?Error(E(u,i,a?.mode===`read-only`)):e}return{path:u,operation:`update`,before:f.before??``,after:f.after??``,message:`edited: ${u} (version ${String(f.version)})`}}}))];return()=>{a();for(let e of l)e()}}export{_ as a,v as c,f as i,S as n,g as o,p as r,h as s,O as t};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as inject, i as collectCwdUpSkillRoots, n as TBOXCODE_SKILL_RANK, o as name, r as apply, t as Config } from "../index-
|
|
1
|
+
import { a as inject, i as collectCwdUpSkillRoots, n as TBOXCODE_SKILL_RANK, o as name, r as apply, t as Config } from "../index-C_e95eAG.js";
|
|
2
2
|
export { Config, TBOXCODE_SKILL_RANK, apply, collectCwdUpSkillRoots, inject, name };
|
package/lib/webfetch/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as name, n as apply, r as inject, t as Config } from "../index-
|
|
1
|
+
import { i as name, n as apply, r as inject, t as Config } from "../index-Bb9goxWJ.js";
|
|
2
2
|
export { Config, apply, inject, name };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tbox.cn/tboxcode-dsh-tools",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "tboxcode 工具插件族:webfetch(截断链三分支)/ skill provider(rank 50 cwd-up)/ restrict / pre-execute 拦截器 / scratch 三件",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
"./scratch": {
|
|
30
30
|
"types": "./lib/scratch/index.d.ts",
|
|
31
31
|
"default": "./lib/scratch/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./auto-commit": {
|
|
34
|
+
"types": "./lib/auto-commit/index.d.ts",
|
|
35
|
+
"default": "./lib/auto-commit/index.js"
|
|
32
36
|
}
|
|
33
37
|
},
|
|
34
38
|
"files": [
|
|
@@ -45,6 +49,7 @@
|
|
|
45
49
|
},
|
|
46
50
|
"devDependencies": {
|
|
47
51
|
"@deepseek-ai/cordis": "4.0.5",
|
|
52
|
+
"@deepseek-ai/dsh-shell": "0.2.1-rc.2",
|
|
48
53
|
"@deepseek-ai/dsh-tools": "0.2.1-rc.2",
|
|
49
54
|
"@types/turndown": "^5.0.5"
|
|
50
55
|
},
|
package/lib/index-DHJdScQt.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
-
|
|
3
|
-
//#region src/scratch/index.d.ts
|
|
4
|
-
declare const name = "tboxcode-scratch";
|
|
5
|
-
declare const inject: string[];
|
|
6
|
-
/** 结构面(dsh 类型子集——零 peer 膨胀)。 */
|
|
7
|
-
interface SandboxExecutionPolicy {
|
|
8
|
-
mode: string;
|
|
9
|
-
workspaceRoot: string;
|
|
10
|
-
sessionId?: string;
|
|
11
|
-
}
|
|
12
|
-
/** 会话私有 scratch 目录(平台 tmp——生命周期归 OS,不自建 retention)。 */
|
|
13
|
-
declare function scratchDirFor(sessionId: string): string;
|
|
14
|
-
/** 相对路径对 session cwd 解析(绝对原样);cwd 缺席且相对 → 拒绝(明错)。 */
|
|
15
|
-
declare function resolveWithSessionCwd(modelPath: string | undefined, cwd: string | undefined): string | undefined;
|
|
16
|
-
/** per-call policy 构造(工作区 = scratch 目录——工作区不可达,/tmp 系由
|
|
17
|
-
* workspace-write 档 writableRoots 原生覆盖)。 */
|
|
18
|
-
declare function scratchPolicy(sessionId: string): SandboxExecutionPolicy;
|
|
19
|
-
declare function apply(ctx: Context): () => void;
|
|
20
|
-
//#endregion
|
|
21
|
-
export { scratchDirFor as a, resolveWithSessionCwd as i, inject as n, scratchPolicy as o, name as r, apply as t };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{z as e}from"zod";const t=`tboxcode-pre-execute`,n=[`tools`],r=e.enum([`allow`,`ask`,`deny`]),i=e.object({permission:e.record(e.string(),r).optional(),mcpDenyPatterns:e.array(e.string()).optional()}),a=[`_write`,`_create`,`_update`,`_delete`,`_remove`,`_patch`,`_modify`,`_rename`,`_move`,`_copy`,`_upload`,`_send`,`_deploy`,`_publish`,`_commit`,`_push`,`_post`,`_put`,`_insert`,`_replace`];function o(e,t,n){if(t===void 0)return;let r=t[e];if(r!==void 0)return r;let i=-1,a;for(let n of Object.keys(t))if(n.endsWith(`_*`)){let r=n.slice(0,-2);e.startsWith(`${r}_`)&&r.length>i&&(i=r.length,a=t[n])}if(a!==void 0)return a;if(n!==void 0&&n.length>0){for(let e of n){let n=t[e];if(n!==void 0)return n}for(let e of Object.keys(t))if(e.endsWith(`:*`)){let r=e.slice(0,-2);for(let i of n)if(i.startsWith(`${r}:`))return t[e]}}return t[`*`]}function s(e,t){if(!e.includes(`_`))return!1;let n=new Set(e.split(`_`));for(let r of t){if(r===e)return!0;let t=r.startsWith(`_`)?r.slice(1):r;if(t.length>0&&n.has(t))return!0}return!1}function c(e,t={}){let n=i.parse(t),r=n.permission,c=n.mcpDenyPatterns??a,l=e.get(`sessionProjections`);return e.on(`tools/pre-execute`,async(e,t)=>{if(o(e.name,r)===`deny`)return{kind:`deny`,reason:`Tool '${e.name}' is denied by the permission configuration. Do not retry this tool; use an alternative approach or ask the user.`};if(l!==void 0&&e.agent!==void 0)try{if(l.stateOf(e.agent.session,`sandboxMode`)===`read-only`&&s(e.name,c))return{kind:`deny`,reason:`Plan mode is active (read-only): the MCP tool '${e.name}' may modify external systems, so it is blocked. Record the intended action in the plan instead, or ask the user to switch to build mode to execute it.`}}catch{}return t()}),()=>{}}export{t as a,s as i,c as n,o,n as r,a as t};
|
package/lib/scratch-9LkFiTBl.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{defineTool as e}from"@deepseek-ai/dsh-tools";import{mkdir as t}from"node:fs/promises";import{tmpdir as n}from"node:os";import{isAbsolute as r,join as i,sep as a}from"node:path";import{randomUUID as o}from"node:crypto";const s=`tboxcode-scratch`,c=[`tools`,`shell`,`fs`];function l(e){return i(n(),`tboxcode-scratch`,e)}function u(e){return e.agent?.session?.header?.cwd}function d(e,t){if(e===void 0)return t;if(r(e))return e;if(t!==void 0)return`${t}${a}${e}`}function f(e){return{mode:`workspace-write`,workspaceRoot:l(e),sessionId:e}}async function p(e){await t(e,{recursive:!0})}function m(e){let t=[];return t.push(`exitCode: ${e.exitCode??`null`}${e.timedOut?` (timed out)`:``}${e.aborted?` (aborted)`:``}`),e.stdout.trim().length>0&&t.push(`stdout:\n${e.stdout}`),e.stderr.trim().length>0&&t.push(`stderr:\n${e.stderr}`),t.join(`
|
|
2
|
-
`)}function h(t){let n=t.shell,r=t.fs,i=e=>{let t=e.agent?.session?.header?.id??`adhoc-${o()}`;return{sessionId:t,dir:l(t),cwd:u(e),policy:f(t)}},a=[t.tools.register(e({name:`scratch_bash`,description:`Run a bash command confined to the session-private scratch directory (a temporary workspace outside the project). Use it for throwaway verification work — quick scripts, dependency checks, file inspection — that must not touch the workspace. Working directory defaults to the session workspace; relative paths resolve against it.`,parameters:{command:{type:`string`,required:!0,description:`The bash command to execute inside the scratch sandbox.`},description:{type:`string`,required:!0,description:`Clear, concise description of what this command does, 5-10 words.`},timeoutMs:{type:`number`,description:`Timeout in milliseconds; the executor applies its configured default and cap.`}},output:{schema:{type:`string`},render:(e,t)=>[{type:`text`,text:String(t)}]},async execute(e,t){let{dir:r,cwd:a,policy:o}=i(t);await p(r);let s=d(void 0,a);if(s===void 0)throw Error(`scratch_bash requires a session workspace (agentless calls cannot resolve a working directory)`);let c={command:e.command,workdir:s,...e.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs},signal:t.signal},l=n.resolve(c);l.sandboxPolicy=o;let u=(await n.execute(l)).result;if(u===void 0)throw Error(`scratch_bash: executor produced no result`);return m(u)}})),t.tools.register(e({name:`scratch_write`,description:`Create or overwrite a file inside the session-private scratch directory (a temporary workspace outside the project). Use it for scratch files, fixture data, and one-off scripts. Paths resolve against the session workspace; the file must land inside the scratch sandbox.`,parameters:{filePath:{type:`string`,required:!0,description:`Target file path (must be inside the scratch directory).`},content:{type:`string`,required:!0,description:`Full file content to write.`}},output:{schema:{type:`string`},render:(e,t)=>[{type:`text`,text:String(t)}]},async execute(e,t){let{dir:n,cwd:a,policy:o}=i(t);await p(n);let s=d(e.filePath,a);if(s===void 0)throw Error(`scratch_write requires a session workspace or an absolute path`);let c=await r.resolve(s,{cwd:a,signal:t.signal}),l=await r.writeText(c,e.content,void 0,t.signal,o);return`written: ${e.filePath} (version ${String(l.version)})`}})),t.tools.register(e({name:`scratch_edit`,description:`Edit a file inside the session-private scratch directory by exact literal replacement. Use it to iterate on scratch files without rewriting them. Paths resolve against the session workspace; the file must land inside the scratch sandbox.`,parameters:{filePath:{type:`string`,required:!0,description:`Target file path (must be inside the scratch directory).`},oldString:{type:`string`,required:!0,description:`Literal text to replace; must match exactly.`},newString:{type:`string`,required:!0,description:`Replacement text (empty string deletes the match).`},replaceAll:{type:`boolean`,description:`Replace every match instead of requiring exactly one (default false).`}},output:{schema:{type:`string`},render:(e,t)=>[{type:`text`,text:String(t)}]},async execute(e,t){let{dir:n,cwd:a,policy:o}=i(t);await p(n);let s=d(e.filePath,a);if(s===void 0)throw Error(`scratch_edit requires a session workspace or an absolute path`);let c=await r.resolve(s,{cwd:a,signal:t.signal}),l=await r.editText(c,{oldString:e.oldString,newString:e.newString,replaceAll:e.replaceAll??!1},void 0,t.signal,o);return`edited: ${e.filePath} (version ${String(l.version)})`}}))];return()=>{for(let e of a)e()}}export{l as a,d as i,c as n,f as o,s as r,h as t};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|