swarmkit 0.0.6 → 0.0.8
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/dist/cli.js +2 -0
- package/dist/commands/configure/configure.d.ts +5 -0
- package/dist/commands/configure/configure.js +354 -0
- package/dist/commands/configure/configure.test.d.ts +1 -0
- package/dist/commands/configure/configure.test.js +539 -0
- package/dist/commands/configure/read-config.d.ts +12 -0
- package/dist/commands/configure/read-config.js +81 -0
- package/dist/commands/configure.d.ts +2 -0
- package/dist/commands/configure.js +14 -0
- package/dist/commands/init/phases/configure.js +0 -21
- package/dist/commands/init/phases/global-setup.d.ts +1 -1
- package/dist/commands/init/phases/global-setup.js +22 -44
- package/dist/commands/init/phases/integrations.d.ts +16 -0
- package/dist/commands/init/phases/integrations.js +172 -0
- package/dist/commands/init/phases/package-config.d.ts +10 -0
- package/dist/commands/init/phases/package-config.js +117 -0
- package/dist/commands/init/phases/phases.test.d.ts +1 -0
- package/dist/commands/init/phases/phases.test.js +711 -0
- package/dist/commands/init/phases/project.js +17 -0
- package/dist/commands/init/phases/review.d.ts +8 -0
- package/dist/commands/init/phases/review.js +79 -0
- package/dist/commands/init/phases/use-case.js +41 -27
- package/dist/commands/init/phases/wizard-flow.test.d.ts +1 -0
- package/dist/commands/init/phases/wizard-flow.test.js +657 -0
- package/dist/commands/init/phases/wizard-modes.test.d.ts +1 -0
- package/dist/commands/init/phases/wizard-modes.test.js +270 -0
- package/dist/commands/init/state.d.ts +31 -1
- package/dist/commands/init/state.js +4 -0
- package/dist/commands/init/state.test.js +7 -0
- package/dist/commands/init/wizard.d.ts +1 -0
- package/dist/commands/init/wizard.js +31 -23
- package/dist/commands/init.js +2 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/packages/registry.d.ts +66 -0
- package/dist/packages/registry.js +258 -0
- package/dist/packages/setup.d.ts +42 -0
- package/dist/packages/setup.js +244 -15
- package/dist/packages/setup.test.js +520 -13
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { join, dirname } from "node:path";
|
|
|
6
6
|
import { registerInitCommand } from "./commands/init.js";
|
|
7
7
|
import { registerAddCommand } from "./commands/add.js";
|
|
8
8
|
import { registerRemoveCommand } from "./commands/remove.js";
|
|
9
|
+
import { registerConfigureCommand } from "./commands/configure.js";
|
|
9
10
|
import { registerStatusCommand } from "./commands/status.js";
|
|
10
11
|
import { registerUpdateCommand } from "./commands/update.js";
|
|
11
12
|
import { registerDoctorCommand } from "./commands/doctor.js";
|
|
@@ -23,6 +24,7 @@ program
|
|
|
23
24
|
registerInitCommand(program);
|
|
24
25
|
registerAddCommand(program);
|
|
25
26
|
registerRemoveCommand(program);
|
|
27
|
+
registerConfigureCommand(program);
|
|
26
28
|
registerStatusCommand(program);
|
|
27
29
|
registerUpdateCommand(program);
|
|
28
30
|
registerDoctorCommand(program);
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { confirm, input, select, password } from "@inquirer/prompts";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { readConfig, isFirstRun, getConfigPath } from "../../config/global.js";
|
|
7
|
+
import { PACKAGES, getActiveIntegrations, isKnownPackage, } from "../../packages/registry.js";
|
|
8
|
+
import { runPackageWizard, relocateAfterWizard, projectConfigDirs, PREFIX_ENV_VARS, PROJECT_ROOT, GLOBAL_CONFIG_DIRS, } from "../../packages/setup.js";
|
|
9
|
+
import { readPackageConfig, getNestedValue } from "./read-config.js";
|
|
10
|
+
import { createEmptyState } from "../init/state.js";
|
|
11
|
+
import * as ui from "../../utils/ui.js";
|
|
12
|
+
export async function runConfigure(opts) {
|
|
13
|
+
if (isFirstRun()) {
|
|
14
|
+
ui.fail("No swarmkit config found. Run `swarmkit init` first.");
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const globalConfig = readConfig();
|
|
19
|
+
if (globalConfig.installedPackages.length === 0) {
|
|
20
|
+
// Config file exists but has no packages — likely corrupted or manually emptied
|
|
21
|
+
const configPath = getConfigPath();
|
|
22
|
+
ui.fail(`Config file exists at ${configPath} but no packages are registered.`);
|
|
23
|
+
ui.info("Run `swarmkit init --global` to re-run setup, or `swarmkit doctor` to diagnose.");
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.log();
|
|
28
|
+
console.log(` ${chalk.bold("swarmkit configure")} ${chalk.dim("— reconfigure package settings")}`);
|
|
29
|
+
console.log();
|
|
30
|
+
// Build a WizardState from existing global config
|
|
31
|
+
const state = createEmptyState();
|
|
32
|
+
state.selectedPackages = [...globalConfig.installedPackages];
|
|
33
|
+
state.usePrefix = globalConfig.usePrefix ?? true;
|
|
34
|
+
state.embeddingProvider = globalConfig.embeddingProvider ?? null;
|
|
35
|
+
state.quick = opts.quick ?? false;
|
|
36
|
+
const cwd = process.cwd();
|
|
37
|
+
// Determine which packages to configure
|
|
38
|
+
let packagesToConfig;
|
|
39
|
+
if (opts.packageName) {
|
|
40
|
+
if (!isKnownPackage(opts.packageName)) {
|
|
41
|
+
ui.fail(`Unknown package: ${opts.packageName}`);
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (!globalConfig.installedPackages.includes(opts.packageName)) {
|
|
46
|
+
ui.fail(`${opts.packageName} is not installed. Run \`swarmkit add ${opts.packageName}\` first.`);
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
packagesToConfig = [opts.packageName];
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// All installed packages that have setup config
|
|
54
|
+
packagesToConfig = state.selectedPackages.filter((pkg) => PACKAGES[pkg]?.setup);
|
|
55
|
+
}
|
|
56
|
+
if (packagesToConfig.length === 0) {
|
|
57
|
+
ui.info("No configurable packages found.");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// ─── Per-package configuration ────────────────────────────────────
|
|
61
|
+
let changedPackages = [];
|
|
62
|
+
for (const pkgName of packagesToConfig) {
|
|
63
|
+
const pkg = PACKAGES[pkgName];
|
|
64
|
+
if (!pkg?.setup) {
|
|
65
|
+
ui.info(`${pkgName} has no configurable settings.`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const setupConfig = pkg.setup;
|
|
69
|
+
const existingConfig = readPackageConfig(pkgName, cwd);
|
|
70
|
+
const config = { values: {}, usedCliWizard: false };
|
|
71
|
+
console.log();
|
|
72
|
+
console.log(` ${chalk.bold(pkgName)} ${chalk.dim(pkg.description)}`);
|
|
73
|
+
if (existingConfig) {
|
|
74
|
+
ui.info("Current config loaded.");
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
ui.info("No existing config found — using defaults.");
|
|
78
|
+
}
|
|
79
|
+
// Offer CLI wizard
|
|
80
|
+
if (setupConfig.cliWizard) {
|
|
81
|
+
const useWizard = await confirm({
|
|
82
|
+
message: `Run ${pkgName}'s interactive setup wizard?`,
|
|
83
|
+
default: false,
|
|
84
|
+
});
|
|
85
|
+
if (useWizard) {
|
|
86
|
+
// Build prefix-aware env vars for project-level packages
|
|
87
|
+
const wizardConfig = { ...setupConfig.cliWizard };
|
|
88
|
+
if (state.usePrefix && PREFIX_ENV_VARS[pkgName]) {
|
|
89
|
+
const envVar = PREFIX_ENV_VARS[pkgName];
|
|
90
|
+
const prefixDir = PROJECT_ROOT + "/" +
|
|
91
|
+
(pkgName === "opentasks" ? "opentasks" :
|
|
92
|
+
pkgName === "self-driving-repo" ? "self-driving" :
|
|
93
|
+
pkgName === "openteams" ? "openteams" :
|
|
94
|
+
pkgName === "cognitive-core" ? "cognitive-core" : pkgName);
|
|
95
|
+
wizardConfig.env = {
|
|
96
|
+
...wizardConfig.env,
|
|
97
|
+
[envVar]: pkgName === "cognitive-core"
|
|
98
|
+
? join(cwd, prefixDir)
|
|
99
|
+
: prefixDir,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const result = await runPackageWizard(pkgName, wizardConfig, state, { cwd });
|
|
103
|
+
if (result.success) {
|
|
104
|
+
relocateAfterWizard(cwd, pkgName, state.usePrefix);
|
|
105
|
+
config.usedCliWizard = true;
|
|
106
|
+
ui.success(`${pkgName} wizard completed.`);
|
|
107
|
+
state.packageConfigs[pkgName] = config;
|
|
108
|
+
changedPackages.push(pkgName);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
ui.warn(`${pkgName} wizard failed: ${result.message ?? "unknown error"}`);
|
|
113
|
+
ui.info("Falling back to inline configuration.");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Inline options — show current values as defaults
|
|
118
|
+
if (setupConfig.inlineOptions && setupConfig.inlineOptions.length > 0) {
|
|
119
|
+
const customize = await confirm({
|
|
120
|
+
message: `Update ${pkgName} settings?`,
|
|
121
|
+
default: true,
|
|
122
|
+
});
|
|
123
|
+
if (customize) {
|
|
124
|
+
let changed = false;
|
|
125
|
+
for (const option of setupConfig.inlineOptions) {
|
|
126
|
+
if (option.when && !option.when(state.selectedPackages)) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
// Resolve current value from existing config
|
|
130
|
+
const currentValue = existingConfig
|
|
131
|
+
? getNestedValue(existingConfig, option.key)
|
|
132
|
+
: undefined;
|
|
133
|
+
const effectiveDefault = currentValue ?? option.default;
|
|
134
|
+
const value = await promptOption(option, effectiveDefault);
|
|
135
|
+
config.values[option.key] = value;
|
|
136
|
+
// Track if anything changed
|
|
137
|
+
if (value !== currentValue) {
|
|
138
|
+
changed = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (changed) {
|
|
142
|
+
changedPackages.push(pkgName);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
state.packageConfigs[pkgName] = config;
|
|
147
|
+
}
|
|
148
|
+
// ─── Apply changes ────────────────────────────────────────────────
|
|
149
|
+
if (changedPackages.length === 0) {
|
|
150
|
+
ui.blank();
|
|
151
|
+
ui.info("No changes made.");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// Apply inline config overrides to existing config files
|
|
155
|
+
for (const pkgName of changedPackages) {
|
|
156
|
+
const pkgConfig = state.packageConfigs[pkgName];
|
|
157
|
+
if (!pkgConfig || pkgConfig.usedCliWizard)
|
|
158
|
+
continue;
|
|
159
|
+
if (Object.keys(pkgConfig.values).length === 0)
|
|
160
|
+
continue;
|
|
161
|
+
const applied = applyConfigUpdates(pkgName, pkgConfig, cwd, state.usePrefix);
|
|
162
|
+
if (applied) {
|
|
163
|
+
ui.success(`${pkgName} config updated.`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// ─── Integration wiring ───────────────────────────────────────────
|
|
167
|
+
const activeIntegrations = getActiveIntegrations(state.selectedPackages);
|
|
168
|
+
const wirable = activeIntegrations.filter((i) => i.requiresWiring && i.configOptions && i.configOptions.length > 0);
|
|
169
|
+
if (wirable.length > 0 && !state.quick) {
|
|
170
|
+
const configureIntegrations = await confirm({
|
|
171
|
+
message: "Reconfigure integration wiring?",
|
|
172
|
+
default: false,
|
|
173
|
+
});
|
|
174
|
+
if (configureIntegrations) {
|
|
175
|
+
ui.heading(" Integration wiring");
|
|
176
|
+
for (const integration of wirable) {
|
|
177
|
+
const [a, b] = integration.packages;
|
|
178
|
+
console.log();
|
|
179
|
+
console.log(` ${chalk.bold(a)} ${chalk.dim("↔")} ${chalk.bold(b)}`);
|
|
180
|
+
ui.info(integration.description);
|
|
181
|
+
const enable = await confirm({
|
|
182
|
+
message: "Configure this integration?",
|
|
183
|
+
default: true,
|
|
184
|
+
});
|
|
185
|
+
if (enable && integration.configOptions) {
|
|
186
|
+
for (const option of integration.configOptions) {
|
|
187
|
+
// Read current value
|
|
188
|
+
const targetConfig = readPackageConfig(option.targetPackage, cwd);
|
|
189
|
+
const currentValue = targetConfig
|
|
190
|
+
? getNestedValue(targetConfig, option.configPath)
|
|
191
|
+
: undefined;
|
|
192
|
+
const effectiveDefault = currentValue ?? option.default;
|
|
193
|
+
const value = await promptIntegrationOption(option, effectiveDefault);
|
|
194
|
+
// Write directly to the target package's config
|
|
195
|
+
applyConfigUpdate(option.targetPackage, option.configPath, value, cwd, state.usePrefix);
|
|
196
|
+
}
|
|
197
|
+
ui.success(`${a} ↔ ${b} integration updated.`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// ─── Summary ──────────────────────────────────────────────────────
|
|
203
|
+
ui.blank();
|
|
204
|
+
ui.heading(" Updated");
|
|
205
|
+
for (const pkg of changedPackages) {
|
|
206
|
+
const config = state.packageConfigs[pkg];
|
|
207
|
+
const suffix = config?.usedCliWizard
|
|
208
|
+
? chalk.dim(" (via wizard)")
|
|
209
|
+
: chalk.dim(" (inline)");
|
|
210
|
+
ui.success(`${pkg}${suffix}`);
|
|
211
|
+
}
|
|
212
|
+
ui.blank();
|
|
213
|
+
}
|
|
214
|
+
// ─── Config file patching ─────────────────────────────────────────────────────
|
|
215
|
+
/**
|
|
216
|
+
* Apply all config overrides from a PackageConfig to the package's existing
|
|
217
|
+
* config file on disk.
|
|
218
|
+
*/
|
|
219
|
+
function applyConfigUpdates(pkg, pkgConfig, cwd, usePrefix) {
|
|
220
|
+
const configPath = resolveConfigPath(pkg, cwd, usePrefix);
|
|
221
|
+
if (!configPath)
|
|
222
|
+
return false;
|
|
223
|
+
try {
|
|
224
|
+
const config = existsSync(configPath)
|
|
225
|
+
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
226
|
+
: {};
|
|
227
|
+
for (const [key, value] of Object.entries(pkgConfig.values)) {
|
|
228
|
+
setNestedValue(config, key, value);
|
|
229
|
+
}
|
|
230
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Apply a single config key update to a package's config file.
|
|
239
|
+
*/
|
|
240
|
+
function applyConfigUpdate(pkg, keyPath, value, cwd, usePrefix) {
|
|
241
|
+
const configPath = resolveConfigPath(pkg, cwd, usePrefix);
|
|
242
|
+
if (!configPath)
|
|
243
|
+
return false;
|
|
244
|
+
try {
|
|
245
|
+
const config = existsSync(configPath)
|
|
246
|
+
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
247
|
+
: {};
|
|
248
|
+
setNestedValue(config, keyPath, value);
|
|
249
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Find the config file path for a package (project or global).
|
|
258
|
+
*/
|
|
259
|
+
function resolveConfigPath(pkg, cwd, usePrefix) {
|
|
260
|
+
const configDirs = projectConfigDirs(usePrefix);
|
|
261
|
+
const configFile = pkg === "sessionlog" ? "settings.json" : "config.json";
|
|
262
|
+
// Check project config first
|
|
263
|
+
const projectDir = configDirs[pkg];
|
|
264
|
+
if (projectDir) {
|
|
265
|
+
const projectPath = join(cwd, projectDir, configFile);
|
|
266
|
+
if (existsSync(projectPath))
|
|
267
|
+
return projectPath;
|
|
268
|
+
}
|
|
269
|
+
// Check global config
|
|
270
|
+
const globalDir = GLOBAL_CONFIG_DIRS[pkg];
|
|
271
|
+
if (globalDir) {
|
|
272
|
+
const globalPath = join(homedir(), globalDir, "config.json");
|
|
273
|
+
if (existsSync(globalPath))
|
|
274
|
+
return globalPath;
|
|
275
|
+
}
|
|
276
|
+
// Return project path even if it doesn't exist yet (for creation)
|
|
277
|
+
if (projectDir) {
|
|
278
|
+
return join(cwd, projectDir, configFile);
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Set a dotted key path in a nested object.
|
|
284
|
+
* Coerces numeric strings to numbers.
|
|
285
|
+
*/
|
|
286
|
+
function setNestedValue(obj, keyPath, value) {
|
|
287
|
+
const parts = keyPath.split(".");
|
|
288
|
+
let target = obj;
|
|
289
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
290
|
+
if (typeof target[parts[i]] !== "object" || target[parts[i]] === null) {
|
|
291
|
+
target[parts[i]] = {};
|
|
292
|
+
}
|
|
293
|
+
target = target[parts[i]];
|
|
294
|
+
}
|
|
295
|
+
const finalKey = parts[parts.length - 1];
|
|
296
|
+
if (typeof value === "string" && /^\d+(\.\d+)?$/.test(value)) {
|
|
297
|
+
target[finalKey] = parseFloat(value);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
target[finalKey] = value;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// ─── Prompt helpers ───────────────────────────────────────────────────────────
|
|
304
|
+
async function promptOption(option, currentDefault) {
|
|
305
|
+
const defaultValue = currentDefault ?? option.default;
|
|
306
|
+
switch (option.type) {
|
|
307
|
+
case "confirm":
|
|
308
|
+
return confirm({
|
|
309
|
+
message: option.label,
|
|
310
|
+
default: defaultValue,
|
|
311
|
+
});
|
|
312
|
+
case "select":
|
|
313
|
+
return select({
|
|
314
|
+
message: option.label,
|
|
315
|
+
choices: option.choices ?? [],
|
|
316
|
+
default: defaultValue,
|
|
317
|
+
});
|
|
318
|
+
case "input":
|
|
319
|
+
return input({
|
|
320
|
+
message: option.label,
|
|
321
|
+
default: defaultValue != null ? String(defaultValue) : undefined,
|
|
322
|
+
});
|
|
323
|
+
case "password":
|
|
324
|
+
return password({
|
|
325
|
+
message: option.label,
|
|
326
|
+
mask: "*",
|
|
327
|
+
});
|
|
328
|
+
default:
|
|
329
|
+
return defaultValue;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async function promptIntegrationOption(option, currentDefault) {
|
|
333
|
+
const defaultValue = currentDefault ?? option.default;
|
|
334
|
+
switch (option.type) {
|
|
335
|
+
case "confirm":
|
|
336
|
+
return confirm({
|
|
337
|
+
message: option.label,
|
|
338
|
+
default: defaultValue,
|
|
339
|
+
});
|
|
340
|
+
case "select":
|
|
341
|
+
return select({
|
|
342
|
+
message: option.label,
|
|
343
|
+
choices: option.choices ?? [],
|
|
344
|
+
default: defaultValue,
|
|
345
|
+
});
|
|
346
|
+
case "input":
|
|
347
|
+
return input({
|
|
348
|
+
message: option.label,
|
|
349
|
+
default: defaultValue != null ? String(defaultValue) : undefined,
|
|
350
|
+
});
|
|
351
|
+
default:
|
|
352
|
+
return defaultValue;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|