@rubytech/create-realagent 1.0.617 → 1.0.619
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/package.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +1 -0
- package/payload/platform/plugins/admin/hooks/webfetch-preflight.mjs +363 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.js +55 -6
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.js.map +1 -1
- package/payload/platform/plugins/admin/skills/stream-log-review/SKILL.md +4 -1
- package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js +135 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +17 -10
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +65 -25
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -1
- package/payload/platform/plugins/cloudflare/references/setup-guide.md +2 -2
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +4 -2
- package/payload/platform/plugins/docs/references/cloudflare.md +8 -8
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
- package/payload/platform/scripts/seed-neo4j.sh +12 -0
- package/payload/platform/templates/agents/admin/IDENTITY.md +2 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +5 -5
- package/payload/server/public/assets/{admin-Df1liz4Y.js → admin-D7LRdkYB.js} +30 -30
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +204 -25
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Real Agent</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/admin-
|
|
8
|
+
<script type="module" crossorigin src="/assets/admin-D7LRdkYB.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-Be6NvmcD.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-rov5CBGT.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-OB_Gtr0e.js">
|
package/payload/server/server.js
CHANGED
|
@@ -6211,14 +6211,22 @@ function purgeOldLogs(logDir, prefix) {
|
|
|
6211
6211
|
}
|
|
6212
6212
|
}
|
|
6213
6213
|
function teeProcStderrToStreamLog(proc, streamLog) {
|
|
6214
|
+
const pid = proc.pid;
|
|
6214
6215
|
if (!proc.stderr) {
|
|
6215
|
-
streamLog.write(`[${isoTs()}] [subproc-stderr-skip] reason=no-stderr
|
|
6216
|
+
streamLog.write(`[${isoTs()}] [subproc-stderr-skip] reason=no-stderr pid=${pid}
|
|
6216
6217
|
`);
|
|
6217
6218
|
return;
|
|
6218
6219
|
}
|
|
6220
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) {
|
|
6221
|
+
streamLog.write(`[${isoTs()}] [subproc-stderr-tee-attached] pid=${pid}
|
|
6222
|
+
`);
|
|
6223
|
+
}
|
|
6219
6224
|
const utf8 = new StringDecoder("utf8");
|
|
6220
6225
|
let buffer = "";
|
|
6226
|
+
let bytesSeen = 0;
|
|
6227
|
+
let linesEmitted = 0;
|
|
6221
6228
|
proc.stderr.on("data", (chunk) => {
|
|
6229
|
+
bytesSeen += typeof chunk === "string" ? Buffer.byteLength(chunk, "utf8") : chunk.length;
|
|
6222
6230
|
const text = typeof chunk === "string" ? chunk : utf8.write(chunk);
|
|
6223
6231
|
buffer += text;
|
|
6224
6232
|
let idx;
|
|
@@ -6229,12 +6237,18 @@ function teeProcStderrToStreamLog(proc, streamLog) {
|
|
|
6229
6237
|
if (streamLog.destroyed || streamLog.writableEnded) continue;
|
|
6230
6238
|
streamLog.write(`[${isoTs()}] [subproc-stderr] ${line}
|
|
6231
6239
|
`);
|
|
6240
|
+
linesEmitted++;
|
|
6232
6241
|
}
|
|
6233
6242
|
});
|
|
6234
6243
|
proc.stderr.on("end", () => {
|
|
6235
6244
|
const tail = (buffer + utf8.end()).trim();
|
|
6236
6245
|
if (tail.length > 0 && !streamLog.destroyed && !streamLog.writableEnded) {
|
|
6237
6246
|
streamLog.write(`[${isoTs()}] [subproc-stderr] ${tail}
|
|
6247
|
+
`);
|
|
6248
|
+
linesEmitted++;
|
|
6249
|
+
}
|
|
6250
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) {
|
|
6251
|
+
streamLog.write(`[${isoTs()}] [subproc-stderr-tee-detached] pid=${pid} bytes=${bytesSeen} lines=${linesEmitted}
|
|
6238
6252
|
`);
|
|
6239
6253
|
}
|
|
6240
6254
|
buffer = "";
|
|
@@ -8284,10 +8298,11 @@ async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSes
|
|
|
8284
8298
|
env: {
|
|
8285
8299
|
...process.env,
|
|
8286
8300
|
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
8287
|
-
ACCOUNT_DIR: accountDir
|
|
8288
|
-
// Task
|
|
8289
|
-
//
|
|
8290
|
-
|
|
8301
|
+
ACCOUNT_DIR: accountDir
|
|
8302
|
+
// Task 535: NODE_DEBUG removed. The Claude Code CLI is a bundled Bun
|
|
8303
|
+
// binary and Bun ignores Node's NODE_DEBUG flag, so setting it here was
|
|
8304
|
+
// a no-op that misled future readers. The [subproc-debug-unavailable]
|
|
8305
|
+
// line below records the source-of-silence explicitly.
|
|
8291
8306
|
}
|
|
8292
8307
|
});
|
|
8293
8308
|
const stderrLog = agentLogStream("claude-agent-compaction-stderr", accountDir, conversationId);
|
|
@@ -8298,6 +8313,8 @@ async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSes
|
|
|
8298
8313
|
streamLog.on("error", () => {
|
|
8299
8314
|
});
|
|
8300
8315
|
teeProcStderrToStreamLog(proc, streamLog);
|
|
8316
|
+
streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
|
|
8317
|
+
`);
|
|
8301
8318
|
streamLog.write(`[${isoTs()}] [compaction-start] resumeSessionId=${resumeSessionId}
|
|
8302
8319
|
`);
|
|
8303
8320
|
proc.on("error", (err) => {
|
|
@@ -9171,11 +9188,11 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9171
9188
|
env: {
|
|
9172
9189
|
...process.env,
|
|
9173
9190
|
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
9174
|
-
ACCOUNT_DIR: accountDir
|
|
9175
|
-
// Task
|
|
9176
|
-
//
|
|
9177
|
-
//
|
|
9178
|
-
|
|
9191
|
+
ACCOUNT_DIR: accountDir
|
|
9192
|
+
// Task 535: NODE_DEBUG removed. The Claude Code CLI is a bundled Bun
|
|
9193
|
+
// binary and Bun ignores Node's NODE_DEBUG flag, so setting it here was
|
|
9194
|
+
// a no-op that misled future readers. The [subproc-debug-unavailable]
|
|
9195
|
+
// line below records the source-of-silence explicitly.
|
|
9179
9196
|
}
|
|
9180
9197
|
});
|
|
9181
9198
|
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, spawnConvId);
|
|
@@ -9186,6 +9203,8 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9186
9203
|
streamLog.on("error", () => {
|
|
9187
9204
|
});
|
|
9188
9205
|
teeProcStderrToStreamLog(proc, streamLog);
|
|
9206
|
+
streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
|
|
9207
|
+
`);
|
|
9189
9208
|
if (sessionKey) {
|
|
9190
9209
|
const prev = activeProcesses.get(sessionKey);
|
|
9191
9210
|
if (prev) {
|
|
@@ -9513,10 +9532,11 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9513
9532
|
env: {
|
|
9514
9533
|
...process.env,
|
|
9515
9534
|
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
9516
|
-
ACCOUNT_DIR: accountDir
|
|
9517
|
-
// Task
|
|
9518
|
-
//
|
|
9519
|
-
|
|
9535
|
+
ACCOUNT_DIR: accountDir
|
|
9536
|
+
// Task 535: NODE_DEBUG removed. The Claude Code CLI is a bundled Bun
|
|
9537
|
+
// binary and Bun ignores Node's NODE_DEBUG flag, so setting it here was
|
|
9538
|
+
// a no-op that misled future readers. The [subproc-debug-unavailable]
|
|
9539
|
+
// line below records the source-of-silence explicitly.
|
|
9520
9540
|
}
|
|
9521
9541
|
});
|
|
9522
9542
|
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, managedConvId);
|
|
@@ -9524,6 +9544,8 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9524
9544
|
});
|
|
9525
9545
|
proc.stderr?.pipe(stderrLog);
|
|
9526
9546
|
teeProcStderrToStreamLog(proc, streamLog);
|
|
9547
|
+
streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
|
|
9548
|
+
`);
|
|
9527
9549
|
if (sessionKey) {
|
|
9528
9550
|
const prev = activeProcesses.get(sessionKey);
|
|
9529
9551
|
if (prev) {
|
|
@@ -10360,8 +10382,10 @@ var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
|
10360
10382
|
"silent-catch",
|
|
10361
10383
|
"file-write-storm",
|
|
10362
10384
|
"stale-log",
|
|
10363
|
-
"rate-limit"
|
|
10385
|
+
"rate-limit",
|
|
10386
|
+
"absent-followup"
|
|
10364
10387
|
]);
|
|
10388
|
+
var MAX_FOLLOWUP_WINDOW_MS = 6e5;
|
|
10365
10389
|
var VALID_SOURCES = /* @__PURE__ */ new Set([
|
|
10366
10390
|
"any",
|
|
10367
10391
|
"server",
|
|
@@ -10480,6 +10504,46 @@ function defaultRules() {
|
|
|
10480
10504
|
scope: "session",
|
|
10481
10505
|
suggestedAction: "A tool call has been pending for 30 seconds without a result. Read the adjacent [tool-wait-diag] and [tool-wait-proc] lines in the conversation's stream log to determine whether the network remained healthy, the subprocess held active sockets, and the HTTP request reached the wire. If diag shows a healthy network but the subprocess has no [subproc-stderr] UNDICI/HTTP activity during the wait window, the tool's internal pipeline is stalled \u2014 do not retry the same request against the same target without a change in approach."
|
|
10482
10506
|
},
|
|
10507
|
+
{
|
|
10508
|
+
// Task 536: detect agents ignoring the WEBFETCH_CANNOT_READ_JS_SPA
|
|
10509
|
+
// structured failure. A single SPA short-circuit per conversation is
|
|
10510
|
+
// expected — the hook is doing its job. Two or more in the same
|
|
10511
|
+
// conversation within 5 minutes means either (a) the agent retried
|
|
10512
|
+
// WebFetch on the same SPA URL despite the directive, or (b) the
|
|
10513
|
+
// owner is asking about multiple SPA URLs in one session and the
|
|
10514
|
+
// pattern needs surfacing as a recurring class. Both signal that the
|
|
10515
|
+
// IDENTITY.md "Tool Failure Discipline" guidance is not landing in the
|
|
10516
|
+
// prompt — revise the copy rather than add mechanical enforcement.
|
|
10517
|
+
id: "webfetch-spa-short-circuit-recurring",
|
|
10518
|
+
name: "WebFetch JS-SPA short-circuit fired repeatedly in conversation",
|
|
10519
|
+
type: "repeated-error",
|
|
10520
|
+
logSource: "system",
|
|
10521
|
+
pattern: "WEBFETCH_CANNOT_READ_JS_SPA",
|
|
10522
|
+
thresholdCount: 2,
|
|
10523
|
+
thresholdWindowMinutes: 5,
|
|
10524
|
+
scope: "session",
|
|
10525
|
+
suggestedAction: "The WebFetch SPA preflight has fired more than once in this conversation. Either the agent is ignoring the loud-failure directive (retrying WebFetch after seeing WEBFETCH_CANNOT_READ_JS_SPA), or multiple SPA URLs are being asked about. Read the conversation's stream log for the [tool-use] / [tool-result] sequence around each occurrence \u2014 if the agent dispatched WebFetch on the same URL or substituted Playwright silently, revisit the IDENTITY.md `Tool Failure Discipline` paragraph that names structured-error handling."
|
|
10526
|
+
},
|
|
10527
|
+
{
|
|
10528
|
+
// Task 538: fires when a [spawn] line appears in a conversation's stream
|
|
10529
|
+
// log but no subprocess-lifecycle marker follows within 10s. The three
|
|
10530
|
+
// acceptable followups are Task 535's contract — at least one must be
|
|
10531
|
+
// emitted immediately at every spawn site. Their absence means
|
|
10532
|
+
// `teeProcStderrToStreamLog` regressed, the markers drifted, or the
|
|
10533
|
+
// spawn site was added without wiring them up. Session scope so a single
|
|
10534
|
+
// broken conversation fires exactly once, not N times for every spawn.
|
|
10535
|
+
id: "subproc-tee-silent-spawn",
|
|
10536
|
+
name: "Subprocess spawn without a stderr-tee lifecycle marker",
|
|
10537
|
+
type: "absent-followup",
|
|
10538
|
+
logSource: "system",
|
|
10539
|
+
pattern: "\\[spawn\\] pid=\\d+",
|
|
10540
|
+
followupPattern: "\\[subproc-stderr-tee-attached\\]|\\[subproc-debug-unavailable\\]|\\[subproc-stderr-skip\\]",
|
|
10541
|
+
followupWindowMs: 1e4,
|
|
10542
|
+
thresholdCount: 0,
|
|
10543
|
+
thresholdWindowMinutes: 0,
|
|
10544
|
+
scope: "session",
|
|
10545
|
+
suggestedAction: "The main-subprocess tee infrastructure has regressed \u2014 a spawn produced no lifecycle marker. Re-check `teeProcStderrToStreamLog` is invoked at the spawn site and that `[subproc-debug-unavailable]` or `[subproc-stderr-skip]` is written immediately when the tee cannot attach (Task 535 contract)."
|
|
10546
|
+
},
|
|
10483
10547
|
{
|
|
10484
10548
|
// Task 533: surface every Cloudflare-plugin refusal. The plugin emits
|
|
10485
10549
|
// exactly one [cloudflare:refuse] line per refusal with a structured
|
|
@@ -10638,6 +10702,17 @@ function validateRule(input, label, seenIds) {
|
|
|
10638
10702
|
};
|
|
10639
10703
|
if (typeof r.watchPath === "string") rule.watchPath = r.watchPath;
|
|
10640
10704
|
if (typeof r.staleHours === "number") rule.staleHours = r.staleHours;
|
|
10705
|
+
if (typeof r.followupPattern === "string") {
|
|
10706
|
+
if (r.followupPattern.length > 0) {
|
|
10707
|
+
try {
|
|
10708
|
+
new RegExp(r.followupPattern);
|
|
10709
|
+
} catch (err) {
|
|
10710
|
+
throw new Error(`${label}: followupPattern is not a valid regex: ${err instanceof Error ? err.message : String(err)}`);
|
|
10711
|
+
}
|
|
10712
|
+
}
|
|
10713
|
+
rule.followupPattern = r.followupPattern;
|
|
10714
|
+
}
|
|
10715
|
+
if (typeof r.followupWindowMs === "number") rule.followupWindowMs = r.followupWindowMs;
|
|
10641
10716
|
if (typeof r.suppressedUntil === "string") rule.suppressedUntil = r.suppressedUntil;
|
|
10642
10717
|
if (r.scope !== void 0) {
|
|
10643
10718
|
if (typeof r.scope !== "string" || !VALID_SCOPES.has(r.scope)) {
|
|
@@ -10655,6 +10730,17 @@ function validateRule(input, label, seenIds) {
|
|
|
10655
10730
|
throw new Error(`${label}: stale-log rules require a positive staleHours`);
|
|
10656
10731
|
}
|
|
10657
10732
|
}
|
|
10733
|
+
if (rule.type === "absent-followup") {
|
|
10734
|
+
if (rule.pattern.length === 0) {
|
|
10735
|
+
throw new Error(`${label}: absent-followup rules require a non-empty pattern`);
|
|
10736
|
+
}
|
|
10737
|
+
if (typeof rule.followupPattern !== "string" || rule.followupPattern.length === 0) {
|
|
10738
|
+
throw new Error(`${label}: absent-followup rules require a non-empty followupPattern`);
|
|
10739
|
+
}
|
|
10740
|
+
if (typeof rule.followupWindowMs !== "number" || rule.followupWindowMs <= 0 || rule.followupWindowMs > MAX_FOLLOWUP_WINDOW_MS) {
|
|
10741
|
+
throw new Error(`${label}: absent-followup rules require followupWindowMs in (0, ${MAX_FOLLOWUP_WINDOW_MS}]`);
|
|
10742
|
+
}
|
|
10743
|
+
}
|
|
10658
10744
|
return rule;
|
|
10659
10745
|
}
|
|
10660
10746
|
|
|
@@ -11126,7 +11212,8 @@ function newRuleState() {
|
|
|
11126
11212
|
matchTimestampsByScope: /* @__PURE__ */ new Map(),
|
|
11127
11213
|
lastAlertAt: null,
|
|
11128
11214
|
cumulativeSinceLastAlert: 0,
|
|
11129
|
-
lastSeenAt: null
|
|
11215
|
+
lastSeenAt: null,
|
|
11216
|
+
pendingFollowups: []
|
|
11130
11217
|
};
|
|
11131
11218
|
}
|
|
11132
11219
|
function evaluateTextRule(rule, lines, state, nowMs) {
|
|
@@ -11224,6 +11311,58 @@ function evaluateStaleLogRule(rule, lastMtimeMs, state, nowMs) {
|
|
|
11224
11311
|
};
|
|
11225
11312
|
return { match: match2, state: updated };
|
|
11226
11313
|
}
|
|
11314
|
+
function evaluateAbsentFollowupRule(rule, lines, state, nowMs) {
|
|
11315
|
+
if (isSuppressed(rule, nowMs)) return { matches: [], state };
|
|
11316
|
+
if (!rule.pattern || !rule.followupPattern || !rule.followupWindowMs) {
|
|
11317
|
+
return { matches: [], state };
|
|
11318
|
+
}
|
|
11319
|
+
const triggerRegex = compileRegex(rule.pattern);
|
|
11320
|
+
const followupRegex = compileRegex(rule.followupPattern);
|
|
11321
|
+
const pending = [...state.pendingFollowups ?? []];
|
|
11322
|
+
for (const line of lines) {
|
|
11323
|
+
if (triggerRegex.test(line)) {
|
|
11324
|
+
pending.push({
|
|
11325
|
+
scope: scopeKeyFor(rule, line),
|
|
11326
|
+
timestamp: nowMs,
|
|
11327
|
+
line,
|
|
11328
|
+
fulfilled: false
|
|
11329
|
+
});
|
|
11330
|
+
continue;
|
|
11331
|
+
}
|
|
11332
|
+
if (followupRegex.test(line)) {
|
|
11333
|
+
const scope = scopeKeyFor(rule, line);
|
|
11334
|
+
for (const entry of pending) {
|
|
11335
|
+
if (!entry.fulfilled && entry.scope === scope) {
|
|
11336
|
+
entry.fulfilled = true;
|
|
11337
|
+
break;
|
|
11338
|
+
}
|
|
11339
|
+
}
|
|
11340
|
+
}
|
|
11341
|
+
}
|
|
11342
|
+
const matches = [];
|
|
11343
|
+
const kept = [];
|
|
11344
|
+
for (const entry of pending) {
|
|
11345
|
+
const age = nowMs - entry.timestamp;
|
|
11346
|
+
if (age >= rule.followupWindowMs) {
|
|
11347
|
+
if (!entry.fulfilled) {
|
|
11348
|
+
matches.push({
|
|
11349
|
+
ruleId: rule.id,
|
|
11350
|
+
ruleName: rule.name,
|
|
11351
|
+
matchedAt: nowMs,
|
|
11352
|
+
sampleEvidence: toSample(entry.line),
|
|
11353
|
+
suggestedAction: rule.suggestedAction,
|
|
11354
|
+
missedForMs: age
|
|
11355
|
+
});
|
|
11356
|
+
}
|
|
11357
|
+
continue;
|
|
11358
|
+
}
|
|
11359
|
+
kept.push(entry);
|
|
11360
|
+
}
|
|
11361
|
+
return {
|
|
11362
|
+
matches,
|
|
11363
|
+
state: { ...state, pendingFollowups: kept }
|
|
11364
|
+
};
|
|
11365
|
+
}
|
|
11227
11366
|
var ALERT_WINDOW_MS = 60 * 60 * 1e3;
|
|
11228
11367
|
function rateLimitDecision(state, nowMs) {
|
|
11229
11368
|
const since = state.lastAlertAt === null ? Infinity : nowMs - state.lastAlertAt;
|
|
@@ -11344,6 +11483,24 @@ async function runScanCycle(runtime) {
|
|
|
11344
11483
|
const result = evaluateStaleLogRule(rule, lastMs, state, cycleStart);
|
|
11345
11484
|
state = result.state;
|
|
11346
11485
|
match2 = result.match;
|
|
11486
|
+
} else if (rule.type === "absent-followup") {
|
|
11487
|
+
let inputLines = [];
|
|
11488
|
+
if (rule.logSource === "any") {
|
|
11489
|
+
for (const [src, lines] of linesBySource.entries()) {
|
|
11490
|
+
if (src !== "config-dir") inputLines.push(...lines);
|
|
11491
|
+
}
|
|
11492
|
+
} else {
|
|
11493
|
+
inputLines = linesBySource.get(rule.logSource) ?? [];
|
|
11494
|
+
}
|
|
11495
|
+
const result = evaluateAbsentFollowupRule(rule, inputLines, state, cycleStart);
|
|
11496
|
+
state = result.state;
|
|
11497
|
+
for (const m of result.matches) {
|
|
11498
|
+
const safeTrigger = m.sampleEvidence.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
11499
|
+
console.error(
|
|
11500
|
+
`[review-detector] absent-followup rule=${rule.id} trigger="${safeTrigger}" missed_for_ms=${m.missedForMs ?? ""}`
|
|
11501
|
+
);
|
|
11502
|
+
matches.push(m);
|
|
11503
|
+
}
|
|
11347
11504
|
}
|
|
11348
11505
|
runtime.ruleState.set(rule.id, state);
|
|
11349
11506
|
if (match2) matches.push(match2);
|
|
@@ -31172,18 +31329,23 @@ async function GET9(request) {
|
|
|
31172
31329
|
const accountLogDir2 = account ? resolve19(account.accountDir, "logs") : null;
|
|
31173
31330
|
if (fileParam) {
|
|
31174
31331
|
const safe = basename5(fileParam);
|
|
31332
|
+
const searched = [];
|
|
31175
31333
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
31176
31334
|
if (!dir) continue;
|
|
31177
31335
|
const filePath = resolve19(dir, safe);
|
|
31336
|
+
searched.push(filePath);
|
|
31178
31337
|
try {
|
|
31179
31338
|
const content = readFileSync20(filePath, "utf-8");
|
|
31180
31339
|
const headers = { "Content-Type": "text/plain; charset=utf-8" };
|
|
31181
31340
|
if (download) headers["Content-Disposition"] = `attachment; filename="${safe}"`;
|
|
31182
31341
|
return new Response(content, { headers });
|
|
31183
|
-
} catch {
|
|
31342
|
+
} catch (err) {
|
|
31343
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
31344
|
+
console.debug(`[admin/logs] miss dir=${dir} name=${safe} reason=${reason}`);
|
|
31184
31345
|
}
|
|
31185
31346
|
}
|
|
31186
|
-
|
|
31347
|
+
console.warn(`[admin/logs] not-found name=${safe} searched=[${searched.join(",")}]`);
|
|
31348
|
+
return Response.json({ error: `File not found: ${safe}`, code: "NOT_FOUND" }, { status: 404 });
|
|
31187
31349
|
}
|
|
31188
31350
|
if (typeParam) {
|
|
31189
31351
|
const prefixMap = {
|
|
@@ -31195,24 +31357,37 @@ async function GET9(request) {
|
|
|
31195
31357
|
};
|
|
31196
31358
|
const prefix = prefixMap[typeParam];
|
|
31197
31359
|
if (!prefix) {
|
|
31198
|
-
|
|
31360
|
+
console.warn(`[admin/logs] rejected reason=unknown-type type=${typeParam}`);
|
|
31361
|
+
return Response.json(
|
|
31362
|
+
{ error: `Unknown type: ${typeParam}. Valid: stream, error, session, sse, public`, code: "UNKNOWN_TYPE" },
|
|
31363
|
+
{ status: 400 }
|
|
31364
|
+
);
|
|
31199
31365
|
}
|
|
31200
31366
|
if (!conversationIdParam) {
|
|
31201
|
-
|
|
31367
|
+
console.warn(`[admin/logs] rejected type=${typeParam} reason=no-conversationId`);
|
|
31368
|
+
return Response.json(
|
|
31369
|
+
{ error: `type=${typeParam} requires conversationId (per-conversation log files, no daily fallback)`, code: "CONVERSATION_ID_REQUIRED" },
|
|
31370
|
+
{ status: 400 }
|
|
31371
|
+
);
|
|
31202
31372
|
}
|
|
31203
31373
|
const fileName = `${prefix}-${conversationIdParam}.log`;
|
|
31374
|
+
const searched = [];
|
|
31204
31375
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
31205
31376
|
if (!dir) continue;
|
|
31206
31377
|
const filePath = resolve19(dir, fileName);
|
|
31378
|
+
searched.push(filePath);
|
|
31207
31379
|
try {
|
|
31208
31380
|
const content = readFileSync20(filePath, "utf-8");
|
|
31209
31381
|
const headers = { "Content-Type": "text/plain; charset=utf-8" };
|
|
31210
31382
|
if (download) headers["Content-Disposition"] = `attachment; filename="${fileName}"`;
|
|
31211
31383
|
return new Response(content, { headers });
|
|
31212
|
-
} catch {
|
|
31384
|
+
} catch (err) {
|
|
31385
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
31386
|
+
console.debug(`[admin/logs] miss dir=${dir} name=${fileName} reason=${reason}`);
|
|
31213
31387
|
}
|
|
31214
31388
|
}
|
|
31215
|
-
|
|
31389
|
+
console.warn(`[admin/logs] not-found name=${fileName} searched=[${searched.join(",")}]`);
|
|
31390
|
+
return Response.json({ error: `Log not found: ${fileName}`, code: "NOT_FOUND" }, { status: 404 });
|
|
31216
31391
|
}
|
|
31217
31392
|
const seen = /* @__PURE__ */ new Set();
|
|
31218
31393
|
const logs = {};
|
|
@@ -31221,7 +31396,9 @@ async function GET9(request) {
|
|
|
31221
31396
|
let files;
|
|
31222
31397
|
try {
|
|
31223
31398
|
files = readdirSync5(dir).filter((f) => f.endsWith(".log"));
|
|
31224
|
-
} catch {
|
|
31399
|
+
} catch (err) {
|
|
31400
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
31401
|
+
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
31225
31402
|
continue;
|
|
31226
31403
|
}
|
|
31227
31404
|
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync7(resolve19(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
@@ -31230,8 +31407,10 @@ async function GET9(request) {
|
|
|
31230
31407
|
const content = readFileSync20(resolve19(dir, name));
|
|
31231
31408
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
31232
31409
|
logs[name] = tail.trim() || "(empty)";
|
|
31233
|
-
} catch {
|
|
31234
|
-
|
|
31410
|
+
} catch (err) {
|
|
31411
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
31412
|
+
console.debug(`[admin/logs] read-fail name=${name} reason=${reason}`);
|
|
31413
|
+
logs[name] = `(unreadable: ${reason})`;
|
|
31235
31414
|
}
|
|
31236
31415
|
});
|
|
31237
31416
|
}
|