@ran-sh/dsh-crew 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -20
- package/README.zh.md +25 -18
- package/lib/client.js +336 -16
- package/official-web-bridge/lib/client.js +336 -16
- package/package.json +166 -166
- package/src/client/index.tsx +853 -716
- package/src/credential-reference.mjs +38 -0
- package/src/hub/index.mjs +890 -65
- package/src/install/install-legacy.mjs +184 -185
- package/src/install/npx-lifecycle.mjs +456 -398
- package/src/model-catalog.mjs +8 -1
- package/src/policy-legacy.mjs +2 -2
- package/src/provider-delete-adapters.mjs +1934 -451
- package/src/provider-inventory.mjs +184 -132
- package/src/provider-layer-migration-adapters.mjs +759 -0
- package/src/provider-layer-migration.mjs +198 -0
- package/src/provider-lifecycle-state.mjs +103 -95
- package/src/provider-lifecycle.mjs +252 -214
- package/src/provider-profile-store.mjs +378 -171
- package/src/provider-settings-store.mjs +291 -0
- package/src/provider-store-lock.mjs +67 -0
- package/src/runtime-identity.mjs +73 -72
package/README.md
CHANGED
|
@@ -17,9 +17,13 @@ dsh-crew integrate
|
|
|
17
17
|
dsh-crew status
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Open <http://127.0.0.1:3080> for the daily console. On Windows, installation
|
|
21
|
-
also registers login startup for the 3080 console; its official bridge starts
|
|
22
|
-
and owns the isolated 3210 Harness backend.
|
|
20
|
+
Open <http://127.0.0.1:3080> for the daily console. On Windows, installation
|
|
21
|
+
also registers login startup for the 3080 console; its official bridge starts
|
|
22
|
+
and owns the isolated 3210 Harness backend.
|
|
23
|
+
|
|
24
|
+
Fresh installs contain only the built-in DeepSeek route. Add your own provider
|
|
25
|
+
credentials, model priorities, and optional integrations in the local Settings;
|
|
26
|
+
they are never bundled into this package.
|
|
23
27
|
|
|
24
28
|
| Surface | Purpose |
|
|
25
29
|
| --- | --- |
|
|
@@ -42,26 +46,30 @@ silently falling back.
|
|
|
42
46
|
## Useful commands
|
|
43
47
|
|
|
44
48
|
```bash
|
|
45
|
-
dsh-crew inspect # live capabilities and readiness
|
|
46
|
-
dsh-crew jobs list # jobs and Result Contracts
|
|
47
|
-
dsh-crew providers list # 3210 Harness provider inventory (secret-free)
|
|
48
|
-
dsh-crew providers
|
|
49
|
-
|
|
50
|
-
dsh-crew
|
|
51
|
-
dsh-crew
|
|
52
|
-
dsh-crew
|
|
53
|
-
dsh-crew
|
|
54
|
-
dsh-crew
|
|
55
|
-
dsh-crew
|
|
56
|
-
dsh-crew
|
|
57
|
-
dsh-crew
|
|
49
|
+
dsh-crew inspect # live capabilities and readiness
|
|
50
|
+
dsh-crew jobs list # jobs and Result Contracts
|
|
51
|
+
dsh-crew providers list # 3210 Harness provider inventory (secret-free)
|
|
52
|
+
dsh-crew providers migration-status # detect legacy base providers; no automatic migration
|
|
53
|
+
dsh-crew providers migrate-plan <provider>
|
|
54
|
+
dsh-crew providers migrate <provider> --plan <id> --confirm # writes user layer, restarts 3210, verifies
|
|
55
|
+
dsh-crew providers rollback-migration <provider> --plan <id> --confirm
|
|
56
|
+
dsh-crew providers probe <provider-id>
|
|
57
|
+
curl http://127.0.0.1:3210/_dsh/dsh-crew/credential-references # references/orphans only
|
|
58
|
+
dsh-crew credentials list # secret-free reference inventory
|
|
59
|
+
dsh-crew credentials purge-plan env:NAME
|
|
60
|
+
dsh-crew credentials purge env:NAME --plan <plan-id> --expected-revision <sha256> --confirm
|
|
61
|
+
dsh-crew providers delete-plan <provider-id> --replacement-default <provider-id>
|
|
62
|
+
dsh-crew providers delete <provider-id> --plan <plan-id> --expected-revision <sha256> --confirm
|
|
63
|
+
dsh-crew releases list # retained validated payloads
|
|
64
|
+
dsh-crew rollback <version> # switch payload and verify the 3210 runtime
|
|
65
|
+
dsh-crew update # update and repair enabled integrations
|
|
58
66
|
dsh-crew uninstall # remove managed files, keep backups/config
|
|
59
67
|
```
|
|
60
68
|
|
|
61
|
-
The runtime is isolated under `~/.config/dsh-crew/harness` with `profile: dsh-crew`;
|
|
62
|
-
the official `web` profile receives only the 3080 bridge.
|
|
63
|
-
All production Worker/Reviewer model calls are executed by the isolated 3210
|
|
64
|
-
Crew Harness; 3080 is a control-plane bridge only.
|
|
69
|
+
The runtime is isolated under `~/.config/dsh-crew/harness` with `profile: dsh-crew`;
|
|
70
|
+
the official `web` profile receives only the 3080 bridge.
|
|
71
|
+
All production Worker/Reviewer model calls are executed by the isolated 3210
|
|
72
|
+
Crew Harness; 3080 is a control-plane bridge only.
|
|
65
73
|
|
|
66
74
|
## Legacy launcher migration
|
|
67
75
|
|
package/README.zh.md
CHANGED
|
@@ -16,8 +16,11 @@ dsh-crew integrate
|
|
|
16
16
|
dsh-crew status
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
打开 <http://127.0.0.1:3080> 进入日常控制台。Windows 安装会注册当前用户的
|
|
20
|
-
登录启动项,先启动 3080 控制台,再由官方桥接启动并监管隔离的 3210 Harness。
|
|
19
|
+
打开 <http://127.0.0.1:3080> 进入日常控制台。Windows 安装会注册当前用户的
|
|
20
|
+
登录启动项,先启动 3080 控制台,再由官方桥接启动并监管隔离的 3210 Harness。
|
|
21
|
+
|
|
22
|
+
全新安装只包含内置 DeepSeek 路由。其他 Provider 凭据、模型优先级和可选集成需要
|
|
23
|
+
在本机设置中自行配置,绝不会打包进发布版本。
|
|
21
24
|
|
|
22
25
|
| 界面 | 用途 |
|
|
23
26
|
| --- | --- |
|
|
@@ -38,25 +41,29 @@ dsh-crew status
|
|
|
38
41
|
## 常用命令
|
|
39
42
|
|
|
40
43
|
```bash
|
|
41
|
-
dsh-crew inspect # 实时能力与就绪度
|
|
42
|
-
dsh-crew jobs list # 任务与 Result Contract
|
|
43
|
-
dsh-crew providers list # 3210 Harness Provider 清单(不含密钥)
|
|
44
|
-
dsh-crew providers
|
|
45
|
-
|
|
46
|
-
dsh-crew
|
|
47
|
-
dsh-crew
|
|
48
|
-
dsh-crew
|
|
49
|
-
dsh-crew
|
|
50
|
-
dsh-crew
|
|
51
|
-
dsh-crew
|
|
52
|
-
dsh-crew
|
|
53
|
-
dsh-crew
|
|
44
|
+
dsh-crew inspect # 实时能力与就绪度
|
|
45
|
+
dsh-crew jobs list # 任务与 Result Contract
|
|
46
|
+
dsh-crew providers list # 3210 Harness Provider 清单(不含密钥)
|
|
47
|
+
dsh-crew providers migration-status # 检查旧 profile Provider,不会自动迁移
|
|
48
|
+
dsh-crew providers migrate-plan <provider>
|
|
49
|
+
dsh-crew providers migrate <provider> --plan <id> --confirm # 写入用户层,重启 3210 并验证
|
|
50
|
+
dsh-crew providers rollback-migration <provider> --plan <id> --confirm
|
|
51
|
+
dsh-crew providers probe <provider-id>
|
|
52
|
+
curl http://127.0.0.1:3210/_dsh/dsh-crew/credential-references # 仅查看引用/孤儿报告
|
|
53
|
+
dsh-crew credentials list # 不含密钥值的引用清单
|
|
54
|
+
dsh-crew credentials purge-plan env:NAME
|
|
55
|
+
dsh-crew credentials purge env:NAME --plan <plan-id> --expected-revision <sha256> --confirm
|
|
56
|
+
dsh-crew providers delete-plan <provider-id> --replacement-default <provider-id>
|
|
57
|
+
dsh-crew providers delete <provider-id> --plan <plan-id> --expected-revision <sha256> --confirm
|
|
58
|
+
dsh-crew releases list # 查看保留且已验证的版本
|
|
59
|
+
dsh-crew rollback <version> # 切换版本并验证 3210 runtime
|
|
60
|
+
dsh-crew update # 更新并修复已启用集成
|
|
54
61
|
dsh-crew uninstall # 移除受管文件,保留配置/备份
|
|
55
62
|
```
|
|
56
63
|
|
|
57
|
-
运行时隔离在 `~/.config/dsh-crew/harness`,使用 `profile: dsh-crew`;官方 `web` profile
|
|
58
|
-
只接收 3080 轻量桥接。
|
|
59
|
-
生产 Worker/Reviewer 的模型调用全部由隔离的 3210 Crew Harness 执行,3080 仅作为控制面桥接。
|
|
64
|
+
运行时隔离在 `~/.config/dsh-crew/harness`,使用 `profile: dsh-crew`;官方 `web` profile
|
|
65
|
+
只接收 3080 轻量桥接。
|
|
66
|
+
生产 Worker/Reviewer 的模型调用全部由隔离的 3210 Crew Harness 执行,3080 仅作为控制面桥接。
|
|
60
67
|
|
|
61
68
|
## 旧启动器迁移
|
|
62
69
|
|
package/lib/client.js
CHANGED
|
@@ -509,6 +509,15 @@ const COPY = {
|
|
|
509
509
|
providerState: (state) => `状态:${state}`,
|
|
510
510
|
providerModels: (count) => `${count} 个模型`,
|
|
511
511
|
providerJobs: (count) => `${count} 个运行任务`,
|
|
512
|
+
providerOfficialBuiltin: "· 官方内置(不可删除)",
|
|
513
|
+
providerSourceUnresolved: "· 来源未解析(安全锁定)",
|
|
514
|
+
providerLayerMigration: "检测到旧版 profile Provider",
|
|
515
|
+
providerLayerMigrationHint: "这些 Provider 仍在 Crew profile/base 层,所以 3210 原生页面不会显示删除。请先按迁移计划提升到 Harness 用户设置层;不会复制密钥。",
|
|
516
|
+
providerLayerMigrationPlan: "迁移计划",
|
|
517
|
+
providerLayerMigrate: "迁移到用户层",
|
|
518
|
+
providerLayerMigrating: "迁移中…",
|
|
519
|
+
providerLayerMigrationConfirm: "确认把这个 Provider 移到 Harness 用户设置层?会先保存可回滚快照,不会复制密钥值。",
|
|
520
|
+
providerLayerRollback: "回滚迁移",
|
|
512
521
|
providerDeletePlan: "生成删除计划",
|
|
513
522
|
providerDelete: "确认删除",
|
|
514
523
|
providerPlanReady: "计划已生成;再次确认后才会修改 3210 配置。",
|
|
@@ -519,6 +528,12 @@ const COPY = {
|
|
|
519
528
|
providerDeleteConfirm: "确认按计划删除这个 Harness Provider?会清理路由引用并重启 3210。",
|
|
520
529
|
providerDeleteBlocked: "当前 Provider 不能删除(内置、在用、已删除或缺少可用替换)。",
|
|
521
530
|
providerLifecycleError: "Provider 生命周期操作失败",
|
|
531
|
+
providerDeletePending: "配置可能已变更,事务仍待重启/验证;请刷新状态后选择回滚。",
|
|
532
|
+
providerRollbackContinue: "继续回滚验证",
|
|
533
|
+
recoveryUnresolved: "发现不可自动恢复的事务",
|
|
534
|
+
recoveryStorage: "存储项",
|
|
535
|
+
recoveryQuarantine: "隔离",
|
|
536
|
+
recoveryQuarantineConfirm: "该事务清单无法安全恢复。将其移入隔离区并阻止自动使用,继续?",
|
|
522
537
|
providerNoInventory: "暂时无法读取 3210 Provider inventory。",
|
|
523
538
|
credentialReferences: "Credential 引用",
|
|
524
539
|
credentialReferenceHint: "仅显示引用名、归属和孤儿状态;不会读取或删除密钥。",
|
|
@@ -529,8 +544,8 @@ const COPY = {
|
|
|
529
544
|
credentialPurging: "清理中…",
|
|
530
545
|
credentialPurgeUnavailable: "凭据清理不可用",
|
|
531
546
|
credentialPurgeUnverified: "已执行清理,但验证失败;引用仍保留用于恢复检查",
|
|
532
|
-
customProviders: "
|
|
533
|
-
addProvider: "+
|
|
547
|
+
customProviders: "多模态适配器",
|
|
548
|
+
addProvider: "+ 添加多模态适配器",
|
|
534
549
|
noCustomProviders: "暂无。添加后会出现在上方的视觉 / 生图 provider 选择里。",
|
|
535
550
|
providerName: "名称",
|
|
536
551
|
modelsField: "模型列表(逗号分隔)",
|
|
@@ -832,6 +847,15 @@ const COPY = {
|
|
|
832
847
|
providerState: (state) => `State: ${state}`,
|
|
833
848
|
providerModels: (count) => `${count} models`,
|
|
834
849
|
providerJobs: (count) => `${count} running jobs`,
|
|
850
|
+
providerOfficialBuiltin: "· official built-in (immutable)",
|
|
851
|
+
providerSourceUnresolved: "· source unresolved (locked)",
|
|
852
|
+
providerLayerMigration: "Legacy profile Providers detected",
|
|
853
|
+
providerLayerMigrationHint: "These Providers still exist in the Crew profile/base layer, so the native 3210 page cannot show Delete. Promote them to Harness user settings with an explicit plan; keys are never copied.",
|
|
854
|
+
providerLayerMigrationPlan: "Migration plan",
|
|
855
|
+
providerLayerMigrate: "Migrate to user layer",
|
|
856
|
+
providerLayerMigrating: "Migrating…",
|
|
857
|
+
providerLayerMigrationConfirm: "Move this Provider into Harness user settings? A rollback snapshot is saved first; credential values are never copied.",
|
|
858
|
+
providerLayerRollback: "Rollback migration",
|
|
835
859
|
providerDeletePlan: "Plan deletion",
|
|
836
860
|
providerDelete: "Confirm delete",
|
|
837
861
|
providerPlanReady: "Plan ready; a second confirmation is required before changing 3210 config.",
|
|
@@ -842,6 +866,12 @@ const COPY = {
|
|
|
842
866
|
providerDeleteConfirm: "Delete this Harness Provider per the plan? Routing refs will be scrubbed and 3210 restarted.",
|
|
843
867
|
providerDeleteBlocked: "This Provider cannot be deleted (built-in, in use, already absent, or missing a valid replacement).",
|
|
844
868
|
providerLifecycleError: "Provider lifecycle operation failed",
|
|
869
|
+
providerDeletePending: "Configuration may already be changed; the transaction still needs restart/verification. Refresh status and use rollback if needed.",
|
|
870
|
+
providerRollbackContinue: "Continue rollback verification",
|
|
871
|
+
recoveryUnresolved: "An unrecoverable recovery transaction is blocking deletion",
|
|
872
|
+
recoveryStorage: "storage entry",
|
|
873
|
+
recoveryQuarantine: "Quarantine",
|
|
874
|
+
recoveryQuarantineConfirm: "This recovery manifest cannot be safely reopened. Move it into the quarantine area and keep it out of automatic recovery?",
|
|
845
875
|
providerNoInventory: "3210 Provider inventory is temporarily unavailable.",
|
|
846
876
|
credentialReferences: "Credential references",
|
|
847
877
|
credentialReferenceHint: "Names, ownership, and orphan status only; values are never read or deleted.",
|
|
@@ -852,8 +882,8 @@ const COPY = {
|
|
|
852
882
|
credentialPurging: "Purging…",
|
|
853
883
|
credentialPurgeUnavailable: "Credential purge unavailable",
|
|
854
884
|
credentialPurgeUnverified: "Purge ran but verification failed; the reference remains visible for recovery",
|
|
855
|
-
customProviders: "
|
|
856
|
-
addProvider: "+ Add
|
|
885
|
+
customProviders: "Multimodal adapters",
|
|
886
|
+
addProvider: "+ Add multimodal adapter",
|
|
857
887
|
noCustomProviders: "None yet. Added providers appear in the vision / image-gen selects above.",
|
|
858
888
|
providerName: "Name",
|
|
859
889
|
modelsField: "Models (comma-separated)",
|
|
@@ -1959,13 +1989,13 @@ function WorkersPanel({ ctx }) {
|
|
|
1959
1989
|
applyPatch(patch);
|
|
1960
1990
|
};
|
|
1961
1991
|
const deleteHarnessProvider = async (record) => {
|
|
1962
|
-
if (!record || record.
|
|
1992
|
+
if (!record || record.delete_capability !== "supported" || record.desired_state === "absent" || Number(record.references?.active_jobs ?? 0) > 0) {
|
|
1963
1993
|
setNotice(copy.providerDeleteBlocked);
|
|
1964
1994
|
return;
|
|
1965
1995
|
}
|
|
1966
1996
|
let replacementDefault;
|
|
1967
1997
|
if (record.references?.harness_default === true) {
|
|
1968
|
-
const candidate = (providerInventory?.records ?? []).find((item) => item.id !== record.id && item.desired_state !== "absent" && item.
|
|
1998
|
+
const candidate = (providerInventory?.records ?? []).find((item) => item.id === "deepseek-official" && item.id !== record.id && item.desired_state !== "absent" && (item.models?.length ?? 0) > 0)?.id ?? (providerInventory?.records ?? []).find((item) => item.id !== record.id && item.desired_state !== "absent" && item.delete_capability !== "source-unresolved" && (item.models?.length ?? 0) > 0)?.id ?? "";
|
|
1969
1999
|
const entered = window.prompt(copy.providerReplacement, candidate);
|
|
1970
2000
|
if (!entered?.trim()) return;
|
|
1971
2001
|
replacementDefault = entered.trim();
|
|
@@ -2005,14 +2035,106 @@ function WorkersPanel({ ctx }) {
|
|
|
2005
2035
|
}
|
|
2006
2036
|
setNotice(copy.saved);
|
|
2007
2037
|
await refreshProviderInventory();
|
|
2038
|
+
} catch (error) {
|
|
2039
|
+
try {
|
|
2040
|
+
await refreshProviderInventory();
|
|
2041
|
+
} catch {}
|
|
2042
|
+
setNotice(`${String(error?.message ?? error)} · ${copy.providerDeletePending}`);
|
|
2043
|
+
} finally {
|
|
2044
|
+
setProviderLifecycleBusy(null);
|
|
2045
|
+
}
|
|
2046
|
+
};
|
|
2047
|
+
const migrateHarnessProvider = async (entry) => {
|
|
2048
|
+
if (!entry || !["promote-existing-user", "materialize-user"].includes(entry.action)) {
|
|
2049
|
+
setNotice(entry?.blocked_reason ?? copy.providerLayerMigrationHint);
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
if (!window.confirm(copy.providerLayerMigrationConfirm)) return;
|
|
2053
|
+
const id = String(entry.provider_id ?? "");
|
|
2054
|
+
if (!id) return;
|
|
2055
|
+
setProviderLifecycleBusy(`migrate:${id}`);
|
|
2056
|
+
setNotice(copy.providerLayerMigrating);
|
|
2057
|
+
try {
|
|
2058
|
+
const encoded = encodeURIComponent(id);
|
|
2059
|
+
const planned = await post(`/providers/${encoded}/migrate-plan`, {});
|
|
2060
|
+
if (!planned.ok || !planned.plan) throw new Error(planned.code ?? copy.providerLifecycleError);
|
|
2061
|
+
if (!window.confirm(`${copy.providerLayerMigrationConfirm}\n\n${id}`)) return;
|
|
2062
|
+
const result = await post(`/providers/${encoded}/migrate`, {
|
|
2063
|
+
plan_id: planned.plan.plan_id,
|
|
2064
|
+
confirm: true
|
|
2065
|
+
});
|
|
2066
|
+
if (!result.ok) throw new Error(result.code ?? result.error ?? copy.providerLifecycleError);
|
|
2067
|
+
if (result.restart_required === true && result.result?.state === "RESTART_PENDING") {
|
|
2068
|
+
const restartPath = "http://127.0.0.1:3080/_dsh/dsh-crew/supervisor/restart";
|
|
2069
|
+
const restarted = await readJson(await fetch(restartPath, {
|
|
2070
|
+
method: "POST",
|
|
2071
|
+
headers: { "content-type": "application/json" },
|
|
2072
|
+
body: JSON.stringify({ confirm: true })
|
|
2073
|
+
}), restartPath);
|
|
2074
|
+
if (!restarted.ok) throw new Error(restarted.code ?? restarted.error ?? copy.providerLifecycleError);
|
|
2075
|
+
const verified = await post(`/providers/${encoded}/verify-migration`, {
|
|
2076
|
+
transaction_id: planned.plan.plan_id,
|
|
2077
|
+
confirm: true
|
|
2078
|
+
});
|
|
2079
|
+
if (!verified.ok || verified.state !== "VERIFIED") throw new Error(verified.code ?? verified.error ?? copy.providerLifecycleError);
|
|
2080
|
+
}
|
|
2081
|
+
setNotice(copy.saved);
|
|
2082
|
+
await refreshProviderInventory();
|
|
2083
|
+
} catch (error) {
|
|
2084
|
+
try {
|
|
2085
|
+
await refreshProviderInventory();
|
|
2086
|
+
} catch {}
|
|
2087
|
+
setNotice(String(error?.message ?? error));
|
|
2088
|
+
} finally {
|
|
2089
|
+
setProviderLifecycleBusy(null);
|
|
2090
|
+
}
|
|
2091
|
+
};
|
|
2092
|
+
const rollbackHarnessMigration = async (record, transactionId) => {
|
|
2093
|
+
if (!record?.id || !transactionId || !window.confirm(copy.providerRollbackConfirm)) return;
|
|
2094
|
+
setProviderLifecycleBusy(`rollback-migrate:${record.id}`);
|
|
2095
|
+
setNotice(copy.working);
|
|
2096
|
+
try {
|
|
2097
|
+
const encoded = encodeURIComponent(record.id);
|
|
2098
|
+
const result = await post(`/providers/${encoded}/rollback-migration`, {
|
|
2099
|
+
transaction_id: transactionId,
|
|
2100
|
+
confirm: true
|
|
2101
|
+
});
|
|
2102
|
+
if (!result.ok) throw new Error(result.code ?? result.error ?? copy.providerLifecycleError);
|
|
2103
|
+
if (result.restart_required === true) {
|
|
2104
|
+
const restartPath = "http://127.0.0.1:3080/_dsh/dsh-crew/supervisor/restart";
|
|
2105
|
+
const restarted = await readJson(await fetch(restartPath, {
|
|
2106
|
+
method: "POST",
|
|
2107
|
+
headers: { "content-type": "application/json" },
|
|
2108
|
+
body: JSON.stringify({ confirm: true })
|
|
2109
|
+
}), restartPath);
|
|
2110
|
+
if (!restarted.ok) throw new Error(restarted.code ?? restarted.error ?? copy.providerLifecycleError);
|
|
2111
|
+
}
|
|
2112
|
+
setNotice(copy.saved);
|
|
2113
|
+
await refreshProviderInventory();
|
|
2008
2114
|
} catch (error) {
|
|
2009
2115
|
setNotice(String(error?.message ?? error));
|
|
2116
|
+
try {
|
|
2117
|
+
await refreshProviderInventory();
|
|
2118
|
+
} catch {}
|
|
2010
2119
|
} finally {
|
|
2011
2120
|
setProviderLifecycleBusy(null);
|
|
2012
2121
|
}
|
|
2013
2122
|
};
|
|
2014
|
-
const rollbackHarnessProvider = async (record, transactionId) => {
|
|
2015
|
-
|
|
2123
|
+
const rollbackHarnessProvider = async (record, transactionId, transactionState = "VERIFIED") => {
|
|
2124
|
+
const continuing = ["ROLLBACK_PENDING", "ROLLBACK_RESTART_PENDING"].includes(transactionState);
|
|
2125
|
+
const recoverable = [
|
|
2126
|
+
"PLANNED",
|
|
2127
|
+
"TOMBSTONE_APPLYING",
|
|
2128
|
+
"TOMBSTONE_APPLIED",
|
|
2129
|
+
"REFERENCES_APPLYING",
|
|
2130
|
+
"REFERENCES_APPLIED",
|
|
2131
|
+
"DECLARATIONS_APPLYING",
|
|
2132
|
+
"DECLARATIONS_APPLIED",
|
|
2133
|
+
"DELETE_RESTART_PENDING",
|
|
2134
|
+
"ROLLBACK_APPLYING",
|
|
2135
|
+
"ROLLBACK_RESTORED"
|
|
2136
|
+
].includes(transactionState);
|
|
2137
|
+
if (!record || !transactionId || !continuing && !recoverable && record.desired_state !== "absent") {
|
|
2016
2138
|
setNotice(copy.providerRollbackUnavailable);
|
|
2017
2139
|
return;
|
|
2018
2140
|
}
|
|
@@ -2021,7 +2143,11 @@ function WorkersPanel({ ctx }) {
|
|
|
2021
2143
|
setNotice(copy.working);
|
|
2022
2144
|
try {
|
|
2023
2145
|
const encoded = encodeURIComponent(record.id);
|
|
2024
|
-
const result =
|
|
2146
|
+
const result = continuing ? {
|
|
2147
|
+
ok: true,
|
|
2148
|
+
restart_required: true,
|
|
2149
|
+
state: "ROLLBACK_PENDING"
|
|
2150
|
+
} : await post(`/providers/${encoded}/rollback`, {
|
|
2025
2151
|
transaction_id: transactionId,
|
|
2026
2152
|
confirm: true
|
|
2027
2153
|
});
|
|
@@ -2043,6 +2169,32 @@ function WorkersPanel({ ctx }) {
|
|
|
2043
2169
|
setNotice(copy.saved);
|
|
2044
2170
|
await refreshProviderInventory();
|
|
2045
2171
|
} catch (error) {
|
|
2172
|
+
try {
|
|
2173
|
+
await refreshProviderInventory();
|
|
2174
|
+
} catch {}
|
|
2175
|
+
setNotice(`${String(error?.message ?? error)} · ${copy.providerDeletePending}`);
|
|
2176
|
+
} finally {
|
|
2177
|
+
setProviderLifecycleBusy(null);
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
const quarantineRecoveryTransaction = async (entry) => {
|
|
2181
|
+
const actionId = typeof entry?.action_id === "string" ? entry.action_id : "";
|
|
2182
|
+
if (!actionId || entry?.recoverable === true) return;
|
|
2183
|
+
if (!window.confirm(copy.recoveryQuarantineConfirm)) return;
|
|
2184
|
+
setProviderLifecycleBusy(`quarantine:${actionId}`);
|
|
2185
|
+
setNotice(copy.working);
|
|
2186
|
+
try {
|
|
2187
|
+
const result = await post("/providers/_recovery/quarantine", {
|
|
2188
|
+
action_id: actionId,
|
|
2189
|
+
confirm: true
|
|
2190
|
+
});
|
|
2191
|
+
if (!result.ok) throw new Error(result.code ?? result.error ?? copy.providerLifecycleError);
|
|
2192
|
+
setNotice(copy.saved);
|
|
2193
|
+
await refreshProviderInventory();
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
try {
|
|
2196
|
+
await refreshProviderInventory();
|
|
2197
|
+
} catch {}
|
|
2046
2198
|
setNotice(String(error?.message ?? error));
|
|
2047
2199
|
} finally {
|
|
2048
2200
|
setProviderLifecycleBusy(null);
|
|
@@ -3040,7 +3192,7 @@ function WorkersPanel({ ctx }) {
|
|
|
3040
3192
|
style: S.fieldLabel,
|
|
3041
3193
|
children: copy.workerProvider
|
|
3042
3194
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CustomSelect, {
|
|
3043
|
-
value: config.worker_provider_mode ?? "
|
|
3195
|
+
value: config.worker_provider_mode ?? "deepseek-official",
|
|
3044
3196
|
onChange: (v) => field("worker_provider_mode", v),
|
|
3045
3197
|
options: ["follow-dsh", "deepseek-official"].map((m) => ({
|
|
3046
3198
|
value: m,
|
|
@@ -3580,6 +3732,78 @@ function WorkersPanel({ ctx }) {
|
|
|
3580
3732
|
},
|
|
3581
3733
|
children: providerInventoryError
|
|
3582
3734
|
}),
|
|
3735
|
+
!providerInventoryError && (providerInventory?.migration?.providers?.length ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3736
|
+
style: {
|
|
3737
|
+
border: "1px solid rgba(201,135,53,0.45)",
|
|
3738
|
+
borderRadius: 7,
|
|
3739
|
+
padding: "8px 10px",
|
|
3740
|
+
marginBottom: 8
|
|
3741
|
+
},
|
|
3742
|
+
children: [
|
|
3743
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3744
|
+
style: {
|
|
3745
|
+
fontWeight: 650,
|
|
3746
|
+
fontSize: 12.5
|
|
3747
|
+
},
|
|
3748
|
+
children: copy.providerLayerMigration
|
|
3749
|
+
}),
|
|
3750
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3751
|
+
style: {
|
|
3752
|
+
fontSize: 11.5,
|
|
3753
|
+
opacity: .7,
|
|
3754
|
+
marginTop: 3
|
|
3755
|
+
},
|
|
3756
|
+
children: copy.providerLayerMigrationHint
|
|
3757
|
+
}),
|
|
3758
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3759
|
+
style: {
|
|
3760
|
+
fontSize: 11,
|
|
3761
|
+
marginTop: 6,
|
|
3762
|
+
fontFamily: "monospace"
|
|
3763
|
+
},
|
|
3764
|
+
children: (providerInventory.migration.providers ?? []).map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3765
|
+
style: {
|
|
3766
|
+
display: "flex",
|
|
3767
|
+
alignItems: "center",
|
|
3768
|
+
gap: 6,
|
|
3769
|
+
marginTop: 3
|
|
3770
|
+
},
|
|
3771
|
+
children: [
|
|
3772
|
+
entry.provider_id,
|
|
3773
|
+
" · ",
|
|
3774
|
+
entry.action,
|
|
3775
|
+
entry.harness_default ? " · Harness Default" : "",
|
|
3776
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
3777
|
+
["promote-existing-user", "materialize-user"].includes(entry.action) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3778
|
+
type: "button",
|
|
3779
|
+
style: {
|
|
3780
|
+
...S.btn,
|
|
3781
|
+
padding: "2px 7px"
|
|
3782
|
+
},
|
|
3783
|
+
disabled: providerLifecycleBusy !== null,
|
|
3784
|
+
onClick: () => {
|
|
3785
|
+
migrateHarnessProvider(entry);
|
|
3786
|
+
},
|
|
3787
|
+
children: providerLifecycleBusy === `migrate:${entry.provider_id}` ? copy.providerLayerMigrating : copy.providerLayerMigrate
|
|
3788
|
+
})
|
|
3789
|
+
]
|
|
3790
|
+
}, entry.provider_id))
|
|
3791
|
+
}),
|
|
3792
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
3793
|
+
href: "http://127.0.0.1:3210/",
|
|
3794
|
+
target: "_blank",
|
|
3795
|
+
rel: "noopener noreferrer",
|
|
3796
|
+
style: {
|
|
3797
|
+
...S.btn,
|
|
3798
|
+
display: "inline-block",
|
|
3799
|
+
textDecoration: "none",
|
|
3800
|
+
marginTop: 7,
|
|
3801
|
+
padding: "3px 8px"
|
|
3802
|
+
},
|
|
3803
|
+
children: [copy.providerLayerMigrationPlan, " → 3210"]
|
|
3804
|
+
})
|
|
3805
|
+
]
|
|
3806
|
+
}),
|
|
3583
3807
|
!providerInventoryError && (providerInventory?.records?.length ?? 0) === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3584
3808
|
style: {
|
|
3585
3809
|
fontSize: 12,
|
|
@@ -3588,9 +3812,34 @@ function WorkersPanel({ ctx }) {
|
|
|
3588
3812
|
children: copy.providerNoInventory
|
|
3589
3813
|
}),
|
|
3590
3814
|
(providerInventory?.records ?? []).map((record) => {
|
|
3591
|
-
const
|
|
3815
|
+
const immutable = record.delete_capability === "immutable-builtin";
|
|
3816
|
+
const unresolved = record.delete_capability === "source-unresolved";
|
|
3817
|
+
const blocked = immutable || unresolved || record.desired_state === "absent" || Number(record.references?.active_jobs ?? 0) > 0;
|
|
3592
3818
|
const lifecycle = record.desired_state === "absent" ? "absent" : record.lifecycle?.enabled === false ? "disabled" : record.lifecycle?.catalogued ? "catalogued" : "configured";
|
|
3593
|
-
const
|
|
3819
|
+
const lifecycleTransactions = providerInventory?.lifecycle_transactions ?? [];
|
|
3820
|
+
const recoveryTransactions = (providerInventory?.recovery_transactions ?? []).map((entry) => ({
|
|
3821
|
+
...entry,
|
|
3822
|
+
state: entry.phase
|
|
3823
|
+
}));
|
|
3824
|
+
const rollbackTransaction = [...lifecycleTransactions, ...recoveryTransactions].filter((entry) => entry.provider_id === record.id && [
|
|
3825
|
+
"VERIFIED",
|
|
3826
|
+
"RESTART_PENDING",
|
|
3827
|
+
"ROLLBACK_PENDING",
|
|
3828
|
+
"PLANNED",
|
|
3829
|
+
"TOMBSTONE_APPLYING",
|
|
3830
|
+
"TOMBSTONE_APPLIED",
|
|
3831
|
+
"REFERENCES_APPLYING",
|
|
3832
|
+
"REFERENCES_APPLIED",
|
|
3833
|
+
"DECLARATIONS_APPLYING",
|
|
3834
|
+
"DECLARATIONS_APPLIED",
|
|
3835
|
+
"DELETE_RESTART_PENDING",
|
|
3836
|
+
"ROLLBACK_RESTART_PENDING",
|
|
3837
|
+
"ROLLBACK_RESTORED",
|
|
3838
|
+
"ROLLBACK_APPLYING",
|
|
3839
|
+
"PREPARED",
|
|
3840
|
+
"USER_MATERIALIZED",
|
|
3841
|
+
"BASE_REMOVED"
|
|
3842
|
+
].includes(entry.state)).sort((a, b) => String(a.updated_at ?? "").localeCompare(String(b.updated_at ?? ""))).at(-1);
|
|
3594
3843
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3595
3844
|
style: {
|
|
3596
3845
|
display: "flex",
|
|
@@ -3619,12 +3868,20 @@ function WorkersPanel({ ctx }) {
|
|
|
3619
3868
|
style: { opacity: .65 },
|
|
3620
3869
|
children: copy.providerModels(record.models?.length ?? 0)
|
|
3621
3870
|
}),
|
|
3871
|
+
immutable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3872
|
+
style: { opacity: .65 },
|
|
3873
|
+
children: copy.providerOfficialBuiltin
|
|
3874
|
+
}),
|
|
3875
|
+
unresolved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3876
|
+
style: { color: "#c98735" },
|
|
3877
|
+
children: copy.providerSourceUnresolved
|
|
3878
|
+
}),
|
|
3622
3879
|
Number(record.references?.active_jobs ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3623
3880
|
style: { color: "#c98735" },
|
|
3624
3881
|
children: copy.providerJobs(record.references.active_jobs)
|
|
3625
3882
|
}),
|
|
3626
3883
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
3627
|
-
record.
|
|
3884
|
+
record.delete_capability === "supported" && record.desired_state !== "absent" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3628
3885
|
type: "button",
|
|
3629
3886
|
style: {
|
|
3630
3887
|
...S.btn,
|
|
@@ -3636,7 +3893,32 @@ function WorkersPanel({ ctx }) {
|
|
|
3636
3893
|
},
|
|
3637
3894
|
children: providerLifecycleBusy === record.id ? copy.working : copy.providerDeletePlan
|
|
3638
3895
|
}),
|
|
3639
|
-
record.desired_state === "absent"
|
|
3896
|
+
(record.desired_state === "absent" || [
|
|
3897
|
+
"PLANNED",
|
|
3898
|
+
"TOMBSTONE_APPLYING",
|
|
3899
|
+
"TOMBSTONE_APPLIED",
|
|
3900
|
+
"REFERENCES_APPLYING",
|
|
3901
|
+
"REFERENCES_APPLIED",
|
|
3902
|
+
"DECLARATIONS_APPLYING",
|
|
3903
|
+
"DECLARATIONS_APPLIED",
|
|
3904
|
+
"DELETE_RESTART_PENDING",
|
|
3905
|
+
"ROLLBACK_PENDING",
|
|
3906
|
+
"ROLLBACK_RESTART_PENDING",
|
|
3907
|
+
"ROLLBACK_RESTORED",
|
|
3908
|
+
"ROLLBACK_APPLYING"
|
|
3909
|
+
].includes(rollbackTransaction?.state)) && rollbackTransaction && rollbackTransaction.source !== "provider-layer-migration" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3910
|
+
type: "button",
|
|
3911
|
+
style: {
|
|
3912
|
+
...S.btn,
|
|
3913
|
+
padding: "2px 8px"
|
|
3914
|
+
},
|
|
3915
|
+
disabled: providerLifecycleBusy !== null,
|
|
3916
|
+
onClick: () => {
|
|
3917
|
+
rollbackHarnessProvider(record, rollbackTransaction.transaction_id, rollbackTransaction.state);
|
|
3918
|
+
},
|
|
3919
|
+
children: providerLifecycleBusy === `rollback:${record.id}` ? copy.working : rollbackTransaction.state === "ROLLBACK_PENDING" ? copy.providerRollbackContinue : copy.providerRollback
|
|
3920
|
+
}),
|
|
3921
|
+
rollbackTransaction?.source === "provider-layer-migration" && rollbackTransaction.transaction_id && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3640
3922
|
type: "button",
|
|
3641
3923
|
style: {
|
|
3642
3924
|
...S.btn,
|
|
@@ -3644,13 +3926,51 @@ function WorkersPanel({ ctx }) {
|
|
|
3644
3926
|
},
|
|
3645
3927
|
disabled: providerLifecycleBusy !== null,
|
|
3646
3928
|
onClick: () => {
|
|
3647
|
-
|
|
3929
|
+
rollbackHarnessMigration(record, rollbackTransaction.transaction_id);
|
|
3648
3930
|
},
|
|
3649
|
-
children: providerLifecycleBusy === `rollback:${record.id}` ? copy.working : copy.
|
|
3931
|
+
children: providerLifecycleBusy === `rollback-migrate:${record.id}` ? copy.working : copy.providerLayerRollback
|
|
3650
3932
|
})
|
|
3651
3933
|
]
|
|
3652
3934
|
}, record.id);
|
|
3653
3935
|
}),
|
|
3936
|
+
(providerInventory?.recovery_transactions ?? []).filter((entry) => entry?.unresolved === true || !entry?.provider_id).map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3937
|
+
style: {
|
|
3938
|
+
display: "flex",
|
|
3939
|
+
alignItems: "center",
|
|
3940
|
+
gap: 8,
|
|
3941
|
+
fontSize: 12,
|
|
3942
|
+
flexWrap: "wrap",
|
|
3943
|
+
color: "#c98735",
|
|
3944
|
+
marginTop: 6
|
|
3945
|
+
},
|
|
3946
|
+
children: [
|
|
3947
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3948
|
+
style: { fontWeight: 600 },
|
|
3949
|
+
children: copy.recoveryUnresolved
|
|
3950
|
+
}),
|
|
3951
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3952
|
+
style: S.mono,
|
|
3953
|
+
children: entry.storage_id ?? entry.transaction_id ?? "unknown"
|
|
3954
|
+
}),
|
|
3955
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3956
|
+
style: { opacity: .7 },
|
|
3957
|
+
children: entry.phase
|
|
3958
|
+
}),
|
|
3959
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
3960
|
+
entry.action_id && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3961
|
+
type: "button",
|
|
3962
|
+
style: {
|
|
3963
|
+
...S.btn,
|
|
3964
|
+
padding: "2px 8px"
|
|
3965
|
+
},
|
|
3966
|
+
disabled: providerLifecycleBusy !== null,
|
|
3967
|
+
onClick: () => {
|
|
3968
|
+
quarantineRecoveryTransaction(entry);
|
|
3969
|
+
},
|
|
3970
|
+
children: providerLifecycleBusy === `quarantine:${entry.action_id}` ? copy.working : copy.recoveryQuarantine
|
|
3971
|
+
})
|
|
3972
|
+
]
|
|
3973
|
+
}, `recovery:${entry.storage_id ?? entry.transaction_id ?? "unknown"}`)),
|
|
3654
3974
|
(credentialRefs.length > 0 || credentialUnverified.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3655
3975
|
style: {
|
|
3656
3976
|
marginTop: 10,
|