portable-agent-layer 0.51.1 → 0.51.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/package.json +1 -1
- package/src/cli/index.ts +78 -33
- package/src/hooks/lib/log.ts +9 -5
- package/src/targets/opencode/plugin.ts +0 -3
package/README.md
CHANGED
|
@@ -142,9 +142,16 @@ PAL routes inference through the host agent's subscription CLI by default. API k
|
|
|
142
142
|
|
|
143
143
|
### Debug / test
|
|
144
144
|
|
|
145
|
+
Enable verbose hook logging with:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
pal cli debug on # enable → logs to memory/state/debug.log
|
|
149
|
+
pal cli debug off # disable
|
|
150
|
+
pal cli debug # show current status and log path
|
|
151
|
+
```
|
|
152
|
+
|
|
145
153
|
| Variable | Description |
|
|
146
154
|
|----------|-------------|
|
|
147
|
-
| `PAL_DEBUG` | Set to `1` to emit verbose hook logs to `memory/state/debug.log` |
|
|
148
155
|
| `PAL_INFERENCE_DISABLED` | Set to `1` to disable all inference (used by the test suite to prevent real CLI spawns) |
|
|
149
156
|
| `PAL_NOTIFICATIONS_DISABLED` | Set to `1` to suppress desktop notifications (used by the test suite) |
|
|
150
157
|
|
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -18,14 +18,23 @@
|
|
|
18
18
|
* usage Summarize token usage and cost
|
|
19
19
|
* skill link <name> Link a personal ~/.pal/skills/<name>/ into installed agents
|
|
20
20
|
* skill doctor <name> Evaluate a skill against the authoring best practices
|
|
21
|
+
* debug [on|off] Enable / disable verbose hook debug logging
|
|
21
22
|
*/
|
|
22
23
|
|
|
23
24
|
import { spawnSync } from "node:child_process";
|
|
24
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
existsSync,
|
|
27
|
+
mkdirSync,
|
|
28
|
+
readdirSync,
|
|
29
|
+
readFileSync,
|
|
30
|
+
rmSync,
|
|
31
|
+
statSync,
|
|
32
|
+
writeFileSync,
|
|
33
|
+
} from "node:fs";
|
|
25
34
|
import { homedir } from "node:os";
|
|
26
35
|
import { resolve } from "node:path";
|
|
27
36
|
import { inference, previewInferenceRoute } from "../hooks/lib/inference";
|
|
28
|
-
import { DEBUG_LOG_MAX_ROTATED } from "../hooks/lib/log";
|
|
37
|
+
import { DEBUG_LOG_MAX_ROTATED, logDebug } from "../hooks/lib/log";
|
|
29
38
|
import { palHome, palPkg, platform } from "../hooks/lib/paths";
|
|
30
39
|
import { hasRealContent, SETUP_STEPS, STEP_ORDER } from "../hooks/lib/setup";
|
|
31
40
|
import { log } from "../targets/lib";
|
|
@@ -200,6 +209,9 @@ async function runCli(command: string | undefined, args: string[]) {
|
|
|
200
209
|
if (code !== 0) process.exit(code);
|
|
201
210
|
break;
|
|
202
211
|
}
|
|
212
|
+
case "debug":
|
|
213
|
+
cliDebug(args);
|
|
214
|
+
break;
|
|
203
215
|
case "--help":
|
|
204
216
|
case "-h":
|
|
205
217
|
case "help":
|
|
@@ -244,6 +256,7 @@ function showHelp() {
|
|
|
244
256
|
(search · graph · stats · hubs · find · show · add · ls)
|
|
245
257
|
pal cli skill link <name> Link a personal ~/.pal/skills/<name>/ into installed agents
|
|
246
258
|
pal cli skill doctor <name> Evaluate a skill against the authoring best practices
|
|
259
|
+
pal cli debug [on|off] Enable/disable verbose hook debug logging (persisted)
|
|
247
260
|
|
|
248
261
|
Environment:
|
|
249
262
|
PAL_HOME Override user state directory (default: ~/.pal or repo root)
|
|
@@ -1151,10 +1164,19 @@ async function exportState(args: string[]) {
|
|
|
1151
1164
|
|
|
1152
1165
|
const dryRun = args.includes("--dry-run");
|
|
1153
1166
|
const pathArg = args.find((a) => !a.startsWith("-"));
|
|
1154
|
-
const
|
|
1155
|
-
|
|
1167
|
+
const resolvedArg = pathArg ? resolve(pathArg) : null;
|
|
1168
|
+
const argIsDir =
|
|
1169
|
+
resolvedArg !== null &&
|
|
1170
|
+
existsSync(resolvedArg) &&
|
|
1171
|
+
statSync(resolvedArg).isDirectory();
|
|
1172
|
+
const outputPath = argIsDir
|
|
1173
|
+
? resolve(resolvedArg, `pal-export-${timestamp()}.zip`)
|
|
1174
|
+
: (resolvedArg ?? resolve(palHome(), `pal-export-${timestamp()}.zip`));
|
|
1175
|
+
|
|
1176
|
+
logDebug("export", `start dryRun=${dryRun} outputPath=${outputPath}`);
|
|
1156
1177
|
if (dryRun) {
|
|
1157
1178
|
const files = collectExportFiles();
|
|
1179
|
+
logDebug("export", `dry-run collected ${files.length} files`);
|
|
1158
1180
|
if (files.length === 0) {
|
|
1159
1181
|
console.log("Nothing to export.");
|
|
1160
1182
|
} else {
|
|
@@ -1163,6 +1185,7 @@ async function exportState(args: string[]) {
|
|
|
1163
1185
|
}
|
|
1164
1186
|
} else {
|
|
1165
1187
|
const count = exportZip(outputPath);
|
|
1188
|
+
logDebug("export", `done count=${count} path=${outputPath}`);
|
|
1166
1189
|
if (count === 0) {
|
|
1167
1190
|
console.log("Nothing to export.");
|
|
1168
1191
|
} else {
|
|
@@ -1179,49 +1202,50 @@ async function importState(args: string[]) {
|
|
|
1179
1202
|
const home = palHome();
|
|
1180
1203
|
const dryRun = args.includes("--dry-run");
|
|
1181
1204
|
const pathArg = args.find((a) => !a.startsWith("-"));
|
|
1205
|
+
logDebug("import", `start dryRun=${dryRun} pathArg=${pathArg ?? "(auto)"}`);
|
|
1182
1206
|
|
|
1183
|
-
function
|
|
1207
|
+
function findLatestIn(dirs: string[]): string | null {
|
|
1184
1208
|
const candidates: string[] = [];
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
...readdirSync(backupDir)
|
|
1200
|
-
.filter(
|
|
1201
|
-
(f) =>
|
|
1202
|
-
(f.startsWith("pal-export-") || f.startsWith("pal-backup-")) &&
|
|
1203
|
-
f.endsWith(".zip")
|
|
1204
|
-
)
|
|
1205
|
-
.map((f) => resolve(backupDir, f))
|
|
1206
|
-
);
|
|
1207
|
-
} catch {
|
|
1208
|
-
/* empty */
|
|
1209
|
+
for (const dir of dirs) {
|
|
1210
|
+
try {
|
|
1211
|
+
candidates.push(
|
|
1212
|
+
...readdirSync(dir)
|
|
1213
|
+
.filter(
|
|
1214
|
+
(f) =>
|
|
1215
|
+
(f.startsWith("pal-export-") || f.startsWith("pal-backup-")) &&
|
|
1216
|
+
f.endsWith(".zip")
|
|
1217
|
+
)
|
|
1218
|
+
.map((f) => resolve(dir, f))
|
|
1219
|
+
);
|
|
1220
|
+
} catch {
|
|
1221
|
+
/* empty */
|
|
1222
|
+
}
|
|
1209
1223
|
}
|
|
1210
|
-
|
|
1211
1224
|
if (candidates.length === 0) return null;
|
|
1212
1225
|
return candidates.sort((a, b) => statSync(b).mtimeMs - statSync(a).mtimeMs)[0];
|
|
1213
1226
|
}
|
|
1214
1227
|
|
|
1215
1228
|
let zipPath: string;
|
|
1216
1229
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1230
|
+
const resolvedArg = pathArg ? resolve(pathArg) : null;
|
|
1231
|
+
const argIsDir =
|
|
1232
|
+
resolvedArg !== null &&
|
|
1233
|
+
existsSync(resolvedArg) &&
|
|
1234
|
+
statSync(resolvedArg).isDirectory();
|
|
1235
|
+
|
|
1236
|
+
if (resolvedArg && !argIsDir) {
|
|
1237
|
+
zipPath = resolvedArg;
|
|
1238
|
+
logDebug("import", `using provided path=${zipPath}`);
|
|
1219
1239
|
} else {
|
|
1220
|
-
const
|
|
1240
|
+
const searchDirs = argIsDir ? [resolvedArg] : [home, resolve(home, "backups")];
|
|
1241
|
+
logDebug("import", `searching dirs=${searchDirs.join(",")}`);
|
|
1242
|
+
const latest = findLatestIn(searchDirs);
|
|
1221
1243
|
if (!latest) {
|
|
1244
|
+
logDebug("import", "no export/backup files found");
|
|
1222
1245
|
log.error("No export or backup files found. Provide a path: pal cli import <path>");
|
|
1223
1246
|
process.exit(1);
|
|
1224
1247
|
}
|
|
1248
|
+
logDebug("import", `auto-selected latest=${latest}`);
|
|
1225
1249
|
console.log(`Found: ${latest}`);
|
|
1226
1250
|
const zip = new AdmZip(latest);
|
|
1227
1251
|
console.log(
|
|
@@ -1253,11 +1277,13 @@ async function importState(args: string[]) {
|
|
|
1253
1277
|
process.exit(0);
|
|
1254
1278
|
}
|
|
1255
1279
|
|
|
1280
|
+
logDebug("import", `zip=${zipPath} entries=${entries.length} dryRun=${dryRun}`);
|
|
1256
1281
|
if (dryRun) {
|
|
1257
1282
|
console.log(`Would import ${entries.length} files → ${home}\n`);
|
|
1258
1283
|
for (const e of entries) console.log(` ${e.entryName}`);
|
|
1259
1284
|
} else {
|
|
1260
1285
|
zip.extractAllTo(home, true);
|
|
1286
|
+
logDebug("import", `done extracted=${entries.length} to=${home}`);
|
|
1261
1287
|
console.log(`Imported ${entries.length} files → ${home}`);
|
|
1262
1288
|
log.info("Run 'pal cli install' to re-register hooks.");
|
|
1263
1289
|
}
|
|
@@ -1309,6 +1335,25 @@ async function update() {
|
|
|
1309
1335
|
await install(resolveTargets([]));
|
|
1310
1336
|
}
|
|
1311
1337
|
|
|
1338
|
+
function cliDebug(args: string[]) {
|
|
1339
|
+
const stateDir = resolve(palHome(), "memory", "state");
|
|
1340
|
+
const flagFile = resolve(stateDir, "debug-enabled");
|
|
1341
|
+
const logFile = resolve(stateDir, "debug.log");
|
|
1342
|
+
const sub = args[0];
|
|
1343
|
+
if (sub === "on") {
|
|
1344
|
+
mkdirSync(stateDir, { recursive: true });
|
|
1345
|
+
writeFileSync(flagFile, "");
|
|
1346
|
+
log.success(`Debug logging enabled → ${logFile}`);
|
|
1347
|
+
} else if (sub === "off") {
|
|
1348
|
+
rmSync(flagFile, { force: true });
|
|
1349
|
+
log.success("Debug logging disabled");
|
|
1350
|
+
} else {
|
|
1351
|
+
const on = existsSync(flagFile);
|
|
1352
|
+
console.log(` Debug: ${on ? "ON" : "OFF"}`);
|
|
1353
|
+
console.log(` Log: ${logFile}`);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1312
1357
|
async function status() {
|
|
1313
1358
|
const home = palHome();
|
|
1314
1359
|
const pkg = palPkg();
|
package/src/hooks/lib/log.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Simple file-based debug logger for PAL hooks.
|
|
3
3
|
* Writes to memory/state/debug.log — rotated on each session start.
|
|
4
4
|
*
|
|
5
|
-
* Only writes when
|
|
5
|
+
* Only writes when debug is enabled (`pal cli debug on`) or when called via logError (always logged).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { appendFileSync, existsSync, renameSync, statSync, unlinkSync } from "node:fs";
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
|
-
import { paths } from "./paths";
|
|
10
|
+
import { palHome, paths } from "./paths";
|
|
11
11
|
|
|
12
12
|
const MAX_LOG_SIZE = 50_000; // ~50KB per file
|
|
13
13
|
const MAX_ROTATED = 5; // keep up to 5 rotated files (.1 newest → .5 oldest)
|
|
@@ -74,9 +74,13 @@ function rotateIfNeeded(path: string): void {
|
|
|
74
74
|
/** Test-only: max rotated count for callers that need to enumerate. */
|
|
75
75
|
export const DEBUG_LOG_MAX_ROTATED = MAX_ROTATED;
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
function isDebugEnabled(): boolean {
|
|
78
|
+
return existsSync(resolve(palHome(), "memory", "state", "debug-enabled"));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Log a debug message (only when debug is enabled via `pal cli debug on`) */
|
|
78
82
|
export function logDebug(source: string, message: string): void {
|
|
79
|
-
if (
|
|
83
|
+
if (!isDebugEnabled()) return;
|
|
80
84
|
const path = logFile();
|
|
81
85
|
rotateIfNeeded(path);
|
|
82
86
|
try {
|
|
@@ -86,7 +90,7 @@ export function logDebug(source: string, message: string): void {
|
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
/** Log an error (always written, regardless of
|
|
93
|
+
/** Log an error (always written, regardless of debug mode) */
|
|
90
94
|
export function logError(source: string, error: unknown): void {
|
|
91
95
|
const path = logFile();
|
|
92
96
|
rotateIfNeeded(path);
|
|
@@ -192,9 +192,6 @@ const PALPlugin: Plugin = async ({ directory, client }: PluginInput) => {
|
|
|
192
192
|
output: { env: Record<string, string> }
|
|
193
193
|
) => {
|
|
194
194
|
output.env.PAL_DIR = PAL_DIR;
|
|
195
|
-
if (process.env.PAL_DEBUG) {
|
|
196
|
-
output.env.PAL_DEBUG = process.env.PAL_DEBUG;
|
|
197
|
-
}
|
|
198
195
|
},
|
|
199
196
|
};
|
|
200
197
|
};
|