@stroq/cli 0.15.0 → 0.15.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/dist/index.js CHANGED
@@ -11467,6 +11467,7 @@ async function readCursorSession(path) {
11467
11467
  var HEAD_BYTES = 64 * 1024;
11468
11468
  var claudeCodeReader = {
11469
11469
  agent: "claude-code",
11470
+ label: "Claude Code",
11470
11471
  find: findTranscripts,
11471
11472
  read: readTranscript,
11472
11473
  root: "~/.claude/projects",
@@ -11481,6 +11482,7 @@ var claudeCodeReader = {
11481
11482
  };
11482
11483
  var codexReader = {
11483
11484
  agent: "codex",
11485
+ label: "Codex CLI",
11484
11486
  find: (cwd) => findCodexRollouts(cwd),
11485
11487
  read: readCodexRollout,
11486
11488
  root: "~/.codex/sessions",
@@ -11493,6 +11495,7 @@ var codexReader = {
11493
11495
  };
11494
11496
  var cursorReader = {
11495
11497
  agent: "cursor",
11498
+ label: "Cursor",
11496
11499
  find: (cwd) => findCursorSessions(cwd),
11497
11500
  read: readCursorSession,
11498
11501
  root: tilde(cursorStateDb()),
@@ -11522,6 +11525,10 @@ function tryParse(line3) {
11522
11525
  return null;
11523
11526
  }
11524
11527
  }
11528
+ function readerLabels() {
11529
+ const labels = READERS.map((r) => r.label);
11530
+ return labels.length <= 1 ? labels[0] ?? "" : `${labels.slice(0, -1).join(", ")} and ${labels[labels.length - 1]}`;
11531
+ }
11525
11532
  var READER_ROOTS = () => READERS.map((r) => r.root).join(" or ");
11526
11533
  async function readerForFile(path) {
11527
11534
  let head = "";
@@ -11554,6 +11561,45 @@ async function newestTranscript(cwd) {
11554
11561
  // src/sent/scan.ts
11555
11562
  import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve9 } from "path";
11556
11563
 
11564
+ // src/sent/heredoc.ts
11565
+ var HEREDOC = /(?<!<)<<(-?)\s*(['"]?)([A-Za-z_][\w.-]*)\2/g;
11566
+ var EXECUTES_STDIN = /^(?:sudo\s+)?(?:(?:\S*\/)?env\s+(?:\S+=\S*\s+)*)?(?:\S*\/)?(?:sh|bash|zsh|dash|ksh|fish|python\d*(?:\.\d+)?|node|deno|bun|tsx|ruby|perl|php|osascript|ssh|eval|source|\.|xargs|npx|pnpm|uv)(?:\s|$)/;
11567
+ function bodyIsExecuted(line3, operatorStart, operatorEnd) {
11568
+ const before = line3.slice(0, operatorStart).split(/&&|\|\||;|\$\(|\|/);
11569
+ const stages = [before[before.length - 1] ?? "", ...line3.slice(operatorEnd).split("|").slice(1)];
11570
+ return stages.some((stage) => EXECUTES_STDIN.test(stage.trim()));
11571
+ }
11572
+ function withoutHeredocData(command) {
11573
+ if (!command.includes("<<")) return command;
11574
+ const lines = command.split("\n");
11575
+ const kept = [];
11576
+ let i = 0;
11577
+ while (i < lines.length) {
11578
+ const line3 = lines[i] ?? "";
11579
+ kept.push(line3);
11580
+ const operators = [...line3.matchAll(HEREDOC)];
11581
+ i += 1;
11582
+ if (operators.length === 0) continue;
11583
+ for (const op of operators) {
11584
+ const dash = op[1] === "-";
11585
+ const delimiter3 = op[3] ?? "";
11586
+ let end = i;
11587
+ while (end < lines.length) {
11588
+ const candidate = lines[end] ?? "";
11589
+ if ((dash ? candidate.replace(/^\t+/, "") : candidate) === delimiter3) break;
11590
+ end += 1;
11591
+ }
11592
+ if (end >= lines.length) return command;
11593
+ const start = op.index;
11594
+ const executed = bodyIsExecuted(line3, start, start + op[0].length);
11595
+ if (executed) kept.push(...lines.slice(i, end));
11596
+ kept.push(lines[end] ?? "");
11597
+ i = end + 1;
11598
+ }
11599
+ }
11600
+ return kept.join("\n");
11601
+ }
11602
+
11557
11603
  // src/sent/report.ts
11558
11604
  var MAX_OCCURRENCES = 5;
11559
11605
  function byTime(a, b) {
@@ -11621,8 +11667,8 @@ function fileEvidenceFor(tool) {
11621
11667
  if (SHELL_TOOLS4.has(tool)) return "named";
11622
11668
  return null;
11623
11669
  }
11624
- function credentialFilesIn(input, scope, sessionCwd) {
11625
- const leaves = collectStrings(input);
11670
+ function credentialFilesIn(tool, input, scope, sessionCwd) {
11671
+ const leaves = SHELL_TOOLS4.has(tool) ? collectStrings(input).map(withoutHeredocData) : collectStrings(input);
11626
11672
  return scope.sourcePaths.filter(
11627
11673
  (path) => spellingsOf(path, scope, sessionCwd).some(
11628
11674
  (spelling) => leaves.some((leaf) => mentions(leaf, spelling))
@@ -11692,7 +11738,7 @@ async function scanTranscript(transcript, source, index, scope) {
11692
11738
  }
11693
11739
  const evidence = fileEvidenceFor(event.tool);
11694
11740
  if (evidence !== null) {
11695
- for (const path of credentialFilesIn(event.input, scope, transcript.cwd)) {
11741
+ for (const path of credentialFilesIn(event.tool, event.input, scope, transcript.cwd)) {
11696
11742
  files.push({ path, tool: event.tool, evidence, call, at: event.at });
11697
11743
  }
11698
11744
  }
@@ -11735,7 +11781,7 @@ function scanAuditLog(allEntries, sessionId, scope) {
11735
11781
  const files = entries.filter((e) => e.phase === "post").flatMap((entry2) => {
11736
11782
  const evidence = fileEvidenceFor(entry2.tool);
11737
11783
  if (evidence === null) return [];
11738
- return credentialFilesIn({ summary: entry2.summary }, scope, null).map((path) => ({
11784
+ return credentialFilesIn(entry2.tool, { summary: entry2.summary }, scope, null).map((path) => ({
11739
11785
  path,
11740
11786
  tool: entry2.tool,
11741
11787
  evidence,
@@ -11780,10 +11826,12 @@ Flags:
11780
11826
  --fail-on-finding exit 1 when a credential is found (for a scheduled job)
11781
11827
  -h, --help show this
11782
11828
 
11783
- Reads Claude Code transcripts and Codex CLI rollouts; --transcript works out
11784
- which from the file itself. It reads this machine's credential files to know what
11785
- to look for, and prints names and sources only, never a value. A finding exits 0
11786
- by default: a session that already happened cannot be un-sent by today's commit.
11829
+ Reads sessions recorded by ${readerLabels()}.
11830
+ --transcript works out which from the file itself; a Cursor session is
11831
+ addressed as <store>#<session>. It reads this machine's credential files to
11832
+ know what to look for, and prints names and sources only, never a value. A
11833
+ finding exits 0 by default: a session that already happened cannot be
11834
+ un-sent by today's commit.
11787
11835
  `;
11788
11836
  async function runSent(args) {
11789
11837
  let parsed;
@@ -2,7 +2,7 @@
2
2
  "id": "stroq",
3
3
  "name": "Stroq",
4
4
  "description": "Local action firewall for OpenClaw: scans what the agent reads, taints the session, blocks or asks before dangerous tool calls.",
5
- "version": "0.15.0",
5
+ "version": "0.15.2",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stroq-openclaw-plugin",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "private": true,
5
5
  "description": "Stroq's OpenClaw plugin entry: forwards before_tool_call and after_tool_call to the Stroq CLI.",
6
6
  "type": "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stroq/cli",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "description": "Local action firewall for AI agents: scans what the agent reads, taints the session, blocks dangerous follow-up actions",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",