modelstat 0.1.2 → 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 +168 -104
- package/dist/cli.mjs.map +1 -1
- package/package.json +7 -7
- 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,
|
|
@@ -4624,16 +4624,22 @@ function redact(text, repoRootAbs) {
|
|
|
4624
4624
|
});
|
|
4625
4625
|
}
|
|
4626
4626
|
out = out.replace(TOKEN_CANDIDATE, (match) => {
|
|
4627
|
-
if (/^[a-
|
|
4628
|
-
|
|
4629
|
-
|
|
4627
|
+
if (/^[a-fA-F0-9]{32,}$/.test(match)) {
|
|
4628
|
+
counts.secrets_found += 1;
|
|
4629
|
+
return "[REDACTED:hash]";
|
|
4630
|
+
}
|
|
4631
|
+
if (/^[A-Z0-9_]+$/.test(match)) return match;
|
|
4632
|
+
if (/=$|\+/.test(match) && entropy(match) >= 3.5) {
|
|
4633
|
+
counts.secrets_found += 1;
|
|
4634
|
+
return "[REDACTED:base64]";
|
|
4635
|
+
}
|
|
4630
4636
|
const hasDigit = /\d/.test(match);
|
|
4631
4637
|
const hasUpper = /[A-Z]/.test(match);
|
|
4632
4638
|
const hasLower = /[a-z]/.test(match);
|
|
4633
|
-
if (!(
|
|
4639
|
+
if (!(hasDigit && hasUpper && hasLower)) return match;
|
|
4634
4640
|
if (entropy(match) < 3.6) return match;
|
|
4635
4641
|
counts.secrets_found += 1;
|
|
4636
|
-
return
|
|
4642
|
+
return "[REDACTED:hi-entropy]";
|
|
4637
4643
|
});
|
|
4638
4644
|
out = out.replace(EMAIL_PATTERN, () => {
|
|
4639
4645
|
counts.emails_redacted += 1;
|
|
@@ -5039,7 +5045,7 @@ var init_schemas = __esm({
|
|
|
5039
5045
|
// available, the on-device Privacy Filter adapter.
|
|
5040
5046
|
// 3. Stripping code blocks and file-path noise.
|
|
5041
5047
|
// Optional — events without it fall back to metadata-only abstracts
|
|
5042
|
-
// (the historical behaviour). The
|
|
5048
|
+
// (the historical behaviour). The daemon-core pipeline runs
|
|
5043
5049
|
// redact() over it again as defence-in-depth before building the
|
|
5044
5050
|
// summarize prompt; it never gets stored long-term server-side, only
|
|
5045
5051
|
// used to construct the summarize input.
|
|
@@ -5070,7 +5076,7 @@ var init_schemas = __esm({
|
|
|
5070
5076
|
root_key: external_exports.string().max(60),
|
|
5071
5077
|
name: external_exports.string().max(120),
|
|
5072
5078
|
confidence: external_exports.number().min(0).max(1).default(0.7),
|
|
5073
|
-
/** Optional free-text reason the
|
|
5079
|
+
/** Optional free-text reason the daemon attached this tag — surfaces
|
|
5074
5080
|
* in the audit log so the user can see "why was this tagged X?" */
|
|
5075
5081
|
reason: external_exports.string().max(200).optional()
|
|
5076
5082
|
});
|
|
@@ -5093,7 +5099,7 @@ var init_schemas = __esm({
|
|
|
5093
5099
|
/** `source_event_id`s covered by this segment. Used for dedupe + replay. */
|
|
5094
5100
|
source_event_ids: external_exports.array(external_exports.string()).max(2e3),
|
|
5095
5101
|
/** Optional embedding of the abstract (BGE-small-en-v1.5, 384 dims).
|
|
5096
|
-
* Present when the
|
|
5102
|
+
* Present when the daemon has an Embedder adapter configured. */
|
|
5097
5103
|
abstract_embedding: external_exports.array(external_exports.number()).length(384).optional()
|
|
5098
5104
|
});
|
|
5099
5105
|
ToolAction = external_exports.object({
|
|
@@ -5107,8 +5113,10 @@ var init_schemas = __esm({
|
|
|
5107
5113
|
object: external_exports.string().max(60).nullable().default(null),
|
|
5108
5114
|
/** Governed safe flags (`destructive`, `remote`, …). (tier 0) */
|
|
5109
5115
|
qualifiers: external_exports.array(external_exports.string().max(40)).max(8).default([]),
|
|
5110
|
-
/** Value-masked argument skeleton (every value → `§`).
|
|
5111
|
-
|
|
5116
|
+
/** Value-masked argument skeleton (every value → `§`). Carried in full up
|
|
5117
|
+
* to a malicious-size guard (mirrors backend `MAX_TOOL_ACTION_PARAM_SHAPE_CHARS`);
|
|
5118
|
+
* the daemon clamps rather than truncating semantically. (tier 1) */
|
|
5119
|
+
param_shape: external_exports.string().max(16384).nullable().default(null),
|
|
5112
5120
|
/** Relevant non-sensitive keywords (e.g. ["rollout","restart","prod"]),
|
|
5113
5121
|
* OpenAI-redacted on-device. (tier 0) */
|
|
5114
5122
|
keywords: external_exports.array(external_exports.string().max(40)).max(12).default([]),
|
|
@@ -5118,7 +5126,7 @@ var init_schemas = __esm({
|
|
|
5118
5126
|
/** The compliance-redacted command text — PII/secrets stripped on-device
|
|
5119
5127
|
* (SOC2/GDPR), org-internal infra intact; the server derives semantics from
|
|
5120
5128
|
* it. Un-redacted raw never ships. (tier 0, post-redaction) */
|
|
5121
|
-
command_redacted: external_exports.string().max(
|
|
5129
|
+
command_redacted: external_exports.string().max(16384).nullable().default(null),
|
|
5122
5130
|
/** Per-script content abstracts for any script/bash FILES the command runs
|
|
5123
5131
|
* — summarized on-device by the local model, then redacted. Ordered by
|
|
5124
5132
|
* appearance; `token` is the script's token exactly as it appears in
|
|
@@ -5139,7 +5147,7 @@ var init_schemas = __esm({
|
|
|
5139
5147
|
session_id: external_exports.string().max(120),
|
|
5140
5148
|
/** The RawEvent that contained the tool_use (dedupe/replay anchor). */
|
|
5141
5149
|
source_event_id: external_exports.string(),
|
|
5142
|
-
/** Segment containing source_event_id — filled by the
|
|
5150
|
+
/** Segment containing source_event_id — filled by the daemon at
|
|
5143
5151
|
* batch-build time when known, else null. */
|
|
5144
5152
|
segment_id: external_exports.string().max(64).nullable().default(null),
|
|
5145
5153
|
/** The agent that made the call (AGENTS enum). */
|
|
@@ -5177,7 +5185,7 @@ var init_schemas = __esm({
|
|
|
5177
5185
|
batch_id: external_exports.string(),
|
|
5178
5186
|
// ULID
|
|
5179
5187
|
device_id: external_exports.string(),
|
|
5180
|
-
|
|
5188
|
+
daemon_version: external_exports.string().max(40),
|
|
5181
5189
|
events: external_exports.array(RawEvent).max(1e4),
|
|
5182
5190
|
segments: external_exports.array(Segment).max(2e3).default([]),
|
|
5183
5191
|
/** Per-call tool invocations (additive — old agents omit it, old
|
|
@@ -5193,8 +5201,8 @@ var init_schemas = __esm({
|
|
|
5193
5201
|
})
|
|
5194
5202
|
).optional(),
|
|
5195
5203
|
/** Optional per-session titles — session_id → short redacted title
|
|
5196
|
-
* (≤120 chars) produced by the
|
|
5197
|
-
* 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
|
|
5198
5206
|
* session view on every upload, so the latest batch always carries the
|
|
5199
5207
|
* freshest title. Absent for runtimes without a titler (older agents,
|
|
5200
5208
|
* no-op browser summariser). */
|
|
@@ -5203,20 +5211,20 @@ var init_schemas = __esm({
|
|
|
5203
5211
|
* {@link SessionMetadata}: the repos, pull requests, commits, and issues the
|
|
5204
5212
|
* session touched, detected on-device across git context, tool calls,
|
|
5205
5213
|
* redacted content, and the local model (so it works for any provider).
|
|
5206
|
-
* Additive — old
|
|
5214
|
+
* Additive — old daemons omit it, old servers ignore it (the wire has no
|
|
5207
5215
|
* `deny_unknown_fields`). The join layer between AI spend and shipped work. */
|
|
5208
5216
|
session_metadata: external_exports.record(external_exports.string(), SessionMetadata).optional()
|
|
5209
5217
|
});
|
|
5210
5218
|
HeartbeatPayload = external_exports.object({
|
|
5211
5219
|
device_id: external_exports.string(),
|
|
5212
|
-
status: external_exports.enum(
|
|
5220
|
+
status: external_exports.enum(DAEMON_PHASES),
|
|
5213
5221
|
message: external_exports.string().max(240).nullable(),
|
|
5214
5222
|
progress_done: external_exports.number().int().nonnegative().default(0),
|
|
5215
5223
|
progress_total: external_exports.number().int().nonnegative().default(0),
|
|
5216
5224
|
queue_size: external_exports.number().int().nonnegative().default(0),
|
|
5217
5225
|
stats: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
|
|
5218
5226
|
last_event_at: external_exports.string().datetime({ offset: true }).nullable(),
|
|
5219
|
-
|
|
5227
|
+
daemon_version: external_exports.string().max(40)
|
|
5220
5228
|
});
|
|
5221
5229
|
DeviceEnrollment = external_exports.object({
|
|
5222
5230
|
machine_id: external_exports.string(),
|
|
@@ -5225,10 +5233,10 @@ var init_schemas = __esm({
|
|
|
5225
5233
|
os_family: external_exports.enum(OS_FAMILIES),
|
|
5226
5234
|
os_version: external_exports.string().max(60),
|
|
5227
5235
|
arch: external_exports.enum(["x86_64", "arm64", "other"]),
|
|
5228
|
-
|
|
5236
|
+
daemon_version: external_exports.string().max(40)
|
|
5229
5237
|
});
|
|
5230
5238
|
DeviceSelfRegister = external_exports.object({
|
|
5231
|
-
/**
|
|
5239
|
+
/** Daemon-generated UUIDv7 — must pass shape + recent-timestamp checks. */
|
|
5232
5240
|
device_uuid: external_exports.string(),
|
|
5233
5241
|
/** Base64-encoded ed25519 public key, exactly 32 raw bytes. Optional
|
|
5234
5242
|
* but recommended (used for sender-constrained tokens / DPoP later). */
|
|
@@ -5241,8 +5249,8 @@ var init_schemas = __esm({
|
|
|
5241
5249
|
os_family: external_exports.enum(OS_FAMILIES).optional(),
|
|
5242
5250
|
os_version: external_exports.string().max(60).optional(),
|
|
5243
5251
|
arch: external_exports.enum(["x86_64", "arm64", "other"]).optional(),
|
|
5244
|
-
|
|
5245
|
-
|
|
5252
|
+
daemon: external_exports.string().max(80).optional(),
|
|
5253
|
+
daemon_version: external_exports.string().max(40).optional()
|
|
5246
5254
|
// Allow extra fields for forward-compat without breaking old agents.
|
|
5247
5255
|
}).catchall(external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])).default({})
|
|
5248
5256
|
});
|
|
@@ -5377,6 +5385,10 @@ var init_scripts = __esm({
|
|
|
5377
5385
|
});
|
|
5378
5386
|
|
|
5379
5387
|
// ../../packages/parsers/src/tool-action/index.ts
|
|
5388
|
+
function clampChars(s, max) {
|
|
5389
|
+
const cps = [...s];
|
|
5390
|
+
return cps.length > max ? cps.slice(0, max).join("") : s;
|
|
5391
|
+
}
|
|
5380
5392
|
function extractToolAction(call) {
|
|
5381
5393
|
const isMcp = call.server.startsWith("mcp:");
|
|
5382
5394
|
const command = isMcp ? null : shellCommandOf(call.input);
|
|
@@ -5387,8 +5399,8 @@ function extractToolAction(call) {
|
|
|
5387
5399
|
if (command != null) {
|
|
5388
5400
|
const [head = "", ...rest] = command.trim().split(/\s+/);
|
|
5389
5401
|
executable = basename(head) || null;
|
|
5390
|
-
param_shape = paramShape(rest.join(" ")) || null;
|
|
5391
|
-
command_redacted = redact(command, call.cwd ?? void 0).text
|
|
5402
|
+
param_shape = clampChars(paramShape(rest.join(" ")), MAX_FIELD_CHARS) || null;
|
|
5403
|
+
command_redacted = clampChars(redact(command, call.cwd ?? void 0).text, MAX_FIELD_CHARS) || null;
|
|
5392
5404
|
}
|
|
5393
5405
|
return {
|
|
5394
5406
|
surface,
|
|
@@ -5426,13 +5438,13 @@ function shellCommandOf(input) {
|
|
|
5426
5438
|
function basename(token) {
|
|
5427
5439
|
return token.split("/").pop() ?? token;
|
|
5428
5440
|
}
|
|
5429
|
-
var
|
|
5441
|
+
var MAX_FIELD_CHARS;
|
|
5430
5442
|
var init_tool_action = __esm({
|
|
5431
5443
|
"../../packages/parsers/src/tool-action/index.ts"() {
|
|
5432
5444
|
"use strict";
|
|
5433
5445
|
init_src();
|
|
5434
5446
|
init_scripts();
|
|
5435
|
-
|
|
5447
|
+
MAX_FIELD_CHARS = 16384;
|
|
5436
5448
|
}
|
|
5437
5449
|
});
|
|
5438
5450
|
|
|
@@ -8538,6 +8550,33 @@ async function probeIdentities(os2) {
|
|
|
8538
8550
|
} catch {
|
|
8539
8551
|
}
|
|
8540
8552
|
}
|
|
8553
|
+
const claudeConfigs = [`${homedir()}/.claude.json`];
|
|
8554
|
+
if (process.env.CLAUDE_CONFIG_DIR) {
|
|
8555
|
+
claudeConfigs.unshift(`${process.env.CLAUDE_CONFIG_DIR}/.claude.json`);
|
|
8556
|
+
}
|
|
8557
|
+
for (const candidate of claudeConfigs) {
|
|
8558
|
+
if (!existsSync3(candidate)) continue;
|
|
8559
|
+
try {
|
|
8560
|
+
const data = await fs4.promises.readFile(candidate, "utf8");
|
|
8561
|
+
const obj = JSON.parse(data);
|
|
8562
|
+
const acct = obj.oauthAccount;
|
|
8563
|
+
const stableId = acct?.accountUuid ?? acct?.organizationUuid;
|
|
8564
|
+
if (acct && stableId) {
|
|
8565
|
+
ids.push({
|
|
8566
|
+
provider: "anthropic",
|
|
8567
|
+
provider_account_id: stableId,
|
|
8568
|
+
provider_account_label: acct.emailAddress ?? acct.organizationName ?? acct.displayName ?? "Claude account",
|
|
8569
|
+
account_email: acct.emailAddress ?? null,
|
|
8570
|
+
account_org: acct.organizationName ?? acct.billingType ?? null,
|
|
8571
|
+
display_name: acct.displayName ?? null,
|
|
8572
|
+
owner_scope: "unassigned",
|
|
8573
|
+
detection_source: "claude_json_oauth"
|
|
8574
|
+
});
|
|
8575
|
+
break;
|
|
8576
|
+
}
|
|
8577
|
+
} catch {
|
|
8578
|
+
}
|
|
8579
|
+
}
|
|
8541
8580
|
for (const candidate of [
|
|
8542
8581
|
`${homedir()}/.codex/auth.json`,
|
|
8543
8582
|
`${homedir()}/.config/codex/auth.json`
|
|
@@ -33569,14 +33608,14 @@ ${captureLines}` : capture.stack;
|
|
|
33569
33608
|
}
|
|
33570
33609
|
});
|
|
33571
33610
|
|
|
33572
|
-
// ../../packages/
|
|
33611
|
+
// ../../packages/daemon-core/src/config/index.ts
|
|
33573
33612
|
function expBackoff(attempt) {
|
|
33574
33613
|
const i = Math.min(Math.max(attempt, 0), BACKOFF_MS.length - 1);
|
|
33575
33614
|
return BACKOFF_MS[i];
|
|
33576
33615
|
}
|
|
33577
33616
|
var INGEST_BATCH_MAX_EVENTS, BACKOFF_MS, BACKSTOP_SCAN_MS;
|
|
33578
33617
|
var init_config = __esm({
|
|
33579
|
-
"../../packages/
|
|
33618
|
+
"../../packages/daemon-core/src/config/index.ts"() {
|
|
33580
33619
|
"use strict";
|
|
33581
33620
|
INGEST_BATCH_MAX_EVENTS = 1e3;
|
|
33582
33621
|
BACKOFF_MS = [1e3, 2500, 5e3, 1e4, 2e4, 6e4];
|
|
@@ -33584,7 +33623,7 @@ var init_config = __esm({
|
|
|
33584
33623
|
}
|
|
33585
33624
|
});
|
|
33586
33625
|
|
|
33587
|
-
// ../../packages/
|
|
33626
|
+
// ../../packages/daemon-core/src/logger.ts
|
|
33588
33627
|
function defaultLevel() {
|
|
33589
33628
|
const env2 = globalThis.process?.env ?? {};
|
|
33590
33629
|
const raw = (env2.LOG_LEVEL ?? "").toLowerCase();
|
|
@@ -33637,7 +33676,7 @@ function describeErrorWithCause(err, depth = 4) {
|
|
|
33637
33676
|
}
|
|
33638
33677
|
var LEVEL_RANK;
|
|
33639
33678
|
var init_logger = __esm({
|
|
33640
|
-
"../../packages/
|
|
33679
|
+
"../../packages/daemon-core/src/logger.ts"() {
|
|
33641
33680
|
"use strict";
|
|
33642
33681
|
LEVEL_RANK = {
|
|
33643
33682
|
debug: 10,
|
|
@@ -33648,7 +33687,7 @@ var init_logger = __esm({
|
|
|
33648
33687
|
}
|
|
33649
33688
|
});
|
|
33650
33689
|
|
|
33651
|
-
// ../../packages/
|
|
33690
|
+
// ../../packages/daemon-core/src/http/index.ts
|
|
33652
33691
|
function classifyStatus(status2, attempt) {
|
|
33653
33692
|
if (status2 >= 200 && status2 < 300) return { type: "commit" };
|
|
33654
33693
|
if (status2 === 400 || status2 === 422) return { type: "drop", reason: `http_${status2}` };
|
|
@@ -33668,7 +33707,7 @@ function sleep(ms) {
|
|
|
33668
33707
|
}
|
|
33669
33708
|
var IngestClient;
|
|
33670
33709
|
var init_http = __esm({
|
|
33671
|
-
"../../packages/
|
|
33710
|
+
"../../packages/daemon-core/src/http/index.ts"() {
|
|
33672
33711
|
"use strict";
|
|
33673
33712
|
init_config();
|
|
33674
33713
|
init_logger();
|
|
@@ -45210,7 +45249,7 @@ var init_config2 = __esm({
|
|
|
45210
45249
|
DEFAULT_API_URL = "https://modelstat.ai";
|
|
45211
45250
|
LEGACY_LOCALHOST_API = "http://localhost:3010";
|
|
45212
45251
|
store = new Conf({
|
|
45213
|
-
projectName: "modelstat-
|
|
45252
|
+
projectName: "modelstat-daemon",
|
|
45214
45253
|
defaults: {
|
|
45215
45254
|
// Intentionally empty — the apiUrl getter below computes this
|
|
45216
45255
|
// from env + stored value + DEFAULT_API_URL. Keeping the stored
|
|
@@ -45242,7 +45281,7 @@ var init_config2 = __esm({
|
|
|
45242
45281
|
* or paired pre-0.0.8) → production default. The legacy localhost
|
|
45243
45282
|
* value is ignored so upgrades from 0.0.7 self-heal. */
|
|
45244
45283
|
get apiUrl() {
|
|
45245
|
-
if (process.env.
|
|
45284
|
+
if (process.env.DAEMON_API_URL) return process.env.DAEMON_API_URL;
|
|
45246
45285
|
const stored = store.get("apiUrl");
|
|
45247
45286
|
if (stored && stored !== LEGACY_LOCALHOST_API) return stored;
|
|
45248
45287
|
return DEFAULT_API_URL;
|
|
@@ -45250,6 +45289,16 @@ var init_config2 = __esm({
|
|
|
45250
45289
|
setApiUrl(v) {
|
|
45251
45290
|
store.set("apiUrl", v);
|
|
45252
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
|
+
},
|
|
45253
45302
|
// ── Identity: backed by ~/.modelstat/identity.json ─────────────
|
|
45254
45303
|
/** Seed a fresh identity after a successful self-register. Writes
|
|
45255
45304
|
* the file atomically; use `state.backupAndReset()` first if
|
|
@@ -45388,7 +45437,7 @@ async function rotateDeviceSecret(currentSecret) {
|
|
|
45388
45437
|
}
|
|
45389
45438
|
async function reportDiscovery(report) {
|
|
45390
45439
|
const bearer = state.bearer;
|
|
45391
|
-
if (!bearer) throw new Error("
|
|
45440
|
+
if (!bearer) throw new Error("daemon not enrolled \u2014 run `register` first");
|
|
45392
45441
|
const res = await (0, import_undici.request)(`${state.apiUrl}/v1/devices/discovery`, {
|
|
45393
45442
|
method: "POST",
|
|
45394
45443
|
headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
|
|
@@ -45433,7 +45482,7 @@ async function fetchDeviceViewLedgerByClaim(claimCode) {
|
|
|
45433
45482
|
}
|
|
45434
45483
|
function ingestClient() {
|
|
45435
45484
|
if (_ingest) return _ingest;
|
|
45436
|
-
const logger = createLogger("
|
|
45485
|
+
const logger = createLogger("daemon.ingest");
|
|
45437
45486
|
_ingest = new IngestClient({
|
|
45438
45487
|
apiUrl: state.apiUrl,
|
|
45439
45488
|
auth: {
|
|
@@ -45593,24 +45642,24 @@ var init_machine_key = __esm({
|
|
|
45593
45642
|
}
|
|
45594
45643
|
});
|
|
45595
45644
|
|
|
45596
|
-
// ../../packages/
|
|
45645
|
+
// ../../packages/daemon-core/src/contracts/index.ts
|
|
45597
45646
|
var init_contracts = __esm({
|
|
45598
|
-
"../../packages/
|
|
45647
|
+
"../../packages/daemon-core/src/contracts/index.ts"() {
|
|
45599
45648
|
"use strict";
|
|
45600
45649
|
init_schemas();
|
|
45601
45650
|
init_enums();
|
|
45602
45651
|
}
|
|
45603
45652
|
});
|
|
45604
45653
|
|
|
45605
|
-
// ../../packages/
|
|
45654
|
+
// ../../packages/daemon-core/src/ids.ts
|
|
45606
45655
|
var init_ids2 = __esm({
|
|
45607
|
-
"../../packages/
|
|
45656
|
+
"../../packages/daemon-core/src/ids.ts"() {
|
|
45608
45657
|
"use strict";
|
|
45609
45658
|
init_ids();
|
|
45610
45659
|
}
|
|
45611
45660
|
});
|
|
45612
45661
|
|
|
45613
|
-
// ../../packages/
|
|
45662
|
+
// ../../packages/daemon-core/src/queue/index.ts
|
|
45614
45663
|
function attachSegmentIdsByMap(calls, segmentByEvent) {
|
|
45615
45664
|
return calls.map((c) => ({
|
|
45616
45665
|
...c,
|
|
@@ -45618,14 +45667,14 @@ function attachSegmentIdsByMap(calls, segmentByEvent) {
|
|
|
45618
45667
|
}));
|
|
45619
45668
|
}
|
|
45620
45669
|
var init_queue = __esm({
|
|
45621
|
-
"../../packages/
|
|
45670
|
+
"../../packages/daemon-core/src/queue/index.ts"() {
|
|
45622
45671
|
"use strict";
|
|
45623
45672
|
init_config();
|
|
45624
45673
|
init_ids2();
|
|
45625
45674
|
}
|
|
45626
45675
|
});
|
|
45627
45676
|
|
|
45628
|
-
// ../../packages/
|
|
45677
|
+
// ../../packages/daemon-core/src/pipeline/cognition.ts
|
|
45629
45678
|
function buildCognitionUserPrompt(abstract) {
|
|
45630
45679
|
return `Summary: "${abstract.replace(/\s+/g, " ").trim().slice(0, 480)}"
|
|
45631
45680
|
|
|
@@ -45697,7 +45746,7 @@ function extractFirstJsonObject(s) {
|
|
|
45697
45746
|
}
|
|
45698
45747
|
var COGNITION_SYSTEM_PROMPT, MAX_COGNITION_TAGS_PER_FIELD, MAX_COGNITION_TAG_CHARS, COGNITION_MAX_TOKENS, COGNITION_TEMPERATURE;
|
|
45699
45748
|
var init_cognition = __esm({
|
|
45700
|
-
"../../packages/
|
|
45749
|
+
"../../packages/daemon-core/src/pipeline/cognition.ts"() {
|
|
45701
45750
|
"use strict";
|
|
45702
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.';
|
|
45703
45752
|
MAX_COGNITION_TAGS_PER_FIELD = 3;
|
|
@@ -45707,10 +45756,10 @@ var init_cognition = __esm({
|
|
|
45707
45756
|
}
|
|
45708
45757
|
});
|
|
45709
45758
|
|
|
45710
|
-
// ../../packages/
|
|
45759
|
+
// ../../packages/daemon-core/src/pipeline/prompts.ts
|
|
45711
45760
|
var OLLAMA_CHAT_MODEL, OLLAMA_EMBED_MODEL, SUMMARISER_SYSTEM_PROMPT, SUMMARISER_MAX_TOKENS, SUMMARISER_TEMPERATURE, QWEN_CHARS_PER_TOKEN, ABSTRACT_OUTPUT_MAX_CHARS;
|
|
45712
45761
|
var init_prompts = __esm({
|
|
45713
|
-
"../../packages/
|
|
45762
|
+
"../../packages/daemon-core/src/pipeline/prompts.ts"() {
|
|
45714
45763
|
"use strict";
|
|
45715
45764
|
OLLAMA_CHAT_MODEL = "qwen3:4b";
|
|
45716
45765
|
OLLAMA_EMBED_MODEL = "bge-small-en-v1.5";
|
|
@@ -45722,7 +45771,7 @@ var init_prompts = __esm({
|
|
|
45722
45771
|
}
|
|
45723
45772
|
});
|
|
45724
45773
|
|
|
45725
|
-
// ../../packages/
|
|
45774
|
+
// ../../packages/daemon-core/src/pipeline/script-summary.ts
|
|
45726
45775
|
function buildScriptSummaryUserPrompt(input) {
|
|
45727
45776
|
const body = input.content.slice(0, SCRIPT_SUMMARY_INPUT_MAX_CHARS);
|
|
45728
45777
|
return [
|
|
@@ -45737,7 +45786,7 @@ function buildScriptSummaryUserPrompt(input) {
|
|
|
45737
45786
|
}
|
|
45738
45787
|
var SCRIPT_SUMMARY_OUTPUT_MAX_CHARS, SCRIPT_SUMMARY_TEMPERATURE, SCRIPT_SUMMARY_MAX_TOKENS, SCRIPT_SUMMARY_INPUT_MAX_CHARS, SCRIPT_SUMMARY_SYSTEM_PROMPT;
|
|
45739
45788
|
var init_script_summary = __esm({
|
|
45740
|
-
"../../packages/
|
|
45789
|
+
"../../packages/daemon-core/src/pipeline/script-summary.ts"() {
|
|
45741
45790
|
"use strict";
|
|
45742
45791
|
SCRIPT_SUMMARY_OUTPUT_MAX_CHARS = 200;
|
|
45743
45792
|
SCRIPT_SUMMARY_TEMPERATURE = 0.2;
|
|
@@ -45754,7 +45803,7 @@ Rules:
|
|
|
45754
45803
|
}
|
|
45755
45804
|
});
|
|
45756
45805
|
|
|
45757
|
-
// ../../packages/
|
|
45806
|
+
// ../../packages/daemon-core/src/pipeline/title.ts
|
|
45758
45807
|
function buildTitleUserPrompt(input) {
|
|
45759
45808
|
const lines = input.abstracts.map(
|
|
45760
45809
|
(a, i) => ` [part ${i + 1}] ${a.replace(/\s+/g, " ").trim().slice(0, TITLER_ABSTRACT_SLICE_CHARS)}`
|
|
@@ -45825,7 +45874,7 @@ async function buildSessionTitles(segments, entitle) {
|
|
|
45825
45874
|
}
|
|
45826
45875
|
var TITLER_SYSTEM_PROMPT, TITLE_MAX_CHARS, TITLER_MAX_TOKENS, TITLER_TEMPERATURE, TITLER_MAX_ABSTRACTS, TITLER_ABSTRACT_SLICE_CHARS;
|
|
45827
45876
|
var init_title = __esm({
|
|
45828
|
-
"../../packages/
|
|
45877
|
+
"../../packages/daemon-core/src/pipeline/title.ts"() {
|
|
45829
45878
|
"use strict";
|
|
45830
45879
|
init_redact();
|
|
45831
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.";
|
|
@@ -45837,7 +45886,7 @@ var init_title = __esm({
|
|
|
45837
45886
|
}
|
|
45838
45887
|
});
|
|
45839
45888
|
|
|
45840
|
-
// ../../packages/
|
|
45889
|
+
// ../../packages/daemon-core/src/pipeline/session-metadata.ts
|
|
45841
45890
|
function buildLinkExtractUserPrompt(abstracts) {
|
|
45842
45891
|
const lines = abstracts.map((a, i) => ` [part ${i + 1}] ${a.replace(/\s+/g, " ").trim().slice(0, 240)}`).join("\n");
|
|
45843
45892
|
return `Summaries of the session's parts:
|
|
@@ -45936,7 +45985,7 @@ async function buildSessionMetadata(segments, events, opts = {}) {
|
|
|
45936
45985
|
}
|
|
45937
45986
|
var LINK_EXTRACT_SYSTEM_PROMPT, LINK_EXTRACT_MAX_TOKENS, LINK_EXTRACT_TEMPERATURE, LINK_EXTRACT_MAX_ABSTRACTS;
|
|
45938
45987
|
var init_session_metadata2 = __esm({
|
|
45939
|
-
"../../packages/
|
|
45988
|
+
"../../packages/daemon-core/src/pipeline/session-metadata.ts"() {
|
|
45940
45989
|
"use strict";
|
|
45941
45990
|
init_session_metadata();
|
|
45942
45991
|
init_title();
|
|
@@ -45947,7 +45996,7 @@ var init_session_metadata2 = __esm({
|
|
|
45947
45996
|
}
|
|
45948
45997
|
});
|
|
45949
45998
|
|
|
45950
|
-
// ../../packages/
|
|
45999
|
+
// ../../packages/daemon-core/src/pipeline/index.ts
|
|
45951
46000
|
async function buildSegmentsForSession(events, adapters2, onProgress) {
|
|
45952
46001
|
if (events.length === 0) return [];
|
|
45953
46002
|
const bySession = /* @__PURE__ */ new Map();
|
|
@@ -46249,7 +46298,7 @@ function inferEnvironment(branch) {
|
|
|
46249
46298
|
}
|
|
46250
46299
|
var SEGMENT_TIME_GAP_MS, SEGMENT_TOPIC_THRESHOLD, SEGMENT_MAX_TURNS, SEGMENT_MAX_DURATION_MS, SEGMENT_MAX_CONTENT_CHARS, ABSTRACT_MAX_CHARS;
|
|
46251
46300
|
var init_pipeline = __esm({
|
|
46252
|
-
"../../packages/
|
|
46301
|
+
"../../packages/daemon-core/src/pipeline/index.ts"() {
|
|
46253
46302
|
"use strict";
|
|
46254
46303
|
init_ids();
|
|
46255
46304
|
init_redact();
|
|
@@ -46270,9 +46319,9 @@ var init_pipeline = __esm({
|
|
|
46270
46319
|
}
|
|
46271
46320
|
});
|
|
46272
46321
|
|
|
46273
|
-
// ../../packages/
|
|
46322
|
+
// ../../packages/daemon-core/src/index.ts
|
|
46274
46323
|
var init_src3 = __esm({
|
|
46275
|
-
"../../packages/
|
|
46324
|
+
"../../packages/daemon-core/src/index.ts"() {
|
|
46276
46325
|
"use strict";
|
|
46277
46326
|
init_contracts();
|
|
46278
46327
|
init_ids2();
|
|
@@ -46284,12 +46333,12 @@ var init_src3 = __esm({
|
|
|
46284
46333
|
}
|
|
46285
46334
|
});
|
|
46286
46335
|
|
|
46287
|
-
// ../../packages/
|
|
46336
|
+
// ../../packages/daemon-core/src/node/file-queue-store.ts
|
|
46288
46337
|
import { promises as fs3 } from "fs";
|
|
46289
46338
|
import { dirname as dirname4 } from "path";
|
|
46290
46339
|
var SENT_TTL_MS, FileQueueStore;
|
|
46291
46340
|
var init_file_queue_store = __esm({
|
|
46292
|
-
"../../packages/
|
|
46341
|
+
"../../packages/daemon-core/src/node/file-queue-store.ts"() {
|
|
46293
46342
|
"use strict";
|
|
46294
46343
|
SENT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
46295
46344
|
FileQueueStore = class {
|
|
@@ -46407,7 +46456,7 @@ var init_file_queue_store = __esm({
|
|
|
46407
46456
|
}
|
|
46408
46457
|
});
|
|
46409
46458
|
|
|
46410
|
-
// ../../packages/
|
|
46459
|
+
// ../../packages/daemon-core/src/node/llama.ts
|
|
46411
46460
|
import { existsSync as existsSync7 } from "fs";
|
|
46412
46461
|
import { mkdir } from "fs/promises";
|
|
46413
46462
|
import { homedir as homedir5 } from "os";
|
|
@@ -46703,7 +46752,7 @@ function llamaExtractLinks(cfg = defaultLlamaConfig()) {
|
|
|
46703
46752
|
}
|
|
46704
46753
|
var DEFAULT_LLAMA_MODEL_URL, LLAMA_MAX_TOKENS, loaded, loadPromise, inflight, llamaInstance;
|
|
46705
46754
|
var init_llama = __esm({
|
|
46706
|
-
"../../packages/
|
|
46755
|
+
"../../packages/daemon-core/src/node/llama.ts"() {
|
|
46707
46756
|
"use strict";
|
|
46708
46757
|
init_cognition();
|
|
46709
46758
|
init_prompts();
|
|
@@ -46719,7 +46768,7 @@ var init_llama = __esm({
|
|
|
46719
46768
|
}
|
|
46720
46769
|
});
|
|
46721
46770
|
|
|
46722
|
-
// ../../packages/
|
|
46771
|
+
// ../../packages/daemon-core/src/node/ollama.ts
|
|
46723
46772
|
function defaultOllamaConfig() {
|
|
46724
46773
|
const base = globalThis.process?.env ?? {};
|
|
46725
46774
|
return {
|
|
@@ -46822,14 +46871,14 @@ function ollamaCognize(cfg = defaultOllamaConfig()) {
|
|
|
46822
46871
|
};
|
|
46823
46872
|
}
|
|
46824
46873
|
var init_ollama = __esm({
|
|
46825
|
-
"../../packages/
|
|
46874
|
+
"../../packages/daemon-core/src/node/ollama.ts"() {
|
|
46826
46875
|
"use strict";
|
|
46827
46876
|
init_prompts();
|
|
46828
46877
|
init_cognition();
|
|
46829
46878
|
}
|
|
46830
46879
|
});
|
|
46831
46880
|
|
|
46832
|
-
// ../../packages/
|
|
46881
|
+
// ../../packages/daemon-core/src/optional-module.ts
|
|
46833
46882
|
function isMissingOptionalModuleError(err) {
|
|
46834
46883
|
const code = err?.code;
|
|
46835
46884
|
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
|
|
@@ -46838,13 +46887,13 @@ function isMissingOptionalModuleError(err) {
|
|
|
46838
46887
|
}
|
|
46839
46888
|
var OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS;
|
|
46840
46889
|
var init_optional_module = __esm({
|
|
46841
|
-
"../../packages/
|
|
46890
|
+
"../../packages/daemon-core/src/optional-module.ts"() {
|
|
46842
46891
|
"use strict";
|
|
46843
46892
|
OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS = 3;
|
|
46844
46893
|
}
|
|
46845
46894
|
});
|
|
46846
46895
|
|
|
46847
|
-
// ../../packages/
|
|
46896
|
+
// ../../packages/daemon-core/src/node/transformersjs-embed.ts
|
|
46848
46897
|
async function loadPipeline(model) {
|
|
46849
46898
|
if (cached) return cached;
|
|
46850
46899
|
if (loadFailedPermanently) return null;
|
|
@@ -46898,7 +46947,7 @@ function createTransformersJsEmbedder(model = DEFAULT_MODEL) {
|
|
|
46898
46947
|
}
|
|
46899
46948
|
var cached, loadPromise2, loadFailedPermanently, loadAttempts, warnedUnavailable, warnedInferenceError, DEFAULT_MODEL, importModule;
|
|
46900
46949
|
var init_transformersjs_embed = __esm({
|
|
46901
|
-
"../../packages/
|
|
46950
|
+
"../../packages/daemon-core/src/node/transformersjs-embed.ts"() {
|
|
46902
46951
|
"use strict";
|
|
46903
46952
|
init_optional_module();
|
|
46904
46953
|
cached = null;
|
|
@@ -46915,7 +46964,7 @@ var init_transformersjs_embed = __esm({
|
|
|
46915
46964
|
}
|
|
46916
46965
|
});
|
|
46917
46966
|
|
|
46918
|
-
// ../../packages/
|
|
46967
|
+
// ../../packages/daemon-core/src/node/index.ts
|
|
46919
46968
|
var node_exports = {};
|
|
46920
46969
|
__export(node_exports, {
|
|
46921
46970
|
DEFAULT_LLAMA_MODEL_URL: () => DEFAULT_LLAMA_MODEL_URL,
|
|
@@ -46937,7 +46986,7 @@ __export(node_exports, {
|
|
|
46937
46986
|
ollamaTokenize: () => ollamaTokenize
|
|
46938
46987
|
});
|
|
46939
46988
|
var init_node2 = __esm({
|
|
46940
|
-
"../../packages/
|
|
46989
|
+
"../../packages/daemon-core/src/node/index.ts"() {
|
|
46941
46990
|
"use strict";
|
|
46942
46991
|
init_file_queue_store();
|
|
46943
46992
|
init_llama();
|
|
@@ -46946,7 +46995,7 @@ var init_node2 = __esm({
|
|
|
46946
46995
|
}
|
|
46947
46996
|
});
|
|
46948
46997
|
|
|
46949
|
-
// ../../packages/
|
|
46998
|
+
// ../../packages/daemon-core/src/redact/privacy-filter.ts
|
|
46950
46999
|
async function createPrivacyFilterRedactor(opts = {}) {
|
|
46951
47000
|
const isBrowser = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
46952
47001
|
const device = opts.device ?? (isBrowser ? "webgpu" : "cpu");
|
|
@@ -47052,7 +47101,7 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
47052
47101
|
};
|
|
47053
47102
|
}
|
|
47054
47103
|
var init_privacy_filter = __esm({
|
|
47055
|
-
"../../packages/
|
|
47104
|
+
"../../packages/daemon-core/src/redact/privacy-filter.ts"() {
|
|
47056
47105
|
"use strict";
|
|
47057
47106
|
init_optional_module();
|
|
47058
47107
|
}
|
|
@@ -47178,7 +47227,7 @@ async function getAdapters() {
|
|
|
47178
47227
|
await import("node-llama-cpp");
|
|
47179
47228
|
} catch (err) {
|
|
47180
47229
|
throw new Error(
|
|
47181
|
-
`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}`
|
|
47182
47231
|
);
|
|
47183
47232
|
}
|
|
47184
47233
|
console.log("[modelstat] using bundled local summariser (Qwen3.5-4B, runs on this machine)");
|
|
@@ -47249,7 +47298,7 @@ function withNonNullTokens(e) {
|
|
|
47249
47298
|
}
|
|
47250
47299
|
async function scanAll(cb = {}) {
|
|
47251
47300
|
const deviceId = state.deviceId;
|
|
47252
|
-
if (!deviceId) throw new Error("
|
|
47301
|
+
if (!deviceId) throw new Error("daemon not enrolled \u2014 run `register` first");
|
|
47253
47302
|
const jobs = [];
|
|
47254
47303
|
try {
|
|
47255
47304
|
const base = join6(homedir6(), ".claude/projects");
|
|
@@ -47354,7 +47403,7 @@ async function scanAll(cb = {}) {
|
|
|
47354
47403
|
const batch = {
|
|
47355
47404
|
batch_id: batchId(),
|
|
47356
47405
|
device_id: deviceId,
|
|
47357
|
-
|
|
47406
|
+
daemon_version: DAEMON_VERSION,
|
|
47358
47407
|
events,
|
|
47359
47408
|
segments,
|
|
47360
47409
|
tool_calls: attachSegmentIdsByMap(toolCallBuffer, callSegmentByEvent),
|
|
@@ -47429,7 +47478,7 @@ async function scanAll(cb = {}) {
|
|
|
47429
47478
|
morePending
|
|
47430
47479
|
};
|
|
47431
47480
|
}
|
|
47432
|
-
var
|
|
47481
|
+
var DAEMON_VERSION, BATCH_MAX_EVENTS, BATCH_MAX_TOOL_CALLS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
|
|
47433
47482
|
var init_scan = __esm({
|
|
47434
47483
|
"src/scan.ts"() {
|
|
47435
47484
|
"use strict";
|
|
@@ -47440,7 +47489,7 @@ var init_scan = __esm({
|
|
|
47440
47489
|
init_api();
|
|
47441
47490
|
init_config2();
|
|
47442
47491
|
init_pipeline2();
|
|
47443
|
-
|
|
47492
|
+
DAEMON_VERSION = true ? "daemon-0.2.0" : "daemon-dev";
|
|
47444
47493
|
BATCH_MAX_EVENTS = INGEST_BATCH_MAX_EVENTS;
|
|
47445
47494
|
BATCH_MAX_TOOL_CALLS = 2e4;
|
|
47446
47495
|
BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
|
|
@@ -47487,7 +47536,7 @@ function readDaemonLock(lockFile = LOCK_FILE) {
|
|
|
47487
47536
|
return {
|
|
47488
47537
|
pid: obj.pid,
|
|
47489
47538
|
startedAt: obj.startedAt ?? "unknown",
|
|
47490
|
-
|
|
47539
|
+
daemonVersion: obj.daemonVersion ?? "unknown",
|
|
47491
47540
|
apiUrl: obj.apiUrl ?? "unknown"
|
|
47492
47541
|
};
|
|
47493
47542
|
} catch {
|
|
@@ -47529,7 +47578,7 @@ function acquireDaemonLock(opts) {
|
|
|
47529
47578
|
const meta = {
|
|
47530
47579
|
pid: process.pid,
|
|
47531
47580
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47532
|
-
|
|
47581
|
+
daemonVersion: opts.daemonVersion,
|
|
47533
47582
|
apiUrl: opts.apiUrl
|
|
47534
47583
|
};
|
|
47535
47584
|
writeLockAtomic(meta);
|
|
@@ -47847,7 +47896,7 @@ var init_node3 = __esm({
|
|
|
47847
47896
|
}
|
|
47848
47897
|
});
|
|
47849
47898
|
|
|
47850
|
-
// ../../packages/
|
|
47899
|
+
// ../../packages/daemon-core/src/policies/index.ts
|
|
47851
47900
|
var policies_exports = {};
|
|
47852
47901
|
__export(policies_exports, {
|
|
47853
47902
|
createPolicyRefresher: () => createPolicyRefresher
|
|
@@ -47891,7 +47940,7 @@ function createPolicyRefresher(opts) {
|
|
|
47891
47940
|
}
|
|
47892
47941
|
var DEFAULT_REFRESH_MS, policiesKind;
|
|
47893
47942
|
var init_policies2 = __esm({
|
|
47894
|
-
"../../packages/
|
|
47943
|
+
"../../packages/daemon-core/src/policies/index.ts"() {
|
|
47895
47944
|
"use strict";
|
|
47896
47945
|
init_src();
|
|
47897
47946
|
init_src4();
|
|
@@ -49671,7 +49720,7 @@ function snapshotBody() {
|
|
|
49671
49720
|
queue_size: status.queueSize,
|
|
49672
49721
|
stats: status.stats,
|
|
49673
49722
|
last_event_at: status.lastEventAt,
|
|
49674
|
-
|
|
49723
|
+
daemon_version: DAEMON_VERSION2,
|
|
49675
49724
|
machine_id: machineKey()
|
|
49676
49725
|
};
|
|
49677
49726
|
}
|
|
@@ -49696,7 +49745,7 @@ async function sendHeartbeat() {
|
|
|
49696
49745
|
if (!bearer || !deviceId) return;
|
|
49697
49746
|
const body = { ...snapshotBody(), device_id: deviceId };
|
|
49698
49747
|
try {
|
|
49699
|
-
const res = await (0, import_undici2.request)(`${state.apiUrl}/v1/
|
|
49748
|
+
const res = await (0, import_undici2.request)(`${state.apiUrl}/v1/daemon/heartbeat`, {
|
|
49700
49749
|
method: "POST",
|
|
49701
49750
|
headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
|
|
49702
49751
|
body: JSON.stringify(body)
|
|
@@ -49827,7 +49876,7 @@ async function runDaemon(opts = {}) {
|
|
|
49827
49876
|
throw new Error("not enrolled \u2014 run `npx modelstat@latest` first");
|
|
49828
49877
|
}
|
|
49829
49878
|
const lock = acquireDaemonLock({
|
|
49830
|
-
|
|
49879
|
+
daemonVersion: DAEMON_VERSION2,
|
|
49831
49880
|
apiUrl: state.apiUrl,
|
|
49832
49881
|
force: opts.force === true,
|
|
49833
49882
|
// If a racing daemon out-renamed us for the lock (see lock.ts
|
|
@@ -49845,7 +49894,7 @@ async function runDaemon(opts = {}) {
|
|
|
49845
49894
|
console.log(
|
|
49846
49895
|
`modelstat daemon is already running \u2014 PID ${lock.owner.pid}, started ${formatAge(
|
|
49847
49896
|
lock.ageSec
|
|
49848
|
-
)} ago,
|
|
49897
|
+
)} ago, daemon ${lock.owner.daemonVersion}.`
|
|
49849
49898
|
);
|
|
49850
49899
|
console.log(" \u2192 to stop it: kill " + lock.owner.pid);
|
|
49851
49900
|
console.log(" \u2192 to force-replace it: modelstat start --force");
|
|
@@ -49937,7 +49986,7 @@ async function runDaemon(opts = {}) {
|
|
|
49937
49986
|
await new Promise(() => {
|
|
49938
49987
|
});
|
|
49939
49988
|
}
|
|
49940
|
-
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;
|
|
49941
49990
|
var init_daemon = __esm({
|
|
49942
49991
|
"src/daemon.ts"() {
|
|
49943
49992
|
"use strict";
|
|
@@ -49950,7 +49999,7 @@ var init_daemon = __esm({
|
|
|
49950
49999
|
init_machine_key();
|
|
49951
50000
|
init_scan();
|
|
49952
50001
|
init_single_flight();
|
|
49953
|
-
|
|
50002
|
+
DAEMON_VERSION2 = true ? "daemon-0.2.0" : "daemon-dev";
|
|
49954
50003
|
HEARTBEAT_INTERVAL_MS = 1e4;
|
|
49955
50004
|
SCAN_INTERVAL_MS = 5 * 60 * 1e3;
|
|
49956
50005
|
DISCOVERY_INTERVAL_MS = 6e4;
|
|
@@ -50082,6 +50131,7 @@ import { createInterface as createInterface3 } from "readline";
|
|
|
50082
50131
|
// src/service.ts
|
|
50083
50132
|
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
50084
50133
|
import {
|
|
50134
|
+
chmodSync as chmodSync3,
|
|
50085
50135
|
copyFileSync,
|
|
50086
50136
|
existsSync as existsSync9,
|
|
50087
50137
|
mkdirSync as mkdirSync3,
|
|
@@ -50094,7 +50144,7 @@ import { createRequire } from "module";
|
|
|
50094
50144
|
import { homedir as homedir7, platform as platform3, userInfo } from "os";
|
|
50095
50145
|
import { dirname as dirname6, join as join7 } from "path";
|
|
50096
50146
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
50097
|
-
var SERVICE_LABEL = "ai.modelstat.
|
|
50147
|
+
var SERVICE_LABEL = "ai.modelstat.daemon";
|
|
50098
50148
|
var SYSTEMD_UNIT = "modelstat";
|
|
50099
50149
|
function home() {
|
|
50100
50150
|
return homedir7();
|
|
@@ -50301,7 +50351,7 @@ function writeSystemdUnit(cliPath) {
|
|
|
50301
50351
|
const unitPath = systemdUnitPath();
|
|
50302
50352
|
mkdirSync3(dirname6(unitPath), { recursive: true });
|
|
50303
50353
|
const unit = `[Unit]
|
|
50304
|
-
Description=modelstat
|
|
50354
|
+
Description=modelstat daemon
|
|
50305
50355
|
Documentation=https://modelstat.ai
|
|
50306
50356
|
After=network-online.target
|
|
50307
50357
|
Wants=network-online.target
|
|
@@ -50368,7 +50418,7 @@ function installService() {
|
|
|
50368
50418
|
return { path: systemdUnitPath(), logs: logDir() };
|
|
50369
50419
|
}
|
|
50370
50420
|
throw new Error(
|
|
50371
|
-
`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.`
|
|
50372
50422
|
);
|
|
50373
50423
|
}
|
|
50374
50424
|
function uninstallService() {
|
|
@@ -50396,6 +50446,7 @@ function installTrayApp(sourceAppPath) {
|
|
|
50396
50446
|
if (r.status !== 0) {
|
|
50397
50447
|
throw new Error(`cp ModelstatTray.app failed: ${r.stderr?.trim() || `exit ${r.status}`}`);
|
|
50398
50448
|
}
|
|
50449
|
+
chmodSync3(join7(dest, "Contents", "MacOS", "modelstat-tray"), 493);
|
|
50399
50450
|
return { installedAt: dest };
|
|
50400
50451
|
}
|
|
50401
50452
|
async function bundledTrayAppPath(progress) {
|
|
@@ -50404,7 +50455,7 @@ async function bundledTrayAppPath(progress) {
|
|
|
50404
50455
|
const candidates = [
|
|
50405
50456
|
// Pre-built .app — CI with codesigning drops one here.
|
|
50406
50457
|
join7(here2, "..", "vendor", "ModelstatTray.app"),
|
|
50407
|
-
// Local dev layout: apps/
|
|
50458
|
+
// Local dev layout: apps/daemon/src/service.ts → ../../tray-mac/build/ModelstatTray.app
|
|
50408
50459
|
join7(here2, "..", "..", "tray-mac", "build", "ModelstatTray.app")
|
|
50409
50460
|
];
|
|
50410
50461
|
for (const c of candidates) {
|
|
@@ -50478,7 +50529,7 @@ function decideSupervision(input) {
|
|
|
50478
50529
|
const fresh = input.statusFreshMs ?? STATUS_FRESH_MS;
|
|
50479
50530
|
const grace = input.bootGraceMs ?? BOOT_GRACE_MS;
|
|
50480
50531
|
if (!input.lock || !input.ownerAlive) return "spawn";
|
|
50481
|
-
if (input.
|
|
50532
|
+
if (input.myDaemonVersion && input.lock.daemonVersion !== "unknown" && input.lock.daemonVersion !== input.myDaemonVersion) {
|
|
50482
50533
|
return "replace";
|
|
50483
50534
|
}
|
|
50484
50535
|
if (input.statusAgeMs !== null && input.statusAgeMs <= fresh) return "adopt";
|
|
@@ -50506,7 +50557,7 @@ function daemonHealth(opts = {}) {
|
|
|
50506
50557
|
ownerAlive,
|
|
50507
50558
|
lockAgeMs,
|
|
50508
50559
|
statusAgeMs,
|
|
50509
|
-
|
|
50560
|
+
myDaemonVersion: opts.myDaemonVersion
|
|
50510
50561
|
}),
|
|
50511
50562
|
lock,
|
|
50512
50563
|
ownerAlive,
|
|
@@ -50550,7 +50601,7 @@ function tryOpenBrowser(url) {
|
|
|
50550
50601
|
return false;
|
|
50551
50602
|
}
|
|
50552
50603
|
}
|
|
50553
|
-
var
|
|
50604
|
+
var DAEMON_VERSION3 = true ? "daemon-0.2.0" : "daemon-dev";
|
|
50554
50605
|
function osFamily() {
|
|
50555
50606
|
const p = platform5();
|
|
50556
50607
|
if (p === "darwin") return "macos";
|
|
@@ -50568,17 +50619,30 @@ function intendedDeviceUuid() {
|
|
|
50568
50619
|
const key = salt ? `${machineKey()}:${salt}` : machineKey();
|
|
50569
50620
|
return deviceUuidFromMachineKey(key);
|
|
50570
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
|
+
}
|
|
50571
50629
|
async function cmdSelfRegister() {
|
|
50572
50630
|
const deviceUuid = state.deviceUuid ?? intendedDeviceUuid();
|
|
50573
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
|
+
}
|
|
50574
50638
|
const mid = machineKey();
|
|
50575
50639
|
const fingerprint = {
|
|
50576
50640
|
hostname: hostname2(),
|
|
50577
50641
|
os_family: osFamily(),
|
|
50578
50642
|
os_version: release(),
|
|
50579
50643
|
arch: osArch(),
|
|
50580
|
-
|
|
50581
|
-
|
|
50644
|
+
daemon: "modelstat-daemon",
|
|
50645
|
+
daemon_version: DAEMON_VERSION3,
|
|
50582
50646
|
// Stable, install-method-independent machine key. The server
|
|
50583
50647
|
// dedupes self-register on this so the same physical machine can
|
|
50584
50648
|
// never become two device rows, even if the UUID somehow differs
|
|
@@ -50804,7 +50868,7 @@ async function cmdConnect(opts) {
|
|
|
50804
50868
|
warn(`couldn't prepare summariser model: ${e.message}`);
|
|
50805
50869
|
warn("the background service will retry the download on its first scan");
|
|
50806
50870
|
}
|
|
50807
|
-
step("Installing/refreshing background service so the
|
|
50871
|
+
step("Installing/refreshing background service so the daemon survives reboots");
|
|
50808
50872
|
let serviceOk = false;
|
|
50809
50873
|
try {
|
|
50810
50874
|
const svc = installService();
|
|
@@ -50818,7 +50882,7 @@ async function cmdConnect(opts) {
|
|
|
50818
50882
|
} catch (e) {
|
|
50819
50883
|
emitEvent(opts, "service_install_failed", { error: e.message });
|
|
50820
50884
|
warn(`couldn't install service: ${e.message}`);
|
|
50821
|
-
warn("the
|
|
50885
|
+
warn("the daemon will not run in the background \u2014 re-run after fixing the issue");
|
|
50822
50886
|
}
|
|
50823
50887
|
step("Detecting installed AI tools and signed-in accounts");
|
|
50824
50888
|
let discovered = null;
|
|
@@ -51040,7 +51104,7 @@ async function cmdStats(args) {
|
|
|
51040
51104
|
console.log(` ${dashboard}`);
|
|
51041
51105
|
if (local) {
|
|
51042
51106
|
console.log(
|
|
51043
|
-
`local
|
|
51107
|
+
`local daemon: ${local.status ?? "?"}${local.message ? ` \xB7 ${local.message}` : ""}`
|
|
51044
51108
|
);
|
|
51045
51109
|
const stats = local.stats ?? {};
|
|
51046
51110
|
for (const [k, v] of Object.entries(stats)) console.log(` ${k}: ${v}`);
|
|
@@ -51055,9 +51119,9 @@ async function cmdStats(args) {
|
|
|
51055
51119
|
}
|
|
51056
51120
|
console.log(`device: ${view.device.id}`);
|
|
51057
51121
|
console.log(`host: ${view.device.hostname ?? "(unknown)"} (${view.device.os_family ?? "?"})`);
|
|
51058
|
-
console.log(`
|
|
51122
|
+
console.log(`daemon: ${view.device.daemon_version ?? "(unknown)"}`);
|
|
51059
51123
|
console.log(
|
|
51060
|
-
`status: ${view.device.
|
|
51124
|
+
`status: ${view.device.daemon_status ?? "(unknown)"}${view.device.last_seen_at ? ` \xB7 last seen ${view.device.last_seen_at}` : ""}`
|
|
51061
51125
|
);
|
|
51062
51126
|
console.log(
|
|
51063
51127
|
`claim: ${view.status}${view.status === "unclaimed" ? ` (at ${view.claim_url})` : ""}`
|
|
@@ -51185,7 +51249,7 @@ async function main() {
|
|
|
51185
51249
|
}
|
|
51186
51250
|
case "_daemon-health": {
|
|
51187
51251
|
try {
|
|
51188
|
-
console.log(JSON.stringify(daemonHealth({
|
|
51252
|
+
console.log(JSON.stringify(daemonHealth({ myDaemonVersion: DAEMON_VERSION3 })));
|
|
51189
51253
|
} catch (e) {
|
|
51190
51254
|
console.log(JSON.stringify({ decision: "spawn", error: e.message }));
|
|
51191
51255
|
}
|