dsh-context-toolkit 0.2.2 → 0.2.3

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 CHANGED
@@ -113,7 +113,7 @@ Tool switches: `tools.execute`, `tools.executeFile`, `tools.batchExecute`, `tool
113
113
  The offline suites need no DSH runtime:
114
114
 
115
115
  ```bash
116
- node smoke.mjs # 66 checks: contract, runtime discovery, tools, gate (segment matching, prefix/path normalisation, script_path coverage), resource disposal, knowledge base, observability, version reporting
116
+ node smoke.mjs # 68 checks: contract, runtime discovery, tools, gate (segment matching, prefix/path normalisation, script_path coverage and relative-path alignment), resource disposal, knowledge base, observability, version reporting
117
117
  node test-client.mjs # 27 checks: browser-half contract and bilingual dictionary parity
118
118
  node test-package.mjs # 20 checks: publish manifest and the zero-dependency constraint
119
119
  node release-check.mjs # 27 checks: release readiness (manifest, deps, tool count, gate matching, version, SHA coverage, no hardcoded paths)
package/README.zh.md CHANGED
@@ -113,7 +113,7 @@ await ctk_search({ queries: ['rate limit', 'auth'] }); // 只取相关片段
113
113
  离线套件不依赖 DSH 运行时:
114
114
 
115
115
  ```bash
116
- node smoke.mjs # 66 项:契约、运行时探测、三工具、门禁(分段匹配 + 前缀/路径归一 + script_path 覆盖)、资源释放、知识库、观测、版本自述
116
+ node smoke.mjs # 68 项:契约、运行时探测、三工具、门禁(分段匹配 + 前缀/路径归一 + script_path 覆盖与相对路径对齐)、资源释放、知识库、观测、版本自述
117
117
  node test-client.mjs # 27 项:浏览器半体契约与双语字典一致性
118
118
  node test-package.mjs # 20 项:发布清单与零依赖约束
119
119
  node release-check.mjs # 27 项:发布就绪(清单/依赖/工具数/门禁匹配/版本/SHA 覆盖/无硬编码路径)
package/index.js CHANGED
@@ -365,18 +365,43 @@ const SCRIPT_READ_MAX = 256 * 1024
365
365
  * the same bypass the shell-name list fixed, one field over: whichever field carries
366
366
  * the payload, the gate has to follow it.
367
367
  */
368
- function shellTextOf(args) {
368
+ function shellTextOf(args, exec) {
369
369
  const parts = []
370
370
  if (typeof args.command === 'string') parts.push(args.command)
371
371
  if (typeof args.script_path === 'string' && args.script_path.trim()) {
372
+ const path = resolveScriptPath(args.script_path, args.cwd, exec)
372
373
  try {
373
- const st = statSync(args.script_path)
374
- if (st.isFile()) parts.push(readFileSync(args.script_path, 'utf8').slice(0, SCRIPT_READ_MAX))
374
+ const st = statSync(path)
375
+ if (st.isFile()) parts.push(readFileSync(path, 'utf8').slice(0, SCRIPT_READ_MAX))
375
376
  } catch { /* an unreadable script is the shell tool's error to report, not the gate's */ }
376
377
  }
377
378
  return parts.join('\n')
378
379
  }
379
380
 
381
+ /**
382
+ * Where a relative script_path actually points.
383
+ *
384
+ * The host resolves a relative script against the *session* working directory, while
385
+ * statSync resolves it against this process's cwd — the install directory. The two
386
+ * disagree, so reading the raw string saw nothing for a relative script_path and waved
387
+ * the call through even though the host would run it happily. Aligning the base closes
388
+ * the last way a shell call can carry a payload the gate cannot see: whenever the host
389
+ * can run the file, the gate resolves the same file.
390
+ */
391
+ function resolveScriptPath(requested, requestedCwd, exec) {
392
+ if (isAbsolute(requested)) return requested
393
+ let session = null
394
+ try {
395
+ session = exec && exec.agent && exec.agent.session && exec.agent.session.header
396
+ ? exec.agent.session.header.cwd : null
397
+ } catch { /* no session on this exec: fall back to the process default */ }
398
+ const base = session || process.cwd()
399
+ const cwd = typeof requestedCwd === 'string' && requestedCwd.trim()
400
+ ? (isAbsolute(requestedCwd) ? requestedCwd : resolve(base, requestedCwd))
401
+ : base
402
+ return resolve(cwd, requested)
403
+ }
404
+
380
405
  function floodReason(toolName, args, text) {
381
406
  if (!SHELL_TOOL_NAMES.has(toolName)) return undefined
382
407
  const command = text === undefined ? shellTextOf(args) : text
@@ -407,7 +432,7 @@ function registerRoutingGate(ctx, config, stats) {
407
432
 
408
433
  // Read the call's text once and reuse it: a script can be 256 KB, and the deny
409
434
  // path used to read it three times to answer three questions about one string.
410
- const shellText = SHELL_TOOL_NAMES.has(toolName) ? shellTextOf(args) : ''
435
+ const shellText = SHELL_TOOL_NAMES.has(toolName) ? shellTextOf(args, exec) : ''
411
436
 
412
437
  if (config.denyCurlWget !== false) {
413
438
  const reason = floodReason(toolName, args, shellText)
@@ -459,7 +484,7 @@ function registerRoutingGate(ctx, config, stats) {
459
484
  })
460
485
 
461
486
  if (config.denyCurlWget !== false && ctx.tools && typeof ctx.tools.guard === 'function') {
462
- ctx.tools.guard((exec) => floodReason(exec && exec.name, (exec && exec.arguments) || {}))
487
+ ctx.tools.guard((exec) => { const a = (exec && exec.arguments) || {}; return floodReason(exec && exec.name, a, shellTextOf(a, exec)) })
463
488
  }
464
489
  }
465
490
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context-toolkit",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "DeepSeek Harness bundle: native ctk_* sandbox tools, a routing gate that keeps bulk output out of the transcript, and a local full-text knowledge base. No third-party runtime dependencies.",
5
5
  "repository": {
6
6
  "type": "git",