@vellumai/cli 0.4.36 → 0.4.40
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/package.json +2 -2
- package/src/__tests__/multi-local.test.ts +275 -0
- package/src/__tests__/skills-uninstall.test.ts +203 -0
- package/src/commands/client.ts +23 -7
- package/src/commands/hatch.ts +38 -42
- package/src/commands/ps.ts +32 -12
- package/src/commands/retire.ts +48 -12
- package/src/commands/skills.ts +130 -5
- package/src/commands/sleep.ts +25 -6
- package/src/commands/use.ts +44 -0
- package/src/commands/wake.ts +25 -16
- package/src/index.ts +5 -49
- package/src/lib/assistant-config.ts +226 -3
- package/src/lib/constants.ts +6 -0
- package/src/lib/local.ts +189 -49
- package/src/lib/status-emoji.ts +3 -0
package/src/commands/wake.ts
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "fs";
|
|
2
|
-
import { homedir } from "os";
|
|
3
2
|
import { join } from "path";
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
defaultLocalResources,
|
|
6
|
+
resolveTargetAssistant,
|
|
7
|
+
} from "../lib/assistant-config.js";
|
|
6
8
|
import { isProcessAlive, stopProcessByPidFile } from "../lib/process";
|
|
7
9
|
import { startLocalDaemon, startGateway } from "../lib/local";
|
|
8
10
|
|
|
9
11
|
export async function wake(): Promise<void> {
|
|
10
12
|
const args = process.argv.slice(3);
|
|
11
13
|
if (args.includes("--help") || args.includes("-h")) {
|
|
12
|
-
console.log("Usage: vellum wake [options]");
|
|
14
|
+
console.log("Usage: vellum wake [<name>] [options]");
|
|
13
15
|
console.log("");
|
|
14
16
|
console.log("Start the assistant and gateway processes.");
|
|
15
17
|
console.log("");
|
|
18
|
+
console.log("Arguments:");
|
|
19
|
+
console.log(
|
|
20
|
+
" <name> Name of the assistant to start (default: active or only local)",
|
|
21
|
+
);
|
|
22
|
+
console.log("");
|
|
16
23
|
console.log("Options:");
|
|
17
24
|
console.log(
|
|
18
25
|
" --watch Run assistant and gateway in watch mode (hot reload on source changes)",
|
|
@@ -21,19 +28,20 @@ export async function wake(): Promise<void> {
|
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
const watch = args.includes("--watch");
|
|
31
|
+
const nameArg = args.find((a) => !a.startsWith("-"));
|
|
32
|
+
const entry = resolveTargetAssistant(nameArg);
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
const hasLocal = assistants.some((a) => a.cloud === "local");
|
|
27
|
-
if (!hasLocal) {
|
|
34
|
+
if (entry.cloud && entry.cloud !== "local") {
|
|
28
35
|
console.error(
|
|
29
|
-
|
|
36
|
+
`Error: 'vellum wake' only works with local assistants. '${entry.assistantId}' is a ${entry.cloud} instance.`,
|
|
30
37
|
);
|
|
31
38
|
process.exit(1);
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const
|
|
41
|
+
const resources = entry.resources ?? defaultLocalResources();
|
|
42
|
+
|
|
43
|
+
const pidFile = resources.pidFile;
|
|
44
|
+
const socketFile = resources.socketPath;
|
|
37
45
|
|
|
38
46
|
// Check if daemon is already running
|
|
39
47
|
let daemonRunning = false;
|
|
@@ -61,11 +69,12 @@ export async function wake(): Promise<void> {
|
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
if (!daemonRunning) {
|
|
64
|
-
await startLocalDaemon(watch);
|
|
72
|
+
await startLocalDaemon(watch, resources);
|
|
65
73
|
}
|
|
66
74
|
|
|
67
|
-
// Start gateway
|
|
68
|
-
|
|
75
|
+
// Start gateway
|
|
76
|
+
{
|
|
77
|
+
const vellumDir = join(resources.instanceDir, ".vellum");
|
|
69
78
|
const gatewayPidFile = join(vellumDir, "gateway.pid");
|
|
70
79
|
const { alive, pid } = isProcessAlive(gatewayPidFile);
|
|
71
80
|
if (alive) {
|
|
@@ -75,14 +84,14 @@ export async function wake(): Promise<void> {
|
|
|
75
84
|
`Gateway running (pid ${pid}) — restarting in watch mode...`,
|
|
76
85
|
);
|
|
77
86
|
await stopProcessByPidFile(gatewayPidFile, "gateway");
|
|
78
|
-
await startGateway(undefined, watch);
|
|
87
|
+
await startGateway(undefined, watch, resources);
|
|
79
88
|
} else {
|
|
80
89
|
console.log(`Gateway already running (pid ${pid}).`);
|
|
81
90
|
}
|
|
82
91
|
} else {
|
|
83
|
-
await startGateway(undefined, watch);
|
|
92
|
+
await startGateway(undefined, watch, resources);
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
|
|
87
|
-
console.log("
|
|
96
|
+
console.log("Wake complete.");
|
|
88
97
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import { createRequire } from "node:module";
|
|
5
|
-
import { dirname, join } from "node:path";
|
|
6
|
-
import { spawn } from "node:child_process";
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
-
|
|
9
3
|
import cliPkg from "../package.json";
|
|
10
4
|
import { client } from "./commands/client";
|
|
11
5
|
import { hatch } from "./commands/hatch";
|
|
@@ -18,6 +12,7 @@ import { skills } from "./commands/skills";
|
|
|
18
12
|
import { sleep } from "./commands/sleep";
|
|
19
13
|
import { ssh } from "./commands/ssh";
|
|
20
14
|
import { tunnel } from "./commands/tunnel";
|
|
15
|
+
import { use } from "./commands/use";
|
|
21
16
|
import { wake } from "./commands/wake";
|
|
22
17
|
|
|
23
18
|
const commands = {
|
|
@@ -33,37 +28,13 @@ const commands = {
|
|
|
33
28
|
sleep,
|
|
34
29
|
ssh,
|
|
35
30
|
tunnel,
|
|
31
|
+
use,
|
|
36
32
|
wake,
|
|
37
33
|
whoami,
|
|
38
34
|
} as const;
|
|
39
35
|
|
|
40
36
|
type CommandName = keyof typeof commands;
|
|
41
37
|
|
|
42
|
-
function resolveAssistantEntry(): string | undefined {
|
|
43
|
-
// When installed globally, resolve from node_modules
|
|
44
|
-
try {
|
|
45
|
-
const require = createRequire(import.meta.url);
|
|
46
|
-
const assistantPkgPath =
|
|
47
|
-
require.resolve("@vellumai/assistant/package.json");
|
|
48
|
-
return join(dirname(assistantPkgPath), "src", "index.ts");
|
|
49
|
-
} catch {
|
|
50
|
-
// For local development, resolve from sibling directory
|
|
51
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
52
|
-
const localPath = join(
|
|
53
|
-
__dirname,
|
|
54
|
-
"..",
|
|
55
|
-
"..",
|
|
56
|
-
"assistant",
|
|
57
|
-
"src",
|
|
58
|
-
"index.ts",
|
|
59
|
-
);
|
|
60
|
-
if (existsSync(localPath)) {
|
|
61
|
-
return localPath;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
38
|
async function main() {
|
|
68
39
|
const args = process.argv.slice(2);
|
|
69
40
|
const commandName = args[0];
|
|
@@ -77,11 +48,7 @@ async function main() {
|
|
|
77
48
|
console.log("Usage: vellum <command> [options]");
|
|
78
49
|
console.log("");
|
|
79
50
|
console.log("Commands:");
|
|
80
|
-
console.log(" autonomy View and configure autonomy tiers");
|
|
81
51
|
console.log(" client Connect to a hatched assistant");
|
|
82
|
-
console.log(" config Manage configuration");
|
|
83
|
-
console.log(" contacts Manage assistant contacts");
|
|
84
|
-
console.log(" email Email operations (provider-agnostic)");
|
|
85
52
|
console.log(" hatch Create a new assistant instance");
|
|
86
53
|
console.log(" login Log in to the Vellum platform");
|
|
87
54
|
console.log(" logout Log out of the Vellum platform");
|
|
@@ -95,6 +62,7 @@ async function main() {
|
|
|
95
62
|
console.log(" sleep Stop the assistant process");
|
|
96
63
|
console.log(" ssh SSH into a remote assistant instance");
|
|
97
64
|
console.log(" tunnel Create a tunnel for a locally hosted assistant");
|
|
65
|
+
console.log(" use Set the active assistant for commands");
|
|
98
66
|
console.log(" wake Start the assistant and gateway");
|
|
99
67
|
console.log(" whoami Show current logged-in user");
|
|
100
68
|
process.exit(0);
|
|
@@ -103,20 +71,8 @@ async function main() {
|
|
|
103
71
|
const command = commands[commandName as CommandName];
|
|
104
72
|
|
|
105
73
|
if (!command) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const child = spawn("bun", ["run", assistantEntry, ...args], {
|
|
109
|
-
stdio: "inherit",
|
|
110
|
-
});
|
|
111
|
-
child.on("exit", (code) => {
|
|
112
|
-
process.exit(code ?? 1);
|
|
113
|
-
});
|
|
114
|
-
} else {
|
|
115
|
-
console.error(`Unknown command: ${commandName}`);
|
|
116
|
-
console.error("Install the full stack with: bun install -g vellum");
|
|
117
|
-
process.exit(1);
|
|
118
|
-
}
|
|
119
|
-
return;
|
|
74
|
+
console.error(`Unknown command: ${commandName}`);
|
|
75
|
+
process.exit(1);
|
|
120
76
|
}
|
|
121
77
|
|
|
122
78
|
try {
|
|
@@ -1,10 +1,49 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_DAEMON_PORT,
|
|
7
|
+
DEFAULT_GATEWAY_PORT,
|
|
8
|
+
DEFAULT_QDRANT_PORT,
|
|
9
|
+
} from "./constants.js";
|
|
10
|
+
import { probePort } from "./port-probe.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Per-instance resource paths and ports. Each local assistant instance gets
|
|
14
|
+
* its own directory tree, ports, and socket so multiple instances can run
|
|
15
|
+
* side-by-side without conflicts.
|
|
16
|
+
*/
|
|
17
|
+
export interface LocalInstanceResources {
|
|
18
|
+
/**
|
|
19
|
+
* Instance-specific data root. The first local assistant uses `~` (workspace
|
|
20
|
+
* at `~/.vellum`); subsequent assistants use
|
|
21
|
+
* `~/.local/share/vellum/assistants/<name>/` (workspace at
|
|
22
|
+
* `~/.local/share/vellum/assistants/<name>/.vellum`).
|
|
23
|
+
* The daemon's `.vellum/` directory lives inside it. Equivalent to
|
|
24
|
+
* `AssistantEntry.baseDataDir` minus the trailing `/.vellum` suffix —
|
|
25
|
+
* `baseDataDir` is kept on the flat entry for legacy lockfile compat.
|
|
26
|
+
*/
|
|
27
|
+
instanceDir: string;
|
|
28
|
+
/** HTTP port for the daemon runtime server */
|
|
29
|
+
daemonPort: number;
|
|
30
|
+
/** HTTP port for the gateway */
|
|
31
|
+
gatewayPort: number;
|
|
32
|
+
/** HTTP port for the Qdrant vector store */
|
|
33
|
+
qdrantPort: number;
|
|
34
|
+
/** Absolute path to the Unix domain socket for IPC */
|
|
35
|
+
socketPath: string;
|
|
36
|
+
/** Absolute path to the daemon PID file */
|
|
37
|
+
pidFile: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
5
40
|
export interface AssistantEntry {
|
|
6
41
|
assistantId: string;
|
|
7
42
|
runtimeUrl: string;
|
|
43
|
+
/** Loopback URL for same-machine health checks (e.g. `http://127.0.0.1:7831`).
|
|
44
|
+
* Avoids mDNS resolution issues when the machine checks its own gateway. */
|
|
45
|
+
localUrl?: string;
|
|
46
|
+
/** @deprecated Use `resources.instanceDir` for multi-instance entries. Legacy equivalent of `join(instanceDir, ".vellum")`. */
|
|
8
47
|
baseDataDir?: string;
|
|
9
48
|
bearerToken?: string;
|
|
10
49
|
cloud: string;
|
|
@@ -16,10 +55,13 @@ export interface AssistantEntry {
|
|
|
16
55
|
sshUser?: string;
|
|
17
56
|
zone?: string;
|
|
18
57
|
hatchedAt?: string;
|
|
58
|
+
/** Per-instance resource config. Present for local entries in multi-instance setups. */
|
|
59
|
+
resources?: LocalInstanceResources;
|
|
19
60
|
}
|
|
20
61
|
|
|
21
62
|
interface LockfileData {
|
|
22
63
|
assistants?: AssistantEntry[];
|
|
64
|
+
activeAssistant?: string;
|
|
23
65
|
platformBaseUrl?: string;
|
|
24
66
|
[key: string]: unknown;
|
|
25
67
|
}
|
|
@@ -91,14 +133,70 @@ export function findAssistantByName(name: string): AssistantEntry | null {
|
|
|
91
133
|
}
|
|
92
134
|
|
|
93
135
|
export function removeAssistantEntry(assistantId: string): void {
|
|
94
|
-
const
|
|
95
|
-
|
|
136
|
+
const data = readLockfile();
|
|
137
|
+
const entries = (data.assistants ?? []).filter(
|
|
138
|
+
(e: AssistantEntry) => e.assistantId !== assistantId,
|
|
139
|
+
);
|
|
140
|
+
data.assistants = entries;
|
|
141
|
+
// Clear active assistant if it matches the removed entry
|
|
142
|
+
if (data.activeAssistant === assistantId) {
|
|
143
|
+
delete data.activeAssistant;
|
|
144
|
+
}
|
|
145
|
+
writeLockfile(data);
|
|
96
146
|
}
|
|
97
147
|
|
|
98
148
|
export function loadAllAssistants(): AssistantEntry[] {
|
|
99
149
|
return readAssistants();
|
|
100
150
|
}
|
|
101
151
|
|
|
152
|
+
export function getActiveAssistant(): string | null {
|
|
153
|
+
const data = readLockfile();
|
|
154
|
+
return data.activeAssistant ?? null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function setActiveAssistant(assistantId: string): void {
|
|
158
|
+
const data = readLockfile();
|
|
159
|
+
data.activeAssistant = assistantId;
|
|
160
|
+
writeLockfile(data);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Resolve which assistant to target for a command. Priority:
|
|
165
|
+
* 1. Explicit name argument
|
|
166
|
+
* 2. Active assistant set via `vellum use`
|
|
167
|
+
* 3. Sole local assistant (when exactly one exists)
|
|
168
|
+
*/
|
|
169
|
+
export function resolveTargetAssistant(nameArg?: string): AssistantEntry {
|
|
170
|
+
if (nameArg) {
|
|
171
|
+
const entry = findAssistantByName(nameArg);
|
|
172
|
+
if (!entry) {
|
|
173
|
+
console.error(`No assistant found with name '${nameArg}'.`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
return entry;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const active = getActiveAssistant();
|
|
180
|
+
if (active) {
|
|
181
|
+
const entry = findAssistantByName(active);
|
|
182
|
+
if (entry) return entry;
|
|
183
|
+
// Active assistant no longer exists in lockfile — fall through
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const all = readAssistants();
|
|
187
|
+
const locals = all.filter((e) => e.cloud === "local");
|
|
188
|
+
if (locals.length === 1) return locals[0];
|
|
189
|
+
|
|
190
|
+
if (locals.length === 0) {
|
|
191
|
+
console.error("No local assistant found. Run 'vellum hatch local' first.");
|
|
192
|
+
} else {
|
|
193
|
+
console.error(
|
|
194
|
+
`Multiple assistants found. Set an active assistant with 'vellum use <name>'.`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
|
|
102
200
|
export function saveAssistantEntry(entry: AssistantEntry): void {
|
|
103
201
|
const entries = readAssistants().filter(
|
|
104
202
|
(e) => e.assistantId !== entry.assistantId,
|
|
@@ -107,6 +205,131 @@ export function saveAssistantEntry(entry: AssistantEntry): void {
|
|
|
107
205
|
writeAssistants(entries);
|
|
108
206
|
}
|
|
109
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Scan upward from `basePort` to find an available port. A port is considered
|
|
210
|
+
* available when `probePort()` returns false (nothing listening). Scans up to
|
|
211
|
+
* 100 ports above the base before giving up.
|
|
212
|
+
*/
|
|
213
|
+
async function findAvailablePort(
|
|
214
|
+
basePort: number,
|
|
215
|
+
excludedPorts: number[] = [],
|
|
216
|
+
): Promise<number> {
|
|
217
|
+
const maxOffset = 100;
|
|
218
|
+
for (let offset = 0; offset < maxOffset; offset++) {
|
|
219
|
+
const port = basePort + offset;
|
|
220
|
+
if (excludedPorts.includes(port)) continue;
|
|
221
|
+
const inUse = await probePort(port);
|
|
222
|
+
if (!inUse) return port;
|
|
223
|
+
}
|
|
224
|
+
throw new Error(
|
|
225
|
+
`Could not find an available port scanning from ${basePort} to ${basePort + maxOffset - 1}`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Allocate an isolated set of resources for a named local instance.
|
|
231
|
+
* The first local assistant gets `instanceDir = ~` with default ports (same as
|
|
232
|
+
* legacy single-instance layout). Subsequent assistants are placed under
|
|
233
|
+
* `~/.local/share/vellum/assistants/<name>/` with scanned ports.
|
|
234
|
+
*/
|
|
235
|
+
export async function allocateLocalResources(
|
|
236
|
+
instanceName: string,
|
|
237
|
+
): Promise<LocalInstanceResources> {
|
|
238
|
+
// First local assistant gets the home directory — identical to legacy layout.
|
|
239
|
+
const existingLocals = loadAllAssistants().filter((e) => e.cloud === "local");
|
|
240
|
+
if (existingLocals.length === 0) {
|
|
241
|
+
return defaultLocalResources();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const instanceDir = join(
|
|
245
|
+
homedir(),
|
|
246
|
+
".local",
|
|
247
|
+
"share",
|
|
248
|
+
"vellum",
|
|
249
|
+
"assistants",
|
|
250
|
+
instanceName,
|
|
251
|
+
);
|
|
252
|
+
mkdirSync(instanceDir, { recursive: true });
|
|
253
|
+
|
|
254
|
+
// Collect ports already assigned to other local instances in the lockfile.
|
|
255
|
+
// Even if those instances are stopped, we must avoid reusing their ports
|
|
256
|
+
// to prevent binding collisions when both are woken.
|
|
257
|
+
const reservedPorts: number[] = [];
|
|
258
|
+
for (const entry of loadAllAssistants()) {
|
|
259
|
+
if (entry.cloud !== "local") continue;
|
|
260
|
+
if (entry.resources) {
|
|
261
|
+
reservedPorts.push(
|
|
262
|
+
entry.resources.daemonPort,
|
|
263
|
+
entry.resources.gatewayPort,
|
|
264
|
+
entry.resources.qdrantPort,
|
|
265
|
+
);
|
|
266
|
+
} else {
|
|
267
|
+
// Legacy entries without resources use the default ports
|
|
268
|
+
reservedPorts.push(
|
|
269
|
+
DEFAULT_DAEMON_PORT,
|
|
270
|
+
DEFAULT_GATEWAY_PORT,
|
|
271
|
+
DEFAULT_QDRANT_PORT,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Allocate ports sequentially to avoid overlapping ranges assigning the
|
|
277
|
+
// same port to multiple services (e.g. daemon 7821-7920 overlaps gateway 7830-7929).
|
|
278
|
+
const daemonPort = await findAvailablePort(
|
|
279
|
+
DEFAULT_DAEMON_PORT,
|
|
280
|
+
reservedPorts,
|
|
281
|
+
);
|
|
282
|
+
const gatewayPort = await findAvailablePort(DEFAULT_GATEWAY_PORT, [
|
|
283
|
+
...reservedPorts,
|
|
284
|
+
daemonPort,
|
|
285
|
+
]);
|
|
286
|
+
const qdrantPort = await findAvailablePort(DEFAULT_QDRANT_PORT, [
|
|
287
|
+
...reservedPorts,
|
|
288
|
+
daemonPort,
|
|
289
|
+
gatewayPort,
|
|
290
|
+
]);
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
instanceDir,
|
|
294
|
+
daemonPort,
|
|
295
|
+
gatewayPort,
|
|
296
|
+
qdrantPort,
|
|
297
|
+
socketPath: join(instanceDir, ".vellum", "vellum.sock"),
|
|
298
|
+
pidFile: join(instanceDir, ".vellum", "vellum.pid"),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Return default resources representing the legacy single-instance layout.
|
|
304
|
+
* Used to normalize existing lockfile entries so callers can treat all local
|
|
305
|
+
* entries uniformly.
|
|
306
|
+
*/
|
|
307
|
+
export function defaultLocalResources(): LocalInstanceResources {
|
|
308
|
+
const vellumDir = join(homedir(), ".vellum");
|
|
309
|
+
return {
|
|
310
|
+
instanceDir: homedir(),
|
|
311
|
+
daemonPort: DEFAULT_DAEMON_PORT,
|
|
312
|
+
gatewayPort: DEFAULT_GATEWAY_PORT,
|
|
313
|
+
qdrantPort: DEFAULT_QDRANT_PORT,
|
|
314
|
+
socketPath: join(vellumDir, "vellum.sock"),
|
|
315
|
+
pidFile: join(vellumDir, "vellum.pid"),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Normalize existing lockfile entries so local entries include resource fields.
|
|
321
|
+
* Remote entries are left untouched. Returns a new array (does not mutate input).
|
|
322
|
+
*/
|
|
323
|
+
export function normalizeExistingEntryResources(
|
|
324
|
+
entries: AssistantEntry[],
|
|
325
|
+
): AssistantEntry[] {
|
|
326
|
+
return entries.map((entry) => {
|
|
327
|
+
if (entry.cloud !== "local") return entry;
|
|
328
|
+
if (entry.resources) return entry;
|
|
329
|
+
return { ...entry, resources: defaultLocalResources() };
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
110
333
|
/**
|
|
111
334
|
* Read the assistant config file and sync client-relevant values to the
|
|
112
335
|
* lockfile. This lets external tools (e.g. vel) discover the platform URL
|
package/src/lib/constants.ts
CHANGED
|
@@ -2,6 +2,12 @@ export const FIREWALL_TAG = "vellum-assistant";
|
|
|
2
2
|
export const GATEWAY_PORT = process.env.GATEWAY_PORT
|
|
3
3
|
? Number(process.env.GATEWAY_PORT)
|
|
4
4
|
: 7830;
|
|
5
|
+
|
|
6
|
+
/** Default ports used as scan start points for multi-instance allocation. */
|
|
7
|
+
export const DEFAULT_DAEMON_PORT = 7821;
|
|
8
|
+
export const DEFAULT_GATEWAY_PORT = 7830;
|
|
9
|
+
export const DEFAULT_QDRANT_PORT = 6333;
|
|
10
|
+
|
|
5
11
|
export const VALID_REMOTE_HOSTS = ["local", "gcp", "aws", "custom"] as const;
|
|
6
12
|
export type RemoteHost = (typeof VALID_REMOTE_HOSTS)[number];
|
|
7
13
|
export const VALID_SPECIES = ["openclaw", "vellum"] as const;
|