modelstat 0.1.3 → 0.2.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/README.md +10 -10
- package/dist/cli.mjs +116 -93
- package/dist/cli.mjs.map +1 -1
- package/package.json +6 -6
- package/scripts/postinstall.mjs +7 -7
- package/vendor/ModelstatTray.app/Contents/MacOS/modelstat-tray +0 -0
- package/vendor/tray-mac/Package.swift +1 -1
- package/vendor/tray-mac/Sources/ModelstatTray/main.swift +8 -8
package/dist/cli.mjs
CHANGED
|
@@ -149,7 +149,7 @@ var init_billing = __esm({
|
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
// ../../packages/core/src/enums.ts
|
|
152
|
-
var AGENTS, PROVIDERS, IDENTITY_OWNER_SCOPES, INSTALL_METHODS, OS_FAMILIES, EVENT_KINDS, TOOL_CALL_STATUSES,
|
|
152
|
+
var AGENTS, PROVIDERS, IDENTITY_OWNER_SCOPES, INSTALL_METHODS, OS_FAMILIES, EVENT_KINDS, TOOL_CALL_STATUSES, DAEMON_PHASES, CLASSIFICATION_CONFIDENCE;
|
|
153
153
|
var init_enums = __esm({
|
|
154
154
|
"../../packages/core/src/enums.ts"() {
|
|
155
155
|
"use strict";
|
|
@@ -181,7 +181,7 @@ var init_enums = __esm({
|
|
|
181
181
|
"raw_sdk_anthropic",
|
|
182
182
|
"raw_sdk_openai",
|
|
183
183
|
"raw_sdk_google",
|
|
184
|
-
// Web chat UIs (Chrome-extension
|
|
184
|
+
// Web chat UIs (Chrome-extension daemon). Categorically distinct
|
|
185
185
|
// from *_cli / *_desktop agents — same provider, different surface.
|
|
186
186
|
"chatgpt_web",
|
|
187
187
|
"claude_web",
|
|
@@ -241,7 +241,7 @@ var init_enums = __esm({
|
|
|
241
241
|
"timeout",
|
|
242
242
|
"unknown"
|
|
243
243
|
];
|
|
244
|
-
|
|
244
|
+
DAEMON_PHASES = [
|
|
245
245
|
"starting",
|
|
246
246
|
"discovering",
|
|
247
247
|
"idle",
|
|
@@ -4540,7 +4540,7 @@ var init_redact_floor = __esm({
|
|
|
4540
4540
|
pattern: /(?:sk|pk|rk)_test_[A-Za-z0-9]{24,}/g,
|
|
4541
4541
|
replacement: "<REDACTED:stripe_test_key>"
|
|
4542
4542
|
},
|
|
4543
|
-
// Discord bot token (was
|
|
4543
|
+
// Discord bot token (was daemon-sdk-only — the canonical drift example).
|
|
4544
4544
|
{
|
|
4545
4545
|
name: "discord_token",
|
|
4546
4546
|
pattern: /[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27}/g,
|
|
@@ -5045,7 +5045,7 @@ var init_schemas = __esm({
|
|
|
5045
5045
|
// available, the on-device Privacy Filter adapter.
|
|
5046
5046
|
// 3. Stripping code blocks and file-path noise.
|
|
5047
5047
|
// Optional — events without it fall back to metadata-only abstracts
|
|
5048
|
-
// (the historical behaviour). The
|
|
5048
|
+
// (the historical behaviour). The daemon-core pipeline runs
|
|
5049
5049
|
// redact() over it again as defence-in-depth before building the
|
|
5050
5050
|
// summarize prompt; it never gets stored long-term server-side, only
|
|
5051
5051
|
// used to construct the summarize input.
|
|
@@ -5076,7 +5076,7 @@ var init_schemas = __esm({
|
|
|
5076
5076
|
root_key: external_exports.string().max(60),
|
|
5077
5077
|
name: external_exports.string().max(120),
|
|
5078
5078
|
confidence: external_exports.number().min(0).max(1).default(0.7),
|
|
5079
|
-
/** Optional free-text reason the
|
|
5079
|
+
/** Optional free-text reason the daemon attached this tag — surfaces
|
|
5080
5080
|
* in the audit log so the user can see "why was this tagged X?" */
|
|
5081
5081
|
reason: external_exports.string().max(200).optional()
|
|
5082
5082
|
});
|
|
@@ -5099,7 +5099,7 @@ var init_schemas = __esm({
|
|
|
5099
5099
|
/** `source_event_id`s covered by this segment. Used for dedupe + replay. */
|
|
5100
5100
|
source_event_ids: external_exports.array(external_exports.string()).max(2e3),
|
|
5101
5101
|
/** Optional embedding of the abstract (BGE-small-en-v1.5, 384 dims).
|
|
5102
|
-
* Present when the
|
|
5102
|
+
* Present when the daemon has an Embedder adapter configured. */
|
|
5103
5103
|
abstract_embedding: external_exports.array(external_exports.number()).length(384).optional()
|
|
5104
5104
|
});
|
|
5105
5105
|
ToolAction = external_exports.object({
|
|
@@ -5115,7 +5115,7 @@ var init_schemas = __esm({
|
|
|
5115
5115
|
qualifiers: external_exports.array(external_exports.string().max(40)).max(8).default([]),
|
|
5116
5116
|
/** Value-masked argument skeleton (every value → `§`). Carried in full up
|
|
5117
5117
|
* to a malicious-size guard (mirrors backend `MAX_TOOL_ACTION_PARAM_SHAPE_CHARS`);
|
|
5118
|
-
* the
|
|
5118
|
+
* the daemon clamps rather than truncating semantically. (tier 1) */
|
|
5119
5119
|
param_shape: external_exports.string().max(16384).nullable().default(null),
|
|
5120
5120
|
/** Relevant non-sensitive keywords (e.g. ["rollout","restart","prod"]),
|
|
5121
5121
|
* OpenAI-redacted on-device. (tier 0) */
|
|
@@ -5147,7 +5147,7 @@ var init_schemas = __esm({
|
|
|
5147
5147
|
session_id: external_exports.string().max(120),
|
|
5148
5148
|
/** The RawEvent that contained the tool_use (dedupe/replay anchor). */
|
|
5149
5149
|
source_event_id: external_exports.string(),
|
|
5150
|
-
/** Segment containing source_event_id — filled by the
|
|
5150
|
+
/** Segment containing source_event_id — filled by the daemon at
|
|
5151
5151
|
* batch-build time when known, else null. */
|
|
5152
5152
|
segment_id: external_exports.string().max(64).nullable().default(null),
|
|
5153
5153
|
/** The agent that made the call (AGENTS enum). */
|
|
@@ -5185,7 +5185,7 @@ var init_schemas = __esm({
|
|
|
5185
5185
|
batch_id: external_exports.string(),
|
|
5186
5186
|
// ULID
|
|
5187
5187
|
device_id: external_exports.string(),
|
|
5188
|
-
|
|
5188
|
+
daemon_version: external_exports.string().max(40),
|
|
5189
5189
|
events: external_exports.array(RawEvent).max(1e4),
|
|
5190
5190
|
segments: external_exports.array(Segment).max(2e3).default([]),
|
|
5191
5191
|
/** Per-call tool invocations (additive — old agents omit it, old
|
|
@@ -5201,8 +5201,8 @@ var init_schemas = __esm({
|
|
|
5201
5201
|
})
|
|
5202
5202
|
).optional(),
|
|
5203
5203
|
/** Optional per-session titles — session_id → short redacted title
|
|
5204
|
-
* (≤120 chars) produced by the
|
|
5205
|
-
* session's segment abstracts.
|
|
5204
|
+
* (≤120 chars) produced by the daemon's local titler from the
|
|
5205
|
+
* session's segment abstracts. Daemons recompute it from the full
|
|
5206
5206
|
* session view on every upload, so the latest batch always carries the
|
|
5207
5207
|
* freshest title. Absent for runtimes without a titler (older agents,
|
|
5208
5208
|
* no-op browser summariser). */
|
|
@@ -5211,20 +5211,20 @@ var init_schemas = __esm({
|
|
|
5211
5211
|
* {@link SessionMetadata}: the repos, pull requests, commits, and issues the
|
|
5212
5212
|
* session touched, detected on-device across git context, tool calls,
|
|
5213
5213
|
* redacted content, and the local model (so it works for any provider).
|
|
5214
|
-
* Additive — old
|
|
5214
|
+
* Additive — old daemons omit it, old servers ignore it (the wire has no
|
|
5215
5215
|
* `deny_unknown_fields`). The join layer between AI spend and shipped work. */
|
|
5216
5216
|
session_metadata: external_exports.record(external_exports.string(), SessionMetadata).optional()
|
|
5217
5217
|
});
|
|
5218
5218
|
HeartbeatPayload = external_exports.object({
|
|
5219
5219
|
device_id: external_exports.string(),
|
|
5220
|
-
status: external_exports.enum(
|
|
5220
|
+
status: external_exports.enum(DAEMON_PHASES),
|
|
5221
5221
|
message: external_exports.string().max(240).nullable(),
|
|
5222
5222
|
progress_done: external_exports.number().int().nonnegative().default(0),
|
|
5223
5223
|
progress_total: external_exports.number().int().nonnegative().default(0),
|
|
5224
5224
|
queue_size: external_exports.number().int().nonnegative().default(0),
|
|
5225
5225
|
stats: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
|
|
5226
5226
|
last_event_at: external_exports.string().datetime({ offset: true }).nullable(),
|
|
5227
|
-
|
|
5227
|
+
daemon_version: external_exports.string().max(40)
|
|
5228
5228
|
});
|
|
5229
5229
|
DeviceEnrollment = external_exports.object({
|
|
5230
5230
|
machine_id: external_exports.string(),
|
|
@@ -5233,10 +5233,10 @@ var init_schemas = __esm({
|
|
|
5233
5233
|
os_family: external_exports.enum(OS_FAMILIES),
|
|
5234
5234
|
os_version: external_exports.string().max(60),
|
|
5235
5235
|
arch: external_exports.enum(["x86_64", "arm64", "other"]),
|
|
5236
|
-
|
|
5236
|
+
daemon_version: external_exports.string().max(40)
|
|
5237
5237
|
});
|
|
5238
5238
|
DeviceSelfRegister = external_exports.object({
|
|
5239
|
-
/**
|
|
5239
|
+
/** Daemon-generated UUIDv7 — must pass shape + recent-timestamp checks. */
|
|
5240
5240
|
device_uuid: external_exports.string(),
|
|
5241
5241
|
/** Base64-encoded ed25519 public key, exactly 32 raw bytes. Optional
|
|
5242
5242
|
* but recommended (used for sender-constrained tokens / DPoP later). */
|
|
@@ -5249,8 +5249,8 @@ var init_schemas = __esm({
|
|
|
5249
5249
|
os_family: external_exports.enum(OS_FAMILIES).optional(),
|
|
5250
5250
|
os_version: external_exports.string().max(60).optional(),
|
|
5251
5251
|
arch: external_exports.enum(["x86_64", "arm64", "other"]).optional(),
|
|
5252
|
-
|
|
5253
|
-
|
|
5252
|
+
daemon: external_exports.string().max(80).optional(),
|
|
5253
|
+
daemon_version: external_exports.string().max(40).optional()
|
|
5254
5254
|
// Allow extra fields for forward-compat without breaking old agents.
|
|
5255
5255
|
}).catchall(external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])).default({})
|
|
5256
5256
|
});
|
|
@@ -33608,14 +33608,14 @@ ${captureLines}` : capture.stack;
|
|
|
33608
33608
|
}
|
|
33609
33609
|
});
|
|
33610
33610
|
|
|
33611
|
-
// ../../packages/
|
|
33611
|
+
// ../../packages/daemon-core/src/config/index.ts
|
|
33612
33612
|
function expBackoff(attempt) {
|
|
33613
33613
|
const i = Math.min(Math.max(attempt, 0), BACKOFF_MS.length - 1);
|
|
33614
33614
|
return BACKOFF_MS[i];
|
|
33615
33615
|
}
|
|
33616
33616
|
var INGEST_BATCH_MAX_EVENTS, BACKOFF_MS, BACKSTOP_SCAN_MS;
|
|
33617
33617
|
var init_config = __esm({
|
|
33618
|
-
"../../packages/
|
|
33618
|
+
"../../packages/daemon-core/src/config/index.ts"() {
|
|
33619
33619
|
"use strict";
|
|
33620
33620
|
INGEST_BATCH_MAX_EVENTS = 1e3;
|
|
33621
33621
|
BACKOFF_MS = [1e3, 2500, 5e3, 1e4, 2e4, 6e4];
|
|
@@ -33623,7 +33623,7 @@ var init_config = __esm({
|
|
|
33623
33623
|
}
|
|
33624
33624
|
});
|
|
33625
33625
|
|
|
33626
|
-
// ../../packages/
|
|
33626
|
+
// ../../packages/daemon-core/src/logger.ts
|
|
33627
33627
|
function defaultLevel() {
|
|
33628
33628
|
const env2 = globalThis.process?.env ?? {};
|
|
33629
33629
|
const raw = (env2.LOG_LEVEL ?? "").toLowerCase();
|
|
@@ -33676,7 +33676,7 @@ function describeErrorWithCause(err, depth = 4) {
|
|
|
33676
33676
|
}
|
|
33677
33677
|
var LEVEL_RANK;
|
|
33678
33678
|
var init_logger = __esm({
|
|
33679
|
-
"../../packages/
|
|
33679
|
+
"../../packages/daemon-core/src/logger.ts"() {
|
|
33680
33680
|
"use strict";
|
|
33681
33681
|
LEVEL_RANK = {
|
|
33682
33682
|
debug: 10,
|
|
@@ -33687,7 +33687,7 @@ var init_logger = __esm({
|
|
|
33687
33687
|
}
|
|
33688
33688
|
});
|
|
33689
33689
|
|
|
33690
|
-
// ../../packages/
|
|
33690
|
+
// ../../packages/daemon-core/src/http/index.ts
|
|
33691
33691
|
function classifyStatus(status2, attempt) {
|
|
33692
33692
|
if (status2 >= 200 && status2 < 300) return { type: "commit" };
|
|
33693
33693
|
if (status2 === 400 || status2 === 422) return { type: "drop", reason: `http_${status2}` };
|
|
@@ -33707,7 +33707,7 @@ function sleep(ms) {
|
|
|
33707
33707
|
}
|
|
33708
33708
|
var IngestClient;
|
|
33709
33709
|
var init_http = __esm({
|
|
33710
|
-
"../../packages/
|
|
33710
|
+
"../../packages/daemon-core/src/http/index.ts"() {
|
|
33711
33711
|
"use strict";
|
|
33712
33712
|
init_config();
|
|
33713
33713
|
init_logger();
|
|
@@ -45249,7 +45249,7 @@ var init_config2 = __esm({
|
|
|
45249
45249
|
DEFAULT_API_URL = "https://modelstat.ai";
|
|
45250
45250
|
LEGACY_LOCALHOST_API = "http://localhost:3010";
|
|
45251
45251
|
store = new Conf({
|
|
45252
|
-
projectName: "modelstat-
|
|
45252
|
+
projectName: "modelstat-daemon",
|
|
45253
45253
|
defaults: {
|
|
45254
45254
|
// Intentionally empty — the apiUrl getter below computes this
|
|
45255
45255
|
// from env + stored value + DEFAULT_API_URL. Keeping the stored
|
|
@@ -45281,7 +45281,7 @@ var init_config2 = __esm({
|
|
|
45281
45281
|
* or paired pre-0.0.8) → production default. The legacy localhost
|
|
45282
45282
|
* value is ignored so upgrades from 0.0.7 self-heal. */
|
|
45283
45283
|
get apiUrl() {
|
|
45284
|
-
if (process.env.
|
|
45284
|
+
if (process.env.DAEMON_API_URL) return process.env.DAEMON_API_URL;
|
|
45285
45285
|
const stored = store.get("apiUrl");
|
|
45286
45286
|
if (stored && stored !== LEGACY_LOCALHOST_API) return stored;
|
|
45287
45287
|
return DEFAULT_API_URL;
|
|
@@ -45289,6 +45289,16 @@ var init_config2 = __esm({
|
|
|
45289
45289
|
setApiUrl(v) {
|
|
45290
45290
|
store.set("apiUrl", v);
|
|
45291
45291
|
},
|
|
45292
|
+
/** True when `apiUrl` resolves to the baked-in production default with no
|
|
45293
|
+
* explicit override (no `DAEMON_API_URL`, no operator-set stored URL). Used
|
|
45294
|
+
* to refuse silent prod self-register from CI/non-interactive environments
|
|
45295
|
+
* so ephemeral runners don't pile up unclaimed device rows. */
|
|
45296
|
+
get isProdDefaultApi() {
|
|
45297
|
+
if (process.env.DAEMON_API_URL) return false;
|
|
45298
|
+
const stored = store.get("apiUrl");
|
|
45299
|
+
if (stored && stored !== LEGACY_LOCALHOST_API) return false;
|
|
45300
|
+
return true;
|
|
45301
|
+
},
|
|
45292
45302
|
// ── Identity: backed by ~/.modelstat/identity.json ─────────────
|
|
45293
45303
|
/** Seed a fresh identity after a successful self-register. Writes
|
|
45294
45304
|
* the file atomically; use `state.backupAndReset()` first if
|
|
@@ -45427,7 +45437,7 @@ async function rotateDeviceSecret(currentSecret) {
|
|
|
45427
45437
|
}
|
|
45428
45438
|
async function reportDiscovery(report) {
|
|
45429
45439
|
const bearer = state.bearer;
|
|
45430
|
-
if (!bearer) throw new Error("
|
|
45440
|
+
if (!bearer) throw new Error("daemon not enrolled \u2014 run `register` first");
|
|
45431
45441
|
const res = await (0, import_undici.request)(`${state.apiUrl}/v1/devices/discovery`, {
|
|
45432
45442
|
method: "POST",
|
|
45433
45443
|
headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
|
|
@@ -45472,7 +45482,7 @@ async function fetchDeviceViewLedgerByClaim(claimCode) {
|
|
|
45472
45482
|
}
|
|
45473
45483
|
function ingestClient() {
|
|
45474
45484
|
if (_ingest) return _ingest;
|
|
45475
|
-
const logger = createLogger("
|
|
45485
|
+
const logger = createLogger("daemon.ingest");
|
|
45476
45486
|
_ingest = new IngestClient({
|
|
45477
45487
|
apiUrl: state.apiUrl,
|
|
45478
45488
|
auth: {
|
|
@@ -45632,24 +45642,24 @@ var init_machine_key = __esm({
|
|
|
45632
45642
|
}
|
|
45633
45643
|
});
|
|
45634
45644
|
|
|
45635
|
-
// ../../packages/
|
|
45645
|
+
// ../../packages/daemon-core/src/contracts/index.ts
|
|
45636
45646
|
var init_contracts = __esm({
|
|
45637
|
-
"../../packages/
|
|
45647
|
+
"../../packages/daemon-core/src/contracts/index.ts"() {
|
|
45638
45648
|
"use strict";
|
|
45639
45649
|
init_schemas();
|
|
45640
45650
|
init_enums();
|
|
45641
45651
|
}
|
|
45642
45652
|
});
|
|
45643
45653
|
|
|
45644
|
-
// ../../packages/
|
|
45654
|
+
// ../../packages/daemon-core/src/ids.ts
|
|
45645
45655
|
var init_ids2 = __esm({
|
|
45646
|
-
"../../packages/
|
|
45656
|
+
"../../packages/daemon-core/src/ids.ts"() {
|
|
45647
45657
|
"use strict";
|
|
45648
45658
|
init_ids();
|
|
45649
45659
|
}
|
|
45650
45660
|
});
|
|
45651
45661
|
|
|
45652
|
-
// ../../packages/
|
|
45662
|
+
// ../../packages/daemon-core/src/queue/index.ts
|
|
45653
45663
|
function attachSegmentIdsByMap(calls, segmentByEvent) {
|
|
45654
45664
|
return calls.map((c) => ({
|
|
45655
45665
|
...c,
|
|
@@ -45657,14 +45667,14 @@ function attachSegmentIdsByMap(calls, segmentByEvent) {
|
|
|
45657
45667
|
}));
|
|
45658
45668
|
}
|
|
45659
45669
|
var init_queue = __esm({
|
|
45660
|
-
"../../packages/
|
|
45670
|
+
"../../packages/daemon-core/src/queue/index.ts"() {
|
|
45661
45671
|
"use strict";
|
|
45662
45672
|
init_config();
|
|
45663
45673
|
init_ids2();
|
|
45664
45674
|
}
|
|
45665
45675
|
});
|
|
45666
45676
|
|
|
45667
|
-
// ../../packages/
|
|
45677
|
+
// ../../packages/daemon-core/src/pipeline/cognition.ts
|
|
45668
45678
|
function buildCognitionUserPrompt(abstract) {
|
|
45669
45679
|
return `Summary: "${abstract.replace(/\s+/g, " ").trim().slice(0, 480)}"
|
|
45670
45680
|
|
|
@@ -45736,7 +45746,7 @@ function extractFirstJsonObject(s) {
|
|
|
45736
45746
|
}
|
|
45737
45747
|
var COGNITION_SYSTEM_PROMPT, MAX_COGNITION_TAGS_PER_FIELD, MAX_COGNITION_TAG_CHARS, COGNITION_MAX_TOKENS, COGNITION_TEMPERATURE;
|
|
45738
45748
|
var init_cognition = __esm({
|
|
45739
|
-
"../../packages/
|
|
45749
|
+
"../../packages/daemon-core/src/pipeline/cognition.ts"() {
|
|
45740
45750
|
"use strict";
|
|
45741
45751
|
COGNITION_SYSTEM_PROMPT = 'You read a one-sentence summary of an AI-coding work session and identify the user\'s emotional state and meta-cognitive state. Output JSON only \u2014 first character of reply is `{`. Schema: {"emotions":[],"meta":[]}. emotions: \u2264 3 short lowercase MOOD tags \u2014 how the user FEELS \u2014 such as frustrated, curious, excited, calm, confused, anxious, satisfied, proud, bored, energised, overwhelmed, confident. meta: \u2264 3 short lowercase MENTAL-MODE tags \u2014 HOW the user is THINKING, never what they are doing. Valid examples: focused, scattered, in-flow, deliberate, hurried, stuck, open, exploratory, methodical, distracted. DO NOT emit ACTIVITY verbs (debugging, refactoring, designing, reviewing, deploying, planning, documenting, implementing) under meta \u2014 those describe the WORK, not the MIND. If the only candidate tag would be an activity verb, return [] for meta instead. Each tag \u2264 24 chars, single word or hyphenated, no punctuation. Only emit a tag if the summary gives clear evidence \u2014 return [] for either field when unsure. Do not invent emotions or mental modes the user did not display. No prose, no markdown.';
|
|
45742
45752
|
MAX_COGNITION_TAGS_PER_FIELD = 3;
|
|
@@ -45746,10 +45756,10 @@ var init_cognition = __esm({
|
|
|
45746
45756
|
}
|
|
45747
45757
|
});
|
|
45748
45758
|
|
|
45749
|
-
// ../../packages/
|
|
45759
|
+
// ../../packages/daemon-core/src/pipeline/prompts.ts
|
|
45750
45760
|
var OLLAMA_CHAT_MODEL, OLLAMA_EMBED_MODEL, SUMMARISER_SYSTEM_PROMPT, SUMMARISER_MAX_TOKENS, SUMMARISER_TEMPERATURE, QWEN_CHARS_PER_TOKEN, ABSTRACT_OUTPUT_MAX_CHARS;
|
|
45751
45761
|
var init_prompts = __esm({
|
|
45752
|
-
"../../packages/
|
|
45762
|
+
"../../packages/daemon-core/src/pipeline/prompts.ts"() {
|
|
45753
45763
|
"use strict";
|
|
45754
45764
|
OLLAMA_CHAT_MODEL = "qwen3:4b";
|
|
45755
45765
|
OLLAMA_EMBED_MODEL = "bge-small-en-v1.5";
|
|
@@ -45761,7 +45771,7 @@ var init_prompts = __esm({
|
|
|
45761
45771
|
}
|
|
45762
45772
|
});
|
|
45763
45773
|
|
|
45764
|
-
// ../../packages/
|
|
45774
|
+
// ../../packages/daemon-core/src/pipeline/script-summary.ts
|
|
45765
45775
|
function buildScriptSummaryUserPrompt(input) {
|
|
45766
45776
|
const body = input.content.slice(0, SCRIPT_SUMMARY_INPUT_MAX_CHARS);
|
|
45767
45777
|
return [
|
|
@@ -45776,7 +45786,7 @@ function buildScriptSummaryUserPrompt(input) {
|
|
|
45776
45786
|
}
|
|
45777
45787
|
var SCRIPT_SUMMARY_OUTPUT_MAX_CHARS, SCRIPT_SUMMARY_TEMPERATURE, SCRIPT_SUMMARY_MAX_TOKENS, SCRIPT_SUMMARY_INPUT_MAX_CHARS, SCRIPT_SUMMARY_SYSTEM_PROMPT;
|
|
45778
45788
|
var init_script_summary = __esm({
|
|
45779
|
-
"../../packages/
|
|
45789
|
+
"../../packages/daemon-core/src/pipeline/script-summary.ts"() {
|
|
45780
45790
|
"use strict";
|
|
45781
45791
|
SCRIPT_SUMMARY_OUTPUT_MAX_CHARS = 200;
|
|
45782
45792
|
SCRIPT_SUMMARY_TEMPERATURE = 0.2;
|
|
@@ -45793,7 +45803,7 @@ Rules:
|
|
|
45793
45803
|
}
|
|
45794
45804
|
});
|
|
45795
45805
|
|
|
45796
|
-
// ../../packages/
|
|
45806
|
+
// ../../packages/daemon-core/src/pipeline/title.ts
|
|
45797
45807
|
function buildTitleUserPrompt(input) {
|
|
45798
45808
|
const lines = input.abstracts.map(
|
|
45799
45809
|
(a, i) => ` [part ${i + 1}] ${a.replace(/\s+/g, " ").trim().slice(0, TITLER_ABSTRACT_SLICE_CHARS)}`
|
|
@@ -45864,7 +45874,7 @@ async function buildSessionTitles(segments, entitle) {
|
|
|
45864
45874
|
}
|
|
45865
45875
|
var TITLER_SYSTEM_PROMPT, TITLE_MAX_CHARS, TITLER_MAX_TOKENS, TITLER_TEMPERATURE, TITLER_MAX_ABSTRACTS, TITLER_ABSTRACT_SLICE_CHARS;
|
|
45866
45876
|
var init_title = __esm({
|
|
45867
|
-
"../../packages/
|
|
45877
|
+
"../../packages/daemon-core/src/pipeline/title.ts"() {
|
|
45868
45878
|
"use strict";
|
|
45869
45879
|
init_redact();
|
|
45870
45880
|
TITLER_SYSTEM_PROMPT = "You write a short TITLE for an AI coding session, given one-sentence summaries of its parts in chronological order. The title names what the session was about at a high level, in 3-8 words. If the session clearly spans more than one theme, name the top themes (at most 3) separated by ' \xB7 '. Use concrete domain keywords (features, components, subsystems). No narration, no filler words like 'session' or 'work on', no quotes, no trailing period, no PII, no code literals, no file paths. Reply with only the title.";
|
|
@@ -45876,7 +45886,7 @@ var init_title = __esm({
|
|
|
45876
45886
|
}
|
|
45877
45887
|
});
|
|
45878
45888
|
|
|
45879
|
-
// ../../packages/
|
|
45889
|
+
// ../../packages/daemon-core/src/pipeline/session-metadata.ts
|
|
45880
45890
|
function buildLinkExtractUserPrompt(abstracts) {
|
|
45881
45891
|
const lines = abstracts.map((a, i) => ` [part ${i + 1}] ${a.replace(/\s+/g, " ").trim().slice(0, 240)}`).join("\n");
|
|
45882
45892
|
return `Summaries of the session's parts:
|
|
@@ -45975,7 +45985,7 @@ async function buildSessionMetadata(segments, events, opts = {}) {
|
|
|
45975
45985
|
}
|
|
45976
45986
|
var LINK_EXTRACT_SYSTEM_PROMPT, LINK_EXTRACT_MAX_TOKENS, LINK_EXTRACT_TEMPERATURE, LINK_EXTRACT_MAX_ABSTRACTS;
|
|
45977
45987
|
var init_session_metadata2 = __esm({
|
|
45978
|
-
"../../packages/
|
|
45988
|
+
"../../packages/daemon-core/src/pipeline/session-metadata.ts"() {
|
|
45979
45989
|
"use strict";
|
|
45980
45990
|
init_session_metadata();
|
|
45981
45991
|
init_title();
|
|
@@ -45986,7 +45996,7 @@ var init_session_metadata2 = __esm({
|
|
|
45986
45996
|
}
|
|
45987
45997
|
});
|
|
45988
45998
|
|
|
45989
|
-
// ../../packages/
|
|
45999
|
+
// ../../packages/daemon-core/src/pipeline/index.ts
|
|
45990
46000
|
async function buildSegmentsForSession(events, adapters2, onProgress) {
|
|
45991
46001
|
if (events.length === 0) return [];
|
|
45992
46002
|
const bySession = /* @__PURE__ */ new Map();
|
|
@@ -46288,7 +46298,7 @@ function inferEnvironment(branch) {
|
|
|
46288
46298
|
}
|
|
46289
46299
|
var SEGMENT_TIME_GAP_MS, SEGMENT_TOPIC_THRESHOLD, SEGMENT_MAX_TURNS, SEGMENT_MAX_DURATION_MS, SEGMENT_MAX_CONTENT_CHARS, ABSTRACT_MAX_CHARS;
|
|
46290
46300
|
var init_pipeline = __esm({
|
|
46291
|
-
"../../packages/
|
|
46301
|
+
"../../packages/daemon-core/src/pipeline/index.ts"() {
|
|
46292
46302
|
"use strict";
|
|
46293
46303
|
init_ids();
|
|
46294
46304
|
init_redact();
|
|
@@ -46309,9 +46319,9 @@ var init_pipeline = __esm({
|
|
|
46309
46319
|
}
|
|
46310
46320
|
});
|
|
46311
46321
|
|
|
46312
|
-
// ../../packages/
|
|
46322
|
+
// ../../packages/daemon-core/src/index.ts
|
|
46313
46323
|
var init_src3 = __esm({
|
|
46314
|
-
"../../packages/
|
|
46324
|
+
"../../packages/daemon-core/src/index.ts"() {
|
|
46315
46325
|
"use strict";
|
|
46316
46326
|
init_contracts();
|
|
46317
46327
|
init_ids2();
|
|
@@ -46323,12 +46333,12 @@ var init_src3 = __esm({
|
|
|
46323
46333
|
}
|
|
46324
46334
|
});
|
|
46325
46335
|
|
|
46326
|
-
// ../../packages/
|
|
46336
|
+
// ../../packages/daemon-core/src/node/file-queue-store.ts
|
|
46327
46337
|
import { promises as fs3 } from "fs";
|
|
46328
46338
|
import { dirname as dirname4 } from "path";
|
|
46329
46339
|
var SENT_TTL_MS, FileQueueStore;
|
|
46330
46340
|
var init_file_queue_store = __esm({
|
|
46331
|
-
"../../packages/
|
|
46341
|
+
"../../packages/daemon-core/src/node/file-queue-store.ts"() {
|
|
46332
46342
|
"use strict";
|
|
46333
46343
|
SENT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
46334
46344
|
FileQueueStore = class {
|
|
@@ -46446,7 +46456,7 @@ var init_file_queue_store = __esm({
|
|
|
46446
46456
|
}
|
|
46447
46457
|
});
|
|
46448
46458
|
|
|
46449
|
-
// ../../packages/
|
|
46459
|
+
// ../../packages/daemon-core/src/node/llama.ts
|
|
46450
46460
|
import { existsSync as existsSync7 } from "fs";
|
|
46451
46461
|
import { mkdir } from "fs/promises";
|
|
46452
46462
|
import { homedir as homedir5 } from "os";
|
|
@@ -46742,7 +46752,7 @@ function llamaExtractLinks(cfg = defaultLlamaConfig()) {
|
|
|
46742
46752
|
}
|
|
46743
46753
|
var DEFAULT_LLAMA_MODEL_URL, LLAMA_MAX_TOKENS, loaded, loadPromise, inflight, llamaInstance;
|
|
46744
46754
|
var init_llama = __esm({
|
|
46745
|
-
"../../packages/
|
|
46755
|
+
"../../packages/daemon-core/src/node/llama.ts"() {
|
|
46746
46756
|
"use strict";
|
|
46747
46757
|
init_cognition();
|
|
46748
46758
|
init_prompts();
|
|
@@ -46758,7 +46768,7 @@ var init_llama = __esm({
|
|
|
46758
46768
|
}
|
|
46759
46769
|
});
|
|
46760
46770
|
|
|
46761
|
-
// ../../packages/
|
|
46771
|
+
// ../../packages/daemon-core/src/node/ollama.ts
|
|
46762
46772
|
function defaultOllamaConfig() {
|
|
46763
46773
|
const base = globalThis.process?.env ?? {};
|
|
46764
46774
|
return {
|
|
@@ -46861,14 +46871,14 @@ function ollamaCognize(cfg = defaultOllamaConfig()) {
|
|
|
46861
46871
|
};
|
|
46862
46872
|
}
|
|
46863
46873
|
var init_ollama = __esm({
|
|
46864
|
-
"../../packages/
|
|
46874
|
+
"../../packages/daemon-core/src/node/ollama.ts"() {
|
|
46865
46875
|
"use strict";
|
|
46866
46876
|
init_prompts();
|
|
46867
46877
|
init_cognition();
|
|
46868
46878
|
}
|
|
46869
46879
|
});
|
|
46870
46880
|
|
|
46871
|
-
// ../../packages/
|
|
46881
|
+
// ../../packages/daemon-core/src/optional-module.ts
|
|
46872
46882
|
function isMissingOptionalModuleError(err) {
|
|
46873
46883
|
const code = err?.code;
|
|
46874
46884
|
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
|
|
@@ -46877,13 +46887,13 @@ function isMissingOptionalModuleError(err) {
|
|
|
46877
46887
|
}
|
|
46878
46888
|
var OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS;
|
|
46879
46889
|
var init_optional_module = __esm({
|
|
46880
|
-
"../../packages/
|
|
46890
|
+
"../../packages/daemon-core/src/optional-module.ts"() {
|
|
46881
46891
|
"use strict";
|
|
46882
46892
|
OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS = 3;
|
|
46883
46893
|
}
|
|
46884
46894
|
});
|
|
46885
46895
|
|
|
46886
|
-
// ../../packages/
|
|
46896
|
+
// ../../packages/daemon-core/src/node/transformersjs-embed.ts
|
|
46887
46897
|
async function loadPipeline(model) {
|
|
46888
46898
|
if (cached) return cached;
|
|
46889
46899
|
if (loadFailedPermanently) return null;
|
|
@@ -46937,7 +46947,7 @@ function createTransformersJsEmbedder(model = DEFAULT_MODEL) {
|
|
|
46937
46947
|
}
|
|
46938
46948
|
var cached, loadPromise2, loadFailedPermanently, loadAttempts, warnedUnavailable, warnedInferenceError, DEFAULT_MODEL, importModule;
|
|
46939
46949
|
var init_transformersjs_embed = __esm({
|
|
46940
|
-
"../../packages/
|
|
46950
|
+
"../../packages/daemon-core/src/node/transformersjs-embed.ts"() {
|
|
46941
46951
|
"use strict";
|
|
46942
46952
|
init_optional_module();
|
|
46943
46953
|
cached = null;
|
|
@@ -46954,7 +46964,7 @@ var init_transformersjs_embed = __esm({
|
|
|
46954
46964
|
}
|
|
46955
46965
|
});
|
|
46956
46966
|
|
|
46957
|
-
// ../../packages/
|
|
46967
|
+
// ../../packages/daemon-core/src/node/index.ts
|
|
46958
46968
|
var node_exports = {};
|
|
46959
46969
|
__export(node_exports, {
|
|
46960
46970
|
DEFAULT_LLAMA_MODEL_URL: () => DEFAULT_LLAMA_MODEL_URL,
|
|
@@ -46976,7 +46986,7 @@ __export(node_exports, {
|
|
|
46976
46986
|
ollamaTokenize: () => ollamaTokenize
|
|
46977
46987
|
});
|
|
46978
46988
|
var init_node2 = __esm({
|
|
46979
|
-
"../../packages/
|
|
46989
|
+
"../../packages/daemon-core/src/node/index.ts"() {
|
|
46980
46990
|
"use strict";
|
|
46981
46991
|
init_file_queue_store();
|
|
46982
46992
|
init_llama();
|
|
@@ -46985,7 +46995,7 @@ var init_node2 = __esm({
|
|
|
46985
46995
|
}
|
|
46986
46996
|
});
|
|
46987
46997
|
|
|
46988
|
-
// ../../packages/
|
|
46998
|
+
// ../../packages/daemon-core/src/redact/privacy-filter.ts
|
|
46989
46999
|
async function createPrivacyFilterRedactor(opts = {}) {
|
|
46990
47000
|
const isBrowser = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
46991
47001
|
const device = opts.device ?? (isBrowser ? "webgpu" : "cpu");
|
|
@@ -47091,7 +47101,7 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
47091
47101
|
};
|
|
47092
47102
|
}
|
|
47093
47103
|
var init_privacy_filter = __esm({
|
|
47094
|
-
"../../packages/
|
|
47104
|
+
"../../packages/daemon-core/src/redact/privacy-filter.ts"() {
|
|
47095
47105
|
"use strict";
|
|
47096
47106
|
init_optional_module();
|
|
47097
47107
|
}
|
|
@@ -47217,7 +47227,7 @@ async function getAdapters() {
|
|
|
47217
47227
|
await import("node-llama-cpp");
|
|
47218
47228
|
} catch (err) {
|
|
47219
47229
|
throw new Error(
|
|
47220
|
-
`modelstat
|
|
47230
|
+
`modelstat daemon can't start: the bundled summariser (node-llama-cpp) failed to load. Re-run \`modelstat connect\` (or \`npm i -g modelstat\`) so the native runtime is re-staged beside the bundle. Underlying error: ${err.message}`
|
|
47221
47231
|
);
|
|
47222
47232
|
}
|
|
47223
47233
|
console.log("[modelstat] using bundled local summariser (Qwen3.5-4B, runs on this machine)");
|
|
@@ -47288,7 +47298,7 @@ function withNonNullTokens(e) {
|
|
|
47288
47298
|
}
|
|
47289
47299
|
async function scanAll(cb = {}) {
|
|
47290
47300
|
const deviceId = state.deviceId;
|
|
47291
|
-
if (!deviceId) throw new Error("
|
|
47301
|
+
if (!deviceId) throw new Error("daemon not enrolled \u2014 run `register` first");
|
|
47292
47302
|
const jobs = [];
|
|
47293
47303
|
try {
|
|
47294
47304
|
const base = join6(homedir6(), ".claude/projects");
|
|
@@ -47393,7 +47403,7 @@ async function scanAll(cb = {}) {
|
|
|
47393
47403
|
const batch = {
|
|
47394
47404
|
batch_id: batchId(),
|
|
47395
47405
|
device_id: deviceId,
|
|
47396
|
-
|
|
47406
|
+
daemon_version: DAEMON_VERSION,
|
|
47397
47407
|
events,
|
|
47398
47408
|
segments,
|
|
47399
47409
|
tool_calls: attachSegmentIdsByMap(toolCallBuffer, callSegmentByEvent),
|
|
@@ -47468,7 +47478,7 @@ async function scanAll(cb = {}) {
|
|
|
47468
47478
|
morePending
|
|
47469
47479
|
};
|
|
47470
47480
|
}
|
|
47471
|
-
var
|
|
47481
|
+
var DAEMON_VERSION, BATCH_MAX_EVENTS, BATCH_MAX_TOOL_CALLS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
|
|
47472
47482
|
var init_scan = __esm({
|
|
47473
47483
|
"src/scan.ts"() {
|
|
47474
47484
|
"use strict";
|
|
@@ -47479,7 +47489,7 @@ var init_scan = __esm({
|
|
|
47479
47489
|
init_api();
|
|
47480
47490
|
init_config2();
|
|
47481
47491
|
init_pipeline2();
|
|
47482
|
-
|
|
47492
|
+
DAEMON_VERSION = true ? "daemon-0.2.0" : "daemon-dev";
|
|
47483
47493
|
BATCH_MAX_EVENTS = INGEST_BATCH_MAX_EVENTS;
|
|
47484
47494
|
BATCH_MAX_TOOL_CALLS = 2e4;
|
|
47485
47495
|
BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
|
|
@@ -47526,7 +47536,7 @@ function readDaemonLock(lockFile = LOCK_FILE) {
|
|
|
47526
47536
|
return {
|
|
47527
47537
|
pid: obj.pid,
|
|
47528
47538
|
startedAt: obj.startedAt ?? "unknown",
|
|
47529
|
-
|
|
47539
|
+
daemonVersion: obj.daemonVersion ?? "unknown",
|
|
47530
47540
|
apiUrl: obj.apiUrl ?? "unknown"
|
|
47531
47541
|
};
|
|
47532
47542
|
} catch {
|
|
@@ -47568,7 +47578,7 @@ function acquireDaemonLock(opts) {
|
|
|
47568
47578
|
const meta = {
|
|
47569
47579
|
pid: process.pid,
|
|
47570
47580
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47571
|
-
|
|
47581
|
+
daemonVersion: opts.daemonVersion,
|
|
47572
47582
|
apiUrl: opts.apiUrl
|
|
47573
47583
|
};
|
|
47574
47584
|
writeLockAtomic(meta);
|
|
@@ -47886,7 +47896,7 @@ var init_node3 = __esm({
|
|
|
47886
47896
|
}
|
|
47887
47897
|
});
|
|
47888
47898
|
|
|
47889
|
-
// ../../packages/
|
|
47899
|
+
// ../../packages/daemon-core/src/policies/index.ts
|
|
47890
47900
|
var policies_exports = {};
|
|
47891
47901
|
__export(policies_exports, {
|
|
47892
47902
|
createPolicyRefresher: () => createPolicyRefresher
|
|
@@ -47930,7 +47940,7 @@ function createPolicyRefresher(opts) {
|
|
|
47930
47940
|
}
|
|
47931
47941
|
var DEFAULT_REFRESH_MS, policiesKind;
|
|
47932
47942
|
var init_policies2 = __esm({
|
|
47933
|
-
"../../packages/
|
|
47943
|
+
"../../packages/daemon-core/src/policies/index.ts"() {
|
|
47934
47944
|
"use strict";
|
|
47935
47945
|
init_src();
|
|
47936
47946
|
init_src4();
|
|
@@ -49710,7 +49720,7 @@ function snapshotBody() {
|
|
|
49710
49720
|
queue_size: status.queueSize,
|
|
49711
49721
|
stats: status.stats,
|
|
49712
49722
|
last_event_at: status.lastEventAt,
|
|
49713
|
-
|
|
49723
|
+
daemon_version: DAEMON_VERSION2,
|
|
49714
49724
|
machine_id: machineKey()
|
|
49715
49725
|
};
|
|
49716
49726
|
}
|
|
@@ -49735,7 +49745,7 @@ async function sendHeartbeat() {
|
|
|
49735
49745
|
if (!bearer || !deviceId) return;
|
|
49736
49746
|
const body = { ...snapshotBody(), device_id: deviceId };
|
|
49737
49747
|
try {
|
|
49738
|
-
const res = await (0, import_undici2.request)(`${state.apiUrl}/v1/
|
|
49748
|
+
const res = await (0, import_undici2.request)(`${state.apiUrl}/v1/daemon/heartbeat`, {
|
|
49739
49749
|
method: "POST",
|
|
49740
49750
|
headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
|
|
49741
49751
|
body: JSON.stringify(body)
|
|
@@ -49866,7 +49876,7 @@ async function runDaemon(opts = {}) {
|
|
|
49866
49876
|
throw new Error("not enrolled \u2014 run `npx modelstat@latest` first");
|
|
49867
49877
|
}
|
|
49868
49878
|
const lock = acquireDaemonLock({
|
|
49869
|
-
|
|
49879
|
+
daemonVersion: DAEMON_VERSION2,
|
|
49870
49880
|
apiUrl: state.apiUrl,
|
|
49871
49881
|
force: opts.force === true,
|
|
49872
49882
|
// If a racing daemon out-renamed us for the lock (see lock.ts
|
|
@@ -49884,7 +49894,7 @@ async function runDaemon(opts = {}) {
|
|
|
49884
49894
|
console.log(
|
|
49885
49895
|
`modelstat daemon is already running \u2014 PID ${lock.owner.pid}, started ${formatAge(
|
|
49886
49896
|
lock.ageSec
|
|
49887
|
-
)} ago,
|
|
49897
|
+
)} ago, daemon ${lock.owner.daemonVersion}.`
|
|
49888
49898
|
);
|
|
49889
49899
|
console.log(" \u2192 to stop it: kill " + lock.owner.pid);
|
|
49890
49900
|
console.log(" \u2192 to force-replace it: modelstat start --force");
|
|
@@ -49976,7 +49986,7 @@ async function runDaemon(opts = {}) {
|
|
|
49976
49986
|
await new Promise(() => {
|
|
49977
49987
|
});
|
|
49978
49988
|
}
|
|
49979
|
-
var import_undici2,
|
|
49989
|
+
var import_undici2, DAEMON_VERSION2, HEARTBEAT_INTERVAL_MS, SCAN_INTERVAL_MS, DISCOVERY_INTERVAL_MS, status, LOCAL_FLUSH_THROTTLE_MS, localFlushTimer, localFlushPending, LOG_MAX_BYTES, LOG_TAIL_KEEP_BYTES, lastStatusPath, scanRunner;
|
|
49980
49990
|
var init_daemon = __esm({
|
|
49981
49991
|
"src/daemon.ts"() {
|
|
49982
49992
|
"use strict";
|
|
@@ -49989,7 +49999,7 @@ var init_daemon = __esm({
|
|
|
49989
49999
|
init_machine_key();
|
|
49990
50000
|
init_scan();
|
|
49991
50001
|
init_single_flight();
|
|
49992
|
-
|
|
50002
|
+
DAEMON_VERSION2 = true ? "daemon-0.2.0" : "daemon-dev";
|
|
49993
50003
|
HEARTBEAT_INTERVAL_MS = 1e4;
|
|
49994
50004
|
SCAN_INTERVAL_MS = 5 * 60 * 1e3;
|
|
49995
50005
|
DISCOVERY_INTERVAL_MS = 6e4;
|
|
@@ -50134,7 +50144,7 @@ import { createRequire } from "module";
|
|
|
50134
50144
|
import { homedir as homedir7, platform as platform3, userInfo } from "os";
|
|
50135
50145
|
import { dirname as dirname6, join as join7 } from "path";
|
|
50136
50146
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
50137
|
-
var SERVICE_LABEL = "ai.modelstat.
|
|
50147
|
+
var SERVICE_LABEL = "ai.modelstat.daemon";
|
|
50138
50148
|
var SYSTEMD_UNIT = "modelstat";
|
|
50139
50149
|
function home() {
|
|
50140
50150
|
return homedir7();
|
|
@@ -50341,7 +50351,7 @@ function writeSystemdUnit(cliPath) {
|
|
|
50341
50351
|
const unitPath = systemdUnitPath();
|
|
50342
50352
|
mkdirSync3(dirname6(unitPath), { recursive: true });
|
|
50343
50353
|
const unit = `[Unit]
|
|
50344
|
-
Description=modelstat
|
|
50354
|
+
Description=modelstat daemon
|
|
50345
50355
|
Documentation=https://modelstat.ai
|
|
50346
50356
|
After=network-online.target
|
|
50347
50357
|
Wants=network-online.target
|
|
@@ -50408,7 +50418,7 @@ function installService() {
|
|
|
50408
50418
|
return { path: systemdUnitPath(), logs: logDir() };
|
|
50409
50419
|
}
|
|
50410
50420
|
throw new Error(
|
|
50411
|
-
`Service installation isn't supported on ${p}. Run 'modelstat start' manually to keep the
|
|
50421
|
+
`Service installation isn't supported on ${p}. Run 'modelstat start' manually to keep the daemon running.`
|
|
50412
50422
|
);
|
|
50413
50423
|
}
|
|
50414
50424
|
function uninstallService() {
|
|
@@ -50445,7 +50455,7 @@ async function bundledTrayAppPath(progress) {
|
|
|
50445
50455
|
const candidates = [
|
|
50446
50456
|
// Pre-built .app — CI with codesigning drops one here.
|
|
50447
50457
|
join7(here2, "..", "vendor", "ModelstatTray.app"),
|
|
50448
|
-
// Local dev layout: apps/
|
|
50458
|
+
// Local dev layout: apps/daemon/src/service.ts → ../../tray-mac/build/ModelstatTray.app
|
|
50449
50459
|
join7(here2, "..", "..", "tray-mac", "build", "ModelstatTray.app")
|
|
50450
50460
|
];
|
|
50451
50461
|
for (const c of candidates) {
|
|
@@ -50519,7 +50529,7 @@ function decideSupervision(input) {
|
|
|
50519
50529
|
const fresh = input.statusFreshMs ?? STATUS_FRESH_MS;
|
|
50520
50530
|
const grace = input.bootGraceMs ?? BOOT_GRACE_MS;
|
|
50521
50531
|
if (!input.lock || !input.ownerAlive) return "spawn";
|
|
50522
|
-
if (input.
|
|
50532
|
+
if (input.myDaemonVersion && input.lock.daemonVersion !== "unknown" && input.lock.daemonVersion !== input.myDaemonVersion) {
|
|
50523
50533
|
return "replace";
|
|
50524
50534
|
}
|
|
50525
50535
|
if (input.statusAgeMs !== null && input.statusAgeMs <= fresh) return "adopt";
|
|
@@ -50547,7 +50557,7 @@ function daemonHealth(opts = {}) {
|
|
|
50547
50557
|
ownerAlive,
|
|
50548
50558
|
lockAgeMs,
|
|
50549
50559
|
statusAgeMs,
|
|
50550
|
-
|
|
50560
|
+
myDaemonVersion: opts.myDaemonVersion
|
|
50551
50561
|
}),
|
|
50552
50562
|
lock,
|
|
50553
50563
|
ownerAlive,
|
|
@@ -50591,7 +50601,7 @@ function tryOpenBrowser(url) {
|
|
|
50591
50601
|
return false;
|
|
50592
50602
|
}
|
|
50593
50603
|
}
|
|
50594
|
-
var
|
|
50604
|
+
var DAEMON_VERSION3 = true ? "daemon-0.2.0" : "daemon-dev";
|
|
50595
50605
|
function osFamily() {
|
|
50596
50606
|
const p = platform5();
|
|
50597
50607
|
if (p === "darwin") return "macos";
|
|
@@ -50609,17 +50619,30 @@ function intendedDeviceUuid() {
|
|
|
50609
50619
|
const key = salt ? `${machineKey()}:${salt}` : machineKey();
|
|
50610
50620
|
return deviceUuidFromMachineKey(key);
|
|
50611
50621
|
}
|
|
50622
|
+
function isNonInteractive() {
|
|
50623
|
+
return Boolean(process.env.CI) || process.stdin.isTTY !== true;
|
|
50624
|
+
}
|
|
50625
|
+
function prodRegisterOptIn() {
|
|
50626
|
+
const v = process.env.MODELSTAT_ALLOW_PROD_REGISTER?.trim().toLowerCase();
|
|
50627
|
+
return v === "1" || v === "true" || v === "yes";
|
|
50628
|
+
}
|
|
50612
50629
|
async function cmdSelfRegister() {
|
|
50613
50630
|
const deviceUuid = state.deviceUuid ?? intendedDeviceUuid();
|
|
50614
50631
|
const derived = !state.deviceUuid;
|
|
50632
|
+
if (derived && state.isProdDefaultApi && isNonInteractive() && !prodRegisterOptIn()) {
|
|
50633
|
+
process.stderr.write(
|
|
50634
|
+
"modelstat: refusing to self-register a new device against production from a\nnon-interactive/CI environment (no claim is possible here anyway). Either:\n \u2022 point at your own backend: DAEMON_API_URL=https://your-host (CI/e2e)\n \u2022 explicitly opt in: MODELSTAT_ALLOW_PROD_REGISTER=1\n \u2022 or run it interactively: npx modelstat@latest\n"
|
|
50635
|
+
);
|
|
50636
|
+
process.exit(2);
|
|
50637
|
+
}
|
|
50615
50638
|
const mid = machineKey();
|
|
50616
50639
|
const fingerprint = {
|
|
50617
50640
|
hostname: hostname2(),
|
|
50618
50641
|
os_family: osFamily(),
|
|
50619
50642
|
os_version: release(),
|
|
50620
50643
|
arch: osArch(),
|
|
50621
|
-
|
|
50622
|
-
|
|
50644
|
+
daemon: "modelstat-daemon",
|
|
50645
|
+
daemon_version: DAEMON_VERSION3,
|
|
50623
50646
|
// Stable, install-method-independent machine key. The server
|
|
50624
50647
|
// dedupes self-register on this so the same physical machine can
|
|
50625
50648
|
// never become two device rows, even if the UUID somehow differs
|
|
@@ -50845,7 +50868,7 @@ async function cmdConnect(opts) {
|
|
|
50845
50868
|
warn(`couldn't prepare summariser model: ${e.message}`);
|
|
50846
50869
|
warn("the background service will retry the download on its first scan");
|
|
50847
50870
|
}
|
|
50848
|
-
step("Installing/refreshing background service so the
|
|
50871
|
+
step("Installing/refreshing background service so the daemon survives reboots");
|
|
50849
50872
|
let serviceOk = false;
|
|
50850
50873
|
try {
|
|
50851
50874
|
const svc = installService();
|
|
@@ -50859,7 +50882,7 @@ async function cmdConnect(opts) {
|
|
|
50859
50882
|
} catch (e) {
|
|
50860
50883
|
emitEvent(opts, "service_install_failed", { error: e.message });
|
|
50861
50884
|
warn(`couldn't install service: ${e.message}`);
|
|
50862
|
-
warn("the
|
|
50885
|
+
warn("the daemon will not run in the background \u2014 re-run after fixing the issue");
|
|
50863
50886
|
}
|
|
50864
50887
|
step("Detecting installed AI tools and signed-in accounts");
|
|
50865
50888
|
let discovered = null;
|
|
@@ -51081,7 +51104,7 @@ async function cmdStats(args) {
|
|
|
51081
51104
|
console.log(` ${dashboard}`);
|
|
51082
51105
|
if (local) {
|
|
51083
51106
|
console.log(
|
|
51084
|
-
`local
|
|
51107
|
+
`local daemon: ${local.status ?? "?"}${local.message ? ` \xB7 ${local.message}` : ""}`
|
|
51085
51108
|
);
|
|
51086
51109
|
const stats = local.stats ?? {};
|
|
51087
51110
|
for (const [k, v] of Object.entries(stats)) console.log(` ${k}: ${v}`);
|
|
@@ -51096,9 +51119,9 @@ async function cmdStats(args) {
|
|
|
51096
51119
|
}
|
|
51097
51120
|
console.log(`device: ${view.device.id}`);
|
|
51098
51121
|
console.log(`host: ${view.device.hostname ?? "(unknown)"} (${view.device.os_family ?? "?"})`);
|
|
51099
|
-
console.log(`
|
|
51122
|
+
console.log(`daemon: ${view.device.daemon_version ?? "(unknown)"}`);
|
|
51100
51123
|
console.log(
|
|
51101
|
-
`status: ${view.device.
|
|
51124
|
+
`status: ${view.device.daemon_status ?? "(unknown)"}${view.device.last_seen_at ? ` \xB7 last seen ${view.device.last_seen_at}` : ""}`
|
|
51102
51125
|
);
|
|
51103
51126
|
console.log(
|
|
51104
51127
|
`claim: ${view.status}${view.status === "unclaimed" ? ` (at ${view.claim_url})` : ""}`
|
|
@@ -51226,7 +51249,7 @@ async function main() {
|
|
|
51226
51249
|
}
|
|
51227
51250
|
case "_daemon-health": {
|
|
51228
51251
|
try {
|
|
51229
|
-
console.log(JSON.stringify(daemonHealth({
|
|
51252
|
+
console.log(JSON.stringify(daemonHealth({ myDaemonVersion: DAEMON_VERSION3 })));
|
|
51230
51253
|
} catch (e) {
|
|
51231
51254
|
console.log(JSON.stringify({ decision: "spawn", error: e.message }));
|
|
51232
51255
|
}
|