dsh-ptc-plus 0.2.1
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/LICENSE +21 -0
- package/README.md +137 -0
- package/README.zh.md +137 -0
- package/assets/dsh-ptc-plus-banner.webp +0 -0
- package/assets/ptc-plus-repair.png +0 -0
- package/assets/ptc-plus-settings.png +0 -0
- package/client.js +366 -0
- package/cordis.patch.yml +5 -0
- package/docs/README.md +21 -0
- package/docs/adr/0001-delegate-governance-to-dsh.md +3 -0
- package/docs/adr/0002-limit-work-map-scope.md +3 -0
- package/docs/adr/0003-prefer-native-program-surfaces.md +3 -0
- package/docs/adr/0004-declare-program-bindings-at-the-host.md +5 -0
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +129 -0
- package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +45 -0
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +39 -0
- package/docs/adr/0008-define-agent-operating-contract.md +25 -0
- package/docs/adr/0009-bound-expensive-acceptance-default.md +39 -0
- package/docs/adr/0010-session-log-derived-recovery-tips.md +26 -0
- package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +20 -0
- package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +23 -0
- package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +21 -0
- package/docs/adr/0014-persist-cell-rewrite-policy.md +25 -0
- package/docs/adr/0015-preserve-source-positions-through-rewrites.md +25 -0
- package/docs/adr/0016-archive-resolved-review-findings.md +43 -0
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +23 -0
- package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +25 -0
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +32 -0
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +36 -0
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +68 -0
- package/docs/adr/README.md +18 -0
- package/docs/architecture.md +134 -0
- package/docs/capability-projection.md +48 -0
- package/docs/client-ui.md +47 -0
- package/docs/durability-design.md +265 -0
- package/docs/evaluation.md +62 -0
- package/docs/installation.md +82 -0
- package/docs/program-data-plane.md +61 -0
- package/docs/publishing.md +37 -0
- package/docs/runtime-reference.md +119 -0
- package/docs/value-wire.md +61 -0
- package/index.js +532 -0
- package/internal/ast-traversal.js +20 -0
- package/internal/binding-descriptors.js +95 -0
- package/internal/binding-pattern.js +54 -0
- package/internal/builtin-esm-sync.js +9 -0
- package/internal/cell-analysis.js +467 -0
- package/internal/cell-rewriter.js +520 -0
- package/internal/config-spec.js +179 -0
- package/internal/cordis-tools-owner.js +363 -0
- package/internal/diagnostic.js +133 -0
- package/internal/direct-surface-owner.js +326 -0
- package/internal/edit-transport-owner.js +287 -0
- package/internal/failure-reporting.js +139 -0
- package/internal/kernel-worker.js +484 -0
- package/internal/module-policy.js +46 -0
- package/internal/program-bindings.js +187 -0
- package/internal/record-utils.js +53 -0
- package/internal/recovery-tips.js +81 -0
- package/internal/rejected-cell-editor.js +328 -0
- package/internal/repl-convenience.js +201 -0
- package/internal/runtime-bridge-owner.js +417 -0
- package/internal/runtime-config.js +77 -0
- package/internal/runtime-contexts.js +31 -0
- package/internal/sdk-projection.js +88 -0
- package/internal/session-cell-executor.js +612 -0
- package/internal/session-journal.js +889 -0
- package/internal/session-log-view.js +80 -0
- package/internal/session-runtime.js +536 -0
- package/internal/session-state.js +45 -0
- package/internal/source-position-map.js +224 -0
- package/internal/tool-call-canonicalizer.js +339 -0
- package/internal/value-wire.js +421 -0
- package/internal/worker-client.js +218 -0
- package/internal/worker-cwd-virtualization.js +155 -0
- package/package.json +100 -0
- package/src/client.js +165 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-ptc-plus contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# dsh-ptc-plus
|
|
2
|
+
|
|
3
|
+
[](package.json)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
6
|
+
[](docs/publishing.md)
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<img src="assets/dsh-ptc-plus-banner.webp" alt="dsh-ptc-plus banner">
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
**PTC Plus gives DSH PTC mode a session-bound persistent TypeScript REPL.** Every `run_code` continues in the same session. Variables, imports, and results from one `run_code` are still available in the next one.
|
|
13
|
+
|
|
14
|
+
> [!NOTE]
|
|
15
|
+
> Community plugin, no affiliation with or endorsement from DeepSeek or DSH.
|
|
16
|
+
|
|
17
|
+
> [!IMPORTANT]
|
|
18
|
+
> Built for `danger-full-access`: direct Node.js and OS access with no extra sandbox. Use it only where that permission scope is acceptable.
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
*The settings card exposes live configuration and the `enabled` kill switch.*
|
|
23
|
+
|
|
24
|
+
## What default PTC mode gets wrong
|
|
25
|
+
|
|
26
|
+
DSH PTC mode starts every `run_code` in a fresh environment. The model computes something, then has to send the same setup code again. One bad line means the whole thing is resent. This plugin attaches `run_code` to a session-backed environment, so later calls reuse what was already there.
|
|
27
|
+
|
|
28
|
+
| Situation | Default PTC mode | With PTC Plus |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| State | starts from zero, setup resent ❌ | previous `run_code` results stay ✅ |
|
|
31
|
+
| Fixing | wrong result resends the code ❌ | one diff ✅ |
|
|
32
|
+
| Modules | `import` / `export` cannot be written ❌ | written normally, AST handles it ✅ |
|
|
33
|
+
| Values | JSON changes or loses special values ❌ | those values stay intact ✅ |
|
|
34
|
+
| Restart | everything is lost ❌ | recoverable parts come back ✅ |
|
|
35
|
+
| Output and errors | printing floods, errors point elsewhere ❌ | output trimmed, errors map to your line ✅ |
|
|
36
|
+
| Tools | list invisible, miscalls fail ❌ | can inspect; known miscalls become `run_code` ✅ |
|
|
37
|
+
| Paths | relative paths can drift ❌ | session remembers the project directory ✅ |
|
|
38
|
+
| Agent tools | tools needing the agent are rejected ❌ | context restored, goal works ✅ |
|
|
39
|
+
|
|
40
|
+
## Three scenes that matter most
|
|
41
|
+
|
|
42
|
+
### State carries over
|
|
43
|
+
|
|
44
|
+
First `run_code`:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { readFile } from 'node:fs/promises'
|
|
48
|
+
const manifest = JSON.parse(await readFile('package.json', 'utf8'))
|
|
49
|
+
const deps = Object.keys(manifest.dependencies ?? {})
|
|
50
|
+
return deps.length
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The next one keeps going:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
return deps.map(dep => dep + '@' + manifest.dependencies[dep])
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`deps` and `manifest` are still there. The setup code is sent only once.
|
|
60
|
+
|
|
61
|
+
### Fix without resending
|
|
62
|
+
|
|
63
|
+
By default, a wrong result or a failure sends the whole code block again.
|
|
64
|
+
|
|
65
|
+
With PTC Plus, it sends one line:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
edit_run_code({ edits: [{ old_string: 'deps.length', new_string: 'deps' }] })
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Only the diff goes in. The full source stays out of the conversation. Exact replacements and regular expressions both have limits, so a bad pattern cannot hang.
|
|
72
|
+
|
|
73
|
+
### Module syntax
|
|
74
|
+
|
|
75
|
+
DSH PTC mode executes each `run_code` as an async function body, where static `import` and `export` declarations are invalid. PTC Plus adapts those forms before execution with AST analysis.
|
|
76
|
+
|
|
77
|
+
The model writes normally:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { readFile } from 'node:fs/promises'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Imports resolve from your project, and named/default imports stay live and read-only. The model never has to know that a `run_code` is really a function body.
|
|
84
|
+
|
|
85
|
+
## One measured A/B
|
|
86
|
+
|
|
87
|
+
One identity-blind paired run used `opencode-go/deepseek-v4-flash`. Both arms used the same versioned fixture, task prompts, permissions, and two replicates per task, so 18 sessions per arm.
|
|
88
|
+
|
|
89
|
+
| Across all 9 tasks | PTC Plus | DSH PTC mode (PTC Plus disabled) | Observed change |
|
|
90
|
+
| --- | ---: | ---: | ---: |
|
|
91
|
+
| Model requests | 66 | 88 | 25.0% fewer |
|
|
92
|
+
| Tool calls | 50 | 79 | 36.7% fewer |
|
|
93
|
+
| Token traffic | 729,642 | 942,901 | 22.6% fewer |
|
|
94
|
+
| Identity-blind rubric score | 138 / 162 | 118 / 162 | +12.3 percentage points |
|
|
95
|
+
|
|
96
|
+
The module-syntax task separated the two arms most clearly. PTC Plus finished both replicates with one `run_code` each. DSH PTC mode without PTC Plus finished neither static-import requirement and used eight tool calls across its attempts.
|
|
97
|
+
|
|
98
|
+
This is one stochastic paired observation, not a performance guarantee. Machine budgets were exceeded in 2 of the 18 PTC Plus sessions and 5 of the 18 sessions without PTC Plus, so the matrix as a whole did not pass machine acceptance. Token traffic includes input, cache-read, cache-write, and output tokens. The fixture, pairing rules, metrics, and blind-review protocol are documented in [Evaluation](docs/evaluation.md).
|
|
99
|
+
|
|
100
|
+

|
|
101
|
+
|
|
102
|
+
*A real session: the long code and the truthful `edit_run_code` repair call. The repair never resends the source.*
|
|
103
|
+
|
|
104
|
+
## Settings
|
|
105
|
+
|
|
106
|
+
Open **Settings → Plugin configuration** to use the card shown above. The `enabled` switch is live: turning it off leaves only the card and that switch, while turning it on restores the session runtime and `run_code`/`edit_run_code`.
|
|
107
|
+
|
|
108
|
+
Every setting applies live and keeps existing bindings. A submitted cell uses one configuration for its complete execution; changes made while it runs apply to cells submitted afterward. A failed change rolls back. Node fixes a worker's V8 old-generation limit when the worker starts, so that one setting is rejected while a session worker is active and can be changed after the session is disposed. A failed enable is rolled back and persisted as disabled.
|
|
109
|
+
|
|
110
|
+
`cordisToolsEnabled` is off by default. Turning it on atomically adds DSH's official Cordis tools, owner guidance, and `cordis-plugin-development` companion Skill to PTC agents; turning it off removes all three. It neither switches presets nor changes the direct `run_code`/`edit_run_code` surface. Cordis runs model-written plugins against the live DSH runtime, so enabling it requires shell-level trust.
|
|
111
|
+
|
|
112
|
+
When a Cordis call rejects after a cell has assigned a large host or client source string, that top-level binding remains live. Retry only the Cordis call from a short later cell and reuse the binding instead of resending the source.
|
|
113
|
+
|
|
114
|
+
See [Client UI](docs/client-ui.md), [ADR 0019](docs/adr/0019-plugin-settings-and-kill-switch.md), and [ADR 0020](docs/adr/0020-optional-cordis-tools-in-ptc-mode.md).
|
|
115
|
+
|
|
116
|
+
## Scope
|
|
117
|
+
|
|
118
|
+
PTC Plus provides the session-bound persistent `run_code` layer. DSH and the operating system continue to own native-tool authority, policy, approval, cancellation, sandboxing and process governance.
|
|
119
|
+
|
|
120
|
+
## Install
|
|
121
|
+
|
|
122
|
+
Requires Node.js `^22.19.0 || >=24.0.0` and DSH with TypeScript PTC mode:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
dsh plugin --profile <profile> add github:muyuanjin/dsh-ptc-plus#main
|
|
126
|
+
dsh --profile <profile> --dump-config
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Other install methods and compatibility details are in the [installation guide](docs/installation.md).
|
|
130
|
+
|
|
131
|
+
`danger-full-access` is the primary supported experience. The worker isolates lifecycle, not malicious code.
|
|
132
|
+
|
|
133
|
+
## Documentation
|
|
134
|
+
|
|
135
|
+
[Installation](docs/installation.md) · [Runtime reference](docs/runtime-reference.md) · [Architecture](docs/architecture.md) · [All docs](docs/README.md)
|
|
136
|
+
|
|
137
|
+
MIT licensed. See [LICENSE](LICENSE).
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# dsh-ptc-plus
|
|
2
|
+
|
|
3
|
+
[](package.json)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
6
|
+
[](docs/publishing.md)
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<img src="assets/dsh-ptc-plus-banner.webp" alt="dsh-ptc-plus 横幅">
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
**PTC Plus 给 DSH 的 PTC 模式一个会话绑定的持久 TypeScript REPL。** 每次 `run_code` 都在同一个会话里继续。上一次 `run_code` 的变量、导入和结果,下一次还能直接用。
|
|
13
|
+
|
|
14
|
+
> [!NOTE]
|
|
15
|
+
> 社区插件,与 DeepSeek 或 DSH 无隶属、无背书。
|
|
16
|
+
|
|
17
|
+
> [!IMPORTANT]
|
|
18
|
+
> 面向 `danger-full-access` 设计:可直接访问 Node.js 与操作系统,不另加沙箱。仅在可接受此权限的环境使用。
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
*设置卡片展示即时配置和 `enabled` 开关。*
|
|
23
|
+
|
|
24
|
+
## 默认 PTC 模式的问题
|
|
25
|
+
|
|
26
|
+
DSH 的 PTC 模式让每次 `run_code` 都从新环境开始。模型算过的东西,下一次还要重发。写错一行,整段代码重发。这个插件把 `run_code` 接到一个会话级环境里,后面的调用直接复用之前的东西。
|
|
27
|
+
|
|
28
|
+
| 场景 | 默认 PTC 模式 | PTC Plus 之后 |
|
|
29
|
+
| --- | --- |----------------------------|
|
|
30
|
+
| 状态 | 每次从零开始,setup 重发 ❌ | 上一次 `run_code` 的结果直接能用 ✅ |
|
|
31
|
+
| 修错 | 结果不对或失败,整段重发 ❌ | 只发一行 diff ✅ |
|
|
32
|
+
| 模块 | `import` / `export` 不能写 ❌ | 照常写,后台 AST 重写 ✅ |
|
|
33
|
+
| 值 | JSON 改掉或丢失特殊值 ❌ | 这些值原样保留 ✅ |
|
|
34
|
+
| 重启 | 重启后一切丢失 ❌ | 能恢复的会回来 ✅ |
|
|
35
|
+
| 输出报错 | 大打印刷屏,报错指到别处 ❌ | 输出裁剪,报错回错的行 ✅ |
|
|
36
|
+
| 工具 | 列表看不见,顶层误发失败 ❌ | 可查看;可确定的误发自动转 `run_code` ✅ |
|
|
37
|
+
| 路径 | 相对路径可能跑偏 ❌ | session 记住项目目录 ✅ |
|
|
38
|
+
| agent 工具 | 需要当前 agent 的工具被拒绝 ❌ | 恢复上下文,goal 等可调 ✅ |
|
|
39
|
+
|
|
40
|
+
## 三个最直接的场景
|
|
41
|
+
|
|
42
|
+
### 状态跨调用
|
|
43
|
+
|
|
44
|
+
第一个 `run_code` 算完:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { readFile } from 'node:fs/promises'
|
|
48
|
+
const manifest = JSON.parse(await readFile('package.json', 'utf8'))
|
|
49
|
+
const deps = Object.keys(manifest.dependencies ?? {})
|
|
50
|
+
return deps.length
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
第二个直接接着用:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
return deps.map(dep => dep + '@' + manifest.dependencies[dep])
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`deps` 和 `manifest` 还在。setup 代码只发一次。
|
|
60
|
+
|
|
61
|
+
### 修错不重发
|
|
62
|
+
|
|
63
|
+
默认情况下,结果不对或执行失败,模型只能把整个代码块再发一遍。
|
|
64
|
+
|
|
65
|
+
PTC Plus 下它只发这一行:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
edit_run_code({ edits: [{ old_string: 'deps.length', new_string: 'deps' }] })
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
模型只发改动,完整源码留在对话之外。精确文本替换和正则替换都有限制,坏的正则不会卡住。
|
|
72
|
+
|
|
73
|
+
### 模块语法
|
|
74
|
+
|
|
75
|
+
DSH 的 PTC 模式把每个 `run_code` 当作 async function body 执行,静态 `import` 和 `export` 声明在这个函数体里无效。PTC Plus 会在执行前用 AST 分析适配这些形式。
|
|
76
|
+
|
|
77
|
+
模型照常写:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { readFile } from 'node:fs/promises'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
依赖从项目解析,具名和默认导入保持 live 且只读。模型不需要知道 `run_code` 其实是一个函数体。
|
|
84
|
+
|
|
85
|
+
## 一次配对实测
|
|
86
|
+
|
|
87
|
+
一次身份盲化的配对实验使用了 `opencode-go/deepseek-v4-flash`。两个 arm 使用同一版本夹具、任务 prompt、权限,每个任务重复两次,每个 arm 共 18 个 session。
|
|
88
|
+
|
|
89
|
+
| 9 个任务合计 | PTC Plus | DSH PTC 模式(未启用 PTC Plus) | 本次观测变化 |
|
|
90
|
+
| --- | ---: | ---: | ---: |
|
|
91
|
+
| 模型请求 | 66 | 88 | 减少 25.0% |
|
|
92
|
+
| 工具调用 | 50 | 79 | 减少 36.7% |
|
|
93
|
+
| Token 流量 | 729,642 | 942,901 | 减少 22.6% |
|
|
94
|
+
| 身份盲评量表得分 | 138 / 162 | 118 / 162 | 提高 12.3 个百分点 |
|
|
95
|
+
|
|
96
|
+
模块语法任务的区分最清楚:PTC Plus 两次都只用一次 `run_code` 完成;未启用 PTC Plus 的 DSH PTC 模式两次都未满足静态 import 要求,尝试过程合计用了 8 次工具调用。
|
|
97
|
+
|
|
98
|
+
这是一次有随机性的配对观测,不是性能保证。预设机器预算在 PTC Plus 的 18 个 session 中有 2 个超限,未启用 PTC Plus 的 18 个 session 中有 5 个超限,因此整组矩阵没有通过 machine acceptance。Token 流量包含 input、cache-read、cache-write 和 output token。夹具、配对规则、指标与盲评流程见[评测说明](docs/evaluation.md)。
|
|
99
|
+
|
|
100
|
+

|
|
101
|
+
|
|
102
|
+
*真实会话:长代码与真实的 `edit_run_code` 修复调用;修复从未重发源码。*
|
|
103
|
+
|
|
104
|
+
## 设置
|
|
105
|
+
|
|
106
|
+
打开 **设置 → 插件配置** 使用上面的设置卡片。`enabled` 是即时生效的总开关:关闭后只保留卡片和这个开关,开启后恢复 session runtime 以及 `run_code`/`edit_run_code`。
|
|
107
|
+
|
|
108
|
+
所有设置都会实时应用,并保留已有 binding。已提交的 cell 在完整执行期间使用同一份配置;执行期间发生的更新用于随后提交的 cell。更新失败会回滚。Node 在 worker 创建时固定 V8 old-generation 上限,因此活动 session worker 存在时修改这一项会被拒绝,释放 session 后才能修改。启用失败时,运行时会回滚并把设置保持为停用。
|
|
109
|
+
|
|
110
|
+
`cordisToolsEnabled` 默认关闭。打开后,DSH 官方 Cordis 工具、owner guidance 与 `cordis-plugin-development` companion Skill 会作为一个整体加入 PTC agent,关闭时也会一起移除;它不切换 preset,也不改变 `run_code`/`edit_run_code` 的直接调用面。Cordis 能在实时 DSH runtime 中运行模型编写的插件,开启它需要接受 shell 级信任。
|
|
111
|
+
|
|
112
|
+
如果 Cordis 调用在 cell 已经赋值大段 host 或 client 源码之后才失败,这些顶层 binding 仍会保留。后续只需在短 cell 中重试 Cordis 调用并复用该 binding,无需再次传输源码。
|
|
113
|
+
|
|
114
|
+
详见 [客户端 UI](docs/client-ui.md)、[ADR 0019](docs/adr/0019-plugin-settings-and-kill-switch.md) 与 [ADR 0020](docs/adr/0020-optional-cordis-tools-in-ptc-mode.md)。
|
|
115
|
+
|
|
116
|
+
## 范围
|
|
117
|
+
|
|
118
|
+
PTC Plus 提供会话绑定的持久 `run_code` 层。原生工具的权限、策略、审批、取消、sandbox 和进程治理仍由 DSH 与操作系统负责。
|
|
119
|
+
|
|
120
|
+
## 安装
|
|
121
|
+
|
|
122
|
+
要求 Node.js `^22.19.0 || >=24.0.0`,并已安装带 TypeScript PTC 模式的 DSH:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
dsh plugin --profile <profile> add github:muyuanjin/dsh-ptc-plus#main
|
|
126
|
+
dsh --profile <profile> --dump-config
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
其他安装方法和兼容性说明见[安装指南](docs/installation.md)。
|
|
130
|
+
|
|
131
|
+
`danger-full-access` 是首要支持方式。worker 只隔离生命周期,不隔离恶意代码。
|
|
132
|
+
|
|
133
|
+
## 文档
|
|
134
|
+
|
|
135
|
+
[安装指南](docs/installation.md) · [运行时参考](docs/runtime-reference.md) · [架构](docs/architecture.md) · [全部文档](docs/README.md)
|
|
136
|
+
|
|
137
|
+
使用 [MIT License](LICENSE)。
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/client.js
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
// internal/config-spec.js
|
|
3
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
4
|
+
var SETTINGS_NAMESPACE = "ptc-plus";
|
|
5
|
+
var CONFIG_FIELDS = Object.freeze([
|
|
6
|
+
{
|
|
7
|
+
key: "enabled",
|
|
8
|
+
type: "boolean",
|
|
9
|
+
default: true,
|
|
10
|
+
label: "\u542F\u7528 PTC Plus",
|
|
11
|
+
description: ""
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
key: "cordisToolsEnabled",
|
|
15
|
+
type: "boolean",
|
|
16
|
+
default: false,
|
|
17
|
+
label: "\u5728 PTC \u6A21\u5F0F\u4E2D\u542F\u7528 Cordis \u5DE5\u5177",
|
|
18
|
+
description: "\u5373\u65F6\u4E3A PTC agent \u52A0\u5165\u6216\u79FB\u9664\u5B98\u65B9 Cordis \u5DE5\u5177\u3002"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
key: "computeMs",
|
|
22
|
+
type: "integer",
|
|
23
|
+
default: 6e4,
|
|
24
|
+
min: 1,
|
|
25
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
26
|
+
label: "\u5355 cell \u6700\u5927 CPU \u65F6\u95F4 (ms)",
|
|
27
|
+
description: "\u540C\u6B65\u8BA1\u7B97\u8D85\u8FC7\u8BE5\u9884\u7B97\u7684 cell \u4F1A\u88AB\u4E2D\u65AD\u3002"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: "maxWallMs",
|
|
31
|
+
type: "integer",
|
|
32
|
+
default: 6e5,
|
|
33
|
+
min: 1,
|
|
34
|
+
max: MAX_TIMER_DELAY_MS,
|
|
35
|
+
label: "\u5355 cell \u6700\u5927\u5899\u949F\u65F6\u95F4 (ms)",
|
|
36
|
+
description: "\u5B8C\u6574 cell \u6267\u884C\uFF08\u542B\u5F02\u6B65\u7B49\u5F85\uFF09\u7684\u6700\u957F\u8017\u65F6\u3002"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: "maxOutputBytes",
|
|
40
|
+
type: "integer",
|
|
41
|
+
default: 64 * 1024 * 1024,
|
|
42
|
+
min: 1,
|
|
43
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
44
|
+
label: "\u6700\u5927\u8F93\u51FA\u5B57\u8282",
|
|
45
|
+
description: "\u9650\u5236\u5355\u4E2A cell \u7684\u8F93\u51FA\u548C\u7ED3\u679C\u6570\u636E\u603B\u5927\u5C0F\u3002"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
key: "maxOldGenerationSizeMb",
|
|
49
|
+
type: "integer",
|
|
50
|
+
default: 512,
|
|
51
|
+
min: 1,
|
|
52
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
53
|
+
label: "worker \u65E7\u751F\u4EE3\u5185\u5B58\u4E0A\u9650 (MiB)",
|
|
54
|
+
description: "\u6BCF\u4E2A worker \u7684 V8 \u65E7\u751F\u4EE3\u4E0A\u9650\uFF1B\u6D3B\u52A8 worker \u5B58\u5728\u65F6\u4FEE\u6539\u4F1A\u88AB\u62D2\u7EDD\u3002"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: "maxValueNodes",
|
|
58
|
+
type: "integer",
|
|
59
|
+
default: 1e5,
|
|
60
|
+
min: 1,
|
|
61
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
62
|
+
label: "Value Graph \u6700\u5927\u8282\u70B9\u6570",
|
|
63
|
+
description: "\u9650\u5236\u5355\u6B21\u8FD4\u56DE\u503C\u7684\u8282\u70B9\u6570\u3002"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
key: "maxValueEdges",
|
|
67
|
+
type: "integer",
|
|
68
|
+
default: 1e6,
|
|
69
|
+
min: 1,
|
|
70
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
71
|
+
label: "Value Graph \u6700\u5927\u8FB9\u6570",
|
|
72
|
+
description: "\u9650\u5236\u5355\u6B21\u8FD4\u56DE\u503C\u7684\u5F15\u7528\u5173\u7CFB\u6570\u3002"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
key: "maxValueArrayLength",
|
|
76
|
+
type: "integer",
|
|
77
|
+
default: 1e6,
|
|
78
|
+
min: 1,
|
|
79
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
80
|
+
label: "\u6570\u7EC4\u6700\u5927\u58F0\u660E\u957F\u5EA6",
|
|
81
|
+
description: "\u9650\u5236\u8FD4\u56DE\u6570\u7EC4\u7684\u6700\u5927\u957F\u5EA6\u3002"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
key: "maxValueBigIntDigits",
|
|
85
|
+
type: "integer",
|
|
86
|
+
default: 1e5,
|
|
87
|
+
min: 1,
|
|
88
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
89
|
+
label: "BigInt \u6700\u5927\u5341\u8FDB\u5236\u4F4D\u6570",
|
|
90
|
+
description: "\u9650\u5236\u8FD4\u56DE\u503C\u4E2D BigInt \u7684\u5341\u8FDB\u5236\u4F4D\u6570\u3002"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
key: "maxNestedRunCodeDepth",
|
|
94
|
+
type: "integer",
|
|
95
|
+
default: 8,
|
|
96
|
+
min: 1,
|
|
97
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
98
|
+
label: "code.run \u6700\u5927\u9012\u5F52\u6DF1\u5EA6",
|
|
99
|
+
description: "\u9650\u5236 code.run \u7684\u5D4C\u5957\u5C42\u6570\u3002"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
key: "canonicalizeToolCalls",
|
|
103
|
+
type: "boolean",
|
|
104
|
+
default: true,
|
|
105
|
+
label: "\u89C4\u8303\u9876\u5C42 native \u8BEF\u8C03",
|
|
106
|
+
description: "\u4FEE\u6B63\u53EF\u4EE5\u660E\u786E\u8BC6\u522B\u7684\u9876\u5C42 native \u8BEF\u8C03\u3002"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
key: "looseTopLevelRedeclarations",
|
|
110
|
+
type: "boolean",
|
|
111
|
+
default: true,
|
|
112
|
+
label: "\u5BBD\u677E\u9876\u5C42\u91CD\u58F0\u660E",
|
|
113
|
+
description: "\u5141\u8BB8\u9876\u5C42 const/let \u91CD\u58F0\u660E\u5DF2\u6709\u53D8\u91CF\u3002"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
key: "durableReplay",
|
|
117
|
+
type: "boolean",
|
|
118
|
+
default: true,
|
|
119
|
+
label: "\u6301\u4E45\u91CD\u653E",
|
|
120
|
+
description: "worker \u91CD\u542F\u540E\u6062\u590D\u53EF\u4EE5\u91CD\u5EFA\u7684 REPL \u72B6\u6001\u3002"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
key: "autoRewriteImports",
|
|
124
|
+
type: "boolean",
|
|
125
|
+
default: true,
|
|
126
|
+
label: "\u81EA\u52A8\u6539\u5199 import",
|
|
127
|
+
description: "\u5141\u8BB8\u5728 run_code \u4E2D\u4F7F\u7528\u9759\u6001 import\u3002"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
key: "autoStripExports",
|
|
131
|
+
type: "boolean",
|
|
132
|
+
default: true,
|
|
133
|
+
label: "\u81EA\u52A8\u5265\u79BB export",
|
|
134
|
+
description: "\u5141\u8BB8\u5728 run_code \u4E2D\u4F7F\u7528\u9876\u5C42 export\u3002"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
key: "autoSplitRedeclarations",
|
|
138
|
+
type: "boolean",
|
|
139
|
+
default: true,
|
|
140
|
+
label: "\u81EA\u52A8\u62C6\u5206\u6DF7\u5408\u91CD\u58F0\u660E",
|
|
141
|
+
description: "\u5141\u8BB8\u9876\u5C42\u89E3\u6784\u58F0\u660E\u540C\u65F6\u5305\u542B\u65B0\u65E7\u53D8\u91CF\u3002"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
key: "tipsEnabled",
|
|
145
|
+
type: "boolean",
|
|
146
|
+
default: true,
|
|
147
|
+
label: "\u542F\u7528\u6062\u590D\u63D0\u793A",
|
|
148
|
+
description: "\u5728\u7B26\u5408\u6761\u4EF6\u7684\u5931\u8D25\u540E\u663E\u793A\u6062\u590D\u63D0\u793A\u3002"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
key: "tipCooldownMessages",
|
|
152
|
+
type: "integer",
|
|
153
|
+
default: 3,
|
|
154
|
+
min: 1,
|
|
155
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
156
|
+
label: "\u6062\u590D\u63D0\u793A\u51B7\u5374\u6B65\u6570",
|
|
157
|
+
description: "\u540C\u7C7B\u6062\u590D\u63D0\u793A\u4E4B\u95F4\u7684\u6700\u5C0F\u95F4\u9694\u3002"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
key: "tipEscalationFailures",
|
|
161
|
+
type: "integer",
|
|
162
|
+
default: 2,
|
|
163
|
+
min: 1,
|
|
164
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
165
|
+
label: "\u6062\u590D\u63D0\u793A\u5347\u7EA7\u5931\u8D25\u6B21\u6570",
|
|
166
|
+
description: "\u8FDE\u7EED\u5931\u8D25\u8FBE\u5230\u6B64\u6B21\u6570\u540E\u663E\u793A\u66F4\u8BE6\u7EC6\u7684\u6062\u590D\u63D0\u793A\u3002"
|
|
167
|
+
}
|
|
168
|
+
]);
|
|
169
|
+
var CONFIG_DEFAULTS = Object.freeze(
|
|
170
|
+
Object.fromEntries(CONFIG_FIELDS.map((field) => [field.key, field.default]))
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// src/client.js
|
|
174
|
+
var CLIENT_STYLE_ID = "ptc-plus-client-style";
|
|
175
|
+
var CLIENT_CSS = `
|
|
176
|
+
.ptcPlusCard{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));background:var(--dsw-alias-bg-layer-3,#fff);border-radius:8px;list-style:none;overflow:hidden}
|
|
177
|
+
.ptcPlusHeader{appearance:none;width:100%;display:flex;align-items:center;gap:12px;padding:14px 16px;border:0;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer;transition:background-color .16s ease}
|
|
178
|
+
.ptcPlusHeader:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusHeader:focus-visible,.ptcPlusButton:focus-visible,.ptcPlusInput:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:-2px}
|
|
179
|
+
.ptcPlusHeadText{display:flex;flex:1;min-width:0;flex-direction:column;align-items:flex-start;gap:1px}.ptcPlusName{font-size:14px;font-weight:600;line-height:20px}.ptcPlusDescription{color:var(--dsw-alias-label-tertiary,#74777d);font-size:12px;line-height:18px;overflow-wrap:anywhere}.ptcPlusStatus{display:inline-flex;align-items:center;flex:none;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:500;line-height:16px}.ptcPlusStatus[data-enabled=true]{color:var(--dsw-alias-state-success-primary,#16794f);background:var(--dsw-alias-state-success-tertiary,#e7f7ef)}.ptcPlusStatus[data-enabled=false]{color:var(--dsw-alias-label-tertiary,#74777d);background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
|
|
180
|
+
.ptcPlusChevron{display:flex;color:var(--dsw-alias-label-tertiary,#74777d);transition:transform .18s ease}.ptcPlusChevron[data-open=true]{transform:rotate(180deg)}.ptcPlusBody{display:grid;grid-template-rows:0fr;transition:grid-template-rows .2s ease}.ptcPlusBody[data-open=true]{grid-template-rows:1fr}.ptcPlusBodyInner{min-height:0;overflow:hidden}.ptcPlusFields{margin:0 16px;padding:8px 0 12px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}
|
|
181
|
+
.ptcPlusRow{display:flex;align-items:center;gap:12px;min-height:48px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}.ptcPlusRow:first-child{border-top:0}.ptcPlusMain{flex:1;min-width:0}.ptcPlusLabel{font-size:14px;font-weight:500;line-height:20px}.ptcPlusDetail,.ptcPlusMessage{color:var(--dsw-alias-label-tertiary,#74777d);font-size:12px;line-height:18px;overflow-wrap:anywhere}.ptcPlusInput{box-sizing:border-box;min-width:72px;width:140px;padding:5px 8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:var(--dsw-alias-bg-layer-1,#fff);color:inherit;font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace}.ptcPlusCheck{width:18px;height:18px;accent-color:var(--dsw-alias-interactive-primary,#4d6bfe)}
|
|
182
|
+
.ptcPlusFooter{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:8px}.ptcPlusButton{min-height:32px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:transparent;color:inherit;cursor:pointer;font:500 13px/20px inherit;transition:background-color .16s ease,border-color .16s ease}.ptcPlusButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusButton:disabled,.ptcPlusInput:disabled,.ptcPlusCheck:disabled{cursor:not-allowed;opacity:.55}.ptcPlusActive{display:inline-flex;align-items:center;gap:5px;color:var(--dsw-alias-state-success-primary,#16794f);font-size:12px;line-height:18px;white-space:nowrap}
|
|
183
|
+
@media(max-width:560px){.ptcPlusHeader{padding:12px}.ptcPlusFields{margin:0 12px}.ptcPlusRow{align-items:flex-start;flex-direction:column;gap:6px;padding:10px 0}.ptcPlusInput{width:100%}.ptcPlusFooter{align-items:stretch;flex-direction:column}.ptcPlusButton{width:100%}}
|
|
184
|
+
@media(prefers-reduced-motion:reduce){.ptcPlusHeader,.ptcPlusChevron,.ptcPlusBody,.ptcPlusButton{transition:none}}
|
|
185
|
+
`;
|
|
186
|
+
window.__ModuleLoader__.load({
|
|
187
|
+
// Replaced by the bundle entry with the package name from package.json.
|
|
188
|
+
id: "dsh-ptc-plus",
|
|
189
|
+
factory: (require2) => {
|
|
190
|
+
const React = require2("react");
|
|
191
|
+
const {
|
|
192
|
+
IconCheckOutline14,
|
|
193
|
+
IconChevronDownOutline14,
|
|
194
|
+
IconSettingsOutline16
|
|
195
|
+
} = require2("@deepseek-ai/dsh-client-ui-primitives");
|
|
196
|
+
const module = { exports: {} };
|
|
197
|
+
const h = React.createElement;
|
|
198
|
+
function installStyles() {
|
|
199
|
+
if (document.getElementById(CLIENT_STYLE_ID) !== null) return () => {
|
|
200
|
+
};
|
|
201
|
+
const style = document.createElement("style");
|
|
202
|
+
style.id = CLIENT_STYLE_ID;
|
|
203
|
+
style.textContent = CLIENT_CSS;
|
|
204
|
+
document.head.append(style);
|
|
205
|
+
return () => style.remove();
|
|
206
|
+
}
|
|
207
|
+
function fieldInput(field, value, disabled, onChange) {
|
|
208
|
+
if (field.type === "boolean") {
|
|
209
|
+
return h("input", {
|
|
210
|
+
type: "checkbox",
|
|
211
|
+
role: "switch",
|
|
212
|
+
className: "ptcPlusCheck",
|
|
213
|
+
checked: value === true,
|
|
214
|
+
disabled,
|
|
215
|
+
"aria-label": field.label,
|
|
216
|
+
onChange: (event) => onChange(field, event.target.checked)
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return h("input", {
|
|
220
|
+
type: "number",
|
|
221
|
+
className: "ptcPlusInput",
|
|
222
|
+
value: Number.isSafeInteger(value) ? String(value) : "",
|
|
223
|
+
min: field.min,
|
|
224
|
+
max: field.max,
|
|
225
|
+
step: 1,
|
|
226
|
+
disabled,
|
|
227
|
+
"aria-label": field.label,
|
|
228
|
+
onChange: (event) => {
|
|
229
|
+
const input = event.target.value;
|
|
230
|
+
const parsed = input === "" ? "" : Number(input);
|
|
231
|
+
onChange(field, Number.isSafeInteger(parsed) ? parsed : input);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
function apply(ctx) {
|
|
236
|
+
const preferenceScope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE });
|
|
237
|
+
ctx.effect(installStyles, "ptc-plus: client styles");
|
|
238
|
+
function PTCPlusSettingsCard() {
|
|
239
|
+
const [open, setOpen] = React.useState(false);
|
|
240
|
+
const [status, setStatus] = React.useState("");
|
|
241
|
+
const [pending, setPending] = React.useState(() => /* @__PURE__ */ new Set());
|
|
242
|
+
const writeTail = React.useRef(Promise.resolve());
|
|
243
|
+
const subscribe = React.useCallback((listener) => preferenceScope.subscribe(listener), []);
|
|
244
|
+
const getSnapshot = React.useCallback(() => preferenceScope.getSnapshot(), []);
|
|
245
|
+
const snapshot = React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
246
|
+
const value = snapshot.status === "ready" ? snapshot.value ?? {} : {};
|
|
247
|
+
const enabled = value.enabled === true;
|
|
248
|
+
const unavailable = snapshot.status !== "ready" || snapshot.writable !== true;
|
|
249
|
+
const persist = (field, nextValue) => {
|
|
250
|
+
if (unavailable || pending.has(field.key)) return;
|
|
251
|
+
const operation = writeTail.current.then(async () => {
|
|
252
|
+
const before = preferenceScope.getSnapshot();
|
|
253
|
+
if (before.status !== "ready" || before.writable !== true) return;
|
|
254
|
+
if (field.key !== "enabled" && before.value?.enabled !== true) return;
|
|
255
|
+
if (before.value?.[field.key] === nextValue) return;
|
|
256
|
+
setPending((current) => new Set(current).add(field.key));
|
|
257
|
+
setStatus("");
|
|
258
|
+
try {
|
|
259
|
+
await preferenceScope.set(field.key, nextValue);
|
|
260
|
+
const after = preferenceScope.getSnapshot();
|
|
261
|
+
if (after.status !== "ready" || after.value?.[field.key] !== nextValue) {
|
|
262
|
+
setStatus("\u8BBE\u7F6E\u672A\u751F\u6548\uFF0C\u8BF7\u68C0\u67E5\u8BBE\u7F6E\u51B2\u7A81");
|
|
263
|
+
} else {
|
|
264
|
+
setStatus("\u8BBE\u7F6E\u5DF2\u7ACB\u5373\u751F\u6548");
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
setStatus("\u8BBE\u7F6E\u5931\u8D25\uFF1A" + (error instanceof Error ? error.message : String(error)));
|
|
268
|
+
} finally {
|
|
269
|
+
setPending((current) => {
|
|
270
|
+
const next = new Set(current);
|
|
271
|
+
next.delete(field.key);
|
|
272
|
+
return next;
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
writeTail.current = operation.catch(() => {
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
const fieldDisabled = (field) => unavailable || pending.has(field.key) || field.key !== "enabled" && !enabled;
|
|
280
|
+
return h(
|
|
281
|
+
"li",
|
|
282
|
+
{ className: "ptcPlusCard" },
|
|
283
|
+
h(
|
|
284
|
+
"button",
|
|
285
|
+
{
|
|
286
|
+
type: "button",
|
|
287
|
+
className: "ptcPlusHeader",
|
|
288
|
+
"aria-expanded": open,
|
|
289
|
+
"aria-label": open ? "\u6536\u8D77 PTC Plus \u8BBE\u7F6E" : "\u5C55\u5F00 PTC Plus \u8BBE\u7F6E",
|
|
290
|
+
"aria-controls": "ptc-plus-settings-body",
|
|
291
|
+
onClick: () => setOpen((current) => !current)
|
|
292
|
+
},
|
|
293
|
+
h(IconSettingsOutline16, { size: 16 }),
|
|
294
|
+
h(
|
|
295
|
+
"span",
|
|
296
|
+
{ className: "ptcPlusHeadText" },
|
|
297
|
+
h("span", { className: "ptcPlusName" }, "PTC Plus"),
|
|
298
|
+
h("span", { className: "ptcPlusDescription" }, "PTC \u6A21\u5F0F\u7684\u4F1A\u8BDD\u7EA7 TypeScript REPL\u3002")
|
|
299
|
+
),
|
|
300
|
+
h("span", { className: "ptcPlusStatus", "data-enabled": enabled }, enabled ? "\u5DF2\u542F\u7528" : "\u5DF2\u505C\u7528"),
|
|
301
|
+
h("span", { className: "ptcPlusChevron", "data-open": open, "aria-hidden": true }, h(IconChevronDownOutline14, { size: 14 }))
|
|
302
|
+
),
|
|
303
|
+
h(
|
|
304
|
+
"div",
|
|
305
|
+
{ id: "ptc-plus-settings-body", className: "ptcPlusBody", "data-open": open, "aria-hidden": !open },
|
|
306
|
+
h("div", { className: "ptcPlusBodyInner" }, h(
|
|
307
|
+
"div",
|
|
308
|
+
{ className: "ptcPlusFields" },
|
|
309
|
+
snapshot.status === "loading" ? h("p", { className: "ptcPlusMessage" }, "\u6B63\u5728\u540C\u6B65\u8BBE\u7F6E...") : snapshot.status === "unavailable" ? h("p", { className: "ptcPlusMessage" }, "\u5F53\u524D DSH \u5B9E\u4F8B\u672A\u63D0\u4F9B\u8BBE\u7F6E\u670D\u52A1") : [
|
|
310
|
+
...CONFIG_FIELDS.map((field) => h(
|
|
311
|
+
"div",
|
|
312
|
+
{ key: field.key, className: "ptcPlusRow" },
|
|
313
|
+
h(
|
|
314
|
+
"div",
|
|
315
|
+
{ className: "ptcPlusMain" },
|
|
316
|
+
h("div", { className: "ptcPlusLabel" }, field.label),
|
|
317
|
+
field.description === "" ? null : h("div", { className: "ptcPlusDetail" }, field.description)
|
|
318
|
+
),
|
|
319
|
+
fieldInput(field, value[field.key], fieldDisabled(field), persist)
|
|
320
|
+
)),
|
|
321
|
+
h(
|
|
322
|
+
"div",
|
|
323
|
+
{ key: "footer", className: "ptcPlusFooter" },
|
|
324
|
+
h("span", { className: "ptcPlusMessage", role: "status" }, status || (snapshot.writable ? "\u8BBE\u7F6E\u4F1A\u5728\u4FEE\u6539\u540E\u7ACB\u5373\u751F\u6548" : "\u5F53\u524D\u8BBE\u7F6E\u4E3A\u53EA\u8BFB"))
|
|
325
|
+
)
|
|
326
|
+
]
|
|
327
|
+
))
|
|
328
|
+
)
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
332
|
+
name: "settings.plugin.item",
|
|
333
|
+
key: SETTINGS_NAMESPACE
|
|
334
|
+
}, PTCPlusSettingsCard));
|
|
335
|
+
ctx.inject(["slots", "sessions"], (scope) => {
|
|
336
|
+
function PTCPlusSessionIndicator({ sessionId }) {
|
|
337
|
+
const sessions = React.useSyncExternalStore(
|
|
338
|
+
(listener) => scope.sessions.list.subscribe(listener),
|
|
339
|
+
() => scope.sessions.list.getSnapshot(),
|
|
340
|
+
() => scope.sessions.list.getSnapshot()
|
|
341
|
+
);
|
|
342
|
+
const settings = React.useSyncExternalStore(
|
|
343
|
+
(listener) => preferenceScope.subscribe(listener),
|
|
344
|
+
() => preferenceScope.getSnapshot(),
|
|
345
|
+
() => preferenceScope.getSnapshot()
|
|
346
|
+
);
|
|
347
|
+
if (sessions.byId?.[sessionId]?.agentPreset !== "code" || settings.status !== "ready" || settings.value?.enabled !== true) return null;
|
|
348
|
+
return h(
|
|
349
|
+
"span",
|
|
350
|
+
{ className: "ptcPlusActive", title: "PTC Plus \u5DF2\u542F\u7528" },
|
|
351
|
+
h(IconCheckOutline14, { size: 14 }),
|
|
352
|
+
"PTC Plus"
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
scope.slots.inject("conversation.session.header.actions", () => scope.slots.register({
|
|
356
|
+
name: "conversation.session.header.actions",
|
|
357
|
+
id: "ptc-plus-active",
|
|
358
|
+
order: -9
|
|
359
|
+
}, PTCPlusSessionIndicator));
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
module.exports = { apply, inject: ["settingsScope", "slots", "sessions"] };
|
|
363
|
+
return module.exports;
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
})();
|