@agentuity/cli 0.1.12 → 0.1.14
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/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +6 -2
- package/dist/auth.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +44 -91
- package/dist/cli.js.map +1 -1
- package/dist/cmd/auth/index.d.ts.map +1 -1
- package/dist/cmd/auth/index.js +3 -0
- package/dist/cmd/auth/index.js.map +1 -1
- package/dist/cmd/auth/org/index.d.ts +2 -0
- package/dist/cmd/auth/org/index.d.ts.map +1 -0
- package/dist/cmd/auth/org/index.js +121 -0
- package/dist/cmd/auth/org/index.js.map +1 -0
- package/dist/cmd/build/vite/beacon-plugin.d.ts +19 -0
- package/dist/cmd/build/vite/beacon-plugin.d.ts.map +1 -0
- package/dist/cmd/build/vite/beacon-plugin.js +137 -0
- package/dist/cmd/build/vite/beacon-plugin.js.map +1 -0
- package/dist/cmd/build/vite/vite-builder.d.ts +2 -0
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +12 -2
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/webanalytics-generator.js +25 -9
- package/dist/cmd/build/webanalytics-generator.js.map +1 -1
- package/dist/cmd/cloud/db/get.d.ts.map +1 -1
- package/dist/cmd/cloud/db/get.js +7 -0
- package/dist/cmd/cloud/db/get.js.map +1 -1
- package/dist/cmd/cloud/db/list.d.ts.map +1 -1
- package/dist/cmd/cloud/db/list.js +19 -6
- package/dist/cmd/cloud/db/list.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +24 -1
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
- package/dist/cmd/cloud/deployment/show.js +5 -0
- package/dist/cmd/cloud/deployment/show.js.map +1 -1
- package/dist/cmd/cloud/index.d.ts.map +1 -1
- package/dist/cmd/cloud/index.js +3 -0
- package/dist/cmd/cloud/index.js.map +1 -1
- package/dist/cmd/cloud/region/index.d.ts +2 -0
- package/dist/cmd/cloud/region/index.d.ts.map +1 -0
- package/dist/cmd/cloud/region/index.js +136 -0
- package/dist/cmd/cloud/region/index.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/build.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/build.js +35 -5
- package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
- package/dist/cmd/cloud/scp/download.d.ts.map +1 -1
- package/dist/cmd/cloud/scp/download.js +4 -2
- package/dist/cmd/cloud/scp/download.js.map +1 -1
- package/dist/cmd/cloud/scp/upload.d.ts.map +1 -1
- package/dist/cmd/cloud/scp/upload.js +4 -2
- package/dist/cmd/cloud/scp/upload.js.map +1 -1
- package/dist/cmd/cloud/ssh.d.ts.map +1 -1
- package/dist/cmd/cloud/ssh.js +3 -1
- package/dist/cmd/cloud/ssh.js.map +1 -1
- package/dist/cmd/cloud/storage/get.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/get.js +12 -5
- package/dist/cmd/cloud/storage/get.js.map +1 -1
- package/dist/cmd/cloud/storage/list.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/list.js +10 -0
- package/dist/cmd/cloud/storage/list.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +62 -5
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/help/index.d.ts.map +1 -1
- package/dist/cmd/help/index.js +8 -18
- package/dist/cmd/help/index.js.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/create.js +10 -7
- package/dist/cmd/project/create.js.map +1 -1
- package/dist/cmd/project/import.d.ts +2 -0
- package/dist/cmd/project/import.d.ts.map +1 -0
- package/dist/cmd/project/import.js +88 -0
- package/dist/cmd/project/import.js.map +1 -0
- package/dist/cmd/project/index.d.ts.map +1 -1
- package/dist/cmd/project/index.js +3 -0
- package/dist/cmd/project/index.js.map +1 -1
- package/dist/cmd/project/reconcile.d.ts +67 -0
- package/dist/cmd/project/reconcile.d.ts.map +1 -0
- package/dist/cmd/project/reconcile.js +458 -0
- package/dist/cmd/project/reconcile.js.map +1 -0
- package/dist/cmd/project/template-flow.d.ts +11 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +25 -7
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/config.d.ts +8 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +50 -21
- package/dist/config.js.map +1 -1
- package/dist/legacy-check.d.ts.map +1 -1
- package/dist/legacy-check.js +8 -0
- package/dist/legacy-check.js.map +1 -1
- package/dist/program-ref.d.ts +4 -0
- package/dist/program-ref.d.ts.map +1 -0
- package/dist/program-ref.js +8 -0
- package/dist/program-ref.js.map +1 -0
- package/dist/regions.d.ts +8 -0
- package/dist/regions.d.ts.map +1 -0
- package/dist/regions.js +77 -0
- package/dist/regions.js.map +1 -0
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +5 -4
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +6 -6
- package/src/auth.ts +8 -8
- package/src/cli.ts +52 -108
- package/src/cmd/auth/index.ts +3 -0
- package/src/cmd/auth/org/index.ts +142 -0
- package/src/cmd/build/vite/beacon-plugin.ts +162 -0
- package/src/cmd/build/vite/vite-builder.ts +15 -2
- package/src/cmd/build/webanalytics-generator.ts +25 -9
- package/src/cmd/cloud/db/get.ts +7 -0
- package/src/cmd/cloud/db/list.ts +20 -6
- package/src/cmd/cloud/deploy.ts +32 -1
- package/src/cmd/cloud/deployment/show.ts +5 -0
- package/src/cmd/cloud/index.ts +3 -0
- package/src/cmd/cloud/region/index.ts +157 -0
- package/src/cmd/cloud/sandbox/snapshot/build.ts +42 -5
- package/src/cmd/cloud/scp/download.ts +6 -2
- package/src/cmd/cloud/scp/upload.ts +6 -2
- package/src/cmd/cloud/ssh.ts +5 -1
- package/src/cmd/cloud/storage/get.ts +12 -5
- package/src/cmd/cloud/storage/list.ts +11 -0
- package/src/cmd/dev/index.ts +62 -5
- package/src/cmd/help/index.ts +8 -22
- package/src/cmd/project/create.ts +10 -7
- package/src/cmd/project/import.ts +98 -0
- package/src/cmd/project/index.ts +3 -0
- package/src/cmd/project/reconcile.ts +606 -0
- package/src/cmd/project/template-flow.ts +37 -7
- package/src/config.ts +58 -22
- package/src/legacy-check.ts +10 -0
- package/src/program-ref.ts +11 -0
- package/src/regions.ts +95 -0
- package/src/tui.ts +6 -4
- package/src/types.ts +1 -0
package/src/cmd/cloud/db/list.ts
CHANGED
|
@@ -13,6 +13,8 @@ const DBListResponseSchema = z.object({
|
|
|
13
13
|
description: z.string().optional().describe('Database description'),
|
|
14
14
|
url: z.string().optional().describe('Database connection URL'),
|
|
15
15
|
cloud_region: z.string().optional().describe('Cloud region where database is hosted'),
|
|
16
|
+
org_id: z.string().optional().describe('Organization ID that owns this database'),
|
|
17
|
+
org_name: z.string().optional().describe('Organization name that owns this database'),
|
|
16
18
|
})
|
|
17
19
|
)
|
|
18
20
|
.describe('List of database resources'),
|
|
@@ -65,6 +67,10 @@ export const listSubcommand = createSubcommand({
|
|
|
65
67
|
const shouldShowCredentials = opts.showCredentials === true;
|
|
66
68
|
const shouldMask = !options.json && !shouldShowCredentials;
|
|
67
69
|
|
|
70
|
+
// Check if resources span multiple orgs
|
|
71
|
+
const uniqueOrgIds = new Set(resources.db.map((db) => db.org_id));
|
|
72
|
+
const showOrgColumn = uniqueOrgIds.size > 1;
|
|
73
|
+
|
|
68
74
|
if (!options.json) {
|
|
69
75
|
if (resources.db.length === 0) {
|
|
70
76
|
tui.info('No databases found');
|
|
@@ -73,12 +79,18 @@ export const listSubcommand = createSubcommand({
|
|
|
73
79
|
console.log(db.name);
|
|
74
80
|
}
|
|
75
81
|
} else {
|
|
76
|
-
const tableData = resources.db.map((db) =>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
const tableData = resources.db.map((db) => {
|
|
83
|
+
const row: Record<string, string> = {
|
|
84
|
+
Name: db.name,
|
|
85
|
+
};
|
|
86
|
+
if (showOrgColumn) {
|
|
87
|
+
row.Organization = db.org_name || db.org_id;
|
|
88
|
+
}
|
|
89
|
+
row.Description = db.description ?? '';
|
|
90
|
+
row.Region = db.cloud_region;
|
|
91
|
+
row.URL = db.url ? (shouldMask ? tui.maskSecret(db.url) : db.url) : '';
|
|
92
|
+
return row;
|
|
93
|
+
});
|
|
82
94
|
tui.table(tableData);
|
|
83
95
|
}
|
|
84
96
|
}
|
|
@@ -89,6 +101,8 @@ export const listSubcommand = createSubcommand({
|
|
|
89
101
|
description: db.description ?? undefined,
|
|
90
102
|
url: db.url ?? undefined,
|
|
91
103
|
cloud_region: db.cloud_region,
|
|
104
|
+
org_id: db.org_id,
|
|
105
|
+
org_name: db.org_name,
|
|
92
106
|
})),
|
|
93
107
|
};
|
|
94
108
|
},
|
package/src/cmd/cloud/deploy.ts
CHANGED
|
@@ -121,7 +121,38 @@ export const deploySubcommand = createSubcommand({
|
|
|
121
121
|
},
|
|
122
122
|
|
|
123
123
|
async handler(ctx) {
|
|
124
|
-
|
|
124
|
+
let { project } = ctx;
|
|
125
|
+
const { apiClient, projectDir, config, options, logger, opts, auth } = ctx;
|
|
126
|
+
|
|
127
|
+
// Verify project access and offer import if needed
|
|
128
|
+
const { reconcileProject } = await import('../project/reconcile');
|
|
129
|
+
const { isTTY } = await import('../../auth');
|
|
130
|
+
|
|
131
|
+
const reconcileResult = await reconcileProject({
|
|
132
|
+
dir: projectDir,
|
|
133
|
+
auth,
|
|
134
|
+
apiClient,
|
|
135
|
+
config: config!,
|
|
136
|
+
logger,
|
|
137
|
+
interactive: isTTY(),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (reconcileResult.status === 'error') {
|
|
141
|
+
tui.fatal(reconcileResult.message!, ErrorCode.PROJECT_NOT_FOUND);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (reconcileResult.status === 'skipped') {
|
|
145
|
+
tui.fatal(
|
|
146
|
+
'Project must be registered with Agentuity Cloud to deploy.',
|
|
147
|
+
ErrorCode.PROJECT_NOT_FOUND
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (reconcileResult.status === 'imported' && reconcileResult.project) {
|
|
152
|
+
// Project was imported - use the new project config
|
|
153
|
+
project = reconcileResult.project;
|
|
154
|
+
tui.newline();
|
|
155
|
+
}
|
|
125
156
|
|
|
126
157
|
// Initialize build report collector if reportFile is specified
|
|
127
158
|
const collector = new BuildReportCollector();
|
|
@@ -19,6 +19,7 @@ const DeploymentShowResponseSchema = z.object({
|
|
|
19
19
|
resourceStorage: z.string().nullable().optional().describe('the storage name'),
|
|
20
20
|
deploymentLogsURL: z.string().nullable().optional().describe('the url to the deployment logs'),
|
|
21
21
|
buildLogsURL: z.string().nullable().optional().describe('the url to the build logs'),
|
|
22
|
+
dnsRecords: z.array(z.string()).optional().describe('DNS records for custom domains'),
|
|
22
23
|
metadata: z
|
|
23
24
|
.object({
|
|
24
25
|
git: z
|
|
@@ -124,6 +125,9 @@ export const showSubcommand = createSubcommand({
|
|
|
124
125
|
if (deployment.buildLogsURL) {
|
|
125
126
|
tableData['Build Logs'] = tui.link(deployment.buildLogsURL);
|
|
126
127
|
}
|
|
128
|
+
if (deployment.dnsRecords && deployment.dnsRecords.length > 0) {
|
|
129
|
+
tableData['DNS Records'] = deployment.dnsRecords.join(', ');
|
|
130
|
+
}
|
|
127
131
|
|
|
128
132
|
tui.table([tableData], Object.keys(tableData), { layout: 'vertical', padStart: ' ' });
|
|
129
133
|
|
|
@@ -190,6 +194,7 @@ export const showSubcommand = createSubcommand({
|
|
|
190
194
|
resourceStorage: deployment.resourceStorage ?? undefined,
|
|
191
195
|
deploymentLogsURL: deployment.deploymentLogsURL ?? undefined,
|
|
192
196
|
buildLogsURL: deployment.buildLogsURL ?? undefined,
|
|
197
|
+
dnsRecords: deployment.dnsRecords ?? undefined,
|
|
193
198
|
};
|
|
194
199
|
} catch (ex) {
|
|
195
200
|
tui.fatal(`Failed to show deployment: ${ex}`);
|
package/src/cmd/cloud/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import apikeyCommand from './apikey';
|
|
|
15
15
|
import streamCommand from './stream';
|
|
16
16
|
import vectorCommand from './vector';
|
|
17
17
|
import sandboxCommand from './sandbox';
|
|
18
|
+
import { regionSubcommand } from './region';
|
|
18
19
|
import { getCommand } from '../../command-prefix';
|
|
19
20
|
|
|
20
21
|
export const command = createCommand({
|
|
@@ -24,6 +25,7 @@ export const command = createCommand({
|
|
|
24
25
|
examples: [
|
|
25
26
|
{ command: getCommand('cloud deploy'), description: 'Deploy your agent to the cloud' },
|
|
26
27
|
{ command: getCommand('cloud deployment list'), description: 'List all deployments' },
|
|
28
|
+
{ command: getCommand('cloud region select'), description: 'Set default region' },
|
|
27
29
|
],
|
|
28
30
|
subcommands: [
|
|
29
31
|
apikeyCommand,
|
|
@@ -42,5 +44,6 @@ export const command = createCommand({
|
|
|
42
44
|
sshSubcommand,
|
|
43
45
|
scpSubcommand,
|
|
44
46
|
deploymentCommand,
|
|
47
|
+
regionSubcommand,
|
|
45
48
|
],
|
|
46
49
|
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand, createCommand } from '../../../types';
|
|
3
|
+
import { getCommand } from '../../../command-prefix';
|
|
4
|
+
import { saveRegion, clearRegion } from '../../../config';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
|
|
7
|
+
const selectCommand = createSubcommand({
|
|
8
|
+
name: 'select',
|
|
9
|
+
description: 'Set the default cloud region for all commands',
|
|
10
|
+
tags: ['fast', 'requires-auth'],
|
|
11
|
+
requires: { auth: true, regions: true },
|
|
12
|
+
examples: [
|
|
13
|
+
{ command: getCommand('cloud region select'), description: 'Select default region' },
|
|
14
|
+
{
|
|
15
|
+
command: getCommand('cloud region select usc'),
|
|
16
|
+
description: 'Set specific region as default',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
schema: {
|
|
20
|
+
args: z.object({
|
|
21
|
+
region: z.string().optional().describe('Region code to set as default'),
|
|
22
|
+
}),
|
|
23
|
+
response: z.object({
|
|
24
|
+
region: z.string().describe('The selected region code'),
|
|
25
|
+
description: z.string().describe('The region description'),
|
|
26
|
+
}),
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
async handler(ctx) {
|
|
30
|
+
const { regions, args, options } = ctx;
|
|
31
|
+
|
|
32
|
+
let selectedRegion = args.region;
|
|
33
|
+
|
|
34
|
+
if (selectedRegion) {
|
|
35
|
+
const region = regions.find((r) => r.region === selectedRegion);
|
|
36
|
+
if (!region) {
|
|
37
|
+
const available = regions.map((r) => r.region).join(', ');
|
|
38
|
+
tui.fatal(`Region '${selectedRegion}' not found. Available regions: ${available}`);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
if (!process.stdin.isTTY) {
|
|
42
|
+
tui.fatal(
|
|
43
|
+
'Region code required in non-interactive mode. Usage: ' +
|
|
44
|
+
getCommand('cloud region select <region>')
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (regions.length === 1) {
|
|
49
|
+
selectedRegion = regions[0].region;
|
|
50
|
+
} else {
|
|
51
|
+
const response = await tui.createPrompt().select<string>({
|
|
52
|
+
message: 'Select a default region',
|
|
53
|
+
options: regions.map((r) => ({
|
|
54
|
+
value: r.region,
|
|
55
|
+
label: `${r.description} ${tui.muted(`(${r.region})`)}`,
|
|
56
|
+
})),
|
|
57
|
+
});
|
|
58
|
+
selectedRegion = response;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
await saveRegion(selectedRegion);
|
|
63
|
+
|
|
64
|
+
const selectedRegionInfo = regions.find((r) => r.region === selectedRegion)!;
|
|
65
|
+
|
|
66
|
+
if (!options.json) {
|
|
67
|
+
tui.success(
|
|
68
|
+
`Default region set to ${tui.bold(selectedRegionInfo.description)} (${selectedRegion})`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { region: selectedRegion, description: selectedRegionInfo.description };
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const unselectCommand = createSubcommand({
|
|
77
|
+
name: 'unselect',
|
|
78
|
+
description: 'Clear the default region preference',
|
|
79
|
+
tags: ['fast'],
|
|
80
|
+
examples: [
|
|
81
|
+
{ command: getCommand('cloud region unselect'), description: 'Clear default region' },
|
|
82
|
+
],
|
|
83
|
+
schema: {
|
|
84
|
+
response: z.object({
|
|
85
|
+
cleared: z.boolean().describe('Whether the preference was cleared'),
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
async handler(ctx) {
|
|
90
|
+
const { options, config } = ctx;
|
|
91
|
+
|
|
92
|
+
const hadRegion = !!config?.preferences?.region;
|
|
93
|
+
|
|
94
|
+
if (hadRegion && process.stdin.isTTY) {
|
|
95
|
+
const confirmed = await tui.confirm('Clear default region preference?', true);
|
|
96
|
+
if (!confirmed) {
|
|
97
|
+
if (!options.json) {
|
|
98
|
+
tui.info('Cancelled');
|
|
99
|
+
}
|
|
100
|
+
return { cleared: false };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await clearRegion();
|
|
105
|
+
|
|
106
|
+
if (!options.json) {
|
|
107
|
+
if (hadRegion) {
|
|
108
|
+
tui.success('Default region cleared');
|
|
109
|
+
} else {
|
|
110
|
+
tui.info('No default region was set');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { cleared: hadRegion };
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const currentCommand = createSubcommand({
|
|
119
|
+
name: 'current',
|
|
120
|
+
description: 'Show the current default region',
|
|
121
|
+
tags: ['read-only', 'fast'],
|
|
122
|
+
idempotent: true,
|
|
123
|
+
examples: [
|
|
124
|
+
{ command: getCommand('cloud region current'), description: 'Show default region' },
|
|
125
|
+
{
|
|
126
|
+
command: getCommand('cloud region current --json'),
|
|
127
|
+
description: 'Show output in JSON format',
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
schema: {
|
|
131
|
+
response: z.string().nullable().describe('The current region code or null if not set'),
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
async handler(ctx) {
|
|
135
|
+
const { options, config } = ctx;
|
|
136
|
+
const region = config?.preferences?.region || null;
|
|
137
|
+
|
|
138
|
+
if (!options.json) {
|
|
139
|
+
if (region) {
|
|
140
|
+
console.log(region);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return region;
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
export const regionSubcommand = createCommand({
|
|
149
|
+
name: 'region',
|
|
150
|
+
description: 'Manage default cloud region preference',
|
|
151
|
+
tags: ['fast'],
|
|
152
|
+
examples: [
|
|
153
|
+
{ command: getCommand('cloud region select'), description: 'Set default region' },
|
|
154
|
+
{ command: getCommand('cloud region current'), description: 'Show current default' },
|
|
155
|
+
],
|
|
156
|
+
subcommands: [selectCommand, unselectCommand, currentCommand],
|
|
157
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { resolve, join, extname } from 'node:path';
|
|
3
|
-
import { existsSync, statSync } from 'node:fs';
|
|
3
|
+
import { existsSync, statSync, createReadStream, createWriteStream } from 'node:fs';
|
|
4
4
|
import { YAML } from 'bun';
|
|
5
5
|
import * as tar from 'tar';
|
|
6
6
|
import { createCommand } from '../../../../types';
|
|
@@ -14,8 +14,9 @@ import {
|
|
|
14
14
|
import type { SnapshotFileInfo } from '@agentuity/server';
|
|
15
15
|
import { getCatalystAPIClient } from '../../../../config';
|
|
16
16
|
import { validateAptDependencies } from '../../../../utils/apt-validator';
|
|
17
|
+
import { encryptFIPSKEMDEMStream } from '../../../../crypto/box';
|
|
17
18
|
import { tmpdir } from 'node:os';
|
|
18
|
-
import { randomUUID, createHash } from 'node:crypto';
|
|
19
|
+
import { randomUUID, createHash, createPublicKey } from 'node:crypto';
|
|
19
20
|
import { rm } from 'node:fs/promises';
|
|
20
21
|
|
|
21
22
|
export const SNAPSHOT_TAG_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
@@ -575,6 +576,7 @@ export const buildSubcommand = createCommand({
|
|
|
575
576
|
description: finalDescription,
|
|
576
577
|
contentHash,
|
|
577
578
|
force: opts.force,
|
|
579
|
+
encrypt: true,
|
|
578
580
|
orgId,
|
|
579
581
|
});
|
|
580
582
|
},
|
|
@@ -609,19 +611,54 @@ export const buildSubcommand = createCommand({
|
|
|
609
611
|
};
|
|
610
612
|
}
|
|
611
613
|
|
|
614
|
+
// Encrypt the archive if public key is provided
|
|
615
|
+
let uploadPath = archivePath;
|
|
616
|
+
let uploadSize = archiveSize;
|
|
617
|
+
|
|
618
|
+
if (initResult.publicKey) {
|
|
619
|
+
const encryptedPath = join(tempDir, 'snapshot.tar.gz.enc');
|
|
620
|
+
|
|
621
|
+
await tui.spinner({
|
|
622
|
+
message: 'Encrypting snapshot...',
|
|
623
|
+
type: 'simple',
|
|
624
|
+
clearOnSuccess: true,
|
|
625
|
+
callback: async () => {
|
|
626
|
+
const publicKey = createPublicKey({
|
|
627
|
+
key: initResult.publicKey!,
|
|
628
|
+
format: 'pem',
|
|
629
|
+
type: 'spki',
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
const src = createReadStream(archivePath);
|
|
633
|
+
const dst = createWriteStream(encryptedPath);
|
|
634
|
+
|
|
635
|
+
await encryptFIPSKEMDEMStream(publicKey, src, dst);
|
|
636
|
+
|
|
637
|
+
await new Promise<void>((resolve, reject) => {
|
|
638
|
+
dst.once('finish', resolve);
|
|
639
|
+
dst.once('error', reject);
|
|
640
|
+
dst.end();
|
|
641
|
+
});
|
|
642
|
+
},
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
uploadPath = encryptedPath;
|
|
646
|
+
uploadSize = Bun.file(encryptedPath).size;
|
|
647
|
+
}
|
|
648
|
+
|
|
612
649
|
await tui.spinner({
|
|
613
650
|
message: 'Uploading snapshot...',
|
|
614
651
|
type: 'progress',
|
|
615
652
|
clearOnSuccess: true,
|
|
616
653
|
callback: async (updateProgress) => {
|
|
617
|
-
const
|
|
654
|
+
const uploadFile = Bun.file(uploadPath);
|
|
618
655
|
const response = await fetch(initResult.uploadUrl!, {
|
|
619
656
|
method: 'PUT',
|
|
620
657
|
headers: {
|
|
621
658
|
'Content-Type': 'application/gzip',
|
|
622
|
-
'Content-Length': String(
|
|
659
|
+
'Content-Length': String(uploadSize),
|
|
623
660
|
},
|
|
624
|
-
body:
|
|
661
|
+
body: uploadFile,
|
|
625
662
|
});
|
|
626
663
|
|
|
627
664
|
if (!response.ok) {
|
|
@@ -52,7 +52,7 @@ export const downloadCommand = createSubcommand({
|
|
|
52
52
|
},
|
|
53
53
|
|
|
54
54
|
async handler(ctx) {
|
|
55
|
-
const { apiClient, args, opts, project, projectDir, config, logger, auth
|
|
55
|
+
const { apiClient, args, opts, project, projectDir, config, logger, auth } = ctx;
|
|
56
56
|
|
|
57
57
|
let identifier = opts?.identifier ?? project?.projectId;
|
|
58
58
|
|
|
@@ -60,8 +60,12 @@ export const downloadCommand = createSubcommand({
|
|
|
60
60
|
identifier = await tui.showProjectList(apiClient, true);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
// Look up region from identifier (project/deployment)
|
|
63
|
+
// Look up region from identifier (project/deployment/sandbox)
|
|
64
64
|
const profileName = config?.name;
|
|
65
|
+
|
|
66
|
+
// For sandbox identifiers, use saved org preference (no prompting)
|
|
67
|
+
const orgId = identifier.startsWith('sbx_') ? config?.preferences?.orgId : undefined;
|
|
68
|
+
|
|
65
69
|
const region = await getIdentifierRegion(
|
|
66
70
|
logger,
|
|
67
71
|
auth,
|
|
@@ -55,7 +55,7 @@ export const uploadCommand = createSubcommand({
|
|
|
55
55
|
prerequisites: ['cloud deploy'],
|
|
56
56
|
|
|
57
57
|
async handler(ctx) {
|
|
58
|
-
const { apiClient, args, opts, project, projectDir, config, logger, auth
|
|
58
|
+
const { apiClient, args, opts, project, projectDir, config, logger, auth } = ctx;
|
|
59
59
|
|
|
60
60
|
let identifier = opts?.identifier ?? project?.projectId;
|
|
61
61
|
|
|
@@ -63,8 +63,12 @@ export const uploadCommand = createSubcommand({
|
|
|
63
63
|
identifier = await tui.showProjectList(apiClient, true);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
// Look up region from identifier (project/deployment)
|
|
66
|
+
// Look up region from identifier (project/deployment/sandbox)
|
|
67
67
|
const profileName = config?.name;
|
|
68
|
+
|
|
69
|
+
// For sandbox identifiers, use saved org preference (no prompting)
|
|
70
|
+
const orgId = identifier.startsWith('sbx_') ? config?.preferences?.orgId : undefined;
|
|
71
|
+
|
|
68
72
|
const region = await getIdentifierRegion(
|
|
69
73
|
logger,
|
|
70
74
|
auth,
|
package/src/cmd/cloud/ssh.ts
CHANGED
|
@@ -46,7 +46,7 @@ export const sshSubcommand = createSubcommand({
|
|
|
46
46
|
schema: { args, options },
|
|
47
47
|
|
|
48
48
|
async handler(ctx) {
|
|
49
|
-
const { apiClient, project, projectDir, args, config, opts, logger, auth
|
|
49
|
+
const { apiClient, project, projectDir, args, config, opts, logger, auth } = ctx;
|
|
50
50
|
|
|
51
51
|
let projectId = project?.projectId;
|
|
52
52
|
let identifier = args?.identifier;
|
|
@@ -70,6 +70,10 @@ export const sshSubcommand = createSubcommand({
|
|
|
70
70
|
// Look up region from identifier (project/deployment/sandbox)
|
|
71
71
|
const profileName = config?.name;
|
|
72
72
|
const targetIdentifier = identifier ?? projectId!;
|
|
73
|
+
|
|
74
|
+
// For sandbox identifiers, use saved org preference (no prompting)
|
|
75
|
+
const orgId = targetIdentifier.startsWith('sbx_') ? config?.preferences?.orgId : undefined;
|
|
76
|
+
|
|
73
77
|
const region = await getIdentifierRegion(
|
|
74
78
|
logger,
|
|
75
79
|
auth,
|
|
@@ -13,6 +13,8 @@ const StorageGetResponseSchema = z.object({
|
|
|
13
13
|
secret_key: z.string().optional().describe('S3 secret key'),
|
|
14
14
|
region: z.string().optional().describe('S3 region'),
|
|
15
15
|
endpoint: z.string().optional().describe('S3 endpoint URL'),
|
|
16
|
+
org_id: z.string().optional().describe('Organization ID that owns this bucket'),
|
|
17
|
+
org_name: z.string().optional().describe('Organization name that owns this bucket'),
|
|
16
18
|
});
|
|
17
19
|
|
|
18
20
|
export const getSubcommand = createSubcommand({
|
|
@@ -100,24 +102,27 @@ export const getSubcommand = createSubcommand({
|
|
|
100
102
|
const shouldMask = !options.json && !shouldShowCredentials;
|
|
101
103
|
|
|
102
104
|
if (!options.json) {
|
|
103
|
-
console.log(tui.bold('Bucket Name:
|
|
105
|
+
console.log(tui.bold('Bucket Name: ') + bucket.bucket_name);
|
|
106
|
+
if (bucket.org_name || bucket.org_id) {
|
|
107
|
+
console.log(tui.bold('Organization: ') + (bucket.org_name || bucket.org_id));
|
|
108
|
+
}
|
|
104
109
|
if (bucket.access_key) {
|
|
105
110
|
const displayAccessKey = shouldMask
|
|
106
111
|
? tui.maskSecret(bucket.access_key)
|
|
107
112
|
: bucket.access_key;
|
|
108
|
-
console.log(tui.bold('Access Key:
|
|
113
|
+
console.log(tui.bold('Access Key: ') + displayAccessKey);
|
|
109
114
|
}
|
|
110
115
|
if (bucket.secret_key) {
|
|
111
116
|
const displaySecretKey = shouldMask
|
|
112
117
|
? tui.maskSecret(bucket.secret_key)
|
|
113
118
|
: bucket.secret_key;
|
|
114
|
-
console.log(tui.bold('Secret Key:
|
|
119
|
+
console.log(tui.bold('Secret Key: ') + displaySecretKey);
|
|
115
120
|
}
|
|
116
121
|
if (bucket.region) {
|
|
117
|
-
console.log(tui.bold('Region:
|
|
122
|
+
console.log(tui.bold('Region: ') + bucket.region);
|
|
118
123
|
}
|
|
119
124
|
if (bucket.endpoint) {
|
|
120
|
-
console.log(tui.bold('Endpoint:
|
|
125
|
+
console.log(tui.bold('Endpoint: ') + bucket.endpoint);
|
|
121
126
|
}
|
|
122
127
|
}
|
|
123
128
|
|
|
@@ -127,6 +132,8 @@ export const getSubcommand = createSubcommand({
|
|
|
127
132
|
secret_key: bucket.secret_key ?? undefined,
|
|
128
133
|
region: bucket.region ?? undefined,
|
|
129
134
|
endpoint: bucket.endpoint ?? undefined,
|
|
135
|
+
org_id: bucket.org_id,
|
|
136
|
+
org_name: bucket.org_name,
|
|
130
137
|
};
|
|
131
138
|
},
|
|
132
139
|
});
|
|
@@ -18,6 +18,8 @@ const StorageListResponseSchema = z.object({
|
|
|
18
18
|
region: z.string().optional().describe('S3 region'),
|
|
19
19
|
endpoint: z.string().optional().describe('S3 endpoint URL'),
|
|
20
20
|
cloud_region: z.string().optional().describe('Cloud region where bucket is hosted'),
|
|
21
|
+
org_id: z.string().optional().describe('Organization ID that owns this bucket'),
|
|
22
|
+
org_name: z.string().optional().describe('Organization name that owns this bucket'),
|
|
21
23
|
})
|
|
22
24
|
)
|
|
23
25
|
.optional()
|
|
@@ -189,6 +191,10 @@ export const listSubcommand = createSubcommand({
|
|
|
189
191
|
const shouldShowCredentials = opts.showCredentials === true;
|
|
190
192
|
const shouldMask = !options.json && !shouldShowCredentials;
|
|
191
193
|
|
|
194
|
+
// Check if resources span multiple orgs
|
|
195
|
+
const uniqueOrgIds = new Set(resources.s3.map((s3) => s3.org_id));
|
|
196
|
+
const showOrgColumn = uniqueOrgIds.size > 1;
|
|
197
|
+
|
|
192
198
|
if (!options.json) {
|
|
193
199
|
if (resources.s3.length === 0) {
|
|
194
200
|
tui.info('No storage buckets found');
|
|
@@ -203,6 +209,9 @@ export const listSubcommand = createSubcommand({
|
|
|
203
209
|
continue;
|
|
204
210
|
}
|
|
205
211
|
console.log(tui.bold(s3.bucket_name));
|
|
212
|
+
if (showOrgColumn) {
|
|
213
|
+
console.log(` Organization: ${tui.muted(s3.org_name || s3.org_id)}`);
|
|
214
|
+
}
|
|
206
215
|
if (s3.access_key) {
|
|
207
216
|
const displayAccessKey = shouldMask
|
|
208
217
|
? tui.maskSecret(s3.access_key)
|
|
@@ -230,6 +239,8 @@ export const listSubcommand = createSubcommand({
|
|
|
230
239
|
region: s3.region ?? undefined,
|
|
231
240
|
endpoint: s3.endpoint ?? undefined,
|
|
232
241
|
cloud_region: s3.cloud_region,
|
|
242
|
+
org_id: s3.org_id,
|
|
243
|
+
org_name: s3.org_name,
|
|
233
244
|
})),
|
|
234
245
|
};
|
|
235
246
|
},
|
package/src/cmd/dev/index.ts
CHANGED
|
@@ -175,8 +175,8 @@ export const command = createCommand({
|
|
|
175
175
|
optional: { project: true },
|
|
176
176
|
|
|
177
177
|
async handler(ctx) {
|
|
178
|
-
const { opts, logger,
|
|
179
|
-
let { config } = ctx;
|
|
178
|
+
const { opts, logger, projectDir } = ctx;
|
|
179
|
+
let { config, project } = ctx;
|
|
180
180
|
|
|
181
181
|
// Get auth state - we handle auth ourselves based on project state
|
|
182
182
|
let auth = await getAuth();
|
|
@@ -227,7 +227,9 @@ export const command = createCommand({
|
|
|
227
227
|
tui.newline();
|
|
228
228
|
|
|
229
229
|
const shouldLogin = await tui.confirm(
|
|
230
|
-
hasProfile
|
|
230
|
+
hasProfile
|
|
231
|
+
? 'Would you like to login now?'
|
|
232
|
+
: 'Would you like to login or create an account?',
|
|
231
233
|
true
|
|
232
234
|
);
|
|
233
235
|
|
|
@@ -272,9 +274,63 @@ export const command = createCommand({
|
|
|
272
274
|
);
|
|
273
275
|
}
|
|
274
276
|
}
|
|
277
|
+
|
|
278
|
+
// After auth is established, verify project access
|
|
279
|
+
if (auth && config) {
|
|
280
|
+
const { reconcileProject } = await import('../project/reconcile');
|
|
281
|
+
const apiClient = new APIClient(getAPIBaseURL(config), logger, auth.apiKey, config);
|
|
282
|
+
|
|
283
|
+
const result = await reconcileProject({
|
|
284
|
+
dir: rootDir,
|
|
285
|
+
auth,
|
|
286
|
+
apiClient,
|
|
287
|
+
config,
|
|
288
|
+
logger,
|
|
289
|
+
interactive: isTTY(),
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
if (result.status === 'error') {
|
|
293
|
+
tui.fatal(result.message!, ErrorCode.PROJECT_NOT_FOUND);
|
|
294
|
+
} else if (result.status === 'imported' && result.project) {
|
|
295
|
+
// Project was re-imported to user's org
|
|
296
|
+
project = result.project;
|
|
297
|
+
tui.newline();
|
|
298
|
+
} else if (result.status === 'skipped') {
|
|
299
|
+
// User declined import - can't continue with cloud features
|
|
300
|
+
tui.warning('Continuing in local-only mode.');
|
|
301
|
+
project = undefined;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
275
304
|
} else {
|
|
276
|
-
// No agentuity.json -
|
|
277
|
-
|
|
305
|
+
// No agentuity.json - check if this is a valid project that needs importing
|
|
306
|
+
if (auth && config) {
|
|
307
|
+
const { reconcileProject } = await import('../project/reconcile');
|
|
308
|
+
const apiClient = new APIClient(getAPIBaseURL(config), logger, auth.apiKey, config);
|
|
309
|
+
|
|
310
|
+
const result = await reconcileProject({
|
|
311
|
+
dir: rootDir,
|
|
312
|
+
auth,
|
|
313
|
+
apiClient,
|
|
314
|
+
config,
|
|
315
|
+
logger,
|
|
316
|
+
interactive: isTTY(),
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
if (result.status === 'error') {
|
|
320
|
+
// Not a valid project - show local-only warning
|
|
321
|
+
tui.showLocalOnlyWarning();
|
|
322
|
+
} else if (result.status === 'imported' && result.project) {
|
|
323
|
+
// Project was imported - reload project config
|
|
324
|
+
project = result.project;
|
|
325
|
+
tui.newline();
|
|
326
|
+
} else if (result.status === 'skipped') {
|
|
327
|
+
// User declined import - continue in local-only mode
|
|
328
|
+
tui.showLocalOnlyWarning();
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
// Not authenticated - local-only mode
|
|
332
|
+
tui.showLocalOnlyWarning();
|
|
333
|
+
}
|
|
278
334
|
}
|
|
279
335
|
|
|
280
336
|
// Prepare dev lock: cleans up stale processes from previous sessions
|
|
@@ -924,6 +980,7 @@ export const command = createCommand({
|
|
|
924
980
|
}
|
|
925
981
|
|
|
926
982
|
process.env.AGENTUITY_SDK_DEV_MODE = 'true';
|
|
983
|
+
process.env.AGENTUITY_RUNTIME = 'yes';
|
|
927
984
|
process.env.AGENTUITY_ENV = 'development';
|
|
928
985
|
process.env.NODE_ENV = 'development';
|
|
929
986
|
process.env.AGENTUITY_PROJECT_DIR = rootDir;
|
package/src/cmd/help/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createCommand } from '../../types';
|
|
2
2
|
import { getCommand } from '../../command-prefix';
|
|
3
|
+
import { getProgram } from '../../program-ref';
|
|
3
4
|
|
|
4
5
|
export const command = createCommand({
|
|
5
6
|
name: 'help',
|
|
@@ -13,28 +14,13 @@ export const command = createCommand({
|
|
|
13
14
|
idempotent: true,
|
|
14
15
|
|
|
15
16
|
async handler() {
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} else {
|
|
23
|
-
// Script mode: spawn runtime and script, omitting the 'help' argument
|
|
24
|
-
spawnArgs = [process.argv[0], ...(process.argv.length > 1 ? [process.argv[1]] : [])];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const proc = Bun.spawn(spawnArgs, {
|
|
28
|
-
stdio: ['inherit', 'inherit', 'inherit'],
|
|
29
|
-
env: process.env,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const exitCode = await proc.exited;
|
|
33
|
-
|
|
34
|
-
if (exitCode !== 0) {
|
|
35
|
-
throw new Error(`Help command exited with code ${exitCode}`);
|
|
17
|
+
// Get the root program and display its help
|
|
18
|
+
// This avoids spawning a subprocess which doesn't work reliably
|
|
19
|
+
// with Bun compiled binaries
|
|
20
|
+
const program = getProgram();
|
|
21
|
+
if (program) {
|
|
22
|
+
program.outputHelp();
|
|
36
23
|
}
|
|
37
|
-
|
|
38
|
-
return undefined;
|
|
24
|
+
process.exit(0);
|
|
39
25
|
},
|
|
40
26
|
});
|