@rivus/agent 0.14.4 → 0.15.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/dist/bootstrap/pi-feishu.d.ts +2 -2
- package/dist/bootstrap/pi-feishu.js +4118 -388
- package/dist/chunks/index.d.ts +117 -1
- package/dist/chunks/pi.js +56 -19
- package/dist/chunks/rivus-daemon-cli.js +452 -144
- package/dist/chunks/rivus-model-management-wire.js +344 -0
- package/dist/chunks/rivus-plugin-testkit.js +1 -1
- package/dist/chunks/{tool-input-digest.js → rivus-tool.js} +67 -67
- package/dist/chunks/src.js +1885 -1687
- package/dist/cli.js +152 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/mcp.js +1 -1
- package/dist/pi.d.ts +2 -0
- package/dist/pi.js +1 -1
- package/examples/pi-feishu-deployment.bootstrap.ts +557 -462
- package/package.json +5 -3
- package/skills/runtime-management/SKILL.md +61 -0
|
@@ -1,144 +1,14 @@
|
|
|
1
|
+
import { t as createSha256Digest } from "./sha256-digest.js";
|
|
1
2
|
import { f as isRivusRuntimeToolId, o as createRivusHostToolDescriptorProvider, p as deepFreeze, t as createRivusAgentCatalog } from "./rivus-agent-definition-resolver.js";
|
|
2
3
|
import { l as narrowBackgroundSessionDefinition } from "./background-session-authority.js";
|
|
3
|
-
import { t as createSha256Digest } from "./sha256-digest.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { Cause, Deferred, Effect, Either, Exit, Option } from "effect";
|
|
6
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
7
|
-
import { lstat, open, readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
7
|
+
import { access, chmod, constants, lstat, mkdir, open, readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
8
8
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
9
|
-
import {
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
10
|
import { pathToFileURL } from "node:url";
|
|
11
|
-
|
|
12
|
-
function createRuntimeCache() {
|
|
13
|
-
const entries = /* @__PURE__ */ new Map();
|
|
14
|
-
const serial = Effect.unsafeMakeSemaphore(1);
|
|
15
|
-
const reserve = (key) => serial.withPermits(1)(Effect.gen(function* () {
|
|
16
|
-
const current = entries.get(key);
|
|
17
|
-
if (current) return {
|
|
18
|
-
created: false,
|
|
19
|
-
entry: current
|
|
20
|
-
};
|
|
21
|
-
const entry = {
|
|
22
|
-
deferred: yield* Deferred.make(),
|
|
23
|
-
initializationStarted: false,
|
|
24
|
-
key
|
|
25
|
-
};
|
|
26
|
-
entries.set(key, entry);
|
|
27
|
-
return {
|
|
28
|
-
created: true,
|
|
29
|
-
entry
|
|
30
|
-
};
|
|
31
|
-
}));
|
|
32
|
-
const start = (entry, create) => Effect.uninterruptible(Effect.gen(function* () {
|
|
33
|
-
if (!(yield* serial.withPermits(1)(Effect.sync(() => {
|
|
34
|
-
if (entry.initializationStarted) return false;
|
|
35
|
-
entry.initializationStarted = true;
|
|
36
|
-
return true;
|
|
37
|
-
})))) return;
|
|
38
|
-
const initialization = create().pipe(Effect.tapError(() => serial.withPermits(1)(Effect.sync(() => {
|
|
39
|
-
if (entries.get(entry.key) === entry) entries.delete(entry.key);
|
|
40
|
-
}))), Effect.exit, Effect.flatMap((exit) => Deferred.done(entry.deferred, exit)), Effect.asVoid);
|
|
41
|
-
yield* Effect.forkDaemon(initialization);
|
|
42
|
-
}));
|
|
43
|
-
const getOrCreate = (key, create) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
44
|
-
const selected = yield* reserve(key);
|
|
45
|
-
yield* start(selected.entry, create);
|
|
46
|
-
return {
|
|
47
|
-
entry: selected.entry,
|
|
48
|
-
runtime: yield* restore(Deferred.await(selected.entry.deferred))
|
|
49
|
-
};
|
|
50
|
-
}));
|
|
51
|
-
return {
|
|
52
|
-
drain: () => serial.withPermits(1)(Effect.sync(() => {
|
|
53
|
-
const drained = Object.freeze([...entries.values()]);
|
|
54
|
-
entries.clear();
|
|
55
|
-
return drained;
|
|
56
|
-
})),
|
|
57
|
-
getExisting: (key) => Effect.gen(function* () {
|
|
58
|
-
const entry = yield* serial.withPermits(1)(Effect.sync(() => entries.get(key)));
|
|
59
|
-
if (!entry) return void 0;
|
|
60
|
-
return {
|
|
61
|
-
entry,
|
|
62
|
-
runtime: yield* Deferred.await(entry.deferred)
|
|
63
|
-
};
|
|
64
|
-
}),
|
|
65
|
-
getOrCreate,
|
|
66
|
-
isCurrent: (key, entry) => serial.withPermits(1)(Effect.sync(() => entries.get(key) === entry)),
|
|
67
|
-
reserve,
|
|
68
|
-
size: () => serial.withPermits(1)(Effect.sync(() => entries.size)),
|
|
69
|
-
start
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
function disposeRuntimeCacheEntries(options) {
|
|
73
|
-
const disposal = Effect.gen(function* () {
|
|
74
|
-
const completions = yield* Effect.forEach(options.entries, (entry) => Effect.gen(function* () {
|
|
75
|
-
const completion = yield* Deferred.make();
|
|
76
|
-
yield* Effect.forkDaemon(Deferred.await(entry.deferred).pipe(Effect.flatMap(options.dispose), Effect.exit, Effect.flatMap((exit) => Deferred.succeed(completion, exit)), Effect.asVoid));
|
|
77
|
-
return completion;
|
|
78
|
-
}), { concurrency: "unbounded" });
|
|
79
|
-
const failures = (yield* Effect.forEach(completions, Deferred.await, { concurrency: "unbounded" })).filter(Exit.isFailure).map(({ cause }) => Cause.squash(cause));
|
|
80
|
-
if (failures.length > 0) return yield* Effect.fail(new AggregateError(failures, options.failureMessage));
|
|
81
|
-
});
|
|
82
|
-
return options.timeout ? disposal.pipe(Effect.timeoutFail({
|
|
83
|
-
duration: options.timeout.milliseconds,
|
|
84
|
-
onTimeout: options.timeout.onTimeout
|
|
85
|
-
})) : disposal;
|
|
86
|
-
}
|
|
87
|
-
function invokeRuntimeControl(runtime, control) {
|
|
88
|
-
return runtime.pipe(Effect.flatMap((selected) => selected ? control(selected) ?? Effect.succeed(false) : Effect.succeed(false)));
|
|
89
|
-
}
|
|
90
|
-
//#endregion
|
|
91
|
-
//#region src/adapters/agent/runtime/process-agent-runtime-adapter.ts
|
|
92
|
-
function toEffectAgentRuntimeInput(input) {
|
|
93
|
-
const onUpdate = input.onUpdate;
|
|
94
|
-
return {
|
|
95
|
-
...runtimeInputFields(input),
|
|
96
|
-
...onUpdate ? { onUpdate: (update) => Effect.tryPromise({
|
|
97
|
-
try: async () => onUpdate(update),
|
|
98
|
-
catch: (failure) => failure
|
|
99
|
-
}) } : {}
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
function toProcessAgentRuntimeInput(input, runEffect) {
|
|
103
|
-
const onUpdate = input.onUpdate;
|
|
104
|
-
return {
|
|
105
|
-
...runtimeInputFields(input),
|
|
106
|
-
...onUpdate ? { onUpdate: (update) => runEffect(onUpdate(update)) } : {}
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
function toEffectAgentRuntime(runtime, runEffect) {
|
|
110
|
-
const cancel = runtime.cancel?.bind(runtime);
|
|
111
|
-
const dispose = runtime.dispose?.bind(runtime);
|
|
112
|
-
const steer = runtime.steer?.bind(runtime);
|
|
113
|
-
return {
|
|
114
|
-
...runtime.concurrency ? { concurrency: runtime.concurrency } : {},
|
|
115
|
-
...cancel ? { cancel: (input) => Effect.tryPromise({
|
|
116
|
-
try: () => cancel(input),
|
|
117
|
-
catch: (failure) => failure
|
|
118
|
-
}) } : {},
|
|
119
|
-
...dispose ? { dispose: () => Effect.tryPromise({
|
|
120
|
-
try: async () => dispose(),
|
|
121
|
-
catch: (failure) => failure
|
|
122
|
-
}) } : {},
|
|
123
|
-
run: (input) => Effect.tryPromise({
|
|
124
|
-
try: () => runtime.run(toProcessAgentRuntimeInput(input, runEffect)),
|
|
125
|
-
catch: (failure) => failure
|
|
126
|
-
}),
|
|
127
|
-
...steer ? { steer: (input) => Effect.tryPromise({
|
|
128
|
-
try: () => steer(input),
|
|
129
|
-
catch: (failure) => failure
|
|
130
|
-
}) } : {}
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
function runtimeInputFields(input) {
|
|
134
|
-
return {
|
|
135
|
-
...input.invocation ? { invocation: input.invocation } : {},
|
|
136
|
-
...input.payload === void 0 ? {} : { payload: input.payload },
|
|
137
|
-
sessionKey: input.sessionKey,
|
|
138
|
-
text: input.text
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
//#endregion
|
|
11
|
+
import { constants as constants$1 } from "node:fs";
|
|
142
12
|
//#region src/platform/home/config/runtime/local-env-file.ts
|
|
143
13
|
var LocalEnvFileError = class extends Error {
|
|
144
14
|
constructor(message) {
|
|
@@ -195,6 +65,310 @@ function unescapeDoubleQuotedValue(value) {
|
|
|
195
65
|
});
|
|
196
66
|
}
|
|
197
67
|
//#endregion
|
|
68
|
+
//#region src/platform/home/config/model-management/rivus-model-management-config-migration.ts
|
|
69
|
+
const RIVUS_MODEL_MANAGEMENT_ENABLED_ENV = "RIVUS_MODEL_MANAGEMENT_ENABLED";
|
|
70
|
+
const RIVUS_MODEL_MANAGEMENT_OWNER_OPEN_ID_ENV = "RIVUS_MODEL_MANAGEMENT_OWNER_OPEN_ID";
|
|
71
|
+
const RIVUS_MODEL_MANAGEMENT_TENANT_KEY_ENV = "RIVUS_MODEL_MANAGEMENT_TENANT_KEY";
|
|
72
|
+
const RIVUS_MODEL_MANAGEMENT_REQUIRE_APPROVAL_ENV = "RIVUS_MODEL_MANAGEMENT_REQUIRE_APPROVAL";
|
|
73
|
+
var RivusModelManagementConfigError = class extends Error {
|
|
74
|
+
code;
|
|
75
|
+
name = "RivusModelManagementConfigError";
|
|
76
|
+
constructor(code, message) {
|
|
77
|
+
super(message);
|
|
78
|
+
this.code = code;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
/** Parse the Home capability flags without accepting truthy arbitrary strings. */
|
|
82
|
+
function parseRivusModelManagementHomeConfig(env) {
|
|
83
|
+
const enabled = parseRivusModelManagementEnabled(env[RIVUS_MODEL_MANAGEMENT_ENABLED_ENV]);
|
|
84
|
+
const requireApproval = parseStrictBoolean(env[RIVUS_MODEL_MANAGEMENT_REQUIRE_APPROVAL_ENV], RIVUS_MODEL_MANAGEMENT_REQUIRE_APPROVAL_ENV, false);
|
|
85
|
+
const ownerOpenId = optional$1(env[RIVUS_MODEL_MANAGEMENT_OWNER_OPEN_ID_ENV]);
|
|
86
|
+
const tenantKey = optional$1(env[RIVUS_MODEL_MANAGEMENT_TENANT_KEY_ENV]);
|
|
87
|
+
if (enabled && !ownerOpenId) throw new RivusModelManagementConfigError("owner_missing", `${RIVUS_MODEL_MANAGEMENT_OWNER_OPEN_ID_ENV} is required when model management is enabled`);
|
|
88
|
+
if (enabled && !tenantKey) throw new RivusModelManagementConfigError("tenant_missing", `${RIVUS_MODEL_MANAGEMENT_TENANT_KEY_ENV} is required when model management is enabled`);
|
|
89
|
+
return Object.freeze({
|
|
90
|
+
enabled,
|
|
91
|
+
...ownerOpenId ? { ownerOpenId } : {},
|
|
92
|
+
requireApproval,
|
|
93
|
+
...tenantKey ? { tenantKey } : {}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function parseRivusModelManagementEnabled(value) {
|
|
97
|
+
return parseStrictBoolean(value, RIVUS_MODEL_MANAGEMENT_ENABLED_ENV, false);
|
|
98
|
+
}
|
|
99
|
+
function parseStrictBoolean(value, variable, defaultValue) {
|
|
100
|
+
if (value === void 0) return defaultValue;
|
|
101
|
+
const normalized = value.trim();
|
|
102
|
+
if (normalized === "true") return true;
|
|
103
|
+
if (normalized === "false") return false;
|
|
104
|
+
throw new RivusModelManagementConfigError("invalid_enabled", `${variable} must be true or false`);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Migrate only the legacy model selection at the explicit enable/disable
|
|
108
|
+
* boundary. The helper does not read or write managed model state.
|
|
109
|
+
*/
|
|
110
|
+
async function migrateRivusModelManagementConfig(input) {
|
|
111
|
+
if (typeof input.managementEnabled !== "boolean") throw new RivusModelManagementConfigError("invalid_enabled", "model management enabled state must be boolean");
|
|
112
|
+
if (input.managementEnabled) return enableModelManagement(input);
|
|
113
|
+
return disableModelManagement(input, parseModelReference(input.knownGoodProvider, input.knownGoodModel));
|
|
114
|
+
}
|
|
115
|
+
async function enableModelManagement(input) {
|
|
116
|
+
if (hasAmbientModelOverride(input.env)) return {
|
|
117
|
+
initialModel: parseConfiguredModel(input.env.PI_MODEL),
|
|
118
|
+
source: "ambient",
|
|
119
|
+
status: "enabled"
|
|
120
|
+
};
|
|
121
|
+
return {
|
|
122
|
+
initialModel: parseConfiguredModel((await loadEnvFile(requireEnvFilePath(input.envFilePath), input.env)).PI_MODEL),
|
|
123
|
+
source: "env-file",
|
|
124
|
+
status: "enabled"
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function disableModelManagement(input, knownGood) {
|
|
128
|
+
const envFilePath = requireEnvFilePath(input.envFilePath);
|
|
129
|
+
if (hasAmbientModelOverride(input.env)) throw new RivusModelManagementConfigError("ambient_model_override", "ambient PI_MODEL overrides the original env source and prevents verified model readback");
|
|
130
|
+
const original = await readWritableEnvFile(envFilePath);
|
|
131
|
+
const existingModel = parseOptionalConfiguredModel((await loadEnvFile(envFilePath, input.env)).PI_MODEL);
|
|
132
|
+
if (existingModel && sameModel(existingModel, knownGood)) return {
|
|
133
|
+
restoredModel: knownGood,
|
|
134
|
+
source: "env-file",
|
|
135
|
+
status: "unchanged"
|
|
136
|
+
};
|
|
137
|
+
const next = replacePiModel(original, knownGood);
|
|
138
|
+
try {
|
|
139
|
+
await input.writeAtomicTextFile(envFilePath, next, { durable: true });
|
|
140
|
+
} catch {
|
|
141
|
+
throw new RivusModelManagementConfigError("env_source_write_failed", "the original env source write did not complete with a known durable result");
|
|
142
|
+
}
|
|
143
|
+
let readBack;
|
|
144
|
+
try {
|
|
145
|
+
readBack = parseConfiguredModel((await loadMergedLocalEnvFile(envFilePath, input.env)).PI_MODEL);
|
|
146
|
+
} catch {
|
|
147
|
+
throw new RivusModelManagementConfigError("readback_mismatch", "the original env source could not be verified after restoring PI_MODEL");
|
|
148
|
+
}
|
|
149
|
+
if (readBack.provider !== knownGood.provider || readBack.model !== knownGood.model) throw new RivusModelManagementConfigError("readback_mismatch", "the original env source did not read back the known-good PI_MODEL");
|
|
150
|
+
return {
|
|
151
|
+
restoredModel: knownGood,
|
|
152
|
+
source: "env-file",
|
|
153
|
+
status: "exported"
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function parseModelReference(provider, model) {
|
|
157
|
+
if (!isModelPart(provider) || !isModelPart(model)) throw new RivusModelManagementConfigError("invalid_model", "known-good model provider and id are invalid");
|
|
158
|
+
return Object.freeze({
|
|
159
|
+
model,
|
|
160
|
+
provider
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function parseConfiguredModel(value) {
|
|
164
|
+
const configured = optional$1(value);
|
|
165
|
+
if (!configured) throw new RivusModelManagementConfigError("model_missing", "PI_MODEL is missing from the effective legacy config");
|
|
166
|
+
const separator = configured.indexOf("/");
|
|
167
|
+
if (separator <= 0 || separator === configured.length - 1 || configured.indexOf("/", separator + 1) !== -1) throw new RivusModelManagementConfigError("invalid_model", "PI_MODEL must use provider/model form");
|
|
168
|
+
return parseModelReference(configured.slice(0, separator), configured.slice(separator + 1));
|
|
169
|
+
}
|
|
170
|
+
function parseOptionalConfiguredModel(value) {
|
|
171
|
+
if (!optional$1(value)) return void 0;
|
|
172
|
+
try {
|
|
173
|
+
return parseConfiguredModel(value);
|
|
174
|
+
} catch {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function sameModel(left, right) {
|
|
179
|
+
return left.provider === right.provider && left.model === right.model;
|
|
180
|
+
}
|
|
181
|
+
async function loadEnvFile(envFilePath, env) {
|
|
182
|
+
try {
|
|
183
|
+
return await loadMergedLocalEnvFile(envFilePath, env);
|
|
184
|
+
} catch {
|
|
185
|
+
throw new RivusModelManagementConfigError("env_source_unreadable", "the original env source could not be loaded");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function readWritableEnvFile(envFilePath) {
|
|
189
|
+
try {
|
|
190
|
+
const contents = await readFile(envFilePath, "utf8");
|
|
191
|
+
if (((await stat(envFilePath)).mode & 146) === 0) throw new RivusModelManagementConfigError("env_source_unwritable", "the original env source is not writable");
|
|
192
|
+
await access(envFilePath, constants.W_OK);
|
|
193
|
+
return contents;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
if (error instanceof RivusModelManagementConfigError) throw error;
|
|
196
|
+
if (isPermissionError(error)) throw new RivusModelManagementConfigError("env_source_unwritable", "the original env source is not writable");
|
|
197
|
+
throw new RivusModelManagementConfigError("env_source_unreadable", "the original env source is unavailable");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function replacePiModel(contents, model) {
|
|
201
|
+
const modelValue = `${model.provider}/${model.model}`;
|
|
202
|
+
const parts = contents.split(/(\r\n|\n|\r)/);
|
|
203
|
+
let replaced = false;
|
|
204
|
+
for (let index = 0; index < parts.length; index += 2) {
|
|
205
|
+
const line = parts[index];
|
|
206
|
+
if (line === void 0) continue;
|
|
207
|
+
const match = line.match(/^(\s*(?:export\s+)?PI_MODEL\s*=\s*)(.*)$/);
|
|
208
|
+
if (!match) continue;
|
|
209
|
+
const inlineComment = match[2].match(/(\s+#.*)$/)?.[1] ?? "";
|
|
210
|
+
parts[index] = `${match[1]}${modelValue}${inlineComment}`;
|
|
211
|
+
replaced = true;
|
|
212
|
+
}
|
|
213
|
+
if (replaced) return parts.join("");
|
|
214
|
+
const newline = contents.includes("\r\n") ? "\r\n" : contents.includes("\n") ? "\n" : "\n";
|
|
215
|
+
if (!contents) return `PI_MODEL=${modelValue}${newline}`;
|
|
216
|
+
return /(?:\r\n|\n|\r)$/.test(contents) ? `${contents}PI_MODEL=${modelValue}${newline}` : `${contents}${newline}PI_MODEL=${modelValue}`;
|
|
217
|
+
}
|
|
218
|
+
function requireEnvFilePath(value) {
|
|
219
|
+
const normalized = optional$1(value);
|
|
220
|
+
if (!normalized) throw new RivusModelManagementConfigError("env_source_missing", "an explicit writable original env source is required for model management migration");
|
|
221
|
+
return normalized;
|
|
222
|
+
}
|
|
223
|
+
function hasAmbientModelOverride(env) {
|
|
224
|
+
return Object.prototype.hasOwnProperty.call(env, "PI_MODEL") && env.PI_MODEL !== void 0;
|
|
225
|
+
}
|
|
226
|
+
function isModelPart(value) {
|
|
227
|
+
return value.trim() === value && value.length > 0 && !/[/\s]/.test(value);
|
|
228
|
+
}
|
|
229
|
+
function optional$1(value) {
|
|
230
|
+
return value?.trim() || void 0;
|
|
231
|
+
}
|
|
232
|
+
function isPermissionError(error) {
|
|
233
|
+
return typeof error === "object" && error !== null && "code" in error && (error.code === "EACCES" || error.code === "EPERM" || error.code === "EROFS");
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region src/platform/home/config/model-management/rivus-runtime-management-skill-installer.ts
|
|
237
|
+
const SKILL_NAME = "runtime-management";
|
|
238
|
+
const SKILL_FILE = "SKILL.md";
|
|
239
|
+
const INSTALL_METADATA_FILE = ".rivus-install.json";
|
|
240
|
+
async function installRivusRuntimeManagementSkill(options) {
|
|
241
|
+
const writeAtomicTextFile = options.writeAtomicTextFile;
|
|
242
|
+
const skillDirectory = join(options.homeDirectory ?? homedir(), ".agents", "skills", SKILL_NAME);
|
|
243
|
+
const destination = join(skillDirectory, SKILL_FILE);
|
|
244
|
+
const metadataPath = join(skillDirectory, INSTALL_METADATA_FILE);
|
|
245
|
+
const source = await readFile(options.sourcePath, "utf8");
|
|
246
|
+
const sourceDigest = digest(source);
|
|
247
|
+
await mkdir(skillDirectory, { recursive: true });
|
|
248
|
+
const current = await readOptional(destination);
|
|
249
|
+
if (current === void 0) {
|
|
250
|
+
await writeAtomicTextFile(destination, source, { mode: 384 });
|
|
251
|
+
await writeMetadata(metadataPath, sourceDigest, writeAtomicTextFile);
|
|
252
|
+
return {
|
|
253
|
+
destination,
|
|
254
|
+
requiresRefresh: true,
|
|
255
|
+
sourceDigest,
|
|
256
|
+
status: "installed"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
const currentDigest = digest(current);
|
|
260
|
+
if (currentDigest === sourceDigest) {
|
|
261
|
+
await writeMetadata(metadataPath, sourceDigest, writeAtomicTextFile);
|
|
262
|
+
return {
|
|
263
|
+
destination,
|
|
264
|
+
requiresRefresh: false,
|
|
265
|
+
sourceDigest,
|
|
266
|
+
status: "unchanged"
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
if ((await readMetadata(metadataPath))?.installedDigest !== currentDigest) return {
|
|
270
|
+
destination,
|
|
271
|
+
requiresRefresh: false,
|
|
272
|
+
sourceDigest,
|
|
273
|
+
status: "conflict"
|
|
274
|
+
};
|
|
275
|
+
await writeAtomicTextFile(destination, source, { mode: 384 });
|
|
276
|
+
await writeMetadata(metadataPath, sourceDigest, writeAtomicTextFile);
|
|
277
|
+
return {
|
|
278
|
+
destination,
|
|
279
|
+
requiresRefresh: true,
|
|
280
|
+
sourceDigest,
|
|
281
|
+
status: "updated"
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function digest(value) {
|
|
285
|
+
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
286
|
+
}
|
|
287
|
+
async function readOptional(path) {
|
|
288
|
+
try {
|
|
289
|
+
return await readFile(path, "utf8");
|
|
290
|
+
} catch (error) {
|
|
291
|
+
if (isMissing$1(error)) return void 0;
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async function readMetadata(path) {
|
|
296
|
+
const contents = await readOptional(path);
|
|
297
|
+
if (contents === void 0) return void 0;
|
|
298
|
+
try {
|
|
299
|
+
const value = JSON.parse(contents);
|
|
300
|
+
if (!isRecord(value) || typeof value.installedDigest !== "string") return void 0;
|
|
301
|
+
return { installedDigest: value.installedDigest };
|
|
302
|
+
} catch {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async function writeMetadata(path, installedDigest, writeAtomicTextFile) {
|
|
307
|
+
await writeAtomicTextFile(path, `${JSON.stringify({
|
|
308
|
+
installedDigest,
|
|
309
|
+
version: 1
|
|
310
|
+
}, null, 2)}\n`, { mode: 384 });
|
|
311
|
+
}
|
|
312
|
+
function isRecord(value) {
|
|
313
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
314
|
+
}
|
|
315
|
+
function isMissing$1(error) {
|
|
316
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
317
|
+
}
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region src/platform/home/config/model-management/rivus-model-management-cli-launcher.ts
|
|
320
|
+
const BIN_DIRECTORY_NAME = "bin";
|
|
321
|
+
const LAUNCHER_NAME = "rivus";
|
|
322
|
+
const PRIVATE_MODE = 448;
|
|
323
|
+
/**
|
|
324
|
+
* Install the private launcher used by model-management child processes.
|
|
325
|
+
*
|
|
326
|
+
* The launcher contains only fixed absolute executable paths. Per-Run
|
|
327
|
+
* context, credentials, and other authority data stay in the spawn
|
|
328
|
+
* environment owned by the Host and are never persisted in this file.
|
|
329
|
+
*/
|
|
330
|
+
async function installRivusModelManagementCliLauncher(options) {
|
|
331
|
+
assertAbsolutePath("directory", options.directory);
|
|
332
|
+
assertAbsolutePath("nodeExecutable", options.nodeExecutable);
|
|
333
|
+
assertAbsolutePath("cliEntryPath", options.cliEntryPath);
|
|
334
|
+
const binDirectory = join(options.directory, BIN_DIRECTORY_NAME);
|
|
335
|
+
const launcherPath = join(binDirectory, LAUNCHER_NAME);
|
|
336
|
+
await mkdir(binDirectory, {
|
|
337
|
+
mode: PRIVATE_MODE,
|
|
338
|
+
recursive: true
|
|
339
|
+
});
|
|
340
|
+
await chmod(binDirectory, PRIVATE_MODE);
|
|
341
|
+
await chmodIfPresent(launcherPath, PRIVATE_MODE);
|
|
342
|
+
await options.writeAtomicTextFile(launcherPath, renderLauncher(options), { mode: PRIVATE_MODE });
|
|
343
|
+
await chmod(launcherPath, PRIVATE_MODE);
|
|
344
|
+
return Object.freeze({
|
|
345
|
+
binDirectory,
|
|
346
|
+
launcherPath
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
function renderLauncher(options) {
|
|
350
|
+
return `#!/bin/sh\nexec ${shellQuote(options.nodeExecutable)} ${shellQuote(options.cliEntryPath)} "$@"\n`;
|
|
351
|
+
}
|
|
352
|
+
function shellQuote(value) {
|
|
353
|
+
if (value.includes("\0")) throw new Error("launcher paths must not contain NUL bytes");
|
|
354
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
355
|
+
}
|
|
356
|
+
function assertAbsolutePath(name, value) {
|
|
357
|
+
if (value.length === 0 || !isAbsolute(value)) throw new Error(`${name} must be an absolute path`);
|
|
358
|
+
if (value.includes("\0")) throw new Error(`${name} must not contain NUL bytes`);
|
|
359
|
+
}
|
|
360
|
+
async function chmodIfPresent(path, mode) {
|
|
361
|
+
try {
|
|
362
|
+
await chmod(path, mode);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
if (isMissing(error)) return;
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function isMissing(error) {
|
|
369
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
370
|
+
}
|
|
371
|
+
//#endregion
|
|
198
372
|
//#region src/platform/home/config/runtime/rivus-daemon-config.ts
|
|
199
373
|
var RivusDaemonConfigError = class {
|
|
200
374
|
variable;
|
|
@@ -287,6 +461,145 @@ function optionalThinkingLevel(value) {
|
|
|
287
461
|
return Effect.fail(new RivusDaemonConfigError("PI_THINKING_LEVEL", "PI_THINKING_LEVEL must be one of off, minimal, low, medium, high, xhigh"));
|
|
288
462
|
}
|
|
289
463
|
//#endregion
|
|
464
|
+
//#region src/bootstrap/deployment/effect-runner.ts
|
|
465
|
+
async function runDeploymentProcessEffect(effect) {
|
|
466
|
+
const exit = await Effect.runPromiseExit(effect);
|
|
467
|
+
if (Exit.isSuccess(exit)) return exit.value;
|
|
468
|
+
const failure = Cause.failureOption(exit.cause);
|
|
469
|
+
throw Option.isSome(failure) ? failure.value : Cause.squash(exit.cause);
|
|
470
|
+
}
|
|
471
|
+
//#endregion
|
|
472
|
+
//#region src/platform/runtime/runtime-cache.ts
|
|
473
|
+
function createRuntimeCache() {
|
|
474
|
+
const entries = /* @__PURE__ */ new Map();
|
|
475
|
+
const serial = Effect.unsafeMakeSemaphore(1);
|
|
476
|
+
const reserve = (key) => serial.withPermits(1)(Effect.gen(function* () {
|
|
477
|
+
const current = entries.get(key);
|
|
478
|
+
if (current) return {
|
|
479
|
+
created: false,
|
|
480
|
+
entry: current
|
|
481
|
+
};
|
|
482
|
+
const entry = {
|
|
483
|
+
deferred: yield* Deferred.make(),
|
|
484
|
+
initializationStarted: false,
|
|
485
|
+
key
|
|
486
|
+
};
|
|
487
|
+
entries.set(key, entry);
|
|
488
|
+
return {
|
|
489
|
+
created: true,
|
|
490
|
+
entry
|
|
491
|
+
};
|
|
492
|
+
}));
|
|
493
|
+
const start = (entry, create) => Effect.uninterruptible(Effect.gen(function* () {
|
|
494
|
+
if (!(yield* serial.withPermits(1)(Effect.sync(() => {
|
|
495
|
+
if (entry.initializationStarted) return false;
|
|
496
|
+
entry.initializationStarted = true;
|
|
497
|
+
return true;
|
|
498
|
+
})))) return;
|
|
499
|
+
const initialization = create().pipe(Effect.tapError(() => serial.withPermits(1)(Effect.sync(() => {
|
|
500
|
+
if (entries.get(entry.key) === entry) entries.delete(entry.key);
|
|
501
|
+
}))), Effect.exit, Effect.flatMap((exit) => Deferred.done(entry.deferred, exit)), Effect.asVoid);
|
|
502
|
+
yield* Effect.forkDaemon(initialization);
|
|
503
|
+
}));
|
|
504
|
+
const getOrCreate = (key, create) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
505
|
+
const selected = yield* reserve(key);
|
|
506
|
+
yield* start(selected.entry, create);
|
|
507
|
+
return {
|
|
508
|
+
entry: selected.entry,
|
|
509
|
+
runtime: yield* restore(Deferred.await(selected.entry.deferred))
|
|
510
|
+
};
|
|
511
|
+
}));
|
|
512
|
+
return {
|
|
513
|
+
drain: () => serial.withPermits(1)(Effect.sync(() => {
|
|
514
|
+
const drained = Object.freeze([...entries.values()]);
|
|
515
|
+
entries.clear();
|
|
516
|
+
return drained;
|
|
517
|
+
})),
|
|
518
|
+
getExisting: (key) => Effect.gen(function* () {
|
|
519
|
+
const entry = yield* serial.withPermits(1)(Effect.sync(() => entries.get(key)));
|
|
520
|
+
if (!entry) return void 0;
|
|
521
|
+
return {
|
|
522
|
+
entry,
|
|
523
|
+
runtime: yield* Deferred.await(entry.deferred)
|
|
524
|
+
};
|
|
525
|
+
}),
|
|
526
|
+
getOrCreate,
|
|
527
|
+
isCurrent: (key, entry) => serial.withPermits(1)(Effect.sync(() => entries.get(key) === entry)),
|
|
528
|
+
reserve,
|
|
529
|
+
size: () => serial.withPermits(1)(Effect.sync(() => entries.size)),
|
|
530
|
+
start
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function disposeRuntimeCacheEntries(options) {
|
|
534
|
+
const disposal = Effect.gen(function* () {
|
|
535
|
+
const completions = yield* Effect.forEach(options.entries, (entry) => Effect.gen(function* () {
|
|
536
|
+
const completion = yield* Deferred.make();
|
|
537
|
+
yield* Effect.forkDaemon(Deferred.await(entry.deferred).pipe(Effect.flatMap(options.dispose), Effect.exit, Effect.flatMap((exit) => Deferred.succeed(completion, exit)), Effect.asVoid));
|
|
538
|
+
return completion;
|
|
539
|
+
}), { concurrency: "unbounded" });
|
|
540
|
+
const failures = (yield* Effect.forEach(completions, Deferred.await, { concurrency: "unbounded" })).filter(Exit.isFailure).map(({ cause }) => Cause.squash(cause));
|
|
541
|
+
if (failures.length > 0) return yield* Effect.fail(new AggregateError(failures, options.failureMessage));
|
|
542
|
+
});
|
|
543
|
+
return options.timeout ? disposal.pipe(Effect.timeoutFail({
|
|
544
|
+
duration: options.timeout.milliseconds,
|
|
545
|
+
onTimeout: options.timeout.onTimeout
|
|
546
|
+
})) : disposal;
|
|
547
|
+
}
|
|
548
|
+
function invokeRuntimeControl(runtime, control) {
|
|
549
|
+
return runtime.pipe(Effect.flatMap((selected) => selected ? control(selected) ?? Effect.succeed(false) : Effect.succeed(false)));
|
|
550
|
+
}
|
|
551
|
+
//#endregion
|
|
552
|
+
//#region src/adapters/agent/runtime/process-agent-runtime-adapter.ts
|
|
553
|
+
function toEffectAgentRuntimeInput(input) {
|
|
554
|
+
const onUpdate = input.onUpdate;
|
|
555
|
+
return {
|
|
556
|
+
...runtimeInputFields(input),
|
|
557
|
+
...onUpdate ? { onUpdate: (update) => Effect.tryPromise({
|
|
558
|
+
try: async () => onUpdate(update),
|
|
559
|
+
catch: (failure) => failure
|
|
560
|
+
}) } : {}
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function toProcessAgentRuntimeInput(input, runEffect) {
|
|
564
|
+
const onUpdate = input.onUpdate;
|
|
565
|
+
return {
|
|
566
|
+
...runtimeInputFields(input),
|
|
567
|
+
...onUpdate ? { onUpdate: (update) => runEffect(onUpdate(update)) } : {}
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function toEffectAgentRuntime(runtime, runEffect) {
|
|
571
|
+
const cancel = runtime.cancel?.bind(runtime);
|
|
572
|
+
const dispose = runtime.dispose?.bind(runtime);
|
|
573
|
+
const steer = runtime.steer?.bind(runtime);
|
|
574
|
+
return {
|
|
575
|
+
...runtime.concurrency ? { concurrency: runtime.concurrency } : {},
|
|
576
|
+
...cancel ? { cancel: (input) => Effect.tryPromise({
|
|
577
|
+
try: () => cancel(input),
|
|
578
|
+
catch: (failure) => failure
|
|
579
|
+
}) } : {},
|
|
580
|
+
...dispose ? { dispose: () => Effect.tryPromise({
|
|
581
|
+
try: async () => dispose(),
|
|
582
|
+
catch: (failure) => failure
|
|
583
|
+
}) } : {},
|
|
584
|
+
run: (input) => Effect.tryPromise({
|
|
585
|
+
try: () => runtime.run(toProcessAgentRuntimeInput(input, runEffect)),
|
|
586
|
+
catch: (failure) => failure
|
|
587
|
+
}),
|
|
588
|
+
...steer ? { steer: (input) => Effect.tryPromise({
|
|
589
|
+
try: () => steer(input),
|
|
590
|
+
catch: (failure) => failure
|
|
591
|
+
}) } : {}
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
function runtimeInputFields(input) {
|
|
595
|
+
return {
|
|
596
|
+
...input.invocation ? { invocation: input.invocation } : {},
|
|
597
|
+
...input.payload === void 0 ? {} : { payload: input.payload },
|
|
598
|
+
sessionKey: input.sessionKey,
|
|
599
|
+
text: input.text
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
//#endregion
|
|
290
603
|
//#region src/adapters/feishu/config/feishu-endpoint-credentials.ts
|
|
291
604
|
var FeishuEndpointCredentialError = class extends Error {
|
|
292
605
|
name = "FeishuEndpointCredentialError";
|
|
@@ -723,7 +1036,7 @@ function parsePositiveInteger(value) {
|
|
|
723
1036
|
return value && /^[1-9]\d*$/.test(value) ? Number(value) : void 0;
|
|
724
1037
|
}
|
|
725
1038
|
async function readPrivateTextFile(filePath, label, maxBytes) {
|
|
726
|
-
const handle = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
1039
|
+
const handle = await open(filePath, constants$1.O_RDONLY | constants$1.O_NOFOLLOW);
|
|
727
1040
|
try {
|
|
728
1041
|
const metadata = await handle.stat();
|
|
729
1042
|
if (!metadata.isFile()) throw new Error(`${label} must be a regular file`);
|
|
@@ -754,6 +1067,7 @@ Project commands:
|
|
|
754
1067
|
rivus check-config
|
|
755
1068
|
rivus init [directory]
|
|
756
1069
|
rivus doctor [directory] [--env-file <path>]
|
|
1070
|
+
rivus model <status|set|rollback> ...
|
|
757
1071
|
|
|
758
1072
|
Options:
|
|
759
1073
|
--bootstrap <module> Module exporting createRivusDaemonProcess(context)
|
|
@@ -3158,14 +3472,6 @@ function trimOptional(value) {
|
|
|
3158
3472
|
return value?.trim() || void 0;
|
|
3159
3473
|
}
|
|
3160
3474
|
//#endregion
|
|
3161
|
-
//#region src/bootstrap/deployment/effect-runner.ts
|
|
3162
|
-
async function runDeploymentProcessEffect(effect) {
|
|
3163
|
-
const exit = await Effect.runPromiseExit(effect);
|
|
3164
|
-
if (Exit.isSuccess(exit)) return exit.value;
|
|
3165
|
-
const failure = Cause.failureOption(exit.cause);
|
|
3166
|
-
throw Option.isSome(failure) ? failure.value : Cause.squash(exit.cause);
|
|
3167
|
-
}
|
|
3168
|
-
//#endregion
|
|
3169
3475
|
//#region src/bootstrap/deployment/rivus-deployment-cli-process.ts
|
|
3170
3476
|
async function createRivusDeploymentCliProcess(factory, context) {
|
|
3171
3477
|
const adapters = await factory(context);
|
|
@@ -3387,6 +3693,8 @@ function runRivusDaemonCli(options) {
|
|
|
3387
3693
|
try: () => parsed.manifestPath ? createCliDeploymentDaemon(module.createRivusDeploymentAdapters, {
|
|
3388
3694
|
argv: options.argv,
|
|
3389
3695
|
env,
|
|
3696
|
+
...parsed.envFilePath ? { envFilePath: parsed.envFilePath } : {},
|
|
3697
|
+
environmentOverrides: options.env,
|
|
3390
3698
|
manifestPath: parsed.manifestPath,
|
|
3391
3699
|
...options.pluginPackageManifestPath ? { pluginPackageManifestPath: options.pluginPackageManifestPath } : {}
|
|
3392
3700
|
}) : createCliLegacyDaemon(module, {
|
|
@@ -3541,4 +3849,4 @@ function hasRecoveryRunner(daemon) {
|
|
|
3541
3849
|
return typeof daemon.openRecoveryControl === "function";
|
|
3542
3850
|
}
|
|
3543
3851
|
//#endregion
|
|
3544
|
-
export {
|
|
3852
|
+
export { validateRivusDeploymentManifest as A, createRuntimeCache as B, createStableId as C, RivusDeploymentManifestError as D, createAgentHostRuntimePool as E, FeishuEndpointCredentialError as F, loadRivusDaemonConfig as G, invokeRuntimeControl as H, resolveFeishuEndpointCredentials as I, migrateRivusModelManagementConfig as J, installRivusModelManagementCliLauncher as K, toEffectAgentRuntime as L, OpenClawEnvImportError as M, createRivusEnvFromOpenClawConfig as N, loadRivusDeploymentManifest as O, formatRivusEnvFile as P, toEffectAgentRuntimeInput as R, createEffectAgentInstanceRegistry as S, AgentRuntimeDisposed as T, runDeploymentProcessEffect as U, disposeRuntimeCacheEntries as V, RivusDaemonConfigError as W, loadMergedLocalEnvFile as X, parseRivusModelManagementHomeConfig as Y, resolveRivusPluginModule as _, RivusDeploymentBackgroundSessionReadinessError as a, createEffectAgentHost as b, RivusDeploymentDaemonLifecycleError as c, createNodeRivusPluginModuleLoader as d, loadNodeRivusPluginModule as f, findTrustedPackageRoot as g, isPathWithin as h, RivusDeploymentAutomationReadinessError as i, createRivusDaemonShutdownController as j, InvalidRivusProjectSpace as k, RivusPluginLoadError as l, validateTrustedModulePath as m, createRivusDeploymentCliProcess as n, RivusDeploymentReadinessError as o, resolveNodeRivusPluginModulePath as p, installRivusRuntimeManagementSkill as q, resolveRivusProjectSpace as r, createRivusDeploymentControl as s, runRivusDaemonCli as t, resolveRivusDeployment as u, createProcessDeploymentControlPorts as v, AgentInstanceBusy as w, AgentInstanceConflict as x, InvalidAgentHostBinding as y, toProcessAgentRuntimeInput as z };
|