nestor-sh 2.1.0 → 2.1.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/nestor.mjs +903 -221
  2. package/package.json +1 -1
package/dist/nestor.mjs CHANGED
@@ -558,7 +558,7 @@ var SCHEMA_VERSION, CREATE_TABLES_SQL, CREATE_FTS5_SQL;
558
558
  var init_schema = __esm({
559
559
  "../db/src/schema.ts"() {
560
560
  "use strict";
561
- SCHEMA_VERSION = 6;
561
+ SCHEMA_VERSION = 7;
562
562
  CREATE_TABLES_SQL = `
563
563
  -- \u2500\u2500\u2500 Tenants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
564
564
 
@@ -1532,6 +1532,22 @@ CREATE TABLE IF NOT EXISTS conversations (
1532
1532
  CREATE INDEX IF NOT EXISTS idx_conversations_agent ON conversations(agent_id);
1533
1533
  CREATE INDEX IF NOT EXISTS idx_conversations_tenant ON conversations(tenant_id);
1534
1534
 
1535
+ -- \u2500\u2500\u2500 LLM Provider Keys (encrypted LLM provider credentials) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1536
+
1537
+ CREATE TABLE IF NOT EXISTS llm_provider_keys (
1538
+ id TEXT PRIMARY KEY NOT NULL,
1539
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1540
+ provider TEXT NOT NULL CHECK(provider IN ('claude','openai','gemini','grok','mistral','ollama')),
1541
+ key_encrypted TEXT NOT NULL,
1542
+ label TEXT DEFAULT '',
1543
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1544
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
1545
+ UNIQUE(tenant_id, provider)
1546
+ );
1547
+
1548
+ CREATE INDEX IF NOT EXISTS idx_llm_keys_tenant ON llm_provider_keys(tenant_id);
1549
+ CREATE INDEX IF NOT EXISTS idx_llm_keys_provider ON llm_provider_keys(provider);
1550
+
1535
1551
  -- NOTE: sql.js (emscripten WASM build of SQLite) does not include the FTS5
1536
1552
  -- extension by default. The FTS5 virtual table and sync triggers below will
1537
1553
  -- only take effect when running against a SQLite build that ships FTS5
@@ -6133,6 +6149,80 @@ var init_store = __esm({
6133
6149
  if (affected > 0) this.save();
6134
6150
  return affected > 0;
6135
6151
  }
6152
+ // ─── API Keys ──────────────────────────────────────────────────────────
6153
+ /**
6154
+ * Upsert an encrypted API key for a provider.
6155
+ */
6156
+ setApiKey(provider, encryptedKey, tenantId = "default", label = "") {
6157
+ const id = crypto2.randomUUID();
6158
+ runSql(
6159
+ this.db,
6160
+ `INSERT INTO llm_provider_keys (id, tenant_id, provider, key_encrypted, label, created_at, updated_at)
6161
+ VALUES (?, ?, ?, ?, ?, datetime('now'), datetime('now'))
6162
+ ON CONFLICT(tenant_id, provider) DO UPDATE SET
6163
+ key_encrypted = excluded.key_encrypted,
6164
+ label = excluded.label,
6165
+ updated_at = datetime('now')`,
6166
+ [id, tenantId, provider, encryptedKey, label]
6167
+ );
6168
+ this.save();
6169
+ }
6170
+ /**
6171
+ * Get the encrypted key for a provider, or null if not set.
6172
+ */
6173
+ getApiKey(provider, tenantId = "default") {
6174
+ const row = queryOne(
6175
+ this.db,
6176
+ "SELECT key_encrypted FROM llm_provider_keys WHERE provider = ? AND tenant_id = ?",
6177
+ [provider, tenantId]
6178
+ );
6179
+ return row ? row.key_encrypted : null;
6180
+ }
6181
+ /**
6182
+ * List all configured API key providers with masked keys (for API response).
6183
+ * Does NOT return the actual encrypted key — only metadata.
6184
+ */
6185
+ listProviderKeys(tenantId = "default") {
6186
+ const rows = queryAll(
6187
+ this.db,
6188
+ "SELECT provider, label, created_at, updated_at FROM llm_provider_keys WHERE tenant_id = ? ORDER BY provider",
6189
+ [tenantId]
6190
+ );
6191
+ return rows.map((row) => ({
6192
+ provider: row.provider,
6193
+ label: row.label || "",
6194
+ createdAt: row.created_at,
6195
+ updatedAt: row.updated_at,
6196
+ hasKey: true
6197
+ }));
6198
+ }
6199
+ /**
6200
+ * List all API keys with encrypted values (server-side only, for decryption).
6201
+ */
6202
+ listApiKeysRaw(tenantId = "default") {
6203
+ const rows = queryAll(
6204
+ this.db,
6205
+ "SELECT provider, key_encrypted, label FROM llm_provider_keys WHERE tenant_id = ?",
6206
+ [tenantId]
6207
+ );
6208
+ return rows.map((row) => ({
6209
+ provider: row.provider,
6210
+ keyEncrypted: row.key_encrypted,
6211
+ label: row.label || ""
6212
+ }));
6213
+ }
6214
+ /**
6215
+ * Delete an API key for a provider.
6216
+ */
6217
+ deleteProviderKey(provider, tenantId = "default") {
6218
+ const affected = runSql(
6219
+ this.db,
6220
+ "DELETE FROM llm_provider_keys WHERE provider = ? AND tenant_id = ?",
6221
+ [provider, tenantId]
6222
+ );
6223
+ if (affected > 0) this.save();
6224
+ return affected > 0;
6225
+ }
6136
6226
  };
6137
6227
  }
6138
6228
  });
@@ -10593,7 +10683,7 @@ var SERVER_VERSION, startTime;
10593
10683
  var init_health = __esm({
10594
10684
  "../server/src/routes/health.ts"() {
10595
10685
  "use strict";
10596
- SERVER_VERSION = "2.1.0";
10686
+ SERVER_VERSION = "2.1.1";
10597
10687
  startTime = Date.now();
10598
10688
  }
10599
10689
  });
@@ -19383,7 +19473,7 @@ function isNativeAvailable() {
19383
19473
  return nativeModule !== null;
19384
19474
  }
19385
19475
  function getNativeVersion() {
19386
- return nativeModule ? "2.1.0" : null;
19476
+ return nativeModule ? "2.1.1" : null;
19387
19477
  }
19388
19478
  function validateSsrf(url, allowPrivate = false) {
19389
19479
  if (nativeModule) {
@@ -70149,7 +70239,7 @@ var require_util2 = __commonJS({
70149
70239
  return path30;
70150
70240
  }
70151
70241
  exports2.normalize = normalize4;
70152
- function join25(aRoot, aPath) {
70242
+ function join26(aRoot, aPath) {
70153
70243
  if (aRoot === "") {
70154
70244
  aRoot = ".";
70155
70245
  }
@@ -70181,7 +70271,7 @@ var require_util2 = __commonJS({
70181
70271
  }
70182
70272
  return joined;
70183
70273
  }
70184
- exports2.join = join25;
70274
+ exports2.join = join26;
70185
70275
  exports2.isAbsolute = function(aPath) {
70186
70276
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
70187
70277
  };
@@ -70354,7 +70444,7 @@ var require_util2 = __commonJS({
70354
70444
  parsed.path = parsed.path.substring(0, index + 1);
70355
70445
  }
70356
70446
  }
70357
- sourceURL = join25(urlGenerate(parsed), sourceURL);
70447
+ sourceURL = join26(urlGenerate(parsed), sourceURL);
70358
70448
  }
70359
70449
  return normalize4(sourceURL);
70360
70450
  }
@@ -72156,7 +72246,7 @@ var require_escodegen = __commonJS({
72156
72246
  function noEmptySpace() {
72157
72247
  return space ? space : " ";
72158
72248
  }
72159
- function join25(left2, right2) {
72249
+ function join26(left2, right2) {
72160
72250
  var leftSource, rightSource, leftCharCode, rightCharCode;
72161
72251
  leftSource = toSourceNodeWhenNeeded(left2).toString();
72162
72252
  if (leftSource.length === 0) {
@@ -72487,8 +72577,8 @@ var require_escodegen = __commonJS({
72487
72577
  } else {
72488
72578
  result.push(that.generateExpression(stmt.left, Precedence.Call, E_TTT));
72489
72579
  }
72490
- result = join25(result, operator);
72491
- result = [join25(
72580
+ result = join26(result, operator);
72581
+ result = [join26(
72492
72582
  result,
72493
72583
  that.generateExpression(stmt.right, Precedence.Assignment, E_TTT)
72494
72584
  ), ")"];
@@ -72631,11 +72721,11 @@ var require_escodegen = __commonJS({
72631
72721
  var result, fragment;
72632
72722
  result = ["class"];
72633
72723
  if (stmt.id) {
72634
- result = join25(result, this.generateExpression(stmt.id, Precedence.Sequence, E_TTT));
72724
+ result = join26(result, this.generateExpression(stmt.id, Precedence.Sequence, E_TTT));
72635
72725
  }
72636
72726
  if (stmt.superClass) {
72637
- fragment = join25("extends", this.generateExpression(stmt.superClass, Precedence.Unary, E_TTT));
72638
- result = join25(result, fragment);
72727
+ fragment = join26("extends", this.generateExpression(stmt.superClass, Precedence.Unary, E_TTT));
72728
+ result = join26(result, fragment);
72639
72729
  }
72640
72730
  result.push(space);
72641
72731
  result.push(this.generateStatement(stmt.body, S_TFFT));
@@ -72648,9 +72738,9 @@ var require_escodegen = __commonJS({
72648
72738
  return escapeDirective(stmt.directive) + this.semicolon(flags);
72649
72739
  },
72650
72740
  DoWhileStatement: function(stmt, flags) {
72651
- var result = join25("do", this.maybeBlock(stmt.body, S_TFFF));
72741
+ var result = join26("do", this.maybeBlock(stmt.body, S_TFFF));
72652
72742
  result = this.maybeBlockSuffix(stmt.body, result);
72653
- return join25(result, [
72743
+ return join26(result, [
72654
72744
  "while" + space + "(",
72655
72745
  this.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
72656
72746
  ")" + this.semicolon(flags)
@@ -72686,11 +72776,11 @@ var require_escodegen = __commonJS({
72686
72776
  ExportDefaultDeclaration: function(stmt, flags) {
72687
72777
  var result = ["export"], bodyFlags;
72688
72778
  bodyFlags = flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF;
72689
- result = join25(result, "default");
72779
+ result = join26(result, "default");
72690
72780
  if (isStatement(stmt.declaration)) {
72691
- result = join25(result, this.generateStatement(stmt.declaration, bodyFlags));
72781
+ result = join26(result, this.generateStatement(stmt.declaration, bodyFlags));
72692
72782
  } else {
72693
- result = join25(result, this.generateExpression(stmt.declaration, Precedence.Assignment, E_TTT) + this.semicolon(flags));
72783
+ result = join26(result, this.generateExpression(stmt.declaration, Precedence.Assignment, E_TTT) + this.semicolon(flags));
72694
72784
  }
72695
72785
  return result;
72696
72786
  },
@@ -72698,15 +72788,15 @@ var require_escodegen = __commonJS({
72698
72788
  var result = ["export"], bodyFlags, that = this;
72699
72789
  bodyFlags = flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF;
72700
72790
  if (stmt.declaration) {
72701
- return join25(result, this.generateStatement(stmt.declaration, bodyFlags));
72791
+ return join26(result, this.generateStatement(stmt.declaration, bodyFlags));
72702
72792
  }
72703
72793
  if (stmt.specifiers) {
72704
72794
  if (stmt.specifiers.length === 0) {
72705
- result = join25(result, "{" + space + "}");
72795
+ result = join26(result, "{" + space + "}");
72706
72796
  } else if (stmt.specifiers[0].type === Syntax.ExportBatchSpecifier) {
72707
- result = join25(result, this.generateExpression(stmt.specifiers[0], Precedence.Sequence, E_TTT));
72797
+ result = join26(result, this.generateExpression(stmt.specifiers[0], Precedence.Sequence, E_TTT));
72708
72798
  } else {
72709
- result = join25(result, "{");
72799
+ result = join26(result, "{");
72710
72800
  withIndent(function(indent2) {
72711
72801
  var i, iz;
72712
72802
  result.push(newline);
@@ -72724,7 +72814,7 @@ var require_escodegen = __commonJS({
72724
72814
  result.push(base + "}");
72725
72815
  }
72726
72816
  if (stmt.source) {
72727
- result = join25(result, [
72817
+ result = join26(result, [
72728
72818
  "from" + space,
72729
72819
  // ModuleSpecifier
72730
72820
  this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
@@ -72812,7 +72902,7 @@ var require_escodegen = __commonJS({
72812
72902
  ];
72813
72903
  cursor = 0;
72814
72904
  if (stmt.specifiers[cursor].type === Syntax.ImportDefaultSpecifier) {
72815
- result = join25(result, [
72905
+ result = join26(result, [
72816
72906
  this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
72817
72907
  ]);
72818
72908
  ++cursor;
@@ -72822,7 +72912,7 @@ var require_escodegen = __commonJS({
72822
72912
  result.push(",");
72823
72913
  }
72824
72914
  if (stmt.specifiers[cursor].type === Syntax.ImportNamespaceSpecifier) {
72825
- result = join25(result, [
72915
+ result = join26(result, [
72826
72916
  space,
72827
72917
  this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
72828
72918
  ]);
@@ -72851,7 +72941,7 @@ var require_escodegen = __commonJS({
72851
72941
  }
72852
72942
  }
72853
72943
  }
72854
- result = join25(result, [
72944
+ result = join26(result, [
72855
72945
  "from" + space,
72856
72946
  // ModuleSpecifier
72857
72947
  this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
@@ -72905,7 +72995,7 @@ var require_escodegen = __commonJS({
72905
72995
  return result;
72906
72996
  },
72907
72997
  ThrowStatement: function(stmt, flags) {
72908
- return [join25(
72998
+ return [join26(
72909
72999
  "throw",
72910
73000
  this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
72911
73001
  ), this.semicolon(flags)];
@@ -72916,7 +73006,7 @@ var require_escodegen = __commonJS({
72916
73006
  result = this.maybeBlockSuffix(stmt.block, result);
72917
73007
  if (stmt.handlers) {
72918
73008
  for (i = 0, iz = stmt.handlers.length; i < iz; ++i) {
72919
- result = join25(result, this.generateStatement(stmt.handlers[i], S_TFFF));
73009
+ result = join26(result, this.generateStatement(stmt.handlers[i], S_TFFF));
72920
73010
  if (stmt.finalizer || i + 1 !== iz) {
72921
73011
  result = this.maybeBlockSuffix(stmt.handlers[i].body, result);
72922
73012
  }
@@ -72924,7 +73014,7 @@ var require_escodegen = __commonJS({
72924
73014
  } else {
72925
73015
  guardedHandlers = stmt.guardedHandlers || [];
72926
73016
  for (i = 0, iz = guardedHandlers.length; i < iz; ++i) {
72927
- result = join25(result, this.generateStatement(guardedHandlers[i], S_TFFF));
73017
+ result = join26(result, this.generateStatement(guardedHandlers[i], S_TFFF));
72928
73018
  if (stmt.finalizer || i + 1 !== iz) {
72929
73019
  result = this.maybeBlockSuffix(guardedHandlers[i].body, result);
72930
73020
  }
@@ -72932,13 +73022,13 @@ var require_escodegen = __commonJS({
72932
73022
  if (stmt.handler) {
72933
73023
  if (Array.isArray(stmt.handler)) {
72934
73024
  for (i = 0, iz = stmt.handler.length; i < iz; ++i) {
72935
- result = join25(result, this.generateStatement(stmt.handler[i], S_TFFF));
73025
+ result = join26(result, this.generateStatement(stmt.handler[i], S_TFFF));
72936
73026
  if (stmt.finalizer || i + 1 !== iz) {
72937
73027
  result = this.maybeBlockSuffix(stmt.handler[i].body, result);
72938
73028
  }
72939
73029
  }
72940
73030
  } else {
72941
- result = join25(result, this.generateStatement(stmt.handler, S_TFFF));
73031
+ result = join26(result, this.generateStatement(stmt.handler, S_TFFF));
72942
73032
  if (stmt.finalizer) {
72943
73033
  result = this.maybeBlockSuffix(stmt.handler.body, result);
72944
73034
  }
@@ -72946,7 +73036,7 @@ var require_escodegen = __commonJS({
72946
73036
  }
72947
73037
  }
72948
73038
  if (stmt.finalizer) {
72949
- result = join25(result, ["finally", this.maybeBlock(stmt.finalizer, S_TFFF)]);
73039
+ result = join26(result, ["finally", this.maybeBlock(stmt.finalizer, S_TFFF)]);
72950
73040
  }
72951
73041
  return result;
72952
73042
  },
@@ -72980,7 +73070,7 @@ var require_escodegen = __commonJS({
72980
73070
  withIndent(function() {
72981
73071
  if (stmt.test) {
72982
73072
  result = [
72983
- join25("case", that.generateExpression(stmt.test, Precedence.Sequence, E_TTT)),
73073
+ join26("case", that.generateExpression(stmt.test, Precedence.Sequence, E_TTT)),
72984
73074
  ":"
72985
73075
  ];
72986
73076
  } else {
@@ -73028,9 +73118,9 @@ var require_escodegen = __commonJS({
73028
73118
  result.push(this.maybeBlock(stmt.consequent, S_TFFF));
73029
73119
  result = this.maybeBlockSuffix(stmt.consequent, result);
73030
73120
  if (stmt.alternate.type === Syntax.IfStatement) {
73031
- result = join25(result, ["else ", this.generateStatement(stmt.alternate, bodyFlags)]);
73121
+ result = join26(result, ["else ", this.generateStatement(stmt.alternate, bodyFlags)]);
73032
73122
  } else {
73033
- result = join25(result, join25("else", this.maybeBlock(stmt.alternate, bodyFlags)));
73123
+ result = join26(result, join26("else", this.maybeBlock(stmt.alternate, bodyFlags)));
73034
73124
  }
73035
73125
  } else {
73036
73126
  result.push(this.maybeBlock(stmt.consequent, bodyFlags));
@@ -73131,7 +73221,7 @@ var require_escodegen = __commonJS({
73131
73221
  },
73132
73222
  ReturnStatement: function(stmt, flags) {
73133
73223
  if (stmt.argument) {
73134
- return [join25(
73224
+ return [join26(
73135
73225
  "return",
73136
73226
  this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
73137
73227
  ), this.semicolon(flags)];
@@ -73220,14 +73310,14 @@ var require_escodegen = __commonJS({
73220
73310
  if (leftSource.charCodeAt(leftSource.length - 1) === 47 && esutils.code.isIdentifierPartES5(expr.operator.charCodeAt(0))) {
73221
73311
  result = [fragment, noEmptySpace(), expr.operator];
73222
73312
  } else {
73223
- result = join25(fragment, expr.operator);
73313
+ result = join26(fragment, expr.operator);
73224
73314
  }
73225
73315
  fragment = this.generateExpression(expr.right, rightPrecedence, flags);
73226
73316
  if (expr.operator === "/" && fragment.toString().charAt(0) === "/" || expr.operator.slice(-1) === "<" && fragment.toString().slice(0, 3) === "!--") {
73227
73317
  result.push(noEmptySpace());
73228
73318
  result.push(fragment);
73229
73319
  } else {
73230
- result = join25(result, fragment);
73320
+ result = join26(result, fragment);
73231
73321
  }
73232
73322
  if (expr.operator === "in" && !(flags & F_ALLOW_IN)) {
73233
73323
  return ["(", result, ")"];
@@ -73267,7 +73357,7 @@ var require_escodegen = __commonJS({
73267
73357
  var result, length, i, iz, itemFlags;
73268
73358
  length = expr["arguments"].length;
73269
73359
  itemFlags = flags & F_ALLOW_UNPARATH_NEW && !parentheses && length === 0 ? E_TFT : E_TFF;
73270
- result = join25(
73360
+ result = join26(
73271
73361
  "new",
73272
73362
  this.generateExpression(expr.callee, Precedence.New, itemFlags)
73273
73363
  );
@@ -73317,11 +73407,11 @@ var require_escodegen = __commonJS({
73317
73407
  var result, fragment, rightCharCode, leftSource, leftCharCode;
73318
73408
  fragment = this.generateExpression(expr.argument, Precedence.Unary, E_TTT);
73319
73409
  if (space === "") {
73320
- result = join25(expr.operator, fragment);
73410
+ result = join26(expr.operator, fragment);
73321
73411
  } else {
73322
73412
  result = [expr.operator];
73323
73413
  if (expr.operator.length > 2) {
73324
- result = join25(result, fragment);
73414
+ result = join26(result, fragment);
73325
73415
  } else {
73326
73416
  leftSource = toSourceNodeWhenNeeded(result).toString();
73327
73417
  leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
@@ -73344,7 +73434,7 @@ var require_escodegen = __commonJS({
73344
73434
  result = "yield";
73345
73435
  }
73346
73436
  if (expr.argument) {
73347
- result = join25(
73437
+ result = join26(
73348
73438
  result,
73349
73439
  this.generateExpression(expr.argument, Precedence.Yield, E_TTT)
73350
73440
  );
@@ -73352,7 +73442,7 @@ var require_escodegen = __commonJS({
73352
73442
  return parenthesize(result, Precedence.Yield, precedence);
73353
73443
  },
73354
73444
  AwaitExpression: function(expr, precedence, flags) {
73355
- var result = join25(
73445
+ var result = join26(
73356
73446
  expr.all ? "await*" : "await",
73357
73447
  this.generateExpression(expr.argument, Precedence.Await, E_TTT)
73358
73448
  );
@@ -73435,11 +73525,11 @@ var require_escodegen = __commonJS({
73435
73525
  var result, fragment;
73436
73526
  result = ["class"];
73437
73527
  if (expr.id) {
73438
- result = join25(result, this.generateExpression(expr.id, Precedence.Sequence, E_TTT));
73528
+ result = join26(result, this.generateExpression(expr.id, Precedence.Sequence, E_TTT));
73439
73529
  }
73440
73530
  if (expr.superClass) {
73441
- fragment = join25("extends", this.generateExpression(expr.superClass, Precedence.Unary, E_TTT));
73442
- result = join25(result, fragment);
73531
+ fragment = join26("extends", this.generateExpression(expr.superClass, Precedence.Unary, E_TTT));
73532
+ result = join26(result, fragment);
73443
73533
  }
73444
73534
  result.push(space);
73445
73535
  result.push(this.generateStatement(expr.body, S_TFFT));
@@ -73454,7 +73544,7 @@ var require_escodegen = __commonJS({
73454
73544
  }
73455
73545
  if (expr.kind === "get" || expr.kind === "set") {
73456
73546
  fragment = [
73457
- join25(expr.kind, this.generatePropertyKey(expr.key, expr.computed)),
73547
+ join26(expr.kind, this.generatePropertyKey(expr.key, expr.computed)),
73458
73548
  this.generateFunctionBody(expr.value)
73459
73549
  ];
73460
73550
  } else {
@@ -73464,7 +73554,7 @@ var require_escodegen = __commonJS({
73464
73554
  this.generateFunctionBody(expr.value)
73465
73555
  ];
73466
73556
  }
73467
- return join25(result, fragment);
73557
+ return join26(result, fragment);
73468
73558
  },
73469
73559
  Property: function(expr, precedence, flags) {
73470
73560
  if (expr.kind === "get" || expr.kind === "set") {
@@ -73659,7 +73749,7 @@ var require_escodegen = __commonJS({
73659
73749
  for (i = 0, iz = expr.blocks.length; i < iz; ++i) {
73660
73750
  fragment = that.generateExpression(expr.blocks[i], Precedence.Sequence, E_TTT);
73661
73751
  if (i > 0 || extra.moz.comprehensionExpressionStartsWithAssignment) {
73662
- result = join25(result, fragment);
73752
+ result = join26(result, fragment);
73663
73753
  } else {
73664
73754
  result.push(fragment);
73665
73755
  }
@@ -73667,13 +73757,13 @@ var require_escodegen = __commonJS({
73667
73757
  });
73668
73758
  }
73669
73759
  if (expr.filter) {
73670
- result = join25(result, "if" + space);
73760
+ result = join26(result, "if" + space);
73671
73761
  fragment = this.generateExpression(expr.filter, Precedence.Sequence, E_TTT);
73672
- result = join25(result, ["(", fragment, ")"]);
73762
+ result = join26(result, ["(", fragment, ")"]);
73673
73763
  }
73674
73764
  if (!extra.moz.comprehensionExpressionStartsWithAssignment) {
73675
73765
  fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
73676
- result = join25(result, fragment);
73766
+ result = join26(result, fragment);
73677
73767
  }
73678
73768
  result.push(expr.type === Syntax.GeneratorExpression ? ")" : "]");
73679
73769
  return result;
@@ -73689,8 +73779,8 @@ var require_escodegen = __commonJS({
73689
73779
  } else {
73690
73780
  fragment = this.generateExpression(expr.left, Precedence.Call, E_TTT);
73691
73781
  }
73692
- fragment = join25(fragment, expr.of ? "of" : "in");
73693
- fragment = join25(fragment, this.generateExpression(expr.right, Precedence.Sequence, E_TTT));
73782
+ fragment = join26(fragment, expr.of ? "of" : "in");
73783
+ fragment = join26(fragment, this.generateExpression(expr.right, Precedence.Sequence, E_TTT));
73694
73784
  return ["for" + space + "(", fragment, ")"];
73695
73785
  },
73696
73786
  SpreadElement: function(expr, precedence, flags) {
@@ -101249,8 +101339,8 @@ function findChromePath() {
101249
101339
  for (const p8 of paths) {
101250
101340
  if (p8) {
101251
101341
  try {
101252
- const { existsSync: existsSync21 } = __require("node:fs");
101253
- if (existsSync21(p8)) return p8;
101342
+ const { existsSync: existsSync22 } = __require("node:fs");
101343
+ if (existsSync22(p8)) return p8;
101254
101344
  } catch {
101255
101345
  }
101256
101346
  }
@@ -101265,8 +101355,8 @@ function findChromePath() {
101265
101355
  ];
101266
101356
  for (const p8 of paths) {
101267
101357
  try {
101268
- const { existsSync: existsSync21 } = __require("node:fs");
101269
- if (existsSync21(p8)) return p8;
101358
+ const { existsSync: existsSync22 } = __require("node:fs");
101359
+ if (existsSync22(p8)) return p8;
101270
101360
  } catch {
101271
101361
  }
101272
101362
  }
@@ -101281,8 +101371,8 @@ function findChromePath() {
101281
101371
  ];
101282
101372
  for (const p8 of linuxPaths) {
101283
101373
  try {
101284
- const { existsSync: existsSync21 } = __require("node:fs");
101285
- if (existsSync21(p8)) return p8;
101374
+ const { existsSync: existsSync22 } = __require("node:fs");
101375
+ if (existsSync22(p8)) return p8;
101286
101376
  } catch {
101287
101377
  }
101288
101378
  }
@@ -102464,8 +102554,19 @@ async function fileListHandler(input, context3) {
102464
102554
  return { output: `Failed to list directory: ${message}`, isError: true };
102465
102555
  }
102466
102556
  }
102557
+ function htmlToText(html) {
102558
+ return html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<noscript[^>]*>[\s\S]*?<\/noscript>/gi, "").replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n\n").replace(/<\/div>/gi, "\n").replace(/<\/h[1-6]>/gi, "\n\n").replace(/<h[1-6][^>]*>/gi, "\n\n## ").replace(/<li>/gi, "- ").replace(/<\/li>/gi, "\n").replace(/<\/tr>/gi, "\n").replace(/<\/td>/gi, " | ").replace(/<\/th>/gi, " | ").replace(/<hr[^>]*>/gi, "\n---\n").replace(/<[^>]*>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#039;/g, "'").replace(/&#x27;/g, "'").replace(/&apos;/g, "'").replace(/&nbsp;/g, " ").replace(/&mdash;/g, "\u2014").replace(/&ndash;/g, "\u2013").replace(/&hellip;/g, "...").replace(/&laquo;/g, "\xAB").replace(/&raquo;/g, "\xBB").replace(/&#(\d+);/g, (_m, code) => String.fromCharCode(parseInt(code, 10))).replace(/&#x([0-9a-fA-F]+);/g, (_m, code) => String.fromCharCode(parseInt(code, 16))).replace(/\n{3,}/g, "\n\n").replace(/[ \t]+/g, " ").replace(/^ +/gm, "").trim();
102559
+ }
102560
+ function extractTitle(html) {
102561
+ const match = html.match(/<title[^>]*>(.*?)<\/title>/i);
102562
+ return match ? match[1].replace(/<[^>]*>/g, "").trim() : "";
102563
+ }
102564
+ function extractMetaDescription(html) {
102565
+ const match = html.match(/<meta[^>]*name=["']description["'][^>]*content=["'](.*?)["'][^>]*>/i) || html.match(/<meta[^>]*content=["'](.*?)["'][^>]*name=["']description["'][^>]*>/i);
102566
+ return match ? match[1].trim() : "";
102567
+ }
102467
102568
  async function webFetchHandler(input, context3) {
102468
- const { url, method, headers, body, timeout: timeout2 } = input;
102569
+ const { url, method, headers, body, timeout: timeout2, raw } = input;
102469
102570
  const timeoutMs = timeout2 ?? 15e3;
102470
102571
  try {
102471
102572
  const controller = new AbortController();
@@ -102473,11 +102574,17 @@ async function webFetchHandler(input, context3) {
102473
102574
  if (context3.signal) {
102474
102575
  context3.signal.addEventListener("abort", () => controller.abort(), { once: true });
102475
102576
  }
102577
+ const defaultHeaders = {
102578
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
102579
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,application/json;q=0.8,*/*;q=0.7",
102580
+ "Accept-Language": "en-US,en;q=0.9,fr;q=0.8"
102581
+ };
102476
102582
  const resp = await fetch(url, {
102477
102583
  method: method ?? "GET",
102478
- headers: headers ?? {},
102584
+ headers: { ...defaultHeaders, ...headers ?? {} },
102479
102585
  body: body ?? void 0,
102480
- signal: controller.signal
102586
+ signal: controller.signal,
102587
+ redirect: "follow"
102481
102588
  });
102482
102589
  clearTimeout(timer2);
102483
102590
  const contentType = resp.headers.get("content-type") ?? "";
@@ -102486,9 +102593,22 @@ async function webFetchHandler(input, context3) {
102486
102593
  const json = await resp.json();
102487
102594
  responseBody = JSON.stringify(json, null, 2);
102488
102595
  } else {
102489
- responseBody = await resp.text();
102596
+ const rawText = await resp.text();
102597
+ if (!raw && contentType.includes("text/html")) {
102598
+ const title = extractTitle(rawText);
102599
+ const description = extractMetaDescription(rawText);
102600
+ const textContent = htmlToText(rawText);
102601
+ const parts = [];
102602
+ if (title) parts.push(`# ${title}`);
102603
+ if (description) parts.push(`> ${description}`);
102604
+ if (parts.length > 0) parts.push("");
102605
+ parts.push(textContent);
102606
+ responseBody = parts.join("\n");
102607
+ } else {
102608
+ responseBody = rawText;
102609
+ }
102490
102610
  }
102491
- const maxLen = 1e5;
102611
+ const maxLen = !raw && contentType.includes("text/html") ? 1e4 : 1e5;
102492
102612
  if (responseBody.length > maxLen) {
102493
102613
  responseBody = responseBody.slice(0, maxLen) + `
102494
102614
 
@@ -102497,6 +102617,7 @@ async function webFetchHandler(input, context3) {
102497
102617
  const output = [
102498
102618
  `HTTP ${resp.status} ${resp.statusText}`,
102499
102619
  `Content-Type: ${contentType}`,
102620
+ `URL: ${resp.url}`,
102500
102621
  "",
102501
102622
  responseBody
102502
102623
  ].join("\n");
@@ -102509,6 +102630,130 @@ async function webFetchHandler(input, context3) {
102509
102630
  return { output: `Fetch failed: ${message}`, isError: true };
102510
102631
  }
102511
102632
  }
102633
+ async function webSearchHandler(input, context3) {
102634
+ const { query, engine, maxResults } = input;
102635
+ const max = maxResults || 5;
102636
+ const searchEngine = engine || "duckduckgo";
102637
+ let searchUrl;
102638
+ switch (searchEngine) {
102639
+ case "google":
102640
+ searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}&num=${max}`;
102641
+ break;
102642
+ case "bing":
102643
+ searchUrl = `https://www.bing.com/search?q=${encodeURIComponent(query)}&count=${max}`;
102644
+ break;
102645
+ case "duckduckgo":
102646
+ default:
102647
+ searchUrl = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
102648
+ break;
102649
+ }
102650
+ try {
102651
+ const controller = new AbortController();
102652
+ const timer2 = setTimeout(() => controller.abort(), 15e3);
102653
+ if (context3.signal) {
102654
+ context3.signal.addEventListener("abort", () => controller.abort(), { once: true });
102655
+ }
102656
+ const resp = await fetch(searchUrl, {
102657
+ headers: {
102658
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
102659
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
102660
+ "Accept-Language": "en-US,en;q=0.9,fr;q=0.8"
102661
+ },
102662
+ signal: controller.signal,
102663
+ redirect: "follow"
102664
+ });
102665
+ clearTimeout(timer2);
102666
+ if (!resp.ok) {
102667
+ return { output: `Search failed: HTTP ${resp.status}. Try a different search engine with engine:"google" or engine:"bing".`, isError: true };
102668
+ }
102669
+ const html = await resp.text();
102670
+ const results = [];
102671
+ if (searchEngine === "duckduckgo") {
102672
+ const linkRegex = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi;
102673
+ const snippetRegex = /<a[^>]*class="result__snippet"[^>]*>(.*?)<\/a>/gi;
102674
+ let linkMatch;
102675
+ const links = [];
102676
+ while ((linkMatch = linkRegex.exec(html)) !== null && links.length < max) {
102677
+ let url = linkMatch[1];
102678
+ const uddg = url.match(/uddg=([^&]*)/);
102679
+ if (uddg) url = decodeURIComponent(uddg[1]);
102680
+ const title = linkMatch[2].replace(/<[^>]*>/g, "").trim();
102681
+ if (url.startsWith("http")) {
102682
+ links.push({ url, title });
102683
+ }
102684
+ }
102685
+ let snippetMatch;
102686
+ const snippets = [];
102687
+ while ((snippetMatch = snippetRegex.exec(html)) !== null) {
102688
+ snippets.push(snippetMatch[1].replace(/<[^>]*>/g, "").trim());
102689
+ }
102690
+ for (let i = 0; i < links.length; i++) {
102691
+ results.push({
102692
+ title: links[i].title,
102693
+ url: links[i].url,
102694
+ snippet: snippets[i] || ""
102695
+ });
102696
+ }
102697
+ } else if (searchEngine === "google") {
102698
+ const resultBlockRegex = /<div class="[^"]*g[^"]*"[^>]*>[\s\S]*?<a[^>]*href="(https?:\/\/[^"]*)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?(?:<span[^>]*>([\s\S]*?)<\/span>)?/gi;
102699
+ let blockMatch;
102700
+ while ((blockMatch = resultBlockRegex.exec(html)) !== null && results.length < max) {
102701
+ const url = blockMatch[1];
102702
+ const title = blockMatch[2].replace(/<[^>]*>/g, "").trim();
102703
+ const snippet = (blockMatch[3] || "").replace(/<[^>]*>/g, "").trim();
102704
+ if (title.length > 3 && !url.includes("google.com")) {
102705
+ results.push({ title, url, snippet });
102706
+ }
102707
+ }
102708
+ } else if (searchEngine === "bing") {
102709
+ const bingRegex = /<li class="b_algo"[^>]*>[\s\S]*?<a[^>]*href="(https?:\/\/[^"]*)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?<p[^>]*>([\s\S]*?)<\/p>/gi;
102710
+ let bingMatch;
102711
+ while ((bingMatch = bingRegex.exec(html)) !== null && results.length < max) {
102712
+ const url = bingMatch[1];
102713
+ const title = bingMatch[2].replace(/<[^>]*>/g, "").trim();
102714
+ const snippet = bingMatch[3].replace(/<[^>]*>/g, "").trim();
102715
+ if (title.length > 3) {
102716
+ results.push({ title, url, snippet });
102717
+ }
102718
+ }
102719
+ }
102720
+ if (results.length === 0) {
102721
+ const anyLinkRegex = /<a[^>]*href="(https?:\/\/[^"]*)"[^>]*>(.*?)<\/a>/gi;
102722
+ let anyMatch;
102723
+ while ((anyMatch = anyLinkRegex.exec(html)) !== null && results.length < max) {
102724
+ const url = anyMatch[1];
102725
+ const title = anyMatch[2].replace(/<[^>]*>/g, "").trim();
102726
+ if (title.length > 5 && !url.includes("duckduckgo.com") && !url.includes("google.com") && !url.includes("bing.com")) {
102727
+ results.push({ title, url, snippet: "" });
102728
+ }
102729
+ }
102730
+ }
102731
+ if (results.length === 0) {
102732
+ return {
102733
+ output: `No results found for "${query}". Try a different query or use a different engine (engine:"google" or engine:"bing"). You can also try web_fetch directly on a relevant URL.`,
102734
+ isError: false
102735
+ };
102736
+ }
102737
+ const formatted = results.map(
102738
+ (r, i) => `${i + 1}. ${r.title}
102739
+ ${r.url}
102740
+ ${r.snippet}`
102741
+ ).join("\n\n");
102742
+ return {
102743
+ output: `Search results for "${query}":
102744
+
102745
+ ${formatted}
102746
+
102747
+ Use web_fetch on any URL above to get the full page content.`,
102748
+ isError: false
102749
+ };
102750
+ } catch (err) {
102751
+ if (err.name === "AbortError") {
102752
+ return { output: `Search timed out for "${query}". Try a simpler query or different engine.`, isError: true };
102753
+ }
102754
+ return { output: `Search error: ${err.message}. Try a different search engine with engine:"google" or engine:"bing".`, isError: true };
102755
+ }
102756
+ }
102512
102757
  async function messageSendHandler(input, context3) {
102513
102758
  const { to, subject, body } = input;
102514
102759
  try {
@@ -102610,6 +102855,7 @@ function registerBuiltinTools(registry) {
102610
102855
  registry.register(fileWriteTool);
102611
102856
  registry.register(fileListTool);
102612
102857
  registry.register(webFetchTool);
102858
+ registry.register(webSearchTool);
102613
102859
  registry.register(messageSendTool);
102614
102860
  registry.register(messageReadTool);
102615
102861
  registry.register(handoffTool);
@@ -102626,7 +102872,7 @@ function registerBrowserTools(registry, config2) {
102626
102872
  }
102627
102873
  return manager;
102628
102874
  }
102629
- var shellExecSchema, sandboxCache, DANGEROUS_METACHARACTERS, shellExecTool, fileReadSchema, fileReadTool, fileWriteSchema, fileWriteTool, fileListSchema, fileListTool, webFetchSchema, webFetchTool, messageSendSchema, messageSendTool, messageReadSchema, messageReadTool, handoffSchema, handoffTool, undoSchema;
102875
+ var shellExecSchema, sandboxCache, DANGEROUS_METACHARACTERS, shellExecTool, fileReadSchema, fileReadTool, fileWriteSchema, fileWriteTool, fileListSchema, fileListTool, webFetchSchema, webFetchTool, webSearchSchema, webSearchTool, messageSendSchema, messageSendTool, messageReadSchema, messageReadTool, handoffSchema, handoffTool, undoSchema;
102630
102876
  var init_builtin = __esm({
102631
102877
  "../agent/src/tools/builtin.ts"() {
102632
102878
  "use strict";
@@ -102692,14 +102938,26 @@ var init_builtin = __esm({
102692
102938
  method: z26.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).optional().describe("HTTP method (default: GET)"),
102693
102939
  headers: z26.record(z26.string()).optional().describe("HTTP headers"),
102694
102940
  body: z26.string().optional().describe("Request body (for POST/PUT/PATCH)"),
102695
- timeout: z26.number().optional().describe("Timeout in milliseconds (default: 15000)")
102941
+ timeout: z26.number().optional().describe("Timeout in milliseconds (default: 15000)"),
102942
+ raw: z26.boolean().optional().describe("If true, return raw response without HTML-to-text conversion (default: false)")
102696
102943
  });
102697
102944
  webFetchTool = {
102698
102945
  name: "web_fetch",
102699
- description: "Make an HTTP request (GET, POST, PUT, PATCH, DELETE) and return the response.",
102946
+ description: "Fetch a URL and return its content. For HTML pages, automatically converts to readable text. Use raw:true to get the original HTML. Useful for reading articles, documentation, API responses, etc.",
102700
102947
  inputSchema: webFetchSchema,
102701
102948
  handler: webFetchHandler
102702
102949
  };
102950
+ webSearchSchema = z26.object({
102951
+ query: z26.string().describe("The search query"),
102952
+ engine: z26.string().optional().describe("Search engine: google, duckduckgo, bing. Default: duckduckgo"),
102953
+ maxResults: z26.number().optional().describe("Max results to return. Default: 5")
102954
+ });
102955
+ webSearchTool = {
102956
+ name: "web_search",
102957
+ description: "Search the internet using DuckDuckGo, Google, or Bing. Returns titles, URLs, and snippets. Use web_fetch on result URLs to read full page content. Always try this FIRST when you need to find information online.",
102958
+ inputSchema: webSearchSchema,
102959
+ handler: webSearchHandler
102960
+ };
102703
102961
  messageSendSchema = z26.object({
102704
102962
  to: z26.string().describe('Target agent ID or name. Use "*" to broadcast to all agents.'),
102705
102963
  subject: z26.string().describe("Message subject \u2014 a short label describing the purpose."),
@@ -111354,6 +111612,7 @@ __export(src_exports3, {
111354
111612
  shellExecTool: () => shellExecTool,
111355
111613
  unloadPlugin: () => unloadPlugin,
111356
111614
  webFetchTool: () => webFetchTool,
111615
+ webSearchTool: () => webSearchTool,
111357
111616
  withinBudget: () => withinBudget,
111358
111617
  withinDuration: () => withinDuration,
111359
111618
  wrapExternalData: () => wrapExternalData,
@@ -144297,22 +144556,64 @@ function chatRoutes(store) {
144297
144556
  initialMessages.push({ role: msg.role, content: msg.content });
144298
144557
  }
144299
144558
  }
144300
- const systemPromptPrefix = `You are a Nestor AI agent with full access to the Nestor platform.
144301
-
144302
- Available system tools:
144303
- - nestor_list_agents, nestor_create_agent, nestor_run_agent, nestor_delete_agent
144304
- - nestor_list_workflows, nestor_create_workflow
144305
- - nestor_search_memory, nestor_store_memory
144306
- - nestor_list_skills, nestor_list_plans, nestor_list_guardrails, nestor_create_guardrail
144307
- - nestor_system_info, nestor_read_config
144308
-
144309
- You also have access to:
144310
- - shell_exec: Execute shell commands on the local machine
144311
- - file_read/file_write/file_list: Read, write, and list files
144312
- - web_fetch: Fetch web pages
144313
-
144314
- When asked to create agents, workflows, or manage the platform, use the nestor_* tools.
144315
- When asked to work with code or files, use shell_exec and file_* tools.
144559
+ const toolCount = registry.size;
144560
+ const systemPromptPrefix = `You are a Nestor AI agent \u2014 a proactive, resourceful assistant that NEVER gives up.
144561
+
144562
+ ## Core Behavior Rules
144563
+ 1. NEVER say "I can't do that" or "I don't have the ability to" \u2014 ALWAYS find a way using your tools
144564
+ 2. NEVER ask for permission to use tools \u2014 just use them
144565
+ 3. If one approach fails, try another approach immediately
144566
+ 4. Break complex tasks into steps and execute them one by one
144567
+ 5. When searching for information, use web_search first, then web_fetch to read specific pages
144568
+ 6. When asked to do something on the system, use shell_exec and file_* tools
144569
+ 7. When asked to manage the Nestor platform, use nestor_* tools
144570
+ 8. Always show your work \u2014 explain what you're doing as you do it
144571
+ 9. If you encounter an error, analyze it and try a different approach
144572
+ 10. Complete tasks from 0% to 100% \u2014 don't stop halfway
144573
+
144574
+ ## Problem-Solving Strategy
144575
+ - Need information? \u2192 web_search \u2192 web_fetch the best results
144576
+ - Need to install something? \u2192 shell_exec with npm/pip/apt/choco
144577
+ - Need to transform data? \u2192 shell_exec with built-in tools (jq, sed, awk, etc.) or write a script
144578
+ - Need to check something? \u2192 Use the right tool, don't guess
144579
+ - Something failed? \u2192 Read the error, fix it, try again. Never give up after one failure.
144580
+ - Don't know how? \u2192 web_search for the answer, then do it
144581
+
144582
+ ## Available Tools (${toolCount} tools)
144583
+
144584
+ ### Internet & Web
144585
+ - **web_search**: Search the internet (DuckDuckGo/Google/Bing). Use this FIRST for any information lookup.
144586
+ - **web_fetch**: Fetch and read a specific URL. Automatically converts HTML to readable text.
144587
+
144588
+ ### System & Files
144589
+ - **shell_exec**: Execute shell commands. Use for builds, tests, git, installs, data processing, anything.
144590
+ - **file_read**: Read file contents.
144591
+ - **file_write**: Write/create files. Can append.
144592
+ - **file_list**: List directory contents.
144593
+
144594
+ ### Nestor Platform
144595
+ - **nestor_create_agent**: Create new AI agents
144596
+ - **nestor_list_agents**: List all agents
144597
+ - **nestor_run_agent**: Run an agent with a prompt
144598
+ - **nestor_delete_agent**: Delete an agent
144599
+ - **nestor_create_workflow**: Create automation workflows
144600
+ - **nestor_list_workflows**: List all workflows
144601
+ - **nestor_search_memory**: Search stored knowledge
144602
+ - **nestor_store_memory**: Remember information for later
144603
+ - **nestor_list_skills**: List installed skills
144604
+ - **nestor_list_plans**: List execution plans
144605
+ - **nestor_list_guardrails / nestor_create_guardrail**: Manage safety rules
144606
+ - **nestor_system_info**: Check platform status
144607
+ - **nestor_read_config**: Read configuration
144608
+
144609
+ ### Communication
144610
+ - **message_send**: Send messages to other agents
144611
+ - **message_read**: Read incoming messages
144612
+ - **handoff**: Hand off to human when truly stuck (last resort)
144613
+
144614
+ ## Language
144615
+ Respond in the same language the user writes in. If they write in French, respond in French.
144616
+ If they write in English, respond in English.
144316
144617
  `;
144317
144618
  const agentDescription = agent.description || "You are a helpful AI assistant.";
144318
144619
  const runtime = new AgentRuntime({
@@ -144516,10 +144817,384 @@ var init_providers = __esm({
144516
144817
  }
144517
144818
  });
144518
144819
 
144820
+ // ../server/src/services/key-vault.ts
144821
+ import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes7, scryptSync as scryptSync2 } from "node:crypto";
144822
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync8, existsSync as existsSync11, mkdirSync as mkdirSync6 } from "node:fs";
144823
+ import { join as join17, dirname as dirname8 } from "node:path";
144824
+ import { homedir as homedir5 } from "node:os";
144825
+ function getVaultKey() {
144826
+ if (!existsSync11(KEY_FILE)) {
144827
+ const dir = dirname8(KEY_FILE);
144828
+ if (!existsSync11(dir)) {
144829
+ mkdirSync6(dir, { recursive: true });
144830
+ }
144831
+ const masterSecret = randomBytes7(32).toString("hex");
144832
+ writeFileSync8(KEY_FILE, masterSecret, { mode: 384 });
144833
+ }
144834
+ const secret = readFileSync16(KEY_FILE, "utf-8").trim();
144835
+ return scryptSync2(secret, "nestor-vault-salt", 32);
144836
+ }
144837
+ function encryptApiKey(plainKey) {
144838
+ const key = getVaultKey();
144839
+ const iv = randomBytes7(16);
144840
+ const cipher = createCipheriv2(ALGORITHM2, key, iv);
144841
+ let encrypted = cipher.update(plainKey, "utf8", "hex");
144842
+ encrypted += cipher.final("hex");
144843
+ const authTag = cipher.getAuthTag().toString("hex");
144844
+ return `${iv.toString("hex")}:${authTag}:${encrypted}`;
144845
+ }
144846
+ function decryptApiKey(encryptedStr) {
144847
+ const key = getVaultKey();
144848
+ const parts = encryptedStr.split(":");
144849
+ if (parts.length !== 3) {
144850
+ throw new Error("Invalid encrypted key format");
144851
+ }
144852
+ const [ivHex, authTagHex, encrypted] = parts;
144853
+ const iv = Buffer.from(ivHex, "hex");
144854
+ const authTag = Buffer.from(authTagHex, "hex");
144855
+ const decipher = createDecipheriv2(ALGORITHM2, key, iv);
144856
+ decipher.setAuthTag(authTag);
144857
+ let decrypted = decipher.update(encrypted, "hex", "utf8");
144858
+ decrypted += decipher.final("utf8");
144859
+ return decrypted;
144860
+ }
144861
+ function maskKey(key) {
144862
+ if (key.length <= 8) return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
144863
+ return key.slice(0, 4) + "\u2022".repeat(Math.min(key.length - 8, 20)) + key.slice(-4);
144864
+ }
144865
+ function loadApiKeysIntoEnv(store, tenantId = "default") {
144866
+ try {
144867
+ if (!store.listApiKeysRaw) return;
144868
+ const keys = store.listApiKeysRaw(tenantId);
144869
+ for (const { provider, keyEncrypted } of keys) {
144870
+ const envVar = PROVIDER_ENV_MAP[provider];
144871
+ if (envVar && !process.env[envVar]) {
144872
+ try {
144873
+ process.env[envVar] = decryptApiKey(keyEncrypted);
144874
+ } catch {
144875
+ }
144876
+ }
144877
+ }
144878
+ } catch {
144879
+ }
144880
+ }
144881
+ var ALGORITHM2, KEY_FILE, PROVIDER_ENV_MAP;
144882
+ var init_key_vault = __esm({
144883
+ "../server/src/services/key-vault.ts"() {
144884
+ "use strict";
144885
+ ALGORITHM2 = "aes-256-gcm";
144886
+ KEY_FILE = join17(homedir5(), ".nestor", ".vault-key");
144887
+ PROVIDER_ENV_MAP = {
144888
+ claude: "ANTHROPIC_API_KEY",
144889
+ openai: "OPENAI_API_KEY",
144890
+ gemini: "GEMINI_API_KEY",
144891
+ grok: "XAI_API_KEY",
144892
+ mistral: "MISTRAL_API_KEY",
144893
+ ollama: ""
144894
+ // Ollama doesn't need an API key
144895
+ };
144896
+ }
144897
+ });
144898
+
144899
+ // ../server/src/routes/api-keys.ts
144900
+ import { Router as Router39 } from "express";
144901
+ function isValidProvider(p8) {
144902
+ return VALID_PROVIDERS.includes(p8);
144903
+ }
144904
+ async function testClaude(apiKey) {
144905
+ try {
144906
+ const res = await fetch("https://api.anthropic.com/v1/messages", {
144907
+ method: "POST",
144908
+ headers: {
144909
+ "Content-Type": "application/json",
144910
+ "x-api-key": apiKey,
144911
+ "anthropic-version": "2023-06-01"
144912
+ },
144913
+ body: JSON.stringify({
144914
+ model: "claude-sonnet-4-20250514",
144915
+ max_tokens: 1,
144916
+ messages: [{ role: "user", content: "Hi" }]
144917
+ })
144918
+ });
144919
+ if (res.ok) {
144920
+ return { valid: true, message: "API key is valid. Claude is accessible." };
144921
+ }
144922
+ const body = await res.json().catch(() => ({}));
144923
+ const errMsg = body?.error?.message || res.statusText;
144924
+ if (res.status === 401) return { valid: false, message: `Invalid API key: ${errMsg}` };
144925
+ if (res.status === 429 || res.status === 400) {
144926
+ return { valid: true, message: `Key accepted (status ${res.status}: ${errMsg})` };
144927
+ }
144928
+ return { valid: false, message: `API error ${res.status}: ${errMsg}` };
144929
+ } catch (err) {
144930
+ return { valid: false, message: `Connection failed: ${err.message}` };
144931
+ }
144932
+ }
144933
+ async function testOpenAI(apiKey) {
144934
+ try {
144935
+ const res = await fetch("https://api.openai.com/v1/models", {
144936
+ headers: { Authorization: `Bearer ${apiKey}` }
144937
+ });
144938
+ if (res.ok) {
144939
+ const body = await res.json();
144940
+ const models = (body.data || []).slice(0, 10).map((m) => m.id);
144941
+ return { valid: true, message: "API key is valid.", models };
144942
+ }
144943
+ if (res.status === 401) return { valid: false, message: "Invalid API key." };
144944
+ return { valid: false, message: `API error ${res.status}: ${res.statusText}` };
144945
+ } catch (err) {
144946
+ return { valid: false, message: `Connection failed: ${err.message}` };
144947
+ }
144948
+ }
144949
+ async function testGemini(apiKey) {
144950
+ try {
144951
+ const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`);
144952
+ if (res.ok) {
144953
+ const body = await res.json();
144954
+ const models = (body.models || []).slice(0, 10).map((m) => m.name.replace("models/", ""));
144955
+ return { valid: true, message: "API key is valid.", models };
144956
+ }
144957
+ if (res.status === 400 || res.status === 403) {
144958
+ return { valid: false, message: "Invalid API key." };
144959
+ }
144960
+ return { valid: false, message: `API error ${res.status}: ${res.statusText}` };
144961
+ } catch (err) {
144962
+ return { valid: false, message: `Connection failed: ${err.message}` };
144963
+ }
144964
+ }
144965
+ async function testGrok(apiKey) {
144966
+ try {
144967
+ const res = await fetch("https://api.x.ai/v1/models", {
144968
+ headers: { Authorization: `Bearer ${apiKey}` }
144969
+ });
144970
+ if (res.ok) {
144971
+ const body = await res.json();
144972
+ const models = (body.data || []).slice(0, 10).map((m) => m.id);
144973
+ return { valid: true, message: "API key is valid.", models };
144974
+ }
144975
+ if (res.status === 401) return { valid: false, message: "Invalid API key." };
144976
+ return { valid: false, message: `API error ${res.status}: ${res.statusText}` };
144977
+ } catch (err) {
144978
+ return { valid: false, message: `Connection failed: ${err.message}` };
144979
+ }
144980
+ }
144981
+ async function testMistral(apiKey) {
144982
+ try {
144983
+ const res = await fetch("https://api.mistral.ai/v1/models", {
144984
+ headers: { Authorization: `Bearer ${apiKey}` }
144985
+ });
144986
+ if (res.ok) {
144987
+ const body = await res.json();
144988
+ const models = (body.data || []).slice(0, 10).map((m) => m.id);
144989
+ return { valid: true, message: "API key is valid.", models };
144990
+ }
144991
+ if (res.status === 401) return { valid: false, message: "Invalid API key." };
144992
+ return { valid: false, message: `API error ${res.status}: ${res.statusText}` };
144993
+ } catch (err) {
144994
+ return { valid: false, message: `Connection failed: ${err.message}` };
144995
+ }
144996
+ }
144997
+ async function testOllama() {
144998
+ const baseUrl = process.env.OLLAMA_BASE_URL || "http://localhost:11434";
144999
+ try {
145000
+ const res = await fetch(`${baseUrl}/api/tags`);
145001
+ if (res.ok) {
145002
+ const body = await res.json();
145003
+ const models = (body.models || []).map((m) => m.name);
145004
+ return { valid: true, message: `Ollama is running at ${baseUrl}.`, models };
145005
+ }
145006
+ return { valid: false, message: `Ollama returned ${res.status}: ${res.statusText}` };
145007
+ } catch (err) {
145008
+ return { valid: false, message: `Ollama not reachable at ${baseUrl}: ${err.message}` };
145009
+ }
145010
+ }
145011
+ function apiKeyRoutes(db) {
145012
+ const router = Router39();
145013
+ router.get("/", (req, res) => {
145014
+ const tenantId = req.tenantId || "default";
145015
+ const dbKeys = db.listProviderKeys(tenantId);
145016
+ const dbProviders = new Set(dbKeys.map((k) => k.provider));
145017
+ const result = [];
145018
+ for (const provider of VALID_PROVIDERS) {
145019
+ const envVar = PROVIDER_ENV_MAP[provider];
145020
+ const dbEntry = dbKeys.find((k) => k.provider === provider);
145021
+ if (dbEntry) {
145022
+ try {
145023
+ const encrypted = db.getApiKey(provider, tenantId);
145024
+ const decrypted = encrypted ? decryptApiKey(encrypted) : "";
145025
+ result.push({
145026
+ provider,
145027
+ configured: true,
145028
+ source: "db",
145029
+ maskedKey: decrypted ? maskKey(decrypted) : null,
145030
+ label: dbEntry.label,
145031
+ createdAt: dbEntry.createdAt,
145032
+ updatedAt: dbEntry.updatedAt
145033
+ });
145034
+ } catch {
145035
+ result.push({
145036
+ provider,
145037
+ configured: true,
145038
+ source: "db",
145039
+ maskedKey: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
145040
+ label: dbEntry.label,
145041
+ createdAt: dbEntry.createdAt,
145042
+ updatedAt: dbEntry.updatedAt
145043
+ });
145044
+ }
145045
+ } else if (envVar && process.env[envVar]) {
145046
+ result.push({
145047
+ provider,
145048
+ configured: true,
145049
+ source: "env",
145050
+ maskedKey: maskKey(process.env[envVar]),
145051
+ label: "From environment variable",
145052
+ createdAt: null,
145053
+ updatedAt: null
145054
+ });
145055
+ } else if (provider === "ollama") {
145056
+ result.push({
145057
+ provider,
145058
+ configured: true,
145059
+ source: "none",
145060
+ maskedKey: null,
145061
+ label: "Local \u2014 no key required",
145062
+ createdAt: null,
145063
+ updatedAt: null
145064
+ });
145065
+ } else {
145066
+ result.push({
145067
+ provider,
145068
+ configured: false,
145069
+ source: "none",
145070
+ maskedKey: null,
145071
+ label: "",
145072
+ createdAt: null,
145073
+ updatedAt: null
145074
+ });
145075
+ }
145076
+ }
145077
+ res.json({ data: result });
145078
+ });
145079
+ router.post("/", (req, res) => {
145080
+ const tenantId = req.tenantId || "default";
145081
+ const { provider, key, label } = req.body;
145082
+ if (!provider || !isValidProvider(provider)) {
145083
+ res.status(400).json({
145084
+ error: { code: "INVALID_PROVIDER", message: `Provider must be one of: ${VALID_PROVIDERS.join(", ")}` }
145085
+ });
145086
+ return;
145087
+ }
145088
+ if (!key || typeof key !== "string" || key.trim().length === 0) {
145089
+ res.status(400).json({
145090
+ error: { code: "INVALID_KEY", message: "API key is required and must be a non-empty string." }
145091
+ });
145092
+ return;
145093
+ }
145094
+ const encrypted = encryptApiKey(key.trim());
145095
+ db.setApiKey(provider, encrypted, tenantId, label || "");
145096
+ const envVar = PROVIDER_ENV_MAP[provider];
145097
+ if (envVar) {
145098
+ process.env[envVar] = key.trim();
145099
+ }
145100
+ res.json({
145101
+ data: {
145102
+ provider,
145103
+ configured: true,
145104
+ source: "db",
145105
+ maskedKey: maskKey(key.trim()),
145106
+ label: label || ""
145107
+ },
145108
+ message: `API key for ${provider} saved successfully.`
145109
+ });
145110
+ });
145111
+ router.delete("/:provider", (req, res) => {
145112
+ const tenantId = req.tenantId || "default";
145113
+ const { provider } = req.params;
145114
+ if (!isValidProvider(provider)) {
145115
+ res.status(400).json({
145116
+ error: { code: "INVALID_PROVIDER", message: `Provider must be one of: ${VALID_PROVIDERS.join(", ")}` }
145117
+ });
145118
+ return;
145119
+ }
145120
+ const deleted = db.deleteProviderKey(provider, tenantId);
145121
+ const envVar = PROVIDER_ENV_MAP[provider];
145122
+ if (envVar) {
145123
+ delete process.env[envVar];
145124
+ }
145125
+ if (deleted) {
145126
+ res.json({ message: `API key for ${provider} removed.` });
145127
+ } else {
145128
+ res.status(404).json({
145129
+ error: { code: "NOT_FOUND", message: `No API key found for provider: ${provider}` }
145130
+ });
145131
+ }
145132
+ });
145133
+ router.get("/test/:provider", async (req, res) => {
145134
+ const tenantId = req.tenantId || "default";
145135
+ const { provider } = req.params;
145136
+ if (!isValidProvider(provider)) {
145137
+ res.status(400).json({
145138
+ error: { code: "INVALID_PROVIDER", message: `Provider must be one of: ${VALID_PROVIDERS.join(", ")}` }
145139
+ });
145140
+ return;
145141
+ }
145142
+ let apiKey = null;
145143
+ const encrypted = db.getApiKey(provider, tenantId);
145144
+ if (encrypted) {
145145
+ try {
145146
+ apiKey = decryptApiKey(encrypted);
145147
+ } catch {
145148
+ }
145149
+ }
145150
+ if (!apiKey) {
145151
+ const envVar = PROVIDER_ENV_MAP[provider];
145152
+ if (envVar && process.env[envVar]) {
145153
+ apiKey = process.env[envVar];
145154
+ }
145155
+ }
145156
+ if (provider === "ollama") {
145157
+ apiKey = "";
145158
+ }
145159
+ if (apiKey === null) {
145160
+ res.json({
145161
+ data: { valid: false, message: "No API key configured for this provider." }
145162
+ });
145163
+ return;
145164
+ }
145165
+ try {
145166
+ const testFn = TEST_FUNCTIONS[provider];
145167
+ const result = await testFn(apiKey);
145168
+ res.json({ data: result });
145169
+ } catch (err) {
145170
+ res.json({
145171
+ data: { valid: false, message: `Test failed: ${err.message}` }
145172
+ });
145173
+ }
145174
+ });
145175
+ return router;
145176
+ }
145177
+ var VALID_PROVIDERS, TEST_FUNCTIONS;
145178
+ var init_api_keys = __esm({
145179
+ "../server/src/routes/api-keys.ts"() {
145180
+ "use strict";
145181
+ init_key_vault();
145182
+ VALID_PROVIDERS = ["claude", "openai", "gemini", "grok", "mistral", "ollama"];
145183
+ TEST_FUNCTIONS = {
145184
+ claude: testClaude,
145185
+ openai: testOpenAI,
145186
+ gemini: testGemini,
145187
+ grok: testGrok,
145188
+ mistral: testMistral,
145189
+ ollama: () => testOllama()
145190
+ };
145191
+ }
145192
+ });
145193
+
144519
145194
  // ../server/src/app.ts
144520
145195
  import express from "express";
144521
- import { resolve as resolve13, join as join17 } from "node:path";
144522
- import { existsSync as existsSync11 } from "node:fs";
145196
+ import { resolve as resolve13, join as join18 } from "node:path";
145197
+ import { existsSync as existsSync12 } from "node:fs";
144523
145198
  import compression from "compression";
144524
145199
  import cors from "cors";
144525
145200
  function getStudioState() {
@@ -144539,6 +145214,10 @@ function createApp(config2) {
144539
145214
  const anomalyDetector = new AnomalyDetector();
144540
145215
  const incidentResponder = new IncidentResponder(config2.db, anomalyDetector, auditLogger);
144541
145216
  const securityCtx = { anomalyDetector, auditLogger, incidentResponder };
145217
+ try {
145218
+ loadApiKeysIntoEnv(config2.db);
145219
+ } catch {
145220
+ }
144542
145221
  const corsOrigins = config2.corsConfig?.allowedOrigins ?? [];
144543
145222
  const corsOptions = {
144544
145223
  origin: (origin, callback) => {
@@ -144673,6 +145352,7 @@ function createApp(config2) {
144673
145352
  app.use("/api/execution", executionRoutes(authService));
144674
145353
  app.use("/api/chat", chatRoutes(config2.db));
144675
145354
  app.use("/api/providers", providerRoutes());
145355
+ app.use("/api/keys", apiKeyRoutes(config2.db));
144676
145356
  app.use("/api/audit", auditRoutes(config2.db));
144677
145357
  app.use("/api/admin", adminMiddleware(config2.apiKey), adminRoutes(config2.db));
144678
145358
  app.get("/api/docs/openapi.json", (_req, res) => {
@@ -144738,7 +145418,7 @@ function createApp(config2) {
144738
145418
  }
144739
145419
  for (const dir of candidateDirs) {
144740
145420
  try {
144741
- if (existsSync11(join17(dir, "index.html"))) {
145421
+ if (existsSync12(join18(dir, "index.html"))) {
144742
145422
  studioDistDir = dir;
144743
145423
  break;
144744
145424
  }
@@ -144762,7 +145442,7 @@ function createApp(config2) {
144762
145442
  if (!studioState.enabled) {
144763
145443
  return res.status(403).json({ error: { code: "STUDIO_DISABLED", message: "Studio is disabled." } });
144764
145444
  }
144765
- res.sendFile(join17(studioDistDir, "index.html"));
145445
+ res.sendFile(join18(studioDistDir, "index.html"));
144766
145446
  });
144767
145447
  app.use("/studio", express.static(studioDistDir, { index: false }));
144768
145448
  app.use(express.static(studioDistDir, { index: false }));
@@ -144770,7 +145450,7 @@ function createApp(config2) {
144770
145450
  if (!studioState.enabled) {
144771
145451
  return res.status(403).json({ error: { code: "STUDIO_DISABLED", message: "Studio is disabled." } });
144772
145452
  }
144773
- res.sendFile(join17(studioDistDir, "index.html"));
145453
+ res.sendFile(join18(studioDistDir, "index.html"));
144774
145454
  });
144775
145455
  } else {
144776
145456
  app.get("/studio", (_req, res) => {
@@ -144798,7 +145478,7 @@ function createApp(config2) {
144798
145478
  if (!studioState.enabled) {
144799
145479
  return res.status(403).json({ error: { code: "STUDIO_DISABLED", message: "Studio is disabled." } });
144800
145480
  }
144801
- res.sendFile(join17(studioDistDir, "index.html"));
145481
+ res.sendFile(join18(studioDistDir, "index.html"));
144802
145482
  });
144803
145483
  }
144804
145484
  app.use(errorHandler());
@@ -144865,12 +145545,14 @@ var init_app = __esm({
144865
145545
  init_knowledge2();
144866
145546
  init_chat();
144867
145547
  init_providers();
145548
+ init_api_keys();
145549
+ init_key_vault();
144868
145550
  studioState = { enabled: true };
144869
145551
  }
144870
145552
  });
144871
145553
 
144872
145554
  // ../server/src/services/config-watcher.ts
144873
- import { watch, existsSync as existsSync12 } from "node:fs";
145555
+ import { watch, existsSync as existsSync13 } from "node:fs";
144874
145556
  import { readFile as readFile2 } from "node:fs/promises";
144875
145557
  var ConfigWatcher;
144876
145558
  var init_config_watcher = __esm({
@@ -144894,7 +145576,7 @@ var init_config_watcher = __esm({
144894
145576
  */
144895
145577
  async start() {
144896
145578
  await this.reload();
144897
- if (existsSync12(this.configPath)) {
145579
+ if (existsSync13(this.configPath)) {
144898
145580
  this.watcher = watch(this.configPath, { persistent: false }, (eventType) => {
144899
145581
  if (eventType === "change") {
144900
145582
  if (this.debounceTimer) clearTimeout(this.debounceTimer);
@@ -144930,7 +145612,7 @@ var init_config_watcher = __esm({
144930
145612
  // ─── Internal ───────────────────────────────────────────────────────
144931
145613
  async reload() {
144932
145614
  try {
144933
- if (!existsSync12(this.configPath)) {
145615
+ if (!existsSync13(this.configPath)) {
144934
145616
  console.warn("[config-watcher] Config file not found, keeping current config.");
144935
145617
  return;
144936
145618
  }
@@ -144974,9 +145656,9 @@ __export(telemetry_exports, {
144974
145656
  resetTelemetry: () => resetTelemetry
144975
145657
  });
144976
145658
  import { randomUUID as randomUUID35 } from "node:crypto";
144977
- import { existsSync as existsSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync8, appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
144978
- import { join as join18 } from "node:path";
144979
- import { homedir as homedir5 } from "node:os";
145659
+ import { existsSync as existsSync14, readFileSync as readFileSync17, writeFileSync as writeFileSync9, appendFileSync as appendFileSync2, mkdirSync as mkdirSync7 } from "node:fs";
145660
+ import { join as join19 } from "node:path";
145661
+ import { homedir as homedir6 } from "node:os";
144980
145662
  function getTelemetry(config2) {
144981
145663
  if (!_instance2) {
144982
145664
  _instance2 = new TelemetryService(config2 ?? { enabled: false, flushIntervalMs: 6e4 });
@@ -144991,9 +145673,9 @@ var init_telemetry2 = __esm({
144991
145673
  "../server/src/services/telemetry.ts"() {
144992
145674
  "use strict";
144993
145675
  VERSION2 = "0.1.0";
144994
- DATA_DIR3 = join18(homedir5(), ".nestor");
144995
- TELEMETRY_ID_FILE = join18(DATA_DIR3, "telemetry-id");
144996
- TELEMETRY_LOG_FILE = join18(DATA_DIR3, "telemetry.jsonl");
145676
+ DATA_DIR3 = join19(homedir6(), ".nestor");
145677
+ TELEMETRY_ID_FILE = join19(DATA_DIR3, "telemetry-id");
145678
+ TELEMETRY_LOG_FILE = join19(DATA_DIR3, "telemetry.jsonl");
144997
145679
  DEFAULT_FLUSH_THRESHOLD = 10;
144998
145680
  TelemetryService = class {
144999
145681
  enabled;
@@ -145086,8 +145768,8 @@ var init_telemetry2 = __esm({
145086
145768
  // ─── Private ─────────────────────────────────────────────────────────
145087
145769
  getOrCreateInstallId() {
145088
145770
  try {
145089
- if (existsSync13(TELEMETRY_ID_FILE)) {
145090
- const id = readFileSync16(TELEMETRY_ID_FILE, "utf-8").trim();
145771
+ if (existsSync14(TELEMETRY_ID_FILE)) {
145772
+ const id = readFileSync17(TELEMETRY_ID_FILE, "utf-8").trim();
145091
145773
  if (id.length > 0) return id;
145092
145774
  }
145093
145775
  } catch {
@@ -145095,14 +145777,14 @@ var init_telemetry2 = __esm({
145095
145777
  const newId = randomUUID35();
145096
145778
  try {
145097
145779
  this.ensureDataDir();
145098
- writeFileSync8(TELEMETRY_ID_FILE, newId + "\n", "utf-8");
145780
+ writeFileSync9(TELEMETRY_ID_FILE, newId + "\n", "utf-8");
145099
145781
  } catch {
145100
145782
  }
145101
145783
  return newId;
145102
145784
  }
145103
145785
  ensureDataDir() {
145104
- if (!existsSync13(DATA_DIR3)) {
145105
- mkdirSync6(DATA_DIR3, { recursive: true });
145786
+ if (!existsSync14(DATA_DIR3)) {
145787
+ mkdirSync7(DATA_DIR3, { recursive: true });
145106
145788
  }
145107
145789
  }
145108
145790
  startFlushTimer() {
@@ -156688,7 +157370,7 @@ var require_dist12 = __commonJS({
156688
157370
  });
156689
157371
 
156690
157372
  // ../skill-tester/src/yaml-loader.ts
156691
- import { readFileSync as readFileSync17 } from "node:fs";
157373
+ import { readFileSync as readFileSync18 } from "node:fs";
156692
157374
  function parseYamlContent(content) {
156693
157375
  try {
156694
157376
  return JSON.parse(content);
@@ -156804,7 +157486,7 @@ var init_yaml_loader = __esm({
156804
157486
  * Load a YAML test suite from a file path.
156805
157487
  */
156806
157488
  static loadFile(filePath) {
156807
- const content = readFileSync17(filePath, "utf-8");
157489
+ const content = readFileSync18(filePath, "utf-8");
156808
157490
  return _YamlTestLoader.parse(content);
156809
157491
  }
156810
157492
  /**
@@ -156883,8 +157565,8 @@ var init_yaml_loader = __esm({
156883
157565
  });
156884
157566
 
156885
157567
  // ../skill-tester/src/runner.ts
156886
- import { readFileSync as readFileSync18, readdirSync as readdirSync4, statSync as statSync5, existsSync as existsSync15, mkdirSync as mkdirSync8, rmSync as rmSync2 } from "node:fs";
156887
- import { join as join20, resolve as resolve14 } from "node:path";
157568
+ import { readFileSync as readFileSync19, readdirSync as readdirSync4, statSync as statSync5, existsSync as existsSync16, mkdirSync as mkdirSync9, rmSync as rmSync2 } from "node:fs";
157569
+ import { join as join21, resolve as resolve14 } from "node:path";
156888
157570
  import { tmpdir as tmpdir4 } from "node:os";
156889
157571
  import { randomUUID as randomUUID37 } from "node:crypto";
156890
157572
  var SequentialMockAdapter, SkillTestRunner;
@@ -156959,25 +157641,25 @@ var init_runner2 = __esm({
156959
157641
  try {
156960
157642
  const mockResponses = test.mockResponses.length > 0 ? test.mockResponses : [{ content: "Test completed." }];
156961
157643
  const adapter = new SequentialMockAdapter(mockResponses);
156962
- const tempDir = join20(tmpdir4(), `nestor-skill-test-${randomUUID37()}`);
156963
- mkdirSync8(tempDir, { recursive: true });
157644
+ const tempDir = join21(tmpdir4(), `nestor-skill-test-${randomUUID37()}`);
157645
+ mkdirSync9(tempDir, { recursive: true });
156964
157646
  try {
156965
157647
  const agentModule = await Promise.resolve().then(() => (init_src5(), src_exports3));
156966
157648
  const events = new agentModule.RuntimeEventBus();
156967
157649
  const tools = new agentModule.ToolRegistry();
156968
157650
  const toolExecutor = new agentModule.ToolExecutor();
156969
157651
  let instructions = "You are a helpful AI assistant.";
156970
- if (test.skill && existsSync15(test.skill)) {
156971
- instructions = readFileSync18(test.skill, "utf-8");
157652
+ if (test.skill && existsSync16(test.skill)) {
157653
+ instructions = readFileSync19(test.skill, "utf-8");
156972
157654
  } else if (test.skill) {
156973
157655
  const skillPaths = [
156974
- join20(process.cwd(), ".nestor", "skills", test.skill, "SKILL.md"),
156975
- join20(process.cwd(), "skills", test.skill, "SKILL.md"),
156976
- join20(process.cwd(), "skills-registry", test.skill, "SKILL.md")
157656
+ join21(process.cwd(), ".nestor", "skills", test.skill, "SKILL.md"),
157657
+ join21(process.cwd(), "skills", test.skill, "SKILL.md"),
157658
+ join21(process.cwd(), "skills-registry", test.skill, "SKILL.md")
156977
157659
  ];
156978
157660
  for (const p8 of skillPaths) {
156979
- if (existsSync15(p8)) {
156980
- instructions = readFileSync18(p8, "utf-8");
157661
+ if (existsSync16(p8)) {
157662
+ instructions = readFileSync19(p8, "utf-8");
156981
157663
  break;
156982
157664
  }
156983
157665
  }
@@ -157187,7 +157869,7 @@ var init_runner2 = __esm({
157187
157869
  discoverTestFiles(dir) {
157188
157870
  const files = [];
157189
157871
  const resolvedDir = resolve14(dir);
157190
- if (!existsSync15(resolvedDir)) return files;
157872
+ if (!existsSync16(resolvedDir)) return files;
157191
157873
  const stat = statSync5(resolvedDir);
157192
157874
  if (!stat.isDirectory()) {
157193
157875
  return [resolvedDir];
@@ -157201,7 +157883,7 @@ var init_runner2 = __esm({
157201
157883
  }
157202
157884
  for (const entry of entries) {
157203
157885
  if (entry === "node_modules" || entry === ".git" || entry === "dist") continue;
157204
- const fullPath = join20(current, entry);
157886
+ const fullPath = join21(current, entry);
157205
157887
  try {
157206
157888
  const s = statSync5(fullPath);
157207
157889
  if (s.isDirectory()) {
@@ -159071,7 +159753,7 @@ var init_server = __esm({
159071
159753
  MCP_PROTOCOL_VERSION = "2024-11-05";
159072
159754
  SERVER_INFO = {
159073
159755
  name: "nestor",
159074
- version: "2.1.0"
159756
+ version: "2.1.1"
159075
159757
  };
159076
159758
  SERVER_CAPABILITIES = {
159077
159759
  tools: { listChanged: false },
@@ -159257,9 +159939,9 @@ __export(shell_exports, {
159257
159939
  registerShellCommand: () => registerShellCommand
159258
159940
  });
159259
159941
  import * as readline3 from "node:readline";
159260
- import { existsSync as existsSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9, appendFileSync as appendFileSync3 } from "node:fs";
159261
- import { join as join21, resolve as resolve15, dirname as dirname9, basename as basename4 } from "node:path";
159262
- import { homedir as homedir6 } from "node:os";
159942
+ import { existsSync as existsSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3 } from "node:fs";
159943
+ import { join as join22, resolve as resolve15, dirname as dirname10, basename as basename4 } from "node:path";
159944
+ import { homedir as homedir7 } from "node:os";
159263
159945
  import { readdirSync as readdirSync5 } from "node:fs";
159264
159946
  import { randomUUID as randomUUID43 } from "node:crypto";
159265
159947
  import chalk11 from "chalk";
@@ -159291,8 +159973,8 @@ async function startShell() {
159291
159973
  currentRouter: null,
159292
159974
  verbose: false
159293
159975
  };
159294
- if (!existsSync16(NESTOR_DIR)) {
159295
- mkdirSync9(NESTOR_DIR, { recursive: true });
159976
+ if (!existsSync17(NESTOR_DIR)) {
159977
+ mkdirSync10(NESTOR_DIR, { recursive: true });
159296
159978
  }
159297
159979
  refreshAgentNameCache(session);
159298
159980
  const history = loadHistory();
@@ -160012,8 +160694,8 @@ function cmdExport(args2, session) {
160012
160694
  timestamp: new Date(e.timestamp).toISOString()
160013
160695
  }))
160014
160696
  };
160015
- const filename = join21(NESTOR_DIR, `conversation-${timestamp}.json`);
160016
- writeFileSync10(filename, JSON.stringify(data, null, 2), "utf-8");
160697
+ const filename = join22(NESTOR_DIR, `conversation-${timestamp}.json`);
160698
+ writeFileSync11(filename, JSON.stringify(data, null, 2), "utf-8");
160017
160699
  console.log(chalk11.green(`Exported to: ${filename}`));
160018
160700
  return;
160019
160701
  }
@@ -160037,8 +160719,8 @@ function cmdExport(args2, session) {
160037
160719
  lines.push(entry.content);
160038
160720
  lines.push("");
160039
160721
  }
160040
- const filename = join21(NESTOR_DIR, `conversation-${timestamp}.md`);
160041
- writeFileSync10(filename, lines.join("\n"), "utf-8");
160722
+ const filename = join22(NESTOR_DIR, `conversation-${timestamp}.md`);
160723
+ writeFileSync11(filename, lines.join("\n"), "utf-8");
160042
160724
  console.log(chalk11.green(`Exported to: ${filename}`));
160043
160725
  return;
160044
160726
  }
@@ -160074,8 +160756,8 @@ ${rows}
160074
160756
  <div class="stats">Tokens: ${fmtNum(session.totalTokensIn)} in / ${fmtNum(session.totalTokensOut)} out | Cost: $${session.totalCostUsd.toFixed(4)} | Tool calls: ${session.totalToolCalls}</div>
160075
160757
  </body>
160076
160758
  </html>`;
160077
- const filename = join21(NESTOR_DIR, `conversation-${timestamp}.html`);
160078
- writeFileSync10(filename, html, "utf-8");
160759
+ const filename = join22(NESTOR_DIR, `conversation-${timestamp}.html`);
160760
+ writeFileSync11(filename, html, "utf-8");
160079
160761
  console.log(chalk11.green(`Exported to: ${filename}`));
160080
160762
  }
160081
160763
  }
@@ -160580,7 +161262,7 @@ function createUnifiedDiff(oldText, newText, fileName) {
160580
161262
  function showDiffForStep(filePath, newContent) {
160581
161263
  try {
160582
161264
  const absolutePath = resolve15(process.cwd(), filePath);
160583
- const oldContent = readFileSync19(absolutePath, "utf-8");
161265
+ const oldContent = readFileSync20(absolutePath, "utf-8");
160584
161266
  const diff = createUnifiedDiff(oldContent, newContent, filePath);
160585
161267
  for (const line of diff.split("\n")) {
160586
161268
  if (line.startsWith("+") && !line.startsWith("+++")) {
@@ -160986,11 +161668,11 @@ function completeFilePath(partial) {
160986
161668
  dir = resolved;
160987
161669
  prefix = partial.endsWith("/") || partial.endsWith("\\") ? partial : partial + "/";
160988
161670
  } else {
160989
- dir = dirname9(resolved);
161671
+ dir = dirname10(resolved);
160990
161672
  prefix = partial.substring(0, partial.lastIndexOf("/") + 1) || partial.substring(0, partial.lastIndexOf("\\") + 1);
160991
161673
  }
160992
161674
  } catch {
160993
- dir = dirname9(resolved);
161675
+ dir = dirname10(resolved);
160994
161676
  prefix = partial.substring(0, Math.max(partial.lastIndexOf("/"), partial.lastIndexOf("\\")) + 1);
160995
161677
  }
160996
161678
  const entries = readdirSync5(dir, { withFileTypes: true });
@@ -161024,8 +161706,8 @@ function refreshAgentNameCache(session) {
161024
161706
  }
161025
161707
  function loadHistory() {
161026
161708
  try {
161027
- if (existsSync16(HISTORY_FILE)) {
161028
- const raw = readFileSync19(HISTORY_FILE, "utf-8");
161709
+ if (existsSync17(HISTORY_FILE)) {
161710
+ const raw = readFileSync20(HISTORY_FILE, "utf-8");
161029
161711
  return raw.split("\n").filter((l) => l.trim().length > 0).slice(-MAX_HISTORY);
161030
161712
  }
161031
161713
  } catch {
@@ -161036,8 +161718,8 @@ function saveHistory(rl) {
161036
161718
  }
161037
161719
  function appendToHistoryFile(line) {
161038
161720
  try {
161039
- if (!existsSync16(NESTOR_DIR)) {
161040
- mkdirSync9(NESTOR_DIR, { recursive: true });
161721
+ if (!existsSync17(NESTOR_DIR)) {
161722
+ mkdirSync10(NESTOR_DIR, { recursive: true });
161041
161723
  }
161042
161724
  appendFileSync3(HISTORY_FILE, line + "\n", "utf-8");
161043
161725
  } catch {
@@ -161379,8 +162061,8 @@ var init_shell = __esm({
161379
162061
  init_config2();
161380
162062
  init_spinner();
161381
162063
  init_table();
161382
- NESTOR_DIR = join21(homedir6(), ".nestor");
161383
- HISTORY_FILE = join21(NESTOR_DIR, "shell_history");
162064
+ NESTOR_DIR = join22(homedir7(), ".nestor");
162065
+ HISTORY_FILE = join22(NESTOR_DIR, "shell_history");
161384
162066
  MAX_HISTORY = 1e3;
161385
162067
  SLASH_COMMANDS = {
161386
162068
  "/help": { description: "Show all commands", usage: "/help" },
@@ -161432,9 +162114,9 @@ var init_shell = __esm({
161432
162114
 
161433
162115
  // src/index.ts
161434
162116
  import { Command } from "commander";
161435
- import { existsSync as existsSync20, readFileSync as readFileSync23 } from "node:fs";
161436
- import { join as join24 } from "node:path";
161437
- import { homedir as homedir9 } from "node:os";
162117
+ import { existsSync as existsSync21, readFileSync as readFileSync24 } from "node:fs";
162118
+ import { join as join25 } from "node:path";
162119
+ import { homedir as homedir10 } from "node:os";
161438
162120
 
161439
162121
  // src/commands/start.ts
161440
162122
  init_config2();
@@ -161578,16 +162260,16 @@ init_config();
161578
162260
  init_config2();
161579
162261
  import * as p from "@clack/prompts";
161580
162262
  import chalk2 from "chalk";
161581
- import { mkdirSync as mkdirSync7, existsSync as existsSync14 } from "node:fs";
161582
- import { join as join19 } from "node:path";
161583
- import { randomBytes as randomBytes7 } from "node:crypto";
162263
+ import { mkdirSync as mkdirSync8, existsSync as existsSync15 } from "node:fs";
162264
+ import { join as join20 } from "node:path";
162265
+ import { randomBytes as randomBytes8 } from "node:crypto";
161584
162266
  function generateApiKey() {
161585
- return `nst_${randomBytes7(32).toString("hex")}`;
162267
+ return `nst_${randomBytes8(32).toString("hex")}`;
161586
162268
  }
161587
162269
  function randomSegment() {
161588
162270
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
161589
162271
  let result = "";
161590
- const bytes = randomBytes7(4);
162272
+ const bytes = randomBytes8(4);
161591
162273
  for (let i = 0; i < 4; i++) {
161592
162274
  result += chars[bytes[i] % chars.length];
161593
162275
  }
@@ -161598,7 +162280,7 @@ function generateBetaKey() {
161598
162280
  }
161599
162281
  async function initializeDatabase(dataDir) {
161600
162282
  const { NestorStore: NestorStore2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
161601
- const dbPath = join19(dataDir, "nestor.db");
162283
+ const dbPath = join20(dataDir, "nestor.db");
161602
162284
  const store = await NestorStore2.open(dbPath);
161603
162285
  store.close();
161604
162286
  }
@@ -161646,31 +162328,31 @@ async function runNonInteractiveInstall() {
161646
162328
  console.log();
161647
162329
  const config2 = buildConfigFromDefaults();
161648
162330
  const dataDir = expandPath(config2.server.dataDir);
161649
- if (!existsSync14(dataDir)) {
161650
- mkdirSync7(dataDir, { recursive: true });
162331
+ if (!existsSync15(dataDir)) {
162332
+ mkdirSync8(dataDir, { recursive: true });
161651
162333
  console.log(chalk2.green(" Created data directory:"), dataDir);
161652
162334
  }
161653
162335
  for (const sub of ["adapters", "skills", "plugins", "logs"]) {
161654
- const subDir = join19(dataDir, sub);
161655
- if (!existsSync14(subDir)) {
161656
- mkdirSync7(subDir, { recursive: true });
162336
+ const subDir = join20(dataDir, sub);
162337
+ if (!existsSync15(subDir)) {
162338
+ mkdirSync8(subDir, { recursive: true });
161657
162339
  }
161658
162340
  }
161659
162341
  writeConfigFile(config2);
161660
162342
  console.log(chalk2.green(" Configuration saved:"), getConfigPath());
161661
162343
  try {
161662
162344
  await initializeDatabase(dataDir);
161663
- console.log(chalk2.green(" Database initialized:"), join19(dataDir, "nestor.db"));
162345
+ console.log(chalk2.green(" Database initialized:"), join20(dataDir, "nestor.db"));
161664
162346
  } catch (err) {
161665
162347
  console.log(chalk2.yellow(" Database initialization skipped (install @nestor/db first)"));
161666
162348
  }
161667
162349
  const apiKey = generateApiKey();
161668
- const apiKeyPath = join19(dataDir, ".api-keys");
161669
- const { writeFileSync: writeFileSync13 } = await import("node:fs");
161670
- writeFileSync13(apiKeyPath, JSON.stringify({ admin: apiKey }, null, 2) + "\n", "utf-8");
162350
+ const apiKeyPath = join20(dataDir, ".api-keys");
162351
+ const { writeFileSync: writeFileSync14 } = await import("node:fs");
162352
+ writeFileSync14(apiKeyPath, JSON.stringify({ admin: apiKey }, null, 2) + "\n", "utf-8");
161671
162353
  console.log(chalk2.green(" Admin API key created"));
161672
162354
  const betaKey = generateBetaKey();
161673
- writeFileSync13(join19(dataDir, "license.key"), betaKey, "utf-8");
162355
+ writeFileSync14(join20(dataDir, "license.key"), betaKey, "utf-8");
161674
162356
  console.log(chalk2.green(" Beta key created"));
161675
162357
  console.log();
161676
162358
  console.log(chalk2.bold("Admin API Key:"), chalk2.yellow(apiKey));
@@ -161766,9 +162448,9 @@ async function runInteractiveInstall() {
161766
162448
  const dataDir = expandPath(config2.server.dataDir);
161767
162449
  const spinner3 = p.spinner();
161768
162450
  spinner3.start("Creating directories...");
161769
- for (const dir of [dataDir, join19(dataDir, "adapters"), join19(dataDir, "skills"), join19(dataDir, "plugins"), join19(dataDir, "logs")]) {
161770
- if (!existsSync14(dir)) {
161771
- mkdirSync7(dir, { recursive: true });
162451
+ for (const dir of [dataDir, join20(dataDir, "adapters"), join20(dataDir, "skills"), join20(dataDir, "plugins"), join20(dataDir, "logs")]) {
162452
+ if (!existsSync15(dir)) {
162453
+ mkdirSync8(dir, { recursive: true });
161772
162454
  }
161773
162455
  }
161774
162456
  spinner3.stop("Directories created");
@@ -161793,14 +162475,14 @@ async function runInteractiveInstall() {
161793
162475
  const spinner4 = p.spinner();
161794
162476
  spinner4.start("Generating admin API key...");
161795
162477
  const apiKey = generateApiKey();
161796
- const apiKeyPath = join19(dataDir, ".api-keys");
161797
- const { writeFileSync: writeFileSync13 } = await import("node:fs");
161798
- writeFileSync13(apiKeyPath, JSON.stringify({ admin: apiKey }, null, 2) + "\n", "utf-8");
162478
+ const apiKeyPath = join20(dataDir, ".api-keys");
162479
+ const { writeFileSync: writeFileSync14 } = await import("node:fs");
162480
+ writeFileSync14(apiKeyPath, JSON.stringify({ admin: apiKey }, null, 2) + "\n", "utf-8");
161799
162481
  spinner4.stop("Admin API key created!");
161800
162482
  const spinner5 = p.spinner();
161801
162483
  spinner5.start("Generating beta key...");
161802
162484
  const betaKey = generateBetaKey();
161803
- writeFileSync13(join19(dataDir, "license.key"), betaKey, "utf-8");
162485
+ writeFileSync14(join20(dataDir, "license.key"), betaKey, "utf-8");
161804
162486
  spinner5.stop("Beta key created!");
161805
162487
  p.note(
161806
162488
  `Config file: ${getConfigPath()}
@@ -163093,23 +163775,23 @@ init_db();
163093
163775
  init_spinner();
163094
163776
  init_table();
163095
163777
  import {
163096
- existsSync as existsSync17,
163097
- readFileSync as readFileSync20,
163098
- writeFileSync as writeFileSync11,
163099
- mkdirSync as mkdirSync10,
163778
+ existsSync as existsSync18,
163779
+ readFileSync as readFileSync21,
163780
+ writeFileSync as writeFileSync12,
163781
+ mkdirSync as mkdirSync11,
163100
163782
  unlinkSync,
163101
163783
  appendFileSync as appendFileSync4
163102
163784
  } from "node:fs";
163103
- import { join as join22 } from "node:path";
163104
- import { homedir as homedir7 } from "node:os";
163785
+ import { join as join23 } from "node:path";
163786
+ import { homedir as homedir8 } from "node:os";
163105
163787
  import { fork as fork2 } from "node:child_process";
163106
163788
  import "node:readline";
163107
163789
  import chalk12 from "chalk";
163108
- var NESTOR_DIR2 = join22(homedir7(), ".nestor");
163109
- var PID_FILE = join22(NESTOR_DIR2, "daemon.pid");
163110
- var LOG_DIR = join22(NESTOR_DIR2, "logs");
163111
- var LOG_FILE = join22(LOG_DIR, "daemon.log");
163112
- var HEARTBEAT_FILE = join22(NESTOR_DIR2, "daemon.heartbeat");
163790
+ var NESTOR_DIR2 = join23(homedir8(), ".nestor");
163791
+ var PID_FILE = join23(NESTOR_DIR2, "daemon.pid");
163792
+ var LOG_DIR = join23(NESTOR_DIR2, "logs");
163793
+ var LOG_FILE = join23(LOG_DIR, "daemon.log");
163794
+ var HEARTBEAT_FILE = join23(NESTOR_DIR2, "daemon.heartbeat");
163113
163795
  var HEARTBEAT_INTERVAL_MS2 = 3e4;
163114
163796
  function registerDaemonCommand(program2) {
163115
163797
  const daemon = program2.command("daemon").description("Manage the Nestor background daemon");
@@ -163277,13 +163959,13 @@ async function showStatus() {
163277
163959
  console.log("");
163278
163960
  }
163279
163961
  async function showLogs(follow, lineCount) {
163280
- if (!existsSync17(LOG_FILE)) {
163962
+ if (!existsSync18(LOG_FILE)) {
163281
163963
  console.log(chalk12.yellow("No daemon log file found."));
163282
163964
  console.log(chalk12.dim(`Expected at: ${LOG_FILE}`));
163283
163965
  return;
163284
163966
  }
163285
163967
  if (!follow) {
163286
- const content = readFileSync20(LOG_FILE, "utf-8");
163968
+ const content = readFileSync21(LOG_FILE, "utf-8");
163287
163969
  const lines = content.split("\n").filter((l) => l.length > 0);
163288
163970
  const lastLines = lines.slice(-lineCount);
163289
163971
  if (lastLines.length === 0) {
@@ -163297,19 +163979,19 @@ async function showLogs(follow, lineCount) {
163297
163979
  }
163298
163980
  console.log(chalk12.dim(`Following ${LOG_FILE}... (Ctrl+C to stop)`));
163299
163981
  console.log("");
163300
- if (existsSync17(LOG_FILE)) {
163301
- const content = readFileSync20(LOG_FILE, "utf-8");
163982
+ if (existsSync18(LOG_FILE)) {
163983
+ const content = readFileSync21(LOG_FILE, "utf-8");
163302
163984
  const lines = content.split("\n").filter((l) => l.length > 0);
163303
163985
  const lastLines = lines.slice(-10);
163304
163986
  for (const line of lastLines) {
163305
163987
  console.log(colorizeLogLine(line));
163306
163988
  }
163307
163989
  }
163308
- let lastSize = existsSync17(LOG_FILE) ? readFileSync20(LOG_FILE).length : 0;
163990
+ let lastSize = existsSync18(LOG_FILE) ? readFileSync21(LOG_FILE).length : 0;
163309
163991
  const watcher = setInterval(() => {
163310
163992
  try {
163311
- if (!existsSync17(LOG_FILE)) return;
163312
- const content = readFileSync20(LOG_FILE, "utf-8");
163993
+ if (!existsSync18(LOG_FILE)) return;
163994
+ const content = readFileSync21(LOG_FILE, "utf-8");
163313
163995
  if (content.length > lastSize) {
163314
163996
  const newContent = content.substring(lastSize);
163315
163997
  const newLines = newContent.split("\n").filter((l) => l.length > 0);
@@ -163619,7 +164301,7 @@ var DaemonRunner = class {
163619
164301
  activeAgents: this.runningAgents.size,
163620
164302
  nextScheduledRun: void 0
163621
164303
  };
163622
- writeFileSync11(HEARTBEAT_FILE, JSON.stringify(data), "utf-8");
164304
+ writeFileSync12(HEARTBEAT_FILE, JSON.stringify(data), "utf-8");
163623
164305
  } catch {
163624
164306
  }
163625
164307
  }
@@ -163637,8 +164319,8 @@ async function importDaemonAgentModule() {
163637
164319
  }
163638
164320
  function readHeartbeat() {
163639
164321
  try {
163640
- if (existsSync17(HEARTBEAT_FILE)) {
163641
- const raw = readFileSync20(HEARTBEAT_FILE, "utf-8");
164322
+ if (existsSync18(HEARTBEAT_FILE)) {
164323
+ const raw = readFileSync21(HEARTBEAT_FILE, "utf-8");
163642
164324
  return JSON.parse(raw);
163643
164325
  }
163644
164326
  } catch {
@@ -163647,8 +164329,8 @@ function readHeartbeat() {
163647
164329
  }
163648
164330
  function readPid() {
163649
164331
  try {
163650
- if (existsSync17(PID_FILE)) {
163651
- const raw = readFileSync20(PID_FILE, "utf-8").trim();
164332
+ if (existsSync18(PID_FILE)) {
164333
+ const raw = readFileSync21(PID_FILE, "utf-8").trim();
163652
164334
  const pid = parseInt(raw, 10);
163653
164335
  return isNaN(pid) ? null : pid;
163654
164336
  }
@@ -163658,11 +164340,11 @@ function readPid() {
163658
164340
  }
163659
164341
  function writePid(pid) {
163660
164342
  ensureDirs();
163661
- writeFileSync11(PID_FILE, String(pid), "utf-8");
164343
+ writeFileSync12(PID_FILE, String(pid), "utf-8");
163662
164344
  }
163663
164345
  function cleanupPid() {
163664
164346
  try {
163665
- if (existsSync17(PID_FILE)) {
164347
+ if (existsSync18(PID_FILE)) {
163666
164348
  unlinkSync(PID_FILE);
163667
164349
  }
163668
164350
  } catch {
@@ -163702,11 +164384,11 @@ function colorizeLogLine(line) {
163702
164384
  return line;
163703
164385
  }
163704
164386
  function ensureDirs() {
163705
- if (!existsSync17(NESTOR_DIR2)) {
163706
- mkdirSync10(NESTOR_DIR2, { recursive: true });
164387
+ if (!existsSync18(NESTOR_DIR2)) {
164388
+ mkdirSync11(NESTOR_DIR2, { recursive: true });
163707
164389
  }
163708
- if (!existsSync17(LOG_DIR)) {
163709
- mkdirSync10(LOG_DIR, { recursive: true });
164390
+ if (!existsSync18(LOG_DIR)) {
164391
+ mkdirSync11(LOG_DIR, { recursive: true });
163710
164392
  }
163711
164393
  }
163712
164394
  function delay(ms) {
@@ -163747,13 +164429,13 @@ if (process.argv[2] === "__daemon_child__" || process.env.NESTOR_DAEMON_MODE ===
163747
164429
 
163748
164430
  // src/commands/watch.ts
163749
164431
  init_db();
163750
- import { existsSync as existsSync18, readFileSync as readFileSync21 } from "node:fs";
163751
- import { join as join23 } from "node:path";
163752
- import { homedir as homedir8 } from "node:os";
164432
+ import { existsSync as existsSync19, readFileSync as readFileSync22 } from "node:fs";
164433
+ import { join as join24 } from "node:path";
164434
+ import { homedir as homedir9 } from "node:os";
163753
164435
  import chalk13 from "chalk";
163754
- var NESTOR_DIR3 = join23(homedir8(), ".nestor");
163755
- var HEARTBEAT_FILE2 = join23(NESTOR_DIR3, "daemon.heartbeat");
163756
- var LOG_FILE2 = join23(NESTOR_DIR3, "logs", "daemon.log");
164436
+ var NESTOR_DIR3 = join24(homedir9(), ".nestor");
164437
+ var HEARTBEAT_FILE2 = join24(NESTOR_DIR3, "daemon.heartbeat");
164438
+ var LOG_FILE2 = join24(NESTOR_DIR3, "logs", "daemon.log");
163757
164439
  var REFRESH_INTERVAL_MS = 1e3;
163758
164440
  var MAX_EVENTS = 12;
163759
164441
  function registerWatchCommand(program2) {
@@ -163821,8 +164503,8 @@ async function refreshData(state) {
163821
164503
  } catch {
163822
164504
  }
163823
164505
  try {
163824
- if (existsSync18(HEARTBEAT_FILE2)) {
163825
- const raw = readFileSync21(HEARTBEAT_FILE2, "utf-8");
164506
+ if (existsSync19(HEARTBEAT_FILE2)) {
164507
+ const raw = readFileSync22(HEARTBEAT_FILE2, "utf-8");
163826
164508
  const heartbeat = JSON.parse(raw);
163827
164509
  state.daemonRunning = isDaemonAlive(heartbeat.pid);
163828
164510
  state.daemonPid = heartbeat.pid;
@@ -163839,8 +164521,8 @@ async function refreshData(state) {
163839
164521
  }
163840
164522
  function loadRecentEvents(state) {
163841
164523
  try {
163842
- if (!existsSync18(LOG_FILE2)) return;
163843
- const content = readFileSync21(LOG_FILE2, "utf-8");
164524
+ if (!existsSync19(LOG_FILE2)) return;
164525
+ const content = readFileSync22(LOG_FILE2, "utf-8");
163844
164526
  const lines = content.split("\n").filter((l) => l.length > 0);
163845
164527
  const recent = lines.slice(-MAX_EVENTS);
163846
164528
  state.recentEvents = recent.map((line) => {
@@ -164166,10 +164848,10 @@ function registerTelemetryCommand(program2) {
164166
164848
  }
164167
164849
  console.log(` ${chalk15.dim("Flush:")} every ${(config2.telemetry?.flushIntervalMs ?? 6e4) / 1e3}s`);
164168
164850
  }
164169
- const { join: join25 } = __require("node:path");
164170
- const { homedir: homedir10 } = __require("node:os");
164171
- const telemetryFile = join25(homedir10(), ".nestor", "telemetry.jsonl");
164172
- const idFile = join25(homedir10(), ".nestor", "telemetry-id");
164851
+ const { join: join26 } = __require("node:path");
164852
+ const { homedir: homedir11 } = __require("node:os");
164853
+ const telemetryFile = join26(homedir11(), ".nestor", "telemetry.jsonl");
164854
+ const idFile = join26(homedir11(), ".nestor", "telemetry-id");
164173
164855
  if (fs28.existsSync(idFile)) {
164174
164856
  const installId = fs28.readFileSync(idFile, "utf-8").trim();
164175
164857
  console.log(` ${chalk15.dim("Install ID:")} ${installId.slice(0, 8)}...`);
@@ -164210,7 +164892,7 @@ function setTelemetryEnabled(enabled) {
164210
164892
  init_db();
164211
164893
  init_table();
164212
164894
  import chalk16 from "chalk";
164213
- import { readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "node:fs";
164895
+ import { readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "node:fs";
164214
164896
  function registerMemoryCommand(program2) {
164215
164897
  const memory = program2.command("memory").description("Manage agent persistent memories");
164216
164898
  memory.command("list").description("List memories for an agent").option("-a, --agent <id>", "Agent ID to filter by").option("-t, --type <type>", "Memory type filter (observation, fact, instruction, reflection)").option("-l, --limit <n>", "Maximum results", "20").action(async (options) => {
@@ -164414,13 +165096,13 @@ Memory Statistics for ${agentId}`));
164414
165096
  }
164415
165097
  }
164416
165098
  const json = JSON.stringify(allMemories, null, 2);
164417
- writeFileSync12(outputFile, json, "utf-8");
165099
+ writeFileSync13(outputFile, json, "utf-8");
164418
165100
  console.log(chalk16.green(`Exported ${allMemories.length} memories to ${outputFile}`));
164419
165101
  });
164420
165102
  memory.command("import <file>").description("Import memories from JSON").action(async (file) => {
164421
165103
  const store = await getStore();
164422
165104
  try {
164423
- const raw = readFileSync22(file, "utf-8");
165105
+ const raw = readFileSync23(file, "utf-8");
164424
165106
  const entries = JSON.parse(raw);
164425
165107
  if (!Array.isArray(entries)) {
164426
165108
  console.error(chalk16.red("Invalid format: expected a JSON array of memory entries."));
@@ -164941,7 +165623,7 @@ ${results.length} results for "${query}":
164941
165623
 
164942
165624
  // src/commands/test.ts
164943
165625
  import { resolve as resolve17, relative as relative5 } from "node:path";
164944
- import { readdirSync as readdirSync6, statSync as statSync6, existsSync as existsSync19 } from "node:fs";
165626
+ import { readdirSync as readdirSync6, statSync as statSync6, existsSync as existsSync20 } from "node:fs";
164945
165627
  import chalk20 from "chalk";
164946
165628
  var TEST_FILE_PATTERNS = [
164947
165629
  ".nestor-test.yaml",
@@ -164954,7 +165636,7 @@ function registerTestCommand(program2) {
164954
165636
  program2.command("test [path]").description("Run skill and agent test suites").option("-l, --list", "List discovered test files", false).option("-f, --file <file>", "Run a specific test file").option("-r, --record <file>", "Record LLM responses to a fixture file").option("-p, --prompt <prompt>", "Prompt for recording mode").option("-v, --verbose", "Show detailed test output", false).option("-j, --json", "Output results as JSON (for CI)", false).action(async (path30, opts) => {
164955
165637
  if (opts.file) {
164956
165638
  const filePath = resolve17(opts.file);
164957
- if (!existsSync19(filePath)) {
165639
+ if (!existsSync20(filePath)) {
164958
165640
  console.error(chalk20.red(`File not found: ${opts.file}`));
164959
165641
  process.exit(1);
164960
165642
  }
@@ -164985,7 +165667,7 @@ function registerTestCommand(program2) {
164985
165667
  ];
164986
165668
  const allFiles = [];
164987
165669
  for (const testDir of defaultTestDirs) {
164988
- if (existsSync19(testDir)) {
165670
+ if (existsSync20(testDir)) {
164989
165671
  allFiles.push(...discoverTestFiles(testDir));
164990
165672
  }
164991
165673
  }
@@ -165028,7 +165710,7 @@ function discoverTestFiles(dir) {
165028
165710
  }
165029
165711
  }
165030
165712
  }
165031
- if (existsSync19(dir) && !statSync6(dir).isDirectory()) {
165713
+ if (existsSync20(dir) && !statSync6(dir).isDirectory()) {
165032
165714
  return [dir];
165033
165715
  }
165034
165716
  walk(dir);
@@ -165037,7 +165719,7 @@ function discoverTestFiles(dir) {
165037
165719
  async function listTestFiles(dir) {
165038
165720
  const files = discoverTestFiles(dir);
165039
165721
  const nestorTestDir = resolve17(dir, ".nestor", "tests");
165040
- if (existsSync19(nestorTestDir)) {
165722
+ if (existsSync20(nestorTestDir)) {
165041
165723
  files.push(...discoverTestFiles(nestorTestDir));
165042
165724
  }
165043
165725
  const unique = [...new Set(files)];
@@ -165093,7 +165775,7 @@ async function runSkillTestsByName(skillName, verbose, json) {
165093
165775
  ];
165094
165776
  const matchingFiles = [];
165095
165777
  for (const dir of searchDirs) {
165096
- if (!existsSync19(dir)) continue;
165778
+ if (!existsSync20(dir)) continue;
165097
165779
  const files = discoverTestFiles(dir);
165098
165780
  for (const file of files) {
165099
165781
  if (file.includes(skillName)) {
@@ -165291,9 +165973,9 @@ function registerGuardrailCommand(program2) {
165291
165973
 
165292
165974
  // src/index.ts
165293
165975
  function checkBetaAccess() {
165294
- const licenseFile = join24(homedir9(), ".nestor", "license.key");
165295
- if (!existsSync20(licenseFile)) return false;
165296
- const key = readFileSync23(licenseFile, "utf-8").trim();
165976
+ const licenseFile = join25(homedir10(), ".nestor", "license.key");
165977
+ if (!existsSync21(licenseFile)) return false;
165978
+ const key = readFileSync24(licenseFile, "utf-8").trim();
165297
165979
  return /^NESTOR-BETA-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(key);
165298
165980
  }
165299
165981
  var command2 = process.argv[2];
@@ -165313,7 +165995,7 @@ if (command2 && !["--help", "-h", "--version", "-V", "install"].includes(command
165313
165995
  }
165314
165996
  }
165315
165997
  var program = new Command();
165316
- program.name("nestor-sh").description("Nestor AI Agent Platform \u2014 orchestrate, secure and monitor AI agents").version("2.1.0");
165998
+ program.name("nestor-sh").description("Nestor AI Agent Platform \u2014 orchestrate, secure and monitor AI agents").version("2.1.1");
165317
165999
  registerStartCommand(program);
165318
166000
  registerInstallCommand(program);
165319
166001
  registerAgentCommand(program);