@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 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: "自定义 Provider",
541
- addProvider: "+ 添加 Provider",
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: "Custom providers",
872
- addProvider: "+ Add provider",
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: "自定义 Provider",
541
- addProvider: "+ 添加 Provider",
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: "Custom providers",
872
- addProvider: "+ Add provider",
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ran-sh/dsh-crew",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "main": "./src/hub/entry.mjs",
6
6
  "bin": {