@ran-sh/dsh-crew 0.5.7 → 1.0.1
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 +6 -0
- package/README.zh.md +6 -0
- package/lib/client.js +223 -9
- package/official-web-bridge/lib/client.js +223 -9
- package/package.json +1 -1
- package/src/client/host-readiness.mjs +16 -3
- package/src/client/index.tsx +109 -8
- package/src/config-readiness.mjs +10 -0
- package/src/credential-lifecycle.mjs +73 -0
- package/src/credential-purge-adapters.mjs +127 -0
- package/src/credential-purge-state.mjs +59 -0
- package/src/credential-reference-inventory.mjs +78 -0
- package/src/dsh-cli-runtime.mjs +36 -9
- package/src/extension-contract.mjs +10 -8
- package/src/hub/index.mjs +175 -20
- package/src/hub-client.mjs +1 -1
- package/src/install/npx-lifecycle.mjs +241 -25
- package/src/job-contracts.mjs +9 -4
- package/src/official-web-bridge.mjs +6 -11
- package/src/provider-delete-adapters.mjs +28 -1
- package/src/provider-desired-state.mjs +37 -0
- package/src/provider-lifecycle-state.mjs +15 -1
- package/src/runtime-identity.mjs +24 -9
- package/src/runtime-readiness-snapshot.mjs +87 -0
- package/src/server.mjs +30 -5
package/README.md
CHANGED
|
@@ -46,8 +46,14 @@ dsh-crew inspect # live capabilities and readiness
|
|
|
46
46
|
dsh-crew jobs list # jobs and Result Contracts
|
|
47
47
|
dsh-crew providers list # 3210 Harness provider inventory (secret-free)
|
|
48
48
|
dsh-crew providers probe <provider-id>
|
|
49
|
+
curl http://127.0.0.1:3210/_dsh/dsh-crew/credential-references # references/orphans only
|
|
50
|
+
dsh-crew credentials list # secret-free reference inventory
|
|
51
|
+
dsh-crew credentials purge-plan env:NAME
|
|
52
|
+
dsh-crew credentials purge env:NAME --plan <plan-id> --expected-revision <sha256> --confirm
|
|
49
53
|
dsh-crew providers delete-plan <provider-id> --replacement-default <provider-id>
|
|
50
54
|
dsh-crew providers delete <provider-id> --plan <plan-id> --expected-revision <sha256> --confirm
|
|
55
|
+
dsh-crew releases list # retained validated payloads
|
|
56
|
+
dsh-crew rollback <version> # switch payload and verify the 3210 runtime
|
|
51
57
|
dsh-crew update # update and repair enabled integrations
|
|
52
58
|
dsh-crew uninstall # remove managed files, keep backups/config
|
|
53
59
|
```
|
package/README.zh.md
CHANGED
|
@@ -42,8 +42,14 @@ dsh-crew inspect # 实时能力与就绪度
|
|
|
42
42
|
dsh-crew jobs list # 任务与 Result Contract
|
|
43
43
|
dsh-crew providers list # 3210 Harness Provider 清单(不含密钥)
|
|
44
44
|
dsh-crew providers probe <provider-id>
|
|
45
|
+
curl http://127.0.0.1:3210/_dsh/dsh-crew/credential-references # 仅查看引用/孤儿报告
|
|
46
|
+
dsh-crew credentials list # 不含密钥值的引用清单
|
|
47
|
+
dsh-crew credentials purge-plan env:NAME
|
|
48
|
+
dsh-crew credentials purge env:NAME --plan <plan-id> --expected-revision <sha256> --confirm
|
|
45
49
|
dsh-crew providers delete-plan <provider-id> --replacement-default <provider-id>
|
|
46
50
|
dsh-crew providers delete <provider-id> --plan <plan-id> --expected-revision <sha256> --confirm
|
|
51
|
+
dsh-crew releases list # 查看保留且已验证的版本
|
|
52
|
+
dsh-crew rollback <version> # 切换版本并验证 3210 runtime
|
|
47
53
|
dsh-crew update # 更新并修复已启用集成
|
|
48
54
|
dsh-crew uninstall # 移除受管文件,保留配置/备份
|
|
49
55
|
```
|
package/lib/client.js
CHANGED
|
@@ -179,7 +179,16 @@ function componentState(status, keys, { aligned = false } = {}) {
|
|
|
179
179
|
if (evidence.some((value) => typeof value !== "boolean")) return READINESS_STATES.UNKNOWN;
|
|
180
180
|
return evidence.every(Boolean) && (!aligned || status.components.target_alignment === true) ? READINESS_STATES.READY : READINESS_STATES.DEGRADED;
|
|
181
181
|
}
|
|
182
|
-
function runtimeState(runtime) {
|
|
182
|
+
function runtimeState(runtime, readinessSnapshot) {
|
|
183
|
+
if (readinessSnapshot && typeof readinessSnapshot === "object") {
|
|
184
|
+
const identity = readinessSnapshot.runtime;
|
|
185
|
+
if (!(identity?.execution_plane === "hub-3210" && identity.profile === "dsh-crew" && Number(identity.listen_port) === 3210 && typeof identity.runtime_id === "string" && identity.runtime_id.trim())) return READINESS_STATES.UNAVAILABLE;
|
|
186
|
+
const row = Array.isArray(readinessSnapshot.readiness_matrix?.rows) ? readinessSnapshot.readiness_matrix.rows.find((entry) => entry?.id === "hub_compatibility") : void 0;
|
|
187
|
+
if (row?.status === "PASS") return READINESS_STATES.READY;
|
|
188
|
+
if (row?.status === "FAIL") return READINESS_STATES.UNAVAILABLE;
|
|
189
|
+
if (row?.status === "NOT_RUN" || row?.status === "SKIP") return READINESS_STATES.DEGRADED;
|
|
190
|
+
return READINESS_STATES.UNKNOWN;
|
|
191
|
+
}
|
|
183
192
|
if (runtime === void 0) return READINESS_STATES.UNKNOWN;
|
|
184
193
|
if (runtime === null) return READINESS_STATES.UNAVAILABLE;
|
|
185
194
|
return runtime?.ok === true && runtime.service === "dsh-crew-hub" ? READINESS_STATES.READY : READINESS_STATES.DEGRADED;
|
|
@@ -190,7 +199,7 @@ function bridgeState(surface) {
|
|
|
190
199
|
return READINESS_STATES.UNKNOWN;
|
|
191
200
|
}
|
|
192
201
|
/** Project only structured, non-secret installer/runtime evidence. */
|
|
193
|
-
function projectHostReadiness({ installStatus, runtime, surface } = {}) {
|
|
202
|
+
function projectHostReadiness({ installStatus, runtime, surface, readinessSnapshot } = {}) {
|
|
194
203
|
const codex = installStatus?.codex;
|
|
195
204
|
const claude = installStatus?.claude;
|
|
196
205
|
const zcode = installStatus?.zcode;
|
|
@@ -230,7 +239,7 @@ function projectHostReadiness({ installStatus, runtime, surface } = {}) {
|
|
|
230
239
|
},
|
|
231
240
|
{
|
|
232
241
|
id: "crew_harness",
|
|
233
|
-
state: runtimeState(runtime),
|
|
242
|
+
state: runtimeState(runtime, readinessSnapshot),
|
|
234
243
|
detail: runtime?.runtime_version ?? null
|
|
235
244
|
},
|
|
236
245
|
{
|
|
@@ -434,7 +443,7 @@ const COPY = {
|
|
|
434
443
|
"follow-dsh": "跟随 Harness · 使用各档独立的模型优先级",
|
|
435
444
|
"deepseek-official": "DeepSeek Official · 使用旧版固定模型路由"
|
|
436
445
|
},
|
|
437
|
-
workerProviderHint: "仅作用于 Hub 模式;Standalone
|
|
446
|
+
workerProviderHint: "仅作用于 Hub 模式;Standalone 仅保留为旧版迁移元数据,不是生产执行路径。",
|
|
438
447
|
workerModels: "Worker 模型",
|
|
439
448
|
refreshHarnessModels: "刷新 Harness 模型",
|
|
440
449
|
refreshingModels: "刷新中…",
|
|
@@ -503,11 +512,23 @@ const COPY = {
|
|
|
503
512
|
providerDeletePlan: "生成删除计划",
|
|
504
513
|
providerDelete: "确认删除",
|
|
505
514
|
providerPlanReady: "计划已生成;再次确认后才会修改 3210 配置。",
|
|
515
|
+
providerRollback: "回滚",
|
|
516
|
+
providerRollbackConfirm: "按已保存事务恢复这个 Harness Provider 及原有路由?",
|
|
517
|
+
providerRollbackUnavailable: "没有可验证的回滚事务",
|
|
506
518
|
providerReplacement: "替换 Harness 默认 Provider(输入 Provider id)",
|
|
507
519
|
providerDeleteConfirm: "确认按计划删除这个 Harness Provider?会清理路由引用并重启 3210。",
|
|
508
520
|
providerDeleteBlocked: "当前 Provider 不能删除(内置、在用、已删除或缺少可用替换)。",
|
|
509
521
|
providerLifecycleError: "Provider 生命周期操作失败",
|
|
510
522
|
providerNoInventory: "暂时无法读取 3210 Provider inventory。",
|
|
523
|
+
credentialReferences: "Credential 引用",
|
|
524
|
+
credentialReferenceHint: "仅显示引用名、归属和孤儿状态;不会读取或删除密钥。",
|
|
525
|
+
credentialOrphan: "孤儿 · 可单独申请清理",
|
|
526
|
+
credentialInUse: (count) => `使用中 · ${count} 个 Provider`,
|
|
527
|
+
credentialPurgePlan: "申请清理",
|
|
528
|
+
credentialPurgeConfirm: "这是不可恢复的 Crew-owned 凭据清理,确认继续?",
|
|
529
|
+
credentialPurging: "清理中…",
|
|
530
|
+
credentialPurgeUnavailable: "凭据清理不可用",
|
|
531
|
+
credentialPurgeUnverified: "已执行清理,但验证失败;引用仍保留用于恢复检查",
|
|
511
532
|
customProviders: "自定义 Provider",
|
|
512
533
|
addProvider: "+ 添加 Provider",
|
|
513
534
|
noCustomProviders: "暂无。添加后会出现在上方的视觉 / 生图 provider 选择里。",
|
|
@@ -745,7 +766,7 @@ const COPY = {
|
|
|
745
766
|
"follow-dsh": "Follow Harness · use each tier's ordered model priority",
|
|
746
767
|
"deepseek-official": "DeepSeek Official · use legacy fixed model routing"
|
|
747
768
|
},
|
|
748
|
-
workerProviderHint: "Applies to Hub workers only; Standalone
|
|
769
|
+
workerProviderHint: "Applies to Hub workers only; Standalone is legacy migration metadata only and is not a production execution path.",
|
|
749
770
|
workerModels: "Worker Models",
|
|
750
771
|
refreshHarnessModels: "Refresh Harness Models",
|
|
751
772
|
refreshingModels: "Refreshing…",
|
|
@@ -814,11 +835,23 @@ const COPY = {
|
|
|
814
835
|
providerDeletePlan: "Plan deletion",
|
|
815
836
|
providerDelete: "Confirm delete",
|
|
816
837
|
providerPlanReady: "Plan ready; a second confirmation is required before changing 3210 config.",
|
|
838
|
+
providerRollback: "Rollback",
|
|
839
|
+
providerRollbackConfirm: "Restore this Harness Provider and its previous routing from the saved transaction?",
|
|
840
|
+
providerRollbackUnavailable: "No verified rollback transaction is available",
|
|
817
841
|
providerReplacement: "Replacement Harness Default provider id",
|
|
818
842
|
providerDeleteConfirm: "Delete this Harness Provider per the plan? Routing refs will be scrubbed and 3210 restarted.",
|
|
819
843
|
providerDeleteBlocked: "This Provider cannot be deleted (built-in, in use, already absent, or missing a valid replacement).",
|
|
820
844
|
providerLifecycleError: "Provider lifecycle operation failed",
|
|
821
845
|
providerNoInventory: "3210 Provider inventory is temporarily unavailable.",
|
|
846
|
+
credentialReferences: "Credential references",
|
|
847
|
+
credentialReferenceHint: "Names, ownership, and orphan status only; values are never read or deleted.",
|
|
848
|
+
credentialOrphan: "orphan · separate purge approval required",
|
|
849
|
+
credentialInUse: (count) => `in use · ${count} provider(s)`,
|
|
850
|
+
credentialPurgePlan: "Purge…",
|
|
851
|
+
credentialPurgeConfirm: "This irreversibly removes a Crew-owned credential. Continue?",
|
|
852
|
+
credentialPurging: "Purging…",
|
|
853
|
+
credentialPurgeUnavailable: "Credential purge unavailable",
|
|
854
|
+
credentialPurgeUnverified: "Purge ran but verification failed; the reference remains visible for recovery",
|
|
822
855
|
customProviders: "Custom providers",
|
|
823
856
|
addProvider: "+ Add provider",
|
|
824
857
|
noCustomProviders: "None yet. Added providers appear in the vision / image-gen selects above.",
|
|
@@ -1406,6 +1439,9 @@ function WorkersPanel({ ctx }) {
|
|
|
1406
1439
|
const [modelCatalogBusy, setModelCatalogBusy] = (0, react.useState)(false);
|
|
1407
1440
|
const [providerInventory, setProviderInventory] = (0, react.useState)(null);
|
|
1408
1441
|
const [providerHealth, setProviderHealth] = (0, react.useState)([]);
|
|
1442
|
+
const [credentialRefs, setCredentialRefs] = (0, react.useState)([]);
|
|
1443
|
+
const [credentialUnverified, setCredentialUnverified] = (0, react.useState)([]);
|
|
1444
|
+
const [credentialLifecycleBusy, setCredentialLifecycleBusy] = (0, react.useState)(null);
|
|
1409
1445
|
const [providerInventoryError, setProviderInventoryError] = (0, react.useState)("");
|
|
1410
1446
|
const [providerLifecycleBusy, setProviderLifecycleBusy] = (0, react.useState)(null);
|
|
1411
1447
|
const [modelPickerTier, setModelPickerTier] = (0, react.useState)(null);
|
|
@@ -1414,6 +1450,7 @@ function WorkersPanel({ ctx }) {
|
|
|
1414
1450
|
const [expandedSections, setExpandedSections] = (0, react.useState)(() => readSectionState(sectionStorage()));
|
|
1415
1451
|
const [surface, setSurface] = (0, react.useState)("detecting");
|
|
1416
1452
|
const [runtimeInfo, setRuntimeInfo] = (0, react.useState)(void 0);
|
|
1453
|
+
const [readinessSnapshot, setReadinessSnapshot] = (0, react.useState)(void 0);
|
|
1417
1454
|
const toggleSection = (sectionId) => {
|
|
1418
1455
|
setExpandedSections((current) => ({
|
|
1419
1456
|
...current,
|
|
@@ -1494,13 +1531,15 @@ function WorkersPanel({ ctx }) {
|
|
|
1494
1531
|
}, [detectSurface]);
|
|
1495
1532
|
const refreshAll = (0, react.useCallback)(async () => {
|
|
1496
1533
|
try {
|
|
1497
|
-
const [j, s, c, pr, pi, ph] = await Promise.all([
|
|
1534
|
+
const [j, s, c, pr, pi, ph, cr, ext] = await Promise.all([
|
|
1498
1535
|
get("/jobs"),
|
|
1499
1536
|
get("/install/status"),
|
|
1500
1537
|
get("/config"),
|
|
1501
1538
|
get("/presets"),
|
|
1502
1539
|
get("/providers").catch(() => null),
|
|
1503
|
-
get("/provider-health").catch(() => null)
|
|
1540
|
+
get("/provider-health").catch(() => null),
|
|
1541
|
+
get("/credential-references").catch(() => null),
|
|
1542
|
+
get("/extension").catch(() => null)
|
|
1504
1543
|
]);
|
|
1505
1544
|
if (j.ok) setJobs(j.jobs ?? []);
|
|
1506
1545
|
if (s.ok) setStatus(s.status);
|
|
@@ -1514,6 +1553,11 @@ function WorkersPanel({ ctx }) {
|
|
|
1514
1553
|
}))]);
|
|
1515
1554
|
if (pi?.ok) setProviderInventory(pi);
|
|
1516
1555
|
if (ph?.ok) setProviderHealth(ph.health ?? []);
|
|
1556
|
+
if (cr?.ok) {
|
|
1557
|
+
setCredentialRefs(cr.records ?? []);
|
|
1558
|
+
setCredentialUnverified(cr.unverified_purges ?? []);
|
|
1559
|
+
}
|
|
1560
|
+
if (ext?.ok) setReadinessSnapshot(ext.extension?.readiness_snapshot ?? void 0);
|
|
1517
1561
|
} catch {}
|
|
1518
1562
|
}, [get]);
|
|
1519
1563
|
(0, react.useEffect)(() => {
|
|
@@ -1547,14 +1591,59 @@ function WorkersPanel({ ctx }) {
|
|
|
1547
1591
|
const refreshProviderInventory = (0, react.useCallback)(async () => {
|
|
1548
1592
|
setProviderInventoryError("");
|
|
1549
1593
|
try {
|
|
1550
|
-
const [result, health] = await Promise.all([
|
|
1594
|
+
const [result, health, refs] = await Promise.all([
|
|
1595
|
+
get("/providers"),
|
|
1596
|
+
get("/provider-health").catch(() => null),
|
|
1597
|
+
get("/credential-references").catch(() => null)
|
|
1598
|
+
]);
|
|
1551
1599
|
if (!result.ok) throw new Error(result.error ?? copy.providerNoInventory);
|
|
1552
1600
|
setProviderInventory(result);
|
|
1553
1601
|
if (health?.ok) setProviderHealth(health.health ?? []);
|
|
1602
|
+
if (refs?.ok) {
|
|
1603
|
+
setCredentialRefs(refs.records ?? []);
|
|
1604
|
+
setCredentialUnverified(refs.unverified_purges ?? []);
|
|
1605
|
+
}
|
|
1554
1606
|
} catch (error) {
|
|
1555
1607
|
setProviderInventoryError(error?.message ?? copy.providerNoInventory);
|
|
1556
1608
|
}
|
|
1557
1609
|
}, [get, copy]);
|
|
1610
|
+
const purgeCredentialRef = (0, react.useCallback)(async (ref) => {
|
|
1611
|
+
if (ref?.ownership !== "crew-owned" || ref?.orphan !== true || ref?.purge_capability !== "eligible") return;
|
|
1612
|
+
const referenceId = String(ref.reference_id ?? "");
|
|
1613
|
+
if (!referenceId || !window.confirm(copy.credentialPurgeConfirm)) return;
|
|
1614
|
+
setCredentialLifecycleBusy(referenceId);
|
|
1615
|
+
setNotice(copy.working);
|
|
1616
|
+
try {
|
|
1617
|
+
const encoded = encodeURIComponent(referenceId);
|
|
1618
|
+
const planned = await post(`/credential-references/${encoded}/purge-plan`, {});
|
|
1619
|
+
if (!planned.ok || !planned.plan) throw new Error(planned.code ?? copy.credentialPurgeUnavailable);
|
|
1620
|
+
if (!window.confirm(`${copy.credentialPurgeConfirm}\n\n${referenceId}`)) return;
|
|
1621
|
+
const result = await readJson(await fetch(withLang(`/credential-references/${encoded}`), {
|
|
1622
|
+
method: "DELETE",
|
|
1623
|
+
headers: { "content-type": "application/json" },
|
|
1624
|
+
body: JSON.stringify({
|
|
1625
|
+
plan_id: planned.plan.plan_id,
|
|
1626
|
+
expected_revision: planned.plan.expected_revision,
|
|
1627
|
+
confirm: true,
|
|
1628
|
+
lang: locale === "zh" ? "zh" : "en"
|
|
1629
|
+
})
|
|
1630
|
+
}), `/credential-references/${encoded}`);
|
|
1631
|
+
if (!result.ok || result.state !== "VERIFIED") throw new Error(result.code ?? copy.credentialPurgeUnavailable);
|
|
1632
|
+
setNotice(copy.saved);
|
|
1633
|
+
await refreshProviderInventory();
|
|
1634
|
+
} catch (error) {
|
|
1635
|
+
setNotice(String(error?.message ?? error));
|
|
1636
|
+
} finally {
|
|
1637
|
+
setCredentialLifecycleBusy(null);
|
|
1638
|
+
}
|
|
1639
|
+
}, [
|
|
1640
|
+
copy,
|
|
1641
|
+
locale,
|
|
1642
|
+
post,
|
|
1643
|
+
readJson,
|
|
1644
|
+
refreshProviderInventory,
|
|
1645
|
+
withLang
|
|
1646
|
+
]);
|
|
1558
1647
|
(0, react.useEffect)(() => {
|
|
1559
1648
|
if (surface === CREW_UI_SURFACES.OFFICIAL) refreshHarnessModels();
|
|
1560
1649
|
}, [surface, refreshHarnessModels]);
|
|
@@ -1922,6 +2011,43 @@ function WorkersPanel({ ctx }) {
|
|
|
1922
2011
|
setProviderLifecycleBusy(null);
|
|
1923
2012
|
}
|
|
1924
2013
|
};
|
|
2014
|
+
const rollbackHarnessProvider = async (record, transactionId) => {
|
|
2015
|
+
if (!record || !transactionId || record.desired_state !== "absent") {
|
|
2016
|
+
setNotice(copy.providerRollbackUnavailable);
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
if (!window.confirm(copy.providerRollbackConfirm)) return;
|
|
2020
|
+
setProviderLifecycleBusy(`rollback:${record.id}`);
|
|
2021
|
+
setNotice(copy.working);
|
|
2022
|
+
try {
|
|
2023
|
+
const encoded = encodeURIComponent(record.id);
|
|
2024
|
+
const result = await post(`/providers/${encoded}/rollback`, {
|
|
2025
|
+
transaction_id: transactionId,
|
|
2026
|
+
confirm: true
|
|
2027
|
+
});
|
|
2028
|
+
if (!result.ok) throw new Error(result.code ?? result.error ?? copy.providerLifecycleError);
|
|
2029
|
+
if (result.restart_required === true && result.state === "ROLLBACK_PENDING") {
|
|
2030
|
+
const restartPath = "http://127.0.0.1:3080/_dsh/dsh-crew/supervisor/restart";
|
|
2031
|
+
const restarted = await readJson(await fetch(restartPath, {
|
|
2032
|
+
method: "POST",
|
|
2033
|
+
headers: { "content-type": "application/json" },
|
|
2034
|
+
body: JSON.stringify({ confirm: true })
|
|
2035
|
+
}), restartPath);
|
|
2036
|
+
if (!restarted.ok) throw new Error(restarted.code ?? restarted.error ?? copy.providerLifecycleError);
|
|
2037
|
+
const verified = await post(`/providers/${encoded}/verify-rollback`, {
|
|
2038
|
+
transaction_id: transactionId,
|
|
2039
|
+
confirm: true
|
|
2040
|
+
});
|
|
2041
|
+
if (!verified.ok || verified.state !== "ROLLED_BACK") throw new Error(verified.code ?? verified.error ?? copy.providerLifecycleError);
|
|
2042
|
+
} else if (result.state !== "ROLLED_BACK") throw new Error(result.code ?? copy.providerLifecycleError);
|
|
2043
|
+
setNotice(copy.saved);
|
|
2044
|
+
await refreshProviderInventory();
|
|
2045
|
+
} catch (error) {
|
|
2046
|
+
setNotice(String(error?.message ?? error));
|
|
2047
|
+
} finally {
|
|
2048
|
+
setProviderLifecycleBusy(null);
|
|
2049
|
+
}
|
|
2050
|
+
};
|
|
1925
2051
|
/** Card: title + one-line description header, then its fields. */
|
|
1926
2052
|
const block = (text, fields, gridded = true) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1927
2053
|
style: S.block,
|
|
@@ -2021,7 +2147,8 @@ function WorkersPanel({ ctx }) {
|
|
|
2021
2147
|
const hostReadiness = projectHostReadiness({
|
|
2022
2148
|
installStatus: status,
|
|
2023
2149
|
runtime: runtimeInfo,
|
|
2024
|
-
surface
|
|
2150
|
+
surface,
|
|
2151
|
+
readinessSnapshot
|
|
2025
2152
|
});
|
|
2026
2153
|
if (!currentSurfaceResponsibilities.fullControlPlane) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MinimalCrewPanel, {
|
|
2027
2154
|
locale,
|
|
@@ -3463,6 +3590,7 @@ function WorkersPanel({ ctx }) {
|
|
|
3463
3590
|
(providerInventory?.records ?? []).map((record) => {
|
|
3464
3591
|
const blocked = record.ownership === "harness" || record.desired_state === "absent" || Number(record.references?.active_jobs ?? 0) > 0;
|
|
3465
3592
|
const lifecycle = record.desired_state === "absent" ? "absent" : record.lifecycle?.enabled === false ? "disabled" : record.lifecycle?.catalogued ? "catalogued" : "configured";
|
|
3593
|
+
const rollbackTransaction = (providerInventory?.lifecycle_transactions ?? []).find((entry) => entry.provider_id === record.id && ["VERIFIED", "RESTART_PENDING"].includes(entry.state));
|
|
3466
3594
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3467
3595
|
style: {
|
|
3468
3596
|
display: "flex",
|
|
@@ -3507,9 +3635,95 @@ function WorkersPanel({ ctx }) {
|
|
|
3507
3635
|
deleteHarnessProvider(record);
|
|
3508
3636
|
},
|
|
3509
3637
|
children: providerLifecycleBusy === record.id ? copy.working : copy.providerDeletePlan
|
|
3638
|
+
}),
|
|
3639
|
+
record.desired_state === "absent" && rollbackTransaction && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3640
|
+
type: "button",
|
|
3641
|
+
style: {
|
|
3642
|
+
...S.btn,
|
|
3643
|
+
padding: "2px 8px"
|
|
3644
|
+
},
|
|
3645
|
+
disabled: providerLifecycleBusy !== null,
|
|
3646
|
+
onClick: () => {
|
|
3647
|
+
rollbackHarnessProvider(record, rollbackTransaction.transaction_id);
|
|
3648
|
+
},
|
|
3649
|
+
children: providerLifecycleBusy === `rollback:${record.id}` ? copy.working : copy.providerRollback
|
|
3510
3650
|
})
|
|
3511
3651
|
]
|
|
3512
3652
|
}, record.id);
|
|
3653
|
+
}),
|
|
3654
|
+
(credentialRefs.length > 0 || credentialUnverified.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3655
|
+
style: {
|
|
3656
|
+
marginTop: 10,
|
|
3657
|
+
borderTop: "1px solid rgba(128,128,128,0.16)",
|
|
3658
|
+
paddingTop: 8
|
|
3659
|
+
},
|
|
3660
|
+
children: [
|
|
3661
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3662
|
+
style: {
|
|
3663
|
+
fontWeight: 600,
|
|
3664
|
+
fontSize: 12.5
|
|
3665
|
+
},
|
|
3666
|
+
children: copy.credentialReferences
|
|
3667
|
+
}),
|
|
3668
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3669
|
+
style: {
|
|
3670
|
+
fontSize: 11,
|
|
3671
|
+
opacity: .6,
|
|
3672
|
+
margin: "3px 0 7px"
|
|
3673
|
+
},
|
|
3674
|
+
children: copy.credentialReferenceHint
|
|
3675
|
+
}),
|
|
3676
|
+
credentialUnverified.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3677
|
+
style: {
|
|
3678
|
+
color: "#c55",
|
|
3679
|
+
fontSize: 11.5
|
|
3680
|
+
},
|
|
3681
|
+
children: [
|
|
3682
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3683
|
+
style: S.mono,
|
|
3684
|
+
children: entry.reference_id
|
|
3685
|
+
}),
|
|
3686
|
+
" · ",
|
|
3687
|
+
copy.credentialPurgeUnverified
|
|
3688
|
+
]
|
|
3689
|
+
}, `unverified:${entry.reference_id}`)),
|
|
3690
|
+
credentialRefs.map((ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3691
|
+
style: {
|
|
3692
|
+
display: "flex",
|
|
3693
|
+
alignItems: "center",
|
|
3694
|
+
gap: 8,
|
|
3695
|
+
fontSize: 11.5,
|
|
3696
|
+
flexWrap: "wrap"
|
|
3697
|
+
},
|
|
3698
|
+
children: [
|
|
3699
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3700
|
+
style: S.mono,
|
|
3701
|
+
children: ref.reference_id
|
|
3702
|
+
}),
|
|
3703
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3704
|
+
style: { opacity: .65 },
|
|
3705
|
+
children: ref.ownership
|
|
3706
|
+
}),
|
|
3707
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3708
|
+
style: { color: ref.orphan ? "#c98735" : void 0 },
|
|
3709
|
+
children: ref.orphan ? copy.credentialOrphan : copy.credentialInUse(ref.consumer_count ?? 0)
|
|
3710
|
+
}),
|
|
3711
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
3712
|
+
ref.ownership === "crew-owned" && ref.orphan === true && ref.purge_capability === "eligible" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3713
|
+
type: "button",
|
|
3714
|
+
style: {
|
|
3715
|
+
...S.btn,
|
|
3716
|
+
padding: "2px 8px"
|
|
3717
|
+
},
|
|
3718
|
+
disabled: credentialLifecycleBusy !== null,
|
|
3719
|
+
onClick: () => {
|
|
3720
|
+
purgeCredentialRef(ref);
|
|
3721
|
+
},
|
|
3722
|
+
children: credentialLifecycleBusy === ref.reference_id ? copy.credentialPurging : copy.credentialPurgePlan
|
|
3723
|
+
})
|
|
3724
|
+
]
|
|
3725
|
+
}, ref.reference_id))
|
|
3726
|
+
]
|
|
3513
3727
|
})
|
|
3514
3728
|
]
|
|
3515
3729
|
}),
|