@kontextmind/kxm 0.7.180 → 0.7.182

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.
@@ -290,6 +290,7 @@ export interface StudioServerOptions {
290
290
  planProvider?: (() => KxmCompiledPlan | undefined) | undefined;
291
291
  stateProvider?: (() => KxmRunState | undefined) | undefined;
292
292
  onMutation?: ((command: string, args: Record<string, unknown>) => Promise<{ ok: boolean; result?: unknown; error?: string }> | { ok: boolean; result?: unknown; error?: string }) | undefined;
293
+ controlUrl?: string | undefined;
293
294
  }
294
295
 
295
296
  export interface StudioServerHandle {
@@ -299,6 +300,15 @@ export interface StudioServerHandle {
299
300
  close: () => Promise<void>;
300
301
  }
301
302
 
303
+ function isKxmdAccountView(value: unknown): boolean {
304
+ if (typeof value !== "object" || value === null) return false;
305
+ const account = value as Record<string, unknown>;
306
+ return account.account === "kxmd"
307
+ && account.database === "kxmd"
308
+ && account.namespace === "kxmd"
309
+ && account.role === "kxmd";
310
+ }
311
+
302
312
  /**
303
313
  * Embedded Web Studio Local Server (Decision Q8 & D14).
304
314
  * Serves the visual DAG, form stepper, and Temporal swimlane dashboards on http://localhost:4242.
@@ -328,6 +338,46 @@ export function createStudioServer(options: StudioServerOptions = {}): StudioSer
328
338
  return;
329
339
  }
330
340
 
341
+ if (url.pathname === "/api/control/account" && req.method === "GET") {
342
+ const base = options.controlUrl ?? process.env.KXM_CONTROL_URL;
343
+ if (!base) {
344
+ res.writeHead(503, { "Content-Type": "application/json" });
345
+ res.end(JSON.stringify({ ok: false, error: "control_unconfigured" }));
346
+ return;
347
+ }
348
+ let upstream: Response;
349
+ try {
350
+ upstream = await fetch(new URL("/v1/account", base), {
351
+ signal: AbortSignal.timeout(3_000),
352
+ });
353
+ } catch {
354
+ res.writeHead(502, { "Content-Type": "application/json" });
355
+ res.end(JSON.stringify({ ok: false, error: "control_unreachable" }));
356
+ return;
357
+ }
358
+ const contentType = upstream.headers.get("content-type");
359
+ if (!contentType?.toLowerCase().startsWith("application/json")) {
360
+ res.writeHead(502, { "Content-Type": "application/json" });
361
+ res.end(JSON.stringify({ ok: false, error: "control_response_invalid" }));
362
+ return;
363
+ }
364
+ let body: unknown;
365
+ try {
366
+ body = await upstream.json();
367
+ } catch {
368
+ res.writeHead(502, { "Content-Type": "application/json" });
369
+ res.end(JSON.stringify({ ok: false, error: "control_response_invalid" }));
370
+ return;
371
+ }
372
+ if (upstream.ok && !isKxmdAccountView(body)) {
373
+ res.writeHead(502, { "Content-Type": "application/json" });
374
+ res.end(JSON.stringify({ ok: false, error: "control_response_invalid" }));
375
+ return;
376
+ }
377
+ res.writeHead(upstream.status, { "Content-Type": "application/json" });
378
+ res.end(JSON.stringify(body));
379
+ return;
380
+ }
331
381
  if (url.pathname === "/api/layout" && req.method === "GET") {
332
382
  const plan = options.planProvider?.();
333
383
  if (!plan) {
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env bash
2
+ # Start PostgreSQL 17 on 127.0.0.1:5432 for the Python MG1 lane.
3
+ # The kontextmind-doks scale set has a client binary and no engine socket, so
4
+ # this job provisions the server from a pinned archive instead of a workflow
5
+ # service. A second call in the same job returns once that server is ready.
6
+
7
+ set -euo pipefail
8
+
9
+ version=17.11.0
10
+ triple=x86_64-unknown-linux-gnu
11
+ sha256=b7a1ba6bae6499d8296e3e81b0171eecfd1766ca9aaa0057e41ad3e844e5e2e0
12
+ archive_name="postgresql-${version}-${triple}.tar.gz"
13
+ url="https://github.com/theseus-rs/postgresql-binaries/releases/download/${version}/${archive_name}"
14
+
15
+ if [[ "$(uname -s)" != "Linux" || "$(uname -m)" != "x86_64" ]]; then
16
+ echo "PostgreSQL 17 CI archive is linux/x86_64 only (got $(uname -s)/$(uname -m))" >&2
17
+ exit 1
18
+ fi
19
+
20
+ work="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/kxm-mg1-postgres"
21
+ prefix="${work}/postgresql-${version}-${triple}"
22
+ pgdata="${work}/data"
23
+ pglog="${work}/postgres.log"
24
+ pwfile="${work}/pwfile"
25
+ mkdir -p "$work"
26
+
27
+ package_for_library() {
28
+ case "$1" in
29
+ libzstd.so.*) echo libzstd1 ;;
30
+ liblz4.so.*) echo liblz4-1 ;;
31
+ libxml2.so.*) echo libxml2 ;;
32
+ liblzma.so.*) echo liblzma5 ;;
33
+ libssl.so.3 | libcrypto.so.3) echo libssl3 ;;
34
+ libicuuc.so.74 | libicui18n.so.74 | libicudata.so.74) echo libicu74 ;;
35
+ libgssapi_krb5.so.*) echo libgssapi-krb5-2 ;;
36
+ libkrb5.so.* | libk5crypto.so.* | libkrb5support.so.*) echo libkrb5-3 ;;
37
+ libcom_err.so.*) echo libcom-err2 ;;
38
+ libkeyutils.so.*) echo libkeyutils1 ;;
39
+ libstdc++.so.*) echo libstdc++6 ;;
40
+ libgcc_s.so.*) echo libgcc-s1 ;;
41
+ libz.so.*) echo zlib1g ;;
42
+ *) return 1 ;;
43
+ esac
44
+ }
45
+
46
+ server_version_num() {
47
+ PGPASSWORD=postgres "${prefix}/bin/psql" \
48
+ -h 127.0.0.1 -p 5432 -U postgres -d postgres \
49
+ -Atqc "SHOW server_version_num"
50
+ }
51
+
52
+ postgres_17_ready() {
53
+ [[ -x "${prefix}/bin/pg_isready" ]] || return 1
54
+ "${prefix}/bin/pg_isready" -h 127.0.0.1 -p 5432 -U postgres >/dev/null 2>&1 || return 1
55
+ local num
56
+ num="$(server_version_num 2>/dev/null)" || return 1
57
+ [[ "$num" -ge 170000 && "$num" -lt 180000 ]]
58
+ }
59
+
60
+ install_missing_libraries() {
61
+ local -a missing=()
62
+ local line
63
+ while IFS= read -r line; do
64
+ [[ -n "$line" ]] && missing+=("$line")
65
+ done < <(ldd "${prefix}/bin/postgres" | awk '/not found$/ { print $1 }')
66
+ if [[ ${#missing[@]} -eq 0 ]]; then
67
+ return 0
68
+ fi
69
+
70
+ local -a packages=()
71
+ local lib pkg
72
+ for lib in "${missing[@]}"; do
73
+ if ! pkg="$(package_for_library "$lib")"; then
74
+ echo "PostgreSQL 17 needs ${lib}, which has no package mapping on this runner" >&2
75
+ ldd "${prefix}/bin/postgres" >&2 || true
76
+ exit 1
77
+ fi
78
+ packages+=("$pkg")
79
+ done
80
+ mapfile -t packages < <(printf '%s\n' "${packages[@]}" | awk '!seen[$0]++')
81
+ echo "Installing shared libraries for PostgreSQL 17: ${packages[*]}"
82
+ sudo -n apt-get update
83
+ sudo -n DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${packages[@]}"
84
+
85
+ local -a still=()
86
+ while IFS= read -r line; do
87
+ [[ -n "$line" ]] && still+=("$line")
88
+ done < <(ldd "${prefix}/bin/postgres" | awk '/not found$/ { print $1 }')
89
+ if [[ ${#still[@]} -gt 0 ]]; then
90
+ echo "PostgreSQL 17 still needs: ${still[*]}" >&2
91
+ exit 1
92
+ fi
93
+ }
94
+
95
+ if postgres_17_ready; then
96
+ echo "PostgreSQL 17 already ready on 127.0.0.1:5432 (server_version_num=$(server_version_num))"
97
+ exit 0
98
+ fi
99
+
100
+ if [[ ! -x "${prefix}/bin/postgres" ]]; then
101
+ echo "Downloading PostgreSQL ${version}"
102
+ curl --fail --silent --show-error --location --retry 5 --retry-delay 2 \
103
+ --output "${work}/${archive_name}" \
104
+ "$url"
105
+ echo "${sha256} ${work}/${archive_name}" | sha256sum -c -
106
+ tar -xzf "${work}/${archive_name}" -C "$work"
107
+ fi
108
+ install_missing_libraries
109
+
110
+ if [[ ! -s "${pgdata}/PG_VERSION" ]]; then
111
+ printf '%s\n' postgres >"$pwfile"
112
+ chmod 600 "$pwfile"
113
+ "${prefix}/bin/initdb" \
114
+ --username=postgres \
115
+ --pwfile="$pwfile" \
116
+ --auth-host=scram-sha-256 \
117
+ --auth-local=trust \
118
+ --encoding=UTF8 \
119
+ --locale=C \
120
+ --set listen_addresses=127.0.0.1 \
121
+ --set port=5432 \
122
+ --set shared_buffers=16MB \
123
+ --set max_connections=20 \
124
+ --set shared_memory_type=mmap \
125
+ --set dynamic_shared_memory_type=mmap \
126
+ --set huge_pages=off \
127
+ --set fsync=off \
128
+ --set synchronous_commit=off \
129
+ --set full_page_writes=off \
130
+ --pgdata="$pgdata"
131
+ fi
132
+
133
+ if ! "${prefix}/bin/pg_ctl" -D "$pgdata" -l "$pglog" -w -t 30 start; then
134
+ echo "PostgreSQL 17 failed to start" >&2
135
+ cat "$pglog" >&2 || true
136
+ exit 1
137
+ fi
138
+
139
+ num="$(server_version_num)"
140
+ if [[ "$num" -lt 170000 || "$num" -ge 180000 ]]; then
141
+ echo "PostgreSQL 17 required, server_version_num=${num}" >&2
142
+ exit 1
143
+ fi
144
+ echo "PostgreSQL 17 ready on 127.0.0.1:5432 (server_version_num=${num})"