@clipit-ai/cli 0.2.7 → 0.2.9
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 +6 -0
- package/bin/clipit.mjs +162 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,6 +110,9 @@ clipit exports wait <jobId> --stream
|
|
|
110
110
|
clipit exports download <jobId> --open
|
|
111
111
|
clipit exports cancel <jobId> --confirm --json
|
|
112
112
|
|
|
113
|
+
clipit deliverables create --export-id <exportId> --title "Client-ready clip" --confirm --json
|
|
114
|
+
clipit deliverables list --status selected --json
|
|
115
|
+
|
|
113
116
|
clipit assets list --json
|
|
114
117
|
clipit assets upload ./brand-logo.png --kind image --confirm --json
|
|
115
118
|
clipit assets delete <assetId> --confirm --json
|
|
@@ -131,6 +134,8 @@ clipit social get <postId> --json
|
|
|
131
134
|
clipit social cancel <postId> --confirm --json
|
|
132
135
|
```
|
|
133
136
|
|
|
137
|
+
For enterprise workspace profiles, create a ready delivery after the exact export completes, then send the returned `clientSelectionUrl` to the client. Publishing and scheduling remain blocked until that client selects the delivery. `clipit deliverables list --export-id <exportId> --json` shows the current delivery state; after selection, the social commands automatically pin the exact delivery ID and granted social account.
|
|
138
|
+
|
|
134
139
|
## Reliability And Spend Guards
|
|
135
140
|
|
|
136
141
|
The CLI retries only idempotent `GET` requests, with at most two retries for network failures, `429`, `502`, `503`, and `504`. `Retry-After` is honored up to 10 seconds. `POST`, `PATCH`, and `DELETE` requests are never retried. Pass `--no-retry` to disable GET retries.
|
|
@@ -275,6 +280,7 @@ Only use `--allow-custom-host` for hosts you control or trust. It permits the CL
|
|
|
275
280
|
| Credits | `credits balance`, `credits usage`, `credits estimate` | Estimate accepts `--metrics @file.json`. |
|
|
276
281
|
| Analytics | `analytics overview`, `analytics top-clips`, `analytics post` | `overview` returns aggregate and by-platform metrics. |
|
|
277
282
|
| Exports | `exports start/list/get/wait/download/cancel` | Export jobs poll through `/api/v1/exports/:jobId`; paid start requires `--confirm`. |
|
|
283
|
+
| Enterprise deliveries | `deliverables list/create` | Workspace profiles can deliver exact exports and inspect client selection; only the client can select. |
|
|
278
284
|
| Assets | `assets list/upload/delete` | Upload signs, PUTs the file to object storage, then finalizes the library asset. Delete requires `--confirm`. |
|
|
279
285
|
| Thumbnails and B-Roll | `thumbnails generate/get/list`, `broll plan/generate/list/get` | Paid generation commands require `--confirm`. |
|
|
280
286
|
| Social | `social accounts/post/schedule/posts/get/cancel` | Publish/schedule/cancel require `--confirm`; `x` maps to the server `twitter` platform. |
|
package/bin/clipit.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import path from 'node:path';
|
|
|
9
9
|
import process from 'node:process';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
11
|
|
|
12
|
-
const VERSION = '0.2.
|
|
12
|
+
const VERSION = '0.2.9';
|
|
13
13
|
const DEFAULT_BASE_URL = 'https://clipit.dev';
|
|
14
14
|
const DEFAULT_SCOPES = [
|
|
15
15
|
'clippy_agent',
|
|
@@ -472,6 +472,7 @@ function usage() {
|
|
|
472
472
|
' clipit billing capabilities|catalog|create-attempt|attempt|receipt|subscription ...',
|
|
473
473
|
' clipit analytics overview|top-clips|post ...',
|
|
474
474
|
' clipit exports start|list|get|wait|download|cancel ...',
|
|
475
|
+
' clipit deliverables list|create ...',
|
|
475
476
|
' clipit assets list|upload|delete ...',
|
|
476
477
|
' clipit thumbnails generate|get|list ...',
|
|
477
478
|
' clipit broll plan|generate|list|get ...',
|
|
@@ -483,6 +484,51 @@ function usage() {
|
|
|
483
484
|
].join('\n');
|
|
484
485
|
}
|
|
485
486
|
|
|
487
|
+
function commandUsage(command, subcommand) {
|
|
488
|
+
if (command === 'social' && subcommand === 'schedule') {
|
|
489
|
+
return [
|
|
490
|
+
'Usage:',
|
|
491
|
+
' clipit social schedule --clip-id <id> --platforms <platform[,platform]> --caption <text> --at <ISO-8601> --confirm',
|
|
492
|
+
'',
|
|
493
|
+
'Required:',
|
|
494
|
+
' --clip-id <id> Clip to schedule',
|
|
495
|
+
' --platforms <list> Comma-separated destination platforms',
|
|
496
|
+
' --caption <text> Post caption',
|
|
497
|
+
' --at <ISO-8601> Scheduled publish time',
|
|
498
|
+
' --confirm Confirm the scheduling mutation',
|
|
499
|
+
'',
|
|
500
|
+
'Optional:',
|
|
501
|
+
' --export-id <id> Pin the exact completed export',
|
|
502
|
+
' --account-ids <map> Exact accounts as platform=accountId pairs',
|
|
503
|
+
' --title <text> Post title',
|
|
504
|
+
' --hashtags <list> Comma-separated hashtags',
|
|
505
|
+
' --profile <name> Named ClipIt credential profile',
|
|
506
|
+
' --json Emit machine-readable JSON',
|
|
507
|
+
'',
|
|
508
|
+
'Enterprise scheduling requires a client-selected deliverable and a granted connected account.',
|
|
509
|
+
].join('\n');
|
|
510
|
+
}
|
|
511
|
+
if (command === 'deliverables' && subcommand === 'create') {
|
|
512
|
+
return [
|
|
513
|
+
'Usage:',
|
|
514
|
+
' clipit deliverables create --export-id <id> --title <text> --confirm',
|
|
515
|
+
'',
|
|
516
|
+
'Required:',
|
|
517
|
+
' --export-id <id> Exact completed export to deliver',
|
|
518
|
+
' --title <text> Client-facing deliverable title',
|
|
519
|
+
' --confirm Confirm the delivery mutation',
|
|
520
|
+
'',
|
|
521
|
+
'Optional:',
|
|
522
|
+
' --note <text> Client-facing note',
|
|
523
|
+
' --profile <name> Named ClipIt credential profile',
|
|
524
|
+
' --json Emit machine-readable JSON',
|
|
525
|
+
'',
|
|
526
|
+
'The response includes clientSelectionUrl; the client must select the delivery before enterprise social publishing.',
|
|
527
|
+
].join('\n');
|
|
528
|
+
}
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
|
|
486
532
|
function getBaseUrl(config, options) {
|
|
487
533
|
const profile = profileData(config, options);
|
|
488
534
|
const selected = profileIsAuthoritative(config, options);
|
|
@@ -1239,14 +1285,17 @@ async function enforceMaxCredits(config, options, commandKey, context = {}) {
|
|
|
1239
1285
|
}
|
|
1240
1286
|
}
|
|
1241
1287
|
|
|
1242
|
-
async function enforceRunMaxCredits(config, options, functionName, parameters = {}, payload = {}) {
|
|
1288
|
+
async function enforceRunMaxCredits(config, options, functionName, parameters = {}, payload = {}, catalogTool = null) {
|
|
1243
1289
|
const limit = maxCreditsLimit(options);
|
|
1244
1290
|
const needsConfirmationPreflight = !boolOption(options.confirm);
|
|
1245
1291
|
if (limit === null && !needsConfirmationPreflight) return null;
|
|
1246
1292
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1293
|
+
let tool = catalogTool;
|
|
1294
|
+
if (!tool) {
|
|
1295
|
+
const catalog = await apiFetch(config, options, 'GET', '/api/v1/agent/tools');
|
|
1296
|
+
const tools = Array.isArray(catalog?.tools) ? catalog.tools : Array.isArray(catalog) ? catalog : [];
|
|
1297
|
+
tool = tools.find((item) => item.name === functionName);
|
|
1298
|
+
}
|
|
1250
1299
|
const runEstimate = tool?.estimate ?? tool?.confirmation?.estimate ?? tool?.confirmation?.costEstimate ?? null;
|
|
1251
1300
|
const isMetered = Boolean(tool?.costBand && tool.costBand !== 'free');
|
|
1252
1301
|
const isMeteredExempt = RUN_METERED_CONFIRMATION_EXEMPTIONS.has(functionName);
|
|
@@ -1663,14 +1712,26 @@ async function contextCommand(config, options, action) {
|
|
|
1663
1712
|
throw Object.assign(new Error(`Unknown context command: ${action || ''}`), { exitCode: EXIT.USAGE });
|
|
1664
1713
|
}
|
|
1665
1714
|
|
|
1666
|
-
function
|
|
1715
|
+
function toolAcceptsContextField(tool, field) {
|
|
1716
|
+
return Boolean(
|
|
1717
|
+
tool?.contextRequirements?.[field]
|
|
1718
|
+
|| tool?.parameters?.properties?.[field],
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
function applyContextToAgentPayload(payload, parameters, context, tool) {
|
|
1667
1723
|
const canMutateParameters = parameters && typeof parameters === 'object' && !Array.isArray(parameters);
|
|
1668
1724
|
const hadExplicitVideoId = canMutateParameters && parameters.videoId !== undefined;
|
|
1669
1725
|
for (const field of ['videoId', 'clipId', 'projectId', 'sequenceId']) {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
if (
|
|
1673
|
-
|
|
1726
|
+
const value = payload[field] || context[field];
|
|
1727
|
+
if (value) {
|
|
1728
|
+
if (!payload[field]) payload[field] = value;
|
|
1729
|
+
if (
|
|
1730
|
+
canMutateParameters
|
|
1731
|
+
&& parameters[field] === undefined
|
|
1732
|
+
&& toolAcceptsContextField(tool, field)
|
|
1733
|
+
) {
|
|
1734
|
+
parameters[field] = value;
|
|
1674
1735
|
}
|
|
1675
1736
|
}
|
|
1676
1737
|
}
|
|
@@ -1710,6 +1771,13 @@ function normalizeAgentExecuteResult(result) {
|
|
|
1710
1771
|
|
|
1711
1772
|
async function runTool(config, options, functionName) {
|
|
1712
1773
|
if (!functionName) throw Object.assign(new Error('Function name is required.'), { exitCode: EXIT.USAGE });
|
|
1774
|
+
if (RUN_CONFIRMATION_LABELS[functionName]) {
|
|
1775
|
+
confirmPaid(options, RUN_CONFIRMATION_LABELS[functionName]);
|
|
1776
|
+
}
|
|
1777
|
+
const catalog = await apiFetch(config, options, 'GET', '/api/v1/agent/tools');
|
|
1778
|
+
const tools = Array.isArray(catalog?.tools) ? catalog.tools : Array.isArray(catalog) ? catalog : [];
|
|
1779
|
+
const tool = tools.find((item) => item.name === functionName);
|
|
1780
|
+
if (!tool) throw Object.assign(new Error(`Tool not found: ${functionName}`), { exitCode: EXIT.USAGE });
|
|
1713
1781
|
const parameters = await readJsonOption(options.params || options['params-json']);
|
|
1714
1782
|
const context = await buildContext(config, options);
|
|
1715
1783
|
const payload = {
|
|
@@ -1726,16 +1794,10 @@ async function runTool(config, options, functionName) {
|
|
|
1726
1794
|
if (options[flag]) {
|
|
1727
1795
|
const value = String(options[flag]);
|
|
1728
1796
|
payload[field] = value;
|
|
1729
|
-
if (parameters && typeof parameters === 'object' && !Array.isArray(parameters) && parameters[field] === undefined) {
|
|
1730
|
-
parameters[field] = value;
|
|
1731
|
-
}
|
|
1732
1797
|
}
|
|
1733
1798
|
}
|
|
1734
|
-
applyContextToAgentPayload(payload, parameters, context);
|
|
1735
|
-
|
|
1736
|
-
confirmPaid(options, RUN_CONFIRMATION_LABELS[functionName]);
|
|
1737
|
-
}
|
|
1738
|
-
const estimate = await enforceRunMaxCredits(config, options, functionName, parameters, payload);
|
|
1799
|
+
applyContextToAgentPayload(payload, parameters, context, tool);
|
|
1800
|
+
const estimate = await enforceRunMaxCredits(config, options, functionName, parameters, payload, tool);
|
|
1739
1801
|
const result = normalizeAgentExecuteResult(await apiFetch(config, options, 'POST', '/api/v1/agent/execute', payload));
|
|
1740
1802
|
const outputResult = estimate && result && typeof result === 'object' && !Array.isArray(result) && result.estimate === undefined
|
|
1741
1803
|
? { ...result, estimate }
|
|
@@ -2952,7 +3014,6 @@ function defaultExportStartBody(clipId) {
|
|
|
2952
3014
|
audioSampleRate: 48000,
|
|
2953
3015
|
},
|
|
2954
3016
|
format: 'mp4',
|
|
2955
|
-
includeAudio: true,
|
|
2956
3017
|
};
|
|
2957
3018
|
}
|
|
2958
3019
|
|
|
@@ -3046,6 +3107,43 @@ async function exportsCommand(config, options, action, args) {
|
|
|
3046
3107
|
throw Object.assign(new Error(`Unknown exports command: ${action || ''}`), { exitCode: EXIT.USAGE });
|
|
3047
3108
|
}
|
|
3048
3109
|
|
|
3110
|
+
function enterpriseClientSelectionUrl(config, options, selectionPath = '/enterprise?tab=delivered') {
|
|
3111
|
+
return new URL(selectionPath, getBaseUrl(config, options)).toString();
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
async function deliverables(config, options, action) {
|
|
3115
|
+
if (action === 'list') {
|
|
3116
|
+
const result = await apiFetch(config, options, 'GET', `/api/v1/deliverables${queryString({
|
|
3117
|
+
status: options.status,
|
|
3118
|
+
exportId: options['export-id'],
|
|
3119
|
+
limit: options.limit,
|
|
3120
|
+
offset: options.offset,
|
|
3121
|
+
})}`);
|
|
3122
|
+
output({
|
|
3123
|
+
...result,
|
|
3124
|
+
clientSelectionUrl: enterpriseClientSelectionUrl(config, options, result.clientSelectionPath),
|
|
3125
|
+
}, options);
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
if (action === 'create') {
|
|
3129
|
+
const exportId = requiredString(options['export-id'], '--export-id');
|
|
3130
|
+
const title = requiredString(options.title, '--title');
|
|
3131
|
+
requireConfirm(options, 'Delivering an export to the enterprise client');
|
|
3132
|
+
const result = await apiFetch(config, options, 'POST', '/api/v1/deliverables', {
|
|
3133
|
+
exportId,
|
|
3134
|
+
title,
|
|
3135
|
+
note: options.note,
|
|
3136
|
+
});
|
|
3137
|
+
output({
|
|
3138
|
+
...result,
|
|
3139
|
+
clientSelectionPath: '/enterprise?tab=delivered',
|
|
3140
|
+
clientSelectionUrl: enterpriseClientSelectionUrl(config, options),
|
|
3141
|
+
}, options);
|
|
3142
|
+
return;
|
|
3143
|
+
}
|
|
3144
|
+
throw Object.assign(new Error(`Unknown deliverables command: ${action || ''}`), { exitCode: EXIT.USAGE });
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3049
3147
|
async function putSignedUpload(uploadUrl, filePath, contentType, size, options, requiredHeaders) {
|
|
3050
3148
|
const headers = requiredHeaders && typeof requiredHeaders === 'object'
|
|
3051
3149
|
? Object.fromEntries(Object.entries(requiredHeaders).map(([key, value]) => [key, String(value)]))
|
|
@@ -3292,6 +3390,41 @@ function socialAccountIdPins(value) {
|
|
|
3292
3390
|
}));
|
|
3293
3391
|
}
|
|
3294
3392
|
|
|
3393
|
+
async function resolveEnterpriseSelectedDelivery(config, options, clipId, exportId) {
|
|
3394
|
+
if (profileData(config, options).scope?.enterprise !== true) return null;
|
|
3395
|
+
const result = await apiFetch(
|
|
3396
|
+
config,
|
|
3397
|
+
options,
|
|
3398
|
+
'GET',
|
|
3399
|
+
`/api/v1/deliverables${queryString({ exportId, limit: 100, offset: 0 })}`,
|
|
3400
|
+
);
|
|
3401
|
+
const deliveries = Array.isArray(result?.deliverables) ? result.deliverables : [];
|
|
3402
|
+
const selected = deliveries.find((delivery) => (
|
|
3403
|
+
delivery?.clipId === clipId
|
|
3404
|
+
&& delivery?.exportId === exportId
|
|
3405
|
+
&& delivery?.status === 'selected'
|
|
3406
|
+
));
|
|
3407
|
+
if (selected?.id) return selected;
|
|
3408
|
+
const current = deliveries.find((delivery) => (
|
|
3409
|
+
delivery?.clipId === clipId && delivery?.exportId === exportId
|
|
3410
|
+
)) ?? null;
|
|
3411
|
+
const clientSelectionPath = result?.clientSelectionPath || '/enterprise?tab=delivered';
|
|
3412
|
+
throw Object.assign(
|
|
3413
|
+
new Error('The client has not selected this delivered export for publishing.'),
|
|
3414
|
+
{
|
|
3415
|
+
exitCode: EXIT.USAGE,
|
|
3416
|
+
data: {
|
|
3417
|
+
clipId,
|
|
3418
|
+
exportId,
|
|
3419
|
+
currentDelivery: current,
|
|
3420
|
+
clientSelectionPath,
|
|
3421
|
+
clientSelectionUrl: enterpriseClientSelectionUrl(config, options, clientSelectionPath),
|
|
3422
|
+
nextCommand: `clipit deliverables list --export-id ${shellQuote(exportId)} --json`,
|
|
3423
|
+
},
|
|
3424
|
+
},
|
|
3425
|
+
);
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3295
3428
|
async function socialPostBody(config, options, scheduled) {
|
|
3296
3429
|
const clipId = requiredString(options['clip-id'], '--clip-id');
|
|
3297
3430
|
const platforms = socialPlatformList(options.platforms);
|
|
@@ -3308,6 +3441,12 @@ async function socialPostBody(config, options, scheduled) {
|
|
|
3308
3441
|
requestedExportId,
|
|
3309
3442
|
requireReadyToPublish: true,
|
|
3310
3443
|
});
|
|
3444
|
+
const enterpriseDelivery = await resolveEnterpriseSelectedDelivery(
|
|
3445
|
+
config,
|
|
3446
|
+
options,
|
|
3447
|
+
clipId,
|
|
3448
|
+
selectedExport.exportId,
|
|
3449
|
+
);
|
|
3311
3450
|
const accountsResponse = await apiFetch(config, options, 'GET', '/api/v1/social/accounts');
|
|
3312
3451
|
const connectedAccounts = Array.isArray(accountsResponse?.accounts)
|
|
3313
3452
|
? accountsResponse.accounts.filter((account) => account?.connected && typeof account?.accountId === 'string')
|
|
@@ -3343,6 +3482,7 @@ async function socialPostBody(config, options, scheduled) {
|
|
|
3343
3482
|
expectedSnapshotId: editorState.snapshotId,
|
|
3344
3483
|
expectedOutputObjectFingerprint: selectedExport.outputObjectFingerprint,
|
|
3345
3484
|
expectedAccountIds,
|
|
3485
|
+
enterpriseDeliverableId: enterpriseDelivery?.id,
|
|
3346
3486
|
publishExactCurrentArtifact: true,
|
|
3347
3487
|
});
|
|
3348
3488
|
if (scheduled) body.scheduledFor = requiredString(options.at, '--at');
|
|
@@ -3800,7 +3940,7 @@ async function handleMcpRequest(config, options, message) {
|
|
|
3800
3940
|
const result = await handleLocalMcpBillingTool(config, options, name, parameters);
|
|
3801
3941
|
return { jsonrpc: '2.0', id, result: mcpTextResult(result, Boolean(result?.error)) };
|
|
3802
3942
|
}
|
|
3803
|
-
applyContextToAgentPayload(payload, parameters, await buildContext(config, options));
|
|
3943
|
+
applyContextToAgentPayload(payload, parameters, await buildContext(config, options), tool);
|
|
3804
3944
|
if (!confirmed && mcpToolRequiresConfirmation(tool, name)) {
|
|
3805
3945
|
return {
|
|
3806
3946
|
jsonrpc: '2.0',
|
|
@@ -4343,7 +4483,7 @@ async function main() {
|
|
|
4343
4483
|
return;
|
|
4344
4484
|
}
|
|
4345
4485
|
if (!command || options.help) {
|
|
4346
|
-
console.log(usage());
|
|
4486
|
+
console.log(commandUsage(command, subcommand) ?? usage());
|
|
4347
4487
|
return;
|
|
4348
4488
|
}
|
|
4349
4489
|
if (command === 'version' || command === '--version' || command === '-v') {
|
|
@@ -4374,6 +4514,7 @@ async function main() {
|
|
|
4374
4514
|
if (command === 'billing') return billing(config, options, subcommand, rest);
|
|
4375
4515
|
if (command === 'analytics') return analytics(config, options, subcommand, rest);
|
|
4376
4516
|
if (command === 'exports') return exportsCommand(config, options, subcommand, rest);
|
|
4517
|
+
if (command === 'deliverables') return deliverables(config, options, subcommand);
|
|
4377
4518
|
if (command === 'assets') return assets(config, options, subcommand, rest);
|
|
4378
4519
|
if (command === 'thumbnails') return thumbnails(config, options, subcommand, rest);
|
|
4379
4520
|
if (command === 'broll') return broll(config, options, subcommand, rest);
|