@agentuity/cli 0.0.41 → 0.0.43
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/bin/cli.ts +7 -5
- package/dist/banner.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cmd/auth/index.d.ts.map +1 -1
- package/dist/cmd/auth/login.d.ts.map +1 -1
- package/dist/cmd/auth/whoami.d.ts +2 -0
- package/dist/cmd/auth/whoami.d.ts.map +1 -0
- package/dist/cmd/bundle/ast.d.ts +2 -0
- package/dist/cmd/bundle/ast.d.ts.map +1 -1
- package/dist/cmd/bundle/index.d.ts +1 -1
- package/dist/cmd/bundle/index.d.ts.map +1 -1
- package/dist/cmd/bundle/plugin.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts +2 -0
- package/dist/cmd/cloud/deploy.d.ts.map +1 -0
- package/dist/cmd/cloud/index.d.ts +2 -0
- package/dist/cmd/cloud/index.d.ts.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/env/delete.d.ts +2 -0
- package/dist/cmd/env/delete.d.ts.map +1 -0
- package/dist/cmd/env/get.d.ts +2 -0
- package/dist/cmd/env/get.d.ts.map +1 -0
- package/dist/cmd/env/import.d.ts +2 -0
- package/dist/cmd/env/import.d.ts.map +1 -0
- package/dist/cmd/env/index.d.ts +2 -0
- package/dist/cmd/env/index.d.ts.map +1 -0
- package/dist/cmd/env/list.d.ts +2 -0
- package/dist/cmd/env/list.d.ts.map +1 -0
- package/dist/cmd/env/pull.d.ts +2 -0
- package/dist/cmd/env/pull.d.ts.map +1 -0
- package/dist/cmd/env/push.d.ts +2 -0
- package/dist/cmd/env/push.d.ts.map +1 -0
- package/dist/cmd/env/set.d.ts +2 -0
- package/dist/cmd/env/set.d.ts.map +1 -0
- package/dist/cmd/project/delete.d.ts.map +1 -1
- package/dist/cmd/project/download.d.ts +1 -1
- package/dist/cmd/project/download.d.ts.map +1 -1
- package/dist/cmd/project/list.d.ts.map +1 -1
- package/dist/cmd/project/show.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.d.ts +1 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/secret/delete.d.ts +2 -0
- package/dist/cmd/secret/delete.d.ts.map +1 -0
- package/dist/cmd/secret/get.d.ts +2 -0
- package/dist/cmd/secret/get.d.ts.map +1 -0
- package/dist/cmd/secret/import.d.ts +2 -0
- package/dist/cmd/secret/import.d.ts.map +1 -0
- package/dist/cmd/secret/index.d.ts +2 -0
- package/dist/cmd/secret/index.d.ts.map +1 -0
- package/dist/cmd/secret/list.d.ts +2 -0
- package/dist/cmd/secret/list.d.ts.map +1 -0
- package/dist/cmd/secret/pull.d.ts +2 -0
- package/dist/cmd/secret/pull.d.ts.map +1 -0
- package/dist/cmd/secret/push.d.ts +2 -0
- package/dist/cmd/secret/push.d.ts.map +1 -0
- package/dist/cmd/secret/set.d.ts +2 -0
- package/dist/cmd/secret/set.d.ts.map +1 -0
- package/dist/cmd/version/index.d.ts.map +1 -1
- package/dist/config.d.ts +4 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/env-util.d.ts +67 -0
- package/dist/env-util.d.ts.map +1 -0
- package/dist/env-util.test.d.ts +2 -0
- package/dist/env-util.test.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/schema-parser.d.ts.map +1 -1
- package/dist/steps.d.ts.map +1 -1
- package/dist/tui.d.ts +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/types.d.ts +35 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/banner.ts +7 -2
- package/src/cli.ts +46 -5
- package/src/cmd/auth/index.ts +2 -1
- package/src/cmd/auth/login.ts +2 -4
- package/src/cmd/auth/whoami.ts +69 -0
- package/src/cmd/bundle/ast.ts +169 -4
- package/src/cmd/bundle/index.ts +2 -2
- package/src/cmd/bundle/plugin.ts +42 -1
- package/src/cmd/cloud/deploy.ts +129 -0
- package/src/cmd/cloud/index.ts +8 -0
- package/src/cmd/dev/index.ts +93 -9
- package/src/cmd/env/delete.ts +62 -0
- package/src/cmd/env/get.ts +66 -0
- package/src/cmd/env/import.ts +117 -0
- package/src/cmd/env/index.ts +22 -0
- package/src/cmd/env/list.ts +69 -0
- package/src/cmd/env/pull.ts +93 -0
- package/src/cmd/env/push.ts +55 -0
- package/src/cmd/env/set.ts +86 -0
- package/src/cmd/project/create.ts +1 -1
- package/src/cmd/project/delete.ts +43 -2
- package/src/cmd/project/download.ts +1 -1
- package/src/cmd/project/list.ts +33 -2
- package/src/cmd/project/show.ts +35 -3
- package/src/cmd/project/template-flow.ts +53 -12
- package/src/cmd/secret/delete.ts +55 -0
- package/src/cmd/secret/get.ts +67 -0
- package/src/cmd/secret/import.ts +79 -0
- package/src/cmd/secret/index.ts +22 -0
- package/src/cmd/secret/list.ts +69 -0
- package/src/cmd/secret/pull.ts +91 -0
- package/src/cmd/secret/push.ts +55 -0
- package/src/cmd/secret/set.ts +60 -0
- package/src/cmd/version/index.ts +2 -1
- package/src/config.ts +60 -7
- package/src/env-util.test.ts +194 -0
- package/src/env-util.ts +290 -0
- package/src/index.ts +5 -1
- package/src/schema-parser.ts +2 -3
- package/src/steps.ts +79 -4
- package/src/tui.ts +92 -56
- package/src/types.ts +30 -1
- package/dist/logger.d.ts +0 -24
- package/dist/logger.d.ts.map +0 -1
- package/src/logger.ts +0 -235
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand } from '../../types';
|
|
3
|
+
import * as tui from '../../tui';
|
|
4
|
+
import { projectEnvUpdate } from '@agentuity/server';
|
|
5
|
+
import { getAPIBaseURL, APIClient } from '../../api';
|
|
6
|
+
import { loadProjectConfig } from '../../config';
|
|
7
|
+
import {
|
|
8
|
+
findEnvFile,
|
|
9
|
+
readEnvFile,
|
|
10
|
+
writeEnvFile,
|
|
11
|
+
filterAgentuitySdkKeys,
|
|
12
|
+
looksLikeSecret,
|
|
13
|
+
} from '../../env-util';
|
|
14
|
+
import { getCommand } from '../../command-prefix';
|
|
15
|
+
|
|
16
|
+
export const setSubcommand = createSubcommand({
|
|
17
|
+
name: 'set',
|
|
18
|
+
description: 'Set an environment variable',
|
|
19
|
+
requiresAuth: true,
|
|
20
|
+
schema: {
|
|
21
|
+
args: z.object({
|
|
22
|
+
key: z.string().describe('the environment variable key'),
|
|
23
|
+
value: z.string().describe('the environment variable value'),
|
|
24
|
+
}),
|
|
25
|
+
options: z.object({
|
|
26
|
+
dir: z.string().optional().describe('project directory (default: current directory)'),
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async handler(ctx) {
|
|
31
|
+
const { args, opts, config } = ctx;
|
|
32
|
+
const dir = opts?.dir ?? process.cwd();
|
|
33
|
+
|
|
34
|
+
// Validate key doesn't start with AGENTUITY_
|
|
35
|
+
if (args.key.startsWith('AGENTUITY_')) {
|
|
36
|
+
tui.fatal('Cannot set AGENTUITY_ prefixed variables. These are reserved for system use.');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Detect if this looks like a secret
|
|
40
|
+
if (looksLikeSecret(args.key, args.value)) {
|
|
41
|
+
tui.warning(`The variable '${args.key}' looks like it should be a secret.`);
|
|
42
|
+
tui.info(`Secrets should be stored using: ${getCommand('secret set <key> <value>')}`);
|
|
43
|
+
tui.info('This keeps them more secure and properly masked in the cloud.');
|
|
44
|
+
|
|
45
|
+
const response = await tui.confirm(
|
|
46
|
+
'Do you still want to store this as a regular environment variable?',
|
|
47
|
+
false
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (!response) {
|
|
51
|
+
tui.info(
|
|
52
|
+
`Cancelled. Use "${getCommand('secret set')}" to store this as a secret instead.`
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Load project config to get project ID
|
|
59
|
+
const projectConfig = await loadProjectConfig(dir);
|
|
60
|
+
if (!projectConfig) {
|
|
61
|
+
tui.fatal(`No Agentuity project found in ${dir}. Missing agentuity.json`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const apiUrl = getAPIBaseURL(config);
|
|
65
|
+
const client = new APIClient(apiUrl, config);
|
|
66
|
+
|
|
67
|
+
// Set in cloud
|
|
68
|
+
await tui.spinner('Setting environment variable in cloud', () => {
|
|
69
|
+
return projectEnvUpdate(client, {
|
|
70
|
+
id: projectConfig.projectId,
|
|
71
|
+
env: { [args.key]: args.value },
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Update local .env.production file
|
|
76
|
+
const envFilePath = await findEnvFile(dir);
|
|
77
|
+
const currentEnv = await readEnvFile(envFilePath);
|
|
78
|
+
currentEnv[args.key] = args.value;
|
|
79
|
+
|
|
80
|
+
// Filter out AGENTUITY_ keys before writing
|
|
81
|
+
const filteredEnv = filterAgentuitySdkKeys(currentEnv);
|
|
82
|
+
await writeEnvFile(envFilePath, filteredEnv);
|
|
83
|
+
|
|
84
|
+
tui.success(`Environment variable '${args.key}' set successfully (cloud + ${envFilePath})`);
|
|
85
|
+
},
|
|
86
|
+
});
|
|
@@ -31,7 +31,7 @@ export const createProjectSubcommand = createSubcommand({
|
|
|
31
31
|
.optional()
|
|
32
32
|
.default(true)
|
|
33
33
|
.describe('Run bun run build after installing (use --no-build to skip)'),
|
|
34
|
-
confirm: z.boolean().optional().describe('
|
|
34
|
+
confirm: z.boolean().optional().describe('Skip confirmation prompts'),
|
|
35
35
|
register: z
|
|
36
36
|
.boolean()
|
|
37
37
|
.default(true)
|
|
@@ -1,13 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { createSubcommand } from '../../types';
|
|
3
|
+
import * as tui from '../../tui';
|
|
4
|
+
import { projectDelete } from '@agentuity/server';
|
|
5
|
+
import { getAPIBaseURL, APIClient } from '../../api';
|
|
2
6
|
|
|
3
7
|
export const deleteSubcommand = createSubcommand({
|
|
4
8
|
name: 'delete',
|
|
5
9
|
description: 'Delete a project',
|
|
6
10
|
aliases: ['rm', 'del'],
|
|
7
11
|
requiresAuth: true,
|
|
12
|
+
schema: {
|
|
13
|
+
args: z.object({
|
|
14
|
+
id: z.string().describe('the project id'),
|
|
15
|
+
}),
|
|
16
|
+
options: z.object({
|
|
17
|
+
confirm: z.boolean().optional().describe('Skip confirmation prompts'),
|
|
18
|
+
}),
|
|
19
|
+
},
|
|
8
20
|
|
|
9
21
|
async handler(ctx) {
|
|
10
|
-
const {
|
|
11
|
-
|
|
22
|
+
const { args, opts, config } = ctx;
|
|
23
|
+
|
|
24
|
+
const apiUrl = getAPIBaseURL(config);
|
|
25
|
+
const client = new APIClient(apiUrl, config);
|
|
26
|
+
|
|
27
|
+
const skipConfirm = opts?.confirm === true;
|
|
28
|
+
|
|
29
|
+
if (!process.stdout.isTTY && !skipConfirm) {
|
|
30
|
+
tui.fatal('no TTY and --confirm is false');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!skipConfirm) {
|
|
34
|
+
const ok = await tui.confirm('Are you sure you want to delete', false);
|
|
35
|
+
if (!ok) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const deleted = await tui.spinner('Deleting project', async () => {
|
|
41
|
+
const val = await projectDelete(client!, args.id);
|
|
42
|
+
if (val.length === 1 && val[0] === args.id) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (deleted) {
|
|
49
|
+
tui.success(`Project ${args.id} deleted`);
|
|
50
|
+
} else {
|
|
51
|
+
tui.warning(`${args.id} not found`);
|
|
52
|
+
}
|
|
12
53
|
},
|
|
13
54
|
});
|
|
@@ -13,7 +13,7 @@ import { tmpdir } from 'node:os';
|
|
|
13
13
|
import { finished } from 'node:stream/promises';
|
|
14
14
|
import { createGunzip } from 'node:zlib';
|
|
15
15
|
import { extract, type Headers } from 'tar-fs';
|
|
16
|
-
import type { Logger } from '
|
|
16
|
+
import type { Logger } from '@agentuity/core';
|
|
17
17
|
import * as tui from '../../tui';
|
|
18
18
|
import { downloadWithSpinner } from '../../download';
|
|
19
19
|
import type { TemplateInfo } from './templates';
|
package/src/cmd/project/list.ts
CHANGED
|
@@ -1,13 +1,44 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { createSubcommand } from '../../types';
|
|
3
|
+
import * as tui from '../../tui';
|
|
4
|
+
import { projectList } from '@agentuity/server';
|
|
5
|
+
import { getAPIBaseURL, APIClient } from '../../api';
|
|
2
6
|
|
|
3
7
|
export const listSubcommand = createSubcommand({
|
|
4
8
|
name: 'list',
|
|
5
9
|
description: 'List all projects',
|
|
6
10
|
aliases: ['ls'],
|
|
7
11
|
requiresAuth: true,
|
|
12
|
+
schema: {
|
|
13
|
+
options: z.object({
|
|
14
|
+
format: z
|
|
15
|
+
.enum(['json', 'table'])
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('the output format: json, table (default)'),
|
|
18
|
+
}),
|
|
19
|
+
},
|
|
8
20
|
|
|
9
21
|
async handler(ctx) {
|
|
10
|
-
const {
|
|
11
|
-
|
|
22
|
+
const { config, opts } = ctx;
|
|
23
|
+
|
|
24
|
+
const apiUrl = getAPIBaseURL(config);
|
|
25
|
+
const client = new APIClient(apiUrl, config);
|
|
26
|
+
|
|
27
|
+
const projects = await tui.spinner('Fetching projects', () => {
|
|
28
|
+
return projectList(client!);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// TODO: might want to sort by the last org_id we used
|
|
32
|
+
if (projects) {
|
|
33
|
+
projects.sort((a, b) => {
|
|
34
|
+
return a.name.localeCompare(b.name);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (opts?.format === 'json') {
|
|
39
|
+
console.log(JSON.stringify(projects, null, 2));
|
|
40
|
+
} else {
|
|
41
|
+
console.table(projects, ['id', 'name', 'orgName']);
|
|
42
|
+
}
|
|
12
43
|
},
|
|
13
44
|
});
|
package/src/cmd/project/show.ts
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { createSubcommand } from '../../types';
|
|
3
|
+
import * as tui from '../../tui';
|
|
4
|
+
import { projectGet } from '@agentuity/server';
|
|
5
|
+
import { getAPIBaseURL, APIClient } from '../../api';
|
|
2
6
|
|
|
3
7
|
export const showSubcommand = createSubcommand({
|
|
4
8
|
name: 'show',
|
|
5
|
-
|
|
9
|
+
aliases: ['get'],
|
|
10
|
+
description: 'Show project detail',
|
|
6
11
|
requiresAuth: true,
|
|
12
|
+
schema: {
|
|
13
|
+
args: z.object({
|
|
14
|
+
id: z.string().describe('the project id'),
|
|
15
|
+
}),
|
|
16
|
+
options: z.object({
|
|
17
|
+
format: z
|
|
18
|
+
.enum(['json', 'table'])
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('the output format: json, table (default)'),
|
|
21
|
+
}),
|
|
22
|
+
},
|
|
7
23
|
|
|
8
24
|
async handler(ctx) {
|
|
9
|
-
const {
|
|
10
|
-
|
|
25
|
+
const { opts, args, config } = ctx;
|
|
26
|
+
|
|
27
|
+
const apiUrl = getAPIBaseURL(config);
|
|
28
|
+
const client = new APIClient(apiUrl, config);
|
|
29
|
+
|
|
30
|
+
const project = await tui.spinner('Fetching project', () => {
|
|
31
|
+
return projectGet(client!, { id: args.id, mask: true });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (!project) {
|
|
35
|
+
tui.fatal('Project not found');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (opts?.format === 'json') {
|
|
39
|
+
console.log(JSON.stringify(project, null, 2));
|
|
40
|
+
} else {
|
|
41
|
+
console.table([project], ['id', 'orgId']);
|
|
42
|
+
}
|
|
11
43
|
},
|
|
12
44
|
});
|
|
@@ -4,12 +4,13 @@ import { cwd } from 'node:process';
|
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import enquirer from 'enquirer';
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
projectCreate,
|
|
8
8
|
projectExists,
|
|
9
9
|
listOrganizations,
|
|
10
|
+
projectEnvUpdate,
|
|
10
11
|
type OrganizationList,
|
|
11
12
|
} from '@agentuity/server';
|
|
12
|
-
import type { Logger } from '
|
|
13
|
+
import type { Logger } from '@agentuity/core';
|
|
13
14
|
import * as tui from '../../tui';
|
|
14
15
|
import { playSound } from '../../sound';
|
|
15
16
|
import { fetchTemplates, type TemplateInfo } from './templates';
|
|
@@ -17,7 +18,13 @@ import { downloadTemplate, setupProject } from './download';
|
|
|
17
18
|
import { showBanner } from '../../banner';
|
|
18
19
|
import type { AuthData, Config } from '../../types';
|
|
19
20
|
import { getAPIBaseURL, APIClient } from '../../api';
|
|
20
|
-
import { createProjectConfig } from '../../config';
|
|
21
|
+
import { createProjectConfig, saveOrgId } from '../../config';
|
|
22
|
+
import {
|
|
23
|
+
findEnvFile,
|
|
24
|
+
readEnvFile,
|
|
25
|
+
filterAgentuitySdkKeys,
|
|
26
|
+
splitEnvAndSecrets,
|
|
27
|
+
} from '../../env-util';
|
|
21
28
|
|
|
22
29
|
interface CreateFlowOptions {
|
|
23
30
|
projectName?: string;
|
|
@@ -53,10 +60,8 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
53
60
|
tui.info(`📋 Loading templates from local directory: ${templateDir}...\n`);
|
|
54
61
|
}
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
await tui.spinner('Fetching templates', async () => {
|
|
59
|
-
templates = await fetchTemplates(templateDir, templateBranch);
|
|
63
|
+
const templates = await tui.spinner('Fetching templates', async () => {
|
|
64
|
+
return fetchTemplates(templateDir, templateBranch);
|
|
60
65
|
});
|
|
61
66
|
|
|
62
67
|
if (templates.length === 0) {
|
|
@@ -73,13 +78,20 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
73
78
|
if (auth) {
|
|
74
79
|
const apiUrl = getAPIBaseURL(config);
|
|
75
80
|
client = new APIClient(apiUrl, config);
|
|
76
|
-
await tui.spinner('Fetching organizations', async () => {
|
|
81
|
+
orgs = await tui.spinner('Fetching organizations', async () => {
|
|
77
82
|
const resp = await listOrganizations(client!);
|
|
78
83
|
if (resp.data) {
|
|
79
|
-
|
|
84
|
+
return resp.data;
|
|
80
85
|
}
|
|
81
86
|
});
|
|
82
|
-
|
|
87
|
+
if (!orgs) {
|
|
88
|
+
tui.fatal('no organizations could be found for your login');
|
|
89
|
+
}
|
|
90
|
+
orgId = await tui.selectOrganization(orgs, config?.preferences?.orgId);
|
|
91
|
+
|
|
92
|
+
if (orgId && orgId !== config?.preferences?.orgId) {
|
|
93
|
+
await saveOrgId(orgId);
|
|
94
|
+
}
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
if (!projectName && !skipPrompts) {
|
|
@@ -200,21 +212,50 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
200
212
|
});
|
|
201
213
|
|
|
202
214
|
if (auth && client && orgId) {
|
|
215
|
+
let projectId: string | undefined;
|
|
216
|
+
|
|
203
217
|
await tui.spinner('Registering your project', async () => {
|
|
204
|
-
const res = await
|
|
218
|
+
const res = await projectCreate(client, {
|
|
205
219
|
name: projectName,
|
|
206
220
|
organization_id: orgId,
|
|
207
221
|
provider: 'bunjs',
|
|
208
222
|
});
|
|
209
223
|
if (res.success && res.data) {
|
|
224
|
+
projectId = res.data.id;
|
|
210
225
|
return createProjectConfig(dest, {
|
|
211
226
|
projectId: res.data.id,
|
|
212
227
|
orgId,
|
|
213
228
|
apiKey: res.data.api_key,
|
|
214
229
|
});
|
|
215
230
|
}
|
|
216
|
-
tui.fatal(res.message
|
|
231
|
+
tui.fatal(res.message ?? 'failed to register project');
|
|
217
232
|
});
|
|
233
|
+
|
|
234
|
+
// After registration, push any existing env/secrets from .env.production
|
|
235
|
+
if (projectId) {
|
|
236
|
+
await tui.spinner('Syncing environment variables', async () => {
|
|
237
|
+
try {
|
|
238
|
+
const envFilePath = await findEnvFile(dest);
|
|
239
|
+
const localEnv = await readEnvFile(envFilePath);
|
|
240
|
+
const filteredEnv = filterAgentuitySdkKeys(localEnv);
|
|
241
|
+
|
|
242
|
+
if (Object.keys(filteredEnv).length > 0) {
|
|
243
|
+
const { env, secrets } = splitEnvAndSecrets(filteredEnv);
|
|
244
|
+
await projectEnvUpdate(client, {
|
|
245
|
+
id: projectId!,
|
|
246
|
+
env,
|
|
247
|
+
secrets,
|
|
248
|
+
});
|
|
249
|
+
logger.debug(
|
|
250
|
+
`Synced ${Object.keys(filteredEnv).length} environment variables to cloud`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
} catch (error) {
|
|
254
|
+
// Non-fatal: just log the error
|
|
255
|
+
logger.debug('Failed to sync environment variables:', error);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
218
259
|
}
|
|
219
260
|
|
|
220
261
|
// Step 8: Show completion message
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand } from '../../types';
|
|
3
|
+
import * as tui from '../../tui';
|
|
4
|
+
import { projectEnvDelete } from '@agentuity/server';
|
|
5
|
+
import { getAPIBaseURL, APIClient } from '../../api';
|
|
6
|
+
import { loadProjectConfig } from '../../config';
|
|
7
|
+
import { findEnvFile, readEnvFile, writeEnvFile, filterAgentuitySdkKeys } from '../../env-util';
|
|
8
|
+
|
|
9
|
+
export const deleteSubcommand = createSubcommand({
|
|
10
|
+
name: 'delete',
|
|
11
|
+
aliases: ['del', 'remove', 'rm'],
|
|
12
|
+
description: 'Delete a secret',
|
|
13
|
+
requiresAuth: true,
|
|
14
|
+
schema: {
|
|
15
|
+
args: z.object({
|
|
16
|
+
key: z.string().describe('the secret key to delete'),
|
|
17
|
+
}),
|
|
18
|
+
options: z.object({
|
|
19
|
+
dir: z.string().optional().describe('project directory (default: current directory)'),
|
|
20
|
+
}),
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
async handler(ctx) {
|
|
24
|
+
const { args, opts, config } = ctx;
|
|
25
|
+
const dir = opts?.dir ?? process.cwd();
|
|
26
|
+
|
|
27
|
+
// Load project config to get project ID
|
|
28
|
+
const projectConfig = await loadProjectConfig(dir);
|
|
29
|
+
if (!projectConfig) {
|
|
30
|
+
tui.fatal(`No Agentuity project found in ${dir}. Missing agentuity.json`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const apiUrl = getAPIBaseURL(config);
|
|
34
|
+
const client = new APIClient(apiUrl, config);
|
|
35
|
+
|
|
36
|
+
// Delete from cloud (using secrets field)
|
|
37
|
+
await tui.spinner('Deleting secret from cloud', () => {
|
|
38
|
+
return projectEnvDelete(client, {
|
|
39
|
+
id: projectConfig.projectId,
|
|
40
|
+
secrets: [args.key],
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Update local .env.production file
|
|
45
|
+
const envFilePath = await findEnvFile(dir);
|
|
46
|
+
const currentEnv = await readEnvFile(envFilePath);
|
|
47
|
+
delete currentEnv[args.key];
|
|
48
|
+
|
|
49
|
+
// Filter out AGENTUITY_ keys before writing
|
|
50
|
+
const filteredEnv = filterAgentuitySdkKeys(currentEnv);
|
|
51
|
+
await writeEnvFile(envFilePath, filteredEnv);
|
|
52
|
+
|
|
53
|
+
tui.success(`Secret '${args.key}' deleted successfully (cloud + ${envFilePath})`);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand } from '../../types';
|
|
3
|
+
import * as tui from '../../tui';
|
|
4
|
+
import { projectGet } from '@agentuity/server';
|
|
5
|
+
import { getAPIBaseURL, APIClient } from '../../api';
|
|
6
|
+
import { loadProjectConfig } from '../../config';
|
|
7
|
+
import { maskSecret } from '../../env-util';
|
|
8
|
+
|
|
9
|
+
export const getSubcommand = createSubcommand({
|
|
10
|
+
name: 'get',
|
|
11
|
+
description: 'Get a secret value',
|
|
12
|
+
requiresAuth: true,
|
|
13
|
+
schema: {
|
|
14
|
+
args: z.object({
|
|
15
|
+
key: z.string().describe('the secret key'),
|
|
16
|
+
}),
|
|
17
|
+
options: z.object({
|
|
18
|
+
dir: z.string().optional().describe('project directory (default: current directory)'),
|
|
19
|
+
mask: z
|
|
20
|
+
.boolean()
|
|
21
|
+
.default(!!process.stdout.isTTY)
|
|
22
|
+
.describe('mask the value in output (default: true in TTY, false otherwise)'),
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
async handler(ctx) {
|
|
27
|
+
const { args, opts, config } = ctx;
|
|
28
|
+
const dir = opts?.dir ?? process.cwd();
|
|
29
|
+
|
|
30
|
+
// Load project config to get project ID
|
|
31
|
+
const projectConfig = await loadProjectConfig(dir);
|
|
32
|
+
if (!projectConfig) {
|
|
33
|
+
tui.fatal(`No Agentuity project found in ${dir}. Missing agentuity.json`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const apiUrl = getAPIBaseURL(config);
|
|
37
|
+
const client = new APIClient(apiUrl, config);
|
|
38
|
+
|
|
39
|
+
// Fetch project with unmasked secrets
|
|
40
|
+
const project = await tui.spinner('Fetching secrets', () => {
|
|
41
|
+
return projectGet(client, { id: projectConfig.projectId, mask: false });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Look for the key in secrets
|
|
45
|
+
const value = project.secrets?.[args.key];
|
|
46
|
+
|
|
47
|
+
if (value === undefined) {
|
|
48
|
+
tui.fatal(`Secret '${args.key}' not found`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (process.stdout.isTTY) {
|
|
52
|
+
// Display the value, masked by default
|
|
53
|
+
if (opts?.mask) {
|
|
54
|
+
tui.success(`${args.key}=${maskSecret(value)}`);
|
|
55
|
+
} else {
|
|
56
|
+
tui.success(`${args.key}=${value}`);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
// Display the value, masked by default
|
|
60
|
+
if (opts?.mask) {
|
|
61
|
+
console.log(`${args.key}=${maskSecret(value)}`);
|
|
62
|
+
} else {
|
|
63
|
+
console.log(`${args.key}=${value}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand } from '../../types';
|
|
3
|
+
import * as tui from '../../tui';
|
|
4
|
+
import { projectEnvUpdate } from '@agentuity/server';
|
|
5
|
+
import { getAPIBaseURL, APIClient } from '../../api';
|
|
6
|
+
import { loadProjectConfig } from '../../config';
|
|
7
|
+
import {
|
|
8
|
+
findEnvFile,
|
|
9
|
+
readEnvFile,
|
|
10
|
+
writeEnvFile,
|
|
11
|
+
filterAgentuitySdkKeys,
|
|
12
|
+
mergeEnvVars,
|
|
13
|
+
} from '../../env-util';
|
|
14
|
+
|
|
15
|
+
export const importSubcommand = createSubcommand({
|
|
16
|
+
name: 'import',
|
|
17
|
+
description: 'Import secrets from a file to cloud and local .env.production',
|
|
18
|
+
requiresAuth: true,
|
|
19
|
+
schema: {
|
|
20
|
+
args: z.object({
|
|
21
|
+
file: z.string().describe('path to the .env file to import'),
|
|
22
|
+
}),
|
|
23
|
+
options: z.object({
|
|
24
|
+
dir: z.string().optional().describe('project directory (default: current directory)'),
|
|
25
|
+
}),
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
async handler(ctx) {
|
|
29
|
+
const { args, opts, config } = ctx;
|
|
30
|
+
const dir = opts?.dir ?? process.cwd();
|
|
31
|
+
|
|
32
|
+
// Load project config to get project ID
|
|
33
|
+
const projectConfig = await loadProjectConfig(dir);
|
|
34
|
+
if (!projectConfig) {
|
|
35
|
+
tui.fatal(`No Agentuity project found in ${dir}. Missing agentuity.json`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Read the import file
|
|
39
|
+
const importedSecrets = await readEnvFile(args.file);
|
|
40
|
+
|
|
41
|
+
if (Object.keys(importedSecrets).length === 0) {
|
|
42
|
+
tui.warning(`No secrets found in ${args.file}`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Filter out AGENTUITY_ prefixed keys
|
|
47
|
+
const filteredSecrets = filterAgentuitySdkKeys(importedSecrets);
|
|
48
|
+
|
|
49
|
+
if (Object.keys(filteredSecrets).length === 0) {
|
|
50
|
+
tui.warning('No valid secrets to import (all were AGENTUITY_ prefixed)');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const apiUrl = getAPIBaseURL(config);
|
|
55
|
+
const client = new APIClient(apiUrl, config);
|
|
56
|
+
|
|
57
|
+
// Push to cloud (using secrets field)
|
|
58
|
+
await tui.spinner('Importing secrets to cloud', () => {
|
|
59
|
+
return projectEnvUpdate(client, {
|
|
60
|
+
id: projectConfig.projectId,
|
|
61
|
+
secrets: filteredSecrets,
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Merge with local .env.production file
|
|
66
|
+
const localEnvPath = await findEnvFile(dir);
|
|
67
|
+
const localEnv = await readEnvFile(localEnvPath);
|
|
68
|
+
const mergedEnv = mergeEnvVars(localEnv, filteredSecrets);
|
|
69
|
+
|
|
70
|
+
await writeEnvFile(localEnvPath, mergedEnv, {
|
|
71
|
+
skipKeys: Object.keys(mergedEnv).filter((k) => k.startsWith('AGENTUITY_')),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const count = Object.keys(filteredSecrets).length;
|
|
75
|
+
tui.success(
|
|
76
|
+
`Imported ${count} secret${count !== 1 ? 's' : ''} from ${args.file} to cloud and ${localEnvPath}`
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createCommand } from '../../types';
|
|
2
|
+
import { pullSubcommand } from './pull';
|
|
3
|
+
import { pushSubcommand } from './push';
|
|
4
|
+
import { setSubcommand } from './set';
|
|
5
|
+
import { getSubcommand } from './get';
|
|
6
|
+
import { deleteSubcommand } from './delete';
|
|
7
|
+
import { importSubcommand } from './import';
|
|
8
|
+
import { listSubcommand } from './list';
|
|
9
|
+
|
|
10
|
+
export const command = createCommand({
|
|
11
|
+
name: 'secret',
|
|
12
|
+
description: 'Manage secrets for your project',
|
|
13
|
+
subcommands: [
|
|
14
|
+
listSubcommand,
|
|
15
|
+
pullSubcommand,
|
|
16
|
+
pushSubcommand,
|
|
17
|
+
setSubcommand,
|
|
18
|
+
getSubcommand,
|
|
19
|
+
deleteSubcommand,
|
|
20
|
+
importSubcommand,
|
|
21
|
+
],
|
|
22
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand } from '../../types';
|
|
3
|
+
import * as tui from '../../tui';
|
|
4
|
+
import { projectGet } from '@agentuity/server';
|
|
5
|
+
import { getAPIBaseURL, APIClient } from '../../api';
|
|
6
|
+
import { loadProjectConfig } from '../../config';
|
|
7
|
+
import { maskSecret } from '../../env-util';
|
|
8
|
+
|
|
9
|
+
export const listSubcommand = createSubcommand({
|
|
10
|
+
name: 'list',
|
|
11
|
+
aliases: ['ls'],
|
|
12
|
+
description: 'List all secrets',
|
|
13
|
+
requiresAuth: true,
|
|
14
|
+
schema: {
|
|
15
|
+
options: z.object({
|
|
16
|
+
dir: z.string().optional().describe('project directory (default: current directory)'),
|
|
17
|
+
mask: z
|
|
18
|
+
.boolean()
|
|
19
|
+
.default(!!process.stdout.isTTY)
|
|
20
|
+
.describe('mask the values in output (default: true in TTY for secrets)'),
|
|
21
|
+
}),
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
async handler(ctx) {
|
|
25
|
+
const { opts, config } = ctx;
|
|
26
|
+
const dir = opts?.dir ?? process.cwd();
|
|
27
|
+
|
|
28
|
+
// Load project config to get project ID
|
|
29
|
+
const projectConfig = await loadProjectConfig(dir);
|
|
30
|
+
if (!projectConfig) {
|
|
31
|
+
tui.fatal(`No Agentuity project found in ${dir}. Missing agentuity.json`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const apiUrl = getAPIBaseURL(config);
|
|
35
|
+
const client = new APIClient(apiUrl, config);
|
|
36
|
+
|
|
37
|
+
// Fetch project with unmasked secrets
|
|
38
|
+
const project = await tui.spinner('Fetching secrets', () => {
|
|
39
|
+
return projectGet(client, { id: projectConfig.projectId, mask: false });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const secrets = project.secrets || {};
|
|
43
|
+
|
|
44
|
+
if (Object.keys(secrets).length === 0) {
|
|
45
|
+
tui.info('No secrets found');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Display the secrets
|
|
50
|
+
if (process.stdout.isTTY) {
|
|
51
|
+
tui.newline();
|
|
52
|
+
tui.success(`Secrets (${Object.keys(secrets).length}):`);
|
|
53
|
+
tui.newline();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const sortedKeys = Object.keys(secrets).sort();
|
|
57
|
+
// For secrets, masking is enabled by default in TTY (can be disabled with --no-mask)
|
|
58
|
+
const shouldMask = opts?.mask !== false;
|
|
59
|
+
for (const key of sortedKeys) {
|
|
60
|
+
const value = secrets[key];
|
|
61
|
+
const displayValue = shouldMask ? maskSecret(value) : value;
|
|
62
|
+
if (process.stdout.isTTY) {
|
|
63
|
+
console.log(`${tui.bold(key)}=${displayValue}`);
|
|
64
|
+
} else {
|
|
65
|
+
console.log(`${key}=${displayValue}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
});
|