@mycadet/cli 0.12.9 → 0.12.10
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 +4 -3
- package/src/__tests__/client-web-config.test.ts +0 -1
- package/src/__tests__/nginx-ingress.test.ts +3 -9
- package/src/__tests__/pair.test.ts +149 -78
- package/src/__tests__/ps-cloud-status.test.ts +4 -3
- package/src/__tests__/teleport.test.ts +0 -15
- package/src/__tests__/tunnel.test.ts +253 -68
- package/src/__tests__/uninstall.test.ts +140 -8
- package/src/commands/client.ts +4 -6
- package/src/commands/hatch.ts +4 -1
- package/src/commands/pair.ts +140 -37
- package/src/commands/ps.ts +6 -18
- package/src/commands/teleport.ts +2 -21
- package/src/commands/tunnel.ts +25 -25
- package/src/commands/uninstall.ts +106 -71
- package/src/lib/__tests__/local-gateway-cli.test.ts +42 -0
- package/src/lib/cloud-client.ts +0 -3
- package/src/lib/cloud-login.ts +0 -1
- package/src/lib/color.ts +4 -0
- package/src/lib/docker.ts +40 -58
- package/src/lib/environments/__tests__/paths.test.ts +27 -1
- package/src/lib/environments/paths.ts +16 -8
- package/src/lib/environments/resolve.ts +2 -4
- package/src/lib/hatch-local.ts +15 -8
- package/src/lib/hatch-next-steps.ts +5 -9
- package/src/lib/local.ts +36 -3
- package/src/lib/nginx-ingress.ts +1 -3
- package/src/lib/step-runner.ts +6 -1
|
@@ -39,9 +39,11 @@ const {
|
|
|
39
39
|
getConfigDir,
|
|
40
40
|
getConfigDirs,
|
|
41
41
|
getDefaultPorts,
|
|
42
|
+
getInputHistoryPath,
|
|
42
43
|
getLockfilePath,
|
|
43
44
|
getLockfilePaths,
|
|
44
45
|
getMultiInstanceDir,
|
|
46
|
+
getSharedStateDir,
|
|
45
47
|
} = await import("../paths.js");
|
|
46
48
|
type EnvironmentDefinition =
|
|
47
49
|
import("@mycadet/environments").EnvironmentDefinition;
|
|
@@ -58,7 +60,12 @@ const dev: EnvironmentDefinition = {
|
|
|
58
60
|
webUrl: "https://dev.mycadet.ai",
|
|
59
61
|
};
|
|
60
62
|
|
|
61
|
-
const XDG_ENV_VARS = [
|
|
63
|
+
const XDG_ENV_VARS = [
|
|
64
|
+
"XDG_DATA_HOME",
|
|
65
|
+
"XDG_CONFIG_HOME",
|
|
66
|
+
"XDG_STATE_HOME",
|
|
67
|
+
"APPDATA",
|
|
68
|
+
] as const;
|
|
62
69
|
|
|
63
70
|
describe("path helpers", () => {
|
|
64
71
|
let savedEnv: Record<string, string | undefined>;
|
|
@@ -224,6 +231,25 @@ describe("path helpers", () => {
|
|
|
224
231
|
});
|
|
225
232
|
});
|
|
226
233
|
|
|
234
|
+
describe("getSharedStateDir", () => {
|
|
235
|
+
test("is not environment-scoped: ~/.local/state/cadet for every env", () => {
|
|
236
|
+
expect(getSharedStateDir()).toBe(
|
|
237
|
+
join(TEST_HOME, ".local", "state", "cadet"),
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("respects XDG_STATE_HOME", () => {
|
|
242
|
+
process.env.XDG_STATE_HOME = "/custom/state";
|
|
243
|
+
expect(getSharedStateDir()).toBe("/custom/state/cadet");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test("holds the shared terminal input history file", () => {
|
|
247
|
+
expect(getInputHistoryPath()).toBe(
|
|
248
|
+
join(getSharedStateDir(), "input-history"),
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
227
253
|
describe("getDefaultPorts", () => {
|
|
228
254
|
test("returns production defaults for production", () => {
|
|
229
255
|
const ports = getDefaultPorts(prod);
|
|
@@ -82,18 +82,26 @@ export function getLogDir(env: EnvironmentDefinition): string {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
85
|
+
* The interactive CLI's shared state directory — `$XDG_STATE_HOME/cadet`.
|
|
86
86
|
*
|
|
87
|
-
* Follows the XDG Base Directory spec:
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* Defaults to `~/.local/state/cadet/input-history`.
|
|
87
|
+
* Follows the XDG Base Directory spec: session state is data that persists
|
|
88
|
+
* across runs but is not portable user-owned content, mirroring `bash`,
|
|
89
|
+
* `zsh`, `psql`, and `gh`. Defaults to `~/.local/state/cadet`.
|
|
91
90
|
*
|
|
92
|
-
* Not environment-scoped:
|
|
93
|
-
*
|
|
91
|
+
* Not environment-scoped: it holds per-user state (the terminal input
|
|
92
|
+
* history, below) that dev and prod CLIs share.
|
|
93
|
+
*/
|
|
94
|
+
export function getSharedStateDir(): string {
|
|
95
|
+
return join(xdgStateHome(), "cadet");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Path to the interactive CLI's input history file, inside
|
|
100
|
+
* {@link getSharedStateDir}. Defaults to
|
|
101
|
+
* `~/.local/state/cadet/input-history`.
|
|
94
102
|
*/
|
|
95
103
|
export function getInputHistoryPath(): string {
|
|
96
|
-
return join(
|
|
104
|
+
return join(getSharedStateDir(), "input-history");
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
/**
|
|
@@ -67,10 +67,8 @@ export function getCurrentEnvironment(
|
|
|
67
67
|
const seed = SEEDS[name];
|
|
68
68
|
if (!seed) {
|
|
69
69
|
if (name !== DEFAULT_ENVIRONMENT_NAME) {
|
|
70
|
-
// Warn on stderr instead of throwing
|
|
71
|
-
//
|
|
72
|
-
// which silently falls back to production; the CLI agrees so neither
|
|
73
|
-
// writer ends up in a disjoint state on a typo.
|
|
70
|
+
// Warn on stderr instead of throwing: an unknown environment falls back
|
|
71
|
+
// to production, so a typo never splits writes between two states.
|
|
74
72
|
process.stderr.write(
|
|
75
73
|
`warning: unknown environment "${name}"; falling back to "${DEFAULT_ENVIRONMENT_NAME}". ` +
|
|
76
74
|
`Add it to packages/environments/src/seeds.ts and rebuild if this was intentional.\n`,
|
package/src/lib/hatch-local.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
setActiveAssistant,
|
|
22
22
|
} from "./assistant-config.js";
|
|
23
23
|
import type { AssistantEntry } from "./assistant-config.js";
|
|
24
|
+
import { green, yellow } from "./color.js";
|
|
24
25
|
import { buildHatchConfigValues, writeInitialConfig } from "./config-utils.js";
|
|
25
26
|
import {
|
|
26
27
|
generateLocalSigningKey,
|
|
@@ -379,14 +380,18 @@ export async function hatchLocal(
|
|
|
379
380
|
) {
|
|
380
381
|
const delayMs = 2000 * 2 ** (attempt - 1);
|
|
381
382
|
reporter.error(
|
|
382
|
-
|
|
383
|
+
yellow(
|
|
384
|
+
`⚠️ Guardian token lease attempt ${attempt}/${maxLeaseAttempts} failed — retrying in ${delayMs / 1000}s: ${err}`,
|
|
385
|
+
),
|
|
383
386
|
);
|
|
384
387
|
await new Promise((r) => setTimeout(r, delayMs));
|
|
385
388
|
} else {
|
|
386
389
|
reporter.error(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
+
yellow(
|
|
391
|
+
`⚠️ Guardian token lease failed${err instanceof GuardianLeasePermanentError ? ", not retrying — a malformed 200 will not fix itself" : ` after ${maxLeaseAttempts} attempts`}: ${err}\n` +
|
|
392
|
+
` The assistant is running but guardian-token.json was not written.\n` +
|
|
393
|
+
` Run \`cadet restart ${JSON.stringify(instanceName)} --repair-guardian\` to re-lease a token.`,
|
|
394
|
+
),
|
|
390
395
|
);
|
|
391
396
|
}
|
|
392
397
|
}
|
|
@@ -398,8 +403,10 @@ export async function hatchLocal(
|
|
|
398
403
|
|
|
399
404
|
if (provider !== undefined && provider !== null && !guardianAccessToken) {
|
|
400
405
|
reporter.error(
|
|
401
|
-
|
|
402
|
-
|
|
406
|
+
yellow(
|
|
407
|
+
`⚠️ Provider credential setup skipped because the guardian token was not leased.\n` +
|
|
408
|
+
` The assistant is still hatched. Run \`cadet setup --provider ${provider}\` after fixing the connection.`,
|
|
409
|
+
),
|
|
403
410
|
);
|
|
404
411
|
} else if (provider !== undefined) {
|
|
405
412
|
await configureHatchProviderApiKey({
|
|
@@ -429,7 +436,7 @@ export async function hatchLocal(
|
|
|
429
436
|
}
|
|
430
437
|
|
|
431
438
|
reporter.log("");
|
|
432
|
-
reporter.log(`✅ Local assistant hatched
|
|
439
|
+
reporter.log(green(`✅ Local assistant hatched!`, process.stdout));
|
|
433
440
|
printInstanceDetails(
|
|
434
441
|
{
|
|
435
442
|
name: instanceName,
|
|
@@ -438,7 +445,7 @@ export async function hatchLocal(
|
|
|
438
445
|
},
|
|
439
446
|
(message) => reporter.log(message),
|
|
440
447
|
);
|
|
441
|
-
logHatchNextSteps((message) => reporter.log(message)
|
|
448
|
+
logHatchNextSteps((message) => reporter.log(message));
|
|
442
449
|
|
|
443
450
|
const result: HatchLocalResult = {
|
|
444
451
|
assistantId: instanceName,
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
export function logHatchNextSteps(
|
|
2
|
-
log: (message: string) => void,
|
|
3
|
-
verbose = false,
|
|
4
|
-
): void {
|
|
1
|
+
export function logHatchNextSteps(log: (message: string) => void): void {
|
|
5
2
|
log("Next steps:");
|
|
6
|
-
log(" cadet client");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
3
|
+
log(" - Run `cadet client` to interact with your cadet via the terminal");
|
|
4
|
+
log(
|
|
5
|
+
" - Run `cadet tunnel` to make your cadet publicly accessible and interact with it via the browser",
|
|
6
|
+
);
|
|
11
7
|
log("");
|
|
12
8
|
}
|
package/src/lib/local.ts
CHANGED
|
@@ -984,6 +984,41 @@ function resolveGatewayDir(resources?: LocalInstanceResources): string {
|
|
|
984
984
|
}
|
|
985
985
|
}
|
|
986
986
|
|
|
987
|
+
/**
|
|
988
|
+
* How to invoke a `gateway <subcommand>` CLI process so it resolves the same
|
|
989
|
+
* workspace AND gateway security dir as the locally spawned gateway server:
|
|
990
|
+
* run the gateway package's CLI entry through bun with the spawn state root
|
|
991
|
+
* as cwd and the instance-pinned CADET_WORKSPACE_DIR / GATEWAY_SECURITY_DIR
|
|
992
|
+
* the server was given (the file-backed pairing store must resolve
|
|
993
|
+
* identically in both processes). Callers pass the resolved assistant
|
|
994
|
+
* entry's `resources` (undefined for the default instance, where both
|
|
995
|
+
* processes fall back to the same cwd-relative paths).
|
|
996
|
+
*/
|
|
997
|
+
export function gatewayCliInvocation(resources?: LocalInstanceResources): {
|
|
998
|
+
command: string;
|
|
999
|
+
args: string[];
|
|
1000
|
+
cwd: string;
|
|
1001
|
+
env: Record<string, string | undefined>;
|
|
1002
|
+
} {
|
|
1003
|
+
return {
|
|
1004
|
+
command: resolveBunExecutable(),
|
|
1005
|
+
args: ["run", join(resolveGatewayDir(resources), "src/cli/index.ts")],
|
|
1006
|
+
cwd: spawnStateRoot(),
|
|
1007
|
+
env: {
|
|
1008
|
+
...process.env,
|
|
1009
|
+
...(resources
|
|
1010
|
+
? {
|
|
1011
|
+
CADET_WORKSPACE_DIR: join(resources.instanceDir, "workspace"),
|
|
1012
|
+
GATEWAY_SECURITY_DIR: join(
|
|
1013
|
+
resources.instanceDir,
|
|
1014
|
+
"gateway-security",
|
|
1015
|
+
),
|
|
1016
|
+
}
|
|
1017
|
+
: {}),
|
|
1018
|
+
},
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
|
|
987
1022
|
function resolveCesDir(resources?: LocalInstanceResources): string {
|
|
988
1023
|
const runtimeCesDir = localRuntimeCesDir(resources);
|
|
989
1024
|
if (runtimeCesDir) return runtimeCesDir;
|
|
@@ -1118,9 +1153,7 @@ export async function startCes(
|
|
|
1118
1153
|
// Source tree / bunx: run the CES entry point via bun.
|
|
1119
1154
|
const cesDir = runtimeCesDir ?? resolveCesDir(resources);
|
|
1120
1155
|
const cesEntry = join(cesDir, "src/main.ts");
|
|
1121
|
-
const bunArgs = watch
|
|
1122
|
-
? ["--watch", "run", cesEntry]
|
|
1123
|
-
: ["run", cesEntry];
|
|
1156
|
+
const bunArgs = watch ? ["--watch", "run", cesEntry] : ["run", cesEntry];
|
|
1124
1157
|
const cesLogFd = openLogFile("hatch.log");
|
|
1125
1158
|
ces = spawn(resolveBunExecutable(), bunArgs, {
|
|
1126
1159
|
cwd: spawnStateRoot(),
|
package/src/lib/nginx-ingress.ts
CHANGED
|
@@ -177,8 +177,6 @@ function remoteWebIngressConfig(
|
|
|
177
177
|
return {
|
|
178
178
|
remote: EDGE_SERVED_REMOTE,
|
|
179
179
|
apiBaseUrl: "/v1",
|
|
180
|
-
cloudDisabled: true,
|
|
181
|
-
disableCloud: true,
|
|
182
180
|
...(opts.assistantName ? { assistantName: opts.assistantName } : {}),
|
|
183
181
|
...(opts.assistantId ? { assistantId: opts.assistantId } : {}),
|
|
184
182
|
...(opts.hubUrl ? { hubUrl: opts.hubUrl } : {}),
|
|
@@ -191,7 +189,7 @@ function remoteWebIngressConfig(
|
|
|
191
189
|
* fingerprint matches, so this must change whenever the generated index or
|
|
192
190
|
* nginx template does.
|
|
193
191
|
*/
|
|
194
|
-
export const EDGE_TEMPLATE_VERSION =
|
|
192
|
+
export const EDGE_TEMPLATE_VERSION = 10;
|
|
195
193
|
|
|
196
194
|
/**
|
|
197
195
|
* Stable fingerprint of the SPA config injected into the served index and
|
package/src/lib/step-runner.ts
CHANGED
|
@@ -112,11 +112,16 @@ export function execWithStdin(
|
|
|
112
112
|
export function execOutput(
|
|
113
113
|
command: string,
|
|
114
114
|
args: string[],
|
|
115
|
-
options: {
|
|
115
|
+
options: {
|
|
116
|
+
cwd?: string;
|
|
117
|
+
timeoutMs?: number;
|
|
118
|
+
env?: Record<string, string | undefined>;
|
|
119
|
+
} = {},
|
|
116
120
|
): Promise<string> {
|
|
117
121
|
return new Promise((resolve, reject) => {
|
|
118
122
|
const child = spawn(command, args, {
|
|
119
123
|
cwd: options.cwd,
|
|
124
|
+
env: options.env,
|
|
120
125
|
stdio: ["pipe", "pipe", "pipe"],
|
|
121
126
|
windowsHide: true,
|
|
122
127
|
});
|