@getpaseo/cli 0.2.0-beta.1 → 0.2.0-beta.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.
Files changed (37) hide show
  1. package/dist/cli.js +10 -2
  2. package/dist/commands/agent/detach.d.ts +9 -0
  3. package/dist/commands/agent/detach.js +38 -0
  4. package/dist/commands/agent/index.js +7 -0
  5. package/dist/commands/agent/open.d.ts +11 -0
  6. package/dist/commands/agent/open.js +61 -0
  7. package/dist/commands/agent/run.d.ts +34 -0
  8. package/dist/commands/agent/run.js +133 -45
  9. package/dist/commands/heartbeat/index.d.ts +3 -0
  10. package/dist/commands/heartbeat/index.js +139 -0
  11. package/dist/commands/hub/index.d.ts +3 -0
  12. package/dist/commands/hub/index.js +65 -0
  13. package/dist/commands/open.d.ts +2 -0
  14. package/dist/commands/open.js +22 -17
  15. package/dist/commands/schedule/index.js +5 -6
  16. package/dist/commands/schedule/inspect.js +3 -0
  17. package/dist/commands/schedule/logs.js +2 -1
  18. package/dist/commands/schedule/ls.js +3 -1
  19. package/dist/commands/schedule/pause.js +2 -1
  20. package/dist/commands/schedule/resume.js +2 -1
  21. package/dist/commands/schedule/run-once.js +2 -1
  22. package/dist/commands/schedule/shared.d.ts +2 -0
  23. package/dist/commands/schedule/shared.js +25 -21
  24. package/dist/commands/schedule/update.js +2 -1
  25. package/dist/commands/workspace/archive.d.ts +12 -0
  26. package/dist/commands/workspace/archive.js +41 -0
  27. package/dist/commands/workspace/create.d.ts +49 -0
  28. package/dist/commands/workspace/create.js +114 -0
  29. package/dist/commands/workspace/index.d.ts +3 -0
  30. package/dist/commands/workspace/index.js +30 -0
  31. package/dist/commands/workspace/ls.d.ts +7 -0
  32. package/dist/commands/workspace/ls.js +28 -0
  33. package/dist/commands/workspace/shared.d.ts +12 -0
  34. package/dist/commands/workspace/shared.js +20 -0
  35. package/dist/utils/duration.d.ts +1 -1
  36. package/dist/utils/duration.js +8 -7
  37. package/package.json +4 -4
@@ -0,0 +1,139 @@
1
+ import { Command } from "commander";
2
+ import { withOutput } from "../../output/index.js";
3
+ import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
4
+ import { parseDuration } from "../../utils/duration.js";
5
+ import { connectScheduleClient, toScheduleCommandError, toScheduleRow, } from "../schedule/shared.js";
6
+ import { scheduleSchema } from "../schedule/schema.js";
7
+ const heartbeatDeleteSchema = {
8
+ idField: "id",
9
+ columns: [
10
+ { header: "ID", field: "id" },
11
+ { header: "STATUS", field: "status" },
12
+ ],
13
+ };
14
+ function requireCallerAgentId() {
15
+ const agentId = process.env.PASEO_AGENT_ID?.trim();
16
+ if (!agentId) {
17
+ throw new Error("Heartbeat commands must run inside a Paseo agent");
18
+ }
19
+ return agentId;
20
+ }
21
+ async function requireOwnedHeartbeat(client, id, agentId) {
22
+ const payload = await client.scheduleInspect({ id });
23
+ if (payload.error || !payload.schedule) {
24
+ throw new Error(payload.error ?? `Heartbeat not found: ${id}`);
25
+ }
26
+ if (payload.schedule.target.type !== "agent" || payload.schedule.target.agentId !== agentId) {
27
+ throw new Error(`Heartbeat ${id} does not belong to agent ${agentId}`);
28
+ }
29
+ }
30
+ async function runCreateHeartbeat(prompt, options, _command) {
31
+ const agentId = requireCallerAgentId();
32
+ const cron = options.cron?.trim();
33
+ if (!cron) {
34
+ throw new Error("--cron is required");
35
+ }
36
+ const { client } = await connectScheduleClient(options.host);
37
+ try {
38
+ const maxRuns = options.maxRuns ? Number.parseInt(options.maxRuns, 10) : undefined;
39
+ if (maxRuns !== undefined && (!Number.isSafeInteger(maxRuns) || maxRuns <= 0)) {
40
+ throw new Error("--max-runs must be a positive integer");
41
+ }
42
+ const payload = await client.scheduleCreate({
43
+ prompt: prompt.trim(),
44
+ cadence: {
45
+ type: "cron",
46
+ expression: cron,
47
+ ...(options.timezone?.trim() ? { timezone: options.timezone.trim() } : {}),
48
+ },
49
+ target: { type: "agent", agentId },
50
+ ...(options.name?.trim() ? { name: options.name.trim() } : {}),
51
+ ...(maxRuns ? { maxRuns } : {}),
52
+ ...(options.expiresIn
53
+ ? { expiresAt: new Date(Date.now() + parseDuration(options.expiresIn)).toISOString() }
54
+ : {}),
55
+ });
56
+ if (payload.error || !payload.schedule) {
57
+ throw new Error(payload.error ?? "Heartbeat creation failed");
58
+ }
59
+ return { type: "single", data: toScheduleRow(payload.schedule), schema: scheduleSchema };
60
+ }
61
+ catch (error) {
62
+ throw toScheduleCommandError("HEARTBEAT_CREATE_FAILED", "create heartbeat", error);
63
+ }
64
+ finally {
65
+ await client.close().catch(() => undefined);
66
+ }
67
+ }
68
+ async function runUpdateHeartbeat(id, options, _command) {
69
+ const agentId = requireCallerAgentId();
70
+ const cron = options.cron?.trim();
71
+ if (!cron) {
72
+ throw new Error("--cron is required");
73
+ }
74
+ const { client } = await connectScheduleClient(options.host);
75
+ try {
76
+ await requireOwnedHeartbeat(client, id, agentId);
77
+ const payload = await client.scheduleUpdate({
78
+ id,
79
+ cadence: {
80
+ type: "cron",
81
+ expression: cron,
82
+ ...(options.timezone?.trim() ? { timezone: options.timezone.trim() } : {}),
83
+ },
84
+ });
85
+ if (payload.error || !payload.schedule) {
86
+ throw new Error(payload.error ?? `Heartbeat update failed: ${id}`);
87
+ }
88
+ return { type: "single", data: toScheduleRow(payload.schedule), schema: scheduleSchema };
89
+ }
90
+ catch (error) {
91
+ throw toScheduleCommandError("HEARTBEAT_UPDATE_FAILED", "update heartbeat", error);
92
+ }
93
+ finally {
94
+ await client.close().catch(() => undefined);
95
+ }
96
+ }
97
+ async function runDeleteHeartbeat(id, options, _command) {
98
+ const agentId = requireCallerAgentId();
99
+ const { client } = await connectScheduleClient(options.host);
100
+ try {
101
+ await requireOwnedHeartbeat(client, id, agentId);
102
+ const payload = await client.scheduleDelete({ id });
103
+ if (payload.error) {
104
+ throw new Error(payload.error);
105
+ }
106
+ return {
107
+ type: "single",
108
+ data: { id: payload.scheduleId, status: "deleted" },
109
+ schema: heartbeatDeleteSchema,
110
+ };
111
+ }
112
+ catch (error) {
113
+ throw toScheduleCommandError("HEARTBEAT_DELETE_FAILED", "delete heartbeat", error);
114
+ }
115
+ finally {
116
+ await client.close().catch(() => undefined);
117
+ }
118
+ }
119
+ export function createHeartbeatCommand() {
120
+ const heartbeat = new Command("heartbeat").description("Manage this agent's heartbeats");
121
+ addJsonAndDaemonHostOptions(heartbeat
122
+ .command("create")
123
+ .description("Create a recurring prompt for this agent")
124
+ .argument("<prompt>", "Prompt to send")
125
+ .requiredOption("--cron <expr>", "Five-field cron cadence")
126
+ .option("--timezone <iana>", "IANA time zone")
127
+ .option("--name <name>", "Heartbeat name")
128
+ .option("--max-runs <n>", "Maximum number of runs")
129
+ .option("--expires-in <duration>", "Time to live")).action(withOutput(runCreateHeartbeat));
130
+ addJsonAndDaemonHostOptions(heartbeat
131
+ .command("update")
132
+ .description("Change a heartbeat cron cadence")
133
+ .argument("<id>", "Heartbeat ID")
134
+ .requiredOption("--cron <expr>", "Five-field cron cadence")
135
+ .option("--timezone <iana>", "IANA time zone")).action(withOutput(runUpdateHeartbeat));
136
+ addJsonAndDaemonHostOptions(heartbeat.command("delete").description("Delete a heartbeat").argument("<id>", "Heartbeat ID")).action(withOutput(runDeleteHeartbeat));
137
+ return heartbeat;
138
+ }
139
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function createHubCommand(): Command;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,65 @@
1
+ import { Command } from "commander";
2
+ import { withOutput } from "../../output/index.js";
3
+ import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
4
+ import { connectToDaemon } from "../../utils/client.js";
5
+ const schema = {
6
+ idField: "state",
7
+ columns: [
8
+ { header: "STATE", field: "state" },
9
+ { header: "HUB", field: "hub" },
10
+ { header: "DAEMON", field: "daemonId" },
11
+ { header: "SCOPES", field: "scopes" },
12
+ { header: "CONNECTED", field: "connectedAt" },
13
+ { header: "ERROR", field: "error" },
14
+ { header: "WARNING", field: "warning" },
15
+ ],
16
+ };
17
+ function result(status, warning) {
18
+ return {
19
+ type: "list",
20
+ data: [
21
+ {
22
+ state: status.state,
23
+ daemonId: status.daemonId,
24
+ hub: status.hubOrigin,
25
+ scopes: status.scopes.join(", "),
26
+ connectedAt: status.connectedAt,
27
+ error: status.lastError,
28
+ warning,
29
+ },
30
+ ],
31
+ schema,
32
+ };
33
+ }
34
+ async function withClient(host, action) {
35
+ const client = await connectToDaemon({ host });
36
+ try {
37
+ return await action(client);
38
+ }
39
+ finally {
40
+ await client.close().catch(() => undefined);
41
+ }
42
+ }
43
+ export function createHubCommand() {
44
+ const hub = new Command("hub").description("Manage this daemon's Paseo Hub relationship");
45
+ addJsonAndDaemonHostOptions(hub.command("connect").argument("<url>").requiredOption("--token <token>")).action(withOutput(async (...args) => {
46
+ const url = args[0];
47
+ const options = args.at(-2);
48
+ return withClient(options.host, async (client) => result((await client.connectHub(url, options.token)).status));
49
+ }));
50
+ addJsonAndDaemonHostOptions(hub.command("status")).action(withOutput(async (...args) => {
51
+ const options = args.at(-2);
52
+ return withClient(options.host, async (client) => result((await client.getHubStatus()).status));
53
+ }));
54
+ addJsonAndDaemonHostOptions(hub
55
+ .command("disconnect")
56
+ .option("--force", "Remove local authority even if the Hub is offline")).action(withOutput(async (...args) => {
57
+ const options = args.at(-2);
58
+ return withClient(options.host, async (client) => {
59
+ const response = await client.disconnectHub(options.force ?? false);
60
+ return result(response.status, response.warning);
61
+ });
62
+ }));
63
+ return hub;
64
+ }
65
+ //# sourceMappingURL=index.js.map
@@ -1,2 +1,4 @@
1
+ import { type AgentDeepLinkTarget } from "@getpaseo/protocol/agent-deep-link";
1
2
  export declare function openDesktopWithProject(projectPath: string): Promise<void>;
3
+ export declare function openDesktopWithAgent(target: AgentDeepLinkTarget): Promise<void>;
2
4
  //# sourceMappingURL=open.d.ts.map
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import path from "node:path";
4
4
  import { spawnProcess } from "@getpaseo/server";
5
+ import { buildAgentDeepLink } from "@getpaseo/protocol/agent-deep-link";
5
6
  function findDesktopApp() {
6
7
  if (process.platform === "darwin") {
7
8
  const candidates = [
@@ -55,25 +56,26 @@ function spawnDetached(command, args) {
55
56
  env: cleanEnvForDesktopLaunch(),
56
57
  }).unref();
57
58
  }
59
+ function launchDesktop(args) {
60
+ if (process.env.PASEO_DESKTOP_CLI === "1") {
61
+ throw new Error("Cannot open Paseo Desktop while running in desktop CLI passthrough mode.");
62
+ }
63
+ const desktopApp = findDesktopApp();
64
+ if (!desktopApp) {
65
+ throw new Error("Paseo desktop app not found. Install it from https://github.com/getpaseo/paseo/releases");
66
+ }
67
+ if (process.platform === "darwin") {
68
+ // -n forces a new instance even if the app is already running. The new
69
+ // instance relays its argv to the existing one through Electron's
70
+ // single-instance lock. -g keeps the terminal in the foreground.
71
+ spawnDetached("open", ["-n", "-g", "-a", desktopApp, "--args", ...args]);
72
+ return;
73
+ }
74
+ spawnDetached(desktopApp, args);
75
+ }
58
76
  export async function openDesktopWithProject(projectPath) {
59
77
  try {
60
- if (process.env.PASEO_DESKTOP_CLI === "1") {
61
- throw new Error("Cannot open a desktop project while running in desktop CLI passthrough mode.");
62
- }
63
- const desktopApp = findDesktopApp();
64
- if (!desktopApp) {
65
- throw new Error("Paseo desktop app not found. Install it from https://github.com/getpaseo/paseo/releases");
66
- }
67
- if (process.platform === "darwin") {
68
- // -n forces a new instance even if the app is already running.
69
- // The new instance hits requestSingleInstanceLock(), fails, and relays
70
- // the argv to the first instance via the second-instance event.
71
- // -g keeps the terminal in the foreground (better CLI UX).
72
- // Without -n, macOS just activates the existing window and drops --args.
73
- spawnDetached("open", ["-n", "-g", "-a", desktopApp, "--args", projectPath]);
74
- return;
75
- }
76
- spawnDetached(desktopApp, [projectPath]);
78
+ launchDesktop([projectPath]);
77
79
  }
78
80
  catch (error) {
79
81
  const message = error instanceof Error ? error.message : String(error);
@@ -81,4 +83,7 @@ export async function openDesktopWithProject(projectPath) {
81
83
  process.exitCode = 1;
82
84
  }
83
85
  }
86
+ export async function openDesktopWithAgent(target) {
87
+ launchDesktop([buildAgentDeepLink(target)]);
88
+ }
84
89
  //# sourceMappingURL=open.js.map
@@ -1,4 +1,4 @@
1
- import { Command } from "commander";
1
+ import { Command, Option } from "commander";
2
2
  import { withOutput } from "../../output/index.js";
3
3
  import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
4
4
  import { runCreateCommand } from "./create.js";
@@ -16,16 +16,15 @@ export function createScheduleCommand() {
16
16
  .command("create")
17
17
  .description("Create a schedule")
18
18
  .argument("<prompt>", "Prompt to run on the schedule")
19
- .option("--every <duration>", "Fixed interval cadence (for example: 5m, 1h)")
19
+ .option("--every <duration>", "Cron-compatible cadence preset (for example: 5m, 1h)")
20
20
  .option("--cron <expr>", "Cron cadence expression")
21
21
  .option("--timezone <iana>", "IANA time zone for cron cadence (default: UTC)")
22
22
  .option("--name <name>", "Optional schedule name")
23
- .option("--target <self|new-agent|agent-id>", "Run target")
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
26
  .option("--cwd <path>", "Working directory (default: current; required with --host)")
27
- .option("--run-now", "Fire one immediate run on creation (only with --cron)")
28
- .option("--no-run-now", "Wait the full interval before the first run (only with --every)")
27
+ .option("--run-now", "Fire one immediate run on creation")
29
28
  .option("--max-runs <n>", "Maximum number of runs")
30
29
  .option("--expires-in <duration>", "Time to live for the schedule")).action(withOutput(runCreateCommand));
31
30
  addJsonAndDaemonHostOptions(schedule.command("ls").description("List schedules")).action(withOutput(runLsCommand));
@@ -48,7 +47,7 @@ export function createScheduleCommand() {
48
47
  .command("update")
49
48
  .description("Update an existing schedule in place")
50
49
  .argument("<id>", "Schedule ID")
51
- .option("--every <duration>", "Switch to fixed interval cadence (for example: 5m, 1h)")
50
+ .option("--every <duration>", "Cron-compatible cadence preset (for example: 5m, 1h)")
52
51
  .option("--cron <expr>", "Switch to cron cadence expression")
53
52
  .option("--timezone <iana>", "IANA time zone for cron cadence (requires --cron)")
54
53
  .option("--name <name>", "Rename the schedule (empty string clears the name)")
@@ -7,6 +7,9 @@ export async function runInspectCommand(id, options, _command) {
7
7
  if (payload.error || !payload.schedule) {
8
8
  throw new Error(payload.error ?? `Schedule not found: ${id}`);
9
9
  }
10
+ if (payload.schedule.target.type !== "new-agent") {
11
+ throw new Error(`Schedule not found: ${id}`);
12
+ }
10
13
  const rows = createScheduleInspectRows(payload.schedule);
11
14
  return {
12
15
  type: "list",
@@ -1,8 +1,9 @@
1
1
  import { scheduleLogSchema, toScheduleLogRow } from "./schema.js";
2
- import { connectScheduleClient, toScheduleCommandError, } from "./shared.js";
2
+ import { connectScheduleClient, requireNewAgentSchedule, toScheduleCommandError, } from "./shared.js";
3
3
  export async function runLogsCommand(id, options, _command) {
4
4
  const { client } = await connectScheduleClient(options.host);
5
5
  try {
6
+ await requireNewAgentSchedule(client, id);
6
7
  const payload = await client.scheduleLogs({ id });
7
8
  if (payload.error) {
8
9
  throw new Error(payload.error);
@@ -9,7 +9,9 @@ export async function runLsCommand(options, _command) {
9
9
  }
10
10
  return {
11
11
  type: "list",
12
- data: payload.schedules.map(toScheduleRow),
12
+ data: payload.schedules
13
+ .filter((schedule) => schedule.target.type === "new-agent")
14
+ .map(toScheduleRow),
13
15
  schema: scheduleSchema,
14
16
  };
15
17
  }
@@ -1,8 +1,9 @@
1
1
  import { scheduleSchema } from "./schema.js";
2
- import { connectScheduleClient, toScheduleCommandError, toScheduleRow, } from "./shared.js";
2
+ import { connectScheduleClient, requireNewAgentSchedule, toScheduleCommandError, toScheduleRow, } from "./shared.js";
3
3
  export async function runPauseCommand(id, options, _command) {
4
4
  const { client } = await connectScheduleClient(options.host);
5
5
  try {
6
+ await requireNewAgentSchedule(client, id);
6
7
  const payload = await client.schedulePause({ id });
7
8
  if (payload.error || !payload.schedule) {
8
9
  throw new Error(payload.error ?? `Failed to pause schedule: ${id}`);
@@ -1,8 +1,9 @@
1
1
  import { scheduleSchema } from "./schema.js";
2
- import { connectScheduleClient, toScheduleCommandError, toScheduleRow, } from "./shared.js";
2
+ import { connectScheduleClient, requireNewAgentSchedule, toScheduleCommandError, toScheduleRow, } from "./shared.js";
3
3
  export async function runResumeCommand(id, options, _command) {
4
4
  const { client } = await connectScheduleClient(options.host);
5
5
  try {
6
+ await requireNewAgentSchedule(client, id);
6
7
  const payload = await client.scheduleResume({ id });
7
8
  if (payload.error || !payload.schedule) {
8
9
  throw new Error(payload.error ?? `Failed to resume schedule: ${id}`);
@@ -1,8 +1,9 @@
1
1
  import { scheduleSchema } from "./schema.js";
2
- import { connectScheduleClient, toScheduleCommandError, toScheduleRow, } from "./shared.js";
2
+ import { connectScheduleClient, requireNewAgentSchedule, toScheduleCommandError, toScheduleRow, } from "./shared.js";
3
3
  export async function runRunOnceCommand(id, options, _command) {
4
4
  const { client } = await connectScheduleClient(options.host);
5
5
  try {
6
+ await requireNewAgentSchedule(client, id);
6
7
  const payload = await client.scheduleRunOnce({ id });
7
8
  if (payload.error || !payload.schedule) {
8
9
  throw new Error(payload.error ?? `Failed to run schedule once: ${id}`);
@@ -8,6 +8,7 @@ export declare function connectScheduleClient(host: string | undefined): Promise
8
8
  host: string;
9
9
  }>;
10
10
  export declare function toScheduleCommandError(code: string, action: string, error: unknown): CommandError;
11
+ export declare function requireNewAgentSchedule(client: ScheduleDaemonClient, id: string): Promise<void>;
11
12
  export declare function formatCadence(cadence: ScheduleCadence): string;
12
13
  export declare function formatTarget(target: ScheduleTarget | ScheduleListItem["target"]): string;
13
14
  export declare function formatDurationMs(durationMs: number): string;
@@ -43,6 +44,7 @@ export interface ScheduleUpdateOptionsInput {
43
44
  clearExpires?: boolean;
44
45
  }
45
46
  export declare function parseScheduleUpdateInput(options: ScheduleUpdateOptionsInput): UpdateScheduleInput;
47
+ export declare function compileEveryPresetToCron(value: string): string;
46
48
  export interface ScheduleRow {
47
49
  id: string;
48
50
  name: string | null;
@@ -1,6 +1,7 @@
1
1
  import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
2
2
  import { parseDuration } from "../../utils/duration.js";
3
3
  import { resolveProviderAndModel } from "../../utils/provider-model.js";
4
+ import { everyMsToFiveFieldCron } from "@getpaseo/protocol/schedule/cadence";
4
5
  export async function connectScheduleClient(host) {
5
6
  const resolvedHost = getDaemonHost({ host });
6
7
  try {
@@ -28,6 +29,12 @@ export function toScheduleCommandError(code, action, error) {
28
29
  message: `Failed to ${action}: ${message}`,
29
30
  };
30
31
  }
32
+ export async function requireNewAgentSchedule(client, id) {
33
+ const payload = await client.scheduleInspect({ id });
34
+ if (payload.error || !payload.schedule || payload.schedule.target.type !== "new-agent") {
35
+ throw new Error(payload.error ?? `Schedule not found: ${id}`);
36
+ }
37
+ }
31
38
  export function formatCadence(cadence) {
32
39
  if (cadence.type === "cron") {
33
40
  const timezoneSuffix = cadence.timezone ? ` (${cadence.timezone})` : "";
@@ -66,11 +73,7 @@ export function formatDurationMs(durationMs) {
66
73
  }
67
74
  function resolveScheduleTarget(args) {
68
75
  const { targetValue, hasExplicitNewAgentOption, createNewAgentTarget } = args;
69
- const currentAgentId = process.env.PASEO_AGENT_ID?.trim();
70
76
  if (!targetValue) {
71
- if (currentAgentId && !hasExplicitNewAgentOption) {
72
- return { type: "self", agentId: currentAgentId };
73
- }
74
77
  return createNewAgentTarget();
75
78
  }
76
79
  if (targetValue === "new-agent") {
@@ -84,6 +87,9 @@ function resolveScheduleTarget(args) {
84
87
  };
85
88
  }
86
89
  if (targetValue === "self") {
90
+ // COMPAT(scheduleSelfTarget): heartbeat creation moved to `paseo heartbeat create`.
91
+ // Added in v0.2.0; remove after 2027-01-17.
92
+ const currentAgentId = process.env.PASEO_AGENT_ID?.trim();
87
93
  if (!currentAgentId) {
88
94
  throw {
89
95
  code: "INVALID_TARGET",
@@ -153,22 +159,8 @@ export function parseScheduleCreateInput(options) {
153
159
  ...(expiresAt ? { expiresAt } : {}),
154
160
  };
155
161
  }
156
- function resolveRunOnCreate(runNow, cadenceType) {
157
- if (runNow === true && cadenceType === "every") {
158
- throw {
159
- code: "REDUNDANT_RUN_NOW",
160
- message: "--run-now is redundant with --every (interval schedules already fire on creation)",
161
- details: "Drop --run-now, or use --no-run-now to wait the full interval before the first run",
162
- };
163
- }
164
- if (runNow === false && cadenceType === "cron") {
165
- throw {
166
- code: "REDUNDANT_NO_RUN_NOW",
167
- message: "--no-run-now is redundant with --cron (cron schedules never fire on creation)",
168
- details: "Drop --no-run-now, or use --run-now to fire one immediate run on creation",
169
- };
170
- }
171
- return runNow ?? cadenceType === "every";
162
+ function resolveRunOnCreate(runNow, _cadenceType) {
163
+ return runNow ?? false;
172
164
  }
173
165
  export function parseScheduleUpdateInput(options) {
174
166
  const id = options.id.trim();
@@ -220,7 +212,7 @@ function parseCadenceFromFlags(every, cron, timezone) {
220
212
  };
221
213
  }
222
214
  if (every !== undefined) {
223
- return { type: "every", everyMs: parseDuration(every) };
215
+ return { type: "cron", expression: compileEveryPresetToCron(every) };
224
216
  }
225
217
  if (cron !== undefined) {
226
218
  return {
@@ -231,6 +223,18 @@ function parseCadenceFromFlags(every, cron, timezone) {
231
223
  }
232
224
  return undefined;
233
225
  }
226
+ export function compileEveryPresetToCron(value) {
227
+ const durationMs = parseDuration(value);
228
+ const cron = everyMsToFiveFieldCron(durationMs);
229
+ if (cron) {
230
+ return cron;
231
+ }
232
+ throw {
233
+ code: "UNREPRESENTABLE_CADENCE",
234
+ message: `${value} cannot be represented faithfully by five-field cron`,
235
+ details: "Use --cron for calendar schedules",
236
+ };
237
+ }
234
238
  function parseTimeZoneFlag(timeZone) {
235
239
  if (timeZone === undefined) {
236
240
  return undefined;
@@ -1,5 +1,5 @@
1
1
  import { createScheduleInspectRows, createScheduleInspectSchema, } from "./schema.js";
2
- import { connectScheduleClient, parseScheduleUpdateInput, toScheduleCommandError, } from "./shared.js";
2
+ import { connectScheduleClient, parseScheduleUpdateInput, requireNewAgentSchedule, toScheduleCommandError, } from "./shared.js";
3
3
  export async function runUpdateCommand(id, options, _command) {
4
4
  const input = parseScheduleUpdateInput({
5
5
  id,
@@ -19,6 +19,7 @@ export async function runUpdateCommand(id, options, _command) {
19
19
  });
20
20
  const { client } = await connectScheduleClient(options.host);
21
21
  try {
22
+ await requireNewAgentSchedule(client, id);
22
23
  const payload = await client.scheduleUpdate(input);
23
24
  if (payload.error || !payload.schedule) {
24
25
  throw new Error(payload.error ?? `Failed to update schedule: ${id}`);
@@ -0,0 +1,12 @@
1
+ import type { Command } from "commander";
2
+ import type { SingleResult } from "../../output/index.js";
3
+ interface WorkspaceArchiveResult {
4
+ workspaceId: string;
5
+ status: "archived";
6
+ archivedAt: string;
7
+ }
8
+ export declare function runArchiveCommand(workspaceId: string, options: {
9
+ host?: string;
10
+ }, _command: Command): Promise<SingleResult<WorkspaceArchiveResult>>;
11
+ export {};
12
+ //# sourceMappingURL=archive.d.ts.map
@@ -0,0 +1,41 @@
1
+ import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
2
+ const workspaceArchiveSchema = {
3
+ idField: "workspaceId",
4
+ columns: [
5
+ { header: "WORKSPACE ID", field: "workspaceId", width: 20 },
6
+ { header: "STATUS", field: "status", width: 10 },
7
+ { header: "ARCHIVED AT", field: "archivedAt", width: 26 },
8
+ ],
9
+ };
10
+ export async function runArchiveCommand(workspaceId, options, _command) {
11
+ const host = getDaemonHost({ host: options.host });
12
+ const client = await connectToDaemon({ host: options.host }).catch((error) => {
13
+ const message = error instanceof Error ? error.message : String(error);
14
+ throw {
15
+ code: "DAEMON_NOT_RUNNING",
16
+ message: `Cannot connect to daemon at ${host}: ${message}`,
17
+ };
18
+ });
19
+ try {
20
+ const payload = await client.archiveWorkspace(workspaceId);
21
+ if (payload.error) {
22
+ throw new Error(payload.error);
23
+ }
24
+ if (!payload.archivedAt) {
25
+ throw new Error("Workspace archive did not return an archive timestamp");
26
+ }
27
+ return {
28
+ type: "single",
29
+ data: { workspaceId, status: "archived", archivedAt: payload.archivedAt },
30
+ schema: workspaceArchiveSchema,
31
+ };
32
+ }
33
+ catch (error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ throw { code: "WORKSPACE_ARCHIVE_FAILED", message };
36
+ }
37
+ finally {
38
+ await client.close().catch(() => undefined);
39
+ }
40
+ }
41
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1,49 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandOptions, SingleResult } from "../../output/index.js";
3
+ import { type WorkspaceRow } from "./shared.js";
4
+ export interface WorkspaceCreateOptions extends CommandOptions {
5
+ isolation?: string;
6
+ path?: string;
7
+ project?: string;
8
+ title?: string;
9
+ mode?: string;
10
+ worktreeSlug?: string;
11
+ newBranch?: string;
12
+ base?: string;
13
+ branch?: string;
14
+ prNumber?: string;
15
+ forge?: string;
16
+ }
17
+ export declare function buildWorkspaceSource(options: WorkspaceCreateOptions): {
18
+ projectId?: string | undefined;
19
+ kind: "directory";
20
+ path: string;
21
+ } | {
22
+ baseBranch?: string | undefined;
23
+ branchName?: string | undefined;
24
+ action: "branch-off";
25
+ kind: "worktree";
26
+ cwd?: string;
27
+ projectId?: string;
28
+ worktreeSlug?: string;
29
+ } | {
30
+ action: "checkout";
31
+ refName: string;
32
+ kind: "worktree";
33
+ cwd?: string;
34
+ projectId?: string;
35
+ worktreeSlug?: string;
36
+ } | {
37
+ action: "checkout";
38
+ checkoutSource: {
39
+ number: number;
40
+ forge?: string | undefined;
41
+ kind: "change_request";
42
+ };
43
+ kind: "worktree";
44
+ cwd?: string;
45
+ projectId?: string;
46
+ worktreeSlug?: string;
47
+ };
48
+ export declare function runCreateCommand(options: WorkspaceCreateOptions, _command: Command): Promise<SingleResult<WorkspaceRow>>;
49
+ //# sourceMappingURL=create.d.ts.map