@assethub/cli 0.1.18 → 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 +66 -6
- 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 +346 -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';
|
|
@@ -91,7 +92,8 @@ Usage:
|
|
|
91
92
|
assethub auth login --access-token-stdin [--base-url <url>] [--profile <name>]
|
|
92
93
|
assethub auth status [--profile <name>]
|
|
93
94
|
assethub auth logout [--profile <name>]
|
|
94
|
-
assethub workspace list
|
|
95
|
+
assethub workspace list [--query <text>] [--limit <1-100>] [--cursor <cursor>]
|
|
96
|
+
assethub workspace get
|
|
95
97
|
assethub workspace create --name <name> [--operation-id <uuid>]
|
|
96
98
|
assethub workspace use <workspace-id>
|
|
97
99
|
assethub workspace members [--workspace <id>]
|
|
@@ -105,6 +107,7 @@ Usage:
|
|
|
105
107
|
assethub project ingest <source-dir> --out-dir <directory> [--exclude-dir <relative-path>...]
|
|
106
108
|
assethub language text|vision --input-json @<json> --operation-id <uuid> [--out <json>]
|
|
107
109
|
assethub canvas download --canvas <id> --out-dir <directory> [--mesh <mesh_asset_id>]
|
|
110
|
+
assethub canvas nodes --canvas <id>
|
|
108
111
|
assethub canvas import --canvas <id> --file <image> [--name <name>]
|
|
109
112
|
assethub canvas context get --canvas <id> [--out <json>]
|
|
110
113
|
assethub canvas context put --canvas <id> --file <json> [--if-version <n>]
|
|
@@ -127,6 +130,7 @@ Usage:
|
|
|
127
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]
|
|
128
131
|
assethub composer run --input-json <json|@file> [--canvas <id>] [--operation-id <uuid>] [--wait] [--download --out-dir <dir>]
|
|
129
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]
|
|
130
134
|
assethub canvas create [--name <name>] [--operation-id <uuid>]
|
|
131
135
|
assethub canvas list [--cursor <cursor>] [--limit <n>]
|
|
132
136
|
assethub canvas get|use|open [<id>]
|
|
@@ -142,6 +146,7 @@ Usage:
|
|
|
142
146
|
assethub evaluations get <evaluation-id>
|
|
143
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]
|
|
144
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]
|
|
145
150
|
assethub mesh list [--query <text>] [--cursor <cursor>] [--limit <n>]
|
|
146
151
|
assethub mesh get <mesh-asset-id>
|
|
147
152
|
assethub mesh download <mesh-asset-id> --out-dir <directory>
|
|
@@ -173,6 +178,7 @@ Global options:
|
|
|
173
178
|
--api-key <key> Overrides saved auth and ASSETHUB_API_KEY.
|
|
174
179
|
--base-url <url> Override the selected API origin. Default: ${defaultBaseUrl}.
|
|
175
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.
|
|
176
182
|
--config <path> Auth config path. Defaults to ~/.assethub/config.json.
|
|
177
183
|
--version Show installed package version.
|
|
178
184
|
--help Show help.
|
|
@@ -434,7 +440,7 @@ const resolveAuth = async (flags) => {
|
|
|
434
440
|
: undefined;
|
|
435
441
|
const profile = explicitProfile ?? config?.defaultProfile ?? defaultProfileName;
|
|
436
442
|
const storedProfile = config?.profiles?.[profile];
|
|
437
|
-
const selected = Boolean(explicitProfile || storedProfile?.workspaceId);
|
|
443
|
+
const selected = Boolean(explicitProfile || storedProfile?.workspaceId || storedProfile?.apiKey?.startsWith('ah_pat_'));
|
|
438
444
|
if (selected && (!storedProfile || (!storedProfile.apiKey && !overrideKey)))
|
|
439
445
|
throw new Error('Selected profile has no workspace API key. Use workspace use <id> or auth login --api-key-stdin --profile <name>.');
|
|
440
446
|
const baseUrl = getFlag(flags, 'base-url') ??
|
|
@@ -442,17 +448,17 @@ const resolveAuth = async (flags) => {
|
|
|
442
448
|
? storedProfile.baseUrl
|
|
443
449
|
: env.ASSETHUB_API_BASE_URL ?? storedProfile?.baseUrl ?? defaultBaseUrl);
|
|
444
450
|
if (overrideKey)
|
|
445
|
-
return { apiKey: overrideKey, baseUrl, profile, source: 'flag' };
|
|
451
|
+
return { apiKey: overrideKey, baseUrl, profile, source: 'flag', workspaceId: getFlag(flags, 'workspace') };
|
|
446
452
|
if (selected && storedProfile?.apiKey) {
|
|
447
|
-
if (storedProfile.workspaceId &&
|
|
453
|
+
if ((storedProfile.workspaceId || storedProfile.apiKey.startsWith('ah_pat_')) &&
|
|
448
454
|
new URL(baseUrl).origin !== new URL(storedProfile.baseUrl).origin)
|
|
449
455
|
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' };
|
|
456
|
+
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile', workspaceId: getFlag(flags, 'workspace') ?? storedProfile.workspaceId };
|
|
451
457
|
}
|
|
452
458
|
if (env.ASSETHUB_API_KEY?.trim())
|
|
453
|
-
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') };
|
|
454
460
|
if (storedProfile?.apiKey)
|
|
455
|
-
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile' };
|
|
461
|
+
return { apiKey: storedProfile.apiKey, baseUrl, profile, source: 'profile', workspaceId: getFlag(flags, 'workspace') ?? storedProfile.workspaceId };
|
|
456
462
|
throw new Error('Missing workspace API key. Use "workspace use <id>" after user login, "auth login --api-key-stdin", or ASSETHUB_API_KEY.');
|
|
457
463
|
};
|
|
458
464
|
const createContext = async (flags, args) => {
|
|
@@ -464,6 +470,7 @@ const createContext = async (flags, args) => {
|
|
|
464
470
|
client: createAssetHubClient({
|
|
465
471
|
apiKey: auth.apiKey,
|
|
466
472
|
baseUrl: auth.baseUrl,
|
|
473
|
+
workspaceId: auth.workspaceId,
|
|
467
474
|
}),
|
|
468
475
|
};
|
|
469
476
|
};
|
|
@@ -1243,12 +1250,25 @@ const commandModels = async (subcommand, positionals, ctx) => {
|
|
|
1243
1250
|
}
|
|
1244
1251
|
throw new Error('Unknown models command. Use "models list" or "models get".');
|
|
1245
1252
|
};
|
|
1246
|
-
const workspaceAuth = async (flags) => {
|
|
1253
|
+
const workspaceAuth = async (flags, allowPersonal = false) => {
|
|
1247
1254
|
const configPath = getConfigPath(flags);
|
|
1248
1255
|
const config = await readAuthConfig(configPath);
|
|
1249
1256
|
const explicitProfile = hasFlag(flags, 'profile') ? requireFlag(flags, 'profile') : undefined;
|
|
1250
1257
|
const profile = explicitProfile ?? config.defaultProfile ?? defaultProfileName;
|
|
1251
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
|
+
}
|
|
1252
1272
|
const environmentToken = explicitProfile ? undefined : env.ASSETHUB_ACCESS_TOKEN?.trim();
|
|
1253
1273
|
const accessToken = environmentToken || stored?.accessToken;
|
|
1254
1274
|
if (!accessToken)
|
|
@@ -1272,6 +1292,7 @@ const workspaceAuth = async (flags) => {
|
|
|
1272
1292
|
workspaceMfaToken,
|
|
1273
1293
|
baseUrl,
|
|
1274
1294
|
selectedWorkspaceId: environmentToken ? undefined : stored?.workspaceId,
|
|
1295
|
+
personal: false,
|
|
1275
1296
|
client: createWorkspaceClient({ accessToken, workspaceMfaToken, baseUrl }),
|
|
1276
1297
|
};
|
|
1277
1298
|
};
|
|
@@ -1281,7 +1302,7 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
|
|
|
1281
1302
|
: hasFlag(flags, 'profile')
|
|
1282
1303
|
? undefined
|
|
1283
1304
|
: env.ASSETHUB_API_KEY?.trim();
|
|
1284
|
-
const auth = await workspaceAuth(flags);
|
|
1305
|
+
const auth = await workspaceAuth(flags, ['list', 'get', 'use'].includes(subcommand ?? ''));
|
|
1285
1306
|
if (['members', 'invite', 'set-role', 'remove-member'].includes(subcommand ?? '')) {
|
|
1286
1307
|
let workspaceId = getFlag(flags, 'workspace') ?? auth.selectedWorkspaceId;
|
|
1287
1308
|
if (!workspaceId)
|
|
@@ -1305,10 +1326,21 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
|
|
|
1305
1326
|
return;
|
|
1306
1327
|
}
|
|
1307
1328
|
if (subcommand === 'list') {
|
|
1308
|
-
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
|
+
}));
|
|
1309
1334
|
return;
|
|
1310
1335
|
}
|
|
1311
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
|
+
}
|
|
1312
1344
|
const account = await auth.client.list();
|
|
1313
1345
|
const selected = auth.selectedWorkspaceId;
|
|
1314
1346
|
const workspace = account.workspaces.find(item => selected ? item.id === selected : item.active);
|
|
@@ -1333,18 +1365,34 @@ const commandWorkspace = async (subcommand, positionals, flags) => {
|
|
|
1333
1365
|
throw new Error('Use workspace list|get|create|use');
|
|
1334
1366
|
const workspaceId = requirePositional(positionals, 2, 'workspace-id');
|
|
1335
1367
|
const selected = await auth.client.select(workspaceId);
|
|
1368
|
+
if (selected.workspaceId !== workspaceId)
|
|
1369
|
+
throw new Error('Workspace API returned an invalid selection scope');
|
|
1336
1370
|
if (selected.mfa.status === 'setup_required' ||
|
|
1337
1371
|
selected.mfa.status === 'verify_required')
|
|
1338
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');
|
|
1339
1373
|
const account = await auth.client.list();
|
|
1340
|
-
const workspace = account.workspaces.find(item => item.id === workspaceId);
|
|
1374
|
+
const workspace = selected.workspace ?? account.workspaces.find(item => item.id === workspaceId);
|
|
1341
1375
|
if (!workspace)
|
|
1342
1376
|
throw new Error('Workspace is not available to this authenticated user');
|
|
1343
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
|
+
}
|
|
1344
1392
|
const cached = Object.entries(profiles).find(([, entry]) => entry.userId === account.userId &&
|
|
1345
1393
|
entry.workspaceId === workspaceId &&
|
|
1346
1394
|
new URL(entry.baseUrl).origin === new URL(auth.baseUrl).origin &&
|
|
1347
|
-
entry.apiKey);
|
|
1395
|
+
entry.apiKey && !entry.apiKey.startsWith('ah_pat_'));
|
|
1348
1396
|
let apiKey;
|
|
1349
1397
|
for (const candidate of new Set([cached?.[1].apiKey, suppliedKey])) {
|
|
1350
1398
|
if (!candidate)
|
|
@@ -1447,7 +1495,13 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1447
1495
|
throw new Error('API key is empty. Pass --api-key, use --api-key-stdin, or set ASSETHUB_API_KEY.');
|
|
1448
1496
|
}
|
|
1449
1497
|
const baseUrl = getFlag(flags, 'base-url') ?? env.ASSETHUB_API_BASE_URL ?? defaultBaseUrl;
|
|
1450
|
-
|
|
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')) {
|
|
1451
1505
|
const client = createAssetHubClient({ apiKey, baseUrl });
|
|
1452
1506
|
await client.v2.listModels();
|
|
1453
1507
|
}
|
|
@@ -1456,6 +1510,7 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1456
1510
|
profiles[profile] = {
|
|
1457
1511
|
apiKey,
|
|
1458
1512
|
baseUrl,
|
|
1513
|
+
...(account ? { userId: account.userId } : {}),
|
|
1459
1514
|
updatedAt: new Date().toISOString(),
|
|
1460
1515
|
};
|
|
1461
1516
|
await writeAuthConfig(configPath, {
|
|
@@ -1468,15 +1523,19 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1468
1523
|
profile,
|
|
1469
1524
|
baseUrl,
|
|
1470
1525
|
configPath,
|
|
1471
|
-
verified: !hasFlag(flags, 'skip-verify'),
|
|
1526
|
+
verified: personal || !hasFlag(flags, 'skip-verify'),
|
|
1527
|
+
...(account ? { authentication: 'personal', userId: account.userId } : {}),
|
|
1472
1528
|
});
|
|
1473
1529
|
return;
|
|
1474
1530
|
}
|
|
1475
1531
|
if (subcommand === 'status') {
|
|
1476
1532
|
const config = await readAuthConfig(configPath);
|
|
1477
1533
|
const current = config.profiles?.[getFlag(flags, 'profile') ?? config.defaultProfile ?? profile];
|
|
1478
|
-
if (
|
|
1479
|
-
|
|
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))) {
|
|
1480
1539
|
const auth = await workspaceAuth(flags);
|
|
1481
1540
|
const account = await auth.client.list();
|
|
1482
1541
|
print({
|
|
@@ -1493,7 +1552,18 @@ const commandAuth = async (subcommand, flags) => {
|
|
|
1493
1552
|
const client = createAssetHubClient({
|
|
1494
1553
|
apiKey: auth.apiKey,
|
|
1495
1554
|
baseUrl: auth.baseUrl,
|
|
1555
|
+
workspaceId: auth.workspaceId,
|
|
1496
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
|
+
}
|
|
1497
1567
|
const models = await client.v2.listModels();
|
|
1498
1568
|
print({
|
|
1499
1569
|
authenticated: true,
|
|
@@ -1639,6 +1709,134 @@ const printRecorded = async (ctx, queued) => {
|
|
|
1639
1709
|
});
|
|
1640
1710
|
process.exitCode = executionExitCode(execution);
|
|
1641
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
|
+
};
|
|
1642
1840
|
const commandImage = async (subcommand, ctx) => {
|
|
1643
1841
|
if (subcommand !== 'generate')
|
|
1644
1842
|
throw new Error('Unknown image command. Use "image generate".');
|
|
@@ -1746,6 +1944,54 @@ const refinementModeInfo = (capabilities, mode) => {
|
|
|
1746
1944
|
throw new Error(`Refinement mode ${mode} is unavailable${info.reason ? `: ${info.reason}` : ''}`);
|
|
1747
1945
|
return info;
|
|
1748
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
|
+
};
|
|
1749
1995
|
const commandComposerRefine = async (ctx) => {
|
|
1750
1996
|
const capabilities = await ctx.client.v2.getMeshRefinementModes();
|
|
1751
1997
|
if (hasFlag(ctx.flags, 'list-modes')) {
|
|
@@ -1769,20 +2015,7 @@ const commandComposerRefine = async (ctx) => {
|
|
|
1769
2015
|
const input = json
|
|
1770
2016
|
? await readJsonArgument(json, '--input-json')
|
|
1771
2017
|
: 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
|
-
}
|
|
2018
|
+
? composerInputFromRun(previous, 'mesh.refine')
|
|
1786
2019
|
: {
|
|
1787
2020
|
parts: parts.map(assetId => ({
|
|
1788
2021
|
assetId: assertFlagHasValue(assetId, '--part <asset-id>'),
|
|
@@ -1850,6 +2083,9 @@ const commandComposerRefine = async (ctx) => {
|
|
|
1850
2083
|
mode,
|
|
1851
2084
|
instruction,
|
|
1852
2085
|
...(maxRounds != null ? { maxRounds } : {}),
|
|
2086
|
+
...(input.agentRuntime
|
|
2087
|
+
? { agentRuntime: input.agentRuntime }
|
|
2088
|
+
: {}),
|
|
1853
2089
|
};
|
|
1854
2090
|
await printRecorded(ctx, await executeRecorded(session, 'mesh.refine', body, {
|
|
1855
2091
|
...executionOptions(ctx.flags),
|
|
@@ -1867,6 +2103,7 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
1867
2103
|
}
|
|
1868
2104
|
if (subcommand !== 'run')
|
|
1869
2105
|
throw new Error('Use composer models|run');
|
|
2106
|
+
const nodeId = canvasNodeFlag(ctx.flags);
|
|
1870
2107
|
const fromRun = getFlag(ctx.flags, 'from-run');
|
|
1871
2108
|
const json = getFlag(ctx.flags, 'input-json');
|
|
1872
2109
|
const parts = getFlagValues(ctx.flags, 'part');
|
|
@@ -1876,20 +2113,23 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
1876
2113
|
const previous = fromRun ? await ctx.client.v2.getRun(fromRun) : undefined;
|
|
1877
2114
|
if (previous && previous.operation !== 'mesh.compose')
|
|
1878
2115
|
throw new Error('--from-run must identify a mesh.compose execution');
|
|
1879
|
-
const input =
|
|
1880
|
-
? {
|
|
1881
|
-
:
|
|
1882
|
-
?
|
|
1883
|
-
:
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
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
|
+
};
|
|
1889
2128
|
delete input.executionContext;
|
|
1890
|
-
if (!
|
|
1891
|
-
!input.parts
|
|
1892
|
-
|
|
2129
|
+
if (!nodeId &&
|
|
2130
|
+
(!Array.isArray(input.parts) ||
|
|
2131
|
+
!input.parts.length ||
|
|
2132
|
+
!input.fullBodyImageAssetId))
|
|
1893
2133
|
throw new Error('Composer requires parts[] and fullBodyImageAssetId');
|
|
1894
2134
|
const mode = getFlag(ctx.flags, 'mode');
|
|
1895
2135
|
if (mode != null && mode !== 'quick' && mode !== 'quality')
|
|
@@ -1905,17 +2145,48 @@ const commandComposer = async (subcommand, ctx) => {
|
|
|
1905
2145
|
const transforms = getFlag(ctx.flags, 'transforms-json');
|
|
1906
2146
|
if (transforms)
|
|
1907
2147
|
input.transforms = await readJsonArgument(transforms, '--transforms-json');
|
|
2148
|
+
if (previous && input.transforms) {
|
|
2149
|
+
delete input.quickRunId;
|
|
2150
|
+
delete input.quickScene;
|
|
2151
|
+
}
|
|
1908
2152
|
const canvasId = selectedCanvasId(ctx.flags) ?? previous?.canvas.id;
|
|
1909
2153
|
if (previous && canvasId !== previous.canvas.id)
|
|
1910
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
|
+
}
|
|
1911
2172
|
const session = await openExecutionSession({
|
|
1912
2173
|
...stateOptions(ctx),
|
|
1913
2174
|
canvasId,
|
|
1914
2175
|
operation: 'mesh.compose',
|
|
2176
|
+
...(nodeId ? { create: false } : {}),
|
|
1915
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
|
+
}
|
|
1916
2186
|
const queued = await executeRecorded(session, 'mesh.compose', input, {
|
|
1917
2187
|
...executionOptions(ctx.flags),
|
|
1918
2188
|
...(previous ? { parentRunId: previous.runId } : {}),
|
|
2189
|
+
...(nodeId ? { canvasNode: { nodeId } } : {}),
|
|
1919
2190
|
});
|
|
1920
2191
|
await printRecorded(ctx, queued);
|
|
1921
2192
|
};
|
|
@@ -1939,6 +2210,11 @@ const commandMesh = async (subcommand, positionals, ctx) => {
|
|
|
1939
2210
|
return;
|
|
1940
2211
|
}
|
|
1941
2212
|
if (subcommand === 'generate') {
|
|
2213
|
+
const nodeId = canvasNodeFlag(ctx.flags);
|
|
2214
|
+
if (nodeId) {
|
|
2215
|
+
await commandNodeMesh(ctx, nodeId);
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
1942
2218
|
const inputJson = getFlag(ctx.flags, 'input-json');
|
|
1943
2219
|
const input = inputJson
|
|
1944
2220
|
? await readJsonArgument(inputJson, '--input-json')
|
|
@@ -2569,12 +2845,6 @@ const productionSession = (ctx, operation) => openExecutionSession({
|
|
|
2569
2845
|
canvasId: selectedCanvasId(ctx.flags),
|
|
2570
2846
|
operation,
|
|
2571
2847
|
});
|
|
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
2848
|
const commandProduction = async (subcommand, positionals, ctx) => {
|
|
2579
2849
|
if (subcommand === 'agents') {
|
|
2580
2850
|
print(sanitizeProductionOutput(await ctx.client.v1.getProductionAgents()));
|
|
@@ -2814,7 +3084,7 @@ const commandParts = async (subcommand, ctx) => {
|
|
|
2814
3084
|
...stateOptions(ctx),
|
|
2815
3085
|
operationId: executeOperationId,
|
|
2816
3086
|
expectedCanvasId: session.canvas.id,
|
|
2817
|
-
|
|
3087
|
+
expectedBodySubset: {
|
|
2818
3088
|
partExtractionMode: getFlag(ctx.flags, 'part-extraction-mode'),
|
|
2819
3089
|
...(getFlagValues(ctx.flags, 'task-id').length
|
|
2820
3090
|
? { confirmedTaskIds: getFlagValues(ctx.flags, 'task-id') }
|
|
@@ -3025,6 +3295,11 @@ const canvasForRead = (ctx) => openExecutionSession({
|
|
|
3025
3295
|
create: false,
|
|
3026
3296
|
});
|
|
3027
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
|
+
}
|
|
3028
3303
|
if (subcommand === 'download') {
|
|
3029
3304
|
const outDir = requireFlag(ctx.flags, 'out-dir');
|
|
3030
3305
|
const { canvas } = await canvasForRead(ctx);
|
|
@@ -3357,6 +3632,14 @@ const commandRuns = async (subcommand, positionals, ctx) => {
|
|
|
3357
3632
|
return;
|
|
3358
3633
|
}
|
|
3359
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
|
+
}
|
|
3360
3643
|
const result = await resumeRecorded({
|
|
3361
3644
|
...stateOptions(ctx),
|
|
3362
3645
|
operationId: requirePositional(positionals, 2, 'operation-id'),
|
|
@@ -3389,6 +3672,7 @@ const commandRuns = async (subcommand, positionals, ctx) => {
|
|
|
3389
3672
|
plan,
|
|
3390
3673
|
baseUrl: ctx.auth.baseUrl,
|
|
3391
3674
|
apiKey: ctx.auth.apiKey,
|
|
3675
|
+
workspaceId: ctx.auth.workspaceId,
|
|
3392
3676
|
skipRegister: args.skipRegister,
|
|
3393
3677
|
onProgress: event => {
|
|
3394
3678
|
if (event.kind === 'register') {
|
|
@@ -3509,7 +3793,17 @@ const run = async () => {
|
|
|
3509
3793
|
if (command === 'mcp') {
|
|
3510
3794
|
if (subcommand !== 'config')
|
|
3511
3795
|
throw new Error('Use mcp tools [tool-name] or mcp config --client cursor|codex');
|
|
3512
|
-
|
|
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));
|
|
3513
3807
|
return;
|
|
3514
3808
|
}
|
|
3515
3809
|
if (command === 'doctor') {
|