@xmoon76/dsh-pi-tui 0.1.8 → 0.2.0
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/config/agent-presets/code/agent.cordis.yml +6 -5
- package/config/agent-presets/cordis/agent.cordis.yml +6 -5
- package/config/agent-presets/cordis/skills/editing-cordis-compositions/SKILL.md +3 -3
- package/config/agent-presets/minimal/agent.cordis.yml +27 -1
- package/config/agent-presets/standard/agent.cordis.yml +6 -5
- package/cordis.patch.yml +14 -1
- package/dist/builtins.d.mts +18 -0
- package/dist/builtins.mjs +75 -0
- package/dist/extension/advanced.d.mts +372 -0
- package/dist/extension/advanced.mjs +46 -0
- package/dist/extension/unstable.d.mts +206 -0
- package/dist/extension/unstable.mjs +30 -0
- package/dist/extensions.d.mts +132 -0
- package/dist/extensions.mjs +3 -0
- package/dist/index.d.mts +63 -3
- package/dist/index.mjs +3 -22739
- package/dist/public-types-2omeXG8d.d.mts +740 -0
- package/dist/src-AgdnRnGA.mjs +30851 -0
- package/dist/theme-registry-CjbtRmhd.d.mts +1 -0
- package/package.json +81 -55
- package/scripts/advanced-plugin-smoke.mjs +314 -0
- package/scripts/bench.mts +40 -5
- package/scripts/examples-plugin-smoke.mjs +320 -0
- package/scripts/extension-fixture-smoke.mjs +219 -0
- package/scripts/phase4-plugin-smoke.mjs +318 -0
- package/scripts/tarball-smoke.mjs +69 -7
- package/scripts/unstable-plugin-smoke.mjs +300 -0
- package/scripts/vim-plugin-smoke.mjs +315 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmoon76/dsh-pi-tui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The dsh-pi-tui bundle: a TUI surface for DeepSeek Harness profiles (dsh --profile pi-tui)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "XMoon <xmoonox@gmail.com>",
|
|
@@ -33,6 +33,22 @@
|
|
|
33
33
|
"types": "./dist/startup.d.mts",
|
|
34
34
|
"default": "./dist/startup.mjs"
|
|
35
35
|
},
|
|
36
|
+
"./extensions": {
|
|
37
|
+
"types": "./dist/extensions.d.mts",
|
|
38
|
+
"default": "./dist/extensions.mjs"
|
|
39
|
+
},
|
|
40
|
+
"./extensions/advanced": {
|
|
41
|
+
"types": "./dist/extension/advanced.d.mts",
|
|
42
|
+
"default": "./dist/extension/advanced.mjs"
|
|
43
|
+
},
|
|
44
|
+
"./extensions/unstable": {
|
|
45
|
+
"types": "./dist/extension/unstable.d.mts",
|
|
46
|
+
"default": "./dist/extension/unstable.mjs"
|
|
47
|
+
},
|
|
48
|
+
"./builtins": {
|
|
49
|
+
"types": "./dist/builtins.d.mts",
|
|
50
|
+
"default": "./dist/builtins.mjs"
|
|
51
|
+
},
|
|
36
52
|
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
37
53
|
"./package.json": "./package.json"
|
|
38
54
|
},
|
|
@@ -61,67 +77,71 @@
|
|
|
61
77
|
"peerDependencies": {
|
|
62
78
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
63
79
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.2",
|
|
64
|
-
"@deepseek-ai/dsh-agent": "^0.1.0-rc.
|
|
65
|
-
"@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.
|
|
66
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.
|
|
67
|
-
"@deepseek-ai/dsh-cmdline": "^0.1.0-rc.
|
|
68
|
-
"@deepseek-ai/dsh-commands": "^0.1.0-rc.
|
|
69
|
-
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.
|
|
70
|
-
"@deepseek-ai/dsh-goal": "^0.1.0-rc.
|
|
71
|
-
"@deepseek-ai/dsh-jobs": "^0.1.0-rc.
|
|
72
|
-
"@deepseek-ai/dsh-llm": "^0.1.0-rc.
|
|
73
|
-
"@deepseek-ai/dsh-llm-retry": "^0.1.0-rc.
|
|
74
|
-
"@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.
|
|
75
|
-
"@deepseek-ai/dsh-plan-mode": "^0.1.0-rc.
|
|
76
|
-
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.
|
|
77
|
-
"@deepseek-ai/dsh-session": "^0.1.0-rc.
|
|
78
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.
|
|
79
|
-
"@deepseek-ai/dsh-session-title": "^0.1.0-rc.
|
|
80
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.
|
|
81
|
-
"@deepseek-ai/dsh-shell": "^0.1.0-rc.
|
|
82
|
-
"@deepseek-ai/dsh-skill": "^0.1.0-rc.
|
|
83
|
-
"@deepseek-ai/dsh-subagent": "^0.1.0-rc.
|
|
84
|
-
"@deepseek-ai/dsh-token-meter": "^0.1.0-rc.
|
|
85
|
-
"@deepseek-ai/dsh-tool-workflow": "^0.1.0-rc.
|
|
86
|
-
"@deepseek-ai/dsh-tools": "^0.1.0-rc.
|
|
87
|
-
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.
|
|
88
|
-
"@deepseek-ai/dsh-user-questions": "^0.1.0-rc.
|
|
80
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
|
|
81
|
+
"@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.8",
|
|
82
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.8",
|
|
83
|
+
"@deepseek-ai/dsh-cmdline": "^0.1.0-rc.8",
|
|
84
|
+
"@deepseek-ai/dsh-commands": "^0.1.0-rc.8",
|
|
85
|
+
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
|
|
86
|
+
"@deepseek-ai/dsh-goal": "^0.1.0-rc.8",
|
|
87
|
+
"@deepseek-ai/dsh-jobs": "^0.1.0-rc.8",
|
|
88
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
|
|
89
|
+
"@deepseek-ai/dsh-llm-retry": "^0.1.0-rc.8",
|
|
90
|
+
"@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.8",
|
|
91
|
+
"@deepseek-ai/dsh-plan-mode": "^0.1.0-rc.8",
|
|
92
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.8",
|
|
93
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
|
|
94
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.8",
|
|
95
|
+
"@deepseek-ai/dsh-session-title": "^0.1.0-rc.8",
|
|
96
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
|
|
97
|
+
"@deepseek-ai/dsh-shell": "^0.1.0-rc.8",
|
|
98
|
+
"@deepseek-ai/dsh-skill": "^0.1.0-rc.8",
|
|
99
|
+
"@deepseek-ai/dsh-subagent": "^0.1.0-rc.8",
|
|
100
|
+
"@deepseek-ai/dsh-token-meter": "^0.1.0-rc.8",
|
|
101
|
+
"@deepseek-ai/dsh-tool-workflow": "^0.1.0-rc.8",
|
|
102
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
|
|
103
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.8",
|
|
104
|
+
"@deepseek-ai/dsh-user-questions": "^0.1.0-rc.8",
|
|
89
105
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
90
106
|
},
|
|
91
107
|
"devDependencies": {
|
|
92
108
|
"@xmoon76/pi-tui": "^0.84.3",
|
|
93
109
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.2",
|
|
94
|
-
"@deepseek-ai/dsh-agent": "^0.1.0-rc.
|
|
95
|
-
"@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.
|
|
96
|
-
"@deepseek-ai/dsh-llm": "^0.1.0-rc.
|
|
97
|
-
"@deepseek-ai/dsh-session": "^0.1.0-rc.
|
|
98
|
-
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.
|
|
110
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
|
|
111
|
+
"@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.8",
|
|
112
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
|
|
113
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.8",
|
|
114
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.8",
|
|
99
115
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
100
116
|
"@xterm/headless": "5.5.0",
|
|
101
117
|
"@types/node": "^22.0.0",
|
|
102
118
|
"typescript": "^5.9.0",
|
|
103
119
|
"tsdown": "0.22.0",
|
|
104
|
-
"@deepseek-ai/dsh-commands": "^0.1.0-rc.
|
|
105
|
-
"@deepseek-ai/dsh-token-meter": "^0.1.0-rc.
|
|
106
|
-
"@deepseek-ai/dsh-skill": "^0.1.0-rc.
|
|
107
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.
|
|
108
|
-
"@deepseek-ai/dsh-plan-mode": "^0.1.0-rc.
|
|
109
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.
|
|
110
|
-
"@deepseek-ai/dsh-user-questions": "^0.1.0-rc.
|
|
111
|
-
"@deepseek-ai/dsh-subagent": "^0.1.0-rc.
|
|
112
|
-
"@deepseek-ai/dsh-goal": "^0.1.0-rc.
|
|
113
|
-
"@deepseek-ai/dsh-tools": "^0.1.0-rc.
|
|
114
|
-
"@deepseek-ai/dsh-tool-workflow": "^0.1.0-rc.
|
|
115
|
-
"@deepseek-ai/dsh-llm-retry": "^0.1.0-rc.
|
|
116
|
-
"@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.
|
|
117
|
-
"@deepseek-ai/dsh-session-title": "^0.1.0-rc.
|
|
118
|
-
"@deepseek-ai/dsh-jobs": "^0.1.0-rc.
|
|
119
|
-
"@deepseek-ai/dsh-shell": "^0.1.0-rc.
|
|
120
|
-
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.
|
|
121
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.
|
|
122
|
-
"@deepseek-ai/dsh-agent-loop": "^0.1.0-rc.
|
|
123
|
-
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.
|
|
124
|
-
"@deepseek-ai/dsh-scope": "^0.1.0-rc.
|
|
120
|
+
"@deepseek-ai/dsh-commands": "^0.1.0-rc.8",
|
|
121
|
+
"@deepseek-ai/dsh-token-meter": "^0.1.0-rc.8",
|
|
122
|
+
"@deepseek-ai/dsh-skill": "^0.1.0-rc.8",
|
|
123
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
|
|
124
|
+
"@deepseek-ai/dsh-plan-mode": "^0.1.0-rc.8",
|
|
125
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.8",
|
|
126
|
+
"@deepseek-ai/dsh-user-questions": "^0.1.0-rc.8",
|
|
127
|
+
"@deepseek-ai/dsh-subagent": "^0.1.0-rc.8",
|
|
128
|
+
"@deepseek-ai/dsh-goal": "^0.1.0-rc.8",
|
|
129
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
|
|
130
|
+
"@deepseek-ai/dsh-tool-workflow": "^0.1.0-rc.8",
|
|
131
|
+
"@deepseek-ai/dsh-llm-retry": "^0.1.0-rc.8",
|
|
132
|
+
"@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.8",
|
|
133
|
+
"@deepseek-ai/dsh-session-title": "^0.1.0-rc.8",
|
|
134
|
+
"@deepseek-ai/dsh-jobs": "^0.1.0-rc.8",
|
|
135
|
+
"@deepseek-ai/dsh-shell": "^0.1.0-rc.8",
|
|
136
|
+
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
|
|
137
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.8",
|
|
138
|
+
"@deepseek-ai/dsh-agent-loop": "^0.1.0-rc.8",
|
|
139
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
|
|
140
|
+
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
|
|
141
|
+
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.8",
|
|
142
|
+
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
|
|
143
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
|
|
144
|
+
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.8"
|
|
125
145
|
},
|
|
126
146
|
"engines": {
|
|
127
147
|
"node": ">=22.19.0"
|
|
@@ -129,9 +149,15 @@
|
|
|
129
149
|
"scripts": {
|
|
130
150
|
"build": "tsdown",
|
|
131
151
|
"build:package": "pnpm --dir ../pi-tui build && pnpm build",
|
|
132
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
133
|
-
"test": "node --test test/*.test.ts test/*.test.mjs",
|
|
152
|
+
"typecheck": "pnpm build:package && tsc -p tsconfig.json --noEmit",
|
|
153
|
+
"test": "pnpm build:package && node --test test/*.test.ts test/*.test.mjs",
|
|
134
154
|
"clean": "rm -rf dist",
|
|
135
|
-
"smoke": "node scripts/tarball-smoke.mjs"
|
|
155
|
+
"smoke": "node scripts/tarball-smoke.mjs",
|
|
156
|
+
"smoke:fixture": "node scripts/extension-fixture-smoke.mjs",
|
|
157
|
+
"smoke:advanced": "node scripts/advanced-plugin-smoke.mjs",
|
|
158
|
+
"smoke:phase4": "node scripts/phase4-plugin-smoke.mjs",
|
|
159
|
+
"smoke:unstable": "node scripts/unstable-plugin-smoke.mjs",
|
|
160
|
+
"smoke:examples": "node scripts/examples-plugin-smoke.mjs",
|
|
161
|
+
"smoke:vim": "node scripts/vim-plugin-smoke.mjs"
|
|
136
162
|
}
|
|
137
163
|
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* advanced-plugin-smoke — verify the PACKED ADVANCED extension surface end
|
|
4
|
+
* to end with a real third-party-shaped plugin (Phase 2, plan §15):
|
|
5
|
+
*
|
|
6
|
+
* 1. the fixture imports ONLY the public `@xmoon76/dsh-pi-tui/
|
|
7
|
+
* extensions/advanced` (and the stable `extensions` entry for the
|
|
8
|
+
* service type) subpaths — importing `@xmoon76/pi-tui`, `src/tui-app`
|
|
9
|
+
* or any repository-relative internal path FAILS the gate (static
|
|
10
|
+
* imports, dynamic import() AND require() are all scanned);
|
|
11
|
+
* 2. the fixture typechecks against the packed `.d.mts`;
|
|
12
|
+
* 3. the fixture COMPILES to JS and RUNTIME-LOADS against the packed
|
|
13
|
+
* bytes (apply() registers through a mock service without throwing);
|
|
14
|
+
* 4. the fixture exercises the ADVANCED surface: ADVANCED_API_LEVEL === 1,
|
|
15
|
+
* normalized input capture (observe + capture), the interactive
|
|
16
|
+
* overlay lease, and the advanced editor controls;
|
|
17
|
+
* 5. the fixture's module shape (name/inject/apply) matches the Loader;
|
|
18
|
+
* 6. no duplicate dsh runtime in the fixture tree.
|
|
19
|
+
*
|
|
20
|
+
* The probe mirrors the host install with SYMLINKS (no npm install — the
|
|
21
|
+
* gate is offline-safe; the packed package's runtime deps resolve from
|
|
22
|
+
* the repo's node_modules exactly like a real profile).
|
|
23
|
+
*
|
|
24
|
+
* Usage: node scripts/advanced-plugin-smoke.mjs [path-to-tgz]
|
|
25
|
+
* @module advanced-plugin-smoke
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { spawnSync } from 'node:child_process'
|
|
29
|
+
import {
|
|
30
|
+
existsSync,
|
|
31
|
+
mkdirSync,
|
|
32
|
+
mkdtempSync,
|
|
33
|
+
readdirSync,
|
|
34
|
+
readFileSync,
|
|
35
|
+
realpathSync,
|
|
36
|
+
rmSync,
|
|
37
|
+
statSync,
|
|
38
|
+
lstatSync,
|
|
39
|
+
symlinkSync,
|
|
40
|
+
writeFileSync,
|
|
41
|
+
} from 'node:fs'
|
|
42
|
+
import { tmpdir } from 'node:os'
|
|
43
|
+
import { dirname, join, resolve } from 'node:path'
|
|
44
|
+
import { fileURLToPath } from 'node:url'
|
|
45
|
+
|
|
46
|
+
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url))
|
|
47
|
+
const PACKAGE_ROOT = join(SCRIPT_DIR, '..')
|
|
48
|
+
const FIXTURE_ROOT = join(PACKAGE_ROOT, 'test', 'fixtures', 'advanced-plugin')
|
|
49
|
+
|
|
50
|
+
const failures = []
|
|
51
|
+
const checks = []
|
|
52
|
+
|
|
53
|
+
function check(name, ok, detail = '') {
|
|
54
|
+
checks.push(`${ok ? 'ok ' : 'FAIL'} ${name}${detail === '' ? '' : ` — ${detail}`}`)
|
|
55
|
+
if (!ok) failures.push(`${name}${detail === '' ? '' : `: ${detail}`}`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function run(command, args, options = {}) {
|
|
59
|
+
return spawnSync(command, args, { encoding: 'utf8', ...options })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveTarball(explicit) {
|
|
63
|
+
if (explicit !== undefined) {
|
|
64
|
+
const absolute = resolve(explicit)
|
|
65
|
+
if (!existsSync(absolute)) throw new Error(`tarball not found: ${explicit}`)
|
|
66
|
+
return absolute
|
|
67
|
+
}
|
|
68
|
+
const dir = PACKAGE_ROOT
|
|
69
|
+
const candidates = readdirSync(dir)
|
|
70
|
+
.filter(name => /xmoon76-dsh-pi-tui-.*\.tgz$/.test(name))
|
|
71
|
+
.map(name => join(dir, name))
|
|
72
|
+
.sort((a, b) => statSync(b).mtimeMs - statSync(a).mtimeMs)
|
|
73
|
+
if (candidates.length === 0) {
|
|
74
|
+
throw new Error(`no xmoon76-dsh-pi-tui-*.tgz in ${dir}; run pnpm pack:release first`)
|
|
75
|
+
}
|
|
76
|
+
return candidates[0]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function main() {
|
|
80
|
+
const tarball = resolveTarball(process.argv[2])
|
|
81
|
+
const workDir = mkdtempSync(join(tmpdir(), 'advanced-plugin-'))
|
|
82
|
+
try {
|
|
83
|
+
// 1. Extract the packed package.
|
|
84
|
+
const extractedDir = join(workDir, 'pkg')
|
|
85
|
+
mkdirSync(extractedDir)
|
|
86
|
+
const extract = run('tar', ['-xzf', tarball, '-C', extractedDir])
|
|
87
|
+
check('tarball extracts', extract.status === 0, extract.stderr)
|
|
88
|
+
const pkgDir = join(extractedDir, 'package')
|
|
89
|
+
check('packed package has dist/extension/advanced.mjs', existsSync(join(pkgDir, 'dist', 'extension', 'advanced.mjs')))
|
|
90
|
+
check('packed package has dist/extension/advanced.d.mts', existsSync(join(pkgDir, 'dist', 'extension', 'advanced.d.mts')))
|
|
91
|
+
|
|
92
|
+
// 2. IMPORT BAN (plan §15 CI gate): the fixture's IMPORT STATEMENTS
|
|
93
|
+
// (multi-line blocks), dynamic import() and require() specifiers
|
|
94
|
+
// must never reference private internals.
|
|
95
|
+
const fixtureSrc = readFileSync(join(FIXTURE_ROOT, 'src', 'index.ts'), 'utf8')
|
|
96
|
+
const lines = fixtureSrc.split('\n')
|
|
97
|
+
const importStatements = []
|
|
98
|
+
for (let index = 0; index < lines.length; index++) {
|
|
99
|
+
if (/^\s*import\b/.test(lines[index] ?? '')) {
|
|
100
|
+
let statement = lines[index] ?? ''
|
|
101
|
+
while (!statement.includes(';') && index + 1 < lines.length) {
|
|
102
|
+
index += 1
|
|
103
|
+
statement += lines[index]
|
|
104
|
+
}
|
|
105
|
+
importStatements.push(statement)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const importLines = importStatements.join('\n')
|
|
109
|
+
const dynamicSpecifiers = [...fixtureSrc.matchAll(/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g)]
|
|
110
|
+
.map(match => match[1])
|
|
111
|
+
.concat([...fixtureSrc.matchAll(/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g)]
|
|
112
|
+
.map(match => match[1]))
|
|
113
|
+
const banned = [
|
|
114
|
+
// Word-boundary: `@xmoon76/pi-tui` must NOT match
|
|
115
|
+
// `@xmoon76/dsh-pi-tui` (a substring of the public package name).
|
|
116
|
+
['@xmoon76/pi-tui (the private fork, word-bounded)', /@xmoon76\/pi-tui(?![-a-z])/g],
|
|
117
|
+
['@moonshot-ai/pi-tui', /@moonshot-ai\/pi-tui/g],
|
|
118
|
+
['src/tui-app', /src\/tui-app/g],
|
|
119
|
+
['TuiApp', /\bTuiApp\b/g],
|
|
120
|
+
['TuiMainScreen', /\bTuiMainScreen\b/g],
|
|
121
|
+
['TuiAltScreen', /\bTuiAltScreen\b/g],
|
|
122
|
+
['repository-relative internal path', /\.\.\/\.\.\/src/g],
|
|
123
|
+
]
|
|
124
|
+
for (const [name, pattern] of banned) {
|
|
125
|
+
const staticMatches = importLines.match(pattern) ?? []
|
|
126
|
+
const dynamicMatches = dynamicSpecifiers.filter(specifier => pattern.test(specifier))
|
|
127
|
+
check(`fixture does not import ${name}`, staticMatches.length === 0 && dynamicMatches.length === 0,
|
|
128
|
+
[...staticMatches, ...dynamicMatches].join(', '))
|
|
129
|
+
}
|
|
130
|
+
// The ONLY @xmoon76/dsh-pi-tui imports are the public subpaths
|
|
131
|
+
// (extensions/advanced + extensions for the service type; cordis is a
|
|
132
|
+
// legitimate peer for the plugin's apply signature).
|
|
133
|
+
const dshImports = importLines.match(/@xmoon76\/dsh-pi-tui[^'"]*/g) ?? []
|
|
134
|
+
check('fixture imports ONLY the public extensions subpaths',
|
|
135
|
+
dshImports.length === 2
|
|
136
|
+
&& dshImports.includes('@xmoon76/dsh-pi-tui/extensions/advanced')
|
|
137
|
+
&& dshImports.includes('@xmoon76/dsh-pi-tui/extensions'),
|
|
138
|
+
dshImports.join(', '))
|
|
139
|
+
|
|
140
|
+
// 3. ADVANCED-SURFACE EXERCISE (plan §15 acceptance): the fixture
|
|
141
|
+
// exercises the Phase-2 advanced surface through the public entry.
|
|
142
|
+
const surface = [
|
|
143
|
+
['ADVANCED_API_LEVEL', 'tier level constant'],
|
|
144
|
+
['advanced(', 'facade builder'],
|
|
145
|
+
['ui.input.capture', 'normalized input capture'],
|
|
146
|
+
["mode: 'observe'", 'observe mode'],
|
|
147
|
+
["mode: 'capture'", 'capture mode'],
|
|
148
|
+
['showInteractiveOverlay', 'interactive overlay'],
|
|
149
|
+
['AdvancedInteractiveComponent', 'interactive component type'],
|
|
150
|
+
['ui.editor', 'advanced editor controls'],
|
|
151
|
+
]
|
|
152
|
+
for (const [api, label] of surface) {
|
|
153
|
+
check(`fixture exercises ${label} (${api})`, fixtureSrc.includes(api))
|
|
154
|
+
}
|
|
155
|
+
check('fixture never parses raw terminal bytes (no CSI-u / escape matching in the plugin)',
|
|
156
|
+
!/\\x1b\[/.test(fixtureSrc) && !/escape\s*\(/.test(fixtureSrc.replace(/handleInput/g, '')))
|
|
157
|
+
|
|
158
|
+
// 4. The fixture tree: symlink the packed package + the host's
|
|
159
|
+
// node_modules (the offline-safe mirror of a real profile install).
|
|
160
|
+
const fixtureDir = join(workDir, 'fixture')
|
|
161
|
+
mkdirSync(join(fixtureDir, 'node_modules', '@xmoon76'), { recursive: true })
|
|
162
|
+
symlinkSync(pkgDir, join(fixtureDir, 'node_modules', '@xmoon76', 'dsh-pi-tui'), 'dir')
|
|
163
|
+
const hostModules = join(PACKAGE_ROOT, 'node_modules')
|
|
164
|
+
mkdirSync(join(pkgDir, 'node_modules'), { recursive: true })
|
|
165
|
+
for (const entry of readdirSync(hostModules)) {
|
|
166
|
+
const target = join(pkgDir, 'node_modules', entry)
|
|
167
|
+
if (existsSync(target)) continue
|
|
168
|
+
symlinkSync(join(hostModules, entry), target, 'dir')
|
|
169
|
+
}
|
|
170
|
+
mkdirSync(join(fixtureDir, 'node_modules'), { recursive: true })
|
|
171
|
+
for (const entry of readdirSync(hostModules)) {
|
|
172
|
+
const target = join(fixtureDir, 'node_modules', entry)
|
|
173
|
+
if (existsSync(target)) continue
|
|
174
|
+
symlinkSync(join(hostModules, entry), target, 'dir')
|
|
175
|
+
}
|
|
176
|
+
mkdirSync(join(fixtureDir, 'src'), { recursive: true })
|
|
177
|
+
writeFileSync(join(fixtureDir, 'src', 'index.ts'), fixtureSrc)
|
|
178
|
+
writeFileSync(join(fixtureDir, 'tsconfig.json'), JSON.stringify({
|
|
179
|
+
compilerOptions: {
|
|
180
|
+
target: 'ES2022',
|
|
181
|
+
module: 'NodeNext',
|
|
182
|
+
moduleResolution: 'NodeNext',
|
|
183
|
+
strict: true,
|
|
184
|
+
noEmit: true,
|
|
185
|
+
skipLibCheck: true,
|
|
186
|
+
types: [],
|
|
187
|
+
},
|
|
188
|
+
include: ['src'],
|
|
189
|
+
}, null, 2))
|
|
190
|
+
writeFileSync(join(fixtureDir, 'package.json'), JSON.stringify({
|
|
191
|
+
name: 'dsh-pi-advanced-fixture',
|
|
192
|
+
private: true,
|
|
193
|
+
type: 'module',
|
|
194
|
+
}, null, 2))
|
|
195
|
+
|
|
196
|
+
// 5. Typecheck against the packed .d.mts (the repo's tsc — no
|
|
197
|
+
// registry fetch).
|
|
198
|
+
const tsc = run('node', [join(PACKAGE_ROOT, 'node_modules', 'typescript', 'bin', 'tsc'), '-p', join(fixtureDir, 'tsconfig.json')], { cwd: fixtureDir })
|
|
199
|
+
check('fixture typechecks against the packed .d.mts', tsc.status === 0,
|
|
200
|
+
tsc.status === 0 ? '' : (tsc.stdout + tsc.stderr).split('\n').slice(-6).join(' '))
|
|
201
|
+
|
|
202
|
+
// 6. Module shape.
|
|
203
|
+
const moduleShape = /export const name = ['"][^'"]+['"]/s.test(fixtureSrc)
|
|
204
|
+
&& /export const inject = \[/s.test(fixtureSrc)
|
|
205
|
+
&& /export function apply\(ctx: Context\)/s.test(fixtureSrc)
|
|
206
|
+
check('fixture module shape (name/inject/apply) matches the Loader', moduleShape)
|
|
207
|
+
|
|
208
|
+
// 7. RUNTIME LOAD: compile the fixture to JS and apply() it with a
|
|
209
|
+
// mock service — the registrations must not throw and must resolve
|
|
210
|
+
// the PACKED advanced module.
|
|
211
|
+
const emitDir = join(fixtureDir, 'dist-out')
|
|
212
|
+
const emit = run('node', [join(PACKAGE_ROOT, 'node_modules', 'typescript', 'bin', 'tsc'),
|
|
213
|
+
'-p', join(fixtureDir, 'tsconfig.json'), '--noEmit', 'false', '--outDir', emitDir], { cwd: fixtureDir })
|
|
214
|
+
check('fixture compiles to JS for the runtime probe', emit.status === 0,
|
|
215
|
+
emit.status === 0 ? '' : (emit.stdout + emit.stderr).split('\n').slice(-4).join(' '))
|
|
216
|
+
if (emit.status === 0) {
|
|
217
|
+
writeFileSync(join(fixtureDir, 'runtime-probe.mjs'), `
|
|
218
|
+
const context = {
|
|
219
|
+
get: (name) => name === 'piTuiExtensions' ? mockService() : undefined,
|
|
220
|
+
on: () => {},
|
|
221
|
+
}
|
|
222
|
+
function mockService() {
|
|
223
|
+
const inertLease = {
|
|
224
|
+
id: 'inert', active: false, focused: false,
|
|
225
|
+
focus: () => {}, blur: () => {}, invalidate: () => {},
|
|
226
|
+
close: () => {}, hide: () => {}, show: () => {},
|
|
227
|
+
}
|
|
228
|
+
const inertControls = {
|
|
229
|
+
getEditorState: () => ({ text: '', cursor: 0, focused: false, composing: false }),
|
|
230
|
+
setEditorText: () => {}, setEditorCursor: () => {},
|
|
231
|
+
insertEditorText: () => {}, pasteToEditor: () => {}, requestEditorFocus: () => {},
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
api: () => ({
|
|
235
|
+
apiVersion: 1,
|
|
236
|
+
hostVersion: 'test',
|
|
237
|
+
capabilities: new Set(['advanced.input.capture', 'advanced.ui.interactive', 'advanced.editor.control']),
|
|
238
|
+
deprecations: new Map(),
|
|
239
|
+
}),
|
|
240
|
+
_advancedCaptureInput: (spec) => ({ id: spec.id, dispose: () => {} }),
|
|
241
|
+
_advancedShowInteractiveOverlay: () => inertLease,
|
|
242
|
+
_advancedEditorControls: () => inertControls,
|
|
243
|
+
_advancedUiSelect: () => Promise.resolve(undefined),
|
|
244
|
+
_advancedUiConfirm: () => Promise.resolve(false),
|
|
245
|
+
_advancedUiInput: () => Promise.resolve(undefined),
|
|
246
|
+
_advancedUiNotify: () => {},
|
|
247
|
+
_advancedUiCustom: () => Promise.resolve(undefined),
|
|
248
|
+
_advancedHostState: () => ({
|
|
249
|
+
getTheme: () => 'dark',
|
|
250
|
+
setTheme: () => {},
|
|
251
|
+
setTitle: () => {},
|
|
252
|
+
setWorkingMessage: () => {},
|
|
253
|
+
setToolsExpanded: () => {},
|
|
254
|
+
}),
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const plugin = await import(${JSON.stringify(join(emitDir, 'index.js').replaceAll('\\', '/'))})
|
|
258
|
+
plugin.apply(context)
|
|
259
|
+
if (plugin.name !== 'dsh-pi-advanced-fixture') throw new Error('bad name: ' + plugin.name)
|
|
260
|
+
console.log('fixture-runtime-ok')
|
|
261
|
+
`)
|
|
262
|
+
const runtime = run(process.execPath, [join(fixtureDir, 'runtime-probe.mjs')], { cwd: fixtureDir })
|
|
263
|
+
check('fixture runtime-loads and apply() registers without throwing',
|
|
264
|
+
runtime.status === 0 && runtime.stdout.includes('fixture-runtime-ok'),
|
|
265
|
+
runtime.status === 0 ? '' : (runtime.stderr || runtime.stdout).split('\n').slice(-8).join(' '))
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// 8. No duplicate dsh runtime: every @deepseek-ai entry in BOTH the
|
|
269
|
+
// packed package tree AND the fixture tree must be a SYMLINK whose
|
|
270
|
+
// realpath resolves into the host module tree (the packed tarball
|
|
271
|
+
// itself carries no node_modules — the scaffolding symlinks them,
|
|
272
|
+
// exactly like a real profile resolves in-box packages from the
|
|
273
|
+
// dsh installation). A REAL directory is a duplicate runtime copy.
|
|
274
|
+
const probeReal = resolve(workDir)
|
|
275
|
+
const auditTrees = [
|
|
276
|
+
['packed package', join(pkgDir, 'node_modules', '@deepseek-ai')],
|
|
277
|
+
['fixture tree', join(fixtureDir, 'node_modules', '@deepseek-ai')],
|
|
278
|
+
]
|
|
279
|
+
const realDirs = []
|
|
280
|
+
for (const [label, nm] of auditTrees) {
|
|
281
|
+
const entries = existsSync(nm) ? readdirSync(nm) : []
|
|
282
|
+
for (const name of entries) {
|
|
283
|
+
const entry = join(nm, name)
|
|
284
|
+
let isSymlink = false
|
|
285
|
+
try { isSymlink = lstatSync(entry).isSymbolicLink() } catch { /* missing */ }
|
|
286
|
+
if (!isSymlink) {
|
|
287
|
+
realDirs.push(`${label}:${name} (real dir)`)
|
|
288
|
+
continue
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
const target = resolve(realpathSync(entry))
|
|
292
|
+
if (target.startsWith(probeReal)) realDirs.push(`${label}:${name}->${target}`)
|
|
293
|
+
} catch {
|
|
294
|
+
realDirs.push(`${label}:${name} (dangling)`)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
check('no duplicate @deepseek-ai dsh-runtime copy (symlinks only, both trees)',
|
|
299
|
+
realDirs.length === 0,
|
|
300
|
+
realDirs.join(', '))
|
|
301
|
+
} finally {
|
|
302
|
+
if (process.env.ADVANCED_SMOKE_KEEP !== '1') rmSync(workDir, { recursive: true, force: true })
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
for (const line of checks) console.log(line)
|
|
306
|
+
if (failures.length > 0) {
|
|
307
|
+
console.error(`\nadvanced-plugin smoke FAILED (${failures.length}):`)
|
|
308
|
+
for (const failure of failures) console.error(` - ${failure}`)
|
|
309
|
+
process.exit(1)
|
|
310
|
+
}
|
|
311
|
+
console.log('\nadvanced-plugin smoke passed')
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
main()
|
package/scripts/bench.mts
CHANGED
|
@@ -167,7 +167,7 @@ const HEAP_REBUILDS = FAST ? 300 : 2000
|
|
|
167
167
|
|
|
168
168
|
// --- the benchmark ----------------------------------------------------------
|
|
169
169
|
|
|
170
|
-
function main(): void {
|
|
170
|
+
async function main(): Promise<void> {
|
|
171
171
|
const rows: string[] = []
|
|
172
172
|
const row = (label: string, value: string): void => { rows.push(`${label.padEnd(58)} ${value}`) }
|
|
173
173
|
|
|
@@ -250,12 +250,47 @@ function main(): void {
|
|
|
250
250
|
row('heap (rerun with --expose-gc)', 'skipped')
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
// 5. extension plugin overhead (M11, plan §23): the frame cost with N
|
|
254
|
+
// widget/list contributions — no-plugin baseline vs 10 vs 50 — and a
|
|
255
|
+
// streaming-invalidation case (the batcher must coalesce bursts).
|
|
256
|
+
const { ExtensionLedger } = await import('../src/extension/internal/ledger.ts')
|
|
257
|
+
const { SurfaceHost } = await import('../src/extension/internal/surface-host.ts')
|
|
258
|
+
const { WidgetOutlet } = await import('../src/extension/internal/widget-outlet.ts')
|
|
259
|
+
for (const count of [0, 10, 50]) {
|
|
260
|
+
const ledger = new ExtensionLedger()
|
|
261
|
+
const host = new SurfaceHost(ledger, () => {})
|
|
262
|
+
let renders = 0
|
|
263
|
+
const outlet = new WidgetOutlet(ledger, { requestRender: () => { renders += 1 } }, 'input.widget.below')
|
|
264
|
+
for (let index = 0; index < count; index++) {
|
|
265
|
+
ledger.register('input.widget.below', { id: `w${index}`, order: index }, {
|
|
266
|
+
view: { kind: 'text', spans: [{ text: `widget ${index}` }] },
|
|
267
|
+
importance: index,
|
|
268
|
+
maxHeight: 1,
|
|
269
|
+
}, `owner-${index}`)
|
|
270
|
+
}
|
|
271
|
+
const t0 = process.hrtime.bigint()
|
|
272
|
+
for (let frame = 0; frame < 200; frame += 1) outlet.refresh(0, 120, 4)
|
|
273
|
+
const elapsed = Number(process.hrtime.bigint() - t0) / 200
|
|
274
|
+
row(`widget outlet refresh ×${count} contributions (200 frames)`, `${elapsed.toFixed(0)} ns/frame`)
|
|
275
|
+
void host
|
|
276
|
+
}
|
|
277
|
+
// Invalidation coalescing: 100 replaces in one tick → ONE flush.
|
|
278
|
+
const { InvalidateBatcher } = await import('../src/extension/internal/batcher.ts')
|
|
279
|
+
let flushCount = 0
|
|
280
|
+
const batcher = new InvalidateBatcher({ requestRender: () => { flushCount += 1 } })
|
|
281
|
+
for (let index = 0; index < 100; index += 1) batcher.invalidate()
|
|
282
|
+
await Promise.resolve()
|
|
283
|
+
row('invalidation burst coalescing (100 in one tick)', `${flushCount} flush(es)`)
|
|
284
|
+
|
|
253
285
|
console.log(rows.join('\n'))
|
|
254
286
|
}
|
|
255
287
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
288
|
+
// Round-1 finding 2 (fixed in round 2 — the previous attempt silently
|
|
289
|
+
// failed to write): main() is async — dynamic imports and awaited stages
|
|
290
|
+
// reject ASYNCHRONOUSLY. A try/catch cannot observe a rejection; catch
|
|
291
|
+
// the PROMISE explicitly so any async failure exits non-zero (never an
|
|
292
|
+
// unhandled rejection with a zero exit code).
|
|
293
|
+
main().catch((error) => {
|
|
259
294
|
console.error(`bench: ${error instanceof Error ? error.message : String(error)}`)
|
|
260
295
|
process.exit(1)
|
|
261
|
-
}
|
|
296
|
+
})
|