@camerontaylor/paseo-cli 0.11.2-fork.2 → 0.12.0-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.d.ts +2 -0
- package/dist/commands/agent/send.js +8 -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 +146 -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,146 @@
|
|
|
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
|
+
if (entry.lastSuccessful) {
|
|
38
|
+
lines.push(`Last successful: ${entry.lastSuccessful.fetchedAt}`);
|
|
39
|
+
lines.push(...availableSummary(entry.lastSuccessful.report).map((line) => `Retained: ${line}`));
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
lines.push("No successful update");
|
|
43
|
+
return lines;
|
|
44
|
+
}
|
|
45
|
+
return [...availableSummary(report), `Last successful: ${successfulTime(entry)}`];
|
|
46
|
+
}
|
|
47
|
+
function availableSummary(report) {
|
|
48
|
+
const lines = report.windows.map(windowText);
|
|
49
|
+
lines.push(...(report.balances ?? []).map(balanceText));
|
|
50
|
+
return lines.length ? lines : ["-"];
|
|
51
|
+
}
|
|
52
|
+
const summarySchema = {
|
|
53
|
+
idField: "id",
|
|
54
|
+
columns: [
|
|
55
|
+
{ header: "REPORT ID", field: "id" },
|
|
56
|
+
{ header: "SOURCE", field: "source" },
|
|
57
|
+
{ header: "ACCOUNT", field: "account" },
|
|
58
|
+
{ header: "STATUS", field: "status" },
|
|
59
|
+
{ header: "USAGE", field: "usage" },
|
|
60
|
+
{ header: "FETCHED (LATEST ATTEMPT)", field: "fetched" },
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
function inspect(entry) {
|
|
64
|
+
const lines = [
|
|
65
|
+
`Report ID: ${entry.id}`,
|
|
66
|
+
`Source: ${entry.sourceLabel}`,
|
|
67
|
+
`Source ID: ${entry.sourceId}`,
|
|
68
|
+
`Account: ${value(entry.account.label)}`,
|
|
69
|
+
`Status: ${entry.report.status}`,
|
|
70
|
+
`Latest attempt: ${value(entry.fetchedAt)}`,
|
|
71
|
+
];
|
|
72
|
+
const report = entry.report.status === "available" ? entry.report : entry.lastSuccessful?.report;
|
|
73
|
+
const successfulAt = successfulTime(entry);
|
|
74
|
+
lines.push(successfulAt ? `Last successful: ${successfulAt}` : "No successful update");
|
|
75
|
+
if (entry.report.status !== "available")
|
|
76
|
+
lines.push(`Reason: ${failureText(entry.report)}`);
|
|
77
|
+
if (report) {
|
|
78
|
+
if (entry.report.status !== "available")
|
|
79
|
+
lines.push("Retained usage:");
|
|
80
|
+
lines.push(...availableDetails(report));
|
|
81
|
+
}
|
|
82
|
+
if (entry.loginErrors?.length) {
|
|
83
|
+
lines.push("Login failures:");
|
|
84
|
+
for (const login of entry.loginErrors)
|
|
85
|
+
lines.push(` ${login.harness}: ${failureText(login.report)}`);
|
|
86
|
+
}
|
|
87
|
+
return lines.join("\n");
|
|
88
|
+
}
|
|
89
|
+
export const usageSchema = {
|
|
90
|
+
idField: "id",
|
|
91
|
+
columns: [],
|
|
92
|
+
renderHuman(result, options) {
|
|
93
|
+
if (result.type === "single")
|
|
94
|
+
return inspect(result.data);
|
|
95
|
+
if (!result.data.length)
|
|
96
|
+
return "No usage reports returned";
|
|
97
|
+
const rows = [];
|
|
98
|
+
for (const entry of result.data) {
|
|
99
|
+
for (const [index, usage] of summary(entry).entries()) {
|
|
100
|
+
const first = index === 0;
|
|
101
|
+
rows.push({
|
|
102
|
+
id: first ? entry.id : "",
|
|
103
|
+
source: first ? entry.sourceLabel : "",
|
|
104
|
+
account: first ? value(entry.account.label) : "",
|
|
105
|
+
status: first ? entry.report.status : "",
|
|
106
|
+
usage,
|
|
107
|
+
fetched: first ? value(entry.fetchedAt) : "",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return renderTable({ type: "list", data: rows, schema: summarySchema }, options);
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
function availableDetails(report) {
|
|
115
|
+
const lines = [];
|
|
116
|
+
lines.push(`Plan: ${value(report.planLabel)}`, "Windows:");
|
|
117
|
+
if (!report.windows.length)
|
|
118
|
+
lines.push(" -");
|
|
119
|
+
for (const window of report.windows) {
|
|
120
|
+
lines.push(` ${windowText(window)} [${window.id}]`, ` Resets: ${value(window.resetsAt)}`);
|
|
121
|
+
if (window.runsOutAt != null)
|
|
122
|
+
lines.push(` Runs out: ${window.runsOutAt}`);
|
|
123
|
+
if (window.shortfallPct != null)
|
|
124
|
+
lines.push(` Shortfall: ${percent(window.shortfallPct)}`);
|
|
125
|
+
}
|
|
126
|
+
lines.push("Balances:");
|
|
127
|
+
if (!report.balances?.length)
|
|
128
|
+
lines.push(" -");
|
|
129
|
+
for (const balance of report.balances ?? []) {
|
|
130
|
+
lines.push(` ${balanceText(balance)} [${balance.id}]`, ` Resets: ${value(balance.resetsAt)}`);
|
|
131
|
+
}
|
|
132
|
+
lines.push("Details:");
|
|
133
|
+
if (!report.details?.length)
|
|
134
|
+
lines.push(" -");
|
|
135
|
+
for (const detail of report.details ?? [])
|
|
136
|
+
lines.push(` ${detail.label}: ${detail.value}`);
|
|
137
|
+
return lines;
|
|
138
|
+
}
|
|
139
|
+
function successfulTime(entry) {
|
|
140
|
+
// COMPAT(usageSuccessfulAt): added in v0.12.0, remove after 2027-04-17 once daemon floor >= v0.12.0.
|
|
141
|
+
// Only available reports from older daemons can use their attempt timestamp as freshness.
|
|
142
|
+
if (entry.report.status === "available")
|
|
143
|
+
return entry.successfulAt ?? entry.fetchedAt;
|
|
144
|
+
return entry.lastSuccessful?.fetchedAt;
|
|
145
|
+
}
|
|
146
|
+
//# 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");
|