@agentuity/cli 0.1.17 → 0.1.19
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/cmd/build/vite/metadata-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/metadata-generator.js +7 -111
- package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/build.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/build.js +69 -4
- package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/get.js +4 -0
- package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
- package/dist/tui/box.d.ts +4 -0
- package/dist/tui/box.d.ts.map +1 -1
- package/dist/tui/box.js +39 -0
- package/dist/tui/box.js.map +1 -1
- package/dist/tui.d.ts +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +1 -1
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +19 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +16 -8
- package/dist/types.js.map +1 -1
- package/dist/utils/git.d.ts +63 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +157 -0
- package/dist/utils/git.js.map +1 -0
- package/package.json +6 -6
- package/src/cmd/build/vite/metadata-generator.ts +8 -139
- package/src/cmd/cloud/sandbox/snapshot/build.ts +83 -5
- package/src/cmd/cloud/sandbox/snapshot/get.ts +4 -0
- package/src/tui/box.ts +52 -0
- package/src/tui.ts +1 -1
- package/src/types.ts +31 -20
- package/src/utils/git.ts +209 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Logger } from '@agentuity/core';
|
|
2
|
+
/**
|
|
3
|
+
* Git information detected from the repository
|
|
4
|
+
*/
|
|
5
|
+
export interface GitInfo {
|
|
6
|
+
branch?: string;
|
|
7
|
+
repo?: string;
|
|
8
|
+
provider?: string;
|
|
9
|
+
tags?: string[];
|
|
10
|
+
commit?: string;
|
|
11
|
+
message?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Git options that can be provided via CLI flags to override auto-detected values
|
|
15
|
+
*/
|
|
16
|
+
export interface GitOptions {
|
|
17
|
+
message?: string;
|
|
18
|
+
commit?: string;
|
|
19
|
+
branch?: string;
|
|
20
|
+
repo?: string;
|
|
21
|
+
provider?: string;
|
|
22
|
+
commitUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Extended git info that includes CI-related fields
|
|
26
|
+
*/
|
|
27
|
+
export interface GitInfoExtended extends GitInfo {
|
|
28
|
+
commitUrl?: string;
|
|
29
|
+
logsUrl?: string;
|
|
30
|
+
trigger?: string;
|
|
31
|
+
event?: string;
|
|
32
|
+
pull_request?: {
|
|
33
|
+
number: number;
|
|
34
|
+
url?: string;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Detect git information from the repository at the given root directory.
|
|
39
|
+
* Walks up parent directories to find .git directory (supports monorepos).
|
|
40
|
+
*
|
|
41
|
+
* @param rootDir - The root directory to start searching for .git
|
|
42
|
+
* @param logger - Logger instance for trace output
|
|
43
|
+
* @returns Git information or undefined if not in a git repository
|
|
44
|
+
*/
|
|
45
|
+
export declare function getGitInfo(rootDir: string, logger: Logger): Promise<GitInfo | undefined>;
|
|
46
|
+
/**
|
|
47
|
+
* Merge auto-detected git info with CLI-provided overrides.
|
|
48
|
+
* CLI options take precedence over auto-detected values.
|
|
49
|
+
*
|
|
50
|
+
* @param autoDetected - Git info auto-detected from the repository
|
|
51
|
+
* @param overrides - CLI options that override auto-detected values
|
|
52
|
+
* @returns Merged git info
|
|
53
|
+
*/
|
|
54
|
+
export declare function mergeGitInfo(autoDetected: GitInfo | undefined, overrides: GitOptions): GitInfoExtended;
|
|
55
|
+
/**
|
|
56
|
+
* Build an array of tags from git info, including branch and short commit.
|
|
57
|
+
* Used for deployment tagging.
|
|
58
|
+
*
|
|
59
|
+
* @param gitInfo - Git information
|
|
60
|
+
* @returns Array of tags
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildGitTags(gitInfo: GitInfo | undefined): string[];
|
|
63
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAI9C;;GAEG;AACH,MAAM,WAAW,OAAO;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,OAAO;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE;QACd,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,MAAM,CAAC;KACb,CAAC;CACF;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAsG9F;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC3B,YAAY,EAAE,OAAO,GAAG,SAAS,EACjC,SAAS,EAAE,UAAU,GACnB,eAAe,CAwBjB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,EAAE,CAUnE"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Detect git information from the repository at the given root directory.
|
|
5
|
+
* Walks up parent directories to find .git directory (supports monorepos).
|
|
6
|
+
*
|
|
7
|
+
* @param rootDir - The root directory to start searching for .git
|
|
8
|
+
* @param logger - Logger instance for trace output
|
|
9
|
+
* @returns Git information or undefined if not in a git repository
|
|
10
|
+
*/
|
|
11
|
+
export async function getGitInfo(rootDir, logger) {
|
|
12
|
+
if (!Bun.which('git')) {
|
|
13
|
+
logger.trace('git not found in PATH');
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
// Find .git directory (may be in parent directories for monorepos)
|
|
18
|
+
let gitDir = join(rootDir, '.git');
|
|
19
|
+
let parentDir = dirname(dirname(gitDir));
|
|
20
|
+
while (!existsSync(gitDir) && parentDir !== dirname(parentDir) && gitDir !== '/') {
|
|
21
|
+
gitDir = join(parentDir, '.git');
|
|
22
|
+
parentDir = dirname(parentDir);
|
|
23
|
+
}
|
|
24
|
+
if (!existsSync(gitDir)) {
|
|
25
|
+
logger.trace('No .git directory found');
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const $ = Bun.$;
|
|
29
|
+
const gitInfo = {
|
|
30
|
+
provider: 'git',
|
|
31
|
+
};
|
|
32
|
+
// Get git tags pointing to HEAD
|
|
33
|
+
const tagResult = $ `git tag -l --points-at HEAD`.nothrow().quiet();
|
|
34
|
+
if (tagResult) {
|
|
35
|
+
const tagText = await tagResult.text();
|
|
36
|
+
if (tagText) {
|
|
37
|
+
gitInfo.tags = tagText
|
|
38
|
+
.trim()
|
|
39
|
+
.split(/\n/)
|
|
40
|
+
.map((s) => s.trim())
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Get current branch
|
|
45
|
+
const branchResult = $ `git branch --show-current`.nothrow().quiet();
|
|
46
|
+
if (branchResult) {
|
|
47
|
+
const branchText = await branchResult.text();
|
|
48
|
+
if (branchText) {
|
|
49
|
+
gitInfo.branch = branchText.trim();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Get commit SHA
|
|
53
|
+
const commitResult = $ `git rev-parse HEAD`.nothrow().quiet();
|
|
54
|
+
if (commitResult) {
|
|
55
|
+
const commitText = await commitResult.text();
|
|
56
|
+
if (commitText) {
|
|
57
|
+
gitInfo.commit = commitText.trim();
|
|
58
|
+
// Get commit message
|
|
59
|
+
const msgResult = $ `git log --pretty=format:%s -n1 ${gitInfo.commit}`.nothrow().quiet();
|
|
60
|
+
if (msgResult) {
|
|
61
|
+
const msgText = await msgResult.text();
|
|
62
|
+
if (msgText) {
|
|
63
|
+
gitInfo.message = msgText.trim();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Get remote origin URL and parse
|
|
69
|
+
const originResult = $ `git config --get remote.origin.url`.nothrow().quiet();
|
|
70
|
+
if (originResult) {
|
|
71
|
+
const originText = await originResult.text();
|
|
72
|
+
if (originText) {
|
|
73
|
+
const remoteUrl = originText.trim();
|
|
74
|
+
// Parse provider and repo from URL
|
|
75
|
+
if (remoteUrl.includes('github.com')) {
|
|
76
|
+
gitInfo.provider = 'github';
|
|
77
|
+
const match = remoteUrl.match(/github\.com[:/](.+?)(?:\.git)?$/);
|
|
78
|
+
if (match) {
|
|
79
|
+
gitInfo.repo = `https://github.com/${match[1]}`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (remoteUrl.includes('gitlab.com')) {
|
|
83
|
+
gitInfo.provider = 'gitlab';
|
|
84
|
+
const match = remoteUrl.match(/gitlab\.com[:/](.+?)(?:\.git)?$/);
|
|
85
|
+
if (match) {
|
|
86
|
+
gitInfo.repo = `https://gitlab.com/${match[1]}`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else if (remoteUrl.includes('bitbucket.org')) {
|
|
90
|
+
gitInfo.provider = 'bitbucket';
|
|
91
|
+
const match = remoteUrl.match(/bitbucket\.org[:/](.+?)(?:\.git)?$/);
|
|
92
|
+
if (match) {
|
|
93
|
+
gitInfo.repo = `https://bitbucket.org/${match[1]}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
gitInfo.repo = remoteUrl;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return gitInfo;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
logger.trace(`Failed to get git info: ${error}`);
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Merge auto-detected git info with CLI-provided overrides.
|
|
110
|
+
* CLI options take precedence over auto-detected values.
|
|
111
|
+
*
|
|
112
|
+
* @param autoDetected - Git info auto-detected from the repository
|
|
113
|
+
* @param overrides - CLI options that override auto-detected values
|
|
114
|
+
* @returns Merged git info
|
|
115
|
+
*/
|
|
116
|
+
export function mergeGitInfo(autoDetected, overrides) {
|
|
117
|
+
const result = { ...(autoDetected ?? {}) };
|
|
118
|
+
// CLI overrides take precedence
|
|
119
|
+
if (overrides.message !== undefined) {
|
|
120
|
+
result.message = overrides.message;
|
|
121
|
+
}
|
|
122
|
+
if (overrides.commit !== undefined) {
|
|
123
|
+
result.commit = overrides.commit;
|
|
124
|
+
}
|
|
125
|
+
if (overrides.branch !== undefined) {
|
|
126
|
+
result.branch = overrides.branch;
|
|
127
|
+
}
|
|
128
|
+
if (overrides.repo !== undefined) {
|
|
129
|
+
result.repo = overrides.repo;
|
|
130
|
+
}
|
|
131
|
+
if (overrides.provider !== undefined) {
|
|
132
|
+
result.provider = overrides.provider;
|
|
133
|
+
}
|
|
134
|
+
if (overrides.commitUrl !== undefined) {
|
|
135
|
+
result.commitUrl = overrides.commitUrl;
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Build an array of tags from git info, including branch and short commit.
|
|
141
|
+
* Used for deployment tagging.
|
|
142
|
+
*
|
|
143
|
+
* @param gitInfo - Git information
|
|
144
|
+
* @returns Array of tags
|
|
145
|
+
*/
|
|
146
|
+
export function buildGitTags(gitInfo) {
|
|
147
|
+
const tags = new Set(gitInfo?.tags ?? []);
|
|
148
|
+
tags.add('latest');
|
|
149
|
+
if (gitInfo?.branch) {
|
|
150
|
+
tags.add(gitInfo.branch);
|
|
151
|
+
}
|
|
152
|
+
if (gitInfo?.commit) {
|
|
153
|
+
tags.add(gitInfo.commit.substring(0, 7));
|
|
154
|
+
}
|
|
155
|
+
return Array.from(tags);
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentuity/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Agentuity employees and contributors",
|
|
6
6
|
"type": "module",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"prepublishOnly": "bun run clean && bun run build"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@agentuity/auth": "0.1.
|
|
44
|
-
"@agentuity/core": "0.1.
|
|
45
|
-
"@agentuity/server": "0.1.
|
|
43
|
+
"@agentuity/auth": "0.1.19",
|
|
44
|
+
"@agentuity/core": "0.1.19",
|
|
45
|
+
"@agentuity/server": "0.1.19",
|
|
46
46
|
"@datasert/cronjs-parser": "^1.4.0",
|
|
47
47
|
"@terascope/fetch-github-release": "^2.2.1",
|
|
48
48
|
"@vitejs/plugin-react": "^5.1.2",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"typescript": "^5.9.0",
|
|
61
61
|
"vite": "^7.2.7",
|
|
62
62
|
"zod": "^4.3.5",
|
|
63
|
-
"@agentuity/frontend": "0.1.
|
|
63
|
+
"@agentuity/frontend": "0.1.19"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@agentuity/test-utils": "0.1.
|
|
66
|
+
"@agentuity/test-utils": "0.1.19",
|
|
67
67
|
"@types/adm-zip": "^0.5.7",
|
|
68
68
|
"@types/bun": "latest",
|
|
69
69
|
"@types/tar-fs": "^2.0.4",
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
* Generates agentuity.metadata.json and .routemapping.json from discovered agents and routes
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { join
|
|
7
|
+
import { join } from 'node:path';
|
|
8
8
|
import { writeFileSync, mkdirSync, existsSync, readFileSync, statSync, readdirSync } from 'node:fs';
|
|
9
9
|
import type { BuildMetadata } from '@agentuity/server';
|
|
10
10
|
import type { AgentMetadata } from './agent-discovery';
|
|
11
11
|
import type { RouteMetadata } from './route-discovery';
|
|
12
12
|
import type { Logger, DeployOptions } from '../../../types';
|
|
13
13
|
import { getVersion } from '../../../version';
|
|
14
|
+
import { getGitInfo, buildGitTags } from '../../../utils/git';
|
|
14
15
|
|
|
15
16
|
interface ViteManifestEntry {
|
|
16
17
|
file: string;
|
|
@@ -473,6 +474,12 @@ export async function generateMetadata(options: MetadataGeneratorOptions): Promi
|
|
|
473
474
|
},
|
|
474
475
|
};
|
|
475
476
|
|
|
477
|
+
// Build tags from git info (includes 'latest', branch, and short commit)
|
|
478
|
+
const gitInfo = metadata.deployment.git;
|
|
479
|
+
if (gitInfo) {
|
|
480
|
+
gitInfo.tags = buildGitTags(gitInfo);
|
|
481
|
+
}
|
|
482
|
+
|
|
476
483
|
if (options.deploymentOptions) {
|
|
477
484
|
const git = { ...(metadata.deployment.git ?? {}), ...options.deploymentOptions };
|
|
478
485
|
if (options.deploymentOptions.pullRequestNumber) {
|
|
@@ -489,144 +496,6 @@ export async function generateMetadata(options: MetadataGeneratorOptions): Promi
|
|
|
489
496
|
return metadata;
|
|
490
497
|
}
|
|
491
498
|
|
|
492
|
-
/**
|
|
493
|
-
* Get git information (branch, repo, provider, tags)
|
|
494
|
-
*/
|
|
495
|
-
async function getGitInfo(
|
|
496
|
-
rootDir: string,
|
|
497
|
-
logger: Logger
|
|
498
|
-
): Promise<
|
|
499
|
-
| {
|
|
500
|
-
branch?: string;
|
|
501
|
-
repo?: string;
|
|
502
|
-
provider?: string;
|
|
503
|
-
tags?: string[];
|
|
504
|
-
commit?: string;
|
|
505
|
-
message?: string;
|
|
506
|
-
}
|
|
507
|
-
| undefined
|
|
508
|
-
> {
|
|
509
|
-
if (!Bun.which('git')) {
|
|
510
|
-
logger.trace('git not found in PATH');
|
|
511
|
-
return undefined;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
try {
|
|
515
|
-
// Find .git directory (may be in parent directories for monorepos)
|
|
516
|
-
let gitDir = join(rootDir, '.git');
|
|
517
|
-
let parentDir = dirname(dirname(gitDir));
|
|
518
|
-
while (!existsSync(gitDir) && parentDir !== dirname(parentDir) && gitDir !== '/') {
|
|
519
|
-
gitDir = join(parentDir, '.git');
|
|
520
|
-
parentDir = dirname(parentDir);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
if (!existsSync(gitDir)) {
|
|
524
|
-
logger.trace('No .git directory found');
|
|
525
|
-
return undefined;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
const $ = Bun.$;
|
|
529
|
-
const gitInfo: {
|
|
530
|
-
branch?: string;
|
|
531
|
-
repo?: string;
|
|
532
|
-
provider?: string;
|
|
533
|
-
tags?: string[];
|
|
534
|
-
commit?: string;
|
|
535
|
-
message?: string;
|
|
536
|
-
} = {
|
|
537
|
-
provider: 'git',
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
// Get git tags pointing to HEAD
|
|
541
|
-
const tagResult = $`git tag -l --points-at HEAD`.nothrow().quiet();
|
|
542
|
-
if (tagResult) {
|
|
543
|
-
const tagText = await tagResult.text();
|
|
544
|
-
if (tagText) {
|
|
545
|
-
gitInfo.tags = tagText
|
|
546
|
-
.trim()
|
|
547
|
-
.split(/\n/)
|
|
548
|
-
.map((s) => s.trim())
|
|
549
|
-
.filter(Boolean);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// Get current branch
|
|
554
|
-
const branchResult = $`git branch --show-current`.nothrow().quiet();
|
|
555
|
-
if (branchResult) {
|
|
556
|
-
const branchText = await branchResult.text();
|
|
557
|
-
if (branchText) {
|
|
558
|
-
gitInfo.branch = branchText.trim();
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// Get commit SHA
|
|
563
|
-
const commitResult = $`git rev-parse HEAD`.nothrow().quiet();
|
|
564
|
-
if (commitResult) {
|
|
565
|
-
const commitText = await commitResult.text();
|
|
566
|
-
if (commitText) {
|
|
567
|
-
gitInfo.commit = commitText.trim();
|
|
568
|
-
|
|
569
|
-
// Get commit message
|
|
570
|
-
const msgResult = $`git log --pretty=format:%s -n1 ${gitInfo.commit}`.nothrow().quiet();
|
|
571
|
-
if (msgResult) {
|
|
572
|
-
const msgText = await msgResult.text();
|
|
573
|
-
if (msgText) {
|
|
574
|
-
gitInfo.message = msgText.trim();
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Get remote origin URL and parse
|
|
581
|
-
const originResult = $`git config --get remote.origin.url`.nothrow().quiet();
|
|
582
|
-
if (originResult) {
|
|
583
|
-
const originText = await originResult.text();
|
|
584
|
-
if (originText) {
|
|
585
|
-
const remoteUrl = originText.trim();
|
|
586
|
-
|
|
587
|
-
// Parse provider and repo from URL
|
|
588
|
-
if (remoteUrl.includes('github.com')) {
|
|
589
|
-
gitInfo.provider = 'github';
|
|
590
|
-
const match = remoteUrl.match(/github\.com[:/](.+?)(?:\.git)?$/);
|
|
591
|
-
if (match) {
|
|
592
|
-
gitInfo.repo = `https://github.com/${match[1]}`;
|
|
593
|
-
}
|
|
594
|
-
} else if (remoteUrl.includes('gitlab.com')) {
|
|
595
|
-
gitInfo.provider = 'gitlab';
|
|
596
|
-
const match = remoteUrl.match(/gitlab\.com[:/](.+?)(?:\.git)?$/);
|
|
597
|
-
if (match) {
|
|
598
|
-
gitInfo.repo = `https://gitlab.com/${match[1]}`;
|
|
599
|
-
}
|
|
600
|
-
} else if (remoteUrl.includes('bitbucket.org')) {
|
|
601
|
-
gitInfo.provider = 'bitbucket';
|
|
602
|
-
const match = remoteUrl.match(/bitbucket\.org[:/](.+?)(?:\.git)?$/);
|
|
603
|
-
if (match) {
|
|
604
|
-
gitInfo.repo = `https://bitbucket.org/${match[1]}`;
|
|
605
|
-
}
|
|
606
|
-
} else {
|
|
607
|
-
gitInfo.repo = remoteUrl;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// Build tags array with defaults
|
|
613
|
-
const tags = new Set(gitInfo.tags ?? []);
|
|
614
|
-
tags.add('latest');
|
|
615
|
-
if (gitInfo.branch) {
|
|
616
|
-
tags.add(gitInfo.branch);
|
|
617
|
-
}
|
|
618
|
-
if (gitInfo.commit) {
|
|
619
|
-
tags.add(gitInfo.commit.substring(0, 7));
|
|
620
|
-
}
|
|
621
|
-
gitInfo.tags = Array.from(tags);
|
|
622
|
-
|
|
623
|
-
return gitInfo;
|
|
624
|
-
} catch (error) {
|
|
625
|
-
logger.trace(`Failed to get git info: ${error}`);
|
|
626
|
-
return undefined;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
499
|
/**
|
|
631
500
|
* Generate AGENTS.md content for AI coding agents
|
|
632
501
|
*/
|
|
@@ -13,9 +13,10 @@ import {
|
|
|
13
13
|
snapshotUpload,
|
|
14
14
|
SnapshotBuildFileSchema,
|
|
15
15
|
} from '@agentuity/server';
|
|
16
|
-
import type { SnapshotFileInfo } from '@agentuity/server';
|
|
16
|
+
import type { SnapshotFileInfo, SnapshotBuildGitInfo } from '@agentuity/server';
|
|
17
17
|
import { getCatalystAPIClient } from '../../../../config';
|
|
18
18
|
import { validateAptDependencies } from '../../../../utils/apt-validator';
|
|
19
|
+
import { getGitInfo, mergeGitInfo } from '../../../../utils/git';
|
|
19
20
|
import { encryptFIPSKEMDEMStream } from '../../../../crypto/box';
|
|
20
21
|
import { tmpdir } from 'node:os';
|
|
21
22
|
import { randomUUID, createHash, createPublicKey } from 'node:crypto';
|
|
@@ -270,6 +271,7 @@ async function generateContentHash(params: {
|
|
|
270
271
|
files: SnapshotFileInfo[];
|
|
271
272
|
fileHashes: Map<string, string>;
|
|
272
273
|
env?: Record<string, string>;
|
|
274
|
+
isPublic?: boolean;
|
|
273
275
|
}): Promise<string> {
|
|
274
276
|
const hash = createHash('sha256');
|
|
275
277
|
|
|
@@ -300,6 +302,8 @@ async function generateContentHash(params: {
|
|
|
300
302
|
}
|
|
301
303
|
}
|
|
302
304
|
|
|
305
|
+
hash.update(`access:${params.isPublic ? 'public' : 'private'}\n`);
|
|
306
|
+
|
|
303
307
|
return hash.digest('hex');
|
|
304
308
|
}
|
|
305
309
|
|
|
@@ -348,12 +352,25 @@ export const buildSubcommand = createCommand({
|
|
|
348
352
|
name: z.string().optional().describe('Snapshot name (overrides build file)'),
|
|
349
353
|
tag: z.string().optional().describe('Snapshot tag (defaults to "latest")'),
|
|
350
354
|
description: z.string().optional().describe('Snapshot description (overrides build file)'),
|
|
355
|
+
message: z.string().optional().describe('Build message for this snapshot'),
|
|
356
|
+
commit: z.string().optional().describe('Git commit SHA (auto-detected if not provided)'),
|
|
357
|
+
branch: z.string().optional().describe('Git branch (auto-detected if not provided)'),
|
|
358
|
+
repo: z.string().optional().describe('Git repo URL (auto-detected if not provided)'),
|
|
359
|
+
provider: z
|
|
360
|
+
.string()
|
|
361
|
+
.optional()
|
|
362
|
+
.describe('Git provider (github, gitlab, bitbucket - auto-detected)'),
|
|
363
|
+
commitUrl: z.string().optional().describe('URL to the commit'),
|
|
351
364
|
metadata: z.array(z.string()).optional().describe('Metadata key-value pairs (KEY=VALUE)'),
|
|
352
365
|
force: z.boolean().optional().describe('Force rebuild even if content is unchanged'),
|
|
353
366
|
public: z
|
|
354
367
|
.boolean()
|
|
355
368
|
.optional()
|
|
356
369
|
.describe('Make snapshot public (enables virus scanning, no encryption)'),
|
|
370
|
+
confirm: z
|
|
371
|
+
.boolean()
|
|
372
|
+
.optional()
|
|
373
|
+
.describe('Confirm public snapshot publishing (required for --public)'),
|
|
357
374
|
}),
|
|
358
375
|
response: SnapshotBuildResponseSchema,
|
|
359
376
|
},
|
|
@@ -362,6 +379,37 @@ export const buildSubcommand = createCommand({
|
|
|
362
379
|
const { args, opts, options, auth, region, config, logger, orgId } = ctx;
|
|
363
380
|
|
|
364
381
|
const dryRun = options.dryRun === true;
|
|
382
|
+
const isPublic = opts.public === true;
|
|
383
|
+
|
|
384
|
+
if (isPublic && !dryRun) {
|
|
385
|
+
if (!opts.confirm) {
|
|
386
|
+
if (!tui.isTTYLike()) {
|
|
387
|
+
logger.fatal(
|
|
388
|
+
`Publishing a public snapshot requires confirmation.\n\n` +
|
|
389
|
+
`Public snapshots make all environment variables and files publicly accessible.\n\n` +
|
|
390
|
+
`To proceed, add the --confirm flag:\n` +
|
|
391
|
+
` ${getCommand('cloud sandbox snapshot build . --public --confirm')}\n\n` +
|
|
392
|
+
`To preview what will be published, use --dry-run first:\n` +
|
|
393
|
+
` ${getCommand('cloud sandbox snapshot build . --public --dry-run')}`
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
tui.warningBox(
|
|
398
|
+
'Public Snapshot',
|
|
399
|
+
`You are publishing a public snapshot.\n\n` +
|
|
400
|
+
`This will make all environment variables and\n` +
|
|
401
|
+
`files in the snapshot publicly accessible.\n\n` +
|
|
402
|
+
`Run with --dry-run to preview the contents.`
|
|
403
|
+
);
|
|
404
|
+
console.log('');
|
|
405
|
+
|
|
406
|
+
const confirmed = await tui.confirm('Proceed with public snapshot?', false);
|
|
407
|
+
|
|
408
|
+
if (!confirmed) {
|
|
409
|
+
logger.fatal('Aborted');
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
365
413
|
|
|
366
414
|
const directory = resolve(args.directory);
|
|
367
415
|
if (!existsSync(directory)) {
|
|
@@ -532,6 +580,7 @@ export const buildSubcommand = createCommand({
|
|
|
532
580
|
files: fileList,
|
|
533
581
|
fileHashes,
|
|
534
582
|
env: finalEnv,
|
|
583
|
+
isPublic,
|
|
535
584
|
});
|
|
536
585
|
|
|
537
586
|
if (dryRun) {
|
|
@@ -545,11 +594,12 @@ export const buildSubcommand = createCommand({
|
|
|
545
594
|
Description: finalDescription ?? '-',
|
|
546
595
|
Runtime: buildConfig.runtime,
|
|
547
596
|
Tag: opts.tag ?? 'latest',
|
|
597
|
+
Access: isPublic ? 'public' : 'private',
|
|
548
598
|
Size: tui.formatBytes(totalSize),
|
|
549
599
|
Files: fileList.length.toFixed(),
|
|
550
600
|
},
|
|
551
601
|
],
|
|
552
|
-
['Name', 'Description', 'Runtime', 'Tag', 'Size', 'Files'],
|
|
602
|
+
['Name', 'Description', 'Runtime', 'Tag', 'Access', 'Size', 'Files'],
|
|
553
603
|
{ layout: 'vertical', padStart: ' ' }
|
|
554
604
|
);
|
|
555
605
|
|
|
@@ -618,7 +668,33 @@ export const buildSubcommand = createCommand({
|
|
|
618
668
|
|
|
619
669
|
const client = getCatalystAPIClient(logger, auth, region);
|
|
620
670
|
|
|
621
|
-
|
|
671
|
+
// Auto-detect git info and merge with CLI overrides
|
|
672
|
+
const autoDetectedGit = await getGitInfo(directory, logger);
|
|
673
|
+
const mergedGitInfo = mergeGitInfo(autoDetectedGit, {
|
|
674
|
+
message: opts.message,
|
|
675
|
+
commit: opts.commit,
|
|
676
|
+
branch: opts.branch,
|
|
677
|
+
repo: opts.repo,
|
|
678
|
+
provider: opts.provider,
|
|
679
|
+
commitUrl: opts.commitUrl,
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
// Build git info for API (only include if we have any git data)
|
|
683
|
+
const hasGitInfo =
|
|
684
|
+
mergedGitInfo.branch ||
|
|
685
|
+
mergedGitInfo.commit ||
|
|
686
|
+
mergedGitInfo.repo ||
|
|
687
|
+
mergedGitInfo.provider ||
|
|
688
|
+
mergedGitInfo.commitUrl;
|
|
689
|
+
const gitInfo: SnapshotBuildGitInfo | undefined = hasGitInfo
|
|
690
|
+
? {
|
|
691
|
+
branch: mergedGitInfo.branch,
|
|
692
|
+
commit: mergedGitInfo.commit,
|
|
693
|
+
repo: mergedGitInfo.repo,
|
|
694
|
+
provider: mergedGitInfo.provider,
|
|
695
|
+
commitUrl: mergedGitInfo.commitUrl,
|
|
696
|
+
}
|
|
697
|
+
: undefined;
|
|
622
698
|
|
|
623
699
|
const initResult = await tui.spinner({
|
|
624
700
|
message: 'Initializing snapshot build...',
|
|
@@ -627,8 +703,10 @@ export const buildSubcommand = createCommand({
|
|
|
627
703
|
return await snapshotBuildInit(client, {
|
|
628
704
|
runtime: buildConfig.runtime,
|
|
629
705
|
name: finalName,
|
|
630
|
-
tag: opts.tag,
|
|
706
|
+
tag: opts.tag ?? 'latest',
|
|
631
707
|
description: finalDescription,
|
|
708
|
+
message: mergedGitInfo.message,
|
|
709
|
+
git: gitInfo,
|
|
632
710
|
contentHash,
|
|
633
711
|
force: opts.force,
|
|
634
712
|
encrypt: !isPublic,
|
|
@@ -755,7 +833,7 @@ export const buildSubcommand = createCommand({
|
|
|
755
833
|
{
|
|
756
834
|
snapshotId: '',
|
|
757
835
|
name: finalName ?? '',
|
|
758
|
-
tag: opts.tag,
|
|
836
|
+
tag: opts.tag ?? 'latest',
|
|
759
837
|
runtime: buildConfig.runtime,
|
|
760
838
|
sizeBytes: totalSize,
|
|
761
839
|
fileCount: fileList.length,
|
|
@@ -27,6 +27,7 @@ const SnapshotGetResponseSchema = z.object({
|
|
|
27
27
|
name: z.string().describe('Snapshot name'),
|
|
28
28
|
fullName: z.string().optional().describe('Full name with org slug (@slug/name:tag)'),
|
|
29
29
|
tag: z.string().nullable().optional().describe('Snapshot tag'),
|
|
30
|
+
message: z.string().nullable().optional().describe('Build message'),
|
|
30
31
|
sizeBytes: z.number().describe('Snapshot size in bytes'),
|
|
31
32
|
fileCount: z.number().describe('Number of files'),
|
|
32
33
|
parentSnapshotId: z.string().nullable().optional().describe('Parent snapshot ID'),
|
|
@@ -92,6 +93,9 @@ export const getSubcommand = createCommand({
|
|
|
92
93
|
if (snapshot.tag) {
|
|
93
94
|
tableData['Tag'] = snapshot.tag;
|
|
94
95
|
}
|
|
96
|
+
if (snapshot.message) {
|
|
97
|
+
tableData['Message'] = snapshot.message;
|
|
98
|
+
}
|
|
95
99
|
tableData['Size'] = tui.formatBytes(snapshot.sizeBytes);
|
|
96
100
|
tableData['Files'] = snapshot.fileCount;
|
|
97
101
|
if (snapshot.public) {
|
package/src/tui/box.ts
CHANGED
|
@@ -250,3 +250,55 @@ export function errorBox(title: string, message: string): void {
|
|
|
250
250
|
|
|
251
251
|
console.log(lines.join('\n'));
|
|
252
252
|
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Draw a warning box with yellow border
|
|
256
|
+
*/
|
|
257
|
+
export function warningBox(title: string, message: string): void {
|
|
258
|
+
const termWidth = getTerminalWidth();
|
|
259
|
+
const maxWidth = termWidth - 3;
|
|
260
|
+
const boxWidth = Math.min(60, maxWidth);
|
|
261
|
+
const innerWidth = Math.max(boxWidth - 2, 1);
|
|
262
|
+
const contentWidth = Math.max(innerWidth - 1, 0);
|
|
263
|
+
const padding = 2;
|
|
264
|
+
|
|
265
|
+
const lines: string[] = [];
|
|
266
|
+
|
|
267
|
+
// Title line with warning symbol
|
|
268
|
+
const warnSymbol = colors.warning(symbols.warning);
|
|
269
|
+
const titleText = colors.warning(title);
|
|
270
|
+
const titleTextWidth = stringWidth(symbols.warning) + stringWidth(title);
|
|
271
|
+
const barsNeeded = Math.max(innerWidth - titleTextWidth - 3, 1);
|
|
272
|
+
const titleLine = `${warnSymbol} ${titleText} ${colors.warning(symbols.barH.repeat(barsNeeded) + symbols.cornerTR)}`;
|
|
273
|
+
|
|
274
|
+
lines.push(colors.warning(symbols.bar));
|
|
275
|
+
lines.push(titleLine);
|
|
276
|
+
|
|
277
|
+
// Empty line
|
|
278
|
+
const emptyLine = `${colors.warning(symbols.bar)}${' '.repeat(contentWidth)}${colors.warning(symbols.bar)}`;
|
|
279
|
+
lines.push(emptyLine);
|
|
280
|
+
|
|
281
|
+
// Wrap and render content lines
|
|
282
|
+
const wrapWidth = Math.max(contentWidth - padding * 2, 0);
|
|
283
|
+
const contentLines = message.split('\n');
|
|
284
|
+
for (const line of contentLines) {
|
|
285
|
+
const wrapped = stringWidth(line) > wrapWidth ? wrapText(line, wrapWidth) : [line];
|
|
286
|
+
for (const wl of wrapped) {
|
|
287
|
+
const lineLen = stringWidth(wl);
|
|
288
|
+
const leftPad = padding;
|
|
289
|
+
const rightPad = Math.max(contentWidth - lineLen - leftPad, 0);
|
|
290
|
+
lines.push(
|
|
291
|
+
`${colors.warning(symbols.bar)}${' '.repeat(leftPad)}${wl}${' '.repeat(rightPad)}${colors.warning(symbols.bar)}`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
lines.push(emptyLine);
|
|
297
|
+
|
|
298
|
+
// Bottom border
|
|
299
|
+
lines.push(
|
|
300
|
+
colors.warning(symbols.connect + symbols.barH.repeat(innerWidth - 1) + symbols.cornerBR)
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
console.log(lines.join('\n'));
|
|
304
|
+
}
|