@agentuity/cli 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +6 -2
- package/dist/auth.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +44 -91
- package/dist/cli.js.map +1 -1
- package/dist/cmd/auth/index.d.ts.map +1 -1
- package/dist/cmd/auth/index.js +3 -0
- package/dist/cmd/auth/index.js.map +1 -1
- package/dist/cmd/auth/org/index.d.ts +2 -0
- package/dist/cmd/auth/org/index.d.ts.map +1 -0
- package/dist/cmd/auth/org/index.js +121 -0
- package/dist/cmd/auth/org/index.js.map +1 -0
- package/dist/cmd/build/vite/beacon-plugin.d.ts +19 -0
- package/dist/cmd/build/vite/beacon-plugin.d.ts.map +1 -0
- package/dist/cmd/build/vite/beacon-plugin.js +137 -0
- package/dist/cmd/build/vite/beacon-plugin.js.map +1 -0
- package/dist/cmd/build/vite/vite-builder.d.ts +2 -0
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +12 -2
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/webanalytics-generator.js +25 -9
- package/dist/cmd/build/webanalytics-generator.js.map +1 -1
- package/dist/cmd/cloud/db/get.d.ts.map +1 -1
- package/dist/cmd/cloud/db/get.js +7 -0
- package/dist/cmd/cloud/db/get.js.map +1 -1
- package/dist/cmd/cloud/db/list.d.ts.map +1 -1
- package/dist/cmd/cloud/db/list.js +19 -6
- package/dist/cmd/cloud/db/list.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +24 -1
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
- package/dist/cmd/cloud/deployment/show.js +5 -0
- package/dist/cmd/cloud/deployment/show.js.map +1 -1
- package/dist/cmd/cloud/index.d.ts.map +1 -1
- package/dist/cmd/cloud/index.js +3 -0
- package/dist/cmd/cloud/index.js.map +1 -1
- package/dist/cmd/cloud/region/index.d.ts +2 -0
- package/dist/cmd/cloud/region/index.d.ts.map +1 -0
- package/dist/cmd/cloud/region/index.js +136 -0
- package/dist/cmd/cloud/region/index.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/build.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/build.js +35 -5
- package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
- package/dist/cmd/cloud/scp/download.d.ts.map +1 -1
- package/dist/cmd/cloud/scp/download.js +4 -2
- package/dist/cmd/cloud/scp/download.js.map +1 -1
- package/dist/cmd/cloud/scp/upload.d.ts.map +1 -1
- package/dist/cmd/cloud/scp/upload.js +4 -2
- package/dist/cmd/cloud/scp/upload.js.map +1 -1
- package/dist/cmd/cloud/ssh.d.ts.map +1 -1
- package/dist/cmd/cloud/ssh.js +3 -1
- package/dist/cmd/cloud/ssh.js.map +1 -1
- package/dist/cmd/cloud/storage/get.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/get.js +12 -5
- package/dist/cmd/cloud/storage/get.js.map +1 -1
- package/dist/cmd/cloud/storage/list.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/list.js +10 -0
- package/dist/cmd/cloud/storage/list.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +62 -5
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/help/index.d.ts.map +1 -1
- package/dist/cmd/help/index.js +8 -18
- package/dist/cmd/help/index.js.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/create.js +10 -7
- package/dist/cmd/project/create.js.map +1 -1
- package/dist/cmd/project/import.d.ts +2 -0
- package/dist/cmd/project/import.d.ts.map +1 -0
- package/dist/cmd/project/import.js +88 -0
- package/dist/cmd/project/import.js.map +1 -0
- package/dist/cmd/project/index.d.ts.map +1 -1
- package/dist/cmd/project/index.js +3 -0
- package/dist/cmd/project/index.js.map +1 -1
- package/dist/cmd/project/reconcile.d.ts +67 -0
- package/dist/cmd/project/reconcile.d.ts.map +1 -0
- package/dist/cmd/project/reconcile.js +458 -0
- package/dist/cmd/project/reconcile.js.map +1 -0
- package/dist/cmd/project/template-flow.d.ts +11 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +25 -7
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/config.d.ts +8 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +50 -21
- package/dist/config.js.map +1 -1
- package/dist/legacy-check.d.ts.map +1 -1
- package/dist/legacy-check.js +8 -0
- package/dist/legacy-check.js.map +1 -1
- package/dist/program-ref.d.ts +4 -0
- package/dist/program-ref.d.ts.map +1 -0
- package/dist/program-ref.js +8 -0
- package/dist/program-ref.js.map +1 -0
- package/dist/regions.d.ts +8 -0
- package/dist/regions.d.ts.map +1 -0
- package/dist/regions.js +77 -0
- package/dist/regions.js.map +1 -0
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +5 -4
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +6 -6
- package/src/auth.ts +8 -8
- package/src/cli.ts +52 -108
- package/src/cmd/auth/index.ts +3 -0
- package/src/cmd/auth/org/index.ts +142 -0
- package/src/cmd/build/vite/beacon-plugin.ts +162 -0
- package/src/cmd/build/vite/vite-builder.ts +15 -2
- package/src/cmd/build/webanalytics-generator.ts +25 -9
- package/src/cmd/cloud/db/get.ts +7 -0
- package/src/cmd/cloud/db/list.ts +20 -6
- package/src/cmd/cloud/deploy.ts +32 -1
- package/src/cmd/cloud/deployment/show.ts +5 -0
- package/src/cmd/cloud/index.ts +3 -0
- package/src/cmd/cloud/region/index.ts +157 -0
- package/src/cmd/cloud/sandbox/snapshot/build.ts +42 -5
- package/src/cmd/cloud/scp/download.ts +6 -2
- package/src/cmd/cloud/scp/upload.ts +6 -2
- package/src/cmd/cloud/ssh.ts +5 -1
- package/src/cmd/cloud/storage/get.ts +12 -5
- package/src/cmd/cloud/storage/list.ts +11 -0
- package/src/cmd/dev/index.ts +62 -5
- package/src/cmd/help/index.ts +8 -22
- package/src/cmd/project/create.ts +10 -7
- package/src/cmd/project/import.ts +98 -0
- package/src/cmd/project/index.ts +3 -0
- package/src/cmd/project/reconcile.ts +606 -0
- package/src/cmd/project/template-flow.ts +37 -7
- package/src/config.ts +58 -22
- package/src/legacy-check.ts +10 -0
- package/src/program-ref.ts +11 -0
- package/src/regions.ts +95 -0
- package/src/tui.ts +6 -4
- package/src/types.ts +1 -0
package/src/cli.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { mkdir, unlink } from 'node:fs/promises';
|
|
2
1
|
import { homedir } from 'node:os';
|
|
3
|
-
import { resolve
|
|
2
|
+
import { resolve } from 'node:path';
|
|
4
3
|
import { Command } from 'commander';
|
|
5
|
-
import { getDefaultConfigDir } from './config';
|
|
6
4
|
import type {
|
|
7
5
|
CommandDefinition,
|
|
8
6
|
SubcommandDefinition,
|
|
@@ -17,7 +15,8 @@ import type {
|
|
|
17
15
|
} from './types';
|
|
18
16
|
import { showBanner, generateBanner } from './banner';
|
|
19
17
|
import { requireAuth, optionalAuth, requireOrg, optionalOrg as selectOptionalOrg } from './auth';
|
|
20
|
-
import {
|
|
18
|
+
import { type RegionList, ValidationOutputError } from '@agentuity/server';
|
|
19
|
+
import { fetchRegionsWithCache } from './regions';
|
|
21
20
|
import enquirer from 'enquirer';
|
|
22
21
|
import * as tui from './tui';
|
|
23
22
|
import { parseArgsSchema, parseOptionsSchema, buildValidationInput } from './schema-parser';
|
|
@@ -27,6 +26,7 @@ import { ErrorCode, ExitCode, createError, exitWithError } from './errors';
|
|
|
27
26
|
import { getCommand } from './command-prefix';
|
|
28
27
|
import { isValidateMode, outputValidation, type ValidationResult } from './output';
|
|
29
28
|
import { StructuredError } from '@agentuity/core';
|
|
29
|
+
import { setProgram } from './program-ref';
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Check if an error is a CLI input validation error (Zod error from schema parsing),
|
|
@@ -462,6 +462,7 @@ async function promptProjectSelection(baseCtx: CommandContext): Promise<ProjectC
|
|
|
462
462
|
|
|
463
463
|
export async function createCLI(version: string): Promise<Command> {
|
|
464
464
|
const program = new Command();
|
|
465
|
+
setProgram(program);
|
|
465
466
|
|
|
466
467
|
program
|
|
467
468
|
.name('agentuity')
|
|
@@ -690,89 +691,6 @@ interface ResolveRegionOptions {
|
|
|
690
691
|
region?: string;
|
|
691
692
|
}
|
|
692
693
|
|
|
693
|
-
const REGIONS_CACHE_MAX_AGE_MS = 5 * 24 * 60 * 60 * 1000; // 5 days
|
|
694
|
-
const LEGACY_REGIONS_CACHE_FILE = 'regions.json';
|
|
695
|
-
|
|
696
|
-
function getRegionsCacheFile(profileName: string): string {
|
|
697
|
-
return `regions-${profileName}.json`;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
async function removeLegacyRegionsCache(logger: Logger): Promise<void> {
|
|
701
|
-
try {
|
|
702
|
-
const legacyPath = join(getDefaultConfigDir(), LEGACY_REGIONS_CACHE_FILE);
|
|
703
|
-
const file = Bun.file(legacyPath);
|
|
704
|
-
if (await file.exists()) {
|
|
705
|
-
await unlink(legacyPath);
|
|
706
|
-
logger.trace('removed legacy regions cache file');
|
|
707
|
-
}
|
|
708
|
-
} catch {
|
|
709
|
-
// Ignore errors when removing legacy file
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
interface RegionsCacheData {
|
|
714
|
-
timestamp: number;
|
|
715
|
-
regions: RegionList;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
async function getCachedRegions(profileName: string, logger: Logger): Promise<RegionList | null> {
|
|
719
|
-
try {
|
|
720
|
-
// Clean up legacy single-file cache from older versions
|
|
721
|
-
await removeLegacyRegionsCache(logger);
|
|
722
|
-
|
|
723
|
-
const cachePath = join(getDefaultConfigDir(), getRegionsCacheFile(profileName));
|
|
724
|
-
const file = Bun.file(cachePath);
|
|
725
|
-
if (!(await file.exists())) {
|
|
726
|
-
return null;
|
|
727
|
-
}
|
|
728
|
-
const data: RegionsCacheData = await file.json();
|
|
729
|
-
const age = Date.now() - data.timestamp;
|
|
730
|
-
if (age > REGIONS_CACHE_MAX_AGE_MS) {
|
|
731
|
-
logger.trace('regions cache expired for profile %s (age: %dms)', profileName, age);
|
|
732
|
-
return null;
|
|
733
|
-
}
|
|
734
|
-
logger.trace('using cached regions for profile %s (age: %dms)', profileName, age);
|
|
735
|
-
return data.regions;
|
|
736
|
-
} catch (error) {
|
|
737
|
-
logger.trace('failed to read regions cache for profile %s: %s', profileName, error);
|
|
738
|
-
return null;
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
async function saveRegionsCache(
|
|
743
|
-
profileName: string,
|
|
744
|
-
regions: RegionList,
|
|
745
|
-
logger: Logger
|
|
746
|
-
): Promise<void> {
|
|
747
|
-
try {
|
|
748
|
-
const cacheDir = getDefaultConfigDir();
|
|
749
|
-
await mkdir(cacheDir, { recursive: true });
|
|
750
|
-
const cachePath = join(cacheDir, getRegionsCacheFile(profileName));
|
|
751
|
-
const data: RegionsCacheData = {
|
|
752
|
-
timestamp: Date.now(),
|
|
753
|
-
regions,
|
|
754
|
-
};
|
|
755
|
-
await Bun.write(cachePath, JSON.stringify(data));
|
|
756
|
-
logger.trace('saved regions cache for profile %s', profileName);
|
|
757
|
-
} catch (error) {
|
|
758
|
-
logger.trace('failed to save regions cache for profile %s: %s', profileName, error);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
async function fetchRegionsWithCache(
|
|
763
|
-
profileName: string,
|
|
764
|
-
apiClient: APIClientType,
|
|
765
|
-
logger: Logger
|
|
766
|
-
): Promise<RegionList> {
|
|
767
|
-
const cached = await getCachedRegions(profileName, logger);
|
|
768
|
-
if (cached) {
|
|
769
|
-
return cached;
|
|
770
|
-
}
|
|
771
|
-
const regions = await listRegions(apiClient);
|
|
772
|
-
await saveRegionsCache(profileName, regions, logger);
|
|
773
|
-
return regions;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
694
|
async function resolveRegion(opts: ResolveRegionOptions): Promise<string | undefined> {
|
|
777
695
|
const { options, regions, logger, required } = opts;
|
|
778
696
|
|
|
@@ -1242,7 +1160,12 @@ async function registerSubcommand(
|
|
|
1242
1160
|
ctx as CommandContext & { apiClient: APIClientType }
|
|
1243
1161
|
);
|
|
1244
1162
|
}
|
|
1245
|
-
if (
|
|
1163
|
+
if (
|
|
1164
|
+
(normalized.requiresRegion ||
|
|
1165
|
+
normalized.optionalRegion ||
|
|
1166
|
+
normalized.requiresRegions) &&
|
|
1167
|
+
ctx.apiClient
|
|
1168
|
+
) {
|
|
1246
1169
|
const apiClient: APIClientType = ctx.apiClient as APIClientType;
|
|
1247
1170
|
const regions = await tui.spinner({
|
|
1248
1171
|
message: 'Fetching cloud regions',
|
|
@@ -1255,15 +1178,20 @@ async function registerSubcommand(
|
|
|
1255
1178
|
);
|
|
1256
1179
|
},
|
|
1257
1180
|
});
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1181
|
+
if (normalized.requiresRegions) {
|
|
1182
|
+
ctx.regions = regions;
|
|
1183
|
+
}
|
|
1184
|
+
if (normalized.requiresRegion || normalized.optionalRegion) {
|
|
1185
|
+
const region = await resolveRegion({
|
|
1186
|
+
options: options as Record<string, unknown>,
|
|
1187
|
+
regions,
|
|
1188
|
+
logger: baseCtx.logger,
|
|
1189
|
+
required: !!normalized.requiresRegion,
|
|
1190
|
+
region: project?.region,
|
|
1191
|
+
});
|
|
1192
|
+
if (region) {
|
|
1193
|
+
ctx.region = region;
|
|
1194
|
+
}
|
|
1267
1195
|
}
|
|
1268
1196
|
}
|
|
1269
1197
|
await executeOrValidate(
|
|
@@ -1318,7 +1246,12 @@ async function registerSubcommand(
|
|
|
1318
1246
|
ctx as CommandContext & { apiClient: APIClientType }
|
|
1319
1247
|
);
|
|
1320
1248
|
}
|
|
1321
|
-
if (
|
|
1249
|
+
if (
|
|
1250
|
+
(normalized.requiresRegion ||
|
|
1251
|
+
normalized.optionalRegion ||
|
|
1252
|
+
normalized.requiresRegions) &&
|
|
1253
|
+
ctx.apiClient
|
|
1254
|
+
) {
|
|
1322
1255
|
const apiClient: APIClientType = ctx.apiClient as APIClientType;
|
|
1323
1256
|
const regions = await tui.spinner({
|
|
1324
1257
|
message: 'Fetching cloud regions',
|
|
@@ -1331,15 +1264,20 @@ async function registerSubcommand(
|
|
|
1331
1264
|
);
|
|
1332
1265
|
},
|
|
1333
1266
|
});
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1267
|
+
if (normalized.requiresRegions) {
|
|
1268
|
+
ctx.regions = regions;
|
|
1269
|
+
}
|
|
1270
|
+
if (normalized.requiresRegion || normalized.optionalRegion) {
|
|
1271
|
+
const region = await resolveRegion({
|
|
1272
|
+
options: options as Record<string, unknown>,
|
|
1273
|
+
regions,
|
|
1274
|
+
logger: baseCtx.logger,
|
|
1275
|
+
required: !!normalized.requiresRegion,
|
|
1276
|
+
region: project?.region,
|
|
1277
|
+
});
|
|
1278
|
+
if (region) {
|
|
1279
|
+
ctx.region = region;
|
|
1280
|
+
}
|
|
1343
1281
|
}
|
|
1344
1282
|
}
|
|
1345
1283
|
if (subcommand.handler) {
|
|
@@ -1384,7 +1322,13 @@ async function registerSubcommand(
|
|
|
1384
1322
|
);
|
|
1385
1323
|
}
|
|
1386
1324
|
|
|
1387
|
-
|
|
1325
|
+
// Check if --confirm flag is set to skip interactive prompts
|
|
1326
|
+
const skipPrompts = options.confirm === true;
|
|
1327
|
+
const auth = await optionalAuth(
|
|
1328
|
+
baseCtx as CommandContext<undefined>,
|
|
1329
|
+
continueText,
|
|
1330
|
+
skipPrompts
|
|
1331
|
+
);
|
|
1388
1332
|
|
|
1389
1333
|
if (subcommand.schema) {
|
|
1390
1334
|
try {
|
package/src/cmd/auth/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { logoutCommand } from './logout';
|
|
|
5
5
|
import { signupCommand } from './signup';
|
|
6
6
|
import { whoamiCommand } from './whoami';
|
|
7
7
|
import { sshSubcommand } from './ssh';
|
|
8
|
+
import { orgSubcommand } from './org';
|
|
8
9
|
import { getCommand } from '../../command-prefix';
|
|
9
10
|
|
|
10
11
|
export const command = createCommand({
|
|
@@ -14,6 +15,7 @@ export const command = createCommand({
|
|
|
14
15
|
examples: [
|
|
15
16
|
{ command: getCommand('auth login'), description: 'Login to your account' },
|
|
16
17
|
{ command: getCommand('auth whoami'), description: 'Show current user info' },
|
|
18
|
+
{ command: getCommand('auth org select'), description: 'Set default organization' },
|
|
17
19
|
],
|
|
18
20
|
subcommands: [
|
|
19
21
|
apikeyCommand,
|
|
@@ -22,5 +24,6 @@ export const command = createCommand({
|
|
|
22
24
|
signupCommand,
|
|
23
25
|
whoamiCommand,
|
|
24
26
|
sshSubcommand,
|
|
27
|
+
orgSubcommand,
|
|
25
28
|
],
|
|
26
29
|
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubcommand, createCommand } from '../../../types';
|
|
3
|
+
import { getCommand } from '../../../command-prefix';
|
|
4
|
+
import { saveOrgId, clearOrgId } from '../../../config';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
import { listOrganizations } from '@agentuity/server';
|
|
7
|
+
|
|
8
|
+
const selectCommand = createSubcommand({
|
|
9
|
+
name: 'select',
|
|
10
|
+
description: 'Set the default organization for all commands',
|
|
11
|
+
tags: ['fast', 'requires-auth'],
|
|
12
|
+
requires: { auth: true, apiClient: true },
|
|
13
|
+
examples: [
|
|
14
|
+
{ command: getCommand('auth org select'), description: 'Select default organization' },
|
|
15
|
+
{
|
|
16
|
+
command: getCommand('auth org select org_abc123'),
|
|
17
|
+
description: 'Set specific organization as default',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
schema: {
|
|
21
|
+
args: z.object({
|
|
22
|
+
org_id: z.string().optional().describe('Organization ID to set as default'),
|
|
23
|
+
}),
|
|
24
|
+
response: z.object({
|
|
25
|
+
orgId: z.string().describe('The selected organization ID'),
|
|
26
|
+
name: z.string().describe('The organization name'),
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async handler(ctx) {
|
|
31
|
+
const { apiClient, args, options } = ctx;
|
|
32
|
+
const argOrgId = args.org_id;
|
|
33
|
+
|
|
34
|
+
const orgs = await tui.spinner({
|
|
35
|
+
message: 'Fetching organizations',
|
|
36
|
+
clearOnSuccess: true,
|
|
37
|
+
callback: () => listOrganizations(apiClient),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (orgs.length === 0) {
|
|
41
|
+
tui.fatal('No organizations found for your account');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let selectedOrgId = argOrgId;
|
|
45
|
+
|
|
46
|
+
if (selectedOrgId) {
|
|
47
|
+
const org = orgs.find((o) => o.id === selectedOrgId);
|
|
48
|
+
if (!org) {
|
|
49
|
+
tui.fatal(
|
|
50
|
+
`Organization '${selectedOrgId}' not found. Use '${getCommand('auth org select')}' to see available organizations.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
if (!process.stdin.isTTY) {
|
|
55
|
+
tui.fatal(
|
|
56
|
+
'Organization ID required in non-interactive mode. Usage: ' +
|
|
57
|
+
getCommand('auth org select <org_id>')
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
selectedOrgId = await tui.selectOrganization(orgs);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await saveOrgId(selectedOrgId);
|
|
64
|
+
|
|
65
|
+
const selectedOrg = orgs.find((o) => o.id === selectedOrgId)!;
|
|
66
|
+
|
|
67
|
+
if (!options.json) {
|
|
68
|
+
tui.success(`Default organization set to ${tui.bold(selectedOrg.name)}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { orgId: selectedOrgId, name: selectedOrg.name };
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const unselectCommand = createSubcommand({
|
|
76
|
+
name: 'unselect',
|
|
77
|
+
description: 'Clear the default organization preference',
|
|
78
|
+
tags: ['fast'],
|
|
79
|
+
examples: [
|
|
80
|
+
{ command: getCommand('auth org unselect'), description: 'Clear default organization' },
|
|
81
|
+
],
|
|
82
|
+
schema: {
|
|
83
|
+
response: z.object({
|
|
84
|
+
cleared: z.boolean().describe('Whether the preference was cleared'),
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
async handler(ctx) {
|
|
89
|
+
const { options, config } = ctx;
|
|
90
|
+
|
|
91
|
+
const hadOrg = !!config?.preferences?.orgId;
|
|
92
|
+
await clearOrgId();
|
|
93
|
+
|
|
94
|
+
if (!options.json) {
|
|
95
|
+
if (hadOrg) {
|
|
96
|
+
tui.success('Default organization cleared');
|
|
97
|
+
} else {
|
|
98
|
+
tui.info('No default organization was set');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { cleared: hadOrg };
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const currentCommand = createSubcommand({
|
|
107
|
+
name: 'current',
|
|
108
|
+
description: 'Show the current default organization',
|
|
109
|
+
tags: ['read-only', 'fast'],
|
|
110
|
+
idempotent: true,
|
|
111
|
+
examples: [
|
|
112
|
+
{ command: getCommand('auth org current'), description: 'Show default organization' },
|
|
113
|
+
{ command: getCommand('auth org current --json'), description: 'Show output in JSON format' },
|
|
114
|
+
],
|
|
115
|
+
schema: {
|
|
116
|
+
response: z.string().nullable().describe('The current organization ID or null if not set'),
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
async handler(ctx) {
|
|
120
|
+
const { options, config } = ctx;
|
|
121
|
+
const orgId = config?.preferences?.orgId || null;
|
|
122
|
+
|
|
123
|
+
if (!options.json) {
|
|
124
|
+
if (orgId) {
|
|
125
|
+
console.log(orgId);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return orgId;
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export const orgSubcommand = createCommand({
|
|
134
|
+
name: 'org',
|
|
135
|
+
description: 'Manage default organization preference',
|
|
136
|
+
tags: ['fast'],
|
|
137
|
+
examples: [
|
|
138
|
+
{ command: getCommand('auth org select'), description: 'Set default organization' },
|
|
139
|
+
{ command: getCommand('auth org current'), description: 'Show current default' },
|
|
140
|
+
],
|
|
141
|
+
subcommands: [selectCommand, unselectCommand, currentCommand],
|
|
142
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin to emit the analytics beacon as a hashed CDN asset
|
|
3
|
+
*
|
|
4
|
+
* This plugin:
|
|
5
|
+
* 1. Reads the pre-built beacon from @agentuity/frontend
|
|
6
|
+
* 2. Emits it as a Rollup asset with content-based hashing
|
|
7
|
+
* 3. Injects a <script data-agentuity-beacon> tag into the HTML
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Plugin, ResolvedConfig } from 'vite';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { createRequire } from 'node:module';
|
|
13
|
+
|
|
14
|
+
export interface BeaconPluginOptions {
|
|
15
|
+
/** Whether analytics is enabled */
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Read the pre-built beacon script from @agentuity/frontend package
|
|
21
|
+
* @param projectRoot - The root directory of the project (for resolving node_modules)
|
|
22
|
+
*/
|
|
23
|
+
async function readBeaconScript(projectRoot: string): Promise<string> {
|
|
24
|
+
let frontendPath: string | null = null;
|
|
25
|
+
|
|
26
|
+
// Strategy 1: Use Bun.resolve (works with workspaces and symlinks)
|
|
27
|
+
try {
|
|
28
|
+
frontendPath = await Bun.resolve('@agentuity/frontend', projectRoot);
|
|
29
|
+
} catch {
|
|
30
|
+
// Not found from project root
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Strategy 2: Try from this file's directory (for installed CLI case)
|
|
34
|
+
if (!frontendPath) {
|
|
35
|
+
try {
|
|
36
|
+
const thisDir = new URL('.', import.meta.url).pathname;
|
|
37
|
+
frontendPath = await Bun.resolve('@agentuity/frontend', thisDir);
|
|
38
|
+
} catch {
|
|
39
|
+
// Not found from CLI directory
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Strategy 3: Fallback to createRequire for Node.js compatibility
|
|
44
|
+
if (!frontendPath) {
|
|
45
|
+
try {
|
|
46
|
+
const projectRequire = createRequire(join(projectRoot, 'package.json'));
|
|
47
|
+
frontendPath = projectRequire.resolve('@agentuity/frontend');
|
|
48
|
+
} catch {
|
|
49
|
+
// Not found via createRequire
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!frontendPath) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
'Could not resolve @agentuity/frontend. Ensure the package is installed and built.'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The beacon.js file is in the dist folder of @agentuity/frontend
|
|
60
|
+
// frontendPath points to dist/index.js, so we go up one level
|
|
61
|
+
const packageDir = join(frontendPath, '..');
|
|
62
|
+
const beaconPath = join(packageDir, 'beacon.js');
|
|
63
|
+
|
|
64
|
+
const beaconFile = Bun.file(beaconPath);
|
|
65
|
+
if (!(await beaconFile.exists())) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Beacon script not found at ${beaconPath}. Run "bun run build" in @agentuity/frontend first.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return beaconFile.text();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Vite plugin that emits the analytics beacon as a hashed asset
|
|
76
|
+
* and injects a script tag into the HTML
|
|
77
|
+
*/
|
|
78
|
+
export function beaconPlugin(options: BeaconPluginOptions): Plugin {
|
|
79
|
+
const { enabled } = options;
|
|
80
|
+
|
|
81
|
+
let resolvedConfig: ResolvedConfig | null = null;
|
|
82
|
+
let beaconReferenceId: string | null = null;
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
name: 'agentuity:beacon',
|
|
86
|
+
apply: 'build',
|
|
87
|
+
|
|
88
|
+
configResolved(config) {
|
|
89
|
+
resolvedConfig = config;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
async buildStart() {
|
|
93
|
+
if (!enabled) return;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// Get project root from Vite config
|
|
97
|
+
const projectRoot = resolvedConfig?.root || process.cwd();
|
|
98
|
+
const beaconCode = await readBeaconScript(projectRoot);
|
|
99
|
+
|
|
100
|
+
// Emit the beacon as an asset - Rollup will hash it
|
|
101
|
+
beaconReferenceId = this.emitFile({
|
|
102
|
+
type: 'asset',
|
|
103
|
+
name: 'agentuity-beacon.js',
|
|
104
|
+
source: beaconCode,
|
|
105
|
+
});
|
|
106
|
+
} catch (error) {
|
|
107
|
+
this.error(`Failed to read beacon script: ${error instanceof Error ? error.message : String(error)}`);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// Use transformIndexHtml hook to modify the HTML
|
|
112
|
+
// This is called during the HTML transformation phase and works with Vite's HTML pipeline
|
|
113
|
+
transformIndexHtml: {
|
|
114
|
+
order: 'post',
|
|
115
|
+
handler(html, ctx) {
|
|
116
|
+
if (!enabled || !beaconReferenceId) return html;
|
|
117
|
+
|
|
118
|
+
// Get the final hashed filename using the bundle from the context
|
|
119
|
+
// We need to use ctx.bundle to access the emitted file
|
|
120
|
+
const bundle = ctx.bundle;
|
|
121
|
+
if (!bundle) return html;
|
|
122
|
+
|
|
123
|
+
// Find our beacon asset in the bundle
|
|
124
|
+
let beaconFileName: string | null = null;
|
|
125
|
+
for (const fileName of Object.keys(bundle)) {
|
|
126
|
+
if (fileName.includes('agentuity-beacon') && fileName.endsWith('.js')) {
|
|
127
|
+
beaconFileName = fileName;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!beaconFileName) return html;
|
|
133
|
+
|
|
134
|
+
// Build the beacon URL using Vite's configured base (respects CDN URL)
|
|
135
|
+
// resolvedConfig.base is e.g., "https://cdn.agentuity.com/{deploymentId}/client/" or "/"
|
|
136
|
+
const base = resolvedConfig?.base || '/';
|
|
137
|
+
// Normalize: ensure base ends with / and beaconFileName doesn't start with /
|
|
138
|
+
const normalizedBase = base.endsWith('/') ? base : `${base}/`;
|
|
139
|
+
const normalizedFileName = beaconFileName.startsWith('/')
|
|
140
|
+
? beaconFileName.slice(1)
|
|
141
|
+
: beaconFileName;
|
|
142
|
+
const beaconUrl = `${normalizedBase}${normalizedFileName}`;
|
|
143
|
+
|
|
144
|
+
// Inject a marker script tag
|
|
145
|
+
// The script must be sync (no async/defer) to patch history API before router loads
|
|
146
|
+
// Use data-agentuity-beacon attribute as a marker for config/session injection
|
|
147
|
+
const beaconScript = `<script data-agentuity-beacon src="${beaconUrl}"></script>`;
|
|
148
|
+
|
|
149
|
+
// Inject before </head>
|
|
150
|
+
if (html.includes('</head>')) {
|
|
151
|
+
return html.replace('</head>', `${beaconScript}</head>`);
|
|
152
|
+
}
|
|
153
|
+
// Fallback: inject at start of body
|
|
154
|
+
if (html.includes('<body')) {
|
|
155
|
+
return html.replace(/<body([^>]*)>/, `<body$1>${beaconScript}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return beaconScript + html;
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -10,6 +10,7 @@ import { createRequire } from 'node:module';
|
|
|
10
10
|
import type { InlineConfig, Plugin } from 'vite';
|
|
11
11
|
import type { Logger, DeployOptions } from '../../../types';
|
|
12
12
|
import { browserEnvPlugin } from './browser-env-plugin';
|
|
13
|
+
import { beaconPlugin } from './beacon-plugin';
|
|
13
14
|
import type { BuildReportCollector } from '../../../build-report';
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -55,6 +56,8 @@ export interface ViteBuildOptions {
|
|
|
55
56
|
deploymentId?: string;
|
|
56
57
|
workbenchRoute?: string;
|
|
57
58
|
workbenchEnabled?: boolean;
|
|
59
|
+
/** Whether analytics is enabled (for beacon injection in client build) */
|
|
60
|
+
analyticsEnabled?: boolean;
|
|
58
61
|
logger: Logger;
|
|
59
62
|
deploymentOptions?: DeployOptions;
|
|
60
63
|
/** Optional collector for structured error reporting */
|
|
@@ -139,11 +142,17 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
|
|
|
139
142
|
const htmlPath = join(rootDir, 'src', 'web', 'index.html');
|
|
140
143
|
|
|
141
144
|
// Use workbench config passed from runAllBuilds
|
|
142
|
-
const { workbenchEnabled = false, workbenchRoute = '/workbench' } = options;
|
|
145
|
+
const { workbenchEnabled = false, workbenchRoute = '/workbench', analyticsEnabled = false } = options;
|
|
143
146
|
|
|
144
147
|
// Load custom user plugins from agentuity.config.ts if it exists
|
|
145
148
|
const clientOutDir = join(rootDir, '.agentuity/client');
|
|
146
|
-
const plugins = [
|
|
149
|
+
const plugins = [
|
|
150
|
+
react(),
|
|
151
|
+
browserEnvPlugin(),
|
|
152
|
+
flattenHtmlOutputPlugin(clientOutDir),
|
|
153
|
+
// Emit analytics beacon as hashed CDN asset (prod builds only)
|
|
154
|
+
beaconPlugin({ enabled: analyticsEnabled && !dev }),
|
|
155
|
+
];
|
|
147
156
|
const { loadAgentuityConfig } = await import('./config-loader');
|
|
148
157
|
const userConfig = await loadAgentuityConfig(rootDir, logger);
|
|
149
158
|
const userPlugins = userConfig?.plugins || [];
|
|
@@ -312,6 +321,9 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
312
321
|
// Check if web frontend exists
|
|
313
322
|
const hasWebFrontend = await Bun.file(join(rootDir, 'src', 'web', 'index.html')).exists();
|
|
314
323
|
|
|
324
|
+
// Check if analytics is enabled
|
|
325
|
+
const analyticsEnabled = config?.analytics !== false;
|
|
326
|
+
|
|
315
327
|
// 2. Build client (only if web frontend exists)
|
|
316
328
|
if (hasWebFrontend) {
|
|
317
329
|
logger.debug('Building client assets...');
|
|
@@ -322,6 +334,7 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
322
334
|
mode: 'client',
|
|
323
335
|
workbenchEnabled: workbenchConfig.enabled,
|
|
324
336
|
workbenchRoute: workbenchConfig.route,
|
|
337
|
+
analyticsEnabled,
|
|
325
338
|
});
|
|
326
339
|
result.client.included = true;
|
|
327
340
|
result.client.duration = Date.now() - started;
|
|
@@ -114,6 +114,9 @@ import type { AnalyticsConfig } from './analytics-config';
|
|
|
114
114
|
// Inject analytics config and script into HTML
|
|
115
115
|
// Note: Only static config is injected (org, project, devmode, tracking options)
|
|
116
116
|
// Session and thread IDs are read from cookies by the beacon script
|
|
117
|
+
//
|
|
118
|
+
// In production: beacon is served from CDN as a hashed asset (injected by Vite build)
|
|
119
|
+
// In development: beacon is served from /_agentuity/webanalytics/analytics.js route
|
|
117
120
|
export function injectAnalytics(html: string, analyticsConfig: AnalyticsConfig): string {
|
|
118
121
|
if (!analyticsConfig.enabled) return html;
|
|
119
122
|
|
|
@@ -132,7 +135,17 @@ export function injectAnalytics(html: string, analyticsConfig: AnalyticsConfig):
|
|
|
132
135
|
const configScript = \`<script>window.__AGENTUITY_ANALYTICS__=\${JSON.stringify(pageConfig)};</script>\`;
|
|
133
136
|
// Session script sets cookies and window.__AGENTUITY_SESSION__ (dynamic, not cached)
|
|
134
137
|
const sessionScript = '<script src="/_agentuity/webanalytics/session.js" async></script>';
|
|
135
|
-
|
|
138
|
+
|
|
139
|
+
// In production, the beacon is already in HTML as a CDN asset (data-agentuity-beacon marker)
|
|
140
|
+
// Inject config/session BEFORE the beacon marker so config exists when beacon runs
|
|
141
|
+
const beaconMarker = '<script data-agentuity-beacon';
|
|
142
|
+
if (html.includes(beaconMarker)) {
|
|
143
|
+
// Production: inject config/session right before the beacon script
|
|
144
|
+
const injection = configScript + sessionScript;
|
|
145
|
+
return html.replace(beaconMarker, injection + beaconMarker);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Development: beacon served from local route, inject all three scripts
|
|
136
149
|
const beaconScript = '<script src="/_agentuity/webanalytics/analytics.js"></script>';
|
|
137
150
|
const injection = configScript + sessionScript + beaconScript;
|
|
138
151
|
|
|
@@ -167,15 +180,18 @@ export function registerAnalyticsRoutes(app: ReturnType<typeof createRouter>): v
|
|
|
167
180
|
});
|
|
168
181
|
});
|
|
169
182
|
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
183
|
+
// Dev mode only: serve beacon script from local route
|
|
184
|
+
// In production, the beacon is served from CDN as a hashed asset
|
|
185
|
+
if (runtimeIsDevMode()) {
|
|
186
|
+
app.get('/_agentuity/webanalytics/analytics.js', async (c: Context) => {
|
|
187
|
+
return new Response(BEACON_SCRIPT, {
|
|
188
|
+
headers: {
|
|
189
|
+
'Content-Type': 'application/javascript; charset=utf-8',
|
|
190
|
+
'Cache-Control': 'no-store, no-cache, must-revalidate',
|
|
191
|
+
},
|
|
192
|
+
});
|
|
177
193
|
});
|
|
178
|
-
}
|
|
194
|
+
}
|
|
179
195
|
}
|
|
180
196
|
`;
|
|
181
197
|
}
|
package/src/cmd/cloud/db/get.ts
CHANGED
|
@@ -12,6 +12,8 @@ const DBGetResponseSchema = z
|
|
|
12
12
|
name: z.string().describe('Database name'),
|
|
13
13
|
description: z.string().optional().describe('Database description'),
|
|
14
14
|
url: z.string().optional().describe('Database connection URL'),
|
|
15
|
+
org_id: z.string().optional().describe('Organization ID that owns this database'),
|
|
16
|
+
org_name: z.string().optional().describe('Organization name that owns this database'),
|
|
15
17
|
})
|
|
16
18
|
.or(
|
|
17
19
|
z.object({
|
|
@@ -175,6 +177,9 @@ export const getSubcommand = createSubcommand({
|
|
|
175
177
|
const tableData: Record<string, string> = {
|
|
176
178
|
Name: tui.bold(db.name),
|
|
177
179
|
};
|
|
180
|
+
if (db.org_name || db.org_id) {
|
|
181
|
+
tableData['Organization'] = db.org_name || db.org_id;
|
|
182
|
+
}
|
|
178
183
|
if (db.description) {
|
|
179
184
|
tableData['Description'] = db.description;
|
|
180
185
|
}
|
|
@@ -192,6 +197,8 @@ export const getSubcommand = createSubcommand({
|
|
|
192
197
|
name: db.name,
|
|
193
198
|
description: db.description ?? undefined,
|
|
194
199
|
url: db.url ?? undefined,
|
|
200
|
+
org_id: db.org_id,
|
|
201
|
+
org_name: db.org_name,
|
|
195
202
|
};
|
|
196
203
|
},
|
|
197
204
|
});
|