@narumitw/pi-sync 0.49.15 → 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 +49 -16
- 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-JF6MZCT4.ts → chunk-AUSDLD6F.ts} +4 -4
- package/dist/chunks/chunk-AUSDLD6F.ts.map +7 -0
- package/dist/chunks/{chunk-X7SQ2EQM.ts → chunk-AXXS2BPA.ts} +9 -9
- package/dist/chunks/chunk-AXXS2BPA.ts.map +7 -0
- package/dist/chunks/{chunk-5T442OP3.ts → chunk-C7XY5NF5.ts} +210 -373
- 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-WSOBYHIZ.ts → chunk-GWMHAQG4.ts} +4 -4
- package/dist/chunks/chunk-GWMHAQG4.ts.map +7 -0
- package/dist/chunks/{chunk-5LZALPCD.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-DFXBKHGN.ts → chunk-LCRI3ABW.ts} +21 -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-HVUSHMVL.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-GLQLN5GV.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-44LPGQTI.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-ARN2672F.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-RKEHALNQ.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-552LXHE4.ts → s3-backend-3P33LQHU.ts} +96 -18
- package/dist/chunks/s3-backend-3P33LQHU.ts.map +7 -0
- package/dist/chunks/{setup-switch-5YDG5YGJ.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-2RYBUDRA.ts → snapshot-HKEWCVK6.ts} +7 -6
- package/dist/chunks/sync-inspection-FEP4L5TR.ts +20 -0
- package/dist/chunks/{sync-operations-3LLIHWZO.ts → sync-operations-ONJ27QRL.ts} +374 -294
- package/dist/chunks/sync-operations-ONJ27QRL.ts.map +7 -0
- package/dist/chunks/{sync-state-SRN6YRBK.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 +30 -6
- package/package.json +1 -1
- 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} +39 -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} +34 -325
- 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 -286
- 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} +17 -13
- 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-44LPGQTI.ts.map +0 -7
- package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
- package/dist/chunks/chunk-5LZALPCD.ts.map +0 -7
- package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
- package/dist/chunks/chunk-5T442OP3.ts.map +0 -7
- package/dist/chunks/chunk-ARN2672F.ts.map +0 -7
- package/dist/chunks/chunk-C7LXZ3UH.ts +0 -167
- package/dist/chunks/chunk-C7LXZ3UH.ts.map +0 -7
- package/dist/chunks/chunk-DFXBKHGN.ts.map +0 -7
- package/dist/chunks/chunk-E4COSV32.ts +0 -82
- package/dist/chunks/chunk-E4COSV32.ts.map +0 -7
- package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
- package/dist/chunks/chunk-GLQLN5GV.ts.map +0 -7
- package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
- package/dist/chunks/chunk-HVUSHMVL.ts.map +0 -7
- package/dist/chunks/chunk-JF6MZCT4.ts.map +0 -7
- package/dist/chunks/chunk-WSOBYHIZ.ts.map +0 -7
- package/dist/chunks/chunk-X7SQ2EQM.ts.map +0 -7
- package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
- package/dist/chunks/file-selection-RKEHALNQ.ts.map +0 -7
- package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
- package/dist/chunks/manager-result-dispatcher-LWPYJAD5.ts +0 -18
- package/dist/chunks/manager-ui-NF4RUHBY.ts +0 -2785
- package/dist/chunks/manager-ui-NF4RUHBY.ts.map +0 -7
- package/dist/chunks/remote-selection-ui-NUF5P4ZJ.ts +0 -17
- package/dist/chunks/s3-backend-552LXHE4.ts.map +0 -7
- package/dist/chunks/sync-operations-3LLIHWZO.ts.map +0 -7
- package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
- package/src/config.ts +0 -923
- 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-LWPYJAD5.ts.map → manager-result-dispatcher-U23ODD32.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-NUF5P4ZJ.ts.map → remote-selection-ui-ZE3TY67S.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-5YDG5YGJ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
- /package/dist/chunks/{snapshot-2RYBUDRA.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
- /package/dist/chunks/{sync-state-SRN6YRBK.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
package/dist/index.ts.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/sync-extension.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n} from \"@earendil-works/pi-coding-agent\";\nimport type { RunRouteResult } from \"./cancellable-operation.js\";\nimport {\n\tcompleteSyncArguments,\n\tparseOptions,\n\tresolveSyncCommand,\n\tsetSyncSetupCompletions,\n\tsplitArgs,\n\tusage,\n\tvalidateCommandOptions,\n} from \"./command.js\";\nimport {\n\tactiveLocalConfigPath,\n\tconfiguredSyncSetupNames,\n\tconsumeLocalConfigMigrationNotice,\n\tcreateLocalConfigDocument,\n\tensureStateDir,\n\tisMissingConfigError,\n\tloadConfig,\n\tloadPartialConfig,\n\tlocalConfigPath,\n\tlocalConfigTemplate,\n\treadLocalConfigObject,\n\treadStateForConfig,\n\tsessionTokenWarnings,\n\tsyncSessionsWarnings,\n} from \"./config.js\";\nimport { unlock, withLock } from \"./lock.js\";\nimport { recoverSnapshotTransactionsOnStartup } from \"./snapshot-transaction.js\";\nimport {\n\tmigrateLegacyStateDirectory,\n\tstateDirectoryMigrationNotice,\n\twithStateDirectoryAccess,\n} from \"./state-directory.js\";\nimport {\n\tcreateSyncAttentionController,\n\ttype SyncAttentionController,\n\ttype SyncAttentionOrigin,\n\tsyncAttentionMatchesConfig,\n} from \"./sync-attention.js\";\nimport {\n\terrorMessage,\n\tisSyncDecisionRequiredError,\n\tSetupPullRequiresUiError,\n} from \"./sync-errors.js\";\nimport {\n\tformatRemoteSelectionMismatch,\n\ttype RemoteSelectionDecision,\n\tRemoteSelectionMismatchError,\n} from \"./sync-policy.js\";\nimport type { AnySyncConfig, CommandOptions, SnapshotOptions } from \"./types.js\";\n\nconst STATUS_KEY = \"sync\";\n\ntype SetupSwitchModule = Pick<typeof import(\"./setup-switch.js\"), \"useSyncSetup\">;\ntype SnapshotModule = Pick<typeof import(\"./snapshot.js\"), \"createSnapshot\">;\ntype SyncStateModule = Pick<typeof import(\"./sync-state.js\"), \"hasLocalChanges\">;\ntype SyncOperations = typeof import(\"./sync-operations.js\");\n\nexport interface SyncDependencies {\n\tloadSetupSwitch(): Promise<SetupSwitchModule>;\n\tloadSnapshot(): Promise<SnapshotModule>;\n\tloadSyncState(): Promise<SyncStateModule>;\n\tloadSyncOperations(): Promise<SyncOperations>;\n}\n\ninterface SyncLoaders {\n\tsetupSwitch(): Promise<SetupSwitchModule>;\n\tsnapshot(): Promise<SnapshotModule>;\n\tsyncState(): Promise<SyncStateModule>;\n\toperations(): Promise<SyncOperations>;\n}\n\nconst AUTO_SYNC_OPTIONS: CommandOptions = {\n\tyes: true,\n\tforce: false,\n\tstale: false,\n\tsilent: true,\n\treload: false,\n\tauto: true,\n\targs: [],\n};\nexport default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDependencies> = {}) {\n\tconst loaders: SyncLoaders = {\n\t\tsetupSwitch: cachedModuleLoader(\n\t\t\tdependencies.loadSetupSwitch ?? (() => import(\"./setup-switch.js\")),\n\t\t),\n\t\tsnapshot: cachedModuleLoader(dependencies.loadSnapshot ?? (() => import(\"./snapshot.js\"))),\n\t\tsyncState: cachedModuleLoader(dependencies.loadSyncState ?? (() => import(\"./sync-state.js\"))),\n\t\toperations: cachedModuleLoader(\n\t\t\tdependencies.loadSyncOperations ?? (() => import(\"./sync-operations.js\")),\n\t\t),\n\t};\n\tconst attention = createSyncAttentionController();\n\tlet sessionAbort = new AbortController();\n\tlet shutdownAbort: AbortController | undefined;\n\n\tpi.registerCommand(\"sync\", {\n\t\tdescription: \"Sync Pi settings through Git, WebDAV, R2, or S3-compatible storage\",\n\t\tgetArgumentCompletions: completeSyncArguments,\n\t\thandler: async (args, ctx) => {\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"/sync requires TUI or RPC mode so results and safety prompts are observable.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst run = () => handleCommand(args, ctx, sessionAbort.signal, loaders, attention);\n\t\t\tif (splitArgs(args)[0] === \"migrate-state\") await run();\n\t\t\telse await withStateDirectoryAccess(run);\n\t\t},\n\t});\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tshutdownAbort?.abort(new DOMException(\"Session replaced\", \"AbortError\"));\n\t\tshutdownAbort = undefined;\n\t\tsessionAbort.abort(new DOMException(\"Session replaced\", \"AbortError\"));\n\t\tsessionAbort = new AbortController();\n\t\tconst signal = sessionAbort.signal;\n\t\tattention.reset(ctx);\n\t\tlet decision: RemoteSelectionDecision | undefined;\n\t\ttry {\n\t\t\tdecision = await withStateDirectoryAccess(() => startSession(ctx, signal, loaders));\n\t\t} catch (error) {\n\t\t\tif (signal.aborted) return;\n\t\t\tctx.ui.notify(`pi-sync state access failed: ${errorMessage(error)}`, \"error\");\n\t\t\treturn;\n\t\t}\n\t\tif (!decision || signal.aborted) return;\n\t\tattention.set(decision, \"sync\");\n\t\tif (ctx.mode !== \"tui\") {\n\t\t\tctx.ui.notify(\n\t\t\t\t`pi-sync auto sync skipped: ${formatRemoteSelectionMismatch(\n\t\t\t\t\tdecision.setupName,\n\t\t\t\t\tdecision.localInclude,\n\t\t\t\t\tdecision.remoteInclude,\n\t\t\t\t)}\\nRPC review is read-only.`,\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t} else if (attention.markOffered()) {\n\t\t\tawait resolveSelectionAttention(ctx, attention, signal, loaders, {\n\t\t\t\tcancelLabel: \"Later\",\n\t\t\t\twithStateAccess: withStateDirectoryAccess,\n\t\t\t});\n\t\t}\n\t\tif (!signal.aborted) attention.publish(ctx);\n\t});\n\n\tpi.on(\"session_shutdown\", async (event, ctx) => {\n\t\tsessionAbort.abort(new DOMException(\"Session shut down\", \"AbortError\"));\n\t\tattention.reset(ctx);\n\t\tshutdownAbort?.abort(new DOMException(\"Session shut down again\", \"AbortError\"));\n\t\tconst controller = new AbortController();\n\t\tshutdownAbort = controller;\n\t\tconst signal = combineSignals(controller.signal, AbortSignal.timeout(30_000));\n\t\tconst reason =\n\t\t\ttypeof event === \"object\" && event ? (event as { reason?: string }).reason : undefined;\n\t\ttry {\n\t\t\tif (reason !== \"reload\") {\n\t\t\t\tawait withStateDirectoryAccess(async () => {\n\t\t\t\t\tif (signal.aborted) return;\n\t\t\t\t\tawait autoPushSessions(ctx, signal, loaders);\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (!signal.aborted) {\n\t\t\t\tctx.ui.notify(`pi-sync session push skipped: ${errorMessage(error)}`, \"warning\");\n\t\t\t}\n\t\t} finally {\n\t\t\tif (shutdownAbort === controller) shutdownAbort = undefined;\n\t\t}\n\t\tif (signal.aborted) return;\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t});\n}\n\nasync function startSession(ctx: ExtensionContext, signal: AbortSignal, loaders: SyncLoaders) {\n\tif (signal.aborted) return;\n\ttry {\n\t\tconst migrationNotice = stateDirectoryMigrationNotice();\n\t\tif (migrationNotice) ctx.ui.notify(migrationNotice, \"warning\");\n\t} catch (error) {\n\t\tctx.ui.notify(`pi-sync state directory requires attention: ${errorMessage(error)}`, \"error\");\n\t\treturn;\n\t}\n\ttry {\n\t\tawait recoverSnapshotTransactionsOnStartup();\n\t\tif (signal.aborted) return;\n\t} catch (error) {\n\t\tif (signal.aborted) return;\n\t\tctx.ui.notify(`pi-sync recovery required: ${errorMessage(error)}`, \"error\");\n\t\treturn;\n\t}\n\ttry {\n\t\tsetSyncSetupCompletions(await configuredSyncSetupNames());\n\t\tif (signal.aborted) return;\n\t} catch {\n\t\tif (signal.aborted) return;\n\t\tsetSyncSetupCompletions([]);\n\t}\n\tconst migrationNotice = consumeLocalConfigMigrationNotice();\n\tif (migrationNotice) ctx.ui.notify(migrationNotice, \"warning\");\n\tif (signal.aborted) return;\n\treturn autoSync(ctx, signal, loaders);\n}\n\nasync function handleCommand(\n\trawArgs: string,\n\tctx: ExtensionCommandContext,\n\tsessionSignal: AbortSignal,\n\tloaders: SyncLoaders,\n\tattention: SyncAttentionController,\n) {\n\tif (!rawArgs.trim()) {\n\t\ttry {\n\t\t\tconst { showSyncManager } = await import(\"./manager-ui.js\");\n\t\t\tif (sessionSignal.aborted) return;\n\t\t\tawait showSyncManager(\n\t\t\t\tctx,\n\t\t\t\t(route, signal, onCommit, target) =>\n\t\t\t\t\texecuteCommand(\n\t\t\t\t\t\troute,\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\tcombineSignals(sessionSignal, signal),\n\t\t\t\t\t\tloaders,\n\t\t\t\t\t\tonCommit,\n\t\t\t\t\t\ttarget,\n\t\t\t\t\t),\n\t\t\t\tsessionSignal,\n\t\t\t\t{\n\t\t\t\t\tgetAttention: () => attention.current(),\n\t\t\t\t\tonSelectionResolved: (expected) => {\n\t\t\t\t\t\tif (attention.current() === expected) attention.clear(ctx);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (sessionSignal.aborted) return;\n\t\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\t\tctx.ui.notify(errorMessage(error), \"error\");\n\t\t}\n\t\tif (!sessionSignal.aborted) attention.publish(ctx);\n\t\treturn;\n\t}\n\tconst result = await executeCommand(rawArgs, ctx, sessionSignal, loaders);\n\tif (result.kind === \"decision-required\") {\n\t\tctx.ui.notify(result.decision.directMessage, \"error\");\n\t} else if (result.kind === \"remote-selection-required\") {\n\t\tconst origin = directSelectionOrigin(rawArgs);\n\t\tif (origin) attention.set(result.decision, origin);\n\t\tconst deterministic = splitArgs(rawArgs).some((arg) => arg === \"--yes\" || arg === \"-y\");\n\t\tif (origin && ctx.mode === \"tui\" && !deterministic) {\n\t\t\tawait resolveSelectionAttention(ctx, attention, sessionSignal, loaders);\n\t\t} else {\n\t\t\tctx.ui.notify(\n\t\t\t\tformatRemoteSelectionMismatch(\n\t\t\t\t\tresult.decision.setupName,\n\t\t\t\t\tresult.decision.localInclude,\n\t\t\t\t\tresult.decision.remoteInclude,\n\t\t\t\t),\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\tawait clearAttentionAfterCompletedOperation(rawArgs, result, ctx, attention, sessionSignal);\n\tawait reconcileSelectionAttention(ctx, attention, sessionSignal);\n\tif (!sessionSignal.aborted) attention.publish(ctx);\n}\n\nasync function clearAttentionAfterCompletedOperation(\n\trawArgs: string,\n\tresult: RunRouteResult,\n\tctx: ExtensionContext,\n\tattention: SyncAttentionController,\n\tsignal: AbortSignal,\n) {\n\tif (result.kind !== \"completed\" || result.outcome === \"cancelled\" || signal.aborted) return;\n\tconst [command, ...rest] = splitArgs(rawArgs);\n\tif (command !== \"sync\" && command !== \"pull\" && command !== \"push\") return;\n\tconst current = attention.current();\n\tif (!current) return;\n\ttry {\n\t\tconst options = parseOptions(rest);\n\t\tconst setupName = options.setup ?? (await loadConfig()).setupName;\n\t\tif (signal.aborted || attention.current() !== current) return;\n\t\tif (current.decision.setupName === setupName) attention.clear(ctx);\n\t} catch {\n\t\t// Attention reconciliation below owns malformed or concurrently changed settings.\n\t}\n}\n\nasync function reconcileSelectionAttention(\n\tctx: ExtensionContext,\n\tattention: SyncAttentionController,\n\tsignal: AbortSignal,\n) {\n\tconst current = attention.current();\n\tif (!current || signal.aborted) return;\n\ttry {\n\t\tconst config = await loadConfig(current.decision.setupName);\n\t\tif (signal.aborted || attention.current() !== current) return;\n\t\tif (!syncAttentionMatchesConfig(current, config)) attention.clear(ctx);\n\t} catch {\n\t\tif (!signal.aborted && attention.current() === current) attention.clear(ctx);\n\t}\n}\n\nfunction directSelectionOrigin(rawArgs: string): SyncAttentionOrigin | undefined {\n\tconst command = splitArgs(rawArgs)[0];\n\treturn command === \"sync\" || command === \"pull\" || command === \"push\" ? command : undefined;\n}\n\nasync function resolveSelectionAttention(\n\tctx: ExtensionContext,\n\tattention: SyncAttentionController,\n\tsignal: AbortSignal,\n\tloaders: SyncLoaders,\n\toptions: {\n\t\tcancelLabel?: string;\n\t\twithStateAccess?: <T>(task: () => Promise<T>) => Promise<T>;\n\t} = {},\n) {\n\tconst current = attention.current();\n\tif (!current || signal.aborted) return;\n\tconst { dispatchManagerResult } = await import(\"./manager-result-dispatcher.js\");\n\tif (signal.aborted || attention.current() !== current) return;\n\tawait dispatchManagerResult(\n\t\tctx,\n\t\t{ kind: \"remote-selection-required\", decision: current.decision },\n\t\tcurrent.origin,\n\t\t(route, actionSignal, onCommit, target) => {\n\t\t\tconst execute = () =>\n\t\t\t\texecuteRecoveryCommand(\n\t\t\t\t\troute,\n\t\t\t\t\tctx,\n\t\t\t\t\tcombineSignals(signal, actionSignal),\n\t\t\t\t\tloaders,\n\t\t\t\t\tonCommit,\n\t\t\t\t\ttarget,\n\t\t\t\t);\n\t\t\treturn options.withStateAccess ? options.withStateAccess(execute) : execute();\n\t\t},\n\t\tsignal,\n\t\t{\n\t\t\tcancelLabel: options.cancelLabel,\n\t\t\twithStateAccess: options.withStateAccess,\n\t\t\tonSelectionResolved: () => {\n\t\t\t\tif (attention.current() === current) attention.clear(ctx);\n\t\t\t},\n\t\t},\n\t);\n}\n\nasync function executeRecoveryCommand(\n\trawArgs: string,\n\tctx: ExtensionContext,\n\tsignal: AbortSignal | undefined,\n\tloaders: SyncLoaders,\n\tonCommit?: () => void,\n\tsetup?: string,\n): Promise<RunRouteResult> {\n\ttry {\n\t\tconst [subcommand, ...rest] = splitArgs(rawArgs);\n\t\tif (subcommand !== \"sync\" && subcommand !== \"pull\" && subcommand !== \"push\") {\n\t\t\tthrow new Error(`Unsupported sync recovery route: ${subcommand ?? \"missing\"}`);\n\t\t}\n\t\tconst options = parseOptions(rest);\n\t\tif (setup !== undefined) options.setup = setup;\n\t\tif (signal) options.signal = signal;\n\t\tif (onCommit) options.onCommit = onCommit;\n\t\toptions.reload = false;\n\t\toptions.auto = false;\n\t\tvalidateCommandOptions(subcommand, options);\n\t\tconst operations = await loaders.operations();\n\t\tthrowIfAborted(options.signal);\n\t\tif (subcommand === \"push\") {\n\t\t\tconst outcome = await withLock(\"push\", () => operations.push(ctx, options));\n\t\t\treturn { kind: \"completed\", ...(outcome ? { outcome } : {}) };\n\t\t}\n\t\tif (subcommand === \"pull\") {\n\t\t\tconst outcome = await withLock(\"pull\", () => operations.pull(ctx, options));\n\t\t\treturn { kind: \"completed\", ...(outcome ? { outcome } : {}) };\n\t\t}\n\t\tawait withLock(\"sync\", () => operations.syncBoth(ctx, options));\n\t\treturn { kind: \"completed\" };\n\t} catch (error) {\n\t\tif (signal?.aborted) return { kind: \"failed\" };\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tif (error instanceof RemoteSelectionMismatchError) {\n\t\t\treturn { kind: \"remote-selection-required\", decision: error.decision };\n\t\t}\n\t\tif (isSyncDecisionRequiredError(error)) {\n\t\t\treturn { kind: \"decision-required\", decision: error.decision };\n\t\t}\n\t\tctx.ui.notify(errorMessage(error), \"error\");\n\t\treturn { kind: \"failed\" };\n\t}\n}\n\nasync function executeCommand(\n\trawArgs: string,\n\tctx: ExtensionCommandContext,\n\tsignal: AbortSignal | undefined,\n\tloaders: SyncLoaders,\n\tonCommit?: () => void,\n\tsetup?: string,\n): Promise<RunRouteResult> {\n\ttry {\n\t\tconst command = await resolveSyncCommand(rawArgs, ctx);\n\t\tif (signal?.aborted || !command) return { kind: \"completed\" };\n\t\tconst { subcommand, rest } = command;\n\t\tconst options = parseOptions(rest);\n\t\tif (setup !== undefined) options.setup = setup;\n\t\tif (signal) options.signal = signal;\n\t\tif (onCommit) options.onCommit = onCommit;\n\t\tvalidateCommandOptions(subcommand, options);\n\n\t\tswitch (subcommand) {\n\t\t\tcase \"help\":\n\t\t\t\tctx.ui.notify(usage(), \"info\");\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tcase \"use\": {\n\t\t\t\tconst { useSyncSetup } = await loaders.setupSwitch();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait useSyncSetup(\n\t\t\t\t\tctx,\n\t\t\t\t\toptions.args[0] ?? \"\",\n\t\t\t\t\tasync (selectedSetup) => {\n\t\t\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\t\t\treturn withLock(\"pull\", () =>\n\t\t\t\t\t\t\toperations.pull(ctx, { ...options, setup: selectedSetup }),\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\tundefined,\n\t\t\t\t\toptions.signal,\n\t\t\t\t);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"init\":\n\t\t\t\tawait initConfig(ctx, signal);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tcase \"config\":\n\t\t\t\tawait showConfig(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tcase \"files\": {\n\t\t\t\tconst { showFileSelection } = await import(\"./file-selection.js\");\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait showFileSelection(ctx, options.setup, options.signal);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"status\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait operations.status(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"diff\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait operations.diff(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"doctor\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait operations.doctor(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"push\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tconst outcome = await withLock(\"push\", () => operations.push(ctx, options));\n\t\t\t\treturn { kind: \"completed\", ...(outcome ? { outcome } : {}) };\n\t\t\t}\n\t\t\tcase \"pull\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tconst outcome = await withLock(\"pull\", () => operations.pull(ctx, options));\n\t\t\t\treturn { kind: \"completed\", ...(outcome ? { outcome } : {}) };\n\t\t\t}\n\t\t\tcase \"sync\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait withLock(\"sync\", () => operations.syncBoth(ctx, options));\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"history\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait operations.history(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"rollback\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait withLock(\"rollback\", () => operations.rollback(ctx, options));\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"migrate-state\":\n\t\t\t\tawait migrateStateDirectory(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tcase \"unlock\":\n\t\t\t\tawait unlock(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tdefault:\n\t\t\t\tctx.ui.notify(`Unknown /sync command: ${subcommand}\\n\\n${usage()}`, \"warning\");\n\t\t\t\treturn { kind: \"failed\" };\n\t\t}\n\t} catch (error) {\n\t\tif (signal?.aborted) return { kind: \"failed\" };\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tif (error instanceof SetupPullRequiresUiError) throw error;\n\t\tif (error instanceof RemoteSelectionMismatchError) {\n\t\t\treturn { kind: \"remote-selection-required\", decision: error.decision };\n\t\t}\n\t\tif (isSyncDecisionRequiredError(error)) {\n\t\t\treturn { kind: \"decision-required\", decision: error.decision };\n\t\t}\n\t\tctx.ui.notify(errorMessage(error), \"error\");\n\t\treturn { kind: \"failed\" };\n\t}\n}\n\nasync function migrateStateDirectory(ctx: ExtensionCommandContext, options: CommandOptions) {\n\tconst notice = stateDirectoryMigrationNotice();\n\tif (!notice) {\n\t\tctx.ui.notify(\"pi-sync already uses the canonical pi-sync/ state directory.\", \"info\");\n\t\treturn;\n\t}\n\tif (\n\t\t!options.yes &&\n\t\t!(await ctx.ui.confirm(\n\t\t\t\"Migrate pi-sync state directory\",\n\t\t\t\"Confirm that every other Pi process is closed. pi-sync will atomically rename .pisync/ to pi-sync/ without merging or deleting either root.\",\n\t\t\t{ signal: options.signal },\n\t\t))\n\t) {\n\t\tctx.ui.notify(\"pi-sync state migration cancelled.\", \"info\");\n\t\treturn;\n\t}\n\tthrowIfAborted(options.signal);\n\tconst result = await migrateLegacyStateDirectory();\n\tthrowIfAborted(options.signal);\n\tif (result.status === \"ready\") {\n\t\tctx.ui.notify(\"pi-sync already uses the canonical pi-sync/ state directory.\", \"info\");\n\t\treturn;\n\t}\n\tctx.ui.notify(result.message, result.status === \"migrated\" ? \"info\" : \"warning\");\n}\n\nasync function autoSync(\n\tctx: ExtensionContext,\n\tsignal: AbortSignal,\n\tloaders: SyncLoaders,\n): Promise<RemoteSelectionDecision | undefined> {\n\ttry {\n\t\tconst partial = await loadPartialConfig();\n\t\tthrowIfAborted(signal);\n\t\tif (!partial.automatic) return;\n\t\tawait ensureStateDir();\n\t\tthrowIfAborted(signal);\n\t\tawait loadConfig();\n\t\tthrowIfAborted(signal);\n\t\tconst operations = await loaders.operations();\n\t\tthrowIfAborted(signal);\n\t\tawait withLock(\"auto-sync\", () => {\n\t\t\tthrowIfAborted(signal);\n\t\t\treturn operations.syncBoth(ctx, { ...AUTO_SYNC_OPTIONS, signal });\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted || isMissingConfigError(error)) return;\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tif (error instanceof RemoteSelectionMismatchError) return error.decision;\n\t\tctx.ui.notify(`pi-sync auto sync skipped: ${errorMessage(error)}`, \"warning\");\n\t}\n}\n\nasync function autoPushSessions(ctx: ExtensionContext, signal: AbortSignal, loaders: SyncLoaders) {\n\ttry {\n\t\tconst partial = await loadPartialConfig();\n\t\tthrowIfAborted(signal);\n\t\tif (!partial.automatic) return;\n\t\tif (!partial.include.includes(\"sessions\")) return;\n\t\tawait ensureStateDir();\n\t\tthrowIfAborted(signal);\n\t\tconst config = await loadConfig();\n\t\tthrowIfAborted(signal);\n\t\tif (!config.include.includes(\"sessions\")) return;\n\t\tconst [operations, snapshotModule, syncStateModule] = await Promise.all([\n\t\t\tloaders.operations(),\n\t\t\tloaders.snapshot(),\n\t\t\tloaders.syncState(),\n\t\t]);\n\t\tthrowIfAborted(signal);\n\t\tawait withLock(\"auto-session-push\", async () => {\n\t\t\tthrowIfAborted(signal);\n\t\t\tconst state = await readStateForConfig(config);\n\t\t\tthrowIfAborted(signal);\n\t\t\tconst local = await snapshotModule.createSnapshot(\n\t\t\t\tconfig.snapshotIdentity,\n\t\t\t\tsnapshotOptionsForContext(ctx, config),\n\t\t\t);\n\t\t\tthrowIfAborted(signal);\n\t\t\tif (!syncStateModule.hasLocalChanges(local, state, config)) return;\n\t\t\tawait operations.push(ctx, { ...AUTO_SYNC_OPTIONS, signal }, { config, state, local });\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted || isMissingConfigError(error)) return;\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tctx.ui.notify(`pi-sync session push skipped: ${errorMessage(error)}`, \"warning\");\n\t}\n}\n\nasync function initConfig(ctx: ExtensionCommandContext, signal?: AbortSignal) {\n\tconst configPath = localConfigPath();\n\tif (await readLocalConfigObject()) {\n\t\tctx.ui.notify(`Config already exists: ${await activeLocalConfigPath()}`, \"info\");\n\t\treturn;\n\t}\n\n\tif (ctx.mode === \"tui\") {\n\t\tconst { showSetupWizard } = await import(\"./manager-ui.js\");\n\t\tthrowIfAborted(signal);\n\t\tawait showSetupWizard(ctx, signal);\n\t\treturn;\n\t}\n\tawait createLocalConfigDocument(localConfigTemplate());\n\tctx.ui.notify(\n\t\t`Created ${configPath}. Add a storage connection and sync setup before syncing.`,\n\t\t\"info\",\n\t);\n}\n\nasync function showConfig(ctx: ExtensionCommandContext, options: CommandOptions) {\n\tconst config = await loadConfig(options.setup);\n\tconst warnings = [\n\t\t...(config.backend.type === \"s3\" ? sessionTokenWarnings(config.backend.profile) : []),\n\t\t...syncSessionsWarnings(config),\n\t];\n\tconst storageLines = configStorageLines(config);\n\tctx.ui.notify(\n\t\t[\n\t\t\t\"pi-sync config:\",\n\t\t\t`sync setup: ${config.setupName}`,\n\t\t\t`storage connection: ${config.connectionName}`,\n\t\t\t...storageLines,\n\t\t\t`storage path: ${config.storagePath}`,\n\t\t\t`automatic sync: ${config.automatic ? \"enabled\" : \"disabled\"}`,\n\t\t\t`included content: ${config.include.join(\", \") || \"none\"}`,\n\t\t\t`sessions: ${config.include.includes(\"sessions\") ? \"included\" : \"not included\"}`,\n\t\t\t`settings file: ${localConfigPath()}`,\n\t\t\t...warnings,\n\t\t].join(\"\\n\"),\n\t\twarnings.length > 0 ? \"warning\" : \"info\",\n\t);\n}\n\nfunction configStorageLines(config: AnySyncConfig) {\n\tswitch (config.backend.type) {\n\t\tcase \"git\":\n\t\t\treturn [\n\t\t\t\t\"kind: git\",\n\t\t\t\t`remote: ${displayGitRemote(config.backend.profile.remote)}`,\n\t\t\t\t\"authentication: existing Git/SSH credentials (not stored)\",\n\t\t\t\t`branch: ${config.backend.destination.branch}`,\n\t\t\t];\n\t\tcase \"webdav\":\n\t\t\treturn [\n\t\t\t\t\"kind: webdav\",\n\t\t\t\t`url: ${displayWebDavUrl(config.backend.profile.url, config.backend.profile.username)}`,\n\t\t\t\t\"username: configured (value hidden)\",\n\t\t\t\t\"password: configured\",\n\t\t\t];\n\t\tcase \"s3\":\n\t\t\treturn [\n\t\t\t\t\"kind: s3\",\n\t\t\t\t`endpoint: ${config.backend.profile.endpoint}`,\n\t\t\t\t`bucket: ${config.backend.destination.bucket}`,\n\t\t\t\t`region: ${config.backend.profile.region}`,\n\t\t\t\t\"access key id: configured\",\n\t\t\t\t\"secret access key: configured\",\n\t\t\t\t`session token: ${config.backend.profile.sessionToken ? \"configured\" : \"not configured\"}`,\n\t\t\t];\n\t}\n}\n\nfunction displayGitRemote(value: string | undefined) {\n\tif (!value) return \"missing\";\n\ttry {\n\t\tconst url = new URL(value);\n\t\turl.username = \"\";\n\t\turl.password = \"\";\n\t\turl.search = \"\";\n\t\turl.hash = \"\";\n\t\treturn url.toString();\n\t} catch {\n\t\treturn value.replace(/^(?:[^@\\s]+@)?(?<host>[^:]+):.+$/u, \"$<host>:\u2026\");\n\t}\n}\n\nfunction displayWebDavUrl(value: string | undefined, username: string | undefined) {\n\tif (!value) return \"missing\";\n\ttry {\n\t\tconst url = new URL(value);\n\t\turl.username = \"\";\n\t\turl.password = \"\";\n\t\turl.search = \"\";\n\t\turl.hash = \"\";\n\t\treturn username ? `${url.origin}/\u2026` : `${url.origin}${url.pathname}`;\n\t} catch {\n\t\treturn \"invalid (value hidden)\";\n\t}\n}\n\nfunction throwIfAborted(signal?: AbortSignal) {\n\tif (!signal?.aborted) return;\n\tthrow signal.reason instanceof Error\n\t\t? signal.reason\n\t\t: new DOMException(\"The operation was aborted\", \"AbortError\");\n}\n\nfunction combineSignals(primary: AbortSignal, secondary?: AbortSignal) {\n\treturn secondary ? AbortSignal.any([primary, secondary]) : primary;\n}\n\nfunction snapshotOptionsForContext(\n\tctx: ExtensionCommandContext | ExtensionContext,\n\tconfig: AnySyncConfig,\n): SnapshotOptions {\n\treturn {\n\t\tinclude: config.include,\n\t\tsessionDir: sessionDirFromContext(ctx),\n\t};\n}\n\nfunction sessionDirFromContext(ctx: ExtensionCommandContext | ExtensionContext) {\n\tconst manager = ctx.sessionManager as typeof ctx.sessionManager & {\n\t\tusesDefaultSessionDir?: () => boolean;\n\t};\n\tconst usesDefaultSessionDir = manager.usesDefaultSessionDir;\n\tif (typeof usesDefaultSessionDir === \"function\" && usesDefaultSessionDir.call(manager)) {\n\t\treturn undefined;\n\t}\n\tconst getSessionDir = manager.getSessionDir;\n\treturn typeof getSessionDir === \"function\"\n\t\t? (getSessionDir.call(manager) as string | undefined)\n\t\t: undefined;\n}\n\nfunction cachedModuleLoader<Module>(load: () => Promise<Module>): () => Promise<Module> {\n\tlet pending: Promise<Module> | undefined;\n\treturn () => {\n\t\tif (!pending) {\n\t\t\tpending = load().catch((error) => {\n\t\t\t\tpending = undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t}\n\t\treturn pending;\n\t};\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["
|
|
3
|
+
"sources": ["../src/commands/command-execution.ts", "../src/commands/command-handler.ts", "../src/settings/config-errors.ts", "../src/sync/automatic-sync.ts", "../src/sync/startup-check.ts", "../src/sync/sync-loaders.ts", "../src/sync-extension.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ExtensionCommandContext, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { loadConfig, sessionTokenWarnings, syncSessionsWarnings } from \"../settings/config.js\";\nimport {\n\tactiveLocalConfigPath,\n\tcreateLocalConfigDocument,\n\tlocalConfigPath,\n} from \"../settings/config-file.js\";\nimport { localConfigTemplate, readLocalConfigObject } from \"../settings/settings-store.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { unlock, withLock } from \"../state/lock.js\";\nimport {\n\tmigrateLegacyStateDirectory,\n\tstateDirectoryMigrationNotice,\n} from \"../state/state-directory.js\";\nimport { throwIfAborted } from \"../sync/signals.js\";\nimport { syncErrorGuidance } from \"../sync/sync-error-guidance.js\";\nimport { isSyncDecisionRequiredError, SetupPullRequiresUiError } from \"../sync/sync-errors.js\";\nimport type { SyncLoaders } from \"../sync/sync-loaders.js\";\nimport { RemoteSelectionMismatchError } from \"../sync/sync-policy.js\";\nimport { automaticSyncSummary } from \"../ui/automatic-sync-summary.js\";\nimport type { RunRouteResult } from \"../ui/cancellable-operation.js\";\nimport {\n\tparseOptions,\n\tresolveSyncCommand,\n\tsplitArgs,\n\tusage,\n\tvalidateCommandOptions,\n} from \"./command.js\";\nimport type { CommandOptions } from \"./command-types.js\";\n\nconst STATUS_KEY = \"sync\";\n\nexport async function executeRecoveryCommand(\n\trawArgs: string,\n\tctx: ExtensionContext,\n\tsignal: AbortSignal | undefined,\n\tloaders: SyncLoaders,\n\tonCommit?: () => void,\n\tsetup?: string,\n): Promise<RunRouteResult> {\n\ttry {\n\t\tconst [subcommand, ...rest] = splitArgs(rawArgs);\n\t\tif (subcommand !== \"sync\" && subcommand !== \"pull\" && subcommand !== \"push\") {\n\t\t\tthrow new Error(`Unsupported sync recovery route: ${subcommand ?? \"missing\"}`);\n\t\t}\n\t\tconst options = parseOptions(rest);\n\t\tif (setup !== undefined) options.setup = setup;\n\t\tif (signal) options.signal = signal;\n\t\tif (onCommit) options.onCommit = onCommit;\n\t\toptions.reload = false;\n\t\toptions.auto = false;\n\t\tvalidateCommandOptions(subcommand, options);\n\t\tconst operations = await loaders.operations();\n\t\tthrowIfAborted(options.signal);\n\t\tif (subcommand === \"push\") {\n\t\t\tconst outcome = await withLock(\"push\", () => operations.push(ctx, options));\n\t\t\treturn { kind: \"completed\", ...(outcome ? { outcome } : {}) };\n\t\t}\n\t\tif (subcommand === \"pull\") {\n\t\t\tconst outcome = await withLock(\"pull\", () => operations.pull(ctx, options));\n\t\t\treturn { kind: \"completed\", ...(outcome ? { outcome } : {}) };\n\t\t}\n\t\tawait withLock(\"sync\", () => operations.syncBoth(ctx, options));\n\t\treturn { kind: \"completed\" };\n\t} catch (error) {\n\t\tif (signal?.aborted) return { kind: \"failed\" };\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tif (error instanceof RemoteSelectionMismatchError) {\n\t\t\treturn { kind: \"remote-selection-required\", decision: error.decision };\n\t\t}\n\t\tif (isSyncDecisionRequiredError(error)) {\n\t\t\treturn { kind: \"decision-required\", decision: error.decision };\n\t\t}\n\t\tctx.ui.notify(syncErrorGuidance(error), \"error\");\n\t\treturn { kind: \"failed\" };\n\t}\n}\n\nexport async function executeCommand(\n\trawArgs: string,\n\tctx: ExtensionCommandContext,\n\tsignal: AbortSignal | undefined,\n\tloaders: SyncLoaders,\n\tonCommit?: () => void,\n\tsetup?: string,\n): Promise<RunRouteResult> {\n\ttry {\n\t\tconst command = await resolveSyncCommand(rawArgs, ctx);\n\t\tif (signal?.aborted || !command) return { kind: \"completed\" };\n\t\tconst { subcommand, rest } = command;\n\t\tconst options = parseOptions(rest);\n\t\tif (setup !== undefined) options.setup = setup;\n\t\tif (signal) options.signal = signal;\n\t\tif (onCommit) options.onCommit = onCommit;\n\t\tvalidateCommandOptions(subcommand, options);\n\n\t\tswitch (subcommand) {\n\t\t\tcase \"help\":\n\t\t\t\tctx.ui.notify(usage(), \"info\");\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tcase \"use\": {\n\t\t\t\tconst { useSyncSetup } = await loaders.setupSwitch();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait useSyncSetup(\n\t\t\t\t\tctx,\n\t\t\t\t\toptions.args[0] ?? \"\",\n\t\t\t\t\tasync (selectedSetup) => {\n\t\t\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\t\t\treturn withLock(\"pull\", () =>\n\t\t\t\t\t\t\toperations.pull(ctx, { ...options, setup: selectedSetup }),\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\tundefined,\n\t\t\t\t\toptions.signal,\n\t\t\t\t);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"init\":\n\t\t\t\tawait initConfig(ctx, signal);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tcase \"config\":\n\t\t\t\tawait showConfig(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tcase \"files\": {\n\t\t\t\tconst { showFileSelection } = await import(\"../ui/file-selection.js\");\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait showFileSelection(ctx, options.setup, options.signal);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"status\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait operations.status(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"diff\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait operations.diff(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"doctor\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait operations.doctor(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"push\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tconst outcome = await withLock(\"push\", () => operations.push(ctx, options));\n\t\t\t\treturn { kind: \"completed\", ...(outcome ? { outcome } : {}) };\n\t\t\t}\n\t\t\tcase \"pull\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tconst outcome = await withLock(\"pull\", () => operations.pull(ctx, options));\n\t\t\t\treturn { kind: \"completed\", ...(outcome ? { outcome } : {}) };\n\t\t\t}\n\t\t\tcase \"sync\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait withLock(\"sync\", () => operations.syncBoth(ctx, options));\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"history\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait operations.history(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"rollback\": {\n\t\t\t\tconst operations = await loaders.operations();\n\t\t\t\tthrowIfAborted(options.signal);\n\t\t\t\tawait withLock(\"rollback\", () => operations.rollback(ctx, options));\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\t}\n\t\t\tcase \"migrate-state\":\n\t\t\t\tawait migrateStateDirectory(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tcase \"unlock\":\n\t\t\t\tawait unlock(ctx, options);\n\t\t\t\treturn { kind: \"completed\" };\n\t\t\tdefault:\n\t\t\t\tctx.ui.notify(`Unknown /sync command: ${subcommand}\\n\\n${usage()}`, \"warning\");\n\t\t\t\treturn { kind: \"failed\" };\n\t\t}\n\t} catch (error) {\n\t\tif (signal?.aborted) return { kind: \"failed\" };\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tif (error instanceof SetupPullRequiresUiError) throw error;\n\t\tif (error instanceof RemoteSelectionMismatchError) {\n\t\t\treturn { kind: \"remote-selection-required\", decision: error.decision };\n\t\t}\n\t\tif (isSyncDecisionRequiredError(error)) {\n\t\t\treturn { kind: \"decision-required\", decision: error.decision };\n\t\t}\n\t\tctx.ui.notify(syncErrorGuidance(error), \"error\");\n\t\treturn { kind: \"failed\" };\n\t}\n}\n\nasync function migrateStateDirectory(ctx: ExtensionCommandContext, options: CommandOptions) {\n\tconst notice = stateDirectoryMigrationNotice();\n\tif (!notice) {\n\t\tctx.ui.notify(\"pi-sync already uses the canonical pi-sync/ state directory.\", \"info\");\n\t\treturn;\n\t}\n\tif (\n\t\t!options.yes &&\n\t\t!(await ctx.ui.confirm(\n\t\t\t\"Migrate pi-sync state directory\",\n\t\t\t\"Confirm that every other Pi process is closed. pi-sync will atomically rename .pisync/ to pi-sync/ without merging or deleting either root.\",\n\t\t\t{ signal: options.signal },\n\t\t))\n\t) {\n\t\tctx.ui.notify(\"pi-sync state migration cancelled.\", \"info\");\n\t\treturn;\n\t}\n\tthrowIfAborted(options.signal);\n\tconst result = await migrateLegacyStateDirectory();\n\tthrowIfAborted(options.signal);\n\tif (result.status === \"ready\") {\n\t\tctx.ui.notify(\"pi-sync already uses the canonical pi-sync/ state directory.\", \"info\");\n\t\treturn;\n\t}\n\tctx.ui.notify(result.message, result.status === \"migrated\" ? \"info\" : \"warning\");\n}\n\nasync function initConfig(ctx: ExtensionCommandContext, signal?: AbortSignal) {\n\tconst configPath = localConfigPath();\n\tif (await readLocalConfigObject()) {\n\t\tctx.ui.notify(`Config already exists: ${await activeLocalConfigPath()}`, \"info\");\n\t\treturn;\n\t}\n\n\tif (ctx.mode === \"tui\") {\n\t\tconst { showSetupWizard } = await import(\"../ui/setup/setup-wizard.js\");\n\t\tthrowIfAborted(signal);\n\t\tawait showSetupWizard(ctx, signal);\n\t\treturn;\n\t}\n\tawait createLocalConfigDocument(localConfigTemplate());\n\tctx.ui.notify(\n\t\t`Created ${configPath}. Add a storage connection and sync setup before syncing.`,\n\t\t\"info\",\n\t);\n}\n\nasync function showConfig(ctx: ExtensionCommandContext, options: CommandOptions) {\n\tconst config = await loadConfig(options.setup);\n\tconst warnings = [\n\t\t...(config.backend.type === \"s3\" ? sessionTokenWarnings(config.backend.profile) : []),\n\t\t...syncSessionsWarnings(config),\n\t];\n\tconst storageLines = configStorageLines(config);\n\tctx.ui.notify(\n\t\t[\n\t\t\t\"pi-sync config:\",\n\t\t\t`sync setup: ${config.setupName}`,\n\t\t\t`storage connection: ${config.connectionName}`,\n\t\t\t...storageLines,\n\t\t\t`storage path: ${config.storagePath}`,\n\t\t\t`automatic sync: ${automaticSyncSummary(config.automatic)}`,\n\t\t\t`included content: ${config.include.join(\", \") || \"none\"}`,\n\t\t\t`sessions: ${config.include.includes(\"sessions\") ? \"included\" : \"not included\"}`,\n\t\t\t`settings file: ${localConfigPath()}`,\n\t\t\t...warnings,\n\t\t].join(\"\\n\"),\n\t\twarnings.length > 0 ? \"warning\" : \"info\",\n\t);\n}\n\nfunction configStorageLines(config: AnySyncConfig) {\n\tswitch (config.backend.type) {\n\t\tcase \"git\":\n\t\t\treturn [\n\t\t\t\t\"kind: git\",\n\t\t\t\t`remote: ${displayGitRemote(config.backend.profile.remote)}`,\n\t\t\t\t\"authentication: existing Git/SSH credentials (not stored)\",\n\t\t\t\t`branch: ${config.backend.destination.branch}`,\n\t\t\t];\n\t\tcase \"webdav\":\n\t\t\treturn [\n\t\t\t\t\"kind: webdav\",\n\t\t\t\t`url: ${displayWebDavUrl(config.backend.profile.url, config.backend.profile.username)}`,\n\t\t\t\t\"username: configured (value hidden)\",\n\t\t\t\t\"password: configured\",\n\t\t\t];\n\t\tcase \"s3\":\n\t\t\treturn [\n\t\t\t\t\"kind: s3\",\n\t\t\t\t`endpoint: ${config.backend.profile.endpoint}`,\n\t\t\t\t`bucket: ${config.backend.destination.bucket}`,\n\t\t\t\t`region: ${config.backend.profile.region}`,\n\t\t\t\t\"access key id: configured\",\n\t\t\t\t\"secret access key: configured\",\n\t\t\t\t`session token: ${config.backend.profile.sessionToken ? \"configured\" : \"not configured\"}`,\n\t\t\t];\n\t}\n}\n\nfunction displayGitRemote(value: string | undefined) {\n\tif (!value) return \"missing\";\n\ttry {\n\t\tconst url = new URL(value);\n\t\turl.username = \"\";\n\t\turl.password = \"\";\n\t\turl.search = \"\";\n\t\turl.hash = \"\";\n\t\treturn url.toString();\n\t} catch {\n\t\treturn value.replace(/^(?:[^@\\s]+@)?(?<host>[^:]+):.+$/u, \"$<host>:\u2026\");\n\t}\n}\n\nfunction displayWebDavUrl(value: string | undefined, username: string | undefined) {\n\tif (!value) return \"missing\";\n\ttry {\n\t\tconst url = new URL(value);\n\t\turl.username = \"\";\n\t\turl.password = \"\";\n\t\turl.search = \"\";\n\t\turl.hash = \"\";\n\t\treturn username ? `${url.origin}/\u2026` : `${url.origin}${url.pathname}`;\n\t} catch {\n\t\treturn \"invalid (value hidden)\";\n\t}\n}\n", "import type { ExtensionCommandContext, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { loadConfig } from \"../settings/config.js\";\nimport { readStateForConfig, syncStateFingerprint } from \"../state/sync-state-store.js\";\nimport { combineSignals } from \"../sync/signals.js\";\nimport { errorMessage } from \"../sync/sync-errors.js\";\nimport type { SyncLoaders } from \"../sync/sync-loaders.js\";\nimport { formatRemoteSelectionMismatch } from \"../sync/sync-policy.js\";\nimport type { RunRouteResult } from \"../ui/cancellable-operation.js\";\nimport {\n\tobservationMatchesConfig,\n\ttype SyncAttentionController,\n\ttype SyncAttentionOrigin,\n\tsyncAttentionMatchesConfig,\n} from \"../ui/sync-attention.js\";\nimport { parseOptions, splitArgs } from \"./command.js\";\nimport { executeCommand, executeRecoveryCommand } from \"./command-execution.js\";\n\nconst STATUS_KEY = \"sync\";\n\nexport async function handleCommand(\n\trawArgs: string,\n\tctx: ExtensionCommandContext,\n\tsessionSignal: AbortSignal,\n\tloaders: SyncLoaders,\n\tattention: SyncAttentionController,\n) {\n\tawait reconcileObservation(attention, sessionSignal);\n\tif (sessionSignal.aborted) return;\n\tconst run = (route: string, signal?: AbortSignal, onCommit?: () => void, target?: string) =>\n\t\texecuteCommand(\n\t\t\troute,\n\t\t\tctx,\n\t\t\tcombineSignals(sessionSignal, signal),\n\t\t\tloaders,\n\t\t\tobservationCommitCallback(attention, sessionSignal, onCommit),\n\t\t\ttarget,\n\t\t);\n\tif (!rawArgs.trim()) {\n\t\ttry {\n\t\t\tconst { showSyncManager } = await import(\"../ui/manager-ui.js\");\n\t\t\tif (sessionSignal.aborted) return;\n\t\t\tawait showSyncManager(ctx, run, sessionSignal, {\n\t\t\t\tgetAttention: () => attention.current(),\n\t\t\t\tgetObservation: () => attention.observation(),\n\t\t\t\tonObservationInvalidated: () => attention.clearObservation(),\n\t\t\t\tonSelectionResolved: (expected) => {\n\t\t\t\t\tif (attention.current() === expected) attention.clear(ctx);\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (sessionSignal.aborted) return;\n\t\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\t\tctx.ui.notify(errorMessage(error), \"error\");\n\t\t}\n\t\tawait reconcileObservation(attention, sessionSignal);\n\t\tif (!sessionSignal.aborted) attention.publish(ctx);\n\t\treturn;\n\t}\n\tconst result = await run(rawArgs);\n\tif (result.kind === \"decision-required\") {\n\t\tctx.ui.notify(result.decision.directMessage, \"error\");\n\t} else if (result.kind === \"remote-selection-required\") {\n\t\tconst origin = directSelectionOrigin(rawArgs);\n\t\tif (origin) attention.set(result.decision, origin);\n\t\tconst deterministic = splitArgs(rawArgs).some((arg) => arg === \"--yes\" || arg === \"-y\");\n\t\tif (origin && ctx.mode === \"tui\" && !deterministic) {\n\t\t\tawait resolveSelectionAttention(ctx, attention, sessionSignal, loaders);\n\t\t} else {\n\t\t\tctx.ui.notify(\n\t\t\t\tformatRemoteSelectionMismatch(\n\t\t\t\t\tresult.decision.setupName,\n\t\t\t\t\tresult.decision.localInclude,\n\t\t\t\t\tresult.decision.remoteInclude,\n\t\t\t\t),\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\tawait clearAttentionAfterCompletedOperation(rawArgs, result, ctx, attention, sessionSignal);\n\tawait reconcileSelectionAttention(ctx, attention, sessionSignal);\n\tawait reconcileObservation(attention, sessionSignal);\n\tif (!sessionSignal.aborted) attention.publish(ctx);\n}\n\nfunction observationCommitCallback(\n\tattention: SyncAttentionController,\n\tsessionSignal: AbortSignal,\n\tonCommit?: () => void,\n) {\n\tconst observed = attention.observation();\n\treturn () => {\n\t\t// Opening or cancelling a review changes nothing. A commit may change data\n\t\t// even if later publication/baseline cleanup fails; never restore old hints.\n\t\tif (!sessionSignal.aborted && attention.observation() === observed)\n\t\t\tattention.clearObservation();\n\t\tonCommit?.();\n\t};\n}\n\nasync function reconcileObservation(attention: SyncAttentionController, signal: AbortSignal) {\n\tconst observed = attention.observation();\n\tif (!observed || signal.aborted) return;\n\ttry {\n\t\tconst config = await loadConfig();\n\t\tif (signal.aborted || attention.observation() !== observed) return;\n\t\tif (!observationMatchesConfig(observed, config)) {\n\t\t\tattention.clearObservation();\n\t\t\treturn;\n\t\t}\n\t\tconst state = await readStateForConfig(config);\n\t\tif (signal.aborted || attention.observation() !== observed) return;\n\t\tif (syncStateFingerprint(state) !== observed.inspection.stateIdentity)\n\t\t\tattention.clearObservation();\n\t} catch {\n\t\tif (!signal.aborted && attention.observation() === observed) attention.clearObservation();\n\t}\n}\n\nasync function clearAttentionAfterCompletedOperation(\n\trawArgs: string,\n\tresult: RunRouteResult,\n\tctx: ExtensionContext,\n\tattention: SyncAttentionController,\n\tsignal: AbortSignal,\n) {\n\tif (result.kind !== \"completed\" || result.outcome === \"cancelled\" || signal.aborted) return;\n\tconst [command, ...rest] = splitArgs(rawArgs);\n\tif (command !== \"sync\" && command !== \"pull\" && command !== \"push\") return;\n\tconst current = attention.current();\n\tif (!current) return;\n\ttry {\n\t\tconst options = parseOptions(rest);\n\t\tconst setupName = options.setup ?? (await loadConfig()).setupName;\n\t\tif (signal.aborted || attention.current() !== current) return;\n\t\tif (current.decision.setupName === setupName) attention.clear(ctx);\n\t} catch {\n\t\t// Attention reconciliation below owns malformed or concurrently changed settings.\n\t}\n}\n\nasync function reconcileSelectionAttention(\n\tctx: ExtensionContext,\n\tattention: SyncAttentionController,\n\tsignal: AbortSignal,\n) {\n\tconst current = attention.current();\n\tif (!current || signal.aborted) return;\n\ttry {\n\t\tconst config = await loadConfig(current.decision.setupName);\n\t\tif (signal.aborted || attention.current() !== current) return;\n\t\tif (!syncAttentionMatchesConfig(current, config)) attention.clear(ctx);\n\t} catch {\n\t\tif (!signal.aborted && attention.current() === current) attention.clear(ctx);\n\t}\n}\n\nfunction directSelectionOrigin(rawArgs: string): SyncAttentionOrigin | undefined {\n\tconst command = splitArgs(rawArgs)[0];\n\treturn command === \"sync\" || command === \"pull\" || command === \"push\" ? command : undefined;\n}\n\nexport async function resolveSelectionAttention(\n\tctx: ExtensionContext,\n\tattention: SyncAttentionController,\n\tsignal: AbortSignal,\n\tloaders: SyncLoaders,\n\toptions: {\n\t\tcancelLabel?: string;\n\t\twithStateAccess?: <T>(task: () => Promise<T>) => Promise<T>;\n\t} = {},\n) {\n\tconst current = attention.current();\n\tif (!current || signal.aborted) return;\n\tconst { dispatchManagerResult } = await import(\"../ui/manager-result-dispatcher.js\");\n\tif (signal.aborted || attention.current() !== current) return;\n\tawait dispatchManagerResult(\n\t\tctx,\n\t\t{ kind: \"remote-selection-required\", decision: current.decision },\n\t\tcurrent.origin,\n\t\t(route, actionSignal, onCommit, target) => {\n\t\t\tconst execute = () =>\n\t\t\t\texecuteRecoveryCommand(\n\t\t\t\t\troute,\n\t\t\t\t\tctx,\n\t\t\t\t\tcombineSignals(signal, actionSignal),\n\t\t\t\t\tloaders,\n\t\t\t\t\tobservationCommitCallback(attention, signal, onCommit),\n\t\t\t\t\ttarget,\n\t\t\t\t);\n\t\t\treturn options.withStateAccess ? options.withStateAccess(execute) : execute();\n\t\t},\n\t\tsignal,\n\t\t{\n\t\t\tcancelLabel: options.cancelLabel,\n\t\t\twithStateAccess: options.withStateAccess,\n\t\t\tonSelectionResolved: () => {\n\t\t\t\tif (attention.current() === current) attention.clear(ctx);\n\t\t\t},\n\t\t},\n\t);\n}\n", "export function isMissingConfigError(error: unknown) {\n\treturn (\n\t\terror instanceof Error &&\n\t\t(error.message.startsWith(\"Missing pi-sync settings.\") ||\n\t\t\terror.message === \"No sync setups are configured.\")\n\t);\n}\n", "import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { setSyncSetupCompletions } from \"../commands/command.js\";\nimport type { CommandOptions } from \"../commands/command-types.js\";\nimport { loadConfig, loadPartialConfig } from \"../settings/config.js\";\nimport { isMissingConfigError } from \"../settings/config-errors.js\";\nimport { consumeLocalConfigMigrationNotice } from \"../settings/config-file.js\";\nimport { configuredSyncSetupNames } from \"../settings/settings-store.js\";\nimport { snapshotOptionsForContext } from \"../snapshot/session-paths.js\";\nimport { recoverSnapshotTransactionsOnStartup } from \"../snapshot/snapshot-transaction.js\";\nimport { withLock } from \"../state/lock.js\";\nimport { stateDirectoryMigrationNotice } from \"../state/state-directory.js\";\nimport { ensureStateDir, readStateForConfig } from \"../state/sync-state-store.js\";\nimport { safeTerminalText } from \"../ui/terminal-text.js\";\nimport { throwIfAborted } from \"./signals.js\";\nimport { errorMessage } from \"./sync-errors.js\";\nimport type { SyncLoaders } from \"./sync-loaders.js\";\n\nconst AUTO_SYNC_OPTIONS: CommandOptions = {\n\tyes: true,\n\tforce: false,\n\tstale: false,\n\tsilent: true,\n\treload: false,\n\tauto: true,\n\targs: [],\n};\n\nconst STATUS_KEY = \"sync\";\n\nexport async function startSession(ctx: ExtensionContext, signal: AbortSignal) {\n\tthrowIfAborted(signal);\n\tconst stateNotice = stateDirectoryMigrationNotice();\n\tif (stateNotice && ctx.hasUI) ctx.ui.notify(stateNotice, \"warning\");\n\t// Recovery can restore managed files: finish it before Pi accepts user edits.\n\t// Unlike remote inspection, it must never be detached behind startup.\n\tawait recoverSnapshotTransactionsOnStartup();\n\tthrowIfAborted(signal);\n\ttry {\n\t\tconst names = await configuredSyncSetupNames();\n\t\tthrowIfAborted(signal);\n\t\tsetSyncSetupCompletions(names);\n\t} catch {\n\t\tif (signal.aborted) return;\n\t\tsetSyncSetupCompletions([]);\n\t}\n\tconst migrationNotice = consumeLocalConfigMigrationNotice();\n\tif (migrationNotice && ctx.hasUI) ctx.ui.notify(migrationNotice, \"warning\");\n\tthrowIfAborted(signal);\n\tif (!ctx.hasUI) return;\n\ttry {\n\t\tconst config = await loadConfig();\n\t\tthrowIfAborted(signal);\n\t\treturn config.automatic ? config : undefined;\n\t} catch (error) {\n\t\tthrowIfAborted(signal);\n\t\tif (!isMissingConfigError(error)) {\n\t\t\tctx.ui.notify(\n\t\t\t\t`pi-sync startup check skipped: ${safeTerminalText(errorMessage(error))}`,\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport async function autoPushSessions(\n\tctx: ExtensionContext,\n\tsignal: AbortSignal,\n\tloaders: SyncLoaders,\n) {\n\ttry {\n\t\tconst partial = await loadPartialConfig();\n\t\tthrowIfAborted(signal);\n\t\tif (!partial.automatic) return;\n\t\tif (!partial.include.includes(\"sessions\")) return;\n\t\tawait ensureStateDir();\n\t\tthrowIfAborted(signal);\n\t\tconst config = await loadConfig();\n\t\tthrowIfAborted(signal);\n\t\tif (!config.include.includes(\"sessions\")) return;\n\t\tconst [operations, snapshotModule, syncStateModule] = await Promise.all([\n\t\t\tloaders.operations(),\n\t\t\tloaders.snapshot(),\n\t\t\tloaders.syncState(),\n\t\t]);\n\t\tthrowIfAborted(signal);\n\t\tawait withLock(\"auto-session-push\", async () => {\n\t\t\tthrowIfAborted(signal);\n\t\t\tconst state = await readStateForConfig(config);\n\t\t\tthrowIfAborted(signal);\n\t\t\tconst local = await snapshotModule.createSnapshot(config.snapshotIdentity, {\n\t\t\t\t...snapshotOptionsForContext(ctx, config),\n\t\t\t\tsignal,\n\t\t\t});\n\t\t\tthrowIfAborted(signal);\n\t\t\tif (!syncStateModule.hasLocalChanges(local, state, config)) return;\n\t\t\tawait operations.push(ctx, { ...AUTO_SYNC_OPTIONS, signal }, { config, state, local });\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted || isMissingConfigError(error)) return;\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tctx.ui.notify(`pi-sync session push skipped: ${errorMessage(error)}`, \"warning\");\n\t}\n}\n", "import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { loadConfigForCheck, syncCheckConfigFingerprint } from \"../settings/config.js\";\nimport { isMissingConfigError } from \"../settings/config-errors.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { snapshotOptionsForContext } from \"../snapshot/session-paths.js\";\nimport { withLock } from \"../state/lock.js\";\nimport { withStateDirectoryAccess } from \"../state/state-directory.js\";\nimport { readStateForConfig, syncStateFingerprint } from \"../state/sync-state-store.js\";\nimport type { SyncAttentionController } from \"../ui/sync-attention.js\";\nimport { safeTerminalText } from \"../ui/terminal-text.js\";\nimport { combineSignals, throwIfAborted } from \"./signals.js\";\nimport { errorMessage } from \"./sync-errors.js\";\nimport type { SyncLoaders } from \"./sync-loaders.js\";\n\nconst STATUS_KEY = \"sync\";\n\n/** One owned task per extension session, never keyed by the shared headless UI. */\nexport function createStartupCheck(\n\tloaders: SyncLoaders,\n\tattention: SyncAttentionController,\n\ttimeoutMs = 30_000,\n) {\n\tlet active:\n\t\t| {\n\t\t\t\tcontroller: AbortController;\n\t\t\t\tpromise: Promise<void>;\n\t\t\t\towner: ExtensionContext[\"sessionManager\"];\n\t\t }\n\t\t| undefined;\n\n\treturn {\n\t\tstart(ctx: ExtensionContext, sessionSignal: AbortSignal, initialConfig?: AnySyncConfig) {\n\t\t\tif (!ctx.hasUI || sessionSignal.aborted) return;\n\t\t\tif (active) throw new Error(\"Previous startup check has not been drained.\");\n\t\t\tconst controller = new AbortController();\n\t\t\tconst signal = combineSignals(sessionSignal, controller.signal);\n\t\t\tconst task = { controller, owner: ctx.sessionManager, promise: Promise.resolve() };\n\t\t\tactive = task;\n\t\t\tconst isCurrent = () =>\n\t\t\t\t!sessionSignal.aborted && active === task && task.owner === ctx.sessionManager;\n\t\t\tconst timer = setTimeout(\n\t\t\t\t() => controller.abort(new DOMException(\"Startup check timed out\", \"TimeoutError\")),\n\t\t\t\ttimeoutMs,\n\t\t\t);\n\t\t\tlet config: AnySyncConfig | undefined;\n\t\t\tlet checking = false;\n\t\t\t// Handle every detached rejection here; Pi's awaited event runner cannot catch it.\n\t\t\ttask.promise = Promise.resolve()\n\t\t\t\t.then(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\tconfig = initialConfig ?? (await loadConfigForCheck());\n\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\tif (!config.automatic) return;\n\t\t\t\t\t\tconst captured = config;\n\t\t\t\t\t\tconst identity = syncCheckConfigFingerprint(captured);\n\t\t\t\t\t\tchecking = true;\n\t\t\t\t\t\tctx.ui.setStatus(STATUS_KEY, \"checking in background\");\n\t\t\t\t\t\tconst { inspectSync } = await loaders.inspection();\n\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\tconst currentConfig = await loadConfigForCheck();\n\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\tif (syncCheckConfigFingerprint(currentConfig) !== identity) return;\n\t\t\t\t\t\t// Lock order: state migration guard \u2192 sync operation \u2192 backend cache.\n\t\t\t\t\t\t// Read heads may write only private cache data. Never call doctor/syncBoth.\n\t\t\t\t\t\tconst inspection = await withStateDirectoryAccess(() => {\n\t\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\t\treturn withLock(\"startup-check\", () => {\n\t\t\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\t\t\treturn inspectSync(captured, snapshotOptionsForContext(ctx, captured), signal);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\tconst latest = await loadConfigForCheck();\n\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\tif (syncCheckConfigFingerprint(latest) !== identity) return;\n\t\t\t\t\t\tconst state = await withStateDirectoryAccess(() => readStateForConfig(latest));\n\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\tif (syncStateFingerprint(state) !== inspection.stateIdentity || !isCurrent()) return;\n\t\t\t\t\t\tattention.observe({\n\t\t\t\t\t\t\tsetupName: captured.setupName,\n\t\t\t\t\t\t\tconfigIdentity: identity,\n\t\t\t\t\t\t\tcheckedAt: new Date().toISOString(),\n\t\t\t\t\t\t\tinspection,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (ctx.mode === \"rpc\") attention.notifyObservation(ctx);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (!isCurrent() || sessionSignal.aborted || isMissingConfigError(error)) return;\n\t\t\t\t\t\tif (signal.aborted && controller.signal.reason?.name !== \"TimeoutError\") return;\n\t\t\t\t\t\t// A settings edit invalidates failures as well as successful observations.\n\t\t\t\t\t\tif (config) {\n\t\t\t\t\t\t\tconst latest = await loadConfigForCheck().catch(() => undefined);\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!isCurrent() ||\n\t\t\t\t\t\t\t\t(signal.aborted && controller.signal.reason?.name !== \"TimeoutError\")\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!latest ||\n\t\t\t\t\t\t\t\tsyncCheckConfigFingerprint(latest) !== syncCheckConfigFingerprint(config)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\t`pi-sync startup check skipped: ${safeTerminalText(errorMessage(error))}. Run /sync status to retry. No startup transfer was performed.`,\n\t\t\t\t\t\t\t\"warning\",\n\t\t\t\t\t\t);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\t\tif (isCurrent() && checking) attention.publish(ctx);\n\t\t\t\t\t\tif (active === task) active = undefined;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {\n\t\t\t\t\t// UI teardown can itself throw. No unhandled rejection or retained timer.\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tif (active === task) active = undefined;\n\t\t\t\t});\n\t\t},\n\t\tasync stop() {\n\t\t\tconst task = active;\n\t\t\tif (!task) return;\n\t\t\ttask.controller.abort(new DOMException(\"Startup check cancelled\", \"AbortError\"));\n\t\t\t// Drain the actual work, including child/ref cleanup, before foreground guards.\n\t\t\tawait task.promise;\n\t\t},\n\t};\n}\n", "type SetupSwitchModule = Pick<typeof import(\"./setup-switch.js\"), \"useSyncSetup\">;\ntype SnapshotModule = Pick<typeof import(\"../snapshot/snapshot.js\"), \"createSnapshot\">;\ntype SyncStateModule = Pick<typeof import(\"./sync-state.js\"), \"hasLocalChanges\">;\ntype SyncOperations = typeof import(\"./sync-operations.js\");\n\ntype SyncInspectionModule = Pick<typeof import(\"./sync-inspection.js\"), \"inspectSync\">;\n\nexport interface SyncDependencies {\n\tloadSyncInspection(): Promise<SyncInspectionModule>;\n\tloadSetupSwitch(): Promise<SetupSwitchModule>;\n\tloadSnapshot(): Promise<SnapshotModule>;\n\tloadSyncState(): Promise<SyncStateModule>;\n\tloadSyncOperations(): Promise<SyncOperations>;\n}\n\nexport interface SyncLoaders {\n\tinspection(): Promise<SyncInspectionModule>;\n\tsetupSwitch(): Promise<SetupSwitchModule>;\n\tsnapshot(): Promise<SnapshotModule>;\n\tsyncState(): Promise<SyncStateModule>;\n\toperations(): Promise<SyncOperations>;\n}\n\nexport function createSyncLoaders(dependencies: Partial<SyncDependencies>): SyncLoaders {\n\tconst loaders: SyncLoaders = {\n\t\tinspection: cachedModuleLoader(\n\t\t\tdependencies.loadSyncInspection ?? (() => import(\"./sync-inspection.js\")),\n\t\t),\n\t\tsetupSwitch: cachedModuleLoader(\n\t\t\tdependencies.loadSetupSwitch ?? (() => import(\"./setup-switch.js\")),\n\t\t),\n\t\tsnapshot: cachedModuleLoader(\n\t\t\tdependencies.loadSnapshot ?? (() => import(\"../snapshot/snapshot.js\")),\n\t\t),\n\t\tsyncState: cachedModuleLoader(dependencies.loadSyncState ?? (() => import(\"./sync-state.js\"))),\n\t\toperations: cachedModuleLoader(\n\t\t\tdependencies.loadSyncOperations ?? (() => import(\"./sync-operations.js\")),\n\t\t),\n\t};\n\treturn loaders;\n}\n\nfunction cachedModuleLoader<Module>(load: () => Promise<Module>): () => Promise<Module> {\n\tlet pending: Promise<Module> | undefined;\n\treturn () => {\n\t\tif (!pending) {\n\t\t\tpending = load().catch((error) => {\n\t\t\t\tpending = undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t}\n\t\treturn pending;\n\t};\n}\n", "import type { ExtensionAPI } from \"@earendil-works/pi-coding-agent\";\nimport { completeSyncArguments, splitArgs } from \"./commands/command.js\";\nimport { handleCommand } from \"./commands/command-handler.js\";\nimport type { AnySyncConfig } from \"./settings/settings-types.js\";\nimport { withStateDirectoryAccess } from \"./state/state-directory.js\";\nimport { autoPushSessions, startSession } from \"./sync/automatic-sync.js\";\nimport { combineSignals } from \"./sync/signals.js\";\nimport { createStartupCheck } from \"./sync/startup-check.js\";\nimport { errorMessage } from \"./sync/sync-errors.js\";\nimport { createSyncLoaders, type SyncDependencies } from \"./sync/sync-loaders.js\";\nimport { createSyncAttentionController } from \"./ui/sync-attention.js\";\nimport { safeTerminalText } from \"./ui/terminal-text.js\";\n\nconst STATUS_KEY = \"sync\";\n\nexport default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDependencies> = {}) {\n\tconst loaders = createSyncLoaders(dependencies);\n\tconst attention = createSyncAttentionController();\n\tconst check = createStartupCheck(loaders, attention);\n\tlet sessionAbort = new AbortController();\n\tlet shutdownAbort: AbortController | undefined;\n\tlet initialization = Promise.resolve<AnySyncConfig | false | undefined>(undefined);\n\n\tpi.registerCommand(\"sync\", {\n\t\tdescription: \"Sync Pi settings through Git, WebDAV, R2, or S3-compatible storage\",\n\t\tgetArgumentCompletions: completeSyncArguments,\n\t\thandler: async (args, ctx) => {\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"/sync requires TUI or RPC mode so results and safety prompts are observable.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst signal = sessionAbort.signal;\n\t\t\tconst ready = await initialization;\n\t\t\tif (signal.aborted) return;\n\t\t\tawait check.stop();\n\t\t\tif (signal.aborted) return;\n\t\t\tconst command = splitArgs(args)[0];\n\t\t\tif (\n\t\t\t\tready === false &&\n\t\t\t\tcommand !== \"help\" &&\n\t\t\t\tcommand !== \"unlock\" &&\n\t\t\t\tcommand !== \"migrate-state\"\n\t\t\t) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\"pi-sync recovery required. Repair the startup error, then /reload before syncing. Use /sync help for recovery guidance.\",\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst run = () => handleCommand(args, ctx, signal, loaders, attention);\n\t\t\t// Drain background cache/child cleanup before entering any foreground guard.\n\t\t\t// Help must remain reachable when the state roots themselves need repair.\n\t\t\tif (command === \"migrate-state\" || command === \"help\") await run();\n\t\t\telse await withStateDirectoryAccess(run);\n\t\t},\n\t});\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tshutdownAbort?.abort(new DOMException(\"Session replaced\", \"AbortError\"));\n\t\tshutdownAbort = undefined;\n\t\tsessionAbort.abort(new DOMException(\"Session replaced\", \"AbortError\"));\n\t\tsessionAbort = new AbortController();\n\t\tconst signal = sessionAbort.signal;\n\t\tattention.reset(ctx);\n\t\tconst previous = initialization;\n\t\tinitialization = (async () => {\n\t\t\tawait previous;\n\t\t\tif (signal.aborted) return false;\n\t\t\tawait check.stop();\n\t\t\tif (signal.aborted) return false;\n\t\t\ttry {\n\t\t\t\tconst config = await withStateDirectoryAccess(() => startSession(ctx, signal));\n\t\t\t\treturn signal.aborted ? false : config;\n\t\t\t} catch (error) {\n\t\t\t\tif (!signal.aborted && ctx.hasUI) {\n\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t`pi-sync recovery required: ${safeTerminalText(errorMessage(error))}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t})();\n\t\tconst ready = await initialization;\n\t\tif (ready && !signal.aborted) check.start(ctx, signal, ready);\n\t});\n\n\tpi.on(\"session_shutdown\", async (event, ctx) => {\n\t\tsessionAbort.abort(new DOMException(\"Session shut down\", \"AbortError\"));\n\t\tattention.reset(ctx);\n\t\tshutdownAbort?.abort(new DOMException(\"Session shut down again\", \"AbortError\"));\n\t\tconst controller = new AbortController();\n\t\tshutdownAbort = controller;\n\t\tconst signal = combineSignals(controller.signal, AbortSignal.timeout(30_000));\n\t\tconst reason =\n\t\t\ttypeof event === \"object\" && event ? (event as { reason?: string }).reason : undefined;\n\t\ttry {\n\t\t\tconst ready = await initialization;\n\t\t\tif (signal.aborted) return;\n\t\t\tawait check.stop();\n\t\t\tif (signal.aborted) return;\n\t\t\tif (ready !== false && reason !== \"reload\") {\n\t\t\t\tawait withStateDirectoryAccess(async () => {\n\t\t\t\t\tif (signal.aborted) return;\n\t\t\t\t\tawait autoPushSessions(ctx, signal, loaders);\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (!signal.aborted) {\n\t\t\t\tctx.ui.notify(`pi-sync session push skipped: ${errorMessage(error)}`, \"warning\");\n\t\t\t}\n\t\t} finally {\n\t\t\tif (shutdownAbort === controller) shutdownAbort = undefined;\n\t\t}\n\t\tif (signal.aborted) return;\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t});\n}\n\nexport type { SyncDependencies } from \"./sync/sync-loaders.js\";\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["STATUS_KEY", "STATUS_KEY", "STATUS_KEY", "STATUS_KEY"]
|
|
7
7
|
}
|
package/docs/settings.md
CHANGED
|
@@ -108,13 +108,33 @@ Temporary S3 credentials may additionally include `credentials.sessionToken`.
|
|
|
108
108
|
- **WebDAV connection:** `type`, HTTPS `url`, and `credentials.username` / `credentials.password`.
|
|
109
109
|
- **WebDAV setup storage:** `connection` and complete relative `path`; `bucket` and `branch` are rejected.
|
|
110
110
|
|
|
111
|
-
Every
|
|
111
|
+
Every backend accepts `.` or `./` for its root; both resolve to `./` and share the same local state and backend identity.
|
|
112
|
+
New setups default to `./`, independently of the setup name; Git also suggests branch `main`.
|
|
113
|
+
For WebDAV, root means directly under the configured collection URL, not the server root.
|
|
114
|
+
For R2/S3, it means the selected bucket root, with no literal `./` object-key prefix.
|
|
115
|
+
WebDAV and R2/S3 write `latest.json`, `history.json`, and `snapshots/` there; use separate relative folders or prefixes for independent setups sharing storage.
|
|
116
|
+
Existing settings, paths, and local state identities remain unchanged. Editing a setup defaults to its current path.
|
|
117
|
+
Manually written documents still require an explicit non-empty `storage.path`; use `./` rather than an empty string for root.
|
|
118
|
+
Git branches are exclusively managed by pi-sync; an existing branch containing unrelated files is rejected rather than overwritten.
|
|
119
|
+
When adding a setup, locally configured destinations are checked before content selection and review, including equivalent connection aliases.
|
|
120
|
+
An occupied destination requires an explicit different path; Git requires a different branch because directories do not isolate publications on one branch.
|
|
121
|
+
This check does not contact the server or discover setups configured only on other machines.
|
|
122
|
+
|
|
123
|
+
Every setup requires `sync.include` and explicit `sync.automatic`. Setup asks for automatic sync with Off first; existing values are not rewritten. When enabled, the current setup receives one background startup check in TUI/RPC, including reload/new/resume/fork. Startup no longer performs automatic transfers or opens dialogs; review changes through `/sync`. Print/JSON skip startup checks. Checks compare local hashes and remote metadata against the last sync baseline, so revision changes and missing baselines require review rather than proving a file conflict.
|
|
124
|
+
|
|
125
|
+
The setting still permits automatic shutdown pushes of selected content when `sessions` is included, in all modes; this is not limited to uploading session files. Reload skips shutdown push. Settings changes apply to subsequent operations; changing this setting does not schedule another startup check until the next session start. Foreground `/sync` cancels a pending check before opening Settings. The check deadline is 30 seconds plus bounded cleanup, with manual retry through `/sync status`; there is no polling. See [Background startup checks](../README.md#background-startup-checks) for recovery and Git cache exceptions.
|
|
126
|
+
|
|
127
|
+
Recommended includes the nine non-session Pi roots listed below; Minimal includes `settings.json` and `AGENTS.md`. The save review lists every selected path and the full backend destination. R2/S3 requires an explicitly entered existing bucket and never creates one. Saving setup or connection settings does not contact remote storage.
|
|
128
|
+
|
|
129
|
+
Invalid fields retry without discarding earlier answers. Retryable local I/O failures retain the exact draft for another Save. Stale reviews and invalid settings files require reopening or repairing current settings rather than overwriting them. A separately saved connection remains if the surrounding Add setup flow is cancelled.
|
|
130
|
+
|
|
131
|
+
**Edit storage location…** changes only bucket, branch, or path. Content and automatic sync belong to the current setup's **Settings** screen; editing another setup's coordinates does not make it current.
|
|
112
132
|
`activeSyncSetup` must reference an own-property setup when any setups exist and must be absent when the setup catalog is empty.
|
|
113
133
|
A referenced connection cannot be removed.
|
|
114
|
-
The current setup must be switched before removal.
|
|
134
|
+
The current setup must be switched before removal when other setups exist. Removing the last setup is allowed; local removal never deletes remote history.
|
|
115
135
|
Two setups cannot resolve to the same normalized backend location.
|
|
116
136
|
|
|
117
|
-
`onSwitch` accepts:
|
|
137
|
+
The global **After switching setup (all setups)** setting persists as `onSwitch` and applies whenever any setup is made current. It accepts:
|
|
118
138
|
|
|
119
139
|
- `ask-before-pull` — switch, then ask in TUI whether to start a reviewed pull;
|
|
120
140
|
- `pull-after-switch` — require observable UI and start the normal reviewed pull;
|
|
@@ -123,11 +143,15 @@ Two setups cannot resolve to the same normalized backend location.
|
|
|
123
143
|
### Secret scanning
|
|
124
144
|
|
|
125
145
|
The global `skipSecretScan` setting accepts a boolean and defaults to `false`, including when omitted from an existing version 3 document.
|
|
126
|
-
Set it through **/sync → Settings → Skip secret scan**; changes are saved immediately and apply to subsequent pushes for every sync setup, including automatic pushes.
|
|
146
|
+
Set it through **/sync → Settings → Skip secret scan (all setups)**; changes are saved immediately and apply to subsequent pushes for every sync setup, including automatic pushes.
|
|
127
147
|
When `true`, pushes skip the local secret scan; other safety checks and confirmations remain unchanged.
|
|
128
148
|
Enable it only after reviewing the destination and selected content because files containing secrets can be uploaded.
|
|
129
149
|
`/sync doctor` still scans and reports possible secrets regardless of this setting.
|
|
130
150
|
|
|
151
|
+
### Check setup
|
|
152
|
+
|
|
153
|
+
**More… → Check setup** and `/sync doctor` retain the same route. S3/R2 reads the selected setup's latest pointer with a ten-second deadline and bounded error details; it never writes, deletes, lists buckets, or changes settings. HTTP success does not validate snapshot contents or write access. `NoSuchKey` indicates no snapshot at the path; `NoSuchBucket` indicates a missing bucket; other 404 responses cannot distinguish the two. Authentication and transport failures give credentials/permissions or address/network guidance. Git reads remote snapshots and checks its local cache; write access is not tested. WebDAV retains its isolated conditional-write/cleanup probe and repair of a missing active-snapshot history entry.
|
|
154
|
+
|
|
131
155
|
### Included content
|
|
132
156
|
|
|
133
157
|
`sync.include` is ordered and duplicate-free.
|
|
@@ -159,7 +183,7 @@ The saved state changes only `sync.include`, preserves unknown settings fields,
|
|
|
159
183
|
Its explicit Continue action starts a fresh **Sync now** route, while **Done** leaves the reviewed settings change saved without implying a file operation.
|
|
160
184
|
Keeping this device's list opens the reviewed force-push path and explicitly replaces the remote policy while preserving eligible unmanaged remote files.
|
|
161
185
|
|
|
162
|
-
|
|
186
|
+
Startup checks report an explicit remote-policy difference without applying it. Pull, including forced pull, pauses on that difference rather than silently expanding local scope.
|
|
163
187
|
Status reports matches and exact local-only/remote-only paths.
|
|
164
188
|
Old snapshots remain readable.
|
|
165
189
|
Because they have no authoritative selection, pi-sync offers only a clearly labeled read-only partial discovery from safe remote file roots; selected-but-missing and preserved-unmanaged intent cannot be reconstructed.
|
|
@@ -167,7 +191,7 @@ Use **Add custom path…** for any needed path.
|
|
|
167
191
|
|
|
168
192
|
Adding `sessions` requires a privacy acknowledgement in interactive flows.
|
|
169
193
|
Session JSONL can contain prompts, tool output, file paths, images, and secrets.
|
|
170
|
-
|
|
194
|
+
Pull protects the currently open session file; restart Pi or resume a pulled session to use newly synchronized conversations.
|
|
171
195
|
|
|
172
196
|
### Unsupported old settings and recovery
|
|
173
197
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { AnySyncConfig } from "../settings/settings-types.js";
|
|
1
2
|
import type { SyncBackend } from "./sync-backend.js";
|
|
2
|
-
import type { AnySyncConfig } from "./types.js";
|
|
3
3
|
|
|
4
4
|
export type SyncBackendFactory = {
|
|
5
5
|
bivarianceHack(config: AnySyncConfig): SyncBackend | Promise<SyncBackend>;
|
|
@@ -8,15 +8,15 @@ export type SyncBackendFactory = {
|
|
|
8
8
|
export const createSyncBackend: SyncBackendFactory = async (config) => {
|
|
9
9
|
switch (config.backend.type) {
|
|
10
10
|
case "s3": {
|
|
11
|
-
const { S3SyncBackend } = await import("./s3-backend.js");
|
|
11
|
+
const { S3SyncBackend } = await import("./s3/s3-backend.js");
|
|
12
12
|
return new S3SyncBackend(config.backend);
|
|
13
13
|
}
|
|
14
14
|
case "webdav": {
|
|
15
|
-
const { WebDavSyncBackend } = await import("./webdav-backend.js");
|
|
15
|
+
const { WebDavSyncBackend } = await import("./webdav/webdav-backend.js");
|
|
16
16
|
return new WebDavSyncBackend(config.backend);
|
|
17
17
|
}
|
|
18
18
|
case "git": {
|
|
19
|
-
const { GitSyncBackend } = await import("./git-backend.js");
|
|
19
|
+
const { GitSyncBackend } = await import("./git/git-backend.js");
|
|
20
20
|
return new GitSyncBackend(config.backend);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AnySyncConfig } from "../settings/settings-types.js";
|
|
2
|
+
import { normalizeGitRemoteIdentity } from "./git/git-config.js";
|
|
3
|
+
import { normalizeWebDavIdentityUrl } from "./webdav/webdav-config.js";
|
|
4
|
+
|
|
5
|
+
export function backendIdentityCoordinates(config: AnySyncConfig) {
|
|
6
|
+
switch (config.backend.type) {
|
|
7
|
+
case "s3":
|
|
8
|
+
return JSON.stringify([
|
|
9
|
+
"s3",
|
|
10
|
+
normalizeEndpointIdentity(config.backend.profile.endpoint),
|
|
11
|
+
config.backend.destination.bucket,
|
|
12
|
+
config.storagePath,
|
|
13
|
+
]);
|
|
14
|
+
case "git":
|
|
15
|
+
return JSON.stringify([
|
|
16
|
+
"git",
|
|
17
|
+
normalizeGitRemoteIdentity(config.backend.profile.remote),
|
|
18
|
+
config.backend.destination.branch,
|
|
19
|
+
config.storagePath,
|
|
20
|
+
]);
|
|
21
|
+
case "webdav":
|
|
22
|
+
return JSON.stringify([
|
|
23
|
+
"webdav",
|
|
24
|
+
normalizeWebDavIdentityUrl(config.backend.profile.url),
|
|
25
|
+
config.backend.profile.username,
|
|
26
|
+
config.storagePath,
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function normalizeEndpointIdentity(endpoint: string) {
|
|
32
|
+
try {
|
|
33
|
+
const url = new URL(endpoint.trim());
|
|
34
|
+
url.hostname = url.hostname.toLowerCase();
|
|
35
|
+
url.pathname = url.pathname.replace(/\/+$/gu, "");
|
|
36
|
+
return url.toString().replace(/\/$/u, "");
|
|
37
|
+
} catch {
|
|
38
|
+
return endpoint.trim();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { SnapshotSelection } from "../snapshot/snapshot-types.js";
|
|
2
|
+
/** Backend-only resolved S3 connection fields. */
|
|
3
|
+
export interface ResolvedS3StorageProfile {
|
|
4
|
+
kind: "r2" | "s3-compatible";
|
|
5
|
+
endpoint: string;
|
|
6
|
+
region: string;
|
|
7
|
+
accessKeyId: string;
|
|
8
|
+
secretAccessKey: string;
|
|
9
|
+
sessionToken?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Backend-only coordinates. `prefix` is the complete reviewed v3 storage path. */
|
|
13
|
+
export interface ResolvedS3Destination {
|
|
14
|
+
bucket: string;
|
|
15
|
+
prefix: string;
|
|
16
|
+
/** Snapshot/wire identity; never derived from the local setup name. */
|
|
17
|
+
namespace: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ResolvedS3Backend {
|
|
21
|
+
type: "s3";
|
|
22
|
+
profile: ResolvedS3StorageProfile;
|
|
23
|
+
destination: ResolvedS3Destination;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ResolvedWebDavStorageProfile {
|
|
27
|
+
kind: "webdav";
|
|
28
|
+
url: string;
|
|
29
|
+
username: string;
|
|
30
|
+
password: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Backend-only coordinates. `path` is the complete reviewed v3 storage path. */
|
|
34
|
+
export interface ResolvedWebDavDestination {
|
|
35
|
+
path: string;
|
|
36
|
+
namespace: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ResolvedWebDavBackend {
|
|
40
|
+
type: "webdav";
|
|
41
|
+
profile: ResolvedWebDavStorageProfile;
|
|
42
|
+
destination: ResolvedWebDavDestination;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ResolvedGitStorageProfile {
|
|
46
|
+
kind: "git";
|
|
47
|
+
remote: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Backend-only coordinates. `directory` is the complete reviewed v3 storage path. */
|
|
51
|
+
export interface ResolvedGitDestination {
|
|
52
|
+
branch: string;
|
|
53
|
+
directory: string;
|
|
54
|
+
namespace: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ResolvedGitBackend {
|
|
58
|
+
type: "git";
|
|
59
|
+
profile: ResolvedGitStorageProfile;
|
|
60
|
+
destination: ResolvedGitDestination;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type ResolvedSyncBackend = ResolvedS3Backend | ResolvedWebDavBackend | ResolvedGitBackend;
|
|
64
|
+
|
|
65
|
+
export interface LatestPointer {
|
|
66
|
+
version: number;
|
|
67
|
+
profile: string;
|
|
68
|
+
snapshot: string;
|
|
69
|
+
sha256: string;
|
|
70
|
+
createdAt: string;
|
|
71
|
+
machine: string;
|
|
72
|
+
syncSessions?: boolean;
|
|
73
|
+
/** Lightweight projection; the immutable snapshot remains authoritative. */
|
|
74
|
+
selection?: SnapshotSelection;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface RemoteObject<T> {
|
|
78
|
+
value?: T;
|
|
79
|
+
etag?: string;
|
|
80
|
+
missing: boolean;
|
|
81
|
+
}
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { posixJoin } from "../../paths.js";
|
|
5
|
+
import type { Snapshot, SnapshotFile } from "../../snapshot/snapshot-types.js";
|
|
6
|
+
import { stateDir } from "../../state/state-directory.js";
|
|
7
|
+
import { syncErrorGuidance } from "../../sync/sync-error-guidance.js";
|
|
8
|
+
import type { ResolvedGitBackend } from "../backend-types.js";
|
|
9
|
+
import {
|
|
10
|
+
type BackendDiagnostic,
|
|
11
|
+
type ExpectedRemoteHead,
|
|
12
|
+
type PublishSnapshotOptions,
|
|
13
|
+
type PublishSnapshotResult,
|
|
14
|
+
type RemoteHead,
|
|
15
|
+
type RemoteHistoryEntry,
|
|
16
|
+
type SyncBackend,
|
|
17
|
+
SyncBackendConflictError,
|
|
18
|
+
SyncBackendPublicationOutcomeUnknownError,
|
|
19
|
+
} from "../sync-backend.js";
|
|
4
20
|
import {
|
|
5
21
|
normalizeGitBranch,
|
|
6
22
|
normalizeGitDirectory,
|
|
@@ -21,20 +37,6 @@ import {
|
|
|
21
37
|
validateGitPublicationTree,
|
|
22
38
|
validateGitSnapshot,
|
|
23
39
|
} from "./git-storage.js";
|
|
24
|
-
import { posixJoin } from "./paths.js";
|
|
25
|
-
import { stateDir } from "./state-directory.js";
|
|
26
|
-
import {
|
|
27
|
-
type BackendDiagnostic,
|
|
28
|
-
type ExpectedRemoteHead,
|
|
29
|
-
type PublishSnapshotOptions,
|
|
30
|
-
type PublishSnapshotResult,
|
|
31
|
-
type RemoteHead,
|
|
32
|
-
type RemoteHistoryEntry,
|
|
33
|
-
type SyncBackend,
|
|
34
|
-
SyncBackendConflictError,
|
|
35
|
-
SyncBackendPublicationOutcomeUnknownError,
|
|
36
|
-
} from "./sync-backend.js";
|
|
37
|
-
import type { ResolvedGitBackend, Snapshot, SnapshotFile } from "./types.js";
|
|
38
40
|
|
|
39
41
|
const COMMAND_TIMEOUT_MS = 30_000;
|
|
40
42
|
const POST_COMMIT_TIMEOUT_MS = 45_000;
|
|
@@ -74,7 +76,7 @@ export class GitSyncBackend implements SyncBackend {
|
|
|
74
76
|
if (!this.allowLocalRemotes) assertProductionRemote(config.profile.remote);
|
|
75
77
|
this.identity = gitBackendIdentity(config);
|
|
76
78
|
this.destination = gitDestination(config);
|
|
77
|
-
this.cacheRoot = options.cacheRoot ?? path.join(stateDir(), "git");
|
|
79
|
+
this.cacheRoot = path.resolve(options.cacheRoot ?? path.join(stateDir(), "git"));
|
|
78
80
|
this.cacheDir = path.join(this.cacheRoot, this.identity.slice("git:".length), "repository.git");
|
|
79
81
|
this.commandTimeoutMs = options.commandTimeoutMs ?? COMMAND_TIMEOUT_MS;
|
|
80
82
|
this.postCommitTimeoutMs = options.postCommitTimeoutMs ?? POST_COMMIT_TIMEOUT_MS;
|
|
@@ -268,7 +270,14 @@ export class GitSyncBackend implements SyncBackend {
|
|
|
268
270
|
}
|
|
269
271
|
|
|
270
272
|
async diagnose(signal?: AbortSignal): Promise<BackendDiagnostic[]> {
|
|
271
|
-
const diagnostics: BackendDiagnostic[] = [
|
|
273
|
+
const diagnostics: BackendDiagnostic[] = [
|
|
274
|
+
{
|
|
275
|
+
key: "git-scope",
|
|
276
|
+
level: "info",
|
|
277
|
+
message:
|
|
278
|
+
"git check scope: read remote snapshots and inspect the local cache. Write access is not tested.",
|
|
279
|
+
},
|
|
280
|
+
];
|
|
272
281
|
try {
|
|
273
282
|
const version = await runGit(["--version"], {
|
|
274
283
|
signal,
|
|
@@ -284,7 +293,13 @@ export class GitSyncBackend implements SyncBackend {
|
|
|
284
293
|
: `${versionText || "unknown Git version"}; pi-sync requires Git 2.30 or newer`,
|
|
285
294
|
});
|
|
286
295
|
} catch (error) {
|
|
287
|
-
return [
|
|
296
|
+
return [
|
|
297
|
+
{
|
|
298
|
+
key: "git-version",
|
|
299
|
+
level: "error",
|
|
300
|
+
message: syncErrorGuidance(new Error(this.safeError(error))),
|
|
301
|
+
},
|
|
302
|
+
];
|
|
288
303
|
}
|
|
289
304
|
try {
|
|
290
305
|
const head = await this.readHead(signal);
|
|
@@ -305,7 +320,7 @@ export class GitSyncBackend implements SyncBackend {
|
|
|
305
320
|
diagnostics.push({
|
|
306
321
|
key: "git-remote",
|
|
307
322
|
level: "error",
|
|
308
|
-
message: `git remote: ${this.safeError(error)}`,
|
|
323
|
+
message: `git remote: ${syncErrorGuidance(new Error(this.safeError(error)))}`,
|
|
309
324
|
});
|
|
310
325
|
}
|
|
311
326
|
return diagnostics;
|
|
@@ -547,12 +562,14 @@ export class GitSyncBackend implements SyncBackend {
|
|
|
547
562
|
try {
|
|
548
563
|
recreate = !(await this.cacheUsesSha1(signal));
|
|
549
564
|
} catch {
|
|
565
|
+
throwIfAborted(signal);
|
|
550
566
|
recreate = true;
|
|
551
567
|
}
|
|
552
568
|
}
|
|
553
569
|
} catch (error) {
|
|
554
570
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
555
571
|
}
|
|
572
|
+
throwIfAborted(signal);
|
|
556
573
|
if (recreate) await fs.rm(this.cacheDir, { recursive: true, force: true });
|
|
557
574
|
try {
|
|
558
575
|
await fs.access(this.cacheDir);
|
|
@@ -604,7 +621,7 @@ export class GitSyncBackend implements SyncBackend {
|
|
|
604
621
|
}
|
|
605
622
|
|
|
606
623
|
private publicationPath() {
|
|
607
|
-
return this.config.destination.directory;
|
|
624
|
+
return this.config.destination.directory === "./" ? "" : this.config.destination.directory;
|
|
608
625
|
}
|
|
609
626
|
|
|
610
627
|
private manifestPath() {
|
|
@@ -766,10 +783,12 @@ async function withGitCacheMutation<T>(
|
|
|
766
783
|
signal?: AbortSignal,
|
|
767
784
|
): Promise<T> {
|
|
768
785
|
const previous = gitCacheMutationQueues.get(cacheDir) ?? Promise.resolve();
|
|
786
|
+
let started = false;
|
|
769
787
|
const operation = previous
|
|
770
788
|
.catch(() => undefined)
|
|
771
789
|
.then(() => {
|
|
772
790
|
throwIfAborted(signal);
|
|
791
|
+
started = true;
|
|
773
792
|
return run();
|
|
774
793
|
});
|
|
775
794
|
const tail = operation.then(
|
|
@@ -791,6 +810,11 @@ async function withGitCacheMutation<T>(
|
|
|
791
810
|
if (signal.aborted) onAbort();
|
|
792
811
|
try {
|
|
793
812
|
return await Promise.race([operation, aborted]);
|
|
813
|
+
} catch (error) {
|
|
814
|
+
// A queued job can cancel immediately, but a running job owns a child/cache
|
|
815
|
+
// mutation. Drain its bounded cleanup before releasing outer sync guards.
|
|
816
|
+
if (started) await tail;
|
|
817
|
+
throw error;
|
|
794
818
|
} finally {
|
|
795
819
|
signal.removeEventListener("abort", onAbort);
|
|
796
820
|
}
|
|
@@ -88,6 +88,7 @@ export function normalizeGitBranch(value: string | undefined) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export function normalizeGitDirectory(value: string | undefined) {
|
|
91
|
+
if (value?.trim() === "." || value?.trim() === "./") return "./";
|
|
91
92
|
const directory = trimSlashes(normalizeOptionalString(value) ?? DEFAULT_GIT_DIRECTORY);
|
|
92
93
|
if (
|
|
93
94
|
!directory ||
|