@agentmonitors/cli 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +841 -133
- package/dist/index.cjs.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -1218,7 +1218,7 @@ var require_command = __commonJS({
|
|
|
1218
1218
|
init_cjs_shims();
|
|
1219
1219
|
var EventEmitter2 = require("events").EventEmitter;
|
|
1220
1220
|
var childProcess = require("child_process");
|
|
1221
|
-
var
|
|
1221
|
+
var path13 = require("path");
|
|
1222
1222
|
var fs = require("fs");
|
|
1223
1223
|
var process3 = require("process");
|
|
1224
1224
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
@@ -2231,9 +2231,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2231
2231
|
let launchWithNode = false;
|
|
2232
2232
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2233
2233
|
function findFile(baseDir, baseName) {
|
|
2234
|
-
const localBin =
|
|
2234
|
+
const localBin = path13.resolve(baseDir, baseName);
|
|
2235
2235
|
if (fs.existsSync(localBin)) return localBin;
|
|
2236
|
-
if (sourceExt.includes(
|
|
2236
|
+
if (sourceExt.includes(path13.extname(baseName))) return void 0;
|
|
2237
2237
|
const foundExt = sourceExt.find(
|
|
2238
2238
|
(ext2) => fs.existsSync(`${localBin}${ext2}`)
|
|
2239
2239
|
);
|
|
@@ -2251,17 +2251,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2251
2251
|
} catch {
|
|
2252
2252
|
resolvedScriptPath = this._scriptPath;
|
|
2253
2253
|
}
|
|
2254
|
-
executableDir =
|
|
2255
|
-
|
|
2254
|
+
executableDir = path13.resolve(
|
|
2255
|
+
path13.dirname(resolvedScriptPath),
|
|
2256
2256
|
executableDir
|
|
2257
2257
|
);
|
|
2258
2258
|
}
|
|
2259
2259
|
if (executableDir) {
|
|
2260
2260
|
let localFile = findFile(executableDir, executableFile);
|
|
2261
2261
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2262
|
-
const legacyName =
|
|
2262
|
+
const legacyName = path13.basename(
|
|
2263
2263
|
this._scriptPath,
|
|
2264
|
-
|
|
2264
|
+
path13.extname(this._scriptPath)
|
|
2265
2265
|
);
|
|
2266
2266
|
if (legacyName !== this._name) {
|
|
2267
2267
|
localFile = findFile(
|
|
@@ -2272,7 +2272,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2272
2272
|
}
|
|
2273
2273
|
executableFile = localFile || executableFile;
|
|
2274
2274
|
}
|
|
2275
|
-
launchWithNode = sourceExt.includes(
|
|
2275
|
+
launchWithNode = sourceExt.includes(path13.extname(executableFile));
|
|
2276
2276
|
let proc2;
|
|
2277
2277
|
if (process3.platform !== "win32") {
|
|
2278
2278
|
if (launchWithNode) {
|
|
@@ -3187,7 +3187,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3187
3187
|
* @return {Command}
|
|
3188
3188
|
*/
|
|
3189
3189
|
nameFromFilename(filename) {
|
|
3190
|
-
this._name =
|
|
3190
|
+
this._name = path13.basename(filename, path13.extname(filename));
|
|
3191
3191
|
return this;
|
|
3192
3192
|
}
|
|
3193
3193
|
/**
|
|
@@ -3201,9 +3201,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3201
3201
|
* @param {string} [path]
|
|
3202
3202
|
* @return {(string|null|Command)}
|
|
3203
3203
|
*/
|
|
3204
|
-
executableDir(
|
|
3205
|
-
if (
|
|
3206
|
-
this._executableDir =
|
|
3204
|
+
executableDir(path14) {
|
|
3205
|
+
if (path14 === void 0) return this._executableDir;
|
|
3206
|
+
this._executableDir = path14;
|
|
3207
3207
|
return this;
|
|
3208
3208
|
}
|
|
3209
3209
|
/**
|
|
@@ -10226,8 +10226,8 @@ var require_utils2 = __commonJS({
|
|
|
10226
10226
|
}
|
|
10227
10227
|
return ind;
|
|
10228
10228
|
}
|
|
10229
|
-
function removeDotSegments(
|
|
10230
|
-
let input =
|
|
10229
|
+
function removeDotSegments(path13) {
|
|
10230
|
+
let input = path13;
|
|
10231
10231
|
const output = [];
|
|
10232
10232
|
let nextSlash = -1;
|
|
10233
10233
|
let len = 0;
|
|
@@ -10427,8 +10427,8 @@ var require_schemes = __commonJS({
|
|
|
10427
10427
|
wsComponent.secure = void 0;
|
|
10428
10428
|
}
|
|
10429
10429
|
if (wsComponent.resourceName) {
|
|
10430
|
-
const [
|
|
10431
|
-
wsComponent.path =
|
|
10430
|
+
const [path13, query] = wsComponent.resourceName.split("?");
|
|
10431
|
+
wsComponent.path = path13 && path13 !== "/" ? path13 : void 0;
|
|
10432
10432
|
wsComponent.query = query;
|
|
10433
10433
|
wsComponent.resourceName = void 0;
|
|
10434
10434
|
}
|
|
@@ -13851,8 +13851,8 @@ var require_dist = __commonJS({
|
|
|
13851
13851
|
|
|
13852
13852
|
// src/index.ts
|
|
13853
13853
|
init_cjs_shims();
|
|
13854
|
-
var
|
|
13855
|
-
var
|
|
13854
|
+
var import_node_fs7 = require("fs");
|
|
13855
|
+
var import_node_path10 = require("path");
|
|
13856
13856
|
var import_node_url4 = require("url");
|
|
13857
13857
|
|
|
13858
13858
|
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/esm.mjs
|
|
@@ -13971,6 +13971,7 @@ Edit the file to configure your monitor, then run:`);
|
|
|
13971
13971
|
|
|
13972
13972
|
// src/commands/validate.ts
|
|
13973
13973
|
init_cjs_shims();
|
|
13974
|
+
var import_node_fs3 = require("fs");
|
|
13974
13975
|
|
|
13975
13976
|
// ../../libs/core/dist/index.js
|
|
13976
13977
|
init_cjs_shims();
|
|
@@ -14468,8 +14469,8 @@ function getErrorMap() {
|
|
|
14468
14469
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
14469
14470
|
init_cjs_shims();
|
|
14470
14471
|
var makeIssue = (params) => {
|
|
14471
|
-
const { data, path:
|
|
14472
|
-
const fullPath = [...
|
|
14472
|
+
const { data, path: path13, errorMaps, issueData } = params;
|
|
14473
|
+
const fullPath = [...path13, ...issueData.path || []];
|
|
14473
14474
|
const fullIssue = {
|
|
14474
14475
|
...issueData,
|
|
14475
14476
|
path: fullPath
|
|
@@ -14589,11 +14590,11 @@ var errorUtil;
|
|
|
14589
14590
|
|
|
14590
14591
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
14591
14592
|
var ParseInputLazyPath = class {
|
|
14592
|
-
constructor(parent, value,
|
|
14593
|
+
constructor(parent, value, path13, key) {
|
|
14593
14594
|
this._cachedPath = [];
|
|
14594
14595
|
this.parent = parent;
|
|
14595
14596
|
this.data = value;
|
|
14596
|
-
this._path =
|
|
14597
|
+
this._path = path13;
|
|
14597
14598
|
this._key = key;
|
|
14598
14599
|
}
|
|
14599
14600
|
get path() {
|
|
@@ -22659,12 +22660,12 @@ var PathBase = class {
|
|
|
22659
22660
|
/**
|
|
22660
22661
|
* Get the Path object referenced by the string path, resolved from this Path
|
|
22661
22662
|
*/
|
|
22662
|
-
resolve(
|
|
22663
|
-
if (!
|
|
22663
|
+
resolve(path13) {
|
|
22664
|
+
if (!path13) {
|
|
22664
22665
|
return this;
|
|
22665
22666
|
}
|
|
22666
|
-
const rootPath = this.getRootString(
|
|
22667
|
-
const dir =
|
|
22667
|
+
const rootPath = this.getRootString(path13);
|
|
22668
|
+
const dir = path13.substring(rootPath.length);
|
|
22668
22669
|
const dirParts = dir.split(this.splitSep);
|
|
22669
22670
|
const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
|
|
22670
22671
|
return result;
|
|
@@ -23416,8 +23417,8 @@ var PathWin32 = class _PathWin32 extends PathBase {
|
|
|
23416
23417
|
/**
|
|
23417
23418
|
* @internal
|
|
23418
23419
|
*/
|
|
23419
|
-
getRootString(
|
|
23420
|
-
return import_node_path2.win32.parse(
|
|
23420
|
+
getRootString(path13) {
|
|
23421
|
+
return import_node_path2.win32.parse(path13).root;
|
|
23421
23422
|
}
|
|
23422
23423
|
/**
|
|
23423
23424
|
* @internal
|
|
@@ -23463,8 +23464,8 @@ var PathPosix = class _PathPosix extends PathBase {
|
|
|
23463
23464
|
/**
|
|
23464
23465
|
* @internal
|
|
23465
23466
|
*/
|
|
23466
|
-
getRootString(
|
|
23467
|
-
return
|
|
23467
|
+
getRootString(path13) {
|
|
23468
|
+
return path13.startsWith("/") ? "/" : "";
|
|
23468
23469
|
}
|
|
23469
23470
|
/**
|
|
23470
23471
|
* @internal
|
|
@@ -23553,11 +23554,11 @@ var PathScurryBase = class {
|
|
|
23553
23554
|
/**
|
|
23554
23555
|
* Get the depth of a provided path, string, or the cwd
|
|
23555
23556
|
*/
|
|
23556
|
-
depth(
|
|
23557
|
-
if (typeof
|
|
23558
|
-
|
|
23557
|
+
depth(path13 = this.cwd) {
|
|
23558
|
+
if (typeof path13 === "string") {
|
|
23559
|
+
path13 = this.cwd.resolve(path13);
|
|
23559
23560
|
}
|
|
23560
|
-
return
|
|
23561
|
+
return path13.depth();
|
|
23561
23562
|
}
|
|
23562
23563
|
/**
|
|
23563
23564
|
* Return the cache of child entries. Exposed so subclasses can create
|
|
@@ -24044,9 +24045,9 @@ var PathScurryBase = class {
|
|
|
24044
24045
|
process3();
|
|
24045
24046
|
return results;
|
|
24046
24047
|
}
|
|
24047
|
-
chdir(
|
|
24048
|
+
chdir(path13 = this.cwd) {
|
|
24048
24049
|
const oldCwd = this.cwd;
|
|
24049
|
-
this.cwd = typeof
|
|
24050
|
+
this.cwd = typeof path13 === "string" ? this.cwd.resolve(path13) : path13;
|
|
24050
24051
|
this.cwd[setAsCwd](oldCwd);
|
|
24051
24052
|
}
|
|
24052
24053
|
};
|
|
@@ -24408,8 +24409,8 @@ var MatchRecord = class {
|
|
|
24408
24409
|
}
|
|
24409
24410
|
// match, absolute, ifdir
|
|
24410
24411
|
entries() {
|
|
24411
|
-
return [...this.store.entries()].map(([
|
|
24412
|
-
|
|
24412
|
+
return [...this.store.entries()].map(([path13, n]) => [
|
|
24413
|
+
path13,
|
|
24413
24414
|
!!(n & 2),
|
|
24414
24415
|
!!(n & 1)
|
|
24415
24416
|
]);
|
|
@@ -24614,9 +24615,9 @@ var GlobUtil = class {
|
|
|
24614
24615
|
signal;
|
|
24615
24616
|
maxDepth;
|
|
24616
24617
|
includeChildMatches;
|
|
24617
|
-
constructor(patterns,
|
|
24618
|
+
constructor(patterns, path13, opts) {
|
|
24618
24619
|
this.patterns = patterns;
|
|
24619
|
-
this.path =
|
|
24620
|
+
this.path = path13;
|
|
24620
24621
|
this.opts = opts;
|
|
24621
24622
|
this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
|
|
24622
24623
|
this.includeChildMatches = opts.includeChildMatches !== false;
|
|
@@ -24635,11 +24636,11 @@ var GlobUtil = class {
|
|
|
24635
24636
|
});
|
|
24636
24637
|
}
|
|
24637
24638
|
}
|
|
24638
|
-
#ignored(
|
|
24639
|
-
return this.seen.has(
|
|
24639
|
+
#ignored(path13) {
|
|
24640
|
+
return this.seen.has(path13) || !!this.#ignore?.ignored?.(path13);
|
|
24640
24641
|
}
|
|
24641
|
-
#childrenIgnored(
|
|
24642
|
-
return !!this.#ignore?.childrenIgnored?.(
|
|
24642
|
+
#childrenIgnored(path13) {
|
|
24643
|
+
return !!this.#ignore?.childrenIgnored?.(path13);
|
|
24643
24644
|
}
|
|
24644
24645
|
// backpressure mechanism
|
|
24645
24646
|
pause() {
|
|
@@ -24854,8 +24855,8 @@ var GlobUtil = class {
|
|
|
24854
24855
|
};
|
|
24855
24856
|
var GlobWalker = class extends GlobUtil {
|
|
24856
24857
|
matches = /* @__PURE__ */ new Set();
|
|
24857
|
-
constructor(patterns,
|
|
24858
|
-
super(patterns,
|
|
24858
|
+
constructor(patterns, path13, opts) {
|
|
24859
|
+
super(patterns, path13, opts);
|
|
24859
24860
|
}
|
|
24860
24861
|
matchEmit(e) {
|
|
24861
24862
|
this.matches.add(e);
|
|
@@ -24892,8 +24893,8 @@ var GlobWalker = class extends GlobUtil {
|
|
|
24892
24893
|
};
|
|
24893
24894
|
var GlobStream = class extends GlobUtil {
|
|
24894
24895
|
results;
|
|
24895
|
-
constructor(patterns,
|
|
24896
|
-
super(patterns,
|
|
24896
|
+
constructor(patterns, path13, opts) {
|
|
24897
|
+
super(patterns, path13, opts);
|
|
24897
24898
|
this.results = new Minipass({
|
|
24898
24899
|
signal: this.signal,
|
|
24899
24900
|
objectMode: true
|
|
@@ -26401,7 +26402,7 @@ Subquery.prototype.getSQL = function() {
|
|
|
26401
26402
|
function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
26402
26403
|
const nullifyMap = {};
|
|
26403
26404
|
const result = columns.reduce(
|
|
26404
|
-
(result2, { path:
|
|
26405
|
+
(result2, { path: path13, field }, columnIndex) => {
|
|
26405
26406
|
let decoder;
|
|
26406
26407
|
if (is(field, Column)) {
|
|
26407
26408
|
decoder = field;
|
|
@@ -26413,8 +26414,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
26413
26414
|
decoder = field.sql.decoder;
|
|
26414
26415
|
}
|
|
26415
26416
|
let node = result2;
|
|
26416
|
-
for (const [pathChunkIndex, pathChunk] of
|
|
26417
|
-
if (pathChunkIndex <
|
|
26417
|
+
for (const [pathChunkIndex, pathChunk] of path13.entries()) {
|
|
26418
|
+
if (pathChunkIndex < path13.length - 1) {
|
|
26418
26419
|
if (!(pathChunk in node)) {
|
|
26419
26420
|
node[pathChunk] = {};
|
|
26420
26421
|
}
|
|
@@ -26422,8 +26423,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
26422
26423
|
} else {
|
|
26423
26424
|
const rawValue = row[columnIndex];
|
|
26424
26425
|
const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
|
|
26425
|
-
if (joinsNotNullableMap && is(field, Column) &&
|
|
26426
|
-
const objectName =
|
|
26426
|
+
if (joinsNotNullableMap && is(field, Column) && path13.length === 2) {
|
|
26427
|
+
const objectName = path13[0];
|
|
26427
26428
|
if (!(objectName in nullifyMap)) {
|
|
26428
26429
|
nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
|
|
26429
26430
|
} else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
|
|
@@ -31375,14 +31376,19 @@ function assertValidSegment(path72, segment) {
|
|
|
31375
31376
|
);
|
|
31376
31377
|
}
|
|
31377
31378
|
}
|
|
31378
|
-
function
|
|
31379
|
-
if (path72 === "$") return
|
|
31380
|
-
if (
|
|
31379
|
+
function normalizeDottedPath(path72) {
|
|
31380
|
+
if (path72 === "$" || path72.startsWith("$.")) return path72;
|
|
31381
|
+
if (path72.startsWith("$")) {
|
|
31381
31382
|
throw new Error(
|
|
31382
|
-
`Invalid collection path "${path72}":
|
|
31383
|
+
`Invalid collection path "${path72}": a path beginning with "$" must be explicit-root form ("$" or "$.field"). Write "${path72.slice(1)}" for a bare root-relative path, or "$.${path72.slice(1)}" for explicit-root form.`
|
|
31383
31384
|
);
|
|
31384
31385
|
}
|
|
31385
|
-
|
|
31386
|
+
return `$.${path72}`;
|
|
31387
|
+
}
|
|
31388
|
+
function resolveDottedPath(root, path72) {
|
|
31389
|
+
const normalizedPath = normalizeDottedPath(path72);
|
|
31390
|
+
if (normalizedPath === "$") return root;
|
|
31391
|
+
const segments = normalizedPath.slice(2).split(".");
|
|
31386
31392
|
let current = root;
|
|
31387
31393
|
for (const segment of segments) {
|
|
31388
31394
|
if (segment.length === 0) {
|
|
@@ -31397,13 +31403,9 @@ function resolveDottedPath(root, path72) {
|
|
|
31397
31403
|
return current;
|
|
31398
31404
|
}
|
|
31399
31405
|
function removeDottedPath(value, path72) {
|
|
31400
|
-
|
|
31401
|
-
if (
|
|
31402
|
-
|
|
31403
|
-
`Invalid ignore-paths entry "${path72}": must start with "$." (e.g. "$.fetchedAt")`
|
|
31404
|
-
);
|
|
31405
|
-
}
|
|
31406
|
-
const segments = path72.slice(2).split(".");
|
|
31406
|
+
const normalizedPath = normalizeDottedPath(path72);
|
|
31407
|
+
if (normalizedPath === "$") return;
|
|
31408
|
+
const segments = normalizedPath.slice(2).split(".");
|
|
31407
31409
|
let current = value;
|
|
31408
31410
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
31409
31411
|
const segment = segments[i];
|
|
@@ -31677,6 +31679,11 @@ function rowToEvent(row) {
|
|
|
31677
31679
|
createdAt: row.createdAt
|
|
31678
31680
|
};
|
|
31679
31681
|
}
|
|
31682
|
+
function deliveryStateForRow(row) {
|
|
31683
|
+
if (row.acknowledgedAt) return "acknowledged";
|
|
31684
|
+
if (row.firstNotifiedAt) return "claimed";
|
|
31685
|
+
return "unread";
|
|
31686
|
+
}
|
|
31680
31687
|
function scopeMatches(eventScope, requested) {
|
|
31681
31688
|
if (!requested) return true;
|
|
31682
31689
|
for (const [key, value] of Object.entries(requested)) {
|
|
@@ -31844,6 +31851,13 @@ var RuntimeStore = class {
|
|
|
31844
31851
|
if (query.objectKey)
|
|
31845
31852
|
conditions.push(eq(monitorEvents.objectKey, query.objectKey));
|
|
31846
31853
|
if (query.since) conditions.push(gt(monitorEvents.createdAt, query.since));
|
|
31854
|
+
if (query.workspacePath !== void 0) {
|
|
31855
|
+
const workspaceCondition = or(
|
|
31856
|
+
eq(monitorEvents.workspacePath, query.workspacePath),
|
|
31857
|
+
isNull(monitorEvents.workspacePath)
|
|
31858
|
+
);
|
|
31859
|
+
if (workspaceCondition) conditions.push(workspaceCondition);
|
|
31860
|
+
}
|
|
31847
31861
|
let rows = query.sessionId ? asInternalDb2(this.db).select({
|
|
31848
31862
|
event: monitorEvents,
|
|
31849
31863
|
state: sessionEventState
|
|
@@ -31918,6 +31932,37 @@ var RuntimeStore = class {
|
|
|
31918
31932
|
createdAt: row.createdAt
|
|
31919
31933
|
}));
|
|
31920
31934
|
}
|
|
31935
|
+
listDeliveryProjectionsForMonitor(monitorId, workspacePath) {
|
|
31936
|
+
const conditions = [eq(monitorEvents.monitorId, monitorId)];
|
|
31937
|
+
if (workspacePath !== void 0) {
|
|
31938
|
+
const workspaceCondition = or(
|
|
31939
|
+
eq(agentSessions.workspacePath, workspacePath),
|
|
31940
|
+
isNull(agentSessions.workspacePath)
|
|
31941
|
+
);
|
|
31942
|
+
if (workspaceCondition) conditions.push(workspaceCondition);
|
|
31943
|
+
}
|
|
31944
|
+
const rows = asInternalDb2(this.db).select({
|
|
31945
|
+
event: monitorEvents,
|
|
31946
|
+
state: sessionEventState,
|
|
31947
|
+
session: agentSessions
|
|
31948
|
+
}).from(sessionEventState).innerJoin(monitorEvents, eq(sessionEventState.eventId, monitorEvents.id)).innerJoin(
|
|
31949
|
+
agentSessions,
|
|
31950
|
+
eq(sessionEventState.sessionId, agentSessions.id)
|
|
31951
|
+
).where(and(...conditions)).orderBy(desc(monitorEvents.createdAt)).all();
|
|
31952
|
+
return rows.map(({ event, state, session }) => ({
|
|
31953
|
+
eventId: event.id,
|
|
31954
|
+
sessionId: session.id,
|
|
31955
|
+
sessionRole: session.role,
|
|
31956
|
+
sessionStatus: session.status,
|
|
31957
|
+
deliveryState: deliveryStateForRow(state),
|
|
31958
|
+
workspacePath: session.workspacePath ?? null,
|
|
31959
|
+
createdAt: state.createdAt,
|
|
31960
|
+
...state.firstNotifiedAt ? { firstNotifiedAt: state.firstNotifiedAt } : {},
|
|
31961
|
+
...state.lastClaimAt ? { lastClaimAt: state.lastClaimAt } : {},
|
|
31962
|
+
...state.lastClaimLifecycle ? { lastClaimLifecycle: state.lastClaimLifecycle } : {},
|
|
31963
|
+
...state.acknowledgedAt ? { acknowledgedAt: state.acknowledgedAt } : {}
|
|
31964
|
+
}));
|
|
31965
|
+
}
|
|
31921
31966
|
sessionsForWorkspace(workspacePath) {
|
|
31922
31967
|
return asInternalDb2(this.db).select().from(agentSessions).where(
|
|
31923
31968
|
workspacePath == null ? isNull(agentSessions.workspacePath) : or(
|
|
@@ -32025,6 +32070,14 @@ var DEFAULT_FILE_FINGERPRINT_POLL_MS = 3e4;
|
|
|
32025
32070
|
var DEFAULT_API_POLL_MS = 3e5;
|
|
32026
32071
|
var DEFAULT_HIGH_URGENCY_SETTLE_MS = 15e3;
|
|
32027
32072
|
var MAX_RECAP_EVENTS = 10;
|
|
32073
|
+
var EXPLAIN_STAGE_LABELS = {
|
|
32074
|
+
definition: "Definition",
|
|
32075
|
+
scheduling: "Scheduling",
|
|
32076
|
+
observation: "Observation",
|
|
32077
|
+
notify: "Notify state",
|
|
32078
|
+
materialization: "Materialization",
|
|
32079
|
+
delivery: "Projection and delivery"
|
|
32080
|
+
};
|
|
32028
32081
|
function watchConfig(watch) {
|
|
32029
32082
|
const { type: _type, ...config2 } = watch;
|
|
32030
32083
|
return config2;
|
|
@@ -32043,6 +32096,28 @@ function writeJsonAtomic(filePath2, payload) {
|
|
|
32043
32096
|
(0, import_fs3.writeFileSync)(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
32044
32097
|
(0, import_fs3.renameSync)(tmpPath, filePath2);
|
|
32045
32098
|
}
|
|
32099
|
+
function monitorIdFromFilePath(filePath2) {
|
|
32100
|
+
const base = import_path4.default.basename(filePath2);
|
|
32101
|
+
return base === "MONITOR.md" ? import_path4.default.basename(import_path4.default.dirname(filePath2)) : import_path4.default.parse(filePath2).name;
|
|
32102
|
+
}
|
|
32103
|
+
function explainStage(id, status, reason, details) {
|
|
32104
|
+
return {
|
|
32105
|
+
id,
|
|
32106
|
+
label: EXPLAIN_STAGE_LABELS[id],
|
|
32107
|
+
status,
|
|
32108
|
+
reason,
|
|
32109
|
+
...details ? { details } : {}
|
|
32110
|
+
};
|
|
32111
|
+
}
|
|
32112
|
+
function explainVerdict(stages) {
|
|
32113
|
+
const stopped = stages.find((stage2) => stage2.status !== "ok");
|
|
32114
|
+
const stage = stopped ?? stages[stages.length - 1];
|
|
32115
|
+
return {
|
|
32116
|
+
status: stage?.status ?? "ok",
|
|
32117
|
+
stage: stage?.id ?? "delivery",
|
|
32118
|
+
reason: stage?.reason ?? "Monitor delivered successfully."
|
|
32119
|
+
};
|
|
32120
|
+
}
|
|
32046
32121
|
function serializeObservation(monitor, observation, observedAt) {
|
|
32047
32122
|
return { monitor, observation, observedAt };
|
|
32048
32123
|
}
|
|
@@ -32157,6 +32232,320 @@ var AgentMonitorRuntime = class {
|
|
|
32157
32232
|
listObservationHistory(query = {}) {
|
|
32158
32233
|
return this.store.listObservationHistory(query);
|
|
32159
32234
|
}
|
|
32235
|
+
async explainMonitor(input) {
|
|
32236
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
32237
|
+
const historyLimit = input.historyLimit ?? 10;
|
|
32238
|
+
const eventLimit = input.eventLimit ?? 10;
|
|
32239
|
+
const stages = [];
|
|
32240
|
+
const scan = await scanMonitors(input.monitorsDir);
|
|
32241
|
+
const parseError = scan.errors.find(
|
|
32242
|
+
(error2) => monitorIdFromFilePath(error2.filePath) === input.monitorId
|
|
32243
|
+
);
|
|
32244
|
+
const monitor = scan.monitors.find(
|
|
32245
|
+
(candidate) => candidate.monitor.id === input.monitorId
|
|
32246
|
+
)?.monitor;
|
|
32247
|
+
const duplicate = scan.duplicateIds.find(
|
|
32248
|
+
(candidate) => candidate.id === input.monitorId
|
|
32249
|
+
);
|
|
32250
|
+
if (parseError) {
|
|
32251
|
+
stages.push(
|
|
32252
|
+
explainStage(
|
|
32253
|
+
"definition",
|
|
32254
|
+
"failure",
|
|
32255
|
+
`MONITOR.md failed to parse or validate: ${parseError.error}`,
|
|
32256
|
+
{ filePath: parseError.filePath }
|
|
32257
|
+
)
|
|
32258
|
+
);
|
|
32259
|
+
return {
|
|
32260
|
+
monitorId: input.monitorId,
|
|
32261
|
+
generatedAt: now,
|
|
32262
|
+
stages,
|
|
32263
|
+
verdict: explainVerdict(stages),
|
|
32264
|
+
observations: [],
|
|
32265
|
+
events: [],
|
|
32266
|
+
projections: [],
|
|
32267
|
+
leadSessions: []
|
|
32268
|
+
};
|
|
32269
|
+
}
|
|
32270
|
+
if (!monitor) {
|
|
32271
|
+
stages.push(
|
|
32272
|
+
explainStage(
|
|
32273
|
+
"definition",
|
|
32274
|
+
"failure",
|
|
32275
|
+
`Monitor "${input.monitorId}" was not found in ${input.monitorsDir}.`
|
|
32276
|
+
)
|
|
32277
|
+
);
|
|
32278
|
+
return {
|
|
32279
|
+
monitorId: input.monitorId,
|
|
32280
|
+
generatedAt: now,
|
|
32281
|
+
stages,
|
|
32282
|
+
verdict: explainVerdict(stages),
|
|
32283
|
+
observations: [],
|
|
32284
|
+
events: [],
|
|
32285
|
+
projections: [],
|
|
32286
|
+
leadSessions: []
|
|
32287
|
+
};
|
|
32288
|
+
}
|
|
32289
|
+
if (duplicate) {
|
|
32290
|
+
stages.push(
|
|
32291
|
+
explainStage(
|
|
32292
|
+
"definition",
|
|
32293
|
+
"failure",
|
|
32294
|
+
`Monitor id "${input.monitorId}" is duplicated across ${String(duplicate.filePaths.length)} files.`,
|
|
32295
|
+
{ filePaths: duplicate.filePaths }
|
|
32296
|
+
)
|
|
32297
|
+
);
|
|
32298
|
+
return {
|
|
32299
|
+
monitorId: input.monitorId,
|
|
32300
|
+
generatedAt: now,
|
|
32301
|
+
monitor: {
|
|
32302
|
+
id: monitor.id,
|
|
32303
|
+
displayName: monitor.displayName,
|
|
32304
|
+
filePath: monitor.filePath,
|
|
32305
|
+
sourceName: monitor.frontmatter.watch.type,
|
|
32306
|
+
urgency: monitor.frontmatter.urgency
|
|
32307
|
+
},
|
|
32308
|
+
stages,
|
|
32309
|
+
verdict: explainVerdict(stages),
|
|
32310
|
+
observations: [],
|
|
32311
|
+
events: [],
|
|
32312
|
+
projections: [],
|
|
32313
|
+
leadSessions: []
|
|
32314
|
+
};
|
|
32315
|
+
}
|
|
32316
|
+
const sourceName = monitor.frontmatter.watch.type;
|
|
32317
|
+
const source6 = this.registry.get(sourceName);
|
|
32318
|
+
const scopeErrors = source6 ? validateScope(
|
|
32319
|
+
watchConfig(monitor.frontmatter.watch),
|
|
32320
|
+
source6.scopeSchema
|
|
32321
|
+
) : [`Unknown source "${sourceName}".`];
|
|
32322
|
+
if (scopeErrors.length > 0) {
|
|
32323
|
+
stages.push(
|
|
32324
|
+
explainStage(
|
|
32325
|
+
"definition",
|
|
32326
|
+
"failure",
|
|
32327
|
+
`Monitor definition is invalid: ${scopeErrors.join("; ")}`,
|
|
32328
|
+
{ filePath: monitor.filePath, sourceName }
|
|
32329
|
+
)
|
|
32330
|
+
);
|
|
32331
|
+
return {
|
|
32332
|
+
monitorId: input.monitorId,
|
|
32333
|
+
generatedAt: now,
|
|
32334
|
+
monitor: {
|
|
32335
|
+
id: monitor.id,
|
|
32336
|
+
displayName: monitor.displayName,
|
|
32337
|
+
filePath: monitor.filePath,
|
|
32338
|
+
sourceName,
|
|
32339
|
+
urgency: monitor.frontmatter.urgency
|
|
32340
|
+
},
|
|
32341
|
+
stages,
|
|
32342
|
+
verdict: explainVerdict(stages),
|
|
32343
|
+
observations: [],
|
|
32344
|
+
events: [],
|
|
32345
|
+
projections: [],
|
|
32346
|
+
leadSessions: []
|
|
32347
|
+
};
|
|
32348
|
+
}
|
|
32349
|
+
stages.push(
|
|
32350
|
+
explainStage("definition", "ok", "Monitor definition is valid.", {
|
|
32351
|
+
filePath: monitor.filePath,
|
|
32352
|
+
sourceName
|
|
32353
|
+
})
|
|
32354
|
+
);
|
|
32355
|
+
const runtimeState = this.store.getMonitorState(input.monitorId);
|
|
32356
|
+
const schedule = this.scheduleForMonitor(monitor, now);
|
|
32357
|
+
const lastObservationAt = runtimeState.lastObservationAt;
|
|
32358
|
+
const nextDueAt = schedule.due ? now : new Date(
|
|
32359
|
+
(lastObservationAt?.getTime() ?? now.getTime()) + schedule.nextPollMs
|
|
32360
|
+
);
|
|
32361
|
+
stages.push(
|
|
32362
|
+
explainStage(
|
|
32363
|
+
"scheduling",
|
|
32364
|
+
"ok",
|
|
32365
|
+
lastObservationAt ? `Last tick completed at ${lastObservationAt.toISOString()}; next due ${schedule.due ? "now" : nextDueAt.toISOString()}.` : "No completed tick is recorded yet; the monitor is due on the next daemon tick.",
|
|
32366
|
+
{
|
|
32367
|
+
due: schedule.due,
|
|
32368
|
+
nextPollMs: schedule.nextPollMs,
|
|
32369
|
+
nextDueAt: nextDueAt.toISOString(),
|
|
32370
|
+
...lastObservationAt ? { lastObservationAt: lastObservationAt.toISOString() } : {}
|
|
32371
|
+
}
|
|
32372
|
+
)
|
|
32373
|
+
);
|
|
32374
|
+
const observations = this.store.listObservationHistory({
|
|
32375
|
+
monitorId: input.monitorId,
|
|
32376
|
+
limit: historyLimit
|
|
32377
|
+
});
|
|
32378
|
+
const latestObservation = observations[0];
|
|
32379
|
+
if (!latestObservation) {
|
|
32380
|
+
stages.push(
|
|
32381
|
+
explainStage(
|
|
32382
|
+
"observation",
|
|
32383
|
+
"failure",
|
|
32384
|
+
"No observation history has been recorded for this monitor."
|
|
32385
|
+
)
|
|
32386
|
+
);
|
|
32387
|
+
} else if (latestObservation.result === "errored") {
|
|
32388
|
+
stages.push(
|
|
32389
|
+
explainStage(
|
|
32390
|
+
"observation",
|
|
32391
|
+
"failure",
|
|
32392
|
+
"The most recent source observation errored.",
|
|
32393
|
+
latestObservation.observationData
|
|
32394
|
+
)
|
|
32395
|
+
);
|
|
32396
|
+
} else if (latestObservation.result === "no-change") {
|
|
32397
|
+
stages.push(
|
|
32398
|
+
explainStage(
|
|
32399
|
+
"observation",
|
|
32400
|
+
"healthy",
|
|
32401
|
+
"Source ran, observed 0 changes \u2014 your watched target genuinely hasn\u2019t changed (not a bug).",
|
|
32402
|
+
latestObservation.observationData
|
|
32403
|
+
)
|
|
32404
|
+
);
|
|
32405
|
+
} else if (latestObservation.result === "rebaselined") {
|
|
32406
|
+
stages.push(
|
|
32407
|
+
explainStage(
|
|
32408
|
+
"observation",
|
|
32409
|
+
"healthy",
|
|
32410
|
+
"Source rebaselined and emitted no change \u2014 your watched target is being tracked, nothing to report (not a bug).",
|
|
32411
|
+
latestObservation.observationData
|
|
32412
|
+
)
|
|
32413
|
+
);
|
|
32414
|
+
} else {
|
|
32415
|
+
stages.push(
|
|
32416
|
+
explainStage(
|
|
32417
|
+
"observation",
|
|
32418
|
+
"ok",
|
|
32419
|
+
`The latest observation outcome was ${latestObservation.result}.`,
|
|
32420
|
+
latestObservation.observationData
|
|
32421
|
+
)
|
|
32422
|
+
);
|
|
32423
|
+
}
|
|
32424
|
+
const observationHealthy = latestObservation?.result === "no-change" || latestObservation?.result === "rebaselined";
|
|
32425
|
+
const notifyState = runtimeState.notifyState;
|
|
32426
|
+
const pendingDebounce = notifyState.pendingDebounce;
|
|
32427
|
+
const suppressedUntil = notifyState.suppressedUntil ? new Date(notifyState.suppressedUntil) : null;
|
|
32428
|
+
if (pendingDebounce && new Date(pendingDebounce.dueAt) > now) {
|
|
32429
|
+
stages.push(
|
|
32430
|
+
explainStage(
|
|
32431
|
+
"notify",
|
|
32432
|
+
"pending",
|
|
32433
|
+
`debounce is holding ${String(pendingDebounce.observations.length)} observation(s) until ${pendingDebounce.dueAt}.`,
|
|
32434
|
+
{
|
|
32435
|
+
dueAt: pendingDebounce.dueAt,
|
|
32436
|
+
observations: pendingDebounce.observations.length
|
|
32437
|
+
}
|
|
32438
|
+
)
|
|
32439
|
+
);
|
|
32440
|
+
} else if (suppressedUntil && suppressedUntil > now) {
|
|
32441
|
+
stages.push(
|
|
32442
|
+
explainStage(
|
|
32443
|
+
"notify",
|
|
32444
|
+
"pending",
|
|
32445
|
+
`throttle is suppressing new notifications until ${suppressedUntil.toISOString()}.`,
|
|
32446
|
+
{ suppressedUntil: suppressedUntil.toISOString() }
|
|
32447
|
+
)
|
|
32448
|
+
);
|
|
32449
|
+
} else {
|
|
32450
|
+
stages.push(
|
|
32451
|
+
explainStage(
|
|
32452
|
+
"notify",
|
|
32453
|
+
"ok",
|
|
32454
|
+
"No debounce or throttle hold is currently active."
|
|
32455
|
+
)
|
|
32456
|
+
);
|
|
32457
|
+
}
|
|
32458
|
+
const events = this.store.listEvents({
|
|
32459
|
+
monitorId: input.monitorId,
|
|
32460
|
+
// Scope to the explained workspace so a same-id monitor in another
|
|
32461
|
+
// workspace cannot leak its events into this report (issue #94 review).
|
|
32462
|
+
...input.workspacePath !== void 0 ? { workspacePath: input.workspacePath } : {}
|
|
32463
|
+
}).slice(0, eventLimit);
|
|
32464
|
+
if (events.length === 0) {
|
|
32465
|
+
stages.push(
|
|
32466
|
+
observationHealthy ? explainStage(
|
|
32467
|
+
"materialization",
|
|
32468
|
+
"healthy",
|
|
32469
|
+
"No events materialized \u2014 expected, because the source observed no changes (not a bug)."
|
|
32470
|
+
) : explainStage(
|
|
32471
|
+
"materialization",
|
|
32472
|
+
"failure",
|
|
32473
|
+
"No monitor_events rows exist for this monitor."
|
|
32474
|
+
)
|
|
32475
|
+
);
|
|
32476
|
+
} else {
|
|
32477
|
+
stages.push(
|
|
32478
|
+
explainStage(
|
|
32479
|
+
"materialization",
|
|
32480
|
+
"ok",
|
|
32481
|
+
`${String(events.length)} recent monitor_events row(s) found.`,
|
|
32482
|
+
{ eventIds: events.map((event) => event.id) }
|
|
32483
|
+
)
|
|
32484
|
+
);
|
|
32485
|
+
}
|
|
32486
|
+
const projections = this.store.listDeliveryProjectionsForMonitor(
|
|
32487
|
+
input.monitorId,
|
|
32488
|
+
// Scope to the explained workspace's sessions (plus global sessions) so
|
|
32489
|
+
// projections from other workspaces are not overcounted (issue #94 review).
|
|
32490
|
+
input.workspacePath
|
|
32491
|
+
);
|
|
32492
|
+
const leadSessions = this.store.sessionsForWorkspace(input.workspacePath).filter((session) => session.role === "lead");
|
|
32493
|
+
if (events.length > 0 && projections.length === 0) {
|
|
32494
|
+
stages.push(
|
|
32495
|
+
explainStage(
|
|
32496
|
+
"delivery",
|
|
32497
|
+
"failure",
|
|
32498
|
+
leadSessions.length === 0 ? "No lead session is registered for this workspace, so events were not projected." : "No session_event_state projections exist for the materialized events.",
|
|
32499
|
+
{ leadSessions: leadSessions.map((session) => session.id) }
|
|
32500
|
+
)
|
|
32501
|
+
);
|
|
32502
|
+
} else if (events.length > 0) {
|
|
32503
|
+
const counts = projections.reduce(
|
|
32504
|
+
(acc, projection) => {
|
|
32505
|
+
acc[projection.deliveryState] = (acc[projection.deliveryState] ?? 0) + 1;
|
|
32506
|
+
return acc;
|
|
32507
|
+
},
|
|
32508
|
+
{}
|
|
32509
|
+
);
|
|
32510
|
+
stages.push(
|
|
32511
|
+
explainStage(
|
|
32512
|
+
"delivery",
|
|
32513
|
+
"ok",
|
|
32514
|
+
`Events are projected to lead sessions (${Object.entries(counts).map(([state, count]) => `${state}: ${String(count)}`).join(", ")}).`,
|
|
32515
|
+
counts
|
|
32516
|
+
)
|
|
32517
|
+
);
|
|
32518
|
+
} else {
|
|
32519
|
+
stages.push(
|
|
32520
|
+
observationHealthy ? explainStage(
|
|
32521
|
+
"delivery",
|
|
32522
|
+
"healthy",
|
|
32523
|
+
"Nothing to deliver \u2014 the source observed no changes, so there is no signal to project (not a bug)."
|
|
32524
|
+
) : explainStage(
|
|
32525
|
+
"delivery",
|
|
32526
|
+
"pending",
|
|
32527
|
+
"Delivery has not started because no event has materialized yet."
|
|
32528
|
+
)
|
|
32529
|
+
);
|
|
32530
|
+
}
|
|
32531
|
+
return {
|
|
32532
|
+
monitorId: input.monitorId,
|
|
32533
|
+
generatedAt: now,
|
|
32534
|
+
monitor: {
|
|
32535
|
+
id: monitor.id,
|
|
32536
|
+
displayName: monitor.displayName,
|
|
32537
|
+
filePath: monitor.filePath,
|
|
32538
|
+
sourceName,
|
|
32539
|
+
urgency: monitor.frontmatter.urgency
|
|
32540
|
+
},
|
|
32541
|
+
stages,
|
|
32542
|
+
verdict: explainVerdict(stages),
|
|
32543
|
+
observations,
|
|
32544
|
+
events,
|
|
32545
|
+
projections,
|
|
32546
|
+
leadSessions
|
|
32547
|
+
};
|
|
32548
|
+
}
|
|
32160
32549
|
acknowledgeSession(sessionId, eventIds) {
|
|
32161
32550
|
const ids = eventIds ?? this.store.unreadEventsForSession(sessionId).map((e) => e.id);
|
|
32162
32551
|
this.store.acknowledgeEvents(sessionId, ids);
|
|
@@ -32969,7 +33358,7 @@ var scopeSchema2 = {
|
|
|
32969
33358
|
properties: {
|
|
32970
33359
|
path: {
|
|
32971
33360
|
type: "string",
|
|
32972
|
-
description: 'Dotted
|
|
33361
|
+
description: 'Dotted path to the array within the parsed JSON (e.g. "tasks" or "$.tasks")'
|
|
32973
33362
|
},
|
|
32974
33363
|
key: {
|
|
32975
33364
|
type: "string",
|
|
@@ -32978,7 +33367,7 @@ var scopeSchema2 = {
|
|
|
32978
33367
|
"ignore-paths": {
|
|
32979
33368
|
type: "array",
|
|
32980
33369
|
items: { type: "string" },
|
|
32981
|
-
description:
|
|
33370
|
+
description: 'Dotted paths (relative to each element, e.g. "fetchedAt" or "$.fetchedAt") removed before comparison'
|
|
32982
33371
|
}
|
|
32983
33372
|
},
|
|
32984
33373
|
required: ["path", "key"]
|
|
@@ -33076,6 +33465,12 @@ function parseScopeConfig3(config2) {
|
|
|
33076
33465
|
const cd = config2["change-detection"];
|
|
33077
33466
|
const rawStrategy = cd?.strategy;
|
|
33078
33467
|
const strategy = rawStrategy === "json-diff" || rawStrategy === "exit-code" ? rawStrategy : "text-diff";
|
|
33468
|
+
const ignorePaths = parseTopLevelIgnorePaths(cd);
|
|
33469
|
+
if (ignorePaths.length > 0 && strategy !== "json-diff") {
|
|
33470
|
+
throw new Error(
|
|
33471
|
+
"change-detection.ignore-paths requires strategy: json-diff"
|
|
33472
|
+
);
|
|
33473
|
+
}
|
|
33079
33474
|
const collection = parseKeyedCollectionConfig(config2["change-detection"]);
|
|
33080
33475
|
if (collection && strategy !== "json-diff") {
|
|
33081
33476
|
throw new Error("change-detection.collection requires strategy: json-diff");
|
|
@@ -33087,7 +33482,16 @@ function parseScopeConfig3(config2) {
|
|
|
33087
33482
|
const timeoutMs = typeof rawTimeout === "string" ? parseDuration(rawTimeout) : DEFAULT_TIMEOUT_MS;
|
|
33088
33483
|
const key = config2["key"];
|
|
33089
33484
|
const objectKey = typeof key === "string" && key.length > 0 ? key : command.join(" ");
|
|
33090
|
-
return {
|
|
33485
|
+
return {
|
|
33486
|
+
command,
|
|
33487
|
+
cwd,
|
|
33488
|
+
env,
|
|
33489
|
+
timeoutMs,
|
|
33490
|
+
objectKey,
|
|
33491
|
+
strategy,
|
|
33492
|
+
ignorePaths,
|
|
33493
|
+
collection
|
|
33494
|
+
};
|
|
33091
33495
|
}
|
|
33092
33496
|
async function runCommand(scope) {
|
|
33093
33497
|
return new Promise((resolve) => {
|
|
@@ -33173,18 +33577,75 @@ function sortKeys3(value) {
|
|
|
33173
33577
|
}
|
|
33174
33578
|
return value;
|
|
33175
33579
|
}
|
|
33176
|
-
function
|
|
33580
|
+
function parseTopLevelIgnorePaths(changeDetection) {
|
|
33581
|
+
const rawIgnorePaths = changeDetection?.["ignore-paths"];
|
|
33582
|
+
if (rawIgnorePaths === void 0) return [];
|
|
33583
|
+
if (!Array.isArray(rawIgnorePaths) || !rawIgnorePaths.every((entry) => typeof entry === "string")) {
|
|
33584
|
+
throw new Error(
|
|
33585
|
+
"change-detection.ignore-paths must be an array of strings"
|
|
33586
|
+
);
|
|
33587
|
+
}
|
|
33588
|
+
return rawIgnorePaths;
|
|
33589
|
+
}
|
|
33590
|
+
function normalizeJsonPath(path13) {
|
|
33591
|
+
if (path13 === "$" || path13.startsWith("$.")) return path13;
|
|
33592
|
+
return `$.${path13}`;
|
|
33593
|
+
}
|
|
33594
|
+
function assertValidJsonPathSegment(path13, segment) {
|
|
33595
|
+
if (segment.length === 0) {
|
|
33596
|
+
throw new Error(
|
|
33597
|
+
`Invalid change-detection.ignore-paths entry "${path13}": empty path segment`
|
|
33598
|
+
);
|
|
33599
|
+
}
|
|
33600
|
+
if (/[.[\]*?]/.test(segment)) {
|
|
33601
|
+
throw new Error(
|
|
33602
|
+
`Invalid change-detection.ignore-paths entry "${path13}": unsupported path segment "${segment}"`
|
|
33603
|
+
);
|
|
33604
|
+
}
|
|
33605
|
+
}
|
|
33606
|
+
function stripIgnoredJsonPaths(value, ignorePaths) {
|
|
33607
|
+
if (ignorePaths.length === 0) return value;
|
|
33608
|
+
const cloned = structuredClone(value);
|
|
33609
|
+
for (const path13 of ignorePaths) {
|
|
33610
|
+
removeJsonPath(cloned, path13);
|
|
33611
|
+
}
|
|
33612
|
+
return cloned;
|
|
33613
|
+
}
|
|
33614
|
+
function removeJsonPath(value, path13) {
|
|
33615
|
+
const normalizedPath = normalizeJsonPath(path13);
|
|
33616
|
+
if (normalizedPath === "$") return;
|
|
33617
|
+
const segments = normalizedPath.slice(2).split(".");
|
|
33618
|
+
let current = value;
|
|
33619
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
33620
|
+
const segment = segments[i];
|
|
33621
|
+
assertValidJsonPathSegment(path13, segment ?? "");
|
|
33622
|
+
if (current === null || typeof current !== "object") return;
|
|
33623
|
+
if (!Object.hasOwn(current, segment ?? "")) return;
|
|
33624
|
+
current = current[segment ?? ""];
|
|
33625
|
+
}
|
|
33626
|
+
const last = segments.at(-1) ?? "";
|
|
33627
|
+
assertValidJsonPathSegment(path13, last);
|
|
33628
|
+
if (current !== null && typeof current === "object") {
|
|
33629
|
+
Reflect.deleteProperty(current, last);
|
|
33630
|
+
}
|
|
33631
|
+
}
|
|
33632
|
+
function hasChanged2(strategy, ignorePaths, prev, curr) {
|
|
33177
33633
|
switch (strategy) {
|
|
33178
33634
|
case "exit-code":
|
|
33179
33635
|
return prev.exitCode !== curr.exitCode;
|
|
33180
|
-
case "json-diff":
|
|
33636
|
+
case "json-diff": {
|
|
33637
|
+
let prevParsed;
|
|
33638
|
+
let currParsed;
|
|
33181
33639
|
try {
|
|
33182
|
-
|
|
33183
|
-
|
|
33184
|
-
return prevJson !== currJson;
|
|
33640
|
+
prevParsed = JSON.parse(prev.stdout);
|
|
33641
|
+
currParsed = JSON.parse(curr.stdout);
|
|
33185
33642
|
} catch {
|
|
33186
33643
|
return prev.stdout !== curr.stdout;
|
|
33187
33644
|
}
|
|
33645
|
+
return JSON.stringify(
|
|
33646
|
+
sortKeys3(stripIgnoredJsonPaths(prevParsed, ignorePaths))
|
|
33647
|
+
) !== JSON.stringify(sortKeys3(stripIgnoredJsonPaths(currParsed, ignorePaths)));
|
|
33648
|
+
}
|
|
33188
33649
|
case "text-diff":
|
|
33189
33650
|
return prev.stdout !== curr.stdout;
|
|
33190
33651
|
}
|
|
@@ -33266,7 +33727,7 @@ var scopeSchema3 = {
|
|
|
33266
33727
|
properties: {
|
|
33267
33728
|
path: {
|
|
33268
33729
|
type: "string",
|
|
33269
|
-
description: 'Dotted
|
|
33730
|
+
description: 'Dotted path to the array within the parsed JSON (e.g. "tasks" or "$.tasks")'
|
|
33270
33731
|
},
|
|
33271
33732
|
key: {
|
|
33272
33733
|
type: "string",
|
|
@@ -33275,20 +33736,38 @@ var scopeSchema3 = {
|
|
|
33275
33736
|
"ignore-paths": {
|
|
33276
33737
|
type: "array",
|
|
33277
33738
|
items: { type: "string" },
|
|
33278
|
-
description:
|
|
33739
|
+
description: 'Dotted paths (relative to each element, e.g. "fetchedAt" or "$.fetchedAt") removed before comparison'
|
|
33279
33740
|
}
|
|
33280
33741
|
},
|
|
33281
|
-
required: ["path", "key"]
|
|
33742
|
+
required: ["path", "key"],
|
|
33743
|
+
additionalProperties: false
|
|
33744
|
+
},
|
|
33745
|
+
"ignore-paths": {
|
|
33746
|
+
type: "array",
|
|
33747
|
+
items: { type: "string" },
|
|
33748
|
+
description: "Dotted paths removed from parsed JSON before plain json-diff comparison"
|
|
33282
33749
|
}
|
|
33283
33750
|
},
|
|
33751
|
+
additionalProperties: false,
|
|
33284
33752
|
// BP3: change-detection.collection requires strategy: json-diff. Under any
|
|
33285
33753
|
// other strategy (or the defaulted text-diff), presence of `collection` is an
|
|
33286
33754
|
// authoring-time error.
|
|
33287
|
-
|
|
33288
|
-
|
|
33289
|
-
|
|
33290
|
-
|
|
33291
|
-
|
|
33755
|
+
allOf: [
|
|
33756
|
+
{
|
|
33757
|
+
if: { required: ["collection"] },
|
|
33758
|
+
then: {
|
|
33759
|
+
properties: { strategy: { const: "json-diff" } },
|
|
33760
|
+
required: ["strategy"]
|
|
33761
|
+
}
|
|
33762
|
+
},
|
|
33763
|
+
{
|
|
33764
|
+
if: { required: ["ignore-paths"] },
|
|
33765
|
+
then: {
|
|
33766
|
+
properties: { strategy: { const: "json-diff" } },
|
|
33767
|
+
required: ["strategy"]
|
|
33768
|
+
}
|
|
33769
|
+
}
|
|
33770
|
+
]
|
|
33292
33771
|
}
|
|
33293
33772
|
},
|
|
33294
33773
|
required: ["command"]
|
|
@@ -33353,7 +33832,7 @@ var source3 = {
|
|
|
33353
33832
|
health: "ok",
|
|
33354
33833
|
baselined: true
|
|
33355
33834
|
};
|
|
33356
|
-
if (prev !== void 0 && hadBaseline && hasChanged2(scope.strategy, prev, result)) {
|
|
33835
|
+
if (prev !== void 0 && hadBaseline && hasChanged2(scope.strategy, scope.ignorePaths, prev, result)) {
|
|
33357
33836
|
observations.push(changedObservation(scope, result));
|
|
33358
33837
|
}
|
|
33359
33838
|
return { observations, nextState };
|
|
@@ -33696,6 +34175,23 @@ function changeDetectionCollectionError(watchConfig2) {
|
|
|
33696
34175
|
if (strategy === "json-diff") return void 0;
|
|
33697
34176
|
return "change-detection.collection requires strategy: json-diff";
|
|
33698
34177
|
}
|
|
34178
|
+
function oldSourceScopeShapeHint(filePath2) {
|
|
34179
|
+
let content;
|
|
34180
|
+
try {
|
|
34181
|
+
content = (0, import_node_fs3.readFileSync)(filePath2, "utf-8");
|
|
34182
|
+
} catch {
|
|
34183
|
+
return null;
|
|
34184
|
+
}
|
|
34185
|
+
const normalised = content.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n");
|
|
34186
|
+
const frontmatter = /^---\n(?<frontmatter>[\s\S]*?)\n---/.exec(normalised)?.groups?.["frontmatter"];
|
|
34187
|
+
if (!frontmatter) return null;
|
|
34188
|
+
const rawSource = /^source:\s*(?<source>[^\n#]+)/m.exec(frontmatter)?.groups?.["source"];
|
|
34189
|
+
const source6 = rawSource?.trim().replace(/^['"]|['"]$/g, "");
|
|
34190
|
+
const hasScope = /^scope\s*:/m.test(frontmatter);
|
|
34191
|
+
const hasWatch = /^watch\s*:/m.test(frontmatter);
|
|
34192
|
+
if (!source6 || !hasScope || hasWatch) return null;
|
|
34193
|
+
return `did you mean to use the current watch shape? Move source/scope into watch:, for example: watch: { type: ${source6}, ... }`;
|
|
34194
|
+
}
|
|
33699
34195
|
var validateCommand = new Command("validate").description("Validate MONITOR.md files in a directory").argument("[path]", "Path to monitors directory", ".claude/monitors").addOption(
|
|
33700
34196
|
new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")
|
|
33701
34197
|
).action(async (monitorPath, options2) => {
|
|
@@ -33728,12 +34224,12 @@ var validateCommand = new Command("validate").description("Validate MONITOR.md f
|
|
|
33728
34224
|
});
|
|
33729
34225
|
const duplicateErrors = result.duplicateIds.map((dup) => ({
|
|
33730
34226
|
filePath: dup.filePaths.join(", "),
|
|
33731
|
-
error: `Duplicate monitor id "${dup.id}"
|
|
34227
|
+
error: `Duplicate monitor id "${dup.id}" -- ids are derived from folder names and must be unique within a tree`
|
|
33732
34228
|
}));
|
|
33733
34229
|
const allErrors = [
|
|
33734
34230
|
...result.errors.map((e) => ({
|
|
33735
34231
|
filePath: e.filePath,
|
|
33736
|
-
error: e.error
|
|
34232
|
+
error: [e.error, oldSourceScopeShapeHint(e.filePath)].filter(Boolean).join("; ")
|
|
33737
34233
|
})),
|
|
33738
34234
|
...scopeErrors.map((e) => ({
|
|
33739
34235
|
filePath: e.id,
|
|
@@ -33983,19 +34479,14 @@ inboxCommand.command("archive").description("Archive a completed or failed inbox
|
|
|
33983
34479
|
|
|
33984
34480
|
// src/commands/monitor-test.ts
|
|
33985
34481
|
init_cjs_shims();
|
|
33986
|
-
var
|
|
34482
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
34483
|
+
var import_node_fs5 = require("fs");
|
|
33987
34484
|
var import_promises4 = require("timers/promises");
|
|
33988
34485
|
|
|
33989
|
-
// src/runtime-client.ts
|
|
33990
|
-
init_cjs_shims();
|
|
33991
|
-
|
|
33992
|
-
// src/runtime.ts
|
|
33993
|
-
init_cjs_shims();
|
|
33994
|
-
|
|
33995
34486
|
// src/daemon-ipc.ts
|
|
33996
34487
|
init_cjs_shims();
|
|
33997
34488
|
var import_node_crypto = require("crypto");
|
|
33998
|
-
var
|
|
34489
|
+
var import_node_fs4 = require("fs");
|
|
33999
34490
|
var import_node_os2 = require("os");
|
|
34000
34491
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
34001
34492
|
var import_node_net = __toESM(require("net"), 1);
|
|
@@ -34018,6 +34509,7 @@ var daemonMethodSchema = external_exports.enum([
|
|
|
34018
34509
|
"events.ack",
|
|
34019
34510
|
"hook.claim",
|
|
34020
34511
|
"history.list",
|
|
34512
|
+
"monitor.explain",
|
|
34021
34513
|
"daemon.tick"
|
|
34022
34514
|
]);
|
|
34023
34515
|
var daemonResponseSchema = external_exports.object({
|
|
@@ -34066,6 +34558,13 @@ var historyListParamsSchema = external_exports.object({
|
|
|
34066
34558
|
monitorId: external_exports.string().optional(),
|
|
34067
34559
|
limit: external_exports.number().int().positive().optional()
|
|
34068
34560
|
});
|
|
34561
|
+
var monitorExplainParamsSchema = external_exports.object({
|
|
34562
|
+
monitorId: external_exports.string(),
|
|
34563
|
+
monitorsDir: external_exports.string(),
|
|
34564
|
+
workspacePath: external_exports.string().optional(),
|
|
34565
|
+
historyLimit: external_exports.number().int().positive().optional(),
|
|
34566
|
+
eventLimit: external_exports.number().int().positive().optional()
|
|
34567
|
+
});
|
|
34069
34568
|
var daemonTickParamsSchema = external_exports.object({
|
|
34070
34569
|
monitorsDir: external_exports.string(),
|
|
34071
34570
|
workspacePath: external_exports.string().optional()
|
|
@@ -34075,6 +34574,13 @@ var daemonRequestSchema = external_exports.object({
|
|
|
34075
34574
|
method: daemonMethodSchema,
|
|
34076
34575
|
params: external_exports.record(external_exports.string(), external_exports.unknown())
|
|
34077
34576
|
});
|
|
34577
|
+
var DaemonConnectionError = class extends Error {
|
|
34578
|
+
constructor(message, cause) {
|
|
34579
|
+
super(message);
|
|
34580
|
+
this.cause = cause;
|
|
34581
|
+
}
|
|
34582
|
+
name = "DaemonConnectionError";
|
|
34583
|
+
};
|
|
34078
34584
|
function isErrnoException(error2) {
|
|
34079
34585
|
return typeof error2 === "object" && error2 !== null && "code" in error2;
|
|
34080
34586
|
}
|
|
@@ -34095,7 +34601,7 @@ function resolveSocketPath(overridePath) {
|
|
|
34095
34601
|
}
|
|
34096
34602
|
function cleanupSocket(socketPath) {
|
|
34097
34603
|
try {
|
|
34098
|
-
(0,
|
|
34604
|
+
(0, import_node_fs4.rmSync)(socketPath);
|
|
34099
34605
|
} catch (error2) {
|
|
34100
34606
|
const code = isErrnoException(error2) ? String(error2.code) : "";
|
|
34101
34607
|
if (code !== "ENOENT") throw error2;
|
|
@@ -34136,8 +34642,8 @@ function acquireStartupLock(socketPath) {
|
|
|
34136
34642
|
const pidFile = import_node_path4.default.join(lock, "pid");
|
|
34137
34643
|
const tryMkdir = () => {
|
|
34138
34644
|
try {
|
|
34139
|
-
(0,
|
|
34140
|
-
(0,
|
|
34645
|
+
(0, import_node_fs4.mkdirSync)(lock);
|
|
34646
|
+
(0, import_node_fs4.writeFileSync)(pidFile, String(process.pid), "utf-8");
|
|
34141
34647
|
return true;
|
|
34142
34648
|
} catch (err) {
|
|
34143
34649
|
if (!isErrnoException(err) || err.code !== "EEXIST") throw err;
|
|
@@ -34147,7 +34653,7 @@ function acquireStartupLock(socketPath) {
|
|
|
34147
34653
|
if (tryMkdir()) return true;
|
|
34148
34654
|
let holderPid;
|
|
34149
34655
|
try {
|
|
34150
|
-
holderPid = parseInt((0,
|
|
34656
|
+
holderPid = parseInt((0, import_node_fs4.readFileSync)(pidFile, "utf-8"), 10);
|
|
34151
34657
|
} catch {
|
|
34152
34658
|
return false;
|
|
34153
34659
|
}
|
|
@@ -34168,11 +34674,11 @@ function acquireStartupLock(socketPath) {
|
|
|
34168
34674
|
return false;
|
|
34169
34675
|
}
|
|
34170
34676
|
try {
|
|
34171
|
-
(0,
|
|
34677
|
+
(0, import_node_fs4.unlinkSync)(pidFile);
|
|
34172
34678
|
} catch {
|
|
34173
34679
|
}
|
|
34174
34680
|
try {
|
|
34175
|
-
(0,
|
|
34681
|
+
(0, import_node_fs4.rmdirSync)(lock);
|
|
34176
34682
|
} catch {
|
|
34177
34683
|
}
|
|
34178
34684
|
return tryMkdir();
|
|
@@ -34181,11 +34687,11 @@ function releaseStartupLock(socketPath) {
|
|
|
34181
34687
|
const lock = lockPath(socketPath);
|
|
34182
34688
|
const pidFile = import_node_path4.default.join(lock, "pid");
|
|
34183
34689
|
try {
|
|
34184
|
-
(0,
|
|
34690
|
+
(0, import_node_fs4.unlinkSync)(pidFile);
|
|
34185
34691
|
} catch {
|
|
34186
34692
|
}
|
|
34187
34693
|
try {
|
|
34188
|
-
(0,
|
|
34694
|
+
(0, import_node_fs4.rmdirSync)(lock);
|
|
34189
34695
|
} catch {
|
|
34190
34696
|
}
|
|
34191
34697
|
}
|
|
@@ -34256,6 +34762,16 @@ function handleRequest(runtime, request, stop) {
|
|
|
34256
34762
|
})
|
|
34257
34763
|
);
|
|
34258
34764
|
}
|
|
34765
|
+
case "monitor.explain": {
|
|
34766
|
+
const params = monitorExplainParamsSchema.parse(request.params);
|
|
34767
|
+
return runtime.explainMonitor({
|
|
34768
|
+
monitorId: params.monitorId,
|
|
34769
|
+
monitorsDir: params.monitorsDir,
|
|
34770
|
+
...params.workspacePath ? { workspacePath: params.workspacePath } : {},
|
|
34771
|
+
...params.historyLimit ? { historyLimit: params.historyLimit } : {},
|
|
34772
|
+
...params.eventLimit ? { eventLimit: params.eventLimit } : {}
|
|
34773
|
+
});
|
|
34774
|
+
}
|
|
34259
34775
|
case "daemon.tick": {
|
|
34260
34776
|
const params = daemonTickParamsSchema.parse(request.params);
|
|
34261
34777
|
return runtime.tick(params.monitorsDir, params.workspacePath);
|
|
@@ -34267,7 +34783,7 @@ function createDaemonServer({
|
|
|
34267
34783
|
socketPath,
|
|
34268
34784
|
onStop
|
|
34269
34785
|
}) {
|
|
34270
|
-
(0,
|
|
34786
|
+
(0, import_node_fs4.mkdirSync)(import_node_path4.default.dirname(socketPath), { recursive: true });
|
|
34271
34787
|
let serverClosed = false;
|
|
34272
34788
|
const server = import_node_net.default.createServer((socket) => {
|
|
34273
34789
|
let buffer = "";
|
|
@@ -34325,7 +34841,7 @@ function createDaemonServer({
|
|
|
34325
34841
|
const live = await probeSocket(socketPath);
|
|
34326
34842
|
if (!live) {
|
|
34327
34843
|
try {
|
|
34328
|
-
(0,
|
|
34844
|
+
(0, import_node_fs4.unlinkSync)(socketPath);
|
|
34329
34845
|
} catch (unlinkErr) {
|
|
34330
34846
|
const code = isErrnoException(unlinkErr) ? String(unlinkErr.code) : "";
|
|
34331
34847
|
if (code !== "ENOENT") throw unlinkErr;
|
|
@@ -34391,11 +34907,20 @@ async function callDaemon(method, params = {}, options2 = {}) {
|
|
|
34391
34907
|
});
|
|
34392
34908
|
};
|
|
34393
34909
|
socket.setTimeout(timeoutMs, () => {
|
|
34394
|
-
fail(
|
|
34910
|
+
fail(
|
|
34911
|
+
new DaemonConnectionError(
|
|
34912
|
+
`Timed out waiting for AgentMon daemon at ${socketPath}`
|
|
34913
|
+
)
|
|
34914
|
+
);
|
|
34395
34915
|
});
|
|
34396
34916
|
socket.setEncoding("utf-8");
|
|
34397
34917
|
socket.on("error", (error2) => {
|
|
34398
|
-
fail(
|
|
34918
|
+
fail(
|
|
34919
|
+
new DaemonConnectionError(
|
|
34920
|
+
error2 instanceof Error ? error2.message : String(error2),
|
|
34921
|
+
error2
|
|
34922
|
+
)
|
|
34923
|
+
);
|
|
34399
34924
|
});
|
|
34400
34925
|
socket.on("data", (chunk) => {
|
|
34401
34926
|
buffer += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
|
|
@@ -34436,7 +34961,11 @@ async function daemonAvailable(socketPath) {
|
|
|
34436
34961
|
}
|
|
34437
34962
|
}
|
|
34438
34963
|
|
|
34964
|
+
// src/runtime-client.ts
|
|
34965
|
+
init_cjs_shims();
|
|
34966
|
+
|
|
34439
34967
|
// src/runtime.ts
|
|
34968
|
+
init_cjs_shims();
|
|
34440
34969
|
function createRuntime(dbPath = resolveDbPath()) {
|
|
34441
34970
|
const db = createDb(dbPath);
|
|
34442
34971
|
const registry2 = new SourceRegistry();
|
|
@@ -34499,6 +35028,13 @@ async function listObservationHistoryClient(query, socketPath) {
|
|
|
34499
35028
|
socketPath ? { socketPath } : {}
|
|
34500
35029
|
);
|
|
34501
35030
|
}
|
|
35031
|
+
async function explainMonitorClient(input, socketPath) {
|
|
35032
|
+
return await callDaemon(
|
|
35033
|
+
"monitor.explain",
|
|
35034
|
+
input,
|
|
35035
|
+
socketPath ? { socketPath } : {}
|
|
35036
|
+
);
|
|
35037
|
+
}
|
|
34502
35038
|
async function daemonStatusClient(socketPath) {
|
|
34503
35039
|
return await callDaemon(
|
|
34504
35040
|
"status",
|
|
@@ -34515,6 +35051,127 @@ async function daemonTickClient(monitorsDir, workspacePath) {
|
|
|
34515
35051
|
var monitorTestCommand = new Command("monitor").description(
|
|
34516
35052
|
"Monitor utilities"
|
|
34517
35053
|
);
|
|
35054
|
+
var EXPLAIN_STAGE_LABELS2 = {
|
|
35055
|
+
definition: "Definition",
|
|
35056
|
+
scheduling: "Scheduling",
|
|
35057
|
+
observation: "Observation",
|
|
35058
|
+
notify: "Notify state",
|
|
35059
|
+
materialization: "Materialization",
|
|
35060
|
+
delivery: "Projection and delivery"
|
|
35061
|
+
};
|
|
35062
|
+
function monitorIdFromFilePath2(filePath2) {
|
|
35063
|
+
const base = import_node_path5.default.basename(filePath2);
|
|
35064
|
+
return base === "MONITOR.md" ? import_node_path5.default.basename(import_node_path5.default.dirname(filePath2)) : import_node_path5.default.parse(filePath2).name;
|
|
35065
|
+
}
|
|
35066
|
+
function explainStage2(id, status, reason, details) {
|
|
35067
|
+
return {
|
|
35068
|
+
id,
|
|
35069
|
+
label: EXPLAIN_STAGE_LABELS2[id],
|
|
35070
|
+
status,
|
|
35071
|
+
reason,
|
|
35072
|
+
...details ? { details } : {}
|
|
35073
|
+
};
|
|
35074
|
+
}
|
|
35075
|
+
function explainVerdict2(stages) {
|
|
35076
|
+
const stopped = stages.find((stage2) => stage2.status !== "ok");
|
|
35077
|
+
const stage = stopped ?? stages[stages.length - 1];
|
|
35078
|
+
return {
|
|
35079
|
+
status: stage?.status ?? "ok",
|
|
35080
|
+
stage: stage?.id ?? "delivery",
|
|
35081
|
+
reason: stage?.reason ?? "Monitor delivered successfully."
|
|
35082
|
+
};
|
|
35083
|
+
}
|
|
35084
|
+
function statusGlyph(status) {
|
|
35085
|
+
if (status === "ok") return "\u2713";
|
|
35086
|
+
if (status === "pending") return "\u23F3";
|
|
35087
|
+
if (status === "healthy") return "\u25CB";
|
|
35088
|
+
return "\u2717";
|
|
35089
|
+
}
|
|
35090
|
+
function printExplainText(report) {
|
|
35091
|
+
console.log(`Monitor ${report.monitorId}`);
|
|
35092
|
+
for (const stage of report.stages) {
|
|
35093
|
+
console.log(`${statusGlyph(stage.status)} ${stage.label}: ${stage.reason}`);
|
|
35094
|
+
}
|
|
35095
|
+
console.log(
|
|
35096
|
+
`Verdict: ${report.verdict.status} at ${EXPLAIN_STAGE_LABELS2[report.verdict.stage]} - ${report.verdict.reason}`
|
|
35097
|
+
);
|
|
35098
|
+
}
|
|
35099
|
+
async function buildDaemonUnavailableReport(input) {
|
|
35100
|
+
const generatedAt = /* @__PURE__ */ new Date();
|
|
35101
|
+
const stages = [];
|
|
35102
|
+
const scan = await scanMonitors(input.monitorsDir);
|
|
35103
|
+
const parseError = scan.errors.find(
|
|
35104
|
+
(error2) => monitorIdFromFilePath2(error2.filePath) === input.monitorId
|
|
35105
|
+
);
|
|
35106
|
+
const monitor = scan.monitors.find(
|
|
35107
|
+
(candidate) => candidate.monitor.id === input.monitorId
|
|
35108
|
+
)?.monitor;
|
|
35109
|
+
if (parseError) {
|
|
35110
|
+
stages.push(
|
|
35111
|
+
explainStage2(
|
|
35112
|
+
"definition",
|
|
35113
|
+
"failure",
|
|
35114
|
+
`MONITOR.md failed to parse or validate: ${parseError.error}`,
|
|
35115
|
+
{ filePath: parseError.filePath }
|
|
35116
|
+
)
|
|
35117
|
+
);
|
|
35118
|
+
} else if (!monitor) {
|
|
35119
|
+
stages.push(
|
|
35120
|
+
explainStage2(
|
|
35121
|
+
"definition",
|
|
35122
|
+
"failure",
|
|
35123
|
+
`Monitor "${input.monitorId}" was not found in ${input.monitorsDir}.`
|
|
35124
|
+
)
|
|
35125
|
+
);
|
|
35126
|
+
} else {
|
|
35127
|
+
const registry2 = new SourceRegistry();
|
|
35128
|
+
registerCoreSources(registry2);
|
|
35129
|
+
const sourceName = monitor.frontmatter.watch.type;
|
|
35130
|
+
const source6 = registry2.get(sourceName);
|
|
35131
|
+
const { type: _type, ...monitorWatchConfig } = monitor.frontmatter.watch;
|
|
35132
|
+
const scopeErrors = source6 ? validateScope(monitorWatchConfig, source6.scopeSchema) : [`Unknown source "${sourceName}".`];
|
|
35133
|
+
stages.push(
|
|
35134
|
+
scopeErrors.length === 0 ? explainStage2("definition", "ok", "Monitor definition is valid.", {
|
|
35135
|
+
filePath: monitor.filePath,
|
|
35136
|
+
sourceName
|
|
35137
|
+
}) : explainStage2(
|
|
35138
|
+
"definition",
|
|
35139
|
+
"failure",
|
|
35140
|
+
`Monitor definition is invalid: ${scopeErrors.join("; ")}`,
|
|
35141
|
+
{ filePath: monitor.filePath, sourceName }
|
|
35142
|
+
)
|
|
35143
|
+
);
|
|
35144
|
+
}
|
|
35145
|
+
if (stages[0]?.status === "ok") {
|
|
35146
|
+
stages.push(
|
|
35147
|
+
explainStage2(
|
|
35148
|
+
"scheduling",
|
|
35149
|
+
"failure",
|
|
35150
|
+
`The daemon is not running or unreachable: ${input.errorMessage}`,
|
|
35151
|
+
{ workspacePath: input.workspacePath }
|
|
35152
|
+
)
|
|
35153
|
+
);
|
|
35154
|
+
}
|
|
35155
|
+
return {
|
|
35156
|
+
monitorId: input.monitorId,
|
|
35157
|
+
generatedAt,
|
|
35158
|
+
...monitor ? {
|
|
35159
|
+
monitor: {
|
|
35160
|
+
id: monitor.id,
|
|
35161
|
+
displayName: monitor.displayName,
|
|
35162
|
+
filePath: monitor.filePath,
|
|
35163
|
+
sourceName: monitor.frontmatter.watch.type,
|
|
35164
|
+
urgency: monitor.frontmatter.urgency
|
|
35165
|
+
}
|
|
35166
|
+
} : {},
|
|
35167
|
+
stages,
|
|
35168
|
+
verdict: explainVerdict2(stages),
|
|
35169
|
+
observations: [],
|
|
35170
|
+
events: [],
|
|
35171
|
+
projections: [],
|
|
35172
|
+
leadSessions: []
|
|
35173
|
+
};
|
|
35174
|
+
}
|
|
34518
35175
|
function printJsonResult(monitorName, sourceName, baseline, observations) {
|
|
34519
35176
|
console.log(
|
|
34520
35177
|
JSON.stringify(
|
|
@@ -34598,13 +35255,13 @@ monitorTestCommand.command("test").description("Dry-run a monitor observation so
|
|
|
34598
35255
|
new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")
|
|
34599
35256
|
).action(async (filePath2, options2) => {
|
|
34600
35257
|
const json = options2.format === "json";
|
|
34601
|
-
if (!(0,
|
|
35258
|
+
if (!(0, import_node_fs5.existsSync)(filePath2)) {
|
|
34602
35259
|
reportError(`Monitor file not found: ${filePath2}`, json);
|
|
34603
35260
|
return;
|
|
34604
35261
|
}
|
|
34605
35262
|
let content;
|
|
34606
35263
|
try {
|
|
34607
|
-
content = (0,
|
|
35264
|
+
content = (0, import_node_fs5.readFileSync)(filePath2, "utf-8");
|
|
34608
35265
|
} catch (err) {
|
|
34609
35266
|
const msg = err instanceof Error ? err.message : String(err);
|
|
34610
35267
|
reportError(`Cannot read monitor file: ${msg}`, json);
|
|
@@ -34660,6 +35317,55 @@ monitorTestCommand.command("test").description("Dry-run a monitor observation so
|
|
|
34660
35317
|
reportError(`Observation failed: ${message}`, json);
|
|
34661
35318
|
}
|
|
34662
35319
|
});
|
|
35320
|
+
monitorTestCommand.command("explain").description("Explain where a monitor's signal currently stops").argument("<monitorId>", "Monitor id to diagnose").option(
|
|
35321
|
+
"--dir <path>",
|
|
35322
|
+
"Directory containing monitor definitions",
|
|
35323
|
+
".claude/monitors"
|
|
35324
|
+
).option("--workspace <path>", "Workspace path used by the daemon").option("--socket <path>", "Unix domain socket path for the daemon").option("--history-limit <n>", "Observation history rows to include", "10").option("--event-limit <n>", "Materialized event rows to include", "10").addOption(
|
|
35325
|
+
new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")
|
|
35326
|
+
).action(
|
|
35327
|
+
async (monitorId, options2) => {
|
|
35328
|
+
const json = options2.format === "json";
|
|
35329
|
+
const monitorsDir = import_node_path5.default.resolve(options2.dir);
|
|
35330
|
+
const workspacePath = import_node_path5.default.resolve(options2.workspace ?? process.cwd());
|
|
35331
|
+
const historyLimit = Number.parseInt(options2.historyLimit, 10);
|
|
35332
|
+
const eventLimit = Number.parseInt(options2.eventLimit, 10);
|
|
35333
|
+
try {
|
|
35334
|
+
const report = await explainMonitorClient(
|
|
35335
|
+
{
|
|
35336
|
+
monitorId,
|
|
35337
|
+
monitorsDir,
|
|
35338
|
+
workspacePath,
|
|
35339
|
+
...Number.isFinite(historyLimit) && historyLimit > 0 ? { historyLimit } : {},
|
|
35340
|
+
...Number.isFinite(eventLimit) && eventLimit > 0 ? { eventLimit } : {}
|
|
35341
|
+
},
|
|
35342
|
+
options2.socket
|
|
35343
|
+
);
|
|
35344
|
+
if (json) {
|
|
35345
|
+
console.log(JSON.stringify(report, null, 2));
|
|
35346
|
+
return;
|
|
35347
|
+
}
|
|
35348
|
+
printExplainText(report);
|
|
35349
|
+
} catch (err) {
|
|
35350
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
35351
|
+
if (!(err instanceof DaemonConnectionError)) {
|
|
35352
|
+
reportError(`Explain failed: ${message}`, json);
|
|
35353
|
+
return;
|
|
35354
|
+
}
|
|
35355
|
+
const report = await buildDaemonUnavailableReport({
|
|
35356
|
+
monitorId,
|
|
35357
|
+
monitorsDir,
|
|
35358
|
+
workspacePath,
|
|
35359
|
+
errorMessage: message
|
|
35360
|
+
});
|
|
35361
|
+
if (json) {
|
|
35362
|
+
console.log(JSON.stringify(report, null, 2));
|
|
35363
|
+
return;
|
|
35364
|
+
}
|
|
35365
|
+
printExplainText(report);
|
|
35366
|
+
}
|
|
35367
|
+
}
|
|
35368
|
+
);
|
|
34663
35369
|
monitorTestCommand.command("history").description(
|
|
34664
35370
|
"Show recent observation outcomes per tick (triggered / suppressed / no-change / errored / rebaselined)"
|
|
34665
35371
|
).argument("[monitorId]", "Filter to a single monitor id").option("--socket <path>", "Unix domain socket path for the daemon").option("--limit <n>", "Maximum rows to return", "50").addOption(
|
|
@@ -34711,9 +35417,11 @@ sourceCommand.command("list").description("List installed observation sources").
|
|
|
34711
35417
|
const output = sources.map((source6) => {
|
|
34712
35418
|
const requiredFields = source6.scopeSchema["required"] ?? [];
|
|
34713
35419
|
const properties = source6.scopeSchema["properties"] ?? {};
|
|
35420
|
+
const configFields = Object.keys(properties);
|
|
34714
35421
|
return {
|
|
34715
35422
|
name: source6.name,
|
|
34716
|
-
|
|
35423
|
+
configFields,
|
|
35424
|
+
scopeFields: configFields,
|
|
34717
35425
|
required: requiredFields
|
|
34718
35426
|
};
|
|
34719
35427
|
});
|
|
@@ -34729,7 +35437,7 @@ sourceCommand.command("list").description("List installed observation sources").
|
|
|
34729
35437
|
const requiredFields = source6.scopeSchema["required"] ?? [];
|
|
34730
35438
|
const properties = source6.scopeSchema["properties"] ?? {};
|
|
34731
35439
|
console.log(` ${source6.name}`);
|
|
34732
|
-
console.log(`
|
|
35440
|
+
console.log(` Config fields: ${Object.keys(properties).join(", ")}`);
|
|
34733
35441
|
console.log(` Required: ${requiredFields.join(", ") || "(none)"}`);
|
|
34734
35442
|
console.log("");
|
|
34735
35443
|
}
|
|
@@ -35026,15 +35734,15 @@ daemonCommand.command("stop").description("Ask the local AgentMon daemon to stop
|
|
|
35026
35734
|
|
|
35027
35735
|
// src/commands/session.ts
|
|
35028
35736
|
init_cjs_shims();
|
|
35029
|
-
var
|
|
35737
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
35030
35738
|
|
|
35031
35739
|
// src/local-state.ts
|
|
35032
35740
|
init_cjs_shims();
|
|
35033
|
-
var
|
|
35034
|
-
var
|
|
35741
|
+
var import_node_fs6 = require("fs");
|
|
35742
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
35035
35743
|
var DEFAULT_REAP_AFTER_MS2 = 5 * 60 * 1e3;
|
|
35036
35744
|
function filePath(workspacePath) {
|
|
35037
|
-
return
|
|
35745
|
+
return import_node_path6.default.join(workspacePath, ".claude", "agentmonitors.local.md");
|
|
35038
35746
|
}
|
|
35039
35747
|
function parseFrontmatter(raw) {
|
|
35040
35748
|
const lines = raw.split("\n");
|
|
@@ -35063,7 +35771,7 @@ function parseFrontmatter(raw) {
|
|
|
35063
35771
|
function readLocalState(workspacePath) {
|
|
35064
35772
|
let raw;
|
|
35065
35773
|
try {
|
|
35066
|
-
raw = (0,
|
|
35774
|
+
raw = (0, import_node_fs6.readFileSync)(filePath(workspacePath), "utf-8");
|
|
35067
35775
|
} catch {
|
|
35068
35776
|
return { enabled: false };
|
|
35069
35777
|
}
|
|
@@ -35084,7 +35792,7 @@ function readLocalState(workspacePath) {
|
|
|
35084
35792
|
}
|
|
35085
35793
|
function writeLocalState(workspacePath, state) {
|
|
35086
35794
|
const target = filePath(workspacePath);
|
|
35087
|
-
(0,
|
|
35795
|
+
(0, import_node_fs6.mkdirSync)(import_node_path6.default.dirname(target), { recursive: true });
|
|
35088
35796
|
const lines = [
|
|
35089
35797
|
"---",
|
|
35090
35798
|
`enabled: ${String(state.enabled)}`,
|
|
@@ -35096,25 +35804,25 @@ function writeLocalState(workspacePath, state) {
|
|
|
35096
35804
|
"> Local AgentMon coordination state. Gitignored; safe to delete (it is regenerated).",
|
|
35097
35805
|
""
|
|
35098
35806
|
];
|
|
35099
|
-
(0,
|
|
35807
|
+
(0, import_node_fs6.writeFileSync)(target, lines.join("\n"), "utf-8");
|
|
35100
35808
|
}
|
|
35101
35809
|
|
|
35102
35810
|
// src/workspace-paths.ts
|
|
35103
35811
|
init_cjs_shims();
|
|
35104
35812
|
var import_node_crypto2 = require("crypto");
|
|
35105
35813
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
35106
|
-
var
|
|
35814
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
35107
35815
|
function workspacePaths(workspacePath) {
|
|
35108
|
-
const hash = (0, import_node_crypto2.createHash)("sha256").update(
|
|
35109
|
-
const dataRoot = process.env["XDG_DATA_HOME"] ??
|
|
35110
|
-
const dir =
|
|
35816
|
+
const hash = (0, import_node_crypto2.createHash)("sha256").update(import_node_path7.default.resolve(workspacePath)).digest("hex").slice(0, 16);
|
|
35817
|
+
const dataRoot = process.env["XDG_DATA_HOME"] ?? import_node_path7.default.join(import_node_os3.default.homedir(), ".local", "share");
|
|
35818
|
+
const dir = import_node_path7.default.join(dataRoot, "agentmonitors", "workspaces", hash);
|
|
35111
35819
|
return {
|
|
35112
35820
|
dir,
|
|
35113
|
-
db:
|
|
35821
|
+
db: import_node_path7.default.join(dir, "inbox.db"),
|
|
35114
35822
|
// Keep the socket short: a 16-char hash under the data dir stays well under
|
|
35115
35823
|
// the 100-char limit on most setups; resolveSocketPath's /tmp fallback still
|
|
35116
35824
|
// applies if a deep home dir pushes it over.
|
|
35117
|
-
socket:
|
|
35825
|
+
socket: import_node_path7.default.join(dir, "agentmonitors.sock")
|
|
35118
35826
|
};
|
|
35119
35827
|
}
|
|
35120
35828
|
|
|
@@ -35122,15 +35830,15 @@ function workspacePaths(workspacePath) {
|
|
|
35122
35830
|
init_cjs_shims();
|
|
35123
35831
|
var import_node_child_process = require("child_process");
|
|
35124
35832
|
var import_node_url3 = require("url");
|
|
35125
|
-
var
|
|
35833
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
35126
35834
|
function cliEntry() {
|
|
35127
35835
|
const thisFile = (0, import_node_url3.fileURLToPath)(importMetaUrl);
|
|
35128
|
-
const isBundle =
|
|
35836
|
+
const isBundle = import_node_path8.default.basename(import_node_path8.default.dirname(thisFile)) === "dist";
|
|
35129
35837
|
if (isBundle) {
|
|
35130
35838
|
return thisFile;
|
|
35131
35839
|
}
|
|
35132
|
-
const packageRoot =
|
|
35133
|
-
return
|
|
35840
|
+
const packageRoot = import_node_path8.default.resolve(import_node_path8.default.dirname(thisFile), "..");
|
|
35841
|
+
return import_node_path8.default.join(packageRoot, "dist", "index.cjs");
|
|
35134
35842
|
}
|
|
35135
35843
|
function spawnDetachedDaemon(options2) {
|
|
35136
35844
|
const args = [
|
|
@@ -35337,7 +36045,7 @@ sessionCommand.command("start").description(
|
|
|
35337
36045
|
const paths = workspacePaths(workspacePath);
|
|
35338
36046
|
const socket = resolveSocketPath(state.socket ?? paths.socket);
|
|
35339
36047
|
const db = state.db ?? paths.db;
|
|
35340
|
-
const monitorsDir =
|
|
36048
|
+
const monitorsDir = import_node_path9.default.join(workspacePath, ".claude", "monitors");
|
|
35341
36049
|
const BOOT_TIMEOUT_MS = 8e3;
|
|
35342
36050
|
if (!await daemonAvailable(socket)) {
|
|
35343
36051
|
spawnDetachedDaemon({
|
|
@@ -35771,10 +36479,10 @@ function assignProp(target, prop, value) {
|
|
|
35771
36479
|
configurable: true
|
|
35772
36480
|
});
|
|
35773
36481
|
}
|
|
35774
|
-
function getElementAtPath(obj,
|
|
35775
|
-
if (!
|
|
36482
|
+
function getElementAtPath(obj, path13) {
|
|
36483
|
+
if (!path13)
|
|
35776
36484
|
return obj;
|
|
35777
|
-
return
|
|
36485
|
+
return path13.reduce((acc, key) => acc?.[key], obj);
|
|
35778
36486
|
}
|
|
35779
36487
|
function promiseAllObject(promisesObj) {
|
|
35780
36488
|
const keys = Object.keys(promisesObj);
|
|
@@ -36094,11 +36802,11 @@ function aborted(x2, startIndex = 0) {
|
|
|
36094
36802
|
}
|
|
36095
36803
|
return false;
|
|
36096
36804
|
}
|
|
36097
|
-
function prefixIssues(
|
|
36805
|
+
function prefixIssues(path13, issues) {
|
|
36098
36806
|
return issues.map((iss) => {
|
|
36099
36807
|
var _a2;
|
|
36100
36808
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
36101
|
-
iss.path.unshift(
|
|
36809
|
+
iss.path.unshift(path13);
|
|
36102
36810
|
return iss;
|
|
36103
36811
|
});
|
|
36104
36812
|
}
|
|
@@ -43007,9 +43715,9 @@ async function runChannelServe(options2) {
|
|
|
43007
43715
|
// src/index.ts
|
|
43008
43716
|
function getVersion() {
|
|
43009
43717
|
try {
|
|
43010
|
-
const dir = (0,
|
|
43718
|
+
const dir = (0, import_node_path10.dirname)((0, import_node_url4.fileURLToPath)(importMetaUrl));
|
|
43011
43719
|
const pkg = JSON.parse(
|
|
43012
|
-
(0,
|
|
43720
|
+
(0, import_node_fs7.readFileSync)((0, import_node_path10.join)(dir, "..", "package.json"), "utf8")
|
|
43013
43721
|
);
|
|
43014
43722
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
43015
43723
|
} catch {
|