@bbigbang/cli 0.1.0
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/cliSupport.d.ts +166 -0
- package/dist/cliSupport.js +1306 -0
- package/dist/commandCatalog.d.ts +36 -0
- package/dist/commandCatalog.js +152 -0
- package/dist/commands/actionCommands.d.ts +3 -0
- package/dist/commands/actionCommands.js +43 -0
- package/dist/commands/authManualCommands.d.ts +3 -0
- package/dist/commands/authManualCommands.js +60 -0
- package/dist/commands/channelWorkspaceCommands.d.ts +3 -0
- package/dist/commands/channelWorkspaceCommands.js +105 -0
- package/dist/commands/contextCommands.d.ts +3 -0
- package/dist/commands/contextCommands.js +253 -0
- package/dist/commands/memoryCommands.d.ts +3 -0
- package/dist/commands/memoryCommands.js +154 -0
- package/dist/commands/messageCommands.d.ts +3 -0
- package/dist/commands/messageCommands.js +241 -0
- package/dist/commands/panelCommands.d.ts +3 -0
- package/dist/commands/panelCommands.js +218 -0
- package/dist/commands/reminderCommands.d.ts +3 -0
- package/dist/commands/reminderCommands.js +220 -0
- package/dist/commands/skillToolAttachmentCommands.d.ts +3 -0
- package/dist/commands/skillToolAttachmentCommands.js +261 -0
- package/dist/commands/taskCommands.d.ts +3 -0
- package/dist/commands/taskCommands.js +195 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/manual/generated/command-catalog.json +12452 -0
- package/dist/manual/topics/cli-overview.md +116 -0
- package/dist/manual/topics/commands.md +706 -0
- package/dist/manual/topics/examples.md +194 -0
- package/dist/manual/topics/index.md +11 -0
- package/dist/program.d.ts +5 -0
- package/dist/program.js +52 -0
- package/package.json +43 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { AGENT_COMMAND_ERROR_CODES, AgentCommandError, appendReminderConversation, createRuntime, normalizeOptionalReminderScheduleKind, normalizeReminderIntervalUnit, normalizeReminderScheduleKind, parseJsonObjectStringOrThrow, parseNumber, parsePositiveInteger, parseRequiredNumber, parseSnoozeDuration, readOptionalStdin, requireConversationId, requireString, responseError, summarizeReminderAction, summarizeReminderCurrent, summarizeReminderList, summarizeReminderLog, summarizeReminderOccurrences, summarizeReminderSnooze, writeOutput } from '../cliSupport.js';
|
|
2
|
+
export function registerReminderCommands(program, options) {
|
|
3
|
+
const reminder = program.command('reminder').description('Reminder operations for the current DM scope');
|
|
4
|
+
reminder.command('list')
|
|
5
|
+
.description('List reminders in the current DM scope')
|
|
6
|
+
.action(async () => {
|
|
7
|
+
const runtime = createRuntime(program, options);
|
|
8
|
+
const params = new URLSearchParams();
|
|
9
|
+
params.set('conversationId', requireConversationId(runtime, 'reminder list'));
|
|
10
|
+
const response = await runtime.client.request('GET', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminders?${params.toString()}`);
|
|
11
|
+
if (!response.ok)
|
|
12
|
+
throw responseError(response, 'list reminders failed');
|
|
13
|
+
writeOutput(runtime, summarizeReminderList(response.data), response.data);
|
|
14
|
+
});
|
|
15
|
+
reminder.command('create')
|
|
16
|
+
.description('Create a reminder from flags or stdin JSON')
|
|
17
|
+
.option('--title <title>', 'Reminder title')
|
|
18
|
+
.option('--prompt-text <text>', 'Reminder task brief')
|
|
19
|
+
.option('--schedule-kind <kind>', 'one_time or recurring')
|
|
20
|
+
.option('--start-at <ms>', 'First trigger time as unix milliseconds')
|
|
21
|
+
.option('--interval-unit <unit>', 'Recurring interval unit: hour, day, week')
|
|
22
|
+
.option('--interval-value <n>', 'Recurring interval value')
|
|
23
|
+
.option('--end-at <ms>', 'Optional end time as unix milliseconds')
|
|
24
|
+
.option('--max-occurrences <n>', 'Optional recurring occurrence cap')
|
|
25
|
+
.action(async (opts) => {
|
|
26
|
+
const runtime = createRuntime(program, options);
|
|
27
|
+
const stdin = (await readOptionalStdin(runtime.io.stdin)) ?? '';
|
|
28
|
+
const body = stdin
|
|
29
|
+
? parseJsonObjectStringOrThrow(stdin, 'reminder create JSON payload')
|
|
30
|
+
: {
|
|
31
|
+
title: requireString(opts.title, '--title'),
|
|
32
|
+
promptText: requireString(opts.promptText, '--prompt-text'),
|
|
33
|
+
scheduleKind: normalizeReminderScheduleKind(opts.scheduleKind),
|
|
34
|
+
startAt: parseRequiredNumber('start-at', opts.startAt),
|
|
35
|
+
...(normalizeReminderIntervalUnit(opts.intervalUnit) ? { intervalUnit: normalizeReminderIntervalUnit(opts.intervalUnit) } : {}),
|
|
36
|
+
...(parsePositiveInteger('interval-value', opts.intervalValue) !== undefined ? { intervalValue: parsePositiveInteger('interval-value', opts.intervalValue) } : {}),
|
|
37
|
+
...(parseNumber('end-at', opts.endAt) !== undefined ? { endAt: parseNumber('end-at', opts.endAt) } : {}),
|
|
38
|
+
...(parsePositiveInteger('max-occurrences', opts.maxOccurrences) !== undefined ? { maxOccurrences: parsePositiveInteger('max-occurrences', opts.maxOccurrences) } : {}),
|
|
39
|
+
};
|
|
40
|
+
const response = await runtime.client.request('POST', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminders`, appendReminderConversation(runtime, 'reminder create', body));
|
|
41
|
+
if (!response.ok)
|
|
42
|
+
throw responseError(response, 'create reminder failed');
|
|
43
|
+
writeOutput(runtime, summarizeReminderAction('Created', response.data), response.data);
|
|
44
|
+
});
|
|
45
|
+
reminder.command('update')
|
|
46
|
+
.description('Update a reminder in the current DM scope from flags or stdin JSON')
|
|
47
|
+
.requiredOption('--reminder-id <id>', 'Reminder id')
|
|
48
|
+
.option('--title <title>', 'New title')
|
|
49
|
+
.option('--prompt-text <text>', 'New task brief')
|
|
50
|
+
.option('--schedule-kind <kind>', 'one_time or recurring')
|
|
51
|
+
.option('--start-at <ms>', 'New start time as unix milliseconds')
|
|
52
|
+
.option('--interval-unit <unit>', 'Recurring interval unit: hour, day, week')
|
|
53
|
+
.option('--interval-value <n>', 'Recurring interval value')
|
|
54
|
+
.option('--end-at <ms>', 'Optional end time as unix milliseconds')
|
|
55
|
+
.option('--max-occurrences <n>', 'Optional recurring occurrence cap')
|
|
56
|
+
.action(async (opts) => {
|
|
57
|
+
const runtime = createRuntime(program, options);
|
|
58
|
+
const reminderId = requireString(opts.reminderId, '--reminder-id');
|
|
59
|
+
const stdin = (await readOptionalStdin(runtime.io.stdin)) ?? '';
|
|
60
|
+
const body = stdin
|
|
61
|
+
? parseJsonObjectStringOrThrow(stdin, 'reminder update JSON payload')
|
|
62
|
+
: {};
|
|
63
|
+
if (!stdin) {
|
|
64
|
+
if (opts.title !== undefined)
|
|
65
|
+
body.title = String(opts.title);
|
|
66
|
+
if (opts.promptText !== undefined)
|
|
67
|
+
body.promptText = String(opts.promptText);
|
|
68
|
+
const scheduleKind = normalizeOptionalReminderScheduleKind(opts.scheduleKind);
|
|
69
|
+
if (scheduleKind !== undefined)
|
|
70
|
+
body.scheduleKind = scheduleKind;
|
|
71
|
+
const startAt = parseNumber('start-at', opts.startAt);
|
|
72
|
+
if (startAt !== undefined)
|
|
73
|
+
body.startAt = startAt;
|
|
74
|
+
const intervalUnit = normalizeReminderIntervalUnit(opts.intervalUnit);
|
|
75
|
+
if (intervalUnit !== undefined)
|
|
76
|
+
body.intervalUnit = intervalUnit;
|
|
77
|
+
const intervalValue = parsePositiveInteger('interval-value', opts.intervalValue);
|
|
78
|
+
if (intervalValue !== undefined)
|
|
79
|
+
body.intervalValue = intervalValue;
|
|
80
|
+
const endAt = parseNumber('end-at', opts.endAt);
|
|
81
|
+
if (endAt !== undefined)
|
|
82
|
+
body.endAt = endAt;
|
|
83
|
+
const maxOccurrences = parsePositiveInteger('max-occurrences', opts.maxOccurrences);
|
|
84
|
+
if (maxOccurrences !== undefined)
|
|
85
|
+
body.maxOccurrences = maxOccurrences;
|
|
86
|
+
}
|
|
87
|
+
const response = await runtime.client.request('PATCH', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminders/${encodeURIComponent(reminderId)}`, appendReminderConversation(runtime, 'reminder update', body));
|
|
88
|
+
if (!response.ok)
|
|
89
|
+
throw responseError(response, 'update reminder failed');
|
|
90
|
+
writeOutput(runtime, summarizeReminderAction('Updated', response.data, reminderId), response.data);
|
|
91
|
+
});
|
|
92
|
+
const simpleReminderAction = (name, routeSuffix, label) => {
|
|
93
|
+
reminder.command(name)
|
|
94
|
+
.description(`${label} a reminder in the current DM scope`)
|
|
95
|
+
.requiredOption('--reminder-id <id>', 'Reminder id')
|
|
96
|
+
.action(async (opts) => {
|
|
97
|
+
const runtime = createRuntime(program, options);
|
|
98
|
+
const reminderId = requireString(opts.reminderId, '--reminder-id');
|
|
99
|
+
const response = await runtime.client.request('POST', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminders/${encodeURIComponent(reminderId)}/${routeSuffix}`, appendReminderConversation(runtime, `reminder ${name}`));
|
|
100
|
+
if (!response.ok)
|
|
101
|
+
throw responseError(response, `${name} reminder failed`);
|
|
102
|
+
writeOutput(runtime, summarizeReminderAction(label, response.data, reminderId), response.data);
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
simpleReminderAction('pause', 'pause', 'Paused');
|
|
106
|
+
simpleReminderAction('resume', 'resume', 'Resumed');
|
|
107
|
+
simpleReminderAction('cancel', 'cancel', 'Cancelled');
|
|
108
|
+
simpleReminderAction('run-now', 'run-now', 'Ran');
|
|
109
|
+
simpleReminderAction('abandon', 'abandon', 'Abandoned');
|
|
110
|
+
reminder.command('occurrences')
|
|
111
|
+
.description('List recent occurrences for one reminder')
|
|
112
|
+
.requiredOption('--reminder-id <id>', 'Reminder id')
|
|
113
|
+
.action(async (opts) => {
|
|
114
|
+
const runtime = createRuntime(program, options);
|
|
115
|
+
const reminderId = requireString(opts.reminderId, '--reminder-id');
|
|
116
|
+
const params = new URLSearchParams();
|
|
117
|
+
params.set('conversationId', requireConversationId(runtime, 'reminder occurrences'));
|
|
118
|
+
const response = await runtime.client.request('GET', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminders/${encodeURIComponent(reminderId)}/occurrences?${params.toString()}`);
|
|
119
|
+
if (!response.ok)
|
|
120
|
+
throw responseError(response, 'list reminder occurrences failed');
|
|
121
|
+
writeOutput(runtime, summarizeReminderOccurrences(response.data), response.data);
|
|
122
|
+
});
|
|
123
|
+
reminder.command('current')
|
|
124
|
+
.description('Read current and latest occurrence for one reminder')
|
|
125
|
+
.requiredOption('--reminder-id <id>', 'Reminder id')
|
|
126
|
+
.action(async (opts) => {
|
|
127
|
+
const runtime = createRuntime(program, options);
|
|
128
|
+
const reminderId = requireString(opts.reminderId, '--reminder-id');
|
|
129
|
+
const params = new URLSearchParams();
|
|
130
|
+
params.set('conversationId', requireConversationId(runtime, 'reminder current'));
|
|
131
|
+
const response = await runtime.client.request('GET', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminders/${encodeURIComponent(reminderId)}/current-occurrence?${params.toString()}`);
|
|
132
|
+
if (!response.ok)
|
|
133
|
+
throw responseError(response, 'get current reminder occurrence failed');
|
|
134
|
+
writeOutput(runtime, summarizeReminderCurrent(response.data), response.data);
|
|
135
|
+
});
|
|
136
|
+
reminder.command('submit-for-review')
|
|
137
|
+
.description('Submit a one-time reminder occurrence for user review')
|
|
138
|
+
.option('--occurrence-id <id>', 'Occurrence id')
|
|
139
|
+
.option('--reminder-id <id>', 'Reminder id; submits current open occurrence when occurrence id is omitted')
|
|
140
|
+
.action(async (opts) => {
|
|
141
|
+
const runtime = createRuntime(program, options);
|
|
142
|
+
const occurrenceId = typeof opts.occurrenceId === 'string' ? opts.occurrenceId.trim() : '';
|
|
143
|
+
const reminderId = typeof opts.reminderId === 'string' ? opts.reminderId.trim() : '';
|
|
144
|
+
if (!occurrenceId && !reminderId) {
|
|
145
|
+
throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, 'Provide --occurrence-id or --reminder-id.');
|
|
146
|
+
}
|
|
147
|
+
const path = occurrenceId
|
|
148
|
+
? `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminder-occurrences/${encodeURIComponent(occurrenceId)}/submit-for-review`
|
|
149
|
+
: `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminders/${encodeURIComponent(reminderId)}/submit-current-for-review`;
|
|
150
|
+
const response = await runtime.client.request('POST', path, appendReminderConversation(runtime, 'reminder submit-for-review'));
|
|
151
|
+
if (!response.ok)
|
|
152
|
+
throw responseError(response, 'submit reminder occurrence for review failed');
|
|
153
|
+
writeOutput(runtime, summarizeReminderAction('Submitted for review', response.data, occurrenceId || reminderId), response.data);
|
|
154
|
+
});
|
|
155
|
+
reminder.command('complete')
|
|
156
|
+
.description('Complete a recurring reminder occurrence; one-time reminders should be submitted for review and completed by the user')
|
|
157
|
+
.option('--occurrence-id <id>', 'Occurrence id')
|
|
158
|
+
.option('--reminder-id <id>', 'Reminder id; completes current open occurrence when occurrence id is omitted')
|
|
159
|
+
.action(async (opts) => {
|
|
160
|
+
const runtime = createRuntime(program, options);
|
|
161
|
+
const occurrenceId = typeof opts.occurrenceId === 'string' ? opts.occurrenceId.trim() : '';
|
|
162
|
+
const reminderId = typeof opts.reminderId === 'string' ? opts.reminderId.trim() : '';
|
|
163
|
+
if (!occurrenceId && !reminderId) {
|
|
164
|
+
throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, 'Provide --occurrence-id or --reminder-id.');
|
|
165
|
+
}
|
|
166
|
+
const path = occurrenceId
|
|
167
|
+
? `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminder-occurrences/${encodeURIComponent(occurrenceId)}/complete`
|
|
168
|
+
: `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminders/${encodeURIComponent(reminderId)}/complete-current`;
|
|
169
|
+
const response = await runtime.client.request('POST', path, appendReminderConversation(runtime, 'reminder complete'));
|
|
170
|
+
if (!response.ok)
|
|
171
|
+
throw responseError(response, 'complete reminder occurrence failed');
|
|
172
|
+
writeOutput(runtime, summarizeReminderAction('Completed', response.data, occurrenceId || reminderId), response.data);
|
|
173
|
+
});
|
|
174
|
+
reminder.command('log')
|
|
175
|
+
.description('Read the lifecycle event log for a reminder')
|
|
176
|
+
.requiredOption('--reminder-id <id>', 'Reminder id')
|
|
177
|
+
.option('--limit <n>', 'Maximum events to return')
|
|
178
|
+
.action(async (opts) => {
|
|
179
|
+
const runtime = createRuntime(program, options);
|
|
180
|
+
const reminderId = requireString(opts.reminderId, '--reminder-id');
|
|
181
|
+
const params = new URLSearchParams();
|
|
182
|
+
const limit = parsePositiveInteger('limit', opts.limit);
|
|
183
|
+
if (limit !== undefined)
|
|
184
|
+
params.set('limit', String(limit));
|
|
185
|
+
const qs = params.toString();
|
|
186
|
+
const response = await runtime.client.request('GET', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminders/${encodeURIComponent(reminderId)}/log${qs ? `?${qs}` : ''}`);
|
|
187
|
+
if (!response.ok)
|
|
188
|
+
throw responseError(response, 'read reminder log failed');
|
|
189
|
+
writeOutput(runtime, summarizeReminderLog(reminderId, response.data), response.data);
|
|
190
|
+
});
|
|
191
|
+
reminder.command('snooze')
|
|
192
|
+
.description('Snooze a reminder occurrence by a relative duration')
|
|
193
|
+
.requiredOption('--occurrence-id <id>', 'Occurrence id')
|
|
194
|
+
.requiredOption('--by <duration>', 'Duration until next run: 30m, 2h, 1d')
|
|
195
|
+
.action(async (opts) => {
|
|
196
|
+
const runtime = createRuntime(program, options);
|
|
197
|
+
const occurrenceId = requireString(opts.occurrenceId, '--occurrence-id');
|
|
198
|
+
const { until } = parseSnoozeDuration(opts.by);
|
|
199
|
+
const response = await runtime.client.request('POST', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminder-occurrences/${encodeURIComponent(occurrenceId)}/snooze`, appendReminderConversation(runtime, 'reminder snooze', { until }));
|
|
200
|
+
if (!response.ok)
|
|
201
|
+
throw responseError(response, 'snooze reminder occurrence failed');
|
|
202
|
+
writeOutput(runtime, summarizeReminderSnooze(response.data), response.data);
|
|
203
|
+
});
|
|
204
|
+
reminder.command('skip')
|
|
205
|
+
.description('Skip a reminder occurrence')
|
|
206
|
+
.requiredOption('--occurrence-id <id>', 'Occurrence id')
|
|
207
|
+
.option('--reason <reason>', 'Optional short reason to store with the skip event')
|
|
208
|
+
.action(async (opts) => {
|
|
209
|
+
const runtime = createRuntime(program, options);
|
|
210
|
+
const occurrenceId = requireString(opts.occurrenceId, '--occurrence-id');
|
|
211
|
+
const body = {};
|
|
212
|
+
if (typeof opts.reason === 'string' && opts.reason.trim().length > 0) {
|
|
213
|
+
body.reason = opts.reason.trim();
|
|
214
|
+
}
|
|
215
|
+
const response = await runtime.client.request('POST', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/reminder-occurrences/${encodeURIComponent(occurrenceId)}/skip`, appendReminderConversation(runtime, 'reminder skip', body));
|
|
216
|
+
if (!response.ok)
|
|
217
|
+
throw responseError(response, 'skip reminder occurrence failed');
|
|
218
|
+
writeOutput(runtime, summarizeReminderAction('Skipped', response.data, occurrenceId), response.data);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { basename } from 'node:path';
|
|
3
|
+
import { AGENT_COMMAND_ERROR_CODES, AgentCommandError, ATTACHMENT_UPLOAD_MAX_BYTES, MUTATION_HINTS, appendCommonBody, appendRunContextParams, createRuntime, fetchBinaryRaw, fetchJsonRaw, formatSize, inferMimeType, parseJsonObjectStringOrThrow, parsePositiveInteger, readOptionalStdin, requireString, responseError, summarizeAttachmentComments, summarizeAttachmentUpload, summarizeAttachmentView, summarizeSkillFile, summarizeSkillList, summarizeToolAction, summarizeToolPublish, summarizeToolStatus, summarizeToolValidate, summarizeToolVerification, withHint, writeOutput } from '../cliSupport.js';
|
|
4
|
+
export function registerSkillToolAttachmentCommands(program, options) {
|
|
5
|
+
const skill = program.command('skill').description('Agent skill operations');
|
|
6
|
+
skill.command('list')
|
|
7
|
+
.description('List configured skills or entries under a skill directory')
|
|
8
|
+
.option('--path <path>', 'Absolute skill directory path under a configured skill root')
|
|
9
|
+
.action(async (opts) => {
|
|
10
|
+
const runtime = createRuntime(program, options);
|
|
11
|
+
const params = new URLSearchParams();
|
|
12
|
+
if (opts.path)
|
|
13
|
+
params.set('path', requireString(opts.path, '--path'));
|
|
14
|
+
const qs = params.toString();
|
|
15
|
+
const response = await runtime.client.request('GET', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/skills${qs ? `?${qs}` : ''}`);
|
|
16
|
+
if (!response.ok)
|
|
17
|
+
throw responseError(response, 'list skills failed');
|
|
18
|
+
writeOutput(runtime, summarizeSkillList(response.data), response.data);
|
|
19
|
+
});
|
|
20
|
+
skill.command('read')
|
|
21
|
+
.description('Read a text skill file')
|
|
22
|
+
.requiredOption('--path <path>', 'Absolute skill file path under a configured skill root')
|
|
23
|
+
.action(async (opts) => {
|
|
24
|
+
const runtime = createRuntime(program, options);
|
|
25
|
+
const params = new URLSearchParams();
|
|
26
|
+
params.set('path', requireString(opts.path, '--path'));
|
|
27
|
+
const response = await runtime.client.request('GET', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/skills/file?${params.toString()}`);
|
|
28
|
+
if (!response.ok)
|
|
29
|
+
throw responseError(response, 'read skill file failed');
|
|
30
|
+
writeOutput(runtime, summarizeSkillFile(response.data), response.data);
|
|
31
|
+
});
|
|
32
|
+
const tool = program.command('tool').description('Workspace Tool operations');
|
|
33
|
+
tool.command('publish')
|
|
34
|
+
.description('Publish a Workspace Tool manifest from the current agent workspace')
|
|
35
|
+
.requiredOption('--manifest-path <path>', 'Workspace-relative path to tool.json')
|
|
36
|
+
.option('--source-message-id <id>', 'Source message id to associate with the published tool')
|
|
37
|
+
.action(async (opts) => {
|
|
38
|
+
const runtime = createRuntime(program, options);
|
|
39
|
+
const manifestPath = requireString(opts.manifestPath, '--manifest-path');
|
|
40
|
+
const stdin = (await readOptionalStdin(runtime.io.stdin)) ?? '';
|
|
41
|
+
const body = {
|
|
42
|
+
manifestPath,
|
|
43
|
+
};
|
|
44
|
+
if (stdin.trim()) {
|
|
45
|
+
body.manifestContent = stdin;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
try {
|
|
49
|
+
const manifestContent = runtime.readFile(manifestPath);
|
|
50
|
+
if (manifestContent.trim())
|
|
51
|
+
body.manifestContent = manifestContent;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Core can still attempt the workspace read; this preserves legacy path-only behavior.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (opts.sourceMessageId)
|
|
58
|
+
body.sourceMessageId = opts.sourceMessageId;
|
|
59
|
+
const response = await runtime.client.request('POST', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/tools/publish`, appendCommonBody(runtime, body));
|
|
60
|
+
if (!response.ok)
|
|
61
|
+
throw responseError(response, 'workspace tool publish failed');
|
|
62
|
+
writeOutput(runtime, withHint(summarizeToolPublish(response.data), MUTATION_HINTS.toolPublish), response.data);
|
|
63
|
+
});
|
|
64
|
+
tool.command('validate')
|
|
65
|
+
.description('Validate a Workspace Tool manifest without publishing')
|
|
66
|
+
.requiredOption('--manifest-path <path>', 'Workspace-relative path to tool.json')
|
|
67
|
+
.action(async (opts) => {
|
|
68
|
+
const runtime = createRuntime(program, options);
|
|
69
|
+
const manifestPath = requireString(opts.manifestPath, '--manifest-path');
|
|
70
|
+
const stdin = (await readOptionalStdin(runtime.io.stdin)) ?? '';
|
|
71
|
+
const body = {
|
|
72
|
+
manifestPath,
|
|
73
|
+
};
|
|
74
|
+
if (stdin.trim()) {
|
|
75
|
+
body.manifestContent = stdin;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
try {
|
|
79
|
+
const manifestContent = runtime.readFile(manifestPath);
|
|
80
|
+
if (manifestContent.trim())
|
|
81
|
+
body.manifestContent = manifestContent;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Core can still attempt the workspace read; this preserves legacy path-only behavior.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const response = await runtime.client.request('POST', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/tools/publish?dryRun=true`, appendCommonBody(runtime, body));
|
|
88
|
+
if (!response.ok)
|
|
89
|
+
throw responseError(response, 'workspace tool validate failed');
|
|
90
|
+
const record = response.data && typeof response.data === 'object' ? response.data : {};
|
|
91
|
+
const isValid = record.valid === true;
|
|
92
|
+
writeOutput(runtime, summarizeToolValidate(response.data), response.data);
|
|
93
|
+
if (!isValid) {
|
|
94
|
+
throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_INPUT, 'Workspace tool manifest validation failed.');
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
tool.command('status')
|
|
98
|
+
.description('Read Workspace Tool runtime and revision status')
|
|
99
|
+
.requiredOption('--tool-id <id>', 'Workspace Tool id')
|
|
100
|
+
.option('--include-runs', 'Include recent run history')
|
|
101
|
+
.option('--run-limit <n>', 'Recent run limit when --include-runs is used')
|
|
102
|
+
.action(async (opts) => {
|
|
103
|
+
const runtime = createRuntime(program, options);
|
|
104
|
+
const params = appendRunContextParams(runtime, new URLSearchParams());
|
|
105
|
+
if (opts.includeRuns)
|
|
106
|
+
params.set('include_runs', 'true');
|
|
107
|
+
const runLimit = parsePositiveInteger('run-limit', opts.runLimit);
|
|
108
|
+
if (runLimit !== undefined)
|
|
109
|
+
params.set('run_limit', String(Math.min(runLimit, 20)));
|
|
110
|
+
const toolId = requireString(opts.toolId, '--tool-id');
|
|
111
|
+
const qs = params.toString();
|
|
112
|
+
const response = await runtime.client.request('GET', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/tools/${encodeURIComponent(toolId)}/status${qs ? `?${qs}` : ''}`);
|
|
113
|
+
if (!response.ok)
|
|
114
|
+
throw responseError(response, 'workspace tool status failed');
|
|
115
|
+
writeOutput(runtime, summarizeToolStatus(response.data), response.data);
|
|
116
|
+
});
|
|
117
|
+
tool.command('run')
|
|
118
|
+
.description('Run a Workspace Tool action')
|
|
119
|
+
.requiredOption('--tool-id <id>', 'Workspace Tool id')
|
|
120
|
+
.requiredOption('--action-id <id>', 'Action id from the tool manifest')
|
|
121
|
+
.option('--params-json <json>', 'Action params as a JSON object; stdin JSON object takes precedence')
|
|
122
|
+
.action(async (opts) => {
|
|
123
|
+
const runtime = createRuntime(program, options);
|
|
124
|
+
const toolId = requireString(opts.toolId, '--tool-id');
|
|
125
|
+
const actionId = requireString(opts.actionId, '--action-id');
|
|
126
|
+
const stdin = (await readOptionalStdin(runtime.io.stdin)) ?? '';
|
|
127
|
+
const params = stdin.trim()
|
|
128
|
+
? parseJsonObjectStringOrThrow(stdin, 'tool action params JSON payload')
|
|
129
|
+
: opts.paramsJson
|
|
130
|
+
? parseJsonObjectStringOrThrow(String(opts.paramsJson), '--params-json')
|
|
131
|
+
: {};
|
|
132
|
+
const response = await runtime.client.request('POST', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/tools/${encodeURIComponent(toolId)}/actions/${encodeURIComponent(actionId)}`, appendCommonBody(runtime, { params }));
|
|
133
|
+
if (!response.ok)
|
|
134
|
+
throw responseError(response, 'workspace tool action failed');
|
|
135
|
+
writeOutput(runtime, summarizeToolAction(response.data, actionId), response.data);
|
|
136
|
+
});
|
|
137
|
+
tool.command('verify')
|
|
138
|
+
.description('Write back target-machine verification for an installed Workspace Tool')
|
|
139
|
+
.requiredOption('--tool-id <id>', 'Workspace Tool id')
|
|
140
|
+
.requiredOption('--status <status>', 'Verification status: verified, warning, or failed')
|
|
141
|
+
.requiredOption('--summary <text>', 'Short verification summary')
|
|
142
|
+
.option('--details-json <json>', 'Additional verification details as a JSON object')
|
|
143
|
+
.option('--smoke-run-id <id>', 'Workspace Tool run id from a smoke action, when available')
|
|
144
|
+
.action(async (opts) => {
|
|
145
|
+
const runtime = createRuntime(program, options);
|
|
146
|
+
const toolId = requireString(opts.toolId, '--tool-id');
|
|
147
|
+
const status = requireString(opts.status, '--status');
|
|
148
|
+
const summary = requireString(opts.summary, '--summary');
|
|
149
|
+
const details = opts.detailsJson
|
|
150
|
+
? parseJsonObjectStringOrThrow(String(opts.detailsJson), '--details-json')
|
|
151
|
+
: undefined;
|
|
152
|
+
const body = { status, summary };
|
|
153
|
+
if (details)
|
|
154
|
+
body.details = details;
|
|
155
|
+
if (opts.smokeRunId)
|
|
156
|
+
body.smokeRunId = requireString(opts.smokeRunId, '--smoke-run-id');
|
|
157
|
+
const response = await runtime.client.request('POST', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/tools/${encodeURIComponent(toolId)}/verify`, appendCommonBody(runtime, body));
|
|
158
|
+
if (!response.ok)
|
|
159
|
+
throw responseError(response, 'workspace tool verification failed');
|
|
160
|
+
writeOutput(runtime, summarizeToolVerification(response.data), response.data);
|
|
161
|
+
});
|
|
162
|
+
const attachment = program.command('attachment').description('Attachment upload and view helpers');
|
|
163
|
+
attachment.command('upload')
|
|
164
|
+
.description('Upload a local file as a platform attachment')
|
|
165
|
+
.requiredOption('--file <path>', 'Local file path visible to the agent runtime')
|
|
166
|
+
.option('--channel <target>', 'Optional channel target override such as #general or a raw channel id')
|
|
167
|
+
.action(async (opts) => {
|
|
168
|
+
const runtime = createRuntime(program, options);
|
|
169
|
+
const filePath = requireString(opts.file, '--file');
|
|
170
|
+
const channel = typeof opts.channel === 'string' ? opts.channel.trim() : '';
|
|
171
|
+
if (!channel && !runtime.context.conversationId) {
|
|
172
|
+
throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.MISSING_ATTACHMENT_SCOPE, 'attachment upload requires BIGBANG_CONVERSATION_ID or --channel.', { suggestedNextAction: 'Run from a managed conversation runtime or pass --channel for a channel-scoped upload.' });
|
|
173
|
+
}
|
|
174
|
+
let size = 0;
|
|
175
|
+
try {
|
|
176
|
+
size = statSync(filePath).size;
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.FILE_UNREADABLE, `Attachment file is not readable: ${filePath}`, { cause: error, suggestedNextAction: 'Check the file path from the agent workspace and retry.' });
|
|
180
|
+
}
|
|
181
|
+
if (size > ATTACHMENT_UPLOAD_MAX_BYTES) {
|
|
182
|
+
throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.FILE_TOO_LARGE, `Attachment file exceeds ${formatSize(ATTACHMENT_UPLOAD_MAX_BYTES)}: ${formatSize(size)}.`, { suggestedNextAction: 'Upload a smaller file or write a summary/artifact path instead.' });
|
|
183
|
+
}
|
|
184
|
+
let fileBuffer;
|
|
185
|
+
try {
|
|
186
|
+
fileBuffer = readFileSync(filePath);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.FILE_UNREADABLE, `Attachment file is not readable: ${filePath}`, { cause: error, suggestedNextAction: 'Check the file path from the agent workspace and retry.' });
|
|
190
|
+
}
|
|
191
|
+
const mimeType = inferMimeType(filePath);
|
|
192
|
+
const form = new FormData();
|
|
193
|
+
if (channel) {
|
|
194
|
+
form.append('channelId', channel);
|
|
195
|
+
}
|
|
196
|
+
else if (runtime.context.conversationId) {
|
|
197
|
+
form.append('conversationId', runtime.context.conversationId);
|
|
198
|
+
}
|
|
199
|
+
form.append('file', new Blob([new Uint8Array(fileBuffer)], { type: mimeType }), basename(filePath));
|
|
200
|
+
const response = await fetchJsonRaw(runtime, `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/assets/upload`, {
|
|
201
|
+
method: 'POST',
|
|
202
|
+
body: form,
|
|
203
|
+
});
|
|
204
|
+
if (!response.ok)
|
|
205
|
+
throw responseError(response, 'attachment upload failed');
|
|
206
|
+
writeOutput(runtime, withHint(summarizeAttachmentUpload(response.data), MUTATION_HINTS.attachmentUpload), response.data);
|
|
207
|
+
});
|
|
208
|
+
attachment.command('view')
|
|
209
|
+
.description('Download and preview a platform attachment')
|
|
210
|
+
.requiredOption('--attachment-id <id>', 'Attachment id returned by upload or shown in a message')
|
|
211
|
+
.option('--channel <target>', 'Optional channel target override for channel-scoped attachments')
|
|
212
|
+
.action(async (opts) => {
|
|
213
|
+
const runtime = createRuntime(program, options);
|
|
214
|
+
const attachmentId = requireString(opts.attachmentId, '--attachment-id');
|
|
215
|
+
const channel = typeof opts.channel === 'string' ? opts.channel.trim() : '';
|
|
216
|
+
if (!channel && !runtime.context.conversationId) {
|
|
217
|
+
throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.MISSING_ATTACHMENT_SCOPE, 'attachment view requires BIGBANG_CONVERSATION_ID or --channel for access checks.', { suggestedNextAction: 'Run from a managed conversation runtime that can access the attachment, or pass --channel for a channel-scoped attachment.' });
|
|
218
|
+
}
|
|
219
|
+
const query = new URLSearchParams({
|
|
220
|
+
agentId: runtime.context.agentId,
|
|
221
|
+
});
|
|
222
|
+
if (channel)
|
|
223
|
+
query.set('channelId', channel);
|
|
224
|
+
else if (runtime.context.conversationId)
|
|
225
|
+
query.set('conversationId', runtime.context.conversationId);
|
|
226
|
+
const metadataResponse = await fetchJsonRaw(runtime, `/api/assets/${encodeURIComponent(attachmentId)}/meta?${query.toString()}`);
|
|
227
|
+
if (!metadataResponse.ok)
|
|
228
|
+
throw responseError(metadataResponse, 'attachment metadata fetch failed');
|
|
229
|
+
const binaryResponse = await fetchBinaryRaw(runtime, `/api/assets/${encodeURIComponent(attachmentId)}?${query.toString()}`);
|
|
230
|
+
if (!binaryResponse.ok) {
|
|
231
|
+
throw new AgentCommandError(binaryResponse.status >= 500 ? AGENT_COMMAND_ERROR_CODES.SERVER_5XX : AGENT_COMMAND_ERROR_CODES.REQUEST_FAILED, `attachment download failed: ${binaryResponse.error}`, { status: binaryResponse.status });
|
|
232
|
+
}
|
|
233
|
+
const metadata = metadataResponse.data && typeof metadataResponse.data === 'object'
|
|
234
|
+
? metadataResponse.data
|
|
235
|
+
: {};
|
|
236
|
+
const summary = summarizeAttachmentView({
|
|
237
|
+
attachmentId,
|
|
238
|
+
metadata,
|
|
239
|
+
buffer: binaryResponse.buffer,
|
|
240
|
+
contentType: binaryResponse.contentType,
|
|
241
|
+
});
|
|
242
|
+
writeOutput(runtime, summary.text, summary.data);
|
|
243
|
+
});
|
|
244
|
+
attachment.command('comments')
|
|
245
|
+
.description('List comments on a platform attachment')
|
|
246
|
+
.requiredOption('--attachment-id <id>', 'Attachment id returned by upload or shown in a message')
|
|
247
|
+
.option('--limit <n>', 'Max comments to return')
|
|
248
|
+
.action(async (opts) => {
|
|
249
|
+
const runtime = createRuntime(program, options);
|
|
250
|
+
const attachmentId = requireString(opts.attachmentId, '--attachment-id');
|
|
251
|
+
const params = new URLSearchParams();
|
|
252
|
+
const limit = parsePositiveInteger('limit', opts.limit);
|
|
253
|
+
if (limit !== undefined)
|
|
254
|
+
params.set('limit', String(limit));
|
|
255
|
+
const qs = params.toString();
|
|
256
|
+
const response = await runtime.client.request('GET', `/api/internal/agent/${encodeURIComponent(runtime.context.agentId)}/attachments/${encodeURIComponent(attachmentId)}/comments${qs ? `?${qs}` : ''}`);
|
|
257
|
+
if (!response.ok)
|
|
258
|
+
throw responseError(response, 'list attachment comments failed');
|
|
259
|
+
writeOutput(runtime, summarizeAttachmentComments(attachmentId, response.data), response.data);
|
|
260
|
+
});
|
|
261
|
+
}
|