@dsh-workbench/shared 0.3.11 → 1.0.0-beta.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/LICENSE +21 -0
- package/README.md +97 -0
- package/dist/api/blocker-table.d.ts +3 -3
- package/dist/api/blocker-table.js +47 -23
- package/dist/api/dsh.d.ts +1 -1
- package/dist/api/workbench-client.js +2 -1
- package/dist/components/Activity.css +0 -1
- package/dist/components/ApprovalDetail.css +1 -2
- package/dist/components/ApprovalDetail.d.ts +1 -1
- package/dist/components/ApprovalPlan.css +0 -1
- package/dist/components/Card.css +0 -1
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/CardBoundary.d.ts +1 -1
- package/dist/components/CardHead.css +0 -1
- package/dist/components/CardHead.d.ts +1 -1
- package/dist/components/ChoiceGroup.css +1 -2
- package/dist/components/ConfirmBar.d.ts +1 -1
- package/dist/components/ConfirmBar.js +1 -1
- package/dist/components/Form.css +1 -1
- package/dist/components/FormActions.js +1 -1
- package/dist/components/LongTaskRow.css +1 -2
- package/dist/components/LongTaskRow.d.ts +1 -1
- package/dist/components/LongTaskRow.js +1 -1
- package/dist/components/Note.css +0 -1
- package/dist/components/ResourceStates.css +0 -1
- package/dist/components/SettingsSection.css +0 -1
- package/dist/components/Spinner.css +0 -1
- package/dist/components/Stack.css +0 -1
- package/dist/components/StateBlock.css +0 -1
- package/dist/components/board-module.d.ts +3 -3
- package/dist/components/icons.d.ts +1 -1
- package/dist/components/icons.js +11 -4
- package/dist/config/constants.d.ts +2 -2
- package/dist/config/constants.js +2 -2
- package/dist/contract/adapters.d.ts +10 -0
- package/dist/contract/cli.d.ts +3 -0
- package/dist/contract/data.d.ts +126 -0
- package/dist/{api → contract}/facts.d.ts +12 -3
- package/dist/{api → contract}/facts.js +19 -1
- package/dist/contract/needs.d.ts +132 -0
- package/dist/contract/needs.js +232 -0
- package/dist/contract/plugin-source.d.ts +1 -0
- package/dist/contract/protocol/bundle-install.d.ts +136 -0
- package/dist/contract/protocol/bundle-install.js +12 -0
- package/dist/contract/protocol/config-sync.d.ts +55 -0
- package/dist/contract/protocol/config-sync.js +2 -0
- package/dist/contract/protocol/diy.d.ts +4 -0
- package/dist/contract/protocol/diy.js +4 -0
- package/dist/contract/protocol/dsh-plugins.d.ts +11 -0
- package/dist/contract/protocol/dsh-plugins.js +1 -0
- package/dist/contract/protocol/long-tasks.d.ts +13 -0
- package/dist/contract/protocol/long-tasks.js +1 -0
- package/dist/contract/protocol/market-index.d.ts +195 -0
- package/dist/contract/protocol/market-index.js +7 -0
- package/dist/contract/protocol/platform.d.ts +44 -0
- package/dist/contract/protocol/platform.js +1 -0
- package/dist/contract/protocol/preflight-wake.d.ts +7 -0
- package/dist/contract/protocol/preflight-wake.js +1 -0
- package/dist/contract/protocol/profile-create.d.ts +30 -0
- package/dist/contract/protocol/profile-create.js +2 -0
- package/dist/contract/protocol/sidebar-edition.d.ts +14 -0
- package/dist/contract/protocol/sidebar-edition.js +2 -0
- package/dist/contract/protocol/unit-manifests.d.ts +6 -0
- package/dist/contract/protocol/unit-manifests.js +1 -0
- package/dist/contract/unit-board.js +1 -0
- package/dist/contract/unit-capability.d.ts +34 -0
- package/dist/contract/unit-capability.js +1 -0
- package/dist/{host → contract}/unit-declaration.d.ts +7 -5
- package/dist/{api → contract}/unit-declaration.js +19 -13
- package/dist/contract/unit.d.ts +57 -0
- package/dist/contract/unit.js +24 -0
- package/dist/contract/workbench-contract.d.ts +15 -0
- package/dist/contract/workbench-contract.js +1 -0
- package/dist/host/adapter-set.d.ts +26 -0
- package/dist/host/adapter-set.js +243 -0
- package/dist/host/adapter-types.d.ts +79 -0
- package/dist/host/adapter-types.js +23 -0
- package/dist/host/adapters.d.ts +22 -0
- package/dist/host/adapters.js +9 -0
- package/dist/host/browser.d.ts +3 -0
- package/dist/host/browser.js +3 -0
- package/dist/host/changelog.d.ts +21 -0
- package/dist/host/changelog.js +188 -0
- package/dist/host/channel-plugin.d.ts +15 -0
- package/dist/host/channel-plugin.js +42 -0
- package/dist/host/cli.d.ts +4 -0
- package/dist/host/cli.js +7 -0
- package/dist/host/command-name.d.ts +1 -0
- package/dist/host/command-name.js +14 -0
- package/dist/host/data-error.d.ts +13 -0
- package/dist/host/data-error.js +20 -0
- package/dist/host/data-root.js +3 -0
- package/dist/host/diy-debug.js +2 -1
- package/dist/host/diy-record.js +2 -1
- package/dist/host/facts.d.ts +1 -1
- package/dist/host/facts.js +3 -2
- package/dist/host/git.js +2 -2
- package/dist/host/http.js +1 -1
- package/dist/host/lib-capabilities.d.ts +22 -0
- package/dist/host/lib-capabilities.js +37 -0
- package/dist/host/lib-install.d.ts +9 -0
- package/dist/host/lib-install.js +10 -0
- package/dist/host/long-task.d.ts +1 -1
- package/dist/host/long-task.js +4 -3
- package/dist/host/market-index.d.ts +22 -0
- package/dist/host/market-index.js +420 -0
- package/dist/host/needs.d.ts +4 -0
- package/dist/host/needs.js +2 -0
- package/dist/host/patch.d.ts +9 -0
- package/dist/host/patch.js +78 -0
- package/dist/host/paths.d.ts +12 -0
- package/dist/host/paths.js +28 -0
- package/dist/host/platform.d.ts +14 -0
- package/dist/host/platform.js +40 -0
- package/dist/host/plugin-source.d.ts +8 -6
- package/dist/host/plugin-source.js +43 -33
- package/dist/host/plugin.d.ts +4 -0
- package/dist/host/plugin.js +39 -0
- package/dist/host/port-types.d.ts +191 -0
- package/dist/host/port-types.js +1 -0
- package/dist/host/preflight.d.ts +28 -0
- package/dist/host/preflight.js +1 -0
- package/dist/host/profile-port.d.ts +4 -0
- package/dist/host/profile-port.js +4 -0
- package/dist/host/provision.d.ts +2 -2
- package/dist/host/provision.js +5 -3
- package/dist/host/recipe-rules.d.ts +5 -0
- package/dist/host/recipe-rules.js +382 -0
- package/dist/host/recipe.d.ts +14 -0
- package/dist/host/recipe.js +283 -0
- package/dist/host/shell-text.d.ts +9 -2
- package/dist/host/shell-text.js +15 -3
- package/dist/host/shell.d.ts +2 -0
- package/dist/host/shell.js +7 -0
- package/dist/host/spec.d.ts +8 -0
- package/dist/host/spec.js +24 -0
- package/dist/host/supply-index.d.ts +34 -0
- package/dist/host/supply-index.js +202 -0
- package/dist/host/supply-port.d.ts +5 -0
- package/dist/host/supply-port.js +39 -0
- package/dist/host/time.d.ts +12 -0
- package/dist/host/time.js +19 -0
- package/dist/host/tokens.d.ts +26 -0
- package/dist/host/tokens.js +154 -0
- package/dist/host/tool-kit.js +1 -1
- package/dist/host/unit-face.d.ts +5 -4
- package/dist/host/version-range.d.ts +2 -0
- package/dist/host/version-range.js +73 -0
- package/dist/host/version.d.ts +3 -0
- package/dist/host/version.js +29 -0
- package/dist/host.d.ts +47 -16
- package/dist/host.js +47 -16
- package/dist/i18n/en.js +224 -71
- package/dist/i18n/index.d.ts +298 -73
- package/dist/i18n/merge.js +1 -1
- package/dist/i18n/need-text.d.ts +26 -0
- package/dist/i18n/need-text.js +26 -0
- package/dist/i18n/settings.d.ts +70 -0
- package/dist/i18n/settings.js +136 -0
- package/dist/i18n/zh.d.ts +297 -72
- package/dist/i18n/zh.js +226 -73
- package/dist/index.d.ts +23 -7
- package/dist/index.js +23 -7
- package/dist/{api → lib}/dsh-client.d.ts +11 -1
- package/dist/lib/dsh-client.js +1 -0
- package/dist/lib/market-index.d.ts +7 -0
- package/dist/lib/market-index.js +25 -0
- package/dist/lib/react.d.ts +0 -40
- package/dist/lib/react.js +0 -36
- package/dist/lib/ui.js +3 -9
- package/dist/testing/data.d.ts +13 -0
- package/dist/testing/data.js +46 -0
- package/dist/testing/declaration.d.ts +1 -1
- package/dist/testing/declaration.js +1 -1
- package/dist/testing/host-fakes.d.ts +0 -13
- package/dist/testing/host-fakes.js +0 -24
- package/dist/testing/needs.d.ts +16 -0
- package/dist/testing/needs.js +46 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +2 -0
- package/package.json +18 -2
- package/src/api/__tests__/blocker-table.test.ts +135 -86
- package/src/api/blocker-table.ts +52 -26
- package/src/api/dsh.ts +1 -1
- package/src/api/host-write.ts +0 -1
- package/src/api/workbench-client.test.ts +2 -2
- package/src/api/workbench-client.ts +2 -1
- package/src/components/Activity.css +0 -1
- package/src/components/ApprovalDetail.css +1 -2
- package/src/components/ApprovalDetail.tsx +1 -1
- package/src/components/ApprovalPlan.css +0 -1
- package/src/components/Card.css +0 -1
- package/src/components/Card.tsx +1 -1
- package/src/components/CardAnchor.test.tsx +1 -1
- package/src/components/CardBoundary.tsx +1 -1
- package/src/components/CardHead.css +0 -1
- package/src/components/CardHead.tsx +1 -1
- package/src/components/ChoiceGroup.css +1 -2
- package/src/components/ConfirmBar.tsx +1 -1
- package/src/components/Form.css +1 -1
- package/src/components/Form.tsx +1 -1
- package/src/components/FormActions.tsx +1 -1
- package/src/components/LongTaskRow.css +1 -2
- package/src/components/LongTaskRow.tsx +1 -1
- package/src/components/Note.css +0 -1
- package/src/components/ResourceStates.css +0 -1
- package/src/components/SelectFieldNodes.tsx +0 -1
- package/src/components/SettingsSection.css +0 -1
- package/src/components/Spinner.css +0 -1
- package/src/components/Stack.css +0 -1
- package/src/components/StateBlock.css +0 -1
- package/src/components/__tests__/long-task-row.test.ts +1 -1
- package/src/components/board-module.ts +3 -3
- package/src/components/icons.tsx +16 -8
- package/src/config/constants.ts +2 -2
- package/src/{api → contract}/__tests__/facts.test.ts +73 -25
- package/src/{api → contract}/__tests__/long-task.test.ts +3 -3
- package/src/contract/__tests__/needs.test.ts +74 -0
- package/src/{host → contract}/__tests__/unit-declaration.test.ts +1 -1
- package/src/contract/__tests__/unit.test.ts +43 -0
- package/src/contract/adapters.ts +9 -0
- package/src/contract/cli.ts +3 -0
- package/src/contract/data.ts +145 -0
- package/src/{api → contract}/facts.ts +34 -4
- package/src/contract/needs.ts +331 -0
- package/src/contract/plugin-source.ts +1 -0
- package/src/contract/protocol/bundle-install.ts +198 -0
- package/src/contract/protocol/config-sync.ts +65 -0
- package/src/contract/protocol/diy.ts +4 -0
- package/src/contract/protocol/dsh-plugins.ts +13 -0
- package/src/contract/protocol/long-tasks.ts +16 -0
- package/src/contract/protocol/market-index.ts +246 -0
- package/src/contract/protocol/platform.ts +47 -0
- package/src/contract/protocol/preflight-wake.ts +17 -0
- package/src/contract/protocol/profile-create.ts +36 -0
- package/src/contract/protocol/sidebar-edition.ts +19 -0
- package/src/contract/protocol/unit-manifests.ts +8 -0
- package/src/{api → contract}/unit-board.ts +0 -1
- package/src/contract/unit-capability.ts +37 -0
- package/src/{host → contract}/unit-declaration.ts +22 -18
- package/src/contract/unit.ts +76 -0
- package/src/contract/workbench-contract.ts +30 -0
- package/src/host/__tests__/adapter-set.test.ts +228 -0
- package/src/host/__tests__/adapters.test.ts +27 -0
- package/src/host/__tests__/changelog.test.ts +102 -0
- package/src/host/__tests__/command-name.test.ts +16 -0
- package/src/host/__tests__/diy-record.test.ts +3 -3
- package/src/host/__tests__/facts.test.ts +5 -5
- package/src/host/__tests__/long-task.test.ts +1 -1
- package/src/host/__tests__/market-index.test.ts +179 -0
- package/src/host/__tests__/plugin-ledger.test.ts +10 -10
- package/src/host/__tests__/provision.test.ts +7 -6
- package/src/host/__tests__/shell-text.test.ts +16 -1
- package/src/host/__tests__/supply-index.test.ts +137 -0
- package/src/host/__tests__/tokens.test.ts +110 -0
- package/src/host/__tests__/version-range.test.ts +21 -0
- package/src/host/adapter-set.ts +298 -0
- package/src/host/adapter-types.ts +105 -0
- package/src/host/adapters.ts +44 -0
- package/src/host/browser.ts +3 -0
- package/src/host/changelog.ts +212 -0
- package/src/host/channel-plugin.ts +55 -0
- package/src/host/cli.ts +11 -0
- package/src/host/command-name.ts +15 -0
- package/src/host/data-error.ts +21 -0
- package/src/host/data-root.ts +2 -0
- package/src/host/diy-debug.ts +2 -1
- package/src/host/diy-record.ts +2 -1
- package/src/host/facts.ts +4 -3
- package/src/host/git.ts +2 -2
- package/src/host/http.ts +1 -1
- package/src/host/lib-capabilities.ts +52 -0
- package/src/host/lib-install.ts +18 -0
- package/src/host/long-task.ts +4 -3
- package/src/host/market-index.ts +413 -0
- package/src/host/needs.ts +6 -0
- package/src/host/patch.ts +93 -0
- package/src/host/paths.ts +37 -0
- package/src/host/platform.ts +48 -0
- package/src/host/plugin-source.ts +48 -56
- package/src/host/plugin.ts +39 -0
- package/src/host/port-types.ts +222 -0
- package/src/host/preflight.ts +31 -0
- package/src/host/profile-port.ts +9 -0
- package/src/host/provision.ts +7 -4
- package/src/host/recipe-rules.ts +330 -0
- package/src/host/recipe.test.ts +334 -0
- package/src/host/recipe.ts +323 -0
- package/src/host/shell-text.ts +15 -3
- package/src/host/shell.ts +9 -0
- package/src/host/spec.ts +32 -0
- package/src/host/supply-index.ts +256 -0
- package/src/host/supply-port.ts +45 -0
- package/src/host/time.ts +22 -0
- package/src/host/tokens.ts +186 -0
- package/src/host/tool-kit.ts +1 -1
- package/src/host/unit-face.ts +5 -4
- package/src/host/version-range.ts +71 -0
- package/src/host/version.ts +30 -0
- package/src/host.ts +47 -16
- package/src/i18n/en.ts +224 -71
- package/src/i18n/merge.test.ts +6 -6
- package/src/i18n/merge.ts +1 -1
- package/src/i18n/need-text.ts +27 -0
- package/src/i18n/settings.ts +141 -0
- package/src/i18n/zh.ts +227 -73
- package/src/index.ts +23 -7
- package/src/lib/blocker-action.ts +0 -1
- package/src/{api → lib}/dsh-client.ts +12 -1
- package/src/lib/market-index.ts +36 -0
- package/src/lib/react.ts +0 -40
- package/src/lib/text.test.ts +2 -2
- package/src/lib/ui.test.ts +6 -5
- package/src/lib/ui.ts +3 -9
- package/src/testing/__tests__/declaration.test.ts +1 -1
- package/src/testing/data.ts +66 -0
- package/src/testing/declaration.ts +2 -2
- package/src/testing/host-fakes.ts +0 -38
- package/src/testing/needs.ts +80 -0
- package/src/testing.ts +2 -0
- package/dist/api/unit-declaration.d.ts +0 -65
- package/dist/api/unit.d.ts +0 -85
- package/dist/api/unit.js +0 -8
- package/dist/api/workbench-contract.d.ts +0 -44
- package/dist/components/MockStrip.css +0 -39
- package/dist/components/MockStrip.d.ts +0 -15
- package/dist/components/MockStrip.js +0 -8
- package/dist/components/SaveBar.css +0 -7
- package/dist/host/browser-find.d.ts +0 -38
- package/dist/host/browser-find.js +0 -242
- package/dist/host/browser-read.d.ts +0 -28
- package/dist/host/browser-read.js +0 -243
- package/dist/host/opencli-runtime.d.ts +0 -25
- package/dist/host/opencli-runtime.js +0 -56
- package/dist/host/opencli-window.d.ts +0 -2
- package/dist/host/opencli-window.js +0 -8
- package/dist/host/opencli.d.ts +0 -37
- package/dist/host/opencli.js +0 -155
- package/dist/host/unit-declaration.js +0 -213
- package/dist/host/unit-libs.d.ts +0 -37
- package/dist/lib/diy-debug.d.ts +0 -80
- package/dist/lib/diy-debug.js +0 -88
- package/dist/lib/diy-record.d.ts +0 -135
- package/dist/lib/diy-record.js +0 -162
- package/dist/lib/lint-probe.d.ts +0 -1
- package/dist/lib/lint-probe.js +0 -6
- package/dist/lib/panel-state.d.ts +0 -120
- package/dist/lib/panel-state.js +0 -176
- package/src/api/__tests__/unit.test.ts +0 -38
- package/src/api/unit.ts +0 -93
- package/src/api/workbench-contract.ts +0 -59
- package/src/host/__tests__/browser-find.test.ts +0 -330
- package/src/host/__tests__/browser-read.test.ts +0 -228
- package/src/host/__tests__/opencli-runtime.test.ts +0 -46
- package/src/host/browser-find.ts +0 -311
- package/src/host/browser-read.ts +0 -311
- package/src/host/opencli-runtime.ts +0 -79
- package/src/host/opencli-window.ts +0 -11
- package/src/host/opencli.ts +0 -213
- package/src/host/unit-libs.ts +0 -41
- /package/dist/{api/blocker.js → contract/adapters.js} +0 -0
- /package/dist/{api → contract}/blocker.d.ts +0 -0
- /package/dist/{api/dsh-client.js → contract/blocker.js} +0 -0
- /package/dist/{api/unit-board.js → contract/cli.js} +0 -0
- /package/dist/{api/workbench-contract.js → contract/data.js} +0 -0
- /package/dist/{api → contract}/long-task.d.ts +0 -0
- /package/dist/{api → contract}/long-task.js +0 -0
- /package/dist/{host/unit-libs.js → contract/plugin-source.js} +0 -0
- /package/dist/{api → contract}/unit-board.d.ts +0 -0
- /package/src/{api → contract}/blocker.ts +0 -0
- /package/src/{api → contract}/long-task.ts +0 -0
package/src/host/browser-read.ts
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import type { DshShell } from '../api/dsh.js';
|
|
2
|
-
import { parseJsonLoose } from '../lib/text.js';
|
|
3
|
-
import { tailLines } from './shell-text.js';
|
|
4
|
-
import { backgroundLine } from './opencli-window.js';
|
|
5
|
-
import { runShell } from './shell.js';
|
|
6
|
-
|
|
7
|
-
export const BROWSER_SCREEN_SESSION = 'workbench';
|
|
8
|
-
export const BROWSER_READ_SESSION = 'workbench-read';
|
|
9
|
-
export const BROWSER_RECORD_SESSION = 'workbench-rec';
|
|
10
|
-
|
|
11
|
-
const DEFAULT_TIMEOUT_MS = 25000;
|
|
12
|
-
const DEFAULT_RETRY_MS = 1200;
|
|
13
|
-
const DEFAULT_SETTLE_MS = 900;
|
|
14
|
-
|
|
15
|
-
const AUTH_CODES = new Set([
|
|
16
|
-
'NOT_LOGGED_IN',
|
|
17
|
-
'NOT_LOGIN',
|
|
18
|
-
'AUTH_FAILED',
|
|
19
|
-
'AUTH_REQUIRED',
|
|
20
|
-
'UNAUTHENTICATED',
|
|
21
|
-
'UNAUTHORIZED',
|
|
22
|
-
'TOKEN_EXPIRED',
|
|
23
|
-
]);
|
|
24
|
-
|
|
25
|
-
const PERMISSION_CODES = new Set(['FORBIDDEN', 'PERMISSION_DENIED', 'NO_PERMISSION', 'EACCES']);
|
|
26
|
-
|
|
27
|
-
export interface BrowserReadResult {
|
|
28
|
-
readonly ok: boolean;
|
|
29
|
-
readonly code: string;
|
|
30
|
-
readonly at: number;
|
|
31
|
-
readonly value?: unknown;
|
|
32
|
-
readonly pageCode?: string;
|
|
33
|
-
readonly message: string;
|
|
34
|
-
readonly hint?: string;
|
|
35
|
-
readonly detail?: string;
|
|
36
|
-
readonly helpUrl?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface BrowserReadOptions {
|
|
40
|
-
readonly shell: DshShell;
|
|
41
|
-
readonly script: string;
|
|
42
|
-
readonly url?: string;
|
|
43
|
-
readonly loginUrl?: string;
|
|
44
|
-
readonly command?: string;
|
|
45
|
-
readonly session?: string;
|
|
46
|
-
readonly open?: boolean;
|
|
47
|
-
readonly timeoutMs?: number;
|
|
48
|
-
readonly retryMs?: number;
|
|
49
|
-
readonly settleMs?: number;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
53
|
-
return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : undefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function textOf(value: unknown): string {
|
|
57
|
-
return typeof value === 'string' ? value.trim() : '';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function codeOf(page: unknown): string {
|
|
61
|
-
const record = asRecord(page);
|
|
62
|
-
return record === undefined ? '' : textOf(record['code']);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function messageOf(page: unknown): string {
|
|
66
|
-
const record = asRecord(page);
|
|
67
|
-
return record === undefined ? '' : textOf(record['message']);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function hintOf(page: unknown): string | undefined {
|
|
71
|
-
const record = asRecord(page);
|
|
72
|
-
const hint = record === undefined ? '' : textOf(record['hint']);
|
|
73
|
-
return hint === '' ? undefined : hint;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function pageFailed(page: unknown): boolean {
|
|
77
|
-
const record = asRecord(page);
|
|
78
|
-
return record !== undefined && record['ok'] === false;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function blockerFor(code: string): 'auth-required' | 'permission-denied' | undefined {
|
|
82
|
-
const normalized = code.trim().toUpperCase().replace(/-/g, '_');
|
|
83
|
-
if (normalized === '') return undefined;
|
|
84
|
-
const matches = (set: Set<string>): boolean =>
|
|
85
|
-
[...set].some((one) => normalized === one || normalized.endsWith(`_${one}`));
|
|
86
|
-
if (matches(PERMISSION_CODES)) return 'permission-denied';
|
|
87
|
-
if (matches(AUTH_CODES) || normalized.includes('UNAUTH')) return 'auth-required';
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function sleep(ms: number): Promise<void> {
|
|
92
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
type Attempt =
|
|
96
|
-
| { readonly kind: 'value'; readonly value: unknown }
|
|
97
|
-
| { readonly kind: 'failure'; readonly result: Omit<BrowserReadResult, 'at'> };
|
|
98
|
-
|
|
99
|
-
type ShellOutcome =
|
|
100
|
-
| { readonly kind: 'ok'; readonly stdout: string }
|
|
101
|
-
| { readonly kind: 'timeout'; readonly detail: string; readonly stderr: string }
|
|
102
|
-
| { readonly kind: 'failed'; readonly detail: string; readonly stderr: string };
|
|
103
|
-
|
|
104
|
-
async function shellRun(
|
|
105
|
-
shell: DshShell,
|
|
106
|
-
command: string,
|
|
107
|
-
argv: readonly string[],
|
|
108
|
-
timeoutMs: number,
|
|
109
|
-
): Promise<ShellOutcome> {
|
|
110
|
-
const result = await runShell(shell, backgroundLine(command, argv), { timeoutMs });
|
|
111
|
-
if (result.timedOut) {
|
|
112
|
-
return { kind: 'timeout', detail: `没在 ${Math.round(timeoutMs / 1000)} 秒内返回`, stderr: result.stderr };
|
|
113
|
-
}
|
|
114
|
-
if (result.exitCode !== 0) {
|
|
115
|
-
return { kind: 'failed', detail: tailLines(result.stderr || result.stdout, 4), stderr: result.stderr };
|
|
116
|
-
}
|
|
117
|
-
return { kind: 'ok', stdout: result.stdout };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async function evalOnce(
|
|
121
|
-
shell: DshShell,
|
|
122
|
-
command: string,
|
|
123
|
-
session: string,
|
|
124
|
-
script: string,
|
|
125
|
-
timeoutMs: number,
|
|
126
|
-
retryMs: number,
|
|
127
|
-
): Promise<Attempt> {
|
|
128
|
-
const run = async (): Promise<Attempt> => {
|
|
129
|
-
let attempt: Awaited<ReturnType<typeof shellRun>>;
|
|
130
|
-
try {
|
|
131
|
-
attempt = await shellRun(
|
|
132
|
-
shell,
|
|
133
|
-
command,
|
|
134
|
-
['browser', session, 'eval', script, '--window', 'background'],
|
|
135
|
-
timeoutMs,
|
|
136
|
-
);
|
|
137
|
-
} catch (error) {
|
|
138
|
-
return {
|
|
139
|
-
kind: 'failure',
|
|
140
|
-
result: {
|
|
141
|
-
ok: false,
|
|
142
|
-
code: 'cli-exec-failed',
|
|
143
|
-
message: '读不了:后台服务起不来',
|
|
144
|
-
hint: '确认后台服务能跑起来,再重试',
|
|
145
|
-
detail: error instanceof Error ? error.message : String(error),
|
|
146
|
-
},
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
if (attempt.kind === 'timeout') {
|
|
150
|
-
return {
|
|
151
|
-
kind: 'failure',
|
|
152
|
-
result: {
|
|
153
|
-
ok: false,
|
|
154
|
-
code: 'cli-timeout',
|
|
155
|
-
message: '这次没读到(等超时了)',
|
|
156
|
-
hint: '稍后重试',
|
|
157
|
-
detail: attempt.detail,
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
if (attempt.kind === 'failed') {
|
|
162
|
-
const normalized = attempt.stderr.trim().toUpperCase().replace(/-/g, '_');
|
|
163
|
-
const forbidden = [...PERMISSION_CODES].some((one) => normalized.includes(one));
|
|
164
|
-
return {
|
|
165
|
-
kind: 'failure',
|
|
166
|
-
result: {
|
|
167
|
-
ok: false,
|
|
168
|
-
code: forbidden ? 'permission-denied' : 'cli-exec-failed',
|
|
169
|
-
message: forbidden ? '没有权限在这台机器上读' : '读不了(命令失败)',
|
|
170
|
-
hint: forbidden ? '换一个有权限的账号,或让用户开权限' : '把下面这段原始输出看一眼再重试',
|
|
171
|
-
detail: attempt.detail,
|
|
172
|
-
},
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
const value = parseJsonLoose(attempt.stdout);
|
|
176
|
-
if (value === undefined) {
|
|
177
|
-
return {
|
|
178
|
-
kind: 'failure',
|
|
179
|
-
result: {
|
|
180
|
-
ok: false,
|
|
181
|
-
code: 'cli-exec-failed',
|
|
182
|
-
message: '读到的格式不对',
|
|
183
|
-
hint: '让 agent 检查一下这条读',
|
|
184
|
-
detail: tailLines(attempt.stdout, 4),
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
return { kind: 'value', value };
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const first = await run();
|
|
192
|
-
if (first.kind === 'value') return first;
|
|
193
|
-
if (first.result.code !== 'cli-exec-failed') return first;
|
|
194
|
-
await sleep(retryMs);
|
|
195
|
-
return run();
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
async function openOnce(
|
|
199
|
-
shell: DshShell,
|
|
200
|
-
command: string,
|
|
201
|
-
session: string,
|
|
202
|
-
url: string,
|
|
203
|
-
timeoutMs: number,
|
|
204
|
-
): Promise<Omit<BrowserReadResult, 'at'> | undefined> {
|
|
205
|
-
let attempt: Awaited<ReturnType<typeof shellRun>>;
|
|
206
|
-
try {
|
|
207
|
-
attempt = await shellRun(shell, command, ['browser', session, 'open', url, '--window', 'background'], timeoutMs);
|
|
208
|
-
} catch (error) {
|
|
209
|
-
return {
|
|
210
|
-
ok: false,
|
|
211
|
-
code: 'cli-exec-failed',
|
|
212
|
-
message: '打不开那个页面',
|
|
213
|
-
hint: '确认后台服务能跑起来,再重试',
|
|
214
|
-
detail: error instanceof Error ? error.message : String(error),
|
|
215
|
-
helpUrl: url,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
if (attempt.kind === 'ok') return undefined;
|
|
219
|
-
return {
|
|
220
|
-
ok: false,
|
|
221
|
-
code: attempt.kind === 'timeout' ? 'cli-timeout' : 'cli-exec-failed',
|
|
222
|
-
message: '打不开那个页面',
|
|
223
|
-
hint: '在浏览器里手动打开一次,然后重试',
|
|
224
|
-
detail: attempt.detail,
|
|
225
|
-
helpUrl: url,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export async function browserRead(options: BrowserReadOptions): Promise<BrowserReadResult> {
|
|
230
|
-
const { shell, script, url, loginUrl, open = true } = options;
|
|
231
|
-
const command = options.command ?? 'opencli';
|
|
232
|
-
const session = options.session ?? BROWSER_READ_SESSION;
|
|
233
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
234
|
-
const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;
|
|
235
|
-
const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
|
|
236
|
-
const at = Date.now();
|
|
237
|
-
|
|
238
|
-
const first = await evalOnce(shell, command, session, script, timeoutMs, retryMs);
|
|
239
|
-
if (first.kind === 'failure') return { ...first.result, at };
|
|
240
|
-
let page = first.value;
|
|
241
|
-
|
|
242
|
-
if (codeOf(page) === 'not-on-site') {
|
|
243
|
-
if (!open || url === undefined) {
|
|
244
|
-
return {
|
|
245
|
-
ok: false,
|
|
246
|
-
code: 'cli-exec-failed',
|
|
247
|
-
at,
|
|
248
|
-
message: '没停在那套系统上',
|
|
249
|
-
hint: '先在那个浏览器里把它打开,再读一次',
|
|
250
|
-
detail: JSON.stringify(page),
|
|
251
|
-
...(url === undefined ? {} : { helpUrl: url }),
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
const opened = await openOnce(shell, command, session, url, timeoutMs);
|
|
255
|
-
if (opened !== undefined) return { ...opened, at };
|
|
256
|
-
await sleep(settleMs);
|
|
257
|
-
const second = await evalOnce(shell, command, session, script, timeoutMs, retryMs);
|
|
258
|
-
if (second.kind === 'failure') return { ...second.result, at };
|
|
259
|
-
page = second.value;
|
|
260
|
-
if (codeOf(page) === 'not-on-site') {
|
|
261
|
-
return {
|
|
262
|
-
ok: false,
|
|
263
|
-
code: 'cli-exec-failed',
|
|
264
|
-
at,
|
|
265
|
-
message: '打开了页面、又读了一遍,仍然不在那套系统上',
|
|
266
|
-
hint: '确认这个地址就是那套系统(或者它跳到了别的域)',
|
|
267
|
-
detail: JSON.stringify(page),
|
|
268
|
-
helpUrl: url,
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (!pageFailed(page)) return { ok: true, code: 'ok', at, value: page, message: '读到了' };
|
|
274
|
-
|
|
275
|
-
const pageCode = codeOf(page) || 'read-failed';
|
|
276
|
-
const message = messageOf(page) || `读不到(${pageCode})`;
|
|
277
|
-
const blocker = blockerFor(pageCode);
|
|
278
|
-
if (blocker === 'auth-required') {
|
|
279
|
-
return {
|
|
280
|
-
ok: false,
|
|
281
|
-
code: blocker,
|
|
282
|
-
at,
|
|
283
|
-
pageCode,
|
|
284
|
-
message,
|
|
285
|
-
hint: '去登录一次,再重试',
|
|
286
|
-
detail: JSON.stringify(page),
|
|
287
|
-
...(loginUrl === undefined && url === undefined ? {} : { helpUrl: loginUrl ?? url }),
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
if (blocker === 'permission-denied') {
|
|
291
|
-
return {
|
|
292
|
-
ok: false,
|
|
293
|
-
code: blocker,
|
|
294
|
-
at,
|
|
295
|
-
pageCode,
|
|
296
|
-
message,
|
|
297
|
-
hint: '这个账号没有权限',
|
|
298
|
-
detail: JSON.stringify(page),
|
|
299
|
-
...(loginUrl === undefined && url === undefined ? {} : { helpUrl: loginUrl ?? url }),
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
return {
|
|
303
|
-
ok: false,
|
|
304
|
-
code: pageCode,
|
|
305
|
-
at,
|
|
306
|
-
pageCode,
|
|
307
|
-
message,
|
|
308
|
-
detail: JSON.stringify(page),
|
|
309
|
-
...(hintOf(page) === undefined ? {} : { hint: hintOf(page) }),
|
|
310
|
-
};
|
|
311
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { OPENCLI_PIN } from '../config/constants.js';
|
|
2
|
-
import type { UnitLibNeed } from '../api/unit.js';
|
|
3
|
-
import { quoteShellArg } from './shell-text.js';
|
|
4
|
-
|
|
5
|
-
import { dshHome } from './data-root.js';
|
|
6
|
-
|
|
7
|
-
export { dshHome };
|
|
8
|
-
|
|
9
|
-
export function isolatedRoot(): string {
|
|
10
|
-
return `${dshHome()}/workbench/opencli`;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function isolatedHome(): string {
|
|
14
|
-
return `${isolatedRoot()}/home`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const OPENCLI_NPM_PACKAGE = '@jackwener/opencli';
|
|
18
|
-
|
|
19
|
-
export function toolchainDir(): string {
|
|
20
|
-
return `${isolatedRoot()}/toolchain`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function toolchainOpencliDir(): string {
|
|
24
|
-
return `${toolchainDir()}/node_modules/${OPENCLI_NPM_PACKAGE}`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function vendorPrefix(): string {
|
|
28
|
-
return `${isolatedRoot()}/plugins-npm`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function vendorPluginsDir(): string {
|
|
32
|
-
return `${vendorPrefix()}/node_modules`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const OPENCLI_CATALOG_ENV = 'DSH_WB_OPENCLI_CATALOG';
|
|
36
|
-
|
|
37
|
-
export function cliCatalogPath(): string {
|
|
38
|
-
const override = process.env[OPENCLI_CATALOG_ENV]?.trim();
|
|
39
|
-
return override === undefined || override === ''
|
|
40
|
-
? `${toolchainOpencliDir()}/cli-manifest.json`
|
|
41
|
-
: override;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function vendorPeerLink(): { readonly dir: string; readonly parent: string } {
|
|
45
|
-
const base = `${vendorPrefix()}/node_modules`;
|
|
46
|
-
const [scope] = OPENCLI_NPM_PACKAGE.split('/');
|
|
47
|
-
return { dir: `${base}/${OPENCLI_NPM_PACKAGE}`, parent: `${base}/${scope}` };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function npmCacheDir(): string {
|
|
51
|
-
return `${isolatedRoot()}/npm-cache`;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function isolatedCommand(): string {
|
|
55
|
-
return `${isolatedRoot()}/bin/opencli`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function libsPrefix(): string {
|
|
59
|
-
return `${isolatedRoot()}/libs`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function libDir(name: string): string {
|
|
63
|
-
return `${libsPrefix()}/node_modules/${name}`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function libInstallCommand(options: {
|
|
67
|
-
readonly need: UnitLibNeed;
|
|
68
|
-
readonly version?: string | undefined;
|
|
69
|
-
}): string {
|
|
70
|
-
const { need, version } = options;
|
|
71
|
-
const registry =
|
|
72
|
-
process.env[need.registryEnv]?.trim() || need.registryDefault;
|
|
73
|
-
const spec = version ?? need.version ?? 'latest';
|
|
74
|
-
return `npm i --prefix '${libsPrefix()}' ${quoteShellArg(`${need.name}@${spec}`)} --registry '${registry}' --cache '${npmCacheDir()}'`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function isolatedInstallCommand(): string {
|
|
78
|
-
return `npm i --prefix '${toolchainDir()}' ${OPENCLI_NPM_PACKAGE}@${OPENCLI_PIN} --cache '${npmCacheDir()}'`;
|
|
79
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { quoteShellArg } from './shell-text.js';
|
|
2
|
-
|
|
3
|
-
const OPENCLI_WINDOW_ENV = 'OPENCLI_WINDOW=background';
|
|
4
|
-
|
|
5
|
-
export function backgroundPrefix(): readonly string[] {
|
|
6
|
-
return [quoteShellArg('env'), quoteShellArg(OPENCLI_WINDOW_ENV)];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function backgroundLine(command: string, argv: readonly string[]): string {
|
|
10
|
-
return [...backgroundPrefix(), quoteShellArg(command), ...argv.map(quoteShellArg)].join(' ');
|
|
11
|
-
}
|
package/src/host/opencli.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import type { DshShell } from '../api/dsh.js';
|
|
2
|
-
import { OPENCLI_MAX_CONCURRENT, OPENCLI_TIMEOUT_MS } from '../config/constants.js';
|
|
3
|
-
import { parseJsonLoose } from '../lib/text.js';
|
|
4
|
-
import { quoteShellArg, tailLines } from './shell-text.js';
|
|
5
|
-
import { helpUrlOf, parseCliErrorEnvelope } from './cli-error.js';
|
|
6
|
-
import { backgroundPrefix } from './opencli-window.js';
|
|
7
|
-
import { runShell } from './shell.js';
|
|
8
|
-
|
|
9
|
-
const FORBIDDEN_CODES = new Set(['FORBIDDEN', 'PERMISSION_DENIED']);
|
|
10
|
-
|
|
11
|
-
const SITE_SESSION_ARGS = ['--site-session', 'persistent'] as const;
|
|
12
|
-
|
|
13
|
-
export class WorkbenchDataError extends Error {
|
|
14
|
-
readonly code: string;
|
|
15
|
-
readonly detail?: string;
|
|
16
|
-
readonly hint?: string;
|
|
17
|
-
readonly project?: unknown;
|
|
18
|
-
readonly helpUrl?: string;
|
|
19
|
-
|
|
20
|
-
constructor(
|
|
21
|
-
code: string,
|
|
22
|
-
message: string,
|
|
23
|
-
extra: { detail?: string; hint?: string; project?: unknown; helpUrl?: string } = {},
|
|
24
|
-
) {
|
|
25
|
-
super(message);
|
|
26
|
-
this.name = 'WorkbenchDataError';
|
|
27
|
-
this.code = code;
|
|
28
|
-
if (extra.detail !== undefined) this.detail = extra.detail;
|
|
29
|
-
if (extra.hint !== undefined) this.hint = extra.hint;
|
|
30
|
-
if (extra.project !== undefined) this.project = extra.project;
|
|
31
|
-
if (extra.helpUrl !== undefined) this.helpUrl = extra.helpUrl;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface OpencliResult {
|
|
36
|
-
readonly value: unknown;
|
|
37
|
-
readonly at: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type OpencliSession = 'persistent' | 'ephemeral' | 'none';
|
|
41
|
-
|
|
42
|
-
export function opencliCommandLine(
|
|
43
|
-
command: string,
|
|
44
|
-
argv: readonly string[],
|
|
45
|
-
session: OpencliSession = 'persistent',
|
|
46
|
-
): string {
|
|
47
|
-
return [
|
|
48
|
-
...backgroundPrefix(),
|
|
49
|
-
quoteShellArg(command),
|
|
50
|
-
...argv.map(quoteShellArg),
|
|
51
|
-
...(session === 'persistent' ? SITE_SESSION_ARGS : []),
|
|
52
|
-
'-f',
|
|
53
|
-
'json',
|
|
54
|
-
].join(' ');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface Opencli {
|
|
58
|
-
json(
|
|
59
|
-
args: readonly string[],
|
|
60
|
-
options?: { key?: string; workdir?: string; session?: OpencliSession },
|
|
61
|
-
): Promise<OpencliResult>;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function createOpencli(options: {
|
|
65
|
-
shell: DshShell;
|
|
66
|
-
plugin: string;
|
|
67
|
-
logger?: { warn?(message: string): void } | undefined;
|
|
68
|
-
command?: string;
|
|
69
|
-
timeoutMs?: number;
|
|
70
|
-
maxConcurrent?: number;
|
|
71
|
-
}): Opencli {
|
|
72
|
-
const {
|
|
73
|
-
shell,
|
|
74
|
-
plugin,
|
|
75
|
-
logger,
|
|
76
|
-
command = 'opencli',
|
|
77
|
-
timeoutMs = OPENCLI_TIMEOUT_MS,
|
|
78
|
-
maxConcurrent = OPENCLI_MAX_CONCURRENT,
|
|
79
|
-
} = options;
|
|
80
|
-
|
|
81
|
-
const inflight = new Map<string, Promise<OpencliResult>>();
|
|
82
|
-
|
|
83
|
-
const MAX_CONCURRENT = maxConcurrent;
|
|
84
|
-
let running = 0;
|
|
85
|
-
const waiting: Array<() => void> = [];
|
|
86
|
-
|
|
87
|
-
async function acquire(): Promise<void> {
|
|
88
|
-
if (running < MAX_CONCURRENT) {
|
|
89
|
-
running += 1;
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
await new Promise<void>((resolve) => waiting.push(resolve));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function release(): void {
|
|
96
|
-
const next = waiting.shift();
|
|
97
|
-
if (next === undefined) running -= 1;
|
|
98
|
-
else next();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async function runLimited<T>(task: () => Promise<T>): Promise<T> {
|
|
102
|
-
await acquire();
|
|
103
|
-
try {
|
|
104
|
-
return await task();
|
|
105
|
-
} finally {
|
|
106
|
-
release();
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function call(commandLine: string, workdir: string | undefined): Promise<unknown> {
|
|
111
|
-
let result;
|
|
112
|
-
try {
|
|
113
|
-
result = await runShell(shell, commandLine, {
|
|
114
|
-
...(workdir === undefined ? {} : { workdir }),
|
|
115
|
-
timeoutMs,
|
|
116
|
-
});
|
|
117
|
-
} catch (error) {
|
|
118
|
-
throw new WorkbenchDataError('cli-exec-failed', `执行 opencli 失败:${String(error)}`, {
|
|
119
|
-
hint: '确认 opencli 已安装且在 PATH 里(which opencli)。',
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (result.timedOut) {
|
|
124
|
-
throw new WorkbenchDataError(
|
|
125
|
-
'cli-timeout',
|
|
126
|
-
`opencli 超过 ${Math.round(timeoutMs / 1000)} 秒没有返回`,
|
|
127
|
-
{ hint: '无头浏览器可能没起来,重试一次通常就好。' },
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (result.exitCode !== 0) {
|
|
132
|
-
const detail = tailLines(result.stderr || result.stdout, 6);
|
|
133
|
-
const envelope = parseCliErrorEnvelope(result.stderr) ?? parseCliErrorEnvelope(result.stdout);
|
|
134
|
-
|
|
135
|
-
const url = envelope === undefined ? undefined : helpUrlOf(envelope);
|
|
136
|
-
if (envelope?.code === 'AUTH_REQUIRED') {
|
|
137
|
-
throw new WorkbenchDataError('auth-required', envelope.message, {
|
|
138
|
-
detail,
|
|
139
|
-
...(url === undefined ? {} : { helpUrl: url }),
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
if (envelope !== undefined && FORBIDDEN_CODES.has(envelope.code)) {
|
|
143
|
-
throw new WorkbenchDataError('permission-denied', envelope.message, {
|
|
144
|
-
detail,
|
|
145
|
-
...(url === undefined ? {} : { helpUrl: url }),
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
if (envelope !== undefined) {
|
|
149
|
-
throw new WorkbenchDataError('cli-command-failed', `${envelope.code}:${envelope.message}`, {
|
|
150
|
-
detail,
|
|
151
|
-
...(envelope.help === undefined ? {} : { hint: envelope.help }),
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
throw new WorkbenchDataError(
|
|
155
|
-
'cli-unclassified',
|
|
156
|
-
`opencli 这条命令没跑通(退出码 ${result.exitCode}),而且没有结构化错误码`,
|
|
157
|
-
{
|
|
158
|
-
detail,
|
|
159
|
-
hint: '多半是插件里没有这条命令(版本旧就会缺),也可能是命令自己失败了:先确认命令存在,再更新插件重试。',
|
|
160
|
-
},
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const parsed = parseJsonLoose(result.stdout);
|
|
165
|
-
if (parsed === undefined) {
|
|
166
|
-
throw new WorkbenchDataError('cli-bad-output', 'opencli 输出不是 JSON', {
|
|
167
|
-
detail: tailLines(result.stdout, 4),
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
return parsed;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async function json(
|
|
174
|
-
args: readonly string[],
|
|
175
|
-
callOptions: { key?: string; workdir?: string; session?: OpencliSession } = {},
|
|
176
|
-
): Promise<OpencliResult> {
|
|
177
|
-
const { key, workdir } = callOptions;
|
|
178
|
-
const session = callOptions.session ?? 'persistent';
|
|
179
|
-
const callKey = key ?? args.map((arg) => String(arg)).join(' ');
|
|
180
|
-
|
|
181
|
-
const pending = inflight.get(callKey);
|
|
182
|
-
if (pending !== undefined) return pending;
|
|
183
|
-
|
|
184
|
-
const commandLine = opencliCommandLine(command, [plugin, ...args], session);
|
|
185
|
-
|
|
186
|
-
const once = async (): Promise<OpencliResult> => {
|
|
187
|
-
const value = await call(commandLine, workdir);
|
|
188
|
-
return { value, at: Date.now() };
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const task = runLimited(async (): Promise<OpencliResult> => {
|
|
192
|
-
try {
|
|
193
|
-
return await once();
|
|
194
|
-
} catch (error) {
|
|
195
|
-
if (!(error instanceof WorkbenchDataError) || error.code !== 'auth-required') throw error;
|
|
196
|
-
return await once();
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
inflight.set(callKey, task);
|
|
201
|
-
try {
|
|
202
|
-
return await task;
|
|
203
|
-
} catch (error) {
|
|
204
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
205
|
-
logger?.warn?.(`[dsh-workbench] opencli 调用失败:${message}`);
|
|
206
|
-
throw error;
|
|
207
|
-
} finally {
|
|
208
|
-
inflight.delete(callKey);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return { json };
|
|
213
|
-
}
|
package/src/host/unit-libs.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { PluginSourceProblem } from './plugin-source.js';
|
|
2
|
-
import type { PluginRelease, UnitLibNeed, UnitPluginNeed } from '../api/unit.js';
|
|
3
|
-
|
|
4
|
-
export interface UnitLibState {
|
|
5
|
-
readonly name: string;
|
|
6
|
-
readonly purpose: string;
|
|
7
|
-
readonly status: 'ok' | 'outdated' | 'missing' | 'unsatisfied';
|
|
8
|
-
readonly installed?: string;
|
|
9
|
-
readonly latest?: string;
|
|
10
|
-
readonly command?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface UnitCommands {
|
|
14
|
-
readonly unit: string;
|
|
15
|
-
readonly plugins?: readonly UnitPluginNeed[];
|
|
16
|
-
readonly libs?: readonly UnitLibNeed[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface UnitPluginState {
|
|
20
|
-
readonly name: string;
|
|
21
|
-
readonly status: "ok" | "outdated" | "missing";
|
|
22
|
-
readonly missing: readonly string[];
|
|
23
|
-
readonly install?: "ready" | PluginSourceProblem;
|
|
24
|
-
readonly command?: string;
|
|
25
|
-
readonly release?: PluginRelease;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface UnitCapability {
|
|
29
|
-
readonly unit: string;
|
|
30
|
-
readonly status: "ok" | "failed" | "missing-commands" | "missing-plugin" | "unchecked";
|
|
31
|
-
readonly plugin?: string;
|
|
32
|
-
readonly required: readonly string[];
|
|
33
|
-
readonly missing: readonly string[];
|
|
34
|
-
readonly detail: string;
|
|
35
|
-
readonly command?: string;
|
|
36
|
-
readonly fix?: "install" | "update";
|
|
37
|
-
readonly install?: "ready" | PluginSourceProblem;
|
|
38
|
-
readonly plugins?: readonly UnitPluginState[];
|
|
39
|
-
readonly libs?: readonly UnitLibState[];
|
|
40
|
-
readonly released?: boolean;
|
|
41
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|