portable-agent-layer 0.85.13 → 0.85.14
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/src/cli/doctor/health.ts +31 -11
- package/src/cli/doctor/state.ts +7 -1
- package/src/cli/migrate.ts +3 -1
- package/src/hooks/lib/log.ts +16 -6
package/package.json
CHANGED
package/src/cli/doctor/health.ts
CHANGED
|
@@ -11,29 +11,49 @@ interface PendingMigration {
|
|
|
11
11
|
detail?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
interface ErrorContext {
|
|
15
|
+
now: number;
|
|
16
|
+
env: Record<string, string | undefined>;
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
const CLAUDE_LOGIN_EXPIRED = /Failed to authenticate/;
|
|
20
|
+
const MINUTE_MS = 60_000;
|
|
15
21
|
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
function ago(at: number, now: number): string {
|
|
23
|
+
const minutes = Math.max(0, Math.round((now - at) / MINUTE_MS));
|
|
24
|
+
return minutes < 60 ? `${minutes}m ago` : `${Math.floor(minutes / 60)}h ago`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function claudeLoginExpiredFinding(
|
|
28
|
+
group: HookErrorGroup,
|
|
29
|
+
context: ErrorContext
|
|
30
|
+
): Finding {
|
|
31
|
+
const title = `Background calls to Claude could not log in — ${group.count} failed in the last 24h, last ${ago(group.lastAt, context.now)}: ${group.last}`;
|
|
32
|
+
if (context.env.CLAUDE_CODE_OAUTH_TOKEN)
|
|
33
|
+
return warning(`hook-errors.${group.source}`, title, {
|
|
34
|
+
say: "CLAUDE_CODE_OAUTH_TOKEN is set — if these failures predate it, they clear 24h after the last one; if not, the token is invalid, so create a new one",
|
|
22
35
|
command: "claude setup-token",
|
|
23
|
-
}
|
|
24
|
-
|
|
36
|
+
});
|
|
37
|
+
return failing(`hook-errors.${group.source}`, title, {
|
|
38
|
+
say: "Create a year-long token, then export it as CLAUDE_CODE_OAUTH_TOKEN in your shell profile",
|
|
39
|
+
command: "claude setup-token",
|
|
40
|
+
});
|
|
25
41
|
}
|
|
26
42
|
|
|
27
|
-
export function hookErrorFindings(
|
|
43
|
+
export function hookErrorFindings(
|
|
44
|
+
groups: HookErrorGroup[],
|
|
45
|
+
context: ErrorContext = { now: Date.now(), env: process.env }
|
|
46
|
+
): Finding[] {
|
|
28
47
|
if (groups.length === 0)
|
|
29
48
|
return [passed("hook-errors", "No hook errors in the last 24h")];
|
|
30
49
|
const log = resolve(palHome(), "debug", "debug.log");
|
|
31
50
|
return groups.map((group) => {
|
|
32
|
-
if (CLAUDE_LOGIN_EXPIRED.test(group.last))
|
|
51
|
+
if (CLAUDE_LOGIN_EXPIRED.test(group.last))
|
|
52
|
+
return claudeLoginExpiredFinding(group, context);
|
|
33
53
|
const times = group.count === 1 ? "once" : `${group.count} times`;
|
|
34
54
|
return warning(
|
|
35
55
|
`hook-errors.${group.source}`,
|
|
36
|
-
`${group.source} failed ${times} in the last 24h
|
|
56
|
+
`${group.source} failed ${times} in the last 24h, last ${ago(group.lastAt, context.now)}: ${group.last}`,
|
|
37
57
|
{ say: `Full trace in ${log}` }
|
|
38
58
|
);
|
|
39
59
|
});
|
package/src/cli/doctor/state.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { palHome, palPkg } from "../../hooks/lib/paths";
|
|
4
4
|
import { auditBindings, type BindingIssue } from "../../hooks/lib/projects";
|
|
5
5
|
import { telosStatus } from "../../hooks/lib/telos-topics";
|
|
6
|
+
import { retiredKeysIn } from "../migrate";
|
|
6
7
|
import { type Finding, failing, optional, passed, warning } from "./finding";
|
|
7
8
|
|
|
8
9
|
interface SchemaNode {
|
|
@@ -142,7 +143,12 @@ function settingsFindings(): Finding[] {
|
|
|
142
143
|
}),
|
|
143
144
|
];
|
|
144
145
|
const schema = readSettingsSchema();
|
|
145
|
-
const
|
|
146
|
+
const leftToMigration = new Set(
|
|
147
|
+
retiredKeysIn(settings as Record<string, Record<string, unknown>>)
|
|
148
|
+
);
|
|
149
|
+
const unknown = schema
|
|
150
|
+
? unknownSettingsKeys(settings, schema).filter((k) => !leftToMigration.has(k.path))
|
|
151
|
+
: [];
|
|
146
152
|
return [identityFinding(settings), unknownKeysFinding(unknown)];
|
|
147
153
|
}
|
|
148
154
|
|
package/src/cli/migrate.ts
CHANGED
|
@@ -671,7 +671,9 @@ function readSettingsObject(): Record<string, Record<string, unknown>> | null {
|
|
|
671
671
|
}
|
|
672
672
|
}
|
|
673
673
|
|
|
674
|
-
function retiredKeysIn(
|
|
674
|
+
export function retiredKeysIn(
|
|
675
|
+
settings: Record<string, Record<string, unknown>>
|
|
676
|
+
): string[] {
|
|
675
677
|
return Object.entries(RETIRED_SETTINGS_KEYS).flatMap(([section, keys]) => {
|
|
676
678
|
const value = settings[section];
|
|
677
679
|
if (typeof value !== "object" || value === null) return [];
|
package/src/hooks/lib/log.ts
CHANGED
|
@@ -83,6 +83,12 @@ export interface HookErrorGroup {
|
|
|
83
83
|
source: string;
|
|
84
84
|
count: number;
|
|
85
85
|
last: string;
|
|
86
|
+
lastAt: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface LoggedError {
|
|
90
|
+
at: number;
|
|
91
|
+
message: string;
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -114,13 +120,14 @@ function whatTheAgentSaid(message: string): string {
|
|
|
114
120
|
return output[1].trim() || output[2].trim() || message;
|
|
115
121
|
}
|
|
116
122
|
|
|
117
|
-
function groupBySource(errors:
|
|
123
|
+
function groupBySource(errors: LoggedError[]): HookErrorGroup[] {
|
|
118
124
|
const groups = new Map<string, HookErrorGroup>();
|
|
119
|
-
for (const
|
|
120
|
-
const source = /^([^:]+):/.exec(
|
|
121
|
-
const group = groups.get(source) ?? { source, count: 0, last: "" };
|
|
125
|
+
for (const { at, message } of errors) {
|
|
126
|
+
const source = /^([^:]+):/.exec(message)?.[1] ?? "unknown";
|
|
127
|
+
const group = groups.get(source) ?? { source, count: 0, last: "", lastAt: 0 };
|
|
122
128
|
group.count++;
|
|
123
|
-
group.last = whatTheAgentSaid(
|
|
129
|
+
group.last = whatTheAgentSaid(message.slice(source.length + 1).trim()).slice(0, 120);
|
|
130
|
+
group.lastAt = at;
|
|
124
131
|
groups.set(source, group);
|
|
125
132
|
}
|
|
126
133
|
return [...groups.values()].sort((a, b) => b.count - a.count);
|
|
@@ -131,7 +138,10 @@ export function recentHookErrors(now: number = Date.now()): HookErrorGroup[] {
|
|
|
131
138
|
const recent = readAllLogs()
|
|
132
139
|
.split("\n")
|
|
133
140
|
.filter((line) => line.includes("] ERROR ") && loggedAt(line) > now - DAY_MS)
|
|
134
|
-
.map((line) =>
|
|
141
|
+
.map((line) => ({
|
|
142
|
+
at: loggedAt(line),
|
|
143
|
+
message: line.replace(/^\[.*?\] ERROR /, ""),
|
|
144
|
+
}));
|
|
135
145
|
return groupBySource(recent);
|
|
136
146
|
} catch {
|
|
137
147
|
return [];
|