@agentuity/cli 0.0.110 → 0.0.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +19 -4
- package/dist/cli.js.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts +1 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.js +3 -3
- package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
- package/dist/cmd/build/vite/index.js +1 -1
- package/dist/cmd/build/vite/index.js.map +1 -1
- package/dist/cmd/build/vite/metadata-generator.js +1 -1
- package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
- package/dist/cmd/build/vite/registry-generator.d.ts +1 -1
- package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/registry-generator.js +70 -23
- package/dist/cmd/build/vite/registry-generator.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts +6 -0
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js +19 -0
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +1 -1
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +63 -1
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/create.js +18 -0
- package/dist/cmd/cloud/sandbox/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.js +2 -6
- package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
- package/dist/cmd/cloud/sandbox/download.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/download.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/download.js +89 -0
- package/dist/cmd/cloud/sandbox/download.js.map +1 -0
- package/dist/cmd/cloud/sandbox/env.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/env.js +90 -0
- package/dist/cmd/cloud/sandbox/env.js.map +1 -0
- package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/get.js +5 -0
- package/dist/cmd/cloud/sandbox/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/index.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/index.js +14 -0
- package/dist/cmd/cloud/sandbox/index.js.map +1 -1
- package/dist/cmd/cloud/sandbox/ls.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/ls.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/ls.js +119 -0
- package/dist/cmd/cloud/sandbox/ls.js.map +1 -0
- package/dist/cmd/cloud/sandbox/mkdir.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/mkdir.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/mkdir.js +59 -0
- package/dist/cmd/cloud/sandbox/mkdir.js.map +1 -0
- package/dist/cmd/cloud/sandbox/rm.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/rm.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/rm.js +45 -0
- package/dist/cmd/cloud/sandbox/rm.js.map +1 -0
- package/dist/cmd/cloud/sandbox/rmdir.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/rmdir.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/rmdir.js +59 -0
- package/dist/cmd/cloud/sandbox/rmdir.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/create.js +0 -2
- package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/get.js +0 -2
- package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/list.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/list.js +0 -3
- package/dist/cmd/cloud/sandbox/snapshot/list.js.map +1 -1
- package/dist/cmd/cloud/sandbox/upload.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/upload.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/upload.js +77 -0
- package/dist/cmd/cloud/sandbox/upload.js.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +17 -8
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/dev/sync.d.ts.map +1 -1
- package/dist/cmd/dev/sync.js +8 -14
- package/dist/cmd/dev/sync.js.map +1 -1
- package/dist/cmd/git/account/add.d.ts +17 -0
- package/dist/cmd/git/account/add.d.ts.map +1 -0
- package/dist/cmd/git/account/add.js +244 -0
- package/dist/cmd/git/account/add.js.map +1 -0
- package/dist/cmd/git/account/index.d.ts +3 -0
- package/dist/cmd/git/account/index.d.ts.map +1 -0
- package/dist/cmd/git/account/index.js +11 -0
- package/dist/cmd/git/account/index.js.map +1 -0
- package/dist/cmd/git/account/list.d.ts +2 -0
- package/dist/cmd/git/account/list.d.ts.map +1 -0
- package/dist/cmd/git/account/list.js +111 -0
- package/dist/cmd/git/account/list.js.map +1 -0
- package/dist/cmd/git/account/remove.d.ts +2 -0
- package/dist/cmd/git/account/remove.d.ts.map +1 -0
- package/dist/cmd/git/account/remove.js +171 -0
- package/dist/cmd/git/account/remove.js.map +1 -0
- package/dist/cmd/git/index.d.ts +3 -0
- package/dist/cmd/git/index.d.ts.map +1 -0
- package/dist/cmd/git/index.js +19 -0
- package/dist/cmd/git/index.js.map +1 -0
- package/dist/cmd/git/link.d.ts +32 -0
- package/dist/cmd/git/link.d.ts.map +1 -0
- package/dist/cmd/git/link.js +357 -0
- package/dist/cmd/git/link.js.map +1 -0
- package/dist/cmd/git/list.d.ts +2 -0
- package/dist/cmd/git/list.d.ts.map +1 -0
- package/dist/cmd/git/list.js +137 -0
- package/dist/cmd/git/list.js.map +1 -0
- package/dist/cmd/git/status.d.ts +2 -0
- package/dist/cmd/git/status.d.ts.map +1 -0
- package/dist/cmd/git/status.js +119 -0
- package/dist/cmd/git/status.js.map +1 -0
- package/dist/cmd/git/unlink.d.ts +2 -0
- package/dist/cmd/git/unlink.d.ts.map +1 -0
- package/dist/cmd/git/unlink.js +98 -0
- package/dist/cmd/git/unlink.js.map +1 -0
- package/dist/cmd/index.d.ts.map +1 -1
- package/dist/cmd/index.js +2 -0
- package/dist/cmd/index.js.map +1 -1
- package/dist/cmd/integration/api.d.ts +61 -0
- package/dist/cmd/integration/api.d.ts.map +1 -0
- package/dist/cmd/integration/api.js +176 -0
- package/dist/cmd/integration/api.js.map +1 -0
- package/dist/cmd/integration/github/connect.d.ts +2 -0
- package/dist/cmd/integration/github/connect.d.ts.map +1 -0
- package/dist/cmd/integration/github/connect.js +197 -0
- package/dist/cmd/integration/github/connect.js.map +1 -0
- package/dist/cmd/integration/github/disconnect.d.ts +2 -0
- package/dist/cmd/integration/github/disconnect.d.ts.map +1 -0
- package/dist/cmd/integration/github/disconnect.js +121 -0
- package/dist/cmd/integration/github/disconnect.js.map +1 -0
- package/dist/cmd/integration/github/index.d.ts +2 -0
- package/dist/cmd/integration/github/index.d.ts.map +1 -0
- package/dist/cmd/integration/github/index.js +21 -0
- package/dist/cmd/integration/github/index.js.map +1 -0
- package/dist/cmd/integration/index.d.ts +2 -0
- package/dist/cmd/integration/index.d.ts.map +1 -0
- package/dist/cmd/integration/index.js +16 -0
- package/dist/cmd/integration/index.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +24 -0
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +5 -0
- package/dist/errors.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/package.json +6 -6
- package/src/cli.ts +20 -4
- package/src/cmd/build/vite/agent-discovery.ts +4 -4
- package/src/cmd/build/vite/index.ts +1 -1
- package/src/cmd/build/vite/metadata-generator.ts +1 -1
- package/src/cmd/build/vite/registry-generator.ts +78 -24
- package/src/cmd/build/vite/route-discovery.ts +20 -0
- package/src/cmd/build/vite/vite-builder.ts +1 -1
- package/src/cmd/cloud/deploy.ts +78 -1
- package/src/cmd/cloud/sandbox/create.ts +22 -0
- package/src/cmd/cloud/sandbox/delete.ts +2 -6
- package/src/cmd/cloud/sandbox/download.ts +96 -0
- package/src/cmd/cloud/sandbox/env.ts +104 -0
- package/src/cmd/cloud/sandbox/get.ts +5 -0
- package/src/cmd/cloud/sandbox/index.ts +14 -0
- package/src/cmd/cloud/sandbox/ls.ts +126 -0
- package/src/cmd/cloud/sandbox/mkdir.ts +65 -0
- package/src/cmd/cloud/sandbox/rm.ts +51 -0
- package/src/cmd/cloud/sandbox/rmdir.ts +65 -0
- package/src/cmd/cloud/sandbox/snapshot/create.ts +0 -2
- package/src/cmd/cloud/sandbox/snapshot/get.ts +0 -2
- package/src/cmd/cloud/sandbox/snapshot/list.ts +0 -3
- package/src/cmd/cloud/sandbox/upload.ts +83 -0
- package/src/cmd/dev/index.ts +32 -19
- package/src/cmd/dev/sync.ts +26 -30
- package/src/cmd/git/account/add.ts +317 -0
- package/src/cmd/git/account/index.ts +12 -0
- package/src/cmd/git/account/list.ts +139 -0
- package/src/cmd/git/account/remove.ts +212 -0
- package/src/cmd/git/index.ts +20 -0
- package/src/cmd/git/link.ts +468 -0
- package/src/cmd/git/list.ts +161 -0
- package/src/cmd/git/status.ts +144 -0
- package/src/cmd/git/unlink.ts +117 -0
- package/src/cmd/index.ts +2 -0
- package/src/cmd/integration/api.ts +379 -0
- package/src/cmd/integration/github/connect.ts +242 -0
- package/src/cmd/integration/github/disconnect.ts +149 -0
- package/src/cmd/integration/github/index.ts +21 -0
- package/src/cmd/integration/index.ts +16 -0
- package/src/config.ts +34 -0
- package/src/errors.ts +7 -0
- package/src/types.ts +4 -0
package/src/cmd/dev/index.ts
CHANGED
|
@@ -680,7 +680,34 @@ export const command = createCommand({
|
|
|
680
680
|
);
|
|
681
681
|
}
|
|
682
682
|
|
|
683
|
-
// Step 2:
|
|
683
|
+
// Step 2: Discover agents and routes for registry generation
|
|
684
|
+
const srcDir = join(rootDir, 'src');
|
|
685
|
+
const { discoverAgents } = await import('../build/vite/agent-discovery');
|
|
686
|
+
const { discoverRoutes } = await import('../build/vite/route-discovery');
|
|
687
|
+
const { generateAgentRegistry, generateRouteRegistry } = await import(
|
|
688
|
+
'../build/vite/registry-generator'
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
const agentMetadata = await discoverAgents(
|
|
692
|
+
srcDir,
|
|
693
|
+
project?.projectId ?? '',
|
|
694
|
+
deploymentId,
|
|
695
|
+
logger
|
|
696
|
+
);
|
|
697
|
+
const { routes, routeInfoList } = await discoverRoutes(
|
|
698
|
+
srcDir,
|
|
699
|
+
project?.projectId ?? '',
|
|
700
|
+
deploymentId,
|
|
701
|
+
logger
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
// Generate agent and route registries for type augmentation
|
|
705
|
+
// (TypeScript needs these files to exist for proper type inference)
|
|
706
|
+
generateAgentRegistry(srcDir, agentMetadata);
|
|
707
|
+
generateRouteRegistry(srcDir, routeInfoList);
|
|
708
|
+
logger.debug('Agent and route registries generated for dev mode');
|
|
709
|
+
|
|
710
|
+
// Step 3: Generate entry file with workbench config
|
|
684
711
|
// Note: vitePort is NOT passed here - the app reads process.env.VITE_PORT at runtime
|
|
685
712
|
const { generateEntryFile } = await import('../build/entry-generator');
|
|
686
713
|
await generateEntryFile({
|
|
@@ -692,7 +719,7 @@ export const command = createCommand({
|
|
|
692
719
|
workbench: workbenchConfigData.enabled ? workbenchConfigData : undefined,
|
|
693
720
|
});
|
|
694
721
|
|
|
695
|
-
// Step
|
|
722
|
+
// Step 4: Bundle the app with LLM patches (dev mode = no minification)
|
|
696
723
|
// This produces .agentuity/app.js with AI Gateway routing patches applied
|
|
697
724
|
const { installExternalsAndBuild } = await import(
|
|
698
725
|
'../build/vite/server-bundler'
|
|
@@ -704,14 +731,11 @@ export const command = createCommand({
|
|
|
704
731
|
});
|
|
705
732
|
|
|
706
733
|
// Generate metadata file (needed for eval ID lookup at runtime)
|
|
707
|
-
|
|
708
|
-
const { discoverRoutes } = await import('../build/vite/route-discovery');
|
|
734
|
+
// Reuse agentMetadata and routes from Step 2
|
|
709
735
|
const { generateMetadata, writeMetadataFile } = await import(
|
|
710
736
|
'../build/vite/metadata-generator'
|
|
711
737
|
);
|
|
712
738
|
|
|
713
|
-
const srcDir = join(rootDir, 'src');
|
|
714
|
-
|
|
715
739
|
const promises: Promise<void>[] = [];
|
|
716
740
|
|
|
717
741
|
// Generate/update prompt files (non-blocking)
|
|
@@ -722,25 +746,13 @@ export const command = createCommand({
|
|
|
722
746
|
logger.warn('Failed to generate prompt files: %s', err.message)
|
|
723
747
|
)
|
|
724
748
|
);
|
|
725
|
-
const agents = await discoverAgents(
|
|
726
|
-
srcDir,
|
|
727
|
-
project?.projectId ?? '',
|
|
728
|
-
deploymentId,
|
|
729
|
-
logger
|
|
730
|
-
);
|
|
731
|
-
const { routes } = await discoverRoutes(
|
|
732
|
-
srcDir,
|
|
733
|
-
project?.projectId ?? '',
|
|
734
|
-
deploymentId,
|
|
735
|
-
logger
|
|
736
|
-
);
|
|
737
749
|
|
|
738
750
|
const metadata = await generateMetadata({
|
|
739
751
|
rootDir,
|
|
740
752
|
projectId: project?.projectId ?? '',
|
|
741
753
|
orgId: project?.orgId ?? '',
|
|
742
754
|
deploymentId,
|
|
743
|
-
agents,
|
|
755
|
+
agents: agentMetadata,
|
|
744
756
|
routes,
|
|
745
757
|
dev: true,
|
|
746
758
|
logger,
|
|
@@ -844,6 +856,7 @@ export const command = createCommand({
|
|
|
844
856
|
process.env.AGENTUITY_STREAM_URL = serviceUrls.stream;
|
|
845
857
|
process.env.AGENTUITY_CLOUD_ORG_ID = project.orgId;
|
|
846
858
|
process.env.AGENTUITY_CLOUD_PROJECT_ID = project.projectId;
|
|
859
|
+
process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID = deploymentId;
|
|
847
860
|
}
|
|
848
861
|
|
|
849
862
|
// Set Vite port for asset proxying in bundled app
|
package/src/cmd/dev/sync.ts
CHANGED
|
@@ -15,7 +15,7 @@ interface AgentSyncPayload {
|
|
|
15
15
|
interface EvalSyncPayload {
|
|
16
16
|
id: string;
|
|
17
17
|
name: string;
|
|
18
|
-
|
|
18
|
+
identifier: string;
|
|
19
19
|
description?: string;
|
|
20
20
|
version: string;
|
|
21
21
|
filename: string;
|
|
@@ -100,7 +100,7 @@ function getEvalsToSync(
|
|
|
100
100
|
|
|
101
101
|
evalsToCreate.push({
|
|
102
102
|
...evalItem,
|
|
103
|
-
|
|
103
|
+
identifier: evalItem.identifier,
|
|
104
104
|
projectId,
|
|
105
105
|
agentIdentifier: agent.agentId,
|
|
106
106
|
});
|
|
@@ -133,18 +133,28 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
133
133
|
projectId: string,
|
|
134
134
|
deploymentId: string
|
|
135
135
|
): Promise<void> {
|
|
136
|
+
this.logger.debug(
|
|
137
|
+
'[CLI SYNC] sync() called with projectId=%s, deploymentId=%s',
|
|
138
|
+
projectId,
|
|
139
|
+
deploymentId
|
|
140
|
+
);
|
|
141
|
+
this.logger.debug(
|
|
142
|
+
'[CLI SYNC] currentMetadata has %d agents',
|
|
143
|
+
currentMetadata.agents?.length ?? 0
|
|
144
|
+
);
|
|
145
|
+
|
|
136
146
|
// Build previous agent IDs set
|
|
137
147
|
const previousAgentIds = new Set<string>();
|
|
138
148
|
if (previousMetadata) {
|
|
139
149
|
this.logger.debug(
|
|
140
|
-
'Previous metadata found with %d agent(s)',
|
|
150
|
+
'[CLI SYNC] Previous metadata found with %d agent(s)',
|
|
141
151
|
previousMetadata.agents?.length ?? 0
|
|
142
152
|
);
|
|
143
153
|
for (const agent of previousMetadata.agents || []) {
|
|
144
154
|
previousAgentIds.add(agent.id);
|
|
145
155
|
}
|
|
146
156
|
} else {
|
|
147
|
-
this.logger.debug('No previous metadata, all agents will be treated as new');
|
|
157
|
+
this.logger.debug('[CLI SYNC] No previous metadata, all agents will be treated as new');
|
|
148
158
|
}
|
|
149
159
|
|
|
150
160
|
// Build previous eval IDs set
|
|
@@ -178,9 +188,10 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
178
188
|
);
|
|
179
189
|
for (const evalItem of agent.evals) {
|
|
180
190
|
this.logger.debug(
|
|
181
|
-
'[CLI EVAL SYNC] - %s (
|
|
191
|
+
'[CLI EVAL SYNC] - %s (id: %s, identifier: %s)',
|
|
182
192
|
evalItem.name,
|
|
183
|
-
evalItem.
|
|
193
|
+
evalItem.id,
|
|
194
|
+
evalItem.identifier
|
|
184
195
|
);
|
|
185
196
|
}
|
|
186
197
|
}
|
|
@@ -208,13 +219,11 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
208
219
|
agentsToDelete.length
|
|
209
220
|
);
|
|
210
221
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
);
|
|
217
|
-
}
|
|
222
|
+
this.logger.debug(
|
|
223
|
+
'[CLI EVAL SYNC] Evals to sync: %d to create, %d to delete',
|
|
224
|
+
evalsToCreate.length,
|
|
225
|
+
evalsToDelete.length
|
|
226
|
+
);
|
|
218
227
|
|
|
219
228
|
// Sync both in parallel
|
|
220
229
|
try {
|
|
@@ -273,14 +282,7 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
273
282
|
evalsToDelete: string[],
|
|
274
283
|
deploymentId: string
|
|
275
284
|
): Promise<void> {
|
|
276
|
-
this.logger.debug(
|
|
277
|
-
'[CLI EVAL SYNC] syncEvals called: %d to create, %d to delete',
|
|
278
|
-
evals.length,
|
|
279
|
-
evalsToDelete.length
|
|
280
|
-
);
|
|
281
|
-
|
|
282
285
|
if (evals.length === 0 && evalsToDelete.length === 0) {
|
|
283
|
-
this.logger.debug('[CLI EVAL SYNC] No evals to sync, skipping');
|
|
284
286
|
return;
|
|
285
287
|
}
|
|
286
288
|
|
|
@@ -290,15 +292,10 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
290
292
|
delete: evalsToDelete,
|
|
291
293
|
};
|
|
292
294
|
|
|
293
|
-
this.logger.debug(
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
evalItem.name,
|
|
298
|
-
evalItem.id,
|
|
299
|
-
evalItem.evalId
|
|
300
|
-
);
|
|
301
|
-
}
|
|
295
|
+
this.logger.debug(
|
|
296
|
+
'[CLI EVAL SYNC] Sending payload to POST /cli/devmode/eval: %s',
|
|
297
|
+
JSON.stringify(payload, null, 2)
|
|
298
|
+
);
|
|
302
299
|
|
|
303
300
|
try {
|
|
304
301
|
await this.apiClient.post(
|
|
@@ -306,7 +303,6 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
306
303
|
payload,
|
|
307
304
|
z.object({ success: z.boolean() })
|
|
308
305
|
);
|
|
309
|
-
this.logger.debug('[CLI EVAL SYNC] Sync successful');
|
|
310
306
|
} catch (error) {
|
|
311
307
|
this.logger.error('[CLI EVAL SYNC] Sync failed: %s', error);
|
|
312
308
|
throw error;
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { createSubcommand } from '../../../types';
|
|
2
|
+
import type { Config } from '../../../types';
|
|
3
|
+
import * as tui from '../../../tui';
|
|
4
|
+
import { getCommand } from '../../../command-prefix';
|
|
5
|
+
import { getAPIBaseURL } from '../../../api';
|
|
6
|
+
import type { APIClient } from '../../../api';
|
|
7
|
+
import { ErrorCode } from '../../../errors';
|
|
8
|
+
import { listOrganizations } from '@agentuity/server';
|
|
9
|
+
import enquirer from 'enquirer';
|
|
10
|
+
import type { Logger } from '@agentuity/core';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import {
|
|
13
|
+
startGithubIntegration,
|
|
14
|
+
pollForGithubIntegration,
|
|
15
|
+
getGithubIntegrationStatus,
|
|
16
|
+
getExistingGithubIntegrations,
|
|
17
|
+
copyGithubIntegration,
|
|
18
|
+
} from '../../integration/api';
|
|
19
|
+
|
|
20
|
+
export interface RunGitAccountConnectOptions {
|
|
21
|
+
apiClient: APIClient;
|
|
22
|
+
orgId: string;
|
|
23
|
+
orgName?: string;
|
|
24
|
+
logger: Logger;
|
|
25
|
+
config?: Config | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RunGitAccountConnectResult {
|
|
29
|
+
connected: boolean;
|
|
30
|
+
cancelled?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function runGitAccountConnect(
|
|
34
|
+
options: RunGitAccountConnectOptions
|
|
35
|
+
): Promise<RunGitAccountConnectResult> {
|
|
36
|
+
const { apiClient, orgId, orgName, logger, config } = options;
|
|
37
|
+
const orgDisplay = orgName ?? orgId;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const currentStatus = await getGithubIntegrationStatus(apiClient, orgId);
|
|
41
|
+
const initialCount = currentStatus.integrations?.length ?? 0;
|
|
42
|
+
|
|
43
|
+
const existingIntegrations = await tui.spinner({
|
|
44
|
+
message: 'Checking for existing GitHub connections...',
|
|
45
|
+
clearOnSuccess: true,
|
|
46
|
+
callback: () => getExistingGithubIntegrations(apiClient, orgId),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const alreadyConnectedNames = new Set(
|
|
50
|
+
currentStatus.integrations?.map((i) => i.githubAccountName) ?? []
|
|
51
|
+
);
|
|
52
|
+
const availableIntegrations = existingIntegrations.filter(
|
|
53
|
+
(i) => !alreadyConnectedNames.has(i.githubAccountName)
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (availableIntegrations.length > 0) {
|
|
57
|
+
tui.newline();
|
|
58
|
+
|
|
59
|
+
const integrationChoices = availableIntegrations.map((i) => ({
|
|
60
|
+
name: i.id,
|
|
61
|
+
message: `${i.githubAccountName} ${tui.muted(`(from ${i.orgName})`)}`,
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
console.log(tui.muted('Press enter with none selected to add a new account'));
|
|
65
|
+
tui.newline();
|
|
66
|
+
|
|
67
|
+
const selectResponse = await enquirer.prompt<{ integrationIds: string[] }>({
|
|
68
|
+
type: 'multiselect',
|
|
69
|
+
name: 'integrationIds',
|
|
70
|
+
message: 'Select GitHub accounts to add',
|
|
71
|
+
choices: integrationChoices,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (selectResponse.integrationIds.length > 0) {
|
|
75
|
+
const selectedIntegrations = availableIntegrations.filter((i) =>
|
|
76
|
+
selectResponse.integrationIds.includes(i.id)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const accountNames = selectedIntegrations.map((i) => i.githubAccountName).join(', ');
|
|
80
|
+
|
|
81
|
+
const confirmResponse = await enquirer.prompt<{ confirm: boolean }>({
|
|
82
|
+
type: 'confirm',
|
|
83
|
+
name: 'confirm',
|
|
84
|
+
message: `Add ${tui.bold(accountNames)} to ${tui.bold(orgDisplay)}?`,
|
|
85
|
+
initial: true,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (confirmResponse.confirm) {
|
|
89
|
+
await tui.spinner({
|
|
90
|
+
message: `Adding ${selectedIntegrations.length} GitHub account${selectedIntegrations.length > 1 ? 's' : ''}...`,
|
|
91
|
+
clearOnSuccess: true,
|
|
92
|
+
callback: async () => {
|
|
93
|
+
for (const integration of selectedIntegrations) {
|
|
94
|
+
await copyGithubIntegration(apiClient, integration.orgId, orgId);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
tui.newline();
|
|
100
|
+
tui.success(
|
|
101
|
+
`Added GitHub account${selectedIntegrations.length > 1 ? 's' : ''} to ${tui.bold(orgDisplay)}`
|
|
102
|
+
);
|
|
103
|
+
return { connected: true };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const startResult = await tui.spinner({
|
|
109
|
+
message: 'Getting GitHub authorization URL...',
|
|
110
|
+
clearOnSuccess: true,
|
|
111
|
+
callback: () => startGithubIntegration(apiClient, orgId),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (!startResult) {
|
|
115
|
+
tui.error('Failed to start GitHub authorization');
|
|
116
|
+
return { connected: false };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const { shortId } = startResult;
|
|
120
|
+
const apiBaseUrl = getAPIBaseURL(config);
|
|
121
|
+
const url = `${apiBaseUrl}/github/connect/${shortId}`;
|
|
122
|
+
|
|
123
|
+
const copied = await tui.copyToClipboard(url);
|
|
124
|
+
|
|
125
|
+
tui.newline();
|
|
126
|
+
if (copied) {
|
|
127
|
+
console.log('GitHub authorization URL copied to clipboard! Open it in your browser:');
|
|
128
|
+
} else {
|
|
129
|
+
console.log('Open this URL in your browser to authorize GitHub access:');
|
|
130
|
+
}
|
|
131
|
+
tui.newline();
|
|
132
|
+
console.log(` ${tui.link(url)}`);
|
|
133
|
+
tui.newline();
|
|
134
|
+
console.log(tui.muted('Press Enter to open in your browser, or Ctrl+C to cancel'));
|
|
135
|
+
tui.newline();
|
|
136
|
+
|
|
137
|
+
const result = await tui.spinner({
|
|
138
|
+
type: 'countdown',
|
|
139
|
+
message: 'Waiting for GitHub authorization',
|
|
140
|
+
timeoutMs: 600000,
|
|
141
|
+
clearOnSuccess: true,
|
|
142
|
+
onEnterPress: () => {
|
|
143
|
+
const platform = process.platform;
|
|
144
|
+
if (platform === 'win32') {
|
|
145
|
+
Bun.spawn(['cmd', '/c', 'start', '', url], {
|
|
146
|
+
stdout: 'ignore',
|
|
147
|
+
stderr: 'ignore',
|
|
148
|
+
});
|
|
149
|
+
} else {
|
|
150
|
+
const command = platform === 'darwin' ? 'open' : 'xdg-open';
|
|
151
|
+
Bun.spawn([command, url], { stdout: 'ignore', stderr: 'ignore' });
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
callback: async () => {
|
|
155
|
+
return await pollForGithubIntegration(apiClient, orgId, initialCount);
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
tui.newline();
|
|
160
|
+
if (result.connected) {
|
|
161
|
+
tui.success(`GitHub account added to ${tui.bold(orgDisplay)}`);
|
|
162
|
+
return { connected: true };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { connected: false };
|
|
166
|
+
} catch (error) {
|
|
167
|
+
const isCancel =
|
|
168
|
+
error === '' ||
|
|
169
|
+
(error instanceof Error && (error.message === '' || error.message === 'User cancelled'));
|
|
170
|
+
|
|
171
|
+
if (isCancel) {
|
|
172
|
+
tui.newline();
|
|
173
|
+
tui.info('Cancelled');
|
|
174
|
+
return { connected: false, cancelled: true };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
logger.trace(error);
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const AddOptionsSchema = z.object({
|
|
183
|
+
org: z.string().optional().describe('Organization ID to add the account to'),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const AddResponseSchema = z.object({
|
|
187
|
+
connected: z.boolean().describe('Whether the account was connected'),
|
|
188
|
+
orgId: z.string().optional().describe('Organization ID'),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
export const addSubcommand = createSubcommand({
|
|
192
|
+
name: 'add',
|
|
193
|
+
description: 'Add a GitHub account to your organization',
|
|
194
|
+
tags: ['mutating', 'creates-resource', 'slow', 'api-intensive'],
|
|
195
|
+
idempotent: false,
|
|
196
|
+
requires: { auth: true, apiClient: true },
|
|
197
|
+
schema: {
|
|
198
|
+
options: AddOptionsSchema,
|
|
199
|
+
response: AddResponseSchema,
|
|
200
|
+
},
|
|
201
|
+
examples: [
|
|
202
|
+
{
|
|
203
|
+
command: getCommand('git account add'),
|
|
204
|
+
description: 'Add a GitHub account to your organization',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
command: getCommand('git account add --org org_abc123'),
|
|
208
|
+
description: 'Add to a specific organization',
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
|
|
212
|
+
async handler(ctx) {
|
|
213
|
+
const { logger, apiClient, config, opts } = ctx;
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
const orgs = await tui.spinner({
|
|
217
|
+
message: 'Fetching organizations...',
|
|
218
|
+
clearOnSuccess: true,
|
|
219
|
+
callback: () => listOrganizations(apiClient),
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (orgs.length === 0) {
|
|
223
|
+
tui.fatal('No organizations found for your account');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let orgId = opts.org;
|
|
227
|
+
let selectedOrg: (typeof orgs)[0] | undefined;
|
|
228
|
+
|
|
229
|
+
if (orgId) {
|
|
230
|
+
selectedOrg = orgs.find((o) => o.id === orgId);
|
|
231
|
+
if (!selectedOrg) {
|
|
232
|
+
tui.fatal(`Organization ${orgId} not found`);
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
const orgStatuses = await tui.spinner({
|
|
236
|
+
message: 'Checking GitHub integration status...',
|
|
237
|
+
clearOnSuccess: true,
|
|
238
|
+
callback: async () => {
|
|
239
|
+
const statuses = await Promise.all(
|
|
240
|
+
orgs.map(async (org) => {
|
|
241
|
+
const status = await getGithubIntegrationStatus(apiClient, org.id);
|
|
242
|
+
return {
|
|
243
|
+
...org,
|
|
244
|
+
connected: status.connected,
|
|
245
|
+
integrations: status.integrations,
|
|
246
|
+
};
|
|
247
|
+
})
|
|
248
|
+
);
|
|
249
|
+
return statuses;
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const sortedOrgs = [...orgStatuses].sort((a, b) => a.name.localeCompare(b.name));
|
|
254
|
+
|
|
255
|
+
if (orgs.length === 1) {
|
|
256
|
+
orgId = orgs[0].id;
|
|
257
|
+
selectedOrg = orgs[0];
|
|
258
|
+
} else {
|
|
259
|
+
const choices = sortedOrgs.map((org) => {
|
|
260
|
+
const count = org.integrations.length;
|
|
261
|
+
const suffix =
|
|
262
|
+
count > 0
|
|
263
|
+
? tui.muted(` (${count} GitHub account${count > 1 ? 's' : ''})`)
|
|
264
|
+
: '';
|
|
265
|
+
return {
|
|
266
|
+
name: org.name,
|
|
267
|
+
message: `${org.name}${suffix}`,
|
|
268
|
+
value: org.id,
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const response = await enquirer.prompt<{ orgName: string }>({
|
|
273
|
+
type: 'select',
|
|
274
|
+
name: 'orgName',
|
|
275
|
+
message: 'Select an organization',
|
|
276
|
+
choices,
|
|
277
|
+
result(name: string) {
|
|
278
|
+
// @ts-expect-error - this.map exists at runtime
|
|
279
|
+
return this.map(name)[name];
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
orgId = response.orgName;
|
|
284
|
+
selectedOrg = sortedOrgs.find((o) => o.id === orgId);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const result = await runGitAccountConnect({
|
|
289
|
+
apiClient,
|
|
290
|
+
orgId: orgId!,
|
|
291
|
+
orgName: selectedOrg?.name,
|
|
292
|
+
logger,
|
|
293
|
+
config,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return { connected: result.connected, orgId };
|
|
297
|
+
} catch (error) {
|
|
298
|
+
const isCancel =
|
|
299
|
+
error === '' ||
|
|
300
|
+
(error instanceof Error &&
|
|
301
|
+
(error.message === '' || error.message === 'User cancelled'));
|
|
302
|
+
|
|
303
|
+
if (isCancel) {
|
|
304
|
+
tui.newline();
|
|
305
|
+
tui.info('Cancelled');
|
|
306
|
+
return { connected: false };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
logger.trace(error);
|
|
310
|
+
return logger.fatal(
|
|
311
|
+
'Failed to add GitHub account: %s',
|
|
312
|
+
error,
|
|
313
|
+
ErrorCode.INTEGRATION_FAILED
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createCommand } from '../../../types';
|
|
2
|
+
import { addSubcommand } from './add';
|
|
3
|
+
import { removeSubcommand } from './remove';
|
|
4
|
+
import { listSubcommand } from './list';
|
|
5
|
+
|
|
6
|
+
export const accountCommand = createCommand({
|
|
7
|
+
name: 'account',
|
|
8
|
+
description: 'Manage GitHub accounts connected to your organization',
|
|
9
|
+
subcommands: [addSubcommand, removeSubcommand, listSubcommand],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export default accountCommand;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { createSubcommand } from '../../../types';
|
|
2
|
+
import * as tui from '../../../tui';
|
|
3
|
+
import { getCommand } from '../../../command-prefix';
|
|
4
|
+
import { ErrorCode } from '../../../errors';
|
|
5
|
+
import { listOrganizations } from '@agentuity/server';
|
|
6
|
+
import { getGithubIntegrationStatus } from '../../integration/api';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
const ListResponseSchema = z.array(
|
|
10
|
+
z.object({
|
|
11
|
+
orgId: z.string().describe('Organization ID'),
|
|
12
|
+
orgName: z.string().describe('Organization name'),
|
|
13
|
+
integrations: z.array(
|
|
14
|
+
z.object({
|
|
15
|
+
id: z.string().describe('Integration ID'),
|
|
16
|
+
githubAccountName: z.string().describe('GitHub account name'),
|
|
17
|
+
githubAccountType: z.enum(['user', 'org']).describe('Account type'),
|
|
18
|
+
})
|
|
19
|
+
),
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export const listSubcommand = createSubcommand({
|
|
24
|
+
name: 'list',
|
|
25
|
+
description: 'List GitHub accounts connected to your organizations',
|
|
26
|
+
aliases: ['ls'],
|
|
27
|
+
tags: ['read-only'],
|
|
28
|
+
idempotent: true,
|
|
29
|
+
requires: { auth: true, apiClient: true },
|
|
30
|
+
schema: {
|
|
31
|
+
response: ListResponseSchema,
|
|
32
|
+
},
|
|
33
|
+
examples: [
|
|
34
|
+
{
|
|
35
|
+
command: getCommand('git account list'),
|
|
36
|
+
description: 'List all connected GitHub accounts',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
command: getCommand('--json git account list'),
|
|
40
|
+
description: 'List accounts in JSON format',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
|
|
44
|
+
async handler(ctx) {
|
|
45
|
+
const { logger, apiClient, options } = ctx;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// Fetch organizations
|
|
49
|
+
const orgs = await tui.spinner({
|
|
50
|
+
message: 'Fetching organizations...',
|
|
51
|
+
clearOnSuccess: true,
|
|
52
|
+
callback: () => listOrganizations(apiClient),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (orgs.length === 0) {
|
|
56
|
+
tui.fatal('No organizations found for your account');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check GitHub status for each org
|
|
60
|
+
const orgStatuses = await tui.spinner({
|
|
61
|
+
message: 'Checking GitHub integration status...',
|
|
62
|
+
clearOnSuccess: true,
|
|
63
|
+
callback: async () => {
|
|
64
|
+
const statuses = await Promise.all(
|
|
65
|
+
orgs.map(async (org) => {
|
|
66
|
+
const status = await getGithubIntegrationStatus(apiClient, org.id);
|
|
67
|
+
return {
|
|
68
|
+
...org,
|
|
69
|
+
connected: status.connected,
|
|
70
|
+
integrations: status.integrations,
|
|
71
|
+
};
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
return statuses;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Sort orgs alphabetically
|
|
79
|
+
const sortedOrgs = [...orgStatuses].sort((a, b) => a.name.localeCompare(b.name));
|
|
80
|
+
|
|
81
|
+
const result = sortedOrgs.map((org) => ({
|
|
82
|
+
orgId: org.id,
|
|
83
|
+
orgName: org.name,
|
|
84
|
+
integrations: org.integrations.map((i) => ({
|
|
85
|
+
id: i.id,
|
|
86
|
+
githubAccountName: i.githubAccountName,
|
|
87
|
+
githubAccountType: i.githubAccountType,
|
|
88
|
+
})),
|
|
89
|
+
}));
|
|
90
|
+
|
|
91
|
+
if (options.json) {
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
tui.newline();
|
|
96
|
+
console.log(tui.bold('GitHub Accounts'));
|
|
97
|
+
tui.newline();
|
|
98
|
+
|
|
99
|
+
let totalCount = 0;
|
|
100
|
+
|
|
101
|
+
for (const org of sortedOrgs) {
|
|
102
|
+
if (org.integrations.length === 0) {
|
|
103
|
+
console.log(` ${org.name} ${tui.muted('(no accounts connected)')}`);
|
|
104
|
+
} else {
|
|
105
|
+
console.log(` ${org.name}`);
|
|
106
|
+
for (const integration of org.integrations) {
|
|
107
|
+
const typeLabel = integration.githubAccountType === 'org' ? 'org' : 'user';
|
|
108
|
+
console.log(
|
|
109
|
+
` ${tui.colorSuccess('✓')} ${integration.githubAccountName} ${tui.muted(`(${typeLabel})`)}`
|
|
110
|
+
);
|
|
111
|
+
totalCount++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
tui.newline();
|
|
117
|
+
if (totalCount === 0) {
|
|
118
|
+
console.log(
|
|
119
|
+
tui.muted(
|
|
120
|
+
`No GitHub accounts connected. Run ${tui.bold('agentuity git account add')} to add one.`
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
} else {
|
|
124
|
+
console.log(
|
|
125
|
+
tui.muted(`${totalCount} GitHub account${totalCount > 1 ? 's' : ''} connected`)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return result;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
logger.trace(error);
|
|
132
|
+
return logger.fatal(
|
|
133
|
+
'Failed to list GitHub accounts: %s',
|
|
134
|
+
error,
|
|
135
|
+
ErrorCode.INTEGRATION_FAILED
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
});
|