@camerontaylor/paseo-cli 0.11.2-fork.2 → 0.12.0-beta.1.fork.1

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 (35) hide show
  1. package/dist/cli.js +2 -0
  2. package/dist/commands/agent/logs.d.ts +19 -2
  3. package/dist/commands/agent/logs.js +143 -12
  4. package/dist/commands/agent/ls.d.ts +17 -2
  5. package/dist/commands/agent/ls.js +24 -6
  6. package/dist/commands/agent/run.d.ts +3 -14
  7. package/dist/commands/agent/run.js +20 -29
  8. package/dist/commands/agent/send.js +4 -1
  9. package/dist/commands/permit/allow.d.ts +17 -2
  10. package/dist/commands/permit/allow.js +96 -82
  11. package/dist/commands/permit/deny.d.ts +3 -2
  12. package/dist/commands/permit/deny.js +85 -71
  13. package/dist/commands/usage/index.d.ts +29 -0
  14. package/dist/commands/usage/index.js +62 -0
  15. package/dist/commands/usage/presentation.d.ts +4 -0
  16. package/dist/commands/usage/presentation.js +122 -0
  17. package/dist/commands/workspace/create.d.ts +1 -0
  18. package/dist/commands/workspace/create.js +4 -0
  19. package/dist/commands/workspace/index.js +7 -2
  20. package/dist/commands/workspace/ls.d.ts +5 -2
  21. package/dist/commands/workspace/ls.js +7 -2
  22. package/dist/commands/workspace/shared.d.ts +3 -0
  23. package/dist/commands/workspace/shared.js +9 -0
  24. package/dist/commands/worktree/archive.d.ts +1 -0
  25. package/dist/commands/worktree/archive.js +3 -1
  26. package/dist/commands/worktree/index.js +6 -2
  27. package/dist/commands/worktree/ls.d.ts +1 -0
  28. package/dist/commands/worktree/ls.js +3 -1
  29. package/dist/commands/worktree/repository-directory.d.ts +6 -0
  30. package/dist/commands/worktree/repository-directory.js +9 -0
  31. package/dist/utils/caller-agent.d.ts +14 -0
  32. package/dist/utils/caller-agent.js +16 -0
  33. package/dist/utils/timeline.d.ts +1 -0
  34. package/dist/utils/timeline.js +6 -1
  35. package/package.json +4 -4
@@ -30,95 +30,109 @@ export function toPermissionResponseItem(agentId, permission, result) {
30
30
  result,
31
31
  };
32
32
  }
33
- export async function runAllowCommand(agentIdOrPrefix, reqId, options, _command) {
34
- // No validation needed - if no reqId provided, allow all by default
35
- // Parse input JSON if provided
36
- let updatedInput;
37
- if (options.input) {
38
- try {
39
- updatedInput = JSON.parse(options.input);
40
- }
41
- catch (err) {
42
- const error = {
43
- code: "INVALID_JSON",
44
- message: `Invalid JSON for --input: ${err instanceof Error ? err.message : String(err)}`,
45
- details: 'Provide valid JSON, e.g., --input \'{"key": "value"}\'',
46
- };
47
- throw error;
48
- }
49
- }
50
- const client = await connectToDaemon({ target: options.daemonTarget });
51
- try {
52
- const fetchResult = await client.fetchAgent({ agentId: agentIdOrPrefix });
53
- if (!fetchResult) {
54
- await client.close();
55
- const error = {
56
- code: "AGENT_NOT_FOUND",
57
- message: `Agent not found: ${agentIdOrPrefix}`,
58
- details: 'Use "paseo ls" to list available agents',
59
- };
60
- throw error;
61
- }
62
- const agent = fetchResult.agent;
63
- const resolvedAgentId = agent.id;
64
- // Get pending permissions for this agent
65
- const pendingPermissions = agent.pendingPermissions || [];
66
- if (pendingPermissions.length === 0) {
67
- await client.close();
68
- const error = {
69
- code: "NO_PENDING_PERMISSIONS",
70
- message: `No pending permissions for agent ${agent.id.slice(0, 7)}`,
71
- };
72
- throw error;
73
- }
74
- // Determine which permissions to allow
75
- let permissionsToAllow;
76
- if (!reqId || options.all) {
77
- // Default: allow all pending permissions if no req_id specified
78
- // --all flag is kept as an explicit alias for clarity
79
- permissionsToAllow = pendingPermissions;
33
+ export function createAllowCommand(connect = connectToDaemon) {
34
+ return async function runAllowCommand(agentIdOrPrefix, reqId, options, _command) {
35
+ // No validation needed - if no reqId provided, allow all by default
36
+ // Parse input JSON if provided
37
+ let updatedInput;
38
+ if (options.input) {
39
+ try {
40
+ updatedInput = JSON.parse(options.input);
41
+ }
42
+ catch (err) {
43
+ const error = {
44
+ code: "INVALID_JSON",
45
+ message: `Invalid JSON for --input: ${err instanceof Error ? err.message : String(err)}`,
46
+ details: 'Provide valid JSON, e.g., --input \'{"key": "value"}\'',
47
+ };
48
+ throw error;
49
+ }
80
50
  }
81
- else {
82
- // Find permission by ID prefix
83
- const permission = pendingPermissions.find((p) => p.id === reqId || p.id.startsWith(reqId));
84
- if (!permission) {
51
+ const client = await connect({ target: options.daemonTarget });
52
+ try {
53
+ const fetchResult = await client.fetchAgent({ agentId: agentIdOrPrefix });
54
+ if (!fetchResult) {
55
+ await client.close();
56
+ const error = {
57
+ code: "AGENT_NOT_FOUND",
58
+ message: `Agent not found: ${agentIdOrPrefix}`,
59
+ details: 'Use "paseo ls" to list available agents',
60
+ };
61
+ throw error;
62
+ }
63
+ const agent = fetchResult.agent;
64
+ const resolvedAgentId = agent.id;
65
+ // Get pending permissions for this agent
66
+ const pendingPermissions = agent.pendingPermissions || [];
67
+ if (pendingPermissions.length === 0) {
85
68
  await client.close();
86
69
  const error = {
87
- code: "PERMISSION_NOT_FOUND",
88
- message: `Permission request not found: ${reqId}`,
89
- details: `Available requests: ${pendingPermissions.map((p) => p.id.slice(0, 8)).join(", ")}`,
70
+ code: "NO_PENDING_PERMISSIONS",
71
+ message: `No pending permissions for agent ${agent.id.slice(0, 7)}`,
90
72
  };
91
73
  throw error;
92
74
  }
93
- permissionsToAllow = [permission];
75
+ // Determine which permissions to allow
76
+ let permissionsToAllow;
77
+ if (!reqId || options.all) {
78
+ // Default: allow all pending permissions if no req_id specified
79
+ // --all flag is kept as an explicit alias for clarity
80
+ permissionsToAllow = pendingPermissions;
81
+ }
82
+ else {
83
+ // Find permission by ID prefix
84
+ const permission = pendingPermissions.find((p) => p.id === reqId || p.id.startsWith(reqId));
85
+ if (!permission) {
86
+ await client.close();
87
+ const error = {
88
+ code: "PERMISSION_NOT_FOUND",
89
+ message: `Permission request not found: ${reqId}`,
90
+ details: `Available requests: ${pendingPermissions.map((p) => p.id.slice(0, 8)).join(", ")}`,
91
+ };
92
+ throw error;
93
+ }
94
+ permissionsToAllow = [permission];
95
+ }
96
+ // Allow permissions
97
+ const results = await Promise.all(permissionsToAllow.map(async (permission) => {
98
+ try {
99
+ await client.respondToPermissionAndWait(resolvedAgentId, permission.id, {
100
+ behavior: "allow",
101
+ ...(updatedInput ? { updatedInput } : {}),
102
+ });
103
+ }
104
+ catch (err) {
105
+ const message = err instanceof Error ? err.message : String(err);
106
+ const error = {
107
+ code: "PERMISSION_RESPONSE_UNCONFIRMED",
108
+ message: `Permission response unconfirmed for request ${permission.id}: ${message}`,
109
+ details: 'Use "paseo permit ls" to check whether the request is still pending',
110
+ };
111
+ throw error;
112
+ }
113
+ return toPermissionResponseItem(resolvedAgentId, permission, "allowed");
114
+ }));
115
+ await client.close();
116
+ return {
117
+ type: "list",
118
+ data: results,
119
+ schema: permitResponseSchema,
120
+ };
94
121
  }
95
- // Allow permissions
96
- const results = await Promise.all(permissionsToAllow.map(async (permission) => {
97
- await client.respondToPermission(resolvedAgentId, permission.id, {
98
- behavior: "allow",
99
- ...(updatedInput ? { updatedInput } : {}),
100
- });
101
- return toPermissionResponseItem(resolvedAgentId, permission, "allowed");
102
- }));
103
- await client.close();
104
- return {
105
- type: "list",
106
- data: results,
107
- schema: permitResponseSchema,
108
- };
109
- }
110
- catch (err) {
111
- await client.close().catch(() => { });
112
- // Re-throw CommandErrors
113
- if (err && typeof err === "object" && "code" in err) {
114
- throw err;
122
+ catch (err) {
123
+ await client.close().catch(() => { });
124
+ // Re-throw CommandErrors
125
+ if (err && typeof err === "object" && "code" in err) {
126
+ throw err;
127
+ }
128
+ const message = err instanceof Error ? err.message : String(err);
129
+ const error = {
130
+ code: "ALLOW_PERMISSION_FAILED",
131
+ message: `Failed to allow permission: ${message}`,
132
+ };
133
+ throw error;
115
134
  }
116
- const message = err instanceof Error ? err.message : String(err);
117
- const error = {
118
- code: "ALLOW_PERMISSION_FAILED",
119
- message: `Failed to allow permission: ${message}`,
120
- };
121
- throw error;
122
- }
135
+ };
123
136
  }
137
+ export const runAllowCommand = createAllowCommand();
124
138
  //# sourceMappingURL=allow.js.map
@@ -1,6 +1,6 @@
1
1
  import type { Command } from "commander";
2
2
  import type { CommandOptions, ListResult } from "../../output/index.js";
3
- import { type PermissionResponseItem } from "./allow.js";
3
+ import { type PermissionResponseItem, type PermitClientConnector } from "./allow.js";
4
4
  export type PermitDenyResult = ListResult<PermissionResponseItem>;
5
5
  export interface PermitDenyOptions extends CommandOptions {
6
6
  all?: boolean;
@@ -8,5 +8,6 @@ export interface PermitDenyOptions extends CommandOptions {
8
8
  interrupt?: boolean;
9
9
  host?: string;
10
10
  }
11
- export declare function runDenyCommand(agentIdOrPrefix: string, reqId: string | undefined, options: PermitDenyOptions, _command: Command): Promise<PermitDenyResult>;
11
+ export declare function createDenyCommand(connect?: PermitClientConnector): (agentIdOrPrefix: string, reqId: string | undefined, options: PermitDenyOptions, _command: Command) => Promise<PermitDenyResult>;
12
+ export declare const runDenyCommand: (agentIdOrPrefix: string, reqId: string | undefined, options: PermitDenyOptions, _command: Command) => Promise<PermitDenyResult>;
12
13
  //# sourceMappingURL=deny.d.ts.map
@@ -1,86 +1,100 @@
1
1
  import { connectToDaemon } from "../../utils/client.js";
2
2
  import { permitResponseSchema, toPermissionResponseItem, } from "./allow.js";
3
- export async function runDenyCommand(agentIdOrPrefix, reqId, options, _command) {
4
- // Validate arguments
5
- if (!options.all && !reqId) {
6
- const error = {
7
- code: "MISSING_ARGUMENT",
8
- message: "Request ID is required unless --all is specified",
9
- details: "Usage: paseo permit deny <agent> <req_id> or paseo permit deny <agent> --all",
10
- };
11
- throw error;
12
- }
13
- const client = await connectToDaemon({ target: options.daemonTarget });
14
- try {
15
- const fetchResult = await client.fetchAgent({ agentId: agentIdOrPrefix });
16
- if (!fetchResult) {
17
- await client.close();
18
- const error = {
19
- code: "AGENT_NOT_FOUND",
20
- message: `Agent not found: ${agentIdOrPrefix}`,
21
- details: 'Use "paseo ls" to list available agents',
22
- };
23
- throw error;
24
- }
25
- const agent = fetchResult.agent;
26
- const resolvedAgentId = agent.id;
27
- // Get pending permissions for this agent
28
- const pendingPermissions = agent.pendingPermissions || [];
29
- if (pendingPermissions.length === 0) {
30
- await client.close();
3
+ export function createDenyCommand(connect = connectToDaemon) {
4
+ return async function runDenyCommand(agentIdOrPrefix, reqId, options, _command) {
5
+ // Validate arguments
6
+ if (!options.all && !reqId) {
31
7
  const error = {
32
- code: "NO_PENDING_PERMISSIONS",
33
- message: `No pending permissions for agent ${agent.id.slice(0, 7)}`,
8
+ code: "MISSING_ARGUMENT",
9
+ message: "Request ID is required unless --all is specified",
10
+ details: "Usage: paseo permit deny <agent> <req_id> or paseo permit deny <agent> --all",
34
11
  };
35
12
  throw error;
36
13
  }
37
- // Determine which permissions to deny
38
- let permissionsToDeny;
39
- if (options.all) {
40
- permissionsToDeny = pendingPermissions;
41
- }
42
- else {
43
- // Find permission by ID prefix
44
- const permission = pendingPermissions.find((p) => p.id === reqId || p.id.startsWith(reqId));
45
- if (!permission) {
14
+ const client = await connect({ target: options.daemonTarget });
15
+ try {
16
+ const fetchResult = await client.fetchAgent({ agentId: agentIdOrPrefix });
17
+ if (!fetchResult) {
18
+ await client.close();
19
+ const error = {
20
+ code: "AGENT_NOT_FOUND",
21
+ message: `Agent not found: ${agentIdOrPrefix}`,
22
+ details: 'Use "paseo ls" to list available agents',
23
+ };
24
+ throw error;
25
+ }
26
+ const agent = fetchResult.agent;
27
+ const resolvedAgentId = agent.id;
28
+ // Get pending permissions for this agent
29
+ const pendingPermissions = agent.pendingPermissions || [];
30
+ if (pendingPermissions.length === 0) {
46
31
  await client.close();
47
32
  const error = {
48
- code: "PERMISSION_NOT_FOUND",
49
- message: `Permission request not found: ${reqId}`,
50
- details: `Available requests: ${pendingPermissions.map((p) => p.id.slice(0, 8)).join(", ")}`,
33
+ code: "NO_PENDING_PERMISSIONS",
34
+ message: `No pending permissions for agent ${agent.id.slice(0, 7)}`,
51
35
  };
52
36
  throw error;
53
37
  }
54
- permissionsToDeny = [permission];
38
+ // Determine which permissions to deny
39
+ let permissionsToDeny;
40
+ if (options.all) {
41
+ permissionsToDeny = pendingPermissions;
42
+ }
43
+ else {
44
+ // Find permission by ID prefix
45
+ const permission = pendingPermissions.find((p) => p.id === reqId || p.id.startsWith(reqId));
46
+ if (!permission) {
47
+ await client.close();
48
+ const error = {
49
+ code: "PERMISSION_NOT_FOUND",
50
+ message: `Permission request not found: ${reqId}`,
51
+ details: `Available requests: ${pendingPermissions.map((p) => p.id.slice(0, 8)).join(", ")}`,
52
+ };
53
+ throw error;
54
+ }
55
+ permissionsToDeny = [permission];
56
+ }
57
+ // Deny permissions
58
+ const results = await Promise.all(permissionsToDeny.map(async (permission) => {
59
+ try {
60
+ await client.respondToPermissionAndWait(resolvedAgentId, permission.id, {
61
+ behavior: "deny",
62
+ ...(options.message ? { message: options.message } : {}),
63
+ ...(options.interrupt ? { interrupt: true } : {}),
64
+ });
65
+ }
66
+ catch (err) {
67
+ const message = err instanceof Error ? err.message : String(err);
68
+ const error = {
69
+ code: "PERMISSION_RESPONSE_UNCONFIRMED",
70
+ message: `Permission response unconfirmed for request ${permission.id}: ${message}`,
71
+ details: 'Use "paseo permit ls" to check whether the request is still pending',
72
+ };
73
+ throw error;
74
+ }
75
+ return toPermissionResponseItem(resolvedAgentId, permission, "denied");
76
+ }));
77
+ await client.close();
78
+ return {
79
+ type: "list",
80
+ data: results,
81
+ schema: permitResponseSchema,
82
+ };
55
83
  }
56
- // Deny permissions
57
- const results = await Promise.all(permissionsToDeny.map(async (permission) => {
58
- await client.respondToPermission(resolvedAgentId, permission.id, {
59
- behavior: "deny",
60
- ...(options.message ? { message: options.message } : {}),
61
- ...(options.interrupt ? { interrupt: true } : {}),
62
- });
63
- return toPermissionResponseItem(resolvedAgentId, permission, "denied");
64
- }));
65
- await client.close();
66
- return {
67
- type: "list",
68
- data: results,
69
- schema: permitResponseSchema,
70
- };
71
- }
72
- catch (err) {
73
- await client.close().catch(() => { });
74
- // Re-throw CommandErrors
75
- if (err && typeof err === "object" && "code" in err) {
76
- throw err;
84
+ catch (err) {
85
+ await client.close().catch(() => { });
86
+ // Re-throw CommandErrors
87
+ if (err && typeof err === "object" && "code" in err) {
88
+ throw err;
89
+ }
90
+ const message = err instanceof Error ? err.message : String(err);
91
+ const error = {
92
+ code: "DENY_PERMISSION_FAILED",
93
+ message: `Failed to deny permission: ${message}`,
94
+ };
95
+ throw error;
77
96
  }
78
- const message = err instanceof Error ? err.message : String(err);
79
- const error = {
80
- code: "DENY_PERMISSION_FAILED",
81
- message: `Failed to deny permission: ${message}`,
82
- };
83
- throw error;
84
- }
97
+ };
85
98
  }
99
+ export const runDenyCommand = createDenyCommand();
86
100
  //# sourceMappingURL=deny.js.map
@@ -0,0 +1,29 @@
1
+ import { Command } from "commander";
2
+ import type { DaemonClient } from "@camerontaylor/paseo-client/internal/daemon-client";
3
+ import type { UsageReportEntry } from "@camerontaylor/paseo-protocol/messages";
4
+ import { type ConnectOptions } from "../../utils/client.js";
5
+ import { type CommandOptions, type CommandResult } from "../../output/index.js";
6
+ interface UsageOptions extends CommandOptions {
7
+ agent?: string;
8
+ refresh?: boolean;
9
+ }
10
+ type UsageSelection = {
11
+ kind: "list";
12
+ } | {
13
+ kind: "inspect";
14
+ id: string;
15
+ };
16
+ interface UsageClient {
17
+ getLastServerInfoMessage(): {
18
+ features?: {
19
+ usageSources?: boolean;
20
+ };
21
+ } | null;
22
+ listUsageReports: DaemonClient["listUsageReports"];
23
+ close: DaemonClient["close"];
24
+ }
25
+ type ConnectUsage = (options: ConnectOptions) => Promise<UsageClient>;
26
+ export declare function runUsageCommand(options: UsageOptions, selection: UsageSelection, connect?: ConnectUsage): Promise<CommandResult<UsageReportEntry>>;
27
+ export declare function createUsageCommand(): Command;
28
+ export {};
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,62 @@
1
+ import { Command } from "commander";
2
+ import { connectToDaemon } from "../../utils/client.js";
3
+ import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
4
+ import { withOutput } from "../../output/index.js";
5
+ import { usageSchema } from "./presentation.js";
6
+ export async function runUsageCommand(options, selection, connect = connectToDaemon) {
7
+ const conflictingSelectors = selection.kind === "inspect" && options.agent !== undefined;
8
+ if (conflictingSelectors) {
9
+ throw {
10
+ code: "INVALID_OPTIONS",
11
+ message: "--agent cannot be combined with usage inspect. Use usage ls --agent <id> for agent account usage, or usage inspect <report-id> for an exact report.",
12
+ };
13
+ }
14
+ const client = await connect({ target: options.daemonTarget });
15
+ try {
16
+ if (client.getLastServerInfoMessage()?.features?.usageSources !== true) {
17
+ throw { code: "UNSUPPORTED_HOST", message: "Update the host to use usage reports." };
18
+ }
19
+ if (selection.kind === "list") {
20
+ const { reports } = await client.listUsageReports({
21
+ agentId: options.agent,
22
+ forceRefresh: options.refresh,
23
+ });
24
+ return { type: "list", data: reports, schema: usageSchema };
25
+ }
26
+ // Report-ID queries only select known accounts. Discover before selecting, including on cold hosts.
27
+ const { reports } = await client.listUsageReports();
28
+ let entry = reports.find((report) => report.id === selection.id);
29
+ if (entry && options.refresh) {
30
+ const refreshed = await client.listUsageReports({
31
+ reportIds: [selection.id],
32
+ forceRefresh: true,
33
+ });
34
+ entry = refreshed.reports.find((report) => report.id === selection.id);
35
+ }
36
+ if (!entry) {
37
+ throw { code: "REPORT_NOT_FOUND", message: `Unknown usage report: ${selection.id}` };
38
+ }
39
+ return { type: "single", data: entry, schema: usageSchema };
40
+ }
41
+ finally {
42
+ await client.close().catch(() => { });
43
+ }
44
+ }
45
+ function addListOptions(command) {
46
+ return addJsonAndDaemonHostOptions(command)
47
+ .option("--agent <id>", "Show account quota associated with this agent (not agent token consumption)")
48
+ .option("--refresh", "Refresh usage instead of using the host cache");
49
+ }
50
+ export function createUsageCommand() {
51
+ const usage = addListOptions(new Command("usage").description("Show account usage reports"));
52
+ const list = (options, _command) => runUsageCommand(options, { kind: "list" });
53
+ usage.action(withOutput(list));
54
+ addListOptions(usage.command("ls").description("List account quotas and balances")).action(withOutput(list));
55
+ addJsonAndDaemonHostOptions(usage
56
+ .command("inspect")
57
+ .description("Show a complete account usage report")
58
+ .argument("<report-id>", "Exact opaque Report ID")
59
+ .option("--refresh", "Refresh the selected report instead of using the host cache")).action(withOutput((id, options, _command) => runUsageCommand(options, { kind: "inspect", id })));
60
+ return usage;
61
+ }
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import type { UsageReportEntry } from "@camerontaylor/paseo-protocol/messages";
2
+ import { type OutputSchema } from "../../output/index.js";
3
+ export declare const usageSchema: OutputSchema<UsageReportEntry>;
4
+ //# sourceMappingURL=presentation.d.ts.map
@@ -0,0 +1,122 @@
1
+ import { renderTable } from "../../output/index.js";
2
+ function value(input) {
3
+ return input == null ? "-" : String(input);
4
+ }
5
+ function percent(input) {
6
+ return input == null ? "-" : `${input}%`;
7
+ }
8
+ function problemText(problem) {
9
+ if (problem.kind === "no_quota")
10
+ return problem.detail;
11
+ const remedy = problem.refreshedBy
12
+ ? `Run ${problem.refreshedBy} to refresh login.`
13
+ : "Sign in again.";
14
+ if (problem.kind === "expired")
15
+ return `Login expired at ${problem.expiresAt}. ${remedy}`;
16
+ return `Login rejected (HTTP ${problem.status}). ${remedy}`;
17
+ }
18
+ function failureText(report) {
19
+ if (report.status === "error")
20
+ return report.error;
21
+ if (report.status === "unavailable")
22
+ return problemText(report.problem);
23
+ return "-";
24
+ }
25
+ function windowText(window) {
26
+ return `${window.label}: ${percent(window.usedPct)} used, ${percent(window.remainingPct)} remaining`;
27
+ }
28
+ function balanceText(balance) {
29
+ return `${balance.label}: ${value(balance.used)} used, ${value(balance.remaining)} remaining, ${value(balance.limit)} limit (${balance.unit})`;
30
+ }
31
+ function summary(entry) {
32
+ const { report } = entry;
33
+ if (report.status !== "available") {
34
+ const lines = [failureText(report)];
35
+ for (const login of entry.loginErrors ?? [])
36
+ lines.push(`${login.harness}: ${failureText(login.report)}`);
37
+ return lines;
38
+ }
39
+ const lines = report.windows.map(windowText);
40
+ lines.push(...(report.balances ?? []).map(balanceText));
41
+ return lines.length ? lines : ["-"];
42
+ }
43
+ const summarySchema = {
44
+ idField: "id",
45
+ columns: [
46
+ { header: "REPORT ID", field: "id" },
47
+ { header: "SOURCE", field: "source" },
48
+ { header: "ACCOUNT", field: "account" },
49
+ { header: "STATUS", field: "status" },
50
+ { header: "USAGE", field: "usage" },
51
+ { header: "FETCHED", field: "fetched" },
52
+ ],
53
+ };
54
+ function inspect(entry) {
55
+ const lines = [
56
+ `Report ID: ${entry.id}`,
57
+ `Source: ${entry.sourceLabel}`,
58
+ `Source ID: ${entry.sourceId}`,
59
+ `Account: ${value(entry.account.label)}`,
60
+ `Status: ${entry.report.status}`,
61
+ `Fetched: ${value(entry.fetchedAt)}`,
62
+ ];
63
+ const { report } = entry;
64
+ if (report.status === "available") {
65
+ lines.push(`Plan: ${value(report.planLabel)}`, "Windows:");
66
+ if (!report.windows.length)
67
+ lines.push(" -");
68
+ for (const window of report.windows) {
69
+ lines.push(` ${windowText(window)} [${window.id}]`, ` Resets: ${value(window.resetsAt)}`);
70
+ if (window.runsOutAt != null)
71
+ lines.push(` Runs out: ${window.runsOutAt}`);
72
+ if (window.shortfallPct != null)
73
+ lines.push(` Shortfall: ${percent(window.shortfallPct)}`);
74
+ }
75
+ lines.push("Balances:");
76
+ if (!report.balances?.length)
77
+ lines.push(" -");
78
+ for (const balance of report.balances ?? []) {
79
+ lines.push(` ${balanceText(balance)} [${balance.id}]`, ` Resets: ${value(balance.resetsAt)}`);
80
+ }
81
+ lines.push("Details:");
82
+ if (!report.details?.length)
83
+ lines.push(" -");
84
+ for (const detail of report.details ?? [])
85
+ lines.push(` ${detail.label}: ${detail.value}`);
86
+ }
87
+ else {
88
+ lines.push(`Reason: ${failureText(report)}`);
89
+ }
90
+ if (entry.loginErrors?.length) {
91
+ lines.push("Login failures:");
92
+ for (const login of entry.loginErrors)
93
+ lines.push(` ${login.harness}: ${failureText(login.report)}`);
94
+ }
95
+ return lines.join("\n");
96
+ }
97
+ export const usageSchema = {
98
+ idField: "id",
99
+ columns: [],
100
+ renderHuman(result, options) {
101
+ if (result.type === "single")
102
+ return inspect(result.data);
103
+ if (!result.data.length)
104
+ return "No usage reports returned";
105
+ const rows = [];
106
+ for (const entry of result.data) {
107
+ for (const [index, usage] of summary(entry).entries()) {
108
+ const first = index === 0;
109
+ rows.push({
110
+ id: first ? entry.id : "",
111
+ source: first ? entry.sourceLabel : "",
112
+ account: first ? value(entry.account.label) : "",
113
+ status: first ? entry.report.status : "",
114
+ usage,
115
+ fetched: first ? value(entry.fetchedAt) : "",
116
+ });
117
+ }
118
+ }
119
+ return renderTable({ type: "list", data: rows, schema: summarySchema }, options);
120
+ },
121
+ };
122
+ //# sourceMappingURL=presentation.js.map
@@ -13,6 +13,7 @@ export interface WorkspaceCreateOptions extends CommandOptions {
13
13
  branch?: string;
14
14
  prNumber?: string;
15
15
  forge?: string;
16
+ background?: boolean;
16
17
  }
17
18
  export declare function buildWorkspaceSource(options: WorkspaceCreateOptions): {
18
19
  projectId?: string | undefined;
@@ -1,4 +1,5 @@
1
1
  import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
2
+ import { resolveCallerAgentId } from "../../utils/caller-agent.js";
2
3
  import { toWorkspaceRow, workspaceSchema } from "./shared.js";
3
4
  function assertOptionsAbsent(values, message) {
4
5
  if (values.some((value) => value !== undefined)) {
@@ -94,9 +95,12 @@ export async function runCreateCommand(options, _command) {
94
95
  };
95
96
  });
96
97
  try {
98
+ const callerAgentId = await resolveCallerAgentId(client);
97
99
  const payload = await client.createWorkspace({
98
100
  source: buildWorkspaceSource(options),
99
101
  ...(options.title ? { title: options.title } : {}),
102
+ ...(options.background !== undefined ? { background: options.background } : {}),
103
+ ...(callerAgentId ? { callerAgentId } : {}),
100
104
  });
101
105
  if (!payload.workspace) {
102
106
  throw new Error(payload.error ?? "Workspace creation failed");
@@ -25,8 +25,13 @@ export function createWorkspaceCommand() {
25
25
  .option("--base <ref>", "Base ref (--mode branch-off)")
26
26
  .option("--branch <name>", "Existing branch (--mode checkout-branch)")
27
27
  .option("--pr-number <n>", "Pull request or change request number (--mode checkout-pr)")
28
- .option("--forge <forge>", "Forge for --mode checkout-pr (default: source checkout)")).action(withOutput(runCreateCommand));
29
- addJsonAndDaemonHostOptions(workspace.command("ls").description("List active workspaces")).action(withOutput(runLsCommand));
28
+ .option("--forge <forge>", "Forge for --mode checkout-pr (default: source checkout)")
29
+ .option("--background", "Create a background workspace")
30
+ .option("--no-background", "Create a visible workspace instead of inheriting background")).action(withOutput(runCreateCommand));
31
+ addJsonAndDaemonHostOptions(workspace
32
+ .command("ls")
33
+ .description("List active workspaces")
34
+ .option("--background", "Include background workspaces")).action(withOutput(runLsCommand));
30
35
  addJsonAndDaemonHostOptions(workspace
31
36
  .command("rename")
32
37
  .description("Set a workspace's user-visible title")