@reximo/cli 0.1.0-alpha.4 → 0.1.0-alpha.6
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/README.md +93 -0
- package/package.json +3 -3
- package/src/client/generated/index.d.ts +1 -1
- package/src/client/generated/index.js +2 -4
- package/src/client/generated/index.js.map +1 -1
- package/src/client/generated/reximo-api-client.generated.d.ts +65 -0
- package/src/client/generated/reximo-api-client.generated.js +139 -0
- package/src/client/generated/reximo-api-client.generated.js.map +1 -1
- package/src/client/reximo-client.d.ts +48 -1
- package/src/client/reximo-client.js +95 -0
- package/src/client/reximo-client.js.map +1 -1
- package/src/lib/command-inputs.d.ts +39 -0
- package/src/lib/command-inputs.js +107 -0
- package/src/lib/command-inputs.js.map +1 -0
- package/src/lib/formatters.d.ts +6 -0
- package/src/lib/formatters.js +73 -0
- package/src/lib/formatters.js.map +1 -0
- package/src/lib/run-cli.js +475 -145
- package/src/lib/run-cli.js.map +1 -1
- package/src/lib/runtime-deps.d.ts +10 -0
- package/src/lib/runtime-deps.js +80 -0
- package/src/lib/runtime-deps.js.map +1 -0
package/src/lib/run-cli.js
CHANGED
|
@@ -4,7 +4,9 @@ exports.runCli = runCli;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const node_process_1 = tslib_1.__importDefault(require("node:process"));
|
|
6
6
|
const commander_1 = require("commander");
|
|
7
|
+
const command_inputs_1 = require("./command-inputs");
|
|
7
8
|
const config_1 = require("./config");
|
|
9
|
+
const formatters_1 = require("./formatters");
|
|
8
10
|
const output_1 = require("./output");
|
|
9
11
|
const prompts_1 = require("./prompts");
|
|
10
12
|
const package_json_1 = tslib_1.__importDefault(require("../../package.json"));
|
|
@@ -84,6 +86,14 @@ function createAuthenticatedContext(dependencies, configOptions) {
|
|
|
84
86
|
organizationId: requireConfiguredOrganization(config)
|
|
85
87
|
};
|
|
86
88
|
}
|
|
89
|
+
async function withAuthenticatedContext(dependencies, configOptions, run) {
|
|
90
|
+
const context = createAuthenticatedContext(dependencies, configOptions);
|
|
91
|
+
return run(context);
|
|
92
|
+
}
|
|
93
|
+
async function runAuthenticatedCommand(params) {
|
|
94
|
+
const result = await withAuthenticatedContext(params.dependencies, params.configOptions, params.operation);
|
|
95
|
+
writeResult(params.command, params.dependencies, params.text(result), params.json(result));
|
|
96
|
+
}
|
|
87
97
|
async function resolveRequiredValue(currentValue, label, prompt) {
|
|
88
98
|
if (currentValue?.trim()) {
|
|
89
99
|
return currentValue.trim();
|
|
@@ -139,107 +149,66 @@ async function resolveOrganizationContext(promptAdapter, organizations, organiza
|
|
|
139
149
|
const selectedOrganizationId = await promptAdapter.select('Select organization', choices);
|
|
140
150
|
return (activeOrganizations.find((organization) => organization.organizationId === selectedOrganizationId) ?? null);
|
|
141
151
|
}
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
];
|
|
155
|
-
for (const issue of result.data) {
|
|
156
|
-
lines.push(`${formatIssueIdentifier(issue)} [id:${issue.id}] ${issue.title} (${issue.status}, ${issue.priority})`);
|
|
157
|
-
}
|
|
158
|
-
return lines;
|
|
159
|
-
}
|
|
160
|
-
function formatIssueDetailLines(issue) {
|
|
161
|
-
return [
|
|
162
|
-
`Issue: ${formatIssueIdentifier(issue)} [id:${issue.id}]`,
|
|
163
|
-
`Title: ${issue.title}`,
|
|
164
|
-
`Status: ${issue.status}`,
|
|
165
|
-
`Priority: ${issue.priority}`,
|
|
166
|
-
`Project: ${issue.project?.name ?? '(none)'}`,
|
|
167
|
-
`Updated: ${issue.updatedAt}`
|
|
168
|
-
];
|
|
169
|
-
}
|
|
170
|
-
function formatProjectAccess(project) {
|
|
171
|
-
if (project.visibility === 'public') {
|
|
172
|
-
return 'public';
|
|
173
|
-
}
|
|
174
|
-
return project.isMember ? 'private member' : 'private';
|
|
175
|
-
}
|
|
176
|
-
function formatProjectLines(result) {
|
|
177
|
-
if (result.data.length === 0) {
|
|
178
|
-
return ['No projects found.'];
|
|
179
|
-
}
|
|
180
|
-
const lines = [
|
|
181
|
-
`Projects: ${result.metadata.pagination.total} total (page ${result.metadata.pagination.page}/${Math.max(result.metadata.pagination.totalPages, 1)})`
|
|
182
|
-
];
|
|
183
|
-
for (const project of result.data) {
|
|
184
|
-
lines.push(`${project.key} [id:${project.id}] ${project.name} (${formatProjectAccess(project)})`);
|
|
185
|
-
}
|
|
186
|
-
return lines;
|
|
187
|
-
}
|
|
188
|
-
function buildUpdateIssuePayload(options) {
|
|
189
|
-
const payload = {};
|
|
190
|
-
if (options.title !== undefined) {
|
|
191
|
-
payload['title'] = options.title;
|
|
192
|
-
}
|
|
193
|
-
if (options.descriptionMarkdown !== undefined) {
|
|
194
|
-
payload['descriptionMarkdown'] = options.descriptionMarkdown;
|
|
195
|
-
}
|
|
196
|
-
if (options.status !== undefined) {
|
|
197
|
-
payload['status'] = options.status;
|
|
198
|
-
}
|
|
199
|
-
if (options.statusDefinitionId !== undefined) {
|
|
200
|
-
payload['statusDefinitionId'] = options.statusDefinitionId;
|
|
201
|
-
}
|
|
202
|
-
if (options.priority !== undefined) {
|
|
203
|
-
payload['priority'] = options.priority;
|
|
204
|
-
}
|
|
205
|
-
if (options.position !== undefined) {
|
|
206
|
-
payload['position'] = options.position;
|
|
207
|
-
}
|
|
208
|
-
if (options.baseRevision !== undefined) {
|
|
209
|
-
payload['baseRevision'] = options.baseRevision;
|
|
210
|
-
}
|
|
211
|
-
if (options.clearParentIssue) {
|
|
212
|
-
payload['parentIssueId'] = null;
|
|
213
|
-
}
|
|
214
|
-
else if (options.parentIssueId !== undefined) {
|
|
215
|
-
payload['parentIssueId'] = options.parentIssueId;
|
|
216
|
-
}
|
|
217
|
-
if (options.clearEstimate) {
|
|
218
|
-
payload['estimate'] = null;
|
|
219
|
-
}
|
|
220
|
-
else if (options.estimate !== undefined) {
|
|
221
|
-
payload['estimate'] = options.estimate;
|
|
222
|
-
}
|
|
223
|
-
if (options.clearDueAt) {
|
|
224
|
-
payload['dueAt'] = null;
|
|
225
|
-
}
|
|
226
|
-
else if (options.dueAt !== undefined) {
|
|
227
|
-
payload['dueAt'] = options.dueAt;
|
|
228
|
-
}
|
|
229
|
-
if (Object.keys(payload).length === 0) {
|
|
230
|
-
throw new Error('At least one update field is required.');
|
|
231
|
-
}
|
|
232
|
-
return payload;
|
|
152
|
+
function buildAuthStatus(config, client) {
|
|
153
|
+
const target = client.describeTarget();
|
|
154
|
+
return {
|
|
155
|
+
profile: config.profile,
|
|
156
|
+
configPath: config.configPath,
|
|
157
|
+
configFileFound: config.configFileFound,
|
|
158
|
+
apiBaseUrl: config.apiBaseUrl ?? config_1.DEFAULT_API_BASE_URL,
|
|
159
|
+
isConfigured: client.isConfigured(),
|
|
160
|
+
hasAccessToken: target.hasAccessToken,
|
|
161
|
+
hasRefreshToken: target.hasRefreshToken,
|
|
162
|
+
organizationId: target.organizationId
|
|
163
|
+
};
|
|
233
164
|
}
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
return options;
|
|
237
|
-
}
|
|
165
|
+
function buildDoctorReport(config, client) {
|
|
166
|
+
const auth = buildAuthStatus(config, client);
|
|
238
167
|
return {
|
|
239
|
-
|
|
240
|
-
|
|
168
|
+
command: 'doctor',
|
|
169
|
+
auth,
|
|
170
|
+
checks: {
|
|
171
|
+
apiBaseUrlConfigured: auth.isConfigured,
|
|
172
|
+
accessTokenConfigured: auth.hasAccessToken,
|
|
173
|
+
refreshTokenConfigured: auth.hasRefreshToken,
|
|
174
|
+
organizationConfigured: Boolean(auth.organizationId),
|
|
175
|
+
readyForAuthenticatedCommands: auth.isConfigured && auth.hasAccessToken && Boolean(auth.organizationId)
|
|
176
|
+
},
|
|
177
|
+
capabilities: {
|
|
178
|
+
issues: {
|
|
179
|
+
search: true,
|
|
180
|
+
list: true,
|
|
181
|
+
get: true,
|
|
182
|
+
create: true,
|
|
183
|
+
update: true,
|
|
184
|
+
delete: true,
|
|
185
|
+
comment: true,
|
|
186
|
+
transition: true,
|
|
187
|
+
link: true
|
|
188
|
+
},
|
|
189
|
+
projects: {
|
|
190
|
+
list: true
|
|
191
|
+
},
|
|
192
|
+
content: {
|
|
193
|
+
list: true,
|
|
194
|
+
get: true,
|
|
195
|
+
create: true,
|
|
196
|
+
update: true,
|
|
197
|
+
delete: true,
|
|
198
|
+
publish: false,
|
|
199
|
+
attach: false
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
notes: [
|
|
203
|
+
'Issue search, get, create, update, delete, comment, transition, and link are currently supported.',
|
|
204
|
+
'Project listing and content list, get, create, update, and delete are currently supported.',
|
|
205
|
+
'The current API surface does not expose a separate content publish endpoint.'
|
|
206
|
+
]
|
|
241
207
|
};
|
|
242
208
|
}
|
|
209
|
+
function unsupportedContentError(subcommand) {
|
|
210
|
+
return new Error(`Content command "${subcommand}" is not yet supported by the installed Reximo CLI/API surface. Run \`reximo doctor --json\` to inspect current capabilities.`);
|
|
211
|
+
}
|
|
243
212
|
function createProgram(dependencies) {
|
|
244
213
|
const configOptions = {
|
|
245
214
|
env: dependencies.env,
|
|
@@ -260,6 +229,17 @@ function createProgram(dependencies) {
|
|
|
260
229
|
dependencies.stderr.write(message);
|
|
261
230
|
}
|
|
262
231
|
});
|
|
232
|
+
registerInfoCommand(program, dependencies, configOptions);
|
|
233
|
+
registerAuthCommands(program, dependencies, configOptions);
|
|
234
|
+
registerDoctorCommand(program, dependencies, configOptions);
|
|
235
|
+
registerIssueCommands(program, dependencies, configOptions);
|
|
236
|
+
registerProjectCommands(program, dependencies, configOptions);
|
|
237
|
+
registerContentCommands(program, dependencies, configOptions);
|
|
238
|
+
registerConfigCommands(program, dependencies, configOptions);
|
|
239
|
+
registerSessionCommands(program, dependencies, configOptions);
|
|
240
|
+
return program;
|
|
241
|
+
}
|
|
242
|
+
function registerInfoCommand(program, dependencies, configOptions) {
|
|
263
243
|
program
|
|
264
244
|
.command('info')
|
|
265
245
|
.description('Show package and runtime information for diagnostics.')
|
|
@@ -296,6 +276,53 @@ function createProgram(dependencies) {
|
|
|
296
276
|
}
|
|
297
277
|
});
|
|
298
278
|
});
|
|
279
|
+
}
|
|
280
|
+
function registerAuthCommands(program, dependencies, configOptions) {
|
|
281
|
+
const authCommand = program.command('auth').description('Inspect Reximo authentication state.');
|
|
282
|
+
authCommand
|
|
283
|
+
.command('status')
|
|
284
|
+
.description('Show whether the current CLI profile is ready for authenticated API commands.')
|
|
285
|
+
.action(function action() {
|
|
286
|
+
const config = (0, config_1.loadResolvedConfig)(configOptions);
|
|
287
|
+
const client = dependencies.clientFactory(config);
|
|
288
|
+
const status = buildAuthStatus(config, client);
|
|
289
|
+
writeResult(this, dependencies, [
|
|
290
|
+
`Profile: ${status.profile}`,
|
|
291
|
+
`Config path: ${status.configPath}`,
|
|
292
|
+
`Config file found: ${status.configFileFound ? 'yes' : 'no'}`,
|
|
293
|
+
`API base URL: ${status.apiBaseUrl}`,
|
|
294
|
+
`Configured for API: ${status.isConfigured ? 'yes' : 'no'}`,
|
|
295
|
+
`Access token configured: ${status.hasAccessToken ? 'yes' : 'no'}`,
|
|
296
|
+
`Refresh token configured: ${status.hasRefreshToken ? 'yes' : 'no'}`,
|
|
297
|
+
`Organization: ${status.organizationId ?? '(not configured)'}`
|
|
298
|
+
], {
|
|
299
|
+
command: 'auth.status',
|
|
300
|
+
status
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function registerDoctorCommand(program, dependencies, configOptions) {
|
|
305
|
+
program
|
|
306
|
+
.command('doctor')
|
|
307
|
+
.description('Report Reximo CLI readiness and command-surface capabilities for automation.')
|
|
308
|
+
.action(function action() {
|
|
309
|
+
const config = (0, config_1.loadResolvedConfig)(configOptions);
|
|
310
|
+
const client = dependencies.clientFactory(config);
|
|
311
|
+
const report = buildDoctorReport(config, client);
|
|
312
|
+
writeResult(this, dependencies, [
|
|
313
|
+
`Profile: ${report.auth.profile}`,
|
|
314
|
+
`Configured for API: ${report.auth.isConfigured ? 'yes' : 'no'}`,
|
|
315
|
+
`Access token configured: ${report.auth.hasAccessToken ? 'yes' : 'no'}`,
|
|
316
|
+
`Refresh token configured: ${report.auth.hasRefreshToken ? 'yes' : 'no'}`,
|
|
317
|
+
`Organization: ${report.auth.organizationId ?? '(not configured)'}`,
|
|
318
|
+
`Ready for authenticated commands: ${report.auth.isConfigured && report.auth.hasAccessToken && Boolean(report.auth.organizationId) ? 'yes' : 'no'}`,
|
|
319
|
+
`Issue commands: search, list, get, create, update, delete, comment, transition, link`,
|
|
320
|
+
`Content commands: list, get, create, update, delete`,
|
|
321
|
+
`Unsupported content commands: publish, attach`
|
|
322
|
+
], report);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
function registerIssueCommands(program, dependencies, configOptions) {
|
|
299
326
|
const issuesCommand = program.command('issues').description('Manage issues.');
|
|
300
327
|
issuesCommand
|
|
301
328
|
.command('search')
|
|
@@ -315,15 +342,41 @@ function createProgram(dependencies) {
|
|
|
315
342
|
.option('--sort-by <sortBy>', 'Sort field')
|
|
316
343
|
.option('--sort-order <sortOrder>', 'Sort direction')
|
|
317
344
|
.action(async function action(options) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
345
|
+
await runAuthenticatedCommand({
|
|
346
|
+
command: this,
|
|
347
|
+
dependencies,
|
|
348
|
+
configOptions,
|
|
349
|
+
operation: ({ client, accessToken, organizationId }) => client.listIssues({
|
|
350
|
+
accessToken,
|
|
351
|
+
organizationId,
|
|
352
|
+
...(0, command_inputs_1.buildIssuesSearchInput)(options)
|
|
353
|
+
}),
|
|
354
|
+
text: (result) => (0, formatters_1.formatIssueLines)(result),
|
|
355
|
+
json: (result) => ({
|
|
356
|
+
command: 'issues.search',
|
|
357
|
+
result
|
|
358
|
+
})
|
|
323
359
|
});
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
360
|
+
});
|
|
361
|
+
issuesCommand
|
|
362
|
+
.command('get <issueId>')
|
|
363
|
+
.alias('show')
|
|
364
|
+
.description('Get one issue by id in the configured organization.')
|
|
365
|
+
.action(async function action(issueId) {
|
|
366
|
+
await runAuthenticatedCommand({
|
|
367
|
+
command: this,
|
|
368
|
+
dependencies,
|
|
369
|
+
configOptions,
|
|
370
|
+
operation: ({ client, accessToken, organizationId }) => client.getIssue({
|
|
371
|
+
accessToken,
|
|
372
|
+
organizationId,
|
|
373
|
+
issueId
|
|
374
|
+
}),
|
|
375
|
+
text: (issue) => (0, formatters_1.formatIssueDetailLines)(issue),
|
|
376
|
+
json: (issue) => ({
|
|
377
|
+
command: 'issues.get',
|
|
378
|
+
issue
|
|
379
|
+
})
|
|
327
380
|
});
|
|
328
381
|
});
|
|
329
382
|
issuesCommand
|
|
@@ -340,21 +393,27 @@ function createProgram(dependencies) {
|
|
|
340
393
|
.option('--estimate <estimate>', 'Issue estimate', Number)
|
|
341
394
|
.option('--due-at <dueAt>', 'Issue due date as an ISO-8601 string')
|
|
342
395
|
.action(async function action(options) {
|
|
343
|
-
const { client, accessToken, organizationId } = createAuthenticatedContext(dependencies, configOptions);
|
|
344
396
|
if (!options.title?.trim()) {
|
|
345
397
|
throw new Error('Title is required');
|
|
346
398
|
}
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
399
|
+
const title = options.title.trim();
|
|
400
|
+
await runAuthenticatedCommand({
|
|
401
|
+
command: this,
|
|
402
|
+
dependencies,
|
|
403
|
+
configOptions,
|
|
404
|
+
operation: ({ client, accessToken, organizationId }) => client.createIssue({
|
|
405
|
+
accessToken,
|
|
406
|
+
organizationId,
|
|
407
|
+
issue: {
|
|
408
|
+
...options,
|
|
409
|
+
title
|
|
410
|
+
}
|
|
411
|
+
}),
|
|
412
|
+
text: (issue) => (0, formatters_1.formatIssueDetailLines)(issue),
|
|
413
|
+
json: (issue) => ({
|
|
414
|
+
command: 'issues.create',
|
|
415
|
+
issue
|
|
416
|
+
})
|
|
358
417
|
});
|
|
359
418
|
});
|
|
360
419
|
issuesCommand
|
|
@@ -374,34 +433,145 @@ function createProgram(dependencies) {
|
|
|
374
433
|
.option('--clear-due-at', 'Clear the current due date')
|
|
375
434
|
.option('--base-revision <baseRevision>', 'Optimistic concurrency token from the last issue read')
|
|
376
435
|
.action(async function action(issueId, options) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
436
|
+
await runAuthenticatedCommand({
|
|
437
|
+
command: this,
|
|
438
|
+
dependencies,
|
|
439
|
+
configOptions,
|
|
440
|
+
operation: ({ client, accessToken, organizationId }) => client.updateIssue({
|
|
441
|
+
accessToken,
|
|
442
|
+
organizationId,
|
|
443
|
+
issueId,
|
|
444
|
+
issue: (0, command_inputs_1.buildUpdateIssuePayload)(options)
|
|
445
|
+
}),
|
|
446
|
+
text: (issue) => (0, formatters_1.formatIssueDetailLines)(issue),
|
|
447
|
+
json: (issue) => ({
|
|
448
|
+
command: 'issues.update',
|
|
449
|
+
issue
|
|
450
|
+
})
|
|
383
451
|
});
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
452
|
+
});
|
|
453
|
+
issuesCommand
|
|
454
|
+
.command('transition <issueId>')
|
|
455
|
+
.description('Transition one issue by updating its status or status definition.')
|
|
456
|
+
.option('--status <status>', 'Issue status')
|
|
457
|
+
.option('--status-definition-id <statusDefinitionId>', 'Issue status definition id', Number)
|
|
458
|
+
.option('--base-revision <baseRevision>', 'Optimistic concurrency token from the last issue read')
|
|
459
|
+
.action(async function action(issueId, options) {
|
|
460
|
+
if (options.status === undefined && options.statusDefinitionId === undefined) {
|
|
461
|
+
throw new Error('Either --status or --status-definition-id is required.');
|
|
462
|
+
}
|
|
463
|
+
await runAuthenticatedCommand({
|
|
464
|
+
command: this,
|
|
465
|
+
dependencies,
|
|
466
|
+
configOptions,
|
|
467
|
+
operation: ({ client, accessToken, organizationId }) => client.updateIssue({
|
|
468
|
+
accessToken,
|
|
469
|
+
organizationId,
|
|
470
|
+
issueId,
|
|
471
|
+
issue: {
|
|
472
|
+
...(options.status !== undefined ? { status: options.status } : {}),
|
|
473
|
+
...(options.statusDefinitionId !== undefined
|
|
474
|
+
? { statusDefinitionId: options.statusDefinitionId }
|
|
475
|
+
: {}),
|
|
476
|
+
...(options.baseRevision !== undefined ? { baseRevision: options.baseRevision } : {})
|
|
477
|
+
}
|
|
478
|
+
}),
|
|
479
|
+
text: (issue) => (0, formatters_1.formatIssueDetailLines)(issue),
|
|
480
|
+
json: (issue) => ({
|
|
481
|
+
command: 'issues.transition',
|
|
482
|
+
issue
|
|
483
|
+
})
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
issuesCommand
|
|
487
|
+
.command('comment <issueId>')
|
|
488
|
+
.description('Create one issue comment in the configured organization.')
|
|
489
|
+
.option('--body-markdown <bodyMarkdown>', 'Issue comment body in markdown')
|
|
490
|
+
.option('--parent-comment-id <parentCommentId>', 'Optional parent comment id', Number)
|
|
491
|
+
.action(async function action(issueId, options) {
|
|
492
|
+
if (!options.bodyMarkdown?.trim()) {
|
|
493
|
+
throw new Error('Comment body is required');
|
|
494
|
+
}
|
|
495
|
+
const bodyMarkdown = options.bodyMarkdown.trim();
|
|
496
|
+
await runAuthenticatedCommand({
|
|
497
|
+
command: this,
|
|
498
|
+
dependencies,
|
|
499
|
+
configOptions,
|
|
500
|
+
operation: ({ client, accessToken, organizationId }) => client.createIssueComment({
|
|
501
|
+
accessToken,
|
|
502
|
+
organizationId,
|
|
503
|
+
issueId,
|
|
504
|
+
comment: {
|
|
505
|
+
bodyMarkdown,
|
|
506
|
+
...(options.parentCommentId !== undefined
|
|
507
|
+
? { parentCommentId: options.parentCommentId }
|
|
508
|
+
: {})
|
|
509
|
+
}
|
|
510
|
+
}),
|
|
511
|
+
text: (issue) => (0, formatters_1.formatIssueDetailLines)(issue),
|
|
512
|
+
json: (issue) => ({
|
|
513
|
+
command: 'issues.comment',
|
|
514
|
+
issue
|
|
515
|
+
})
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
issuesCommand
|
|
519
|
+
.command('link <issueId>')
|
|
520
|
+
.description('Create one issue relation from the selected issue to another issue.')
|
|
521
|
+
.option('--target-issue-id <targetIssueId>', 'Target issue id', Number)
|
|
522
|
+
.option('--relation-type <relationType>', 'Relation type key')
|
|
523
|
+
.action(async function action(issueId, options) {
|
|
524
|
+
if (options.targetIssueId === undefined) {
|
|
525
|
+
throw new Error('Target issue id is required');
|
|
526
|
+
}
|
|
527
|
+
if (!options.relationType?.trim()) {
|
|
528
|
+
throw new Error('Relation type is required');
|
|
529
|
+
}
|
|
530
|
+
const targetIssueId = options.targetIssueId;
|
|
531
|
+
const relationType = options.relationType;
|
|
532
|
+
await runAuthenticatedCommand({
|
|
533
|
+
command: this,
|
|
534
|
+
dependencies,
|
|
535
|
+
configOptions,
|
|
536
|
+
operation: ({ client, accessToken, organizationId }) => client.createIssueRelation({
|
|
537
|
+
accessToken,
|
|
538
|
+
organizationId,
|
|
539
|
+
issueId,
|
|
540
|
+
relation: {
|
|
541
|
+
targetIssueId,
|
|
542
|
+
relationType
|
|
543
|
+
}
|
|
544
|
+
}),
|
|
545
|
+
text: (issue) => (0, formatters_1.formatIssueDetailLines)(issue),
|
|
546
|
+
json: (issue) => ({
|
|
547
|
+
command: 'issues.link',
|
|
548
|
+
issue
|
|
549
|
+
})
|
|
387
550
|
});
|
|
388
551
|
});
|
|
389
552
|
issuesCommand
|
|
390
553
|
.command('delete <issueId>')
|
|
391
554
|
.description('Delete one issue by id in the configured organization.')
|
|
392
555
|
.action(async function action(issueId) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
556
|
+
await runAuthenticatedCommand({
|
|
557
|
+
command: this,
|
|
558
|
+
dependencies,
|
|
559
|
+
configOptions,
|
|
560
|
+
operation: ({ client, accessToken, organizationId }) => client.deleteIssue({
|
|
561
|
+
accessToken,
|
|
562
|
+
organizationId,
|
|
563
|
+
issueId
|
|
564
|
+
}),
|
|
565
|
+
text: () => [`Issue ${issueId} deleted.`],
|
|
566
|
+
json: () => ({
|
|
567
|
+
command: 'issues.delete',
|
|
568
|
+
issueId,
|
|
569
|
+
deleted: true
|
|
570
|
+
})
|
|
403
571
|
});
|
|
404
572
|
});
|
|
573
|
+
}
|
|
574
|
+
function registerProjectCommands(program, dependencies, configOptions) {
|
|
405
575
|
const projectsCommand = program.command('projects').description('Manage projects.');
|
|
406
576
|
projectsCommand
|
|
407
577
|
.command('list')
|
|
@@ -413,17 +583,176 @@ function createProgram(dependencies) {
|
|
|
413
583
|
.option('--sort-by <sortBy>', 'Sort field')
|
|
414
584
|
.option('--sort-order <sortOrder>', 'Sort direction')
|
|
415
585
|
.action(async function action(options) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
586
|
+
await runAuthenticatedCommand({
|
|
587
|
+
command: this,
|
|
588
|
+
dependencies,
|
|
589
|
+
configOptions,
|
|
590
|
+
operation: ({ client, accessToken, organizationId }) => client.listProjects({
|
|
591
|
+
accessToken,
|
|
592
|
+
organizationId,
|
|
593
|
+
...options
|
|
594
|
+
}),
|
|
595
|
+
text: (result) => (0, formatters_1.formatProjectLines)(result),
|
|
596
|
+
json: (result) => ({
|
|
597
|
+
command: 'projects.list',
|
|
598
|
+
result
|
|
599
|
+
})
|
|
421
600
|
});
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
function registerContentCommands(program, dependencies, configOptions) {
|
|
604
|
+
const contentCommand = program.command('content').description('Manage content entries.');
|
|
605
|
+
contentCommand
|
|
606
|
+
.command('list')
|
|
607
|
+
.description('List org-scoped content by default, or project-scoped content when a project id or key is provided.')
|
|
608
|
+
.option('--scope <scope>', 'Content scope filter')
|
|
609
|
+
.option('--project-id <projectId>', 'Project id filter', Number)
|
|
610
|
+
.option('--project-key <projectKey>', 'Project key filter')
|
|
611
|
+
.option('--parent-id <parentId>', 'Parent content id filter', Number)
|
|
612
|
+
.option('--slug <slug>', 'Slug filter')
|
|
613
|
+
.action(async function action(options) {
|
|
614
|
+
const normalizedOptions = (0, command_inputs_1.normalizeContentListOptions)(options);
|
|
615
|
+
await runAuthenticatedCommand({
|
|
616
|
+
command: this,
|
|
617
|
+
dependencies,
|
|
618
|
+
configOptions,
|
|
619
|
+
operation: ({ client, accessToken, organizationId }) => client.listContent({
|
|
620
|
+
accessToken,
|
|
621
|
+
organizationId,
|
|
622
|
+
...normalizedOptions
|
|
623
|
+
}),
|
|
624
|
+
text: (result) => (0, formatters_1.formatContentLines)(result),
|
|
625
|
+
json: (result) => ({
|
|
626
|
+
command: 'content.list',
|
|
627
|
+
result
|
|
628
|
+
})
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
contentCommand
|
|
632
|
+
.command('get <contentId>')
|
|
633
|
+
.alias('show')
|
|
634
|
+
.description('Get one content entry by id in the configured organization.')
|
|
635
|
+
.action(async function action(contentId) {
|
|
636
|
+
await runAuthenticatedCommand({
|
|
637
|
+
command: this,
|
|
638
|
+
dependencies,
|
|
639
|
+
configOptions,
|
|
640
|
+
operation: ({ client, accessToken, organizationId }) => client.getContent({
|
|
641
|
+
accessToken,
|
|
642
|
+
organizationId,
|
|
643
|
+
contentId
|
|
644
|
+
}),
|
|
645
|
+
text: (content) => (0, formatters_1.formatContentDetailLines)(content),
|
|
646
|
+
json: (content) => ({
|
|
647
|
+
command: 'content.get',
|
|
648
|
+
content
|
|
649
|
+
})
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
contentCommand
|
|
653
|
+
.command('create')
|
|
654
|
+
.description('Create one content entry in the configured organization.')
|
|
655
|
+
.option('--title <title>', 'Content title')
|
|
656
|
+
.option('--content-markdown <contentMarkdown>', 'Content body in markdown')
|
|
657
|
+
.option('--slug <slug>', 'Content slug')
|
|
658
|
+
.option('--project-id <projectId>', 'Project id', Number)
|
|
659
|
+
.option('--parent-id <parentId>', 'Parent content id', Number)
|
|
660
|
+
.option('--position <position>', 'Content position', Number)
|
|
661
|
+
.action(async function action(options) {
|
|
662
|
+
if (!options.title?.trim()) {
|
|
663
|
+
throw new Error('Title is required');
|
|
664
|
+
}
|
|
665
|
+
if (!options.contentMarkdown?.trim()) {
|
|
666
|
+
throw new Error('Content markdown is required');
|
|
667
|
+
}
|
|
668
|
+
const title = options.title.trim();
|
|
669
|
+
const contentMarkdown = options.contentMarkdown;
|
|
670
|
+
await runAuthenticatedCommand({
|
|
671
|
+
command: this,
|
|
672
|
+
dependencies,
|
|
673
|
+
configOptions,
|
|
674
|
+
operation: ({ client, accessToken, organizationId }) => client.createContent({
|
|
675
|
+
accessToken,
|
|
676
|
+
organizationId,
|
|
677
|
+
content: {
|
|
678
|
+
title,
|
|
679
|
+
contentMarkdown,
|
|
680
|
+
...(options.slug !== undefined ? { slug: options.slug } : {}),
|
|
681
|
+
...(options.projectId !== undefined ? { projectId: options.projectId } : {}),
|
|
682
|
+
...(options.parentId !== undefined ? { parentId: options.parentId } : {}),
|
|
683
|
+
...(options.position !== undefined ? { position: options.position } : {})
|
|
684
|
+
}
|
|
685
|
+
}),
|
|
686
|
+
text: (content) => (0, formatters_1.formatContentDetailLines)(content),
|
|
687
|
+
json: (content) => ({
|
|
688
|
+
command: 'content.create',
|
|
689
|
+
content
|
|
690
|
+
})
|
|
425
691
|
});
|
|
426
692
|
});
|
|
693
|
+
contentCommand
|
|
694
|
+
.command('update <contentId>')
|
|
695
|
+
.description('Update one content entry by id in the configured organization.')
|
|
696
|
+
.option('--title <title>', 'Content title')
|
|
697
|
+
.option('--content-markdown <contentMarkdown>', 'Content body in markdown')
|
|
698
|
+
.option('--slug <slug>', 'Content slug')
|
|
699
|
+
.option('--parent-id <parentId>', 'Parent content id', Number)
|
|
700
|
+
.option('--position <position>', 'Content position', Number)
|
|
701
|
+
.option('--clear-parent', 'Clear the current parent content id')
|
|
702
|
+
.option('--base-revision <baseRevision>', 'Optimistic concurrency token from the last content read')
|
|
703
|
+
.action(async function action(contentId, options) {
|
|
704
|
+
await runAuthenticatedCommand({
|
|
705
|
+
command: this,
|
|
706
|
+
dependencies,
|
|
707
|
+
configOptions,
|
|
708
|
+
operation: ({ client, accessToken, organizationId }) => client.updateContent({
|
|
709
|
+
accessToken,
|
|
710
|
+
organizationId,
|
|
711
|
+
contentId,
|
|
712
|
+
content: (0, command_inputs_1.buildUpdateContentPayload)(options)
|
|
713
|
+
}),
|
|
714
|
+
text: (content) => (0, formatters_1.formatContentDetailLines)(content),
|
|
715
|
+
json: (content) => ({
|
|
716
|
+
command: 'content.update',
|
|
717
|
+
content
|
|
718
|
+
})
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
contentCommand
|
|
722
|
+
.command('delete <contentId>')
|
|
723
|
+
.description('Delete one content entry by id in the configured organization.')
|
|
724
|
+
.action(async function action(contentId) {
|
|
725
|
+
await runAuthenticatedCommand({
|
|
726
|
+
command: this,
|
|
727
|
+
dependencies,
|
|
728
|
+
configOptions,
|
|
729
|
+
operation: ({ client, accessToken, organizationId }) => client.deleteContent({
|
|
730
|
+
accessToken,
|
|
731
|
+
organizationId,
|
|
732
|
+
contentId
|
|
733
|
+
}),
|
|
734
|
+
text: () => [`Content ${contentId} deleted.`],
|
|
735
|
+
json: () => ({
|
|
736
|
+
command: 'content.delete',
|
|
737
|
+
contentId,
|
|
738
|
+
deleted: true
|
|
739
|
+
})
|
|
740
|
+
});
|
|
741
|
+
});
|
|
742
|
+
contentCommand
|
|
743
|
+
.command('publish <contentId>')
|
|
744
|
+
.description('Publish one content artifact when supported by the installed CLI/API surface.')
|
|
745
|
+
.action(async () => {
|
|
746
|
+
throw unsupportedContentError("publish" /* UnsupportedContentCommand.Publish */);
|
|
747
|
+
});
|
|
748
|
+
contentCommand
|
|
749
|
+
.command('attach <contentId>')
|
|
750
|
+
.description('Attach an artifact to content when supported by the installed CLI/API surface.')
|
|
751
|
+
.action(async () => {
|
|
752
|
+
throw unsupportedContentError("attach" /* UnsupportedContentCommand.Attach */);
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
function registerConfigCommands(program, dependencies, configOptions) {
|
|
427
756
|
const configCommand = program
|
|
428
757
|
.command('config')
|
|
429
758
|
.description('Inspect resolved CLI configuration.');
|
|
@@ -446,6 +775,8 @@ function createProgram(dependencies) {
|
|
|
446
775
|
config: renderConfigSummary(config, Boolean(options.includeSensitive))
|
|
447
776
|
});
|
|
448
777
|
});
|
|
778
|
+
}
|
|
779
|
+
function registerSessionCommands(program, dependencies, configOptions) {
|
|
449
780
|
program
|
|
450
781
|
.command('login')
|
|
451
782
|
.description('Authenticate with the Reximo API and save auth state for the active profile.')
|
|
@@ -546,7 +877,6 @@ function createProgram(dependencies) {
|
|
|
546
877
|
tokensCleared: true
|
|
547
878
|
});
|
|
548
879
|
});
|
|
549
|
-
return program;
|
|
550
880
|
}
|
|
551
881
|
async function runCli(args, dependencies) {
|
|
552
882
|
const runtimeDependencies = toRuntimeDependencies(dependencies);
|