@camerontaylor/paseo-cli 0.8.0-fork.1 → 0.8.0-fork.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/cli.js +25 -37
- package/dist/commands/agent/archive.js +2 -15
- package/dist/commands/agent/attach.d.ts +1 -0
- package/dist/commands/agent/attach.js +8 -12
- package/dist/commands/agent/delete.js +4 -15
- package/dist/commands/agent/detach.js +2 -13
- package/dist/commands/agent/import.js +2 -16
- package/dist/commands/agent/inspect.js +2 -15
- package/dist/commands/agent/logs.js +12 -12
- package/dist/commands/agent/ls.js +2 -23
- package/dist/commands/agent/mode.js +2 -2
- package/dist/commands/agent/open.js +2 -8
- package/dist/commands/agent/reload.js +2 -15
- package/dist/commands/agent/run.js +11 -16
- package/dist/commands/agent/send.js +6 -15
- package/dist/commands/agent/stop.js +2 -15
- package/dist/commands/agent/update.js +2 -15
- package/dist/commands/agent/wait.js +4 -15
- package/dist/commands/clone.js +2 -8
- package/dist/commands/daemon/config.d.ts +3 -0
- package/dist/commands/daemon/config.js +102 -0
- package/dist/commands/daemon/index.js +20 -52
- package/dist/commands/daemon/local-daemon.d.ts +10 -75
- package/dist/commands/daemon/local-daemon.js +61 -503
- package/dist/commands/daemon/pair.d.ts +3 -8
- package/dist/commands/daemon/pair.js +31 -61
- package/dist/commands/daemon/reload.d.ts +3 -1
- package/dist/commands/daemon/reload.js +10 -2
- package/dist/commands/daemon/restart.d.ts +19 -11
- package/dist/commands/daemon/restart.js +102 -88
- package/dist/commands/daemon/set-password.js +6 -7
- package/dist/commands/daemon/start.d.ts +17 -3
- package/dist/commands/daemon/start.js +47 -52
- package/dist/commands/daemon/status.d.ts +12 -20
- package/dist/commands/daemon/status.js +90 -342
- package/dist/commands/daemon/stop.d.ts +19 -14
- package/dist/commands/daemon/stop.js +53 -55
- package/dist/commands/heartbeat/index.js +3 -3
- package/dist/commands/hub/connect.d.ts +1 -0
- package/dist/commands/hub/connect.js +1 -1
- package/dist/commands/hub/daemon-client.d.ts +3 -2
- package/dist/commands/hub/daemon-client.js +3 -3
- package/dist/commands/hub/disconnect.d.ts +1 -0
- package/dist/commands/hub/disconnect.js +1 -1
- package/dist/commands/hub/index.js +2 -2
- package/dist/commands/hub/init.d.ts +3 -1
- package/dist/commands/hub/init.js +13 -12
- package/dist/commands/hub/login.d.ts +4 -4
- package/dist/commands/hub/login.js +1 -1
- package/dist/commands/hub/logout.d.ts +1 -0
- package/dist/commands/hub/logout.js +2 -2
- package/dist/commands/hub/permissions.d.ts +1 -0
- package/dist/commands/hub/permissions.js +2 -2
- package/dist/commands/onboard.d.ts +7 -2
- package/dist/commands/onboard.js +58 -210
- package/dist/commands/open.js +2 -0
- package/dist/commands/permit/allow.js +2 -15
- package/dist/commands/permit/deny.js +2 -15
- package/dist/commands/permit/ls.js +2 -15
- package/dist/commands/plugin/index.js +7 -7
- package/dist/commands/plugin/shared.d.ts +4 -3
- package/dist/commands/plugin/shared.js +8 -8
- package/dist/commands/project/create.js +5 -3
- package/dist/commands/project/delete.js +4 -2
- package/dist/commands/project/ls.js +2 -2
- package/dist/commands/project/rename.js +4 -2
- package/dist/commands/provider/add.js +2 -2
- package/dist/commands/provider/diagnostic.js +1 -1
- package/dist/commands/provider/ls.js +2 -30
- package/dist/commands/provider/models.js +1 -1
- package/dist/commands/provider/rm.js +2 -2
- package/dist/commands/schedule/create.js +2 -2
- package/dist/commands/schedule/delete.js +1 -1
- package/dist/commands/schedule/inspect.js +1 -1
- package/dist/commands/schedule/logs.js +1 -1
- package/dist/commands/schedule/ls.js +1 -1
- package/dist/commands/schedule/pause.js +1 -1
- package/dist/commands/schedule/resume.js +1 -1
- package/dist/commands/schedule/run-once.js +1 -1
- package/dist/commands/schedule/shared.d.ts +3 -1
- package/dist/commands/schedule/shared.js +7 -5
- package/dist/commands/schedule/update.js +1 -1
- package/dist/commands/script/ls.js +1 -1
- package/dist/commands/script/shared.d.ts +2 -1
- package/dist/commands/script/shared.js +3 -3
- package/dist/commands/script/start.js +1 -1
- package/dist/commands/script/stop.js +1 -1
- package/dist/commands/terminal/capture.d.ts +1 -1
- package/dist/commands/terminal/capture.js +2 -3
- package/dist/commands/terminal/create.js +1 -1
- package/dist/commands/terminal/kill.js +1 -1
- package/dist/commands/terminal/ls.js +1 -1
- package/dist/commands/terminal/send-keys.d.ts +1 -1
- package/dist/commands/terminal/send-keys.js +2 -3
- package/dist/commands/terminal/shared.d.ts +2 -1
- package/dist/commands/terminal/shared.js +5 -3
- package/dist/commands/workspace/archive.d.ts +1 -0
- package/dist/commands/workspace/archive.js +4 -2
- package/dist/commands/workspace/create.js +4 -2
- package/dist/commands/workspace/ls.d.ts +1 -0
- package/dist/commands/workspace/ls.js +2 -2
- package/dist/commands/workspace/rename.d.ts +1 -0
- package/dist/commands/workspace/rename.js +4 -2
- package/dist/commands/workspace/setup.d.ts +1 -0
- package/dist/commands/workspace/setup.js +2 -2
- package/dist/commands/worktree/archive.js +4 -2
- package/dist/commands/worktree/create.js +2 -10
- package/dist/commands/worktree/ls.d.ts +0 -2
- package/dist/commands/worktree/ls.js +4 -30
- package/dist/output/with-output.d.ts +2 -0
- package/dist/run.js +11 -1
- package/dist/utils/client-id.d.ts +1 -1
- package/dist/utils/client-id.js +8 -14
- package/dist/utils/client.d.ts +16 -21
- package/dist/utils/client.js +57 -109
- package/dist/utils/command-options.d.ts +1 -0
- package/dist/utils/command-options.js +24 -2
- package/dist/utils/daemon-target.d.ts +13 -0
- package/dist/utils/daemon-target.js +51 -0
- package/package.json +8 -4
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DaemonTarget } from "../../utils/daemon-target.js";
|
|
1
2
|
import type { ProviderSnapshotEntry } from "@camerontaylor/paseo-protocol/agent-types";
|
|
2
3
|
export interface HubStatus {
|
|
3
4
|
state: string;
|
|
@@ -33,8 +34,8 @@ export interface HubDaemonClient {
|
|
|
33
34
|
close(): Promise<void>;
|
|
34
35
|
}
|
|
35
36
|
export interface HubDaemonConnection {
|
|
36
|
-
connect(
|
|
37
|
+
connect(target: DaemonTarget): Promise<HubDaemonClient>;
|
|
37
38
|
}
|
|
38
39
|
export declare const productionHubDaemonConnection: HubDaemonConnection;
|
|
39
|
-
export declare function withHubDaemon<T>(connection: HubDaemonConnection,
|
|
40
|
+
export declare function withHubDaemon<T>(connection: HubDaemonConnection, target: DaemonTarget, action: (client: HubDaemonClient) => Promise<T>): Promise<T>;
|
|
40
41
|
//# sourceMappingURL=daemon-client.d.ts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { connectToDaemon } from "../../utils/client.js";
|
|
2
2
|
export const productionHubDaemonConnection = {
|
|
3
|
-
connect: (
|
|
3
|
+
connect: (target) => connectToDaemon({ target }),
|
|
4
4
|
};
|
|
5
|
-
export async function withHubDaemon(connection,
|
|
6
|
-
const client = await connection.connect(
|
|
5
|
+
export async function withHubDaemon(connection, target, action) {
|
|
6
|
+
const client = await connection.connect(target);
|
|
7
7
|
try {
|
|
8
8
|
return await action(client);
|
|
9
9
|
}
|
|
@@ -4,7 +4,7 @@ import { withHubDaemon } from "./daemon-client.js";
|
|
|
4
4
|
import { reportHubProgress } from "./reporter.js";
|
|
5
5
|
import { hubStatusResult } from "./status-output.js";
|
|
6
6
|
export function runHubDisconnect(options, dependencies) {
|
|
7
|
-
return withHubDaemon(dependencies.daemon, options.
|
|
7
|
+
return withHubDaemon(dependencies.daemon, options.daemonTarget, async (client) => {
|
|
8
8
|
const current = (await client.getHubStatus()).status;
|
|
9
9
|
if (current.hubOrigin !== null) {
|
|
10
10
|
reportHubProgress(dependencies.reporter, options, `Disconnecting this daemon from ${current.hubOrigin}`);
|
|
@@ -40,7 +40,7 @@ export function createHubCommand(overrides = {}) {
|
|
|
40
40
|
flow: environment.login,
|
|
41
41
|
reporter: environment.reporter,
|
|
42
42
|
isInteractive: environment.isInteractive,
|
|
43
|
-
continueGuidedSetup: (origin) => continueHubGuidedSetup(origin, environment),
|
|
43
|
+
continueGuidedSetup: (origin, daemonTarget) => continueHubGuidedSetup(origin, { ...environment, daemonTarget }),
|
|
44
44
|
});
|
|
45
45
|
addHubInitCommand(hub, environment);
|
|
46
46
|
addHubConnectCommand(hub, {
|
|
@@ -52,7 +52,7 @@ export function createHubCommand(overrides = {}) {
|
|
|
52
52
|
});
|
|
53
53
|
addJsonAndDaemonHostOptions(hub.command("status")).action(withOutput(async (...args) => {
|
|
54
54
|
const options = args.at(-2);
|
|
55
|
-
return withHubDaemon(environment.daemon, options.
|
|
55
|
+
return withHubDaemon(environment.daemon, options.daemonTarget, async (client) => hubStatusResult((await client.getHubStatus()).status));
|
|
56
56
|
}));
|
|
57
57
|
addHubDisconnectCommand(hub, {
|
|
58
58
|
daemon: environment.daemon,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DaemonTarget } from "../../utils/daemon-target.js";
|
|
1
2
|
import { select, text } from "@clack/prompts";
|
|
2
3
|
import type { Command } from "commander";
|
|
3
4
|
import type { HubCredentialStore } from "./credentials.js";
|
|
@@ -6,6 +7,7 @@ import type { HubHttpClient } from "./hub-client/index.js";
|
|
|
6
7
|
import type { CliLoginFlow } from "./login-flow.js";
|
|
7
8
|
import type { HubReporter } from "./reporter.js";
|
|
8
9
|
export interface HubGuidedSetupEnvironment {
|
|
10
|
+
daemonTarget: DaemonTarget;
|
|
9
11
|
env: Readonly<Record<string, string | undefined>>;
|
|
10
12
|
credentials: HubCredentialStore;
|
|
11
13
|
hub: HubHttpClient;
|
|
@@ -26,7 +28,7 @@ interface HubGuidedSetupState {
|
|
|
26
28
|
daemonId?: string;
|
|
27
29
|
deploy?: boolean;
|
|
28
30
|
}
|
|
29
|
-
export declare function addHubInitCommand(parent: Command, environment: HubGuidedSetupEnvironment): void;
|
|
31
|
+
export declare function addHubInitCommand(parent: Command, environment: Omit<HubGuidedSetupEnvironment, "daemonTarget">): void;
|
|
30
32
|
export declare function runHubInit(environment: HubGuidedSetupEnvironment): Promise<void>;
|
|
31
33
|
export declare function runHubGuidedSetup(environment: HubGuidedSetupEnvironment, state?: HubGuidedSetupState): Promise<void>;
|
|
32
34
|
export declare function continueHubGuidedSetup(origin: string, environment: HubGuidedSetupEnvironment): Promise<void>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withGlobalOptions } from "../../utils/command-options.js";
|
|
1
2
|
import { cancel, confirm, intro, isCancel, log, note, outro, select, spinner, text, } from "@clack/prompts";
|
|
2
3
|
import { execFile } from "node:child_process";
|
|
3
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -28,9 +29,9 @@ export function addHubInitCommand(parent, environment) {
|
|
|
28
29
|
parent
|
|
29
30
|
.command("init")
|
|
30
31
|
.description("Create and optionally deploy a safe starter Hub trigger")
|
|
31
|
-
.action(async () => {
|
|
32
|
+
.action(withGlobalOptions(async (options) => {
|
|
32
33
|
try {
|
|
33
|
-
await runHubInit(environment);
|
|
34
|
+
await runHubInit({ ...environment, daemonTarget: options.daemonTarget });
|
|
34
35
|
}
|
|
35
36
|
catch (error) {
|
|
36
37
|
if (error instanceof HubInitCancelledError) {
|
|
@@ -40,7 +41,7 @@ export function addHubInitCommand(parent, environment) {
|
|
|
40
41
|
cancel(initErrorMessage(error));
|
|
41
42
|
process.exitCode = 1;
|
|
42
43
|
}
|
|
43
|
-
});
|
|
44
|
+
}));
|
|
44
45
|
}
|
|
45
46
|
export async function runHubInit(environment) {
|
|
46
47
|
await runHubGuidedSetup(environment);
|
|
@@ -102,13 +103,13 @@ export async function runHubGuidedSetup(environment, state = {}) {
|
|
|
102
103
|
outro(deploy ? "Hub is ready" : "Hub trigger is ready");
|
|
103
104
|
}
|
|
104
105
|
export async function continueHubGuidedSetup(origin, environment) {
|
|
105
|
-
const currentStatus = await withHubDaemon(environment.daemon,
|
|
106
|
+
const currentStatus = await withHubDaemon(environment.daemon, environment.daemonTarget, async (daemon) => daemon.getHubStatus().then((response) => response.status));
|
|
106
107
|
const current = resolveHubInitConnection(currentStatus, origin);
|
|
107
108
|
if (current.kind === "connected") {
|
|
108
109
|
reportMessage(environment, `This daemon is already connected to ${origin}. Permissions: ${currentStatus.permissions.join(", ") || "None"}.`);
|
|
109
110
|
}
|
|
110
111
|
else if (current.kind === "pending") {
|
|
111
|
-
await waitForDaemonReady(origin, environment
|
|
112
|
+
await waitForDaemonReady(origin, environment);
|
|
112
113
|
}
|
|
113
114
|
else if (current.kind === "conflict") {
|
|
114
115
|
reportMessage(environment, `This daemon is connected to ${current.origin}. Disconnect it before connecting to ${origin}.`);
|
|
@@ -163,7 +164,7 @@ async function ensureLogin(activeOrigin, environment) {
|
|
|
163
164
|
return normalizedOrigin;
|
|
164
165
|
}
|
|
165
166
|
async function ensureDaemonConnection(origin, environment, confirmed = false, permissions = ["hub.execute"]) {
|
|
166
|
-
const status = await withHubDaemon(environment.daemon,
|
|
167
|
+
const status = await withHubDaemon(environment.daemon, environment.daemonTarget, async (daemon) => daemon.getHubStatus().then((response) => response.status));
|
|
167
168
|
const connection = resolveHubInitConnection(status, origin);
|
|
168
169
|
if (connection.kind === "connected") {
|
|
169
170
|
if (permissions.includes("hub.execute") && !status.permissions.includes("hub.execute")) {
|
|
@@ -172,7 +173,7 @@ async function ensureDaemonConnection(origin, environment, confirmed = false, pe
|
|
|
172
173
|
return connection.daemonId;
|
|
173
174
|
}
|
|
174
175
|
if (connection.kind === "pending") {
|
|
175
|
-
return waitForDaemonReady(origin, environment
|
|
176
|
+
return waitForDaemonReady(origin, environment);
|
|
176
177
|
}
|
|
177
178
|
if (connection.kind === "conflict") {
|
|
178
179
|
throw new HubCommandError("HUB_DAEMON_ALREADY_CONNECTED", `This daemon is connected to ${connection.origin}. Disconnect it before running Hub init for ${origin}.`);
|
|
@@ -184,17 +185,17 @@ async function ensureDaemonConnection(origin, environment, confirmed = false, pe
|
|
|
184
185
|
return connectDaemon(origin, environment, permissions);
|
|
185
186
|
}
|
|
186
187
|
async function connectDaemon(origin, environment, permissions = ["hub.execute"]) {
|
|
187
|
-
await runHubConnect(origin, { permissions }, {
|
|
188
|
+
await runHubConnect(origin, { permissions, daemonTarget: environment.daemonTarget }, {
|
|
188
189
|
env: environment.env,
|
|
189
190
|
credentials: environment.credentials,
|
|
190
191
|
hub: environment.hub,
|
|
191
192
|
daemon: environment.daemon,
|
|
192
193
|
reporter: environment.reporter,
|
|
193
194
|
});
|
|
194
|
-
return waitForDaemonReady(origin, environment
|
|
195
|
+
return waitForDaemonReady(origin, environment);
|
|
195
196
|
}
|
|
196
|
-
async function waitForDaemonReady(origin,
|
|
197
|
-
return withSpinner("Waiting for the daemon to connect", async (reporter) => withHubDaemon(
|
|
197
|
+
async function waitForDaemonReady(origin, environment) {
|
|
198
|
+
return withSpinner("Waiting for the daemon to connect", async (reporter) => withHubDaemon(environment.daemon, environment.daemonTarget, async (daemon) => {
|
|
198
199
|
const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
|
|
199
200
|
while (true) {
|
|
200
201
|
const status = (await daemon.getHubStatus()).status;
|
|
@@ -260,7 +261,7 @@ async function chooseStarterAgentRuntime(environment, cwd) {
|
|
|
260
261
|
return { ...runtime, mode: runtime.mode };
|
|
261
262
|
}
|
|
262
263
|
async function waitForStarterAgentProviders(environment, cwd) {
|
|
263
|
-
return withSpinner("Discovering agent runtimes", async (reporter) => withHubDaemon(environment.daemon,
|
|
264
|
+
return withSpinner("Discovering agent runtimes", async (reporter) => withHubDaemon(environment.daemon, environment.daemonTarget, async (daemon) => {
|
|
264
265
|
const deadline = Date.now() + PROVIDER_READY_TIMEOUT_MS;
|
|
265
266
|
while (true) {
|
|
266
267
|
const snapshot = await daemon.getProvidersSnapshot({ cwd });
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { CommandOptions } from "../../output/index.js";
|
|
2
|
+
import type { DaemonTarget } from "../../utils/daemon-target.js";
|
|
1
3
|
import type { Command } from "commander";
|
|
2
4
|
import { type SingleResult } from "../../output/index.js";
|
|
3
5
|
import type { HubCredentialStore } from "./credentials.js";
|
|
@@ -13,11 +15,9 @@ interface HubLoginDependencies {
|
|
|
13
15
|
flow: Pick<CliLoginFlow, "authorize">;
|
|
14
16
|
reporter: HubReporter;
|
|
15
17
|
isInteractive?(): boolean;
|
|
16
|
-
continueGuidedSetup?(origin: string): Promise<void>;
|
|
18
|
+
continueGuidedSetup?(origin: string, daemonTarget: DaemonTarget): Promise<void>;
|
|
17
19
|
}
|
|
18
|
-
export declare function runHubLogin(originInput: string | undefined, options:
|
|
19
|
-
json?: boolean;
|
|
20
|
-
}, dependencies: HubLoginDependencies): Promise<SingleResult<HubLoginResult>>;
|
|
20
|
+
export declare function runHubLogin(originInput: string | undefined, options: Pick<CommandOptions, "json" | "daemonTarget">, dependencies: HubLoginDependencies): Promise<SingleResult<HubLoginResult>>;
|
|
21
21
|
export declare function addHubLoginCommand(parent: Command, dependencies: HubLoginDependencies): void;
|
|
22
22
|
export {};
|
|
23
23
|
//# sourceMappingURL=login.d.ts.map
|
|
@@ -23,7 +23,7 @@ export async function runHubLogin(originInput, options, dependencies) {
|
|
|
23
23
|
if (!options.json &&
|
|
24
24
|
dependencies.isInteractive?.() &&
|
|
25
25
|
dependencies.continueGuidedSetup !== undefined) {
|
|
26
|
-
await dependencies.continueGuidedSetup(origin);
|
|
26
|
+
await dependencies.continueGuidedSetup(origin, options.daemonTarget);
|
|
27
27
|
}
|
|
28
28
|
return { type: "single", data: { origin, status: "logged_in" }, schema };
|
|
29
29
|
}
|
|
@@ -22,7 +22,7 @@ export async function runHubLogout(options, dependencies) {
|
|
|
22
22
|
let daemonStatus = null;
|
|
23
23
|
const shouldInspectDaemon = options.disconnectDaemon === true || mayPrompt;
|
|
24
24
|
if (shouldInspectDaemon) {
|
|
25
|
-
daemonStatus = await withHubDaemon(dependencies.daemon, options.
|
|
25
|
+
daemonStatus = await withHubDaemon(dependencies.daemon, options.daemonTarget, async (daemon) => daemon.getHubStatus().then((response) => response.status));
|
|
26
26
|
}
|
|
27
27
|
const sameHub = daemonStatus?.hubOrigin === active.origin;
|
|
28
28
|
let shouldDisconnect = options.disconnectDaemon === true && sameHub;
|
|
@@ -34,7 +34,7 @@ export async function runHubLogout(options, dependencies) {
|
|
|
34
34
|
return logoutResult({ origin: active.origin, status: "logged_out", daemonDisconnected: false });
|
|
35
35
|
}
|
|
36
36
|
reportHubProgress(dependencies.reporter, options, `Disconnecting this daemon from ${active.origin}`);
|
|
37
|
-
const disconnect = await withHubDaemon(dependencies.daemon, options.
|
|
37
|
+
const disconnect = await withHubDaemon(dependencies.daemon, options.daemonTarget, async (daemon) => {
|
|
38
38
|
return daemon.disconnectHub(options.force ?? false);
|
|
39
39
|
});
|
|
40
40
|
dependencies.credentials.logoutActive();
|
|
@@ -4,6 +4,7 @@ import type { HubDaemonConnection } from "./daemon-client.js";
|
|
|
4
4
|
import { type HubReporter } from "./reporter.js";
|
|
5
5
|
interface HubPermissionsOptions {
|
|
6
6
|
host?: string;
|
|
7
|
+
daemonTarget: import("../../utils/daemon-target.js").DaemonTarget;
|
|
7
8
|
json?: boolean;
|
|
8
9
|
}
|
|
9
10
|
interface HubPermissionsDependencies {
|
|
@@ -11,7 +11,7 @@ const schema = {
|
|
|
11
11
|
],
|
|
12
12
|
};
|
|
13
13
|
export function runHubPermissionsList(options, dependencies) {
|
|
14
|
-
return withHubDaemon(dependencies.daemon, options.
|
|
14
|
+
return withHubDaemon(dependencies.daemon, options.daemonTarget, async (client) => {
|
|
15
15
|
const status = (await client.getHubStatus()).status;
|
|
16
16
|
requireConnectedHub(status);
|
|
17
17
|
return {
|
|
@@ -25,7 +25,7 @@ export function runHubPermissionsList(options, dependencies) {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
export function runHubPermissionChange(operation, permission, options, dependencies) {
|
|
28
|
-
return withHubDaemon(dependencies.daemon, options.
|
|
28
|
+
return withHubDaemon(dependencies.daemon, options.daemonTarget, async (client) => {
|
|
29
29
|
const current = (await client.getHubStatus()).status;
|
|
30
30
|
requireConnectedHub(current);
|
|
31
31
|
const response = await client.updateHubPermissions(operation === "grant" ? { grant: [permission] } : { revoke: [permission] });
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
3
|
-
interface OnboardOptions extends
|
|
2
|
+
import type { CommandOptions } from "../output/index.js";
|
|
3
|
+
interface OnboardOptions extends CommandOptions {
|
|
4
|
+
port?: string;
|
|
5
|
+
listen?: string;
|
|
6
|
+
relay?: boolean;
|
|
7
|
+
mcp?: boolean;
|
|
8
|
+
hostnames?: string;
|
|
4
9
|
timeout?: string;
|
|
5
10
|
voice?: "ask" | "enable" | "disable";
|
|
6
11
|
}
|
package/dist/commands/onboard.js
CHANGED
|
@@ -1,39 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { addLocalDaemonOptions } from "../utils/command-options.js";
|
|
2
|
+
import { cancel, confirm, intro, isCancel, log, note, outro } from "@clack/prompts";
|
|
2
3
|
import { Command, Option } from "commander";
|
|
3
|
-
import { writeFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { loadPersistedConfig } from "@camerontaylor/paseo-server";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { readPersistedConfig as loadPersistedConfig, savePersistedConfig, readDaemonInstance, waitForDaemonReady, } from "@camerontaylor/paseo-server";
|
|
6
|
+
import { withGlobalOptions } from "../utils/command-options.js";
|
|
7
|
+
import { launchLocalDaemon, parseTimeoutMs } from "./daemon/local-daemon.js";
|
|
8
|
+
import { connectToDaemon } from "../utils/client.js";
|
|
8
9
|
import { formatPairingInstructions } from "../output/pairing.js";
|
|
9
10
|
import { confirmRelayPairing, printDirectConnectionGuidance, resolveLocalPairingOffer, } from "./daemon/pair.js";
|
|
10
|
-
const DEFAULT_READY_TIMEOUT_MS = 10 * 60 * 1000;
|
|
11
|
-
const READY_PROBE_TIMEOUT_MS = 1200;
|
|
12
11
|
class OnboardCancelledError extends Error {
|
|
13
12
|
}
|
|
14
13
|
const plainNoteFormat = (line) => line;
|
|
15
14
|
function renderNote(message, title) {
|
|
16
15
|
note(message, title, { format: plainNoteFormat });
|
|
17
16
|
}
|
|
18
|
-
function sleep(ms) {
|
|
19
|
-
return new Promise((resolve) => {
|
|
20
|
-
setTimeout(resolve, ms);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
function parseTimeoutMs(raw) {
|
|
24
|
-
if (!raw || raw.trim().length === 0) {
|
|
25
|
-
return DEFAULT_READY_TIMEOUT_MS;
|
|
26
|
-
}
|
|
27
|
-
const seconds = Number(raw);
|
|
28
|
-
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
29
|
-
throw new Error(`Invalid timeout value: ${raw}`);
|
|
30
|
-
}
|
|
31
|
-
return Math.ceil(seconds * 1000);
|
|
32
|
-
}
|
|
33
|
-
function savePersistedConfig(paseoHome, config) {
|
|
34
|
-
const configPath = path.join(paseoHome, "config.json");
|
|
35
|
-
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
36
|
-
}
|
|
37
17
|
function applyVoiceSelection(config, enabled) {
|
|
38
18
|
return {
|
|
39
19
|
...config,
|
|
@@ -83,101 +63,6 @@ async function resolveVoiceSelection(mode) {
|
|
|
83
63
|
}
|
|
84
64
|
return answer;
|
|
85
65
|
}
|
|
86
|
-
function parseDownloadProgress(logTail) {
|
|
87
|
-
const lines = logTail.split("\n").filter(Boolean);
|
|
88
|
-
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
89
|
-
const line = lines[index];
|
|
90
|
-
if (!line || !line.includes("Downloading model artifact")) {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
const pctMatch = line.match(/"pct"\s*:\s*(\d{1,3})|\bpct[=:]\s*(\d{1,3})/);
|
|
94
|
-
const modelMatch = line.match(/"modelId"\s*:\s*"([^"]+)"|\bmodelId[=:]\s*"?([^\s",}]+)/);
|
|
95
|
-
return {
|
|
96
|
-
modelId: modelMatch?.[1] ?? modelMatch?.[2] ?? null,
|
|
97
|
-
pct: pctMatch ? Number(pctMatch[1] ?? pctMatch[2]) : null,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
function renderProgressLine(progress) {
|
|
103
|
-
const modelSuffix = progress.modelId ? ` (${progress.modelId})` : "";
|
|
104
|
-
if (progress.pct === null) {
|
|
105
|
-
return `Downloading speech model${modelSuffix}...`;
|
|
106
|
-
}
|
|
107
|
-
return `Downloading speech model${modelSuffix}: ${progress.pct}%`;
|
|
108
|
-
}
|
|
109
|
-
async function probeDaemonReady(home, timeoutMs) {
|
|
110
|
-
const state = resolveLocalDaemonState({ home });
|
|
111
|
-
const host = resolveTcpHostFromListen(state.listen);
|
|
112
|
-
const deadline = Date.now() + timeoutMs;
|
|
113
|
-
const remainingTimeoutMs = () => Math.max(1, deadline - Date.now());
|
|
114
|
-
if (state.running && host) {
|
|
115
|
-
const client = await tryConnectToDaemon({
|
|
116
|
-
host,
|
|
117
|
-
timeout: Math.min(remainingTimeoutMs(), READY_PROBE_TIMEOUT_MS),
|
|
118
|
-
});
|
|
119
|
-
if (client) {
|
|
120
|
-
try {
|
|
121
|
-
await client.fetchAgents({
|
|
122
|
-
timeout: Math.min(remainingTimeoutMs(), READY_PROBE_TIMEOUT_MS),
|
|
123
|
-
});
|
|
124
|
-
return { kind: "ready", listen: state.listen, host };
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
// Daemon process is alive but not API-ready yet.
|
|
128
|
-
}
|
|
129
|
-
finally {
|
|
130
|
-
await client.close().catch(() => { });
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
else if (state.running && !host) {
|
|
135
|
-
return { kind: "ready", listen: state.listen, host: null };
|
|
136
|
-
}
|
|
137
|
-
return { kind: "pending" };
|
|
138
|
-
}
|
|
139
|
-
function announceProgress(home, state, onStatus) {
|
|
140
|
-
const progress = parseDownloadProgress(tailDaemonLog(home, 120) ?? "");
|
|
141
|
-
const progressLine = progress ? renderProgressLine(progress) : null;
|
|
142
|
-
const statusMessage = progressLine ?? "Waiting for daemon to become ready...";
|
|
143
|
-
if (statusMessage !== state.lastStatus) {
|
|
144
|
-
onStatus?.(statusMessage);
|
|
145
|
-
return { lastStatus: statusMessage, lastPrintedAt: Date.now() };
|
|
146
|
-
}
|
|
147
|
-
if (!onStatus && Date.now() - state.lastPrintedAt >= 3000) {
|
|
148
|
-
console.log(statusMessage);
|
|
149
|
-
return { lastStatus: state.lastStatus, lastPrintedAt: Date.now() };
|
|
150
|
-
}
|
|
151
|
-
return state;
|
|
152
|
-
}
|
|
153
|
-
async function waitForDaemonReady(args) {
|
|
154
|
-
const deadline = Date.now() + args.timeoutMs;
|
|
155
|
-
const createTimeoutError = () => {
|
|
156
|
-
const recentLogs = tailDaemonLog(args.home, 60);
|
|
157
|
-
return new Error([
|
|
158
|
-
`Timed out after ${Math.ceil(args.timeoutMs / 1000)}s waiting for daemon readiness.`,
|
|
159
|
-
recentLogs ? `Recent daemon logs:\n${recentLogs}` : null,
|
|
160
|
-
]
|
|
161
|
-
.filter(Boolean)
|
|
162
|
-
.join("\n\n"));
|
|
163
|
-
};
|
|
164
|
-
async function poll(state) {
|
|
165
|
-
if (Date.now() >= deadline) {
|
|
166
|
-
throw createTimeoutError();
|
|
167
|
-
}
|
|
168
|
-
const probe = await probeDaemonReady(args.home, Math.max(1, deadline - Date.now()));
|
|
169
|
-
if (probe.kind === "ready") {
|
|
170
|
-
return { listen: probe.listen, host: probe.host };
|
|
171
|
-
}
|
|
172
|
-
const nextState = announceProgress(args.home, state, args.onStatus);
|
|
173
|
-
if (Date.now() >= deadline) {
|
|
174
|
-
throw createTimeoutError();
|
|
175
|
-
}
|
|
176
|
-
await sleep(200);
|
|
177
|
-
return poll(nextState);
|
|
178
|
-
}
|
|
179
|
-
return poll({ lastStatus: "", lastPrintedAt: 0 });
|
|
180
|
-
}
|
|
181
66
|
function printNextSteps(pairingUrl, paseoHome, richUi) {
|
|
182
67
|
const daemonLogPath = path.join(paseoHome, "daemon.log");
|
|
183
68
|
const nextStepsLines = [
|
|
@@ -187,13 +72,13 @@ function printNextSteps(pairingUrl, paseoHome, richUi) {
|
|
|
187
72
|
"2. Web app: https://app.paseo.sh",
|
|
188
73
|
"3. Desktop app: https://github.com/getpaseo/paseo/releases/latest",
|
|
189
74
|
"4. Docs: https://paseo.sh/docs",
|
|
190
|
-
|
|
75
|
+
`5. Example: paseo run --home ${JSON.stringify(paseoHome)} --output-schema schema.json "extract fields"`,
|
|
191
76
|
];
|
|
192
77
|
const quickReferenceLines = [
|
|
193
78
|
"1. paseo --help",
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
79
|
+
`2. paseo ls --home ${JSON.stringify(paseoHome)}`,
|
|
80
|
+
`3. paseo run --home ${JSON.stringify(paseoHome)} "your prompt"`,
|
|
81
|
+
`4. paseo status --home ${JSON.stringify(paseoHome)}`,
|
|
197
82
|
`5. Daemon logs: ${daemonLogPath}`,
|
|
198
83
|
];
|
|
199
84
|
if (!richUi) {
|
|
@@ -213,11 +98,10 @@ function printNextSteps(pairingUrl, paseoHome, richUi) {
|
|
|
213
98
|
renderNote(quickReferenceLines.join("\n"), "CLI quick reference");
|
|
214
99
|
}
|
|
215
100
|
export function onboardCommand() {
|
|
216
|
-
return new Command("onboard")
|
|
101
|
+
return addLocalDaemonOptions(new Command("onboard"))
|
|
217
102
|
.description("Run first-time setup, start daemon, and print pairing instructions")
|
|
218
103
|
.option("--listen <listen>", "Listen target (host:port, port, or unix socket path)")
|
|
219
104
|
.option("--port <port>", "Port to listen on (default: 6767)")
|
|
220
|
-
.option("--home <path>", "Paseo home directory (default: ~/.paseo)")
|
|
221
105
|
.option("--relay", "Enable relay connection without prompting")
|
|
222
106
|
.option("--no-relay", "Disable relay connection")
|
|
223
107
|
.option("--no-mcp", "Disable the Agent MCP HTTP endpoint")
|
|
@@ -225,12 +109,13 @@ export function onboardCommand() {
|
|
|
225
109
|
.addOption(new Option("--allowed-hosts <hosts>").hideHelp())
|
|
226
110
|
.option("--timeout <seconds>", "Max time to wait for daemon readiness (default: 600)")
|
|
227
111
|
.option("--voice <mode>", "Voice setup mode: ask, enable, disable", "ask")
|
|
228
|
-
.action(async (options) => {
|
|
112
|
+
.action(withGlobalOptions(async (options, command) => {
|
|
229
113
|
await runOnboard({
|
|
230
114
|
...options,
|
|
115
|
+
mcp: command.getOptionValueSource("mcp") === "cli" ? options.mcp : undefined,
|
|
231
116
|
hostnames: options.hostnames ?? options.allowedHosts,
|
|
232
117
|
});
|
|
233
|
-
});
|
|
118
|
+
}));
|
|
234
119
|
}
|
|
235
120
|
async function resolveAndPersistVoice(paseoHome, options) {
|
|
236
121
|
let persisted = loadPersistedConfig(paseoHome);
|
|
@@ -257,72 +142,30 @@ async function resolveAndPersistVoice(paseoHome, options) {
|
|
|
257
142
|
savePersistedConfig(paseoHome, persisted);
|
|
258
143
|
return voiceEnabled;
|
|
259
144
|
}
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
try {
|
|
268
|
-
if (startSpinner) {
|
|
269
|
-
startSpinner.start("Starting daemon...");
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
log.message("Starting daemon...");
|
|
273
|
-
}
|
|
274
|
-
const startup = await startLocalDaemonDetached(options);
|
|
275
|
-
if (startSpinner) {
|
|
276
|
-
startSpinner.stop(`Daemon started (PID ${startup.pid ?? "unknown"})`);
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
log.message(`Daemon started (PID ${startup.pid ?? "unknown"})`);
|
|
280
|
-
}
|
|
281
|
-
log.message(`Logs: ${startup.logPath}`);
|
|
282
|
-
}
|
|
283
|
-
catch (error) {
|
|
284
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
285
|
-
if (startSpinner) {
|
|
286
|
-
startSpinner.error(message);
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
log.error(message);
|
|
290
|
-
}
|
|
291
|
-
process.exit(1);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
async function waitForDaemonReadyWithUi(args) {
|
|
295
|
-
const readySpinner = args.richUi ? spinner() : null;
|
|
296
|
-
try {
|
|
297
|
-
if (readySpinner) {
|
|
298
|
-
readySpinner.start("Waiting for daemon to become ready...");
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
log.message("Waiting for daemon to become ready...");
|
|
302
|
-
}
|
|
303
|
-
const readyState = await waitForDaemonReady({
|
|
304
|
-
home: args.home,
|
|
305
|
-
timeoutMs: args.timeoutMs,
|
|
306
|
-
onStatus: readySpinner ? (message) => readySpinner.message(message) : undefined,
|
|
307
|
-
});
|
|
308
|
-
if (readySpinner) {
|
|
309
|
-
readySpinner.stop(`Daemon ready on ${readyState.listen}`);
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
log.message(`Daemon ready on ${readyState.listen}`);
|
|
313
|
-
}
|
|
314
|
-
return readyState;
|
|
315
|
-
}
|
|
316
|
-
catch (error) {
|
|
317
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
318
|
-
if (readySpinner) {
|
|
319
|
-
readySpinner.error(message);
|
|
320
|
-
}
|
|
321
|
-
else {
|
|
322
|
-
log.error(message);
|
|
323
|
-
}
|
|
324
|
-
return process.exit(1);
|
|
145
|
+
function persistSetupChoices(paseoHome, options) {
|
|
146
|
+
const persisted = loadPersistedConfig(paseoHome, { defaultsIfMissing: true });
|
|
147
|
+
if (options.listen || options.port) {
|
|
148
|
+
persisted.daemon = {
|
|
149
|
+
...persisted.daemon,
|
|
150
|
+
listen: options.listen ?? `127.0.0.1:${options.port}`,
|
|
151
|
+
};
|
|
325
152
|
}
|
|
153
|
+
if (options.relay !== undefined)
|
|
154
|
+
persisted.daemon = {
|
|
155
|
+
...persisted.daemon,
|
|
156
|
+
relay: { ...persisted.daemon?.relay, enabled: options.relay },
|
|
157
|
+
};
|
|
158
|
+
if (options.mcp !== undefined)
|
|
159
|
+
persisted.daemon = {
|
|
160
|
+
...persisted.daemon,
|
|
161
|
+
mcp: { ...persisted.daemon?.mcp, enabled: options.mcp },
|
|
162
|
+
};
|
|
163
|
+
if (options.hostnames)
|
|
164
|
+
persisted.daemon = {
|
|
165
|
+
...persisted.daemon,
|
|
166
|
+
hostnames: options.hostnames === "true" ? true : options.hostnames.split(","),
|
|
167
|
+
};
|
|
168
|
+
savePersistedConfig(paseoHome, persisted);
|
|
326
169
|
}
|
|
327
170
|
export async function runOnboard(options) {
|
|
328
171
|
const richUi = process.stdin.isTTY && process.stdout.isTTY;
|
|
@@ -333,16 +176,12 @@ export async function runOnboard(options) {
|
|
|
333
176
|
cancel("Cannot use --listen and --port together");
|
|
334
177
|
process.exit(1);
|
|
335
178
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
cancel(message);
|
|
343
|
-
process.exit(1);
|
|
344
|
-
}
|
|
345
|
-
const paseoHome = resolveLocalPaseoHome(options.home);
|
|
179
|
+
const timeoutMs = parseTimeoutMs(options.timeout);
|
|
180
|
+
if (options.daemonTarget.kind !== "instance")
|
|
181
|
+
throw new Error("Onboarding requires a local home");
|
|
182
|
+
const paseoHome = options.daemonTarget.home;
|
|
183
|
+
const alreadyRunning = await readDaemonInstance(paseoHome);
|
|
184
|
+
persistSetupChoices(paseoHome, options);
|
|
346
185
|
if (richUi) {
|
|
347
186
|
renderNote(paseoHome, "Paseo home");
|
|
348
187
|
}
|
|
@@ -350,12 +189,21 @@ export async function runOnboard(options) {
|
|
|
350
189
|
log.message(voiceEnabled
|
|
351
190
|
? "Voice features enabled. Local speech models will be downloaded automatically if missing."
|
|
352
191
|
: "Voice features disabled. Local speech models will not be downloaded.");
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
192
|
+
if (alreadyRunning) {
|
|
193
|
+
log.message(`Daemon already running (PID ${alreadyRunning.pid}); retaining its supervisor.`);
|
|
194
|
+
const client = await connectToDaemon({ target: options.daemonTarget, timeout: timeoutMs });
|
|
195
|
+
try {
|
|
196
|
+
log.message(JSON.stringify(await client.reloadDaemonConfig()));
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
await client.close();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
await launchLocalDaemon({ home: paseoHome, timeoutMs });
|
|
204
|
+
}
|
|
205
|
+
const ready = await waitForDaemonReady(paseoHome, { timeoutMs });
|
|
206
|
+
log.message(`Daemon ready on ${ready.listen}`);
|
|
359
207
|
if (options.relay === false) {
|
|
360
208
|
log.message("Relay pairing skipped because --no-relay was provided.");
|
|
361
209
|
printNextSteps(null, paseoHome, richUi);
|
package/dist/commands/open.js
CHANGED
|
@@ -78,6 +78,8 @@ export async function openDesktopWithProject(projectPath) {
|
|
|
78
78
|
launchDesktop([projectPath]);
|
|
79
79
|
}
|
|
80
80
|
catch (error) {
|
|
81
|
+
if (error && typeof error === "object" && "code" in error)
|
|
82
|
+
throw error;
|
|
81
83
|
const message = error instanceof Error ? error.message : String(error);
|
|
82
84
|
process.stderr.write(`${message}\n`);
|
|
83
85
|
process.exitCode = 1;
|