impel-cli 0.18.15-beta.2 → 0.18.15-beta.3
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/package.json +1 -1
- package/src/apps.js +15 -2
- package/src/commands/apps.js +43 -8
package/package.json
CHANGED
package/src/apps.js
CHANGED
|
@@ -679,7 +679,7 @@ export function migrateLegacyClaudeAppSessions(userData, homeDir = os.homedir())
|
|
|
679
679
|
return copied;
|
|
680
680
|
}
|
|
681
681
|
|
|
682
|
-
export async function fetchGatewayModels(config, fetchImpl = fetch) {
|
|
682
|
+
export async function fetchGatewayModels(config, fetchImpl = fetch, { allowEmpty = false } = {}) {
|
|
683
683
|
const controller = new AbortController();
|
|
684
684
|
const timeout = setTimeout(() => controller.abort(), 20000);
|
|
685
685
|
try {
|
|
@@ -697,7 +697,9 @@ export async function fetchGatewayModels(config, fetchImpl = fetch) {
|
|
|
697
697
|
}
|
|
698
698
|
if (!Array.isArray(payload?.data)) throw new Error("response has no model data array");
|
|
699
699
|
const models = payload.data.filter(isGatewayModel);
|
|
700
|
-
if (models.length === 0
|
|
700
|
+
if (models.length === 0 && !(allowEmpty && isExplicitNoSeatCatalog(payload))) {
|
|
701
|
+
throw new Error("gateway returned no supported models");
|
|
702
|
+
}
|
|
701
703
|
return { models, source: "gateway", version: payload.version ?? null };
|
|
702
704
|
} finally {
|
|
703
705
|
clearTimeout(timeout);
|
|
@@ -2547,6 +2549,17 @@ function isGatewayModel(model) {
|
|
|
2547
2549
|
return model && typeof model.id === "string" && (model.provider === "claude" || model.provider === "codex");
|
|
2548
2550
|
}
|
|
2549
2551
|
|
|
2552
|
+
function isExplicitNoSeatCatalog(payload) {
|
|
2553
|
+
const statuses = payload?.provider_status;
|
|
2554
|
+
return Boolean(statuses
|
|
2555
|
+
&& typeof statuses === "object"
|
|
2556
|
+
&& !Array.isArray(statuses)
|
|
2557
|
+
&& ["claude", "codex"].every((provider) => (
|
|
2558
|
+
statuses[provider]?.state === "no_seat"
|
|
2559
|
+
&& statuses[provider]?.routable === false
|
|
2560
|
+
)));
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2550
2563
|
function writeAtomic(target, contents, mode) {
|
|
2551
2564
|
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
2552
2565
|
if (fs.existsSync(target) && fs.readFileSync(target, "utf8") === contents) {
|
package/src/commands/apps.js
CHANGED
|
@@ -76,7 +76,7 @@ export function selectCatalogAppTargets(
|
|
|
76
76
|
requestedTargets,
|
|
77
77
|
models,
|
|
78
78
|
config,
|
|
79
|
-
{ log = console.log } = {},
|
|
79
|
+
{ log = console.log, allowNone = false } = {},
|
|
80
80
|
) {
|
|
81
81
|
const supported = appTargetsSupportedByModels(
|
|
82
82
|
requestedTargets,
|
|
@@ -87,6 +87,14 @@ export function selectCatalogAppTargets(
|
|
|
87
87
|
const unavailable = requestedTargets.filter((target) => !supportedSet.has(target));
|
|
88
88
|
if (unavailable.length === 0) return supported;
|
|
89
89
|
|
|
90
|
+
if (allowNone && supported.length === 0) {
|
|
91
|
+
for (const target of unavailable) {
|
|
92
|
+
const capability = APP_CAPABILITIES[target];
|
|
93
|
+
log(`Skipping ${capability.label}: the selected tenant has no ${capability.provider} models.`);
|
|
94
|
+
}
|
|
95
|
+
return supported;
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
if (requestedTargets.length === 1 || supported.length === 0) {
|
|
91
99
|
const target = unavailable[0];
|
|
92
100
|
const capability = APP_CAPABILITIES[target];
|
|
@@ -297,9 +305,9 @@ export function openManagedLauncher(launcher, {
|
|
|
297
305
|
return false;
|
|
298
306
|
}
|
|
299
307
|
|
|
300
|
-
async function fetchWindowsCatalog(config, io) {
|
|
308
|
+
async function fetchWindowsCatalog(config, io, { allowEmpty = false } = {}) {
|
|
301
309
|
try {
|
|
302
|
-
return await io.fetchModels(config);
|
|
310
|
+
return await io.fetchModels(config, undefined, { allowEmpty });
|
|
303
311
|
} catch (error) {
|
|
304
312
|
throw new Error(`tenant model catalog is unavailable (${redactSecretText(error.message)}); the Impel app profiles were not changed`);
|
|
305
313
|
}
|
|
@@ -374,12 +382,25 @@ export async function reconcileWindowsTenantApps({
|
|
|
374
382
|
...overrides,
|
|
375
383
|
};
|
|
376
384
|
const config = explicitTenantAppConfig(baseConfig, tenant, targets);
|
|
377
|
-
const catalog = await fetchWindowsCatalog(config, io);
|
|
378
|
-
const actionTargets = selectCatalogAppTargets(targets, catalog.models, config, {
|
|
385
|
+
const catalog = await fetchWindowsCatalog(config, io, { allowEmpty: true });
|
|
386
|
+
const actionTargets = selectCatalogAppTargets(targets, catalog.models, config, {
|
|
387
|
+
log: io.log,
|
|
388
|
+
allowNone: true,
|
|
389
|
+
});
|
|
379
390
|
const supported = new Set(actionTargets);
|
|
380
391
|
const unsupported = targets.filter((target) => !supported.has(target));
|
|
381
392
|
const userData = io.claudeUserData(environment, config.tenantId);
|
|
382
393
|
const paths = appPaths(homeDir, config.tenantId, { claudeUserData: userData, tenantName: config.tenantName });
|
|
394
|
+
if (actionTargets.length === 0) {
|
|
395
|
+
return {
|
|
396
|
+
tenantId: config.tenantId,
|
|
397
|
+
targets: [],
|
|
398
|
+
unsupported,
|
|
399
|
+
paths,
|
|
400
|
+
verification: {},
|
|
401
|
+
failed: [],
|
|
402
|
+
};
|
|
403
|
+
}
|
|
383
404
|
const vendorPaths = {};
|
|
384
405
|
const preparedTargets = new Set(skipVendor ? actionTargets : skipVendorTargets);
|
|
385
406
|
|
|
@@ -501,13 +522,28 @@ export async function reconcileMacTenantApps({
|
|
|
501
522
|
const config = explicitTenantAppConfig(baseConfig, tenant, targets);
|
|
502
523
|
let catalog;
|
|
503
524
|
try {
|
|
504
|
-
catalog = await io.fetchModels(config);
|
|
525
|
+
catalog = await io.fetchModels(config, undefined, { allowEmpty: true });
|
|
505
526
|
} catch (error) {
|
|
506
527
|
throw new Error(`tenant model catalog is unavailable (${redactSecretText(error?.message || error)}); no Impel app files were changed`);
|
|
507
528
|
}
|
|
508
|
-
const actionTargets = selectCatalogAppTargets(targets, catalog.models, config, {
|
|
529
|
+
const actionTargets = selectCatalogAppTargets(targets, catalog.models, config, {
|
|
530
|
+
log: io.log,
|
|
531
|
+
allowNone: true,
|
|
532
|
+
});
|
|
509
533
|
const supported = new Set(actionTargets);
|
|
510
534
|
const unsupported = targets.filter((target) => !supported.has(target));
|
|
535
|
+
const paths = appPaths(homeDir, config.tenantId, { tenantName: config.tenantName });
|
|
536
|
+
if (actionTargets.length === 0) {
|
|
537
|
+
return {
|
|
538
|
+
tenantId: config.tenantId,
|
|
539
|
+
targets: [],
|
|
540
|
+
unsupported,
|
|
541
|
+
paths,
|
|
542
|
+
verification: {},
|
|
543
|
+
installed: [],
|
|
544
|
+
failed: [],
|
|
545
|
+
};
|
|
546
|
+
}
|
|
511
547
|
const vendorPaths = {};
|
|
512
548
|
const statuses = io.status(actionTargets, homeDir, config.tenantId, config.tenantName);
|
|
513
549
|
for (const status of statuses) vendorPaths[status.target] ||= status.vendorPath;
|
|
@@ -549,7 +585,6 @@ export async function reconcileMacTenantApps({
|
|
|
549
585
|
vendorPaths,
|
|
550
586
|
writeBundles: staleTargets,
|
|
551
587
|
}) : [];
|
|
552
|
-
const paths = appPaths(homeDir, config.tenantId, { tenantName: config.tenantName });
|
|
553
588
|
const agentItems = [];
|
|
554
589
|
for (const item of installed) {
|
|
555
590
|
const { client, env, label } = appSkillTarget(item.target, paths);
|