@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
package/src/lib/local.ts
CHANGED
|
@@ -8,9 +8,19 @@ import {
|
|
|
8
8
|
writeFileSync,
|
|
9
9
|
} from "fs";
|
|
10
10
|
import { createRequire } from "module";
|
|
11
|
+
import { Socket } from "net";
|
|
11
12
|
import { homedir, networkInterfaces, platform, tmpdir } from "os";
|
|
12
13
|
import { basename, dirname, join } from "path";
|
|
13
14
|
|
|
15
|
+
import {
|
|
16
|
+
findAssistantCommand,
|
|
17
|
+
isRepoCheckoutPath,
|
|
18
|
+
} from "@vellumai/environments";
|
|
19
|
+
import {
|
|
20
|
+
isNamedPipePath,
|
|
21
|
+
removeIpcEndpointFile,
|
|
22
|
+
resolveIpcEndpoint,
|
|
23
|
+
} from "@vellumai/ipc-server-utils";
|
|
14
24
|
import { isValidReleaseVersion } from "@vellumai/local-mode";
|
|
15
25
|
|
|
16
26
|
import {
|
|
@@ -134,11 +144,13 @@ function resolveBunExecutable(): string {
|
|
|
134
144
|
|
|
135
145
|
function envWithBunPath(
|
|
136
146
|
env: Record<string, string | undefined>,
|
|
147
|
+
commandDirs: string[] = [],
|
|
137
148
|
): Record<string, string | undefined> {
|
|
138
149
|
const bunPath = resolveBunExecutable();
|
|
139
150
|
const basePath = env.PATH || "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
140
151
|
const extraDirs = [
|
|
141
152
|
bunPath.includes("/") ? dirname(bunPath) : "",
|
|
153
|
+
...commandDirs,
|
|
142
154
|
join(homedir(), ".bun", "bin"),
|
|
143
155
|
join(homedir(), ".local", "bin"),
|
|
144
156
|
].filter((dir) => dir && !basePath.split(":").includes(dir));
|
|
@@ -675,9 +687,15 @@ async function startDaemonFromSource(
|
|
|
675
687
|
...process.env,
|
|
676
688
|
RUNTIME_HTTP_PORT: process.env.RUNTIME_HTTP_PORT || "7821",
|
|
677
689
|
VELLUM_CLOUD: "local",
|
|
678
|
-
VELLUM_DEV: "1",
|
|
679
690
|
VELLUM_ENVIRONMENT: process.env.VELLUM_ENVIRONMENT || "local",
|
|
680
691
|
};
|
|
692
|
+
// "From source" covers both a developer's checkout and the npm-installed
|
|
693
|
+
// runtime the desktop app runs. Only the former is a dev run: marking an
|
|
694
|
+
// installed runtime as dev suppresses its telemetry and skips the
|
|
695
|
+
// `assistant` command install. An inherited VELLUM_DEV is left alone.
|
|
696
|
+
if (isRepoCheckoutPath(assistantIndex)) {
|
|
697
|
+
env.VELLUM_DEV = "1";
|
|
698
|
+
}
|
|
681
699
|
applyDaemonEnvOverrides(env, resources, options);
|
|
682
700
|
|
|
683
701
|
// Write a sentinel PID file before spawning so concurrent hatch() calls
|
|
@@ -685,7 +703,11 @@ async function startDaemonFromSource(
|
|
|
685
703
|
writeFileSync(pidFile, "starting", "utf-8");
|
|
686
704
|
|
|
687
705
|
const bunPath = resolveBunExecutable();
|
|
688
|
-
const
|
|
706
|
+
const assistantCommand = findAssistantCommand(assistantIndex);
|
|
707
|
+
const spawnEnv = envWithBunPath(
|
|
708
|
+
env,
|
|
709
|
+
assistantCommand ? [dirname(assistantCommand)] : [],
|
|
710
|
+
);
|
|
689
711
|
const child = foreground
|
|
690
712
|
? spawn(bunPath, ["run", daemonMainPath], {
|
|
691
713
|
stdio: "inherit",
|
|
@@ -855,26 +877,55 @@ function resolveCesDir(resources?: LocalInstanceResources): string {
|
|
|
855
877
|
}
|
|
856
878
|
|
|
857
879
|
/**
|
|
858
|
-
* Resolve the
|
|
859
|
-
*
|
|
860
|
-
*
|
|
861
|
-
* to a short tmpdir override (the same one the IPC sockets use) to stay under
|
|
862
|
-
* the AF_UNIX path limit.
|
|
880
|
+
* Resolve the local IPC endpoint shared by the CLI-launched CES sibling and
|
|
881
|
+
* assistant. Windows uses a named pipe. POSIX uses a Unix socket, including
|
|
882
|
+
* the existing short macOS fallback.
|
|
863
883
|
*/
|
|
864
|
-
function resolveCesSocketPath(
|
|
884
|
+
export function resolveCesSocketPath(
|
|
885
|
+
resources?: LocalInstanceResources,
|
|
886
|
+
hostPlatform: NodeJS.Platform = platform(),
|
|
887
|
+
): string {
|
|
865
888
|
const workspaceDir = resources
|
|
866
889
|
? join(resources.instanceDir, ".vellum", "workspace")
|
|
867
890
|
: join(homedir(), ".vellum", "workspace");
|
|
891
|
+
if (hostPlatform === "win32") {
|
|
892
|
+
return resolveIpcEndpoint("ces", {
|
|
893
|
+
workspaceDir,
|
|
894
|
+
platform: hostPlatform,
|
|
895
|
+
}).path;
|
|
896
|
+
}
|
|
868
897
|
const override = computeIpcSocketDirOverride(workspaceDir);
|
|
869
898
|
const socketDir = override ?? workspaceDir;
|
|
870
899
|
mkdirSync(socketDir, { recursive: true });
|
|
871
900
|
return join(socketDir, "ces.sock");
|
|
872
901
|
}
|
|
873
902
|
|
|
903
|
+
async function isIpcEndpointReady(endpointPath: string): Promise<boolean> {
|
|
904
|
+
if (!isNamedPipePath(endpointPath)) {
|
|
905
|
+
return existsSync(endpointPath);
|
|
906
|
+
}
|
|
907
|
+
return new Promise((resolve) => {
|
|
908
|
+
const socket = new Socket();
|
|
909
|
+
let settled = false;
|
|
910
|
+
const finish = (ready: boolean): void => {
|
|
911
|
+
if (settled) {
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
settled = true;
|
|
915
|
+
clearTimeout(timer);
|
|
916
|
+
socket.destroy();
|
|
917
|
+
resolve(ready);
|
|
918
|
+
};
|
|
919
|
+
socket.once("connect", () => finish(true));
|
|
920
|
+
socket.once("error", () => finish(false));
|
|
921
|
+
const timer = setTimeout(() => finish(false), 100);
|
|
922
|
+
socket.connect(endpointPath);
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
|
|
874
926
|
/**
|
|
875
|
-
* Launch the local CES sibling over a
|
|
876
|
-
*
|
|
877
|
-
* containerized homes already run CES.
|
|
927
|
+
* Launch the local CES sibling over a local IPC endpoint. This is the default
|
|
928
|
+
* topology for local instances and matches the containerized CES layout.
|
|
878
929
|
*
|
|
879
930
|
* The sibling runs as an independent process with its lifecycle anchored to
|
|
880
931
|
* SIGTERM, mirroring the gateway: a CLI-owned process with a PID file under
|
|
@@ -898,11 +949,7 @@ export async function startCes(
|
|
|
898
949
|
const socketPath = resolveCesSocketPath(resources);
|
|
899
950
|
// A stale socket file from an unclean shutdown blocks re-bind; CES unlinks it
|
|
900
951
|
// on startup, but remove it here too so a leftover never masks a launch bug.
|
|
901
|
-
|
|
902
|
-
unlinkSync(socketPath);
|
|
903
|
-
} catch {
|
|
904
|
-
/* no stale socket — fine */
|
|
905
|
-
}
|
|
952
|
+
removeIpcEndpointFile(socketPath);
|
|
906
953
|
|
|
907
954
|
const securityDir = resources
|
|
908
955
|
? join(resources.instanceDir, ".vellum", "protected")
|
|
@@ -957,14 +1004,18 @@ export async function startCes(
|
|
|
957
1004
|
writeFileSync(cesPidFile, String(ces.pid), "utf-8");
|
|
958
1005
|
}
|
|
959
1006
|
|
|
960
|
-
// Wait for the
|
|
1007
|
+
// Wait for the endpoint so the daemon's discovery finds it on the
|
|
961
1008
|
// first probe rather than burning its retry budget.
|
|
962
1009
|
const deadline = Date.now() + 10_000;
|
|
1010
|
+
let endpointReady = false;
|
|
963
1011
|
while (Date.now() < deadline) {
|
|
964
|
-
|
|
1012
|
+
endpointReady = await isIpcEndpointReady(socketPath);
|
|
1013
|
+
if (endpointReady) {
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
965
1016
|
await new Promise((r) => setTimeout(r, 50));
|
|
966
1017
|
}
|
|
967
|
-
if (!
|
|
1018
|
+
if (!endpointReady) {
|
|
968
1019
|
console.warn(
|
|
969
1020
|
"⚠ credential-executor started but its socket did not appear within 10s",
|
|
970
1021
|
);
|
|
@@ -1394,7 +1445,13 @@ export async function startLocalDaemon(
|
|
|
1394
1445
|
const localBinDir = join(home, ".local", "bin");
|
|
1395
1446
|
const basePath =
|
|
1396
1447
|
process.env.PATH || "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
1397
|
-
|
|
1448
|
+
// The compiled `assistant` ships beside the daemon binary, so its
|
|
1449
|
+
// directory is what puts `assistant …` on PATH for agent-run commands.
|
|
1450
|
+
const daemonBinaryDir = dirname(daemonBinary);
|
|
1451
|
+
const assistantBinaryDir = existsSync(join(daemonBinaryDir, "assistant"))
|
|
1452
|
+
? [daemonBinaryDir]
|
|
1453
|
+
: [];
|
|
1454
|
+
const extraDirs = [...assistantBinaryDir, bunBinDir, localBinDir].filter(
|
|
1398
1455
|
(d) => !basePath.split(":").includes(d),
|
|
1399
1456
|
);
|
|
1400
1457
|
const daemonEnv: Record<string, string> = {
|