@rivus/agent 0.16.1 → 0.16.6

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/dist/cli.js CHANGED
@@ -1,1239 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import { A as validateRivusDeploymentManifest, I as resolveFeishuEndpointCredentials, O as loadRivusDeploymentManifest, X as loadMergedLocalEnvFile, g as findTrustedPackageRoot, h as isPathWithin, m as validateTrustedModulePath, p as resolveNodeRivusPluginModulePath, t as runRivusDaemonCli } from "./chunks/rivus-daemon-cli.js";
3
- import { c as renderRivusModelCliHelp, n as createRivusModelManagementWireRequest, o as parseRivusModelCliArguments, s as renderRivusModelCliArgumentError } from "./chunks/rivus-model-management-wire.js";
4
- import { Effect, Either } from "effect";
5
- import { lstat, mkdir, readFile, realpath, rmdir, stat, unlink, writeFile } from "node:fs/promises";
6
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
7
- import { homedir } from "node:os";
8
- import { fileURLToPath, pathToFileURL } from "node:url";
9
- import { createConnection } from "node:net";
10
- //#region src/adapters/deployment/inspection/rivus-home-deployment-inspector.ts
11
- const DEFAULT_BOOTSTRAP_PEERS = Object.freeze(["@earendil-works/pi-coding-agent", "@larksuiteoapi/node-sdk"]);
12
- function createRivusHomeDeploymentInspector(options = {}) {
13
- const resolveModule = options.resolveModule ?? ((specifier) => defaultResolveModule(specifier, options.packageManifestPath));
14
- const resolvePluginModule = options.resolvePluginModule ?? ((request) => resolveNodeRivusPluginModulePath(request, options.packageManifestPath ? { packageManifestPath: options.packageManifestPath } : {}));
15
- return { inspect: (input) => Effect.gen(function* () {
16
- const manifestResult = yield* checkManifest$1(input.home);
17
- const credentials = yield* checkCredentials$1(input.home, manifestResult.manifest, input.env, input.envFilePath);
18
- const modules = yield* checkModules(input.home, manifestResult.manifest, resolveModule, resolvePluginModule);
19
- return {
20
- credentials,
21
- manifest: manifestResult.check,
22
- modules
23
- };
24
- }) };
25
- }
26
- function checkManifest$1(home) {
27
- return loadRivusDeploymentManifest(home.manifestPath).pipe(Effect.map((manifest) => {
28
- validateRivusDeploymentManifest(manifest);
29
- return {
30
- check: {
31
- message: "deployment manifest is valid",
32
- name: "manifest",
33
- status: "pass"
34
- },
35
- manifest
36
- };
37
- }), Effect.catchAll((error) => Effect.succeed({ check: {
38
- message: `deployment manifest is invalid: ${formatError(error)}`,
39
- name: "manifest",
40
- status: "fail"
41
- } })));
42
- }
43
- function checkModules(home, manifest, resolveModule, resolvePluginModule) {
44
- if (!manifest) return Effect.succeed({
45
- message: "modules cannot be checked until manifest is valid",
46
- name: "modules",
47
- status: "fail"
48
- });
49
- const specifiers = [home.bootstrap, ...home.bootstrap === "@rivus/agent/bootstrap/pi-feishu" ? DEFAULT_BOOTSTRAP_PEERS : []];
50
- return Effect.gen(function* () {
51
- const missing = [];
52
- for (const specifier of specifiers) {
53
- const result = yield* toEffect(() => resolveModule(specifier)).pipe(Effect.either);
54
- if (result._tag === "Left") missing.push(`${specifier} (${formatError(result.left)})`);
55
- }
56
- for (const plugin of manifest.plugins) {
57
- const result = yield* toEffect(() => resolvePluginModule({
58
- deploymentRoot: home.directory,
59
- module: plugin.module,
60
- pluginId: plugin.id
61
- })).pipe(Effect.either);
62
- if (result._tag === "Left") missing.push(`${plugin.module} (${formatError(result.left)})`);
63
- }
64
- return missing.length === 0 ? {
65
- message: "Bootstrap, Plugin, Pi, and Feishu modules resolve from the global installation",
66
- name: "modules",
67
- status: "pass"
68
- } : {
69
- message: `global npm modules are unavailable: ${missing.join(", ")}`,
70
- name: "modules",
71
- status: "fail"
72
- };
73
- });
74
- }
75
- function toEffect(read) {
76
- return Effect.suspend(() => {
77
- try {
78
- const value = read();
79
- if (Effect.isEffect(value)) return value;
80
- return Effect.tryPromise({
81
- try: () => Promise.resolve(value),
82
- catch: toError$1
83
- });
84
- } catch (error) {
85
- return Effect.fail(toError$1(error));
86
- }
87
- });
88
- }
89
- function checkCredentials$1(home, manifest, env, envFilePath) {
90
- if (!manifest) return Effect.succeed({
91
- message: "credentials cannot be checked until manifest is valid",
92
- name: "credentials",
93
- status: "fail"
94
- });
95
- return Effect.tryPromise({
96
- try: async () => {
97
- const merged = await loadMergedLocalEnvFile(envFilePath ? resolve(home.directory, envFilePath) : home.envFilePath, env);
98
- for (const endpoint of manifest.endpoints.filter(({ enabled }) => enabled)) resolveFeishuEndpointCredentials(endpoint.credentialRef, merged);
99
- return {
100
- message: "enabled Endpoint credential references resolve",
101
- name: "credentials",
102
- status: "pass"
103
- };
104
- },
105
- catch: toError$1
106
- }).pipe(Effect.catchAll((error) => Effect.succeed({
107
- message: `enabled Endpoint credentials are incomplete: ${formatError(error)}`,
108
- name: "credentials",
109
- status: "fail"
110
- })));
111
- }
112
- function defaultResolveModule(specifier, packageManifestPath) {
113
- if (!packageManifestPath) return Effect.fail(/* @__PURE__ */ new Error("Rivus package manifest path is required for trusted module resolution"));
114
- return Effect.gen(function* () {
115
- const resolvedRealpath = yield* Effect.tryPromise({
116
- try: () => realpath(fileURLToPath(import.meta.resolve(specifier))),
117
- catch: toError$1
118
- });
119
- return yield* validateTrustedModulePath(yield* findTrustedPackageRoot(packageManifestPath), resolvedRealpath, `module ${specifier}`);
120
- });
121
- }
122
- function toError$1(error) {
123
- return error instanceof Error ? error : new Error(String(error));
124
- }
125
- function formatError(error) {
126
- return error instanceof Error ? error.message : String(error);
127
- }
128
- //#endregion
129
- //#region src/platform/home/config/rivus-home-config.ts
130
- /** Loads and validates the operator-owned Home config file. */
131
- function loadRivusHome(directoryPath) {
132
- const directory = resolve(directoryPath);
133
- const configPath = resolve(directory, "config.json");
134
- return Effect.tryPromise({
135
- try: async () => {
136
- const config = parseRivusHomeConfig(JSON.parse(await readFile(configPath, "utf8")));
137
- return {
138
- bootstrap: resolveBootstrap(directory, config.bootstrap),
139
- config,
140
- configPath,
141
- directory,
142
- envFilePath: resolveHomePath(directory, config.envFile, "envFile"),
143
- logsDirectory: resolveHomePath(directory, config.logs, "logs"),
144
- manifestPath: resolveHomePath(directory, config.manifest, "manifest"),
145
- stateDirectory: resolveHomePath(directory, config.state, "state"),
146
- workspaceDirectory: resolveHomePath(directory, config.workspace, "workspace")
147
- };
148
- },
149
- catch: asError$1
150
- });
151
- }
152
- function parseRivusHomeConfig(value) {
153
- const config = record(value, "Rivus Home config");
154
- if (config.version !== 1) throw new Error("Rivus Home config version must be 1");
155
- return {
156
- bootstrap: nonEmptyString(config.bootstrap, "bootstrap"),
157
- envFile: relativePath(config.envFile, "envFile"),
158
- logs: relativePath(config.logs, "logs"),
159
- manifest: relativePath(config.manifest, "manifest"),
160
- state: relativePath(config.state, "state"),
161
- version: 1,
162
- workspace: relativePath(config.workspace, "workspace")
163
- };
164
- }
165
- function resolveBootstrap(directory, value) {
166
- if (value.startsWith("./") || value.startsWith("../")) return resolveHomePath(directory, value, "bootstrap");
167
- if (isAbsolute(value)) throw new Error("Rivus Home bootstrap must be a package specifier or a relative path inside Rivus Home");
168
- return value;
169
- }
170
- function resolveHomePath(directory, value, owner) {
171
- const candidate = resolve(directory, value);
172
- const relation = relative(directory, candidate);
173
- if (relation === "" || !relation.startsWith(`..${sep}`) && relation !== ".." && !isAbsolute(relation)) return candidate;
174
- throw new Error(`Rivus Home ${owner} escapes the Home directory`);
175
- }
176
- function relativePath(value, owner) {
177
- const path = nonEmptyString(value, owner);
178
- if (isAbsolute(path)) throw new Error(`Rivus Home ${owner} must be a relative path`);
179
- return path;
180
- }
181
- function nonEmptyString(value, owner) {
182
- if (typeof value !== "string" || !value.trim()) throw new Error(`Rivus Home ${owner} must be a non-empty string`);
183
- return value;
184
- }
185
- function record(value, owner) {
186
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${owner} must be an object`);
187
- return value;
188
- }
189
- function asError$1(error) {
190
- return error instanceof Error ? error : new Error(String(error));
191
- }
192
- //#endregion
193
- //#region src/platform/home/doctor/rivus-home-doctor.ts
194
- function diagnoseRivusHome(input, options) {
195
- return Effect.gen(function* () {
196
- const checks = [checkNode$1(input.nodeVersion)];
197
- const loaded = yield* options.load(input.directory).pipe(Effect.either);
198
- if (Either.isLeft(loaded)) {
199
- checks.push({
200
- message: `Rivus Home is invalid: ${loaded.left.message}`,
201
- name: "home",
202
- status: "fail"
203
- }, blockedCheck("workspace", "Workspace"), blockedCheck("manifest", "manifest"), blockedCheck("modules", "modules"), blockedCheck("credentials", "credentials"));
204
- return report(input.directory, checks);
205
- }
206
- const home = loaded.right;
207
- checks.push({
208
- message: "config.json is valid and contained in Rivus Home",
209
- name: "home",
210
- status: "pass"
211
- });
212
- checks.push(yield* checkWorkspace(home, options.findMissingWorkspacePaths));
213
- const deployment = yield* options.deploymentInspector.inspect({
214
- env: input.env,
215
- ...input.envFilePath ? { envFilePath: input.envFilePath } : {},
216
- home
217
- });
218
- checks.push(deployment.manifest, deployment.modules, deployment.credentials);
219
- return report(input.directory, checks);
220
- });
221
- }
222
- function checkNode$1(version) {
223
- const [major, minor] = version.split(".").map(Number);
224
- return major === 24 && Number.isInteger(minor) && minor >= 11 ? {
225
- message: `Node.js ${version} satisfies the supported ^24.11 runtime`,
226
- name: "node",
227
- status: "pass"
228
- } : {
229
- message: `Node.js 24.11 or newer on major 24 is required; current version is ${version}`,
230
- name: "node",
231
- status: "fail"
232
- };
233
- }
234
- function checkWorkspace(home, findMissingWorkspacePaths) {
235
- return findMissingWorkspacePaths(home).pipe(Effect.map((missing) => missing.length === 0 ? {
236
- message: "Workspace instructions, Memory, Skills, and work directories are present",
237
- name: "workspace",
238
- status: "pass"
239
- } : {
240
- message: `Workspace paths are missing: ${missing.join(", ")}`,
241
- name: "workspace",
242
- status: "fail"
243
- }));
244
- }
245
- function blockedCheck(name, owner) {
246
- return {
247
- message: `${owner} cannot be checked until config.json is valid`,
248
- name,
249
- status: "fail"
250
- };
251
- }
252
- function report(directory, checks) {
253
- return {
254
- checks,
255
- directory,
256
- ready: checks.every(({ status }) => status === "pass")
257
- };
258
- }
259
- //#endregion
260
- //#region src/platform/home/setup/rivus-home-layout.ts
261
- const HOME_DIRECTORIES = Object.freeze([
262
- "logs",
263
- "plugins",
264
- "state",
265
- "workspace/memory",
266
- "workspace/skills",
267
- "workspace/work/artifacts",
268
- "workspace/work/drafts",
269
- "workspace/work/inbox",
270
- "workspace/work/tmp"
271
- ]);
272
- function createRivusHomeLayout() {
273
- return {
274
- directories: HOME_DIRECTORIES,
275
- files: /* @__PURE__ */ new Map([
276
- [".env.example", environmentTemplate$1()],
277
- [".gitignore", ".DS_Store\n.env\nlogs/\nstate/\nworkspace/work/tmp/\n"],
278
- ["config.json", homeConfig()],
279
- ["rivus.config.json", deploymentManifest$1()],
280
- ["workspace/AGENTS.md", agentsTemplate()],
281
- ["workspace/IDENTITY.md", "# Identity\n\nName: Rivus\n\nRole: Personal agent\n"],
282
- ["workspace/MEMORY.md", "# Curated Memory\n\nStore confirmed, durable facts and decisions here.\n"],
283
- ["workspace/SOUL.md", "# Character\n\nBe direct, thoughtful, and evidence-led.\n"],
284
- ["workspace/USER.md", "# User\n\nRecord stable user preferences here.\n"],
285
- ["plugins/.gitkeep", ""],
286
- ["workspace/memory/.gitkeep", ""],
287
- ["workspace/skills/.gitkeep", ""],
288
- ["workspace/work/artifacts/.gitkeep", ""],
289
- ["workspace/work/drafts/.gitkeep", ""],
290
- ["workspace/work/inbox/.gitkeep", ""]
291
- ])
292
- };
293
- }
294
- function homeConfig() {
295
- return `${JSON.stringify({
296
- bootstrap: "@rivus/agent/bootstrap/pi-feishu",
297
- envFile: ".env",
298
- logs: "logs",
299
- manifest: "rivus.config.json",
300
- state: "state",
301
- version: 1,
302
- workspace: "workspace"
303
- }, null, 2)}\n`;
304
- }
305
- function deploymentManifest$1() {
306
- return `${JSON.stringify({
307
- agents: [{
308
- agentId: "personal",
309
- endpointIds: ["personal-feishu"],
310
- memory: {
311
- scopes: ["agent-private"],
312
- tool: true
313
- },
314
- pluginId: "rivus-starter",
315
- profileId: "agent-a",
316
- projectSpaceId: "personal-home",
317
- runtimeTools: { allow: [
318
- "read",
319
- "bash",
320
- "edit",
321
- "write",
322
- "grep",
323
- "find",
324
- "ls"
325
- ] },
326
- skills: { allow: [] },
327
- tools: { allow: ["rivus-starter/current-weather"] }
328
- }],
329
- defaultAgentId: "personal",
330
- defaultEndpointId: "personal-feishu",
331
- endpoints: [{
332
- agentId: "personal",
333
- baseUrl: "https://open.feishu.cn",
334
- cardStreamLeaseMs: 51e4,
335
- credentialRef: "env:RIVUS_FEISHU",
336
- enabled: true,
337
- experimental: { cotMessages: false },
338
- groupPolicy: "mention-only",
339
- id: "personal-feishu",
340
- progressDisplay: "collapsed",
341
- required: true,
342
- sessionNamespace: "rivus-home-v1",
343
- streamMinIntervalMs: 200
344
- }],
345
- plugins: [{
346
- id: "rivus-starter",
347
- module: "@rivus/agent/plugin/starter",
348
- required: true
349
- }],
350
- projectSpaces: [{
351
- id: "personal-home",
352
- root: "workspace",
353
- skills: { sources: ["skills"] },
354
- workingDirectory: "."
355
- }]
356
- }, null, 2)}\n`;
357
- }
358
- function environmentTemplate$1() {
359
- return [
360
- "# Copy this file to .env and keep the real values untracked.",
361
- "RIVUS_FEISHU_APP_ID=",
362
- "RIVUS_FEISHU_APP_SECRET=",
363
- "PI_MODEL=",
364
- "PI_API_KEY=",
365
- "# PI_BASE_URL=",
366
- "RIVUS_WEATHER_DEFAULT_LOCATION=上海",
367
- ""
368
- ].join("\n");
369
- }
370
- function agentsTemplate() {
371
- return [
372
- "# Personal Workspace",
373
- "",
374
- "Use this directory as the default home for personal work.",
375
- "",
376
- "- Read `SOUL.md` and `IDENTITY.md` when identity or tone matters.",
377
- "- Read `USER.md` when stable user preferences affect the task.",
378
- "- Search `MEMORY.md` and dated files under `memory/` when prior facts or decisions matter.",
379
- "- Read the selected `skills/<name>/SKILL.md` before following a Workspace Skill.",
380
- "- Put incoming material in `work/inbox/`, drafts in `work/drafts/`, and durable general outputs in `work/artifacts/`.",
381
- "- Put disposable files in `work/tmp/`.",
382
- "- Store only confirmed durable facts in Memory; keep credentials and raw private transcripts out of tracked files.",
383
- ""
384
- ].join("\n");
385
- }
386
- //#endregion
387
- //#region src/platform/home/setup/rivus-home-initializer.ts
388
- function initializeRivusHome(directoryPath) {
389
- const directory = resolve(directoryPath);
390
- const { directories, files } = createRivusHomeLayout();
391
- const paths = [...files.keys()].sort();
392
- return Effect.tryPromise({
393
- try: async () => {
394
- for (const path of [...directories].sort()) await mkdir(join(directory, path), { recursive: true });
395
- for (const path of paths) {
396
- const destination = join(directory, path);
397
- await mkdir(dirname(destination), { recursive: true });
398
- await writeFile(destination, files.get(path), {
399
- encoding: "utf8",
400
- flag: "wx"
401
- });
402
- }
403
- return {
404
- directory,
405
- files: paths
406
- };
407
- },
408
- catch: (error) => error instanceof Error ? error : new Error(String(error))
409
- });
410
- }
411
- //#endregion
412
- //#region src/platform/home/workspace/rivus-home-workspace.ts
413
- function findMissingRivusHomeWorkspacePaths(home) {
414
- const required = [
415
- home.workspaceDirectory,
416
- resolve(home.workspaceDirectory, "AGENTS.md"),
417
- resolve(home.workspaceDirectory, "MEMORY.md"),
418
- resolve(home.workspaceDirectory, "memory"),
419
- resolve(home.workspaceDirectory, "skills"),
420
- resolve(home.workspaceDirectory, "work")
421
- ];
422
- return Effect.tryPromise({
423
- try: async () => {
424
- const missing = [];
425
- for (const path of required) try {
426
- await stat(path);
427
- } catch (error) {
428
- if (error instanceof Error && "code" in error && error.code === "ENOENT") missing.push(path);
429
- else throw error;
430
- }
431
- return missing;
432
- },
433
- catch: (error) => error instanceof Error ? error : new Error(String(error))
434
- });
435
- }
436
- //#endregion
437
- //#region src/platform/home/node/node-rivus-home.ts
438
- function createNodeRivusHome(options) {
439
- return {
440
- diagnose: (input) => diagnoseRivusHome(input, {
441
- deploymentInspector: options.deploymentInspector,
442
- findMissingWorkspacePaths: findMissingRivusHomeWorkspacePaths,
443
- load: loadRivusHome
444
- }),
445
- load: loadRivusHome,
446
- setup: initializeRivusHome
447
- };
448
- }
449
- //#endregion
450
- //#region src/adapters/cli/command/rivus-cli-protocol.ts
451
- const RIVUS_CLI_USAGE = `Usage:
452
- rivus setup [directory]
453
- rivus start
454
- rivus status
455
- rivus check-config
456
- rivus init [directory]
457
- rivus doctor [directory] [--env-file <path>]
458
- rivus model <status|set|rollback> ...
459
- rivus --bootstrap <module> [--manifest <rivus.config.json>] [options]
460
-
461
- Commands:
462
- setup Create Rivus Home (default ~/.rivus-agent) without overwriting files
463
- start Start the Rivus Home deployment in the foreground
464
- status Print Rivus Home deployment status without activating Endpoints
465
- check-config
466
- Validate and print the redacted Rivus Home manifest
467
- init Create a standalone local Rivus project without overwriting files
468
- doctor Check Rivus Home by default, or an explicit standalone project directory
469
- model Query or change the managed default model through the current Home
470
-
471
- Run rivus --help for the complete daemon option list.
472
- `;
473
- function renderRivusCliError(error) {
474
- return `${error instanceof Error ? error.message : String(error)}\n`;
475
- }
476
- function renderRivusCliUnknownCommand(command) {
477
- return `Unknown command: ${command}\n\n${RIVUS_CLI_USAGE}`;
478
- }
479
- function renderRivusDirectoryCommandUsage(command) {
480
- return `Usage: rivus ${command} [directory]\n`;
481
- }
482
- function renderRivusHomeCommandUsage(command) {
483
- return `Usage: rivus ${command}\n`;
484
- }
485
- function renderRivusDoctorUsage() {
486
- return "Usage: rivus doctor [directory] [--env-file <path>]\n";
487
- }
488
- function renderRivusDoctorArgumentError(error) {
489
- return `${error}\n${renderRivusDoctorUsage()}`;
490
- }
491
- function renderRivusSetupSuccess(directory) {
492
- return `Initialized Rivus Home in ${directory}\n\nNext:\n cp ${shellQuote(`${directory}/.env.example`)} ${shellQuote(`${directory}/.env`)}\n rivus doctor\n rivus start\n`;
493
- }
494
- function renderRivusProjectInitializationSuccess(directory) {
495
- return `Initialized Rivus project in ${directory}\n\nNext:\n cd ${shellQuote(directory)}\n npm install\n cp .env.example .env.local\n npm run doctor\n npm start\n`;
496
- }
497
- function* renderRivusDoctorReport(report, owner) {
498
- yield `Rivus doctor: ${report.directory}\n`;
499
- for (const check of report.checks) yield `${check.status === "pass" ? "PASS" : "FAIL"} ${check.name}: ${check.message}\n`;
500
- yield report.ready ? `Rivus ${owner} is ready\n` : `Rivus ${owner} is not ready\n`;
501
- }
502
- function parseRivusDirectoryArguments(argv) {
503
- if (argv.includes("--help") || argv.includes("-h")) return { help: true };
504
- if (argv.length > 1 || argv[0]?.startsWith("-")) return { error: "invalid directory arguments" };
505
- return argv[0] === void 0 ? {} : { directory: argv[0] };
506
- }
507
- function hasRivusHomeCommandArguments(argv) {
508
- return argv.length > 0;
509
- }
510
- function parseRivusDoctorArguments(argv) {
511
- let directory;
512
- let envFilePath;
513
- for (let index = 0; index < argv.length; index += 1) {
514
- const argument = argv[index];
515
- if (argument === "--help" || argument === "-h") return { help: true };
516
- if (argument === "--env-file") {
517
- const value = argv[index + 1];
518
- if (!value) return { error: "--env-file requires a path" };
519
- envFilePath = value;
520
- index += 1;
521
- continue;
522
- }
523
- if (argument.startsWith("--env-file=")) {
524
- envFilePath = argument.slice(11);
525
- if (!envFilePath) return { error: "--env-file requires a path" };
526
- continue;
527
- }
528
- if (argument.startsWith("-")) return { error: `Unknown doctor option: ${argument}` };
529
- if (directory !== void 0) return { error: "doctor accepts at most one directory" };
530
- directory = argument;
531
- }
532
- return {
533
- ...directory !== void 0 ? { directory } : {},
534
- ...envFilePath !== void 0 ? { envFilePath } : {}
535
- };
536
- }
537
- function shellQuote(value) {
538
- return `'${value.replaceAll("'", "'\\''")}'`;
539
- }
540
- //#endregion
541
- //#region src/adapters/cli/model/rivus-model-management-socket-client.ts
542
- const MAX_FRAME_BYTES = 64 * 1024;
543
- const DEFAULT_TIMEOUT_MS = 1e4;
544
- var RivusModelManagementTransportError = class extends Error {
545
- code;
546
- constructor(code, message, options) {
547
- super(message, options);
548
- this.name = "RivusModelManagementTransportError";
549
- this.code = code;
550
- }
551
- };
552
- function createRivusModelManagementSocketClient(options) {
553
- if (!isAbsolute(options.socketPath)) throw new Error("model socket path must be absolute");
554
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
555
- return { execute: (command) => request(options.socketPath, options.env, command, timeoutMs) };
556
- }
557
- async function request(socketPath, env, command, timeoutMs) {
558
- const payload = createRivusModelManagementWireRequest(command, env);
559
- return new Promise((resolve, reject) => {
560
- const socket = createConnection(socketPath);
561
- let response = "";
562
- let settled = false;
563
- const finish = (callback) => {
564
- if (settled) return;
565
- settled = true;
566
- callback();
567
- };
568
- socket.setEncoding("utf8");
569
- socket.setTimeout(timeoutMs, () => {
570
- finish(() => reject(new RivusModelManagementTransportError("timeout", "model management socket timed out")));
571
- socket.destroy();
572
- });
573
- socket.on("connect", () => socket.write(`${JSON.stringify(payload)}\n`));
574
- socket.on("data", (chunk) => {
575
- response += chunk;
576
- if (Buffer.byteLength(response, "utf8") > MAX_FRAME_BYTES) {
577
- finish(() => reject(new RivusModelManagementTransportError("invalid_response", "model response is too large")));
578
- socket.destroy();
579
- }
580
- });
581
- socket.on("error", (error) => {
582
- finish(() => reject(new RivusModelManagementTransportError("socket_error", "model management socket is unavailable", { cause: error })));
583
- });
584
- socket.on("end", () => {
585
- const line = response.split("\n", 1)[0]?.trim();
586
- if (!line) {
587
- finish(() => reject(new RivusModelManagementTransportError("invalid_response", "model response was empty")));
588
- return;
589
- }
590
- try {
591
- const parsed = JSON.parse(line);
592
- if (!isResponse(parsed)) throw new Error("model response must be a JSON object");
593
- finish(() => resolve(parsed));
594
- } catch (error) {
595
- finish(() => reject(new RivusModelManagementTransportError("invalid_response", "model response was not valid JSON", { cause: error })));
596
- }
597
- });
598
- });
599
- }
600
- function isResponse(value) {
601
- return typeof value === "object" && value !== null && !Array.isArray(value);
602
- }
603
- //#endregion
604
- //#region src/adapters/cli/model/rivus-model-socket.ts
605
- const RIVUS_MODEL_SOCKET_ENV = "RIVUS_MODEL_SOCKET";
606
- const RIVUS_MODEL_MANAGEMENT_ENABLED_ENV = "RIVUS_MODEL_MANAGEMENT_ENABLED";
607
- const RIVUS_MODEL_SOCKET_RELATIVE_PATH = "state/model-management/control.sock";
608
- function resolveRivusModelSocketPath(options) {
609
- const configured = optional(options.env[RIVUS_MODEL_SOCKET_ENV]);
610
- if (configured) {
611
- if (!isAbsolute(configured)) throw new Error(`${RIVUS_MODEL_SOCKET_ENV} must be an absolute path`);
612
- return resolve(configured);
613
- }
614
- const configuredHome = optional(options.env.RIVUS_HOME);
615
- return join(configuredHome ? resolveAbsoluteHome(configuredHome) : resolve(options.homeDirectory, ".rivus-agent"), RIVUS_MODEL_SOCKET_RELATIVE_PATH);
616
- }
617
- function isRivusModelManagementEnabled(env) {
618
- const value = optional(env[RIVUS_MODEL_MANAGEMENT_ENABLED_ENV]);
619
- return value === "1" || value === "true";
620
- }
621
- function resolveAbsoluteHome(value) {
622
- if (!isAbsolute(value)) throw new Error("RIVUS_HOME must be an absolute path");
623
- return resolve(value);
624
- }
625
- function optional(value) {
626
- return value?.trim() || void 0;
627
- }
628
- //#endregion
629
- //#region src/adapters/deployment/inspection/rivus-project-doctor.ts
630
- const REQUIRED_FILES = Object.freeze([
631
- "package.json",
632
- "rivus.bootstrap.ts",
633
- "rivus.config.json"
634
- ]);
635
- const REQUIRED_DEPENDENCIES = Object.freeze([
636
- "@rivus/agent",
637
- "@earendil-works/pi-coding-agent",
638
- "@larksuiteoapi/node-sdk"
639
- ]);
640
- function diagnoseRivusProject(options) {
641
- return Effect.gen(function* () {
642
- const directory = resolve(options.directory);
643
- const checks = [checkNode(options.nodeVersion)];
644
- checks.push(yield* Effect.tryPromise({
645
- try: () => checkFiles(directory),
646
- catch: toError
647
- }));
648
- checks.push(yield* Effect.tryPromise({
649
- try: () => checkDependencies(directory),
650
- catch: toError
651
- }));
652
- const manifestResult = yield* checkManifest(directory);
653
- checks.push(manifestResult.check);
654
- const envFilePath = resolve(directory, options.envFilePath ?? ".env.local");
655
- checks.push(yield* Effect.tryPromise({
656
- try: () => checkCredentials(manifestResult.manifest, envFilePath, options.env),
657
- catch: toError
658
- }));
659
- return Object.freeze({
660
- checks: Object.freeze(checks),
661
- directory,
662
- ready: checks.every(({ status }) => status === "pass")
663
- });
664
- });
665
- }
666
- function checkNode(version) {
667
- const [major, minor] = version.split(".").map(Number);
668
- if (major === 24 && Number.isInteger(minor) && minor >= 11) return {
669
- message: `Node.js ${version} satisfies the supported ^24.11 runtime`,
670
- name: "node",
671
- status: "pass"
672
- };
673
- return {
674
- message: `Node.js 24.11 or newer on major 24 is required; current version is ${version}`,
675
- name: "node",
676
- status: "fail"
677
- };
678
- }
679
- async function checkFiles(directory) {
680
- const missing = await missingRegularFiles(directory, REQUIRED_FILES);
681
- return missing.length === 0 ? {
682
- message: "required project files are present",
683
- name: "files",
684
- status: "pass"
685
- } : {
686
- message: `required project files are missing: ${missing.join(", ")}`,
687
- name: "files",
688
- status: "fail"
689
- };
690
- }
691
- async function checkDependencies(directory) {
692
- const packageFiles = REQUIRED_DEPENDENCIES.map((name) => join("node_modules", ...name.split("/"), "package.json"));
693
- const missingIndexes = new Set((await missingRegularFiles(directory, packageFiles)).map((path) => packageFiles.indexOf(path)));
694
- const missing = REQUIRED_DEPENDENCIES.filter((_, index) => missingIndexes.has(index));
695
- return missing.length === 0 ? {
696
- message: "Rivus, Pi, and Feishu dependencies are installed",
697
- name: "dependencies",
698
- status: "pass"
699
- } : {
700
- message: `run npm install; missing local dependencies: ${missing.join(", ")}`,
701
- name: "dependencies",
702
- status: "fail"
703
- };
704
- }
705
- function checkManifest(directory) {
706
- return loadRivusDeploymentManifest(join(directory, "rivus.config.json")).pipe(Effect.flatMap((manifest) => Effect.gen(function* () {
707
- validateRivusDeploymentManifest(manifest);
708
- const missingModules = [];
709
- for (const plugin of manifest.plugins) {
710
- const result = yield* resolveNodeRivusPluginModulePath({
711
- deploymentRoot: directory,
712
- module: plugin.module,
713
- pluginId: plugin.id
714
- }).pipe(Effect.either);
715
- if (Either.isLeft(result)) missingModules.push(plugin.module);
716
- }
717
- if (missingModules.length > 0) return { check: {
718
- message: `manifest Plugin modules are missing: ${missingModules.join(", ")}`,
719
- name: "manifest",
720
- status: "fail"
721
- } };
722
- return {
723
- check: {
724
- message: "deployment manifest is valid",
725
- name: "manifest",
726
- status: "pass"
727
- },
728
- manifest
729
- };
730
- })), Effect.catchAll((error) => Effect.succeed({ check: {
731
- message: `deployment manifest is invalid: ${error.message}`,
732
- name: "manifest",
733
- status: "fail"
734
- } })));
735
- }
736
- async function checkCredentials(manifest, envFilePath, env) {
737
- let mergedEnv;
738
- try {
739
- mergedEnv = await loadMergedLocalEnvFile(envFilePath, env);
740
- } catch (error) {
741
- return {
742
- message: `required env file is unavailable at ${envFilePath}: ${error instanceof Error ? error.message : String(error)}`,
743
- name: "credentials",
744
- status: "fail"
745
- };
746
- }
747
- if (!manifest) return {
748
- message: "enabled Endpoint credentials cannot be checked until the deployment manifest is valid",
749
- name: "credentials",
750
- status: "fail"
751
- };
752
- try {
753
- for (const endpoint of manifest.endpoints.filter(({ enabled }) => enabled)) resolveFeishuEndpointCredentials(endpoint.credentialRef, mergedEnv);
754
- return {
755
- message: "enabled Endpoint credential references resolve",
756
- name: "credentials",
757
- status: "pass"
758
- };
759
- } catch (error) {
760
- return {
761
- message: `enabled Endpoint credentials are incomplete: ${error instanceof Error ? error.message : String(error)}`,
762
- name: "credentials",
763
- status: "fail"
764
- };
765
- }
766
- }
767
- async function missingRegularFiles(directory, paths) {
768
- const missing = [];
769
- for (const path of paths) try {
770
- if (!(await stat(resolve(directory, path))).isFile()) missing.push(path);
771
- } catch (error) {
772
- if (!isMissingPath$1(error)) throw error;
773
- missing.push(path);
774
- }
775
- return missing;
776
- }
777
- function isMissingPath$1(error) {
778
- return error instanceof Error && "code" in error && error.code === "ENOENT";
779
- }
780
- function toError(error) {
781
- return error instanceof Error ? error : new Error(String(error));
782
- }
783
- //#endregion
784
- //#region src/platform/project/setup/rivus-project-initializer.ts
785
- const TEMPLATE_FILES = Object.freeze({
786
- "current-weather.mjs": "current-weather.mjs",
787
- "https-response-reader.mjs": "https-response-reader.mjs",
788
- "rivus-agents.plugin.mjs": "rivus-starter.plugin.mjs",
789
- "rivus.bootstrap.ts": "pi-feishu-deployment.bootstrap.ts"
790
- });
791
- async function initializeRivusProject(options) {
792
- const directory = resolve(options.directory);
793
- const manifest = await readPackageManifest(options.packageManifestPath);
794
- const files = /* @__PURE__ */ new Map([
795
- [".env.example", environmentTemplate()],
796
- [".gitignore", ".env.local\n.rivus/\nnode_modules/\n"],
797
- ["deploy/launchd/com.rivus.agent.plist", launchdService(directory, options.nodeExecutable)],
798
- ["deploy/systemd/rivus.service", systemdService(directory, options.nodeExecutable)],
799
- ["package.json", projectPackageJson(directory, manifest)],
800
- ["rivus.config.json", deploymentManifest()]
801
- ]);
802
- for (const [target, source] of Object.entries(TEMPLATE_FILES)) files.set(target, await readFile(join(options.templateDirectory, source), "utf8"));
803
- const paths = [...files.keys()].sort();
804
- await assertSafeProjectAncestors(directory, paths);
805
- const conflicts = await findConflicts(directory, paths);
806
- if (conflicts.length > 0) throw new Error(`Rivus project initialization refused; refusing to overwrite: ${conflicts.join(", ")}`);
807
- const createdDirectories = [];
808
- const createdFiles = [];
809
- const writeProjectFile = options.writeProjectFile ?? writeExclusiveProjectFile;
810
- try {
811
- for (const path of paths) {
812
- const destination = join(directory, path);
813
- await ensureDirectory(dirname(destination), directory, createdDirectories);
814
- await assertSafeProjectAncestors(directory, [path]);
815
- await writeProjectFile(destination, files.get(path));
816
- createdFiles.push(destination);
817
- }
818
- } catch (error) {
819
- const rollbackErrors = await rollbackCreatedPaths(createdFiles, createdDirectories);
820
- if (rollbackErrors.length > 0) throw new AggregateError([error, ...rollbackErrors], "Rivus project initialization failed and rollback was incomplete");
821
- throw error;
822
- }
823
- return {
824
- directory,
825
- files: Object.freeze(paths)
826
- };
827
- }
828
- async function writeExclusiveProjectFile(path, contents) {
829
- await writeFile(path, contents, {
830
- encoding: "utf8",
831
- flag: "wx"
832
- });
833
- }
834
- async function assertSafeProjectAncestors(directory, paths) {
835
- const rootState = await lstatOrUndefined(directory);
836
- if (rootState?.isSymbolicLink()) throw new Error("Rivus project initialization refused; symbolic link ancestor: .");
837
- if (rootState && !rootState.isDirectory()) throw new Error("Rivus project initialization refused; project path is not a directory");
838
- if (!rootState) return;
839
- for (const path of paths) {
840
- let current = directory;
841
- for (const segment of path.split("/").slice(0, -1)) {
842
- current = join(current, segment);
843
- const state = await lstatOrUndefined(current);
844
- if (!state) break;
845
- if (state.isSymbolicLink()) throw new Error(`Rivus project initialization refused; symbolic link ancestor: ${relative(directory, current)}`);
846
- if (!state.isDirectory()) throw new Error(`Rivus project initialization refused; non-directory ancestor: ${relative(directory, current)}`);
847
- }
848
- }
849
- }
850
- async function ensureDirectory(path, projectRoot, createdDirectories) {
851
- try {
852
- await mkdir(path);
853
- createdDirectories.push(path);
854
- } catch (error) {
855
- if (isMissingPath(error)) {
856
- const parent = dirname(path);
857
- if (parent === path) throw error;
858
- await ensureDirectory(parent, projectRoot, createdDirectories);
859
- await ensureDirectory(path, projectRoot, createdDirectories);
860
- return;
861
- }
862
- if (!isAlreadyExists(error)) throw error;
863
- const state = await lstat(path);
864
- if (state.isSymbolicLink()) {
865
- if (isPathWithin(projectRoot, path)) throw new Error(`Rivus project initialization refused; symbolic link ancestor: ${relative(projectRoot, path) || "."}`);
866
- if ((await stat(path)).isDirectory()) return;
867
- }
868
- if (!state.isDirectory()) throw new Error(`Rivus project initialization refused; non-directory ancestor: ${path}`);
869
- }
870
- }
871
- async function rollbackCreatedPaths(files, directories) {
872
- const errors = [];
873
- for (const path of files.reverse()) try {
874
- await unlink(path);
875
- } catch (error) {
876
- if (!isMissingPath(error)) errors.push(asError(error));
877
- }
878
- for (const path of directories.reverse()) try {
879
- await rmdir(path);
880
- } catch (error) {
881
- if (!isMissingPath(error) && !isDirectoryNotEmpty(error)) errors.push(asError(error));
882
- }
883
- return errors;
884
- }
885
- async function lstatOrUndefined(path) {
886
- try {
887
- return await lstat(path);
888
- } catch (error) {
889
- if (isMissingPath(error)) return void 0;
890
- throw error;
891
- }
892
- }
893
- function isAlreadyExists(error) {
894
- return error instanceof Error && "code" in error && error.code === "EEXIST";
895
- }
896
- function isDirectoryNotEmpty(error) {
897
- return error instanceof Error && "code" in error && error.code === "ENOTEMPTY";
898
- }
899
- function asError(error) {
900
- return error instanceof Error ? error : new Error(String(error));
901
- }
902
- async function readPackageManifest(path) {
903
- const manifest = JSON.parse(await readFile(path, "utf8"));
904
- if (manifest.name !== "@rivus/agent" || typeof manifest.version !== "string") throw new Error("Rivus package manifest is missing its release identity");
905
- return manifest;
906
- }
907
- async function findConflicts(directory, paths) {
908
- const conflicts = [];
909
- for (const path of paths) try {
910
- await lstat(join(directory, path));
911
- conflicts.push(path);
912
- } catch (error) {
913
- if (!isMissingPath(error)) throw error;
914
- }
915
- return conflicts;
916
- }
917
- function isMissingPath(error) {
918
- return error instanceof Error && "code" in error && error.code === "ENOENT";
919
- }
920
- function projectPackageJson(directory, manifest) {
921
- const pi = manifest.dependencies?.["@earendil-works/pi-coding-agent"];
922
- const lark = manifest.dependencies?.["@larksuiteoapi/node-sdk"];
923
- const effect = manifest.dependencies?.effect;
924
- if (!pi || !lark || !effect) throw new Error("Rivus package manifest is missing its Effect, Pi, or Feishu dependency range");
925
- const projectName = sanitizePackageName(basename(directory));
926
- return `${JSON.stringify({
927
- name: projectName,
928
- private: true,
929
- type: "module",
930
- scripts: {
931
- "check-config": "rivus --manifest ./rivus.config.json --check-config",
932
- doctor: "rivus doctor .",
933
- start: "rivus --env-file .env.local --bootstrap ./rivus.bootstrap.ts --manifest ./rivus.config.json"
934
- },
935
- dependencies: {
936
- "@earendil-works/pi-coding-agent": pi,
937
- "@larksuiteoapi/node-sdk": lark,
938
- "@rivus/agent": `^${manifest.version}`,
939
- effect
940
- }
941
- }, null, 2)}\n`;
942
- }
943
- function sanitizePackageName(value) {
944
- return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "") || "rivus-app";
945
- }
946
- function deploymentManifest() {
947
- return `${JSON.stringify({
948
- plugins: [{
949
- id: "rivus-starter",
950
- module: "./rivus-agents.plugin.mjs",
951
- required: true
952
- }],
953
- agents: [{
954
- agentId: "agent-a",
955
- endpointIds: ["feishu-agent-a"],
956
- pluginId: "rivus-starter",
957
- profileId: "agent-a",
958
- skills: { allow: [] },
959
- tools: { allow: ["rivus-starter/current-weather"] }
960
- }],
961
- defaultAgentId: "agent-a",
962
- defaultEndpointId: "feishu-agent-a",
963
- endpoints: [{
964
- agentId: "agent-a",
965
- baseUrl: "https://open.feishu.cn",
966
- cardStreamLeaseMs: 51e4,
967
- credentialRef: "env:RIVUS_FEISHU",
968
- enabled: true,
969
- experimental: { cotMessages: false },
970
- groupPolicy: "mention-only",
971
- id: "feishu-agent-a",
972
- progressDisplay: "collapsed",
973
- required: true,
974
- sessionNamespace: "rivus-starter",
975
- streamMinIntervalMs: 200
976
- }]
977
- }, null, 2)}\n`;
978
- }
979
- function environmentTemplate() {
980
- return [
981
- "# Copy this file to .env.local and keep the real values untracked.",
982
- "RIVUS_FEISHU_APP_ID=",
983
- "RIVUS_FEISHU_APP_SECRET=",
984
- "PI_MODEL=",
985
- "PI_API_KEY=",
986
- "# PI_BASE_URL=",
987
- "RIVUS_WEATHER_DEFAULT_LOCATION=上海",
988
- "# LANGFUSE_BASE_URL=https://jp.cloud.langfuse.com",
989
- "# LANGFUSE_PUBLIC_KEY=",
990
- "# LANGFUSE_SECRET_KEY=",
991
- "# RIVUS_TELEMETRY_CONTENT=redacted",
992
- ""
993
- ].join("\n");
994
- }
995
- function systemdService(directory, nodeExecutable) {
996
- const cli = join(directory, "node_modules", "@rivus", "agent", "dist", "cli.js");
997
- return `[Unit]\nDescription=Rivus Agent\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nWorkingDirectory=${systemdPath(directory)}\nExecStart=${systemdQuote(nodeExecutable)} ${systemdQuote(cli)} --env-file .env.local --bootstrap ./rivus.bootstrap.ts --manifest ./rivus.config.json\nRestart=on-failure\nRestartSec=5\nEnvironment=NODE_ENV=production\n\n[Install]\nWantedBy=default.target\n`;
998
- }
999
- function systemdPath(value) {
1000
- return value.replaceAll("%", "%%");
1001
- }
1002
- function systemdQuote(value) {
1003
- return `"${value.replace(/%/g, "%%").replace(/\$/g, () => "$$").replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
1004
- }
1005
- function launchdService(directory, nodeExecutable) {
1006
- return `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n <key>Label</key>\n <string>com.rivus.agent</string>\n <key>ProgramArguments</key>\n <array>\n${[
1007
- nodeExecutable,
1008
- join(directory, "node_modules", "@rivus", "agent", "dist", "cli.js"),
1009
- "--env-file",
1010
- ".env.local",
1011
- "--bootstrap",
1012
- "./rivus.bootstrap.ts",
1013
- "--manifest",
1014
- "./rivus.config.json"
1015
- ].map((value) => ` <string>${xmlEscape(value)}</string>`).join("\n")}\n </array>\n <key>WorkingDirectory</key>\n <string>${xmlEscape(directory)}</string>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n <key>ThrottleInterval</key>\n <integer>5</integer>\n</dict>\n</plist>\n`;
1016
- }
1017
- function xmlEscape(value) {
1018
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1019
- }
1020
- //#endregion
1021
- //#region src/bootstrap/cli/rivus-cli.ts
1022
- function runRivusCli(options) {
1023
- const command = options.argv[0];
1024
- if (command === "setup") return runSetupCommand(options);
1025
- if (command === "start") return runHomeDaemonCommand(options, "start", []);
1026
- if (command === "status") return runHomeDaemonCommand(options, "status", ["--status"]);
1027
- if (command === "check-config") return runHomeDaemonCommand(options, "check-config", ["--check-config"]);
1028
- if (command === "init") return runInitCommand(options);
1029
- if (command === "doctor") return runDoctorCommand(options);
1030
- if (command === "model") return runModelCommand(options);
1031
- if (command && !command.startsWith("-")) return Effect.sync(() => {
1032
- options.stderr.write(renderRivusCliUnknownCommand(command));
1033
- return 1;
1034
- });
1035
- return runRivusDaemonCli(options);
1036
- }
1037
- function runModelCommand(options) {
1038
- const parsed = parseRivusModelCliArguments(options.argv.slice(1));
1039
- if ("help" in parsed) return Effect.sync(() => {
1040
- options.stdout.write(renderRivusModelCliHelp());
1041
- return 0;
1042
- });
1043
- if ("error" in parsed) return Effect.sync(() => {
1044
- options.stderr.write(renderRivusModelCliArgumentError(parsed.error));
1045
- return 1;
1046
- });
1047
- if (parsed.operation !== "status" && !isRivusModelManagementEnabled(options.env)) return Effect.sync(() => {
1048
- writeModelResponse(options.stdout, {
1049
- error: {
1050
- code: "management_disabled",
1051
- message: "model management is not enabled for this Home"
1052
- },
1053
- schemaVersion: 1,
1054
- status: "failed"
1055
- });
1056
- return 1;
1057
- });
1058
- const client = options.modelManagementClient ?? createRivusModelManagementSocketClient({
1059
- env: options.env,
1060
- socketPath: resolveRivusModelSocketPath({
1061
- env: options.env,
1062
- homeDirectory: options.homeDirectory
1063
- })
1064
- });
1065
- return Effect.tryPromise({
1066
- try: () => client.execute(parsed),
1067
- catch: (error) => error
1068
- }).pipe(Effect.tap((response) => Effect.sync(() => writeModelResponse(options.stdout, response))), Effect.map((response) => modelManagementExitCode(response)), Effect.catchAll((error) => Effect.sync(() => {
1069
- writeModelResponse(options.stdout, modelTransportFailure(error));
1070
- return 1;
1071
- })));
1072
- }
1073
- function modelManagementExitCode(response) {
1074
- if (response.status !== "failed") return 0;
1075
- if (typeof response.requestId === "string" && response.requestId.trim()) return 0;
1076
- const request = response.request;
1077
- if (typeof request !== "object" || request === null || Array.isArray(request)) return 1;
1078
- const requestId = request.requestId;
1079
- return typeof requestId === "string" && requestId.trim() ? 0 : 1;
1080
- }
1081
- function writeModelResponse(stdout, response) {
1082
- stdout.write(`${JSON.stringify(response)}\n`);
1083
- }
1084
- function modelTransportFailure(error) {
1085
- const code = error instanceof RivusModelManagementTransportError ? error.code : "socket_error";
1086
- return {
1087
- error: {
1088
- code,
1089
- message: code === "timeout" ? "model management service timed out" : code === "invalid_response" ? "model management returned an invalid response" : "model management service is unavailable"
1090
- },
1091
- schemaVersion: 1,
1092
- status: "failed"
1093
- };
1094
- }
1095
- function runSetupCommand(options) {
1096
- return withOptionalDirectoryArgument(options, "setup", (argument) => resolveHomeDirectoryEffect(options, argument).pipe(Effect.flatMap((directory) => options.homeApi.setup(directory).pipe(Effect.tap(() => Effect.sync(() => options.stdout.write(renderRivusSetupSuccess(directory)))), Effect.as(0))), Effect.catchAll((error) => writeError(options, error))));
1097
- }
1098
- function runHomeDaemonCommand(options, command, daemonArgs) {
1099
- if (hasRivusHomeCommandArguments(options.argv.slice(1))) return Effect.sync(() => {
1100
- options.stderr.write(renderRivusHomeCommandUsage(command));
1101
- return 1;
1102
- });
1103
- return resolveHomeDirectoryEffect(options).pipe(Effect.flatMap((directory) => options.homeApi.load(directory)), Effect.tap((home) => Effect.sync(() => options.changeWorkingDirectory(home.workspaceDirectory))), Effect.flatMap((home) => runRivusDaemonCli({
1104
- ...options,
1105
- argv: [
1106
- "--env-file",
1107
- home.envFilePath,
1108
- "--bootstrap",
1109
- home.bootstrap,
1110
- "--manifest",
1111
- home.manifestPath,
1112
- ...daemonArgs
1113
- ],
1114
- env: {
1115
- ...options.env,
1116
- RIVUS_DEPLOYMENT_STATE_DIR: home.stateDirectory,
1117
- RIVUS_HOME: home.directory
1118
- },
1119
- pluginPackageManifestPath: options.packageManifestPath
1120
- })), Effect.catchAll((error) => writeError(options, error)));
1121
- }
1122
- function runInitCommand(options) {
1123
- return withOptionalDirectoryArgument(options, "init", (argument) => {
1124
- const directory = resolve(options.cwd, argument ?? ".");
1125
- return Effect.tryPromise({
1126
- try: () => initializeRivusProject({
1127
- directory,
1128
- nodeExecutable: options.nodeExecutable,
1129
- packageManifestPath: options.packageManifestPath,
1130
- templateDirectory: options.templateDirectory
1131
- }),
1132
- catch: (error) => error
1133
- }).pipe(Effect.tap((result) => Effect.sync(() => options.stdout.write(renderRivusProjectInitializationSuccess(result.directory)))), Effect.as(0), Effect.catchAll((error) => writeError(options, error)));
1134
- });
1135
- }
1136
- function withOptionalDirectoryArgument(options, command, run) {
1137
- const parsed = parseRivusDirectoryArguments(options.argv.slice(1));
1138
- if (parsed.help) return Effect.sync(() => {
1139
- options.stdout.write(renderRivusDirectoryCommandUsage(command));
1140
- return 0;
1141
- });
1142
- if (parsed.error) return Effect.sync(() => {
1143
- options.stderr.write(renderRivusDirectoryCommandUsage(command));
1144
- return 1;
1145
- });
1146
- return run(parsed.directory);
1147
- }
1148
- function runDoctorCommand(options) {
1149
- const parsed = parseRivusDoctorArguments(options.argv.slice(1));
1150
- if (parsed.help) return Effect.sync(() => {
1151
- options.stdout.write(renderRivusDoctorUsage());
1152
- return 0;
1153
- });
1154
- if (parsed.error) {
1155
- const error = parsed.error;
1156
- return Effect.sync(() => {
1157
- options.stderr.write(renderRivusDoctorArgumentError(error));
1158
- return 1;
1159
- });
1160
- }
1161
- if (parsed.directory === void 0) return resolveHomeDirectoryEffect(options).pipe(Effect.flatMap((directory) => options.homeApi.diagnose({
1162
- directory,
1163
- env: options.env,
1164
- ...parsed.envFilePath ? { envFilePath: parsed.envFilePath } : {},
1165
- nodeVersion: options.nodeVersion
1166
- })), Effect.map((report) => writeDoctorReport(options.stdout, report, "Home")), Effect.catchAll((error) => writeError(options, error)));
1167
- const projectDirectory = parsed.directory;
1168
- return diagnoseRivusProject({
1169
- directory: resolve(options.cwd, projectDirectory),
1170
- env: options.env,
1171
- ...parsed.envFilePath ? { envFilePath: parsed.envFilePath } : {},
1172
- nodeVersion: options.nodeVersion
1173
- }).pipe(Effect.map((report) => writeDoctorReport(options.stdout, report, "project")), Effect.catchAll((error) => writeError(options, error)));
1174
- }
1175
- function resolveHomeDirectoryEffect(options, argument) {
1176
- return Effect.try({
1177
- try: () => {
1178
- if (argument) return resolve(options.cwd, argument);
1179
- const configured = options.env.RIVUS_HOME?.trim();
1180
- if (!configured) return resolve(options.homeDirectory, ".rivus-agent");
1181
- if (!isAbsolute(configured)) throw new Error("RIVUS_HOME must be an absolute path");
1182
- return resolve(configured);
1183
- },
1184
- catch: (error) => error instanceof Error ? error : new Error(String(error))
1185
- });
1186
- }
1187
- function writeDoctorReport(stdout, report, owner) {
1188
- for (const chunk of renderRivusDoctorReport(report, owner)) stdout.write(chunk);
1189
- return report.ready ? 0 : 1;
1190
- }
1191
- function writeError(options, error) {
1192
- return Effect.sync(() => {
1193
- options.stderr.write(renderRivusCliError(error));
1194
- return 1;
1195
- });
1196
- }
1197
- //#endregion
1198
- //#region src/bootstrap/cli/rivus-node-entrypoint.ts
1199
- function runRivusNodeEntrypoint(options) {
1200
- const packageManifestPath = join(options.packageDirectory, "package.json");
1201
- return runRivusCli({
1202
- argv: options.argv,
1203
- changeWorkingDirectory: options.changeWorkingDirectory,
1204
- cwd: options.cwd,
1205
- env: options.env,
1206
- ...options.exitAfterSignal !== void 0 ? { exitAfterSignal: options.exitAfterSignal } : {},
1207
- homeApi: createNodeRivusHome({ deploymentInspector: createRivusHomeDeploymentInspector({ packageManifestPath }) }),
1208
- homeDirectory: homedir(),
1209
- loadBootstrap: (specifier) => import(toImportSpecifier(specifier)),
1210
- nodeExecutable: process.execPath,
1211
- nodeVersion: process.versions.node,
1212
- packageManifestPath,
1213
- signalSource: options.signalSource,
1214
- stderr: options.stderr,
1215
- stdout: options.stdout,
1216
- templateDirectory: join(options.packageDirectory, "examples")
1217
- });
1218
- }
1219
- function toImportSpecifier(specifier) {
1220
- if (specifier.startsWith(".") || specifier.startsWith("/")) return pathToFileURL(resolve(specifier)).href;
1221
- return specifier;
1222
- }
1223
- //#endregion
2
+ import { runRivusNodeEntrypoint } from "@rivus/gateway";
3
+ import { Effect } from "effect";
1224
4
  //#region src/cli.ts
1225
- const packageDirectory = fileURLToPath(new URL("..", import.meta.url));
1226
- const exitCode = await Effect.runPromise(runRivusNodeEntrypoint({
1227
- argv: process.argv.slice(2),
1228
- changeWorkingDirectory: (directory) => process.chdir(directory),
1229
- cwd: process.cwd(),
1230
- env: process.env,
1231
- exitAfterSignal: (code) => process.exit(code),
1232
- packageDirectory,
1233
- signalSource: process,
5
+ const processResult = runRivusNodeEntrypoint({
6
+ stdout: process.stdout,
1234
7
  stderr: process.stderr,
1235
- stdout: process.stdout
1236
- }));
1237
- process.exitCode = exitCode;
8
+ signalSource: process,
9
+ exitAfterSignal: (code) => process.exit(code),
10
+ env: process.env,
11
+ cwd: process.cwd(),
12
+ changeWorkingDirectory: (directory) => process.chdir(directory),
13
+ argv: process.argv.slice(2)
14
+ });
15
+ process.exitCode = await Effect.runPromise(processResult);
1238
16
  //#endregion
1239
17
  export {};