@vellumai/cli 0.11.2 → 0.11.3-staging.2
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/knip.json +1 -0
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +18 -0
- package/node_modules/@vellumai/ipc-server-utils/src/ipc-framing.ts +295 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +444 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +236 -0
- package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
- package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/LICENSE +20 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/README.md +38 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/bin/nanoid.js +55 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/index.browser.js +29 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/index.d.ts +106 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/index.js +47 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/nanoid.js +1 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/non-secure/index.d.ts +48 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/non-secure/index.js +21 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/package.json +46 -0
- package/node_modules/@vellumai/local-mode/node_modules/nanoid/url-alphabet/index.js +2 -0
- package/node_modules/@vellumai/local-mode/package.json +1 -0
- package/node_modules/@vellumai/local-mode/src/__tests__/environment.test.ts +63 -1
- package/node_modules/@vellumai/local-mode/src/__tests__/gateway-proxy.test.ts +503 -0
- package/node_modules/@vellumai/local-mode/src/__tests__/guardian-token.test.ts +203 -0
- package/node_modules/@vellumai/local-mode/src/__tests__/loopback-auth.test.ts +46 -1
- package/node_modules/@vellumai/local-mode/src/__tests__/package-boundary.test.ts +6 -4
- package/node_modules/@vellumai/local-mode/src/__tests__/pair.test.ts +621 -0
- package/node_modules/@vellumai/local-mode/src/__tests__/status.test.ts +5 -0
- package/node_modules/@vellumai/local-mode/src/__tests__/unpair.test.ts +232 -0
- package/node_modules/@vellumai/local-mode/src/config.ts +126 -20
- package/node_modules/@vellumai/local-mode/src/environment.ts +36 -15
- package/node_modules/@vellumai/local-mode/src/gateway-proxy.ts +313 -25
- package/node_modules/@vellumai/local-mode/src/guardian-token.ts +222 -13
- package/node_modules/@vellumai/local-mode/src/index.ts +35 -3
- package/node_modules/@vellumai/local-mode/src/lockfile-contract.test.ts +18 -0
- package/node_modules/@vellumai/local-mode/src/lockfile-contract.ts +72 -0
- package/node_modules/@vellumai/local-mode/src/lockfile.test.ts +130 -0
- package/node_modules/@vellumai/local-mode/src/lockfile.ts +134 -45
- package/node_modules/@vellumai/local-mode/src/pair.ts +354 -0
- package/node_modules/@vellumai/local-mode/src/paths.ts +49 -0
- package/node_modules/@vellumai/local-mode/src/status.ts +10 -8
- package/node_modules/@vellumai/local-mode/src/unpair.ts +101 -0
- package/node_modules/@vellumai/local-mode/src/util.ts +34 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/package.json +3 -1
- package/src/__tests__/client-token.test.ts +26 -1
- package/src/__tests__/guardian-token.test.ts +4 -0
- package/src/__tests__/nginx-ingress-command.test.ts +176 -16
- package/src/__tests__/nginx-ingress.test.ts +1821 -101
- package/src/__tests__/pair.test.ts +104 -2
- package/src/__tests__/sleep.test.ts +7 -3
- package/src/__tests__/tailscale-tunnel.test.ts +0 -1
- package/src/__tests__/tunnel.test.ts +1099 -55
- package/src/__tests__/wake.test.ts +166 -99
- package/src/commands/client.ts +182 -27
- package/src/commands/connect/import.ts +35 -156
- package/src/commands/nginx-ingress.ts +87 -110
- package/src/commands/pair.ts +84 -21
- package/src/commands/tunnel.ts +196 -85
- package/src/commands/upgrade.ts +3 -2
- package/src/commands/wake.ts +5 -163
- package/src/index.ts +1 -1
- package/src/lib/__tests__/local-ces.test.ts +10 -1
- package/src/lib/cloudflare-tunnel.ts +3 -16
- package/src/lib/environments/__tests__/paths.test.ts +20 -1
- package/src/lib/environments/paths.ts +29 -41
- package/src/lib/environments/resolve.ts +7 -5
- package/src/lib/guardian-token.ts +22 -62
- package/src/lib/ingress-config.ts +25 -0
- package/src/lib/local.ts +77 -20
- package/src/lib/nginx-ingress.ts +523 -76
- package/src/lib/ngrok.ts +154 -61
- package/src/lib/orphan-detection.test.ts +167 -0
- package/src/lib/orphan-detection.ts +40 -0
- package/src/lib/pair.test.ts +87 -0
- package/src/lib/pair.ts +55 -0
- package/src/lib/platform-client.ts +8 -5
- package/src/lib/tailscale-tunnel.ts +7 -18
- package/src/lib/tunnel-edge.ts +114 -0
- package/src/lib/xdg-log.ts +2 -2
|
@@ -11,9 +11,13 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
|
|
14
|
-
// Real assistant-config reads the lockfile from VELLUM_LOCKFILE_DIR.
|
|
14
|
+
// Real assistant-config reads the lockfile from VELLUM_LOCKFILE_DIR. Pin the
|
|
15
|
+
// environment too: the lockfile filename is environment-dependent, and a dev
|
|
16
|
+
// machine's persisted default environment would otherwise redirect the lookup.
|
|
15
17
|
const testDir = mkdtempSync(join(tmpdir(), "pair-command-test-"));
|
|
16
18
|
process.env.VELLUM_LOCKFILE_DIR = testDir;
|
|
19
|
+
const originalEnvironment = process.env.VELLUM_ENVIRONMENT;
|
|
20
|
+
process.env.VELLUM_ENVIRONMENT = "production";
|
|
17
21
|
|
|
18
22
|
import { buildAppConnectUrl, pair } from "../commands/pair.js";
|
|
19
23
|
|
|
@@ -55,6 +59,11 @@ describe("pair command", () => {
|
|
|
55
59
|
afterAll(() => {
|
|
56
60
|
rmSync(testDir, { recursive: true, force: true });
|
|
57
61
|
delete process.env.VELLUM_LOCKFILE_DIR;
|
|
62
|
+
if (originalEnvironment === undefined) {
|
|
63
|
+
delete process.env.VELLUM_ENVIRONMENT;
|
|
64
|
+
} else {
|
|
65
|
+
process.env.VELLUM_ENVIRONMENT = originalEnvironment;
|
|
66
|
+
}
|
|
58
67
|
});
|
|
59
68
|
|
|
60
69
|
test("POSTs a cli device-bound pair request and prints a decodable bundle", async () => {
|
|
@@ -967,6 +976,29 @@ describe("pair command", () => {
|
|
|
967
976
|
).toBe(
|
|
968
977
|
"vellum-assistant-dev://connect?url=https%3A%2F%2Fhost.example.ts.net%2Fassistant-123&code=a%2Bb%2Fc%3D",
|
|
969
978
|
);
|
|
979
|
+
// A label rides along as an encoded `name` param (spaces percent-encoded,
|
|
980
|
+
// not form-encoded, for the app's URLComponents parser); an empty one is
|
|
981
|
+
// omitted.
|
|
982
|
+
expect(
|
|
983
|
+
buildAppConnectUrl(
|
|
984
|
+
"vellum-assistant",
|
|
985
|
+
"https://pair.example.ts.net",
|
|
986
|
+
"device-code",
|
|
987
|
+
"My Homelab",
|
|
988
|
+
),
|
|
989
|
+
).toBe(
|
|
990
|
+
"vellum-assistant://connect?url=https%3A%2F%2Fpair.example.ts.net&code=device-code&name=My%20Homelab",
|
|
991
|
+
);
|
|
992
|
+
expect(
|
|
993
|
+
buildAppConnectUrl(
|
|
994
|
+
"vellum-assistant",
|
|
995
|
+
"https://pair.example.ts.net",
|
|
996
|
+
"device-code",
|
|
997
|
+
"",
|
|
998
|
+
),
|
|
999
|
+
).toBe(
|
|
1000
|
+
"vellum-assistant://connect?url=https%3A%2F%2Fpair.example.ts.net&code=device-code",
|
|
1001
|
+
);
|
|
970
1002
|
});
|
|
971
1003
|
|
|
972
1004
|
test("--qr --app emits an app connect URL alongside the browser URL", async () => {
|
|
@@ -1032,8 +1064,10 @@ describe("pair command", () => {
|
|
|
1032
1064
|
}
|
|
1033
1065
|
|
|
1034
1066
|
const out = JSON.parse(logs.join("\n"));
|
|
1067
|
+
// Without --label the connect link names the assistant after its lockfile
|
|
1068
|
+
// display name (the id here, since the entry has no name).
|
|
1035
1069
|
expect(out.appUrl).toBe(
|
|
1036
|
-
"vellum-assistant-dev://connect?url=https%3A%2F%2Fpair.example.ts.net&code=device-code",
|
|
1070
|
+
"vellum-assistant-dev://connect?url=https%3A%2F%2Fpair.example.ts.net&code=device-code&name=pair-test",
|
|
1037
1071
|
);
|
|
1038
1072
|
// The browser URL stays available as the no-app fallback.
|
|
1039
1073
|
expect(out.pairUrl).toBe(
|
|
@@ -1042,6 +1076,74 @@ describe("pair command", () => {
|
|
|
1042
1076
|
expect(out.deviceCode).toBe("device-code");
|
|
1043
1077
|
});
|
|
1044
1078
|
|
|
1079
|
+
test("--qr --app --label overrides the assistant name in the connect link", async () => {
|
|
1080
|
+
const origFetch = globalThis.fetch;
|
|
1081
|
+
globalThis.fetch = (async (url: string) => {
|
|
1082
|
+
if (url === `${LOCAL_URL}/v1/assistants/pair-test/feature-flags`) {
|
|
1083
|
+
return new Response(
|
|
1084
|
+
JSON.stringify({
|
|
1085
|
+
flags: [{ key: "web-remote-ingress", enabled: true }],
|
|
1086
|
+
}),
|
|
1087
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
if (url === `${LOCAL_URL}/v1/remote-web/pairing-challenge`) {
|
|
1091
|
+
return new Response(
|
|
1092
|
+
JSON.stringify({
|
|
1093
|
+
deviceCode: "device-code",
|
|
1094
|
+
userCode: "ABCD-EFGH",
|
|
1095
|
+
verificationUri: "https://pair.example.ts.net/assistant/pair",
|
|
1096
|
+
expiresAt: "2026-06-04T00:10:00.000Z",
|
|
1097
|
+
expiresInSeconds: 600,
|
|
1098
|
+
}),
|
|
1099
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
if (url === `${LOCAL_URL}/v1/remote-web/pairing-verification`) {
|
|
1103
|
+
return new Response(
|
|
1104
|
+
JSON.stringify({
|
|
1105
|
+
status: "approved",
|
|
1106
|
+
verificationUri: "https://pair.example.ts.net/assistant/pair",
|
|
1107
|
+
expiresAt: "2026-06-04T00:10:00.000Z",
|
|
1108
|
+
}),
|
|
1109
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
return new Response("not found", { status: 404 });
|
|
1113
|
+
}) as unknown as typeof fetch;
|
|
1114
|
+
|
|
1115
|
+
const logs: string[] = [];
|
|
1116
|
+
const logSpy = spyOn(console, "log").mockImplementation(
|
|
1117
|
+
(...a: unknown[]) => {
|
|
1118
|
+
logs.push(a.join(" "));
|
|
1119
|
+
},
|
|
1120
|
+
);
|
|
1121
|
+
|
|
1122
|
+
process.argv = [
|
|
1123
|
+
"bun",
|
|
1124
|
+
"vellum",
|
|
1125
|
+
"pair",
|
|
1126
|
+
"--qr",
|
|
1127
|
+
"--app",
|
|
1128
|
+
"--label",
|
|
1129
|
+
"Homelab",
|
|
1130
|
+
"--url",
|
|
1131
|
+
"https://pair.example.ts.net",
|
|
1132
|
+
"--json",
|
|
1133
|
+
];
|
|
1134
|
+
try {
|
|
1135
|
+
await pair();
|
|
1136
|
+
} finally {
|
|
1137
|
+
logSpy.mockRestore();
|
|
1138
|
+
globalThis.fetch = origFetch;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
const out = JSON.parse(logs.join("\n"));
|
|
1142
|
+
expect(out.appUrl).toBe(
|
|
1143
|
+
"vellum-assistant://connect?url=https%3A%2F%2Fpair.example.ts.net&code=device-code&name=Homelab",
|
|
1144
|
+
);
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1045
1147
|
test("--app without --qr is refused", async () => {
|
|
1046
1148
|
let fetchCalled = false;
|
|
1047
1149
|
const origFetch = globalThis.fetch;
|
|
@@ -56,9 +56,11 @@ mock.module("../lib/guardian-token.js", () => ({
|
|
|
56
56
|
loadGuardianToken: loadGuardianTokenMock,
|
|
57
57
|
}));
|
|
58
58
|
|
|
59
|
-
// Stub the token-refresh helper
|
|
60
|
-
//
|
|
61
|
-
//
|
|
59
|
+
// Stub the token-refresh helper. Pass-through by default: the stored token is
|
|
60
|
+
// returned as-is. Capture the real module first so afterAll can restore it;
|
|
61
|
+
// without the restore this mock leaks into client-tui-refresh.test.ts, whose
|
|
62
|
+
// refresh test then receives the pass-through instead of the real refresher.
|
|
63
|
+
const realClient = { ...(await import("../commands/client.js")) };
|
|
62
64
|
const resolveFreshBearerTokenMock = mock(
|
|
63
65
|
async (
|
|
64
66
|
_runtimeUrl: string,
|
|
@@ -68,6 +70,7 @@ const resolveFreshBearerTokenMock = mock(
|
|
|
68
70
|
) => bearerToken,
|
|
69
71
|
);
|
|
70
72
|
mock.module("../commands/client.js", () => ({
|
|
73
|
+
...realClient,
|
|
71
74
|
resolveFreshBearerToken: resolveFreshBearerTokenMock,
|
|
72
75
|
}));
|
|
73
76
|
|
|
@@ -77,6 +80,7 @@ afterAll(() => {
|
|
|
77
80
|
mock.module("../lib/process.js", () => realProcess);
|
|
78
81
|
mock.module("../lib/drain.js", () => realDrain);
|
|
79
82
|
mock.module("../lib/guardian-token.js", () => realGuardianToken);
|
|
83
|
+
mock.module("../commands/client.js", () => realClient);
|
|
80
84
|
});
|
|
81
85
|
|
|
82
86
|
import { sleep } from "../commands/sleep.js";
|
|
@@ -160,7 +160,6 @@ describe("startTailscaleServe", () => {
|
|
|
160
160
|
|
|
161
161
|
expect(info.publicUrl).toBe("https://my-host.tail-scale.ts.net");
|
|
162
162
|
expect(info.port).toBe(7840);
|
|
163
|
-
expect(info.viaIngress).toBe(false);
|
|
164
163
|
expect(calls).toContainEqual(["serve", "--bg", "7840"]);
|
|
165
164
|
|
|
166
165
|
const config = JSON.parse(
|