@compforge/harness-toolbox 0.3.1 → 0.3.3

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.
Files changed (41) hide show
  1. package/README.md +47 -0
  2. package/dist/{chunk-0cq1gx86.js → chunk-35cjec9z.js} +1 -1
  3. package/dist/chunk-5fgbhx2r.js +253 -0
  4. package/dist/{chunk-0rs1aacd.js → chunk-hmah9t8e.js} +1 -1
  5. package/dist/{chunk-jkpwa8tb.js → chunk-jx740hjh.js} +30 -8
  6. package/dist/{chunk-s06hs7a8.js → chunk-m91t44jh.js} +36 -5
  7. package/dist/{chunk-ztenxgjv.js → chunk-mn6dwsvh.js} +8 -1
  8. package/dist/{chunk-205a4vma.js → chunk-qhpqse1y.js} +3 -3
  9. package/dist/chunk-ssj93brx.js +89 -0
  10. package/dist/{chunk-4pdwsfcx.js → chunk-td11zjfb.js} +8 -8
  11. package/dist/{chunk-hv2kx47y.js → chunk-xh3zwhjh.js} +2 -2
  12. package/dist/kubernetes/client.js +8 -8
  13. package/dist/kubernetes/environment.js +8 -8
  14. package/dist/kubernetes/executor.d.ts +1 -1
  15. package/dist/kubernetes/executor.js +2 -2
  16. package/dist/kubernetes/pod-log-client.js +2 -2
  17. package/dist/kubernetes/pod-log-datasource.js +3 -3
  18. package/dist/kubernetes/port-forward.js +3 -3
  19. package/dist/kubernetes/resources.js +2 -2
  20. package/dist/kubernetes/service-port-forward.js +4 -4
  21. package/dist/mysql/client.d.ts +7 -1
  22. package/dist/mysql/client.js +8 -3
  23. package/dist/mysql/index.js +12 -7
  24. package/dist/mysql/pod.d.ts +10 -1
  25. package/dist/mysql/pod.js +4 -2
  26. package/dist/mysql/types.d.ts +4 -0
  27. package/dist/process/index.d.ts +1 -1
  28. package/dist/process/index.js +1 -1
  29. package/dist/redis/index.js +4 -4
  30. package/dist/s3/datasource.js +8 -3
  31. package/dist/s3/index.js +8 -3
  32. package/dist/transport/index.d.ts +1 -0
  33. package/dist/transport/index.js +9 -1
  34. package/dist/transport/pod-relay-script.d.ts +2 -0
  35. package/dist/transport/pod-relay-script.js +6 -0
  36. package/dist/transport/pod-relay.d.ts +31 -0
  37. package/dist/transport/pod-relay.js +10 -0
  38. package/package.json +4 -1
  39. package/dist/{chunk-mnad4zkp.js → chunk-010339ga.js} +3 -3
  40. package/dist/{chunk-1kpbvat9.js → chunk-2m0mh09h.js} +3 -3
  41. /package/dist/{chunk-xy4g56g4.js → chunk-h8fwx0k7.js} +0 -0
package/README.md CHANGED
@@ -156,3 +156,50 @@ is not an arbitrary-SQL sandbox. This API requires MySQL 5.7.8+ `max_execution_t
156
156
  fall back to Pod Python. Completion, truncation, timeout, and cancellation destroy the disposable
157
157
  session without replay or changes to ordinary borrowed sessions. Disconnect does not prove immediate
158
158
  server cancellation. The byte budget limits retained JSON rows, not individual wire packets or fields.
159
+
160
+ ## Shared Pod relay transport
161
+
162
+ `PodRelayTransport` opens TCP routes through an existing Running/Ready Pod. It discovers an
163
+ eligible container in the selected namespace (optionally restricted by a label selector); the
164
+ current implementation uses Python 3.8+ standard-library asyncio. Callers supply endpoints,
165
+ not a Pod name or interpreter. It creates no Pods and installs no packages. The selected Pod
166
+ must be able to resolve and reach each destination.
167
+
168
+ Create one transport in the root execution's ClientManager and lend it to all Service/protocol
169
+ clients. Its key describes cluster access and relay policy, never a Service or database identity.
170
+ Concurrent callers share Pod discovery, one relay process and one forward per host/port. Database
171
+ credentials and connections remain owned by their protocol clients.
172
+
173
+ ```ts
174
+ import { ClientManager } from '@compforge/harness-toolbox';
175
+ import { PodRelayTransport } from '@compforge/harness-toolbox/transport';
176
+
177
+ const clients = new ClientManager();
178
+ try {
179
+ const relay = await clients.get({
180
+ clientKey: 'transport:customer-cluster:app',
181
+ createClient: (_clients, signal) => new PodRelayTransport({
182
+ kubeconfig: '/config/cluster', context: 'customer', namespace: 'app',
183
+ selector: 'diagnostic-relay=eligible',
184
+ startupTimeoutMs: 5000, connectTimeoutMs: 5000,
185
+ maxConnections: 16, maxTargets: 8, maxCandidatePods: 8, signal,
186
+ }),
187
+ });
188
+ // The same instance belongs in each protocol client's ConnectionSource.transports.
189
+ const address = await relay.connect({host: 'database.internal', port: 3306});
190
+ // Connect the native protocol client to address; do not dispose relay in a Service.
191
+ } finally {
192
+ await clients.dispose();
193
+ }
194
+ ```
195
+
196
+ The host must authorize `list pods`, `create pods/exec` and `create pods/portforward`, including
197
+ running a temporary process in the selected container. List/discovery and each readiness operation
198
+ use startupTimeoutMs; candidate Pod count, target count and aggregate live relay connections are
199
+ bounded separately. List pods is namespace scoped. Relay listeners bind only to Pod loopback and
200
+ local forwards only to host loopback. Control messages contain destinations, never DB credentials.
201
+
202
+ Disposal closes forwards and the exec stream; EOF closes remote listeners/connections. If an exec
203
+ connection is lost without EOF, a 30-second heartbeat lease bounds remote process lifetime.
204
+ A dead relay invalidates its routes and fails callers; no SQL or other protocol operation is replayed.
205
+ The root should finish or cancel protocol work before disposing the transport.
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-zp1s833x.js";
7
7
  import {
8
8
  DirectTransport
9
- } from "./chunk-xy4g56g4.js";
9
+ } from "./chunk-h8fwx0k7.js";
10
10
 
11
11
  // src/s3/datasource.ts
12
12
  class S3DataSource {
@@ -0,0 +1,253 @@
1
+ import {
2
+ startPortForward
3
+ } from "./chunk-xh3zwhjh.js";
4
+ import {
5
+ KubectlExecutor
6
+ } from "./chunk-hmah9t8e.js";
7
+ import {
8
+ POD_RELAY_SCRIPT
9
+ } from "./chunk-ssj93brx.js";
10
+
11
+ // src/transport/pod-relay.ts
12
+ class PodRelayTransport {
13
+ options;
14
+ kind = "tcp";
15
+ name = "pod-relay";
16
+ #controller = new AbortController;
17
+ #signal;
18
+ #executor;
19
+ #startForward;
20
+ #targets = new Map;
21
+ #pending = new Map;
22
+ #forwards = [];
23
+ #startup;
24
+ #execution;
25
+ #input;
26
+ #heartbeat;
27
+ #failure;
28
+ #disposal;
29
+ #sequence = 0;
30
+ constructor(options, dependencies) {
31
+ this.options = options;
32
+ for (const key of ["startupTimeoutMs", "connectTimeoutMs", "maxConnections", "maxTargets", "maxCandidatePods"]) {
33
+ if (!Number.isSafeInteger(options[key]) || options[key] <= 0)
34
+ throw new Error(`${key} must be a positive integer`);
35
+ }
36
+ this.#executor = dependencies?.executor ?? new KubectlExecutor(options);
37
+ this.#startForward = dependencies?.startForward ?? startPortForward;
38
+ this.#signal = options.signal ? AbortSignal.any([options.signal, this.#controller.signal]) : this.#controller.signal;
39
+ this.#signal.addEventListener("abort", this.#onAbort, { once: true });
40
+ }
41
+ async initialize() {
42
+ this.#check();
43
+ }
44
+ #check() {
45
+ this.#signal.throwIfAborted();
46
+ if (this.#failure)
47
+ throw this.#failure;
48
+ }
49
+ #onAbort = () => {
50
+ this.#fail(new Error("Pod relay aborted"));
51
+ };
52
+ #fail(error) {
53
+ this.#failure ??= error;
54
+ if (this.#heartbeat)
55
+ clearInterval(this.#heartbeat);
56
+ if (this.#input) {
57
+ try {
58
+ this.#input.close();
59
+ } catch {}
60
+ }
61
+ this.#input = undefined;
62
+ for (const pending of this.#pending.values())
63
+ pending.reject(this.#failure);
64
+ this.#pending.clear();
65
+ for (const forward of this.#forwards.splice(0))
66
+ forward.stop();
67
+ }
68
+ async#select() {
69
+ const command = ["get", "pods", "--field-selector=status.phase=Running", "-o", "json"];
70
+ if (this.options.selector)
71
+ command.push("-l", this.options.selector);
72
+ const listed = await this.#executor.run(command, { timeoutMs: this.options.startupTimeoutMs, signal: this.#signal });
73
+ if (!listed.ok)
74
+ throw new Error("Cannot discover an existing Pod for relay");
75
+ const pods = JSON.parse(listed.stdout).items.filter((pod) => pod.status?.phase === "Running" && !pod.metadata?.deletionTimestamp).sort((a, b) => (a.metadata?.name ?? "").localeCompare(b.metadata?.name ?? "")).slice(0, this.options.maxCandidatePods);
76
+ for (const pod of pods) {
77
+ if (!pod.metadata?.name)
78
+ continue;
79
+ for (const container of pod.status?.containerStatuses ?? []) {
80
+ if (!container.ready)
81
+ continue;
82
+ const target = { pod: pod.metadata.name, container: container.name };
83
+ for (const executable of ["python3", "python"]) {
84
+ this.#check();
85
+ const probe = await this.#executor.exec(target, [executable, "-c", "import asyncio,sys; assert sys.version_info >= (3, 8); print('relay-ready')"], { timeoutMs: this.options.startupTimeoutMs, signal: this.#signal });
86
+ if (probe.ok && probe.stdout.trim() === "relay-ready")
87
+ return { target, executable };
88
+ }
89
+ }
90
+ }
91
+ throw new Error("No eligible existing Pod can run the TCP relay");
92
+ }
93
+ async#start() {
94
+ const pod = await this.#select();
95
+ this.#check();
96
+ const input = new ReadableStream({ start: (controller) => {
97
+ this.#input = controller;
98
+ } });
99
+ let ready;
100
+ let rejectReady;
101
+ const readiness = new Promise((resolve, reject) => {
102
+ ready = resolve;
103
+ rejectReady = reject;
104
+ });
105
+ let buffered = "";
106
+ const config = {
107
+ connectTimeoutMs: this.options.connectTimeoutMs,
108
+ maxConnections: this.options.maxConnections,
109
+ maxTargets: this.options.maxTargets,
110
+ leaseMs: 30000
111
+ };
112
+ this.#execution = this.#executor.exec(pod.target, [pod.executable, "-u", "-c", POD_RELAY_SCRIPT, JSON.stringify(config)], {
113
+ stdin: input,
114
+ signal: this.#signal,
115
+ timeoutMs: 2147483647,
116
+ collectStdout: false,
117
+ onStdout: (chunk) => {
118
+ buffered += chunk;
119
+ if (buffered.length > 65536) {
120
+ this.#fail(new Error("Pod relay control response exceeded limit"));
121
+ return;
122
+ }
123
+ while (buffered.includes(`
124
+ `)) {
125
+ const end = buffered.indexOf(`
126
+ `);
127
+ const line = buffered.slice(0, end);
128
+ buffered = buffered.slice(end + 1);
129
+ try {
130
+ const response = JSON.parse(line);
131
+ if (response.ready)
132
+ ready();
133
+ else if (response.id !== undefined) {
134
+ const pending = this.#pending.get(response.id);
135
+ this.#pending.delete(response.id);
136
+ if (Number.isInteger(response.port) && response.port > 0 && response.port <= 65535)
137
+ pending?.resolve(response.port);
138
+ else
139
+ pending?.reject(new Error("Pod relay listener unavailable"));
140
+ }
141
+ } catch {
142
+ this.#fail(new Error("Invalid Pod relay control response"));
143
+ }
144
+ }
145
+ }
146
+ }).then(() => {
147
+ const error = new Error("Pod relay execution ended");
148
+ rejectReady(error);
149
+ this.#fail(error);
150
+ }, () => {
151
+ const error = new Error("Pod relay execution failed");
152
+ rejectReady(error);
153
+ this.#fail(error);
154
+ });
155
+ this.#heartbeat = setInterval(() => {
156
+ try {
157
+ this.#send({ heartbeat: true });
158
+ } catch {
159
+ this.#fail(new Error("Pod relay control channel closed"));
160
+ }
161
+ }, 5000);
162
+ const timer = setTimeout(() => rejectReady(new Error("Pod relay startup timed out")), this.options.startupTimeoutMs);
163
+ try {
164
+ await readiness;
165
+ this.#check();
166
+ return pod;
167
+ } finally {
168
+ clearTimeout(timer);
169
+ }
170
+ }
171
+ #send(value) {
172
+ this.#check();
173
+ if (!this.#input)
174
+ throw new Error("Pod relay control channel unavailable");
175
+ this.#input.enqueue(new TextEncoder().encode(`${JSON.stringify(value)}
176
+ `));
177
+ }
178
+ connect(endpoint) {
179
+ this.#check();
180
+ if (!endpoint.host || !Number.isInteger(endpoint.port) || endpoint.port < 1 || endpoint.port > 65535) {
181
+ return Promise.reject(new Error("Invalid relay endpoint"));
182
+ }
183
+ const key = JSON.stringify([endpoint.host, endpoint.port]);
184
+ let pending = this.#targets.get(key);
185
+ if (!pending) {
186
+ if (this.#targets.size >= this.options.maxTargets)
187
+ return Promise.reject(new Error("Pod relay target limit reached"));
188
+ this.#startup ??= this.#start().catch((error) => {
189
+ this.#fail(error instanceof Error ? error : new Error("Pod relay startup failed"));
190
+ throw error;
191
+ });
192
+ pending = this.#connect(endpoint, this.#startup);
193
+ this.#targets.set(key, pending);
194
+ }
195
+ return pending.then((local) => ({ ...local, servername: endpoint.servername ?? endpoint.host }));
196
+ }
197
+ async#connect(endpoint, startup) {
198
+ const pod = await startup;
199
+ this.#check();
200
+ const id = ++this.#sequence;
201
+ const remotePort = await new Promise((resolve, reject) => {
202
+ const timer = setTimeout(() => {
203
+ this.#pending.delete(id);
204
+ reject(new Error("Pod relay listener timed out"));
205
+ }, this.options.startupTimeoutMs);
206
+ this.#pending.set(id, {
207
+ resolve: (value) => {
208
+ clearTimeout(timer);
209
+ resolve(value);
210
+ },
211
+ reject: (error) => {
212
+ clearTimeout(timer);
213
+ reject(error);
214
+ }
215
+ });
216
+ try {
217
+ this.#send({ id, host: endpoint.host, port: endpoint.port });
218
+ } catch (error) {
219
+ this.#pending.get(id)?.reject(error);
220
+ this.#pending.delete(id);
221
+ }
222
+ });
223
+ this.#check();
224
+ const forwarded = await this.#startForward({
225
+ ...this.options,
226
+ target: { kind: "pod", name: pod.target.pod },
227
+ remotePort,
228
+ timeoutMs: this.options.startupTimeoutMs
229
+ });
230
+ if (!forwarded.ok || !forwarded.value)
231
+ throw new Error("Pod relay port-forward unavailable");
232
+ try {
233
+ this.#check();
234
+ } catch (error) {
235
+ forwarded.value.stop();
236
+ throw error;
237
+ }
238
+ this.#forwards.push(forwarded.value);
239
+ return { host: "127.0.0.1", port: forwarded.value.localPort };
240
+ }
241
+ dispose() {
242
+ return this.#disposal ??= (async () => {
243
+ this.#controller.abort(new Error("Pod relay disposed"));
244
+ this.#fail(new Error("Pod relay disposed"));
245
+ await Promise.allSettled(this.#targets.values());
246
+ await this.#execution;
247
+ this.#signal.removeEventListener("abort", this.#onAbort);
248
+ this.#targets.clear();
249
+ })();
250
+ }
251
+ }
252
+
253
+ export { PodRelayTransport };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  spawnProcess
3
- } from "./chunk-ztenxgjv.js";
3
+ } from "./chunk-mn6dwsvh.js";
4
4
 
5
5
  // src/kubernetes/executor.ts
6
6
  function buildKubectlArgs(opts, sub) {
@@ -3,14 +3,15 @@ import {
3
3
  validateQueryLimits
4
4
  } from "./chunk-kbewz29a.js";
5
5
  import {
6
- queryMysqlViaPod
7
- } from "./chunk-s06hs7a8.js";
6
+ queryMysqlViaPod,
7
+ queryMysqlViaPodBatch
8
+ } from "./chunk-m91t44jh.js";
8
9
  import {
9
10
  ConcurrencyPool
10
11
  } from "./chunk-v3hxvnrc.js";
11
12
  import {
12
13
  isConnectionNetworkError
13
- } from "./chunk-xy4g56g4.js";
14
+ } from "./chunk-h8fwx0k7.js";
14
15
 
15
16
  // src/mysql/client.ts
16
17
  import { createConnection } from "mysql2/promise";
@@ -28,6 +29,22 @@ class MysqlDatabase {
28
29
  query(target, sql, values) {
29
30
  return this.#queries.run(() => this.#query(target, sql, values), this.options.signal);
30
31
  }
32
+ queryBatch(target, statements) {
33
+ return this.#queries.run(async () => {
34
+ if (!statements.length)
35
+ return [];
36
+ return this.#withSession(target, async (session) => {
37
+ if (session.kind === "python")
38
+ return queryMysqlViaPodBatch(session.transport, target, statements, this.options);
39
+ const results = [];
40
+ for (const { sql, values } of statements) {
41
+ const [rows] = await session.connection.execute({ sql, values: [...values], timeout: this.options.queryTimeoutMs });
42
+ results.push(rows);
43
+ }
44
+ return results;
45
+ });
46
+ }, this.options.signal);
47
+ }
31
48
  queryReadonly(target, sql, values, limits) {
32
49
  validateQueryLimits(limits);
33
50
  return this.#queries.run(async () => {
@@ -60,7 +77,7 @@ class MysqlDatabase {
60
77
  }
61
78
  return pending;
62
79
  }
63
- async#query(target, sql, values) {
80
+ async#withSession(target, run) {
64
81
  this.options.signal?.throwIfAborted();
65
82
  const key = [target.host, target.port, target.database, target.user, target.password].join("\x00");
66
83
  const pending = this.#session(target);
@@ -68,10 +85,7 @@ class MysqlDatabase {
68
85
  try {
69
86
  session = await pending;
70
87
  this.options.signal?.throwIfAborted();
71
- if (session.kind === "python")
72
- return await queryMysqlViaPod(session.transport, target, sql, values, this.options);
73
- const [rows] = await session.connection.execute({ sql, values: [...values], timeout: this.options.queryTimeoutMs });
74
- return rows;
88
+ return await run(session);
75
89
  } catch (error) {
76
90
  if (this.#connections.get(key) === pending)
77
91
  this.#connections.delete(key);
@@ -80,6 +94,14 @@ class MysqlDatabase {
80
94
  throw error;
81
95
  }
82
96
  }
97
+ async#query(target, sql, values) {
98
+ return this.#withSession(target, async (session) => {
99
+ if (session.kind === "python")
100
+ return queryMysqlViaPod(session.transport, target, sql, values, this.options);
101
+ const [rows] = await session.connection.execute({ sql, values: [...values], timeout: this.options.queryTimeoutMs });
102
+ return rows;
103
+ });
104
+ }
83
105
  async#open(target) {
84
106
  let reason;
85
107
  for (let i = 0;i < this.transports.length; i++) {
@@ -50,9 +50,20 @@ try:
50
50
  )
51
51
  try:
52
52
  with connection.cursor(pymysql.cursors.DictCursor) as cursor:
53
- cursor.execute(request["sql"], request["values"])
54
- rows = cursor.fetchall()
55
- print(json.dumps({"rows": rows}, default=encode, ensure_ascii=False))
53
+ statements = request.get("statements")
54
+ if statements is None:
55
+ cursor.execute(request["sql"], request["values"])
56
+ rows = cursor.fetchall()
57
+ print(json.dumps({"rows": rows}, default=encode, ensure_ascii=False))
58
+ else:
59
+ # Batch: one process and one connection for independent statements;
60
+ # the per-statement deadline is re-armed before each execute.
61
+ results = []
62
+ for statement in statements:
63
+ signal.alarm(math.ceil(request["queryTimeoutMs"] / 1000))
64
+ cursor.execute(statement["sql"], statement["values"])
65
+ results.append({"rows": cursor.fetchall()})
66
+ print(json.dumps({"results": results}, default=encode, ensure_ascii=False))
56
67
  finally:
57
68
  connection.close()
58
69
  except Exception as error:
@@ -62,6 +73,9 @@ except Exception as error:
62
73
  finally:
63
74
  signal.alarm(0)
64
75
  `;
76
+ function parsePodResponse(raw) {
77
+ return JSON.parse(raw, (_key, value) => value?.type === "Buffer" && Array.isArray(value.data) ? Buffer.from(value.data) : value);
78
+ }
65
79
  async function queryMysqlViaPod(transport, target, sql, values, options) {
66
80
  const raw = await transport.run(POD_QUERY, {
67
81
  target,
@@ -69,11 +83,28 @@ async function queryMysqlViaPod(transport, target, sql, values, options) {
69
83
  values,
70
84
  ...options
71
85
  }, options.queryTimeoutMs + 3000);
72
- const response = JSON.parse(raw, (_key, value) => value?.type === "Buffer" && Array.isArray(value.data) ? Buffer.from(value.data) : value);
86
+ const response = parsePodResponse(raw);
73
87
  if (response.error || !Array.isArray(response.rows)) {
74
88
  throw new Error(`MySQL via Pod failed: ${response.error ?? "invalid response"}${response.code ? ` (${response.code})` : ""}`);
75
89
  }
76
90
  return response.rows;
77
91
  }
92
+ async function queryMysqlViaPodBatch(transport, target, statements, options) {
93
+ const raw = await transport.run(POD_QUERY, {
94
+ target,
95
+ statements: statements.map(({ sql, values }) => ({ sql: preparePodSql(sql, values), values })),
96
+ ...options
97
+ }, options.connectTimeoutMs + options.queryTimeoutMs * statements.length + 3000);
98
+ const response = parsePodResponse(raw);
99
+ if (response.error || !Array.isArray(response.results)) {
100
+ throw new Error(`MySQL via Pod batch failed: ${response.error ?? "invalid response"}${response.code ? ` (${response.code})` : ""}`);
101
+ }
102
+ return response.results.map((result, index) => {
103
+ if (!Array.isArray(result.rows)) {
104
+ throw new Error(`MySQL via Pod batch statement ${index} returned no rows`);
105
+ }
106
+ return result.rows;
107
+ });
108
+ }
78
109
 
79
- export { preparePodSql, queryMysqlViaPod };
110
+ export { preparePodSql, queryMysqlViaPod, queryMysqlViaPodBatch };
@@ -8,7 +8,14 @@ function spawnProcess(argv, opts) {
8
8
  stdio: "pipe",
9
9
  env: opts?.env
10
10
  });
11
- child.stdin.end(opts?.stdin);
11
+ if (opts?.stdin instanceof ReadableStream) {
12
+ const input = Readable.fromWeb(opts.stdin);
13
+ input.on("error", () => child.stdin.destroy());
14
+ child.stdin.on("error", () => input.destroy());
15
+ child.once("close", () => input.destroy());
16
+ input.pipe(child.stdin);
17
+ } else
18
+ child.stdin.end(opts?.stdin);
12
19
  const exited = new Promise((resolve, reject) => {
13
20
  child.once("error", reject);
14
21
  child.once("close", (code) => resolve(code ?? 1));
@@ -1,6 +1,3 @@
1
- import {
2
- PortForwardScope
3
- } from "./chunk-hv2kx47y.js";
4
1
  import {
5
2
  findPodsForService,
6
3
  findService,
@@ -9,6 +6,9 @@ import {
9
6
  import {
10
7
  findPod
11
8
  } from "./chunk-6fnhzhdb.js";
9
+ import {
10
+ PortForwardScope
11
+ } from "./chunk-xh3zwhjh.js";
12
12
 
13
13
  // src/kubernetes/service-port-forward.ts
14
14
  class ServicePortForwarder {
@@ -0,0 +1,89 @@
1
+ // src/transport/pod-relay-script.ts
2
+ var POD_RELAY_SCRIPT = String.raw`
3
+ import asyncio, json, sys
4
+
5
+ async def main():
6
+ config = json.loads(sys.argv[1])
7
+ loop = asyncio.get_running_loop()
8
+ reader = asyncio.StreamReader(limit=4096)
9
+ await loop.connect_read_pipe(lambda: asyncio.StreamReaderProtocol(reader), sys.stdin.buffer)
10
+ servers = []
11
+ active = set()
12
+
13
+ async def pipe(source, destination):
14
+ while True:
15
+ chunk = await source.read(65536)
16
+ if not chunk:
17
+ if destination.can_write_eof():
18
+ destination.write_eof()
19
+ return
20
+ destination.write(chunk)
21
+ await destination.drain()
22
+
23
+ async def relay(source, destination, host, port):
24
+ task = asyncio.current_task()
25
+ if len(active) >= config["maxConnections"]:
26
+ destination.close()
27
+ await destination.wait_closed()
28
+ return
29
+ active.add(task)
30
+ upstream = None
31
+ pumps = []
32
+ try:
33
+ remote, upstream = await asyncio.wait_for(
34
+ asyncio.open_connection(host, port), config["connectTimeoutMs"] / 1000)
35
+ pumps = [asyncio.create_task(pipe(source, upstream)), asyncio.create_task(pipe(remote, destination))]
36
+ await asyncio.gather(*pumps)
37
+ except (OSError, asyncio.TimeoutError):
38
+ pass
39
+ finally:
40
+ for pump in pumps:
41
+ pump.cancel()
42
+ await asyncio.gather(*pumps, return_exceptions=True)
43
+ for writer in (upstream, destination):
44
+ if writer:
45
+ writer.close()
46
+ for writer in (upstream, destination):
47
+ if writer:
48
+ try:
49
+ await writer.wait_closed()
50
+ except OSError:
51
+ pass
52
+ active.discard(task)
53
+
54
+ print(json.dumps({"ready": True}), flush=True)
55
+ try:
56
+ while True:
57
+ # EOF cleans up normal shutdown; lease expiry bounds an orphan after lost exec transport.
58
+ line = await asyncio.wait_for(reader.readline(), config["leaseMs"] / 1000)
59
+ if not line:
60
+ break
61
+ request = json.loads(line)
62
+ if request.get("heartbeat"):
63
+ continue
64
+ identifier = request["id"]
65
+ try:
66
+ if len(servers) >= config["maxTargets"]:
67
+ raise ValueError("target limit")
68
+ host, port = request["host"], request["port"]
69
+ server = await asyncio.start_server(
70
+ lambda r, w, h=host, p=port: relay(r, w, h, p), "127.0.0.1", 0)
71
+ servers.append(server)
72
+ print(json.dumps({"id": identifier, "port": server.sockets[0].getsockname()[1]}), flush=True)
73
+ except (OSError, ValueError):
74
+ print(json.dumps({"id": identifier, "error": "relay listener unavailable"}), flush=True)
75
+ except asyncio.TimeoutError:
76
+ pass
77
+ finally:
78
+ for server in servers:
79
+ server.close()
80
+ await asyncio.gather(*(server.wait_closed() for server in servers))
81
+ tasks = list(active)
82
+ for task in tasks:
83
+ task.cancel()
84
+ await asyncio.gather(*tasks, return_exceptions=True)
85
+
86
+ asyncio.run(main())
87
+ `;
88
+
89
+ export { POD_RELAY_SCRIPT };
@@ -1,18 +1,18 @@
1
1
  import {
2
- ServicePortForwarder
3
- } from "./chunk-205a4vma.js";
2
+ resolveWorkload
3
+ } from "./chunk-y3dx8jh6.js";
4
4
  import {
5
- KubectlExecutor
6
- } from "./chunk-0rs1aacd.js";
5
+ ServicePortForwarder
6
+ } from "./chunk-qhpqse1y.js";
7
7
  import {
8
8
  resourceAccess
9
- } from "./chunk-1kpbvat9.js";
10
- import {
11
- resolveWorkload
12
- } from "./chunk-y3dx8jh6.js";
9
+ } from "./chunk-2m0mh09h.js";
13
10
  import {
14
11
  KubernetesError
15
12
  } from "./chunk-ntfm5e85.js";
13
+ import {
14
+ KubectlExecutor
15
+ } from "./chunk-hmah9t8e.js";
16
16
 
17
17
  // src/kubernetes/client.ts
18
18
  class KubernetesClient {
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  buildKubectlArgs
3
- } from "./chunk-0rs1aacd.js";
3
+ } from "./chunk-hmah9t8e.js";
4
4
  import {
5
5
  sleep,
6
6
  spawnProcess
7
- } from "./chunk-ztenxgjv.js";
7
+ } from "./chunk-mn6dwsvh.js";
8
8
 
9
9
  // src/kubernetes/port-forward.ts
10
10
  async function startPortForward(opts) {
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  KubernetesClient
3
- } from "../chunk-4pdwsfcx.js";
4
- import"../chunk-205a4vma.js";
5
- import"../chunk-hv2kx47y.js";
3
+ } from "../chunk-td11zjfb.js";
4
+ import"../chunk-y3dx8jh6.js";
5
+ import"../chunk-qhpqse1y.js";
6
6
  import"../chunk-j46x7j7c.js";
7
7
  import"../chunk-6fnhzhdb.js";
8
- import"../chunk-0rs1aacd.js";
9
- import"../chunk-ztenxgjv.js";
10
- import"../chunk-1kpbvat9.js";
11
- import"../chunk-y3dx8jh6.js";
12
- import"../chunk-v3hxvnrc.js";
8
+ import"../chunk-2m0mh09h.js";
13
9
  import"../chunk-ntfm5e85.js";
10
+ import"../chunk-v3hxvnrc.js";
11
+ import"../chunk-xh3zwhjh.js";
12
+ import"../chunk-hmah9t8e.js";
13
+ import"../chunk-mn6dwsvh.js";
14
14
  export {
15
15
  KubernetesClient
16
16
  };
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  KubernetesClient
3
- } from "../chunk-4pdwsfcx.js";
4
- import"../chunk-205a4vma.js";
5
- import"../chunk-hv2kx47y.js";
3
+ } from "../chunk-td11zjfb.js";
4
+ import"../chunk-y3dx8jh6.js";
5
+ import"../chunk-qhpqse1y.js";
6
6
  import"../chunk-j46x7j7c.js";
7
7
  import"../chunk-6fnhzhdb.js";
8
- import"../chunk-0rs1aacd.js";
9
- import"../chunk-ztenxgjv.js";
10
- import"../chunk-1kpbvat9.js";
11
- import"../chunk-y3dx8jh6.js";
8
+ import"../chunk-2m0mh09h.js";
12
9
  import {
13
10
  clientKey
14
11
  } from "../chunk-a9htb6cr.js";
15
- import"../chunk-v3hxvnrc.js";
16
12
  import"../chunk-ntfm5e85.js";
13
+ import"../chunk-v3hxvnrc.js";
14
+ import"../chunk-xh3zwhjh.js";
15
+ import"../chunk-hmah9t8e.js";
16
+ import"../chunk-mn6dwsvh.js";
17
17
 
18
18
  // src/kubernetes/environment.ts
19
19
  class KubernetesEnvironment {
@@ -9,7 +9,7 @@ export interface ExecTarget {
9
9
  container?: string;
10
10
  }
11
11
  export interface RunOptions {
12
- stdin?: string | Uint8Array;
12
+ stdin?: string | Uint8Array | ReadableStream<Uint8Array>;
13
13
  timeoutMs?: number;
14
14
  signal?: AbortSignal;
15
15
  onStdout?: (chunk: string) => void;
@@ -3,8 +3,8 @@ import {
3
3
  buildExecArgs,
4
4
  buildKubectlArgs,
5
5
  runArgv
6
- } from "../chunk-0rs1aacd.js";
7
- import"../chunk-ztenxgjv.js";
6
+ } from "../chunk-hmah9t8e.js";
7
+ import"../chunk-mn6dwsvh.js";
8
8
  export {
9
9
  runArgv,
10
10
  buildKubectlArgs,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  PodLogClient
3
- } from "../chunk-mnad4zkp.js";
3
+ } from "../chunk-010339ga.js";
4
4
  import"../chunk-8nm9f0hh.js";
5
- import"../chunk-mp88s7r7.js";
6
5
  import"../chunk-1vfwvgkj.js";
6
+ import"../chunk-mp88s7r7.js";
7
7
  import"../chunk-a9htb6cr.js";
8
8
  import"../chunk-v3hxvnrc.js";
9
9
  export {
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  PodLogClient
3
- } from "../chunk-mnad4zkp.js";
3
+ } from "../chunk-010339ga.js";
4
4
  import"../chunk-8nm9f0hh.js";
5
- import"../chunk-mp88s7r7.js";
6
5
  import"../chunk-1vfwvgkj.js";
6
+ import"../chunk-mp88s7r7.js";
7
7
  import"../chunk-a9htb6cr.js";
8
8
  import {
9
9
  clientKey
10
10
  } from "../chunk-z5pm2emz.js";
11
- import"../chunk-v3hxvnrc.js";
12
11
  import"../chunk-ntfm5e85.js";
12
+ import"../chunk-v3hxvnrc.js";
13
13
 
14
14
  // src/kubernetes/pod-log-datasource.ts
15
15
  class PodLogDataSource {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  PortForwardScope,
3
3
  startPortForward
4
- } from "../chunk-hv2kx47y.js";
5
- import"../chunk-0rs1aacd.js";
6
- import"../chunk-ztenxgjv.js";
4
+ } from "../chunk-xh3zwhjh.js";
5
+ import"../chunk-hmah9t8e.js";
6
+ import"../chunk-mn6dwsvh.js";
7
7
  export {
8
8
  startPortForward,
9
9
  PortForwardScope
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  resourceAccess
3
- } from "../chunk-1kpbvat9.js";
4
- import"../chunk-v3hxvnrc.js";
3
+ } from "../chunk-2m0mh09h.js";
5
4
  import"../chunk-ntfm5e85.js";
5
+ import"../chunk-v3hxvnrc.js";
6
6
  export {
7
7
  resourceAccess
8
8
  };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  ServicePortForwarder
3
- } from "../chunk-205a4vma.js";
4
- import"../chunk-hv2kx47y.js";
3
+ } from "../chunk-qhpqse1y.js";
5
4
  import"../chunk-j46x7j7c.js";
6
5
  import"../chunk-6fnhzhdb.js";
7
- import"../chunk-0rs1aacd.js";
8
- import"../chunk-ztenxgjv.js";
6
+ import"../chunk-xh3zwhjh.js";
7
+ import"../chunk-hmah9t8e.js";
8
+ import"../chunk-mn6dwsvh.js";
9
9
  export {
10
10
  ServicePortForwarder
11
11
  };
@@ -1,6 +1,6 @@
1
1
  import type { Client } from "../client";
2
2
  import { type Connection, type ConnectionOptions } from "mysql2/promise";
3
- import type { Database, DatabaseTarget, DatabaseRow, DatabaseQueryLimits, DatabaseQueryResult } from "./types";
3
+ import type { Database, DatabaseTarget, DatabaseRow, DatabaseQueryLimits, DatabaseQueryResult, SqlStatement } from "./types";
4
4
  import type { ConnectionSource, ClientLifecycle } from "../datasource";
5
5
  import { type Transport } from "../transport";
6
6
  export interface MysqlDatabaseOptions extends ClientLifecycle {
@@ -16,6 +16,12 @@ export declare class MysqlDatabase implements Database {
16
16
  private readonly connect;
17
17
  constructor(transports: readonly Transport[], options: MysqlDatabaseOptions, connect?: ConnectionFactory);
18
18
  query(target: DatabaseTarget, sql: string, values: readonly unknown[]): Promise<DatabaseRow[]>;
19
+ /**
20
+ * Independent statements in submission order, one session: a Pod transport pays the exec
21
+ * and connection cost once instead of per statement. Fail-fast like query(); use query()
22
+ * when a statement depends on an earlier result.
23
+ */
24
+ queryBatch(target: DatabaseTarget, statements: readonly SqlStatement[]): Promise<DatabaseRow[][]>;
19
25
  /** Bounded, single-statement prepared execution on an isolated READ ONLY session (native only). */
20
26
  queryReadonly(target: DatabaseTarget, sql: string, values: readonly unknown[], limits: DatabaseQueryLimits): Promise<DatabaseQueryResult>;
21
27
  initialize(target: DatabaseTarget): Promise<void>;
@@ -2,11 +2,16 @@ import {
2
2
  MysqlClient,
3
3
  MysqlDatabase,
4
4
  openMysql
5
- } from "../chunk-jkpwa8tb.js";
5
+ } from "../chunk-jx740hjh.js";
6
6
  import"../chunk-kbewz29a.js";
7
- import"../chunk-s06hs7a8.js";
7
+ import"../chunk-m91t44jh.js";
8
8
  import"../chunk-v3hxvnrc.js";
9
- import"../chunk-xy4g56g4.js";
9
+ import"../chunk-h8fwx0k7.js";
10
+ import"../chunk-5fgbhx2r.js";
11
+ import"../chunk-xh3zwhjh.js";
12
+ import"../chunk-hmah9t8e.js";
13
+ import"../chunk-mn6dwsvh.js";
14
+ import"../chunk-ssj93brx.js";
10
15
  export {
11
16
  openMysql,
12
17
  MysqlDatabase,
@@ -1,16 +1,21 @@
1
- import {
2
- parseMysqlEnvTarget
3
- } from "../chunk-qq77x9ek.js";
4
- import"../chunk-pfar9cm3.js";
5
1
  import {
6
2
  MysqlClient,
7
3
  MysqlDatabase,
8
4
  openMysql
9
- } from "../chunk-jkpwa8tb.js";
5
+ } from "../chunk-jx740hjh.js";
10
6
  import"../chunk-kbewz29a.js";
11
- import"../chunk-s06hs7a8.js";
7
+ import {
8
+ parseMysqlEnvTarget
9
+ } from "../chunk-qq77x9ek.js";
10
+ import"../chunk-pfar9cm3.js";
11
+ import"../chunk-m91t44jh.js";
12
12
  import"../chunk-v3hxvnrc.js";
13
- import"../chunk-xy4g56g4.js";
13
+ import"../chunk-h8fwx0k7.js";
14
+ import"../chunk-5fgbhx2r.js";
15
+ import"../chunk-xh3zwhjh.js";
16
+ import"../chunk-hmah9t8e.js";
17
+ import"../chunk-mn6dwsvh.js";
18
+ import"../chunk-ssj93brx.js";
14
19
  export {
15
20
  parseMysqlEnvTarget,
16
21
  openMysql,
@@ -1,7 +1,16 @@
1
- import type { DatabaseRow, DatabaseTarget } from "./types";
1
+ import type { DatabaseRow, DatabaseTarget, SqlStatement } from "./types";
2
2
  import type { PodPythonTransport } from "../transport";
3
3
  export declare function preparePodSql(sql: string, values: readonly unknown[]): string;
4
4
  export declare function queryMysqlViaPod(transport: PodPythonTransport, target: DatabaseTarget, sql: string, values: readonly unknown[], options: {
5
5
  connectTimeoutMs: number;
6
6
  queryTimeoutMs: number;
7
7
  }): Promise<DatabaseRow[]>;
8
+ /**
9
+ * Independent statements over one Pod process and one MySQL connection: the per-exec cost
10
+ * (kubectl handshake, interpreter start, auth) is paid once. Fail-fast: a statement error
11
+ * aborts the batch, later statements never execute, and the whole batch throws.
12
+ */
13
+ export declare function queryMysqlViaPodBatch(transport: PodPythonTransport, target: DatabaseTarget, statements: readonly SqlStatement[], options: {
14
+ connectTimeoutMs: number;
15
+ queryTimeoutMs: number;
16
+ }): Promise<DatabaseRow[][]>;
package/dist/mysql/pod.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  preparePodSql,
3
- queryMysqlViaPod
4
- } from "../chunk-s06hs7a8.js";
3
+ queryMysqlViaPod,
4
+ queryMysqlViaPodBatch
5
+ } from "../chunk-m91t44jh.js";
5
6
  export {
7
+ queryMysqlViaPodBatch,
6
8
  queryMysqlViaPod,
7
9
  preparePodSql
8
10
  };
@@ -8,6 +8,10 @@ export interface DatabaseTarget extends DatabaseIdentity {
8
8
  database: string;
9
9
  }
10
10
  export type DatabaseRow = Record<string, unknown>;
11
+ export interface SqlStatement {
12
+ sql: string;
13
+ values: readonly unknown[];
14
+ }
11
15
  export interface DatabaseQueryLimits {
12
16
  timeoutMs: number;
13
17
  maxRows: number;
@@ -5,7 +5,7 @@ export interface RuntimeProcess {
5
5
  kill(): void;
6
6
  }
7
7
  export interface SpawnProcessOptions {
8
- stdin?: string | Uint8Array;
8
+ stdin?: string | Uint8Array | ReadableStream<Uint8Array>;
9
9
  env?: NodeJS.ProcessEnv;
10
10
  }
11
11
  /** 使用 Node-compatible 子进程 API,不要求调用方使用 Bun runtime。 */
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  sleep,
3
3
  spawnProcess
4
- } from "../chunk-ztenxgjv.js";
4
+ } from "../chunk-mn6dwsvh.js";
5
5
  export {
6
6
  spawnProcess,
7
7
  sleep
@@ -1,3 +1,7 @@
1
+ import {
2
+ collectRedisKeyStats,
3
+ redisGroupedSampleSizes
4
+ } from "../chunk-jr9y0ghr.js";
1
5
  import {
2
6
  discoverRedisTopology
3
7
  } from "../chunk-c6z919qb.js";
@@ -8,10 +12,6 @@ import {
8
12
  openRedis,
9
13
  parseRedisInfo
10
14
  } from "../chunk-ajhdd4hp.js";
11
- import {
12
- collectRedisKeyStats,
13
- redisGroupedSampleSizes
14
- } from "../chunk-jr9y0ghr.js";
15
15
  export {
16
16
  redisGroupedSampleSizes,
17
17
  parseRedisInfo,
@@ -1,12 +1,17 @@
1
1
  import {
2
2
  S3DataSource
3
- } from "../chunk-0cq1gx86.js";
3
+ } from "../chunk-35cjec9z.js";
4
4
  import"../chunk-z5pm2emz.js";
5
+ import"../chunk-ntfm5e85.js";
5
6
  import"../chunk-zp1s833x.js";
6
7
  import"../chunk-v3hxvnrc.js";
7
8
  import"../chunk-x4ntb898.js";
8
- import"../chunk-xy4g56g4.js";
9
- import"../chunk-ntfm5e85.js";
9
+ import"../chunk-h8fwx0k7.js";
10
+ import"../chunk-5fgbhx2r.js";
11
+ import"../chunk-xh3zwhjh.js";
12
+ import"../chunk-hmah9t8e.js";
13
+ import"../chunk-mn6dwsvh.js";
14
+ import"../chunk-ssj93brx.js";
10
15
  export {
11
16
  S3DataSource
12
17
  };
package/dist/s3/index.js CHANGED
@@ -1,14 +1,19 @@
1
1
  import {
2
2
  S3DataSource
3
- } from "../chunk-0cq1gx86.js";
3
+ } from "../chunk-35cjec9z.js";
4
4
  import"../chunk-z5pm2emz.js";
5
+ import"../chunk-ntfm5e85.js";
5
6
  import {
6
7
  S3Client
7
8
  } from "../chunk-zp1s833x.js";
8
9
  import"../chunk-v3hxvnrc.js";
9
10
  import"../chunk-x4ntb898.js";
10
- import"../chunk-xy4g56g4.js";
11
- import"../chunk-ntfm5e85.js";
11
+ import"../chunk-h8fwx0k7.js";
12
+ import"../chunk-5fgbhx2r.js";
13
+ import"../chunk-xh3zwhjh.js";
14
+ import"../chunk-hmah9t8e.js";
15
+ import"../chunk-mn6dwsvh.js";
16
+ import"../chunk-ssj93brx.js";
12
17
  export {
13
18
  S3DataSource,
14
19
  S3Client
@@ -38,3 +38,4 @@ export declare class PodPythonTransport {
38
38
  export type Transport = TcpTransport | PodPythonTransport;
39
39
  /** Only failure to establish a connection permits a route change; never replay protocol operations. */
40
40
  export declare function isConnectionNetworkError(error: unknown): boolean;
41
+ export { PodRelayTransport, type PodRelayOptions } from "./pod-relay";
@@ -3,10 +3,18 @@ import {
3
3
  PodPythonTransport,
4
4
  PortForwardTransport,
5
5
  isConnectionNetworkError
6
- } from "../chunk-xy4g56g4.js";
6
+ } from "../chunk-h8fwx0k7.js";
7
+ import {
8
+ PodRelayTransport
9
+ } from "../chunk-5fgbhx2r.js";
10
+ import"../chunk-xh3zwhjh.js";
11
+ import"../chunk-hmah9t8e.js";
12
+ import"../chunk-mn6dwsvh.js";
13
+ import"../chunk-ssj93brx.js";
7
14
  export {
8
15
  isConnectionNetworkError,
9
16
  PortForwardTransport,
17
+ PodRelayTransport,
10
18
  PodPythonTransport,
11
19
  DirectTransport
12
20
  };
@@ -0,0 +1,2 @@
1
+ /** Private wire protocol: control travels over exec stdin; database bytes only over TCP. */
2
+ export declare const POD_RELAY_SCRIPT: string;
@@ -0,0 +1,6 @@
1
+ import {
2
+ POD_RELAY_SCRIPT
3
+ } from "../chunk-ssj93brx.js";
4
+ export {
5
+ POD_RELAY_SCRIPT
6
+ };
@@ -0,0 +1,31 @@
1
+ import type { Client } from "../client";
2
+ import { type Executor, type KubectlOptions } from "../kubernetes/executor";
3
+ import { type StartPortForward } from "../kubernetes/port-forward";
4
+ import type { Endpoint, TcpTransport } from "./index";
5
+ export interface PodRelayOptions extends KubectlOptions {
6
+ namespace: string;
7
+ /** Restrict eligible existing Pods without coupling callers to the relay runtime. */
8
+ selector?: string;
9
+ startupTimeoutMs: number;
10
+ connectTimeoutMs: number;
11
+ maxConnections: number;
12
+ maxTargets: number;
13
+ maxCandidatePods: number;
14
+ signal?: AbortSignal;
15
+ }
16
+ /** Execution-scoped TCP transport borrowed by all Service clients; only its root owner disposes it.
17
+ * Python is private. Requires list pods, pods/exec and pods/portforward, never pods/create.
18
+ */
19
+ export declare class PodRelayTransport implements TcpTransport, Client {
20
+ #private;
21
+ private readonly options;
22
+ readonly kind = "tcp";
23
+ readonly name = "pod-relay";
24
+ constructor(options: PodRelayOptions, dependencies?: {
25
+ executor?: Executor;
26
+ startForward?: StartPortForward;
27
+ });
28
+ initialize(): Promise<void>;
29
+ connect(endpoint: Endpoint): Promise<Endpoint>;
30
+ dispose(): Promise<void>;
31
+ }
@@ -0,0 +1,10 @@
1
+ import {
2
+ PodRelayTransport
3
+ } from "../chunk-5fgbhx2r.js";
4
+ import"../chunk-xh3zwhjh.js";
5
+ import"../chunk-hmah9t8e.js";
6
+ import"../chunk-mn6dwsvh.js";
7
+ import"../chunk-ssj93brx.js";
8
+ export {
9
+ PodRelayTransport
10
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compforge/harness-toolbox",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Shared infrastructure clients, transports and bounded data capture",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -102,6 +102,9 @@
102
102
  "@types/node-fetch": "2.6.13",
103
103
  "@types/node": "^22"
104
104
  },
105
+ "overrides": {
106
+ "@compforge/harness-common": "file:../common"
107
+ },
105
108
  "publishConfig": {
106
109
  "access": "public"
107
110
  },
@@ -2,12 +2,12 @@ import {
2
2
  PodLogByteBudget,
3
3
  runPodLogCapturePlan
4
4
  } from "./chunk-8nm9f0hh.js";
5
- import {
6
- logTimestampNanos
7
- } from "./chunk-mp88s7r7.js";
8
5
  import {
9
6
  LogCaptureFile
10
7
  } from "./chunk-1vfwvgkj.js";
8
+ import {
9
+ logTimestampNanos
10
+ } from "./chunk-mp88s7r7.js";
11
11
  import {
12
12
  clientKey
13
13
  } from "./chunk-a9htb6cr.js";
@@ -1,11 +1,11 @@
1
- import {
2
- ConcurrencyPool
3
- } from "./chunk-v3hxvnrc.js";
4
1
  import {
5
2
  KubernetesError,
6
3
  ToolboxError,
7
4
  kubernetesHttpError
8
5
  } from "./chunk-ntfm5e85.js";
6
+ import {
7
+ ConcurrencyPool
8
+ } from "./chunk-v3hxvnrc.js";
9
9
 
10
10
  // src/kubernetes/resources.ts
11
11
  import { KubeConfig } from "@kubernetes/client-node";
File without changes