@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
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BundleCancellation,
|
|
3
|
+
BundleChange,
|
|
4
|
+
BundleInspection,
|
|
5
|
+
BundleInstallRequest,
|
|
6
|
+
BundleRegistries,
|
|
7
|
+
BundleRegistry,
|
|
8
|
+
} from '../contract/protocol/bundle-install.js';
|
|
9
|
+
|
|
10
|
+
export type InstalledModule =
|
|
11
|
+
| {
|
|
12
|
+
readonly kind: 'module';
|
|
13
|
+
readonly pkg: string;
|
|
14
|
+
readonly unit: string;
|
|
15
|
+
readonly version: string;
|
|
16
|
+
readonly registry?: string;
|
|
17
|
+
readonly local?: boolean;
|
|
18
|
+
}
|
|
19
|
+
| { readonly kind: 'not-module'; readonly pkg: string }
|
|
20
|
+
| { readonly kind: 'unknown'; readonly pkg: string };
|
|
21
|
+
|
|
22
|
+
export interface ProfileModule {
|
|
23
|
+
readonly pkg: string;
|
|
24
|
+
readonly unit: string;
|
|
25
|
+
readonly version: string;
|
|
26
|
+
readonly registry?: string;
|
|
27
|
+
readonly local?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ProfilePackage {
|
|
31
|
+
readonly pkg: string;
|
|
32
|
+
readonly version: string;
|
|
33
|
+
readonly registry?: string;
|
|
34
|
+
readonly local?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface BundleManager {
|
|
38
|
+
registries(): Promise<BundleRegistries | null>;
|
|
39
|
+
inspect(spec: string, registry?: BundleRegistry): Promise<BundleInspection | null>;
|
|
40
|
+
install(request: BundleInstallRequest): Promise<BundleChange | null>;
|
|
41
|
+
remove(name: string): Promise<BundleChange | null>;
|
|
42
|
+
setEnabled?(name: string, enabled: boolean): Promise<BundleChange | null>;
|
|
43
|
+
cancel(requestId: string): Promise<BundleCancellation | null>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface RegistryEntry {
|
|
47
|
+
readonly scope: string;
|
|
48
|
+
readonly registry: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface RegistryNotebook {
|
|
52
|
+
sources(): readonly RegistryEntry[];
|
|
53
|
+
remember(entries: readonly RegistryEntry[]): readonly string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface SelfUpdatePaths {
|
|
57
|
+
readonly dir: string;
|
|
58
|
+
readonly script: string;
|
|
59
|
+
readonly marker: string;
|
|
60
|
+
readonly log: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface SelfUpdateRequest {
|
|
64
|
+
readonly dshHome: string;
|
|
65
|
+
readonly profile: string;
|
|
66
|
+
readonly pkg: string;
|
|
67
|
+
readonly from: string;
|
|
68
|
+
readonly to: string;
|
|
69
|
+
readonly pinned: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface SelfUpdateStart {
|
|
73
|
+
readonly started: boolean;
|
|
74
|
+
readonly paths: SelfUpdatePaths;
|
|
75
|
+
readonly forward: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface OpencliResolution {
|
|
79
|
+
readonly command: string;
|
|
80
|
+
readonly source: 'env' | 'isolated' | 'path';
|
|
81
|
+
readonly isolated: string | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function scopeOf(pkg: string): string | null {
|
|
85
|
+
const slash = pkg.indexOf('/');
|
|
86
|
+
return pkg.startsWith('@') && slash > 1 ? pkg.slice(0, slash) : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function normalizeRegistry(url: string): string {
|
|
90
|
+
const trimmed = url.trim();
|
|
91
|
+
return trimmed.endsWith('/') ? trimmed : `${trimmed}/`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function sameRegistry(a: string | null | undefined, b: string | null | undefined): boolean {
|
|
95
|
+
if (a === null || a === undefined || b === null || b === undefined) return false;
|
|
96
|
+
if (a.trim() === '' || b.trim() === '') return false;
|
|
97
|
+
return normalizeRegistry(a) === normalizeRegistry(b);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function registryFailure(change: { application?: string; packageResult?: { kind?: string; output?: string }; errorDetail?: string }): boolean {
|
|
101
|
+
if (change.application !== 'failed') return false;
|
|
102
|
+
if (change.packageResult?.kind === 'not-found') return true;
|
|
103
|
+
const output = change.packageResult?.output ?? change.errorDetail ?? '';
|
|
104
|
+
return /ERR_PNPM_[A-Z_]*FETCH/.test(output);
|
|
105
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { DshHostContext } from '../api/dsh.js';
|
|
2
|
+
import type { DataReader } from '../contract/data.js';
|
|
3
|
+
import type {
|
|
4
|
+
EnvSupplyPort,
|
|
5
|
+
HostActions,
|
|
6
|
+
ModulePort,
|
|
7
|
+
PluginSurface,
|
|
8
|
+
ProbePort,
|
|
9
|
+
ProfilePort,
|
|
10
|
+
RuntimePort,
|
|
11
|
+
} from './port-types.js';
|
|
12
|
+
|
|
13
|
+
export const WORKBENCH_ADAPTERS = 'workbenchAdapters';
|
|
14
|
+
|
|
15
|
+
export const ADAPTER_SET_API_VERSION = 1;
|
|
16
|
+
|
|
17
|
+
export const SUPPORTED_ADAPTER_API_VERSIONS: readonly number[] = [ADAPTER_SET_API_VERSION];
|
|
18
|
+
|
|
19
|
+
export interface AdapterSet {
|
|
20
|
+
readonly apiVersion: number;
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly version: string;
|
|
23
|
+
readonly data: DataReader;
|
|
24
|
+
readonly supply: EnvSupplyPort;
|
|
25
|
+
readonly modules: ModulePort;
|
|
26
|
+
readonly profile: ProfilePort;
|
|
27
|
+
readonly probe: ProbePort;
|
|
28
|
+
readonly runtime: RuntimePort;
|
|
29
|
+
readonly plugins: PluginSurface;
|
|
30
|
+
readonly actions: HostActions;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type CoreAdapterSet = Pick<
|
|
34
|
+
AdapterSet,
|
|
35
|
+
'apiVersion' | 'id' | 'version' | 'data' | 'supply' | 'profile' | 'probe'
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
export type WorkbenchAdapters = AdapterSet;
|
|
39
|
+
|
|
40
|
+
export function adaptersOf(ctx: DshHostContext): AdapterSet {
|
|
41
|
+
const adapters = ctx.get<AdapterSet>(WORKBENCH_ADAPTERS, false);
|
|
42
|
+
if (adapters !== undefined) return adapters;
|
|
43
|
+
throw new Error('workbenchAdapters 未提供');
|
|
44
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
|
|
3
|
+
export const CHANGELOG_FILE = 'CHANGELOG.md';
|
|
4
|
+
|
|
5
|
+
export const CHANGELOG_KINDS = ['added', 'changed', 'fixed', 'removed', 'security', 'deprecated'] as const;
|
|
6
|
+
|
|
7
|
+
export type ChangelogKind = (typeof CHANGELOG_KINDS)[number];
|
|
8
|
+
|
|
9
|
+
export interface ChangelogSection {
|
|
10
|
+
readonly version: string;
|
|
11
|
+
readonly body: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ChangelogEntryOptions {
|
|
15
|
+
readonly version: string;
|
|
16
|
+
readonly kind?: ChangelogKind | undefined;
|
|
17
|
+
readonly note: string;
|
|
18
|
+
readonly date?: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const KIND_LABELS: Readonly<Record<ChangelogKind, string>> = {
|
|
22
|
+
added: '✨ 新增',
|
|
23
|
+
changed: '⚡ 改进',
|
|
24
|
+
fixed: '🐛 修复',
|
|
25
|
+
removed: '🗑️ 移除',
|
|
26
|
+
security: '🔒 安全',
|
|
27
|
+
deprecated: '⚠️ 弃用',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const VERSION_HEADING = /^##[ \t]+\[?v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)\]?[ \t]*(?:-[ \t]*.*)?$/;
|
|
31
|
+
const ANY_SECTION = /^##[ \t]/;
|
|
32
|
+
const SUBSECTION = /^###[ \t]/;
|
|
33
|
+
|
|
34
|
+
export function changelogVersion(value: string): string {
|
|
35
|
+
return String(value ?? '')
|
|
36
|
+
.trim()
|
|
37
|
+
.replace(/^v/, '');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function changelogDate(now: Date = new Date()): string {
|
|
41
|
+
return dayjs(now).format('YYYY-MM-DD');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function normalizeChangelogText(text: string): string {
|
|
45
|
+
return String(text ?? '').replace(/\r\n?/g, '\n');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function linesOf(text: string): string[] {
|
|
49
|
+
return normalizeChangelogText(text).split('\n');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function bodyOf(rows: readonly string[]): string {
|
|
53
|
+
return rows.join('\n').trim();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function sectionRange(
|
|
57
|
+
rows: readonly string[],
|
|
58
|
+
version: string,
|
|
59
|
+
): { readonly start: number; readonly end: number } | undefined {
|
|
60
|
+
const wanted = changelogVersion(version);
|
|
61
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
62
|
+
const row = rows[index] ?? '';
|
|
63
|
+
if (!ANY_SECTION.test(row)) continue;
|
|
64
|
+
const match = VERSION_HEADING.exec(row);
|
|
65
|
+
if (match === null || changelogVersion(match[1] ?? '') !== wanted) continue;
|
|
66
|
+
let end = rows.length;
|
|
67
|
+
for (let next = index + 1; next < rows.length; next += 1) {
|
|
68
|
+
if (ANY_SECTION.test(rows[next] ?? '')) {
|
|
69
|
+
end = next;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { start: index, end };
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function changelogSkeleton(): string {
|
|
79
|
+
return '# 更新日志\n';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function parseChangelog(text: string): readonly ChangelogSection[] {
|
|
83
|
+
const rows = linesOf(text);
|
|
84
|
+
const sections: ChangelogSection[] = [];
|
|
85
|
+
let current: { readonly version: string; readonly from: number } | undefined;
|
|
86
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
87
|
+
const row = rows[index] ?? '';
|
|
88
|
+
if (!ANY_SECTION.test(row)) continue;
|
|
89
|
+
if (current !== undefined) {
|
|
90
|
+
sections.push({ version: current.version, body: bodyOf(rows.slice(current.from, index)) });
|
|
91
|
+
}
|
|
92
|
+
const match = VERSION_HEADING.exec(row);
|
|
93
|
+
current =
|
|
94
|
+
match === null
|
|
95
|
+
? undefined
|
|
96
|
+
: { version: changelogVersion(match[1] ?? ''), from: index + 1 };
|
|
97
|
+
}
|
|
98
|
+
if (current !== undefined) {
|
|
99
|
+
sections.push({ version: current.version, body: bodyOf(rows.slice(current.from)) });
|
|
100
|
+
}
|
|
101
|
+
return sections;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function changelogSectionOf(text: string, version: string): string | undefined {
|
|
105
|
+
const wanted = changelogVersion(version);
|
|
106
|
+
return parseChangelog(text).find((one) => one.version === wanted)?.body;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function changelogHasEntry(text: string, version: string): boolean {
|
|
110
|
+
const body = changelogSectionOf(text, version);
|
|
111
|
+
if (body === undefined) return false;
|
|
112
|
+
return body
|
|
113
|
+
.split('\n')
|
|
114
|
+
.map((one) => one.trim())
|
|
115
|
+
.some((one) => one !== '' && !one.startsWith('#') && !one.startsWith('<!--') && !/^`[^`]*`$/.test(one));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function bulletsOf(note: string): string[] {
|
|
119
|
+
return note
|
|
120
|
+
.split('\n')
|
|
121
|
+
.map((one) => one.trim())
|
|
122
|
+
.filter((one) => one !== '')
|
|
123
|
+
.map((one) => `- ${one}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function blockOf(options: ChangelogEntryOptions, label: string, bullets: readonly string[]): string[] {
|
|
127
|
+
const date = (options.date ?? '').trim();
|
|
128
|
+
return [
|
|
129
|
+
`## v${changelogVersion(options.version)}`,
|
|
130
|
+
...(date === '' ? [] : ['', `\`${date}\``]),
|
|
131
|
+
'',
|
|
132
|
+
`### ${label}`,
|
|
133
|
+
'',
|
|
134
|
+
...bullets,
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function insertBlock(rows: readonly string[], at: number, block: readonly string[]): string[] {
|
|
139
|
+
const before = rows.slice(0, at);
|
|
140
|
+
while (before.length > 0 && (before[before.length - 1] ?? '').trim() === '') before.pop();
|
|
141
|
+
const after = rows.slice(at);
|
|
142
|
+
while (after.length > 0 && (after[0] ?? '').trim() === '') after.shift();
|
|
143
|
+
const out = [...before];
|
|
144
|
+
if (out.length > 0) out.push('');
|
|
145
|
+
out.push(...block);
|
|
146
|
+
if (after.length > 0) {
|
|
147
|
+
out.push('');
|
|
148
|
+
out.push(...after);
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function newSectionAt(rows: readonly string[]): number {
|
|
154
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
155
|
+
if (/^##[ \t]+\[?unreleased\]?[ \t]*$/i.test(rows[index] ?? '')) {
|
|
156
|
+
for (let next = index + 1; next < rows.length; next += 1) {
|
|
157
|
+
if (ANY_SECTION.test(rows[next] ?? '')) return next;
|
|
158
|
+
}
|
|
159
|
+
return rows.length;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
163
|
+
if (ANY_SECTION.test(rows[index] ?? '')) return index;
|
|
164
|
+
}
|
|
165
|
+
return rows.length;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function appendChangelogEntry(text: string, options: ChangelogEntryOptions): string {
|
|
169
|
+
const kind: ChangelogKind = options.kind ?? 'changed';
|
|
170
|
+
const label = KIND_LABELS[kind];
|
|
171
|
+
const bullets = bulletsOf(options.note);
|
|
172
|
+
if (bullets.length === 0) return normalizeChangelogText(text);
|
|
173
|
+
const blocks = blockOf(options, label, bullets);
|
|
174
|
+
const source = normalizeChangelogText(text).trim() === '' ? changelogSkeleton() : normalizeChangelogText(text);
|
|
175
|
+
const rows = linesOf(source);
|
|
176
|
+
const range = sectionRange(rows, options.version);
|
|
177
|
+
if (range === undefined) return `${insertBlock(rows, newSectionAt(rows), blocks).join('\n').replace(/\n+$/, '')}\n`;
|
|
178
|
+
let subsection = -1;
|
|
179
|
+
for (let index = range.start + 1; index < range.end; index += 1) {
|
|
180
|
+
const heading = (rows[index] ?? '').trim();
|
|
181
|
+
if (SUBSECTION.test(heading) && heading.includes(label)) {
|
|
182
|
+
subsection = index;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (subsection === -1) {
|
|
187
|
+
const out = [...rows];
|
|
188
|
+
out.splice(range.end, 0, '', `### ${label}`, '', ...bullets);
|
|
189
|
+
return `${normalizeWhitespace(out)}\n`;
|
|
190
|
+
}
|
|
191
|
+
let end = range.end;
|
|
192
|
+
for (let index = subsection + 1; index < range.end; index += 1) {
|
|
193
|
+
if (SUBSECTION.test(rows[index] ?? '')) {
|
|
194
|
+
end = index;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const out = [...rows];
|
|
199
|
+
let at = end;
|
|
200
|
+
while (at > subsection + 1 && (out[at - 1] ?? '').trim() === '') at -= 1;
|
|
201
|
+
out.splice(at, 0, ...bullets);
|
|
202
|
+
return `${normalizeWhitespace(out)}\n`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function normalizeWhitespace(rows: readonly string[]): string {
|
|
206
|
+
const out: string[] = [];
|
|
207
|
+
for (const row of rows) {
|
|
208
|
+
if (row.trim() === '' && out.length > 0 && (out[out.length - 1] ?? '').trim() === '') continue;
|
|
209
|
+
out.push(row);
|
|
210
|
+
}
|
|
211
|
+
return out.join('\n').replace(/\n+$/, '');
|
|
212
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { isolatedHome, toolchainOpencliDir, vendorPeerLink, vendorPluginsDir } from './paths.js';
|
|
4
|
+
import { quoteShellArg } from './shell-text.js';
|
|
5
|
+
|
|
6
|
+
export const CHANNEL_PLUGIN_NAME = 'opencli-plugin-workbench';
|
|
7
|
+
export const CHANNEL_PLUGIN_SITE = 'workbench';
|
|
8
|
+
|
|
9
|
+
export interface ChannelPluginPlan {
|
|
10
|
+
readonly dest: string;
|
|
11
|
+
readonly version: string;
|
|
12
|
+
readonly commands: readonly string[];
|
|
13
|
+
readonly line: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function textOf(value: unknown): string {
|
|
17
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function channelVersionOf(dir: string): string | undefined {
|
|
21
|
+
try {
|
|
22
|
+
const parsed: unknown = JSON.parse(readFileSync(join(dir, 'opencli-plugin.json'), 'utf8'));
|
|
23
|
+
if (typeof parsed !== 'object' || parsed === null) return undefined;
|
|
24
|
+
const version = textOf((parsed as Record<string, unknown>)['version']);
|
|
25
|
+
return version === '' ? undefined : version;
|
|
26
|
+
} catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function channelPluginPlan(options: {
|
|
32
|
+
readonly channelDir: string;
|
|
33
|
+
readonly command?: string;
|
|
34
|
+
}): ChannelPluginPlan | undefined {
|
|
35
|
+
const version = channelVersionOf(options.channelDir);
|
|
36
|
+
if (version === undefined) return undefined;
|
|
37
|
+
const command = options.command ?? 'opencli';
|
|
38
|
+
const dest = join(vendorPluginsDir(), CHANNEL_PLUGIN_NAME);
|
|
39
|
+
const peer = vendorPeerLink();
|
|
40
|
+
const commands = [
|
|
41
|
+
`rm -rf ${quoteShellArg(dest)} && mkdir -p ${quoteShellArg(dest)} && cp -R ${quoteShellArg(options.channelDir)}/. ${quoteShellArg(dest)}/`,
|
|
42
|
+
`rm -rf ${quoteShellArg(peer.dir)} && mkdir -p ${quoteShellArg(peer.parent)} && ln -s ${quoteShellArg(toolchainOpencliDir())} ${quoteShellArg(peer.dir)}`,
|
|
43
|
+
`(${quoteShellArg(command)} plugin uninstall ${quoteShellArg(CHANNEL_PLUGIN_SITE)} || true)`,
|
|
44
|
+
`${quoteShellArg(command)} plugin install ${quoteShellArg(dest)}`,
|
|
45
|
+
];
|
|
46
|
+
return { dest, version, commands, line: commands.join(' && ') };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function channelInstalledVersion(): string | undefined {
|
|
50
|
+
return channelVersionOf(join(vendorPluginsDir(), CHANNEL_PLUGIN_NAME));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function channelSiteInstalled(): boolean {
|
|
54
|
+
return existsSync(join(isolatedHome(), '.opencli', 'plugins', CHANNEL_PLUGIN_SITE));
|
|
55
|
+
}
|
package/src/host/cli.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DshHostContext } from '../api/dsh.js';
|
|
2
|
+
import type { CliPort } from '../contract/cli.js';
|
|
3
|
+
import { adaptersOf } from './adapters.js';
|
|
4
|
+
|
|
5
|
+
export function cliPortOf(ctx: DshHostContext): CliPort {
|
|
6
|
+
return { command: adaptersOf(ctx).runtime.opencliCommand(ctx).command };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function cliCommandOf(ctx: DshHostContext): string {
|
|
10
|
+
return cliPortOf(ctx).command;
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function baseName(value: string): string {
|
|
2
|
+
const parts = value.trim().split(/[\\/]/);
|
|
3
|
+
return parts[parts.length - 1] ?? '';
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function isOpencli(name: string): boolean {
|
|
7
|
+
return name === 'opencli' || name === 'opencli.cmd' || name === 'opencli.exe';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function commandNamesMatch(declared: string, resolved: string): boolean {
|
|
11
|
+
const left = baseName(declared);
|
|
12
|
+
const right = baseName(resolved);
|
|
13
|
+
if (left === '' || right === '') return false;
|
|
14
|
+
return left === right || (isOpencli(left) && isOpencli(right));
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class WorkbenchDataError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
readonly detail?: string;
|
|
4
|
+
readonly hint?: string;
|
|
5
|
+
readonly project?: unknown;
|
|
6
|
+
readonly helpUrl?: string;
|
|
7
|
+
|
|
8
|
+
constructor(
|
|
9
|
+
code: string,
|
|
10
|
+
message: string,
|
|
11
|
+
extra: { detail?: string; hint?: string; project?: unknown; helpUrl?: string } = {},
|
|
12
|
+
) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'WorkbenchDataError';
|
|
15
|
+
this.code = code;
|
|
16
|
+
if (extra.detail !== undefined) this.detail = extra.detail;
|
|
17
|
+
if (extra.hint !== undefined) this.hint = extra.hint;
|
|
18
|
+
if (extra.project !== undefined) this.project = extra.project;
|
|
19
|
+
if (extra.helpUrl !== undefined) this.helpUrl = extra.helpUrl;
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/host/data-root.ts
CHANGED
package/src/host/diy-debug.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clockOf } from './time.js';
|
|
1
2
|
/**
|
|
2
3
|
* 调试约定的两个相位:`debugging` 正占着用户的屏,`paused` 已暂停但仍持着约定。
|
|
3
4
|
*/
|
|
@@ -156,5 +157,5 @@ export function createDebugStore(): DebugStore {
|
|
|
156
157
|
* @returns 形如「会话 x(自 HH:mm:ssZ 起在调试 y)」的中文串。
|
|
157
158
|
*/
|
|
158
159
|
export function debugOwnerText(state: DebugState): string {
|
|
159
|
-
return `会话 ${state.sessionId}(自 ${
|
|
160
|
+
return `会话 ${state.sessionId}(自 ${clockOf(state.startedAt)}Z 起在调试 ${state.unit})`;
|
|
160
161
|
}
|
package/src/host/diy-record.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clockOf } from './time.js';
|
|
1
2
|
/**
|
|
2
3
|
* 录制交接的三个相位:`armed` 等用户在外站页面上动手,`recording` 正在录,`finished` 录完待取。
|
|
3
4
|
*/
|
|
@@ -274,6 +275,6 @@ export function createRecordStore(): RecordStore {
|
|
|
274
275
|
* @returns 形如「会话 x(自 HH:mm:ssZ 起在录 y 的「动作」)」的中文串。
|
|
275
276
|
*/
|
|
276
277
|
export function recordOwnerText(state: RecordState): string {
|
|
277
|
-
const at =
|
|
278
|
+
const at = clockOf(state.startedAt);
|
|
278
279
|
return `会话 ${state.sessionId}(自 ${at}Z 起在录 ${state.unit} 的「${state.action}」)`;
|
|
279
280
|
}
|
package/src/host/facts.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import type { UnitManifest } from '../contract/unit.js';
|
|
3
|
+
import { FACT_SCOPE_SUFFIX } from '../contract/facts.js';
|
|
3
4
|
|
|
4
5
|
export interface FactChange {
|
|
5
6
|
readonly rev: number;
|
|
@@ -19,7 +20,7 @@ export interface FactLog {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export function createFactLog(options: { readonly epoch?: string } = {}): FactLog {
|
|
22
|
-
const epoch = options.epoch ?? `e${
|
|
23
|
+
const epoch = options.epoch ?? `e${randomUUID()}`;
|
|
23
24
|
let rev = 0;
|
|
24
25
|
const last = new Map<string, number>();
|
|
25
26
|
const listeners = new Set<(change: FactChange) => void>();
|
package/src/host/git.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DshShell } from '../api/dsh.js';
|
|
2
|
-
import { quoteShellArg, tailLines } from '
|
|
3
|
-
import { runShell } from '
|
|
2
|
+
import { quoteShellArg, tailLines } from '../host/shell-text.js';
|
|
3
|
+
import { runShell } from '../host/shell.js';
|
|
4
4
|
|
|
5
5
|
export type OriginRead =
|
|
6
6
|
| { readonly kind: 'ok'; readonly remote: string }
|
package/src/host/http.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type ProvisionHandler,
|
|
8
8
|
type ProvisionRequest,
|
|
9
9
|
} from './provision.js';
|
|
10
|
-
import { WorkbenchDataError } from '
|
|
10
|
+
import { WorkbenchDataError } from './data-error.js';
|
|
11
11
|
import { writeResultForClient } from './write-result.js';
|
|
12
12
|
|
|
13
13
|
export type RouteHandler = (search: URLSearchParams) => Promise<unknown> | unknown;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨平台库的「能力 → 首选实现」表:外壳 arch 的 S68 与 DIY 模块的 `source` 判据共用这一份,
|
|
3
|
+
* 避免两边各写一张表后漂开。
|
|
4
|
+
*
|
|
5
|
+
* 能力名是「做什么」,`preferred` 是「用什么」;两者不是同一个层级(类似 datetime → dayjs)。
|
|
6
|
+
* 手写轮子的指纹只认最常见的直连写法;`importsPreferred()` 负责判断这个文件是否已经在用首选库。
|
|
7
|
+
*/
|
|
8
|
+
export interface LibCapability {
|
|
9
|
+
readonly capability: string;
|
|
10
|
+
readonly preferred: string;
|
|
11
|
+
readonly pattern: RegExp;
|
|
12
|
+
readonly why: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 手写轮子的硬指纹;按能力名稳定排序,方便两边报告与测试对照。 */
|
|
16
|
+
export const LIB_CAPABILITIES: readonly LibCapability[] = [
|
|
17
|
+
{
|
|
18
|
+
capability: 'process-spawn',
|
|
19
|
+
preferred: 'execa',
|
|
20
|
+
pattern: /from\s+['"]node:child_process['"]/,
|
|
21
|
+
why: '自己起进程要用 execa;同步/跨平台差异交给它',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
capability: 'cli-locate',
|
|
25
|
+
preferred: 'which',
|
|
26
|
+
pattern: /\bprocess\.env\.(?:PATH|PATHEXT)\b/,
|
|
27
|
+
why: '自己扫 PATH/PATHEXT 要用 which(它处理 PATHEXT 与 Path 大小写)',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
capability: 'file-lock',
|
|
31
|
+
preferred: 'proper-lockfile',
|
|
32
|
+
pattern: /\bprocess\.kill\([^)]*,\s*0\)|flag:\s*['"]wx['"]/,
|
|
33
|
+
why: '自己抢文件锁要用 proper-lockfile(stale / 重试 / 跨平台)',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
capability: 'atomic-state',
|
|
37
|
+
preferred: 'write-file-atomic',
|
|
38
|
+
pattern: /writeFileSync\([^)]*\.(?:sync\.json|self-update\.json)/,
|
|
39
|
+
why: '自己直写状态文件要用 write-file-atomic(防写一半)',
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 这个文件是否已经 import 了某个首选库(静态或动态 import 都算)。
|
|
45
|
+
* @param source 文件全文。
|
|
46
|
+
* @param preferred 首选库包名。
|
|
47
|
+
* @returns 有 import 就 `true`。
|
|
48
|
+
*/
|
|
49
|
+
export function importsPreferred(source: string, preferred: string): boolean {
|
|
50
|
+
const escaped = preferred.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
51
|
+
return new RegExp(`(?:from\\s*|import\\s*\\(\\s*)['"]${escaped}['"]`).test(source);
|
|
52
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LibNeed, SupplySource } from '../contract/needs.js';
|
|
2
|
+
import { npmRegistryOf } from './plugin-source.js';
|
|
3
|
+
import { quoteShellArg } from './shell-text.js';
|
|
4
|
+
import { libsPrefix, npmCacheDir } from './paths.js';
|
|
5
|
+
|
|
6
|
+
export type NpmSource = Extract<SupplySource, { kind: 'npm' }>;
|
|
7
|
+
|
|
8
|
+
export function libInstallCommand(options: {
|
|
9
|
+
readonly need: LibNeed;
|
|
10
|
+
readonly source: NpmSource;
|
|
11
|
+
readonly version?: string | undefined;
|
|
12
|
+
}): string {
|
|
13
|
+
const { need, source, version } = options;
|
|
14
|
+
const registry = npmRegistryOf(source, process.env);
|
|
15
|
+
const spec = version ?? need.version ?? 'latest';
|
|
16
|
+
const registryFlag = registry.ok ? ` --registry ${quoteShellArg(registry.value)}` : '';
|
|
17
|
+
return `npm i --prefix ${quoteShellArg(libsPrefix())} ${quoteShellArg(`${need.name}@${spec}`)}${registryFlag} --cache ${quoteShellArg(npmCacheDir())}`;
|
|
18
|
+
}
|
package/src/host/long-task.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { epochOf } from './time.js';
|
|
2
|
+
import { runsRouteOf, type LongTaskStatus, type LongTaskStep } from '../contract/long-task.js';
|
|
2
3
|
|
|
3
4
|
const LIVE: ReadonlySet<string> = new Set<LongTaskStatus>(['running', 'needs-login']);
|
|
4
5
|
|
|
@@ -104,7 +105,7 @@ export function createLongTaskRegistry<S extends LongTaskBase>(deps: LongTaskDep
|
|
|
104
105
|
},
|
|
105
106
|
|
|
106
107
|
current(workspace) {
|
|
107
|
-
const floor =
|
|
108
|
+
const floor = epochOf(now()) - recentMs;
|
|
108
109
|
const newest = new Map<string, LongTaskBase>();
|
|
109
110
|
for (const snapshot of runs.values()) {
|
|
110
111
|
if (snapshot.workspace !== workspace) continue;
|
|
@@ -112,7 +113,7 @@ export function createLongTaskRegistry<S extends LongTaskBase>(deps: LongTaskDep
|
|
|
112
113
|
}
|
|
113
114
|
return [...newest.values()]
|
|
114
115
|
.filter(
|
|
115
|
-
(snapshot) => LIVE.has(snapshot.status) ||
|
|
116
|
+
(snapshot) => LIVE.has(snapshot.status) || epochOf(snapshot.at) >= floor,
|
|
116
117
|
)
|
|
117
118
|
.reverse()
|
|
118
119
|
.map((snapshot) => ({ ...deps.view(snapshot as S), id: snapshot.id }));
|