@runeya/runeya 2.0.140 → 2.0.141
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/agent/index.js.map +1 -1
- package/agent-manager-M4NA5YFD.js +11 -0
- package/{chat-L7RRHXX5.js → chat-UQIVCG33.js} +9 -7
- package/{chunk-G4TOLDZB.js → chunk-36CC5CZK.js} +123 -57
- package/chunk-36CC5CZK.js.map +1 -0
- package/{chunk-GOMF7NWS.js → chunk-7L6AQUBR.js} +2 -2
- package/{chunk-O2KOX4LU.js → chunk-C2BGNVBT.js} +35 -9
- package/chunk-C2BGNVBT.js.map +1 -0
- package/{chunk-SYCUQDA4.js → chunk-GXE5TLLJ.js} +10 -4
- package/chunk-GXE5TLLJ.js.map +1 -0
- package/{chunk-QWX6R6AW.js → chunk-H2MAALKN.js} +46 -8
- package/chunk-H2MAALKN.js.map +1 -0
- package/{chunk-EV3TBM7E.js → chunk-IXO5KIO5.js} +9 -2
- package/{chunk-EV3TBM7E.js.map → chunk-IXO5KIO5.js.map} +1 -1
- package/{chunk-6HSZPK2W.js → chunk-UR3HL373.js} +3 -3
- package/{companion-LQVLEHU7.js → companion-EVCS2HNC.js} +5 -5
- package/{desktop-installer-page-OKE4WY5Q.js → desktop-installer-page-6JDSXK2E.js} +226 -33
- package/desktop-installer-page-6JDSXK2E.js.map +1 -0
- package/{free-port-565NMFIZ.js → free-port-5UNHDWGE.js} +2 -2
- package/index.js +70 -22
- package/index.js.map +1 -1
- package/install-gate-SFHNMBWT.js +60 -0
- package/install-gate-SFHNMBWT.js.map +1 -0
- package/mcp-server/index.js +84 -10
- package/mcp-server/index.js.map +1 -1
- package/mcp-server/launch.d.ts +5 -4
- package/mcp-server/launch.js.map +1 -1
- package/package.json +1 -1
- package/settings-manager-32SVMAFN.js +10 -0
- package/{src-NVQHFMTQ.js → src-ZSQISY6W.js} +936 -807
- package/src-ZSQISY6W.js.map +1 -0
- package/{vault-resolver-B5P2OUI5.js → vault-resolver-VK6W5J6W.js} +2 -2
- package/web/assets/{AgentScalarChatInterface.vue-Dgobg9wk.js → AgentScalarChatInterface.vue-Df0_SDmK.js} +1 -1
- package/web/assets/{AgentTerminalPage-B43nSPSz.js → AgentTerminalPage-BGA_7YUb.js} +1 -1
- package/web/assets/{AiChatModals-CVZ5ilmt.js → AiChatModals-CaGj0i1I.js} +1 -1
- package/web/assets/{AppLayout-B5yKLUj9.js → AppLayout-CDOSGDLm.js} +1 -1
- package/web/assets/{AuditPage-KR5ydSRG.js → AuditPage-BMYSSho1.js} +1 -1
- package/web/assets/{CloudAccountPage-RNosmi99.js → CloudAccountPage-DJNbXhOH.js} +1 -1
- package/web/assets/{CloudMessagingPage-Dp-FDdoH.js → CloudMessagingPage-JXnnH4aT.js} +1 -1
- package/web/assets/{DashboardPage-Bjj11oP_.js → DashboardPage-BjeN6ZQ3.js} +1 -1
- package/web/assets/{DirectoryPickerDialog-C28UH35_.js → DirectoryPickerDialog-CNIrtkmh.js} +1 -1
- package/web/assets/{EnvironmentForm-Dj2T2DuS.js → EnvironmentForm-DTJsmGg_.js} +1 -1
- package/web/assets/{EnvironmentFormPage-B4Bt1bXS.js → EnvironmentFormPage-HNIgKqaJ.js} +1 -1
- package/web/assets/{EnvironmentListPage-B_V9mEhR.js → EnvironmentListPage-DRI3aFZw.js} +1 -1
- package/web/assets/{KanbanBoardPage-CQLiin5v.js → KanbanBoardPage-DocxhfeF.js} +3 -3
- package/web/assets/{KanbanListPage-ChD0Zj0Y.js → KanbanListPage-DR_DYt46.js} +1 -1
- package/web/assets/{LaunchProjectPage-ByIfq7ka.js → LaunchProjectPage-Dgtz9dX8.js} +1 -1
- package/web/assets/{LaunchServicesPage-BweD0CWu.js → LaunchServicesPage-iM8a_UHk.js} +1 -1
- package/web/assets/{LaunchShell-C9jV0DDh.js → LaunchShell-DccCGeLx.js} +1 -1
- package/web/assets/{LogViewer-Cv024Meq.js → LogViewer-DOcy8D-m.js} +2 -2
- package/web/assets/{OrgKeyPrompt-D95Zm3Tl.js → OrgKeyPrompt-Dx95YhKV.js} +1 -1
- package/web/assets/{ProcessControls-84xtt8ud.js → ProcessControls-BUMbz3Cc.js} +1 -1
- package/web/assets/{ProcessDetailPage-CqMk8XQv.js → ProcessDetailPage-DKVRY4sj.js} +1 -1
- package/web/assets/{ProjectCreateDialog-C4rLTHw0.js → ProjectCreateDialog-CdYIXATf.js} +1 -1
- package/web/assets/{ProjectLocationGate-BtI0T-GU.js → ProjectLocationGate-BpKnR1WI.js} +1 -1
- package/web/assets/{ProjectPublishDialog-v5xz8t4N.js → ProjectPublishDialog-BTZXEmZh.js} +1 -1
- package/web/assets/{RandomJobTestPage-CzT3LSMu.js → RandomJobTestPage-BLR9jVah.js} +1 -1
- package/web/assets/ScenarioEditorPage-ebK_glPw.js +14 -0
- package/web/assets/{SendToChatDialog-BtsCq9hv.js → SendToChatDialog-VwH8FOP7.js} +1 -1
- package/web/assets/{ServiceControlsBar-BFemdioZ.js → ServiceControlsBar-3dlgA6Sg.js} +1 -1
- package/web/assets/{ServiceDetailPage-DsGHPdUc.js → ServiceDetailPage-C9MWIf-1.js} +1 -1
- package/web/assets/{ServiceFormPage-x3jtyV1A.js → ServiceFormPage-DIJMr_Ij.js} +1 -1
- package/web/assets/{SettingsPage-3tVWDaUN.js → SettingsPage-4bd7gsHf.js} +6 -6
- package/web/assets/{UnlockPage-CPcll-ov.js → UnlockPage-F37odjgM.js} +1 -1
- package/web/assets/{VaultMigrationPage-BMS4BcsA.js → VaultMigrationPage-CT6EFAXK.js} +1 -1
- package/web/assets/VersionBadge-4MjBn5ih.js +1 -0
- package/web/assets/VersionBadge-CtFji3sv.css +1 -0
- package/web/assets/{WikiPage-CM1-hJvF.js → WikiPage-Dhil7fWU.js} +1 -1
- package/web/assets/{WikiView-MQLuO7ey.js → WikiView-Ca2kGbvZ.js} +1 -1
- package/web/assets/{add-scalar-classes-BDJ0Zhvc.js → add-scalar-classes-ny-bwITc.js} +2 -2
- package/web/assets/{agent.store-CEmOZ8XE.js → agent.store-BBUsxY3E.js} +1 -1
- package/web/assets/auth-DgcowXmP.js +2 -0
- package/web/assets/{cloud-chat.store-DhCEhagv.js → cloud-chat.store-BT0ikifC.js} +1 -1
- package/web/assets/{cloud-client-CmrptmtM.js → cloud-client-7eaj_DyS.js} +2 -2
- package/web/assets/cloud-session-BI06sY79.js +2 -0
- package/web/assets/{editor.store-Be5P5Eur.js → editor.store-uLBvEAgM.js} +1 -1
- package/web/assets/index-CZJJuyAp.css +1 -0
- package/web/assets/{index-CXnyET_X.js → index-c8l1Vzb5.js} +101 -101
- package/web/assets/{modal-9BC14oRF.js → modal-CNk5Pd2V.js} +1 -1
- package/web/assets/{project-launch.store-cO8Wr508.js → project-launch.store-C0TgNDYm.js} +1 -1
- package/web/assets/{project.store-CpUEwUgF.js → project.store-Dx530iqh.js} +1 -1
- package/web/assets/{scenario.store-B5uv1DSO.js → scenario.store-DWezewHe.js} +1 -1
- package/web/assets/{service.store-ve_P4_2O.js → service.store-CgzQ1QCL.js} +1 -1
- package/web/assets/{settings-scope-CcYu-GYo.js → settings-scope-54SF-1Hf.js} +1 -1
- package/web/assets/{settings.store-CdCOUAsL.js → settings.store-Cz6g0qH2.js} +1 -1
- package/web/assets/{src-RO7PD0gR.js → src-BbRuct-7.js} +1 -1
- package/web/assets/{src-T5uKA02c.js → src-gyxHZ5yB.js} +1 -1
- package/web/assets/{theme-CPaZfI9r.js → theme-BZCAxfJD.js} +1 -1
- package/web/assets/trpc-PGQoddea.js +3 -0
- package/web/assets/{useCloudOrg-DaFUgt9j.js → useCloudOrg-DePshxqA.js} +1 -1
- package/web/assets/{useEnvVarAutocomplete-CXlyG01r.js → useEnvVarAutocomplete-lyIwYncA.js} +1 -1
- package/web/assets/{useProjectCatalog-1vLcCQlh.js → useProjectCatalog-BFmo-l75.js} +1 -1
- package/web/assets/{useProjectCloudOrg-MAfohLuM.js → useProjectCloudOrg-DN7UK1So.js} +1 -1
- package/web/assets/{useServiceUrlHost-D9k9s21s.js → useServiceUrlHost-D31YSsBc.js} +1 -1
- package/web/assets/{vue-draggable-plus-SmOauPHy.js → vue-draggable-plus-DE0HToYu.js} +1 -1
- package/web/favicon.svg +1 -5
- package/web/index.html +17 -17
- package/agent-manager-RZI65MSV.js +0 -11
- package/chunk-G4TOLDZB.js.map +0 -1
- package/chunk-O2KOX4LU.js.map +0 -1
- package/chunk-QWX6R6AW.js.map +0 -1
- package/chunk-SYCUQDA4.js.map +0 -1
- package/desktop-installer-page-OKE4WY5Q.js.map +0 -1
- package/settings-manager-5JHSSKQ2.js +0 -10
- package/src-NVQHFMTQ.js.map +0 -1
- package/web/assets/ScenarioEditorPage-DrWcVOHg.js +0 -14
- package/web/assets/VersionBadge-54viPqlY.css +0 -1
- package/web/assets/VersionBadge-prnLNUGg.js +0 -1
- package/web/assets/auth-BpxI0N26.js +0 -2
- package/web/assets/cloud-session-ByODsQrA.js +0 -2
- package/web/assets/index-Dsh11_J0.css +0 -1
- package/web/assets/trpc-BFA-mSM8.js +0 -3
- /package/{agent-manager-RZI65MSV.js.map → agent-manager-M4NA5YFD.js.map} +0 -0
- /package/{chat-L7RRHXX5.js.map → chat-UQIVCG33.js.map} +0 -0
- /package/{chunk-GOMF7NWS.js.map → chunk-7L6AQUBR.js.map} +0 -0
- /package/{chunk-6HSZPK2W.js.map → chunk-UR3HL373.js.map} +0 -0
- /package/{companion-LQVLEHU7.js.map → companion-EVCS2HNC.js.map} +0 -0
- /package/{free-port-565NMFIZ.js.map → free-port-5UNHDWGE.js.map} +0 -0
- /package/{settings-manager-5JHSSKQ2.js.map → settings-manager-32SVMAFN.js.map} +0 -0
- /package/{vault-resolver-B5P2OUI5.js.map → vault-resolver-VK6W5J6W.js.map} +0 -0
|
@@ -62,10 +62,10 @@ import {
|
|
|
62
62
|
soleAccountScope,
|
|
63
63
|
startCliSession,
|
|
64
64
|
vaultOfProject
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-36CC5CZK.js";
|
|
66
66
|
import {
|
|
67
67
|
settingsManager
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-UR3HL373.js";
|
|
69
69
|
import {
|
|
70
70
|
LocalProjectRefusedError,
|
|
71
71
|
activeSessionRegistry,
|
|
@@ -87,7 +87,7 @@ import {
|
|
|
87
87
|
stopClaudeTranscriptWatcher,
|
|
88
88
|
stopCodexTranscriptWatcher,
|
|
89
89
|
verifyJwt
|
|
90
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-H2MAALKN.js";
|
|
91
91
|
import {
|
|
92
92
|
agentManager,
|
|
93
93
|
agentSpawner,
|
|
@@ -126,13 +126,13 @@ import {
|
|
|
126
126
|
stableEncryptValue,
|
|
127
127
|
traefikManager,
|
|
128
128
|
unregisterInstance
|
|
129
|
-
} from "./chunk-
|
|
129
|
+
} from "./chunk-GXE5TLLJ.js";
|
|
130
130
|
import {
|
|
131
131
|
atomicWriteSecure,
|
|
132
132
|
renameWithRetry,
|
|
133
133
|
resolveConversationRunDir,
|
|
134
134
|
sweepStaleTempFiles
|
|
135
|
-
} from "./chunk-
|
|
135
|
+
} from "./chunk-7L6AQUBR.js";
|
|
136
136
|
import "./chunk-MD3VW4HQ.js";
|
|
137
137
|
import {
|
|
138
138
|
AGENT_LONG_FETCH_TIMEOUT,
|
|
@@ -196,7 +196,7 @@ import {
|
|
|
196
196
|
rememberOrgName,
|
|
197
197
|
stampOrgCloud,
|
|
198
198
|
vaultFor
|
|
199
|
-
} from "./chunk-
|
|
199
|
+
} from "./chunk-IXO5KIO5.js";
|
|
200
200
|
import {
|
|
201
201
|
decryptValueWith,
|
|
202
202
|
encryptValueWith,
|
|
@@ -215,24 +215,74 @@ import { applyWSSHandler } from "@trpc/server/adapters/ws";
|
|
|
215
215
|
|
|
216
216
|
// ../server/src/trpc/routers/health.ts
|
|
217
217
|
import os from "os";
|
|
218
|
+
import { execFile as execFile2 } from "child_process";
|
|
219
|
+
import { promisify as promisify2 } from "util";
|
|
220
|
+
import { z } from "zod";
|
|
221
|
+
|
|
222
|
+
// ../server/src/services/npm-cli.ts
|
|
218
223
|
import { execFile } from "child_process";
|
|
219
224
|
import { promisify } from "util";
|
|
220
|
-
import {
|
|
225
|
+
import { existsSync, mkdtempSync, readFileSync, rmdirSync } from "fs";
|
|
226
|
+
import { dirname, isAbsolute, join, relative } from "path";
|
|
221
227
|
var execFileAsync = promisify(execFile);
|
|
222
|
-
|
|
228
|
+
var PACKAGE_NAME = "@runeya/runeya";
|
|
229
|
+
var INSTALL_TIMEOUT_MS = 10 * 6e4;
|
|
230
|
+
function resolveNpm() {
|
|
231
|
+
const nodeDir = dirname(process.execPath);
|
|
232
|
+
const candidates = [
|
|
233
|
+
// Windows (installateur officiel, nvm-windows, volta) : npm est un dossier
|
|
234
|
+
// frère de node.exe.
|
|
235
|
+
join(nodeDir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
236
|
+
// Unix : node est dans `bin/`, npm sous `lib/node_modules/`.
|
|
237
|
+
join(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
|
|
238
|
+
];
|
|
239
|
+
for (const cli2 of candidates) {
|
|
240
|
+
if (existsSync(cli2)) return { command: process.execPath, args: [cli2], shell: false };
|
|
241
|
+
}
|
|
242
|
+
if (process.platform === "win32") {
|
|
243
|
+
for (const entry of (process.env["PATH"] ?? "").split(";").filter(Boolean)) {
|
|
244
|
+
const cliCandidates = [
|
|
245
|
+
join(entry, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
246
|
+
join(entry, "..", "npm", "bin", "npm-cli.js")
|
|
247
|
+
];
|
|
248
|
+
for (const cli2 of cliCandidates) {
|
|
249
|
+
if (existsSync(cli2)) return { command: process.execPath, args: [cli2], shell: false };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
throw new Error("Impossible de trouver npm-cli.js dans les emplacements connus du PATH Windows.");
|
|
253
|
+
}
|
|
254
|
+
return { command: "npm", args: [], shell: false };
|
|
255
|
+
}
|
|
256
|
+
function npmLabel(npm) {
|
|
257
|
+
return [npm.command, ...npm.args].join(" ");
|
|
258
|
+
}
|
|
259
|
+
async function probeVersion(npm, args) {
|
|
223
260
|
try {
|
|
224
|
-
const { stdout } = await execFileAsync(
|
|
225
|
-
timeout:
|
|
226
|
-
windowsHide: true
|
|
261
|
+
const { stdout } = await execFileAsync(npm.command, [...npm.args, ...args], {
|
|
262
|
+
timeout: 1e4,
|
|
263
|
+
windowsHide: true,
|
|
264
|
+
shell: npm.shell
|
|
227
265
|
});
|
|
228
|
-
|
|
229
|
-
return match?.[1] ?? (stdout.trim() || null);
|
|
266
|
+
return stdout.trim().split("\n")[0]?.trim() || null;
|
|
230
267
|
} catch {
|
|
231
268
|
return null;
|
|
232
269
|
}
|
|
233
270
|
}
|
|
234
|
-
|
|
235
|
-
|
|
271
|
+
async function installRuneyaGlobally(npm, prefix) {
|
|
272
|
+
try {
|
|
273
|
+
const { stdout, stderr } = await execFileAsync(
|
|
274
|
+
npm.command,
|
|
275
|
+
[...npm.args, "install", "-g", "--prefix", prefix, `${PACKAGE_NAME}@latest`],
|
|
276
|
+
{ timeout: INSTALL_TIMEOUT_MS, windowsHide: true, shell: npm.shell, maxBuffer: 10 * 1024 * 1024 }
|
|
277
|
+
);
|
|
278
|
+
const installedVersion = await probeVersion(npm, ["view", `${PACKAGE_NAME}`, "version"]);
|
|
279
|
+
return { ok: true, output: [stdout, stderr].filter(Boolean).join("\n").trim(), installedVersion };
|
|
280
|
+
} catch (err) {
|
|
281
|
+
const e = err;
|
|
282
|
+
const output = [e.stdout, e.stderr, e.message].filter(Boolean).join("\n").trim();
|
|
283
|
+
return { ok: false, output, installedVersion: null };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
236
286
|
function compareSemver(a, b) {
|
|
237
287
|
const pa = a.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
238
288
|
const pb = b.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
@@ -244,6 +294,68 @@ function compareSemver(a, b) {
|
|
|
244
294
|
}
|
|
245
295
|
return 0;
|
|
246
296
|
}
|
|
297
|
+
function installedRuneya(prefix, platform = process.platform) {
|
|
298
|
+
const modules = platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
|
|
299
|
+
const pkgDir = join(modules, ...PACKAGE_NAME.split("/"));
|
|
300
|
+
try {
|
|
301
|
+
const pkg = JSON.parse(readFileSync(join(pkgDir, "package.json"), "utf8"));
|
|
302
|
+
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["runeya"];
|
|
303
|
+
return bin ? { entry: join(pkgDir, bin), version: pkg.version ?? "0.0.0" } : null;
|
|
304
|
+
} catch {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function globalCliEntry(prefix, platform = process.platform) {
|
|
309
|
+
return installedRuneya(prefix, platform)?.entry ?? null;
|
|
310
|
+
}
|
|
311
|
+
function userPrefix() {
|
|
312
|
+
return join(machineRoot(), "npm");
|
|
313
|
+
}
|
|
314
|
+
function isWritable(dir) {
|
|
315
|
+
let current2 = dir;
|
|
316
|
+
while (!existsSync(current2)) {
|
|
317
|
+
const parent = dirname(current2);
|
|
318
|
+
if (parent === current2) return false;
|
|
319
|
+
current2 = parent;
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
rmdirSync(mkdtempSync(join(current2, ".runeya-write-test-")));
|
|
323
|
+
return true;
|
|
324
|
+
} catch {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function installPrefix(globalPrefix, platform = process.platform) {
|
|
329
|
+
const modules = platform === "win32" ? join(globalPrefix, "node_modules") : join(globalPrefix, "lib", "node_modules");
|
|
330
|
+
return isWritable(modules) ? globalPrefix : userPrefix();
|
|
331
|
+
}
|
|
332
|
+
function isInside(dir, path) {
|
|
333
|
+
const rel = relative(dir, path);
|
|
334
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
335
|
+
}
|
|
336
|
+
function updatePrefix(globalPrefix, runningEntry) {
|
|
337
|
+
if (isInside(userPrefix(), runningEntry)) return userPrefix();
|
|
338
|
+
if (!globalPrefix) return null;
|
|
339
|
+
if (isInside(globalPrefix, runningEntry)) return globalPrefix;
|
|
340
|
+
return installPrefix(globalPrefix);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ../server/src/trpc/routers/health.ts
|
|
344
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
345
|
+
async function getDockerVersion() {
|
|
346
|
+
try {
|
|
347
|
+
const { stdout } = await execFileAsync2("docker", ["--version"], {
|
|
348
|
+
timeout: 3e3,
|
|
349
|
+
windowsHide: true
|
|
350
|
+
});
|
|
351
|
+
const match = /Docker version\s+([^,\s]+)/i.exec(stdout);
|
|
352
|
+
return match?.[1] ?? (stdout.trim() || null);
|
|
353
|
+
} catch {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
var startedAt = Date.now();
|
|
358
|
+
var NPM_LATEST_URL = "https://registry.npmjs.org/@runeya%2Fruneya/latest";
|
|
247
359
|
var VERSION_CACHE_MS = 4 * 60 * 1e3;
|
|
248
360
|
var versionCache = null;
|
|
249
361
|
var FORCED_CHECK_FLOOR_MS = 2e3;
|
|
@@ -336,48 +448,37 @@ var healthRouter = router({
|
|
|
336
448
|
})
|
|
337
449
|
});
|
|
338
450
|
|
|
339
|
-
// ../server/src/trpc/routers/
|
|
340
|
-
import { spawn, execFile as execFile2 } from "child_process";
|
|
341
|
-
import { promisify as promisify2 } from "util";
|
|
342
|
-
import { existsSync } from "fs";
|
|
343
|
-
import { dirname, join } from "path";
|
|
451
|
+
// ../server/src/trpc/routers/session.ts
|
|
344
452
|
import { z as z2 } from "zod";
|
|
345
453
|
import { TRPCError } from "@trpc/server";
|
|
346
|
-
var
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
shell: npm.shell
|
|
372
|
-
});
|
|
373
|
-
return stdout.trim().split("\n")[0]?.trim() || null;
|
|
374
|
-
} catch {
|
|
375
|
-
return null;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
454
|
+
var sessionRouter = router({
|
|
455
|
+
/**
|
|
456
|
+
* Donne au WebSocket le jeton que l'onglet vient de renouveler.
|
|
457
|
+
*
|
|
458
|
+
* Le contexte d'un WebSocket est créé une fois, à la connexion : sans cet
|
|
459
|
+
* appel, chaque requête passée par la socket présenterait le jeton d'origine
|
|
460
|
+
* et serait refusée dès qu'il expire.
|
|
461
|
+
*/
|
|
462
|
+
renewSocket: publicProcedure.input(z2.object({ token: z2.string().min(1).max(4096) })).mutation(async ({ ctx, input }) => {
|
|
463
|
+
if (!ctx.renewSocketToken) {
|
|
464
|
+
throw new TRPCError({ code: "BAD_REQUEST", message: "Only available over the WebSocket" });
|
|
465
|
+
}
|
|
466
|
+
try {
|
|
467
|
+
await ctx.renewSocketToken(input.token);
|
|
468
|
+
} catch {
|
|
469
|
+
throw new TRPCError({ code: "UNAUTHORIZED", message: "Invalid or expired token" });
|
|
470
|
+
}
|
|
471
|
+
return { ok: true };
|
|
472
|
+
})
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// ../server/src/trpc/routers/app-update.ts
|
|
476
|
+
import { spawn } from "child_process";
|
|
477
|
+
import { z as z3 } from "zod";
|
|
478
|
+
import { TRPCError as TRPCError2 } from "@trpc/server";
|
|
378
479
|
function requireDesktop() {
|
|
379
480
|
if (!env.FROM_DESKTOP) {
|
|
380
|
-
throw new
|
|
481
|
+
throw new TRPCError2({
|
|
381
482
|
code: "PRECONDITION_FAILED",
|
|
382
483
|
message: "Update from the app is only available for desktop launches."
|
|
383
484
|
});
|
|
@@ -386,19 +487,20 @@ function requireDesktop() {
|
|
|
386
487
|
var appUpdateRouter = router({
|
|
387
488
|
/** De quoi la mise à jour a besoin, et ce que la machine en offre. */
|
|
388
489
|
prerequisites: publicProcedure.output(
|
|
389
|
-
|
|
390
|
-
fromDesktop:
|
|
391
|
-
nodeVersion:
|
|
392
|
-
npmVersion:
|
|
393
|
-
npmPath:
|
|
394
|
-
globalPrefix:
|
|
395
|
-
ready:
|
|
490
|
+
z3.object({
|
|
491
|
+
fromDesktop: z3.boolean(),
|
|
492
|
+
nodeVersion: z3.string(),
|
|
493
|
+
npmVersion: z3.string().nullable(),
|
|
494
|
+
npmPath: z3.string(),
|
|
495
|
+
globalPrefix: z3.string().nullable(),
|
|
496
|
+
ready: z3.boolean()
|
|
396
497
|
})
|
|
397
498
|
).query(async ({ ctx }) => {
|
|
398
499
|
await requireLoopbackOrToken(ctx);
|
|
399
500
|
const npm = resolveNpm();
|
|
400
501
|
const npmVersion = await probeVersion(npm, ["--version"]);
|
|
401
|
-
const
|
|
502
|
+
const npmPrefix = npmVersion ? await probeVersion(npm, ["prefix", "-g"]) : null;
|
|
503
|
+
const globalPrefix = updatePrefix(npmPrefix, process.argv[1] ?? "");
|
|
402
504
|
return {
|
|
403
505
|
fromDesktop: env.FROM_DESKTOP,
|
|
404
506
|
nodeVersion: process.version,
|
|
@@ -416,28 +518,18 @@ var appUpdateRouter = router({
|
|
|
416
518
|
* est la seule chose qui dit à l'utilisateur quoi faire.
|
|
417
519
|
*/
|
|
418
520
|
install: publicProcedure.output(
|
|
419
|
-
|
|
420
|
-
ok:
|
|
421
|
-
output:
|
|
422
|
-
installedVersion:
|
|
521
|
+
z3.object({
|
|
522
|
+
ok: z3.boolean(),
|
|
523
|
+
output: z3.string(),
|
|
524
|
+
installedVersion: z3.string().nullable()
|
|
423
525
|
})
|
|
424
526
|
).mutation(async ({ ctx }) => {
|
|
425
527
|
await requireLoopbackOrToken(ctx);
|
|
426
528
|
requireDesktop();
|
|
427
529
|
const npm = resolveNpm();
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
[...npm.args, "install", "-g", `${PACKAGE_NAME}@latest`],
|
|
432
|
-
{ timeout: INSTALL_TIMEOUT_MS, windowsHide: true, shell: npm.shell, maxBuffer: 10 * 1024 * 1024 }
|
|
433
|
-
);
|
|
434
|
-
const installedVersion = await probeVersion(npm, ["view", `${PACKAGE_NAME}`, "version"]);
|
|
435
|
-
return { ok: true, output: [stdout, stderr].filter(Boolean).join("\n").trim(), installedVersion };
|
|
436
|
-
} catch (err) {
|
|
437
|
-
const e = err;
|
|
438
|
-
const output = [e.stdout, e.stderr, e.message].filter(Boolean).join("\n").trim();
|
|
439
|
-
return { ok: false, output, installedVersion: null };
|
|
440
|
-
}
|
|
530
|
+
const prefix = updatePrefix(await probeVersion(npm, ["prefix", "-g"]), process.argv[1] ?? "");
|
|
531
|
+
if (!prefix) return { ok: false, output: "npm ne r\xE9pond pas : impossible de trouver o\xF9 installer Runeya.", installedVersion: null };
|
|
532
|
+
return installRuneyaGlobally(npm, prefix);
|
|
441
533
|
}),
|
|
442
534
|
/**
|
|
443
535
|
* Relance Runeya, puis rend la main au nouveau process.
|
|
@@ -447,7 +539,7 @@ var appUpdateRouter = router({
|
|
|
447
539
|
* quelques instants — le démarrage réessaie déjà sur EADDRINUSE, c'est ce qui
|
|
448
540
|
* rend ce relais possible sans temporisation ici.
|
|
449
541
|
*/
|
|
450
|
-
restart: publicProcedure.output(
|
|
542
|
+
restart: publicProcedure.output(z3.object({ restarting: z3.literal(true), version: z3.string() })).mutation(async ({ ctx }) => {
|
|
451
543
|
await requireLoopbackOrToken(ctx);
|
|
452
544
|
requireDesktop();
|
|
453
545
|
const child = spawn(process.execPath, process.argv.slice(1), {
|
|
@@ -472,7 +564,7 @@ var appUpdateRouter = router({
|
|
|
472
564
|
});
|
|
473
565
|
|
|
474
566
|
// ../server/src/trpc/routers/changelog.ts
|
|
475
|
-
import { z as
|
|
567
|
+
import { z as z4 } from "zod";
|
|
476
568
|
var CACHE_MS = 5 * 60 * 1e3;
|
|
477
569
|
var cache = /* @__PURE__ */ new Map();
|
|
478
570
|
async function cloudChangelog(path, params) {
|
|
@@ -493,38 +585,38 @@ async function cloudChangelog(path, params) {
|
|
|
493
585
|
function appCeiling() {
|
|
494
586
|
return appVersion === "dev" ? null : appVersion;
|
|
495
587
|
}
|
|
496
|
-
var inputSchema =
|
|
588
|
+
var inputSchema = z4.object({
|
|
497
589
|
surface: ChangelogSurfaceSchema,
|
|
498
590
|
/** Dernière version déjà vue par ce navigateur, exclue du résultat. */
|
|
499
|
-
since:
|
|
591
|
+
since: z4.string().max(32).nullish()
|
|
500
592
|
});
|
|
501
593
|
var changelogRouter = router({
|
|
502
594
|
/** Ce qu'il y a à lire, sans les corps. */
|
|
503
|
-
index: publicProcedure.input(inputSchema).output(
|
|
595
|
+
index: publicProcedure.input(inputSchema).output(z4.object({ releases: z4.array(ChangelogIndexItemSchema), currentAppVersion: z4.string() })).query(async ({ input }) => {
|
|
504
596
|
const data = await cloudChangelog("/api/changelog", {
|
|
505
597
|
surface: input.surface,
|
|
506
598
|
since: input.since ?? null,
|
|
507
599
|
until: input.surface === "app" ? appCeiling() : null
|
|
508
600
|
});
|
|
509
|
-
const parsed =
|
|
601
|
+
const parsed = z4.object({ releases: z4.array(ChangelogIndexItemSchema) }).parse(data);
|
|
510
602
|
return { releases: parsed.releases, currentAppVersion: appVersion };
|
|
511
603
|
}),
|
|
512
604
|
/** Les entrées complètes. */
|
|
513
|
-
releases: publicProcedure.input(inputSchema).output(
|
|
605
|
+
releases: publicProcedure.input(inputSchema).output(z4.object({ releases: z4.array(ChangelogReleaseSchema), currentAppVersion: z4.string() })).query(async ({ input }) => {
|
|
514
606
|
const data = await cloudChangelog("/api/changelog/releases", {
|
|
515
607
|
surface: input.surface,
|
|
516
608
|
since: input.since ?? null,
|
|
517
609
|
until: input.surface === "app" ? appCeiling() : null
|
|
518
610
|
});
|
|
519
|
-
const parsed =
|
|
611
|
+
const parsed = z4.object({ releases: z4.array(ChangelogReleaseSchema) }).parse(data);
|
|
520
612
|
return { releases: parsed.releases, currentAppVersion: appVersion };
|
|
521
613
|
})
|
|
522
614
|
});
|
|
523
615
|
|
|
524
616
|
// ../server/src/trpc/routers/project.ts
|
|
525
|
-
import { z as
|
|
526
|
-
import { dirname as dirname2, isAbsolute } from "path";
|
|
527
|
-
import { TRPCError as
|
|
617
|
+
import { z as z5 } from "zod";
|
|
618
|
+
import { dirname as dirname2, isAbsolute as isAbsolute2 } from "path";
|
|
619
|
+
import { TRPCError as TRPCError3 } from "@trpc/server";
|
|
528
620
|
|
|
529
621
|
// ../server/src/services/current-project.ts
|
|
530
622
|
var current = null;
|
|
@@ -850,7 +942,7 @@ async function assertReadable(orgId) {
|
|
|
850
942
|
await assertOrgVaultReadable(orgId);
|
|
851
943
|
} catch (err) {
|
|
852
944
|
if (err instanceof OrgVaultUnreadableError) {
|
|
853
|
-
throw new
|
|
945
|
+
throw new TRPCError3({ code: "FORBIDDEN", message: err.message });
|
|
854
946
|
}
|
|
855
947
|
throw err;
|
|
856
948
|
}
|
|
@@ -863,10 +955,10 @@ var projectRouter = router({
|
|
|
863
955
|
* `orgId` names which project — the same id can exist in several
|
|
864
956
|
* organizations. Left out, the id must answer for exactly one project.
|
|
865
957
|
*/
|
|
866
|
-
get: protectedProcedure.input(
|
|
958
|
+
get: protectedProcedure.input(z5.object({ id: z5.string().max(128), orgId: z5.string().max(128).nullable().optional() })).query(async ({ input }) => {
|
|
867
959
|
const project = await projectStore.get(input);
|
|
868
960
|
if (!project) {
|
|
869
|
-
throw new
|
|
961
|
+
throw new TRPCError3({ code: "NOT_FOUND", message: `Project ${input.id} not found` });
|
|
870
962
|
}
|
|
871
963
|
await assertReadable(project.orgId);
|
|
872
964
|
return project;
|
|
@@ -877,23 +969,23 @@ var projectRouter = router({
|
|
|
877
969
|
* Environments are intentionally excluded — they carry secret values that
|
|
878
970
|
* stay local.
|
|
879
971
|
*/
|
|
880
|
-
export: protectedProcedure.input(
|
|
972
|
+
export: protectedProcedure.input(z5.object({ id: z5.string().max(128), orgId: z5.string().max(128).nullable().optional() })).query(async ({ input }) => {
|
|
881
973
|
const project = await projectStore.get(input);
|
|
882
974
|
if (!project) {
|
|
883
|
-
throw new
|
|
975
|
+
throw new TRPCError3({ code: "NOT_FOUND", message: `Project ${input.id} not found` });
|
|
884
976
|
}
|
|
885
977
|
await assertReadable(project.orgId);
|
|
886
978
|
const services = (await Promise.all(project.serviceIds.map((sid) => serviceStore.getSanitized(sid)))).filter((s) => s !== null);
|
|
887
979
|
return { project, services };
|
|
888
980
|
}),
|
|
889
981
|
create: protectedProcedure.input(
|
|
890
|
-
|
|
891
|
-
name:
|
|
892
|
-
description:
|
|
893
|
-
orgId:
|
|
894
|
-
serviceIds:
|
|
982
|
+
z5.object({
|
|
983
|
+
name: z5.string().min(1).max(200),
|
|
984
|
+
description: z5.string().max(1e4).optional(),
|
|
985
|
+
orgId: z5.string().max(128).nullable().optional(),
|
|
986
|
+
serviceIds: z5.array(z5.string().max(128)).max(1e4).optional(),
|
|
895
987
|
/** Le dossier d'un projet local. Omis : le dossier de lancement. */
|
|
896
|
-
localPath:
|
|
988
|
+
localPath: z5.string().min(1).max(4096).refine(isAbsolute2, "Chemin absolu attendu").optional()
|
|
897
989
|
})
|
|
898
990
|
).mutation(async ({ input }) => {
|
|
899
991
|
const { localPath, ...fields } = input;
|
|
@@ -907,9 +999,9 @@ var projectRouter = router({
|
|
|
907
999
|
}, fields.orgId ? void 0 : localPath);
|
|
908
1000
|
} catch (err) {
|
|
909
1001
|
if (err instanceof LocalProjectRefusedError) {
|
|
910
|
-
throw new
|
|
1002
|
+
throw new TRPCError3({ code: "CONFLICT", message: err.code, cause: err });
|
|
911
1003
|
}
|
|
912
|
-
throw new
|
|
1004
|
+
throw new TRPCError3({ code: "CONFLICT", message: err.message });
|
|
913
1005
|
}
|
|
914
1006
|
const localEnv = await environmentStore.create({
|
|
915
1007
|
projectId: project.id,
|
|
@@ -928,38 +1020,38 @@ var projectRouter = router({
|
|
|
928
1020
|
return result;
|
|
929
1021
|
}),
|
|
930
1022
|
update: protectedProcedure.input(
|
|
931
|
-
|
|
932
|
-
id:
|
|
933
|
-
name:
|
|
934
|
-
description:
|
|
935
|
-
orgId:
|
|
936
|
-
serviceIds:
|
|
937
|
-
groupOrder:
|
|
1023
|
+
z5.object({
|
|
1024
|
+
id: z5.string().max(128),
|
|
1025
|
+
name: z5.string().min(1).max(200).optional(),
|
|
1026
|
+
description: z5.string().max(1e4).optional(),
|
|
1027
|
+
orgId: z5.string().max(128).nullable().optional(),
|
|
1028
|
+
serviceIds: z5.array(z5.string().max(128)).max(1e4).optional(),
|
|
1029
|
+
groupOrder: z5.array(z5.string().max(200)).max(100).optional()
|
|
938
1030
|
})
|
|
939
1031
|
).mutation(async ({ input }) => {
|
|
940
1032
|
const project = await projectStore.update(input);
|
|
941
1033
|
if (!project) {
|
|
942
|
-
throw new
|
|
1034
|
+
throw new TRPCError3({ code: "NOT_FOUND", message: `Project ${input.id} not found` });
|
|
943
1035
|
}
|
|
944
1036
|
await auditService.log({ userId: "local", action: "update", entityType: "project", entityId: input.id });
|
|
945
1037
|
return project;
|
|
946
1038
|
}),
|
|
947
|
-
delete: protectedProcedure.input(
|
|
1039
|
+
delete: protectedProcedure.input(z5.object({ id: z5.string().max(128), orgId: z5.string().max(128).nullable().optional() })).mutation(async ({ input }) => {
|
|
948
1040
|
const deleted = await projectStore.delete(input);
|
|
949
1041
|
if (!deleted) {
|
|
950
|
-
throw new
|
|
1042
|
+
throw new TRPCError3({ code: "NOT_FOUND", message: `Project ${input.id} not found` });
|
|
951
1043
|
}
|
|
952
1044
|
await auditService.log({ userId: "local", action: "delete", entityType: "project", entityId: input.id });
|
|
953
1045
|
return { success: true };
|
|
954
1046
|
}),
|
|
955
|
-
addService: protectedProcedure.input(
|
|
956
|
-
projectId:
|
|
957
|
-
orgId:
|
|
958
|
-
serviceId:
|
|
1047
|
+
addService: protectedProcedure.input(z5.object({
|
|
1048
|
+
projectId: z5.string().max(128),
|
|
1049
|
+
orgId: z5.string().max(128).nullable().optional(),
|
|
1050
|
+
serviceId: z5.string().max(128)
|
|
959
1051
|
})).mutation(async ({ input }) => {
|
|
960
1052
|
const project = await attachServiceToProject({ id: input.projectId, orgId: input.orgId }, input.serviceId);
|
|
961
1053
|
if (!project) {
|
|
962
|
-
throw new
|
|
1054
|
+
throw new TRPCError3({ code: "NOT_FOUND", message: `Project ${input.projectId} not found` });
|
|
963
1055
|
}
|
|
964
1056
|
await auditService.log({
|
|
965
1057
|
userId: "local",
|
|
@@ -970,17 +1062,17 @@ var projectRouter = router({
|
|
|
970
1062
|
});
|
|
971
1063
|
return project;
|
|
972
1064
|
}),
|
|
973
|
-
removeService: protectedProcedure.input(
|
|
974
|
-
projectId:
|
|
975
|
-
orgId:
|
|
976
|
-
serviceId:
|
|
1065
|
+
removeService: protectedProcedure.input(z5.object({
|
|
1066
|
+
projectId: z5.string().max(128),
|
|
1067
|
+
orgId: z5.string().max(128).nullable().optional(),
|
|
1068
|
+
serviceId: z5.string().max(128)
|
|
977
1069
|
})).mutation(async ({ input }) => {
|
|
978
1070
|
const project = await projectStore.removeService(
|
|
979
1071
|
{ id: input.projectId, orgId: input.orgId },
|
|
980
1072
|
input.serviceId
|
|
981
1073
|
);
|
|
982
1074
|
if (!project) {
|
|
983
|
-
throw new
|
|
1075
|
+
throw new TRPCError3({ code: "NOT_FOUND", message: `Project ${input.projectId} not found` });
|
|
984
1076
|
}
|
|
985
1077
|
await auditService.log({
|
|
986
1078
|
userId: "local",
|
|
@@ -991,21 +1083,21 @@ var projectRouter = router({
|
|
|
991
1083
|
});
|
|
992
1084
|
return project;
|
|
993
1085
|
}),
|
|
994
|
-
setActiveEnvironment: protectedProcedure.input(
|
|
995
|
-
projectId:
|
|
996
|
-
orgId:
|
|
997
|
-
environmentId:
|
|
1086
|
+
setActiveEnvironment: protectedProcedure.input(z5.object({
|
|
1087
|
+
projectId: z5.string().max(128),
|
|
1088
|
+
orgId: z5.string().max(128).nullable().optional(),
|
|
1089
|
+
environmentId: z5.string().max(128)
|
|
998
1090
|
})).mutation(async ({ input }) => {
|
|
999
1091
|
const project = await projectStore.get({ id: input.projectId, orgId: input.orgId });
|
|
1000
1092
|
if (!project) {
|
|
1001
|
-
throw new
|
|
1093
|
+
throw new TRPCError3({ code: "NOT_FOUND", message: `Project ${input.projectId} not found` });
|
|
1002
1094
|
}
|
|
1003
1095
|
const environment = await environmentStore.get(input.environmentId);
|
|
1004
1096
|
if (!environment) {
|
|
1005
|
-
throw new
|
|
1097
|
+
throw new TRPCError3({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
1006
1098
|
}
|
|
1007
1099
|
if (environment.projectId !== input.projectId) {
|
|
1008
|
-
throw new
|
|
1100
|
+
throw new TRPCError3({ code: "FORBIDDEN", message: "Environment does not belong to this project" });
|
|
1009
1101
|
}
|
|
1010
1102
|
const runningServices = [];
|
|
1011
1103
|
for (const serviceId of project.serviceIds) {
|
|
@@ -1030,7 +1122,7 @@ var projectRouter = router({
|
|
|
1030
1122
|
input.environmentId
|
|
1031
1123
|
);
|
|
1032
1124
|
if (!updated) {
|
|
1033
|
-
throw new
|
|
1125
|
+
throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR", message: "Failed to set active environment" });
|
|
1034
1126
|
}
|
|
1035
1127
|
for (const serviceId of project.serviceIds) {
|
|
1036
1128
|
const service = await serviceStore.get(serviceId);
|
|
@@ -1079,7 +1171,7 @@ var projectRouter = router({
|
|
|
1079
1171
|
* système s'en sert, pour montrer les services du projet ouvert plutôt que
|
|
1080
1172
|
* ceux de toute la machine.
|
|
1081
1173
|
*/
|
|
1082
|
-
setCurrent: protectedProcedure.input(
|
|
1174
|
+
setCurrent: protectedProcedure.input(z5.object({ id: z5.string().max(128), orgId: z5.string().max(128).nullable().default(null) }).nullable()).mutation(({ input }) => {
|
|
1083
1175
|
setCurrentProject(input ? { id: input.id, orgId: input.orgId } : null);
|
|
1084
1176
|
return { ok: true };
|
|
1085
1177
|
}),
|
|
@@ -1095,13 +1187,13 @@ var projectRouter = router({
|
|
|
1095
1187
|
* des secrets déchiffrés : elles servent à détecter la référence, et
|
|
1096
1188
|
* s'arrêtent là.
|
|
1097
1189
|
*/
|
|
1098
|
-
graph: protectedProcedure.input(
|
|
1099
|
-
id:
|
|
1100
|
-
orgId:
|
|
1190
|
+
graph: protectedProcedure.input(z5.object({
|
|
1191
|
+
id: z5.string().min(1).max(128),
|
|
1192
|
+
orgId: z5.string().max(128).nullable().optional()
|
|
1101
1193
|
})).query(async ({ ctx, input }) => {
|
|
1102
1194
|
const project = await projectStore.get(input);
|
|
1103
1195
|
if (!project) {
|
|
1104
|
-
throw new
|
|
1196
|
+
throw new TRPCError3({ code: "NOT_FOUND", message: `Project ${input.id} not found` });
|
|
1105
1197
|
}
|
|
1106
1198
|
await assertReadable(project.orgId);
|
|
1107
1199
|
const allowed = getAllowedServiceIds(ctx);
|
|
@@ -1143,9 +1235,9 @@ var projectRouter = router({
|
|
|
1143
1235
|
}
|
|
1144
1236
|
return paths;
|
|
1145
1237
|
}),
|
|
1146
|
-
getLocalPath: protectedProcedure.input(
|
|
1147
|
-
id:
|
|
1148
|
-
orgId:
|
|
1238
|
+
getLocalPath: protectedProcedure.input(z5.object({
|
|
1239
|
+
id: z5.string().min(1).max(128),
|
|
1240
|
+
orgId: z5.string().max(128).nullable().optional()
|
|
1149
1241
|
})).query(async ({ input }) => {
|
|
1150
1242
|
return { path: await projectPathStore.get(input) };
|
|
1151
1243
|
}),
|
|
@@ -1157,34 +1249,34 @@ var projectRouter = router({
|
|
|
1157
1249
|
* makes services run against the wrong code.
|
|
1158
1250
|
*/
|
|
1159
1251
|
/** Ajouter à la liste le projet local déjà présent dans un dossier. */
|
|
1160
|
-
importLocal: protectedProcedure.input(
|
|
1252
|
+
importLocal: protectedProcedure.input(z5.object({ path: z5.string().min(1).max(4096).refine(isAbsolute2, "Chemin absolu attendu") })).mutation(async ({ input }) => {
|
|
1161
1253
|
try {
|
|
1162
1254
|
const project = await projectStore.importLocal(input.path);
|
|
1163
1255
|
await auditService.log({ userId: "local", action: "update", entityType: "project", entityId: project.id, metadata: { importedFrom: input.path } });
|
|
1164
1256
|
return project;
|
|
1165
1257
|
} catch (err) {
|
|
1166
1258
|
if (err instanceof LocalProjectRefusedError) {
|
|
1167
|
-
throw new
|
|
1259
|
+
throw new TRPCError3({ code: "CONFLICT", message: err.code, cause: err });
|
|
1168
1260
|
}
|
|
1169
1261
|
throw err;
|
|
1170
1262
|
}
|
|
1171
1263
|
}),
|
|
1172
|
-
setLocalPath: protectedProcedure.input(
|
|
1173
|
-
id:
|
|
1174
|
-
orgId:
|
|
1175
|
-
path:
|
|
1264
|
+
setLocalPath: protectedProcedure.input(z5.object({
|
|
1265
|
+
id: z5.string().min(1).max(128),
|
|
1266
|
+
orgId: z5.string().max(128).nullable().optional(),
|
|
1267
|
+
path: z5.string().min(1).max(4096)
|
|
1176
1268
|
})).mutation(async ({ input }) => {
|
|
1177
1269
|
const project = await projectStore.get(input);
|
|
1178
|
-
if (!project) throw new
|
|
1270
|
+
if (!project) throw new TRPCError3({ code: "NOT_FOUND", message: "Projet introuvable" });
|
|
1179
1271
|
if (await holdsRuneyaProject(input.path)) {
|
|
1180
|
-
throw new
|
|
1272
|
+
throw new TRPCError3({
|
|
1181
1273
|
code: "CONFLICT",
|
|
1182
1274
|
message: "Ce r\xE9pertoire contient d\xE9j\xE0 un projet Runeya"
|
|
1183
1275
|
});
|
|
1184
1276
|
}
|
|
1185
1277
|
const taken = await projectPathStore.projectAt(input.path, project);
|
|
1186
1278
|
if (taken) {
|
|
1187
|
-
throw new
|
|
1279
|
+
throw new TRPCError3({
|
|
1188
1280
|
code: "CONFLICT",
|
|
1189
1281
|
message: "Un autre projet est d\xE9j\xE0 rattach\xE9 \xE0 ce r\xE9pertoire"
|
|
1190
1282
|
});
|
|
@@ -1253,8 +1345,8 @@ var projectRouter = router({
|
|
|
1253
1345
|
});
|
|
1254
1346
|
|
|
1255
1347
|
// ../server/src/trpc/routers/agent.ts
|
|
1256
|
-
import { z as
|
|
1257
|
-
import { TRPCError as
|
|
1348
|
+
import { z as z6 } from "zod";
|
|
1349
|
+
import { TRPCError as TRPCError4 } from "@trpc/server";
|
|
1258
1350
|
var SUBSCRIPTION_QUEUE_MAX = LOG_BUFFER_SIZE;
|
|
1259
1351
|
var agentRouter = router({
|
|
1260
1352
|
/** List all registered agents with their connection status */
|
|
@@ -1262,10 +1354,10 @@ var agentRouter = router({
|
|
|
1262
1354
|
return agentManager.listAgents();
|
|
1263
1355
|
}),
|
|
1264
1356
|
/** Get a single agent's config and status */
|
|
1265
|
-
get: protectedProcedure.input(
|
|
1357
|
+
get: protectedProcedure.input(z6.object({ id: z6.string().max(128) })).query(({ input }) => {
|
|
1266
1358
|
const agent = agentManager.getAgent(input.id);
|
|
1267
1359
|
if (!agent) {
|
|
1268
|
-
throw new
|
|
1360
|
+
throw new TRPCError4({ code: "NOT_FOUND", message: `Agent ${input.id} not found` });
|
|
1269
1361
|
}
|
|
1270
1362
|
return {
|
|
1271
1363
|
config: { ...agent.config, passphrase: "***" },
|
|
@@ -1274,10 +1366,10 @@ var agentRouter = router({
|
|
|
1274
1366
|
}),
|
|
1275
1367
|
/** Test connectivity to an agent given url + passphrase */
|
|
1276
1368
|
testConnection: protectedProcedure.input(
|
|
1277
|
-
|
|
1278
|
-
url:
|
|
1279
|
-
passphrase:
|
|
1280
|
-
agentId:
|
|
1369
|
+
z6.object({
|
|
1370
|
+
url: z6.string().url().max(2048),
|
|
1371
|
+
passphrase: z6.string().min(1).max(1024).optional(),
|
|
1372
|
+
agentId: z6.string().max(128).optional()
|
|
1281
1373
|
})
|
|
1282
1374
|
).mutation(async ({ input }) => {
|
|
1283
1375
|
let passphrase = input.passphrase;
|
|
@@ -1285,13 +1377,13 @@ var agentRouter = router({
|
|
|
1285
1377
|
if (!passphrase && input.agentId) {
|
|
1286
1378
|
const agent = agentManager.getAgentConfig(input.agentId);
|
|
1287
1379
|
if (!agent) {
|
|
1288
|
-
throw new
|
|
1380
|
+
throw new TRPCError4({ code: "NOT_FOUND", message: "Agent not found" });
|
|
1289
1381
|
}
|
|
1290
1382
|
passphrase = agent.passphrase;
|
|
1291
1383
|
targetUrl = agent.url;
|
|
1292
1384
|
}
|
|
1293
1385
|
if (!passphrase) {
|
|
1294
|
-
throw new
|
|
1386
|
+
throw new TRPCError4({ code: "BAD_REQUEST", message: "Passphrase is required" });
|
|
1295
1387
|
}
|
|
1296
1388
|
const url = new URL("/api/trpc/health.check", targetUrl);
|
|
1297
1389
|
try {
|
|
@@ -1314,13 +1406,13 @@ var agentRouter = router({
|
|
|
1314
1406
|
}),
|
|
1315
1407
|
/** Register a new agent */
|
|
1316
1408
|
register: protectedProcedure.input(
|
|
1317
|
-
|
|
1318
|
-
name:
|
|
1319
|
-
url:
|
|
1320
|
-
passphrase:
|
|
1409
|
+
z6.object({
|
|
1410
|
+
name: z6.string().min(1).max(100),
|
|
1411
|
+
url: z6.string().url().max(2048),
|
|
1412
|
+
passphrase: z6.string().min(1).max(1024),
|
|
1321
1413
|
// À quelle organisation rattacher l'agent. Absent = la machine, ce que
|
|
1322
1414
|
// font l'agent local et tout client antérieur à ce champ.
|
|
1323
|
-
orgId:
|
|
1415
|
+
orgId: z6.string().min(1).max(128).nullable().optional()
|
|
1324
1416
|
})
|
|
1325
1417
|
).mutation(async ({ input }) => {
|
|
1326
1418
|
const config = await agentManager.register(
|
|
@@ -1333,13 +1425,13 @@ var agentRouter = router({
|
|
|
1333
1425
|
}),
|
|
1334
1426
|
/** Update an existing agent */
|
|
1335
1427
|
update: protectedProcedure.input(
|
|
1336
|
-
|
|
1337
|
-
id:
|
|
1338
|
-
name:
|
|
1339
|
-
url:
|
|
1340
|
-
passphrase:
|
|
1341
|
-
passthroughEnv:
|
|
1342
|
-
|
|
1428
|
+
z6.object({
|
|
1429
|
+
id: z6.string().max(128),
|
|
1430
|
+
name: z6.string().min(1).max(100).optional(),
|
|
1431
|
+
url: z6.string().url().max(2048).optional(),
|
|
1432
|
+
passphrase: z6.string().min(1).max(1024).optional(),
|
|
1433
|
+
passthroughEnv: z6.array(
|
|
1434
|
+
z6.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).max(256)
|
|
1343
1435
|
).max(100).optional()
|
|
1344
1436
|
})
|
|
1345
1437
|
).mutation(async ({ input }) => {
|
|
@@ -1350,17 +1442,17 @@ var agentRouter = router({
|
|
|
1350
1442
|
} catch (err) {
|
|
1351
1443
|
if (err instanceof Error) {
|
|
1352
1444
|
if (err.message.includes("not found")) {
|
|
1353
|
-
throw new
|
|
1445
|
+
throw new TRPCError4({ code: "NOT_FOUND", message: err.message });
|
|
1354
1446
|
}
|
|
1355
1447
|
if (err.message.includes("Cannot modify")) {
|
|
1356
|
-
throw new
|
|
1448
|
+
throw new TRPCError4({ code: "FORBIDDEN", message: err.message });
|
|
1357
1449
|
}
|
|
1358
1450
|
}
|
|
1359
1451
|
throw err;
|
|
1360
1452
|
}
|
|
1361
1453
|
}),
|
|
1362
1454
|
/** Get resolved passthrough env values from an agent */
|
|
1363
|
-
getPassthroughEnv: protectedProcedure.input(
|
|
1455
|
+
getPassthroughEnv: protectedProcedure.input(z6.object({ id: z6.string().max(128) })).query(async ({ input }) => {
|
|
1364
1456
|
try {
|
|
1365
1457
|
return await agentManager.agentQuery(input.id, "config.getPassthroughEnv");
|
|
1366
1458
|
} catch {
|
|
@@ -1368,16 +1460,16 @@ var agentRouter = router({
|
|
|
1368
1460
|
}
|
|
1369
1461
|
}),
|
|
1370
1462
|
/** Remove a registered agent */
|
|
1371
|
-
remove: protectedProcedure.input(
|
|
1463
|
+
remove: protectedProcedure.input(z6.object({ id: z6.string().max(128) })).mutation(async ({ input }) => {
|
|
1372
1464
|
try {
|
|
1373
1465
|
const removed = await agentManager.remove(input.id);
|
|
1374
1466
|
if (!removed) {
|
|
1375
|
-
throw new
|
|
1467
|
+
throw new TRPCError4({ code: "NOT_FOUND", message: `Agent ${input.id} not found` });
|
|
1376
1468
|
}
|
|
1377
1469
|
return { success: true };
|
|
1378
1470
|
} catch (err) {
|
|
1379
1471
|
if (err instanceof Error && err.message === "Cannot remove the local agent") {
|
|
1380
|
-
throw new
|
|
1472
|
+
throw new TRPCError4({ code: "FORBIDDEN", message: err.message });
|
|
1381
1473
|
}
|
|
1382
1474
|
throw err;
|
|
1383
1475
|
}
|
|
@@ -1415,16 +1507,16 @@ var agentRouter = router({
|
|
|
1415
1507
|
});
|
|
1416
1508
|
|
|
1417
1509
|
// ../server/src/trpc/routers/process.ts
|
|
1418
|
-
import { z as
|
|
1419
|
-
import { TRPCError as
|
|
1510
|
+
import { z as z7 } from "zod";
|
|
1511
|
+
import { TRPCError as TRPCError6 } from "@trpc/server";
|
|
1420
1512
|
|
|
1421
1513
|
// ../server/src/services/process-control.ts
|
|
1422
|
-
import { TRPCError as
|
|
1514
|
+
import { TRPCError as TRPCError5 } from "@trpc/server";
|
|
1423
1515
|
async function resolveTarget(processId, fallbackAgentId) {
|
|
1424
1516
|
const service = await serviceStore.get(processId) ?? null;
|
|
1425
1517
|
const agentId = (service ? await resolveEffectiveAgentId(service) : null) ?? fallbackAgentId ?? null;
|
|
1426
1518
|
if (!agentId) {
|
|
1427
|
-
throw new
|
|
1519
|
+
throw new TRPCError5({
|
|
1428
1520
|
code: "BAD_REQUEST",
|
|
1429
1521
|
message: "No agent assigned to this service. Set an agent on the service or on the active environment."
|
|
1430
1522
|
});
|
|
@@ -1440,7 +1532,7 @@ async function assertEnvironmentComplete(service) {
|
|
|
1440
1532
|
activeEnvironmentId: project.activeEnvironmentId
|
|
1441
1533
|
});
|
|
1442
1534
|
if (missing.length === 0) return;
|
|
1443
|
-
throw new
|
|
1535
|
+
throw new TRPCError5({
|
|
1444
1536
|
code: "PRECONDITION_FAILED",
|
|
1445
1537
|
message: `Missing environment variables: ${missing.map((m) => m.key).join(", ")}`,
|
|
1446
1538
|
cause: { missingVariables: missing, environmentId: project.activeEnvironmentId }
|
|
@@ -1474,7 +1566,7 @@ async function startProcess(processId, fallbackAgentId) {
|
|
|
1474
1566
|
} catch (err) {
|
|
1475
1567
|
const err_ = err;
|
|
1476
1568
|
const isFetchError = err_?.cause?.code === "ECONNREFUSED" || err_?.message === "fetch failed" || err_?.name === "TypeError";
|
|
1477
|
-
throw new
|
|
1569
|
+
throw new TRPCError5({
|
|
1478
1570
|
code: "INTERNAL_SERVER_ERROR",
|
|
1479
1571
|
message: isFetchError ? `Cannot reach agent \u2014 make sure the agent is running` : err_?.message ?? "Failed to start process"
|
|
1480
1572
|
});
|
|
@@ -1581,13 +1673,13 @@ async function* forwardLogBatches(agentId, processId) {
|
|
|
1581
1673
|
}
|
|
1582
1674
|
var processRouter = router({
|
|
1583
1675
|
/** List all processes across all connected agents */
|
|
1584
|
-
list: protectedProcedure.meta({ openapi: { method: "GET", path: "/processes", tags: ["processes"], summary: "List all processes", protect: true } }).output(
|
|
1676
|
+
list: protectedProcedure.meta({ openapi: { method: "GET", path: "/processes", tags: ["processes"], summary: "List all processes", protect: true } }).output(z7.any()).query(() => listAllProcesses()),
|
|
1585
1677
|
/** Get a single process from a specific agent */
|
|
1586
|
-
get: protectedProcedure.meta({ openapi: { method: "GET", path: "/processes/{agentId}/{id}", tags: ["processes"], summary: "Get a process by agent and ID", protect: true } }).input(
|
|
1678
|
+
get: protectedProcedure.meta({ openapi: { method: "GET", path: "/processes/{agentId}/{id}", tags: ["processes"], summary: "Get a process by agent and ID", protect: true } }).input(z7.object({ agentId: z7.string().max(128), id: z7.string().max(128) })).output(z7.any()).query(async ({ input, ctx }) => {
|
|
1587
1679
|
requireServiceAction(ctx, input.id, "service:read");
|
|
1588
1680
|
const status = agentManager.getStatus(input.agentId);
|
|
1589
1681
|
if (!status) {
|
|
1590
|
-
throw new
|
|
1682
|
+
throw new TRPCError6({ code: "NOT_FOUND", message: `Agent ${input.agentId} not found` });
|
|
1591
1683
|
}
|
|
1592
1684
|
const proc = await agentManager.agentQuery(input.agentId, "process.get", { id: input.id });
|
|
1593
1685
|
return { ...proc, agentId: input.agentId };
|
|
@@ -1597,37 +1689,37 @@ var processRouter = router({
|
|
|
1597
1689
|
* (or restarted). This avoids pushing configs for every service at agent boot,
|
|
1598
1690
|
* which would be wasteful when many services are never started.
|
|
1599
1691
|
* The deploy call is idempotent — creates the process if missing, updates config if it exists. */
|
|
1600
|
-
start: protectedProcedure.meta({ openapi: { method: "POST", path: "/processes/{agentId}/{processId}/start", tags: ["processes"], summary: "Start a process", protect: true } }).input(
|
|
1692
|
+
start: protectedProcedure.meta({ openapi: { method: "POST", path: "/processes/{agentId}/{processId}/start", tags: ["processes"], summary: "Start a process", protect: true } }).input(z7.object({ agentId: z7.string().max(128).optional(), processId: z7.string().max(128) })).output(z7.any()).mutation(async ({ input, ctx }) => {
|
|
1601
1693
|
requireServiceAction(ctx, input.processId, "service:start");
|
|
1602
1694
|
return startProcess(input.processId, input.agentId);
|
|
1603
1695
|
}),
|
|
1604
1696
|
/** Stop a process on an agent */
|
|
1605
|
-
stop: protectedProcedure.meta({ openapi: { method: "POST", path: "/processes/{agentId}/{processId}/stop", tags: ["processes"], summary: "Stop a process", protect: true } }).input(
|
|
1697
|
+
stop: protectedProcedure.meta({ openapi: { method: "POST", path: "/processes/{agentId}/{processId}/stop", tags: ["processes"], summary: "Stop a process", protect: true } }).input(z7.object({ agentId: z7.string().max(128), processId: z7.string().max(128) })).output(z7.any()).mutation(async ({ input, ctx }) => {
|
|
1606
1698
|
requireServiceAction(ctx, input.processId, "service:stop");
|
|
1607
1699
|
return stopProcess(input.processId, input.agentId);
|
|
1608
1700
|
}),
|
|
1609
1701
|
/** Restart a process on an agent.
|
|
1610
1702
|
* Sends stop → deploy (latest resolved config) → start so that
|
|
1611
1703
|
* config/variable changes take effect. Same lazy deploy pattern as start. */
|
|
1612
|
-
restart: protectedProcedure.meta({ openapi: { method: "POST", path: "/processes/{agentId}/{processId}/restart", tags: ["processes"], summary: "Restart a process", protect: true } }).input(
|
|
1704
|
+
restart: protectedProcedure.meta({ openapi: { method: "POST", path: "/processes/{agentId}/{processId}/restart", tags: ["processes"], summary: "Restart a process", protect: true } }).input(z7.object({ agentId: z7.string().max(128).optional(), processId: z7.string().max(128) })).output(z7.any()).mutation(async ({ input, ctx }) => {
|
|
1613
1705
|
requireServiceAction(ctx, input.processId, "service:restart");
|
|
1614
1706
|
return restartProcess(input.processId, input.agentId);
|
|
1615
1707
|
}),
|
|
1616
1708
|
/** Get buffered logs for a process from an agent */
|
|
1617
|
-
logs: protectedProcedure.meta({ openapi: { method: "GET", path: "/processes/{agentId}/{processId}/logs", tags: ["processes"], summary: "Get buffered logs for a process", protect: true } }).input(
|
|
1709
|
+
logs: protectedProcedure.meta({ openapi: { method: "GET", path: "/processes/{agentId}/{processId}/logs", tags: ["processes"], summary: "Get buffered logs for a process", protect: true } }).input(z7.object({ agentId: z7.string().max(128), processId: z7.string().max(128) })).output(z7.any()).query(async ({ input, ctx }) => {
|
|
1618
1710
|
requireServiceAction(ctx, input.processId, "logs:read");
|
|
1619
1711
|
return agentManager.agentQuery(input.agentId, "process.logs", { id: input.processId });
|
|
1620
1712
|
}),
|
|
1621
1713
|
/** Clear buffered logs for a process on an agent */
|
|
1622
|
-
clearLogBuffer: protectedProcedure.input(
|
|
1714
|
+
clearLogBuffer: protectedProcedure.input(z7.object({ agentId: z7.string().max(128), processId: z7.string().max(128) })).mutation(async ({ input }) => {
|
|
1623
1715
|
await agentManager.agentMutation(input.agentId, "process.clearLogBuffer", { id: input.processId });
|
|
1624
1716
|
await auditService.log({ userId: "local", action: "clearLogBuffer", entityType: "process", entityId: input.processId, metadata: { agentId: input.agentId } });
|
|
1625
1717
|
return { success: true };
|
|
1626
1718
|
}),
|
|
1627
1719
|
/** Execute a shortcut command on an agent, streaming output into the process log */
|
|
1628
|
-
executeShortcut: protectedProcedure.input(
|
|
1629
|
-
agentId:
|
|
1630
|
-
processId:
|
|
1720
|
+
executeShortcut: protectedProcedure.input(z7.object({
|
|
1721
|
+
agentId: z7.string().max(128),
|
|
1722
|
+
processId: z7.string().max(128),
|
|
1631
1723
|
shortcut: ServiceShortcutSchema
|
|
1632
1724
|
})).mutation(async ({ input }) => {
|
|
1633
1725
|
const service = await serviceStore.get(input.processId);
|
|
@@ -1655,12 +1747,12 @@ var processRouter = router({
|
|
|
1655
1747
|
return result;
|
|
1656
1748
|
}),
|
|
1657
1749
|
/** Execute an ad-hoc command on an agent, streaming output into the process log */
|
|
1658
|
-
executeAdHocCommand: protectedProcedure.input(
|
|
1659
|
-
agentId:
|
|
1660
|
-
processId:
|
|
1661
|
-
command:
|
|
1662
|
-
cwd:
|
|
1663
|
-
commandId:
|
|
1750
|
+
executeAdHocCommand: protectedProcedure.input(z7.object({
|
|
1751
|
+
agentId: z7.string().max(128),
|
|
1752
|
+
processId: z7.string().max(128),
|
|
1753
|
+
command: z7.string().min(1).max(4096),
|
|
1754
|
+
cwd: z7.string().max(4096).optional(),
|
|
1755
|
+
commandId: z7.string().max(128).optional()
|
|
1664
1756
|
})).mutation(async ({ input }) => {
|
|
1665
1757
|
const service = await serviceStore.get(input.processId);
|
|
1666
1758
|
if (service) {
|
|
@@ -1694,25 +1786,25 @@ var processRouter = router({
|
|
|
1694
1786
|
return result;
|
|
1695
1787
|
}),
|
|
1696
1788
|
/** Cancel a running shortcut command on an agent */
|
|
1697
|
-
cancelShortcut: protectedProcedure.input(
|
|
1698
|
-
agentId:
|
|
1699
|
-
processId:
|
|
1700
|
-
shortcutId:
|
|
1789
|
+
cancelShortcut: protectedProcedure.input(z7.object({
|
|
1790
|
+
agentId: z7.string().max(128),
|
|
1791
|
+
processId: z7.string().max(128),
|
|
1792
|
+
shortcutId: z7.string().max(128)
|
|
1701
1793
|
})).mutation(async ({ input }) => {
|
|
1702
1794
|
await agentManager.agentMutation(input.agentId, "process.cancelShortcut", { id: input.processId, shortcutId: input.shortcutId });
|
|
1703
1795
|
return { success: true };
|
|
1704
1796
|
}),
|
|
1705
1797
|
/** Cancel a running ad-hoc command on an agent */
|
|
1706
|
-
cancelAdHocCommand: protectedProcedure.input(
|
|
1707
|
-
agentId:
|
|
1708
|
-
processId:
|
|
1709
|
-
commandId:
|
|
1798
|
+
cancelAdHocCommand: protectedProcedure.input(z7.object({
|
|
1799
|
+
agentId: z7.string().max(128),
|
|
1800
|
+
processId: z7.string().max(128),
|
|
1801
|
+
commandId: z7.string().max(128).optional()
|
|
1710
1802
|
})).mutation(async ({ input }) => {
|
|
1711
1803
|
await agentManager.agentMutation(input.agentId, "process.cancelAdHocCommand", { id: input.processId, commandId: input.commandId });
|
|
1712
1804
|
return { success: true };
|
|
1713
1805
|
}),
|
|
1714
1806
|
/** Get current metrics for a process from an agent */
|
|
1715
|
-
metrics: protectedProcedure.input(
|
|
1807
|
+
metrics: protectedProcedure.input(z7.object({ agentId: z7.string().max(128), processId: z7.string().max(128) })).query(async ({ input }) => {
|
|
1716
1808
|
return agentManager.agentQuery(input.agentId, "process.metrics", { id: input.processId });
|
|
1717
1809
|
}),
|
|
1718
1810
|
/**
|
|
@@ -1720,7 +1812,7 @@ var processRouter = router({
|
|
|
1720
1812
|
* Forwards batched events from the AgentWSBridge in real-time.
|
|
1721
1813
|
* Historical logs are fetched separately via process.logs query.
|
|
1722
1814
|
*/
|
|
1723
|
-
onLog: protectedProcedure.input(
|
|
1815
|
+
onLog: protectedProcedure.input(z7.object({ agentId: z7.string().max(128), processId: z7.string().max(128) })).subscription(async function* ({ input, ctx }) {
|
|
1724
1816
|
requireServiceAction(ctx, input.processId, "logs:read");
|
|
1725
1817
|
yield* forwardLogBatches(input.agentId, input.processId);
|
|
1726
1818
|
}),
|
|
@@ -1729,9 +1821,9 @@ var processRouter = router({
|
|
|
1729
1821
|
* Input is optional — omit agentId/processId for global monitoring (dashboard).
|
|
1730
1822
|
* Forwards events from the AgentWSBridge in real-time.
|
|
1731
1823
|
*/
|
|
1732
|
-
onStatus: protectedProcedure.input(
|
|
1733
|
-
agentId:
|
|
1734
|
-
processId:
|
|
1824
|
+
onStatus: protectedProcedure.input(z7.object({
|
|
1825
|
+
agentId: z7.string().max(128).optional(),
|
|
1826
|
+
processId: z7.string().max(128).optional()
|
|
1735
1827
|
}).optional()).subscription(async function* ({ input, ctx }) {
|
|
1736
1828
|
const allowedIds = getAllowedServiceIds(ctx);
|
|
1737
1829
|
if (input?.processId) requireServiceAction(ctx, input.processId, "service:status");
|
|
@@ -1759,9 +1851,9 @@ var processRouter = router({
|
|
|
1759
1851
|
});
|
|
1760
1852
|
}),
|
|
1761
1853
|
/** Subscribe to real-time metrics for a process. */
|
|
1762
|
-
onMetrics: protectedProcedure.input(
|
|
1763
|
-
agentId:
|
|
1764
|
-
processId:
|
|
1854
|
+
onMetrics: protectedProcedure.input(z7.object({
|
|
1855
|
+
agentId: z7.string().max(128).optional(),
|
|
1856
|
+
processId: z7.string().max(128).optional()
|
|
1765
1857
|
}).optional()).subscription(async function* ({ input, ctx }) {
|
|
1766
1858
|
const allowedIds = getAllowedServiceIds(ctx);
|
|
1767
1859
|
if (input?.processId) requireServiceAction(ctx, input.processId, "service:status");
|
|
@@ -1773,9 +1865,9 @@ var processRouter = router({
|
|
|
1773
1865
|
});
|
|
1774
1866
|
}),
|
|
1775
1867
|
/** Subscribe to real-time health status changes. */
|
|
1776
|
-
onHealth: protectedProcedure.input(
|
|
1777
|
-
agentId:
|
|
1778
|
-
processId:
|
|
1868
|
+
onHealth: protectedProcedure.input(z7.object({
|
|
1869
|
+
agentId: z7.string().max(128).optional(),
|
|
1870
|
+
processId: z7.string().max(128).optional()
|
|
1779
1871
|
}).optional()).subscription(async function* ({ input, ctx }) {
|
|
1780
1872
|
const allowedIds = getAllowedServiceIds(ctx);
|
|
1781
1873
|
if (input?.processId) requireServiceAction(ctx, input.processId, "service:healthcheck");
|
|
@@ -1789,21 +1881,21 @@ var processRouter = router({
|
|
|
1789
1881
|
});
|
|
1790
1882
|
|
|
1791
1883
|
// ../server/src/trpc/routers/audit.ts
|
|
1792
|
-
import { z as
|
|
1884
|
+
import { z as z8 } from "zod";
|
|
1793
1885
|
var auditRouter = router({
|
|
1794
|
-
list: protectedProcedure.input(
|
|
1795
|
-
action:
|
|
1796
|
-
entityType:
|
|
1797
|
-
entityId:
|
|
1798
|
-
limit:
|
|
1799
|
-
offset:
|
|
1886
|
+
list: protectedProcedure.input(z8.object({
|
|
1887
|
+
action: z8.string().max(50).optional(),
|
|
1888
|
+
entityType: z8.string().max(50).optional(),
|
|
1889
|
+
entityId: z8.string().max(100).optional(),
|
|
1890
|
+
limit: z8.number().int().positive().max(100).default(50),
|
|
1891
|
+
offset: z8.number().int().nonnegative().default(0)
|
|
1800
1892
|
}).optional()).query(async ({ input }) => {
|
|
1801
1893
|
return auditService.list(input ?? {});
|
|
1802
1894
|
})
|
|
1803
1895
|
});
|
|
1804
1896
|
|
|
1805
1897
|
// ../server/src/trpc/routers/setup.ts
|
|
1806
|
-
import { z as
|
|
1898
|
+
import { z as z9 } from "zod";
|
|
1807
1899
|
var setupRouter = router({
|
|
1808
1900
|
/**
|
|
1809
1901
|
* Check if encryption key is required (project initialized but no key).
|
|
@@ -1907,9 +1999,9 @@ var setupRouter = router({
|
|
|
1907
1999
|
* une validation de la clé à adopter rendait le parcours impossible : vert
|
|
1908
2000
|
* quand le geste est inutile, rouge sinon.
|
|
1909
2001
|
*/
|
|
1910
|
-
checkOrgKeyLocal: protectedProcedure.input(
|
|
1911
|
-
orgId:
|
|
1912
|
-
keyHex:
|
|
2002
|
+
checkOrgKeyLocal: protectedProcedure.input(z9.object({
|
|
2003
|
+
orgId: z9.string().min(1).max(128),
|
|
2004
|
+
keyHex: z9.string().length(64).regex(/^[0-9a-f]+$/i, "Invalid hex format")
|
|
1913
2005
|
})).query(async ({ input }) => {
|
|
1914
2006
|
const canary = await readVerifyFile(orgVaultRoot(input.orgId));
|
|
1915
2007
|
const locked = isVaultLocked(input.orgId);
|
|
@@ -1926,8 +2018,8 @@ var setupRouter = router({
|
|
|
1926
2018
|
* Spawns the local agent if in local-simple mode.
|
|
1927
2019
|
*/
|
|
1928
2020
|
submitEncryptionKey: publicProcedure.input(
|
|
1929
|
-
|
|
1930
|
-
keyHex:
|
|
2021
|
+
z9.object({
|
|
2022
|
+
keyHex: z9.string().length(64).regex(/^[0-9a-f]+$/i, "Invalid hex format")
|
|
1931
2023
|
})
|
|
1932
2024
|
).mutation(async ({ input }) => {
|
|
1933
2025
|
try {
|
|
@@ -1947,8 +2039,8 @@ var setupRouter = router({
|
|
|
1947
2039
|
|
|
1948
2040
|
// ../server/src/trpc/routers/environment.ts
|
|
1949
2041
|
import { randomUUID } from "crypto";
|
|
1950
|
-
import { z as
|
|
1951
|
-
import { TRPCError as
|
|
2042
|
+
import { z as z10 } from "zod";
|
|
2043
|
+
import { TRPCError as TRPCError7 } from "@trpc/server";
|
|
1952
2044
|
|
|
1953
2045
|
// ../server/src/services/ai-variable-write.ts
|
|
1954
2046
|
var AiVariableWriteError = class extends Error {
|
|
@@ -2027,29 +2119,29 @@ async function builtinsForEnvironment(environment) {
|
|
|
2027
2119
|
return own ? values : Object.fromEntries(Object.entries(values).filter(([name]) => name.startsWith("services.")));
|
|
2028
2120
|
}
|
|
2029
2121
|
var environmentRouter = router({
|
|
2030
|
-
list: protectedProcedure.input(
|
|
2031
|
-
projectId:
|
|
2032
|
-
orgId:
|
|
2122
|
+
list: protectedProcedure.input(z10.object({
|
|
2123
|
+
projectId: z10.string().max(128),
|
|
2124
|
+
orgId: z10.string().max(128).nullable().optional()
|
|
2033
2125
|
})).query(async ({ input }) => {
|
|
2034
2126
|
return await environmentStore.list(input.projectId, input.orgId);
|
|
2035
2127
|
}),
|
|
2036
|
-
get: protectedProcedure.input(
|
|
2128
|
+
get: protectedProcedure.input(z10.object({ id: z10.string().max(128) })).query(async ({ input }) => {
|
|
2037
2129
|
const env2 = await environmentStore.get(input.id);
|
|
2038
|
-
if (!env2) throw new
|
|
2130
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.id} not found` });
|
|
2039
2131
|
return env2;
|
|
2040
2132
|
}),
|
|
2041
|
-
listByScope: protectedProcedure.input(
|
|
2042
|
-
projectId:
|
|
2043
|
-
orgId:
|
|
2044
|
-
scope:
|
|
2133
|
+
listByScope: protectedProcedure.input(z10.object({
|
|
2134
|
+
projectId: z10.string().max(128),
|
|
2135
|
+
orgId: z10.string().max(128).nullable().optional(),
|
|
2136
|
+
scope: z10.enum(["global", "service"])
|
|
2045
2137
|
})).query(async ({ input }) => {
|
|
2046
2138
|
return await environmentStore.listByScope(input.projectId, input.scope, input.orgId);
|
|
2047
2139
|
}),
|
|
2048
|
-
findServiceEnvironment: protectedProcedure.input(
|
|
2140
|
+
findServiceEnvironment: protectedProcedure.input(z10.object({ serviceId: z10.string().max(128), globalEnvId: z10.string().max(128) })).query(async ({ input }) => {
|
|
2049
2141
|
return await environmentStore.findServiceEnvironment(input.serviceId, input.globalEnvId);
|
|
2050
2142
|
}),
|
|
2051
2143
|
create: protectedProcedure.input(CreateEnvironmentSchema.extend({
|
|
2052
|
-
orgId:
|
|
2144
|
+
orgId: z10.string().max(128).nullable().optional()
|
|
2053
2145
|
})).mutation(async ({ input }) => {
|
|
2054
2146
|
const { orgId, ...environment } = input;
|
|
2055
2147
|
const target = { id: input.projectId, orgId };
|
|
@@ -2085,9 +2177,9 @@ var environmentRouter = router({
|
|
|
2085
2177
|
}),
|
|
2086
2178
|
update: protectedProcedure.input(UpdateEnvironmentSchema).mutation(async ({ input }) => {
|
|
2087
2179
|
const existing = await environmentStore.get(input.id);
|
|
2088
|
-
if (!existing) throw new
|
|
2180
|
+
if (!existing) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.id} not found` });
|
|
2089
2181
|
const env2 = await environmentStore.update(input);
|
|
2090
|
-
if (!env2) throw new
|
|
2182
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.id} not found` });
|
|
2091
2183
|
if (env2.scope === "global" && input.name && input.name !== existing.name) {
|
|
2092
2184
|
const project = await projectStore.get(environmentStore.projectRefOf(env2) ?? env2.projectId);
|
|
2093
2185
|
if (project) {
|
|
@@ -2101,24 +2193,24 @@ var environmentRouter = router({
|
|
|
2101
2193
|
}
|
|
2102
2194
|
return env2;
|
|
2103
2195
|
}),
|
|
2104
|
-
delete: protectedProcedure.input(
|
|
2196
|
+
delete: protectedProcedure.input(z10.object({ id: z10.string().max(128) })).mutation(async ({ input }) => {
|
|
2105
2197
|
const env2 = await environmentStore.get(input.id);
|
|
2106
|
-
if (!env2) throw new
|
|
2198
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.id} not found` });
|
|
2107
2199
|
if (env2.immutable) {
|
|
2108
|
-
throw new
|
|
2200
|
+
throw new TRPCError7({ code: "FORBIDDEN", message: "Cannot delete immutable environment" });
|
|
2109
2201
|
}
|
|
2110
2202
|
if (env2.scope === "global") {
|
|
2111
2203
|
await environmentStore.deleteByActivateOn(input.id);
|
|
2112
2204
|
}
|
|
2113
2205
|
const deleted = await environmentStore.delete(input.id);
|
|
2114
|
-
if (!deleted) throw new
|
|
2206
|
+
if (!deleted) throw new TRPCError7({ code: "INTERNAL_SERVER_ERROR", message: "Failed to delete environment" });
|
|
2115
2207
|
await projectStore.removeEnvironment(environmentStore.projectRefOf(env2) ?? env2.projectId, input.id);
|
|
2116
2208
|
return { success: true };
|
|
2117
2209
|
}),
|
|
2118
|
-
resolve: protectedProcedure.input(
|
|
2210
|
+
resolve: protectedProcedure.input(z10.object({ environmentId: z10.string().max(128) })).query(async ({ input, ctx }) => {
|
|
2119
2211
|
const { includeSecrets } = requireEnvAction(ctx, input.environmentId, "env:read");
|
|
2120
2212
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2121
|
-
if (!env2) throw new
|
|
2213
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2122
2214
|
const resolved = await environmentResolver.resolveEnvironmentWithSources(input.environmentId);
|
|
2123
2215
|
if (!includeSecrets) {
|
|
2124
2216
|
const sanitized = { ...resolved };
|
|
@@ -2134,10 +2226,10 @@ var environmentRouter = router({
|
|
|
2134
2226
|
}
|
|
2135
2227
|
return resolved;
|
|
2136
2228
|
}),
|
|
2137
|
-
resolveFlat: protectedProcedure.meta({ openapi: { method: "GET", path: "/environments/{environmentId}/resolve-flat", tags: ["environments"], summary: "Resolve environment variables as a flat key-value map", protect: true } }).input(
|
|
2229
|
+
resolveFlat: protectedProcedure.meta({ openapi: { method: "GET", path: "/environments/{environmentId}/resolve-flat", tags: ["environments"], summary: "Resolve environment variables as a flat key-value map", protect: true } }).input(z10.object({ environmentId: z10.string().max(128) })).output(z10.record(z10.string(), z10.string())).query(async ({ input, ctx }) => {
|
|
2138
2230
|
const { includeSecrets } = requireEnvAction(ctx, input.environmentId, "env:read");
|
|
2139
2231
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2140
|
-
if (!env2) throw new
|
|
2232
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2141
2233
|
const { env: resolved, envSecrets } = await environmentResolver.resolveEnvironment(input.environmentId);
|
|
2142
2234
|
const result = {};
|
|
2143
2235
|
for (const [key, value] of Object.entries(resolved)) {
|
|
@@ -2159,10 +2251,10 @@ var environmentRouter = router({
|
|
|
2159
2251
|
* motif. C'est ce que l'assistant de complétion affiche, et il n'a pas
|
|
2160
2252
|
* besoin de connaître ce qui est déjà rempli ailleurs.
|
|
2161
2253
|
*/
|
|
2162
|
-
missingVariables: protectedProcedure.input(
|
|
2254
|
+
missingVariables: protectedProcedure.input(z10.object({ environmentId: z10.string().max(128) })).query(async ({ input, ctx }) => {
|
|
2163
2255
|
requireEnvAction(ctx, input.environmentId, "env:read");
|
|
2164
2256
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2165
|
-
if (!env2) throw new
|
|
2257
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2166
2258
|
const missing = await environmentResolver.findMissingForActiveEnvironment(input.environmentId);
|
|
2167
2259
|
return missing.filter((item) => canEnvAction(ctx, item.environmentId, "env:read"));
|
|
2168
2260
|
}),
|
|
@@ -2181,17 +2273,17 @@ var environmentRouter = router({
|
|
|
2181
2273
|
* poste, en recopiant depuis la définition la forme (`pre`, `post`) et le
|
|
2182
2274
|
* caractère secret que la surcharge ne porte pas d'elle-même.
|
|
2183
2275
|
*/
|
|
2184
|
-
fillVariable: protectedProcedure.input(
|
|
2185
|
-
environmentId:
|
|
2186
|
-
variableId:
|
|
2187
|
-
value:
|
|
2276
|
+
fillVariable: protectedProcedure.input(z10.object({
|
|
2277
|
+
environmentId: z10.string().max(128),
|
|
2278
|
+
variableId: z10.string().max(128),
|
|
2279
|
+
value: z10.string().max(8192),
|
|
2188
2280
|
reason: MissingVariableReasonSchema
|
|
2189
2281
|
})).mutation(async ({ input, ctx }) => {
|
|
2190
2282
|
requireEnvAction(ctx, input.environmentId, "env:write");
|
|
2191
2283
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2192
|
-
if (!env2) throw new
|
|
2284
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2193
2285
|
const variable = env2.variables[input.variableId];
|
|
2194
|
-
if (!variable) throw new
|
|
2286
|
+
if (!variable) throw new TRPCError7({ code: "NOT_FOUND", message: `Variable ${input.variableId} not found` });
|
|
2195
2287
|
const slot = variable.value;
|
|
2196
2288
|
if (input.reason === "overrideOnly") {
|
|
2197
2289
|
await environmentOverridesStore.setOverride(input.variableId, {
|
|
@@ -2203,11 +2295,11 @@ var environmentRouter = router({
|
|
|
2203
2295
|
...variable,
|
|
2204
2296
|
value: { ...slot, value: input.value }
|
|
2205
2297
|
});
|
|
2206
|
-
if (!result) throw new
|
|
2298
|
+
if (!result) throw new TRPCError7({ code: "INTERNAL_SERVER_ERROR", message: "Failed to fill variable" });
|
|
2207
2299
|
return { success: true };
|
|
2208
2300
|
}),
|
|
2209
|
-
setOverride: protectedProcedure.input(
|
|
2210
|
-
variableId:
|
|
2301
|
+
setOverride: protectedProcedure.input(z10.object({
|
|
2302
|
+
variableId: z10.string().max(128),
|
|
2211
2303
|
// Only the value slot: the env var name comes from the variable
|
|
2212
2304
|
// definition (a `key` sent by an older client is stripped here).
|
|
2213
2305
|
variable: VariableOverrideSchema
|
|
@@ -2215,8 +2307,8 @@ var environmentRouter = router({
|
|
|
2215
2307
|
await environmentOverridesStore.setOverride(input.variableId, input.variable);
|
|
2216
2308
|
return { success: true };
|
|
2217
2309
|
}),
|
|
2218
|
-
removeOverride: protectedProcedure.input(
|
|
2219
|
-
variableId:
|
|
2310
|
+
removeOverride: protectedProcedure.input(z10.object({
|
|
2311
|
+
variableId: z10.string().max(128)
|
|
2220
2312
|
})).mutation(async ({ input }) => {
|
|
2221
2313
|
await environmentOverridesStore.removeOverride(input.variableId);
|
|
2222
2314
|
return { success: true };
|
|
@@ -2224,16 +2316,16 @@ var environmentRouter = router({
|
|
|
2224
2316
|
getOverrides: protectedProcedure.query(async () => {
|
|
2225
2317
|
return await environmentOverridesStore.getAll();
|
|
2226
2318
|
}),
|
|
2227
|
-
upsertVariable: protectedProcedure.meta({ openapi: { method: "PUT", path: "/environments/{environmentId}/variables/{variableId}", tags: ["environments"], summary: "Upsert a variable in an environment", protect: true } }).output(
|
|
2228
|
-
environmentId:
|
|
2229
|
-
variableId:
|
|
2319
|
+
upsertVariable: protectedProcedure.meta({ openapi: { method: "PUT", path: "/environments/{environmentId}/variables/{variableId}", tags: ["environments"], summary: "Upsert a variable in an environment", protect: true } }).output(z10.any()).input(z10.object({
|
|
2320
|
+
environmentId: z10.string().max(128),
|
|
2321
|
+
variableId: z10.string().max(128),
|
|
2230
2322
|
variable: VariableSchema
|
|
2231
2323
|
})).mutation(async ({ input, ctx }) => {
|
|
2232
2324
|
requireEnvAction(ctx, input.environmentId, "env:write");
|
|
2233
2325
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2234
|
-
if (!env2) throw new
|
|
2326
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2235
2327
|
const result = await environmentStore.upsertVariable(input.environmentId, input.variableId, input.variable);
|
|
2236
|
-
if (!result) throw new
|
|
2328
|
+
if (!result) throw new TRPCError7({ code: "INTERNAL_SERVER_ERROR", message: "Failed to update variable" });
|
|
2237
2329
|
return result;
|
|
2238
2330
|
}),
|
|
2239
2331
|
/**
|
|
@@ -2241,15 +2333,15 @@ var environmentRouter = router({
|
|
|
2241
2333
|
* hérite de qui, lequel est actif — sans aucune valeur (`describeEnvironmentsForAi`).
|
|
2242
2334
|
* Une clé d'API n'y voit que les environnements qu'elle peut lire.
|
|
2243
2335
|
*/
|
|
2244
|
-
listForAi: protectedProcedure.input(
|
|
2245
|
-
projectId:
|
|
2246
|
-
orgId:
|
|
2336
|
+
listForAi: protectedProcedure.input(z10.object({
|
|
2337
|
+
projectId: z10.string().max(128),
|
|
2338
|
+
orgId: z10.string().max(128).nullable().optional()
|
|
2247
2339
|
})).query(async ({ input, ctx }) => {
|
|
2248
2340
|
const project = await projectStore.get({
|
|
2249
2341
|
id: input.projectId,
|
|
2250
2342
|
...input.orgId !== void 0 ? { orgId: input.orgId } : {}
|
|
2251
2343
|
});
|
|
2252
|
-
if (!project) throw new
|
|
2344
|
+
if (!project) throw new TRPCError7({ code: "NOT_FOUND", message: `Project ${input.projectId} not found` });
|
|
2253
2345
|
const environments = (await environmentStore.list(input.projectId, input.orgId)).filter((env2) => canEnvAction(ctx, env2.id, "env:read"));
|
|
2254
2346
|
const serviceNames = {};
|
|
2255
2347
|
for (const serviceId of project.serviceIds) {
|
|
@@ -2265,10 +2357,10 @@ var environmentRouter = router({
|
|
|
2265
2357
|
* remplies — elles dépendent du service qui les reçoit — mais une référence
|
|
2266
2358
|
* qui ne correspond à rien est nommée.
|
|
2267
2359
|
*/
|
|
2268
|
-
resolveForAi: protectedProcedure.input(
|
|
2360
|
+
resolveForAi: protectedProcedure.input(z10.object({ environmentId: z10.string().max(128) })).query(async ({ input, ctx }) => {
|
|
2269
2361
|
requireEnvAction(ctx, input.environmentId, "env:read");
|
|
2270
2362
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2271
|
-
if (!env2) throw new
|
|
2363
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2272
2364
|
const [resolved, shared] = await Promise.all([
|
|
2273
2365
|
environmentResolver.resolveEnvironment(input.environmentId),
|
|
2274
2366
|
environmentResolver.resolveAiSharedKeys(input.environmentId)
|
|
@@ -2290,29 +2382,29 @@ var environmentRouter = router({
|
|
|
2290
2382
|
* pas un secret public et ne le partage jamais avec les IA. La réponse ne
|
|
2291
2383
|
* porte aucune valeur : ce qui vient d'être écrit peut être un secret.
|
|
2292
2384
|
*/
|
|
2293
|
-
setVariableBlind: protectedProcedure.input(
|
|
2294
|
-
environmentId:
|
|
2295
|
-
key:
|
|
2296
|
-
value:
|
|
2297
|
-
pre:
|
|
2298
|
-
post:
|
|
2299
|
-
isSecret:
|
|
2300
|
-
description:
|
|
2385
|
+
setVariableBlind: protectedProcedure.input(z10.object({
|
|
2386
|
+
environmentId: z10.string().max(128),
|
|
2387
|
+
key: z10.string().min(1).max(256),
|
|
2388
|
+
value: z10.string().max(8192).optional(),
|
|
2389
|
+
pre: z10.string().max(8192).optional(),
|
|
2390
|
+
post: z10.string().max(8192).optional(),
|
|
2391
|
+
isSecret: z10.boolean().optional(),
|
|
2392
|
+
description: z10.string().max(2e3).optional()
|
|
2301
2393
|
})).mutation(async ({ input, ctx }) => {
|
|
2302
2394
|
requireEnvAction(ctx, input.environmentId, "env:write");
|
|
2303
2395
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2304
|
-
if (!env2) throw new
|
|
2396
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2305
2397
|
const found = Object.entries(env2.variables).find(([, v]) => v.key === input.key);
|
|
2306
2398
|
const variableId = found?.[0] ?? randomUUID();
|
|
2307
2399
|
let variable;
|
|
2308
2400
|
try {
|
|
2309
2401
|
variable = planAiVariableWrite(found?.[1], input);
|
|
2310
2402
|
} catch (err) {
|
|
2311
|
-
if (err instanceof AiVariableWriteError) throw new
|
|
2403
|
+
if (err instanceof AiVariableWriteError) throw new TRPCError7({ code: "FORBIDDEN", message: err.message });
|
|
2312
2404
|
throw err;
|
|
2313
2405
|
}
|
|
2314
2406
|
const result = await environmentStore.upsertVariable(input.environmentId, variableId, variable);
|
|
2315
|
-
if (!result) throw new
|
|
2407
|
+
if (!result) throw new TRPCError7({ code: "INTERNAL_SERVER_ERROR", message: "Failed to write variable" });
|
|
2316
2408
|
const written = `${variable.value.pre}${variable.value.value}${variable.value.post}`;
|
|
2317
2409
|
let suggestions = [];
|
|
2318
2410
|
if (!variable.value.isSecret) {
|
|
@@ -2338,26 +2430,26 @@ var environmentRouter = router({
|
|
|
2338
2430
|
* une route dédiée parce que `update` rend l'environnement entier, valeurs
|
|
2339
2431
|
* comprises.
|
|
2340
2432
|
*/
|
|
2341
|
-
setInheritanceBlind: protectedProcedure.input(
|
|
2433
|
+
setInheritanceBlind: protectedProcedure.input(z10.object({ environmentId: z10.string().max(128), inherit: z10.boolean() })).mutation(async ({ input, ctx }) => {
|
|
2342
2434
|
requireEnvAction(ctx, input.environmentId, "env:write");
|
|
2343
2435
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2344
|
-
if (!env2) throw new
|
|
2436
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2345
2437
|
if (env2.scope !== "service" || !env2.activateOn) {
|
|
2346
|
-
throw new
|
|
2438
|
+
throw new TRPCError7({ code: "BAD_REQUEST", message: `Environment ${input.environmentId} is not a service environment: only those inherit from, or are isolated from, the global environment that activates them` });
|
|
2347
2439
|
}
|
|
2348
2440
|
const updated = await environmentStore.update({ id: env2.id, parentIds: input.inherit ? [env2.activateOn] : [] });
|
|
2349
|
-
if (!updated) throw new
|
|
2441
|
+
if (!updated) throw new TRPCError7({ code: "INTERNAL_SERVER_ERROR", message: "Failed to update inheritance" });
|
|
2350
2442
|
return { environmentId: env2.id, isolated: !input.inherit };
|
|
2351
2443
|
}),
|
|
2352
2444
|
/** Retirer une variable par son nom, pour le compte d'une IA : la réponse ne porte aucune valeur. */
|
|
2353
|
-
removeVariableBlind: protectedProcedure.input(
|
|
2445
|
+
removeVariableBlind: protectedProcedure.input(z10.object({ environmentId: z10.string().max(128), key: z10.string().min(1).max(256) })).mutation(async ({ input, ctx }) => {
|
|
2354
2446
|
requireEnvAction(ctx, input.environmentId, "env:write");
|
|
2355
2447
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2356
|
-
if (!env2) throw new
|
|
2448
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2357
2449
|
const found = Object.entries(env2.variables).find(([, v]) => v.key === input.key);
|
|
2358
|
-
if (!found) throw new
|
|
2450
|
+
if (!found) throw new TRPCError7({ code: "NOT_FOUND", message: `Variable ${input.key} not found in environment ${input.environmentId}` });
|
|
2359
2451
|
const result = await environmentStore.removeVariable(input.environmentId, found[0]);
|
|
2360
|
-
if (!result) throw new
|
|
2452
|
+
if (!result) throw new TRPCError7({ code: "INTERNAL_SERVER_ERROR", message: "Failed to remove variable" });
|
|
2361
2453
|
return { environmentId: input.environmentId, key: input.key, removed: true };
|
|
2362
2454
|
}),
|
|
2363
2455
|
/**
|
|
@@ -2365,12 +2457,12 @@ var environmentRouter = router({
|
|
|
2365
2457
|
* define it, and variables that reference it through {{environment.KEY}}.
|
|
2366
2458
|
* Used by the UI to warn before editing a shared variable from a resolved view.
|
|
2367
2459
|
*/
|
|
2368
|
-
keyUsages: protectedProcedure.input(
|
|
2369
|
-
projectId:
|
|
2370
|
-
orgId:
|
|
2371
|
-
key:
|
|
2460
|
+
keyUsages: protectedProcedure.input(z10.object({
|
|
2461
|
+
projectId: z10.string().max(128),
|
|
2462
|
+
orgId: z10.string().max(128).nullable().optional(),
|
|
2463
|
+
key: z10.string().min(1).max(256),
|
|
2372
2464
|
/** Environment holding the variable about to be edited — enables inherited-impact detection */
|
|
2373
|
-
environmentId:
|
|
2465
|
+
environmentId: z10.string().max(128).optional()
|
|
2374
2466
|
})).query(async ({ input }) => {
|
|
2375
2467
|
const environments = await environmentStore.list(input.projectId, input.orgId);
|
|
2376
2468
|
const services = await serviceStore.list();
|
|
@@ -2427,22 +2519,22 @@ var environmentRouter = router({
|
|
|
2427
2519
|
}
|
|
2428
2520
|
return { definedIn, referencedBy, inheritedBy };
|
|
2429
2521
|
}),
|
|
2430
|
-
removeVariable: protectedProcedure.input(
|
|
2431
|
-
environmentId:
|
|
2432
|
-
variableId:
|
|
2522
|
+
removeVariable: protectedProcedure.input(z10.object({
|
|
2523
|
+
environmentId: z10.string().max(128),
|
|
2524
|
+
variableId: z10.string().max(128)
|
|
2433
2525
|
})).mutation(async ({ input }) => {
|
|
2434
2526
|
const env2 = await environmentStore.get(input.environmentId);
|
|
2435
|
-
if (!env2) throw new
|
|
2527
|
+
if (!env2) throw new TRPCError7({ code: "NOT_FOUND", message: `Environment ${input.environmentId} not found` });
|
|
2436
2528
|
const result = await environmentStore.removeVariable(input.environmentId, input.variableId);
|
|
2437
|
-
if (!result) throw new
|
|
2529
|
+
if (!result) throw new TRPCError7({ code: "INTERNAL_SERVER_ERROR", message: "Failed to remove variable" });
|
|
2438
2530
|
return result;
|
|
2439
2531
|
})
|
|
2440
2532
|
});
|
|
2441
2533
|
|
|
2442
2534
|
// ../server/src/trpc/routers/settings.ts
|
|
2443
2535
|
import { promises as dns } from "dns";
|
|
2444
|
-
import { z as
|
|
2445
|
-
import { TRPCError as
|
|
2536
|
+
import { z as z11 } from "zod";
|
|
2537
|
+
import { TRPCError as TRPCError8 } from "@trpc/server";
|
|
2446
2538
|
var NotificationPreferencesPatchSchema = NotificationPreferencesSchema.partial().strict();
|
|
2447
2539
|
function listWithOrigin(parsers, vault) {
|
|
2448
2540
|
return [
|
|
@@ -2455,8 +2547,8 @@ function maskApiKey2(plaintext) {
|
|
|
2455
2547
|
return plaintext.slice(0, 4) + "****" + plaintext.slice(-4);
|
|
2456
2548
|
}
|
|
2457
2549
|
var ScopeShape = {
|
|
2458
|
-
orgId:
|
|
2459
|
-
projectId:
|
|
2550
|
+
orgId: z11.string().max(128).nullable().optional(),
|
|
2551
|
+
projectId: z11.string().max(128).optional()
|
|
2460
2552
|
};
|
|
2461
2553
|
async function scopeOf(input) {
|
|
2462
2554
|
return input?.orgId ?? await vaultOfProject(input?.projectId);
|
|
@@ -2499,11 +2591,11 @@ var settingsRouter = router({
|
|
|
2499
2591
|
* celle de l'onglet qui bascule, ne sont pas coupées. Le choix est mémorisé
|
|
2500
2592
|
* dans les réglages, donc conservé au prochain démarrage.
|
|
2501
2593
|
*/
|
|
2502
|
-
setLanMode: protectedProcedure.input(
|
|
2594
|
+
setLanMode: protectedProcedure.input(z11.object({ enabled: z11.boolean() })).mutation(async ({ input }) => {
|
|
2503
2595
|
try {
|
|
2504
2596
|
return await setLanMode(input.enabled);
|
|
2505
2597
|
} catch (err) {
|
|
2506
|
-
throw new
|
|
2598
|
+
throw new TRPCError8({
|
|
2507
2599
|
code: "INTERNAL_SERVER_ERROR",
|
|
2508
2600
|
message: `Impossible de changer l'adresse d'\xE9coute : ${err.message}`
|
|
2509
2601
|
});
|
|
@@ -2524,7 +2616,7 @@ var settingsRouter = router({
|
|
|
2524
2616
|
/**
|
|
2525
2617
|
* Get notification preferences
|
|
2526
2618
|
*/
|
|
2527
|
-
getNotificationPreferences: protectedProcedure.input(
|
|
2619
|
+
getNotificationPreferences: protectedProcedure.input(z11.object(ScopeShape).optional()).query(async ({ input }) => {
|
|
2528
2620
|
const priv = await settingsManager.getPrivateSettings(await scopeOf(input));
|
|
2529
2621
|
return NotificationPreferencesSchema.parse(priv.notifications ?? {});
|
|
2530
2622
|
}),
|
|
@@ -2552,7 +2644,7 @@ var settingsRouter = router({
|
|
|
2552
2644
|
* parseur d'organisation se modifie pour toute l'équipe, et l'interface doit
|
|
2553
2645
|
* pouvoir le dire avant que quelqu'un le change sans le savoir.
|
|
2554
2646
|
*/
|
|
2555
|
-
listParsers: protectedProcedure.input(
|
|
2647
|
+
listParsers: protectedProcedure.input(z11.object(ScopeShape).optional()).query(async ({ input }) => {
|
|
2556
2648
|
const vault = await scopeOf(input);
|
|
2557
2649
|
return listWithOrigin(await logParserStore.list(vault), vault);
|
|
2558
2650
|
}),
|
|
@@ -2564,7 +2656,7 @@ var settingsRouter = router({
|
|
|
2564
2656
|
* retard — affichant une liste dans laquelle le parseur qu'un service vient
|
|
2565
2657
|
* de référencer n'existe pas encore.
|
|
2566
2658
|
*/
|
|
2567
|
-
onParsersChanged: protectedProcedure.input(
|
|
2659
|
+
onParsersChanged: protectedProcedure.input(z11.object(ScopeShape).optional()).subscription(async function* ({ input, signal }) {
|
|
2568
2660
|
const vault = await scopeOf(input);
|
|
2569
2661
|
let pending = true;
|
|
2570
2662
|
let wake = null;
|
|
@@ -2602,10 +2694,10 @@ var settingsRouter = router({
|
|
|
2602
2694
|
/**
|
|
2603
2695
|
* Get single parser by ID
|
|
2604
2696
|
*/
|
|
2605
|
-
getParser: protectedProcedure.input(
|
|
2697
|
+
getParser: protectedProcedure.input(z11.object({ id: z11.string().max(128), ...ScopeShape })).query(async ({ input }) => {
|
|
2606
2698
|
const parser = await logParserStore.get(input.id, await scopeOf(input));
|
|
2607
2699
|
if (!parser) {
|
|
2608
|
-
throw new
|
|
2700
|
+
throw new TRPCError8({ code: "NOT_FOUND", message: `Parser not found: ${input.id}` });
|
|
2609
2701
|
}
|
|
2610
2702
|
return parser;
|
|
2611
2703
|
}),
|
|
@@ -2621,7 +2713,7 @@ var settingsRouter = router({
|
|
|
2621
2713
|
).mutation(async ({ input }) => {
|
|
2622
2714
|
const { orgId, projectId, ...fields } = input;
|
|
2623
2715
|
if (!fields.code.trim()) {
|
|
2624
|
-
throw new
|
|
2716
|
+
throw new TRPCError8({ code: "BAD_REQUEST", message: "Parser code cannot be empty" });
|
|
2625
2717
|
}
|
|
2626
2718
|
return logParserStore.create(fields, await scopeOf({ orgId, projectId }));
|
|
2627
2719
|
}),
|
|
@@ -2636,27 +2728,27 @@ var settingsRouter = router({
|
|
|
2636
2728
|
).mutation(async ({ input }) => {
|
|
2637
2729
|
const { id, orgId, projectId, ...patch } = input;
|
|
2638
2730
|
if (id.startsWith("native:")) {
|
|
2639
|
-
throw new
|
|
2731
|
+
throw new TRPCError8({ code: "FORBIDDEN", message: "Cannot modify built-in parsers" });
|
|
2640
2732
|
}
|
|
2641
2733
|
if (patch.code !== void 0 && !patch.code.trim()) {
|
|
2642
|
-
throw new
|
|
2734
|
+
throw new TRPCError8({ code: "BAD_REQUEST", message: "Parser code cannot be empty" });
|
|
2643
2735
|
}
|
|
2644
2736
|
const updated = await logParserStore.update(id, patch, await scopeOf({ orgId, projectId }));
|
|
2645
2737
|
if (!updated) {
|
|
2646
|
-
throw new
|
|
2738
|
+
throw new TRPCError8({ code: "NOT_FOUND", message: `Parser not found: ${id}` });
|
|
2647
2739
|
}
|
|
2648
2740
|
return updated;
|
|
2649
2741
|
}),
|
|
2650
2742
|
/**
|
|
2651
2743
|
* Delete parser by ID
|
|
2652
2744
|
*/
|
|
2653
|
-
deleteParser: protectedProcedure.input(
|
|
2745
|
+
deleteParser: protectedProcedure.input(z11.object({ id: z11.string().max(128), ...ScopeShape })).mutation(async ({ input }) => {
|
|
2654
2746
|
if (input.id.startsWith("native:")) {
|
|
2655
|
-
throw new
|
|
2747
|
+
throw new TRPCError8({ code: "FORBIDDEN", message: "Cannot delete built-in parsers" });
|
|
2656
2748
|
}
|
|
2657
2749
|
const deleted = await logParserStore.delete(input.id, await scopeOf(input));
|
|
2658
2750
|
if (!deleted) {
|
|
2659
|
-
throw new
|
|
2751
|
+
throw new TRPCError8({ code: "NOT_FOUND", message: `Parser not found: ${input.id}` });
|
|
2660
2752
|
}
|
|
2661
2753
|
return { deleted: true };
|
|
2662
2754
|
}),
|
|
@@ -2688,11 +2780,11 @@ var settingsRouter = router({
|
|
|
2688
2780
|
defaultPrompt: companionDefaultPrompt()
|
|
2689
2781
|
};
|
|
2690
2782
|
}),
|
|
2691
|
-
setCompanionSettings: protectedProcedure.input(
|
|
2692
|
-
enabledByDefault:
|
|
2783
|
+
setCompanionSettings: protectedProcedure.input(z11.object({
|
|
2784
|
+
enabledByDefault: z11.boolean().optional(),
|
|
2693
2785
|
// Rejeté au-delà, jamais tronqué : un prompt coupé produirait des
|
|
2694
2786
|
// relectures incohérentes sans rien signaler.
|
|
2695
|
-
prompt:
|
|
2787
|
+
prompt: z11.string().max(2e4).optional()
|
|
2696
2788
|
})).mutation(async ({ input }) => {
|
|
2697
2789
|
const settings = await settingsManager.getSettings();
|
|
2698
2790
|
const current2 = settings.companion ?? { enabledByDefault: false };
|
|
@@ -2711,7 +2803,7 @@ var settingsRouter = router({
|
|
|
2711
2803
|
/**
|
|
2712
2804
|
* List AI providers (API keys are masked)
|
|
2713
2805
|
*/
|
|
2714
|
-
listAIProviders: protectedProcedure.input(
|
|
2806
|
+
listAIProviders: protectedProcedure.input(z11.object(ScopeShape).optional()).query(async ({ input }) => {
|
|
2715
2807
|
const vault = await scopeOf(input);
|
|
2716
2808
|
const priv = await settingsManager.getPrivateSettings(vault);
|
|
2717
2809
|
return (priv.aiProviders ?? []).map((p) => {
|
|
@@ -2729,16 +2821,16 @@ var settingsRouter = router({
|
|
|
2729
2821
|
/**
|
|
2730
2822
|
* Create AI provider
|
|
2731
2823
|
*/
|
|
2732
|
-
createAIProvider: protectedProcedure.input(
|
|
2733
|
-
name:
|
|
2824
|
+
createAIProvider: protectedProcedure.input(z11.object({
|
|
2825
|
+
name: z11.string().min(1).max(200),
|
|
2734
2826
|
provider: AIProviderSchema.shape.provider,
|
|
2735
|
-
apiKey:
|
|
2827
|
+
apiKey: z11.string().max(500).optional(),
|
|
2736
2828
|
// optional for providers like Ollama
|
|
2737
|
-
model:
|
|
2829
|
+
model: z11.string().max(200).optional(),
|
|
2738
2830
|
...ScopeShape
|
|
2739
2831
|
})).mutation(async ({ input }) => {
|
|
2740
2832
|
if (input.provider !== "ollama" && !input.apiKey?.trim()) {
|
|
2741
|
-
throw new
|
|
2833
|
+
throw new TRPCError8({ code: "BAD_REQUEST", message: "Une cl\xE9 API est requise pour ce provider." });
|
|
2742
2834
|
}
|
|
2743
2835
|
const vault = await scopeOf(input);
|
|
2744
2836
|
const priv = await settingsManager.getPrivateSettings(vault);
|
|
@@ -2767,19 +2859,19 @@ var settingsRouter = router({
|
|
|
2767
2859
|
/**
|
|
2768
2860
|
* Update AI provider (apiKey optional — omit to keep existing)
|
|
2769
2861
|
*/
|
|
2770
|
-
updateAIProvider: protectedProcedure.input(
|
|
2771
|
-
id:
|
|
2772
|
-
name:
|
|
2862
|
+
updateAIProvider: protectedProcedure.input(z11.object({
|
|
2863
|
+
id: z11.string().min(1).max(128),
|
|
2864
|
+
name: z11.string().min(1).max(200).optional(),
|
|
2773
2865
|
provider: AIProviderSchema.shape.provider.optional(),
|
|
2774
|
-
apiKey:
|
|
2775
|
-
model:
|
|
2866
|
+
apiKey: z11.string().min(1).max(500).optional(),
|
|
2867
|
+
model: z11.string().max(200).optional(),
|
|
2776
2868
|
...ScopeShape
|
|
2777
2869
|
})).mutation(async ({ input }) => {
|
|
2778
2870
|
const vault = await scopeOf(input);
|
|
2779
2871
|
const priv = await settingsManager.getPrivateSettings(vault);
|
|
2780
2872
|
const providers = priv.aiProviders ?? [];
|
|
2781
2873
|
const idx = providers.findIndex((p) => p.id === input.id);
|
|
2782
|
-
if (idx === -1) throw new
|
|
2874
|
+
if (idx === -1) throw new TRPCError8({ code: "NOT_FOUND", message: `AI provider not found: ${input.id}` });
|
|
2783
2875
|
const existing = providers[idx];
|
|
2784
2876
|
const encryptedApiKey = input.apiKey ? stableEncryptValue(input.apiKey, existing.encryptedApiKey, vault) : existing.encryptedApiKey;
|
|
2785
2877
|
const updated = {
|
|
@@ -2805,12 +2897,12 @@ var settingsRouter = router({
|
|
|
2805
2897
|
/**
|
|
2806
2898
|
* Delete AI provider
|
|
2807
2899
|
*/
|
|
2808
|
-
deleteAIProvider: protectedProcedure.input(
|
|
2900
|
+
deleteAIProvider: protectedProcedure.input(z11.object({ id: z11.string().min(1).max(128), ...ScopeShape })).mutation(async ({ input }) => {
|
|
2809
2901
|
const vault = await scopeOf(input);
|
|
2810
2902
|
const priv = await settingsManager.getPrivateSettings(vault);
|
|
2811
2903
|
const providers = priv.aiProviders ?? [];
|
|
2812
2904
|
const filtered = providers.filter((p) => p.id !== input.id);
|
|
2813
|
-
if (filtered.length === providers.length) throw new
|
|
2905
|
+
if (filtered.length === providers.length) throw new TRPCError8({ code: "NOT_FOUND", message: `AI provider not found: ${input.id}` });
|
|
2814
2906
|
const deletedWasActive = providers.find((p) => p.id === input.id)?.active ?? false;
|
|
2815
2907
|
if (deletedWasActive && filtered.length > 0) filtered[0].active = true;
|
|
2816
2908
|
await settingsManager.savePrivateSettings({ ...priv, aiProviders: filtered }, vault);
|
|
@@ -2819,12 +2911,12 @@ var settingsRouter = router({
|
|
|
2819
2911
|
/**
|
|
2820
2912
|
* Set one provider as active (clears active flag on all others)
|
|
2821
2913
|
*/
|
|
2822
|
-
setActiveAIProvider: protectedProcedure.input(
|
|
2914
|
+
setActiveAIProvider: protectedProcedure.input(z11.object({ id: z11.string().min(1).max(128), ...ScopeShape })).mutation(async ({ input }) => {
|
|
2823
2915
|
const vault = await scopeOf(input);
|
|
2824
2916
|
const priv = await settingsManager.getPrivateSettings(vault);
|
|
2825
2917
|
const providers = priv.aiProviders ?? [];
|
|
2826
2918
|
const idx = providers.findIndex((p) => p.id === input.id);
|
|
2827
|
-
if (idx === -1) throw new
|
|
2919
|
+
if (idx === -1) throw new TRPCError8({ code: "NOT_FOUND", message: `AI provider not found: ${input.id}` });
|
|
2828
2920
|
const updated = providers.map((p) => ({ ...p, active: p.id === input.id }));
|
|
2829
2921
|
await settingsManager.savePrivateSettings({ ...priv, aiProviders: updated }, vault);
|
|
2830
2922
|
return { id: input.id, active: true };
|
|
@@ -2835,7 +2927,7 @@ var settingsRouter = router({
|
|
|
2835
2927
|
/**
|
|
2836
2928
|
* Get Traefik config for a specific agent (never returns credentials or cert key in plain text)
|
|
2837
2929
|
*/
|
|
2838
|
-
getAgentTraefikConfig: protectedProcedure.input(
|
|
2930
|
+
getAgentTraefikConfig: protectedProcedure.input(z11.object({ agentId: z11.string().max(128) })).query(async ({ input }) => {
|
|
2839
2931
|
const raw = agentStore.getTraefikConfig(input.agentId);
|
|
2840
2932
|
const parsed = raw ? TraefikConfigSchema.safeParse(raw) : null;
|
|
2841
2933
|
const config = parsed?.success ? parsed.data : null;
|
|
@@ -2855,16 +2947,16 @@ var settingsRouter = router({
|
|
|
2855
2947
|
/**
|
|
2856
2948
|
* Update Traefik config for a specific agent (credentials are encrypted before saving)
|
|
2857
2949
|
*/
|
|
2858
|
-
updateAgentTraefikConfig: protectedProcedure.input(
|
|
2859
|
-
agentId:
|
|
2860
|
-
enabled:
|
|
2861
|
-
acmeEmail:
|
|
2862
|
-
wildcardDomain:
|
|
2863
|
-
httpPort:
|
|
2864
|
-
httpsPort:
|
|
2865
|
-
dnsProvider:
|
|
2866
|
-
dnsCredentials:
|
|
2867
|
-
acmeStaging:
|
|
2950
|
+
updateAgentTraefikConfig: protectedProcedure.input(z11.object({
|
|
2951
|
+
agentId: z11.string().max(128),
|
|
2952
|
+
enabled: z11.boolean().optional(),
|
|
2953
|
+
acmeEmail: z11.string().email().max(256).optional(),
|
|
2954
|
+
wildcardDomain: z11.string().max(253).optional(),
|
|
2955
|
+
httpPort: z11.number().int().min(1).max(65535).optional(),
|
|
2956
|
+
httpsPort: z11.number().int().min(1).max(65535).optional(),
|
|
2957
|
+
dnsProvider: z11.string().max(64).optional(),
|
|
2958
|
+
dnsCredentials: z11.record(z11.string().max(64), z11.string().max(2048)).optional(),
|
|
2959
|
+
acmeStaging: z11.boolean().optional()
|
|
2868
2960
|
})).mutation(async ({ input }) => {
|
|
2869
2961
|
const { agentId, ...fields } = input;
|
|
2870
2962
|
const raw = agentStore.getTraefikConfig(agentId);
|
|
@@ -2893,13 +2985,13 @@ var settingsRouter = router({
|
|
|
2893
2985
|
/**
|
|
2894
2986
|
* Get Traefik config files content for a specific agent
|
|
2895
2987
|
*/
|
|
2896
|
-
getAgentTraefikConfigFiles: protectedProcedure.input(
|
|
2988
|
+
getAgentTraefikConfigFiles: protectedProcedure.input(z11.object({ agentId: z11.string().max(128) })).query(async ({ input }) => {
|
|
2897
2989
|
return agentManager.agentQuery(
|
|
2898
2990
|
input.agentId,
|
|
2899
2991
|
"config.getTraefikConfigFiles"
|
|
2900
2992
|
);
|
|
2901
2993
|
}),
|
|
2902
|
-
getAgentTraefikFileTree: protectedProcedure.input(
|
|
2994
|
+
getAgentTraefikFileTree: protectedProcedure.input(z11.object({ agentId: z11.string().max(128) })).query(async ({ input }) => {
|
|
2903
2995
|
return agentManager.agentQuery(
|
|
2904
2996
|
input.agentId,
|
|
2905
2997
|
"config.getTraefikFileTree"
|
|
@@ -2908,7 +3000,7 @@ var settingsRouter = router({
|
|
|
2908
3000
|
/**
|
|
2909
3001
|
* Get Traefik container logs for a specific agent (snapshot)
|
|
2910
3002
|
*/
|
|
2911
|
-
getAgentTraefikLogs: protectedProcedure.input(
|
|
3003
|
+
getAgentTraefikLogs: protectedProcedure.input(z11.object({ agentId: z11.string().max(128), lines: z11.number().int().min(1).max(500).default(100) })).query(async ({ input }) => {
|
|
2912
3004
|
const result = await agentManager.agentQuery(
|
|
2913
3005
|
input.agentId,
|
|
2914
3006
|
"config.getTraefikLogs",
|
|
@@ -2919,7 +3011,7 @@ var settingsRouter = router({
|
|
|
2919
3011
|
/**
|
|
2920
3012
|
* Stream Traefik logs in real-time (subscription, yields new lines every 500ms)
|
|
2921
3013
|
*/
|
|
2922
|
-
streamAgentTraefikLogs: protectedProcedure.input(
|
|
3014
|
+
streamAgentTraefikLogs: protectedProcedure.input(z11.object({ agentId: z11.string().max(128) })).subscription(async function* ({ input, signal }) {
|
|
2923
3015
|
let seen = 0;
|
|
2924
3016
|
while (!signal?.aborted) {
|
|
2925
3017
|
try {
|
|
@@ -2942,11 +3034,11 @@ var settingsRouter = router({
|
|
|
2942
3034
|
/**
|
|
2943
3035
|
* Restart Traefik container on a specific agent (re-pushes config first to handle agent restarts)
|
|
2944
3036
|
*/
|
|
2945
|
-
restartAgentTraefik: protectedProcedure.input(
|
|
3037
|
+
restartAgentTraefik: protectedProcedure.input(z11.object({ agentId: z11.string().max(128) })).mutation(async ({ input }) => {
|
|
2946
3038
|
const raw = agentStore.getTraefikConfig(input.agentId);
|
|
2947
3039
|
const parsed = raw ? TraefikConfigSchema.safeParse(raw) : null;
|
|
2948
3040
|
const config = parsed?.success ? parsed.data : null;
|
|
2949
|
-
if (!config?.enabled) throw new
|
|
3041
|
+
if (!config?.enabled) throw new TRPCError8({ code: "BAD_REQUEST", message: "Traefik non activ\xE9 sur cet agent" });
|
|
2950
3042
|
const decrypted = { ...config, dnsCredentials: traefikManager.decryptDnsCredentials(config.dnsCredentials ?? {}) };
|
|
2951
3043
|
await agentManager.agentMutation(input.agentId, "config.initTraefikConfig", decrypted, 5e3);
|
|
2952
3044
|
await agentManager.agentMutation(input.agentId, "config.restartTraefik", void 0, 3e4);
|
|
@@ -2955,11 +3047,11 @@ var settingsRouter = router({
|
|
|
2955
3047
|
/**
|
|
2956
3048
|
* Force certificate renewal for a specific agent (wipes acme.json + restarts container)
|
|
2957
3049
|
*/
|
|
2958
|
-
renewAgentTraefikCert: protectedProcedure.input(
|
|
3050
|
+
renewAgentTraefikCert: protectedProcedure.input(z11.object({ agentId: z11.string().max(128) })).mutation(async ({ input }) => {
|
|
2959
3051
|
const raw = agentStore.getTraefikConfig(input.agentId);
|
|
2960
3052
|
const parsed = raw ? TraefikConfigSchema.safeParse(raw) : null;
|
|
2961
3053
|
const config = parsed?.success ? parsed.data : null;
|
|
2962
|
-
if (!config?.enabled) throw new
|
|
3054
|
+
if (!config?.enabled) throw new TRPCError8({ code: "BAD_REQUEST", message: "Traefik non activ\xE9 sur cet agent" });
|
|
2963
3055
|
const decrypted = { ...config, dnsCredentials: traefikManager.decryptDnsCredentials(config.dnsCredentials ?? {}) };
|
|
2964
3056
|
await agentManager.agentMutation(input.agentId, "config.initTraefikConfig", decrypted, 5e3);
|
|
2965
3057
|
await agentManager.agentMutation(input.agentId, "config.renewTraefikCert", void 0, 3e4);
|
|
@@ -2968,7 +3060,7 @@ var settingsRouter = router({
|
|
|
2968
3060
|
/**
|
|
2969
3061
|
* Validate Traefik configuration for a specific agent
|
|
2970
3062
|
*/
|
|
2971
|
-
validateAgentTraefikConfig: protectedProcedure.input(
|
|
3063
|
+
validateAgentTraefikConfig: protectedProcedure.input(z11.object({ agentId: z11.string().max(128) })).query(async ({ input }) => {
|
|
2972
3064
|
const raw = agentStore.getTraefikConfig(input.agentId);
|
|
2973
3065
|
const parsed = raw ? TraefikConfigSchema.safeParse(raw) : null;
|
|
2974
3066
|
const config = parsed?.success ? parsed.data : null;
|
|
@@ -3070,12 +3162,12 @@ var settingsRouter = router({
|
|
|
3070
3162
|
* Test parser code: compile and run it against sample logs, return per-line results
|
|
3071
3163
|
*/
|
|
3072
3164
|
testParser: protectedProcedure.input(
|
|
3073
|
-
|
|
3074
|
-
code:
|
|
3075
|
-
sampleLogs:
|
|
3076
|
-
|
|
3077
|
-
raw:
|
|
3078
|
-
stream:
|
|
3165
|
+
z11.object({
|
|
3166
|
+
code: z11.string().max(5e4),
|
|
3167
|
+
sampleLogs: z11.array(
|
|
3168
|
+
z11.object({
|
|
3169
|
+
raw: z11.string().max(16384),
|
|
3170
|
+
stream: z11.enum(["stdout", "stderr"])
|
|
3079
3171
|
})
|
|
3080
3172
|
).max(100).default([])
|
|
3081
3173
|
})
|
|
@@ -3091,7 +3183,7 @@ var settingsRouter = router({
|
|
|
3091
3183
|
updatedAt: /* @__PURE__ */ new Date()
|
|
3092
3184
|
}]);
|
|
3093
3185
|
} catch (err) {
|
|
3094
|
-
throw new
|
|
3186
|
+
throw new TRPCError8({
|
|
3095
3187
|
code: "BAD_REQUEST",
|
|
3096
3188
|
message: err instanceof Error ? err.message : String(err)
|
|
3097
3189
|
});
|
|
@@ -3119,8 +3211,8 @@ var settingsRouter = router({
|
|
|
3119
3211
|
});
|
|
3120
3212
|
|
|
3121
3213
|
// ../server/src/trpc/routers/editor.ts
|
|
3122
|
-
import { z as
|
|
3123
|
-
import { TRPCError as
|
|
3214
|
+
import { z as z13 } from "zod";
|
|
3215
|
+
import { TRPCError as TRPCError9 } from "@trpc/server";
|
|
3124
3216
|
import { execFile as execFile3, spawn as spawn2 } from "child_process";
|
|
3125
3217
|
import { stat as stat2 } from "fs/promises";
|
|
3126
3218
|
import { resolve, join as join4 } from "path";
|
|
@@ -3130,19 +3222,19 @@ import { homedir as homedir2 } from "os";
|
|
|
3130
3222
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
3131
3223
|
import { join as join3 } from "path";
|
|
3132
3224
|
import { homedir } from "os";
|
|
3133
|
-
import { z as
|
|
3225
|
+
import { z as z12 } from "zod";
|
|
3134
3226
|
var CONFIG_DIR = join3(homedir(), ".runeya");
|
|
3135
3227
|
var CONFIG_FILE = "runeya.conf";
|
|
3136
|
-
var EditorPreferencesSchema =
|
|
3137
|
-
default:
|
|
3138
|
-
}).catchall(
|
|
3139
|
-
var ProjectSettingsSchema =
|
|
3140
|
-
settings:
|
|
3228
|
+
var EditorPreferencesSchema = z12.object({
|
|
3229
|
+
default: z12.string().max(64).optional()
|
|
3230
|
+
}).catchall(z12.string().max(64)).refine((obj) => Object.keys(obj).length <= 500, "Too many editor preferences");
|
|
3231
|
+
var ProjectSettingsSchema = z12.object({
|
|
3232
|
+
settings: z12.object({
|
|
3141
3233
|
editors: EditorPreferencesSchema.optional()
|
|
3142
3234
|
}).optional()
|
|
3143
3235
|
});
|
|
3144
|
-
var UserConfigSchema =
|
|
3145
|
-
|
|
3236
|
+
var UserConfigSchema = z12.record(
|
|
3237
|
+
z12.string().max(128),
|
|
3146
3238
|
ProjectSettingsSchema
|
|
3147
3239
|
).refine((obj) => Object.keys(obj).length <= 100, "Too many projects in config");
|
|
3148
3240
|
function configKey(ref) {
|
|
@@ -3213,7 +3305,7 @@ async function getServiceCwd(serviceId) {
|
|
|
3213
3305
|
const services = await serviceStore.list();
|
|
3214
3306
|
const service = services.find((s) => s.id === serviceId);
|
|
3215
3307
|
if (!service) {
|
|
3216
|
-
throw new
|
|
3308
|
+
throw new TRPCError9({ code: "NOT_FOUND", message: "Service not found" });
|
|
3217
3309
|
}
|
|
3218
3310
|
return resolveCwd(await resolveWorkspaceCwd({ serviceId }));
|
|
3219
3311
|
}
|
|
@@ -3234,19 +3326,19 @@ var editorRouter = router({
|
|
|
3234
3326
|
);
|
|
3235
3327
|
return results;
|
|
3236
3328
|
}),
|
|
3237
|
-
getPreferences: protectedProcedure.input(
|
|
3238
|
-
projectId:
|
|
3239
|
-
orgId:
|
|
3329
|
+
getPreferences: protectedProcedure.input(z13.object({
|
|
3330
|
+
projectId: z13.string().max(128),
|
|
3331
|
+
orgId: z13.string().max(128).nullable().optional()
|
|
3240
3332
|
})).query(async ({ input }) => {
|
|
3241
3333
|
return userConfigManager.getEditorPreferences({ id: input.projectId, orgId: input.orgId });
|
|
3242
3334
|
}),
|
|
3243
|
-
setDefault: protectedProcedure.input(
|
|
3244
|
-
projectId:
|
|
3245
|
-
orgId:
|
|
3246
|
-
editorKey:
|
|
3335
|
+
setDefault: protectedProcedure.input(z13.object({
|
|
3336
|
+
projectId: z13.string().max(128),
|
|
3337
|
+
orgId: z13.string().max(128).nullable().optional(),
|
|
3338
|
+
editorKey: z13.string().max(64)
|
|
3247
3339
|
})).mutation(async ({ input }) => {
|
|
3248
3340
|
if (!EDITOR_BY_KEY.has(input.editorKey)) {
|
|
3249
|
-
throw new
|
|
3341
|
+
throw new TRPCError9({ code: "BAD_REQUEST", message: `Unknown editor: ${input.editorKey}` });
|
|
3250
3342
|
}
|
|
3251
3343
|
await userConfigManager.setEditorPreference(
|
|
3252
3344
|
{ id: input.projectId, orgId: input.orgId },
|
|
@@ -3255,14 +3347,14 @@ var editorRouter = router({
|
|
|
3255
3347
|
);
|
|
3256
3348
|
return { success: true };
|
|
3257
3349
|
}),
|
|
3258
|
-
setServiceEditor: protectedProcedure.input(
|
|
3259
|
-
projectId:
|
|
3260
|
-
orgId:
|
|
3261
|
-
serviceId:
|
|
3262
|
-
editorKey:
|
|
3350
|
+
setServiceEditor: protectedProcedure.input(z13.object({
|
|
3351
|
+
projectId: z13.string().max(128),
|
|
3352
|
+
orgId: z13.string().max(128).nullable().optional(),
|
|
3353
|
+
serviceId: z13.string().max(128),
|
|
3354
|
+
editorKey: z13.string().max(64)
|
|
3263
3355
|
})).mutation(async ({ input }) => {
|
|
3264
3356
|
if (!EDITOR_BY_KEY.has(input.editorKey)) {
|
|
3265
|
-
throw new
|
|
3357
|
+
throw new TRPCError9({ code: "BAD_REQUEST", message: `Unknown editor: ${input.editorKey}` });
|
|
3266
3358
|
}
|
|
3267
3359
|
await userConfigManager.setEditorPreference(
|
|
3268
3360
|
{ id: input.projectId, orgId: input.orgId },
|
|
@@ -3271,10 +3363,10 @@ var editorRouter = router({
|
|
|
3271
3363
|
);
|
|
3272
3364
|
return { success: true };
|
|
3273
3365
|
}),
|
|
3274
|
-
removeServiceEditor: protectedProcedure.input(
|
|
3275
|
-
projectId:
|
|
3276
|
-
orgId:
|
|
3277
|
-
serviceId:
|
|
3366
|
+
removeServiceEditor: protectedProcedure.input(z13.object({
|
|
3367
|
+
projectId: z13.string().max(128),
|
|
3368
|
+
orgId: z13.string().max(128).nullable().optional(),
|
|
3369
|
+
serviceId: z13.string().max(128)
|
|
3278
3370
|
})).mutation(async ({ input }) => {
|
|
3279
3371
|
await userConfigManager.removeEditorPreference(
|
|
3280
3372
|
{ id: input.projectId, orgId: input.orgId },
|
|
@@ -3282,21 +3374,21 @@ var editorRouter = router({
|
|
|
3282
3374
|
);
|
|
3283
3375
|
return { success: true };
|
|
3284
3376
|
}),
|
|
3285
|
-
open: protectedProcedure.input(
|
|
3286
|
-
editorKey:
|
|
3287
|
-
path:
|
|
3377
|
+
open: protectedProcedure.input(z13.object({
|
|
3378
|
+
editorKey: z13.string().max(64),
|
|
3379
|
+
path: z13.string().max(4096)
|
|
3288
3380
|
})).mutation(async ({ input }) => {
|
|
3289
3381
|
const editor = EDITOR_BY_KEY.get(input.editorKey);
|
|
3290
3382
|
if (!editor) {
|
|
3291
|
-
throw new
|
|
3383
|
+
throw new TRPCError9({ code: "BAD_REQUEST", message: `Unknown editor: ${input.editorKey}` });
|
|
3292
3384
|
}
|
|
3293
3385
|
const expanded = input.path.replace(/^~(\/|$)/, `${process.env["HOME"] ?? "/tmp"}$1`).replace(/\$\{(\w+)\}|\$(\w+)/g, (_, a, b) => process.env[a || b] ?? "");
|
|
3294
3386
|
const resolvedPath = resolve(expanded || ".");
|
|
3295
3387
|
try {
|
|
3296
3388
|
await stat2(resolvedPath);
|
|
3297
3389
|
} catch (err) {
|
|
3298
|
-
if (err instanceof
|
|
3299
|
-
throw new
|
|
3390
|
+
if (err instanceof TRPCError9) throw err;
|
|
3391
|
+
throw new TRPCError9({ code: "BAD_REQUEST", message: `Path does not exist: ${resolvedPath}` });
|
|
3300
3392
|
}
|
|
3301
3393
|
const invocation = resolveCliInvocation(editor.command, [resolvedPath]);
|
|
3302
3394
|
const child = spawn2(invocation.command, invocation.args, {
|
|
@@ -3308,24 +3400,24 @@ var editorRouter = router({
|
|
|
3308
3400
|
child.unref();
|
|
3309
3401
|
return { success: true };
|
|
3310
3402
|
}),
|
|
3311
|
-
openFile: protectedProcedure.input(
|
|
3312
|
-
editorKey:
|
|
3313
|
-
serviceId:
|
|
3314
|
-
relativePath:
|
|
3403
|
+
openFile: protectedProcedure.input(z13.object({
|
|
3404
|
+
editorKey: z13.string().max(64),
|
|
3405
|
+
serviceId: z13.string().max(128),
|
|
3406
|
+
relativePath: z13.string().max(4096)
|
|
3315
3407
|
})).mutation(async ({ input }) => {
|
|
3316
3408
|
const editor = EDITOR_BY_KEY.get(input.editorKey);
|
|
3317
3409
|
if (!editor) {
|
|
3318
|
-
throw new
|
|
3410
|
+
throw new TRPCError9({ code: "BAD_REQUEST", message: `Unknown editor: ${input.editorKey}` });
|
|
3319
3411
|
}
|
|
3320
3412
|
const cwd = await getServiceCwd(input.serviceId);
|
|
3321
3413
|
const filePath = join4(cwd, input.relativePath);
|
|
3322
3414
|
if (!filePath.startsWith(cwd)) {
|
|
3323
|
-
throw new
|
|
3415
|
+
throw new TRPCError9({ code: "BAD_REQUEST", message: "Path traversal not allowed" });
|
|
3324
3416
|
}
|
|
3325
3417
|
try {
|
|
3326
3418
|
await stat2(filePath);
|
|
3327
3419
|
} catch {
|
|
3328
|
-
throw new
|
|
3420
|
+
throw new TRPCError9({ code: "BAD_REQUEST", message: `File does not exist: ${filePath}` });
|
|
3329
3421
|
}
|
|
3330
3422
|
const invocation = resolveCliInvocation(editor.command, [filePath]);
|
|
3331
3423
|
const child = spawn2(invocation.command, invocation.args, {
|
|
@@ -3340,8 +3432,8 @@ var editorRouter = router({
|
|
|
3340
3432
|
});
|
|
3341
3433
|
|
|
3342
3434
|
// ../server/src/trpc/routers/api-keys.ts
|
|
3343
|
-
import { z as
|
|
3344
|
-
import { TRPCError as
|
|
3435
|
+
import { z as z14 } from "zod";
|
|
3436
|
+
import { TRPCError as TRPCError10 } from "@trpc/server";
|
|
3345
3437
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
3346
3438
|
import { join as join5, dirname as dirname3 } from "path";
|
|
3347
3439
|
var ALLOWED_SKILL_PATHS = /* @__PURE__ */ new Set([
|
|
@@ -3349,46 +3441,46 @@ var ALLOWED_SKILL_PATHS = /* @__PURE__ */ new Set([
|
|
|
3349
3441
|
".cursor/rules/runeya.mdc",
|
|
3350
3442
|
".windsurf/rules/runeya.md"
|
|
3351
3443
|
]);
|
|
3352
|
-
var KeyInputSchema =
|
|
3353
|
-
name:
|
|
3444
|
+
var KeyInputSchema = z14.object({
|
|
3445
|
+
name: z14.string().min(1).max(200),
|
|
3354
3446
|
scopes: ApiKeyScopeSchema,
|
|
3355
|
-
isPublic:
|
|
3447
|
+
isPublic: z14.boolean().default(false)
|
|
3356
3448
|
});
|
|
3357
3449
|
var apiKeysRouter = router({
|
|
3358
3450
|
list: protectedProcedure.query(() => apiKeyStore.list()),
|
|
3359
|
-
create: protectedProcedure.input(KeyInputSchema.extend({ orgId:
|
|
3451
|
+
create: protectedProcedure.input(KeyInputSchema.extend({ orgId: z14.string().min(1).max(128).nullable().optional() })).mutation(
|
|
3360
3452
|
({ input }) => apiKeyStore.create(input.name, input.scopes, input.isPublic, input.orgId ?? null)
|
|
3361
3453
|
),
|
|
3362
3454
|
/**
|
|
3363
3455
|
* Renvoie le secret d'une seule clé, sur demande explicite. `list` ne le
|
|
3364
3456
|
* contient pas : une clé ne doit jamais être re-servie en masse.
|
|
3365
3457
|
*/
|
|
3366
|
-
reveal: protectedProcedure.input(
|
|
3458
|
+
reveal: protectedProcedure.input(z14.object({ id: z14.string().max(128) })).query(async ({ input }) => {
|
|
3367
3459
|
const key = await apiKeyStore.reveal(input.id);
|
|
3368
|
-
if (!key) throw new
|
|
3460
|
+
if (!key) throw new TRPCError10({ code: "NOT_FOUND", message: "API key not found" });
|
|
3369
3461
|
return { key };
|
|
3370
3462
|
}),
|
|
3371
|
-
update: protectedProcedure.input(KeyInputSchema.extend({ id:
|
|
3463
|
+
update: protectedProcedure.input(KeyInputSchema.extend({ id: z14.string().max(128) })).mutation(async ({ input }) => {
|
|
3372
3464
|
const updated = await apiKeyStore.update(input.id, input.name, input.scopes, input.isPublic);
|
|
3373
|
-
if (!updated) throw new
|
|
3465
|
+
if (!updated) throw new TRPCError10({ code: "NOT_FOUND", message: "API key not found" });
|
|
3374
3466
|
return maskApiKey(updated);
|
|
3375
3467
|
}),
|
|
3376
|
-
remove: protectedProcedure.input(
|
|
3468
|
+
remove: protectedProcedure.input(z14.object({ id: z14.string().max(128) })).mutation(async ({ input }) => {
|
|
3377
3469
|
const removed = await apiKeyStore.remove(input.id);
|
|
3378
|
-
if (!removed) throw new
|
|
3470
|
+
if (!removed) throw new TRPCError10({ code: "NOT_FOUND", message: "API key not found" });
|
|
3379
3471
|
return { success: true };
|
|
3380
3472
|
}),
|
|
3381
|
-
renewKey: protectedProcedure.input(
|
|
3473
|
+
renewKey: protectedProcedure.input(z14.object({ id: z14.string().max(128) })).mutation(async ({ input }) => {
|
|
3382
3474
|
const renewed = await apiKeyStore.renewKey(input.id);
|
|
3383
|
-
if (!renewed) throw new
|
|
3475
|
+
if (!renewed) throw new TRPCError10({ code: "NOT_FOUND", message: "API key not found" });
|
|
3384
3476
|
return renewed;
|
|
3385
3477
|
}),
|
|
3386
|
-
writeSkillFile: protectedProcedure.input(
|
|
3387
|
-
relativePath:
|
|
3388
|
-
content:
|
|
3478
|
+
writeSkillFile: protectedProcedure.input(z14.object({
|
|
3479
|
+
relativePath: z14.string().max(256),
|
|
3480
|
+
content: z14.string().max(32768)
|
|
3389
3481
|
})).mutation(async ({ input }) => {
|
|
3390
3482
|
if (!ALLOWED_SKILL_PATHS.has(input.relativePath)) {
|
|
3391
|
-
throw new
|
|
3483
|
+
throw new TRPCError10({ code: "BAD_REQUEST", message: "Chemin non autoris\xE9" });
|
|
3392
3484
|
}
|
|
3393
3485
|
const abs = join5(await resolveWorkspaceCwd({}), input.relativePath);
|
|
3394
3486
|
await mkdir3(dirname3(abs), { recursive: true });
|
|
@@ -3398,133 +3490,133 @@ var apiKeysRouter = router({
|
|
|
3398
3490
|
});
|
|
3399
3491
|
|
|
3400
3492
|
// ../server/src/trpc/routers/git.ts
|
|
3401
|
-
import { z as
|
|
3402
|
-
import { TRPCError as
|
|
3493
|
+
import { z as z15 } from "zod";
|
|
3494
|
+
import { TRPCError as TRPCError11 } from "@trpc/server";
|
|
3403
3495
|
import Anthropic from "@anthropic-ai/sdk";
|
|
3404
3496
|
import OpenAI from "openai";
|
|
3405
|
-
var SERVICE_ID_SCHEMA =
|
|
3406
|
-
var SAFE_PATH_SCHEMA =
|
|
3497
|
+
var SERVICE_ID_SCHEMA = z15.string().max(128);
|
|
3498
|
+
var SAFE_PATH_SCHEMA = z15.string().min(1).max(4096).refine((p) => !p.startsWith("/") && !p.includes("../"), {
|
|
3407
3499
|
message: "Path must be relative and not traverse up"
|
|
3408
3500
|
});
|
|
3409
|
-
var REMOTE_SCHEMA =
|
|
3410
|
-
var BRANCH_SCHEMA =
|
|
3501
|
+
var REMOTE_SCHEMA = z15.string().min(1).max(128).regex(/^[a-zA-Z0-9_\-.]+$/);
|
|
3502
|
+
var BRANCH_SCHEMA = z15.string().min(1).max(256).regex(/^[a-zA-Z0-9._\-/]+$/);
|
|
3411
3503
|
async function resolveServiceContext(serviceId) {
|
|
3412
3504
|
const service = await serviceStore.get(serviceId);
|
|
3413
3505
|
if (!service) {
|
|
3414
|
-
throw new
|
|
3506
|
+
throw new TRPCError11({ code: "NOT_FOUND", message: "Service not found" });
|
|
3415
3507
|
}
|
|
3416
3508
|
const agentId = await resolveEffectiveAgentId(service);
|
|
3417
3509
|
if (!agentId) {
|
|
3418
|
-
throw new
|
|
3510
|
+
throw new TRPCError11({ code: "BAD_REQUEST", message: "No agent configured for this service" });
|
|
3419
3511
|
}
|
|
3420
3512
|
return { agentId, cwd: await resolveWorkspaceCwd({ serviceId }) };
|
|
3421
3513
|
}
|
|
3422
3514
|
var gitTargetShape = {
|
|
3423
3515
|
serviceId: SERVICE_ID_SCHEMA.optional(),
|
|
3424
|
-
agentId:
|
|
3516
|
+
agentId: z15.string().max(128).optional(),
|
|
3425
3517
|
/** Anchors on a project when no service is targeted (e.g. from the AI chat). */
|
|
3426
|
-
projectId:
|
|
3518
|
+
projectId: z15.string().max(128).optional(),
|
|
3427
3519
|
/** Son organisation : un id de projet seul peut en nommer un par organisation. */
|
|
3428
|
-
orgId:
|
|
3520
|
+
orgId: z15.string().max(128).nullable().optional()
|
|
3429
3521
|
};
|
|
3430
3522
|
async function resolveGitContext(input) {
|
|
3431
3523
|
if (input.serviceId) return resolveServiceContext(input.serviceId);
|
|
3432
3524
|
const agentId = input.agentId ?? agentManager.getDefaultAgentId();
|
|
3433
3525
|
if (!agentId) {
|
|
3434
|
-
throw new
|
|
3526
|
+
throw new TRPCError11({ code: "BAD_REQUEST", message: "No agent available" });
|
|
3435
3527
|
}
|
|
3436
3528
|
const cwd = await resolveAgentCwd({ projectId: input.projectId, orgId: input.orgId }) ?? ".";
|
|
3437
3529
|
return { agentId, cwd };
|
|
3438
3530
|
}
|
|
3439
3531
|
var gitRouter = router({
|
|
3440
3532
|
/** Get full git status */
|
|
3441
|
-
status: protectedProcedure.input(
|
|
3533
|
+
status: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
|
|
3442
3534
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3443
3535
|
return agentManager.agentQuery(agentId, "git.status", { cwd });
|
|
3444
3536
|
}),
|
|
3445
3537
|
/** Get commit log */
|
|
3446
|
-
log: protectedProcedure.input(
|
|
3538
|
+
log: protectedProcedure.input(z15.object({ ...gitTargetShape, limit: z15.number().int().min(1).max(200).default(50) })).query(async ({ input }) => {
|
|
3447
3539
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3448
3540
|
return agentManager.agentQuery(agentId, "git.log", { cwd, limit: input.limit });
|
|
3449
3541
|
}),
|
|
3450
3542
|
/** List local and remote branches */
|
|
3451
|
-
branches: protectedProcedure.input(
|
|
3543
|
+
branches: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
|
|
3452
3544
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3453
3545
|
return agentManager.agentQuery(agentId, "git.branches", { cwd });
|
|
3454
3546
|
}),
|
|
3455
3547
|
/** Checkout a branch */
|
|
3456
|
-
checkout: protectedProcedure.input(
|
|
3548
|
+
checkout: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, create: z15.boolean().default(false) })).mutation(async ({ input }) => {
|
|
3457
3549
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3458
3550
|
return agentManager.agentMutation(agentId, "git.checkout", { cwd, branch: input.branch, create: input.create });
|
|
3459
3551
|
}),
|
|
3460
3552
|
/** Stage files */
|
|
3461
|
-
stage: protectedProcedure.input(
|
|
3553
|
+
stage: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
|
|
3462
3554
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3463
3555
|
return agentManager.agentMutation(agentId, "git.stage", { cwd, paths: input.paths });
|
|
3464
3556
|
}),
|
|
3465
3557
|
/** Stage all files */
|
|
3466
|
-
stageAll: protectedProcedure.input(
|
|
3558
|
+
stageAll: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
|
|
3467
3559
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3468
3560
|
return agentManager.agentMutation(agentId, "git.stageAll", { cwd });
|
|
3469
3561
|
}),
|
|
3470
3562
|
/** Unstage files */
|
|
3471
|
-
unstage: protectedProcedure.input(
|
|
3563
|
+
unstage: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
|
|
3472
3564
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3473
3565
|
return agentManager.agentMutation(agentId, "git.unstage", { cwd, paths: input.paths });
|
|
3474
3566
|
}),
|
|
3475
3567
|
/** Unstage all files */
|
|
3476
|
-
unstageAll: protectedProcedure.input(
|
|
3568
|
+
unstageAll: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
|
|
3477
3569
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3478
3570
|
return agentManager.agentMutation(agentId, "git.unstageAll", { cwd });
|
|
3479
3571
|
}),
|
|
3480
3572
|
/** Commit staged changes */
|
|
3481
|
-
commit: protectedProcedure.input(
|
|
3573
|
+
commit: protectedProcedure.input(z15.object({ ...gitTargetShape, message: z15.string().min(1).max(4096) })).mutation(async ({ input }) => {
|
|
3482
3574
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3483
3575
|
return agentManager.agentMutation(agentId, "git.commit", { cwd, message: input.message });
|
|
3484
3576
|
}),
|
|
3485
3577
|
/** Push to remote */
|
|
3486
|
-
push: protectedProcedure.input(
|
|
3578
|
+
push: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin"), setUpstream: z15.boolean().default(false) })).mutation(async ({ input }) => {
|
|
3487
3579
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3488
3580
|
return agentManager.agentMutation(agentId, "git.push", { cwd, remote: input.remote, setUpstream: input.setUpstream });
|
|
3489
3581
|
}),
|
|
3490
3582
|
/** Pull from remote */
|
|
3491
|
-
pull: protectedProcedure.input(
|
|
3583
|
+
pull: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
|
|
3492
3584
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3493
3585
|
return agentManager.agentMutation(agentId, "git.pull", { cwd, remote: input.remote });
|
|
3494
3586
|
}),
|
|
3495
3587
|
/** Fetch from remote */
|
|
3496
|
-
fetch: protectedProcedure.input(
|
|
3588
|
+
fetch: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
|
|
3497
3589
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3498
3590
|
return agentManager.agentMutation(agentId, "git.fetch", { cwd, remote: input.remote });
|
|
3499
3591
|
}),
|
|
3500
3592
|
/** Get diff for a file or the whole index */
|
|
3501
|
-
diff: protectedProcedure.input(
|
|
3593
|
+
diff: protectedProcedure.input(z15.object({
|
|
3502
3594
|
...gitTargetShape,
|
|
3503
|
-
path:
|
|
3504
|
-
staged:
|
|
3505
|
-
untracked:
|
|
3595
|
+
path: z15.string().max(4096).optional(),
|
|
3596
|
+
staged: z15.boolean().default(false),
|
|
3597
|
+
untracked: z15.boolean().default(false)
|
|
3506
3598
|
})).query(async ({ input }) => {
|
|
3507
3599
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3508
3600
|
return agentManager.agentQuery(agentId, "git.diff", { cwd, path: input.path, staged: input.staged, untracked: input.untracked });
|
|
3509
3601
|
}),
|
|
3510
3602
|
/** Discard changes to working tree files */
|
|
3511
|
-
discard: protectedProcedure.input(
|
|
3603
|
+
discard: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
|
|
3512
3604
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3513
3605
|
return agentManager.agentMutation(agentId, "git.discard", { cwd, paths: input.paths });
|
|
3514
3606
|
}),
|
|
3515
3607
|
/** Generate a commit message from staged diffs using AI (diff fetched from agent, AI runs on server) */
|
|
3516
|
-
generateCommitMessage: protectedProcedure.input(
|
|
3608
|
+
generateCommitMessage: protectedProcedure.input(z15.object({ ...gitTargetShape, providerId: OptionalProviderIdSchema, model: z15.string().max(200).optional() })).mutation(async ({ input }) => {
|
|
3517
3609
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3518
3610
|
const { diff } = await agentManager.agentQuery(agentId, "git.stagedDiff", { cwd });
|
|
3519
3611
|
if (!diff.trim()) {
|
|
3520
|
-
throw new
|
|
3612
|
+
throw new TRPCError11({ code: "BAD_REQUEST", message: "No staged changes to generate a commit message from" });
|
|
3521
3613
|
}
|
|
3522
3614
|
const active = await getActiveProvider(
|
|
3523
3615
|
input.providerId,
|
|
3524
3616
|
input.orgId ?? await vaultOfProject(input.projectId)
|
|
3525
3617
|
);
|
|
3526
3618
|
if (!active) {
|
|
3527
|
-
throw new
|
|
3619
|
+
throw new TRPCError11({ code: "BAD_REQUEST", message: "No AI provider configured" });
|
|
3528
3620
|
}
|
|
3529
3621
|
const systemPrompt = `You are a git commit message generator. Given a git diff, write a concise and meaningful conventional commit message (e.g. feat:, fix:, refactor:, chore:, docs:, style:, test:).
|
|
3530
3622
|
Rules:
|
|
@@ -3557,45 +3649,45 @@ Rules:
|
|
|
3557
3649
|
});
|
|
3558
3650
|
return { message: res.choices[0]?.message?.content?.trim() ?? "" };
|
|
3559
3651
|
}
|
|
3560
|
-
throw new
|
|
3652
|
+
throw new TRPCError11({ code: "BAD_REQUEST", message: "Unsupported AI provider" });
|
|
3561
3653
|
}),
|
|
3562
3654
|
/** Merge a branch into the current branch */
|
|
3563
|
-
merge: protectedProcedure.input(
|
|
3655
|
+
merge: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, noFf: z15.boolean().default(false) })).mutation(async ({ input }) => {
|
|
3564
3656
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3565
3657
|
return agentManager.agentMutation(agentId, "git.merge", { cwd, branch: input.branch, noFf: input.noFf });
|
|
3566
3658
|
}),
|
|
3567
3659
|
/** Rebase current branch onto another branch */
|
|
3568
|
-
rebase: protectedProcedure.input(
|
|
3660
|
+
rebase: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA })).mutation(async ({ input }) => {
|
|
3569
3661
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3570
3662
|
return agentManager.agentMutation(agentId, "git.rebase", { cwd, branch: input.branch });
|
|
3571
3663
|
}),
|
|
3572
3664
|
/** List stash entries */
|
|
3573
|
-
stashList: protectedProcedure.input(
|
|
3665
|
+
stashList: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
|
|
3574
3666
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3575
3667
|
return agentManager.agentQuery(agentId, "git.stashList", { cwd });
|
|
3576
3668
|
}),
|
|
3577
3669
|
/** Stash current changes */
|
|
3578
|
-
stashPush: protectedProcedure.input(
|
|
3670
|
+
stashPush: protectedProcedure.input(z15.object({ ...gitTargetShape, message: z15.string().max(4096).optional(), includeUntracked: z15.boolean().default(true) })).mutation(async ({ input }) => {
|
|
3579
3671
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3580
3672
|
return agentManager.agentMutation(agentId, "git.stashPush", { cwd, message: input.message, includeUntracked: input.includeUntracked });
|
|
3581
3673
|
}),
|
|
3582
3674
|
/** Pop a stash entry */
|
|
3583
|
-
stashPop: protectedProcedure.input(
|
|
3675
|
+
stashPop: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
|
|
3584
3676
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3585
3677
|
return agentManager.agentMutation(agentId, "git.stashPop", { cwd, index: input.index });
|
|
3586
3678
|
}),
|
|
3587
3679
|
/** Apply a stash entry without dropping it */
|
|
3588
|
-
stashApply: protectedProcedure.input(
|
|
3680
|
+
stashApply: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
|
|
3589
3681
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3590
3682
|
return agentManager.agentMutation(agentId, "git.stashApply", { cwd, index: input.index });
|
|
3591
3683
|
}),
|
|
3592
3684
|
/** Drop a stash entry */
|
|
3593
|
-
stashDrop: protectedProcedure.input(
|
|
3685
|
+
stashDrop: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
|
|
3594
3686
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3595
3687
|
return agentManager.agentMutation(agentId, "git.stashDrop", { cwd, index: input.index });
|
|
3596
3688
|
}),
|
|
3597
3689
|
/** Watch .git/ for changes and stream updated status */
|
|
3598
|
-
onStatusChange: protectedProcedure.input(
|
|
3690
|
+
onStatusChange: protectedProcedure.input(z15.object({ ...gitTargetShape })).subscription(async function* ({ input }) {
|
|
3599
3691
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3600
3692
|
const queue = [];
|
|
3601
3693
|
let resolve4 = null;
|
|
@@ -3622,7 +3714,7 @@ Rules:
|
|
|
3622
3714
|
}
|
|
3623
3715
|
);
|
|
3624
3716
|
} catch (err) {
|
|
3625
|
-
throw new
|
|
3717
|
+
throw new TRPCError11({
|
|
3626
3718
|
code: "BAD_REQUEST",
|
|
3627
3719
|
message: `Cannot subscribe to git status: ${err.message}`
|
|
3628
3720
|
});
|
|
@@ -3642,7 +3734,7 @@ Rules:
|
|
|
3642
3734
|
}
|
|
3643
3735
|
}),
|
|
3644
3736
|
/** List remotes */
|
|
3645
|
-
remotes: protectedProcedure.input(
|
|
3737
|
+
remotes: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
|
|
3646
3738
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
3647
3739
|
return agentManager.agentQuery(agentId, "git.remotes", { cwd });
|
|
3648
3740
|
})
|
|
@@ -3650,41 +3742,41 @@ Rules:
|
|
|
3650
3742
|
|
|
3651
3743
|
// ../server/src/trpc/routers/scenario.ts
|
|
3652
3744
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3653
|
-
import { TRPCError as
|
|
3654
|
-
import { z as
|
|
3745
|
+
import { TRPCError as TRPCError14 } from "@trpc/server";
|
|
3746
|
+
import { z as z16 } from "zod";
|
|
3655
3747
|
|
|
3656
3748
|
// ../server/src/trpc/crud-procedure-utils.ts
|
|
3657
|
-
import { TRPCError as
|
|
3749
|
+
import { TRPCError as TRPCError12 } from "@trpc/server";
|
|
3658
3750
|
async function getOrNotFound(getter, notFoundMessage) {
|
|
3659
3751
|
const entity = await getter();
|
|
3660
|
-
if (!entity) throw new
|
|
3752
|
+
if (!entity) throw new TRPCError12({ code: "NOT_FOUND", message: notFoundMessage });
|
|
3661
3753
|
return entity;
|
|
3662
3754
|
}
|
|
3663
3755
|
async function createOrBadRequest(creator) {
|
|
3664
3756
|
try {
|
|
3665
3757
|
return await creator();
|
|
3666
3758
|
} catch (err) {
|
|
3667
|
-
throw new
|
|
3759
|
+
throw new TRPCError12({ code: "BAD_REQUEST", message: err.message });
|
|
3668
3760
|
}
|
|
3669
3761
|
}
|
|
3670
3762
|
async function updateOrThrow(updater, notFoundMessage) {
|
|
3671
3763
|
try {
|
|
3672
3764
|
const entity = await updater();
|
|
3673
|
-
if (!entity) throw new
|
|
3765
|
+
if (!entity) throw new TRPCError12({ code: "NOT_FOUND", message: notFoundMessage });
|
|
3674
3766
|
return entity;
|
|
3675
3767
|
} catch (err) {
|
|
3676
|
-
if (err instanceof
|
|
3677
|
-
throw new
|
|
3768
|
+
if (err instanceof TRPCError12) throw err;
|
|
3769
|
+
throw new TRPCError12({ code: "BAD_REQUEST", message: err.message });
|
|
3678
3770
|
}
|
|
3679
3771
|
}
|
|
3680
3772
|
async function deleteOrNotFound(deleter, notFoundMessage) {
|
|
3681
3773
|
const deleted = await deleter();
|
|
3682
|
-
if (!deleted) throw new
|
|
3774
|
+
if (!deleted) throw new TRPCError12({ code: "NOT_FOUND", message: notFoundMessage });
|
|
3683
3775
|
return { ok: true };
|
|
3684
3776
|
}
|
|
3685
3777
|
|
|
3686
3778
|
// ../server/src/trpc/ai-generation-procedure-utils.ts
|
|
3687
|
-
import { TRPCError as
|
|
3779
|
+
import { TRPCError as TRPCError13 } from "@trpc/server";
|
|
3688
3780
|
|
|
3689
3781
|
// ../server/src/services/ai-generation.ts
|
|
3690
3782
|
import Anthropic2 from "@anthropic-ai/sdk";
|
|
@@ -3740,7 +3832,7 @@ async function* streamProviderText(input) {
|
|
|
3740
3832
|
// ../server/src/trpc/ai-generation-procedure-utils.ts
|
|
3741
3833
|
async function* streamWithActiveProvider(opts) {
|
|
3742
3834
|
const active = await getActiveProvider(opts.providerId, opts.vault);
|
|
3743
|
-
if (!active) throw new
|
|
3835
|
+
if (!active) throw new TRPCError13({ code: "BAD_REQUEST", message: "Aucun fournisseur IA configur\xE9" });
|
|
3744
3836
|
const { provider, plaintextKey } = active;
|
|
3745
3837
|
const model = opts.model || provider.model || DEFAULT_MODELS[provider.provider] || "";
|
|
3746
3838
|
try {
|
|
@@ -3758,7 +3850,7 @@ async function* streamWithActiveProvider(opts) {
|
|
|
3758
3850
|
yield { type: "done", text: "" };
|
|
3759
3851
|
} catch (err) {
|
|
3760
3852
|
if (String(err?.message ?? "").startsWith("Unsupported provider:")) {
|
|
3761
|
-
throw new
|
|
3853
|
+
throw new TRPCError13({ code: "BAD_REQUEST", message: `Fournisseur non support\xE9: ${provider.provider}` });
|
|
3762
3854
|
}
|
|
3763
3855
|
throw err;
|
|
3764
3856
|
}
|
|
@@ -5250,28 +5342,28 @@ Modify the scenario. Keep existing ids when possible.`;
|
|
|
5250
5342
|
return `Generate an scenario org-chart based on: ${userPrompt}`;
|
|
5251
5343
|
}
|
|
5252
5344
|
var ScopeShape2 = {
|
|
5253
|
-
orgId:
|
|
5345
|
+
orgId: z16.string().max(128).nullable().optional(),
|
|
5254
5346
|
projectId: Id128Schema.optional()
|
|
5255
5347
|
};
|
|
5256
5348
|
async function scopeOf2(input) {
|
|
5257
5349
|
return input?.orgId ?? await vaultOfProject(input?.projectId);
|
|
5258
5350
|
}
|
|
5259
5351
|
var scenarioRouter = router({
|
|
5260
|
-
list: protectedProcedure.input(
|
|
5261
|
-
get: protectedProcedure.input(
|
|
5352
|
+
list: protectedProcedure.input(z16.object(ScopeShape2).optional()).query(async ({ input }) => scenarioStore.list(await scopeOf2(input))),
|
|
5353
|
+
get: protectedProcedure.input(z16.object({ id: Id128Schema, ...ScopeShape2 })).query(async ({ input }) => getOrNotFound(
|
|
5262
5354
|
async () => scenarioStore.get(input.id, await scopeOf2(input)),
|
|
5263
5355
|
"Scenario not found"
|
|
5264
5356
|
)),
|
|
5265
|
-
create: protectedProcedure.input(
|
|
5357
|
+
create: protectedProcedure.input(z16.object({
|
|
5266
5358
|
name: NameSchema,
|
|
5267
|
-
icon:
|
|
5359
|
+
icon: z16.string().max(64).optional(),
|
|
5268
5360
|
description: DescriptionSchema.optional(),
|
|
5269
5361
|
leaderId: Id128Schema,
|
|
5270
|
-
groups:
|
|
5271
|
-
nodes:
|
|
5272
|
-
traversalMode:
|
|
5273
|
-
presetId:
|
|
5274
|
-
isPublished:
|
|
5362
|
+
groups: z16.array(ScenarioGroupSchema).max(100).optional(),
|
|
5363
|
+
nodes: z16.array(ScenarioNodeSchema).min(1).max(500),
|
|
5364
|
+
traversalMode: z16.enum(["hierarchical", "topological"]).optional(),
|
|
5365
|
+
presetId: z16.string().max(128).optional(),
|
|
5366
|
+
isPublished: z16.boolean().optional(),
|
|
5275
5367
|
...ScopeShape2
|
|
5276
5368
|
})).mutation(({ input }) => {
|
|
5277
5369
|
const now = Date.now();
|
|
@@ -5290,17 +5382,17 @@ var scenarioRouter = router({
|
|
|
5290
5382
|
updatedAt: now
|
|
5291
5383
|
}));
|
|
5292
5384
|
}),
|
|
5293
|
-
update: protectedProcedure.input(
|
|
5385
|
+
update: protectedProcedure.input(z16.object({
|
|
5294
5386
|
id: Id128Schema,
|
|
5295
5387
|
name: NameSchema.optional(),
|
|
5296
|
-
icon:
|
|
5388
|
+
icon: z16.string().max(64).optional(),
|
|
5297
5389
|
description: DescriptionSchema.optional(),
|
|
5298
5390
|
leaderId: Id128Schema.optional(),
|
|
5299
|
-
groups:
|
|
5300
|
-
nodes:
|
|
5301
|
-
traversalMode:
|
|
5302
|
-
presetId:
|
|
5303
|
-
isPublished:
|
|
5391
|
+
groups: z16.array(ScenarioGroupSchema).max(100).optional(),
|
|
5392
|
+
nodes: z16.array(ScenarioNodeSchema).min(1).max(500).optional(),
|
|
5393
|
+
traversalMode: z16.enum(["hierarchical", "topological"]).optional(),
|
|
5394
|
+
presetId: z16.string().max(128).optional(),
|
|
5395
|
+
isPublished: z16.boolean().optional(),
|
|
5304
5396
|
...ScopeShape2
|
|
5305
5397
|
})).mutation(({ input }) => updateOrThrow(
|
|
5306
5398
|
async () => scenarioStore.update(input.id, {
|
|
@@ -5317,25 +5409,25 @@ var scenarioRouter = router({
|
|
|
5317
5409
|
}, await scopeOf2(input)),
|
|
5318
5410
|
"Scenario not found"
|
|
5319
5411
|
)),
|
|
5320
|
-
delete: protectedProcedure.input(
|
|
5412
|
+
delete: protectedProcedure.input(z16.object({ id: Id128Schema, ...ScopeShape2 })).mutation(({ input }) => deleteOrNotFound(
|
|
5321
5413
|
async () => scenarioStore.delete(input.id, await scopeOf2(input)),
|
|
5322
5414
|
"Scenario not found"
|
|
5323
5415
|
)),
|
|
5324
|
-
run: protectedProcedure.input(
|
|
5416
|
+
run: protectedProcedure.input(z16.object({
|
|
5325
5417
|
conversationId: ConversationIdSchema,
|
|
5326
5418
|
scenarioId: Id128Schema,
|
|
5327
|
-
userRequest:
|
|
5328
|
-
runnerOverride:
|
|
5419
|
+
userRequest: z16.string().max(1e5),
|
|
5420
|
+
runnerOverride: z16.enum(["claude-code", "codex"]).optional(),
|
|
5329
5421
|
modelOverride: Id128Schema.optional(),
|
|
5330
5422
|
agentId: Id128Schema.optional(),
|
|
5331
5423
|
/** Anchors the run's working directory on the project it belongs to. */
|
|
5332
5424
|
projectId: Id128Schema.optional(),
|
|
5333
5425
|
/** Son organisation : sans elle, un id partagé désignerait un projet au hasard. */
|
|
5334
|
-
orgId:
|
|
5335
|
-
resume:
|
|
5426
|
+
orgId: z16.string().max(128).nullable().optional(),
|
|
5427
|
+
resume: z16.boolean().optional()
|
|
5336
5428
|
})).subscription(async function* ({ input, signal }) {
|
|
5337
5429
|
const scenario = await scenarioStore.get(input.scenarioId, await scopeOf2(input));
|
|
5338
|
-
if (!scenario) throw new
|
|
5430
|
+
if (!scenario) throw new TRPCError14({ code: "NOT_FOUND", message: "Scenario not found" });
|
|
5339
5431
|
const canStart = input.userRequest.trim().length > 0 || !!input.resume;
|
|
5340
5432
|
if (!isScenarioRunning(input.conversationId) && canStart) {
|
|
5341
5433
|
let userRequest = input.userRequest;
|
|
@@ -5352,41 +5444,41 @@ var scenarioRouter = router({
|
|
|
5352
5444
|
}
|
|
5353
5445
|
yield* tapBus(input.conversationId, signal ?? new AbortController().signal);
|
|
5354
5446
|
}),
|
|
5355
|
-
stop: protectedProcedure.input(
|
|
5447
|
+
stop: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).mutation(({ input }) => {
|
|
5356
5448
|
abortScenario(input.conversationId);
|
|
5357
5449
|
return { ok: true };
|
|
5358
5450
|
}),
|
|
5359
|
-
sendNodeInput: protectedProcedure.input(
|
|
5451
|
+
sendNodeInput: protectedProcedure.input(z16.object({
|
|
5360
5452
|
conversationId: ConversationIdSchema,
|
|
5361
5453
|
nodeId: Id128Schema,
|
|
5362
|
-
text:
|
|
5454
|
+
text: z16.string().min(1).max(1e5)
|
|
5363
5455
|
})).mutation(({ input }) => {
|
|
5364
5456
|
const resolved = resolveNodeInput(input.conversationId, input.nodeId, input.text);
|
|
5365
|
-
if (!resolved) throw new
|
|
5457
|
+
if (!resolved) throw new TRPCError14({ code: "NOT_FOUND", message: "No pending input request for this node" });
|
|
5366
5458
|
return { ok: true };
|
|
5367
5459
|
}),
|
|
5368
|
-
isRunning: protectedProcedure.input(
|
|
5369
|
-
runState: protectedProcedure.input(
|
|
5460
|
+
isRunning: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(({ input }) => ({ running: isScenarioRunning(input.conversationId) })),
|
|
5461
|
+
runState: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(({ input }) => {
|
|
5370
5462
|
const runState = scenarioRunStore.getRunState(input.conversationId);
|
|
5371
5463
|
return runState ?? { nodeStates: {}, nodeOutputs: {}, activeNodeId: void 0 };
|
|
5372
5464
|
}),
|
|
5373
|
-
getPersistedRunState: protectedProcedure.input(
|
|
5465
|
+
getPersistedRunState: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
|
|
5374
5466
|
return await scenarioRunStore.loadPersistedRunState(input.conversationId);
|
|
5375
5467
|
}),
|
|
5376
|
-
getReadme: protectedProcedure.input(
|
|
5468
|
+
getReadme: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
|
|
5377
5469
|
return await scenarioRunStore.readReadme(input.conversationId);
|
|
5378
5470
|
}),
|
|
5379
|
-
getSnapshot: protectedProcedure.input(
|
|
5471
|
+
getSnapshot: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
|
|
5380
5472
|
return await scenarioRunStore.loadScenarioSnapshot(input.conversationId);
|
|
5381
5473
|
}),
|
|
5382
|
-
streamGenerateWithAI: protectedProcedure.input(
|
|
5383
|
-
prompt:
|
|
5474
|
+
streamGenerateWithAI: protectedProcedure.input(z16.object({
|
|
5475
|
+
prompt: z16.string().min(1).max(1e4),
|
|
5384
5476
|
providerId: OptionalProviderIdSchema,
|
|
5385
|
-
model:
|
|
5386
|
-
current:
|
|
5477
|
+
model: z16.string().max(200).optional(),
|
|
5478
|
+
current: z16.object({
|
|
5387
5479
|
leaderId: Id128Schema,
|
|
5388
|
-
groups:
|
|
5389
|
-
nodes:
|
|
5480
|
+
groups: z16.array(ScenarioGroupSchema).max(100),
|
|
5481
|
+
nodes: z16.array(ScenarioNodeSchema).max(500)
|
|
5390
5482
|
}).optional()
|
|
5391
5483
|
})).subscription(async function* ({ input, signal }) {
|
|
5392
5484
|
const userMessage = buildScenarioUserMessage(input.prompt, input.current);
|
|
@@ -5399,7 +5491,7 @@ var scenarioRouter = router({
|
|
|
5399
5491
|
signal: signal ?? void 0
|
|
5400
5492
|
});
|
|
5401
5493
|
}),
|
|
5402
|
-
streamRunEvents: protectedProcedure.input(
|
|
5494
|
+
streamRunEvents: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).subscription(async function* ({ input }) {
|
|
5403
5495
|
const convId = input.conversationId;
|
|
5404
5496
|
const runState = scenarioRunStore.getRunState(convId);
|
|
5405
5497
|
if (runState) {
|
|
@@ -5413,24 +5505,24 @@ var scenarioRouter = router({
|
|
|
5413
5505
|
* open a debug PTY with `claude --resume <sessionId>` on the right agent.
|
|
5414
5506
|
* Falls back to the persisted run-state for past nodes.
|
|
5415
5507
|
*/
|
|
5416
|
-
getNodeSession: protectedProcedure.input(
|
|
5508
|
+
getNodeSession: protectedProcedure.input(z16.object({
|
|
5417
5509
|
conversationId: ConversationIdSchema,
|
|
5418
5510
|
nodeId: Id128Schema
|
|
5419
5511
|
})).query(async ({ input }) => {
|
|
5420
5512
|
return await scenarioRunStore.getNodeSessionPersistent(input.conversationId, input.nodeId);
|
|
5421
5513
|
}),
|
|
5422
5514
|
listPresets: protectedProcedure.query(() => listPresets()),
|
|
5423
|
-
getPreset: protectedProcedure.input(
|
|
5515
|
+
getPreset: protectedProcedure.input(z16.object({ id: Id128Schema })).query(({ input }) => {
|
|
5424
5516
|
const preset = getPreset(input.id);
|
|
5425
|
-
if (!preset) throw new
|
|
5517
|
+
if (!preset) throw new TRPCError14({ code: "NOT_FOUND", message: "Preset not found" });
|
|
5426
5518
|
return preset;
|
|
5427
5519
|
})
|
|
5428
5520
|
});
|
|
5429
5521
|
|
|
5430
5522
|
// ../server/src/trpc/routers/debug.ts
|
|
5431
5523
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5432
|
-
import { TRPCError as
|
|
5433
|
-
import { z as
|
|
5524
|
+
import { TRPCError as TRPCError15 } from "@trpc/server";
|
|
5525
|
+
import { z as z17 } from "zod";
|
|
5434
5526
|
var MAX_BUFFER2 = 300;
|
|
5435
5527
|
var jobs = /* @__PURE__ */ new Map();
|
|
5436
5528
|
function publish(job, event) {
|
|
@@ -5487,9 +5579,9 @@ async function* tapJob(job, signal) {
|
|
|
5487
5579
|
}
|
|
5488
5580
|
}
|
|
5489
5581
|
var debugRouter = router({
|
|
5490
|
-
startRandomJob: protectedProcedure.input(
|
|
5491
|
-
intervalMs:
|
|
5492
|
-
maxTicks:
|
|
5582
|
+
startRandomJob: protectedProcedure.input(z17.object({
|
|
5583
|
+
intervalMs: z17.number().int().min(100).max(5e3).optional(),
|
|
5584
|
+
maxTicks: z17.number().int().min(1).max(1e4).optional()
|
|
5493
5585
|
}).optional()).mutation(({ input }) => {
|
|
5494
5586
|
const jobId = randomUUID4();
|
|
5495
5587
|
const intervalMs = input?.intervalMs ?? 400;
|
|
@@ -5523,15 +5615,15 @@ var debugRouter = router({
|
|
|
5523
5615
|
jobs.set(jobId, job);
|
|
5524
5616
|
return { jobId, intervalMs, maxTicks, startedAt: job.startedAt };
|
|
5525
5617
|
}),
|
|
5526
|
-
streamRandomJob: protectedProcedure.input(
|
|
5618
|
+
streamRandomJob: protectedProcedure.input(z17.object({ jobId: z17.string().max(128) })).subscription(async function* ({ input, signal }) {
|
|
5527
5619
|
const job = jobs.get(input.jobId);
|
|
5528
5620
|
if (!job) {
|
|
5529
|
-
throw new
|
|
5621
|
+
throw new TRPCError15({ code: "NOT_FOUND", message: "Random job not found" });
|
|
5530
5622
|
}
|
|
5531
5623
|
yield* tapJob(job, signal ?? new AbortController().signal);
|
|
5532
5624
|
}),
|
|
5533
|
-
stopRandomJob: protectedProcedure.input(
|
|
5534
|
-
randomJobStatus: protectedProcedure.input(
|
|
5625
|
+
stopRandomJob: protectedProcedure.input(z17.object({ jobId: z17.string().max(128) })).mutation(({ input }) => ({ ok: stopJob(input.jobId, "stopped") })),
|
|
5626
|
+
randomJobStatus: protectedProcedure.input(z17.object({ jobId: z17.string().max(128) })).query(({ input }) => {
|
|
5535
5627
|
const job = jobs.get(input.jobId);
|
|
5536
5628
|
return {
|
|
5537
5629
|
running: !!job && !job.done,
|
|
@@ -5541,8 +5633,8 @@ var debugRouter = router({
|
|
|
5541
5633
|
});
|
|
5542
5634
|
|
|
5543
5635
|
// ../server/src/trpc/routers/diagnostics.ts
|
|
5544
|
-
import { z as
|
|
5545
|
-
import { TRPCError as
|
|
5636
|
+
import { z as z18 } from "zod";
|
|
5637
|
+
import { TRPCError as TRPCError16 } from "@trpc/server";
|
|
5546
5638
|
|
|
5547
5639
|
// ../server/src/services/log-buffer.ts
|
|
5548
5640
|
import { EventEmitter } from "events";
|
|
@@ -5613,7 +5705,7 @@ function onEntry(listener) {
|
|
|
5613
5705
|
// ../server/src/trpc/routers/diagnostics.ts
|
|
5614
5706
|
var diagnosticsRouter = router({
|
|
5615
5707
|
/** Backlog for a client that just opened the panel. */
|
|
5616
|
-
recentLogs: protectedProcedure.input(
|
|
5708
|
+
recentLogs: protectedProcedure.input(z18.object({ limit: z18.number().int().min(1).max(2e3).default(500) }).optional()).query(({ input }) => recentEntries(input?.limit ?? 500)),
|
|
5617
5709
|
/**
|
|
5618
5710
|
* Live log stream.
|
|
5619
5711
|
*
|
|
@@ -5622,7 +5714,7 @@ var diagnosticsRouter = router({
|
|
|
5622
5714
|
* silently loses lines, which is precisely what someone watching a boot
|
|
5623
5715
|
* failure cannot afford.
|
|
5624
5716
|
*/
|
|
5625
|
-
streamLogs: protectedProcedure.input(
|
|
5717
|
+
streamLogs: protectedProcedure.input(z18.object({ afterSeq: z18.number().int().nonnegative().default(0) }).optional()).subscription(async function* ({ input, signal }) {
|
|
5626
5718
|
for (const entry of entriesSince(input?.afterSeq ?? 0)) yield entry;
|
|
5627
5719
|
const queue = [];
|
|
5628
5720
|
let wake = null;
|
|
@@ -5656,18 +5748,18 @@ var diagnosticsRouter = router({
|
|
|
5656
5748
|
* refused. That is what keeps this from being a way to signal arbitrary
|
|
5657
5749
|
* processes with the server's privileges.
|
|
5658
5750
|
*/
|
|
5659
|
-
killInstance: protectedProcedure.input(
|
|
5751
|
+
killInstance: protectedProcedure.input(z18.object({ pid: z18.number().int().positive() })).mutation(async ({ input }) => {
|
|
5660
5752
|
try {
|
|
5661
5753
|
return await killInstance(input.pid);
|
|
5662
5754
|
} catch (err) {
|
|
5663
5755
|
const reason = err.message;
|
|
5664
5756
|
if (reason === "instance-not-found") {
|
|
5665
|
-
throw new
|
|
5757
|
+
throw new TRPCError16({ code: "NOT_FOUND", message: "No live Runeya instance with this pid." });
|
|
5666
5758
|
}
|
|
5667
5759
|
if (reason === "instance-is-current") {
|
|
5668
|
-
throw new
|
|
5760
|
+
throw new TRPCError16({ code: "BAD_REQUEST", message: "This instance cannot stop itself." });
|
|
5669
5761
|
}
|
|
5670
|
-
throw new
|
|
5762
|
+
throw new TRPCError16({ code: "INTERNAL_SERVER_ERROR", message: reason });
|
|
5671
5763
|
}
|
|
5672
5764
|
}),
|
|
5673
5765
|
/**
|
|
@@ -5684,7 +5776,7 @@ var diagnosticsRouter = router({
|
|
|
5684
5776
|
* reproductible : instance trop ancienne pour avoir publié sa ligne de
|
|
5685
5777
|
* commande, ou lancée avec un secret que le registre ne conserve pas.
|
|
5686
5778
|
*/
|
|
5687
|
-
restartInstance: protectedProcedure.input(
|
|
5779
|
+
restartInstance: protectedProcedure.input(z18.object({ pid: z18.number().int().positive() })).mutation(async ({ input }) => {
|
|
5688
5780
|
try {
|
|
5689
5781
|
if (input.pid === process.pid) {
|
|
5690
5782
|
const { relauncherPid, port } = await scheduleSelfRestart();
|
|
@@ -5694,21 +5786,21 @@ var diagnosticsRouter = router({
|
|
|
5694
5786
|
} catch (err) {
|
|
5695
5787
|
const reason = err.message;
|
|
5696
5788
|
if (reason === "instance-not-found") {
|
|
5697
|
-
throw new
|
|
5789
|
+
throw new TRPCError16({ code: "NOT_FOUND", message: "No live Runeya instance with this pid." });
|
|
5698
5790
|
}
|
|
5699
5791
|
if (reason.startsWith("instance-not-restartable:")) {
|
|
5700
|
-
throw new
|
|
5792
|
+
throw new TRPCError16({
|
|
5701
5793
|
code: "BAD_REQUEST",
|
|
5702
5794
|
message: `This instance cannot be restarted (${reason.split(":")[1]}).`
|
|
5703
5795
|
});
|
|
5704
5796
|
}
|
|
5705
|
-
throw new
|
|
5797
|
+
throw new TRPCError16({ code: "INTERNAL_SERVER_ERROR", message: reason });
|
|
5706
5798
|
}
|
|
5707
5799
|
})
|
|
5708
5800
|
});
|
|
5709
5801
|
|
|
5710
5802
|
// ../server/src/trpc/routers/kanban-queue.ts
|
|
5711
|
-
import { z as
|
|
5803
|
+
import { z as z19 } from "zod";
|
|
5712
5804
|
|
|
5713
5805
|
// ../server/src/services/kanban-queue.ts
|
|
5714
5806
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
@@ -5900,38 +5992,38 @@ var kanbanQueue = new KanbanQueueService();
|
|
|
5900
5992
|
// ../server/src/trpc/routers/kanban-queue.ts
|
|
5901
5993
|
var kanbanQueueRouter = router({
|
|
5902
5994
|
getState: protectedProcedure.query(() => kanbanQueue.getState()),
|
|
5903
|
-
add: protectedProcedure.input(
|
|
5904
|
-
boardId:
|
|
5905
|
-
cardId:
|
|
5906
|
-
runner:
|
|
5907
|
-
model:
|
|
5908
|
-
scenarioId:
|
|
5995
|
+
add: protectedProcedure.input(z19.object({
|
|
5996
|
+
boardId: z19.string().max(128),
|
|
5997
|
+
cardId: z19.string().max(128),
|
|
5998
|
+
runner: z19.enum(["claude-code", "codex"]).optional(),
|
|
5999
|
+
model: z19.string().max(128).optional(),
|
|
6000
|
+
scenarioId: z19.string().max(128).optional(),
|
|
5909
6001
|
// Le projet ouvert quand la carte a été mise en file : la conversation
|
|
5910
6002
|
// qui en naîtra lui appartient, plutôt que de rester orpheline et de
|
|
5911
6003
|
// s'afficher dans tous les projets.
|
|
5912
|
-
projectId:
|
|
5913
|
-
orgId:
|
|
6004
|
+
projectId: z19.string().max(128).optional(),
|
|
6005
|
+
orgId: z19.string().max(128).nullable().optional()
|
|
5914
6006
|
})).mutation(({ input }) => kanbanQueue.add(input)),
|
|
5915
|
-
addColumn: protectedProcedure.input(
|
|
5916
|
-
boardId:
|
|
5917
|
-
columnId:
|
|
6007
|
+
addColumn: protectedProcedure.input(z19.object({
|
|
6008
|
+
boardId: z19.string().max(128),
|
|
6009
|
+
columnId: z19.string().max(128),
|
|
5918
6010
|
// Les cartes visibles côté écran : une colonne filtrée n'envoie qu'elles.
|
|
5919
|
-
cardIds:
|
|
5920
|
-
runner:
|
|
5921
|
-
model:
|
|
5922
|
-
scenarioId:
|
|
5923
|
-
projectId:
|
|
5924
|
-
orgId:
|
|
6011
|
+
cardIds: z19.array(z19.string().max(128)).max(5e3).optional(),
|
|
6012
|
+
runner: z19.enum(["claude-code", "codex"]).optional(),
|
|
6013
|
+
model: z19.string().max(128).optional(),
|
|
6014
|
+
scenarioId: z19.string().max(128).optional(),
|
|
6015
|
+
projectId: z19.string().max(128).optional(),
|
|
6016
|
+
orgId: z19.string().max(128).nullable().optional()
|
|
5925
6017
|
})).mutation(async ({ input }) => {
|
|
5926
6018
|
const { boardId, columnId, ...options } = input;
|
|
5927
6019
|
return kanbanQueue.addColumn(boardId, columnId, options);
|
|
5928
6020
|
}),
|
|
5929
|
-
remove: protectedProcedure.input(
|
|
6021
|
+
remove: protectedProcedure.input(z19.object({ itemId: z19.string().max(128) })).mutation(({ input }) => ({ removed: kanbanQueue.remove(input.itemId) })),
|
|
5930
6022
|
stopAll: protectedProcedure.mutation(() => {
|
|
5931
6023
|
kanbanQueue.stopAll();
|
|
5932
6024
|
return { ok: true };
|
|
5933
6025
|
}),
|
|
5934
|
-
setMaxConcurrent: protectedProcedure.input(
|
|
6026
|
+
setMaxConcurrent: protectedProcedure.input(z19.object({ max: z19.number().int().min(1).max(20) })).mutation(({ input }) => {
|
|
5935
6027
|
kanbanQueue.setMaxConcurrent(input.max);
|
|
5936
6028
|
return kanbanQueue.getState();
|
|
5937
6029
|
}),
|
|
@@ -5969,8 +6061,8 @@ import { mkdir as mkdir5, readdir, rm, stat as stat3, writeFile as writeFile4 }
|
|
|
5969
6061
|
import { createHash } from "crypto";
|
|
5970
6062
|
import { tmpdir } from "os";
|
|
5971
6063
|
import { join as join7 } from "path";
|
|
5972
|
-
import { z as
|
|
5973
|
-
import { TRPCError as
|
|
6064
|
+
import { z as z20 } from "zod";
|
|
6065
|
+
import { TRPCError as TRPCError17 } from "@trpc/server";
|
|
5974
6066
|
function summarizeCard(card, boardId, columnTitle) {
|
|
5975
6067
|
return {
|
|
5976
6068
|
boardId,
|
|
@@ -6041,7 +6133,7 @@ async function storeAttachment(id, name, body, dirs = attachmentCacheDirs()) {
|
|
|
6041
6133
|
refusals.push(`${dir} (${err instanceof Error ? err.message : String(err)})`);
|
|
6042
6134
|
}
|
|
6043
6135
|
}
|
|
6044
|
-
throw new
|
|
6136
|
+
throw new TRPCError17({
|
|
6045
6137
|
code: "INTERNAL_SERVER_ERROR",
|
|
6046
6138
|
message: `Impossible d'\xE9crire la pi\xE8ce jointe sur cette machine. Emplacements essay\xE9s : ${refusals.join(" ; ")}. V\xE9rifiez les droits d'\xE9criture, ou l'espace disque.`
|
|
6047
6139
|
});
|
|
@@ -6062,9 +6154,9 @@ var kanbanRouter = router({
|
|
|
6062
6154
|
* card graph, so listing them in full would be megabytes to answer "which
|
|
6063
6155
|
* boards exist?". Use `getBoard` for the cards of one.
|
|
6064
6156
|
*/
|
|
6065
|
-
listBoards: protectedProcedure.input(
|
|
6066
|
-
includeArchived:
|
|
6067
|
-
organizationId:
|
|
6157
|
+
listBoards: protectedProcedure.input(z20.object({
|
|
6158
|
+
includeArchived: z20.boolean().optional(),
|
|
6159
|
+
organizationId: z20.string().max(128).optional()
|
|
6068
6160
|
}).optional()).query(async ({ input }) => {
|
|
6069
6161
|
const { organizationId, boards } = await kanbanStore.list(
|
|
6070
6162
|
input?.includeArchived ?? false,
|
|
@@ -6090,7 +6182,7 @@ var kanbanRouter = router({
|
|
|
6090
6182
|
};
|
|
6091
6183
|
}),
|
|
6092
6184
|
/** One board: its columns, and the cards in each. */
|
|
6093
|
-
getBoard: protectedProcedure.input(
|
|
6185
|
+
getBoard: protectedProcedure.input(z20.object({ boardId: z20.string().max(128) })).query(async ({ input }) => {
|
|
6094
6186
|
const board = await kanbanStore.get(input.boardId);
|
|
6095
6187
|
if (!board) return { board: null };
|
|
6096
6188
|
const { links } = await kanbanStore.cardLinks(input.boardId);
|
|
@@ -6129,12 +6221,12 @@ var kanbanRouter = router({
|
|
|
6129
6221
|
* organization — or one board when `boardId` is given. Archived boards are
|
|
6130
6222
|
* left out: they are what the team decided to stop looking at.
|
|
6131
6223
|
*/
|
|
6132
|
-
searchCards: protectedProcedure.input(
|
|
6133
|
-
query:
|
|
6134
|
-
createdBy:
|
|
6135
|
-
boardId:
|
|
6136
|
-
limit:
|
|
6137
|
-
organizationId:
|
|
6224
|
+
searchCards: protectedProcedure.input(z20.object({
|
|
6225
|
+
query: z20.string().max(256).optional(),
|
|
6226
|
+
createdBy: z20.string().min(1).max(200).optional(),
|
|
6227
|
+
boardId: z20.string().max(128).optional(),
|
|
6228
|
+
limit: z20.number().int().min(1).max(100).optional(),
|
|
6229
|
+
organizationId: z20.string().max(128).optional()
|
|
6138
6230
|
}).refine((i) => !!i.query?.trim() || !!i.createdBy?.trim(), { message: "query or createdBy is required." })).query(async ({ input }) => {
|
|
6139
6231
|
return kanbanStore.searchCards(input);
|
|
6140
6232
|
}),
|
|
@@ -6142,10 +6234,10 @@ var kanbanRouter = router({
|
|
|
6142
6234
|
* Les conversations de support liées à une carte, avec leur fil — ce que
|
|
6143
6235
|
* le client a écrit à l'équipe, séparé des commentaires de la carte.
|
|
6144
6236
|
*/
|
|
6145
|
-
getCardConversations: protectedProcedure.input(
|
|
6146
|
-
boardId:
|
|
6147
|
-
cardId:
|
|
6148
|
-
limit:
|
|
6237
|
+
getCardConversations: protectedProcedure.input(z20.object({
|
|
6238
|
+
boardId: z20.string().max(128),
|
|
6239
|
+
cardId: z20.string().max(128),
|
|
6240
|
+
limit: z20.number().int().min(1).max(500).optional()
|
|
6149
6241
|
})).query(async ({ input }) => {
|
|
6150
6242
|
const conversations = await kanbanStore.cardConversations(input.boardId, input.cardId);
|
|
6151
6243
|
const limit = input.limit ?? 100;
|
|
@@ -6158,13 +6250,13 @@ var kanbanRouter = router({
|
|
|
6158
6250
|
};
|
|
6159
6251
|
}),
|
|
6160
6252
|
/** Chercher une conversation chez les fournisseurs de l'organisation (lecture seule côté fournisseur). */
|
|
6161
|
-
searchConversations: protectedProcedure.input(
|
|
6253
|
+
searchConversations: protectedProcedure.input(z20.object({ boardId: z20.string().max(128), query: z20.string().trim().min(1).max(500) })).query(async ({ input }) => ({ results: await kanbanStore.searchConversations(input.boardId, input.query) })),
|
|
6162
6254
|
/** Lier une conversation trouvée à une carte. N'écrit rien chez le fournisseur. */
|
|
6163
|
-
linkConversation: protectedProcedure.input(
|
|
6164
|
-
boardId:
|
|
6165
|
-
cardId:
|
|
6166
|
-
connectionId:
|
|
6167
|
-
externalId:
|
|
6255
|
+
linkConversation: protectedProcedure.input(z20.object({
|
|
6256
|
+
boardId: z20.string().max(128),
|
|
6257
|
+
cardId: z20.string().max(128),
|
|
6258
|
+
connectionId: z20.string().max(128),
|
|
6259
|
+
externalId: z20.string().max(128)
|
|
6168
6260
|
})).mutation(async ({ input }) => ({ conversations: await kanbanStore.linkConversation(input) })),
|
|
6169
6261
|
/**
|
|
6170
6262
|
* Le fil de commentaires d'une carte, relu à la demande.
|
|
@@ -6175,10 +6267,10 @@ var kanbanRouter = router({
|
|
|
6175
6267
|
* qu'un `commentCount`, et rien n'en donnait le texte. Cette route est la
|
|
6176
6268
|
* relecture, pour une seule carte : c'est le journal qui pèse, pas le reste.
|
|
6177
6269
|
*/
|
|
6178
|
-
getCardComments: protectedProcedure.input(
|
|
6179
|
-
boardId:
|
|
6180
|
-
cardId:
|
|
6181
|
-
limit:
|
|
6270
|
+
getCardComments: protectedProcedure.input(z20.object({
|
|
6271
|
+
boardId: z20.string().max(128),
|
|
6272
|
+
cardId: z20.string().max(128),
|
|
6273
|
+
limit: z20.number().int().min(1).max(200).optional()
|
|
6182
6274
|
})).query(async ({ input }) => {
|
|
6183
6275
|
const history = await kanbanStore.cardHistory(input.boardId, input.cardId);
|
|
6184
6276
|
const comments = history.filter((e) => e.type === "comment");
|
|
@@ -6207,18 +6299,18 @@ var kanbanRouter = router({
|
|
|
6207
6299
|
* `getBoard` en donne déjà l'essentiel carte par carte ; cette route sert
|
|
6208
6300
|
* quand c'est l'arbre lui-même qu'on veut, dates et auteurs compris.
|
|
6209
6301
|
*/
|
|
6210
|
-
getCardLinks: protectedProcedure.input(
|
|
6302
|
+
getCardLinks: protectedProcedure.input(z20.object({ boardId: z20.string().max(128) })).query(async ({ input }) => kanbanStore.cardLinks(input.boardId)),
|
|
6211
6303
|
/**
|
|
6212
6304
|
* Rattacher une carte à une carte parente, ou l'en détacher.
|
|
6213
6305
|
*
|
|
6214
6306
|
* Sans `parentCardId`, le lien est retiré. Le parent peut vivre sur un autre
|
|
6215
6307
|
* tableau : `parentBoardId` le désigne alors, sinon c'est celui de la carte.
|
|
6216
6308
|
*/
|
|
6217
|
-
setCardParent: protectedProcedure.input(
|
|
6218
|
-
boardId:
|
|
6219
|
-
cardId:
|
|
6220
|
-
parentCardId:
|
|
6221
|
-
parentBoardId:
|
|
6309
|
+
setCardParent: protectedProcedure.input(z20.object({
|
|
6310
|
+
boardId: z20.string().max(128),
|
|
6311
|
+
cardId: z20.string().max(128),
|
|
6312
|
+
parentCardId: z20.string().max(128).optional(),
|
|
6313
|
+
parentBoardId: z20.string().max(128).optional()
|
|
6222
6314
|
})).mutation(async ({ input }) => {
|
|
6223
6315
|
const ok = await kanbanStore.setCardParent(
|
|
6224
6316
|
input.boardId,
|
|
@@ -6226,7 +6318,7 @@ var kanbanRouter = router({
|
|
|
6226
6318
|
input.parentCardId ? { boardId: input.parentBoardId ?? input.boardId, cardId: input.parentCardId } : null
|
|
6227
6319
|
);
|
|
6228
6320
|
if (!ok) {
|
|
6229
|
-
throw new
|
|
6321
|
+
throw new TRPCError17({ code: "BAD_REQUEST", message: "The cloud refused the link (unknown card, or a cycle)." });
|
|
6230
6322
|
}
|
|
6231
6323
|
return { ok: true };
|
|
6232
6324
|
}),
|
|
@@ -6235,48 +6327,48 @@ var kanbanRouter = router({
|
|
|
6235
6327
|
* `boardId` est le tableau où on la pose (ou d'où on la retire) ; la carte
|
|
6236
6328
|
* reste sur son tableau d'origine.
|
|
6237
6329
|
*/
|
|
6238
|
-
getCardPlacements: protectedProcedure.input(
|
|
6239
|
-
placeCard: protectedProcedure.input(
|
|
6330
|
+
getCardPlacements: protectedProcedure.input(z20.object({ boardId: z20.string().max(128), cardId: z20.string().max(128) })).query(async ({ input }) => ({ placements: await kanbanStore.cardPlacements(input.boardId, input.cardId) })),
|
|
6331
|
+
placeCard: protectedProcedure.input(z20.object({ boardId: z20.string().max(128), cardId: z20.string().max(128), column: z20.string().min(1).max(200) })).mutation(async ({ input }) => {
|
|
6240
6332
|
const res = await kanbanStore.placeCard(input.boardId, input.cardId, input.column);
|
|
6241
6333
|
if (!res.ok) {
|
|
6242
|
-
throw new
|
|
6334
|
+
throw new TRPCError17({
|
|
6243
6335
|
code: "BAD_REQUEST",
|
|
6244
6336
|
message: res.reason ?? "The cloud refused the placement (unknown card or column, archived card, or already on this board)."
|
|
6245
6337
|
});
|
|
6246
6338
|
}
|
|
6247
6339
|
return { ok: true };
|
|
6248
6340
|
}),
|
|
6249
|
-
unplaceCard: protectedProcedure.input(
|
|
6341
|
+
unplaceCard: protectedProcedure.input(z20.object({ boardId: z20.string().max(128), cardId: z20.string().max(128) })).mutation(async ({ input }) => {
|
|
6250
6342
|
if (!await kanbanStore.unplaceCard(input.boardId, input.cardId)) {
|
|
6251
|
-
throw new
|
|
6343
|
+
throw new TRPCError17({ code: "NOT_FOUND", message: "This card is not placed on this board." });
|
|
6252
6344
|
}
|
|
6253
6345
|
return { ok: true };
|
|
6254
6346
|
}),
|
|
6255
|
-
moveCard: protectedProcedure.input(
|
|
6256
|
-
boardId:
|
|
6257
|
-
cardId:
|
|
6258
|
-
targetColumn:
|
|
6347
|
+
moveCard: protectedProcedure.input(z20.object({
|
|
6348
|
+
boardId: z20.string().max(128),
|
|
6349
|
+
cardId: z20.string().max(128),
|
|
6350
|
+
targetColumn: z20.string().max(256)
|
|
6259
6351
|
})).mutation(async ({ input }) => {
|
|
6260
6352
|
await executeKanbanCommands(input.boardId, input.cardId, [
|
|
6261
6353
|
{ type: "move_card", columnTitle: input.targetColumn }
|
|
6262
6354
|
]);
|
|
6263
6355
|
return { ok: true };
|
|
6264
6356
|
}),
|
|
6265
|
-
addComment: protectedProcedure.input(
|
|
6266
|
-
boardId:
|
|
6267
|
-
cardId:
|
|
6268
|
-
comment:
|
|
6357
|
+
addComment: protectedProcedure.input(z20.object({
|
|
6358
|
+
boardId: z20.string().max(128),
|
|
6359
|
+
cardId: z20.string().max(128),
|
|
6360
|
+
comment: z20.string().max(1e5)
|
|
6269
6361
|
})).mutation(async ({ input }) => {
|
|
6270
6362
|
await executeKanbanCommands(input.boardId, input.cardId, [
|
|
6271
6363
|
{ type: "add_comment", comment: input.comment }
|
|
6272
6364
|
]);
|
|
6273
6365
|
return { ok: true };
|
|
6274
6366
|
}),
|
|
6275
|
-
addCard: protectedProcedure.input(
|
|
6276
|
-
boardId:
|
|
6277
|
-
column:
|
|
6278
|
-
title:
|
|
6279
|
-
content:
|
|
6367
|
+
addCard: protectedProcedure.input(z20.object({
|
|
6368
|
+
boardId: z20.string().max(128),
|
|
6369
|
+
column: z20.string().max(256),
|
|
6370
|
+
title: z20.string().max(512),
|
|
6371
|
+
content: z20.string().max(2e4).optional()
|
|
6280
6372
|
})).mutation(async ({ input }) => {
|
|
6281
6373
|
await executeKanbanBoardCommands(input.boardId, [
|
|
6282
6374
|
{ type: "add_card", columnTitle: input.column, cardTitle: input.title, cardContent: input.content }
|
|
@@ -6289,7 +6381,7 @@ var kanbanRouter = router({
|
|
|
6289
6381
|
* `getBoard` rapatrie le tableau entier pour n'en regarder qu'une ; et il ne
|
|
6290
6382
|
* porte pas les cartes rangées. Une carte s'adresse par son identifiant.
|
|
6291
6383
|
*/
|
|
6292
|
-
getCard: protectedProcedure.input(
|
|
6384
|
+
getCard: protectedProcedure.input(z20.object({ boardId: z20.string().max(128), cardId: z20.string().max(128) })).query(async ({ input }) => {
|
|
6293
6385
|
const found = await kanbanStore.card(input.boardId, input.cardId);
|
|
6294
6386
|
if (!found) throw new Error(`Carte ${input.cardId} introuvable sur le tableau ${input.boardId}.`);
|
|
6295
6387
|
return { ...summarizeCard(found.card, input.boardId, ""), columnId: found.columnId };
|
|
@@ -6303,16 +6395,16 @@ var kanbanRouter = router({
|
|
|
6303
6395
|
* Un champ absent n'est pas touché ; `null` l'efface. La distinction compte —
|
|
6304
6396
|
* changer un titre ne doit pas vider une couleur.
|
|
6305
6397
|
*/
|
|
6306
|
-
updateCard: protectedProcedure.input(
|
|
6307
|
-
boardId:
|
|
6308
|
-
cardId:
|
|
6309
|
-
title:
|
|
6310
|
-
content:
|
|
6311
|
-
priority:
|
|
6312
|
-
color:
|
|
6313
|
-
groups:
|
|
6314
|
-
deadline:
|
|
6315
|
-
assignees:
|
|
6398
|
+
updateCard: protectedProcedure.input(z20.object({
|
|
6399
|
+
boardId: z20.string().max(128),
|
|
6400
|
+
cardId: z20.string().max(128),
|
|
6401
|
+
title: z20.string().min(1).max(512).optional(),
|
|
6402
|
+
content: z20.string().max(1e5).optional(),
|
|
6403
|
+
priority: z20.number().int().min(1).max(10).nullable().optional(),
|
|
6404
|
+
color: z20.string().max(32).nullable().optional(),
|
|
6405
|
+
groups: z20.array(z20.string().min(1).max(60)).max(30).optional(),
|
|
6406
|
+
deadline: z20.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
|
|
6407
|
+
assignees: z20.array(z20.string().max(128)).max(50).optional()
|
|
6316
6408
|
})).mutation(async ({ input }) => {
|
|
6317
6409
|
const { boardId, cardId, content, ...rest } = input;
|
|
6318
6410
|
const patch = { ...rest };
|
|
@@ -6323,10 +6415,10 @@ var kanbanRouter = router({
|
|
|
6323
6415
|
return summarizeCard(card, boardId, "");
|
|
6324
6416
|
}),
|
|
6325
6417
|
/** Ranger une carte, ou la reprendre. */
|
|
6326
|
-
setCardArchived: protectedProcedure.input(
|
|
6327
|
-
boardId:
|
|
6328
|
-
cardId:
|
|
6329
|
-
archived:
|
|
6418
|
+
setCardArchived: protectedProcedure.input(z20.object({
|
|
6419
|
+
boardId: z20.string().max(128),
|
|
6420
|
+
cardId: z20.string().max(128),
|
|
6421
|
+
archived: z20.boolean()
|
|
6330
6422
|
})).mutation(async ({ input }) => {
|
|
6331
6423
|
const ok = await kanbanStore.setCardArchived(input.boardId, input.cardId, input.archived);
|
|
6332
6424
|
if (!ok) throw new Error(`La carte ${input.cardId} n'a pas pu \xEAtre ${input.archived ? "archiv\xE9e" : "reprise"}.`);
|
|
@@ -6338,48 +6430,48 @@ var kanbanRouter = router({
|
|
|
6338
6430
|
* Sans cette liste, assigner revient à deviner un identifiant — et l'agent
|
|
6339
6431
|
* n'a aucun moyen de le trouver.
|
|
6340
6432
|
*/
|
|
6341
|
-
listMembers: protectedProcedure.input(
|
|
6433
|
+
listMembers: protectedProcedure.input(z20.object({ organizationId: z20.string().max(128).optional() })).query(async ({ input }) => ({ members: await kanbanStore.members(input.organizationId) })),
|
|
6342
6434
|
/**
|
|
6343
6435
|
* Fetch one card attachment from the cloud and drop it on this machine, so an
|
|
6344
6436
|
* agent can open it with its own file tools. The bytes never go through the
|
|
6345
6437
|
* prompt: the card context lists the attachments, this returns a path.
|
|
6346
6438
|
*/
|
|
6347
|
-
downloadAttachment: protectedProcedure.input(
|
|
6348
|
-
attachmentId:
|
|
6439
|
+
downloadAttachment: protectedProcedure.input(z20.object({
|
|
6440
|
+
attachmentId: z20.string().max(128),
|
|
6349
6441
|
// La pièce jointe appartient à une organisation : la nommer dit sous quel
|
|
6350
6442
|
// compte la demander.
|
|
6351
|
-
orgId:
|
|
6443
|
+
orgId: z20.string().max(128).optional(),
|
|
6352
6444
|
// À défaut, le tableau d'où vient la carte suffit : il désigne un compte
|
|
6353
6445
|
// comme le fait n'importe quel autre appel du kanban. Sans lui, la route
|
|
6354
6446
|
// retombait sur le compte unique du poste et refusait de servir dès qu'il
|
|
6355
6447
|
// y en avait plusieurs — alors que l'agent, qui n'a qu'un identifiant de
|
|
6356
6448
|
// fichier, n'avait aucun moyen de nommer l'organisation.
|
|
6357
|
-
boardId:
|
|
6449
|
+
boardId: z20.string().max(128).optional()
|
|
6358
6450
|
})).mutation(async ({ input }) => {
|
|
6359
6451
|
const id = input.attachmentId;
|
|
6360
6452
|
if (!ATTACHMENT_ID_REGEX.test(id)) {
|
|
6361
|
-
throw new
|
|
6453
|
+
throw new TRPCError17({ code: "BAD_REQUEST", message: "Invalid attachment id" });
|
|
6362
6454
|
}
|
|
6363
6455
|
const res = await cloudFetch2(`/api/kanban/file/${id}`, {
|
|
6364
6456
|
timeoutMs: 12e4,
|
|
6365
6457
|
accountId: await attachmentScope(input)
|
|
6366
6458
|
});
|
|
6367
6459
|
if (res.status === 404) {
|
|
6368
|
-
throw new
|
|
6460
|
+
throw new TRPCError17({ code: "NOT_FOUND", message: `Attachment ${id} not found` });
|
|
6369
6461
|
}
|
|
6370
6462
|
if (!res.ok) {
|
|
6371
|
-
throw new
|
|
6463
|
+
throw new TRPCError17({
|
|
6372
6464
|
code: res.status === 403 ? "FORBIDDEN" : "INTERNAL_SERVER_ERROR",
|
|
6373
6465
|
message: `Cloud refused the attachment (HTTP ${res.status})`
|
|
6374
6466
|
});
|
|
6375
6467
|
}
|
|
6376
6468
|
const declared = Number(res.headers.get("content-length") ?? 0);
|
|
6377
6469
|
if (declared > MAX_ATTACHMENT_BYTES) {
|
|
6378
|
-
throw new
|
|
6470
|
+
throw new TRPCError17({ code: "PAYLOAD_TOO_LARGE", message: "Attachment too large to download" });
|
|
6379
6471
|
}
|
|
6380
6472
|
const body = Buffer.from(await res.arrayBuffer());
|
|
6381
6473
|
if (body.length > MAX_ATTACHMENT_BYTES) {
|
|
6382
|
-
throw new
|
|
6474
|
+
throw new TRPCError17({ code: "PAYLOAD_TOO_LARGE", message: "Attachment too large to download" });
|
|
6383
6475
|
}
|
|
6384
6476
|
const contentType = res.headers.get("content-type") ?? "application/octet-stream";
|
|
6385
6477
|
const disposition = res.headers.get("content-disposition") ?? "";
|
|
@@ -6391,21 +6483,21 @@ var kanbanRouter = router({
|
|
|
6391
6483
|
});
|
|
6392
6484
|
|
|
6393
6485
|
// ../server/src/trpc/routers/terminal.ts
|
|
6394
|
-
import { z as
|
|
6395
|
-
import { TRPCError as
|
|
6486
|
+
import { z as z21 } from "zod";
|
|
6487
|
+
import { TRPCError as TRPCError18 } from "@trpc/server";
|
|
6396
6488
|
var terminalRouter = router({
|
|
6397
6489
|
/** Create a PTY shell session on the target agent */
|
|
6398
|
-
create: protectedProcedure.input(
|
|
6399
|
-
agentId:
|
|
6400
|
-
cwd:
|
|
6490
|
+
create: protectedProcedure.input(z21.object({
|
|
6491
|
+
agentId: z21.string().max(128),
|
|
6492
|
+
cwd: z21.string().max(512).optional(),
|
|
6401
6493
|
/** Anchors the terminal when no explicit cwd is given. */
|
|
6402
|
-
serviceId:
|
|
6403
|
-
projectId:
|
|
6494
|
+
serviceId: z21.string().max(128).optional(),
|
|
6495
|
+
projectId: z21.string().max(128).optional(),
|
|
6404
6496
|
/** L'organisation du projet : un id seul peut en nommer un par organisation. */
|
|
6405
|
-
orgId:
|
|
6406
|
-
cols:
|
|
6407
|
-
rows:
|
|
6408
|
-
title:
|
|
6497
|
+
orgId: z21.string().max(128).nullable().optional(),
|
|
6498
|
+
cols: z21.number().int().min(1).max(512).optional(),
|
|
6499
|
+
rows: z21.number().int().min(1).max(256).optional(),
|
|
6500
|
+
title: z21.string().max(128).optional()
|
|
6409
6501
|
})).mutation(async ({ input }) => {
|
|
6410
6502
|
const result = await agentManager.agentMutation(
|
|
6411
6503
|
input.agentId,
|
|
@@ -6426,17 +6518,17 @@ var terminalRouter = router({
|
|
|
6426
6518
|
return result;
|
|
6427
6519
|
}),
|
|
6428
6520
|
/** Les sessions PTY encore vivantes sur l'agent (onglets à retrouver) */
|
|
6429
|
-
list: protectedProcedure.input(
|
|
6521
|
+
list: protectedProcedure.input(z21.object({ agentId: z21.string().max(128) })).query(async ({ input }) => {
|
|
6430
6522
|
return agentManager.agentQuery(
|
|
6431
6523
|
input.agentId,
|
|
6432
6524
|
"terminal.list"
|
|
6433
6525
|
);
|
|
6434
6526
|
}),
|
|
6435
6527
|
/** Renommer un onglet */
|
|
6436
|
-
rename: protectedProcedure.input(
|
|
6437
|
-
agentId:
|
|
6438
|
-
sessionId:
|
|
6439
|
-
title:
|
|
6528
|
+
rename: protectedProcedure.input(z21.object({
|
|
6529
|
+
agentId: z21.string().max(128),
|
|
6530
|
+
sessionId: z21.string().max(128),
|
|
6531
|
+
title: z21.string().max(128)
|
|
6440
6532
|
})).mutation(async ({ input }) => {
|
|
6441
6533
|
await agentManager.agentMutation(
|
|
6442
6534
|
input.agentId,
|
|
@@ -6446,10 +6538,10 @@ var terminalRouter = router({
|
|
|
6446
6538
|
return { ok: true };
|
|
6447
6539
|
}),
|
|
6448
6540
|
/** Send keystrokes / data to a PTY session */
|
|
6449
|
-
write: protectedProcedure.input(
|
|
6450
|
-
agentId:
|
|
6451
|
-
sessionId:
|
|
6452
|
-
data:
|
|
6541
|
+
write: protectedProcedure.input(z21.object({
|
|
6542
|
+
agentId: z21.string().max(128),
|
|
6543
|
+
sessionId: z21.string().max(128),
|
|
6544
|
+
data: z21.string().max(65536)
|
|
6453
6545
|
})).mutation(async ({ input }) => {
|
|
6454
6546
|
await agentManager.agentMutation(
|
|
6455
6547
|
input.agentId,
|
|
@@ -6459,11 +6551,11 @@ var terminalRouter = router({
|
|
|
6459
6551
|
return { ok: true };
|
|
6460
6552
|
}),
|
|
6461
6553
|
/** Resize the PTY */
|
|
6462
|
-
resize: protectedProcedure.input(
|
|
6463
|
-
agentId:
|
|
6464
|
-
sessionId:
|
|
6465
|
-
cols:
|
|
6466
|
-
rows:
|
|
6554
|
+
resize: protectedProcedure.input(z21.object({
|
|
6555
|
+
agentId: z21.string().max(128),
|
|
6556
|
+
sessionId: z21.string().max(128),
|
|
6557
|
+
cols: z21.number().int().min(1).max(512),
|
|
6558
|
+
rows: z21.number().int().min(1).max(256)
|
|
6467
6559
|
})).mutation(async ({ input }) => {
|
|
6468
6560
|
await agentManager.agentMutation(
|
|
6469
6561
|
input.agentId,
|
|
@@ -6473,9 +6565,9 @@ var terminalRouter = router({
|
|
|
6473
6565
|
return { ok: true };
|
|
6474
6566
|
}),
|
|
6475
6567
|
/** Destroy a PTY session */
|
|
6476
|
-
destroy: protectedProcedure.input(
|
|
6477
|
-
agentId:
|
|
6478
|
-
sessionId:
|
|
6568
|
+
destroy: protectedProcedure.input(z21.object({
|
|
6569
|
+
agentId: z21.string().max(128),
|
|
6570
|
+
sessionId: z21.string().max(128)
|
|
6479
6571
|
})).mutation(async ({ input }) => {
|
|
6480
6572
|
await agentManager.agentMutation(
|
|
6481
6573
|
input.agentId,
|
|
@@ -6485,11 +6577,11 @@ var terminalRouter = router({
|
|
|
6485
6577
|
return { ok: true };
|
|
6486
6578
|
}),
|
|
6487
6579
|
/** Stream PTY output — proxied via AgentWSBridge dynamic subscription */
|
|
6488
|
-
onData: protectedProcedure.input(
|
|
6489
|
-
agentId:
|
|
6490
|
-
sessionId:
|
|
6580
|
+
onData: protectedProcedure.input(z21.object({
|
|
6581
|
+
agentId: z21.string().max(128),
|
|
6582
|
+
sessionId: z21.string().max(128),
|
|
6491
6583
|
/** Rejouer l'historique de la session avant le flux temps réel. */
|
|
6492
|
-
replay:
|
|
6584
|
+
replay: z21.boolean().optional()
|
|
6493
6585
|
})).subscription(async function* ({ input }) {
|
|
6494
6586
|
const queue = [];
|
|
6495
6587
|
let resolve4 = null;
|
|
@@ -6516,7 +6608,7 @@ var terminalRouter = router({
|
|
|
6516
6608
|
}
|
|
6517
6609
|
);
|
|
6518
6610
|
} catch (err) {
|
|
6519
|
-
throw new
|
|
6611
|
+
throw new TRPCError18({
|
|
6520
6612
|
code: "BAD_REQUEST",
|
|
6521
6613
|
message: `Cannot subscribe to terminal: ${err.message}`
|
|
6522
6614
|
});
|
|
@@ -6538,11 +6630,11 @@ var terminalRouter = router({
|
|
|
6538
6630
|
});
|
|
6539
6631
|
|
|
6540
6632
|
// ../server/src/trpc/routers/utility.ts
|
|
6541
|
-
import { z as
|
|
6633
|
+
import { z as z22 } from "zod";
|
|
6542
6634
|
var MAX_WAIT_MS = 5 * 60 * 1e3;
|
|
6543
6635
|
var utilityRouter = router({
|
|
6544
|
-
wait: protectedProcedure.input(
|
|
6545
|
-
ms:
|
|
6636
|
+
wait: protectedProcedure.input(z22.object({
|
|
6637
|
+
ms: z22.number().int().min(0).max(MAX_WAIT_MS)
|
|
6546
6638
|
})).mutation(async ({ input }) => {
|
|
6547
6639
|
await new Promise((resolve4) => setTimeout(resolve4, input.ms));
|
|
6548
6640
|
return { waited: input.ms };
|
|
@@ -6550,15 +6642,15 @@ var utilityRouter = router({
|
|
|
6550
6642
|
});
|
|
6551
6643
|
|
|
6552
6644
|
// ../server/src/trpc/routers/wiki.ts
|
|
6553
|
-
import { z as
|
|
6554
|
-
import { TRPCError as
|
|
6645
|
+
import { z as z23 } from "zod";
|
|
6646
|
+
import { TRPCError as TRPCError19 } from "@trpc/server";
|
|
6555
6647
|
import { readFile as readFile3, readdir as readdir2, stat as stat4, realpath, writeFile as writeFile5, mkdir as mkdir6, rename, rm as rm2 } from "fs/promises";
|
|
6556
|
-
import { resolve as resolvePath, join as join8, relative, sep, dirname as dirname4 } from "path";
|
|
6557
|
-
var SCOPE_SCHEMA =
|
|
6558
|
-
|
|
6559
|
-
|
|
6648
|
+
import { resolve as resolvePath, join as join8, relative as relative2, sep, dirname as dirname4 } from "path";
|
|
6649
|
+
var SCOPE_SCHEMA = z23.union([
|
|
6650
|
+
z23.literal("global"),
|
|
6651
|
+
z23.string().regex(/^service:[A-Za-z0-9_-]{1,128}$/, "Invalid service scope")
|
|
6560
6652
|
]);
|
|
6561
|
-
var REL_PATH_SCHEMA =
|
|
6653
|
+
var REL_PATH_SCHEMA = z23.string().min(1).max(512).regex(/^[A-Za-z0-9_./-]+$/).refine((p) => !p.includes("..") && !p.startsWith("/"), {
|
|
6562
6654
|
message: "Path must be a relative path without traversal"
|
|
6563
6655
|
});
|
|
6564
6656
|
var MAX_FILES = 500;
|
|
@@ -6578,36 +6670,36 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
6578
6670
|
"target",
|
|
6579
6671
|
"out"
|
|
6580
6672
|
]);
|
|
6581
|
-
var DocsEntrySchema =
|
|
6582
|
-
|
|
6583
|
-
|
|
6673
|
+
var DocsEntrySchema = z23.lazy(() => z23.union([
|
|
6674
|
+
z23.object({ type: z23.literal("file"), name: z23.string(), path: z23.string() }),
|
|
6675
|
+
z23.object({ type: z23.literal("dir"), name: z23.string(), path: z23.string(), children: z23.array(DocsEntrySchema) })
|
|
6584
6676
|
]));
|
|
6585
6677
|
async function resolveScope(scope) {
|
|
6586
6678
|
if (scope === "global") {
|
|
6587
6679
|
const settings = await settingsManager.getSettings();
|
|
6588
6680
|
const sub = (settings.globalDocsPath?.trim() || "wiki").replace(/^[\\/]+|[\\/]+$/g, "");
|
|
6589
6681
|
if (sub.includes("..") || sub.includes("\0")) {
|
|
6590
|
-
throw new
|
|
6682
|
+
throw new TRPCError19({ code: "BAD_REQUEST", message: "Sous-chemin invalide" });
|
|
6591
6683
|
}
|
|
6592
6684
|
const dataDirAbs = resolvePath(env.DATA_DIR);
|
|
6593
6685
|
await mkdir6(dataDirAbs, { recursive: true });
|
|
6594
6686
|
const base = dirname4(await realpath(dataDirAbs));
|
|
6595
6687
|
const target = resolvePath(base, sub);
|
|
6596
6688
|
if (target !== base && !(target + sep).startsWith(base + sep)) {
|
|
6597
|
-
throw new
|
|
6689
|
+
throw new TRPCError19({ code: "FORBIDDEN", message: "Hors du dossier de donn\xE9es du serveur" });
|
|
6598
6690
|
}
|
|
6599
6691
|
await mkdir6(target, { recursive: true });
|
|
6600
6692
|
const root = await realpath(target);
|
|
6601
6693
|
if (root !== base && !(root + sep).startsWith(base + sep)) {
|
|
6602
|
-
throw new
|
|
6694
|
+
throw new TRPCError19({ code: "FORBIDDEN", message: "Le dossier wiki r\xE9sout hors du dossier de donn\xE9es" });
|
|
6603
6695
|
}
|
|
6604
6696
|
return { mode: "local", root };
|
|
6605
6697
|
}
|
|
6606
6698
|
const serviceId = scope.slice("service:".length);
|
|
6607
6699
|
const service = await serviceStore.get(serviceId);
|
|
6608
|
-
if (!service) throw new
|
|
6700
|
+
if (!service) throw new TRPCError19({ code: "NOT_FOUND", message: "Service introuvable" });
|
|
6609
6701
|
const agentId = await resolveEffectiveAgentId(service);
|
|
6610
|
-
if (!agentId) throw new
|
|
6702
|
+
if (!agentId) throw new TRPCError19({ code: "BAD_REQUEST", message: "Aucun agent configur\xE9 pour ce service" });
|
|
6611
6703
|
const cwd = await resolveWorkspaceCwd({ serviceId, override: service.docsPath?.trim() || void 0 });
|
|
6612
6704
|
return { mode: "agent", agentId, cwd };
|
|
6613
6705
|
}
|
|
@@ -6624,7 +6716,7 @@ async function walkLocal(root, dir, depth, counter) {
|
|
|
6624
6716
|
if (counter.n >= MAX_FILES) break;
|
|
6625
6717
|
if (e.name.startsWith(".") && e.name !== ".") continue;
|
|
6626
6718
|
const abs = join8(dir, e.name);
|
|
6627
|
-
const rel =
|
|
6719
|
+
const rel = relative2(root, abs).split(sep).join("/");
|
|
6628
6720
|
if (e.isDirectory()) {
|
|
6629
6721
|
if (IGNORED_DIRS.has(e.name)) continue;
|
|
6630
6722
|
const children = await walkLocal(root, abs, depth + 1, counter);
|
|
@@ -6642,7 +6734,7 @@ async function walkLocal(root, dir, depth, counter) {
|
|
|
6642
6734
|
}
|
|
6643
6735
|
async function resolveSafeLocal(root, relPath, requireExist) {
|
|
6644
6736
|
if (relPath.includes("..") || relPath.startsWith("/")) {
|
|
6645
|
-
throw new
|
|
6737
|
+
throw new TRPCError19({ code: "BAD_REQUEST", message: "Chemin invalide" });
|
|
6646
6738
|
}
|
|
6647
6739
|
const target = resolvePath(root, relPath);
|
|
6648
6740
|
if (requireExist) {
|
|
@@ -6650,10 +6742,10 @@ async function resolveSafeLocal(root, relPath, requireExist) {
|
|
|
6650
6742
|
try {
|
|
6651
6743
|
real = await realpath(target);
|
|
6652
6744
|
} catch {
|
|
6653
|
-
throw new
|
|
6745
|
+
throw new TRPCError19({ code: "NOT_FOUND", message: "Chemin introuvable" });
|
|
6654
6746
|
}
|
|
6655
6747
|
if (real !== root && !(real + sep).startsWith(root + sep)) {
|
|
6656
|
-
throw new
|
|
6748
|
+
throw new TRPCError19({ code: "FORBIDDEN", message: "Hors du dossier autoris\xE9" });
|
|
6657
6749
|
}
|
|
6658
6750
|
return real;
|
|
6659
6751
|
}
|
|
@@ -6665,12 +6757,12 @@ async function resolveSafeLocal(root, relPath, requireExist) {
|
|
|
6665
6757
|
realParent = parent;
|
|
6666
6758
|
}
|
|
6667
6759
|
if (realParent !== root && !(realParent + sep).startsWith(root + sep)) {
|
|
6668
|
-
throw new
|
|
6760
|
+
throw new TRPCError19({ code: "FORBIDDEN", message: "Hors du dossier autoris\xE9" });
|
|
6669
6761
|
}
|
|
6670
6762
|
return resolvePath(realParent, target.slice(parent.length).replace(/^[\\/]+/, ""));
|
|
6671
6763
|
}
|
|
6672
6764
|
var wikiRouter = router({
|
|
6673
|
-
list: protectedProcedure.input(
|
|
6765
|
+
list: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA })).output(z23.object({ root: z23.string(), entries: z23.array(DocsEntrySchema), truncated: z23.boolean() })).query(async ({ input }) => {
|
|
6674
6766
|
const target = await resolveScope(input.scope);
|
|
6675
6767
|
if (target.mode === "agent") {
|
|
6676
6768
|
return agentManager.agentQuery(target.agentId, "wiki.listRepoMarkdown", { cwd: target.cwd });
|
|
@@ -6679,9 +6771,9 @@ var wikiRouter = router({
|
|
|
6679
6771
|
const entries2 = await walkLocal(target.root, target.root, 0, counter);
|
|
6680
6772
|
return { root: target.root, entries: entries2, truncated: counter.n >= MAX_FILES };
|
|
6681
6773
|
}),
|
|
6682
|
-
read: protectedProcedure.input(
|
|
6774
|
+
read: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).output(z23.object({ path: z23.string(), content: z23.string() })).query(async ({ input }) => {
|
|
6683
6775
|
if (!/\.(md|markdown)$/i.test(input.path)) {
|
|
6684
|
-
throw new
|
|
6776
|
+
throw new TRPCError19({ code: "BAD_REQUEST", message: "Seuls les .md/.markdown sont autoris\xE9s" });
|
|
6685
6777
|
}
|
|
6686
6778
|
const target = await resolveScope(input.scope);
|
|
6687
6779
|
if (target.mode === "agent") {
|
|
@@ -6689,14 +6781,14 @@ var wikiRouter = router({
|
|
|
6689
6781
|
}
|
|
6690
6782
|
const file = await resolveSafeLocal(target.root, input.path, true);
|
|
6691
6783
|
const s = await stat4(file);
|
|
6692
|
-
if (!s.isFile()) throw new
|
|
6693
|
-
if (s.size > MAX_FILE_BYTES) throw new
|
|
6784
|
+
if (!s.isFile()) throw new TRPCError19({ code: "BAD_REQUEST", message: "Pas un fichier" });
|
|
6785
|
+
if (s.size > MAX_FILE_BYTES) throw new TRPCError19({ code: "PAYLOAD_TOO_LARGE", message: "Fichier trop volumineux" });
|
|
6694
6786
|
const content = await readFile3(file, "utf-8");
|
|
6695
6787
|
return { path: input.path, content };
|
|
6696
6788
|
}),
|
|
6697
|
-
write: protectedProcedure.input(
|
|
6789
|
+
write: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA, content: z23.string().max(5e6) })).mutation(async ({ input }) => {
|
|
6698
6790
|
if (!/\.(md|markdown)$/i.test(input.path)) {
|
|
6699
|
-
throw new
|
|
6791
|
+
throw new TRPCError19({ code: "BAD_REQUEST", message: "Seuls les .md/.markdown sont autoris\xE9s" });
|
|
6700
6792
|
}
|
|
6701
6793
|
const target = await resolveScope(input.scope);
|
|
6702
6794
|
if (target.mode === "agent") {
|
|
@@ -6707,9 +6799,9 @@ var wikiRouter = router({
|
|
|
6707
6799
|
await writeFile5(file, input.content, "utf-8");
|
|
6708
6800
|
return { path: input.path, ok: true };
|
|
6709
6801
|
}),
|
|
6710
|
-
createFile: protectedProcedure.input(
|
|
6802
|
+
createFile: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
|
|
6711
6803
|
if (!/\.(md|markdown)$/i.test(input.path)) {
|
|
6712
|
-
throw new
|
|
6804
|
+
throw new TRPCError19({ code: "BAD_REQUEST", message: "Seuls les .md/.markdown sont autoris\xE9s" });
|
|
6713
6805
|
}
|
|
6714
6806
|
const target = await resolveScope(input.scope);
|
|
6715
6807
|
if (target.mode === "agent") {
|
|
@@ -6718,15 +6810,15 @@ var wikiRouter = router({
|
|
|
6718
6810
|
const file = await resolveSafeLocal(target.root, input.path, false);
|
|
6719
6811
|
try {
|
|
6720
6812
|
await stat4(file);
|
|
6721
|
-
throw new
|
|
6813
|
+
throw new TRPCError19({ code: "CONFLICT", message: "Fichier d\xE9j\xE0 existant" });
|
|
6722
6814
|
} catch (err) {
|
|
6723
|
-
if (err instanceof
|
|
6815
|
+
if (err instanceof TRPCError19) throw err;
|
|
6724
6816
|
}
|
|
6725
6817
|
await mkdir6(dirname4(file), { recursive: true });
|
|
6726
6818
|
await writeFile5(file, "", "utf-8");
|
|
6727
6819
|
return { path: input.path, ok: true };
|
|
6728
6820
|
}),
|
|
6729
|
-
createDir: protectedProcedure.input(
|
|
6821
|
+
createDir: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
|
|
6730
6822
|
const target = await resolveScope(input.scope);
|
|
6731
6823
|
if (target.mode === "agent") {
|
|
6732
6824
|
return agentManager.agentMutation(target.agentId, "wiki.createDir", { cwd: target.cwd, path: input.path });
|
|
@@ -6735,7 +6827,7 @@ var wikiRouter = router({
|
|
|
6735
6827
|
await mkdir6(dir, { recursive: true });
|
|
6736
6828
|
return { path: input.path, ok: true };
|
|
6737
6829
|
}),
|
|
6738
|
-
rename: protectedProcedure.input(
|
|
6830
|
+
rename: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, fromPath: REL_PATH_SCHEMA, toPath: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
|
|
6739
6831
|
const target = await resolveScope(input.scope);
|
|
6740
6832
|
if (target.mode === "agent") {
|
|
6741
6833
|
return agentManager.agentMutation(target.agentId, "wiki.renamePath", { cwd: target.cwd, fromPath: input.fromPath, toPath: input.toPath });
|
|
@@ -6744,34 +6836,34 @@ var wikiRouter = router({
|
|
|
6744
6836
|
const to = await resolveSafeLocal(target.root, input.toPath, false);
|
|
6745
6837
|
try {
|
|
6746
6838
|
await stat4(to);
|
|
6747
|
-
throw new
|
|
6839
|
+
throw new TRPCError19({ code: "CONFLICT", message: "La cible existe d\xE9j\xE0" });
|
|
6748
6840
|
} catch (err) {
|
|
6749
|
-
if (err instanceof
|
|
6841
|
+
if (err instanceof TRPCError19) throw err;
|
|
6750
6842
|
}
|
|
6751
6843
|
await mkdir6(dirname4(to), { recursive: true });
|
|
6752
6844
|
await rename(from, to);
|
|
6753
6845
|
return { fromPath: input.fromPath, toPath: input.toPath, ok: true };
|
|
6754
6846
|
}),
|
|
6755
|
-
ensureRoot: protectedProcedure.input(
|
|
6847
|
+
ensureRoot: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA })).mutation(async ({ input }) => {
|
|
6756
6848
|
if (input.scope === "global") {
|
|
6757
6849
|
await resolveScope("global");
|
|
6758
6850
|
return { ok: true };
|
|
6759
6851
|
}
|
|
6760
6852
|
const serviceId = input.scope.slice("service:".length);
|
|
6761
6853
|
const service = await serviceStore.get(serviceId);
|
|
6762
|
-
if (!service) throw new
|
|
6854
|
+
if (!service) throw new TRPCError19({ code: "NOT_FOUND", message: "Service introuvable" });
|
|
6763
6855
|
const agentId = await resolveEffectiveAgentId(service);
|
|
6764
|
-
if (!agentId) throw new
|
|
6856
|
+
if (!agentId) throw new TRPCError19({ code: "BAD_REQUEST", message: "Aucun agent configur\xE9" });
|
|
6765
6857
|
const cwd = await resolveWorkspaceCwd({ serviceId, override: service.docsPath?.trim() || void 0 });
|
|
6766
6858
|
return agentManager.agentMutation(agentId, "wiki.ensureRoot", { cwd });
|
|
6767
6859
|
}),
|
|
6768
|
-
delete: protectedProcedure.input(
|
|
6860
|
+
delete: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
|
|
6769
6861
|
const target = await resolveScope(input.scope);
|
|
6770
6862
|
if (target.mode === "agent") {
|
|
6771
6863
|
return agentManager.agentMutation(target.agentId, "wiki.deletePath", { cwd: target.cwd, path: input.path });
|
|
6772
6864
|
}
|
|
6773
6865
|
const fp = await resolveSafeLocal(target.root, input.path, true);
|
|
6774
|
-
if (fp === target.root) throw new
|
|
6866
|
+
if (fp === target.root) throw new TRPCError19({ code: "FORBIDDEN", message: "Impossible de supprimer la racine" });
|
|
6775
6867
|
await rm2(fp, { recursive: true, force: true });
|
|
6776
6868
|
return { path: input.path, ok: true };
|
|
6777
6869
|
})
|
|
@@ -6791,8 +6883,8 @@ var configRouter = router({
|
|
|
6791
6883
|
});
|
|
6792
6884
|
|
|
6793
6885
|
// ../server/src/trpc/routers/cloud-sync.ts
|
|
6794
|
-
import { z as
|
|
6795
|
-
import { TRPCError as
|
|
6886
|
+
import { z as z24 } from "zod";
|
|
6887
|
+
import { TRPCError as TRPCError20 } from "@trpc/server";
|
|
6796
6888
|
|
|
6797
6889
|
// ../server/src/services/org-sync-scope.ts
|
|
6798
6890
|
import { access } from "fs/promises";
|
|
@@ -6878,7 +6970,7 @@ import { mkdir as mkdir7, rm as rm3 } from "fs/promises";
|
|
|
6878
6970
|
import * as Y2 from "yjs";
|
|
6879
6971
|
|
|
6880
6972
|
// ../server/src/services/sync-doc-cache.ts
|
|
6881
|
-
import { readFileSync, renameSync, rmSync, writeFileSync, mkdirSync } from "fs";
|
|
6973
|
+
import { readFileSync as readFileSync2, renameSync, rmSync, writeFileSync, mkdirSync } from "fs";
|
|
6882
6974
|
import { readFile as readFile4 } from "fs/promises";
|
|
6883
6975
|
import { dirname as dirname5, join as join10 } from "path";
|
|
6884
6976
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
@@ -6909,7 +7001,7 @@ function writeSyncDocSync(orgId, state) {
|
|
|
6909
7001
|
const path = cachePath(orgId);
|
|
6910
7002
|
let existing = null;
|
|
6911
7003
|
try {
|
|
6912
|
-
existing = new Uint8Array(
|
|
7004
|
+
existing = new Uint8Array(readFileSync2(path));
|
|
6913
7005
|
} catch {
|
|
6914
7006
|
}
|
|
6915
7007
|
mkdirSync(dirname5(path), { recursive: true });
|
|
@@ -7098,7 +7190,7 @@ var OrgSyncConnection = class {
|
|
|
7098
7190
|
}
|
|
7099
7191
|
track(work) {
|
|
7100
7192
|
this.applying.add(work);
|
|
7101
|
-
void work.finally(() => this.applying.delete(work));
|
|
7193
|
+
void work.catch((err) => console.error("[cloud-sync] \xE9chec en appliquant un changement distant :", err)).finally(() => this.applying.delete(work));
|
|
7102
7194
|
}
|
|
7103
7195
|
async settleApplying() {
|
|
7104
7196
|
while (this.applying.size > 0) await Promise.allSettled(this.applying);
|
|
@@ -8073,7 +8165,7 @@ async function assertOrgReadable(orgId) {
|
|
|
8073
8165
|
await assertOrgVaultReadable(orgId);
|
|
8074
8166
|
} catch (err) {
|
|
8075
8167
|
if (err instanceof OrgVaultUnreadableError) {
|
|
8076
|
-
throw new
|
|
8168
|
+
throw new TRPCError20({ code: "FORBIDDEN", message: err.message });
|
|
8077
8169
|
}
|
|
8078
8170
|
throw err;
|
|
8079
8171
|
}
|
|
@@ -8083,10 +8175,10 @@ async function assertOrgKey(orgId) {
|
|
|
8083
8175
|
await reconcileOrgCanary(orgId);
|
|
8084
8176
|
} catch (err) {
|
|
8085
8177
|
if (err instanceof OrgKeyMismatchError) {
|
|
8086
|
-
throw new
|
|
8178
|
+
throw new TRPCError20({ code: "FORBIDDEN", message: err.message });
|
|
8087
8179
|
}
|
|
8088
8180
|
if (err instanceof CanaryUnavailableError) {
|
|
8089
|
-
throw new
|
|
8181
|
+
throw new TRPCError20({ code: "PRECONDITION_FAILED", message: err.message });
|
|
8090
8182
|
}
|
|
8091
8183
|
throw err;
|
|
8092
8184
|
}
|
|
@@ -8103,14 +8195,14 @@ var cloudSyncRouter = router({
|
|
|
8103
8195
|
* "open this project" is the whole gesture.
|
|
8104
8196
|
*/
|
|
8105
8197
|
trackProject: protectedProcedure.input(
|
|
8106
|
-
|
|
8107
|
-
orgId:
|
|
8108
|
-
cloudUrl:
|
|
8109
|
-
projectId:
|
|
8198
|
+
z24.object({
|
|
8199
|
+
orgId: z24.string().min(1).max(128),
|
|
8200
|
+
cloudUrl: z24.string().url().max(2048),
|
|
8201
|
+
projectId: z24.string().min(1).max(128),
|
|
8110
8202
|
// Le compte sous lequel cette organisation a été listée. Tamponné avec
|
|
8111
8203
|
// elle : c'est ce qui permettra à tout appel ultérieur de retrouver le
|
|
8112
8204
|
// bon token sans qu'on ait à le lui redire.
|
|
8113
|
-
accountId:
|
|
8205
|
+
accountId: z24.string().max(64).optional()
|
|
8114
8206
|
})
|
|
8115
8207
|
).mutation(async ({ input }) => {
|
|
8116
8208
|
await assertOrgReadable(input.orgId);
|
|
@@ -8132,7 +8224,7 @@ var cloudSyncRouter = router({
|
|
|
8132
8224
|
return { pulled, state: syncState(input.orgId) };
|
|
8133
8225
|
} catch (err) {
|
|
8134
8226
|
if (err instanceof ProjectHeldElsewhereError) {
|
|
8135
|
-
throw new
|
|
8227
|
+
throw new TRPCError20({ code: "CONFLICT", message: err.code, cause: err });
|
|
8136
8228
|
}
|
|
8137
8229
|
throw err;
|
|
8138
8230
|
}
|
|
@@ -8151,22 +8243,22 @@ var cloudSyncRouter = router({
|
|
|
8151
8243
|
* fusionné.
|
|
8152
8244
|
*/
|
|
8153
8245
|
publishProject: protectedProcedure.input(
|
|
8154
|
-
|
|
8155
|
-
orgId:
|
|
8156
|
-
cloudUrl:
|
|
8157
|
-
projectId:
|
|
8246
|
+
z24.object({
|
|
8247
|
+
orgId: z24.string().min(1).max(128),
|
|
8248
|
+
cloudUrl: z24.string().url().max(2048),
|
|
8249
|
+
projectId: z24.string().min(1).max(128),
|
|
8158
8250
|
// Le compte sous lequel cette organisation a été listée. Tamponné avec
|
|
8159
8251
|
// elle : c'est ce qui permettra à tout appel ultérieur de retrouver le
|
|
8160
8252
|
// bon token sans qu'on ait à le lui redire.
|
|
8161
|
-
accountId:
|
|
8253
|
+
accountId: z24.string().max(64).optional()
|
|
8162
8254
|
})
|
|
8163
8255
|
).mutation(async ({ input }) => {
|
|
8164
8256
|
const local = await projectStore.get({ id: input.projectId, orgId: null });
|
|
8165
8257
|
if (!local) {
|
|
8166
|
-
throw new
|
|
8258
|
+
throw new TRPCError20({ code: "NOT_FOUND", message: "PROJECT_NOT_LOCAL" });
|
|
8167
8259
|
}
|
|
8168
8260
|
if (await projectStore.get({ id: input.projectId, orgId: input.orgId })) {
|
|
8169
|
-
throw new
|
|
8261
|
+
throw new TRPCError20({ code: "CONFLICT", message: "PROJECT_ALREADY_IN_ORG" });
|
|
8170
8262
|
}
|
|
8171
8263
|
await assertOrgReadable(input.orgId);
|
|
8172
8264
|
const connected = cloudSyncBridge.states().some((s) => s.orgId === input.orgId);
|
|
@@ -8191,7 +8283,7 @@ var cloudSyncRouter = router({
|
|
|
8191
8283
|
await cloudSyncBridge.followProject(input.orgId, input.projectId);
|
|
8192
8284
|
} catch (err) {
|
|
8193
8285
|
if (err instanceof ProjectHeldElsewhereError) {
|
|
8194
|
-
throw new
|
|
8286
|
+
throw new TRPCError20({ code: "CONFLICT", message: err.code, cause: err });
|
|
8195
8287
|
}
|
|
8196
8288
|
throw err;
|
|
8197
8289
|
}
|
|
@@ -8205,16 +8297,16 @@ var cloudSyncRouter = router({
|
|
|
8205
8297
|
* publié. Le cloud, lui, ne peut pas répondre — il n'a que du chiffré sous
|
|
8206
8298
|
* les yeux — donc le canari est rapatrié et jugé ici.
|
|
8207
8299
|
*/
|
|
8208
|
-
checkOrgKey: protectedProcedure.input(
|
|
8209
|
-
orgId:
|
|
8210
|
-
keyHex:
|
|
8300
|
+
checkOrgKey: protectedProcedure.input(z24.object({
|
|
8301
|
+
orgId: z24.string().min(1).max(128),
|
|
8302
|
+
keyHex: z24.string().length(64).regex(/^[0-9a-f]+$/i)
|
|
8211
8303
|
})).query(async ({ input }) => {
|
|
8212
8304
|
const res = await cloudFetch2(
|
|
8213
8305
|
`/api/auth/organization/canary?organizationId=${encodeURIComponent(input.orgId)}`,
|
|
8214
8306
|
{ method: "GET", accountId: await accountForOrgScope(input.orgId) }
|
|
8215
8307
|
);
|
|
8216
8308
|
if (!res.ok) {
|
|
8217
|
-
throw new
|
|
8309
|
+
throw new TRPCError20({ code: "BAD_GATEWAY", message: await cloudMessage(res) || `CANARY_READ_FAILED (${res.status})` });
|
|
8218
8310
|
}
|
|
8219
8311
|
const { canary } = await res.json();
|
|
8220
8312
|
if (typeof canary !== "string") return { matches: false, published: false };
|
|
@@ -8229,7 +8321,7 @@ var cloudSyncRouter = router({
|
|
|
8229
8321
|
* que l'interface puisse proposer ni l'adoption ni la remise à zéro — le seul
|
|
8230
8322
|
* chemin qui la répare. Rien n'est écrit ici, ni sur le disque ni sur le cloud.
|
|
8231
8323
|
*/
|
|
8232
|
-
orgKeyStatus: protectedProcedure.input(
|
|
8324
|
+
orgKeyStatus: protectedProcedure.input(z24.object({ orgId: z24.string().min(1).max(128) })).query(async ({ input }) => {
|
|
8233
8325
|
const key = getEncryptionKey(input.orgId);
|
|
8234
8326
|
if (!key) return { status: "unknown" };
|
|
8235
8327
|
const res = await cloudFetch2(
|
|
@@ -8252,10 +8344,10 @@ var cloudSyncRouter = router({
|
|
|
8252
8344
|
* Bornée au coffre de l'organisation et à ceux de ses projets : les autres
|
|
8253
8345
|
* organisations et la machine gardent leur clé.
|
|
8254
8346
|
*/
|
|
8255
|
-
adoptOrgKey: protectedProcedure.input(
|
|
8256
|
-
orgId:
|
|
8257
|
-
keyHex:
|
|
8258
|
-
masterPassword:
|
|
8347
|
+
adoptOrgKey: protectedProcedure.input(z24.object({
|
|
8348
|
+
orgId: z24.string().min(1).max(128),
|
|
8349
|
+
keyHex: z24.string().length(64).regex(/^[0-9a-f]+$/i),
|
|
8350
|
+
masterPassword: z24.string().min(1).max(1024)
|
|
8259
8351
|
})).mutation(async ({ input }) => {
|
|
8260
8352
|
const report = await adoptOrgKey(
|
|
8261
8353
|
input.orgId,
|
|
@@ -8285,14 +8377,14 @@ var cloudSyncRouter = router({
|
|
|
8285
8377
|
* Ce qui a déjà été synchronisé sous l'ancienne clé de référence reste
|
|
8286
8378
|
* illisible : aucune clé d'ici ne l'ouvre. L'interface le dit avant.
|
|
8287
8379
|
*/
|
|
8288
|
-
resetOrgKey: protectedProcedure.input(
|
|
8289
|
-
orgId:
|
|
8290
|
-
masterPassword:
|
|
8380
|
+
resetOrgKey: protectedProcedure.input(z24.object({
|
|
8381
|
+
orgId: z24.string().min(1).max(128),
|
|
8382
|
+
masterPassword: z24.string().min(1).max(1024)
|
|
8291
8383
|
})).mutation(async ({ input }) => {
|
|
8292
8384
|
const { keyHex } = await resetOrgKeyToFresh(input.orgId, input.masterPassword, /* @__PURE__ */ new Date());
|
|
8293
8385
|
const canary = await readVerifyFile(orgVaultRoot(input.orgId));
|
|
8294
8386
|
if (!canary) {
|
|
8295
|
-
throw new
|
|
8387
|
+
throw new TRPCError20({
|
|
8296
8388
|
code: "PRECONDITION_FAILED",
|
|
8297
8389
|
message: "Le coffre de cette organisation n'a pas re\xE7u sa cl\xE9."
|
|
8298
8390
|
});
|
|
@@ -8304,7 +8396,7 @@ var cloudSyncRouter = router({
|
|
|
8304
8396
|
});
|
|
8305
8397
|
if (!res.ok) {
|
|
8306
8398
|
const detail = await cloudMessage(res);
|
|
8307
|
-
throw new
|
|
8399
|
+
throw new TRPCError20({
|
|
8308
8400
|
code: res.status === 403 ? "FORBIDDEN" : "BAD_GATEWAY",
|
|
8309
8401
|
message: detail || `ORG_KEY_RESET_FAILED (${res.status})`
|
|
8310
8402
|
});
|
|
@@ -8320,9 +8412,9 @@ var cloudSyncRouter = router({
|
|
|
8320
8412
|
* every other member and for this one on another machine.
|
|
8321
8413
|
*/
|
|
8322
8414
|
untrackProject: protectedProcedure.input(
|
|
8323
|
-
|
|
8324
|
-
orgId:
|
|
8325
|
-
projectId:
|
|
8415
|
+
z24.object({
|
|
8416
|
+
orgId: z24.string().min(1).max(128),
|
|
8417
|
+
projectId: z24.string().min(1).max(128)
|
|
8326
8418
|
})
|
|
8327
8419
|
).mutation(async ({ input }) => {
|
|
8328
8420
|
await cloudSyncBridge.unfollowProject(input.orgId, input.projectId);
|
|
@@ -8338,9 +8430,9 @@ var cloudSyncRouter = router({
|
|
|
8338
8430
|
* membre, ce qui évite d'effacer les fichiers si le cloud a refusé l'ordre.
|
|
8339
8431
|
*/
|
|
8340
8432
|
deleteProjectFromOrg: protectedProcedure.input(
|
|
8341
|
-
|
|
8342
|
-
orgId:
|
|
8343
|
-
projectId:
|
|
8433
|
+
z24.object({
|
|
8434
|
+
orgId: z24.string().min(1).max(128),
|
|
8435
|
+
projectId: z24.string().min(1).max(128)
|
|
8344
8436
|
})
|
|
8345
8437
|
).mutation(async ({ input }) => {
|
|
8346
8438
|
let res;
|
|
@@ -8353,8 +8445,8 @@ var cloudSyncRouter = router({
|
|
|
8353
8445
|
accountId: await accountForOrgScope(input.orgId)
|
|
8354
8446
|
});
|
|
8355
8447
|
} catch (err) {
|
|
8356
|
-
if (err instanceof
|
|
8357
|
-
throw new
|
|
8448
|
+
if (err instanceof TRPCError20 && err.code === "PRECONDITION_FAILED") {
|
|
8449
|
+
throw new TRPCError20({ code: "PRECONDITION_FAILED", message: "CLOUD_NOT_LINKED" });
|
|
8358
8450
|
}
|
|
8359
8451
|
throw err;
|
|
8360
8452
|
}
|
|
@@ -8362,12 +8454,12 @@ var cloudSyncRouter = router({
|
|
|
8362
8454
|
const detail = await cloudMessage(res);
|
|
8363
8455
|
if (res.status === 403) {
|
|
8364
8456
|
const code = /not a member/i.test(detail) ? "PROJECT_NOT_MEMBER" : "PROJECT_DELETE_FORBIDDEN";
|
|
8365
|
-
throw new
|
|
8457
|
+
throw new TRPCError20({ code: "FORBIDDEN", message: code, cause: new Error(detail) });
|
|
8366
8458
|
}
|
|
8367
8459
|
if (res.status === 404) {
|
|
8368
|
-
throw new
|
|
8460
|
+
throw new TRPCError20({ code: "NOT_FOUND", message: "PROJECT_NOT_IN_ORG", cause: new Error(detail) });
|
|
8369
8461
|
}
|
|
8370
|
-
throw new
|
|
8462
|
+
throw new TRPCError20({
|
|
8371
8463
|
code: "BAD_GATEWAY",
|
|
8372
8464
|
message: detail || `PROJECT_DELETE_FAILED (${res.status})`
|
|
8373
8465
|
});
|
|
@@ -8396,10 +8488,10 @@ var cloudSyncRouter = router({
|
|
|
8396
8488
|
* d'un autre. Même forme que `rememberOrgNames`, et pour la même raison :
|
|
8397
8489
|
* ce que seul le cloud sait, on le recopie quand il le dit.
|
|
8398
8490
|
*/
|
|
8399
|
-
rememberOrgAccounts: protectedProcedure.input(
|
|
8400
|
-
orgs:
|
|
8401
|
-
orgId:
|
|
8402
|
-
accountId:
|
|
8491
|
+
rememberOrgAccounts: protectedProcedure.input(z24.object({
|
|
8492
|
+
orgs: z24.array(z24.object({
|
|
8493
|
+
orgId: z24.string().min(1).max(128),
|
|
8494
|
+
accountId: z24.string().min(1).max(64)
|
|
8403
8495
|
})).max(200)
|
|
8404
8496
|
})).mutation(async ({ input }) => {
|
|
8405
8497
|
const candidates = /* @__PURE__ */ new Map();
|
|
@@ -8426,10 +8518,10 @@ var cloudSyncRouter = router({
|
|
|
8426
8518
|
}
|
|
8427
8519
|
return { remembered };
|
|
8428
8520
|
}),
|
|
8429
|
-
rememberOrgNames: protectedProcedure.input(
|
|
8430
|
-
orgs:
|
|
8431
|
-
orgId:
|
|
8432
|
-
name:
|
|
8521
|
+
rememberOrgNames: protectedProcedure.input(z24.object({
|
|
8522
|
+
orgs: z24.array(z24.object({
|
|
8523
|
+
orgId: z24.string().min(1).max(128),
|
|
8524
|
+
name: z24.string().min(1).max(100)
|
|
8433
8525
|
})).max(200)
|
|
8434
8526
|
})).mutation(async ({ input }) => {
|
|
8435
8527
|
for (const { orgId, name } of input.orgs) await rememberOrgName(orgId, name);
|
|
@@ -8459,7 +8551,7 @@ var cloudSyncRouter = router({
|
|
|
8459
8551
|
* Un 404 efface le logo retenu — l'organisation en a retiré un — tandis qu'un
|
|
8460
8552
|
* réseau muet laisse en place ce qui est déjà là : hors ligne, on garde.
|
|
8461
8553
|
*/
|
|
8462
|
-
cacheOrgLogos: protectedProcedure.input(
|
|
8554
|
+
cacheOrgLogos: protectedProcedure.input(z24.object({ orgIds: z24.array(z24.string().min(1).max(128)).max(200) })).mutation(async ({ input }) => {
|
|
8463
8555
|
let cached = 0;
|
|
8464
8556
|
for (const orgId of input.orgIds) {
|
|
8465
8557
|
const base = await orgCloudUrl(orgId);
|
|
@@ -8517,14 +8609,14 @@ var cloudSyncRouter = router({
|
|
|
8517
8609
|
// ../server/src/trpc/routers/filesystem.ts
|
|
8518
8610
|
import { readdir as readdir4, stat as stat6 } from "fs/promises";
|
|
8519
8611
|
import { homedir as homedir3 } from "os";
|
|
8520
|
-
import { isAbsolute as
|
|
8521
|
-
import { z as
|
|
8612
|
+
import { isAbsolute as isAbsolute3, join as join12, resolve as resolve2, dirname as dirname6, basename } from "path";
|
|
8613
|
+
import { z as z25 } from "zod";
|
|
8522
8614
|
var MAX_ENTRIES2 = 500;
|
|
8523
|
-
var PATH_SCHEMA =
|
|
8615
|
+
var PATH_SCHEMA = z25.string().min(1).max(4096);
|
|
8524
8616
|
function safeResolve(input) {
|
|
8525
8617
|
if (input.includes("\0")) throw new Error("Chemin invalide");
|
|
8526
8618
|
const expanded = input.startsWith("~") ? join12(homedir3(), input.slice(1)) : input;
|
|
8527
|
-
if (!
|
|
8619
|
+
if (!isAbsolute3(expanded)) throw new Error("Le chemin doit \xEAtre absolu");
|
|
8528
8620
|
return resolve2(expanded);
|
|
8529
8621
|
}
|
|
8530
8622
|
var filesystemRouter = router({
|
|
@@ -8544,7 +8636,7 @@ var filesystemRouter = router({
|
|
|
8544
8636
|
* Hidden entries are skipped: they are noise for choosing a workspace, and
|
|
8545
8637
|
* `.runeya` itself would invite placing a project inside another's data.
|
|
8546
8638
|
*/
|
|
8547
|
-
listDirectories: protectedProcedure.input(
|
|
8639
|
+
listDirectories: protectedProcedure.input(z25.object({ path: PATH_SCHEMA })).query(async ({ input }) => {
|
|
8548
8640
|
const dir = safeResolve(input.path);
|
|
8549
8641
|
const info = await stat6(dir).catch(() => null);
|
|
8550
8642
|
if (!info?.isDirectory()) {
|
|
@@ -8577,7 +8669,7 @@ var filesystemRouter = router({
|
|
|
8577
8669
|
* qui s'y trouve, ou rien (déjà dans la liste, lié à un projet d'organisation,
|
|
8578
8670
|
* racine machine).
|
|
8579
8671
|
*/
|
|
8580
|
-
checkLocalProjectTarget: protectedProcedure.input(
|
|
8672
|
+
checkLocalProjectTarget: protectedProcedure.input(z25.object({ path: PATH_SCHEMA })).query(async ({ input }) => {
|
|
8581
8673
|
const dir = safeResolve(input.path);
|
|
8582
8674
|
const info = await stat6(dir).catch(() => null);
|
|
8583
8675
|
if (!info?.isDirectory()) return { path: dir, status: "missing", projectName: null };
|
|
@@ -8589,10 +8681,10 @@ var filesystemRouter = router({
|
|
|
8589
8681
|
* Two projects in one directory is what makes services run against the wrong
|
|
8590
8682
|
* code, so a directory that already holds one is refused outright.
|
|
8591
8683
|
*/
|
|
8592
|
-
checkProjectTarget: protectedProcedure.input(
|
|
8684
|
+
checkProjectTarget: protectedProcedure.input(z25.object({
|
|
8593
8685
|
path: PATH_SCHEMA,
|
|
8594
|
-
projectId:
|
|
8595
|
-
orgId:
|
|
8686
|
+
projectId: z25.string().min(1).max(128),
|
|
8687
|
+
orgId: z25.string().max(128).nullable().optional()
|
|
8596
8688
|
})).query(async ({ input }) => {
|
|
8597
8689
|
const dir = safeResolve(input.path);
|
|
8598
8690
|
const info = await stat6(dir).catch(() => null);
|
|
@@ -8611,13 +8703,13 @@ var filesystemRouter = router({
|
|
|
8611
8703
|
});
|
|
8612
8704
|
|
|
8613
8705
|
// ../server/src/trpc/routers/desktop.ts
|
|
8614
|
-
import { z as
|
|
8615
|
-
import { isAbsolute as
|
|
8616
|
-
import { TRPCError as
|
|
8706
|
+
import { z as z26 } from "zod";
|
|
8707
|
+
import { isAbsolute as isAbsolute5 } from "path";
|
|
8708
|
+
import { TRPCError as TRPCError21 } from "@trpc/server";
|
|
8617
8709
|
|
|
8618
8710
|
// ../server/src/services/desktop-launcher.ts
|
|
8619
8711
|
import { execFile as execFile4 } from "child_process";
|
|
8620
|
-
import { isAbsolute as
|
|
8712
|
+
import { isAbsolute as isAbsolute4 } from "path";
|
|
8621
8713
|
import { promisify as promisify3 } from "util";
|
|
8622
8714
|
var exec = promisify3(execFile4);
|
|
8623
8715
|
var INSTALL_TIMEOUT_MS2 = 3e4;
|
|
@@ -8638,7 +8730,7 @@ function suggestLauncher() {
|
|
|
8638
8730
|
}
|
|
8639
8731
|
async function installLauncher(input) {
|
|
8640
8732
|
if (!cli) throw new Error("launcher-unavailable");
|
|
8641
|
-
if (input.workingDir !== null && !
|
|
8733
|
+
if (input.workingDir !== null && !isAbsolute4(input.workingDir)) {
|
|
8642
8734
|
throw new Error("launcher-working-dir-not-absolute");
|
|
8643
8735
|
}
|
|
8644
8736
|
const args = [
|
|
@@ -8696,43 +8788,43 @@ var desktopRouter = router({
|
|
|
8696
8788
|
* dessinée par le serveur. Sans cette annonce elle garderait la teinte figée
|
|
8697
8789
|
* du fichier livré avec le CLI. Rien n'est persisté — voir `tray-accent.ts`.
|
|
8698
8790
|
*/
|
|
8699
|
-
setTrayAccent: protectedProcedure.input(
|
|
8791
|
+
setTrayAccent: protectedProcedure.input(z26.object({
|
|
8700
8792
|
// `#rgb` ou `#rrggbb` : la forme est vérifiée ici, pas rabotée plus loin.
|
|
8701
|
-
color:
|
|
8793
|
+
color: z26.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/).nullable()
|
|
8702
8794
|
})).mutation(({ input }) => {
|
|
8703
8795
|
setTrayAccent(input.color);
|
|
8704
8796
|
return { ok: true };
|
|
8705
8797
|
}),
|
|
8706
|
-
installLauncher: protectedProcedure.input(
|
|
8707
|
-
name:
|
|
8798
|
+
installLauncher: protectedProcedure.input(z26.object({
|
|
8799
|
+
name: z26.string().trim().min(1).max(120),
|
|
8708
8800
|
/**
|
|
8709
8801
|
* `null` : raccourci global, sans dossier épinglé. Sinon un chemin
|
|
8710
8802
|
* absolu, refusé autrement : le dossier devient un argument positionnel
|
|
8711
8803
|
* du CLI, où une valeur commençant par un tiret passerait pour un drapeau.
|
|
8712
8804
|
*/
|
|
8713
|
-
workingDir:
|
|
8805
|
+
workingDir: z26.string().max(4096).refine(isAbsolute5, {
|
|
8714
8806
|
message: "Le dossier de travail doit \xEAtre un chemin absolu."
|
|
8715
8807
|
}).nullable(),
|
|
8716
|
-
port:
|
|
8717
|
-
agentPort:
|
|
8808
|
+
port: z26.number().int().min(1).max(65535),
|
|
8809
|
+
agentPort: z26.number().int().min(1).max(65535)
|
|
8718
8810
|
})).mutation(async ({ input }) => {
|
|
8719
8811
|
try {
|
|
8720
8812
|
return await installLauncher(input);
|
|
8721
8813
|
} catch (err) {
|
|
8722
8814
|
const message = err.message;
|
|
8723
8815
|
if (message === "launcher-working-dir-not-absolute") {
|
|
8724
|
-
throw new
|
|
8816
|
+
throw new TRPCError21({
|
|
8725
8817
|
code: "BAD_REQUEST",
|
|
8726
8818
|
message: "Le dossier de travail doit \xEAtre un chemin absolu."
|
|
8727
8819
|
});
|
|
8728
8820
|
}
|
|
8729
8821
|
if (message === "launcher-unavailable") {
|
|
8730
|
-
throw new
|
|
8822
|
+
throw new TRPCError21({
|
|
8731
8823
|
code: "PRECONDITION_FAILED",
|
|
8732
8824
|
message: "Cette Runeya ne sait pas o\xF9 trouver son CLI : cr\xE9ez le raccourci avec `runeya install-desktop`."
|
|
8733
8825
|
});
|
|
8734
8826
|
}
|
|
8735
|
-
throw new
|
|
8827
|
+
throw new TRPCError21({ code: "INTERNAL_SERVER_ERROR", message });
|
|
8736
8828
|
}
|
|
8737
8829
|
})
|
|
8738
8830
|
});
|
|
@@ -8740,6 +8832,7 @@ var desktopRouter = router({
|
|
|
8740
8832
|
// ../server/src/trpc/router.ts
|
|
8741
8833
|
var appRouter = router({
|
|
8742
8834
|
health: healthRouter,
|
|
8835
|
+
session: sessionRouter,
|
|
8743
8836
|
appUpdate: appUpdateRouter,
|
|
8744
8837
|
changelog: changelogRouter,
|
|
8745
8838
|
diagnostics: diagnosticsRouter,
|
|
@@ -10184,6 +10277,13 @@ var authLimiter = rateLimit({
|
|
|
10184
10277
|
legacyHeaders: false,
|
|
10185
10278
|
message: { error: "Trop de tentatives. R\xE9essayez dans 15 minutes." }
|
|
10186
10279
|
});
|
|
10280
|
+
var refreshLimiter = rateLimit({
|
|
10281
|
+
windowMs: 60 * 1e3,
|
|
10282
|
+
max: 120,
|
|
10283
|
+
standardHeaders: true,
|
|
10284
|
+
legacyHeaders: false,
|
|
10285
|
+
message: { error: "Trop de renouvellements de session." }
|
|
10286
|
+
});
|
|
10187
10287
|
var llmsLimiter = rateLimit({
|
|
10188
10288
|
windowMs: 60 * 1e3,
|
|
10189
10289
|
// 1 minute
|
|
@@ -10553,6 +10653,17 @@ async function createLocalServer() {
|
|
|
10553
10653
|
});
|
|
10554
10654
|
proxyCloudStream("/api/cloud/chat/stream", "/api/chat/stream");
|
|
10555
10655
|
proxyCloudStream("/api/cloud/kanban/stream", "/api/kanban/stream");
|
|
10656
|
+
app.post("/api/auth/refresh", refreshLimiter, async (req, res) => {
|
|
10657
|
+
const auth = req.headers.authorization;
|
|
10658
|
+
const jwt = typeof auth === "string" && auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
10659
|
+
try {
|
|
10660
|
+
await verifyJwt(jwt);
|
|
10661
|
+
} catch {
|
|
10662
|
+
res.status(401).json({ error: "Session invalide" });
|
|
10663
|
+
return;
|
|
10664
|
+
}
|
|
10665
|
+
res.json({ token: await signJwt() });
|
|
10666
|
+
});
|
|
10556
10667
|
app.post("/api/unlock", authLimiter, csrfMiddleware, express.json({ limit: "10kb" }), async (req, res) => {
|
|
10557
10668
|
try {
|
|
10558
10669
|
const body = req.body;
|
|
@@ -10848,12 +10959,22 @@ async function createLocalServer() {
|
|
|
10848
10959
|
applyWSSHandler({
|
|
10849
10960
|
wss,
|
|
10850
10961
|
router: appRouter,
|
|
10851
|
-
createContext: ({ req }) => {
|
|
10962
|
+
createContext: ({ req, res: ws }) => {
|
|
10852
10963
|
const headerAuth = req.headers.authorization ?? null;
|
|
10853
10964
|
const urlObj = new URL(req.url ?? "/", `http://localhost:${env.PORT}`);
|
|
10854
10965
|
const queryToken = urlObj.searchParams.get("token");
|
|
10855
10966
|
const queryAuth = queryToken ? `Bearer ${queryToken}` : null;
|
|
10856
|
-
|
|
10967
|
+
let authorization = headerAuth ?? queryAuth;
|
|
10968
|
+
return {
|
|
10969
|
+
get authorization() {
|
|
10970
|
+
return authorization;
|
|
10971
|
+
},
|
|
10972
|
+
renewSocketToken: async (token) => {
|
|
10973
|
+
const { exp, iat } = await verifyJwt(token);
|
|
10974
|
+
authorization = `Bearer ${token}`;
|
|
10975
|
+
if (ws.readyState === ws.OPEN) wsConnRegistry.set(ws, { exp, iat });
|
|
10976
|
+
}
|
|
10977
|
+
};
|
|
10857
10978
|
}
|
|
10858
10979
|
});
|
|
10859
10980
|
const wsConnRegistry = /* @__PURE__ */ new Map();
|
|
@@ -10931,7 +11052,7 @@ async function createLocalServer() {
|
|
|
10931
11052
|
]);
|
|
10932
11053
|
console.log("[server] Shutdown complete");
|
|
10933
11054
|
};
|
|
10934
|
-
const { initCompanion } = await import("./companion-
|
|
11055
|
+
const { initCompanion } = await import("./companion-EVCS2HNC.js");
|
|
10935
11056
|
initCompanion();
|
|
10936
11057
|
return { app, server, wss, shutdown };
|
|
10937
11058
|
}
|
|
@@ -11361,21 +11482,29 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
11361
11482
|
}
|
|
11362
11483
|
export {
|
|
11363
11484
|
PullEnvError,
|
|
11485
|
+
compareSemver,
|
|
11364
11486
|
createLocalServer,
|
|
11487
|
+
globalCliEntry,
|
|
11488
|
+
installPrefix,
|
|
11489
|
+
installRuneyaGlobally,
|
|
11490
|
+
installedRuneya,
|
|
11365
11491
|
killInstance,
|
|
11366
11492
|
listInstances,
|
|
11367
11493
|
listTrayServices,
|
|
11368
11494
|
networkInfo,
|
|
11369
11495
|
onTrayServicesChanged,
|
|
11496
|
+
probeVersion,
|
|
11370
11497
|
pullEnv,
|
|
11371
11498
|
registerCli as registerCliLocation,
|
|
11372
11499
|
registerInstance,
|
|
11500
|
+
resolveNpm,
|
|
11373
11501
|
resolveTrayIcon,
|
|
11374
11502
|
restartInstance,
|
|
11375
11503
|
restartProcess,
|
|
11376
11504
|
scheduleSelfRestart,
|
|
11377
11505
|
setLanMode,
|
|
11378
11506
|
startProcess,
|
|
11379
|
-
stopProcess
|
|
11507
|
+
stopProcess,
|
|
11508
|
+
userPrefix
|
|
11380
11509
|
};
|
|
11381
|
-
//# sourceMappingURL=src-
|
|
11510
|
+
//# sourceMappingURL=src-ZSQISY6W.js.map
|