@camerontaylor/paseo-cli 0.11.2-fork.1 → 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.
- package/dist/cli.js +2 -0
- package/dist/commands/agent/logs.d.ts +19 -2
- package/dist/commands/agent/logs.js +143 -12
- package/dist/commands/agent/ls.d.ts +17 -2
- package/dist/commands/agent/ls.js +24 -6
- package/dist/commands/agent/run.d.ts +3 -14
- package/dist/commands/agent/run.js +20 -29
- package/dist/commands/agent/send.js +4 -1
- package/dist/commands/permit/allow.d.ts +17 -2
- package/dist/commands/permit/allow.js +96 -82
- package/dist/commands/permit/deny.d.ts +3 -2
- package/dist/commands/permit/deny.js +85 -71
- package/dist/commands/usage/index.d.ts +29 -0
- package/dist/commands/usage/index.js +62 -0
- package/dist/commands/usage/presentation.d.ts +4 -0
- package/dist/commands/usage/presentation.js +122 -0
- package/dist/commands/workspace/create.d.ts +1 -0
- package/dist/commands/workspace/create.js +4 -0
- package/dist/commands/workspace/index.js +7 -2
- package/dist/commands/workspace/ls.d.ts +5 -2
- package/dist/commands/workspace/ls.js +7 -2
- package/dist/commands/workspace/shared.d.ts +3 -0
- package/dist/commands/workspace/shared.js +9 -0
- package/dist/commands/worktree/archive.d.ts +1 -0
- package/dist/commands/worktree/archive.js +3 -1
- package/dist/commands/worktree/index.js +6 -2
- package/dist/commands/worktree/ls.d.ts +1 -0
- package/dist/commands/worktree/ls.js +3 -1
- package/dist/commands/worktree/repository-directory.d.ts +6 -0
- package/dist/commands/worktree/repository-directory.js +9 -0
- package/dist/utils/caller-agent.d.ts +14 -0
- package/dist/utils/caller-agent.js +16 -0
- package/dist/utils/timeline.d.ts +1 -0
- package/dist/utils/timeline.js +6 -1
- package/package.json +4 -4
|
@@ -30,95 +30,109 @@ export function toPermissionResponseItem(agentId, permission, result) {
|
|
|
30
30
|
result,
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
if (!
|
|
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: "
|
|
88
|
-
message: `
|
|
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
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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: "
|
|
33
|
-
message:
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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: "
|
|
49
|
-
message: `
|
|
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
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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,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)")
|
|
29
|
-
|
|
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")
|