nodal-agents 0.7.0 → 0.7.2
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 +335 -35
- 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/meta/_journal.json +28 -0
- package/package.json +1 -1
- package/runner.js +47123 -35332
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +4 -4
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- 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 +2 -2
- 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 +205 -185
- package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app-paths-manifest.json +4 -4
- package/web/.next/server/chunks/2713.js +47 -27
- package/web/.next/server/chunks/3223.js +1 -1
- package/web/.next/server/chunks/4574.js +1 -1
- package/web/.next/server/chunks/5515.js +1 -0
- package/web/.next/server/chunks/6468.js +1 -0
- package/web/.next/server/chunks/8410.js +1 -0
- package/web/.next/server/chunks/{4488.js → 8810.js} +2 -2
- 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/4964-c6711d3d479b6085.js +1 -0
- package/web/.next/static/chunks/{9836-6830cb9d49ad261f.js → 9836-177f6ca2f15ff703.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-cfce874f4acede5e.js +6 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-9249c2839f551ba1.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/page-7d784d737acedccf.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/approvals/{page-9a01cb723a6ccff0.js → page-b3a838ecbc94a030.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/automations/page-b1b548de4f52696c.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/chat/{page-a6cc7a58986c0340.js → page-bcf6dcc73b370eb0.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/connectors/{page-21d6c042800c8cd3.js → page-2c6f913cc6aea4b7.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-43f033251eddcc4b.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/jobs/page-1f892f950bb30403.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/layout-e184673151ff8a05.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-f873bb6f8539e47e.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-64adda30176f4ccb.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/logs/page-79cfd9141c39d8ef.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-d276d7bab25286a1.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/memories/page-c66720494a340503.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/page-3649204dd27a0ad0.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/settings/page-1cab404d2eccfd80.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-b5898c5444f88c9a.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-82d91fc6b6acc161.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/new/page-58b5ed04583ca7d4.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/page-312bd4ae8ddc8114.js +1 -0
- package/web/.next/static/chunks/app/onboarding/{page-a4116e2f430eaff8.js → page-f2f04878e81fdf23.js} +2 -2
- package/web/.next/static/css/{eff119dd24f92093.css → 4d808b91d7b58fce.css} +1 -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/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)/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)/logs/page-0bca733315e8a208.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/chunks/app/(dashboard)/skills/page-54e939d40c8b5b6d.js +0 -1
- /package/web/.next/static/{KFac7DQ7gdp9XPQ2bnb-2 → SgnP_MouhUmw0bBf-lUmD}/_buildManifest.js +0 -0
- /package/web/.next/static/{KFac7DQ7gdp9XPQ2bnb-2 → SgnP_MouhUmw0bBf-lUmD}/_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,7 +74,7 @@ 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";
|
|
63
78
|
import { randomBytes } from "crypto";
|
|
64
79
|
function ensureConfigDir() {
|
|
65
80
|
for (const dir of [CONFIG_DIR, PID_DIR, LOG_DIR, PG_DATA_DIR]) {
|
|
@@ -81,11 +96,21 @@ function readConfig(configFile = CONFIG_FILE) {
|
|
|
81
96
|
parsed = { ...parsed, serverActionsKey: randomBytes(32).toString("base64") };
|
|
82
97
|
writeConfig(parsed, configFile);
|
|
83
98
|
}
|
|
99
|
+
if (!parsed.authSecret) {
|
|
100
|
+
parsed = { ...parsed, authSecret: randomBytes(32).toString("base64") };
|
|
101
|
+
writeConfig(parsed, configFile);
|
|
102
|
+
}
|
|
103
|
+
if (configFile === CONFIG_FILE) {
|
|
104
|
+
try {
|
|
105
|
+
chmodSync(configFile, 384);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
}
|
|
84
109
|
return parsed;
|
|
85
110
|
}
|
|
86
111
|
function writeConfig(config, configFile = CONFIG_FILE) {
|
|
87
112
|
if (configFile === CONFIG_FILE) ensureConfigDir();
|
|
88
|
-
writeFileSync(configFile, JSON.stringify(config, null, 2), "utf-8");
|
|
113
|
+
writeFileSync(configFile, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
|
|
89
114
|
}
|
|
90
115
|
var ConfigSchema, CONFIG_DIR, CONFIG_FILE, PID_DIR, LOG_DIR, PG_DATA_DIR;
|
|
91
116
|
var init_config = __esm({
|
|
@@ -133,6 +158,18 @@ var init_config = __esm({
|
|
|
133
158
|
* files; `readConfig` auto-mints + persists when absent.
|
|
134
159
|
*/
|
|
135
160
|
serverActionsKey: z.string().length(44).optional(),
|
|
161
|
+
/**
|
|
162
|
+
* Base64-encoded 32-byte AES key used as better-auth's AUTH_SECRET (session
|
|
163
|
+
* cookie signing) for the web process. Deliberately SEPARATE from
|
|
164
|
+
* `workerSecret` (M-4): `workerSecret` is the runner's auth frontier — a
|
|
165
|
+
* leak of it must not also let an attacker forge a signed session cookie.
|
|
166
|
+
*
|
|
167
|
+
* Optional in the schema for back-compat with pre-existing config.json
|
|
168
|
+
* files; `readConfig` auto-mints + persists when absent. On an existing
|
|
169
|
+
* install this invalidates any active better-auth sessions ONCE (users
|
|
170
|
+
* re-login) — an acceptable one-time cost for closing the amplification.
|
|
171
|
+
*/
|
|
172
|
+
authSecret: z.string().length(44).optional(),
|
|
136
173
|
bind: z.enum(["loopback", "lan"]).default("loopback"),
|
|
137
174
|
bearerToken: z.string().optional(),
|
|
138
175
|
/**
|
|
@@ -6037,6 +6074,12 @@ var init_policies = __esm({
|
|
|
6037
6074
|
});
|
|
6038
6075
|
|
|
6039
6076
|
// ../../node_modules/.pnpm/drizzle-orm@0.45.2_@electri_96bd1661c4535b1384d37581a38c7c74/node_modules/drizzle-orm/pg-core/primary-keys.js
|
|
6077
|
+
function primaryKey(...config) {
|
|
6078
|
+
if (config[0].columns) {
|
|
6079
|
+
return new PrimaryKeyBuilder(config[0].columns, config[0].name);
|
|
6080
|
+
}
|
|
6081
|
+
return new PrimaryKeyBuilder(config);
|
|
6082
|
+
}
|
|
6040
6083
|
var PrimaryKeyBuilder, PrimaryKey;
|
|
6041
6084
|
var init_primary_keys = __esm({
|
|
6042
6085
|
"../../node_modules/.pnpm/drizzle-orm@0.45.2_@electri_96bd1661c4535b1384d37581a38c7c74/node_modules/drizzle-orm/pg-core/primary-keys.js"() {
|
|
@@ -6400,19 +6443,19 @@ function extractTablesRelationalConfig(schema, configHelpers) {
|
|
|
6400
6443
|
const relations2 = value.config(
|
|
6401
6444
|
configHelpers(value.table)
|
|
6402
6445
|
);
|
|
6403
|
-
let
|
|
6446
|
+
let primaryKey2;
|
|
6404
6447
|
for (const [relationName, relation] of Object.entries(relations2)) {
|
|
6405
6448
|
if (tableName) {
|
|
6406
6449
|
const tableConfig = tablesConfig[tableName];
|
|
6407
6450
|
tableConfig.relations[relationName] = relation;
|
|
6408
|
-
if (
|
|
6409
|
-
tableConfig.primaryKey.push(...
|
|
6451
|
+
if (primaryKey2) {
|
|
6452
|
+
tableConfig.primaryKey.push(...primaryKey2);
|
|
6410
6453
|
}
|
|
6411
6454
|
} else {
|
|
6412
6455
|
if (!(dbName in relationsBuffer)) {
|
|
6413
6456
|
relationsBuffer[dbName] = {
|
|
6414
6457
|
relations: {},
|
|
6415
|
-
primaryKey
|
|
6458
|
+
primaryKey: primaryKey2
|
|
6416
6459
|
};
|
|
6417
6460
|
}
|
|
6418
6461
|
relationsBuffer[dbName].relations[relationName] = relation;
|
|
@@ -11883,6 +11926,13 @@ var init_root_agent = __esm({
|
|
|
11883
11926
|
}
|
|
11884
11927
|
});
|
|
11885
11928
|
|
|
11929
|
+
// ../../packages/shared/src/system-prompt-cache.ts
|
|
11930
|
+
var init_system_prompt_cache = __esm({
|
|
11931
|
+
"../../packages/shared/src/system-prompt-cache.ts"() {
|
|
11932
|
+
"use strict";
|
|
11933
|
+
}
|
|
11934
|
+
});
|
|
11935
|
+
|
|
11886
11936
|
// ../../packages/shared/src/connector-catalog.ts
|
|
11887
11937
|
var init_connector_catalog = __esm({
|
|
11888
11938
|
"../../packages/shared/src/connector-catalog.ts"() {
|
|
@@ -11914,7 +11964,10 @@ var init_model_catalog = __esm({
|
|
|
11914
11964
|
// deepseek-chat is the production alias for DeepSeek V3. Standard tool
|
|
11915
11965
|
// calling with forced tool_choice supported.
|
|
11916
11966
|
capabilities: { tools: true, forcedToolChoice: true },
|
|
11917
|
-
contextWindow: 128e3
|
|
11967
|
+
contextWindow: 128e3,
|
|
11968
|
+
// Standard (cache-miss) rate per DeepSeek's public pricing page —
|
|
11969
|
+
// verify against api-docs.deepseek.com/quick_start/pricing if this drifts.
|
|
11970
|
+
pricing: { inputPerMillionUsd: 0.27, outputPerMillionUsd: 1.1 }
|
|
11918
11971
|
},
|
|
11919
11972
|
{
|
|
11920
11973
|
modelId: "deepseek-reasoner",
|
|
@@ -11924,7 +11977,8 @@ var init_model_catalog = __esm({
|
|
|
11924
11977
|
// reasoning:true). Also requires reasoning_content to be echoed back on
|
|
11925
11978
|
// assistant messages with tool_calls (the deepseek fetch shim handles this).
|
|
11926
11979
|
capabilities: { tools: true, forcedToolChoice: true, reasoning: true },
|
|
11927
|
-
contextWindow: 128e3
|
|
11980
|
+
contextWindow: 128e3,
|
|
11981
|
+
pricing: { inputPerMillionUsd: 0.55, outputPerMillionUsd: 2.19 }
|
|
11928
11982
|
}
|
|
11929
11983
|
],
|
|
11930
11984
|
// ─── Native MiniMax (api.minimax.io/anthropic) ───────────────────────────────
|
|
@@ -11937,6 +11991,11 @@ var init_model_catalog = __esm({
|
|
|
11937
11991
|
// forced tool_choice with a 400/404 (observed on M3 via OpenRouter); the
|
|
11938
11992
|
// runtime completion floor covers it. Context windows are docs-sourced where
|
|
11939
11993
|
// available, else a conservative 200K until a live probe confirms.
|
|
11994
|
+
// No `pricing` yet (unlike deepseek above) — no confidently-current MiniMax
|
|
11995
|
+
// rate card verified at write time. Left as documented debt (Fix #21): the
|
|
11996
|
+
// derived-cost fallback returns 0 for these until pricing is added, same as
|
|
11997
|
+
// every other unpriced entry in this catalog; Guard 1a (token budget) is the
|
|
11998
|
+
// backstop meanwhile.
|
|
11940
11999
|
minimax: [
|
|
11941
12000
|
{
|
|
11942
12001
|
modelId: "MiniMax-M3",
|
|
@@ -12220,6 +12279,7 @@ var init_src2 = __esm({
|
|
|
12220
12279
|
init_operation();
|
|
12221
12280
|
init_providers();
|
|
12222
12281
|
init_root_agent();
|
|
12282
|
+
init_system_prompt_cache();
|
|
12223
12283
|
init_connector_catalog();
|
|
12224
12284
|
init_community_skill_catalog();
|
|
12225
12285
|
init_model_catalog();
|
|
@@ -12370,7 +12430,13 @@ var init_entities = __esm({
|
|
|
12370
12430
|
check(
|
|
12371
12431
|
"entity_members_role_check",
|
|
12372
12432
|
sql`${table.role} IN ('owner', 'admin', 'member', 'viewer')`
|
|
12373
|
-
)
|
|
12433
|
+
),
|
|
12434
|
+
// R5 (audit #2 follow-up, found while sweeping helpers.ts for the SAME
|
|
12435
|
+
// class of phantom constraint): the pglite test DDL already assumed a
|
|
12436
|
+
// user can only be a member of an entity once — this table had no such
|
|
12437
|
+
// constraint in the real schema/migrations. Both columns are NOT NULL, so
|
|
12438
|
+
// a plain UNIQUE is correct (no NULLS NOT DISTINCT needed).
|
|
12439
|
+
uniqueIndex("entity_members_entity_user_unique").on(table.entityId, table.userId)
|
|
12374
12440
|
]
|
|
12375
12441
|
);
|
|
12376
12442
|
}
|
|
@@ -12422,7 +12488,7 @@ var init_agents = __esm({
|
|
|
12422
12488
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
12423
12489
|
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
12424
12490
|
name: text("name").notNull(),
|
|
12425
|
-
slug: text("slug").notNull()
|
|
12491
|
+
slug: text("slug").notNull(),
|
|
12426
12492
|
personality: text("personality").notNull(),
|
|
12427
12493
|
model: text("model").default("claude-sonnet-4-6-20260217"),
|
|
12428
12494
|
llmKeyId: uuid("llm_key_id").references(() => entityLlmKeys.id, { onDelete: "set null" }),
|
|
@@ -12472,7 +12538,17 @@ var init_agents = __esm({
|
|
|
12472
12538
|
"agents_orchestrator_mode_check",
|
|
12473
12539
|
sql`${table.orchestratorMode} IN ('router', 'planner') OR ${table.orchestratorMode} IS NULL`
|
|
12474
12540
|
),
|
|
12475
|
-
check("agents_max_tokens_per_job_check", sql`${table.maxTokensPerJob} >= 0`)
|
|
12541
|
+
check("agents_max_tokens_per_job_check", sql`${table.maxTokensPerJob} >= 0`),
|
|
12542
|
+
// F-6 (audit #2): slug was UNIQUE GLOBALLY, so a 2nd workspace/entity
|
|
12543
|
+
// installing the same community skill's companion agent (or any agent
|
|
12544
|
+
// sharing a slug with another entity's agent) would crash the insert —
|
|
12545
|
+
// and, in multi-user mode, let one entity enumerate/squat another's
|
|
12546
|
+
// slugs. Scoped to (entity_id, slug): every real insert always sets
|
|
12547
|
+
// entityId (createAgentRepo takes it as a required param — no production
|
|
12548
|
+
// path leaves it NULL), but the column itself is nullable, so
|
|
12549
|
+
// NULLS NOT DISTINCT closes the same NULL-entity gap as DB-1/agent_plugins
|
|
12550
|
+
// (multiple (NULL, 'x') rows would otherwise all satisfy a plain UNIQUE).
|
|
12551
|
+
unique("agents_entity_slug_unique").on(table.entityId, table.slug).nullsNotDistinct()
|
|
12476
12552
|
]
|
|
12477
12553
|
);
|
|
12478
12554
|
agentAssignments = pgTable(
|
|
@@ -12485,7 +12561,18 @@ var init_agents = __esm({
|
|
|
12485
12561
|
instructions: text("instructions"),
|
|
12486
12562
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
12487
12563
|
},
|
|
12488
|
-
() => [
|
|
12564
|
+
(table) => [
|
|
12565
|
+
// F-18 (audit #2): no index beyond the PK meant updateAgentAction's
|
|
12566
|
+
// delete-then-insert (apps/web/src/lib/actions.ts) could race and leave the
|
|
12567
|
+
// same sub-agent attached twice under one orchestrator (duplicated in the
|
|
12568
|
+
// team-block). The unique index's leading column (orchestrator_id) also
|
|
12569
|
+
// serves the hot read path (team-block.ts, assign-tools.ts, detach-agent.ts
|
|
12570
|
+
// all look up by orchestratorId alone) — no separate index needed.
|
|
12571
|
+
unique("agent_assignments_orchestrator_sub_agent_unique").on(
|
|
12572
|
+
table.orchestratorId,
|
|
12573
|
+
table.subAgentId
|
|
12574
|
+
)
|
|
12575
|
+
]
|
|
12489
12576
|
);
|
|
12490
12577
|
agentBudgets = pgTable(
|
|
12491
12578
|
"agent_budgets",
|
|
@@ -12614,8 +12701,19 @@ var init_jobs = __esm({
|
|
|
12614
12701
|
sql`${table.createdAt} DESC`
|
|
12615
12702
|
),
|
|
12616
12703
|
index("idx_agent_jobs_parent_job_id").on(table.parentJobId),
|
|
12617
|
-
|
|
12704
|
+
// idx_jobs_parent (F-20, audit #2) was an exact duplicate of
|
|
12705
|
+
// idx_agent_jobs_parent_job_id above — same column, same order, both from
|
|
12706
|
+
// migration 0000. Dropped in migration 0054; kept this one (matches the
|
|
12707
|
+
// idx_agent_jobs_* naming convention used everywhere else on this table).
|
|
12618
12708
|
index("idx_jobs_status").on(table.status, table.createdAt),
|
|
12709
|
+
// DB-3 (audit #2): findUndeliveredRootJobIds (cron/deliver-results.ts)
|
|
12710
|
+
// joins on this column filtered to IS NULL every tick; the cron recovery
|
|
12711
|
+
// scans (findPendingJobsToRecover, resetOrphanedJobs, failStalePendingJobs
|
|
12712
|
+
// in cron/reset-orphans.ts) also filter by status without an entity_id,
|
|
12713
|
+
// which idx_jobs_status (kept above) still serves — completed rows vastly
|
|
12714
|
+
// outnumber the still-open ones, so a partial index keyed on the open set
|
|
12715
|
+
// stays small regardless of table growth.
|
|
12716
|
+
index("idx_agent_jobs_completed_at_null").on(table.completedAt).where(sql`${table.completedAt} IS NULL`),
|
|
12619
12717
|
check(
|
|
12620
12718
|
"agent_jobs_status_check",
|
|
12621
12719
|
sql`${table.status} IN ('pending','processing','completed','failed','awaiting_approval','awaiting_delegation','cancelled')`
|
|
@@ -12678,6 +12776,10 @@ var init_tasks = __esm({
|
|
|
12678
12776
|
index("idx_agent_tasks_orchestrator_status").on(table.orchestratorId, table.status),
|
|
12679
12777
|
index("idx_agent_tasks_status").on(table.status),
|
|
12680
12778
|
index("idx_agent_tasks_assigned").on(table.assignedAgentId),
|
|
12779
|
+
// DB-3 (audit #2): findUndeliveredRootJobIds (apps/runner/src/cron/
|
|
12780
|
+
// deliver-results.ts) scans `WHERE root_job_id IS NOT NULL` on every cron
|
|
12781
|
+
// tick and had no index to support it — full scan on the hottest task table.
|
|
12782
|
+
index("idx_agent_tasks_root_job_id").on(table.rootJobId),
|
|
12681
12783
|
check(
|
|
12682
12784
|
"agent_tasks_status_check",
|
|
12683
12785
|
sql`${table.status} IN ('todo','in_progress','done','cancelled','blocked')`
|
|
@@ -12862,7 +12964,16 @@ var init_approvals = __esm({
|
|
|
12862
12964
|
check(
|
|
12863
12965
|
"approval_rules_action_check",
|
|
12864
12966
|
sql`${table.action} IN ('auto_approve','require_approval','block')`
|
|
12865
|
-
)
|
|
12967
|
+
),
|
|
12968
|
+
// DB-1 (audit #2): one canonical rule per (entity, agent-or-null, tool) —
|
|
12969
|
+
// without this, two concurrent setAgentApprovalRuleAction calls (or any
|
|
12970
|
+
// direct insert) can leave two rows for the same scope with DIVERGENT
|
|
12971
|
+
// actions, and matchApprovalRule's `.find()` picks whichever the SELECT
|
|
12972
|
+
// happens to return first — a non-deterministic approval gate. agentId IS
|
|
12973
|
+
// NULL marks an entity-wide rule (e.g. the run_command LAN master-switch),
|
|
12974
|
+
// so a plain UNIQUE would treat two NULL rows as distinct and let the
|
|
12975
|
+
// duplicate back in; NULLS NOT DISTINCT (PG15+) closes that gap.
|
|
12976
|
+
unique("approval_rules_entity_agent_tool_unique").on(table.entityId, table.agentId, table.toolName).nullsNotDistinct()
|
|
12866
12977
|
]
|
|
12867
12978
|
);
|
|
12868
12979
|
}
|
|
@@ -12994,8 +13105,8 @@ var init_skills = __esm({
|
|
|
12994
13105
|
{
|
|
12995
13106
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
12996
13107
|
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
12997
|
-
name: text("name").notNull()
|
|
12998
|
-
slug: text("slug").notNull()
|
|
13108
|
+
name: text("name").notNull(),
|
|
13109
|
+
slug: text("slug").notNull(),
|
|
12999
13110
|
content: text("content").notNull(),
|
|
13000
13111
|
active: boolean("active").default(true),
|
|
13001
13112
|
description: text("description"),
|
|
@@ -13035,7 +13146,20 @@ var init_skills = __esm({
|
|
|
13035
13146
|
(table) => [
|
|
13036
13147
|
index("idx_agent_skills_entity_id").on(table.entityId),
|
|
13037
13148
|
index("idx_skills_active").on(table.active, table.slug),
|
|
13038
|
-
index("idx_agent_skills_is_community").on(table.isCommunity)
|
|
13149
|
+
index("idx_agent_skills_is_community").on(table.isCommunity),
|
|
13150
|
+
// F-6 (audit #2): slug AND name were UNIQUE GLOBALLY — a 2nd workspace/
|
|
13151
|
+
// entity installing the same community skill (e.g. slug 'comfyui') or
|
|
13152
|
+
// creating a skill with the same display name crashed the insert, and in
|
|
13153
|
+
// multi-user mode let one entity squat/enumerate another's slugs or
|
|
13154
|
+
// names. Scoped to (entity_id, slug) / (entity_id, name): every real
|
|
13155
|
+
// insert always sets entityId (createSkillRepo takes it as a required
|
|
13156
|
+
// param; the system-skill seeder and community-skill installer both pass
|
|
13157
|
+
// it too — no production path leaves it NULL), but the column itself is
|
|
13158
|
+
// nullable, so NULLS NOT DISTINCT closes the same NULL-entity gap as
|
|
13159
|
+
// DB-1/agent_plugins (multiple (NULL, 'x') rows would otherwise all
|
|
13160
|
+
// satisfy a plain UNIQUE).
|
|
13161
|
+
unique("agent_skills_entity_slug_unique").on(table.entityId, table.slug).nullsNotDistinct(),
|
|
13162
|
+
unique("agent_skills_entity_name_unique").on(table.entityId, table.name).nullsNotDistinct()
|
|
13039
13163
|
]
|
|
13040
13164
|
);
|
|
13041
13165
|
skillVersions = pgTable(
|
|
@@ -13092,7 +13216,14 @@ var init_skills = __esm({
|
|
|
13092
13216
|
filesWritable: boolean("files_writable").notNull().default(false),
|
|
13093
13217
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
13094
13218
|
},
|
|
13095
|
-
() => [
|
|
13219
|
+
(table) => [
|
|
13220
|
+
// DB-2 (audit #2): the table had no index beyond the PK, so assignSkillRepo's
|
|
13221
|
+
// check-then-insert (packages/db/src/repos/skills.ts) could race and leave
|
|
13222
|
+
// the same skill assigned twice to one agent. The unique index's leading
|
|
13223
|
+
// column (agent_id) also serves the hot read path (execute.ts loads all
|
|
13224
|
+
// skills for an agent) — no separate index needed on top of it.
|
|
13225
|
+
unique("agent_skill_assignments_agent_skill_unique").on(table.agentId, table.skillId)
|
|
13226
|
+
]
|
|
13096
13227
|
);
|
|
13097
13228
|
}
|
|
13098
13229
|
});
|
|
@@ -13224,7 +13355,14 @@ var init_mcp = __esm({
|
|
|
13224
13355
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
13225
13356
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
13226
13357
|
},
|
|
13227
|
-
(table) => [
|
|
13358
|
+
(table) => [
|
|
13359
|
+
index("idx_mcp_connections_entity").on(table.entityId),
|
|
13360
|
+
// R5 (audit #2 follow-up): the pglite test DDL (packages/db/src/tests/
|
|
13361
|
+
// helpers.ts) already assumed a per-entity unique slug here — same class
|
|
13362
|
+
// of phantom-constraint bug as DB-2/F-18. entityId is NOT NULL on this
|
|
13363
|
+
// table, so a plain UNIQUE (no NULLS NOT DISTINCT needed) is correct.
|
|
13364
|
+
uniqueIndex("mcp_connections_entity_slug_unique").on(table.entityId, table.slug)
|
|
13365
|
+
]
|
|
13228
13366
|
);
|
|
13229
13367
|
}
|
|
13230
13368
|
});
|
|
@@ -13279,7 +13417,14 @@ var init_misc = __esm({
|
|
|
13279
13417
|
check(
|
|
13280
13418
|
"agent_plugins_hook_check",
|
|
13281
13419
|
sql`${table.hook} IN ('pre_task','post_task','pre_tool','post_tool','on_memory_save')`
|
|
13282
|
-
)
|
|
13420
|
+
),
|
|
13421
|
+
// R5 (audit #2 follow-up): the pglite test DDL (packages/db/src/tests/
|
|
13422
|
+
// helpers.ts) already assumed a per-entity unique slug here — same class
|
|
13423
|
+
// of phantom-constraint bug as DB-2/F-18 (test mock ahead of the real
|
|
13424
|
+
// schema/migration). entityId is nullable (no `.notNull()` above), so
|
|
13425
|
+
// NULLS NOT DISTINCT closes the same NULL-entity gap as DB-1's
|
|
13426
|
+
// approval_rules constraint.
|
|
13427
|
+
unique("agent_plugins_entity_slug_unique").on(table.entityId, table.slug).nullsNotDistinct()
|
|
13283
13428
|
]
|
|
13284
13429
|
);
|
|
13285
13430
|
rateLimits = pgTable("rate_limits", {
|
|
@@ -13451,16 +13596,65 @@ var init_chat_messages = __esm({
|
|
|
13451
13596
|
});
|
|
13452
13597
|
|
|
13453
13598
|
// ../../packages/db/src/schema/app-settings.ts
|
|
13454
|
-
var appSettings;
|
|
13599
|
+
var appSettings, entitySettings;
|
|
13455
13600
|
var init_app_settings = __esm({
|
|
13456
13601
|
"../../packages/db/src/schema/app-settings.ts"() {
|
|
13457
13602
|
"use strict";
|
|
13458
13603
|
init_pg_core();
|
|
13604
|
+
init_entities();
|
|
13459
13605
|
appSettings = pgTable("app_settings", {
|
|
13460
13606
|
key: text("key").primaryKey(),
|
|
13461
13607
|
value: text("value").default("").notNull(),
|
|
13462
13608
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
13463
13609
|
});
|
|
13610
|
+
entitySettings = pgTable(
|
|
13611
|
+
"entity_settings",
|
|
13612
|
+
{
|
|
13613
|
+
entityId: uuid("entity_id").notNull().references(() => entities.id, { onDelete: "cascade" }),
|
|
13614
|
+
key: text("key").notNull(),
|
|
13615
|
+
value: text("value").default("").notNull(),
|
|
13616
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
13617
|
+
},
|
|
13618
|
+
(table) => [primaryKey({ columns: [table.entityId, table.key] })]
|
|
13619
|
+
);
|
|
13620
|
+
}
|
|
13621
|
+
});
|
|
13622
|
+
|
|
13623
|
+
// ../../packages/db/src/schema/telegram-allowed-chats.ts
|
|
13624
|
+
var telegramAllowedChats;
|
|
13625
|
+
var init_telegram_allowed_chats = __esm({
|
|
13626
|
+
"../../packages/db/src/schema/telegram-allowed-chats.ts"() {
|
|
13627
|
+
"use strict";
|
|
13628
|
+
init_pg_core();
|
|
13629
|
+
init_drizzle_orm();
|
|
13630
|
+
init_entities();
|
|
13631
|
+
init_agents();
|
|
13632
|
+
telegramAllowedChats = pgTable(
|
|
13633
|
+
"telegram_allowed_chats",
|
|
13634
|
+
{
|
|
13635
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
13636
|
+
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
13637
|
+
/** The bot the chat is talking to (the RECEIVING agent). */
|
|
13638
|
+
agentId: uuid("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
|
|
13639
|
+
/** Telegram chat id (private chat, group, or supergroup) as a string. */
|
|
13640
|
+
chatId: text("chat_id").notNull(),
|
|
13641
|
+
/** 'owner' = the bot's controller; 'member' = an approved additional chat. */
|
|
13642
|
+
role: text("role").notNull().default("member"),
|
|
13643
|
+
/** 'active' = may create jobs; 'pending' = awaiting the owner's confirmation. */
|
|
13644
|
+
status: text("status").notNull().default("pending"),
|
|
13645
|
+
/** Display name of the requester, for the owner's confirmation card. */
|
|
13646
|
+
requesterName: text("requester_name"),
|
|
13647
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
|
13648
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
|
|
13649
|
+
},
|
|
13650
|
+
(table) => [
|
|
13651
|
+
unique("telegram_allowed_chats_agent_chat_unique").on(table.agentId, table.chatId),
|
|
13652
|
+
index("idx_telegram_allowed_chats_agent").on(table.agentId),
|
|
13653
|
+
index("idx_telegram_allowed_chats_entity").on(table.entityId),
|
|
13654
|
+
check("telegram_allowed_chats_role_check", sql`${table.role} IN ('owner','member')`),
|
|
13655
|
+
check("telegram_allowed_chats_status_check", sql`${table.status} IN ('active','pending')`)
|
|
13656
|
+
]
|
|
13657
|
+
);
|
|
13464
13658
|
}
|
|
13465
13659
|
});
|
|
13466
13660
|
|
|
@@ -13512,12 +13706,14 @@ __export(schema_exports, {
|
|
|
13512
13706
|
entities: () => entities,
|
|
13513
13707
|
entityLlmKeys: () => entityLlmKeys,
|
|
13514
13708
|
entityMembers: () => entityMembers,
|
|
13709
|
+
entitySettings: () => entitySettings,
|
|
13515
13710
|
mcpConnections: () => mcpConnections,
|
|
13516
13711
|
mcpServers: () => mcpServers,
|
|
13517
13712
|
rateLimits: () => rateLimits,
|
|
13518
13713
|
sessions: () => sessions,
|
|
13519
13714
|
skillConnectors: () => skillConnectors,
|
|
13520
13715
|
skillVersions: () => skillVersions,
|
|
13716
|
+
telegramAllowedChats: () => telegramAllowedChats,
|
|
13521
13717
|
toolCalls: () => toolCalls,
|
|
13522
13718
|
userProfiles: () => userProfiles,
|
|
13523
13719
|
users: () => users,
|
|
@@ -13549,6 +13745,7 @@ var init_schema2 = __esm({
|
|
|
13549
13745
|
init_agent_workspaces();
|
|
13550
13746
|
init_chat_messages();
|
|
13551
13747
|
init_app_settings();
|
|
13748
|
+
init_telegram_allowed_chats();
|
|
13552
13749
|
}
|
|
13553
13750
|
});
|
|
13554
13751
|
|
|
@@ -13557,7 +13754,29 @@ function createClient(connectionString, options = {}) {
|
|
|
13557
13754
|
const pool = src_default(connectionString, {
|
|
13558
13755
|
max: options.max ?? 10,
|
|
13559
13756
|
// Fail fast on connection issues rather than hanging
|
|
13560
|
-
connect_timeout: 10
|
|
13757
|
+
connect_timeout: 10,
|
|
13758
|
+
// R2 (audit2 concurrency review): bound the two ways a query on this pool
|
|
13759
|
+
// can hang indefinitely with no client-side timeout at all. Sent as
|
|
13760
|
+
// Postgres startup/session parameters (postgres.js `connection` option),
|
|
13761
|
+
// so every connection in the pool gets them — no per-query opt-in needed.
|
|
13762
|
+
// - lock_timeout: the vector identified for the cron watchdog (R1) — a
|
|
13763
|
+
// query blocked waiting on a row lock previously waited forever.
|
|
13764
|
+
// 30s is comfortably longer than any legitimate lock wait in this
|
|
13765
|
+
// app's write patterns (single-row updates/inserts, no long-held
|
|
13766
|
+
// locks by design).
|
|
13767
|
+
// - idle_in_transaction_session_timeout: a transaction left open with
|
|
13768
|
+
// no further statements (BEGIN without COMMIT/ROLLBACK) is always a
|
|
13769
|
+
// bug, never a legitimate long-running operation — 60s is generous.
|
|
13770
|
+
// Deliberately NOT setting statement_timeout here: a global cap would
|
|
13771
|
+
// also apply to legitimate long-running operations (a large backfill, a
|
|
13772
|
+
// slow migration-adjacent query) and risk cutting them off mid-flight.
|
|
13773
|
+
// migrate.ts uses its own separate `postgres()` connection (not this
|
|
13774
|
+
// factory) for running migrations, so these timeouts don't touch
|
|
13775
|
+
// migrations at all either way.
|
|
13776
|
+
connection: {
|
|
13777
|
+
lock_timeout: 3e4,
|
|
13778
|
+
idle_in_transaction_session_timeout: 6e4
|
|
13779
|
+
}
|
|
13561
13780
|
});
|
|
13562
13781
|
const db = drizzle(pool, { schema: schema_exports });
|
|
13563
13782
|
return {
|
|
@@ -13584,12 +13803,23 @@ var init_transaction = __esm({
|
|
|
13584
13803
|
|
|
13585
13804
|
// ../../packages/secrets/src/index.ts
|
|
13586
13805
|
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";
|
|
13806
|
+
import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
13588
13807
|
import { homedir as homedir2 } from "os";
|
|
13589
13808
|
import { dirname as dirname3, join as join5 } from "path";
|
|
13809
|
+
function masterKeyPath() {
|
|
13810
|
+
return join5(homedir2(), ".nodalai", "secrets.key");
|
|
13811
|
+
}
|
|
13812
|
+
function masterKeyFileMissing(path = masterKeyPath()) {
|
|
13813
|
+
return !existsSync5(path);
|
|
13814
|
+
}
|
|
13815
|
+
function isEncrypted(value) {
|
|
13816
|
+
return value.startsWith(`enc:${VERSION}:`);
|
|
13817
|
+
}
|
|
13818
|
+
var VERSION;
|
|
13590
13819
|
var init_src3 = __esm({
|
|
13591
13820
|
"../../packages/secrets/src/index.ts"() {
|
|
13592
13821
|
"use strict";
|
|
13822
|
+
VERSION = "v1";
|
|
13593
13823
|
}
|
|
13594
13824
|
});
|
|
13595
13825
|
|
|
@@ -13605,6 +13835,29 @@ var init_credentials2 = __esm({
|
|
|
13605
13835
|
}
|
|
13606
13836
|
});
|
|
13607
13837
|
|
|
13838
|
+
// ../../packages/db/src/queries/master-key-guard.ts
|
|
13839
|
+
async function assertMasterKeyRestorable(db, options = {}) {
|
|
13840
|
+
if (!masterKeyFileMissing(options.keyPath)) return;
|
|
13841
|
+
const [credRow] = await db.select({ id: credentials.id }).from(credentials).limit(1);
|
|
13842
|
+
if (credRow) throw missingKeyError("a stored OAuth credential");
|
|
13843
|
+
const llmKeyRows = await db.select({ apiKey: entityLlmKeys.apiKey }).from(entityLlmKeys);
|
|
13844
|
+
const hasEncryptedLlmKey = llmKeyRows.some((r) => r.apiKey !== "" && isEncrypted(r.apiKey));
|
|
13845
|
+
if (hasEncryptedLlmKey) throw missingKeyError("a stored LLM API key");
|
|
13846
|
+
}
|
|
13847
|
+
function missingKeyError(what) {
|
|
13848
|
+
return new Error(
|
|
13849
|
+
`~/.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.`
|
|
13850
|
+
);
|
|
13851
|
+
}
|
|
13852
|
+
var init_master_key_guard = __esm({
|
|
13853
|
+
"../../packages/db/src/queries/master-key-guard.ts"() {
|
|
13854
|
+
"use strict";
|
|
13855
|
+
init_credentials();
|
|
13856
|
+
init_llm_keys();
|
|
13857
|
+
init_src3();
|
|
13858
|
+
}
|
|
13859
|
+
});
|
|
13860
|
+
|
|
13608
13861
|
// ../../packages/db/src/repos/agents.ts
|
|
13609
13862
|
var init_agents2 = __esm({
|
|
13610
13863
|
"../../packages/db/src/repos/agents.ts"() {
|
|
@@ -13648,6 +13901,7 @@ var init_src4 = __esm({
|
|
|
13648
13901
|
init_transaction();
|
|
13649
13902
|
init_schema2();
|
|
13650
13903
|
init_credentials2();
|
|
13904
|
+
init_master_key_guard();
|
|
13651
13905
|
init_agents2();
|
|
13652
13906
|
init_skills2();
|
|
13653
13907
|
init_retention();
|
|
@@ -13707,11 +13961,20 @@ var init_local_auth = __esm({
|
|
|
13707
13961
|
}
|
|
13708
13962
|
});
|
|
13709
13963
|
|
|
13964
|
+
// ../../packages/auth/src/lib/constant-time.ts
|
|
13965
|
+
import { createHash, timingSafeEqual } from "crypto";
|
|
13966
|
+
var init_constant_time = __esm({
|
|
13967
|
+
"../../packages/auth/src/lib/constant-time.ts"() {
|
|
13968
|
+
"use strict";
|
|
13969
|
+
}
|
|
13970
|
+
});
|
|
13971
|
+
|
|
13710
13972
|
// ../../packages/auth/src/providers/bearer-token.ts
|
|
13711
13973
|
var init_bearer_token = __esm({
|
|
13712
13974
|
"../../packages/auth/src/providers/bearer-token.ts"() {
|
|
13713
13975
|
"use strict";
|
|
13714
13976
|
init_local_trust();
|
|
13977
|
+
init_constant_time();
|
|
13715
13978
|
}
|
|
13716
13979
|
});
|
|
13717
13980
|
|
|
@@ -13750,6 +14013,7 @@ var init_seed = __esm({
|
|
|
13750
14013
|
});
|
|
13751
14014
|
|
|
13752
14015
|
// src/lib/env.ts
|
|
14016
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
13753
14017
|
function buildEnvForRunner(config, databaseUrl) {
|
|
13754
14018
|
const bind = config.bind === "loopback" ? "127.0.0.1" : "0.0.0.0";
|
|
13755
14019
|
const env = {
|
|
@@ -13773,7 +14037,19 @@ function buildEnvForRunner(config, databaseUrl) {
|
|
|
13773
14037
|
return env;
|
|
13774
14038
|
}
|
|
13775
14039
|
function resolveAuthMode(config) {
|
|
13776
|
-
if (config.
|
|
14040
|
+
if (config.bearerToken) {
|
|
14041
|
+
throw new Error(
|
|
14042
|
+
'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.'
|
|
14043
|
+
);
|
|
14044
|
+
}
|
|
14045
|
+
if (config.auth?.mode) {
|
|
14046
|
+
if (config.auth.mode === "local-trust" && config.bind === "lan") {
|
|
14047
|
+
throw new Error(
|
|
14048
|
+
'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.'
|
|
14049
|
+
);
|
|
14050
|
+
}
|
|
14051
|
+
return config.auth.mode;
|
|
14052
|
+
}
|
|
13777
14053
|
return config.bind === "lan" ? "local-auth" : "local-trust";
|
|
13778
14054
|
}
|
|
13779
14055
|
function buildEnvForWeb(config, databaseUrl) {
|
|
@@ -13797,8 +14073,16 @@ function buildEnvForWeb(config, databaseUrl) {
|
|
|
13797
14073
|
// The web itself doesn't bind on this — Next.js listens on 0.0.0.0 anyway.
|
|
13798
14074
|
BIND: bind,
|
|
13799
14075
|
NODE_ENV: "production",
|
|
13800
|
-
// AUTH_SECRET is required by better-auth in local-auth mode; harmless in
|
|
13801
|
-
|
|
14076
|
+
// AUTH_SECRET is required by better-auth in local-auth mode; harmless in
|
|
14077
|
+
// local-trust. M-4: deliberately SEPARATE from workerSecret — the two
|
|
14078
|
+
// used to share a value, so a leak of workerSecret (the runner's auth
|
|
14079
|
+
// frontier) would also let an attacker forge a signed better-auth session
|
|
14080
|
+
// cookie. readConfig() auto-mints authSecret, so this is set in every
|
|
14081
|
+
// normal boot path; the random fallback below only guards a caller that
|
|
14082
|
+
// somehow bypasses readConfig (ephemeral — would invalidate sessions on
|
|
14083
|
+
// every restart, which is a symptom to fix at the config layer, not a
|
|
14084
|
+
// reason to crash boot or fall back to workerSecret).
|
|
14085
|
+
AUTH_SECRET: config.authSecret ?? randomBytes3(32).toString("base64"),
|
|
13802
14086
|
// WORKER_SECRET — same value as the runner — so sendTaskAction can sign
|
|
13803
14087
|
// the POST /api/worker call. Without this the runner returns 403 and the
|
|
13804
14088
|
// job stays pending forever (cron only scans task-board, not API jobs).
|
|
@@ -14081,7 +14365,7 @@ __export(up_exports, {
|
|
|
14081
14365
|
import chalk from "chalk";
|
|
14082
14366
|
import ora from "ora";
|
|
14083
14367
|
import open from "open";
|
|
14084
|
-
import { randomBytes as
|
|
14368
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
14085
14369
|
async function killSilent(child) {
|
|
14086
14370
|
await killProcessTree(child);
|
|
14087
14371
|
}
|
|
@@ -14106,8 +14390,12 @@ async function runUp(opts = {}) {
|
|
|
14106
14390
|
const defaultConfig = {
|
|
14107
14391
|
// No `llm` section: the browser onboarding sets it via createLlmKeyAction.
|
|
14108
14392
|
ports: { web: 3e3, runner: 3001, postgres: 25432 },
|
|
14109
|
-
workerSecret:
|
|
14110
|
-
serverActionsKey:
|
|
14393
|
+
workerSecret: randomBytes4(32).toString("hex"),
|
|
14394
|
+
serverActionsKey: randomBytes4(32).toString("base64"),
|
|
14395
|
+
// M-4: authSecret is minted here (distinct from workerSecret) rather
|
|
14396
|
+
// than left for readConfig()'s auto-mint, because this in-memory
|
|
14397
|
+
// `config` is used directly below without a re-read.
|
|
14398
|
+
authSecret: randomBytes4(32).toString("base64"),
|
|
14111
14399
|
// loopback derives auth: local-trust (no login/signup). Leave `auth`
|
|
14112
14400
|
// unset so resolveAuthMode falls back to the loopback → local-trust map.
|
|
14113
14401
|
bind: "loopback"
|
|
@@ -14115,6 +14403,7 @@ async function runUp(opts = {}) {
|
|
|
14115
14403
|
writeConfig(defaultConfig);
|
|
14116
14404
|
config = defaultConfig;
|
|
14117
14405
|
}
|
|
14406
|
+
resolveAuthMode(config);
|
|
14118
14407
|
const orphans = [];
|
|
14119
14408
|
for (const [name, port] of [
|
|
14120
14409
|
["web", config.ports.web],
|
|
@@ -14220,6 +14509,17 @@ async function runUp(opts = {}) {
|
|
|
14220
14509
|
await pg.stop();
|
|
14221
14510
|
throw err;
|
|
14222
14511
|
}
|
|
14512
|
+
{
|
|
14513
|
+
const { db, close } = createClient(databaseUrl, { max: 1 });
|
|
14514
|
+
try {
|
|
14515
|
+
await assertMasterKeyRestorable(db);
|
|
14516
|
+
} catch (err) {
|
|
14517
|
+
await close();
|
|
14518
|
+
await pg.stop();
|
|
14519
|
+
throw err;
|
|
14520
|
+
}
|
|
14521
|
+
await close();
|
|
14522
|
+
}
|
|
14223
14523
|
const authMode = resolveAuthMode(config);
|
|
14224
14524
|
if (authMode === "local-trust") {
|
|
14225
14525
|
const seedSpinner = ora("Seeding default user and agent\u2026").start();
|
|
@@ -14269,13 +14569,13 @@ async function runUp(opts = {}) {
|
|
|
14269
14569
|
}
|
|
14270
14570
|
void (async () => {
|
|
14271
14571
|
try {
|
|
14272
|
-
const { getInstalledVersion: getInstalledVersion2, getLatestVersion: getLatestVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
14572
|
+
const { getInstalledVersion: getInstalledVersion2, getLatestVersion: getLatestVersion2, isNewerVersion: isNewerVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
14273
14573
|
const installed = getInstalledVersion2();
|
|
14274
14574
|
const latest = await Promise.race([
|
|
14275
14575
|
getLatestVersion2(),
|
|
14276
14576
|
new Promise((resolve2) => setTimeout(() => resolve2(null), 3e3))
|
|
14277
14577
|
]);
|
|
14278
|
-
if (latest !== null && latest
|
|
14578
|
+
if (latest !== null && isNewerVersion2(latest, installed)) {
|
|
14279
14579
|
console.log(chalk.cyan(` \u2139 v${latest} available \u2014 run \`nodal-agents update\``));
|
|
14280
14580
|
}
|
|
14281
14581
|
} catch {
|
|
@@ -14431,9 +14731,9 @@ __export(init_exports, {
|
|
|
14431
14731
|
import chalk2 from "chalk";
|
|
14432
14732
|
import prompts from "prompts";
|
|
14433
14733
|
import ora2 from "ora";
|
|
14434
|
-
import { randomBytes as
|
|
14734
|
+
import { randomBytes as randomBytes5 } from "crypto";
|
|
14435
14735
|
function generateSecret(bytes = 32) {
|
|
14436
|
-
return
|
|
14736
|
+
return randomBytes5(bytes).toString("hex");
|
|
14437
14737
|
}
|
|
14438
14738
|
async function runInit(options = {}) {
|
|
14439
14739
|
const existing = readConfig();
|
|
@@ -14454,7 +14754,7 @@ async function runInit(options = {}) {
|
|
|
14454
14754
|
const config2 = {
|
|
14455
14755
|
ports: { web: 3e3, runner: 3001, postgres: 25432 },
|
|
14456
14756
|
workerSecret: generateSecret(32),
|
|
14457
|
-
serverActionsKey:
|
|
14757
|
+
serverActionsKey: randomBytes5(32).toString("base64"),
|
|
14458
14758
|
bind: "lan",
|
|
14459
14759
|
auth: { mode: "local-auth" }
|
|
14460
14760
|
};
|