nodal-agents 0.2.0 → 0.3.0

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 (129) hide show
  1. package/README.md +24 -0
  2. package/cli.js +314 -99
  3. package/migrations/0020_agent_workspaces.sql +60 -0
  4. package/migrations/meta/0009_snapshot.json +4454 -4454
  5. package/migrations/meta/0010_snapshot.json +4460 -4460
  6. package/migrations/meta/_journal.json +153 -146
  7. package/package.json +7 -3
  8. package/runner.js +2998 -59214
  9. package/web/.next/BUILD_ID +1 -1
  10. package/web/.next/app-path-routes-manifest.json +4 -4
  11. package/web/.next/build-manifest.json +2 -2
  12. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +3 -2
  13. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js.nft.json +1 -1
  14. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page_client-reference-manifest.js +1 -1
  15. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js +1 -1
  16. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js.nft.json +1 -1
  17. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page_client-reference-manifest.js +1 -1
  18. package/web/.next/server/app/(dashboard)/agents/page.js +2 -2
  19. package/web/.next/server/app/(dashboard)/agents/page.js.nft.json +1 -1
  20. package/web/.next/server/app/(dashboard)/agents/page_client-reference-manifest.js +1 -1
  21. package/web/.next/server/app/(dashboard)/approvals/page.js +2 -2
  22. package/web/.next/server/app/(dashboard)/approvals/page.js.nft.json +1 -1
  23. package/web/.next/server/app/(dashboard)/approvals/page_client-reference-manifest.js +1 -1
  24. package/web/.next/server/app/(dashboard)/automations/page.js +2 -2
  25. package/web/.next/server/app/(dashboard)/automations/page.js.nft.json +1 -1
  26. package/web/.next/server/app/(dashboard)/automations/page_client-reference-manifest.js +1 -1
  27. package/web/.next/server/app/(dashboard)/billing/page_client-reference-manifest.js +1 -1
  28. package/web/.next/server/app/(dashboard)/connectors/page.js +2 -2
  29. package/web/.next/server/app/(dashboard)/connectors/page.js.nft.json +1 -1
  30. package/web/.next/server/app/(dashboard)/connectors/page_client-reference-manifest.js +1 -1
  31. package/web/.next/server/app/(dashboard)/credentials/page.js +1 -1
  32. package/web/.next/server/app/(dashboard)/credentials/page.js.nft.json +1 -1
  33. package/web/.next/server/app/(dashboard)/credentials/page_client-reference-manifest.js +1 -1
  34. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +2 -2
  35. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js.nft.json +1 -1
  36. package/web/.next/server/app/(dashboard)/jobs/[id]/page_client-reference-manifest.js +1 -1
  37. package/web/.next/server/app/(dashboard)/jobs/page.js +2 -2
  38. package/web/.next/server/app/(dashboard)/jobs/page.js.nft.json +1 -1
  39. package/web/.next/server/app/(dashboard)/jobs/page_client-reference-manifest.js +1 -1
  40. package/web/.next/server/app/(dashboard)/llm-providers/page.js +2 -2
  41. package/web/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +1 -1
  42. package/web/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/(dashboard)/logs/page.js +2 -2
  44. package/web/.next/server/app/(dashboard)/logs/page.js.nft.json +1 -1
  45. package/web/.next/server/app/(dashboard)/logs/page_client-reference-manifest.js +1 -1
  46. package/web/.next/server/app/(dashboard)/mcp/page.js +1 -1
  47. package/web/.next/server/app/(dashboard)/mcp/page.js.nft.json +1 -1
  48. package/web/.next/server/app/(dashboard)/mcp/page_client-reference-manifest.js +1 -1
  49. package/web/.next/server/app/(dashboard)/memories/page.js +2 -2
  50. package/web/.next/server/app/(dashboard)/memories/page.js.nft.json +1 -1
  51. package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
  52. package/web/.next/server/app/(dashboard)/page.js +2 -2
  53. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  54. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  55. package/web/.next/server/app/(dashboard)/settings/page.js +2 -2
  56. package/web/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
  57. package/web/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
  58. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js +2 -2
  59. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js.nft.json +1 -1
  60. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page_client-reference-manifest.js +1 -1
  61. package/web/.next/server/app/(dashboard)/skills/new/page.js +1299 -101
  62. package/web/.next/server/app/(dashboard)/skills/new/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/(dashboard)/skills/page.js +2 -2
  64. package/web/.next/server/app/(dashboard)/skills/page.js.nft.json +1 -1
  65. package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  67. package/web/.next/server/app/_global-error.html +1 -1
  68. package/web/.next/server/app/_global-error.rsc +1 -1
  69. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  70. package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  71. package/web/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  72. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  73. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  74. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  75. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  76. package/web/.next/server/app/_not-found.html +1 -1
  77. package/web/.next/server/app/_not-found.rsc +2 -2
  78. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  79. package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  81. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  84. package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
  85. package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  86. package/web/.next/server/app/onboarding.html +1 -1
  87. package/web/.next/server/app/onboarding.rsc +2 -2
  88. package/web/.next/server/app/onboarding.segments/_full.segment.rsc +2 -2
  89. package/web/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
  90. package/web/.next/server/app/onboarding.segments/_index.segment.rsc +2 -2
  91. package/web/.next/server/app/onboarding.segments/_tree.segment.rsc +2 -2
  92. package/web/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +1 -1
  93. package/web/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
  94. package/web/.next/server/app-paths-manifest.json +4 -4
  95. package/web/.next/server/chunks/{9327.js → 2456.js} +1 -1
  96. package/web/.next/server/chunks/4574.js +1 -1
  97. package/web/.next/server/chunks/5786.js +1223 -0
  98. package/web/.next/server/chunks/5832.js +1 -0
  99. package/web/.next/server/chunks/6263.js +1 -0
  100. package/web/.next/server/chunks/9682.js +1 -0
  101. package/web/.next/server/middleware-build-manifest.js +1 -1
  102. package/web/.next/server/pages/404.html +1 -1
  103. package/web/.next/server/pages/500.html +1 -1
  104. package/web/.next/server/server-reference-manifest.js +1 -1
  105. package/web/.next/server/server-reference-manifest.json +1 -1
  106. package/web/.next/static/chunks/3141-f595f19634be6813.js +1 -0
  107. package/web/.next/static/chunks/{6118-c6a8b66d0a3e446c.js → 4239-99af3d7559fd9ec1.js} +1 -1
  108. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-f6f39ebe8d6fb019.js +2 -0
  109. package/web/.next/static/chunks/app/(dashboard)/agents/page-d2e35b0af87cdc0b.js +1 -0
  110. package/web/.next/static/chunks/app/(dashboard)/connectors/page-6ccde8b8b9197d2f.js +1 -0
  111. package/web/.next/static/chunks/app/(dashboard)/credentials/page-679ca09e4625b70f.js +1 -0
  112. package/web/.next/static/chunks/app/(dashboard)/mcp/page-ad271be896b650a9.js +1 -0
  113. package/web/.next/static/chunks/app/(dashboard)/memories/page-0eed8d3610720c33.js +1 -0
  114. package/web/.next/static/chunks/app/(dashboard)/skills/page-472bb16429cd2798.js +1 -0
  115. package/web/.next/static/css/845089bde6c9e6fb.css +3 -0
  116. package/web/.next/server/chunks/5616.js +0 -25
  117. package/web/.next/server/chunks/6595.js +0 -1
  118. package/web/.next/server/chunks/9323.js +0 -1
  119. package/web/.next/static/chunks/3422-9078c04bb8df8ce5.js +0 -1
  120. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-faaf5d7084db1fbc.js +0 -1
  121. package/web/.next/static/chunks/app/(dashboard)/agents/page-9f2c2130d2f4de95.js +0 -1
  122. package/web/.next/static/chunks/app/(dashboard)/connectors/page-cd7ee7dd1458ae47.js +0 -1
  123. package/web/.next/static/chunks/app/(dashboard)/credentials/page-73d252ca88f194f1.js +0 -1
  124. package/web/.next/static/chunks/app/(dashboard)/mcp/page-1b2bf2315b3b213e.js +0 -1
  125. package/web/.next/static/chunks/app/(dashboard)/memories/page-1e537215e80cb0c3.js +0 -1
  126. package/web/.next/static/chunks/app/(dashboard)/skills/page-a6736503d29a2f1c.js +0 -1
  127. package/web/.next/static/css/a161c147ca3606b3.css +0 -3
  128. /package/web/.next/static/{be4PVOMd_EIWn1rY3m-Q2 → RdognT8Zq7jK6wHyNY5k3}/_buildManifest.js +0 -0
  129. /package/web/.next/static/{be4PVOMd_EIWn1rY3m-Q2 → RdognT8Zq7jK6wHyNY5k3}/_ssgManifest.js +0 -0
package/cli.js CHANGED
@@ -9,21 +9,67 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // src/lib/version.ts
13
+ var version_exports = {};
14
+ __export(version_exports, {
15
+ getInstalledVersion: () => getInstalledVersion,
16
+ getLatestVersion: () => getLatestVersion
17
+ });
18
+ import { createRequire } from "module";
19
+ import { fileURLToPath } from "url";
20
+ import { dirname, join } from "path";
21
+ import { existsSync } from "fs";
22
+ import { execa } from "execa";
23
+ function resolveCliPackageJsonPath() {
24
+ const here = dirname(fileURLToPath(import.meta.url));
25
+ if (existsSync(join(here, "runner.js"))) {
26
+ return join(here, "package.json");
27
+ }
28
+ return join(here, "..", "..", "package.json");
29
+ }
30
+ function getInstalledVersion() {
31
+ try {
32
+ const pkgPath = resolveCliPackageJsonPath();
33
+ const _require = createRequire(import.meta.url);
34
+ const pkg = _require(pkgPath);
35
+ return typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
36
+ } catch {
37
+ return "0.0.0";
38
+ }
39
+ }
40
+ async function getLatestVersion() {
41
+ try {
42
+ const { stdout } = await execa("npm", ["view", "nodal-agents", "version"], {
43
+ timeout: 5e3,
44
+ reject: true
45
+ });
46
+ const trimmed = stdout.trim();
47
+ return trimmed || null;
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+ var init_version = __esm({
53
+ "src/lib/version.ts"() {
54
+ "use strict";
55
+ }
56
+ });
57
+
12
58
  // src/lib/config.ts
13
59
  import { z } from "zod";
14
60
  import { homedir } from "os";
15
- import { join } from "path";
16
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
61
+ import { join as join2 } from "path";
62
+ import { readFileSync, writeFileSync, mkdirSync, existsSync as existsSync2 } from "fs";
17
63
  import { randomBytes } from "crypto";
18
64
  function ensureConfigDir() {
19
65
  for (const dir of [CONFIG_DIR, PID_DIR, LOG_DIR, PG_DATA_DIR]) {
20
- if (!existsSync(dir)) {
66
+ if (!existsSync2(dir)) {
21
67
  mkdirSync(dir, { recursive: true });
22
68
  }
23
69
  }
24
70
  }
25
71
  function readConfig(configFile = CONFIG_FILE) {
26
- if (!existsSync(configFile)) return null;
72
+ if (!existsSync2(configFile)) return null;
27
73
  let parsed;
28
74
  try {
29
75
  const raw = JSON.parse(readFileSync(configFile, "utf-8"));
@@ -102,11 +148,11 @@ var init_config = __esm({
102
148
  googleClientSecret: z.string().optional()
103
149
  }).optional()
104
150
  });
105
- CONFIG_DIR = join(homedir(), ".nodalai");
106
- CONFIG_FILE = join(CONFIG_DIR, "config.json");
107
- PID_DIR = join(CONFIG_DIR, "pids");
108
- LOG_DIR = join(CONFIG_DIR, "logs");
109
- PG_DATA_DIR = join(CONFIG_DIR, "pg-data");
151
+ CONFIG_DIR = join2(homedir(), ".nodalai");
152
+ CONFIG_FILE = join2(CONFIG_DIR, "config.json");
153
+ PID_DIR = join2(CONFIG_DIR, "pids");
154
+ LOG_DIR = join2(CONFIG_DIR, "logs");
155
+ PG_DATA_DIR = join2(CONFIG_DIR, "pg-data");
110
156
  }
111
157
  });
112
158
 
@@ -3392,7 +3438,7 @@ var init_subquery = __esm({
3392
3438
 
3393
3439
  // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@electri_96bd1661c4535b1384d37581a38c7c74/node_modules/drizzle-orm/version.js
3394
3440
  var version;
3395
- var init_version = __esm({
3441
+ var init_version2 = __esm({
3396
3442
  "../../node_modules/.pnpm/drizzle-orm@0.45.2_@electri_96bd1661c4535b1384d37581a38c7c74/node_modules/drizzle-orm/version.js"() {
3397
3443
  "use strict";
3398
3444
  version = "0.45.2";
@@ -3405,7 +3451,7 @@ var init_tracing = __esm({
3405
3451
  "../../node_modules/.pnpm/drizzle-orm@0.45.2_@electri_96bd1661c4535b1384d37581a38c7c74/node_modules/drizzle-orm/tracing.js"() {
3406
3452
  "use strict";
3407
3453
  init_tracing_utils();
3408
- init_version();
3454
+ init_version2();
3409
3455
  tracer = {
3410
3456
  startActiveSpan(name, fn) {
3411
3457
  if (!otel) {
@@ -3840,7 +3886,7 @@ var init_sql = __esm({
3840
3886
  return new SQL([new StringChunk(str)]);
3841
3887
  }
3842
3888
  sql2.raw = raw;
3843
- function join8(chunks, separator) {
3889
+ function join9(chunks, separator) {
3844
3890
  const result = [];
3845
3891
  for (const [i, chunk] of chunks.entries()) {
3846
3892
  if (i > 0 && separator !== void 0) {
@@ -3850,7 +3896,7 @@ var init_sql = __esm({
3850
3896
  }
3851
3897
  return new SQL(result);
3852
3898
  }
3853
- sql2.join = join8;
3899
+ sql2.join = join9;
3854
3900
  function identifier(value) {
3855
3901
  return new Name(value);
3856
3902
  }
@@ -8149,7 +8195,7 @@ var init_select2 = __esm({
8149
8195
  const baseTableName = this.tableName;
8150
8196
  const tableName = getTableLikeName(table);
8151
8197
  for (const item of extractUsedTable(table)) this.usedTables.add(item);
8152
- if (typeof tableName === "string" && this.config.joins?.some((join8) => join8.alias === tableName)) {
8198
+ if (typeof tableName === "string" && this.config.joins?.some((join9) => join9.alias === tableName)) {
8153
8199
  throw new Error(`Alias "${tableName}" is already used in this query`);
8154
8200
  }
8155
8201
  if (!this.isPartialSelect) {
@@ -9684,7 +9730,7 @@ var init_update = __esm({
9684
9730
  createJoin(joinType) {
9685
9731
  return (table, on) => {
9686
9732
  const tableName = getTableLikeName(table);
9687
- if (typeof tableName === "string" && this.config.joins.some((join8) => join8.alias === tableName)) {
9733
+ if (typeof tableName === "string" && this.config.joins.some((join9) => join9.alias === tableName)) {
9688
9734
  throw new Error(`Alias "${tableName}" is already used in this query`);
9689
9735
  }
9690
9736
  if (typeof on === "function") {
@@ -9780,10 +9826,10 @@ var init_update = __esm({
9780
9826
  const fromFields = this.getTableLikeFields(this.config.from);
9781
9827
  fields[tableName] = fromFields;
9782
9828
  }
9783
- for (const join8 of this.config.joins) {
9784
- const tableName2 = getTableLikeName(join8.table);
9785
- if (typeof tableName2 === "string" && !is(join8.table, SQL)) {
9786
- const fromFields = this.getTableLikeFields(join8.table);
9829
+ for (const join9 of this.config.joins) {
9830
+ const tableName2 = getTableLikeName(join9.table);
9831
+ if (typeof tableName2 === "string" && !is(join9.table, SQL)) {
9832
+ const fromFields = this.getTableLikeFields(join9.table);
9787
9833
  fields[tableName2] = fromFields;
9788
9834
  }
9789
9835
  }
@@ -11025,10 +11071,10 @@ var migrate_exports = {};
11025
11071
  __export(migrate_exports, {
11026
11072
  runMigrations: () => runMigrations
11027
11073
  });
11028
- import { fileURLToPath } from "url";
11029
- import { join as join2, dirname } from "path";
11074
+ import { fileURLToPath as fileURLToPath2 } from "url";
11075
+ import { join as join3, dirname as dirname2 } from "path";
11030
11076
  import {
11031
- existsSync as existsSync2,
11077
+ existsSync as existsSync3,
11032
11078
  mkdtempSync,
11033
11079
  readdirSync,
11034
11080
  readFileSync as readFileSync2,
@@ -11039,27 +11085,27 @@ import { tmpdir } from "os";
11039
11085
  async function runMigrations(connectionString, opts = {}) {
11040
11086
  const sql2 = src_default(connectionString, { max: 1 });
11041
11087
  const db = drizzle(sql2);
11042
- const here = dirname(fileURLToPath(import.meta.url));
11043
- const sibling = join2(here, "migrations");
11044
- const sourceFolder = existsSync2(sibling) ? sibling : join2(here, "../migrations");
11088
+ const here = dirname2(fileURLToPath2(import.meta.url));
11089
+ const sibling = join3(here, "migrations");
11090
+ const sourceFolder = existsSync3(sibling) ? sibling : join3(here, "../migrations");
11045
11091
  const migrationsFolder = opts.patchVectorAsText ? patchMigrationsForNoVector(sourceFolder) : sourceFolder;
11046
11092
  await migrate(db, { migrationsFolder });
11047
11093
  await sql2.end();
11048
11094
  }
11049
11095
  function patchMigrationsForNoVector(sourceFolder) {
11050
- const tmpFolder = mkdtempSync(join2(tmpdir(), "nodalai-migrations-"));
11051
- const metaDir = join2(tmpFolder, "meta");
11096
+ const tmpFolder = mkdtempSync(join3(tmpdir(), "nodalai-migrations-"));
11097
+ const metaDir = join3(tmpFolder, "meta");
11052
11098
  mkdirSync2(metaDir, { recursive: true });
11053
11099
  for (const entry of readdirSync(sourceFolder, { withFileTypes: true })) {
11054
11100
  if (entry.isDirectory() && entry.name === "meta") {
11055
- for (const metaFile of readdirSync(join2(sourceFolder, "meta"))) {
11056
- const content = readFileSync2(join2(sourceFolder, "meta", metaFile), "utf8");
11057
- writeFileSync2(join2(metaDir, metaFile), content);
11101
+ for (const metaFile of readdirSync(join3(sourceFolder, "meta"))) {
11102
+ const content = readFileSync2(join3(sourceFolder, "meta", metaFile), "utf8");
11103
+ writeFileSync2(join3(metaDir, metaFile), content);
11058
11104
  }
11059
11105
  } else if (entry.isFile() && entry.name.endsWith(".sql")) {
11060
- const content = readFileSync2(join2(sourceFolder, entry.name), "utf8");
11106
+ const content = readFileSync2(join3(sourceFolder, entry.name), "utf8");
11061
11107
  const patched = content.replace(/\bvector\(\d+\)/g, "text");
11062
- writeFileSync2(join2(tmpFolder, entry.name), patched);
11108
+ writeFileSync2(join3(tmpFolder, entry.name), patched);
11063
11109
  }
11064
11110
  }
11065
11111
  return tmpFolder;
@@ -11074,11 +11120,11 @@ var init_migrate = __esm({
11074
11120
  });
11075
11121
 
11076
11122
  // src/lib/postgres.ts
11077
- import { existsSync as existsSync3, readFileSync as readFileSync3, unlinkSync } from "fs";
11078
- import { join as join3 } from "path";
11123
+ import { existsSync as existsSync4, readFileSync as readFileSync3, unlinkSync } from "fs";
11124
+ import { join as join4 } from "path";
11079
11125
  function clearStalePostmasterPid(dataDir) {
11080
- const pidFile = join3(dataDir, "postmaster.pid");
11081
- if (!existsSync3(pidFile)) return;
11126
+ const pidFile = join4(dataDir, "postmaster.pid");
11127
+ if (!existsSync4(pidFile)) return;
11082
11128
  let pid;
11083
11129
  try {
11084
11130
  const firstLine = readFileSync3(pidFile, "utf-8").split("\n")[0]?.trim() ?? "";
@@ -11099,8 +11145,8 @@ function clearStalePostmasterPid(dataDir) {
11099
11145
  }
11100
11146
  }
11101
11147
  async function stopOrphanPostgres(dataDir = PG_DATA_DIR) {
11102
- const pidFile = join3(dataDir, "postmaster.pid");
11103
- if (!existsSync3(pidFile)) return false;
11148
+ const pidFile = join4(dataDir, "postmaster.pid");
11149
+ if (!existsSync4(pidFile)) return false;
11104
11150
  try {
11105
11151
  const EmbeddedPostgres = (await import("embedded-postgres")).default;
11106
11152
  const pg = new EmbeddedPostgres({
@@ -11147,7 +11193,7 @@ async function startEmbeddedPostgres(dataDir = PG_DATA_DIR, port = 25432) {
11147
11193
  if (verboseLog) process.stdout.write("[pg] " + m + "\n");
11148
11194
  }
11149
11195
  });
11150
- const alreadyInitialised = existsSync3(join3(dataDir, "PG_VERSION"));
11196
+ const alreadyInitialised = existsSync4(join4(dataDir, "PG_VERSION"));
11151
11197
  if (alreadyInitialised) clearStalePostmasterPid(dataDir);
11152
11198
  try {
11153
11199
  if (!alreadyInitialised) {
@@ -12327,12 +12373,6 @@ var init_agents = __esm({
12327
12373
  // (Memory Sprint 2). Pure char budget — token estimation done at call site
12328
12374
  // (length/4). 1500 chars ≈ ~375 tokens, similar to Hermes' 2200+1375 split.
12329
12375
  memoryTokenBudget: integer("memory_token_budget").default(1500).notNull(),
12330
- // Absolute filesystem path the agent's file_* tools are scoped to. NULL =
12331
- // file tools fail loud (`workspace_not_configured`). Per-agent so each
12332
- // agent in an entity can have its own scope (e.g. one agent on Obsidian
12333
- // vault, another on a code repo). Resolution at tool-call time runs
12334
- // through realpath + boundary check (see file-ops/workspace.ts).
12335
- workspaceRootPath: text("workspace_root_path"),
12336
12376
  // User-controlled order on the /agents page (Brique A, migration 0019).
12337
12377
  // Default 0 — ties are broken by `name ASC` in the list query. Newly
12338
12378
  // created agents land at the front of their group by default; the user
@@ -13172,6 +13212,43 @@ var init_agent_connector_assignments = __esm({
13172
13212
  }
13173
13213
  });
13174
13214
 
13215
+ // ../../packages/db/src/schema/agent-workspaces.ts
13216
+ var agentWorkspaces;
13217
+ var init_agent_workspaces = __esm({
13218
+ "../../packages/db/src/schema/agent-workspaces.ts"() {
13219
+ "use strict";
13220
+ init_pg_core();
13221
+ init_agents();
13222
+ init_entities();
13223
+ agentWorkspaces = pgTable(
13224
+ "agent_workspaces",
13225
+ {
13226
+ id: uuid("id").primaryKey().defaultRandom(),
13227
+ agentId: uuid("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
13228
+ // entityId mirrors the agent's entity — useful for ownership checks without
13229
+ // a join. Set to CASCADE so deleting an entity also sweeps its workspaces.
13230
+ entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
13231
+ // Human-readable label, also the first path segment the LLM must use when
13232
+ // the agent has more than one workspace (e.g. "notes/file.md").
13233
+ // Must be unique per agent. Max 80 chars keeps prompts readable.
13234
+ label: text("label").notNull(),
13235
+ // Absolute filesystem path — validated to be absolute at write time.
13236
+ path: text("path").notNull(),
13237
+ // UI display order; lower = appears first. Default 0.
13238
+ position: integer("position").default(0).notNull(),
13239
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
13240
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
13241
+ },
13242
+ (table) => [
13243
+ // Enforce label uniqueness per agent
13244
+ uniqueIndex("agent_workspaces_agent_label_unique").on(table.agentId, table.label),
13245
+ // Fast lookup of all workspaces for an agent (execute.ts, actions.ts)
13246
+ index("idx_agent_workspaces_agent_id").on(table.agentId)
13247
+ ]
13248
+ );
13249
+ }
13250
+ });
13251
+
13175
13252
  // ../../packages/db/src/schema/index.ts
13176
13253
  var schema_exports = {};
13177
13254
  __export(schema_exports, {
@@ -13209,6 +13286,7 @@ __export(schema_exports, {
13209
13286
  agentSkillAssignments: () => agentSkillAssignments,
13210
13287
  agentSkills: () => agentSkills,
13211
13288
  agentTasks: () => agentTasks,
13289
+ agentWorkspaces: () => agentWorkspaces,
13212
13290
  agents: () => agents,
13213
13291
  approvalRequests: () => approvalRequests,
13214
13292
  approvalRules: () => approvalRules,
@@ -13253,6 +13331,7 @@ var init_schema2 = __esm({
13253
13331
  init_misc();
13254
13332
  init_auth();
13255
13333
  init_agent_connector_assignments();
13334
+ init_agent_workspaces();
13256
13335
  }
13257
13336
  });
13258
13337
 
@@ -13288,9 +13367,9 @@ var init_transaction = __esm({
13288
13367
 
13289
13368
  // ../../packages/secrets/src/index.ts
13290
13369
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes3 } from "crypto";
13291
- import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
13370
+ import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
13292
13371
  import { homedir as homedir2 } from "os";
13293
- import { dirname as dirname2, join as join4 } from "path";
13372
+ import { dirname as dirname3, join as join5 } from "path";
13294
13373
  var init_src3 = __esm({
13295
13374
  "../../packages/secrets/src/index.ts"() {
13296
13375
  "use strict";
@@ -13546,16 +13625,16 @@ var init_ports = __esm({
13546
13625
  });
13547
13626
 
13548
13627
  // src/lib/processes.ts
13549
- import { execa } from "execa";
13550
- import { createWriteStream, writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
13551
- import { join as join5, dirname as dirname3, resolve } from "path";
13552
- import { fileURLToPath as fileURLToPath2 } from "url";
13628
+ import { execa as execa2 } from "execa";
13629
+ import { createWriteStream, writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
13630
+ import { join as join6, dirname as dirname4, resolve } from "path";
13631
+ import { fileURLToPath as fileURLToPath3 } from "url";
13553
13632
  async function killProcessTree(child) {
13554
13633
  const pid = child.pid;
13555
13634
  if (!pid) return;
13556
13635
  if (process.platform === "win32") {
13557
13636
  try {
13558
- await execa("taskkill", ["/T", "/F", "/PID", String(pid)], { reject: false });
13637
+ await execa2("taskkill", ["/T", "/F", "/PID", String(pid)], { reject: false });
13559
13638
  } catch {
13560
13639
  }
13561
13640
  return;
@@ -13570,29 +13649,45 @@ async function killProcessTree(child) {
13570
13649
  } catch {
13571
13650
  }
13572
13651
  }
13652
+ function isPidAlive(pid) {
13653
+ try {
13654
+ process.kill(pid, 0);
13655
+ return true;
13656
+ } catch (err) {
13657
+ return err.code === "EPERM";
13658
+ }
13659
+ }
13660
+ async function waitForPidDead(pid, timeoutMs = 5e3) {
13661
+ const deadline = Date.now() + timeoutMs;
13662
+ while (Date.now() < deadline) {
13663
+ if (!isPidAlive(pid)) return true;
13664
+ await sleep(250);
13665
+ }
13666
+ return !isPidAlive(pid);
13667
+ }
13573
13668
  function resolveAppDir(appName) {
13574
- const here = fileURLToPath2(import.meta.url);
13575
- const fileDir = dirname3(here);
13669
+ const here = fileURLToPath3(import.meta.url);
13670
+ const fileDir = dirname4(here);
13576
13671
  let cursor = fileDir;
13577
13672
  for (let i = 0; i < 8; i++) {
13578
- if (existsSync5(join5(cursor, "pnpm-workspace.yaml"))) {
13579
- return join5(cursor, appName);
13673
+ if (existsSync6(join6(cursor, "pnpm-workspace.yaml"))) {
13674
+ return join6(cursor, appName);
13580
13675
  }
13581
- const parent = dirname3(cursor);
13676
+ const parent = dirname4(cursor);
13582
13677
  if (parent === cursor) break;
13583
13678
  cursor = parent;
13584
13679
  }
13585
13680
  return resolve(fileDir, "..", "..", "..", "..", appName);
13586
13681
  }
13587
13682
  function spawnRunner(env) {
13588
- const logFile = join5(LOG_DIR, "runner.log");
13683
+ const logFile = join6(LOG_DIR, "runner.log");
13589
13684
  const outStream = createWriteStream(logFile, { flags: "a" });
13590
13685
  let bin;
13591
13686
  let args;
13592
13687
  let cwd;
13593
13688
  if (BUNDLED_ROOT) {
13594
13689
  bin = process.execPath;
13595
- args = [join5(BUNDLED_ROOT, "runner.js")];
13690
+ args = [join6(BUNDLED_ROOT, "runner.js")];
13596
13691
  cwd = BUNDLED_ROOT;
13597
13692
  } else {
13598
13693
  const runnerDir = resolveAppDir("apps/runner");
@@ -13610,7 +13705,7 @@ env keys: ${Object.keys(env).join(",")}
13610
13705
 
13611
13706
  `
13612
13707
  );
13613
- const child = execa(bin, args, {
13708
+ const child = execa2(bin, args, {
13614
13709
  cwd,
13615
13710
  env: { ...process.env, ...env },
13616
13711
  stdio: ["ignore", "pipe", "pipe"],
@@ -13624,7 +13719,7 @@ env keys: ${Object.keys(env).join(",")}
13624
13719
  return child;
13625
13720
  }
13626
13721
  function spawnWeb(env, opts = {}) {
13627
- const logFile = join5(LOG_DIR, "web.log");
13722
+ const logFile = join6(LOG_DIR, "web.log");
13628
13723
  const outStream = createWriteStream(logFile, { flags: "a" });
13629
13724
  let bin;
13630
13725
  let args;
@@ -13632,8 +13727,8 @@ function spawnWeb(env, opts = {}) {
13632
13727
  let modeLabel;
13633
13728
  if (BUNDLED_ROOT) {
13634
13729
  bin = process.execPath;
13635
- args = [join5(BUNDLED_ROOT, "web", "server.js")];
13636
- cwd = join5(BUNDLED_ROOT, "web");
13730
+ args = [join6(BUNDLED_ROOT, "web", "server.js")];
13731
+ cwd = join6(BUNDLED_ROOT, "web");
13637
13732
  modeLabel = "bundled-standalone";
13638
13733
  } else {
13639
13734
  const webDir = resolveAppDir("apps/web");
@@ -13653,7 +13748,7 @@ env keys: ${Object.keys(env).join(",")}
13653
13748
 
13654
13749
  `
13655
13750
  );
13656
- const child = execa(bin, args, {
13751
+ const child = execa2(bin, args, {
13657
13752
  cwd,
13658
13753
  env: { ...process.env, ...env },
13659
13754
  stdio: ["ignore", "pipe", "pipe"],
@@ -13666,14 +13761,14 @@ env keys: ${Object.keys(env).join(",")}
13666
13761
  }
13667
13762
  function resolveLocalBin(packageDir, binName) {
13668
13763
  const ext = process.platform === "win32" ? ".CMD" : "";
13669
- return join5(packageDir, "node_modules", ".bin", `${binName}${ext}`);
13764
+ return join6(packageDir, "node_modules", ".bin", `${binName}${ext}`);
13670
13765
  }
13671
13766
  function writePids(pids) {
13672
- writeFileSync4(join5(PID_DIR, "processes.json"), JSON.stringify(pids, null, 2), "utf-8");
13767
+ writeFileSync4(join6(PID_DIR, "processes.json"), JSON.stringify(pids, null, 2), "utf-8");
13673
13768
  }
13674
13769
  function readPids() {
13675
- const pidFile = join5(PID_DIR, "processes.json");
13676
- if (!existsSync5(pidFile)) return null;
13770
+ const pidFile = join6(PID_DIR, "processes.json");
13771
+ if (!existsSync6(pidFile)) return null;
13677
13772
  try {
13678
13773
  return JSON.parse(readFileSync5(pidFile, "utf-8"));
13679
13774
  } catch {
@@ -13681,8 +13776,8 @@ function readPids() {
13681
13776
  }
13682
13777
  }
13683
13778
  function clearPids() {
13684
- const pidFile = join5(PID_DIR, "processes.json");
13685
- if (existsSync5(pidFile)) {
13779
+ const pidFile = join6(PID_DIR, "processes.json");
13780
+ if (existsSync6(pidFile)) {
13686
13781
  writeFileSync4(pidFile, JSON.stringify({}), "utf-8");
13687
13782
  }
13688
13783
  }
@@ -13710,8 +13805,8 @@ var init_processes = __esm({
13710
13805
  "use strict";
13711
13806
  init_config();
13712
13807
  BUNDLED_ROOT = (() => {
13713
- const here = dirname3(fileURLToPath2(import.meta.url));
13714
- return existsSync5(join5(here, "runner.js")) ? here : null;
13808
+ const here = dirname4(fileURLToPath3(import.meta.url));
13809
+ return existsSync6(join6(here, "runner.js")) ? here : null;
13715
13810
  })();
13716
13811
  }
13717
13812
  });
@@ -13728,9 +13823,9 @@ async function killSilent(child) {
13728
13823
  await killProcessTree(child);
13729
13824
  }
13730
13825
  async function pidListeningOnPort(port) {
13731
- const { execa: execa2 } = await import("execa");
13826
+ const { execa: execa4 } = await import("execa");
13732
13827
  try {
13733
- const { stdout } = await execa2("netstat", ["-ano"], { reject: false });
13828
+ const { stdout } = await execa4("netstat", ["-ano"], { reject: false });
13734
13829
  for (const line2 of stdout.split(/\r?\n/)) {
13735
13830
  const m = line2.match(/\s+TCP\s+\S+:(\d+)\s+\S+\s+LISTENING\s+(\d+)/);
13736
13831
  if (m && m[1] && m[2] && Number.parseInt(m[1], 10) === port) {
@@ -13766,26 +13861,34 @@ async function runUp(opts = {}) {
13766
13861
  console.log(chalk2.gray(` - ${o.name} on :${o.port} (pid ${o.pid})`));
13767
13862
  }
13768
13863
  console.log(chalk2.yellow("Cleaning up before starting\u2026"));
13769
- const { execa: execa2 } = await import("execa");
13864
+ const { execa: execa4 } = await import("execa");
13770
13865
  const pgOrphan = orphans.find((o) => o.name === "postgres");
13771
13866
  if (pgOrphan) {
13772
- const stopped = await stopOrphanPostgres();
13773
- if (!stopped) {
13867
+ await stopOrphanPostgres();
13868
+ if (isPidAlive(pgOrphan.pid)) {
13774
13869
  try {
13775
13870
  if (process.platform === "win32") {
13776
- await execa2("taskkill", ["/T", "/F", "/PID", String(pgOrphan.pid)], { reject: false });
13871
+ await execa4("taskkill", ["/T", "/F", "/PID", String(pgOrphan.pid)], { reject: false });
13777
13872
  } else {
13778
13873
  process.kill(pgOrphan.pid, "SIGKILL");
13779
13874
  }
13780
13875
  } catch {
13781
13876
  }
13877
+ await waitForPidDead(pgOrphan.pid, 5e3);
13878
+ }
13879
+ if (isPidAlive(pgOrphan.pid)) {
13880
+ const killCmd = process.platform === "win32" ? `taskkill /T /F /PID ${pgOrphan.pid}` : `kill -9 ${pgOrphan.pid}`;
13881
+ throw new Error(
13882
+ `An orphaned Postgres (pid ${pgOrphan.pid}) is still running and holds the shared-memory block for the data dir. Rotating ports won't help \u2014 the SHM segment is keyed to the data dir, not the port.
13883
+ Fix: run \`nodal-agents down\`, then \`${killCmd}\`. If it persists, reboot to clear the orphan kernel object. No data is lost \u2014 pg-data is preserved.`
13884
+ );
13782
13885
  }
13783
13886
  }
13784
13887
  for (const o of orphans) {
13785
13888
  if (o.name === "postgres") continue;
13786
13889
  try {
13787
13890
  if (process.platform === "win32") {
13788
- await execa2("taskkill", ["/T", "/F", "/PID", String(o.pid)], { reject: false });
13891
+ await execa4("taskkill", ["/T", "/F", "/PID", String(o.pid)], { reject: false });
13789
13892
  } else {
13790
13893
  process.kill(o.pid, "SIGKILL");
13791
13894
  }
@@ -13890,6 +13993,20 @@ async function runUp(opts = {}) {
13890
13993
  clearPids();
13891
13994
  throw err;
13892
13995
  }
13996
+ void (async () => {
13997
+ try {
13998
+ const { getInstalledVersion: getInstalledVersion2, getLatestVersion: getLatestVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
13999
+ const installed = getInstalledVersion2();
14000
+ const latest = await Promise.race([
14001
+ getLatestVersion2(),
14002
+ new Promise((resolve2) => setTimeout(() => resolve2(null), 3e3))
14003
+ ]);
14004
+ if (latest !== null && latest !== installed) {
14005
+ console.log(chalk2.cyan(` \u2139 v${latest} available \u2014 run \`nodal-agents update\``));
14006
+ }
14007
+ } catch {
14008
+ }
14009
+ })();
13893
14010
  if (!process.env["NODALAI_NO_BROWSER"]) {
13894
14011
  try {
13895
14012
  await open(webUrl);
@@ -13936,8 +14053,8 @@ __export(down_exports, {
13936
14053
  runDown: () => runDown
13937
14054
  });
13938
14055
  import chalk3 from "chalk";
13939
- import { existsSync as existsSync6 } from "fs";
13940
- import { join as join6 } from "path";
14056
+ import { existsSync as existsSync7 } from "fs";
14057
+ import { join as join7 } from "path";
13941
14058
  function killPid(pid, label) {
13942
14059
  try {
13943
14060
  process.kill(pid, "SIGTERM");
@@ -13954,8 +14071,8 @@ function killPid(pid, label) {
13954
14071
  }
13955
14072
  }
13956
14073
  async function stopPostgresGracefully() {
13957
- const pidFile = join6(PG_DATA_DIR, "postmaster.pid");
13958
- if (!existsSync6(pidFile)) return false;
14074
+ const pidFile = join7(PG_DATA_DIR, "postmaster.pid");
14075
+ if (!existsSync7(pidFile)) return false;
13959
14076
  try {
13960
14077
  const EmbeddedPostgres = (await import("embedded-postgres")).default;
13961
14078
  const pg = new EmbeddedPostgres({
@@ -14006,8 +14123,8 @@ __export(logs_exports, {
14006
14123
  runLogs: () => runLogs
14007
14124
  });
14008
14125
  import chalk4 from "chalk";
14009
- import { existsSync as existsSync7, createReadStream, watch, statSync } from "fs";
14010
- import { join as join7 } from "path";
14126
+ import { existsSync as existsSync8, createReadStream, watch, statSync } from "fs";
14127
+ import { join as join8 } from "path";
14011
14128
  function isServiceName(s) {
14012
14129
  return VALID_SERVICES.includes(s);
14013
14130
  }
@@ -14019,8 +14136,8 @@ async function runLogs(service) {
14019
14136
  }
14020
14137
  const services = target === "all" ? VALID_SERVICES : [target];
14021
14138
  for (const svc of services) {
14022
- const logFile = join7(LOG_DIR, `${svc}.log`);
14023
- if (!existsSync7(logFile)) {
14139
+ const logFile = join8(LOG_DIR, `${svc}.log`);
14140
+ if (!existsSync8(logFile)) {
14024
14141
  console.log(chalk4.gray(` [${svc}] No log file yet at ${logFile}`));
14025
14142
  continue;
14026
14143
  }
@@ -14034,8 +14151,8 @@ async function runLogs(service) {
14034
14151
  });
14035
14152
  }
14036
14153
  if (target !== "all") {
14037
- const logFile = join7(LOG_DIR, `${target}.log`);
14038
- if (!existsSync7(logFile)) {
14154
+ const logFile = join8(LOG_DIR, `${target}.log`);
14155
+ if (!existsSync8(logFile)) {
14039
14156
  console.log(chalk4.yellow("Waiting for log file to be created\u2026"));
14040
14157
  }
14041
14158
  console.log(chalk4.gray("\nFollowing log (Ctrl+C to stop)...\n"));
@@ -14076,7 +14193,7 @@ __export(reset_exports, {
14076
14193
  });
14077
14194
  import chalk5 from "chalk";
14078
14195
  import prompts2 from "prompts";
14079
- import { rmSync, existsSync as existsSync8 } from "fs";
14196
+ import { rmSync, existsSync as existsSync9 } from "fs";
14080
14197
  async function runReset(options = {}) {
14081
14198
  if (!options.yes) {
14082
14199
  const { confirm } = await prompts2({
@@ -14094,7 +14211,7 @@ async function runReset(options = {}) {
14094
14211
  }
14095
14212
  console.log(chalk5.yellow("Stopping any running processes\u2026"));
14096
14213
  await runDown();
14097
- if (existsSync8(CONFIG_DIR)) {
14214
+ if (existsSync9(CONFIG_DIR)) {
14098
14215
  rmSync(CONFIG_DIR, { recursive: true, force: true });
14099
14216
  console.log(chalk5.green(` Removed ${CONFIG_DIR}`));
14100
14217
  } else {
@@ -14112,17 +14229,106 @@ var init_reset = __esm({
14112
14229
  }
14113
14230
  });
14114
14231
 
14232
+ // src/commands/update.ts
14233
+ var update_exports = {};
14234
+ __export(update_exports, {
14235
+ runUpdate: () => runUpdate
14236
+ });
14237
+ import chalk6 from "chalk";
14238
+ import { execa as execa3 } from "execa";
14239
+ async function runUpdate(opts = {}) {
14240
+ const installed = getInstalledVersion();
14241
+ const latest = await getLatestVersion();
14242
+ if (latest === null) {
14243
+ console.log(
14244
+ chalk6.yellow(
14245
+ "Could not reach the npm registry to check for the latest version (offline or npm not in PATH)."
14246
+ )
14247
+ );
14248
+ console.log(
14249
+ chalk6.yellow(
14250
+ "To update manually, run: npm install -g nodal-agents@latest && nodal-agents up"
14251
+ )
14252
+ );
14253
+ process.exit(1);
14254
+ }
14255
+ if (installed === latest) {
14256
+ console.log(chalk6.green(` \u2713 Already on the latest version (${installed})`));
14257
+ return;
14258
+ }
14259
+ console.log(chalk6.cyan(` \u2191 Updating ${installed} \u2192 ${latest}`));
14260
+ console.log(chalk6.gray(" Stopping services\u2026"));
14261
+ try {
14262
+ await runDown();
14263
+ } catch {
14264
+ }
14265
+ console.log(chalk6.gray(" Installing nodal-agents@latest\u2026"));
14266
+ try {
14267
+ await execa3("npm", ["install", "-g", "nodal-agents@latest"], { stdio: "inherit" });
14268
+ } catch (err) {
14269
+ const code = err.code;
14270
+ const message = err instanceof Error ? err.message : String(err);
14271
+ if (code === "ENOENT" || message.includes("ENOENT")) {
14272
+ console.error(
14273
+ chalk6.red(
14274
+ "\n \u2717 npm not found in PATH.\n Make sure Node.js is installed and `npm` is on your PATH,\n then re-run `nodal-agents update`."
14275
+ )
14276
+ );
14277
+ process.exit(1);
14278
+ }
14279
+ if (code === "EACCES" || message.toLowerCase().includes("permission") || message.includes("EACCES")) {
14280
+ console.error(
14281
+ chalk6.red(
14282
+ "\n \u2717 Permission denied while installing.\n Run the command with elevated privileges:\n" + (process.platform === "win32" ? " Run your terminal as Administrator, then retry `nodal-agents update`." : " sudo nodal-agents update")
14283
+ )
14284
+ );
14285
+ process.exit(1);
14286
+ }
14287
+ throw err;
14288
+ }
14289
+ console.log(chalk6.green(" \u2713 nodal-agents updated."));
14290
+ if (opts.noRestart) {
14291
+ console.log(chalk6.cyan(" Run `nodal-agents up` to start the updated stack."));
14292
+ return;
14293
+ }
14294
+ console.log(chalk6.gray(" Starting updated services (detached)\u2026"));
14295
+ try {
14296
+ const child = execa3("nodal-agents", ["up"], {
14297
+ detached: true,
14298
+ stdio: "ignore",
14299
+ // Prefer the PATH-resolved global shim so the new version is loaded.
14300
+ shell: process.platform === "win32"
14301
+ });
14302
+ child.unref();
14303
+ } catch {
14304
+ console.log(
14305
+ chalk6.yellow(
14306
+ " Could not auto-start. Run `nodal-agents up` manually to start the updated stack."
14307
+ )
14308
+ );
14309
+ }
14310
+ console.log(chalk6.green(` \u2713 Updated to ${latest}. Services starting in background.`));
14311
+ }
14312
+ var init_update2 = __esm({
14313
+ "src/commands/update.ts"() {
14314
+ "use strict";
14315
+ init_version();
14316
+ init_down();
14317
+ }
14318
+ });
14319
+
14115
14320
  // src/index.ts
14321
+ init_version();
14116
14322
  import { Command } from "commander";
14117
- import chalk6 from "chalk";
14323
+ import chalk7 from "chalk";
14118
14324
  var program = new Command();
14119
- program.name("nodal-agents").description("Local AI agent platform \u2014 one command to start everything").version("0.0.0").enablePositionalOptions();
14325
+ program.name("nodal-agents").description("Local AI agent platform \u2014 one command to start everything").version(getInstalledVersion()).enablePositionalOptions();
14120
14326
  program.option("--dev", "Run web in `next dev` mode (HMR, no prebuild required)").action(async (opts) => {
14121
14327
  const { runUp: runUp2 } = await Promise.resolve().then(() => (init_up(), up_exports));
14122
14328
  try {
14123
14329
  await runUp2({ dev: opts.dev });
14124
14330
  } catch (err) {
14125
- console.error(chalk6.red("Error:"), err instanceof Error ? err.message : String(err));
14331
+ console.error(chalk7.red("Error:"), err instanceof Error ? err.message : String(err));
14126
14332
  process.exit(1);
14127
14333
  }
14128
14334
  });
@@ -14134,7 +14340,7 @@ program.command("init").description("Interactive wizard \u2014 configure LLM, po
14134
14340
  try {
14135
14341
  await runInit2({ force: opts.force, nonInteractive: opts.nonInteractive });
14136
14342
  } catch (err) {
14137
- console.error(chalk6.red("Error:"), err instanceof Error ? err.message : String(err));
14343
+ console.error(chalk7.red("Error:"), err instanceof Error ? err.message : String(err));
14138
14344
  process.exit(1);
14139
14345
  }
14140
14346
  });
@@ -14143,7 +14349,7 @@ program.command("up").description("Start Postgres, runner, and web \u2014 open b
14143
14349
  try {
14144
14350
  await runUp2({ dev: opts.dev });
14145
14351
  } catch (err) {
14146
- console.error(chalk6.red("Error:"), err instanceof Error ? err.message : String(err));
14352
+ console.error(chalk7.red("Error:"), err instanceof Error ? err.message : String(err));
14147
14353
  process.exit(1);
14148
14354
  }
14149
14355
  });
@@ -14152,7 +14358,7 @@ program.command("down").description("Stop all running Nodal-Agents processes").a
14152
14358
  try {
14153
14359
  await runDown2();
14154
14360
  } catch (err) {
14155
- console.error(chalk6.red("Error:"), err instanceof Error ? err.message : String(err));
14361
+ console.error(chalk7.red("Error:"), err instanceof Error ? err.message : String(err));
14156
14362
  process.exit(1);
14157
14363
  }
14158
14364
  });
@@ -14161,7 +14367,7 @@ program.command("logs [service]").description("Tail logs for a service (web | ru
14161
14367
  try {
14162
14368
  await runLogs2(service);
14163
14369
  } catch (err) {
14164
- console.error(chalk6.red("Error:"), err instanceof Error ? err.message : String(err));
14370
+ console.error(chalk7.red("Error:"), err instanceof Error ? err.message : String(err));
14165
14371
  process.exit(1);
14166
14372
  }
14167
14373
  });
@@ -14170,7 +14376,16 @@ program.command("reset").description("Delete all Nodal-Agents data and config").
14170
14376
  try {
14171
14377
  await runReset2({ yes: opts.yes });
14172
14378
  } catch (err) {
14173
- console.error(chalk6.red("Error:"), err instanceof Error ? err.message : String(err));
14379
+ console.error(chalk7.red("Error:"), err instanceof Error ? err.message : String(err));
14380
+ process.exit(1);
14381
+ }
14382
+ });
14383
+ program.command("update").description("Update nodal-agents to the latest version and restart the stack").option("--no-restart", "Install the update but do not restart services automatically").action(async (opts) => {
14384
+ const { runUpdate: runUpdate2 } = await Promise.resolve().then(() => (init_update2(), update_exports));
14385
+ try {
14386
+ await runUpdate2({ noRestart: opts.restart === false });
14387
+ } catch (err) {
14388
+ console.error(chalk7.red("Error:"), err instanceof Error ? err.message : String(err));
14174
14389
  process.exit(1);
14175
14390
  }
14176
14391
  });