modelstat 0.0.45 → 0.0.47
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/dist/cli.mjs +1090 -292
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
- package/vendor/tray-mac/Sources/ModelstatTray/main.swift +145 -21
package/dist/cli.mjs
CHANGED
|
@@ -50,9 +50,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
50
50
|
));
|
|
51
51
|
|
|
52
52
|
// ../../packages/parsers/src/types.ts
|
|
53
|
+
var PARSER_EVENT_CHUNK;
|
|
53
54
|
var init_types = __esm({
|
|
54
55
|
"../../packages/parsers/src/types.ts"() {
|
|
55
56
|
"use strict";
|
|
57
|
+
PARSER_EVENT_CHUNK = 256;
|
|
56
58
|
}
|
|
57
59
|
});
|
|
58
60
|
|
|
@@ -76,7 +78,7 @@ var init_git = __esm({
|
|
|
76
78
|
});
|
|
77
79
|
|
|
78
80
|
// ../../packages/core/src/enums.ts
|
|
79
|
-
var TOOLS, PROVIDERS, IDENTITY_OWNER_SCOPES, INSTALL_METHODS, OS_FAMILIES, EVENT_KINDS, COMPANION_PHASES, CLASSIFICATION_CONFIDENCE;
|
|
81
|
+
var TOOLS, PROVIDERS, IDENTITY_OWNER_SCOPES, INSTALL_METHODS, OS_FAMILIES, EVENT_KINDS, TOOL_CALL_STATUSES, COMPANION_PHASES, CLASSIFICATION_CONFIDENCE;
|
|
80
82
|
var init_enums = __esm({
|
|
81
83
|
"../../packages/core/src/enums.ts"() {
|
|
82
84
|
"use strict";
|
|
@@ -160,6 +162,13 @@ var init_enums = __esm({
|
|
|
160
162
|
"tool_result",
|
|
161
163
|
"summary"
|
|
162
164
|
];
|
|
165
|
+
TOOL_CALL_STATUSES = [
|
|
166
|
+
"success",
|
|
167
|
+
"error",
|
|
168
|
+
"denied",
|
|
169
|
+
"timeout",
|
|
170
|
+
"unknown"
|
|
171
|
+
];
|
|
163
172
|
COMPANION_PHASES = [
|
|
164
173
|
"starting",
|
|
165
174
|
"discovering",
|
|
@@ -4302,7 +4311,7 @@ var init_zod = __esm({
|
|
|
4302
4311
|
});
|
|
4303
4312
|
|
|
4304
4313
|
// ../../packages/core/src/schemas.ts
|
|
4305
|
-
var TokenUsage, GitContext, RawEvent, RedactionReport, TaxonomyHintRooted, Segment, IngestBatch, HeartbeatPayload, DeviceEnrollment, DeviceSelfRegister, DeviceClaimRequest, ProcessingMetadata, RedactionPolicy, DetectedInstallation, DetectedIdentity, DiscoveryReport, ClassificationConfidenceEnum;
|
|
4314
|
+
var TokenUsage, GitContext, RawEvent, RedactionReport, TaxonomyHintRooted, Segment, ToolCallWire, IngestBatch, HeartbeatPayload, DeviceEnrollment, DeviceSelfRegister, DeviceClaimRequest, ProcessingMetadata, RedactionPolicy, DetectedInstallation, DetectedIdentity, DiscoveryReport, ClassificationConfidenceEnum;
|
|
4306
4315
|
var init_schemas = __esm({
|
|
4307
4316
|
"../../packages/core/src/schemas.ts"() {
|
|
4308
4317
|
"use strict";
|
|
@@ -4407,6 +4416,49 @@ var init_schemas = __esm({
|
|
|
4407
4416
|
* Reserved for server-side similarity / clustering. */
|
|
4408
4417
|
abstract_embedding: external_exports.array(external_exports.number()).length(384).optional()
|
|
4409
4418
|
});
|
|
4419
|
+
ToolCallWire = external_exports.object({
|
|
4420
|
+
/** tool_use block `id` / codex `call_id`; parsers fall back to a
|
|
4421
|
+
* deterministic `tc_<djb2-base36>` of `${source_event_id}|${call_index}`
|
|
4422
|
+
* when the source line carries no id. */
|
|
4423
|
+
external_call_id: external_exports.string().max(120),
|
|
4424
|
+
/** Tool-local session id — same id space as RawEvent.session_id. */
|
|
4425
|
+
session_id: external_exports.string().max(120),
|
|
4426
|
+
/** The RawEvent that contained the tool_use (dedupe/replay anchor). */
|
|
4427
|
+
source_event_id: external_exports.string(),
|
|
4428
|
+
/** Segment containing source_event_id — filled by the companion at
|
|
4429
|
+
* batch-build time when known, else null. */
|
|
4430
|
+
segment_id: external_exports.string().max(64).nullable().default(null),
|
|
4431
|
+
/** The agent that made the call (TOOLS enum — "tool" in legacy naming). */
|
|
4432
|
+
agent: external_exports.enum(TOOLS),
|
|
4433
|
+
/** `builtin` or `mcp:<server>`. */
|
|
4434
|
+
server: external_exports.string().max(120),
|
|
4435
|
+
/** Bare tool name (`Bash`, `create_pr`) — normalised vendor identifier. */
|
|
4436
|
+
name: external_exports.string().max(120),
|
|
4437
|
+
turn_index: external_exports.number().int().nonnegative().nullable(),
|
|
4438
|
+
/** Ordinal of the call within its source event (0-based). */
|
|
4439
|
+
call_index: external_exports.number().int().nonnegative(),
|
|
4440
|
+
/** ts of the line carrying the tool_use. */
|
|
4441
|
+
started_at: external_exports.string().datetime({ offset: true }),
|
|
4442
|
+
/** ts of the line carrying the matching tool_result; null if unmatched. */
|
|
4443
|
+
ended_at: external_exports.string().datetime({ offset: true }).nullable(),
|
|
4444
|
+
status: external_exports.enum(TOOL_CALL_STATUSES),
|
|
4445
|
+
/** Hex sha256 of JSON.stringify(input); `""` when the call had no input. */
|
|
4446
|
+
args_hash: external_exports.string().max(64),
|
|
4447
|
+
/** Sha256 of the sorted top-level arg key names joined by `,`; the
|
|
4448
|
+
* literal `none` when input is not a non-empty object. */
|
|
4449
|
+
signature_hash: external_exports.string().max(64),
|
|
4450
|
+
/** UTF-8 byte length of JSON.stringify(input); 0 if none. */
|
|
4451
|
+
args_bytes: external_exports.number().int().nonnegative(),
|
|
4452
|
+
/** UTF-8 byte length of JSON.stringify(tool_result content); 0 if
|
|
4453
|
+
* unmatched/empty. */
|
|
4454
|
+
result_bytes: external_exports.number().int().nonnegative(),
|
|
4455
|
+
/** Model of the assistant message that issued the call. `<synthetic>`
|
|
4456
|
+
* kept verbatim per the PR #12 attribution rules. */
|
|
4457
|
+
model: external_exports.string().max(120).nullable(),
|
|
4458
|
+
/** ONLY for shell-ish tools: command verbs from the fixed allowlist
|
|
4459
|
+
* (@modelstat/parsers/shell-families). Never raw command text. */
|
|
4460
|
+
command_families: external_exports.array(external_exports.string().max(40)).max(3).default([])
|
|
4461
|
+
});
|
|
4410
4462
|
IngestBatch = external_exports.object({
|
|
4411
4463
|
batch_id: external_exports.string(),
|
|
4412
4464
|
// ULID
|
|
@@ -4414,6 +4466,10 @@ var init_schemas = __esm({
|
|
|
4414
4466
|
agent_version: external_exports.string().max(40),
|
|
4415
4467
|
events: external_exports.array(RawEvent).max(1e4),
|
|
4416
4468
|
segments: external_exports.array(Segment).max(2e3).default([]),
|
|
4469
|
+
/** Per-call tool invocations (additive — old agents omit it, old
|
|
4470
|
+
* servers ignore it). See ToolCallWire for the privacy contract:
|
|
4471
|
+
* hashes / byte sizes / allowlisted verbs only, never payloads. */
|
|
4472
|
+
tool_calls: external_exports.array(ToolCallWire).max(2e4).default([]),
|
|
4417
4473
|
/** Optional per-session metadata hint: which installation produced them, etc. */
|
|
4418
4474
|
session_installs: external_exports.record(
|
|
4419
4475
|
external_exports.string(),
|
|
@@ -4682,22 +4738,222 @@ var init_src = __esm({
|
|
|
4682
4738
|
}
|
|
4683
4739
|
});
|
|
4684
4740
|
|
|
4685
|
-
// ../../packages/parsers/src/
|
|
4741
|
+
// ../../packages/parsers/src/shell-families/index.ts
|
|
4742
|
+
function extractCommandFamilies(command) {
|
|
4743
|
+
const families = [];
|
|
4744
|
+
for (const part of splitCommandParts(command)) {
|
|
4745
|
+
const verb = leadingVerb(part);
|
|
4746
|
+
if (!verb || !ALLOWLIST.has(verb)) continue;
|
|
4747
|
+
if (!families.includes(verb)) families.push(verb);
|
|
4748
|
+
if (families.length >= MAX_COMMAND_FAMILIES) break;
|
|
4749
|
+
}
|
|
4750
|
+
return families;
|
|
4751
|
+
}
|
|
4752
|
+
function splitCommandParts(command) {
|
|
4753
|
+
const parts = [];
|
|
4754
|
+
let current = "";
|
|
4755
|
+
let inSingle = false;
|
|
4756
|
+
let inDouble = false;
|
|
4757
|
+
for (let i = 0; i < command.length; i++) {
|
|
4758
|
+
const ch = command[i];
|
|
4759
|
+
if (!inSingle && ch === "\\") {
|
|
4760
|
+
current += ch + (command[i + 1] ?? "");
|
|
4761
|
+
i++;
|
|
4762
|
+
continue;
|
|
4763
|
+
}
|
|
4764
|
+
if (ch === "'" && !inDouble) {
|
|
4765
|
+
inSingle = !inSingle;
|
|
4766
|
+
current += ch;
|
|
4767
|
+
continue;
|
|
4768
|
+
}
|
|
4769
|
+
if (ch === '"' && !inSingle) {
|
|
4770
|
+
inDouble = !inDouble;
|
|
4771
|
+
current += ch;
|
|
4772
|
+
continue;
|
|
4773
|
+
}
|
|
4774
|
+
if (!inSingle && !inDouble) {
|
|
4775
|
+
if (ch === ";" || ch === "|" || ch === "\n") {
|
|
4776
|
+
parts.push(current);
|
|
4777
|
+
current = "";
|
|
4778
|
+
continue;
|
|
4779
|
+
}
|
|
4780
|
+
if (ch === "&" && command[i + 1] === "&") {
|
|
4781
|
+
parts.push(current);
|
|
4782
|
+
current = "";
|
|
4783
|
+
i++;
|
|
4784
|
+
continue;
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
current += ch;
|
|
4788
|
+
}
|
|
4789
|
+
parts.push(current);
|
|
4790
|
+
return parts;
|
|
4791
|
+
}
|
|
4792
|
+
function leadingVerb(part) {
|
|
4793
|
+
const tokens = part.trim().split(/\s+/);
|
|
4794
|
+
let i = 0;
|
|
4795
|
+
while (i < tokens.length) {
|
|
4796
|
+
const tok = stripQuotes(tokens[i] ?? "");
|
|
4797
|
+
if (tok === "") {
|
|
4798
|
+
i++;
|
|
4799
|
+
continue;
|
|
4800
|
+
}
|
|
4801
|
+
if (VAR_ASSIGNMENT.test(tok)) {
|
|
4802
|
+
i++;
|
|
4803
|
+
continue;
|
|
4804
|
+
}
|
|
4805
|
+
if (WRAPPERS.has(tok)) {
|
|
4806
|
+
i++;
|
|
4807
|
+
while (i < tokens.length && (tokens[i] ?? "").startsWith("-")) i++;
|
|
4808
|
+
continue;
|
|
4809
|
+
}
|
|
4810
|
+
const base = tok.split("/").pop() ?? tok;
|
|
4811
|
+
return base === "" ? null : base;
|
|
4812
|
+
}
|
|
4813
|
+
return null;
|
|
4814
|
+
}
|
|
4815
|
+
function stripQuotes(token) {
|
|
4816
|
+
if (token.length >= 2) {
|
|
4817
|
+
const first = token[0];
|
|
4818
|
+
const last = token[token.length - 1];
|
|
4819
|
+
if (first === "'" && last === "'" || first === '"' && last === '"') {
|
|
4820
|
+
return token.slice(1, -1);
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
return token;
|
|
4824
|
+
}
|
|
4825
|
+
var SHELL_FAMILY_ALLOWLIST, MAX_COMMAND_FAMILIES, ALLOWLIST, WRAPPERS, VAR_ASSIGNMENT;
|
|
4826
|
+
var init_shell_families = __esm({
|
|
4827
|
+
"../../packages/parsers/src/shell-families/index.ts"() {
|
|
4828
|
+
"use strict";
|
|
4829
|
+
SHELL_FAMILY_ALLOWLIST = [
|
|
4830
|
+
"git",
|
|
4831
|
+
"npm",
|
|
4832
|
+
"pnpm",
|
|
4833
|
+
"npx",
|
|
4834
|
+
"yarn",
|
|
4835
|
+
"node",
|
|
4836
|
+
"python",
|
|
4837
|
+
"python3",
|
|
4838
|
+
"pytest",
|
|
4839
|
+
"pip",
|
|
4840
|
+
"pip3",
|
|
4841
|
+
"cargo",
|
|
4842
|
+
"rustc",
|
|
4843
|
+
"go",
|
|
4844
|
+
"make",
|
|
4845
|
+
"cmake",
|
|
4846
|
+
"docker",
|
|
4847
|
+
"docker-compose",
|
|
4848
|
+
"kubectl",
|
|
4849
|
+
"helm",
|
|
4850
|
+
"terraform",
|
|
4851
|
+
"gh",
|
|
4852
|
+
"aws",
|
|
4853
|
+
"gcloud",
|
|
4854
|
+
"az",
|
|
4855
|
+
"curl",
|
|
4856
|
+
"wget",
|
|
4857
|
+
"rg",
|
|
4858
|
+
"grep",
|
|
4859
|
+
"find",
|
|
4860
|
+
"sed",
|
|
4861
|
+
"awk",
|
|
4862
|
+
"jq",
|
|
4863
|
+
"psql",
|
|
4864
|
+
"mysql",
|
|
4865
|
+
"redis-cli",
|
|
4866
|
+
"brew",
|
|
4867
|
+
"apt",
|
|
4868
|
+
"tsx",
|
|
4869
|
+
"vitest",
|
|
4870
|
+
"jest",
|
|
4871
|
+
"playwright",
|
|
4872
|
+
"ruby",
|
|
4873
|
+
"bundle",
|
|
4874
|
+
"mvn",
|
|
4875
|
+
"gradle",
|
|
4876
|
+
"ls",
|
|
4877
|
+
"cat"
|
|
4878
|
+
];
|
|
4879
|
+
MAX_COMMAND_FAMILIES = 3;
|
|
4880
|
+
ALLOWLIST = new Set(SHELL_FAMILY_ALLOWLIST);
|
|
4881
|
+
WRAPPERS = /* @__PURE__ */ new Set(["sudo", "env", "time", "nice"]);
|
|
4882
|
+
VAR_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
4883
|
+
}
|
|
4884
|
+
});
|
|
4885
|
+
|
|
4886
|
+
// ../../packages/parsers/src/tool-hash/index.ts
|
|
4686
4887
|
import { createHash } from "crypto";
|
|
4888
|
+
function hashArgs(input) {
|
|
4889
|
+
if (input === void 0 || input === null) {
|
|
4890
|
+
return { args_hash: "", signature_hash: SIGNATURE_NONE, args_bytes: 0 };
|
|
4891
|
+
}
|
|
4892
|
+
const json = JSON.stringify(input);
|
|
4893
|
+
if (json === void 0) {
|
|
4894
|
+
return { args_hash: "", signature_hash: SIGNATURE_NONE, args_bytes: 0 };
|
|
4895
|
+
}
|
|
4896
|
+
const argsHash = createHash("sha256").update(json).digest("hex");
|
|
4897
|
+
let signatureHash = SIGNATURE_NONE;
|
|
4898
|
+
if (typeof input === "object" && !Array.isArray(input)) {
|
|
4899
|
+
const keys = Object.keys(input).sort();
|
|
4900
|
+
if (keys.length > 0) {
|
|
4901
|
+
signatureHash = createHash("sha256").update(keys.join(",")).digest("hex");
|
|
4902
|
+
}
|
|
4903
|
+
}
|
|
4904
|
+
return {
|
|
4905
|
+
args_hash: argsHash,
|
|
4906
|
+
signature_hash: signatureHash,
|
|
4907
|
+
args_bytes: Buffer.byteLength(json, "utf8")
|
|
4908
|
+
};
|
|
4909
|
+
}
|
|
4910
|
+
function jsonBytes(value) {
|
|
4911
|
+
if (value === void 0 || value === null || value === "") return 0;
|
|
4912
|
+
const json = JSON.stringify(value);
|
|
4913
|
+
return json === void 0 ? 0 : Buffer.byteLength(json, "utf8");
|
|
4914
|
+
}
|
|
4915
|
+
function normalizeToolName(raw) {
|
|
4916
|
+
const cleaned = raw.normalize("NFC").trim().replace(UUID_RE, "<dyn>").replace(HEX_TAIL_RE, "<dyn>");
|
|
4917
|
+
return cleaned.slice(0, 120);
|
|
4918
|
+
}
|
|
4919
|
+
function splitObservedToolName(observed) {
|
|
4920
|
+
const m = /^mcp__([^_].*?)__(.+)$/.exec(observed.trim());
|
|
4921
|
+
if (m?.[1] && m[2]) {
|
|
4922
|
+
return {
|
|
4923
|
+
server: `mcp:${normalizeToolName(m[1]).slice(0, 116)}`,
|
|
4924
|
+
name: normalizeToolName(m[2])
|
|
4925
|
+
};
|
|
4926
|
+
}
|
|
4927
|
+
return { server: "builtin", name: normalizeToolName(observed) };
|
|
4928
|
+
}
|
|
4929
|
+
function toolIdentity(server, name) {
|
|
4930
|
+
return server === "builtin" ? name : `${server}/${name}`;
|
|
4931
|
+
}
|
|
4932
|
+
function fallbackCallId(sourceEventId2, callIndex) {
|
|
4933
|
+
const s = `${sourceEventId2}|${callIndex}`;
|
|
4934
|
+
let h = 5381n;
|
|
4935
|
+
for (let i = 0; i < s.length; i++) {
|
|
4936
|
+
h = h * 33n ^ BigInt(s.charCodeAt(i));
|
|
4937
|
+
h &= 0xffffffffffffffffn;
|
|
4938
|
+
}
|
|
4939
|
+
return `tc_${h.toString(36)}`;
|
|
4940
|
+
}
|
|
4941
|
+
var SIGNATURE_NONE, UUID_RE, HEX_TAIL_RE;
|
|
4942
|
+
var init_tool_hash = __esm({
|
|
4943
|
+
"../../packages/parsers/src/tool-hash/index.ts"() {
|
|
4944
|
+
"use strict";
|
|
4945
|
+
SIGNATURE_NONE = "none";
|
|
4946
|
+
UUID_RE = /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g;
|
|
4947
|
+
HEX_TAIL_RE = /[0-9a-fA-F]{8,}(?=$|[^0-9a-zA-Z])/g;
|
|
4948
|
+
}
|
|
4949
|
+
});
|
|
4950
|
+
|
|
4951
|
+
// ../../packages/parsers/src/claude-code/index.ts
|
|
4952
|
+
import { createHash as createHash2 } from "crypto";
|
|
4687
4953
|
import { createReadStream, existsSync as existsSync2, readdirSync } from "fs";
|
|
4688
4954
|
import { stat } from "fs/promises";
|
|
4689
4955
|
import { dirname as dirname2, join } from "path";
|
|
4690
4956
|
import { createInterface } from "readline";
|
|
4691
|
-
function countToolCalls(content) {
|
|
4692
|
-
const counts = {};
|
|
4693
|
-
if (!Array.isArray(content)) return counts;
|
|
4694
|
-
for (const block of content) {
|
|
4695
|
-
if (block && block.type === "tool_use" && typeof block.name === "string" && block.name) {
|
|
4696
|
-
counts[block.name] = (counts[block.name] ?? 0) + 1;
|
|
4697
|
-
}
|
|
4698
|
-
}
|
|
4699
|
-
return counts;
|
|
4700
|
-
}
|
|
4701
4957
|
function extractExcerpt(content) {
|
|
4702
4958
|
if (!content) return void 0;
|
|
4703
4959
|
let text = "";
|
|
@@ -4720,8 +4976,56 @@ function extractExcerpt(content) {
|
|
|
4720
4976
|
const truncated = cleaned.slice(0, 320);
|
|
4721
4977
|
return truncated.length > 0 ? truncated : void 0;
|
|
4722
4978
|
}
|
|
4979
|
+
function commandFamiliesFor(server, name, input) {
|
|
4980
|
+
if (server !== "builtin" || !SHELL_TOOL_NAMES.has(name)) return [];
|
|
4981
|
+
if (typeof input !== "object" || input === null) return [];
|
|
4982
|
+
const command = input.command;
|
|
4983
|
+
return typeof command === "string" ? extractCommandFamilies(command) : [];
|
|
4984
|
+
}
|
|
4985
|
+
function buildToolCallDraft(opts) {
|
|
4986
|
+
const { server, name } = splitObservedToolName(opts.observedName);
|
|
4987
|
+
const hashes = hashArgs(opts.input);
|
|
4988
|
+
return {
|
|
4989
|
+
external_call_id: typeof opts.rawCallId === "string" && opts.rawCallId.trim() !== "" ? opts.rawCallId.trim().slice(0, 120) : fallbackCallId(opts.sourceEventId, opts.callIndex),
|
|
4990
|
+
session_id: opts.sessionId,
|
|
4991
|
+
source_event_id: opts.sourceEventId,
|
|
4992
|
+
agent: "claude_code",
|
|
4993
|
+
server,
|
|
4994
|
+
name,
|
|
4995
|
+
// This parser never derives a turn index (events above carry
|
|
4996
|
+
// turn_index: null too), so per-call records can't either.
|
|
4997
|
+
turn_index: null,
|
|
4998
|
+
call_index: opts.callIndex,
|
|
4999
|
+
started_at: opts.startedAt,
|
|
5000
|
+
ended_at: null,
|
|
5001
|
+
status: "unknown",
|
|
5002
|
+
args_hash: hashes.args_hash,
|
|
5003
|
+
signature_hash: hashes.signature_hash,
|
|
5004
|
+
args_bytes: hashes.args_bytes,
|
|
5005
|
+
result_bytes: 0,
|
|
5006
|
+
model: opts.model,
|
|
5007
|
+
command_families: commandFamiliesFor(server, name, opts.input)
|
|
5008
|
+
};
|
|
5009
|
+
}
|
|
4723
5010
|
async function parseClaudeCodeJsonl(ctx) {
|
|
4724
5011
|
const events = [];
|
|
5012
|
+
const toolCalls = [];
|
|
5013
|
+
const pendingByCallId = /* @__PURE__ */ new Map();
|
|
5014
|
+
let chunk = [];
|
|
5015
|
+
let emitted = 0;
|
|
5016
|
+
const emit = async (e) => {
|
|
5017
|
+
emitted += 1;
|
|
5018
|
+
if (!ctx.onEvents) {
|
|
5019
|
+
events.push(e);
|
|
5020
|
+
return;
|
|
5021
|
+
}
|
|
5022
|
+
chunk.push(e);
|
|
5023
|
+
if (chunk.length >= PARSER_EVENT_CHUNK) {
|
|
5024
|
+
const full = chunk;
|
|
5025
|
+
chunk = [];
|
|
5026
|
+
await ctx.onEvents(full);
|
|
5027
|
+
}
|
|
5028
|
+
};
|
|
4725
5029
|
let rawLines = 0;
|
|
4726
5030
|
let skipped = 0;
|
|
4727
5031
|
let bytePos = 0;
|
|
@@ -4809,7 +5113,33 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4809
5113
|
}
|
|
4810
5114
|
const slug = guessRepoSlugFromPath(cwd);
|
|
4811
5115
|
const excerpt = extractExcerpt(a.message?.content);
|
|
4812
|
-
|
|
5116
|
+
const aggregate = {};
|
|
5117
|
+
const blocks = Array.isArray(a.message?.content) ? a.message.content : [];
|
|
5118
|
+
let callIndex = 0;
|
|
5119
|
+
for (const block of blocks) {
|
|
5120
|
+
if (!block || block.type !== "tool_use") continue;
|
|
5121
|
+
const index = callIndex;
|
|
5122
|
+
callIndex += 1;
|
|
5123
|
+
const observed = typeof block.name === "string" ? block.name.trim() : "";
|
|
5124
|
+
if (!observed) continue;
|
|
5125
|
+
const draft = buildToolCallDraft({
|
|
5126
|
+
observedName: observed,
|
|
5127
|
+
rawCallId: block.id,
|
|
5128
|
+
input: block.input,
|
|
5129
|
+
sessionId,
|
|
5130
|
+
sourceEventId: eventId,
|
|
5131
|
+
callIndex: index,
|
|
5132
|
+
startedAt: a.timestamp,
|
|
5133
|
+
// Model verbatim from the issuing assistant message —
|
|
5134
|
+
// including "<synthetic>" (same rule as the event below).
|
|
5135
|
+
model: a.message?.model ?? null
|
|
5136
|
+
});
|
|
5137
|
+
const identity = toolIdentity(draft.server, draft.name);
|
|
5138
|
+
aggregate[identity] = (aggregate[identity] ?? 0) + 1;
|
|
5139
|
+
toolCalls.push(draft);
|
|
5140
|
+
if (typeof block.id === "string" && block.id) pendingByCallId.set(block.id, draft);
|
|
5141
|
+
}
|
|
5142
|
+
await emit({
|
|
4813
5143
|
source_event_id: eventId,
|
|
4814
5144
|
ts: a.timestamp,
|
|
4815
5145
|
kind: "assistant_message",
|
|
@@ -4835,7 +5165,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4835
5165
|
reasoning: 0
|
|
4836
5166
|
},
|
|
4837
5167
|
duration_ms: null,
|
|
4838
|
-
tool_calls:
|
|
5168
|
+
tool_calls: aggregate,
|
|
4839
5169
|
files_touched: [],
|
|
4840
5170
|
...excerpt ? { content_excerpt: excerpt } : {},
|
|
4841
5171
|
source_file: ctx.sourceFile,
|
|
@@ -4848,6 +5178,20 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4848
5178
|
});
|
|
4849
5179
|
} else if (obj.type === "user") {
|
|
4850
5180
|
const u = obj;
|
|
5181
|
+
const uContent = u.message?.content;
|
|
5182
|
+
if (Array.isArray(uContent)) {
|
|
5183
|
+
for (const block of uContent) {
|
|
5184
|
+
if (!block || block.type !== "tool_result") continue;
|
|
5185
|
+
const ref = block.tool_use_id;
|
|
5186
|
+
if (typeof ref !== "string") continue;
|
|
5187
|
+
const draft = pendingByCallId.get(ref);
|
|
5188
|
+
if (!draft) continue;
|
|
5189
|
+
pendingByCallId.delete(ref);
|
|
5190
|
+
draft.ended_at = u.timestamp;
|
|
5191
|
+
draft.status = block.is_error === true ? "error" : "success";
|
|
5192
|
+
draft.result_bytes = jsonBytes(block.content);
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
4851
5195
|
if (!u.uuid || !sessionId) {
|
|
4852
5196
|
skipped += 1;
|
|
4853
5197
|
continue;
|
|
@@ -4858,7 +5202,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4858
5202
|
continue;
|
|
4859
5203
|
}
|
|
4860
5204
|
const excerpt = extractExcerpt(u.message?.content);
|
|
4861
|
-
|
|
5205
|
+
await emit({
|
|
4862
5206
|
source_event_id: eventId,
|
|
4863
5207
|
ts: u.timestamp,
|
|
4864
5208
|
kind: "user_message",
|
|
@@ -4879,13 +5223,39 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4879
5223
|
source_byte_offset: offsetAtLineStart,
|
|
4880
5224
|
billing: "subscription"
|
|
4881
5225
|
});
|
|
5226
|
+
} else if (obj.type === "tool_use") {
|
|
5227
|
+
const t = obj;
|
|
5228
|
+
const observed = typeof t.name === "string" ? t.name.trim() : "";
|
|
5229
|
+
const ts = typeof t.timestamp === "string" ? t.timestamp : null;
|
|
5230
|
+
const sid = (typeof t.sessionId === "string" ? t.sessionId : null) ?? sessionId;
|
|
5231
|
+
if (!observed || !ts || !sid) {
|
|
5232
|
+
skipped += 1;
|
|
5233
|
+
continue;
|
|
5234
|
+
}
|
|
5235
|
+
const draft = buildToolCallDraft({
|
|
5236
|
+
observedName: observed,
|
|
5237
|
+
rawCallId: t.id,
|
|
5238
|
+
input: t.input,
|
|
5239
|
+
sessionId: sid,
|
|
5240
|
+
sourceEventId: sourceEventId(ctx.deviceId, ctx.sourceFile, offsetAtLineStart),
|
|
5241
|
+
callIndex: 0,
|
|
5242
|
+
startedAt: ts,
|
|
5243
|
+
// No issuing assistant message on this line — attribute to the
|
|
5244
|
+
// session's last real model, same as user_message attribution
|
|
5245
|
+
// (lastModel never holds "<synthetic>", per the rule above).
|
|
5246
|
+
model: lastModel
|
|
5247
|
+
});
|
|
5248
|
+
toolCalls.push(draft);
|
|
5249
|
+
if (typeof t.id === "string" && t.id) pendingByCallId.set(t.id, draft);
|
|
4882
5250
|
} else {
|
|
4883
5251
|
skipped += 1;
|
|
4884
5252
|
}
|
|
4885
5253
|
}
|
|
5254
|
+
if (ctx.onEvents && chunk.length > 0) await ctx.onEvents(chunk);
|
|
4886
5255
|
return {
|
|
4887
5256
|
events,
|
|
4888
|
-
|
|
5257
|
+
toolCalls,
|
|
5258
|
+
stats: { rawLines, emittedEvents: emitted, skipped },
|
|
4889
5259
|
sourceFile: ctx.sourceFile
|
|
4890
5260
|
};
|
|
4891
5261
|
}
|
|
@@ -4899,15 +5269,26 @@ async function quickChecksum(path5) {
|
|
|
4899
5269
|
start: Math.max(0, st.size - 4096),
|
|
4900
5270
|
encoding: "utf8"
|
|
4901
5271
|
});
|
|
4902
|
-
const h =
|
|
5272
|
+
const h = createHash2("sha1");
|
|
4903
5273
|
for await (const chunk of stream) h.update(chunk);
|
|
4904
5274
|
return { size: st.size, mtime: st.mtimeMs, tailHash: h.digest("hex").slice(0, 16) };
|
|
4905
5275
|
}
|
|
5276
|
+
var SHELL_TOOL_NAMES;
|
|
4906
5277
|
var init_claude_code = __esm({
|
|
4907
5278
|
"../../packages/parsers/src/claude-code/index.ts"() {
|
|
4908
5279
|
"use strict";
|
|
4909
5280
|
init_src();
|
|
4910
5281
|
init_git();
|
|
5282
|
+
init_shell_families();
|
|
5283
|
+
init_tool_hash();
|
|
5284
|
+
init_types();
|
|
5285
|
+
SHELL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
5286
|
+
"Bash",
|
|
5287
|
+
"shell",
|
|
5288
|
+
"local_shell_call",
|
|
5289
|
+
"exec_command",
|
|
5290
|
+
"run_terminal_cmd"
|
|
5291
|
+
]);
|
|
4911
5292
|
}
|
|
4912
5293
|
});
|
|
4913
5294
|
|
|
@@ -4920,8 +5301,105 @@ function deriveSessionIdFromRolloutPath(path5) {
|
|
|
4920
5301
|
);
|
|
4921
5302
|
return m ? m[1] ?? null : null;
|
|
4922
5303
|
}
|
|
5304
|
+
function commandFieldToString(cmd) {
|
|
5305
|
+
if (typeof cmd === "string") return cmd || null;
|
|
5306
|
+
if (Array.isArray(cmd)) {
|
|
5307
|
+
const parts = cmd.filter((p) => typeof p === "string");
|
|
5308
|
+
if (parts.length === 0) return null;
|
|
5309
|
+
const head = (parts[0] ?? "").split("/").pop() ?? "";
|
|
5310
|
+
const flag = parts[1] ?? "";
|
|
5311
|
+
if (parts.length >= 3 && SHELL_WRAPPER_BINARIES.has(head) && /^-[a-z]*c[a-z]*$/i.test(flag)) {
|
|
5312
|
+
return parts.slice(2).join("\n");
|
|
5313
|
+
}
|
|
5314
|
+
return parts.join(" ");
|
|
5315
|
+
}
|
|
5316
|
+
return null;
|
|
5317
|
+
}
|
|
5318
|
+
function firstString(...values) {
|
|
5319
|
+
for (const v of values) {
|
|
5320
|
+
if (typeof v === "string" && v) return v;
|
|
5321
|
+
}
|
|
5322
|
+
return null;
|
|
5323
|
+
}
|
|
5324
|
+
function extractToolCallPayload(pt, p) {
|
|
5325
|
+
const callId = firstString(p.call_id, p.id);
|
|
5326
|
+
const failed = p.status === "failed";
|
|
5327
|
+
if (pt === "local_shell_call") {
|
|
5328
|
+
const action = p.action && typeof p.action === "object" ? p.action : null;
|
|
5329
|
+
const command = commandFieldToString(action?.command);
|
|
5330
|
+
return {
|
|
5331
|
+
callId,
|
|
5332
|
+
server: "builtin",
|
|
5333
|
+
name: "shell",
|
|
5334
|
+
input: action,
|
|
5335
|
+
commandFamilies: command ? extractCommandFamilies(command) : [],
|
|
5336
|
+
failed
|
|
5337
|
+
};
|
|
5338
|
+
}
|
|
5339
|
+
const observed = firstString(p.name, p.tool);
|
|
5340
|
+
if (!observed) return null;
|
|
5341
|
+
let input = pt === "custom_tool_call" ? p.input : p.arguments ?? p.input;
|
|
5342
|
+
if (typeof input === "string" && !input.trim()) input = void 0;
|
|
5343
|
+
if (pt !== "custom_tool_call" && typeof input === "string") {
|
|
5344
|
+
try {
|
|
5345
|
+
input = JSON.parse(input);
|
|
5346
|
+
} catch {
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
if (SHELL_TOOL_NAMES2.has(observed)) {
|
|
5350
|
+
const rec = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
5351
|
+
const command = commandFieldToString(
|
|
5352
|
+
rec?.command ?? rec?.cmd ?? (typeof input === "string" ? input : null)
|
|
5353
|
+
);
|
|
5354
|
+
return {
|
|
5355
|
+
callId,
|
|
5356
|
+
server: "builtin",
|
|
5357
|
+
name: "shell",
|
|
5358
|
+
input,
|
|
5359
|
+
commandFamilies: command ? extractCommandFamilies(command) : [],
|
|
5360
|
+
failed
|
|
5361
|
+
};
|
|
5362
|
+
}
|
|
5363
|
+
if (pt === "mcp_tool_call" && typeof p.server === "string" && p.server) {
|
|
5364
|
+
return {
|
|
5365
|
+
callId,
|
|
5366
|
+
// Mirror splitObservedToolName's cap: 116 + the `mcp:` prefix ≤ 120.
|
|
5367
|
+
server: `mcp:${normalizeToolName(p.server).slice(0, 116)}`,
|
|
5368
|
+
name: normalizeToolName(observed),
|
|
5369
|
+
input,
|
|
5370
|
+
commandFamilies: [],
|
|
5371
|
+
failed
|
|
5372
|
+
};
|
|
5373
|
+
}
|
|
5374
|
+
const { server, name } = splitObservedToolName(observed);
|
|
5375
|
+
return { callId, server, name, input, commandFamilies: [], failed };
|
|
5376
|
+
}
|
|
5377
|
+
function outputIndicatesError(p) {
|
|
5378
|
+
const out = p.output ?? p.result;
|
|
5379
|
+
if (out && typeof out === "object" && !Array.isArray(out)) {
|
|
5380
|
+
const o = out;
|
|
5381
|
+
if (o.success === false || o.is_error === true) return true;
|
|
5382
|
+
}
|
|
5383
|
+
return false;
|
|
5384
|
+
}
|
|
4923
5385
|
async function parseCodexRollout(ctx) {
|
|
4924
5386
|
const events = [];
|
|
5387
|
+
const toolCalls = [];
|
|
5388
|
+
let chunk = [];
|
|
5389
|
+
let emitted = 0;
|
|
5390
|
+
const emit = async (e) => {
|
|
5391
|
+
emitted += 1;
|
|
5392
|
+
if (!ctx.onEvents) {
|
|
5393
|
+
events.push(e);
|
|
5394
|
+
return;
|
|
5395
|
+
}
|
|
5396
|
+
chunk.push(e);
|
|
5397
|
+
if (chunk.length >= PARSER_EVENT_CHUNK) {
|
|
5398
|
+
const full = chunk;
|
|
5399
|
+
chunk = [];
|
|
5400
|
+
await ctx.onEvents(full);
|
|
5401
|
+
}
|
|
5402
|
+
};
|
|
4925
5403
|
let rawLines = 0;
|
|
4926
5404
|
let skipped = 0;
|
|
4927
5405
|
let bytePos = 0;
|
|
@@ -4935,6 +5413,9 @@ async function parseCodexRollout(ctx) {
|
|
|
4935
5413
|
let cwd = null;
|
|
4936
5414
|
let model = null;
|
|
4937
5415
|
let turnIndex = 0;
|
|
5416
|
+
let lastTs = null;
|
|
5417
|
+
const openCalls = /* @__PURE__ */ new Map();
|
|
5418
|
+
let pendingToolAggregate = {};
|
|
4938
5419
|
for await (const line of rl) {
|
|
4939
5420
|
const byteLen = Buffer.byteLength(line, "utf8") + 1;
|
|
4940
5421
|
const offsetAtLineStart = startOffset + bytePos;
|
|
@@ -4951,22 +5432,91 @@ async function parseCodexRollout(ctx) {
|
|
|
4951
5432
|
skipped += 1;
|
|
4952
5433
|
continue;
|
|
4953
5434
|
}
|
|
5435
|
+
const lineTs = obj.timestamp;
|
|
5436
|
+
if (typeof lineTs === "string" && lineTs) lastTs = lineTs;
|
|
4954
5437
|
if (obj.type === "session_meta") {
|
|
4955
5438
|
const m = obj;
|
|
4956
|
-
|
|
5439
|
+
const id = m.id ?? m.payload?.id ?? null;
|
|
5440
|
+
if (id && id !== sessionId) {
|
|
5441
|
+
sessionId = id;
|
|
5442
|
+
pendingToolAggregate = {};
|
|
5443
|
+
openCalls.clear();
|
|
5444
|
+
}
|
|
4957
5445
|
continue;
|
|
4958
5446
|
}
|
|
4959
5447
|
if (obj.type === "turn_context") {
|
|
4960
5448
|
const t = obj;
|
|
4961
|
-
cwd = t.cwd ?? cwd;
|
|
4962
|
-
model = t.model ?? model;
|
|
5449
|
+
cwd = t.cwd ?? t.payload?.cwd ?? cwd;
|
|
5450
|
+
model = t.model ?? t.payload?.model ?? model;
|
|
5451
|
+
continue;
|
|
5452
|
+
}
|
|
5453
|
+
if (obj.type === "response_item") {
|
|
5454
|
+
const r = obj;
|
|
5455
|
+
const payload = r.payload;
|
|
5456
|
+
const pt = payload && typeof payload.type === "string" ? payload.type : null;
|
|
5457
|
+
if (pt && TOOL_CALL_PAYLOAD_TYPES.has(pt) && sessionId) {
|
|
5458
|
+
const extracted = extractToolCallPayload(pt, payload);
|
|
5459
|
+
if (!extracted) {
|
|
5460
|
+
skipped += 1;
|
|
5461
|
+
continue;
|
|
5462
|
+
}
|
|
5463
|
+
const ts = r.timestamp ?? lastTs;
|
|
5464
|
+
if (!ts) {
|
|
5465
|
+
skipped += 1;
|
|
5466
|
+
continue;
|
|
5467
|
+
}
|
|
5468
|
+
const srcId = sourceEventId(ctx.deviceId, ctx.sourceFile, offsetAtLineStart);
|
|
5469
|
+
const { args_hash, signature_hash, args_bytes } = hashArgs(extracted.input);
|
|
5470
|
+
const draft = {
|
|
5471
|
+
external_call_id: (extracted.callId ?? fallbackCallId(srcId, 0)).slice(0, 120),
|
|
5472
|
+
session_id: sessionId,
|
|
5473
|
+
source_event_id: srcId,
|
|
5474
|
+
agent: "codex_cli",
|
|
5475
|
+
server: extracted.server,
|
|
5476
|
+
name: extracted.name,
|
|
5477
|
+
turn_index: turnIndex,
|
|
5478
|
+
// Each response_item line carries exactly one call.
|
|
5479
|
+
call_index: 0,
|
|
5480
|
+
started_at: ts,
|
|
5481
|
+
ended_at: null,
|
|
5482
|
+
status: extracted.failed ? "error" : "unknown",
|
|
5483
|
+
args_hash,
|
|
5484
|
+
signature_hash,
|
|
5485
|
+
args_bytes,
|
|
5486
|
+
result_bytes: 0,
|
|
5487
|
+
model,
|
|
5488
|
+
command_families: extracted.commandFamilies
|
|
5489
|
+
};
|
|
5490
|
+
toolCalls.push(draft);
|
|
5491
|
+
if (extracted.callId) openCalls.set(extracted.callId, draft);
|
|
5492
|
+
const identity = toolIdentity(extracted.server, extracted.name);
|
|
5493
|
+
pendingToolAggregate[identity] = (pendingToolAggregate[identity] ?? 0) + 1;
|
|
5494
|
+
continue;
|
|
5495
|
+
}
|
|
5496
|
+
if (pt && TOOL_CALL_OUTPUT_PAYLOAD_TYPES.has(pt)) {
|
|
5497
|
+
const p = payload;
|
|
5498
|
+
const callId = firstString(p.call_id, p.id);
|
|
5499
|
+
const open2 = callId ? openCalls.get(callId) : void 0;
|
|
5500
|
+
if (!callId || !open2) {
|
|
5501
|
+
skipped += 1;
|
|
5502
|
+
continue;
|
|
5503
|
+
}
|
|
5504
|
+
openCalls.delete(callId);
|
|
5505
|
+
open2.ended_at = r.timestamp ?? lastTs ?? open2.started_at;
|
|
5506
|
+
open2.result_bytes = jsonBytes(p.output ?? p.result);
|
|
5507
|
+
if (open2.status === "unknown") {
|
|
5508
|
+
open2.status = outputIndicatesError(p) ? "error" : "success";
|
|
5509
|
+
}
|
|
5510
|
+
continue;
|
|
5511
|
+
}
|
|
5512
|
+
skipped += 1;
|
|
4963
5513
|
continue;
|
|
4964
5514
|
}
|
|
4965
5515
|
if (obj.type === "event_msg") {
|
|
4966
5516
|
const m = obj;
|
|
4967
5517
|
const ts = m.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
4968
5518
|
const payload = m.payload;
|
|
4969
|
-
if (!payload
|
|
5519
|
+
if (!payload?.type) {
|
|
4970
5520
|
skipped += 1;
|
|
4971
5521
|
continue;
|
|
4972
5522
|
}
|
|
@@ -4977,7 +5527,7 @@ async function parseCodexRollout(ctx) {
|
|
|
4977
5527
|
continue;
|
|
4978
5528
|
}
|
|
4979
5529
|
const slug = guessRepoSlugFromPath(cwd);
|
|
4980
|
-
|
|
5530
|
+
await emit({
|
|
4981
5531
|
source_event_id: sourceEventId(ctx.deviceId, ctx.sourceFile, offsetAtLineStart),
|
|
4982
5532
|
ts,
|
|
4983
5533
|
kind: "assistant_message",
|
|
@@ -5003,11 +5553,12 @@ async function parseCodexRollout(ctx) {
|
|
|
5003
5553
|
reasoning: tk.reasoning_output_tokens ?? 0
|
|
5004
5554
|
},
|
|
5005
5555
|
duration_ms: null,
|
|
5006
|
-
tool_calls:
|
|
5556
|
+
tool_calls: pendingToolAggregate,
|
|
5007
5557
|
files_touched: [],
|
|
5008
5558
|
source_file: ctx.sourceFile,
|
|
5009
5559
|
source_byte_offset: offsetAtLineStart
|
|
5010
5560
|
});
|
|
5561
|
+
pendingToolAggregate = {};
|
|
5011
5562
|
turnIndex += 1;
|
|
5012
5563
|
continue;
|
|
5013
5564
|
}
|
|
@@ -5016,7 +5567,7 @@ async function parseCodexRollout(ctx) {
|
|
|
5016
5567
|
skipped += 1;
|
|
5017
5568
|
continue;
|
|
5018
5569
|
}
|
|
5019
|
-
|
|
5570
|
+
await emit({
|
|
5020
5571
|
source_event_id: sourceEventId(ctx.deviceId, ctx.sourceFile, offsetAtLineStart),
|
|
5021
5572
|
ts,
|
|
5022
5573
|
kind: "user_message",
|
|
@@ -5042,17 +5593,42 @@ async function parseCodexRollout(ctx) {
|
|
|
5042
5593
|
}
|
|
5043
5594
|
skipped += 1;
|
|
5044
5595
|
}
|
|
5596
|
+
if (ctx.onEvents && chunk.length > 0) await ctx.onEvents(chunk);
|
|
5045
5597
|
return {
|
|
5046
5598
|
events,
|
|
5047
|
-
|
|
5599
|
+
toolCalls,
|
|
5600
|
+
stats: { rawLines, emittedEvents: emitted, skipped },
|
|
5048
5601
|
sourceFile: ctx.sourceFile
|
|
5049
5602
|
};
|
|
5050
5603
|
}
|
|
5604
|
+
var TOOL_CALL_PAYLOAD_TYPES, TOOL_CALL_OUTPUT_PAYLOAD_TYPES, SHELL_TOOL_NAMES2, SHELL_WRAPPER_BINARIES;
|
|
5051
5605
|
var init_codex = __esm({
|
|
5052
5606
|
"../../packages/parsers/src/codex/index.ts"() {
|
|
5053
5607
|
"use strict";
|
|
5054
5608
|
init_src();
|
|
5055
5609
|
init_git();
|
|
5610
|
+
init_shell_families();
|
|
5611
|
+
init_tool_hash();
|
|
5612
|
+
init_types();
|
|
5613
|
+
TOOL_CALL_PAYLOAD_TYPES = /* @__PURE__ */ new Set([
|
|
5614
|
+
"function_call",
|
|
5615
|
+
"local_shell_call",
|
|
5616
|
+
"custom_tool_call",
|
|
5617
|
+
"mcp_tool_call"
|
|
5618
|
+
]);
|
|
5619
|
+
TOOL_CALL_OUTPUT_PAYLOAD_TYPES = /* @__PURE__ */ new Set([
|
|
5620
|
+
"function_call_output",
|
|
5621
|
+
"local_shell_call_output",
|
|
5622
|
+
"custom_tool_call_output",
|
|
5623
|
+
"mcp_tool_call_output"
|
|
5624
|
+
]);
|
|
5625
|
+
SHELL_TOOL_NAMES2 = /* @__PURE__ */ new Set([
|
|
5626
|
+
"shell",
|
|
5627
|
+
"local_shell_call",
|
|
5628
|
+
"exec_command",
|
|
5629
|
+
"run_terminal_cmd"
|
|
5630
|
+
]);
|
|
5631
|
+
SHELL_WRAPPER_BINARIES = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash", "fish"]);
|
|
5056
5632
|
}
|
|
5057
5633
|
});
|
|
5058
5634
|
|
|
@@ -7622,107 +8198,6 @@ var init_src2 = __esm({
|
|
|
7622
8198
|
}
|
|
7623
8199
|
});
|
|
7624
8200
|
|
|
7625
|
-
// src/identity.ts
|
|
7626
|
-
import {
|
|
7627
|
-
chmodSync,
|
|
7628
|
-
mkdirSync,
|
|
7629
|
-
readFileSync as readFileSync2,
|
|
7630
|
-
renameSync,
|
|
7631
|
-
writeFileSync,
|
|
7632
|
-
existsSync as existsSync4
|
|
7633
|
-
} from "fs";
|
|
7634
|
-
import { homedir as homedir2, hostname as osHostname } from "os";
|
|
7635
|
-
import { join as join3 } from "path";
|
|
7636
|
-
function ensureRoot() {
|
|
7637
|
-
mkdirSync(ROOT, { recursive: true, mode: 448 });
|
|
7638
|
-
}
|
|
7639
|
-
function writeAtomic(meta) {
|
|
7640
|
-
ensureRoot();
|
|
7641
|
-
const tmp = `${IDENTITY_FILE}.${process.pid}.tmp`;
|
|
7642
|
-
writeFileSync(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
7643
|
-
renameSync(tmp, IDENTITY_FILE);
|
|
7644
|
-
try {
|
|
7645
|
-
chmodSync(IDENTITY_FILE, 384);
|
|
7646
|
-
} catch {
|
|
7647
|
-
}
|
|
7648
|
-
}
|
|
7649
|
-
function identityPath() {
|
|
7650
|
-
return IDENTITY_FILE;
|
|
7651
|
-
}
|
|
7652
|
-
function hasIdentityFile() {
|
|
7653
|
-
return existsSync4(IDENTITY_FILE);
|
|
7654
|
-
}
|
|
7655
|
-
function parseFile() {
|
|
7656
|
-
try {
|
|
7657
|
-
const raw = readFileSync2(IDENTITY_FILE, "utf8");
|
|
7658
|
-
const obj = JSON.parse(raw);
|
|
7659
|
-
if (!obj.deviceUuid || !obj.deviceId || !obj.bearerToken) {
|
|
7660
|
-
return null;
|
|
7661
|
-
}
|
|
7662
|
-
return {
|
|
7663
|
-
deviceUuid: obj.deviceUuid,
|
|
7664
|
-
deviceId: obj.deviceId,
|
|
7665
|
-
bearerToken: obj.bearerToken,
|
|
7666
|
-
claimCode: obj.claimCode ?? null,
|
|
7667
|
-
claimUrl: obj.claimUrl ?? null,
|
|
7668
|
-
hostname: obj.hostname ?? osHostname(),
|
|
7669
|
-
createdAt: obj.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7670
|
-
userEmail: obj.userEmail ?? null,
|
|
7671
|
-
defaultOrgId: obj.defaultOrgId ?? null
|
|
7672
|
-
};
|
|
7673
|
-
} catch {
|
|
7674
|
-
return null;
|
|
7675
|
-
}
|
|
7676
|
-
}
|
|
7677
|
-
function loadIdentity(migrateFromConf2) {
|
|
7678
|
-
const fromFile = parseFile();
|
|
7679
|
-
if (fromFile) return fromFile;
|
|
7680
|
-
if (!migrateFromConf2) return null;
|
|
7681
|
-
const legacy = migrateFromConf2();
|
|
7682
|
-
if (!legacy) return null;
|
|
7683
|
-
if (!legacy.deviceUuid || !legacy.deviceId || !legacy.bearerToken) {
|
|
7684
|
-
return null;
|
|
7685
|
-
}
|
|
7686
|
-
const migrated = {
|
|
7687
|
-
deviceUuid: legacy.deviceUuid,
|
|
7688
|
-
deviceId: legacy.deviceId,
|
|
7689
|
-
bearerToken: legacy.bearerToken,
|
|
7690
|
-
claimCode: legacy.claimCode ?? null,
|
|
7691
|
-
claimUrl: legacy.claimUrl ?? null,
|
|
7692
|
-
hostname: osHostname(),
|
|
7693
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7694
|
-
userEmail: legacy.userEmail ?? null,
|
|
7695
|
-
defaultOrgId: legacy.defaultOrgId ?? null
|
|
7696
|
-
};
|
|
7697
|
-
writeAtomic(migrated);
|
|
7698
|
-
return migrated;
|
|
7699
|
-
}
|
|
7700
|
-
function saveIdentity(meta) {
|
|
7701
|
-
writeAtomic(meta);
|
|
7702
|
-
}
|
|
7703
|
-
function backupIdentity() {
|
|
7704
|
-
if (!existsSync4(IDENTITY_FILE)) return null;
|
|
7705
|
-
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7706
|
-
const dest = `${IDENTITY_FILE}.bak-${stamp}`;
|
|
7707
|
-
renameSync(IDENTITY_FILE, dest);
|
|
7708
|
-
return dest;
|
|
7709
|
-
}
|
|
7710
|
-
function updateIdentity(patch) {
|
|
7711
|
-
const current = parseFile();
|
|
7712
|
-
if (!current) return null;
|
|
7713
|
-
const merged = { ...current, ...patch };
|
|
7714
|
-
writeAtomic(merged);
|
|
7715
|
-
return merged;
|
|
7716
|
-
}
|
|
7717
|
-
var ROOT, IDENTITY_FILE;
|
|
7718
|
-
var init_identity = __esm({
|
|
7719
|
-
"src/identity.ts"() {
|
|
7720
|
-
"use strict";
|
|
7721
|
-
ROOT = join3(homedir2(), ".modelstat");
|
|
7722
|
-
IDENTITY_FILE = join3(ROOT, "identity.json");
|
|
7723
|
-
}
|
|
7724
|
-
});
|
|
7725
|
-
|
|
7726
8201
|
// ../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/core/symbols.js
|
|
7727
8202
|
var require_symbols = __commonJS({
|
|
7728
8203
|
"../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/core/symbols.js"(exports, module) {
|
|
@@ -32614,6 +33089,9 @@ function classifyStatus(status2, attempt) {
|
|
|
32614
33089
|
}
|
|
32615
33090
|
return { type: "drop", reason: `http_${status2}` };
|
|
32616
33091
|
}
|
|
33092
|
+
function wellFormedStringify(value) {
|
|
33093
|
+
return JSON.stringify(value, (_key, v) => typeof v === "string" ? v.toWellFormed() : v);
|
|
33094
|
+
}
|
|
32617
33095
|
function sleep(ms) {
|
|
32618
33096
|
return new Promise((r) => {
|
|
32619
33097
|
setTimeout(r, ms);
|
|
@@ -32652,7 +33130,10 @@ var init_http = __esm({
|
|
|
32652
33130
|
"content-type": "application/json",
|
|
32653
33131
|
authorization: `Bearer ${token}`
|
|
32654
33132
|
},
|
|
32655
|
-
|
|
33133
|
+
// wellFormedStringify, not JSON.stringify: a truncated-emoji
|
|
33134
|
+
// lone surrogate in any excerpt 400s the whole batch on the
|
|
33135
|
+
// serde_json side. See the helper's doc comment.
|
|
33136
|
+
body: wellFormedStringify(batch)
|
|
32656
33137
|
});
|
|
32657
33138
|
} catch (err) {
|
|
32658
33139
|
const detail = describeErrorWithCause(err);
|
|
@@ -33301,15 +33782,15 @@ function envPaths(name, { suffix = "nodejs" } = {}) {
|
|
|
33301
33782
|
}
|
|
33302
33783
|
return linux(name);
|
|
33303
33784
|
}
|
|
33304
|
-
var
|
|
33785
|
+
var homedir2, tmpdir, env, macos, windows, linux;
|
|
33305
33786
|
var init_env_paths = __esm({
|
|
33306
33787
|
"../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js"() {
|
|
33307
33788
|
"use strict";
|
|
33308
|
-
|
|
33789
|
+
homedir2 = os.homedir();
|
|
33309
33790
|
tmpdir = os.tmpdir();
|
|
33310
33791
|
({ env } = process2);
|
|
33311
33792
|
macos = (name) => {
|
|
33312
|
-
const library = path.join(
|
|
33793
|
+
const library = path.join(homedir2, "Library");
|
|
33313
33794
|
return {
|
|
33314
33795
|
data: path.join(library, "Application Support", name),
|
|
33315
33796
|
config: path.join(library, "Preferences", name),
|
|
@@ -33319,8 +33800,8 @@ var init_env_paths = __esm({
|
|
|
33319
33800
|
};
|
|
33320
33801
|
};
|
|
33321
33802
|
windows = (name) => {
|
|
33322
|
-
const appData = env.APPDATA || path.join(
|
|
33323
|
-
const localAppData = env.LOCALAPPDATA || path.join(
|
|
33803
|
+
const appData = env.APPDATA || path.join(homedir2, "AppData", "Roaming");
|
|
33804
|
+
const localAppData = env.LOCALAPPDATA || path.join(homedir2, "AppData", "Local");
|
|
33324
33805
|
return {
|
|
33325
33806
|
// Data/config/cache/log are invented by me as Windows isn't opinionated about this
|
|
33326
33807
|
data: path.join(localAppData, name, "Data"),
|
|
@@ -33331,13 +33812,13 @@ var init_env_paths = __esm({
|
|
|
33331
33812
|
};
|
|
33332
33813
|
};
|
|
33333
33814
|
linux = (name) => {
|
|
33334
|
-
const username = path.basename(
|
|
33815
|
+
const username = path.basename(homedir2);
|
|
33335
33816
|
return {
|
|
33336
|
-
data: path.join(env.XDG_DATA_HOME || path.join(
|
|
33337
|
-
config: path.join(env.XDG_CONFIG_HOME || path.join(
|
|
33338
|
-
cache: path.join(env.XDG_CACHE_HOME || path.join(
|
|
33817
|
+
data: path.join(env.XDG_DATA_HOME || path.join(homedir2, ".local", "share"), name),
|
|
33818
|
+
config: path.join(env.XDG_CONFIG_HOME || path.join(homedir2, ".config"), name),
|
|
33819
|
+
cache: path.join(env.XDG_CACHE_HOME || path.join(homedir2, ".cache"), name),
|
|
33339
33820
|
// https://wiki.debian.org/XDGBaseDirectorySpecification#state
|
|
33340
|
-
log: path.join(env.XDG_STATE_HOME || path.join(
|
|
33821
|
+
log: path.join(env.XDG_STATE_HOME || path.join(homedir2, ".local", "state"), name),
|
|
33341
33822
|
temp: path.join(tmpdir, username, name)
|
|
33342
33823
|
};
|
|
33343
33824
|
};
|
|
@@ -33778,9 +34259,9 @@ import { once } from "events";
|
|
|
33778
34259
|
import { createWriteStream } from "fs";
|
|
33779
34260
|
import path3 from "path";
|
|
33780
34261
|
import { Readable } from "stream";
|
|
33781
|
-
function
|
|
34262
|
+
function writeFileSync(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
|
|
33782
34263
|
if (isString(options))
|
|
33783
|
-
return
|
|
34264
|
+
return writeFileSync(filePath, data, { encoding: options });
|
|
33784
34265
|
const timeout = options.timeout ?? DEFAULT_TIMEOUT_SYNC;
|
|
33785
34266
|
const retryOptions = { timeout };
|
|
33786
34267
|
let tempDisposer = null;
|
|
@@ -43903,7 +44384,7 @@ var init_source = __esm({
|
|
|
43903
44384
|
fs2.writeFileSync(this.path, data, { mode: this.#options.configFileMode });
|
|
43904
44385
|
} else {
|
|
43905
44386
|
try {
|
|
43906
|
-
|
|
44387
|
+
writeFileSync(this.path, data, { mode: this.#options.configFileMode });
|
|
43907
44388
|
} catch (error) {
|
|
43908
44389
|
if (error?.code === "EXDEV") {
|
|
43909
44390
|
fs2.writeFileSync(this.path, data, { mode: this.#options.configFileMode });
|
|
@@ -44004,6 +44485,107 @@ var init_source = __esm({
|
|
|
44004
44485
|
}
|
|
44005
44486
|
});
|
|
44006
44487
|
|
|
44488
|
+
// src/identity.ts
|
|
44489
|
+
import {
|
|
44490
|
+
chmodSync,
|
|
44491
|
+
mkdirSync,
|
|
44492
|
+
readFileSync as readFileSync2,
|
|
44493
|
+
renameSync,
|
|
44494
|
+
writeFileSync as writeFileSync2,
|
|
44495
|
+
existsSync as existsSync4
|
|
44496
|
+
} from "fs";
|
|
44497
|
+
import { homedir as homedir3, hostname as osHostname } from "os";
|
|
44498
|
+
import { join as join3 } from "path";
|
|
44499
|
+
function ensureRoot() {
|
|
44500
|
+
mkdirSync(ROOT, { recursive: true, mode: 448 });
|
|
44501
|
+
}
|
|
44502
|
+
function writeAtomic(meta) {
|
|
44503
|
+
ensureRoot();
|
|
44504
|
+
const tmp = `${IDENTITY_FILE}.${process.pid}.tmp`;
|
|
44505
|
+
writeFileSync2(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
44506
|
+
renameSync(tmp, IDENTITY_FILE);
|
|
44507
|
+
try {
|
|
44508
|
+
chmodSync(IDENTITY_FILE, 384);
|
|
44509
|
+
} catch {
|
|
44510
|
+
}
|
|
44511
|
+
}
|
|
44512
|
+
function identityPath() {
|
|
44513
|
+
return IDENTITY_FILE;
|
|
44514
|
+
}
|
|
44515
|
+
function hasIdentityFile() {
|
|
44516
|
+
return existsSync4(IDENTITY_FILE);
|
|
44517
|
+
}
|
|
44518
|
+
function parseFile() {
|
|
44519
|
+
try {
|
|
44520
|
+
const raw = readFileSync2(IDENTITY_FILE, "utf8");
|
|
44521
|
+
const obj = JSON.parse(raw);
|
|
44522
|
+
if (!obj.deviceUuid || !obj.deviceId || !obj.bearerToken) {
|
|
44523
|
+
return null;
|
|
44524
|
+
}
|
|
44525
|
+
return {
|
|
44526
|
+
deviceUuid: obj.deviceUuid,
|
|
44527
|
+
deviceId: obj.deviceId,
|
|
44528
|
+
bearerToken: obj.bearerToken,
|
|
44529
|
+
claimCode: obj.claimCode ?? null,
|
|
44530
|
+
claimUrl: obj.claimUrl ?? null,
|
|
44531
|
+
hostname: obj.hostname ?? osHostname(),
|
|
44532
|
+
createdAt: obj.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
44533
|
+
userEmail: obj.userEmail ?? null,
|
|
44534
|
+
defaultOrgId: obj.defaultOrgId ?? null
|
|
44535
|
+
};
|
|
44536
|
+
} catch {
|
|
44537
|
+
return null;
|
|
44538
|
+
}
|
|
44539
|
+
}
|
|
44540
|
+
function loadIdentity(migrateFromConf2) {
|
|
44541
|
+
const fromFile = parseFile();
|
|
44542
|
+
if (fromFile) return fromFile;
|
|
44543
|
+
if (!migrateFromConf2) return null;
|
|
44544
|
+
const legacy = migrateFromConf2();
|
|
44545
|
+
if (!legacy) return null;
|
|
44546
|
+
if (!legacy.deviceUuid || !legacy.deviceId || !legacy.bearerToken) {
|
|
44547
|
+
return null;
|
|
44548
|
+
}
|
|
44549
|
+
const migrated = {
|
|
44550
|
+
deviceUuid: legacy.deviceUuid,
|
|
44551
|
+
deviceId: legacy.deviceId,
|
|
44552
|
+
bearerToken: legacy.bearerToken,
|
|
44553
|
+
claimCode: legacy.claimCode ?? null,
|
|
44554
|
+
claimUrl: legacy.claimUrl ?? null,
|
|
44555
|
+
hostname: osHostname(),
|
|
44556
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
44557
|
+
userEmail: legacy.userEmail ?? null,
|
|
44558
|
+
defaultOrgId: legacy.defaultOrgId ?? null
|
|
44559
|
+
};
|
|
44560
|
+
writeAtomic(migrated);
|
|
44561
|
+
return migrated;
|
|
44562
|
+
}
|
|
44563
|
+
function saveIdentity(meta) {
|
|
44564
|
+
writeAtomic(meta);
|
|
44565
|
+
}
|
|
44566
|
+
function backupIdentity() {
|
|
44567
|
+
if (!existsSync4(IDENTITY_FILE)) return null;
|
|
44568
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
44569
|
+
const dest = `${IDENTITY_FILE}.bak-${stamp}`;
|
|
44570
|
+
renameSync(IDENTITY_FILE, dest);
|
|
44571
|
+
return dest;
|
|
44572
|
+
}
|
|
44573
|
+
function updateIdentity(patch) {
|
|
44574
|
+
const current = parseFile();
|
|
44575
|
+
if (!current) return null;
|
|
44576
|
+
const merged = { ...current, ...patch };
|
|
44577
|
+
writeAtomic(merged);
|
|
44578
|
+
return merged;
|
|
44579
|
+
}
|
|
44580
|
+
var ROOT, IDENTITY_FILE;
|
|
44581
|
+
var init_identity = __esm({
|
|
44582
|
+
"src/identity.ts"() {
|
|
44583
|
+
"use strict";
|
|
44584
|
+
ROOT = join3(homedir3(), ".modelstat");
|
|
44585
|
+
IDENTITY_FILE = join3(ROOT, "identity.json");
|
|
44586
|
+
}
|
|
44587
|
+
});
|
|
44588
|
+
|
|
44007
44589
|
// src/config.ts
|
|
44008
44590
|
import { existsSync as existsSync5 } from "fs";
|
|
44009
44591
|
import { hostname } from "os";
|
|
@@ -44350,6 +44932,16 @@ var init_ids2 = __esm({
|
|
|
44350
44932
|
});
|
|
44351
44933
|
|
|
44352
44934
|
// ../../packages/companion-core/src/queue/index.ts
|
|
44935
|
+
function attachSegmentIds(calls, segments) {
|
|
44936
|
+
const segmentByEvent = /* @__PURE__ */ new Map();
|
|
44937
|
+
for (const seg of segments) {
|
|
44938
|
+
for (const id of seg.source_event_ids) segmentByEvent.set(id, seg.segment_id);
|
|
44939
|
+
}
|
|
44940
|
+
return calls.map((c) => ({
|
|
44941
|
+
...c,
|
|
44942
|
+
segment_id: segmentByEvent.get(c.source_event_id) ?? null
|
|
44943
|
+
}));
|
|
44944
|
+
}
|
|
44353
44945
|
var init_queue = __esm({
|
|
44354
44946
|
"../../packages/companion-core/src/queue/index.ts"() {
|
|
44355
44947
|
"use strict";
|
|
@@ -44358,21 +44950,6 @@ var init_queue = __esm({
|
|
|
44358
44950
|
}
|
|
44359
44951
|
});
|
|
44360
44952
|
|
|
44361
|
-
// ../../packages/companion-core/src/pipeline/prompts.ts
|
|
44362
|
-
var OLLAMA_CHAT_MODEL, OLLAMA_EMBED_MODEL, SUMMARISER_SYSTEM_PROMPT, SUMMARISER_MAX_TOKENS, SUMMARISER_TEMPERATURE, QWEN_CHARS_PER_TOKEN, ABSTRACT_OUTPUT_MAX_CHARS;
|
|
44363
|
-
var init_prompts = __esm({
|
|
44364
|
-
"../../packages/companion-core/src/pipeline/prompts.ts"() {
|
|
44365
|
-
"use strict";
|
|
44366
|
-
OLLAMA_CHAT_MODEL = "qwen3:4b";
|
|
44367
|
-
OLLAMA_EMBED_MODEL = "bge-small-en-v1.5";
|
|
44368
|
-
SUMMARISER_SYSTEM_PROMPT = "You summarise an AI coding session in ONE sentence, \u2264 240 characters. If the user message includes sampled conversation excerpts, base your summary on what the developer was actually working on (the substance \u2014 what was being built, debugged, refactored, or designed). If only metadata is given, paraphrase the metadata. Never quote the excerpts verbatim. No PII, no code literals, no file paths, no API keys. Reply with only the sentence.";
|
|
44369
|
-
SUMMARISER_MAX_TOKENS = 120;
|
|
44370
|
-
SUMMARISER_TEMPERATURE = 0.2;
|
|
44371
|
-
QWEN_CHARS_PER_TOKEN = 3.3;
|
|
44372
|
-
ABSTRACT_OUTPUT_MAX_CHARS = 240;
|
|
44373
|
-
}
|
|
44374
|
-
});
|
|
44375
|
-
|
|
44376
44953
|
// ../../packages/companion-core/src/pipeline/cognition.ts
|
|
44377
44954
|
function buildCognitionUserPrompt(abstract) {
|
|
44378
44955
|
return `Summary: "${abstract.replace(/\s+/g, " ").trim().slice(0, 480)}"
|
|
@@ -44455,6 +45032,21 @@ var init_cognition = __esm({
|
|
|
44455
45032
|
}
|
|
44456
45033
|
});
|
|
44457
45034
|
|
|
45035
|
+
// ../../packages/companion-core/src/pipeline/prompts.ts
|
|
45036
|
+
var OLLAMA_CHAT_MODEL, OLLAMA_EMBED_MODEL, SUMMARISER_SYSTEM_PROMPT, SUMMARISER_MAX_TOKENS, SUMMARISER_TEMPERATURE, QWEN_CHARS_PER_TOKEN, ABSTRACT_OUTPUT_MAX_CHARS;
|
|
45037
|
+
var init_prompts = __esm({
|
|
45038
|
+
"../../packages/companion-core/src/pipeline/prompts.ts"() {
|
|
45039
|
+
"use strict";
|
|
45040
|
+
OLLAMA_CHAT_MODEL = "qwen3:4b";
|
|
45041
|
+
OLLAMA_EMBED_MODEL = "bge-small-en-v1.5";
|
|
45042
|
+
SUMMARISER_SYSTEM_PROMPT = "You summarise an AI coding session in ONE sentence, \u2264 240 characters. If the user message includes sampled conversation excerpts, base your summary on what the developer was actually working on (the substance \u2014 what was being built, debugged, refactored, or designed). If only metadata is given, paraphrase the metadata. Never quote the excerpts verbatim. No PII, no code literals, no file paths, no API keys. Reply with only the sentence.";
|
|
45043
|
+
SUMMARISER_MAX_TOKENS = 120;
|
|
45044
|
+
SUMMARISER_TEMPERATURE = 0.2;
|
|
45045
|
+
QWEN_CHARS_PER_TOKEN = 3.3;
|
|
45046
|
+
ABSTRACT_OUTPUT_MAX_CHARS = 240;
|
|
45047
|
+
}
|
|
45048
|
+
});
|
|
45049
|
+
|
|
44458
45050
|
// ../../packages/companion-core/src/pipeline/title.ts
|
|
44459
45051
|
function buildTitleUserPrompt(input) {
|
|
44460
45052
|
const lines = input.abstracts.map(
|
|
@@ -44632,9 +45224,7 @@ async function buildForOneSession(sessionId, events, adapters2, onSlice) {
|
|
|
44632
45224
|
} catch (err) {
|
|
44633
45225
|
failed += 1;
|
|
44634
45226
|
lastError = err instanceof Error ? err.message : String(err);
|
|
44635
|
-
console.warn(
|
|
44636
|
-
`[modelstat] slice failed in session ${sessionId}: ${lastError}`
|
|
44637
|
-
);
|
|
45227
|
+
console.warn(`[modelstat] slice failed in session ${sessionId}: ${lastError}`);
|
|
44638
45228
|
}
|
|
44639
45229
|
}
|
|
44640
45230
|
if (failed > 0) {
|
|
@@ -44739,6 +45329,24 @@ Write the SHORTEST keyword-dense paragraph (1-3 sentences, \u2264${ABSTRACT_OUTP
|
|
|
44739
45329
|
for (const c of [...components].slice(0, 8)) {
|
|
44740
45330
|
tags.push({ root_key: "components", name: c, confidence: 0.6 });
|
|
44741
45331
|
}
|
|
45332
|
+
const toolCallCounts = /* @__PURE__ */ new Map();
|
|
45333
|
+
let toolCallTotal = 0;
|
|
45334
|
+
for (const ev of slice) {
|
|
45335
|
+
for (const [identity, n] of Object.entries(ev.tool_calls ?? {})) {
|
|
45336
|
+
if (!(n > 0)) continue;
|
|
45337
|
+
toolCallCounts.set(identity, (toolCallCounts.get(identity) ?? 0) + n);
|
|
45338
|
+
toolCallTotal += n;
|
|
45339
|
+
}
|
|
45340
|
+
}
|
|
45341
|
+
const topToolCalls = [...toolCallCounts.entries()].filter(([identity]) => identity.length <= 120).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 8);
|
|
45342
|
+
for (const [identity, count] of topToolCalls) {
|
|
45343
|
+
const share = Math.round(count / toolCallTotal * 100) / 100;
|
|
45344
|
+
tags.push({
|
|
45345
|
+
root_key: "tool_calls",
|
|
45346
|
+
name: identity,
|
|
45347
|
+
confidence: Math.min(1, Math.max(0.05, share))
|
|
45348
|
+
});
|
|
45349
|
+
}
|
|
44742
45350
|
let segmentEmbedding;
|
|
44743
45351
|
try {
|
|
44744
45352
|
const embedded = await adapters2.embed(redacted.text.slice(0, ABSTRACT_MAX_CHARS));
|
|
@@ -44826,13 +45434,13 @@ var SEGMENT_TIME_GAP_MS, SEGMENT_TOPIC_THRESHOLD, SEGMENT_MAX_TURNS, SEGMENT_MAX
|
|
|
44826
45434
|
var init_pipeline = __esm({
|
|
44827
45435
|
"../../packages/companion-core/src/pipeline/index.ts"() {
|
|
44828
45436
|
"use strict";
|
|
44829
|
-
init_redact();
|
|
44830
45437
|
init_ids();
|
|
44831
|
-
init_prompts();
|
|
44832
|
-
init_cognition();
|
|
44833
45438
|
init_redact();
|
|
45439
|
+
init_cognition();
|
|
44834
45440
|
init_prompts();
|
|
45441
|
+
init_redact();
|
|
44835
45442
|
init_cognition();
|
|
45443
|
+
init_prompts();
|
|
44836
45444
|
init_title();
|
|
44837
45445
|
SEGMENT_TIME_GAP_MS = 15 * 6e4;
|
|
44838
45446
|
SEGMENT_TOPIC_THRESHOLD = 0.35;
|
|
@@ -45322,6 +45930,21 @@ var init_llama = __esm({
|
|
|
45322
45930
|
}
|
|
45323
45931
|
});
|
|
45324
45932
|
|
|
45933
|
+
// ../../packages/companion-core/src/optional-module.ts
|
|
45934
|
+
function isMissingOptionalModuleError(err) {
|
|
45935
|
+
const code = err?.code;
|
|
45936
|
+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
|
|
45937
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
45938
|
+
return /cannot find (package|module)|cannot resolve|failed to resolve/i.test(msg);
|
|
45939
|
+
}
|
|
45940
|
+
var OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS;
|
|
45941
|
+
var init_optional_module = __esm({
|
|
45942
|
+
"../../packages/companion-core/src/optional-module.ts"() {
|
|
45943
|
+
"use strict";
|
|
45944
|
+
OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS = 3;
|
|
45945
|
+
}
|
|
45946
|
+
});
|
|
45947
|
+
|
|
45325
45948
|
// ../../packages/companion-core/src/node/transformersjs-embed.ts
|
|
45326
45949
|
async function loadPipeline(model) {
|
|
45327
45950
|
if (cached) return cached;
|
|
@@ -45329,11 +45952,7 @@ async function loadPipeline(model) {
|
|
|
45329
45952
|
if (!loadPromise2) {
|
|
45330
45953
|
loadPromise2 = (async () => {
|
|
45331
45954
|
try {
|
|
45332
|
-
const
|
|
45333
|
-
const tjs = await import(
|
|
45334
|
-
/* @vite-ignore */
|
|
45335
|
-
moduleId
|
|
45336
|
-
);
|
|
45955
|
+
const tjs = await importModule("@huggingface/transformers");
|
|
45337
45956
|
const p = await tjs.pipeline("feature-extraction", model, {
|
|
45338
45957
|
device: "cpu",
|
|
45339
45958
|
dtype: "fp32"
|
|
@@ -45342,12 +45961,16 @@ async function loadPipeline(model) {
|
|
|
45342
45961
|
return p;
|
|
45343
45962
|
} catch (err) {
|
|
45344
45963
|
const msg = err.message;
|
|
45345
|
-
|
|
45964
|
+
loadAttempts += 1;
|
|
45965
|
+
if (isMissingOptionalModuleError(err) || /unsupported|architecture|not supported|onnx/i.test(msg) || loadAttempts >= OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS) {
|
|
45346
45966
|
loadFailedPermanently = true;
|
|
45347
45967
|
}
|
|
45348
|
-
|
|
45349
|
-
|
|
45350
|
-
|
|
45968
|
+
if (!warnedUnavailable) {
|
|
45969
|
+
warnedUnavailable = true;
|
|
45970
|
+
console.warn(
|
|
45971
|
+
`[modelstat] transformers.js embedder unavailable (segments will be re-embedded server-side; further attempts ${loadFailedPermanently ? "disabled" : `limited to ${OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS}`}, warning once): ${msg}`
|
|
45972
|
+
);
|
|
45973
|
+
}
|
|
45351
45974
|
loadPromise2 = null;
|
|
45352
45975
|
return null;
|
|
45353
45976
|
}
|
|
@@ -45364,21 +45987,32 @@ function createTransformersJsEmbedder(model = DEFAULT_MODEL) {
|
|
|
45364
45987
|
const out = await pipe(text, { pooling: "mean", normalize: true });
|
|
45365
45988
|
return Array.from(out.data);
|
|
45366
45989
|
} catch (err) {
|
|
45367
|
-
|
|
45368
|
-
|
|
45369
|
-
|
|
45990
|
+
if (!warnedInferenceError) {
|
|
45991
|
+
warnedInferenceError = true;
|
|
45992
|
+
console.warn(
|
|
45993
|
+
`[modelstat] embed error (returning empty vectors, server will re-embed; warning once): ${err.message}`
|
|
45994
|
+
);
|
|
45995
|
+
}
|
|
45370
45996
|
return [];
|
|
45371
45997
|
}
|
|
45372
45998
|
};
|
|
45373
45999
|
}
|
|
45374
|
-
var cached, loadPromise2, loadFailedPermanently, DEFAULT_MODEL;
|
|
46000
|
+
var cached, loadPromise2, loadFailedPermanently, loadAttempts, warnedUnavailable, warnedInferenceError, DEFAULT_MODEL, importModule;
|
|
45375
46001
|
var init_transformersjs_embed = __esm({
|
|
45376
46002
|
"../../packages/companion-core/src/node/transformersjs-embed.ts"() {
|
|
45377
46003
|
"use strict";
|
|
46004
|
+
init_optional_module();
|
|
45378
46005
|
cached = null;
|
|
45379
46006
|
loadPromise2 = null;
|
|
45380
46007
|
loadFailedPermanently = false;
|
|
46008
|
+
loadAttempts = 0;
|
|
46009
|
+
warnedUnavailable = false;
|
|
46010
|
+
warnedInferenceError = false;
|
|
45381
46011
|
DEFAULT_MODEL = "Xenova/bge-small-en-v1.5";
|
|
46012
|
+
importModule = (id) => import(
|
|
46013
|
+
/* @vite-ignore */
|
|
46014
|
+
id
|
|
46015
|
+
);
|
|
45382
46016
|
}
|
|
45383
46017
|
});
|
|
45384
46018
|
|
|
@@ -45417,18 +46051,23 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45417
46051
|
const device = opts.device ?? (isBrowser ? "webgpu" : "cpu");
|
|
45418
46052
|
const dtype = opts.dtype ?? "q4";
|
|
45419
46053
|
const modelId = opts.model ?? "openai/privacy-filter";
|
|
46054
|
+
const importModule2 = opts.importModule ?? ((id) => import(
|
|
46055
|
+
/* @vite-ignore */
|
|
46056
|
+
id
|
|
46057
|
+
));
|
|
45420
46058
|
let cached2 = null;
|
|
45421
46059
|
let loadPromise3 = null;
|
|
46060
|
+
let loadFailedPermanently2 = false;
|
|
46061
|
+
let loadAttempts2 = 0;
|
|
46062
|
+
let warnedUnavailable2 = false;
|
|
46063
|
+
let warnedInferenceError2 = false;
|
|
45422
46064
|
async function loadPipeline2() {
|
|
45423
46065
|
if (cached2) return cached2;
|
|
46066
|
+
if (loadFailedPermanently2) return null;
|
|
45424
46067
|
if (!loadPromise3) {
|
|
45425
46068
|
loadPromise3 = (async () => {
|
|
45426
46069
|
try {
|
|
45427
|
-
const
|
|
45428
|
-
const tjs = await import(
|
|
45429
|
-
/* @vite-ignore */
|
|
45430
|
-
moduleId
|
|
45431
|
-
);
|
|
46070
|
+
const tjs = await importModule2("@huggingface/transformers");
|
|
45432
46071
|
const p = await tjs.pipeline("token-classification", modelId, {
|
|
45433
46072
|
device,
|
|
45434
46073
|
dtype,
|
|
@@ -45438,10 +46077,17 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45438
46077
|
return p;
|
|
45439
46078
|
} catch (err) {
|
|
45440
46079
|
loadPromise3 = null;
|
|
45441
|
-
|
|
45442
|
-
|
|
45443
|
-
|
|
45444
|
-
|
|
46080
|
+
loadAttempts2 += 1;
|
|
46081
|
+
if (isMissingOptionalModuleError(err) || loadAttempts2 >= OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS) {
|
|
46082
|
+
loadFailedPermanently2 = true;
|
|
46083
|
+
}
|
|
46084
|
+
if (!warnedUnavailable2) {
|
|
46085
|
+
warnedUnavailable2 = true;
|
|
46086
|
+
console.warn(
|
|
46087
|
+
"[privacy-filter] adapter unavailable \u2014 install @huggingface/transformers in the consuming package to enable model-based redaction. Falling back to pass-through (warning once).",
|
|
46088
|
+
err.message
|
|
46089
|
+
);
|
|
46090
|
+
}
|
|
45445
46091
|
return null;
|
|
45446
46092
|
}
|
|
45447
46093
|
})();
|
|
@@ -45464,10 +46110,13 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45464
46110
|
try {
|
|
45465
46111
|
tokens = await classify(text);
|
|
45466
46112
|
} catch (err) {
|
|
45467
|
-
|
|
45468
|
-
|
|
45469
|
-
|
|
45470
|
-
|
|
46113
|
+
if (!warnedInferenceError2) {
|
|
46114
|
+
warnedInferenceError2 = true;
|
|
46115
|
+
console.warn(
|
|
46116
|
+
"[privacy-filter] inference failed, returning input unchanged (warning once):",
|
|
46117
|
+
err.message
|
|
46118
|
+
);
|
|
46119
|
+
}
|
|
45471
46120
|
return empty;
|
|
45472
46121
|
}
|
|
45473
46122
|
const spans = [];
|
|
@@ -45504,6 +46153,7 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45504
46153
|
var init_privacy_filter = __esm({
|
|
45505
46154
|
"../../packages/companion-core/src/redact/privacy-filter.ts"() {
|
|
45506
46155
|
"use strict";
|
|
46156
|
+
init_optional_module();
|
|
45507
46157
|
}
|
|
45508
46158
|
});
|
|
45509
46159
|
|
|
@@ -45613,9 +46263,9 @@ async function scanAll(cb = {}) {
|
|
|
45613
46263
|
const full = join5(dir, f);
|
|
45614
46264
|
jobs.push({
|
|
45615
46265
|
path: full,
|
|
45616
|
-
parse: async () => {
|
|
45617
|
-
const r = await parseClaudeCodeJsonl({ deviceId, sourceFile: full });
|
|
45618
|
-
return {
|
|
46266
|
+
parse: async (sink2) => {
|
|
46267
|
+
const r = await parseClaudeCodeJsonl({ deviceId, sourceFile: full, onEvents: sink2 });
|
|
46268
|
+
return { toolCalls: r.toolCalls ?? [] };
|
|
45619
46269
|
}
|
|
45620
46270
|
});
|
|
45621
46271
|
}
|
|
@@ -45637,9 +46287,9 @@ async function scanAll(cb = {}) {
|
|
|
45637
46287
|
const full = join5(base, y, m, d, f);
|
|
45638
46288
|
jobs.push({
|
|
45639
46289
|
path: full,
|
|
45640
|
-
parse: async () => {
|
|
45641
|
-
const r = await parseCodexRollout({ deviceId, sourceFile: full });
|
|
45642
|
-
return {
|
|
46290
|
+
parse: async (sink2) => {
|
|
46291
|
+
const r = await parseCodexRollout({ deviceId, sourceFile: full, onEvents: sink2 });
|
|
46292
|
+
return { toolCalls: r.toolCalls ?? [] };
|
|
45643
46293
|
}
|
|
45644
46294
|
});
|
|
45645
46295
|
}
|
|
@@ -45655,10 +46305,11 @@ async function scanAll(cb = {}) {
|
|
|
45655
46305
|
let eventsUploaded = 0;
|
|
45656
46306
|
let segmentsUploaded = 0;
|
|
45657
46307
|
let buffer = [];
|
|
46308
|
+
let toolCallBuffer = [];
|
|
45658
46309
|
let pendingCursors = [];
|
|
45659
46310
|
const runSegmentsBySession = /* @__PURE__ */ new Map();
|
|
45660
46311
|
async function flushBatch() {
|
|
45661
|
-
if (!buffer.length) return;
|
|
46312
|
+
if (!buffer.length && !toolCallBuffer.length) return;
|
|
45662
46313
|
const events = buffer.map(withNonNullTokens);
|
|
45663
46314
|
const segments = await buildSegments(events, cb.onProgress);
|
|
45664
46315
|
for (const seg of segments) {
|
|
@@ -45682,6 +46333,10 @@ async function scanAll(cb = {}) {
|
|
|
45682
46333
|
agent_version: AGENT_VERSION,
|
|
45683
46334
|
events,
|
|
45684
46335
|
segments,
|
|
46336
|
+
// Per-call tool invocations: now that segments exist, attribute
|
|
46337
|
+
// each call to the segment covering its source event (null when
|
|
46338
|
+
// no segment covers it — e.g. codex response_item anchors).
|
|
46339
|
+
tool_calls: attachSegmentIds(toolCallBuffer, segments),
|
|
45685
46340
|
...Object.keys(sessionTitles).length ? { session_titles: sessionTitles } : {}
|
|
45686
46341
|
};
|
|
45687
46342
|
cb.onUpload?.({ events: events.length, segments: segments.length });
|
|
@@ -45692,8 +46347,26 @@ async function scanAll(cb = {}) {
|
|
|
45692
46347
|
for (const pc of pendingCursors) state.setCursor(pc.path, pc.cs);
|
|
45693
46348
|
pendingCursors = [];
|
|
45694
46349
|
buffer = [];
|
|
46350
|
+
toolCallBuffer = [];
|
|
45695
46351
|
cb.onUploaded?.({ events: res.accepted, segments: segments.length });
|
|
45696
46352
|
}
|
|
46353
|
+
const sink = async (events) => {
|
|
46354
|
+
for (const e of events) {
|
|
46355
|
+
buffer.push(e);
|
|
46356
|
+
if (buffer.length >= BATCH_MAX_EVENTS) await flushBatch();
|
|
46357
|
+
}
|
|
46358
|
+
if (buffer.length > BATCH_BUFFER_HARD_CAP) {
|
|
46359
|
+
throw new Error(
|
|
46360
|
+
`scan event buffer exceeded ${BATCH_BUFFER_HARD_CAP} events \u2014 incremental batch flushing has regressed (see BATCH_BUFFER_HARD_CAP)`
|
|
46361
|
+
);
|
|
46362
|
+
}
|
|
46363
|
+
};
|
|
46364
|
+
const bufferToolCalls = async (calls) => {
|
|
46365
|
+
for (const c of calls) {
|
|
46366
|
+
if (toolCallBuffer.length >= BATCH_MAX_TOOL_CALLS) await flushBatch();
|
|
46367
|
+
toolCallBuffer.push(c);
|
|
46368
|
+
}
|
|
46369
|
+
};
|
|
45697
46370
|
for (let i = 0; i < jobs.length; i++) {
|
|
45698
46371
|
const job = jobs[i];
|
|
45699
46372
|
cb.onFile?.(job.path, i, jobs.length);
|
|
@@ -45705,13 +46378,8 @@ async function scanAll(cb = {}) {
|
|
|
45705
46378
|
}
|
|
45706
46379
|
filesScanned += 1;
|
|
45707
46380
|
try {
|
|
45708
|
-
const r = await job.parse();
|
|
45709
|
-
|
|
45710
|
-
for (const e of r.events) {
|
|
45711
|
-
buffer.push(e);
|
|
45712
|
-
if (buffer.length >= BATCH_MAX_EVENTS) await flushBatch();
|
|
45713
|
-
}
|
|
45714
|
-
}
|
|
46381
|
+
const r = await job.parse(sink);
|
|
46382
|
+
await bufferToolCalls(r.toolCalls);
|
|
45715
46383
|
if (cs) pendingCursors.push({ path: job.path, cs });
|
|
45716
46384
|
} catch (e) {
|
|
45717
46385
|
console.warn(` ! parse failed for ${job.path}:`, e.message);
|
|
@@ -45720,17 +46388,20 @@ async function scanAll(cb = {}) {
|
|
|
45720
46388
|
await flushBatch();
|
|
45721
46389
|
return { filesScanned, filesUnchanged, batchesUploaded, eventsUploaded, segmentsUploaded };
|
|
45722
46390
|
}
|
|
45723
|
-
var AGENT_VERSION, BATCH_MAX_EVENTS, ZERO_TOKENS;
|
|
46391
|
+
var AGENT_VERSION, BATCH_MAX_EVENTS, BATCH_MAX_TOOL_CALLS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
|
|
45724
46392
|
var init_scan = __esm({
|
|
45725
46393
|
"src/scan.ts"() {
|
|
45726
46394
|
"use strict";
|
|
45727
|
-
init_src2();
|
|
45728
46395
|
init_src3();
|
|
45729
|
-
|
|
45730
|
-
|
|
46396
|
+
init_queue();
|
|
46397
|
+
init_src2();
|
|
45731
46398
|
init_api();
|
|
45732
|
-
|
|
46399
|
+
init_config2();
|
|
46400
|
+
init_pipeline2();
|
|
46401
|
+
AGENT_VERSION = true ? "agent-0.0.47" : "agent-dev";
|
|
45733
46402
|
BATCH_MAX_EVENTS = 2e3;
|
|
46403
|
+
BATCH_MAX_TOOL_CALLS = 2e4;
|
|
46404
|
+
BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
|
|
45734
46405
|
ZERO_TOKENS = {
|
|
45735
46406
|
input: 0,
|
|
45736
46407
|
output: 0,
|
|
@@ -45766,9 +46437,9 @@ function isProcessAlive(pid) {
|
|
|
45766
46437
|
return false;
|
|
45767
46438
|
}
|
|
45768
46439
|
}
|
|
45769
|
-
function
|
|
46440
|
+
function readDaemonLock(lockFile = LOCK_FILE) {
|
|
45770
46441
|
try {
|
|
45771
|
-
const raw = readFileSync4(
|
|
46442
|
+
const raw = readFileSync4(lockFile, "utf8");
|
|
45772
46443
|
const obj = JSON.parse(raw);
|
|
45773
46444
|
if (typeof obj.pid !== "number") return null;
|
|
45774
46445
|
return {
|
|
@@ -45793,7 +46464,7 @@ function writeLockAtomic(meta) {
|
|
|
45793
46464
|
renameSync2(tmp, LOCK_FILE);
|
|
45794
46465
|
}
|
|
45795
46466
|
function removeLockIfOwned(ownerPid) {
|
|
45796
|
-
const lock =
|
|
46467
|
+
const lock = readDaemonLock();
|
|
45797
46468
|
if (!lock) return;
|
|
45798
46469
|
if (lock.pid !== ownerPid) return;
|
|
45799
46470
|
try {
|
|
@@ -45802,7 +46473,7 @@ function removeLockIfOwned(ownerPid) {
|
|
|
45802
46473
|
}
|
|
45803
46474
|
}
|
|
45804
46475
|
function acquireDaemonLock(opts) {
|
|
45805
|
-
const existing =
|
|
46476
|
+
const existing = readDaemonLock();
|
|
45806
46477
|
if (existing && isProcessAlive(existing.pid)) {
|
|
45807
46478
|
if (!opts.force) {
|
|
45808
46479
|
const ageSec = ageInSeconds(existing.startedAt);
|
|
@@ -45820,6 +46491,20 @@ function acquireDaemonLock(opts) {
|
|
|
45820
46491
|
apiUrl: opts.apiUrl
|
|
45821
46492
|
};
|
|
45822
46493
|
writeLockAtomic(meta);
|
|
46494
|
+
const recheck = setTimeout(() => {
|
|
46495
|
+
const current = readDaemonLock();
|
|
46496
|
+
if (checkLockOwnership(process.pid, current) !== "lost") return;
|
|
46497
|
+
const winner = current;
|
|
46498
|
+
if (opts.onLockLost) {
|
|
46499
|
+
opts.onLockLost(winner);
|
|
46500
|
+
return;
|
|
46501
|
+
}
|
|
46502
|
+
console.log(
|
|
46503
|
+
`[modelstat] another daemon (pid ${winner.pid}, started ${winner.startedAt}) won the lock race \u2014 this instance is standing down.`
|
|
46504
|
+
);
|
|
46505
|
+
process.exit(0);
|
|
46506
|
+
}, LOCK_RECHECK_MS);
|
|
46507
|
+
recheck.unref();
|
|
45823
46508
|
const cleanup = () => removeLockIfOwned(process.pid);
|
|
45824
46509
|
process.once("beforeExit", cleanup);
|
|
45825
46510
|
process.once("SIGINT", () => {
|
|
@@ -45837,6 +46522,11 @@ function acquireDaemonLock(opts) {
|
|
|
45837
46522
|
});
|
|
45838
46523
|
return { kind: "acquired" };
|
|
45839
46524
|
}
|
|
46525
|
+
function checkLockOwnership(myPid, current, pidAlive = isProcessAlive) {
|
|
46526
|
+
if (!current || current.pid === myPid) return "owned";
|
|
46527
|
+
if (!pidAlive(current.pid)) return "winner_dead";
|
|
46528
|
+
return "lost";
|
|
46529
|
+
}
|
|
45840
46530
|
function ageInSeconds(iso) {
|
|
45841
46531
|
const t = Date.parse(iso);
|
|
45842
46532
|
if (Number.isNaN(t)) return -1;
|
|
@@ -45851,12 +46541,13 @@ function formatAge(seconds) {
|
|
|
45851
46541
|
const h = Math.floor(m / 60);
|
|
45852
46542
|
return `${h}h ${m % 60}m`;
|
|
45853
46543
|
}
|
|
45854
|
-
var LOCK_DIR, LOCK_FILE;
|
|
46544
|
+
var LOCK_DIR, LOCK_FILE, LOCK_RECHECK_MS;
|
|
45855
46545
|
var init_lock = __esm({
|
|
45856
46546
|
"src/lock.ts"() {
|
|
45857
46547
|
"use strict";
|
|
45858
46548
|
LOCK_DIR = join7(homedir7(), ".modelstat");
|
|
45859
46549
|
LOCK_FILE = join7(LOCK_DIR, "daemon.lock");
|
|
46550
|
+
LOCK_RECHECK_MS = 5e3;
|
|
45860
46551
|
}
|
|
45861
46552
|
});
|
|
45862
46553
|
|
|
@@ -45920,7 +46611,7 @@ var PROCESSING_VERSION;
|
|
|
45920
46611
|
var init_processing_version = __esm({
|
|
45921
46612
|
"src/processing-version.ts"() {
|
|
45922
46613
|
"use strict";
|
|
45923
|
-
PROCESSING_VERSION =
|
|
46614
|
+
PROCESSING_VERSION = 5;
|
|
45924
46615
|
}
|
|
45925
46616
|
});
|
|
45926
46617
|
|
|
@@ -47730,17 +48421,44 @@ async function sendHeartbeat() {
|
|
|
47730
48421
|
}
|
|
47731
48422
|
writeLocalStatus(body).catch(() => void 0);
|
|
47732
48423
|
}
|
|
48424
|
+
async function rotateRunawayLogs() {
|
|
48425
|
+
const { homedir: homedir10 } = await import("os");
|
|
48426
|
+
const { join: join12 } = await import("path");
|
|
48427
|
+
const { open: open2, stat: stat6, truncate, writeFile } = await import("fs/promises");
|
|
48428
|
+
const dir = join12(homedir10(), ".modelstat", "logs");
|
|
48429
|
+
for (const name of ["out.log", "err.log"]) {
|
|
48430
|
+
const p = join12(dir, name);
|
|
48431
|
+
try {
|
|
48432
|
+
const st = await stat6(p);
|
|
48433
|
+
if (st.size <= LOG_MAX_BYTES) continue;
|
|
48434
|
+
const keep = Math.min(LOG_TAIL_KEEP_BYTES, st.size);
|
|
48435
|
+
const fh = await open2(p, "r");
|
|
48436
|
+
try {
|
|
48437
|
+
const buf = Buffer.alloc(keep);
|
|
48438
|
+
await fh.read(buf, 0, keep, st.size - keep);
|
|
48439
|
+
await writeFile(p.replace(/\.log$/, ".old.log"), buf);
|
|
48440
|
+
} finally {
|
|
48441
|
+
await fh.close();
|
|
48442
|
+
}
|
|
48443
|
+
await truncate(p, 0);
|
|
48444
|
+
console.log(
|
|
48445
|
+
`[modelstat] rotated ${name}: was ${(st.size / 1024 / 1024).toFixed(0)} MB (> ${LOG_MAX_BYTES / 1024 / 1024} MB cap), tail kept in ${name.replace(/\.log$/, ".old.log")}`
|
|
48446
|
+
);
|
|
48447
|
+
} catch {
|
|
48448
|
+
}
|
|
48449
|
+
}
|
|
48450
|
+
}
|
|
47733
48451
|
async function writeLocalStatus(snapshot) {
|
|
47734
|
-
const { homedir:
|
|
47735
|
-
const { join:
|
|
48452
|
+
const { homedir: homedir10 } = await import("os");
|
|
48453
|
+
const { join: join12 } = await import("path");
|
|
47736
48454
|
const { writeFile, mkdir: mkdir2, rename } = await import("fs/promises");
|
|
47737
48455
|
if (!lastStatusPath) {
|
|
47738
|
-
const dir =
|
|
48456
|
+
const dir = join12(homedir10(), ".modelstat");
|
|
47739
48457
|
try {
|
|
47740
48458
|
await mkdir2(dir, { recursive: true });
|
|
47741
48459
|
} catch {
|
|
47742
48460
|
}
|
|
47743
|
-
lastStatusPath =
|
|
48461
|
+
lastStatusPath = join12(dir, "last-status.json");
|
|
47744
48462
|
}
|
|
47745
48463
|
const tmp = `${lastStatusPath}.tmp`;
|
|
47746
48464
|
try {
|
|
@@ -47822,7 +48540,17 @@ async function runDaemon(opts = {}) {
|
|
|
47822
48540
|
const lock = acquireDaemonLock({
|
|
47823
48541
|
agentVersion: AGENT_VERSION2,
|
|
47824
48542
|
apiUrl: state.apiUrl,
|
|
47825
|
-
force: opts.force === true
|
|
48543
|
+
force: opts.force === true,
|
|
48544
|
+
// If a racing daemon out-renamed us for the lock (see lock.ts
|
|
48545
|
+
// recheck), stand down with exit 0: our supervisor's next health
|
|
48546
|
+
// check sees the winner and adopts it instead of respawning us.
|
|
48547
|
+
onLockLost: (winner) => {
|
|
48548
|
+
setPhase("offline", `another daemon (pid ${winner.pid}) owns the lock \u2014 standing down`);
|
|
48549
|
+
console.log(
|
|
48550
|
+
`[modelstat] another daemon (pid ${winner.pid}, started ${winner.startedAt}) won the lock race \u2014 this instance is standing down.`
|
|
48551
|
+
);
|
|
48552
|
+
process.exit(0);
|
|
48553
|
+
}
|
|
47826
48554
|
});
|
|
47827
48555
|
if (lock.kind === "already_running") {
|
|
47828
48556
|
console.log(
|
|
@@ -47839,6 +48567,7 @@ async function runDaemon(opts = {}) {
|
|
|
47839
48567
|
return;
|
|
47840
48568
|
}
|
|
47841
48569
|
setPhase("starting", "Booting");
|
|
48570
|
+
await rotateRunawayLogs();
|
|
47842
48571
|
setStat("segments_sent", state.segmentsSent);
|
|
47843
48572
|
const hb = setInterval(() => void sendHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
47844
48573
|
hb.unref();
|
|
@@ -47862,19 +48591,19 @@ async function runDaemon(opts = {}) {
|
|
|
47862
48591
|
await runDiscovery();
|
|
47863
48592
|
await requestScan("startup");
|
|
47864
48593
|
const chokidar = (await Promise.resolve().then(() => (init_esm2(), esm_exports))).default;
|
|
47865
|
-
const { homedir:
|
|
47866
|
-
const { join:
|
|
47867
|
-
const home2 =
|
|
48594
|
+
const { homedir: homedir10, platform: platform5 } = await import("os");
|
|
48595
|
+
const { join: join12 } = await import("path");
|
|
48596
|
+
const home2 = homedir10();
|
|
47868
48597
|
const dirs = [
|
|
47869
|
-
|
|
47870
|
-
|
|
47871
|
-
|
|
47872
|
-
|
|
48598
|
+
join12(home2, ".claude/projects"),
|
|
48599
|
+
join12(home2, ".codex/sessions"),
|
|
48600
|
+
join12(home2, ".cursor/ai-tracking"),
|
|
48601
|
+
join12(home2, ".gemini"),
|
|
47873
48602
|
...platform5() === "darwin" ? [
|
|
47874
|
-
|
|
47875
|
-
|
|
48603
|
+
join12(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
|
|
48604
|
+
join12(home2, "Library/Application Support/Claude")
|
|
47876
48605
|
] : [
|
|
47877
|
-
|
|
48606
|
+
join12(home2, ".config/Cursor/User/workspaceStorage")
|
|
47878
48607
|
]
|
|
47879
48608
|
].filter((p) => existsSync9(p) && statSync2(p).isDirectory());
|
|
47880
48609
|
setPhase("watching", `Watching ${dirs.length} directories`);
|
|
@@ -47917,7 +48646,7 @@ async function runDaemon(opts = {}) {
|
|
|
47917
48646
|
await new Promise(() => {
|
|
47918
48647
|
});
|
|
47919
48648
|
}
|
|
47920
|
-
var import_undici2, AGENT_VERSION2, HEARTBEAT_INTERVAL_MS, SCAN_INTERVAL_MS, DISCOVERY_INTERVAL_MS, status, LOCAL_FLUSH_THROTTLE_MS, localFlushTimer, localFlushPending, lastStatusPath, scanRunner;
|
|
48649
|
+
var import_undici2, AGENT_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;
|
|
47921
48650
|
var init_daemon = __esm({
|
|
47922
48651
|
"src/daemon.ts"() {
|
|
47923
48652
|
"use strict";
|
|
@@ -47929,7 +48658,7 @@ var init_daemon = __esm({
|
|
|
47929
48658
|
init_lock();
|
|
47930
48659
|
init_scan();
|
|
47931
48660
|
init_single_flight();
|
|
47932
|
-
AGENT_VERSION2 = true ? "agent-0.0.
|
|
48661
|
+
AGENT_VERSION2 = true ? "agent-0.0.47" : "agent-dev";
|
|
47933
48662
|
HEARTBEAT_INTERVAL_MS = 1e4;
|
|
47934
48663
|
SCAN_INTERVAL_MS = 5 * 60 * 1e3;
|
|
47935
48664
|
DISCOVERY_INTERVAL_MS = 6e4;
|
|
@@ -47945,6 +48674,8 @@ var init_daemon = __esm({
|
|
|
47945
48674
|
LOCAL_FLUSH_THROTTLE_MS = 400;
|
|
47946
48675
|
localFlushTimer = null;
|
|
47947
48676
|
localFlushPending = false;
|
|
48677
|
+
LOG_MAX_BYTES = 64 * 1024 * 1024;
|
|
48678
|
+
LOG_TAIL_KEEP_BYTES = 4 * 1024 * 1024;
|
|
47948
48679
|
lastStatusPath = null;
|
|
47949
48680
|
scanRunner = createCoalescingRunner(runScanCycle);
|
|
47950
48681
|
}
|
|
@@ -47956,33 +48687,33 @@ __export(watch_exports, {
|
|
|
47956
48687
|
watchForever: () => watchForever
|
|
47957
48688
|
});
|
|
47958
48689
|
import { existsSync as existsSync10 } from "fs";
|
|
47959
|
-
import { homedir as
|
|
47960
|
-
import { join as
|
|
48690
|
+
import { homedir as homedir9, platform as platform3 } from "os";
|
|
48691
|
+
import { join as join11 } from "path";
|
|
47961
48692
|
function resolveWatchDirs() {
|
|
47962
|
-
const home2 =
|
|
47963
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ??
|
|
47964
|
-
const xdgData = process.env.XDG_DATA_HOME ??
|
|
48693
|
+
const home2 = homedir9();
|
|
48694
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join11(home2, ".config");
|
|
48695
|
+
const xdgData = process.env.XDG_DATA_HOME ?? join11(home2, ".local/share");
|
|
47965
48696
|
const candidates = [
|
|
47966
48697
|
// universal (default HOME-rooted CLI data dirs)
|
|
47967
|
-
|
|
47968
|
-
|
|
47969
|
-
|
|
47970
|
-
|
|
47971
|
-
|
|
48698
|
+
join11(home2, ".claude/projects"),
|
|
48699
|
+
join11(home2, ".codex/sessions"),
|
|
48700
|
+
join11(home2, ".cursor/ai-tracking"),
|
|
48701
|
+
join11(home2, ".gemini"),
|
|
48702
|
+
join11(home2, ".aider"),
|
|
47972
48703
|
// XDG / Linux
|
|
47973
|
-
|
|
47974
|
-
|
|
47975
|
-
|
|
47976
|
-
|
|
47977
|
-
|
|
47978
|
-
|
|
48704
|
+
join11(xdgConfig, "claude/projects"),
|
|
48705
|
+
join11(xdgConfig, "codex/sessions"),
|
|
48706
|
+
join11(xdgConfig, "Cursor/User/workspaceStorage"),
|
|
48707
|
+
join11(xdgConfig, "Code/User/workspaceStorage"),
|
|
48708
|
+
join11(xdgConfig, "Code - Insiders/User/workspaceStorage"),
|
|
48709
|
+
join11(xdgData, "claude/projects"),
|
|
47979
48710
|
// macOS
|
|
47980
48711
|
...platform3() === "darwin" ? [
|
|
47981
|
-
|
|
47982
|
-
|
|
47983
|
-
|
|
47984
|
-
|
|
47985
|
-
|
|
48712
|
+
join11(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
|
|
48713
|
+
join11(home2, "Library/Application Support/Claude"),
|
|
48714
|
+
join11(home2, "Library/Application Support/Code/User/workspaceStorage"),
|
|
48715
|
+
join11(home2, "Library/Application Support/Windsurf/User/workspaceStorage"),
|
|
48716
|
+
join11(home2, "Library/Application Support/Zed")
|
|
47986
48717
|
] : []
|
|
47987
48718
|
];
|
|
47988
48719
|
return Array.from(new Set(candidates)).filter((p) => existsSync10(p));
|
|
@@ -48047,13 +48778,13 @@ var init_watch = __esm({
|
|
|
48047
48778
|
|
|
48048
48779
|
// src/cli.ts
|
|
48049
48780
|
init_src2();
|
|
48050
|
-
init_identity();
|
|
48051
48781
|
init_api();
|
|
48052
48782
|
init_config2();
|
|
48783
|
+
init_identity();
|
|
48053
48784
|
init_scan();
|
|
48054
48785
|
import { spawn } from "child_process";
|
|
48055
48786
|
import { randomBytes } from "crypto";
|
|
48056
|
-
import {
|
|
48787
|
+
import { arch as cpuArch, hostname as hostname2, platform as platform4, release } from "os";
|
|
48057
48788
|
import { createInterface as createInterface3 } from "readline";
|
|
48058
48789
|
|
|
48059
48790
|
// src/service.ts
|
|
@@ -48409,6 +49140,59 @@ function trayStatus() {
|
|
|
48409
49140
|
return exe ? { installed: true, path: exe.replace(/\/Contents\/MacOS\/modelstat-tray$/, "") } : { installed: false, path: null };
|
|
48410
49141
|
}
|
|
48411
49142
|
|
|
49143
|
+
// src/supervise.ts
|
|
49144
|
+
init_lock();
|
|
49145
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
49146
|
+
import { homedir as homedir8 } from "os";
|
|
49147
|
+
import { join as join8 } from "path";
|
|
49148
|
+
var STATUS_FRESH_MS = 12e4;
|
|
49149
|
+
var BOOT_GRACE_MS = 9e4;
|
|
49150
|
+
function decideSupervision(input) {
|
|
49151
|
+
const fresh = input.statusFreshMs ?? STATUS_FRESH_MS;
|
|
49152
|
+
const grace = input.bootGraceMs ?? BOOT_GRACE_MS;
|
|
49153
|
+
if (!input.lock || !input.ownerAlive) return "spawn";
|
|
49154
|
+
if (input.myAgentVersion && input.lock.agentVersion !== "unknown" && input.lock.agentVersion !== input.myAgentVersion) {
|
|
49155
|
+
return "replace";
|
|
49156
|
+
}
|
|
49157
|
+
if (input.statusAgeMs !== null && input.statusAgeMs <= fresh) return "adopt";
|
|
49158
|
+
if (input.lockAgeMs !== null && input.lockAgeMs <= grace) return "adopt";
|
|
49159
|
+
return "replace";
|
|
49160
|
+
}
|
|
49161
|
+
function daemonHealth(opts = {}) {
|
|
49162
|
+
const now = opts.now ?? Date.now();
|
|
49163
|
+
const pidAlive = opts.pidAlive ?? isProcessAlive;
|
|
49164
|
+
const statusPath = opts.statusPath ?? join8(homedir8(), ".modelstat", "last-status.json");
|
|
49165
|
+
const lock = opts.lockPath === void 0 ? readDaemonLock() : readDaemonLock(opts.lockPath);
|
|
49166
|
+
const ownerAlive = lock !== null && pidAlive(lock.pid);
|
|
49167
|
+
const lockAgeMs = lock ? ageMs(lock.startedAt, now) : null;
|
|
49168
|
+
let statusAgeMs = null;
|
|
49169
|
+
try {
|
|
49170
|
+
const raw = readFileSync5(statusPath, "utf8");
|
|
49171
|
+
const writtenAt = JSON.parse(raw).written_at;
|
|
49172
|
+
statusAgeMs = writtenAt ? ageMs(writtenAt, now) : null;
|
|
49173
|
+
} catch {
|
|
49174
|
+
statusAgeMs = null;
|
|
49175
|
+
}
|
|
49176
|
+
return {
|
|
49177
|
+
decision: decideSupervision({
|
|
49178
|
+
lock,
|
|
49179
|
+
ownerAlive,
|
|
49180
|
+
lockAgeMs,
|
|
49181
|
+
statusAgeMs,
|
|
49182
|
+
myAgentVersion: opts.myAgentVersion
|
|
49183
|
+
}),
|
|
49184
|
+
lock,
|
|
49185
|
+
ownerAlive,
|
|
49186
|
+
lockAgeMs,
|
|
49187
|
+
statusAgeMs
|
|
49188
|
+
};
|
|
49189
|
+
}
|
|
49190
|
+
function ageMs(iso, now) {
|
|
49191
|
+
const t = Date.parse(iso);
|
|
49192
|
+
if (Number.isNaN(t)) return null;
|
|
49193
|
+
return Math.max(0, now - t);
|
|
49194
|
+
}
|
|
49195
|
+
|
|
48412
49196
|
// src/cli.ts
|
|
48413
49197
|
async function confirmPrompt(question, defaultYes) {
|
|
48414
49198
|
if (process.stdin.isTTY !== true) return defaultYes;
|
|
@@ -48439,7 +49223,7 @@ function tryOpenBrowser(url) {
|
|
|
48439
49223
|
return false;
|
|
48440
49224
|
}
|
|
48441
49225
|
}
|
|
48442
|
-
var AGENT_VERSION3 = true ? "agent-0.0.
|
|
49226
|
+
var AGENT_VERSION3 = true ? "agent-0.0.47" : "agent-dev";
|
|
48443
49227
|
function osFamily() {
|
|
48444
49228
|
const p = platform4();
|
|
48445
49229
|
if (p === "darwin") return "macos";
|
|
@@ -48482,10 +49266,8 @@ async function cmdSelfRegister() {
|
|
|
48482
49266
|
process.stdout.write(` \x1B[2mgenerated UUIDv7 ${deviceUuid.slice(0, 8)}\u2026\x1B[0m
|
|
48483
49267
|
`);
|
|
48484
49268
|
}
|
|
48485
|
-
process.stdout.write(
|
|
48486
|
-
|
|
48487
|
-
`
|
|
48488
|
-
);
|
|
49269
|
+
process.stdout.write(` \x1B[2m\u2192 POST ${state.apiUrl}/v1/devices/self-register\x1B[0m
|
|
49270
|
+
`);
|
|
48489
49271
|
const res = await selfRegister({
|
|
48490
49272
|
device_uuid: deviceUuid,
|
|
48491
49273
|
fingerprint
|
|
@@ -48499,8 +49281,10 @@ async function cmdSelfRegister() {
|
|
|
48499
49281
|
});
|
|
48500
49282
|
process.stdout.write(` \x1B[32m\u2713\x1B[0m registered device_id=${res.device_id}
|
|
48501
49283
|
`);
|
|
48502
|
-
process.stdout.write(
|
|
48503
|
-
`)
|
|
49284
|
+
process.stdout.write(
|
|
49285
|
+
` \x1B[32m\u2713\x1B[0m secret ${res.secret_prefix}\u2026 (hashed on server, never re-sent)
|
|
49286
|
+
`
|
|
49287
|
+
);
|
|
48504
49288
|
process.stdout.write(` \x1B[32m\u2713\x1B[0m claim code ${res.claim_code}
|
|
48505
49289
|
`);
|
|
48506
49290
|
}
|
|
@@ -48533,10 +49317,8 @@ async function cmdAwaitClaim() {
|
|
|
48533
49317
|
}
|
|
48534
49318
|
function emitEvent(opts, event, fields = {}) {
|
|
48535
49319
|
if (!opts.json) return;
|
|
48536
|
-
process.stdout.write(
|
|
48537
|
-
|
|
48538
|
-
`
|
|
48539
|
-
);
|
|
49320
|
+
process.stdout.write(`${JSON.stringify({ v: 1, ts: Date.now(), event, ...fields })}
|
|
49321
|
+
`);
|
|
48540
49322
|
}
|
|
48541
49323
|
async function cmdConnect(opts) {
|
|
48542
49324
|
const step = (msg) => {
|
|
@@ -48639,9 +49421,7 @@ async function cmdConnect(opts) {
|
|
|
48639
49421
|
error: e.message
|
|
48640
49422
|
});
|
|
48641
49423
|
warn(`couldn't prepare summariser model: ${e.message}`);
|
|
48642
|
-
warn(
|
|
48643
|
-
"the background service will retry the download on its first scan"
|
|
48644
|
-
);
|
|
49424
|
+
warn("the background service will retry the download on its first scan");
|
|
48645
49425
|
}
|
|
48646
49426
|
step("Installing/refreshing background service so the agent survives reboots");
|
|
48647
49427
|
let serviceOk = false;
|
|
@@ -48657,9 +49437,7 @@ async function cmdConnect(opts) {
|
|
|
48657
49437
|
} catch (e) {
|
|
48658
49438
|
emitEvent(opts, "service_install_failed", { error: e.message });
|
|
48659
49439
|
warn(`couldn't install service: ${e.message}`);
|
|
48660
|
-
warn(
|
|
48661
|
-
"the agent will not run in the background \u2014 re-run after fixing the issue"
|
|
48662
|
-
);
|
|
49440
|
+
warn("the agent will not run in the background \u2014 re-run after fixing the issue");
|
|
48663
49441
|
}
|
|
48664
49442
|
step("Detecting installed AI tools and signed-in accounts");
|
|
48665
49443
|
let discovered = null;
|
|
@@ -48677,9 +49455,7 @@ async function cmdConnect(opts) {
|
|
|
48677
49455
|
identities: d.identities.length
|
|
48678
49456
|
};
|
|
48679
49457
|
emitEvent(opts, "discovered", discovered);
|
|
48680
|
-
ok(
|
|
48681
|
-
`${discovered.installations} installs \xB7 ${discovered.identities} accounts`
|
|
48682
|
-
);
|
|
49458
|
+
ok(`${discovered.installations} installs \xB7 ${discovered.identities} accounts`);
|
|
48683
49459
|
} catch (e) {
|
|
48684
49460
|
emitEvent(opts, "discovery_failed", { error: e.message });
|
|
48685
49461
|
warn(`couldn't detect accounts: ${e.message}`);
|
|
@@ -48775,9 +49551,7 @@ async function cmdRescan() {
|
|
|
48775
49551
|
console.log(
|
|
48776
49552
|
`[modelstat] cursors wiped \u2014 next \`modelstat scan\` (or daemon scan cycle) will re-read every JSONL from the start and re-summarise every session at processing version v${PROCESSING_VERSION2}.`
|
|
48777
49553
|
);
|
|
48778
|
-
console.log(
|
|
48779
|
-
" If the daemon is running, kick it with: modelstat stop && modelstat start"
|
|
48780
|
-
);
|
|
49554
|
+
console.log(" If the daemon is running, kick it with: modelstat stop && modelstat start");
|
|
48781
49555
|
}
|
|
48782
49556
|
async function cmdWatch() {
|
|
48783
49557
|
const { watchForever: watchForever2 } = await Promise.resolve().then(() => (init_watch(), watch_exports));
|
|
@@ -48833,10 +49607,10 @@ function fmtTokens(v) {
|
|
|
48833
49607
|
}
|
|
48834
49608
|
async function readLocalStatus() {
|
|
48835
49609
|
try {
|
|
48836
|
-
const { homedir:
|
|
48837
|
-
const { join:
|
|
49610
|
+
const { homedir: homedir10 } = await import("os");
|
|
49611
|
+
const { join: join12 } = await import("path");
|
|
48838
49612
|
const { readFile } = await import("fs/promises");
|
|
48839
|
-
const p =
|
|
49613
|
+
const p = join12(homedir10(), ".modelstat", "last-status.json");
|
|
48840
49614
|
const txt = await readFile(p, "utf8");
|
|
48841
49615
|
return JSON.parse(txt);
|
|
48842
49616
|
} catch {
|
|
@@ -48879,7 +49653,9 @@ async function cmdStats(args) {
|
|
|
48879
49653
|
console.log("device is claimed \u2014 live stats available at:");
|
|
48880
49654
|
console.log(` ${dashboard}`);
|
|
48881
49655
|
if (local) {
|
|
48882
|
-
console.log(
|
|
49656
|
+
console.log(
|
|
49657
|
+
`local agent: ${local.status ?? "?"}${local.message ? ` \xB7 ${local.message}` : ""}`
|
|
49658
|
+
);
|
|
48883
49659
|
const stats = local.stats ?? {};
|
|
48884
49660
|
for (const [k, v] of Object.entries(stats)) console.log(` ${k}: ${v}`);
|
|
48885
49661
|
}
|
|
@@ -48897,7 +49673,9 @@ async function cmdStats(args) {
|
|
|
48897
49673
|
console.log(
|
|
48898
49674
|
`status: ${view.device.agent_status ?? "(unknown)"}${view.device.last_seen_at ? ` \xB7 last seen ${view.device.last_seen_at}` : ""}`
|
|
48899
49675
|
);
|
|
48900
|
-
console.log(
|
|
49676
|
+
console.log(
|
|
49677
|
+
`claim: ${view.status}${view.status === "unclaimed" ? ` (at ${view.claim_url})` : ""}`
|
|
49678
|
+
);
|
|
48901
49679
|
console.log(`sessions: ${fmtInt(view.analyzed.count)}`);
|
|
48902
49680
|
console.log(`tokens: ${fmtTokens(view.analyzed.totalTokens)}`);
|
|
48903
49681
|
console.log(`cost: ${fmtCost(view.analyzed.totalCostUsd)}`);
|
|
@@ -48922,10 +49700,8 @@ async function cmdJobs(args) {
|
|
|
48922
49700
|
if (!jobs && !ledger) {
|
|
48923
49701
|
const dashboard = `${state.apiUrl.replace(/\/$/, "")}/dashboard/jobs`;
|
|
48924
49702
|
if (asJson) {
|
|
48925
|
-
process.stdout.write(
|
|
48926
|
-
|
|
48927
|
-
`
|
|
48928
|
-
);
|
|
49703
|
+
process.stdout.write(`${JSON.stringify({ paired: true, claimed: true, dashboard })}
|
|
49704
|
+
`);
|
|
48929
49705
|
} else {
|
|
48930
49706
|
console.log("device is claimed \u2014 job queue at:");
|
|
48931
49707
|
console.log(` ${dashboard}`);
|
|
@@ -49015,6 +49791,14 @@ async function main() {
|
|
|
49015
49791
|
console.log(`\u2713 runtime staged at ${dest}`);
|
|
49016
49792
|
return;
|
|
49017
49793
|
}
|
|
49794
|
+
case "_daemon-health": {
|
|
49795
|
+
try {
|
|
49796
|
+
console.log(JSON.stringify(daemonHealth({ myAgentVersion: AGENT_VERSION3 })));
|
|
49797
|
+
} catch (e) {
|
|
49798
|
+
console.log(JSON.stringify({ decision: "spawn", error: e.message }));
|
|
49799
|
+
}
|
|
49800
|
+
return;
|
|
49801
|
+
}
|
|
49018
49802
|
// ── Diagnostics / dev one-shots ────────────────────────────
|
|
49019
49803
|
case "status":
|
|
49020
49804
|
return cmdStatus();
|
|
@@ -49042,10 +49826,18 @@ async function main() {
|
|
|
49042
49826
|
return cmdAwaitClaim();
|
|
49043
49827
|
default:
|
|
49044
49828
|
console.log("usage:");
|
|
49045
|
-
console.log(
|
|
49046
|
-
|
|
49047
|
-
|
|
49048
|
-
console.log(
|
|
49829
|
+
console.log(
|
|
49830
|
+
" npx modelstat@latest \u2014 install or upgrade. Registers the device, installs the background service, exits."
|
|
49831
|
+
);
|
|
49832
|
+
console.log(
|
|
49833
|
+
" flags: --json (NDJSON events on stdout), --no-browser, --fresh, -y"
|
|
49834
|
+
);
|
|
49835
|
+
console.log(
|
|
49836
|
+
" npx modelstat@latest remove \u2014 stop and uninstall the background service. Keeps your identity."
|
|
49837
|
+
);
|
|
49838
|
+
console.log(
|
|
49839
|
+
" npx modelstat@latest reinstall \u2014 alias for the default. Useful when you want to be explicit."
|
|
49840
|
+
);
|
|
49049
49841
|
console.log();
|
|
49050
49842
|
console.log("Diagnostics:");
|
|
49051
49843
|
console.log(" npx modelstat@latest status \u2014 show pairing + service state");
|
|
@@ -49056,11 +49848,17 @@ async function main() {
|
|
|
49056
49848
|
console.log();
|
|
49057
49849
|
console.log("Dev / one-shots:");
|
|
49058
49850
|
console.log(" npx modelstat@latest scan \u2014 one-shot parse + upload of local JSONL");
|
|
49059
|
-
console.log(
|
|
49060
|
-
|
|
49851
|
+
console.log(
|
|
49852
|
+
" npx modelstat@latest rescan \u2014 wipe file cursors so the next scan re-reads & re-summarises everything"
|
|
49853
|
+
);
|
|
49854
|
+
console.log(
|
|
49855
|
+
" npx modelstat@latest watch \u2014 continuous (chokidar) with periodic backstop"
|
|
49856
|
+
);
|
|
49061
49857
|
console.log(" npx modelstat@latest discover \u2014 one-shot report of installs/identities");
|
|
49062
49858
|
console.log();
|
|
49063
|
-
console.log(
|
|
49859
|
+
console.log(
|
|
49860
|
+
"Internal (called by launchd/systemd, not by humans): _daemon, start, self-register, await-claim"
|
|
49861
|
+
);
|
|
49064
49862
|
process.exit(1);
|
|
49065
49863
|
}
|
|
49066
49864
|
}
|