@lazyingart/agintiflow 0.20.222 → 0.20.223

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.222",
3
+ "version": "0.20.223",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
6
6
  "license": "Apache-2.0",
@@ -658,6 +658,21 @@ try {
658
658
  exactCompoundReadOnlyAuditPolicy.writesWorkspace === false,
659
659
  "a finite read-only prelude and conditional directory audit incorrectly required destructive host permission"
660
660
  );
661
+ const exactReadOnlyExistenceLoopWithSuffix =
662
+ 'for f in scripts/xyq_cdp_browser.py scripts/xyq_chrome/launch_chrome.sh scripts/xyq_chrome/watch_thread_dom_download.py scripts/musia_mv_finalize.sh; do test -e "/home/lachlan/ProjectsLFS/LALACHAN/$f" && echo "OK $f" || echo "MISS $f"; done; echo "---mv_packs---"; ls -1 /home/lachlan/ProjectsLFS/Musia/data/mv_packs 2>&1; echo "---creative_projects---"; ls -1 /home/lachlan/ProjectsLFS/Musia/data/creative_projects 2>&1 | head -20';
663
+ const exactReadOnlyExistenceLoopWithSuffixPolicy = evaluateCommandPolicy(
664
+ exactReadOnlyExistenceLoopWithSuffix,
665
+ hostReadRootPolicy
666
+ );
667
+ assert(
668
+ exactReadOnlyExistenceLoopWithSuffixPolicy.allowed &&
669
+ exactReadOnlyExistenceLoopWithSuffixPolicy.category === "read-only" &&
670
+ exactReadOnlyExistenceLoopWithSuffixPolicy.boundedForLoop === true &&
671
+ exactReadOnlyExistenceLoopWithSuffixPolicy.boundedCompoundSequence === true &&
672
+ exactReadOnlyExistenceLoopWithSuffixPolicy.needsNetwork === false &&
673
+ exactReadOnlyExistenceLoopWithSuffixPolicy.writesWorkspace === false,
674
+ "a finite read-only existence loop with bounded list suffix incorrectly required destructive host permission"
675
+ );
661
676
  const exactReadOnlyGitIdentityProbe =
662
677
  'git status --short && echo "TOPLEVEL=$(git rev-parse --show-toplevel 2>&1)" && echo "BRANCH=$(git rev-parse --abbrev-ref HEAD 2>&1)"';
663
678
  const exactReadOnlyGitIdentityProbePolicy = evaluateCommandPolicy(
@@ -754,6 +769,9 @@ try {
754
769
  'echo start; for d in /tmp; do if [ -d "$d" ]; then if [ -f "$d/x" ]; then cat "$d/x"; else echo missing; fi; else echo absent; fi; done',
755
770
  'echo start; for d in $ROOTS; do if [ -d "$d" ]; then ls "$d"; else echo missing; fi; done',
756
771
  'echo start; for d in /tmp; do if [ -d "$d" ]; then ls "$d"; else echo missing; fi; done; rm -rf report.md',
772
+ 'for f in README.md; do test -e "$f" && echo ok || echo missing; done; cp README.md copied.md',
773
+ 'for f in README.md; do test -e "$f" && echo ok || echo missing; done; curl https://example.com',
774
+ 'for f in README.md; do test -e "$f" && echo ok || echo missing; done; echo done > report.md',
757
775
  ]) {
758
776
  const unsafeCompoundAuditPolicy = evaluateCommandPolicy(unsafeCompoundAudit, hostReadRootPolicy);
759
777
  assert(
@@ -1357,7 +1357,11 @@ function classifySimpleCommand(normalized) {
1357
1357
  };
1358
1358
  }
1359
1359
 
1360
- if (matchAny(READ_ONLY_PATTERNS, commandForPatternMatching) || isReadOnlyFindCommand(normalized)) {
1360
+ if (
1361
+ matchAny(READ_ONLY_PATTERNS, commandForPatternMatching) ||
1362
+ isReadOnlyFindCommand(normalized) ||
1363
+ (!hasActiveShellExpansion(benignRedirectCommand) && isReadOnlyShellCondition(benignRedirectCommand))
1364
+ ) {
1361
1365
  return {
1362
1366
  category: "read-only",
1363
1367
  needsNetwork: false,
@@ -1767,7 +1771,7 @@ function classifyReadOnlyForLoop(normalized) {
1767
1771
 
1768
1772
  function classifyReadOnlyCompoundSequence(normalized) {
1769
1773
  const text = String(normalized || "").trim();
1770
- if (!text || /^for\s+/.test(text) || hasActiveShellCommandSubstitution(text)) return null;
1774
+ if (!text || hasActiveShellCommandSubstitution(text)) return null;
1771
1775
 
1772
1776
  let forIndex = findUnquotedShellWord(text, "for");
1773
1777
  const startsAtCommandBoundary = (index) => {
@@ -1778,16 +1782,24 @@ function classifyReadOnlyCompoundSequence(normalized) {
1778
1782
  while (forIndex > 0 && !startsAtCommandBoundary(forIndex)) {
1779
1783
  forIndex = findUnquotedShellWord(text, "for", forIndex + 3);
1780
1784
  }
1781
- if (forIndex <= 0) return null;
1785
+ if (forIndex < 0) return null;
1782
1786
 
1783
1787
  const prefixSource = trimShellListBoundary(text.slice(0, forIndex));
1784
- const loopSource = text.slice(forIndex).trim();
1785
- const prefixClassification = classifyReadOnlyCommandList(prefixSource);
1786
- if (prefixClassification.category === "blocked" || prefixClassification.category === "destructive") {
1787
- return { ...prefixClassification, gitOnly: false };
1788
- }
1789
- if (prefixClassification.category !== "read-only" || prefixClassification.writesWorkspace) {
1790
- return broadForLoopClassification(text, "the command prelude is not read-only");
1788
+ const doneIndex = findUnquotedShellWord(text, "done", forIndex + 3);
1789
+ if (doneIndex < 0) return null;
1790
+ const loopSource = text.slice(forIndex, doneIndex + 4).trim();
1791
+ const suffixSource = trimShellListBoundary(text.slice(doneIndex + 4));
1792
+ if (!prefixSource && !suffixSource) return null;
1793
+
1794
+ for (const [label, source] of [["prelude", prefixSource], ["suffix", suffixSource]]) {
1795
+ if (!source) continue;
1796
+ const classification = classifyReadOnlyCommandList(source);
1797
+ if (classification.category === "blocked" || classification.category === "destructive") {
1798
+ return { ...classification, gitOnly: false };
1799
+ }
1800
+ if (classification.category !== "read-only" || classification.writesWorkspace) {
1801
+ return broadForLoopClassification(text, `the command ${label} is not read-only`);
1802
+ }
1791
1803
  }
1792
1804
  const loopClassification = classifyReadOnlyForLoop(loopSource);
1793
1805
  if (!loopClassification) return null;
@@ -1801,7 +1813,7 @@ function classifyReadOnlyCompoundSequence(normalized) {
1801
1813
  gitOnly: false,
1802
1814
  boundedForLoop: true,
1803
1815
  boundedCompoundSequence: true,
1804
- reason: "Finite read-only command prelude followed by a bounded read-only shell loop.",
1816
+ reason: "A bounded read-only shell loop has only finite read-only command context.",
1805
1817
  };
1806
1818
  }
1807
1819