nodal-agents 0.7.0 → 0.7.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.
- package/cli.js +453 -37
- package/migrations/0054_audit2_db_integrity.sql +52 -0
- package/migrations/0055_entity_settings.sql +22 -0
- package/migrations/0056_agents_skills_entity_scoped_unique.sql +23 -0
- package/migrations/0057_telegram_allowed_chats.sql +34 -0
- package/migrations/0058_entity_llm_keys_context_window.sql +6 -0
- package/migrations/0059_agent_jobs_conversation_id.sql +10 -0
- package/migrations/meta/_journal.json +42 -0
- package/package.json +1 -1
- package/runner.js +47940 -35285
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +7 -7
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/routes-manifest.json +32 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +3 -3
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js +2 -2
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/agents/page.js +2 -2
- package/web/.next/server/app/(dashboard)/agents/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/agents/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/approvals/page.js +2 -2
- package/web/.next/server/app/(dashboard)/approvals/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/approvals/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/automations/page.js +2 -2
- package/web/.next/server/app/(dashboard)/automations/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/automations/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/chat/page.js +2 -2
- package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/connectors/page.js +1 -1
- package/web/.next/server/app/(dashboard)/connectors/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/connectors/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/credentials/page.js +1 -1
- package/web/.next/server/app/(dashboard)/credentials/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/credentials/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +2 -2
- package/web/.next/server/app/(dashboard)/jobs/[id]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/jobs/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/jobs/page.js +2 -2
- package/web/.next/server/app/(dashboard)/jobs/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/jobs/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/learned-skills/page.js +2 -2
- package/web/.next/server/app/(dashboard)/learned-skills/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/learned-skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/llm-providers/page.js +1 -1
- package/web/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/logs/page.js +2 -2
- package/web/.next/server/app/(dashboard)/logs/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/logs/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/mcp/page.js +1 -1
- package/web/.next/server/app/(dashboard)/mcp/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/mcp/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/memories/page.js +2 -2
- package/web/.next/server/app/(dashboard)/memories/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page.js +2 -2
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/settings/page.js +2 -2
- package/web/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/settings/root-context/page.js +2 -2
- package/web/.next/server/app/(dashboard)/settings/root-context/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/settings/root-context/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js +2 -2
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/new/page.js +2 -2
- package/web/.next/server/app/(dashboard)/skills/new/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/skills/new/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/page.js +2 -2
- package/web/.next/server/app/(dashboard)/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +1 -1
- package/web/.next/server/app/_global-error.rsc +2 -2
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +2 -2
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/server/app/_not-found.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/api/auth/[...all]/route.js +1 -1
- package/web/.next/server/app/api/auth/[...all]/route.js.nft.json +1 -1
- package/web/.next/server/app/api/oauth/[provider]/callback/route.js +1 -1
- package/web/.next/server/app/api/oauth/[provider]/callback/route.js.nft.json +1 -1
- package/web/.next/server/app/api/oauth/[provider]/start/route.js +1 -1
- package/web/.next/server/app/api/oauth/[provider]/start/route.js.nft.json +1 -1
- package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/onboarding/page.js +122 -94
- package/web/.next/server/app/onboarding/page.js.nft.json +1 -1
- package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app-paths-manifest.json +7 -7
- package/web/.next/server/chunks/2043.js +1 -0
- package/web/.next/server/chunks/2046.js +1 -0
- package/web/.next/server/chunks/2713.js +56 -28
- package/web/.next/server/chunks/4479.js +1 -0
- package/web/.next/server/chunks/4574.js +1 -1
- package/web/.next/server/chunks/{4488.js → 5744.js} +5 -4
- package/web/.next/server/chunks/{8464.js → 7388.js} +1 -2
- package/web/.next/server/chunks/7560.js +1 -0
- package/web/.next/server/chunks/{3154.js → 960.js} +10 -10
- package/web/.next/server/middleware-build-manifest.js +1 -1
- package/web/.next/server/pages/404.html +1 -1
- package/web/.next/server/pages/500.html +1 -1
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/static/chunks/3114-d846062bc8434b58.js +1 -0
- package/web/.next/static/chunks/4893-730f4c62f76c6c48.js +1 -0
- package/web/.next/static/chunks/8097-bad68cd78aa59a10.js +1 -0
- package/web/.next/static/chunks/{9836-6830cb9d49ad261f.js → 9836-a69e19544b669a71.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-18973089c62eb12e.js +6 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-7913483b3bfd7dda.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/page-eb7b72d4d7bfde01.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/approvals/{page-9a01cb723a6ccff0.js → page-dc8dc54ed07bc81a.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/automations/page-1034613ecd525509.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/chat/page-4d0e2fd060f2aafb.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/connectors/{page-21d6c042800c8cd3.js → page-8bad5bc43f197471.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-116a001adade3a1e.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/jobs/page-0fc661a4e7588bdb.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/layout-9068b164d3ae4853.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-01f1b68b334bf442.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-99fd3e7b631131e8.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/logs/{page-0bca733315e8a208.js → page-94babd54bdd0d1af.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-2d190bdd49b7b4b4.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/memories/page-ac872edd26efe323.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/page-8cd3f1d4d072ac25.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/settings/page-aed8fc039205ccd7.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-a2eb786f9d897813.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-7f87e2c66d1ceea4.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/new/page-54734a6e0675887a.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/{page-54e939d40c8b5b6d.js → page-cae26ae45b9b11e5.js} +1 -1
- package/web/.next/static/chunks/app/onboarding/{page-a4116e2f430eaff8.js → page-e63eee720a95baac.js} +2 -2
- package/web/.next/static/css/{eff119dd24f92093.css → 289a2e89c0d093fd.css} +1 -1
- package/web/.next/server/chunks/3223.js +0 -1
- package/web/.next/server/chunks/4626.js +0 -1
- package/web/.next/server/chunks/8971.js +0 -1
- package/web/.next/server/chunks/9043.js +0 -1
- package/web/.next/static/chunks/4964-08171c654e41b0a4.js +0 -1
- package/web/.next/static/chunks/609-a0c06f3592427d58.js +0 -1
- package/web/.next/static/chunks/7704-7ba1c3d45662ffc8.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-fa5717f0d0450659.js +0 -6
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-73012222871598aa.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/page-7e9616b28c098e9e.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/automations/page-85c985cd687406ed.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/chat/page-a6cc7a58986c0340.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-f6b993a88c70a2fe.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/page-d0d6b9db1b68451f.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/layout-d2ac2646967496bb.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-ec6312f6dfd7ee23.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-1fb26463eddf509c.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-ff56ecf2f65f5466.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/memories/page-fe4d9438c93750d6.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/page-9402fe2931216f4b.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/page-995662f10ebff365.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-785b6ce3e4c5890b.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-2106201c4df8b5a4.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/new/page-0135bcb86ab60dc2.js +0 -1
- /package/web/.next/static/{KFac7DQ7gdp9XPQ2bnb-2 → XF1rXMlGkoyAu_GI5theh}/_buildManifest.js +0 -0
- /package/web/.next/static/{KFac7DQ7gdp9XPQ2bnb-2 → XF1rXMlGkoyAu_GI5theh}/_ssgManifest.js +0 -0
package/cli.js
CHANGED
|
@@ -13,7 +13,8 @@ var __export = (target, all) => {
|
|
|
13
13
|
var version_exports = {};
|
|
14
14
|
__export(version_exports, {
|
|
15
15
|
getInstalledVersion: () => getInstalledVersion,
|
|
16
|
-
getLatestVersion: () => getLatestVersion
|
|
16
|
+
getLatestVersion: () => getLatestVersion,
|
|
17
|
+
isNewerVersion: () => isNewerVersion
|
|
17
18
|
});
|
|
18
19
|
import { createRequire } from "module";
|
|
19
20
|
import { fileURLToPath } from "url";
|
|
@@ -49,6 +50,20 @@ async function getLatestVersion() {
|
|
|
49
50
|
return null;
|
|
50
51
|
}
|
|
51
52
|
}
|
|
53
|
+
function parseVersionTriple(v) {
|
|
54
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
55
|
+
if (!match) return null;
|
|
56
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
57
|
+
}
|
|
58
|
+
function isNewerVersion(candidate, current) {
|
|
59
|
+
const a = parseVersionTriple(candidate);
|
|
60
|
+
const b2 = parseVersionTriple(current);
|
|
61
|
+
if (!a || !b2) return false;
|
|
62
|
+
for (let i = 0; i < 3; i++) {
|
|
63
|
+
if (a[i] !== b2[i]) return a[i] > b2[i];
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
52
67
|
var init_version = __esm({
|
|
53
68
|
"src/lib/version.ts"() {
|
|
54
69
|
"use strict";
|
|
@@ -59,8 +74,22 @@ var init_version = __esm({
|
|
|
59
74
|
import { z } from "zod";
|
|
60
75
|
import { homedir } from "os";
|
|
61
76
|
import { join as join2 } from "path";
|
|
62
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync as existsSync2 } from "fs";
|
|
77
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync as existsSync2, chmodSync } from "fs";
|
|
78
|
+
import { execFileSync } from "child_process";
|
|
63
79
|
import { randomBytes } from "crypto";
|
|
80
|
+
function restrictFileToOwner(path) {
|
|
81
|
+
try {
|
|
82
|
+
chmodSync(path, 384);
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
if (process.platform !== "win32") return;
|
|
86
|
+
const user = process.env["USERNAME"] || process.env["USER"];
|
|
87
|
+
if (!user) return;
|
|
88
|
+
try {
|
|
89
|
+
execFileSync("icacls", [path, "/inheritance:r", "/grant:r", `${user}:F`], { stdio: "ignore" });
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
64
93
|
function ensureConfigDir() {
|
|
65
94
|
for (const dir of [CONFIG_DIR, PID_DIR, LOG_DIR, PG_DATA_DIR]) {
|
|
66
95
|
if (!existsSync2(dir)) {
|
|
@@ -81,11 +110,22 @@ function readConfig(configFile = CONFIG_FILE) {
|
|
|
81
110
|
parsed = { ...parsed, serverActionsKey: randomBytes(32).toString("base64") };
|
|
82
111
|
writeConfig(parsed, configFile);
|
|
83
112
|
}
|
|
113
|
+
if (!parsed.authSecret) {
|
|
114
|
+
parsed = { ...parsed, authSecret: randomBytes(32).toString("base64") };
|
|
115
|
+
writeConfig(parsed, configFile);
|
|
116
|
+
}
|
|
117
|
+
if (configFile === CONFIG_FILE) {
|
|
118
|
+
try {
|
|
119
|
+
chmodSync(configFile, 384);
|
|
120
|
+
} catch {
|
|
121
|
+
}
|
|
122
|
+
}
|
|
84
123
|
return parsed;
|
|
85
124
|
}
|
|
86
125
|
function writeConfig(config, configFile = CONFIG_FILE) {
|
|
87
126
|
if (configFile === CONFIG_FILE) ensureConfigDir();
|
|
88
|
-
writeFileSync(configFile, JSON.stringify(config, null, 2), "utf-8");
|
|
127
|
+
writeFileSync(configFile, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
|
|
128
|
+
restrictFileToOwner(configFile);
|
|
89
129
|
}
|
|
90
130
|
var ConfigSchema, CONFIG_DIR, CONFIG_FILE, PID_DIR, LOG_DIR, PG_DATA_DIR;
|
|
91
131
|
var init_config = __esm({
|
|
@@ -133,6 +173,18 @@ var init_config = __esm({
|
|
|
133
173
|
* files; `readConfig` auto-mints + persists when absent.
|
|
134
174
|
*/
|
|
135
175
|
serverActionsKey: z.string().length(44).optional(),
|
|
176
|
+
/**
|
|
177
|
+
* Base64-encoded 32-byte AES key used as better-auth's AUTH_SECRET (session
|
|
178
|
+
* cookie signing) for the web process. Deliberately SEPARATE from
|
|
179
|
+
* `workerSecret` (M-4): `workerSecret` is the runner's auth frontier — a
|
|
180
|
+
* leak of it must not also let an attacker forge a signed session cookie.
|
|
181
|
+
*
|
|
182
|
+
* Optional in the schema for back-compat with pre-existing config.json
|
|
183
|
+
* files; `readConfig` auto-mints + persists when absent. On an existing
|
|
184
|
+
* install this invalidates any active better-auth sessions ONCE (users
|
|
185
|
+
* re-login) — an acceptable one-time cost for closing the amplification.
|
|
186
|
+
*/
|
|
187
|
+
authSecret: z.string().length(44).optional(),
|
|
136
188
|
bind: z.enum(["loopback", "lan"]).default("loopback"),
|
|
137
189
|
bearerToken: z.string().optional(),
|
|
138
190
|
/**
|
|
@@ -6037,6 +6089,12 @@ var init_policies = __esm({
|
|
|
6037
6089
|
});
|
|
6038
6090
|
|
|
6039
6091
|
// ../../node_modules/.pnpm/drizzle-orm@0.45.2_@electri_96bd1661c4535b1384d37581a38c7c74/node_modules/drizzle-orm/pg-core/primary-keys.js
|
|
6092
|
+
function primaryKey(...config) {
|
|
6093
|
+
if (config[0].columns) {
|
|
6094
|
+
return new PrimaryKeyBuilder(config[0].columns, config[0].name);
|
|
6095
|
+
}
|
|
6096
|
+
return new PrimaryKeyBuilder(config);
|
|
6097
|
+
}
|
|
6040
6098
|
var PrimaryKeyBuilder, PrimaryKey;
|
|
6041
6099
|
var init_primary_keys = __esm({
|
|
6042
6100
|
"../../node_modules/.pnpm/drizzle-orm@0.45.2_@electri_96bd1661c4535b1384d37581a38c7c74/node_modules/drizzle-orm/pg-core/primary-keys.js"() {
|
|
@@ -6400,19 +6458,19 @@ function extractTablesRelationalConfig(schema, configHelpers) {
|
|
|
6400
6458
|
const relations2 = value.config(
|
|
6401
6459
|
configHelpers(value.table)
|
|
6402
6460
|
);
|
|
6403
|
-
let
|
|
6461
|
+
let primaryKey2;
|
|
6404
6462
|
for (const [relationName, relation] of Object.entries(relations2)) {
|
|
6405
6463
|
if (tableName) {
|
|
6406
6464
|
const tableConfig = tablesConfig[tableName];
|
|
6407
6465
|
tableConfig.relations[relationName] = relation;
|
|
6408
|
-
if (
|
|
6409
|
-
tableConfig.primaryKey.push(...
|
|
6466
|
+
if (primaryKey2) {
|
|
6467
|
+
tableConfig.primaryKey.push(...primaryKey2);
|
|
6410
6468
|
}
|
|
6411
6469
|
} else {
|
|
6412
6470
|
if (!(dbName in relationsBuffer)) {
|
|
6413
6471
|
relationsBuffer[dbName] = {
|
|
6414
6472
|
relations: {},
|
|
6415
|
-
primaryKey
|
|
6473
|
+
primaryKey: primaryKey2
|
|
6416
6474
|
};
|
|
6417
6475
|
}
|
|
6418
6476
|
relationsBuffer[dbName].relations[relationName] = relation;
|
|
@@ -11231,6 +11289,10 @@ var init_job = __esm({
|
|
|
11231
11289
|
task: z7.string().min(1),
|
|
11232
11290
|
original_task: z7.string().nullable(),
|
|
11233
11291
|
chat_id: z7.string().nullable(),
|
|
11292
|
+
// Groups jobs from the same conversational thread (migration 0059) — see
|
|
11293
|
+
// apps/runner/src/job/conversation-id.ts for the stamping rule. Null for
|
|
11294
|
+
// non-conversational jobs (cron/schedule/webhook with no parent).
|
|
11295
|
+
conversation_id: z7.string().guid().nullable(),
|
|
11234
11296
|
system_prompt: z7.string().nullable(),
|
|
11235
11297
|
messages: z7.array(z7.record(z7.string(), z7.unknown())),
|
|
11236
11298
|
tools_used: z7.array(z7.string()),
|
|
@@ -11271,6 +11333,7 @@ var init_job = __esm({
|
|
|
11271
11333
|
status: JobStatusSchema.default("pending"),
|
|
11272
11334
|
original_task: z7.string().nullable().optional(),
|
|
11273
11335
|
chat_id: z7.string().nullable().optional(),
|
|
11336
|
+
conversation_id: z7.string().guid().nullable().optional(),
|
|
11274
11337
|
system_prompt: z7.string().nullable().optional(),
|
|
11275
11338
|
request_id: z7.string().nullable().optional(),
|
|
11276
11339
|
parent_job_id: z7.string().guid().nullable().optional()
|
|
@@ -11278,6 +11341,38 @@ var init_job = __esm({
|
|
|
11278
11341
|
}
|
|
11279
11342
|
});
|
|
11280
11343
|
|
|
11344
|
+
// ../../packages/shared/src/entities/job-classification.ts
|
|
11345
|
+
var CHAT_DELIVERY_TOOLS, CHAT_CONTEXT_READ_TOOLS, CHAT_ALLOWED_TOOLS;
|
|
11346
|
+
var init_job_classification = __esm({
|
|
11347
|
+
"../../packages/shared/src/entities/job-classification.ts"() {
|
|
11348
|
+
"use strict";
|
|
11349
|
+
CHAT_DELIVERY_TOOLS = [
|
|
11350
|
+
"telegram_send_message",
|
|
11351
|
+
"send_image",
|
|
11352
|
+
"send_file",
|
|
11353
|
+
"send_video",
|
|
11354
|
+
"send_audio",
|
|
11355
|
+
"send_voice",
|
|
11356
|
+
"return_result",
|
|
11357
|
+
"dashboard_publish"
|
|
11358
|
+
];
|
|
11359
|
+
CHAT_CONTEXT_READ_TOOLS = [
|
|
11360
|
+
"query_memory",
|
|
11361
|
+
"search_history",
|
|
11362
|
+
"skill_view",
|
|
11363
|
+
"list_models",
|
|
11364
|
+
"list_schedules",
|
|
11365
|
+
"file_read",
|
|
11366
|
+
"file_list",
|
|
11367
|
+
"file_search"
|
|
11368
|
+
];
|
|
11369
|
+
CHAT_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
|
|
11370
|
+
...CHAT_DELIVERY_TOOLS,
|
|
11371
|
+
...CHAT_CONTEXT_READ_TOOLS
|
|
11372
|
+
]);
|
|
11373
|
+
}
|
|
11374
|
+
});
|
|
11375
|
+
|
|
11281
11376
|
// ../../packages/shared/src/entities/task.ts
|
|
11282
11377
|
import { z as z8 } from "zod";
|
|
11283
11378
|
var TaskDescriptionSchema, TaskTitleSchema, AgentTaskSchema, AgentTaskInsertSchema;
|
|
@@ -11383,7 +11478,9 @@ var init_connector = __esm({
|
|
|
11383
11478
|
name: z10.string().min(1).max(120),
|
|
11384
11479
|
slug: z10.string().min(1).max(80).regex(/^[a-z0-9-]+$/),
|
|
11385
11480
|
base_url: z10.string().nullable(),
|
|
11386
|
-
//
|
|
11481
|
+
// audit#2026-07-07 SEC-5: this comment used to claim pgp_sym_encrypt (pgcrypto),
|
|
11482
|
+
// which is not used anywhere. Real encryption is application-level AES-256-GCM
|
|
11483
|
+
// via @nodal-agents/secrets before the value ever reaches the DB; raw value never returned.
|
|
11387
11484
|
api_key: z10.string().nullable(),
|
|
11388
11485
|
active: z10.boolean(),
|
|
11389
11486
|
auth_type: ConnectorAuthTypeSchema,
|
|
@@ -11883,6 +11980,13 @@ var init_root_agent = __esm({
|
|
|
11883
11980
|
}
|
|
11884
11981
|
});
|
|
11885
11982
|
|
|
11983
|
+
// ../../packages/shared/src/system-prompt-cache.ts
|
|
11984
|
+
var init_system_prompt_cache = __esm({
|
|
11985
|
+
"../../packages/shared/src/system-prompt-cache.ts"() {
|
|
11986
|
+
"use strict";
|
|
11987
|
+
}
|
|
11988
|
+
});
|
|
11989
|
+
|
|
11886
11990
|
// ../../packages/shared/src/connector-catalog.ts
|
|
11887
11991
|
var init_connector_catalog = __esm({
|
|
11888
11992
|
"../../packages/shared/src/connector-catalog.ts"() {
|
|
@@ -11914,7 +12018,10 @@ var init_model_catalog = __esm({
|
|
|
11914
12018
|
// deepseek-chat is the production alias for DeepSeek V3. Standard tool
|
|
11915
12019
|
// calling with forced tool_choice supported.
|
|
11916
12020
|
capabilities: { tools: true, forcedToolChoice: true },
|
|
11917
|
-
contextWindow: 128e3
|
|
12021
|
+
contextWindow: 128e3,
|
|
12022
|
+
// Standard (cache-miss) rate per DeepSeek's public pricing page —
|
|
12023
|
+
// verify against api-docs.deepseek.com/quick_start/pricing if this drifts.
|
|
12024
|
+
pricing: { inputPerMillionUsd: 0.27, outputPerMillionUsd: 1.1 }
|
|
11918
12025
|
},
|
|
11919
12026
|
{
|
|
11920
12027
|
modelId: "deepseek-reasoner",
|
|
@@ -11924,7 +12031,8 @@ var init_model_catalog = __esm({
|
|
|
11924
12031
|
// reasoning:true). Also requires reasoning_content to be echoed back on
|
|
11925
12032
|
// assistant messages with tool_calls (the deepseek fetch shim handles this).
|
|
11926
12033
|
capabilities: { tools: true, forcedToolChoice: true, reasoning: true },
|
|
11927
|
-
contextWindow: 128e3
|
|
12034
|
+
contextWindow: 128e3,
|
|
12035
|
+
pricing: { inputPerMillionUsd: 0.55, outputPerMillionUsd: 2.19 }
|
|
11928
12036
|
}
|
|
11929
12037
|
],
|
|
11930
12038
|
// ─── Native MiniMax (api.minimax.io/anthropic) ───────────────────────────────
|
|
@@ -11937,6 +12045,11 @@ var init_model_catalog = __esm({
|
|
|
11937
12045
|
// forced tool_choice with a 400/404 (observed on M3 via OpenRouter); the
|
|
11938
12046
|
// runtime completion floor covers it. Context windows are docs-sourced where
|
|
11939
12047
|
// available, else a conservative 200K until a live probe confirms.
|
|
12048
|
+
// No `pricing` yet (unlike deepseek above) — no confidently-current MiniMax
|
|
12049
|
+
// rate card verified at write time. Left as documented debt (Fix #21): the
|
|
12050
|
+
// derived-cost fallback returns 0 for these until pricing is added, same as
|
|
12051
|
+
// every other unpriced entry in this catalog; Guard 1a (token budget) is the
|
|
12052
|
+
// backstop meanwhile.
|
|
11940
12053
|
minimax: [
|
|
11941
12054
|
{
|
|
11942
12055
|
modelId: "MiniMax-M3",
|
|
@@ -12197,6 +12310,28 @@ var init_timezone = __esm({
|
|
|
12197
12310
|
}
|
|
12198
12311
|
});
|
|
12199
12312
|
|
|
12313
|
+
// ../../packages/shared/src/redact-secrets.ts
|
|
12314
|
+
var init_redact_secrets = __esm({
|
|
12315
|
+
"../../packages/shared/src/redact-secrets.ts"() {
|
|
12316
|
+
"use strict";
|
|
12317
|
+
}
|
|
12318
|
+
});
|
|
12319
|
+
|
|
12320
|
+
// ../../packages/shared/src/catastrophic-command.ts
|
|
12321
|
+
var init_catastrophic_command = __esm({
|
|
12322
|
+
"../../packages/shared/src/catastrophic-command.ts"() {
|
|
12323
|
+
"use strict";
|
|
12324
|
+
}
|
|
12325
|
+
});
|
|
12326
|
+
|
|
12327
|
+
// ../../packages/shared/src/approval-impact.ts
|
|
12328
|
+
var init_approval_impact = __esm({
|
|
12329
|
+
"../../packages/shared/src/approval-impact.ts"() {
|
|
12330
|
+
"use strict";
|
|
12331
|
+
init_catastrophic_command();
|
|
12332
|
+
}
|
|
12333
|
+
});
|
|
12334
|
+
|
|
12200
12335
|
// ../../packages/shared/src/index.ts
|
|
12201
12336
|
var init_src2 = __esm({
|
|
12202
12337
|
"../../packages/shared/src/index.ts"() {
|
|
@@ -12207,6 +12342,7 @@ var init_src2 = __esm({
|
|
|
12207
12342
|
init_user();
|
|
12208
12343
|
init_agent();
|
|
12209
12344
|
init_job();
|
|
12345
|
+
init_job_classification();
|
|
12210
12346
|
init_task();
|
|
12211
12347
|
init_credential();
|
|
12212
12348
|
init_connector();
|
|
@@ -12220,11 +12356,15 @@ var init_src2 = __esm({
|
|
|
12220
12356
|
init_operation();
|
|
12221
12357
|
init_providers();
|
|
12222
12358
|
init_root_agent();
|
|
12359
|
+
init_system_prompt_cache();
|
|
12223
12360
|
init_connector_catalog();
|
|
12224
12361
|
init_community_skill_catalog();
|
|
12225
12362
|
init_model_catalog();
|
|
12226
12363
|
init_mcp_catalog();
|
|
12227
12364
|
init_timezone();
|
|
12365
|
+
init_redact_secrets();
|
|
12366
|
+
init_approval_impact();
|
|
12367
|
+
init_catastrophic_command();
|
|
12228
12368
|
}
|
|
12229
12369
|
});
|
|
12230
12370
|
|
|
@@ -12370,7 +12510,13 @@ var init_entities = __esm({
|
|
|
12370
12510
|
check(
|
|
12371
12511
|
"entity_members_role_check",
|
|
12372
12512
|
sql`${table.role} IN ('owner', 'admin', 'member', 'viewer')`
|
|
12373
|
-
)
|
|
12513
|
+
),
|
|
12514
|
+
// R5 (audit #2 follow-up, found while sweeping helpers.ts for the SAME
|
|
12515
|
+
// class of phantom constraint): the pglite test DDL already assumed a
|
|
12516
|
+
// user can only be a member of an entity once — this table had no such
|
|
12517
|
+
// constraint in the real schema/migrations. Both columns are NOT NULL, so
|
|
12518
|
+
// a plain UNIQUE is correct (no NULLS NOT DISTINCT needed).
|
|
12519
|
+
uniqueIndex("entity_members_entity_user_unique").on(table.entityId, table.userId)
|
|
12374
12520
|
]
|
|
12375
12521
|
);
|
|
12376
12522
|
}
|
|
@@ -12398,6 +12544,11 @@ var init_llm_keys = __esm({
|
|
|
12398
12544
|
apiKeyLast4: text("api_key_last4").notNull().default(""),
|
|
12399
12545
|
baseUrl: text("base_url"),
|
|
12400
12546
|
nickname: text("nickname"),
|
|
12547
|
+
// Real context window (tokens) for a custom/local model the catalog can't
|
|
12548
|
+
// know (É-3). Auto-detected from the endpoint at save time or set by the
|
|
12549
|
+
// user; NULL ⇒ fall back to the catalogued value or DEFAULT_CONTEXT_WINDOW.
|
|
12550
|
+
// Used window-relative to trigger compaction before the model overflows.
|
|
12551
|
+
contextWindow: integer("context_window"),
|
|
12401
12552
|
isActive: boolean("is_active").notNull().default(true),
|
|
12402
12553
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
12403
12554
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
@@ -12422,7 +12573,7 @@ var init_agents = __esm({
|
|
|
12422
12573
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
12423
12574
|
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
12424
12575
|
name: text("name").notNull(),
|
|
12425
|
-
slug: text("slug").notNull()
|
|
12576
|
+
slug: text("slug").notNull(),
|
|
12426
12577
|
personality: text("personality").notNull(),
|
|
12427
12578
|
model: text("model").default("claude-sonnet-4-6-20260217"),
|
|
12428
12579
|
llmKeyId: uuid("llm_key_id").references(() => entityLlmKeys.id, { onDelete: "set null" }),
|
|
@@ -12472,7 +12623,17 @@ var init_agents = __esm({
|
|
|
12472
12623
|
"agents_orchestrator_mode_check",
|
|
12473
12624
|
sql`${table.orchestratorMode} IN ('router', 'planner') OR ${table.orchestratorMode} IS NULL`
|
|
12474
12625
|
),
|
|
12475
|
-
check("agents_max_tokens_per_job_check", sql`${table.maxTokensPerJob} >= 0`)
|
|
12626
|
+
check("agents_max_tokens_per_job_check", sql`${table.maxTokensPerJob} >= 0`),
|
|
12627
|
+
// F-6 (audit #2): slug was UNIQUE GLOBALLY, so a 2nd workspace/entity
|
|
12628
|
+
// installing the same community skill's companion agent (or any agent
|
|
12629
|
+
// sharing a slug with another entity's agent) would crash the insert —
|
|
12630
|
+
// and, in multi-user mode, let one entity enumerate/squat another's
|
|
12631
|
+
// slugs. Scoped to (entity_id, slug): every real insert always sets
|
|
12632
|
+
// entityId (createAgentRepo takes it as a required param — no production
|
|
12633
|
+
// path leaves it NULL), but the column itself is nullable, so
|
|
12634
|
+
// NULLS NOT DISTINCT closes the same NULL-entity gap as DB-1/agent_plugins
|
|
12635
|
+
// (multiple (NULL, 'x') rows would otherwise all satisfy a plain UNIQUE).
|
|
12636
|
+
unique("agents_entity_slug_unique").on(table.entityId, table.slug).nullsNotDistinct()
|
|
12476
12637
|
]
|
|
12477
12638
|
);
|
|
12478
12639
|
agentAssignments = pgTable(
|
|
@@ -12485,7 +12646,18 @@ var init_agents = __esm({
|
|
|
12485
12646
|
instructions: text("instructions"),
|
|
12486
12647
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
12487
12648
|
},
|
|
12488
|
-
() => [
|
|
12649
|
+
(table) => [
|
|
12650
|
+
// F-18 (audit #2): no index beyond the PK meant updateAgentAction's
|
|
12651
|
+
// delete-then-insert (apps/web/src/lib/actions.ts) could race and leave the
|
|
12652
|
+
// same sub-agent attached twice under one orchestrator (duplicated in the
|
|
12653
|
+
// team-block). The unique index's leading column (orchestrator_id) also
|
|
12654
|
+
// serves the hot read path (team-block.ts, assign-tools.ts, detach-agent.ts
|
|
12655
|
+
// all look up by orchestratorId alone) — no separate index needed.
|
|
12656
|
+
unique("agent_assignments_orchestrator_sub_agent_unique").on(
|
|
12657
|
+
table.orchestratorId,
|
|
12658
|
+
table.subAgentId
|
|
12659
|
+
)
|
|
12660
|
+
]
|
|
12489
12661
|
);
|
|
12490
12662
|
agentBudgets = pgTable(
|
|
12491
12663
|
"agent_budgets",
|
|
@@ -12532,6 +12704,18 @@ var init_jobs = __esm({
|
|
|
12532
12704
|
task: text("task").notNull(),
|
|
12533
12705
|
originalTask: text("original_task"),
|
|
12534
12706
|
chatId: text("chat_id"),
|
|
12707
|
+
/**
|
|
12708
|
+
* Groups jobs from the same conversational thread for the Jobs page UI
|
|
12709
|
+
* (migration 0059). Purely a reporting/grouping concern — the runtime
|
|
12710
|
+
* execution path (delegation, cron, task-board) never reads this column.
|
|
12711
|
+
* NULL for non-conversational jobs (cron/schedule/webhook with no parent).
|
|
12712
|
+
* Stamped at creation by apps/runner/src/job/conversation-id.ts, reusing
|
|
12713
|
+
* the exact idle-reset session boundary thread-history.ts already uses
|
|
12714
|
+
* for chat continuity — see that file for the gap rule. Delegated/
|
|
12715
|
+
* task-board children inherit their creator's value; nothing else
|
|
12716
|
+
* mutates it after insert.
|
|
12717
|
+
*/
|
|
12718
|
+
conversationId: uuid("conversation_id"),
|
|
12535
12719
|
systemPrompt: text("system_prompt"),
|
|
12536
12720
|
messages: jsonb("messages").default(sql`'[]'::jsonb`),
|
|
12537
12721
|
/**
|
|
@@ -12614,8 +12798,22 @@ var init_jobs = __esm({
|
|
|
12614
12798
|
sql`${table.createdAt} DESC`
|
|
12615
12799
|
),
|
|
12616
12800
|
index("idx_agent_jobs_parent_job_id").on(table.parentJobId),
|
|
12617
|
-
|
|
12801
|
+
// Migration 0059: the Jobs page groups conversational jobs by
|
|
12802
|
+
// (entity_id, conversation_id) — this index serves that lookup directly.
|
|
12803
|
+
index("idx_agent_jobs_entity_conversation").on(table.entityId, table.conversationId),
|
|
12804
|
+
// idx_jobs_parent (F-20, audit #2) was an exact duplicate of
|
|
12805
|
+
// idx_agent_jobs_parent_job_id above — same column, same order, both from
|
|
12806
|
+
// migration 0000. Dropped in migration 0054; kept this one (matches the
|
|
12807
|
+
// idx_agent_jobs_* naming convention used everywhere else on this table).
|
|
12618
12808
|
index("idx_jobs_status").on(table.status, table.createdAt),
|
|
12809
|
+
// DB-3 (audit #2): findUndeliveredRootJobIds (cron/deliver-results.ts)
|
|
12810
|
+
// joins on this column filtered to IS NULL every tick; the cron recovery
|
|
12811
|
+
// scans (findPendingJobsToRecover, resetOrphanedJobs, failStalePendingJobs
|
|
12812
|
+
// in cron/reset-orphans.ts) also filter by status without an entity_id,
|
|
12813
|
+
// which idx_jobs_status (kept above) still serves — completed rows vastly
|
|
12814
|
+
// outnumber the still-open ones, so a partial index keyed on the open set
|
|
12815
|
+
// stays small regardless of table growth.
|
|
12816
|
+
index("idx_agent_jobs_completed_at_null").on(table.completedAt).where(sql`${table.completedAt} IS NULL`),
|
|
12619
12817
|
check(
|
|
12620
12818
|
"agent_jobs_status_check",
|
|
12621
12819
|
sql`${table.status} IN ('pending','processing','completed','failed','awaiting_approval','awaiting_delegation','cancelled')`
|
|
@@ -12678,6 +12876,10 @@ var init_tasks = __esm({
|
|
|
12678
12876
|
index("idx_agent_tasks_orchestrator_status").on(table.orchestratorId, table.status),
|
|
12679
12877
|
index("idx_agent_tasks_status").on(table.status),
|
|
12680
12878
|
index("idx_agent_tasks_assigned").on(table.assignedAgentId),
|
|
12879
|
+
// DB-3 (audit #2): findUndeliveredRootJobIds (apps/runner/src/cron/
|
|
12880
|
+
// deliver-results.ts) scans `WHERE root_job_id IS NOT NULL` on every cron
|
|
12881
|
+
// tick and had no index to support it — full scan on the hottest task table.
|
|
12882
|
+
index("idx_agent_tasks_root_job_id").on(table.rootJobId),
|
|
12681
12883
|
check(
|
|
12682
12884
|
"agent_tasks_status_check",
|
|
12683
12885
|
sql`${table.status} IN ('todo','in_progress','done','cancelled','blocked')`
|
|
@@ -12742,7 +12944,10 @@ var init_connectors = __esm({
|
|
|
12742
12944
|
name: text("name").notNull(),
|
|
12743
12945
|
slug: text("slug").notNull(),
|
|
12744
12946
|
baseUrl: text("base_url"),
|
|
12745
|
-
//
|
|
12947
|
+
// audit#2026-07-07 SEC-5: this comment used to claim pgp_sym_encrypt (pgcrypto),
|
|
12948
|
+
// which is not used anywhere. Real encryption is application-level AES-256-GCM
|
|
12949
|
+
// via @nodal-agents/secrets before the value ever reaches this column — the DB
|
|
12950
|
+
// column itself just holds ciphertext as opaque text. Raw value never returned in select.
|
|
12746
12951
|
apiKey: text("api_key"),
|
|
12747
12952
|
active: boolean("active").default(true),
|
|
12748
12953
|
authType: text("auth_type").notNull().default("api_key"),
|
|
@@ -12862,7 +13067,16 @@ var init_approvals = __esm({
|
|
|
12862
13067
|
check(
|
|
12863
13068
|
"approval_rules_action_check",
|
|
12864
13069
|
sql`${table.action} IN ('auto_approve','require_approval','block')`
|
|
12865
|
-
)
|
|
13070
|
+
),
|
|
13071
|
+
// DB-1 (audit #2): one canonical rule per (entity, agent-or-null, tool) —
|
|
13072
|
+
// without this, two concurrent setAgentApprovalRuleAction calls (or any
|
|
13073
|
+
// direct insert) can leave two rows for the same scope with DIVERGENT
|
|
13074
|
+
// actions, and matchApprovalRule's `.find()` picks whichever the SELECT
|
|
13075
|
+
// happens to return first — a non-deterministic approval gate. agentId IS
|
|
13076
|
+
// NULL marks an entity-wide rule (e.g. the run_command LAN master-switch),
|
|
13077
|
+
// so a plain UNIQUE would treat two NULL rows as distinct and let the
|
|
13078
|
+
// duplicate back in; NULLS NOT DISTINCT (PG15+) closes that gap.
|
|
13079
|
+
unique("approval_rules_entity_agent_tool_unique").on(table.entityId, table.agentId, table.toolName).nullsNotDistinct()
|
|
12866
13080
|
]
|
|
12867
13081
|
);
|
|
12868
13082
|
}
|
|
@@ -12994,8 +13208,8 @@ var init_skills = __esm({
|
|
|
12994
13208
|
{
|
|
12995
13209
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
12996
13210
|
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
12997
|
-
name: text("name").notNull()
|
|
12998
|
-
slug: text("slug").notNull()
|
|
13211
|
+
name: text("name").notNull(),
|
|
13212
|
+
slug: text("slug").notNull(),
|
|
12999
13213
|
content: text("content").notNull(),
|
|
13000
13214
|
active: boolean("active").default(true),
|
|
13001
13215
|
description: text("description"),
|
|
@@ -13035,7 +13249,20 @@ var init_skills = __esm({
|
|
|
13035
13249
|
(table) => [
|
|
13036
13250
|
index("idx_agent_skills_entity_id").on(table.entityId),
|
|
13037
13251
|
index("idx_skills_active").on(table.active, table.slug),
|
|
13038
|
-
index("idx_agent_skills_is_community").on(table.isCommunity)
|
|
13252
|
+
index("idx_agent_skills_is_community").on(table.isCommunity),
|
|
13253
|
+
// F-6 (audit #2): slug AND name were UNIQUE GLOBALLY — a 2nd workspace/
|
|
13254
|
+
// entity installing the same community skill (e.g. slug 'comfyui') or
|
|
13255
|
+
// creating a skill with the same display name crashed the insert, and in
|
|
13256
|
+
// multi-user mode let one entity squat/enumerate another's slugs or
|
|
13257
|
+
// names. Scoped to (entity_id, slug) / (entity_id, name): every real
|
|
13258
|
+
// insert always sets entityId (createSkillRepo takes it as a required
|
|
13259
|
+
// param; the system-skill seeder and community-skill installer both pass
|
|
13260
|
+
// it too — no production path leaves it NULL), but the column itself is
|
|
13261
|
+
// nullable, so NULLS NOT DISTINCT closes the same NULL-entity gap as
|
|
13262
|
+
// DB-1/agent_plugins (multiple (NULL, 'x') rows would otherwise all
|
|
13263
|
+
// satisfy a plain UNIQUE).
|
|
13264
|
+
unique("agent_skills_entity_slug_unique").on(table.entityId, table.slug).nullsNotDistinct(),
|
|
13265
|
+
unique("agent_skills_entity_name_unique").on(table.entityId, table.name).nullsNotDistinct()
|
|
13039
13266
|
]
|
|
13040
13267
|
);
|
|
13041
13268
|
skillVersions = pgTable(
|
|
@@ -13092,7 +13319,14 @@ var init_skills = __esm({
|
|
|
13092
13319
|
filesWritable: boolean("files_writable").notNull().default(false),
|
|
13093
13320
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
13094
13321
|
},
|
|
13095
|
-
() => [
|
|
13322
|
+
(table) => [
|
|
13323
|
+
// DB-2 (audit #2): the table had no index beyond the PK, so assignSkillRepo's
|
|
13324
|
+
// check-then-insert (packages/db/src/repos/skills.ts) could race and leave
|
|
13325
|
+
// the same skill assigned twice to one agent. The unique index's leading
|
|
13326
|
+
// column (agent_id) also serves the hot read path (execute.ts loads all
|
|
13327
|
+
// skills for an agent) — no separate index needed on top of it.
|
|
13328
|
+
unique("agent_skill_assignments_agent_skill_unique").on(table.agentId, table.skillId)
|
|
13329
|
+
]
|
|
13096
13330
|
);
|
|
13097
13331
|
}
|
|
13098
13332
|
});
|
|
@@ -13224,7 +13458,14 @@ var init_mcp = __esm({
|
|
|
13224
13458
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
13225
13459
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
13226
13460
|
},
|
|
13227
|
-
(table) => [
|
|
13461
|
+
(table) => [
|
|
13462
|
+
index("idx_mcp_connections_entity").on(table.entityId),
|
|
13463
|
+
// R5 (audit #2 follow-up): the pglite test DDL (packages/db/src/tests/
|
|
13464
|
+
// helpers.ts) already assumed a per-entity unique slug here — same class
|
|
13465
|
+
// of phantom-constraint bug as DB-2/F-18. entityId is NOT NULL on this
|
|
13466
|
+
// table, so a plain UNIQUE (no NULLS NOT DISTINCT needed) is correct.
|
|
13467
|
+
uniqueIndex("mcp_connections_entity_slug_unique").on(table.entityId, table.slug)
|
|
13468
|
+
]
|
|
13228
13469
|
);
|
|
13229
13470
|
}
|
|
13230
13471
|
});
|
|
@@ -13279,7 +13520,14 @@ var init_misc = __esm({
|
|
|
13279
13520
|
check(
|
|
13280
13521
|
"agent_plugins_hook_check",
|
|
13281
13522
|
sql`${table.hook} IN ('pre_task','post_task','pre_tool','post_tool','on_memory_save')`
|
|
13282
|
-
)
|
|
13523
|
+
),
|
|
13524
|
+
// R5 (audit #2 follow-up): the pglite test DDL (packages/db/src/tests/
|
|
13525
|
+
// helpers.ts) already assumed a per-entity unique slug here — same class
|
|
13526
|
+
// of phantom-constraint bug as DB-2/F-18 (test mock ahead of the real
|
|
13527
|
+
// schema/migration). entityId is nullable (no `.notNull()` above), so
|
|
13528
|
+
// NULLS NOT DISTINCT closes the same NULL-entity gap as DB-1's
|
|
13529
|
+
// approval_rules constraint.
|
|
13530
|
+
unique("agent_plugins_entity_slug_unique").on(table.entityId, table.slug).nullsNotDistinct()
|
|
13283
13531
|
]
|
|
13284
13532
|
);
|
|
13285
13533
|
rateLimits = pgTable("rate_limits", {
|
|
@@ -13451,16 +13699,65 @@ var init_chat_messages = __esm({
|
|
|
13451
13699
|
});
|
|
13452
13700
|
|
|
13453
13701
|
// ../../packages/db/src/schema/app-settings.ts
|
|
13454
|
-
var appSettings;
|
|
13702
|
+
var appSettings, entitySettings;
|
|
13455
13703
|
var init_app_settings = __esm({
|
|
13456
13704
|
"../../packages/db/src/schema/app-settings.ts"() {
|
|
13457
13705
|
"use strict";
|
|
13458
13706
|
init_pg_core();
|
|
13707
|
+
init_entities();
|
|
13459
13708
|
appSettings = pgTable("app_settings", {
|
|
13460
13709
|
key: text("key").primaryKey(),
|
|
13461
13710
|
value: text("value").default("").notNull(),
|
|
13462
13711
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
13463
13712
|
});
|
|
13713
|
+
entitySettings = pgTable(
|
|
13714
|
+
"entity_settings",
|
|
13715
|
+
{
|
|
13716
|
+
entityId: uuid("entity_id").notNull().references(() => entities.id, { onDelete: "cascade" }),
|
|
13717
|
+
key: text("key").notNull(),
|
|
13718
|
+
value: text("value").default("").notNull(),
|
|
13719
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
13720
|
+
},
|
|
13721
|
+
(table) => [primaryKey({ columns: [table.entityId, table.key] })]
|
|
13722
|
+
);
|
|
13723
|
+
}
|
|
13724
|
+
});
|
|
13725
|
+
|
|
13726
|
+
// ../../packages/db/src/schema/telegram-allowed-chats.ts
|
|
13727
|
+
var telegramAllowedChats;
|
|
13728
|
+
var init_telegram_allowed_chats = __esm({
|
|
13729
|
+
"../../packages/db/src/schema/telegram-allowed-chats.ts"() {
|
|
13730
|
+
"use strict";
|
|
13731
|
+
init_pg_core();
|
|
13732
|
+
init_drizzle_orm();
|
|
13733
|
+
init_entities();
|
|
13734
|
+
init_agents();
|
|
13735
|
+
telegramAllowedChats = pgTable(
|
|
13736
|
+
"telegram_allowed_chats",
|
|
13737
|
+
{
|
|
13738
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
13739
|
+
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
13740
|
+
/** The bot the chat is talking to (the RECEIVING agent). */
|
|
13741
|
+
agentId: uuid("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
|
|
13742
|
+
/** Telegram chat id (private chat, group, or supergroup) as a string. */
|
|
13743
|
+
chatId: text("chat_id").notNull(),
|
|
13744
|
+
/** 'owner' = the bot's controller; 'member' = an approved additional chat. */
|
|
13745
|
+
role: text("role").notNull().default("member"),
|
|
13746
|
+
/** 'active' = may create jobs; 'pending' = awaiting the owner's confirmation. */
|
|
13747
|
+
status: text("status").notNull().default("pending"),
|
|
13748
|
+
/** Display name of the requester, for the owner's confirmation card. */
|
|
13749
|
+
requesterName: text("requester_name"),
|
|
13750
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
|
13751
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
|
|
13752
|
+
},
|
|
13753
|
+
(table) => [
|
|
13754
|
+
unique("telegram_allowed_chats_agent_chat_unique").on(table.agentId, table.chatId),
|
|
13755
|
+
index("idx_telegram_allowed_chats_agent").on(table.agentId),
|
|
13756
|
+
index("idx_telegram_allowed_chats_entity").on(table.entityId),
|
|
13757
|
+
check("telegram_allowed_chats_role_check", sql`${table.role} IN ('owner','member')`),
|
|
13758
|
+
check("telegram_allowed_chats_status_check", sql`${table.status} IN ('active','pending')`)
|
|
13759
|
+
]
|
|
13760
|
+
);
|
|
13464
13761
|
}
|
|
13465
13762
|
});
|
|
13466
13763
|
|
|
@@ -13512,12 +13809,14 @@ __export(schema_exports, {
|
|
|
13512
13809
|
entities: () => entities,
|
|
13513
13810
|
entityLlmKeys: () => entityLlmKeys,
|
|
13514
13811
|
entityMembers: () => entityMembers,
|
|
13812
|
+
entitySettings: () => entitySettings,
|
|
13515
13813
|
mcpConnections: () => mcpConnections,
|
|
13516
13814
|
mcpServers: () => mcpServers,
|
|
13517
13815
|
rateLimits: () => rateLimits,
|
|
13518
13816
|
sessions: () => sessions,
|
|
13519
13817
|
skillConnectors: () => skillConnectors,
|
|
13520
13818
|
skillVersions: () => skillVersions,
|
|
13819
|
+
telegramAllowedChats: () => telegramAllowedChats,
|
|
13521
13820
|
toolCalls: () => toolCalls,
|
|
13522
13821
|
userProfiles: () => userProfiles,
|
|
13523
13822
|
users: () => users,
|
|
@@ -13549,6 +13848,7 @@ var init_schema2 = __esm({
|
|
|
13549
13848
|
init_agent_workspaces();
|
|
13550
13849
|
init_chat_messages();
|
|
13551
13850
|
init_app_settings();
|
|
13851
|
+
init_telegram_allowed_chats();
|
|
13552
13852
|
}
|
|
13553
13853
|
});
|
|
13554
13854
|
|
|
@@ -13557,7 +13857,29 @@ function createClient(connectionString, options = {}) {
|
|
|
13557
13857
|
const pool = src_default(connectionString, {
|
|
13558
13858
|
max: options.max ?? 10,
|
|
13559
13859
|
// Fail fast on connection issues rather than hanging
|
|
13560
|
-
connect_timeout: 10
|
|
13860
|
+
connect_timeout: 10,
|
|
13861
|
+
// R2 (audit2 concurrency review): bound the two ways a query on this pool
|
|
13862
|
+
// can hang indefinitely with no client-side timeout at all. Sent as
|
|
13863
|
+
// Postgres startup/session parameters (postgres.js `connection` option),
|
|
13864
|
+
// so every connection in the pool gets them — no per-query opt-in needed.
|
|
13865
|
+
// - lock_timeout: the vector identified for the cron watchdog (R1) — a
|
|
13866
|
+
// query blocked waiting on a row lock previously waited forever.
|
|
13867
|
+
// 30s is comfortably longer than any legitimate lock wait in this
|
|
13868
|
+
// app's write patterns (single-row updates/inserts, no long-held
|
|
13869
|
+
// locks by design).
|
|
13870
|
+
// - idle_in_transaction_session_timeout: a transaction left open with
|
|
13871
|
+
// no further statements (BEGIN without COMMIT/ROLLBACK) is always a
|
|
13872
|
+
// bug, never a legitimate long-running operation — 60s is generous.
|
|
13873
|
+
// Deliberately NOT setting statement_timeout here: a global cap would
|
|
13874
|
+
// also apply to legitimate long-running operations (a large backfill, a
|
|
13875
|
+
// slow migration-adjacent query) and risk cutting them off mid-flight.
|
|
13876
|
+
// migrate.ts uses its own separate `postgres()` connection (not this
|
|
13877
|
+
// factory) for running migrations, so these timeouts don't touch
|
|
13878
|
+
// migrations at all either way.
|
|
13879
|
+
connection: {
|
|
13880
|
+
lock_timeout: 3e4,
|
|
13881
|
+
idle_in_transaction_session_timeout: 6e4
|
|
13882
|
+
}
|
|
13561
13883
|
});
|
|
13562
13884
|
const db = drizzle(pool, { schema: schema_exports });
|
|
13563
13885
|
return {
|
|
@@ -13584,12 +13906,24 @@ var init_transaction = __esm({
|
|
|
13584
13906
|
|
|
13585
13907
|
// ../../packages/secrets/src/index.ts
|
|
13586
13908
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "crypto";
|
|
13587
|
-
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
13909
|
+
import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
13910
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
13588
13911
|
import { homedir as homedir2 } from "os";
|
|
13589
13912
|
import { dirname as dirname3, join as join5 } from "path";
|
|
13913
|
+
function masterKeyPath() {
|
|
13914
|
+
return join5(homedir2(), ".nodalai", "secrets.key");
|
|
13915
|
+
}
|
|
13916
|
+
function masterKeyFileMissing(path = masterKeyPath()) {
|
|
13917
|
+
return !existsSync5(path);
|
|
13918
|
+
}
|
|
13919
|
+
function isEncrypted(value) {
|
|
13920
|
+
return value.startsWith(`enc:${VERSION}:`);
|
|
13921
|
+
}
|
|
13922
|
+
var VERSION;
|
|
13590
13923
|
var init_src3 = __esm({
|
|
13591
13924
|
"../../packages/secrets/src/index.ts"() {
|
|
13592
13925
|
"use strict";
|
|
13926
|
+
VERSION = "v1";
|
|
13593
13927
|
}
|
|
13594
13928
|
});
|
|
13595
13929
|
|
|
@@ -13605,6 +13939,41 @@ var init_credentials2 = __esm({
|
|
|
13605
13939
|
}
|
|
13606
13940
|
});
|
|
13607
13941
|
|
|
13942
|
+
// ../../packages/db/src/queries/master-key-guard.ts
|
|
13943
|
+
async function assertMasterKeyRestorable(db, options = {}) {
|
|
13944
|
+
if (!masterKeyFileMissing(options.keyPath)) return;
|
|
13945
|
+
const [credRow] = await db.select({ id: credentials.id }).from(credentials).limit(1);
|
|
13946
|
+
if (credRow) throw missingKeyError("a stored OAuth credential");
|
|
13947
|
+
const llmKeyRows = await db.select({ apiKey: entityLlmKeys.apiKey }).from(entityLlmKeys);
|
|
13948
|
+
const hasEncryptedLlmKey = llmKeyRows.some((r) => r.apiKey !== "" && isEncrypted(r.apiKey));
|
|
13949
|
+
if (hasEncryptedLlmKey) throw missingKeyError("a stored LLM API key");
|
|
13950
|
+
const connectorRows = await db.select({ apiKey: connectors.apiKey }).from(connectors);
|
|
13951
|
+
const hasEncryptedConnector = connectorRows.some((r) => !!r.apiKey && isEncrypted(r.apiKey));
|
|
13952
|
+
if (hasEncryptedConnector) throw missingKeyError("a stored connector API key");
|
|
13953
|
+
const mcpRows = await db.select({ apiKey: mcpServers.apiKey, envVars: mcpServers.envVars }).from(mcpServers);
|
|
13954
|
+
const hasEncryptedMcp = mcpRows.some((r) => {
|
|
13955
|
+
if (r.apiKey && isEncrypted(r.apiKey)) return true;
|
|
13956
|
+
const env = r.envVars ?? {};
|
|
13957
|
+
return Object.values(env).some((v) => typeof v === "string" && isEncrypted(v));
|
|
13958
|
+
});
|
|
13959
|
+
if (hasEncryptedMcp) throw missingKeyError("a stored MCP server secret");
|
|
13960
|
+
}
|
|
13961
|
+
function missingKeyError(what) {
|
|
13962
|
+
return new Error(
|
|
13963
|
+
`~/.nodalai/secrets.key is missing, but the database already has ${what} encrypted with it. Generating a NEW key here would make every existing credential / LLM key permanently undecryptable, with no further warning. Restore the original secrets.key from a backup before running \`nodal-agents up\` again. If you intend to start fresh, delete the affected rows (or wipe the database) first, then retry.`
|
|
13964
|
+
);
|
|
13965
|
+
}
|
|
13966
|
+
var init_master_key_guard = __esm({
|
|
13967
|
+
"../../packages/db/src/queries/master-key-guard.ts"() {
|
|
13968
|
+
"use strict";
|
|
13969
|
+
init_credentials();
|
|
13970
|
+
init_llm_keys();
|
|
13971
|
+
init_connectors();
|
|
13972
|
+
init_mcp();
|
|
13973
|
+
init_src3();
|
|
13974
|
+
}
|
|
13975
|
+
});
|
|
13976
|
+
|
|
13608
13977
|
// ../../packages/db/src/repos/agents.ts
|
|
13609
13978
|
var init_agents2 = __esm({
|
|
13610
13979
|
"../../packages/db/src/repos/agents.ts"() {
|
|
@@ -13648,6 +14017,7 @@ var init_src4 = __esm({
|
|
|
13648
14017
|
init_transaction();
|
|
13649
14018
|
init_schema2();
|
|
13650
14019
|
init_credentials2();
|
|
14020
|
+
init_master_key_guard();
|
|
13651
14021
|
init_agents2();
|
|
13652
14022
|
init_skills2();
|
|
13653
14023
|
init_retention();
|
|
@@ -13707,11 +14077,20 @@ var init_local_auth = __esm({
|
|
|
13707
14077
|
}
|
|
13708
14078
|
});
|
|
13709
14079
|
|
|
14080
|
+
// ../../packages/auth/src/lib/constant-time.ts
|
|
14081
|
+
import { createHash, timingSafeEqual } from "crypto";
|
|
14082
|
+
var init_constant_time = __esm({
|
|
14083
|
+
"../../packages/auth/src/lib/constant-time.ts"() {
|
|
14084
|
+
"use strict";
|
|
14085
|
+
}
|
|
14086
|
+
});
|
|
14087
|
+
|
|
13710
14088
|
// ../../packages/auth/src/providers/bearer-token.ts
|
|
13711
14089
|
var init_bearer_token = __esm({
|
|
13712
14090
|
"../../packages/auth/src/providers/bearer-token.ts"() {
|
|
13713
14091
|
"use strict";
|
|
13714
14092
|
init_local_trust();
|
|
14093
|
+
init_constant_time();
|
|
13715
14094
|
}
|
|
13716
14095
|
});
|
|
13717
14096
|
|
|
@@ -13750,6 +14129,7 @@ var init_seed = __esm({
|
|
|
13750
14129
|
});
|
|
13751
14130
|
|
|
13752
14131
|
// src/lib/env.ts
|
|
14132
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
13753
14133
|
function buildEnvForRunner(config, databaseUrl) {
|
|
13754
14134
|
const bind = config.bind === "loopback" ? "127.0.0.1" : "0.0.0.0";
|
|
13755
14135
|
const env = {
|
|
@@ -13773,7 +14153,19 @@ function buildEnvForRunner(config, databaseUrl) {
|
|
|
13773
14153
|
return env;
|
|
13774
14154
|
}
|
|
13775
14155
|
function resolveAuthMode(config) {
|
|
13776
|
-
if (config.
|
|
14156
|
+
if (config.bearerToken) {
|
|
14157
|
+
throw new Error(
|
|
14158
|
+
'Invalid config: "bearerToken" is set in config.json, which the docs describe as activating AUTH_MODE=bearer-token \u2014 but that mode is not fully wired end-to-end yet (the web dashboard\'s auth provider still falls back to no-auth for bearer-token mode; see apps/web/src/lib/server.ts getAuthProvider). Booting anyway would silently run in local-trust or local-auth instead \u2014 NOT the bearer-token protection you configured. Refused. Remove "bearerToken" from config.json until this is wired, or use `bind`/`auth.mode` for local-trust/local-auth.'
|
|
14159
|
+
);
|
|
14160
|
+
}
|
|
14161
|
+
if (config.auth?.mode) {
|
|
14162
|
+
if (config.auth.mode === "local-trust" && config.bind === "lan") {
|
|
14163
|
+
throw new Error(
|
|
14164
|
+
'Invalid config: auth.mode="local-trust" with bind="lan" would expose every runner route unauthenticated on the network (including run_command auto-approve). Refused. Use auth.mode="local-auth" for a LAN bind, or bind="loopback" for local-trust.'
|
|
14165
|
+
);
|
|
14166
|
+
}
|
|
14167
|
+
return config.auth.mode;
|
|
14168
|
+
}
|
|
13777
14169
|
return config.bind === "lan" ? "local-auth" : "local-trust";
|
|
13778
14170
|
}
|
|
13779
14171
|
function buildEnvForWeb(config, databaseUrl) {
|
|
@@ -13797,8 +14189,16 @@ function buildEnvForWeb(config, databaseUrl) {
|
|
|
13797
14189
|
// The web itself doesn't bind on this — Next.js listens on 0.0.0.0 anyway.
|
|
13798
14190
|
BIND: bind,
|
|
13799
14191
|
NODE_ENV: "production",
|
|
13800
|
-
// AUTH_SECRET is required by better-auth in local-auth mode; harmless in
|
|
13801
|
-
|
|
14192
|
+
// AUTH_SECRET is required by better-auth in local-auth mode; harmless in
|
|
14193
|
+
// local-trust. M-4: deliberately SEPARATE from workerSecret — the two
|
|
14194
|
+
// used to share a value, so a leak of workerSecret (the runner's auth
|
|
14195
|
+
// frontier) would also let an attacker forge a signed better-auth session
|
|
14196
|
+
// cookie. readConfig() auto-mints authSecret, so this is set in every
|
|
14197
|
+
// normal boot path; the random fallback below only guards a caller that
|
|
14198
|
+
// somehow bypasses readConfig (ephemeral — would invalidate sessions on
|
|
14199
|
+
// every restart, which is a symptom to fix at the config layer, not a
|
|
14200
|
+
// reason to crash boot or fall back to workerSecret).
|
|
14201
|
+
AUTH_SECRET: config.authSecret ?? randomBytes3(32).toString("base64"),
|
|
13802
14202
|
// WORKER_SECRET — same value as the runner — so sendTaskAction can sign
|
|
13803
14203
|
// the POST /api/worker call. Without this the runner returns 403 and the
|
|
13804
14204
|
// job stays pending forever (cron only scans task-board, not API jobs).
|
|
@@ -14081,7 +14481,7 @@ __export(up_exports, {
|
|
|
14081
14481
|
import chalk from "chalk";
|
|
14082
14482
|
import ora from "ora";
|
|
14083
14483
|
import open from "open";
|
|
14084
|
-
import { randomBytes as
|
|
14484
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
14085
14485
|
async function killSilent(child) {
|
|
14086
14486
|
await killProcessTree(child);
|
|
14087
14487
|
}
|
|
@@ -14106,8 +14506,12 @@ async function runUp(opts = {}) {
|
|
|
14106
14506
|
const defaultConfig = {
|
|
14107
14507
|
// No `llm` section: the browser onboarding sets it via createLlmKeyAction.
|
|
14108
14508
|
ports: { web: 3e3, runner: 3001, postgres: 25432 },
|
|
14109
|
-
workerSecret:
|
|
14110
|
-
serverActionsKey:
|
|
14509
|
+
workerSecret: randomBytes4(32).toString("hex"),
|
|
14510
|
+
serverActionsKey: randomBytes4(32).toString("base64"),
|
|
14511
|
+
// M-4: authSecret is minted here (distinct from workerSecret) rather
|
|
14512
|
+
// than left for readConfig()'s auto-mint, because this in-memory
|
|
14513
|
+
// `config` is used directly below without a re-read.
|
|
14514
|
+
authSecret: randomBytes4(32).toString("base64"),
|
|
14111
14515
|
// loopback derives auth: local-trust (no login/signup). Leave `auth`
|
|
14112
14516
|
// unset so resolveAuthMode falls back to the loopback → local-trust map.
|
|
14113
14517
|
bind: "loopback"
|
|
@@ -14115,6 +14519,7 @@ async function runUp(opts = {}) {
|
|
|
14115
14519
|
writeConfig(defaultConfig);
|
|
14116
14520
|
config = defaultConfig;
|
|
14117
14521
|
}
|
|
14522
|
+
resolveAuthMode(config);
|
|
14118
14523
|
const orphans = [];
|
|
14119
14524
|
for (const [name, port] of [
|
|
14120
14525
|
["web", config.ports.web],
|
|
@@ -14220,6 +14625,17 @@ async function runUp(opts = {}) {
|
|
|
14220
14625
|
await pg.stop();
|
|
14221
14626
|
throw err;
|
|
14222
14627
|
}
|
|
14628
|
+
{
|
|
14629
|
+
const { db, close } = createClient(databaseUrl, { max: 1 });
|
|
14630
|
+
try {
|
|
14631
|
+
await assertMasterKeyRestorable(db);
|
|
14632
|
+
} catch (err) {
|
|
14633
|
+
await close();
|
|
14634
|
+
await pg.stop();
|
|
14635
|
+
throw err;
|
|
14636
|
+
}
|
|
14637
|
+
await close();
|
|
14638
|
+
}
|
|
14223
14639
|
const authMode = resolveAuthMode(config);
|
|
14224
14640
|
if (authMode === "local-trust") {
|
|
14225
14641
|
const seedSpinner = ora("Seeding default user and agent\u2026").start();
|
|
@@ -14269,13 +14685,13 @@ async function runUp(opts = {}) {
|
|
|
14269
14685
|
}
|
|
14270
14686
|
void (async () => {
|
|
14271
14687
|
try {
|
|
14272
|
-
const { getInstalledVersion: getInstalledVersion2, getLatestVersion: getLatestVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
14688
|
+
const { getInstalledVersion: getInstalledVersion2, getLatestVersion: getLatestVersion2, isNewerVersion: isNewerVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
14273
14689
|
const installed = getInstalledVersion2();
|
|
14274
14690
|
const latest = await Promise.race([
|
|
14275
14691
|
getLatestVersion2(),
|
|
14276
14692
|
new Promise((resolve2) => setTimeout(() => resolve2(null), 3e3))
|
|
14277
14693
|
]);
|
|
14278
|
-
if (latest !== null && latest
|
|
14694
|
+
if (latest !== null && isNewerVersion2(latest, installed)) {
|
|
14279
14695
|
console.log(chalk.cyan(` \u2139 v${latest} available \u2014 run \`nodal-agents update\``));
|
|
14280
14696
|
}
|
|
14281
14697
|
} catch {
|
|
@@ -14431,9 +14847,9 @@ __export(init_exports, {
|
|
|
14431
14847
|
import chalk2 from "chalk";
|
|
14432
14848
|
import prompts from "prompts";
|
|
14433
14849
|
import ora2 from "ora";
|
|
14434
|
-
import { randomBytes as
|
|
14850
|
+
import { randomBytes as randomBytes5 } from "crypto";
|
|
14435
14851
|
function generateSecret(bytes = 32) {
|
|
14436
|
-
return
|
|
14852
|
+
return randomBytes5(bytes).toString("hex");
|
|
14437
14853
|
}
|
|
14438
14854
|
async function runInit(options = {}) {
|
|
14439
14855
|
const existing = readConfig();
|
|
@@ -14454,7 +14870,7 @@ async function runInit(options = {}) {
|
|
|
14454
14870
|
const config2 = {
|
|
14455
14871
|
ports: { web: 3e3, runner: 3001, postgres: 25432 },
|
|
14456
14872
|
workerSecret: generateSecret(32),
|
|
14457
|
-
serverActionsKey:
|
|
14873
|
+
serverActionsKey: randomBytes5(32).toString("base64"),
|
|
14458
14874
|
bind: "lan",
|
|
14459
14875
|
auth: { mode: "local-auth" }
|
|
14460
14876
|
};
|