deepdebug-local-agent 1.0.23 → 1.2.0
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/.claude/context.md +76 -0
- package/.claude/skills/README.md +25 -0
- package/.claude/skills/tdd-debugging/SKILL.md +53 -0
- package/.claude/skills/tdd-debugging/references/anti-patterns.md +43 -0
- package/.claude/skills/tdd-debugging/references/command-reference.md +46 -0
- package/.claude/standards/README.md +51 -0
- package/.claude/standards/clean-code.md +75 -0
- package/.claude/standards/code-review-checklist.md +61 -0
- package/.claude/standards/layered-architecture.md +163 -0
- package/.claude/standards/node.md +45 -0
- package/.claude/standards/security-owasp.md +114 -0
- package/.claude/standards/simplicity.md +42 -0
- package/.claude/standards/solid.md +64 -0
- package/.claude/standards/testing.md +59 -0
- package/.cursor/rules/standards.mdc +17 -0
- package/.github/workflows/build-byoc-image.yml +315 -0
- package/.github/workflows/deploy-gcp.yml +102 -0
- package/.github/workflows/npm-publish.yml +76 -8
- package/AGENTS.md +21 -0
- package/CHANGELOG.md +76 -0
- package/CLAUDE.md +13 -0
- package/CLAUDE_CODE_AGENT_INFINITE.md +5 -3
- package/Dockerfile +4 -0
- package/Dockerfile.full +55 -3
- package/bin/install.js +16 -5
- package/cloudbuild-full.yaml +57 -0
- package/docker-compose.yml +5 -3
- package/docs/OBSERVABILITY.md +164 -0
- package/docs/byoc-agent-image.md +70 -0
- package/docs/dlp.md +78 -0
- package/docs/graphify-packaging-plan.md +133 -0
- package/docs/graphify-validation-run-2026-10-28.md +243 -0
- package/docs/releasing.md +37 -0
- package/docs/signed-commands.md +163 -0
- package/env.example +34 -0
- package/helm/Chart.yaml +2 -2
- package/helm/templates/deployment.yaml +13 -3
- package/helm/templates/networkpolicy.yaml +22 -0
- package/helm/templates/secret.yaml +1 -0
- package/helm/values.yaml +13 -14
- package/mcp-server.js +4 -1
- package/package.json +9 -4
- package/scripts/next-version.mjs +75 -0
- package/scripts/upgrade-observability-push.sh +227 -0
- package/src/ai-engine.js +22 -22
- package/src/config/trusted-signing-keys.json +3 -0
- package/src/detectors/build-tool-detector.js +50 -0
- package/src/detectors/multi-project-detector.js +22 -22
- package/src/detectors/service-detector.js +24 -24
- package/src/dlp/engine.js +111 -0
- package/src/dlp/index.js +137 -0
- package/src/dlp/rules.js +97 -0
- package/src/dlp/validators.js +123 -0
- package/src/exec-utils.js +26 -4
- package/src/git/base-git-provider.js +117 -117
- package/src/git/bitbucket-provider.js +2 -2
- package/src/git/git-provider-registry.js +18 -18
- package/src/git/github-provider.js +13 -13
- package/src/git/multi-repo-pr.js +161 -0
- package/src/mcp-http-server.js +32 -16
- package/src/observability/bundle-files.js +418 -0
- package/src/observability/detect.js +151 -0
- package/src/observability/provision.js +212 -0
- package/src/observability/render-cli.js +59 -0
- package/src/observability/routes.js +88 -0
- package/src/patch/patch-engine.js +28 -28
- package/src/patch-manager.js +85 -85
- package/src/patch_bkp.js +14 -14
- package/src/ports.js +4 -4
- package/src/routes/workspace.route.js +47 -16
- package/src/runtimes/base-runtime.js +75 -75
- package/src/runtimes/java/gradle-runtime.js +17 -17
- package/src/runtimes/java/java-integrations.js +25 -25
- package/src/runtimes/java/maven-runtime.js +34 -34
- package/src/runtimes/node/node-integrations.js +1 -1
- package/src/runtimes/node/npm-runtime.js +14 -14
- package/src/runtimes/node/yarn-runtime.js +4 -4
- package/src/runtimes/runtime-registry.js +25 -25
- package/src/server-local.js +4 -4
- package/src/server.js +1366 -333
- package/src/siblings/burst-suppressor.js +89 -0
- package/src/siblings/capabilities.js +273 -0
- package/src/siblings/env-compat.cjs +58 -0
- package/src/siblings/incident-forwarder.js +322 -0
- package/src/siblings/logger.js +15 -0
- package/src/siblings/metrics.js +94 -0
- package/src/utils/agent-auth.js +184 -0
- package/src/utils/approval-token.js +56 -0
- package/src/utils/bind-host.js +29 -0
- package/src/utils/branch-refspec.js +98 -0
- package/src/utils/child-env.js +34 -0
- package/src/utils/environment-diagnostics.js +55 -55
- package/src/utils/exec-policy.js +962 -0
- package/src/utils/exec-utils.js +15 -15
- package/src/utils/file-count.js +46 -0
- package/src/utils/fs-utils.js +10 -10
- package/src/utils/multi-repo-open.js +132 -0
- package/src/utils/open-gate.js +183 -0
- package/src/utils/ranged-read.js +65 -0
- package/src/utils/request-guards.js +77 -0
- package/src/utils/request-signing.js +342 -0
- package/src/utils/root-recovery.js +244 -0
- package/src/utils/safe-exec-args.js +58 -0
- package/src/utils/sandbox.js +182 -0
- package/src/utils/search-outcome.js +56 -0
- package/src/utils/workspace-root.js +80 -0
- package/src/validators/syntax-validator.js +149 -0
- package/src/workspace/detect-port.js +16 -16
- package/src/workspace/file-reader.js +1 -1
- package/src/workspace/process-manager.js +46 -46
- package/src/workspace-manager.js +86 -24
- package/test/adapters/registry.test.js +9 -2
- package/test/agent-auth.test.js +194 -0
- package/test/bind-host.test.js +26 -0
- package/test/branch-refspec.test.js +224 -0
- package/test/burst-suppressor.test.js +60 -0
- package/test/capabilities-access.test.js +47 -0
- package/test/capabilities.test.js +87 -0
- package/test/child-env.test.js +23 -0
- package/test/dlp/engine.test.js +173 -0
- package/test/dlp/fixtures.js +126 -0
- package/test/dlp/index.test.js +166 -0
- package/test/env-compat.test.js +71 -0
- package/test/exec-policy.test.js +463 -0
- package/test/file-count.test.js +64 -0
- package/test/incident-forwarder.test.js +231 -0
- package/test/multi-repo-open.test.js +180 -0
- package/test/multi-repo-pr.test.js +129 -0
- package/test/next-version.test.js +48 -0
- package/test/observability-detect.test.js +199 -0
- package/test/open-gate.test.js +341 -0
- package/test/ranged-read.test.js +60 -0
- package/test/request-guards.test.js +91 -0
- package/test/request-signing.test.js +284 -0
- package/test/root-recovery-sidecar.test.js +171 -0
- package/test/root-recovery.test.js +274 -0
- package/test/safe-exec-args.test.js +58 -0
- package/test/sandbox.test.js +98 -0
- package/test/search-outcome.test.js +50 -0
- package/test/workspace-root.test.js +64 -0
- package/tools/graphify-validation/README.md +92 -0
- package/tools/graphify-validation/config/questions.json +31 -0
- package/tools/graphify-validation/config/repos.json +9 -0
- package/tools/graphify-validation/config/symbols.json +6 -0
- package/tools/graphify-validation/fixtures/spring/expected.json +34 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AppConfig.java +29 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AuditLogger.java +15 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/Cache.java +7 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/CheckoutService.java +25 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/InMemoryCache.java +18 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/OrderController.java +22 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaymentGateway.java +5 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaypalGateway.java +11 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/RefundService.java +14 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/StripeGateway.java +11 -0
- package/tools/graphify-validation/lib/affected_vs_rg.py +81 -0
- package/tools/graphify-validation/lib/audit_hook/sitecustomize.py +22 -0
- package/tools/graphify-validation/lib/graph_metrics.py +96 -0
- package/tools/graphify-validation/lib/gv_common.py +130 -0
- package/tools/graphify-validation/lib/licenses.py +47 -0
- package/tools/graphify-validation/lib/pip_audit_summary.py +18 -0
- package/tools/graphify-validation/lib/recall.py +115 -0
- package/tools/graphify-validation/lib/report.py +226 -0
- package/tools/graphify-validation/lib/run_all.py +237 -0
- package/tools/graphify-validation/lib/run_graphify.py +144 -0
- package/tools/graphify-validation/lib/rusage_helper.py +17 -0
- package/tools/graphify-validation/lib/sample_edges.py +246 -0
- package/tools/graphify-validation/lib/spring_fixture.py +81 -0
- package/tools/graphify-validation/requirements-py311.lock +508 -0
- package/tools/graphify-validation/requirements-tools.in +1 -0
- package/tools/graphify-validation/requirements-tools.lock +371 -0
- package/tools/graphify-validation/requirements.in +1 -0
- package/tools/graphify-validation/requirements.lock +502 -0
- package/tools/graphify-validation/run.sh +43 -0
- package/tools/graphify-validation/tests/test_harness.py +151 -0
- package/cloudbuild-agent-qa.yaml +0 -43
- package/cloudbuild-agent-staging.yaml +0 -40
- package/cloudbuild.yaml.deprecated +0 -42
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepdebug-local-agent",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "DeepDebug Local Agent - AI-powered code debugging assistant",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -41,9 +41,14 @@
|
|
|
41
41
|
"js-yaml": "^4.1.0",
|
|
42
42
|
"pg": "^8.20.0",
|
|
43
43
|
"pidusage": "^3.0.2",
|
|
44
|
+
"pino": "^10.3.1",
|
|
45
|
+
"prom-client": "^15.1.3",
|
|
44
46
|
"properties-reader": "^2.3.0",
|
|
45
47
|
"ssh2": "^1.16.0",
|
|
46
48
|
"strip-ansi": "^7.1.0",
|
|
49
|
+
"tree-sitter": "^0.22.4",
|
|
50
|
+
"tree-sitter-swift": "^0.7.1",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
47
52
|
"unidiff": "^1.0.2",
|
|
48
53
|
"ws": "^8.18.0"
|
|
49
54
|
},
|
|
@@ -75,10 +80,10 @@
|
|
|
75
80
|
"license": "Proprietary",
|
|
76
81
|
"repository": {
|
|
77
82
|
"type": "git",
|
|
78
|
-
"url": "https://github.com/
|
|
83
|
+
"url": "git+https://github.com/VISIONHAMMER/deepdebug-local-agent.git"
|
|
79
84
|
},
|
|
80
|
-
"homepage": "https://
|
|
85
|
+
"homepage": "https://visionhammer.io",
|
|
81
86
|
"bugs": {
|
|
82
|
-
"url": "https://github.com/
|
|
87
|
+
"url": "https://github.com/VISIONHAMMER/deepdebug-local-agent/issues"
|
|
83
88
|
}
|
|
84
89
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* next-version.mjs: semantic version for the automatic npm release.
|
|
4
|
+
*
|
|
5
|
+
* major (X.0.0) a breaking change: "type!:" subject or a "BREAKING CHANGE:" footer
|
|
6
|
+
* minor (0.X.0) new functionality: a "feat:" commit
|
|
7
|
+
* patch (0.0.X) everything else (fix, perf, refactor, docs, chore, ci, ...)
|
|
8
|
+
*
|
|
9
|
+
* Conventional Commits. The highest level among the commits since the last release
|
|
10
|
+
* tag wins. Merge commits and the CI auto-bump commits ("[skip ci]") are ignored.
|
|
11
|
+
*
|
|
12
|
+
* CLI: git log -z --no-merges --format=%B <range> | node scripts/next-version.mjs <base-version>
|
|
13
|
+
* prints the next version; stderr says which commit decided the bump.
|
|
14
|
+
*/
|
|
15
|
+
import { fileURLToPath } from 'node:url'
|
|
16
|
+
|
|
17
|
+
const SUBJECT_TYPE = /^([a-z]+)(\([^)]*\))?(!)?:\s/i
|
|
18
|
+
const BREAKING_FOOTER = /^BREAKING[ -]CHANGE:\s/m
|
|
19
|
+
|
|
20
|
+
/** @param {string} message full commit message (subject + body) */
|
|
21
|
+
export function classify(message) {
|
|
22
|
+
const text = String(message || '').trim()
|
|
23
|
+
if (!text) return null
|
|
24
|
+
const subject = text.split('\n', 1)[0]
|
|
25
|
+
if (/\[skip ci\]/i.test(subject) || /^Merge /.test(subject)) return null
|
|
26
|
+
const m = SUBJECT_TYPE.exec(subject)
|
|
27
|
+
if (BREAKING_FOOTER.test(text) || (m && m[3])) return 'major'
|
|
28
|
+
if (m && m[1].toLowerCase() === 'feat') return 'minor'
|
|
29
|
+
return 'patch'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const RANK = { patch: 0, minor: 1, major: 2 }
|
|
33
|
+
|
|
34
|
+
/** @param {string[]} messages commit messages since the last release */
|
|
35
|
+
export function bumpFor(messages) {
|
|
36
|
+
let level = 'patch'
|
|
37
|
+
for (const msg of messages) {
|
|
38
|
+
const c = classify(msg)
|
|
39
|
+
if (c && RANK[c] > RANK[level]) level = c
|
|
40
|
+
}
|
|
41
|
+
return level
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** @param {string} base X.Y.Z @param {'major'|'minor'|'patch'} bump */
|
|
45
|
+
export function nextVersion(base, bump) {
|
|
46
|
+
const parts = String(base).split('.').map(Number)
|
|
47
|
+
if (parts.length !== 3 || parts.some((n) => !Number.isInteger(n) || n < 0)) {
|
|
48
|
+
throw new Error(`base version '${base}' is not X.Y.Z`)
|
|
49
|
+
}
|
|
50
|
+
const [a, b, c] = parts
|
|
51
|
+
if (bump === 'major') return `${a + 1}.0.0`
|
|
52
|
+
if (bump === 'minor') return `${a}.${b + 1}.0`
|
|
53
|
+
return `${a}.${b}.${c + 1}`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function main() {
|
|
57
|
+
const base = process.argv[2]
|
|
58
|
+
if (!base) {
|
|
59
|
+
console.error('usage: next-version.mjs <base-version> < commit messages separated by NUL')
|
|
60
|
+
process.exit(2)
|
|
61
|
+
}
|
|
62
|
+
const chunks = []
|
|
63
|
+
for await (const chunk of process.stdin) chunks.push(chunk)
|
|
64
|
+
const messages = Buffer.concat(chunks).toString('utf8').split('\0')
|
|
65
|
+
const bump = bumpFor(messages)
|
|
66
|
+
console.error(`[next-version] ${messages.filter((m) => classify(m)).length} releasable commit(s) since the last release, bump=${bump}`)
|
|
67
|
+
console.log(nextVersion(base, bump))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
71
|
+
main().catch((e) => {
|
|
72
|
+
console.error(e.message)
|
|
73
|
+
process.exit(1)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Upgrade an installed DeepDebug client observability bundle so that:
|
|
3
|
+
# 1. Alloy also pushes the Docker log stream to VisionHammer
|
|
4
|
+
# (OTLP/HTTP JSON -> <gateway>/api/v1/webhooks/logs/otlp, Bearer <trigger secret>),
|
|
5
|
+
# which feeds Live logs and the services list;
|
|
6
|
+
# 2. Alertmanager sends the trigger secret as a Bearer header instead of ?s= in the URL.
|
|
7
|
+
#
|
|
8
|
+
# Safe to re-run (idempotent). Backs up both files, validates the new configs with the
|
|
9
|
+
# same images the stack runs, restarts only alloy + alertmanager, and rolls back on failure.
|
|
10
|
+
#
|
|
11
|
+
# Usage (on the server, as a user that can run docker):
|
|
12
|
+
# sudo bash upgrade-observability-push.sh # apply
|
|
13
|
+
# sudo bash upgrade-observability-push.sh --dry-run # show what would change
|
|
14
|
+
# Optional env:
|
|
15
|
+
# INSTALL_DIR=/path/to/deepdebug-observability (auto-detected otherwise)
|
|
16
|
+
# GATEWAY_URL=https://api-qa.visionhammer.io (read from alertmanager.yml otherwise)
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
DRY_RUN=0
|
|
20
|
+
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=1
|
|
21
|
+
|
|
22
|
+
log() { printf '\033[1;34m[upgrade]\033[0m %s\n' "$*"; }
|
|
23
|
+
warn() { printf '\033[1;33m[upgrade]\033[0m %s\n' "$*" >&2; }
|
|
24
|
+
die() { printf '\033[1;31m[upgrade]\033[0m %s\n' "$*" >&2; exit 1; }
|
|
25
|
+
mask() { local s="$1"; printf '%s...%s' "${s:0:8}" "${s: -4}"; }
|
|
26
|
+
|
|
27
|
+
command -v docker >/dev/null || die "docker not found"
|
|
28
|
+
docker compose version >/dev/null 2>&1 || die "docker compose plugin not found"
|
|
29
|
+
command -v curl >/dev/null || die "curl not found"
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------- locate the stack
|
|
32
|
+
find_install_dir() {
|
|
33
|
+
if [[ -n "${INSTALL_DIR:-}" ]]; then echo "$INSTALL_DIR"; return; fi
|
|
34
|
+
# 1) the running compose project
|
|
35
|
+
local cfg
|
|
36
|
+
cfg=$(docker compose ls --all --format json 2>/dev/null \
|
|
37
|
+
| tr '{' '\n' | grep '"Name":"deepdebug-observability"' \
|
|
38
|
+
| sed -n 's/.*"ConfigFiles":"\([^",]*\).*/\1/p' | head -1 || true)
|
|
39
|
+
if [[ -n "$cfg" && -f "$cfg" ]]; then dirname "$cfg"; return; fi
|
|
40
|
+
# 2) default provision paths
|
|
41
|
+
local d
|
|
42
|
+
for d in "$HOME/.deepdebug/observability/deepdebug-observability" \
|
|
43
|
+
/root/.deepdebug/observability/deepdebug-observability \
|
|
44
|
+
/home/*/.deepdebug/observability/deepdebug-observability; do
|
|
45
|
+
[[ -f "$d/docker-compose.yml" ]] && { echo "$d"; return; }
|
|
46
|
+
done
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
DIR=$(find_install_dir || true)
|
|
50
|
+
[[ -n "$DIR" && -f "$DIR/docker-compose.yml" ]] \
|
|
51
|
+
|| die "observability bundle not found; set INSTALL_DIR=/path/to/deepdebug-observability"
|
|
52
|
+
ALLOY="$DIR/alloy/config.alloy"
|
|
53
|
+
AM="$DIR/alertmanager/alertmanager.yml"
|
|
54
|
+
[[ -f "$ALLOY" ]] || die "missing $ALLOY"
|
|
55
|
+
[[ -f "$AM" ]] || die "missing $AM"
|
|
56
|
+
log "bundle: $DIR"
|
|
57
|
+
|
|
58
|
+
# ---------------------------------------------------------------- secret + gateway
|
|
59
|
+
SECRET=$(grep -oE 'vht_[A-Za-z0-9._-]+' "$AM" | head -1 || true)
|
|
60
|
+
[[ -n "$SECRET" ]] || die "no vht_ trigger secret found in $AM"
|
|
61
|
+
if [[ -z "${GATEWAY_URL:-}" ]]; then
|
|
62
|
+
GATEWAY_URL=$(grep -oE "https?://[^/'\" ]+" "$AM" | head -1 || true)
|
|
63
|
+
fi
|
|
64
|
+
GATEWAY_URL="${GATEWAY_URL%/}"
|
|
65
|
+
[[ "$GATEWAY_URL" =~ ^https?://[A-Za-z0-9.:-]+$ ]] || die "could not determine GATEWAY_URL (set it explicitly)"
|
|
66
|
+
log "gateway: $GATEWAY_URL secret: $(mask "$SECRET")"
|
|
67
|
+
case "$GATEWAY_URL" in
|
|
68
|
+
https://api-qa.visionhammer.io|https://api.visionhammer.io) ;;
|
|
69
|
+
*) warn "gateway is not api-qa/api.visionhammer.io; the push endpoint only exists on the current gateway." ;;
|
|
70
|
+
esac
|
|
71
|
+
|
|
72
|
+
# ---------------------------------------------------------------- 1. probe the endpoint
|
|
73
|
+
# An empty OTLP batch authenticates and registers the SELF_HOSTED log source without
|
|
74
|
+
# writing any log line.
|
|
75
|
+
code=$(curl -sS -o /tmp/vh-otlp-probe.$$ -w '%{http_code}' -m 20 \
|
|
76
|
+
-X POST "$GATEWAY_URL/api/v1/webhooks/logs/otlp" \
|
|
77
|
+
-H "Authorization: Bearer $SECRET" -H 'Content-Type: application/json' \
|
|
78
|
+
--data '{"resourceLogs":[]}' || echo 000)
|
|
79
|
+
case "$code" in
|
|
80
|
+
200) log "push endpoint OK (200)" ;;
|
|
81
|
+
401) die "push endpoint rejected the secret (401): the trigger source is disabled or the secret changed" ;;
|
|
82
|
+
404) die "push endpoint not deployed yet on $GATEWAY_URL (404); retry after the gateway deploy" ;;
|
|
83
|
+
*) die "push endpoint probe failed (HTTP $code): $(head -c 300 /tmp/vh-otlp-probe.$$ 2>/dev/null)" ;;
|
|
84
|
+
esac
|
|
85
|
+
rm -f /tmp/vh-otlp-probe.$$
|
|
86
|
+
|
|
87
|
+
# ---------------------------------------------------------------- 2. build new configs
|
|
88
|
+
TMP=$(mktemp -d)
|
|
89
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
90
|
+
cp "$ALLOY" "$TMP/config.alloy"
|
|
91
|
+
cp "$AM" "$TMP/alertmanager.yml"
|
|
92
|
+
|
|
93
|
+
alloy_changed=0
|
|
94
|
+
if grep -q 'otelcol.exporter.otlphttp "visionhammer"' "$TMP/config.alloy"; then
|
|
95
|
+
log "alloy: push already configured"
|
|
96
|
+
else
|
|
97
|
+
# loki.process "default" must also forward to the OTLP receiver.
|
|
98
|
+
n=$(grep -c 'forward_to = \[loki.write.default.receiver\]' "$TMP/config.alloy" || true)
|
|
99
|
+
[[ "$n" == "1" ]] || die "alloy config does not look like the stock bundle (expected one 'forward_to = [loki.write.default.receiver]', found $n); edit $ALLOY by hand"
|
|
100
|
+
sed -i 's|forward_to = \[loki.write.default.receiver\]|forward_to = [loki.write.default.receiver, otelcol.receiver.loki.visionhammer.receiver]|' "$TMP/config.alloy"
|
|
101
|
+
cat >> "$TMP/config.alloy" <<EOF
|
|
102
|
+
|
|
103
|
+
// ---- push to VisionHammer: Loki entries -> OTLP logs (labels become attributes) ----
|
|
104
|
+
otelcol.receiver.loki "visionhammer" {
|
|
105
|
+
output {
|
|
106
|
+
logs = [otelcol.processor.batch.visionhammer.input]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
otelcol.processor.batch "visionhammer" {
|
|
111
|
+
send_batch_size = 500
|
|
112
|
+
send_batch_max_size = 2000
|
|
113
|
+
timeout = "5s"
|
|
114
|
+
output {
|
|
115
|
+
logs = [otelcol.exporter.otlphttp.visionhammer.input]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
otelcol.exporter.otlphttp "visionhammer" {
|
|
120
|
+
client {
|
|
121
|
+
endpoint = "$GATEWAY_URL"
|
|
122
|
+
compression = "gzip"
|
|
123
|
+
headers = { "Authorization" = "Bearer $SECRET" }
|
|
124
|
+
}
|
|
125
|
+
encoding = "json"
|
|
126
|
+
logs_endpoint = "$GATEWAY_URL/api/v1/webhooks/logs/otlp"
|
|
127
|
+
}
|
|
128
|
+
EOF
|
|
129
|
+
alloy_changed=1
|
|
130
|
+
log "alloy: push blocks added"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
am_changed=0
|
|
134
|
+
if grep -q 'webhooks/alertmanager?s=' "$TMP/alertmanager.yml"; then
|
|
135
|
+
# Drop ?s=... from the URL and add http_config.authorization at the same indent
|
|
136
|
+
# as the url key of that list item.
|
|
137
|
+
# Each receiver keeps ITS OWN secret (a bundle may post to QA and PROD gateways,
|
|
138
|
+
# each registered with a different vht_ secret).
|
|
139
|
+
awk '
|
|
140
|
+
/webhooks\/alertmanager\?s=/ {
|
|
141
|
+
match($0, /vht_[A-Za-z0-9._-]+/)
|
|
142
|
+
secret = substr($0, RSTART, RLENGTH)
|
|
143
|
+
line = $0
|
|
144
|
+
sub(/\?s=[^'"'"'" ]*/, "", line)
|
|
145
|
+
print line
|
|
146
|
+
match($0, /^[ ]*(- )?/)
|
|
147
|
+
ind = RLENGTH
|
|
148
|
+
pad = sprintf("%" ind "s", "")
|
|
149
|
+
print pad "http_config:"
|
|
150
|
+
print pad " authorization:"
|
|
151
|
+
print pad " type: Bearer"
|
|
152
|
+
print pad " credentials: '"'"'" secret "'"'"'"
|
|
153
|
+
next
|
|
154
|
+
}
|
|
155
|
+
{ print }
|
|
156
|
+
' "$TMP/alertmanager.yml" > "$TMP/alertmanager.yml.new"
|
|
157
|
+
mv "$TMP/alertmanager.yml.new" "$TMP/alertmanager.yml"
|
|
158
|
+
am_changed=1
|
|
159
|
+
log "alertmanager: secret moved from ?s= to Authorization header"
|
|
160
|
+
else
|
|
161
|
+
log "alertmanager: already uses header auth (or a custom config); left unchanged"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
if [[ $alloy_changed == 0 && $am_changed == 0 ]]; then
|
|
165
|
+
log "nothing to do"; exit 0
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# Mask every vht_ secret in printed output, not only the first one.
|
|
169
|
+
mask_all() { sed -E 's/(vht_[A-Za-z0-9]{4})[A-Za-z0-9._-]*([A-Za-z0-9._-]{4})/\1...\2/g'; }
|
|
170
|
+
show_diff() { diff -u "$1" "$2" | mask_all || true; }
|
|
171
|
+
[[ $alloy_changed == 1 ]] && show_diff "$ALLOY" "$TMP/config.alloy"
|
|
172
|
+
[[ $am_changed == 1 ]] && show_diff "$AM" "$TMP/alertmanager.yml"
|
|
173
|
+
|
|
174
|
+
if [[ $DRY_RUN == 1 ]]; then log "dry run: nothing written"; exit 0; fi
|
|
175
|
+
|
|
176
|
+
# ---------------------------------------------------------------- 3. validate with the stack's images
|
|
177
|
+
image_of() { grep -E "^\s*image:\s*.*$1" "$DIR/docker-compose.yml" | head -1 | awk '{print $2}'; }
|
|
178
|
+
ALLOY_IMG=$(image_of 'grafana/alloy'); ALLOY_IMG=${ALLOY_IMG:-grafana/alloy:v1.3.1}
|
|
179
|
+
AM_IMG=$(image_of 'prom/alertmanager'); AM_IMG=${AM_IMG:-prom/alertmanager:v0.27.0}
|
|
180
|
+
|
|
181
|
+
if [[ $alloy_changed == 1 ]]; then
|
|
182
|
+
docker run --rm -v "$TMP/config.alloy:/c/config.alloy:ro" "$ALLOY_IMG" fmt /c/config.alloy >/dev/null \
|
|
183
|
+
|| die "new alloy config failed validation; nothing changed"
|
|
184
|
+
log "alloy config valid ($ALLOY_IMG)"
|
|
185
|
+
fi
|
|
186
|
+
if [[ $am_changed == 1 ]]; then
|
|
187
|
+
docker run --rm --entrypoint amtool -v "$TMP/alertmanager.yml:/c/am.yml:ro" "$AM_IMG" check-config /c/am.yml >/dev/null \
|
|
188
|
+
|| die "new alertmanager config failed validation; nothing changed"
|
|
189
|
+
log "alertmanager config valid ($AM_IMG)"
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
# ---------------------------------------------------------------- 4. apply + restart
|
|
193
|
+
STAMP=$(date +%Y%m%d-%H%M%S)
|
|
194
|
+
cp -p "$ALLOY" "$ALLOY.bak-$STAMP"
|
|
195
|
+
cp -p "$AM" "$AM.bak-$STAMP"
|
|
196
|
+
log "backups: *.bak-$STAMP"
|
|
197
|
+
# Overwrite in place (keeps owner/permissions and the bind-mounted inode semantics on restart).
|
|
198
|
+
cat "$TMP/config.alloy" > "$ALLOY"
|
|
199
|
+
cat "$TMP/alertmanager.yml" > "$AM"
|
|
200
|
+
|
|
201
|
+
rollback() {
|
|
202
|
+
warn "rolling back to *.bak-$STAMP"
|
|
203
|
+
cat "$ALLOY.bak-$STAMP" > "$ALLOY"
|
|
204
|
+
cat "$AM.bak-$STAMP" > "$AM"
|
|
205
|
+
(cd "$DIR" && docker compose restart alloy alertmanager >/dev/null 2>&1) || true
|
|
206
|
+
die "$1"
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
(cd "$DIR" && docker compose restart alloy alertmanager) || rollback "restart failed"
|
|
210
|
+
|
|
211
|
+
# ---------------------------------------------------------------- 5. verify
|
|
212
|
+
log "waiting 30s for alloy to ship its first batch..."
|
|
213
|
+
sleep 30
|
|
214
|
+
running=$(cd "$DIR" && docker compose ps --status running --services 2>/dev/null || true)
|
|
215
|
+
grep -qx alloy <<<"$running" || rollback "alloy is not running after the restart"
|
|
216
|
+
grep -qx alertmanager <<<"$running" || rollback "alertmanager is not running after the restart"
|
|
217
|
+
|
|
218
|
+
errs=$(cd "$DIR" && docker compose logs --since 40s alloy 2>&1 \
|
|
219
|
+
| grep -iE 'level=error|Exporting failed|permanent error' | grep -i 'visionhammer\|otlphttp' || true)
|
|
220
|
+
if [[ -n "$errs" ]]; then
|
|
221
|
+
warn "alloy reported export errors:"
|
|
222
|
+
mask_all <<<"$errs" | tail -5 >&2
|
|
223
|
+
rollback "alloy cannot push to $GATEWAY_URL"
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
log "done. Logs should appear in VisionHammer > Live logs within ~1-2 minutes."
|
|
227
|
+
log "to undo: cp $ALLOY.bak-$STAMP $ALLOY; cp $AM.bak-$STAMP $AM; (cd $DIR && docker compose restart alloy alertmanager)"
|
package/src/ai-engine.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ============================================
|
|
2
2
|
// 🧠 AI VIBE CODING ENGINE
|
|
3
|
-
//
|
|
3
|
+
// File: ai-engine.js
|
|
4
4
|
// Coloca em: ~/deepdebug-local-agent/src/ai-engine.js
|
|
5
5
|
// ============================================
|
|
6
6
|
|
|
@@ -15,15 +15,15 @@ const execAsync = promisify(exec);
|
|
|
15
15
|
/**
|
|
16
16
|
* AIVibeCodingEngine
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* Universal auto-healing system that uses AI to resolve
|
|
19
|
+
* ANY error automatically - not just startup.
|
|
20
20
|
*
|
|
21
21
|
* Features:
|
|
22
|
-
* -
|
|
23
|
-
* -
|
|
22
|
+
* - Real-time error monitoring
|
|
23
|
+
* - Automatic error classification
|
|
24
24
|
* - Quick fixes locais (sem chamar AI)
|
|
25
|
-
* -
|
|
26
|
-
* -
|
|
25
|
+
* - Escalates to AI when quick fixes fail
|
|
26
|
+
* - Fix history for reuse
|
|
27
27
|
*/
|
|
28
28
|
export class AIVibeCodingEngine extends EventEmitter {
|
|
29
29
|
constructor(processManager, getWorkspaceRoot) {
|
|
@@ -34,12 +34,12 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
34
34
|
this.maxRetries = 3;
|
|
35
35
|
this.isActive = true;
|
|
36
36
|
|
|
37
|
-
//
|
|
37
|
+
// History
|
|
38
38
|
this.errorHistory = [];
|
|
39
39
|
this.fixHistory = [];
|
|
40
40
|
this.pendingFixes = [];
|
|
41
41
|
|
|
42
|
-
//
|
|
42
|
+
// State
|
|
43
43
|
this.currentSession = null;
|
|
44
44
|
this.lastSuccessfulConfig = null;
|
|
45
45
|
|
|
@@ -54,14 +54,14 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
54
54
|
// ============================================
|
|
55
55
|
|
|
56
56
|
setupErrorMonitoring() {
|
|
57
|
-
//
|
|
57
|
+
// Monitor ProcessManager logs
|
|
58
58
|
this.processManager.on('log', async ({ serviceId, message, type }) => {
|
|
59
59
|
if (this.isActive && message && this.isError(message)) {
|
|
60
60
|
await this.handleRuntimeError(serviceId, message, type);
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
//
|
|
64
|
+
// Monitor crashes
|
|
65
65
|
this.processManager.on('stopped', async ({ serviceId, code, signal }) => {
|
|
66
66
|
if (this.isActive && code !== 0 && code !== null) {
|
|
67
67
|
console.log(`🧠 [AI-Engine] Process ${serviceId} crashed (code: ${code})`);
|
|
@@ -69,7 +69,7 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
//
|
|
72
|
+
// Monitor process errors
|
|
73
73
|
this.processManager.on('error', async ({ serviceId, error }) => {
|
|
74
74
|
if (this.isActive) {
|
|
75
75
|
console.log(`🧠 [AI-Engine] Process error: ${error}`);
|
|
@@ -212,7 +212,7 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
212
212
|
async handleRuntimeError(serviceId, errorMessage, type) {
|
|
213
213
|
const classification = this.classifyError(errorMessage);
|
|
214
214
|
|
|
215
|
-
// Debounce:
|
|
215
|
+
// Debounce: avoid duplicates within 5 seconds
|
|
216
216
|
const recentSimilar = this.errorHistory.find(e =>
|
|
217
217
|
e.classification.type === classification.type &&
|
|
218
218
|
Date.now() - e.timestamp < 5000
|
|
@@ -235,10 +235,10 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
235
235
|
console.log(`🧠 [AI-Engine] Error detected: ${classification.type} (${classification.severity})`);
|
|
236
236
|
console.log(` Message: ${errorMessage.substring(0, 150)}...`);
|
|
237
237
|
|
|
238
|
-
//
|
|
238
|
+
// Emit the event
|
|
239
239
|
this.emit('error_detected', errorEntry);
|
|
240
240
|
|
|
241
|
-
//
|
|
241
|
+
// If auto-fixable, prepare a fix
|
|
242
242
|
if (classification.autoFixable) {
|
|
243
243
|
const quickFix = this.getQuickFix(classification.type, errorMessage);
|
|
244
244
|
if (quickFix) {
|
|
@@ -357,14 +357,14 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
357
357
|
attempts++;
|
|
358
358
|
console.log(`\n🔄 [AI-Engine] Attempt ${attempts}/${this.maxRetries}`);
|
|
359
359
|
|
|
360
|
-
//
|
|
360
|
+
// Apply pending fixes
|
|
361
361
|
if (attempts > 1 && this.pendingFixes.length > 0) {
|
|
362
362
|
const fix = this.pendingFixes.shift();
|
|
363
363
|
console.log(`💡 [AI-Engine] Applying fix: ${fix.fix.description}`);
|
|
364
364
|
currentConfig = await this.applyFix(currentConfig, fix.fix);
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
//
|
|
367
|
+
// Recompile if needed
|
|
368
368
|
if (currentConfig.recompile) {
|
|
369
369
|
console.log('🔨 [AI-Engine] Recompiling...');
|
|
370
370
|
const compileResult = await this.recompile();
|
|
@@ -379,7 +379,7 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
379
379
|
delete currentConfig.recompile;
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
//
|
|
382
|
+
// Try to start
|
|
383
383
|
const result = await this.attemptStart(currentConfig);
|
|
384
384
|
|
|
385
385
|
this.currentSession.attempts.push({
|
|
@@ -419,14 +419,14 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
419
419
|
lastError = result.error;
|
|
420
420
|
console.log(`❌ [AI-Engine] Attempt ${attempts} failed: ${lastError?.substring(0, 100)}...`);
|
|
421
421
|
|
|
422
|
-
//
|
|
422
|
+
// Get a fix
|
|
423
423
|
const fix = await this.getFix(result.error, result.logs, currentConfig);
|
|
424
424
|
|
|
425
425
|
if (fix) {
|
|
426
426
|
console.log(`💡 [AI-Engine] Fix: ${fix.description}`);
|
|
427
427
|
currentConfig = await this.applyFix(currentConfig, fix);
|
|
428
428
|
} else {
|
|
429
|
-
//
|
|
429
|
+
// Try AI
|
|
430
430
|
console.log(`🤖 [AI-Engine] Requesting AI analysis...`);
|
|
431
431
|
const aiAnalysis = await this.analyzeWithAI('startup', lastError, currentConfig);
|
|
432
432
|
if (aiAnalysis?.newConfig) {
|
|
@@ -536,7 +536,7 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
536
536
|
return quickFix;
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
-
//
|
|
539
|
+
// Check the history
|
|
540
540
|
const similarFix = this.findSimilarFix(classification.type);
|
|
541
541
|
if (similarFix) {
|
|
542
542
|
console.log(`📚 [AI-Engine] Found similar fix in history`);
|
|
@@ -707,7 +707,7 @@ export class AIVibeCodingEngine extends EventEmitter {
|
|
|
707
707
|
}
|
|
708
708
|
|
|
709
709
|
try {
|
|
710
|
-
//
|
|
710
|
+
// Import detectProject dynamically to avoid a circular dependency
|
|
711
711
|
const { detectProject } = await import('./detectors.js');
|
|
712
712
|
const meta = await detectProject(workspaceRoot);
|
|
713
713
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* build-tool-detector.js — PASSO 0 of the fix validation ladder.
|
|
3
|
+
*
|
|
4
|
+
* Thin facade over the battle-tested detectProject() (src/detectors.js) that
|
|
5
|
+
* normalizes its output into the {stack, tier} shape the auto-fix engine
|
|
6
|
+
* consumes. One source of truth: marker priority, multi-language and the
|
|
7
|
+
* "Swift wins" product rule all live in detectProject — this file only maps.
|
|
8
|
+
*
|
|
9
|
+
* Tiers (validation ladder):
|
|
10
|
+
* A = C1 structural + C2 syntax + C3 build (maven/gradle/node)
|
|
11
|
+
* B = C1 + C2, build unsupported on this host (xcode/swift — needs macOS)
|
|
12
|
+
* C = C1 only (anything unrecognized)
|
|
13
|
+
*/
|
|
14
|
+
import { detectProject } from '../detectors.js';
|
|
15
|
+
|
|
16
|
+
const STACK_BY_BUILD_TOOL = {
|
|
17
|
+
maven: 'maven',
|
|
18
|
+
gradle: 'gradle',
|
|
19
|
+
npm: 'node',
|
|
20
|
+
yarn: 'node',
|
|
21
|
+
pnpm: 'node',
|
|
22
|
+
xcodebuild: 'xcode',
|
|
23
|
+
swiftpm: 'xcode',
|
|
24
|
+
cocoapods: 'xcode',
|
|
25
|
+
go: 'go',
|
|
26
|
+
pip: 'python',
|
|
27
|
+
poetry: 'python',
|
|
28
|
+
bundler: 'ruby',
|
|
29
|
+
rake: 'ruby',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const TIER_BY_STACK = {
|
|
33
|
+
maven: 'A',
|
|
34
|
+
gradle: 'A',
|
|
35
|
+
node: 'A',
|
|
36
|
+
xcode: 'B',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export async function detectBuildTool(workspacePath) {
|
|
40
|
+
const meta = await detectProject(workspacePath);
|
|
41
|
+
const buildTool = meta?.buildTool || 'unknown';
|
|
42
|
+
const stack = STACK_BY_BUILD_TOOL[buildTool] || 'unknown';
|
|
43
|
+
return {
|
|
44
|
+
stack,
|
|
45
|
+
tier: TIER_BY_STACK[stack] || 'C',
|
|
46
|
+
language: meta?.language || 'unknown',
|
|
47
|
+
buildTool,
|
|
48
|
+
marker: meta?.marker || null,
|
|
49
|
+
};
|
|
50
|
+
}
|