@agentuity/cli 0.0.109 → 0.0.111
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/build-report.d.ts +201 -0
- package/dist/build-report.d.ts.map +1 -0
- package/dist/build-report.js +335 -0
- package/dist/build-report.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +19 -4
- package/dist/cli.js.map +1 -1
- package/dist/cmd/build/entry-generator.d.ts.map +1 -1
- package/dist/cmd/build/entry-generator.js +3 -1
- package/dist/cmd/build/entry-generator.js.map +1 -1
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +44 -1
- package/dist/cmd/build/index.js.map +1 -1
- package/dist/cmd/build/typecheck.d.ts +7 -1
- package/dist/cmd/build/typecheck.d.ts.map +1 -1
- package/dist/cmd/build/typecheck.js +11 -1
- package/dist/cmd/build/typecheck.js.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts +1 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.js +3 -3
- package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
- package/dist/cmd/build/vite/index.d.ts +2 -1
- package/dist/cmd/build/vite/index.d.ts.map +1 -1
- package/dist/cmd/build/vite/index.js +3 -2
- package/dist/cmd/build/vite/index.js.map +1 -1
- package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/metadata-generator.js +3 -5
- package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
- package/dist/cmd/build/vite/registry-generator.d.ts +1 -1
- package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/registry-generator.js +126 -41
- package/dist/cmd/build/vite/registry-generator.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts +6 -0
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js +19 -0
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts +3 -0
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +10 -2
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/vite-bundler.d.ts +3 -0
- package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
- package/dist/cmd/build/vite-bundler.js +14 -5
- package/dist/cmd/build/vite-bundler.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +149 -10
- 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 +0 -1
- package/dist/cmd/cloud/deployment/show.js.map +1 -1
- package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/create.js +18 -0
- package/dist/cmd/cloud/sandbox/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.js +2 -6
- package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
- package/dist/cmd/cloud/sandbox/download.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/download.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/download.js +89 -0
- package/dist/cmd/cloud/sandbox/download.js.map +1 -0
- package/dist/cmd/cloud/sandbox/env.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/env.js +90 -0
- package/dist/cmd/cloud/sandbox/env.js.map +1 -0
- package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/get.js +5 -0
- package/dist/cmd/cloud/sandbox/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/index.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/index.js +14 -0
- package/dist/cmd/cloud/sandbox/index.js.map +1 -1
- package/dist/cmd/cloud/sandbox/ls.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/ls.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/ls.js +119 -0
- package/dist/cmd/cloud/sandbox/ls.js.map +1 -0
- package/dist/cmd/cloud/sandbox/mkdir.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/mkdir.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/mkdir.js +59 -0
- package/dist/cmd/cloud/sandbox/mkdir.js.map +1 -0
- package/dist/cmd/cloud/sandbox/rm.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/rm.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/rm.js +45 -0
- package/dist/cmd/cloud/sandbox/rm.js.map +1 -0
- package/dist/cmd/cloud/sandbox/rmdir.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/rmdir.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/rmdir.js +59 -0
- package/dist/cmd/cloud/sandbox/rmdir.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/create.js +0 -2
- package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/get.js +0 -2
- package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/list.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/list.js +0 -3
- package/dist/cmd/cloud/sandbox/snapshot/list.js.map +1 -1
- package/dist/cmd/cloud/sandbox/upload.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/upload.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/upload.js +77 -0
- package/dist/cmd/cloud/sandbox/upload.js.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +126 -23
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/dev/sync.d.ts.map +1 -1
- package/dist/cmd/dev/sync.js +8 -14
- package/dist/cmd/dev/sync.js.map +1 -1
- package/dist/cmd/git/account/add.d.ts +17 -0
- package/dist/cmd/git/account/add.d.ts.map +1 -0
- package/dist/cmd/git/account/add.js +244 -0
- package/dist/cmd/git/account/add.js.map +1 -0
- package/dist/cmd/git/account/index.d.ts +3 -0
- package/dist/cmd/git/account/index.d.ts.map +1 -0
- package/dist/cmd/git/account/index.js +11 -0
- package/dist/cmd/git/account/index.js.map +1 -0
- package/dist/cmd/git/account/list.d.ts +2 -0
- package/dist/cmd/git/account/list.d.ts.map +1 -0
- package/dist/cmd/git/account/list.js +111 -0
- package/dist/cmd/git/account/list.js.map +1 -0
- package/dist/cmd/git/account/remove.d.ts +2 -0
- package/dist/cmd/git/account/remove.d.ts.map +1 -0
- package/dist/cmd/git/account/remove.js +171 -0
- package/dist/cmd/git/account/remove.js.map +1 -0
- package/dist/cmd/git/index.d.ts +3 -0
- package/dist/cmd/git/index.d.ts.map +1 -0
- package/dist/cmd/git/index.js +19 -0
- package/dist/cmd/git/index.js.map +1 -0
- package/dist/cmd/git/link.d.ts +32 -0
- package/dist/cmd/git/link.d.ts.map +1 -0
- package/dist/cmd/git/link.js +357 -0
- package/dist/cmd/git/link.js.map +1 -0
- package/dist/cmd/git/list.d.ts +2 -0
- package/dist/cmd/git/list.d.ts.map +1 -0
- package/dist/cmd/git/list.js +137 -0
- package/dist/cmd/git/list.js.map +1 -0
- package/dist/cmd/git/status.d.ts +2 -0
- package/dist/cmd/git/status.d.ts.map +1 -0
- package/dist/cmd/git/status.js +119 -0
- package/dist/cmd/git/status.js.map +1 -0
- package/dist/cmd/git/unlink.d.ts +2 -0
- package/dist/cmd/git/unlink.d.ts.map +1 -0
- package/dist/cmd/git/unlink.js +98 -0
- package/dist/cmd/git/unlink.js.map +1 -0
- package/dist/cmd/index.d.ts.map +1 -1
- package/dist/cmd/index.js +2 -0
- package/dist/cmd/index.js.map +1 -1
- package/dist/cmd/integration/api.d.ts +61 -0
- package/dist/cmd/integration/api.d.ts.map +1 -0
- package/dist/cmd/integration/api.js +176 -0
- package/dist/cmd/integration/api.js.map +1 -0
- package/dist/cmd/integration/github/connect.d.ts +2 -0
- package/dist/cmd/integration/github/connect.d.ts.map +1 -0
- package/dist/cmd/integration/github/connect.js +197 -0
- package/dist/cmd/integration/github/connect.js.map +1 -0
- package/dist/cmd/integration/github/disconnect.d.ts +2 -0
- package/dist/cmd/integration/github/disconnect.d.ts.map +1 -0
- package/dist/cmd/integration/github/disconnect.js +121 -0
- package/dist/cmd/integration/github/disconnect.js.map +1 -0
- package/dist/cmd/integration/github/index.d.ts +2 -0
- package/dist/cmd/integration/github/index.d.ts.map +1 -0
- package/dist/cmd/integration/github/index.js +21 -0
- package/dist/cmd/integration/github/index.js.map +1 -0
- package/dist/cmd/integration/index.d.ts +2 -0
- package/dist/cmd/integration/index.d.ts.map +1 -0
- package/dist/cmd/integration/index.js +16 -0
- package/dist/cmd/integration/index.js.map +1 -0
- package/dist/cmd/project/auth/generate.d.ts +5 -0
- package/dist/cmd/project/auth/generate.d.ts.map +1 -0
- package/dist/cmd/project/auth/generate.js +102 -0
- package/dist/cmd/project/auth/generate.js.map +1 -0
- package/dist/cmd/project/auth/index.d.ts +2 -0
- package/dist/cmd/project/auth/index.d.ts.map +1 -0
- package/dist/cmd/project/auth/index.js +21 -0
- package/dist/cmd/project/auth/index.js.map +1 -0
- package/dist/cmd/project/auth/init.d.ts +2 -0
- package/dist/cmd/project/auth/init.d.ts.map +1 -0
- package/dist/cmd/project/auth/init.js +220 -0
- package/dist/cmd/project/auth/init.js.map +1 -0
- package/dist/cmd/project/auth/shared.d.ts +88 -0
- package/dist/cmd/project/auth/shared.d.ts.map +1 -0
- package/dist/cmd/project/auth/shared.js +435 -0
- package/dist/cmd/project/auth/shared.js.map +1 -0
- package/dist/cmd/project/index.d.ts.map +1 -1
- package/dist/cmd/project/index.js +9 -1
- package/dist/cmd/project/index.js.map +1 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +106 -0
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +24 -0
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +5 -0
- package/dist/errors.js.map +1 -1
- package/dist/types.d.ts +3 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +5 -2
- package/dist/types.js.map +1 -1
- package/package.json +6 -5
- package/src/build-report.ts +457 -0
- package/src/cli.ts +20 -4
- package/src/cmd/build/entry-generator.ts +3 -1
- package/src/cmd/build/index.ts +51 -1
- package/src/cmd/build/typecheck.ts +19 -1
- package/src/cmd/build/vite/agent-discovery.ts +4 -4
- package/src/cmd/build/vite/index.ts +5 -2
- package/src/cmd/build/vite/metadata-generator.ts +5 -7
- package/src/cmd/build/vite/registry-generator.ts +136 -43
- package/src/cmd/build/vite/route-discovery.ts +20 -0
- package/src/cmd/build/vite/vite-builder.ts +13 -2
- package/src/cmd/build/vite-bundler.ts +17 -4
- package/src/cmd/cloud/deploy.ts +183 -12
- package/src/cmd/cloud/deployment/show.ts +0 -1
- package/src/cmd/cloud/sandbox/create.ts +22 -0
- package/src/cmd/cloud/sandbox/delete.ts +2 -6
- package/src/cmd/cloud/sandbox/download.ts +96 -0
- package/src/cmd/cloud/sandbox/env.ts +104 -0
- package/src/cmd/cloud/sandbox/get.ts +5 -0
- package/src/cmd/cloud/sandbox/index.ts +14 -0
- package/src/cmd/cloud/sandbox/ls.ts +126 -0
- package/src/cmd/cloud/sandbox/mkdir.ts +65 -0
- package/src/cmd/cloud/sandbox/rm.ts +51 -0
- package/src/cmd/cloud/sandbox/rmdir.ts +65 -0
- package/src/cmd/cloud/sandbox/snapshot/create.ts +0 -2
- package/src/cmd/cloud/sandbox/snapshot/get.ts +0 -2
- package/src/cmd/cloud/sandbox/snapshot/list.ts +0 -3
- package/src/cmd/cloud/sandbox/upload.ts +83 -0
- package/src/cmd/dev/index.ts +147 -33
- package/src/cmd/dev/sync.ts +26 -30
- package/src/cmd/git/account/add.ts +317 -0
- package/src/cmd/git/account/index.ts +12 -0
- package/src/cmd/git/account/list.ts +139 -0
- package/src/cmd/git/account/remove.ts +212 -0
- package/src/cmd/git/index.ts +20 -0
- package/src/cmd/git/link.ts +468 -0
- package/src/cmd/git/list.ts +161 -0
- package/src/cmd/git/status.ts +144 -0
- package/src/cmd/git/unlink.ts +117 -0
- package/src/cmd/index.ts +2 -0
- package/src/cmd/integration/api.ts +379 -0
- package/src/cmd/integration/github/connect.ts +242 -0
- package/src/cmd/integration/github/disconnect.ts +149 -0
- package/src/cmd/integration/github/index.ts +21 -0
- package/src/cmd/integration/index.ts +16 -0
- package/src/cmd/project/auth/generate.ts +116 -0
- package/src/cmd/project/auth/index.ts +21 -0
- package/src/cmd/project/auth/init.ts +263 -0
- package/src/cmd/project/auth/shared.ts +534 -0
- package/src/cmd/project/index.ts +9 -1
- package/src/cmd/project/template-flow.ts +125 -0
- package/src/config.ts +34 -0
- package/src/errors.ts +7 -0
- package/src/types.ts +5 -2
|
@@ -12,6 +12,7 @@ import { runAllBuilds } from './vite/vite-builder';
|
|
|
12
12
|
import { checkAndUpgradeDependencies } from '../../utils/dependency-checker';
|
|
13
13
|
import { checkBunVersion } from '../../utils/bun-version-checker';
|
|
14
14
|
import * as tui from '../../tui';
|
|
15
|
+
import type { BuildReportCollector } from '../../build-report';
|
|
15
16
|
|
|
16
17
|
const AppFileNotFoundError = StructuredError('AppFileNotFoundError');
|
|
17
18
|
const BuildFailedError = StructuredError('BuildFailedError');
|
|
@@ -26,6 +27,8 @@ export interface ViteBundleOptions {
|
|
|
26
27
|
port?: number;
|
|
27
28
|
logger: Logger;
|
|
28
29
|
deploymentOptions?: DeployOptions;
|
|
30
|
+
/** Optional collector for structured error reporting */
|
|
31
|
+
collector?: BuildReportCollector;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
/**
|
|
@@ -41,6 +44,7 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
|
|
|
41
44
|
port = 3500,
|
|
42
45
|
logger,
|
|
43
46
|
deploymentOptions,
|
|
47
|
+
collector,
|
|
44
48
|
} = options;
|
|
45
49
|
|
|
46
50
|
const output: string[] = [];
|
|
@@ -52,8 +56,10 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
|
|
|
52
56
|
// Verify app.ts exists
|
|
53
57
|
const appFile = join(rootDir, 'app.ts');
|
|
54
58
|
if (!(await Bun.file(appFile).exists())) {
|
|
59
|
+
const errorMessage = `App file not found at expected location: ${appFile}`;
|
|
60
|
+
collector?.addGeneralError('build', errorMessage, 'BUILD001');
|
|
55
61
|
throw new AppFileNotFoundError({
|
|
56
|
-
message:
|
|
62
|
+
message: errorMessage,
|
|
57
63
|
});
|
|
58
64
|
}
|
|
59
65
|
|
|
@@ -63,16 +69,20 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
|
|
|
63
69
|
.then((s) => s.isDirectory())
|
|
64
70
|
.catch(() => false);
|
|
65
71
|
if (!srcDirExists) {
|
|
72
|
+
const errorMessage = `Source directory not found: ${srcDir}`;
|
|
73
|
+
collector?.addGeneralError('build', errorMessage, 'BUILD002');
|
|
66
74
|
throw new BuildFailedError({
|
|
67
|
-
message:
|
|
75
|
+
message: errorMessage,
|
|
68
76
|
});
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
// Check and upgrade @agentuity/* dependencies if needed
|
|
72
80
|
const upgradeResult = await checkAndUpgradeDependencies(rootDir, logger);
|
|
73
81
|
if (upgradeResult.failed.length > 0 && process.stdin.isTTY) {
|
|
82
|
+
const errorMessage = `Failed to upgrade dependencies: ${upgradeResult.failed.join(', ')}`;
|
|
83
|
+
collector?.addGeneralError('build', errorMessage, 'BUILD003');
|
|
74
84
|
throw new BuildFailedError({
|
|
75
|
-
message:
|
|
85
|
+
message: errorMessage,
|
|
76
86
|
});
|
|
77
87
|
}
|
|
78
88
|
|
|
@@ -90,6 +100,7 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
|
|
|
90
100
|
deploymentId,
|
|
91
101
|
logger,
|
|
92
102
|
deploymentOptions,
|
|
103
|
+
collector,
|
|
93
104
|
});
|
|
94
105
|
|
|
95
106
|
if (result.client.included) {
|
|
@@ -106,8 +117,10 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
|
|
|
106
117
|
|
|
107
118
|
return { output };
|
|
108
119
|
} catch (error) {
|
|
120
|
+
const errorMessage = `Build failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
121
|
+
collector?.addGeneralError('build', errorMessage, 'BUILD004');
|
|
109
122
|
throw new BuildFailedError({
|
|
110
|
-
message:
|
|
123
|
+
message: errorMessage,
|
|
111
124
|
});
|
|
112
125
|
}
|
|
113
126
|
}
|
package/src/cmd/cloud/deploy.ts
CHANGED
|
@@ -8,7 +8,14 @@ import { isRunningFromExecutable } from '../upgrade';
|
|
|
8
8
|
import { createSubcommand, DeployOptionsSchema } from '../../types';
|
|
9
9
|
import { getUserAgent } from '../../api';
|
|
10
10
|
import * as tui from '../../tui';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
saveProjectDir,
|
|
13
|
+
getDefaultConfigDir,
|
|
14
|
+
loadProjectSDKKey,
|
|
15
|
+
updateProjectConfig,
|
|
16
|
+
} from '../../config';
|
|
17
|
+
import { getProjectGithubStatus } from '../integration/api';
|
|
18
|
+
import { runGitLink } from '../git/link';
|
|
12
19
|
import {
|
|
13
20
|
runSteps,
|
|
14
21
|
stepSuccess,
|
|
@@ -45,6 +52,7 @@ import { getCommand } from '../../command-prefix';
|
|
|
45
52
|
import * as domain from '../../domain';
|
|
46
53
|
import { ErrorCode } from '../../errors';
|
|
47
54
|
import { typecheck } from '../build/typecheck';
|
|
55
|
+
import { BuildReportCollector, setGlobalCollector, clearGlobalCollector } from '../../build-report';
|
|
48
56
|
|
|
49
57
|
const DeploymentCancelledError = StructuredError(
|
|
50
58
|
'DeploymentCancelled',
|
|
@@ -93,7 +101,12 @@ export const deploySubcommand = createSubcommand({
|
|
|
93
101
|
options: z.intersection(
|
|
94
102
|
DeployOptionsSchema,
|
|
95
103
|
z.object({
|
|
96
|
-
|
|
104
|
+
reportFile: z
|
|
105
|
+
.string()
|
|
106
|
+
.optional()
|
|
107
|
+
.describe(
|
|
108
|
+
'file path to save build report JSON with errors, warnings, and diagnostics'
|
|
109
|
+
),
|
|
97
110
|
})
|
|
98
111
|
),
|
|
99
112
|
response: DeployResponseSchema,
|
|
@@ -102,6 +115,14 @@ export const deploySubcommand = createSubcommand({
|
|
|
102
115
|
async handler(ctx) {
|
|
103
116
|
const { project, apiClient, projectDir, config, options, logger, opts } = ctx;
|
|
104
117
|
|
|
118
|
+
// Initialize build report collector if reportFile is specified
|
|
119
|
+
const collector = new BuildReportCollector();
|
|
120
|
+
if (opts.reportFile) {
|
|
121
|
+
collector.setOutputPath(opts.reportFile);
|
|
122
|
+
collector.enableAutoWrite();
|
|
123
|
+
setGlobalCollector(collector);
|
|
124
|
+
}
|
|
125
|
+
|
|
105
126
|
let deployment: Deployment | undefined;
|
|
106
127
|
let build: BuildMetadata | undefined;
|
|
107
128
|
let instructions: DeploymentInstructions | undefined;
|
|
@@ -149,6 +170,76 @@ export const deploySubcommand = createSubcommand({
|
|
|
149
170
|
try {
|
|
150
171
|
await saveProjectDir(projectDir);
|
|
151
172
|
|
|
173
|
+
// Check GitHub status and prompt for setup if not linked
|
|
174
|
+
// Skip in non-TTY environments (CI, automated runs) to prevent hanging
|
|
175
|
+
const hasTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
176
|
+
if (!useExistingDeployment && !project.skipGitSetup && hasTTY) {
|
|
177
|
+
try {
|
|
178
|
+
const githubStatus = await getProjectGithubStatus(apiClient, project.projectId);
|
|
179
|
+
|
|
180
|
+
if (githubStatus.linked && githubStatus.autoDeploy) {
|
|
181
|
+
// GitHub is already set up with auto-deploy, tell user to push instead
|
|
182
|
+
tui.newline();
|
|
183
|
+
tui.info(
|
|
184
|
+
`This project is linked to ${tui.bold(githubStatus.repoFullName ?? 'GitHub')} with automatic deployments enabled.`
|
|
185
|
+
);
|
|
186
|
+
tui.newline();
|
|
187
|
+
tui.info(
|
|
188
|
+
`Push a commit to the ${tui.bold(githubStatus.branch ?? 'main')} branch to trigger a deployment.`
|
|
189
|
+
);
|
|
190
|
+
tui.newline();
|
|
191
|
+
throw new DeploymentCancelledError();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!githubStatus.linked) {
|
|
195
|
+
tui.newline();
|
|
196
|
+
const wantSetup = await tui.confirm(
|
|
197
|
+
'Would you like to set up automatic deployments from GitHub?'
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (wantSetup) {
|
|
201
|
+
const result = await runGitLink({
|
|
202
|
+
apiClient,
|
|
203
|
+
projectId: project.projectId,
|
|
204
|
+
orgId: project.orgId,
|
|
205
|
+
logger,
|
|
206
|
+
skipAlreadyLinkedCheck: true,
|
|
207
|
+
config,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (result.linked && result.autoDeploy) {
|
|
211
|
+
// GitHub linked with auto-deploy, tell user to push instead
|
|
212
|
+
tui.newline();
|
|
213
|
+
tui.info('GitHub integration set up successfully!');
|
|
214
|
+
tui.newline();
|
|
215
|
+
tui.info('Push a commit to trigger your first deployment.');
|
|
216
|
+
tui.newline();
|
|
217
|
+
throw new DeploymentCancelledError();
|
|
218
|
+
} else if (result.linked) {
|
|
219
|
+
// Linked but auto-deploy disabled, continue with manual deploy
|
|
220
|
+
tui.newline();
|
|
221
|
+
tui.info('GitHub repository linked. Continuing with deployment...');
|
|
222
|
+
tui.newline();
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
await updateProjectConfig(projectDir, { skipGitSetup: true }, config);
|
|
226
|
+
tui.newline();
|
|
227
|
+
tui.info(
|
|
228
|
+
`Skipping GitHub setup. Run ${tui.bold(getCommand('git link'))} later to enable it.`
|
|
229
|
+
);
|
|
230
|
+
tui.newline();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
// Re-throw intentional cancellations
|
|
235
|
+
if (err instanceof DeploymentCancelledError) {
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
// Log other errors as non-fatal and continue
|
|
239
|
+
logger.trace('Failed to check GitHub status: %s', err);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
152
243
|
await runSteps(
|
|
153
244
|
[
|
|
154
245
|
!project.deployment?.domains?.length
|
|
@@ -239,8 +330,13 @@ export const deploySubcommand = createSubcommand({
|
|
|
239
330
|
}
|
|
240
331
|
let capturedOutput: string[] = [];
|
|
241
332
|
const rootDir = resolve(projectDir);
|
|
333
|
+
|
|
334
|
+
// Run typecheck with collector for error reporting
|
|
335
|
+
const endTypecheckDiagnostic = collector.startDiagnostic('typecheck');
|
|
242
336
|
const started = Date.now();
|
|
243
|
-
const typeResult = await typecheck(rootDir);
|
|
337
|
+
const typeResult = await typecheck(rootDir, { collector });
|
|
338
|
+
endTypecheckDiagnostic();
|
|
339
|
+
|
|
244
340
|
if (typeResult.success) {
|
|
245
341
|
capturedOutput.push(
|
|
246
342
|
tui.muted(
|
|
@@ -248,9 +344,10 @@ export const deploySubcommand = createSubcommand({
|
|
|
248
344
|
)
|
|
249
345
|
);
|
|
250
346
|
} else {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
347
|
+
// Errors already added to collector by typecheck()
|
|
348
|
+
// Write report before returning error
|
|
349
|
+
if (opts.reportFile) {
|
|
350
|
+
await collector.forceWrite();
|
|
254
351
|
}
|
|
255
352
|
return stepError('Typecheck failed\n\n' + typeResult.output);
|
|
256
353
|
}
|
|
@@ -264,6 +361,7 @@ export const deploySubcommand = createSubcommand({
|
|
|
264
361
|
region: project.region,
|
|
265
362
|
logger: ctx.logger,
|
|
266
363
|
deploymentOptions: opts,
|
|
364
|
+
collector,
|
|
267
365
|
});
|
|
268
366
|
capturedOutput = [...capturedOutput, ...bundleResult.output];
|
|
269
367
|
build = await loadBuildMetadata(join(projectDir, '.agentuity'));
|
|
@@ -275,6 +373,10 @@ export const deploySubcommand = createSubcommand({
|
|
|
275
373
|
return stepSuccess(capturedOutput.length > 0 ? capturedOutput : undefined);
|
|
276
374
|
} catch (ex) {
|
|
277
375
|
const _ex = ex as Error;
|
|
376
|
+
// Write report before returning error
|
|
377
|
+
if (opts.reportFile) {
|
|
378
|
+
await collector.forceWrite();
|
|
379
|
+
}
|
|
278
380
|
return stepError(
|
|
279
381
|
_ex.message ?? 'Error building your project',
|
|
280
382
|
_ex,
|
|
@@ -294,6 +396,8 @@ export const deploySubcommand = createSubcommand({
|
|
|
294
396
|
return stepError('deployment instructions were null');
|
|
295
397
|
}
|
|
296
398
|
|
|
399
|
+
// Start diagnostic for zip/encrypt phase
|
|
400
|
+
const endZipDiagnostic = collector.startDiagnostic('zip-package');
|
|
297
401
|
progress(5);
|
|
298
402
|
ctx.logger.trace('Starting deployment zip creation');
|
|
299
403
|
// zip up the assets folder
|
|
@@ -319,8 +423,11 @@ export const deploySubcommand = createSubcommand({
|
|
|
319
423
|
});
|
|
320
424
|
ctx.logger.trace(`Deployment zip created: ${deploymentZip}`);
|
|
321
425
|
|
|
426
|
+
endZipDiagnostic();
|
|
427
|
+
|
|
322
428
|
progress(20);
|
|
323
429
|
// Encrypt the deployment zip using the public key from deployment
|
|
430
|
+
const endEncryptDiagnostic = collector.startDiagnostic('encrypt');
|
|
324
431
|
const encryptedZip = join(tmpdir(), `${deployment.id}.enc.zip`);
|
|
325
432
|
try {
|
|
326
433
|
ctx.logger.trace('Creating public key');
|
|
@@ -348,8 +455,11 @@ export const deploySubcommand = createSubcommand({
|
|
|
348
455
|
dst.end();
|
|
349
456
|
});
|
|
350
457
|
ctx.logger.trace('Stream finished');
|
|
458
|
+
endEncryptDiagnostic();
|
|
351
459
|
|
|
352
460
|
progress(50);
|
|
461
|
+
// Start code upload diagnostic
|
|
462
|
+
const endCodeUploadDiagnostic = collector.startDiagnostic('code-upload');
|
|
353
463
|
ctx.logger.trace(`Uploading deployment to ${instructions.deployment}`);
|
|
354
464
|
const zipfile = Bun.file(encryptedZip);
|
|
355
465
|
const fileSize = await zipfile.size;
|
|
@@ -364,8 +474,15 @@ export const deploySubcommand = createSubcommand({
|
|
|
364
474
|
});
|
|
365
475
|
ctx.logger.trace(`Upload response: ${resp.status}`);
|
|
366
476
|
if (!resp.ok) {
|
|
367
|
-
|
|
477
|
+
endCodeUploadDiagnostic();
|
|
478
|
+
const errorMsg = `Error uploading deployment: ${await resp.text()}`;
|
|
479
|
+
collector.addGeneralError('deploy', errorMsg, 'DEPLOY002');
|
|
480
|
+
if (opts.reportFile) {
|
|
481
|
+
await collector.forceWrite();
|
|
482
|
+
}
|
|
483
|
+
return stepError(errorMsg);
|
|
368
484
|
}
|
|
485
|
+
endCodeUploadDiagnostic();
|
|
369
486
|
|
|
370
487
|
progress(70);
|
|
371
488
|
ctx.logger.trace('Consuming response body');
|
|
@@ -385,11 +502,17 @@ export const deploySubcommand = createSubcommand({
|
|
|
385
502
|
progress(80);
|
|
386
503
|
let bytes = 0;
|
|
387
504
|
if (build?.assets) {
|
|
505
|
+
// Start CDN upload diagnostic
|
|
506
|
+
const endCdnUploadDiagnostic = collector.startDiagnostic('cdn-upload');
|
|
388
507
|
ctx.logger.trace(`Uploading ${build.assets.length} assets`);
|
|
389
508
|
if (!instructions.assets) {
|
|
390
|
-
|
|
391
|
-
'server did not provide asset upload URLs; upload aborted'
|
|
392
|
-
);
|
|
509
|
+
const errorMsg =
|
|
510
|
+
'server did not provide asset upload URLs; upload aborted';
|
|
511
|
+
collector.addGeneralError('deploy', errorMsg, 'DEPLOY006');
|
|
512
|
+
if (opts.reportFile) {
|
|
513
|
+
await collector.forceWrite();
|
|
514
|
+
}
|
|
515
|
+
return stepError(errorMsg);
|
|
393
516
|
}
|
|
394
517
|
|
|
395
518
|
// Workaround for Bun crash in compiled executables (https://github.com/agentuity/sdk/issues/191)
|
|
@@ -452,11 +575,17 @@ export const deploySubcommand = createSubcommand({
|
|
|
452
575
|
const resps = await Promise.all(promises);
|
|
453
576
|
for (const r of resps) {
|
|
454
577
|
if (!r.ok) {
|
|
455
|
-
|
|
578
|
+
const errorMsg = `error uploading asset: ${await r.text()}`;
|
|
579
|
+
collector.addGeneralError('deploy', errorMsg, 'DEPLOY006');
|
|
580
|
+
if (opts.reportFile) {
|
|
581
|
+
await collector.forceWrite();
|
|
582
|
+
}
|
|
583
|
+
return stepError(errorMsg);
|
|
456
584
|
}
|
|
457
585
|
}
|
|
458
586
|
}
|
|
459
587
|
ctx.logger.trace('Asset uploads complete');
|
|
588
|
+
endCdnUploadDiagnostic();
|
|
460
589
|
progress(95);
|
|
461
590
|
}
|
|
462
591
|
|
|
@@ -503,6 +632,7 @@ export const deploySubcommand = createSubcommand({
|
|
|
503
632
|
const dashboard = `${appUrl}/r/${deployment.id}`;
|
|
504
633
|
|
|
505
634
|
// Poll for deployment status with optional log streaming
|
|
635
|
+
const endDeploymentWaitDiagnostic = collector.startDiagnostic('deployment-wait');
|
|
506
636
|
const pollInterval = 500;
|
|
507
637
|
const maxAttempts = 600;
|
|
508
638
|
let attempts = 0;
|
|
@@ -615,11 +745,16 @@ export const deploySubcommand = createSubcommand({
|
|
|
615
745
|
},
|
|
616
746
|
})
|
|
617
747
|
.then(() => {
|
|
748
|
+
endDeploymentWaitDiagnostic();
|
|
618
749
|
tui.success('Your project was deployed!');
|
|
619
750
|
})
|
|
620
|
-
.catch((ex) => {
|
|
751
|
+
.catch(async (ex) => {
|
|
752
|
+
endDeploymentWaitDiagnostic();
|
|
621
753
|
// Handle cancellation
|
|
622
754
|
if (ex instanceof DeploymentCancelledError) {
|
|
755
|
+
if (opts.reportFile) {
|
|
756
|
+
await collector.forceWrite();
|
|
757
|
+
}
|
|
623
758
|
tui.warning('Deployment cancelled');
|
|
624
759
|
process.exit(130); // Standard exit code for SIGINT
|
|
625
760
|
}
|
|
@@ -628,6 +763,18 @@ export const deploySubcommand = createSubcommand({
|
|
|
628
763
|
exwithmessage.message === 'Deployment failed'
|
|
629
764
|
? ''
|
|
630
765
|
: exwithmessage.toString();
|
|
766
|
+
|
|
767
|
+
// Add error to collector
|
|
768
|
+
const isTimeout = exwithmessage.message === 'Deployment timed out';
|
|
769
|
+
collector.addGeneralError(
|
|
770
|
+
'deploy',
|
|
771
|
+
msg || 'Deployment failed',
|
|
772
|
+
isTimeout ? 'DEPLOY003' : 'DEPLOY004'
|
|
773
|
+
);
|
|
774
|
+
if (opts.reportFile) {
|
|
775
|
+
await collector.forceWrite();
|
|
776
|
+
}
|
|
777
|
+
|
|
631
778
|
tui.error(`Your deployment failed to start${msg ? `: ${msg}` : ''}`);
|
|
632
779
|
if (logs.length) {
|
|
633
780
|
const logsDir = join(getDefaultConfigDir(), 'logs');
|
|
@@ -687,9 +834,22 @@ export const deploySubcommand = createSubcommand({
|
|
|
687
834
|
},
|
|
688
835
|
});
|
|
689
836
|
|
|
837
|
+
endDeploymentWaitDiagnostic();
|
|
690
838
|
tui.success('Your project was deployed!');
|
|
691
839
|
}
|
|
692
840
|
} catch (ex) {
|
|
841
|
+
endDeploymentWaitDiagnostic();
|
|
842
|
+
const exwithmessage = ex as { message: string };
|
|
843
|
+
const isTimeout = exwithmessage?.message === 'Deployment timed out';
|
|
844
|
+
collector.addGeneralError(
|
|
845
|
+
'deploy',
|
|
846
|
+
exwithmessage?.message || String(ex),
|
|
847
|
+
isTimeout ? 'DEPLOY003' : 'DEPLOY004'
|
|
848
|
+
);
|
|
849
|
+
if (opts.reportFile) {
|
|
850
|
+
await collector.forceWrite();
|
|
851
|
+
}
|
|
852
|
+
|
|
693
853
|
const lines = [`${ex}`, ''];
|
|
694
854
|
lines.push(
|
|
695
855
|
`${tui.ICONS.arrow} ${
|
|
@@ -741,6 +901,12 @@ export const deploySubcommand = createSubcommand({
|
|
|
741
901
|
});
|
|
742
902
|
}
|
|
743
903
|
|
|
904
|
+
// Write final report on success
|
|
905
|
+
if (opts.reportFile) {
|
|
906
|
+
await collector.forceWrite();
|
|
907
|
+
}
|
|
908
|
+
clearGlobalCollector();
|
|
909
|
+
|
|
744
910
|
return {
|
|
745
911
|
success: true,
|
|
746
912
|
deploymentId: deployment.id,
|
|
@@ -756,6 +922,11 @@ export const deploySubcommand = createSubcommand({
|
|
|
756
922
|
: undefined,
|
|
757
923
|
};
|
|
758
924
|
} catch (ex) {
|
|
925
|
+
collector.addGeneralError('deploy', String(ex), 'DEPLOY004');
|
|
926
|
+
if (opts.reportFile) {
|
|
927
|
+
await collector.forceWrite();
|
|
928
|
+
}
|
|
929
|
+
clearGlobalCollector();
|
|
759
930
|
tui.fatal(`unexpected error trying to deploy project. ${ex}`);
|
|
760
931
|
}
|
|
761
932
|
},
|
|
@@ -4,6 +4,12 @@ import * as tui from '../../../tui';
|
|
|
4
4
|
import { createSandboxClient, parseFileArgs } from './util';
|
|
5
5
|
import { getCommand } from '../../../command-prefix';
|
|
6
6
|
import { sandboxCreate } from '@agentuity/server';
|
|
7
|
+
import { StructuredError } from '@agentuity/core';
|
|
8
|
+
|
|
9
|
+
const InvalidMetadataError = StructuredError(
|
|
10
|
+
'InvalidMetadataError',
|
|
11
|
+
'Metadata must be a valid JSON object'
|
|
12
|
+
);
|
|
7
13
|
|
|
8
14
|
const SandboxCreateResponseSchema = z.object({
|
|
9
15
|
sandboxId: z.string().describe('Unique sandbox identifier'),
|
|
@@ -55,6 +61,7 @@ export const createSubcommand = createCommand({
|
|
|
55
61
|
.array(z.string())
|
|
56
62
|
.optional()
|
|
57
63
|
.describe('Apt packages to install (can be specified multiple times)'),
|
|
64
|
+
metadata: z.string().optional().describe('JSON object of user-defined metadata'),
|
|
58
65
|
}),
|
|
59
66
|
response: SandboxCreateResponseSchema,
|
|
60
67
|
},
|
|
@@ -77,6 +84,20 @@ export const createSubcommand = createCommand({
|
|
|
77
84
|
const files = parseFileArgs(opts.file);
|
|
78
85
|
const hasFiles = files.length > 0;
|
|
79
86
|
|
|
87
|
+
let metadata: Record<string, unknown> | undefined;
|
|
88
|
+
if (opts.metadata) {
|
|
89
|
+
let parsed: unknown;
|
|
90
|
+
try {
|
|
91
|
+
parsed = JSON.parse(opts.metadata);
|
|
92
|
+
} catch {
|
|
93
|
+
throw new InvalidMetadataError();
|
|
94
|
+
}
|
|
95
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
96
|
+
throw new InvalidMetadataError();
|
|
97
|
+
}
|
|
98
|
+
metadata = parsed as Record<string, unknown>;
|
|
99
|
+
}
|
|
100
|
+
|
|
80
101
|
const result = await sandboxCreate(client, {
|
|
81
102
|
options: {
|
|
82
103
|
resources:
|
|
@@ -93,6 +114,7 @@ export const createSubcommand = createCommand({
|
|
|
93
114
|
command: hasFiles ? { exec: [], files } : undefined,
|
|
94
115
|
snapshot: opts.snapshot,
|
|
95
116
|
dependencies: opts.dependency,
|
|
117
|
+
metadata,
|
|
96
118
|
},
|
|
97
119
|
orgId,
|
|
98
120
|
});
|
|
@@ -14,7 +14,7 @@ const SandboxDeleteResponseSchema = z.object({
|
|
|
14
14
|
|
|
15
15
|
export const deleteSubcommand = createCommand({
|
|
16
16
|
name: 'delete',
|
|
17
|
-
aliases: ['del', '
|
|
17
|
+
aliases: ['del', 'remove', 'destroy'],
|
|
18
18
|
description: 'Delete a sandbox',
|
|
19
19
|
tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
|
|
20
20
|
requires: { auth: true, region: true, org: true },
|
|
@@ -25,11 +25,7 @@ export const deleteSubcommand = createCommand({
|
|
|
25
25
|
description: 'Delete a sandbox',
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
|
-
command: getCommand('cloud sandbox
|
|
29
|
-
description: 'Delete using alias',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
command: getCommand('cloud sandbox rm abc123 --confirm'),
|
|
28
|
+
command: getCommand('cloud sandbox delete abc123 --confirm'),
|
|
33
29
|
description: 'Delete without confirmation prompt',
|
|
34
30
|
},
|
|
35
31
|
],
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { writeFileSync } from 'node:fs';
|
|
3
|
+
import { createCommand } from '../../../types';
|
|
4
|
+
import * as tui from '../../../tui';
|
|
5
|
+
import { createSandboxClient } from './util';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { sandboxDownloadArchive } from '@agentuity/server';
|
|
8
|
+
|
|
9
|
+
export const downloadSubcommand = createCommand({
|
|
10
|
+
name: 'download',
|
|
11
|
+
aliases: ['dl'],
|
|
12
|
+
description: 'Download files from a sandbox as a compressed archive',
|
|
13
|
+
tags: ['slow', 'requires-auth'],
|
|
14
|
+
requires: { auth: true, region: true, org: true },
|
|
15
|
+
examples: [
|
|
16
|
+
{
|
|
17
|
+
command: getCommand('cloud sandbox download sbx_abc123 ./backup.tar.gz'),
|
|
18
|
+
description: 'Download sandbox files as tar.gz archive',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
command: getCommand('cloud sandbox download sbx_abc123 ./backup.zip --format zip'),
|
|
22
|
+
description: 'Download sandbox files as zip archive',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
command: getCommand('cloud sandbox download sbx_abc123 ./backup.tar.gz --path /subdir'),
|
|
26
|
+
description: 'Download only a specific directory',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
schema: {
|
|
30
|
+
args: z.object({
|
|
31
|
+
sandboxId: z.string().describe('The sandbox ID'),
|
|
32
|
+
output: z.string().describe('Output file path for the archive'),
|
|
33
|
+
}),
|
|
34
|
+
options: z.object({
|
|
35
|
+
path: z.string().optional().describe('Path in sandbox to download (defaults to root)'),
|
|
36
|
+
format: z
|
|
37
|
+
.enum(['zip', 'tar.gz'])
|
|
38
|
+
.default('tar.gz')
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('Archive format (zip or tar.gz)'),
|
|
41
|
+
}),
|
|
42
|
+
response: z.object({
|
|
43
|
+
success: z.boolean(),
|
|
44
|
+
output: z.string(),
|
|
45
|
+
bytes: z.number(),
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
async handler(ctx) {
|
|
50
|
+
const { args, opts, options, auth, region, logger, orgId } = ctx;
|
|
51
|
+
|
|
52
|
+
const client = createSandboxClient(logger, auth, region);
|
|
53
|
+
const format = opts.format || 'tar.gz';
|
|
54
|
+
|
|
55
|
+
const stream = await sandboxDownloadArchive(client, {
|
|
56
|
+
sandboxId: args.sandboxId,
|
|
57
|
+
path: opts.path || '.',
|
|
58
|
+
format,
|
|
59
|
+
orgId,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const chunks: Uint8Array[] = [];
|
|
63
|
+
const reader = stream.getReader();
|
|
64
|
+
|
|
65
|
+
while (true) {
|
|
66
|
+
const { done, value } = await reader.read();
|
|
67
|
+
if (done) break;
|
|
68
|
+
chunks.push(value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const totalBytes = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
72
|
+
const buffer = new Uint8Array(totalBytes);
|
|
73
|
+
let offset = 0;
|
|
74
|
+
for (const chunk of chunks) {
|
|
75
|
+
buffer.set(chunk, offset);
|
|
76
|
+
offset += chunk.length;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
writeFileSync(args.output, buffer);
|
|
80
|
+
|
|
81
|
+
if (!options.json) {
|
|
82
|
+
tui.success(`Downloaded ${formatSize(totalBytes)} to ${args.output}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { success: true, output: args.output, bytes: totalBytes };
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
function formatSize(bytes: number): string {
|
|
90
|
+
if (bytes < 1024) return `${bytes} bytes`;
|
|
91
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
92
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
93
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default downloadSubcommand;
|