@fml-inc/fml 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/bin/fml +17 -0
- package/bin/mcp-server +17 -0
- package/dist/{chunk-CUBBYHH7.js → chunk-5BHBYQ2F.js} +84 -30
- package/dist/chunk-5BHBYQ2F.js.map +1 -0
- package/dist/chunk-H2VOISTZ.js +225 -0
- package/dist/chunk-H2VOISTZ.js.map +1 -0
- package/dist/chunk-YH5ROR7G.js +69 -0
- package/dist/chunk-YH5ROR7G.js.map +1 -0
- package/dist/{chunk-NFC24I3N.js → chunk-ZFUJ5ZQP.js} +17 -9
- package/dist/chunk-ZFUJ5ZQP.js.map +1 -0
- package/dist/cli.js +86 -64
- package/dist/cli.js.map +1 -1
- package/dist/daemon-J27JBWLV.js +17 -0
- package/dist/daemon-utils-OODBIYB7.js +10 -0
- package/dist/mcp/server.js +112 -13
- package/dist/mcp/server.js.map +1 -1
- package/dist/postinstall.js +1 -1
- package/dist/postinstall.js.map +1 -1
- package/dist/{token-store-HOFFHNNN.js → token-store-T2RNLXRM.js} +2 -3
- package/package.json +4 -9
- package/scripts/postinstall.cjs +25 -0
- package/bin/sync-daemon +0 -3
- package/dist/chunk-4YEAQJPF.js +0 -47
- package/dist/chunk-4YEAQJPF.js.map +0 -1
- package/dist/chunk-BOREGHA6.js +0 -221
- package/dist/chunk-BOREGHA6.js.map +0 -1
- package/dist/chunk-CUBBYHH7.js.map +0 -1
- package/dist/chunk-NFC24I3N.js.map +0 -1
- package/dist/chunk-ZKHH6EYQ.js +0 -61
- package/dist/chunk-ZKHH6EYQ.js.map +0 -1
- package/dist/daemon-WXE6FYZD.js +0 -16
- package/dist/daemon-utils-EUOTVCKL.js +0 -17
- /package/dist/{daemon-WXE6FYZD.js.map → daemon-J27JBWLV.js.map} +0 -0
- /package/dist/{daemon-utils-EUOTVCKL.js.map → daemon-utils-OODBIYB7.js.map} +0 -0
- /package/dist/{token-store-HOFFHNNN.js.map → token-store-T2RNLXRM.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bin-utils.ts","../src/daemon-utils.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { execFileSync, execSync } from \"node:child_process\";\n\nconst IS_WIN = process.platform === \"win32\";\nconst DEFAULT_PATHEXT = \".COM;.EXE;.BAT;.CMD\";\n\n/**\n * Resolve a CLI binary by walking process.env.PATH. On Windows, probe each\n * process.env.PATHEXT extension. Returns an absolute path or null.\n *\n * Pass the bare command name (e.g. \"npm\", not \"npm.cmd\"); the extension is\n * applied via PATHEXT on Windows. If the caller does include an extension,\n * it's used as-is.\n *\n * Replaces `which`/`where`: avoids spawning a child process and works the\n * same on macOS, Linux, and Windows.\n */\nexport function resolveBin(name: string): string | null {\n const pathDirs = (process.env.PATH ?? \"\").split(path.delimiter).filter(Boolean);\n const hasExt = path.extname(name) !== \"\";\n const exts = !IS_WIN || hasExt\n ? [\"\"]\n : (process.env.PATHEXT ?? DEFAULT_PATHEXT).split(\";\").filter(Boolean);\n\n for (const dir of pathDirs) {\n for (const ext of exts) {\n const candidate = path.join(dir, name + ext);\n try {\n if (fs.statSync(candidate).isFile()) return candidate;\n } catch {}\n }\n }\n return null;\n}\n\n/** True for Windows shell shims that execFile cannot run directly. */\nfunction isWinShim(bin: string): boolean {\n return IS_WIN && /\\.(cmd|bat)$/i.test(bin);\n}\n\n/**\n * Quote a single argument for safe inclusion in a Windows command line that\n * will be parsed by cmd.exe and then re-parsed by a CRT program.\n *\n * Two layers:\n * 1. CRT (CommandLineToArgvW): wrap in `\"`, escape internal `\"` as `\\\"`,\n * double any backslashes that precede a quote.\n * 2. cmd.exe: escape its metacharacters with `^` so they aren't expanded\n * before the CRT layer sees them. `%` and `!` (delayed expansion) are\n * the dangerous ones inside quotes; the rest are safest belt-and-braces.\n */\nexport function quoteWinArg(arg: string): string {\n if (arg.length === 0) return '\"\"';\n\n // Fast path: bare-word args that need no quoting at all.\n // Backslash is intentionally excluded so `path\\` and similar still go\n // through the trailing-backslash doubling logic below.\n if (/^[A-Za-z0-9@\\-_+=:/.,]+$/.test(arg)) return arg;\n\n // CRT layer: double backslashes before quotes, escape quotes, double trailing backslashes.\n const crt = arg\n .replace(/(\\\\*)\"/g, '$1$1\\\\\"')\n .replace(/(\\\\+)$/, \"$1$1\");\n const quoted = `\"${crt}\"`;\n\n // cmd.exe layer: escape metacharacters (including the surrounding quotes)\n // so cmd's first pass passes them through verbatim.\n return quoted.replace(/([()%!^<>&|\"])/g, \"^$1\");\n}\n\nexport interface ExecBinOptions {\n timeout?: number;\n encoding?: BufferEncoding;\n stdio?: \"pipe\" | \"inherit\" | \"ignore\";\n}\n\n/**\n * Run a resolved binary path with the given args, handling Windows .cmd/.bat\n * shims correctly. Throws on non-zero exit (matching execFileSync).\n */\nexport function execBinSync(\n bin: string,\n args: string[],\n opts: ExecBinOptions = {},\n): string {\n const { timeout = 10_000, encoding = \"utf-8\", stdio = \"pipe\" } = opts;\n\n if (isWinShim(bin)) {\n // CVE-2024-27980: Node refuses to spawn .cmd/.bat without shell: true.\n // Build the command string ourselves so we control the quoting.\n const command = [bin, ...args].map(quoteWinArg).join(\" \");\n return execSync(command, { timeout, encoding, stdio });\n }\n\n return execFileSync(bin, args, { timeout, encoding, stdio });\n}\n","import { execBinSync, resolveBin } from \"./bin-utils.js\";\n\n/** Resolve the panopticon CLI binary from PATH (globally installed). */\nexport function resolvePanopticonBin(): string | null {\n return resolveBin(\"panopticon\");\n}\n\n/** Run a panopticon CLI command and return stdout. */\nexport function panopticonExec(...args: string[]): {\n ok: boolean;\n stdout: string;\n};\nexport function panopticonExec(\n ...argsAndOpts: [...string[], { timeout?: number }]\n): { ok: boolean; stdout: string };\nexport function panopticonExec(\n ...argsAndOpts: Array<string | { timeout?: number }>\n): {\n ok: boolean;\n stdout: string;\n} {\n let timeout = 10_000;\n const args: string[] = [];\n for (const a of argsAndOpts) {\n if (typeof a === \"string\") args.push(a);\n else timeout = a.timeout ?? timeout;\n }\n const bin = resolvePanopticonBin();\n if (!bin) return { ok: false, stdout: \"panopticon binary not found\" };\n try {\n const stdout = execBinSync(bin, args, { timeout });\n return { ok: true, stdout };\n } catch (err: unknown) {\n const e = err as { stdout?: string; stderr?: string; status?: number };\n return { ok: false, stdout: (e.stdout ?? \"\") + (e.stderr ?? \"\") };\n }\n}\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,cAAc,gBAAgB;AAEvC,IAAM,SAAS,QAAQ,aAAa;AACpC,IAAM,kBAAkB;AAajB,SAAS,WAAW,MAA6B;AACtD,QAAM,YAAY,QAAQ,IAAI,QAAQ,IAAI,MAAM,KAAK,SAAS,EAAE,OAAO,OAAO;AAC9E,QAAM,SAAS,KAAK,QAAQ,IAAI,MAAM;AACtC,QAAM,OAAO,CAAC,UAAU,SACpB,CAAC,EAAE,KACF,QAAQ,IAAI,WAAW,iBAAiB,MAAM,GAAG,EAAE,OAAO,OAAO;AAEtE,aAAW,OAAO,UAAU;AAC1B,eAAW,OAAO,MAAM;AACtB,YAAM,YAAY,KAAK,KAAK,KAAK,OAAO,GAAG;AAC3C,UAAI;AACF,YAAI,GAAG,SAAS,SAAS,EAAE,OAAO,EAAG,QAAO;AAAA,MAC9C,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,UAAU,KAAsB;AACvC,SAAO,UAAU,gBAAgB,KAAK,GAAG;AAC3C;AAaO,SAAS,YAAY,KAAqB;AAC/C,MAAI,IAAI,WAAW,EAAG,QAAO;AAK7B,MAAI,2BAA2B,KAAK,GAAG,EAAG,QAAO;AAGjD,QAAM,MAAM,IACT,QAAQ,WAAW,SAAS,EAC5B,QAAQ,UAAU,MAAM;AAC3B,QAAM,SAAS,IAAI,GAAG;AAItB,SAAO,OAAO,QAAQ,mBAAmB,KAAK;AAChD;AAYO,SAAS,YACd,KACA,MACA,OAAuB,CAAC,GAChB;AACR,QAAM,EAAE,UAAU,KAAQ,WAAW,SAAS,QAAQ,OAAO,IAAI;AAEjE,MAAI,UAAU,GAAG,GAAG;AAGlB,UAAM,UAAU,CAAC,KAAK,GAAG,IAAI,EAAE,IAAI,WAAW,EAAE,KAAK,GAAG;AACxD,WAAO,SAAS,SAAS,EAAE,SAAS,UAAU,MAAM,CAAC;AAAA,EACvD;AAEA,SAAO,aAAa,KAAK,MAAM,EAAE,SAAS,UAAU,MAAM,CAAC;AAC7D;;;AC7FO,SAAS,uBAAsC;AACpD,SAAO,WAAW,YAAY;AAChC;AAUO,SAAS,kBACX,aAIH;AACA,MAAI,UAAU;AACd,QAAM,OAAiB,CAAC;AACxB,aAAW,KAAK,aAAa;AAC3B,QAAI,OAAO,MAAM,SAAU,MAAK,KAAK,CAAC;AAAA,QACjC,WAAU,EAAE,WAAW;AAAA,EAC9B;AACA,QAAM,MAAM,qBAAqB;AACjC,MAAI,CAAC,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,8BAA8B;AACpE,MAAI;AACF,UAAM,SAAS,YAAY,KAAK,MAAM,EAAE,QAAQ,CAAC;AACjD,WAAO,EAAE,IAAI,MAAM,OAAO;AAAA,EAC5B,SAAS,KAAc;AACrB,UAAM,IAAI;AACV,WAAO,EAAE,IAAI,OAAO,SAAS,EAAE,UAAU,OAAO,EAAE,UAAU,IAAI;AAAA,EAClE;AACF;","names":[]}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
SYNC_PID_FILE,
|
|
3
|
-
isDaemonRunning,
|
|
4
2
|
panopticonExec
|
|
5
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YH5ROR7G.js";
|
|
6
4
|
|
|
7
5
|
// src/commands/daemon.ts
|
|
8
6
|
function parsePanopticonRunning() {
|
|
@@ -10,9 +8,6 @@ function parsePanopticonRunning() {
|
|
|
10
8
|
const serverLine = result.stdout.split("\n").find((l) => l.startsWith("Server:"));
|
|
11
9
|
return result.ok && /running/i.test(serverLine ?? "");
|
|
12
10
|
}
|
|
13
|
-
function isSyncRunning() {
|
|
14
|
-
return isDaemonRunning(SYNC_PID_FILE);
|
|
15
|
-
}
|
|
16
11
|
async function handleStart() {
|
|
17
12
|
const startResult = panopticonExec("start");
|
|
18
13
|
console.log(
|
|
@@ -23,11 +18,24 @@ function handleStop() {
|
|
|
23
18
|
const stopResult = panopticonExec("stop");
|
|
24
19
|
console.log(stopResult.stdout.trim() || "Panopticon stopped");
|
|
25
20
|
}
|
|
21
|
+
function handleSyncStart() {
|
|
22
|
+
const result = panopticonExec("sync", "enable");
|
|
23
|
+
console.log(
|
|
24
|
+
result.stdout.trim() || (result.ok ? "Sync enabled" : "Failed to enable sync")
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function handleSyncStop() {
|
|
28
|
+
const result = panopticonExec("sync", "disable");
|
|
29
|
+
console.log(
|
|
30
|
+
result.stdout.trim() || (result.ok ? "Sync disabled" : "Failed to disable sync")
|
|
31
|
+
);
|
|
32
|
+
}
|
|
26
33
|
|
|
27
34
|
export {
|
|
28
35
|
parsePanopticonRunning,
|
|
29
|
-
isSyncRunning,
|
|
30
36
|
handleStart,
|
|
31
|
-
handleStop
|
|
37
|
+
handleStop,
|
|
38
|
+
handleSyncStart,
|
|
39
|
+
handleSyncStop
|
|
32
40
|
};
|
|
33
|
-
//# sourceMappingURL=chunk-
|
|
41
|
+
//# sourceMappingURL=chunk-ZFUJ5ZQP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/daemon.ts"],"sourcesContent":["import { panopticonExec } from \"../daemon-utils.js\";\n\n// ── Status helpers (used by doctor, status) ─────────────────────────────────\n\nexport function parsePanopticonRunning(): boolean {\n const result = panopticonExec(\"status\");\n const serverLine = result.stdout\n .split(\"\\n\")\n .find((l) => l.startsWith(\"Server:\"));\n return result.ok && /running/i.test(serverLine ?? \"\");\n}\n\n// ── Panopticon server start / stop (used by CLI, env restart, install, MCP) ──\n\nexport async function handleStart(): Promise<void> {\n const startResult = panopticonExec(\"start\");\n console.log(\n startResult.stdout.trim() ||\n (startResult.ok ? \"Panopticon started\" : \"Failed to start panopticon\"),\n );\n}\n\nexport function handleStop(): void {\n const stopResult = panopticonExec(\"stop\");\n console.log(stopResult.stdout.trim() || \"Panopticon stopped\");\n}\n\n// ── Sync start / stop (used by `fml sync start|stop`) ───────────────────────\n// Sync runs inside the panopticon server; these toggle the persisted\n// sync-enabled flag via panopticon, independent of the server lifecycle.\n\nexport function handleSyncStart(): void {\n const result = panopticonExec(\"sync\", \"enable\");\n console.log(\n result.stdout.trim() ||\n (result.ok ? \"Sync enabled\" : \"Failed to enable sync\"),\n );\n}\n\nexport function handleSyncStop(): void {\n const result = panopticonExec(\"sync\", \"disable\");\n console.log(\n result.stdout.trim() ||\n (result.ok ? \"Sync disabled\" : \"Failed to disable sync\"),\n );\n}\n"],"mappings":";;;;;AAIO,SAAS,yBAAkC;AAChD,QAAM,SAAS,eAAe,QAAQ;AACtC,QAAM,aAAa,OAAO,OACvB,MAAM,IAAI,EACV,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC;AACtC,SAAO,OAAO,MAAM,WAAW,KAAK,cAAc,EAAE;AACtD;AAIA,eAAsB,cAA6B;AACjD,QAAM,cAAc,eAAe,OAAO;AAC1C,UAAQ;AAAA,IACN,YAAY,OAAO,KAAK,MACrB,YAAY,KAAK,uBAAuB;AAAA,EAC7C;AACF;AAEO,SAAS,aAAmB;AACjC,QAAM,aAAa,eAAe,MAAM;AACxC,UAAQ,IAAI,WAAW,OAAO,KAAK,KAAK,oBAAoB;AAC9D;AAMO,SAAS,kBAAwB;AACtC,QAAM,SAAS,eAAe,QAAQ,QAAQ;AAC9C,UAAQ;AAAA,IACN,OAAO,OAAO,KAAK,MAChB,OAAO,KAAK,iBAAiB;AAAA,EAClC;AACF;AAEO,SAAS,iBAAuB;AACrC,QAAM,SAAS,eAAe,QAAQ,SAAS;AAC/C,UAAQ;AAAA,IACN,OAAO,OAAO,KAAK,MAChB,OAAO,KAAK,kBAAkB;AAAA,EACnC;AACF;","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
ensureNativeModules,
|
|
3
|
+
createFmlClient,
|
|
5
4
|
getAuthenticatedClient
|
|
6
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-H2VOISTZ.js";
|
|
7
6
|
import {
|
|
8
7
|
CONVEX_URL,
|
|
9
8
|
DEFAULT_SYNC_URL,
|
|
10
9
|
DEFAULT_TARGET_NAME,
|
|
10
|
+
FML_DATA_DIR,
|
|
11
|
+
FML_LOG_DIR,
|
|
11
12
|
Sentry,
|
|
12
13
|
WORKOS_API_URL,
|
|
13
14
|
WORKOS_AUTH_URL,
|
|
14
15
|
authStorePath,
|
|
16
|
+
envConfigExists,
|
|
15
17
|
getActiveEnv,
|
|
16
18
|
getSelectedOrg,
|
|
17
19
|
getSiteUrl,
|
|
@@ -22,19 +24,19 @@ import {
|
|
|
22
24
|
setSelectedOrg,
|
|
23
25
|
writeEnvConfig,
|
|
24
26
|
writeTokens
|
|
25
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-5BHBYQ2F.js";
|
|
26
28
|
import {
|
|
27
29
|
handleStart,
|
|
28
30
|
handleStop,
|
|
31
|
+
handleSyncStart,
|
|
32
|
+
handleSyncStop,
|
|
29
33
|
parsePanopticonRunning
|
|
30
|
-
} from "./chunk-
|
|
31
|
-
import {
|
|
32
|
-
panopticonExec
|
|
33
|
-
} from "./chunk-ZKHH6EYQ.js";
|
|
34
|
+
} from "./chunk-ZFUJ5ZQP.js";
|
|
34
35
|
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
execBinSync,
|
|
37
|
+
panopticonExec,
|
|
38
|
+
resolveBin
|
|
39
|
+
} from "./chunk-YH5ROR7G.js";
|
|
38
40
|
import "./chunk-PZ5AY32C.js";
|
|
39
41
|
|
|
40
42
|
// src/cli.ts
|
|
@@ -218,7 +220,7 @@ async function handleDoctor(opts) {
|
|
|
218
220
|
const spinner = startSpinner("API");
|
|
219
221
|
if (token) {
|
|
220
222
|
try {
|
|
221
|
-
const result = await
|
|
223
|
+
const result = await createFmlClient(token).callBackend("ping", {});
|
|
222
224
|
if (result.ok) {
|
|
223
225
|
pushAndReport(
|
|
224
226
|
{
|
|
@@ -273,7 +275,6 @@ async function handleDoctor(opts) {
|
|
|
273
275
|
}
|
|
274
276
|
|
|
275
277
|
// src/commands/install.ts
|
|
276
|
-
import { execFileSync } from "child_process";
|
|
277
278
|
import fs from "fs";
|
|
278
279
|
import os from "os";
|
|
279
280
|
import path from "path";
|
|
@@ -365,15 +366,17 @@ function writeJsonFile(filePath, data) {
|
|
|
365
366
|
fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
|
|
366
367
|
`);
|
|
367
368
|
}
|
|
368
|
-
async function handleInstall() {
|
|
369
|
+
async function handleInstall(opts = {}) {
|
|
370
|
+
const force = opts.force ?? false;
|
|
369
371
|
const pluginRoot = getPluginRoot();
|
|
370
|
-
console.log(
|
|
372
|
+
console.log(`Installing fml${force ? " (--force)" : ""}...
|
|
373
|
+
`);
|
|
371
374
|
console.log("[1/5] Setting up panopticon...");
|
|
372
|
-
const { resolvePanopticonBin } = await import("./daemon-utils-
|
|
375
|
+
const { resolvePanopticonBin } = await import("./daemon-utils-OODBIYB7.js");
|
|
373
376
|
let freshInstall = false;
|
|
374
377
|
const bin = resolvePanopticonBin();
|
|
375
|
-
let needsInstall = !bin;
|
|
376
|
-
if (bin) {
|
|
378
|
+
let needsInstall = !bin || force;
|
|
379
|
+
if (bin && !force) {
|
|
377
380
|
const MIN_PANOPTICON = "0.2.2";
|
|
378
381
|
const vResult = panopticonExec("--version", { timeout: 5e3 });
|
|
379
382
|
const installed = vResult.ok ? vResult.stdout.trim().split("+")[0] : "0.0.0";
|
|
@@ -386,18 +389,22 @@ async function handleInstall() {
|
|
|
386
389
|
console.log(
|
|
387
390
|
bin ? " Upgrading @fml-inc/panopticon..." : " Installing @fml-inc/panopticon globally..."
|
|
388
391
|
);
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
timeout: 12e4,
|
|
393
|
-
stdio: "pipe"
|
|
394
|
-
});
|
|
395
|
-
freshInstall = true;
|
|
396
|
-
console.log(" Installed @fml-inc/panopticon");
|
|
397
|
-
} catch (err) {
|
|
398
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
399
|
-
console.error(` Failed to install panopticon: ${msg}`);
|
|
392
|
+
const npmBin = resolveBin("npm");
|
|
393
|
+
if (!npmBin) {
|
|
394
|
+
console.error(" Failed to install panopticon: npm not found on PATH");
|
|
400
395
|
console.error(" Install manually: npm install -g @fml-inc/panopticon@latest");
|
|
396
|
+
} else {
|
|
397
|
+
try {
|
|
398
|
+
execBinSync(npmBin, ["install", "-g", "@fml-inc/panopticon@latest"], {
|
|
399
|
+
timeout: 12e4
|
|
400
|
+
});
|
|
401
|
+
freshInstall = true;
|
|
402
|
+
console.log(" Installed @fml-inc/panopticon");
|
|
403
|
+
} catch (err) {
|
|
404
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
405
|
+
console.error(` Failed to install panopticon: ${msg}`);
|
|
406
|
+
console.error(" Install manually: npm install -g @fml-inc/panopticon@latest");
|
|
407
|
+
}
|
|
401
408
|
}
|
|
402
409
|
}
|
|
403
410
|
if (!freshInstall) {
|
|
@@ -481,21 +488,26 @@ async function handleInstall() {
|
|
|
481
488
|
settings.enabledPlugins["fml@local-plugins"] = true;
|
|
482
489
|
writeJsonFile(CLAUDE_SETTINGS_PATH, settings);
|
|
483
490
|
console.log(` Claude settings: ${CLAUDE_SETTINGS_PATH}`);
|
|
484
|
-
|
|
491
|
+
const claudeBin = resolveBin("claude");
|
|
492
|
+
if (!claudeBin) {
|
|
493
|
+
console.log(" warn: claude CLI not found, run 'claude plugin install fml@local-plugins' manually");
|
|
494
|
+
} else {
|
|
485
495
|
try {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
496
|
+
try {
|
|
497
|
+
execBinSync(claudeBin, ["plugin", "install", "fml@local-plugins"], {
|
|
498
|
+
timeout: 15e3
|
|
499
|
+
});
|
|
500
|
+
} catch {
|
|
501
|
+
execBinSync(claudeBin, ["plugin", "update", "fml@local-plugins"], {
|
|
502
|
+
timeout: 15e3
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
console.log(" Plugin registered via Claude Code CLI");
|
|
506
|
+
} catch (err) {
|
|
507
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
508
|
+
console.log(` warn: claude plugin install/update failed: ${msg}`);
|
|
509
|
+
console.log(" Run 'claude plugin install fml@local-plugins' manually");
|
|
495
510
|
}
|
|
496
|
-
console.log(" Plugin registered via Claude Code CLI");
|
|
497
|
-
} catch {
|
|
498
|
-
console.log(" warn: claude CLI not found, run 'claude plugin install fml@local-plugins' manually");
|
|
499
511
|
}
|
|
500
512
|
console.log();
|
|
501
513
|
console.log("[5/5] Configuring sync target...");
|
|
@@ -519,7 +531,9 @@ async function handleInstall() {
|
|
|
519
531
|
);
|
|
520
532
|
}
|
|
521
533
|
}
|
|
522
|
-
|
|
534
|
+
if (!envConfigExists()) {
|
|
535
|
+
writeEnvConfig({ active: DEFAULT_TARGET_NAME });
|
|
536
|
+
}
|
|
523
537
|
console.log("");
|
|
524
538
|
printBanner();
|
|
525
539
|
console.log("Done! Start a new coding session to activate.\n");
|
|
@@ -531,7 +545,6 @@ async function handleInstall() {
|
|
|
531
545
|
}
|
|
532
546
|
|
|
533
547
|
// src/commands/uninstall.ts
|
|
534
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
535
548
|
import fs2 from "fs";
|
|
536
549
|
import os2 from "os";
|
|
537
550
|
import path2 from "path";
|
|
@@ -568,14 +581,17 @@ function handleUninstall(opts) {
|
|
|
568
581
|
console.log("[1/5] Skipping plugin uninstall (target-specific uninstall)");
|
|
569
582
|
} else {
|
|
570
583
|
console.log("[1/5] Uninstalling MCP plugin...");
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
584
|
+
const claudeBin = resolveBin("claude");
|
|
585
|
+
if (claudeBin) {
|
|
586
|
+
try {
|
|
587
|
+
execBinSync(
|
|
588
|
+
claudeBin,
|
|
589
|
+
["plugin", "uninstall", "fml@local-plugins", "--scope", "user"],
|
|
590
|
+
{ timeout: 1e4 }
|
|
591
|
+
);
|
|
592
|
+
console.log(" Uninstalled (user scope)");
|
|
593
|
+
} catch {
|
|
594
|
+
}
|
|
579
595
|
}
|
|
580
596
|
const installedPluginsPath = path2.join(
|
|
581
597
|
CLAUDE_DIR2,
|
|
@@ -689,7 +705,7 @@ function handleUninstall(opts) {
|
|
|
689
705
|
// src/commands/update.ts
|
|
690
706
|
import { execSync as execSync2 } from "child_process";
|
|
691
707
|
async function handleUpdate() {
|
|
692
|
-
const currentVersion = true ? "0.
|
|
708
|
+
const currentVersion = true ? "0.4.0+ef00cc0" : "unknown";
|
|
693
709
|
console.log(`Current: ${currentVersion}`);
|
|
694
710
|
console.log("Updating via npm...\n");
|
|
695
711
|
try {
|
|
@@ -700,7 +716,7 @@ async function handleUpdate() {
|
|
|
700
716
|
timeout: 12e4,
|
|
701
717
|
env
|
|
702
718
|
});
|
|
703
|
-
console.log("\nUpdated. Run `fml install --
|
|
719
|
+
console.log("\nUpdated. Run `fml install --force` to reconfigure.");
|
|
704
720
|
} catch (err) {
|
|
705
721
|
Sentry.captureException(err);
|
|
706
722
|
const msg = err instanceof Error ? err.stderr?.toString() ?? err.message : String(err);
|
|
@@ -1036,10 +1052,11 @@ async function selectOrg() {
|
|
|
1036
1052
|
try {
|
|
1037
1053
|
const token = await getValidToken();
|
|
1038
1054
|
if (!token) return;
|
|
1039
|
-
const api =
|
|
1055
|
+
const api = createFmlClient(token);
|
|
1040
1056
|
const orgs = await api.queryOrgs();
|
|
1041
1057
|
if (orgs.length === 0) return;
|
|
1042
|
-
const
|
|
1058
|
+
const current = getSelectedOrg();
|
|
1059
|
+
const org = current && orgs.find((o) => (o.slug ?? o.name) === current) || orgs[0];
|
|
1043
1060
|
const slug = org.slug ?? org.name;
|
|
1044
1061
|
setSelectedOrg(slug);
|
|
1045
1062
|
console.log(`Selected org: ${org.name} (${slug})`);
|
|
@@ -1050,7 +1067,7 @@ async function handleLogin(opts) {
|
|
|
1050
1067
|
const { name: envName } = getActiveEnv();
|
|
1051
1068
|
const existingToken = await getValidToken();
|
|
1052
1069
|
if (existingToken) {
|
|
1053
|
-
const { readTokens: readTokens2 } = await import("./token-store-
|
|
1070
|
+
const { readTokens: readTokens2 } = await import("./token-store-T2RNLXRM.js");
|
|
1054
1071
|
const stored = readTokens2();
|
|
1055
1072
|
console.log(
|
|
1056
1073
|
`Already logged in as ${stored?.user.name} (${stored?.user.email}) on ${envName}.`
|
|
@@ -1102,7 +1119,7 @@ async function handleOrg(slug) {
|
|
|
1102
1119
|
console.error("Not logged in. Run `fml login` first.");
|
|
1103
1120
|
process.exit(1);
|
|
1104
1121
|
}
|
|
1105
|
-
const api =
|
|
1122
|
+
const api = createFmlClient(token);
|
|
1106
1123
|
const orgs = await api.queryOrgs();
|
|
1107
1124
|
if (orgs.length === 0) {
|
|
1108
1125
|
console.error("No organizations found.");
|
|
@@ -1934,7 +1951,7 @@ async function handleConfigDetail(opts) {
|
|
|
1934
1951
|
// src/commands/tools.ts
|
|
1935
1952
|
import { readFileSync } from "fs";
|
|
1936
1953
|
import path3 from "path";
|
|
1937
|
-
var pluginVersion = true ? "0.
|
|
1954
|
+
var pluginVersion = true ? "0.4.0+ef00cc0" : void 0;
|
|
1938
1955
|
async function getApi() {
|
|
1939
1956
|
const api = await getAuthenticatedClient();
|
|
1940
1957
|
if (!api) {
|
|
@@ -2060,13 +2077,12 @@ async function handleToolsCall(name, opts) {
|
|
|
2060
2077
|
}
|
|
2061
2078
|
|
|
2062
2079
|
// src/cli.ts
|
|
2063
|
-
ensureNativeModules();
|
|
2064
2080
|
await initSentry();
|
|
2065
2081
|
if (process.argv.length <= 2 || process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
2066
2082
|
printBanner();
|
|
2067
2083
|
}
|
|
2068
2084
|
var program = new Command().name("fml").description("FML agent tools for Claude Code").version(
|
|
2069
|
-
true ? "0.
|
|
2085
|
+
true ? "0.4.0+ef00cc0" : "dev"
|
|
2070
2086
|
);
|
|
2071
2087
|
var tools = program.command("tools", { hidden: true }).description("List all available CLI commands (including hidden agent tools)").action(() => {
|
|
2072
2088
|
function formatCmd(cmd, prefix = "") {
|
|
@@ -2090,7 +2106,10 @@ var tools = program.command("tools", { hidden: true }).description("List all ava
|
|
|
2090
2106
|
tools.command("list").description("List backend tools available via the dynamic catalog").option("--category <category>", "Filter by category").option("--json", "Output as JSON").action((opts) => handleToolsList(opts));
|
|
2091
2107
|
tools.command("describe").description("Show description and input schema for a backend tool").argument("<name>", "Tool name (e.g. integration-github)").option("--json", "Output as JSON").action((name, opts) => handleToolsDescribe(name, opts));
|
|
2092
2108
|
tools.command("call").description("Invoke a backend tool by name with JSON args").argument("<name>", "Tool name").option("--args <json>", "Args as a JSON string (defaults to {})").option("--file <path>", "Path to a JSON file containing args").action((name, opts) => handleToolsCall(name, opts));
|
|
2093
|
-
program.command("install").description("Register plugin, hooks, and daemons").
|
|
2109
|
+
program.command("install").description("Register plugin, hooks, and daemons").option(
|
|
2110
|
+
"--force",
|
|
2111
|
+
"Reinstall even if already set up (upgrades panopticon, re-registers plugin)"
|
|
2112
|
+
).action((opts) => handleInstall(opts));
|
|
2094
2113
|
program.command("uninstall").description("Remove fml plugin, hooks, and optionally all data").option(
|
|
2095
2114
|
"--target <target>",
|
|
2096
2115
|
"Target CLI: claude, gemini, codex, claude-desktop, all"
|
|
@@ -2106,9 +2125,12 @@ program.command("env").description("Show or switch environment (dev/prod)").argu
|
|
|
2106
2125
|
if (target) handleEnvSwitch(target);
|
|
2107
2126
|
else handleEnvShow();
|
|
2108
2127
|
});
|
|
2109
|
-
program.command("
|
|
2110
|
-
|
|
2128
|
+
var panopticon = program.command("panopticon").description("Start or stop the panopticon server");
|
|
2129
|
+
panopticon.command("start").description("Start the panopticon server").action(() => handleStart());
|
|
2130
|
+
panopticon.command("stop").description("Stop the panopticon server").action(() => handleStop());
|
|
2111
2131
|
var sync = program.command("sync").description("Manage sync configuration");
|
|
2132
|
+
sync.command("start").description("Start syncing to configured targets (enable sync)").action(() => handleSyncStart());
|
|
2133
|
+
sync.command("stop").description("Stop syncing (disable sync; leaves targets configured)").action(() => handleSyncStop());
|
|
2112
2134
|
sync.command("setup").description("Configure sync targets (convenience shortcut)").action(() => handleSyncSetup());
|
|
2113
2135
|
sync.command("list").description("List all sync targets").action(() => handleSyncList());
|
|
2114
2136
|
sync.command("add").description("Add a sync target").argument("<name>", "Unique target name").argument("<url>", "OTLP HTTP endpoint URL").option("--token-cmd <cmd>", "Shell command that returns a bearer token").option("--token <token>", "Static bearer token").action(
|