@fml-inc/fml 0.8.7 → 0.8.10-dev.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.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/{chunk-6BTC3S7C.js → chunk-2DGHAVVR.js} +1 -1
- package/dist/{chunk-LPSZCTTJ.js → chunk-2ZCXGYI5.js} +1 -1
- package/dist/{chunk-4YLSKFDU.js → chunk-5PEFBQNX.js} +107 -64
- package/dist/{chunk-XFALHTDL.js → chunk-5SI6S4HW.js} +2 -2
- package/dist/{chunk-LOJPIJZW.js → chunk-GDOXY4W2.js} +2 -2
- package/dist/{chunk-B3H2YWPJ.js → chunk-HDAFEHUY.js} +62 -14
- package/dist/{chunk-5JJQWV4G.js → chunk-JPHN4VKM.js} +1 -1
- package/dist/{chunk-PJWMSHHZ.js → chunk-WE7MAWAW.js} +1 -1
- package/dist/cli.js +20 -10
- package/dist/hook-handler.js +4 -4
- package/dist/mcp/server.js +2 -2
- package/dist/observer-server.js +53 -13
- package/dist/pi/extension.js +1427 -1198
- package/dist/sync-cycle.js +3 -3
- package/dist/telemetry-config-HRSNDMTG.js +148 -0
- package/dist/{token-store-ORR5MIJL.js → token-store-GYU3AXBK.js} +1 -1
- package/package.json +2 -2
package/dist/sync-cycle.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runObserverSyncCycle
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-2DGHAVVR.js";
|
|
5
|
+
import "./chunk-JPHN4VKM.js";
|
|
6
6
|
import "./chunk-F57IFY3K.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-HDAFEHUY.js";
|
|
8
8
|
import "./chunk-MLKGABMK.js";
|
|
9
9
|
|
|
10
10
|
// src/sync-cycle.ts
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_SYNC_URL,
|
|
3
|
+
FML_DATA_DIR,
|
|
4
|
+
getActiveEnv,
|
|
5
|
+
getSiteUrlForConvexUrl,
|
|
6
|
+
isHostedBackendUrl,
|
|
7
|
+
readTokens,
|
|
8
|
+
resolveEnvConvexUrl
|
|
9
|
+
} from "./chunk-HDAFEHUY.js";
|
|
10
|
+
import "./chunk-MLKGABMK.js";
|
|
11
|
+
|
|
12
|
+
// src/telemetry-config.ts
|
|
13
|
+
import { createHash, randomUUID } from "crypto";
|
|
14
|
+
import fs from "fs";
|
|
15
|
+
import path from "path";
|
|
16
|
+
var CONFIG_TTL_MS = 60 * 60 * 1e3;
|
|
17
|
+
var CONFIG_TIMEOUT_MS = 1500;
|
|
18
|
+
var refreshesInFlight = /* @__PURE__ */ new Map();
|
|
19
|
+
function parseSentryConfig(value) {
|
|
20
|
+
if (value === null) return null;
|
|
21
|
+
if (!value || typeof value !== "object")
|
|
22
|
+
throw new Error("Invalid Sentry config");
|
|
23
|
+
const config = value;
|
|
24
|
+
if (typeof config.dsn !== "string") throw new Error("Invalid Sentry DSN");
|
|
25
|
+
const url = new URL(config.dsn);
|
|
26
|
+
if (url.protocol !== "https:" || !url.username || url.password || url.search || url.hash || !/^\/(?:.*\/)?\d+$/.test(url.pathname)) {
|
|
27
|
+
throw new Error("Invalid Sentry DSN");
|
|
28
|
+
}
|
|
29
|
+
if (config.environment !== void 0 && (typeof config.environment !== "string" || !config.environment.trim())) {
|
|
30
|
+
throw new Error("Invalid Sentry environment");
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
dsn: config.dsn,
|
|
34
|
+
...typeof config.environment === "string" ? { environment: config.environment } : {}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function cachePathFor(siteUrl) {
|
|
38
|
+
const key = isHostedBackendUrl(siteUrl) ? DEFAULT_SYNC_URL : siteUrl;
|
|
39
|
+
return path.join(
|
|
40
|
+
FML_DATA_DIR,
|
|
41
|
+
"telemetry-config",
|
|
42
|
+
`${createHash("sha256").update(key).digest("hex")}.json`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
function readCache(siteUrl) {
|
|
46
|
+
try {
|
|
47
|
+
const value = JSON.parse(
|
|
48
|
+
fs.readFileSync(cachePathFor(siteUrl), "utf8")
|
|
49
|
+
);
|
|
50
|
+
if (!value || typeof value !== "object") return void 0;
|
|
51
|
+
const entry = value;
|
|
52
|
+
if (typeof entry.siteUrl !== "string" || entry.siteUrl !== siteUrl && !(isHostedBackendUrl(entry.siteUrl) && isHostedBackendUrl(siteUrl)) || typeof entry.fetchedAt !== "number" || !Number.isFinite(entry.fetchedAt))
|
|
53
|
+
return void 0;
|
|
54
|
+
return {
|
|
55
|
+
siteUrl: entry.siteUrl,
|
|
56
|
+
fetchedAt: entry.fetchedAt,
|
|
57
|
+
...entry.sentry === void 0 ? {} : { sentry: parseSentryConfig(entry.sentry) }
|
|
58
|
+
};
|
|
59
|
+
} catch {
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function writeCache(entry) {
|
|
64
|
+
const cachePath = cachePathFor(entry.siteUrl);
|
|
65
|
+
const temporaryPath = `${cachePath}.${randomUUID()}.tmp`;
|
|
66
|
+
try {
|
|
67
|
+
fs.mkdirSync(path.dirname(cachePath), { recursive: true, mode: 448 });
|
|
68
|
+
fs.writeFileSync(temporaryPath, JSON.stringify(entry), { mode: 384 });
|
|
69
|
+
fs.renameSync(temporaryPath, cachePath);
|
|
70
|
+
} catch {
|
|
71
|
+
} finally {
|
|
72
|
+
try {
|
|
73
|
+
fs.rmSync(temporaryPath, { force: true });
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function refreshCliTelemetryConfig(opts) {
|
|
79
|
+
try {
|
|
80
|
+
if (opts.signal?.aborted) return;
|
|
81
|
+
const siteUrl = opts.siteUrl ?? getSiteUrlForConvexUrl(resolveEnvConvexUrl(opts.envName));
|
|
82
|
+
if (!opts.siteUrl && !isHostedBackendUrl(siteUrl)) return;
|
|
83
|
+
if (!opts.force) {
|
|
84
|
+
const age = Date.now() - (readCache(siteUrl)?.fetchedAt ?? Number.NaN);
|
|
85
|
+
if (age >= 0 && age < CONFIG_TTL_MS) return;
|
|
86
|
+
}
|
|
87
|
+
const key = cachePathFor(siteUrl);
|
|
88
|
+
let refresh = refreshesInFlight.get(key);
|
|
89
|
+
if (!refresh) {
|
|
90
|
+
refresh = fetchAndCache(siteUrl, opts.accessToken, opts.signal).finally(
|
|
91
|
+
() => refreshesInFlight.delete(key)
|
|
92
|
+
);
|
|
93
|
+
refreshesInFlight.set(key, refresh);
|
|
94
|
+
}
|
|
95
|
+
await refresh;
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function fetchAndCache(siteUrl, accessToken, signal) {
|
|
100
|
+
const timeout = AbortSignal.timeout(CONFIG_TIMEOUT_MS);
|
|
101
|
+
const response = await fetch(`${siteUrl}/api/telemetry/config`, {
|
|
102
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
103
|
+
signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
|
|
104
|
+
redirect: "error"
|
|
105
|
+
});
|
|
106
|
+
if (!response.ok) throw new Error("Config fetch failed");
|
|
107
|
+
const data = await response.json();
|
|
108
|
+
if (!data || typeof data !== "object" || data.ok !== true)
|
|
109
|
+
throw new Error("Invalid telemetry config");
|
|
110
|
+
const sentry = data.sentry;
|
|
111
|
+
writeCache({
|
|
112
|
+
siteUrl,
|
|
113
|
+
fetchedAt: Date.now(),
|
|
114
|
+
...sentry === void 0 ? {} : { sentry: parseSentryConfig(sentry) }
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async function resolveCliSentryConfig(defaultDsn, forceRefresh = false) {
|
|
118
|
+
if (process.env.FML_SENTRY_DSN !== void 0) {
|
|
119
|
+
return process.env.FML_SENTRY_DSN ? {
|
|
120
|
+
dsn: process.env.FML_SENTRY_DSN,
|
|
121
|
+
environment: process.env.FML_SENTRY_ENVIRONMENT
|
|
122
|
+
} : null;
|
|
123
|
+
}
|
|
124
|
+
const active = getActiveEnv();
|
|
125
|
+
const siteUrl = getSiteUrlForConvexUrl(active.convexUrl);
|
|
126
|
+
const hosted = active.convexUrl !== null && isHostedBackendUrl(active.convexUrl) && isHostedBackendUrl(siteUrl);
|
|
127
|
+
const fallback = hosted && defaultDsn ? {
|
|
128
|
+
dsn: defaultDsn,
|
|
129
|
+
environment: process.env.DEPLOYMENT_ENV ?? process.env.NODE_ENV ?? "production"
|
|
130
|
+
} : null;
|
|
131
|
+
const auth = readTokens(active.name);
|
|
132
|
+
if (!auth || auth.refreshInvalidAt || (auth.siteUrl ? auth.siteUrl !== siteUrl && !(hosted && isHostedBackendUrl(auth.siteUrl)) : !hosted))
|
|
133
|
+
return fallback;
|
|
134
|
+
if (forceRefresh && auth.expiresAt > Date.now()) {
|
|
135
|
+
await refreshCliTelemetryConfig({
|
|
136
|
+
envName: active.name,
|
|
137
|
+
accessToken: auth.accessToken,
|
|
138
|
+
siteUrl: auth.siteUrl,
|
|
139
|
+
force: true
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
const cached = readCache(siteUrl);
|
|
143
|
+
return cached?.sentry === void 0 ? fallback : cached.sentry;
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
refreshCliTelemetryConfig,
|
|
147
|
+
resolveCliSentryConfig
|
|
148
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fml-inc/fml",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10-dev.1",
|
|
4
4
|
"description": "FML CLI and agent tools",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"open": "^10.1.0",
|
|
34
34
|
"ora": "^8.2.0",
|
|
35
35
|
"picocolors": "^1.1.1",
|
|
36
|
-
"smol-toml": "
|
|
36
|
+
"smol-toml": "1.7.1",
|
|
37
37
|
"zod": "4.1.12"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|