@vellumai/cli 0.4.32 → 0.4.33
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 +1 -1
- package/src/commands/client.ts +1 -17
- package/src/commands/contacts.ts +1 -19
- package/src/commands/hatch.ts +2 -2
- package/src/commands/ps.ts +1 -1
- package/src/lib/health-check.ts +10 -2
- package/src/lib/local.ts +0 -63
package/package.json
CHANGED
package/src/commands/client.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { readFileSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
|
|
4
1
|
import {
|
|
5
2
|
findAssistantByName,
|
|
6
3
|
loadLatestAssistant,
|
|
@@ -60,21 +57,8 @@ function parseArgs(): ParsedArgs {
|
|
|
60
57
|
process.env.RUNTIME_URL || entry?.runtimeUrl || FALLBACK_RUNTIME_URL;
|
|
61
58
|
let assistantId =
|
|
62
59
|
process.env.ASSISTANT_ID || entry?.assistantId || FALLBACK_ASSISTANT_ID;
|
|
63
|
-
|
|
60
|
+
const bearerToken =
|
|
64
61
|
process.env.RUNTIME_PROXY_BEARER_TOKEN || entry?.bearerToken || undefined;
|
|
65
|
-
|
|
66
|
-
// For local assistants, read the daemon's http-token file as a fallback
|
|
67
|
-
// when the lockfile doesn't include a bearer token.
|
|
68
|
-
if (!bearerToken && entry?.cloud === "local") {
|
|
69
|
-
const tokenDir =
|
|
70
|
-
entry.baseDataDir ?? join(process.env.HOME ?? "", ".vellum");
|
|
71
|
-
try {
|
|
72
|
-
const token = readFileSync(join(tokenDir, "http-token"), "utf-8").trim();
|
|
73
|
-
if (token) bearerToken = token;
|
|
74
|
-
} catch {
|
|
75
|
-
// Token file may not exist
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
62
|
const species: Species = (entry?.species as Species) ?? "vellum";
|
|
79
63
|
|
|
80
64
|
for (let i = 0; i < flagArgs.length; i++) {
|
package/src/commands/contacts.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
1
|
import { loadLatestAssistant } from "../lib/assistant-config";
|
|
6
2
|
import { GATEWAY_PORT } from "../lib/constants.js";
|
|
7
3
|
|
|
@@ -17,21 +13,7 @@ function getGatewayUrl(): string {
|
|
|
17
13
|
|
|
18
14
|
function getBearerToken(): string | undefined {
|
|
19
15
|
const entry = loadLatestAssistant();
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const tokenPath = join(
|
|
23
|
-
process.env.BASE_DATA_DIR?.trim() || homedir(),
|
|
24
|
-
".vellum",
|
|
25
|
-
"http-token",
|
|
26
|
-
);
|
|
27
|
-
if (existsSync(tokenPath)) {
|
|
28
|
-
const token = readFileSync(tokenPath, "utf-8").trim();
|
|
29
|
-
if (token) return token;
|
|
30
|
-
}
|
|
31
|
-
} catch {
|
|
32
|
-
// ignore
|
|
33
|
-
}
|
|
34
|
-
return undefined;
|
|
16
|
+
return entry?.bearerToken;
|
|
35
17
|
}
|
|
36
18
|
|
|
37
19
|
function buildHeaders(): Record<string, string> {
|
package/src/commands/hatch.ts
CHANGED
|
@@ -756,8 +756,8 @@ async function hatchLocal(
|
|
|
756
756
|
throw error;
|
|
757
757
|
}
|
|
758
758
|
|
|
759
|
-
// Read the bearer token written by the daemon so the
|
|
760
|
-
// with the gateway
|
|
759
|
+
// Read the bearer token (JWT) written by the daemon so the CLI can
|
|
760
|
+
// authenticate with the gateway.
|
|
761
761
|
let bearerToken: string | undefined;
|
|
762
762
|
try {
|
|
763
763
|
const token = readFileSync(join(baseDataDir, "http-token"), "utf-8").trim();
|
package/src/commands/ps.ts
CHANGED
|
@@ -403,7 +403,7 @@ async function listAllAssistants(): Promise<void> {
|
|
|
403
403
|
|
|
404
404
|
await Promise.all(
|
|
405
405
|
assistants.map(async (a, rowIndex) => {
|
|
406
|
-
const health = await checkHealth(a.runtimeUrl);
|
|
406
|
+
const health = await checkHealth(a.runtimeUrl, a.bearerToken);
|
|
407
407
|
|
|
408
408
|
const infoParts = [a.runtimeUrl];
|
|
409
409
|
if (a.cloud) infoParts.push(`cloud: ${a.cloud}`);
|
package/src/lib/health-check.ts
CHANGED
|
@@ -12,18 +12,26 @@ export interface HealthCheckResult {
|
|
|
12
12
|
|
|
13
13
|
export async function checkHealth(
|
|
14
14
|
runtimeUrl: string,
|
|
15
|
+
bearerToken?: string,
|
|
15
16
|
): Promise<HealthCheckResult> {
|
|
16
17
|
try {
|
|
17
|
-
const url = `${runtimeUrl}/
|
|
18
|
+
const url = `${runtimeUrl}/v1/health`;
|
|
18
19
|
const controller = new AbortController();
|
|
19
20
|
const timeoutId = setTimeout(
|
|
20
21
|
() => controller.abort(),
|
|
21
22
|
HEALTH_CHECK_TIMEOUT_MS,
|
|
22
23
|
);
|
|
23
24
|
|
|
25
|
+
const headers: Record<string, string> = {
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
};
|
|
28
|
+
if (bearerToken) {
|
|
29
|
+
headers["Authorization"] = `Bearer ${bearerToken}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
const response = await fetch(url, {
|
|
25
33
|
signal: controller.signal,
|
|
26
|
-
headers
|
|
34
|
+
headers,
|
|
27
35
|
});
|
|
28
36
|
|
|
29
37
|
clearTimeout(timeoutId);
|
package/src/lib/local.ts
CHANGED
|
@@ -829,73 +829,10 @@ export async function startGateway(
|
|
|
829
829
|
process.env.GATEWAY_DEFAULT_ASSISTANT_ID ||
|
|
830
830
|
loadLatestAssistant()?.assistantId;
|
|
831
831
|
|
|
832
|
-
// Read the bearer token so the gateway can authenticate proxied requests
|
|
833
|
-
// (e.g. from paired iOS devices). Respect VELLUM_HTTP_TOKEN_PATH and
|
|
834
|
-
// BASE_DATA_DIR for consistency with gateway/config.ts and the daemon.
|
|
835
|
-
const httpTokenPath =
|
|
836
|
-
process.env.VELLUM_HTTP_TOKEN_PATH ??
|
|
837
|
-
join(
|
|
838
|
-
process.env.BASE_DATA_DIR?.trim() || homedir(),
|
|
839
|
-
".vellum",
|
|
840
|
-
"http-token",
|
|
841
|
-
);
|
|
842
|
-
let runtimeProxyBearerToken: string | undefined;
|
|
843
|
-
try {
|
|
844
|
-
const tok = readFileSync(httpTokenPath, "utf-8").trim();
|
|
845
|
-
if (tok) runtimeProxyBearerToken = tok;
|
|
846
|
-
} catch {
|
|
847
|
-
// Token file doesn't exist yet — daemon hasn't written it.
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
// If no token is available (first startup — daemon hasn't written it yet),
|
|
851
|
-
// poll for the file to appear. On fresh installs the daemon may take 60s+
|
|
852
|
-
// for Qdrant download, migrations, and first-time init. Starting the
|
|
853
|
-
// gateway without auth is a security risk since the config is loaded once
|
|
854
|
-
// at startup and never reloads, so we fail rather than silently disabling auth.
|
|
855
|
-
if (!runtimeProxyBearerToken) {
|
|
856
|
-
console.log(" Waiting for bearer token file...");
|
|
857
|
-
const maxWait = 60000;
|
|
858
|
-
const pollInterval = 500;
|
|
859
|
-
const start = Date.now();
|
|
860
|
-
const pidFile = join(
|
|
861
|
-
process.env.BASE_DATA_DIR?.trim() || homedir(),
|
|
862
|
-
".vellum",
|
|
863
|
-
"vellum.pid",
|
|
864
|
-
);
|
|
865
|
-
while (Date.now() - start < maxWait) {
|
|
866
|
-
await new Promise((r) => setTimeout(r, pollInterval));
|
|
867
|
-
try {
|
|
868
|
-
const tok = readFileSync(httpTokenPath, "utf-8").trim();
|
|
869
|
-
if (tok) {
|
|
870
|
-
runtimeProxyBearerToken = tok;
|
|
871
|
-
break;
|
|
872
|
-
}
|
|
873
|
-
} catch {
|
|
874
|
-
// File still doesn't exist, keep polling.
|
|
875
|
-
}
|
|
876
|
-
// Check if the daemon process is still alive — no point waiting if it crashed
|
|
877
|
-
try {
|
|
878
|
-
const pid = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
|
|
879
|
-
if (pid) process.kill(pid, 0); // throws if process doesn't exist
|
|
880
|
-
} catch {
|
|
881
|
-
break; // daemon process is gone
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
if (!runtimeProxyBearerToken) {
|
|
887
|
-
throw new Error(
|
|
888
|
-
`Bearer token file not found at ${httpTokenPath} after 60s.\n` +
|
|
889
|
-
" The gateway cannot start without authentication — this would leave the proxy permanently unauthenticated.\n" +
|
|
890
|
-
" Ensure the daemon is running and has written the token file, or set VELLUM_HTTP_TOKEN_PATH to the correct path.",
|
|
891
|
-
);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
832
|
const gatewayEnv: Record<string, string> = {
|
|
895
833
|
...(process.env as Record<string, string>),
|
|
896
834
|
GATEWAY_RUNTIME_PROXY_ENABLED: "true",
|
|
897
835
|
GATEWAY_RUNTIME_PROXY_REQUIRE_AUTH: "true",
|
|
898
|
-
RUNTIME_PROXY_BEARER_TOKEN: runtimeProxyBearerToken,
|
|
899
836
|
RUNTIME_HTTP_PORT: process.env.RUNTIME_HTTP_PORT || "7821",
|
|
900
837
|
// Skip the drain window for locally-launched gateways — there is no load
|
|
901
838
|
// balancer draining connections, so waiting serves no purpose and causes
|