@deepseek-ai/dsh-experimental-worktree 0.0.0-stage → 0.2.1-alpha.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/LICENSE +21 -0
- package/README.i18n.yaml +37 -0
- package/README.md +97 -2
- package/README.zh.md +98 -0
- package/lib/index.js +368 -0
- package/lib/types/directory.d.ts +7 -0
- package/lib/types/index.d.ts +54 -0
- package/lib/types/process.d.ts +29 -0
- package/lib/types/types.d.ts +20 -0
- package/package.json +55 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeepSeek
|
|
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.i18n.yaml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
|
2
|
+
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
|
3
|
+
# After editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/experimental/worktree/README.md
|
|
5
|
+
/:
|
|
6
|
+
en: abb4e123de7c0434
|
|
7
|
+
zh: 40f28bb7301d3d0a
|
|
8
|
+
/deepseek-ai-dsh-experimental-worktree:
|
|
9
|
+
en: 0fbe7569a8801a86
|
|
10
|
+
zh: 794c1773e8825da5
|
|
11
|
+
/deepseek-ai-dsh-experimental-worktree/summary:
|
|
12
|
+
en: 954278bb62827f55
|
|
13
|
+
zh: cb96870bfb05ec23
|
|
14
|
+
/deepseek-ai-dsh-experimental-worktree/table-of-contents:
|
|
15
|
+
en: d152484eb41ac6b4
|
|
16
|
+
zh: 2f8a621b991260a1
|
|
17
|
+
/deepseek-ai-dsh-experimental-worktree/use-this-package:
|
|
18
|
+
en: 68aef00468f63589
|
|
19
|
+
zh: a3cdb93c91ff1b9e
|
|
20
|
+
/deepseek-ai-dsh-experimental-worktree/understand-the-implementation:
|
|
21
|
+
en: 6971a7ca9073bef1
|
|
22
|
+
zh: b977c28a75c7f963
|
|
23
|
+
/deepseek-ai-dsh-experimental-worktree/further-exploration:
|
|
24
|
+
en: 5bcd45f0bba56207
|
|
25
|
+
zh: fd6342fd6bac5179
|
|
26
|
+
/deepseek-ai-dsh-experimental-worktree/model-experience:
|
|
27
|
+
en: 4a3506885138c925
|
|
28
|
+
zh: 7ac65dc18dc5c292
|
|
29
|
+
/deepseek-ai-dsh-experimental-worktree/model-experience/kv-cache-effect:
|
|
30
|
+
en: 6826dfaadb24f3da
|
|
31
|
+
zh: 2e2ee715247fc015
|
|
32
|
+
/deepseek-ai-dsh-experimental-worktree/known-limitations-and-deferred-work:
|
|
33
|
+
en: 0adcf97622e86f3b
|
|
34
|
+
zh: 8bcdc336011cd7e1
|
|
35
|
+
/deepseek-ai-dsh-experimental-worktree/known-limitations-and-deferred-work/dev-note:
|
|
36
|
+
en: ad12746a6cc96d8a
|
|
37
|
+
zh: 66275f30a35c88e4
|
package/README.md
CHANGED
|
@@ -1,3 +1,98 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "Create and enter a retained Git worktree under the calling Session file policy."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
# @deepseek-ai/dsh-experimental-worktree
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
Create an isolated checkout from a local commit and continue the same Session in it. Each operation creates a new named branch and leaves the source checkout intact. The existing sandbox governs all writes. Leaving the checkout retains its files and branch.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Mount the service alongside `workingDirectory`, `fs`, `subprocess`, `sandbox`, and `sandboxPolicy` providers. Add the tool package when models need the operation.
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
- name: '@deepseek-ai/dsh-experimental-worktree'
|
|
32
|
+
- name: '@deepseek-ai/dsh-experimental-tool-worktree'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`ctx.worktrees.create(agent, { name?, from? }, signal?)` creates a new branch and checkout, then enters the canonical checkout directory through `ctx.workingDirectory.set`. `from` resolves to a local commit before creation and defaults to `HEAD`; staged, unstaged, untracked, and ignored source files are not copied. `baseCommit` reports the resolved commit object name; repository-local replacement refs still apply to the checked-out content. Checkout disables configured Git clean, smudge, and process filters without changing repository configuration. An existing branch or checkout path is an error. The returned record contains `path`, `branch`, `baseCommit`, and `repositoryRoot`.
|
|
36
|
+
|
|
37
|
+
The default checkout directory is `<current checkout root>/.agents/worktrees/<name>`. The returned `repositoryRoot` is the top-level directory of the caller’s current checkout; when it is a linked checkout, the pool is nested inside it. Omitted names use `worktree-` plus a UUID. The service writes a `.gitignore` containing `*` and a trailing newline only when its allocation creates the pool; existing files and ignore rules are preserved. Relative `directory`, generated `namePrefix`, executable choices, and process limits are configurable in the [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-worktree).
|
|
38
|
+
|
|
39
|
+
-----
|
|
40
|
+
|
|
41
|
+
<a id="understand-the-implementation"></a>
|
|
42
|
+
## Understand the implementation
|
|
43
|
+
|
|
44
|
+
<details>
|
|
45
|
+
<summary>Implementation internals — click to expand</summary>
|
|
46
|
+
|
|
47
|
+
Git and directory allocation run through the mounted subprocess and sandbox providers with the calling Session's existing file policy. Creation does not widen write access to the destination or shared Git administration directory. Directory creation claims the destination exclusively; cancellation and failed setup can leave retained artifacts, whose path appears in the error. Service disposal aborts pending operations and waits for their settlement.
|
|
48
|
+
|
|
49
|
+
| Source | Responsibility |
|
|
50
|
+
|---|---|
|
|
51
|
+
| [`src/index.ts`](src/index.ts) | Resolve the source revision, create the checkout, and publish the new working directory |
|
|
52
|
+
| [`src/process.ts`](src/process.ts) | Confine literal argv, cap output, and await process termination |
|
|
53
|
+
| [`src/directory.ts`](src/directory.ts) | Claim the destination and initialize a newly created pool's ignore file |
|
|
54
|
+
|
|
55
|
+
No runtime invariant companion is published: Git owns checkout registration and the working-directory service owns the Session directory; this package retains no independent projection of either.
|
|
56
|
+
|
|
57
|
+
</details>
|
|
58
|
+
|
|
59
|
+
-----
|
|
60
|
+
|
|
61
|
+
<a id="further-exploration"></a>
|
|
62
|
+
## Further Exploration
|
|
63
|
+
|
|
64
|
+
- [Worktree subsystem](../../../docs/subsystems/worktrees.md) — request, result, and service reference.
|
|
65
|
+
- [Working directory](../../session/working-directory/README.md) — recorded Session directory and directory changes.
|
|
66
|
+
- [Worktree tool](../tool-worktree/README.md) — model-facing creation.
|
|
67
|
+
- [Subprocess](../../subprocess/subprocess/README.md) — process ownership and execution worlds.
|
|
68
|
+
- [Creation decision](../../../.agents/notes/implemented/feature/2026-09-13-explicit-worktree-creation.md) — retained checkouts and existing write grants.
|
|
69
|
+
|
|
70
|
+
-----
|
|
71
|
+
|
|
72
|
+
<a id="model-experience"></a>
|
|
73
|
+
## Model Experience
|
|
74
|
+
|
|
75
|
+
Indirectly, through the worktree tool's creation result and the working-directory service's recorded Session context.
|
|
76
|
+
|
|
77
|
+
#### KV Cache effect
|
|
78
|
+
|
|
79
|
+
This package contributes no prompt text. Its consumers own the appended tool result and recorded directory context.
|
|
80
|
+
|
|
81
|
+
## Known Limitations and Deferred Work
|
|
82
|
+
|
|
83
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
84
|
+
|
|
85
|
+
- **Retained artifacts** — leaving a worktree keeps its branch and files. Setup failures can also retain partial artifacts; removal, pruning, and branch deletion belong to the user or another consumer.
|
|
86
|
+
- **Local revisions only** — creation does not fetch commits or missing partial-clone objects, initialize submodules, install dependencies, or copy uncommitted files. On Git that meets the execution requirements, missing local objects cause creation to fail.
|
|
87
|
+
- **Filtered representations** — checkout skips configured filters to avoid executing repository-configured programs or triggering implicit downloads. Git LFS pointers and encrypted blobs stay in their stored representation; hydration or decryption is a separate user-controlled operation.
|
|
88
|
+
- **Execution requirements** — the subprocess provider must offer Git and Node in the same execution world as the filesystem provider. Git must honor `GIT_NO_LAZY_FETCH`, which Git 2.45 and the 2.39.4, 2.40.2, 2.41.1, 2.42.2, 2.43.4, and 2.44.1 maintenance releases do; older releases ignore it and proceed instead of failing, so creation can fetch missing partial-clone objects from a promisor remote. The `git --no-lazy-fetch` option would reject these releases, but it exists only in Git 2.45 and later and also rejects the patched maintenance releases. The existing sandbox must permit all checkout and shared Git metadata writes.
|
|
89
|
+
|
|
90
|
+
<a id="dev-note"></a>
|
|
91
|
+
### Dev Note
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
95
|
+
|
|
96
|
+
The sandboxed Node child supplies exclusive directory creation, which the [filesystem service](../../fs/fs/src/index.ts) does not expose, and identifies whether this service created the pool. The filesystem contract permits bare providers such as [fs-local](../../fs/fs-local/src/index.ts) to ignore `sandboxPolicy` on writes; `createIfAbsent` protects a file but neither enforces permissions nor identifies who created its directory. Keeping directory allocation and ignore-file creation under the same process sandbox as Git preserves refusal of existing checkout paths and the condition for creating `.gitignore`.
|
|
97
|
+
|
|
98
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "按照调用 Session 的文件策略创建并进入保留的 Git 工作树。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-experimental-worktree
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
从本地提交创建隔离的检出目录,并在其中继续同一个 Session。每次操作创建新的具名分支,源检出目录保持不变。所有写入均受现有沙箱约束。离开检出目录会保留文件与分支。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [了解实现](#understand-the-implementation)
|
|
18
|
+
- [深入探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与待办工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
将服务与 `workingDirectory`、`fs`、`subprocess`、`sandbox` 和 `sandboxPolicy` 提供方一起挂载。模型需要此操作时,再添加工具包。
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
- name: '@deepseek-ai/dsh-experimental-worktree'
|
|
32
|
+
- name: '@deepseek-ai/dsh-experimental-tool-worktree'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`ctx.worktrees.create(agent, { name?, from? }, signal?)` 创建新分支与检出目录,再通过 `ctx.workingDirectory.set` 进入规范化的检出目录。`from` 在创建前解析为本地提交,默认是 `HEAD`;暂存、未暂存、未跟踪以及被忽略的源文件均不复制。`baseCommit` 报告解析出的提交对象名称;仓库本地的替换引用仍作用于检出的内容。检出操作禁用已配置的 Git clean、smudge 和 process 过滤器,不修改仓库配置。分支或检出路径已存在时会报错。返回记录包含 `path`、`branch`、`baseCommit` 和 `repositoryRoot`。
|
|
36
|
+
|
|
37
|
+
默认检出目录是 `<current checkout root>/.agents/worktrees/<name>`。返回的 `repositoryRoot` 是调用方当前检出目录的顶层目录;如果它是链接检出目录,工作树父目录就嵌套在其中。省略名称时,使用 `worktree-` 加 UUID。仅当服务的分配操作创建了父目录时,服务才会写入内容为 `*` 加换行符的 `.gitignore`;已有文件与忽略规则保持不变。相对路径 `directory`、生成名称使用的 `namePrefix`、可执行程序与进程限制可在[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-worktree)中查阅。
|
|
38
|
+
|
|
39
|
+
-----
|
|
40
|
+
|
|
41
|
+
<a id="understand-the-implementation"></a>
|
|
42
|
+
## 了解实现
|
|
43
|
+
|
|
44
|
+
<details>
|
|
45
|
+
<summary>实现细节——点击展开</summary>
|
|
46
|
+
|
|
47
|
+
Git 与目录分配通过挂载的子进程及沙箱提供方执行,沿用调用 Session 的文件策略。创建操作不会扩大目标目录或共享 Git 管理目录的写权限。目录创建独占申请目标路径;取消或初始化失败可能留下已有产物,错误会包含其路径。服务销毁会中止待完成操作,并等待它们结束。
|
|
48
|
+
|
|
49
|
+
| 源码 | 职责 |
|
|
50
|
+
|---|---|
|
|
51
|
+
| [`src/index.ts`](src/index.ts) | 解析源提交、创建检出目录并发布新的工作目录 |
|
|
52
|
+
| [`src/process.ts`](src/process.ts) | 约束字面参数、限制输出并等待进程终止 |
|
|
53
|
+
| [`src/directory.ts`](src/directory.ts) | 申请目标目录并初始化新建工作树父目录的忽略文件 |
|
|
54
|
+
|
|
55
|
+
本包不发布运行时不变量伴随入口:Git 拥有工作树注册信息,工作目录服务拥有 Session 目录;本包不保留二者的独立投影。
|
|
56
|
+
|
|
57
|
+
</details>
|
|
58
|
+
|
|
59
|
+
-----
|
|
60
|
+
|
|
61
|
+
<a id="further-exploration"></a>
|
|
62
|
+
## 深入探索
|
|
63
|
+
|
|
64
|
+
- [工作树子系统](../../../docs/subsystems/worktrees.zh.md)——请求、结果与服务参考。
|
|
65
|
+
- [工作目录](../../session/working-directory/README.zh.md) — 记录的 Session 目录与目录切换。
|
|
66
|
+
- [工作树工具](../tool-worktree/README.zh.md) — 面向模型的创建操作。
|
|
67
|
+
- [子进程](../../subprocess/subprocess/README.zh.md) — 进程所有权与执行环境。
|
|
68
|
+
- [创建决策](../../../.agents/notes/implemented/feature/2026-09-13-explicit-worktree-creation.zh.md) — 保留检出目录与既有写权限。
|
|
69
|
+
|
|
70
|
+
-----
|
|
71
|
+
|
|
72
|
+
<a id="model-experience"></a>
|
|
73
|
+
## 模型体验
|
|
74
|
+
|
|
75
|
+
通过工作树工具的创建结果与工作目录服务记录的 Session 上下文间接影响模型。
|
|
76
|
+
|
|
77
|
+
#### KV Cache 影响
|
|
78
|
+
|
|
79
|
+
本包不贡献提示文本。其消费方负责追加工具结果与记录的目录上下文。
|
|
80
|
+
|
|
81
|
+
## 已知限制与待办工作
|
|
82
|
+
|
|
83
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
84
|
+
|
|
85
|
+
- **保留产物**——离开工作树会保留其分支和文件。初始化失败也可能留下部分产物;移除、清理注册信息与删除分支由用户或其他消费方负责。
|
|
86
|
+
- **仅限本地提交**——创建操作不会获取远端提交或部分克隆中缺失的对象、初始化子模块、安装依赖或复制未提交文件。Git 满足执行要求时,本地对象缺失会导致创建失败。
|
|
87
|
+
- **过滤器处理前的内容**——检出时跳过已配置的过滤器,以避免执行仓库配置的程序或隐式下载。Git LFS 指针与加密 blob 保持存储时的形式;拉取实体内容或解密由用户另行执行。
|
|
88
|
+
- **执行要求**——子进程提供方必须在与文件系统提供方相同的执行环境中提供 Git 与 Node。Git 必须识别 `GIT_NO_LAZY_FETCH`:Git 2.45 及 2.39.4、2.40.2、2.41.1、2.42.2、2.43.4、2.44.1 维护版本均支持;更早的版本会忽略该变量并继续执行而不报错,因此创建操作可能从 promisor 远程获取缺失的部分克隆对象。`git --no-lazy-fetch` 选项会拒绝这些版本,但它仅在 Git 2.45 及以后存在,也会拒绝上述已修补的维护版本。现有沙箱必须允许写入检出目录与共享 Git 元数据。
|
|
89
|
+
|
|
90
|
+
<a id="dev-note"></a>
|
|
91
|
+
### 开发备注
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
95
|
+
|
|
96
|
+
沙箱内的 Node 子进程提供[文件系统服务](../../fs/fs/src/index.ts)未公开的独占目录创建能力,并确定父目录是否由本服务创建。文件系统契约允许 [fs-local](../../fs/fs-local/src/index.ts) 等普通提供方在写入时忽略 `sandboxPolicy`;`createIfAbsent` 保护文件,但既不执行权限约束,也不确定目录由谁创建。将目录分配和忽略文件创建放在与 Git 相同的进程沙箱内,可保留对已有检出路径的拒绝行为,并落实创建 `.gitignore` 的条件。
|
|
97
|
+
|
|
98
|
+
</details>
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { Service } from "@deepseek-ai/cordis";
|
|
3
|
+
import z from "@deepseek-ai/schemastery";
|
|
4
|
+
import { MAX_TIMER_DELAY_MS, deadline } from "@deepseek-ai/dsh-timeout";
|
|
5
|
+
//#region lib/types/directory.js
|
|
6
|
+
/** Static child program for exclusive checkout-directory allocation inside the process sandbox. */
|
|
7
|
+
/**
|
|
8
|
+
* The child receives the configured root and destination as literal argv. Existing roots retain
|
|
9
|
+
* their files; only the process that creates a root writes its self-ignoring .gitignore.
|
|
10
|
+
*/
|
|
11
|
+
const PREPARE_DIRECTORY = `
|
|
12
|
+
import fs from 'node:fs/promises';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
async function main() {
|
|
15
|
+
const [root, destination] = process.argv.slice(1);
|
|
16
|
+
await fs.mkdir(path.dirname(root), { recursive: true });
|
|
17
|
+
let created = false;
|
|
18
|
+
try {
|
|
19
|
+
await fs.mkdir(root);
|
|
20
|
+
created = true;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (error.code !== 'EEXIST' || !(await fs.stat(root)).isDirectory()) throw error;
|
|
23
|
+
}
|
|
24
|
+
if (created) await fs.writeFile(path.join(root, '.gitignore'), '*\\n', { flag: 'wx' });
|
|
25
|
+
await fs.mkdir(path.dirname(destination), { recursive: true });
|
|
26
|
+
await fs.mkdir(destination);
|
|
27
|
+
}
|
|
28
|
+
main().catch(error => { console.error(error.message); process.exitCode = 1; });
|
|
29
|
+
`;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region lib/types/process.js
|
|
32
|
+
/** Bounded, cancellable commands under the calling Session's existing file policy. */
|
|
33
|
+
var __addDisposableResource = function(env, value, async) {
|
|
34
|
+
if (value !== null && value !== void 0) {
|
|
35
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
36
|
+
var dispose, inner;
|
|
37
|
+
if (async) {
|
|
38
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
39
|
+
dispose = value[Symbol.asyncDispose];
|
|
40
|
+
}
|
|
41
|
+
if (dispose === void 0) {
|
|
42
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
43
|
+
dispose = value[Symbol.dispose];
|
|
44
|
+
if (async) inner = dispose;
|
|
45
|
+
}
|
|
46
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
47
|
+
if (inner) dispose = function() {
|
|
48
|
+
try {
|
|
49
|
+
inner.call(this);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
return Promise.reject(e);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
env.stack.push({
|
|
55
|
+
value,
|
|
56
|
+
dispose,
|
|
57
|
+
async
|
|
58
|
+
});
|
|
59
|
+
} else if (async) env.stack.push({ async: true });
|
|
60
|
+
return value;
|
|
61
|
+
};
|
|
62
|
+
var __disposeResources = (function(SuppressedError) {
|
|
63
|
+
return function(env) {
|
|
64
|
+
function fail(e) {
|
|
65
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
66
|
+
env.hasError = true;
|
|
67
|
+
}
|
|
68
|
+
var r, s = 0;
|
|
69
|
+
function next() {
|
|
70
|
+
while (r = env.stack.pop()) try {
|
|
71
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
72
|
+
if (r.dispose) {
|
|
73
|
+
var result = r.dispose.call(r.value);
|
|
74
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
|
|
75
|
+
fail(e);
|
|
76
|
+
return next();
|
|
77
|
+
});
|
|
78
|
+
} else s |= 1;
|
|
79
|
+
} catch (e) {
|
|
80
|
+
fail(e);
|
|
81
|
+
}
|
|
82
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
83
|
+
if (env.hasError) throw env.error;
|
|
84
|
+
}
|
|
85
|
+
return next();
|
|
86
|
+
};
|
|
87
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
|
|
88
|
+
var e = new Error(message);
|
|
89
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
90
|
+
});
|
|
91
|
+
/** Clear inherited repository, attribute-tree, command-line configuration, and replacement-ref selectors. */
|
|
92
|
+
const GIT_ENV = Object.fromEntries([
|
|
93
|
+
"GIT_DIR",
|
|
94
|
+
"GIT_WORK_TREE",
|
|
95
|
+
"GIT_COMMON_DIR",
|
|
96
|
+
"GIT_INDEX_FILE",
|
|
97
|
+
"GIT_OBJECT_DIRECTORY",
|
|
98
|
+
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
99
|
+
"GIT_CONFIG",
|
|
100
|
+
"GIT_CONFIG_PARAMETERS",
|
|
101
|
+
"GIT_CONFIG_COUNT",
|
|
102
|
+
"GIT_CEILING_DIRECTORIES",
|
|
103
|
+
"GIT_PREFIX",
|
|
104
|
+
"GIT_SHALLOW_FILE",
|
|
105
|
+
"GIT_GRAFT_FILE",
|
|
106
|
+
"GIT_REPLACE_REF_BASE",
|
|
107
|
+
"GIT_ATTR_SOURCE"
|
|
108
|
+
].map((key) => [key, void 0]));
|
|
109
|
+
/**
|
|
110
|
+
* Run an argv without a shell and await termination of its managed process range.
|
|
111
|
+
* @param ctx - subprocess and sandbox providers in the same execution world.
|
|
112
|
+
* @param config - complete process resource limits.
|
|
113
|
+
* @param policy - unchanged calling Session policy.
|
|
114
|
+
* @param cwd - canonical process working directory.
|
|
115
|
+
* @param argv - executable followed by literal arguments.
|
|
116
|
+
* @param signal - operation cancellation.
|
|
117
|
+
* @param allowedExitCodes - command-specific successful statuses.
|
|
118
|
+
* @param env - command-local environment overrides, applied after Git selector removal.
|
|
119
|
+
* @returns complete bounded stdout and the exit code.
|
|
120
|
+
*/
|
|
121
|
+
async function runCommand(ctx, config, policy, cwd, argv, signal, allowedExitCodes = [0], env = {}) {
|
|
122
|
+
const env_1 = {
|
|
123
|
+
stack: [],
|
|
124
|
+
error: void 0,
|
|
125
|
+
hasError: false
|
|
126
|
+
};
|
|
127
|
+
try {
|
|
128
|
+
const timeout = __addDisposableResource(env_1, deadline(signal, config.timeoutMs, "WORKTREE_TIMEOUT"), false);
|
|
129
|
+
timeout.signal.throwIfAborted();
|
|
130
|
+
const command = [await ctx.subprocess.resolveExecutable(argv[0], void 0, timeout.signal), ...argv.slice(1)];
|
|
131
|
+
const wrapped = policy.mode === "danger-full-access" ? command : (await ctx.sandbox.confine(command, {
|
|
132
|
+
...policy,
|
|
133
|
+
mode: policy.mode
|
|
134
|
+
}, timeout.signal)).argv;
|
|
135
|
+
timeout.signal.throwIfAborted();
|
|
136
|
+
const child = ctx.subprocess.spawn({
|
|
137
|
+
argv: wrapped,
|
|
138
|
+
cwd,
|
|
139
|
+
env: {
|
|
140
|
+
...GIT_ENV,
|
|
141
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
142
|
+
GIT_LFS_SKIP_SMUDGE: "1",
|
|
143
|
+
GIT_NO_LAZY_FETCH: "1",
|
|
144
|
+
...env
|
|
145
|
+
},
|
|
146
|
+
signal: timeout.signal,
|
|
147
|
+
graceMs: config.graceMs,
|
|
148
|
+
stdio: {
|
|
149
|
+
stdin: "ignore",
|
|
150
|
+
stdout: { maxBytes: config.maxOutputBytes },
|
|
151
|
+
stderr: { maxBytes: config.maxOutputBytes }
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
try {
|
|
155
|
+
const outcome = await child.done;
|
|
156
|
+
timeout.signal.throwIfAborted();
|
|
157
|
+
const captured = child.collected;
|
|
158
|
+
const stdout = captured.stdout.readFrom(0);
|
|
159
|
+
const stderr = captured.stderr.readFrom(0);
|
|
160
|
+
if (outcome.exitCode === null || !allowedExitCodes.includes(outcome.exitCode)) throw new Error(`worktree command failed (${outcome.signal ?? outcome.exitCode}): ${stderr.text.trim()}`);
|
|
161
|
+
if (stdout.lossy) throw new Error("worktree command output exceeds maxOutputBytes");
|
|
162
|
+
return {
|
|
163
|
+
stdout: stdout.text,
|
|
164
|
+
exitCode: outcome.exitCode
|
|
165
|
+
};
|
|
166
|
+
} finally {
|
|
167
|
+
child.terminate();
|
|
168
|
+
await child.waitForExit();
|
|
169
|
+
}
|
|
170
|
+
} catch (e_1) {
|
|
171
|
+
env_1.error = e_1;
|
|
172
|
+
env_1.hasError = true;
|
|
173
|
+
} finally {
|
|
174
|
+
__disposeResources(env_1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region lib/types/index.js
|
|
179
|
+
/** Creates a new Git branch and checkout, then changes the calling Session's working directory. */
|
|
180
|
+
/** Reject path traversal independently of Git's branch-name grammar. */
|
|
181
|
+
function relativePath(value) {
|
|
182
|
+
return value.length > 0 && !value.includes("\\") && !value.includes(":") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== "..");
|
|
183
|
+
}
|
|
184
|
+
/** Creates retained Git worktrees under the mounted filesystem and sandbox providers. */
|
|
185
|
+
var WorktreeService = class extends Service {
|
|
186
|
+
static inject = [
|
|
187
|
+
"workingDirectory",
|
|
188
|
+
"fs",
|
|
189
|
+
"subprocess",
|
|
190
|
+
"sandbox",
|
|
191
|
+
"sandboxPolicy"
|
|
192
|
+
];
|
|
193
|
+
static Config = z.object({
|
|
194
|
+
directory: z.string().default(".agents/worktrees"),
|
|
195
|
+
namePrefix: z.string().default("worktree-"),
|
|
196
|
+
gitCommand: z.string().default("git"),
|
|
197
|
+
nodeCommand: z.string().default("node"),
|
|
198
|
+
timeoutMs: z.number().default(6e4),
|
|
199
|
+
graceMs: z.number().default(3e3),
|
|
200
|
+
maxOutputBytes: z.number().default(65536)
|
|
201
|
+
});
|
|
202
|
+
config;
|
|
203
|
+
lifetime = new AbortController();
|
|
204
|
+
pending = /* @__PURE__ */ new Set();
|
|
205
|
+
constructor(ctx, config) {
|
|
206
|
+
super(ctx, "worktrees");
|
|
207
|
+
this.config = config;
|
|
208
|
+
if (!relativePath(this.config.directory)) throw new Error("worktree: directory must be a relative directory without dot segments");
|
|
209
|
+
if (!relativePath(`${this.config.namePrefix}name`) || this.config.namePrefix.startsWith("-")) throw new Error("worktree: namePrefix must produce a relative branch name");
|
|
210
|
+
for (const key of ["gitCommand", "nodeCommand"]) if (this.config[key].trim().length === 0) throw new Error(`worktree: ${key} must not be empty`);
|
|
211
|
+
for (const key of [
|
|
212
|
+
"timeoutMs",
|
|
213
|
+
"graceMs",
|
|
214
|
+
"maxOutputBytes"
|
|
215
|
+
]) {
|
|
216
|
+
const value = this.config[key];
|
|
217
|
+
if (!Number.isSafeInteger(value) || value <= 0 || value > MAX_TIMER_DELAY_MS) throw new Error(`worktree: ${key} must be a positive integer no greater than ${MAX_TIMER_DELAY_MS}`);
|
|
218
|
+
}
|
|
219
|
+
ctx.effect(() => async () => {
|
|
220
|
+
this.lifetime.abort(/* @__PURE__ */ new Error("worktree service disposed"));
|
|
221
|
+
await Promise.allSettled(this.pending);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Create a fresh branch and checkout at a pinned local revision, then enter it.
|
|
226
|
+
* Existing branches or checkout paths fail. Uncommitted files stay in the source checkout.
|
|
227
|
+
* Checkout disables configured clean, smudge, and process filters without changing Git config.
|
|
228
|
+
* Repository-local replacement refs still apply; baseCommit reports the resolved object name.
|
|
229
|
+
* The new checkout becomes current only after Git setup succeeds. Failures may retain newly
|
|
230
|
+
* allocated Git/filesystem artifacts; no branch or checkout is removed automatically.
|
|
231
|
+
* @param agent - caller whose current directory selects the source repository and file policy.
|
|
232
|
+
* @param request - optional new name and local revision; defaults are generated name and HEAD.
|
|
233
|
+
* @param signal - cancellation of lookup, creation, and working-directory publication.
|
|
234
|
+
* @returns canonical checkout path, branch name, pinned commit, and source repository root.
|
|
235
|
+
*/
|
|
236
|
+
async create(agent, request = {}, signal) {
|
|
237
|
+
const operationSignal = signal === void 0 ? this.lifetime.signal : AbortSignal.any([this.lifetime.signal, signal]);
|
|
238
|
+
operationSignal.throwIfAborted();
|
|
239
|
+
const operation = this.createCheckout(agent, this.resolve(request), operationSignal);
|
|
240
|
+
this.pending.add(operation);
|
|
241
|
+
try {
|
|
242
|
+
return await operation;
|
|
243
|
+
} finally {
|
|
244
|
+
this.pending.delete(operation);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
resolve(request) {
|
|
248
|
+
const branch = request.name ?? `${this.config.namePrefix}${randomUUID()}`;
|
|
249
|
+
if (!relativePath(branch) || branch.startsWith("-")) throw new Error("worktree name must be a relative Git branch name");
|
|
250
|
+
const revision = request.from ?? "HEAD";
|
|
251
|
+
if (revision.length === 0) throw new Error("worktree from must name a local commit, branch, or tag");
|
|
252
|
+
return {
|
|
253
|
+
branch,
|
|
254
|
+
revision
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
async createCheckout(agent, { branch, revision }, signal) {
|
|
258
|
+
const cwd = await this.ctx.workingDirectory.ensure(agent, signal);
|
|
259
|
+
const policy = this.ctx.sandboxPolicy.resolve({ session: agent.session });
|
|
260
|
+
const run = (directory, args, allowedExitCodes, env) => runCommand(this.ctx, this.config, policy, directory, [
|
|
261
|
+
this.config.gitCommand,
|
|
262
|
+
"-c",
|
|
263
|
+
"core.hooksPath=/dev/null",
|
|
264
|
+
"-c",
|
|
265
|
+
"core.fsmonitor=false",
|
|
266
|
+
...args
|
|
267
|
+
], signal, allowedExitCodes, env);
|
|
268
|
+
await run(cwd, ["check-ref-format", `refs/heads/${branch}`]);
|
|
269
|
+
const rootOutput = await run(cwd, ["rev-parse", "--show-toplevel"]);
|
|
270
|
+
const rootTarget = await this.ctx.fs.resolve(rootOutput.stdout.replace(/\r?\n$/, ""), {
|
|
271
|
+
cwd,
|
|
272
|
+
signal
|
|
273
|
+
});
|
|
274
|
+
const repositoryRoot = this.ctx.fs.processPath(rootTarget);
|
|
275
|
+
const baseCommit = (await run(repositoryRoot, [
|
|
276
|
+
"rev-parse",
|
|
277
|
+
"--verify",
|
|
278
|
+
"--end-of-options",
|
|
279
|
+
`${revision}^{commit}`
|
|
280
|
+
])).stdout.trim();
|
|
281
|
+
if ((await run(repositoryRoot, [
|
|
282
|
+
"show-ref",
|
|
283
|
+
"--verify",
|
|
284
|
+
"--quiet",
|
|
285
|
+
`refs/heads/${branch}`
|
|
286
|
+
], [0, 1])).exitCode === 0) throw new Error(`worktree branch already exists: ${branch}`);
|
|
287
|
+
const pool = await this.ctx.fs.resolve(this.config.directory, {
|
|
288
|
+
cwd: repositoryRoot,
|
|
289
|
+
signal
|
|
290
|
+
});
|
|
291
|
+
const poolPath = this.ctx.fs.processPath(pool);
|
|
292
|
+
const destination = await this.ctx.fs.resolve(branch, {
|
|
293
|
+
cwd: poolPath,
|
|
294
|
+
signal
|
|
295
|
+
});
|
|
296
|
+
const path = this.ctx.fs.processPath(destination);
|
|
297
|
+
if (await this.ctx.fs.lstat(path, void 0, signal) !== void 0) throw new Error(`worktree path already exists: ${path}`);
|
|
298
|
+
try {
|
|
299
|
+
await runCommand(this.ctx, this.config, policy, repositoryRoot, [
|
|
300
|
+
this.config.nodeCommand,
|
|
301
|
+
"--input-type=module",
|
|
302
|
+
"-e",
|
|
303
|
+
PREPARE_DIRECTORY,
|
|
304
|
+
poolPath,
|
|
305
|
+
path
|
|
306
|
+
], signal);
|
|
307
|
+
await run(repositoryRoot, [
|
|
308
|
+
"--work-tree",
|
|
309
|
+
repositoryRoot,
|
|
310
|
+
"worktree",
|
|
311
|
+
"add",
|
|
312
|
+
"--no-checkout",
|
|
313
|
+
"-b",
|
|
314
|
+
branch,
|
|
315
|
+
"--",
|
|
316
|
+
path,
|
|
317
|
+
baseCommit
|
|
318
|
+
]);
|
|
319
|
+
const configured = await run(path, [
|
|
320
|
+
"config",
|
|
321
|
+
"--null",
|
|
322
|
+
"--name-only",
|
|
323
|
+
"--get-regexp",
|
|
324
|
+
"^filter\\."
|
|
325
|
+
], [0, 1]);
|
|
326
|
+
const drivers = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const key of configured.stdout.split("\0")) {
|
|
328
|
+
const separator = key.lastIndexOf(".");
|
|
329
|
+
if (separator > 7 && [
|
|
330
|
+
"clean",
|
|
331
|
+
"smudge",
|
|
332
|
+
"process",
|
|
333
|
+
"required"
|
|
334
|
+
].includes(key.slice(separator + 1))) drivers.add(key.slice(7, separator));
|
|
335
|
+
}
|
|
336
|
+
const overrides = { GIT_CONFIG_COUNT: String(drivers.size * 4) };
|
|
337
|
+
let index = 0;
|
|
338
|
+
for (const driver of drivers) for (const attribute of [
|
|
339
|
+
"clean",
|
|
340
|
+
"smudge",
|
|
341
|
+
"process",
|
|
342
|
+
"required"
|
|
343
|
+
]) {
|
|
344
|
+
overrides[`GIT_CONFIG_KEY_${index}`] = `filter.${driver}.${attribute}`;
|
|
345
|
+
overrides[`GIT_CONFIG_VALUE_${index}`] = attribute === "required" ? "false" : "";
|
|
346
|
+
index++;
|
|
347
|
+
}
|
|
348
|
+
await run(path, [
|
|
349
|
+
"--work-tree",
|
|
350
|
+
path,
|
|
351
|
+
"reset",
|
|
352
|
+
"--hard",
|
|
353
|
+
"--no-recurse-submodules",
|
|
354
|
+
baseCommit
|
|
355
|
+
], void 0, overrides);
|
|
356
|
+
return {
|
|
357
|
+
path: await this.ctx.workingDirectory.set(agent, path, signal),
|
|
358
|
+
branch,
|
|
359
|
+
baseCommit,
|
|
360
|
+
repositoryRoot
|
|
361
|
+
};
|
|
362
|
+
} catch (error) {
|
|
363
|
+
throw new Error(`Could not finish worktree ${branch} at ${path}. Any created checkout and branch are retained. ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
//#endregion
|
|
368
|
+
export { WorktreeService, WorktreeService as default };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Static child program for exclusive checkout-directory allocation inside the process sandbox. */
|
|
2
|
+
/**
|
|
3
|
+
* The child receives the configured root and destination as literal argv. Existing roots retain
|
|
4
|
+
* their files; only the process that creates a root writes its self-ignoring .gitignore.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PREPARE_DIRECTORY = "\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nasync function main() {\n const [root, destination] = process.argv.slice(1);\n await fs.mkdir(path.dirname(root), { recursive: true });\n let created = false;\n try {\n await fs.mkdir(root);\n created = true;\n } catch (error) {\n if (error.code !== 'EEXIST' || !(await fs.stat(root)).isDirectory()) throw error;\n }\n if (created) await fs.writeFile(path.join(root, '.gitignore'), '*\\n', { flag: 'wx' });\n await fs.mkdir(path.dirname(destination), { recursive: true });\n await fs.mkdir(destination);\n}\nmain().catch(error => { console.error(error.message); process.exitCode = 1; });\n";
|
|
7
|
+
//# sourceMappingURL=directory.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Creates a new Git branch and checkout, then changes the calling Session's working directory. */
|
|
2
|
+
import { Context, Service } from '@deepseek-ai/cordis';
|
|
3
|
+
import z from '@deepseek-ai/schemastery';
|
|
4
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
5
|
+
import type { CreatedWorktree, CreateWorktreeRequest } from './types.ts';
|
|
6
|
+
export type { CreatedWorktree, CreateWorktreeRequest } from './types.ts';
|
|
7
|
+
/** Worktree location, generated names, executables, and subprocess resource limits. */
|
|
8
|
+
export interface Config {
|
|
9
|
+
/** Relative checkout pool inside the source repository. */
|
|
10
|
+
directory?: string;
|
|
11
|
+
/** Prefix for automatically generated branch and checkout names. */
|
|
12
|
+
namePrefix?: string;
|
|
13
|
+
/** Executable name or absolute execution-world path of a Git release that honors `GIT_NO_LAZY_FETCH`. */
|
|
14
|
+
gitCommand?: string;
|
|
15
|
+
/** Node executable used for sandboxed directory allocation. */
|
|
16
|
+
nodeCommand?: string;
|
|
17
|
+
/** Deadline in milliseconds for one command. */
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
/** Subprocess termination grace in milliseconds. */
|
|
20
|
+
graceMs?: number;
|
|
21
|
+
/** Maximum captured bytes per subprocess output stream. */
|
|
22
|
+
maxOutputBytes?: number;
|
|
23
|
+
}
|
|
24
|
+
declare module '@deepseek-ai/cordis' {
|
|
25
|
+
interface Context {
|
|
26
|
+
worktrees: WorktreeService;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Creates retained Git worktrees under the mounted filesystem and sandbox providers. */
|
|
30
|
+
export declare class WorktreeService extends Service {
|
|
31
|
+
static inject: string[];
|
|
32
|
+
static Config: z<Config>;
|
|
33
|
+
private readonly config;
|
|
34
|
+
private readonly lifetime;
|
|
35
|
+
private readonly pending;
|
|
36
|
+
constructor(ctx: Context, config: Config);
|
|
37
|
+
/**
|
|
38
|
+
* Create a fresh branch and checkout at a pinned local revision, then enter it.
|
|
39
|
+
* Existing branches or checkout paths fail. Uncommitted files stay in the source checkout.
|
|
40
|
+
* Checkout disables configured clean, smudge, and process filters without changing Git config.
|
|
41
|
+
* Repository-local replacement refs still apply; baseCommit reports the resolved object name.
|
|
42
|
+
* The new checkout becomes current only after Git setup succeeds. Failures may retain newly
|
|
43
|
+
* allocated Git/filesystem artifacts; no branch or checkout is removed automatically.
|
|
44
|
+
* @param agent - caller whose current directory selects the source repository and file policy.
|
|
45
|
+
* @param request - optional new name and local revision; defaults are generated name and HEAD.
|
|
46
|
+
* @param signal - cancellation of lookup, creation, and working-directory publication.
|
|
47
|
+
* @returns canonical checkout path, branch name, pinned commit, and source repository root.
|
|
48
|
+
*/
|
|
49
|
+
create(agent: Agent, request?: CreateWorktreeRequest, signal?: AbortSignal): Promise<CreatedWorktree>;
|
|
50
|
+
private resolve;
|
|
51
|
+
private createCheckout;
|
|
52
|
+
}
|
|
53
|
+
export default WorktreeService;
|
|
54
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Bounded, cancellable commands under the calling Session's existing file policy. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { SandboxExecutionPolicy } from '@deepseek-ai/dsh-sandbox';
|
|
4
|
+
/** Resolved process resource limits owned by the worktree service. */
|
|
5
|
+
export interface ProcessConfig {
|
|
6
|
+
/** Deadline for one command, including executable lookup and confinement. */
|
|
7
|
+
timeoutMs: number;
|
|
8
|
+
/** Termination grace period for each managed subprocess. */
|
|
9
|
+
graceMs: number;
|
|
10
|
+
/** Maximum captured bytes per output stream. */
|
|
11
|
+
maxOutputBytes: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Run an argv without a shell and await termination of its managed process range.
|
|
15
|
+
* @param ctx - subprocess and sandbox providers in the same execution world.
|
|
16
|
+
* @param config - complete process resource limits.
|
|
17
|
+
* @param policy - unchanged calling Session policy.
|
|
18
|
+
* @param cwd - canonical process working directory.
|
|
19
|
+
* @param argv - executable followed by literal arguments.
|
|
20
|
+
* @param signal - operation cancellation.
|
|
21
|
+
* @param allowedExitCodes - command-specific successful statuses.
|
|
22
|
+
* @param env - command-local environment overrides, applied after Git selector removal.
|
|
23
|
+
* @returns complete bounded stdout and the exit code.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runCommand(ctx: Context, config: ProcessConfig, policy: SandboxExecutionPolicy, cwd: string, argv: readonly [string, ...string[]], signal: AbortSignal, allowedExitCodes?: readonly number[], env?: NodeJS.ProcessEnv): Promise<{
|
|
26
|
+
stdout: string;
|
|
27
|
+
exitCode: number;
|
|
28
|
+
}>;
|
|
29
|
+
//# sourceMappingURL=process.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Public requests and results for creating a Git worktree and entering it. */
|
|
2
|
+
/** A new checkout from a locally available Git commit, branch, or tag. */
|
|
3
|
+
export interface CreateWorktreeRequest {
|
|
4
|
+
/** New branch name, also used as the relative checkout directory; omission generates a name. */
|
|
5
|
+
name?: string;
|
|
6
|
+
/** Local revision to resolve before creation; omission selects HEAD. */
|
|
7
|
+
from?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Committed Git baseline and the calling Session's resulting working directory. */
|
|
10
|
+
export interface CreatedWorktree {
|
|
11
|
+
/** Canonical absolute checkout directory in the filesystem provider's execution world. */
|
|
12
|
+
path: string;
|
|
13
|
+
/** Newly created branch name. */
|
|
14
|
+
branch: string;
|
|
15
|
+
/** Resolved commit object name; repository-local replacement refs can change its checked-out content. */
|
|
16
|
+
baseCommit: string;
|
|
17
|
+
/** Canonical root of the source checkout. */
|
|
18
|
+
repositoryRoot: string;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-experimental-worktree",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"description": "Creates a named Git worktree and changes the calling Session working directory",
|
|
4
|
+
"version": "0.2.1-alpha.3",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "lib/index.js",
|
|
10
|
+
"types": "lib/types/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./lib/types/index.d.ts",
|
|
14
|
+
"default": "./lib/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./src/*": "./src/*",
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib/index.js",
|
|
21
|
+
"lib/types/**/*.d.ts"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
27
|
+
"directory": "packages/experimental/worktree"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.2",
|
|
31
|
+
"@deepseek-ai/dsh-timeout": "0.2.1-alpha.3"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.2",
|
|
35
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.3",
|
|
36
|
+
"@deepseek-ai/dsh-fs": "0.2.1-alpha.3",
|
|
37
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.3",
|
|
38
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.3",
|
|
39
|
+
"@deepseek-ai/dsh-subprocess": "0.2.1-alpha.3",
|
|
40
|
+
"@deepseek-ai/dsh-working-directory": "0.2.1-alpha.3"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.2",
|
|
44
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.3",
|
|
45
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.3",
|
|
46
|
+
"@deepseek-ai/dsh-fs": "0.2.1-alpha.3",
|
|
47
|
+
"@deepseek-ai/dsh-fs-local": "0.2.1-alpha.3",
|
|
48
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.3",
|
|
49
|
+
"@deepseek-ai/dsh-sandbox-local": "0.2.1-alpha.3",
|
|
50
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.3",
|
|
51
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.3",
|
|
52
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.3",
|
|
53
|
+
"@deepseek-ai/dsh-subprocess": "0.2.1-alpha.3",
|
|
54
|
+
"@deepseek-ai/dsh-subprocess-local": "0.2.1-alpha.3",
|
|
55
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.3",
|
|
56
|
+
"@deepseek-ai/dsh-working-directory": "0.2.1-alpha.3"
|
|
57
|
+
}
|
|
6
58
|
}
|