@runeya/runeya 2.0.146 → 2.0.148
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 +10 -4
- package/agent/index.js.map +1 -1
- package/{agent-manager-R7BPQO6D.js → agent-manager-46C4NIQ2.js} +4 -2
- package/{chat-LPML22J5.js → chat-CMNDMXIP.js} +9 -5
- package/{chunk-SEPAURTQ.js → chunk-2NE52CL5.js} +156 -923
- package/chunk-2NE52CL5.js.map +1 -0
- package/chunk-4D3GCBPO.js +462 -0
- package/chunk-4D3GCBPO.js.map +1 -0
- package/{chunk-KV4GJKXU.js → chunk-5XG3X2TM.js} +27 -1479
- package/chunk-5XG3X2TM.js.map +1 -0
- package/chunk-7QXP4FXD.js +969 -0
- package/chunk-7QXP4FXD.js.map +1 -0
- package/chunk-DA2DZBTL.js +43 -0
- package/chunk-DA2DZBTL.js.map +1 -0
- package/chunk-DFPZ4MTI.js +1041 -0
- package/chunk-DFPZ4MTI.js.map +1 -0
- package/{chunk-UD42LACH.js → chunk-JNK6H5KH.js} +164 -1007
- package/chunk-JNK6H5KH.js.map +1 -0
- package/chunk-WDHLEO5S.js +809 -0
- package/chunk-WDHLEO5S.js.map +1 -0
- package/{companion-W3UMZWGH.js → companion-XKMNRIJB.js} +8 -3
- package/environment-resolver-ZDTF3MHI.js +18 -0
- package/environment-resolver-ZDTF3MHI.js.map +1 -0
- package/index.js +7 -7
- package/{install-gate-PVMGJDWE.js → install-gate-PWPGC63Z.js} +2 -2
- package/mcp-ready-TAPBAOZY.js +9 -0
- package/mcp-ready-TAPBAOZY.js.map +1 -0
- package/mcp-server/index.js +24 -0
- package/mcp-server/index.js.map +1 -1
- package/mcp-server/launch.d.ts +6 -0
- package/mcp-server/launch.js +2 -1
- package/mcp-server/launch.js.map +1 -1
- package/package.json +1 -1
- package/project-store-FLXTAD3J.js +23 -0
- package/project-store-FLXTAD3J.js.map +1 -0
- package/{src-LEDGW5KP.js → src-I5TPC6GW.js} +155 -63
- package/src-I5TPC6GW.js.map +1 -0
- package/web/assets/{AgentScalarChatInterface.vue-BYszptaB.js → AgentScalarChatInterface.vue-DKBinB_l.js} +2 -2
- package/web/assets/AgentTerminalPage-CixU7x6O.js +1 -0
- package/web/assets/AiChatModals-BvGUd-QD.css +1 -0
- package/web/assets/AiChatModals-C-hXImkO.js +4 -0
- package/web/assets/AppLayout-At9wk0sx.js +1 -0
- package/web/assets/{AppLayout-tsw5Ow0w.css → AppLayout-BlhwflMq.css} +1 -1
- package/web/assets/AuditPage-BkqbHxjY.js +1 -0
- package/web/assets/CloudAccountPage-D5C2SCS9.js +1 -0
- package/web/assets/CloudMessagingPage-Bi57GGHO.js +2 -0
- package/web/assets/DashboardPage-dEQop9ql.js +1 -0
- package/web/assets/DirectoryPickerDialog-DIMSeDhw.js +1 -0
- package/web/assets/{EditorIcon-CypGLV6w.js → EditorIcon-W3Szca4J.js} +1 -1
- package/web/assets/EnvironmentForm-C5emavNh.js +1 -0
- package/web/assets/EnvironmentFormPage-BIoy8fsW.js +1 -0
- package/web/assets/EnvironmentListPage-BDYeh7XF.js +1 -0
- package/web/assets/{FlagChip-R_T5qYpL.js → FlagChip-DPtIBCjQ.js} +1 -1
- package/web/assets/KanbanBoardPage-BlCcjBwV.js +4 -0
- package/web/assets/KanbanListPage-DbXONxfC.js +1 -0
- package/web/assets/LaunchProjectPage-Cg92hHwQ.js +1 -0
- package/web/assets/LaunchServicesPage-CO8gOB9D.js +1 -0
- package/web/assets/LaunchServicesPage-h5o3zLcy.css +1 -0
- package/web/assets/{LaunchShell-DUrSunB4.js → LaunchShell-CDb21ihZ.js} +1 -1
- package/web/assets/LogViewer-DsusKk2b.js +3 -0
- package/web/assets/OrgKeyPrompt-CX9K7r9I.js +1 -0
- package/web/assets/ProcessControls-B7hWVqiR.css +1 -0
- package/web/assets/ProcessControls-TEZutuxq.js +1 -0
- package/web/assets/ProcessDetailPage-Cj23FECq.js +1 -0
- package/web/assets/ProcessMetrics-1P6iE2Il.js +1 -0
- package/web/assets/ProjectCreateDialog-D3aMWKfw.js +1 -0
- package/web/assets/{ProjectLocationGate-DhMVdIjN.js → ProjectLocationGate-DU0Jj8Nw.js} +1 -1
- package/web/assets/ProjectPublishDialog-D7sNKei3.js +1 -0
- package/web/assets/{RandomJobTestPage-BUS2Ty6-.js → RandomJobTestPage-DQUMRHyf.js} +1 -1
- package/web/assets/{ScenarioEditorPage-vMoRt0Qn.css → ScenarioEditorPage-BSjSX4bw.css} +1 -1
- package/web/assets/ScenarioEditorPage-DvCdcY9m.js +14 -0
- package/web/assets/SendToChatDialog-BdWKjYmv.js +1 -0
- package/web/assets/ServiceControlsBar-B_J5m-Kc.js +1 -0
- package/web/assets/ServiceDetailPage-DhJcjFfY.js +2 -0
- package/web/assets/{ServiceDetailPage-COdaCZ6r.css → ServiceDetailPage-DnSf5cm9.css} +1 -1
- package/web/assets/ServiceFormPage-fp8NCHSv.js +1 -0
- package/web/assets/{SettingsPage-4pKUgKPm.css → SettingsPage-NH2fQap4.css} +1 -1
- package/web/assets/SettingsPage-s28BodWe.js +2086 -0
- package/web/assets/UnlockPage-BXXIPHW_.js +1 -0
- package/web/assets/VaultMigrationPage-BfNuKtkJ.js +1 -0
- package/web/assets/{VersionBadge-BmArk24p.js → VersionBadge-CW7czacg.js} +1 -1
- package/web/assets/{WikiPage-DADu8WUA.js → WikiPage-BiwpCM3E.js} +1 -1
- package/web/assets/WikiView-CMch_06Z.js +1 -0
- package/web/assets/{add-scalar-classes-dWEWPlFD.js → add-scalar-classes-Ckanjq1h.js} +14 -14
- package/web/assets/{agent.store-CKLAkF9a.js → agent.store-piokQ9Dt.js} +1 -1
- package/web/assets/{auth-Cg9BWTfK.js → auth-BT_jf-5e.js} +2 -2
- package/web/assets/cloud-chat.store-CDKh4tIy.js +1 -0
- package/web/assets/{cloud-client-D_zCiW0s.js → cloud-client-CzPdZLDq.js} +2 -2
- package/web/assets/{cloud-session-DOaDpZTy.js → cloud-session-BYfn6YM4.js} +2 -2
- package/web/assets/floating-ui.dom-DF3oyfHd.js +1 -0
- package/web/assets/i18n-D0FT7rCj.js +1 -0
- package/web/assets/index-3LTzgeU-.css +1 -0
- package/web/assets/index-Ci2CNnit.js +309 -0
- package/web/assets/modal-iRaP1izE.js +5 -0
- package/web/assets/notification.store-CvvuTBVz.js +1 -0
- package/web/assets/{project-launch.store-y7JBMNiE.js → project-launch.store-CTmbl8UY.js} +1 -1
- package/web/assets/{project.store-DGJr0Qr9.js → project.store-CNC6_4r4.js} +1 -1
- package/web/assets/runtime-dom.esm-bundler-CyEVRK6B.js +1 -0
- package/web/assets/scenario.store-BIFQPXgl.js +7 -0
- package/web/assets/{service.store-5jWsJxPx.js → service.store-B40U4QzI.js} +1 -1
- package/web/assets/settings-scope-CyWMCjRY.js +1 -0
- package/web/assets/settings.store-Dw6bAoWE.js +1 -0
- package/web/assets/{src-Dnwk6NpV.js → src-CrNi4Pjg.js} +3 -3
- package/web/assets/src-DIBHat7I.js +3 -0
- package/web/assets/src-DtCW_XCh.js +1 -0
- package/web/assets/{trpc-Drabzwpd.js → trpc-BAZGRZBD.js} +3 -3
- package/web/assets/{useCloudOrg-BAQEtFPl.js → useCloudOrg-EnXFBwt1.js} +1 -1
- package/web/assets/useEnvVarAutocomplete-BxJh2Lw0.js +1 -0
- package/web/assets/useProjectCatalog-BrvUGv9A.js +1 -0
- package/web/assets/{useProjectCloudOrg-CEN9-_OA.js → useProjectCloudOrg-Czxlg00k.js} +1 -1
- package/web/assets/{useServiceUrlHost-BuoPEXZX.js → useServiceUrlHost-Szz9y3rh.js} +1 -1
- package/web/assets/vue-i18n-o7yraQpR.js +3 -0
- package/web/index.html +21 -21
- package/chunk-KV4GJKXU.js.map +0 -1
- package/chunk-SEPAURTQ.js.map +0 -1
- package/chunk-UD42LACH.js.map +0 -1
- package/src-LEDGW5KP.js.map +0 -1
- package/web/assets/AgentTerminalPage-C3TJ1NL9.js +0 -1
- package/web/assets/AiChatModals-D3scRE3S.js +0 -4
- package/web/assets/AiChatModals-EfRuvId7.css +0 -1
- package/web/assets/AppLayout-BMQUjPKe.js +0 -1
- package/web/assets/AuditPage-CKcExBws.js +0 -1
- package/web/assets/CloudAccountPage-B6oPg0BZ.js +0 -1
- package/web/assets/CloudMessagingPage-C5fwVe-F.js +0 -2
- package/web/assets/DashboardPage-9RV7sx3e.js +0 -1
- package/web/assets/DirectoryPickerDialog-C06BXyfJ.js +0 -1
- package/web/assets/EnvironmentForm-CAvDoira.js +0 -1
- package/web/assets/EnvironmentFormPage-BmAxhN-m.js +0 -1
- package/web/assets/EnvironmentListPage-B8uATPBI.js +0 -1
- package/web/assets/KanbanBoardPage-CQsOkRUf.js +0 -4
- package/web/assets/KanbanListPage-CWZqkjD6.js +0 -1
- package/web/assets/LaunchProjectPage-saSmC7Pr.js +0 -1
- package/web/assets/LaunchServicesPage-BkKyLMEz.js +0 -1
- package/web/assets/LaunchServicesPage-DoLDPqQq.css +0 -1
- package/web/assets/LogViewer-CZBYkH9J.js +0 -3
- package/web/assets/OrgKeyPrompt-DzQ7pwbs.js +0 -1
- package/web/assets/ProcessControls-BEeoNlNV.js +0 -1
- package/web/assets/ProcessControls-BlpnVylq.css +0 -1
- package/web/assets/ProcessDetailPage-CQgu5uKm.js +0 -1
- package/web/assets/ProcessMetrics-Crlfkelj.js +0 -1
- package/web/assets/ProjectCreateDialog-BolrhRD-.js +0 -1
- package/web/assets/ProjectPublishDialog-DsZNWrfz.js +0 -1
- package/web/assets/ScenarioEditorPage-MIAGh2us.js +0 -14
- package/web/assets/SendToChatDialog-BxWsyv29.js +0 -1
- package/web/assets/ServiceControlsBar-DqDPAZt7.js +0 -1
- package/web/assets/ServiceDetailPage-hkSAzZ-l.js +0 -2
- package/web/assets/ServiceFormPage-DXqhqXg8.js +0 -1
- package/web/assets/SettingsPage-DS2YhMIM.js +0 -2086
- package/web/assets/UnlockPage-s8SgVcKk.js +0 -1
- package/web/assets/VaultMigrationPage-s0b44MDM.js +0 -1
- package/web/assets/WikiView-DtGwIt_Q.js +0 -1
- package/web/assets/cloud-chat.store-b-BHGz7I.js +0 -1
- package/web/assets/editor.store-DCAZSeVG.js +0 -1
- package/web/assets/floating-ui.dom-DWhsZyqT.js +0 -1
- package/web/assets/i18n-D0afQEGs.js +0 -1
- package/web/assets/index-D8VRbyoP.js +0 -309
- package/web/assets/index-DsezNbSM.css +0 -1
- package/web/assets/modal-qYgTMkYb.js +0 -5
- package/web/assets/scenario.store-YWHL2_wu.js +0 -7
- package/web/assets/settings-scope-Bv7QGPrt.js +0 -1
- package/web/assets/settings.store-bEsB7oKb.js +0 -1
- package/web/assets/src--GO-VSJ0.js +0 -5
- package/web/assets/src-C-D_y7ou.js +0 -1
- package/web/assets/useEnvVarAutocomplete-BqGTODUv.js +0 -1
- package/web/assets/useProjectCatalog-yocjIKt6.js +0 -1
- package/web/assets/vue-draggable-plus-BHsveB8w.css +0 -1
- package/web/assets/vue-draggable-plus-DX4rTBiu.js +0 -1
- /package/{agent-manager-R7BPQO6D.js.map → agent-manager-46C4NIQ2.js.map} +0 -0
- /package/{chat-LPML22J5.js.map → chat-CMNDMXIP.js.map} +0 -0
- /package/{companion-W3UMZWGH.js.map → companion-XKMNRIJB.js.map} +0 -0
- /package/{install-gate-PVMGJDWE.js.map → install-gate-PWPGC63Z.js.map} +0 -0
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RUNEYA_CAPABILITIES,
|
|
3
|
-
applyProjectAnchor,
|
|
4
|
-
buildAnthropicTools,
|
|
5
|
-
buildOpenAITools,
|
|
6
|
-
buildToolSafetyInstruction,
|
|
7
|
-
runCapability,
|
|
8
|
-
toRawParams
|
|
9
|
-
} from "./chunk-6IBPK6UE.js";
|
|
10
|
-
import {
|
|
11
|
-
settingsManager
|
|
12
|
-
} from "./chunk-NPMSBYXC.js";
|
|
13
1
|
import {
|
|
14
2
|
COMPANION_SIGNATURE,
|
|
3
|
+
PASTED_TASK_LEAD,
|
|
4
|
+
PTY_MCP_READY_MAX_MS,
|
|
15
5
|
activeSessionRegistry,
|
|
16
6
|
claudeSessionExists,
|
|
17
7
|
companionCreateDefaults,
|
|
@@ -20,7 +10,6 @@ import {
|
|
|
20
10
|
companionSkips,
|
|
21
11
|
companionSystemNote,
|
|
22
12
|
companionThinkingIds,
|
|
23
|
-
conversationSectionStore,
|
|
24
13
|
conversationStore,
|
|
25
14
|
deliverToMainConversation,
|
|
26
15
|
findClaudeSessionFile,
|
|
@@ -36,7 +25,7 @@ import {
|
|
|
36
25
|
noteSpawnConversation,
|
|
37
26
|
observePtyChunk,
|
|
38
27
|
onCompanionThinkingChanged,
|
|
39
|
-
|
|
28
|
+
pasteToPty,
|
|
40
29
|
readClaudeTranscript,
|
|
41
30
|
resetRelaunches,
|
|
42
31
|
resolveAgentCwd,
|
|
@@ -46,31 +35,54 @@ import {
|
|
|
46
35
|
signJwt,
|
|
47
36
|
stopCompanion,
|
|
48
37
|
syncConversationFromClaudeTranscript,
|
|
49
|
-
verifyJwt
|
|
50
|
-
|
|
38
|
+
verifyJwt,
|
|
39
|
+
waitForPtyInput
|
|
40
|
+
} from "./chunk-JNK6H5KH.js";
|
|
41
|
+
import {
|
|
42
|
+
RUNEYA_CAPABILITIES,
|
|
43
|
+
applyProjectAnchor,
|
|
44
|
+
buildAnthropicTools,
|
|
45
|
+
buildOpenAITools,
|
|
46
|
+
buildToolSafetyInstruction,
|
|
47
|
+
runCapability,
|
|
48
|
+
toRawParams
|
|
49
|
+
} from "./chunk-6IBPK6UE.js";
|
|
50
|
+
import {
|
|
51
|
+
conversationSectionStore,
|
|
52
|
+
projectStore
|
|
53
|
+
} from "./chunk-7QXP4FXD.js";
|
|
54
|
+
import {
|
|
55
|
+
describeVariables,
|
|
56
|
+
environmentOverridesStore,
|
|
57
|
+
environmentResolver,
|
|
58
|
+
filterEnvForAi
|
|
59
|
+
} from "./chunk-WDHLEO5S.js";
|
|
51
60
|
import {
|
|
52
|
-
StoreDiskCoordinator,
|
|
53
61
|
agentManager,
|
|
54
|
-
agentStore
|
|
62
|
+
agentStore
|
|
63
|
+
} from "./chunk-5XG3X2TM.js";
|
|
64
|
+
import {
|
|
65
|
+
serviceStore
|
|
66
|
+
} from "./chunk-4D3GCBPO.js";
|
|
67
|
+
import {
|
|
68
|
+
StoreDiskCoordinator,
|
|
55
69
|
decryptValue,
|
|
56
|
-
decryptVariableSlots,
|
|
57
70
|
encryptValue,
|
|
58
|
-
encryptVariableSlots,
|
|
59
71
|
ensureProjectsLoaded,
|
|
60
72
|
environmentStore,
|
|
61
|
-
extractEncryptedVariableSlots,
|
|
62
|
-
getEncryptionKey,
|
|
63
73
|
hasEncryptionKey,
|
|
64
74
|
isCiMode,
|
|
65
75
|
isEncryptedValue,
|
|
66
76
|
recordLocalRemoval,
|
|
67
|
-
refsOfProjectId
|
|
68
|
-
|
|
69
|
-
} from "./chunk-KV4GJKXU.js";
|
|
77
|
+
refsOfProjectId
|
|
78
|
+
} from "./chunk-DFPZ4MTI.js";
|
|
70
79
|
import {
|
|
71
80
|
isCliAvailable,
|
|
72
81
|
resolveCliInvocation
|
|
73
82
|
} from "./chunk-OZBSOSAS.js";
|
|
83
|
+
import {
|
|
84
|
+
settingsManager
|
|
85
|
+
} from "./chunk-NPMSBYXC.js";
|
|
74
86
|
import {
|
|
75
87
|
atomicWriteJsonSecure,
|
|
76
88
|
atomicWriteSecure,
|
|
@@ -83,15 +95,13 @@ import {
|
|
|
83
95
|
wasRead
|
|
84
96
|
} from "./chunk-4LJQIQMN.js";
|
|
85
97
|
import {
|
|
86
|
-
allVaultRoots,
|
|
87
98
|
env,
|
|
88
99
|
keyedFilePath,
|
|
89
100
|
legacyMachineFilePath,
|
|
90
101
|
listOrgVaults,
|
|
91
102
|
machineFilePath,
|
|
92
103
|
machineRoot,
|
|
93
|
-
orgVaultRoot
|
|
94
|
-
vaultIdOfRoot
|
|
104
|
+
orgVaultRoot
|
|
95
105
|
} from "./chunk-VPSQJOSN.js";
|
|
96
106
|
import {
|
|
97
107
|
AGENT_STOP_FETCH_TIMEOUT,
|
|
@@ -101,7 +111,6 @@ import {
|
|
|
101
111
|
HealthCheckSchema,
|
|
102
112
|
JSLogParserSchema,
|
|
103
113
|
LogSourceConfigSchema,
|
|
104
|
-
MASKED_VALUE,
|
|
105
114
|
MissingVariableSchema,
|
|
106
115
|
RestartStrategySchema,
|
|
107
116
|
ScenarioSchema,
|
|
@@ -111,7 +120,6 @@ import {
|
|
|
111
120
|
crossServiceValues,
|
|
112
121
|
findUnresolvedReferences,
|
|
113
122
|
interpolateBuiltinsInEnv,
|
|
114
|
-
isSharedWithAi,
|
|
115
123
|
nativeParsers,
|
|
116
124
|
restoreLogSourceSecrets,
|
|
117
125
|
servicePortValues,
|
|
@@ -126,6 +134,7 @@ import { z as z6 } from "zod";
|
|
|
126
134
|
import Anthropic2 from "@anthropic-ai/sdk";
|
|
127
135
|
import OpenAI2 from "openai";
|
|
128
136
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
137
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
129
138
|
|
|
130
139
|
// ../../packages/mcp-server/src/launch.ts
|
|
131
140
|
import { fileURLToPath } from "url";
|
|
@@ -157,7 +166,8 @@ function pickEnv(env2) {
|
|
|
157
166
|
...env2.RUNEYA_BASE_URL ? { RUNEYA_BASE_URL: env2.RUNEYA_BASE_URL } : {},
|
|
158
167
|
...env2.RUNEYA_API_TOKEN ? { RUNEYA_API_TOKEN: env2.RUNEYA_API_TOKEN } : {},
|
|
159
168
|
...env2.RUNEYA_PROJECT_ID ? { RUNEYA_PROJECT_ID: env2.RUNEYA_PROJECT_ID } : {},
|
|
160
|
-
...env2.RUNEYA_ORG_ID ? { RUNEYA_ORG_ID: env2.RUNEYA_ORG_ID } : {}
|
|
169
|
+
...env2.RUNEYA_ORG_ID ? { RUNEYA_ORG_ID: env2.RUNEYA_ORG_ID } : {},
|
|
170
|
+
...env2.RUNEYA_MCP_READY_ID ? { RUNEYA_MCP_READY_ID: env2.RUNEYA_MCP_READY_ID } : {}
|
|
161
171
|
};
|
|
162
172
|
}
|
|
163
173
|
function runeyaMcpConfig(env2) {
|
|
@@ -621,783 +631,6 @@ var ShortString200Schema = z2.string().max(200);
|
|
|
621
631
|
var DescriptionSchema = z2.string().max(2e3);
|
|
622
632
|
var OptionalProviderIdSchema = Id128Schema.optional();
|
|
623
633
|
|
|
624
|
-
// ../server/src/services/environment-overrides-store.ts
|
|
625
|
-
import { readFile as readFile2, writeFile, mkdir, chmod } from "fs/promises";
|
|
626
|
-
import { dirname, join as join3 } from "path";
|
|
627
|
-
var FILENAME = "environment-overrides.json";
|
|
628
|
-
var EnvironmentOverridesStore = class {
|
|
629
|
-
data = {};
|
|
630
|
-
loaded = false;
|
|
631
|
-
/**
|
|
632
|
-
* Le coordinateur partagé par les stores : un seul rechargement à la fois,
|
|
633
|
-
* jamais pendant une écriture, et une substitution atomique à la fin.
|
|
634
|
-
*
|
|
635
|
-
* Ce store faisait sa propre cuisine et se marquait « chargé » avant ses
|
|
636
|
-
* `await` : toute lecture concurrente repartait avec une table vide — une
|
|
637
|
-
* surcharge déjà écrite paraissait absente, et l'assistant réclamait au
|
|
638
|
-
* déverrouillage des variables que ce poste avait déjà renseignées.
|
|
639
|
-
*/
|
|
640
|
-
disk = new StoreDiskCoordinator();
|
|
641
|
-
/** Ce que le disque portait au dernier chargement (cf. `load`). */
|
|
642
|
-
fingerprint = "";
|
|
643
|
-
diskEncryptedSlots = {};
|
|
644
|
-
/**
|
|
645
|
-
* Les surcharges dont le secret ne s'est pas ouvert.
|
|
646
|
-
*
|
|
647
|
-
* Vides en mémoire, elles doivent repartir chiffrées : sans cela, la
|
|
648
|
-
* sauvegarde suivante remplacerait par du blanc une valeur dont il ne
|
|
649
|
-
* manquait que la clé.
|
|
650
|
-
*/
|
|
651
|
-
unreadableSecrets = /* @__PURE__ */ new Set();
|
|
652
|
-
/** Which vault each override was read from, until its project is known. */
|
|
653
|
-
roots = /* @__PURE__ */ new Map();
|
|
654
|
-
/**
|
|
655
|
-
* An override belongs to the project whose variable it answers.
|
|
656
|
-
*
|
|
657
|
-
* Nothing in an override says so — they are keyed by variable id alone — so
|
|
658
|
-
* the owning project is resolved through the environment that defines the
|
|
659
|
-
* variable. Overrides are never synced: they stay in the project's vault on
|
|
660
|
-
* this machine, and no other machine sees them.
|
|
661
|
-
*
|
|
662
|
-
* An override whose variable no longer exists (a deleted environment, a row
|
|
663
|
-
* left behind) has no project to belong to. It stays wherever it was read,
|
|
664
|
-
* falling back to the key's own directory — never dropped, since the variable
|
|
665
|
-
* may simply not be loaded yet.
|
|
666
|
-
*/
|
|
667
|
-
async rootFor(variableId) {
|
|
668
|
-
const root = await environmentStore.rootOfVariable(variableId);
|
|
669
|
-
if (root) return root;
|
|
670
|
-
return this.roots.get(variableId) ?? dirname(await keyedFilePath(FILENAME));
|
|
671
|
-
}
|
|
672
|
-
/** Where earlier versions kept them: read once, rewritten to their vaults. */
|
|
673
|
-
getLegacyFilePath() {
|
|
674
|
-
return legacyMachineFilePath(FILENAME);
|
|
675
|
-
}
|
|
676
|
-
/** Les fichiers qui portent des surcharges : les coffres, et l'emplacement d'avant. */
|
|
677
|
-
async sourcePaths() {
|
|
678
|
-
const roots = await allVaultRoots();
|
|
679
|
-
return [
|
|
680
|
-
this.getLegacyFilePath(),
|
|
681
|
-
await keyedFilePath(FILENAME),
|
|
682
|
-
...roots.map((root) => join3(root, FILENAME))
|
|
683
|
-
];
|
|
684
|
-
}
|
|
685
|
-
/**
|
|
686
|
-
* Charger, et recharger quand les fichiers ont bougé sous nos pieds.
|
|
687
|
-
*
|
|
688
|
-
* L'empreinte se prend **après** `ensureProjectsLoaded` : la liste des coffres
|
|
689
|
-
* en dépend, et une empreinte prise sur une liste incomplète ne ressemblerait
|
|
690
|
-
* à aucune des suivantes — le store se rechargerait à chaque lecture.
|
|
691
|
-
*/
|
|
692
|
-
async load() {
|
|
693
|
-
await this.disk.load(async () => {
|
|
694
|
-
if (!getEncryptionKey()) return null;
|
|
695
|
-
await ensureProjectsLoaded();
|
|
696
|
-
const fingerprint = await fingerprintFiles(await this.sourcePaths());
|
|
697
|
-
if (this.loaded && fingerprint === this.fingerprint) return null;
|
|
698
|
-
const next = await this.readVaults();
|
|
699
|
-
return () => {
|
|
700
|
-
this.data = next.data;
|
|
701
|
-
this.diskEncryptedSlots = next.diskEncryptedSlots;
|
|
702
|
-
this.roots = next.roots;
|
|
703
|
-
this.unreadableSecrets = next.unreadableSecrets;
|
|
704
|
-
this.fingerprint = fingerprint;
|
|
705
|
-
this.loaded = true;
|
|
706
|
-
};
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
/**
|
|
710
|
-
* Read every vault, then the places older versions used, into fresh tables
|
|
711
|
-
* that `load` swaps in at once.
|
|
712
|
-
*
|
|
713
|
-
* Variable ids are unique, so merging the files cannot collide; a vault read
|
|
714
|
-
* later simply wins over a legacy copy of the same override.
|
|
715
|
-
*/
|
|
716
|
-
async readVaults() {
|
|
717
|
-
const data = {};
|
|
718
|
-
const diskEncryptedSlots = {};
|
|
719
|
-
const roots = /* @__PURE__ */ new Map();
|
|
720
|
-
const unreadableSecrets = /* @__PURE__ */ new Set();
|
|
721
|
-
for (const path of await this.sourcePaths()) {
|
|
722
|
-
let parsed;
|
|
723
|
-
try {
|
|
724
|
-
parsed = JSON.parse(await readFile2(path, "utf-8"));
|
|
725
|
-
} catch {
|
|
726
|
-
continue;
|
|
727
|
-
}
|
|
728
|
-
Object.assign(
|
|
729
|
-
diskEncryptedSlots,
|
|
730
|
-
extractEncryptedVariableSlots(parsed)
|
|
731
|
-
);
|
|
732
|
-
const decrypted = decryptVariableSlots(
|
|
733
|
-
parsed,
|
|
734
|
-
"environment-overrides",
|
|
735
|
-
(variableId) => unreadableSecrets.add(variableId),
|
|
736
|
-
vaultIdOfRoot(dirname(path))
|
|
737
|
-
);
|
|
738
|
-
for (const [id, override] of Object.entries(decrypted)) {
|
|
739
|
-
data[id] = { value: override.value };
|
|
740
|
-
roots.set(id, dirname(path));
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
return { data, diskEncryptedSlots, roots, unreadableSecrets };
|
|
744
|
-
}
|
|
745
|
-
save() {
|
|
746
|
-
return this.disk.save(() => this.doSave());
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* Write each vault that holds — or held — an override.
|
|
750
|
-
*
|
|
751
|
-
* A vault emptied by this save is still written, as an empty object: an
|
|
752
|
-
* override that moved to its project's vault must not survive in the one it
|
|
753
|
-
* came from.
|
|
754
|
-
*/
|
|
755
|
-
async doSave() {
|
|
756
|
-
const byRoot = /* @__PURE__ */ new Map();
|
|
757
|
-
for (const root of this.roots.values()) byRoot.set(root, {});
|
|
758
|
-
for (const [variableId, override] of Object.entries(this.data)) {
|
|
759
|
-
const root = await this.rootFor(variableId);
|
|
760
|
-
this.roots.set(variableId, root);
|
|
761
|
-
const bucket = byRoot.get(root) ?? {};
|
|
762
|
-
bucket[variableId] = override;
|
|
763
|
-
byRoot.set(root, bucket);
|
|
764
|
-
}
|
|
765
|
-
for (const [root, overrides] of byRoot) {
|
|
766
|
-
const encrypted = this.keepUnreadableSecrets(encryptVariableSlots(
|
|
767
|
-
overrides,
|
|
768
|
-
this.diskEncryptedSlots,
|
|
769
|
-
vaultIdOfRoot(root)
|
|
770
|
-
));
|
|
771
|
-
Object.assign(
|
|
772
|
-
this.diskEncryptedSlots,
|
|
773
|
-
extractEncryptedVariableSlots(encrypted)
|
|
774
|
-
);
|
|
775
|
-
await mkdir(root, { recursive: true });
|
|
776
|
-
const path = join3(root, FILENAME);
|
|
777
|
-
const tmpPath = path + ".tmp";
|
|
778
|
-
await writeFile(tmpPath, JSON.stringify(encrypted, null, 2), "utf-8");
|
|
779
|
-
await renameWithRetry(tmpPath, path);
|
|
780
|
-
await chmod(path, 384);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
/**
|
|
784
|
-
* Rendre au disque le chiffré d'une surcharge illisible, plutôt qu'un blanc.
|
|
785
|
-
*
|
|
786
|
-
* Pendant du garde-fou du store des environnements : tant que la valeur en
|
|
787
|
-
* mémoire est vide, c'est que personne n'a retouché la variable et que son
|
|
788
|
-
* chiffré fait toujours foi.
|
|
789
|
-
*/
|
|
790
|
-
keepUnreadableSecrets(overrides) {
|
|
791
|
-
if (this.unreadableSecrets.size === 0) return overrides;
|
|
792
|
-
const result = { ...overrides };
|
|
793
|
-
for (const variableId of this.unreadableSecrets) {
|
|
794
|
-
const ciphertext = this.diskEncryptedSlots[variableId];
|
|
795
|
-
const override = result[variableId];
|
|
796
|
-
if (!ciphertext || !override || override.value.value) continue;
|
|
797
|
-
result[variableId] = { ...override, value: { ...override.value, value: ciphertext } };
|
|
798
|
-
}
|
|
799
|
-
return result;
|
|
800
|
-
}
|
|
801
|
-
async getOverride(variableId) {
|
|
802
|
-
await this.load();
|
|
803
|
-
return this.data[variableId] ?? null;
|
|
804
|
-
}
|
|
805
|
-
async setOverride(variableId, override) {
|
|
806
|
-
await this.load();
|
|
807
|
-
this.data[variableId] = { value: override.value };
|
|
808
|
-
await this.save();
|
|
809
|
-
}
|
|
810
|
-
async removeOverride(variableId) {
|
|
811
|
-
await this.load();
|
|
812
|
-
if (!this.data[variableId]) return;
|
|
813
|
-
delete this.data[variableId];
|
|
814
|
-
this.unreadableSecrets.delete(variableId);
|
|
815
|
-
await this.save();
|
|
816
|
-
}
|
|
817
|
-
async getAll() {
|
|
818
|
-
await this.load();
|
|
819
|
-
return { ...this.data };
|
|
820
|
-
}
|
|
821
|
-
async removeOverridesForVariables(variableIds) {
|
|
822
|
-
await this.load();
|
|
823
|
-
let changed = false;
|
|
824
|
-
for (const id of variableIds) {
|
|
825
|
-
if (this.data[id]) {
|
|
826
|
-
delete this.data[id];
|
|
827
|
-
this.unreadableSecrets.delete(id);
|
|
828
|
-
changed = true;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
if (changed) await this.save();
|
|
832
|
-
}
|
|
833
|
-
};
|
|
834
|
-
var environmentOverridesStore = new EnvironmentOverridesStore();
|
|
835
|
-
|
|
836
|
-
// ../server/src/services/environment-resolver.ts
|
|
837
|
-
function interpolateValue(raw, envContext) {
|
|
838
|
-
return raw.replace(/\{\{environment\.([^}]+)\}\}/g, (match, key) => {
|
|
839
|
-
return envContext[key] !== void 0 ? envContext[key] : match;
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
function resolveVariable(variable, envContext) {
|
|
843
|
-
const slot = variable.value;
|
|
844
|
-
const finalValue = interpolateValue(slot.pre, envContext) + interpolateValue(slot.value, envContext) + interpolateValue(slot.post, envContext);
|
|
845
|
-
return { value: finalValue, isSecret: slot.isSecret };
|
|
846
|
-
}
|
|
847
|
-
function slotReferenceText(variable) {
|
|
848
|
-
const slot = variable.value;
|
|
849
|
-
return `${slot.pre}
|
|
850
|
-
${slot.value}
|
|
851
|
-
${slot.post}`;
|
|
852
|
-
}
|
|
853
|
-
var HIDDEN_VALUES_NOTICE = `Values shown as ${MASKED_VALUE} are secrets their author did not share with AI. The service does receive them: never treat such a key as missing, and never pass ${MASKED_VALUE} as its value.`;
|
|
854
|
-
function filterEnvForAi(resolved, sharedKeys) {
|
|
855
|
-
const secret = new Set(resolved.envSecrets);
|
|
856
|
-
const shared = new Set(sharedKeys);
|
|
857
|
-
const env2 = {};
|
|
858
|
-
const envSecrets = [];
|
|
859
|
-
const hiddenKeys = [];
|
|
860
|
-
for (const [key, value] of Object.entries(resolved.env)) {
|
|
861
|
-
const hidden = secret.has(key) && !shared.has(key);
|
|
862
|
-
env2[key] = hidden ? MASKED_VALUE : value;
|
|
863
|
-
if (secret.has(key)) envSecrets.push(key);
|
|
864
|
-
if (hidden) hiddenKeys.push(key);
|
|
865
|
-
}
|
|
866
|
-
return {
|
|
867
|
-
env: env2,
|
|
868
|
-
envSecrets,
|
|
869
|
-
hiddenKeys,
|
|
870
|
-
...hiddenKeys.length > 0 ? { hiddenNotice: HIDDEN_VALUES_NOTICE } : {}
|
|
871
|
-
};
|
|
872
|
-
}
|
|
873
|
-
function describeVariables(environments, ctx) {
|
|
874
|
-
const details = [];
|
|
875
|
-
for (const environment of environments) {
|
|
876
|
-
for (const [variableId, variable] of Object.entries(environment.variables ?? {})) {
|
|
877
|
-
const slot = variable.value;
|
|
878
|
-
const isOverridden = ctx.overriddenVariableIds.has(variableId);
|
|
879
|
-
const isSecret = slot.isSecret || ctx.resolvedSecretKeys.has(variable.key);
|
|
880
|
-
const readableByAi = ctx.maskedKeys.has(variable.key) ? false : !isSecret || isSharedWithAi(slot);
|
|
881
|
-
details.push({
|
|
882
|
-
key: variable.key,
|
|
883
|
-
inService: ctx.serviceKeys.has(variable.key),
|
|
884
|
-
environmentId: environment.id,
|
|
885
|
-
environmentName: environment.name,
|
|
886
|
-
scope: environment.scope,
|
|
887
|
-
...environment.scope === "service" && environment.reference ? { environmentServiceId: environment.reference } : {},
|
|
888
|
-
...slot.description ? { description: slot.description } : {},
|
|
889
|
-
secret: isSecret,
|
|
890
|
-
readableByAi,
|
|
891
|
-
injectedIntoAi: slot.injectIntoAi,
|
|
892
|
-
required: slot.required,
|
|
893
|
-
overrideOnly: slot.overrideOnly,
|
|
894
|
-
overridden: isOverridden,
|
|
895
|
-
...isOverridden && ctx.sharedValues[variable.key] !== void 0 ? { sharedValue: ctx.sharedValues[variable.key] } : {}
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
return details.sort((a, b) => a.key.localeCompare(b.key) || a.environmentName.localeCompare(b.environmentName));
|
|
900
|
-
}
|
|
901
|
-
function withOverriddenValue(variable, override) {
|
|
902
|
-
return {
|
|
903
|
-
...variable,
|
|
904
|
-
value: {
|
|
905
|
-
...variable.value,
|
|
906
|
-
value: override.value,
|
|
907
|
-
pre: override.pre,
|
|
908
|
-
post: override.post
|
|
909
|
-
}
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
var EnvironmentResolver = class {
|
|
913
|
-
async resolveForService(ctx, opts) {
|
|
914
|
-
if (!ctx.activeEnvironmentId) {
|
|
915
|
-
return { env: {}, envSecrets: [] };
|
|
916
|
-
}
|
|
917
|
-
const serviceEnv = await environmentStore.findServiceEnvironment(ctx.serviceId, ctx.activeEnvironmentId);
|
|
918
|
-
if (!serviceEnv) {
|
|
919
|
-
return this.resolveEnvironment(ctx.activeEnvironmentId, opts);
|
|
920
|
-
}
|
|
921
|
-
return this.resolveEnvironment(serviceEnv.id, opts);
|
|
922
|
-
}
|
|
923
|
-
/**
|
|
924
|
-
* `applyOverrides: false` rend ce que l'équipe partage, sans ce que ce poste
|
|
925
|
-
* a mis par-dessus : c'est la réponse à « qu'est-ce que ma surcharge
|
|
926
|
-
* remplace ». Le défaut reste `true` — tout le reste du produit veut la
|
|
927
|
-
* valeur avec laquelle le service tourne réellement.
|
|
928
|
-
*/
|
|
929
|
-
async resolveEnvironment(environmentId, opts) {
|
|
930
|
-
const visited = /* @__PURE__ */ new Set();
|
|
931
|
-
const result = await this.resolveDag(environmentId, visited);
|
|
932
|
-
const overridesByKey = opts?.applyOverrides === false ? {} : await this.resolveOverridesByKey(environmentId);
|
|
933
|
-
for (const [varId, variable] of Object.entries(result.variables)) {
|
|
934
|
-
const override = overridesByKey[variable.key];
|
|
935
|
-
if (override) {
|
|
936
|
-
result.variables[varId] = withOverriddenValue(variable, override.variable.value);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
const rawEnv = {};
|
|
940
|
-
for (const [_varId, variable] of Object.entries(result.variables)) {
|
|
941
|
-
const slot = variable.value;
|
|
942
|
-
rawEnv[variable.key] = slot.pre + slot.value + slot.post;
|
|
943
|
-
}
|
|
944
|
-
const { ctx: interpolationCtx, secretKeys: ctxSecretKeys } = await this.buildInterpolationContext(environmentId, rawEnv, result.parentEnv, result.parentSecretKeys, result.parentKeySources);
|
|
945
|
-
const secretKeys = this.collectSecretKeys(result.variables, ctxSecretKeys);
|
|
946
|
-
const env2 = {};
|
|
947
|
-
const envSecrets = [];
|
|
948
|
-
for (const [_varId, variable] of Object.entries(result.variables)) {
|
|
949
|
-
const { value, isSecret } = resolveVariable(variable, interpolationCtx);
|
|
950
|
-
env2[variable.key] = value;
|
|
951
|
-
if (isSecret || secretKeys.has(variable.key)) {
|
|
952
|
-
envSecrets.push(variable.key);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
return { env: env2, envSecrets };
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* Resolve an environment (with inheritance) and return only the variables
|
|
959
|
-
* flagged `injectIntoAi`, as a flat KEY→value dict ready to be injected into
|
|
960
|
-
* the process environment of an AI agent spawn.
|
|
961
|
-
*/
|
|
962
|
-
async resolveAiInjectedEnv(environmentId) {
|
|
963
|
-
if (!environmentId) return {};
|
|
964
|
-
const visited = /* @__PURE__ */ new Set();
|
|
965
|
-
const result = await this.resolveDag(environmentId, visited);
|
|
966
|
-
const overridesByKey = await this.resolveOverridesByKey(environmentId);
|
|
967
|
-
for (const [varId, variable] of Object.entries(result.variables)) {
|
|
968
|
-
const override = overridesByKey[variable.key];
|
|
969
|
-
if (override) {
|
|
970
|
-
result.variables[varId] = withOverriddenValue(variable, override.variable.value);
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
const rawEnv = {};
|
|
974
|
-
for (const variable of Object.values(result.variables)) {
|
|
975
|
-
const slot = variable.value;
|
|
976
|
-
rawEnv[variable.key] = slot.pre + slot.value + slot.post;
|
|
977
|
-
}
|
|
978
|
-
const { ctx: interpolationCtx } = await this.buildInterpolationContext(
|
|
979
|
-
environmentId,
|
|
980
|
-
rawEnv,
|
|
981
|
-
result.parentEnv,
|
|
982
|
-
result.parentSecretKeys,
|
|
983
|
-
result.parentKeySources
|
|
984
|
-
);
|
|
985
|
-
const injected = {};
|
|
986
|
-
for (const variable of Object.values(result.variables)) {
|
|
987
|
-
if (!variable.value.injectIntoAi) continue;
|
|
988
|
-
const { value } = resolveVariable(variable, interpolationCtx);
|
|
989
|
-
injected[variable.key] = value;
|
|
990
|
-
}
|
|
991
|
-
return injected;
|
|
992
|
-
}
|
|
993
|
-
/**
|
|
994
|
-
* Les variables qu'il faut remplir avant qu'un service de cet environnement
|
|
995
|
-
* puisse démarrer.
|
|
996
|
-
*
|
|
997
|
-
* Deux motifs, et ils ne se mesurent pas au même endroit :
|
|
998
|
-
*
|
|
999
|
-
* - `required` porte sur la valeur **finale**, interpolation et surcharge
|
|
1000
|
-
* comprises : une variable obligatoire dont la valeur ne tient qu'à une
|
|
1001
|
-
* référence morte est aussi vide qu'une case blanche.
|
|
1002
|
-
* - `overrideOnly` porte sur l'**existence d'une surcharge machine**. Elle
|
|
1003
|
-
* n'a par construction pas de valeur partagée ; ce qui manque n'est pas un
|
|
1004
|
-
* contenu, c'est la réponse de ce poste-ci.
|
|
1005
|
-
*
|
|
1006
|
-
* Les drapeaux se lisent sur la définition, jamais sur le slot résolu : une
|
|
1007
|
-
* surcharge remplace le slot entier et ne porte aucun drapeau — les y lire
|
|
1008
|
-
* rendrait toute variable surchargée silencieusement facultative.
|
|
1009
|
-
*/
|
|
1010
|
-
async findMissingVariables(environmentId) {
|
|
1011
|
-
if (!environmentId) return [];
|
|
1012
|
-
const [{ env: env2 }, definitions, overridesByKey] = await Promise.all([
|
|
1013
|
-
this.resolveEnvironment(environmentId),
|
|
1014
|
-
this.resolveDag(environmentId, /* @__PURE__ */ new Set()),
|
|
1015
|
-
this.resolveOverridesByKey(environmentId)
|
|
1016
|
-
]);
|
|
1017
|
-
const missing = [];
|
|
1018
|
-
for (const [variableId, variable] of Object.entries(definitions.variables)) {
|
|
1019
|
-
const slot = variable.value;
|
|
1020
|
-
const source = definitions.sources[variableId];
|
|
1021
|
-
const base = {
|
|
1022
|
-
variableId,
|
|
1023
|
-
key: variable.key,
|
|
1024
|
-
description: slot.description,
|
|
1025
|
-
isSecret: slot.isSecret,
|
|
1026
|
-
environmentId: source?.environmentId ?? environmentId,
|
|
1027
|
-
environmentName: source?.environmentName ?? ""
|
|
1028
|
-
};
|
|
1029
|
-
if (slot.overrideOnly && !overridesByKey[variable.key]) {
|
|
1030
|
-
missing.push({ ...base, reason: "overrideOnly" });
|
|
1031
|
-
continue;
|
|
1032
|
-
}
|
|
1033
|
-
if (slot.required && (env2[variable.key] ?? "") === "") {
|
|
1034
|
-
missing.push({ ...base, reason: "required" });
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
return missing;
|
|
1038
|
-
}
|
|
1039
|
-
/**
|
|
1040
|
-
* Tout ce qu'il reste à remplir sous l'environnement courant.
|
|
1041
|
-
*
|
|
1042
|
-
* Deux périmètres, et il faut les deux :
|
|
1043
|
-
*
|
|
1044
|
-
* 1. **L'environnement courant lui-même**, héritage compris —
|
|
1045
|
-
* `findMissingVariables` remonte ses `parentIds`. C'est là que vivent les
|
|
1046
|
-
* variables globales du projet, et c'est la première branche balayée.
|
|
1047
|
-
* 2. **Les environnements de service qui s'activent avec lui**
|
|
1048
|
-
* (`activateOn`). Ceux-là ne sont pas des ancêtres du global : ils en
|
|
1049
|
-
* héritent, dans l'autre sens. Ne pas y descendre revenait à ne rien
|
|
1050
|
-
* réclamer d'un service jusqu'à ce qu'on essaie de le démarrer.
|
|
1051
|
-
*
|
|
1052
|
-
* Dédoublonné par identifiant de variable : une variable du global remonte
|
|
1053
|
-
* dans chaque branche de service qui en hérite, et l'assistant ne doit pas
|
|
1054
|
-
* demander deux fois la même chose.
|
|
1055
|
-
*/
|
|
1056
|
-
async findMissingForActiveEnvironment(globalEnvId) {
|
|
1057
|
-
if (!globalEnvId) return [];
|
|
1058
|
-
const serviceEnvs = await environmentStore.listByActivateOn(globalEnvId);
|
|
1059
|
-
const branches = await Promise.all([
|
|
1060
|
-
this.findMissingVariables(globalEnvId),
|
|
1061
|
-
...serviceEnvs.map((env2) => this.findMissingVariables(env2.id))
|
|
1062
|
-
]);
|
|
1063
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1064
|
-
const missing = [];
|
|
1065
|
-
for (const branch of branches) {
|
|
1066
|
-
for (const item of branch) {
|
|
1067
|
-
if (seen.has(item.variableId)) continue;
|
|
1068
|
-
seen.add(item.variableId);
|
|
1069
|
-
missing.push(item);
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
return missing;
|
|
1073
|
-
}
|
|
1074
|
-
/**
|
|
1075
|
-
* Les clés que leur auteur a partagées avec les IA, héritage compris.
|
|
1076
|
-
*
|
|
1077
|
-
* Le partage se lit sur la **définition**, et seulement là : une surcharge
|
|
1078
|
-
* machine ne dit que la valeur de ce poste (`withOverriddenValue`). Il n'y a
|
|
1079
|
-
* donc qu'un endroit où ce drapeau existe, et rien à réconcilier.
|
|
1080
|
-
*
|
|
1081
|
-
* Des clés, pas des valeurs : la valeur rendue reste celle que le service
|
|
1082
|
-
* reçoit, surcharges et interpolation comprises.
|
|
1083
|
-
*/
|
|
1084
|
-
async resolveAiSharedKeys(environmentId) {
|
|
1085
|
-
if (!environmentId) return /* @__PURE__ */ new Set();
|
|
1086
|
-
const result = await this.resolveDag(environmentId, /* @__PURE__ */ new Set());
|
|
1087
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1088
|
-
for (const variable of Object.values(result.variables)) {
|
|
1089
|
-
if (isSharedWithAi(variable.value)) keys.add(variable.key);
|
|
1090
|
-
}
|
|
1091
|
-
return keys;
|
|
1092
|
-
}
|
|
1093
|
-
/**
|
|
1094
|
-
* Idem, pour ce qu'un service verra réellement : son environnement dédié
|
|
1095
|
-
* quand il en a un, l'environnement global sinon — la règle de sélection de
|
|
1096
|
-
* `resolveForService`.
|
|
1097
|
-
*/
|
|
1098
|
-
async resolveAiSharedKeysForService(ctx) {
|
|
1099
|
-
if (!ctx.activeEnvironmentId) return /* @__PURE__ */ new Set();
|
|
1100
|
-
const serviceEnv = await environmentStore.findServiceEnvironment(ctx.serviceId, ctx.activeEnvironmentId);
|
|
1101
|
-
return this.resolveAiSharedKeys(serviceEnv ? serviceEnv.id : ctx.activeEnvironmentId);
|
|
1102
|
-
}
|
|
1103
|
-
/**
|
|
1104
|
-
* Idem, pour ce qu'un service verra réellement : son environnement dédié
|
|
1105
|
-
* quand il en a un, l'environnement global sinon. Miroir de
|
|
1106
|
-
* `resolveForService`, dont il partage la règle de sélection.
|
|
1107
|
-
*/
|
|
1108
|
-
async findMissingForService(ctx) {
|
|
1109
|
-
if (!ctx.activeEnvironmentId) return [];
|
|
1110
|
-
const serviceEnv = await environmentStore.findServiceEnvironment(ctx.serviceId, ctx.activeEnvironmentId);
|
|
1111
|
-
return this.findMissingVariables(serviceEnv ? serviceEnv.id : ctx.activeEnvironmentId);
|
|
1112
|
-
}
|
|
1113
|
-
async resolveEnvironmentWithSources(environmentId) {
|
|
1114
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1115
|
-
const result = await this.resolveDag(environmentId, visited);
|
|
1116
|
-
const overridesByKey = await this.resolveOverridesByKey(environmentId);
|
|
1117
|
-
for (const [varId, variable] of Object.entries(result.variables)) {
|
|
1118
|
-
const override = overridesByKey[variable.key];
|
|
1119
|
-
if (override) {
|
|
1120
|
-
result.variables[varId] = withOverriddenValue(variable, override.variable.value);
|
|
1121
|
-
result.sources[varId] = {
|
|
1122
|
-
...result.sources[varId],
|
|
1123
|
-
overridden: true,
|
|
1124
|
-
...override.source ? { overriddenBy: override.source } : {}
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
const rawEnv = {};
|
|
1129
|
-
for (const [_varId, variable] of Object.entries(result.variables)) {
|
|
1130
|
-
const slot = variable.value;
|
|
1131
|
-
rawEnv[variable.key] = slot.pre + slot.value + slot.post;
|
|
1132
|
-
}
|
|
1133
|
-
const { ctx: interpolationCtx, secretKeys: ctxSecretKeys, keySources } = await this.buildInterpolationContext(environmentId, rawEnv, result.parentEnv, result.parentSecretKeys, result.parentKeySources);
|
|
1134
|
-
const secretKeys = this.collectSecretKeys(result.variables, ctxSecretKeys);
|
|
1135
|
-
const env2 = {};
|
|
1136
|
-
const envSecrets = [];
|
|
1137
|
-
for (const [_varId, variable] of Object.entries(result.variables)) {
|
|
1138
|
-
const { value, isSecret } = resolveVariable(variable, interpolationCtx);
|
|
1139
|
-
env2[variable.key] = value;
|
|
1140
|
-
if (isSecret || secretKeys.has(variable.key)) {
|
|
1141
|
-
envSecrets.push(variable.key);
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
const refPattern = /^\{\{environment\.([^}]+)\}\}$/;
|
|
1145
|
-
for (const [varId, variable] of Object.entries(result.variables)) {
|
|
1146
|
-
const match = variable.value.value.match(refPattern);
|
|
1147
|
-
if (match) {
|
|
1148
|
-
const refSource = keySources[match[1]];
|
|
1149
|
-
if (refSource && result.sources[varId]?.environmentId !== refSource.environmentId) {
|
|
1150
|
-
result.sources[varId] = {
|
|
1151
|
-
...result.sources[varId],
|
|
1152
|
-
resolvedFrom: refSource
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
return {
|
|
1158
|
-
variables: result.variables,
|
|
1159
|
-
env: env2,
|
|
1160
|
-
envSecrets,
|
|
1161
|
-
sources: result.sources
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
/** Check if a raw value string interpolates any key that is secret in the context */
|
|
1165
|
-
/**
|
|
1166
|
-
* Toutes les clés dont la valeur est, directement ou par interpolation, un
|
|
1167
|
-
* secret.
|
|
1168
|
-
*
|
|
1169
|
-
* Le contexte d'interpolation ne connaît que les secrets *hérités* : les
|
|
1170
|
-
* secrets définis dans le même environnement n'y figurent pas, si bien qu'un
|
|
1171
|
-
* `AUTH='Bearer {{environment.TOKEN}}'` voisin de son propre `TOKEN` secret
|
|
1172
|
-
* ressortait non marqué — et donc non masqué par toute la chaîne en aval.
|
|
1173
|
-
*
|
|
1174
|
-
* On part des secrets hérités plus ceux déclarés ici, puis on propage
|
|
1175
|
-
* jusqu'au point fixe : une valeur qui référence une clé secrète devient
|
|
1176
|
-
* secrète à son tour, y compris en chaîne (A secret → B cite A → C cite B).
|
|
1177
|
-
*/
|
|
1178
|
-
collectSecretKeys(variables, parentSecretKeys) {
|
|
1179
|
-
const secretKeys = new Set(parentSecretKeys);
|
|
1180
|
-
for (const variable of Object.values(variables)) {
|
|
1181
|
-
if (variable.value.isSecret) secretKeys.add(variable.key);
|
|
1182
|
-
}
|
|
1183
|
-
let changed = true;
|
|
1184
|
-
while (changed) {
|
|
1185
|
-
changed = false;
|
|
1186
|
-
for (const variable of Object.values(variables)) {
|
|
1187
|
-
if (secretKeys.has(variable.key)) continue;
|
|
1188
|
-
if (this.interpolatesSecretKey(slotReferenceText(variable), secretKeys)) {
|
|
1189
|
-
secretKeys.add(variable.key);
|
|
1190
|
-
changed = true;
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
return secretKeys;
|
|
1195
|
-
}
|
|
1196
|
-
interpolatesSecretKey(rawValue, ctxSecretKeys) {
|
|
1197
|
-
const refs = [...rawValue.matchAll(/\{\{environment\.([^}]+)\}\}/g)].map((m) => m[1]);
|
|
1198
|
-
return refs.some((ref) => ctxSecretKeys.has(ref));
|
|
1199
|
-
}
|
|
1200
|
-
/**
|
|
1201
|
-
* Build the interpolation context for an environment.
|
|
1202
|
-
*
|
|
1203
|
-
* For inheriting envs (has parents): start from parent concrete values,
|
|
1204
|
-
* then overlay with current concrete values. This ensures that
|
|
1205
|
-
* {{environment.KEY}} resolves against the parent's value when the child
|
|
1206
|
-
* redefines KEY as an interpolation reference.
|
|
1207
|
-
*
|
|
1208
|
-
* For isolated service envs (scope=service, activateOn set, no parents):
|
|
1209
|
-
* the global env is resolved and used as fallback.
|
|
1210
|
-
*/
|
|
1211
|
-
async buildInterpolationContext(environmentId, rawEnv, parentEnv, parentSecretKeys, parentKeySources) {
|
|
1212
|
-
const environment = await environmentStore.get(environmentId);
|
|
1213
|
-
if (environment && environment.scope === "service" && environment.activateOn && (!environment.parentIds || environment.parentIds.length === 0)) {
|
|
1214
|
-
const globalResolved = await this.resolveEnvironmentWithSources(environment.activateOn);
|
|
1215
|
-
const secretKeys = new Set(globalResolved.envSecrets);
|
|
1216
|
-
const keySources = {};
|
|
1217
|
-
for (const [varId, variable] of Object.entries(globalResolved.variables)) {
|
|
1218
|
-
const source = globalResolved.sources[varId];
|
|
1219
|
-
if (source) {
|
|
1220
|
-
keySources[variable.key] = { environmentId: source.environmentId, environmentName: source.environmentName };
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
const ctx2 = { ...globalResolved.env };
|
|
1224
|
-
for (const [key, value] of Object.entries(rawEnv)) {
|
|
1225
|
-
if (!/\{\{environment\.[^}]+\}\}/.test(value)) {
|
|
1226
|
-
ctx2[key] = value;
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
return { ctx: ctx2, secretKeys, keySources };
|
|
1230
|
-
}
|
|
1231
|
-
const ctx = { ...parentEnv };
|
|
1232
|
-
for (const [key, value] of Object.entries(rawEnv)) {
|
|
1233
|
-
if (!/\{\{environment\.[^}]+\}\}/.test(value)) {
|
|
1234
|
-
ctx[key] = value;
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
return { ctx, secretKeys: parentSecretKeys, keySources: parentKeySources };
|
|
1238
|
-
}
|
|
1239
|
-
/**
|
|
1240
|
-
* For service-scoped envs, compute effective parentIds by also including
|
|
1241
|
-
* the service-scoped envs of ancestor global environments.
|
|
1242
|
-
*
|
|
1243
|
-
* Example: "Server (test)" has parentIds=[test-global]. test-global has
|
|
1244
|
-
* parentIds=[Local-global]. This method adds "Server (Local)" to the
|
|
1245
|
-
* effective parents so service-specific variables from ancestor envs
|
|
1246
|
-
* are inherited correctly.
|
|
1247
|
-
*/
|
|
1248
|
-
async getEffectiveParentIds(environment) {
|
|
1249
|
-
if (environment.scope !== "service" || !environment.reference || !environment.activateOn) {
|
|
1250
|
-
return environment.parentIds;
|
|
1251
|
-
}
|
|
1252
|
-
const additionalIds = [];
|
|
1253
|
-
for (const parentId of environment.parentIds) {
|
|
1254
|
-
const parentEnv = await environmentStore.get(parentId);
|
|
1255
|
-
if (!parentEnv || parentEnv.scope !== "global") continue;
|
|
1256
|
-
for (const grandparentGlobalId of parentEnv.parentIds) {
|
|
1257
|
-
const ancestorServiceEnv = await environmentStore.findServiceEnvironment(environment.reference, grandparentGlobalId);
|
|
1258
|
-
if (ancestorServiceEnv && !environment.parentIds.includes(ancestorServiceEnv.id)) {
|
|
1259
|
-
additionalIds.push(ancestorServiceEnv.id);
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
if (additionalIds.length === 0) return environment.parentIds;
|
|
1264
|
-
return [...environment.parentIds, ...additionalIds];
|
|
1265
|
-
}
|
|
1266
|
-
/**
|
|
1267
|
-
* Project an environment's variables onto their overrides: keep only the
|
|
1268
|
-
* variables that have one, and swap in the override's value slot. The key
|
|
1269
|
-
* always comes from the definition.
|
|
1270
|
-
*
|
|
1271
|
-
* Feeding this projection back through `resolveDag` resolves the override
|
|
1272
|
-
* chain with the exact same inheritance and dedup-by-key rules as the value
|
|
1273
|
-
* chain — so an override set on a parent env is no longer swallowed when a
|
|
1274
|
-
* child (or a service env) redeclares the same key.
|
|
1275
|
-
*/
|
|
1276
|
-
projectOverrides(variables, overrides) {
|
|
1277
|
-
const projected = {};
|
|
1278
|
-
for (const [varId, variable] of Object.entries(variables)) {
|
|
1279
|
-
const override = overrides[varId];
|
|
1280
|
-
if (override) {
|
|
1281
|
-
projected[varId] = { key: variable.key, value: override.value };
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
return projected;
|
|
1285
|
-
}
|
|
1286
|
-
/**
|
|
1287
|
-
* Resolve the override chain independently of the value chain and return the
|
|
1288
|
-
* winning override per variable key (empty when nothing is overridden).
|
|
1289
|
-
*/
|
|
1290
|
-
async resolveOverridesByKey(environmentId) {
|
|
1291
|
-
const allOverrides = await environmentOverridesStore.getAll();
|
|
1292
|
-
if (Object.keys(allOverrides).length === 0) return {};
|
|
1293
|
-
const result = await this.resolveDag(
|
|
1294
|
-
environmentId,
|
|
1295
|
-
/* @__PURE__ */ new Set(),
|
|
1296
|
-
(variables) => this.projectOverrides(variables, allOverrides)
|
|
1297
|
-
);
|
|
1298
|
-
const byKey = {};
|
|
1299
|
-
for (const [varId, variable] of Object.entries(result.variables)) {
|
|
1300
|
-
const source = result.sources[varId];
|
|
1301
|
-
byKey[variable.key] = {
|
|
1302
|
-
variable,
|
|
1303
|
-
...source ? { source: { environmentId: source.environmentId, environmentName: source.environmentName } } : {}
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
return byKey;
|
|
1307
|
-
}
|
|
1308
|
-
async resolveDag(environmentId, visited, project) {
|
|
1309
|
-
if (visited.has(environmentId)) {
|
|
1310
|
-
throw new Error(`Circular dependency detected in environment inheritance for ${environmentId}`);
|
|
1311
|
-
}
|
|
1312
|
-
const environment = await environmentStore.get(environmentId);
|
|
1313
|
-
if (!environment) {
|
|
1314
|
-
throw new Error(`Environment "${environmentId}" not found`);
|
|
1315
|
-
}
|
|
1316
|
-
visited.add(environmentId);
|
|
1317
|
-
const effectiveParentIds = await this.getEffectiveParentIds(environment);
|
|
1318
|
-
const parentResults = [];
|
|
1319
|
-
for (const parentId of effectiveParentIds) {
|
|
1320
|
-
const parentVisited = new Set(visited);
|
|
1321
|
-
const parentResult = await this.resolveDag(parentId, parentVisited, project);
|
|
1322
|
-
parentResults.push(parentResult);
|
|
1323
|
-
}
|
|
1324
|
-
const mergedVariables = {};
|
|
1325
|
-
const sources = {};
|
|
1326
|
-
for (const result of parentResults) {
|
|
1327
|
-
for (const [varId, variable] of Object.entries(result.variables)) {
|
|
1328
|
-
for (const [existingId, existingVar] of Object.entries(mergedVariables)) {
|
|
1329
|
-
if (existingVar.key === variable.key && existingId !== varId) {
|
|
1330
|
-
delete mergedVariables[existingId];
|
|
1331
|
-
delete sources[existingId];
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
mergedVariables[varId] = variable;
|
|
1335
|
-
sources[varId] = result.sources[varId] ?? sources[varId];
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
const parentEnv = {};
|
|
1339
|
-
const parentSecretKeys = /* @__PURE__ */ new Set();
|
|
1340
|
-
const parentKeySources = {};
|
|
1341
|
-
for (const [varId, variable] of Object.entries(mergedVariables)) {
|
|
1342
|
-
const slot = variable.value;
|
|
1343
|
-
parentEnv[variable.key] = slot.pre + slot.value + slot.post;
|
|
1344
|
-
if (slot.isSecret) {
|
|
1345
|
-
parentSecretKeys.add(variable.key);
|
|
1346
|
-
}
|
|
1347
|
-
const source = sources[varId];
|
|
1348
|
-
if (source) {
|
|
1349
|
-
parentKeySources[variable.key] = { environmentId: source.environmentId, environmentName: source.environmentName };
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
const isInterpRef = (v) => /\{\{environment\.[^}]+\}\}/.test(v);
|
|
1353
|
-
for (const result of parentResults) {
|
|
1354
|
-
for (const [key, value] of Object.entries(result.parentEnv)) {
|
|
1355
|
-
const existingIsRef = key in parentEnv && isInterpRef(parentEnv[key]);
|
|
1356
|
-
const newIsConcrete = !isInterpRef(value);
|
|
1357
|
-
if (!(key in parentEnv) || existingIsRef && newIsConcrete) {
|
|
1358
|
-
parentEnv[key] = value;
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
for (const key of result.parentSecretKeys) {
|
|
1362
|
-
parentSecretKeys.add(key);
|
|
1363
|
-
}
|
|
1364
|
-
for (const [key, source] of Object.entries(result.parentKeySources)) {
|
|
1365
|
-
if (!(key in parentKeySources)) {
|
|
1366
|
-
parentKeySources[key] = source;
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
for (const key of this.collectSecretKeys(mergedVariables, parentSecretKeys)) {
|
|
1371
|
-
parentSecretKeys.add(key);
|
|
1372
|
-
}
|
|
1373
|
-
const ownVariables = project ? project(environment.variables) : environment.variables;
|
|
1374
|
-
for (const [varId, variable] of Object.entries(ownVariables)) {
|
|
1375
|
-
let wasInherited = varId in mergedVariables;
|
|
1376
|
-
for (const [existingId, existingVar] of Object.entries(mergedVariables)) {
|
|
1377
|
-
if (existingVar.key === variable.key && existingId !== varId) {
|
|
1378
|
-
delete mergedVariables[existingId];
|
|
1379
|
-
delete sources[existingId];
|
|
1380
|
-
wasInherited = true;
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
mergedVariables[varId] = variable;
|
|
1384
|
-
sources[varId] = {
|
|
1385
|
-
environmentId: environment.id,
|
|
1386
|
-
environmentName: environment.name,
|
|
1387
|
-
overridden: wasInherited
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1390
|
-
return {
|
|
1391
|
-
variables: mergedVariables,
|
|
1392
|
-
sources,
|
|
1393
|
-
parentEnv,
|
|
1394
|
-
parentSecretKeys,
|
|
1395
|
-
parentKeySources
|
|
1396
|
-
};
|
|
1397
|
-
}
|
|
1398
|
-
};
|
|
1399
|
-
var environmentResolver = new EnvironmentResolver();
|
|
1400
|
-
|
|
1401
634
|
// ../server/src/services/llms-txt-generator.ts
|
|
1402
635
|
function generateAdminLlmsTxt(services, jwt, project) {
|
|
1403
636
|
const lines = [];
|
|
@@ -1611,7 +844,7 @@ import { z as z3 } from "zod";
|
|
|
1611
844
|
import { TRPCError as TRPCError2 } from "@trpc/server";
|
|
1612
845
|
|
|
1613
846
|
// ../server/src/services/audit-service.ts
|
|
1614
|
-
import { appendFile, readFile as
|
|
847
|
+
import { appendFile, readFile as readFile2, stat, rename, unlink, readdir as readdir2, mkdir } from "fs/promises";
|
|
1615
848
|
var AUDIT_FILENAME = "audit.log";
|
|
1616
849
|
var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
1617
850
|
var MAX_AGE_DAYS = 30;
|
|
@@ -1631,7 +864,7 @@ var AuditService = class {
|
|
|
1631
864
|
const line = JSON.stringify(record) + "\n";
|
|
1632
865
|
const filepath = this.getFilePath();
|
|
1633
866
|
try {
|
|
1634
|
-
await
|
|
867
|
+
await mkdir(machineRoot(), { recursive: true });
|
|
1635
868
|
await appendFile(filepath, line, "utf-8");
|
|
1636
869
|
await this.rotateIfNeeded();
|
|
1637
870
|
} catch (err) {
|
|
@@ -1644,7 +877,7 @@ var AuditService = class {
|
|
|
1644
877
|
const filepath = this.getFilePath();
|
|
1645
878
|
let lines;
|
|
1646
879
|
try {
|
|
1647
|
-
const content = await
|
|
880
|
+
const content = await readFile2(filepath, "utf-8");
|
|
1648
881
|
lines = content.trim().split("\n").filter(Boolean);
|
|
1649
882
|
} catch {
|
|
1650
883
|
return { entries: [], total: 0 };
|
|
@@ -2485,8 +1718,8 @@ var ScenarioStore = class {
|
|
|
2485
1718
|
var scenarioStore = new ScenarioStore();
|
|
2486
1719
|
|
|
2487
1720
|
// ../server/src/services/scenario-run-store.ts
|
|
2488
|
-
import { mkdir as
|
|
2489
|
-
import { join as
|
|
1721
|
+
import { mkdir as mkdir2, rm, readFile as readFile3 } from "fs/promises";
|
|
1722
|
+
import { join as join3 } from "path";
|
|
2490
1723
|
function sanitizeSlug(value) {
|
|
2491
1724
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "member";
|
|
2492
1725
|
}
|
|
@@ -2502,7 +1735,7 @@ var ScenarioRunStore = class _ScenarioRunStore {
|
|
|
2502
1735
|
}
|
|
2503
1736
|
async initializeConversationWorkspace(conversationId, scenario, userInput) {
|
|
2504
1737
|
const dir = this.getRunDir(conversationId);
|
|
2505
|
-
await
|
|
1738
|
+
await mkdir2(dir, { recursive: true });
|
|
2506
1739
|
await this.saveScenarioSnapshot(conversationId, scenario);
|
|
2507
1740
|
const nodesById = new Map(scenario.nodes.map((p) => [p.id, p]));
|
|
2508
1741
|
const teamMap = new Map(scenario.groups.map((t2) => [t2.id, t2.name]));
|
|
@@ -2527,8 +1760,8 @@ var ScenarioRunStore = class _ScenarioRunStore {
|
|
|
2527
1760
|
"- Use read-only references from linked upstream files as context.",
|
|
2528
1761
|
""
|
|
2529
1762
|
].join("\n");
|
|
2530
|
-
await atomicWriteSecure(
|
|
2531
|
-
await atomicWriteSecure(
|
|
1763
|
+
await atomicWriteSecure(join3(dir, "README.md"), overview);
|
|
1764
|
+
await atomicWriteSecure(join3(dir, "USER_REQUEST.md"), `# User request
|
|
2532
1765
|
|
|
2533
1766
|
${userInput.trim() || "(empty)"}
|
|
2534
1767
|
`);
|
|
@@ -2554,7 +1787,7 @@ ${userInput.trim() || "(empty)"}
|
|
|
2554
1787
|
"- Waiting for assignment.",
|
|
2555
1788
|
""
|
|
2556
1789
|
].join("\n");
|
|
2557
|
-
await atomicWriteSecure(
|
|
1790
|
+
await atomicWriteSecure(join3(dir, fileName), content);
|
|
2558
1791
|
}
|
|
2559
1792
|
}
|
|
2560
1793
|
async deleteRunDir(conversationId) {
|
|
@@ -2614,7 +1847,7 @@ ${userInput.trim() || "(empty)"}
|
|
|
2614
1847
|
if (!runState) return;
|
|
2615
1848
|
try {
|
|
2616
1849
|
const dir = this.getRunDir(conversationId);
|
|
2617
|
-
await
|
|
1850
|
+
await mkdir2(dir, { recursive: true });
|
|
2618
1851
|
const data = {
|
|
2619
1852
|
nodeStates: { ...runState.nodeStates },
|
|
2620
1853
|
nodeOutputs: { ...runState.nodeOutputs },
|
|
@@ -2622,18 +1855,18 @@ ${userInput.trim() || "(empty)"}
|
|
|
2622
1855
|
...Object.keys(runState.nodeAgentIds).length > 0 ? { nodeAgentIds: { ...runState.nodeAgentIds } } : {},
|
|
2623
1856
|
...Object.keys(runState.waitingInputs).length > 0 ? { waitingInputs: { ...runState.waitingInputs } } : {}
|
|
2624
1857
|
};
|
|
2625
|
-
await atomicWriteJsonSecure(
|
|
1858
|
+
await atomicWriteJsonSecure(join3(dir, "run-state.json"), data);
|
|
2626
1859
|
} catch {
|
|
2627
1860
|
}
|
|
2628
1861
|
}
|
|
2629
1862
|
async loadPersistedRunState(conversationId) {
|
|
2630
1863
|
const dir = this.getRunDir(conversationId);
|
|
2631
|
-
return readJsonFileSafe(
|
|
1864
|
+
return readJsonFileSafe(join3(dir, "run-state.json"));
|
|
2632
1865
|
}
|
|
2633
1866
|
async readUserRequest(conversationId) {
|
|
2634
1867
|
try {
|
|
2635
1868
|
const dir = this.getRunDir(conversationId);
|
|
2636
|
-
const content = await
|
|
1869
|
+
const content = await readFile3(join3(dir, "USER_REQUEST.md"), "utf-8");
|
|
2637
1870
|
return content.replace(/^#\s*User request\s*\n+/, "").trim() || null;
|
|
2638
1871
|
} catch {
|
|
2639
1872
|
return null;
|
|
@@ -2642,23 +1875,23 @@ ${userInput.trim() || "(empty)"}
|
|
|
2642
1875
|
async readReadme(conversationId) {
|
|
2643
1876
|
try {
|
|
2644
1877
|
const dir = this.getRunDir(conversationId);
|
|
2645
|
-
return await
|
|
1878
|
+
return await readFile3(join3(dir, "README.md"), "utf-8");
|
|
2646
1879
|
} catch {
|
|
2647
1880
|
return null;
|
|
2648
1881
|
}
|
|
2649
1882
|
}
|
|
2650
1883
|
async saveScenarioSnapshot(conversationId, scenario) {
|
|
2651
1884
|
const dir = this.getRunDir(conversationId);
|
|
2652
|
-
await
|
|
1885
|
+
await mkdir2(dir, { recursive: true });
|
|
2653
1886
|
await atomicWriteJsonSecure(
|
|
2654
|
-
|
|
1887
|
+
join3(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),
|
|
2655
1888
|
scenario,
|
|
2656
1889
|
true
|
|
2657
1890
|
);
|
|
2658
1891
|
}
|
|
2659
1892
|
async loadScenarioSnapshot(conversationId) {
|
|
2660
1893
|
const dir = this.getRunDir(conversationId);
|
|
2661
|
-
return readJsonFileSafe(
|
|
1894
|
+
return readJsonFileSafe(join3(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE));
|
|
2662
1895
|
}
|
|
2663
1896
|
};
|
|
2664
1897
|
var scenarioRunStore = new ScenarioRunStore();
|
|
@@ -2713,9 +1946,9 @@ async function vaultOfProject(projectId) {
|
|
|
2713
1946
|
|
|
2714
1947
|
// ../server/src/services/image-storage.service.ts
|
|
2715
1948
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2716
|
-
import { mkdir as
|
|
1949
|
+
import { mkdir as mkdir3, writeFile, readFile as readFile4, unlink as unlink2, readdir as readdir3 } from "fs/promises";
|
|
2717
1950
|
import { existsSync as existsSync2 } from "fs";
|
|
2718
|
-
import { join as
|
|
1951
|
+
import { join as join4, extname, resolve, sep } from "path";
|
|
2719
1952
|
var ALLOWED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
2720
1953
|
"image/png",
|
|
2721
1954
|
"image/jpeg",
|
|
@@ -2732,7 +1965,7 @@ var ImageStorageService = class {
|
|
|
2732
1965
|
this.dir = dir;
|
|
2733
1966
|
}
|
|
2734
1967
|
async ensureTmpDir() {
|
|
2735
|
-
await
|
|
1968
|
+
await mkdir3(this.dir, { recursive: true, mode: 448 });
|
|
2736
1969
|
}
|
|
2737
1970
|
async saveImage(buffer, originalName, mimeType, contextId) {
|
|
2738
1971
|
if (!ALLOWED_MIME_TYPES.has(mimeType)) {
|
|
@@ -2752,15 +1985,15 @@ var ImageStorageService = class {
|
|
|
2752
1985
|
const imageId = randomUUID3();
|
|
2753
1986
|
const safeExt = ext.replace(/[^a-z.]/g, "");
|
|
2754
1987
|
const fileName = `${imageId}${safeExt}`;
|
|
2755
|
-
const filePath =
|
|
1988
|
+
const filePath = join4(this.dir, fileName);
|
|
2756
1989
|
const resolvedPath = resolve(filePath);
|
|
2757
1990
|
const resolvedDir = resolve(this.dir);
|
|
2758
1991
|
if (resolvedPath !== resolvedDir && !resolvedPath.startsWith(resolvedDir + sep)) {
|
|
2759
1992
|
throw new Error("Invalid file path");
|
|
2760
1993
|
}
|
|
2761
|
-
await
|
|
1994
|
+
await writeFile(filePath, buffer, { mode: 384 });
|
|
2762
1995
|
const meta = { imageId, filePath, mimeType, originalName, createdAt: Date.now(), contextId };
|
|
2763
|
-
await
|
|
1996
|
+
await writeFile(`${filePath}.meta.json`, JSON.stringify(meta), { encoding: "utf-8", mode: 384 });
|
|
2764
1997
|
return meta;
|
|
2765
1998
|
}
|
|
2766
1999
|
/**
|
|
@@ -2778,7 +2011,7 @@ var ImageStorageService = class {
|
|
|
2778
2011
|
const files = await readdir3(this.dir);
|
|
2779
2012
|
const metaFile = files.find((f) => f.startsWith(imageId) && f.endsWith(".meta.json"));
|
|
2780
2013
|
if (!metaFile) return null;
|
|
2781
|
-
const raw = JSON.parse(await
|
|
2014
|
+
const raw = JSON.parse(await readFile4(join4(this.dir, metaFile), "utf-8"));
|
|
2782
2015
|
if (expectedContextId !== void 0 && raw.contextId !== expectedContextId) return null;
|
|
2783
2016
|
if (!existsSync2(raw.filePath)) return null;
|
|
2784
2017
|
return { filePath: raw.filePath, mimeType: raw.mimeType, contextId: raw.contextId };
|
|
@@ -2804,7 +2037,7 @@ var ImageStorageService = class {
|
|
|
2804
2037
|
await Promise.all(
|
|
2805
2038
|
files.filter((f) => f.endsWith(".meta.json")).map(async (file) => {
|
|
2806
2039
|
try {
|
|
2807
|
-
const raw = JSON.parse(await
|
|
2040
|
+
const raw = JSON.parse(await readFile4(join4(this.dir, file), "utf-8"));
|
|
2808
2041
|
if (raw.contextId === contextId) {
|
|
2809
2042
|
await unlink2(raw.filePath).catch(() => {
|
|
2810
2043
|
});
|
|
@@ -2819,7 +2052,7 @@ var ImageStorageService = class {
|
|
|
2819
2052
|
async readImageAsBase64(imageId, expectedContextId) {
|
|
2820
2053
|
const result = await this.getImagePath(imageId, expectedContextId);
|
|
2821
2054
|
if (!result) return null;
|
|
2822
|
-
const buffer = await
|
|
2055
|
+
const buffer = await readFile4(result.filePath);
|
|
2823
2056
|
return { base64: buffer.toString("base64"), mimeType: result.mimeType };
|
|
2824
2057
|
}
|
|
2825
2058
|
async listExpiredImages(ttlMs = IMAGE_TTL_MS) {
|
|
@@ -2834,7 +2067,7 @@ var ImageStorageService = class {
|
|
|
2834
2067
|
for (const file of files) {
|
|
2835
2068
|
if (!file.endsWith(".meta.json")) continue;
|
|
2836
2069
|
try {
|
|
2837
|
-
const raw = JSON.parse(await
|
|
2070
|
+
const raw = JSON.parse(await readFile4(join4(this.dir, file), "utf-8"));
|
|
2838
2071
|
if (now - raw.createdAt > ttlMs) expired.push(raw);
|
|
2839
2072
|
} catch {
|
|
2840
2073
|
}
|
|
@@ -2903,10 +2136,10 @@ import { basename } from "path";
|
|
|
2903
2136
|
import { TRPCError as TRPCError3 } from "@trpc/server";
|
|
2904
2137
|
|
|
2905
2138
|
// ../server/src/services/cloud-session-store.ts
|
|
2906
|
-
import { readFile as
|
|
2907
|
-
import { dirname
|
|
2139
|
+
import { readFile as readFile5, writeFile as writeFile2, mkdir as mkdir4, chmod, unlink as unlink3 } from "fs/promises";
|
|
2140
|
+
import { dirname } from "path";
|
|
2908
2141
|
import { randomBytes as randomBytes2, createHash, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
2909
|
-
var
|
|
2142
|
+
var FILENAME = "cloud-session.json";
|
|
2910
2143
|
var STATE_TTL_MS = 5 * 60 * 1e3;
|
|
2911
2144
|
function accountIdFor(cloudUrl, userId) {
|
|
2912
2145
|
return createHash("sha256").update(`${cloudUrl.replace(/\/+$/, "")}|${userId}`).digest("hex").slice(0, 32);
|
|
@@ -2921,11 +2154,11 @@ var CloudSessionStore = class {
|
|
|
2921
2154
|
* root, but a launch directory owning its own key keeps both together.
|
|
2922
2155
|
*/
|
|
2923
2156
|
getFilePath() {
|
|
2924
|
-
return keyedFilePath(
|
|
2157
|
+
return keyedFilePath(FILENAME);
|
|
2925
2158
|
}
|
|
2926
2159
|
/** Where older versions kept it — read only, so an older Runeya keeps working. */
|
|
2927
2160
|
getLegacyFilePath() {
|
|
2928
|
-
return legacyMachineFilePath(
|
|
2161
|
+
return legacyMachineFilePath(FILENAME);
|
|
2929
2162
|
}
|
|
2930
2163
|
/**
|
|
2931
2164
|
* Read the machine file, falling back to the launch directory of old.
|
|
@@ -2935,9 +2168,9 @@ var CloudSessionStore = class {
|
|
|
2935
2168
|
*/
|
|
2936
2169
|
async readFromDisk() {
|
|
2937
2170
|
try {
|
|
2938
|
-
return await
|
|
2171
|
+
return await readFile5(await this.getFilePath(), "utf-8");
|
|
2939
2172
|
} catch {
|
|
2940
|
-
return
|
|
2173
|
+
return readFile5(this.getLegacyFilePath(), "utf-8");
|
|
2941
2174
|
}
|
|
2942
2175
|
}
|
|
2943
2176
|
/**
|
|
@@ -3011,12 +2244,12 @@ var CloudSessionStore = class {
|
|
|
3011
2244
|
}
|
|
3012
2245
|
async save() {
|
|
3013
2246
|
const filePath = await this.getFilePath();
|
|
3014
|
-
await
|
|
2247
|
+
await mkdir4(dirname(filePath), { recursive: true });
|
|
3015
2248
|
const payload = { version: 2, accounts: this.accounts };
|
|
3016
2249
|
const tmpPath = filePath + ".tmp";
|
|
3017
|
-
await
|
|
2250
|
+
await writeFile2(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
3018
2251
|
await renameWithRetry(tmpPath, filePath);
|
|
3019
|
-
await
|
|
2252
|
+
await chmod(filePath, 384);
|
|
3020
2253
|
}
|
|
3021
2254
|
/**
|
|
3022
2255
|
* Ranger le token obtenu par l'échange de jeton à usage unique.
|
|
@@ -3152,8 +2385,8 @@ var cloudSessionStore = new CloudSessionStore();
|
|
|
3152
2385
|
|
|
3153
2386
|
// ../server/src/services/lan-mode.ts
|
|
3154
2387
|
import { networkInterfaces } from "os";
|
|
3155
|
-
import { readFile as
|
|
3156
|
-
import { join as
|
|
2388
|
+
import { readFile as readFile6, writeFile as writeFile3, mkdir as mkdir5 } from "fs/promises";
|
|
2389
|
+
import { join as join5 } from "path";
|
|
3157
2390
|
import { z as z4 } from "zod";
|
|
3158
2391
|
var LOOPBACK2 = "127.0.0.1";
|
|
3159
2392
|
var ANY = "0.0.0.0";
|
|
@@ -3166,11 +2399,11 @@ function instanceKey() {
|
|
|
3166
2399
|
return env.DATA_DIR;
|
|
3167
2400
|
}
|
|
3168
2401
|
function networkFilePath() {
|
|
3169
|
-
return
|
|
2402
|
+
return join5(machineRoot(), NETWORK_FILE);
|
|
3170
2403
|
}
|
|
3171
2404
|
async function readFileState() {
|
|
3172
2405
|
try {
|
|
3173
|
-
return NetworkFileSchema.parse(JSON.parse(await
|
|
2406
|
+
return NetworkFileSchema.parse(JSON.parse(await readFile6(networkFilePath(), "utf-8")));
|
|
3174
2407
|
} catch {
|
|
3175
2408
|
return { instances: {} };
|
|
3176
2409
|
}
|
|
@@ -3181,12 +2414,12 @@ async function readPersisted() {
|
|
|
3181
2414
|
}
|
|
3182
2415
|
async function writePersisted(lanEnabled) {
|
|
3183
2416
|
const root = machineRoot();
|
|
3184
|
-
await
|
|
2417
|
+
await mkdir5(root, { recursive: true });
|
|
3185
2418
|
const state = await readFileState();
|
|
3186
2419
|
state.instances[instanceKey()] = { lanEnabled };
|
|
3187
2420
|
const filePath = networkFilePath();
|
|
3188
2421
|
const tmpPath = filePath + ".tmp";
|
|
3189
|
-
await
|
|
2422
|
+
await writeFile3(tmpPath, JSON.stringify(state, null, 2), "utf-8");
|
|
3190
2423
|
await renameWithRetry(tmpPath, filePath);
|
|
3191
2424
|
}
|
|
3192
2425
|
function registerHttpServer(server) {
|
|
@@ -4282,6 +3515,12 @@ function generateKanbanPopulatePrompt(board, scope) {
|
|
|
4282
3515
|
lines.push("");
|
|
4283
3516
|
return lines.join("\n");
|
|
4284
3517
|
}
|
|
3518
|
+
async function kanbanContextFor(conv, scope) {
|
|
3519
|
+
if (!conv?.kanbanBoardId) return "";
|
|
3520
|
+
const kanbanBoard = await kanbanStore.get(conv.kanbanBoardId);
|
|
3521
|
+
if (!kanbanBoard) return "";
|
|
3522
|
+
return "\n\n" + (conv.kanbanCardId ? await buildKanbanCardPrompt(kanbanBoard, conv.kanbanCardId, scope) : generateKanbanPopulatePrompt(kanbanBoard, scope));
|
|
3523
|
+
}
|
|
4285
3524
|
|
|
4286
3525
|
// ../server/src/services/ai-runners/claude-code-runner.ts
|
|
4287
3526
|
import { spawn } from "child_process";
|
|
@@ -4454,7 +3693,7 @@ function tailTranscript(opts) {
|
|
|
4454
3693
|
|
|
4455
3694
|
// ../server/src/services/ai-runners/claude-pty-runner.ts
|
|
4456
3695
|
async function getAgentManager() {
|
|
4457
|
-
const { agentManager: agentManager2 } = await import("./agent-manager-
|
|
3696
|
+
const { agentManager: agentManager2 } = await import("./agent-manager-46C4NIQ2.js");
|
|
4458
3697
|
return agentManager2;
|
|
4459
3698
|
}
|
|
4460
3699
|
function buildPtyArgs(params, sessionId, opts) {
|
|
@@ -4472,7 +3711,7 @@ ${TOKEN_INSTRUCTIONS}`;
|
|
|
4472
3711
|
"--append-system-prompt",
|
|
4473
3712
|
systemPrompt,
|
|
4474
3713
|
...mcpArgs,
|
|
4475
|
-
prompt
|
|
3714
|
+
...opts.deferPrompt ? [] : [prompt]
|
|
4476
3715
|
];
|
|
4477
3716
|
return args;
|
|
4478
3717
|
}
|
|
@@ -4521,7 +3760,9 @@ async function runClaudeViaPty(params) {
|
|
|
4521
3760
|
const { agentId, signal, skipTokenInstructions } = params;
|
|
4522
3761
|
if (!agentId) throw new Error("runClaudeViaPty requires an agentId");
|
|
4523
3762
|
const sessionId = params.sessionId && params.sessionId.length > 0 ? params.sessionId : randomUUID4();
|
|
4524
|
-
const
|
|
3763
|
+
const deferPrompt = !!params.mcpConfig;
|
|
3764
|
+
const mcpReadyId = deferPrompt ? randomUUID4() : void 0;
|
|
3765
|
+
const args = buildPtyArgs(params, sessionId, { excludeMcp: true, deferPrompt });
|
|
4525
3766
|
const agentMgr = await getAgentManager();
|
|
4526
3767
|
const displayArgs = args.map((a) => {
|
|
4527
3768
|
if (a === params.prompt) return "<prompt>";
|
|
@@ -4601,7 +3842,7 @@ async function runClaudeViaPty(params) {
|
|
|
4601
3842
|
}
|
|
4602
3843
|
}
|
|
4603
3844
|
});
|
|
4604
|
-
agentMgr.agentMutation(agentId, "ai.spawnPty", {
|
|
3845
|
+
const spawned = agentMgr.agentMutation(agentId, "ai.spawnPty", {
|
|
4605
3846
|
cli: "claude",
|
|
4606
3847
|
args,
|
|
4607
3848
|
env: params.injectedEnv && Object.keys(params.injectedEnv).length > 0 ? params.injectedEnv : void 0,
|
|
@@ -4621,7 +3862,9 @@ async function runClaudeViaPty(params) {
|
|
|
4621
3862
|
// signe elle-même, faute de requête HTTP d'où le tirer.
|
|
4622
3863
|
mcpApiToken: params.apiToken || void 0,
|
|
4623
3864
|
mcpProjectId: params.projectId || void 0,
|
|
4624
|
-
mcpOrgId: params.orgId || void 0
|
|
3865
|
+
mcpOrgId: params.orgId || void 0,
|
|
3866
|
+
// Un agent ancien ignore ce champ : le plafond de `waitForPtyInput` couvre ce cas.
|
|
3867
|
+
mcpReadyId
|
|
4625
3868
|
}).then(({ spawnId: id }) => {
|
|
4626
3869
|
spawnId = id;
|
|
4627
3870
|
if (signal.aborted) {
|
|
@@ -4631,6 +3874,18 @@ async function runClaudeViaPty(params) {
|
|
|
4631
3874
|
}).catch((err) => {
|
|
4632
3875
|
fail(new Error(`Failed to spawn claude PTY: ${err.message}`));
|
|
4633
3876
|
});
|
|
3877
|
+
if (mcpReadyId) {
|
|
3878
|
+
void spawned.then(async () => {
|
|
3879
|
+
if (settled || !spawnId) return;
|
|
3880
|
+
const id = spawnId;
|
|
3881
|
+
const wait = await waitForPtyInput({ agentId, spawnId: id, mcpReadyId, runner: "claude-code", signal: innerAbort.signal });
|
|
3882
|
+
if (settled || wait.status !== "ready") return;
|
|
3883
|
+
if (!wait.mcpReady) console.warn(`[claude-pty] serveur MCP non signal\xE9 apr\xE8s ${PTY_MCP_READY_MAX_MS} ms, prompt \xE9crit quand m\xEAme (session ${sessionId})`);
|
|
3884
|
+
await pasteToPty((data) => agentMgr.agentMutation(agentId, "ai.writePty", { spawnId: id, data }), params.prompt, { lead: PASTED_TASK_LEAD });
|
|
3885
|
+
}).catch((err) => {
|
|
3886
|
+
fail(new Error(`Failed to write the prompt to the claude PTY: ${err.message}`));
|
|
3887
|
+
});
|
|
3888
|
+
}
|
|
4634
3889
|
signal.addEventListener("abort", () => fail(new Error("Aborted")), { once: true });
|
|
4635
3890
|
void tail.whenDone.then(() => {
|
|
4636
3891
|
if (settled) return;
|
|
@@ -5139,7 +4394,7 @@ async function shutdownCodexAppServer() {
|
|
|
5139
4394
|
}
|
|
5140
4395
|
}
|
|
5141
4396
|
async function getAgentManager2() {
|
|
5142
|
-
const { agentManager: agentManager2 } = await import("./agent-manager-
|
|
4397
|
+
const { agentManager: agentManager2 } = await import("./agent-manager-46C4NIQ2.js");
|
|
5143
4398
|
return agentManager2;
|
|
5144
4399
|
}
|
|
5145
4400
|
function parseRuneyaCurlCommand(command) {
|
|
@@ -5867,12 +5122,6 @@ async function* drainSession(session, signal, isReconnect) {
|
|
|
5867
5122
|
if (signal?.aborted) return;
|
|
5868
5123
|
}
|
|
5869
5124
|
}
|
|
5870
|
-
async function kanbanContextFor(conv, scope) {
|
|
5871
|
-
if (!conv?.kanbanBoardId) return "";
|
|
5872
|
-
const kanbanBoard = await kanbanStore.get(conv.kanbanBoardId);
|
|
5873
|
-
if (!kanbanBoard) return "";
|
|
5874
|
-
return "\n\n" + (conv.kanbanCardId ? await buildKanbanCardPrompt(kanbanBoard, conv.kanbanCardId, scope) : generateKanbanPopulatePrompt(kanbanBoard, scope));
|
|
5875
|
-
}
|
|
5876
5125
|
async function cliToken(ctx) {
|
|
5877
5126
|
const bearer = ctx.authorization?.startsWith("Bearer ") ? ctx.authorization.slice(7) : void 0;
|
|
5878
5127
|
if (!bearer || ctx.callerType !== "app") return bearer;
|
|
@@ -6057,6 +5306,26 @@ async function conversationCwd(conv) {
|
|
|
6057
5306
|
function startCliSession(runner, convId, prompt, opts = {}) {
|
|
6058
5307
|
return runner === "codex" ? startCodexSession(convId, prompt, opts) : startClaudeCodeSession(convId, prompt, opts);
|
|
6059
5308
|
}
|
|
5309
|
+
async function writeWhenPtyReady(opts) {
|
|
5310
|
+
const { agentId, spawnId } = opts;
|
|
5311
|
+
const write = (data) => agentManager.agentMutation(agentId, "ai.writePty", { spawnId, data });
|
|
5312
|
+
try {
|
|
5313
|
+
const wait = await waitForPtyInput({ agentId, spawnId, mcpReadyId: opts.mcpReadyId, runner: opts.runner });
|
|
5314
|
+
if (wait.status !== "ready") return;
|
|
5315
|
+
if (opts.contexts.length > 0) {
|
|
5316
|
+
const body = opts.contexts.map((c) => `### ${c.label}
|
|
5317
|
+
${c.text}`).join("\n\n");
|
|
5318
|
+
await write(`\x1B[200~---------
|
|
5319
|
+
Context:
|
|
5320
|
+
\x1B[201~\x1B[200~${body}\x1B[201~\x1B[200~
|
|
5321
|
+
---------
|
|
5322
|
+
\x1B[201~`);
|
|
5323
|
+
}
|
|
5324
|
+
if (opts.initialPrompt) await pasteToPty(write, opts.initialPrompt, { lead: PASTED_TASK_LEAD });
|
|
5325
|
+
} catch (err) {
|
|
5326
|
+
console.warn("[chat.pty.start] deferred writePty failed:", err.message);
|
|
5327
|
+
}
|
|
5328
|
+
}
|
|
6060
5329
|
var chatRouter = router({
|
|
6061
5330
|
conversations: router({
|
|
6062
5331
|
/**
|
|
@@ -6839,7 +6108,12 @@ ${input.prompt}` : input.prompt;
|
|
|
6839
6108
|
attachedContexts: z6.array(z6.object({
|
|
6840
6109
|
label: z6.string().max(200),
|
|
6841
6110
|
text: z6.string().max(1e6)
|
|
6842
|
-
})).max(20).optional()
|
|
6111
|
+
})).max(20).optional(),
|
|
6112
|
+
/**
|
|
6113
|
+
* Prompt collé puis soumis dès que le CLI accepte la saisie (remplissage
|
|
6114
|
+
* d'un tableau kanban). Ignoré quand on se rattache à un terminal existant.
|
|
6115
|
+
*/
|
|
6116
|
+
initialPrompt: z6.string().max(1e6).optional()
|
|
6843
6117
|
})).mutation(async ({ input, ctx }) => {
|
|
6844
6118
|
const t0 = Date.now();
|
|
6845
6119
|
const lap = (label) => {
|
|
@@ -6911,6 +6185,8 @@ ${input.prompt}` : input.prompt;
|
|
|
6911
6185
|
}
|
|
6912
6186
|
const injectedEnv = await resolveInjectedEnv(input.environmentId);
|
|
6913
6187
|
lap("resolveInjectedEnv");
|
|
6188
|
+
const hasStartupInput = (input.attachedContexts?.length ?? 0) > 0 || !!input.initialPrompt?.trim();
|
|
6189
|
+
const mcpReadyId = input.enableMcp && hasStartupInput ? randomUUID5() : void 0;
|
|
6914
6190
|
const res = await agentManager.agentMutation(
|
|
6915
6191
|
agentId,
|
|
6916
6192
|
"ai.spawnPty",
|
|
@@ -6936,65 +6212,23 @@ ${input.prompt}` : input.prompt;
|
|
|
6936
6212
|
mcpApiBaseUrl: input.apiBaseUrl ?? `http://localhost:${env.PORT}`,
|
|
6937
6213
|
mcpApiToken: jwt,
|
|
6938
6214
|
...ptyRef?.id ? { mcpProjectId: ptyRef.id } : {},
|
|
6939
|
-
...ptyRef?.orgId ? { mcpOrgId: ptyRef.orgId } : {}
|
|
6215
|
+
...ptyRef?.orgId ? { mcpOrgId: ptyRef.orgId } : {},
|
|
6216
|
+
// Un agent ancien ignore ce champ : le plafond de `waitForPtyInput` couvre ce cas.
|
|
6217
|
+
...mcpReadyId ? { mcpReadyId } : {}
|
|
6940
6218
|
}
|
|
6941
6219
|
);
|
|
6942
6220
|
lap("agent ai.spawnPty");
|
|
6943
|
-
const contexts =
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
const injection = `\x1B[200~---------
|
|
6948
|
-
Context:
|
|
6949
|
-
\x1B[201~\x1B[200~${body}\x1B[201~\x1B[200~
|
|
6950
|
-
---------
|
|
6951
|
-
\x1B[201~`;
|
|
6952
|
-
const doInject = () => {
|
|
6953
|
-
agentManager.agentMutation(agentId, "ai.writePty", { spawnId: res.spawnId, data: injection }).catch((err) => {
|
|
6954
|
-
console.warn("[chat.pty.start] deferred context writePty failed:", err.message);
|
|
6955
|
-
});
|
|
6956
|
-
};
|
|
6957
|
-
const startedAt = Date.now();
|
|
6958
|
-
const MIN_MS = input.runner === "codex" ? 500 : 1200;
|
|
6959
|
-
const QUIET_MS = 400;
|
|
6960
|
-
const MAX_MS = 8e3;
|
|
6961
|
-
let injected = false;
|
|
6962
|
-
let quietTimer = null;
|
|
6963
|
-
let maxTimer = null;
|
|
6964
|
-
let unsub = null;
|
|
6965
|
-
const cleanup = () => {
|
|
6966
|
-
if (quietTimer) clearTimeout(quietTimer);
|
|
6967
|
-
if (maxTimer) clearTimeout(maxTimer);
|
|
6968
|
-
unsub?.();
|
|
6969
|
-
};
|
|
6970
|
-
const inject = () => {
|
|
6971
|
-
if (injected) return;
|
|
6972
|
-
injected = true;
|
|
6973
|
-
cleanup();
|
|
6974
|
-
doInject();
|
|
6975
|
-
};
|
|
6976
|
-
const scheduleQuiet = () => {
|
|
6977
|
-
if (quietTimer) clearTimeout(quietTimer);
|
|
6978
|
-
const wait = Math.max(QUIET_MS, MIN_MS - (Date.now() - startedAt));
|
|
6979
|
-
quietTimer = setTimeout(inject, wait);
|
|
6980
|
-
};
|
|
6981
|
-
maxTimer = setTimeout(inject, MAX_MS);
|
|
6982
|
-
unsub = agentManager.agentSubscribe(
|
|
6221
|
+
const contexts = input.attachedContexts ?? [];
|
|
6222
|
+
const initialPrompt = input.initialPrompt?.trim() ? input.initialPrompt : void 0;
|
|
6223
|
+
if (contexts.length > 0 || initialPrompt) {
|
|
6224
|
+
void writeWhenPtyReady({
|
|
6983
6225
|
agentId,
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
cleanup();
|
|
6991
|
-
return;
|
|
6992
|
-
}
|
|
6993
|
-
scheduleQuiet();
|
|
6994
|
-
},
|
|
6995
|
-
() => {
|
|
6996
|
-
}
|
|
6997
|
-
);
|
|
6226
|
+
spawnId: res.spawnId,
|
|
6227
|
+
runner: input.runner,
|
|
6228
|
+
mcpReadyId,
|
|
6229
|
+
contexts,
|
|
6230
|
+
initialPrompt
|
|
6231
|
+
});
|
|
6998
6232
|
}
|
|
6999
6233
|
noteSpawnConversation(res.spawnId, input.sessionId);
|
|
7000
6234
|
return { spawnId: res.spawnId, agentId, reattached: false };
|
|
@@ -7255,9 +6489,6 @@ export {
|
|
|
7255
6489
|
cloudFetch,
|
|
7256
6490
|
readCloudIdentity,
|
|
7257
6491
|
attachServiceToProject,
|
|
7258
|
-
environmentOverridesStore,
|
|
7259
|
-
filterEnvForAi,
|
|
7260
|
-
environmentResolver,
|
|
7261
6492
|
auditService,
|
|
7262
6493
|
isShareable,
|
|
7263
6494
|
logParserStore,
|
|
@@ -7270,6 +6501,7 @@ export {
|
|
|
7270
6501
|
networkInfo,
|
|
7271
6502
|
applyPersistedLanMode,
|
|
7272
6503
|
setLanMode,
|
|
6504
|
+
runeyaMcpConfig,
|
|
7273
6505
|
Id128Schema,
|
|
7274
6506
|
ConversationIdSchema,
|
|
7275
6507
|
NameSchema,
|
|
@@ -7293,6 +6525,7 @@ export {
|
|
|
7293
6525
|
executeKanbanCommands,
|
|
7294
6526
|
executeKanbanBoardCommands,
|
|
7295
6527
|
buildKanbanCardPrompt,
|
|
6528
|
+
kanbanContextFor,
|
|
7296
6529
|
ClaudeCodeRunner,
|
|
7297
6530
|
shutdownCodexAppServer,
|
|
7298
6531
|
CodexRunner,
|
|
@@ -7302,4 +6535,4 @@ export {
|
|
|
7302
6535
|
startCliSession,
|
|
7303
6536
|
chatRouter
|
|
7304
6537
|
};
|
|
7305
|
-
//# sourceMappingURL=chunk-
|
|
6538
|
+
//# sourceMappingURL=chunk-2NE52CL5.js.map
|