nodal-agents 0.2.1 → 0.3.5

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 (170) hide show
  1. package/README.md +24 -0
  2. package/cli.js +288 -97
  3. package/migrations/0020_agent_workspaces.sql +60 -0
  4. package/migrations/meta/_journal.json +7 -0
  5. package/package.json +5 -2
  6. package/runner.js +2321 -1192
  7. package/web/.next/BUILD_ID +1 -1
  8. package/web/.next/app-path-routes-manifest.json +8 -8
  9. package/web/.next/build-manifest.json +2 -2
  10. package/web/.next/prerender-manifest.json +3 -3
  11. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +3 -2
  12. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js.nft.json +1 -1
  13. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page_client-reference-manifest.js +1 -1
  14. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js +2 -2
  15. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js.nft.json +1 -1
  16. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page_client-reference-manifest.js +1 -1
  17. package/web/.next/server/app/(dashboard)/agents/page.js +2 -2
  18. package/web/.next/server/app/(dashboard)/agents/page.js.nft.json +1 -1
  19. package/web/.next/server/app/(dashboard)/agents/page_client-reference-manifest.js +1 -1
  20. package/web/.next/server/app/(dashboard)/approvals/page.js +2 -2
  21. package/web/.next/server/app/(dashboard)/approvals/page.js.nft.json +1 -1
  22. package/web/.next/server/app/(dashboard)/approvals/page_client-reference-manifest.js +1 -1
  23. package/web/.next/server/app/(dashboard)/automations/page.js +2 -2
  24. package/web/.next/server/app/(dashboard)/automations/page.js.nft.json +1 -1
  25. package/web/.next/server/app/(dashboard)/automations/page_client-reference-manifest.js +1 -1
  26. package/web/.next/server/app/(dashboard)/billing/page.js +2 -2
  27. package/web/.next/server/app/(dashboard)/billing/page.js.nft.json +1 -1
  28. package/web/.next/server/app/(dashboard)/billing/page_client-reference-manifest.js +1 -1
  29. package/web/.next/server/app/(dashboard)/connectors/page.js +2 -2
  30. package/web/.next/server/app/(dashboard)/connectors/page.js.nft.json +1 -1
  31. package/web/.next/server/app/(dashboard)/connectors/page_client-reference-manifest.js +1 -1
  32. package/web/.next/server/app/(dashboard)/credentials/page.js +2 -2
  33. package/web/.next/server/app/(dashboard)/credentials/page.js.nft.json +1 -1
  34. package/web/.next/server/app/(dashboard)/credentials/page_client-reference-manifest.js +1 -1
  35. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +2 -2
  36. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js.nft.json +1 -1
  37. package/web/.next/server/app/(dashboard)/jobs/[id]/page_client-reference-manifest.js +1 -1
  38. package/web/.next/server/app/(dashboard)/jobs/page.js +2 -2
  39. package/web/.next/server/app/(dashboard)/jobs/page.js.nft.json +1 -1
  40. package/web/.next/server/app/(dashboard)/jobs/page_client-reference-manifest.js +1 -1
  41. package/web/.next/server/app/(dashboard)/llm-providers/page.js +2 -2
  42. package/web/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +1 -1
  43. package/web/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
  44. package/web/.next/server/app/(dashboard)/logs/page.js +2 -2
  45. package/web/.next/server/app/(dashboard)/logs/page.js.nft.json +1 -1
  46. package/web/.next/server/app/(dashboard)/logs/page_client-reference-manifest.js +1 -1
  47. package/web/.next/server/app/(dashboard)/mcp/page.js +1 -1
  48. package/web/.next/server/app/(dashboard)/mcp/page.js.nft.json +1 -1
  49. package/web/.next/server/app/(dashboard)/mcp/page_client-reference-manifest.js +1 -1
  50. package/web/.next/server/app/(dashboard)/memories/page.js +2 -2
  51. package/web/.next/server/app/(dashboard)/memories/page.js.nft.json +1 -1
  52. package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
  53. package/web/.next/server/app/(dashboard)/page.js +3 -3
  54. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  55. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  56. package/web/.next/server/app/(dashboard)/settings/page.js +2 -2
  57. package/web/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
  58. package/web/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
  59. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js +2 -2
  60. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js.nft.json +1 -1
  61. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page_client-reference-manifest.js +1 -1
  62. package/web/.next/server/app/(dashboard)/skills/new/page.js +2 -547
  63. package/web/.next/server/app/(dashboard)/skills/new/page.js.nft.json +1 -1
  64. package/web/.next/server/app/(dashboard)/skills/new/page_client-reference-manifest.js +1 -1
  65. package/web/.next/server/app/(dashboard)/skills/page.js +2 -2
  66. package/web/.next/server/app/(dashboard)/skills/page.js.nft.json +1 -1
  67. package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
  68. package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  69. package/web/.next/server/app/_global-error.html +1 -1
  70. package/web/.next/server/app/_global-error.rsc +2 -2
  71. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  72. package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  73. package/web/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  74. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  75. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +2 -2
  76. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  77. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  78. package/web/.next/server/app/_not-found.html +1 -1
  79. package/web/.next/server/app/_not-found.rsc +3 -3
  80. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  81. package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/web/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  83. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  84. package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  86. package/web/.next/server/app/api/oauth/[provider]/callback/route.js +1 -1
  87. package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
  88. package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  89. package/web/.next/server/app/onboarding.html +1 -1
  90. package/web/.next/server/app/onboarding.rsc +3 -3
  91. package/web/.next/server/app/onboarding.segments/_full.segment.rsc +3 -3
  92. package/web/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
  93. package/web/.next/server/app/onboarding.segments/_index.segment.rsc +3 -3
  94. package/web/.next/server/app/onboarding.segments/_tree.segment.rsc +2 -2
  95. package/web/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +1 -1
  96. package/web/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
  97. package/web/.next/server/app-paths-manifest.json +8 -8
  98. package/web/.next/server/chunks/211.js +1 -0
  99. package/web/.next/server/chunks/277.js +1 -0
  100. package/web/.next/server/chunks/{4242.js → 4155.js} +1 -1
  101. package/web/.next/server/chunks/4574.js +1 -1
  102. package/web/.next/server/chunks/6294.js +1 -0
  103. package/web/.next/server/chunks/6937.js +16 -0
  104. package/web/.next/server/chunks/755.js +1 -0
  105. package/web/.next/server/chunks/7741.js +1223 -1
  106. package/web/.next/server/chunks/8362.js +1 -0
  107. package/web/.next/server/middleware-build-manifest.js +1 -1
  108. package/web/.next/server/pages/404.html +1 -1
  109. package/web/.next/server/pages/500.html +1 -1
  110. package/web/.next/server/server-reference-manifest.js +1 -1
  111. package/web/.next/server/server-reference-manifest.json +1 -1
  112. package/web/.next/static/chunks/374-60415230f01c844a.js +1 -0
  113. package/web/.next/static/chunks/639-79c3c2ac769ef007.js +1 -0
  114. package/web/.next/static/chunks/8666-90a1f5d99b79411e.js +1 -0
  115. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-daa833e779c2b465.js +2 -0
  116. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-e6b35d5f361044a9.js +1 -0
  117. package/web/.next/static/chunks/app/(dashboard)/agents/page-b58294bf588f4581.js +1 -0
  118. package/web/.next/static/chunks/app/(dashboard)/approvals/{page-78f89fc75e0098e3.js → page-b9e504918d043b6d.js} +1 -1
  119. package/web/.next/static/chunks/app/(dashboard)/automations/page-70005fabd08ae4a5.js +1 -0
  120. package/web/.next/static/chunks/app/(dashboard)/connectors/page-e12174534c2c2acf.js +1 -0
  121. package/web/.next/static/chunks/app/(dashboard)/credentials/page-f1e797e327ecdf7c.js +1 -0
  122. package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-40172a14d0b1368f.js +1 -0
  123. package/web/.next/static/chunks/app/(dashboard)/jobs/page-d4a3a16745e02fd1.js +1 -0
  124. package/web/.next/static/chunks/app/(dashboard)/layout-d01f5919f54fb37a.js +1 -0
  125. package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-90fb785e2ab32759.js +1 -0
  126. package/web/.next/static/chunks/app/(dashboard)/logs/page-b814deb9854b0cb5.js +1 -0
  127. package/web/.next/static/chunks/app/(dashboard)/mcp/page-efb99104821983ce.js +1 -0
  128. package/web/.next/static/chunks/app/(dashboard)/memories/page-aa46f5f7efbfa262.js +1 -0
  129. package/web/.next/static/chunks/app/(dashboard)/page-fc49d7ed8e472118.js +1 -0
  130. package/web/.next/static/chunks/app/(dashboard)/settings/page-bd180e5a2b3a5bff.js +1 -0
  131. package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-5fab5bdd950d7037.js +1 -0
  132. package/web/.next/static/chunks/app/(dashboard)/skills/new/page-86cfe805e82b43aa.js +1 -0
  133. package/web/.next/static/chunks/app/(dashboard)/skills/page-54c6adeb65475a1a.js +1 -0
  134. package/web/.next/static/css/e7183ce0b0791ec7.css +3 -0
  135. package/web/.next/server/chunks/1985.js +0 -1
  136. package/web/.next/server/chunks/2385.js +0 -1
  137. package/web/.next/server/chunks/2734.js +0 -1
  138. package/web/.next/server/chunks/3455.js +0 -1
  139. package/web/.next/server/chunks/3771.js +0 -1
  140. package/web/.next/server/chunks/414.js +0 -16
  141. package/web/.next/server/chunks/5616.js +0 -25
  142. package/web/.next/server/chunks/6595.js +0 -1
  143. package/web/.next/server/chunks/9323.js +0 -1
  144. package/web/.next/server/chunks/9327.js +0 -1
  145. package/web/.next/server/chunks/9427.js +0 -1
  146. package/web/.next/static/chunks/3422-9078c04bb8df8ce5.js +0 -1
  147. package/web/.next/static/chunks/5365-f17cb5257d2e5cd5.js +0 -1
  148. package/web/.next/static/chunks/6118-c6a8b66d0a3e446c.js +0 -1
  149. package/web/.next/static/chunks/9457-a94614afa0c2954c.js +0 -1
  150. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-faaf5d7084db1fbc.js +0 -1
  151. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-8f2ca5f7fb70eab4.js +0 -1
  152. package/web/.next/static/chunks/app/(dashboard)/agents/page-9f2c2130d2f4de95.js +0 -1
  153. package/web/.next/static/chunks/app/(dashboard)/automations/page-7f71b6201decf4e5.js +0 -1
  154. package/web/.next/static/chunks/app/(dashboard)/connectors/page-cd7ee7dd1458ae47.js +0 -1
  155. package/web/.next/static/chunks/app/(dashboard)/credentials/page-73d252ca88f194f1.js +0 -1
  156. package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-1740cbacc93cb4ac.js +0 -1
  157. package/web/.next/static/chunks/app/(dashboard)/jobs/page-d340869dc040dc5d.js +0 -1
  158. package/web/.next/static/chunks/app/(dashboard)/layout-c63442ae0b085dcc.js +0 -1
  159. package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-2f472dc5ade5697f.js +0 -1
  160. package/web/.next/static/chunks/app/(dashboard)/logs/page-34e669d9863bdd6e.js +0 -1
  161. package/web/.next/static/chunks/app/(dashboard)/mcp/page-1b2bf2315b3b213e.js +0 -1
  162. package/web/.next/static/chunks/app/(dashboard)/memories/page-1e537215e80cb0c3.js +0 -1
  163. package/web/.next/static/chunks/app/(dashboard)/page-fe750fe3304e8025.js +0 -1
  164. package/web/.next/static/chunks/app/(dashboard)/settings/page-499e32816fa8881b.js +0 -1
  165. package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-6a4102dd95d3a6e1.js +0 -1
  166. package/web/.next/static/chunks/app/(dashboard)/skills/new/page-ca70dd7b9f24b76b.js +0 -1
  167. package/web/.next/static/chunks/app/(dashboard)/skills/page-a6736503d29a2f1c.js +0 -1
  168. package/web/.next/static/css/a161c147ca3606b3.css +0 -3
  169. /package/web/.next/static/{c71dl6akFEjsFRL4fGw_M → BQkpUUo2Ux3Z5moUBYJBq}/_buildManifest.js +0 -0
  170. /package/web/.next/static/{c71dl6akFEjsFRL4fGw_M → BQkpUUo2Ux3Z5moUBYJBq}/_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;
@@ -13587,28 +13666,28 @@ async function waitForPidDead(pid, timeoutMs = 5e3) {
13587
13666
  return !isPidAlive(pid);
13588
13667
  }
13589
13668
  function resolveAppDir(appName) {
13590
- const here = fileURLToPath2(import.meta.url);
13591
- const fileDir = dirname3(here);
13669
+ const here = fileURLToPath3(import.meta.url);
13670
+ const fileDir = dirname4(here);
13592
13671
  let cursor = fileDir;
13593
13672
  for (let i = 0; i < 8; i++) {
13594
- if (existsSync5(join5(cursor, "pnpm-workspace.yaml"))) {
13595
- return join5(cursor, appName);
13673
+ if (existsSync6(join6(cursor, "pnpm-workspace.yaml"))) {
13674
+ return join6(cursor, appName);
13596
13675
  }
13597
- const parent = dirname3(cursor);
13676
+ const parent = dirname4(cursor);
13598
13677
  if (parent === cursor) break;
13599
13678
  cursor = parent;
13600
13679
  }
13601
13680
  return resolve(fileDir, "..", "..", "..", "..", appName);
13602
13681
  }
13603
13682
  function spawnRunner(env) {
13604
- const logFile = join5(LOG_DIR, "runner.log");
13683
+ const logFile = join6(LOG_DIR, "runner.log");
13605
13684
  const outStream = createWriteStream(logFile, { flags: "a" });
13606
13685
  let bin;
13607
13686
  let args;
13608
13687
  let cwd;
13609
13688
  if (BUNDLED_ROOT) {
13610
13689
  bin = process.execPath;
13611
- args = [join5(BUNDLED_ROOT, "runner.js")];
13690
+ args = [join6(BUNDLED_ROOT, "runner.js")];
13612
13691
  cwd = BUNDLED_ROOT;
13613
13692
  } else {
13614
13693
  const runnerDir = resolveAppDir("apps/runner");
@@ -13626,7 +13705,7 @@ env keys: ${Object.keys(env).join(",")}
13626
13705
 
13627
13706
  `
13628
13707
  );
13629
- const child = execa(bin, args, {
13708
+ const child = execa2(bin, args, {
13630
13709
  cwd,
13631
13710
  env: { ...process.env, ...env },
13632
13711
  stdio: ["ignore", "pipe", "pipe"],
@@ -13640,7 +13719,7 @@ env keys: ${Object.keys(env).join(",")}
13640
13719
  return child;
13641
13720
  }
13642
13721
  function spawnWeb(env, opts = {}) {
13643
- const logFile = join5(LOG_DIR, "web.log");
13722
+ const logFile = join6(LOG_DIR, "web.log");
13644
13723
  const outStream = createWriteStream(logFile, { flags: "a" });
13645
13724
  let bin;
13646
13725
  let args;
@@ -13648,8 +13727,8 @@ function spawnWeb(env, opts = {}) {
13648
13727
  let modeLabel;
13649
13728
  if (BUNDLED_ROOT) {
13650
13729
  bin = process.execPath;
13651
- args = [join5(BUNDLED_ROOT, "web", "server.js")];
13652
- cwd = join5(BUNDLED_ROOT, "web");
13730
+ args = [join6(BUNDLED_ROOT, "web", "server.js")];
13731
+ cwd = join6(BUNDLED_ROOT, "web");
13653
13732
  modeLabel = "bundled-standalone";
13654
13733
  } else {
13655
13734
  const webDir = resolveAppDir("apps/web");
@@ -13669,7 +13748,7 @@ env keys: ${Object.keys(env).join(",")}
13669
13748
 
13670
13749
  `
13671
13750
  );
13672
- const child = execa(bin, args, {
13751
+ const child = execa2(bin, args, {
13673
13752
  cwd,
13674
13753
  env: { ...process.env, ...env },
13675
13754
  stdio: ["ignore", "pipe", "pipe"],
@@ -13682,14 +13761,14 @@ env keys: ${Object.keys(env).join(",")}
13682
13761
  }
13683
13762
  function resolveLocalBin(packageDir, binName) {
13684
13763
  const ext = process.platform === "win32" ? ".CMD" : "";
13685
- return join5(packageDir, "node_modules", ".bin", `${binName}${ext}`);
13764
+ return join6(packageDir, "node_modules", ".bin", `${binName}${ext}`);
13686
13765
  }
13687
13766
  function writePids(pids) {
13688
- 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");
13689
13768
  }
13690
13769
  function readPids() {
13691
- const pidFile = join5(PID_DIR, "processes.json");
13692
- if (!existsSync5(pidFile)) return null;
13770
+ const pidFile = join6(PID_DIR, "processes.json");
13771
+ if (!existsSync6(pidFile)) return null;
13693
13772
  try {
13694
13773
  return JSON.parse(readFileSync5(pidFile, "utf-8"));
13695
13774
  } catch {
@@ -13697,8 +13776,8 @@ function readPids() {
13697
13776
  }
13698
13777
  }
13699
13778
  function clearPids() {
13700
- const pidFile = join5(PID_DIR, "processes.json");
13701
- if (existsSync5(pidFile)) {
13779
+ const pidFile = join6(PID_DIR, "processes.json");
13780
+ if (existsSync6(pidFile)) {
13702
13781
  writeFileSync4(pidFile, JSON.stringify({}), "utf-8");
13703
13782
  }
13704
13783
  }
@@ -13726,8 +13805,8 @@ var init_processes = __esm({
13726
13805
  "use strict";
13727
13806
  init_config();
13728
13807
  BUNDLED_ROOT = (() => {
13729
- const here = dirname3(fileURLToPath2(import.meta.url));
13730
- 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;
13731
13810
  })();
13732
13811
  }
13733
13812
  });
@@ -13744,9 +13823,9 @@ async function killSilent(child) {
13744
13823
  await killProcessTree(child);
13745
13824
  }
13746
13825
  async function pidListeningOnPort(port) {
13747
- const { execa: execa2 } = await import("execa");
13826
+ const { execa: execa4 } = await import("execa");
13748
13827
  try {
13749
- const { stdout } = await execa2("netstat", ["-ano"], { reject: false });
13828
+ const { stdout } = await execa4("netstat", ["-ano"], { reject: false });
13750
13829
  for (const line2 of stdout.split(/\r?\n/)) {
13751
13830
  const m = line2.match(/\s+TCP\s+\S+:(\d+)\s+\S+\s+LISTENING\s+(\d+)/);
13752
13831
  if (m && m[1] && m[2] && Number.parseInt(m[1], 10) === port) {
@@ -13782,14 +13861,14 @@ async function runUp(opts = {}) {
13782
13861
  console.log(chalk2.gray(` - ${o.name} on :${o.port} (pid ${o.pid})`));
13783
13862
  }
13784
13863
  console.log(chalk2.yellow("Cleaning up before starting\u2026"));
13785
- const { execa: execa2 } = await import("execa");
13864
+ const { execa: execa4 } = await import("execa");
13786
13865
  const pgOrphan = orphans.find((o) => o.name === "postgres");
13787
13866
  if (pgOrphan) {
13788
13867
  await stopOrphanPostgres();
13789
13868
  if (isPidAlive(pgOrphan.pid)) {
13790
13869
  try {
13791
13870
  if (process.platform === "win32") {
13792
- await execa2("taskkill", ["/T", "/F", "/PID", String(pgOrphan.pid)], { reject: false });
13871
+ await execa4("taskkill", ["/T", "/F", "/PID", String(pgOrphan.pid)], { reject: false });
13793
13872
  } else {
13794
13873
  process.kill(pgOrphan.pid, "SIGKILL");
13795
13874
  }
@@ -13809,7 +13888,7 @@ async function runUp(opts = {}) {
13809
13888
  if (o.name === "postgres") continue;
13810
13889
  try {
13811
13890
  if (process.platform === "win32") {
13812
- await execa2("taskkill", ["/T", "/F", "/PID", String(o.pid)], { reject: false });
13891
+ await execa4("taskkill", ["/T", "/F", "/PID", String(o.pid)], { reject: false });
13813
13892
  } else {
13814
13893
  process.kill(o.pid, "SIGKILL");
13815
13894
  }
@@ -13914,6 +13993,20 @@ async function runUp(opts = {}) {
13914
13993
  clearPids();
13915
13994
  throw err;
13916
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
+ })();
13917
14010
  if (!process.env["NODALAI_NO_BROWSER"]) {
13918
14011
  try {
13919
14012
  await open(webUrl);
@@ -13960,8 +14053,8 @@ __export(down_exports, {
13960
14053
  runDown: () => runDown
13961
14054
  });
13962
14055
  import chalk3 from "chalk";
13963
- import { existsSync as existsSync6 } from "fs";
13964
- import { join as join6 } from "path";
14056
+ import { existsSync as existsSync7 } from "fs";
14057
+ import { join as join7 } from "path";
13965
14058
  function killPid(pid, label) {
13966
14059
  try {
13967
14060
  process.kill(pid, "SIGTERM");
@@ -13978,8 +14071,8 @@ function killPid(pid, label) {
13978
14071
  }
13979
14072
  }
13980
14073
  async function stopPostgresGracefully() {
13981
- const pidFile = join6(PG_DATA_DIR, "postmaster.pid");
13982
- if (!existsSync6(pidFile)) return false;
14074
+ const pidFile = join7(PG_DATA_DIR, "postmaster.pid");
14075
+ if (!existsSync7(pidFile)) return false;
13983
14076
  try {
13984
14077
  const EmbeddedPostgres = (await import("embedded-postgres")).default;
13985
14078
  const pg = new EmbeddedPostgres({
@@ -14030,8 +14123,8 @@ __export(logs_exports, {
14030
14123
  runLogs: () => runLogs
14031
14124
  });
14032
14125
  import chalk4 from "chalk";
14033
- import { existsSync as existsSync7, createReadStream, watch, statSync } from "fs";
14034
- import { join as join7 } from "path";
14126
+ import { existsSync as existsSync8, createReadStream, watch, statSync } from "fs";
14127
+ import { join as join8 } from "path";
14035
14128
  function isServiceName(s) {
14036
14129
  return VALID_SERVICES.includes(s);
14037
14130
  }
@@ -14043,8 +14136,8 @@ async function runLogs(service) {
14043
14136
  }
14044
14137
  const services = target === "all" ? VALID_SERVICES : [target];
14045
14138
  for (const svc of services) {
14046
- const logFile = join7(LOG_DIR, `${svc}.log`);
14047
- if (!existsSync7(logFile)) {
14139
+ const logFile = join8(LOG_DIR, `${svc}.log`);
14140
+ if (!existsSync8(logFile)) {
14048
14141
  console.log(chalk4.gray(` [${svc}] No log file yet at ${logFile}`));
14049
14142
  continue;
14050
14143
  }
@@ -14058,8 +14151,8 @@ async function runLogs(service) {
14058
14151
  });
14059
14152
  }
14060
14153
  if (target !== "all") {
14061
- const logFile = join7(LOG_DIR, `${target}.log`);
14062
- if (!existsSync7(logFile)) {
14154
+ const logFile = join8(LOG_DIR, `${target}.log`);
14155
+ if (!existsSync8(logFile)) {
14063
14156
  console.log(chalk4.yellow("Waiting for log file to be created\u2026"));
14064
14157
  }
14065
14158
  console.log(chalk4.gray("\nFollowing log (Ctrl+C to stop)...\n"));
@@ -14100,7 +14193,7 @@ __export(reset_exports, {
14100
14193
  });
14101
14194
  import chalk5 from "chalk";
14102
14195
  import prompts2 from "prompts";
14103
- import { rmSync, existsSync as existsSync8 } from "fs";
14196
+ import { rmSync, existsSync as existsSync9 } from "fs";
14104
14197
  async function runReset(options = {}) {
14105
14198
  if (!options.yes) {
14106
14199
  const { confirm } = await prompts2({
@@ -14118,7 +14211,7 @@ async function runReset(options = {}) {
14118
14211
  }
14119
14212
  console.log(chalk5.yellow("Stopping any running processes\u2026"));
14120
14213
  await runDown();
14121
- if (existsSync8(CONFIG_DIR)) {
14214
+ if (existsSync9(CONFIG_DIR)) {
14122
14215
  rmSync(CONFIG_DIR, { recursive: true, force: true });
14123
14216
  console.log(chalk5.green(` Removed ${CONFIG_DIR}`));
14124
14217
  } else {
@@ -14136,17 +14229,106 @@ var init_reset = __esm({
14136
14229
  }
14137
14230
  });
14138
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
+
14139
14320
  // src/index.ts
14321
+ init_version();
14140
14322
  import { Command } from "commander";
14141
- import chalk6 from "chalk";
14323
+ import chalk7 from "chalk";
14142
14324
  var program = new Command();
14143
- 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();
14144
14326
  program.option("--dev", "Run web in `next dev` mode (HMR, no prebuild required)").action(async (opts) => {
14145
14327
  const { runUp: runUp2 } = await Promise.resolve().then(() => (init_up(), up_exports));
14146
14328
  try {
14147
14329
  await runUp2({ dev: opts.dev });
14148
14330
  } catch (err) {
14149
- 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));
14150
14332
  process.exit(1);
14151
14333
  }
14152
14334
  });
@@ -14158,7 +14340,7 @@ program.command("init").description("Interactive wizard \u2014 configure LLM, po
14158
14340
  try {
14159
14341
  await runInit2({ force: opts.force, nonInteractive: opts.nonInteractive });
14160
14342
  } catch (err) {
14161
- 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));
14162
14344
  process.exit(1);
14163
14345
  }
14164
14346
  });
@@ -14167,7 +14349,7 @@ program.command("up").description("Start Postgres, runner, and web \u2014 open b
14167
14349
  try {
14168
14350
  await runUp2({ dev: opts.dev });
14169
14351
  } catch (err) {
14170
- 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));
14171
14353
  process.exit(1);
14172
14354
  }
14173
14355
  });
@@ -14176,7 +14358,7 @@ program.command("down").description("Stop all running Nodal-Agents processes").a
14176
14358
  try {
14177
14359
  await runDown2();
14178
14360
  } catch (err) {
14179
- 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));
14180
14362
  process.exit(1);
14181
14363
  }
14182
14364
  });
@@ -14185,7 +14367,7 @@ program.command("logs [service]").description("Tail logs for a service (web | ru
14185
14367
  try {
14186
14368
  await runLogs2(service);
14187
14369
  } catch (err) {
14188
- 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));
14189
14371
  process.exit(1);
14190
14372
  }
14191
14373
  });
@@ -14194,7 +14376,16 @@ program.command("reset").description("Delete all Nodal-Agents data and config").
14194
14376
  try {
14195
14377
  await runReset2({ yes: opts.yes });
14196
14378
  } catch (err) {
14197
- 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));
14198
14389
  process.exit(1);
14199
14390
  }
14200
14391
  });