mixdog 0.9.18 → 0.9.20
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/LICENSE +21 -0
- package/README.md +93 -29
- package/package.json +5 -3
- package/scripts/build-runtime-windows.ps1 +242 -242
- package/scripts/build-tui.mjs +6 -0
- package/scripts/hook-bus-test.mjs +8 -0
- package/scripts/log-writer-guard-smoke.mjs +131 -0
- package/scripts/output-style-smoke.mjs +2 -2
- package/scripts/reactive-compact-persist-smoke.mjs +22 -6
- package/scripts/recall-bench-cases.json +10 -0
- package/scripts/recall-bench.mjs +91 -2
- package/scripts/recall-quality-cases.json +1 -2
- package/scripts/recall-usecase-cases.json +1 -1
- package/scripts/session-ingest-compaction-smoke.mjs +241 -0
- package/scripts/smoke-runtime-negative.ps1 +106 -106
- package/scripts/tool-efficiency-diag.mjs +5 -2
- package/scripts/tool-smoke.mjs +251 -72
- package/src/agents/debugger/AGENT.md +3 -3
- package/src/agents/heavy-worker/AGENT.md +7 -10
- package/src/agents/maintainer/AGENT.md +1 -2
- package/src/agents/reviewer/AGENT.md +1 -2
- package/src/agents/worker/AGENT.md +5 -8
- package/src/defaults/agents.json +3 -0
- package/src/help.mjs +2 -5
- package/src/lib/mixdog-debug.cjs +13 -0
- package/src/mixdog-session-runtime.mjs +7 -3311
- package/src/rules/agent/00-core.md +4 -3
- package/src/rules/agent/30-explorer.md +53 -22
- package/src/rules/lead/02-channels.md +3 -3
- package/src/rules/lead/lead-tool.md +3 -2
- package/src/rules/shared/01-tool.md +24 -29
- package/src/runtime/agent/orchestrator/context/collect.mjs +33 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +24 -3
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +663 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +153 -0
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +49 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +250 -35
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/loop.mjs +8 -1860
- package/src/runtime/agent/orchestrator/session/manager/agent-runtime-singleton.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +686 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +60 -12
- package/src/runtime/agent/orchestrator/session/manager/env-utils.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +159 -0
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +143 -0
- package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +268 -0
- package/src/runtime/agent/orchestrator/session/manager/provider-cache-key.mjs +22 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-loaders.mjs +26 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +124 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +258 -0
- package/src/runtime/agent/orchestrator/session/manager/session-errors.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/manager/session-id.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +475 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +6 -4
- package/src/runtime/agent/orchestrator/session/manager.mjs +88 -2285
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +440 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +153 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +642 -0
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +23 -3
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +198 -6
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +10 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +13 -15
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +45 -3
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +195 -3
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +108 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +15 -3
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +7 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +24 -2
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +72 -8
- package/src/runtime/agent/orchestrator/tools/shell-policy-danger-target.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +1 -1
- package/src/runtime/channels/backends/discord-gateway.mjs +14 -1
- package/src/runtime/channels/backends/discord.mjs +43 -1
- package/src/runtime/channels/index.mjs +6 -2096
- package/src/runtime/channels/lib/inbound-handler.mjs +328 -0
- package/src/runtime/channels/lib/inbound-routing.mjs +19 -12
- package/src/runtime/channels/lib/interaction-handlers.mjs +260 -0
- package/src/runtime/channels/lib/memory-client.mjs +32 -14
- package/src/runtime/channels/lib/network-retry.mjs +23 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +38 -7
- package/src/runtime/channels/lib/owned-runtime.mjs +547 -0
- package/src/runtime/channels/lib/owner-heartbeat.mjs +13 -4
- package/src/runtime/channels/lib/runtime-paths.mjs +35 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +17 -7
- package/src/runtime/channels/lib/transcript-binding.mjs +336 -0
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +39 -2
- package/src/runtime/channels/lib/worker-bootstrap.mjs +97 -0
- package/src/runtime/channels/lib/worker-ipc.mjs +201 -0
- package/src/runtime/channels/lib/worker-main.mjs +771 -0
- package/src/runtime/memory/index.mjs +73 -1725
- package/src/runtime/memory/lib/embedding-provider.mjs +4 -2
- package/src/runtime/memory/lib/embedding-worker.mjs +47 -6
- package/src/runtime/memory/lib/http-router.mjs +772 -0
- package/src/runtime/memory/lib/memory-action-handlers.mjs +901 -0
- package/src/runtime/memory/lib/memory-embed.mjs +28 -7
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +8 -2
- package/src/runtime/memory/lib/memory-recall-store.mjs +182 -9
- package/src/runtime/memory/lib/query-handlers.mjs +38 -6
- package/src/runtime/memory/lib/recall-format.mjs +106 -6
- package/src/runtime/memory/lib/session-ingest-runtime.mjs +401 -0
- package/src/runtime/memory/lib/session-ingest.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +10 -4
- package/src/runtime/shared/background-tasks.mjs +4 -2
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +30 -1
- package/src/session-runtime/boot-profile.mjs +36 -0
- package/src/session-runtime/channel-config-api.mjs +70 -0
- package/src/session-runtime/config-lifecycle.mjs +1 -1
- package/src/session-runtime/context-status.mjs +181 -0
- package/src/session-runtime/cwd-plugins.mjs +46 -3
- package/src/session-runtime/env.mjs +17 -0
- package/src/session-runtime/lifecycle-api.mjs +242 -0
- package/src/session-runtime/mcp-glue.mjs +24 -3
- package/src/session-runtime/model-route-api.mjs +198 -0
- package/src/session-runtime/output-styles.mjs +44 -10
- package/src/session-runtime/provider-auth-api.mjs +135 -0
- package/src/session-runtime/resource-api.mjs +282 -0
- package/src/session-runtime/runtime-core.mjs +2046 -0
- package/src/session-runtime/session-turn-api.mjs +274 -0
- package/src/session-runtime/tool-catalog.mjs +18 -264
- package/src/session-runtime/tool-defs.mjs +2 -2
- package/src/session-runtime/workflow-agents-api.mjs +238 -0
- package/src/session-runtime/workflow.mjs +16 -1
- package/src/standalone/agent-tool.mjs +2 -2
- package/src/standalone/channel-worker.mjs +78 -5
- package/src/standalone/explore-tool.mjs +1 -1
- package/src/standalone/memory-runtime-proxy.mjs +56 -6
- package/src/tui/App.jsx +88 -97
- package/src/tui/app/channel-pickers.mjs +45 -0
- package/src/tui/app/core-memory-picker.mjs +1 -1
- package/src/tui/app/slash-commands.mjs +0 -1
- package/src/tui/app/slash-dispatch.mjs +0 -16
- package/src/tui/app/transcript-window.mjs +44 -1
- package/src/tui/app/use-mouse-input.mjs +15 -2
- package/src/tui/app/use-prompt-handlers.mjs +7 -94
- package/src/tui/app/use-transcript-scroll.mjs +82 -4
- package/src/tui/app/use-transcript-window.mjs +65 -5
- package/src/tui/components/PromptInput.jsx +33 -64
- package/src/tui/components/ToolExecution.jsx +2 -2
- package/src/tui/dist/index.mjs +7908 -7558
- package/src/tui/engine/context-state.mjs +145 -0
- package/src/tui/engine/prompt-history.mjs +27 -0
- package/src/tui/engine/session-api-ext.mjs +478 -0
- package/src/tui/engine/session-api.mjs +545 -0
- package/src/tui/engine/session-flow.mjs +485 -0
- package/src/tui/engine/turn.mjs +1078 -0
- package/src/tui/engine.mjs +69 -2582
- package/src/tui/index.jsx +7 -0
- package/src/tui/lib/voice-setup.mjs +166 -0
- package/src/tui/paste-attachments.mjs +12 -5
- package/src/tui/prompt-history-store.mjs +125 -12
- package/vendor/ink/build/ink.js +16 -1
- package/vendor/ink/build/output.js +30 -4
- package/vendor/ink/build/render.js +5 -0
- package/scripts/bench/cache-probe-tasks.json +0 -8
- package/scripts/bench/lead-review-tasks-r3.json +0 -20
- package/scripts/bench/lead-review-tasks.json +0 -20
- package/scripts/bench/r4-mixed-tasks.json +0 -20
- package/scripts/bench/r5-orchestrated-task.json +0 -7
- package/scripts/bench/review-tasks.json +0 -20
- package/scripts/bench/round-codex.json +0 -114
- package/scripts/bench/round-mixdog-lead-r3.json +0 -269
- package/scripts/bench/round-mixdog-lead.json +0 -269
- package/scripts/bench/round-mixdog.json +0 -126
- package/scripts/bench/round-r10-bigsample.json +0 -679
- package/scripts/bench/round-r11-codexalign.json +0 -257
- package/scripts/bench/round-r13-clientmeta.json +0 -464
- package/scripts/bench/round-r14-betafeatures.json +0 -466
- package/scripts/bench/round-r15-fulldefault.json +0 -462
- package/scripts/bench/round-r16-sessionid.json +0 -466
- package/scripts/bench/round-r17-wirebytes.json +0 -456
- package/scripts/bench/round-r18-prewarm.json +0 -468
- package/scripts/bench/round-r19-clean.json +0 -472
- package/scripts/bench/round-r20-prewarm-clean.json +0 -475
- package/scripts/bench/round-r21-delta-retry.json +0 -473
- package/scripts/bench/round-r22-full-probe.json +0 -693
- package/scripts/bench/round-r23-itemprobe.json +0 -701
- package/scripts/bench/round-r24-shapefix.json +0 -677
- package/scripts/bench/round-r25-serial.json +0 -464
- package/scripts/bench/round-r26-parallel3.json +0 -671
- package/scripts/bench/round-r27-parallel10.json +0 -894
- package/scripts/bench/round-r28-parallel10-stagger.json +0 -882
- package/scripts/bench/round-r29-parallel10-stagger166.json +0 -886
- package/scripts/bench/round-r30-instid.json +0 -253
- package/scripts/bench/round-r31-upgradeprobe.json +0 -256
- package/scripts/bench/round-r32-vs-codex-lead.json +0 -254
- package/scripts/bench/round-r33-vs-codex-codex.json +0 -115
- package/scripts/bench/round-r34-orchestrated.json +0 -120
- package/scripts/bench/round-r35-orchestrated-codex.json +0 -61
- package/scripts/bench/round-r36-orchestrated-capped.json +0 -128
- package/scripts/bench/round-r4-codex.json +0 -114
- package/scripts/bench/round-r4-mixed.json +0 -225
- package/scripts/bench/round-r5-gpt-lead.json +0 -259
- package/scripts/bench/round-r6-codex.json +0 -114
- package/scripts/bench/round-r6-solo.json +0 -257
- package/scripts/bench/round-r7-full.json +0 -254
- package/scripts/bench/round-r8-fulldefault.json +0 -255
- package/src/tui/components/prompt-input/voice-indicator.mjs +0 -39
- package/src/tui/lib/voice-recorder.mjs +0 -469
- package/src/workflows/sequential/WORKFLOW.md +0 -51
|
@@ -1,242 +1,242 @@
|
|
|
1
|
-
# build-runtime-windows.ps1 — Build PostgreSQL 16 + pgvector runtime on Windows.
|
|
2
|
-
# Uses windows-2022 GHA runner's preinstalled PostgreSQL 16 (consistent
|
|
3
|
-
# pg_config + postgres.exe from same package) — avoids EDB zip's split-version
|
|
4
|
-
# packaging bug that linked pgvector against PG 14 ABI.
|
|
5
|
-
# Builds pgvector from source via MSVC/nmake, then assembles a self-contained
|
|
6
|
-
# runtime tree (bin + lib + share at root). Final smoke: initdb + CREATE
|
|
7
|
-
# EXTENSION vector + distance query.
|
|
8
|
-
# Produces: dist\mixdog-runtime-win32-x64-pg{pgver}-pgvector{vecver}.tar.gz
|
|
9
|
-
|
|
10
|
-
$ErrorActionPreference = 'Stop'
|
|
11
|
-
|
|
12
|
-
$PG_VERSION = '16.4'
|
|
13
|
-
$PGVECTOR_VERSION = '0.8.2'
|
|
14
|
-
$TARGET_OS = $env:TARGET_OS ?? 'win32'
|
|
15
|
-
$TARGET_ARCH = $env:TARGET_ARCH ?? 'x64'
|
|
16
|
-
|
|
17
|
-
# Auto-detect highest preinstalled PG ≥ 16 OR install via chocolatey.
|
|
18
|
-
$PgInstallRoot = 'C:\Program Files\PostgreSQL'
|
|
19
|
-
|
|
20
|
-
function Find-PgRoot {
|
|
21
|
-
if (-not (Test-Path $PgInstallRoot)) { return $null }
|
|
22
|
-
$cands = Get-ChildItem $PgInstallRoot -Directory -ErrorAction SilentlyContinue |
|
|
23
|
-
Where-Object { $_.Name -match '^(\d+)' } |
|
|
24
|
-
Sort-Object { [int]([regex]::Match($_.Name, '^(\d+)').Groups[1].Value) } -Descending
|
|
25
|
-
foreach ($c in $cands) {
|
|
26
|
-
$major = [int]([regex]::Match($c.Name, '^(\d+)').Groups[1].Value)
|
|
27
|
-
if ($major -ge 16 -and (Test-Path "$($c.FullName)\bin\pg_config.exe")) {
|
|
28
|
-
return $c.FullName
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return $null
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
$PgRoot = Find-PgRoot
|
|
35
|
-
if (-not $PgRoot) {
|
|
36
|
-
Write-Host "==> No preinstalled PG ≥ 16 found. Installing via chocolatey..."
|
|
37
|
-
if (Test-Path $PgInstallRoot) {
|
|
38
|
-
Write-Host "Existing PG dirs (none usable):"
|
|
39
|
-
Get-ChildItem $PgInstallRoot -Directory -ErrorAction SilentlyContinue | Select-Object Name
|
|
40
|
-
}
|
|
41
|
-
choco install postgresql16 --version=16.4.0 --params '/Password:postgres' -y --no-progress 2>&1 | Out-Host
|
|
42
|
-
if ($LASTEXITCODE -ne 0) { Write-Error "choco install postgresql16 failed (exit $LASTEXITCODE)"; exit 1 }
|
|
43
|
-
$PgRoot = Find-PgRoot
|
|
44
|
-
if (-not $PgRoot) {
|
|
45
|
-
Write-Error "ASSERT FAILED: chocolatey install completed but PG ≥ 16 still not found under $PgInstallRoot"
|
|
46
|
-
Get-ChildItem $PgInstallRoot -Directory -ErrorAction SilentlyContinue | Select-Object Name
|
|
47
|
-
exit 1
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
52
|
-
$RootDir = (Resolve-Path "$ScriptDir\..").Path
|
|
53
|
-
$BuildDir = "$RootDir\build\runtime-win32-$TARGET_ARCH"
|
|
54
|
-
$DistDir = "$RootDir\dist"
|
|
55
|
-
$RuntimeDir = "$BuildDir\runtime"
|
|
56
|
-
|
|
57
|
-
$PgBin = "$PgRoot\bin"
|
|
58
|
-
$PgConfig = "$PgBin\pg_config.exe"
|
|
59
|
-
|
|
60
|
-
$OutputName = "mixdog-runtime-${TARGET_OS}-${TARGET_ARCH}-pg${PG_VERSION}-pgvector${PGVECTOR_VERSION}.tar.gz"
|
|
61
|
-
|
|
62
|
-
Write-Host "==> Using preinstalled PG: $PgRoot"
|
|
63
|
-
& $PgConfig --version
|
|
64
|
-
$RealVersion = (& $PgConfig --version) -replace 'PostgreSQL ', ''
|
|
65
|
-
Write-Host " pg_config reports version: $RealVersion"
|
|
66
|
-
|
|
67
|
-
if (Test-Path $RuntimeDir) { Remove-Item -Recurse -Force $RuntimeDir }
|
|
68
|
-
New-Item -ItemType Directory -Force -Path $BuildDir, $DistDir,
|
|
69
|
-
"$RuntimeDir\bin", "$RuntimeDir\lib", "$RuntimeDir\share" | Out-Null
|
|
70
|
-
|
|
71
|
-
Write-Host "==> Cloning pgvector $PGVECTOR_VERSION"
|
|
72
|
-
$PgVectorDir = "$BuildDir\pgvector"
|
|
73
|
-
$VectorDllBuilt = "$PgVectorDir\vector.dll"
|
|
74
|
-
|
|
75
|
-
if (Test-Path $VectorDllBuilt) {
|
|
76
|
-
Write-Host " Cache hit: vector.dll already built at $VectorDllBuilt"
|
|
77
|
-
} else {
|
|
78
|
-
if (Test-Path $PgVectorDir) { Remove-Item -Recurse -Force $PgVectorDir }
|
|
79
|
-
git clone --branch "v$PGVECTOR_VERSION" --depth 1 `
|
|
80
|
-
https://github.com/pgvector/pgvector.git $PgVectorDir
|
|
81
|
-
|
|
82
|
-
Write-Host "==> Building pgvector (MSVC/nmake against system PG 16)"
|
|
83
|
-
Push-Location $PgVectorDir
|
|
84
|
-
try {
|
|
85
|
-
$VsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
|
86
|
-
$VcVarsAll = & $VsWhere -latest -find 'VC\Auxiliary\Build\vcvarsall.bat' 2>$null | Select-Object -First 1
|
|
87
|
-
if (-not $VcVarsAll) {
|
|
88
|
-
Write-Error "vswhere could not locate vcvarsall.bat — Visual Studio Build Tools required."
|
|
89
|
-
exit 1
|
|
90
|
-
}
|
|
91
|
-
# CRITICAL: prepend $PgRoot\bin to PATH so any bare `pg_config` call
|
|
92
|
-
# inside Makefile.win resolves to PG 16 — runner has PG 14/15
|
|
93
|
-
# preinstalled and would otherwise win the PATH lookup, producing
|
|
94
|
-
# PG14-ABI vector.dll that fails to load in our PG 16 postgres.exe.
|
|
95
|
-
# Set in PowerShell so cmd /c inherits; setting inside the cmd batch
|
|
96
|
-
# via %PATH% loses vcvarsall's additions due to parse-time expansion.
|
|
97
|
-
$env:PATH = "$PgRoot\bin;$env:PATH"
|
|
98
|
-
$env:PGROOT = $PgRoot
|
|
99
|
-
$BuildCmd = "`"$VcVarsAll`" amd64 && nmake /F Makefile.win PG_CONFIG=`"$PgConfig`""
|
|
100
|
-
cmd /c $BuildCmd
|
|
101
|
-
if ($LASTEXITCODE -ne 0) {
|
|
102
|
-
Write-Error "pgvector nmake build failed (exit $LASTEXITCODE)"
|
|
103
|
-
exit 1
|
|
104
|
-
}
|
|
105
|
-
} finally {
|
|
106
|
-
Pop-Location
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
Write-Host "==> Assembling runtime layout — copy bin (.exe + .dll), lib, share from $PgRoot"
|
|
111
|
-
# Copy ALL .exe + .dll from PG bin so postgres.exe + libpq.dll + libcrypto/libssl/
|
|
112
|
-
# libintl/libiconv/icu*/libxml2/libxslt/libwinpthread/libecpg/libpgtypes etc. all
|
|
113
|
-
# ship together. PG 16 install layout puts these all in bin\.
|
|
114
|
-
Copy-Item "$PgBin\*.exe","$PgBin\*.dll" "$RuntimeDir\bin\" -Force
|
|
115
|
-
|
|
116
|
-
# lib\: PG extension modules (incl. contrib like pgcrypto.dll). vector.dll
|
|
117
|
-
# placed here below — PG looks for $libdir/<ext>.dll which resolves to lib\.
|
|
118
|
-
Copy-Item -Recurse -Force "$PgRoot\lib\*" "$RuntimeDir\lib\" -ErrorAction SilentlyContinue
|
|
119
|
-
# share\: extension SQL/control, locale, timezone data, conf samples.
|
|
120
|
-
Copy-Item -Recurse -Force "$PgRoot\share\*" "$RuntimeDir\share\" -ErrorAction SilentlyContinue
|
|
121
|
-
|
|
122
|
-
Write-Host "==> Manually staging pgvector artifacts (avoid pg_config-derived install paths)"
|
|
123
|
-
$RuntimeExtDir = "$RuntimeDir\share\extension"
|
|
124
|
-
New-Item -ItemType Directory -Force -Path $RuntimeExtDir | Out-Null
|
|
125
|
-
Copy-Item "$PgVectorDir\vector.dll" "$RuntimeDir\lib\" -Force
|
|
126
|
-
Copy-Item "$PgVectorDir\vector.control" $RuntimeExtDir -Force
|
|
127
|
-
Copy-Item "$PgVectorDir\sql\vector--*.sql" $RuntimeExtDir -Force
|
|
128
|
-
|
|
129
|
-
Write-Host "==> Asserting runtime layout"
|
|
130
|
-
$VectorControl = "$RuntimeDir\share\extension\vector.control"
|
|
131
|
-
if (-not (Test-Path $VectorControl)) { Write-Error "ASSERT FAILED: $VectorControl not found"; exit 1 }
|
|
132
|
-
$VectorSql = "$RuntimeDir\share\extension\vector--$PGVECTOR_VERSION.sql"
|
|
133
|
-
if (-not (Test-Path $VectorSql)) { Write-Error "ASSERT FAILED: $VectorSql not found"; exit 1 }
|
|
134
|
-
if (-not (Test-Path "$RuntimeDir\lib\vector.dll")) {
|
|
135
|
-
Write-Error "ASSERT FAILED: vector.dll not found in lib\"
|
|
136
|
-
exit 1
|
|
137
|
-
}
|
|
138
|
-
Write-Host " PASS runtime layout"
|
|
139
|
-
|
|
140
|
-
# Licenses
|
|
141
|
-
if (Test-Path "$PgRoot\doc\postgresql\COPYRIGHT") {
|
|
142
|
-
Copy-Item "$PgRoot\doc\postgresql\COPYRIGHT" "$RuntimeDir\LICENSE.postgresql" -Force
|
|
143
|
-
} elseif (Test-Path "$PgRoot\doc\COPYRIGHT") {
|
|
144
|
-
Copy-Item "$PgRoot\doc\COPYRIGHT" "$RuntimeDir\LICENSE.postgresql" -Force
|
|
145
|
-
}
|
|
146
|
-
if (Test-Path "$PgVectorDir\LICENSE") {
|
|
147
|
-
Copy-Item "$PgVectorDir\LICENSE" "$RuntimeDir\LICENSE.pgvector" -Force
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
Write-Host "==> Self-contained smoke test (initdb + CREATE EXTENSION vector + distance query)"
|
|
151
|
-
& "$RuntimeDir\bin\postgres.exe" --version
|
|
152
|
-
if ($LASTEXITCODE -ne 0) { Write-Error "FAIL: postgres.exe --version exit $LASTEXITCODE"; exit 1 }
|
|
153
|
-
|
|
154
|
-
$SmokeData = "$BuildDir\smoke-pgdata"
|
|
155
|
-
$SmokeLog = "$BuildDir\smoke-pg.log"
|
|
156
|
-
$SmokePort = 55899
|
|
157
|
-
if (Test-Path $SmokeData) { Remove-Item -Recurse -Force $SmokeData }
|
|
158
|
-
|
|
159
|
-
& "$RuntimeDir\bin\initdb.exe" -D $SmokeData --username=postgres --auth-local=trust --no-locale -E UTF8 | Out-Null
|
|
160
|
-
if ($LASTEXITCODE -ne 0) { Write-Error "FAIL: initdb"; exit 1 }
|
|
161
|
-
|
|
162
|
-
& "$RuntimeDir\bin\pg_ctl.exe" -D $SmokeData -o "-p $SmokePort -h 127.0.0.1" -l $SmokeLog -w start
|
|
163
|
-
if ($LASTEXITCODE -ne 0) { Write-Error "FAIL: pg_ctl start (see $SmokeLog)"; Get-Content $SmokeLog | Select-Object -Last 30; exit 1 }
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
& "$RuntimeDir\bin\psql.exe" -h 127.0.0.1 -p $SmokePort -U postgres -d postgres -c "CREATE EXTENSION vector;" | Out-Null
|
|
167
|
-
if ($LASTEXITCODE -ne 0) { throw "CREATE EXTENSION vector failed" }
|
|
168
|
-
$ExtV = & "$RuntimeDir\bin\psql.exe" -h 127.0.0.1 -p $SmokePort -U postgres -d postgres -tAc "SELECT extversion FROM pg_extension WHERE extname='vector';"
|
|
169
|
-
$Dist = & "$RuntimeDir\bin\psql.exe" -h 127.0.0.1 -p $SmokePort -U postgres -d postgres -tAc "SELECT '[1,2,3]'::vector <-> '[1,2,4]'::vector;"
|
|
170
|
-
Write-Host " vector extension version: $ExtV"
|
|
171
|
-
Write-Host " distance query result: $Dist"
|
|
172
|
-
if ($ExtV.Trim() -ne $PGVECTOR_VERSION) {
|
|
173
|
-
Write-Error "FAIL: extversion='$ExtV' expected='$PGVECTOR_VERSION'"
|
|
174
|
-
exit 1
|
|
175
|
-
}
|
|
176
|
-
Write-Host " PASS smoke (extension load + vector distance)"
|
|
177
|
-
}
|
|
178
|
-
finally {
|
|
179
|
-
& "$RuntimeDir\bin\pg_ctl.exe" -D $SmokeData -m fast stop 2>$null | Out-Null
|
|
180
|
-
Remove-Item -Recurse -Force $SmokeData -ErrorAction SilentlyContinue
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
Write-Host "==> Creating tarball: $OutputName"
|
|
184
|
-
$DistDirFwd = $DistDir.Replace('\', '/')
|
|
185
|
-
$RuntimeDirFwd = $RuntimeDir.Replace('\', '/')
|
|
186
|
-
& tar -czf "$DistDirFwd/$OutputName" -C "$RuntimeDirFwd" .
|
|
187
|
-
if ($LASTEXITCODE -ne 0) { Write-Error "tar failed (exit $LASTEXITCODE)"; exit 1 }
|
|
188
|
-
|
|
189
|
-
Write-Host "==> Generating sha256 sidecar"
|
|
190
|
-
Push-Location $DistDir
|
|
191
|
-
$Hash = (Get-FileHash -Algorithm SHA256 $OutputName).Hash.ToLower()
|
|
192
|
-
"$Hash $OutputName" | Out-File -Encoding ascii "${OutputName}.sha256"
|
|
193
|
-
Pop-Location
|
|
194
|
-
|
|
195
|
-
# ---------------------------------------------------------------------------
|
|
196
|
-
# Phase A: Re-smoke from EXTRACTED tarball with hostile env (cleared PATH,
|
|
197
|
-
# only system32). Catches false-pass where the build host has VC redist /
|
|
198
|
-
# preinstalled DLLs that the tarball might be missing.
|
|
199
|
-
# ---------------------------------------------------------------------------
|
|
200
|
-
Write-Host "==> Re-smoke from extracted tarball (hostile env)"
|
|
201
|
-
$ExtractDir = "$BuildDir\extract-smoke"
|
|
202
|
-
if (Test-Path $ExtractDir) { Remove-Item -Recurse -Force $ExtractDir }
|
|
203
|
-
New-Item -ItemType Directory -Force -Path $ExtractDir | Out-Null
|
|
204
|
-
& tar -xzf "$DistDirFwd/$OutputName" -C ($ExtractDir.Replace('\','/'))
|
|
205
|
-
if ($LASTEXITCODE -ne 0) { Write-Error "extract failed"; exit 1 }
|
|
206
|
-
|
|
207
|
-
$ExtractData = "$ExtractDir\extract-pgdata"
|
|
208
|
-
$ExtractLog = "$ExtractDir\extract-pg.log"
|
|
209
|
-
$ExtractPort = 55898
|
|
210
|
-
|
|
211
|
-
# Snapshot current env, then strip to minimal Windows PATH (no PG14/15/16
|
|
212
|
-
# preinstalled bin, no chocolatey, no MSVC tools).
|
|
213
|
-
$SavedPath = $env:PATH
|
|
214
|
-
$SavedPgRoot = $env:PGROOT
|
|
215
|
-
$env:PATH = "$env:SystemRoot\System32;$env:SystemRoot"
|
|
216
|
-
$env:PGROOT = $null
|
|
217
|
-
$env:PGDATA = $null
|
|
218
|
-
|
|
219
|
-
try {
|
|
220
|
-
& "$ExtractDir\bin\postgres.exe" --version
|
|
221
|
-
if ($LASTEXITCODE -ne 0) { throw "FAIL: postgres --version under hostile env" }
|
|
222
|
-
& "$ExtractDir\bin\initdb.exe" -D $ExtractData --username=postgres --auth-local=trust --no-locale -E UTF8 | Out-Null
|
|
223
|
-
if ($LASTEXITCODE -ne 0) { throw "FAIL: initdb under hostile env" }
|
|
224
|
-
& "$ExtractDir\bin\pg_ctl.exe" -D $ExtractData -o "-p $ExtractPort -h 127.0.0.1" -l $ExtractLog -w start
|
|
225
|
-
if ($LASTEXITCODE -ne 0) { Get-Content $ExtractLog | Select-Object -Last 30; throw "FAIL: pg_ctl start under hostile env" }
|
|
226
|
-
try {
|
|
227
|
-
& "$ExtractDir\bin\psql.exe" -h 127.0.0.1 -p $ExtractPort -U postgres -d postgres -c "CREATE EXTENSION vector;" | Out-Null
|
|
228
|
-
if ($LASTEXITCODE -ne 0) { throw "FAIL: CREATE EXTENSION vector under hostile env" }
|
|
229
|
-
$ExtV2 = & "$ExtractDir\bin\psql.exe" -h 127.0.0.1 -p $ExtractPort -U postgres -d postgres -tAc "SELECT extversion FROM pg_extension WHERE extname='vector';"
|
|
230
|
-
if ($ExtV2.Trim() -ne $PGVECTOR_VERSION) { throw "FAIL: extracted-smoke extversion='$ExtV2'" }
|
|
231
|
-
Write-Host " PASS extracted-tarball smoke (hostile env)"
|
|
232
|
-
} finally {
|
|
233
|
-
& "$ExtractDir\bin\pg_ctl.exe" -D $ExtractData -m fast stop 2>$null | Out-Null
|
|
234
|
-
}
|
|
235
|
-
} finally {
|
|
236
|
-
$env:PATH = $SavedPath
|
|
237
|
-
$env:PGROOT = $SavedPgRoot
|
|
238
|
-
Remove-Item -Recurse -Force $ExtractDir -ErrorAction SilentlyContinue
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
Write-Host "==> Done: $DistDir\$OutputName"
|
|
242
|
-
Get-Item "$DistDir\$OutputName" | Select-Object Name, Length
|
|
1
|
+
# build-runtime-windows.ps1 — Build PostgreSQL 16 + pgvector runtime on Windows.
|
|
2
|
+
# Uses windows-2022 GHA runner's preinstalled PostgreSQL 16 (consistent
|
|
3
|
+
# pg_config + postgres.exe from same package) — avoids EDB zip's split-version
|
|
4
|
+
# packaging bug that linked pgvector against PG 14 ABI.
|
|
5
|
+
# Builds pgvector from source via MSVC/nmake, then assembles a self-contained
|
|
6
|
+
# runtime tree (bin + lib + share at root). Final smoke: initdb + CREATE
|
|
7
|
+
# EXTENSION vector + distance query.
|
|
8
|
+
# Produces: dist\mixdog-runtime-win32-x64-pg{pgver}-pgvector{vecver}.tar.gz
|
|
9
|
+
|
|
10
|
+
$ErrorActionPreference = 'Stop'
|
|
11
|
+
|
|
12
|
+
$PG_VERSION = '16.4'
|
|
13
|
+
$PGVECTOR_VERSION = '0.8.2'
|
|
14
|
+
$TARGET_OS = $env:TARGET_OS ?? 'win32'
|
|
15
|
+
$TARGET_ARCH = $env:TARGET_ARCH ?? 'x64'
|
|
16
|
+
|
|
17
|
+
# Auto-detect highest preinstalled PG ≥ 16 OR install via chocolatey.
|
|
18
|
+
$PgInstallRoot = 'C:\Program Files\PostgreSQL'
|
|
19
|
+
|
|
20
|
+
function Find-PgRoot {
|
|
21
|
+
if (-not (Test-Path $PgInstallRoot)) { return $null }
|
|
22
|
+
$cands = Get-ChildItem $PgInstallRoot -Directory -ErrorAction SilentlyContinue |
|
|
23
|
+
Where-Object { $_.Name -match '^(\d+)' } |
|
|
24
|
+
Sort-Object { [int]([regex]::Match($_.Name, '^(\d+)').Groups[1].Value) } -Descending
|
|
25
|
+
foreach ($c in $cands) {
|
|
26
|
+
$major = [int]([regex]::Match($c.Name, '^(\d+)').Groups[1].Value)
|
|
27
|
+
if ($major -ge 16 -and (Test-Path "$($c.FullName)\bin\pg_config.exe")) {
|
|
28
|
+
return $c.FullName
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return $null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
$PgRoot = Find-PgRoot
|
|
35
|
+
if (-not $PgRoot) {
|
|
36
|
+
Write-Host "==> No preinstalled PG ≥ 16 found. Installing via chocolatey..."
|
|
37
|
+
if (Test-Path $PgInstallRoot) {
|
|
38
|
+
Write-Host "Existing PG dirs (none usable):"
|
|
39
|
+
Get-ChildItem $PgInstallRoot -Directory -ErrorAction SilentlyContinue | Select-Object Name
|
|
40
|
+
}
|
|
41
|
+
choco install postgresql16 --version=16.4.0 --params '/Password:postgres' -y --no-progress 2>&1 | Out-Host
|
|
42
|
+
if ($LASTEXITCODE -ne 0) { Write-Error "choco install postgresql16 failed (exit $LASTEXITCODE)"; exit 1 }
|
|
43
|
+
$PgRoot = Find-PgRoot
|
|
44
|
+
if (-not $PgRoot) {
|
|
45
|
+
Write-Error "ASSERT FAILED: chocolatey install completed but PG ≥ 16 still not found under $PgInstallRoot"
|
|
46
|
+
Get-ChildItem $PgInstallRoot -Directory -ErrorAction SilentlyContinue | Select-Object Name
|
|
47
|
+
exit 1
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
52
|
+
$RootDir = (Resolve-Path "$ScriptDir\..").Path
|
|
53
|
+
$BuildDir = "$RootDir\build\runtime-win32-$TARGET_ARCH"
|
|
54
|
+
$DistDir = "$RootDir\dist"
|
|
55
|
+
$RuntimeDir = "$BuildDir\runtime"
|
|
56
|
+
|
|
57
|
+
$PgBin = "$PgRoot\bin"
|
|
58
|
+
$PgConfig = "$PgBin\pg_config.exe"
|
|
59
|
+
|
|
60
|
+
$OutputName = "mixdog-runtime-${TARGET_OS}-${TARGET_ARCH}-pg${PG_VERSION}-pgvector${PGVECTOR_VERSION}.tar.gz"
|
|
61
|
+
|
|
62
|
+
Write-Host "==> Using preinstalled PG: $PgRoot"
|
|
63
|
+
& $PgConfig --version
|
|
64
|
+
$RealVersion = (& $PgConfig --version) -replace 'PostgreSQL ', ''
|
|
65
|
+
Write-Host " pg_config reports version: $RealVersion"
|
|
66
|
+
|
|
67
|
+
if (Test-Path $RuntimeDir) { Remove-Item -Recurse -Force $RuntimeDir }
|
|
68
|
+
New-Item -ItemType Directory -Force -Path $BuildDir, $DistDir,
|
|
69
|
+
"$RuntimeDir\bin", "$RuntimeDir\lib", "$RuntimeDir\share" | Out-Null
|
|
70
|
+
|
|
71
|
+
Write-Host "==> Cloning pgvector $PGVECTOR_VERSION"
|
|
72
|
+
$PgVectorDir = "$BuildDir\pgvector"
|
|
73
|
+
$VectorDllBuilt = "$PgVectorDir\vector.dll"
|
|
74
|
+
|
|
75
|
+
if (Test-Path $VectorDllBuilt) {
|
|
76
|
+
Write-Host " Cache hit: vector.dll already built at $VectorDllBuilt"
|
|
77
|
+
} else {
|
|
78
|
+
if (Test-Path $PgVectorDir) { Remove-Item -Recurse -Force $PgVectorDir }
|
|
79
|
+
git clone --branch "v$PGVECTOR_VERSION" --depth 1 `
|
|
80
|
+
https://github.com/pgvector/pgvector.git $PgVectorDir
|
|
81
|
+
|
|
82
|
+
Write-Host "==> Building pgvector (MSVC/nmake against system PG 16)"
|
|
83
|
+
Push-Location $PgVectorDir
|
|
84
|
+
try {
|
|
85
|
+
$VsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
|
86
|
+
$VcVarsAll = & $VsWhere -latest -find 'VC\Auxiliary\Build\vcvarsall.bat' 2>$null | Select-Object -First 1
|
|
87
|
+
if (-not $VcVarsAll) {
|
|
88
|
+
Write-Error "vswhere could not locate vcvarsall.bat — Visual Studio Build Tools required."
|
|
89
|
+
exit 1
|
|
90
|
+
}
|
|
91
|
+
# CRITICAL: prepend $PgRoot\bin to PATH so any bare `pg_config` call
|
|
92
|
+
# inside Makefile.win resolves to PG 16 — runner has PG 14/15
|
|
93
|
+
# preinstalled and would otherwise win the PATH lookup, producing
|
|
94
|
+
# PG14-ABI vector.dll that fails to load in our PG 16 postgres.exe.
|
|
95
|
+
# Set in PowerShell so cmd /c inherits; setting inside the cmd batch
|
|
96
|
+
# via %PATH% loses vcvarsall's additions due to parse-time expansion.
|
|
97
|
+
$env:PATH = "$PgRoot\bin;$env:PATH"
|
|
98
|
+
$env:PGROOT = $PgRoot
|
|
99
|
+
$BuildCmd = "`"$VcVarsAll`" amd64 && nmake /F Makefile.win PG_CONFIG=`"$PgConfig`""
|
|
100
|
+
cmd /c $BuildCmd
|
|
101
|
+
if ($LASTEXITCODE -ne 0) {
|
|
102
|
+
Write-Error "pgvector nmake build failed (exit $LASTEXITCODE)"
|
|
103
|
+
exit 1
|
|
104
|
+
}
|
|
105
|
+
} finally {
|
|
106
|
+
Pop-Location
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
Write-Host "==> Assembling runtime layout — copy bin (.exe + .dll), lib, share from $PgRoot"
|
|
111
|
+
# Copy ALL .exe + .dll from PG bin so postgres.exe + libpq.dll + libcrypto/libssl/
|
|
112
|
+
# libintl/libiconv/icu*/libxml2/libxslt/libwinpthread/libecpg/libpgtypes etc. all
|
|
113
|
+
# ship together. PG 16 install layout puts these all in bin\.
|
|
114
|
+
Copy-Item "$PgBin\*.exe","$PgBin\*.dll" "$RuntimeDir\bin\" -Force
|
|
115
|
+
|
|
116
|
+
# lib\: PG extension modules (incl. contrib like pgcrypto.dll). vector.dll
|
|
117
|
+
# placed here below — PG looks for $libdir/<ext>.dll which resolves to lib\.
|
|
118
|
+
Copy-Item -Recurse -Force "$PgRoot\lib\*" "$RuntimeDir\lib\" -ErrorAction SilentlyContinue
|
|
119
|
+
# share\: extension SQL/control, locale, timezone data, conf samples.
|
|
120
|
+
Copy-Item -Recurse -Force "$PgRoot\share\*" "$RuntimeDir\share\" -ErrorAction SilentlyContinue
|
|
121
|
+
|
|
122
|
+
Write-Host "==> Manually staging pgvector artifacts (avoid pg_config-derived install paths)"
|
|
123
|
+
$RuntimeExtDir = "$RuntimeDir\share\extension"
|
|
124
|
+
New-Item -ItemType Directory -Force -Path $RuntimeExtDir | Out-Null
|
|
125
|
+
Copy-Item "$PgVectorDir\vector.dll" "$RuntimeDir\lib\" -Force
|
|
126
|
+
Copy-Item "$PgVectorDir\vector.control" $RuntimeExtDir -Force
|
|
127
|
+
Copy-Item "$PgVectorDir\sql\vector--*.sql" $RuntimeExtDir -Force
|
|
128
|
+
|
|
129
|
+
Write-Host "==> Asserting runtime layout"
|
|
130
|
+
$VectorControl = "$RuntimeDir\share\extension\vector.control"
|
|
131
|
+
if (-not (Test-Path $VectorControl)) { Write-Error "ASSERT FAILED: $VectorControl not found"; exit 1 }
|
|
132
|
+
$VectorSql = "$RuntimeDir\share\extension\vector--$PGVECTOR_VERSION.sql"
|
|
133
|
+
if (-not (Test-Path $VectorSql)) { Write-Error "ASSERT FAILED: $VectorSql not found"; exit 1 }
|
|
134
|
+
if (-not (Test-Path "$RuntimeDir\lib\vector.dll")) {
|
|
135
|
+
Write-Error "ASSERT FAILED: vector.dll not found in lib\"
|
|
136
|
+
exit 1
|
|
137
|
+
}
|
|
138
|
+
Write-Host " PASS runtime layout"
|
|
139
|
+
|
|
140
|
+
# Licenses
|
|
141
|
+
if (Test-Path "$PgRoot\doc\postgresql\COPYRIGHT") {
|
|
142
|
+
Copy-Item "$PgRoot\doc\postgresql\COPYRIGHT" "$RuntimeDir\LICENSE.postgresql" -Force
|
|
143
|
+
} elseif (Test-Path "$PgRoot\doc\COPYRIGHT") {
|
|
144
|
+
Copy-Item "$PgRoot\doc\COPYRIGHT" "$RuntimeDir\LICENSE.postgresql" -Force
|
|
145
|
+
}
|
|
146
|
+
if (Test-Path "$PgVectorDir\LICENSE") {
|
|
147
|
+
Copy-Item "$PgVectorDir\LICENSE" "$RuntimeDir\LICENSE.pgvector" -Force
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
Write-Host "==> Self-contained smoke test (initdb + CREATE EXTENSION vector + distance query)"
|
|
151
|
+
& "$RuntimeDir\bin\postgres.exe" --version
|
|
152
|
+
if ($LASTEXITCODE -ne 0) { Write-Error "FAIL: postgres.exe --version exit $LASTEXITCODE"; exit 1 }
|
|
153
|
+
|
|
154
|
+
$SmokeData = "$BuildDir\smoke-pgdata"
|
|
155
|
+
$SmokeLog = "$BuildDir\smoke-pg.log"
|
|
156
|
+
$SmokePort = 55899
|
|
157
|
+
if (Test-Path $SmokeData) { Remove-Item -Recurse -Force $SmokeData }
|
|
158
|
+
|
|
159
|
+
& "$RuntimeDir\bin\initdb.exe" -D $SmokeData --username=postgres --auth-local=trust --no-locale -E UTF8 | Out-Null
|
|
160
|
+
if ($LASTEXITCODE -ne 0) { Write-Error "FAIL: initdb"; exit 1 }
|
|
161
|
+
|
|
162
|
+
& "$RuntimeDir\bin\pg_ctl.exe" -D $SmokeData -o "-p $SmokePort -h 127.0.0.1" -l $SmokeLog -w start
|
|
163
|
+
if ($LASTEXITCODE -ne 0) { Write-Error "FAIL: pg_ctl start (see $SmokeLog)"; Get-Content $SmokeLog | Select-Object -Last 30; exit 1 }
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
& "$RuntimeDir\bin\psql.exe" -h 127.0.0.1 -p $SmokePort -U postgres -d postgres -c "CREATE EXTENSION vector;" | Out-Null
|
|
167
|
+
if ($LASTEXITCODE -ne 0) { throw "CREATE EXTENSION vector failed" }
|
|
168
|
+
$ExtV = & "$RuntimeDir\bin\psql.exe" -h 127.0.0.1 -p $SmokePort -U postgres -d postgres -tAc "SELECT extversion FROM pg_extension WHERE extname='vector';"
|
|
169
|
+
$Dist = & "$RuntimeDir\bin\psql.exe" -h 127.0.0.1 -p $SmokePort -U postgres -d postgres -tAc "SELECT '[1,2,3]'::vector <-> '[1,2,4]'::vector;"
|
|
170
|
+
Write-Host " vector extension version: $ExtV"
|
|
171
|
+
Write-Host " distance query result: $Dist"
|
|
172
|
+
if ($ExtV.Trim() -ne $PGVECTOR_VERSION) {
|
|
173
|
+
Write-Error "FAIL: extversion='$ExtV' expected='$PGVECTOR_VERSION'"
|
|
174
|
+
exit 1
|
|
175
|
+
}
|
|
176
|
+
Write-Host " PASS smoke (extension load + vector distance)"
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
& "$RuntimeDir\bin\pg_ctl.exe" -D $SmokeData -m fast stop 2>$null | Out-Null
|
|
180
|
+
Remove-Item -Recurse -Force $SmokeData -ErrorAction SilentlyContinue
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
Write-Host "==> Creating tarball: $OutputName"
|
|
184
|
+
$DistDirFwd = $DistDir.Replace('\', '/')
|
|
185
|
+
$RuntimeDirFwd = $RuntimeDir.Replace('\', '/')
|
|
186
|
+
& tar -czf "$DistDirFwd/$OutputName" -C "$RuntimeDirFwd" .
|
|
187
|
+
if ($LASTEXITCODE -ne 0) { Write-Error "tar failed (exit $LASTEXITCODE)"; exit 1 }
|
|
188
|
+
|
|
189
|
+
Write-Host "==> Generating sha256 sidecar"
|
|
190
|
+
Push-Location $DistDir
|
|
191
|
+
$Hash = (Get-FileHash -Algorithm SHA256 $OutputName).Hash.ToLower()
|
|
192
|
+
"$Hash $OutputName" | Out-File -Encoding ascii "${OutputName}.sha256"
|
|
193
|
+
Pop-Location
|
|
194
|
+
|
|
195
|
+
# ---------------------------------------------------------------------------
|
|
196
|
+
# Phase A: Re-smoke from EXTRACTED tarball with hostile env (cleared PATH,
|
|
197
|
+
# only system32). Catches false-pass where the build host has VC redist /
|
|
198
|
+
# preinstalled DLLs that the tarball might be missing.
|
|
199
|
+
# ---------------------------------------------------------------------------
|
|
200
|
+
Write-Host "==> Re-smoke from extracted tarball (hostile env)"
|
|
201
|
+
$ExtractDir = "$BuildDir\extract-smoke"
|
|
202
|
+
if (Test-Path $ExtractDir) { Remove-Item -Recurse -Force $ExtractDir }
|
|
203
|
+
New-Item -ItemType Directory -Force -Path $ExtractDir | Out-Null
|
|
204
|
+
& tar -xzf "$DistDirFwd/$OutputName" -C ($ExtractDir.Replace('\','/'))
|
|
205
|
+
if ($LASTEXITCODE -ne 0) { Write-Error "extract failed"; exit 1 }
|
|
206
|
+
|
|
207
|
+
$ExtractData = "$ExtractDir\extract-pgdata"
|
|
208
|
+
$ExtractLog = "$ExtractDir\extract-pg.log"
|
|
209
|
+
$ExtractPort = 55898
|
|
210
|
+
|
|
211
|
+
# Snapshot current env, then strip to minimal Windows PATH (no PG14/15/16
|
|
212
|
+
# preinstalled bin, no chocolatey, no MSVC tools).
|
|
213
|
+
$SavedPath = $env:PATH
|
|
214
|
+
$SavedPgRoot = $env:PGROOT
|
|
215
|
+
$env:PATH = "$env:SystemRoot\System32;$env:SystemRoot"
|
|
216
|
+
$env:PGROOT = $null
|
|
217
|
+
$env:PGDATA = $null
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
& "$ExtractDir\bin\postgres.exe" --version
|
|
221
|
+
if ($LASTEXITCODE -ne 0) { throw "FAIL: postgres --version under hostile env" }
|
|
222
|
+
& "$ExtractDir\bin\initdb.exe" -D $ExtractData --username=postgres --auth-local=trust --no-locale -E UTF8 | Out-Null
|
|
223
|
+
if ($LASTEXITCODE -ne 0) { throw "FAIL: initdb under hostile env" }
|
|
224
|
+
& "$ExtractDir\bin\pg_ctl.exe" -D $ExtractData -o "-p $ExtractPort -h 127.0.0.1" -l $ExtractLog -w start
|
|
225
|
+
if ($LASTEXITCODE -ne 0) { Get-Content $ExtractLog | Select-Object -Last 30; throw "FAIL: pg_ctl start under hostile env" }
|
|
226
|
+
try {
|
|
227
|
+
& "$ExtractDir\bin\psql.exe" -h 127.0.0.1 -p $ExtractPort -U postgres -d postgres -c "CREATE EXTENSION vector;" | Out-Null
|
|
228
|
+
if ($LASTEXITCODE -ne 0) { throw "FAIL: CREATE EXTENSION vector under hostile env" }
|
|
229
|
+
$ExtV2 = & "$ExtractDir\bin\psql.exe" -h 127.0.0.1 -p $ExtractPort -U postgres -d postgres -tAc "SELECT extversion FROM pg_extension WHERE extname='vector';"
|
|
230
|
+
if ($ExtV2.Trim() -ne $PGVECTOR_VERSION) { throw "FAIL: extracted-smoke extversion='$ExtV2'" }
|
|
231
|
+
Write-Host " PASS extracted-tarball smoke (hostile env)"
|
|
232
|
+
} finally {
|
|
233
|
+
& "$ExtractDir\bin\pg_ctl.exe" -D $ExtractData -m fast stop 2>$null | Out-Null
|
|
234
|
+
}
|
|
235
|
+
} finally {
|
|
236
|
+
$env:PATH = $SavedPath
|
|
237
|
+
$env:PGROOT = $SavedPgRoot
|
|
238
|
+
Remove-Item -Recurse -Force $ExtractDir -ErrorAction SilentlyContinue
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
Write-Host "==> Done: $DistDir\$OutputName"
|
|
242
|
+
Get-Item "$DistDir\$OutputName" | Select-Object Name, Length
|
package/scripts/build-tui.mjs
CHANGED
|
@@ -44,6 +44,12 @@ await build({
|
|
|
44
44
|
// Only `ink` is redirected to Mixdog's checked-in renderer instead of
|
|
45
45
|
// node_modules/ink.
|
|
46
46
|
packages: 'external',
|
|
47
|
+
// Bundled CJS helpers (e.g. src/lib/mixdog-debug.cjs) compile to esbuild's
|
|
48
|
+
// __require shim, which throws "Dynamic require of ..." in plain ESM.
|
|
49
|
+
// Provide a real module-scope require so those requires resolve at runtime.
|
|
50
|
+
banner: {
|
|
51
|
+
js: "import { createRequire as __mixdogCreateRequire } from 'node:module';\nconst require = __mixdogCreateRequire(import.meta.url);",
|
|
52
|
+
},
|
|
47
53
|
external: [
|
|
48
54
|
'../vendor/*',
|
|
49
55
|
'../../vendor/*',
|
|
@@ -142,6 +142,14 @@ console.log(JSON.stringify({ hookSpecificOutput: {
|
|
|
142
142
|
},
|
|
143
143
|
});
|
|
144
144
|
|
|
145
|
+
// Untrusted project: executable project hooks must be neutered (security gate).
|
|
146
|
+
const busUntrusted = createStandaloneHookBus({ dataDir: join(root, 'data-untrusted') });
|
|
147
|
+
const blocked = await busUntrusted.beforeTool({ sessionId: 'sess_test', cwd: root, name: 'shell', args: { command: 'echo ok' } });
|
|
148
|
+
assert.equal(blocked, null);
|
|
149
|
+
|
|
150
|
+
// Trusted project: user-level trustedProjects list re-enables project command hooks.
|
|
151
|
+
mkdirSync(join(root, 'data-mixdog'), { recursive: true });
|
|
152
|
+
writeJson(join(root, 'data-mixdog', 'config.json'), { trustedProjects: [root] });
|
|
145
153
|
const busWithMixdog = createStandaloneHookBus({ dataDir: join(root, 'data-mixdog') });
|
|
146
154
|
const denied = await busWithMixdog.beforeTool({ sessionId: 'sess_test', cwd: root, name: 'shell', args: { command: 'echo ok' } });
|
|
147
155
|
assert.equal(denied.action, 'deny');
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/log-writer-guard-smoke.mjs — unbounded `.log` writer regression gate.
|
|
4
|
+
*
|
|
5
|
+
* A new `.log` file name that lands in src/ without being covered by one of the
|
|
6
|
+
* bounded-writer contracts is an unbounded-growth footgun: the sibling-GC in
|
|
7
|
+
* mixdog-debug.cjs only prunes names it knows (canonical set + stale-sibling
|
|
8
|
+
* regexes), and the worker-boot rotation loop only rotates its explicit list.
|
|
9
|
+
* Any other steady `.log` append grows without limit.
|
|
10
|
+
*
|
|
11
|
+
* This scans src/ for string-literal `.log` file names (the targets that feed
|
|
12
|
+
* appendFile/appendFileSync/createWriteStream) and fails when a name is not:
|
|
13
|
+
* - in the canonical set (parsed live from mixdog-debug.cjs), or
|
|
14
|
+
* - in the worker-boot rotation list (parsed live from worker-bootstrap.mjs), or
|
|
15
|
+
* - matched by the dynamic / per-PID sibling patterns, or
|
|
16
|
+
* - in the explicit in-script allowlist below.
|
|
17
|
+
*
|
|
18
|
+
* Run: node scripts/log-writer-guard-smoke.mjs (or `npm run smoke:logguard`)
|
|
19
|
+
* Exit: 0 = every writer covered, 1 = uncovered `.log` name(s) found.
|
|
20
|
+
*/
|
|
21
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
22
|
+
import { dirname, join, relative } from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
|
+
|
|
25
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
26
|
+
const SRC = join(ROOT, 'src');
|
|
27
|
+
// `dist` = built TUI bundle (generated from src/tui/*); vendor is third-party.
|
|
28
|
+
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', 'vendor']);
|
|
29
|
+
const SCAN_EXT = /\.(mjs|cjs|js|jsx)$/;
|
|
30
|
+
|
|
31
|
+
// `.log` file-name targets. Two shapes, both bounded to real string content:
|
|
32
|
+
// STATIC — a quoted/backtick literal (`'boot.log'`, `"logs\\a.log"`). The
|
|
33
|
+
// `+` stem keeps us off the bare `.endsWith('.log')` guards.
|
|
34
|
+
// TEMPLATE — a `${...}` interpolation immediately followed by a static `.log`
|
|
35
|
+
// tail (`${jobId}.stdout.log`, `${root}.log`). The dynamic prefix
|
|
36
|
+
// is unverifiable, so the trailing literal (down to bare `.log`)
|
|
37
|
+
// must be covered/allowlisted or it fails.
|
|
38
|
+
// Separators (`/` and `\`) are captured; basename() reduces to the file name.
|
|
39
|
+
const STATIC_RE = /['"`]([\w.\-/\\]+\.log)\b/g;
|
|
40
|
+
const TEMPLATE_RE = /\$\{[^}]*\}([\w.\-/\\]*\.log)\b/g;
|
|
41
|
+
|
|
42
|
+
/** Last path segment, splitting on both POSIX and Windows separators. */
|
|
43
|
+
function basename(p) {
|
|
44
|
+
const parts = p.split(/[\\/]/);
|
|
45
|
+
return parts[parts.length - 1];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Parse a `new Set([...])` / array of quoted `.log` names out of a source file. */
|
|
49
|
+
function parseLogNames(file, anchor) {
|
|
50
|
+
let src;
|
|
51
|
+
try { src = readFileSync(file, 'utf8'); } catch { return []; }
|
|
52
|
+
const at = src.indexOf(anchor);
|
|
53
|
+
if (at === -1) return [];
|
|
54
|
+
const tail = src.slice(at, at + 2000);
|
|
55
|
+
const end = tail.indexOf(']');
|
|
56
|
+
const block = end === -1 ? tail : tail.slice(0, end);
|
|
57
|
+
return [...block.matchAll(/['"]([\w.\-]+\.log)['"]/g)].map((m) => m[1]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Covered names ────────────────────────────────────────────────────
|
|
61
|
+
// Parsed live so a name added to either contract auto-covers here.
|
|
62
|
+
const canonical = new Set(
|
|
63
|
+
parseLogNames(join(SRC, 'lib/mixdog-debug.cjs'), 'CANONICAL_PLUGIN_LOG_NAMES'),
|
|
64
|
+
);
|
|
65
|
+
const rotation = new Set(
|
|
66
|
+
parseLogNames(join(SRC, 'runtime/channels/lib/worker-bootstrap.mjs'), '_rotLog of ['),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Explicitly bounded / non-plugin-dir writers that live outside the sibling-GC
|
|
70
|
+
// contract by design (size-rotated in place, per-job spill, one-shot probes).
|
|
71
|
+
const ALLOWLIST = new Set([
|
|
72
|
+
'session-start-critical.log', // size-bounded via rotateBoundedLog()
|
|
73
|
+
'mixdog-ws-upgrade-probe.log', // one-shot WS upgrade probe dump
|
|
74
|
+
'.stdout.log', // shell-jobs per-jobId spill (`${jobId}.stdout.log`)
|
|
75
|
+
'.stderr.log', // shell-jobs per-jobId spill (`${jobId}.stderr.log`)
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
// Dynamic / per-PID sibling names (never literal, but keep the contract explicit).
|
|
79
|
+
const DYNAMIC_RE = [
|
|
80
|
+
/^(channels|memory)-worker\.\d+\.\d+\.log$/,
|
|
81
|
+
/-worker\.\d+\.\d+\.log$/,
|
|
82
|
+
/^mcp-debug\.\d+\.\d+\.log$/,
|
|
83
|
+
/^supervisor\.\d+\.log$/,
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
function isCovered(name) {
|
|
87
|
+
return (
|
|
88
|
+
canonical.has(name) ||
|
|
89
|
+
rotation.has(name) ||
|
|
90
|
+
ALLOWLIST.has(name) ||
|
|
91
|
+
DYNAMIC_RE.some((re) => re.test(name))
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function* walk(dir) {
|
|
96
|
+
let entries;
|
|
97
|
+
try { entries = readdirSync(dir); } catch { return; }
|
|
98
|
+
for (const name of entries) {
|
|
99
|
+
if (SKIP_DIRS.has(name)) continue;
|
|
100
|
+
const p = join(dir, name);
|
|
101
|
+
if (statSync(p).isDirectory()) yield* walk(p);
|
|
102
|
+
else if (SCAN_EXT.test(name)) yield p;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const offenders = [];
|
|
107
|
+
let scanned = 0;
|
|
108
|
+
for (const file of walk(SRC)) {
|
|
109
|
+
scanned++;
|
|
110
|
+
const lines = readFileSync(file, 'utf8').split('\n');
|
|
111
|
+
for (let i = 0; i < lines.length; i++) {
|
|
112
|
+
for (const re of [STATIC_RE, TEMPLATE_RE]) {
|
|
113
|
+
for (const m of lines[i].matchAll(re)) {
|
|
114
|
+
const name = basename(m[1]);
|
|
115
|
+
if (isCovered(name)) continue;
|
|
116
|
+
offenders.push(`${relative(ROOT, file)}:${i + 1} — uncovered \`.log\` writer target \`${name}\``);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (offenders.length > 0) {
|
|
123
|
+
console.error('log-writer-guard: FAIL — uncovered unbounded `.log` writer(s):');
|
|
124
|
+
for (const line of offenders) console.error(` ${line}`);
|
|
125
|
+
console.error(
|
|
126
|
+
`log-writer-guard: ${offenders.length} offender(s). Add the name to CANONICAL_PLUGIN_LOG_NAMES ` +
|
|
127
|
+
'(mixdog-debug.cjs), the worker-boot rotation list, or the ALLOWLIST in this script.',
|
|
128
|
+
);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
console.log(`log-writer-guard: ok — ${scanned} files scanned, every \`.log\` writer covered`);
|