@ran-sh/dsh-crew 1.0.2 → 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 +4 -0
- package/README.zh.md +4 -0
- package/lib/client.js +183 -6
- package/official-web-bridge/lib/client.js +183 -6
- package/package.json +1 -1
- package/src/client/index.tsx +92 -4
- package/src/credential-reference.mjs +38 -0
- package/src/hub/index.mjs +483 -14
- package/src/install/npx-lifecycle.mjs +456 -398
- package/src/model-catalog.mjs +8 -1
- package/src/provider-delete-adapters.mjs +165 -17
- package/src/provider-inventory.mjs +17 -3
- package/src/provider-layer-migration-adapters.mjs +759 -0
- package/src/provider-layer-migration.mjs +198 -0
- package/src/provider-lifecycle-state.mjs +4 -1
- package/src/provider-profile-store.mjs +193 -4
- package/src/provider-settings-store.mjs +82 -6
- package/src/provider-store-lock.mjs +67 -0
- package/src/runtime-identity.mjs +2 -1
package/README.md
CHANGED
|
@@ -49,6 +49,10 @@ silently falling back.
|
|
|
49
49
|
dsh-crew inspect # live capabilities and readiness
|
|
50
50
|
dsh-crew jobs list # jobs and Result Contracts
|
|
51
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
|
|
52
56
|
dsh-crew providers probe <provider-id>
|
|
53
57
|
curl http://127.0.0.1:3210/_dsh/dsh-crew/credential-references # references/orphans only
|
|
54
58
|
dsh-crew credentials list # secret-free reference inventory
|
package/README.zh.md
CHANGED
|
@@ -44,6 +44,10 @@ dsh-crew status
|
|
|
44
44
|
dsh-crew inspect # 实时能力与就绪度
|
|
45
45
|
dsh-crew jobs list # 任务与 Result Contract
|
|
46
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
|
|
47
51
|
dsh-crew providers probe <provider-id>
|
|
48
52
|
curl http://127.0.0.1:3210/_dsh/dsh-crew/credential-references # 仅查看引用/孤儿报告
|
|
49
53
|
dsh-crew credentials list # 不含密钥值的引用清单
|
package/lib/client.js
CHANGED
|
@@ -511,6 +511,13 @@ const COPY = {
|
|
|
511
511
|
providerJobs: (count) => `${count} 个运行任务`,
|
|
512
512
|
providerOfficialBuiltin: "· 官方内置(不可删除)",
|
|
513
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: "回滚迁移",
|
|
514
521
|
providerDeletePlan: "生成删除计划",
|
|
515
522
|
providerDelete: "确认删除",
|
|
516
523
|
providerPlanReady: "计划已生成;再次确认后才会修改 3210 配置。",
|
|
@@ -537,8 +544,8 @@ const COPY = {
|
|
|
537
544
|
credentialPurging: "清理中…",
|
|
538
545
|
credentialPurgeUnavailable: "凭据清理不可用",
|
|
539
546
|
credentialPurgeUnverified: "已执行清理,但验证失败;引用仍保留用于恢复检查",
|
|
540
|
-
customProviders: "
|
|
541
|
-
addProvider: "+
|
|
547
|
+
customProviders: "多模态适配器",
|
|
548
|
+
addProvider: "+ 添加多模态适配器",
|
|
542
549
|
noCustomProviders: "暂无。添加后会出现在上方的视觉 / 生图 provider 选择里。",
|
|
543
550
|
providerName: "名称",
|
|
544
551
|
modelsField: "模型列表(逗号分隔)",
|
|
@@ -842,6 +849,13 @@ const COPY = {
|
|
|
842
849
|
providerJobs: (count) => `${count} running jobs`,
|
|
843
850
|
providerOfficialBuiltin: "· official built-in (immutable)",
|
|
844
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",
|
|
845
859
|
providerDeletePlan: "Plan deletion",
|
|
846
860
|
providerDelete: "Confirm delete",
|
|
847
861
|
providerPlanReady: "Plan ready; a second confirmation is required before changing 3210 config.",
|
|
@@ -868,8 +882,8 @@ const COPY = {
|
|
|
868
882
|
credentialPurging: "Purging…",
|
|
869
883
|
credentialPurgeUnavailable: "Credential purge unavailable",
|
|
870
884
|
credentialPurgeUnverified: "Purge ran but verification failed; the reference remains visible for recovery",
|
|
871
|
-
customProviders: "
|
|
872
|
-
addProvider: "+ Add
|
|
885
|
+
customProviders: "Multimodal adapters",
|
|
886
|
+
addProvider: "+ Add multimodal adapter",
|
|
873
887
|
noCustomProviders: "None yet. Added providers appear in the vision / image-gen selects above.",
|
|
874
888
|
providerName: "Name",
|
|
875
889
|
modelsField: "Models (comma-separated)",
|
|
@@ -2030,6 +2044,82 @@ function WorkersPanel({ ctx }) {
|
|
|
2030
2044
|
setProviderLifecycleBusy(null);
|
|
2031
2045
|
}
|
|
2032
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();
|
|
2114
|
+
} catch (error) {
|
|
2115
|
+
setNotice(String(error?.message ?? error));
|
|
2116
|
+
try {
|
|
2117
|
+
await refreshProviderInventory();
|
|
2118
|
+
} catch {}
|
|
2119
|
+
} finally {
|
|
2120
|
+
setProviderLifecycleBusy(null);
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2033
2123
|
const rollbackHarnessProvider = async (record, transactionId, transactionState = "VERIFIED") => {
|
|
2034
2124
|
const continuing = ["ROLLBACK_PENDING", "ROLLBACK_RESTART_PENDING"].includes(transactionState);
|
|
2035
2125
|
const recoverable = [
|
|
@@ -3642,6 +3732,78 @@ function WorkersPanel({ ctx }) {
|
|
|
3642
3732
|
},
|
|
3643
3733
|
children: providerInventoryError
|
|
3644
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
|
+
}),
|
|
3645
3807
|
!providerInventoryError && (providerInventory?.records?.length ?? 0) === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3646
3808
|
style: {
|
|
3647
3809
|
fontSize: 12,
|
|
@@ -3673,7 +3835,10 @@ function WorkersPanel({ ctx }) {
|
|
|
3673
3835
|
"DELETE_RESTART_PENDING",
|
|
3674
3836
|
"ROLLBACK_RESTART_PENDING",
|
|
3675
3837
|
"ROLLBACK_RESTORED",
|
|
3676
|
-
"ROLLBACK_APPLYING"
|
|
3838
|
+
"ROLLBACK_APPLYING",
|
|
3839
|
+
"PREPARED",
|
|
3840
|
+
"USER_MATERIALIZED",
|
|
3841
|
+
"BASE_REMOVED"
|
|
3677
3842
|
].includes(entry.state)).sort((a, b) => String(a.updated_at ?? "").localeCompare(String(b.updated_at ?? ""))).at(-1);
|
|
3678
3843
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3679
3844
|
style: {
|
|
@@ -3741,7 +3906,7 @@ function WorkersPanel({ ctx }) {
|
|
|
3741
3906
|
"ROLLBACK_RESTART_PENDING",
|
|
3742
3907
|
"ROLLBACK_RESTORED",
|
|
3743
3908
|
"ROLLBACK_APPLYING"
|
|
3744
|
-
].includes(rollbackTransaction?.state)) && rollbackTransaction && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3909
|
+
].includes(rollbackTransaction?.state)) && rollbackTransaction && rollbackTransaction.source !== "provider-layer-migration" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3745
3910
|
type: "button",
|
|
3746
3911
|
style: {
|
|
3747
3912
|
...S.btn,
|
|
@@ -3752,6 +3917,18 @@ function WorkersPanel({ ctx }) {
|
|
|
3752
3917
|
rollbackHarnessProvider(record, rollbackTransaction.transaction_id, rollbackTransaction.state);
|
|
3753
3918
|
},
|
|
3754
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", {
|
|
3922
|
+
type: "button",
|
|
3923
|
+
style: {
|
|
3924
|
+
...S.btn,
|
|
3925
|
+
padding: "2px 8px"
|
|
3926
|
+
},
|
|
3927
|
+
disabled: providerLifecycleBusy !== null,
|
|
3928
|
+
onClick: () => {
|
|
3929
|
+
rollbackHarnessMigration(record, rollbackTransaction.transaction_id);
|
|
3930
|
+
},
|
|
3931
|
+
children: providerLifecycleBusy === `rollback-migrate:${record.id}` ? copy.working : copy.providerLayerRollback
|
|
3755
3932
|
})
|
|
3756
3933
|
]
|
|
3757
3934
|
}, record.id);
|
|
@@ -511,6 +511,13 @@ const COPY = {
|
|
|
511
511
|
providerJobs: (count) => `${count} 个运行任务`,
|
|
512
512
|
providerOfficialBuiltin: "· 官方内置(不可删除)",
|
|
513
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: "回滚迁移",
|
|
514
521
|
providerDeletePlan: "生成删除计划",
|
|
515
522
|
providerDelete: "确认删除",
|
|
516
523
|
providerPlanReady: "计划已生成;再次确认后才会修改 3210 配置。",
|
|
@@ -537,8 +544,8 @@ const COPY = {
|
|
|
537
544
|
credentialPurging: "清理中…",
|
|
538
545
|
credentialPurgeUnavailable: "凭据清理不可用",
|
|
539
546
|
credentialPurgeUnverified: "已执行清理,但验证失败;引用仍保留用于恢复检查",
|
|
540
|
-
customProviders: "
|
|
541
|
-
addProvider: "+
|
|
547
|
+
customProviders: "多模态适配器",
|
|
548
|
+
addProvider: "+ 添加多模态适配器",
|
|
542
549
|
noCustomProviders: "暂无。添加后会出现在上方的视觉 / 生图 provider 选择里。",
|
|
543
550
|
providerName: "名称",
|
|
544
551
|
modelsField: "模型列表(逗号分隔)",
|
|
@@ -842,6 +849,13 @@ const COPY = {
|
|
|
842
849
|
providerJobs: (count) => `${count} running jobs`,
|
|
843
850
|
providerOfficialBuiltin: "· official built-in (immutable)",
|
|
844
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",
|
|
845
859
|
providerDeletePlan: "Plan deletion",
|
|
846
860
|
providerDelete: "Confirm delete",
|
|
847
861
|
providerPlanReady: "Plan ready; a second confirmation is required before changing 3210 config.",
|
|
@@ -868,8 +882,8 @@ const COPY = {
|
|
|
868
882
|
credentialPurging: "Purging…",
|
|
869
883
|
credentialPurgeUnavailable: "Credential purge unavailable",
|
|
870
884
|
credentialPurgeUnverified: "Purge ran but verification failed; the reference remains visible for recovery",
|
|
871
|
-
customProviders: "
|
|
872
|
-
addProvider: "+ Add
|
|
885
|
+
customProviders: "Multimodal adapters",
|
|
886
|
+
addProvider: "+ Add multimodal adapter",
|
|
873
887
|
noCustomProviders: "None yet. Added providers appear in the vision / image-gen selects above.",
|
|
874
888
|
providerName: "Name",
|
|
875
889
|
modelsField: "Models (comma-separated)",
|
|
@@ -2030,6 +2044,82 @@ function WorkersPanel({ ctx }) {
|
|
|
2030
2044
|
setProviderLifecycleBusy(null);
|
|
2031
2045
|
}
|
|
2032
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();
|
|
2114
|
+
} catch (error) {
|
|
2115
|
+
setNotice(String(error?.message ?? error));
|
|
2116
|
+
try {
|
|
2117
|
+
await refreshProviderInventory();
|
|
2118
|
+
} catch {}
|
|
2119
|
+
} finally {
|
|
2120
|
+
setProviderLifecycleBusy(null);
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2033
2123
|
const rollbackHarnessProvider = async (record, transactionId, transactionState = "VERIFIED") => {
|
|
2034
2124
|
const continuing = ["ROLLBACK_PENDING", "ROLLBACK_RESTART_PENDING"].includes(transactionState);
|
|
2035
2125
|
const recoverable = [
|
|
@@ -3642,6 +3732,78 @@ function WorkersPanel({ ctx }) {
|
|
|
3642
3732
|
},
|
|
3643
3733
|
children: providerInventoryError
|
|
3644
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
|
+
}),
|
|
3645
3807
|
!providerInventoryError && (providerInventory?.records?.length ?? 0) === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3646
3808
|
style: {
|
|
3647
3809
|
fontSize: 12,
|
|
@@ -3673,7 +3835,10 @@ function WorkersPanel({ ctx }) {
|
|
|
3673
3835
|
"DELETE_RESTART_PENDING",
|
|
3674
3836
|
"ROLLBACK_RESTART_PENDING",
|
|
3675
3837
|
"ROLLBACK_RESTORED",
|
|
3676
|
-
"ROLLBACK_APPLYING"
|
|
3838
|
+
"ROLLBACK_APPLYING",
|
|
3839
|
+
"PREPARED",
|
|
3840
|
+
"USER_MATERIALIZED",
|
|
3841
|
+
"BASE_REMOVED"
|
|
3677
3842
|
].includes(entry.state)).sort((a, b) => String(a.updated_at ?? "").localeCompare(String(b.updated_at ?? ""))).at(-1);
|
|
3678
3843
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3679
3844
|
style: {
|
|
@@ -3741,7 +3906,7 @@ function WorkersPanel({ ctx }) {
|
|
|
3741
3906
|
"ROLLBACK_RESTART_PENDING",
|
|
3742
3907
|
"ROLLBACK_RESTORED",
|
|
3743
3908
|
"ROLLBACK_APPLYING"
|
|
3744
|
-
].includes(rollbackTransaction?.state)) && rollbackTransaction && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3909
|
+
].includes(rollbackTransaction?.state)) && rollbackTransaction && rollbackTransaction.source !== "provider-layer-migration" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3745
3910
|
type: "button",
|
|
3746
3911
|
style: {
|
|
3747
3912
|
...S.btn,
|
|
@@ -3752,6 +3917,18 @@ function WorkersPanel({ ctx }) {
|
|
|
3752
3917
|
rollbackHarnessProvider(record, rollbackTransaction.transaction_id, rollbackTransaction.state);
|
|
3753
3918
|
},
|
|
3754
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", {
|
|
3922
|
+
type: "button",
|
|
3923
|
+
style: {
|
|
3924
|
+
...S.btn,
|
|
3925
|
+
padding: "2px 8px"
|
|
3926
|
+
},
|
|
3927
|
+
disabled: providerLifecycleBusy !== null,
|
|
3928
|
+
onClick: () => {
|
|
3929
|
+
rollbackHarnessMigration(record, rollbackTransaction.transaction_id);
|
|
3930
|
+
},
|
|
3931
|
+
children: providerLifecycleBusy === `rollback-migrate:${record.id}` ? copy.working : copy.providerLayerRollback
|
|
3755
3932
|
})
|
|
3756
3933
|
]
|
|
3757
3934
|
}, record.id);
|