@paradigma-inc/flywheel 0.1.93 → 0.1.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -4
- package/package.json +2 -1
- package/skills/flywheel-llm-proof-paper/SKILL.md +156 -0
- package/skills/flywheel-llm-proof-paper/agents/openai.yaml +4 -0
- package/skills/flywheel-llm-proof-paper/references/checks.md +60 -0
- package/skills/flywheel-llm-proof-paper/scripts/__pycache__/check_paper.cpython-311.pyc +0 -0
- package/skills/flywheel-llm-proof-paper/scripts/check_paper.py +1065 -0
- package/src/agents.mjs +5 -2
- package/src/cli.mjs +27 -3
- package/src/public-command-metadata.mjs +44 -0
- package/src/runtime/vendor/flywheel-cli-dist/commands/_wait-polling.d.ts +21 -0
- package/src/runtime/vendor/flywheel-cli-dist/commands/_wait-polling.js +68 -2
- package/src/runtime/vendor/flywheel-cli-dist/commands/_wait-polling.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/compute-acquire.js +2 -8
- package/src/runtime/vendor/flywheel-cli-dist/commands/compute-acquire.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/feedback-create.js +4 -0
- package/src/runtime/vendor/flywheel-cli-dist/commands/feedback-create.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry/compute.js +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry/compute.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry/resources.js +16 -0
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry/resources.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/generatedProductTelemetryContract.d.ts +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/generatedProductTelemetryContract.js +3 -3
- package/src/runtime/vendor/flywheel-cli-dist/generatedProductTelemetryContract.js.map +1 -1
- package/src/runtime/vendor/manifest.json +1 -1
- package/src/setup/shared/prior-mode-detect.mjs +76 -9
- package/src/unified-cli.mjs +54 -16
- package/src/update/cache.mjs +124 -0
- package/src/update/command.mjs +438 -0
- package/src/update/install-state.mjs +135 -0
- package/src/update/refresh.mjs +393 -0
- package/src/update/registry.mjs +35 -0
- package/src/update/version.mjs +59 -0
- package/src/update/warning.mjs +109 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { access } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
import { loadPackageVersion } from "../public-command-metadata.mjs";
|
|
7
|
+
import { resolveFlywheelBinaryOnPath as defaultResolveFlywheelBinaryOnPath } from "../setup/modes/cli.mjs";
|
|
8
|
+
import { discoverInstalledFlywheelState } from "./install-state.mjs";
|
|
9
|
+
import { fetchLatestVersionFromRegistry } from "./registry.mjs";
|
|
10
|
+
import {
|
|
11
|
+
renderRefreshPlan,
|
|
12
|
+
refreshExistingSetup,
|
|
13
|
+
resolveDiscoveryServerUrl,
|
|
14
|
+
} from "./refresh.mjs";
|
|
15
|
+
import {
|
|
16
|
+
compareInstalledToLatest,
|
|
17
|
+
FLYWHEEL_PACKAGE_NAME,
|
|
18
|
+
} from "./version.mjs";
|
|
19
|
+
|
|
20
|
+
const PACKAGE_SPEC = `${FLYWHEEL_PACKAGE_NAME}@latest`;
|
|
21
|
+
const PUBLIC_NPM_REGISTRY = "https://registry.npmjs.org/";
|
|
22
|
+
const PUBLIC_NPM_REGISTRY_ARGS = [
|
|
23
|
+
`--registry=${PUBLIC_NPM_REGISTRY}`,
|
|
24
|
+
`--@paradigma-inc:registry=${PUBLIC_NPM_REGISTRY}`,
|
|
25
|
+
];
|
|
26
|
+
const POSIX_MANUAL_INSTALL_COMMAND =
|
|
27
|
+
'npm install -g --prefix "${FLYWHEEL_INSTALL_PREFIX:-$HOME/.local}" --registry=https://registry.npmjs.org/ --@paradigma-inc:registry=https://registry.npmjs.org/ @paradigma-inc/flywheel@latest';
|
|
28
|
+
const WINDOWS_MANUAL_INSTALL_COMMAND =
|
|
29
|
+
'$prefix = if ($env:FLYWHEEL_INSTALL_PREFIX) { $env:FLYWHEEL_INSTALL_PREFIX } else { "$env:USERPROFILE\\.local" }; npm install -g --prefix $prefix --registry=https://registry.npmjs.org/ --@paradigma-inc:registry=https://registry.npmjs.org/ @paradigma-inc/flywheel@latest';
|
|
30
|
+
|
|
31
|
+
function isWindowsCommandShim(filePath, platform) {
|
|
32
|
+
return (
|
|
33
|
+
platform === "win32" &&
|
|
34
|
+
[".bat", ".cmd"].includes(path.extname(filePath).toLowerCase())
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function shouldUseWindowsCommandShell(filePath, platform) {
|
|
39
|
+
return (
|
|
40
|
+
platform === "win32" &&
|
|
41
|
+
(path.extname(filePath) === "" || isWindowsCommandShim(filePath, platform))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function quoteWindowsCommandArgument(value) {
|
|
46
|
+
return `"${String(value).replaceAll('"', '""')}"`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveWindowsCommandShell(env) {
|
|
50
|
+
return env.ComSpec || env.COMSPEC || "cmd.exe";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function buildWindowsCommandLine(file, args) {
|
|
54
|
+
return `"${[
|
|
55
|
+
quoteWindowsCommandArgument(file),
|
|
56
|
+
...args.map((arg) => quoteWindowsCommandArgument(arg)),
|
|
57
|
+
].join(" ")}"`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function buildSpawnFileInvocation({ file, args, env, platform }) {
|
|
61
|
+
if (!shouldUseWindowsCommandShell(file, platform)) {
|
|
62
|
+
return { command: file, args };
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
command: resolveWindowsCommandShell(env),
|
|
66
|
+
args: ["/d", "/s", "/c", buildWindowsCommandLine(file, args)],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function defaultSpawnFile(file, args, options = {}) {
|
|
71
|
+
const {
|
|
72
|
+
env = process.env,
|
|
73
|
+
platform = process.platform,
|
|
74
|
+
...spawnOptions
|
|
75
|
+
} = options;
|
|
76
|
+
const invocation = buildSpawnFileInvocation({ file, args, env, platform });
|
|
77
|
+
return await new Promise((resolve, reject) => {
|
|
78
|
+
const child = spawn(invocation.command, invocation.args, {
|
|
79
|
+
stdio: "inherit",
|
|
80
|
+
env,
|
|
81
|
+
...spawnOptions,
|
|
82
|
+
});
|
|
83
|
+
child.on("error", reject);
|
|
84
|
+
child.on("close", (code, signal) => {
|
|
85
|
+
resolve({
|
|
86
|
+
exitCode: typeof code === "number" ? code : signal ? 1 : 0,
|
|
87
|
+
signal,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function manualInstallCommand(platform) {
|
|
94
|
+
return platform === "win32"
|
|
95
|
+
? WINDOWS_MANUAL_INSTALL_COMMAND
|
|
96
|
+
: POSIX_MANUAL_INSTALL_COMMAND;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function pathExists(filePath) {
|
|
100
|
+
try {
|
|
101
|
+
await access(filePath);
|
|
102
|
+
return true;
|
|
103
|
+
} catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function stripTrailingSlash(filePath) {
|
|
109
|
+
const parsed = path.parse(filePath);
|
|
110
|
+
let normalized = filePath;
|
|
111
|
+
while (
|
|
112
|
+
normalized.length > parsed.root.length &&
|
|
113
|
+
/[/\\]$/.test(normalized)
|
|
114
|
+
) {
|
|
115
|
+
normalized = normalized.slice(0, -1);
|
|
116
|
+
}
|
|
117
|
+
return normalized;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function normalizeInstallPrefix(rawPrefix, { cwd, homeDir }) {
|
|
121
|
+
const raw = String(rawPrefix || "").trim();
|
|
122
|
+
if (raw === "~") {
|
|
123
|
+
return homeDir;
|
|
124
|
+
}
|
|
125
|
+
if (raw.startsWith(`~${path.sep}`) || raw.startsWith("~/")) {
|
|
126
|
+
return stripTrailingSlash(path.join(homeDir, raw.slice(2)));
|
|
127
|
+
}
|
|
128
|
+
const resolved = path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
|
|
129
|
+
return stripTrailingSlash(resolved);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function parseFlags(argv) {
|
|
133
|
+
const flags = {
|
|
134
|
+
check: false,
|
|
135
|
+
dryRun: false,
|
|
136
|
+
yes: false,
|
|
137
|
+
help: false,
|
|
138
|
+
refreshExistingSetup: false,
|
|
139
|
+
};
|
|
140
|
+
const unknown = [];
|
|
141
|
+
for (const token of argv) {
|
|
142
|
+
if (token === "--check") flags.check = true;
|
|
143
|
+
else if (token === "--dry-run") flags.dryRun = true;
|
|
144
|
+
else if (token === "--yes") flags.yes = true;
|
|
145
|
+
else if (token === "--help" || token === "-h") flags.help = true;
|
|
146
|
+
else if (token === "--refresh-existing-setup") {
|
|
147
|
+
flags.refreshExistingSetup = true;
|
|
148
|
+
} else {
|
|
149
|
+
unknown.push(token);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { flags, unknown };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function renderUpdateHelp() {
|
|
156
|
+
return [
|
|
157
|
+
"Usage: flywheel update [--check] [--dry-run] [--yes]",
|
|
158
|
+
"",
|
|
159
|
+
"Update the local Flywheel CLI package and refresh local setup artifacts.",
|
|
160
|
+
"",
|
|
161
|
+
"Options:",
|
|
162
|
+
" --check Print installed/latest version status without changing files.",
|
|
163
|
+
" --dry-run Print planned package, MCP, and skill refresh steps.",
|
|
164
|
+
" --yes Run prompt-free for agent execution.",
|
|
165
|
+
" -h, --help Show this help.",
|
|
166
|
+
"",
|
|
167
|
+
].join("\n");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function deriveManagedPrefixFromFlywheelBinaryPath({
|
|
171
|
+
binaryPath,
|
|
172
|
+
platform = process.platform,
|
|
173
|
+
}) {
|
|
174
|
+
if (!binaryPath) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
const basename = path.basename(binaryPath).toLowerCase();
|
|
178
|
+
if (
|
|
179
|
+
platform === "win32" &&
|
|
180
|
+
["flywheel", "flywheel.cmd", "flywheel.bat", "flywheel.exe"].includes(
|
|
181
|
+
basename,
|
|
182
|
+
)
|
|
183
|
+
) {
|
|
184
|
+
return path.dirname(binaryPath);
|
|
185
|
+
}
|
|
186
|
+
if (basename !== "flywheel") {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const binDir = path.dirname(binaryPath);
|
|
190
|
+
if (path.basename(binDir).toLowerCase() !== "bin") {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
return path.dirname(binDir);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function resolveUpdatePrefix({
|
|
197
|
+
cwd = process.cwd(),
|
|
198
|
+
env = process.env,
|
|
199
|
+
homeDir = os.homedir(),
|
|
200
|
+
currentExecPath,
|
|
201
|
+
platform = process.platform,
|
|
202
|
+
resolveFlywheelBinaryOnPath = defaultResolveFlywheelBinaryOnPath,
|
|
203
|
+
} = {}) {
|
|
204
|
+
const explicitPrefix = String(env.FLYWHEEL_INSTALL_PREFIX || "").trim();
|
|
205
|
+
if (explicitPrefix) {
|
|
206
|
+
return normalizeInstallPrefix(explicitPrefix, { cwd, homeDir });
|
|
207
|
+
}
|
|
208
|
+
const currentPrefix = deriveManagedPrefixFromFlywheelBinaryPath({
|
|
209
|
+
binaryPath: currentExecPath,
|
|
210
|
+
platform,
|
|
211
|
+
});
|
|
212
|
+
if (currentPrefix) {
|
|
213
|
+
return currentPrefix;
|
|
214
|
+
}
|
|
215
|
+
const pathBinary = await resolveFlywheelBinaryOnPath({ env, platform });
|
|
216
|
+
const pathPrefix = deriveManagedPrefixFromFlywheelBinaryPath({
|
|
217
|
+
binaryPath: pathBinary,
|
|
218
|
+
platform,
|
|
219
|
+
});
|
|
220
|
+
return pathPrefix || path.join(homeDir, ".local");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function resolveFlywheelBinaryInPrefix({ prefix, platform = process.platform }) {
|
|
224
|
+
if (platform === "win32") {
|
|
225
|
+
return path.join(prefix, "flywheel.cmd");
|
|
226
|
+
}
|
|
227
|
+
return path.join(prefix, "bin", "flywheel");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function isPathWithin(childPath, parentPath) {
|
|
231
|
+
const relative = path.relative(
|
|
232
|
+
path.resolve(parentPath),
|
|
233
|
+
path.resolve(childPath),
|
|
234
|
+
);
|
|
235
|
+
return (
|
|
236
|
+
relative === "" ||
|
|
237
|
+
(!relative.startsWith("..") && !path.isAbsolute(relative))
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function canUseManagedPrefix({
|
|
242
|
+
prefix,
|
|
243
|
+
currentExecPath,
|
|
244
|
+
flywheelBinaryPath,
|
|
245
|
+
pathExistsFn = pathExists,
|
|
246
|
+
}) {
|
|
247
|
+
if (currentExecPath && isPathWithin(currentExecPath, prefix)) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
return await pathExistsFn(flywheelBinaryPath);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function statusLines(comparison) {
|
|
254
|
+
return [
|
|
255
|
+
`Installed ${FLYWHEEL_PACKAGE_NAME}: ${comparison.currentVersion}`,
|
|
256
|
+
`Latest ${FLYWHEEL_PACKAGE_NAME}: ${comparison.latestVersion}`,
|
|
257
|
+
`Status: ${comparison.status}`,
|
|
258
|
+
"",
|
|
259
|
+
].join("\n");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function packageInstallArgs(prefix) {
|
|
263
|
+
return [
|
|
264
|
+
"install",
|
|
265
|
+
"-g",
|
|
266
|
+
"--prefix",
|
|
267
|
+
prefix,
|
|
268
|
+
...PUBLIC_NPM_REGISTRY_ARGS,
|
|
269
|
+
PACKAGE_SPEC,
|
|
270
|
+
];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function packageInstallCommand(prefix) {
|
|
274
|
+
return ["npm", ...packageInstallArgs(prefix)].join(" ");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function runCheck({
|
|
278
|
+
currentVersion,
|
|
279
|
+
fetchLatestVersion,
|
|
280
|
+
stdout,
|
|
281
|
+
stderr,
|
|
282
|
+
}) {
|
|
283
|
+
try {
|
|
284
|
+
const latestVersion = await fetchLatestVersion();
|
|
285
|
+
const comparison = compareInstalledToLatest({
|
|
286
|
+
currentVersion,
|
|
287
|
+
latestVersion,
|
|
288
|
+
});
|
|
289
|
+
stdout.write(statusLines(comparison));
|
|
290
|
+
return 0;
|
|
291
|
+
} catch (error) {
|
|
292
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
293
|
+
stderr.write(
|
|
294
|
+
`Unable to check latest ${FLYWHEEL_PACKAGE_NAME} version: ${message}\n`,
|
|
295
|
+
);
|
|
296
|
+
return 1;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export async function runUpdateCommand(argv = [], deps = {}) {
|
|
301
|
+
const {
|
|
302
|
+
cwd = process.cwd(),
|
|
303
|
+
env = process.env,
|
|
304
|
+
homeDir = os.homedir(),
|
|
305
|
+
platform = process.platform,
|
|
306
|
+
stdout = process.stdout,
|
|
307
|
+
stderr = process.stderr,
|
|
308
|
+
currentExecPath = process.argv[1],
|
|
309
|
+
currentVersion = await loadPackageVersion(),
|
|
310
|
+
fetchLatestVersion = () => fetchLatestVersionFromRegistry(),
|
|
311
|
+
spawnFile = defaultSpawnFile,
|
|
312
|
+
discoverInstalledState = discoverInstalledFlywheelState,
|
|
313
|
+
refreshSetup = refreshExistingSetup,
|
|
314
|
+
pathExistsFn = pathExists,
|
|
315
|
+
resolveFlywheelBinaryOnPath = defaultResolveFlywheelBinaryOnPath,
|
|
316
|
+
} = deps;
|
|
317
|
+
const { flags, unknown } = parseFlags(argv);
|
|
318
|
+
|
|
319
|
+
if (flags.help) {
|
|
320
|
+
stdout.write(renderUpdateHelp());
|
|
321
|
+
return 0;
|
|
322
|
+
}
|
|
323
|
+
if (unknown.length > 0) {
|
|
324
|
+
stderr.write(`unknown update option: ${unknown[0]}\n`);
|
|
325
|
+
return 1;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (flags.check) {
|
|
329
|
+
return await runCheck({
|
|
330
|
+
currentVersion,
|
|
331
|
+
fetchLatestVersion,
|
|
332
|
+
stdout,
|
|
333
|
+
stderr,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const prefix = await resolveUpdatePrefix({
|
|
338
|
+
cwd,
|
|
339
|
+
env,
|
|
340
|
+
homeDir,
|
|
341
|
+
currentExecPath,
|
|
342
|
+
platform,
|
|
343
|
+
resolveFlywheelBinaryOnPath,
|
|
344
|
+
});
|
|
345
|
+
const flywheelBinaryPath = resolveFlywheelBinaryInPrefix({ prefix, platform });
|
|
346
|
+
|
|
347
|
+
if (flags.dryRun) {
|
|
348
|
+
const state = await discoverInstalledState({
|
|
349
|
+
cwd,
|
|
350
|
+
env,
|
|
351
|
+
serverUrl: resolveDiscoveryServerUrl({ env }),
|
|
352
|
+
});
|
|
353
|
+
stdout.write(
|
|
354
|
+
renderRefreshPlan({
|
|
355
|
+
state,
|
|
356
|
+
prefix,
|
|
357
|
+
packageInstallCommand: packageInstallCommand(prefix),
|
|
358
|
+
}),
|
|
359
|
+
);
|
|
360
|
+
return 0;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (flags.refreshExistingSetup) {
|
|
364
|
+
try {
|
|
365
|
+
return await refreshSetup({
|
|
366
|
+
cwd,
|
|
367
|
+
env,
|
|
368
|
+
stdout,
|
|
369
|
+
stderr,
|
|
370
|
+
});
|
|
371
|
+
} catch (error) {
|
|
372
|
+
const stepName = error?.stepName || "refresh-existing-setup";
|
|
373
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
374
|
+
stderr.write(
|
|
375
|
+
`Failed refresh-existing-setup step ${stepName}: ${message}\n`,
|
|
376
|
+
);
|
|
377
|
+
return Number(error?.exitCode || 1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (
|
|
382
|
+
!(await canUseManagedPrefix({
|
|
383
|
+
prefix,
|
|
384
|
+
currentExecPath,
|
|
385
|
+
flywheelBinaryPath,
|
|
386
|
+
pathExistsFn,
|
|
387
|
+
}))
|
|
388
|
+
) {
|
|
389
|
+
stderr.write(
|
|
390
|
+
[
|
|
391
|
+
`Unable to update ${FLYWHEEL_PACKAGE_NAME}: no managed Flywheel binary was found under ${prefix}.`,
|
|
392
|
+
`Manual command: ${manualInstallCommand(platform)}`,
|
|
393
|
+
"",
|
|
394
|
+
].join("\n"),
|
|
395
|
+
);
|
|
396
|
+
return 2;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
stderr.write(`Updating ${FLYWHEEL_PACKAGE_NAME} with npm.\n`);
|
|
400
|
+
let npmResult;
|
|
401
|
+
try {
|
|
402
|
+
npmResult = await spawnFile(
|
|
403
|
+
"npm",
|
|
404
|
+
packageInstallArgs(prefix),
|
|
405
|
+
{ env, platform },
|
|
406
|
+
);
|
|
407
|
+
} catch (error) {
|
|
408
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
409
|
+
stderr.write(`Failed to update ${FLYWHEEL_PACKAGE_NAME}: ${message}\n`);
|
|
410
|
+
return 1;
|
|
411
|
+
}
|
|
412
|
+
const npmExitCode = Number(npmResult?.exitCode || 0);
|
|
413
|
+
if (npmExitCode !== 0) {
|
|
414
|
+
stderr.write(`Failed to update ${FLYWHEEL_PACKAGE_NAME}.\n`);
|
|
415
|
+
return npmExitCode;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
stderr.write("Refreshing local Flywheel setup artifacts.\n");
|
|
419
|
+
let refreshResult;
|
|
420
|
+
try {
|
|
421
|
+
refreshResult = await spawnFile(
|
|
422
|
+
flywheelBinaryPath,
|
|
423
|
+
["update", "--refresh-existing-setup", "--yes"],
|
|
424
|
+
{ env, platform },
|
|
425
|
+
);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
428
|
+
stderr.write(`Failed refresh-existing-setup step spawn: ${message}\n`);
|
|
429
|
+
return 1;
|
|
430
|
+
}
|
|
431
|
+
const refreshExitCode = Number(refreshResult?.exitCode || 0);
|
|
432
|
+
if (refreshExitCode !== 0) {
|
|
433
|
+
stderr.write("Failed refresh-existing-setup step updated-binary.\n");
|
|
434
|
+
return refreshExitCode;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return 0;
|
|
438
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ALL_HOST_NAMES,
|
|
5
|
+
SERVER_NAME,
|
|
6
|
+
getHost as defaultGetHost,
|
|
7
|
+
isSkillOnlyHost,
|
|
8
|
+
} from "../agents.mjs";
|
|
9
|
+
import { resolveRuntimeConfigPath } from "../runtime/runtime-config.mjs";
|
|
10
|
+
import {
|
|
11
|
+
resolveFlywheelInstalledMarkerPath,
|
|
12
|
+
SUPPORTED_INSTALL_SKILL_HOSTS,
|
|
13
|
+
} from "../skill-installer.mjs";
|
|
14
|
+
import { findFlywheelMcpEntries as defaultFindFlywheelMcpEntries } from "../setup/shared/prior-mode-detect.mjs";
|
|
15
|
+
|
|
16
|
+
const DEFAULT_SERVER_URL = "https://flywheel.paradigma.inc/mcp-server";
|
|
17
|
+
|
|
18
|
+
async function pathExists(filePath) {
|
|
19
|
+
try {
|
|
20
|
+
await access(filePath);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function baseUrlFromServerUrl(serverUrl) {
|
|
28
|
+
try {
|
|
29
|
+
return new URL(serverUrl).origin;
|
|
30
|
+
} catch {
|
|
31
|
+
return "https://flywheel.paradigma.inc";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function originOf(url) {
|
|
36
|
+
try {
|
|
37
|
+
return new URL(url).origin.toLowerCase();
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function sortArtifacts(artifacts) {
|
|
44
|
+
return artifacts.sort((left, right) => {
|
|
45
|
+
const byHost = left.hostName.localeCompare(right.hostName);
|
|
46
|
+
if (byHost !== 0) return byHost;
|
|
47
|
+
const byScope = left.scope.localeCompare(right.scope);
|
|
48
|
+
if (byScope !== 0) return byScope;
|
|
49
|
+
return String(left.serverName || "").localeCompare(
|
|
50
|
+
String(right.serverName || ""),
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function discoverInstalledFlywheelState({
|
|
56
|
+
cwd = process.cwd(),
|
|
57
|
+
env = process.env,
|
|
58
|
+
hostNames = ALL_HOST_NAMES,
|
|
59
|
+
scopes = ["project", "global"],
|
|
60
|
+
serverUrl = DEFAULT_SERVER_URL,
|
|
61
|
+
getHost = defaultGetHost,
|
|
62
|
+
findFlywheelMcpEntries = defaultFindFlywheelMcpEntries,
|
|
63
|
+
markerPathForHost = resolveFlywheelInstalledMarkerPath,
|
|
64
|
+
runtimeConfigPath = resolveRuntimeConfigPath({ env }),
|
|
65
|
+
pathExistsFn = pathExists,
|
|
66
|
+
} = {}) {
|
|
67
|
+
const targetOrigin = originOf(serverUrl);
|
|
68
|
+
const mcpArtifacts = [];
|
|
69
|
+
const cliArtifacts = [];
|
|
70
|
+
const seenCli = new Set();
|
|
71
|
+
const runtimeConfigExists = await pathExistsFn(runtimeConfigPath);
|
|
72
|
+
|
|
73
|
+
for (const hostName of hostNames) {
|
|
74
|
+
let host;
|
|
75
|
+
try {
|
|
76
|
+
host = getHost(hostName);
|
|
77
|
+
} catch {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const scope of scopes) {
|
|
82
|
+
if (!isSkillOnlyHost(host)) {
|
|
83
|
+
// eslint-disable-next-line no-await-in-loop
|
|
84
|
+
const entries = await findFlywheelMcpEntries({
|
|
85
|
+
hostName,
|
|
86
|
+
scope,
|
|
87
|
+
cwd,
|
|
88
|
+
targetOrigin,
|
|
89
|
+
trustedServerNames: [SERVER_NAME],
|
|
90
|
+
getHost,
|
|
91
|
+
});
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
mcpArtifacts.push({
|
|
94
|
+
hostName,
|
|
95
|
+
scope,
|
|
96
|
+
serverName: entry.serverName,
|
|
97
|
+
serverUrl: entry.serverUrl,
|
|
98
|
+
baseUrl: baseUrlFromServerUrl(entry.serverUrl),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!SUPPORTED_INSTALL_SKILL_HOSTS.includes(hostName)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
let markerPath;
|
|
107
|
+
try {
|
|
108
|
+
markerPath = markerPathForHost(cwd, hostName, scope);
|
|
109
|
+
} catch {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
// eslint-disable-next-line no-await-in-loop
|
|
113
|
+
const markerExists = await pathExistsFn(markerPath);
|
|
114
|
+
if (!markerExists) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const key = `${hostName}\0${scope}`;
|
|
118
|
+
if (seenCli.has(key)) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
seenCli.add(key);
|
|
122
|
+
cliArtifacts.push({
|
|
123
|
+
hostName,
|
|
124
|
+
scope,
|
|
125
|
+
runtimeConfigExists,
|
|
126
|
+
markerPath,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
mcpArtifacts: sortArtifacts(mcpArtifacts),
|
|
133
|
+
cliArtifacts: sortArtifacts(cliArtifacts),
|
|
134
|
+
};
|
|
135
|
+
}
|