Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .aiox-core/install-manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# - File types for categorization
#
version: 5.3.0
generated_at: "2026-07-11T16:43:33.419Z"
generated_at: "2026-07-11T17:07:50.052Z"
generator: scripts/generate-install-manifest.js
file_count: 1164
files:
Expand Down Expand Up @@ -4553,9 +4553,9 @@ files:
type: script
size: 9488
- path: scripts/pm.sh
hash: sha256:5e066c431a20e8ff1be7c1d17ef04f0a76e9bcd82efa9cdd8e8887662a34d1e1
hash: sha256:dae90035d05e3756bc450526e6b7336625d017552d6809bb00012a2f525cd4b1
type: script
size: 15321
size: 15958
- path: scripts/README.md
hash: sha256:4b0aa7d4664919c7d7b241b6451bd97bf07ca66e7e5f6d4b455e12e094d0e6ae
type: script
Expand Down
9 changes: 8 additions & 1 deletion .aiox-core/scripts/pm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ Environment Variables:
AIOX_DEBUG Enable debug mode (default: false)
AIOX_TIMEOUT Timeout in seconds (default: 300)
AIOX_INLINE_MODE Run without a visual terminal (default: false)
AIOX_NO_VISUAL_TERMINAL
Never open a visual terminal, force inline (default: false;
also implied by JEST_WORKER_ID, NODE_ENV=test, or CI=true)
CLAUDE_CMD Claude CLI command (default: claude)
AIOX_MODEL_BUDGET_CEILING_USD
Required positive ceiling for automated model dispatch
Expand Down Expand Up @@ -482,7 +485,11 @@ spawn_terminal() {
log_debug "Created lock file: $LOCK_FILE"

# Check for inline mode (Story 12.10 - fallback for non-visual environments)
if [[ "$INLINE_MODE" == "true" ]]; then
# Shell-level defense in depth (#802 review follow-up): direct `bash pm.sh …`
# invocations bypass the Node spawner's detectEnvironment(), so mirror its
# guards here — test runners (JEST_WORKER_ID / NODE_ENV=test), CI, and the
# explicit AIOX_NO_VISUAL_TERMINAL opt-out must never open a real terminal.
if [[ "$INLINE_MODE" == "true" || -n "${JEST_WORKER_ID:-}" || "${NODE_ENV:-}" == "test" || "${CI:-false}" == "true" || "${AIOX_NO_VISUAL_TERMINAL:-false}" == "true" ]]; then
local inline_result=0
spawn_inline || inline_result=$?
if [[ $inline_result -ne 0 ]]; then
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/terminal-spawner-shell-safety.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,22 @@ describe('terminal dispatch shell safety', () => {
expect(source).not.toContain('full_cmd+=" ${PARAMS}"');
expect(source).toContain('osascript - "$cmd"');
});

it('guards spawn_terminal at the shell level against test/CI/no-visual environments (#802 follow-up)', () => {
// Direct `bash pm.sh …` invocations bypass the Node spawner's
// detectEnvironment(); the shell guard must mirror it so a test runner,
// CI, or an explicit opt-out can never open a real terminal window.
const source = fs.readFileSync(
path.resolve(__dirname, '../../.aiox-core/scripts/pm.sh'),
'utf8',
);
const guard = source
.split('\n')
.find((line) => line.includes('if [[') && line.includes('$INLINE_MODE'));
expect(guard).toBeDefined();
expect(guard).toContain('JEST_WORKER_ID');
expect(guard).toContain('NODE_ENV');
expect(guard).toContain('${CI:-false}');
expect(guard).toContain('AIOX_NO_VISUAL_TERMINAL');
});
});
Loading