@sentropic/remote-cli 0.0.3
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/dist/attach-YI2AMS3B.js +22 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-E3GXAKJ3.js +258 -0
- package/dist/chunk-PG4CWJNC.js +197 -0
- package/dist/chunk-RLFQALHC.js +45 -0
- package/dist/chunk-VRTZ23J3.js +541 -0
- package/dist/chunk-Z277FK2S.js +294 -0
- package/dist/h2a-bridge-FPB5D3TB.js +20 -0
- package/dist/index.d.ts +730 -0
- package/dist/index.js +13966 -0
- package/dist/llm-gateway-runtime/index.d.ts +2 -0
- package/dist/llm-gateway-runtime/index.js +1295 -0
- package/dist/sync-status-WRQK4YRK.js +15 -0
- package/dist/workspace-O32VX2JG.js +38 -0
- package/dist/workspace-sync-incremental-W3SHAYHS.js +159 -0
- package/package.json +46 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Live-session registry — the source of truth for `remote ls` / `remote
|
|
4
|
+
* restore`, so they stop GUESSING sessions from filesystem mtimes.
|
|
5
|
+
*
|
|
6
|
+
* Entries land here from:
|
|
7
|
+
* - `remote run` (source "run" — local tmux sessions),
|
|
8
|
+
* - Claude Code hooks (source "hook" — `remote enroll --hook claude-*`),
|
|
9
|
+
* - the restore scanner (source "scan" — legacy fallback),
|
|
10
|
+
* - the control-plane (source "remote" — reconciled by the caller).
|
|
11
|
+
*
|
|
12
|
+
* The file is `<configDir>/registry.json`, written atomically (tmp + rename).
|
|
13
|
+
* Every function takes an optional explicit path so tests never touch the real
|
|
14
|
+
* config dir (default path honors REMOTE_CLI_CONFIG_HOME like config.ts).
|
|
15
|
+
*/
|
|
16
|
+
type RegistryTool = "claude" | "codex" | "agy";
|
|
17
|
+
type RegistryKind = "local-tmux" | "local" | "remote";
|
|
18
|
+
type RegistrySource = "run" | "hook" | "scan" | "remote";
|
|
19
|
+
/**
|
|
20
|
+
* Delegated-job extension (P1 of cross-type agent delegation). A job IS a
|
|
21
|
+
* RegistryEntry with `role: "job"` — same atomic-write, same liveness guards,
|
|
22
|
+
* same `listLive`. These fields are OPTIONAL so every existing entry stays a
|
|
23
|
+
* valid RegistryEntry (back-compat).
|
|
24
|
+
*/
|
|
25
|
+
type RegistryRole = "job";
|
|
26
|
+
type JobState = "pending" | "running" | "throttled" | "done" | "failed";
|
|
27
|
+
/**
|
|
28
|
+
* Rate-limit ("throttled") bookkeeping for a HEADLESS LOCAL job whose agent CLI
|
|
29
|
+
* hit a TRANSIENT provider rate-limit (reliability slice 1). A throttled job
|
|
30
|
+
* KEEPS its concurrency slot (the limit is account-wide; admitting a replacement
|
|
31
|
+
* just burns the same quota) and is auto-resumed by the conductor on
|
|
32
|
+
* `nextRetryAt` with exponential backoff, up to a hard attempt cap. All fields
|
|
33
|
+
* are written under `withRegistryLock`; the whole object is optional so every
|
|
34
|
+
* existing entry stays a valid RegistryEntry (back-compat).
|
|
35
|
+
*/
|
|
36
|
+
type ThrottleInfo = {
|
|
37
|
+
/** How many times this job has entered `throttled` (drives the backoff). */
|
|
38
|
+
attempts: number;
|
|
39
|
+
/** ISO ts of the FIRST throttle (for age / history windows). */
|
|
40
|
+
firstAt: string;
|
|
41
|
+
/** ISO ts the conductor may resume the job at (now + jitteredDelay(attempts)). */
|
|
42
|
+
nextRetryAt: string;
|
|
43
|
+
/** The signature tag that classified the last throttle (e.g. claude:rate-limited). */
|
|
44
|
+
lastSignature?: string;
|
|
45
|
+
};
|
|
46
|
+
type RegistryEntry = {
|
|
47
|
+
/** Stable key: claude session uuid / codex rollout id / remoteId / tmux slug. */
|
|
48
|
+
id: string;
|
|
49
|
+
tool: RegistryTool;
|
|
50
|
+
kind: RegistryKind;
|
|
51
|
+
cwd: string;
|
|
52
|
+
label?: string;
|
|
53
|
+
/** Conversation id usable with the CLI's --resume. */
|
|
54
|
+
convId?: string;
|
|
55
|
+
/** Control-plane session id (kind "remote"). */
|
|
56
|
+
remoteId?: string;
|
|
57
|
+
/** Full tmux session name (kind "local-tmux"), e.g. `remote-surch`. */
|
|
58
|
+
tmuxSession?: string;
|
|
59
|
+
/** Local process id (kind "local"); liveness = process.kill(pid, 0). */
|
|
60
|
+
pid?: number;
|
|
61
|
+
enrolledAt: string;
|
|
62
|
+
lastSeenAt: string;
|
|
63
|
+
endedAt?: string;
|
|
64
|
+
source: RegistrySource;
|
|
65
|
+
/** "job" marks a delegated agent (see `delegate.ts`); absent = a session. */
|
|
66
|
+
role?: RegistryRole;
|
|
67
|
+
/** Lifecycle of a delegated job (role "job" only). */
|
|
68
|
+
jobState?: JobState;
|
|
69
|
+
/** Parent job/session id that delegated this job. */
|
|
70
|
+
parent?: string;
|
|
71
|
+
/** The task the delegated agent was primed with. */
|
|
72
|
+
task?: string;
|
|
73
|
+
/** h2a instance to address the `job.done` callback to (P3); the delegating
|
|
74
|
+
* parent/master. Absent = no callback recipient (best-effort, no-op). */
|
|
75
|
+
callbackTo?: string;
|
|
76
|
+
/**
|
|
77
|
+
* P4 — queued-launch spec. A job over the concurrency cap is enrolled
|
|
78
|
+
* `pending` WITHOUT being launched; the conductor launches it later. These
|
|
79
|
+
* fields carry everything `startJob` needs to launch it from the queue (they
|
|
80
|
+
* are also set on an immediately-launched job, harmlessly). All optional so
|
|
81
|
+
* every existing entry stays a valid RegistryEntry (back-compat).
|
|
82
|
+
*/
|
|
83
|
+
/** Run the job in a Pod (the remote control-plane URL), else a local tmux session. */
|
|
84
|
+
remoteTarget?: string;
|
|
85
|
+
/** Run-once-exit headless mode (claude -p / codex exec). */
|
|
86
|
+
headless?: boolean;
|
|
87
|
+
/** The cwd the delegate was invoked from (origin for the per-job worktree/logs). */
|
|
88
|
+
originCwd?: string;
|
|
89
|
+
/** Explicit `--cwd` override (local; used as-is, no worktree). */
|
|
90
|
+
explicitCwd?: string;
|
|
91
|
+
/** Remaining spawn-depth budget this job may spend if it re-delegates (P4 depth clamp). */
|
|
92
|
+
depthBudget?: number;
|
|
93
|
+
/** Track workpackage id to mirror this job under (`track item new --parent`). */
|
|
94
|
+
trackWp?: string;
|
|
95
|
+
/** Rate-limit backoff/resume bookkeeping (HEADLESS LOCAL only; reliability slice 1). */
|
|
96
|
+
throttle?: ThrottleInfo;
|
|
97
|
+
/** Model override passed to the CLI binary (--model for claude, -m for codex). */
|
|
98
|
+
model?: string;
|
|
99
|
+
/** Effort/reasoning override (claude --effort; not supported by codex). */
|
|
100
|
+
effort?: string;
|
|
101
|
+
/** Force a specific account from the pool (bypass selectAccountWithFallback). */
|
|
102
|
+
accountId?: string;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
type PtyHandle = {
|
|
106
|
+
readonly cols: number;
|
|
107
|
+
readonly rows: number;
|
|
108
|
+
write(data: string): void;
|
|
109
|
+
resize(cols: number, rows: number): void;
|
|
110
|
+
kill(signal?: string): void;
|
|
111
|
+
onData(handler: (chunk: string) => void): {
|
|
112
|
+
dispose(): void;
|
|
113
|
+
};
|
|
114
|
+
onExit(handler: (event: {
|
|
115
|
+
exitCode: number;
|
|
116
|
+
signal?: number;
|
|
117
|
+
}) => void): {
|
|
118
|
+
dispose(): void;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
type PtySpawner = (options: {
|
|
122
|
+
command: string;
|
|
123
|
+
args: ReadonlyArray<string>;
|
|
124
|
+
cwd: string;
|
|
125
|
+
env: Readonly<Record<string, string>>;
|
|
126
|
+
cols: number;
|
|
127
|
+
rows: number;
|
|
128
|
+
}) => PtyHandle;
|
|
129
|
+
|
|
130
|
+
type RunOptions = {
|
|
131
|
+
readonly profile: string;
|
|
132
|
+
readonly resume?: string | true | undefined;
|
|
133
|
+
readonly port?: number;
|
|
134
|
+
readonly cwd?: string;
|
|
135
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
136
|
+
readonly startupArgs?: ReadonlyArray<string>;
|
|
137
|
+
readonly stdin?: NodeJS.ReadStream;
|
|
138
|
+
readonly stdout?: NodeJS.WriteStream;
|
|
139
|
+
readonly spawner?: PtySpawner;
|
|
140
|
+
readonly randomId?: (prefix: string) => string;
|
|
141
|
+
readonly clock?: () => Date;
|
|
142
|
+
readonly initialSize?: {
|
|
143
|
+
cols: number;
|
|
144
|
+
rows: number;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
type RunResult = {
|
|
148
|
+
readonly sessionId: string;
|
|
149
|
+
readonly port: number;
|
|
150
|
+
readonly exit: Promise<{
|
|
151
|
+
exitCode: number;
|
|
152
|
+
signal?: number;
|
|
153
|
+
}>;
|
|
154
|
+
readonly stop: () => Promise<void>;
|
|
155
|
+
};
|
|
156
|
+
declare function run(options: RunOptions): Promise<RunResult>;
|
|
157
|
+
|
|
158
|
+
type InputRetryOptions = {
|
|
159
|
+
readonly maxAttempts?: number;
|
|
160
|
+
readonly baseDelayMs?: number;
|
|
161
|
+
readonly maxDelayMs?: number;
|
|
162
|
+
};
|
|
163
|
+
type AttachOptions = {
|
|
164
|
+
readonly baseUrl: string;
|
|
165
|
+
readonly sessionId: string;
|
|
166
|
+
readonly stdin?: NodeJS.ReadStream;
|
|
167
|
+
readonly stdout?: NodeJS.WriteStream;
|
|
168
|
+
readonly stderr?: NodeJS.WriteStream;
|
|
169
|
+
readonly fetchImpl?: typeof fetch;
|
|
170
|
+
readonly inputRetry?: InputRetryOptions;
|
|
171
|
+
};
|
|
172
|
+
type AttachResult = {
|
|
173
|
+
readonly close: () => Promise<void>;
|
|
174
|
+
readonly finished: Promise<void>;
|
|
175
|
+
};
|
|
176
|
+
declare function attach(options: AttachOptions): Promise<AttachResult>;
|
|
177
|
+
declare function listRemoteSessions(baseUrl: string, fetchImpl?: typeof fetch): Promise<ReadonlyArray<{
|
|
178
|
+
id: string;
|
|
179
|
+
profile: string;
|
|
180
|
+
target: string;
|
|
181
|
+
createdAt: string;
|
|
182
|
+
workspaceId?: string;
|
|
183
|
+
workspacePath?: string;
|
|
184
|
+
displayName?: string;
|
|
185
|
+
cliSessionId?: string;
|
|
186
|
+
}>>;
|
|
187
|
+
declare function stopRemoteSession(baseUrl: string, sessionId: string, reason?: string, fetchImpl?: typeof fetch): Promise<{
|
|
188
|
+
accepted: boolean;
|
|
189
|
+
}>;
|
|
190
|
+
declare function getRemoteSession(baseUrl: string, sessionId: string, fetchImpl?: typeof fetch): Promise<{
|
|
191
|
+
session: {
|
|
192
|
+
id: string;
|
|
193
|
+
profile: string;
|
|
194
|
+
};
|
|
195
|
+
}>;
|
|
196
|
+
declare function refreshRemoteSession(baseUrl: string, sessionId: string, credentials: Readonly<Record<string, string>>, fetchImpl?: typeof fetch, displayName?: string): Promise<{
|
|
197
|
+
sessionId: string;
|
|
198
|
+
accepted: boolean;
|
|
199
|
+
}>;
|
|
200
|
+
/**
|
|
201
|
+
* Rename a session's display name in the store without touching the Pod.
|
|
202
|
+
* Calls PATCH /sessions/:id with body { displayName }.
|
|
203
|
+
*/
|
|
204
|
+
declare function renameRemoteSession(baseUrl: string, sessionId: string, displayName: string, fetchImpl?: typeof fetch): Promise<{
|
|
205
|
+
sessionId: string;
|
|
206
|
+
displayName: string;
|
|
207
|
+
accepted: boolean;
|
|
208
|
+
}>;
|
|
209
|
+
declare function createRemoteSession(baseUrl: string, body: {
|
|
210
|
+
profile: string;
|
|
211
|
+
target?: string;
|
|
212
|
+
resume?: string;
|
|
213
|
+
startupArgs?: readonly string[];
|
|
214
|
+
displayName?: string;
|
|
215
|
+
credentials?: Readonly<Record<string, string>>;
|
|
216
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
217
|
+
workspaceSync?: boolean;
|
|
218
|
+
workspaceExport?: boolean;
|
|
219
|
+
workspaceId?: string;
|
|
220
|
+
workspacePath?: string;
|
|
221
|
+
home?: string;
|
|
222
|
+
agentImage?: string;
|
|
223
|
+
}, fetchImpl?: typeof fetch): Promise<{
|
|
224
|
+
id: string;
|
|
225
|
+
}>;
|
|
226
|
+
|
|
227
|
+
declare const CLI_PROFILES: readonly ["shell", "codex", "opencode", "claude", "agy", "gemini", "mistral"];
|
|
228
|
+
type CliProfile = (typeof CLI_PROFILES)[number];
|
|
229
|
+
type SessionTarget = "docker" | "k3s" | "scaleway-kapsule" | "gke";
|
|
230
|
+
|
|
231
|
+
type AuthBundle = Readonly<Record<string, string>>;
|
|
232
|
+
declare class AuthBundleMissingError extends Error {
|
|
233
|
+
readonly profile: CliProfile;
|
|
234
|
+
readonly knownPaths: ReadonlyArray<string>;
|
|
235
|
+
readonly refreshHint: string;
|
|
236
|
+
constructor(profile: CliProfile, knownPaths: ReadonlyArray<string>, refreshHint: string);
|
|
237
|
+
}
|
|
238
|
+
type CollectAuthOptions = {
|
|
239
|
+
readonly home?: string;
|
|
240
|
+
readonly readFileImpl?: (path: string) => Promise<Uint8Array | Buffer>;
|
|
241
|
+
};
|
|
242
|
+
declare function collectProfileAuth(profile: CliProfile, options?: CollectAuthOptions): Promise<AuthBundle>;
|
|
243
|
+
declare function assertRequiredAuthBundle(profile: CliProfile, bundle: AuthBundle): void;
|
|
244
|
+
|
|
245
|
+
type CommandResult = {
|
|
246
|
+
readonly status: number;
|
|
247
|
+
readonly stdout: string;
|
|
248
|
+
readonly stderr: string;
|
|
249
|
+
readonly timedOut?: boolean;
|
|
250
|
+
};
|
|
251
|
+
type RunCommand = (command: string, args: ReadonlyArray<string>, options: {
|
|
252
|
+
timeoutMs: number;
|
|
253
|
+
}) => Promise<CommandResult>;
|
|
254
|
+
type AuthRefreshResult = {
|
|
255
|
+
readonly checked: true;
|
|
256
|
+
readonly command: string;
|
|
257
|
+
} | {
|
|
258
|
+
readonly checked: false;
|
|
259
|
+
readonly reason: "no-status-command";
|
|
260
|
+
};
|
|
261
|
+
type EnsureAuthFreshOptions = {
|
|
262
|
+
readonly timeoutMs?: number;
|
|
263
|
+
readonly runCommand?: RunCommand;
|
|
264
|
+
};
|
|
265
|
+
declare class AuthRefreshError extends Error {
|
|
266
|
+
readonly profile: CliProfile;
|
|
267
|
+
readonly refreshHint: string;
|
|
268
|
+
readonly result: CommandResult;
|
|
269
|
+
constructor(profile: CliProfile, refreshHint: string, result: CommandResult);
|
|
270
|
+
}
|
|
271
|
+
declare function ensureProfileAuthFresh(profile: CliProfile, options?: EnsureAuthFreshOptions): Promise<AuthRefreshResult>;
|
|
272
|
+
|
|
273
|
+
type AuthDiagnosticsStatus = AuthRefreshResult | {
|
|
274
|
+
readonly checked: false;
|
|
275
|
+
readonly reason: "skipped";
|
|
276
|
+
};
|
|
277
|
+
type AuthDiagnosticsResult = {
|
|
278
|
+
readonly profile: CliProfile;
|
|
279
|
+
readonly authStatus: AuthDiagnosticsStatus;
|
|
280
|
+
readonly bundledFiles: ReadonlyArray<string>;
|
|
281
|
+
};
|
|
282
|
+
type InspectProfileAuthOptions = CollectAuthOptions & {
|
|
283
|
+
readonly authRefresh?: boolean;
|
|
284
|
+
readonly runCommand?: RunCommand;
|
|
285
|
+
};
|
|
286
|
+
declare function inspectProfileAuth(profile: CliProfile, options?: InspectProfileAuthOptions): Promise<AuthDiagnosticsResult>;
|
|
287
|
+
|
|
288
|
+
type ProfileConfig = {
|
|
289
|
+
readonly profile: CliProfile;
|
|
290
|
+
readonly command: string;
|
|
291
|
+
readonly args: ReadonlyArray<string>;
|
|
292
|
+
};
|
|
293
|
+
declare function isCliProfile(value: string): value is CliProfile;
|
|
294
|
+
declare function coerceCliProfileName(value: string): CliProfile | undefined;
|
|
295
|
+
declare function resolveProfile(name: string): ProfileConfig;
|
|
296
|
+
declare function withResume(config: ProfileConfig, sessionId?: string | true): ProfileConfig;
|
|
297
|
+
|
|
298
|
+
type SmokeRemoteProfileOptions = {
|
|
299
|
+
readonly profile: CliProfile;
|
|
300
|
+
readonly baseUrl: string;
|
|
301
|
+
readonly target?: SessionTarget;
|
|
302
|
+
readonly displayName?: string;
|
|
303
|
+
readonly timeoutMs?: number;
|
|
304
|
+
readonly auth?: boolean;
|
|
305
|
+
readonly authRefresh?: boolean;
|
|
306
|
+
readonly fetchImpl?: typeof fetch;
|
|
307
|
+
readonly collectAuth?: (profile: CliProfile) => Promise<AuthBundle>;
|
|
308
|
+
readonly ensureAuthFresh?: (profile: CliProfile) => Promise<AuthRefreshResult>;
|
|
309
|
+
};
|
|
310
|
+
type SmokeRemoteProfileResult = {
|
|
311
|
+
readonly profile: CliProfile;
|
|
312
|
+
readonly sessionId: string;
|
|
313
|
+
readonly terminalId: string;
|
|
314
|
+
readonly shell: string;
|
|
315
|
+
};
|
|
316
|
+
declare function smokeRemoteProfile(options: SmokeRemoteProfileOptions): Promise<SmokeRemoteProfileResult>;
|
|
317
|
+
|
|
318
|
+
type OnConflict = "backup" | "keep-local" | "block";
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* remote migrate — convenience wrapper for round-tripping a local CLI session
|
|
322
|
+
* to a remote (SCW k8s) session and back.
|
|
323
|
+
*
|
|
324
|
+
* Forward (local → remote):
|
|
325
|
+
* 1. Resolve remote URL.
|
|
326
|
+
* 2. Ensure the cwd is linked to a workspace (reads .remote/workspace.json).
|
|
327
|
+
* 3. Push the workspace archive (project files, honours .gitignore).
|
|
328
|
+
* 4. Create a remote session for <profile> bound to that workspace.
|
|
329
|
+
* 5. Hand off the current terminal to the remote session via `attach`.
|
|
330
|
+
* The attach call blocks until the session ends or the user detaches
|
|
331
|
+
* (Ctrl+P Ctrl+Q). There is no separate process to kill — `migrate`
|
|
332
|
+
* itself IS the process holding the terminal, and attach takes it over.
|
|
333
|
+
*
|
|
334
|
+
* Back (remote → local):
|
|
335
|
+
* 1. Resolve URL + workspace.
|
|
336
|
+
* 2. Pull the workspace + conversation state (3-way merge).
|
|
337
|
+
* 3. Restore conversation state to the local HOME.
|
|
338
|
+
* 4. Stop the remote session.
|
|
339
|
+
* 5. Print the exact local CLI command to resume from the restored state.
|
|
340
|
+
* We do NOT spawn the CLI — we print the command so the user can run it.
|
|
341
|
+
*/
|
|
342
|
+
|
|
343
|
+
type MigrateForwardOptions = {
|
|
344
|
+
/** CLI profile to start on the remote (e.g. "claude", "codex"). */
|
|
345
|
+
readonly profile: string;
|
|
346
|
+
/** Remote control-plane URL. Required — callers must resolve the default. */
|
|
347
|
+
readonly remoteUrl: string;
|
|
348
|
+
/**
|
|
349
|
+
* Workspace id override. When set, the cwd does not need an existing
|
|
350
|
+
* .remote/workspace.json (one is created/reused for that id).
|
|
351
|
+
* When absent, the .remote/workspace.json for the cwd is used or a new
|
|
352
|
+
* workspace is created and linked.
|
|
353
|
+
*/
|
|
354
|
+
readonly workspaceId?: string;
|
|
355
|
+
/**
|
|
356
|
+
* Whether to pass a --resume/<conv-id> flag to the remote CLI.
|
|
357
|
+
* Pass `true` for the most-recent conversation, or a specific id string.
|
|
358
|
+
*/
|
|
359
|
+
readonly resume?: string | true;
|
|
360
|
+
/**
|
|
361
|
+
* When true, do NOT hijack the current terminal: push + create the remote
|
|
362
|
+
* session, print the `remote attach` command, and return. Used to migrate
|
|
363
|
+
* many sessions non-interactively and to let YOUR terminal reconnect to the
|
|
364
|
+
* remote session itself (rather than this process taking it over).
|
|
365
|
+
*/
|
|
366
|
+
readonly noAttach?: boolean;
|
|
367
|
+
/**
|
|
368
|
+
* Revive a session on the EXISTING workspace without re-pushing the project
|
|
369
|
+
* (preserves work done remotely) — for bringing a session back after an
|
|
370
|
+
* accidental exit. Path/HOME parity + resume still apply; the conversation is
|
|
371
|
+
* the one already on the retained PVC.
|
|
372
|
+
*/
|
|
373
|
+
readonly reconnect?: boolean;
|
|
374
|
+
/** Tool CLIs whose local auth to also bundle into the Pod (scw, gh, aws, …). */
|
|
375
|
+
readonly tools?: ReadonlyArray<string>;
|
|
376
|
+
/** Inject a custom fetch for tests. */
|
|
377
|
+
readonly fetchImpl?: typeof fetch;
|
|
378
|
+
/** Override process.cwd() for tests. */
|
|
379
|
+
readonly cwd?: string;
|
|
380
|
+
/** Override process.stderr.write for tests. */
|
|
381
|
+
readonly stderr?: NodeJS.WriteStream;
|
|
382
|
+
};
|
|
383
|
+
type MigrateForwardResult = {
|
|
384
|
+
/** The workspace id that was used/created. */
|
|
385
|
+
readonly workspaceId: string;
|
|
386
|
+
/** The remote session id that was created. */
|
|
387
|
+
readonly sessionId: string;
|
|
388
|
+
};
|
|
389
|
+
type MigrateBackOptions = {
|
|
390
|
+
/** Remote control-plane URL. Required — callers must resolve the default. */
|
|
391
|
+
readonly remoteUrl: string;
|
|
392
|
+
/** Workspace id override; falls back to .remote/workspace.json. */
|
|
393
|
+
readonly workspaceId?: string;
|
|
394
|
+
/** Known remote session id (from lineage incarnation). When set, skips the
|
|
395
|
+
* list-sessions round-trip and targets this session directly. */
|
|
396
|
+
readonly sessionId?: string;
|
|
397
|
+
/**
|
|
398
|
+
* Conflict resolution for diverged conversations: "backup" | "keep-local".
|
|
399
|
+
* Defaults to "block" (leaves diverged files untouched, exits non-zero).
|
|
400
|
+
*/
|
|
401
|
+
readonly onConflict?: OnConflict;
|
|
402
|
+
/** Inject a custom fetch for tests. */
|
|
403
|
+
readonly fetchImpl?: typeof fetch;
|
|
404
|
+
/** Override process.cwd() for tests. */
|
|
405
|
+
readonly cwd?: string;
|
|
406
|
+
/** Override HOME for session restore in tests. */
|
|
407
|
+
readonly home?: string;
|
|
408
|
+
/** Override process.stderr.write for tests. */
|
|
409
|
+
readonly stderr?: NodeJS.WriteStream;
|
|
410
|
+
/** Override process.stdout.write for tests. */
|
|
411
|
+
readonly stdout?: NodeJS.WriteStream;
|
|
412
|
+
};
|
|
413
|
+
type MigrateBackResult = {
|
|
414
|
+
/** The workspace id that was pulled. */
|
|
415
|
+
readonly workspaceId: string;
|
|
416
|
+
/** The session id that was stopped, if any. */
|
|
417
|
+
readonly stoppedSessionId?: string;
|
|
418
|
+
/** The resume command to print for the user. */
|
|
419
|
+
readonly resumeCommand: string;
|
|
420
|
+
/** Whether there were unresolved merge conflicts. */
|
|
421
|
+
readonly hasConflicts: boolean;
|
|
422
|
+
};
|
|
423
|
+
/**
|
|
424
|
+
* Forward: migrate the current local session to a remote k8s session.
|
|
425
|
+
*
|
|
426
|
+
* Steps: link workspace → push files → create remote session → attach terminal.
|
|
427
|
+
*
|
|
428
|
+
* Terminal handoff: `migrateForward` calls `attach`, which hijacks the current
|
|
429
|
+
* process's stdin/stdout in raw mode and blocks until the remote session ends
|
|
430
|
+
* or the user presses Ctrl+P Ctrl+Q to detach. There is no separate process
|
|
431
|
+
* involved — this function IS the process holding the terminal.
|
|
432
|
+
*/
|
|
433
|
+
declare function migrateForward(options: MigrateForwardOptions): Promise<MigrateForwardResult>;
|
|
434
|
+
/**
|
|
435
|
+
* Back: pull the remote session back to local.
|
|
436
|
+
*
|
|
437
|
+
* Steps: pull workspace + conversation state → stop remote session → print
|
|
438
|
+
* resume command.
|
|
439
|
+
*
|
|
440
|
+
* We do NOT spawn the local CLI — we print the resume command so the user
|
|
441
|
+
* retains control of when and how they restart.
|
|
442
|
+
*/
|
|
443
|
+
declare function migrateBack(options: MigrateBackOptions): Promise<MigrateBackResult>;
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* One MCP server provided by an installed plugin package.
|
|
447
|
+
*
|
|
448
|
+
* `command` is always "node" and `args` the script's realpath: some packages
|
|
449
|
+
* (track@0.2.0) have an entrypoint guard that breaks when the script is run
|
|
450
|
+
* through the npm-global bin symlink, so the bare bin name must never be
|
|
451
|
+
* registered — see plugin.ts.
|
|
452
|
+
*/
|
|
453
|
+
type PluginMcp = {
|
|
454
|
+
name: string;
|
|
455
|
+
command: string;
|
|
456
|
+
args: string[];
|
|
457
|
+
/**
|
|
458
|
+
* Bin script path relative to the package dir (e.g. "dist/mcp.js") — used by
|
|
459
|
+
* `remote plugin sync` to recompute the realpath inside remote Pods, where
|
|
460
|
+
* the npm global root differs from the local one.
|
|
461
|
+
*/
|
|
462
|
+
scriptRel?: string;
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* How a plugin is installed in a session Pod. Default (omitted) = `npm`
|
|
466
|
+
* (`npm i -g <pkg>@<version>`). `curl` pipes an installer script
|
|
467
|
+
* (`curl -fsSL <spec> | bash`) — e.g. a Go binary's install.sh. `script` runs
|
|
468
|
+
* an arbitrary shell line (from the user's own config). Lets non-npm tools be
|
|
469
|
+
* propagated the same way.
|
|
470
|
+
*/
|
|
471
|
+
type PluginInstall = {
|
|
472
|
+
method: "npm" | "curl" | "script";
|
|
473
|
+
/** curl: the installer URL; script: the shell command. Unused for npm. */
|
|
474
|
+
spec?: string;
|
|
475
|
+
};
|
|
476
|
+
/** A plugin propagated to sessions (npm pkg, or curl/script installer) + MCP(s). */
|
|
477
|
+
type PluginEntry = {
|
|
478
|
+
pkg: string;
|
|
479
|
+
version: string;
|
|
480
|
+
mcp: PluginMcp[];
|
|
481
|
+
/** Install method; omitted ⇒ npm (pkg@version). */
|
|
482
|
+
install?: PluginInstall;
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Plugin/MCP DESIRED-STATE manifest + drift diff (remote supervise slice 3) —
|
|
487
|
+
* ADDITIVE, PURE. No IO, no clock. The watch loop / `plugin sync --check` call
|
|
488
|
+
* these to RENDER the desired set, HASH it (to gate a sidecar push exactly like
|
|
489
|
+
* CREDS_HASH_FILE), and DIFF it against what a Pod actually has installed.
|
|
490
|
+
*
|
|
491
|
+
* Why a manifest at all: MCP servers + plugin CLIs (track, h2a, harness,
|
|
492
|
+
* graphify, skills) baked/installed into Pods drift away from the LOCAL
|
|
493
|
+
* source-of-truth — a Pod restart loses globally-installed plugins, a re-pin
|
|
494
|
+
* bumps the local version, and nothing today DETECTS the gap (`plugin ls`
|
|
495
|
+
* prints `REMOTE ?`). The manifest is the canonical "what every Pod SHOULD have"
|
|
496
|
+
* record; its sha256 is the cheap drift signal (one `kubectl exec cat`), and the
|
|
497
|
+
* diff is the per-item report.
|
|
498
|
+
*
|
|
499
|
+
* BIGGEST REMAINING DRIFT SOURCE (DEFERRED — converge-on-session-start): a
|
|
500
|
+
* freshly (re)created Pod boots with NO plugins until the next `plugin sync` /
|
|
501
|
+
* watch pass reconciles it, so its CLI starts WITHOUT track/h2a/etc. Closing
|
|
502
|
+
* that needs the session-agent / orchestrator to run the sync BEFORE the CLI
|
|
503
|
+
* launches — a session-agent change that is OUT OF SCOPE this slice. It hooks in
|
|
504
|
+
* at the session-agent startup path (packages/session-agent, the pane that
|
|
505
|
+
* launches `<cli>`): before exec'ing the CLI, run the equivalent of
|
|
506
|
+
* `buildPodSyncScript` for each desired plugin + write this manifest sidecar.
|
|
507
|
+
* TODO(slice 4): wire converge-on-session-start there; until then the watch-pass
|
|
508
|
+
* reconcile (compareManifestHash → re-run buildPodSyncScript) is the safety net.
|
|
509
|
+
*
|
|
510
|
+
* NOTHING here is a NEW push path: `plugin sync` (no --check) still converges via
|
|
511
|
+
* the EXISTING buildPodSyncScript, untouched. This module only adds a manifest
|
|
512
|
+
* artifact + a read-only drift report.
|
|
513
|
+
*/
|
|
514
|
+
|
|
515
|
+
/** Drift status for one desired item in one Pod. */
|
|
516
|
+
type DriftStatus = "ok" | "version-drift" | "missing" | "mcp-unregistered";
|
|
517
|
+
/** One row of the drift report: a desired item's status in a Pod. */
|
|
518
|
+
type DriftRow = {
|
|
519
|
+
readonly pod: string;
|
|
520
|
+
/** "plugin:<pkg>" or "mcp:<name>". */
|
|
521
|
+
readonly item: string;
|
|
522
|
+
readonly status: DriftStatus;
|
|
523
|
+
/** Human detail (e.g. "local@1.2.0 vs pod@1.1.0"); empty for ok. */
|
|
524
|
+
readonly detail: string;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* `remote plugin` — install npm "plugin" packages (a CLI + an MCP server, e.g.
|
|
529
|
+
* @sentropic/track shipping bins `track` and `track-mcp`) for every agent CLI
|
|
530
|
+
* (claude, codex, agy), both LOCALLY (npm i -g + MCP registration) and inside
|
|
531
|
+
* live REMOTE session Pods (`remote plugin sync`: kubectl exec → npm i -g +
|
|
532
|
+
* per-profile MCP registration).
|
|
533
|
+
*
|
|
534
|
+
* agy (Antigravity CLI) has NO `agy mcp` subcommand: MCP servers are declared
|
|
535
|
+
* in ~/.gemini/config/mcp_config.json, a Claude-style `{"mcpServers": {…}}`
|
|
536
|
+
* JSON file (the agy changelog 1.0.3 calls this the "migrated" path; the old
|
|
537
|
+
* ~/.gemini/antigravity/mcp_config.json is legacy). We merge idempotently and
|
|
538
|
+
* keep a one-shot `.bak.<epoch>` backup the first time we touch a non-empty
|
|
539
|
+
* file.
|
|
540
|
+
*
|
|
541
|
+
* KNOWN PITFALL — broken entrypoint guard through the npm-global symlink:
|
|
542
|
+
* some packages (track@0.2.0) guard their entry script with a
|
|
543
|
+
* "was-I-run-directly?" check that compares argv[1] with the module path;
|
|
544
|
+
* invoked through the npm-global bin SYMLINK the two differ and the guard
|
|
545
|
+
* never fires (the CLI/MCP silently does nothing). So MCP servers are ALWAYS
|
|
546
|
+
* registered as `node <realpathSync(script)>` — never the bare bin name.
|
|
547
|
+
*
|
|
548
|
+
* Baking plugins into the session image is a separate TODO that belongs in
|
|
549
|
+
* packages/session-agent/Dockerfile (left untouched here): until then a Pod
|
|
550
|
+
* restart loses globally-installed plugins and `remote plugin sync` must be
|
|
551
|
+
* re-run.
|
|
552
|
+
*
|
|
553
|
+
* DRIFT (remote supervise slice 3, ADDITIVE): a desired-state manifest
|
|
554
|
+
* (plugin-manifest.ts) + a sidecar pushed to each Pod (~/.remote-manifest.json /
|
|
555
|
+
* ~/.remote-manifest.sha256, gated EXACTLY like CREDS_HASH_FILE), a read-only
|
|
556
|
+
* `plugin sync --check` drift report, and a watch-pass reconcile that re-runs
|
|
557
|
+
* the EXISTING buildPodSyncScript on a hash mismatch (an extra TRIGGER, not a
|
|
558
|
+
* new push path). The plain `plugin add`/`plugin sync` push is UNCHANGED.
|
|
559
|
+
*
|
|
560
|
+
* TODO(slice 4 — converge-on-session-start, the BIGGEST remaining drift source):
|
|
561
|
+
* a freshly (re)created Pod boots with NO plugins until the next sync/watch pass,
|
|
562
|
+
* so its CLI launches WITHOUT track/h2a/etc. Closing that needs the session-agent
|
|
563
|
+
* startup path (packages/session-agent, the pane that launches `<cli>`) to run
|
|
564
|
+
* the sync + write the manifest sidecar BEFORE exec'ing the CLI — OUT OF SCOPE
|
|
565
|
+
* here (do not touch session-agent this slice). The watch-pass reconcile is the
|
|
566
|
+
* interim safety net.
|
|
567
|
+
*/
|
|
568
|
+
|
|
569
|
+
type McpRequest = {
|
|
570
|
+
name: string;
|
|
571
|
+
bin: string;
|
|
572
|
+
};
|
|
573
|
+
/** Parse one `--mcp <name>=<bin>` spec. */
|
|
574
|
+
declare function parseMcpSpec(spec: string): McpRequest;
|
|
575
|
+
declare function parseMcpSpecs(specs: readonly string[]): McpRequest[];
|
|
576
|
+
/**
|
|
577
|
+
* Heuristic when no --mcp is given: every bin ending in `-mcp` is an MCP
|
|
578
|
+
* server named after the bin minus the suffix (track-mcp → track).
|
|
579
|
+
*/
|
|
580
|
+
declare function detectMcpBins(bins: Readonly<Record<string, string>>): McpRequest[];
|
|
581
|
+
/**
|
|
582
|
+
* Idempotently upsert the `[mcp_servers.<name>]` section in a config.toml
|
|
583
|
+
* body: an existing section (up to the next `[…]` header) is replaced in
|
|
584
|
+
* place, otherwise the block is appended. Applying twice is a no-op.
|
|
585
|
+
*/
|
|
586
|
+
declare function upsertCodexMcpServer(toml: string, name: string, command: string, args: readonly string[]): string;
|
|
587
|
+
/** Idempotent `mcpServers.<name>` merge for a ~/.claude.json body. */
|
|
588
|
+
declare function mergeClaudeMcpServers(json: string, name: string, command: string, args: readonly string[]): string;
|
|
589
|
+
/**
|
|
590
|
+
* Bash script run inside a session Pod (`kubectl exec … bash -lc`) by
|
|
591
|
+
* `remote plugin sync`: installs the plugin globally, recomputes each MCP
|
|
592
|
+
* script's realpath against the POD's npm global root (the local realpath is
|
|
593
|
+
* meaningless there), then registers the MCP server for the Pod's profile.
|
|
594
|
+
* Every step echoes one line — the CLI prints them as the per-session recap.
|
|
595
|
+
*/
|
|
596
|
+
declare function buildPodSyncScript(plugin: PluginEntry, profile: string): string;
|
|
597
|
+
/**
|
|
598
|
+
* Production wiring used by the supervise/watch pass: reconcile manifest drift
|
|
599
|
+
* for ONE session via the configured tunnel. Thin — the decision + the reused
|
|
600
|
+
* buildPodSyncScript live in `reconcilePodManifest`. No-op (and no error) when
|
|
601
|
+
* no plugins are configured. Best-effort; never throws into the caller.
|
|
602
|
+
*/
|
|
603
|
+
declare function reconcileSessionPlugins(sessionId: string, profile: string, stderr?: NodeJS.WriteStream): {
|
|
604
|
+
reconciled: boolean;
|
|
605
|
+
};
|
|
606
|
+
/**
|
|
607
|
+
* `remote plugin add <npmPkg> [--mcp name=bin]...` — npm i -g, register the
|
|
608
|
+
* MCP server(s) with claude + codex + agy, persist in the config.
|
|
609
|
+
*/
|
|
610
|
+
declare function pluginAdd(npmSpec: string, mcpSpecs: readonly string[], stderr?: NodeJS.WriteStream): PluginEntry;
|
|
611
|
+
/**
|
|
612
|
+
* `remote plugin add <name> --curl <url>` / `--install "<shell>"` — register a
|
|
613
|
+
* NON-npm plugin (a tool installed by piping an https script, or an arbitrary
|
|
614
|
+
* shell command). No local install is run (unlike the npm path, which must read
|
|
615
|
+
* the package to detect MCP bins): the installer runs in each Pod on
|
|
616
|
+
* `remote plugin sync`. Validates the command up front so a bad URL fails now.
|
|
617
|
+
*/
|
|
618
|
+
declare function pluginAddInstaller(name: string, install: PluginInstall, stderr?: NodeJS.WriteStream): PluginEntry;
|
|
619
|
+
/**
|
|
620
|
+
* `remote plugin ls` — pkg / version / MCPs / where (local ok/missing, remote).
|
|
621
|
+
*
|
|
622
|
+
* The REMOTE column is now REAL per-Pod drift status (slice 3), not the old
|
|
623
|
+
* guess-`?`: when a `url` is passed (the command is connected to the control-
|
|
624
|
+
* plane) we probe each live Pod via the same drift mechanism `--check` uses and
|
|
625
|
+
* summarize each plugin's worst per-Pod status across all Pods
|
|
626
|
+
* (ok / version-drift / missing). Without a url (offline) we still print `?`
|
|
627
|
+
* with the documented hint — the `?` only remains when we genuinely can't reach
|
|
628
|
+
* the cluster, never as guesswork when we can.
|
|
629
|
+
*/
|
|
630
|
+
declare function pluginLs(stdout?: NodeJS.WriteStream, url?: string): Promise<void>;
|
|
631
|
+
/**
|
|
632
|
+
* `remote plugin sync` — for every live remote session: kubectl exec into the
|
|
633
|
+
* Pod, `npm i -g <pkg>@<version>`, then register the MCP servers for the
|
|
634
|
+
* Pod's profile. Prints a per-session recap. Needs the configured tunnel.
|
|
635
|
+
*/
|
|
636
|
+
declare function pluginSync(url: string, stderr?: NodeJS.WriteStream): Promise<void>;
|
|
637
|
+
/**
|
|
638
|
+
* `remote plugin sync --check` — a READ-ONLY drift report (converges NOTHING).
|
|
639
|
+
* For each live Pod it probes the actual installed state (npm ls -g --json for
|
|
640
|
+
* the desired pkgs + the registered MCP server names from the config files the
|
|
641
|
+
* existing sync writes) via the argv-safe pod exec, then the pure
|
|
642
|
+
* `diffManifest` rows it: ok / version-drift / missing / mcp-unregistered.
|
|
643
|
+
* Prints a table and EXITS 1 when ANY row is drift (via process.exitCode). This
|
|
644
|
+
* REPLACES the `plugin ls` `REMOTE ?` guesswork with REAL per-Pod status. Pure
|
|
645
|
+
* decisions live in plugin-manifest.ts; this is the thin probe + printer.
|
|
646
|
+
*/
|
|
647
|
+
declare function pluginSyncCheck(url: string, stdout?: NodeJS.WriteStream, stderr?: NodeJS.WriteStream): Promise<DriftRow[]>;
|
|
648
|
+
|
|
649
|
+
declare const packageName = "@sentropic/remote-cli";
|
|
650
|
+
|
|
651
|
+
/** Validate `--watch <minutes>`: a whole number of minutes >= 1. */
|
|
652
|
+
declare function parseWatchMinutes(raw: string): number;
|
|
653
|
+
/**
|
|
654
|
+
* Soft-refresh EVERY live remote session (profile carried by each session).
|
|
655
|
+
* Per-session errors don't stop the pass; ends with a recap (ok / unchanged /
|
|
656
|
+
* failed) and returns the failure count. `hashes` carries the previous pass's
|
|
657
|
+
* bundle hashes (sessionId -> sha256) so unchanged creds are a no-op WITHOUT
|
|
658
|
+
* respawning the Pod CLI.
|
|
659
|
+
*/
|
|
660
|
+
declare function softRefreshAllSessions(url: string, opts: {
|
|
661
|
+
authRefresh?: boolean;
|
|
662
|
+
}, hashes: Map<string, string>): Promise<{
|
|
663
|
+
failed: number;
|
|
664
|
+
}>;
|
|
665
|
+
/**
|
|
666
|
+
* Foreground refresh loop for `--watch <minutes>`: pass, sleep, repeat. NO
|
|
667
|
+
* daemonization, no pid file — the user runs it in a dedicated tmux window.
|
|
668
|
+
* Each pass is timestamped on stderr; SIGINT (Ctrl-C) stops it cleanly with a
|
|
669
|
+
* message and exit 0. Pass failures are logged and the loop keeps going.
|
|
670
|
+
*/
|
|
671
|
+
declare function watchRefreshLoop(minutes: number, pass: () => Promise<{
|
|
672
|
+
failed: number;
|
|
673
|
+
}>, signals?: {
|
|
674
|
+
on(event: "SIGINT", listener: () => void): unknown;
|
|
675
|
+
removeListener(event: "SIGINT", listener: () => void): unknown;
|
|
676
|
+
}): Promise<number>;
|
|
677
|
+
/**
|
|
678
|
+
* P4 — the conductor's FOREGROUND watch loop (NO daemon, NO pid file — run it in
|
|
679
|
+
* a dedicated tmux window, exactly like `watchRefreshLoop` / `h2a bridge
|
|
680
|
+
* --watch`). Each pass is timestamped; SIGINT (Ctrl-C) stops it cleanly with a
|
|
681
|
+
* message and exit 0. A pass failure is logged and the loop keeps going. The
|
|
682
|
+
* `pass` is the conductor pass (reconcile + start `pending` jobs under the cap);
|
|
683
|
+
* `signals` is injectable so tests never emit a real SIGINT.
|
|
684
|
+
*/
|
|
685
|
+
declare function conductLoop(minutes: number, pass: () => Promise<{
|
|
686
|
+
started: number;
|
|
687
|
+
finished: number;
|
|
688
|
+
}>, signals?: {
|
|
689
|
+
on(event: "SIGINT", listener: () => void): unknown;
|
|
690
|
+
removeListener(event: "SIGINT", listener: () => void): unknown;
|
|
691
|
+
}): Promise<number>;
|
|
692
|
+
type StartJobResult = {
|
|
693
|
+
started: true;
|
|
694
|
+
target: "local" | "remote";
|
|
695
|
+
detail: string;
|
|
696
|
+
} | {
|
|
697
|
+
started: false;
|
|
698
|
+
error: string;
|
|
699
|
+
};
|
|
700
|
+
/**
|
|
701
|
+
* Launch a job that is enrolled in the registry (typically `pending`): spawn the
|
|
702
|
+
* agent (local detached tmux, or a Pod), advance the registry entry to
|
|
703
|
+
* `running`, mirror it under track (best-effort), and propagate the spawn-depth
|
|
704
|
+
* budget to the child via `REMOTE_DELEGATE_DEPTH`. The launch params are read
|
|
705
|
+
* from the entry's queued-launch fields (tool/task/headless/remoteTarget/
|
|
706
|
+
* originCwd/explicitCwd/depthBudget/trackWp), set at `delegate` time.
|
|
707
|
+
*
|
|
708
|
+
* Never throws: any spawn/registry error is returned as `{started:false}` so the
|
|
709
|
+
* conductor loop keeps going. For remote, the per-job workspace is created here
|
|
710
|
+
* (so a queued remote job doesn't hold a workspace while waiting).
|
|
711
|
+
*/
|
|
712
|
+
declare function startJob(job: RegistryEntry): Promise<StartJobResult>;
|
|
713
|
+
/**
|
|
714
|
+
* Reliability slice 1 — RESUME a throttled HEADLESS LOCAL job. Relaunch the SAME
|
|
715
|
+
* job in the SAME `runCwd` it already ran in (its recorded `cwd` — NOT a fresh
|
|
716
|
+
* worktree, so it continues the prior conversation in place) with the tool's
|
|
717
|
+
* CONTINUE flag (`claude -p --continue` / `codex exec resume --last`, via the
|
|
718
|
+
* safe argv `buildThrottleResumeArgs` — never `bash -lc` concat), redirecting to
|
|
719
|
+
* the SAME result.json/output.log, then transition `throttled → running`. The
|
|
720
|
+
* throttle bookkeeping is PRESERVED so a re-throttle bumps `attempts` (the cap is
|
|
721
|
+
* enforced in reconcile). Never throws — a spawn error is returned as
|
|
722
|
+
* `{started:false}` so the conductor keeps going.
|
|
723
|
+
*
|
|
724
|
+
* SCOPE: headless local only. Interactive resume (send-keys) and remote resume
|
|
725
|
+
* (control-plane) are phase 2 — see the TODOs at the call site.
|
|
726
|
+
*/
|
|
727
|
+
declare function resumeThrottledJob(job: RegistryEntry): StartJobResult;
|
|
728
|
+
declare function main(argv: ReadonlyArray<string>): Promise<number>;
|
|
729
|
+
|
|
730
|
+
export { type AttachOptions, type AttachResult, type AuthBundle, AuthBundleMissingError, type AuthDiagnosticsResult, type AuthDiagnosticsStatus, AuthRefreshError, type MigrateBackOptions, type MigrateBackResult, type MigrateForwardOptions, type MigrateForwardResult, type ProfileConfig, type RunOptions, type RunResult, type SmokeRemoteProfileOptions, type SmokeRemoteProfileResult, type StartJobResult, assertRequiredAuthBundle, attach, buildPodSyncScript, coerceCliProfileName, collectProfileAuth, conductLoop, createRemoteSession, detectMcpBins, ensureProfileAuthFresh, getRemoteSession, inspectProfileAuth, isCliProfile, listRemoteSessions, main, mergeClaudeMcpServers, migrateBack, migrateForward, packageName, parseMcpSpec, parseMcpSpecs, parseWatchMinutes, pluginAdd, pluginAddInstaller, pluginLs, pluginSync, pluginSyncCheck, reconcileSessionPlugins, refreshRemoteSession, renameRemoteSession, resolveProfile, resumeThrottledJob, run, smokeRemoteProfile, softRefreshAllSessions, startJob, stopRemoteSession, upsertCodexMcpServer, watchRefreshLoop, withResume };
|