@assethub/cli 0.1.18 → 0.1.20
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/README.md +79 -12
- package/dist/canvas.d.ts +3 -1
- package/dist/canvas.d.ts.map +1 -1
- package/dist/canvas.js +6 -3
- package/dist/execution.d.ts +7 -0
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +33 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +358 -52
- package/dist/nodeMeshBatch.d.ts +30 -0
- package/dist/nodeMeshBatch.d.ts.map +1 -0
- package/dist/nodeMeshBatch.js +122 -0
- package/dist/runsUpload/uploadRun.d.ts +2 -1
- package/dist/runsUpload/uploadRun.d.ts.map +1 -1
- package/dist/runsUpload/uploadRun.js +2 -2
- package/dist/setup.d.ts +2 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +5 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { setTimeout as delay } from 'node:timers/promises';
|
|
|
3
3
|
import { cliVersion, diagnose, mcpConfig } from './setup.js';
|
|
4
4
|
import { ingestProjectSources } from './projectSources.js';
|
|
5
5
|
import { downloadCanvas } from './canvasDownload.js';
|
|
6
|
+
import { executeNodeMeshBatch, resumeNodeMeshBatch, } from './nodeMeshBatch.js';
|
|
6
7
|
import { writeCanvasComparison } from './canvasComparison.js';
|
|
7
8
|
import { importCanvasAssetWithState } from './canvasAssetImport.js';
|
|
8
9
|
import { execFile, spawn } from 'node:child_process';
|
|
@@ -16,7 +17,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
16
17
|
import { promisify } from 'node:util';
|
|
17
18
|
import { AssetHubApiError, WorkspaceClientError, createWorkspaceClient, createAssetHubClient, } from '@assethub/api-client';
|
|
18
19
|
import { resolveCanvasSelection, saveCanvasSelection } from './canvas.js';
|
|
19
|
-
import { activeExecution, CliExecutionError, executeRecorded, hasRecordedOperation, openExecutionSession, resumeRecorded, waitForExecution, } from './execution.js';
|
|
20
|
+
import { activeExecution, childOperationId, CliExecutionError, executeRecorded, hasRecordedOperation, openExecutionSession, resumeRecorded, waitForExecution, } from './execution.js';
|
|
20
21
|
import { buildRunUploadPlan, formatBytes } from './runsUpload/buildRunUpload.js';
|
|
21
22
|
import { readGraphFolder } from './runsUpload/graphFolder.js';
|
|
22
23
|
import { uploadRun } from './runsUpload/uploadRun.js';
|
|
@@ -33,6 +34,8 @@ const artifactGraphPartExtractorApiValues = new Set([
|
|
|
33
34
|
'ah_agent_graph_v3_6_1',
|
|
34
35
|
'ah_agent_graph_v3_6_3',
|
|
35
36
|
'ah_agent_graph_v3_6_4',
|
|
37
|
+
'ah_agent_graph_v3_6_5',
|
|
38
|
+
'ah_agent_graph_pluffy_v3_1',
|
|
36
39
|
]);
|
|
37
40
|
const partExtractorOptions = [
|
|
38
41
|
{
|
|
@@ -65,6 +68,16 @@ const partExtractorOptions = [
|
|
|
65
68
|
apiValue: 'ah_agent_graph_v3_6_4',
|
|
66
69
|
aliases: ['v3.6.4', '3.6.4'],
|
|
67
70
|
},
|
|
71
|
+
{
|
|
72
|
+
publicName: 'V3.6.5 Primary Images',
|
|
73
|
+
apiValue: 'ah_agent_graph_v3_6_5',
|
|
74
|
+
aliases: ['v3.6.5', '3.6.5'],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
publicName: 'Chibi Character (Pluffy) v3.1',
|
|
78
|
+
apiValue: 'ah_agent_graph_pluffy_v3_1',
|
|
79
|
+
aliases: ['pluffy', 'pluffy v3.1', 'pluffy 3.1'],
|
|
80
|
+
},
|
|
68
81
|
];
|
|
69
82
|
const partExtractorPublicNames = partExtractorOptions
|
|
70
83
|
.map(option => option.publicName)
|
|
@@ -91,7 +104,8 @@ Usage:
|
|
|
91
104
|
assethub auth login --access-token-stdin [--base-url <url>] [--profile <name>]
|
|
92
105
|
assethub auth status [--profile <name>]
|
|
93
106
|
assethub auth logout [--profile <name>]
|
|
94
|
-
assethub workspace list
|
|
107
|
+
assethub workspace list [--query <text>] [--limit <1-100>] [--cursor <cursor>]
|
|
108
|
+
assethub workspace get
|
|
95
109
|
assethub workspace create --name <name> [--operation-id <uuid>]
|
|
96
110
|
assethub workspace use <workspace-id>
|
|
97
111
|
assethub workspace members [--workspace <id>]
|
|
@@ -105,6 +119,7 @@ Usage:
|
|
|
105
119
|
assethub project ingest <source-dir> --out-dir <directory> [--exclude-dir <relative-path>...]
|
|
106
120
|
assethub language text|vision --input-json @<json> --operation-id <uuid> [--out <json>]
|
|
107
121
|
assethub canvas download --canvas <id> --out-dir <directory> [--mesh <mesh_asset_id>]
|
|
122
|
+
assethub canvas nodes --canvas <id>
|
|
108
123
|
assethub canvas import --canvas <id> --file <image> [--name <name>]
|
|
109
124
|
assethub canvas context get --canvas <id> [--out <json>]
|
|
110
125
|
assethub canvas context put --canvas <id> --file <json> [--if-version <n>]
|
|
@@ -127,6 +142,7 @@ Usage:
|
|
|
127
142
|
assethub composer run --part <mesh-asset-id> [--part <mesh-asset-id>...] --reference <image-asset-id> [--model <id>] [--mode quick|quality] [--transforms-json <json|@file>] [--canvas <id>] [--wait]
|
|
128
143
|
assethub composer run --input-json <json|@file> [--canvas <id>] [--operation-id <uuid>] [--wait] [--download --out-dir <dir>]
|
|
129
144
|
assethub composer run --from-run <run-id> [--transforms-json <json|@file>] [--mode quick|quality] [--wait]
|
|
145
|
+
assethub composer run --node <shape:id> --canvas <id> [--model <id>] [--mode quick|quality] [--operation-id <uuid>] [--wait]
|
|
130
146
|
assethub canvas create [--name <name>] [--operation-id <uuid>]
|
|
131
147
|
assethub canvas list [--cursor <cursor>] [--limit <n>]
|
|
132
148
|
assethub canvas get|use|open [<id>]
|
|
@@ -142,6 +158,7 @@ Usage:
|
|
|
142
158
|
assethub evaluations get <evaluation-id>
|
|
143
159
|
assethub image generate --input-json <json|@file|@-> [--canvas <id>] [--operation-id <uuid>] [--agent <name>] [--derived-from-run <run-id>] [--from-node <graphId/nodeId> --graph-revision <rev>] [--wait]
|
|
144
160
|
assethub mesh generate --input-json <json|@file|@-> [--canvas <id>] [--operation-id <uuid>] [--agent <name>] [--derived-from-run <run-id>] [--from-node <graphId/nodeId> --graph-revision <rev>] [--wait]
|
|
161
|
+
assethub mesh generate --node <shape:id> --canvas <id> [--model-id <id>] [--params-json <json|@file>] [--operation-id <uuid>] [--wait]
|
|
145
162
|
assethub mesh list [--query <text>] [--cursor <cursor>] [--limit <n>]
|
|
146
163
|
assethub mesh get <mesh-asset-id>
|
|
147
164
|
assethub mesh download <mesh-asset-id> --out-dir <directory>
|
|
@@ -173,6 +190,7 @@ Global options:
|
|
|
173
190
|
--api-key <key> Overrides saved auth and ASSETHUB_API_KEY.
|
|
174
191
|
--base-url <url> Override the selected API origin. Default: ${defaultBaseUrl}.
|
|
175
192
|
--profile <name> Select this saved profile ahead of environment credentials and origin.
|
|
193
|
+
--workspace <id> Select workspace scope for this command without changing saved selection.
|
|
176
194
|
--config <path> Auth config path. Defaults to ~/.assethub/config.json.
|
|
177
195
|
--version Show installed package version.
|
|
178
196
|
--help Show help.
|
|
@@ -434,7 +452,7 @@ const resolveAuth = async (flags) => {
|
|
|
434
452
|
: undefined;
|
|
435
453
|
const profile = explicitProfile ?? config?.defaultProfile ?? defaultProfileName;
|
|
436
454
|
const storedProfile = config?.profiles?.[profile];
|
|
437
|
-
const selected = Boolean(explicitProfile || storedProfile?.workspaceId);
|
|
455
|
+
const selected = Boolean(explicitProfile || storedProfile?.workspaceId || storedProfile?.apiKey?.startsWith('ah_pat_'));
|
|
438
456
|
if (selected && (!storedProfile || (!storedProfile.apiKey && !overrideKey)))
|
|
439
457
|
throw new Error('Selected profile has no workspace API key. Use workspace use <id> or auth login --api-key-stdin --profile <name>.');
|
|
440
458
|
const baseUrl = getFlag(flags, 'base-url') ??
|
|
@@ -442,17 +460,17 @@ const resolveAuth = async (flags) => {
|
|
|
442
460
|
? storedProfile.baseUrl
|
|
443
461
|
: env.ASSETHUB_API_BASE_URL ?? storedProfile?.baseUrl ?? defaultBaseUrl);
|
|
444
462
|
if (overrideKey)
|
|
445
|
-
return { apiKey: overrideKey, baseUrl, profile, source: 'flag' };
|
|
463
|
+
return { apiKey: overrideKey, baseUrl, profile, source: 'flag', workspaceId: getFlag(flags, 'workspace') };
|
|
446
464
|
if (selected && storedProfile?.apiKey) {
|
|
447
|
-
if (storedProfile.workspaceId &&
|
|
465
|
+
if ((storedProfile.workspaceId || storedProfile.apiKey.startsWith('ah_pat_')) &&
|
|
448
466
|
new URL(baseUrl).origin !== new URL(storedProfile.baseUrl).origin)
|
|
449
467
|
throw new Error('Selected workspace belongs to another API origin; select a workspace for this origin or supply an explicit API key');
|
|
450
|
-
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile' };
|
|
468
|
+
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile', workspaceId: getFlag(flags, 'workspace') ?? storedProfile.workspaceId };
|
|
451
469
|
}
|
|
452
470
|
if (env.ASSETHUB_API_KEY?.trim())
|
|
453
|
-
return { apiKey: env.ASSETHUB_API_KEY, baseUrl, profile, source: 'env' };
|
|
471
|
+
return { apiKey: env.ASSETHUB_API_KEY, baseUrl, profile, source: 'env', workspaceId: getFlag(flags, 'workspace') };
|
|
454
472
|
if (storedProfile?.apiKey)
|
|
455
|
-
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile' };
|
|
473
|
+
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile', workspaceId: getFlag(flags, 'workspace') ?? storedProfile.workspaceId };
|
|
456
474
|
throw new Error('Missing workspace API key. Use "workspace use <id>" after user login, "auth login --api-key-stdin", or ASSETHUB_API_KEY.');
|
|
457
475
|
};
|
|
458
476
|
const createContext = async (flags, args) => {
|
|
@@ -464,6 +482,7 @@ const createContext = async (flags, args) => {
|
|
|
464
482
|
client: createAssetHubClient({
|
|
465
483
|
apiKey: auth.apiKey,
|
|
466
484
|
baseUrl: auth.baseUrl,
|
|
485
|
+
workspaceId: auth.workspaceId,
|
|
467
486
|
}),
|
|
468
487
|
};
|
|
469
488
|
};
|
|
@@ -1243,12 +1262,25 @@ const commandModels = async (subcommand, positionals, ctx) => {
|
|
|
1243
1262
|
}
|
|
1244
1263
|
throw new Error('Unknown models command. Use "models list" or "models get".');
|
|
1245
1264
|
};
|
|
1246
|
-
const workspaceAuth = async (flags) => {
|
|
1265
|
+
const workspaceAuth = async (flags, allowPersonal = false) => {
|
|
1247
1266
|
const configPath = getConfigPath(flags);
|
|
1248
1267
|
const config = await readAuthConfig(configPath);
|
|
1249
1268
|
const explicitProfile = hasFlag(flags, 'profile') ? requireFlag(flags, 'profile') : undefined;
|
|
1250
1269
|
const profile = explicitProfile ?? config.defaultProfile ?? defaultProfileName;
|
|
1251
1270
|
const stored = config.profiles?.[profile];
|
|
1271
|
+
const preferredKey = getFlag(flags, 'api-key') ??
|
|
1272
|
+
(explicitProfile || stored?.workspaceId || stored?.apiKey?.startsWith('ah_pat_')
|
|
1273
|
+
? stored?.apiKey
|
|
1274
|
+
: env.ASSETHUB_API_KEY?.trim() || stored?.apiKey);
|
|
1275
|
+
if (allowPersonal && preferredKey?.startsWith('ah_pat_')) {
|
|
1276
|
+
const auth = await resolveAuth(flags);
|
|
1277
|
+
return {
|
|
1278
|
+
configPath, config, profile, accessToken: auth.apiKey,
|
|
1279
|
+
workspaceMfaToken: undefined, baseUrl: auth.baseUrl,
|
|
1280
|
+
selectedWorkspaceId: auth.workspaceId, personal: true,
|
|
1281
|
+
client: createWorkspaceClient({ accessToken: auth.apiKey, baseUrl: auth.baseUrl, workspaceId: auth.workspaceId }),
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1252
1284
|
const environmentToken = explicitProfile ? undefined : env.ASSETHUB_ACCESS_TOKEN?.trim();
|
|
1253
1285
|
const accessToken = environmentToken || stored?.accessToken;
|
|
1254
1286
|
if (!accessToken)
|
|
@@ -1272,6 +1304,7 @@ const workspaceAuth = async (flags) => {
|
|
|
1272
1304
|
workspaceMfaToken,
|
|
1273
1305
|
baseUrl,
|
|
1274
1306
|
selectedWorkspaceId: environmentToken ? undefined : stored?.workspaceId,
|
|
1307
|
+
personal: false,
|
|
1275
1308
|
client: createWorkspaceClient({ accessToken, workspaceMfaToken, baseUrl }),
|
|
1276
1309
|
};
|
|
1277
1310
|
};
|
|
@@ -1281,7 +1314,7 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
|
|
|
1281
1314
|
: hasFlag(flags, 'profile')
|
|
1282
1315
|
? undefined
|
|
1283
1316
|
: env.ASSETHUB_API_KEY?.trim();
|
|
1284
|
-
const auth = await workspaceAuth(flags);
|
|
1317
|
+
const auth = await workspaceAuth(flags, ['list', 'get', 'use'].includes(subcommand ?? ''));
|
|
1285
1318
|
if (['members', 'invite', 'set-role', 'remove-member'].includes(subcommand ?? '')) {
|
|
1286
1319
|
let workspaceId = getFlag(flags, 'workspace') ?? auth.selectedWorkspaceId;
|
|
1287
1320
|
if (!workspaceId)
|
|
@@ -1305,10 +1338,21 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
|
|
|
1305
1338
|
return;
|
|
1306
1339
|
}
|
|
1307
1340
|
if (subcommand === 'list') {
|
|
1308
|
-
print(await auth.client.list(
|
|
1341
|
+
print(await auth.client.list({
|
|
1342
|
+
query: getFlag(flags, 'query'),
|
|
1343
|
+
cursor: getFlag(flags, 'cursor'),
|
|
1344
|
+
limit: hasFlag(flags, 'limit') ? parsePositiveIntegerFlag(flags, 'limit', 100) : undefined,
|
|
1345
|
+
}));
|
|
1309
1346
|
return;
|
|
1310
1347
|
}
|
|
1311
1348
|
if (subcommand === 'get') {
|
|
1349
|
+
if (auth.personal && auth.selectedWorkspaceId) {
|
|
1350
|
+
const selected = await auth.client.select(auth.selectedWorkspaceId);
|
|
1351
|
+
if (!selected.workspace)
|
|
1352
|
+
throw new Error('Workspace API returned no selected workspace');
|
|
1353
|
+
print(selected.workspace);
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1312
1356
|
const account = await auth.client.list();
|
|
1313
1357
|
const selected = auth.selectedWorkspaceId;
|
|
1314
1358
|
const workspace = account.workspaces.find(item => selected ? item.id === selected : item.active);
|
|
@@ -1333,18 +1377,34 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
|
|
|
1333
1377
|
throw new Error('Use workspace list|get|create|use');
|
|
1334
1378
|
const workspaceId = requirePositional(positionals, 2, 'workspace-id');
|
|
1335
1379
|
const selected = await auth.client.select(workspaceId);
|
|
1380
|
+
if (selected.workspaceId !== workspaceId)
|
|
1381
|
+
throw new Error('Workspace API returned an invalid selection scope');
|
|
1336
1382
|
if (selected.mfa.status === 'setup_required' ||
|
|
1337
1383
|
selected.mfa.status === 'verify_required')
|
|
1338
1384
|
throw new Error('Workspace requires MFA. Complete verification in AssetHub, then provide the signed ah_workspace_mfa cookie through ASSETHUB_WORKSPACE_MFA together with your user access token');
|
|
1339
1385
|
const account = await auth.client.list();
|
|
1340
|
-
const workspace = account.workspaces.find(item => item.id === workspaceId);
|
|
1386
|
+
const workspace = selected.workspace ?? account.workspaces.find(item => item.id === workspaceId);
|
|
1341
1387
|
if (!workspace)
|
|
1342
1388
|
throw new Error('Workspace is not available to this authenticated user');
|
|
1343
1389
|
const profiles = auth.config.profiles ?? {};
|
|
1390
|
+
if (auth.personal) {
|
|
1391
|
+
if (account.authentication !== 'personal')
|
|
1392
|
+
throw new Error('Workspace API did not verify personal authentication');
|
|
1393
|
+
profiles[auth.profile] = {
|
|
1394
|
+
apiKey: auth.accessToken,
|
|
1395
|
+
userId: account.userId,
|
|
1396
|
+
workspaceId,
|
|
1397
|
+
baseUrl: auth.baseUrl,
|
|
1398
|
+
updatedAt: new Date().toISOString(),
|
|
1399
|
+
};
|
|
1400
|
+
await writeAuthConfig(auth.configPath, { ...auth.config, defaultProfile: auth.profile, profiles });
|
|
1401
|
+
print({ workspaceId, workspace, profile: auth.profile, selected: true, baseUrl: auth.baseUrl });
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1344
1404
|
const cached = Object.entries(profiles).find(([, entry]) => entry.userId === account.userId &&
|
|
1345
1405
|
entry.workspaceId === workspaceId &&
|
|
1346
1406
|
new URL(entry.baseUrl).origin === new URL(auth.baseUrl).origin &&
|
|
1347
|
-
entry.apiKey);
|
|
1407
|
+
entry.apiKey && !entry.apiKey.startsWith('ah_pat_'));
|
|
1348
1408
|
let apiKey;
|
|
1349
1409
|
for (const candidate of new Set([cached?.[1].apiKey, suppliedKey])) {
|
|
1350
1410
|
if (!candidate)
|
|
@@ -1447,7 +1507,13 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1447
1507
|
throw new Error('API key is empty. Pass --api-key, use --api-key-stdin, or set ASSETHUB_API_KEY.');
|
|
1448
1508
|
}
|
|
1449
1509
|
const baseUrl = getFlag(flags, 'base-url') ?? env.ASSETHUB_API_BASE_URL ?? defaultBaseUrl;
|
|
1450
|
-
|
|
1510
|
+
const personal = apiKey.startsWith('ah_pat_');
|
|
1511
|
+
const account = personal
|
|
1512
|
+
? await createWorkspaceClient({ accessToken: apiKey, baseUrl }).list()
|
|
1513
|
+
: undefined;
|
|
1514
|
+
if (personal && account?.authentication !== 'personal')
|
|
1515
|
+
throw new Error('Workspace API did not verify personal authentication');
|
|
1516
|
+
if (!personal && !hasFlag(flags, 'skip-verify')) {
|
|
1451
1517
|
const client = createAssetHubClient({ apiKey, baseUrl });
|
|
1452
1518
|
await client.v2.listModels();
|
|
1453
1519
|
}
|
|
@@ -1456,6 +1522,7 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1456
1522
|
profiles[profile] = {
|
|
1457
1523
|
apiKey,
|
|
1458
1524
|
baseUrl,
|
|
1525
|
+
...(account ? { userId: account.userId } : {}),
|
|
1459
1526
|
updatedAt: new Date().toISOString(),
|
|
1460
1527
|
};
|
|
1461
1528
|
await writeAuthConfig(configPath, {
|
|
@@ -1468,15 +1535,19 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1468
1535
|
profile,
|
|
1469
1536
|
baseUrl,
|
|
1470
1537
|
configPath,
|
|
1471
|
-
verified: !hasFlag(flags, 'skip-verify'),
|
|
1538
|
+
verified: personal || !hasFlag(flags, 'skip-verify'),
|
|
1539
|
+
...(account ? { authentication: 'personal', userId: account.userId } : {}),
|
|
1472
1540
|
});
|
|
1473
1541
|
return;
|
|
1474
1542
|
}
|
|
1475
1543
|
if (subcommand === 'status') {
|
|
1476
1544
|
const config = await readAuthConfig(configPath);
|
|
1477
1545
|
const current = config.profiles?.[getFlag(flags, 'profile') ?? config.defaultProfile ?? profile];
|
|
1478
|
-
if (
|
|
1479
|
-
|
|
1546
|
+
if (!hasFlag(flags, 'api-key') &&
|
|
1547
|
+
!current?.apiKey?.startsWith('ah_pat_') &&
|
|
1548
|
+
(hasFlag(flags, 'profile') || !env.ASSETHUB_API_KEY?.startsWith('ah_pat_')) &&
|
|
1549
|
+
((!hasFlag(flags, 'profile') && env.ASSETHUB_ACCESS_TOKEN?.trim()) ||
|
|
1550
|
+
(current?.accessToken && !current.apiKey))) {
|
|
1480
1551
|
const auth = await workspaceAuth(flags);
|
|
1481
1552
|
const account = await auth.client.list();
|
|
1482
1553
|
print({
|
|
@@ -1493,7 +1564,18 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1493
1564
|
const client = createAssetHubClient({
|
|
1494
1565
|
apiKey: auth.apiKey,
|
|
1495
1566
|
baseUrl: auth.baseUrl,
|
|
1567
|
+
workspaceId: auth.workspaceId,
|
|
1496
1568
|
});
|
|
1569
|
+
if (auth.apiKey.startsWith('ah_pat_')) {
|
|
1570
|
+
const account = await createWorkspaceClient({ accessToken: auth.apiKey, baseUrl: auth.baseUrl, workspaceId: auth.workspaceId }).list();
|
|
1571
|
+
if (account.authentication !== 'personal')
|
|
1572
|
+
throw new Error('Workspace API did not verify personal authentication');
|
|
1573
|
+
if (auth.workspaceId)
|
|
1574
|
+
await client.v2.getCapabilities();
|
|
1575
|
+
print({ authenticated: true, authentication: 'personal', userId: account.userId, workspaceId: auth.workspaceId,
|
|
1576
|
+
profile: auth.profile, source: auth.source, baseUrl: auth.baseUrl });
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1497
1579
|
const models = await client.v2.listModels();
|
|
1498
1580
|
print({
|
|
1499
1581
|
authenticated: true,
|
|
@@ -1639,6 +1721,134 @@ const printRecorded = async (ctx, queued) => {
|
|
|
1639
1721
|
});
|
|
1640
1722
|
process.exitCode = executionExitCode(execution);
|
|
1641
1723
|
};
|
|
1724
|
+
const canvasNodeFlag = (flags) => {
|
|
1725
|
+
if (!hasFlag(flags, 'node'))
|
|
1726
|
+
return undefined;
|
|
1727
|
+
const nodeId = requireFlag(flags, 'node');
|
|
1728
|
+
if (!nodeId.startsWith('shape:') || nodeId.length <= 6)
|
|
1729
|
+
throw new Error('--node must be a native shape:<id> from canvas nodes');
|
|
1730
|
+
if (sourceInputCount(flags) ||
|
|
1731
|
+
[
|
|
1732
|
+
'input-json',
|
|
1733
|
+
'upload-id',
|
|
1734
|
+
'from-node',
|
|
1735
|
+
'graph-revision',
|
|
1736
|
+
'from-run',
|
|
1737
|
+
'part',
|
|
1738
|
+
'reference',
|
|
1739
|
+
'transforms-json',
|
|
1740
|
+
].some(name => hasFlag(flags, name)))
|
|
1741
|
+
throw new Error('--node cannot be combined with explicit sources, parts, reference, input JSON, or another node/run selector');
|
|
1742
|
+
return nodeId;
|
|
1743
|
+
};
|
|
1744
|
+
const printNodeMeshBatch = async (ctx, batch) => {
|
|
1745
|
+
const executions = [...batch.executions];
|
|
1746
|
+
if (hasFlag(ctx.flags, 'wait')) {
|
|
1747
|
+
try {
|
|
1748
|
+
for (let i = 0; i < executions.length; i++)
|
|
1749
|
+
executions[i] = await waitForExecution(ctx.client, executions[i].runId, watchOptions(ctx.flags));
|
|
1750
|
+
}
|
|
1751
|
+
catch (error) {
|
|
1752
|
+
if (!(error instanceof CliExecutionError))
|
|
1753
|
+
throw error;
|
|
1754
|
+
throw new CliExecutionError(error.message, error.exitCode, batch.operationId, error.execution, error.runId);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
const downloads = await maybeDownloadUrls(ctx.flags, executions.flatMap(execution => execution.outputs));
|
|
1758
|
+
print({ ...batch, executions, ...(downloads ? { downloads } : {}) });
|
|
1759
|
+
process.exitCode = Math.max(0, ...executions.map(executionExitCode));
|
|
1760
|
+
};
|
|
1761
|
+
const commandNodeMesh = async (ctx, nodeId) => {
|
|
1762
|
+
const body = {};
|
|
1763
|
+
const modelId = getFlag(ctx.flags, 'model-id');
|
|
1764
|
+
if (modelId)
|
|
1765
|
+
body.modelId = modelId;
|
|
1766
|
+
const name = getFlag(ctx.flags, 'name');
|
|
1767
|
+
if (name)
|
|
1768
|
+
body.name = name;
|
|
1769
|
+
if (hasFlag(ctx.flags, 'face-limit'))
|
|
1770
|
+
body.faceLimit = parsePositiveIntegerFlag(ctx.flags, 'face-limit', 1);
|
|
1771
|
+
if (hasFlag(ctx.flags, 'low-poly'))
|
|
1772
|
+
body.isLowPoly = parseBooleanFlag(ctx.flags, 'low-poly');
|
|
1773
|
+
if (hasFlag(ctx.flags, 'params-json')) {
|
|
1774
|
+
const params = await readJsonArgument(requireFlag(ctx.flags, 'params-json'), '--params-json');
|
|
1775
|
+
if (Object.values(params).some(value => typeof value !== 'string' &&
|
|
1776
|
+
typeof value !== 'boolean' &&
|
|
1777
|
+
!(typeof value === 'number' && Number.isFinite(value))))
|
|
1778
|
+
throw new Error('--params-json values must be strings, booleans, or finite numbers');
|
|
1779
|
+
body.params = params;
|
|
1780
|
+
}
|
|
1781
|
+
const operationId = getFlag(ctx.flags, 'operation-id');
|
|
1782
|
+
const canvasId = selectedCanvasId(ctx.flags);
|
|
1783
|
+
const resumeOptions = {
|
|
1784
|
+
...stateOptions(ctx),
|
|
1785
|
+
expectedCanvasId: canvasId,
|
|
1786
|
+
expectedCanvasNodeId: nodeId,
|
|
1787
|
+
expectedBody: body,
|
|
1788
|
+
};
|
|
1789
|
+
if (operationId) {
|
|
1790
|
+
const batch = await resumeNodeMeshBatch({ ...resumeOptions, operationId });
|
|
1791
|
+
if (batch) {
|
|
1792
|
+
await printNodeMeshBatch(ctx, batch);
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
if (await hasRecordedOperation(resumeOptions.stateDir, operationId)) {
|
|
1796
|
+
await printRecorded(ctx, await resumeRecorded({
|
|
1797
|
+
...resumeOptions,
|
|
1798
|
+
operationId,
|
|
1799
|
+
expectedOperation: 'mesh.generate',
|
|
1800
|
+
}));
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
const session = await openExecutionSession({
|
|
1805
|
+
...stateOptions(ctx),
|
|
1806
|
+
canvasId,
|
|
1807
|
+
operation: 'mesh.generate',
|
|
1808
|
+
create: false,
|
|
1809
|
+
});
|
|
1810
|
+
const { items } = await ctx.client.v2.listCanvasNodes(session.canvas.id);
|
|
1811
|
+
const target = items.find(item => item.nodeId === nodeId);
|
|
1812
|
+
if (!target)
|
|
1813
|
+
throw new Error('Canvas node is unavailable; read canvas nodes again');
|
|
1814
|
+
const finishedOrBusy = (status) => ['complete', 'completed', 'generating'].includes(status ?? '');
|
|
1815
|
+
if (target.type === 'production-3d') {
|
|
1816
|
+
if (!target.actions.includes('mesh.generate'))
|
|
1817
|
+
throw new Error('Mesh generation is unavailable for this node');
|
|
1818
|
+
const children = items.filter(item => item.sourceNodeId === nodeId &&
|
|
1819
|
+
['mesh-gen', 'preview-asset', 'part-group'].includes(item.type));
|
|
1820
|
+
if (!children.length)
|
|
1821
|
+
throw new Error('Production node has no saved part nodes to generate');
|
|
1822
|
+
const pending = children.filter(item => item.actions.includes('mesh.generate') &&
|
|
1823
|
+
!finishedOrBusy(item.meshStatus));
|
|
1824
|
+
const batchId = operationId ?? randomUUID();
|
|
1825
|
+
stderr.write(`[nodes] batch ${batchId}\n`);
|
|
1826
|
+
await printNodeMeshBatch(ctx, await executeNodeMeshBatch(session, {
|
|
1827
|
+
...executionOptions(ctx.flags),
|
|
1828
|
+
operationId: batchId,
|
|
1829
|
+
nodeId,
|
|
1830
|
+
body,
|
|
1831
|
+
children: pending.map(item => item.nodeId),
|
|
1832
|
+
skipped: children
|
|
1833
|
+
.filter(item => !pending.includes(item))
|
|
1834
|
+
.map(({ nodeId, meshStatus }) => ({ nodeId, meshStatus })),
|
|
1835
|
+
}));
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
if (!['mesh-gen', 'preview-asset', 'part-group'].includes(target.type))
|
|
1839
|
+
throw new Error('This canvas node cannot generate a mesh');
|
|
1840
|
+
if (target.meshStatus === 'generating' ||
|
|
1841
|
+
(target.type !== 'mesh-gen' && finishedOrBusy(target.meshStatus))) {
|
|
1842
|
+
print({ nodeId, skipped: true, meshStatus: target.meshStatus });
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
if (!target.actions.includes('mesh.generate'))
|
|
1846
|
+
throw new Error('Mesh generation is unavailable for this node');
|
|
1847
|
+
await printRecorded(ctx, await executeRecorded(session, 'mesh.generate', body, {
|
|
1848
|
+
...executionOptions(ctx.flags),
|
|
1849
|
+
canvasNode: { nodeId },
|
|
1850
|
+
}));
|
|
1851
|
+
};
|
|
1642
1852
|
const commandImage = async (subcommand, ctx) => {
|
|
1643
1853
|
if (subcommand !== 'generate')
|
|
1644
1854
|
throw new Error('Unknown image command. Use "image generate".');
|
|
@@ -1746,6 +1956,54 @@ const refinementModeInfo = (capabilities, mode) => {
|
|
|
1746
1956
|
throw new Error(`Refinement mode ${mode} is unavailable${info.reason ? `: ${info.reason}` : ''}`);
|
|
1747
1957
|
return info;
|
|
1748
1958
|
};
|
|
1959
|
+
/** Reuse the frozen sources and latest editable scene, never replay server-only receipt fields. */
|
|
1960
|
+
const composerInputFromRun = (previous, operation) => {
|
|
1961
|
+
const source = previous.resolvedInput ?? previous.input ?? previous.requestedInput ?? {};
|
|
1962
|
+
const fields = operation === 'mesh.compose'
|
|
1963
|
+
? [
|
|
1964
|
+
'fullBodyImageAssetId',
|
|
1965
|
+
'agentVersion',
|
|
1966
|
+
'agentRuntime',
|
|
1967
|
+
'mode',
|
|
1968
|
+
'targetCharacterHeightM',
|
|
1969
|
+
'projectName',
|
|
1970
|
+
'decimateRatio',
|
|
1971
|
+
'quickRunId',
|
|
1972
|
+
'quickScene',
|
|
1973
|
+
]
|
|
1974
|
+
: ['fullBodyImageAssetId', 'agentRuntime', 'mode', 'instruction', 'maxRounds'];
|
|
1975
|
+
const input = Object.fromEntries(fields
|
|
1976
|
+
.filter(key => source[key] !== undefined)
|
|
1977
|
+
.map(key => [key, source[key]]));
|
|
1978
|
+
const parts = previous.composition?.parts ?? source.parts;
|
|
1979
|
+
const partFields = operation === 'mesh.compose'
|
|
1980
|
+
? ['assetId', 'name', 'canonicalKey', 'partImageAssetId', 'partTaskId']
|
|
1981
|
+
: [
|
|
1982
|
+
'assetId',
|
|
1983
|
+
'name',
|
|
1984
|
+
'canonicalKey',
|
|
1985
|
+
'volumeCentroid',
|
|
1986
|
+
'sourceAssetId',
|
|
1987
|
+
'transform',
|
|
1988
|
+
];
|
|
1989
|
+
input.parts = Array.isArray(parts)
|
|
1990
|
+
? parts.map(part => part && typeof part === 'object'
|
|
1991
|
+
? Object.fromEntries(Object.entries(part).filter(([key]) => partFields.includes(key)))
|
|
1992
|
+
: part)
|
|
1993
|
+
: parts;
|
|
1994
|
+
const transforms = previous.composition?.transforms ?? source.transforms;
|
|
1995
|
+
if (transforms != null)
|
|
1996
|
+
input.transforms = transforms;
|
|
1997
|
+
if (operation === 'mesh.refine') {
|
|
1998
|
+
const referenceTransform = previous.composition?.referenceTransform ?? source.referenceTransform;
|
|
1999
|
+
if (referenceTransform != null)
|
|
2000
|
+
input.referenceTransform = referenceTransform;
|
|
2001
|
+
// Compose modes are not refinement modes; preserve a prior refinement's mode only.
|
|
2002
|
+
if (previous.operation !== 'mesh.refine')
|
|
2003
|
+
delete input.mode;
|
|
2004
|
+
}
|
|
2005
|
+
return input;
|
|
2006
|
+
};
|
|
1749
2007
|
const commandComposerRefine = async (ctx) => {
|
|
1750
2008
|
const capabilities = await ctx.client.v2.getMeshRefinementModes();
|
|
1751
2009
|
if (hasFlag(ctx.flags, 'list-modes')) {
|
|
@@ -1769,20 +2027,7 @@ const commandComposerRefine = async (ctx) => {
|
|
|
1769
2027
|
const input = json
|
|
1770
2028
|
? await readJsonArgument(json, '--input-json')
|
|
1771
2029
|
: previous
|
|
1772
|
-
?
|
|
1773
|
-
...(previous.requestedInput ?? previous.input),
|
|
1774
|
-
...(previous.operation === 'mesh.refine' &&
|
|
1775
|
-
(previous.requestedInput?.mode ?? previous.input.mode)
|
|
1776
|
-
? { mode: previous.requestedInput?.mode ?? previous.input.mode }
|
|
1777
|
-
: { mode: undefined }),
|
|
1778
|
-
parts: previous.composition?.parts ??
|
|
1779
|
-
previous.requestedInput?.parts ??
|
|
1780
|
-
previous.input.parts,
|
|
1781
|
-
transforms: previous.composition?.transforms,
|
|
1782
|
-
referenceTransform: previous.composition?.referenceTransform ??
|
|
1783
|
-
previous.requestedInput?.referenceTransform ??
|
|
1784
|
-
previous.input.referenceTransform,
|
|
1785
|
-
}
|
|
2030
|
+
? composerInputFromRun(previous, 'mesh.refine')
|
|
1786
2031
|
: {
|
|
1787
2032
|
parts: parts.map(assetId => ({
|
|
1788
2033
|
assetId: assertFlagHasValue(assetId, '--part <asset-id>'),
|
|
@@ -1850,6 +2095,9 @@ const commandComposerRefine = async (ctx) => {
|
|
|
1850
2095
|
mode,
|
|
1851
2096
|
instruction,
|
|
1852
2097
|
...(maxRounds != null ? { maxRounds } : {}),
|
|
2098
|
+
...(input.agentRuntime
|
|
2099
|
+
? { agentRuntime: input.agentRuntime }
|
|
2100
|
+
: {}),
|
|
1853
2101
|
};
|
|
1854
2102
|
await printRecorded(ctx, await executeRecorded(session, 'mesh.refine', body, {
|
|
1855
2103
|
...executionOptions(ctx.flags),
|
|
@@ -1867,6 +2115,7 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
1867
2115
|
}
|
|
1868
2116
|
if (subcommand !== 'run')
|
|
1869
2117
|
throw new Error('Use composer models|run');
|
|
2118
|
+
const nodeId = canvasNodeFlag(ctx.flags);
|
|
1870
2119
|
const fromRun = getFlag(ctx.flags, 'from-run');
|
|
1871
2120
|
const json = getFlag(ctx.flags, 'input-json');
|
|
1872
2121
|
const parts = getFlagValues(ctx.flags, 'part');
|
|
@@ -1876,20 +2125,23 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
1876
2125
|
const previous = fromRun ? await ctx.client.v2.getRun(fromRun) : undefined;
|
|
1877
2126
|
if (previous && previous.operation !== 'mesh.compose')
|
|
1878
2127
|
throw new Error('--from-run must identify a mesh.compose execution');
|
|
1879
|
-
const input =
|
|
1880
|
-
? {
|
|
1881
|
-
:
|
|
1882
|
-
?
|
|
1883
|
-
:
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
2128
|
+
const input = nodeId
|
|
2129
|
+
? {}
|
|
2130
|
+
: previous
|
|
2131
|
+
? composerInputFromRun(previous, 'mesh.compose')
|
|
2132
|
+
: json
|
|
2133
|
+
? await readJsonArgument(json, '--input-json')
|
|
2134
|
+
: {
|
|
2135
|
+
parts: parts.map(assetId => ({
|
|
2136
|
+
assetId: assertFlagHasValue(assetId, '--part <asset-id>'),
|
|
2137
|
+
})),
|
|
2138
|
+
fullBodyImageAssetId: requireFlag(ctx.flags, 'reference'),
|
|
2139
|
+
};
|
|
1889
2140
|
delete input.executionContext;
|
|
1890
|
-
if (!
|
|
1891
|
-
!input.parts
|
|
1892
|
-
|
|
2141
|
+
if (!nodeId &&
|
|
2142
|
+
(!Array.isArray(input.parts) ||
|
|
2143
|
+
!input.parts.length ||
|
|
2144
|
+
!input.fullBodyImageAssetId))
|
|
1893
2145
|
throw new Error('Composer requires parts[] and fullBodyImageAssetId');
|
|
1894
2146
|
const mode = getFlag(ctx.flags, 'mode');
|
|
1895
2147
|
if (mode != null && mode !== 'quick' && mode !== 'quality')
|
|
@@ -1905,17 +2157,48 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
1905
2157
|
const transforms = getFlag(ctx.flags, 'transforms-json');
|
|
1906
2158
|
if (transforms)
|
|
1907
2159
|
input.transforms = await readJsonArgument(transforms, '--transforms-json');
|
|
2160
|
+
if (previous && input.transforms) {
|
|
2161
|
+
delete input.quickRunId;
|
|
2162
|
+
delete input.quickScene;
|
|
2163
|
+
}
|
|
1908
2164
|
const canvasId = selectedCanvasId(ctx.flags) ?? previous?.canvas.id;
|
|
1909
2165
|
if (previous && canvasId !== previous.canvas.id)
|
|
1910
2166
|
throw new Error('Recomposition must use the original canvas to retain lineage');
|
|
2167
|
+
const operationId = getFlag(ctx.flags, 'operation-id');
|
|
2168
|
+
if (nodeId && operationId) {
|
|
2169
|
+
const resumeOptions = {
|
|
2170
|
+
...stateOptions(ctx),
|
|
2171
|
+
operationId,
|
|
2172
|
+
expectedCanvasId: canvasId,
|
|
2173
|
+
expectedCanvasNodeId: nodeId,
|
|
2174
|
+
expectedOperation: 'mesh.compose',
|
|
2175
|
+
expectedBody: input,
|
|
2176
|
+
};
|
|
2177
|
+
// A mesh batch reserves its root key even though its paid requests have child keys.
|
|
2178
|
+
await resumeNodeMeshBatch(resumeOptions);
|
|
2179
|
+
if (await hasRecordedOperation(resumeOptions.stateDir, operationId)) {
|
|
2180
|
+
await printRecorded(ctx, await resumeRecorded(resumeOptions));
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
1911
2184
|
const session = await openExecutionSession({
|
|
1912
2185
|
...stateOptions(ctx),
|
|
1913
2186
|
canvasId,
|
|
1914
2187
|
operation: 'mesh.compose',
|
|
2188
|
+
...(nodeId ? { create: false } : {}),
|
|
1915
2189
|
});
|
|
2190
|
+
if (nodeId) {
|
|
2191
|
+
const { items } = await ctx.client.v2.listCanvasNodes(session.canvas.id);
|
|
2192
|
+
const target = items.find(item => item.nodeId === nodeId);
|
|
2193
|
+
if (!target ||
|
|
2194
|
+
!['production-3d', 'part-composer'].includes(target.type) ||
|
|
2195
|
+
!target.actions.includes('mesh.compose'))
|
|
2196
|
+
throw new Error('Composition is unavailable for this canvas node');
|
|
2197
|
+
}
|
|
1916
2198
|
const queued = await executeRecorded(session, 'mesh.compose', input, {
|
|
1917
2199
|
...executionOptions(ctx.flags),
|
|
1918
2200
|
...(previous ? { parentRunId: previous.runId } : {}),
|
|
2201
|
+
...(nodeId ? { canvasNode: { nodeId } } : {}),
|
|
1919
2202
|
});
|
|
1920
2203
|
await printRecorded(ctx, queued);
|
|
1921
2204
|
};
|
|
@@ -1939,6 +2222,11 @@ const commandMesh = async (subcommand, positionals, ctx) => {
|
|
|
1939
2222
|
return;
|
|
1940
2223
|
}
|
|
1941
2224
|
if (subcommand === 'generate') {
|
|
2225
|
+
const nodeId = canvasNodeFlag(ctx.flags);
|
|
2226
|
+
if (nodeId) {
|
|
2227
|
+
await commandNodeMesh(ctx, nodeId);
|
|
2228
|
+
return;
|
|
2229
|
+
}
|
|
1942
2230
|
const inputJson = getFlag(ctx.flags, 'input-json');
|
|
1943
2231
|
const input = inputJson
|
|
1944
2232
|
? await readJsonArgument(inputJson, '--input-json')
|
|
@@ -2569,12 +2857,6 @@ const productionSession = (ctx, operation) => openExecutionSession({
|
|
|
2569
2857
|
canvasId: selectedCanvasId(ctx.flags),
|
|
2570
2858
|
operation,
|
|
2571
2859
|
});
|
|
2572
|
-
const childOperationId = (parent, step) => {
|
|
2573
|
-
const hash = createHash('sha256')
|
|
2574
|
-
.update(JSON.stringify([parent, step]))
|
|
2575
|
-
.digest('hex');
|
|
2576
|
-
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-8${hash.slice(17, 20)}-${hash.slice(20, 32)}`;
|
|
2577
|
-
};
|
|
2578
2860
|
const commandProduction = async (subcommand, positionals, ctx) => {
|
|
2579
2861
|
if (subcommand === 'agents') {
|
|
2580
2862
|
print(sanitizeProductionOutput(await ctx.client.v1.getProductionAgents()));
|
|
@@ -2814,7 +3096,7 @@ const commandParts = async (subcommand, ctx) => {
|
|
|
2814
3096
|
...stateOptions(ctx),
|
|
2815
3097
|
operationId: executeOperationId,
|
|
2816
3098
|
expectedCanvasId: session.canvas.id,
|
|
2817
|
-
|
|
3099
|
+
expectedBodySubset: {
|
|
2818
3100
|
partExtractionMode: getFlag(ctx.flags, 'part-extraction-mode'),
|
|
2819
3101
|
...(getFlagValues(ctx.flags, 'task-id').length
|
|
2820
3102
|
? { confirmedTaskIds: getFlagValues(ctx.flags, 'task-id') }
|
|
@@ -3025,6 +3307,11 @@ const canvasForRead = (ctx) => openExecutionSession({
|
|
|
3025
3307
|
create: false,
|
|
3026
3308
|
});
|
|
3027
3309
|
const commandCanvas = async (subcommand, positionals, ctx) => {
|
|
3310
|
+
if (subcommand === 'nodes') {
|
|
3311
|
+
const { canvas } = await canvasForRead(ctx);
|
|
3312
|
+
print(await ctx.client.v2.listCanvasNodes(canvas.id));
|
|
3313
|
+
return;
|
|
3314
|
+
}
|
|
3028
3315
|
if (subcommand === 'download') {
|
|
3029
3316
|
const outDir = requireFlag(ctx.flags, 'out-dir');
|
|
3030
3317
|
const { canvas } = await canvasForRead(ctx);
|
|
@@ -3357,6 +3644,14 @@ const commandRuns = async (subcommand, positionals, ctx) => {
|
|
|
3357
3644
|
return;
|
|
3358
3645
|
}
|
|
3359
3646
|
if (subcommand === 'resume') {
|
|
3647
|
+
const batch = await resumeNodeMeshBatch({
|
|
3648
|
+
...stateOptions(ctx),
|
|
3649
|
+
operationId: requirePositional(positionals, 2, 'operation-id'),
|
|
3650
|
+
});
|
|
3651
|
+
if (batch) {
|
|
3652
|
+
await printNodeMeshBatch(ctx, batch);
|
|
3653
|
+
return;
|
|
3654
|
+
}
|
|
3360
3655
|
const result = await resumeRecorded({
|
|
3361
3656
|
...stateOptions(ctx),
|
|
3362
3657
|
operationId: requirePositional(positionals, 2, 'operation-id'),
|
|
@@ -3389,6 +3684,7 @@ const commandRuns = async (subcommand, positionals, ctx) => {
|
|
|
3389
3684
|
plan,
|
|
3390
3685
|
baseUrl: ctx.auth.baseUrl,
|
|
3391
3686
|
apiKey: ctx.auth.apiKey,
|
|
3687
|
+
workspaceId: ctx.auth.workspaceId,
|
|
3392
3688
|
skipRegister: args.skipRegister,
|
|
3393
3689
|
onProgress: event => {
|
|
3394
3690
|
if (event.kind === 'register') {
|
|
@@ -3509,7 +3805,17 @@ const run = async () => {
|
|
|
3509
3805
|
if (command === 'mcp') {
|
|
3510
3806
|
if (subcommand !== 'config')
|
|
3511
3807
|
throw new Error('Use mcp tools [tool-name] or mcp config --client cursor|codex');
|
|
3512
|
-
|
|
3808
|
+
const config = await readAuthConfig(getConfigPath(parsed.flags));
|
|
3809
|
+
const explicitProfile = getFlag(parsed.flags, 'profile');
|
|
3810
|
+
const stored = config.profiles?.[explicitProfile ?? config.defaultProfile ?? defaultProfileName];
|
|
3811
|
+
if (explicitProfile && !stored)
|
|
3812
|
+
throw new Error('Selected profile does not exist');
|
|
3813
|
+
const selected = explicitProfile || stored?.workspaceId || stored?.apiKey?.startsWith('ah_pat_');
|
|
3814
|
+
const baseUrl = getFlag(parsed.flags, 'base-url') ??
|
|
3815
|
+
(selected ? stored?.baseUrl : env.ASSETHUB_API_BASE_URL ?? stored?.baseUrl) ?? defaultBaseUrl;
|
|
3816
|
+
const workspaceId = getFlag(parsed.flags, 'workspace') ??
|
|
3817
|
+
(stored && new URL(baseUrl).origin === new URL(stored.baseUrl).origin ? stored.workspaceId : undefined);
|
|
3818
|
+
stdout.write(mcpConfig(requireFlag(parsed.flags, 'client'), baseUrl, hasFlag(parsed.flags, 'account'), workspaceId));
|
|
3513
3819
|
return;
|
|
3514
3820
|
}
|
|
3515
3821
|
if (command === 'doctor') {
|