@agentuity/cli 0.0.43 → 0.0.44
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/AGENTS.md +1 -1
- package/README.md +1 -1
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/auth.d.ts +10 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/banner.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cmd/auth/api.d.ts +4 -4
- package/dist/cmd/auth/api.d.ts.map +1 -1
- package/dist/cmd/auth/index.d.ts.map +1 -1
- package/dist/cmd/auth/login.d.ts.map +1 -1
- package/dist/cmd/auth/signup.d.ts.map +1 -1
- package/dist/cmd/auth/ssh/add.d.ts +2 -0
- package/dist/cmd/auth/ssh/add.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/api.d.ts +16 -0
- package/dist/cmd/auth/ssh/api.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/delete.d.ts +2 -0
- package/dist/cmd/auth/ssh/delete.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/index.d.ts +3 -0
- package/dist/cmd/auth/ssh/index.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/list.d.ts +2 -0
- package/dist/cmd/auth/ssh/list.d.ts.map +1 -0
- package/dist/cmd/auth/whoami.d.ts.map +1 -1
- package/dist/cmd/bundle/ast.d.ts +14 -3
- package/dist/cmd/bundle/ast.d.ts.map +1 -1
- package/dist/cmd/bundle/ast.test.d.ts +2 -0
- package/dist/cmd/bundle/ast.test.d.ts.map +1 -0
- package/dist/cmd/bundle/bundler.d.ts +6 -1
- package/dist/cmd/bundle/bundler.d.ts.map +1 -1
- package/dist/cmd/bundle/file.d.ts.map +1 -1
- package/dist/cmd/bundle/fix-duplicate-exports.d.ts +2 -0
- package/dist/cmd/bundle/fix-duplicate-exports.d.ts.map +1 -0
- package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts +2 -0
- package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts.map +1 -0
- package/dist/cmd/bundle/plugin.d.ts +2 -0
- package/dist/cmd/bundle/plugin.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/domain.d.ts +17 -0
- package/dist/cmd/cloud/domain.d.ts.map +1 -0
- package/dist/cmd/cloud/index.d.ts.map +1 -1
- package/dist/cmd/cloud/resource/add.d.ts +2 -0
- package/dist/cmd/cloud/resource/add.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/delete.d.ts +2 -0
- package/dist/cmd/cloud/resource/delete.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/index.d.ts +3 -0
- package/dist/cmd/cloud/resource/index.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/list.d.ts +2 -0
- package/dist/cmd/cloud/resource/list.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/download.d.ts +2 -0
- package/dist/cmd/cloud/scp/download.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/index.d.ts +3 -0
- package/dist/cmd/cloud/scp/index.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/upload.d.ts +2 -0
- package/dist/cmd/cloud/scp/upload.d.ts.map +1 -0
- package/dist/cmd/cloud/ssh.d.ts +2 -0
- package/dist/cmd/cloud/ssh.d.ts.map +1 -0
- package/dist/cmd/dev/api.d.ts +18 -0
- package/dist/cmd/dev/api.d.ts.map +1 -0
- package/dist/cmd/dev/download.d.ts +11 -0
- package/dist/cmd/dev/download.d.ts.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/templates.d.ts +3 -0
- package/dist/cmd/dev/templates.d.ts.map +1 -0
- package/dist/cmd/env/delete.d.ts.map +1 -1
- package/dist/cmd/env/get.d.ts.map +1 -1
- package/dist/cmd/env/import.d.ts.map +1 -1
- package/dist/cmd/env/list.d.ts.map +1 -1
- package/dist/cmd/env/pull.d.ts.map +1 -1
- package/dist/cmd/env/push.d.ts.map +1 -1
- package/dist/cmd/env/set.d.ts.map +1 -1
- package/dist/cmd/profile/show.d.ts.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/delete.d.ts.map +1 -1
- package/dist/cmd/project/list.d.ts.map +1 -1
- package/dist/cmd/project/show.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.d.ts +4 -0
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/secret/delete.d.ts.map +1 -1
- package/dist/cmd/secret/get.d.ts.map +1 -1
- package/dist/cmd/secret/import.d.ts.map +1 -1
- package/dist/cmd/secret/list.d.ts.map +1 -1
- package/dist/cmd/secret/pull.d.ts.map +1 -1
- package/dist/cmd/secret/push.d.ts.map +1 -1
- package/dist/cmd/secret/set.d.ts.map +1 -1
- package/dist/config.d.ts +9 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/crypto/box.d.ts +65 -0
- package/dist/crypto/box.d.ts.map +1 -0
- package/dist/crypto/box.test.d.ts +2 -0
- package/dist/crypto/box.test.d.ts.map +1 -0
- package/dist/download.d.ts.map +1 -1
- package/dist/steps.d.ts +4 -1
- package/dist/steps.d.ts.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/tui.d.ts +31 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/types.d.ts +249 -126
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/detectSubagent.d.ts +15 -0
- package/dist/utils/detectSubagent.d.ts.map +1 -0
- package/dist/utils/zip.d.ts +7 -0
- package/dist/utils/zip.d.ts.map +1 -0
- package/package.json +11 -3
- package/src/api-errors.md +2 -2
- package/src/api.ts +12 -7
- package/src/auth.ts +116 -7
- package/src/banner.ts +13 -6
- package/src/cli.ts +695 -63
- package/src/cmd/auth/api.ts +10 -16
- package/src/cmd/auth/index.ts +2 -1
- package/src/cmd/auth/login.ts +24 -8
- package/src/cmd/auth/signup.ts +15 -11
- package/src/cmd/auth/ssh/add.ts +263 -0
- package/src/cmd/auth/ssh/api.ts +94 -0
- package/src/cmd/auth/ssh/delete.ts +102 -0
- package/src/cmd/auth/ssh/index.ts +10 -0
- package/src/cmd/auth/ssh/list.ts +74 -0
- package/src/cmd/auth/whoami.ts +13 -13
- package/src/cmd/bundle/ast.test.ts +565 -0
- package/src/cmd/bundle/ast.ts +457 -44
- package/src/cmd/bundle/bundler.ts +255 -57
- package/src/cmd/bundle/file.ts +6 -12
- package/src/cmd/bundle/fix-duplicate-exports.test.ts +387 -0
- package/src/cmd/bundle/fix-duplicate-exports.ts +204 -0
- package/src/cmd/bundle/index.ts +9 -9
- package/src/cmd/bundle/patch/aisdk.ts +1 -1
- package/src/cmd/bundle/plugin.ts +373 -53
- package/src/cmd/cloud/deploy.ts +300 -93
- package/src/cmd/cloud/domain.ts +92 -0
- package/src/cmd/cloud/index.ts +4 -1
- package/src/cmd/cloud/resource/add.ts +56 -0
- package/src/cmd/cloud/resource/delete.ts +120 -0
- package/src/cmd/cloud/resource/index.ts +11 -0
- package/src/cmd/cloud/resource/list.ts +69 -0
- package/src/cmd/cloud/scp/download.ts +59 -0
- package/src/cmd/cloud/scp/index.ts +9 -0
- package/src/cmd/cloud/scp/upload.ts +62 -0
- package/src/cmd/cloud/ssh.ts +68 -0
- package/src/cmd/dev/api.ts +46 -0
- package/src/cmd/dev/download.ts +111 -0
- package/src/cmd/dev/index.ts +360 -34
- package/src/cmd/dev/templates.ts +84 -0
- package/src/cmd/env/delete.ts +5 -20
- package/src/cmd/env/get.ts +5 -18
- package/src/cmd/env/import.ts +5 -20
- package/src/cmd/env/list.ts +5 -18
- package/src/cmd/env/pull.ts +10 -23
- package/src/cmd/env/push.ts +5 -23
- package/src/cmd/env/set.ts +5 -20
- package/src/cmd/index.ts +2 -2
- package/src/cmd/profile/show.ts +15 -6
- package/src/cmd/project/create.ts +7 -2
- package/src/cmd/project/delete.ts +75 -18
- package/src/cmd/project/download.ts +2 -2
- package/src/cmd/project/list.ts +8 -8
- package/src/cmd/project/show.ts +3 -7
- package/src/cmd/project/template-flow.ts +170 -72
- package/src/cmd/secret/delete.ts +5 -20
- package/src/cmd/secret/get.ts +5 -18
- package/src/cmd/secret/import.ts +5 -20
- package/src/cmd/secret/list.ts +5 -18
- package/src/cmd/secret/pull.ts +10 -23
- package/src/cmd/secret/push.ts +5 -23
- package/src/cmd/secret/set.ts +5 -20
- package/src/config.ts +224 -24
- package/src/crypto/box.test.ts +431 -0
- package/src/crypto/box.ts +477 -0
- package/src/download.ts +1 -0
- package/src/env-util.test.ts +1 -1
- package/src/steps.ts +65 -6
- package/src/terminal.ts +24 -23
- package/src/tui.ts +192 -61
- package/src/types.ts +291 -201
- package/src/utils/detectSubagent.ts +31 -0
- package/src/utils/zip.ts +38 -0
- package/dist/cmd/example/create-user.d.ts +0 -2
- package/dist/cmd/example/create-user.d.ts.map +0 -1
- package/dist/cmd/example/create.d.ts +0 -2
- package/dist/cmd/example/create.d.ts.map +0 -1
- package/dist/cmd/example/deploy.d.ts +0 -2
- package/dist/cmd/example/deploy.d.ts.map +0 -1
- package/dist/cmd/example/index.d.ts +0 -2
- package/dist/cmd/example/index.d.ts.map +0 -1
- package/dist/cmd/example/list.d.ts +0 -2
- package/dist/cmd/example/list.d.ts.map +0 -1
- package/dist/cmd/example/optional-auth.d.ts +0 -3
- package/dist/cmd/example/optional-auth.d.ts.map +0 -1
- package/dist/cmd/example/run-command.d.ts +0 -2
- package/dist/cmd/example/run-command.d.ts.map +0 -1
- package/dist/cmd/example/sound.d.ts +0 -3
- package/dist/cmd/example/sound.d.ts.map +0 -1
- package/dist/cmd/example/spinner.d.ts +0 -2
- package/dist/cmd/example/spinner.d.ts.map +0 -1
- package/dist/cmd/example/steps.d.ts +0 -2
- package/dist/cmd/example/steps.d.ts.map +0 -1
- package/dist/cmd/example/version.d.ts +0 -2
- package/dist/cmd/example/version.d.ts.map +0 -1
- package/src/cmd/example/create-user.ts +0 -38
- package/src/cmd/example/create.ts +0 -31
- package/src/cmd/example/deploy.ts +0 -36
- package/src/cmd/example/index.ts +0 -29
- package/src/cmd/example/list.ts +0 -32
- package/src/cmd/example/optional-auth.ts +0 -38
- package/src/cmd/example/run-command.ts +0 -45
- package/src/cmd/example/sound.ts +0 -14
- package/src/cmd/example/spinner.ts +0 -44
- package/src/cmd/example/steps.ts +0 -66
- package/src/cmd/example/version.ts +0 -13
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { listResources, deleteResources } from '@agentuity/server';
|
|
3
|
+
import enquirer from 'enquirer';
|
|
4
|
+
import { createSubcommand } from '../../../types';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
import { getCatalystAPIClient } from '../../../config';
|
|
7
|
+
|
|
8
|
+
export const deleteSubcommand = createSubcommand({
|
|
9
|
+
name: 'delete',
|
|
10
|
+
description: 'Delete cloud resource(s) for an organization',
|
|
11
|
+
aliases: ['rm', 'del', 'remove'],
|
|
12
|
+
requires: { auth: true, org: true, region: true },
|
|
13
|
+
schema: {
|
|
14
|
+
options: z.object({
|
|
15
|
+
type: z.enum(['db', 's3']).optional().describe('Resource type (db or s3)'),
|
|
16
|
+
name: z.string().optional().describe('Resource name to delete'),
|
|
17
|
+
confirm: z.boolean().optional().describe('Skip confirmation prompts'),
|
|
18
|
+
}),
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async handler(ctx) {
|
|
22
|
+
const { logger, opts, config, orgId, region, auth } = ctx;
|
|
23
|
+
|
|
24
|
+
const catalystClient = getCatalystAPIClient(config, logger, auth);
|
|
25
|
+
|
|
26
|
+
// Determine what to delete
|
|
27
|
+
let resourcesToDelete: Array<{ type: 'db' | 's3'; name: string }> = [];
|
|
28
|
+
|
|
29
|
+
if (opts.type && opts.name) {
|
|
30
|
+
// Command line arguments provided
|
|
31
|
+
resourcesToDelete = [{ type: opts.type, name: opts.name }];
|
|
32
|
+
} else {
|
|
33
|
+
// Fetch resources and prompt for selection
|
|
34
|
+
const resources = await tui.spinner({
|
|
35
|
+
message: `Fetching resources for ${orgId} in ${region}`,
|
|
36
|
+
clearOnSuccess: true,
|
|
37
|
+
callback: async () => {
|
|
38
|
+
return listResources(catalystClient, orgId, region!);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (resources.db.length === 0 && resources.s3.length === 0) {
|
|
43
|
+
tui.info('No resources found to delete');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Build choices for multi-select and resource map
|
|
48
|
+
const choices: Array<{ name: string; message: string }> = [];
|
|
49
|
+
const resourceMap = new Map<string, { type: 'db' | 's3'; name: string }>();
|
|
50
|
+
|
|
51
|
+
for (const db of resources.db) {
|
|
52
|
+
const key = `db:${db.name}`;
|
|
53
|
+
choices.push({
|
|
54
|
+
name: key,
|
|
55
|
+
message: `Database: ${db.name}`,
|
|
56
|
+
});
|
|
57
|
+
resourceMap.set(key, { type: 'db', name: db.name });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const s3 of resources.s3) {
|
|
61
|
+
const key = `s3:${s3.bucket_name}`;
|
|
62
|
+
choices.push({
|
|
63
|
+
name: key,
|
|
64
|
+
message: `Storage: ${s3.bucket_name}`,
|
|
65
|
+
});
|
|
66
|
+
resourceMap.set(key, { type: 's3', name: s3.bucket_name });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const response = await enquirer.prompt<{ resources: string[] }>({
|
|
70
|
+
type: 'multiselect',
|
|
71
|
+
name: 'resources',
|
|
72
|
+
message: 'Select resource(s) to delete:',
|
|
73
|
+
choices,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Map selected keys back to resource objects
|
|
77
|
+
resourcesToDelete = response.resources
|
|
78
|
+
.map((key) => resourceMap.get(key))
|
|
79
|
+
.filter((r): r is { type: 'db' | 's3'; name: string } => r !== undefined);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (resourcesToDelete.length === 0) {
|
|
83
|
+
tui.info('No resources selected for deletion');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Confirm deletion
|
|
88
|
+
if (!opts.confirm) {
|
|
89
|
+
const resourceNames = resourcesToDelete.map((r) => `${r.type}:${r.name}`).join(', ');
|
|
90
|
+
tui.warning(`You are about to delete: ${tui.bold(resourceNames)}`);
|
|
91
|
+
|
|
92
|
+
const confirm = await enquirer.prompt<{ confirm: boolean }>({
|
|
93
|
+
type: 'confirm',
|
|
94
|
+
name: 'confirm',
|
|
95
|
+
message: 'Are you sure you want to delete these resources?',
|
|
96
|
+
initial: false,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (!confirm.confirm) {
|
|
100
|
+
tui.info('Deletion cancelled');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Delete resources
|
|
106
|
+
const deleted = await tui.spinner({
|
|
107
|
+
message: `Deleting ${resourcesToDelete.length} resource(s)`,
|
|
108
|
+
clearOnSuccess: true,
|
|
109
|
+
callback: async () => {
|
|
110
|
+
return deleteResources(catalystClient, orgId, region!, resourcesToDelete);
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (deleted.length > 0) {
|
|
115
|
+
tui.success(`Deleted ${deleted.length} resource(s): ${deleted.join(', ')}`);
|
|
116
|
+
} else {
|
|
117
|
+
tui.error('Failed to delete resources');
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SubcommandDefinition } from '../../../types';
|
|
2
|
+
import { listSubcommand } from './list';
|
|
3
|
+
import { addSubcommand } from './add';
|
|
4
|
+
import { deleteSubcommand } from './delete';
|
|
5
|
+
|
|
6
|
+
export const resourceSubcommand: SubcommandDefinition = {
|
|
7
|
+
name: 'resource',
|
|
8
|
+
aliases: ['resources'],
|
|
9
|
+
description: 'Manage cloud resources',
|
|
10
|
+
subcommands: [listSubcommand, addSubcommand, deleteSubcommand],
|
|
11
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { listResources } from '@agentuity/server';
|
|
3
|
+
import { createSubcommand } from '../../../types';
|
|
4
|
+
import * as tui from '../../../tui';
|
|
5
|
+
import { getCatalystAPIClient } from '../../../config';
|
|
6
|
+
|
|
7
|
+
export const listSubcommand = createSubcommand({
|
|
8
|
+
name: 'list',
|
|
9
|
+
description: 'List cloud resources for an organization',
|
|
10
|
+
aliases: ['ls'],
|
|
11
|
+
requires: { auth: true, org: true, region: true },
|
|
12
|
+
schema: {
|
|
13
|
+
options: z.object({
|
|
14
|
+
format: z
|
|
15
|
+
.enum(['text', 'json'])
|
|
16
|
+
.optional()
|
|
17
|
+
.default('text')
|
|
18
|
+
.describe('Output format (text or json)'),
|
|
19
|
+
}),
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
async handler(ctx) {
|
|
23
|
+
const { logger, opts, orgId, region, config, auth } = ctx;
|
|
24
|
+
|
|
25
|
+
const catalystClient = getCatalystAPIClient(config, logger, auth);
|
|
26
|
+
|
|
27
|
+
const resources = await tui.spinner({
|
|
28
|
+
message: `Fetching resources for ${orgId} in ${region}`,
|
|
29
|
+
clearOnSuccess: true,
|
|
30
|
+
callback: async () => {
|
|
31
|
+
return listResources(catalystClient, orgId, region);
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Output based on format
|
|
36
|
+
if (opts.format === 'json') {
|
|
37
|
+
console.log(JSON.stringify(resources, null, 2));
|
|
38
|
+
} else {
|
|
39
|
+
// Text table format
|
|
40
|
+
if (resources.db.length === 0 && resources.s3.length === 0) {
|
|
41
|
+
tui.info('No resources found');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (resources.db.length > 0) {
|
|
46
|
+
tui.info(tui.bold('Databases'));
|
|
47
|
+
tui.newline();
|
|
48
|
+
for (const db of resources.db) {
|
|
49
|
+
console.log(tui.bold(db.name));
|
|
50
|
+
if (db.url) console.log(` URL: ${tui.muted(db.url)}`);
|
|
51
|
+
tui.newline();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (resources.s3.length > 0) {
|
|
56
|
+
tui.info(tui.bold('Storage'));
|
|
57
|
+
tui.newline();
|
|
58
|
+
for (const s3 of resources.s3) {
|
|
59
|
+
console.log(tui.bold(s3.bucket_name));
|
|
60
|
+
if (s3.access_key) console.log(` Access Key: ${tui.muted(s3.access_key)}`);
|
|
61
|
+
if (s3.secret_key) console.log(` Secret Key: ${tui.muted(s3.secret_key)}`);
|
|
62
|
+
if (s3.region) console.log(` Region: ${tui.muted(s3.region)}`);
|
|
63
|
+
if (s3.endpoint) console.log(` Endpoint: ${tui.muted(s3.endpoint)}`);
|
|
64
|
+
tui.newline();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand } from '../../../types';
|
|
3
|
+
import * as tui from '../../../tui';
|
|
4
|
+
import { getIONHost } from '../../../config';
|
|
5
|
+
|
|
6
|
+
const args = z.object({
|
|
7
|
+
source: z.string().describe('the source file'),
|
|
8
|
+
destination: z.string().optional().describe('the destination file'),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const options = z.object({
|
|
12
|
+
identifier: z.string().optional().describe('The project or deployment id to use'),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const downloadCommand = createSubcommand({
|
|
16
|
+
name: 'download',
|
|
17
|
+
aliases: ['get'],
|
|
18
|
+
description: 'Download a file using security copy',
|
|
19
|
+
requires: { apiClient: true, auth: true },
|
|
20
|
+
optional: { project: true },
|
|
21
|
+
schema: { args, options },
|
|
22
|
+
|
|
23
|
+
async handler(ctx) {
|
|
24
|
+
const { apiClient, args, opts, project, projectDir, config } = ctx;
|
|
25
|
+
|
|
26
|
+
let identifier = opts?.identifier ?? project?.projectId;
|
|
27
|
+
|
|
28
|
+
if (!identifier) {
|
|
29
|
+
identifier = await tui.showProjectList(apiClient, true);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const hostname = getIONHost(config);
|
|
33
|
+
const destination = args.destination ?? projectDir;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const spawn = Bun.spawn({
|
|
37
|
+
cmd: ['scp', `${identifier}@${hostname}:${args.source}`, destination],
|
|
38
|
+
cwd: projectDir,
|
|
39
|
+
stdout: 'inherit',
|
|
40
|
+
stderr: 'inherit',
|
|
41
|
+
stdin: 'inherit',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await spawn.exited;
|
|
45
|
+
|
|
46
|
+
if (spawn.exitCode !== 0) {
|
|
47
|
+
tui.error(
|
|
48
|
+
`SCP download failed: ${identifier}@${hostname}:${args.source} -> ${destination} (exit code: ${spawn.exitCode})`
|
|
49
|
+
);
|
|
50
|
+
process.exit(spawn.exitCode ?? 1);
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
tui.error(
|
|
54
|
+
`SCP download error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
55
|
+
);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SubcommandDefinition } from '../../../types';
|
|
2
|
+
import { downloadCommand } from './download';
|
|
3
|
+
import { uploadCommand } from './upload';
|
|
4
|
+
|
|
5
|
+
export const scpSubcommand: SubcommandDefinition = {
|
|
6
|
+
name: 'scp',
|
|
7
|
+
description: 'Secure Copy commands',
|
|
8
|
+
subcommands: [downloadCommand, uploadCommand],
|
|
9
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand } from '../../../types';
|
|
3
|
+
import * as tui from '../../../tui';
|
|
4
|
+
import { getIONHost } from '../../../config';
|
|
5
|
+
|
|
6
|
+
const args = z.object({
|
|
7
|
+
source: z.string().describe('the source file'),
|
|
8
|
+
destination: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('the destination file (defaults to . for current directory on remote)'),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const options = z.object({
|
|
15
|
+
identifier: z.string().optional().describe('The project or deployment id to use'),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const uploadCommand = createSubcommand({
|
|
19
|
+
name: 'upload',
|
|
20
|
+
aliases: ['cp', 'put'],
|
|
21
|
+
description: 'Upload a file using security copy',
|
|
22
|
+
requires: { apiClient: true, auth: true },
|
|
23
|
+
schema: { args, options },
|
|
24
|
+
optional: { project: true },
|
|
25
|
+
|
|
26
|
+
async handler(ctx) {
|
|
27
|
+
const { apiClient, args, opts, project, projectDir, config } = ctx;
|
|
28
|
+
|
|
29
|
+
let identifier = opts?.identifier ?? project?.projectId;
|
|
30
|
+
|
|
31
|
+
if (!identifier) {
|
|
32
|
+
identifier = await tui.showProjectList(apiClient, true);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const hostname = getIONHost(config);
|
|
36
|
+
const destination = args.destination ?? '.';
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const spawn = Bun.spawn({
|
|
40
|
+
cmd: ['scp', args.source, `${identifier}@${hostname}:${destination}`],
|
|
41
|
+
cwd: projectDir,
|
|
42
|
+
stdout: 'inherit',
|
|
43
|
+
stderr: 'inherit',
|
|
44
|
+
stdin: 'inherit',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await spawn.exited;
|
|
48
|
+
|
|
49
|
+
if (spawn.exitCode !== 0) {
|
|
50
|
+
tui.error(
|
|
51
|
+
`SCP upload failed: ${args.source} -> ${identifier}@${hostname}:${destination} (exit code: ${spawn.exitCode})`
|
|
52
|
+
);
|
|
53
|
+
process.exit(spawn.exitCode ?? 1);
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
tui.error(
|
|
57
|
+
`SCP upload error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
58
|
+
);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand } from '../../types';
|
|
3
|
+
import * as tui from '../../tui';
|
|
4
|
+
import { getIONHost } from '../../config';
|
|
5
|
+
|
|
6
|
+
const args = z.object({
|
|
7
|
+
identifier: z.string().optional().describe('The project or deployment id to use'),
|
|
8
|
+
command: z.string().optional().describe('The command to run'),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const options = z.object({
|
|
12
|
+
show: z.boolean().optional().describe('Show the command and exit'),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const sshSubcommand = createSubcommand({
|
|
16
|
+
name: 'ssh',
|
|
17
|
+
description: 'SSH into a cloud project',
|
|
18
|
+
toplevel: true,
|
|
19
|
+
requires: { auth: true, apiClient: true },
|
|
20
|
+
optional: { project: true },
|
|
21
|
+
schema: { args, options },
|
|
22
|
+
|
|
23
|
+
async handler(ctx) {
|
|
24
|
+
const { apiClient, project, projectDir, args, config, opts } = ctx;
|
|
25
|
+
|
|
26
|
+
let projectId = project?.projectId;
|
|
27
|
+
let identifier = args?.identifier;
|
|
28
|
+
let command = args?.command;
|
|
29
|
+
|
|
30
|
+
if (!(identifier?.startsWith('proj_') || identifier?.startsWith('deploy_'))) {
|
|
31
|
+
command = identifier;
|
|
32
|
+
identifier = undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!projectId && !identifier) {
|
|
36
|
+
projectId = await tui.showProjectList(apiClient, true);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const hostname = getIONHost(config);
|
|
40
|
+
|
|
41
|
+
const cmd = ['ssh', `${identifier ?? projectId}@${hostname}`, command].filter(
|
|
42
|
+
Boolean
|
|
43
|
+
) as string[];
|
|
44
|
+
|
|
45
|
+
// if show is passed, just show the SSH command and exit
|
|
46
|
+
if (opts?.show) {
|
|
47
|
+
if (command) {
|
|
48
|
+
// if we have a command we want to show it quoted
|
|
49
|
+
console.log(cmd[0], cmd[1], `'${cmd.slice(2).join(' ')}'`);
|
|
50
|
+
} else {
|
|
51
|
+
console.log(cmd.join(' '));
|
|
52
|
+
}
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const spawn = Bun.spawn({
|
|
57
|
+
cmd,
|
|
58
|
+
cwd: projectDir,
|
|
59
|
+
stdout: 'inherit',
|
|
60
|
+
stderr: 'inherit',
|
|
61
|
+
stdin: 'inherit',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await spawn.exited;
|
|
65
|
+
|
|
66
|
+
process.exit(spawn.exitCode);
|
|
67
|
+
},
|
|
68
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { APIResponseSchema } from '@agentuity/server';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { APIClient } from '../../api';
|
|
4
|
+
|
|
5
|
+
const DevmodeRequestSchema = z.object({
|
|
6
|
+
hostname: z.string().optional().describe('the hostname for the endpoint'),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
type DevmodeRequest = z.infer<typeof DevmodeRequestSchema>;
|
|
10
|
+
|
|
11
|
+
const DevmodeResponseSchema = z.object({
|
|
12
|
+
id: z.string(),
|
|
13
|
+
hostname: z.string(),
|
|
14
|
+
});
|
|
15
|
+
export type DevmodeResponse = z.infer<typeof DevmodeResponseSchema>;
|
|
16
|
+
|
|
17
|
+
const DevmodeResponseAPISchema = APIResponseSchema(DevmodeResponseSchema);
|
|
18
|
+
type DevmodeResponseAPI = z.infer<typeof DevmodeResponseAPISchema>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Generate an Endpoint ID and Hostname
|
|
22
|
+
*
|
|
23
|
+
* @param apiClient the api client to use
|
|
24
|
+
* @param projectId the project id
|
|
25
|
+
* @param hostname the hostname is already configured
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
export async function generateEndpoint(
|
|
29
|
+
apiClient: APIClient,
|
|
30
|
+
projectId: string,
|
|
31
|
+
hostname?: string
|
|
32
|
+
): Promise<DevmodeResponse> {
|
|
33
|
+
const resp = await apiClient.request<DevmodeResponseAPI, DevmodeRequest>(
|
|
34
|
+
'POST',
|
|
35
|
+
`/cli/devmode/2/${projectId}`,
|
|
36
|
+
DevmodeResponseAPISchema,
|
|
37
|
+
{ hostname },
|
|
38
|
+
DevmodeRequestSchema
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (!resp.success) {
|
|
42
|
+
throw new Error(resp.message);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return resp.data;
|
|
46
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync, createReadStream, mkdirSync, rmSync } from 'node:fs';
|
|
3
|
+
import { tmpdir, platform } from 'node:os';
|
|
4
|
+
import { join, dirname } from 'node:path';
|
|
5
|
+
import * as tar from 'tar';
|
|
6
|
+
import { downloadRelease } from '@terascope/fetch-github-release';
|
|
7
|
+
import { spinner } from '../../tui';
|
|
8
|
+
|
|
9
|
+
const user = 'agentuity';
|
|
10
|
+
const repo = 'gravity';
|
|
11
|
+
|
|
12
|
+
function filterRelease(release: { prerelease: boolean }) {
|
|
13
|
+
// Filter out prereleases.
|
|
14
|
+
return release.prerelease === false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function filterAsset(asset: { name: string }): boolean {
|
|
18
|
+
// Filter out the release matching our os and architecture
|
|
19
|
+
let arch: string = process.arch;
|
|
20
|
+
if (arch === 'x64') {
|
|
21
|
+
arch = 'x86_64';
|
|
22
|
+
}
|
|
23
|
+
return asset.name.includes(arch) && asset.name.includes(platform());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface GravityClient {
|
|
27
|
+
filename: string;
|
|
28
|
+
version: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @returns full path to the downloaded file
|
|
34
|
+
*/
|
|
35
|
+
export async function download(gravityDir: string): Promise<GravityClient> {
|
|
36
|
+
const outputdir = join(tmpdir(), randomUUID());
|
|
37
|
+
|
|
38
|
+
const res = (await spinner({
|
|
39
|
+
message: 'Checking Agentuity Gravity',
|
|
40
|
+
callback: async () => {
|
|
41
|
+
return downloadRelease(
|
|
42
|
+
user,
|
|
43
|
+
repo,
|
|
44
|
+
outputdir,
|
|
45
|
+
filterRelease,
|
|
46
|
+
filterAsset,
|
|
47
|
+
false,
|
|
48
|
+
true,
|
|
49
|
+
true,
|
|
50
|
+
''
|
|
51
|
+
);
|
|
52
|
+
},
|
|
53
|
+
clearOnSuccess: true,
|
|
54
|
+
})) as { release: string; assetFileNames: string[] };
|
|
55
|
+
|
|
56
|
+
const versionTok = res.release.split('@');
|
|
57
|
+
const version = versionTok[1];
|
|
58
|
+
const releaseFilename = join(gravityDir, version, 'gravity');
|
|
59
|
+
const mustDownload = !existsSync(releaseFilename);
|
|
60
|
+
|
|
61
|
+
if (!mustDownload) {
|
|
62
|
+
return { filename: releaseFilename, version };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const downloadedFile = await spinner({
|
|
66
|
+
message: `Downloading Gravity ${version}`,
|
|
67
|
+
callback: async () => {
|
|
68
|
+
const res = (await downloadRelease(
|
|
69
|
+
user,
|
|
70
|
+
repo,
|
|
71
|
+
outputdir,
|
|
72
|
+
filterRelease,
|
|
73
|
+
filterAsset,
|
|
74
|
+
false,
|
|
75
|
+
true,
|
|
76
|
+
false,
|
|
77
|
+
''
|
|
78
|
+
)) as string[];
|
|
79
|
+
return res[0] as string;
|
|
80
|
+
},
|
|
81
|
+
clearOnSuccess: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (downloadedFile.endsWith('.tar.gz')) {
|
|
85
|
+
await spinner({
|
|
86
|
+
message: 'Extracting release',
|
|
87
|
+
callback: async () => {
|
|
88
|
+
return new Promise<void>((resolve, reject) => {
|
|
89
|
+
const input = createReadStream(downloadedFile);
|
|
90
|
+
const downloadDir = dirname(releaseFilename);
|
|
91
|
+
if (!existsSync(downloadDir)) {
|
|
92
|
+
mkdirSync(downloadDir, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
input.on('finish', resolve);
|
|
95
|
+
input.on('end', resolve);
|
|
96
|
+
input.on('error', reject);
|
|
97
|
+
input.pipe(tar.x({ C: downloadDir, chmod: true }));
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
clearOnSuccess: true,
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
// TODO:
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (existsSync(outputdir)) {
|
|
107
|
+
rmSync(outputdir, { recursive: true });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { filename: releaseFilename, version };
|
|
111
|
+
}
|