fleetlens 0.10.2 → 0.10.3
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/app/apps/web/.next/BUILD_ID +1 -1
- package/app/apps/web/.next/build-manifest.json +3 -3
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0.za3_3._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_0m67-xi._.js +1 -1
- package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/app/apps/web/.next/server/pages/500.html +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/app/apps/web/.next/static/chunks/0o3uir-f-t73p.js +14 -0
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +367 -53
- package/dist/index.js +249 -184
- package/package.json +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0s4dg1y._.js +0 -3
- package/app/apps/web/.next/static/chunks/063r4x2.9csh8.js +0 -14
- /package/app/apps/web/.next/static/{XTns3Y6jHWmb7EYKzQi55 → dGL5ege3-_A21c65DU7Jq}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{XTns3Y6jHWmb7EYKzQi55 → dGL5ege3-_A21c65DU7Jq}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{XTns3Y6jHWmb7EYKzQi55 → dGL5ege3-_A21c65DU7Jq}/_ssgManifest.js +0 -0
package/dist/daemon-worker.js
CHANGED
|
@@ -8,6 +8,40 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// src/usage/storage.ts
|
|
12
|
+
import { appendFileSync, mkdirSync, readFileSync as readFileSync2, existsSync } from "node:fs";
|
|
13
|
+
import { dirname } from "node:path";
|
|
14
|
+
function appendSnapshot(filePath, snapshot) {
|
|
15
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
16
|
+
appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
|
|
17
|
+
}
|
|
18
|
+
function readSnapshots(filePath) {
|
|
19
|
+
if (!existsSync(filePath)) return [];
|
|
20
|
+
const content = readFileSync2(filePath, "utf8");
|
|
21
|
+
const snapshots = [];
|
|
22
|
+
for (const line of content.split("\n")) {
|
|
23
|
+
if (!line.trim()) continue;
|
|
24
|
+
try {
|
|
25
|
+
snapshots.push(JSON.parse(line));
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return snapshots;
|
|
30
|
+
}
|
|
31
|
+
function latestClaudeCodeSnapshot(filePath) {
|
|
32
|
+
const all = readSnapshots(filePath);
|
|
33
|
+
for (let i = all.length - 1; i >= 0; i--) {
|
|
34
|
+
const s = all[i];
|
|
35
|
+
if (!s.agent || s.agent === "claude-code") return s;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
var init_storage = __esm({
|
|
40
|
+
"src/usage/storage.ts"() {
|
|
41
|
+
"use strict";
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
11
45
|
// ../parser/dist/analytics.js
|
|
12
46
|
function toLocalDay(ms) {
|
|
13
47
|
const d = new Date(ms);
|
|
@@ -3742,8 +3776,10 @@ var init_dist = __esm({
|
|
|
3742
3776
|
});
|
|
3743
3777
|
|
|
3744
3778
|
// src/daemon-worker.ts
|
|
3745
|
-
import {
|
|
3746
|
-
import {
|
|
3779
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
3780
|
+
import { appendFileSync as appendFileSync5, closeSync, existsSync as existsSync11, mkdirSync as mkdirSync12, openSync, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "node:fs";
|
|
3781
|
+
import { dirname as dirname8, join as join8 } from "node:path";
|
|
3782
|
+
import { fileURLToPath } from "node:url";
|
|
3747
3783
|
|
|
3748
3784
|
// src/usage/token.ts
|
|
3749
3785
|
import { execFileSync } from "node:child_process";
|
|
@@ -3883,36 +3919,8 @@ function normalizeExtra(raw) {
|
|
|
3883
3919
|
};
|
|
3884
3920
|
}
|
|
3885
3921
|
|
|
3886
|
-
// src/usage/storage.ts
|
|
3887
|
-
import { appendFileSync, mkdirSync, readFileSync as readFileSync2, existsSync } from "node:fs";
|
|
3888
|
-
import { dirname } from "node:path";
|
|
3889
|
-
function appendSnapshot(filePath, snapshot) {
|
|
3890
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
3891
|
-
appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
|
|
3892
|
-
}
|
|
3893
|
-
function readSnapshots(filePath) {
|
|
3894
|
-
if (!existsSync(filePath)) return [];
|
|
3895
|
-
const content = readFileSync2(filePath, "utf8");
|
|
3896
|
-
const snapshots = [];
|
|
3897
|
-
for (const line of content.split("\n")) {
|
|
3898
|
-
if (!line.trim()) continue;
|
|
3899
|
-
try {
|
|
3900
|
-
snapshots.push(JSON.parse(line));
|
|
3901
|
-
} catch {
|
|
3902
|
-
}
|
|
3903
|
-
}
|
|
3904
|
-
return snapshots;
|
|
3905
|
-
}
|
|
3906
|
-
function latestClaudeCodeSnapshot(filePath) {
|
|
3907
|
-
const all = readSnapshots(filePath);
|
|
3908
|
-
for (let i = all.length - 1; i >= 0; i--) {
|
|
3909
|
-
const s = all[i];
|
|
3910
|
-
if (!s.agent || s.agent === "claude-code") return s;
|
|
3911
|
-
}
|
|
3912
|
-
return null;
|
|
3913
|
-
}
|
|
3914
|
-
|
|
3915
3922
|
// src/daemon-worker.ts
|
|
3923
|
+
init_storage();
|
|
3916
3924
|
init_fs();
|
|
3917
3925
|
|
|
3918
3926
|
// src/usage/backoff.ts
|
|
@@ -3945,8 +3953,10 @@ function writeTeamConfig(config, dir) {
|
|
|
3945
3953
|
|
|
3946
3954
|
// src/team/push.ts
|
|
3947
3955
|
init_dist();
|
|
3956
|
+
init_storage();
|
|
3948
3957
|
import { randomUUID } from "node:crypto";
|
|
3949
3958
|
var SNAPSHOT_FRESHNESS_MS = 10 * 60 * 1e3;
|
|
3959
|
+
var POST_TIMEOUT_MS = 15e3;
|
|
3950
3960
|
function readLatestUsageSnapshotForWire(filePath, nowMs = Date.now()) {
|
|
3951
3961
|
const raw = latestClaudeCodeSnapshot(filePath);
|
|
3952
3962
|
if (!raw) return null;
|
|
@@ -4005,6 +4015,7 @@ function buildIngestPayload(rollup, usageSnapshot, planTier, cyclePeaks) {
|
|
|
4005
4015
|
async function pushToTeamServer(config, payload) {
|
|
4006
4016
|
const res = await fetch(`${config.serverUrl}/api/ingest/metrics`, {
|
|
4007
4017
|
method: "POST",
|
|
4018
|
+
signal: AbortSignal.timeout(POST_TIMEOUT_MS),
|
|
4008
4019
|
headers: {
|
|
4009
4020
|
"Content-Type": "application/json",
|
|
4010
4021
|
Authorization: `Bearer ${config.bearerToken}`
|
|
@@ -4066,6 +4077,9 @@ function pruneOldest(queuePath) {
|
|
|
4066
4077
|
writeFileSync2(queuePath, keep.join("\n") + "\n", "utf8");
|
|
4067
4078
|
}
|
|
4068
4079
|
|
|
4080
|
+
// src/team/backfill.ts
|
|
4081
|
+
init_storage();
|
|
4082
|
+
|
|
4069
4083
|
// src/usage/profile.ts
|
|
4070
4084
|
import { existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
4071
4085
|
import { dirname as dirname3 } from "node:path";
|
|
@@ -4141,36 +4155,184 @@ async function getPlanTier(cachePath, nowMs = Date.now(), fetcher = fetchProfile
|
|
|
4141
4155
|
return fresh.planTier;
|
|
4142
4156
|
}
|
|
4143
4157
|
|
|
4144
|
-
// src/team/
|
|
4158
|
+
// src/team/backfill.ts
|
|
4145
4159
|
init_fs();
|
|
4146
4160
|
var USAGE_LOG = cclensPath("usage.jsonl");
|
|
4147
4161
|
var PROFILE_CACHE = cclensPath("profile.json");
|
|
4162
|
+
var BATCH_SIZE = 500;
|
|
4163
|
+
var POST_TIMEOUT_MS2 = 15e3;
|
|
4148
4164
|
var noopLog = () => {
|
|
4149
4165
|
};
|
|
4150
|
-
|
|
4166
|
+
function rawToWire(raw) {
|
|
4167
|
+
const win = (w) => w ? { utilization: w.utilization, resetsAt: w.resets_at } : null;
|
|
4168
|
+
return {
|
|
4169
|
+
capturedAt: raw.captured_at,
|
|
4170
|
+
fiveHour: { utilization: raw.five_hour.utilization, resetsAt: raw.five_hour.resets_at },
|
|
4171
|
+
sevenDay: { utilization: raw.seven_day.utilization, resetsAt: raw.seven_day.resets_at },
|
|
4172
|
+
sevenDayOpus: win(raw.seven_day_opus),
|
|
4173
|
+
sevenDaySonnet: win(raw.seven_day_sonnet),
|
|
4174
|
+
sevenDayOauthApps: win(raw.seven_day_oauth_apps),
|
|
4175
|
+
sevenDayCowork: win(raw.seven_day_cowork),
|
|
4176
|
+
extraUsage: raw.extra_usage ? {
|
|
4177
|
+
isEnabled: raw.extra_usage.is_enabled,
|
|
4178
|
+
monthlyLimitUsd: raw.extra_usage.monthly_limit,
|
|
4179
|
+
usedCreditsUsd: raw.extra_usage.used_credits,
|
|
4180
|
+
utilization: raw.extra_usage.utilization
|
|
4181
|
+
} : null
|
|
4182
|
+
};
|
|
4183
|
+
}
|
|
4184
|
+
function chunk(items, size) {
|
|
4185
|
+
if (size <= 0) return [items];
|
|
4186
|
+
const out = [];
|
|
4187
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
4188
|
+
return out;
|
|
4189
|
+
}
|
|
4190
|
+
async function postBatch(config, snapshots, planTier) {
|
|
4191
|
+
const res = await fetch(`${config.serverUrl}/api/ingest/usage-history`, {
|
|
4192
|
+
method: "POST",
|
|
4193
|
+
signal: AbortSignal.timeout(POST_TIMEOUT_MS2),
|
|
4194
|
+
headers: {
|
|
4195
|
+
"Content-Type": "application/json",
|
|
4196
|
+
Authorization: `Bearer ${config.bearerToken}`
|
|
4197
|
+
},
|
|
4198
|
+
body: JSON.stringify({ snapshots, ...planTier ? { planTier } : {} })
|
|
4199
|
+
});
|
|
4200
|
+
if (!res.ok) {
|
|
4201
|
+
return { inserted: 0, skipped: snapshots.length, status: res.status };
|
|
4202
|
+
}
|
|
4203
|
+
const body = await res.json().catch(() => ({}));
|
|
4204
|
+
return {
|
|
4205
|
+
inserted: body.inserted ?? 0,
|
|
4206
|
+
skipped: body.skipped ?? 0,
|
|
4207
|
+
status: res.status
|
|
4208
|
+
};
|
|
4209
|
+
}
|
|
4210
|
+
function isAfterSince(snapshot, sinceCapturedAt) {
|
|
4211
|
+
if (!sinceCapturedAt) return true;
|
|
4212
|
+
const sinceMs = Date.parse(sinceCapturedAt);
|
|
4213
|
+
if (Number.isNaN(sinceMs)) return true;
|
|
4214
|
+
const capturedMs = Date.parse(snapshot.captured_at);
|
|
4215
|
+
return !Number.isNaN(capturedMs) && capturedMs > sinceMs;
|
|
4216
|
+
}
|
|
4217
|
+
function capturedAtMs(snapshot) {
|
|
4218
|
+
const ms = Date.parse(snapshot.captured_at);
|
|
4219
|
+
return Number.isNaN(ms) ? Number.MAX_SAFE_INTEGER : ms;
|
|
4220
|
+
}
|
|
4221
|
+
function latestCapturedAt(snapshots) {
|
|
4222
|
+
let latest = null;
|
|
4223
|
+
for (const s of snapshots) {
|
|
4224
|
+
const ms = Date.parse(s.capturedAt);
|
|
4225
|
+
if (Number.isNaN(ms)) continue;
|
|
4226
|
+
if (!latest || ms > latest.ms) latest = { at: s.capturedAt, ms };
|
|
4227
|
+
}
|
|
4228
|
+
return latest?.at;
|
|
4229
|
+
}
|
|
4230
|
+
async function runTeamBackfill(log3 = noopLog, filePath = USAGE_LOG, configOverride, options = {}) {
|
|
4231
|
+
const config = configOverride === void 0 ? readTeamConfig() : configOverride;
|
|
4232
|
+
if (!config) {
|
|
4233
|
+
return { paired: false, sentSnapshots: 0, insertedSnapshots: 0, skippedSnapshots: 0, batches: 0 };
|
|
4234
|
+
}
|
|
4235
|
+
const raw = readSnapshots(filePath);
|
|
4236
|
+
const claudeOnly = raw.filter(
|
|
4237
|
+
(s) => (!s.agent || s.agent === "claude-code") && isAfterSince(s, options.sinceCapturedAt)
|
|
4238
|
+
).sort((a, b) => capturedAtMs(a) - capturedAtMs(b));
|
|
4239
|
+
if (claudeOnly.length === 0) {
|
|
4240
|
+
log3("info", "team backfill: no usage snapshots to send");
|
|
4241
|
+
return { paired: true, sentSnapshots: 0, insertedSnapshots: 0, skippedSnapshots: 0, batches: 0 };
|
|
4242
|
+
}
|
|
4243
|
+
const wire = claudeOnly.map(rawToWire);
|
|
4244
|
+
const batches = chunk(wire, BATCH_SIZE);
|
|
4245
|
+
const planTier = await getPlanTier(PROFILE_CACHE).catch(() => null) ?? void 0;
|
|
4246
|
+
let inserted = 0;
|
|
4247
|
+
let skipped = 0;
|
|
4248
|
+
let lastSnapshotAt;
|
|
4249
|
+
for (let i = 0; i < batches.length; i++) {
|
|
4250
|
+
const batch = batches[i];
|
|
4251
|
+
try {
|
|
4252
|
+
const result = await postBatch(config, batch, planTier);
|
|
4253
|
+
if (result.status >= 400) {
|
|
4254
|
+
log3("warn", `team backfill: batch ${i + 1}/${batches.length} failed (${result.status})`);
|
|
4255
|
+
return {
|
|
4256
|
+
paired: true,
|
|
4257
|
+
sentSnapshots: wire.length,
|
|
4258
|
+
insertedSnapshots: inserted,
|
|
4259
|
+
skippedSnapshots: skipped,
|
|
4260
|
+
batches: i,
|
|
4261
|
+
lastSnapshotAt,
|
|
4262
|
+
error: `HTTP ${result.status}`
|
|
4263
|
+
};
|
|
4264
|
+
}
|
|
4265
|
+
inserted += result.inserted;
|
|
4266
|
+
skipped += result.skipped;
|
|
4267
|
+
lastSnapshotAt = latestCapturedAt(batch) ?? lastSnapshotAt;
|
|
4268
|
+
} catch (err) {
|
|
4269
|
+
log3("warn", `team backfill: batch ${i + 1}/${batches.length} error: ${err.message}`);
|
|
4270
|
+
return {
|
|
4271
|
+
paired: true,
|
|
4272
|
+
sentSnapshots: wire.length,
|
|
4273
|
+
insertedSnapshots: inserted,
|
|
4274
|
+
skippedSnapshots: skipped,
|
|
4275
|
+
batches: i,
|
|
4276
|
+
lastSnapshotAt,
|
|
4277
|
+
error: err.message
|
|
4278
|
+
};
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4281
|
+
log3(
|
|
4282
|
+
"info",
|
|
4283
|
+
`team backfill: ${inserted} new, ${skipped} already-known across ${batches.length} batch${batches.length === 1 ? "" : "es"}`
|
|
4284
|
+
);
|
|
4285
|
+
return {
|
|
4286
|
+
paired: true,
|
|
4287
|
+
sentSnapshots: wire.length,
|
|
4288
|
+
insertedSnapshots: inserted,
|
|
4289
|
+
skippedSnapshots: skipped,
|
|
4290
|
+
batches: batches.length,
|
|
4291
|
+
lastSnapshotAt
|
|
4292
|
+
};
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4295
|
+
// src/team/sync.ts
|
|
4296
|
+
init_fs();
|
|
4297
|
+
var USAGE_LOG2 = cclensPath("usage.jsonl");
|
|
4298
|
+
var PROFILE_CACHE2 = cclensPath("profile.json");
|
|
4299
|
+
var noopLog2 = () => {
|
|
4300
|
+
};
|
|
4301
|
+
async function runTeamSync(log3 = noopLog2, configOverride, options = {}) {
|
|
4151
4302
|
const config = configOverride === void 0 ? readTeamConfig() : configOverride;
|
|
4152
4303
|
if (!config) return { paired: false, pushed: 0, queued: 0, queuedDrained: 0 };
|
|
4153
4304
|
try {
|
|
4154
4305
|
const { listSessions: listSessions2, loadCalibrationCurve: loadCalibrationCurve2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
4155
4306
|
const { toLocalDay: toLocalDay3 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
4156
4307
|
const today = toLocalDay3(Date.now());
|
|
4308
|
+
let nextConfig = { ...config };
|
|
4309
|
+
const persistConfig = (patch) => {
|
|
4310
|
+
nextConfig = { ...nextConfig, ...patch };
|
|
4311
|
+
writeTeamConfig(nextConfig);
|
|
4312
|
+
};
|
|
4313
|
+
const usageBackfill = await runTeamBackfill(log3, USAGE_LOG2, config, {
|
|
4314
|
+
sinceCapturedAt: options.forceUsageBackfill ? void 0 : config.lastSyncedUsageSnapshotAt
|
|
4315
|
+
});
|
|
4316
|
+
if (usageBackfill.lastSnapshotAt) {
|
|
4317
|
+
persistConfig({ lastSyncedUsageSnapshotAt: usageBackfill.lastSnapshotAt });
|
|
4318
|
+
}
|
|
4157
4319
|
const sessions = await listSessions2({ limit: 1e4 });
|
|
4158
4320
|
const rollups = buildRollupsForRange(sessions, config.lastSyncedDay);
|
|
4159
|
-
const usageSnapshot = readLatestUsageSnapshotForWire(
|
|
4160
|
-
const planTier = await getPlanTier(
|
|
4321
|
+
const usageSnapshot = readLatestUsageSnapshotForWire(USAGE_LOG2) ?? void 0;
|
|
4322
|
+
const planTier = await getPlanTier(PROFILE_CACHE2).catch(() => null) ?? void 0;
|
|
4161
4323
|
const cyclePeaks = await buildCyclePeaksForPush(planTier, loadCalibrationCurve2);
|
|
4162
4324
|
if (rollups.length === 0) {
|
|
4163
4325
|
const hasLiveData = Boolean(usageSnapshot || planTier || cyclePeaks);
|
|
4164
4326
|
if (!hasLiveData) {
|
|
4165
|
-
log3("info", "team push: nothing to sync");
|
|
4166
|
-
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0 };
|
|
4327
|
+
if (usageBackfill.sentSnapshots === 0) log3("info", "team push: nothing to sync");
|
|
4328
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
|
|
4167
4329
|
}
|
|
4168
4330
|
const payload = buildIngestPayload(void 0, usageSnapshot, planTier, cyclePeaks);
|
|
4169
4331
|
const result = await pushToTeamServer(config, payload);
|
|
4170
4332
|
if (!result.ok) {
|
|
4171
4333
|
log3("warn", `team push (live-only) failed (${result.status}); queueing`);
|
|
4172
4334
|
enqueuePayload(payload);
|
|
4173
|
-
return { paired: true, pushed: 0, queued: 1, queuedDrained: 0 };
|
|
4335
|
+
return { paired: true, pushed: 0, queued: 1, queuedDrained: 0, usageBackfill };
|
|
4174
4336
|
}
|
|
4175
4337
|
let queuedDrained2 = 0;
|
|
4176
4338
|
const backlog = dequeuePayloads();
|
|
@@ -4183,11 +4345,12 @@ async function runTeamSync(log3 = noopLog, configOverride) {
|
|
|
4183
4345
|
queuedDrained2++;
|
|
4184
4346
|
}
|
|
4185
4347
|
log3("info", `team push ok: live-only (no new daily activity)` + (queuedDrained2 ? `, ${queuedDrained2} queued retried` : ""));
|
|
4186
|
-
return { paired: true, pushed: 1, queued: 0, queuedDrained: queuedDrained2 };
|
|
4348
|
+
return { paired: true, pushed: 1, queued: 0, queuedDrained: queuedDrained2, usageBackfill };
|
|
4187
4349
|
}
|
|
4188
4350
|
let pushed = 0;
|
|
4189
4351
|
let queued = 0;
|
|
4190
4352
|
let failedDay;
|
|
4353
|
+
let lastPushedDay;
|
|
4191
4354
|
for (let i = 0; i < rollups.length; i++) {
|
|
4192
4355
|
const rollup = rollups[i];
|
|
4193
4356
|
const isLatest = i === rollups.length - 1;
|
|
@@ -4208,9 +4371,13 @@ async function runTeamSync(log3 = noopLog, configOverride) {
|
|
|
4208
4371
|
break;
|
|
4209
4372
|
}
|
|
4210
4373
|
pushed++;
|
|
4374
|
+
lastPushedDay = rollup.day;
|
|
4375
|
+
}
|
|
4376
|
+
if (failedDay) {
|
|
4377
|
+
if (lastPushedDay) persistConfig({ lastSyncedDay: lastPushedDay });
|
|
4378
|
+
} else {
|
|
4379
|
+
persistConfig({ lastSyncedDay: today });
|
|
4211
4380
|
}
|
|
4212
|
-
const nextConfig = { ...config, lastSyncedDay: today };
|
|
4213
|
-
if (pushed > 0 || !failedDay) writeTeamConfig(nextConfig);
|
|
4214
4381
|
let queuedDrained = 0;
|
|
4215
4382
|
if (!failedDay) {
|
|
4216
4383
|
const backlog = dequeuePayloads();
|
|
@@ -4226,7 +4393,7 @@ async function runTeamSync(log3 = noopLog, configOverride) {
|
|
|
4226
4393
|
if (pushed > 0) {
|
|
4227
4394
|
log3("info", `team push ok: ${pushed} day${pushed === 1 ? "" : "s"} pushed` + (queuedDrained ? `, ${queuedDrained} queued retried` : "") + (queued ? `, ${queued} queued for retry` : ""));
|
|
4228
4395
|
}
|
|
4229
|
-
return { paired: true, pushed, queued, queuedDrained, failedDay };
|
|
4396
|
+
return { paired: true, pushed, queued, queuedDrained, usageBackfill, failedDay };
|
|
4230
4397
|
} catch (err) {
|
|
4231
4398
|
const message = err.message;
|
|
4232
4399
|
log3("warn", `team push error: ${message}`);
|
|
@@ -9671,10 +9838,10 @@ exec ${shellEscape(claudeBin)} \\
|
|
|
9671
9838
|
"$USR"
|
|
9672
9839
|
`, { mode: 493 });
|
|
9673
9840
|
const existingBefore = snapshotAllTranscripts();
|
|
9674
|
-
const
|
|
9675
|
-
if (
|
|
9841
|
+
const spawn3 = spawnSync(tmuxBin, ["new-session", "-d", "-s", sessionName, "-x", "140", "-y", "50", "-c", cwd, wrapper], { encoding: "utf8" });
|
|
9842
|
+
if (spawn3.status !== 0) {
|
|
9676
9843
|
cleanup(cwd, runId, tmuxBin, sessionName);
|
|
9677
|
-
throw new TmuxRunError(`tmux new-session failed: ${
|
|
9844
|
+
throw new TmuxRunError(`tmux new-session failed: ${spawn3.stderr.trim()}`);
|
|
9678
9845
|
}
|
|
9679
9846
|
const onAbort = () => {
|
|
9680
9847
|
try {
|
|
@@ -9701,10 +9868,10 @@ exec ${shellEscape(claudeBin)} \\
|
|
|
9701
9868
|
}
|
|
9702
9869
|
}
|
|
9703
9870
|
if (args.onDelta && assembled.length > lastEmittedLength) {
|
|
9704
|
-
const
|
|
9871
|
+
const chunk2 = assembled.slice(lastEmittedLength);
|
|
9705
9872
|
lastEmittedLength = assembled.length;
|
|
9706
9873
|
try {
|
|
9707
|
-
args.onDelta(
|
|
9874
|
+
args.onDelta(chunk2);
|
|
9708
9875
|
} catch {
|
|
9709
9876
|
}
|
|
9710
9877
|
}
|
|
@@ -10068,8 +10235,8 @@ ${args.reminder}` : args.userPrompt;
|
|
|
10068
10235
|
let buffer = "", inputTokens = 0, outputTokens = 0, modelUsed = args.model, stderr = "";
|
|
10069
10236
|
const startMs = Date.now();
|
|
10070
10237
|
let lastReportedKb = -1;
|
|
10071
|
-
proc.stdout.on("data", (
|
|
10072
|
-
for (const line of
|
|
10238
|
+
proc.stdout.on("data", (chunk2) => {
|
|
10239
|
+
for (const line of chunk2.toString("utf8").split("\n")) {
|
|
10073
10240
|
const t = line.trim();
|
|
10074
10241
|
if (!t)
|
|
10075
10242
|
continue;
|
|
@@ -13179,11 +13346,59 @@ async function backfillYesterdayDigest(opts = {}) {
|
|
|
13179
13346
|
return { fired: true, reason: "ok", key: yesterday };
|
|
13180
13347
|
}
|
|
13181
13348
|
|
|
13349
|
+
// src/updater.ts
|
|
13350
|
+
var PACKAGE_NAME = "fleetlens";
|
|
13351
|
+
var CHECK_TIMEOUT_MS = 3e3;
|
|
13352
|
+
function shouldUpdate(local, remote) {
|
|
13353
|
+
const l = local.split(".").map(Number);
|
|
13354
|
+
const r = remote.split(".").map(Number);
|
|
13355
|
+
for (let i = 0; i < 3; i++) {
|
|
13356
|
+
if ((r[i] ?? 0) > (l[i] ?? 0)) return true;
|
|
13357
|
+
if ((r[i] ?? 0) < (l[i] ?? 0)) return false;
|
|
13358
|
+
}
|
|
13359
|
+
return false;
|
|
13360
|
+
}
|
|
13361
|
+
async function fetchLatestVersion() {
|
|
13362
|
+
try {
|
|
13363
|
+
const controller = new AbortController();
|
|
13364
|
+
const timer = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
13365
|
+
const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
|
|
13366
|
+
signal: controller.signal
|
|
13367
|
+
});
|
|
13368
|
+
clearTimeout(timer);
|
|
13369
|
+
if (!res.ok) return null;
|
|
13370
|
+
const data = await res.json();
|
|
13371
|
+
return data.version ?? null;
|
|
13372
|
+
} catch {
|
|
13373
|
+
return null;
|
|
13374
|
+
}
|
|
13375
|
+
}
|
|
13376
|
+
function isDevMode() {
|
|
13377
|
+
const script = process.argv[1] ?? "";
|
|
13378
|
+
return script.includes("packages/cli/") || script.includes("packages\\cli\\");
|
|
13379
|
+
}
|
|
13380
|
+
async function getUpdateAvailability() {
|
|
13381
|
+
if (isDevMode()) return null;
|
|
13382
|
+
const latest = await fetchLatestVersion();
|
|
13383
|
+
if (latest === null) return {
|
|
13384
|
+
current: "0.10.3",
|
|
13385
|
+
latest: null,
|
|
13386
|
+
updateAvailable: false
|
|
13387
|
+
};
|
|
13388
|
+
return {
|
|
13389
|
+
current: "0.10.3",
|
|
13390
|
+
latest,
|
|
13391
|
+
updateAvailable: shouldUpdate("0.10.3", latest)
|
|
13392
|
+
};
|
|
13393
|
+
}
|
|
13394
|
+
|
|
13182
13395
|
// src/daemon-worker.ts
|
|
13183
|
-
var
|
|
13396
|
+
var USAGE_LOG3 = cclensPath("usage.jsonl");
|
|
13184
13397
|
var DAEMON_LOG = cclensPath("daemon.log");
|
|
13398
|
+
var UPDATE_STATE = cclensPath("daemon-update.json");
|
|
13185
13399
|
var WATCHDOG_INTERVAL_MS = 5 * 1e3;
|
|
13186
|
-
|
|
13400
|
+
var AUTO_UPDATE_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
13401
|
+
mkdirSync12(dirname8(USAGE_LOG3), { recursive: true });
|
|
13187
13402
|
function log2(level, message) {
|
|
13188
13403
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${message}
|
|
13189
13404
|
`;
|
|
@@ -13193,8 +13408,10 @@ function log2(level, message) {
|
|
|
13193
13408
|
}
|
|
13194
13409
|
}
|
|
13195
13410
|
var nextPollAtMs = 0;
|
|
13411
|
+
var nextTeamSyncAtMs = 0;
|
|
13196
13412
|
var currentIntervalMs = BASE_INTERVAL_MS;
|
|
13197
13413
|
var waitingForRefresh = false;
|
|
13414
|
+
var updateCheckInFlight = false;
|
|
13198
13415
|
var PERCEPTION_INTERVAL_MS = 5 * 60 * 1e3;
|
|
13199
13416
|
var perceptionInFlight = false;
|
|
13200
13417
|
var backfillAttempted = false;
|
|
@@ -13221,6 +13438,96 @@ var perceptionHandle = setInterval(async () => {
|
|
|
13221
13438
|
perceptionInFlight = false;
|
|
13222
13439
|
}
|
|
13223
13440
|
}, PERCEPTION_INTERVAL_MS);
|
|
13441
|
+
function readUpdateState() {
|
|
13442
|
+
try {
|
|
13443
|
+
return JSON.parse(readFileSync14(UPDATE_STATE, "utf8"));
|
|
13444
|
+
} catch {
|
|
13445
|
+
return null;
|
|
13446
|
+
}
|
|
13447
|
+
}
|
|
13448
|
+
function writeUpdateState(state) {
|
|
13449
|
+
try {
|
|
13450
|
+
writeFileSync11(UPDATE_STATE, JSON.stringify(state, null, 2), { mode: 384 });
|
|
13451
|
+
} catch {
|
|
13452
|
+
}
|
|
13453
|
+
}
|
|
13454
|
+
function nextUpdateCheckFromState(nowMs) {
|
|
13455
|
+
const lastCheckedAt = readUpdateState()?.lastCheckedAt;
|
|
13456
|
+
if (!lastCheckedAt) return 0;
|
|
13457
|
+
const lastMs = Date.parse(lastCheckedAt);
|
|
13458
|
+
if (Number.isNaN(lastMs)) return 0;
|
|
13459
|
+
return nowMs - lastMs >= AUTO_UPDATE_INTERVAL_MS ? 0 : lastMs + AUTO_UPDATE_INTERVAL_MS;
|
|
13460
|
+
}
|
|
13461
|
+
var nextUpdateCheckAtMs = nextUpdateCheckFromState(Date.now());
|
|
13462
|
+
function cliEntryPath() {
|
|
13463
|
+
return join8(dirname8(fileURLToPath(import.meta.url)), "index.js");
|
|
13464
|
+
}
|
|
13465
|
+
async function runDaemonUpdateCheck() {
|
|
13466
|
+
if (process.env.FLEETLENS_DAEMON_AUTO_UPDATE === "0") {
|
|
13467
|
+
nextUpdateCheckAtMs = Date.now() + AUTO_UPDATE_INTERVAL_MS;
|
|
13468
|
+
return;
|
|
13469
|
+
}
|
|
13470
|
+
if (updateCheckInFlight) return;
|
|
13471
|
+
updateCheckInFlight = true;
|
|
13472
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13473
|
+
try {
|
|
13474
|
+
const availability = await getUpdateAvailability();
|
|
13475
|
+
if (!availability) return;
|
|
13476
|
+
const state = {
|
|
13477
|
+
lastCheckedAt: checkedAt,
|
|
13478
|
+
current: availability.current,
|
|
13479
|
+
latest: availability.latest,
|
|
13480
|
+
updateAvailable: availability.updateAvailable
|
|
13481
|
+
};
|
|
13482
|
+
writeUpdateState(state);
|
|
13483
|
+
if (!availability.latest) {
|
|
13484
|
+
log2("warn", "update check skipped: npm registry unreachable");
|
|
13485
|
+
return;
|
|
13486
|
+
}
|
|
13487
|
+
if (!availability.updateAvailable) {
|
|
13488
|
+
log2("info", `update check ok: ${availability.current} is current`);
|
|
13489
|
+
return;
|
|
13490
|
+
}
|
|
13491
|
+
const entry = cliEntryPath();
|
|
13492
|
+
if (!existsSync11(entry)) {
|
|
13493
|
+
log2("warn", `update check found ${availability.latest}, but CLI entry is missing at ${entry}`);
|
|
13494
|
+
return;
|
|
13495
|
+
}
|
|
13496
|
+
let logFd = "ignore";
|
|
13497
|
+
try {
|
|
13498
|
+
logFd = openSync(DAEMON_LOG, "a");
|
|
13499
|
+
} catch {
|
|
13500
|
+
}
|
|
13501
|
+
const child = spawn2(process.execPath, [entry, "update"], {
|
|
13502
|
+
detached: true,
|
|
13503
|
+
stdio: ["ignore", logFd, logFd],
|
|
13504
|
+
env: { ...process.env, FLEETLENS_UPDATE_SOURCE: "daemon" }
|
|
13505
|
+
});
|
|
13506
|
+
if (typeof logFd === "number") {
|
|
13507
|
+
child.on("exit", () => {
|
|
13508
|
+
try {
|
|
13509
|
+
closeSync(logFd);
|
|
13510
|
+
} catch {
|
|
13511
|
+
}
|
|
13512
|
+
});
|
|
13513
|
+
}
|
|
13514
|
+
child.unref();
|
|
13515
|
+
writeUpdateState({
|
|
13516
|
+
...state,
|
|
13517
|
+
updateSpawnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13518
|
+
});
|
|
13519
|
+
log2(
|
|
13520
|
+
"info",
|
|
13521
|
+
`update check found ${availability.latest}; spawned updater pid=${child.pid ?? "unknown"}`
|
|
13522
|
+
);
|
|
13523
|
+
} catch (err) {
|
|
13524
|
+
writeUpdateState({ lastCheckedAt: checkedAt, error: err.message });
|
|
13525
|
+
log2("warn", `update check failed: ${err.message}`);
|
|
13526
|
+
} finally {
|
|
13527
|
+
updateCheckInFlight = false;
|
|
13528
|
+
nextUpdateCheckAtMs = Date.now() + AUTO_UPDATE_INTERVAL_MS;
|
|
13529
|
+
}
|
|
13530
|
+
}
|
|
13224
13531
|
async function tick() {
|
|
13225
13532
|
for (const source of agentSources) {
|
|
13226
13533
|
if (source.kind === "claude-code") continue;
|
|
@@ -13236,7 +13543,7 @@ async function tick() {
|
|
|
13236
13543
|
seven_day_cowork: null,
|
|
13237
13544
|
extra_usage: null
|
|
13238
13545
|
};
|
|
13239
|
-
appendSnapshot(
|
|
13546
|
+
appendSnapshot(USAGE_LOG3, snapshot);
|
|
13240
13547
|
log2(
|
|
13241
13548
|
"info",
|
|
13242
13549
|
`${source.kind} snapshot 5h=${snapshot.five_hour.utilization}% 7d=${snapshot.seven_day.utilization}%`
|
|
@@ -13247,7 +13554,7 @@ async function tick() {
|
|
|
13247
13554
|
}
|
|
13248
13555
|
try {
|
|
13249
13556
|
const snapshot = await fetchUsage();
|
|
13250
|
-
appendSnapshot(
|
|
13557
|
+
appendSnapshot(USAGE_LOG3, snapshot);
|
|
13251
13558
|
log2(
|
|
13252
13559
|
"info",
|
|
13253
13560
|
`claude-code snapshot 5h=${snapshot.five_hour.utilization}% 7d=${snapshot.seven_day.utilization}%`
|
|
@@ -13304,7 +13611,14 @@ async function runLoop() {
|
|
|
13304
13611
|
const outcome = await tick();
|
|
13305
13612
|
scheduleAfter(Date.now(), outcome);
|
|
13306
13613
|
}
|
|
13614
|
+
}
|
|
13615
|
+
const teamNow = Date.now();
|
|
13616
|
+
if (teamNow >= nextTeamSyncAtMs) {
|
|
13307
13617
|
await runTeamSync(log2);
|
|
13618
|
+
nextTeamSyncAtMs = Date.now() + BASE_INTERVAL_MS;
|
|
13619
|
+
}
|
|
13620
|
+
if (Date.now() >= nextUpdateCheckAtMs) {
|
|
13621
|
+
await runDaemonUpdateCheck();
|
|
13308
13622
|
}
|
|
13309
13623
|
await sleep2(WATCHDOG_INTERVAL_MS);
|
|
13310
13624
|
}
|