@rivus/agent 0.13.2 → 0.14.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/README.md +20 -15
- package/dist/acp.d.ts +40 -40
- package/dist/acp.js +71 -31
- package/dist/bootstrap/pi-feishu.d.ts +20 -0
- package/dist/bootstrap/pi-feishu.js +594 -0
- package/dist/{agent-loop.d.ts → chunks/agent-loop.d.ts} +293 -44
- package/dist/chunks/agent-loop.js +1272 -0
- package/dist/chunks/api.d.ts +70 -0
- package/dist/chunks/api.js +471 -0
- package/dist/{rivus-plugin.d.ts → chunks/api2.d.ts} +271 -120
- package/dist/chunks/api2.js +1331 -0
- package/dist/chunks/api3.d.ts +402 -0
- package/dist/chunks/index.d.ts +3662 -0
- package/dist/chunks/module.js +267 -0
- package/dist/chunks/pi-skill-tool.js +460 -0
- package/dist/chunks/pi-tool-proxy.d.ts +188 -0
- package/dist/chunks/pi.js +329 -0
- package/dist/{rivus-daemon-cli.js → chunks/rivus-daemon-cli.js} +2197 -1526
- package/dist/{rivus-plugin-testkit.d.ts → chunks/rivus-plugin-testkit.d.ts} +1 -1
- package/dist/{rivus-plugin-testkit.js → chunks/rivus-plugin-testkit.js} +11 -3
- package/dist/chunks/sha256-digest.js +12 -0
- package/dist/chunks/spi.d.ts +1 -0
- package/dist/chunks/spi.js +2 -0
- package/dist/chunks/src.js +9897 -0
- package/dist/cli.js +602 -93
- package/dist/index.d.ts +8 -3645
- package/dist/index.js +9 -10483
- package/dist/mcp.d.ts +3 -38
- package/dist/mcp.js +4 -114
- package/dist/pi.d.ts +95 -9
- package/dist/pi.js +3 -146
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +1 -1
- package/examples/pi-feishu-deployment.bootstrap.ts +35 -46
- package/examples/pi-feishu.bootstrap.ts +35 -20
- package/examples/rivus-starter.plugin.mjs +3 -1
- package/package.json +8 -2
- package/dist/agent-loop.js +0 -121
- package/dist/agent-memory.d.ts +0 -100
- package/dist/agent-memory.js +0 -114
- package/dist/background-session-authority.js +0 -224
- package/dist/background-session-input.js +0 -45
- package/dist/background-session-service.d.ts +0 -291
- package/dist/pi-tool-proxy.d.ts +0 -197
- package/dist/rivus-plugin-registry.js +0 -215
- package/dist/tool-input-digest.js +0 -128
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,130 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { Effect } from "effect";
|
|
4
|
-
import { lstat, mkdir, readFile, rmdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { T as loadRivusDeploymentManifest, V as resolveFeishuEndpointCredentials, W as loadMergedLocalEnvFile, d as validateTrustedModulePath, f as isPathWithin, j as validateRivusDeploymentManifest, p as findTrustedPackageRoot, t as runRivusDaemonCli, u as resolveNodeRivusPluginModulePath } from "./chunks/rivus-daemon-cli.js";
|
|
3
|
+
import { Effect, Either } from "effect";
|
|
4
|
+
import { lstat, mkdir, readFile, realpath, rmdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
5
5
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
6
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
-
//#region src/
|
|
8
|
+
//#region src/adapters/deployment/inspection/rivus-home-deployment-inspector.ts
|
|
9
|
+
const DEFAULT_BOOTSTRAP_PEERS = Object.freeze(["@earendil-works/pi-coding-agent", "@larksuiteoapi/node-sdk"]);
|
|
10
|
+
function createRivusHomeDeploymentInspector(options = {}) {
|
|
11
|
+
const resolveModule = options.resolveModule ?? ((specifier) => defaultResolveModule(specifier, options.packageManifestPath));
|
|
12
|
+
const resolvePluginModule = options.resolvePluginModule ?? ((request) => resolveNodeRivusPluginModulePath(request, options.packageManifestPath ? { packageManifestPath: options.packageManifestPath } : {}));
|
|
13
|
+
return { inspect: (input) => Effect.gen(function* () {
|
|
14
|
+
const manifestResult = yield* checkManifest$1(input.home);
|
|
15
|
+
const credentials = yield* checkCredentials$1(input.home, manifestResult.manifest, input.env, input.envFilePath);
|
|
16
|
+
const modules = yield* checkModules(input.home, manifestResult.manifest, resolveModule, resolvePluginModule);
|
|
17
|
+
return {
|
|
18
|
+
credentials,
|
|
19
|
+
manifest: manifestResult.check,
|
|
20
|
+
modules
|
|
21
|
+
};
|
|
22
|
+
}) };
|
|
23
|
+
}
|
|
24
|
+
function checkManifest$1(home) {
|
|
25
|
+
return loadRivusDeploymentManifest(home.manifestPath).pipe(Effect.map((manifest) => {
|
|
26
|
+
validateRivusDeploymentManifest(manifest);
|
|
27
|
+
return {
|
|
28
|
+
check: {
|
|
29
|
+
message: "deployment manifest is valid",
|
|
30
|
+
name: "manifest",
|
|
31
|
+
status: "pass"
|
|
32
|
+
},
|
|
33
|
+
manifest
|
|
34
|
+
};
|
|
35
|
+
}), Effect.catchAll((error) => Effect.succeed({ check: {
|
|
36
|
+
message: `deployment manifest is invalid: ${formatError$1(error)}`,
|
|
37
|
+
name: "manifest",
|
|
38
|
+
status: "fail"
|
|
39
|
+
} })));
|
|
40
|
+
}
|
|
41
|
+
function checkModules(home, manifest, resolveModule, resolvePluginModule) {
|
|
42
|
+
if (!manifest) return Effect.succeed({
|
|
43
|
+
message: "modules cannot be checked until manifest is valid",
|
|
44
|
+
name: "modules",
|
|
45
|
+
status: "fail"
|
|
46
|
+
});
|
|
47
|
+
const specifiers = [home.bootstrap, ...home.bootstrap === "@rivus/agent/bootstrap/pi-feishu" ? DEFAULT_BOOTSTRAP_PEERS : []];
|
|
48
|
+
return Effect.gen(function* () {
|
|
49
|
+
const missing = [];
|
|
50
|
+
for (const specifier of specifiers) {
|
|
51
|
+
const result = yield* toEffect(() => resolveModule(specifier)).pipe(Effect.either);
|
|
52
|
+
if (result._tag === "Left") missing.push(`${specifier} (${formatError$1(result.left)})`);
|
|
53
|
+
}
|
|
54
|
+
for (const plugin of manifest.plugins) {
|
|
55
|
+
const result = yield* toEffect(() => resolvePluginModule({
|
|
56
|
+
deploymentRoot: home.directory,
|
|
57
|
+
module: plugin.module,
|
|
58
|
+
pluginId: plugin.id
|
|
59
|
+
})).pipe(Effect.either);
|
|
60
|
+
if (result._tag === "Left") missing.push(`${plugin.module} (${formatError$1(result.left)})`);
|
|
61
|
+
}
|
|
62
|
+
return missing.length === 0 ? {
|
|
63
|
+
message: "Bootstrap, Plugin, Pi, and Feishu modules resolve from the global installation",
|
|
64
|
+
name: "modules",
|
|
65
|
+
status: "pass"
|
|
66
|
+
} : {
|
|
67
|
+
message: `global npm modules are unavailable: ${missing.join(", ")}`,
|
|
68
|
+
name: "modules",
|
|
69
|
+
status: "fail"
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function toEffect(read) {
|
|
74
|
+
return Effect.suspend(() => {
|
|
75
|
+
try {
|
|
76
|
+
const value = read();
|
|
77
|
+
if (Effect.isEffect(value)) return value;
|
|
78
|
+
return Effect.tryPromise({
|
|
79
|
+
try: () => Promise.resolve(value),
|
|
80
|
+
catch: toError$1
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return Effect.fail(toError$1(error));
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function checkCredentials$1(home, manifest, env, envFilePath) {
|
|
88
|
+
if (!manifest) return Effect.succeed({
|
|
89
|
+
message: "credentials cannot be checked until manifest is valid",
|
|
90
|
+
name: "credentials",
|
|
91
|
+
status: "fail"
|
|
92
|
+
});
|
|
93
|
+
return Effect.tryPromise({
|
|
94
|
+
try: async () => {
|
|
95
|
+
const merged = await loadMergedLocalEnvFile(envFilePath ? resolve(home.directory, envFilePath) : home.envFilePath, env);
|
|
96
|
+
for (const endpoint of manifest.endpoints.filter(({ enabled }) => enabled)) resolveFeishuEndpointCredentials(endpoint.credentialRef, merged);
|
|
97
|
+
return {
|
|
98
|
+
message: "enabled Endpoint credential references resolve",
|
|
99
|
+
name: "credentials",
|
|
100
|
+
status: "pass"
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
catch: toError$1
|
|
104
|
+
}).pipe(Effect.catchAll((error) => Effect.succeed({
|
|
105
|
+
message: `enabled Endpoint credentials are incomplete: ${formatError$1(error)}`,
|
|
106
|
+
name: "credentials",
|
|
107
|
+
status: "fail"
|
|
108
|
+
})));
|
|
109
|
+
}
|
|
110
|
+
function defaultResolveModule(specifier, packageManifestPath) {
|
|
111
|
+
if (!packageManifestPath) return Effect.fail(/* @__PURE__ */ new Error("Rivus package manifest path is required for trusted module resolution"));
|
|
112
|
+
return Effect.gen(function* () {
|
|
113
|
+
const resolvedRealpath = yield* Effect.tryPromise({
|
|
114
|
+
try: () => realpath(fileURLToPath(import.meta.resolve(specifier))),
|
|
115
|
+
catch: toError$1
|
|
116
|
+
});
|
|
117
|
+
return yield* validateTrustedModulePath(yield* findTrustedPackageRoot(packageManifestPath), resolvedRealpath, `module ${specifier}`);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function toError$1(error) {
|
|
121
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
122
|
+
}
|
|
123
|
+
function formatError$1(error) {
|
|
124
|
+
return error instanceof Error ? error.message : String(error);
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/adapters/deployment/inspection/rivus-project-doctor.ts
|
|
8
128
|
const REQUIRED_FILES = Object.freeze([
|
|
9
129
|
"package.json",
|
|
10
130
|
"rivus.bootstrap.ts",
|
|
@@ -15,23 +135,33 @@ const REQUIRED_DEPENDENCIES = Object.freeze([
|
|
|
15
135
|
"@earendil-works/pi-coding-agent",
|
|
16
136
|
"@larksuiteoapi/node-sdk"
|
|
17
137
|
]);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
directory
|
|
31
|
-
|
|
138
|
+
function diagnoseRivusProject(options) {
|
|
139
|
+
return Effect.gen(function* () {
|
|
140
|
+
const directory = resolve(options.directory);
|
|
141
|
+
const checks = [checkNode$1(options.nodeVersion)];
|
|
142
|
+
checks.push(yield* Effect.tryPromise({
|
|
143
|
+
try: () => checkFiles(directory),
|
|
144
|
+
catch: toError
|
|
145
|
+
}));
|
|
146
|
+
checks.push(yield* Effect.tryPromise({
|
|
147
|
+
try: () => checkDependencies(directory),
|
|
148
|
+
catch: toError
|
|
149
|
+
}));
|
|
150
|
+
const manifestResult = yield* checkManifest(directory);
|
|
151
|
+
checks.push(manifestResult.check);
|
|
152
|
+
const envFilePath = resolve(directory, options.envFilePath ?? ".env.local");
|
|
153
|
+
checks.push(yield* Effect.tryPromise({
|
|
154
|
+
try: () => checkCredentials(manifestResult.manifest, envFilePath, options.env),
|
|
155
|
+
catch: toError
|
|
156
|
+
}));
|
|
157
|
+
return Object.freeze({
|
|
158
|
+
checks: Object.freeze(checks),
|
|
159
|
+
directory,
|
|
160
|
+
ready: checks.every(({ status }) => status === "pass")
|
|
161
|
+
});
|
|
32
162
|
});
|
|
33
163
|
}
|
|
34
|
-
function checkNode(version) {
|
|
164
|
+
function checkNode$1(version) {
|
|
35
165
|
const [major, minor] = version.split(".").map(Number);
|
|
36
166
|
if (major === 24 && Number.isInteger(minor) && minor >= 11) return {
|
|
37
167
|
message: `Node.js ${version} satisfies the supported ^24.11 runtime`,
|
|
@@ -70,19 +200,17 @@ async function checkDependencies(directory) {
|
|
|
70
200
|
status: "fail"
|
|
71
201
|
};
|
|
72
202
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const manifest = await loadRivusDeploymentManifest(join(directory, "rivus.config.json"));
|
|
203
|
+
function checkManifest(directory) {
|
|
204
|
+
return loadRivusDeploymentManifest(join(directory, "rivus.config.json")).pipe(Effect.flatMap((manifest) => Effect.gen(function* () {
|
|
76
205
|
validateRivusDeploymentManifest(manifest);
|
|
77
206
|
const missingModules = [];
|
|
78
|
-
for (const plugin of manifest.plugins)
|
|
79
|
-
|
|
207
|
+
for (const plugin of manifest.plugins) {
|
|
208
|
+
const result = yield* resolveNodeRivusPluginModulePath({
|
|
80
209
|
deploymentRoot: directory,
|
|
81
210
|
module: plugin.module,
|
|
82
211
|
pluginId: plugin.id
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
missingModules.push(plugin.module);
|
|
212
|
+
}).pipe(Effect.either);
|
|
213
|
+
if (Either.isLeft(result)) missingModules.push(plugin.module);
|
|
86
214
|
}
|
|
87
215
|
if (missingModules.length > 0) return { check: {
|
|
88
216
|
message: `manifest Plugin modules are missing: ${missingModules.join(", ")}`,
|
|
@@ -97,13 +225,11 @@ async function checkManifest(directory) {
|
|
|
97
225
|
},
|
|
98
226
|
manifest
|
|
99
227
|
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
} };
|
|
106
|
-
}
|
|
228
|
+
})), Effect.catchAll((error) => Effect.succeed({ check: {
|
|
229
|
+
message: `deployment manifest is invalid: ${error.message}`,
|
|
230
|
+
name: "manifest",
|
|
231
|
+
status: "fail"
|
|
232
|
+
} })));
|
|
107
233
|
}
|
|
108
234
|
async function checkCredentials(manifest, envFilePath, env) {
|
|
109
235
|
let mergedEnv;
|
|
@@ -149,8 +275,11 @@ async function missingRegularFiles(directory, paths) {
|
|
|
149
275
|
function isMissingPath$1(error) {
|
|
150
276
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
151
277
|
}
|
|
278
|
+
function toError(error) {
|
|
279
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
280
|
+
}
|
|
152
281
|
//#endregion
|
|
153
|
-
//#region src/
|
|
282
|
+
//#region src/platform/project/setup/rivus-project-initializer.ts
|
|
154
283
|
const TEMPLATE_FILES = Object.freeze({
|
|
155
284
|
"current-weather.mjs": "current-weather.mjs",
|
|
156
285
|
"https-response-reader.mjs": "https-response-reader.mjs",
|
|
@@ -161,12 +290,12 @@ async function initializeRivusProject(options) {
|
|
|
161
290
|
const directory = resolve(options.directory);
|
|
162
291
|
const manifest = await readPackageManifest(options.packageManifestPath);
|
|
163
292
|
const files = /* @__PURE__ */ new Map([
|
|
164
|
-
[".env.example", environmentTemplate()],
|
|
293
|
+
[".env.example", environmentTemplate$1()],
|
|
165
294
|
[".gitignore", ".env.local\n.rivus/\nnode_modules/\n"],
|
|
166
295
|
["deploy/launchd/com.rivus.agent.plist", launchdService(directory, options.nodeExecutable)],
|
|
167
296
|
["deploy/systemd/rivus.service", systemdService(directory, options.nodeExecutable)],
|
|
168
297
|
["package.json", projectPackageJson(directory, manifest)],
|
|
169
|
-
["rivus.config.json", deploymentManifest()]
|
|
298
|
+
["rivus.config.json", deploymentManifest$1()]
|
|
170
299
|
]);
|
|
171
300
|
for (const [target, source] of Object.entries(TEMPLATE_FILES)) files.set(target, await readFile(join(options.templateDirectory, source), "utf8"));
|
|
172
301
|
const paths = [...files.keys()].sort();
|
|
@@ -231,7 +360,7 @@ async function ensureDirectory(path, projectRoot, createdDirectories) {
|
|
|
231
360
|
if (!isAlreadyExists(error)) throw error;
|
|
232
361
|
const state = await lstat(path);
|
|
233
362
|
if (state.isSymbolicLink()) {
|
|
234
|
-
if (
|
|
363
|
+
if (isPathWithin(projectRoot, path)) throw new Error(`Rivus project initialization refused; symbolic link ancestor: ${relative(projectRoot, path) || "."}`);
|
|
235
364
|
if ((await stat(path)).isDirectory()) return;
|
|
236
365
|
}
|
|
237
366
|
if (!state.isDirectory()) throw new Error(`Rivus project initialization refused; non-directory ancestor: ${path}`);
|
|
@@ -242,12 +371,12 @@ async function rollbackCreatedPaths(files, directories) {
|
|
|
242
371
|
for (const path of files.reverse()) try {
|
|
243
372
|
await unlink(path);
|
|
244
373
|
} catch (error) {
|
|
245
|
-
if (!isMissingPath(error)) errors.push(asError(error));
|
|
374
|
+
if (!isMissingPath(error)) errors.push(asError$1(error));
|
|
246
375
|
}
|
|
247
376
|
for (const path of directories.reverse()) try {
|
|
248
377
|
await rmdir(path);
|
|
249
378
|
} catch (error) {
|
|
250
|
-
if (!isMissingPath(error) && !isDirectoryNotEmpty(error)) errors.push(asError(error));
|
|
379
|
+
if (!isMissingPath(error) && !isDirectoryNotEmpty(error)) errors.push(asError$1(error));
|
|
251
380
|
}
|
|
252
381
|
return errors;
|
|
253
382
|
}
|
|
@@ -259,17 +388,13 @@ async function lstatOrUndefined(path) {
|
|
|
259
388
|
throw error;
|
|
260
389
|
}
|
|
261
390
|
}
|
|
262
|
-
function isWithin(root, candidate) {
|
|
263
|
-
const child = relative(root, candidate);
|
|
264
|
-
return child === "" || !child.startsWith(`..${sep}`) && child !== ".." && !isAbsolute(child);
|
|
265
|
-
}
|
|
266
391
|
function isAlreadyExists(error) {
|
|
267
392
|
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
268
393
|
}
|
|
269
394
|
function isDirectoryNotEmpty(error) {
|
|
270
395
|
return error instanceof Error && "code" in error && error.code === "ENOTEMPTY";
|
|
271
396
|
}
|
|
272
|
-
function asError(error) {
|
|
397
|
+
function asError$1(error) {
|
|
273
398
|
return error instanceof Error ? error : new Error(String(error));
|
|
274
399
|
}
|
|
275
400
|
async function readPackageManifest(path) {
|
|
@@ -316,7 +441,7 @@ function projectPackageJson(directory, manifest) {
|
|
|
316
441
|
function sanitizePackageName(value) {
|
|
317
442
|
return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "") || "rivus-app";
|
|
318
443
|
}
|
|
319
|
-
function deploymentManifest() {
|
|
444
|
+
function deploymentManifest$1() {
|
|
320
445
|
return `${JSON.stringify({
|
|
321
446
|
plugins: [{
|
|
322
447
|
id: "rivus-starter",
|
|
@@ -349,7 +474,7 @@ function deploymentManifest() {
|
|
|
349
474
|
}]
|
|
350
475
|
}, null, 2)}\n`;
|
|
351
476
|
}
|
|
352
|
-
function environmentTemplate() {
|
|
477
|
+
function environmentTemplate$1() {
|
|
353
478
|
return [
|
|
354
479
|
"# Copy this file to .env.local and keep the real values untracked.",
|
|
355
480
|
"RIVUS_FEISHU_APP_ID=",
|
|
@@ -393,18 +518,31 @@ function xmlEscape(value) {
|
|
|
393
518
|
//#endregion
|
|
394
519
|
//#region src/composition/rivus-cli.ts
|
|
395
520
|
const USAGE = `Usage:
|
|
521
|
+
rivus setup [directory]
|
|
522
|
+
rivus start
|
|
523
|
+
rivus status
|
|
524
|
+
rivus check-config
|
|
396
525
|
rivus init [directory]
|
|
397
526
|
rivus doctor [directory] [--env-file <path>]
|
|
398
527
|
rivus --bootstrap <module> [--manifest <rivus.config.json>] [options]
|
|
399
528
|
|
|
400
529
|
Commands:
|
|
530
|
+
setup Create Rivus Home (default ~/.rivus-agent) without overwriting files
|
|
531
|
+
start Start the Rivus Home deployment in the foreground
|
|
532
|
+
status Print Rivus Home deployment status without activating Endpoints
|
|
533
|
+
check-config
|
|
534
|
+
Validate and print the redacted Rivus Home manifest
|
|
401
535
|
init Create a standalone local Rivus project without overwriting files
|
|
402
|
-
doctor Check
|
|
536
|
+
doctor Check Rivus Home by default, or an explicit standalone project directory
|
|
403
537
|
|
|
404
538
|
Run rivus --help for the complete daemon option list.
|
|
405
539
|
`;
|
|
406
540
|
function runRivusCli(options) {
|
|
407
541
|
const command = options.argv[0];
|
|
542
|
+
if (command === "setup") return runSetupCommand(options);
|
|
543
|
+
if (command === "start") return runHomeDaemonCommand(options, []);
|
|
544
|
+
if (command === "status") return runHomeDaemonCommand(options, ["--status"]);
|
|
545
|
+
if (command === "check-config") return runHomeDaemonCommand(options, ["--check-config"]);
|
|
408
546
|
if (command === "init") return runInitCommand(options);
|
|
409
547
|
if (command === "doctor") return runDoctorCommand(options);
|
|
410
548
|
if (command && !command.startsWith("-")) return Effect.sync(() => {
|
|
@@ -413,60 +551,107 @@ function runRivusCli(options) {
|
|
|
413
551
|
});
|
|
414
552
|
return runRivusDaemonCli(options);
|
|
415
553
|
}
|
|
554
|
+
function runSetupCommand(options) {
|
|
555
|
+
return withOptionalDirectoryArgument(options, "setup", (argument) => resolveHomeDirectoryEffect(options, argument).pipe(Effect.flatMap((directory) => options.homeApi.setup(directory).pipe(Effect.tap(() => Effect.sync(() => options.stdout.write(`Initialized Rivus Home in ${directory}\n\nNext:\n cp ${shellQuote(`${directory}/.env.example`)} ${shellQuote(`${directory}/.env`)}\n rivus doctor\n rivus start\n`))), Effect.as(0))), Effect.catchAll((error) => writeError(options, error))));
|
|
556
|
+
}
|
|
557
|
+
function runHomeDaemonCommand(options, daemonArgs) {
|
|
558
|
+
if (options.argv.length > 1) return Effect.sync(() => {
|
|
559
|
+
options.stderr.write(`Usage: rivus ${options.argv[0]}\n`);
|
|
560
|
+
return 1;
|
|
561
|
+
});
|
|
562
|
+
return resolveHomeDirectoryEffect(options).pipe(Effect.flatMap((directory) => options.homeApi.load(directory)), Effect.tap((home) => Effect.sync(() => options.changeWorkingDirectory(home.workspaceDirectory))), Effect.flatMap((home) => runRivusDaemonCli({
|
|
563
|
+
...options,
|
|
564
|
+
argv: [
|
|
565
|
+
"--env-file",
|
|
566
|
+
home.envFilePath,
|
|
567
|
+
"--bootstrap",
|
|
568
|
+
home.bootstrap,
|
|
569
|
+
"--manifest",
|
|
570
|
+
home.manifestPath,
|
|
571
|
+
...daemonArgs
|
|
572
|
+
],
|
|
573
|
+
env: {
|
|
574
|
+
...options.env,
|
|
575
|
+
RIVUS_DEPLOYMENT_STATE_DIR: home.stateDirectory,
|
|
576
|
+
RIVUS_HOME: home.directory
|
|
577
|
+
},
|
|
578
|
+
pluginPackageManifestPath: options.packageManifestPath
|
|
579
|
+
})), Effect.catchAll((error) => writeError(options, error)));
|
|
580
|
+
}
|
|
416
581
|
function runInitCommand(options) {
|
|
417
|
-
return
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
return 0;
|
|
422
|
-
}
|
|
423
|
-
if (args.length > 1 || args[0]?.startsWith("-")) {
|
|
424
|
-
options.stderr.write("Usage: rivus init [directory]\n");
|
|
425
|
-
return 1;
|
|
426
|
-
}
|
|
427
|
-
const directory = resolve(options.cwd, args[0] ?? ".");
|
|
428
|
-
try {
|
|
429
|
-
const result = await initializeRivusProject({
|
|
582
|
+
return withOptionalDirectoryArgument(options, "init", (argument) => {
|
|
583
|
+
const directory = resolve(options.cwd, argument ?? ".");
|
|
584
|
+
return Effect.tryPromise({
|
|
585
|
+
try: () => initializeRivusProject({
|
|
430
586
|
directory,
|
|
431
587
|
nodeExecutable: options.nodeExecutable,
|
|
432
588
|
packageManifestPath: options.packageManifestPath,
|
|
433
589
|
templateDirectory: options.templateDirectory
|
|
434
|
-
})
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
} catch (error) {
|
|
438
|
-
options.stderr.write(`${formatError(error)}\n`);
|
|
439
|
-
return 1;
|
|
440
|
-
}
|
|
590
|
+
}),
|
|
591
|
+
catch: (error) => error
|
|
592
|
+
}).pipe(Effect.tap((result) => Effect.sync(() => options.stdout.write(`Initialized Rivus project in ${result.directory}\n\nNext:\n cd ${shellQuote(result.directory)}\n npm install\n cp .env.example .env.local\n npm run doctor\n npm start\n`))), Effect.as(0), Effect.catchAll((error) => writeError(options, error)));
|
|
441
593
|
});
|
|
442
594
|
}
|
|
595
|
+
function withOptionalDirectoryArgument(options, command, run) {
|
|
596
|
+
const args = options.argv.slice(1);
|
|
597
|
+
const usage = `Usage: rivus ${command} [directory]\n`;
|
|
598
|
+
if (args.includes("--help") || args.includes("-h")) return Effect.sync(() => {
|
|
599
|
+
options.stdout.write(usage);
|
|
600
|
+
return 0;
|
|
601
|
+
});
|
|
602
|
+
if (args.length > 1 || args[0]?.startsWith("-")) return Effect.sync(() => {
|
|
603
|
+
options.stderr.write(usage);
|
|
604
|
+
return 1;
|
|
605
|
+
});
|
|
606
|
+
return run(args[0]);
|
|
607
|
+
}
|
|
443
608
|
function runDoctorCommand(options) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
return 1;
|
|
453
|
-
}
|
|
454
|
-
try {
|
|
455
|
-
const report = await diagnoseRivusProject({
|
|
456
|
-
directory: resolve(options.cwd, parsed.directory ?? "."),
|
|
457
|
-
env: options.env,
|
|
458
|
-
...parsed.envFilePath ? { envFilePath: parsed.envFilePath } : {},
|
|
459
|
-
nodeVersion: options.nodeVersion
|
|
460
|
-
});
|
|
461
|
-
options.stdout.write(`Rivus doctor: ${report.directory}\n`);
|
|
462
|
-
for (const check of report.checks) options.stdout.write(`${check.status === "pass" ? "PASS" : "FAIL"} ${check.name}: ${check.message}\n`);
|
|
463
|
-
options.stdout.write(report.ready ? "Rivus project is ready\n" : "Rivus project is not ready\n");
|
|
464
|
-
return report.ready ? 0 : 1;
|
|
465
|
-
} catch (error) {
|
|
466
|
-
options.stderr.write(`${formatError(error)}\n`);
|
|
467
|
-
return 1;
|
|
468
|
-
}
|
|
609
|
+
const parsed = parseDoctorArguments(options.argv.slice(1));
|
|
610
|
+
if (parsed.help) return Effect.sync(() => {
|
|
611
|
+
options.stdout.write("Usage: rivus doctor [directory] [--env-file <path>]\n");
|
|
612
|
+
return 0;
|
|
613
|
+
});
|
|
614
|
+
if (parsed.error) return Effect.sync(() => {
|
|
615
|
+
options.stderr.write(`${parsed.error}\nUsage: rivus doctor [directory] [--env-file <path>]\n`);
|
|
616
|
+
return 1;
|
|
469
617
|
});
|
|
618
|
+
if (parsed.directory === void 0) return resolveHomeDirectoryEffect(options).pipe(Effect.flatMap((directory) => options.homeApi.diagnose({
|
|
619
|
+
directory,
|
|
620
|
+
env: options.env,
|
|
621
|
+
...parsed.envFilePath ? { envFilePath: parsed.envFilePath } : {},
|
|
622
|
+
nodeVersion: options.nodeVersion
|
|
623
|
+
})), Effect.map((report) => writeDoctorReport(options.stdout, report, "Home")), Effect.catchAll((error) => writeError(options, error)));
|
|
624
|
+
const projectDirectory = parsed.directory;
|
|
625
|
+
return diagnoseRivusProject({
|
|
626
|
+
directory: resolve(options.cwd, projectDirectory),
|
|
627
|
+
env: options.env,
|
|
628
|
+
...parsed.envFilePath ? { envFilePath: parsed.envFilePath } : {},
|
|
629
|
+
nodeVersion: options.nodeVersion
|
|
630
|
+
}).pipe(Effect.map((report) => writeDoctorReport(options.stdout, report, "project")), Effect.catchAll((error) => writeError(options, error)));
|
|
631
|
+
}
|
|
632
|
+
function resolveHomeDirectoryEffect(options, argument) {
|
|
633
|
+
return Effect.try({
|
|
634
|
+
try: () => {
|
|
635
|
+
if (argument) return resolve(options.cwd, argument);
|
|
636
|
+
const configured = options.env.RIVUS_HOME?.trim();
|
|
637
|
+
if (!configured) return resolve(options.homeDirectory, ".rivus-agent");
|
|
638
|
+
if (!isAbsolute(configured)) throw new Error("RIVUS_HOME must be an absolute path");
|
|
639
|
+
return resolve(configured);
|
|
640
|
+
},
|
|
641
|
+
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
function writeError(options, error) {
|
|
645
|
+
return Effect.sync(() => {
|
|
646
|
+
options.stderr.write(`${formatError(error)}\n`);
|
|
647
|
+
return 1;
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
function writeDoctorReport(stdout, report, owner) {
|
|
651
|
+
stdout.write(`Rivus doctor: ${report.directory}\n`);
|
|
652
|
+
for (const check of report.checks) stdout.write(`${check.status === "pass" ? "PASS" : "FAIL"} ${check.name}: ${check.message}\n`);
|
|
653
|
+
stdout.write(report.ready ? `Rivus ${owner} is ready\n` : `Rivus ${owner} is not ready\n`);
|
|
654
|
+
return report.ready ? 0 : 1;
|
|
470
655
|
}
|
|
471
656
|
function parseDoctorArguments(argv) {
|
|
472
657
|
let directory;
|
|
@@ -502,14 +687,338 @@ function shellQuote(value) {
|
|
|
502
687
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
503
688
|
}
|
|
504
689
|
//#endregion
|
|
690
|
+
//#region src/platform/home/config/rivus-home-config.ts
|
|
691
|
+
/** Loads and validates the operator-owned Home config file. */
|
|
692
|
+
function loadRivusHome(directoryPath) {
|
|
693
|
+
const directory = resolve(directoryPath);
|
|
694
|
+
const configPath = resolve(directory, "config.json");
|
|
695
|
+
return Effect.tryPromise({
|
|
696
|
+
try: async () => {
|
|
697
|
+
const config = parseRivusHomeConfig(JSON.parse(await readFile(configPath, "utf8")));
|
|
698
|
+
return {
|
|
699
|
+
bootstrap: resolveBootstrap(directory, config.bootstrap),
|
|
700
|
+
config,
|
|
701
|
+
configPath,
|
|
702
|
+
directory,
|
|
703
|
+
envFilePath: resolveHomePath(directory, config.envFile, "envFile"),
|
|
704
|
+
logsDirectory: resolveHomePath(directory, config.logs, "logs"),
|
|
705
|
+
manifestPath: resolveHomePath(directory, config.manifest, "manifest"),
|
|
706
|
+
stateDirectory: resolveHomePath(directory, config.state, "state"),
|
|
707
|
+
workspaceDirectory: resolveHomePath(directory, config.workspace, "workspace")
|
|
708
|
+
};
|
|
709
|
+
},
|
|
710
|
+
catch: asError
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
function parseRivusHomeConfig(value) {
|
|
714
|
+
const config = record(value, "Rivus Home config");
|
|
715
|
+
if (config.version !== 1) throw new Error("Rivus Home config version must be 1");
|
|
716
|
+
return {
|
|
717
|
+
bootstrap: nonEmptyString(config.bootstrap, "bootstrap"),
|
|
718
|
+
envFile: relativePath(config.envFile, "envFile"),
|
|
719
|
+
logs: relativePath(config.logs, "logs"),
|
|
720
|
+
manifest: relativePath(config.manifest, "manifest"),
|
|
721
|
+
state: relativePath(config.state, "state"),
|
|
722
|
+
version: 1,
|
|
723
|
+
workspace: relativePath(config.workspace, "workspace")
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
function resolveBootstrap(directory, value) {
|
|
727
|
+
if (value.startsWith("./") || value.startsWith("../")) return resolveHomePath(directory, value, "bootstrap");
|
|
728
|
+
if (isAbsolute(value)) throw new Error("Rivus Home bootstrap must be a package specifier or a relative path inside Rivus Home");
|
|
729
|
+
return value;
|
|
730
|
+
}
|
|
731
|
+
function resolveHomePath(directory, value, owner) {
|
|
732
|
+
const candidate = resolve(directory, value);
|
|
733
|
+
const relation = relative(directory, candidate);
|
|
734
|
+
if (relation === "" || !relation.startsWith(`..${sep}`) && relation !== ".." && !isAbsolute(relation)) return candidate;
|
|
735
|
+
throw new Error(`Rivus Home ${owner} escapes the Home directory`);
|
|
736
|
+
}
|
|
737
|
+
function relativePath(value, owner) {
|
|
738
|
+
const path = nonEmptyString(value, owner);
|
|
739
|
+
if (isAbsolute(path)) throw new Error(`Rivus Home ${owner} must be a relative path`);
|
|
740
|
+
return path;
|
|
741
|
+
}
|
|
742
|
+
function nonEmptyString(value, owner) {
|
|
743
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(`Rivus Home ${owner} must be a non-empty string`);
|
|
744
|
+
return value;
|
|
745
|
+
}
|
|
746
|
+
function record(value, owner) {
|
|
747
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${owner} must be an object`);
|
|
748
|
+
return value;
|
|
749
|
+
}
|
|
750
|
+
function asError(error) {
|
|
751
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
752
|
+
}
|
|
753
|
+
//#endregion
|
|
754
|
+
//#region src/platform/home/doctor/rivus-home-doctor.ts
|
|
755
|
+
function diagnoseRivusHome(input, options) {
|
|
756
|
+
return Effect.gen(function* () {
|
|
757
|
+
const checks = [checkNode(input.nodeVersion)];
|
|
758
|
+
const loaded = yield* options.load(input.directory).pipe(Effect.either);
|
|
759
|
+
if (Either.isLeft(loaded)) {
|
|
760
|
+
checks.push({
|
|
761
|
+
message: `Rivus Home is invalid: ${loaded.left.message}`,
|
|
762
|
+
name: "home",
|
|
763
|
+
status: "fail"
|
|
764
|
+
}, blockedCheck("workspace", "Workspace"), blockedCheck("manifest", "manifest"), blockedCheck("modules", "modules"), blockedCheck("credentials", "credentials"));
|
|
765
|
+
return report(input.directory, checks);
|
|
766
|
+
}
|
|
767
|
+
const home = loaded.right;
|
|
768
|
+
checks.push({
|
|
769
|
+
message: "config.json is valid and contained in Rivus Home",
|
|
770
|
+
name: "home",
|
|
771
|
+
status: "pass"
|
|
772
|
+
});
|
|
773
|
+
checks.push(yield* checkWorkspace(home, options.findMissingWorkspacePaths));
|
|
774
|
+
const deployment = yield* options.deploymentInspector.inspect({
|
|
775
|
+
env: input.env,
|
|
776
|
+
...input.envFilePath ? { envFilePath: input.envFilePath } : {},
|
|
777
|
+
home
|
|
778
|
+
});
|
|
779
|
+
checks.push(deployment.manifest, deployment.modules, deployment.credentials);
|
|
780
|
+
return report(input.directory, checks);
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
function checkNode(version) {
|
|
784
|
+
const [major, minor] = version.split(".").map(Number);
|
|
785
|
+
return major === 24 && Number.isInteger(minor) && minor >= 11 ? {
|
|
786
|
+
message: `Node.js ${version} satisfies the supported ^24.11 runtime`,
|
|
787
|
+
name: "node",
|
|
788
|
+
status: "pass"
|
|
789
|
+
} : {
|
|
790
|
+
message: `Node.js 24.11 or newer on major 24 is required; current version is ${version}`,
|
|
791
|
+
name: "node",
|
|
792
|
+
status: "fail"
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
function checkWorkspace(home, findMissingWorkspacePaths) {
|
|
796
|
+
return findMissingWorkspacePaths(home).pipe(Effect.map((missing) => missing.length === 0 ? {
|
|
797
|
+
message: "Workspace instructions, Memory, Skills, and work directories are present",
|
|
798
|
+
name: "workspace",
|
|
799
|
+
status: "pass"
|
|
800
|
+
} : {
|
|
801
|
+
message: `Workspace paths are missing: ${missing.join(", ")}`,
|
|
802
|
+
name: "workspace",
|
|
803
|
+
status: "fail"
|
|
804
|
+
}));
|
|
805
|
+
}
|
|
806
|
+
function blockedCheck(name, owner) {
|
|
807
|
+
return {
|
|
808
|
+
message: `${owner} cannot be checked until config.json is valid`,
|
|
809
|
+
name,
|
|
810
|
+
status: "fail"
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
function report(directory, checks) {
|
|
814
|
+
return {
|
|
815
|
+
checks,
|
|
816
|
+
directory,
|
|
817
|
+
ready: checks.every(({ status }) => status === "pass")
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
//#endregion
|
|
821
|
+
//#region src/platform/home/setup/rivus-home-layout.ts
|
|
822
|
+
const HOME_DIRECTORIES = Object.freeze([
|
|
823
|
+
"logs",
|
|
824
|
+
"plugins",
|
|
825
|
+
"state",
|
|
826
|
+
"workspace/memory",
|
|
827
|
+
"workspace/skills",
|
|
828
|
+
"workspace/work/artifacts",
|
|
829
|
+
"workspace/work/drafts",
|
|
830
|
+
"workspace/work/inbox",
|
|
831
|
+
"workspace/work/tmp"
|
|
832
|
+
]);
|
|
833
|
+
function createRivusHomeLayout() {
|
|
834
|
+
return {
|
|
835
|
+
directories: HOME_DIRECTORIES,
|
|
836
|
+
files: /* @__PURE__ */ new Map([
|
|
837
|
+
[".env.example", environmentTemplate()],
|
|
838
|
+
[".gitignore", ".DS_Store\n.env\nlogs/\nstate/\nworkspace/work/tmp/\n"],
|
|
839
|
+
["config.json", homeConfig()],
|
|
840
|
+
["rivus.config.json", deploymentManifest()],
|
|
841
|
+
["workspace/AGENTS.md", agentsTemplate()],
|
|
842
|
+
["workspace/IDENTITY.md", "# Identity\n\nName: Rivus\n\nRole: Personal agent\n"],
|
|
843
|
+
["workspace/MEMORY.md", "# Curated Memory\n\nStore confirmed, durable facts and decisions here.\n"],
|
|
844
|
+
["workspace/SOUL.md", "# Character\n\nBe direct, thoughtful, and evidence-led.\n"],
|
|
845
|
+
["workspace/USER.md", "# User\n\nRecord stable user preferences here.\n"],
|
|
846
|
+
["plugins/.gitkeep", ""],
|
|
847
|
+
["workspace/memory/.gitkeep", ""],
|
|
848
|
+
["workspace/skills/.gitkeep", ""],
|
|
849
|
+
["workspace/work/artifacts/.gitkeep", ""],
|
|
850
|
+
["workspace/work/drafts/.gitkeep", ""],
|
|
851
|
+
["workspace/work/inbox/.gitkeep", ""]
|
|
852
|
+
])
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
function homeConfig() {
|
|
856
|
+
return `${JSON.stringify({
|
|
857
|
+
bootstrap: "@rivus/agent/bootstrap/pi-feishu",
|
|
858
|
+
envFile: ".env",
|
|
859
|
+
logs: "logs",
|
|
860
|
+
manifest: "rivus.config.json",
|
|
861
|
+
state: "state",
|
|
862
|
+
version: 1,
|
|
863
|
+
workspace: "workspace"
|
|
864
|
+
}, null, 2)}\n`;
|
|
865
|
+
}
|
|
866
|
+
function deploymentManifest() {
|
|
867
|
+
return `${JSON.stringify({
|
|
868
|
+
agents: [{
|
|
869
|
+
agentId: "personal",
|
|
870
|
+
endpointIds: ["personal-feishu"],
|
|
871
|
+
memory: {
|
|
872
|
+
scopes: ["agent-private"],
|
|
873
|
+
tool: true
|
|
874
|
+
},
|
|
875
|
+
pluginId: "rivus-starter",
|
|
876
|
+
profileId: "agent-a",
|
|
877
|
+
projectSpaceId: "personal-home",
|
|
878
|
+
runtimeTools: { allow: [
|
|
879
|
+
"read",
|
|
880
|
+
"bash",
|
|
881
|
+
"edit",
|
|
882
|
+
"write",
|
|
883
|
+
"grep",
|
|
884
|
+
"find",
|
|
885
|
+
"ls"
|
|
886
|
+
] },
|
|
887
|
+
skills: { allow: [] },
|
|
888
|
+
tools: { allow: ["rivus-starter/current-weather"] }
|
|
889
|
+
}],
|
|
890
|
+
defaultAgentId: "personal",
|
|
891
|
+
defaultEndpointId: "personal-feishu",
|
|
892
|
+
endpoints: [{
|
|
893
|
+
agentId: "personal",
|
|
894
|
+
baseUrl: "https://open.feishu.cn",
|
|
895
|
+
cardStreamLeaseMs: 51e4,
|
|
896
|
+
credentialRef: "env:RIVUS_FEISHU",
|
|
897
|
+
enabled: true,
|
|
898
|
+
experimental: { cotMessages: false },
|
|
899
|
+
groupPolicy: "mention-only",
|
|
900
|
+
id: "personal-feishu",
|
|
901
|
+
progressDisplay: "collapsed",
|
|
902
|
+
required: true,
|
|
903
|
+
sessionNamespace: "rivus-home-v1",
|
|
904
|
+
streamMinIntervalMs: 200
|
|
905
|
+
}],
|
|
906
|
+
plugins: [{
|
|
907
|
+
id: "rivus-starter",
|
|
908
|
+
module: "@rivus/agent/plugin/starter",
|
|
909
|
+
required: true
|
|
910
|
+
}],
|
|
911
|
+
projectSpaces: [{
|
|
912
|
+
id: "personal-home",
|
|
913
|
+
root: "workspace",
|
|
914
|
+
skills: { sources: ["skills"] },
|
|
915
|
+
workingDirectory: "."
|
|
916
|
+
}]
|
|
917
|
+
}, null, 2)}\n`;
|
|
918
|
+
}
|
|
919
|
+
function environmentTemplate() {
|
|
920
|
+
return [
|
|
921
|
+
"# Copy this file to .env and keep the real values untracked.",
|
|
922
|
+
"RIVUS_FEISHU_APP_ID=",
|
|
923
|
+
"RIVUS_FEISHU_APP_SECRET=",
|
|
924
|
+
"PI_MODEL=",
|
|
925
|
+
"PI_API_KEY=",
|
|
926
|
+
"# PI_BASE_URL=",
|
|
927
|
+
"RIVUS_WEATHER_DEFAULT_LOCATION=上海",
|
|
928
|
+
""
|
|
929
|
+
].join("\n");
|
|
930
|
+
}
|
|
931
|
+
function agentsTemplate() {
|
|
932
|
+
return [
|
|
933
|
+
"# Personal Workspace",
|
|
934
|
+
"",
|
|
935
|
+
"Use this directory as the default home for personal work.",
|
|
936
|
+
"",
|
|
937
|
+
"- Read `SOUL.md` and `IDENTITY.md` when identity or tone matters.",
|
|
938
|
+
"- Read `USER.md` when stable user preferences affect the task.",
|
|
939
|
+
"- Search `MEMORY.md` and dated files under `memory/` when prior facts or decisions matter.",
|
|
940
|
+
"- Read the selected `skills/<name>/SKILL.md` before following a Workspace Skill.",
|
|
941
|
+
"- Put incoming material in `work/inbox/`, drafts in `work/drafts/`, and durable general outputs in `work/artifacts/`.",
|
|
942
|
+
"- Put disposable files in `work/tmp/`.",
|
|
943
|
+
"- Store only confirmed durable facts in Memory; keep credentials and raw private transcripts out of tracked files.",
|
|
944
|
+
""
|
|
945
|
+
].join("\n");
|
|
946
|
+
}
|
|
947
|
+
//#endregion
|
|
948
|
+
//#region src/platform/home/setup/rivus-home-initializer.ts
|
|
949
|
+
function initializeRivusHome(directoryPath) {
|
|
950
|
+
const directory = resolve(directoryPath);
|
|
951
|
+
const { directories, files } = createRivusHomeLayout();
|
|
952
|
+
const paths = [...files.keys()].sort();
|
|
953
|
+
return Effect.tryPromise({
|
|
954
|
+
try: async () => {
|
|
955
|
+
for (const path of [...directories].sort()) await mkdir(join(directory, path), { recursive: true });
|
|
956
|
+
for (const path of paths) {
|
|
957
|
+
const destination = join(directory, path);
|
|
958
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
959
|
+
await writeFile(destination, files.get(path), {
|
|
960
|
+
encoding: "utf8",
|
|
961
|
+
flag: "wx"
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
return {
|
|
965
|
+
directory,
|
|
966
|
+
files: paths
|
|
967
|
+
};
|
|
968
|
+
},
|
|
969
|
+
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
//#endregion
|
|
973
|
+
//#region src/platform/home/workspace/rivus-home-workspace.ts
|
|
974
|
+
function findMissingRivusHomeWorkspacePaths(home) {
|
|
975
|
+
const required = [
|
|
976
|
+
home.workspaceDirectory,
|
|
977
|
+
resolve(home.workspaceDirectory, "AGENTS.md"),
|
|
978
|
+
resolve(home.workspaceDirectory, "MEMORY.md"),
|
|
979
|
+
resolve(home.workspaceDirectory, "memory"),
|
|
980
|
+
resolve(home.workspaceDirectory, "skills"),
|
|
981
|
+
resolve(home.workspaceDirectory, "work")
|
|
982
|
+
];
|
|
983
|
+
return Effect.tryPromise({
|
|
984
|
+
try: async () => {
|
|
985
|
+
const missing = [];
|
|
986
|
+
for (const path of required) try {
|
|
987
|
+
await stat(path);
|
|
988
|
+
} catch (error) {
|
|
989
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") missing.push(path);
|
|
990
|
+
else throw error;
|
|
991
|
+
}
|
|
992
|
+
return missing;
|
|
993
|
+
},
|
|
994
|
+
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
//#endregion
|
|
998
|
+
//#region src/platform/home/node/node-rivus-home.ts
|
|
999
|
+
function createNodeRivusHome(options) {
|
|
1000
|
+
return {
|
|
1001
|
+
diagnose: (input) => diagnoseRivusHome(input, {
|
|
1002
|
+
deploymentInspector: options.deploymentInspector,
|
|
1003
|
+
findMissingWorkspacePaths: findMissingRivusHomeWorkspacePaths,
|
|
1004
|
+
load: loadRivusHome
|
|
1005
|
+
}),
|
|
1006
|
+
load: loadRivusHome,
|
|
1007
|
+
setup: initializeRivusHome
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
//#endregion
|
|
505
1011
|
//#region src/cli.ts
|
|
506
1012
|
const packageDirectory = fileURLToPath(new URL("..", import.meta.url));
|
|
507
1013
|
const exitCode = await Effect.runPromise(runRivusCli({
|
|
508
1014
|
argv: process.argv.slice(2),
|
|
1015
|
+
changeWorkingDirectory: (directory) => process.chdir(directory),
|
|
509
1016
|
cwd: process.cwd(),
|
|
510
1017
|
env: process.env,
|
|
511
1018
|
exitAfterSignal: (code) => process.exit(code),
|
|
1019
|
+
homeApi: createNodeRivusHome({ deploymentInspector: createRivusHomeDeploymentInspector({ packageManifestPath: join(packageDirectory, "package.json") }) }),
|
|
512
1020
|
loadBootstrap: (specifier) => import(toImportSpecifier(specifier)),
|
|
1021
|
+
homeDirectory: homedir(),
|
|
513
1022
|
nodeExecutable: process.execPath,
|
|
514
1023
|
nodeVersion: process.versions.node,
|
|
515
1024
|
packageManifestPath: join(packageDirectory, "package.json"),
|