@rivus/gateway 0.16.6 → 0.18.0
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/LICENSE +674 -21
- package/README.md +25 -0
- package/dist/bootstrap/herdr-feishu.d.ts +37 -0
- package/dist/bootstrap/herdr-feishu.js +853 -0
- package/dist/bootstrap/pi-feishu.d.ts +281 -16
- package/dist/bootstrap/pi-feishu.js +17315 -345
- package/dist/chunks/background-session-authority.js +2 -38
- package/dist/chunks/rivus-daemon-cli.d.ts +1115 -0
- package/dist/chunks/rivus-node-entrypoint.js +735 -847
- package/dist/chunks/{node-rivus-deployment-manifest.js → runtime-cache.js} +1118 -1476
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +1 -0
- package/dist/index.d.ts +65 -3
- package/dist/index.js +3 -1215
- package/dist/mcp.d.ts +7 -63
- package/dist/mcp.js +3 -250
- package/package.json +21 -13
- package/templates/a-share-briefing-analysis.mjs +3 -1
- package/templates/a-share-briefing-renderer.mjs +15 -5
- package/templates/a-share-index-evidence.mjs +9 -3
- package/templates/a-share-market-briefing.mjs +3 -1
- package/templates/a-share-overseas-evidence.mjs +12 -4
- package/templates/a-share-policy-evidence.mjs +30 -10
- package/templates/a-share-sector-evidence.mjs +3 -1
- package/templates/acp-stdio-proxy.mjs +10 -4
- package/templates/current-weather.mjs +36 -12
- package/templates/herdr-feishu.mjs +30 -0
- package/templates/html-drive-tools.mjs +36 -12
- package/templates/pi-feishu-deployment.bootstrap.ts +1 -3
- package/templates/pi-feishu.bootstrap.ts +1 -242
- package/templates/rivus-agents.plugin.mjs +3 -1
- package/dist/chunks/background-session-control-input.js +0 -45
- package/dist/chunks/background-session-service.d.ts +0 -390
- package/dist/chunks/index.d.ts +0 -4703
- package/dist/chunks/service.js +0 -12112
- package/templates/langfuse-drive-e2e.mjs +0 -175
|
@@ -1,143 +1,11 @@
|
|
|
1
|
-
import { Cause, Deferred, Effect, Exit, Option } from "effect";
|
|
2
1
|
import { isRivusRuntimeToolId } from "@rivus/runtime";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { dirname, isAbsolute } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { Cause, Deferred, Effect, Exit, Semaphore } from "effect";
|
|
5
|
+
import { chmod, mkdir, open, readFile, rename, stat, unlink } from "node:fs/promises";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
5
7
|
import { createConnection, createServer } from "node:net";
|
|
6
|
-
import { dirname, isAbsolute, join } from "node:path";
|
|
7
8
|
import { readPersistenceFile } from "@rivus/platform/persistence";
|
|
8
|
-
import { homedir } from "node:os";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
//#region src/platform/runtime-pool/runtime-cache.ts
|
|
11
|
-
function createRuntimeCache() {
|
|
12
|
-
const entries = /* @__PURE__ */ new Map();
|
|
13
|
-
const serial = Effect.unsafeMakeSemaphore(1);
|
|
14
|
-
const reserve = (key) => serial.withPermits(1)(Effect.gen(function* () {
|
|
15
|
-
const current = entries.get(key);
|
|
16
|
-
if (current) return {
|
|
17
|
-
created: false,
|
|
18
|
-
entry: current
|
|
19
|
-
};
|
|
20
|
-
const entry = {
|
|
21
|
-
deferred: yield* Deferred.make(),
|
|
22
|
-
initializationStarted: false,
|
|
23
|
-
key
|
|
24
|
-
};
|
|
25
|
-
entries.set(key, entry);
|
|
26
|
-
return {
|
|
27
|
-
created: true,
|
|
28
|
-
entry
|
|
29
|
-
};
|
|
30
|
-
}));
|
|
31
|
-
const start = (entry, create) => Effect.uninterruptible(Effect.gen(function* () {
|
|
32
|
-
if (!(yield* serial.withPermits(1)(Effect.sync(() => {
|
|
33
|
-
if (entry.initializationStarted) return false;
|
|
34
|
-
entry.initializationStarted = true;
|
|
35
|
-
return true;
|
|
36
|
-
})))) return;
|
|
37
|
-
const initialization = create().pipe(Effect.tapError(() => serial.withPermits(1)(Effect.sync(() => {
|
|
38
|
-
if (entries.get(entry.key) === entry) entries.delete(entry.key);
|
|
39
|
-
}))), Effect.exit, Effect.flatMap((exit) => Deferred.done(entry.deferred, exit)), Effect.asVoid);
|
|
40
|
-
yield* Effect.forkDaemon(initialization);
|
|
41
|
-
}));
|
|
42
|
-
const getOrCreate = (key, create) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
43
|
-
const selected = yield* reserve(key);
|
|
44
|
-
yield* start(selected.entry, create);
|
|
45
|
-
return {
|
|
46
|
-
entry: selected.entry,
|
|
47
|
-
runtime: yield* restore(Deferred.await(selected.entry.deferred))
|
|
48
|
-
};
|
|
49
|
-
}));
|
|
50
|
-
return {
|
|
51
|
-
drain: () => serial.withPermits(1)(Effect.sync(() => {
|
|
52
|
-
const drained = Object.freeze([...entries.values()]);
|
|
53
|
-
entries.clear();
|
|
54
|
-
return drained;
|
|
55
|
-
})),
|
|
56
|
-
getExisting: (key) => Effect.gen(function* () {
|
|
57
|
-
const entry = yield* serial.withPermits(1)(Effect.sync(() => entries.get(key)));
|
|
58
|
-
if (!entry) return void 0;
|
|
59
|
-
return {
|
|
60
|
-
entry,
|
|
61
|
-
runtime: yield* Deferred.await(entry.deferred)
|
|
62
|
-
};
|
|
63
|
-
}),
|
|
64
|
-
getOrCreate,
|
|
65
|
-
isCurrent: (key, entry) => serial.withPermits(1)(Effect.sync(() => entries.get(key) === entry)),
|
|
66
|
-
reserve,
|
|
67
|
-
size: () => serial.withPermits(1)(Effect.sync(() => entries.size)),
|
|
68
|
-
start
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function disposeRuntimeCacheEntries(options) {
|
|
72
|
-
const disposal = Effect.gen(function* () {
|
|
73
|
-
const completions = yield* Effect.forEach(options.entries, (entry) => Effect.gen(function* () {
|
|
74
|
-
const completion = yield* Deferred.make();
|
|
75
|
-
yield* Effect.forkDaemon(Deferred.await(entry.deferred).pipe(Effect.flatMap(options.dispose), Effect.exit, Effect.flatMap((exit) => Deferred.succeed(completion, exit)), Effect.asVoid));
|
|
76
|
-
return completion;
|
|
77
|
-
}), { concurrency: "unbounded" });
|
|
78
|
-
const failures = (yield* Effect.forEach(completions, Deferred.await, { concurrency: "unbounded" })).filter(Exit.isFailure).map(({ cause }) => Cause.squash(cause));
|
|
79
|
-
if (failures.length > 0) return yield* Effect.fail(new AggregateError(failures, options.failureMessage));
|
|
80
|
-
});
|
|
81
|
-
return options.timeout ? disposal.pipe(Effect.timeoutFail({
|
|
82
|
-
duration: options.timeout.milliseconds,
|
|
83
|
-
onTimeout: options.timeout.onTimeout
|
|
84
|
-
})) : disposal;
|
|
85
|
-
}
|
|
86
|
-
function invokeRuntimeControl(runtime, control) {
|
|
87
|
-
return runtime.pipe(Effect.flatMap((selected) => selected ? control(selected) ?? Effect.succeed(false) : Effect.succeed(false)));
|
|
88
|
-
}
|
|
89
|
-
//#endregion
|
|
90
|
-
//#region src/adapters/agent/runtime/process-agent-runtime-adapter.ts
|
|
91
|
-
function toEffectAgentRuntimeInput(input) {
|
|
92
|
-
const onUpdate = input.onUpdate;
|
|
93
|
-
return {
|
|
94
|
-
...runtimeInputFields(input),
|
|
95
|
-
...onUpdate ? { onUpdate: (update) => Effect.tryPromise({
|
|
96
|
-
try: async () => onUpdate(update),
|
|
97
|
-
catch: (failure) => failure
|
|
98
|
-
}) } : {}
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
function toProcessAgentRuntimeInput(input, runEffect) {
|
|
102
|
-
const onUpdate = input.onUpdate;
|
|
103
|
-
return {
|
|
104
|
-
...runtimeInputFields(input),
|
|
105
|
-
...onUpdate ? { onUpdate: (update) => runEffect(onUpdate(update)) } : {}
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
function toEffectAgentRuntime(runtime, runEffect) {
|
|
109
|
-
const cancel = runtime.cancel?.bind(runtime);
|
|
110
|
-
const dispose = runtime.dispose?.bind(runtime);
|
|
111
|
-
const steer = runtime.steer?.bind(runtime);
|
|
112
|
-
return {
|
|
113
|
-
...runtime.concurrency ? { concurrency: runtime.concurrency } : {},
|
|
114
|
-
...cancel ? { cancel: (input) => Effect.tryPromise({
|
|
115
|
-
try: () => cancel(input),
|
|
116
|
-
catch: (failure) => failure
|
|
117
|
-
}) } : {},
|
|
118
|
-
...dispose ? { dispose: () => Effect.tryPromise({
|
|
119
|
-
try: async () => dispose(),
|
|
120
|
-
catch: (failure) => failure
|
|
121
|
-
}) } : {},
|
|
122
|
-
run: (input) => Effect.tryPromise({
|
|
123
|
-
try: () => runtime.run(toProcessAgentRuntimeInput(input, runEffect)),
|
|
124
|
-
catch: (failure) => failure
|
|
125
|
-
}),
|
|
126
|
-
...steer ? { steer: (input) => Effect.tryPromise({
|
|
127
|
-
try: () => steer(input),
|
|
128
|
-
catch: (failure) => failure
|
|
129
|
-
}) } : {}
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
function runtimeInputFields(input) {
|
|
133
|
-
return {
|
|
134
|
-
...input.invocation ? { invocation: input.invocation } : {},
|
|
135
|
-
...input.payload === void 0 ? {} : { payload: input.payload },
|
|
136
|
-
sessionKey: input.sessionKey,
|
|
137
|
-
text: input.text
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
//#endregion
|
|
141
9
|
//#region src/platform/values/deep-freeze.ts
|
|
142
10
|
function deepFreeze(value) {
|
|
143
11
|
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -147,630 +15,384 @@ function deepFreeze(value) {
|
|
|
147
15
|
return value;
|
|
148
16
|
}
|
|
149
17
|
//#endregion
|
|
150
|
-
//#region src/
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (operation !== "status" && operation !== "set" && operation !== "rollback") return { error: "model requires one of status, set, rollback" };
|
|
176
|
-
let expectedRevision;
|
|
177
|
-
let json = false;
|
|
178
|
-
let model;
|
|
179
|
-
let requestId;
|
|
180
|
-
let verbose = false;
|
|
181
|
-
for (let index = 1; index < argv.length; index += 1) {
|
|
182
|
-
const argument = argv[index];
|
|
183
|
-
if (argument === "--json") {
|
|
184
|
-
if (json) return { error: "--json may only be provided once" };
|
|
185
|
-
json = true;
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (argument === "--verbose") {
|
|
189
|
-
if (verbose) return { error: "--verbose may only be provided once" };
|
|
190
|
-
verbose = true;
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
if (argument === "--request") {
|
|
194
|
-
if (requestId !== void 0) return { error: "--request may only be provided once" };
|
|
195
|
-
const value = argv[index + 1];
|
|
196
|
-
if (!value || value.startsWith("-")) return { error: "--request requires an id" };
|
|
197
|
-
requestId = value;
|
|
198
|
-
index += 1;
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
if (argument.startsWith("--request=")) {
|
|
202
|
-
if (requestId !== void 0) return { error: "--request may only be provided once" };
|
|
203
|
-
requestId = argument.slice(10);
|
|
204
|
-
if (!requestId) return { error: "--request requires an id" };
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (argument === "--expected-revision") {
|
|
208
|
-
if (expectedRevision !== void 0) return { error: "--expected-revision may only be provided once" };
|
|
209
|
-
const value = argv[index + 1];
|
|
210
|
-
if (!value || value.startsWith("-")) return { error: "--expected-revision requires a non-negative integer" };
|
|
211
|
-
const parsed = parseRevision(value);
|
|
212
|
-
if (parsed === void 0) return { error: "--expected-revision requires a non-negative integer" };
|
|
213
|
-
expectedRevision = parsed;
|
|
214
|
-
index += 1;
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (argument.startsWith("--expected-revision=")) {
|
|
218
|
-
if (expectedRevision !== void 0) return { error: "--expected-revision may only be provided once" };
|
|
219
|
-
const parsed = parseRevision(argument.slice(20));
|
|
220
|
-
if (parsed === void 0) return { error: "--expected-revision requires a non-negative integer" };
|
|
221
|
-
expectedRevision = parsed;
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
if (argument === "--model") {
|
|
225
|
-
if (model !== void 0) return { error: "--model may only be provided once" };
|
|
226
|
-
const value = argv[index + 1];
|
|
227
|
-
if (!value || value.startsWith("-")) return { error: "--model requires provider/model" };
|
|
228
|
-
if (!isModelReference(value)) return { error: "--model must be provider/model" };
|
|
229
|
-
model = value;
|
|
230
|
-
index += 1;
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
if (argument.startsWith("--model=")) {
|
|
234
|
-
if (model !== void 0) return { error: "--model may only be provided once" };
|
|
235
|
-
const value = argument.slice(8);
|
|
236
|
-
if (!isModelReference(value)) return { error: "--model must be provider/model" };
|
|
237
|
-
model = value;
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
return { error: `Unknown model option: ${argument}` };
|
|
241
|
-
}
|
|
242
|
-
if (!json) return { error: "model commands require --json" };
|
|
243
|
-
if (operation === "status") {
|
|
244
|
-
if (model !== void 0) return { error: "status does not accept --model" };
|
|
245
|
-
if (expectedRevision !== void 0) return { error: "status does not accept --expected-revision" };
|
|
246
|
-
return {
|
|
247
|
-
json: true,
|
|
248
|
-
operation,
|
|
249
|
-
...requestId !== void 0 ? { requestId } : {},
|
|
250
|
-
...verbose ? { verbose: true } : {}
|
|
251
|
-
};
|
|
18
|
+
//#region src/core/application/deployment/manifest/deployment-manifest.ts
|
|
19
|
+
const DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_MS = 3e4;
|
|
20
|
+
const DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS = 1e4;
|
|
21
|
+
const DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES = 3;
|
|
22
|
+
const DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS = 4;
|
|
23
|
+
const DEFAULT_MANIFEST_BACKGROUND_SESSION_RETRY_BACKOFF_MS = 3e4;
|
|
24
|
+
const DEFAULT_MANIFEST_BACKGROUND_SESSION_LIFETIME_MS = 864e5;
|
|
25
|
+
const DEFAULT_MANIFEST_BACKGROUND_SESSION_STEP_TIMEOUT_MS = 3e5;
|
|
26
|
+
const DEFAULT_MANIFEST_CARD_STREAM_LEASE_MS = 51e4;
|
|
27
|
+
const DEFAULT_MANIFEST_CONVERSATION_PROGRESS_DISPLAY = "collapsed";
|
|
28
|
+
const MEMORY_SCOPES = [
|
|
29
|
+
"conversation",
|
|
30
|
+
"agent-private",
|
|
31
|
+
"project",
|
|
32
|
+
"shared-user-profile"
|
|
33
|
+
];
|
|
34
|
+
var InvalidRivusProjectSpace = class extends Error {
|
|
35
|
+
name = "InvalidRivusProjectSpace";
|
|
36
|
+
};
|
|
37
|
+
function validateRivusProjectSpaceDeployments(declarations) {
|
|
38
|
+
const projectSpaceIds = /* @__PURE__ */ new Set();
|
|
39
|
+
for (const declaration of declarations) {
|
|
40
|
+
if (projectSpaceIds.has(declaration.id)) throw new InvalidRivusProjectSpace(`duplicate project space: ${declaration.id}`);
|
|
41
|
+
projectSpaceIds.add(declaration.id);
|
|
42
|
+
validateRivusProjectSpaceDeployment(declaration);
|
|
252
43
|
}
|
|
253
|
-
|
|
254
|
-
if (expectedRevision === void 0) return { error: "--expected-revision is required for model changes" };
|
|
255
|
-
if (operation === "set" && model === void 0) return { error: "--model is required for model set" };
|
|
256
|
-
if (operation === "rollback" && model !== void 0) return { error: "rollback does not accept --model" };
|
|
257
|
-
return operation === "set" ? {
|
|
258
|
-
expectedRevision,
|
|
259
|
-
json: true,
|
|
260
|
-
model,
|
|
261
|
-
operation,
|
|
262
|
-
requestId,
|
|
263
|
-
...verbose ? { verbose: true } : {}
|
|
264
|
-
} : {
|
|
265
|
-
expectedRevision,
|
|
266
|
-
json: true,
|
|
267
|
-
operation,
|
|
268
|
-
requestId,
|
|
269
|
-
...verbose ? { verbose: true } : {}
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
function renderRivusModelCliHelp() {
|
|
273
|
-
return RIVUS_MODEL_CLI_USAGE;
|
|
44
|
+
return projectSpaceIds;
|
|
274
45
|
}
|
|
275
|
-
function
|
|
276
|
-
|
|
46
|
+
function validateRivusProjectSpaceDeployment(declaration) {
|
|
47
|
+
validateRelativeProjectPath(declaration.root, `project space ${declaration.id} root`);
|
|
48
|
+
validateRelativeProjectPath(declaration.workingDirectory, `project space ${declaration.id} working directory`);
|
|
49
|
+
const sources = /* @__PURE__ */ new Set();
|
|
50
|
+
for (const source of declaration.skills.sources) {
|
|
51
|
+
validateRelativeProjectPath(source, `project space ${declaration.id} Skill source`);
|
|
52
|
+
if (sources.has(source)) throw new InvalidRivusProjectSpace(`project space ${declaration.id} contains duplicate Skill source: ${source}`);
|
|
53
|
+
sources.add(source);
|
|
54
|
+
}
|
|
277
55
|
}
|
|
278
|
-
function
|
|
279
|
-
|
|
56
|
+
function validateRelativeProjectPath(value, owner) {
|
|
57
|
+
if (value.trim() === "" || isAbsoluteProjectPath(value) || value.includes("\0")) throw new InvalidRivusProjectSpace(`${owner} must be a non-empty relative path`);
|
|
280
58
|
}
|
|
281
|
-
function
|
|
282
|
-
|
|
283
|
-
const revision = Number(value);
|
|
284
|
-
return Number.isSafeInteger(revision) ? revision : void 0;
|
|
59
|
+
function isAbsoluteProjectPath(value) {
|
|
60
|
+
return value.startsWith("/") || value.startsWith("\\") || /^[a-z]:[\\/]/i.test(value);
|
|
285
61
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (!requestId) throw new Error("model request requires a request id");
|
|
315
|
-
if (operation === "set") {
|
|
316
|
-
const model = optionalString(value.model);
|
|
317
|
-
if (!model || !/^[^/\s]+\/[^/\s]+$/.test(model)) throw new Error("model request target is invalid");
|
|
318
|
-
return {
|
|
319
|
-
expectedRevision,
|
|
320
|
-
json: true,
|
|
321
|
-
model,
|
|
322
|
-
operation,
|
|
323
|
-
requestId,
|
|
324
|
-
...context ? { context } : {},
|
|
325
|
-
...verbose ? { verbose: true } : {}
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
return {
|
|
329
|
-
expectedRevision,
|
|
330
|
-
json: true,
|
|
331
|
-
operation,
|
|
332
|
-
requestId,
|
|
333
|
-
...context ? { context } : {},
|
|
334
|
-
...verbose ? { verbose: true } : {}
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
function toRivusModelManagementSubmission(request) {
|
|
338
|
-
const target = request.operation === "set" ? parseTarget(request.model) : void 0;
|
|
339
|
-
return {
|
|
340
|
-
expectedRevision: request.expectedRevision,
|
|
341
|
-
operation: request.operation,
|
|
342
|
-
requestId: request.requestId,
|
|
343
|
-
...target ? { target } : {},
|
|
344
|
-
...request.verbose ? { verbose: true } : {}
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
function projectRivusModelCliResponse(value, operation) {
|
|
348
|
-
if (!isRecord$2(value)) throw new Error("model handler must return a JSON object");
|
|
349
|
-
const response = { schemaVersion: 1 };
|
|
350
|
-
copyScalar(response, value, "requestId");
|
|
351
|
-
copyScalar(response, value, "revision");
|
|
352
|
-
copyScalar(response, value, "source");
|
|
353
|
-
copyScalar(response, value, "protocolVersion");
|
|
354
|
-
copyScalar(response, value, "runtimeVersion");
|
|
355
|
-
copyScalar(response, value, "operation");
|
|
356
|
-
copyScalar(response, value, "expectedRevision");
|
|
357
|
-
copyScalar(response, value, "phase");
|
|
358
|
-
copyScalar(response, value, "reason");
|
|
359
|
-
for (const key of [
|
|
360
|
-
"actual",
|
|
361
|
-
"current",
|
|
362
|
-
"persisted",
|
|
363
|
-
"previous",
|
|
364
|
-
"target"
|
|
365
|
-
]) {
|
|
366
|
-
const model = projectModelReference(value[key]);
|
|
367
|
-
if (model) response[key] = model;
|
|
368
|
-
}
|
|
369
|
-
const budget = projectBudget(value.budget);
|
|
370
|
-
if (budget) response.budget = budget;
|
|
371
|
-
const pending = projectPending(value.pending);
|
|
372
|
-
if (pending) response.pending = pending;
|
|
373
|
-
const request = projectReceipt(value.request);
|
|
374
|
-
if (request) response.request = request;
|
|
375
|
-
const error = projectError(value.error);
|
|
376
|
-
if (error) response.error = error;
|
|
377
|
-
const recoveryRequired = projectError(value.recoveryRequired);
|
|
378
|
-
if (recoveryRequired) response.recoveryRequired = recoveryRequired;
|
|
379
|
-
const notification = projectNotification(value.notification);
|
|
380
|
-
if (notification) response.notification = notification;
|
|
381
|
-
const status = value.status;
|
|
382
|
-
response.status = isPublicStatus(status) ? status : value.recoveryRequired !== void 0 ? "recovery-required" : operation === "status" && value.pending !== void 0 ? "pending" : operation === "status" ? "applied" : "failed";
|
|
383
|
-
return response;
|
|
384
|
-
}
|
|
385
|
-
function createRivusModelManagementFailure(code, message) {
|
|
386
|
-
return {
|
|
387
|
-
error: {
|
|
388
|
-
code,
|
|
389
|
-
message
|
|
390
|
-
},
|
|
391
|
-
schemaVersion: 1,
|
|
392
|
-
status: "failed"
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
function projectModelReference(value) {
|
|
396
|
-
if (!isRecord$2(value) || typeof value.provider !== "string" || typeof value.model !== "string") return void 0;
|
|
397
|
-
const result = {
|
|
398
|
-
model: value.model,
|
|
399
|
-
provider: value.provider
|
|
400
|
-
};
|
|
401
|
-
if (typeof value.label === "string") return {
|
|
402
|
-
...result,
|
|
403
|
-
label: value.label
|
|
404
|
-
};
|
|
405
|
-
if (typeof value.bindingRevision === "string") return {
|
|
406
|
-
...result,
|
|
407
|
-
bindingRevision: value.bindingRevision
|
|
408
|
-
};
|
|
409
|
-
return result;
|
|
410
|
-
}
|
|
411
|
-
function projectPending(value) {
|
|
412
|
-
if (!isRecord$2(value)) return void 0;
|
|
413
|
-
const result = {};
|
|
414
|
-
copyScalar(result, value, "phase");
|
|
415
|
-
copyScalar(result, value, "reason");
|
|
416
|
-
if (result.reason === void 0 && typeof result.phase === "string") result.reason = result.phase;
|
|
417
|
-
copyScalar(result, value, "requestId");
|
|
418
|
-
copyScalar(result, value, "updatedAt");
|
|
419
|
-
const request = projectReceipt(value.request);
|
|
420
|
-
if (request) result.request = request;
|
|
421
|
-
const target = projectModelReference(value.target);
|
|
422
|
-
if (target) result.target = target;
|
|
423
|
-
return Object.keys(result).length > 0 ? result : void 0;
|
|
424
|
-
}
|
|
425
|
-
function projectReceipt(value) {
|
|
426
|
-
if (!isRecord$2(value)) return void 0;
|
|
427
|
-
const result = {};
|
|
428
|
-
for (const key of [
|
|
429
|
-
"requestId",
|
|
430
|
-
"revision",
|
|
431
|
-
"status",
|
|
432
|
-
"phase",
|
|
433
|
-
"operation",
|
|
434
|
-
"expectedRevision",
|
|
435
|
-
"acceptedAt",
|
|
436
|
-
"updatedAt"
|
|
437
|
-
]) copyScalar(result, value, key);
|
|
438
|
-
const current = projectModelReference(value.current);
|
|
439
|
-
if (current) result.current = current;
|
|
440
|
-
const previous = projectModelReference(value.previous);
|
|
441
|
-
if (previous) result.previous = previous;
|
|
442
|
-
const target = projectModelReference(value.target);
|
|
443
|
-
if (target) result.target = target;
|
|
444
|
-
const budget = projectBudget(value.budget);
|
|
445
|
-
if (budget) result.budget = budget;
|
|
446
|
-
const error = projectError(value.error);
|
|
447
|
-
if (error) result.error = error;
|
|
448
|
-
const notification = projectNotification(value.notification);
|
|
449
|
-
if (notification) result.notification = notification;
|
|
450
|
-
return Object.keys(result).length > 0 ? result : void 0;
|
|
451
|
-
}
|
|
452
|
-
function projectBudget(value) {
|
|
453
|
-
if (!isRecord$2(value)) return void 0;
|
|
454
|
-
const result = {};
|
|
455
|
-
for (const key of [
|
|
456
|
-
"deadlineAt",
|
|
457
|
-
"maxOutputTokens",
|
|
458
|
-
"maxPaidRequests",
|
|
459
|
-
"outputTokens",
|
|
460
|
-
"paidRequests"
|
|
461
|
-
]) copyScalar(result, value, key);
|
|
462
|
-
return Object.keys(result).length > 0 ? result : void 0;
|
|
463
|
-
}
|
|
464
|
-
function projectError(value) {
|
|
465
|
-
if (!isRecord$2(value) || typeof value.code !== "string" || typeof value.message !== "string") return void 0;
|
|
466
|
-
return {
|
|
467
|
-
code: value.code,
|
|
468
|
-
message: value.message
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
function projectNotification(value) {
|
|
472
|
-
if (!isRecord$2(value)) return void 0;
|
|
473
|
-
const result = {};
|
|
474
|
-
copyScalar(result, value, "attempts");
|
|
475
|
-
copyScalar(result, value, "lastAttemptAt");
|
|
476
|
-
copyScalar(result, value, "status");
|
|
477
|
-
return Object.keys(result).length > 0 ? result : void 0;
|
|
478
|
-
}
|
|
479
|
-
function copyScalar(target, source, key) {
|
|
480
|
-
const value = source[key];
|
|
481
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") target[key] = value;
|
|
482
|
-
}
|
|
483
|
-
function parseTarget(value) {
|
|
484
|
-
if (!value) throw new Error("model request target is missing");
|
|
485
|
-
const separator = value.indexOf("/");
|
|
486
|
-
return {
|
|
487
|
-
model: value.slice(separator + 1),
|
|
488
|
-
provider: value.slice(0, separator)
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
function isPublicStatus(value) {
|
|
492
|
-
return value === "applied" || value === "failed" || value === "pending" || value === "recovery-required" || value === "restored";
|
|
493
|
-
}
|
|
494
|
-
function optionalString(value) {
|
|
495
|
-
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
496
|
-
}
|
|
497
|
-
function isRecord$2(value) {
|
|
498
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
499
|
-
}
|
|
500
|
-
//#endregion
|
|
501
|
-
//#region src/adapters/cli/model/rivus-model-management-socket-server.ts
|
|
502
|
-
const MAX_FRAME_BYTES = 64 * 1024;
|
|
503
|
-
function createRivusModelManagementSocketServer(options) {
|
|
504
|
-
assertAbsoluteSocketPath(options.socketPath);
|
|
505
|
-
let server;
|
|
506
|
-
let socketIdentity;
|
|
507
|
-
let listening = false;
|
|
508
|
-
let startPromise;
|
|
509
|
-
return {
|
|
510
|
-
close: async () => {
|
|
511
|
-
if (startPromise) await startPromise.catch(() => void 0);
|
|
512
|
-
const current = server;
|
|
513
|
-
const identity = socketIdentity;
|
|
514
|
-
server = void 0;
|
|
515
|
-
socketIdentity = void 0;
|
|
516
|
-
if (current && listening) await closeServer(current);
|
|
517
|
-
listening = false;
|
|
518
|
-
if (identity) await unlinkOwnedSocket(options.socketPath, identity);
|
|
519
|
-
},
|
|
520
|
-
listening: () => listening,
|
|
521
|
-
start: async () => {
|
|
522
|
-
if (listening) return;
|
|
523
|
-
if (startPromise) return startPromise;
|
|
524
|
-
startPromise = startServer(options, (next, identity) => {
|
|
525
|
-
server = next;
|
|
526
|
-
socketIdentity = identity;
|
|
527
|
-
listening = true;
|
|
528
|
-
});
|
|
529
|
-
try {
|
|
530
|
-
await startPromise;
|
|
531
|
-
} finally {
|
|
532
|
-
startPromise = void 0;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
async function startServer(options, onStarted) {
|
|
538
|
-
await prepareSocketParentDirectory(options.socketPath);
|
|
539
|
-
const lockPath = `${options.socketPath}.lock`;
|
|
540
|
-
const lock = await acquireStartLock(lockPath);
|
|
541
|
-
const next = createServer((socket) => handleConnection(socket, options));
|
|
542
|
-
let bound = false;
|
|
543
|
-
try {
|
|
544
|
-
await prepareSocketPath(options.socketPath);
|
|
545
|
-
await listen(next, options.socketPath);
|
|
546
|
-
bound = true;
|
|
547
|
-
await chmod(options.socketPath, 384);
|
|
548
|
-
onStarted(next, await readSocketIdentity(options.socketPath));
|
|
549
|
-
} catch (error) {
|
|
550
|
-
await closeServer(next).catch(() => void 0);
|
|
551
|
-
if (bound) await unlinkSocketIfPresent(options.socketPath);
|
|
552
|
-
throw error;
|
|
553
|
-
} finally {
|
|
554
|
-
await releaseStartLock(lock, lockPath);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
async function handleConnection(socket, options) {
|
|
558
|
-
let buffer = "";
|
|
559
|
-
let handled = false;
|
|
560
|
-
socket.setEncoding("utf8");
|
|
561
|
-
socket.on("data", (chunk) => {
|
|
562
|
-
if (handled) return;
|
|
563
|
-
buffer += chunk;
|
|
564
|
-
if (Buffer.byteLength(buffer, "utf8") > MAX_FRAME_BYTES) {
|
|
565
|
-
handled = true;
|
|
566
|
-
writeResponse(socket, createRivusModelManagementFailure("request_too_large", "model management request is too large"));
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
const newline = buffer.indexOf("\n");
|
|
570
|
-
if (newline < 0) return;
|
|
571
|
-
handled = true;
|
|
572
|
-
handleFrame(buffer.slice(0, newline).trim(), socket, options);
|
|
62
|
+
function parseRivusDeploymentManifest(value) {
|
|
63
|
+
const root = record(value, "manifest");
|
|
64
|
+
exactKeys(root, [
|
|
65
|
+
"agents",
|
|
66
|
+
"automations",
|
|
67
|
+
"backgroundSessions",
|
|
68
|
+
"defaultAgentId",
|
|
69
|
+
"defaultEndpointId",
|
|
70
|
+
"endpoints",
|
|
71
|
+
"plugins",
|
|
72
|
+
"projectSpaces"
|
|
73
|
+
], "manifest", [
|
|
74
|
+
"automations",
|
|
75
|
+
"backgroundSessions",
|
|
76
|
+
"projectSpaces"
|
|
77
|
+
]);
|
|
78
|
+
const plugins = array(root.plugins, "manifest.plugins").map((entry, index) => {
|
|
79
|
+
const plugin = record(entry, `manifest.plugins[${index}]`);
|
|
80
|
+
exactKeys(plugin, [
|
|
81
|
+
"id",
|
|
82
|
+
"module",
|
|
83
|
+
"required"
|
|
84
|
+
], `manifest.plugins[${index}]`);
|
|
85
|
+
return Object.freeze({
|
|
86
|
+
id: string(plugin.id, `manifest.plugins[${index}].id`),
|
|
87
|
+
module: string(plugin.module, `manifest.plugins[${index}].module`),
|
|
88
|
+
required: boolean(plugin.required, `manifest.plugins[${index}].required`)
|
|
89
|
+
});
|
|
573
90
|
});
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
91
|
+
const agents = array(root.agents, "manifest.agents").map((entry, index) => {
|
|
92
|
+
const agent = record(entry, `manifest.agents[${index}]`);
|
|
93
|
+
exactKeys(agent, [
|
|
94
|
+
"agentId",
|
|
95
|
+
"endpointIds",
|
|
96
|
+
"memory",
|
|
97
|
+
"pluginId",
|
|
98
|
+
"profileId",
|
|
99
|
+
"projectSpaceId",
|
|
100
|
+
"runtimeTools",
|
|
101
|
+
"skills",
|
|
102
|
+
"tools"
|
|
103
|
+
], `manifest.agents[${index}]`, [
|
|
104
|
+
"memory",
|
|
105
|
+
"projectSpaceId",
|
|
106
|
+
"runtimeTools"
|
|
107
|
+
]);
|
|
108
|
+
const memory = agent.memory === void 0 ? void 0 : record(agent.memory, `manifest.agents[${index}].memory`);
|
|
109
|
+
if (memory) exactKeys(memory, ["scopes", "tool"], `manifest.agents[${index}].memory`);
|
|
110
|
+
const skills = record(agent.skills, `manifest.agents[${index}].skills`);
|
|
111
|
+
exactKeys(skills, ["allow"], `manifest.agents[${index}].skills`);
|
|
112
|
+
const tools = record(agent.tools, `manifest.agents[${index}].tools`);
|
|
113
|
+
exactKeys(tools, ["allow"], `manifest.agents[${index}].tools`);
|
|
114
|
+
const runtimeTools = agent.runtimeTools === void 0 ? void 0 : record(agent.runtimeTools, `manifest.agents[${index}].runtimeTools`);
|
|
115
|
+
if (runtimeTools) exactKeys(runtimeTools, ["allow"], `manifest.agents[${index}].runtimeTools`);
|
|
116
|
+
return Object.freeze({
|
|
117
|
+
agentId: string(agent.agentId, `manifest.agents[${index}].agentId`),
|
|
118
|
+
endpointIds: Object.freeze(array(agent.endpointIds, `manifest.agents[${index}].endpointIds`).map((item, itemIndex) => string(item, `manifest.agents[${index}].endpointIds[${itemIndex}]`))),
|
|
119
|
+
...memory ? { memory: Object.freeze({
|
|
120
|
+
scopes: Object.freeze(array(memory.scopes, `manifest.agents[${index}].memory.scopes`).map((item, itemIndex) => memoryScope(item, `manifest.agents[${index}].memory.scopes[${itemIndex}]`))),
|
|
121
|
+
tool: boolean(memory.tool, `manifest.agents[${index}].memory.tool`)
|
|
122
|
+
}) } : {},
|
|
123
|
+
pluginId: string(agent.pluginId, `manifest.agents[${index}].pluginId`),
|
|
124
|
+
profileId: string(agent.profileId, `manifest.agents[${index}].profileId`),
|
|
125
|
+
...agent.projectSpaceId === void 0 ? {} : { projectSpaceId: string(agent.projectSpaceId, `manifest.agents[${index}].projectSpaceId`) },
|
|
126
|
+
...runtimeTools ? { runtimeTools: Object.freeze({ allow: Object.freeze(uniqueRuntimeTools(array(runtimeTools.allow, `manifest.agents[${index}].runtimeTools.allow`).map((item, itemIndex) => runtimeToolId(item, `manifest.agents[${index}].runtimeTools.allow[${itemIndex}]`)), `manifest.agents[${index}].runtimeTools.allow`)) }) } : {},
|
|
127
|
+
skills: Object.freeze({ allow: Object.freeze(array(skills.allow, `manifest.agents[${index}].skills.allow`).map((item, itemIndex) => string(item, `manifest.agents[${index}].skills.allow[${itemIndex}]`))) }),
|
|
128
|
+
tools: Object.freeze({ allow: Object.freeze(array(tools.allow, `manifest.agents[${index}].tools.allow`).map((item, itemIndex) => string(item, `manifest.agents[${index}].tools.allow[${itemIndex}]`))) })
|
|
129
|
+
});
|
|
578
130
|
});
|
|
579
|
-
|
|
580
|
-
|
|
131
|
+
const endpoints = array(root.endpoints, "manifest.endpoints").map((entry, index) => {
|
|
132
|
+
const endpoint = record(entry, `manifest.endpoints[${index}]`);
|
|
133
|
+
exactKeys(endpoint, [
|
|
134
|
+
"agentId",
|
|
135
|
+
"baseUrl",
|
|
136
|
+
"cardStreamLeaseMs",
|
|
137
|
+
"credentialRef",
|
|
138
|
+
"enabled",
|
|
139
|
+
"experimental",
|
|
140
|
+
"groupPolicy",
|
|
141
|
+
"id",
|
|
142
|
+
"progressDisplay",
|
|
143
|
+
"required",
|
|
144
|
+
"sessionNamespace",
|
|
145
|
+
"streamMinIntervalMs"
|
|
146
|
+
], `manifest.endpoints[${index}]`, [
|
|
147
|
+
"cardStreamLeaseMs",
|
|
148
|
+
"experimental",
|
|
149
|
+
"progressDisplay"
|
|
150
|
+
]);
|
|
151
|
+
const experimental = endpoint.experimental === void 0 ? void 0 : record(endpoint.experimental, `manifest.endpoints[${index}].experimental`);
|
|
152
|
+
if (experimental) exactKeys(experimental, ["cotMessages"], `manifest.endpoints[${index}].experimental`);
|
|
153
|
+
return Object.freeze({
|
|
154
|
+
agentId: string(endpoint.agentId, `manifest.endpoints[${index}].agentId`),
|
|
155
|
+
baseUrl: string(endpoint.baseUrl, `manifest.endpoints[${index}].baseUrl`),
|
|
156
|
+
cardStreamLeaseMs: endpoint.cardStreamLeaseMs === void 0 ? DEFAULT_MANIFEST_CARD_STREAM_LEASE_MS : positiveInteger(endpoint.cardStreamLeaseMs, `manifest.endpoints[${index}].cardStreamLeaseMs`),
|
|
157
|
+
credentialRef: string(endpoint.credentialRef, `manifest.endpoints[${index}].credentialRef`),
|
|
158
|
+
enabled: boolean(endpoint.enabled, `manifest.endpoints[${index}].enabled`),
|
|
159
|
+
...experimental ? { experimental: Object.freeze({ cotMessages: boolean(experimental.cotMessages, `manifest.endpoints[${index}].experimental.cotMessages`) }) } : {},
|
|
160
|
+
groupPolicy: groupPolicy(endpoint.groupPolicy, `manifest.endpoints[${index}].groupPolicy`),
|
|
161
|
+
id: string(endpoint.id, `manifest.endpoints[${index}].id`),
|
|
162
|
+
progressDisplay: endpoint.progressDisplay === void 0 ? DEFAULT_MANIFEST_CONVERSATION_PROGRESS_DISPLAY : progressDisplay(endpoint.progressDisplay, `manifest.endpoints[${index}].progressDisplay`),
|
|
163
|
+
required: boolean(endpoint.required, `manifest.endpoints[${index}].required`),
|
|
164
|
+
sessionNamespace: string(endpoint.sessionNamespace, `manifest.endpoints[${index}].sessionNamespace`),
|
|
165
|
+
streamMinIntervalMs: positiveInteger(endpoint.streamMinIntervalMs, `manifest.endpoints[${index}].streamMinIntervalMs`)
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
const automations = array(root.automations ?? [], "manifest.automations").map((entry, index) => {
|
|
169
|
+
const automation = record(entry, `manifest.automations[${index}]`);
|
|
170
|
+
exactKeys(automation, [
|
|
171
|
+
"agentId",
|
|
172
|
+
"delivery",
|
|
173
|
+
"enabled",
|
|
174
|
+
"id",
|
|
175
|
+
"required",
|
|
176
|
+
"schedule",
|
|
177
|
+
"templateId",
|
|
178
|
+
"timeZone"
|
|
179
|
+
], `manifest.automations[${index}]`);
|
|
180
|
+
const delivery = record(automation.delivery, `manifest.automations[${index}].delivery`);
|
|
181
|
+
exactKeys(delivery, [
|
|
182
|
+
"endpointId",
|
|
183
|
+
"targetRef",
|
|
184
|
+
"targetType"
|
|
185
|
+
], `manifest.automations[${index}].delivery`);
|
|
186
|
+
return Object.freeze({
|
|
187
|
+
agentId: string(automation.agentId, `manifest.automations[${index}].agentId`),
|
|
188
|
+
delivery: Object.freeze({
|
|
189
|
+
endpointId: string(delivery.endpointId, `manifest.automations[${index}].delivery.endpointId`),
|
|
190
|
+
targetRef: string(delivery.targetRef, `manifest.automations[${index}].delivery.targetRef`),
|
|
191
|
+
targetType: automationTargetType(delivery.targetType, `manifest.automations[${index}].delivery.targetType`)
|
|
192
|
+
}),
|
|
193
|
+
enabled: boolean(automation.enabled, `manifest.automations[${index}].enabled`),
|
|
194
|
+
id: string(automation.id, `manifest.automations[${index}].id`),
|
|
195
|
+
required: boolean(automation.required, `manifest.automations[${index}].required`),
|
|
196
|
+
schedule: string(automation.schedule, `manifest.automations[${index}].schedule`),
|
|
197
|
+
templateId: string(automation.templateId, `manifest.automations[${index}].templateId`),
|
|
198
|
+
timeZone: string(automation.timeZone, `manifest.automations[${index}].timeZone`)
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
const projectSpaces = array(root.projectSpaces ?? [], "manifest.projectSpaces").map((entry, index) => {
|
|
202
|
+
const projectSpace = record(entry, `manifest.projectSpaces[${index}]`);
|
|
203
|
+
exactKeys(projectSpace, [
|
|
204
|
+
"id",
|
|
205
|
+
"root",
|
|
206
|
+
"skills",
|
|
207
|
+
"workingDirectory"
|
|
208
|
+
], `manifest.projectSpaces[${index}]`);
|
|
209
|
+
const skills = record(projectSpace.skills, `manifest.projectSpaces[${index}].skills`);
|
|
210
|
+
exactKeys(skills, ["sources"], `manifest.projectSpaces[${index}].skills`);
|
|
211
|
+
return Object.freeze({
|
|
212
|
+
id: string(projectSpace.id, `manifest.projectSpaces[${index}].id`),
|
|
213
|
+
root: string(projectSpace.root, `manifest.projectSpaces[${index}].root`),
|
|
214
|
+
skills: Object.freeze({ sources: Object.freeze(array(skills.sources, `manifest.projectSpaces[${index}].skills.sources`).map((item, itemIndex) => string(item, `manifest.projectSpaces[${index}].skills.sources[${itemIndex}]`))) }),
|
|
215
|
+
workingDirectory: string(projectSpace.workingDirectory, `manifest.projectSpaces[${index}].workingDirectory`)
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
const backgroundSessions = root.backgroundSessions === void 0 ? void 0 : parseBackgroundSessions(record(root.backgroundSessions, "manifest.backgroundSessions"));
|
|
219
|
+
return Object.freeze({
|
|
220
|
+
agents: Object.freeze(agents),
|
|
221
|
+
automations: Object.freeze(automations),
|
|
222
|
+
...backgroundSessions ? { backgroundSessions } : {},
|
|
223
|
+
defaultAgentId: string(root.defaultAgentId, "manifest.defaultAgentId"),
|
|
224
|
+
defaultEndpointId: string(root.defaultEndpointId, "manifest.defaultEndpointId"),
|
|
225
|
+
endpoints: Object.freeze(endpoints),
|
|
226
|
+
plugins: Object.freeze(plugins),
|
|
227
|
+
projectSpaces: Object.freeze(projectSpaces)
|
|
581
228
|
});
|
|
582
229
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
writeResponse(socket, response);
|
|
591
|
-
return;
|
|
230
|
+
function validateRivusDeploymentManifest(manifest) {
|
|
231
|
+
const projectSpaceIds = validateRivusProjectSpaceDeployments(manifest.projectSpaces ?? []);
|
|
232
|
+
const pluginIds = /* @__PURE__ */ new Set();
|
|
233
|
+
for (const plugin of manifest.plugins) {
|
|
234
|
+
if (pluginIds.has(plugin.id)) throw new Error(`duplicate plugin declaration: ${plugin.id}`);
|
|
235
|
+
validateModuleSpecifier(plugin.module);
|
|
236
|
+
pluginIds.add(plugin.id);
|
|
592
237
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
238
|
+
const agentIds = /* @__PURE__ */ new Set();
|
|
239
|
+
const agentById = /* @__PURE__ */ new Map();
|
|
240
|
+
for (const agent of manifest.agents) {
|
|
241
|
+
if (agentIds.has(agent.agentId)) throw new Error(`duplicate agent deployment: ${agent.agentId}`);
|
|
242
|
+
agentIds.add(agent.agentId);
|
|
243
|
+
if (!pluginIds.has(agent.pluginId)) throw new Error(`agent ${agent.agentId} references undeclared plugin: ${agent.pluginId}`);
|
|
244
|
+
if (agent.projectSpaceId && !projectSpaceIds.has(agent.projectSpaceId)) throw new Error(`agent ${agent.agentId} references unknown project space: ${agent.projectSpaceId}`);
|
|
245
|
+
agentById.set(agent.agentId, agent);
|
|
601
246
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
247
|
+
const endpointIds = /* @__PURE__ */ new Set();
|
|
248
|
+
const sessionNamespaces = /* @__PURE__ */ new Set();
|
|
249
|
+
for (const endpoint of manifest.endpoints) {
|
|
250
|
+
if (endpointIds.has(endpoint.id)) throw new Error(`duplicate endpoint binding: ${endpoint.id}`);
|
|
251
|
+
endpointIds.add(endpoint.id);
|
|
252
|
+
if (sessionNamespaces.has(endpoint.sessionNamespace)) throw new Error(`duplicate endpoint session namespace: ${endpoint.sessionNamespace}`);
|
|
253
|
+
sessionNamespaces.add(endpoint.sessionNamespace);
|
|
254
|
+
const agent = agentById.get(endpoint.agentId);
|
|
255
|
+
if (!agent) throw new Error(`endpoint ${endpoint.id} references unknown agent: ${endpoint.agentId}`);
|
|
256
|
+
if (!agent.endpointIds.includes(endpoint.id)) throw new Error(`endpoint ${endpoint.id} is not declared by agent ${endpoint.agentId}`);
|
|
611
257
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
return createRivusModelManagementFailure("handler_failed", "the model management request could not be completed");
|
|
258
|
+
for (const agent of manifest.agents) for (const endpointId of agent.endpointIds) {
|
|
259
|
+
const endpoint = manifest.endpoints.find(({ id }) => id === endpointId);
|
|
260
|
+
if (!endpoint) throw new Error(`agent ${agent.agentId} references unknown endpoint: ${endpointId}`);
|
|
261
|
+
if (endpoint.agentId !== agent.agentId) throw new Error(`endpoint ${endpointId} is bound to a different agent`);
|
|
617
262
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
263
|
+
const automationIds = /* @__PURE__ */ new Set();
|
|
264
|
+
for (const automation of manifest.automations ?? []) {
|
|
265
|
+
if (automationIds.has(automation.id)) throw new Error(`duplicate automation binding: ${automation.id}`);
|
|
266
|
+
automationIds.add(automation.id);
|
|
267
|
+
if (!agentById.has(automation.agentId)) throw new Error(`automation ${automation.id} references unknown agent: ${automation.agentId}`);
|
|
268
|
+
const endpoint = manifest.endpoints.find(({ id }) => id === automation.delivery.endpointId);
|
|
269
|
+
if (!endpoint) throw new Error(`automation ${automation.id} references unknown delivery endpoint: ${automation.delivery.endpointId}`);
|
|
270
|
+
if (automation.enabled && !endpoint.enabled) throw new Error(`automation ${automation.id} delivery endpoint must be enabled`);
|
|
624
271
|
}
|
|
272
|
+
const defaultAgent = agentById.get(manifest.defaultAgentId);
|
|
273
|
+
if (!defaultAgent) throw new Error(`default agent does not exist: ${manifest.defaultAgentId}`);
|
|
274
|
+
const defaultEndpoint = manifest.endpoints.find(({ id }) => id === manifest.defaultEndpointId);
|
|
275
|
+
if (!defaultEndpoint) throw new Error(`default endpoint does not exist: ${manifest.defaultEndpointId}`);
|
|
276
|
+
if (defaultEndpoint.agentId !== defaultAgent.agentId) throw new Error("default endpoint is not bound to the default agent");
|
|
277
|
+
if (!defaultEndpoint.enabled) throw new Error("default endpoint must be enabled");
|
|
278
|
+
if (manifest.backgroundSessions) validateBackgroundSessions(manifest.backgroundSessions);
|
|
625
279
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
const current = await stat(socketPath);
|
|
632
|
-
if (current.dev !== metadata.dev || current.ino !== metadata.ino) throw new Error(`model management socket changed while starting: ${socketPath}`);
|
|
633
|
-
await unlink(socketPath);
|
|
634
|
-
} catch (error) {
|
|
635
|
-
if (!isMissing$2(error)) throw error;
|
|
636
|
-
}
|
|
280
|
+
function validateBackgroundSessions(config) {
|
|
281
|
+
if (config.required && !config.enabled) throw new Error("backgroundSessions.required requires backgroundSessions.enabled");
|
|
282
|
+
if (config.leaseRenewalIntervalMs >= config.leaseMs) throw new Error("backgroundSessions.leaseRenewalIntervalMs must be shorter than leaseMs");
|
|
283
|
+
if (config.stepTimeoutMs <= 0 || config.maxConcurrentSessions <= 0 || config.leaseMs <= 0) throw new Error("backgroundSessions durations and concurrency must be positive");
|
|
284
|
+
if (config.retryBackoffMs <= 0 || config.sessionLifetimeMs <= 0 || config.maxConsecutiveFailures <= 0) throw new Error("backgroundSessions retry and lifetime limits must be positive");
|
|
637
285
|
}
|
|
638
|
-
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
recursive: true
|
|
643
|
-
});
|
|
644
|
-
await chmod(parent, 448);
|
|
286
|
+
function validateModuleSpecifier(moduleSpecifier) {
|
|
287
|
+
const absolutePath = moduleSpecifier.startsWith("/") || moduleSpecifier.startsWith("\\\\") || /^[a-zA-Z]:[\\/]/.test(moduleSpecifier);
|
|
288
|
+
if (moduleSpecifier.trim() === "" || absolutePath || /^[a-z][a-z+.-]*:/i.test(moduleSpecifier) || moduleSpecifier.includes("\0")) throw new Error(`invalid plugin module specifier: ${moduleSpecifier}`);
|
|
289
|
+
if ((moduleSpecifier.startsWith("./") || moduleSpecifier.startsWith("../")) && moduleSpecifier.split(/[\\/]/).includes("..")) throw new Error(`plugin module escapes deployment root: ${moduleSpecifier}`);
|
|
645
290
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
291
|
+
function parseBackgroundSessions(value) {
|
|
292
|
+
exactKeys(value, [
|
|
293
|
+
"enabled",
|
|
294
|
+
"leaseMs",
|
|
295
|
+
"leaseRenewalIntervalMs",
|
|
296
|
+
"maxConsecutiveFailures",
|
|
297
|
+
"maxConcurrentSessions",
|
|
298
|
+
"required",
|
|
299
|
+
"retryBackoffMs",
|
|
300
|
+
"sessionLifetimeMs",
|
|
301
|
+
"stepTimeoutMs"
|
|
302
|
+
], "manifest.backgroundSessions", [
|
|
303
|
+
"leaseMs",
|
|
304
|
+
"leaseRenewalIntervalMs",
|
|
305
|
+
"maxConsecutiveFailures",
|
|
306
|
+
"maxConcurrentSessions",
|
|
307
|
+
"retryBackoffMs",
|
|
308
|
+
"sessionLifetimeMs",
|
|
309
|
+
"stepTimeoutMs"
|
|
310
|
+
]);
|
|
311
|
+
return Object.freeze({
|
|
312
|
+
enabled: boolean(value.enabled, "manifest.backgroundSessions.enabled"),
|
|
313
|
+
leaseMs: positiveInteger(value.leaseMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_MS, "manifest.backgroundSessions.leaseMs"),
|
|
314
|
+
leaseRenewalIntervalMs: positiveInteger(value.leaseRenewalIntervalMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS, "manifest.backgroundSessions.leaseRenewalIntervalMs"),
|
|
315
|
+
maxConsecutiveFailures: positiveInteger(value.maxConsecutiveFailures ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES, "manifest.backgroundSessions.maxConsecutiveFailures"),
|
|
316
|
+
maxConcurrentSessions: positiveInteger(value.maxConcurrentSessions ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS, "manifest.backgroundSessions.maxConcurrentSessions"),
|
|
317
|
+
required: boolean(value.required, "manifest.backgroundSessions.required"),
|
|
318
|
+
retryBackoffMs: positiveInteger(value.retryBackoffMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_RETRY_BACKOFF_MS, "manifest.backgroundSessions.retryBackoffMs"),
|
|
319
|
+
sessionLifetimeMs: positiveInteger(value.sessionLifetimeMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_LIFETIME_MS, "manifest.backgroundSessions.sessionLifetimeMs"),
|
|
320
|
+
stepTimeoutMs: positiveInteger(value.stepTimeoutMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_STEP_TIMEOUT_MS, "manifest.backgroundSessions.stepTimeoutMs")
|
|
663
321
|
});
|
|
664
322
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
try {
|
|
669
|
-
handle = await open(path, "wx", 384);
|
|
670
|
-
await handle.writeFile(`${process.pid}\n`, "utf8");
|
|
671
|
-
const metadata = await handle.stat();
|
|
672
|
-
return {
|
|
673
|
-
handle,
|
|
674
|
-
identity: {
|
|
675
|
-
dev: metadata.dev,
|
|
676
|
-
ino: metadata.ino
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
} catch (error) {
|
|
680
|
-
await handle?.close().catch(() => void 0);
|
|
681
|
-
if (!isAlreadyExists(error)) throw new Error("model management socket startup is already in progress", { cause: error });
|
|
682
|
-
const pid = parseLockOwner(await readPersistenceFile(path));
|
|
683
|
-
if (pid === void 0) throw new Error("model management socket startup lock owner could not be verified; manual recovery is required");
|
|
684
|
-
if (processIsAlive(pid)) throw new Error("model management socket startup is already in progress");
|
|
685
|
-
const reclaimed = `${path}.reclaim-${process.pid}-${randomUUID()}`;
|
|
686
|
-
try {
|
|
687
|
-
await rename(path, reclaimed);
|
|
688
|
-
} catch (reclaimError) {
|
|
689
|
-
if (isMissing$2(reclaimError)) continue;
|
|
690
|
-
throw new Error("model management socket startup lock could not be reclaimed safely", { cause: reclaimError });
|
|
691
|
-
}
|
|
692
|
-
await unlink(reclaimed).catch((reclaimError) => {
|
|
693
|
-
if (!isMissing$2(reclaimError)) throw reclaimError;
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
throw new Error("model management socket startup lock changed during stale-owner recovery; manual recovery is required");
|
|
323
|
+
function record(value, path) {
|
|
324
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must be an object`);
|
|
325
|
+
return value;
|
|
698
326
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
const current = await stat(path);
|
|
703
|
-
if (current.dev !== lock.identity.dev || current.ino !== lock.identity.ino) return;
|
|
704
|
-
await unlink(path);
|
|
705
|
-
} catch (error) {
|
|
706
|
-
if (!isMissing$2(error)) throw error;
|
|
707
|
-
}
|
|
327
|
+
function array(value, path) {
|
|
328
|
+
if (!Array.isArray(value)) throw new Error(`${path} must be an array`);
|
|
329
|
+
return value;
|
|
708
330
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
} catch (error) {
|
|
713
|
-
if (!isMissing$2(error)) throw error;
|
|
714
|
-
}
|
|
331
|
+
function string(value, path) {
|
|
332
|
+
if (typeof value !== "string" || value.trim() === "") throw new Error(`${path} must be a non-empty string`);
|
|
333
|
+
return value;
|
|
715
334
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
if (current.dev !== identity.dev || current.ino !== identity.ino) return;
|
|
720
|
-
await unlink(socketPath);
|
|
721
|
-
} catch (error) {
|
|
722
|
-
if (!isMissing$2(error)) throw error;
|
|
723
|
-
}
|
|
335
|
+
function boolean(value, path) {
|
|
336
|
+
if (typeof value !== "boolean") throw new Error(`${path} must be a boolean`);
|
|
337
|
+
return value;
|
|
724
338
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
return
|
|
728
|
-
dev: metadata.dev,
|
|
729
|
-
ino: metadata.ino
|
|
730
|
-
};
|
|
339
|
+
function positiveInteger(value, path) {
|
|
340
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`${path} must be a positive integer`);
|
|
341
|
+
return value;
|
|
731
342
|
}
|
|
732
|
-
function
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
server.off("listening", onListening);
|
|
736
|
-
reject(error);
|
|
737
|
-
};
|
|
738
|
-
const onListening = () => {
|
|
739
|
-
server.off("error", onError);
|
|
740
|
-
resolve();
|
|
741
|
-
};
|
|
742
|
-
server.once("error", onError);
|
|
743
|
-
server.once("listening", onListening);
|
|
744
|
-
server.listen(socketPath);
|
|
745
|
-
});
|
|
343
|
+
function memoryScope(value, path) {
|
|
344
|
+
if (typeof value !== "string" || !MEMORY_SCOPES.includes(value)) throw new Error(`${path} must be conversation, agent-private, project, or shared-user-profile`);
|
|
345
|
+
return value;
|
|
746
346
|
}
|
|
747
|
-
function
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
});
|
|
347
|
+
function groupPolicy(value, path) {
|
|
348
|
+
if (value !== "mention-only" && value !== "ignore-unmentioned" && value !== "default-responder") throw new Error(`${path} must be mention-only, ignore-unmentioned, or default-responder`);
|
|
349
|
+
return value;
|
|
751
350
|
}
|
|
752
|
-
function
|
|
753
|
-
if (
|
|
351
|
+
function progressDisplay(value, path) {
|
|
352
|
+
if (value !== "hidden" && value !== "collapsed" && value !== "expanded") throw new Error(`${path} must be hidden, collapsed, or expanded`);
|
|
353
|
+
return value;
|
|
754
354
|
}
|
|
755
|
-
function
|
|
756
|
-
|
|
355
|
+
function runtimeToolId(value, path) {
|
|
356
|
+
const id = string(value, path);
|
|
357
|
+
if (!isRivusRuntimeToolId(id)) throw new Error(`${path} must be read, bash, edit, write, grep, find, or ls`);
|
|
358
|
+
return id;
|
|
359
|
+
}
|
|
360
|
+
function uniqueRuntimeTools(ids, path) {
|
|
361
|
+
const result = /* @__PURE__ */ new Set();
|
|
362
|
+
for (const id of ids) {
|
|
363
|
+
if (result.has(id)) throw new Error(`${path} contains duplicate Runtime Tool: ${id}`);
|
|
364
|
+
result.add(id);
|
|
365
|
+
}
|
|
366
|
+
return [...result];
|
|
367
|
+
}
|
|
368
|
+
function automationTargetType(value, path) {
|
|
369
|
+
if (value !== "chat_id" && value !== "open_id" && value !== "user_id" && value !== "union_id" && value !== "email") throw new Error(`${path} must be chat_id, open_id, user_id, union_id, or email`);
|
|
370
|
+
return value;
|
|
757
371
|
}
|
|
758
|
-
function
|
|
759
|
-
|
|
372
|
+
function exactKeys(value, allowed, path, optional = []) {
|
|
373
|
+
const unexpected = Object.keys(value).find((key) => !allowed.includes(key));
|
|
374
|
+
if (unexpected) throw new Error(`${path} contains unsupported field: ${unexpected}`);
|
|
375
|
+
const missing = allowed.find((key) => !optional.includes(key) && !Object.hasOwn(value, key));
|
|
376
|
+
if (missing) throw new Error(`${path} is missing required field: ${missing}`);
|
|
760
377
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/adapters/feishu/config/feishu-endpoint-credentials.ts
|
|
380
|
+
var FeishuEndpointCredentialError = class extends Error {
|
|
381
|
+
name = "FeishuEndpointCredentialError";
|
|
382
|
+
};
|
|
383
|
+
function resolveFeishuEndpointCredentials(credentialRef, env) {
|
|
384
|
+
if (!credentialRef.startsWith("env:")) throw new FeishuEndpointCredentialError("Feishu endpoint credentialRef must use env:<PREFIX>");
|
|
385
|
+
const prefix = credentialRef.slice(4);
|
|
386
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(prefix)) throw new FeishuEndpointCredentialError(`Invalid environment prefix in credentialRef: ${credentialRef}`);
|
|
387
|
+
return Object.freeze({
|
|
388
|
+
appId: required$1(env, `${prefix}_APP_ID`),
|
|
389
|
+
appSecret: required$1(env, `${prefix}_APP_SECRET`)
|
|
390
|
+
});
|
|
768
391
|
}
|
|
769
|
-
function
|
|
770
|
-
const
|
|
771
|
-
if (!
|
|
772
|
-
|
|
773
|
-
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
392
|
+
function required$1(env, variable) {
|
|
393
|
+
const value = env[variable]?.trim();
|
|
394
|
+
if (!value) throw new FeishuEndpointCredentialError(`${variable} is required`);
|
|
395
|
+
return value;
|
|
774
396
|
}
|
|
775
397
|
//#endregion
|
|
776
398
|
//#region src/platform/home/config/runtime/local-env-file.ts
|
|
@@ -829,822 +451,842 @@ function unescapeDoubleQuotedValue(value) {
|
|
|
829
451
|
});
|
|
830
452
|
}
|
|
831
453
|
//#endregion
|
|
832
|
-
//#region src/platform/home/config/
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
constructor(code, message) {
|
|
841
|
-
super(message);
|
|
842
|
-
this.code = code;
|
|
454
|
+
//#region src/platform/home/config/runtime/rivus-daemon-config.ts
|
|
455
|
+
var RivusDaemonConfigError = class {
|
|
456
|
+
variable;
|
|
457
|
+
message;
|
|
458
|
+
_tag = "RivusDaemonConfigError";
|
|
459
|
+
constructor(variable, message) {
|
|
460
|
+
this.variable = variable;
|
|
461
|
+
this.message = message;
|
|
843
462
|
}
|
|
844
463
|
};
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
464
|
+
const DEFAULT_AGENT_ID = "main";
|
|
465
|
+
const DEFAULT_CARD_STREAM_LEASE_MS = 51e4;
|
|
466
|
+
const DEFAULT_FEISHU_BASE_URL = "https://open.feishu.cn";
|
|
467
|
+
const DEFAULT_STREAM_MIN_INTERVAL_MS = 200;
|
|
468
|
+
const THINKING_LEVELS = /* @__PURE__ */ new Set([
|
|
469
|
+
"off",
|
|
470
|
+
"minimal",
|
|
471
|
+
"low",
|
|
472
|
+
"medium",
|
|
473
|
+
"high",
|
|
474
|
+
"xhigh"
|
|
475
|
+
]);
|
|
476
|
+
function loadRivusDaemonConfig(env, options = {}) {
|
|
477
|
+
return Effect.gen(function* () {
|
|
478
|
+
const appId = yield* required(env, "FEISHU_APP_ID");
|
|
479
|
+
const appSecret = yield* required(env, "FEISHU_APP_SECRET");
|
|
480
|
+
const streamMinIntervalMs = yield* optionalPositiveInteger(env.FEISHU_STREAM_MIN_INTERVAL_MS, "FEISHU_STREAM_MIN_INTERVAL_MS", DEFAULT_STREAM_MIN_INTERVAL_MS);
|
|
481
|
+
const cardStreamLeaseMs = yield* optionalPositiveInteger(env.FEISHU_CARD_STREAM_LEASE_MS, "FEISHU_CARD_STREAM_LEASE_MS", DEFAULT_CARD_STREAM_LEASE_MS);
|
|
482
|
+
const thinkingLevel = yield* optionalThinkingLevel(env.PI_THINKING_LEVEL);
|
|
483
|
+
const apiKey = yield* optionalPiApiKey(env, options.readTextFile ?? readUtf8File);
|
|
484
|
+
const baseUrl = optional(env.PI_BASE_URL);
|
|
485
|
+
const model = optional(env.PI_MODEL);
|
|
486
|
+
return {
|
|
487
|
+
agentId: optional(env.RIVUS_AGENT_ID) ?? DEFAULT_AGENT_ID,
|
|
488
|
+
feishu: {
|
|
489
|
+
appId,
|
|
490
|
+
appSecret,
|
|
491
|
+
baseUrl: optional(env.FEISHU_BASE_URL) ?? DEFAULT_FEISHU_BASE_URL,
|
|
492
|
+
cardStreamLeaseMs,
|
|
493
|
+
streamMinIntervalMs
|
|
494
|
+
},
|
|
495
|
+
pi: {
|
|
496
|
+
...apiKey ? { apiKey } : {},
|
|
497
|
+
...baseUrl ? { baseUrl } : {},
|
|
498
|
+
...model ? { model } : {},
|
|
499
|
+
...thinkingLevel ? { thinkingLevel } : {}
|
|
500
|
+
}
|
|
501
|
+
};
|
|
858
502
|
});
|
|
859
503
|
}
|
|
860
|
-
function
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
function parseStrictBoolean(value, variable, defaultValue) {
|
|
864
|
-
if (value === void 0) return defaultValue;
|
|
865
|
-
const normalized = value.trim();
|
|
866
|
-
if (normalized === "true") return true;
|
|
867
|
-
if (normalized === "false") return false;
|
|
868
|
-
throw new RivusModelManagementConfigError("invalid_enabled", `${variable} must be true or false`);
|
|
869
|
-
}
|
|
870
|
-
/**
|
|
871
|
-
* Migrate only the legacy model selection at the explicit enable/disable
|
|
872
|
-
* boundary. The helper does not read or write managed model state.
|
|
873
|
-
*/
|
|
874
|
-
async function migrateRivusModelManagementConfig(input) {
|
|
875
|
-
if (typeof input.managementEnabled !== "boolean") throw new RivusModelManagementConfigError("invalid_enabled", "model management enabled state must be boolean");
|
|
876
|
-
if (input.managementEnabled) return enableModelManagement(input);
|
|
877
|
-
return disableModelManagement(input, parseModelReference(input.knownGoodProvider, input.knownGoodModel));
|
|
878
|
-
}
|
|
879
|
-
async function enableModelManagement(input) {
|
|
880
|
-
if (hasAmbientModelOverride(input.env)) return {
|
|
881
|
-
initialModel: parseConfiguredModel(input.env.PI_MODEL),
|
|
882
|
-
source: "ambient",
|
|
883
|
-
status: "enabled"
|
|
884
|
-
};
|
|
885
|
-
return {
|
|
886
|
-
initialModel: parseConfiguredModel((await loadEnvFile(requireEnvFilePath(input.envFilePath), input.env)).PI_MODEL),
|
|
887
|
-
source: "env-file",
|
|
888
|
-
status: "enabled"
|
|
889
|
-
};
|
|
504
|
+
function optional(value) {
|
|
505
|
+
const trimmed = value?.trim();
|
|
506
|
+
return trimmed ? trimmed : void 0;
|
|
890
507
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
if (hasAmbientModelOverride(input.env)) throw new RivusModelManagementConfigError("ambient_model_override", "ambient PI_MODEL overrides the original env source and prevents verified model readback");
|
|
894
|
-
const original = await readWritableEnvFile(envFilePath);
|
|
895
|
-
const existingModel = parseOptionalConfiguredModel((await loadEnvFile(envFilePath, input.env)).PI_MODEL);
|
|
896
|
-
if (existingModel && sameModel(existingModel, knownGood)) return {
|
|
897
|
-
restoredModel: knownGood,
|
|
898
|
-
source: "env-file",
|
|
899
|
-
status: "unchanged"
|
|
900
|
-
};
|
|
901
|
-
const next = replacePiModel(original, knownGood);
|
|
902
|
-
try {
|
|
903
|
-
await input.writeAtomicTextFile(envFilePath, next, { durable: true });
|
|
904
|
-
} catch {
|
|
905
|
-
throw new RivusModelManagementConfigError("env_source_write_failed", "the original env source write did not complete with a known durable result");
|
|
906
|
-
}
|
|
907
|
-
let readBack;
|
|
908
|
-
try {
|
|
909
|
-
readBack = parseConfiguredModel((await loadMergedLocalEnvFile(envFilePath, input.env)).PI_MODEL);
|
|
910
|
-
} catch {
|
|
911
|
-
throw new RivusModelManagementConfigError("readback_mismatch", "the original env source could not be verified after restoring PI_MODEL");
|
|
912
|
-
}
|
|
913
|
-
if (readBack.provider !== knownGood.provider || readBack.model !== knownGood.model) throw new RivusModelManagementConfigError("readback_mismatch", "the original env source did not read back the known-good PI_MODEL");
|
|
914
|
-
return {
|
|
915
|
-
restoredModel: knownGood,
|
|
916
|
-
source: "env-file",
|
|
917
|
-
status: "exported"
|
|
918
|
-
};
|
|
508
|
+
function readUtf8File(path) {
|
|
509
|
+
return readFile(path, "utf8");
|
|
919
510
|
}
|
|
920
|
-
function
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
511
|
+
function optionalPiApiKey(env, readTextFile) {
|
|
512
|
+
const inlineApiKey = optional(env.PI_API_KEY);
|
|
513
|
+
const apiKeyFile = optional(env.PI_API_KEY_FILE);
|
|
514
|
+
if (inlineApiKey && apiKeyFile) return Effect.fail(new RivusDaemonConfigError("PI_API_KEY", "PI_API_KEY and PI_API_KEY_FILE cannot both be set"));
|
|
515
|
+
if (inlineApiKey) return Effect.succeed(inlineApiKey);
|
|
516
|
+
if (!apiKeyFile) return Effect.succeed(void 0);
|
|
517
|
+
return Effect.tryPromise({
|
|
518
|
+
try: async () => readTextFile(apiKeyFile),
|
|
519
|
+
catch: (error) => new RivusDaemonConfigError("PI_API_KEY_FILE", `PI_API_KEY_FILE could not be read: ${formatConfigError(error)}`)
|
|
520
|
+
}).pipe(Effect.flatMap((contents) => {
|
|
521
|
+
const apiKey = optional(contents);
|
|
522
|
+
return apiKey ? Effect.succeed(apiKey) : Effect.fail(new RivusDaemonConfigError("PI_API_KEY_FILE", "PI_API_KEY_FILE must not be empty"));
|
|
523
|
+
}));
|
|
926
524
|
}
|
|
927
|
-
function
|
|
928
|
-
|
|
929
|
-
if (!configured) throw new RivusModelManagementConfigError("model_missing", "PI_MODEL is missing from the effective legacy config");
|
|
930
|
-
const separator = configured.indexOf("/");
|
|
931
|
-
if (separator <= 0 || separator === configured.length - 1 || configured.indexOf("/", separator + 1) !== -1) throw new RivusModelManagementConfigError("invalid_model", "PI_MODEL must use provider/model form");
|
|
932
|
-
return parseModelReference(configured.slice(0, separator), configured.slice(separator + 1));
|
|
525
|
+
function formatConfigError(error) {
|
|
526
|
+
return error instanceof Error ? error.message : String(error);
|
|
933
527
|
}
|
|
934
|
-
function
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
}
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
528
|
+
function optionalPositiveInteger(value, variable, fallback) {
|
|
529
|
+
const normalized = optional(value);
|
|
530
|
+
if (!normalized) return Effect.succeed(fallback);
|
|
531
|
+
if (/^[1-9]\d*$/.test(normalized)) return Effect.succeed(Number(normalized));
|
|
532
|
+
return Effect.fail(new RivusDaemonConfigError(variable, `${variable} must be a positive integer`));
|
|
941
533
|
}
|
|
942
|
-
function
|
|
943
|
-
|
|
534
|
+
function required(env, variable) {
|
|
535
|
+
const value = optional(env[variable]);
|
|
536
|
+
if (value) return Effect.succeed(value);
|
|
537
|
+
return Effect.fail(new RivusDaemonConfigError(variable, `${variable} is required`));
|
|
944
538
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
}
|
|
539
|
+
function optionalThinkingLevel(value) {
|
|
540
|
+
const normalized = optional(value);
|
|
541
|
+
if (!normalized) return Effect.succeed(void 0);
|
|
542
|
+
if (THINKING_LEVELS.has(normalized)) return Effect.succeed(normalized);
|
|
543
|
+
return Effect.fail(new RivusDaemonConfigError("PI_THINKING_LEVEL", "PI_THINKING_LEVEL must be one of off, minimal, low, medium, high, xhigh"));
|
|
951
544
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
if (isPermissionError(error)) throw new RivusModelManagementConfigError("env_source_unwritable", "the original env source is not writable");
|
|
961
|
-
throw new RivusModelManagementConfigError("env_source_unreadable", "the original env source is unavailable");
|
|
545
|
+
//#endregion
|
|
546
|
+
//#region src/adapters/deployment/manifest/node-rivus-deployment-manifest.ts
|
|
547
|
+
var RivusDeploymentManifestError = class extends Error {
|
|
548
|
+
manifestPath;
|
|
549
|
+
name = "RivusDeploymentManifestError";
|
|
550
|
+
constructor(manifestPath, message, options) {
|
|
551
|
+
super(message, options);
|
|
552
|
+
this.manifestPath = manifestPath;
|
|
962
553
|
}
|
|
554
|
+
};
|
|
555
|
+
function loadRivusDeploymentManifest(manifestPath, options = {}) {
|
|
556
|
+
const maxBytes = options.maxBytes ?? 1048576;
|
|
557
|
+
return Effect.tryPromise({
|
|
558
|
+
try: async () => {
|
|
559
|
+
const metadata = await stat(manifestPath);
|
|
560
|
+
if (!metadata.isFile()) throw new Error("deployment manifest must be a regular file");
|
|
561
|
+
if (metadata.size > maxBytes) throw new Error(`deployment manifest exceeds ${maxBytes} byte limit`);
|
|
562
|
+
const bytes = await readFile(manifestPath);
|
|
563
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
564
|
+
return parseRivusDeploymentManifest(JSON.parse(text));
|
|
565
|
+
},
|
|
566
|
+
catch: (cause) => cause instanceof RivusDeploymentManifestError ? cause : new RivusDeploymentManifestError(manifestPath, `failed to load Rivus deployment manifest: ${cause instanceof Error ? cause.message : String(cause)}`, { cause })
|
|
567
|
+
});
|
|
963
568
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
569
|
+
//#endregion
|
|
570
|
+
//#region src/platform/project/setup/gateway-package-resources.ts
|
|
571
|
+
const packageManifestUrl = import.meta.resolve("@rivus/gateway/package.json");
|
|
572
|
+
const packageRootUrl = new URL("./", packageManifestUrl);
|
|
573
|
+
const gatewayPackageDirectory = fileURLToPath(packageRootUrl);
|
|
574
|
+
const gatewayPackageManifestPath = fileURLToPath(new URL("package.json", packageRootUrl));
|
|
575
|
+
const gatewayTemplateDirectory = fileURLToPath(new URL("templates/", packageRootUrl));
|
|
576
|
+
const gatewaySkillsDirectory = fileURLToPath(new URL("skills/", packageRootUrl));
|
|
577
|
+
//#endregion
|
|
578
|
+
//#region src/adapters/cli/model/rivus-model-cli-protocol.ts
|
|
579
|
+
const RIVUS_MODEL_CLI_USAGE = `Usage:
|
|
580
|
+
rivus model status --json [--request <id>] [--verbose]
|
|
581
|
+
rivus model set --model <provider/model> --expected-revision <n> --request <id> --json [--verbose]
|
|
582
|
+
rivus model rollback --expected-revision <n> --request <id> --json [--verbose]
|
|
583
|
+
|
|
584
|
+
Commands:
|
|
585
|
+
status Read the effective and persistent model selection
|
|
586
|
+
set Submit one authorized model change
|
|
587
|
+
rollback Submit one authorized change to the previous model
|
|
588
|
+
|
|
589
|
+
All command results are JSON. A successful set or rollback call means the request
|
|
590
|
+
was accepted; query status by request id for the final outcome.
|
|
591
|
+
`;
|
|
592
|
+
function parseRivusModelCliArguments(argv) {
|
|
593
|
+
if (argv.includes("--help") || argv.includes("-h")) return { help: true };
|
|
594
|
+
const operation = argv[0];
|
|
595
|
+
if (operation !== "status" && operation !== "set" && operation !== "rollback") return { error: "model requires one of status, set, rollback" };
|
|
596
|
+
let expectedRevision;
|
|
597
|
+
let json = false;
|
|
598
|
+
let model;
|
|
599
|
+
let requestId;
|
|
600
|
+
let verbose = false;
|
|
601
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
602
|
+
const argument = argv[index];
|
|
603
|
+
if (argument === "--json") {
|
|
604
|
+
if (json) return { error: "--json may only be provided once" };
|
|
605
|
+
json = true;
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
if (argument === "--verbose") {
|
|
609
|
+
if (verbose) return { error: "--verbose may only be provided once" };
|
|
610
|
+
verbose = true;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
if (argument === "--request") {
|
|
614
|
+
if (requestId !== void 0) return { error: "--request may only be provided once" };
|
|
615
|
+
const value = argv[index + 1];
|
|
616
|
+
if (!value || value.startsWith("-")) return { error: "--request requires an id" };
|
|
617
|
+
requestId = value;
|
|
618
|
+
index += 1;
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
if (argument.startsWith("--request=")) {
|
|
622
|
+
if (requestId !== void 0) return { error: "--request may only be provided once" };
|
|
623
|
+
requestId = argument.slice(10);
|
|
624
|
+
if (!requestId) return { error: "--request requires an id" };
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (argument === "--expected-revision") {
|
|
628
|
+
if (expectedRevision !== void 0) return { error: "--expected-revision may only be provided once" };
|
|
629
|
+
const value = argv[index + 1];
|
|
630
|
+
if (!value || value.startsWith("-")) return { error: "--expected-revision requires a non-negative integer" };
|
|
631
|
+
const parsed = parseRevision(value);
|
|
632
|
+
if (parsed === void 0) return { error: "--expected-revision requires a non-negative integer" };
|
|
633
|
+
expectedRevision = parsed;
|
|
634
|
+
index += 1;
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
if (argument.startsWith("--expected-revision=")) {
|
|
638
|
+
if (expectedRevision !== void 0) return { error: "--expected-revision may only be provided once" };
|
|
639
|
+
const parsed = parseRevision(argument.slice(20));
|
|
640
|
+
if (parsed === void 0) return { error: "--expected-revision requires a non-negative integer" };
|
|
641
|
+
expectedRevision = parsed;
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (argument === "--model") {
|
|
645
|
+
if (model !== void 0) return { error: "--model may only be provided once" };
|
|
646
|
+
const value = argv[index + 1];
|
|
647
|
+
if (!value || value.startsWith("-")) return { error: "--model requires provider/model" };
|
|
648
|
+
if (!isModelReference(value)) return { error: "--model must be provider/model" };
|
|
649
|
+
model = value;
|
|
650
|
+
index += 1;
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
if (argument.startsWith("--model=")) {
|
|
654
|
+
if (model !== void 0) return { error: "--model may only be provided once" };
|
|
655
|
+
const value = argument.slice(8);
|
|
656
|
+
if (!isModelReference(value)) return { error: "--model must be provider/model" };
|
|
657
|
+
model = value;
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
return { error: `Unknown model option: ${argument}` };
|
|
661
|
+
}
|
|
662
|
+
if (!json) return { error: "model commands require --json" };
|
|
663
|
+
if (operation === "status") {
|
|
664
|
+
if (model !== void 0) return { error: "status does not accept --model" };
|
|
665
|
+
if (expectedRevision !== void 0) return { error: "status does not accept --expected-revision" };
|
|
666
|
+
return {
|
|
667
|
+
json: true,
|
|
668
|
+
operation,
|
|
669
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
670
|
+
...verbose ? { verbose: true } : {}
|
|
671
|
+
};
|
|
976
672
|
}
|
|
977
|
-
if (
|
|
978
|
-
|
|
979
|
-
if (
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
673
|
+
if (requestId === void 0) return { error: "--request is required for model changes" };
|
|
674
|
+
if (expectedRevision === void 0) return { error: "--expected-revision is required for model changes" };
|
|
675
|
+
if (operation === "set" && model === void 0) return { error: "--model is required for model set" };
|
|
676
|
+
if (operation === "rollback" && model !== void 0) return { error: "rollback does not accept --model" };
|
|
677
|
+
return operation === "set" ? {
|
|
678
|
+
expectedRevision,
|
|
679
|
+
json: true,
|
|
680
|
+
model,
|
|
681
|
+
operation,
|
|
682
|
+
requestId,
|
|
683
|
+
...verbose ? { verbose: true } : {}
|
|
684
|
+
} : {
|
|
685
|
+
expectedRevision,
|
|
686
|
+
json: true,
|
|
687
|
+
operation,
|
|
688
|
+
requestId,
|
|
689
|
+
...verbose ? { verbose: true } : {}
|
|
690
|
+
};
|
|
986
691
|
}
|
|
987
|
-
function
|
|
988
|
-
return
|
|
692
|
+
function renderRivusModelCliHelp() {
|
|
693
|
+
return RIVUS_MODEL_CLI_USAGE;
|
|
989
694
|
}
|
|
990
|
-
function
|
|
991
|
-
return
|
|
695
|
+
function renderRivusModelCliArgumentError(error) {
|
|
696
|
+
return `${error}\n\n${RIVUS_MODEL_CLI_USAGE}`;
|
|
992
697
|
}
|
|
993
|
-
function
|
|
994
|
-
return value
|
|
698
|
+
function isModelReference(value) {
|
|
699
|
+
return /^[^/\s]+\/[^/\s]+$/.test(value);
|
|
995
700
|
}
|
|
996
|
-
function
|
|
997
|
-
|
|
701
|
+
function parseRevision(value) {
|
|
702
|
+
if (!/^\d+$/.test(value)) return;
|
|
703
|
+
const revision = Number(value);
|
|
704
|
+
return Number.isSafeInteger(revision) ? revision : void 0;
|
|
998
705
|
}
|
|
999
706
|
//#endregion
|
|
1000
|
-
//#region src/
|
|
1001
|
-
|
|
1002
|
-
const
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
707
|
+
//#region src/adapters/cli/model/rivus-model-management-wire.ts
|
|
708
|
+
function createRivusModelManagementWireRequest(command, env) {
|
|
709
|
+
const context = optionalString(env.RIVUS_MODEL_CONTEXT);
|
|
710
|
+
return {
|
|
711
|
+
...command,
|
|
712
|
+
...context ? { context } : {}
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function parseRivusModelManagementWireRequest(value) {
|
|
716
|
+
if (!isRecord$1(value)) throw new Error("model request must be a JSON object");
|
|
717
|
+
const operation = value.operation;
|
|
718
|
+
if (operation !== "status" && operation !== "set" && operation !== "rollback") throw new Error("model request operation is invalid");
|
|
719
|
+
const context = optionalString(value.context);
|
|
720
|
+
const requestId = optionalString(value.requestId);
|
|
721
|
+
const verbose = value.verbose === true ? true : void 0;
|
|
722
|
+
if (operation === "status") {
|
|
723
|
+
if (value.json !== true) throw new Error("model request must require JSON output");
|
|
1016
724
|
return {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
725
|
+
json: true,
|
|
726
|
+
operation,
|
|
727
|
+
...context ? { context } : {},
|
|
728
|
+
...requestId ? { requestId } : {},
|
|
729
|
+
...verbose ? { verbose: true } : {}
|
|
1021
730
|
};
|
|
1022
731
|
}
|
|
1023
|
-
const
|
|
1024
|
-
if (
|
|
1025
|
-
|
|
732
|
+
const expectedRevision = value.expectedRevision;
|
|
733
|
+
if (value.json !== true || typeof expectedRevision !== "number" || !Number.isSafeInteger(expectedRevision) || expectedRevision < 0) throw new Error("model request has an invalid expected revision or output mode");
|
|
734
|
+
if (!requestId) throw new Error("model request requires a request id");
|
|
735
|
+
if (operation === "set") {
|
|
736
|
+
const model = optionalString(value.model);
|
|
737
|
+
if (!model || !/^[^/\s]+\/[^/\s]+$/.test(model)) throw new Error("model request target is invalid");
|
|
1026
738
|
return {
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
739
|
+
expectedRevision,
|
|
740
|
+
json: true,
|
|
741
|
+
model,
|
|
742
|
+
operation,
|
|
743
|
+
requestId,
|
|
744
|
+
...context ? { context } : {},
|
|
745
|
+
...verbose ? { verbose: true } : {}
|
|
1031
746
|
};
|
|
1032
747
|
}
|
|
1033
|
-
if ((await readMetadata(metadataPath))?.installedDigest !== currentDigest) return {
|
|
1034
|
-
destination,
|
|
1035
|
-
requiresRefresh: false,
|
|
1036
|
-
sourceDigest,
|
|
1037
|
-
status: "conflict"
|
|
1038
|
-
};
|
|
1039
|
-
await writeAtomicTextFile(destination, source, { mode: 384 });
|
|
1040
|
-
await writeMetadata(metadataPath, sourceDigest, writeAtomicTextFile);
|
|
1041
748
|
return {
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
749
|
+
expectedRevision,
|
|
750
|
+
json: true,
|
|
751
|
+
operation,
|
|
752
|
+
requestId,
|
|
753
|
+
...context ? { context } : {},
|
|
754
|
+
...verbose ? { verbose: true } : {}
|
|
1046
755
|
};
|
|
1047
756
|
}
|
|
1048
|
-
function
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
async function readMetadata(path) {
|
|
1060
|
-
const contents = await readOptional(path);
|
|
1061
|
-
if (contents === void 0) return void 0;
|
|
1062
|
-
try {
|
|
1063
|
-
const value = JSON.parse(contents);
|
|
1064
|
-
if (!isRecord$1(value) || typeof value.installedDigest !== "string") return void 0;
|
|
1065
|
-
return { installedDigest: value.installedDigest };
|
|
1066
|
-
} catch {
|
|
1067
|
-
return;
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
async function writeMetadata(path, installedDigest, writeAtomicTextFile) {
|
|
1071
|
-
await writeAtomicTextFile(path, `${JSON.stringify({
|
|
1072
|
-
installedDigest,
|
|
1073
|
-
version: 1
|
|
1074
|
-
}, null, 2)}\n`, { mode: 384 });
|
|
1075
|
-
}
|
|
1076
|
-
function isRecord$1(value) {
|
|
1077
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1078
|
-
}
|
|
1079
|
-
function isMissing$1(error) {
|
|
1080
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1081
|
-
}
|
|
1082
|
-
//#endregion
|
|
1083
|
-
//#region src/platform/home/config/model-management/rivus-model-management-cli-launcher.ts
|
|
1084
|
-
const BIN_DIRECTORY_NAME = "bin";
|
|
1085
|
-
const LAUNCHER_NAME = "rivus";
|
|
1086
|
-
const PRIVATE_MODE = 448;
|
|
1087
|
-
/**
|
|
1088
|
-
* Install the private launcher used by model-management child processes.
|
|
1089
|
-
*
|
|
1090
|
-
* The launcher contains only fixed absolute executable paths. Per-Run
|
|
1091
|
-
* context, credentials, and other authority data stay in the spawn
|
|
1092
|
-
* environment owned by the Host and are never persisted in this file.
|
|
1093
|
-
*/
|
|
1094
|
-
async function installRivusModelManagementCliLauncher(options) {
|
|
1095
|
-
assertAbsolutePath("directory", options.directory);
|
|
1096
|
-
assertAbsolutePath("nodeExecutable", options.nodeExecutable);
|
|
1097
|
-
assertAbsolutePath("cliEntryPath", options.cliEntryPath);
|
|
1098
|
-
const binDirectory = join(options.directory, BIN_DIRECTORY_NAME);
|
|
1099
|
-
const launcherPath = join(binDirectory, LAUNCHER_NAME);
|
|
1100
|
-
await mkdir(binDirectory, {
|
|
1101
|
-
mode: PRIVATE_MODE,
|
|
1102
|
-
recursive: true
|
|
1103
|
-
});
|
|
1104
|
-
await chmod(binDirectory, PRIVATE_MODE);
|
|
1105
|
-
await chmodIfPresent(launcherPath, PRIVATE_MODE);
|
|
1106
|
-
await options.writeAtomicTextFile(launcherPath, renderLauncher(options), { mode: PRIVATE_MODE });
|
|
1107
|
-
await chmod(launcherPath, PRIVATE_MODE);
|
|
1108
|
-
return Object.freeze({
|
|
1109
|
-
binDirectory,
|
|
1110
|
-
launcherPath
|
|
1111
|
-
});
|
|
1112
|
-
}
|
|
1113
|
-
function renderLauncher(options) {
|
|
1114
|
-
return `#!/bin/sh\nexec ${shellQuote(options.nodeExecutable)} ${shellQuote(options.cliEntryPath)} "$@"\n`;
|
|
1115
|
-
}
|
|
1116
|
-
function shellQuote(value) {
|
|
1117
|
-
if (value.includes("\0")) throw new Error("launcher paths must not contain NUL bytes");
|
|
1118
|
-
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
1119
|
-
}
|
|
1120
|
-
function assertAbsolutePath(name, value) {
|
|
1121
|
-
if (value.length === 0 || !isAbsolute(value)) throw new Error(`${name} must be an absolute path`);
|
|
1122
|
-
if (value.includes("\0")) throw new Error(`${name} must not contain NUL bytes`);
|
|
1123
|
-
}
|
|
1124
|
-
async function chmodIfPresent(path, mode) {
|
|
1125
|
-
try {
|
|
1126
|
-
await chmod(path, mode);
|
|
1127
|
-
} catch (error) {
|
|
1128
|
-
if (isMissing(error)) return;
|
|
1129
|
-
throw error;
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
function isMissing(error) {
|
|
1133
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
757
|
+
function toRivusModelManagementSubmission(request) {
|
|
758
|
+
const target = request.operation === "set" ? parseTarget(request.model) : void 0;
|
|
759
|
+
return {
|
|
760
|
+
expectedRevision: request.expectedRevision,
|
|
761
|
+
operation: request.operation,
|
|
762
|
+
requestId: request.requestId,
|
|
763
|
+
...target ? { target } : {},
|
|
764
|
+
...request.verbose ? { verbose: true } : {}
|
|
765
|
+
};
|
|
1134
766
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
767
|
+
function projectRivusModelCliResponse(value, operation) {
|
|
768
|
+
if (!isRecord$1(value)) throw new Error("model handler must return a JSON object");
|
|
769
|
+
const response = { schemaVersion: 1 };
|
|
770
|
+
copyScalar(response, value, "requestId");
|
|
771
|
+
copyScalar(response, value, "revision");
|
|
772
|
+
copyScalar(response, value, "source");
|
|
773
|
+
copyScalar(response, value, "protocolVersion");
|
|
774
|
+
copyScalar(response, value, "runtimeVersion");
|
|
775
|
+
copyScalar(response, value, "operation");
|
|
776
|
+
copyScalar(response, value, "expectedRevision");
|
|
777
|
+
copyScalar(response, value, "phase");
|
|
778
|
+
copyScalar(response, value, "reason");
|
|
779
|
+
for (const key of [
|
|
780
|
+
"actual",
|
|
781
|
+
"current",
|
|
782
|
+
"persisted",
|
|
783
|
+
"previous",
|
|
784
|
+
"target"
|
|
785
|
+
]) {
|
|
786
|
+
const model = projectModelReference(value[key]);
|
|
787
|
+
if (model) response[key] = model;
|
|
1144
788
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
const
|
|
1148
|
-
|
|
1149
|
-
const
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
return
|
|
1160
|
-
const appId = yield* required$1(env, "FEISHU_APP_ID");
|
|
1161
|
-
const appSecret = yield* required$1(env, "FEISHU_APP_SECRET");
|
|
1162
|
-
const streamMinIntervalMs = yield* optionalPositiveInteger(env.FEISHU_STREAM_MIN_INTERVAL_MS, "FEISHU_STREAM_MIN_INTERVAL_MS", DEFAULT_STREAM_MIN_INTERVAL_MS);
|
|
1163
|
-
const cardStreamLeaseMs = yield* optionalPositiveInteger(env.FEISHU_CARD_STREAM_LEASE_MS, "FEISHU_CARD_STREAM_LEASE_MS", DEFAULT_CARD_STREAM_LEASE_MS);
|
|
1164
|
-
const thinkingLevel = yield* optionalThinkingLevel(env.PI_THINKING_LEVEL);
|
|
1165
|
-
const apiKey = yield* optionalPiApiKey(env, options.readTextFile ?? readUtf8File);
|
|
1166
|
-
const baseUrl = optional(env.PI_BASE_URL);
|
|
1167
|
-
const model = optional(env.PI_MODEL);
|
|
1168
|
-
return {
|
|
1169
|
-
agentId: optional(env.RIVUS_AGENT_ID) ?? DEFAULT_AGENT_ID,
|
|
1170
|
-
feishu: {
|
|
1171
|
-
appId,
|
|
1172
|
-
appSecret,
|
|
1173
|
-
baseUrl: optional(env.FEISHU_BASE_URL) ?? DEFAULT_FEISHU_BASE_URL,
|
|
1174
|
-
cardStreamLeaseMs,
|
|
1175
|
-
streamMinIntervalMs
|
|
1176
|
-
},
|
|
1177
|
-
pi: {
|
|
1178
|
-
...apiKey ? { apiKey } : {},
|
|
1179
|
-
...baseUrl ? { baseUrl } : {},
|
|
1180
|
-
...model ? { model } : {},
|
|
1181
|
-
...thinkingLevel ? { thinkingLevel } : {}
|
|
1182
|
-
}
|
|
1183
|
-
};
|
|
1184
|
-
});
|
|
789
|
+
const budget = projectBudget(value.budget);
|
|
790
|
+
if (budget) response.budget = budget;
|
|
791
|
+
const pending = projectPending(value.pending);
|
|
792
|
+
if (pending) response.pending = pending;
|
|
793
|
+
const request = projectReceipt(value.request);
|
|
794
|
+
if (request) response.request = request;
|
|
795
|
+
const error = projectError(value.error);
|
|
796
|
+
if (error) response.error = error;
|
|
797
|
+
const recoveryRequired = projectError(value.recoveryRequired);
|
|
798
|
+
if (recoveryRequired) response.recoveryRequired = recoveryRequired;
|
|
799
|
+
const notification = projectNotification(value.notification);
|
|
800
|
+
if (notification) response.notification = notification;
|
|
801
|
+
const status = value.status;
|
|
802
|
+
response.status = isPublicStatus(status) ? status : value.recoveryRequired !== void 0 ? "recovery-required" : operation === "status" && value.pending !== void 0 ? "pending" : operation === "status" ? "applied" : "failed";
|
|
803
|
+
return response;
|
|
1185
804
|
}
|
|
1186
|
-
function
|
|
1187
|
-
|
|
1188
|
-
|
|
805
|
+
function createRivusModelManagementFailure(code, message) {
|
|
806
|
+
return {
|
|
807
|
+
error: {
|
|
808
|
+
code,
|
|
809
|
+
message
|
|
810
|
+
},
|
|
811
|
+
schemaVersion: 1,
|
|
812
|
+
status: "failed"
|
|
813
|
+
};
|
|
1189
814
|
}
|
|
1190
|
-
function
|
|
1191
|
-
|
|
815
|
+
function projectModelReference(value) {
|
|
816
|
+
if (!isRecord$1(value) || typeof value.provider !== "string" || typeof value.model !== "string") return;
|
|
817
|
+
const result = {
|
|
818
|
+
model: value.model,
|
|
819
|
+
provider: value.provider
|
|
820
|
+
};
|
|
821
|
+
if (typeof value.label === "string") return {
|
|
822
|
+
...result,
|
|
823
|
+
label: value.label
|
|
824
|
+
};
|
|
825
|
+
if (typeof value.bindingRevision === "string") return {
|
|
826
|
+
...result,
|
|
827
|
+
bindingRevision: value.bindingRevision
|
|
828
|
+
};
|
|
829
|
+
return result;
|
|
1192
830
|
}
|
|
1193
|
-
function
|
|
1194
|
-
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
if (
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
831
|
+
function projectPending(value) {
|
|
832
|
+
if (!isRecord$1(value)) return;
|
|
833
|
+
const result = {};
|
|
834
|
+
copyScalar(result, value, "phase");
|
|
835
|
+
copyScalar(result, value, "reason");
|
|
836
|
+
if (result.reason === void 0 && typeof result.phase === "string") result.reason = result.phase;
|
|
837
|
+
copyScalar(result, value, "requestId");
|
|
838
|
+
copyScalar(result, value, "updatedAt");
|
|
839
|
+
const request = projectReceipt(value.request);
|
|
840
|
+
if (request) result.request = request;
|
|
841
|
+
const target = projectModelReference(value.target);
|
|
842
|
+
if (target) result.target = target;
|
|
843
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
1206
844
|
}
|
|
1207
|
-
function
|
|
1208
|
-
|
|
845
|
+
function projectReceipt(value) {
|
|
846
|
+
if (!isRecord$1(value)) return;
|
|
847
|
+
const result = scalarFields(value, [
|
|
848
|
+
"requestId",
|
|
849
|
+
"revision",
|
|
850
|
+
"status",
|
|
851
|
+
"phase",
|
|
852
|
+
"operation",
|
|
853
|
+
"expectedRevision",
|
|
854
|
+
"acceptedAt",
|
|
855
|
+
"updatedAt"
|
|
856
|
+
]);
|
|
857
|
+
const current = projectModelReference(value.current);
|
|
858
|
+
if (current) result.current = current;
|
|
859
|
+
const previous = projectModelReference(value.previous);
|
|
860
|
+
if (previous) result.previous = previous;
|
|
861
|
+
const target = projectModelReference(value.target);
|
|
862
|
+
if (target) result.target = target;
|
|
863
|
+
const budget = projectBudget(value.budget);
|
|
864
|
+
if (budget) result.budget = budget;
|
|
865
|
+
const error = projectError(value.error);
|
|
866
|
+
if (error) result.error = error;
|
|
867
|
+
const notification = projectNotification(value.notification);
|
|
868
|
+
if (notification) result.notification = notification;
|
|
869
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
1209
870
|
}
|
|
1210
|
-
function
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
871
|
+
function projectBudget(value) {
|
|
872
|
+
if (!isRecord$1(value)) return;
|
|
873
|
+
const result = scalarFields(value, [
|
|
874
|
+
"deadlineAt",
|
|
875
|
+
"maxOutputTokens",
|
|
876
|
+
"maxPaidRequests",
|
|
877
|
+
"outputTokens",
|
|
878
|
+
"paidRequests"
|
|
879
|
+
]);
|
|
880
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
1215
881
|
}
|
|
1216
|
-
function
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
882
|
+
function projectError(value) {
|
|
883
|
+
if (!isRecord$1(value) || typeof value.code !== "string" || typeof value.message !== "string") return;
|
|
884
|
+
return {
|
|
885
|
+
code: value.code,
|
|
886
|
+
message: value.message
|
|
887
|
+
};
|
|
1220
888
|
}
|
|
1221
|
-
function
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
889
|
+
function projectNotification(value) {
|
|
890
|
+
if (!isRecord$1(value)) return;
|
|
891
|
+
const result = scalarFields(value, [
|
|
892
|
+
"attempts",
|
|
893
|
+
"lastAttemptAt",
|
|
894
|
+
"status"
|
|
895
|
+
]);
|
|
896
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
1226
897
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
const
|
|
1230
|
-
|
|
1231
|
-
const gatewayPackageDirectory = fileURLToPath(packageRootUrl);
|
|
1232
|
-
const gatewayPackageManifestPath = fileURLToPath(new URL("package.json", packageRootUrl));
|
|
1233
|
-
const gatewayTemplateDirectory = fileURLToPath(new URL("templates/", packageRootUrl));
|
|
1234
|
-
const gatewaySkillsDirectory = fileURLToPath(new URL("skills/", packageRootUrl));
|
|
1235
|
-
//#endregion
|
|
1236
|
-
//#region src/platform/project/setup/release-descriptor.ts
|
|
1237
|
-
const RIVUS_RELEASE_DESCRIPTOR = Object.freeze({
|
|
1238
|
-
version: "0.16.6",
|
|
1239
|
-
dependencies: Object.freeze({
|
|
1240
|
-
"@earendil-works/pi-coding-agent": "0.84.4",
|
|
1241
|
-
"@larksuiteoapi/node-sdk": "1.71.1",
|
|
1242
|
-
effect: "^3.21.4"
|
|
1243
|
-
})
|
|
1244
|
-
});
|
|
1245
|
-
//#endregion
|
|
1246
|
-
//#region src/core/application/deployment/manifest/deployment-manifest.ts
|
|
1247
|
-
const DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_MS = 3e4;
|
|
1248
|
-
const DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS = 1e4;
|
|
1249
|
-
const DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES = 3;
|
|
1250
|
-
const DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS = 4;
|
|
1251
|
-
const DEFAULT_MANIFEST_BACKGROUND_SESSION_RETRY_BACKOFF_MS = 3e4;
|
|
1252
|
-
const DEFAULT_MANIFEST_BACKGROUND_SESSION_LIFETIME_MS = 1440 * 60 * 1e3;
|
|
1253
|
-
const DEFAULT_MANIFEST_BACKGROUND_SESSION_STEP_TIMEOUT_MS = 300 * 1e3;
|
|
1254
|
-
const DEFAULT_MANIFEST_CARD_STREAM_LEASE_MS = 51e4;
|
|
1255
|
-
const DEFAULT_MANIFEST_CONVERSATION_PROGRESS_DISPLAY = "collapsed";
|
|
1256
|
-
const MEMORY_SCOPES = [
|
|
1257
|
-
"conversation",
|
|
1258
|
-
"agent-private",
|
|
1259
|
-
"project",
|
|
1260
|
-
"shared-user-profile"
|
|
1261
|
-
];
|
|
1262
|
-
var InvalidRivusProjectSpace = class extends Error {
|
|
1263
|
-
name = "InvalidRivusProjectSpace";
|
|
1264
|
-
};
|
|
1265
|
-
function validateRivusProjectSpaceDeployments(declarations) {
|
|
1266
|
-
const projectSpaceIds = /* @__PURE__ */ new Set();
|
|
1267
|
-
for (const declaration of declarations) {
|
|
1268
|
-
if (projectSpaceIds.has(declaration.id)) throw new InvalidRivusProjectSpace(`duplicate project space: ${declaration.id}`);
|
|
1269
|
-
projectSpaceIds.add(declaration.id);
|
|
1270
|
-
validateRivusProjectSpaceDeployment(declaration);
|
|
1271
|
-
}
|
|
1272
|
-
return projectSpaceIds;
|
|
898
|
+
function scalarFields(source, keys) {
|
|
899
|
+
const result = {};
|
|
900
|
+
for (const key of keys) copyScalar(result, source, key);
|
|
901
|
+
return result;
|
|
1273
902
|
}
|
|
1274
|
-
function
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
const sources = /* @__PURE__ */ new Set();
|
|
1278
|
-
for (const source of declaration.skills.sources) {
|
|
1279
|
-
validateRelativeProjectPath(source, `project space ${declaration.id} Skill source`);
|
|
1280
|
-
if (sources.has(source)) throw new InvalidRivusProjectSpace(`project space ${declaration.id} contains duplicate Skill source: ${source}`);
|
|
1281
|
-
sources.add(source);
|
|
1282
|
-
}
|
|
903
|
+
function copyScalar(target, source, key) {
|
|
904
|
+
const value = source[key];
|
|
905
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") target[key] = value;
|
|
1283
906
|
}
|
|
1284
|
-
function
|
|
1285
|
-
if (value
|
|
907
|
+
function parseTarget(value) {
|
|
908
|
+
if (!value) throw new Error("model request target is missing");
|
|
909
|
+
const separator = value.indexOf("/");
|
|
910
|
+
return {
|
|
911
|
+
model: value.slice(separator + 1),
|
|
912
|
+
provider: value.slice(0, separator)
|
|
913
|
+
};
|
|
1286
914
|
}
|
|
1287
|
-
function
|
|
1288
|
-
return value
|
|
915
|
+
function isPublicStatus(value) {
|
|
916
|
+
return value === "applied" || value === "failed" || value === "pending" || value === "recovery-required" || value === "restored";
|
|
1289
917
|
}
|
|
1290
|
-
function
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
"
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
"id",
|
|
1370
|
-
"progressDisplay",
|
|
1371
|
-
"required",
|
|
1372
|
-
"sessionNamespace",
|
|
1373
|
-
"streamMinIntervalMs"
|
|
1374
|
-
], `manifest.endpoints[${index}]`, [
|
|
1375
|
-
"cardStreamLeaseMs",
|
|
1376
|
-
"experimental",
|
|
1377
|
-
"progressDisplay"
|
|
1378
|
-
]);
|
|
1379
|
-
const experimental = endpoint.experimental === void 0 ? void 0 : record(endpoint.experimental, `manifest.endpoints[${index}].experimental`);
|
|
1380
|
-
if (experimental) exactKeys(experimental, ["cotMessages"], `manifest.endpoints[${index}].experimental`);
|
|
1381
|
-
return Object.freeze({
|
|
1382
|
-
agentId: string(endpoint.agentId, `manifest.endpoints[${index}].agentId`),
|
|
1383
|
-
baseUrl: string(endpoint.baseUrl, `manifest.endpoints[${index}].baseUrl`),
|
|
1384
|
-
cardStreamLeaseMs: endpoint.cardStreamLeaseMs === void 0 ? DEFAULT_MANIFEST_CARD_STREAM_LEASE_MS : positiveInteger(endpoint.cardStreamLeaseMs, `manifest.endpoints[${index}].cardStreamLeaseMs`),
|
|
1385
|
-
credentialRef: string(endpoint.credentialRef, `manifest.endpoints[${index}].credentialRef`),
|
|
1386
|
-
enabled: boolean(endpoint.enabled, `manifest.endpoints[${index}].enabled`),
|
|
1387
|
-
...experimental ? { experimental: Object.freeze({ cotMessages: boolean(experimental.cotMessages, `manifest.endpoints[${index}].experimental.cotMessages`) }) } : {},
|
|
1388
|
-
groupPolicy: groupPolicy(endpoint.groupPolicy, `manifest.endpoints[${index}].groupPolicy`),
|
|
1389
|
-
id: string(endpoint.id, `manifest.endpoints[${index}].id`),
|
|
1390
|
-
progressDisplay: endpoint.progressDisplay === void 0 ? DEFAULT_MANIFEST_CONVERSATION_PROGRESS_DISPLAY : progressDisplay(endpoint.progressDisplay, `manifest.endpoints[${index}].progressDisplay`),
|
|
1391
|
-
required: boolean(endpoint.required, `manifest.endpoints[${index}].required`),
|
|
1392
|
-
sessionNamespace: string(endpoint.sessionNamespace, `manifest.endpoints[${index}].sessionNamespace`),
|
|
1393
|
-
streamMinIntervalMs: positiveInteger(endpoint.streamMinIntervalMs, `manifest.endpoints[${index}].streamMinIntervalMs`)
|
|
1394
|
-
});
|
|
1395
|
-
});
|
|
1396
|
-
const automations = array(root.automations ?? [], "manifest.automations").map((entry, index) => {
|
|
1397
|
-
const automation = record(entry, `manifest.automations[${index}]`);
|
|
1398
|
-
exactKeys(automation, [
|
|
1399
|
-
"agentId",
|
|
1400
|
-
"delivery",
|
|
1401
|
-
"enabled",
|
|
1402
|
-
"id",
|
|
1403
|
-
"required",
|
|
1404
|
-
"schedule",
|
|
1405
|
-
"templateId",
|
|
1406
|
-
"timeZone"
|
|
1407
|
-
], `manifest.automations[${index}]`);
|
|
1408
|
-
const delivery = record(automation.delivery, `manifest.automations[${index}].delivery`);
|
|
1409
|
-
exactKeys(delivery, [
|
|
1410
|
-
"endpointId",
|
|
1411
|
-
"targetRef",
|
|
1412
|
-
"targetType"
|
|
1413
|
-
], `manifest.automations[${index}].delivery`);
|
|
1414
|
-
return Object.freeze({
|
|
1415
|
-
agentId: string(automation.agentId, `manifest.automations[${index}].agentId`),
|
|
1416
|
-
delivery: Object.freeze({
|
|
1417
|
-
endpointId: string(delivery.endpointId, `manifest.automations[${index}].delivery.endpointId`),
|
|
1418
|
-
targetRef: string(delivery.targetRef, `manifest.automations[${index}].delivery.targetRef`),
|
|
1419
|
-
targetType: automationTargetType(delivery.targetType, `manifest.automations[${index}].delivery.targetType`)
|
|
1420
|
-
}),
|
|
1421
|
-
enabled: boolean(automation.enabled, `manifest.automations[${index}].enabled`),
|
|
1422
|
-
id: string(automation.id, `manifest.automations[${index}].id`),
|
|
1423
|
-
required: boolean(automation.required, `manifest.automations[${index}].required`),
|
|
1424
|
-
schedule: string(automation.schedule, `manifest.automations[${index}].schedule`),
|
|
1425
|
-
templateId: string(automation.templateId, `manifest.automations[${index}].templateId`),
|
|
1426
|
-
timeZone: string(automation.timeZone, `manifest.automations[${index}].timeZone`)
|
|
1427
|
-
});
|
|
918
|
+
function optionalString(value) {
|
|
919
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
920
|
+
}
|
|
921
|
+
function isRecord$1(value) {
|
|
922
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
923
|
+
}
|
|
924
|
+
//#endregion
|
|
925
|
+
//#region src/adapters/cli/model/rivus-model-management-socket-server.ts
|
|
926
|
+
const MAX_FRAME_BYTES = 65536;
|
|
927
|
+
function createRivusModelManagementSocketServer(options) {
|
|
928
|
+
assertAbsoluteSocketPath(options.socketPath);
|
|
929
|
+
let server;
|
|
930
|
+
let socketIdentity;
|
|
931
|
+
let listening = false;
|
|
932
|
+
let startPromise;
|
|
933
|
+
return {
|
|
934
|
+
close: async () => {
|
|
935
|
+
if (startPromise) await startPromise.catch(() => void 0);
|
|
936
|
+
const current = server;
|
|
937
|
+
const identity = socketIdentity;
|
|
938
|
+
server = void 0;
|
|
939
|
+
socketIdentity = void 0;
|
|
940
|
+
if (current && listening) await closeServer(current);
|
|
941
|
+
listening = false;
|
|
942
|
+
if (identity) await unlinkOwnedSocket(options.socketPath, identity);
|
|
943
|
+
},
|
|
944
|
+
listening: () => listening,
|
|
945
|
+
start: async () => {
|
|
946
|
+
if (listening) return;
|
|
947
|
+
if (startPromise) return startPromise;
|
|
948
|
+
startPromise = startServer(options, (next, identity) => {
|
|
949
|
+
server = next;
|
|
950
|
+
socketIdentity = identity;
|
|
951
|
+
listening = true;
|
|
952
|
+
});
|
|
953
|
+
try {
|
|
954
|
+
await startPromise;
|
|
955
|
+
} finally {
|
|
956
|
+
startPromise = void 0;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
async function startServer(options, onStarted) {
|
|
962
|
+
await prepareSocketParentDirectory(options.socketPath);
|
|
963
|
+
const lockPath = `${options.socketPath}.lock`;
|
|
964
|
+
const lock = await acquireStartLock(lockPath);
|
|
965
|
+
const next = createServer((socket) => handleConnection(socket, options));
|
|
966
|
+
let bound = false;
|
|
967
|
+
try {
|
|
968
|
+
await prepareSocketPath(options.socketPath);
|
|
969
|
+
await listen(next, options.socketPath);
|
|
970
|
+
bound = true;
|
|
971
|
+
await chmod(options.socketPath, 384);
|
|
972
|
+
onStarted(next, await readSocketIdentity(options.socketPath));
|
|
973
|
+
} catch (error) {
|
|
974
|
+
await closeServer(next).catch(() => void 0);
|
|
975
|
+
if (bound) await unlinkSocketIfPresent(options.socketPath);
|
|
976
|
+
throw error;
|
|
977
|
+
} finally {
|
|
978
|
+
await releaseStartLock(lock, lockPath);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
async function handleConnection(socket, options) {
|
|
982
|
+
let buffer = "";
|
|
983
|
+
let handled = false;
|
|
984
|
+
socket.setEncoding("utf8");
|
|
985
|
+
socket.on("data", (chunk) => {
|
|
986
|
+
if (handled) return;
|
|
987
|
+
buffer += chunk;
|
|
988
|
+
if (Buffer.byteLength(buffer, "utf8") > MAX_FRAME_BYTES) {
|
|
989
|
+
handled = true;
|
|
990
|
+
writeResponse(socket, createRivusModelManagementFailure("request_too_large", "model management request is too large"));
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
const newline = buffer.indexOf("\n");
|
|
994
|
+
if (newline < 0) return;
|
|
995
|
+
handled = true;
|
|
996
|
+
handleFrame(buffer.slice(0, newline).trim(), socket, options);
|
|
1428
997
|
});
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
"root",
|
|
1434
|
-
"skills",
|
|
1435
|
-
"workingDirectory"
|
|
1436
|
-
], `manifest.projectSpaces[${index}]`);
|
|
1437
|
-
const skills = record(projectSpace.skills, `manifest.projectSpaces[${index}].skills`);
|
|
1438
|
-
exactKeys(skills, ["sources"], `manifest.projectSpaces[${index}].skills`);
|
|
1439
|
-
return Object.freeze({
|
|
1440
|
-
id: string(projectSpace.id, `manifest.projectSpaces[${index}].id`),
|
|
1441
|
-
root: string(projectSpace.root, `manifest.projectSpaces[${index}].root`),
|
|
1442
|
-
skills: Object.freeze({ sources: Object.freeze(array(skills.sources, `manifest.projectSpaces[${index}].skills.sources`).map((item, itemIndex) => string(item, `manifest.projectSpaces[${index}].skills.sources[${itemIndex}]`))) }),
|
|
1443
|
-
workingDirectory: string(projectSpace.workingDirectory, `manifest.projectSpaces[${index}].workingDirectory`)
|
|
1444
|
-
});
|
|
998
|
+
socket.on("end", () => {
|
|
999
|
+
if (handled || buffer.trim() === "") return;
|
|
1000
|
+
handled = true;
|
|
1001
|
+
handleFrame(buffer.trim(), socket, options);
|
|
1445
1002
|
});
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
agents: Object.freeze(agents),
|
|
1449
|
-
automations: Object.freeze(automations),
|
|
1450
|
-
...backgroundSessions ? { backgroundSessions } : {},
|
|
1451
|
-
defaultAgentId: string(root.defaultAgentId, "manifest.defaultAgentId"),
|
|
1452
|
-
defaultEndpointId: string(root.defaultEndpointId, "manifest.defaultEndpointId"),
|
|
1453
|
-
endpoints: Object.freeze(endpoints),
|
|
1454
|
-
plugins: Object.freeze(plugins),
|
|
1455
|
-
projectSpaces: Object.freeze(projectSpaces)
|
|
1003
|
+
socket.on("error", () => {
|
|
1004
|
+
handled = true;
|
|
1456
1005
|
});
|
|
1457
1006
|
}
|
|
1458
|
-
function
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1007
|
+
async function handleFrame(frame, socket, options) {
|
|
1008
|
+
let response;
|
|
1009
|
+
let request;
|
|
1010
|
+
try {
|
|
1011
|
+
request = parseRivusModelManagementWireRequest(JSON.parse(frame));
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
response = createRivusModelManagementFailure("invalid_request", error instanceof Error ? error.message : "invalid model request");
|
|
1014
|
+
writeResponse(socket, response);
|
|
1015
|
+
return;
|
|
1465
1016
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1017
|
+
try {
|
|
1018
|
+
if (request.operation === "status") response = projectRivusModelCliResponse(await options.handlers.status({
|
|
1019
|
+
...request.requestId ? { requestId: request.requestId } : {},
|
|
1020
|
+
...request.verbose ? { verbose: true } : {}
|
|
1021
|
+
}), request.operation);
|
|
1022
|
+
else response = await handleMutation(request, options);
|
|
1023
|
+
} catch {
|
|
1024
|
+
response = createRivusModelManagementFailure("handler_failed", "the model management request could not be completed");
|
|
1474
1025
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
if (!agent.endpointIds.includes(endpoint.id)) throw new Error(`endpoint ${endpoint.id} is not declared by agent ${endpoint.agentId}`);
|
|
1026
|
+
writeResponse(socket, response);
|
|
1027
|
+
}
|
|
1028
|
+
async function handleMutation(request, options) {
|
|
1029
|
+
if (!request.context || !options.resolveTrustedRun) return createRivusModelManagementFailure("trusted_context_required", "a trusted Run context is required for model changes");
|
|
1030
|
+
let trustedRun;
|
|
1031
|
+
try {
|
|
1032
|
+
trustedRun = await options.resolveTrustedRun.resolve(request.context);
|
|
1033
|
+
} catch {
|
|
1034
|
+
trustedRun = void 0;
|
|
1485
1035
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1036
|
+
if (trustedRun === void 0 || trustedRun === null) return createRivusModelManagementFailure("trusted_context_invalid", "the trusted Run context is invalid or expired");
|
|
1037
|
+
try {
|
|
1038
|
+
return projectRivusModelCliResponse(await options.handlers.handle(toRivusModelManagementSubmission(request), trustedRun), request.operation);
|
|
1039
|
+
} catch {
|
|
1040
|
+
return createRivusModelManagementFailure("handler_failed", "the model management request could not be completed");
|
|
1490
1041
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
if (!endpoint) throw new Error(`automation ${automation.id} references unknown delivery endpoint: ${automation.delivery.endpointId}`);
|
|
1498
|
-
if (automation.enabled && !endpoint.enabled) throw new Error(`automation ${automation.id} delivery endpoint must be enabled`);
|
|
1042
|
+
}
|
|
1043
|
+
function writeResponse(socket, response) {
|
|
1044
|
+
try {
|
|
1045
|
+
socket.end(`${JSON.stringify(response)}\n`);
|
|
1046
|
+
} catch {
|
|
1047
|
+
socket.destroy();
|
|
1499
1048
|
}
|
|
1500
|
-
const defaultAgent = agentById.get(manifest.defaultAgentId);
|
|
1501
|
-
if (!defaultAgent) throw new Error(`default agent does not exist: ${manifest.defaultAgentId}`);
|
|
1502
|
-
const defaultEndpoint = manifest.endpoints.find(({ id }) => id === manifest.defaultEndpointId);
|
|
1503
|
-
if (!defaultEndpoint) throw new Error(`default endpoint does not exist: ${manifest.defaultEndpointId}`);
|
|
1504
|
-
if (defaultEndpoint.agentId !== defaultAgent.agentId) throw new Error("default endpoint is not bound to the default agent");
|
|
1505
|
-
if (!defaultEndpoint.enabled) throw new Error("default endpoint must be enabled");
|
|
1506
|
-
if (manifest.backgroundSessions) validateBackgroundSessions(manifest.backgroundSessions);
|
|
1507
1049
|
}
|
|
1508
|
-
function
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1050
|
+
async function prepareSocketPath(socketPath) {
|
|
1051
|
+
try {
|
|
1052
|
+
const metadata = await stat(socketPath);
|
|
1053
|
+
if (!metadata.isSocket()) throw new Error(`refusing to replace non-socket model path: ${socketPath}`);
|
|
1054
|
+
if (await socketIsLive(socketPath)) throw new Error(`model management socket is already active: ${socketPath}`);
|
|
1055
|
+
const current = await stat(socketPath);
|
|
1056
|
+
if (current.dev !== metadata.dev || current.ino !== metadata.ino) throw new Error(`model management socket changed while starting: ${socketPath}`);
|
|
1057
|
+
await unlink(socketPath);
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
if (!isMissing(error)) throw error;
|
|
1060
|
+
}
|
|
1513
1061
|
}
|
|
1514
|
-
function
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
|
|
1062
|
+
async function prepareSocketParentDirectory(socketPath) {
|
|
1063
|
+
const parent = dirname(socketPath);
|
|
1064
|
+
await mkdir(parent, {
|
|
1065
|
+
mode: 448,
|
|
1066
|
+
recursive: true
|
|
1067
|
+
});
|
|
1068
|
+
await chmod(parent, 448);
|
|
1518
1069
|
}
|
|
1519
|
-
function
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
"
|
|
1532
|
-
"
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
"sessionLifetimeMs",
|
|
1537
|
-
"stepTimeoutMs"
|
|
1538
|
-
]);
|
|
1539
|
-
return Object.freeze({
|
|
1540
|
-
enabled: boolean(value.enabled, "manifest.backgroundSessions.enabled"),
|
|
1541
|
-
leaseMs: positiveInteger(value.leaseMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_MS, "manifest.backgroundSessions.leaseMs"),
|
|
1542
|
-
leaseRenewalIntervalMs: positiveInteger(value.leaseRenewalIntervalMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS, "manifest.backgroundSessions.leaseRenewalIntervalMs"),
|
|
1543
|
-
maxConsecutiveFailures: positiveInteger(value.maxConsecutiveFailures ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES, "manifest.backgroundSessions.maxConsecutiveFailures"),
|
|
1544
|
-
maxConcurrentSessions: positiveInteger(value.maxConcurrentSessions ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS, "manifest.backgroundSessions.maxConcurrentSessions"),
|
|
1545
|
-
required: boolean(value.required, "manifest.backgroundSessions.required"),
|
|
1546
|
-
retryBackoffMs: positiveInteger(value.retryBackoffMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_RETRY_BACKOFF_MS, "manifest.backgroundSessions.retryBackoffMs"),
|
|
1547
|
-
sessionLifetimeMs: positiveInteger(value.sessionLifetimeMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_LIFETIME_MS, "manifest.backgroundSessions.sessionLifetimeMs"),
|
|
1548
|
-
stepTimeoutMs: positiveInteger(value.stepTimeoutMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_STEP_TIMEOUT_MS, "manifest.backgroundSessions.stepTimeoutMs")
|
|
1070
|
+
async function socketIsLive(socketPath) {
|
|
1071
|
+
return new Promise((resolve, reject) => {
|
|
1072
|
+
const socket = createConnection(socketPath);
|
|
1073
|
+
let settled = false;
|
|
1074
|
+
const finish = (result, error) => {
|
|
1075
|
+
if (settled) return;
|
|
1076
|
+
settled = true;
|
|
1077
|
+
socket.destroy();
|
|
1078
|
+
if (error) reject(error);
|
|
1079
|
+
else resolve(result);
|
|
1080
|
+
};
|
|
1081
|
+
socket.setTimeout(250, () => finish(false, /* @__PURE__ */ new Error("model management socket liveness could not be verified")));
|
|
1082
|
+
socket.once("connect", () => finish(true));
|
|
1083
|
+
socket.once("error", (error) => {
|
|
1084
|
+
if (error.code === "ECONNREFUSED" || error.code === "ENOENT") finish(false);
|
|
1085
|
+
else finish(false, error);
|
|
1086
|
+
});
|
|
1549
1087
|
});
|
|
1550
1088
|
}
|
|
1551
|
-
function
|
|
1552
|
-
|
|
1553
|
-
|
|
1089
|
+
async function acquireStartLock(path) {
|
|
1090
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1091
|
+
let handle;
|
|
1092
|
+
try {
|
|
1093
|
+
handle = await open(path, "wx", 384);
|
|
1094
|
+
await handle.writeFile(`${process.pid}\n`, "utf8");
|
|
1095
|
+
const metadata = await handle.stat();
|
|
1096
|
+
return {
|
|
1097
|
+
handle,
|
|
1098
|
+
identity: {
|
|
1099
|
+
dev: metadata.dev,
|
|
1100
|
+
ino: metadata.ino
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
} catch (error) {
|
|
1104
|
+
await handle?.close().catch(() => void 0);
|
|
1105
|
+
if (!isAlreadyExists(error)) throw new Error("model management socket startup is already in progress", { cause: error });
|
|
1106
|
+
const pid = parseLockOwner(await readPersistenceFile(path));
|
|
1107
|
+
if (pid === void 0) throw new Error("model management socket startup lock owner could not be verified; manual recovery is required");
|
|
1108
|
+
if (processIsAlive(pid)) throw new Error("model management socket startup is already in progress");
|
|
1109
|
+
const reclaimed = `${path}.reclaim-${process.pid}-${randomUUID()}`;
|
|
1110
|
+
try {
|
|
1111
|
+
await rename(path, reclaimed);
|
|
1112
|
+
} catch (reclaimError) {
|
|
1113
|
+
if (isMissing(reclaimError)) continue;
|
|
1114
|
+
throw new Error("model management socket startup lock could not be reclaimed safely", { cause: reclaimError });
|
|
1115
|
+
}
|
|
1116
|
+
await unlink(reclaimed).catch((reclaimError) => {
|
|
1117
|
+
if (!isMissing(reclaimError)) throw reclaimError;
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
throw new Error("model management socket startup lock changed during stale-owner recovery; manual recovery is required");
|
|
1554
1122
|
}
|
|
1555
|
-
function
|
|
1556
|
-
|
|
1557
|
-
|
|
1123
|
+
async function releaseStartLock(lock, path) {
|
|
1124
|
+
await lock.handle.close();
|
|
1125
|
+
try {
|
|
1126
|
+
const current = await stat(path);
|
|
1127
|
+
if (current.dev !== lock.identity.dev || current.ino !== lock.identity.ino) return;
|
|
1128
|
+
await unlink(path);
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
if (!isMissing(error)) throw error;
|
|
1131
|
+
}
|
|
1558
1132
|
}
|
|
1559
|
-
function
|
|
1560
|
-
|
|
1561
|
-
|
|
1133
|
+
async function unlinkSocketIfPresent(socketPath) {
|
|
1134
|
+
try {
|
|
1135
|
+
if ((await stat(socketPath)).isSocket()) await unlink(socketPath);
|
|
1136
|
+
} catch (error) {
|
|
1137
|
+
if (!isMissing(error)) throw error;
|
|
1138
|
+
}
|
|
1562
1139
|
}
|
|
1563
|
-
function
|
|
1564
|
-
|
|
1565
|
-
|
|
1140
|
+
async function unlinkOwnedSocket(socketPath, identity) {
|
|
1141
|
+
try {
|
|
1142
|
+
const current = await readSocketIdentity(socketPath);
|
|
1143
|
+
if (current.dev !== identity.dev || current.ino !== identity.ino) return;
|
|
1144
|
+
await unlink(socketPath);
|
|
1145
|
+
} catch (error) {
|
|
1146
|
+
if (!isMissing(error)) throw error;
|
|
1147
|
+
}
|
|
1566
1148
|
}
|
|
1567
|
-
function
|
|
1568
|
-
|
|
1569
|
-
return
|
|
1149
|
+
async function readSocketIdentity(socketPath) {
|
|
1150
|
+
const metadata = await stat(socketPath);
|
|
1151
|
+
return {
|
|
1152
|
+
dev: metadata.dev,
|
|
1153
|
+
ino: metadata.ino
|
|
1154
|
+
};
|
|
1570
1155
|
}
|
|
1571
|
-
function
|
|
1572
|
-
|
|
1573
|
-
|
|
1156
|
+
function listen(server, socketPath) {
|
|
1157
|
+
return new Promise((resolve, reject) => {
|
|
1158
|
+
const onError = (error) => {
|
|
1159
|
+
server.off("listening", onListening);
|
|
1160
|
+
reject(error);
|
|
1161
|
+
};
|
|
1162
|
+
const onListening = () => {
|
|
1163
|
+
server.off("error", onError);
|
|
1164
|
+
resolve();
|
|
1165
|
+
};
|
|
1166
|
+
server.once("error", onError);
|
|
1167
|
+
server.once("listening", onListening);
|
|
1168
|
+
server.listen(socketPath);
|
|
1169
|
+
});
|
|
1574
1170
|
}
|
|
1575
|
-
function
|
|
1576
|
-
|
|
1577
|
-
|
|
1171
|
+
function closeServer(server) {
|
|
1172
|
+
return new Promise((resolve, reject) => {
|
|
1173
|
+
server.close((error) => error ? reject(error) : resolve());
|
|
1174
|
+
});
|
|
1578
1175
|
}
|
|
1579
|
-
function
|
|
1580
|
-
if (
|
|
1581
|
-
return value;
|
|
1176
|
+
function assertAbsoluteSocketPath(socketPath) {
|
|
1177
|
+
if (!isAbsolute(socketPath)) throw new Error("model socket path must be absolute");
|
|
1582
1178
|
}
|
|
1583
|
-
function
|
|
1584
|
-
|
|
1585
|
-
if (!isRivusRuntimeToolId(id)) throw new Error(`${path} must be read, bash, edit, write, grep, find, or ls`);
|
|
1586
|
-
return id;
|
|
1179
|
+
function isMissing(error) {
|
|
1180
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1587
1181
|
}
|
|
1588
|
-
function
|
|
1589
|
-
|
|
1590
|
-
for (const id of ids) {
|
|
1591
|
-
if (result.has(id)) throw new Error(`${path} contains duplicate Runtime Tool: ${id}`);
|
|
1592
|
-
result.add(id);
|
|
1593
|
-
}
|
|
1594
|
-
return [...result];
|
|
1182
|
+
function isAlreadyExists(error) {
|
|
1183
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
1595
1184
|
}
|
|
1596
|
-
function
|
|
1597
|
-
|
|
1598
|
-
|
|
1185
|
+
function processIsAlive(pid) {
|
|
1186
|
+
try {
|
|
1187
|
+
process.kill(pid, 0);
|
|
1188
|
+
return true;
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
return typeof error === "object" && error !== null && "code" in error && error.code !== "ESRCH";
|
|
1191
|
+
}
|
|
1599
1192
|
}
|
|
1600
|
-
function
|
|
1601
|
-
const
|
|
1602
|
-
if (
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1193
|
+
function parseLockOwner(value) {
|
|
1194
|
+
const normalized = value?.trim();
|
|
1195
|
+
if (!normalized || !/^\d+$/.test(normalized)) return;
|
|
1196
|
+
const pid = Number(normalized);
|
|
1197
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
1605
1198
|
}
|
|
1606
1199
|
//#endregion
|
|
1607
|
-
//#region src/
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
appId: required(env, `${prefix}_APP_ID`),
|
|
1617
|
-
appSecret: required(env, `${prefix}_APP_SECRET`)
|
|
1618
|
-
});
|
|
1619
|
-
}
|
|
1620
|
-
function required(env, variable) {
|
|
1621
|
-
const value = env[variable]?.trim();
|
|
1622
|
-
if (!value) throw new FeishuEndpointCredentialError(`${variable} is required`);
|
|
1623
|
-
return value;
|
|
1624
|
-
}
|
|
1200
|
+
//#region src/platform/project/setup/release-descriptor.ts
|
|
1201
|
+
const RIVUS_RELEASE_DESCRIPTOR = Object.freeze({
|
|
1202
|
+
version: "0.18.0",
|
|
1203
|
+
dependencies: Object.freeze({
|
|
1204
|
+
"@earendil-works/pi-coding-agent": "0.84.4",
|
|
1205
|
+
"@larksuiteoapi/node-sdk": "1.74.0",
|
|
1206
|
+
effect: "^4.0.0"
|
|
1207
|
+
})
|
|
1208
|
+
});
|
|
1625
1209
|
//#endregion
|
|
1626
|
-
//#region src/
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1210
|
+
//#region src/platform/runtime-pool/runtime-cache.ts
|
|
1211
|
+
function createRuntimeCache() {
|
|
1212
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1213
|
+
const serial = Semaphore.makeUnsafe(1);
|
|
1214
|
+
const reserve = (key) => serial.withPermits(1)(Effect.gen(function* () {
|
|
1215
|
+
const current = entries.get(key);
|
|
1216
|
+
if (current) return {
|
|
1217
|
+
created: false,
|
|
1218
|
+
entry: current
|
|
1219
|
+
};
|
|
1220
|
+
const entry = {
|
|
1221
|
+
deferred: yield* Deferred.make(),
|
|
1222
|
+
initializationStarted: false,
|
|
1223
|
+
key
|
|
1224
|
+
};
|
|
1225
|
+
entries.set(key, entry);
|
|
1226
|
+
return {
|
|
1227
|
+
created: true,
|
|
1228
|
+
entry
|
|
1229
|
+
};
|
|
1230
|
+
}));
|
|
1231
|
+
const start = (entry, create) => Effect.uninterruptible(Effect.gen(function* () {
|
|
1232
|
+
if (!(yield* serial.withPermits(1)(Effect.sync(() => {
|
|
1233
|
+
if (entry.initializationStarted) return false;
|
|
1234
|
+
entry.initializationStarted = true;
|
|
1235
|
+
return true;
|
|
1236
|
+
})))) return;
|
|
1237
|
+
const initialization = create().pipe(Effect.tapError(() => serial.withPermits(1)(Effect.sync(() => {
|
|
1238
|
+
if (entries.get(entry.key) === entry) entries.delete(entry.key);
|
|
1239
|
+
}))), Effect.exit, Effect.flatMap((exit) => Deferred.done(entry.deferred, exit)), Effect.asVoid);
|
|
1240
|
+
yield* Effect.forkDetach(initialization, { uninterruptible: "inherit" });
|
|
1241
|
+
}));
|
|
1242
|
+
const getOrCreate = (key, create) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
1243
|
+
const selected = yield* reserve(key);
|
|
1244
|
+
yield* start(selected.entry, create);
|
|
1245
|
+
return {
|
|
1246
|
+
entry: selected.entry,
|
|
1247
|
+
runtime: yield* restore(Deferred.await(selected.entry.deferred))
|
|
1248
|
+
};
|
|
1249
|
+
}));
|
|
1250
|
+
return {
|
|
1251
|
+
drain: () => serial.withPermits(1)(Effect.sync(() => {
|
|
1252
|
+
const drained = Object.freeze([...entries.values()]);
|
|
1253
|
+
entries.clear();
|
|
1254
|
+
return drained;
|
|
1255
|
+
})),
|
|
1256
|
+
getExisting: (key) => Effect.gen(function* () {
|
|
1257
|
+
const entry = yield* serial.withPermits(1)(Effect.sync(() => entries.get(key)));
|
|
1258
|
+
if (!entry) return;
|
|
1259
|
+
return {
|
|
1260
|
+
entry,
|
|
1261
|
+
runtime: yield* Deferred.await(entry.deferred)
|
|
1262
|
+
};
|
|
1263
|
+
}),
|
|
1264
|
+
getOrCreate,
|
|
1265
|
+
isCurrent: (key, entry) => serial.withPermits(1)(Effect.sync(() => entries.get(key) === entry)),
|
|
1266
|
+
reserve,
|
|
1267
|
+
size: () => serial.withPermits(1)(Effect.sync(() => entries.size)),
|
|
1268
|
+
start
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
function disposeRuntimeCacheEntries(options) {
|
|
1272
|
+
const disposal = Effect.gen(function* () {
|
|
1273
|
+
const completions = yield* Effect.forEach(options.entries, (entry) => Effect.gen(function* () {
|
|
1274
|
+
const completion = yield* Deferred.make();
|
|
1275
|
+
yield* Effect.forkDetach(Deferred.await(entry.deferred).pipe(Effect.flatMap(options.dispose), Effect.exit, Effect.flatMap((exit) => Deferred.succeed(completion, exit)), Effect.asVoid), { uninterruptible: "inherit" });
|
|
1276
|
+
return completion;
|
|
1277
|
+
}), { concurrency: "unbounded" });
|
|
1278
|
+
const failures = (yield* Effect.forEach(completions, Deferred.await, { concurrency: "unbounded" })).filter(Exit.isFailure).map(({ cause }) => Cause.squash(cause));
|
|
1279
|
+
if (failures.length > 0) return yield* Effect.fail(new AggregateError(failures, options.failureMessage));
|
|
1647
1280
|
});
|
|
1281
|
+
const timeout = options.timeout;
|
|
1282
|
+
return timeout ? disposal.pipe(Effect.timeoutOrElse({
|
|
1283
|
+
duration: timeout.milliseconds,
|
|
1284
|
+
orElse: () => Effect.fail(timeout.onTimeout())
|
|
1285
|
+
})) : disposal;
|
|
1286
|
+
}
|
|
1287
|
+
function invokeRuntimeControl(runtime, control) {
|
|
1288
|
+
return runtime.pipe(Effect.flatMap((selected) => selected ? control(selected) ?? Effect.succeed(false) : Effect.succeed(false)));
|
|
1648
1289
|
}
|
|
1649
1290
|
//#endregion
|
|
1650
|
-
export {
|
|
1291
|
+
export { deepFreeze as S, loadMergedLocalEnvFile as _, createRivusModelManagementSocketServer as a, validateRivusDeploymentManifest as b, parseRivusModelCliArguments as c, gatewayPackageDirectory as d, gatewayPackageManifestPath as f, loadRivusDaemonConfig as g, loadRivusDeploymentManifest as h, RIVUS_RELEASE_DESCRIPTOR as i, renderRivusModelCliArgumentError as l, gatewayTemplateDirectory as m, disposeRuntimeCacheEntries as n, createRivusModelManagementFailure as o, gatewaySkillsDirectory as p, invokeRuntimeControl as r, createRivusModelManagementWireRequest as s, createRuntimeCache as t, renderRivusModelCliHelp as u, resolveFeishuEndpointCredentials as v, validateRivusProjectSpaceDeployment as x, InvalidRivusProjectSpace as y };
|
|
1292
|
+
|