dsh-dl-server 0.0.0-stage → 0.1.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 +119 -2
- package/README.zh.md +120 -0
- package/cordis.patch.yml +16 -0
- package/lib/bootlog.js +31 -0
- package/lib/domain.js +51 -0
- package/lib/index.js +92 -0
- package/lib/queue.js +591 -0
- package/lib/tools.js +216 -0
- package/lib/transport.js +357 -0
- package/lib/types.js +54 -0
- package/package.json +64 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jestions
|
|
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
CHANGED
|
@@ -1,3 +1,120 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-dl-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
**DLS** — DSH Download Server
|
|
6
|
+
A persistent download queue for DeepSeek Harness.
|
|
7
|
+
|
|
8
|
+
> Downloads should not live inside a session.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
### From the UI (recommended)
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Settings → Plugins → Install a third-party plugin → Add plugin
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Type **`dsh-dl-server`**, confirm, then **restart DSH once**.
|
|
21
|
+
|
|
22
|
+
### From the command line
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
dsh plugin add dsh-dl-server
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> `dsh` must be on your PATH. The desktop build ships one at
|
|
29
|
+
> `…\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd`; if you have never added it, use the UI path.
|
|
30
|
+
|
|
31
|
+
**Requires DSH 0.2.1-rc.5 or newer.**
|
|
32
|
+
**aria2 is optional** — install it for 16-way segmented downloading, or skip it and the plugin falls back to a single-stream transfer. Same features, less speed.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Why
|
|
37
|
+
|
|
38
|
+
Most DSH downloaders are one tool function that blocks until the file lands. They make downloads **fast**; they don't solve three things:
|
|
39
|
+
|
|
40
|
+
- the download dies when the session or the turn ends
|
|
41
|
+
- there is no queue, no pause, no resume
|
|
42
|
+
- nothing survives a restart
|
|
43
|
+
|
|
44
|
+
DLS is the **queue layer**, not the transport layer.
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
Jobs live in DSH, not in a session.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A download started in one session is visible to every other session — progress, destination, everything. But only the session that started it can pause, resume, or cancel it.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## The seven tools
|
|
55
|
+
|
|
56
|
+
| Tool | What it does |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `download_enqueue` | Queue a job, **return immediately** with a job id |
|
|
59
|
+
| `download_status` | Inspect the queue or a single job (`J#1042`) |
|
|
60
|
+
| `download_wait` | Block until a job reaches a terminal state, with a timeout |
|
|
61
|
+
| `download_pause` | Pause, **keeping the bytes already on disk** |
|
|
62
|
+
| `download_resume` | Continue from the last checkpoint |
|
|
63
|
+
| `download_cancel` | Cancel, optionally dropping the partial file |
|
|
64
|
+
| `download_cleanup` | Drop records (the default scope leaves downloaded files alone) |
|
|
65
|
+
|
|
66
|
+
## Example
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
You: download these two PDFs into D:\Papers and tell me when they're done
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The agent calls `download_enqueue` twice and gets `J#10` / `J#11` back, then `download_wait`.
|
|
73
|
+
|
|
74
|
+
Because enqueue returns immediately, it can fire off ten downloads without waiting for any of them.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Measured
|
|
79
|
+
|
|
80
|
+
Same machine, same file:
|
|
81
|
+
|
|
82
|
+
| Setup | Throughput |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| TUNA mirror · single connection | 43.98 MB/s |
|
|
85
|
+
| TUNA mirror · 16 threads (another plugin) | 54.20 MB/s |
|
|
86
|
+
| TUNA mirror · **DLS** | **61.08 MB/s** (saturates a 500 Mbps link) |
|
|
87
|
+
| HuggingFace · single-stream fallback | 1.00 MB/s |
|
|
88
|
+
| HuggingFace · **DLS** | **6.96 MB/s** (7×) |
|
|
89
|
+
|
|
90
|
+
The last row is the point: **on a per-connection-throttled host, segmentation beats bandwidth.**
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Behaviour worth knowing
|
|
95
|
+
|
|
96
|
+
- **Ownership is per session.** Any session on the machine can *see* every job; only the owner can mutate one.
|
|
97
|
+
- **`cleanup(scope="all")` deletes files.** The default `allWithoutFiles` only drops records.
|
|
98
|
+
- **Checkpoints live in DSH storage**, not as lock files dropped into your home directory — the plugin does not depend on `TMPDIR` or the working directory.
|
|
99
|
+
- **When aria2 is unavailable** the transport degrades to a single Node stream. No features lost, some speed lost.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Docs
|
|
104
|
+
|
|
105
|
+
- [SPEC.md](SPEC.md) — the 565-line design doc: comparison with four sibling plugins, what was borrowed from PCL2, and the v1 trade-offs
|
|
106
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) — layout, how to run the tests, what not to touch
|
|
107
|
+
- [SECURITY.md](SECURITY.md) — how to report a vulnerability, and what counts as one
|
|
108
|
+
|
|
109
|
+
## Roadmap
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
v1 queue + persistence + seven tools + aria2 transport ← released
|
|
113
|
+
v1.1 disk headroom / silent failures / session fairness / mirrors
|
|
114
|
+
v2 shell interception / torrents / own segmented engine / content addressing
|
|
115
|
+
v3 user panel + callbacks into the agent
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# dsh-dl-server
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 中文
|
|
4
|
+
|
|
5
|
+
**DLS** — DSH Download Server
|
|
6
|
+
大肥鱼自己的下载服务器插件
|
|
7
|
+
|
|
8
|
+
> 下载不该活在会话里。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 装
|
|
13
|
+
|
|
14
|
+
### 方式一:界面(推荐)
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
设置 → 插件 → 安装第三方插件 → 添加插件
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
输入框里填 **`dsh-dl-server`**,确认,然后**重启一次 DSH**。
|
|
21
|
+
|
|
22
|
+
### 方式二:命令行
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
dsh plugin add dsh-dl-server
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> `dsh` 需要先在 PATH 里。桌面版自带的那份在
|
|
29
|
+
> `…\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd`,没加过 PATH 的话用方式一。
|
|
30
|
+
|
|
31
|
+
**要求 DSH 0.2.1-rc.5 或以上。**
|
|
32
|
+
**aria2 可选** —— 装了走 16 线程分段,没装走单流兜底,一样能下,只是慢一些。
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 它解决什么
|
|
37
|
+
|
|
38
|
+
现有的 DSH 下载插件几乎都是「一个工具函数 + 阻塞等到文件落地」。它们让下载**变快**,但没解决三件事:
|
|
39
|
+
|
|
40
|
+
- 会话或回合结束,下载就断了
|
|
41
|
+
- 不能排队、不能暂停恢复
|
|
42
|
+
- 进程重启,什么都没了
|
|
43
|
+
|
|
44
|
+
DLS 做的是**队列层**,不是传输层。
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
任务活在 DSH 里,不活在会话里
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
一个会话发起的下载,其他会话都能看见进度和落盘路径;但只有发起它的那个会话能暂停、续传、取消。
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 七个工具
|
|
55
|
+
|
|
56
|
+
| 工具 | 作用 |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `download_enqueue` | 入队,**立刻返回** jobId,不等下载 |
|
|
59
|
+
| `download_status` | 看队列和单个任务的进度(`J#1042` 这种 id) |
|
|
60
|
+
| `download_wait` | 等某个任务到终态,带超时 |
|
|
61
|
+
| `download_pause` | 暂停,**保留已下字节** |
|
|
62
|
+
| `download_resume` | 从断点接着下 |
|
|
63
|
+
| `download_cancel` | 取消,可选删掉半截文件 |
|
|
64
|
+
| `download_cleanup` | 清记录(默认不动已下载的文件) |
|
|
65
|
+
|
|
66
|
+
## 用它
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
你: 把这两个 PDF 下到 D:\Papers,下完告诉我
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
agent 会调 `download_enqueue` 两次拿到 `J#10` / `J#11`,然后 `download_wait`。
|
|
73
|
+
|
|
74
|
+
**入队就返回**,所以它可以一次发十个,不用等。
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 实测
|
|
79
|
+
|
|
80
|
+
同一台机器,同一个文件:
|
|
81
|
+
|
|
82
|
+
| 场景 | 结果 |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| 清华 TUNA 源 · 单连接 | 43.98 MB/s |
|
|
85
|
+
| 清华 TUNA 源 · 16 线程(其他插件) | 54.20 MB/s |
|
|
86
|
+
| 清华 TUNA 源 · **DLS** | **61.08 MB/s**(跑满 500M 链路) |
|
|
87
|
+
| HuggingFace · 单流兜底 | 1.00 MB/s |
|
|
88
|
+
| HuggingFace · **DLS** | **6.96 MB/s**(7 倍) |
|
|
89
|
+
|
|
90
|
+
最后一个说明问题:**被限速的源上,分段比带宽更值钱。**
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 已知行为(不是 bug)
|
|
95
|
+
|
|
96
|
+
- **归属按会话**:同一台机器上任何会话都能**看见**全部任务,但只有 owner 能改。
|
|
97
|
+
- **`cleanup(scope="all")` 会删文件**;默认的 `allWithoutFiles` 只删记录。
|
|
98
|
+
- **断点落在 DSH 的存储里**,不是自己往用户目录丢锁文件 —— 所以不看 TMPDIR 或 cwd 的脸色。
|
|
99
|
+
- **aria2 不可用时自动降级**到 Node 单流,功能不减,速度降。
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 文档
|
|
104
|
+
|
|
105
|
+
- [SPEC.md](SPEC.md) —— 565 行设计说明,含四个同类插件的对比、从 PCL2 借了什么、v1 的取舍
|
|
106
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) —— 代码布局、怎么跑测试、哪些地方别乱动
|
|
107
|
+
- [SECURITY.md](SECURITY.md) —— 漏洞怎么报,什么算漏洞什么不算
|
|
108
|
+
|
|
109
|
+
## 路线
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
v1 队列 + 持久化 + 七个工具 + aria2 传输 ← 已发布
|
|
113
|
+
v1.1 磁盘余量 / 静默失败 / 会话公平 / 镜像表
|
|
114
|
+
v2 拦截钩子 / 种子 / 自研分段 / 内容寻址
|
|
115
|
+
v3 用户面板 + 操作回传
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 许可
|
|
119
|
+
|
|
120
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# dsh-dl-server bundle patch
|
|
2
|
+
- insert:
|
|
3
|
+
- id: dsh-dl-server
|
|
4
|
+
name: 'dsh-dl-server'
|
|
5
|
+
config:
|
|
6
|
+
maxConcurrentJobs: 3
|
|
7
|
+
connectionsPerJob: 16
|
|
8
|
+
aria2Path: ''
|
|
9
|
+
aria2Port: 16801
|
|
10
|
+
speedLimitKbps: 0
|
|
11
|
+
proxy: ''
|
|
12
|
+
defaultDownloadDir: ''
|
|
13
|
+
checkpointSeconds: 30
|
|
14
|
+
autoResumeOnSessionReopen: true
|
|
15
|
+
autoResumeOnHostRestart: true
|
|
16
|
+
keepCompletedRecords: 200
|
package/lib/bootlog.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Minimal boot trace. Overwritten every boot, so it always describes the latest
|
|
7
|
+
* start. A support report is "send me %TEMP%/dls-boot.log".
|
|
8
|
+
*/
|
|
9
|
+
export const BOOT_LOG = path.join(os.tmpdir(), 'dls-boot.log')
|
|
10
|
+
|
|
11
|
+
let started = false
|
|
12
|
+
|
|
13
|
+
/** @param {string} step @param {string} [detail] */
|
|
14
|
+
export function mark(step, detail) {
|
|
15
|
+
try {
|
|
16
|
+
if (!started) {
|
|
17
|
+
fs.writeFileSync(BOOT_LOG, `dsh-dl-server boot trace\n`)
|
|
18
|
+
started = true
|
|
19
|
+
}
|
|
20
|
+
const line = `${new Date().toISOString()} ${step}${detail ? ` ${detail}` : ''}`
|
|
21
|
+
fs.appendFileSync(BOOT_LOG, `${line}\n`)
|
|
22
|
+
} catch {
|
|
23
|
+
/* tracing must never break the plugin */
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @param {string} step @param {unknown} error */
|
|
28
|
+
export function markFailure(step, error) {
|
|
29
|
+
const stack = error instanceof Error ? (error.stack ?? error.message) : String(error)
|
|
30
|
+
mark(`FAILED at ${step}`, stack.split('\n').slice(0, 6).join(' | '))
|
|
31
|
+
}
|
package/lib/domain.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'
|
|
3
|
+
|
|
4
|
+
const JobRecordSchema = z.object({
|
|
5
|
+
id: z.string(),
|
|
6
|
+
seq: z.number().int().nonnegative(),
|
|
7
|
+
|
|
8
|
+
ownerWorkspace: z.string(),
|
|
9
|
+
ownerSession: z.string(),
|
|
10
|
+
origin: z.string().optional(),
|
|
11
|
+
|
|
12
|
+
urls: z.array(z.string()).min(1),
|
|
13
|
+
destPath: z.string(),
|
|
14
|
+
|
|
15
|
+
state: z.enum(['queued', 'running', 'paused', 'completed', 'failed', 'canceled']),
|
|
16
|
+
pauseReason: z.enum(['user', 'session_closed', 'host_restart']).optional(),
|
|
17
|
+
|
|
18
|
+
createdAt: z.number(),
|
|
19
|
+
updatedAt: z.number(),
|
|
20
|
+
|
|
21
|
+
bytesTotal: z.number(),
|
|
22
|
+
bytesCheckpoint: z.number(),
|
|
23
|
+
|
|
24
|
+
attempts: z.number().int().nonnegative(),
|
|
25
|
+
lastError: z.string().optional(),
|
|
26
|
+
|
|
27
|
+
expectSha256: z.string().optional(),
|
|
28
|
+
finalBytes: z.number().optional(),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The persistent queue domain.
|
|
33
|
+
*
|
|
34
|
+
* per-record: one corrupt job must not make the whole queue unreadable.
|
|
35
|
+
* backup-and-skip: move the bad document aside and keep booting.
|
|
36
|
+
* global.nextSeq: the global counter behind J# ids.
|
|
37
|
+
*/
|
|
38
|
+
export const JobDomain = defineDomain({
|
|
39
|
+
// Domain names must match /^[a-z][a-z0-9_]*$/ — hyphens are rejected at load.
|
|
40
|
+
name: 'dsh_dl_jobs',
|
|
41
|
+
version: 1,
|
|
42
|
+
layout: 'per-record',
|
|
43
|
+
invalidRecords: 'backup-and-skip',
|
|
44
|
+
tables: {
|
|
45
|
+
jobs: domainTable(JobRecordSchema),
|
|
46
|
+
},
|
|
47
|
+
global: {
|
|
48
|
+
initial: { nextSeq: 1 },
|
|
49
|
+
schema: z.object({ nextSeq: z.number().int().positive() }),
|
|
50
|
+
},
|
|
51
|
+
})
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { JobDomain } from './domain.js'
|
|
2
|
+
import { Queue } from './queue.js'
|
|
3
|
+
import { Transport } from './transport.js'
|
|
4
|
+
import { registerTools } from './tools.js'
|
|
5
|
+
import { mark, markFailure } from './bootlog.js'
|
|
6
|
+
|
|
7
|
+
export const name = 'dsh-dl-server'
|
|
8
|
+
export const inject = ['tools', 'storageDomain']
|
|
9
|
+
|
|
10
|
+
const DEFAULTS = Object.freeze({
|
|
11
|
+
maxConcurrentJobs: 3,
|
|
12
|
+
connectionsPerJob: 16,
|
|
13
|
+
aria2Path: '',
|
|
14
|
+
aria2Port: 16801,
|
|
15
|
+
speedLimitKbps: 0,
|
|
16
|
+
proxy: '',
|
|
17
|
+
defaultDownloadDir: '',
|
|
18
|
+
checkpointSeconds: 30,
|
|
19
|
+
autoResumeOnSessionReopen: true,
|
|
20
|
+
autoResumeOnHostRestart: true,
|
|
21
|
+
keepCompletedRecords: 200,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Use the host logger when one is available so INFO lines never land on stdout.
|
|
26
|
+
* @param {any} ctx
|
|
27
|
+
*/
|
|
28
|
+
function makeLog(ctx) {
|
|
29
|
+
try {
|
|
30
|
+
const factory = ctx?.logger
|
|
31
|
+
if (typeof factory === 'function') {
|
|
32
|
+
const logger = factory('dsh-dl-server')
|
|
33
|
+
if (logger && typeof logger.info === 'function') {
|
|
34
|
+
return (level, message) => {
|
|
35
|
+
const fn = logger[level] ?? logger.info
|
|
36
|
+
fn.call(logger, message)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
/* fall through to the console fallback */
|
|
42
|
+
}
|
|
43
|
+
return (level, message) => {
|
|
44
|
+
// DLS_DEBUG=1 surfaces INFO lines on stderr; useful when no host logger is wired.
|
|
45
|
+
if (process.env.DLS_DEBUG === '1' || level === 'warn' || level === 'error') {
|
|
46
|
+
console.error(`[dsh-dl-server] ${message}`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {import('@deepseek-ai/cordis').Context} ctx
|
|
53
|
+
* @param {Partial<typeof DEFAULTS>} [rawConfig]
|
|
54
|
+
*/
|
|
55
|
+
export async function apply(ctx, rawConfig) {
|
|
56
|
+
mark('apply enter')
|
|
57
|
+
|
|
58
|
+
const config = { ...DEFAULTS, ...(rawConfig ?? {}) }
|
|
59
|
+
const log = makeLog(ctx)
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
mark('opening domain')
|
|
63
|
+
const domain = await ctx.storageDomain.open(JobDomain)
|
|
64
|
+
mark('domain open')
|
|
65
|
+
|
|
66
|
+
const transport = new Transport(config, log)
|
|
67
|
+
await transport.init()
|
|
68
|
+
mark('transport ready', `mode=${transport.mode}`)
|
|
69
|
+
|
|
70
|
+
const queue = new Queue({ domain, transport, config, log })
|
|
71
|
+
await queue.recover()
|
|
72
|
+
mark('queue ready')
|
|
73
|
+
|
|
74
|
+
registerTools({ ctx, queue, config })
|
|
75
|
+
// The tools themselves register inside the deferred ctx.inject callback;
|
|
76
|
+
// this mark only records that the subscription was installed.
|
|
77
|
+
mark('registerTools dispatched')
|
|
78
|
+
|
|
79
|
+
ctx.effect(() => async () => {
|
|
80
|
+
await queue.close()
|
|
81
|
+
await transport.dispose()
|
|
82
|
+
await domain.close()
|
|
83
|
+
})
|
|
84
|
+
mark('effect installed')
|
|
85
|
+
|
|
86
|
+
log('info', `ready (transport: ${transport.mode}, concurrency: ${config.maxConcurrentJobs})`)
|
|
87
|
+
mark('apply done')
|
|
88
|
+
} catch (error) {
|
|
89
|
+
markFailure('apply', error)
|
|
90
|
+
throw error
|
|
91
|
+
}
|
|
92
|
+
}
|