@vellumai/cli 0.11.3 → 0.11.4-dev.202608190019.b94dbf2
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/node_modules/@vellumai/local-mode/src/__tests__/unpair.test.ts +33 -0
- package/node_modules/@vellumai/local-mode/src/index.ts +3 -0
- package/node_modules/@vellumai/local-mode/src/lockfile-lock.test.ts +165 -0
- package/node_modules/@vellumai/local-mode/src/lockfile-lock.ts +156 -0
- package/node_modules/@vellumai/local-mode/src/lockfile.test.ts +249 -8
- package/node_modules/@vellumai/local-mode/src/lockfile.ts +186 -68
- package/node_modules/@vellumai/local-mode/src/unpair.ts +18 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/package.json +1 -1
- package/src/__tests__/assistant-config.test.ts +35 -0
- package/src/__tests__/nginx-ingress-command.test.ts +4 -23
- package/src/__tests__/nginx-ingress.test.ts +59 -215
- package/src/__tests__/pair.test.ts +11 -197
- package/src/__tests__/retire-archive.test.ts +13 -1
- package/src/__tests__/retire-local.test.ts +58 -4
- package/src/__tests__/tunnel.test.ts +0 -28
- package/src/__tests__/wake.test.ts +91 -69
- package/src/__tests__/windows-lifecycle.test.ts +157 -0
- package/src/commands/client.ts +9 -31
- package/src/commands/nginx-ingress.ts +0 -15
- package/src/commands/pair.ts +0 -39
- package/src/commands/wake.ts +39 -12
- package/src/lib/__tests__/web-dist.test.ts +86 -0
- package/src/lib/assistant-config.ts +64 -27
- package/src/lib/local.ts +60 -20
- package/src/lib/nginx-ingress.ts +35 -108
- package/src/lib/orphan-detection.test.ts +3 -0
- package/src/lib/orphan-detection.ts +33 -11
- package/src/lib/pgrep.ts +20 -2
- package/src/lib/process.ts +191 -18
- package/src/lib/retire-archive.ts +38 -8
- package/src/lib/retire-local.ts +75 -9
- package/src/lib/tunnel-edge.ts +14 -22
- package/src/lib/web-dist.ts +48 -0
- package/src/lib/feature-flags.test.ts +0 -157
- package/src/lib/feature-flags.ts +0 -38
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const loadModule = createRequire(import.meta.url);
|
|
6
|
+
|
|
7
|
+
interface WebDistOptions {
|
|
8
|
+
execPath?: string;
|
|
9
|
+
platform?: NodeJS.Platform;
|
|
10
|
+
resolvePackage?: () => string;
|
|
11
|
+
startDir?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Locate the pre-built web SPA for installed, packaged, or source CLIs. */
|
|
15
|
+
export function findWebDistDir(options: WebDistOptions = {}): string | null {
|
|
16
|
+
try {
|
|
17
|
+
const packageJson = (
|
|
18
|
+
options.resolvePackage ??
|
|
19
|
+
(() => loadModule.resolve("@vellumai/web/package.json"))
|
|
20
|
+
)();
|
|
21
|
+
const distDir = join(dirname(packageJson), "dist");
|
|
22
|
+
if (existsSync(join(distDir, "index.html"))) {
|
|
23
|
+
return distDir;
|
|
24
|
+
}
|
|
25
|
+
} catch {}
|
|
26
|
+
|
|
27
|
+
if ((options.platform ?? process.platform) === "win32") {
|
|
28
|
+
const runtimeDir = dirname(options.execPath ?? process.execPath);
|
|
29
|
+
const packagedDist = join(runtimeDir, "web-dist");
|
|
30
|
+
if (existsSync(join(packagedDist, "index.html"))) {
|
|
31
|
+
return packagedDist;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let dir = options.startDir ?? import.meta.dir;
|
|
36
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
37
|
+
const candidate = join(dir, "clients", "web", "dist", "index.html");
|
|
38
|
+
if (existsSync(candidate)) {
|
|
39
|
+
return dirname(candidate);
|
|
40
|
+
}
|
|
41
|
+
const parent = dirname(dir);
|
|
42
|
+
if (parent === dir) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
dir = parent;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
afterAll,
|
|
3
|
-
afterEach,
|
|
4
|
-
beforeEach,
|
|
5
|
-
describe,
|
|
6
|
-
expect,
|
|
7
|
-
test,
|
|
8
|
-
} from "bun:test";
|
|
9
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
10
|
-
import { tmpdir } from "node:os";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
isAssistantFeatureFlagEnabled,
|
|
15
|
-
WEB_REMOTE_INGRESS_FLAG,
|
|
16
|
-
} from "./feature-flags.js";
|
|
17
|
-
|
|
18
|
-
const testDir = mkdtempSync(join(tmpdir(), "cli-feature-flags-test-"));
|
|
19
|
-
const originalFetch = globalThis.fetch;
|
|
20
|
-
const originalLockfileDir = process.env.VELLUM_LOCKFILE_DIR;
|
|
21
|
-
|
|
22
|
-
function writeLockfile(): void {
|
|
23
|
-
mkdirSync(testDir, { recursive: true });
|
|
24
|
-
writeFileSync(
|
|
25
|
-
join(testDir, ".vellum.lock.json"),
|
|
26
|
-
JSON.stringify(
|
|
27
|
-
{
|
|
28
|
-
activeAssistant: "assistant-1",
|
|
29
|
-
assistants: [
|
|
30
|
-
{
|
|
31
|
-
assistantId: "assistant-1",
|
|
32
|
-
runtimeUrl: "http://127.0.0.1:7830",
|
|
33
|
-
cloud: "local",
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
null,
|
|
38
|
-
2,
|
|
39
|
-
),
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function jsonResponse(body: unknown, status = 200): Response {
|
|
44
|
-
return new Response(JSON.stringify(body), {
|
|
45
|
-
status,
|
|
46
|
-
headers: { "content-type": "application/json" },
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function mockFetch(response: Response): void {
|
|
51
|
-
const fetchMock = async () => response;
|
|
52
|
-
globalThis.fetch = fetchMock as unknown as typeof globalThis.fetch;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function mockFetchWithUrls(response: Response): string[] {
|
|
56
|
-
const urls: string[] = [];
|
|
57
|
-
const fetchMock = async (input: RequestInfo | URL) => {
|
|
58
|
-
urls.push(String(input));
|
|
59
|
-
return response;
|
|
60
|
-
};
|
|
61
|
-
globalThis.fetch = fetchMock as unknown as typeof globalThis.fetch;
|
|
62
|
-
return urls;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
describe("isAssistantFeatureFlagEnabled", () => {
|
|
66
|
-
beforeEach(() => {
|
|
67
|
-
process.env.VELLUM_LOCKFILE_DIR = testDir;
|
|
68
|
-
rmSync(join(testDir, ".vellum.lock.json"), { force: true });
|
|
69
|
-
writeLockfile();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
afterEach(() => {
|
|
73
|
-
globalThis.fetch = originalFetch;
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
afterAll(() => {
|
|
77
|
-
if (originalLockfileDir === undefined) {
|
|
78
|
-
delete process.env.VELLUM_LOCKFILE_DIR;
|
|
79
|
-
} else {
|
|
80
|
-
process.env.VELLUM_LOCKFILE_DIR = originalLockfileDir;
|
|
81
|
-
}
|
|
82
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("returns true when the assistant flag is enabled", async () => {
|
|
86
|
-
mockFetch(
|
|
87
|
-
jsonResponse({
|
|
88
|
-
flags: [{ key: WEB_REMOTE_INGRESS_FLAG, enabled: true }],
|
|
89
|
-
}),
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
await expect(
|
|
93
|
-
isAssistantFeatureFlagEnabled("assistant-1", WEB_REMOTE_INGRESS_FLAG),
|
|
94
|
-
).resolves.toBe(true);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("uses the supplied runtime URL instead of a stale lockfile runtimeUrl", async () => {
|
|
98
|
-
writeFileSync(
|
|
99
|
-
join(testDir, ".vellum.lock.json"),
|
|
100
|
-
JSON.stringify(
|
|
101
|
-
{
|
|
102
|
-
activeAssistant: "assistant-1",
|
|
103
|
-
assistants: [
|
|
104
|
-
{
|
|
105
|
-
assistantId: "assistant-1",
|
|
106
|
-
runtimeUrl: "https://stale-tunnel.ngrok-free.dev",
|
|
107
|
-
cloud: "local",
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
},
|
|
111
|
-
null,
|
|
112
|
-
2,
|
|
113
|
-
),
|
|
114
|
-
);
|
|
115
|
-
const urls = mockFetchWithUrls(
|
|
116
|
-
jsonResponse({
|
|
117
|
-
flags: [{ key: WEB_REMOTE_INGRESS_FLAG, enabled: true }],
|
|
118
|
-
}),
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
await expect(
|
|
122
|
-
isAssistantFeatureFlagEnabled("assistant-1", WEB_REMOTE_INGRESS_FLAG, {
|
|
123
|
-
runtimeUrl: "http://127.0.0.1:9123",
|
|
124
|
-
}),
|
|
125
|
-
).resolves.toBe(true);
|
|
126
|
-
|
|
127
|
-
expect(urls).toEqual([
|
|
128
|
-
"http://127.0.0.1:9123/v1/assistants/assistant-1/feature-flags",
|
|
129
|
-
]);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test("returns false when the assistant flag is disabled or missing", async () => {
|
|
133
|
-
mockFetch(
|
|
134
|
-
jsonResponse({
|
|
135
|
-
flags: [{ key: WEB_REMOTE_INGRESS_FLAG, enabled: false }],
|
|
136
|
-
}),
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
await expect(
|
|
140
|
-
isAssistantFeatureFlagEnabled("assistant-1", WEB_REMOTE_INGRESS_FLAG),
|
|
141
|
-
).resolves.toBe(false);
|
|
142
|
-
|
|
143
|
-
mockFetch(jsonResponse({ flags: [] }));
|
|
144
|
-
|
|
145
|
-
await expect(
|
|
146
|
-
isAssistantFeatureFlagEnabled("assistant-1", WEB_REMOTE_INGRESS_FLAG),
|
|
147
|
-
).resolves.toBe(false);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test("throws when the gateway rejects the flag request", async () => {
|
|
151
|
-
mockFetch(jsonResponse({ error: "nope" }, 500));
|
|
152
|
-
|
|
153
|
-
await expect(
|
|
154
|
-
isAssistantFeatureFlagEnabled("assistant-1", WEB_REMOTE_INGRESS_FLAG),
|
|
155
|
-
).rejects.toThrow("Failed to fetch feature flags");
|
|
156
|
-
});
|
|
157
|
-
});
|
package/src/lib/feature-flags.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { AssistantClient } from "./assistant-client.js";
|
|
2
|
-
|
|
3
|
-
export const WEB_REMOTE_INGRESS_FLAG = "web-remote-ingress";
|
|
4
|
-
|
|
5
|
-
type FeatureFlagEntry = {
|
|
6
|
-
key?: unknown;
|
|
7
|
-
enabled?: unknown;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
type FeatureFlagsResponse = {
|
|
11
|
-
flags?: FeatureFlagEntry[];
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export async function isAssistantFeatureFlagEnabled(
|
|
15
|
-
assistantId: string,
|
|
16
|
-
key: string,
|
|
17
|
-
opts: { runtimeUrl?: string } = {},
|
|
18
|
-
): Promise<boolean> {
|
|
19
|
-
const client = new AssistantClient({
|
|
20
|
-
assistantId,
|
|
21
|
-
runtimeUrl: opts.runtimeUrl,
|
|
22
|
-
});
|
|
23
|
-
const res = await client.get("/feature-flags");
|
|
24
|
-
if (!res.ok) {
|
|
25
|
-
const body = await res.text().catch(() => "");
|
|
26
|
-
throw new Error(
|
|
27
|
-
`Failed to fetch feature flags: HTTP ${res.status} ${body}`.trim(),
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const data = (await res.json()) as FeatureFlagsResponse;
|
|
32
|
-
const flag = data.flags?.find((entry) => entry.key === key);
|
|
33
|
-
return flag?.enabled === true;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function formatFeatureFlagGateMessage(flagKey: string): string {
|
|
37
|
-
return `This command is behind the \`${flagKey}\` feature flag. Enable it with \`vellum flags set ${flagKey} true\` and try again.`;
|
|
38
|
-
}
|