@narumitw/pi-sync 0.49.14 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -241
- package/dist/chunks/chunk-2B2ELUF5.ts +29 -0
- package/dist/chunks/chunk-2B2ELUF5.ts.map +7 -0
- package/dist/chunks/chunk-6LP3MCR2.ts +115 -0
- package/dist/chunks/chunk-6LP3MCR2.ts.map +7 -0
- package/dist/chunks/chunk-6UHKGALE.ts +134 -0
- package/dist/chunks/chunk-6UHKGALE.ts.map +7 -0
- package/dist/chunks/{chunk-RGJ6SOYR.ts → chunk-AUSDLD6F.ts} +4 -4
- package/dist/chunks/chunk-AUSDLD6F.ts.map +7 -0
- package/dist/chunks/{chunk-3NDD5QPM.ts → chunk-AXXS2BPA.ts} +9 -9
- package/dist/chunks/chunk-AXXS2BPA.ts.map +7 -0
- package/dist/chunks/{chunk-APZVBSM6.ts → chunk-C7XY5NF5.ts} +214 -365
- package/dist/chunks/chunk-C7XY5NF5.ts.map +7 -0
- package/dist/chunks/{chunk-4U6AFLJB.ts → chunk-CHH26D4F.ts} +3 -2
- package/dist/chunks/chunk-CHH26D4F.ts.map +7 -0
- package/dist/chunks/chunk-CPEHGXJV.ts +50 -0
- package/dist/chunks/chunk-CPEHGXJV.ts.map +7 -0
- package/dist/chunks/{chunk-5MQ76BK5.ts → chunk-D5X27LCA.ts} +2 -2
- package/dist/chunks/chunk-D5X27LCA.ts.map +7 -0
- package/dist/chunks/{chunk-LJDSHNS3.ts → chunk-GWMHAQG4.ts} +4 -4
- package/dist/chunks/chunk-GWMHAQG4.ts.map +7 -0
- package/dist/chunks/{chunk-FR6CVXAL.ts → chunk-HUDQATCL.ts} +25 -25
- package/dist/chunks/chunk-HUDQATCL.ts.map +7 -0
- package/dist/chunks/{chunk-YQ6UW7IF.ts → chunk-JAAKLL6M.ts} +2 -2
- package/dist/chunks/chunk-JAAKLL6M.ts.map +7 -0
- package/dist/chunks/chunk-JSYMGJQQ.ts +51 -0
- package/dist/chunks/chunk-JSYMGJQQ.ts.map +7 -0
- package/dist/chunks/chunk-L7TBO32M.ts +25 -0
- package/dist/chunks/chunk-L7TBO32M.ts.map +7 -0
- package/dist/chunks/{chunk-4F3W4CSC.ts → chunk-LCRI3ABW.ts} +22 -8
- package/dist/chunks/chunk-LCRI3ABW.ts.map +7 -0
- package/dist/chunks/chunk-LTBWGSBV.ts +12 -0
- package/dist/chunks/chunk-LTBWGSBV.ts.map +7 -0
- package/dist/chunks/chunk-NJFUIVYL.ts +17 -0
- package/dist/chunks/chunk-NJFUIVYL.ts.map +7 -0
- package/dist/chunks/{chunk-EWX3TPLQ.ts → chunk-OEX4ABY6.ts} +39 -37
- package/dist/chunks/chunk-OEX4ABY6.ts.map +7 -0
- package/dist/chunks/chunk-OUHGBFYF.ts +1255 -0
- package/dist/chunks/chunk-OUHGBFYF.ts.map +7 -0
- package/dist/chunks/{chunk-HGV24P4T.ts → chunk-Q2TUM5X6.ts} +3 -3
- package/dist/chunks/chunk-Q2TUM5X6.ts.map +7 -0
- package/dist/chunks/{chunk-BHJUISB4.ts → chunk-SL64RLXR.ts} +8 -6
- package/dist/chunks/chunk-SL64RLXR.ts.map +7 -0
- package/dist/chunks/chunk-T3HH3UEJ.ts +96 -0
- package/dist/chunks/chunk-T3HH3UEJ.ts.map +7 -0
- package/dist/chunks/chunk-TEN2ZBJV.ts +101 -0
- package/dist/chunks/chunk-TEN2ZBJV.ts.map +7 -0
- package/dist/chunks/chunk-THMF4BVL.ts +18 -0
- package/dist/chunks/chunk-THMF4BVL.ts.map +7 -0
- package/dist/chunks/{chunk-LONPFFNP.ts → chunk-X5OM6GXT.ts} +9 -7
- package/dist/chunks/chunk-X5OM6GXT.ts.map +7 -0
- package/dist/chunks/chunk-XBX4HBLG.ts +57 -0
- package/dist/chunks/chunk-XBX4HBLG.ts.map +7 -0
- package/dist/chunks/{chunk-5WJNWFTG.ts → chunk-YKJHPTGO.ts} +23 -55
- package/dist/chunks/chunk-YKJHPTGO.ts.map +7 -0
- package/dist/chunks/chunk-Z4DQF6L3.ts +12 -0
- package/dist/chunks/chunk-Z4DQF6L3.ts.map +7 -0
- package/dist/chunks/{chunk-F5QL2GPY.ts → chunk-ZI5MXMEX.ts} +5 -99
- package/dist/chunks/chunk-ZI5MXMEX.ts.map +7 -0
- package/dist/chunks/{file-selection-4UABOP44.ts → file-selection-FTTKAOYX.ts} +10 -7
- package/dist/chunks/file-selection-FTTKAOYX.ts.map +7 -0
- package/dist/chunks/{git-backend-E3UOUUOM.ts → git-backend-OQW46G2X.ts} +42 -17
- package/dist/chunks/git-backend-OQW46G2X.ts.map +7 -0
- package/dist/chunks/manager-result-dispatcher-U23ODD32.ts +22 -0
- package/dist/chunks/manager-ui-ABATHC36.ts +1680 -0
- package/dist/chunks/manager-ui-ABATHC36.ts.map +7 -0
- package/dist/chunks/remote-selection-ui-ZE3TY67S.ts +21 -0
- package/dist/chunks/{s3-backend-3QZGO2EF.ts → s3-backend-3P33LQHU.ts} +96 -18
- package/dist/chunks/s3-backend-3P33LQHU.ts.map +7 -0
- package/dist/chunks/{setup-switch-X3W3FALZ.ts → setup-switch-IS2PEZ3J.ts} +9 -9
- package/dist/chunks/setup-wizard-VB3VDOV5.ts +89 -0
- package/dist/chunks/setup-wizard-VB3VDOV5.ts.map +7 -0
- package/dist/chunks/{snapshot-WBSV4MQG.ts → snapshot-HKEWCVK6.ts} +7 -6
- package/dist/chunks/sync-inspection-FEP4L5TR.ts +20 -0
- package/dist/chunks/{sync-operations-MPAFVVHA.ts → sync-operations-ONJ27QRL.ts} +381 -299
- package/dist/chunks/sync-operations-ONJ27QRL.ts.map +7 -0
- package/dist/chunks/{sync-state-36H7ULG2.ts → sync-state-Q2FTXTW6.ts} +6 -6
- package/dist/chunks/sync-state-Q2FTXTW6.ts.map +7 -0
- package/dist/chunks/{webdav-backend-ZD5RHRIU.ts → webdav-backend-ECIKD7SO.ts} +26 -13
- package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +7 -0
- package/dist/index.ts +477 -342
- package/dist/index.ts.map +4 -4
- package/docs/settings.md +211 -0
- package/package.json +6 -8
- package/src/{backend-factory.ts → backends/backend-factory.ts} +4 -4
- package/src/backends/backend-identity.ts +40 -0
- package/src/backends/backend-types.ts +81 -0
- package/src/{git-backend.ts → backends/git/git-backend.ts} +43 -19
- package/src/{git-config.ts → backends/git/git-config.ts} +1 -0
- package/src/{git-storage.ts → backends/git/git-storage.ts} +2 -2
- package/src/{s3-backend.ts → backends/s3/s3-backend.ts} +45 -9
- package/src/{s3-client.ts → backends/s3/s3-client.ts} +56 -8
- package/src/{sync-backend.ts → backends/sync-backend.ts} +1 -1
- package/src/{webdav-backend.ts → backends/webdav/webdav-backend.ts} +22 -10
- package/src/{webdav-client.ts → backends/webdav/webdav-client.ts} +1 -1
- package/src/{webdav-config.ts → backends/webdav/webdav-config.ts} +1 -0
- package/src/commands/command-execution.ts +330 -0
- package/src/commands/command-handler.ts +201 -0
- package/src/commands/command-types.ts +18 -0
- package/src/{command.ts → commands/command.ts} +2 -1
- package/src/commands/setup-completions.ts +6 -0
- package/src/settings/config.ts +238 -0
- package/src/{settings-management.ts → settings/settings-management.ts} +40 -14
- package/src/settings/settings-store.ts +109 -0
- package/src/settings/settings-types.ts +128 -0
- package/src/settings/settings-validation.ts +443 -0
- package/src/snapshot/session-paths.ts +84 -0
- package/src/{snapshot-apply.ts → snapshot/snapshot-apply.ts} +3 -3
- package/src/{snapshot-codec.ts → snapshot/snapshot-codec.ts} +2 -2
- package/src/{snapshot-transaction.ts → snapshot/snapshot-transaction.ts} +5 -4
- package/src/snapshot/snapshot-types.ts +38 -0
- package/src/{snapshot.ts → snapshot/snapshot.ts} +39 -20
- package/src/state/json-file.ts +20 -0
- package/src/{lock.ts → state/lock.ts} +3 -2
- package/src/{operation-availability.ts → state/operation-availability.ts} +2 -2
- package/src/state/state-types.ts +20 -0
- package/src/state/sync-state-store.ts +62 -0
- package/src/sync/automatic-sync.ts +103 -0
- package/src/{remote-snapshot.ts → sync/remote-snapshot.ts} +27 -4
- package/src/{setup-switch.ts → sync/setup-switch.ts} +6 -7
- package/src/sync/signals.ts +10 -0
- package/src/sync/startup-check.ts +128 -0
- package/src/{sync-decision.ts → sync/sync-decision.ts} +6 -3
- package/src/sync/sync-error-guidance.ts +53 -0
- package/src/sync/sync-inspection.ts +51 -0
- package/src/sync/sync-loaders.ts +54 -0
- package/src/{sync-operations.ts → sync/sync-mutations.ts} +41 -330
- package/src/sync/sync-operations.ts +12 -0
- package/src/{sync-policy.ts → sync/sync-policy.ts} +2 -2
- package/src/sync/sync-queries.ts +235 -0
- package/src/{sync-state.ts → sync/sync-state.ts} +5 -4
- package/src/sync-extension.ts +64 -708
- package/src/sync.ts +8 -8
- package/src/types.ts +47 -284
- package/src/ui/automatic-sync-summary.ts +6 -0
- package/src/{cancellable-operation.ts → ui/cancellable-operation.ts} +4 -4
- package/src/{file-selection.ts → ui/file-selection.ts} +5 -3
- package/src/{manager-attention.ts → ui/manager-attention.ts} +5 -1
- package/src/{manager-recovery.ts → ui/manager-recovery.ts} +6 -2
- package/src/{manager-state.ts → ui/manager-state.ts} +35 -14
- package/src/ui/manager-ui.ts +386 -0
- package/src/{remote-selection-ui.ts → ui/remote-selection-ui.ts} +21 -13
- package/src/{secret-input.ts → ui/secret-input.ts} +22 -4
- package/src/{settings-ui.ts → ui/settings-ui.ts} +42 -11
- package/src/ui/setup/git-ui.ts +293 -0
- package/src/{s3-credentials-ui.ts → ui/setup/s3-credentials-ui.ts} +13 -21
- package/src/ui/setup/s3-ui.ts +217 -0
- package/src/ui/setup/setup-actions.ts +127 -0
- package/src/ui/setup/setup-location-ui.ts +167 -0
- package/src/ui/setup/setup-name-ui.ts +34 -0
- package/src/ui/setup/setup-prompts.ts +92 -0
- package/src/ui/setup/setup-review.ts +70 -0
- package/src/ui/setup/setup-switcher.ts +181 -0
- package/src/ui/setup/setup-wizard.ts +39 -0
- package/src/ui/setup/text-input.ts +78 -0
- package/src/ui/setup/webdav-ui.ts +330 -0
- package/src/{storage-connections-ui.ts → ui/storage-connections-ui.ts} +99 -69
- package/src/{sync-attention.ts → ui/sync-attention.ts} +75 -6
- package/src/{sync-format.ts → ui/sync-format.ts} +7 -13
- package/src/{sync-resolution-ui.ts → ui/sync-resolution-ui.ts} +4 -3
- package/src/{sync-setups-ui.ts → ui/sync-setups-ui.ts} +23 -10
- package/src/ui/terminal-text.ts +4 -0
- package/dist/chunks/chunk-3NDD5QPM.ts.map +0 -7
- package/dist/chunks/chunk-4F3W4CSC.ts.map +0 -7
- package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
- package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
- package/dist/chunks/chunk-5WJNWFTG.ts.map +0 -7
- package/dist/chunks/chunk-APZVBSM6.ts.map +0 -7
- package/dist/chunks/chunk-BHJUISB4.ts.map +0 -7
- package/dist/chunks/chunk-EWX3TPLQ.ts.map +0 -7
- package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
- package/dist/chunks/chunk-FR6CVXAL.ts.map +0 -7
- package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
- package/dist/chunks/chunk-JNOOSPEW.ts +0 -82
- package/dist/chunks/chunk-JNOOSPEW.ts.map +0 -7
- package/dist/chunks/chunk-LJDSHNS3.ts.map +0 -7
- package/dist/chunks/chunk-LONPFFNP.ts.map +0 -7
- package/dist/chunks/chunk-RGJ6SOYR.ts.map +0 -7
- package/dist/chunks/chunk-W64NEHXT.ts +0 -167
- package/dist/chunks/chunk-W64NEHXT.ts.map +0 -7
- package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
- package/dist/chunks/file-selection-4UABOP44.ts.map +0 -7
- package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
- package/dist/chunks/manager-result-dispatcher-RNRQA3PW.ts +0 -18
- package/dist/chunks/manager-ui-QO2P4V3Z.ts +0 -2757
- package/dist/chunks/manager-ui-QO2P4V3Z.ts.map +0 -7
- package/dist/chunks/remote-selection-ui-LHSAGKGA.ts +0 -17
- package/dist/chunks/s3-backend-3QZGO2EF.ts.map +0 -7
- package/dist/chunks/sync-operations-MPAFVVHA.ts.map +0 -7
- package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
- package/src/config.ts +0 -910
- package/src/git-ui.ts +0 -307
- package/src/manager-helpers.ts +0 -64
- package/src/manager-ui.ts +0 -997
- package/src/webdav-ui.ts +0 -421
- /package/dist/chunks/{manager-result-dispatcher-RNRQA3PW.ts.map → manager-result-dispatcher-U23ODD32.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-LHSAGKGA.ts.map → remote-selection-ui-ZE3TY67S.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-X3W3FALZ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
- /package/dist/chunks/{snapshot-WBSV4MQG.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
- /package/dist/chunks/{sync-state-36H7ULG2.ts.map → sync-inspection-FEP4L5TR.ts.map} +0 -0
- /package/src/{git-runner.ts → backends/git/git-runner.ts} +0 -0
- /package/src/{config-errors.ts → settings/config-errors.ts} +0 -0
- /package/src/{config-file.ts → settings/config-file.ts} +0 -0
- /package/src/{snapshot-paths.ts → snapshot/snapshot-paths.ts} +0 -0
- /package/src/{lock-policy.ts → state/lock-policy.ts} +0 -0
- /package/src/{lockfile-fs.ts → state/lockfile-fs.ts} +0 -0
- /package/src/{state-directory.ts → state/state-directory.ts} +0 -0
- /package/src/{sync-errors.ts → sync/sync-errors.ts} +0 -0
- /package/src/{manager-result-dispatcher.ts → ui/manager-result-dispatcher.ts} +0 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type ActionMenuItem, defineMenu, runMenu } from "@narumitw/pi-tui-kit";
|
|
3
|
+
import { loadConfig } from "../settings/config.js";
|
|
4
|
+
import { operationBlocksChanges, operationCanRecover } from "../state/operation-availability.js";
|
|
5
|
+
import { type RunRoute, runCancellableOperation } from "./cancellable-operation.js";
|
|
6
|
+
import {
|
|
7
|
+
attentionMainMenuItems,
|
|
8
|
+
blockedSyncMenuItem,
|
|
9
|
+
type SyncManagerAttentionOptions,
|
|
10
|
+
showManagerAttention,
|
|
11
|
+
} from "./manager-attention.js";
|
|
12
|
+
import { recoverSyncAccess } from "./manager-recovery.js";
|
|
13
|
+
import { dispatchManagerResult } from "./manager-result-dispatcher.js";
|
|
14
|
+
import { describeManagerState } from "./manager-state.js";
|
|
15
|
+
import { showSyncSettings } from "./settings-ui.js";
|
|
16
|
+
import { showSyncSetupManager } from "./setup/setup-actions.js";
|
|
17
|
+
import { showSetupSwitcher } from "./setup/setup-switcher.js";
|
|
18
|
+
import { showStorageConnections } from "./storage-connections-ui.js";
|
|
19
|
+
import { observationMatchesConfig } from "./sync-attention.js";
|
|
20
|
+
|
|
21
|
+
export async function showSyncManager(
|
|
22
|
+
ctx: ExtensionCommandContext,
|
|
23
|
+
runRoute: RunRoute,
|
|
24
|
+
sessionSignal?: AbortSignal,
|
|
25
|
+
options: SyncManagerAttentionOptions = {},
|
|
26
|
+
): Promise<void> {
|
|
27
|
+
if (!ctx.hasUI) {
|
|
28
|
+
await runRoute("help");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
type Screen = "main" | "more" | "recovery";
|
|
32
|
+
type Action =
|
|
33
|
+
| "review-attention"
|
|
34
|
+
| "sync"
|
|
35
|
+
| "switch"
|
|
36
|
+
| "diff"
|
|
37
|
+
| "settings"
|
|
38
|
+
| "pull"
|
|
39
|
+
| "push"
|
|
40
|
+
| "setups"
|
|
41
|
+
| "connections"
|
|
42
|
+
| "history"
|
|
43
|
+
| "doctor"
|
|
44
|
+
| "unlock"
|
|
45
|
+
| "recover"
|
|
46
|
+
| "refresh"
|
|
47
|
+
| "help"
|
|
48
|
+
| "init"
|
|
49
|
+
| "back";
|
|
50
|
+
interface State {
|
|
51
|
+
manager: Awaited<ReturnType<typeof describeManagerState>>;
|
|
52
|
+
}
|
|
53
|
+
const menu = defineMenu<State, Screen, Action, ExtensionCommandContext>({
|
|
54
|
+
start: "main",
|
|
55
|
+
screens: {
|
|
56
|
+
main: ({ state }) => {
|
|
57
|
+
const attentionItems = attentionMainMenuItems(state.manager);
|
|
58
|
+
const managerItems = state.manager.actions.map(
|
|
59
|
+
(label) => blockedSyncMenuItem(label, state.manager) ?? syncMainMenuItem(label),
|
|
60
|
+
);
|
|
61
|
+
const operationFirst =
|
|
62
|
+
state.manager.operation !== undefined && state.manager.operation.kind !== "free";
|
|
63
|
+
return {
|
|
64
|
+
kind: "actions",
|
|
65
|
+
title: "Manage sync",
|
|
66
|
+
lines: state.manager.title.split("\n").slice(1),
|
|
67
|
+
items: operationFirst
|
|
68
|
+
? [...managerItems, ...attentionItems]
|
|
69
|
+
: [...attentionItems, ...managerItems],
|
|
70
|
+
hint: "close",
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
more: () => ({
|
|
74
|
+
kind: "actions",
|
|
75
|
+
title: "More options",
|
|
76
|
+
items: [
|
|
77
|
+
{ id: "pull", label: "Pull from remote…", action: "pull" },
|
|
78
|
+
{ id: "push", label: "Push to remote…", action: "push" },
|
|
79
|
+
{ id: "setups", label: "Sync setups…", action: "setups" },
|
|
80
|
+
{
|
|
81
|
+
id: "connections",
|
|
82
|
+
label: "Storage connections…",
|
|
83
|
+
action: "connections",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: "doctor",
|
|
87
|
+
label: "Check setup",
|
|
88
|
+
description:
|
|
89
|
+
"Check current setup access. WebDAV also probes writes and repairs history.",
|
|
90
|
+
action: "doctor",
|
|
91
|
+
},
|
|
92
|
+
{ id: "recovery", label: "History & recovery…", to: "recovery" },
|
|
93
|
+
{ id: "help", label: "Help", action: "help" },
|
|
94
|
+
{ id: "back", label: "Back", action: "back" },
|
|
95
|
+
],
|
|
96
|
+
hint: "back",
|
|
97
|
+
}),
|
|
98
|
+
recovery: ({ state }) => ({
|
|
99
|
+
kind: "actions",
|
|
100
|
+
title: "History & recovery",
|
|
101
|
+
items: [
|
|
102
|
+
{ id: "history", label: "Browse history", action: "history" },
|
|
103
|
+
{
|
|
104
|
+
id: "doctor",
|
|
105
|
+
label: "Check setup",
|
|
106
|
+
description: "WebDAV also probes writes and repairs history.",
|
|
107
|
+
action: "doctor",
|
|
108
|
+
},
|
|
109
|
+
...(state.manager.operation && operationCanRecover(state.manager.operation)
|
|
110
|
+
? [{ id: "unlock", label: "Recover stale operation", action: "unlock" as const }]
|
|
111
|
+
: []),
|
|
112
|
+
{ id: "back", label: "Back", action: "back" },
|
|
113
|
+
],
|
|
114
|
+
hint: "back",
|
|
115
|
+
}),
|
|
116
|
+
},
|
|
117
|
+
actions: {
|
|
118
|
+
"review-attention": async () => {
|
|
119
|
+
const attention = options.getAttention?.();
|
|
120
|
+
if (!attention) {
|
|
121
|
+
const observation = options.getObservation?.();
|
|
122
|
+
if (!observation) return { kind: "stay" };
|
|
123
|
+
// A head-only observation is not an authoritative decision. Load a fresh
|
|
124
|
+
// snapshot only after the user activates the existing review flow.
|
|
125
|
+
const { showRemoteSelectionReview } = await import("./remote-selection-ui.js");
|
|
126
|
+
if (sessionSignal?.aborted) return { kind: "close" };
|
|
127
|
+
const config = await loadConfig();
|
|
128
|
+
if (sessionSignal?.aborted) return { kind: "close" };
|
|
129
|
+
if (
|
|
130
|
+
options.getObservation?.() !== observation ||
|
|
131
|
+
!observationMatchesConfig(observation, config)
|
|
132
|
+
) {
|
|
133
|
+
options.onObservationInvalidated?.();
|
|
134
|
+
return { kind: "stay" };
|
|
135
|
+
}
|
|
136
|
+
// Keep the check-time hint on cancellation or failure. Route commits and
|
|
137
|
+
// the manager's config/baseline reconciliation own invalidation.
|
|
138
|
+
const result = await showRemoteSelectionReview(
|
|
139
|
+
ctx,
|
|
140
|
+
observation.setupName,
|
|
141
|
+
sessionSignal,
|
|
142
|
+
undefined,
|
|
143
|
+
{
|
|
144
|
+
origin: "sync",
|
|
145
|
+
runRoute,
|
|
146
|
+
onSelectionResolved: () => {
|
|
147
|
+
if (!sessionSignal?.aborted && options.getObservation?.() === observation)
|
|
148
|
+
options.onObservationInvalidated?.();
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
if (sessionSignal?.aborted) return { kind: "close" };
|
|
153
|
+
if (result.kind === "route-result") {
|
|
154
|
+
return dispatchManagerResult(ctx, result.result, result.route, runRoute, sessionSignal);
|
|
155
|
+
}
|
|
156
|
+
return { kind: result.kind === "closed" || result.kind === "stale" ? "close" : "stay" };
|
|
157
|
+
}
|
|
158
|
+
const disposition = await showManagerAttention(
|
|
159
|
+
ctx,
|
|
160
|
+
attention,
|
|
161
|
+
runRoute,
|
|
162
|
+
sessionSignal,
|
|
163
|
+
() => options.onSelectionResolved?.(attention),
|
|
164
|
+
);
|
|
165
|
+
return { kind: disposition };
|
|
166
|
+
},
|
|
167
|
+
sync: async () => {
|
|
168
|
+
const pendingAttention = options.getAttention?.();
|
|
169
|
+
if (pendingAttention) {
|
|
170
|
+
const activeConfig = await loadConfig();
|
|
171
|
+
if (sessionSignal?.aborted) return { kind: "close" };
|
|
172
|
+
if (pendingAttention.decision.setupName === activeConfig.setupName) {
|
|
173
|
+
ctx.ui.notify("Review synced content before starting Sync now.", "warning");
|
|
174
|
+
return { kind: "stay" };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const result = await runCancellableOperation(
|
|
178
|
+
ctx,
|
|
179
|
+
"Checking current sync setup…",
|
|
180
|
+
"sync",
|
|
181
|
+
runRoute,
|
|
182
|
+
{
|
|
183
|
+
commitAware: true,
|
|
184
|
+
signal: sessionSignal,
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
const disposition = await dispatchManagerResult(
|
|
188
|
+
ctx,
|
|
189
|
+
result,
|
|
190
|
+
"sync",
|
|
191
|
+
runRoute,
|
|
192
|
+
sessionSignal,
|
|
193
|
+
);
|
|
194
|
+
return disposition.kind === "close" ? { kind: "close" } : { kind: "stay" };
|
|
195
|
+
},
|
|
196
|
+
switch: async () => {
|
|
197
|
+
const result = await showSetupSwitcher(ctx, runRoute, undefined, sessionSignal);
|
|
198
|
+
return result === "pull-attempted" || result === "closed"
|
|
199
|
+
? { kind: "close" }
|
|
200
|
+
: { kind: "stay" };
|
|
201
|
+
},
|
|
202
|
+
diff: async () => {
|
|
203
|
+
const result = await runCancellableOperation(
|
|
204
|
+
ctx,
|
|
205
|
+
"Checking current sync setup…",
|
|
206
|
+
"diff",
|
|
207
|
+
runRoute,
|
|
208
|
+
{ signal: sessionSignal },
|
|
209
|
+
);
|
|
210
|
+
return result.kind === "closed" ? { kind: "close" } : { kind: "stay" };
|
|
211
|
+
},
|
|
212
|
+
settings: async () => {
|
|
213
|
+
await showSyncSettings(ctx, runRoute, sessionSignal);
|
|
214
|
+
return { kind: "stay" };
|
|
215
|
+
},
|
|
216
|
+
pull: async () => {
|
|
217
|
+
const result = await runCancellableOperation(
|
|
218
|
+
ctx,
|
|
219
|
+
"Checking remote changes…",
|
|
220
|
+
"pull",
|
|
221
|
+
runRoute,
|
|
222
|
+
{
|
|
223
|
+
commitAware: true,
|
|
224
|
+
cancelledMessage: "Pull check cancelled; no local files were changed.",
|
|
225
|
+
signal: sessionSignal,
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
const disposition = await dispatchManagerResult(
|
|
229
|
+
ctx,
|
|
230
|
+
result,
|
|
231
|
+
"pull",
|
|
232
|
+
runRoute,
|
|
233
|
+
sessionSignal,
|
|
234
|
+
);
|
|
235
|
+
return disposition.kind === "close" ? { kind: "close" } : { kind: "stay" };
|
|
236
|
+
},
|
|
237
|
+
push: async () => {
|
|
238
|
+
const result = await runCancellableOperation(
|
|
239
|
+
ctx,
|
|
240
|
+
"Preparing push preview…",
|
|
241
|
+
"push",
|
|
242
|
+
runRoute,
|
|
243
|
+
{
|
|
244
|
+
commitAware: true,
|
|
245
|
+
cancelledMessage: "Push preparation cancelled; no remote files were changed.",
|
|
246
|
+
signal: sessionSignal,
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
const disposition = await dispatchManagerResult(
|
|
250
|
+
ctx,
|
|
251
|
+
result,
|
|
252
|
+
"push",
|
|
253
|
+
runRoute,
|
|
254
|
+
sessionSignal,
|
|
255
|
+
);
|
|
256
|
+
return disposition.kind === "close" ? { kind: "close" } : { kind: "stay" };
|
|
257
|
+
},
|
|
258
|
+
setups: async () => {
|
|
259
|
+
const result = await showSyncSetupManager(ctx, runRoute, sessionSignal);
|
|
260
|
+
return result === "exit" ? { kind: "close" } : { kind: "stay" };
|
|
261
|
+
},
|
|
262
|
+
connections: async () => {
|
|
263
|
+
await showStorageConnections(ctx, sessionSignal);
|
|
264
|
+
return { kind: "stay" };
|
|
265
|
+
},
|
|
266
|
+
history: async () => {
|
|
267
|
+
await runRoute("history");
|
|
268
|
+
return { kind: "stay" };
|
|
269
|
+
},
|
|
270
|
+
doctor: async () => {
|
|
271
|
+
const result = await runCancellableOperation(
|
|
272
|
+
ctx,
|
|
273
|
+
"Checking setup access…",
|
|
274
|
+
"doctor",
|
|
275
|
+
runRoute,
|
|
276
|
+
{ signal: sessionSignal },
|
|
277
|
+
);
|
|
278
|
+
return { kind: result.kind === "closed" ? "close" : "stay" };
|
|
279
|
+
},
|
|
280
|
+
unlock: async ({ state, signal: actionSignal }) => {
|
|
281
|
+
const result = await recoverSyncAccess(
|
|
282
|
+
ctx,
|
|
283
|
+
state.manager,
|
|
284
|
+
runRoute,
|
|
285
|
+
sessionSignal,
|
|
286
|
+
actionSignal,
|
|
287
|
+
);
|
|
288
|
+
if (result === "close") return { kind: "close" };
|
|
289
|
+
return result === "restored" ? { kind: "to", screen: "main" } : { kind: "stay" };
|
|
290
|
+
},
|
|
291
|
+
recover: async ({ state, signal: actionSignal }) => {
|
|
292
|
+
const result = await recoverSyncAccess(
|
|
293
|
+
ctx,
|
|
294
|
+
state.manager,
|
|
295
|
+
runRoute,
|
|
296
|
+
sessionSignal,
|
|
297
|
+
actionSignal,
|
|
298
|
+
);
|
|
299
|
+
return { kind: result === "close" ? "close" : "stay" };
|
|
300
|
+
},
|
|
301
|
+
refresh: async () => ({ kind: "stay" }),
|
|
302
|
+
help: async () => {
|
|
303
|
+
await runRoute("help");
|
|
304
|
+
return { kind: "close" };
|
|
305
|
+
},
|
|
306
|
+
init: async () => {
|
|
307
|
+
await runRoute("init");
|
|
308
|
+
return { kind: "stay" };
|
|
309
|
+
},
|
|
310
|
+
back: async () => ({ kind: "back" }),
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
await runMenu(ctx, menu, {
|
|
314
|
+
getState: async () => {
|
|
315
|
+
const pendingAttention = options.getAttention?.();
|
|
316
|
+
const observation = options.getObservation?.();
|
|
317
|
+
const manager = await describeManagerState(
|
|
318
|
+
sessionSignal,
|
|
319
|
+
pendingAttention,
|
|
320
|
+
undefined,
|
|
321
|
+
observation,
|
|
322
|
+
);
|
|
323
|
+
if (sessionSignal?.aborted) return { manager };
|
|
324
|
+
// A busy operation suppresses baseline reads, not the stored check-time hint.
|
|
325
|
+
if (
|
|
326
|
+
observation &&
|
|
327
|
+
options.getObservation?.() === observation &&
|
|
328
|
+
!manager.observation &&
|
|
329
|
+
!(manager.operation && operationBlocksChanges(manager.operation))
|
|
330
|
+
) {
|
|
331
|
+
options.onObservationInvalidated?.();
|
|
332
|
+
}
|
|
333
|
+
if (pendingAttention && options.getAttention?.() === pendingAttention && !manager.attention) {
|
|
334
|
+
options.onSelectionResolved?.(pendingAttention);
|
|
335
|
+
}
|
|
336
|
+
return { manager };
|
|
337
|
+
},
|
|
338
|
+
signal: sessionSignal,
|
|
339
|
+
isCurrent: () => !sessionSignal?.aborted,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function syncMainMenuItem(
|
|
344
|
+
label: string,
|
|
345
|
+
): ActionMenuItem<
|
|
346
|
+
"main" | "more" | "recovery",
|
|
347
|
+
| "sync"
|
|
348
|
+
| "switch"
|
|
349
|
+
| "diff"
|
|
350
|
+
| "settings"
|
|
351
|
+
| "setups"
|
|
352
|
+
| "connections"
|
|
353
|
+
| "recover"
|
|
354
|
+
| "refresh"
|
|
355
|
+
| "help"
|
|
356
|
+
| "init"
|
|
357
|
+
> {
|
|
358
|
+
if (label === "More…") return { id: "more", label, to: "more" };
|
|
359
|
+
if (label === "History & recovery…") return { id: "recovery", label, to: "recovery" };
|
|
360
|
+
const actions = new Map<
|
|
361
|
+
string,
|
|
362
|
+
| "sync"
|
|
363
|
+
| "switch"
|
|
364
|
+
| "diff"
|
|
365
|
+
| "settings"
|
|
366
|
+
| "setups"
|
|
367
|
+
| "connections"
|
|
368
|
+
| "recover"
|
|
369
|
+
| "refresh"
|
|
370
|
+
| "help"
|
|
371
|
+
| "init"
|
|
372
|
+
>([
|
|
373
|
+
["Sync now (recommended)", "sync"],
|
|
374
|
+
["Switch sync setup", "switch"],
|
|
375
|
+
["Status & changes", "diff"],
|
|
376
|
+
["Settings", "settings"],
|
|
377
|
+
["Restore sync access… (recommended)", "recover"],
|
|
378
|
+
["Refresh operation status", "refresh"],
|
|
379
|
+
["Sync setups…", "setups"],
|
|
380
|
+
["Storage connections…", "connections"],
|
|
381
|
+
["Help", "help"],
|
|
382
|
+
["Set up sync", "init"],
|
|
383
|
+
["Use existing settings", "init"],
|
|
384
|
+
]);
|
|
385
|
+
return { id: actions.get(label) ?? "help", label, action: actions.get(label) ?? "help" };
|
|
386
|
+
}
|
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { defineMenu, runMenu } from "@narumitw/pi-tui-kit";
|
|
3
|
-
import { createSyncBackend, type SyncBackendFactory } from "
|
|
4
|
-
import {
|
|
5
|
-
type RunRoute,
|
|
6
|
-
type RunRouteResult,
|
|
7
|
-
runCancellableOperation,
|
|
8
|
-
} from "./cancellable-operation.js";
|
|
3
|
+
import { createSyncBackend, type SyncBackendFactory } from "../backends/backend-factory.js";
|
|
4
|
+
import type { RemoteHead, SyncBackend } from "../backends/sync-backend.js";
|
|
9
5
|
import {
|
|
10
6
|
loadConfig,
|
|
11
7
|
loadPartialConfig,
|
|
12
8
|
type SyncSetupStorageReview,
|
|
13
9
|
syncConfigReviewFingerprint,
|
|
14
|
-
} from "
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
18
|
-
import { errorMessage
|
|
10
|
+
} from "../settings/config.js";
|
|
11
|
+
import { SyncSetupReviewChangedError, updateSyncSetup } from "../settings/settings-management.js";
|
|
12
|
+
import type { AnySyncConfig, PartialConfig } from "../settings/settings-types.js";
|
|
13
|
+
import { readSnapshotForHead } from "../sync/remote-snapshot.js";
|
|
14
|
+
import { errorMessage } from "../sync/sync-errors.js";
|
|
19
15
|
import {
|
|
20
16
|
compareSyncInclude,
|
|
21
17
|
inspectRemoteSelection,
|
|
22
18
|
type RemoteSelectionDecision,
|
|
23
19
|
type RemoteSelectionState,
|
|
24
20
|
sameSyncInclude,
|
|
25
|
-
} from "
|
|
26
|
-
import
|
|
21
|
+
} from "../sync/sync-policy.js";
|
|
22
|
+
import {
|
|
23
|
+
type RunRoute,
|
|
24
|
+
type RunRouteResult,
|
|
25
|
+
runCancellableOperation,
|
|
26
|
+
} from "./cancellable-operation.js";
|
|
27
|
+
import { safeTerminalText } from "./terminal-text.js";
|
|
27
28
|
|
|
28
29
|
const STATUS_KEY = "sync";
|
|
29
30
|
|
|
@@ -34,6 +35,7 @@ export interface RemoteSelectionReviewOptions {
|
|
|
34
35
|
origin?: RemoteSelectionOrigin;
|
|
35
36
|
runRoute?: RunRoute;
|
|
36
37
|
cancelLabel?: string;
|
|
38
|
+
/** The explicit mismatch is resolved or superseded; this does not imply file equality. */
|
|
37
39
|
onSelectionResolved?: () => void;
|
|
38
40
|
withStateAccess?: <T>(task: () => Promise<T>) => Promise<T>;
|
|
39
41
|
}
|
|
@@ -57,6 +59,11 @@ export async function showRemoteSelectionReview(
|
|
|
57
59
|
if (!decision) {
|
|
58
60
|
const inspected = await inspectConfiguredRemoteSelection(ctx, setupName, signal, factory);
|
|
59
61
|
if (!inspected || signal?.aborted) return { kind: "stale" };
|
|
62
|
+
// A successful fresh read can supersede an old explicit mismatch without
|
|
63
|
+
// changing local settings. This is not user cancellation or proof of file equality.
|
|
64
|
+
if (inspected.kind === "empty" || inspected.state.kind !== "different") {
|
|
65
|
+
options.onSelectionResolved?.();
|
|
66
|
+
}
|
|
60
67
|
if (inspected.kind === "empty") {
|
|
61
68
|
ctx.ui.notify("Remote storage has no snapshot or synced-content list yet.", "info");
|
|
62
69
|
return { kind: "back" };
|
|
@@ -99,6 +106,7 @@ export async function showRemoteSelectionReview(
|
|
|
99
106
|
);
|
|
100
107
|
if (!refreshed || signal?.aborted) return { kind: "stale" };
|
|
101
108
|
if (refreshed.kind === "empty") {
|
|
109
|
+
options.onSelectionResolved?.();
|
|
102
110
|
ctx.ui.notify("Remote storage no longer has a snapshot or synced-content list.", "warning");
|
|
103
111
|
return { kind: "back" };
|
|
104
112
|
}
|
|
@@ -29,7 +29,9 @@ export async function promptSecret(
|
|
|
29
29
|
const cancelKey = keybindingText(keybindings, "tui.select.cancel", "esc", ["ctrl+c"]);
|
|
30
30
|
const applyTheme = () => {
|
|
31
31
|
heading.setText(theme.fg("accent", theme.bold(title)));
|
|
32
|
-
hint.setText(
|
|
32
|
+
hint.setText(
|
|
33
|
+
theme.fg("dim", `${submitKey} continue • ${cancelKey} cancel • Input is hidden`),
|
|
34
|
+
);
|
|
33
35
|
};
|
|
34
36
|
applyTheme();
|
|
35
37
|
const input = new MaskedInput(keybindings);
|
|
@@ -60,10 +62,22 @@ export async function promptSecret(
|
|
|
60
62
|
hint.invalidate();
|
|
61
63
|
},
|
|
62
64
|
handleInput(data: string) {
|
|
63
|
-
if (matchesKey(data, Key.ctrl("c"))
|
|
65
|
+
if (matchesKey(data, Key.ctrl("c"))) {
|
|
64
66
|
complete(undefined);
|
|
65
|
-
} else if (
|
|
66
|
-
|
|
67
|
+
} else if (input.isPasting || data.includes("\u001b[200~")) {
|
|
68
|
+
input.handleInput(data);
|
|
69
|
+
} else if (keybindings.matches(data, "tui.select.cancel")) {
|
|
70
|
+
complete(undefined);
|
|
71
|
+
} else if (keybindings.matches(data, "tui.input.submit")) {
|
|
72
|
+
if (options.required !== false && input.getValue().length === 0) {
|
|
73
|
+
ctx.ui.notify(`${title} is required. Enter a value, or cancel.`, "warning");
|
|
74
|
+
} else if (hasControlCharacter(input.getValue())) {
|
|
75
|
+
ctx.ui.notify(
|
|
76
|
+
`${title} contains control characters. Remove them or re-enter the value, then continue.`,
|
|
77
|
+
"warning",
|
|
78
|
+
);
|
|
79
|
+
} else complete(input.getValue());
|
|
80
|
+
} else input.handleInput(data);
|
|
67
81
|
tui.requestRender();
|
|
68
82
|
},
|
|
69
83
|
dispose() {
|
|
@@ -92,6 +106,10 @@ class MaskedInput implements Focusable {
|
|
|
92
106
|
|
|
93
107
|
constructor(private readonly keybindings: KeybindingsManager) {}
|
|
94
108
|
|
|
109
|
+
get isPasting() {
|
|
110
|
+
return this.pasting;
|
|
111
|
+
}
|
|
112
|
+
|
|
95
113
|
getValue() {
|
|
96
114
|
return this.value.join("");
|
|
97
115
|
}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { defineMenu, runMenu } from "@narumitw/pi-tui-kit";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { loadConfig } from "../settings/config.js";
|
|
4
|
+
import { localConfigPath } from "../settings/config-file.js";
|
|
5
|
+
import { updateSyncSetup } from "../settings/settings-management.js";
|
|
6
|
+
import { updateLocalConfig } from "../settings/settings-store.js";
|
|
7
7
|
import {
|
|
8
8
|
SETUP_SWITCH_ACTION_OPTIONS,
|
|
9
9
|
saveOnSwitch,
|
|
10
10
|
setupSwitchActionFromLabel,
|
|
11
11
|
setupSwitchActionLabel,
|
|
12
|
-
} from "
|
|
13
|
-
import {
|
|
12
|
+
} from "../sync/setup-switch.js";
|
|
13
|
+
import type { RunRoute } from "./cancellable-operation.js";
|
|
14
|
+
import { dispatchManagerResult } from "./manager-result-dispatcher.js";
|
|
15
|
+
import { AUTOMATIC_SYNC_DESCRIPTION } from "./setup/setup-prompts.js";
|
|
16
|
+
import { safeTerminalText } from "./terminal-text.js";
|
|
14
17
|
|
|
15
18
|
export type SyncSettingsRoute = RunRoute;
|
|
16
19
|
|
|
@@ -26,7 +29,7 @@ export async function showSyncSettings(
|
|
|
26
29
|
const initial = await loadConfig();
|
|
27
30
|
if (signal?.aborted) return;
|
|
28
31
|
const setupName = initial.setupName;
|
|
29
|
-
type Action = "automatic" | "on-switch" | "include" | "remote-include";
|
|
32
|
+
type Action = "automatic" | "skip-secret-scan" | "on-switch" | "include" | "remote-include";
|
|
30
33
|
const menu = defineMenu<
|
|
31
34
|
Awaited<ReturnType<typeof loadConfig>>,
|
|
32
35
|
"settings",
|
|
@@ -45,16 +48,25 @@ export async function showSyncSettings(
|
|
|
45
48
|
{
|
|
46
49
|
id: "automatic",
|
|
47
50
|
label: "Automatic sync",
|
|
48
|
-
description:
|
|
51
|
+
description: AUTOMATIC_SYNC_DESCRIPTION,
|
|
49
52
|
currentValue: state.automatic ? "On" : "Off",
|
|
50
53
|
values: ["On", "Off"],
|
|
51
54
|
action: "automatic",
|
|
52
55
|
},
|
|
56
|
+
{
|
|
57
|
+
id: "skipSecretScan",
|
|
58
|
+
label: "Skip secret scan (all setups)",
|
|
59
|
+
description:
|
|
60
|
+
"All setups: allow pushes without checking managed local files for possible secrets.",
|
|
61
|
+
currentValue: state.skipSecretScan ? "On" : "Off",
|
|
62
|
+
values: ["On", "Off"],
|
|
63
|
+
action: "skip-secret-scan",
|
|
64
|
+
},
|
|
53
65
|
{
|
|
54
66
|
id: "onSwitch",
|
|
55
|
-
label: "After switching setup",
|
|
67
|
+
label: "After switching setup (all setups)",
|
|
56
68
|
description:
|
|
57
|
-
"
|
|
69
|
+
"All setups: ask before a pull review, open it automatically, or switch without pulling.",
|
|
58
70
|
currentValue: setupSwitchActionLabel(state.onSwitch),
|
|
59
71
|
values: SETUP_SWITCH_ACTION_OPTIONS.map(({ label }) => label),
|
|
60
72
|
action: "on-switch",
|
|
@@ -62,7 +74,7 @@ export async function showSyncSettings(
|
|
|
62
74
|
{
|
|
63
75
|
id: "include",
|
|
64
76
|
label: "Included content",
|
|
65
|
-
description: `${state.include.length} selected path${state.include.length === 1 ? "" : "s"}.
|
|
77
|
+
description: `${state.include.length} selected path${state.include.length === 1 ? "" : "s"}. Choose which paths this setup syncs.`,
|
|
66
78
|
currentValue: "Open editor",
|
|
67
79
|
action: "include",
|
|
68
80
|
},
|
|
@@ -100,6 +112,25 @@ export async function showSyncSettings(
|
|
|
100
112
|
return { kind: "rejected" };
|
|
101
113
|
}
|
|
102
114
|
},
|
|
115
|
+
"skip-secret-scan": async ({ value, signal: actionSignal }) => {
|
|
116
|
+
const skipSecretScan = value === "On";
|
|
117
|
+
const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
|
|
118
|
+
try {
|
|
119
|
+
const latest = await loadConfig(setupName);
|
|
120
|
+
if (mutationSignal.aborted) return { kind: "rejected" };
|
|
121
|
+
if (latest.skipSecretScan === skipSecretScan) return { kind: "stay" };
|
|
122
|
+
await updateLocalConfig((settings) => ({ ...settings, skipSecretScan }), mutationSignal);
|
|
123
|
+
if (mutationSignal.aborted) return { kind: "rejected" };
|
|
124
|
+
ctx.ui.notify(
|
|
125
|
+
`Secret scan ${skipSecretScan ? "disabled" : "enabled"} for pushes in all setups.`,
|
|
126
|
+
"info",
|
|
127
|
+
);
|
|
128
|
+
return { kind: "stay" };
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
|
|
131
|
+
return { kind: "rejected" };
|
|
132
|
+
}
|
|
133
|
+
},
|
|
103
134
|
"on-switch": async ({ value, signal: actionSignal }) => {
|
|
104
135
|
const action = value ? setupSwitchActionFromLabel(value) : undefined;
|
|
105
136
|
if (!action) return { kind: "rejected" };
|