nodal-agents 0.8.6 → 0.8.8
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/README.md +1 -1
- package/cli.js +308 -37
- package/migrations/0082_entities_auto_run_paused.sql +32 -0
- package/migrations/0083_code_project_archives.sql +14 -0
- package/migrations/0084_release_wrong_auto_run_pause.sql +24 -0
- package/migrations/0085_workspaces_is_dev_folder.sql +33 -0
- package/migrations/0086_code_projects_rename_and_hide.sql +61 -0
- package/migrations/0087_workspaces_hidden_from_code.sql +44 -0
- package/migrations/meta/_journal.json +42 -0
- package/package.json +1 -1
- package/runner.js +3388 -1801
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +12 -12
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/server/app/(dashboard)/agents/[id]/channels/page.js +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/channels/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +4 -4
- 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 +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 +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 +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 +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 +1 -1
- package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/code/[id]/page.js +1 -1
- package/web/.next/server/app/(dashboard)/code/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/code/page.js +1 -1
- package/web/.next/server/app/(dashboard)/code/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_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_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +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 +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 +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_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/logs/page.js +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_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/memories/page.js +1 -1
- package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page.js +3 -3
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/settings/page.js +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 +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 +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 +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 +1 -1
- package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error/page.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 +3 -3
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +3 -3
- package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +3 -3
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page.js +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 +4 -4
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +4 -4
- 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/health/route.js +1 -1
- package/web/.next/server/app/login/page.js +1 -1
- package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/onboarding/page.js +2 -2
- package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app-paths-manifest.json +12 -12
- package/web/.next/server/chunks/1126.js +1 -1
- package/web/.next/server/chunks/1721.js +1749 -0
- package/web/.next/server/chunks/1824.js +1 -0
- package/web/.next/server/chunks/196.js +1 -0
- package/web/.next/server/chunks/{5149.js → 2009.js} +1 -1
- package/web/.next/server/chunks/210.js +1 -0
- package/web/.next/server/chunks/3927.js +1597 -0
- package/web/.next/server/chunks/42.js +2 -2
- package/web/.next/server/chunks/5384.js +1 -0
- package/web/.next/server/chunks/6484.js +1629 -0
- package/web/.next/server/chunks/7048.js +1 -0
- package/web/.next/server/chunks/7741.js +1 -1
- package/web/.next/server/chunks/7915.js +1 -0
- package/web/.next/server/chunks/{5877.js → 8191.js} +71 -89
- package/web/.next/server/chunks/8447.js +1 -1
- package/web/.next/server/chunks/9949.js +1746 -0
- package/web/.next/server/middleware-build-manifest.js +1 -1
- package/web/.next/server/middleware.js +4 -4
- 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/server/webpack-runtime.js +1 -1
- package/web/.next/static/chunks/3451-58a5c57e79371ae4.js +1629 -0
- package/web/.next/static/chunks/5034-e249414dc457d97d.js +1 -0
- package/web/.next/static/chunks/6646-35d756ba2c002ecf.js +1 -0
- package/web/.next/static/chunks/6967-3b77488c4b708136.js +1 -0
- package/web/.next/static/chunks/8423-57a00fc00778c1e1.js +1 -0
- package/web/.next/static/chunks/{9416-baf32aa3deceb0ed.js → 9416-391f60c712340297.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-dd6460b3a050ddf7.js +6 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/page-04ac243f09f1f620.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/approvals/page-dfd4cf86827c0aed.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/automations/page-0e724dfa1716a937.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/chat/page-041eedb2e6cf29fb.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/code/[id]/page-bed8be1148fb7d1d.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/code/page-ac3a349d07d95fd9.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/connectors/{page-a67da30a55296b54.js → page-0f39f3e59541badf.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-1719a805a2e39ccc.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/jobs/page-129c4cd41458438a.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/layout-1602118a43db0d42.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-16a376c609001dd2.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-7bd5a6f909d8a136.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/logs/page-a2d32cf8ffa4d4a6.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-86fac62ecfc87b98.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/memories/page-bd61659f6bbccb21.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/{page-1e05fa622609cdf1.js → page-70ea130d3de66e30.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/page-2fec30e99b307637.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-5a7dae2d7e9ce07f.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-c22bde781a44b751.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/new/page-0e8b89dc355078a8.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/page-93884e1ef158297f.js +1 -0
- package/web/.next/static/chunks/app/error-3d9af79e7856dcc8.js +1 -0
- package/web/.next/static/chunks/app/login/page-7303a5eca7eeb19b.js +1 -0
- package/web/.next/static/chunks/app/onboarding/{page-f02705f7710caa08.js → page-0ec8d988ee84bdb1.js} +2 -2
- package/web/.next/static/css/be2ece2e5b18d112.css +3 -0
- package/web/.next/server/app/(dashboard)/agents/[id]/channels/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/agents/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/approvals/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/automations/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/code/[id]/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/code/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/connectors/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/credentials/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/jobs/[id]/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/jobs/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/learned-skills/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/logs/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/mcp/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/memories/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/settings/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/settings/root-context/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/skills/new/page.js.nft.json +0 -1
- package/web/.next/server/app/(dashboard)/skills/page.js.nft.json +0 -1
- package/web/.next/server/app/_global-error/page.js.nft.json +0 -1
- package/web/.next/server/app/_not-found/page.js.nft.json +0 -1
- package/web/.next/server/app/api/auth/[...all]/route.js.nft.json +0 -1
- package/web/.next/server/app/api/health/route.js.nft.json +0 -1
- package/web/.next/server/app/api/oauth/[provider]/callback/route.js.nft.json +0 -1
- package/web/.next/server/app/api/oauth/[provider]/start/route.js.nft.json +0 -1
- package/web/.next/server/app/auth/callback/route.js.nft.json +0 -1
- package/web/.next/server/app/login/page.js.nft.json +0 -1
- package/web/.next/server/app/onboarding/page.js.nft.json +0 -1
- package/web/.next/server/chunks/2745.js +0 -1
- package/web/.next/server/chunks/2938.js +0 -1
- package/web/.next/server/chunks/4630.js +0 -1
- package/web/.next/server/chunks/4639.js +0 -1744
- package/web/.next/server/chunks/4711.js +0 -1
- package/web/.next/server/chunks/4924.js +0 -1741
- package/web/.next/server/chunks/6680.js +0 -1615
- package/web/.next/server/chunks/9729.js +0 -1
- package/web/.next/server/middleware.js.nft.json +0 -1
- package/web/.next/static/chunks/4994-7effa935c5703218.js +0 -1
- package/web/.next/static/chunks/5034-7b0857c231a2db7e.js +0 -1
- package/web/.next/static/chunks/8423-bf78a9de790d8ebc.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-f55badb51d6e10b1.js +0 -6
- package/web/.next/static/chunks/app/(dashboard)/agents/page-d256dbe832dfb238.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/approvals/page-99ec2b36c44ed3d3.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/automations/page-9242e9b754d98c68.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/chat/page-7f9c147e49730cb4.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/code/[id]/page-46acd3da2c78b6df.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/code/page-12598290e23cb30a.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-793efbdc85566e24.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/page-cce723799dad2458.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/layout-5976fd8a05f7cefd.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-0f3b9332eeaf4282.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-4e0fef0a652f4e8a.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/logs/page-31b25388df6e0212.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-2364c615affa47d3.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/memories/page-52944e872698b85f.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/page-19ac2c3c794fa0f7.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-cb20f211b06e807f.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-5bc99166041fcf0d.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/new/page-453b7333a310658c.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/page-055a17f39f670958.js +0 -1
- package/web/.next/static/chunks/app/error-d7d2badaa59bb441.js +0 -1
- package/web/.next/static/chunks/app/login/page-02aa17b387bdb416.js +0 -1
- package/web/.next/static/css/f0904e0d2f4e1435.css +0 -3
- /package/web/.next/static/{PMZOOK9t9OB35ORr4mOpd → Ox1nSNbTz9Sz93pV6rEcD}/_buildManifest.js +0 -0
- /package/web/.next/static/{PMZOOK9t9OB35ORr4mOpd → Ox1nSNbTz9Sz93pV6rEcD}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ Every agent runs on the model **you** choose, with the tools **you** grant, in a
|
|
|
46
46
|
### 🏠 Own your whole stack
|
|
47
47
|
|
|
48
48
|
- **Local-first** — single command, embedded Postgres, zero cloud dependency.
|
|
49
|
-
- **Your data & keys** — conversations, memory, and credentials never leave your machine
|
|
49
|
+
- **Your data & keys** — conversations, memory, and credentials never leave your machine, and every credential is AES-256-GCM encrypted at rest: API keys, OAuth tokens, MCP server secrets, the bot tokens of every messaging channel, and your webhook secrets.
|
|
50
50
|
- **Workspaces** — multiple isolated workspaces on one install (personal vs work), each with its own agents, skills, connectors, jobs, and memory.
|
|
51
51
|
- **No markup** — bring your own API keys; you pay the provider directly, nothing on top.
|
|
52
52
|
|
package/cli.js
CHANGED
|
@@ -13085,6 +13085,33 @@ var init_model_catalog = __esm({
|
|
|
13085
13085
|
contextWindow: 1048576,
|
|
13086
13086
|
pricing: { inputPerMillionUsd: 1.4, outputPerMillionUsd: 4.4 }
|
|
13087
13087
|
},
|
|
13088
|
+
{
|
|
13089
|
+
modelId: "z-ai/glm-5.3-flash",
|
|
13090
|
+
label: "GLM 5.3 Flash",
|
|
13091
|
+
// The cheap, fast sibling of 5.3 — ~19x cheaper in, ~18x cheaper out,
|
|
13092
|
+
// and a LARGER context (1.31M vs 1.05M). Two real differences from the
|
|
13093
|
+
// full 5.3, both read off OpenRouter's /api/v1/models on 2026-08-28:
|
|
13094
|
+
// - it is MULTIMODAL (input_modalities: text, image, video) where 5.3
|
|
13095
|
+
// is text-only, so it is in VISION_MODEL_IDS below;
|
|
13096
|
+
// - reasoning is supported (`reasoning`, `reasoning_effort` are in
|
|
13097
|
+
// supported_parameters) but NOT mandatory the way 5.2/5.3's is —
|
|
13098
|
+
// nothing upstream says this one always thinks, so 'off' stays on
|
|
13099
|
+
// the UI scale rather than being hidden on a guess.
|
|
13100
|
+
// forcedToolChoice:false follows the family posture: `tool_choice` is
|
|
13101
|
+
// accepted, but nothing states 'required' is honoured, and the runtime
|
|
13102
|
+
// tool_choice floor relaxes it anyway.
|
|
13103
|
+
capabilities: {
|
|
13104
|
+
tools: true,
|
|
13105
|
+
forcedToolChoice: false,
|
|
13106
|
+
reasoning: true,
|
|
13107
|
+
reasoningControl: {
|
|
13108
|
+
kind: "effort",
|
|
13109
|
+
levels: ["low", "medium", "high", "max"]
|
|
13110
|
+
}
|
|
13111
|
+
},
|
|
13112
|
+
contextWindow: 1310720,
|
|
13113
|
+
pricing: { inputPerMillionUsd: 0.075, outputPerMillionUsd: 0.25 }
|
|
13114
|
+
},
|
|
13088
13115
|
// Moonshot (Kimi)
|
|
13089
13116
|
{
|
|
13090
13117
|
modelId: "moonshotai/kimi-k2.6",
|
|
@@ -13242,6 +13269,10 @@ var init_model_catalog = __esm({
|
|
|
13242
13269
|
"openai/gpt-5.6-luna-pro",
|
|
13243
13270
|
"openai/gpt-5.6-terra-pro",
|
|
13244
13271
|
"qwen/qwen3.8-max",
|
|
13272
|
+
// Verified via OpenRouter /api/v1/models input_modalities on 2026-08-28:
|
|
13273
|
+
// z-ai/glm-5.3-flash reports ["text","image","video"]. The full z-ai/glm-5.3
|
|
13274
|
+
// reports ["text"] only and is deliberately NOT in this set.
|
|
13275
|
+
"z-ai/glm-5.3-flash",
|
|
13245
13276
|
// Verified via OpenRouter /api/v1/models input_modalities (2026-07-20).
|
|
13246
13277
|
// qwen/qwen3.7-max is deliberately absent — text-only per the same source.
|
|
13247
13278
|
"x-ai/grok-4.5",
|
|
@@ -13500,11 +13531,19 @@ var init_entities = __esm({
|
|
|
13500
13531
|
// Controls whether agent-authored skills are auto-assigned to the authoring
|
|
13501
13532
|
// agent ('auto') or queued for the entity owner to approve ('approval').
|
|
13502
13533
|
skillAssignmentMode: text("skill_assignment_mode").notNull().default("approval").$type(),
|
|
13503
|
-
|
|
13504
|
-
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
|
|
13534
|
+
/**
|
|
13535
|
+
* Frein d'urgence de l'auto-exécution (0082, inversion du modèle à deux
|
|
13536
|
+
* clés — décision Quentin 24/08). true = TOUT l'auto-run de code du
|
|
13537
|
+
* workspace est en pause : les règles auto_approve des outils d'exécution
|
|
13538
|
+
* (run_command, code_task, scripts, MCP stdio) deviennent dormantes sans
|
|
13539
|
+
* être supprimées — le runner les déshabille à l'exécution. false
|
|
13540
|
+
* (défaut) = les toggles Yolo par agent (owner-only) s'appliquent tels
|
|
13541
|
+
* quels. Remplace lan_command_yolo, qui était une PRÉ-CONDITION
|
|
13542
|
+
* d'activation : redondante (les toggles par agent étaient déjà
|
|
13543
|
+
* owner-only) et pénible — sa vraie valeur était le coupe-circuit, qui
|
|
13544
|
+
* devient son seul rôle.
|
|
13545
|
+
*/
|
|
13546
|
+
autoRunPaused: boolean("auto_run_paused").notNull().default(false),
|
|
13508
13547
|
/**
|
|
13509
13548
|
* Interrupteur maitre du serveur MCP (PR C, decision Quentin 23/08).
|
|
13510
13549
|
* Defaut FERME : un point d'entree externe qui cree des jobs doit etre
|
|
@@ -13654,12 +13693,16 @@ var init_agents = __esm({
|
|
|
13654
13693
|
// action layer guarantees at least one provider stays enabled.
|
|
13655
13694
|
cliDefaults: jsonb("cli_defaults").$type(),
|
|
13656
13695
|
// Which harness drives this agent (étape E). 'nodal' (default) = the
|
|
13657
|
-
// Nodal runner loop. 'claude-code' = the agent IS a
|
|
13658
|
-
// persona from DB
|
|
13659
|
-
//
|
|
13660
|
-
//
|
|
13661
|
-
//
|
|
13662
|
-
//
|
|
13696
|
+
// Nodal runner loop. 'claude-code' and 'codex' = the agent IS a session of
|
|
13697
|
+
// that CLI: persona from DB, channels/cron/workspaces/budget/approvals stay
|
|
13698
|
+
// Nodal's, but the LOOP, tools and context are the CLI's — said as-is to
|
|
13699
|
+
// the user (dispatcher, not brain). A DATA field, never a hardcoded
|
|
13700
|
+
// per-agent branch (invariant #1).
|
|
13701
|
+
//
|
|
13702
|
+
// 'codex' a été RÉSERVÉ ici du 19/08 au 27/08 : accepté par la contrainte,
|
|
13703
|
+
// refusé par le Zod de l'action et par le runner. Ouvert le 27/08 —
|
|
13704
|
+
// apps/runner/src/cli-runtime/provider.ts porte désormais LE tableau des
|
|
13705
|
+
// runtimes servis ; cette contrainte et lui doivent rester d'accord.
|
|
13663
13706
|
runtime: text("runtime").default("nodal").notNull(),
|
|
13664
13707
|
// Runtime-agent permission posture, as DATA: mode 'read' (default when
|
|
13665
13708
|
// NULL) hides the CLI's write tools; 'write' allows workspace edits
|
|
@@ -14621,6 +14664,35 @@ var init_mcp = __esm({
|
|
|
14621
14664
|
}
|
|
14622
14665
|
});
|
|
14623
14666
|
|
|
14667
|
+
// ../../packages/db/src/schema/code-projects.ts
|
|
14668
|
+
var codeProjects;
|
|
14669
|
+
var init_code_projects = __esm({
|
|
14670
|
+
"../../packages/db/src/schema/code-projects.ts"() {
|
|
14671
|
+
"use strict";
|
|
14672
|
+
init_pg_core();
|
|
14673
|
+
init_entities();
|
|
14674
|
+
codeProjects = pgTable(
|
|
14675
|
+
"code_projects",
|
|
14676
|
+
{
|
|
14677
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
14678
|
+
entityId: uuid("entity_id").notNull().references(() => entities.id, { onDelete: "cascade" }),
|
|
14679
|
+
/** Chemin absolu slash-normalisé du projet (la clé de groupement dérivée). */
|
|
14680
|
+
projectPath: text("project_path").notNull(),
|
|
14681
|
+
/** Nom choisi par le propriétaire. NULL = nom du dossier. */
|
|
14682
|
+
displayName: text("display_name"),
|
|
14683
|
+
/** Masqué de la liste ET du contexte injecté aux agents. */
|
|
14684
|
+
hidden: boolean("hidden").notNull().default(false),
|
|
14685
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
14686
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
14687
|
+
},
|
|
14688
|
+
(table) => [
|
|
14689
|
+
unique("code_projects_entity_path_unique").on(table.entityId, table.projectPath),
|
|
14690
|
+
index("idx_code_projects_entity").on(table.entityId)
|
|
14691
|
+
]
|
|
14692
|
+
);
|
|
14693
|
+
}
|
|
14694
|
+
});
|
|
14695
|
+
|
|
14624
14696
|
// ../../packages/db/src/schema/auth.ts
|
|
14625
14697
|
var sessions, accounts, verifications;
|
|
14626
14698
|
var init_auth = __esm({
|
|
@@ -14718,6 +14790,28 @@ var init_agent_workspaces = __esm({
|
|
|
14718
14790
|
path: text("path").notNull(),
|
|
14719
14791
|
// UI display order; lower = appears first. Default 0.
|
|
14720
14792
|
position: integer("position").default(0).notNull(),
|
|
14793
|
+
/**
|
|
14794
|
+
* Ticked by the owner: "nothing written here is a project of mine."
|
|
14795
|
+
*
|
|
14796
|
+
* NOT the return of `is_dev_folder` (0085, dropped by 0086). That one asked
|
|
14797
|
+
* which folders to INCLUDE, so nothing showed until you ticked, a folder
|
|
14798
|
+
* you forgot lost real work in silence, and you still had to guess whether
|
|
14799
|
+
* the ticked folder WAS a project or CONTAINED projects.
|
|
14800
|
+
*
|
|
14801
|
+
* This one only ever removes, and it removes a whole subtree — so the
|
|
14802
|
+
* "is it a project?" question never arises. Everything shows by default,
|
|
14803
|
+
* which keeps the honest failure mode: visible noise you clear, rather than
|
|
14804
|
+
* missing work nothing tells you about. See 0087.
|
|
14805
|
+
*
|
|
14806
|
+
* The folder stays a full workspace: the agent reads and writes there as
|
|
14807
|
+
* before, and its LABEL is still used to resolve relative paths. Only the
|
|
14808
|
+
* project view ignores it — the Code tab and the injected `## Runtime`
|
|
14809
|
+
* block alike.
|
|
14810
|
+
*
|
|
14811
|
+
* A PATH counts as hidden when at least one of its rows is ticked: the same
|
|
14812
|
+
* folder attached to five agents is one gesture, not five.
|
|
14813
|
+
*/
|
|
14814
|
+
hiddenFromCode: boolean("hidden_from_code").default(false).notNull(),
|
|
14721
14815
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
|
14722
14816
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
|
|
14723
14817
|
},
|
|
@@ -15158,6 +15252,7 @@ __export(schema_exports, {
|
|
|
15158
15252
|
chatMessages: () => chatMessages,
|
|
15159
15253
|
cliRuns: () => cliRuns,
|
|
15160
15254
|
cliSessions: () => cliSessions,
|
|
15255
|
+
codeProjects: () => codeProjects,
|
|
15161
15256
|
connectors: () => connectors,
|
|
15162
15257
|
conversations: () => conversations,
|
|
15163
15258
|
credentials: () => credentials,
|
|
@@ -15198,6 +15293,7 @@ var init_schema2 = __esm({
|
|
|
15198
15293
|
init_schedules();
|
|
15199
15294
|
init_llm_keys();
|
|
15200
15295
|
init_mcp();
|
|
15296
|
+
init_code_projects();
|
|
15201
15297
|
init_auth();
|
|
15202
15298
|
init_agent_connector_assignments();
|
|
15203
15299
|
init_agent_workspaces();
|
|
@@ -15539,6 +15635,17 @@ async function assertMasterKeyRestorable(db, options = {}) {
|
|
|
15539
15635
|
return Object.values(env).some((v) => typeof v === "string" && isEncrypted(v));
|
|
15540
15636
|
});
|
|
15541
15637
|
if (hasEncryptedMcp) throw missingKeyError("a stored MCP server secret");
|
|
15638
|
+
const bindingRows = await db.select({ credentials: channelBindings.credentials }).from(channelBindings);
|
|
15639
|
+
const hasEncryptedBinding = bindingRows.some(
|
|
15640
|
+
(r) => !!r.credentials && isEncrypted(r.credentials)
|
|
15641
|
+
);
|
|
15642
|
+
if (hasEncryptedBinding) throw missingKeyError("a stored channel credential");
|
|
15643
|
+
const agentRows = await db.select({ botToken: agents.telegramBotToken }).from(agents);
|
|
15644
|
+
const hasEncryptedBotToken = agentRows.some((r) => !!r.botToken && isEncrypted(r.botToken));
|
|
15645
|
+
if (hasEncryptedBotToken) throw missingKeyError("a stored channel credential (bot token)");
|
|
15646
|
+
const webhookRows = await db.select({ secret: webhookTriggers.secret }).from(webhookTriggers);
|
|
15647
|
+
const hasEncryptedWebhookSecret = webhookRows.some((r) => !!r.secret && isEncrypted(r.secret));
|
|
15648
|
+
if (hasEncryptedWebhookSecret) throw missingKeyError("a stored webhook secret");
|
|
15542
15649
|
}
|
|
15543
15650
|
function missingKeyError(what) {
|
|
15544
15651
|
return new Error(
|
|
@@ -15552,11 +15659,15 @@ var init_master_key_guard = __esm({
|
|
|
15552
15659
|
init_llm_keys();
|
|
15553
15660
|
init_connectors();
|
|
15554
15661
|
init_mcp();
|
|
15662
|
+
init_channel_bindings();
|
|
15663
|
+
init_agents();
|
|
15664
|
+
init_webhooks();
|
|
15555
15665
|
init_src3();
|
|
15556
15666
|
}
|
|
15557
15667
|
});
|
|
15558
15668
|
|
|
15559
15669
|
// ../../packages/db/src/queries/channel-identity.ts
|
|
15670
|
+
import { createHash } from "crypto";
|
|
15560
15671
|
async function resolveOwnerConversation(db, agentId, channel) {
|
|
15561
15672
|
if (channel === "telegram") {
|
|
15562
15673
|
const [row2] = await db.select({ chatId: telegramAllowedChats.chatId }).from(telegramAllowedChats).where(
|
|
@@ -15612,15 +15723,44 @@ async function getChannelBinding(db, agentId, channel) {
|
|
|
15612
15723
|
async function listChannelBindings(db, agentId) {
|
|
15613
15724
|
return db.select().from(channelBindings).where(eq(channelBindings.agentId, agentId));
|
|
15614
15725
|
}
|
|
15726
|
+
function decryptChannelSecret(raw, what) {
|
|
15727
|
+
if (!isEncrypted(raw)) return raw;
|
|
15728
|
+
try {
|
|
15729
|
+
return decrypt(raw);
|
|
15730
|
+
} catch (err) {
|
|
15731
|
+
throw new Error(
|
|
15732
|
+
`failed to decrypt ${what}: ${err instanceof Error ? err.message : String(err)}. The master key at ~/.nodalai/secrets.key does not match the one this row was written with (restored backup? copied database?). Re-enter the credential from the dashboard to re-encrypt it under the current key.`
|
|
15733
|
+
);
|
|
15734
|
+
}
|
|
15735
|
+
}
|
|
15736
|
+
function encryptChannelCredentials(creds) {
|
|
15737
|
+
return encrypt(JSON.stringify(creds));
|
|
15738
|
+
}
|
|
15739
|
+
function encryptChannelSecret(plaintext) {
|
|
15740
|
+
return isEncrypted(plaintext) ? plaintext : encrypt(plaintext);
|
|
15741
|
+
}
|
|
15742
|
+
function credentialsFingerprint(creds) {
|
|
15743
|
+
const canonical = JSON.stringify(
|
|
15744
|
+
Object.keys(creds).sort().map((k) => [k, creds[k]])
|
|
15745
|
+
);
|
|
15746
|
+
return createHash("sha256").update(canonical).digest("hex");
|
|
15747
|
+
}
|
|
15615
15748
|
async function getBindingCredentials(db, agentId, channel) {
|
|
15616
15749
|
if (channel === "telegram") {
|
|
15617
15750
|
const [row] = await db.select({ botToken: agents.telegramBotToken }).from(agents).where(eq(agents.id, agentId)).limit(1);
|
|
15618
|
-
|
|
15751
|
+
if (!row?.botToken) return null;
|
|
15752
|
+
return {
|
|
15753
|
+
botToken: decryptChannelSecret(row.botToken, `telegram bot token (agent ${agentId})`)
|
|
15754
|
+
};
|
|
15619
15755
|
}
|
|
15620
15756
|
const binding = await getChannelBinding(db, agentId, channel);
|
|
15621
15757
|
if (!binding) return null;
|
|
15758
|
+
const json2 = decryptChannelSecret(
|
|
15759
|
+
binding.credentials,
|
|
15760
|
+
`${channel} credentials (agent ${agentId})`
|
|
15761
|
+
);
|
|
15622
15762
|
try {
|
|
15623
|
-
const parsed = JSON.parse(
|
|
15763
|
+
const parsed = JSON.parse(json2);
|
|
15624
15764
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
15625
15765
|
return parsed;
|
|
15626
15766
|
} catch {
|
|
@@ -15673,6 +15813,7 @@ var init_channel_identity = __esm({
|
|
|
15673
15813
|
"../../packages/db/src/queries/channel-identity.ts"() {
|
|
15674
15814
|
"use strict";
|
|
15675
15815
|
init_drizzle_orm();
|
|
15816
|
+
init_src3();
|
|
15676
15817
|
init_telegram_allowed_chats();
|
|
15677
15818
|
init_channel_bindings();
|
|
15678
15819
|
init_channel_allowed_conversations();
|
|
@@ -16140,6 +16281,7 @@ __export(src_exports, {
|
|
|
16140
16281
|
chatMessages: () => chatMessages,
|
|
16141
16282
|
cliRuns: () => cliRuns,
|
|
16142
16283
|
cliSessions: () => cliSessions,
|
|
16284
|
+
codeProjects: () => codeProjects,
|
|
16143
16285
|
connectors: () => connectors,
|
|
16144
16286
|
conversations: () => conversations,
|
|
16145
16287
|
count: () => count,
|
|
@@ -16148,8 +16290,12 @@ __export(src_exports, {
|
|
|
16148
16290
|
createClient: () => createClient,
|
|
16149
16291
|
createSkillRepo: () => createSkillRepo,
|
|
16150
16292
|
credentials: () => credentials,
|
|
16293
|
+
credentialsFingerprint: () => credentialsFingerprint,
|
|
16294
|
+
decryptChannelSecret: () => decryptChannelSecret,
|
|
16151
16295
|
decryptCredentialForDisplay: () => decryptCredentialForDisplay,
|
|
16152
16296
|
desc: () => desc,
|
|
16297
|
+
encryptChannelCredentials: () => encryptChannelCredentials,
|
|
16298
|
+
encryptChannelSecret: () => encryptChannelSecret,
|
|
16153
16299
|
entities: () => entities,
|
|
16154
16300
|
entityLlmKeys: () => entityLlmKeys,
|
|
16155
16301
|
entityMembers: () => entityMembers,
|
|
@@ -16283,7 +16429,7 @@ var init_local_auth = __esm({
|
|
|
16283
16429
|
});
|
|
16284
16430
|
|
|
16285
16431
|
// ../../packages/auth/src/lib/constant-time.ts
|
|
16286
|
-
import { createHash, timingSafeEqual } from "crypto";
|
|
16432
|
+
import { createHash as createHash2, timingSafeEqual } from "crypto";
|
|
16287
16433
|
var init_constant_time = __esm({
|
|
16288
16434
|
"../../packages/auth/src/lib/constant-time.ts"() {
|
|
16289
16435
|
"use strict";
|
|
@@ -16502,9 +16648,34 @@ var init_ports = __esm({
|
|
|
16502
16648
|
}
|
|
16503
16649
|
});
|
|
16504
16650
|
|
|
16651
|
+
// src/lib/log-rotation.ts
|
|
16652
|
+
import { existsSync as existsSync6, renameSync, rmSync, statSync } from "fs";
|
|
16653
|
+
function rotateLogIfNeeded(logFile, maxBytes = LOG_ROTATE_MAX_BYTES) {
|
|
16654
|
+
try {
|
|
16655
|
+
if (!existsSync6(logFile)) return false;
|
|
16656
|
+
if (statSync(logFile).size < maxBytes) return false;
|
|
16657
|
+
const archived = `${logFile}.1`;
|
|
16658
|
+
rmSync(archived, { force: true });
|
|
16659
|
+
renameSync(logFile, archived);
|
|
16660
|
+
return true;
|
|
16661
|
+
} catch (err) {
|
|
16662
|
+
console.warn(
|
|
16663
|
+
`[nodalai] log rotation skipped for ${logFile}: ${err instanceof Error ? err.message : String(err)}`
|
|
16664
|
+
);
|
|
16665
|
+
return false;
|
|
16666
|
+
}
|
|
16667
|
+
}
|
|
16668
|
+
var LOG_ROTATE_MAX_BYTES;
|
|
16669
|
+
var init_log_rotation = __esm({
|
|
16670
|
+
"src/lib/log-rotation.ts"() {
|
|
16671
|
+
"use strict";
|
|
16672
|
+
LOG_ROTATE_MAX_BYTES = 20 * 1024 * 1024;
|
|
16673
|
+
}
|
|
16674
|
+
});
|
|
16675
|
+
|
|
16505
16676
|
// src/lib/processes.ts
|
|
16506
16677
|
import { execa as execa2 } from "execa";
|
|
16507
|
-
import { createWriteStream, openSync, writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as
|
|
16678
|
+
import { createWriteStream, openSync, writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
|
|
16508
16679
|
import { join as join6, dirname as dirname4, resolve } from "path";
|
|
16509
16680
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
16510
16681
|
async function killProcessTree(child) {
|
|
@@ -16653,7 +16824,7 @@ function resolveAppDir(appName) {
|
|
|
16653
16824
|
const fileDir = dirname4(here);
|
|
16654
16825
|
let cursor = fileDir;
|
|
16655
16826
|
for (let i = 0; i < 8; i++) {
|
|
16656
|
-
if (
|
|
16827
|
+
if (existsSync7(join6(cursor, "pnpm-workspace.yaml"))) {
|
|
16657
16828
|
return join6(cursor, appName);
|
|
16658
16829
|
}
|
|
16659
16830
|
const parent = dirname4(cursor);
|
|
@@ -16672,6 +16843,7 @@ function spawnWiring(logFile, detach) {
|
|
|
16672
16843
|
}
|
|
16673
16844
|
function spawnRunner(env, opts = {}) {
|
|
16674
16845
|
const logFile = join6(LOG_DIR, "runner.log");
|
|
16846
|
+
rotateLogIfNeeded(logFile);
|
|
16675
16847
|
const outStream = createWriteStream(logFile, { flags: "a" });
|
|
16676
16848
|
let bin;
|
|
16677
16849
|
let args;
|
|
@@ -16717,6 +16889,7 @@ env keys: ${Object.keys(env).join(",")}
|
|
|
16717
16889
|
}
|
|
16718
16890
|
function spawnWeb(env, opts = {}) {
|
|
16719
16891
|
const logFile = join6(LOG_DIR, "web.log");
|
|
16892
|
+
rotateLogIfNeeded(logFile);
|
|
16720
16893
|
const outStream = createWriteStream(logFile, { flags: "a" });
|
|
16721
16894
|
let bin;
|
|
16722
16895
|
let args;
|
|
@@ -16804,7 +16977,7 @@ function writePids(pids) {
|
|
|
16804
16977
|
}
|
|
16805
16978
|
function readPids() {
|
|
16806
16979
|
const pidFile = join6(PID_DIR, "processes.json");
|
|
16807
|
-
if (!
|
|
16980
|
+
if (!existsSync7(pidFile)) return null;
|
|
16808
16981
|
try {
|
|
16809
16982
|
return JSON.parse(readFileSync5(pidFile, "utf-8"));
|
|
16810
16983
|
} catch {
|
|
@@ -16813,7 +16986,7 @@ function readPids() {
|
|
|
16813
16986
|
}
|
|
16814
16987
|
function clearPids() {
|
|
16815
16988
|
const pidFile = join6(PID_DIR, "processes.json");
|
|
16816
|
-
if (
|
|
16989
|
+
if (existsSync7(pidFile)) {
|
|
16817
16990
|
writeFileSync4(pidFile, JSON.stringify({}), "utf-8");
|
|
16818
16991
|
}
|
|
16819
16992
|
}
|
|
@@ -16859,6 +17032,7 @@ var init_processes = __esm({
|
|
|
16859
17032
|
"src/lib/processes.ts"() {
|
|
16860
17033
|
"use strict";
|
|
16861
17034
|
init_config();
|
|
17035
|
+
init_log_rotation();
|
|
16862
17036
|
SNAPSHOT_BUDGET_MS = 2e4;
|
|
16863
17037
|
SHUTDOWN_SNAPSHOT_BUDGET_MS = 6e3;
|
|
16864
17038
|
inFlightSnapshot = null;
|
|
@@ -16890,11 +17064,76 @@ var init_processes = __esm({
|
|
|
16890
17064
|
lastSnapshotFailure = null;
|
|
16891
17065
|
BUNDLED_ROOT = (() => {
|
|
16892
17066
|
const here = dirname4(fileURLToPath3(import.meta.url));
|
|
16893
|
-
return
|
|
17067
|
+
return existsSync7(join6(here, "runner.js")) ? here : null;
|
|
16894
17068
|
})();
|
|
16895
17069
|
}
|
|
16896
17070
|
});
|
|
16897
17071
|
|
|
17072
|
+
// src/lib/watchdog.ts
|
|
17073
|
+
async function probeRunnerHealth(runnerUrl) {
|
|
17074
|
+
try {
|
|
17075
|
+
const controller = new AbortController();
|
|
17076
|
+
const t = setTimeout(() => controller.abort(), 3e3);
|
|
17077
|
+
const res = await fetch(`${runnerUrl}/api/health`, { signal: controller.signal });
|
|
17078
|
+
clearTimeout(t);
|
|
17079
|
+
if (res.status === 200) return { state: "healthy", detail: "" };
|
|
17080
|
+
const body = await res.json().catch(() => null);
|
|
17081
|
+
const detail = body?.db === "error" ? "database unreachable (runner reports db: error)" : `runner is unhealthy (HTTP ${res.status})`;
|
|
17082
|
+
return { state: "degraded", detail };
|
|
17083
|
+
} catch (err) {
|
|
17084
|
+
const aborted = err instanceof Error && err.name === "AbortError";
|
|
17085
|
+
return {
|
|
17086
|
+
state: "unreachable",
|
|
17087
|
+
detail: aborted ? "runner did not answer /api/health within 3s (busy or stalled)" : "runner is not answering /api/health"
|
|
17088
|
+
};
|
|
17089
|
+
}
|
|
17090
|
+
}
|
|
17091
|
+
function startHealthWatchdog(opts) {
|
|
17092
|
+
const intervalMs = opts.intervalMs ?? 3e4;
|
|
17093
|
+
const failureThreshold = opts.failureThreshold ?? 2;
|
|
17094
|
+
let announced = "healthy";
|
|
17095
|
+
let consecutiveFailures = 0;
|
|
17096
|
+
let stopped = false;
|
|
17097
|
+
let inFlight = false;
|
|
17098
|
+
const tick = async () => {
|
|
17099
|
+
if (stopped || inFlight) return;
|
|
17100
|
+
inFlight = true;
|
|
17101
|
+
try {
|
|
17102
|
+
const result = await opts.probe();
|
|
17103
|
+
if (stopped) return;
|
|
17104
|
+
if (result.state === "healthy") {
|
|
17105
|
+
consecutiveFailures = 0;
|
|
17106
|
+
if (announced !== "healthy") {
|
|
17107
|
+
announced = "healthy";
|
|
17108
|
+
opts.onTransition("healthy", "services are answering again");
|
|
17109
|
+
}
|
|
17110
|
+
return;
|
|
17111
|
+
}
|
|
17112
|
+
consecutiveFailures += 1;
|
|
17113
|
+
if (consecutiveFailures < failureThreshold) return;
|
|
17114
|
+
if (announced !== result.state) {
|
|
17115
|
+
announced = result.state;
|
|
17116
|
+
opts.onTransition(result.state, result.detail);
|
|
17117
|
+
}
|
|
17118
|
+
} finally {
|
|
17119
|
+
inFlight = false;
|
|
17120
|
+
}
|
|
17121
|
+
};
|
|
17122
|
+
const timer2 = setInterval(() => void tick(), intervalMs);
|
|
17123
|
+
timer2.unref?.();
|
|
17124
|
+
return {
|
|
17125
|
+
stop: () => {
|
|
17126
|
+
stopped = true;
|
|
17127
|
+
clearInterval(timer2);
|
|
17128
|
+
}
|
|
17129
|
+
};
|
|
17130
|
+
}
|
|
17131
|
+
var init_watchdog = __esm({
|
|
17132
|
+
"src/lib/watchdog.ts"() {
|
|
17133
|
+
"use strict";
|
|
17134
|
+
}
|
|
17135
|
+
});
|
|
17136
|
+
|
|
16898
17137
|
// src/commands/up.ts
|
|
16899
17138
|
var up_exports = {};
|
|
16900
17139
|
__export(up_exports, {
|
|
@@ -16904,7 +17143,7 @@ import chalk from "chalk";
|
|
|
16904
17143
|
import ora from "ora";
|
|
16905
17144
|
import open from "open";
|
|
16906
17145
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
16907
|
-
import { existsSync as
|
|
17146
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
16908
17147
|
import { join as join7 } from "path";
|
|
16909
17148
|
async function killSilent(child) {
|
|
16910
17149
|
await killProcessTree(child);
|
|
@@ -16912,7 +17151,7 @@ async function killSilent(child) {
|
|
|
16912
17151
|
function printLogTail(service, lines) {
|
|
16913
17152
|
const logFile = join7(LOG_DIR, `${service}.log`);
|
|
16914
17153
|
try {
|
|
16915
|
-
if (!
|
|
17154
|
+
if (!existsSync8(logFile)) {
|
|
16916
17155
|
console.error(chalk.gray(`
|
|
16917
17156
|
${service}: no log at ${logFile}`));
|
|
16918
17157
|
return;
|
|
@@ -17274,7 +17513,32 @@ ${lines}
|
|
|
17274
17513
|
}
|
|
17275
17514
|
console.log(chalk.gray(" Ctrl+C to stop all services"));
|
|
17276
17515
|
console.log("");
|
|
17516
|
+
const watchdog = startHealthWatchdog({
|
|
17517
|
+
probe: () => probeRunnerHealth(runnerUrl),
|
|
17518
|
+
onTransition: (state, detail) => {
|
|
17519
|
+
const at = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
17520
|
+
if (state === "healthy") {
|
|
17521
|
+
console.log(chalk.green(`
|
|
17522
|
+
[${at}] Recovered \u2014 ${detail}.`));
|
|
17523
|
+
return;
|
|
17524
|
+
}
|
|
17525
|
+
console.error(chalk.red.bold(`
|
|
17526
|
+
[${at}] DEGRADED \u2014 ${detail}.`));
|
|
17527
|
+
if (state === "degraded") {
|
|
17528
|
+
console.error(
|
|
17529
|
+
chalk.red(
|
|
17530
|
+
" The dashboard may still render, but nothing can read or write the database:\n jobs are not created, chats and schedules are failing."
|
|
17531
|
+
)
|
|
17532
|
+
);
|
|
17533
|
+
printLogTail("runner", 10);
|
|
17534
|
+
}
|
|
17535
|
+
console.error(
|
|
17536
|
+
chalk.yellow(" \u2192 Restart the stack: `nodal-agents down`, then `nodal-agents up`.")
|
|
17537
|
+
);
|
|
17538
|
+
}
|
|
17539
|
+
});
|
|
17277
17540
|
await Promise.race([runnerProcess, webProcess]);
|
|
17541
|
+
watchdog.stop();
|
|
17278
17542
|
await shutdown();
|
|
17279
17543
|
}
|
|
17280
17544
|
var init_up = __esm({
|
|
@@ -17287,6 +17551,7 @@ var init_up = __esm({
|
|
|
17287
17551
|
init_ports();
|
|
17288
17552
|
init_processes();
|
|
17289
17553
|
init_src4();
|
|
17554
|
+
init_watchdog();
|
|
17290
17555
|
}
|
|
17291
17556
|
});
|
|
17292
17557
|
|
|
@@ -17574,7 +17839,7 @@ __export(down_exports, {
|
|
|
17574
17839
|
runDown: () => runDown
|
|
17575
17840
|
});
|
|
17576
17841
|
import chalk3 from "chalk";
|
|
17577
|
-
import { existsSync as
|
|
17842
|
+
import { existsSync as existsSync9 } from "fs";
|
|
17578
17843
|
import { join as join8 } from "path";
|
|
17579
17844
|
async function killPid(pid, label) {
|
|
17580
17845
|
if (!isPidAlive(pid)) {
|
|
@@ -17591,7 +17856,7 @@ async function killPid(pid, label) {
|
|
|
17591
17856
|
}
|
|
17592
17857
|
async function stopPostgresGracefully() {
|
|
17593
17858
|
const pidFile = join8(PG_DATA_DIR, "postmaster.pid");
|
|
17594
|
-
if (!
|
|
17859
|
+
if (!existsSync9(pidFile)) return false;
|
|
17595
17860
|
const pgPid = readPostmasterPid(PG_DATA_DIR);
|
|
17596
17861
|
try {
|
|
17597
17862
|
const EmbeddedPostgres = (await import("embedded-postgres")).default;
|
|
@@ -17661,7 +17926,7 @@ __export(logs_exports, {
|
|
|
17661
17926
|
runLogs: () => runLogs
|
|
17662
17927
|
});
|
|
17663
17928
|
import chalk4 from "chalk";
|
|
17664
|
-
import { existsSync as
|
|
17929
|
+
import { existsSync as existsSync10, createReadStream, watch, statSync as statSync2 } from "fs";
|
|
17665
17930
|
import { join as join9 } from "path";
|
|
17666
17931
|
function isServiceName(s) {
|
|
17667
17932
|
return VALID_SERVICES.includes(s);
|
|
@@ -17675,7 +17940,7 @@ async function runLogs(service) {
|
|
|
17675
17940
|
const services = target === "all" ? VALID_SERVICES : [target];
|
|
17676
17941
|
for (const svc of services) {
|
|
17677
17942
|
const logFile = join9(LOG_DIR, `${svc}.log`);
|
|
17678
|
-
if (!
|
|
17943
|
+
if (!existsSync10(logFile)) {
|
|
17679
17944
|
console.log(chalk4.gray(` [${svc}] No log file yet at ${logFile}`));
|
|
17680
17945
|
continue;
|
|
17681
17946
|
}
|
|
@@ -17690,18 +17955,18 @@ async function runLogs(service) {
|
|
|
17690
17955
|
}
|
|
17691
17956
|
if (target !== "all") {
|
|
17692
17957
|
const logFile = join9(LOG_DIR, `${target}.log`);
|
|
17693
|
-
if (!
|
|
17958
|
+
if (!existsSync10(logFile)) {
|
|
17694
17959
|
console.log(chalk4.yellow("Waiting for log file to be created\u2026"));
|
|
17695
17960
|
}
|
|
17696
17961
|
console.log(chalk4.gray("\nFollowing log (Ctrl+C to stop)...\n"));
|
|
17697
17962
|
let fileSize = 0;
|
|
17698
17963
|
try {
|
|
17699
|
-
fileSize =
|
|
17964
|
+
fileSize = statSync2(logFile).size;
|
|
17700
17965
|
} catch {
|
|
17701
17966
|
}
|
|
17702
17967
|
const watcher = watch(logFile, () => {
|
|
17703
17968
|
try {
|
|
17704
|
-
const newSize =
|
|
17969
|
+
const newSize = statSync2(logFile).size;
|
|
17705
17970
|
if (newSize > fileSize) {
|
|
17706
17971
|
const stream = createReadStream(logFile, { start: fileSize, encoding: "utf-8" });
|
|
17707
17972
|
stream.on("data", (chunk) => process.stdout.write(chunk));
|
|
@@ -17726,9 +17991,9 @@ var init_logs = __esm({
|
|
|
17726
17991
|
|
|
17727
17992
|
// ../../packages/checkpoints/src/checkpoints.ts
|
|
17728
17993
|
import { execFile } from "child_process";
|
|
17729
|
-
import { createHash as
|
|
17994
|
+
import { createHash as createHash3 } from "crypto";
|
|
17730
17995
|
import { mkdir, writeFile } from "fs/promises";
|
|
17731
|
-
import { existsSync as
|
|
17996
|
+
import { existsSync as existsSync11 } from "fs";
|
|
17732
17997
|
import { join as join10 } from "path";
|
|
17733
17998
|
import { promisify } from "util";
|
|
17734
17999
|
function workspaceKey(workspace) {
|
|
@@ -17737,7 +18002,7 @@ function workspaceKey(workspace) {
|
|
|
17737
18002
|
norm = norm.toLowerCase().replace(/\//g, "\\");
|
|
17738
18003
|
}
|
|
17739
18004
|
norm = norm.replace(/[\\/]+$/, "");
|
|
17740
|
-
return
|
|
18005
|
+
return createHash3("sha256").update(norm).digest("hex").slice(0, 16);
|
|
17741
18006
|
}
|
|
17742
18007
|
async function git(store, workspace, args) {
|
|
17743
18008
|
const key = workspaceKey(workspace);
|
|
@@ -17768,7 +18033,7 @@ async function ensureStore(store) {
|
|
|
17768
18033
|
const gitDir = join10(store, "store");
|
|
17769
18034
|
await mkdir(join10(store, "indexes"), { recursive: true });
|
|
17770
18035
|
await writeFile(join10(store, "gitconfig"), "", { flag: "a" });
|
|
17771
|
-
if (!
|
|
18036
|
+
if (!existsSync11(join10(gitDir, "HEAD"))) {
|
|
17772
18037
|
await mkdir(gitDir, { recursive: true });
|
|
17773
18038
|
await run("git", ["init", "--bare", "--quiet", gitDir], {
|
|
17774
18039
|
timeout: GIT_TIMEOUT_MS,
|
|
@@ -17801,7 +18066,7 @@ async function snapshot(store, workspace, label) {
|
|
|
17801
18066
|
return { sha, workspace, at, label };
|
|
17802
18067
|
}
|
|
17803
18068
|
async function listCheckpoints(store, workspace, limit = 20) {
|
|
17804
|
-
if (!
|
|
18069
|
+
if (!existsSync11(join10(store, "store", "HEAD"))) return [];
|
|
17805
18070
|
const ref = `refs/nodal/${workspaceKey(workspace)}`;
|
|
17806
18071
|
const out = await git(store, workspace, [
|
|
17807
18072
|
"log",
|
|
@@ -30975,7 +31240,13 @@ async function buildNodalMcpServer(opts) {
|
|
|
30975
31240
|
}
|
|
30976
31241
|
targetAgent = root;
|
|
30977
31242
|
} else {
|
|
30978
|
-
const [launcher] = await opts.db.select({ id: agents.id }).from(agents).where(
|
|
31243
|
+
const [launcher] = await opts.db.select({ id: agents.id }).from(agents).where(
|
|
31244
|
+
and(
|
|
31245
|
+
eq(agents.id, agentRow.id),
|
|
31246
|
+
eq(agents.active, true),
|
|
31247
|
+
eq(agents.entityId, entityId)
|
|
31248
|
+
)
|
|
31249
|
+
).limit(1);
|
|
30979
31250
|
if (!launcher) {
|
|
30980
31251
|
throw new Error(
|
|
30981
31252
|
"mcp_agent_not_found: the agent this server was launched for has been deactivated. Reactivate it, set a root agent, or restart the server for another agent."
|
|
@@ -31185,7 +31456,7 @@ __export(reset_exports, {
|
|
|
31185
31456
|
});
|
|
31186
31457
|
import chalk6 from "chalk";
|
|
31187
31458
|
import prompts2 from "prompts";
|
|
31188
|
-
import { rmSync, existsSync as
|
|
31459
|
+
import { rmSync as rmSync2, existsSync as existsSync12 } from "fs";
|
|
31189
31460
|
async function runReset(options = {}) {
|
|
31190
31461
|
if (!options.yes) {
|
|
31191
31462
|
const { confirm } = await prompts2({
|
|
@@ -31203,8 +31474,8 @@ async function runReset(options = {}) {
|
|
|
31203
31474
|
}
|
|
31204
31475
|
console.log(chalk6.yellow("Stopping any running processes\u2026"));
|
|
31205
31476
|
await runDown();
|
|
31206
|
-
if (
|
|
31207
|
-
|
|
31477
|
+
if (existsSync12(CONFIG_DIR)) {
|
|
31478
|
+
rmSync2(CONFIG_DIR, { recursive: true, force: true });
|
|
31208
31479
|
console.log(chalk6.green(` Removed ${CONFIG_DIR}`));
|
|
31209
31480
|
} else {
|
|
31210
31481
|
console.log(chalk6.gray(` ${CONFIG_DIR} was already empty`));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
-- 0082 — inversion du modele a deux cles Yolo (decision Quentin 24/08).
|
|
2
|
+
--
|
|
3
|
+
-- L'ancien lan_command_yolo etait une PRE-CONDITION : sur une install reseau,
|
|
4
|
+
-- il fallait l'allumer pour avoir LE DROIT d'activer le Yolo par agent — deux
|
|
5
|
+
-- serrures, une seule cle (les deux gestes etaient deja owner-only). Sa vraie
|
|
6
|
+
-- valeur etait le coupe-circuit : l'eteindre rendait dormantes toutes les
|
|
7
|
+
-- regles d'un coup. auto_run_paused ne garde QUE ce role : un frein
|
|
8
|
+
-- d'urgence, inactif par defaut, qui met en pause tout l'auto-run de code du
|
|
9
|
+
-- workspace quand on l'enclenche.
|
|
10
|
+
--
|
|
11
|
+
-- Le backfill preserve l'INTENTION de chaque install : un master OFF alors
|
|
12
|
+
-- que des regles auto_approve d'outils d'execution existent = le proprietaire
|
|
13
|
+
-- avait deliberement coupe (usage coupe-circuit) -> pause maintenue. Un
|
|
14
|
+
-- master OFF sans aucune regle armee (l'immense majorite, dont toute install
|
|
15
|
+
-- neuve) = rien a couper -> pas de pause. Un master ON = il autorisait ->
|
|
16
|
+
-- pas de pause. Aucun comportement effectif ne change au reboot.
|
|
17
|
+
ALTER TABLE entities ADD COLUMN auto_run_paused boolean NOT NULL DEFAULT false;
|
|
18
|
+
|
|
19
|
+
UPDATE entities e
|
|
20
|
+
SET auto_run_paused = true
|
|
21
|
+
WHERE e.lan_command_yolo = false
|
|
22
|
+
AND EXISTS (
|
|
23
|
+
SELECT 1
|
|
24
|
+
FROM approval_rules ar
|
|
25
|
+
LEFT JOIN agents a ON a.id = ar.agent_id
|
|
26
|
+
WHERE ar.action = 'auto_approve'
|
|
27
|
+
AND ar.tool_name IN ('run_command', 'code_task', 'run_skill_script',
|
|
28
|
+
'skill_file_write', 'create_mcp', 'attach_mcp')
|
|
29
|
+
AND (a.entity_id = e.id OR ar.entity_id = e.id)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
ALTER TABLE entities DROP COLUMN lan_command_yolo;
|