@vtstech/pi-diag 1.1.9 → 1.2.1

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.
Files changed (2) hide show
  1. package/diag.js +83 -17
  2. package/package.json +3 -3
package/diag.js CHANGED
@@ -46,6 +46,11 @@ function redactValue(key, value) {
46
46
  return value;
47
47
  }
48
48
  function diag_temp_default(pi) {
49
+ let cachedSystemPrompt = null;
50
+ let cachedPayload = null;
51
+ pi.on("before_provider_request", (event) => {
52
+ cachedPayload = event.payload;
53
+ });
49
54
  const branding = [
50
55
  ` \u26A1 Pi Diagnostics v${EXTENSION_VERSION}`,
51
56
  ` Written by VTSTech`,
@@ -310,21 +315,21 @@ function diag_temp_default(pi) {
310
315
  lines.push(section("SECURITY"));
311
316
  const secMode = getSecurityMode();
312
317
  lines.push(info(`Security mode: ${secMode.toUpperCase()}`));
313
- const effectiveCmds = secMode === "max" ? BLOCKED_COMMANDS : CRITICAL_COMMANDS;
318
+ const effectiveCmds = secMode === "off" ? /* @__PURE__ */ new Set() : secMode === "max" ? BLOCKED_COMMANDS : CRITICAL_COMMANDS;
314
319
  const blockedCmdList = Array.from(effectiveCmds).sort();
315
- lines.push(info(`Command blocklist: ${blockedCmdList.length} commands blocked (${CRITICAL_COMMANDS.size} critical` + (secMode === "max" ? ` + ${EXTENDED_COMMANDS.size} extended)` : ")")));
320
+ lines.push(info(`Command blocklist: ${blockedCmdList.length} commands blocked (${CRITICAL_COMMANDS.size} critical` + (secMode === "max" ? ` + ${EXTENDED_COMMANDS.size} extended)` : secMode === "off" ? " (disabled in off mode)" : ")")));
316
321
  const exampleCmds = blockedCmdList.filter((c) => ["rm", "sudo", "chmod", "curl", "wget", "eval"].includes(c));
317
322
  if (exampleCmds.length > 0) {
318
323
  lines.push(info(` Examples: ${exampleCmds.join(", ")}`));
319
324
  }
320
325
  check(
321
- blockedCmdList.length > 0,
322
- `Command blocklist active (${blockedCmdList.length} rules)`,
323
- `Command blocklist is EMPTY \u2014 security risk!`
326
+ secMode === "off" ? true : blockedCmdList.length > 0,
327
+ secMode === "off" ? "Security disabled in off mode" : `Command blocklist active (${blockedCmdList.length} rules)`,
328
+ secMode === "off" ? "" : `Command blocklist is EMPTY \u2014 security risk!`
324
329
  );
325
- const effectivePatterns = secMode === "max" ? BLOCKED_URL_PATTERNS : BLOCKED_URL_ALWAYS;
330
+ const effectivePatterns = secMode === "off" ? /* @__PURE__ */ new Set() : secMode === "max" ? BLOCKED_URL_PATTERNS : BLOCKED_URL_ALWAYS;
326
331
  const blockedPatterns = Array.from(effectivePatterns).sort();
327
- lines.push(info(`SSRF protection: ${blockedPatterns.length} hostname patterns blocked (${BLOCKED_URL_ALWAYS.size} always` + (secMode === "max" ? ` + ${BLOCKED_URL_MAX_ONLY.size} max-only)` : ")")));
332
+ lines.push(info(`SSRF protection: ${blockedPatterns.length} hostname patterns blocked (${BLOCKED_URL_ALWAYS.size} always` + (secMode === "max" ? ` + ${BLOCKED_URL_MAX_ONLY.size} max-only)` : secMode === "off" ? " (disabled in off mode)" : ")")));
328
333
  const examplePatterns = blockedPatterns.filter(
329
334
  (p) => ["localhost", "127.0.0.1", "169.254.169.254", "10.", "192.168.", "internal."].includes(p)
330
335
  );
@@ -332,19 +337,19 @@ function diag_temp_default(pi) {
332
337
  lines.push(info(` Examples: ${examplePatterns.join(", ")}`));
333
338
  }
334
339
  check(
335
- blockedPatterns.length > 0,
336
- `SSRF protection active (${blockedPatterns.length} patterns)`,
337
- `SSRF blocklist is EMPTY \u2014 vulnerability risk!`
340
+ secMode === "off" ? true : blockedPatterns.length > 0,
341
+ secMode === "off" ? "SSRF protection disabled in off mode" : `SSRF protection active (${blockedPatterns.length} patterns)`,
342
+ secMode === "off" ? "" : `SSRF blocklist is EMPTY \u2014 vulnerability risk!`
338
343
  );
339
344
  lines.push(info("SSRF validation tests:"));
340
345
  const ssrfTests = [
341
- { url: "http://localhost:8080/api", expectBlocked: secMode === "max" },
342
- { url: "http://169.254.169.254/latest/meta-data/", expectBlocked: true },
343
- { url: "http://192.168.1.1/admin", expectBlocked: true },
346
+ { url: "http://localhost:8080/api", expectBlocked: secMode !== "off" && secMode === "max" },
347
+ { url: "http://169.254.169.254/latest/meta-data/", expectBlocked: secMode !== "off" },
348
+ { url: "http://192.168.1.1/admin", expectBlocked: secMode !== "off" },
344
349
  { url: "https://api.example.com/data", expectBlocked: false }
345
350
  ];
346
351
  for (const test of ssrfTests) {
347
- const result = isSafeUrl(test.url);
352
+ const result = isSafeUrl(test.url, true, secMode);
348
353
  if (test.expectBlocked && !result.safe) {
349
354
  lines.push(ok(` BLOCKED: ${test.url} \u2192 ${result.error}`));
350
355
  } else if (!test.expectBlocked && result.safe) {
@@ -376,9 +381,9 @@ function diag_temp_default(pi) {
376
381
  }
377
382
  lines.push(info("Command injection tests:"));
378
383
  const cmdTests = [
379
- { cmd: "ls; rm -rf /", expectSafe: false },
380
- { cmd: "sudo chmod 777 /etc/passwd", expectSafe: false },
381
- { cmd: "curl http://localhost/secret", expectSafe: secMode !== "max" },
384
+ { cmd: "ls; rm -rf /", expectSafe: secMode === "off" },
385
+ { cmd: "sudo chmod 777 /etc/passwd", expectSafe: secMode === "off" },
386
+ { cmd: "curl http://localhost/secret", expectSafe: secMode !== "max" && secMode !== "off" },
382
387
  { cmd: "ls -la", expectSafe: true },
383
388
  { cmd: "cat README.md", expectSafe: true },
384
389
  { cmd: "echo hello", expectSafe: true }
@@ -449,6 +454,67 @@ function diag_temp_default(pi) {
449
454
  }
450
455
  const thinking = pi.getThinkingLevel();
451
456
  lines.push(info(`Thinking level: ${thinking}`));
457
+ lines.push(section("SYSTEM PROMPT"));
458
+ let systemPromptText = null;
459
+ try {
460
+ if (typeof ctx.getSystemPrompt === "function") {
461
+ systemPromptText = ctx.getSystemPrompt();
462
+ if (systemPromptText) {
463
+ debugLog("diag", `system prompt retrieved via getSystemPrompt(): ${systemPromptText.length} chars`);
464
+ }
465
+ }
466
+ } catch (err) {
467
+ debugLog("diag", "getSystemPrompt() not available", err);
468
+ }
469
+ if (!systemPromptText && cachedPayload) {
470
+ try {
471
+ const messages = cachedPayload.messages;
472
+ if (messages?.length) {
473
+ const sysMsg = messages.find((m) => m.role === "system") ?? messages[0];
474
+ if (sysMsg?.content) {
475
+ systemPromptText = sysMsg.content;
476
+ debugLog("diag", `system prompt extracted from payload: ${systemPromptText.length} chars`);
477
+ }
478
+ }
479
+ } catch (err) {
480
+ debugLog("diag", "failed to extract system prompt from payload", err);
481
+ }
482
+ }
483
+ if (systemPromptText) {
484
+ const charCount = systemPromptText.length;
485
+ const wordCount = systemPromptText.split(/\s+/).filter(Boolean).length;
486
+ const lineCount = systemPromptText.split("\n").length;
487
+ lines.push(info(`Size: ${charCount} chars, ~${wordCount} words, ${lineCount} lines`));
488
+ const preview = systemPromptText.split("\n")[0]?.slice(0, 80) || "(empty first line)";
489
+ lines.push(info(`Opening line: ${preview}${preview.length >= 80 ? "..." : ""}`));
490
+ const TRUNCATE_AT = 2e3;
491
+ if (charCount <= TRUNCATE_AT) {
492
+ lines.push("");
493
+ lines.push(" \u250C\u2500\u2500\u2500 SYSTEM PROMPT \u2500\u2500\u2500");
494
+ for (const line of systemPromptText.split("\n")) {
495
+ lines.push(` \u2502 ${line}`);
496
+ }
497
+ lines.push(" \u2514" + "\u2500".repeat(Math.min("\u2500\u2500\u2500 SYSTEM PROMPT \u2500\u2500\u2500".length + 4, 50)));
498
+ check(true, "System prompt retrieved successfully");
499
+ } else {
500
+ const truncated = systemPromptText.slice(0, 1500);
501
+ const remaining = charCount - 1500;
502
+ lines.push("");
503
+ lines.push(" \u250C\u2500\u2500\u2500 SYSTEM PROMPT (truncated) \u2500\u2500\u2500");
504
+ for (const line of truncated.split("\n")) {
505
+ lines.push(` \u2502 ${line}`);
506
+ }
507
+ lines.push(` \u2502 ... (${remaining} more chars not shown)`);
508
+ lines.push(" \u2514" + "\u2500".repeat(Math.min("\u2500\u2500\u2500 SYSTEM PROMPT (truncated) \u2500\u2500\u2500".length + 4, 50)));
509
+ check(true, `System prompt retrieved (${charCount} chars, showing first 1500)`);
510
+ }
511
+ } else {
512
+ lines.push(warn("System prompt not available"));
513
+ lines.push(info(" Possible reasons:"));
514
+ lines.push(info(" \u2022 No provider request has been made yet in this session"));
515
+ lines.push(info(" \u2022 ctx.getSystemPrompt() is not supported by your Pi version"));
516
+ lines.push(info(" \u2022 The provider payload does not contain a messages array"));
517
+ }
452
518
  lines.push(section("SUMMARY"));
453
519
  lines.push(info(`Passed: ${passCount} Failed: ${failCount} Warnings: ${warnCount}`));
454
520
  if (failCount === 0) {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@vtstech/pi-diag",
3
- "version": "1.1.9",
3
+ "version": "1.2.1",
4
4
  "description": "Diagnostics extension for Pi Coding Agent",
5
5
  "main": "diag.js",
6
- "keywords": ["pi-extensions"],
6
+ "keywords": ["pi-package", "pi", "pi-coding-agent", "pi-extensions"],
7
7
  "license": "MIT",
8
8
  "access": "public",
9
9
  "type": "module",
@@ -14,7 +14,7 @@
14
14
  "url": "https://github.com/VTSTech/pi-coding-agent"
15
15
  },
16
16
  "dependencies": {
17
- "@vtstech/pi-shared": "1.1.9"
17
+ "@vtstech/pi-shared": "1.2.1"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@mariozechner/pi-coding-agent": ">=0.66"