@blogic-cz/agent-tools 1.5.9 → 1.5.11

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 CHANGED
@@ -612,6 +612,10 @@ The guard blocks agents from accessing sensitive files, leaking secrets, and run
612
612
 
613
613
  GitHub text and file publication, database queries, Azure and Kubernetes command arguments, and observability requests are checked before execution. GitHub file uploads use snapshots of the inspected bytes. Generated release notes and existing pending reviews or draft releases are inspected before publication. Authentication headers and connection credentials remain inside the tools.
614
614
 
615
+ GitHub API requests validate the serialized body that will be sent, including JSON keys and decoded string values. Serialization happens once, before authentication lookup or network access. File publication checks both the supplied path and its resolved target against known credential locations before reading content. This includes SSH, AWS, Kubernetes, Azure, and common CLI credential stores. An unknown filename can still contain credentials, so content checks also apply.
616
+
617
+ The two direct GitHub CLI launch paths receive an explicit environment containing supported GitHub authentication, host/configuration, proxy/CA, and platform variables. Unrelated parent variables and runtime loader settings are not inherited. Other tools retain their own environment contracts; this does not isolate the parent agent or prevent GitHub CLI access to its configured credential files.
618
+
615
619
  Results retain their JSON or TOON structure, with detected credentials replaced by `[REDACTED]`. Audit records are sanitized before storage; reading older records also sanitizes the returned content but does not rewrite the database.
616
620
 
617
621
  These checks apply to these wrappers. They do not intercept arbitrary shell programs or other agent tools. GitHub does not offer an atomic content-version condition for submitting pending reviews or publishing existing drafts, so a concurrent remote edit between inspection and publication remains possible.
@@ -654,6 +658,8 @@ Paths are checked against sensitive filename patterns and configured path rules.
654
658
 
655
659
  Single literal `echo`/`printf` writes with output redirection and single `cat`/`tee` heredoc writes are allowed. Heredoc braces are data. Use a quoted delimiter for literal code; unquoted heredocs containing `$`, backticks, or backslashes do not receive the literal-write exception. The exception permits only supported combinations with navigation or selected inspections; unsupported following commands remain outside it. Supported complete Python stdin heredocs are checked as inline programs, with the normal runtime-argument, path, execution, and environment checks.
656
660
 
661
+ Python JSON parsing, dictionary iteration, and printing are not themselves evidence of credential access. They follow the same access and execution rules as other supported inline programs. This intentionally permits ordinary JSON output that older versions refused. Unknown secrets in ordinary files can therefore reach arbitrary shell output; wrapper content checks do not protect that output. A refusal caused by unsupported syntax means the guard could not establish its policy conditions, not that it observed a secret being read.
662
+
657
663
  A separate bounded Node proof supports literal `package.json` inventory loops that print `bin` metadata or use the supported scripts filter. The complete invocation must preserve loader identity through known passive commands, closed literal writers, or proved inventories. Startup/environment mutations, unknown commands, loader changes, and other dynamic loading remain unproved and are refused.
658
664
 
659
665
  ### Setup for OpenCode
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/credential-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAmB,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAI5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAM/D,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,+CAA+C;AAC/C,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,CAAC;AAQF,+CAA+C;AAC/C,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IACxE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3E,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjF,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACxD,CAAC;AA07DF,6CAA6C;AAC7C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAErE;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEpE;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEpE;AAMD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,eAAe,CAiTrF;AAQD,kEAAkE;AAClE,eAAO,MAAM,uBAAuB,UArxED,SAAS,UAAU,UAAU,KAAK,IAqxEM,CAAC;AAE5E,oDAAoD;AACpD,eAAO,MAAM,aAAa,YAvxEC,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAuxEjB,CAAC;AAExD,yEAAyE;AACzE,eAAO,MAAM,aAAa,aAzxEE,MAAM,KAAK,OAyxEgB,CAAC;AAExD,yDAAyD;AACzD,eAAO,MAAM,aAAa,aA3xEE,MAAM,KAAK,OA2xEgB,CAAC;AAExD,oEAAoE;AACpE,eAAO,MAAM,sBAAsB,YA7xEC,MAAM,KAAK,OA6xE0B,CAAC;AAE1E,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,YA/xEC,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IA+xEf,CAAC;AAEhE,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,YAjyEC,MAAM,KAAK,OAiyEsB,CAAC;AAElE,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,YAnyEC,MAAM,KAAK,MAAM,GAAG,IAmyEa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/credential-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAmB,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAI5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAM/D,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,+CAA+C;AAC/C,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,CAAC;AAQF,+CAA+C;AAC/C,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IACxE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3E,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjF,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACxD,CAAC;AAg7DF,6CAA6C;AAC7C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAErE;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEpE;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEpE;AAMD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,eAAe,CAiTrF;AAQD,kEAAkE;AAClE,eAAO,MAAM,uBAAuB,UA3wED,SAAS,UAAU,UAAU,KAAK,IA2wEM,CAAC;AAE5E,oDAAoD;AACpD,eAAO,MAAM,aAAa,YA7wEC,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IA6wEjB,CAAC;AAExD,yEAAyE;AACzE,eAAO,MAAM,aAAa,aA/wEE,MAAM,KAAK,OA+wEgB,CAAC;AAExD,yDAAyD;AACzD,eAAO,MAAM,aAAa,aAjxEE,MAAM,KAAK,OAixEgB,CAAC;AAExD,oEAAoE;AACpE,eAAO,MAAM,sBAAsB,YAnxEC,MAAM,KAAK,OAmxE0B,CAAC;AAE1E,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,YArxEC,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAqxEf,CAAC;AAEhE,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,YAvxEC,MAAM,KAAK,OAuxEsB,CAAC;AAElE,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,YAzxEC,MAAM,KAAK,MAAM,GAAG,IAyxEa,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/gh-tool/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,OAAO,EAAE,eAAe,EAA2C,MAAM,UAAU,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAU/C,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAYF,eAAO,MAAM,kBAAkB,qDA4B7B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B,CAAC;AA4FF,eAAO,MAAM,SAAS,GAAI,CAAC,QACnB,gBAAgB,KACrB,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,cAAc,CAQjD,CAAC"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/gh-tool/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,OAAO,EAAE,eAAe,EAA2C,MAAM,UAAU,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAW/C,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAYF,eAAO,MAAM,kBAAkB,qDA6B7B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B,CAAC;AAuFF,eAAO,MAAM,SAAS,GAAI,CAAC,QACnB,gBAAgB,KACrB,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,cAAc,CAqCjD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const ghEnvironment: (environment?: Record<string, string | undefined>) => Record<string, string>;
2
+ //# sourceMappingURL=environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/gh-tool/environment.ts"],"names":[],"mappings":"AAyCA,eAAO,MAAM,aAAa,iBACX,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAC9C,MAAM,CAAC,MAAM,EAAE,MAAM,CAMrB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/gh-tool/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGxC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,aAAa,EAA4C,MAAM,SAAS,CAAC;AA4DlF,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,kBAAkB,GAAG,eAAe,GAAG,mBAAmB,CAAC;;oBAK7D,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;wBAChD,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC;yBAC/C,CACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,KAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;yBACf,CAAC,CAAC,EACrB,IAAI,EAAE,gBAAgB,KACnB,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;4BACzB,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,EAAE,KAAK,CAAC;0BAC1D,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;6BACnC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC,CAAC;;AAjBpD,qBAAa,aAAc,SAAQ,kBAmBF;IAC/B,MAAM,CAAC,QAAQ,CAAC,KAAK,6FAyRnB;CACH"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/gh-tool/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAIxC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,aAAa,EAA4C,MAAM,SAAS,CAAC;AA4DlF,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,kBAAkB,GAAG,eAAe,GAAG,mBAAmB,CAAC;;oBAK7D,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;wBAChD,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC;yBAC/C,CACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,KAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;yBACf,CAAC,CAAC,EACrB,IAAI,EAAE,gBAAgB,KACnB,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;4BACzB,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,EAAE,KAAK,CAAC;0BAC1D,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;6BACnC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC,CAAC;;AAjBpD,qBAAa,aAAc,SAAQ,kBAmBF;IAC/B,MAAM,CAAC,QAAQ,CAAC,KAAK,6FA0RnB;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"text-input.d.ts","sourceRoot":"","sources":["../../src/gh-tool/text-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AASpE,QAAA,MAAM,WAAW,wDAAgD,CAAC;AAIlE,eAAO,MAAM,eAAe,aAAc,MAAM,YACmB,CAAC;AAEpE,eAAO,MAAM,oBAAoB,SAAU,MAAM,WAAW,MAAM,0FAYjE,CAAC;AAEF,eAAO,MAAM,yBAAyB,aAAc,MAAM,WAAW,MAAM;;;6BAUH,CAAC;AAEzE,eAAO,MAAM,oBAAoB,aAAc,MAAM,WAAW,MAAM,mDACJ,CAAC;AAuBnE,KAAK,uBAAuB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAwFF,eAAO,MAAM,wBAAwB,YAC1B,IAAI,CAAC,uBAAuB,EAAE,aAAa,GAAG,cAAc,CAAC,KACrE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAIuD,CAAC;AAEnG,eAAO,MAAM,wBAAwB,YAC1B,IAAI,CAAC,uBAAuB,EAAE,aAAa,GAAG,cAAc,CAAC,KACrE,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,kBAAkB,CAI9C,CAAC;AAEL,eAAO,MAAM,uBAAuB,YACzB,IAAI,CAAC,uBAAuB,EAAE,aAAa,GAAG,cAAc,CAAC,GAAG;IACvE,YAAY,EAAE,MAAM,CAAC;CACtB,KACA,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAKsD,CAAC"}
1
+ {"version":3,"file":"text-input.d.ts","sourceRoot":"","sources":["../../src/gh-tool/text-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAepE,QAAA,MAAM,WAAW,wDAAgD,CAAC;AAIlE,eAAO,MAAM,eAAe,aAAc,MAAM,YACyC,CAAC;AAE1F,eAAO,MAAM,oBAAoB,SAAU,MAAM,WAAW,MAAM,0FAYjE,CAAC;AAEF,eAAO,MAAM,yBAAyB,aAAc,MAAM,WAAW,MAAM;;;6BAUH,CAAC;AAEzE,eAAO,MAAM,oBAAoB,aAAc,MAAM,WAAW,MAAM,mDACJ,CAAC;AAuBnE,KAAK,uBAAuB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAwFF,eAAO,MAAM,wBAAwB,YAC1B,IAAI,CAAC,uBAAuB,EAAE,aAAa,GAAG,cAAc,CAAC,KACrE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAIuD,CAAC;AAEnG,eAAO,MAAM,wBAAwB,YAC1B,IAAI,CAAC,uBAAuB,EAAE,aAAa,GAAG,cAAc,CAAC,KACrE,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,kBAAkB,CAI9C,CAAC;AAEL,eAAO,MAAM,uBAAuB,YACzB,IAAI,CAAC,uBAAuB,EAAE,aAAa,GAAG,cAAc,CAAC,GAAG;IACvE,YAAY,EAAE,MAAM,CAAC;CACtB,KACA,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAKsD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blogic-cz/agent-tools",
3
- "version": "1.5.9",
3
+ "version": "1.5.11",
4
4
  "description": "CLI tools for AI coding agent workflows — GitHub, database, Kubernetes, Azure platform, Azure DevOps, logs, sessions, and audit",
5
5
  "keywords": [
6
6
  "agent",
@@ -478,7 +478,7 @@ function unwrapProofCommand(words: string[], marker: string): string[] | undefin
478
478
  function isLiteralLoopCommand(words: string[], marker: string, value: string): boolean {
479
479
  const marked = unwrapProofCommand(words, marker);
480
480
  if (!marked) return false;
481
- const argv = marked.map((word) => word.replaceAll(marker, value));
481
+ const argv = marked.map((word) => word.replaceAll(marker, () => value));
482
482
  const name = argv[0];
483
483
  const uses = marked.flatMap((word, index) => (word.includes(marker) ? [index] : []));
484
484
  if (name === "cat")
@@ -579,7 +579,7 @@ function materializeLiteralShell(command: string): LiteralShellProof | undefined
579
579
  );
580
580
  if (size > 65_536) return undefined;
581
581
  return {
582
- command: values.map((value) => marked.text.replaceAll(marker, value)).join(";\n"),
582
+ command: values.map((value) => marked.text.replaceAll(marker, () => value)).join(";\n"),
583
583
  paths: [],
584
584
  };
585
585
  }
@@ -621,8 +621,11 @@ function materializeLiteralShell(command: string): LiteralShellProof | undefined
621
621
  (assignment[1]?.length ?? 0) +
622
622
  7;
623
623
  if (size > 65_536) return undefined;
624
- const paths = argv.slice(3).map((word) => word.replaceAll(marker, value));
625
- return { command: `cd ${assignment[1]} && ${use.replaceAll(marker, value)}${suffix}`, paths };
624
+ const paths = argv.slice(3).map((word) => word.replaceAll(marker, () => value));
625
+ return {
626
+ command: `cd ${assignment[1]} && ${use.replaceAll(marker, () => value)}${suffix}`,
627
+ paths,
628
+ };
626
629
  }
627
630
  // Only the observed rg -n PATTERN PATH form; marker position proves a real file operand.
628
631
  const brace =
@@ -662,7 +665,7 @@ function materializeLiteralShell(command: string): LiteralShellProof | undefined
662
665
  new TextEncoder().encode(command.slice(0, start)).byteLength +
663
666
  values.reduce((total, value) => total + path.length - members[0].length + value.length + 1, -1);
664
667
  if (size > 65_536) return undefined;
665
- const paths = values.map((member) => path.replace(members[0], member));
668
+ const paths = values.map((member) => path.replace(members[0], () => member));
666
669
  return { command: command.slice(0, start) + paths.join(" "), paths };
667
670
  }
668
671
 
@@ -770,7 +773,7 @@ function normalizeProgramHeredocs(command: string): string {
770
773
  if (!/-\s*$/.test(heredoc.header)) return command;
771
774
  const quoted = "'" + heredoc.body.replaceAll("'", "'\"'\"'") + "'";
772
775
  return (
773
- heredoc.header.replace(/-\s*$/, "-c " + quoted) +
776
+ heredoc.header.replace(/-\s*$/, () => "-c " + quoted) +
774
777
  "\n" +
775
778
  normalizeProgramHeredocs(heredoc.following)
776
779
  );
@@ -987,7 +990,7 @@ function inlinePackageCommands(command: string, allowInventory: boolean): string
987
990
  position === 2
988
991
  ? program.replace(
989
992
  /\brequire\s*\(\s*(["'])\.\/\1\s*\+\s*[a-z]\s*\)/,
990
- `require(${JSON.stringify(path)})`,
993
+ () => `require(${JSON.stringify(path)})`,
991
994
  )
992
995
  : word;
993
996
  return "'" + materialized.replaceAll("'", "'\"'\"'") + "'";
@@ -997,18 +1000,6 @@ function inlinePackageCommands(command: string, allowInventory: boolean): string
997
1000
  });
998
1001
  }
999
1002
 
1000
- /** JSON values selected by arbitrary keys or printed wholesale have no metadata proof. */
1001
- function hasUnverifiedJsonValueOutput(code: string): boolean {
1002
- if (!/\bjson\.loads?\s*\(/.test(code) || !/\bprint\s*\(/.test(code)) return false;
1003
- if (/\.items\s*\(/.test(code) || /\bprint\s*\(\s*json\.loads?\s*\(/.test(code)) return true;
1004
- const names = [...code.matchAll(/\b([A-Za-z_]\w*)\s*=\s*json\.loads?\s*\(/g)].map(
1005
- (match) => match[1] ?? "",
1006
- );
1007
- return names.some((name) =>
1008
- new RegExp(`\\bprint\\s*\\(\\s*(?:json\\.dumps\\s*\\(\\s*)?${name}\\s*[,)]`).test(code),
1009
- );
1010
- }
1011
-
1012
1003
  function hasInlineProgramExecution(
1013
1004
  program: string,
1014
1005
  python: boolean,
@@ -1016,7 +1007,6 @@ function hasInlineProgramExecution(
1016
1007
  ): boolean {
1017
1008
  const parts = programParts(program, python);
1018
1009
  if (!parts) return true;
1019
- if (python && hasUnverifiedJsonValueOutput(parts.code)) return true;
1020
1010
  if (parts.loadedModules.some((name) => /^(?:node:)?vm$/.test(name))) return true;
1021
1011
  // Dynamic loading and reflective access cannot establish a non-executing code operand.
1022
1012
  if (
@@ -1842,7 +1832,7 @@ function hasEnvironmentVariableExpansionRead(
1842
1832
  if (invalidExpansion) return true;
1843
1833
 
1844
1834
  const localMaterialized = [...localValues].reduce(
1845
- (text, [marker, value]) => text.replaceAll(marker, value),
1835
+ (text, [marker, value]) => text.replaceAll(marker, () => value),
1846
1836
  normalized,
1847
1837
  );
1848
1838
  // Options must be checked after local literals become actual argv, never as placeholders.
@@ -1900,7 +1890,7 @@ function hasEnvironmentVariableExpansionRead(
1900
1890
  }
1901
1891
  }
1902
1892
  const materialized = [...localValues].reduce(
1903
- (text, [marker, value]) => text.replaceAll(marker, value),
1893
+ (text, [marker, value]) => text.replaceAll(marker, () => value),
1904
1894
  normalized.replaceAll("ENVVALUE", "SAFEENVVALUE"),
1905
1895
  );
1906
1896
  return hasSensitiveFileRead(materialized, isPathBlocked);
@@ -2137,7 +2127,7 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
2137
2127
  const rawEnvironmentSource =
2138
2128
  mentionsEnvironmentSource(command) && mentionsInlineRuntime(command);
2139
2129
  if (rawEnvironmentSource && hasEnvironmentMutationPrefix(originalCommand))
2140
- return "reads process environment after an unproved environment mutation";
2130
+ return "blocked by the environment/execution policy after an unproved environment mutation";
2141
2131
  command = normalizeProgramHeredocs(command);
2142
2132
  const proof = materializeLiteralShell(command);
2143
2133
  if (!proof && /^\s*for\b/.test(command)) return "cannot prove a bounded literal shell loop";
@@ -2158,19 +2148,19 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
2158
2148
  if (
2159
2149
  hasSensitiveFileRead(command, isPathBlocked) ||
2160
2150
  hasSensitivePathRedirect(command, isPathBlocked)
2161
- ) {
2162
- return "accesses a sensitive file path";
2163
- }
2151
+ )
2152
+ return "blocked by the file-access policy; a sensitive path matched or this command form could not be verified";
2164
2153
  if (
2165
2154
  !isLiteralTextWrite(command) &&
2166
2155
  hasEnvironmentVariableExpansionRead(command, allowedEnvironmentVariables, isPathBlocked)
2167
2156
  ) {
2168
2157
  return "expands an unapproved or executable environment variable value";
2169
2158
  }
2170
- if (hasEnvironmentSourceAccess(command, rawEnvironmentSource))
2171
- return "reads process environment from a script";
2172
- if (hasEnvironmentRead(command, allowedEnvironmentVariables, allowPackageInventory)) {
2173
- return "reads environment variables or passes them to an executor";
2159
+ if (
2160
+ hasEnvironmentSourceAccess(command, rawEnvironmentSource) ||
2161
+ hasEnvironmentRead(command, allowedEnvironmentVariables, allowPackageInventory)
2162
+ ) {
2163
+ return "blocked by the environment/execution policy; permitted access or non-execution could not be established for this command form";
2174
2164
  }
2175
2165
  if (
2176
2166
  dangerousBashPatterns.some(
@@ -2263,7 +2253,7 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
2263
2253
  if (error instanceof Error) {
2264
2254
  if (sanitized !== message) {
2265
2255
  error.message = sanitized;
2266
- if (error.stack) error.stack = error.stack.replace(message, sanitized);
2256
+ if (error.stack) error.stack = error.stack.replace(message, () => sanitized);
2267
2257
  }
2268
2258
  throw error;
2269
2259
  }
@@ -2319,7 +2309,7 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
2319
2309
  throw new Error(
2320
2310
  `\u{1F6AB} Command blocked: ${dangerousReason}; this command might expose secrets.\n\n` +
2321
2311
  `Command: ${command}\n\n` +
2322
- `If you need environment info, ask the user directly.\n\n` +
2312
+ `Use a supported command form or the appropriate wrapper tool.\n\n` +
2323
2313
  `Think this is wrong? Review the project repository, adjust the patterns, and submit a PR.`,
2324
2314
  );
2325
2315
  }
@@ -4,6 +4,7 @@ import { retryTransient } from "#shared/retry-transient";
4
4
 
5
5
  import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "./errors";
6
6
  import type { GitHubApiError } from "./errors";
7
+ import { ghEnvironment } from "./environment";
7
8
  import { validateOutboundText } from "./text-input";
8
9
 
9
10
  // Direct HTTP, not `gh api`: the CLI collapses every failure into a non-zero exit and
@@ -44,6 +45,7 @@ export const resolveGitHubToken = Effect.fn("gh.resolveGitHubToken")(function* (
44
45
  const proc = Bun.spawn(["gh", "auth", "token", "--hostname", "github.com"], {
45
46
  stdout: "pipe",
46
47
  stderr: "ignore",
48
+ env: ghEnvironment(),
47
49
  });
48
50
  const stdout = await new Response(proc.stdout).text();
49
51
  const exitCode = await proc.exited;
@@ -67,27 +69,22 @@ export type GitHubApiRequest = {
67
69
 
68
70
  const MAX_API_RETRIES = 2;
69
71
 
70
- const outboundBodyText = (value: unknown): string => {
72
+ // Traverse only parsed JSON, never the caller's getters or toJSON hooks.
73
+ const decodedBodyText = (value: unknown): string => {
71
74
  if (typeof value === "string") return value;
72
- if (Array.isArray(value)) return value.map(outboundBodyText).join("\n");
75
+ if (Array.isArray(value)) return value.map(decodedBodyText).join("\n");
73
76
  if (value !== null && typeof value === "object")
74
- return Object.values(value).map(outboundBodyText).join("\n");
77
+ return Object.entries(value)
78
+ .map(([key, item]) => `${key}\n${decodedBodyText(item)}`)
79
+ .join("\n");
75
80
  return "";
76
81
  };
77
82
 
78
- const githubApiAttempt = Effect.fn("gh.githubApiAttempt")(function* <T>(opts: GitHubApiRequest) {
83
+ const githubApiAttempt = Effect.fn("gh.githubApiAttempt")(function* <T>(
84
+ opts: GitHubApiRequest,
85
+ serializedBody: string | undefined,
86
+ ) {
79
87
  const method = opts.method ?? "GET";
80
- const bodyText = yield* Effect.try({
81
- try: () => outboundBodyText(opts.body),
82
- catch: () =>
83
- new GitHubCommandError({
84
- command: `gh-tool ${method} ${opts.path}`,
85
- exitCode: 1,
86
- stderr: "Refusing to publish an invalid request body",
87
- message: "Refusing to publish an invalid request body",
88
- }),
89
- });
90
- yield* validateOutboundText(`${opts.path}\n${bodyText}`, `gh-tool ${method} ${opts.path}`);
91
88
  const token = yield* resolveGitHubToken();
92
89
  const url = `${GITHUB_API_ROOT}/${opts.path.replace(/^\//, "")}`;
93
90
 
@@ -99,9 +96,9 @@ const githubApiAttempt = Effect.fn("gh.githubApiAttempt")(function* <T>(opts: Gi
99
96
  Accept: GITHUB_ACCEPT,
100
97
  Authorization: `Bearer ${token}`,
101
98
  "X-GitHub-Api-Version": GITHUB_API_VERSION,
102
- ...(opts.body === undefined ? {} : { "Content-Type": "application/json" }),
99
+ ...(serializedBody === undefined ? {} : { "Content-Type": "application/json" }),
103
100
  },
104
- ...(opts.body === undefined ? {} : { body: JSON.stringify(opts.body) }),
101
+ ...(serializedBody === undefined ? {} : { body: serializedBody }),
105
102
  }),
106
103
  catch: (cause) =>
107
104
  new GitHubCommandError({
@@ -158,13 +155,42 @@ const githubApiAttempt = Effect.fn("gh.githubApiAttempt")(function* <T>(opts: Gi
158
155
  export const githubApi = <T>(
159
156
  opts: GitHubApiRequest,
160
157
  ): Effect.Effect<GitHubApiResponse<T>, GitHubApiError> =>
161
- retryTransient({
162
- attempt: () => githubApiAttempt<T>(opts),
163
- isTransient: (error) =>
164
- error._tag === "GitHubCommandError" &&
165
- error.retryable === true &&
166
- (opts.method ?? "GET") === "GET",
167
- maxRetries: MAX_API_RETRIES,
158
+ Effect.gen(function* () {
159
+ const snapshot = yield* Effect.try({
160
+ try: () => {
161
+ const path = opts.path;
162
+ const method = opts.method ?? "GET";
163
+ const alsoAcceptStatus = opts.alsoAcceptStatus;
164
+ const request = {
165
+ path,
166
+ method,
167
+ ...(alsoAcceptStatus === undefined ? {} : { alsoAcceptStatus: [...alsoAcceptStatus] }),
168
+ };
169
+ const body = opts.body;
170
+ if (body === undefined) return { request, serialized: undefined, decoded: "" };
171
+ const serialized = JSON.stringify(body);
172
+ if (serialized === undefined) throw new Error("Body is not JSON serializable");
173
+ return { request, serialized, decoded: decodedBodyText(JSON.parse(serialized)) };
174
+ },
175
+ catch: () =>
176
+ new GitHubCommandError({
177
+ command: "gh-tool API",
178
+ exitCode: 1,
179
+ stderr: "Refusing to publish an invalid request body",
180
+ message: "Refusing to publish an invalid request body",
181
+ }),
182
+ });
183
+ const { request } = snapshot;
184
+ yield* validateOutboundText(
185
+ `${request.path}\n${snapshot.serialized ?? ""}\n${snapshot.decoded}`,
186
+ `gh-tool ${request.method} ${request.path}`,
187
+ );
188
+ return yield* retryTransient({
189
+ attempt: () => githubApiAttempt<T>(request, snapshot.serialized),
190
+ isTransient: (error) =>
191
+ error._tag === "GitHubCommandError" && error.retryable === true && request.method === "GET",
192
+ maxRetries: MAX_API_RETRIES,
193
+ });
168
194
  });
169
195
 
170
196
  const safeJsonParse = (text: string): unknown => {
@@ -0,0 +1,50 @@
1
+ // Exact names only: GH children need their own auth/config, not unrelated credentials
2
+ // or executable-loader, editor, and pager settings from the parent process.
3
+ const GH_ENVIRONMENT_NAMES = [
4
+ "PATH",
5
+ "HOME",
6
+ "USERPROFILE",
7
+ "APPDATA",
8
+ "AppData",
9
+ "HOMEDRIVE",
10
+ "HOMEPATH",
11
+ "SYSTEMROOT",
12
+ "SystemRoot",
13
+ "WINDIR",
14
+ "TMPDIR",
15
+ "TMP",
16
+ "TEMP",
17
+ "LANG",
18
+ "LC_ALL",
19
+ "LC_CTYPE",
20
+ "TERM",
21
+ "XDG_CONFIG_HOME",
22
+ "DBUS_SESSION_BUS_ADDRESS",
23
+ "GH_CONFIG_DIR",
24
+ "GH_HOST",
25
+ "GH_REPO",
26
+ "GH_TOKEN",
27
+ "GITHUB_TOKEN",
28
+ "GH_ENTERPRISE_TOKEN",
29
+ "GITHUB_ENTERPRISE_TOKEN",
30
+ "HTTP_PROXY",
31
+ "http_proxy",
32
+ "HTTPS_PROXY",
33
+ "https_proxy",
34
+ "ALL_PROXY",
35
+ "all_proxy",
36
+ "NO_PROXY",
37
+ "no_proxy",
38
+ "SSL_CERT_FILE",
39
+ "SSL_CERT_DIR",
40
+ ] as const;
41
+
42
+ export const ghEnvironment = (
43
+ environment: Record<string, string | undefined> = process.env,
44
+ ): Record<string, string> =>
45
+ Object.fromEntries(
46
+ GH_ENVIRONMENT_NAMES.flatMap((name) => {
47
+ const value = environment[name];
48
+ return value === undefined ? [] : [[name, value]];
49
+ }),
50
+ );
@@ -5,6 +5,7 @@ import type { GitHubRepoConfig } from "#config";
5
5
  import type { RepoInfo } from "./types";
6
6
 
7
7
  import { GH_BINARY } from "./config";
8
+ import { ghEnvironment } from "./environment";
8
9
  import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "./errors";
9
10
  import { retryTransient } from "#shared/retry-transient";
10
11
  import { githubApi } from "./api";
@@ -165,7 +166,8 @@ export class GitHubService extends Context.Service<
165
166
  const command = ChildProcess.make(GH_BINARY, args, {
166
167
  stdout: "pipe",
167
168
  stderr: "pipe",
168
- ...(ghRepo ? { env: { GH_REPO: ghRepo }, extendEnv: true } : {}),
169
+ env: { ...ghEnvironment(), ...(ghRepo ? { GH_REPO: ghRepo } : {}) },
170
+ extendEnv: false,
169
171
  });
170
172
 
171
173
  const proc = yield* executor.spawn(command);
@@ -12,13 +12,19 @@ const SENSITIVE_PATH_PATTERNS = [
12
12
  /\.envrc$/,
13
13
  /\.(pem|key|p12|pfx|cer|crt)$/i,
14
14
  /(?:^|[\\/])(credentials?|passwd|shadow)$/i,
15
+ /(?:^|\/)(?:\.ssh|\.aws|\.kube|\.azure|secrets?|credentials?)(?:\/|$)/i,
16
+ /(?:^|\/)(?:\.npmrc|\.netrc|\.git-credentials|\.sentryclirc|\.pypirc|\.pgpass)$/i,
17
+ /(?:^|\/)\.docker\/config\.json$/i,
18
+ /(?:^|\/)\.config\/gh\/hosts\.yml$/i,
19
+ /(?:^|\/)GitHub CLI\/hosts\.yml$/i,
20
+ /(?:^|\/)(?:kubeconfig(?:[.-].+)?|[^/]+\.kubeconfig)$/i,
15
21
  ];
16
22
  const MissingMode = Schema.Literals(["error", "null", "default"]);
17
23
 
18
24
  const readTextFromStdin = () => Bun.stdin.text();
19
25
 
20
26
  export const isSensitivePath = (filePath: string) =>
21
- SENSITIVE_PATH_PATTERNS.some((pattern) => pattern.test(filePath));
27
+ SENSITIVE_PATH_PATTERNS.some((pattern) => pattern.test(filePath.replaceAll("\\", "/")));
22
28
 
23
29
  export const validateOutboundText = (text: string, command: string) => {
24
30
  const reason = unsafeOutboundTextReason(text);
package/src/shared/bun.ts CHANGED
@@ -6,7 +6,7 @@ export function expandPath(path: string): string {
6
6
  if (!home || home.trim() === "") {
7
7
  throw new Error("HOME environment variable not set");
8
8
  }
9
- return path.replace("~", home);
9
+ return home + path.slice(1);
10
10
  }
11
11
  return path;
12
12
  }