@elsium-ai/cli 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +331 -50
  2. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -5814,71 +5814,344 @@ init_src();
5814
5814
 
5815
5815
  // ../tools/src/define.ts
5816
5816
  init_src();
5817
+
5818
+ // ../tools/src/sandbox/runner.ts
5819
+ init_src();
5820
+ import { Worker } from "node:worker_threads";
5821
+ var WORKER_SCRIPT = `
5822
+ const { parentPort, workerData } = require('node:worker_threads')
5823
+
5824
+ if (!parentPort) throw new Error('worker-runner must be run as a worker thread')
5825
+
5826
+ const { handlerPath } = workerData
5827
+ let handlerPromise = null
5828
+
5829
+ async function loadHandler() {
5830
+ if (!handlerPromise) {
5831
+ handlerPromise = (async () => {
5832
+ const mod = await import(handlerPath)
5833
+ const fn = (mod && (mod.default || mod.handler)) || null
5834
+ if (typeof fn !== 'function') {
5835
+ throw new Error(
5836
+ 'Sandbox handler module must export a default function or a named "handler" function: ' + handlerPath,
5837
+ )
5838
+ }
5839
+ return fn
5840
+ })().catch((err) => {
5841
+ handlerPromise = null
5842
+ throw err
5843
+ })
5844
+ }
5845
+ return handlerPromise
5846
+ }
5847
+
5848
+ parentPort.on('message', async (msg) => {
5849
+ if (!msg || msg.type !== 'invoke') return
5850
+ try {
5851
+ const handler = await loadHandler()
5852
+ const result = await handler(msg.input)
5853
+ parentPort.postMessage({
5854
+ type: 'result',
5855
+ invocationId: msg.invocationId,
5856
+ success: true,
5857
+ data: result,
5858
+ })
5859
+ } catch (err) {
5860
+ parentPort.postMessage({
5861
+ type: 'result',
5862
+ invocationId: msg.invocationId,
5863
+ success: false,
5864
+ error: {
5865
+ name: (err && err.name) || 'Error',
5866
+ message: (err && err.message) || String(err),
5867
+ stack: err && err.stack,
5868
+ },
5869
+ })
5870
+ }
5871
+ })
5872
+ `;
5873
+ function rejectPending(state, error) {
5874
+ if (!state.pending)
5875
+ return;
5876
+ const pending = state.pending;
5877
+ state.pending = null;
5878
+ pending.reject(error);
5879
+ }
5880
+ function attachWorkerListeners(worker, state) {
5881
+ worker.on("message", (msg) => {
5882
+ if (msg?.type !== "result")
5883
+ return;
5884
+ const pending = state.pending;
5885
+ if (!pending || pending.id !== msg.invocationId)
5886
+ return;
5887
+ state.pending = null;
5888
+ if (msg.success) {
5889
+ pending.resolve(msg.data);
5890
+ } else {
5891
+ const error = new Error(msg.error?.message ?? "Sandbox handler failed");
5892
+ if (msg.error?.name)
5893
+ error.name = msg.error.name;
5894
+ if (msg.error?.stack)
5895
+ error.stack = msg.error.stack;
5896
+ pending.reject(error);
5897
+ }
5898
+ });
5899
+ worker.on("error", (err2) => {
5900
+ state.worker = null;
5901
+ rejectPending(state, err2);
5902
+ });
5903
+ worker.on("exit", (code) => {
5904
+ if (state.worker === worker)
5905
+ state.worker = null;
5906
+ if (code !== 0) {
5907
+ rejectPending(state, new Error(`Sandbox worker exited with code ${code}`));
5908
+ }
5909
+ });
5910
+ }
5911
+ function ensureWorker(state, handlerPath) {
5912
+ if (state.worker)
5913
+ return state.worker;
5914
+ const w = new Worker(WORKER_SCRIPT, {
5915
+ eval: true,
5916
+ workerData: { handlerPath }
5917
+ });
5918
+ attachWorkerListeners(w, state);
5919
+ w.unref();
5920
+ state.worker = w;
5921
+ return w;
5922
+ }
5923
+ function killWorker(state) {
5924
+ const dying = state.worker;
5925
+ state.worker = null;
5926
+ if (dying) {
5927
+ dying.terminate().catch(() => {});
5928
+ }
5929
+ }
5930
+ function postInvocation(state, worker, invocationId, input, timeoutMs, handlerPath, signal) {
5931
+ return new Promise((resolve2, reject) => {
5932
+ let timer = null;
5933
+ let abortHandler = null;
5934
+ const cleanup = () => {
5935
+ if (timer)
5936
+ clearTimeout(timer);
5937
+ if (signal && abortHandler)
5938
+ signal.removeEventListener("abort", abortHandler);
5939
+ };
5940
+ state.pending = {
5941
+ id: invocationId,
5942
+ resolve: (v) => {
5943
+ cleanup();
5944
+ resolve2(v);
5945
+ },
5946
+ reject: (e) => {
5947
+ cleanup();
5948
+ reject(e);
5949
+ }
5950
+ };
5951
+ timer = setTimeout(() => {
5952
+ if (state.pending?.id !== invocationId)
5953
+ return;
5954
+ const pending = state.pending;
5955
+ state.pending = null;
5956
+ killWorker(state);
5957
+ pending.reject(ElsiumError.timeout(`sandbox(${handlerPath})`, timeoutMs));
5958
+ }, timeoutMs);
5959
+ if (signal) {
5960
+ abortHandler = () => {
5961
+ if (state.pending?.id !== invocationId)
5962
+ return;
5963
+ const pending = state.pending;
5964
+ state.pending = null;
5965
+ killWorker(state);
5966
+ pending.reject(new Error("Sandbox invocation aborted"));
5967
+ };
5968
+ signal.addEventListener("abort", abortHandler, { once: true });
5969
+ }
5970
+ try {
5971
+ worker.postMessage({ type: "invoke", invocationId, input });
5972
+ } catch (err2) {
5973
+ if (state.pending?.id === invocationId)
5974
+ state.pending = null;
5975
+ cleanup();
5976
+ reject(err2 instanceof Error ? err2 : new Error(String(err2)));
5977
+ }
5978
+ });
5979
+ }
5980
+ function createWorkerSandboxRunner(config, defaultTimeoutMs) {
5981
+ const handlerPath = typeof config.handler === "string" ? config.handler : config.handler.href;
5982
+ const timeoutMs = config.timeoutMs ?? defaultTimeoutMs;
5983
+ const state = {
5984
+ worker: null,
5985
+ pending: null,
5986
+ chain: Promise.resolve(),
5987
+ disposed: false
5988
+ };
5989
+ async function runOnce(input, signal) {
5990
+ if (state.disposed) {
5991
+ throw new Error("Sandbox runner has been disposed");
5992
+ }
5993
+ if (signal?.aborted) {
5994
+ throw new Error("Sandbox invocation aborted");
5995
+ }
5996
+ const worker = ensureWorker(state, handlerPath);
5997
+ const invocationId = generateId("si");
5998
+ return postInvocation(state, worker, invocationId, input, timeoutMs, handlerPath, signal);
5999
+ }
6000
+ return {
6001
+ async invoke(input, signal) {
6002
+ const previous = state.chain.catch(() => {
6003
+ return;
6004
+ });
6005
+ const next = previous.then(() => runOnce(input, signal));
6006
+ state.chain = next.catch(() => {
6007
+ return;
6008
+ });
6009
+ return next;
6010
+ },
6011
+ async dispose() {
6012
+ state.disposed = true;
6013
+ const w = state.worker;
6014
+ state.worker = null;
6015
+ rejectPending(state, new Error("Sandbox runner disposed"));
6016
+ if (w) {
6017
+ try {
6018
+ await w.terminate();
6019
+ } catch {}
6020
+ }
6021
+ }
6022
+ };
6023
+ }
6024
+
6025
+ // ../tools/src/define.ts
6026
+ var log7 = createLogger();
6027
+ var IS_BUN = typeof globalThis.Bun !== "undefined" || typeof process !== "undefined" && Boolean(process.versions.bun);
6028
+ var bunSandboxWarningShown = false;
6029
+ function warnBunSandboxOnce(toolName) {
6030
+ if (bunSandboxWarningShown)
6031
+ return;
6032
+ bunSandboxWarningShown = true;
6033
+ log7.warn(`Tool "${toolName}" uses sandbox.mode="worker" under Bun. Crash isolation is incomplete on Bun: process.exit() inside the handler does NOT terminate the worker (it does on Node). Other guarantees (process, memory, closure-state, timeout, abort) hold. Track: https://github.com/elsium-ai/elsium-ai/issues — search for "Bun crash isolation".`);
6034
+ }
6035
+ function formatZodErrors(error) {
6036
+ return error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
6037
+ }
6038
+ function buildExecutionFailure(toolCallId, startTime, error) {
6039
+ return {
6040
+ success: false,
6041
+ error,
6042
+ toolCallId,
6043
+ durationMs: Math.round(performance.now() - startTime)
6044
+ };
6045
+ }
6046
+ function buildExecutionSuccess(toolCallId, startTime, data) {
6047
+ return {
6048
+ success: true,
6049
+ data,
6050
+ toolCallId,
6051
+ durationMs: Math.round(performance.now() - startTime)
6052
+ };
6053
+ }
6054
+ function wireUserSignalToController(controller, userSignal) {
6055
+ if (!userSignal)
6056
+ return;
6057
+ if (userSignal.aborted) {
6058
+ controller.abort();
6059
+ return;
6060
+ }
6061
+ userSignal.addEventListener("abort", () => controller.abort(), { once: true });
6062
+ }
6063
+ function createAbortRejection(signal, isTimeout, name, timeoutMs) {
6064
+ return new Promise((_, reject) => {
6065
+ signal.addEventListener("abort", () => {
6066
+ if (isTimeout()) {
6067
+ reject(ElsiumError.timeout(name, timeoutMs));
6068
+ } else {
6069
+ reject(new ElsiumError({
6070
+ code: "TOOL_ERROR",
6071
+ message: `Tool "${name}" was aborted`,
6072
+ retryable: false
6073
+ }));
6074
+ }
6075
+ }, { once: true });
6076
+ });
6077
+ }
5817
6078
  function defineTool(config) {
5818
6079
  const input = config.input ?? config.parameters;
5819
6080
  if (!input) {
5820
6081
  throw ElsiumError.validation(`Tool "${config.name}" requires an input schema (use "input" or "parameters" key)`);
5821
6082
  }
5822
- const { name, description, output, handler, timeoutMs = 30000 } = config;
5823
- return {
6083
+ if (!config.handler && !config.sandbox) {
6084
+ throw ElsiumError.validation(`Tool "${config.name}" requires either an inline "handler" or a "sandbox" config`);
6085
+ }
6086
+ if (config.sandbox && config.sandbox.mode !== "worker") {
6087
+ throw ElsiumError.validation(`Tool "${config.name}" sandbox.mode must be "worker" (received "${config.sandbox.mode}")`);
6088
+ }
6089
+ if (config.sandbox && IS_BUN) {
6090
+ warnBunSandboxOnce(config.name);
6091
+ }
6092
+ const { name, description, output, sandbox, timeoutMs = 30000 } = config;
6093
+ const handler = config.handler;
6094
+ let sandboxRunner = null;
6095
+ function getSandboxRunner() {
6096
+ if (!sandboxRunner) {
6097
+ if (!sandbox) {
6098
+ throw ElsiumError.validation(`Tool "${name}" has no sandbox config`);
6099
+ }
6100
+ sandboxRunner = createWorkerSandboxRunner(sandbox, timeoutMs);
6101
+ }
6102
+ return sandboxRunner;
6103
+ }
6104
+ async function runHandler(parsedInput, context) {
6105
+ if (sandbox) {
6106
+ const result = await getSandboxRunner().invoke(parsedInput, context.signal);
6107
+ return result;
6108
+ }
6109
+ if (!handler) {
6110
+ throw ElsiumError.validation(`Tool "${name}" has no handler`);
6111
+ }
6112
+ return handler(parsedInput, context);
6113
+ }
6114
+ const tool = {
5824
6115
  name,
5825
6116
  description,
5826
6117
  inputSchema: input,
5827
6118
  outputSchema: output,
5828
6119
  timeoutMs,
6120
+ sandbox,
5829
6121
  async execute(rawInput, partialCtx) {
5830
6122
  const toolCallId = partialCtx?.toolCallId ?? generateId("tc");
5831
6123
  const startTime = performance.now();
5832
6124
  const parsed = input.safeParse(rawInput);
5833
6125
  if (!parsed.success) {
5834
- return {
5835
- success: false,
5836
- error: `Invalid input: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")}`,
5837
- toolCallId,
5838
- durationMs: Math.round(performance.now() - startTime)
5839
- };
6126
+ return buildExecutionFailure(toolCallId, startTime, `Invalid input: ${formatZodErrors(parsed.error)}`);
5840
6127
  }
5841
6128
  const controller = new AbortController;
5842
- const timer = setTimeout(() => controller.abort(), timeoutMs);
6129
+ let timedOut = false;
6130
+ const timer = setTimeout(() => {
6131
+ timedOut = true;
6132
+ controller.abort();
6133
+ }, timeoutMs);
6134
+ wireUserSignalToController(controller, partialCtx?.signal);
5843
6135
  const context = {
5844
6136
  toolCallId,
5845
6137
  traceId: partialCtx?.traceId,
5846
- signal: partialCtx?.signal ?? controller.signal
6138
+ signal: controller.signal
5847
6139
  };
5848
6140
  try {
5849
6141
  const result = await Promise.race([
5850
- handler(parsed.data, context),
5851
- new Promise((_, reject) => {
5852
- controller.signal.addEventListener("abort", () => {
5853
- reject(ElsiumError.timeout(name, timeoutMs));
5854
- });
5855
- })
6142
+ runHandler(parsed.data, context),
6143
+ createAbortRejection(controller.signal, () => timedOut, name, timeoutMs)
5856
6144
  ]);
5857
6145
  if (output) {
5858
6146
  const validated = output.safeParse(result);
5859
6147
  if (!validated.success) {
5860
- return {
5861
- success: false,
5862
- error: `Invalid output: ${validated.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")}`,
5863
- toolCallId,
5864
- durationMs: Math.round(performance.now() - startTime)
5865
- };
6148
+ return buildExecutionFailure(toolCallId, startTime, `Invalid output: ${formatZodErrors(validated.error)}`);
5866
6149
  }
5867
6150
  }
5868
- return {
5869
- success: true,
5870
- data: result,
5871
- toolCallId,
5872
- durationMs: Math.round(performance.now() - startTime)
5873
- };
6151
+ return buildExecutionSuccess(toolCallId, startTime, result);
5874
6152
  } catch (error) {
5875
6153
  const message = error instanceof Error ? error.message : String(error);
5876
- return {
5877
- success: false,
5878
- error: message,
5879
- toolCallId,
5880
- durationMs: Math.round(performance.now() - startTime)
5881
- };
6154
+ return buildExecutionFailure(toolCallId, startTime, message);
5882
6155
  } finally {
5883
6156
  clearTimeout(timer);
5884
6157
  }
@@ -5889,8 +6162,16 @@ function defineTool(config) {
5889
6162
  description,
5890
6163
  inputSchema: zodToJsonSchema(input)
5891
6164
  };
6165
+ },
6166
+ async dispose() {
6167
+ if (sandboxRunner) {
6168
+ const r = sandboxRunner;
6169
+ sandboxRunner = null;
6170
+ await r.dispose();
6171
+ }
5892
6172
  }
5893
6173
  };
6174
+ return tool;
5894
6175
  }
5895
6176
  // ../tools/src/toolkit.ts
5896
6177
  init_src();
@@ -10218,7 +10499,7 @@ init_src();
10218
10499
  init_src();
10219
10500
  import { createRequire } from "node:module";
10220
10501
  var require2 = createRequire(import.meta.url);
10221
- var log7 = createLogger();
10502
+ var log8 = createLogger();
10222
10503
  var BLOCKED_KEYS2 = new Set(["__proto__", "constructor", "prototype"]);
10223
10504
  // ../agents/src/stores/integrity.ts
10224
10505
  var ZERO_HASH = "0".repeat(64);
@@ -10228,7 +10509,7 @@ init_src();
10228
10509
  init_src();
10229
10510
  // ../agents/src/async-agent.ts
10230
10511
  init_src();
10231
- var log8 = createLogger();
10512
+ var log9 = createLogger();
10232
10513
  // ../agents/src/session.ts
10233
10514
  init_src();
10234
10515
  // ../agents/src/react.ts
@@ -10251,7 +10532,7 @@ var vectorStoreRegistry = createRegistry("vectorStore");
10251
10532
  init_src();
10252
10533
  import { createRequire as createRequire2 } from "node:module";
10253
10534
  var require3 = createRequire2(import.meta.url);
10254
- var log9 = createLogger();
10535
+ var log10 = createLogger();
10255
10536
  var BLOCKED_KEYS3 = new Set(["__proto__", "constructor", "prototype"]);
10256
10537
  // ../rag/src/stores/qdrant.ts
10257
10538
  init_src();
@@ -10466,13 +10747,13 @@ init_src();
10466
10747
  init_src();
10467
10748
  // ../observe/src/tracer.ts
10468
10749
  init_src();
10469
- var log10 = createLogger();
10750
+ var log11 = createLogger();
10470
10751
  // ../observe/src/audit.ts
10471
10752
  import { createHash as createHash5 } from "node:crypto";
10472
10753
 
10473
10754
  // ../observe/src/audit-sink.ts
10474
10755
  init_src();
10475
- var log11 = createLogger();
10756
+ var log12 = createLogger();
10476
10757
  function getRetryDelay2(attempt, baseDelayMs, maxDelayMs) {
10477
10758
  const delay = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
10478
10759
  return delay * (0.5 + Math.random() * 0.5);
@@ -10500,14 +10781,14 @@ async function deliverToSink(sink, events, retryConfig, deadLetterSink, onError)
10500
10781
  try {
10501
10782
  await sendWithRetry(sink, filtered, retryConfig);
10502
10783
  } catch (error) {
10503
- log11.error("Audit sink delivery failed", { sink: sink.name });
10784
+ log12.error("Audit sink delivery failed", { sink: sink.name });
10504
10785
  onError?.(sink.name, error);
10505
10786
  if (!deadLetterSink)
10506
10787
  return;
10507
10788
  try {
10508
10789
  await deadLetterSink.send(filtered);
10509
10790
  } catch (dlqError) {
10510
- log11.error("Dead letter sink delivery failed", { sink: deadLetterSink.name });
10791
+ log12.error("Dead letter sink delivery failed", { sink: deadLetterSink.name });
10511
10792
  onError?.(deadLetterSink.name, dlqError);
10512
10793
  }
10513
10794
  }
@@ -10551,7 +10832,7 @@ function createSinkManager(config) {
10551
10832
  dispatch(event) {
10552
10833
  if (buffer.length >= maxBufferSize) {
10553
10834
  buffer.shift();
10554
- log11.warn("Audit sink buffer full, dropping oldest event");
10835
+ log12.warn("Audit sink buffer full, dropping oldest event");
10555
10836
  }
10556
10837
  buffer.push(event);
10557
10838
  if (buffer.length >= batchSize)
@@ -10862,16 +11143,16 @@ function auditMiddleware(auditTrail) {
10862
11143
  }
10863
11144
  // ../observe/src/audit-sink-jsonl.ts
10864
11145
  init_src();
10865
- var log12 = createLogger();
11146
+ var log13 = createLogger();
10866
11147
  // ../observe/src/experiment.ts
10867
11148
  init_src();
10868
- var log13 = createLogger();
11149
+ var log14 = createLogger();
10869
11150
  // ../observe/src/studio-exporter.ts
10870
11151
  init_src();
10871
- var log14 = createLogger();
11152
+ var log15 = createLogger();
10872
11153
  // ../observe/src/otel.ts
10873
11154
  init_src();
10874
- var log15 = createLogger();
11155
+ var log16 = createLogger();
10875
11156
  // ../app/src/app.ts
10876
11157
  init_src();
10877
11158
 
@@ -12632,19 +12913,19 @@ init_src();
12632
12913
  init_src();
12633
12914
 
12634
12915
  // ../app/src/app.ts
12635
- var log16 = createLogger();
12916
+ var log17 = createLogger();
12636
12917
  // ../app/src/rbac.ts
12637
12918
  init_src();
12638
- var log17 = createLogger();
12919
+ var log18 = createLogger();
12639
12920
  // ../app/src/tenant.ts
12640
12921
  init_src();
12641
- var log18 = createLogger();
12922
+ var log19 = createLogger();
12642
12923
  var tenantUsage = new Map;
12643
12924
  // ../mcp/src/client.ts
12644
12925
  init_src();
12645
12926
  // ../mcp/src/server.ts
12646
12927
  init_src();
12647
- var log19 = createLogger();
12928
+ var log20 = createLogger();
12648
12929
  // ../mcp/src/trust.ts
12649
12930
  init_src();
12650
12931
  var MAX_TOOL_OUTPUT_SIZE = 1024 * 1024;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elsium-ai/cli",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "CLI tool for ElsiumAI projects",
5
5
  "license": "MIT",
6
6
  "author": "Eric Utrera <ebutrera9103@gmail.com>",
@@ -24,9 +24,9 @@
24
24
  "dev": "bun --watch src/cli.ts"
25
25
  },
26
26
  "dependencies": {
27
- "@elsium-ai/core": "^0.11.0",
28
- "@elsium-ai/observe": "^0.11.0",
29
- "elsium-ai": "^0.11.0"
27
+ "@elsium-ai/core": "^0.12.1",
28
+ "@elsium-ai/observe": "^0.12.1",
29
+ "elsium-ai": "^0.12.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "typescript": "^5.7.0"