dexto 1.14.5 → 2.0.0-beta.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/cli/auth/billing.d.ts +0 -6
- package/dist/cli/auth/billing.d.ts.map +1 -1
- package/dist/cli/auth/billing.js +0 -23
- package/dist/cli/auth/constants.d.ts +3 -31
- package/dist/cli/auth/constants.d.ts.map +1 -1
- package/dist/cli/auth/constants.js +9 -33
- package/dist/cli/auth/index.d.ts +2 -9
- package/dist/cli/auth/index.d.ts.map +1 -1
- package/dist/cli/auth/index.js +2 -7
- package/dist/cli/auth/sign-in-runtime.d.ts +12 -0
- package/dist/cli/auth/sign-in-runtime.d.ts.map +1 -0
- package/dist/cli/auth/sign-in-runtime.js +59 -0
- package/dist/cli/cloud/app-scopes.d.ts +4 -0
- package/dist/cli/cloud/app-scopes.d.ts.map +1 -0
- package/dist/cli/cloud/app-scopes.js +19 -0
- package/dist/cli/cloud/command.d.ts +143 -0
- package/dist/cli/cloud/command.d.ts.map +1 -0
- package/dist/cli/cloud/command.js +202 -0
- package/dist/cli/cloud/commands/account.d.ts +11 -0
- package/dist/cli/cloud/commands/account.d.ts.map +1 -0
- package/dist/cli/cloud/commands/account.js +128 -0
- package/dist/cli/cloud/commands/agents.d.ts +3 -0
- package/dist/cli/cloud/commands/agents.d.ts.map +1 -0
- package/dist/cli/cloud/commands/agents.js +524 -0
- package/dist/cli/cloud/commands/api.d.ts +3 -0
- package/dist/cli/cloud/commands/api.d.ts.map +1 -0
- package/dist/cli/cloud/commands/api.js +26 -0
- package/dist/cli/cloud/commands/app-files.d.ts +29 -0
- package/dist/cli/cloud/commands/app-files.d.ts.map +1 -0
- package/dist/cli/cloud/commands/app-files.js +72 -0
- package/dist/cli/cloud/commands/automations.d.ts +3 -0
- package/dist/cli/cloud/commands/automations.d.ts.map +1 -0
- package/dist/cli/cloud/commands/automations.js +232 -0
- package/dist/cli/cloud/commands/chats.d.ts +3 -0
- package/dist/cli/cloud/commands/chats.d.ts.map +1 -0
- package/dist/cli/cloud/commands/chats.js +189 -0
- package/dist/cli/cloud/commands/data-deletions.d.ts +3 -0
- package/dist/cli/cloud/commands/data-deletions.d.ts.map +1 -0
- package/dist/cli/cloud/commands/data-deletions.js +54 -0
- package/dist/cli/cloud/commands/deploys.d.ts +10 -0
- package/dist/cli/cloud/commands/deploys.d.ts.map +1 -0
- package/dist/cli/cloud/commands/deploys.js +223 -0
- package/dist/cli/cloud/commands/drive.d.ts +12 -0
- package/dist/cli/cloud/commands/drive.d.ts.map +1 -0
- package/dist/cli/cloud/commands/drive.js +320 -0
- package/dist/cli/cloud/commands/files.d.ts +24 -0
- package/dist/cli/cloud/commands/files.d.ts.map +1 -0
- package/dist/cli/cloud/commands/files.js +282 -0
- package/dist/cli/cloud/commands/find.d.ts +3 -0
- package/dist/cli/cloud/commands/find.d.ts.map +1 -0
- package/dist/cli/cloud/commands/find.js +63 -0
- package/dist/cli/cloud/commands/models.d.ts +3 -0
- package/dist/cli/cloud/commands/models.d.ts.map +1 -0
- package/dist/cli/cloud/commands/models.js +248 -0
- package/dist/cli/cloud/commands/profile.d.ts +3 -0
- package/dist/cli/cloud/commands/profile.d.ts.map +1 -0
- package/dist/cli/cloud/commands/profile.js +119 -0
- package/dist/cli/cloud/commands/projects.d.ts +5 -0
- package/dist/cli/cloud/commands/projects.d.ts.map +1 -0
- package/dist/cli/cloud/commands/projects.js +66 -0
- package/dist/cli/cloud/commands/publish.d.ts +3 -0
- package/dist/cli/cloud/commands/publish.d.ts.map +1 -0
- package/dist/cli/cloud/commands/publish.js +138 -0
- package/dist/cli/cloud/commands/sandboxes.d.ts +3 -0
- package/dist/cli/cloud/commands/sandboxes.d.ts.map +1 -0
- package/dist/cli/cloud/commands/sandboxes.js +265 -0
- package/dist/cli/cloud/commands/secrets.d.ts +3 -0
- package/dist/cli/cloud/commands/secrets.d.ts.map +1 -0
- package/dist/cli/cloud/commands/secrets.js +110 -0
- package/dist/cli/cloud/commands/skills.d.ts +3 -0
- package/dist/cli/cloud/commands/skills.d.ts.map +1 -0
- package/dist/cli/cloud/commands/skills.js +202 -0
- package/dist/cli/cloud/commands/web.d.ts +3 -0
- package/dist/cli/cloud/commands/web.d.ts.map +1 -0
- package/dist/cli/cloud/commands/web.js +53 -0
- package/dist/cli/cloud/commands/workspace-apps.d.ts +58 -0
- package/dist/cli/cloud/commands/workspace-apps.d.ts.map +1 -0
- package/dist/cli/cloud/commands/workspace-apps.js +656 -0
- package/dist/cli/cloud/commands/workspace.d.ts +36 -0
- package/dist/cli/cloud/commands/workspace.d.ts.map +1 -0
- package/dist/cli/cloud/commands/workspace.js +279 -0
- package/dist/cli/cloud/connection.d.ts +33 -0
- package/dist/cli/cloud/connection.d.ts.map +1 -0
- package/dist/cli/cloud/connection.js +38 -0
- package/dist/cli/cloud/events.d.ts +63 -0
- package/dist/cli/cloud/events.d.ts.map +1 -0
- package/dist/cli/cloud/events.js +72 -0
- package/dist/cli/cloud/host.d.ts +9 -0
- package/dist/cli/cloud/host.d.ts.map +1 -0
- package/dist/cli/cloud/host.js +105 -0
- package/dist/cli/cloud/local-chats.d.ts +73 -0
- package/dist/cli/cloud/local-chats.d.ts.map +1 -0
- package/dist/cli/cloud/local-chats.js +441 -0
- package/dist/cli/cloud/output.d.ts +34 -0
- package/dist/cli/cloud/output.d.ts.map +1 -0
- package/dist/cli/cloud/output.js +76 -0
- package/dist/cli/cloud/run.d.ts +26 -0
- package/dist/cli/cloud/run.d.ts.map +1 -0
- package/dist/cli/cloud/run.js +444 -0
- package/dist/cli/cloud/runtime.d.ts +33 -0
- package/dist/cli/cloud/runtime.d.ts.map +1 -0
- package/dist/cli/cloud/runtime.js +86 -0
- package/dist/cli/cloud/tar.d.ts +9 -0
- package/dist/cli/cloud/tar.d.ts.map +1 -0
- package/dist/cli/cloud/tar.js +69 -0
- package/dist/cli/cloud/transfer.d.ts +13 -0
- package/dist/cli/cloud/transfer.d.ts.map +1 -0
- package/dist/cli/cloud/transfer.js +35 -0
- package/dist/cli/cloud/v1.d.ts +19 -0
- package/dist/cli/cloud/v1.d.ts.map +1 -0
- package/dist/cli/cloud/v1.js +65 -0
- package/dist/cli/cloud/zip.d.ts +8 -0
- package/dist/cli/cloud/zip.d.ts.map +1 -0
- package/dist/cli/cloud/zip.js +66 -0
- package/dist/cli/commands/auth/sign-in-commands.d.ts +62 -0
- package/dist/cli/commands/auth/sign-in-commands.d.ts.map +1 -0
- package/dist/cli/commands/auth/sign-in-commands.js +247 -0
- package/dist/cli/commands/auth/sign-in-context.d.ts +16 -0
- package/dist/cli/commands/auth/sign-in-context.d.ts.map +1 -0
- package/dist/cli/commands/auth/sign-in-context.js +68 -0
- package/dist/cli/commands/cloud/register.d.ts +5 -0
- package/dist/cli/commands/cloud/register.d.ts.map +1 -1
- package/dist/cli/commands/cloud/register.js +42 -24
- package/dist/cli/commands/create-app.js +5 -6
- package/dist/cli/commands/create-image.d.ts.map +1 -1
- package/dist/cli/commands/create-image.js +9 -10
- package/dist/cli/commands/index.d.ts +0 -2
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +0 -2
- package/dist/cli/commands/init.d.ts +0 -2
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +5 -26
- package/dist/cli/commands/run/headless.js +2 -2
- package/dist/cli/commands/run/output.d.ts +29 -10
- package/dist/cli/commands/run/output.d.ts.map +1 -1
- package/dist/cli/commands/run/output.js +44 -22
- package/dist/cli/commands/run/register.d.ts.map +1 -1
- package/dist/cli/commands/run/register.js +21 -9
- package/dist/cli/commands/session/register.d.ts.map +1 -1
- package/dist/cli/commands/session/register.js +51 -21
- package/dist/cli/commands/session-commands.js +1 -1
- package/dist/cli/commands/setup.js +17 -14
- package/dist/cli/help-json.d.ts +22 -0
- package/dist/cli/help-json.d.ts.map +1 -0
- package/dist/cli/help-json.js +20 -0
- package/dist/cli/modes/cli.d.ts.map +1 -1
- package/dist/cli/modes/cli.js +42 -9
- package/dist/cli/modes/server.js +8 -8
- package/dist/cli/modes/web.js +3 -3
- package/dist/cli/utils/dexto-auth-check.d.ts +1 -1
- package/dist/cli/utils/dexto-auth-check.d.ts.map +1 -1
- package/dist/cli/utils/dexto-auth-check.js +4 -5
- package/dist/cli/utils/dexto-setup.d.ts +4 -5
- package/dist/cli/utils/dexto-setup.d.ts.map +1 -1
- package/dist/cli/utils/dexto-setup.js +17 -14
- package/dist/cli/utils/provider-setup.js +1 -1
- package/dist/cli/utils/scaffolding-utils.d.ts +9 -3
- package/dist/cli/utils/scaffolding-utils.d.ts.map +1 -1
- package/dist/cli/utils/scaffolding-utils.js +48 -40
- package/dist/cli/utils/version-check.d.ts.map +1 -1
- package/dist/cli/utils/version-check.js +2 -1
- package/dist/index-main.js +33 -65
- package/dist/webui/assets/index-D9xiOs62.js +1649 -0
- package/dist/webui/index.html +1 -1
- package/package.json +15 -14
- package/dist/cli/auth/api-client.d.ts +0 -125
- package/dist/cli/auth/api-client.d.ts.map +0 -1
- package/dist/cli/auth/api-client.js +0 -585
- package/dist/cli/auth/device.d.ts +0 -14
- package/dist/cli/auth/device.d.ts.map +0 -1
- package/dist/cli/auth/device.js +0 -84
- package/dist/cli/auth/dexto-api-key.d.ts +0 -34
- package/dist/cli/auth/dexto-api-key.d.ts.map +0 -1
- package/dist/cli/auth/dexto-api-key.js +0 -160
- package/dist/cli/auth/login-persistence.d.ts +0 -15
- package/dist/cli/auth/login-persistence.d.ts.map +0 -1
- package/dist/cli/auth/login-persistence.js +0 -72
- package/dist/cli/auth/oauth.d.ts +0 -39
- package/dist/cli/auth/oauth.d.ts.map +0 -1
- package/dist/cli/auth/oauth.js +0 -434
- package/dist/cli/auth/service.d.ts +0 -24
- package/dist/cli/auth/service.d.ts.map +0 -1
- package/dist/cli/auth/service.js +0 -161
- package/dist/cli/auth/types.d.ts +0 -11
- package/dist/cli/auth/types.d.ts.map +0 -1
- package/dist/cli/auth/types.js +0 -1
- package/dist/cli/cloud-chat.d.ts +0 -18
- package/dist/cli/cloud-chat.d.ts.map +0 -1
- package/dist/cli/cloud-chat.js +0 -734
- package/dist/cli/commands/auth/index.d.ts +0 -4
- package/dist/cli/commands/auth/index.d.ts.map +0 -1
- package/dist/cli/commands/auth/index.js +0 -4
- package/dist/cli/commands/auth/login.d.ts +0 -12
- package/dist/cli/commands/auth/login.d.ts.map +0 -1
- package/dist/cli/commands/auth/login.js +0 -153
- package/dist/cli/commands/auth/logout.d.ts +0 -5
- package/dist/cli/commands/auth/logout.d.ts.map +0 -1
- package/dist/cli/commands/auth/logout.js +0 -81
- package/dist/cli/commands/auth/register.d.ts +0 -3
- package/dist/cli/commands/auth/register.d.ts.map +0 -1
- package/dist/cli/commands/auth/register.js +0 -96
- package/dist/cli/commands/auth/status.d.ts +0 -2
- package/dist/cli/commands/auth/status.d.ts.map +0 -1
- package/dist/cli/commands/auth/status.js +0 -22
- package/dist/cli/commands/billing/index.d.ts +0 -2
- package/dist/cli/commands/billing/index.d.ts.map +0 -1
- package/dist/cli/commands/billing/index.js +0 -2
- package/dist/cli/commands/billing/register.d.ts +0 -3
- package/dist/cli/commands/billing/register.d.ts.map +0 -1
- package/dist/cli/commands/billing/register.js +0 -20
- package/dist/cli/commands/billing/status.d.ts +0 -8
- package/dist/cli/commands/billing/status.d.ts.map +0 -1
- package/dist/cli/commands/billing/status.js +0 -71
- package/dist/cli/commands/cloud/discovery.d.ts +0 -17
- package/dist/cli/commands/cloud/discovery.d.ts.map +0 -1
- package/dist/cli/commands/cloud/discovery.js +0 -88
- package/dist/cli/commands/deploy/client.d.ts +0 -113
- package/dist/cli/commands/deploy/client.d.ts.map +0 -1
- package/dist/cli/commands/deploy/client.js +0 -519
- package/dist/cli/commands/deploy/config.d.ts +0 -37
- package/dist/cli/commands/deploy/config.d.ts.map +0 -1
- package/dist/cli/commands/deploy/config.js +0 -144
- package/dist/cli/commands/deploy/entry-agent.d.ts +0 -3
- package/dist/cli/commands/deploy/entry-agent.d.ts.map +0 -1
- package/dist/cli/commands/deploy/entry-agent.js +0 -70
- package/dist/cli/commands/deploy/index.d.ts +0 -12
- package/dist/cli/commands/deploy/index.d.ts.map +0 -1
- package/dist/cli/commands/deploy/index.js +0 -370
- package/dist/cli/commands/deploy/links.d.ts +0 -3
- package/dist/cli/commands/deploy/links.d.ts.map +0 -1
- package/dist/cli/commands/deploy/links.js +0 -53
- package/dist/cli/commands/deploy/register.d.ts +0 -6
- package/dist/cli/commands/deploy/register.d.ts.map +0 -1
- package/dist/cli/commands/deploy/register.js +0 -128
- package/dist/cli/commands/deploy/snapshot.d.ts +0 -12
- package/dist/cli/commands/deploy/snapshot.d.ts.map +0 -1
- package/dist/cli/commands/deploy/snapshot.js +0 -76
- package/dist/cli/commands/deploy/state.d.ts +0 -13
- package/dist/cli/commands/deploy/state.d.ts.map +0 -1
- package/dist/cli/commands/deploy/state.js +0 -122
- package/dist/cli/commands/span/register.d.ts +0 -6
- package/dist/cli/commands/span/register.d.ts.map +0 -1
- package/dist/cli/commands/span/register.js +0 -25
- package/dist/cli/commands/trace/client.d.ts +0 -98
- package/dist/cli/commands/trace/client.d.ts.map +0 -1
- package/dist/cli/commands/trace/client.js +0 -150
- package/dist/cli/commands/trace/format.d.ts +0 -5
- package/dist/cli/commands/trace/format.d.ts.map +0 -1
- package/dist/cli/commands/trace/format.js +0 -58
- package/dist/cli/commands/trace/index.d.ts +0 -21
- package/dist/cli/commands/trace/index.d.ts.map +0 -1
- package/dist/cli/commands/trace/index.js +0 -49
- package/dist/cli/commands/trace/register.d.ts +0 -6
- package/dist/cli/commands/trace/register.d.ts.map +0 -1
- package/dist/cli/commands/trace/register.js +0 -71
- package/dist/webui/assets/index-DEfRFmFT.js +0 -1667
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// `cloud files`: upload, download, share and delete the project's files.
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { createReadStream, createWriteStream, openAsBlob } from 'node:fs';
|
|
4
|
+
import { stat } from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { Writable } from 'node:stream';
|
|
7
|
+
import { unwrap } from '@dexto/cloud-client';
|
|
8
|
+
import { argument, CommandFailure, flag, integerOption, PAGE_OPTIONS, pageQuery, requireConsent, stringOption, UsageError, YES_OPTION, } from '../command.js';
|
|
9
|
+
import { sendV1, v1Failure } from '../v1.js';
|
|
10
|
+
/** Larger files go up in parts, through an upload session. */
|
|
11
|
+
const ONE_REQUEST_MAX_BYTES = 80 * 1024 * 1024;
|
|
12
|
+
const COLUMNS = [
|
|
13
|
+
{ header: 'id', value: (file) => file.id },
|
|
14
|
+
{ header: 'path', value: (file) => file.path },
|
|
15
|
+
{ header: 'size', value: (file) => formatBytes(file.size_bytes) },
|
|
16
|
+
{ header: 'type', value: (file) => file.content_type },
|
|
17
|
+
{ header: 'updated', value: (file) => file.updated_at },
|
|
18
|
+
];
|
|
19
|
+
const list = {
|
|
20
|
+
options: {
|
|
21
|
+
...PAGE_OPTIONS,
|
|
22
|
+
contains: { description: 'Only paths containing this text.', type: 'string' },
|
|
23
|
+
prefix: { description: 'Only paths starting with this, such as reports/.', type: 'string' },
|
|
24
|
+
},
|
|
25
|
+
path: ['cloud', 'files', 'list'],
|
|
26
|
+
summary: "List the project's files.",
|
|
27
|
+
async run(context, input) {
|
|
28
|
+
const prefix = stringOption(input, 'prefix');
|
|
29
|
+
const contains = stringOption(input, 'contains');
|
|
30
|
+
const page = await unwrap(await (await context.client()).GET('/v1/files', {
|
|
31
|
+
params: {
|
|
32
|
+
query: {
|
|
33
|
+
...pageQuery(input),
|
|
34
|
+
...(prefix === undefined ? {} : { prefix }),
|
|
35
|
+
...(contains === undefined ? {} : { contains }),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
}));
|
|
39
|
+
context.output.list(page, COLUMNS);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const get = {
|
|
43
|
+
arguments: '<file>',
|
|
44
|
+
path: ['cloud', 'files', 'get'],
|
|
45
|
+
summary: 'Show a file by id or path.',
|
|
46
|
+
async run(context, input) {
|
|
47
|
+
const client = await context.client();
|
|
48
|
+
context.output.record(await getFile(client, await resolveFileId(client, argument(input, 0, 'file'))));
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
const upload = {
|
|
52
|
+
arguments: '<local_path> [path]',
|
|
53
|
+
options: {
|
|
54
|
+
'content-type': {
|
|
55
|
+
description: 'The media type (default from the extension).',
|
|
56
|
+
type: 'string',
|
|
57
|
+
},
|
|
58
|
+
'expires-at': {
|
|
59
|
+
description: 'Delete the file at this ISO 8601 time.',
|
|
60
|
+
type: 'string',
|
|
61
|
+
valueName: 'TIME',
|
|
62
|
+
},
|
|
63
|
+
overwrite: { description: 'Replace a file already at the path.', type: 'boolean' },
|
|
64
|
+
},
|
|
65
|
+
path: ['cloud', 'files', 'upload'],
|
|
66
|
+
summary: 'Upload a local file to a project path (default: its file name).',
|
|
67
|
+
async run(context, input) {
|
|
68
|
+
const localPath = argument(input, 0, 'local_path');
|
|
69
|
+
if (!(await stat(localPath).catch(() => null))?.isFile())
|
|
70
|
+
throw new UsageError(`${localPath} is not a file.`);
|
|
71
|
+
const body = await openAsBlob(localPath).catch((error) => {
|
|
72
|
+
throw new UsageError(`Cannot read ${localPath}: ${error instanceof Error ? error.message : 'unknown error'}.`);
|
|
73
|
+
});
|
|
74
|
+
const expiresAt = stringOption(input, 'expires-at');
|
|
75
|
+
const file = await uploadFile(context, {
|
|
76
|
+
body,
|
|
77
|
+
contentType: stringOption(input, 'content-type') ?? contentTypeFor(localPath),
|
|
78
|
+
expiresAt: expiresAt ?? null,
|
|
79
|
+
overwrite: flag(input, 'overwrite'),
|
|
80
|
+
path: input.positionals[1] ?? path.basename(localPath),
|
|
81
|
+
sha256: await hashFile(localPath),
|
|
82
|
+
});
|
|
83
|
+
context.output.result(file, `Uploaded ${file.path} (${formatBytes(file.size_bytes)}, ${file.id}).`);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
const download = {
|
|
87
|
+
arguments: '<file> [local_path]',
|
|
88
|
+
path: ['cloud', 'files', 'download'],
|
|
89
|
+
summary: 'Download a file by id or path to a local path (default: its file name), or - for stdout.',
|
|
90
|
+
async run(context, input) {
|
|
91
|
+
const client = await context.client();
|
|
92
|
+
const fileId = await resolveFileId(client, argument(input, 0, 'file'));
|
|
93
|
+
const file = await getFile(client, fileId);
|
|
94
|
+
const target = input.positionals[1] ?? path.basename(file.path);
|
|
95
|
+
const { data, response } = await client.GET('/v1/files/{file_id}/content', {
|
|
96
|
+
params: { path: { file_id: fileId } },
|
|
97
|
+
parseAs: 'stream',
|
|
98
|
+
});
|
|
99
|
+
if (!response.ok || data === undefined || data === null)
|
|
100
|
+
throw v1Failure(context, response.status, await response.text().catch(() => ''));
|
|
101
|
+
if (target === '-') {
|
|
102
|
+
const reader = data.getReader();
|
|
103
|
+
for (let chunk = await reader.read(); !chunk.done; chunk = await reader.read())
|
|
104
|
+
await context.runtime.writeBytes(chunk.value);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
await data.pipeTo(Writable.toWeb(createWriteStream(target)));
|
|
108
|
+
context.output.result({ ...file, local_path: target }, `Saved ${file.path} to ${target}.`);
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
const url = {
|
|
112
|
+
arguments: '<file>',
|
|
113
|
+
options: {
|
|
114
|
+
'expires-in': {
|
|
115
|
+
description: 'Seconds the URL works, up to 86400 (default 3600).',
|
|
116
|
+
type: 'string',
|
|
117
|
+
valueName: 'SECONDS',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
path: ['cloud', 'files', 'url'],
|
|
121
|
+
summary: 'Create a URL anyone can read the file from until it expires.',
|
|
122
|
+
async run(context, input) {
|
|
123
|
+
const client = await context.client();
|
|
124
|
+
const expiresIn = integerOption(input, 'expires-in');
|
|
125
|
+
const result = await unwrap(await client.POST('/v1/files/{file_id}/url', {
|
|
126
|
+
params: {
|
|
127
|
+
path: { file_id: await resolveFileId(client, argument(input, 0, 'file')) },
|
|
128
|
+
},
|
|
129
|
+
body: expiresIn === undefined ? {} : { expires_in: expiresIn },
|
|
130
|
+
}));
|
|
131
|
+
context.output.result(result, result.url);
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
const remove = {
|
|
135
|
+
arguments: '<file>',
|
|
136
|
+
options: YES_OPTION,
|
|
137
|
+
path: ['cloud', 'files', 'delete'],
|
|
138
|
+
summary: 'Delete a file by id or path.',
|
|
139
|
+
async run(context, input) {
|
|
140
|
+
await requireConsent(context, flag(input, 'yes'), `Deleting ${argument(input, 0, 'file')} can't be undone.`);
|
|
141
|
+
const client = await context.client();
|
|
142
|
+
const result = await unwrap(await client.DELETE('/v1/files/{file_id}', {
|
|
143
|
+
params: {
|
|
144
|
+
path: { file_id: await resolveFileId(client, argument(input, 0, 'file')) },
|
|
145
|
+
},
|
|
146
|
+
}));
|
|
147
|
+
context.output.result(result, `Deleted file ${result.id}.`);
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
async function getFile(client, fileId) {
|
|
151
|
+
return await unwrap(await client.GET('/v1/files/{file_id}', { params: { path: { file_id: fileId } } }));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Uploads a file: in one request up to 80 MiB, otherwise in parts through an upload session,
|
|
155
|
+
* aborted when a part fails.
|
|
156
|
+
*/
|
|
157
|
+
export async function uploadFile(context, upload) {
|
|
158
|
+
if (upload.body.size <= ONE_REQUEST_MAX_BYTES) {
|
|
159
|
+
const query = new URLSearchParams({ path: upload.path });
|
|
160
|
+
if (upload.overwrite)
|
|
161
|
+
query.set('overwrite', 'true');
|
|
162
|
+
if (upload.expiresAt !== null)
|
|
163
|
+
query.set('expires_at', upload.expiresAt);
|
|
164
|
+
const response = await sendV1(context, 'POST', `/v1/files?${query}`, {
|
|
165
|
+
body: upload.body,
|
|
166
|
+
headers: {
|
|
167
|
+
accept: 'application/json',
|
|
168
|
+
'content-type': upload.contentType,
|
|
169
|
+
'content-length': String(upload.body.size),
|
|
170
|
+
'x-content-sha256': upload.sha256,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
const text = await response.text();
|
|
174
|
+
if (!response.ok)
|
|
175
|
+
throw v1Failure(context, response.status, text);
|
|
176
|
+
return JSON.parse(text);
|
|
177
|
+
}
|
|
178
|
+
const client = await context.client();
|
|
179
|
+
const session = await unwrap(await client.POST('/v1/files/uploads', {
|
|
180
|
+
body: {
|
|
181
|
+
content_type: upload.contentType,
|
|
182
|
+
overwrite: upload.overwrite,
|
|
183
|
+
path: upload.path,
|
|
184
|
+
sha256: upload.sha256,
|
|
185
|
+
size_bytes: upload.body.size,
|
|
186
|
+
...(upload.expiresAt === null ? {} : { expires_at: upload.expiresAt }),
|
|
187
|
+
},
|
|
188
|
+
}));
|
|
189
|
+
const params = { params: { path: { upload_id: session.id } } };
|
|
190
|
+
try {
|
|
191
|
+
if (session.upload_url === null)
|
|
192
|
+
throw new CommandFailure('failed', 'The upload session has no upload URL.', null);
|
|
193
|
+
// `parts` lists the parts already uploaded; send every other one of `part_count`.
|
|
194
|
+
const uploaded = new Set(session.parts.map((part) => part.part_number));
|
|
195
|
+
for (let partNumber = 1; partNumber <= session.part_count; partNumber += 1) {
|
|
196
|
+
if (uploaded.has(partNumber))
|
|
197
|
+
continue;
|
|
198
|
+
const start = (partNumber - 1) * session.part_size_bytes;
|
|
199
|
+
const response = await context.runtime.fetch(`${session.upload_url}/${partNumber}`, {
|
|
200
|
+
body: upload.body.slice(start, Math.min(start + session.part_size_bytes, session.size_bytes)),
|
|
201
|
+
method: 'PUT',
|
|
202
|
+
signal: context.runtime.signal,
|
|
203
|
+
});
|
|
204
|
+
if (!response.ok)
|
|
205
|
+
throw new CommandFailure('failed', `Part ${partNumber} of the upload was refused (HTTP ${response.status}).`, null);
|
|
206
|
+
}
|
|
207
|
+
return await unwrap(await client.POST('/v1/files/uploads/{upload_id}/complete', params));
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
await client.POST('/v1/files/uploads/{upload_id}/abort', params).catch(() => undefined);
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
215
|
+
/**
|
|
216
|
+
* A file id as given, or the id of the file at a path. The API addresses files only by id, so a
|
|
217
|
+
* path is found by listing its prefix.
|
|
218
|
+
*/
|
|
219
|
+
async function resolveFileId(client, file) {
|
|
220
|
+
if (UUID.test(file))
|
|
221
|
+
return file;
|
|
222
|
+
let after;
|
|
223
|
+
for (;;) {
|
|
224
|
+
const page = await unwrap(await client.GET('/v1/files', {
|
|
225
|
+
params: {
|
|
226
|
+
query: { limit: 1000, prefix: file, ...(after === undefined ? {} : { after }) },
|
|
227
|
+
},
|
|
228
|
+
}));
|
|
229
|
+
const match = page.data.find((each) => each.path === file);
|
|
230
|
+
if (match !== undefined)
|
|
231
|
+
return match.id;
|
|
232
|
+
if (!page.has_more || page.next_after === null)
|
|
233
|
+
break;
|
|
234
|
+
after = page.next_after;
|
|
235
|
+
}
|
|
236
|
+
throw new CommandFailure('not_found', `No file at ${file}.`, null);
|
|
237
|
+
}
|
|
238
|
+
/** A local file's SHA-256, hex, read as a stream. */
|
|
239
|
+
export async function hashFile(localPath) {
|
|
240
|
+
const hash = createHash('sha256');
|
|
241
|
+
for await (const chunk of createReadStream(localPath))
|
|
242
|
+
hash.update(chunk);
|
|
243
|
+
return hash.digest('hex');
|
|
244
|
+
}
|
|
245
|
+
const CONTENT_TYPES = {
|
|
246
|
+
'.csv': 'text/csv',
|
|
247
|
+
'.gif': 'image/gif',
|
|
248
|
+
'.html': 'text/html',
|
|
249
|
+
'.jpeg': 'image/jpeg',
|
|
250
|
+
'.jpg': 'image/jpeg',
|
|
251
|
+
'.json': 'application/json',
|
|
252
|
+
'.m4v': 'video/mp4',
|
|
253
|
+
'.md': 'text/markdown',
|
|
254
|
+
'.mov': 'video/quicktime',
|
|
255
|
+
'.mp3': 'audio/mpeg',
|
|
256
|
+
'.mp4': 'video/mp4',
|
|
257
|
+
'.pdf': 'application/pdf',
|
|
258
|
+
'.png': 'image/png',
|
|
259
|
+
'.svg': 'image/svg+xml',
|
|
260
|
+
'.txt': 'text/plain',
|
|
261
|
+
'.wav': 'audio/wav',
|
|
262
|
+
'.webm': 'video/webm',
|
|
263
|
+
'.webp': 'image/webp',
|
|
264
|
+
'.zip': 'application/zip',
|
|
265
|
+
};
|
|
266
|
+
/** The media type for a local file, by its extension. */
|
|
267
|
+
export function contentTypeFor(localPath) {
|
|
268
|
+
return CONTENT_TYPES[path.extname(localPath).toLowerCase()] ?? 'application/octet-stream';
|
|
269
|
+
}
|
|
270
|
+
export function formatBytes(bytes) {
|
|
271
|
+
if (bytes < 1024)
|
|
272
|
+
return `${bytes} B`;
|
|
273
|
+
const units = ['KiB', 'MiB', 'GiB'];
|
|
274
|
+
let value = bytes / 1024;
|
|
275
|
+
let unit = 0;
|
|
276
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
277
|
+
value /= 1024;
|
|
278
|
+
unit += 1;
|
|
279
|
+
}
|
|
280
|
+
return `${value.toFixed(1)} ${units[unit]}`;
|
|
281
|
+
}
|
|
282
|
+
export const fileCommands = [list, get, upload, download, url, remove];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../../src/cli/cloud/commands/find.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2C,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAmFtF,eAAO,MAAM,YAAY,EAAE,SAAS,OAAO,EAAW,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// `find`: what to run for something you want to do, from Dexto's search, as commands.
|
|
2
|
+
import { integerOption, isJsonObject, UsageError } from '../command.js';
|
|
3
|
+
import { commandForCall } from './workspace-apps.js';
|
|
4
|
+
import { listWorkspaceTools, queryWorkspaceTool } from './workspace.js';
|
|
5
|
+
const find = {
|
|
6
|
+
arguments: '<what...>',
|
|
7
|
+
options: {
|
|
8
|
+
limit: {
|
|
9
|
+
description: 'How many to show (default 5, up to 8).',
|
|
10
|
+
type: 'string',
|
|
11
|
+
valueName: 'N',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
path: ['find'],
|
|
15
|
+
summary: "Find what to run for something you want to do: Dexto's operations, apps, skills, agents and more, each as a command.",
|
|
16
|
+
async run(context, input) {
|
|
17
|
+
const query = input.positionals.join(' ').trim();
|
|
18
|
+
if (query.length < 2)
|
|
19
|
+
throw new UsageError('Say what you want to do, such as: dexto find "share a file".');
|
|
20
|
+
const limit = integerOption(input, 'limit');
|
|
21
|
+
const page = await queryWorkspaceTool(context, 'dexto_search', {
|
|
22
|
+
query,
|
|
23
|
+
...(limit === undefined ? {} : { limit }),
|
|
24
|
+
});
|
|
25
|
+
const results = isJsonObject(page) && Array.isArray(page.results)
|
|
26
|
+
? page.results.filter(isJsonObject)
|
|
27
|
+
: [];
|
|
28
|
+
const tools = await listWorkspaceTools(context);
|
|
29
|
+
const operationsByApp = new Map();
|
|
30
|
+
const found = [];
|
|
31
|
+
for (const result of results) {
|
|
32
|
+
const call = isJsonObject(result.call) && typeof result.call.tool === 'string'
|
|
33
|
+
? {
|
|
34
|
+
arguments: isJsonObject(result.call.arguments)
|
|
35
|
+
? result.call.arguments
|
|
36
|
+
: {},
|
|
37
|
+
tool: result.call.tool,
|
|
38
|
+
}
|
|
39
|
+
: null;
|
|
40
|
+
found.push({
|
|
41
|
+
availability: String(result.availability ?? ''),
|
|
42
|
+
command: call === null
|
|
43
|
+
? null
|
|
44
|
+
: await commandForCall(context, tools, call, operationsByApp),
|
|
45
|
+
kind: String(result.sourceKind ?? ''),
|
|
46
|
+
next_step: String(result.nextStep ?? ''),
|
|
47
|
+
summary: String(result.summary ?? ''),
|
|
48
|
+
title: String(result.title ?? ''),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (context.output.json)
|
|
52
|
+
return context.output.list({ data: found }, []);
|
|
53
|
+
if (found.length === 0) {
|
|
54
|
+
context.runtime.writeOutput('Nothing found. Try other words, or run dexto workspace for your apps.\n');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
context.runtime.writeOutput(found
|
|
58
|
+
.map((each, index) => `${index + 1}. ${each.title} (${each.kind})\n ${each.command ?? each.next_step}\n`)
|
|
59
|
+
.join(''));
|
|
60
|
+
context.output.note('Fill in each <value>, and run a command with --help for its input.');
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
export const findCommands = [find];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../../src/cli/cloud/commands/models.ts"],"names":[],"mappings":"AAIA,OAAO,EAYH,KAAK,OAAO,EAGf,MAAM,eAAe,CAAC;AA4UvB,eAAO,MAAM,aAAa,EAAE,SAAS,OAAO,EAAmC,CAAC"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// `cloud models`: every model call, text or media, as a model id and its input.
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { unwrap } from '@dexto/cloud-client';
|
|
4
|
+
import { argument, BODY_OPTIONS, CommandFailure, flag, isJsonObject, PAGE_OPTIONS, pageQuery, readJsonBody, requireConsent, stringOption, UsageError, } from '../command.js';
|
|
5
|
+
const KINDS = ['text', 'image', 'video', 'audio', 'file'];
|
|
6
|
+
const MODEL_COLUMNS = [
|
|
7
|
+
{ header: 'id', value: (model) => model.id },
|
|
8
|
+
{ header: 'kind', value: (model) => model.kind },
|
|
9
|
+
{ header: 'title', value: (model) => ('media' in model ? model.media.title : model.text.name) },
|
|
10
|
+
{ header: 'price', value: (model) => price(model) },
|
|
11
|
+
];
|
|
12
|
+
const RUN_COLUMNS = [
|
|
13
|
+
{ header: 'id', value: (run) => run.id },
|
|
14
|
+
{ header: 'model', value: (run) => run.model },
|
|
15
|
+
{ header: 'kind', value: (run) => run.media_kind },
|
|
16
|
+
{ header: 'status', value: (run) => run.status },
|
|
17
|
+
{ header: 'created', value: (run) => run.created_at },
|
|
18
|
+
];
|
|
19
|
+
const list = {
|
|
20
|
+
options: {
|
|
21
|
+
for: {
|
|
22
|
+
description: 'Recommend media models for what you want to make.',
|
|
23
|
+
type: 'string',
|
|
24
|
+
valueName: 'INTENT',
|
|
25
|
+
},
|
|
26
|
+
type: {
|
|
27
|
+
description: `Only models of this kind: ${KINDS.join(', ')}.`,
|
|
28
|
+
type: 'string',
|
|
29
|
+
valueName: 'KIND',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
path: ['cloud', 'models', 'list'],
|
|
33
|
+
summary: 'List the models you can run, text and media, with their prices.',
|
|
34
|
+
async run(context, input) {
|
|
35
|
+
const type = stringOption(input, 'type');
|
|
36
|
+
if (type !== undefined && !KINDS.some((kind) => kind === type))
|
|
37
|
+
throw new UsageError(`--type must be one of ${KINDS.join(', ')}.`);
|
|
38
|
+
const intent = stringOption(input, 'for');
|
|
39
|
+
const client = await context.client();
|
|
40
|
+
const models = intent === undefined
|
|
41
|
+
? await catalog(client, type === undefined ? 'both' : type === 'text' ? 'text' : 'media')
|
|
42
|
+
: (await unwrap(await client.GET('/v1/media/recommendations', {
|
|
43
|
+
params: { query: { intent } },
|
|
44
|
+
}))).data.map(mediaModel);
|
|
45
|
+
context.output.list({ data: models.filter((model) => type === undefined || model.kind === type) }, MODEL_COLUMNS);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
const get = {
|
|
49
|
+
arguments: '<model>',
|
|
50
|
+
path: ['cloud', 'models', 'get'],
|
|
51
|
+
summary: 'Show a model: its kind, price, and what it takes.',
|
|
52
|
+
async run(context, input) {
|
|
53
|
+
const model = await findModel(await context.client(), argument(input, 0, 'model'));
|
|
54
|
+
if (context.output.json)
|
|
55
|
+
return context.output.record('media' in model ? model.media : model.text);
|
|
56
|
+
if (!('media' in model)) {
|
|
57
|
+
const capabilities = model.text.capabilities;
|
|
58
|
+
const longContext = model.text.pricing?.contextOver200kPerM ?? null;
|
|
59
|
+
return context.output.record({
|
|
60
|
+
id: model.id,
|
|
61
|
+
kind: model.kind,
|
|
62
|
+
price: price(model),
|
|
63
|
+
...(longContext === null
|
|
64
|
+
? {}
|
|
65
|
+
: {
|
|
66
|
+
long_prompt_price: `$${longContext.inputPerM} in / $${longContext.outputPerM} out per 1M tokens above ${longContext.inputTokensAbove} input tokens`,
|
|
67
|
+
}),
|
|
68
|
+
context_tokens: capabilities.maxInputTokens,
|
|
69
|
+
input: capabilities.input.join(', '),
|
|
70
|
+
output: capabilities.output.join(', '),
|
|
71
|
+
reasoning: capabilities.reasoning,
|
|
72
|
+
tools: capabilities.supportsToolCall,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const required = new Set(model.media.input.required);
|
|
76
|
+
context.output.record({
|
|
77
|
+
id: model.id,
|
|
78
|
+
kind: model.kind,
|
|
79
|
+
title: model.media.title,
|
|
80
|
+
description: model.media.description,
|
|
81
|
+
price: price(model),
|
|
82
|
+
input: model.media.input.fields
|
|
83
|
+
.map((field) => `${field.name}${required.has(field.name) ? '*' : ''} (${field.type})`)
|
|
84
|
+
.join(', '),
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
const run = {
|
|
89
|
+
arguments: '<model> [prompt...]',
|
|
90
|
+
options: {
|
|
91
|
+
...BODY_OPTIONS,
|
|
92
|
+
'no-wait': {
|
|
93
|
+
description: 'Start a media run and print its id instead of waiting for it.',
|
|
94
|
+
type: 'boolean',
|
|
95
|
+
},
|
|
96
|
+
overwrite: {
|
|
97
|
+
description: 'With --save-to, replace a file already there.',
|
|
98
|
+
type: 'boolean',
|
|
99
|
+
},
|
|
100
|
+
'save-to': {
|
|
101
|
+
description: "Keep a media run's outputs as project files: a path, or a prefix ending in /.",
|
|
102
|
+
type: 'string',
|
|
103
|
+
valueName: 'PATH',
|
|
104
|
+
},
|
|
105
|
+
system: { description: 'A system prompt, for a text model.', type: 'string' },
|
|
106
|
+
yes: {
|
|
107
|
+
description: 'Run a media model without confirming its estimated price. Pass it only once your person has agreed.',
|
|
108
|
+
type: 'boolean',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
path: ['cloud', 'models', 'run'],
|
|
112
|
+
summary: 'Run any model. A text model replies to the prompt (or stdin); a media model is priced, confirmed, then run.',
|
|
113
|
+
async run(context, input) {
|
|
114
|
+
const client = await context.client();
|
|
115
|
+
const model = await findModel(client, argument(input, 0, 'model'));
|
|
116
|
+
const prompt = input.positionals.slice(1).join(' ').trim();
|
|
117
|
+
return 'media' in model
|
|
118
|
+
? await runMedia(context, input, client, model.id, prompt)
|
|
119
|
+
: await runText(context, input, client, model.id, prompt);
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
const runs = {
|
|
123
|
+
options: PAGE_OPTIONS,
|
|
124
|
+
path: ['cloud', 'models', 'runs', 'list'],
|
|
125
|
+
summary: 'List recent media runs in the Cloud project.',
|
|
126
|
+
async run(context, input) {
|
|
127
|
+
context.output.list(await unwrap(await (await context.client()).GET('/v1/media/generations', {
|
|
128
|
+
params: { query: pageQuery(input) },
|
|
129
|
+
})), RUN_COLUMNS);
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
const runGet = {
|
|
133
|
+
arguments: '<run_id>',
|
|
134
|
+
path: ['cloud', 'models', 'runs', 'get'],
|
|
135
|
+
summary: 'Show a media run, its status and its outputs.',
|
|
136
|
+
async run(context, input) {
|
|
137
|
+
context.output.record(await getRun(await context.client(), argument(input, 0, 'run_id')));
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
async function getRun(client, id) {
|
|
141
|
+
return await unwrap(await client.GET('/v1/media/generations/{generation_id}', {
|
|
142
|
+
params: { path: { generation_id: id } },
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
async function runText(context, input, client, model, argumentPrompt) {
|
|
146
|
+
if (stringOption(input, 'data') !== undefined || stringOption(input, 'file') !== undefined)
|
|
147
|
+
throw new UsageError(`${model} is a text model: give it a prompt, not --data or --file.`);
|
|
148
|
+
// A prompt piped in, such as a file to summarise, when none is given as arguments.
|
|
149
|
+
const prompt = argumentPrompt.length > 0 || context.runtime.prompt !== null
|
|
150
|
+
? argumentPrompt
|
|
151
|
+
: (await context.runtime.readStdin()).trim();
|
|
152
|
+
if (prompt.length === 0)
|
|
153
|
+
throw new UsageError('Give the prompt as arguments, or pipe it on stdin.');
|
|
154
|
+
const system = stringOption(input, 'system');
|
|
155
|
+
const response = await unwrap(await client.POST('/v1/chat/completions', {
|
|
156
|
+
body: {
|
|
157
|
+
model,
|
|
158
|
+
messages: [
|
|
159
|
+
...(system === undefined ? [] : [{ role: 'system', content: system }]),
|
|
160
|
+
{ role: 'user', content: prompt },
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
}));
|
|
164
|
+
if (context.output.jsonContent)
|
|
165
|
+
return context.output.record(response);
|
|
166
|
+
// The operation also answers as a stream; without `stream` it is always the JSON reply.
|
|
167
|
+
if (typeof response !== 'object' || response === null || !('choices' in response))
|
|
168
|
+
throw new CommandFailure('failed', `${model} answered without a reply.`, null);
|
|
169
|
+
const content = response.choices[0]?.message.content;
|
|
170
|
+
context.runtime.writeOutput(`${typeof content === 'string' ? content : JSON.stringify(content ?? null)}\n`);
|
|
171
|
+
}
|
|
172
|
+
async function runMedia(context, input, client, model, prompt) {
|
|
173
|
+
if (stringOption(input, 'system') !== undefined)
|
|
174
|
+
throw new UsageError(`${model} is a media model: --system is for text models.`);
|
|
175
|
+
const body = await readJsonBody(context, input);
|
|
176
|
+
if (body !== null && !isJsonObject(body))
|
|
177
|
+
throw new UsageError("The model's input must be a JSON object.");
|
|
178
|
+
const modelInput = {
|
|
179
|
+
...(isJsonObject(body) ? body : {}),
|
|
180
|
+
...(prompt.length === 0 ? {} : { prompt }),
|
|
181
|
+
};
|
|
182
|
+
if (Object.keys(modelInput).length === 0)
|
|
183
|
+
throw new UsageError("Give the model's input as a prompt, --data or --file.");
|
|
184
|
+
const estimate = await unwrap(await client.POST('/v1/media/estimates', { body: { input: modelInput, model } }));
|
|
185
|
+
context.output.note(`Estimated $${estimate.amount_usd} (${estimate.confidence}): ${estimate.explanation}`);
|
|
186
|
+
await requireConsent(context, flag(input, 'yes'), `Running ${model} spends about $${estimate.amount_usd} of credits.`);
|
|
187
|
+
const saveTo = stringOption(input, 'save-to');
|
|
188
|
+
let generation = await unwrap(await client.POST('/v1/media/generations', {
|
|
189
|
+
params: { header: { 'Idempotency-Key': randomUUID() } },
|
|
190
|
+
body: {
|
|
191
|
+
estimate_id: estimate.id,
|
|
192
|
+
...(saveTo === undefined
|
|
193
|
+
? {}
|
|
194
|
+
: { overwrite: flag(input, 'overwrite'), save_to: saveTo }),
|
|
195
|
+
},
|
|
196
|
+
}));
|
|
197
|
+
if (flag(input, 'no-wait'))
|
|
198
|
+
return context.output.result(generation, `Started run ${generation.id}. Check it with dexto cloud models runs get ${generation.id}.`);
|
|
199
|
+
while (!generation.completed) {
|
|
200
|
+
await context.runtime.sleep(2000);
|
|
201
|
+
generation = await getRun(client, generation.id);
|
|
202
|
+
}
|
|
203
|
+
if (generation.error !== null)
|
|
204
|
+
throw new CommandFailure('failed', `Run ${generation.id} failed: ${generation.error.message}`, null);
|
|
205
|
+
if (context.output.json)
|
|
206
|
+
return context.output.record(generation);
|
|
207
|
+
context.runtime.writeOutput(`Run ${generation.id} ${generation.status}.\n`);
|
|
208
|
+
for (const resource of generation.resources)
|
|
209
|
+
context.runtime.writeOutput(`${resource.file_path ?? resource.filename ?? resource.id}${resource.url === null ? '' : ` ${resource.url}`}\n`);
|
|
210
|
+
}
|
|
211
|
+
/** The text catalog, the media catalog, or both (text first), as one list. */
|
|
212
|
+
async function catalog(client, which) {
|
|
213
|
+
const [text, media] = await Promise.all([
|
|
214
|
+
which === 'media' ? null : unwrap(await client.GET('/v1/models')),
|
|
215
|
+
which === 'text' ? null : unwrap(await client.GET('/v1/media/models')),
|
|
216
|
+
]);
|
|
217
|
+
return [
|
|
218
|
+
...(text?.data ?? []).map((model) => ({
|
|
219
|
+
id: model.id,
|
|
220
|
+
kind: 'text',
|
|
221
|
+
text: model,
|
|
222
|
+
})),
|
|
223
|
+
...(media?.data ?? []).map(mediaModel),
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
async function findModel(client, id) {
|
|
227
|
+
const model = (await catalog(client, 'both')).find((each) => each.id === id);
|
|
228
|
+
if (model === undefined)
|
|
229
|
+
throw new UsageError(`Unknown model ${id}. List them with dexto cloud models list.`);
|
|
230
|
+
return model;
|
|
231
|
+
}
|
|
232
|
+
function mediaModel(model) {
|
|
233
|
+
return { id: model.id, kind: model.media_kind, media: model };
|
|
234
|
+
}
|
|
235
|
+
function price(model) {
|
|
236
|
+
if (!('media' in model)) {
|
|
237
|
+
const pricing = model.text.pricing;
|
|
238
|
+
if (pricing === null)
|
|
239
|
+
return '-';
|
|
240
|
+
const base = `$${pricing.inputPerM} in / $${pricing.outputPerM} out per 1M tokens`;
|
|
241
|
+
return pricing.contextOver200kPerM === null ? base : `${base}, more for long prompts`;
|
|
242
|
+
}
|
|
243
|
+
const pricing = model.media.pricing;
|
|
244
|
+
return pricing.unit_price_usd === undefined
|
|
245
|
+
? pricing.estimate_mode
|
|
246
|
+
: `$${pricing.unit_price_usd}/${pricing.unit}`;
|
|
247
|
+
}
|
|
248
|
+
export const modelCommands = [list, get, run, runs, runGet];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../../../src/cli/cloud/commands/profile.ts"],"names":[],"mappings":"AAIA,OAAO,EAIH,KAAK,OAAO,EAEf,MAAM,eAAe,CAAC;AAqIvB,eAAO,MAAM,eAAe,EAAE,SAAS,OAAO,EAAkB,CAAC"}
|