@assethub/cli 0.1.17 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -6
- package/dist/canvas.d.ts +3 -1
- package/dist/canvas.d.ts.map +1 -1
- package/dist/canvas.js +6 -3
- package/dist/canvasDownload.d.ts +28 -0
- package/dist/canvasDownload.d.ts.map +1 -0
- package/dist/canvasDownload.js +184 -0
- 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 +365 -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
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
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
|
+
import { downloadCanvas } from './canvasDownload.js';
|
|
6
|
+
import { executeNodeMeshBatch, resumeNodeMeshBatch, } from './nodeMeshBatch.js';
|
|
5
7
|
import { writeCanvasComparison } from './canvasComparison.js';
|
|
6
8
|
import { importCanvasAssetWithState } from './canvasAssetImport.js';
|
|
7
9
|
import { execFile, spawn } from 'node:child_process';
|
|
@@ -15,7 +17,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
15
17
|
import { promisify } from 'node:util';
|
|
16
18
|
import { AssetHubApiError, WorkspaceClientError, createWorkspaceClient, createAssetHubClient, } from '@assethub/api-client';
|
|
17
19
|
import { resolveCanvasSelection, saveCanvasSelection } from './canvas.js';
|
|
18
|
-
import { activeExecution, CliExecutionError, executeRecorded, hasRecordedOperation, openExecutionSession, resumeRecorded, waitForExecution, } from './execution.js';
|
|
20
|
+
import { activeExecution, childOperationId, CliExecutionError, executeRecorded, hasRecordedOperation, openExecutionSession, resumeRecorded, waitForExecution, } from './execution.js';
|
|
19
21
|
import { buildRunUploadPlan, formatBytes } from './runsUpload/buildRunUpload.js';
|
|
20
22
|
import { readGraphFolder } from './runsUpload/graphFolder.js';
|
|
21
23
|
import { uploadRun } from './runsUpload/uploadRun.js';
|
|
@@ -90,7 +92,8 @@ Usage:
|
|
|
90
92
|
assethub auth login --access-token-stdin [--base-url <url>] [--profile <name>]
|
|
91
93
|
assethub auth status [--profile <name>]
|
|
92
94
|
assethub auth logout [--profile <name>]
|
|
93
|
-
assethub workspace list
|
|
95
|
+
assethub workspace list [--query <text>] [--limit <1-100>] [--cursor <cursor>]
|
|
96
|
+
assethub workspace get
|
|
94
97
|
assethub workspace create --name <name> [--operation-id <uuid>]
|
|
95
98
|
assethub workspace use <workspace-id>
|
|
96
99
|
assethub workspace members [--workspace <id>]
|
|
@@ -103,6 +106,8 @@ Usage:
|
|
|
103
106
|
assethub workspace context get --internal --org <uuid> --actor <uuid> --canvas <id> [--out <json>] [--order <uuid>]
|
|
104
107
|
assethub project ingest <source-dir> --out-dir <directory> [--exclude-dir <relative-path>...]
|
|
105
108
|
assethub language text|vision --input-json @<json> --operation-id <uuid> [--out <json>]
|
|
109
|
+
assethub canvas download --canvas <id> --out-dir <directory> [--mesh <mesh_asset_id>]
|
|
110
|
+
assethub canvas nodes --canvas <id>
|
|
106
111
|
assethub canvas import --canvas <id> --file <image> [--name <name>]
|
|
107
112
|
assethub canvas context get --canvas <id> [--out <json>]
|
|
108
113
|
assethub canvas context put --canvas <id> --file <json> [--if-version <n>]
|
|
@@ -125,6 +130,7 @@ Usage:
|
|
|
125
130
|
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]
|
|
126
131
|
assethub composer run --input-json <json|@file> [--canvas <id>] [--operation-id <uuid>] [--wait] [--download --out-dir <dir>]
|
|
127
132
|
assethub composer run --from-run <run-id> [--transforms-json <json|@file>] [--mode quick|quality] [--wait]
|
|
133
|
+
assethub composer run --node <shape:id> --canvas <id> [--model <id>] [--mode quick|quality] [--operation-id <uuid>] [--wait]
|
|
128
134
|
assethub canvas create [--name <name>] [--operation-id <uuid>]
|
|
129
135
|
assethub canvas list [--cursor <cursor>] [--limit <n>]
|
|
130
136
|
assethub canvas get|use|open [<id>]
|
|
@@ -140,6 +146,7 @@ Usage:
|
|
|
140
146
|
assethub evaluations get <evaluation-id>
|
|
141
147
|
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]
|
|
142
148
|
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]
|
|
149
|
+
assethub mesh generate --node <shape:id> --canvas <id> [--model-id <id>] [--params-json <json|@file>] [--operation-id <uuid>] [--wait]
|
|
143
150
|
assethub mesh list [--query <text>] [--cursor <cursor>] [--limit <n>]
|
|
144
151
|
assethub mesh get <mesh-asset-id>
|
|
145
152
|
assethub mesh download <mesh-asset-id> --out-dir <directory>
|
|
@@ -171,6 +178,7 @@ Global options:
|
|
|
171
178
|
--api-key <key> Overrides saved auth and ASSETHUB_API_KEY.
|
|
172
179
|
--base-url <url> Override the selected API origin. Default: ${defaultBaseUrl}.
|
|
173
180
|
--profile <name> Select this saved profile ahead of environment credentials and origin.
|
|
181
|
+
--workspace <id> Select workspace scope for this command without changing saved selection.
|
|
174
182
|
--config <path> Auth config path. Defaults to ~/.assethub/config.json.
|
|
175
183
|
--version Show installed package version.
|
|
176
184
|
--help Show help.
|
|
@@ -432,7 +440,7 @@ const resolveAuth = async (flags) => {
|
|
|
432
440
|
: undefined;
|
|
433
441
|
const profile = explicitProfile ?? config?.defaultProfile ?? defaultProfileName;
|
|
434
442
|
const storedProfile = config?.profiles?.[profile];
|
|
435
|
-
const selected = Boolean(explicitProfile || storedProfile?.workspaceId);
|
|
443
|
+
const selected = Boolean(explicitProfile || storedProfile?.workspaceId || storedProfile?.apiKey?.startsWith('ah_pat_'));
|
|
436
444
|
if (selected && (!storedProfile || (!storedProfile.apiKey && !overrideKey)))
|
|
437
445
|
throw new Error('Selected profile has no workspace API key. Use workspace use <id> or auth login --api-key-stdin --profile <name>.');
|
|
438
446
|
const baseUrl = getFlag(flags, 'base-url') ??
|
|
@@ -440,17 +448,17 @@ const resolveAuth = async (flags) => {
|
|
|
440
448
|
? storedProfile.baseUrl
|
|
441
449
|
: env.ASSETHUB_API_BASE_URL ?? storedProfile?.baseUrl ?? defaultBaseUrl);
|
|
442
450
|
if (overrideKey)
|
|
443
|
-
return { apiKey: overrideKey, baseUrl, profile, source: 'flag' };
|
|
451
|
+
return { apiKey: overrideKey, baseUrl, profile, source: 'flag', workspaceId: getFlag(flags, 'workspace') };
|
|
444
452
|
if (selected && storedProfile?.apiKey) {
|
|
445
|
-
if (storedProfile.workspaceId &&
|
|
453
|
+
if ((storedProfile.workspaceId || storedProfile.apiKey.startsWith('ah_pat_')) &&
|
|
446
454
|
new URL(baseUrl).origin !== new URL(storedProfile.baseUrl).origin)
|
|
447
455
|
throw new Error('Selected workspace belongs to another API origin; select a workspace for this origin or supply an explicit API key');
|
|
448
|
-
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile' };
|
|
456
|
+
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile', workspaceId: getFlag(flags, 'workspace') ?? storedProfile.workspaceId };
|
|
449
457
|
}
|
|
450
458
|
if (env.ASSETHUB_API_KEY?.trim())
|
|
451
|
-
return { apiKey: env.ASSETHUB_API_KEY, baseUrl, profile, source: 'env' };
|
|
459
|
+
return { apiKey: env.ASSETHUB_API_KEY, baseUrl, profile, source: 'env', workspaceId: getFlag(flags, 'workspace') };
|
|
452
460
|
if (storedProfile?.apiKey)
|
|
453
|
-
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile' };
|
|
461
|
+
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile', workspaceId: getFlag(flags, 'workspace') ?? storedProfile.workspaceId };
|
|
454
462
|
throw new Error('Missing workspace API key. Use "workspace use <id>" after user login, "auth login --api-key-stdin", or ASSETHUB_API_KEY.');
|
|
455
463
|
};
|
|
456
464
|
const createContext = async (flags, args) => {
|
|
@@ -462,6 +470,7 @@ const createContext = async (flags, args) => {
|
|
|
462
470
|
client: createAssetHubClient({
|
|
463
471
|
apiKey: auth.apiKey,
|
|
464
472
|
baseUrl: auth.baseUrl,
|
|
473
|
+
workspaceId: auth.workspaceId,
|
|
465
474
|
}),
|
|
466
475
|
};
|
|
467
476
|
};
|
|
@@ -1241,12 +1250,25 @@ const commandModels = async (subcommand, positionals, ctx) => {
|
|
|
1241
1250
|
}
|
|
1242
1251
|
throw new Error('Unknown models command. Use "models list" or "models get".');
|
|
1243
1252
|
};
|
|
1244
|
-
const workspaceAuth = async (flags) => {
|
|
1253
|
+
const workspaceAuth = async (flags, allowPersonal = false) => {
|
|
1245
1254
|
const configPath = getConfigPath(flags);
|
|
1246
1255
|
const config = await readAuthConfig(configPath);
|
|
1247
1256
|
const explicitProfile = hasFlag(flags, 'profile') ? requireFlag(flags, 'profile') : undefined;
|
|
1248
1257
|
const profile = explicitProfile ?? config.defaultProfile ?? defaultProfileName;
|
|
1249
1258
|
const stored = config.profiles?.[profile];
|
|
1259
|
+
const preferredKey = getFlag(flags, 'api-key') ??
|
|
1260
|
+
(explicitProfile || stored?.workspaceId || stored?.apiKey?.startsWith('ah_pat_')
|
|
1261
|
+
? stored?.apiKey
|
|
1262
|
+
: env.ASSETHUB_API_KEY?.trim() || stored?.apiKey);
|
|
1263
|
+
if (allowPersonal && preferredKey?.startsWith('ah_pat_')) {
|
|
1264
|
+
const auth = await resolveAuth(flags);
|
|
1265
|
+
return {
|
|
1266
|
+
configPath, config, profile, accessToken: auth.apiKey,
|
|
1267
|
+
workspaceMfaToken: undefined, baseUrl: auth.baseUrl,
|
|
1268
|
+
selectedWorkspaceId: auth.workspaceId, personal: true,
|
|
1269
|
+
client: createWorkspaceClient({ accessToken: auth.apiKey, baseUrl: auth.baseUrl, workspaceId: auth.workspaceId }),
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1250
1272
|
const environmentToken = explicitProfile ? undefined : env.ASSETHUB_ACCESS_TOKEN?.trim();
|
|
1251
1273
|
const accessToken = environmentToken || stored?.accessToken;
|
|
1252
1274
|
if (!accessToken)
|
|
@@ -1270,6 +1292,7 @@ const workspaceAuth = async (flags) => {
|
|
|
1270
1292
|
workspaceMfaToken,
|
|
1271
1293
|
baseUrl,
|
|
1272
1294
|
selectedWorkspaceId: environmentToken ? undefined : stored?.workspaceId,
|
|
1295
|
+
personal: false,
|
|
1273
1296
|
client: createWorkspaceClient({ accessToken, workspaceMfaToken, baseUrl }),
|
|
1274
1297
|
};
|
|
1275
1298
|
};
|
|
@@ -1279,7 +1302,7 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
|
|
|
1279
1302
|
: hasFlag(flags, 'profile')
|
|
1280
1303
|
? undefined
|
|
1281
1304
|
: env.ASSETHUB_API_KEY?.trim();
|
|
1282
|
-
const auth = await workspaceAuth(flags);
|
|
1305
|
+
const auth = await workspaceAuth(flags, ['list', 'get', 'use'].includes(subcommand ?? ''));
|
|
1283
1306
|
if (['members', 'invite', 'set-role', 'remove-member'].includes(subcommand ?? '')) {
|
|
1284
1307
|
let workspaceId = getFlag(flags, 'workspace') ?? auth.selectedWorkspaceId;
|
|
1285
1308
|
if (!workspaceId)
|
|
@@ -1303,10 +1326,21 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
|
|
|
1303
1326
|
return;
|
|
1304
1327
|
}
|
|
1305
1328
|
if (subcommand === 'list') {
|
|
1306
|
-
print(await auth.client.list(
|
|
1329
|
+
print(await auth.client.list({
|
|
1330
|
+
query: getFlag(flags, 'query'),
|
|
1331
|
+
cursor: getFlag(flags, 'cursor'),
|
|
1332
|
+
limit: hasFlag(flags, 'limit') ? parsePositiveIntegerFlag(flags, 'limit', 100) : undefined,
|
|
1333
|
+
}));
|
|
1307
1334
|
return;
|
|
1308
1335
|
}
|
|
1309
1336
|
if (subcommand === 'get') {
|
|
1337
|
+
if (auth.personal && auth.selectedWorkspaceId) {
|
|
1338
|
+
const selected = await auth.client.select(auth.selectedWorkspaceId);
|
|
1339
|
+
if (!selected.workspace)
|
|
1340
|
+
throw new Error('Workspace API returned no selected workspace');
|
|
1341
|
+
print(selected.workspace);
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1310
1344
|
const account = await auth.client.list();
|
|
1311
1345
|
const selected = auth.selectedWorkspaceId;
|
|
1312
1346
|
const workspace = account.workspaces.find(item => selected ? item.id === selected : item.active);
|
|
@@ -1331,18 +1365,34 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
|
|
|
1331
1365
|
throw new Error('Use workspace list|get|create|use');
|
|
1332
1366
|
const workspaceId = requirePositional(positionals, 2, 'workspace-id');
|
|
1333
1367
|
const selected = await auth.client.select(workspaceId);
|
|
1368
|
+
if (selected.workspaceId !== workspaceId)
|
|
1369
|
+
throw new Error('Workspace API returned an invalid selection scope');
|
|
1334
1370
|
if (selected.mfa.status === 'setup_required' ||
|
|
1335
1371
|
selected.mfa.status === 'verify_required')
|
|
1336
1372
|
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');
|
|
1337
1373
|
const account = await auth.client.list();
|
|
1338
|
-
const workspace = account.workspaces.find(item => item.id === workspaceId);
|
|
1374
|
+
const workspace = selected.workspace ?? account.workspaces.find(item => item.id === workspaceId);
|
|
1339
1375
|
if (!workspace)
|
|
1340
1376
|
throw new Error('Workspace is not available to this authenticated user');
|
|
1341
1377
|
const profiles = auth.config.profiles ?? {};
|
|
1378
|
+
if (auth.personal) {
|
|
1379
|
+
if (account.authentication !== 'personal')
|
|
1380
|
+
throw new Error('Workspace API did not verify personal authentication');
|
|
1381
|
+
profiles[auth.profile] = {
|
|
1382
|
+
apiKey: auth.accessToken,
|
|
1383
|
+
userId: account.userId,
|
|
1384
|
+
workspaceId,
|
|
1385
|
+
baseUrl: auth.baseUrl,
|
|
1386
|
+
updatedAt: new Date().toISOString(),
|
|
1387
|
+
};
|
|
1388
|
+
await writeAuthConfig(auth.configPath, { ...auth.config, defaultProfile: auth.profile, profiles });
|
|
1389
|
+
print({ workspaceId, workspace, profile: auth.profile, selected: true, baseUrl: auth.baseUrl });
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1342
1392
|
const cached = Object.entries(profiles).find(([, entry]) => entry.userId === account.userId &&
|
|
1343
1393
|
entry.workspaceId === workspaceId &&
|
|
1344
1394
|
new URL(entry.baseUrl).origin === new URL(auth.baseUrl).origin &&
|
|
1345
|
-
entry.apiKey);
|
|
1395
|
+
entry.apiKey && !entry.apiKey.startsWith('ah_pat_'));
|
|
1346
1396
|
let apiKey;
|
|
1347
1397
|
for (const candidate of new Set([cached?.[1].apiKey, suppliedKey])) {
|
|
1348
1398
|
if (!candidate)
|
|
@@ -1445,7 +1495,13 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1445
1495
|
throw new Error('API key is empty. Pass --api-key, use --api-key-stdin, or set ASSETHUB_API_KEY.');
|
|
1446
1496
|
}
|
|
1447
1497
|
const baseUrl = getFlag(flags, 'base-url') ?? env.ASSETHUB_API_BASE_URL ?? defaultBaseUrl;
|
|
1448
|
-
|
|
1498
|
+
const personal = apiKey.startsWith('ah_pat_');
|
|
1499
|
+
const account = personal
|
|
1500
|
+
? await createWorkspaceClient({ accessToken: apiKey, baseUrl }).list()
|
|
1501
|
+
: undefined;
|
|
1502
|
+
if (personal && account?.authentication !== 'personal')
|
|
1503
|
+
throw new Error('Workspace API did not verify personal authentication');
|
|
1504
|
+
if (!personal && !hasFlag(flags, 'skip-verify')) {
|
|
1449
1505
|
const client = createAssetHubClient({ apiKey, baseUrl });
|
|
1450
1506
|
await client.v2.listModels();
|
|
1451
1507
|
}
|
|
@@ -1454,6 +1510,7 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1454
1510
|
profiles[profile] = {
|
|
1455
1511
|
apiKey,
|
|
1456
1512
|
baseUrl,
|
|
1513
|
+
...(account ? { userId: account.userId } : {}),
|
|
1457
1514
|
updatedAt: new Date().toISOString(),
|
|
1458
1515
|
};
|
|
1459
1516
|
await writeAuthConfig(configPath, {
|
|
@@ -1466,15 +1523,19 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1466
1523
|
profile,
|
|
1467
1524
|
baseUrl,
|
|
1468
1525
|
configPath,
|
|
1469
|
-
verified: !hasFlag(flags, 'skip-verify'),
|
|
1526
|
+
verified: personal || !hasFlag(flags, 'skip-verify'),
|
|
1527
|
+
...(account ? { authentication: 'personal', userId: account.userId } : {}),
|
|
1470
1528
|
});
|
|
1471
1529
|
return;
|
|
1472
1530
|
}
|
|
1473
1531
|
if (subcommand === 'status') {
|
|
1474
1532
|
const config = await readAuthConfig(configPath);
|
|
1475
1533
|
const current = config.profiles?.[getFlag(flags, 'profile') ?? config.defaultProfile ?? profile];
|
|
1476
|
-
if (
|
|
1477
|
-
|
|
1534
|
+
if (!hasFlag(flags, 'api-key') &&
|
|
1535
|
+
!current?.apiKey?.startsWith('ah_pat_') &&
|
|
1536
|
+
(hasFlag(flags, 'profile') || !env.ASSETHUB_API_KEY?.startsWith('ah_pat_')) &&
|
|
1537
|
+
((!hasFlag(flags, 'profile') && env.ASSETHUB_ACCESS_TOKEN?.trim()) ||
|
|
1538
|
+
(current?.accessToken && !current.apiKey))) {
|
|
1478
1539
|
const auth = await workspaceAuth(flags);
|
|
1479
1540
|
const account = await auth.client.list();
|
|
1480
1541
|
print({
|
|
@@ -1491,7 +1552,18 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1491
1552
|
const client = createAssetHubClient({
|
|
1492
1553
|
apiKey: auth.apiKey,
|
|
1493
1554
|
baseUrl: auth.baseUrl,
|
|
1555
|
+
workspaceId: auth.workspaceId,
|
|
1494
1556
|
});
|
|
1557
|
+
if (auth.apiKey.startsWith('ah_pat_')) {
|
|
1558
|
+
const account = await createWorkspaceClient({ accessToken: auth.apiKey, baseUrl: auth.baseUrl, workspaceId: auth.workspaceId }).list();
|
|
1559
|
+
if (account.authentication !== 'personal')
|
|
1560
|
+
throw new Error('Workspace API did not verify personal authentication');
|
|
1561
|
+
if (auth.workspaceId)
|
|
1562
|
+
await client.v2.getCapabilities();
|
|
1563
|
+
print({ authenticated: true, authentication: 'personal', userId: account.userId, workspaceId: auth.workspaceId,
|
|
1564
|
+
profile: auth.profile, source: auth.source, baseUrl: auth.baseUrl });
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1495
1567
|
const models = await client.v2.listModels();
|
|
1496
1568
|
print({
|
|
1497
1569
|
authenticated: true,
|
|
@@ -1637,6 +1709,134 @@ const printRecorded = async (ctx, queued) => {
|
|
|
1637
1709
|
});
|
|
1638
1710
|
process.exitCode = executionExitCode(execution);
|
|
1639
1711
|
};
|
|
1712
|
+
const canvasNodeFlag = (flags) => {
|
|
1713
|
+
if (!hasFlag(flags, 'node'))
|
|
1714
|
+
return undefined;
|
|
1715
|
+
const nodeId = requireFlag(flags, 'node');
|
|
1716
|
+
if (!nodeId.startsWith('shape:') || nodeId.length <= 6)
|
|
1717
|
+
throw new Error('--node must be a native shape:<id> from canvas nodes');
|
|
1718
|
+
if (sourceInputCount(flags) ||
|
|
1719
|
+
[
|
|
1720
|
+
'input-json',
|
|
1721
|
+
'upload-id',
|
|
1722
|
+
'from-node',
|
|
1723
|
+
'graph-revision',
|
|
1724
|
+
'from-run',
|
|
1725
|
+
'part',
|
|
1726
|
+
'reference',
|
|
1727
|
+
'transforms-json',
|
|
1728
|
+
].some(name => hasFlag(flags, name)))
|
|
1729
|
+
throw new Error('--node cannot be combined with explicit sources, parts, reference, input JSON, or another node/run selector');
|
|
1730
|
+
return nodeId;
|
|
1731
|
+
};
|
|
1732
|
+
const printNodeMeshBatch = async (ctx, batch) => {
|
|
1733
|
+
const executions = [...batch.executions];
|
|
1734
|
+
if (hasFlag(ctx.flags, 'wait')) {
|
|
1735
|
+
try {
|
|
1736
|
+
for (let i = 0; i < executions.length; i++)
|
|
1737
|
+
executions[i] = await waitForExecution(ctx.client, executions[i].runId, watchOptions(ctx.flags));
|
|
1738
|
+
}
|
|
1739
|
+
catch (error) {
|
|
1740
|
+
if (!(error instanceof CliExecutionError))
|
|
1741
|
+
throw error;
|
|
1742
|
+
throw new CliExecutionError(error.message, error.exitCode, batch.operationId, error.execution, error.runId);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
const downloads = await maybeDownloadUrls(ctx.flags, executions.flatMap(execution => execution.outputs));
|
|
1746
|
+
print({ ...batch, executions, ...(downloads ? { downloads } : {}) });
|
|
1747
|
+
process.exitCode = Math.max(0, ...executions.map(executionExitCode));
|
|
1748
|
+
};
|
|
1749
|
+
const commandNodeMesh = async (ctx, nodeId) => {
|
|
1750
|
+
const body = {};
|
|
1751
|
+
const modelId = getFlag(ctx.flags, 'model-id');
|
|
1752
|
+
if (modelId)
|
|
1753
|
+
body.modelId = modelId;
|
|
1754
|
+
const name = getFlag(ctx.flags, 'name');
|
|
1755
|
+
if (name)
|
|
1756
|
+
body.name = name;
|
|
1757
|
+
if (hasFlag(ctx.flags, 'face-limit'))
|
|
1758
|
+
body.faceLimit = parsePositiveIntegerFlag(ctx.flags, 'face-limit', 1);
|
|
1759
|
+
if (hasFlag(ctx.flags, 'low-poly'))
|
|
1760
|
+
body.isLowPoly = parseBooleanFlag(ctx.flags, 'low-poly');
|
|
1761
|
+
if (hasFlag(ctx.flags, 'params-json')) {
|
|
1762
|
+
const params = await readJsonArgument(requireFlag(ctx.flags, 'params-json'), '--params-json');
|
|
1763
|
+
if (Object.values(params).some(value => typeof value !== 'string' &&
|
|
1764
|
+
typeof value !== 'boolean' &&
|
|
1765
|
+
!(typeof value === 'number' && Number.isFinite(value))))
|
|
1766
|
+
throw new Error('--params-json values must be strings, booleans, or finite numbers');
|
|
1767
|
+
body.params = params;
|
|
1768
|
+
}
|
|
1769
|
+
const operationId = getFlag(ctx.flags, 'operation-id');
|
|
1770
|
+
const canvasId = selectedCanvasId(ctx.flags);
|
|
1771
|
+
const resumeOptions = {
|
|
1772
|
+
...stateOptions(ctx),
|
|
1773
|
+
expectedCanvasId: canvasId,
|
|
1774
|
+
expectedCanvasNodeId: nodeId,
|
|
1775
|
+
expectedBody: body,
|
|
1776
|
+
};
|
|
1777
|
+
if (operationId) {
|
|
1778
|
+
const batch = await resumeNodeMeshBatch({ ...resumeOptions, operationId });
|
|
1779
|
+
if (batch) {
|
|
1780
|
+
await printNodeMeshBatch(ctx, batch);
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
if (await hasRecordedOperation(resumeOptions.stateDir, operationId)) {
|
|
1784
|
+
await printRecorded(ctx, await resumeRecorded({
|
|
1785
|
+
...resumeOptions,
|
|
1786
|
+
operationId,
|
|
1787
|
+
expectedOperation: 'mesh.generate',
|
|
1788
|
+
}));
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
const session = await openExecutionSession({
|
|
1793
|
+
...stateOptions(ctx),
|
|
1794
|
+
canvasId,
|
|
1795
|
+
operation: 'mesh.generate',
|
|
1796
|
+
create: false,
|
|
1797
|
+
});
|
|
1798
|
+
const { items } = await ctx.client.v2.listCanvasNodes(session.canvas.id);
|
|
1799
|
+
const target = items.find(item => item.nodeId === nodeId);
|
|
1800
|
+
if (!target)
|
|
1801
|
+
throw new Error('Canvas node is unavailable; read canvas nodes again');
|
|
1802
|
+
const finishedOrBusy = (status) => ['complete', 'completed', 'generating'].includes(status ?? '');
|
|
1803
|
+
if (target.type === 'production-3d') {
|
|
1804
|
+
if (!target.actions.includes('mesh.generate'))
|
|
1805
|
+
throw new Error('Mesh generation is unavailable for this node');
|
|
1806
|
+
const children = items.filter(item => item.sourceNodeId === nodeId &&
|
|
1807
|
+
['mesh-gen', 'preview-asset', 'part-group'].includes(item.type));
|
|
1808
|
+
if (!children.length)
|
|
1809
|
+
throw new Error('Production node has no saved part nodes to generate');
|
|
1810
|
+
const pending = children.filter(item => item.actions.includes('mesh.generate') &&
|
|
1811
|
+
!finishedOrBusy(item.meshStatus));
|
|
1812
|
+
const batchId = operationId ?? randomUUID();
|
|
1813
|
+
stderr.write(`[nodes] batch ${batchId}\n`);
|
|
1814
|
+
await printNodeMeshBatch(ctx, await executeNodeMeshBatch(session, {
|
|
1815
|
+
...executionOptions(ctx.flags),
|
|
1816
|
+
operationId: batchId,
|
|
1817
|
+
nodeId,
|
|
1818
|
+
body,
|
|
1819
|
+
children: pending.map(item => item.nodeId),
|
|
1820
|
+
skipped: children
|
|
1821
|
+
.filter(item => !pending.includes(item))
|
|
1822
|
+
.map(({ nodeId, meshStatus }) => ({ nodeId, meshStatus })),
|
|
1823
|
+
}));
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
if (!['mesh-gen', 'preview-asset', 'part-group'].includes(target.type))
|
|
1827
|
+
throw new Error('This canvas node cannot generate a mesh');
|
|
1828
|
+
if (target.meshStatus === 'generating' ||
|
|
1829
|
+
(target.type !== 'mesh-gen' && finishedOrBusy(target.meshStatus))) {
|
|
1830
|
+
print({ nodeId, skipped: true, meshStatus: target.meshStatus });
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
if (!target.actions.includes('mesh.generate'))
|
|
1834
|
+
throw new Error('Mesh generation is unavailable for this node');
|
|
1835
|
+
await printRecorded(ctx, await executeRecorded(session, 'mesh.generate', body, {
|
|
1836
|
+
...executionOptions(ctx.flags),
|
|
1837
|
+
canvasNode: { nodeId },
|
|
1838
|
+
}));
|
|
1839
|
+
};
|
|
1640
1840
|
const commandImage = async (subcommand, ctx) => {
|
|
1641
1841
|
if (subcommand !== 'generate')
|
|
1642
1842
|
throw new Error('Unknown image command. Use "image generate".');
|
|
@@ -1744,6 +1944,54 @@ const refinementModeInfo = (capabilities, mode) => {
|
|
|
1744
1944
|
throw new Error(`Refinement mode ${mode} is unavailable${info.reason ? `: ${info.reason}` : ''}`);
|
|
1745
1945
|
return info;
|
|
1746
1946
|
};
|
|
1947
|
+
/** Reuse the frozen sources and latest editable scene, never replay server-only receipt fields. */
|
|
1948
|
+
const composerInputFromRun = (previous, operation) => {
|
|
1949
|
+
const source = previous.resolvedInput ?? previous.input ?? previous.requestedInput ?? {};
|
|
1950
|
+
const fields = operation === 'mesh.compose'
|
|
1951
|
+
? [
|
|
1952
|
+
'fullBodyImageAssetId',
|
|
1953
|
+
'agentVersion',
|
|
1954
|
+
'agentRuntime',
|
|
1955
|
+
'mode',
|
|
1956
|
+
'targetCharacterHeightM',
|
|
1957
|
+
'projectName',
|
|
1958
|
+
'decimateRatio',
|
|
1959
|
+
'quickRunId',
|
|
1960
|
+
'quickScene',
|
|
1961
|
+
]
|
|
1962
|
+
: ['fullBodyImageAssetId', 'agentRuntime', 'mode', 'instruction', 'maxRounds'];
|
|
1963
|
+
const input = Object.fromEntries(fields
|
|
1964
|
+
.filter(key => source[key] !== undefined)
|
|
1965
|
+
.map(key => [key, source[key]]));
|
|
1966
|
+
const parts = previous.composition?.parts ?? source.parts;
|
|
1967
|
+
const partFields = operation === 'mesh.compose'
|
|
1968
|
+
? ['assetId', 'name', 'canonicalKey', 'partImageAssetId', 'partTaskId']
|
|
1969
|
+
: [
|
|
1970
|
+
'assetId',
|
|
1971
|
+
'name',
|
|
1972
|
+
'canonicalKey',
|
|
1973
|
+
'volumeCentroid',
|
|
1974
|
+
'sourceAssetId',
|
|
1975
|
+
'transform',
|
|
1976
|
+
];
|
|
1977
|
+
input.parts = Array.isArray(parts)
|
|
1978
|
+
? parts.map(part => part && typeof part === 'object'
|
|
1979
|
+
? Object.fromEntries(Object.entries(part).filter(([key]) => partFields.includes(key)))
|
|
1980
|
+
: part)
|
|
1981
|
+
: parts;
|
|
1982
|
+
const transforms = previous.composition?.transforms ?? source.transforms;
|
|
1983
|
+
if (transforms != null)
|
|
1984
|
+
input.transforms = transforms;
|
|
1985
|
+
if (operation === 'mesh.refine') {
|
|
1986
|
+
const referenceTransform = previous.composition?.referenceTransform ?? source.referenceTransform;
|
|
1987
|
+
if (referenceTransform != null)
|
|
1988
|
+
input.referenceTransform = referenceTransform;
|
|
1989
|
+
// Compose modes are not refinement modes; preserve a prior refinement's mode only.
|
|
1990
|
+
if (previous.operation !== 'mesh.refine')
|
|
1991
|
+
delete input.mode;
|
|
1992
|
+
}
|
|
1993
|
+
return input;
|
|
1994
|
+
};
|
|
1747
1995
|
const commandComposerRefine = async (ctx) => {
|
|
1748
1996
|
const capabilities = await ctx.client.v2.getMeshRefinementModes();
|
|
1749
1997
|
if (hasFlag(ctx.flags, 'list-modes')) {
|
|
@@ -1767,20 +2015,7 @@ const commandComposerRefine = async (ctx) => {
|
|
|
1767
2015
|
const input = json
|
|
1768
2016
|
? await readJsonArgument(json, '--input-json')
|
|
1769
2017
|
: previous
|
|
1770
|
-
?
|
|
1771
|
-
...(previous.requestedInput ?? previous.input),
|
|
1772
|
-
...(previous.operation === 'mesh.refine' &&
|
|
1773
|
-
(previous.requestedInput?.mode ?? previous.input.mode)
|
|
1774
|
-
? { mode: previous.requestedInput?.mode ?? previous.input.mode }
|
|
1775
|
-
: { mode: undefined }),
|
|
1776
|
-
parts: previous.composition?.parts ??
|
|
1777
|
-
previous.requestedInput?.parts ??
|
|
1778
|
-
previous.input.parts,
|
|
1779
|
-
transforms: previous.composition?.transforms,
|
|
1780
|
-
referenceTransform: previous.composition?.referenceTransform ??
|
|
1781
|
-
previous.requestedInput?.referenceTransform ??
|
|
1782
|
-
previous.input.referenceTransform,
|
|
1783
|
-
}
|
|
2018
|
+
? composerInputFromRun(previous, 'mesh.refine')
|
|
1784
2019
|
: {
|
|
1785
2020
|
parts: parts.map(assetId => ({
|
|
1786
2021
|
assetId: assertFlagHasValue(assetId, '--part <asset-id>'),
|
|
@@ -1848,6 +2083,9 @@ const commandComposerRefine = async (ctx) => {
|
|
|
1848
2083
|
mode,
|
|
1849
2084
|
instruction,
|
|
1850
2085
|
...(maxRounds != null ? { maxRounds } : {}),
|
|
2086
|
+
...(input.agentRuntime
|
|
2087
|
+
? { agentRuntime: input.agentRuntime }
|
|
2088
|
+
: {}),
|
|
1851
2089
|
};
|
|
1852
2090
|
await printRecorded(ctx, await executeRecorded(session, 'mesh.refine', body, {
|
|
1853
2091
|
...executionOptions(ctx.flags),
|
|
@@ -1865,6 +2103,7 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
1865
2103
|
}
|
|
1866
2104
|
if (subcommand !== 'run')
|
|
1867
2105
|
throw new Error('Use composer models|run');
|
|
2106
|
+
const nodeId = canvasNodeFlag(ctx.flags);
|
|
1868
2107
|
const fromRun = getFlag(ctx.flags, 'from-run');
|
|
1869
2108
|
const json = getFlag(ctx.flags, 'input-json');
|
|
1870
2109
|
const parts = getFlagValues(ctx.flags, 'part');
|
|
@@ -1874,20 +2113,23 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
1874
2113
|
const previous = fromRun ? await ctx.client.v2.getRun(fromRun) : undefined;
|
|
1875
2114
|
if (previous && previous.operation !== 'mesh.compose')
|
|
1876
2115
|
throw new Error('--from-run must identify a mesh.compose execution');
|
|
1877
|
-
const input =
|
|
1878
|
-
? {
|
|
1879
|
-
:
|
|
1880
|
-
?
|
|
1881
|
-
:
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2116
|
+
const input = nodeId
|
|
2117
|
+
? {}
|
|
2118
|
+
: previous
|
|
2119
|
+
? composerInputFromRun(previous, 'mesh.compose')
|
|
2120
|
+
: json
|
|
2121
|
+
? await readJsonArgument(json, '--input-json')
|
|
2122
|
+
: {
|
|
2123
|
+
parts: parts.map(assetId => ({
|
|
2124
|
+
assetId: assertFlagHasValue(assetId, '--part <asset-id>'),
|
|
2125
|
+
})),
|
|
2126
|
+
fullBodyImageAssetId: requireFlag(ctx.flags, 'reference'),
|
|
2127
|
+
};
|
|
1887
2128
|
delete input.executionContext;
|
|
1888
|
-
if (!
|
|
1889
|
-
!input.parts
|
|
1890
|
-
|
|
2129
|
+
if (!nodeId &&
|
|
2130
|
+
(!Array.isArray(input.parts) ||
|
|
2131
|
+
!input.parts.length ||
|
|
2132
|
+
!input.fullBodyImageAssetId))
|
|
1891
2133
|
throw new Error('Composer requires parts[] and fullBodyImageAssetId');
|
|
1892
2134
|
const mode = getFlag(ctx.flags, 'mode');
|
|
1893
2135
|
if (mode != null && mode !== 'quick' && mode !== 'quality')
|
|
@@ -1903,17 +2145,48 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
1903
2145
|
const transforms = getFlag(ctx.flags, 'transforms-json');
|
|
1904
2146
|
if (transforms)
|
|
1905
2147
|
input.transforms = await readJsonArgument(transforms, '--transforms-json');
|
|
2148
|
+
if (previous && input.transforms) {
|
|
2149
|
+
delete input.quickRunId;
|
|
2150
|
+
delete input.quickScene;
|
|
2151
|
+
}
|
|
1906
2152
|
const canvasId = selectedCanvasId(ctx.flags) ?? previous?.canvas.id;
|
|
1907
2153
|
if (previous && canvasId !== previous.canvas.id)
|
|
1908
2154
|
throw new Error('Recomposition must use the original canvas to retain lineage');
|
|
2155
|
+
const operationId = getFlag(ctx.flags, 'operation-id');
|
|
2156
|
+
if (nodeId && operationId) {
|
|
2157
|
+
const resumeOptions = {
|
|
2158
|
+
...stateOptions(ctx),
|
|
2159
|
+
operationId,
|
|
2160
|
+
expectedCanvasId: canvasId,
|
|
2161
|
+
expectedCanvasNodeId: nodeId,
|
|
2162
|
+
expectedOperation: 'mesh.compose',
|
|
2163
|
+
expectedBody: input,
|
|
2164
|
+
};
|
|
2165
|
+
// A mesh batch reserves its root key even though its paid requests have child keys.
|
|
2166
|
+
await resumeNodeMeshBatch(resumeOptions);
|
|
2167
|
+
if (await hasRecordedOperation(resumeOptions.stateDir, operationId)) {
|
|
2168
|
+
await printRecorded(ctx, await resumeRecorded(resumeOptions));
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
1909
2172
|
const session = await openExecutionSession({
|
|
1910
2173
|
...stateOptions(ctx),
|
|
1911
2174
|
canvasId,
|
|
1912
2175
|
operation: 'mesh.compose',
|
|
2176
|
+
...(nodeId ? { create: false } : {}),
|
|
1913
2177
|
});
|
|
2178
|
+
if (nodeId) {
|
|
2179
|
+
const { items } = await ctx.client.v2.listCanvasNodes(session.canvas.id);
|
|
2180
|
+
const target = items.find(item => item.nodeId === nodeId);
|
|
2181
|
+
if (!target ||
|
|
2182
|
+
!['production-3d', 'part-composer'].includes(target.type) ||
|
|
2183
|
+
!target.actions.includes('mesh.compose'))
|
|
2184
|
+
throw new Error('Composition is unavailable for this canvas node');
|
|
2185
|
+
}
|
|
1914
2186
|
const queued = await executeRecorded(session, 'mesh.compose', input, {
|
|
1915
2187
|
...executionOptions(ctx.flags),
|
|
1916
2188
|
...(previous ? { parentRunId: previous.runId } : {}),
|
|
2189
|
+
...(nodeId ? { canvasNode: { nodeId } } : {}),
|
|
1917
2190
|
});
|
|
1918
2191
|
await printRecorded(ctx, queued);
|
|
1919
2192
|
};
|
|
@@ -1937,6 +2210,11 @@ const commandMesh = async (subcommand, positionals, ctx) => {
|
|
|
1937
2210
|
return;
|
|
1938
2211
|
}
|
|
1939
2212
|
if (subcommand === 'generate') {
|
|
2213
|
+
const nodeId = canvasNodeFlag(ctx.flags);
|
|
2214
|
+
if (nodeId) {
|
|
2215
|
+
await commandNodeMesh(ctx, nodeId);
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
1940
2218
|
const inputJson = getFlag(ctx.flags, 'input-json');
|
|
1941
2219
|
const input = inputJson
|
|
1942
2220
|
? await readJsonArgument(inputJson, '--input-json')
|
|
@@ -2567,12 +2845,6 @@ const productionSession = (ctx, operation) => openExecutionSession({
|
|
|
2567
2845
|
canvasId: selectedCanvasId(ctx.flags),
|
|
2568
2846
|
operation,
|
|
2569
2847
|
});
|
|
2570
|
-
const childOperationId = (parent, step) => {
|
|
2571
|
-
const hash = createHash('sha256')
|
|
2572
|
-
.update(JSON.stringify([parent, step]))
|
|
2573
|
-
.digest('hex');
|
|
2574
|
-
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-8${hash.slice(17, 20)}-${hash.slice(20, 32)}`;
|
|
2575
|
-
};
|
|
2576
2848
|
const commandProduction = async (subcommand, positionals, ctx) => {
|
|
2577
2849
|
if (subcommand === 'agents') {
|
|
2578
2850
|
print(sanitizeProductionOutput(await ctx.client.v1.getProductionAgents()));
|
|
@@ -2812,7 +3084,7 @@ const commandParts = async (subcommand, ctx) => {
|
|
|
2812
3084
|
...stateOptions(ctx),
|
|
2813
3085
|
operationId: executeOperationId,
|
|
2814
3086
|
expectedCanvasId: session.canvas.id,
|
|
2815
|
-
|
|
3087
|
+
expectedBodySubset: {
|
|
2816
3088
|
partExtractionMode: getFlag(ctx.flags, 'part-extraction-mode'),
|
|
2817
3089
|
...(getFlagValues(ctx.flags, 'task-id').length
|
|
2818
3090
|
? { confirmedTaskIds: getFlagValues(ctx.flags, 'task-id') }
|
|
@@ -3023,6 +3295,28 @@ const canvasForRead = (ctx) => openExecutionSession({
|
|
|
3023
3295
|
create: false,
|
|
3024
3296
|
});
|
|
3025
3297
|
const commandCanvas = async (subcommand, positionals, ctx) => {
|
|
3298
|
+
if (subcommand === 'nodes') {
|
|
3299
|
+
const { canvas } = await canvasForRead(ctx);
|
|
3300
|
+
print(await ctx.client.v2.listCanvasNodes(canvas.id));
|
|
3301
|
+
return;
|
|
3302
|
+
}
|
|
3303
|
+
if (subcommand === 'download') {
|
|
3304
|
+
const outDir = requireFlag(ctx.flags, 'out-dir');
|
|
3305
|
+
const { canvas } = await canvasForRead(ctx);
|
|
3306
|
+
const mesh = getFlag(ctx.flags, 'mesh');
|
|
3307
|
+
if (mesh && !/^mesh_[1-9]\d*$/.test(mesh))
|
|
3308
|
+
throw new Error('--mesh must be a mesh_<id> asset ID');
|
|
3309
|
+
const result = await downloadCanvas({
|
|
3310
|
+
client: ctx.client,
|
|
3311
|
+
canvasId: canvas.id,
|
|
3312
|
+
mesh,
|
|
3313
|
+
outDir,
|
|
3314
|
+
progress: message => process.stderr.write(`${message}\n`),
|
|
3315
|
+
});
|
|
3316
|
+
print(result);
|
|
3317
|
+
process.exitCode = result.exitCode;
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3026
3320
|
if (subcommand === 'context' ||
|
|
3027
3321
|
subcommand === 'import' ||
|
|
3028
3322
|
subcommand === 'compare' ||
|
|
@@ -3338,6 +3632,14 @@ const commandRuns = async (subcommand, positionals, ctx) => {
|
|
|
3338
3632
|
return;
|
|
3339
3633
|
}
|
|
3340
3634
|
if (subcommand === 'resume') {
|
|
3635
|
+
const batch = await resumeNodeMeshBatch({
|
|
3636
|
+
...stateOptions(ctx),
|
|
3637
|
+
operationId: requirePositional(positionals, 2, 'operation-id'),
|
|
3638
|
+
});
|
|
3639
|
+
if (batch) {
|
|
3640
|
+
await printNodeMeshBatch(ctx, batch);
|
|
3641
|
+
return;
|
|
3642
|
+
}
|
|
3341
3643
|
const result = await resumeRecorded({
|
|
3342
3644
|
...stateOptions(ctx),
|
|
3343
3645
|
operationId: requirePositional(positionals, 2, 'operation-id'),
|
|
@@ -3370,6 +3672,7 @@ const commandRuns = async (subcommand, positionals, ctx) => {
|
|
|
3370
3672
|
plan,
|
|
3371
3673
|
baseUrl: ctx.auth.baseUrl,
|
|
3372
3674
|
apiKey: ctx.auth.apiKey,
|
|
3675
|
+
workspaceId: ctx.auth.workspaceId,
|
|
3373
3676
|
skipRegister: args.skipRegister,
|
|
3374
3677
|
onProgress: event => {
|
|
3375
3678
|
if (event.kind === 'register') {
|
|
@@ -3490,7 +3793,17 @@ const run = async () => {
|
|
|
3490
3793
|
if (command === 'mcp') {
|
|
3491
3794
|
if (subcommand !== 'config')
|
|
3492
3795
|
throw new Error('Use mcp tools [tool-name] or mcp config --client cursor|codex');
|
|
3493
|
-
|
|
3796
|
+
const config = await readAuthConfig(getConfigPath(parsed.flags));
|
|
3797
|
+
const explicitProfile = getFlag(parsed.flags, 'profile');
|
|
3798
|
+
const stored = config.profiles?.[explicitProfile ?? config.defaultProfile ?? defaultProfileName];
|
|
3799
|
+
if (explicitProfile && !stored)
|
|
3800
|
+
throw new Error('Selected profile does not exist');
|
|
3801
|
+
const selected = explicitProfile || stored?.workspaceId || stored?.apiKey?.startsWith('ah_pat_');
|
|
3802
|
+
const baseUrl = getFlag(parsed.flags, 'base-url') ??
|
|
3803
|
+
(selected ? stored?.baseUrl : env.ASSETHUB_API_BASE_URL ?? stored?.baseUrl) ?? defaultBaseUrl;
|
|
3804
|
+
const workspaceId = getFlag(parsed.flags, 'workspace') ??
|
|
3805
|
+
(stored && new URL(baseUrl).origin === new URL(stored.baseUrl).origin ? stored.workspaceId : undefined);
|
|
3806
|
+
stdout.write(mcpConfig(requireFlag(parsed.flags, 'client'), baseUrl, hasFlag(parsed.flags, 'account'), workspaceId));
|
|
3494
3807
|
return;
|
|
3495
3808
|
}
|
|
3496
3809
|
if (command === 'doctor') {
|