@getpaseo/cli 0.2.2 → 0.2.4
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 +3 -0
- package/dist/commands/agent/index.js +2 -1
- package/dist/commands/agent/update.d.ts +33 -0
- package/dist/commands/agent/update.js +72 -25
- package/dist/commands/hub/cloud-device-authorization.d.ts +45 -0
- package/dist/commands/hub/cloud-device-authorization.js +92 -0
- package/dist/commands/hub/device-authorization.d.ts +37 -0
- package/dist/commands/hub/device-authorization.js +87 -0
- package/dist/commands/hub/index.d.ts +28 -1
- package/dist/commands/hub/index.js +27 -7
- package/dist/commands/schedule/create.d.ts +1 -0
- package/dist/commands/schedule/create.js +1 -0
- package/dist/commands/schedule/index.js +1 -0
- package/dist/commands/schedule/shared.d.ts +1 -0
- package/dist/commands/schedule/shared.js +10 -2
- package/dist/commands/script/index.d.ts +3 -0
- package/dist/commands/script/index.js +19 -0
- package/dist/commands/script/ls.d.ts +6 -0
- package/dist/commands/script/ls.js +20 -0
- package/dist/commands/script/schema.d.ts +5 -0
- package/dist/commands/script/schema.js +13 -0
- package/dist/commands/script/shared.d.ts +11 -0
- package/dist/commands/script/shared.js +59 -0
- package/dist/commands/script/start.d.ts +6 -0
- package/dist/commands/script/start.js +23 -0
- package/dist/commands/script/stop.d.ts +6 -0
- package/dist/commands/script/stop.js +23 -0
- package/package.json +8 -6
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { createPermitCommand } from "./commands/permit/index.js";
|
|
|
7
7
|
import { createProviderCommand } from "./commands/provider/index.js";
|
|
8
8
|
import { createScheduleCommand } from "./commands/schedule/index.js";
|
|
9
9
|
import { createSpeechCommand } from "./commands/speech/index.js";
|
|
10
|
+
import { createScriptCommand } from "./commands/script/index.js";
|
|
10
11
|
import { createTerminalCommand } from "./commands/terminal/index.js";
|
|
11
12
|
import { createWorktreeCommand } from "./commands/worktree/index.js";
|
|
12
13
|
import { createWorkspaceCommand } from "./commands/workspace/index.js";
|
|
@@ -108,6 +109,8 @@ export function createCli() {
|
|
|
108
109
|
program.addCommand(createChatCommand());
|
|
109
110
|
// Terminal commands
|
|
110
111
|
program.addCommand(createTerminalCommand());
|
|
112
|
+
// Workspace script commands
|
|
113
|
+
program.addCommand(createScriptCommand());
|
|
111
114
|
// Loop commands
|
|
112
115
|
program.addCommand(createLoopCommand());
|
|
113
116
|
// Schedule commands
|
|
@@ -46,9 +46,10 @@ export function createAgentCommand() {
|
|
|
46
46
|
.argument("<id>", "Agent ID, prefix, or name")).action(withOutput(runDetachCommand));
|
|
47
47
|
addJsonAndDaemonHostOptions(agent
|
|
48
48
|
.command("update")
|
|
49
|
-
.description("Update an agent's metadata")
|
|
49
|
+
.description("Update an agent's settings or metadata")
|
|
50
50
|
.argument("<id>", "Agent ID (or prefix)")
|
|
51
51
|
.option("--name <name>", "Update the agent's display name")
|
|
52
|
+
.option("--thinking <id>", "Update the agent's thinking option ID")
|
|
52
53
|
.option("--label <label>", "Add/set label(s) on the agent (can be used multiple times or comma-separated)", collectMultiple, [])).action(withOutput(runUpdateCommand));
|
|
53
54
|
return agent;
|
|
54
55
|
}
|
|
@@ -1,18 +1,51 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
+
import type { AgentProviderNotice } from "@getpaseo/protocol/agent-types";
|
|
3
|
+
import type { AgentSnapshotPayload } from "@getpaseo/protocol/messages";
|
|
2
4
|
import type { CommandOptions, SingleResult, OutputSchema } from "../../output/index.js";
|
|
3
5
|
/** Result type for agent update command */
|
|
4
6
|
export interface AgentUpdateResult {
|
|
5
7
|
agentId: string;
|
|
6
8
|
name: string | null;
|
|
7
9
|
labels: string;
|
|
10
|
+
thinkingOptionId: string | null;
|
|
11
|
+
noticeType: AgentProviderNotice["type"] | null;
|
|
12
|
+
notice: string | null;
|
|
8
13
|
}
|
|
9
14
|
/** Schema for update command output */
|
|
10
15
|
export declare const updateSchema: OutputSchema<AgentUpdateResult>;
|
|
11
16
|
export interface AgentUpdateOptions extends CommandOptions {
|
|
12
17
|
name?: string;
|
|
13
18
|
label?: string[];
|
|
19
|
+
thinking?: string;
|
|
14
20
|
host?: string;
|
|
15
21
|
}
|
|
16
22
|
export type AgentUpdateCommandResult = SingleResult<AgentUpdateResult>;
|
|
23
|
+
export interface AgentMetadataChanges {
|
|
24
|
+
name?: string;
|
|
25
|
+
labels?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
interface AgentUpdateServerInfo {
|
|
28
|
+
features?: {
|
|
29
|
+
agentThinkingUpdate?: boolean;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface AgentUpdateClient {
|
|
33
|
+
getLastServerInfoMessage(): AgentUpdateServerInfo | null;
|
|
34
|
+
updateAgent(agentId: string, updates: AgentMetadataChanges): Promise<void>;
|
|
35
|
+
setAgentThinkingOption(agentId: string, thinkingOptionId: string): Promise<AgentProviderNotice | null>;
|
|
36
|
+
}
|
|
37
|
+
export type AgentChanges = {
|
|
38
|
+
type: "metadata";
|
|
39
|
+
updates: AgentMetadataChanges;
|
|
40
|
+
} | {
|
|
41
|
+
type: "thinking";
|
|
42
|
+
thinkingOptionId: string;
|
|
43
|
+
};
|
|
44
|
+
export interface AppliedAgentChanges {
|
|
45
|
+
notice: AgentProviderNotice | null;
|
|
46
|
+
}
|
|
47
|
+
export declare function toAgentUpdateResult(agent: Pick<AgentSnapshotPayload, "id" | "title" | "labels" | "effectiveThinkingOptionId">, appliedChanges: AppliedAgentChanges): AgentUpdateResult;
|
|
48
|
+
export declare function applyAgentChanges(client: AgentUpdateClient, agentId: string, changes: AgentChanges): Promise<AppliedAgentChanges>;
|
|
17
49
|
export declare function runUpdateCommand(agentIdArg: string, options: AgentUpdateOptions, _command: Command): Promise<AgentUpdateCommandResult>;
|
|
50
|
+
export {};
|
|
18
51
|
//# sourceMappingURL=update.d.ts.map
|
|
@@ -6,8 +6,35 @@ export const updateSchema = {
|
|
|
6
6
|
{ header: "AGENT ID", field: "agentId" },
|
|
7
7
|
{ header: "NAME", field: "name" },
|
|
8
8
|
{ header: "LABELS", field: "labels" },
|
|
9
|
+
{ header: "THINKING", field: "thinkingOptionId" },
|
|
10
|
+
{ header: "NOTICE", field: "notice" },
|
|
9
11
|
],
|
|
10
12
|
};
|
|
13
|
+
export function toAgentUpdateResult(agent, appliedChanges) {
|
|
14
|
+
return {
|
|
15
|
+
agentId: agent.id,
|
|
16
|
+
name: agent.title,
|
|
17
|
+
labels: formatLabels(agent.labels),
|
|
18
|
+
thinkingOptionId: agent.effectiveThinkingOptionId ?? null,
|
|
19
|
+
noticeType: appliedChanges.notice?.type ?? null,
|
|
20
|
+
notice: appliedChanges.notice?.message ?? null,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export async function applyAgentChanges(client, agentId, changes) {
|
|
24
|
+
if (changes.type === "thinking") {
|
|
25
|
+
// COMPAT(agentThinkingUpdate): added in v0.2.4, remove gate after 2027-01-28.
|
|
26
|
+
if (client.getLastServerInfoMessage()?.features?.agentThinkingUpdate !== true) {
|
|
27
|
+
throw {
|
|
28
|
+
code: "DAEMON_UPDATE_REQUIRED",
|
|
29
|
+
message: "Update the host to use agent thinking updates.",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const notice = await client.setAgentThinkingOption(agentId, changes.thinkingOptionId);
|
|
33
|
+
return { notice };
|
|
34
|
+
}
|
|
35
|
+
await client.updateAgent(agentId, changes.updates);
|
|
36
|
+
return { notice: null };
|
|
37
|
+
}
|
|
11
38
|
function parseLabelOptions(labels) {
|
|
12
39
|
const parsed = {};
|
|
13
40
|
if (!labels) {
|
|
@@ -50,35 +77,62 @@ function formatLabels(labels) {
|
|
|
50
77
|
}
|
|
51
78
|
return entries.map(([key, value]) => `${key}=${value}`).join(",");
|
|
52
79
|
}
|
|
53
|
-
|
|
54
|
-
const host = getDaemonHost({ host: options.host });
|
|
55
|
-
// Validate arguments
|
|
56
|
-
if (!agentIdArg || agentIdArg.trim().length === 0) {
|
|
57
|
-
const error = {
|
|
58
|
-
code: "MISSING_AGENT_ID",
|
|
59
|
-
message: "Agent ID is required",
|
|
60
|
-
details: "Usage: paseo agent update <id> [--name <name>] [--label <key=value>]",
|
|
61
|
-
};
|
|
62
|
-
throw error;
|
|
63
|
-
}
|
|
80
|
+
function parseAgentChanges(options) {
|
|
64
81
|
const name = options.name?.trim();
|
|
65
82
|
if (options.name !== undefined && !name) {
|
|
66
|
-
|
|
83
|
+
throw {
|
|
67
84
|
code: "INVALID_NAME",
|
|
68
85
|
message: "Name cannot be empty",
|
|
69
86
|
details: "Use --name <name> with a non-empty value",
|
|
70
87
|
};
|
|
71
|
-
throw error;
|
|
72
88
|
}
|
|
73
89
|
const labels = parseLabelOptions(options.label);
|
|
74
|
-
|
|
75
|
-
|
|
90
|
+
const thinkingOptionId = options.thinking?.trim();
|
|
91
|
+
if (options.thinking !== undefined && !thinkingOptionId) {
|
|
92
|
+
throw {
|
|
93
|
+
code: "INVALID_THINKING_OPTION",
|
|
94
|
+
message: "--thinking cannot be empty",
|
|
95
|
+
details: 'Provide a thinking option ID. Use "paseo provider models <provider> --thinking" to list valid IDs.',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const hasMetadataUpdates = Boolean(name) || Object.keys(labels).length > 0;
|
|
99
|
+
if (hasMetadataUpdates && thinkingOptionId) {
|
|
100
|
+
throw {
|
|
101
|
+
code: "INVALID_OPTIONS",
|
|
102
|
+
message: "--thinking cannot be combined with --name or --label",
|
|
103
|
+
details: "Run separate agent update commands for runtime settings and metadata.",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (!hasMetadataUpdates && !thinkingOptionId) {
|
|
107
|
+
throw {
|
|
76
108
|
code: "NO_CHANGES_PROVIDED",
|
|
77
109
|
message: "Nothing to update",
|
|
78
|
-
details: "Provide at least one of: --name <name>, --label <key=value>",
|
|
110
|
+
details: "Provide at least one of: --name <name>, --label <key=value>, --thinking <id>",
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (thinkingOptionId) {
|
|
114
|
+
return { type: "thinking", thinkingOptionId };
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
type: "metadata",
|
|
118
|
+
updates: {
|
|
119
|
+
...(name ? { name } : {}),
|
|
120
|
+
...(Object.keys(labels).length > 0 ? { labels } : {}),
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export async function runUpdateCommand(agentIdArg, options, _command) {
|
|
125
|
+
const host = getDaemonHost({ host: options.host });
|
|
126
|
+
// Validate arguments
|
|
127
|
+
if (!agentIdArg || agentIdArg.trim().length === 0) {
|
|
128
|
+
const error = {
|
|
129
|
+
code: "MISSING_AGENT_ID",
|
|
130
|
+
message: "Agent ID is required",
|
|
131
|
+
details: "Usage: paseo agent update <id> [--name <name>] [--label <key=value>]",
|
|
79
132
|
};
|
|
80
133
|
throw error;
|
|
81
134
|
}
|
|
135
|
+
const changes = parseAgentChanges(options);
|
|
82
136
|
let client;
|
|
83
137
|
try {
|
|
84
138
|
client = await connectToDaemon({ host: options.host });
|
|
@@ -103,10 +157,7 @@ export async function runUpdateCommand(agentIdArg, options, _command) {
|
|
|
103
157
|
throw error;
|
|
104
158
|
}
|
|
105
159
|
const agentId = fetchResult.agent.id;
|
|
106
|
-
await client
|
|
107
|
-
...(name ? { name } : {}),
|
|
108
|
-
...(Object.keys(labels).length > 0 ? { labels } : {}),
|
|
109
|
-
});
|
|
160
|
+
const appliedChanges = await applyAgentChanges(client, agentId, changes);
|
|
110
161
|
const updatedResult = await client.fetchAgent({ agentId });
|
|
111
162
|
if (!updatedResult) {
|
|
112
163
|
throw new Error(`Agent not found after update: ${agentId}`);
|
|
@@ -114,11 +165,7 @@ export async function runUpdateCommand(agentIdArg, options, _command) {
|
|
|
114
165
|
await client.close();
|
|
115
166
|
return {
|
|
116
167
|
type: "single",
|
|
117
|
-
data:
|
|
118
|
-
agentId,
|
|
119
|
-
name: updatedResult.agent.title,
|
|
120
|
-
labels: formatLabels(updatedResult.agent.labels),
|
|
121
|
-
},
|
|
168
|
+
data: toAgentUpdateResult(updatedResult.agent, appliedChanges),
|
|
122
169
|
schema: updateSchema,
|
|
123
170
|
};
|
|
124
171
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const authorizationSchema: z.ZodObject<{
|
|
3
|
+
deviceCode: z.ZodString;
|
|
4
|
+
userCode: z.ZodString;
|
|
5
|
+
verificationUri: z.ZodURL;
|
|
6
|
+
verificationUriComplete: z.ZodURL;
|
|
7
|
+
expiresAt: z.ZodString;
|
|
8
|
+
interval: z.ZodNumber;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
declare const pollSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
11
|
+
status: z.ZodLiteral<"pending">;
|
|
12
|
+
interval: z.ZodNumber;
|
|
13
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
14
|
+
status: z.ZodLiteral<"slow_down">;
|
|
15
|
+
interval: z.ZodNumber;
|
|
16
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
17
|
+
status: z.ZodLiteral<"approved">;
|
|
18
|
+
interval: z.ZodNumber;
|
|
19
|
+
enrollmentToken: z.ZodString;
|
|
20
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
21
|
+
status: z.ZodLiteral<"denied">;
|
|
22
|
+
interval: z.ZodNumber;
|
|
23
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
24
|
+
status: z.ZodLiteral<"expired">;
|
|
25
|
+
interval: z.ZodNumber;
|
|
26
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
27
|
+
status: z.ZodLiteral<"enrolled">;
|
|
28
|
+
interval: z.ZodNumber;
|
|
29
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
30
|
+
status: z.ZodLiteral<"retry_later">;
|
|
31
|
+
}, z.core.$strip>], "status">;
|
|
32
|
+
export type DeviceAuthorization = z.infer<typeof authorizationSchema>;
|
|
33
|
+
export type DeviceAuthorizationPoll = z.infer<typeof pollSchema>;
|
|
34
|
+
export interface CloudDeviceAuthorization {
|
|
35
|
+
start(hubUrl: string, displayName: string): Promise<DeviceAuthorization>;
|
|
36
|
+
poll(hubUrl: string, deviceCode: string, timeoutMilliseconds: number): Promise<DeviceAuthorizationPoll>;
|
|
37
|
+
}
|
|
38
|
+
export declare class CloudDeviceAuthorizationClient implements CloudDeviceAuthorization {
|
|
39
|
+
private readonly startTimeoutMilliseconds;
|
|
40
|
+
constructor(startTimeoutMilliseconds?: number);
|
|
41
|
+
start(hubUrl: string, displayName: string): Promise<DeviceAuthorization>;
|
|
42
|
+
poll(hubUrl: string, deviceCode: string, timeoutMilliseconds: number): Promise<DeviceAuthorizationPoll>;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=cloud-device-authorization.d.ts.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const START_TIMEOUT_MS = 15000;
|
|
3
|
+
const activationUrlSchema = z.url({ protocol: /^https?$/u });
|
|
4
|
+
const authorizationSchema = z.object({
|
|
5
|
+
deviceCode: z.string().min(32),
|
|
6
|
+
userCode: z.string().min(1),
|
|
7
|
+
verificationUri: activationUrlSchema,
|
|
8
|
+
verificationUriComplete: activationUrlSchema,
|
|
9
|
+
expiresAt: z.string().datetime(),
|
|
10
|
+
interval: z.number().int().min(5),
|
|
11
|
+
});
|
|
12
|
+
const pollSchema = z.discriminatedUnion("status", [
|
|
13
|
+
z.object({ status: z.literal("pending"), interval: z.number().int().min(5) }),
|
|
14
|
+
z.object({ status: z.literal("slow_down"), interval: z.number().int().min(5) }),
|
|
15
|
+
z.object({
|
|
16
|
+
status: z.literal("approved"),
|
|
17
|
+
interval: z.number().int().min(5),
|
|
18
|
+
enrollmentToken: z.string().min(32),
|
|
19
|
+
}),
|
|
20
|
+
z.object({ status: z.literal("denied"), interval: z.number().int().min(5) }),
|
|
21
|
+
z.object({ status: z.literal("expired"), interval: z.number().int().min(5) }),
|
|
22
|
+
z.object({ status: z.literal("enrolled"), interval: z.number().int().min(5) }),
|
|
23
|
+
z.object({ status: z.literal("retry_later") }),
|
|
24
|
+
]);
|
|
25
|
+
export class CloudDeviceAuthorizationClient {
|
|
26
|
+
constructor(startTimeoutMilliseconds = START_TIMEOUT_MS) {
|
|
27
|
+
this.startTimeoutMilliseconds = startTimeoutMilliseconds;
|
|
28
|
+
}
|
|
29
|
+
async start(hubUrl, displayName) {
|
|
30
|
+
const signal = AbortSignal.timeout(this.startTimeoutMilliseconds);
|
|
31
|
+
try {
|
|
32
|
+
const response = await fetch(endpoint(hubUrl, "/api/device-authorizations/"), {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "content-type": "application/json" },
|
|
35
|
+
body: JSON.stringify({ displayName }),
|
|
36
|
+
signal,
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok)
|
|
39
|
+
throw new Error(`Cloud registration failed (${response.status})`);
|
|
40
|
+
return authorizationSchema.parse(await response.json());
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (signal.aborted) {
|
|
44
|
+
throw new Error("Cloud registration start timed out", { cause: error });
|
|
45
|
+
}
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async poll(hubUrl, deviceCode, timeoutMilliseconds) {
|
|
50
|
+
const signal = AbortSignal.timeout(timeoutMilliseconds);
|
|
51
|
+
let response;
|
|
52
|
+
try {
|
|
53
|
+
response = await fetch(endpoint(hubUrl, "/api/device-authorizations/poll"), {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "content-type": "application/json" },
|
|
56
|
+
body: JSON.stringify({ deviceCode }),
|
|
57
|
+
signal,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return { status: "retry_later" };
|
|
62
|
+
}
|
|
63
|
+
if ([408, 425, 429].includes(response.status) || response.status >= 500) {
|
|
64
|
+
return { status: "retry_later" };
|
|
65
|
+
}
|
|
66
|
+
if (!response.ok)
|
|
67
|
+
throw new Error(`Cloud registration poll failed (${response.status})`);
|
|
68
|
+
let body;
|
|
69
|
+
try {
|
|
70
|
+
body = await response.json();
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (signal.aborted || error instanceof TypeError)
|
|
74
|
+
return { status: "retry_later" };
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
return pollSchema.parse(body);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function endpoint(hubUrl, pathname) {
|
|
81
|
+
const url = new URL(hubUrl);
|
|
82
|
+
if (!["http:", "https:"].includes(url.protocol) ||
|
|
83
|
+
url.username ||
|
|
84
|
+
url.password ||
|
|
85
|
+
url.search ||
|
|
86
|
+
url.hash) {
|
|
87
|
+
throw new Error("Hub URL must be an HTTP or HTTPS origin without credentials or a query");
|
|
88
|
+
}
|
|
89
|
+
url.pathname = `${url.pathname.replace(/\/$/u, "")}${pathname}`;
|
|
90
|
+
return url.toString();
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=cloud-device-authorization.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type CloudDeviceAuthorization } from "./cloud-device-authorization.js";
|
|
2
|
+
export interface AuthorizationWaiter {
|
|
3
|
+
wait(milliseconds: number): Promise<void>;
|
|
4
|
+
now(): number;
|
|
5
|
+
}
|
|
6
|
+
export interface BrowserOpener {
|
|
7
|
+
open(url: string): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
type BrowserLaunch = (command: string, args: string[]) => Promise<void>;
|
|
10
|
+
interface SystemBrowserOptions {
|
|
11
|
+
hostPlatform?: NodeJS.Platform;
|
|
12
|
+
launch?: BrowserLaunch;
|
|
13
|
+
}
|
|
14
|
+
export declare class SystemBrowser implements BrowserOpener {
|
|
15
|
+
private readonly hostPlatform;
|
|
16
|
+
private readonly launch;
|
|
17
|
+
constructor(options?: SystemBrowserOptions);
|
|
18
|
+
open(url: string): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export interface AuthorizationReporter {
|
|
21
|
+
instructions(verificationUri: string, userCode: string): void;
|
|
22
|
+
}
|
|
23
|
+
interface DeviceAuthorizationWorkflowOptions {
|
|
24
|
+
cloud: CloudDeviceAuthorization;
|
|
25
|
+
waiter: AuthorizationWaiter;
|
|
26
|
+
browser: BrowserOpener;
|
|
27
|
+
reporter: AuthorizationReporter;
|
|
28
|
+
openBrowser?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare class DeviceAuthorizationWorkflow {
|
|
31
|
+
private readonly options;
|
|
32
|
+
constructor(options: DeviceAuthorizationWorkflowOptions);
|
|
33
|
+
authorize(hubUrl: string, displayName: string): Promise<string>;
|
|
34
|
+
}
|
|
35
|
+
export declare function createDeviceAuthorizationWorkflow(): DeviceAuthorizationWorkflow;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=device-authorization.d.ts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { platform } from "node:os";
|
|
3
|
+
import { CloudDeviceAuthorizationClient, } from "./cloud-device-authorization.js";
|
|
4
|
+
export class SystemBrowser {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.hostPlatform = options.hostPlatform ?? platform();
|
|
7
|
+
this.launch = options.launch ?? launchDetached;
|
|
8
|
+
}
|
|
9
|
+
async open(url) {
|
|
10
|
+
if (this.hostPlatform === "win32") {
|
|
11
|
+
await this.launch("rundll32.exe", ["url.dll,FileProtocolHandler", url]);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
await this.launch(this.hostPlatform === "darwin" ? "open" : "xdg-open", [url]);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class DeviceAuthorizationWorkflow {
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = options;
|
|
20
|
+
}
|
|
21
|
+
async authorize(hubUrl, displayName) {
|
|
22
|
+
const authorization = await this.options.cloud.start(hubUrl, displayName);
|
|
23
|
+
this.options.reporter.instructions(authorization.verificationUri, authorization.userCode);
|
|
24
|
+
if (this.options.openBrowser !== false) {
|
|
25
|
+
await this.options.browser.open(authorization.verificationUriComplete).catch(() => undefined);
|
|
26
|
+
}
|
|
27
|
+
let interval = authorization.interval;
|
|
28
|
+
const expiresAt = Date.parse(authorization.expiresAt);
|
|
29
|
+
while (true) {
|
|
30
|
+
const remaining = expiresAt - this.options.waiter.now();
|
|
31
|
+
if (remaining <= 0)
|
|
32
|
+
throw new Error("Daemon registration expired");
|
|
33
|
+
await this.options.waiter.wait(Math.min(interval * 1000, remaining));
|
|
34
|
+
if (this.options.waiter.now() >= expiresAt)
|
|
35
|
+
throw new Error("Daemon registration expired");
|
|
36
|
+
const pollLifetime = expiresAt - this.options.waiter.now();
|
|
37
|
+
if (pollLifetime <= 0)
|
|
38
|
+
throw new Error("Daemon registration expired");
|
|
39
|
+
const outcome = await this.options.cloud.poll(hubUrl, authorization.deviceCode, pollLifetime);
|
|
40
|
+
if (this.options.waiter.now() >= expiresAt)
|
|
41
|
+
throw new Error("Daemon registration expired");
|
|
42
|
+
if (outcome.status === "retry_later")
|
|
43
|
+
continue;
|
|
44
|
+
interval = outcome.interval;
|
|
45
|
+
if (outcome.status === "approved")
|
|
46
|
+
return outcome.enrollmentToken;
|
|
47
|
+
if (outcome.status === "denied")
|
|
48
|
+
throw new Error("Daemon registration was denied");
|
|
49
|
+
if (outcome.status === "expired")
|
|
50
|
+
throw new Error("Daemon registration expired");
|
|
51
|
+
if (outcome.status === "enrolled") {
|
|
52
|
+
throw new Error("Daemon registration was already used");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function createDeviceAuthorizationWorkflow() {
|
|
58
|
+
return new DeviceAuthorizationWorkflow({
|
|
59
|
+
cloud: new CloudDeviceAuthorizationClient(),
|
|
60
|
+
waiter: {
|
|
61
|
+
wait: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
|
|
62
|
+
now: Date.now,
|
|
63
|
+
},
|
|
64
|
+
browser: new SystemBrowser(),
|
|
65
|
+
reporter: {
|
|
66
|
+
instructions(verificationUri, userCode) {
|
|
67
|
+
process.stderr.write(`Open ${verificationUri} and enter code ${userCode}\n`);
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
openBrowser: process.stderr.isTTY === true,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async function launchDetached(command, args) {
|
|
74
|
+
await new Promise((resolve, reject) => {
|
|
75
|
+
const child = spawn(command, args, {
|
|
76
|
+
detached: true,
|
|
77
|
+
shell: false,
|
|
78
|
+
stdio: "ignore",
|
|
79
|
+
});
|
|
80
|
+
child.once("spawn", () => {
|
|
81
|
+
child.unref();
|
|
82
|
+
resolve();
|
|
83
|
+
});
|
|
84
|
+
child.once("error", reject);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=device-authorization.js.map
|
|
@@ -1,3 +1,30 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
|
|
2
|
+
interface HubCommandClient {
|
|
3
|
+
connectHub(url: string, token: string): Promise<{
|
|
4
|
+
status: HubStatus;
|
|
5
|
+
}>;
|
|
6
|
+
getHubStatus(): Promise<{
|
|
7
|
+
status: HubStatus;
|
|
8
|
+
}>;
|
|
9
|
+
disconnectHub(force: boolean): Promise<{
|
|
10
|
+
status: HubStatus;
|
|
11
|
+
warning?: string;
|
|
12
|
+
}>;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
interface HubStatus {
|
|
16
|
+
state: string;
|
|
17
|
+
daemonId: string | null;
|
|
18
|
+
hubOrigin: string | null;
|
|
19
|
+
scopes: string[];
|
|
20
|
+
connectedAt: string | null;
|
|
21
|
+
lastError: string | null;
|
|
22
|
+
}
|
|
23
|
+
interface HubCommandEnvironment {
|
|
24
|
+
connect(host: string | undefined): Promise<HubCommandClient>;
|
|
25
|
+
authorize(url: string, displayName: string): Promise<string>;
|
|
26
|
+
displayName(): string;
|
|
27
|
+
}
|
|
28
|
+
export declare function createHubCommand(environment?: HubCommandEnvironment): Command;
|
|
29
|
+
export {};
|
|
3
30
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
import { hostname } from "node:os";
|
|
2
3
|
import { withOutput } from "../../output/index.js";
|
|
3
4
|
import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
|
|
4
5
|
import { connectToDaemon } from "../../utils/client.js";
|
|
6
|
+
import { createDeviceAuthorizationWorkflow } from "./device-authorization.js";
|
|
7
|
+
const productionEnvironment = {
|
|
8
|
+
connect: (host) => connectToDaemon({ host }),
|
|
9
|
+
authorize: (url, displayName) => createDeviceAuthorizationWorkflow().authorize(url, displayName),
|
|
10
|
+
displayName: hostname,
|
|
11
|
+
};
|
|
5
12
|
const schema = {
|
|
6
13
|
idField: "state",
|
|
7
14
|
columns: [
|
|
@@ -31,8 +38,8 @@ function result(status, warning) {
|
|
|
31
38
|
schema,
|
|
32
39
|
};
|
|
33
40
|
}
|
|
34
|
-
async function withClient(host, action) {
|
|
35
|
-
const client = await
|
|
41
|
+
async function withClient(environment, host, action) {
|
|
42
|
+
const client = await environment.connect(host);
|
|
36
43
|
try {
|
|
37
44
|
return await action(client);
|
|
38
45
|
}
|
|
@@ -40,26 +47,39 @@ async function withClient(host, action) {
|
|
|
40
47
|
await client.close().catch(() => undefined);
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
|
-
export function createHubCommand() {
|
|
50
|
+
export function createHubCommand(environment = productionEnvironment) {
|
|
44
51
|
const hub = new Command("hub").description("Manage this daemon's Paseo Hub relationship");
|
|
45
|
-
addJsonAndDaemonHostOptions(hub.command("connect").argument("<url>").
|
|
52
|
+
addJsonAndDaemonHostOptions(hub.command("connect").argument("<url>").option("--token <token>")).action(withOutput(async (...args) => {
|
|
46
53
|
const url = args[0];
|
|
47
54
|
const options = args.at(-2);
|
|
48
|
-
return withClient(options.host, async (client) =>
|
|
55
|
+
return withClient(environment, options.host, async (client) => {
|
|
56
|
+
if (options.token !== undefined) {
|
|
57
|
+
return result((await client.connectHub(url, options.token)).status);
|
|
58
|
+
}
|
|
59
|
+
const existing = (await client.getHubStatus()).status;
|
|
60
|
+
if (existing.state !== "not_connected" && existing.state !== "revoked") {
|
|
61
|
+
throw new Error("This daemon already has a Hub relationship");
|
|
62
|
+
}
|
|
63
|
+
const token = await environment.authorize(url, suggestedDisplayName(environment.displayName()));
|
|
64
|
+
return result((await client.connectHub(url, token)).status);
|
|
65
|
+
});
|
|
49
66
|
}));
|
|
50
67
|
addJsonAndDaemonHostOptions(hub.command("status")).action(withOutput(async (...args) => {
|
|
51
68
|
const options = args.at(-2);
|
|
52
|
-
return withClient(options.host, async (client) => result((await client.getHubStatus()).status));
|
|
69
|
+
return withClient(environment, options.host, async (client) => result((await client.getHubStatus()).status));
|
|
53
70
|
}));
|
|
54
71
|
addJsonAndDaemonHostOptions(hub
|
|
55
72
|
.command("disconnect")
|
|
56
73
|
.option("--force", "Remove local authority even if the Hub is offline")).action(withOutput(async (...args) => {
|
|
57
74
|
const options = args.at(-2);
|
|
58
|
-
return withClient(options.host, async (client) => {
|
|
75
|
+
return withClient(environment, options.host, async (client) => {
|
|
59
76
|
const response = await client.disconnectHub(options.force ?? false);
|
|
60
77
|
return result(response.status, response.warning);
|
|
61
78
|
});
|
|
62
79
|
}));
|
|
63
80
|
return hub;
|
|
64
81
|
}
|
|
82
|
+
function suggestedDisplayName(value) {
|
|
83
|
+
return value.trim().slice(0, 100) || "Paseo daemon";
|
|
84
|
+
}
|
|
65
85
|
//# sourceMappingURL=index.js.map
|
|
@@ -23,6 +23,7 @@ export function createScheduleCommand() {
|
|
|
23
23
|
.addOption(new Option("--target <target>", "Legacy schedule target").hideHelp())
|
|
24
24
|
.option("--provider <provider>", "Agent provider, or provider/model (e.g. codex or codex/gpt-5.4)")
|
|
25
25
|
.option("--mode <mode>", "Provider-specific mode (e.g. claude bypassPermissions, opencode build)")
|
|
26
|
+
.option("--thinking <id>", "Thinking option ID for new-agent runs")
|
|
26
27
|
.option("--cwd <path>", "Working directory (default: current; required with --host)")
|
|
27
28
|
.option("--run-now", "Fire one immediate run on creation")
|
|
28
29
|
.option("--max-runs <n>", "Maximum number of runs")
|
|
@@ -82,7 +82,7 @@ function resolveScheduleTarget(args) {
|
|
|
82
82
|
if (hasExplicitNewAgentOption) {
|
|
83
83
|
throw {
|
|
84
84
|
code: "INVALID_TARGET",
|
|
85
|
-
message: "--provider/--mode can only be used with a new-agent target",
|
|
85
|
+
message: "--provider/--mode/--thinking can only be used with a new-agent target",
|
|
86
86
|
details: "Use --target new-agent or omit --target to create a new agent schedule",
|
|
87
87
|
};
|
|
88
88
|
}
|
|
@@ -125,7 +125,14 @@ export function parseScheduleCreateInput(options) {
|
|
|
125
125
|
const runOnCreate = resolveRunOnCreate(options.runNow, cadence.type);
|
|
126
126
|
const targetValue = options.target?.trim();
|
|
127
127
|
const modeId = options.mode?.trim();
|
|
128
|
-
const
|
|
128
|
+
const thinkingOptionId = options.thinking?.trim();
|
|
129
|
+
if (options.thinking !== undefined && !thinkingOptionId) {
|
|
130
|
+
throw {
|
|
131
|
+
code: "INVALID_THINKING_OPTION",
|
|
132
|
+
message: "--thinking cannot be empty",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const hasExplicitNewAgentOption = options.provider !== undefined || options.mode !== undefined || options.thinking !== undefined;
|
|
129
136
|
const createNewAgentTarget = () => {
|
|
130
137
|
const resolvedProviderModel = resolveProviderAndModel({
|
|
131
138
|
provider: options.provider,
|
|
@@ -137,6 +144,7 @@ export function parseScheduleCreateInput(options) {
|
|
|
137
144
|
cwd: cwdInput ?? process.cwd(),
|
|
138
145
|
...(resolvedProviderModel.model ? { model: resolvedProviderModel.model } : {}),
|
|
139
146
|
...(modeId ? { modeId } : {}),
|
|
147
|
+
...(thinkingOptionId ? { thinkingOptionId } : {}),
|
|
140
148
|
},
|
|
141
149
|
};
|
|
142
150
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { withOutput } from "../../output/index.js";
|
|
3
|
+
import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
|
|
4
|
+
import { runLsCommand } from "./ls.js";
|
|
5
|
+
import { runStartCommand } from "./start.js";
|
|
6
|
+
import { runStopCommand } from "./stop.js";
|
|
7
|
+
function addWorkspaceSelectionOptions(command) {
|
|
8
|
+
return command
|
|
9
|
+
.option("--cwd <path>", "Workspace directory (default: current directory)")
|
|
10
|
+
.option("--workspace <workspace-id>", "Workspace ID (required when a directory has multiple workspaces)");
|
|
11
|
+
}
|
|
12
|
+
export function createScriptCommand() {
|
|
13
|
+
const script = new Command("script").description("Manage configured workspace scripts");
|
|
14
|
+
addJsonAndDaemonHostOptions(addWorkspaceSelectionOptions(script.command("ls").description("List configured workspace scripts"))).action(withOutput(runLsCommand));
|
|
15
|
+
addJsonAndDaemonHostOptions(addWorkspaceSelectionOptions(script.command("start").description("Start a configured workspace script").argument("<name>"))).action(withOutput(runStartCommand));
|
|
16
|
+
addJsonAndDaemonHostOptions(addWorkspaceSelectionOptions(script.command("stop").description("Stop a running workspace script").argument("<name>"))).action(withOutput(runStopCommand));
|
|
17
|
+
return script;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { ListResult } from "../../output/index.js";
|
|
3
|
+
import { type WorkspaceScriptCommandOptions } from "./shared.js";
|
|
4
|
+
import { type WorkspaceScriptRow } from "./schema.js";
|
|
5
|
+
export declare function runLsCommand(options: WorkspaceScriptCommandOptions, _command: Command): Promise<ListResult<WorkspaceScriptRow>>;
|
|
6
|
+
//# sourceMappingURL=ls.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { connectWorkspaceScriptClient, resolveWorkspaceScriptWorkspaceId, toWorkspaceScriptCommandError, } from "./shared.js";
|
|
2
|
+
import { workspaceScriptSchema } from "./schema.js";
|
|
3
|
+
export async function runLsCommand(options, _command) {
|
|
4
|
+
const client = await connectWorkspaceScriptClient(options.host);
|
|
5
|
+
try {
|
|
6
|
+
const workspaceId = await resolveWorkspaceScriptWorkspaceId(client, options);
|
|
7
|
+
const payload = await client.listWorkspaceScripts(workspaceId);
|
|
8
|
+
if (payload.error) {
|
|
9
|
+
throw new Error(payload.error);
|
|
10
|
+
}
|
|
11
|
+
return { type: "list", data: payload.scripts ?? [], schema: workspaceScriptSchema };
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
throw toWorkspaceScriptCommandError("WORKSPACE_SCRIPT_LIST_FAILED", "list workspace scripts", error);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
await client.close().catch(() => { });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=ls.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WorkspaceScriptPayload } from "@getpaseo/protocol/messages";
|
|
2
|
+
import type { OutputSchema } from "../../output/index.js";
|
|
3
|
+
export type WorkspaceScriptRow = WorkspaceScriptPayload;
|
|
4
|
+
export declare const workspaceScriptSchema: OutputSchema<WorkspaceScriptRow>;
|
|
5
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const workspaceScriptSchema = {
|
|
2
|
+
idField: "scriptName",
|
|
3
|
+
columns: [
|
|
4
|
+
{ header: "NAME", field: "scriptName", width: 20 },
|
|
5
|
+
{ header: "TYPE", field: "type", width: 9 },
|
|
6
|
+
{ header: "LIFECYCLE", field: "lifecycle", width: 10 },
|
|
7
|
+
{ header: "HEALTH", field: (script) => script.health ?? "-", width: 10 },
|
|
8
|
+
{ header: "PORT", field: (script) => script.port ?? "-", width: 7 },
|
|
9
|
+
{ header: "PROXY URL", field: (script) => script.proxyUrl ?? "-", width: 42 },
|
|
10
|
+
{ header: "TERMINAL", field: (script) => script.terminalId ?? "-", width: 12 },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
|
2
|
+
import type { CommandError, CommandOptions } from "../../output/index.js";
|
|
3
|
+
export interface WorkspaceScriptCommandOptions extends CommandOptions {
|
|
4
|
+
host?: string;
|
|
5
|
+
cwd?: string;
|
|
6
|
+
workspace?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function connectWorkspaceScriptClient(host?: string): Promise<DaemonClient>;
|
|
9
|
+
export declare function resolveWorkspaceScriptWorkspaceId(client: DaemonClient, options: WorkspaceScriptCommandOptions): Promise<string>;
|
|
10
|
+
export declare function toWorkspaceScriptCommandError(code: string, action: string, error: unknown): CommandError;
|
|
11
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
|
|
3
|
+
export async function connectWorkspaceScriptClient(host) {
|
|
4
|
+
const daemonHost = getDaemonHost({ host });
|
|
5
|
+
try {
|
|
6
|
+
const client = await connectToDaemon({ host });
|
|
7
|
+
// COMPAT(workspaceScriptManagement): added in v0.1.105, remove gate after 2027-01-10.
|
|
8
|
+
if (!client.getLastServerInfoMessage()?.features?.workspaceScriptManagement) {
|
|
9
|
+
await client.close().catch(() => { });
|
|
10
|
+
throw {
|
|
11
|
+
code: "DAEMON_UPDATE_REQUIRED",
|
|
12
|
+
message: "Update the host to use workspace script management.",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return client;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error && typeof error === "object" && "code" in error && "message" in error) {
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
22
|
+
throw {
|
|
23
|
+
code: "DAEMON_NOT_RUNNING",
|
|
24
|
+
message: `Cannot connect to daemon at ${daemonHost}: ${message}`,
|
|
25
|
+
details: "Start the daemon with: paseo daemon start",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function resolveWorkspaceScriptWorkspaceId(client, options) {
|
|
30
|
+
if (options.workspace) {
|
|
31
|
+
return options.workspace;
|
|
32
|
+
}
|
|
33
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
34
|
+
const payload = await client.fetchWorkspaces({ page: { limit: 200 } });
|
|
35
|
+
const matches = payload.entries.filter((workspace) => resolve(workspace.workspaceDirectory) === cwd);
|
|
36
|
+
if (matches.length === 1) {
|
|
37
|
+
return matches[0].id;
|
|
38
|
+
}
|
|
39
|
+
if (matches.length > 1) {
|
|
40
|
+
throw {
|
|
41
|
+
code: "WORKSPACE_AMBIGUOUS",
|
|
42
|
+
message: `Multiple workspaces use ${cwd}`,
|
|
43
|
+
details: "Pass --workspace <workspace-id> to select one.",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
throw {
|
|
47
|
+
code: "WORKSPACE_NOT_FOUND",
|
|
48
|
+
message: `No Paseo workspace found for ${cwd}`,
|
|
49
|
+
details: "Open the directory in Paseo first, or pass --workspace <workspace-id>.",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function toWorkspaceScriptCommandError(code, action, error) {
|
|
53
|
+
if (error && typeof error === "object" && "code" in error && "message" in error) {
|
|
54
|
+
return error;
|
|
55
|
+
}
|
|
56
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
57
|
+
return { code, message: `Failed to ${action}: ${message}` };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { SingleResult } from "../../output/index.js";
|
|
3
|
+
import { type WorkspaceScriptCommandOptions } from "./shared.js";
|
|
4
|
+
import { type WorkspaceScriptRow } from "./schema.js";
|
|
5
|
+
export declare function runStartCommand(scriptName: string, options: WorkspaceScriptCommandOptions, _command: Command): Promise<SingleResult<WorkspaceScriptRow>>;
|
|
6
|
+
//# sourceMappingURL=start.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { connectWorkspaceScriptClient, resolveWorkspaceScriptWorkspaceId, toWorkspaceScriptCommandError, } from "./shared.js";
|
|
2
|
+
import { workspaceScriptSchema } from "./schema.js";
|
|
3
|
+
export async function runStartCommand(scriptName, options, _command) {
|
|
4
|
+
const client = await connectWorkspaceScriptClient(options.host);
|
|
5
|
+
try {
|
|
6
|
+
const workspaceId = await resolveWorkspaceScriptWorkspaceId(client, options);
|
|
7
|
+
const payload = await client.startWorkspaceScriptWithStatus(workspaceId, scriptName);
|
|
8
|
+
if (payload.error || !payload.script) {
|
|
9
|
+
throw {
|
|
10
|
+
code: "WORKSPACE_SCRIPT_START_FAILED",
|
|
11
|
+
message: payload.error ?? `Script '${scriptName}' did not return status metadata`,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return { type: "single", data: payload.script, schema: workspaceScriptSchema };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
throw toWorkspaceScriptCommandError("WORKSPACE_SCRIPT_START_FAILED", "start workspace script", error);
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
await client.close().catch(() => { });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=start.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { SingleResult } from "../../output/index.js";
|
|
3
|
+
import { type WorkspaceScriptCommandOptions } from "./shared.js";
|
|
4
|
+
import { type WorkspaceScriptRow } from "./schema.js";
|
|
5
|
+
export declare function runStopCommand(scriptName: string, options: WorkspaceScriptCommandOptions, _command: Command): Promise<SingleResult<WorkspaceScriptRow>>;
|
|
6
|
+
//# sourceMappingURL=stop.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { connectWorkspaceScriptClient, resolveWorkspaceScriptWorkspaceId, toWorkspaceScriptCommandError, } from "./shared.js";
|
|
2
|
+
import { workspaceScriptSchema } from "./schema.js";
|
|
3
|
+
export async function runStopCommand(scriptName, options, _command) {
|
|
4
|
+
const client = await connectWorkspaceScriptClient(options.host);
|
|
5
|
+
try {
|
|
6
|
+
const workspaceId = await resolveWorkspaceScriptWorkspaceId(client, options);
|
|
7
|
+
const payload = await client.stopWorkspaceScript(workspaceId, scriptName);
|
|
8
|
+
if (payload.error || !payload.script) {
|
|
9
|
+
throw {
|
|
10
|
+
code: "WORKSPACE_SCRIPT_STOP_FAILED",
|
|
11
|
+
message: payload.error ?? `Script '${scriptName}' did not return status metadata`,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return { type: "single", data: payload.script, schema: workspaceScriptSchema };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
throw toWorkspaceScriptCommandError("WORKSPACE_SCRIPT_STOP_FAILED", "stop workspace script", error);
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
await client.close().catch(() => { });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=stop.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getpaseo/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Paseo CLI - control your AI coding agents from the command line",
|
|
5
5
|
"bin": {
|
|
6
6
|
"paseo": "bin/paseo"
|
|
@@ -20,22 +20,24 @@
|
|
|
20
20
|
"build:clean": "npm run clean && npm run build",
|
|
21
21
|
"prepack": "npm run build:clean",
|
|
22
22
|
"typecheck": "tsgo --noEmit",
|
|
23
|
-
"test": "npm run test:local",
|
|
23
|
+
"test": "npm run test:unit && npm run test:local",
|
|
24
|
+
"test:unit": "vitest run src",
|
|
24
25
|
"test:local": "tsx tests/run-all.ts",
|
|
25
26
|
"test:e2e": "npm run test:local",
|
|
26
27
|
"test:e2e:lifecycle": "npx tsx tests/e2e/agent-lifecycle.test.ts"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"@clack/prompts": "^1.0.0",
|
|
30
|
-
"@getpaseo/client": "0.2.
|
|
31
|
-
"@getpaseo/protocol": "0.2.
|
|
32
|
-
"@getpaseo/server": "0.2.
|
|
31
|
+
"@getpaseo/client": "0.2.4",
|
|
32
|
+
"@getpaseo/protocol": "0.2.4",
|
|
33
|
+
"@getpaseo/server": "0.2.4",
|
|
33
34
|
"chalk": "^5.3.0",
|
|
34
35
|
"commander": "^12.0.0",
|
|
35
36
|
"mime-types": "^2.1.35",
|
|
36
37
|
"tree-kill": "^1.2.2",
|
|
37
38
|
"ws": "^8.14.2",
|
|
38
|
-
"yaml": "^2.8.4"
|
|
39
|
+
"yaml": "^2.8.4",
|
|
40
|
+
"zod": "^4.4.3"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@types/mime-types": "^3.0.1",
|