dsh-cliworker-now 0.7.6 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +53 -11
- package/README.md +56 -11
- package/lib/account-models.mjs +49 -18
- package/lib/client.js +1221 -151
- package/lib/client.js.map +1 -1
- package/lib/grok-catalog.mjs +2 -2
- package/lib/hermes-native-entry.py +48 -13
- package/lib/hermes-nous-refresh.py +331 -0
- package/lib/index.js +9469 -6030
- package/lib/index.js.map +1 -1
- package/lib/native-launch.mjs +62 -7
- package/lib/native-spawn.mjs +32 -0
- package/lib/pi-installation.mjs +27 -3
- package/lib/pi-native-catalog.mjs +17 -7
- package/lib/pi-omp-bridge.mjs +2 -2
- package/lib/pi-omp-refresh.mjs +3 -3
- package/lib/private-launch.mjs +6 -2
- package/lib/terminal-bridge.mjs +2 -2
- package/lib/typert.host.js +265 -25
- package/lib/typert.remote-client.d.ts +8 -0
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +241 -25
- package/lib/types/client/account-query-queue.d.ts +16 -0
- package/lib/types/client/account-query-queue.js +61 -0
- package/lib/types/client/account-query-queue.js.map +1 -0
- package/lib/types/client/account-terminal.d.ts +6 -2
- package/lib/types/client/account-terminal.js +260 -31
- package/lib/types/client/account-terminal.js.map +1 -1
- package/lib/types/client/cli-executable-settings.d.ts +13 -0
- package/lib/types/client/cli-executable-settings.js +128 -0
- package/lib/types/client/cli-executable-settings.js.map +1 -0
- package/lib/types/client/management-styles.d.ts +1 -1
- package/lib/types/client/management-styles.js +4 -0
- package/lib/types/client/management-styles.js.map +1 -1
- package/lib/types/client/settings-dialog.js +196 -80
- package/lib/types/client/settings-dialog.js.map +1 -1
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/client/styles.js +18 -16
- package/lib/types/client/styles.js.map +1 -1
- package/lib/types/client/zcode-login-link-provider.d.ts +9 -0
- package/lib/types/client/zcode-login-link-provider.js +105 -0
- package/lib/types/client/zcode-login-link-provider.js.map +1 -0
- package/lib/types/client/zcode-login-link.d.ts +16 -0
- package/lib/types/client/zcode-login-link.js +194 -0
- package/lib/types/client/zcode-login-link.js.map +1 -0
- package/lib/types/host/account-models.d.mts +10 -2
- package/lib/types/host/account-models.mjs +49 -18
- package/lib/types/host/accounts.d.ts +3 -2
- package/lib/types/host/accounts.js +168 -29
- package/lib/types/host/accounts.js.map +1 -1
- package/lib/types/host/adapters.d.ts +3 -1
- package/lib/types/host/adapters.js +138 -11
- package/lib/types/host/adapters.js.map +1 -1
- package/lib/types/host/agy-background-environment.d.ts +2 -0
- package/lib/types/host/agy-background-environment.js +7 -0
- package/lib/types/host/agy-background-environment.js.map +1 -0
- package/lib/types/host/agy-models.d.ts +2 -1
- package/lib/types/host/agy-models.js +6 -6
- package/lib/types/host/agy-models.js.map +1 -1
- package/lib/types/host/agy-windows-account.d.ts +14 -0
- package/lib/types/host/agy-windows-account.js +104 -0
- package/lib/types/host/agy-windows-account.js.map +1 -0
- package/lib/types/host/authorized-catalog.js +13 -4
- package/lib/types/host/authorized-catalog.js.map +1 -1
- package/lib/types/host/claude-native-oauth.d.ts +17 -0
- package/lib/types/host/claude-native-oauth.js +274 -0
- package/lib/types/host/claude-native-oauth.js.map +1 -0
- package/lib/types/host/claude-own-route.d.ts +45 -0
- package/lib/types/host/claude-own-route.js +375 -0
- package/lib/types/host/claude-own-route.js.map +1 -0
- package/lib/types/host/cli-account-binding.d.ts +2 -1
- package/lib/types/host/cli-account-binding.js +64 -21
- package/lib/types/host/cli-account-binding.js.map +1 -1
- package/lib/types/host/cli-executable-paths.d.ts +30 -0
- package/lib/types/host/cli-executable-paths.js +272 -0
- package/lib/types/host/cli-executable-paths.js.map +1 -0
- package/lib/types/host/codex-models.d.ts +3 -1
- package/lib/types/host/codex-models.js +41 -4
- package/lib/types/host/codex-models.js.map +1 -1
- package/lib/types/host/extended-adapters.d.ts +3 -2
- package/lib/types/host/extended-adapters.js +9 -7
- package/lib/types/host/extended-adapters.js.map +1 -1
- package/lib/types/host/first-party-models.d.ts +5 -0
- package/lib/types/host/first-party-models.js +173 -50
- package/lib/types/host/first-party-models.js.map +1 -1
- package/lib/types/host/hermes-accounts.js +68 -9
- package/lib/types/host/hermes-accounts.js.map +1 -1
- package/lib/types/host/hermes-adapter.d.ts +3 -0
- package/lib/types/host/hermes-adapter.js +13 -4
- package/lib/types/host/hermes-adapter.js.map +1 -1
- package/lib/types/host/hermes-installation.js +54 -35
- package/lib/types/host/hermes-installation.js.map +1 -1
- package/lib/types/host/hermes-models.js +21 -2
- package/lib/types/host/hermes-models.js.map +1 -1
- package/lib/types/host/hermes-native-entry.py +48 -13
- package/lib/types/host/hermes-nous-refresh.py +331 -0
- package/lib/types/host/hermes-nous.d.ts +5 -1
- package/lib/types/host/hermes-nous.js +5 -5
- package/lib/types/host/hermes-nous.js.map +1 -1
- package/lib/types/host/hermes-renewal.d.ts +27 -0
- package/lib/types/host/hermes-renewal.js +292 -0
- package/lib/types/host/hermes-renewal.js.map +1 -0
- package/lib/types/host/hermes-windows-installation.d.ts +5 -0
- package/lib/types/host/hermes-windows-installation.js +91 -0
- package/lib/types/host/hermes-windows-installation.js.map +1 -0
- package/lib/types/host/index.d.ts +12 -0
- package/lib/types/host/index.js +138 -8
- package/lib/types/host/index.js.map +1 -1
- package/lib/types/host/kimi-accounts.d.ts +2 -0
- package/lib/types/host/kimi-accounts.js +4 -2
- package/lib/types/host/kimi-accounts.js.map +1 -1
- package/lib/types/host/kimi-oauth-models.d.ts +14 -0
- package/lib/types/host/kimi-oauth-models.js +246 -0
- package/lib/types/host/kimi-oauth-models.js.map +1 -0
- package/lib/types/host/mimo-metadata-cache.d.ts +22 -0
- package/lib/types/host/mimo-metadata-cache.js +121 -0
- package/lib/types/host/mimo-metadata-cache.js.map +1 -0
- package/lib/types/host/native-capability-cache.d.ts +11 -0
- package/lib/types/host/native-capability-cache.js +94 -0
- package/lib/types/host/native-capability-cache.js.map +1 -0
- package/lib/types/host/native-launch.mjs +62 -7
- package/lib/types/host/native-process.d.ts +3 -1
- package/lib/types/host/native-process.js +30 -5
- package/lib/types/host/native-process.js.map +1 -1
- package/lib/types/host/native-spawn.mjs +32 -0
- package/lib/types/host/opencode-native.js +13 -0
- package/lib/types/host/opencode-native.js.map +1 -1
- package/lib/types/host/pi-installation.d.mts +1 -1
- package/lib/types/host/pi-installation.mjs +27 -3
- package/lib/types/host/pi-omp-adapter.js +17 -3
- package/lib/types/host/pi-omp-adapter.js.map +1 -1
- package/lib/types/host/pi-omp-native.js +5 -3
- package/lib/types/host/pi-omp-native.js.map +1 -1
- package/lib/types/host/pi-omp-refresh.mjs +3 -3
- package/lib/types/host/private-launch.mjs +23 -0
- package/lib/types/host/process.d.ts +3 -0
- package/lib/types/host/process.js +3 -1
- package/lib/types/host/process.js.map +1 -1
- package/lib/types/host/routing.js +25 -2
- package/lib/types/host/routing.js.map +1 -1
- package/lib/types/host/runtime.js +21 -5
- package/lib/types/host/runtime.js.map +1 -1
- package/lib/types/host/settings-catalog-cache.d.ts +28 -0
- package/lib/types/host/settings-catalog-cache.js +189 -0
- package/lib/types/host/settings-catalog-cache.js.map +1 -0
- package/lib/types/host/windows-account-bootstrap.ps1 +44 -0
- package/lib/types/host/windows-account-launch.mjs +47 -0
- package/lib/types/host/windows-account-owner.ps1 +137 -0
- package/lib/types/host/windows-account-stdio.mjs +19 -0
- package/lib/types/host/windows-account-terminal.d.ts +17 -0
- package/lib/types/host/windows-account-terminal.js +208 -0
- package/lib/types/host/windows-account-terminal.js.map +1 -0
- package/lib/types/host/windows-agy-account.ps1 +81 -0
- package/lib/types/host/windows-cli-installation.d.ts +35 -0
- package/lib/types/host/windows-cli-installation.js +164 -0
- package/lib/types/host/windows-cli-installation.js.map +1 -0
- package/lib/types/host/windows-codex-launch.d.ts +9 -0
- package/lib/types/host/windows-codex-launch.js +216 -0
- package/lib/types/host/windows-codex-launch.js.map +1 -0
- package/lib/types/host/windows-desktop-cli-installation.d.ts +20 -0
- package/lib/types/host/windows-desktop-cli-installation.js +420 -0
- package/lib/types/host/windows-desktop-cli-installation.js.map +1 -0
- package/lib/types/host/windows-hidden-launch.mjs +51 -0
- package/lib/types/host/windows-hidden-process.d.ts +12 -0
- package/lib/types/host/windows-hidden-process.js +116 -0
- package/lib/types/host/windows-hidden-process.js.map +1 -0
- package/lib/types/host/zcode-adapter.js +5 -1
- package/lib/types/host/zcode-adapter.js.map +1 -1
- package/lib/types/host/zcode-grok-accounts.d.ts +3 -1
- package/lib/types/host/zcode-grok-accounts.js +60 -11
- package/lib/types/host/zcode-grok-accounts.js.map +1 -1
- package/lib/types/shared/accounts.d.ts +4 -0
- package/lib/types/shared/cli-settings.d.ts +8 -0
- package/lib/windows-account-bootstrap.ps1 +44 -0
- package/lib/windows-account-launch.mjs +47 -0
- package/lib/windows-account-owner.ps1 +137 -0
- package/lib/windows-account-stdio.mjs +19 -0
- package/lib/windows-agy-account.ps1 +81 -0
- package/lib/windows-hidden-launch.mjs +51 -0
- package/lib/zcode-resume.mjs +2 -2
- package/package.json +2 -2
package/lib/native-launch.mjs
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// Inspect fixed launcher data; never ask a shell to interpret CLI arguments.
|
|
2
2
|
import { openSync, readSync, fstatSync, closeSync, realpathSync, statSync, existsSync } from 'node:fs'
|
|
3
|
-
import { dirname, extname, isAbsolute, resolve, sep, delimiter, join } from 'node:path'
|
|
3
|
+
import { basename, dirname, extname, isAbsolute, relative, resolve, sep, delimiter, join } from 'node:path'
|
|
4
4
|
import { homedir, userInfo } from 'node:os'
|
|
5
5
|
|
|
6
|
-
const invalid = () =>
|
|
6
|
+
const invalid = () => Object.assign(
|
|
7
|
+
new Error('CLI 启动入口无法确认,请配置原生程序或固定 Node/Bun 入口'),
|
|
8
|
+
{ code: 'CLI_LAUNCHER_UNSUPPORTED' },
|
|
9
|
+
)
|
|
7
10
|
const batch = (value) => /\.(?:cmd|bat)$/i.test(value)
|
|
8
11
|
function text(path, limit = 128 * 1024) {
|
|
9
12
|
const descriptor = openSync(path, 'r')
|
|
@@ -73,6 +76,45 @@ function runtimeProgram(runtime, platform, launcherDirectory) {
|
|
|
73
76
|
if (!candidate) throw new Error('未找到 Bun,请安装该 CLI 所需的 Bun 运行时')
|
|
74
77
|
return file(candidate)
|
|
75
78
|
}
|
|
79
|
+
/** npm's native bin shim is data, not a batch program to execute. */
|
|
80
|
+
function nativePackageBin(shim, suffix) {
|
|
81
|
+
const parts = suffix.replace(/\\/g, '/').split('/')
|
|
82
|
+
if (parts[0] !== 'node_modules' || parts.some((part) => !part || part === '.' || part === '..'))
|
|
83
|
+
throw invalid()
|
|
84
|
+
const scoped = parts[1]?.startsWith('@')
|
|
85
|
+
const name = parts.slice(1, scoped ? 3 : 2).join('/')
|
|
86
|
+
if (!/^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/.test(name)) throw invalid()
|
|
87
|
+
const root = fileDirectory(resolve(dirname(shim), ...parts.slice(0, scoped ? 3 : 2)))
|
|
88
|
+
const entry = file(resolve(dirname(shim), ...parts))
|
|
89
|
+
const inside = relative(root, entry)
|
|
90
|
+
if (!inside || inside === '..' || inside.startsWith('..' + sep) || isAbsolute(inside)) throw invalid()
|
|
91
|
+
const manifest = JSON.parse(text(join(root, 'package.json')))
|
|
92
|
+
const command = basename(shim, extname(shim))
|
|
93
|
+
const declared = typeof manifest.bin === 'string'
|
|
94
|
+
? name.split('/').at(-1) === command ? manifest.bin : undefined
|
|
95
|
+
: manifest.bin?.[command]
|
|
96
|
+
if (manifest.name !== name || typeof declared !== 'string' || isAbsolute(declared) ||
|
|
97
|
+
declared.replace(/\\/g, '/').split('/').includes('..') || file(resolve(root, declared)) !== entry)
|
|
98
|
+
throw invalid()
|
|
99
|
+
const descriptor = openSync(entry, 'r')
|
|
100
|
+
try {
|
|
101
|
+
const header = Buffer.alloc(64), signature = Buffer.alloc(4)
|
|
102
|
+
const size = fstatSync(descriptor).size
|
|
103
|
+
if (readSync(descriptor, header, 0, 64, 0) !== 64 || header.toString('ascii', 0, 2) !== 'MZ')
|
|
104
|
+
throw invalid()
|
|
105
|
+
const offset = header.readUInt32LE(0x3c)
|
|
106
|
+
if (offset < 64 || offset + 4 > size || readSync(descriptor, signature, 0, 4, offset) !== 4 ||
|
|
107
|
+
signature.toString('hex') !== '50450000') throw invalid()
|
|
108
|
+
} finally {
|
|
109
|
+
closeSync(descriptor)
|
|
110
|
+
}
|
|
111
|
+
return entry
|
|
112
|
+
}
|
|
113
|
+
function fileDirectory(path) {
|
|
114
|
+
const canonical = realpathSync(path)
|
|
115
|
+
if (!statSync(canonical).isDirectory()) throw invalid()
|
|
116
|
+
return canonical
|
|
117
|
+
}
|
|
76
118
|
function inspectBatch(executable, platform) {
|
|
77
119
|
const source = text(file(executable)).trim().replace(/\r\n/g, '\n')
|
|
78
120
|
// Nous' official fallback contains just a fixed interpreter and its encoded
|
|
@@ -101,8 +143,14 @@ function inspectBatch(executable, platform) {
|
|
|
101
143
|
// npm cmd-shim's standard final control prefix is launcher boilerplate;
|
|
102
144
|
// it is discarded, and nothing from it is executed by this module.
|
|
103
145
|
line = line.replace(/^endLocal & goto #_undefined_# 2>NUL \|\| title [^&\n]+ & /i, '')
|
|
146
|
+
const native = /^@?"%(?:~dp0[\\/]?|dp0%[\\/])([^"\n%]+\.exe)"\s+%\*$/i.exec(line)
|
|
147
|
+
if (native) {
|
|
148
|
+
entries.add(nativePackageBin(executable, native[1]))
|
|
149
|
+
runtimes.add('native')
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
104
152
|
const command =
|
|
105
|
-
/^@?(?:(node|bun)|"%_prog%"|"%(?:~dp0
|
|
153
|
+
/^@?(?:(node|bun)|"%_prog%"|"%(?:~dp0[\\/]?|dp0%[\\/])(node|bun)\.exe")\s+"%(?:~dp0[\\/]?|dp0%[\\/])([^"\n%]+)"\s+%\*$/i.exec(
|
|
106
154
|
line,
|
|
107
155
|
)
|
|
108
156
|
if (command) {
|
|
@@ -112,7 +160,7 @@ function inspectBatch(executable, platform) {
|
|
|
112
160
|
if (command[1] || command[2]) runtimes.add((command[1] || command[2]).toLowerCase())
|
|
113
161
|
continue
|
|
114
162
|
}
|
|
115
|
-
const program = /^@?set "?_prog=(?:%(?:~dp0
|
|
163
|
+
const program = /^@?set "?_prog=(?:%(?:~dp0[\\/]?|dp0%[\\/]))?(node|bun)(?:\.exe)?"?$/i.exec(line)
|
|
116
164
|
if (program) {
|
|
117
165
|
runtimes.add(program[1].toLowerCase())
|
|
118
166
|
continue
|
|
@@ -121,7 +169,7 @@ function inspectBatch(executable, platform) {
|
|
|
121
169
|
/^@?(?:goto start|:find_dp0|:start|set dp0=%~dp0|exit \/b|setlocal|endlocal|call :find_dp0)$/i.test(
|
|
122
170
|
line,
|
|
123
171
|
) ||
|
|
124
|
-
/^@?if (?:not )?exist "%(?:~dp0
|
|
172
|
+
/^@?if (?:not )?exist "%(?:~dp0[\\/]?|dp0%[\\/])(?:node|bun)\.exe" \($/i.test(line) ||
|
|
125
173
|
/^\)(?: else \()?$/i.test(line) ||
|
|
126
174
|
/^@?set pathext=%pathext:;\.js;=;%$/i.test(line) ||
|
|
127
175
|
/^@?if (?:not )?defined node_path \($/i.test(line) ||
|
|
@@ -133,6 +181,7 @@ function inspectBatch(executable, platform) {
|
|
|
133
181
|
if (entries.size !== 1 || runtimes.size !== 1) throw invalid()
|
|
134
182
|
const entry = [...entries][0],
|
|
135
183
|
runtime = [...runtimes][0]
|
|
184
|
+
if (runtime === 'native') return { kind: 'native', argv: [entry] }
|
|
136
185
|
if (scriptRuntime(entry) !== runtime) throw invalid()
|
|
137
186
|
return { kind: runtime, argv: [runtimeProgram(runtime, platform, dirname(executable)), entry] }
|
|
138
187
|
}
|
|
@@ -151,10 +200,16 @@ export function resolveNativeEntry(executable, platform = process.platform) {
|
|
|
151
200
|
if (candidate) executable = candidate
|
|
152
201
|
}
|
|
153
202
|
if (!batch(executable)) return executable
|
|
154
|
-
|
|
203
|
+
let inspected
|
|
204
|
+
try {
|
|
205
|
+
inspected = inspectBatch(executable, platform)
|
|
206
|
+
} catch {
|
|
207
|
+
// A present but invalid shim is not an absent CLI. Never forward raw paths or script text.
|
|
208
|
+
throw invalid()
|
|
209
|
+
}
|
|
155
210
|
// Keep Bun's fixed shim identity: it may pin a bundled bun.exe beside the
|
|
156
211
|
// shim which cannot be recovered from only the package's JS path.
|
|
157
|
-
return inspected.kind === 'node' ? inspected.argv[1] : executable
|
|
212
|
+
return inspected.kind === 'node' ? inspected.argv[1] : inspected.kind === 'native' ? inspected.argv[0] : executable
|
|
158
213
|
}
|
|
159
214
|
|
|
160
215
|
/** Windows CreateProcess accepts real programs, not npm command files. */
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
|
|
3
|
+
/** Native CLI descendants keep their pipes/ConPTY and the enclosing Host Job.
|
|
4
|
+
* Window presentation is per child: hiding a wrapper does not hide its children.
|
|
5
|
+
* Node ignores windowsHide on POSIX, so both platforms use this one boundary.
|
|
6
|
+
*/
|
|
7
|
+
export function spawnNativeProcess(executable, argv, options) {
|
|
8
|
+
return spawn(executable, argv, { ...options, windowsHide: true })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Relay ordinary background pipes as bytes with Node's stream backpressure.
|
|
12
|
+
* Numeric/inherited descriptors make libuv omit CREATE_NO_WINDOW on Windows.
|
|
13
|
+
* This must never be used for a ConPTY: its console handles stay inherited. */
|
|
14
|
+
export function forwardNativeStdio(child) {
|
|
15
|
+
const stopInput = () => {
|
|
16
|
+
process.stdin.unpipe(child.stdin)
|
|
17
|
+
process.stdin.destroy()
|
|
18
|
+
}
|
|
19
|
+
const fail = () => { child.kill('SIGTERM'); process.exitCode = 1 }
|
|
20
|
+
child.stdin.on('error', error => { stopInput(); if (error.code !== 'EPIPE') fail() })
|
|
21
|
+
child.stdin.once('close', stopInput)
|
|
22
|
+
child.once('exit', stopInput)
|
|
23
|
+
child.once('error', stopInput)
|
|
24
|
+
process.stdin.on('error', fail)
|
|
25
|
+
process.stdout.on('error', fail)
|
|
26
|
+
process.stderr.on('error', fail)
|
|
27
|
+
child.stdout.on('error', fail)
|
|
28
|
+
child.stderr.on('error', fail)
|
|
29
|
+
process.stdin.pipe(child.stdin)
|
|
30
|
+
child.stdout.pipe(process.stdout, { end: false })
|
|
31
|
+
child.stderr.pipe(process.stderr, { end: false })
|
|
32
|
+
}
|
package/lib/pi-installation.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// One verified Pi package for the Host, account UI, native catalog and RPC entry.
|
|
2
2
|
// Resolve installation metadata only; never execute an arbitrary shell launcher.
|
|
3
3
|
import { readFileSync, realpathSync, statSync } from 'node:fs'
|
|
4
|
-
import {
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
5
6
|
import { resolveNativeEntry } from './native-launch.mjs'
|
|
6
7
|
|
|
7
|
-
const versions = new Set(['1.0.2', '1.0.4'])
|
|
8
|
+
const versions = new Set(['1.0.2', '1.0.4', '1.1.0'])
|
|
8
9
|
const failure = () =>
|
|
9
10
|
Object.assign(
|
|
10
|
-
new Error('Pi 安装身份或原生接口不匹配;需要 Pi Coding Agent 1.0.2 或 1.0
|
|
11
|
+
new Error('Pi 安装身份或原生接口不匹配;需要 Pi Coding Agent 1.0.2、1.0.4 或 1.1.0,不能使用 OMP 入口'),
|
|
11
12
|
{ code: 'CLI_IDENTITY_MISMATCH' },
|
|
12
13
|
)
|
|
13
14
|
function metadata(path) {
|
|
@@ -15,6 +16,27 @@ function metadata(path) {
|
|
|
15
16
|
if (!info.isFile() || info.size > 128 * 1024) throw failure()
|
|
16
17
|
return readFileSync(path, 'utf8')
|
|
17
18
|
}
|
|
19
|
+
// Exact official Windows managed launcher inspected from Pi 1.1.0. It is read as
|
|
20
|
+
// installation metadata, never evaluated. Unknown bootstrap code fails closed.
|
|
21
|
+
const managedWindowsLauncherSHA256 = '3d8b27bb3f5c07543303b0ee774ce215e4d6ae2b0ae43311c4acf51f517ffd13'
|
|
22
|
+
function windowsManagedEntry(launcher) {
|
|
23
|
+
const source = metadata(launcher).trim().replace(/\r\n/g, '\n')
|
|
24
|
+
if (createHash('sha256').update(source).digest('hex') !== managedWindowsLauncherSHA256) throw failure()
|
|
25
|
+
const install = join(dirname(dirname(launcher)), 'install')
|
|
26
|
+
const version = metadata(join(install, 'current-version')).trim()
|
|
27
|
+
if (!versions.has(version)) throw failure()
|
|
28
|
+
const directory = resolve(install, 'releases', version, 'node_modules/@earendil-works/pi-coding-agent')
|
|
29
|
+
// A version pointer/package symlink must not select code outside this release.
|
|
30
|
+
if (realpathSync(directory) !== directory) throw failure()
|
|
31
|
+
const manifest = JSON.parse(metadata(join(directory, 'package.json')))
|
|
32
|
+
const bin = typeof manifest.bin === 'string' ? manifest.bin : manifest.bin?.pi
|
|
33
|
+
if (manifest.name !== '@earendil-works/pi-coding-agent' || manifest.version !== version ||
|
|
34
|
+
!['dist/cli.js', 'dist/bundle/cli.js'].includes(bin)) throw failure()
|
|
35
|
+
const entry = realpathSync(resolve(directory, bin))
|
|
36
|
+
const inside = relative(directory, entry)
|
|
37
|
+
if (!inside || inside === '..' || inside.startsWith('..' + sep) || isAbsolute(inside)) throw failure()
|
|
38
|
+
return entry
|
|
39
|
+
}
|
|
18
40
|
|
|
19
41
|
export function inspectPiInstallation(entry) {
|
|
20
42
|
try {
|
|
@@ -34,6 +56,8 @@ export function inspectPiInstallation(entry) {
|
|
|
34
56
|
}
|
|
35
57
|
}
|
|
36
58
|
executable = realpathSync(resolveNativeEntry(executable))
|
|
59
|
+
if (basename(executable).toLowerCase() === 'pi-launcher.js' && basename(dirname(executable)) === 'bin')
|
|
60
|
+
executable = windowsManagedEntry(executable)
|
|
37
61
|
let directory = dirname(executable)
|
|
38
62
|
for (let depth = 0; depth < 5; depth++, directory = dirname(directory)) {
|
|
39
63
|
let manifest
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Native metadata only. No prompts, API keys, OAuth objects, headers or raw errors leave this boundary.
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { pathToFileURL } from 'node:url'
|
|
4
|
-
import {
|
|
4
|
+
import { spawnNativeProcess } from './native-spawn.mjs'
|
|
5
5
|
import { inspectPiInstallation } from './pi-installation.mjs'
|
|
6
6
|
import { nativeLaunchArgv } from './native-launch.mjs'
|
|
7
7
|
|
|
@@ -144,7 +144,7 @@ async function nativeCandidates(
|
|
|
144
144
|
}
|
|
145
145
|
const args = ['models', '--json', '--no-extensions', '--config', join(directory, 'config.yml')]
|
|
146
146
|
const nativeArgs = nativeLaunchArgv([executable, ...args])
|
|
147
|
-
const child =
|
|
147
|
+
const child = spawnNativeProcess(nativeArgs[0], nativeArgs.slice(1), {
|
|
148
148
|
cwd: directory,
|
|
149
149
|
env,
|
|
150
150
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -196,16 +196,23 @@ async function nativeCandidates(
|
|
|
196
196
|
// Availability is deliberately separate from native catalog presence. A model
|
|
197
197
|
// is selectable only through current account scope or a declared exact API route.
|
|
198
198
|
async function privateJSON(path, fallback = {}) {
|
|
199
|
-
const { open } = await import('node:fs/promises')
|
|
199
|
+
const { open, lstat } = await import('node:fs/promises')
|
|
200
200
|
const { constants } = await import('node:fs')
|
|
201
|
-
let handle
|
|
201
|
+
let handle, entry
|
|
202
202
|
try {
|
|
203
|
+
// Windows does not implement O_NOFOLLOW. Check the directory entry before
|
|
204
|
+
// opening and bind it to the descriptor and final entry on every platform.
|
|
205
|
+
entry = await lstat(path)
|
|
206
|
+
if (entry.isSymbolicLink() || !entry.isFile() || entry.nlink !== 1)
|
|
207
|
+
throw new Error('Unsafe private metadata input')
|
|
203
208
|
handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK)
|
|
204
209
|
const info = await handle.stat()
|
|
205
|
-
if (!info.isFile() || info.nlink !== 1 || info.size > 2 * 1024 * 1024
|
|
210
|
+
if (!info.isFile() || info.nlink !== 1 || info.size > 2 * 1024 * 1024 ||
|
|
211
|
+
info.dev !== entry.dev || info.ino !== entry.ino)
|
|
206
212
|
throw new Error('Unsafe private metadata input')
|
|
207
213
|
const text = await handle.readFile('utf8')
|
|
208
214
|
const after = await handle.stat()
|
|
215
|
+
const current = await lstat(path)
|
|
209
216
|
const value = JSON.parse(text)
|
|
210
217
|
if (
|
|
211
218
|
!value ||
|
|
@@ -213,12 +220,15 @@ async function privateJSON(path, fallback = {}) {
|
|
|
213
220
|
Array.isArray(value) ||
|
|
214
221
|
Buffer.byteLength(text) > 2 * 1024 * 1024 ||
|
|
215
222
|
info.size !== after.size ||
|
|
216
|
-
info.mtimeMs !== after.mtimeMs
|
|
223
|
+
info.mtimeMs !== after.mtimeMs ||
|
|
224
|
+
current.isSymbolicLink() || !current.isFile() || current.nlink !== 1 ||
|
|
225
|
+
current.dev !== info.dev || current.ino !== info.ino ||
|
|
226
|
+
current.size !== after.size || current.mtimeMs !== after.mtimeMs
|
|
217
227
|
)
|
|
218
228
|
throw new Error('Invalid private metadata input')
|
|
219
229
|
return value
|
|
220
230
|
} catch (error) {
|
|
221
|
-
if (error?.code === 'ENOENT') return fallback
|
|
231
|
+
if (!entry && error?.code === 'ENOENT') return fallback
|
|
222
232
|
throw error
|
|
223
233
|
} finally {
|
|
224
234
|
await handle?.close()
|
package/lib/pi-omp-bridge.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Subprocess protocol bridge. The Host owns the external sandbox, credentials,
|
|
2
2
|
// deadlines, directory/session locks, and descendant cleanup. No shell is used.
|
|
3
|
-
import {
|
|
3
|
+
import { spawnNativeProcess } from './native-spawn.mjs'
|
|
4
4
|
import { createHash, randomBytes } from 'node:crypto'
|
|
5
5
|
import { mkdir, writeFile, rename, realpath, unlink, rmdir, lstat, open, rm } from 'node:fs/promises'
|
|
6
6
|
import { constants } from 'node:fs'
|
|
@@ -360,7 +360,7 @@ try {
|
|
|
360
360
|
]
|
|
361
361
|
const launch = nativeLaunchArgv([config.executable, ...nativeArgs])
|
|
362
362
|
if (stopped) throw new Error('任务已停止')
|
|
363
|
-
child =
|
|
363
|
+
child = spawnNativeProcess(launch[0], launch.slice(1), {
|
|
364
364
|
cwd: config.project,
|
|
365
365
|
env,
|
|
366
366
|
detached: false,
|
package/lib/pi-omp-refresh.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { constants } from 'node:fs'
|
|
|
2
2
|
import { lstat, open, realpath, rename, rm, writeFile } from 'node:fs/promises'
|
|
3
3
|
import { createHash, randomUUID } from 'node:crypto'
|
|
4
4
|
import { createRequire } from 'node:module'
|
|
5
|
-
import { dirname, join } from 'node:path'
|
|
5
|
+
import { dirname, join, toNamespacedPath } from 'node:path'
|
|
6
6
|
import { pathToFileURL } from 'node:url'
|
|
7
7
|
import { DatabaseSync } from 'node:sqlite'
|
|
8
8
|
import { inspectPiInstallation } from './pi-installation.mjs'
|
|
@@ -138,7 +138,7 @@ export async function assertPiOmpRefreshSources(root, expected) {
|
|
|
138
138
|
let current
|
|
139
139
|
if (receipt.cli === 'pi') current = (await refreshJson(entry.path, false))[entry.provider]
|
|
140
140
|
else {
|
|
141
|
-
const db = new DatabaseSync(entry.path, { readOnly: true })
|
|
141
|
+
const db = new DatabaseSync(toNamespacedPath(entry.path), { readOnly: true })
|
|
142
142
|
try {
|
|
143
143
|
current = rowCredential(
|
|
144
144
|
db
|
|
@@ -173,7 +173,7 @@ async function refreshedEntries(root, receipt) {
|
|
|
173
173
|
if (refreshFingerprint(data[entry.provider]) !== entry.hash) throw unsafe()
|
|
174
174
|
for (const entry of receipt.entries) result.push([entry, data[entry.provider]])
|
|
175
175
|
} else {
|
|
176
|
-
const db = new DatabaseSync(join(root, 'agent/agent.db'), { readOnly: true })
|
|
176
|
+
const db = new DatabaseSync(toNamespacedPath(join(root, 'agent/agent.db')), { readOnly: true })
|
|
177
177
|
try {
|
|
178
178
|
const rows = db
|
|
179
179
|
.prepare('SELECT rowid AS id,provider,credential_type,data,disabled_cause FROM auth_credentials')
|
package/lib/private-launch.mjs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
// Set a private file-creation mask without mutating the shared Harness process.
|
|
2
|
-
import {
|
|
2
|
+
import { forwardNativeStdio, spawnNativeProcess } from './native-spawn.mjs'
|
|
3
3
|
import { nativeLaunchArgv } from './native-launch.mjs'
|
|
4
4
|
process.umask(0o077)
|
|
5
5
|
const [executable, ...argv] = nativeLaunchArgv(process.argv.slice(2))
|
|
6
|
-
|
|
6
|
+
// This helper is also used under POSIX PTYs, whose original handles must stay.
|
|
7
|
+
// Windows ConPTY account launch unwraps it in windows-account-launch.mjs.
|
|
8
|
+
const backgroundPipes = process.platform === 'win32'
|
|
9
|
+
const child = spawnNativeProcess(executable, argv, { env: process.env, stdio: backgroundPipes ? ['pipe', 'pipe', 'pipe'] : 'inherit' })
|
|
10
|
+
if (backgroundPipes) forwardNativeStdio(child)
|
|
7
11
|
let stopped = false
|
|
8
12
|
for (const signal of ['SIGTERM', 'SIGINT'])
|
|
9
13
|
process.on(signal, () => {
|
package/lib/terminal-bridge.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// The native terminal owner tracks descendants across their own process groups.
|
|
2
2
|
// Keep this root alive until the Host acknowledges cleanup; never merge stderr
|
|
3
3
|
// into the CLI JSON protocol. Envelopes are transport only, not agent events.
|
|
4
|
-
import {
|
|
4
|
+
import { spawnNativeProcess } from './native-spawn.mjs'
|
|
5
5
|
import { nativeLaunchArgv } from './native-launch.mjs'
|
|
6
6
|
const [executable, ...args] = nativeLaunchArgv(process.argv.slice(2))
|
|
7
7
|
const send = (channel, fields) => process.stdout.write(JSON.stringify({ channel, ...fields }) + '\n')
|
|
8
8
|
const keepAlive = setInterval(() => {}, 60000)
|
|
9
|
-
const child =
|
|
9
|
+
const child = spawnNativeProcess(executable, args, { stdio: ['ignore', 'pipe', 'pipe'] })
|
|
10
10
|
child.stdout.on('data', (bytes) => send('stdout', { bytes: bytes.toString('base64') }))
|
|
11
11
|
child.stderr.on('data', (bytes) => send('stderr', { bytes: bytes.toString('base64') }))
|
|
12
12
|
child.on('error', (error) => send('error', { message: error.message }))
|