gnosys 5.11.4 → 5.12.2

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.
Files changed (265) hide show
  1. package/dist/cli.js +377 -5162
  2. package/dist/index.js +542 -244
  3. package/dist/lib/addCommand.d.ts +9 -0
  4. package/dist/lib/addCommand.js +102 -0
  5. package/dist/lib/addStructuredCommand.d.ts +16 -0
  6. package/dist/lib/addStructuredCommand.js +103 -0
  7. package/dist/lib/ambiguityCommand.d.ts +4 -0
  8. package/dist/lib/ambiguityCommand.js +36 -0
  9. package/dist/lib/apiKeyVault.d.ts +78 -0
  10. package/dist/lib/apiKeyVault.js +447 -0
  11. package/dist/lib/archive.js +0 -2
  12. package/dist/lib/askCommand.d.ts +13 -0
  13. package/dist/lib/askCommand.js +145 -0
  14. package/dist/lib/attachCommand.d.ts +17 -0
  15. package/dist/lib/attachCommand.js +66 -0
  16. package/dist/lib/attachments.d.ts +43 -2
  17. package/dist/lib/attachments.js +81 -2
  18. package/dist/lib/audioExtract.js +4 -1
  19. package/dist/lib/auditCommand.d.ts +7 -0
  20. package/dist/lib/auditCommand.js +27 -0
  21. package/dist/lib/backupCommand.d.ts +6 -0
  22. package/dist/lib/backupCommand.js +54 -0
  23. package/dist/lib/bootstrapCommand.d.ts +15 -0
  24. package/dist/lib/bootstrapCommand.js +51 -0
  25. package/dist/lib/briefingCommand.d.ts +7 -0
  26. package/dist/lib/briefingCommand.js +92 -0
  27. package/dist/lib/centralizeCommand.d.ts +5 -0
  28. package/dist/lib/centralizeCommand.js +16 -0
  29. package/dist/lib/chat/choose.js +2 -2
  30. package/dist/lib/chatCommand.d.ts +12 -0
  31. package/dist/lib/chatCommand.js +46 -0
  32. package/dist/lib/checkCommand.d.ts +4 -0
  33. package/dist/lib/checkCommand.js +133 -0
  34. package/dist/lib/clientReadOverlay.d.ts +27 -0
  35. package/dist/lib/clientReadOverlay.js +76 -0
  36. package/dist/lib/clientReadResolve.d.ts +32 -0
  37. package/dist/lib/clientReadResolve.js +84 -0
  38. package/dist/lib/commitContextCommand.d.ts +9 -0
  39. package/dist/lib/commitContextCommand.js +142 -0
  40. package/dist/lib/config.d.ts +41 -48
  41. package/dist/lib/config.js +58 -57
  42. package/dist/lib/configCommand.d.ts +10 -0
  43. package/dist/lib/configCommand.js +321 -0
  44. package/dist/lib/connectCommand.d.ts +8 -0
  45. package/dist/lib/connectCommand.js +19 -0
  46. package/dist/lib/db.d.ts +68 -1
  47. package/dist/lib/db.js +385 -120
  48. package/dist/lib/dbWrite.d.ts +1 -1
  49. package/dist/lib/dearchiveCommand.d.ts +7 -0
  50. package/dist/lib/dearchiveCommand.js +41 -0
  51. package/dist/lib/discoverCommand.d.ts +9 -0
  52. package/dist/lib/discoverCommand.js +87 -0
  53. package/dist/lib/doctorCommand.d.ts +6 -0
  54. package/dist/lib/doctorCommand.js +256 -0
  55. package/dist/lib/docxExtract.js +1 -1
  56. package/dist/lib/dream.d.ts +50 -2
  57. package/dist/lib/dream.js +324 -30
  58. package/dist/lib/dreamCommand.d.ts +10 -0
  59. package/dist/lib/dreamCommand.js +195 -0
  60. package/dist/lib/dreamLaunchd.d.ts +2 -0
  61. package/dist/lib/dreamLaunchd.js +72 -0
  62. package/dist/lib/dreamLogCommand.d.ts +10 -0
  63. package/dist/lib/dreamLogCommand.js +58 -0
  64. package/dist/lib/dreamReport.d.ts +7 -0
  65. package/dist/lib/dreamReport.js +114 -0
  66. package/dist/lib/dreamRunLog.d.ts +121 -0
  67. package/dist/lib/dreamRunLog.js +234 -0
  68. package/dist/lib/embeddings.js +3 -3
  69. package/dist/lib/exportCommand.d.ts +18 -0
  70. package/dist/lib/exportCommand.js +101 -0
  71. package/dist/lib/exportProject.d.ts +3 -2
  72. package/dist/lib/exportProject.js +2 -1
  73. package/dist/lib/federated.js +1 -1
  74. package/dist/lib/fsearchCommand.d.ts +8 -0
  75. package/dist/lib/fsearchCommand.js +44 -0
  76. package/dist/lib/graphCommand.d.ts +4 -0
  77. package/dist/lib/graphCommand.js +68 -0
  78. package/dist/lib/helperGenerateCommand.d.ts +5 -0
  79. package/dist/lib/helperGenerateCommand.js +27 -0
  80. package/dist/lib/historyCommand.d.ts +5 -0
  81. package/dist/lib/historyCommand.js +51 -0
  82. package/dist/lib/hybridSearchCommand.d.ts +12 -0
  83. package/dist/lib/hybridSearchCommand.js +95 -0
  84. package/dist/lib/importCommand.d.ts +16 -0
  85. package/dist/lib/importCommand.js +89 -0
  86. package/dist/lib/importProject.js +2 -1
  87. package/dist/lib/importProjectCommand.d.ts +6 -0
  88. package/dist/lib/importProjectCommand.js +43 -0
  89. package/dist/lib/ingestCommand.d.ts +13 -0
  90. package/dist/lib/ingestCommand.js +95 -0
  91. package/dist/lib/installOutput.d.ts +36 -0
  92. package/dist/lib/installOutput.js +55 -0
  93. package/dist/lib/lensCommand.d.ts +20 -0
  94. package/dist/lib/lensCommand.js +61 -0
  95. package/dist/lib/lensing.d.ts +1 -0
  96. package/dist/lib/lensing.js +50 -9
  97. package/dist/lib/linksCommand.d.ts +7 -0
  98. package/dist/lib/linksCommand.js +48 -0
  99. package/dist/lib/listCommand.d.ts +8 -0
  100. package/dist/lib/listCommand.js +74 -0
  101. package/dist/lib/llm.d.ts +1 -1
  102. package/dist/lib/llm.js +27 -9
  103. package/dist/lib/localDiskCheck.d.ts +17 -0
  104. package/dist/lib/localDiskCheck.js +54 -0
  105. package/dist/lib/lock.d.ts +1 -1
  106. package/dist/lib/lock.js +5 -3
  107. package/dist/lib/machineConfig.d.ts +11 -1
  108. package/dist/lib/machineConfig.js +16 -0
  109. package/dist/lib/machineRegistry.d.ts +61 -0
  110. package/dist/lib/machineRegistry.js +80 -0
  111. package/dist/lib/maintainCommand.d.ts +8 -0
  112. package/dist/lib/maintainCommand.js +34 -0
  113. package/dist/lib/masterLease.d.ts +20 -0
  114. package/dist/lib/masterLease.js +68 -0
  115. package/dist/lib/migrate.js +0 -1
  116. package/dist/lib/migrateCommand.d.ts +7 -0
  117. package/dist/lib/migrateCommand.js +158 -0
  118. package/dist/lib/migrateDbCommand.d.ts +9 -0
  119. package/dist/lib/migrateDbCommand.js +94 -0
  120. package/dist/lib/modelValidation.d.ts +5 -0
  121. package/dist/lib/modelValidation.js +27 -0
  122. package/dist/lib/multimodalIngest.js +1 -1
  123. package/dist/lib/openrouterTiers.d.ts +29 -0
  124. package/dist/lib/openrouterTiers.js +113 -0
  125. package/dist/lib/platform.d.ts +0 -6
  126. package/dist/lib/platform.js +0 -28
  127. package/dist/lib/prefCommand.d.ts +10 -0
  128. package/dist/lib/prefCommand.js +118 -0
  129. package/dist/lib/projectsCommand.d.ts +8 -0
  130. package/dist/lib/projectsCommand.js +131 -0
  131. package/dist/lib/readCommand.d.ts +7 -0
  132. package/dist/lib/readCommand.js +63 -0
  133. package/dist/lib/recall.d.ts +3 -0
  134. package/dist/lib/recall.js +19 -4
  135. package/dist/lib/recallCommand.d.ts +11 -0
  136. package/dist/lib/recallCommand.js +112 -0
  137. package/dist/lib/reflectCommand.d.ts +8 -0
  138. package/dist/lib/reflectCommand.js +61 -0
  139. package/dist/lib/reindexCommand.d.ts +4 -0
  140. package/dist/lib/reindexCommand.js +34 -0
  141. package/dist/lib/reindexGraphCommand.d.ts +4 -0
  142. package/dist/lib/reindexGraphCommand.js +12 -0
  143. package/dist/lib/reinforceCommand.d.ts +8 -0
  144. package/dist/lib/reinforceCommand.js +40 -0
  145. package/dist/lib/remote.d.ts +5 -1
  146. package/dist/lib/remote.js +5 -1
  147. package/dist/lib/remoteWizard.d.ts +24 -5
  148. package/dist/lib/remoteWizard.js +308 -319
  149. package/dist/lib/restoreCommand.d.ts +5 -0
  150. package/dist/lib/restoreCommand.js +35 -0
  151. package/dist/lib/rulesGen.d.ts +8 -0
  152. package/dist/lib/rulesGen.js +16 -0
  153. package/dist/lib/sandboxStartCommand.d.ts +6 -0
  154. package/dist/lib/sandboxStartCommand.js +25 -0
  155. package/dist/lib/sandboxStatusCommand.d.ts +4 -0
  156. package/dist/lib/sandboxStatusCommand.js +24 -0
  157. package/dist/lib/sandboxStopCommand.d.ts +4 -0
  158. package/dist/lib/sandboxStopCommand.js +21 -0
  159. package/dist/lib/search.d.ts +0 -2
  160. package/dist/lib/search.js +0 -7
  161. package/dist/lib/searchCommand.d.ts +9 -0
  162. package/dist/lib/searchCommand.js +90 -0
  163. package/dist/lib/semanticSearchCommand.d.ts +8 -0
  164. package/dist/lib/semanticSearchCommand.js +52 -0
  165. package/dist/lib/setup/configSetRender.js +2 -0
  166. package/dist/lib/setup/providerGlyphs.d.ts +19 -0
  167. package/dist/lib/setup/providerGlyphs.js +42 -0
  168. package/dist/lib/setup/remoteRender.d.ts +31 -1
  169. package/dist/lib/setup/remoteRender.js +95 -4
  170. package/dist/lib/setup/sections/providers.d.ts +17 -0
  171. package/dist/lib/setup/sections/providers.js +307 -0
  172. package/dist/lib/setup/sections/routing.d.ts +2 -6
  173. package/dist/lib/setup/sections/routing.js +67 -82
  174. package/dist/lib/setup/sections/taskRoutingEditor.d.ts +13 -0
  175. package/dist/lib/setup/sections/taskRoutingEditor.js +139 -0
  176. package/dist/lib/setup/summary.d.ts +9 -0
  177. package/dist/lib/setup/summary.js +51 -37
  178. package/dist/lib/setup/ui/header.js +0 -1
  179. package/dist/lib/setup.d.ts +105 -15
  180. package/dist/lib/setup.js +747 -287
  181. package/dist/lib/setupKeys.d.ts +42 -0
  182. package/dist/lib/setupKeys.js +564 -0
  183. package/dist/lib/setupRemoteCommand.d.ts +4 -0
  184. package/dist/lib/setupRemoteCommand.js +28 -0
  185. package/dist/lib/setupRemotePullCommand.d.ts +5 -0
  186. package/dist/lib/setupRemotePullCommand.js +52 -0
  187. package/dist/lib/setupRemotePushCommand.d.ts +5 -0
  188. package/dist/lib/setupRemotePushCommand.js +57 -0
  189. package/dist/lib/setupRemoteResolveCommand.d.ts +4 -0
  190. package/dist/lib/setupRemoteResolveCommand.js +48 -0
  191. package/dist/lib/setupRemoteStatusCommand.d.ts +4 -0
  192. package/dist/lib/setupRemoteStatusCommand.js +73 -0
  193. package/dist/lib/setupRemoteSyncCommand.d.ts +6 -0
  194. package/dist/lib/setupRemoteSyncCommand.js +65 -0
  195. package/dist/lib/setupSyncProjectsCommand.d.ts +4 -0
  196. package/dist/lib/setupSyncProjectsCommand.js +292 -0
  197. package/dist/lib/staleCommand.d.ts +8 -0
  198. package/dist/lib/staleCommand.js +34 -0
  199. package/dist/lib/statsCommand.d.ts +6 -0
  200. package/dist/lib/statsCommand.js +142 -0
  201. package/dist/lib/statusCommand.d.ts +18 -0
  202. package/dist/lib/statusCommand.js +250 -0
  203. package/dist/lib/storesCommand.d.ts +2 -0
  204. package/dist/lib/storesCommand.js +4 -0
  205. package/dist/lib/syncClient.d.ts +41 -0
  206. package/dist/lib/syncClient.js +234 -0
  207. package/dist/lib/syncCommand.d.ts +6 -0
  208. package/dist/lib/syncCommand.js +57 -0
  209. package/dist/lib/syncDoctorCommand.d.ts +5 -0
  210. package/dist/lib/syncDoctorCommand.js +100 -0
  211. package/dist/lib/syncIngest.d.ts +30 -0
  212. package/dist/lib/syncIngest.js +175 -0
  213. package/dist/lib/syncIngestLaunchd.d.ts +8 -0
  214. package/dist/lib/syncIngestLaunchd.js +93 -0
  215. package/dist/lib/syncIngestStartup.d.ts +5 -0
  216. package/dist/lib/syncIngestStartup.js +29 -0
  217. package/dist/lib/syncIngestSystemd.d.ts +10 -0
  218. package/dist/lib/syncIngestSystemd.js +97 -0
  219. package/dist/lib/syncIngestTimer.d.ts +8 -0
  220. package/dist/lib/syncIngestTimer.js +27 -0
  221. package/dist/lib/syncIngestTimerCommand.d.ts +7 -0
  222. package/dist/lib/syncIngestTimerCommand.js +83 -0
  223. package/dist/lib/syncLock.d.ts +6 -0
  224. package/dist/lib/syncLock.js +74 -0
  225. package/dist/lib/syncSnapshot.d.ts +32 -0
  226. package/dist/lib/syncSnapshot.js +188 -0
  227. package/dist/lib/syncStaging.d.ts +79 -0
  228. package/dist/lib/syncStaging.js +237 -0
  229. package/dist/lib/tagsAddCommand.d.ts +8 -0
  230. package/dist/lib/tagsAddCommand.js +18 -0
  231. package/dist/lib/tagsCommand.d.ts +4 -0
  232. package/dist/lib/tagsCommand.js +16 -0
  233. package/dist/lib/timelineCommand.d.ts +7 -0
  234. package/dist/lib/timelineCommand.js +49 -0
  235. package/dist/lib/traceCommand.d.ts +6 -0
  236. package/dist/lib/traceCommand.js +39 -0
  237. package/dist/lib/traverseCommand.d.ts +6 -0
  238. package/dist/lib/traverseCommand.js +58 -0
  239. package/dist/lib/updateCommand.d.ts +13 -0
  240. package/dist/lib/updateCommand.js +67 -0
  241. package/dist/lib/updateStatusCommand.d.ts +5 -0
  242. package/dist/lib/updateStatusCommand.js +38 -0
  243. package/dist/lib/webAddCommand.d.ts +8 -0
  244. package/dist/lib/webAddCommand.js +55 -0
  245. package/dist/lib/webBuildCommand.d.ts +10 -0
  246. package/dist/lib/webBuildCommand.js +65 -0
  247. package/dist/lib/webBuildIndexCommand.d.ts +8 -0
  248. package/dist/lib/webBuildIndexCommand.js +37 -0
  249. package/dist/lib/webIndex.js +0 -1
  250. package/dist/lib/webIngestCommand.d.ts +11 -0
  251. package/dist/lib/webIngestCommand.js +51 -0
  252. package/dist/lib/webInitCommand.d.ts +9 -0
  253. package/dist/lib/webInitCommand.js +167 -0
  254. package/dist/lib/webRemoveCommand.d.ts +5 -0
  255. package/dist/lib/webRemoveCommand.js +41 -0
  256. package/dist/lib/webStatusCommand.d.ts +5 -0
  257. package/dist/lib/webStatusCommand.js +94 -0
  258. package/dist/lib/webUpdateCommand.d.ts +7 -0
  259. package/dist/lib/webUpdateCommand.js +72 -0
  260. package/dist/lib/workingSetCommand.d.ts +6 -0
  261. package/dist/lib/workingSetCommand.js +37 -0
  262. package/dist/sandbox/client.js +1 -1
  263. package/dist/sandbox/manager.js +1 -14
  264. package/dist/sandbox/server.js +3 -5
  265. package/package.json +6 -2
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Setup: Providers — API keys and provider credentials.
3
+ *
4
+ * `gnosys setup providers` or summary menu row "providers".
5
+ */
6
+ import { loadConfig, updateConfig, } from "../../config.js";
7
+ import { apiKeyServiceName, deleteStoredSecret, listStoredKeySlots, maskKeySnippet, storeApiKeySecret, } from "../../apiKeyVault.js";
8
+ import { resolveActiveStorePath } from "../storePath.js";
9
+ import { safeQuestion } from "../ui/safePrompt.js";
10
+ import { Header } from "../ui/header.js";
11
+ import { Title } from "../ui/title.js";
12
+ import { Footer } from "../ui/footer.js";
13
+ import { printStatus } from "../ui/status.js";
14
+ import { c, color, glyph } from "../ui/tokens.js";
15
+ import { CLOUD_PROVIDERS_FOR_KEYS, LOCAL_PROVIDERS, renderProviderLabel, renderProviderMark, } from "../providerGlyphs.js";
16
+ async function ask(rl, prompt) {
17
+ return (await safeQuestion(rl, prompt)).trim();
18
+ }
19
+ async function askYesNo(rl, prompt, defaultYes = true) {
20
+ const hint = defaultYes ? " [Y/n] " : " [y/N] ";
21
+ const answer = (await ask(rl, prompt + hint)).toLowerCase();
22
+ if (!answer)
23
+ return defaultYes;
24
+ return answer === "y" || answer === "yes";
25
+ }
26
+ async function askChoice(rl, prompt, choices, defaultIdx = 0) {
27
+ console.log("");
28
+ if (prompt)
29
+ console.log(prompt);
30
+ choices.forEach((ch, i) => {
31
+ const marker = i === defaultIdx ? color(c.textDim, " (default)") : "";
32
+ console.log(` ${i + 1}. ${ch}${marker}`);
33
+ });
34
+ for (let attempts = 0; attempts < 5; attempts++) {
35
+ const answer = await ask(rl, ` ${color(c.accent, glyph.prompt)} `);
36
+ if (!answer)
37
+ return defaultIdx;
38
+ const n = parseInt(answer, 10);
39
+ if (!Number.isNaN(n) && n >= 1 && n <= choices.length)
40
+ return n - 1;
41
+ printStatus("warn", `pick a number 1–${choices.length}`);
42
+ }
43
+ return defaultIdx;
44
+ }
45
+ function slotScopeLabel(slot) {
46
+ if (slot.scope === "global")
47
+ return "global";
48
+ return "provider";
49
+ }
50
+ async function clearConfigProviderApiKey(storePath, provider) {
51
+ const cfg = await loadConfig(storePath);
52
+ const patch = {};
53
+ switch (provider) {
54
+ case "anthropic":
55
+ if (!cfg.llm.anthropic.apiKey)
56
+ return;
57
+ patch.anthropic = { ...cfg.llm.anthropic, apiKey: "" };
58
+ break;
59
+ case "openai":
60
+ if (!cfg.llm.openai.apiKey)
61
+ return;
62
+ patch.openai = { ...cfg.llm.openai, apiKey: "" };
63
+ break;
64
+ case "groq":
65
+ if (!cfg.llm.groq.apiKey)
66
+ return;
67
+ patch.groq = { ...cfg.llm.groq, apiKey: "" };
68
+ break;
69
+ case "xai":
70
+ if (!cfg.llm.xai.apiKey)
71
+ return;
72
+ patch.xai = { ...cfg.llm.xai, apiKey: "" };
73
+ break;
74
+ case "mistral":
75
+ if (!cfg.llm.mistral.apiKey)
76
+ return;
77
+ patch.mistral = { ...cfg.llm.mistral, apiKey: "" };
78
+ break;
79
+ case "openrouter":
80
+ if (!cfg.llm.openrouter.apiKey)
81
+ return;
82
+ patch.openrouter = { ...cfg.llm.openrouter, apiKey: "" };
83
+ break;
84
+ case "custom":
85
+ if (!cfg.llm.custom?.apiKey)
86
+ return;
87
+ patch.custom = { ...cfg.llm.custom, apiKey: "" };
88
+ break;
89
+ default:
90
+ return;
91
+ }
92
+ await updateConfig(storePath, { llm: patch });
93
+ }
94
+ function renderProviderRow(index, provider, cfg, local = false) {
95
+ const mark = renderProviderMark(provider);
96
+ const slots = listStoredKeySlots(cfg, provider);
97
+ const hasKey = slots.length > 0;
98
+ const status = local
99
+ ? color(c.textDim, "local · no key")
100
+ : hasKey
101
+ ? color(c.ok, `${glyph.ok} key`)
102
+ : color(c.textGhost, "no key");
103
+ const preview = hasKey && !local ? ` ${color(c.textDim, slots[0].preview ?? "")}` : "";
104
+ const num = color(c.textDim, String(index));
105
+ const name = color(c.text, provider.padEnd(12));
106
+ return ` ${num} ${mark} ${name} ${status}${preview}`;
107
+ }
108
+ async function manageProviderKeys(rl, storePath, cfg, provider) {
109
+ let changed = false;
110
+ while (true) {
111
+ const slots = listStoredKeySlots(cfg, provider);
112
+ console.log("");
113
+ console.log(Header(["gnosys", "setup", "providers", provider]));
114
+ console.log("");
115
+ console.log(Title(renderProviderLabel(provider), "stored credentials for this provider"));
116
+ console.log("");
117
+ if (slots.length === 0) {
118
+ console.log(` ${color(c.textDim, "No keys found for this provider.")}`);
119
+ console.log(` ${color(c.textDim, `Add one with rotate — saved as GNOSYS_GLOBAL_${provider.toUpperCase()}_KEY`)}`);
120
+ console.log("");
121
+ }
122
+ else {
123
+ for (const slot of slots) {
124
+ const scope = color(c.textMid, slotScopeLabel(slot).padEnd(14));
125
+ const src = color(c.textDim, slot.source.padEnd(22));
126
+ const prev = slot.preview ? color(c.text, slot.preview) : "";
127
+ console.log(` ${scope} ${src} ${prev}`);
128
+ console.log(` ${color(c.textGhost, " " + slot.service)}`);
129
+ }
130
+ console.log("");
131
+ }
132
+ const defaultAction = slots.length > 0 ? 2 : 0; // when they have a key, default to "make default + model"
133
+ const choice = await askChoice(rl, "Actions", [
134
+ slots.length === 0 ? "Add API key (global)" : "Rotate global API key",
135
+ "Delete one stored key…",
136
+ "Use this provider as default for everything + pick model",
137
+ "Back",
138
+ ], defaultAction);
139
+ if (choice === 3) {
140
+ return changed;
141
+ }
142
+ if (choice === 2) {
143
+ // "Use this provider as default for everything + pick model"
144
+ // Set it as the global default provider
145
+ const current = await loadConfig(storePath);
146
+ await updateConfig(storePath, {
147
+ llm: {
148
+ ...current.llm,
149
+ defaultProvider: provider,
150
+ },
151
+ // Clear per-task overrides so this truly becomes the single default for everything
152
+ taskModels: {},
153
+ });
154
+ printStatus("ok", `default provider set to ${provider} (all tasks now default to it)`);
155
+ // Immediately let them pick a model for it (using the same picker the main flow uses)
156
+ try {
157
+ const { fetchDynamicModels } = await import("../../setup.js");
158
+ const { pickModel } = await import("../../setup.js");
159
+ const dynamicModels = await fetchDynamicModels();
160
+ const tiers = dynamicModels[provider] ?? [];
161
+ let chosenModel;
162
+ if (tiers.length > 0) {
163
+ chosenModel = await pickModel(rl, provider, dynamicModels, `Default model for ${provider} (used for all tasks)`, current.llm[provider]?.model);
164
+ }
165
+ else {
166
+ chosenModel = await ask(rl, `Enter default model name for ${provider}: `);
167
+ }
168
+ if (chosenModel) {
169
+ const after = await loadConfig(storePath);
170
+ await updateConfig(storePath, {
171
+ llm: {
172
+ ...after.llm,
173
+ [provider]: {
174
+ ...(after.llm[provider] || {}),
175
+ model: chosenModel,
176
+ },
177
+ },
178
+ });
179
+ printStatus("ok", `default model set · ${provider} / ${chosenModel}`);
180
+ printStatus("progress", "all tasks will now default to this provider + model", "use task routing for per-task overrides");
181
+ changed = true;
182
+ }
183
+ else {
184
+ printStatus("progress", "model left unchanged — you can set it from task routing");
185
+ }
186
+ }
187
+ catch (_err) {
188
+ printStatus("warn", "could not pick model right now", "you can set a default model from the main setup → task routing");
189
+ }
190
+ cfg = await loadConfig(storePath);
191
+ continue;
192
+ }
193
+ if (choice === 0) {
194
+ const service = apiKeyServiceName(provider, "global");
195
+ console.log("");
196
+ console.log(` ${color(c.textDim, `Keychain: ${service}`)}`);
197
+ const key = await ask(rl, ` ${color(c.accent, glyph.prompt)} Enter ${provider} API key: `);
198
+ if (!key) {
199
+ printStatus("warn", "skipped");
200
+ continue;
201
+ }
202
+ if (storeApiKeySecret(service, key, provider)) {
203
+ const store = process.platform === "darwin" ? "macOS Keychain" : "GNOME Keyring";
204
+ printStatus("ok", "key saved", `${store} · ${maskKeySnippet(key)}`);
205
+ changed = true;
206
+ }
207
+ else {
208
+ printStatus("fail", "could not write to secure store");
209
+ }
210
+ continue;
211
+ }
212
+ if (choice === 1) {
213
+ if (slots.length === 0) {
214
+ printStatus("warn", "nothing to delete");
215
+ continue;
216
+ }
217
+ const labels = slots.map((s, i) => `${i + 1}. ${slotScopeLabel(s)} · ${s.service}`);
218
+ const pick = await askChoice(rl, "Delete which key?", [...labels, "Cancel"], labels.length);
219
+ if (pick >= slots.length)
220
+ continue;
221
+ const slot = slots[pick];
222
+ if (slot.service === "gnosys.json") {
223
+ if (await askYesNo(rl, "Clear API key from gnosys.json?", false)) {
224
+ await clearConfigProviderApiKey(storePath, provider);
225
+ printStatus("ok", "cleared gnosys.json apiKey");
226
+ changed = true;
227
+ cfg = await loadConfig(storePath);
228
+ }
229
+ continue;
230
+ }
231
+ if (slot.source.startsWith("$")) {
232
+ printStatus("warn", `unset ${slot.service} in your shell or ~/.config/gnosys/.env`, "env vars cannot be removed from here");
233
+ continue;
234
+ }
235
+ if (await askYesNo(rl, `Delete ${slot.service} from secure store?`, false)) {
236
+ if (deleteStoredSecret(slot.service)) {
237
+ printStatus("ok", "deleted", slot.service);
238
+ changed = true;
239
+ }
240
+ else {
241
+ printStatus("fail", "delete failed or entry not found");
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ /**
248
+ * Providers + API key management screen.
249
+ */
250
+ export async function runProvidersSetup(opts) {
251
+ const storePath = resolveActiveStorePath(opts.directory);
252
+ let cfg = await loadConfig(storePath);
253
+ let anyChange = false;
254
+ const menuProviders = [
255
+ ...CLOUD_PROVIDERS_FOR_KEYS,
256
+ ...LOCAL_PROVIDERS,
257
+ ];
258
+ while (true) {
259
+ console.log("");
260
+ console.log(Header(["gnosys", "setup", "providers"]));
261
+ console.log("");
262
+ console.log(Title("Providers", "API keys live here. Set a global default (or per-task models) from the main setup menu."));
263
+ console.log("");
264
+ for (let i = 0; i < menuProviders.length; i++) {
265
+ const p = menuProviders[i];
266
+ const local = LOCAL_PROVIDERS.includes(p);
267
+ console.log(renderProviderRow(i + 1, p, cfg, local));
268
+ }
269
+ console.log("");
270
+ console.log(` ${color(c.textDim, "Keys: global = GNOSYS_GLOBAL_<PROVIDER>_KEY (shared across tasks)")}`);
271
+ console.log(Footer("number · manage enter · back"));
272
+ const answer = await ask(opts.rl, ` ${color(c.accent, glyph.prompt)} `);
273
+ if (!answer) {
274
+ return anyChange;
275
+ }
276
+ const n = parseInt(answer, 10);
277
+ if (Number.isNaN(n) || n < 1 || n > menuProviders.length) {
278
+ printStatus("warn", `enter 1–${menuProviders.length} or press Enter to go back`);
279
+ continue;
280
+ }
281
+ const provider = menuProviders[n - 1];
282
+ if (LOCAL_PROVIDERS.includes(provider)) {
283
+ console.log("");
284
+ printStatus("ok", `${provider} is local`, "no API key required");
285
+ continue;
286
+ }
287
+ const changed = await manageProviderKeys(opts.rl, storePath, cfg, provider);
288
+ if (changed) {
289
+ anyChange = true;
290
+ cfg = await loadConfig(storePath);
291
+ }
292
+ }
293
+ }
294
+ /** Summary line: how many providers have keys. */
295
+ export async function describeProvidersSummary(cfg) {
296
+ const named = [];
297
+ for (const p of CLOUD_PROVIDERS_FOR_KEYS) {
298
+ if (listStoredKeySlots(cfg, p).length > 0) {
299
+ named.push(p);
300
+ }
301
+ }
302
+ if (named.length === 0)
303
+ return "no keys stored";
304
+ const preview = named.slice(0, 3).join(", ");
305
+ const suffix = named.length > 3 ? ` +${named.length - 3}` : "";
306
+ return `${named.length} with keys · ${preview}${suffix}`;
307
+ }
@@ -2,9 +2,7 @@
2
2
  * Setup: Task Routing.
3
3
  *
4
4
  * Standalone wizard for configuring per-task LLM routing
5
- * (structuring / synthesis / vision / transcription / dream).
6
- * Extracted from the linear `runSetup` flow so it can be invoked
7
- * directly via `gnosys setup routing` or from the summary-first menu.
5
+ * (structuring / synthesis / vision / transcription / chat / dream).
8
6
  */
9
7
  import type { Interface as ReadlineInterface } from "readline/promises";
10
8
  export interface RoutingOptions {
@@ -12,8 +10,6 @@ export interface RoutingOptions {
12
10
  directory: string;
13
11
  }
14
12
  /**
15
- * Run the task-routing wizard. Reads current config, walks the 3-way choice
16
- * (keep defaults / customize individual / use same for all), writes any
17
- * overrides via updateConfig(). Returns true if config was changed.
13
+ * Run the task-routing wizard.
18
14
  */
19
15
  export declare function runRoutingSetup(opts: RoutingOptions): Promise<boolean>;
@@ -2,17 +2,18 @@
2
2
  * Setup: Task Routing.
3
3
  *
4
4
  * Standalone wizard for configuring per-task LLM routing
5
- * (structuring / synthesis / vision / transcription / dream).
6
- * Extracted from the linear `runSetup` flow so it can be invoked
7
- * directly via `gnosys setup routing` or from the summary-first menu.
5
+ * (structuring / synthesis / vision / transcription / chat / dream).
8
6
  */
9
- import { loadConfig, updateConfig, resolveTaskModel, getProviderModel, } from "../../config.js";
7
+ import { loadConfig, updateConfig, getProviderModel, } from "../../config.js";
8
+ import { buildApiKeyRequirementsFromConfig, buildEffectiveRouting, ensureApiKeys, } from "../../apiKeyVault.js";
10
9
  import { safeQuestion } from "../ui/safePrompt.js";
11
10
  import { Header } from "../ui/header.js";
12
11
  import { Title } from "../ui/title.js";
13
12
  import { Footer } from "../ui/footer.js";
14
13
  import { printStatus } from "../ui/status.js";
15
14
  import { classifyCost, renderRoutingTable, renderRoutingDiff, } from "../routingRender.js";
15
+ import { runCommaListRoutingEditor } from "./taskRoutingEditor.js";
16
+ import { resolveActiveStorePath } from "../storePath.js";
16
17
  const DIM = "\x1b[2m";
17
18
  const RESET = "\x1b[0m";
18
19
  const TASKS = ["structuring", "synthesis", "chat", "vision", "transcription"];
@@ -45,23 +46,6 @@ async function askChoice(rl, prompt, choices, defaultIdx = 0) {
45
46
  }
46
47
  return defaultIdx;
47
48
  }
48
- function buildEffectiveRouting(cfg) {
49
- const out = {};
50
- for (const t of TASKS) {
51
- const r = resolveTaskModel(cfg, t);
52
- out[t] = { provider: r.provider, model: r.model };
53
- }
54
- out.dream = {
55
- provider: cfg.dream?.provider ?? "ollama",
56
- model: cfg.dream?.model ?? getProviderModel(cfg, (cfg.dream?.provider ?? "ollama")),
57
- };
58
- return out;
59
- }
60
- /**
61
- * Build the table-row payload from current routing data. Marks any
62
- * task whose effective provider/model differs from the snapshot as
63
- * `changed` so the renderer can highlight it (▶ in accent-hi).
64
- */
65
49
  function buildTaskRows(routing, baseline, dreamEnabled) {
66
50
  const rows = [];
67
51
  for (const t of [...TASKS, "dream"]) {
@@ -75,72 +59,68 @@ function buildTaskRows(routing, baseline, dreamEnabled) {
75
59
  return rows;
76
60
  }
77
61
  /**
78
- * Run the task-routing wizard. Reads current config, walks the 3-way choice
79
- * (keep defaults / customize individual / use same for all), writes any
80
- * overrides via updateConfig(). Returns true if config was changed.
62
+ * Run the task-routing wizard.
81
63
  */
82
64
  export async function runRoutingSetup(opts) {
83
- const cfg = await loadConfig(opts.directory);
65
+ const storePath = resolveActiveStorePath(opts.directory);
66
+ const cfg = await loadConfig(storePath);
84
67
  const provider = cfg.llm.defaultProvider;
85
68
  const model = getProviderModel(cfg, provider);
86
69
  console.log("");
87
70
  console.log(Header(["gnosys", "setup", "routing"]));
88
71
  console.log("");
89
- console.log(Title("Task routing", "each task can use a different model overrides the default"));
72
+ console.log(Title("Task routing", "pick provider + model per taskset API keys under setup providers"));
90
73
  console.log("");
91
74
  const dreamEnabled = !!cfg.dream?.enabled;
92
75
  const baseline = buildEffectiveRouting(cfg);
93
- // Initial table: nothing has changed yet, so every row is `changed: false`.
94
76
  const initialRows = buildTaskRows(baseline, baseline, dreamEnabled);
95
77
  console.log(renderRoutingTable(initialRows));
96
78
  console.log("");
97
- // v5.9.4 Bug 5 — clearer option copy. Option 1 keeps current routing
98
- // (skip, no changes). Option 2 customises individual tasks. Option 3
99
- // CLEARS all task overrides so every task falls back to the default
100
- // provider; we show a Diff() of what's being removed before committing.
101
79
  const choice = await askChoice(opts.rl, "What would you like to do?", [
102
80
  "Keep current routing (no changes)",
103
- "Customize individual tasks",
104
- "Reset all task overrides to use default",
81
+ "Edit tasks — set the same provider + model for all tasks (simple global default)",
82
+ "Edit tasks pick different providers/models for specific tasks (advanced)",
83
+ "Reset all task overrides to the current default (the one shown in the main setup summary)",
105
84
  ], 0);
106
85
  if (choice === 0) {
107
86
  console.log(`${DIM}No changes.${RESET}`);
108
87
  return false;
109
88
  }
110
- const newTaskModels = {
111
- ...(cfg.taskModels ?? {}),
112
- };
113
- let dreamProvider = (cfg.dream?.provider ?? "ollama");
114
- let dreamModel = cfg.dream?.model ?? "llama3.2";
115
- let dreamEnabledNew = dreamEnabled;
116
89
  if (choice === 1) {
117
- // Customize each task
118
- for (const t of TASKS) {
119
- const current = baseline[t];
120
- const keep = await askYesNo(opts.rl, `Keep ${t} → ${current.provider} / ${current.model}?`, true);
121
- if (!keep) {
122
- const p = await ask(opts.rl, ` Provider for ${t} (e.g. anthropic, openai, xai, ollama): `);
123
- const m = await ask(opts.rl, ` Model for ${t}: `);
124
- if (p && m)
125
- newTaskModels[t] = { provider: p, model: m };
90
+ // Edit tasks — set the same provider + model for all tasks (simple global default)
91
+ console.log("");
92
+ printStatus("progress", "setting a single default for all tasks…");
93
+ try {
94
+ const { fetchDynamicModels } = await import("../../setup.js");
95
+ const { pickModel } = await import("../../setup.js");
96
+ const dynamicModels = await fetchDynamicModels();
97
+ const currentModel = getProviderModel(cfg, provider);
98
+ const chosenModel = await pickModel(opts.rl, provider, dynamicModels, `Default model for ${provider} (used for all tasks + dream)`, currentModel);
99
+ if (chosenModel && chosenModel !== currentModel) {
100
+ const after = await loadConfig(storePath);
101
+ await updateConfig(storePath, {
102
+ llm: {
103
+ ...after.llm,
104
+ [provider]: {
105
+ ...(after.llm[provider] || {}),
106
+ model: chosenModel,
107
+ },
108
+ },
109
+ // Clear per-task overrides so everything truly uses the single default
110
+ taskModels: {},
111
+ });
112
+ printStatus("ok", `default set for everything · ${provider} / ${chosenModel}`);
126
113
  }
127
- }
128
- // Dream
129
- dreamEnabledNew = await askYesNo(opts.rl, "Enable dream mode?", dreamEnabled);
130
- if (dreamEnabledNew) {
131
- const keepDream = await askYesNo(opts.rl, `Keep dream → ${dreamProvider} / ${dreamModel}?`, true);
132
- if (!keepDream) {
133
- const p = (await ask(opts.rl, " Provider for dream: "));
134
- if (p)
135
- dreamProvider = p;
136
- dreamModel = (await ask(opts.rl, " Model for dream: ")) || dreamModel;
114
+ else {
115
+ printStatus("ok", "no change to the global default");
137
116
  }
138
117
  }
118
+ catch (err) {
119
+ printStatus("warn", "could not update default model", String(err));
120
+ }
121
+ return true;
139
122
  }
140
- else {
141
- // v5.9.4 Bug 5 — choice === 2 now means "reset all task overrides to use
142
- // default". Show the user what's about to be cleared (the rows currently
143
- // pinned to non-default providers) before committing.
123
+ if (choice === 3) {
144
124
  const { Diff } = await import("../ui/diff.js");
145
125
  const overridesBeingCleared = Object.entries(cfg.taskModels ?? {})
146
126
  .filter(([, v]) => v.provider !== provider || v.model !== model)
@@ -157,31 +137,29 @@ export async function runRoutingSetup(opts) {
157
137
  else {
158
138
  console.log(`${DIM}No overrides to clear — already using default everywhere.${RESET}`);
159
139
  }
160
- const confirmReset = await askYesNo(opts.rl, "Reset all task overrides?", true);
140
+ console.log(`${DIM}The current default is ${provider} / ${model} (set via the main setup "Default provider" or the simple global option above).${RESET}`);
141
+ const confirmReset = await askYesNo(opts.rl, "Reset all task overrides to the current default?", true);
161
142
  if (!confirmReset) {
162
143
  console.log(`${DIM}Cancelled.${RESET}`);
163
144
  return false;
164
145
  }
165
- // Clear every overridden task back to default by deleting the keys.
166
- for (const t of TASKS)
167
- delete newTaskModels[t];
146
+ await updateConfig(storePath, { taskModels: {} });
147
+ printStatus("ok", "routing reset", "all tasks now use the global default provider/model");
148
+ console.log(Footer("press enter to return"));
149
+ return true;
150
+ }
151
+ // choice === 2 → advanced per-task editor (the powerful comma-list path)
152
+ const patch = await runCommaListRoutingEditor(opts.rl, storePath, cfg);
153
+ if (!patch) {
154
+ return false;
168
155
  }
169
- // Persist
170
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
- await updateConfig(opts.directory, {
172
- taskModels: newTaskModels,
173
- dream: {
174
- ...(cfg.dream ?? {}),
175
- enabled: dreamEnabledNew,
176
- provider: dreamProvider,
177
- model: dreamModel,
178
- },
156
+ await updateConfig(storePath, {
157
+ taskModels: patch.taskModels,
158
+ dream: patch.dream,
179
159
  });
180
- // v5.9.3 Screen 4 — re-render the table with `▶` markers on changed rows
181
- // followed by a Diff() block summarizing what shipped. Then a final
182
- // status line that names the saved file.
183
- const updatedCfg = await loadConfig(opts.directory);
160
+ const updatedCfg = await loadConfig(storePath);
184
161
  const updatedRouting = buildEffectiveRouting(updatedCfg);
162
+ const dreamEnabledNew = !!updatedCfg.dream?.enabled;
185
163
  const finalRows = buildTaskRows(updatedRouting, baseline, dreamEnabledNew);
186
164
  console.log("");
187
165
  console.log(renderRoutingTable(finalRows));
@@ -196,8 +174,15 @@ export async function runRoutingSetup(opts) {
196
174
  }
197
175
  console.log(renderRoutingDiff(diffEntries));
198
176
  console.log("");
199
- printStatus("ok", "routing saved", `${opts.directory}/.gnosys/gnosys.json`);
200
- // Footer hint (right-aligned) for any follow-up navigation in the menu flow.
177
+ printStatus("ok", "routing saved", `${storePath}/gnosys.json`);
178
+ const keyReqs = buildApiKeyRequirementsFromConfig(updatedCfg);
179
+ if (keyReqs.length > 0) {
180
+ const askInput = async (r, prompt) => ask(r, `${prompt}: `);
181
+ await ensureApiKeys(opts.rl, keyReqs, askInput, {
182
+ dim: DIM,
183
+ reset: RESET,
184
+ });
185
+ }
201
186
  console.log(Footer("press enter to return"));
202
187
  return true;
203
188
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Comma-list task routing editor (provider + model per selected task).
3
+ */
4
+ import type { Interface as ReadlineInterface } from "readline/promises";
5
+ import type { GnosysConfig } from "../../config.js";
6
+ /**
7
+ * Let the user pick tasks by number, then provider + model for each.
8
+ * Returns a patch for taskModels + dream, or null if cancelled.
9
+ */
10
+ export declare function runCommaListRoutingEditor(rl: ReadlineInterface, storePath: string, cfg: GnosysConfig): Promise<{
11
+ taskModels: NonNullable<GnosysConfig["taskModels"]>;
12
+ dream: GnosysConfig["dream"];
13
+ } | null>;