@fml-inc/fml 0.7.0 → 0.8.0-rc.1

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 (45) hide show
  1. package/.claude-plugin/plugin.json +2 -4
  2. package/README.md +312 -79
  3. package/agent-assets/fml/SKILL.md +180 -0
  4. package/bin/fml +4 -18
  5. package/bin/hook-handler +7 -0
  6. package/bin/mcp-server +4 -18
  7. package/bin/observer +6 -0
  8. package/dist/chunk-7DR5X7XI.js +1522 -0
  9. package/dist/chunk-CK5MEL5J.js +140 -0
  10. package/dist/chunk-DEMVOVVY.js +4442 -0
  11. package/dist/chunk-F66IBM3Z.js +1037 -0
  12. package/dist/chunk-HUR2HCUG.js +953 -0
  13. package/dist/chunk-I4GZPTBM.js +109 -0
  14. package/dist/chunk-JXSSPQ5B.js +3331 -0
  15. package/dist/{chunk-PZ5AY32C.js → chunk-MLKGABMK.js} +0 -1
  16. package/dist/{chunk-PTUZMKSN.js → chunk-P3QLARXE.js} +34 -23
  17. package/dist/chunk-T5YVLSUC.js +138 -0
  18. package/dist/cli.js +7523 -1005
  19. package/dist/{esm-2EUDFZWP.js → esm-4YKLKFRX.js} +546 -741
  20. package/dist/hook-handler.js +190 -0
  21. package/dist/mcp/server.js +112 -98
  22. package/dist/observer-server.js +1566 -0
  23. package/dist/sync-cycle.js +31 -0
  24. package/dist/{token-store-3IQSLOTQ.js → token-store-R7WXEPLI.js} +10 -3
  25. package/hooks/hooks.json +290 -0
  26. package/package.json +24 -27
  27. package/dist/chunk-PTUZMKSN.js.map +0 -1
  28. package/dist/chunk-PZ5AY32C.js.map +0 -1
  29. package/dist/chunk-S4TJNLEN.js +0 -61
  30. package/dist/chunk-S4TJNLEN.js.map +0 -1
  31. package/dist/chunk-VV4JHH4H.js +0 -437
  32. package/dist/chunk-VV4JHH4H.js.map +0 -1
  33. package/dist/chunk-YH5ROR7G.js +0 -69
  34. package/dist/chunk-YH5ROR7G.js.map +0 -1
  35. package/dist/cli.js.map +0 -1
  36. package/dist/daemon-MSLDHLMH.js +0 -21
  37. package/dist/daemon-MSLDHLMH.js.map +0 -1
  38. package/dist/daemon-utils-OODBIYB7.js +0 -10
  39. package/dist/daemon-utils-OODBIYB7.js.map +0 -1
  40. package/dist/esm-2EUDFZWP.js.map +0 -1
  41. package/dist/mcp/server.js.map +0 -1
  42. package/dist/postinstall.js +0 -16
  43. package/dist/postinstall.js.map +0 -1
  44. package/dist/token-store-3IQSLOTQ.js.map +0 -1
  45. package/scripts/postinstall.cjs +0 -25
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runObserverSyncCycle
4
+ } from "./chunk-CK5MEL5J.js";
5
+ import "./chunk-JXSSPQ5B.js";
6
+ import "./chunk-7DR5X7XI.js";
7
+ import "./chunk-MLKGABMK.js";
8
+
9
+ // src/sync-cycle.ts
10
+ var abort = new AbortController();
11
+ for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
12
+ process.once(signal, () => abort.abort());
13
+ }
14
+ try {
15
+ const result = await runObserverSyncCycle(abort.signal);
16
+ if (process.send) {
17
+ await new Promise((resolve, reject) => {
18
+ process.send?.(result, void 0, void 0, (error) => {
19
+ if (error) reject(error);
20
+ else resolve();
21
+ });
22
+ });
23
+ } else {
24
+ console.log(JSON.stringify(result));
25
+ }
26
+ } catch (error) {
27
+ if (!abort.signal.aborted) console.error(error);
28
+ process.exitCode = abort.signal.aborted ? 0 : 1;
29
+ } finally {
30
+ process.disconnect?.();
31
+ }
@@ -1,20 +1,27 @@
1
1
  import {
2
2
  SERVICE_TOKEN_LOGIN_USER_ID,
3
+ clearStoredCredentials,
3
4
  getSelectedOrg,
5
+ getSelectedOrgId,
6
+ getSyncDestinationAuthBinding,
7
+ getSyncDestinationIdentity,
4
8
  getValidToken,
5
9
  readTokens,
6
10
  setSelectedOrg,
7
11
  storeServiceRefreshToken,
8
12
  writeTokens
9
- } from "./chunk-VV4JHH4H.js";
10
- import "./chunk-PZ5AY32C.js";
13
+ } from "./chunk-7DR5X7XI.js";
14
+ import "./chunk-MLKGABMK.js";
11
15
  export {
12
16
  SERVICE_TOKEN_LOGIN_USER_ID,
17
+ clearStoredCredentials,
13
18
  getSelectedOrg,
19
+ getSelectedOrgId,
20
+ getSyncDestinationAuthBinding,
21
+ getSyncDestinationIdentity,
14
22
  getValidToken,
15
23
  readTokens,
16
24
  setSelectedOrg,
17
25
  storeServiceRefreshToken,
18
26
  writeTokens
19
27
  };
20
- //# sourceMappingURL=token-store-3IQSLOTQ.js.map
@@ -0,0 +1,290 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node",
9
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "Setup": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "node",
20
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "InstructionsLoaded": [
26
+ {
27
+ "hooks": [
28
+ {
29
+ "type": "command",
30
+ "command": "node",
31
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
32
+ }
33
+ ]
34
+ }
35
+ ],
36
+ "UserPromptSubmit": [
37
+ {
38
+ "hooks": [
39
+ {
40
+ "type": "command",
41
+ "command": "node",
42
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
43
+ }
44
+ ]
45
+ }
46
+ ],
47
+ "UserPromptExpansion": [
48
+ {
49
+ "hooks": [
50
+ {
51
+ "type": "command",
52
+ "command": "node",
53
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
54
+ }
55
+ ]
56
+ }
57
+ ],
58
+ "PreToolUse": [
59
+ {
60
+ "hooks": [
61
+ {
62
+ "type": "command",
63
+ "command": "node",
64
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ "PermissionRequest": [
70
+ {
71
+ "hooks": [
72
+ {
73
+ "type": "command",
74
+ "command": "node",
75
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
76
+ }
77
+ ]
78
+ }
79
+ ],
80
+ "PostToolUse": [
81
+ {
82
+ "hooks": [
83
+ {
84
+ "type": "command",
85
+ "command": "node",
86
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
87
+ }
88
+ ]
89
+ }
90
+ ],
91
+ "PostToolUseFailure": [
92
+ {
93
+ "hooks": [
94
+ {
95
+ "type": "command",
96
+ "command": "node",
97
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
98
+ }
99
+ ]
100
+ }
101
+ ],
102
+ "PostToolBatch": [
103
+ {
104
+ "hooks": [
105
+ {
106
+ "type": "command",
107
+ "command": "node",
108
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
109
+ }
110
+ ]
111
+ }
112
+ ],
113
+ "PermissionDenied": [
114
+ {
115
+ "hooks": [
116
+ {
117
+ "type": "command",
118
+ "command": "node",
119
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
120
+ }
121
+ ]
122
+ }
123
+ ],
124
+ "Notification": [
125
+ {
126
+ "hooks": [
127
+ {
128
+ "type": "command",
129
+ "command": "node",
130
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
131
+ }
132
+ ]
133
+ }
134
+ ],
135
+ "SubagentStart": [
136
+ {
137
+ "hooks": [
138
+ {
139
+ "type": "command",
140
+ "command": "node",
141
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
142
+ }
143
+ ]
144
+ }
145
+ ],
146
+ "SubagentStop": [
147
+ {
148
+ "hooks": [
149
+ {
150
+ "type": "command",
151
+ "command": "node",
152
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
153
+ }
154
+ ]
155
+ }
156
+ ],
157
+ "TaskCreated": [
158
+ {
159
+ "hooks": [
160
+ {
161
+ "type": "command",
162
+ "command": "node",
163
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
164
+ }
165
+ ]
166
+ }
167
+ ],
168
+ "TaskCompleted": [
169
+ {
170
+ "hooks": [
171
+ {
172
+ "type": "command",
173
+ "command": "node",
174
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
175
+ }
176
+ ]
177
+ }
178
+ ],
179
+ "Stop": [
180
+ {
181
+ "hooks": [
182
+ {
183
+ "type": "command",
184
+ "command": "node",
185
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
186
+ }
187
+ ]
188
+ }
189
+ ],
190
+ "StopFailure": [
191
+ {
192
+ "hooks": [
193
+ {
194
+ "type": "command",
195
+ "command": "node",
196
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
197
+ }
198
+ ]
199
+ }
200
+ ],
201
+ "TeammateIdle": [
202
+ {
203
+ "hooks": [
204
+ {
205
+ "type": "command",
206
+ "command": "node",
207
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
208
+ }
209
+ ]
210
+ }
211
+ ],
212
+ "ConfigChange": [
213
+ {
214
+ "hooks": [
215
+ {
216
+ "type": "command",
217
+ "command": "node",
218
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
219
+ }
220
+ ]
221
+ }
222
+ ],
223
+ "CwdChanged": [
224
+ {
225
+ "hooks": [
226
+ {
227
+ "type": "command",
228
+ "command": "node",
229
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
230
+ }
231
+ ]
232
+ }
233
+ ],
234
+ "PreCompact": [
235
+ {
236
+ "hooks": [
237
+ {
238
+ "type": "command",
239
+ "command": "node",
240
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
241
+ }
242
+ ]
243
+ }
244
+ ],
245
+ "PostCompact": [
246
+ {
247
+ "hooks": [
248
+ {
249
+ "type": "command",
250
+ "command": "node",
251
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
252
+ }
253
+ ]
254
+ }
255
+ ],
256
+ "SessionEnd": [
257
+ {
258
+ "hooks": [
259
+ {
260
+ "type": "command",
261
+ "command": "node",
262
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
263
+ }
264
+ ]
265
+ }
266
+ ],
267
+ "Elicitation": [
268
+ {
269
+ "hooks": [
270
+ {
271
+ "type": "command",
272
+ "command": "node",
273
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
274
+ }
275
+ ]
276
+ }
277
+ ],
278
+ "ElicitationResult": [
279
+ {
280
+ "hooks": [
281
+ {
282
+ "type": "command",
283
+ "command": "node",
284
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/hook-handler", "claude"]
285
+ }
286
+ ]
287
+ }
288
+ ]
289
+ }
290
+ }
package/package.json CHANGED
@@ -1,59 +1,56 @@
1
1
  {
2
2
  "name": "@fml-inc/fml",
3
- "version": "0.7.0",
4
- "description": "FML agent tools for Claude Code",
3
+ "version": "0.8.0-rc.1",
4
+ "description": "FML CLI and agent tools",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
7
- "packageManager": "pnpm@10.17.1",
8
7
  "engines": {
9
- "node": ">=24.0.0"
8
+ "node": ">=22.15.0"
10
9
  },
11
10
  "bin": {
12
- "fml": "./bin/fml"
11
+ "fml": "./bin/fml",
12
+ "fml-mcp-server": "./bin/mcp-server"
13
13
  },
14
14
  "repository": {
15
15
  "type": "git",
16
- "url": "https://github.com/fml-inc/observe.git"
16
+ "url": "https://github.com/fml-inc/fml.git"
17
17
  },
18
18
  "publishConfig": {
19
19
  "registry": "https://registry.npmjs.org",
20
20
  "access": "public"
21
21
  },
22
22
  "files": [
23
+ "agent-assets/",
23
24
  "bin/",
24
25
  "dist/",
25
- "scripts/postinstall.cjs",
26
+ "hooks/",
26
27
  ".claude-plugin/"
27
28
  ],
28
- "scripts": {
29
- "build": "node scripts/generate-plugin-manifest.js && tsup",
30
- "dev": "tsup --watch",
31
- "prepack": "node scripts/generate-plugin-manifest.js",
32
- "pack:local": "pnpm build && pnpm pack",
33
- "postinstall": "node scripts/postinstall.cjs",
34
- "type-check": "tsc --noEmit",
35
- "test": "vitest run",
36
- "test:watch": "vitest"
37
- },
38
29
  "dependencies": {
39
- "@fml-inc/panopticon": "^0.2.16",
40
30
  "@modelcontextprotocol/sdk": "^1.0.0",
41
31
  "commander": "^13.1.0",
32
+ "jsonc-parser": "^3.3.1",
42
33
  "open": "^10.1.0",
43
34
  "ora": "^8.2.0",
44
35
  "picocolors": "^1.1.1",
45
- "zod": "^3.23.0"
46
- },
47
- "pnpm": {
48
- "onlyBuiltDependencies": [
49
- "esbuild"
50
- ]
36
+ "smol-toml": "^1.7.0",
37
+ "zod": "4.1.12"
51
38
  },
52
39
  "devDependencies": {
53
40
  "@sentry/core": "^10.43.0",
54
- "@types/node": "^24.0.0",
41
+ "@types/node": "^22.0.0",
55
42
  "tsup": "^8.0.0",
56
43
  "typescript": "^5",
57
- "vitest": "^4.1.0"
44
+ "@fml/panopticon": "0.0.0",
45
+ "@fml/agent-targets": "0.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "node scripts/generate-plugin-manifest.js && tsup",
49
+ "dev": "tsup --watch",
50
+ "pack:local": "pnpm pack",
51
+ "type-check": "tsc --noEmit",
52
+ "test": "pnpm build && vitest run && pnpm test:release",
53
+ "test:release": "node --test scripts/prepare-release-node-tests.mjs",
54
+ "test:watch": "vitest"
58
55
  }
59
- }
56
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/fml-client.ts"],"sourcesContent":["import { resolveRepoFromCwd } from \"@fml-inc/panopticon/repo\";\nimport { getSelectedOrg, getValidToken } from \"./auth/token-store.js\";\nimport { CONVEX_URL } from \"./config.js\";\nimport type {\n RepoConfigSnapshotDetail,\n RepoConfigSnapshotSummary,\n ResolvedRepo,\n UserConfigSnapshotDetail,\n UserConfigSnapshotSummary,\n} from \"./types.js\";\n\n// ── Shared plumbing ─────────────────────────────────────────────────────────\n\nexport type ToolCategory =\n | \"messages\"\n | \"slack\"\n | \"skills\"\n | \"analysis\"\n | \"integrations\"\n | \"engineering\"\n | \"memory\"\n | \"automations\"\n | \"amplitude\"\n | \"posthog\"\n | \"meta-ads\";\n\nexport interface PublicToolDescriptor {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n category?: ToolCategory;\n experimental?: boolean;\n}\n\nexport type KnownToolErrorCode =\n | \"UNAUTHENTICATED\"\n | \"TOKEN_EXPIRED\"\n | \"ACCESS_DENIED\"\n | \"ORG_REQUIRED\"\n | \"ORG_NOT_FOUND\"\n | \"REPO_NOT_FOUND\"\n | \"UNKNOWN_TOOL\"\n | \"INVALID_ARGS\"\n | \"RATE_LIMITED\"\n | \"INTERNAL_ERROR\";\n\nexport type ToolErrorCode = KnownToolErrorCode | (string & {});\n\nexport interface ToolResult {\n ok: boolean;\n result?: unknown;\n error?: string;\n code?: ToolErrorCode;\n}\n\nexport interface OrgInfo {\n _id: string;\n name: string;\n slug?: string;\n repos?: Array<{\n _id: string;\n fullName: string;\n owner: string;\n name: string;\n private: boolean;\n }>;\n}\n\nconst LOGIN_EXPIRED_MESSAGE =\n \"Authentication expired. Run `fml login` to sign in again, then restart Claude Code.\";\n\nfunction isAuthError(data: { error?: string; code?: string }): boolean {\n return (\n data.code === \"UNAUTHENTICATED\" ||\n data.code === \"TOKEN_EXPIRED\" ||\n data.error?.includes(\"Unauthorized\") === true ||\n data.error?.includes(\"not authenticated\") === true\n );\n}\n\nfunction normalizeToolResult(data: ToolResult): ToolResult {\n if (!data.ok && isAuthError(data)) {\n return { ok: false, error: LOGIN_EXPIRED_MESSAGE, code: data.code };\n }\n return data;\n}\n\nfunction requireToolResult<T>(result: ToolResult): T {\n if (!result.ok) {\n throw new Error(result.error ?? \"Backend request failed\");\n }\n return result.result as T;\n}\n\n// ── API client factory ──────────────────────────────────────────────────────\n\nexport function createFmlClient(token: string) {\n const isServiceToken = token.startsWith(\"fml_st_\");\n\n /**\n * Derive the Convex site URL (HTTP actions) from the cloud URL.\n * Convex uses paired domains: *.convex.cloud for client APIs,\n * *.convex.site for HTTP actions. This is a stable Convex convention.\n */\n function getSiteUrl(): string {\n return CONVEX_URL.replace(\".convex.cloud\", \".convex.site\").replace(\n /\\/$/,\n \"\",\n );\n }\n\n /** Authed fetch to a fml-be HTTP action, parsing the JSON envelope. */\n async function fetchContext(\n pathAndQuery: string,\n init: RequestInit,\n ): Promise<ToolResult> {\n try {\n const res = await fetch(`${getSiteUrl()}${pathAndQuery}`, {\n ...init,\n headers: {\n Authorization: `Bearer ${token}`,\n ...(init.body ? { \"Content-Type\": \"application/json\" } : {}),\n },\n });\n const text = await res.text();\n let data: unknown;\n try {\n data = JSON.parse(text);\n } catch {\n return { ok: false, error: `HTTP ${res.status}: ${text.slice(0, 200)}` };\n }\n if (!res.ok) {\n const envelope = data as { error?: string; code?: ToolErrorCode };\n return normalizeToolResult({\n ok: false,\n error: envelope.error ?? `HTTP ${res.status}`,\n code: envelope.code,\n });\n }\n return { ok: true, result: data };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n }\n\n return {\n // ── Orgs ──────────────────────────────────────────────────────────────\n\n async queryOrgs(): Promise<OrgInfo[]> {\n const result = await this.callBackend(\"list-orgs\", {});\n return requireToolResult<OrgInfo[] | null>(result) ?? [];\n },\n\n // ── Tool gateway ─────────────────────────────────────────────────────\n\n async callBackend(\n toolName: string,\n args: Record<string, unknown>,\n opts?: { org?: string },\n ): Promise<ToolResult> {\n try {\n // Unified CLI/MCP path: POST to the dual-auth HTTP endpoint for both\n // service tokens and OAuth/JWT user tokens. This keeps command handlers\n // independent of token class and matches the backend's agent tool contract.\n const body: Record<string, unknown> = { toolName, args };\n\n // Explicit org > stored org selection > repo-based inference.\n const org = opts?.org ?? getSelectedOrg();\n if (org) body.org = org;\n const repo = resolveRepoFromCwd(process.cwd());\n if (repo) body.repo = repo.repo;\n\n // Thread user identity for service-token-backed sandbox agents. For\n // JWT callers this is ignored by the backend; for service tokens the\n // backend validates membership before honoring the override.\n if (isServiceToken) {\n const { readTokens, SERVICE_TOKEN_LOGIN_USER_ID } = await import(\n \"./auth/token-store.js\"\n );\n const stored = readTokens();\n const storedUserExternalId =\n stored?.tokenType === \"service\" &&\n stored.user.id !== SERVICE_TOKEN_LOGIN_USER_ID\n ? stored.user.id\n : undefined;\n const userExternalId =\n process.env.FML_USER_EXTERNAL_ID ?? storedUserExternalId;\n if (userExternalId) body.userExternalId = userExternalId;\n }\n\n const res = await fetch(`${getSiteUrl()}/api/tools/execute`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(body),\n });\n const text = await res.text();\n let data: ToolResult;\n try {\n data = JSON.parse(text) as ToolResult;\n } catch {\n return {\n ok: false,\n error: `HTTP ${res.status}: ${text.slice(0, 200)}`,\n };\n }\n if (!res.ok && !data.error) {\n return { ok: false, error: `HTTP ${res.status}` };\n }\n return normalizeToolResult(data);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes(\"Unauthorized\") || msg.includes(\"not authenticated\")) {\n return { ok: false, error: LOGIN_EXPIRED_MESSAGE };\n }\n return { ok: false, error: msg };\n }\n },\n\n // ── Tool catalog ─────────────────────────────────────────────────────\n\n async listTools(pluginVersion?: string): Promise<PublicToolDescriptor[]> {\n try {\n // Always use the HTTP catalog endpoint — it accepts both a service\n // token and a user OAuth JWT. A raw client.query rejects the user JWT\n // (no `aud` claim, which Convex's client-protocol auth requires), so\n // the httpAction path is the only one that works for both.\n const url = new URL(`${getSiteUrl()}/api/tools/list`);\n if (pluginVersion) url.searchParams.set(\"pluginVersion\", pluginVersion);\n const res = await fetch(url.toString(), {\n headers: { Authorization: `Bearer ${token}` },\n });\n const text = await res.text();\n let data: {\n ok: boolean;\n descriptors?: PublicToolDescriptor[];\n error?: string;\n code?: ToolErrorCode;\n };\n try {\n data = JSON.parse(text) as typeof data;\n } catch {\n throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);\n }\n if (!data.ok || !data.descriptors) {\n if (isAuthError(data)) {\n throw new Error(LOGIN_EXPIRED_MESSAGE);\n }\n throw new Error(data.error ?? `HTTP ${res.status}`);\n }\n return data.descriptors;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes(\"Unauthorized\") || msg.includes(\"not authenticated\")) {\n throw new Error(LOGIN_EXPIRED_MESSAGE);\n }\n throw err;\n }\n },\n\n // ── Anamnesis ground-truth context ───────────────────────────────────\n\n /**\n * Call a read-only anamnesis context endpoint\n * (/v1/anamnesis/context/{path,commit,pr}). Sends the current token\n * (service token or user JWT) — the endpoint accepts either.\n */\n async anamnesisContext(\n kind: \"path\" | \"commit\" | \"pr\",\n params: Record<string, string | number | undefined>,\n ): Promise<ToolResult> {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v != null) qs.set(k, String(v));\n }\n return fetchContext(`/v1/anamnesis/context/${kind}?${qs.toString()}`, {\n method: \"GET\",\n });\n },\n\n /** POST /v1/anamnesis/context/query — generic predicate query. */\n async anamnesisQuery(body: Record<string, unknown>): Promise<ToolResult> {\n return fetchContext(\"/v1/anamnesis/context/query\", {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n },\n\n // ── Repo resolution ──────────────────────────────────────────────────\n\n async resolveRepo(\n orgSlug: string,\n repoFullName: string,\n ): Promise<ResolvedRepo | null> {\n const result = await this.callBackend(\n \"resolve-repo\",\n { orgSlug, repoFullName },\n { org: orgSlug },\n );\n return requireToolResult<ResolvedRepo | null>(result);\n },\n\n // ── Config snapshots ─────────────────────────────────────────────────\n\n async listUserConfigSnapshots(\n orgSlug: string,\n ): Promise<UserConfigSnapshotSummary[]> {\n const result = await this.callBackend(\n \"list-user-config-snapshots\",\n { orgSlug },\n { org: orgSlug },\n );\n return requireToolResult<UserConfigSnapshotSummary[] | null>(result) ?? [];\n },\n\n async getUserConfigDetail(\n orgSlug: string,\n githubUsername: string,\n ): Promise<UserConfigSnapshotDetail | null> {\n const result = await this.callBackend(\n \"get-user-config-snapshot\",\n { orgSlug, githubUsername },\n { org: orgSlug },\n );\n return requireToolResult<UserConfigSnapshotDetail | null>(result);\n },\n\n async listRepoConfigSnapshots(\n orgSlug: string,\n repository?: string,\n ): Promise<RepoConfigSnapshotSummary[]> {\n const result = await this.callBackend(\n \"list-repo-config-snapshots\",\n { orgSlug, repository },\n { org: orgSlug },\n );\n return requireToolResult<RepoConfigSnapshotSummary[] | null>(result) ?? [];\n },\n\n async getRepoConfigDetail(\n orgSlug: string,\n repository: string,\n ): Promise<RepoConfigSnapshotDetail | null> {\n const result = await this.callBackend(\n \"get-repo-config-snapshot\",\n { orgSlug, repository },\n { org: orgSlug },\n );\n return requireToolResult<RepoConfigSnapshotDetail | null>(result);\n },\n };\n}\n\n// ── Convenience: auto-authenticated client ──────────────────────────────────\n\n/**\n * Create an API client using the stored auth token.\n * Returns null if not authenticated.\n */\nexport async function getAuthenticatedClient() {\n const token = await getValidToken();\n if (!token) return null;\n return createFmlClient(token);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,61 +0,0 @@
1
- import {
2
- panopticonExec
3
- } from "./chunk-YH5ROR7G.js";
4
-
5
- // src/commands/daemon.ts
6
- function printPanopticonOutput(output, opts = {}) {
7
- const lines = output.trim().split("\n").filter(Boolean).filter(
8
- (line) => (
9
- // Top-level `fml start|stop` immediately starts/stops Panopticon after
10
- // toggling sync, so Panopticon's lower-level restart hint is already
11
- // satisfied and reads as confusing noise. Keep it for explicit
12
- // `fml sync start|stop`, where the user really is only changing sync.
13
- !opts.suppressRestartHint || line.trim() !== "Restart panopticon to apply."
14
- )
15
- );
16
- if (lines.length > 0) console.log(lines.join("\n"));
17
- }
18
- function parsePanopticonRunning() {
19
- const result = panopticonExec("status");
20
- const serverLine = result.stdout.split("\n").find((l) => l.startsWith("Server:"));
21
- return result.ok && /running/i.test(serverLine ?? "");
22
- }
23
- async function handlePanopticonStart() {
24
- const startResult = panopticonExec("start");
25
- console.log(
26
- startResult.stdout.trim() || (startResult.ok ? "Panopticon started" : "Failed to start panopticon")
27
- );
28
- }
29
- function handlePanopticonStop() {
30
- const stopResult = panopticonExec("stop");
31
- console.log(stopResult.stdout.trim() || "Panopticon stopped");
32
- }
33
- async function handleFmlStart() {
34
- handleSyncStart({ suppressRestartHint: true });
35
- await handlePanopticonStart();
36
- }
37
- function handleFmlStop() {
38
- handleSyncStop({ suppressRestartHint: true });
39
- handlePanopticonStop();
40
- }
41
- function handleSyncStart(opts = {}) {
42
- const result = panopticonExec("sync", "enable");
43
- const output = result.stdout.trim() || (result.ok ? "Sync enabled" : "Failed to enable sync");
44
- printPanopticonOutput(output, opts);
45
- }
46
- function handleSyncStop(opts = {}) {
47
- const result = panopticonExec("sync", "disable");
48
- const output = result.stdout.trim() || (result.ok ? "Sync disabled" : "Failed to disable sync");
49
- printPanopticonOutput(output, opts);
50
- }
51
-
52
- export {
53
- parsePanopticonRunning,
54
- handlePanopticonStart,
55
- handlePanopticonStop,
56
- handleFmlStart,
57
- handleFmlStop,
58
- handleSyncStart,
59
- handleSyncStop
60
- };
61
- //# sourceMappingURL=chunk-S4TJNLEN.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/daemon.ts"],"sourcesContent":["import { panopticonExec } from \"../daemon-utils.js\";\n\n// ── Status helpers (used by doctor, status) ─────────────────────────────────\n\nfunction printPanopticonOutput(output: string, opts: { suppressRestartHint?: boolean } = {}): void {\n const lines = output\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .filter(\n (line) =>\n // Top-level `fml start|stop` immediately starts/stops Panopticon after\n // toggling sync, so Panopticon's lower-level restart hint is already\n // satisfied and reads as confusing noise. Keep it for explicit\n // `fml sync start|stop`, where the user really is only changing sync.\n !opts.suppressRestartHint || line.trim() !== \"Restart panopticon to apply.\",\n );\n if (lines.length > 0) console.log(lines.join(\"\\n\"));\n}\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 (component-level lifecycle) ──────────────\n\nexport async function handlePanopticonStart(): 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 handlePanopticonStop(): void {\n const stopResult = panopticonExec(\"stop\");\n console.log(stopResult.stdout.trim() || \"Panopticon stopped\");\n}\n\n// ── FML runtime start / stop (product-level lifecycle) ──────────────────────\n\nexport async function handleFmlStart(): Promise<void> {\n handleSyncStart({ suppressRestartHint: true });\n await handlePanopticonStart();\n}\n\nexport function handleFmlStop(): void {\n handleSyncStop({ suppressRestartHint: true });\n handlePanopticonStop();\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(opts: { suppressRestartHint?: boolean } = {}): void {\n const result = panopticonExec(\"sync\", \"enable\");\n const output = result.stdout.trim() ||\n (result.ok ? \"Sync enabled\" : \"Failed to enable sync\");\n printPanopticonOutput(output, opts);\n}\n\nexport function handleSyncStop(opts: { suppressRestartHint?: boolean } = {}): void {\n const result = panopticonExec(\"sync\", \"disable\");\n const output = result.stdout.trim() ||\n (result.ok ? \"Sync disabled\" : \"Failed to disable sync\");\n printPanopticonOutput(output, opts);\n}\n"],"mappings":";;;;;AAIA,SAAS,sBAAsB,QAAgB,OAA0C,CAAC,GAAS;AACjG,QAAM,QAAQ,OACX,KAAK,EACL,MAAM,IAAI,EACV,OAAO,OAAO,EACd;AAAA,IACC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,MAKC,CAAC,KAAK,uBAAuB,KAAK,KAAK,MAAM;AAAA;AAAA,EACjD;AACF,MAAI,MAAM,SAAS,EAAG,SAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;AACpD;AAEO,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,wBAAuC;AAC3D,QAAM,cAAc,eAAe,OAAO;AAC1C,UAAQ;AAAA,IACN,YAAY,OAAO,KAAK,MACrB,YAAY,KAAK,uBAAuB;AAAA,EAC7C;AACF;AAEO,SAAS,uBAA6B;AAC3C,QAAM,aAAa,eAAe,MAAM;AACxC,UAAQ,IAAI,WAAW,OAAO,KAAK,KAAK,oBAAoB;AAC9D;AAIA,eAAsB,iBAAgC;AACpD,kBAAgB,EAAE,qBAAqB,KAAK,CAAC;AAC7C,QAAM,sBAAsB;AAC9B;AAEO,SAAS,gBAAsB;AACpC,iBAAe,EAAE,qBAAqB,KAAK,CAAC;AAC5C,uBAAqB;AACvB;AAMO,SAAS,gBAAgB,OAA0C,CAAC,GAAS;AAClF,QAAM,SAAS,eAAe,QAAQ,QAAQ;AAC9C,QAAM,SAAS,OAAO,OAAO,KAAK,MAC/B,OAAO,KAAK,iBAAiB;AAChC,wBAAsB,QAAQ,IAAI;AACpC;AAEO,SAAS,eAAe,OAA0C,CAAC,GAAS;AACjF,QAAM,SAAS,eAAe,QAAQ,SAAS;AAC/C,QAAM,SAAS,OAAO,OAAO,KAAK,MAC/B,OAAO,KAAK,kBAAkB;AACjC,wBAAsB,QAAQ,IAAI;AACpC;","names":[]}