@caupulican/pi-adaptative 0.81.26 → 0.81.27
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/CHANGELOG.md +31 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +3 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +4 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +19 -2
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +21 -10
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +71 -33
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
- package/dist/core/autonomy/bounded-completion.js +59 -19
- package/dist/core/autonomy/bounded-completion.js.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.d.ts +31 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -0
- package/dist/core/autonomy/lane-tool-surface.js +96 -0
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -0
- package/dist/core/background-lane-controller.d.ts +6 -3
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +134 -25
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/billing-failover-controller.d.ts.map +1 -1
- package/dist/core/billing-failover-controller.js +1 -1
- package/dist/core/billing-failover-controller.js.map +1 -1
- package/dist/core/context/current-work-memory.d.ts +9 -0
- package/dist/core/context/current-work-memory.d.ts.map +1 -0
- package/dist/core/context/current-work-memory.js +61 -0
- package/dist/core/context/current-work-memory.js.map +1 -0
- package/dist/core/context/file-store-memory-provider.d.ts +9 -0
- package/dist/core/context/file-store-memory-provider.d.ts.map +1 -0
- package/dist/core/context/file-store-memory-provider.js +106 -0
- package/dist/core/context/file-store-memory-provider.js.map +1 -0
- package/dist/core/context/long-term-memory-trigger.d.ts +15 -0
- package/dist/core/context/long-term-memory-trigger.d.ts.map +1 -0
- package/dist/core/context/long-term-memory-trigger.js +39 -0
- package/dist/core/context/long-term-memory-trigger.js.map +1 -0
- package/dist/core/context/memory-prompt-block.d.ts +3 -1
- package/dist/core/context/memory-prompt-block.d.ts.map +1 -1
- package/dist/core/context/memory-prompt-block.js +20 -3
- package/dist/core/context/memory-prompt-block.js.map +1 -1
- package/dist/core/context/memory-prompt-budget.d.ts +17 -0
- package/dist/core/context/memory-prompt-budget.d.ts.map +1 -0
- package/dist/core/context/memory-prompt-budget.js +64 -0
- package/dist/core/context/memory-prompt-budget.js.map +1 -0
- package/dist/core/context/memory-provider-contract.d.ts.map +1 -1
- package/dist/core/context/memory-provider-contract.js +2 -5
- package/dist/core/context/memory-provider-contract.js.map +1 -1
- package/dist/core/context/memory-tier-composer.d.ts +27 -0
- package/dist/core/context/memory-tier-composer.d.ts.map +1 -0
- package/dist/core/context/memory-tier-composer.js +79 -0
- package/dist/core/context/memory-tier-composer.js.map +1 -0
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +1 -0
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/cost/cost-summary.d.ts +3 -1
- package/dist/core/cost/cost-summary.d.ts.map +1 -1
- package/dist/core/cost/cost-summary.js +3 -3
- package/dist/core/cost/cost-summary.js.map +1 -1
- package/dist/core/cost-guard.d.ts +17 -7
- package/dist/core/cost-guard.d.ts.map +1 -1
- package/dist/core/cost-guard.js +33 -10
- package/dist/core/cost-guard.js.map +1 -1
- package/dist/core/default-tool-surface.d.ts +9 -0
- package/dist/core/default-tool-surface.d.ts.map +1 -0
- package/dist/core/default-tool-surface.js +18 -0
- package/dist/core/default-tool-surface.js.map +1 -0
- package/dist/core/delegation/worker-runner.d.ts +11 -5
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +24 -12
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +15 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +2 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +7 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +9 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/failure-corpus.d.ts.map +1 -1
- package/dist/core/failure-corpus.js +2 -4
- package/dist/core/failure-corpus.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +9 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.js +4 -3
- package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts +12 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +42 -4
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/memory/memory-manager.d.ts +8 -3
- package/dist/core/memory/memory-manager.d.ts.map +1 -1
- package/dist/core/memory/memory-manager.js +27 -11
- package/dist/core/memory/memory-manager.js.map +1 -1
- package/dist/core/memory/memory-provider.d.ts +5 -1
- package/dist/core/memory/memory-provider.d.ts.map +1 -1
- package/dist/core/memory/memory-provider.js.map +1 -1
- package/dist/core/memory/providers/file-store.d.ts +3 -1
- package/dist/core/memory/providers/file-store.d.ts.map +1 -1
- package/dist/core/memory/providers/file-store.js +7 -2
- package/dist/core/memory/providers/file-store.js.map +1 -1
- package/dist/core/memory/providers/transcript-recall.d.ts +1 -0
- package/dist/core/memory/providers/transcript-recall.d.ts.map +1 -1
- package/dist/core/memory/providers/transcript-recall.js +4 -7
- package/dist/core/memory/providers/transcript-recall.js.map +1 -1
- package/dist/core/memory-controller.d.ts +36 -10
- package/dist/core/memory-controller.d.ts.map +1 -1
- package/dist/core/memory-controller.js +137 -21
- package/dist/core/memory-controller.js.map +1 -1
- package/dist/core/model-registry.d.ts +23 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +87 -8
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +30 -11
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +33 -29
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/model-selection-controller.d.ts +3 -0
- package/dist/core/model-selection-controller.d.ts.map +1 -1
- package/dist/core/model-selection-controller.js +8 -1
- package/dist/core/model-selection-controller.js.map +1 -1
- package/dist/core/models/perf-profile.d.ts.map +1 -1
- package/dist/core/models/perf-profile.js +95 -33
- package/dist/core/models/perf-profile.js.map +1 -1
- package/dist/core/profile-filter-controller.d.ts +11 -0
- package/dist/core/profile-filter-controller.d.ts.map +1 -1
- package/dist/core/profile-filter-controller.js +56 -11
- package/dist/core/profile-filter-controller.js.map +1 -1
- package/dist/core/profile-registry.d.ts +2 -2
- package/dist/core/profile-registry.d.ts.map +1 -1
- package/dist/core/profile-registry.js +58 -7
- package/dist/core/profile-registry.js.map +1 -1
- package/dist/core/reflection-controller.d.ts +7 -7
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +78 -12
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/research/research-runner.d.ts +2 -2
- package/dist/core/research/research-runner.d.ts.map +1 -1
- package/dist/core/research/research-runner.js +8 -6
- package/dist/core/research/research-runner.js.map +1 -1
- package/dist/core/resource-loader.d.ts +4 -2
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +11 -1
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +13 -1
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +127 -35
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/sdk.d.ts +8 -7
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +35 -5
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/security/secret-text.d.ts +3 -0
- package/dist/core/security/secret-text.d.ts.map +1 -0
- package/dist/core/security/secret-text.js +26 -0
- package/dist/core/security/secret-text.js.map +1 -0
- package/dist/core/settings-manager.d.ts +59 -17
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +300 -51
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +1 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/system-prompt-builder.d.ts +7 -0
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/core/system-prompt-builder.js +23 -1
- package/dist/core/system-prompt-builder.js.map +1 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +15 -11
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +8 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +69 -29
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +2 -0
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/config-backup.d.ts +7 -1
- package/dist/modes/interactive/config-backup.d.ts.map +1 -1
- package/dist/modes/interactive/config-backup.js +196 -32
- package/dist/modes/interactive/config-backup.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +10 -28
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
- package/dist/modes/interactive/local-model-commands.js +2 -0
- package/dist/modes/interactive/local-model-commands.js.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.d.ts +6 -4
- package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.js +248 -65
- package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
- package/dist/modes/interactive/report-commands.d.ts.map +1 -1
- package/dist/modes/interactive/report-commands.js +8 -2
- package/dist/modes/interactive/report-commands.js.map +1 -1
- package/dist/modes/interactive/settings-selector-flow.d.ts.map +1 -1
- package/dist/modes/interactive/settings-selector-flow.js +8 -0
- package/dist/modes/interactive/settings-selector-flow.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +3 -1
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/docs/extensions.md +32 -0
- package/docs/resources.md +15 -7
- package/docs/settings.md +54 -5
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/preset.ts +2 -2
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +44 -35
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thinking-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAmB,UAAU,EAAgC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"thinking-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAmB,UAAU,EAAgC,MAAM,oBAAoB,CAAC;AAoB1G;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,OAAO,CAAC,UAAU,CAAa;IAE/B,YACC,YAAY,EAAE,aAAa,EAC3B,eAAe,EAAE,aAAa,EAAE,EAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,EACxC,QAAQ,EAAE,MAAM,IAAI,EAuCpB;IAED,aAAa,IAAI,UAAU,CAE1B;CACD","sourcesContent":["import type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { Container, type SelectItem, SelectList, type SelectListLayoutOptions } from \"@caupulican/pi-tui\";\nimport { getSelectListTheme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\nconst THINKING_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 12,\n\tmaxPrimaryColumnWidth: 32,\n};\n\nconst LEVEL_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning (~1k tokens)\",\n\tlow: \"Light reasoning (~2k tokens)\",\n\tmedium: \"Moderate reasoning (~8k tokens)\",\n\thigh: \"Deep reasoning (~16k tokens)\",\n\txhigh: \"Maximum reasoning (~32k tokens)\",\n\tmax: \"Maximum reasoning depth for the hardest problems\",\n\tultra: \"Maximum reasoning with reinforced proactive delegation\",\n};\n\n/**\n * Component that renders a thinking level selector with borders\n */\nexport class ThinkingSelectorComponent extends Container {\n\tprivate selectList: SelectList;\n\n\tconstructor(\n\t\tcurrentLevel: ThinkingLevel,\n\t\tavailableLevels: ThinkingLevel[],\n\t\tonSelect: (level: ThinkingLevel) => void,\n\t\tonCancel: () => void,\n\t) {\n\t\tsuper();\n\n\t\tconst thinkingLevels: SelectItem[] = availableLevels.map((level) => ({\n\t\t\tvalue: level,\n\t\t\tlabel: level,\n\t\t\tdescription: LEVEL_DESCRIPTIONS[level],\n\t\t}));\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Create selector\n\t\tthis.selectList = new SelectList(\n\t\t\tthinkingLevels,\n\t\t\tthinkingLevels.length,\n\t\t\tgetSelectListTheme(),\n\t\t\tTHINKING_SELECT_LIST_LAYOUT,\n\t\t);\n\n\t\t// Preselect current level\n\t\tconst currentIndex = thinkingLevels.findIndex((item) => item.value === currentLevel);\n\t\tif (currentIndex !== -1) {\n\t\t\tthis.selectList.setSelectedIndex(currentIndex);\n\t\t}\n\n\t\tthis.selectList.onSelect = (item) => {\n\t\t\tonSelect(item.value as ThinkingLevel);\n\t\t};\n\n\t\tthis.selectList.onCancel = () => {\n\t\t\tonCancel();\n\t\t};\n\n\t\tthis.addChild(this.selectList);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tgetSelectList(): SelectList {\n\t\treturn this.selectList;\n\t}\n}\n"]}
|
|
@@ -12,6 +12,8 @@ const LEVEL_DESCRIPTIONS = {
|
|
|
12
12
|
medium: "Moderate reasoning (~8k tokens)",
|
|
13
13
|
high: "Deep reasoning (~16k tokens)",
|
|
14
14
|
xhigh: "Maximum reasoning (~32k tokens)",
|
|
15
|
+
max: "Maximum reasoning depth for the hardest problems",
|
|
16
|
+
ultra: "Maximum reasoning with reinforced proactive delegation",
|
|
15
17
|
};
|
|
16
18
|
/**
|
|
17
19
|
* Component that renders a thinking level selector with borders
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thinking-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-selector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAmB,UAAU,EAAgC,MAAM,oBAAoB,CAAC;AAC1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,2BAA2B,GAA4B;IAC5D,qBAAqB,EAAE,EAAE;IACzB,qBAAqB,EAAE,EAAE;CACzB,CAAC;AAEF,MAAM,kBAAkB,GAAkC;IACzD,GAAG,EAAE,cAAc;IACnB,OAAO,EAAE,mCAAmC;IAC5C,GAAG,EAAE,8BAA8B;IACnC,MAAM,EAAE,iCAAiC;IACzC,IAAI,EAAE,8BAA8B;IACpC,KAAK,EAAE,iCAAiC;
|
|
1
|
+
{"version":3,"file":"thinking-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-selector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAmB,UAAU,EAAgC,MAAM,oBAAoB,CAAC;AAC1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,2BAA2B,GAA4B;IAC5D,qBAAqB,EAAE,EAAE;IACzB,qBAAqB,EAAE,EAAE;CACzB,CAAC;AAEF,MAAM,kBAAkB,GAAkC;IACzD,GAAG,EAAE,cAAc;IACnB,OAAO,EAAE,mCAAmC;IAC5C,GAAG,EAAE,8BAA8B;IACnC,MAAM,EAAE,iCAAiC;IACzC,IAAI,EAAE,8BAA8B;IACpC,KAAK,EAAE,iCAAiC;IACxC,GAAG,EAAE,kDAAkD;IACvD,KAAK,EAAE,wDAAwD;CAC/D,CAAC;AAEF;;GAEG;AACH,MAAM,OAAO,yBAA0B,SAAQ,SAAS;IAC/C,UAAU,CAAa;IAE/B,YACC,YAA2B,EAC3B,eAAgC,EAChC,QAAwC,EACxC,QAAoB,EACnB;QACD,KAAK,EAAE,CAAC;QAER,MAAM,cAAc,GAAiB,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACpE,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,KAAK;YACZ,WAAW,EAAE,kBAAkB,CAAC,KAAK,CAAC;SACtC,CAAC,CAAC,CAAC;QAEJ,iBAAiB;QACjB,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;QAEnC,kBAAkB;QAClB,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,CAC/B,cAAc,EACd,cAAc,CAAC,MAAM,EACrB,kBAAkB,EAAE,EACpB,2BAA2B,CAC3B,CAAC;QAEF,0BAA0B;QAC1B,MAAM,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC;QACrF,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAChD,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YACpC,QAAQ,CAAC,IAAI,CAAC,KAAsB,CAAC,CAAC;QAAA,CACtC,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC;YAChC,QAAQ,EAAE,CAAC;QAAA,CACX,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAE/B,oBAAoB;QACpB,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;IAAA,CACnC;IAED,aAAa,GAAe;QAC3B,OAAO,IAAI,CAAC,UAAU,CAAC;IAAA,CACvB;CACD","sourcesContent":["import type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { Container, type SelectItem, SelectList, type SelectListLayoutOptions } from \"@caupulican/pi-tui\";\nimport { getSelectListTheme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\nconst THINKING_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 12,\n\tmaxPrimaryColumnWidth: 32,\n};\n\nconst LEVEL_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning (~1k tokens)\",\n\tlow: \"Light reasoning (~2k tokens)\",\n\tmedium: \"Moderate reasoning (~8k tokens)\",\n\thigh: \"Deep reasoning (~16k tokens)\",\n\txhigh: \"Maximum reasoning (~32k tokens)\",\n\tmax: \"Maximum reasoning depth for the hardest problems\",\n\tultra: \"Maximum reasoning with reinforced proactive delegation\",\n};\n\n/**\n * Component that renders a thinking level selector with borders\n */\nexport class ThinkingSelectorComponent extends Container {\n\tprivate selectList: SelectList;\n\n\tconstructor(\n\t\tcurrentLevel: ThinkingLevel,\n\t\tavailableLevels: ThinkingLevel[],\n\t\tonSelect: (level: ThinkingLevel) => void,\n\t\tonCancel: () => void,\n\t) {\n\t\tsuper();\n\n\t\tconst thinkingLevels: SelectItem[] = availableLevels.map((level) => ({\n\t\t\tvalue: level,\n\t\t\tlabel: level,\n\t\t\tdescription: LEVEL_DESCRIPTIONS[level],\n\t\t}));\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Create selector\n\t\tthis.selectList = new SelectList(\n\t\t\tthinkingLevels,\n\t\t\tthinkingLevels.length,\n\t\t\tgetSelectListTheme(),\n\t\t\tTHINKING_SELECT_LIST_LAYOUT,\n\t\t);\n\n\t\t// Preselect current level\n\t\tconst currentIndex = thinkingLevels.findIndex((item) => item.value === currentLevel);\n\t\tif (currentIndex !== -1) {\n\t\t\tthis.selectList.setSelectedIndex(currentIndex);\n\t\t}\n\n\t\tthis.selectList.onSelect = (item) => {\n\t\t\tonSelect(item.value as ThinkingLevel);\n\t\t};\n\n\t\tthis.selectList.onCancel = () => {\n\t\t\tonCancel();\n\t\t};\n\n\t\tthis.addChild(this.selectList);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tgetSelectList(): SelectList {\n\t\treturn this.selectList;\n\t}\n}\n"]}
|
|
@@ -20,8 +20,14 @@ export interface ConfigRestoreHost extends ConfigBackupHost {
|
|
|
20
20
|
component: Component;
|
|
21
21
|
focus: Component;
|
|
22
22
|
}): void;
|
|
23
|
-
handleReloadCommand(): Promise<
|
|
23
|
+
handleReloadCommand(): Promise<boolean>;
|
|
24
24
|
}
|
|
25
|
+
export interface ProfileFilesSnapshot {
|
|
26
|
+
directoryExisted: boolean;
|
|
27
|
+
files: Map<string, string>;
|
|
28
|
+
}
|
|
29
|
+
export declare function captureProfileFiles(profilesDir: string): ProfileFilesSnapshot;
|
|
30
|
+
export declare function restoreProfileFiles(profilesDir: string, snapshot: ProfileFilesSnapshot): void;
|
|
25
31
|
export declare function handleConfigBackupCommand(host: ConfigBackupHost, fileArg?: string): Promise<void>;
|
|
26
32
|
export declare function handleConfigRestoreCommand(host: ConfigRestoreHost, fileArg: string): Promise<void>;
|
|
27
33
|
//# sourceMappingURL=config-backup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-backup.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/config-backup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGtE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IAC1D,YAAY,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK;QAAE,SAAS,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7F,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDvG;AAED,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkGxG","sourcesContent":["/**\n * Config backup/restore commands extracted from interactive-mode.\n *\n * `handleConfigBackupCommand` snapshots the reusable-file profiles and the\n * resource-relevant settings to a JSON bundle; `handleConfigRestoreCommand`\n * reads such a bundle back, confirming with the user before clobbering local\n * state. Both operate through a narrow `host` seam (settings manager + the\n * status/error/selector callbacks) so the file IO and validation live outside\n * the god file while interactive-mode keeps only thin delegating wrappers.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { Component } from \"@caupulican/pi-tui\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type { SettingsManager } from \"../../core/settings-manager.ts\";\nimport { SelectSubmenu } from \"./components/settings-selector.ts\";\n\nexport interface ConfigBackupHost {\n\treadonly settingsManager: SettingsManager;\n\tshowStatus(message: string): void;\n\tshowError(errorMessage: string): void;\n}\n\nexport interface ConfigRestoreHost extends ConfigBackupHost {\n\tshowSelector(create: (done: () => void) => { component: Component; focus: Component }): void;\n\thandleReloadCommand(): Promise<void>;\n}\n\nexport async function handleConfigBackupCommand(host: ConfigBackupHost, fileArg?: string): Promise<void> {\n\ttry {\n\t\tconst profilesDir = path.join(getAgentDir(), \"profiles\");\n\t\tconst profiles: Record<string, any> = {};\n\t\tif (fs.existsSync(profilesDir)) {\n\t\t\tconst entries = fs.readdirSync(profilesDir);\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.endsWith(\".json\")) {\n\t\t\t\t\tconst pPath = path.join(profilesDir, entry);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst content = fs.readFileSync(pPath, \"utf-8\");\n\t\t\t\t\t\tprofiles[entry] = JSON.parse(content);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// skip\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst backupData = {\n\t\t\tprofiles,\n\t\t\tsettings: {\n\t\t\t\tresourceProfiles: host.settingsManager.settings.resourceProfiles,\n\t\t\t\tactiveResourceProfile: host.settingsManager.settings.activeResourceProfile,\n\t\t\t\texternalResourceRoots: host.settingsManager.settings.externalResourceRoots,\n\t\t\t\ttrustedResourceRoots: host.settingsManager.settings.trustedResourceRoots,\n\t\t\t},\n\t\t};\n\n\t\tlet targetFile = fileArg;\n\t\tif (!targetFile) {\n\t\t\tconst backupsDir = path.join(getAgentDir(), \"backups\");\n\t\t\tfs.mkdirSync(backupsDir, { recursive: true });\n\t\t\tconst timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n\t\t\ttargetFile = path.join(backupsDir, `config-${timestamp}.json`);\n\t\t} else {\n\t\t\tconst resolved = host.settingsManager.canonicalizePath(targetFile);\n\t\t\tif (resolved) {\n\t\t\t\ttargetFile = resolved;\n\t\t\t}\n\t\t}\n\n\t\tfs.mkdirSync(path.dirname(targetFile), { recursive: true });\n\t\tfs.writeFileSync(targetFile, JSON.stringify(backupData, null, 2), \"utf-8\");\n\t\thost.showStatus(`Configuration backup saved to ${targetFile}`);\n\t} catch (error) {\n\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t}\n}\n\nexport async function handleConfigRestoreCommand(host: ConfigRestoreHost, fileArg: string): Promise<void> {\n\ttry {\n\t\tconst trimmed = fileArg.trim();\n\t\tif (!trimmed) {\n\t\t\thost.showError(\"Usage: /config-restore <file>\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst resolved = host.settingsManager.canonicalizePath(trimmed);\n\t\tif (!resolved || !fs.existsSync(resolved)) {\n\t\t\thost.showError(`Backup file does not exist: ${trimmed}`);\n\t\t\treturn;\n\t\t}\n\n\t\tlet bundle: any;\n\t\ttry {\n\t\t\tconst content = fs.readFileSync(resolved, \"utf-8\");\n\t\t\tbundle = JSON.parse(content);\n\t\t} catch (error) {\n\t\t\thost.showError(`Failed to parse backup file: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!bundle || typeof bundle !== \"object\") {\n\t\t\thost.showError(\"Invalid backup file: must be a JSON object\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Confirm before clobbering\n\t\tconst confirm = await new Promise<boolean>((resolve) => {\n\t\t\thost.showSelector((done) => {\n\t\t\t\tconst submenu = new SelectSubmenu(\n\t\t\t\t\t\"Restore configuration?\",\n\t\t\t\t\t\"This will overwrite existing local profiles and settings with the backup values. Do you want to continue?\",\n\t\t\t\t\t[\n\t\t\t\t\t\t{ value: \"yes\", label: \"Yes\", description: \"Proceed with restoration.\" },\n\t\t\t\t\t\t{ value: \"no\", label: \"No\", description: \"Cancel and abort.\" },\n\t\t\t\t\t],\n\t\t\t\t\t\"no\",\n\t\t\t\t\t(value) => {\n\t\t\t\t\t\tdone();\n\t\t\t\t\t\tresolve(value === \"yes\");\n\t\t\t\t\t},\n\t\t\t\t\t() => {\n\t\t\t\t\t\tdone();\n\t\t\t\t\t\tresolve(false);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\treturn { component: submenu, focus: submenu.getSelectList() };\n\t\t\t});\n\t\t});\n\n\t\tif (!confirm) {\n\t\t\thost.showStatus(\"Restore aborted.\");\n\t\t\treturn;\n\t\t}\n\n\t\t// 1. Restore profile files (reusable-file scope)\n\t\tif (bundle.profiles && typeof bundle.profiles === \"object\") {\n\t\t\tconst profilesDir = path.join(getAgentDir(), \"profiles\");\n\t\t\tfs.mkdirSync(profilesDir, { recursive: true });\n\t\t\tfor (const [filename, content] of Object.entries(bundle.profiles)) {\n\t\t\t\tconst targetPath = path.join(profilesDir, filename);\n\t\t\t\tfs.writeFileSync(targetPath, JSON.stringify(content, null, 2), \"utf-8\");\n\t\t\t}\n\t\t}\n\n\t\t// 2. Restore settings\n\t\tif (bundle.settings && typeof bundle.settings === \"object\") {\n\t\t\tconst bs = bundle.settings;\n\n\t\t\t// Global profiles definitions\n\t\t\tif (bs.resourceProfiles && typeof bs.resourceProfiles === \"object\") {\n\t\t\t\tfor (const [name, definition] of Object.entries(bs.resourceProfiles)) {\n\t\t\t\t\thost.settingsManager.setProfileDefinition(name, definition as any, \"global\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Active profile selection\n\t\t\tif (bs.activeResourceProfile) {\n\t\t\t\thost.settingsManager.setActiveProfile(bs.activeResourceProfile, \"global\");\n\t\t\t}\n\n\t\t\t// External roots (trustedRoots are NOT restored, as per SECURITY requirement)\n\t\t\tif (Array.isArray(bs.externalResourceRoots)) {\n\t\t\t\thost.settingsManager.setExternalResourceRoots(bs.externalResourceRoots, \"global\");\n\n\t\t\t\tconst currentTrusted = host.settingsManager.getTrustedResourceRoots();\n\t\t\t\tconst newTrusted = currentTrusted.filter((r) => !bs.externalResourceRoots.includes(r));\n\t\t\t\thost.settingsManager.setTrustedResourceRoots(newTrusted, \"global\");\n\t\t\t}\n\t\t}\n\n\t\thost.showStatus(\"Configuration restored successfully.\");\n\t\tawait host.handleReloadCommand();\n\t} catch (error) {\n\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"config-backup.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/config-backup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,KAAK,EAMX,eAAe,EAEf,MAAM,gCAAgC,CAAC;AAGxC,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IAC1D,YAAY,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK;QAAE,SAAS,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7F,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACxC;AAqCD,MAAM,WAAW,oBAAoB;IACpC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3B;AAkDD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,oBAAoB,CAU7E;AAsBD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAS7F;AAYD,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDvG;AAED,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuHxG","sourcesContent":["/**\n * Config backup/restore commands extracted from interactive-mode.\n *\n * `handleConfigBackupCommand` snapshots the reusable-file profiles and the\n * resource-relevant settings to a JSON bundle; `handleConfigRestoreCommand`\n * reads such a bundle back, confirming with the user before clobbering local\n * state. Both operate through a narrow `host` seam (settings manager + the\n * status/error/selector callbacks) so the file IO and validation live outside\n * the god file while interactive-mode keeps only thin delegating wrappers.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { Component } from \"@caupulican/pi-tui\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type {\n\tGlobalResourceProfileConfiguration,\n\tModelRouterSettings,\n\tProfileDefinitionInput,\n\tResourceProfileSettings,\n\tSettings,\n\tSettingsManager,\n\tThinkingLevel,\n} from \"../../core/settings-manager.ts\";\nimport { SelectSubmenu } from \"./components/settings-selector.ts\";\n\nexport interface ConfigBackupHost {\n\treadonly settingsManager: SettingsManager;\n\tshowStatus(message: string): void;\n\tshowError(errorMessage: string): void;\n}\n\nexport interface ConfigRestoreHost extends ConfigBackupHost {\n\tshowSelector(create: (done: () => void) => { component: Component; focus: Component }): void;\n\thandleReloadCommand(): Promise<boolean>;\n}\n\nconst PROFILE_THINKING_LEVELS = new Set<ThinkingLevel>([\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n\t\"ultra\",\n]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction decodeBackupProfile(name: string, value: unknown): ProfileDefinitionInput | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst isWrapper = Object.hasOwn(value, \"resources\");\n\tconst resources = isWrapper ? value.resources : value;\n\tif (!isRecord(resources)) return undefined;\n\tconst thinking =\n\t\ttypeof value.thinking === \"string\" && PROFILE_THINKING_LEVELS.has(value.thinking as ThinkingLevel)\n\t\t\t? (value.thinking as ThinkingLevel)\n\t\t\t: undefined;\n\treturn {\n\t\tname,\n\t\tdescription: typeof value.description === \"string\" ? value.description : undefined,\n\t\tmodel: typeof value.model === \"string\" ? value.model : undefined,\n\t\tthinking,\n\t\tmodelRouter: isRecord(value.modelRouter) ? (value.modelRouter as ModelRouterSettings) : undefined,\n\t\tsoul: typeof value.soul === \"string\" ? value.soul : undefined,\n\t\tresources: resources as ResourceProfileSettings,\n\t};\n}\n\nexport interface ProfileFilesSnapshot {\n\tdirectoryExisted: boolean;\n\tfiles: Map<string, string>;\n}\n\nfunction canonicalActiveResourceProfiles(settings: Settings): string[] {\n\tconst values = settings.activeResourceProfiles ?? settings.activeResourceProfile ?? [];\n\tconst entries = Array.isArray(values) ? values : [values];\n\treturn [...new Set(entries.map((entry) => entry.trim()).filter((entry) => entry.length > 0))];\n}\n\nfunction decodeStringArray(label: string, value: unknown): string[] {\n\tif (!Array.isArray(value) || value.some((entry) => typeof entry !== \"string\")) {\n\t\tthrow new Error(`Invalid backup file: ${label} must be an array of strings`);\n\t}\n\treturn [...new Set(value.map((entry) => entry.trim()).filter((entry) => entry.length > 0))];\n}\n\nfunction decodeActiveResourceProfiles(settings: Record<string, unknown>): string[] {\n\tif (Object.hasOwn(settings, \"activeResourceProfiles\")) {\n\t\treturn decodeStringArray(\"settings.activeResourceProfiles\", settings.activeResourceProfiles);\n\t}\n\tconst legacy = settings.activeResourceProfile;\n\tif (legacy === undefined) return [];\n\tif (typeof legacy === \"string\") return legacy.trim() ? [legacy.trim()] : [];\n\treturn decodeStringArray(\"settings.activeResourceProfile\", legacy);\n}\n\nfunction decodeResourceProfiles(value: unknown): Record<string, ProfileDefinitionInput> {\n\tif (value === undefined) return {};\n\tif (!isRecord(value)) {\n\t\tthrow new Error(\"Invalid backup file: settings.resourceProfiles must be an object\");\n\t}\n\tconst profiles: Record<string, ProfileDefinitionInput> = {};\n\tfor (const [name, definition] of Object.entries(value)) {\n\t\tconst decoded = decodeBackupProfile(name, definition);\n\t\tif (!decoded) throw new Error(`Invalid backup file: malformed resource profile \"${name}\"`);\n\t\tprofiles[name] = decoded;\n\t}\n\treturn profiles;\n}\n\nfunction decodeProfileFiles(value: unknown): Record<string, unknown> {\n\tif (value === undefined) return {};\n\tif (!isRecord(value)) throw new Error(\"Invalid backup file: profiles must be an object\");\n\tfor (const filename of Object.keys(value)) {\n\t\tif (path.basename(filename) !== filename || !filename.endsWith(\".json\")) {\n\t\t\tthrow new Error(`Invalid backup file: unsafe profile filename \"${filename}\"`);\n\t\t}\n\t}\n\treturn value;\n}\n\nexport function captureProfileFiles(profilesDir: string): ProfileFilesSnapshot {\n\tconst files = new Map<string, string>();\n\tif (!fs.existsSync(profilesDir)) return { directoryExisted: false, files };\n\tfor (const filename of fs.readdirSync(profilesDir)) {\n\t\tif (!filename.endsWith(\".json\")) continue;\n\t\tconst profilePath = path.join(profilesDir, filename);\n\t\tif (!fs.lstatSync(profilePath).isFile()) continue;\n\t\tfiles.set(filename, fs.readFileSync(profilePath, \"utf-8\"));\n\t}\n\treturn { directoryExisted: true, files };\n}\n\nfunction clearProfileFiles(profilesDir: string): void {\n\tif (!fs.existsSync(profilesDir)) return;\n\tfor (const filename of fs.readdirSync(profilesDir)) {\n\t\tif (!filename.endsWith(\".json\")) continue;\n\t\tconst profilePath = path.join(profilesDir, filename);\n\t\tconst stat = fs.lstatSync(profilePath);\n\t\tif (stat.isFile() || stat.isSymbolicLink()) fs.rmSync(profilePath, { force: true });\n\t}\n}\n\nfunction replaceProfileFiles(profilesDir: string, profiles: Record<string, unknown>): void {\n\tfs.mkdirSync(profilesDir, { recursive: true });\n\tclearProfileFiles(profilesDir);\n\tfor (const [filename, content] of Object.entries(profiles)) {\n\t\tconst serialized = JSON.stringify(content, null, 2);\n\t\tif (serialized === undefined) throw new Error(`Invalid backup file: profile \"${filename}\" is not serializable`);\n\t\tfs.writeFileSync(path.join(profilesDir, filename), serialized, \"utf-8\");\n\t}\n}\n\nexport function restoreProfileFiles(profilesDir: string, snapshot: ProfileFilesSnapshot): void {\n\tclearProfileFiles(profilesDir);\n\tif (snapshot.files.size > 0) fs.mkdirSync(profilesDir, { recursive: true });\n\tfor (const [filename, content] of snapshot.files) {\n\t\tfs.writeFileSync(path.join(profilesDir, filename), content, \"utf-8\");\n\t}\n\tif (!snapshot.directoryExisted && fs.existsSync(profilesDir) && fs.readdirSync(profilesDir).length === 0) {\n\t\tfs.rmdirSync(profilesDir);\n\t}\n}\n\nfunction profileConfigurationFromSettings(settings: Settings): GlobalResourceProfileConfiguration {\n\treturn {\n\t\tresourceProfiles: settings.resourceProfiles,\n\t\tactiveResourceProfile: settings.activeResourceProfile,\n\t\tactiveResourceProfiles: settings.activeResourceProfiles,\n\t\texternalResourceRoots: settings.externalResourceRoots,\n\t\ttrustedResourceRoots: settings.trustedResourceRoots,\n\t};\n}\n\nexport async function handleConfigBackupCommand(host: ConfigBackupHost, fileArg?: string): Promise<void> {\n\ttry {\n\t\tconst profilesDir = path.join(getAgentDir(), \"profiles\");\n\t\tconst profiles: Record<string, unknown> = {};\n\t\tif (fs.existsSync(profilesDir)) {\n\t\t\tconst entries = fs.readdirSync(profilesDir);\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.endsWith(\".json\")) {\n\t\t\t\t\tconst pPath = path.join(profilesDir, entry);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (!fs.lstatSync(pPath).isFile()) continue;\n\t\t\t\t\t\tconst content = fs.readFileSync(pPath, \"utf-8\");\n\t\t\t\t\t\tprofiles[entry] = JSON.parse(content);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// skip\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst globalSettings = host.settingsManager.getGlobalSettings();\n\t\tconst backupData = {\n\t\t\tversion: 2,\n\t\t\tprofiles,\n\t\t\tsettings: {\n\t\t\t\tresourceProfiles: globalSettings.resourceProfiles ?? {},\n\t\t\t\tactiveResourceProfiles: canonicalActiveResourceProfiles(globalSettings),\n\t\t\t\texternalResourceRoots: globalSettings.externalResourceRoots ?? [],\n\t\t\t\ttrustedResourceRoots: globalSettings.trustedResourceRoots ?? [],\n\t\t\t},\n\t\t};\n\n\t\tlet targetFile = fileArg;\n\t\tif (!targetFile) {\n\t\t\tconst backupsDir = path.join(getAgentDir(), \"backups\");\n\t\t\tfs.mkdirSync(backupsDir, { recursive: true });\n\t\t\tconst timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n\t\t\ttargetFile = path.join(backupsDir, `config-${timestamp}.json`);\n\t\t} else {\n\t\t\tconst resolved = host.settingsManager.canonicalizePath(targetFile);\n\t\t\tif (resolved) {\n\t\t\t\ttargetFile = resolved;\n\t\t\t}\n\t\t}\n\n\t\tfs.mkdirSync(path.dirname(targetFile), { recursive: true });\n\t\tfs.writeFileSync(targetFile, JSON.stringify(backupData, null, 2), \"utf-8\");\n\t\thost.showStatus(`Configuration backup saved to ${targetFile}`);\n\t} catch (error) {\n\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t}\n}\n\nexport async function handleConfigRestoreCommand(host: ConfigRestoreHost, fileArg: string): Promise<void> {\n\ttry {\n\t\tconst trimmed = fileArg.trim();\n\t\tif (!trimmed) {\n\t\t\thost.showError(\"Usage: /config-restore <file>\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst resolved = host.settingsManager.canonicalizePath(trimmed);\n\t\tif (!resolved || !fs.existsSync(resolved)) {\n\t\t\thost.showError(`Backup file does not exist: ${trimmed}`);\n\t\t\treturn;\n\t\t}\n\n\t\tlet bundle: unknown;\n\t\ttry {\n\t\t\tconst content = fs.readFileSync(resolved, \"utf-8\");\n\t\t\tbundle = JSON.parse(content);\n\t\t} catch (error) {\n\t\t\thost.showError(`Failed to parse backup file: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!isRecord(bundle)) {\n\t\t\thost.showError(\"Invalid backup file: must be a JSON object\");\n\t\t\treturn;\n\t\t}\n\n\t\tlet profileFiles: Record<string, unknown>;\n\t\tlet restoredResourceProfiles: Record<string, ProfileDefinitionInput>;\n\t\tlet restoredActiveProfiles: string[];\n\t\tlet restoredExternalRoots: string[];\n\t\ttry {\n\t\t\tprofileFiles = decodeProfileFiles(bundle.profiles);\n\t\t\tconst backupSettings = isRecord(bundle.settings) ? bundle.settings : {};\n\t\t\trestoredResourceProfiles = decodeResourceProfiles(backupSettings.resourceProfiles);\n\t\t\trestoredActiveProfiles = decodeActiveResourceProfiles(backupSettings);\n\t\t\trestoredExternalRoots = decodeStringArray(\n\t\t\t\t\"settings.externalResourceRoots\",\n\t\t\t\tbackupSettings.externalResourceRoots ?? [],\n\t\t\t);\n\t\t} catch (error) {\n\t\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t\t\treturn;\n\t\t}\n\n\t\t// Confirm before clobbering\n\t\tconst confirm = await new Promise<boolean>((resolve) => {\n\t\t\thost.showSelector((done) => {\n\t\t\t\tconst submenu = new SelectSubmenu(\n\t\t\t\t\t\"Restore configuration?\",\n\t\t\t\t\t\"This will overwrite existing local profiles and settings with the backup values. Do you want to continue?\",\n\t\t\t\t\t[\n\t\t\t\t\t\t{ value: \"yes\", label: \"Yes\", description: \"Proceed with restoration.\" },\n\t\t\t\t\t\t{ value: \"no\", label: \"No\", description: \"Cancel and abort.\" },\n\t\t\t\t\t],\n\t\t\t\t\t\"no\",\n\t\t\t\t\t(value) => {\n\t\t\t\t\t\tdone();\n\t\t\t\t\t\tresolve(value === \"yes\");\n\t\t\t\t\t},\n\t\t\t\t\t() => {\n\t\t\t\t\t\tdone();\n\t\t\t\t\t\tresolve(false);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\treturn { component: submenu, focus: submenu.getSelectList() };\n\t\t\t});\n\t\t});\n\n\t\tif (!confirm) {\n\t\t\thost.showStatus(\"Restore aborted.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst profilesDir = path.join(getAgentDir(), \"profiles\");\n\t\tconst profileFilesSnapshot = captureProfileFiles(profilesDir);\n\t\tconst settingsSnapshot = host.settingsManager.createReloadSnapshot();\n\t\tlet restoreStarted = false;\n\t\ttry {\n\t\t\trestoreStarted = true;\n\t\t\treplaceProfileFiles(profilesDir, profileFiles);\n\t\t\tconst currentTrusted = settingsSnapshot.globalSettings.trustedResourceRoots ?? [];\n\t\t\thost.settingsManager.replaceGlobalResourceProfileConfiguration({\n\t\t\t\tresourceProfiles: Object.keys(restoredResourceProfiles).length > 0 ? restoredResourceProfiles : undefined,\n\t\t\t\tactiveResourceProfiles: restoredActiveProfiles,\n\t\t\t\texternalResourceRoots: restoredExternalRoots,\n\t\t\t\t// Security boundary: roots named by a backup never become trusted through restore.\n\t\t\t\ttrustedResourceRoots: currentTrusted.filter((root) => !restoredExternalRoots.includes(root)),\n\t\t\t});\n\t\t\tawait host.settingsManager.flush();\n\t\t\tif (!(await host.handleReloadCommand())) {\n\t\t\t\tthrow new Error(\"restored configuration failed runtime validation\");\n\t\t\t}\n\t\t\thost.showStatus(\"Configuration restored successfully.\");\n\t\t} catch (error) {\n\t\t\tlet rollbackError: unknown;\n\t\t\tif (restoreStarted) {\n\t\t\t\ttry {\n\t\t\t\t\trestoreProfileFiles(profilesDir, profileFilesSnapshot);\n\t\t\t\t\thost.settingsManager.replaceGlobalResourceProfileConfiguration(\n\t\t\t\t\t\tprofileConfigurationFromSettings(settingsSnapshot.globalSettings),\n\t\t\t\t\t);\n\t\t\t\t\tawait host.settingsManager.flush();\n\t\t\t\t\thost.settingsManager.restoreReloadSnapshot(settingsSnapshot);\n\t\t\t\t} catch (restoreError) {\n\t\t\t\t\trollbackError = restoreError;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\thost.showError(\n\t\t\t\trollbackError\n\t\t\t\t\t? `Configuration restore failed: ${message}; rollback failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`\n\t\t\t\t\t: `Configuration restore failed: ${message}; previous configuration restored`,\n\t\t\t);\n\t\t}\n\t} catch (error) {\n\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t}\n}\n"]}
|
|
@@ -12,6 +12,144 @@ import * as fs from "node:fs";
|
|
|
12
12
|
import * as path from "node:path";
|
|
13
13
|
import { getAgentDir } from "../../config.js";
|
|
14
14
|
import { SelectSubmenu } from "./components/settings-selector.js";
|
|
15
|
+
const PROFILE_THINKING_LEVELS = new Set([
|
|
16
|
+
"off",
|
|
17
|
+
"minimal",
|
|
18
|
+
"low",
|
|
19
|
+
"medium",
|
|
20
|
+
"high",
|
|
21
|
+
"xhigh",
|
|
22
|
+
"max",
|
|
23
|
+
"ultra",
|
|
24
|
+
]);
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function decodeBackupProfile(name, value) {
|
|
29
|
+
if (!isRecord(value))
|
|
30
|
+
return undefined;
|
|
31
|
+
const isWrapper = Object.hasOwn(value, "resources");
|
|
32
|
+
const resources = isWrapper ? value.resources : value;
|
|
33
|
+
if (!isRecord(resources))
|
|
34
|
+
return undefined;
|
|
35
|
+
const thinking = typeof value.thinking === "string" && PROFILE_THINKING_LEVELS.has(value.thinking)
|
|
36
|
+
? value.thinking
|
|
37
|
+
: undefined;
|
|
38
|
+
return {
|
|
39
|
+
name,
|
|
40
|
+
description: typeof value.description === "string" ? value.description : undefined,
|
|
41
|
+
model: typeof value.model === "string" ? value.model : undefined,
|
|
42
|
+
thinking,
|
|
43
|
+
modelRouter: isRecord(value.modelRouter) ? value.modelRouter : undefined,
|
|
44
|
+
soul: typeof value.soul === "string" ? value.soul : undefined,
|
|
45
|
+
resources: resources,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function canonicalActiveResourceProfiles(settings) {
|
|
49
|
+
const values = settings.activeResourceProfiles ?? settings.activeResourceProfile ?? [];
|
|
50
|
+
const entries = Array.isArray(values) ? values : [values];
|
|
51
|
+
return [...new Set(entries.map((entry) => entry.trim()).filter((entry) => entry.length > 0))];
|
|
52
|
+
}
|
|
53
|
+
function decodeStringArray(label, value) {
|
|
54
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
55
|
+
throw new Error(`Invalid backup file: ${label} must be an array of strings`);
|
|
56
|
+
}
|
|
57
|
+
return [...new Set(value.map((entry) => entry.trim()).filter((entry) => entry.length > 0))];
|
|
58
|
+
}
|
|
59
|
+
function decodeActiveResourceProfiles(settings) {
|
|
60
|
+
if (Object.hasOwn(settings, "activeResourceProfiles")) {
|
|
61
|
+
return decodeStringArray("settings.activeResourceProfiles", settings.activeResourceProfiles);
|
|
62
|
+
}
|
|
63
|
+
const legacy = settings.activeResourceProfile;
|
|
64
|
+
if (legacy === undefined)
|
|
65
|
+
return [];
|
|
66
|
+
if (typeof legacy === "string")
|
|
67
|
+
return legacy.trim() ? [legacy.trim()] : [];
|
|
68
|
+
return decodeStringArray("settings.activeResourceProfile", legacy);
|
|
69
|
+
}
|
|
70
|
+
function decodeResourceProfiles(value) {
|
|
71
|
+
if (value === undefined)
|
|
72
|
+
return {};
|
|
73
|
+
if (!isRecord(value)) {
|
|
74
|
+
throw new Error("Invalid backup file: settings.resourceProfiles must be an object");
|
|
75
|
+
}
|
|
76
|
+
const profiles = {};
|
|
77
|
+
for (const [name, definition] of Object.entries(value)) {
|
|
78
|
+
const decoded = decodeBackupProfile(name, definition);
|
|
79
|
+
if (!decoded)
|
|
80
|
+
throw new Error(`Invalid backup file: malformed resource profile "${name}"`);
|
|
81
|
+
profiles[name] = decoded;
|
|
82
|
+
}
|
|
83
|
+
return profiles;
|
|
84
|
+
}
|
|
85
|
+
function decodeProfileFiles(value) {
|
|
86
|
+
if (value === undefined)
|
|
87
|
+
return {};
|
|
88
|
+
if (!isRecord(value))
|
|
89
|
+
throw new Error("Invalid backup file: profiles must be an object");
|
|
90
|
+
for (const filename of Object.keys(value)) {
|
|
91
|
+
if (path.basename(filename) !== filename || !filename.endsWith(".json")) {
|
|
92
|
+
throw new Error(`Invalid backup file: unsafe profile filename "${filename}"`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
export function captureProfileFiles(profilesDir) {
|
|
98
|
+
const files = new Map();
|
|
99
|
+
if (!fs.existsSync(profilesDir))
|
|
100
|
+
return { directoryExisted: false, files };
|
|
101
|
+
for (const filename of fs.readdirSync(profilesDir)) {
|
|
102
|
+
if (!filename.endsWith(".json"))
|
|
103
|
+
continue;
|
|
104
|
+
const profilePath = path.join(profilesDir, filename);
|
|
105
|
+
if (!fs.lstatSync(profilePath).isFile())
|
|
106
|
+
continue;
|
|
107
|
+
files.set(filename, fs.readFileSync(profilePath, "utf-8"));
|
|
108
|
+
}
|
|
109
|
+
return { directoryExisted: true, files };
|
|
110
|
+
}
|
|
111
|
+
function clearProfileFiles(profilesDir) {
|
|
112
|
+
if (!fs.existsSync(profilesDir))
|
|
113
|
+
return;
|
|
114
|
+
for (const filename of fs.readdirSync(profilesDir)) {
|
|
115
|
+
if (!filename.endsWith(".json"))
|
|
116
|
+
continue;
|
|
117
|
+
const profilePath = path.join(profilesDir, filename);
|
|
118
|
+
const stat = fs.lstatSync(profilePath);
|
|
119
|
+
if (stat.isFile() || stat.isSymbolicLink())
|
|
120
|
+
fs.rmSync(profilePath, { force: true });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function replaceProfileFiles(profilesDir, profiles) {
|
|
124
|
+
fs.mkdirSync(profilesDir, { recursive: true });
|
|
125
|
+
clearProfileFiles(profilesDir);
|
|
126
|
+
for (const [filename, content] of Object.entries(profiles)) {
|
|
127
|
+
const serialized = JSON.stringify(content, null, 2);
|
|
128
|
+
if (serialized === undefined)
|
|
129
|
+
throw new Error(`Invalid backup file: profile "${filename}" is not serializable`);
|
|
130
|
+
fs.writeFileSync(path.join(profilesDir, filename), serialized, "utf-8");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function restoreProfileFiles(profilesDir, snapshot) {
|
|
134
|
+
clearProfileFiles(profilesDir);
|
|
135
|
+
if (snapshot.files.size > 0)
|
|
136
|
+
fs.mkdirSync(profilesDir, { recursive: true });
|
|
137
|
+
for (const [filename, content] of snapshot.files) {
|
|
138
|
+
fs.writeFileSync(path.join(profilesDir, filename), content, "utf-8");
|
|
139
|
+
}
|
|
140
|
+
if (!snapshot.directoryExisted && fs.existsSync(profilesDir) && fs.readdirSync(profilesDir).length === 0) {
|
|
141
|
+
fs.rmdirSync(profilesDir);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function profileConfigurationFromSettings(settings) {
|
|
145
|
+
return {
|
|
146
|
+
resourceProfiles: settings.resourceProfiles,
|
|
147
|
+
activeResourceProfile: settings.activeResourceProfile,
|
|
148
|
+
activeResourceProfiles: settings.activeResourceProfiles,
|
|
149
|
+
externalResourceRoots: settings.externalResourceRoots,
|
|
150
|
+
trustedResourceRoots: settings.trustedResourceRoots,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
15
153
|
export async function handleConfigBackupCommand(host, fileArg) {
|
|
16
154
|
try {
|
|
17
155
|
const profilesDir = path.join(getAgentDir(), "profiles");
|
|
@@ -22,6 +160,8 @@ export async function handleConfigBackupCommand(host, fileArg) {
|
|
|
22
160
|
if (entry.endsWith(".json")) {
|
|
23
161
|
const pPath = path.join(profilesDir, entry);
|
|
24
162
|
try {
|
|
163
|
+
if (!fs.lstatSync(pPath).isFile())
|
|
164
|
+
continue;
|
|
25
165
|
const content = fs.readFileSync(pPath, "utf-8");
|
|
26
166
|
profiles[entry] = JSON.parse(content);
|
|
27
167
|
}
|
|
@@ -31,13 +171,15 @@ export async function handleConfigBackupCommand(host, fileArg) {
|
|
|
31
171
|
}
|
|
32
172
|
}
|
|
33
173
|
}
|
|
174
|
+
const globalSettings = host.settingsManager.getGlobalSettings();
|
|
34
175
|
const backupData = {
|
|
176
|
+
version: 2,
|
|
35
177
|
profiles,
|
|
36
178
|
settings: {
|
|
37
|
-
resourceProfiles:
|
|
38
|
-
|
|
39
|
-
externalResourceRoots:
|
|
40
|
-
trustedResourceRoots:
|
|
179
|
+
resourceProfiles: globalSettings.resourceProfiles ?? {},
|
|
180
|
+
activeResourceProfiles: canonicalActiveResourceProfiles(globalSettings),
|
|
181
|
+
externalResourceRoots: globalSettings.externalResourceRoots ?? [],
|
|
182
|
+
trustedResourceRoots: globalSettings.trustedResourceRoots ?? [],
|
|
41
183
|
},
|
|
42
184
|
};
|
|
43
185
|
let targetFile = fileArg;
|
|
@@ -82,10 +224,25 @@ export async function handleConfigRestoreCommand(host, fileArg) {
|
|
|
82
224
|
host.showError(`Failed to parse backup file: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
225
|
return;
|
|
84
226
|
}
|
|
85
|
-
if (!bundle
|
|
227
|
+
if (!isRecord(bundle)) {
|
|
86
228
|
host.showError("Invalid backup file: must be a JSON object");
|
|
87
229
|
return;
|
|
88
230
|
}
|
|
231
|
+
let profileFiles;
|
|
232
|
+
let restoredResourceProfiles;
|
|
233
|
+
let restoredActiveProfiles;
|
|
234
|
+
let restoredExternalRoots;
|
|
235
|
+
try {
|
|
236
|
+
profileFiles = decodeProfileFiles(bundle.profiles);
|
|
237
|
+
const backupSettings = isRecord(bundle.settings) ? bundle.settings : {};
|
|
238
|
+
restoredResourceProfiles = decodeResourceProfiles(backupSettings.resourceProfiles);
|
|
239
|
+
restoredActiveProfiles = decodeActiveResourceProfiles(backupSettings);
|
|
240
|
+
restoredExternalRoots = decodeStringArray("settings.externalResourceRoots", backupSettings.externalResourceRoots ?? []);
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
host.showError(error instanceof Error ? error.message : String(error));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
89
246
|
// Confirm before clobbering
|
|
90
247
|
const confirm = await new Promise((resolve) => {
|
|
91
248
|
host.showSelector((done) => {
|
|
@@ -106,38 +263,45 @@ export async function handleConfigRestoreCommand(host, fileArg) {
|
|
|
106
263
|
host.showStatus("Restore aborted.");
|
|
107
264
|
return;
|
|
108
265
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
266
|
+
const profilesDir = path.join(getAgentDir(), "profiles");
|
|
267
|
+
const profileFilesSnapshot = captureProfileFiles(profilesDir);
|
|
268
|
+
const settingsSnapshot = host.settingsManager.createReloadSnapshot();
|
|
269
|
+
let restoreStarted = false;
|
|
270
|
+
try {
|
|
271
|
+
restoreStarted = true;
|
|
272
|
+
replaceProfileFiles(profilesDir, profileFiles);
|
|
273
|
+
const currentTrusted = settingsSnapshot.globalSettings.trustedResourceRoots ?? [];
|
|
274
|
+
host.settingsManager.replaceGlobalResourceProfileConfiguration({
|
|
275
|
+
resourceProfiles: Object.keys(restoredResourceProfiles).length > 0 ? restoredResourceProfiles : undefined,
|
|
276
|
+
activeResourceProfiles: restoredActiveProfiles,
|
|
277
|
+
externalResourceRoots: restoredExternalRoots,
|
|
278
|
+
// Security boundary: roots named by a backup never become trusted through restore.
|
|
279
|
+
trustedResourceRoots: currentTrusted.filter((root) => !restoredExternalRoots.includes(root)),
|
|
280
|
+
});
|
|
281
|
+
await host.settingsManager.flush();
|
|
282
|
+
if (!(await host.handleReloadCommand())) {
|
|
283
|
+
throw new Error("restored configuration failed runtime validation");
|
|
116
284
|
}
|
|
285
|
+
host.showStatus("Configuration restored successfully.");
|
|
117
286
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
host.settingsManager.
|
|
287
|
+
catch (error) {
|
|
288
|
+
let rollbackError;
|
|
289
|
+
if (restoreStarted) {
|
|
290
|
+
try {
|
|
291
|
+
restoreProfileFiles(profilesDir, profileFilesSnapshot);
|
|
292
|
+
host.settingsManager.replaceGlobalResourceProfileConfiguration(profileConfigurationFromSettings(settingsSnapshot.globalSettings));
|
|
293
|
+
await host.settingsManager.flush();
|
|
294
|
+
host.settingsManager.restoreReloadSnapshot(settingsSnapshot);
|
|
295
|
+
}
|
|
296
|
+
catch (restoreError) {
|
|
297
|
+
rollbackError = restoreError;
|
|
125
298
|
}
|
|
126
299
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// External roots (trustedRoots are NOT restored, as per SECURITY requirement)
|
|
132
|
-
if (Array.isArray(bs.externalResourceRoots)) {
|
|
133
|
-
host.settingsManager.setExternalResourceRoots(bs.externalResourceRoots, "global");
|
|
134
|
-
const currentTrusted = host.settingsManager.getTrustedResourceRoots();
|
|
135
|
-
const newTrusted = currentTrusted.filter((r) => !bs.externalResourceRoots.includes(r));
|
|
136
|
-
host.settingsManager.setTrustedResourceRoots(newTrusted, "global");
|
|
137
|
-
}
|
|
300
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
301
|
+
host.showError(rollbackError
|
|
302
|
+
? `Configuration restore failed: ${message}; rollback failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`
|
|
303
|
+
: `Configuration restore failed: ${message}; previous configuration restored`);
|
|
138
304
|
}
|
|
139
|
-
host.showStatus("Configuration restored successfully.");
|
|
140
|
-
await host.handleReloadCommand();
|
|
141
305
|
}
|
|
142
306
|
catch (error) {
|
|
143
307
|
host.showError(error instanceof Error ? error.message : String(error));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-backup.js","sourceRoot":"","sources":["../../../src/modes/interactive/config-backup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAalE,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,IAAsB,EAAE,OAAgB,EAAiB;IACxG,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAwB,EAAE,CAAC;QACzC,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YAC5C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;oBAC5C,IAAI,CAAC;wBACJ,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;wBAChD,QAAQ,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBACvC,CAAC;oBAAC,MAAM,CAAC;wBACR,OAAO;oBACR,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAED,MAAM,UAAU,GAAG;YAClB,QAAQ;YACR,QAAQ,EAAE;gBACT,gBAAgB,EAAE,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,gBAAgB;gBAChE,qBAAqB,EAAE,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,qBAAqB;gBAC1E,qBAAqB,EAAE,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,qBAAqB;gBAC1E,oBAAoB,EAAE,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,oBAAoB;aACxE;SACD,CAAC;QAEF,IAAI,UAAU,GAAG,OAAO,CAAC;QACzB,IAAI,CAAC,UAAU,EAAE,CAAC;YACjB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC,CAAC;YACvD,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9C,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YACjE,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,SAAS,OAAO,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACP,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;YACnE,IAAI,QAAQ,EAAE,CAAC;gBACd,UAAU,GAAG,QAAQ,CAAC;YACvB,CAAC;QACF,CAAC;QAED,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC3E,IAAI,CAAC,UAAU,CAAC,iCAAiC,UAAU,EAAE,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC,SAAS,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;AAAA,CACD;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,IAAuB,EAAE,OAAe,EAAiB;IACzG,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,IAAI,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;YAChD,OAAO;QACR,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,SAAS,CAAC,+BAA+B,OAAO,EAAE,CAAC,CAAC;YACzD,OAAO;QACR,CAAC;QAED,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,CAAC,gCAAgC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACzG,OAAO;QACR,CAAC;QAED,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC3C,IAAI,CAAC,SAAS,CAAC,4CAA4C,CAAC,CAAC;YAC7D,OAAO;QACR,CAAC;QAED,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE,CAAC;YACvD,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG,IAAI,aAAa,CAChC,wBAAwB,EACxB,2GAA2G,EAC3G;oBACC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,2BAA2B,EAAE;oBACxE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,mBAAmB,EAAE;iBAC9D,EACD,IAAI,EACJ,CAAC,KAAK,EAAE,EAAE,CAAC;oBACV,IAAI,EAAE,CAAC;oBACP,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;gBAAA,CACzB,EACD,GAAG,EAAE,CAAC;oBACL,IAAI,EAAE,CAAC;oBACP,OAAO,CAAC,KAAK,CAAC,CAAC;gBAAA,CACf,CACD,CAAC;gBACF,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;YAAA,CAC9D,CAAC,CAAC;QAAA,CACH,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;YACpC,OAAO;QACR,CAAC;QAED,iDAAiD;QACjD,IAAI,MAAM,CAAC,QAAQ,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,CAAC,CAAC;YACzD,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/C,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;gBACpD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YACzE,CAAC;QACF,CAAC;QAED,sBAAsB;QACtB,IAAI,MAAM,CAAC,QAAQ,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC5D,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;YAE3B,8BAA8B;YAC9B,IAAI,EAAE,CAAC,gBAAgB,IAAI,OAAO,EAAE,CAAC,gBAAgB,KAAK,QAAQ,EAAE,CAAC;gBACpE,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC;oBACtE,IAAI,CAAC,eAAe,CAAC,oBAAoB,CAAC,IAAI,EAAE,UAAiB,EAAE,QAAQ,CAAC,CAAC;gBAC9E,CAAC;YACF,CAAC;YAED,2BAA2B;YAC3B,IAAI,EAAE,CAAC,qBAAqB,EAAE,CAAC;gBAC9B,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,EAAE,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC;YAC3E,CAAC;YAED,8EAA8E;YAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,qBAAqB,CAAC,EAAE,CAAC;gBAC7C,IAAI,CAAC,eAAe,CAAC,wBAAwB,CAAC,EAAE,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC;gBAElF,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,uBAAuB,EAAE,CAAC;gBACtE,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvF,IAAI,CAAC,eAAe,CAAC,uBAAuB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YACpE,CAAC;QACF,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,sCAAsC,CAAC,CAAC;QACxD,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC,SAAS,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;AAAA,CACD","sourcesContent":["/**\n * Config backup/restore commands extracted from interactive-mode.\n *\n * `handleConfigBackupCommand` snapshots the reusable-file profiles and the\n * resource-relevant settings to a JSON bundle; `handleConfigRestoreCommand`\n * reads such a bundle back, confirming with the user before clobbering local\n * state. Both operate through a narrow `host` seam (settings manager + the\n * status/error/selector callbacks) so the file IO and validation live outside\n * the god file while interactive-mode keeps only thin delegating wrappers.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { Component } from \"@caupulican/pi-tui\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type { SettingsManager } from \"../../core/settings-manager.ts\";\nimport { SelectSubmenu } from \"./components/settings-selector.ts\";\n\nexport interface ConfigBackupHost {\n\treadonly settingsManager: SettingsManager;\n\tshowStatus(message: string): void;\n\tshowError(errorMessage: string): void;\n}\n\nexport interface ConfigRestoreHost extends ConfigBackupHost {\n\tshowSelector(create: (done: () => void) => { component: Component; focus: Component }): void;\n\thandleReloadCommand(): Promise<void>;\n}\n\nexport async function handleConfigBackupCommand(host: ConfigBackupHost, fileArg?: string): Promise<void> {\n\ttry {\n\t\tconst profilesDir = path.join(getAgentDir(), \"profiles\");\n\t\tconst profiles: Record<string, any> = {};\n\t\tif (fs.existsSync(profilesDir)) {\n\t\t\tconst entries = fs.readdirSync(profilesDir);\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.endsWith(\".json\")) {\n\t\t\t\t\tconst pPath = path.join(profilesDir, entry);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst content = fs.readFileSync(pPath, \"utf-8\");\n\t\t\t\t\t\tprofiles[entry] = JSON.parse(content);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// skip\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst backupData = {\n\t\t\tprofiles,\n\t\t\tsettings: {\n\t\t\t\tresourceProfiles: host.settingsManager.settings.resourceProfiles,\n\t\t\t\tactiveResourceProfile: host.settingsManager.settings.activeResourceProfile,\n\t\t\t\texternalResourceRoots: host.settingsManager.settings.externalResourceRoots,\n\t\t\t\ttrustedResourceRoots: host.settingsManager.settings.trustedResourceRoots,\n\t\t\t},\n\t\t};\n\n\t\tlet targetFile = fileArg;\n\t\tif (!targetFile) {\n\t\t\tconst backupsDir = path.join(getAgentDir(), \"backups\");\n\t\t\tfs.mkdirSync(backupsDir, { recursive: true });\n\t\t\tconst timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n\t\t\ttargetFile = path.join(backupsDir, `config-${timestamp}.json`);\n\t\t} else {\n\t\t\tconst resolved = host.settingsManager.canonicalizePath(targetFile);\n\t\t\tif (resolved) {\n\t\t\t\ttargetFile = resolved;\n\t\t\t}\n\t\t}\n\n\t\tfs.mkdirSync(path.dirname(targetFile), { recursive: true });\n\t\tfs.writeFileSync(targetFile, JSON.stringify(backupData, null, 2), \"utf-8\");\n\t\thost.showStatus(`Configuration backup saved to ${targetFile}`);\n\t} catch (error) {\n\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t}\n}\n\nexport async function handleConfigRestoreCommand(host: ConfigRestoreHost, fileArg: string): Promise<void> {\n\ttry {\n\t\tconst trimmed = fileArg.trim();\n\t\tif (!trimmed) {\n\t\t\thost.showError(\"Usage: /config-restore <file>\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst resolved = host.settingsManager.canonicalizePath(trimmed);\n\t\tif (!resolved || !fs.existsSync(resolved)) {\n\t\t\thost.showError(`Backup file does not exist: ${trimmed}`);\n\t\t\treturn;\n\t\t}\n\n\t\tlet bundle: any;\n\t\ttry {\n\t\t\tconst content = fs.readFileSync(resolved, \"utf-8\");\n\t\t\tbundle = JSON.parse(content);\n\t\t} catch (error) {\n\t\t\thost.showError(`Failed to parse backup file: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!bundle || typeof bundle !== \"object\") {\n\t\t\thost.showError(\"Invalid backup file: must be a JSON object\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Confirm before clobbering\n\t\tconst confirm = await new Promise<boolean>((resolve) => {\n\t\t\thost.showSelector((done) => {\n\t\t\t\tconst submenu = new SelectSubmenu(\n\t\t\t\t\t\"Restore configuration?\",\n\t\t\t\t\t\"This will overwrite existing local profiles and settings with the backup values. Do you want to continue?\",\n\t\t\t\t\t[\n\t\t\t\t\t\t{ value: \"yes\", label: \"Yes\", description: \"Proceed with restoration.\" },\n\t\t\t\t\t\t{ value: \"no\", label: \"No\", description: \"Cancel and abort.\" },\n\t\t\t\t\t],\n\t\t\t\t\t\"no\",\n\t\t\t\t\t(value) => {\n\t\t\t\t\t\tdone();\n\t\t\t\t\t\tresolve(value === \"yes\");\n\t\t\t\t\t},\n\t\t\t\t\t() => {\n\t\t\t\t\t\tdone();\n\t\t\t\t\t\tresolve(false);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\treturn { component: submenu, focus: submenu.getSelectList() };\n\t\t\t});\n\t\t});\n\n\t\tif (!confirm) {\n\t\t\thost.showStatus(\"Restore aborted.\");\n\t\t\treturn;\n\t\t}\n\n\t\t// 1. Restore profile files (reusable-file scope)\n\t\tif (bundle.profiles && typeof bundle.profiles === \"object\") {\n\t\t\tconst profilesDir = path.join(getAgentDir(), \"profiles\");\n\t\t\tfs.mkdirSync(profilesDir, { recursive: true });\n\t\t\tfor (const [filename, content] of Object.entries(bundle.profiles)) {\n\t\t\t\tconst targetPath = path.join(profilesDir, filename);\n\t\t\t\tfs.writeFileSync(targetPath, JSON.stringify(content, null, 2), \"utf-8\");\n\t\t\t}\n\t\t}\n\n\t\t// 2. Restore settings\n\t\tif (bundle.settings && typeof bundle.settings === \"object\") {\n\t\t\tconst bs = bundle.settings;\n\n\t\t\t// Global profiles definitions\n\t\t\tif (bs.resourceProfiles && typeof bs.resourceProfiles === \"object\") {\n\t\t\t\tfor (const [name, definition] of Object.entries(bs.resourceProfiles)) {\n\t\t\t\t\thost.settingsManager.setProfileDefinition(name, definition as any, \"global\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Active profile selection\n\t\t\tif (bs.activeResourceProfile) {\n\t\t\t\thost.settingsManager.setActiveProfile(bs.activeResourceProfile, \"global\");\n\t\t\t}\n\n\t\t\t// External roots (trustedRoots are NOT restored, as per SECURITY requirement)\n\t\t\tif (Array.isArray(bs.externalResourceRoots)) {\n\t\t\t\thost.settingsManager.setExternalResourceRoots(bs.externalResourceRoots, \"global\");\n\n\t\t\t\tconst currentTrusted = host.settingsManager.getTrustedResourceRoots();\n\t\t\t\tconst newTrusted = currentTrusted.filter((r) => !bs.externalResourceRoots.includes(r));\n\t\t\t\thost.settingsManager.setTrustedResourceRoots(newTrusted, \"global\");\n\t\t\t}\n\t\t}\n\n\t\thost.showStatus(\"Configuration restored successfully.\");\n\t\tawait host.handleReloadCommand();\n\t} catch (error) {\n\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"config-backup.js","sourceRoot":"","sources":["../../../src/modes/interactive/config-backup.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Config backup/restore commands extracted from interactive-mode.\n *\n * `handleConfigBackupCommand` snapshots the reusable-file profiles and the\n * resource-relevant settings to a JSON bundle; `handleConfigRestoreCommand`\n * reads such a bundle back, confirming with the user before clobbering local\n * state. Both operate through a narrow `host` seam (settings manager + the\n * status/error/selector callbacks) so the file IO and validation live outside\n * the god file while interactive-mode keeps only thin delegating wrappers.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { Component } from \"@caupulican/pi-tui\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type {\n\tGlobalResourceProfileConfiguration,\n\tModelRouterSettings,\n\tProfileDefinitionInput,\n\tResourceProfileSettings,\n\tSettings,\n\tSettingsManager,\n\tThinkingLevel,\n} from \"../../core/settings-manager.ts\";\nimport { SelectSubmenu } from \"./components/settings-selector.ts\";\n\nexport interface ConfigBackupHost {\n\treadonly settingsManager: SettingsManager;\n\tshowStatus(message: string): void;\n\tshowError(errorMessage: string): void;\n}\n\nexport interface ConfigRestoreHost extends ConfigBackupHost {\n\tshowSelector(create: (done: () => void) => { component: Component; focus: Component }): void;\n\thandleReloadCommand(): Promise<boolean>;\n}\n\nconst PROFILE_THINKING_LEVELS = new Set<ThinkingLevel>([\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n\t\"ultra\",\n]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction decodeBackupProfile(name: string, value: unknown): ProfileDefinitionInput | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst isWrapper = Object.hasOwn(value, \"resources\");\n\tconst resources = isWrapper ? value.resources : value;\n\tif (!isRecord(resources)) return undefined;\n\tconst thinking =\n\t\ttypeof value.thinking === \"string\" && PROFILE_THINKING_LEVELS.has(value.thinking as ThinkingLevel)\n\t\t\t? (value.thinking as ThinkingLevel)\n\t\t\t: undefined;\n\treturn {\n\t\tname,\n\t\tdescription: typeof value.description === \"string\" ? value.description : undefined,\n\t\tmodel: typeof value.model === \"string\" ? value.model : undefined,\n\t\tthinking,\n\t\tmodelRouter: isRecord(value.modelRouter) ? (value.modelRouter as ModelRouterSettings) : undefined,\n\t\tsoul: typeof value.soul === \"string\" ? value.soul : undefined,\n\t\tresources: resources as ResourceProfileSettings,\n\t};\n}\n\nexport interface ProfileFilesSnapshot {\n\tdirectoryExisted: boolean;\n\tfiles: Map<string, string>;\n}\n\nfunction canonicalActiveResourceProfiles(settings: Settings): string[] {\n\tconst values = settings.activeResourceProfiles ?? settings.activeResourceProfile ?? [];\n\tconst entries = Array.isArray(values) ? values : [values];\n\treturn [...new Set(entries.map((entry) => entry.trim()).filter((entry) => entry.length > 0))];\n}\n\nfunction decodeStringArray(label: string, value: unknown): string[] {\n\tif (!Array.isArray(value) || value.some((entry) => typeof entry !== \"string\")) {\n\t\tthrow new Error(`Invalid backup file: ${label} must be an array of strings`);\n\t}\n\treturn [...new Set(value.map((entry) => entry.trim()).filter((entry) => entry.length > 0))];\n}\n\nfunction decodeActiveResourceProfiles(settings: Record<string, unknown>): string[] {\n\tif (Object.hasOwn(settings, \"activeResourceProfiles\")) {\n\t\treturn decodeStringArray(\"settings.activeResourceProfiles\", settings.activeResourceProfiles);\n\t}\n\tconst legacy = settings.activeResourceProfile;\n\tif (legacy === undefined) return [];\n\tif (typeof legacy === \"string\") return legacy.trim() ? [legacy.trim()] : [];\n\treturn decodeStringArray(\"settings.activeResourceProfile\", legacy);\n}\n\nfunction decodeResourceProfiles(value: unknown): Record<string, ProfileDefinitionInput> {\n\tif (value === undefined) return {};\n\tif (!isRecord(value)) {\n\t\tthrow new Error(\"Invalid backup file: settings.resourceProfiles must be an object\");\n\t}\n\tconst profiles: Record<string, ProfileDefinitionInput> = {};\n\tfor (const [name, definition] of Object.entries(value)) {\n\t\tconst decoded = decodeBackupProfile(name, definition);\n\t\tif (!decoded) throw new Error(`Invalid backup file: malformed resource profile \"${name}\"`);\n\t\tprofiles[name] = decoded;\n\t}\n\treturn profiles;\n}\n\nfunction decodeProfileFiles(value: unknown): Record<string, unknown> {\n\tif (value === undefined) return {};\n\tif (!isRecord(value)) throw new Error(\"Invalid backup file: profiles must be an object\");\n\tfor (const filename of Object.keys(value)) {\n\t\tif (path.basename(filename) !== filename || !filename.endsWith(\".json\")) {\n\t\t\tthrow new Error(`Invalid backup file: unsafe profile filename \"${filename}\"`);\n\t\t}\n\t}\n\treturn value;\n}\n\nexport function captureProfileFiles(profilesDir: string): ProfileFilesSnapshot {\n\tconst files = new Map<string, string>();\n\tif (!fs.existsSync(profilesDir)) return { directoryExisted: false, files };\n\tfor (const filename of fs.readdirSync(profilesDir)) {\n\t\tif (!filename.endsWith(\".json\")) continue;\n\t\tconst profilePath = path.join(profilesDir, filename);\n\t\tif (!fs.lstatSync(profilePath).isFile()) continue;\n\t\tfiles.set(filename, fs.readFileSync(profilePath, \"utf-8\"));\n\t}\n\treturn { directoryExisted: true, files };\n}\n\nfunction clearProfileFiles(profilesDir: string): void {\n\tif (!fs.existsSync(profilesDir)) return;\n\tfor (const filename of fs.readdirSync(profilesDir)) {\n\t\tif (!filename.endsWith(\".json\")) continue;\n\t\tconst profilePath = path.join(profilesDir, filename);\n\t\tconst stat = fs.lstatSync(profilePath);\n\t\tif (stat.isFile() || stat.isSymbolicLink()) fs.rmSync(profilePath, { force: true });\n\t}\n}\n\nfunction replaceProfileFiles(profilesDir: string, profiles: Record<string, unknown>): void {\n\tfs.mkdirSync(profilesDir, { recursive: true });\n\tclearProfileFiles(profilesDir);\n\tfor (const [filename, content] of Object.entries(profiles)) {\n\t\tconst serialized = JSON.stringify(content, null, 2);\n\t\tif (serialized === undefined) throw new Error(`Invalid backup file: profile \"${filename}\" is not serializable`);\n\t\tfs.writeFileSync(path.join(profilesDir, filename), serialized, \"utf-8\");\n\t}\n}\n\nexport function restoreProfileFiles(profilesDir: string, snapshot: ProfileFilesSnapshot): void {\n\tclearProfileFiles(profilesDir);\n\tif (snapshot.files.size > 0) fs.mkdirSync(profilesDir, { recursive: true });\n\tfor (const [filename, content] of snapshot.files) {\n\t\tfs.writeFileSync(path.join(profilesDir, filename), content, \"utf-8\");\n\t}\n\tif (!snapshot.directoryExisted && fs.existsSync(profilesDir) && fs.readdirSync(profilesDir).length === 0) {\n\t\tfs.rmdirSync(profilesDir);\n\t}\n}\n\nfunction profileConfigurationFromSettings(settings: Settings): GlobalResourceProfileConfiguration {\n\treturn {\n\t\tresourceProfiles: settings.resourceProfiles,\n\t\tactiveResourceProfile: settings.activeResourceProfile,\n\t\tactiveResourceProfiles: settings.activeResourceProfiles,\n\t\texternalResourceRoots: settings.externalResourceRoots,\n\t\ttrustedResourceRoots: settings.trustedResourceRoots,\n\t};\n}\n\nexport async function handleConfigBackupCommand(host: ConfigBackupHost, fileArg?: string): Promise<void> {\n\ttry {\n\t\tconst profilesDir = path.join(getAgentDir(), \"profiles\");\n\t\tconst profiles: Record<string, unknown> = {};\n\t\tif (fs.existsSync(profilesDir)) {\n\t\t\tconst entries = fs.readdirSync(profilesDir);\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.endsWith(\".json\")) {\n\t\t\t\t\tconst pPath = path.join(profilesDir, entry);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (!fs.lstatSync(pPath).isFile()) continue;\n\t\t\t\t\t\tconst content = fs.readFileSync(pPath, \"utf-8\");\n\t\t\t\t\t\tprofiles[entry] = JSON.parse(content);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// skip\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst globalSettings = host.settingsManager.getGlobalSettings();\n\t\tconst backupData = {\n\t\t\tversion: 2,\n\t\t\tprofiles,\n\t\t\tsettings: {\n\t\t\t\tresourceProfiles: globalSettings.resourceProfiles ?? {},\n\t\t\t\tactiveResourceProfiles: canonicalActiveResourceProfiles(globalSettings),\n\t\t\t\texternalResourceRoots: globalSettings.externalResourceRoots ?? [],\n\t\t\t\ttrustedResourceRoots: globalSettings.trustedResourceRoots ?? [],\n\t\t\t},\n\t\t};\n\n\t\tlet targetFile = fileArg;\n\t\tif (!targetFile) {\n\t\t\tconst backupsDir = path.join(getAgentDir(), \"backups\");\n\t\t\tfs.mkdirSync(backupsDir, { recursive: true });\n\t\t\tconst timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n\t\t\ttargetFile = path.join(backupsDir, `config-${timestamp}.json`);\n\t\t} else {\n\t\t\tconst resolved = host.settingsManager.canonicalizePath(targetFile);\n\t\t\tif (resolved) {\n\t\t\t\ttargetFile = resolved;\n\t\t\t}\n\t\t}\n\n\t\tfs.mkdirSync(path.dirname(targetFile), { recursive: true });\n\t\tfs.writeFileSync(targetFile, JSON.stringify(backupData, null, 2), \"utf-8\");\n\t\thost.showStatus(`Configuration backup saved to ${targetFile}`);\n\t} catch (error) {\n\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t}\n}\n\nexport async function handleConfigRestoreCommand(host: ConfigRestoreHost, fileArg: string): Promise<void> {\n\ttry {\n\t\tconst trimmed = fileArg.trim();\n\t\tif (!trimmed) {\n\t\t\thost.showError(\"Usage: /config-restore <file>\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst resolved = host.settingsManager.canonicalizePath(trimmed);\n\t\tif (!resolved || !fs.existsSync(resolved)) {\n\t\t\thost.showError(`Backup file does not exist: ${trimmed}`);\n\t\t\treturn;\n\t\t}\n\n\t\tlet bundle: unknown;\n\t\ttry {\n\t\t\tconst content = fs.readFileSync(resolved, \"utf-8\");\n\t\t\tbundle = JSON.parse(content);\n\t\t} catch (error) {\n\t\t\thost.showError(`Failed to parse backup file: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!isRecord(bundle)) {\n\t\t\thost.showError(\"Invalid backup file: must be a JSON object\");\n\t\t\treturn;\n\t\t}\n\n\t\tlet profileFiles: Record<string, unknown>;\n\t\tlet restoredResourceProfiles: Record<string, ProfileDefinitionInput>;\n\t\tlet restoredActiveProfiles: string[];\n\t\tlet restoredExternalRoots: string[];\n\t\ttry {\n\t\t\tprofileFiles = decodeProfileFiles(bundle.profiles);\n\t\t\tconst backupSettings = isRecord(bundle.settings) ? bundle.settings : {};\n\t\t\trestoredResourceProfiles = decodeResourceProfiles(backupSettings.resourceProfiles);\n\t\t\trestoredActiveProfiles = decodeActiveResourceProfiles(backupSettings);\n\t\t\trestoredExternalRoots = decodeStringArray(\n\t\t\t\t\"settings.externalResourceRoots\",\n\t\t\t\tbackupSettings.externalResourceRoots ?? [],\n\t\t\t);\n\t\t} catch (error) {\n\t\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t\t\treturn;\n\t\t}\n\n\t\t// Confirm before clobbering\n\t\tconst confirm = await new Promise<boolean>((resolve) => {\n\t\t\thost.showSelector((done) => {\n\t\t\t\tconst submenu = new SelectSubmenu(\n\t\t\t\t\t\"Restore configuration?\",\n\t\t\t\t\t\"This will overwrite existing local profiles and settings with the backup values. Do you want to continue?\",\n\t\t\t\t\t[\n\t\t\t\t\t\t{ value: \"yes\", label: \"Yes\", description: \"Proceed with restoration.\" },\n\t\t\t\t\t\t{ value: \"no\", label: \"No\", description: \"Cancel and abort.\" },\n\t\t\t\t\t],\n\t\t\t\t\t\"no\",\n\t\t\t\t\t(value) => {\n\t\t\t\t\t\tdone();\n\t\t\t\t\t\tresolve(value === \"yes\");\n\t\t\t\t\t},\n\t\t\t\t\t() => {\n\t\t\t\t\t\tdone();\n\t\t\t\t\t\tresolve(false);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\treturn { component: submenu, focus: submenu.getSelectList() };\n\t\t\t});\n\t\t});\n\n\t\tif (!confirm) {\n\t\t\thost.showStatus(\"Restore aborted.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst profilesDir = path.join(getAgentDir(), \"profiles\");\n\t\tconst profileFilesSnapshot = captureProfileFiles(profilesDir);\n\t\tconst settingsSnapshot = host.settingsManager.createReloadSnapshot();\n\t\tlet restoreStarted = false;\n\t\ttry {\n\t\t\trestoreStarted = true;\n\t\t\treplaceProfileFiles(profilesDir, profileFiles);\n\t\t\tconst currentTrusted = settingsSnapshot.globalSettings.trustedResourceRoots ?? [];\n\t\t\thost.settingsManager.replaceGlobalResourceProfileConfiguration({\n\t\t\t\tresourceProfiles: Object.keys(restoredResourceProfiles).length > 0 ? restoredResourceProfiles : undefined,\n\t\t\t\tactiveResourceProfiles: restoredActiveProfiles,\n\t\t\t\texternalResourceRoots: restoredExternalRoots,\n\t\t\t\t// Security boundary: roots named by a backup never become trusted through restore.\n\t\t\t\ttrustedResourceRoots: currentTrusted.filter((root) => !restoredExternalRoots.includes(root)),\n\t\t\t});\n\t\t\tawait host.settingsManager.flush();\n\t\t\tif (!(await host.handleReloadCommand())) {\n\t\t\t\tthrow new Error(\"restored configuration failed runtime validation\");\n\t\t\t}\n\t\t\thost.showStatus(\"Configuration restored successfully.\");\n\t\t} catch (error) {\n\t\t\tlet rollbackError: unknown;\n\t\t\tif (restoreStarted) {\n\t\t\t\ttry {\n\t\t\t\t\trestoreProfileFiles(profilesDir, profileFilesSnapshot);\n\t\t\t\t\thost.settingsManager.replaceGlobalResourceProfileConfiguration(\n\t\t\t\t\t\tprofileConfigurationFromSettings(settingsSnapshot.globalSettings),\n\t\t\t\t\t);\n\t\t\t\t\tawait host.settingsManager.flush();\n\t\t\t\t\thost.settingsManager.restoreReloadSnapshot(settingsSnapshot);\n\t\t\t\t} catch (restoreError) {\n\t\t\t\t\trollbackError = restoreError;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\thost.showError(\n\t\t\t\trollbackError\n\t\t\t\t\t? `Configuration restore failed: ${message}; rollback failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`\n\t\t\t\t\t: `Configuration restore failed: ${message}; previous configuration restored`,\n\t\t\t);\n\t\t}\n\t} catch (error) {\n\t\thost.showError(error instanceof Error ? error.message : String(error));\n\t}\n}\n"]}
|
|
@@ -334,8 +334,8 @@ export declare class InteractiveMode {
|
|
|
334
334
|
private showSessionSelector;
|
|
335
335
|
private handleResumeSession;
|
|
336
336
|
private handleReloadCommand;
|
|
337
|
+
private handleReloadCommandWithResult;
|
|
337
338
|
private refreshUIAfterExtensionsChanged;
|
|
338
|
-
private reconcileExtensionsAndRefreshUI;
|
|
339
339
|
private handleExportCommand;
|
|
340
340
|
private getPathCommandArgument;
|
|
341
341
|
private handleImportCommand;
|