dsh-cliworker-now 0.0.0-stage → 0.7.7
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.en.md +364 -0
- package/README.md +368 -2
- package/cordis.patch.yml +3 -0
- package/lib/account-models.mjs +262 -0
- package/lib/client.js +22139 -0
- package/lib/client.js.map +1 -0
- package/lib/grok-catalog.mjs +145 -0
- package/lib/hermes-native-entry.py +96 -0
- package/lib/hermes-nous-refresh.py +331 -0
- package/lib/index.js +14544 -0
- package/lib/index.js.map +1 -0
- package/lib/native-launch.mjs +234 -0
- package/lib/native-spawn.mjs +32 -0
- package/lib/own-account-lease.mjs +105 -0
- package/lib/pi-installation.mjs +87 -0
- package/lib/pi-login.mjs +80 -0
- package/lib/pi-native-catalog.mjs +417 -0
- package/lib/pi-omp-bridge.mjs +602 -0
- package/lib/pi-omp-environment.mjs +15 -0
- package/lib/pi-omp-refresh.mjs +324 -0
- package/lib/private-launch.mjs +23 -0
- package/lib/terminal-bridge.mjs +18 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +1430 -0
- package/lib/typert.remote-client.d.ts +72 -0
- package/lib/typert.remote-client.d.ts.map +1 -0
- package/lib/typert.remote-client.js +1256 -0
- 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-css.d.ts +1 -0
- package/lib/types/client/account-terminal-css.js +3 -0
- package/lib/types/client/account-terminal-css.js.map +1 -0
- package/lib/types/client/account-terminal.d.ts +17 -0
- package/lib/types/client/account-terminal.js +525 -0
- package/lib/types/client/account-terminal.js.map +1 -0
- package/lib/types/client/assets/antigravity.png +0 -0
- package/lib/types/client/assets/claude-code.png +0 -0
- package/lib/types/client/assets/cliworker-now-logo.png +0 -0
- package/lib/types/client/assets/codex.png +0 -0
- package/lib/types/client/assets/grok.png +0 -0
- package/lib/types/client/assets/hermes.png +0 -0
- package/lib/types/client/assets/kimi-dark.png +0 -0
- package/lib/types/client/assets/kimi-light.png +0 -0
- package/lib/types/client/assets/mimo-code.png +0 -0
- package/lib/types/client/assets/omp.png +0 -0
- package/lib/types/client/assets/opencode.png +0 -0
- package/lib/types/client/assets/pi.png +0 -0
- package/lib/types/client/assets/zcode.png +0 -0
- package/lib/types/client/composer-keymap.d.ts +18 -0
- package/lib/types/client/composer-keymap.js +49 -0
- package/lib/types/client/composer-keymap.js.map +1 -0
- package/lib/types/client/conversation-timeline.d.ts +6 -0
- package/lib/types/client/conversation-timeline.js +65 -0
- package/lib/types/client/conversation-timeline.js.map +1 -0
- package/lib/types/client/copy-text.d.ts +5 -0
- package/lib/types/client/copy-text.js +45 -0
- package/lib/types/client/copy-text.js.map +1 -0
- package/lib/types/client/icons.d.ts +21 -0
- package/lib/types/client/icons.js +68 -0
- package/lib/types/client/icons.js.map +1 -0
- package/lib/types/client/index.d.ts +3 -0
- package/lib/types/client/index.js +124 -0
- package/lib/types/client/index.js.map +1 -0
- package/lib/types/client/management-styles.d.ts +1 -0
- package/lib/types/client/management-styles.js +17 -0
- package/lib/types/client/management-styles.js.map +1 -0
- package/lib/types/client/name-copy.d.ts +5 -0
- package/lib/types/client/name-copy.js +107 -0
- package/lib/types/client/name-copy.js.map +1 -0
- package/lib/types/client/native-job-overlay.d.ts +11 -0
- package/lib/types/client/native-job-overlay.js +129 -0
- package/lib/types/client/native-job-overlay.js.map +1 -0
- package/lib/types/client/operation-error.d.ts +2 -0
- package/lib/types/client/operation-error.js +12 -0
- package/lib/types/client/operation-error.js.map +1 -0
- package/lib/types/client/panel.d.ts +8 -0
- package/lib/types/client/panel.js +310 -0
- package/lib/types/client/panel.js.map +1 -0
- package/lib/types/client/rename-copy-styles.d.ts +1 -0
- package/lib/types/client/rename-copy-styles.js +21 -0
- package/lib/types/client/rename-copy-styles.js.map +1 -0
- package/lib/types/client/rename-worker.d.ts +9 -0
- package/lib/types/client/rename-worker.js +53 -0
- package/lib/types/client/rename-worker.js.map +1 -0
- package/lib/types/client/role-presets.d.ts +6 -0
- package/lib/types/client/role-presets.js +111 -0
- package/lib/types/client/role-presets.js.map +1 -0
- package/lib/types/client/settings-dialog.d.ts +13 -0
- package/lib/types/client/settings-dialog.js +442 -0
- package/lib/types/client/settings-dialog.js.map +1 -0
- package/lib/types/client/stat-popover.d.ts +10 -0
- package/lib/types/client/stat-popover.js +32 -0
- package/lib/types/client/stat-popover.js.map +1 -0
- package/lib/types/client/styles.d.ts +9 -0
- package/lib/types/client/styles.js +221 -0
- package/lib/types/client/styles.js.map +1 -0
- package/lib/types/client/telemetry.d.ts +11 -0
- package/lib/types/client/telemetry.js +24 -0
- package/lib/types/client/telemetry.js.map +1 -0
- package/lib/types/client/worker-card.d.ts +10 -0
- package/lib/types/client/worker-card.js +75 -0
- package/lib/types/client/worker-card.js.map +1 -0
- package/lib/types/client/worker-model-menu.d.ts +9 -0
- package/lib/types/client/worker-model-menu.js +136 -0
- package/lib/types/client/worker-model-menu.js.map +1 -0
- package/lib/types/client/workers.d.ts +21 -0
- package/lib/types/client/workers.js +59 -0
- package/lib/types/client/workers.js.map +1 -0
- 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-identity.d.ts +16 -0
- package/lib/types/host/account-identity.js +126 -0
- package/lib/types/host/account-identity.js.map +1 -0
- package/lib/types/host/account-login.d.ts +10 -0
- package/lib/types/host/account-login.js +126 -0
- package/lib/types/host/account-login.js.map +1 -0
- package/lib/types/host/account-models.d.mts +21 -0
- package/lib/types/host/account-models.mjs +262 -0
- package/lib/types/host/accounts.d.ts +42 -0
- package/lib/types/host/accounts.js +945 -0
- package/lib/types/host/accounts.js.map +1 -0
- package/lib/types/host/adapters.d.ts +24 -0
- package/lib/types/host/adapters.js +380 -0
- package/lib/types/host/adapters.js.map +1 -0
- package/lib/types/host/agy-context.d.ts +8 -0
- package/lib/types/host/agy-context.js +122 -0
- package/lib/types/host/agy-context.js.map +1 -0
- package/lib/types/host/agy-models.d.ts +5 -0
- package/lib/types/host/agy-models.js +37 -0
- package/lib/types/host/agy-models.js.map +1 -0
- 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.d.ts +14 -0
- package/lib/types/host/authorized-catalog.js +35 -0
- package/lib/types/host/authorized-catalog.js.map +1 -0
- package/lib/types/host/bridge-protocol.d.ts +15 -0
- package/lib/types/host/bridge-protocol.js +80 -0
- package/lib/types/host/bridge-protocol.js.map +1 -0
- package/lib/types/host/claude-native-oauth.d.ts +17 -0
- package/lib/types/host/claude-native-oauth.js +272 -0
- package/lib/types/host/claude-native-oauth.js.map +1 -0
- package/lib/types/host/claude-own-route.d.ts +33 -0
- package/lib/types/host/claude-own-route.js +250 -0
- package/lib/types/host/claude-own-route.js.map +1 -0
- package/lib/types/host/cli-account-binding.d.ts +33 -0
- package/lib/types/host/cli-account-binding.js +540 -0
- package/lib/types/host/cli-account-binding.js.map +1 -0
- package/lib/types/host/cli-protocol.d.ts +27 -0
- package/lib/types/host/cli-protocol.js +212 -0
- package/lib/types/host/cli-protocol.js.map +1 -0
- package/lib/types/host/codex-account.d.ts +8 -0
- package/lib/types/host/codex-account.js +143 -0
- package/lib/types/host/codex-account.js.map +1 -0
- package/lib/types/host/codex-models.d.ts +6 -0
- package/lib/types/host/codex-models.js +169 -0
- package/lib/types/host/codex-models.js.map +1 -0
- package/lib/types/host/extended-adapters.d.ts +44 -0
- package/lib/types/host/extended-adapters.js +207 -0
- package/lib/types/host/extended-adapters.js.map +1 -0
- package/lib/types/host/first-party-account-context.d.ts +26 -0
- package/lib/types/host/first-party-account-context.js +55 -0
- package/lib/types/host/first-party-account-context.js.map +1 -0
- package/lib/types/host/first-party-models.d.ts +29 -0
- package/lib/types/host/first-party-models.js +459 -0
- package/lib/types/host/first-party-models.js.map +1 -0
- package/lib/types/host/grok-adapter.d.ts +44 -0
- package/lib/types/host/grok-adapter.js +293 -0
- package/lib/types/host/grok-adapter.js.map +1 -0
- package/lib/types/host/grok-models.d.ts +47 -0
- package/lib/types/host/grok-models.js +320 -0
- package/lib/types/host/grok-models.js.map +1 -0
- package/lib/types/host/harness-opencode-accounts.d.ts +17 -0
- package/lib/types/host/harness-opencode-accounts.js +80 -0
- package/lib/types/host/harness-opencode-accounts.js.map +1 -0
- package/lib/types/host/hermes-account-context.d.ts +8 -0
- package/lib/types/host/hermes-account-context.js +99 -0
- package/lib/types/host/hermes-account-context.js.map +1 -0
- package/lib/types/host/hermes-accounts.d.ts +19 -0
- package/lib/types/host/hermes-accounts.js +356 -0
- package/lib/types/host/hermes-accounts.js.map +1 -0
- package/lib/types/host/hermes-adapter.d.ts +93 -0
- package/lib/types/host/hermes-adapter.js +455 -0
- package/lib/types/host/hermes-adapter.js.map +1 -0
- package/lib/types/host/hermes-env.d.ts +4 -0
- package/lib/types/host/hermes-env.js +22 -0
- package/lib/types/host/hermes-env.js.map +1 -0
- package/lib/types/host/hermes-installation.d.ts +10 -0
- package/lib/types/host/hermes-installation.js +181 -0
- package/lib/types/host/hermes-installation.js.map +1 -0
- package/lib/types/host/hermes-models.d.ts +26 -0
- package/lib/types/host/hermes-models.js +328 -0
- package/lib/types/host/hermes-models.js.map +1 -0
- package/lib/types/host/hermes-native-entry.py +96 -0
- package/lib/types/host/hermes-nous-refresh.py +331 -0
- package/lib/types/host/hermes-nous.d.ts +53 -0
- package/lib/types/host/hermes-nous.js +343 -0
- package/lib/types/host/hermes-nous.js.map +1 -0
- 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-suppression.d.ts +7 -0
- package/lib/types/host/hermes-suppression.js +69 -0
- package/lib/types/host/hermes-suppression.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 +140 -0
- package/lib/types/host/index.js +947 -0
- package/lib/types/host/index.js.map +1 -0
- package/lib/types/host/kimi-accounts.d.ts +17 -0
- package/lib/types/host/kimi-accounts.js +220 -0
- package/lib/types/host/kimi-accounts.js.map +1 -0
- 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/managed-agent.d.ts +4 -0
- package/lib/types/host/managed-agent.js +127 -0
- package/lib/types/host/managed-agent.js.map +1 -0
- package/lib/types/host/mimo-configuration.d.ts +22 -0
- package/lib/types/host/mimo-configuration.js +258 -0
- package/lib/types/host/mimo-configuration.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.d.mts +2 -0
- package/lib/types/host/native-launch.mjs +234 -0
- package/lib/types/host/native-process.d.ts +5 -0
- package/lib/types/host/native-process.js +41 -0
- package/lib/types/host/native-process.js.map +1 -0
- package/lib/types/host/native-spawn.mjs +32 -0
- package/lib/types/host/opencode-adapter.d.ts +94 -0
- package/lib/types/host/opencode-adapter.js +507 -0
- package/lib/types/host/opencode-adapter.js.map +1 -0
- package/lib/types/host/opencode-native.d.ts +27 -0
- package/lib/types/host/opencode-native.js +403 -0
- package/lib/types/host/opencode-native.js.map +1 -0
- package/lib/types/host/own-account-lease.d.mts +5 -0
- package/lib/types/host/own-account-lease.mjs +105 -0
- package/lib/types/host/pi-installation.d.mts +7 -0
- package/lib/types/host/pi-installation.mjs +87 -0
- package/lib/types/host/pi-omp-accounts.d.ts +28 -0
- package/lib/types/host/pi-omp-accounts.js +175 -0
- package/lib/types/host/pi-omp-accounts.js.map +1 -0
- package/lib/types/host/pi-omp-adapter.d.ts +32 -0
- package/lib/types/host/pi-omp-adapter.js +190 -0
- package/lib/types/host/pi-omp-adapter.js.map +1 -0
- package/lib/types/host/pi-omp-environment.d.mts +1 -0
- package/lib/types/host/pi-omp-environment.mjs +15 -0
- package/lib/types/host/pi-omp-identity.d.ts +2 -0
- package/lib/types/host/pi-omp-identity.js +142 -0
- package/lib/types/host/pi-omp-identity.js.map +1 -0
- package/lib/types/host/pi-omp-logout.d.ts +3 -0
- package/lib/types/host/pi-omp-logout.js +152 -0
- package/lib/types/host/pi-omp-logout.js.map +1 -0
- package/lib/types/host/pi-omp-native.d.ts +71 -0
- package/lib/types/host/pi-omp-native.js +753 -0
- package/lib/types/host/pi-omp-native.js.map +1 -0
- package/lib/types/host/pi-omp-refresh.d.mts +7 -0
- package/lib/types/host/pi-omp-refresh.mjs +324 -0
- package/lib/types/host/private-launch.mjs +23 -0
- package/lib/types/host/process.d.ts +43 -0
- package/lib/types/host/process.js +134 -0
- package/lib/types/host/process.js.map +1 -0
- package/lib/types/host/protocol.d.ts +23 -0
- package/lib/types/host/protocol.js +112 -0
- package/lib/types/host/protocol.js.map +1 -0
- package/lib/types/host/roles.d.ts +34 -0
- package/lib/types/host/roles.js +80 -0
- package/lib/types/host/roles.js.map +1 -0
- package/lib/types/host/routing.d.ts +31 -0
- package/lib/types/host/routing.js +248 -0
- package/lib/types/host/routing.js.map +1 -0
- package/lib/types/host/runtime.d.ts +60 -0
- package/lib/types/host/runtime.js +598 -0
- package/lib/types/host/runtime.js.map +1 -0
- package/lib/types/host/storage.d.ts +48 -0
- package/lib/types/host/storage.js +449 -0
- package/lib/types/host/storage.js.map +1 -0
- package/lib/types/host/telemetry.d.ts +17 -0
- package/lib/types/host/telemetry.js +158 -0
- package/lib/types/host/telemetry.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 +31 -0
- package/lib/types/host/windows-cli-installation.js +157 -0
- package/lib/types/host/windows-cli-installation.js.map +1 -0
- package/lib/types/host/windows-hidden-launch.mjs +49 -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-account-models.d.ts +16 -0
- package/lib/types/host/zcode-account-models.js +114 -0
- package/lib/types/host/zcode-account-models.js.map +1 -0
- package/lib/types/host/zcode-adapter.d.ts +93 -0
- package/lib/types/host/zcode-adapter.js +808 -0
- package/lib/types/host/zcode-adapter.js.map +1 -0
- package/lib/types/host/zcode-grok-accounts.d.ts +30 -0
- package/lib/types/host/zcode-grok-accounts.js +408 -0
- package/lib/types/host/zcode-grok-accounts.js.map +1 -0
- package/lib/types/shared/accounts.d.ts +47 -0
- package/lib/types/shared/accounts.js +2 -0
- package/lib/types/shared/accounts.js.map +1 -0
- package/lib/types/shared/builtin-presets.d.ts +2 -0
- package/lib/types/shared/builtin-presets.js +231 -0
- package/lib/types/shared/builtin-presets.js.map +1 -0
- package/lib/types/shared/cli-settings.d.ts +5 -0
- package/lib/types/shared/cli-settings.js +2 -0
- package/lib/types/shared/cli-settings.js.map +1 -0
- package/lib/types/shared/model-presentation.d.ts +10 -0
- package/lib/types/shared/model-presentation.js +95 -0
- package/lib/types/shared/model-presentation.js.map +1 -0
- package/lib/types/shared/models.d.ts +7 -0
- package/lib/types/shared/models.js +35 -0
- package/lib/types/shared/models.js.map +1 -0
- package/lib/types/shared/telemetry.d.ts +18 -0
- package/lib/types/shared/telemetry.js +16 -0
- package/lib/types/shared/telemetry.js.map +1 -0
- package/lib/types/shared/types.d.ts +127 -0
- package/lib/types/shared/types.js +147 -0
- package/lib/types/shared/types.js.map +1 -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 +49 -0
- package/lib/zcode-resume.mjs +423 -0
- package/package.json +109 -4
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// Inspect fixed launcher data; never ask a shell to interpret CLI arguments.
|
|
2
|
+
import { openSync, readSync, fstatSync, closeSync, realpathSync, statSync, existsSync } from 'node:fs'
|
|
3
|
+
import { basename, dirname, extname, isAbsolute, relative, resolve, sep, delimiter, join } from 'node:path'
|
|
4
|
+
import { homedir, userInfo } from 'node:os'
|
|
5
|
+
|
|
6
|
+
const invalid = () => Object.assign(
|
|
7
|
+
new Error('CLI 启动入口无法确认,请配置原生程序或固定 Node/Bun 入口'),
|
|
8
|
+
{ code: 'CLI_LAUNCHER_UNSUPPORTED' },
|
|
9
|
+
)
|
|
10
|
+
const batch = (value) => /\.(?:cmd|bat)$/i.test(value)
|
|
11
|
+
function text(path, limit = 128 * 1024) {
|
|
12
|
+
const descriptor = openSync(path, 'r')
|
|
13
|
+
try {
|
|
14
|
+
const info = fstatSync(descriptor)
|
|
15
|
+
if (!info.isFile() || info.size > limit) throw invalid()
|
|
16
|
+
const bytes = Buffer.alloc(info.size + 1)
|
|
17
|
+
const read = readSync(descriptor, bytes, 0, bytes.length, 0)
|
|
18
|
+
const after = fstatSync(descriptor)
|
|
19
|
+
if (read !== info.size || info.size !== after.size || info.mtimeMs !== after.mtimeMs) throw invalid()
|
|
20
|
+
return bytes
|
|
21
|
+
.subarray(0, read)
|
|
22
|
+
.toString('utf8')
|
|
23
|
+
.replace(/^\uFEFF/, '')
|
|
24
|
+
} finally {
|
|
25
|
+
closeSync(descriptor)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function file(path) {
|
|
29
|
+
const canonical = realpathSync(path)
|
|
30
|
+
if (!statSync(canonical).isFile()) throw invalid()
|
|
31
|
+
return canonical
|
|
32
|
+
}
|
|
33
|
+
function scriptRuntime(path) {
|
|
34
|
+
const descriptor = openSync(path, 'r')
|
|
35
|
+
try {
|
|
36
|
+
const bytes = Buffer.alloc(256)
|
|
37
|
+
const length = readSync(descriptor, bytes, 0, bytes.length, 0)
|
|
38
|
+
const header = bytes.subarray(0, length).toString('utf8').split('\n')[0].trim()
|
|
39
|
+
if (header.startsWith('#!')) {
|
|
40
|
+
const match = /^#!\s*(?:\/usr\/bin\/env\s+(?:-S\s+)?)?(?:[^\s]*\/)?(node|bun)\s*$/.exec(header)
|
|
41
|
+
if (!match) throw invalid()
|
|
42
|
+
return match[1]
|
|
43
|
+
}
|
|
44
|
+
// A fixed npm bin entry can declare its runtime in the package manifest.
|
|
45
|
+
for (let directory = dirname(path), depth = 0; depth < 5; depth++, directory = dirname(directory)) {
|
|
46
|
+
const manifestPath = join(directory, 'package.json')
|
|
47
|
+
if (!existsSync(manifestPath)) continue
|
|
48
|
+
const manifest = JSON.parse(text(manifestPath))
|
|
49
|
+
const bins = typeof manifest.bin === 'string' ? [manifest.bin] : Object.values(manifest.bin ?? {})
|
|
50
|
+
if (bins.some((entry) => typeof entry === 'string' && resolve(directory, entry) === path)) {
|
|
51
|
+
if (manifest.engines?.bun && !manifest.engines?.node) return 'bun'
|
|
52
|
+
return 'node'
|
|
53
|
+
}
|
|
54
|
+
break
|
|
55
|
+
}
|
|
56
|
+
return /\.[cm]?js$/i.test(path) ? 'node' : undefined
|
|
57
|
+
} finally {
|
|
58
|
+
closeSync(descriptor)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function runtimeProgram(runtime, platform, launcherDirectory) {
|
|
62
|
+
if (runtime === 'node') return process.execPath
|
|
63
|
+
if (runtime !== 'bun') throw invalid()
|
|
64
|
+
const name = platform === 'win32' ? 'bun.exe' : 'bun'
|
|
65
|
+
const candidates = [
|
|
66
|
+
...(launcherDirectory ? [join(launcherDirectory, name)] : []),
|
|
67
|
+
...(process.env.PATH ?? '')
|
|
68
|
+
.split(delimiter)
|
|
69
|
+
.map((directory) => directory.replace(/^"|"$/g, ''))
|
|
70
|
+
.filter(isAbsolute)
|
|
71
|
+
.map((directory) => join(directory, name)),
|
|
72
|
+
...(process.env.BUN_INSTALL ? [join(process.env.BUN_INSTALL, 'bin', name)] : []),
|
|
73
|
+
...[homedir(), userInfo().homedir].map((home) => join(home, '.bun', 'bin', name)),
|
|
74
|
+
]
|
|
75
|
+
const candidate = candidates.find((path) => existsSync(path) && statSync(path).isFile())
|
|
76
|
+
if (!candidate) throw new Error('未找到 Bun,请安装该 CLI 所需的 Bun 运行时')
|
|
77
|
+
return file(candidate)
|
|
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
|
+
}
|
|
118
|
+
function inspectBatch(executable, platform) {
|
|
119
|
+
const source = text(file(executable)).trim().replace(/\r\n/g, '\n')
|
|
120
|
+
// Nous' official fallback contains just a fixed interpreter and its encoded
|
|
121
|
+
// launcher. Preserve its own bootstrap; do not guess a virtualenv interpreter.
|
|
122
|
+
const hermes =
|
|
123
|
+
/^@echo off\n"([^"\n%]+)" -I -c "(import base64; exec\(base64\.b64decode\('([A-Za-z0-9+/]+={0,2})'\)\))" %\*$/i.exec(
|
|
124
|
+
source,
|
|
125
|
+
)
|
|
126
|
+
if (hermes) {
|
|
127
|
+
const decoded = Buffer.from(hermes[3], 'base64')
|
|
128
|
+
if (
|
|
129
|
+
!isAbsolute(hermes[1]) ||
|
|
130
|
+
decoded.toString('base64') !== hermes[3] ||
|
|
131
|
+
!decoded.toString('utf8').startsWith('import os, re, sys\n') ||
|
|
132
|
+
!decoded.toString('utf8').includes('import hermes_bootstrap') ||
|
|
133
|
+
!decoded.toString('utf8').includes('from hermes_cli.main import main')
|
|
134
|
+
)
|
|
135
|
+
throw invalid()
|
|
136
|
+
return { kind: 'python', argv: [file(hermes[1]), '-I', '-c', hermes[2]] }
|
|
137
|
+
}
|
|
138
|
+
const entries = new Set()
|
|
139
|
+
const runtimes = new Set()
|
|
140
|
+
for (const original of source.split('\n')) {
|
|
141
|
+
let line = original.trim()
|
|
142
|
+
if (!line || /^@?(?:echo off|rem\b.*)$/i.test(line)) continue
|
|
143
|
+
// npm cmd-shim's standard final control prefix is launcher boilerplate;
|
|
144
|
+
// it is discarded, and nothing from it is executed by this module.
|
|
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
|
+
}
|
|
152
|
+
const command =
|
|
153
|
+
/^@?(?:(node|bun)|"%_prog%"|"%(?:~dp0[\\/]?|dp0%[\\/])(node|bun)\.exe")\s+"%(?:~dp0[\\/]?|dp0%[\\/])([^"\n%]+)"\s+%\*$/i.exec(
|
|
154
|
+
line,
|
|
155
|
+
)
|
|
156
|
+
if (command) {
|
|
157
|
+
const path = file(resolve(dirname(executable), command[3].replace(/[\\/]/g, sep)))
|
|
158
|
+
if (!scriptRuntime(path)) throw invalid()
|
|
159
|
+
entries.add(path)
|
|
160
|
+
if (command[1] || command[2]) runtimes.add((command[1] || command[2]).toLowerCase())
|
|
161
|
+
continue
|
|
162
|
+
}
|
|
163
|
+
const program = /^@?set "?_prog=(?:%(?:~dp0[\\/]?|dp0%[\\/]))?(node|bun)(?:\.exe)?"?$/i.exec(line)
|
|
164
|
+
if (program) {
|
|
165
|
+
runtimes.add(program[1].toLowerCase())
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
if (
|
|
169
|
+
/^@?(?:goto start|:find_dp0|:start|set dp0=%~dp0|exit \/b|setlocal|endlocal|call :find_dp0)$/i.test(
|
|
170
|
+
line,
|
|
171
|
+
) ||
|
|
172
|
+
/^@?if (?:not )?exist "%(?:~dp0[\\/]?|dp0%[\\/])(?:node|bun)\.exe" \($/i.test(line) ||
|
|
173
|
+
/^\)(?: else \()?$/i.test(line) ||
|
|
174
|
+
/^@?set pathext=%pathext:;\.js;=;%$/i.test(line) ||
|
|
175
|
+
/^@?if (?:not )?defined node_path \($/i.test(line) ||
|
|
176
|
+
/^@?set "node_path=[^"\n&|<>]+"$/i.test(line)
|
|
177
|
+
)
|
|
178
|
+
continue
|
|
179
|
+
throw invalid()
|
|
180
|
+
}
|
|
181
|
+
if (entries.size !== 1 || runtimes.size !== 1) throw invalid()
|
|
182
|
+
const entry = [...entries][0],
|
|
183
|
+
runtime = [...runtimes][0]
|
|
184
|
+
if (runtime === 'native') return { kind: 'native', argv: [entry] }
|
|
185
|
+
if (scriptRuntime(entry) !== runtime) throw invalid()
|
|
186
|
+
return { kind: runtime, argv: [runtimeProgram(runtime, platform, dirname(executable)), entry] }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Canonical npm entry for installation identity checks; Python launchers keep their identity. */
|
|
190
|
+
export function resolveNativeEntry(executable, platform = process.platform) {
|
|
191
|
+
if (platform !== 'win32') return executable
|
|
192
|
+
if (isAbsolute(executable) && !extname(executable) && !existsSync(executable)) {
|
|
193
|
+
const extensions = (process.env.PATHEXT || '.EXE;.COM;.CMD;.BAT')
|
|
194
|
+
.split(';')
|
|
195
|
+
.map((extension) => extension.toLowerCase())
|
|
196
|
+
.filter((extension) => /^\.(?:exe|com|cmd|bat)$/.test(extension))
|
|
197
|
+
const candidate = [...new Set(extensions)]
|
|
198
|
+
.map((extension) => executable + extension)
|
|
199
|
+
.find((path) => existsSync(path) && statSync(path).isFile())
|
|
200
|
+
if (candidate) executable = candidate
|
|
201
|
+
}
|
|
202
|
+
if (!batch(executable)) return executable
|
|
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
|
+
}
|
|
210
|
+
// Keep Bun's fixed shim identity: it may pin a bundled bun.exe beside the
|
|
211
|
+
// shim which cannot be recovered from only the package's JS path.
|
|
212
|
+
return inspected.kind === 'node' ? inspected.argv[1] : inspected.kind === 'native' ? inspected.argv[0] : executable
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Windows CreateProcess accepts real programs, not npm command files. */
|
|
216
|
+
export function nativeLaunchArgv(argv, platform = process.platform) {
|
|
217
|
+
if (
|
|
218
|
+
!Array.isArray(argv) ||
|
|
219
|
+
!argv.length ||
|
|
220
|
+
argv.some((value) => typeof value !== 'string' || value.includes('\0'))
|
|
221
|
+
)
|
|
222
|
+
throw invalid()
|
|
223
|
+
if (platform !== 'win32') {
|
|
224
|
+
if (!/\.[cm]?js$/i.test(argv[0])) return [...argv]
|
|
225
|
+
const entry = file(argv[0])
|
|
226
|
+
return [runtimeProgram(scriptRuntime(entry), platform), entry, ...argv.slice(1)]
|
|
227
|
+
}
|
|
228
|
+
const executable = resolveNativeEntry(argv[0], platform),
|
|
229
|
+
arguments_ = argv.slice(1)
|
|
230
|
+
if (batch(executable)) return [...inspectBatch(executable, platform).argv, ...arguments_]
|
|
231
|
+
if (/\.[cm]?js$/i.test(executable) || (extname(executable) === '' && scriptRuntime(executable)))
|
|
232
|
+
return [runtimeProgram(scriptRuntime(executable), platform), file(executable), ...arguments_]
|
|
233
|
+
return [executable, ...arguments_]
|
|
234
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { constants } from 'node:fs'
|
|
2
|
+
import { lstat, mkdir, open, realpath, rename, rm, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { join, resolve } from 'node:path'
|
|
4
|
+
|
|
5
|
+
export const OWN_ACCOUNT_BUSY = '账号正被使用,请稍后刷新'
|
|
6
|
+
const nonceValid = (value) => typeof value === 'string' && /^[a-f0-9-]{36}$/.test(value)
|
|
7
|
+
async function privateRoot(root) {
|
|
8
|
+
const info = await lstat(root)
|
|
9
|
+
if (
|
|
10
|
+
!info.isDirectory() ||
|
|
11
|
+
info.isSymbolicLink() ||
|
|
12
|
+
(process.platform !== 'win32' && (info.mode & 0o077)) ||
|
|
13
|
+
(await realpath(root)) !== resolve(root)
|
|
14
|
+
)
|
|
15
|
+
throw new Error('账号运行目录无法安全使用')
|
|
16
|
+
}
|
|
17
|
+
/** A lease is not stolen after PID death: its descendants may still be alive.
|
|
18
|
+
* Only Host cleanup after confirmed process-range exit may release it. */
|
|
19
|
+
export async function assertOwnAccountLeaseIdle(root) {
|
|
20
|
+
await privateRoot(root)
|
|
21
|
+
try {
|
|
22
|
+
await lstat(join(root, '.lease'))
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (error?.code === 'ENOENT') return
|
|
25
|
+
throw error
|
|
26
|
+
}
|
|
27
|
+
throw new Error(OWN_ACCOUNT_BUSY)
|
|
28
|
+
}
|
|
29
|
+
export async function acquireOwnAccountLease(root, nonce) {
|
|
30
|
+
if (!nonceValid(nonce)) throw new Error('Invalid account lease')
|
|
31
|
+
await privateRoot(root)
|
|
32
|
+
const lock = join(root, '.lease')
|
|
33
|
+
const pending = join(root, `.lease-${nonce}.pending`)
|
|
34
|
+
await assertOwnAccountLeaseIdle(root)
|
|
35
|
+
await mkdir(pending, { mode: 0o700 })
|
|
36
|
+
try {
|
|
37
|
+
await writeFile(join(pending, 'owner.json'), JSON.stringify({ nonce, pid: process.pid }), {
|
|
38
|
+
flag: 'wx',
|
|
39
|
+
mode: 0o600,
|
|
40
|
+
})
|
|
41
|
+
// Publish a complete, nonempty owner directory in one filesystem operation.
|
|
42
|
+
// A killed initializer leaves only its nonce-specific pending directory.
|
|
43
|
+
await rename(pending, lock)
|
|
44
|
+
} catch (error) {
|
|
45
|
+
await rm(pending, { recursive: true, force: true })
|
|
46
|
+
if (error?.code === 'EEXIST' || error?.code === 'ENOTEMPTY') throw new Error(OWN_ACCOUNT_BUSY)
|
|
47
|
+
throw error
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function ownsOwnAccountLease(root, nonce) {
|
|
51
|
+
if (!nonceValid(nonce)) throw new Error('Invalid account lease')
|
|
52
|
+
await privateRoot(root)
|
|
53
|
+
let handle
|
|
54
|
+
try {
|
|
55
|
+
const lock = await lstat(join(root, '.lease'))
|
|
56
|
+
if (!lock.isDirectory() || lock.isSymbolicLink() || (process.platform !== 'win32' && (lock.mode & 0o077)))
|
|
57
|
+
throw new Error('账号使用状态无法确认')
|
|
58
|
+
handle = await open(
|
|
59
|
+
join(root, '.lease/owner.json'),
|
|
60
|
+
constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,
|
|
61
|
+
)
|
|
62
|
+
const info = await handle.stat()
|
|
63
|
+
if (!info.isFile() || info.nlink !== 1 || (process.platform !== 'win32' && (info.mode & 0o077)) || info.size > 1024)
|
|
64
|
+
throw new Error('账号使用状态无法确认')
|
|
65
|
+
return JSON.parse(await handle.readFile('utf8')).nonce === nonce
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (error?.code === 'ENOENT') return false
|
|
68
|
+
throw error
|
|
69
|
+
} finally {
|
|
70
|
+
await handle?.close()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export async function releaseOwnAccountLease(root, nonce) {
|
|
74
|
+
if (!nonceValid(nonce)) throw new Error('Invalid account lease')
|
|
75
|
+
await privateRoot(root)
|
|
76
|
+
// Safe only at this API's contract boundary: Host confirmed this nonce's whole
|
|
77
|
+
// process range exited, including an initializer killed before publication.
|
|
78
|
+
await rm(join(root, `.lease-${nonce}.pending`), { recursive: true, force: true })
|
|
79
|
+
const lock = join(root, '.lease')
|
|
80
|
+
let handle
|
|
81
|
+
try {
|
|
82
|
+
const directory = await lstat(lock)
|
|
83
|
+
if (!directory.isDirectory() || directory.isSymbolicLink() || (process.platform !== 'win32' && (directory.mode & 0o077)))
|
|
84
|
+
throw new Error('账号使用状态无法安全清理')
|
|
85
|
+
handle = await open(
|
|
86
|
+
join(lock, 'owner.json'),
|
|
87
|
+
constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,
|
|
88
|
+
)
|
|
89
|
+
const info = await handle.stat()
|
|
90
|
+
if (!info.isFile() || info.nlink !== 1 || (process.platform !== 'win32' && (info.mode & 0o077)) || info.size > 1024)
|
|
91
|
+
throw new Error('账号使用状态无法安全清理')
|
|
92
|
+
const owner = JSON.parse(await handle.readFile('utf8'))
|
|
93
|
+
if (owner.nonce !== nonce) return
|
|
94
|
+
const current = await lstat(lock)
|
|
95
|
+
if (current.dev !== directory.dev || current.ino !== directory.ino || current.isSymbolicLink())
|
|
96
|
+
throw new Error('账号使用状态在清理前改变')
|
|
97
|
+
await handle.close()
|
|
98
|
+
handle = undefined
|
|
99
|
+
await rm(lock, { recursive: true })
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error?.code !== 'ENOENT') throw error
|
|
102
|
+
} finally {
|
|
103
|
+
await handle?.close()
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// One verified Pi package for the Host, account UI, native catalog and RPC entry.
|
|
2
|
+
// Resolve installation metadata only; never execute an arbitrary shell launcher.
|
|
3
|
+
import { readFileSync, realpathSync, statSync } from 'node:fs'
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
6
|
+
import { resolveNativeEntry } from './native-launch.mjs'
|
|
7
|
+
|
|
8
|
+
const versions = new Set(['1.0.2', '1.0.4', '1.1.0'])
|
|
9
|
+
const failure = () =>
|
|
10
|
+
Object.assign(
|
|
11
|
+
new Error('Pi 安装身份或原生接口不匹配;需要 Pi Coding Agent 1.0.2、1.0.4 或 1.1.0,不能使用 OMP 入口'),
|
|
12
|
+
{ code: 'CLI_IDENTITY_MISMATCH' },
|
|
13
|
+
)
|
|
14
|
+
function metadata(path) {
|
|
15
|
+
const info = statSync(path)
|
|
16
|
+
if (!info.isFile() || info.size > 128 * 1024) throw failure()
|
|
17
|
+
return readFileSync(path, 'utf8')
|
|
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
|
+
}
|
|
40
|
+
|
|
41
|
+
export function inspectPiInstallation(entry) {
|
|
42
|
+
try {
|
|
43
|
+
let executable = realpathSync(entry)
|
|
44
|
+
if (
|
|
45
|
+
['pi', 'pi.cmd', 'pi.bat'].includes(basename(executable).toLowerCase()) &&
|
|
46
|
+
basename(dirname(executable)) === 'bin'
|
|
47
|
+
) {
|
|
48
|
+
const launcher = metadata(executable)
|
|
49
|
+
if (launcher.includes('PI_MANAGED_INSTALL_ROOT=') && launcher.includes('install/current-version')) {
|
|
50
|
+
const install = join(dirname(dirname(executable)), 'install')
|
|
51
|
+
const version = metadata(join(install, 'current-version')).trim()
|
|
52
|
+
if (!versions.has(version)) throw failure()
|
|
53
|
+
executable = realpathSync(
|
|
54
|
+
join(install, 'releases', version, 'node_modules/@earendil-works/pi-coding-agent/dist/cli.js'),
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
executable = realpathSync(resolveNativeEntry(executable))
|
|
59
|
+
if (basename(executable).toLowerCase() === 'pi-launcher.js' && basename(dirname(executable)) === 'bin')
|
|
60
|
+
executable = windowsManagedEntry(executable)
|
|
61
|
+
let directory = dirname(executable)
|
|
62
|
+
for (let depth = 0; depth < 5; depth++, directory = dirname(directory)) {
|
|
63
|
+
let manifest
|
|
64
|
+
try {
|
|
65
|
+
manifest = JSON.parse(metadata(join(directory, 'package.json')))
|
|
66
|
+
} catch {
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
if (manifest.name !== '@earendil-works/pi-coding-agent' || !versions.has(manifest.version))
|
|
70
|
+
throw failure()
|
|
71
|
+
const dist = join(directory, 'dist')
|
|
72
|
+
if (![join(dist, 'cli.js'), join(dist, 'bundle/cli.js')].includes(executable)) throw failure()
|
|
73
|
+
for (const relative of ['cli.js', 'index.js', 'core/model-runtime.js', 'core/auth-storage.js']) {
|
|
74
|
+
if (!statSync(join(dist, relative)).isFile()) throw failure()
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
root: directory,
|
|
78
|
+
dist,
|
|
79
|
+
version: manifest.version,
|
|
80
|
+
executable: realpathSync(join(dist, 'cli.js')),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch {
|
|
84
|
+
throw failure()
|
|
85
|
+
}
|
|
86
|
+
throw failure()
|
|
87
|
+
}
|
package/lib/pi-login.mjs
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Pi 1.0.2 / 1.0.4 native UI compatibility entry. No initial prompt, injected keystrokes,
|
|
2
|
+
// model request, or subscription validation: open its own provider login menu.
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
5
|
+
import { inspectPiInstallation } from './pi-installation.mjs'
|
|
6
|
+
|
|
7
|
+
process.umask(0o077)
|
|
8
|
+
try {
|
|
9
|
+
const executable = process.argv[2]
|
|
10
|
+
const action = process.argv[3] ?? 'login'
|
|
11
|
+
if (!['login', 'logout'].includes(action)) throw new Error('Invalid account action')
|
|
12
|
+
if (!executable) throw new Error('Missing Pi entry')
|
|
13
|
+
const { dist } = inspectPiInstallation(executable)
|
|
14
|
+
const {
|
|
15
|
+
createAgentSessionServices,
|
|
16
|
+
createAgentSessionFromServices,
|
|
17
|
+
createAgentSessionRuntime,
|
|
18
|
+
SessionManager,
|
|
19
|
+
InteractiveMode,
|
|
20
|
+
ModelRuntime,
|
|
21
|
+
} = await import(pathToFileURL(join(dist, 'index.js')).href)
|
|
22
|
+
if (
|
|
23
|
+
[
|
|
24
|
+
createAgentSessionServices,
|
|
25
|
+
createAgentSessionFromServices,
|
|
26
|
+
createAgentSessionRuntime,
|
|
27
|
+
SessionManager?.inMemory,
|
|
28
|
+
InteractiveMode,
|
|
29
|
+
].some((entry) => typeof entry !== 'function') ||
|
|
30
|
+
['init', action === 'logout' ? 'showOAuthSelector' : 'handleLoginCommand', 'run'].some(
|
|
31
|
+
(method) => typeof InteractiveMode.prototype[method] !== 'function',
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
throw new Error('Unsupported Pi login SDK capabilities')
|
|
35
|
+
const cwd = process.cwd(),
|
|
36
|
+
agentDir = process.env.PI_CODING_AGENT_DIR
|
|
37
|
+
const runtime = await createAgentSessionRuntime(
|
|
38
|
+
async ({ cwd, agentDir, sessionManager }) => {
|
|
39
|
+
const services = await createAgentSessionServices({
|
|
40
|
+
cwd,
|
|
41
|
+
agentDir,
|
|
42
|
+
...(action === 'logout'
|
|
43
|
+
? {
|
|
44
|
+
modelRuntime: await ModelRuntime.create({
|
|
45
|
+
authPath: process.argv[4],
|
|
46
|
+
modelsPath: null,
|
|
47
|
+
modelsStorePath: join(agentDir, 'models-store.json'),
|
|
48
|
+
refreshOnCreate: false,
|
|
49
|
+
allowModelNetwork: false,
|
|
50
|
+
}),
|
|
51
|
+
}
|
|
52
|
+
: {}),
|
|
53
|
+
resourceLoaderOptions: {
|
|
54
|
+
noExtensions: true,
|
|
55
|
+
noSkills: true,
|
|
56
|
+
noPromptTemplates: true,
|
|
57
|
+
noThemes: true,
|
|
58
|
+
noContextFiles: true,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
const created = await createAgentSessionFromServices({
|
|
62
|
+
services,
|
|
63
|
+
sessionManager,
|
|
64
|
+
noTools: 'all',
|
|
65
|
+
tools: [],
|
|
66
|
+
})
|
|
67
|
+
return { ...created, services, diagnostics: services.diagnostics }
|
|
68
|
+
},
|
|
69
|
+
{ cwd, agentDir, sessionManager: SessionManager.inMemory(cwd) },
|
|
70
|
+
)
|
|
71
|
+
const ui = new InteractiveMode(runtime)
|
|
72
|
+
await ui.init()
|
|
73
|
+
if (action === 'logout') await ui.showOAuthSelector('logout')
|
|
74
|
+
else await ui.handleLoginCommand()
|
|
75
|
+
await ui.run()
|
|
76
|
+
} catch {
|
|
77
|
+
// Native errors can contain authentication data. Keep this boundary generic.
|
|
78
|
+
process.stderr.write('无法打开 Pi 原生账号界面,请检查 Pi Coding Agent 安装与受支持的原生接口。\n')
|
|
79
|
+
process.exitCode = 1
|
|
80
|
+
}
|