feat: wire Launcher AI Workspace entitlements
This commit is contained in:
parent
e11f9bd7e7
commit
f35cd34167
|
|
@ -86,6 +86,16 @@ NDC_LAUNCHER_INTERNAL_ACCESS_TOKEN=...
|
|||
|
||||
If `NDC_LAUNCHER_INTERNAL_ACCESS_TOKEN` is not set, the service falls back to `NODEDC_INTERNAL_ACCESS_TOKEN`.
|
||||
|
||||
Launcher entitlement adapter configuration:
|
||||
|
||||
```text
|
||||
AI_WORKSPACE_LAUNCHER_ENTITLEMENT_URL=http://launcher:5173/api/ai-workspace/internal/v1/entitlements
|
||||
AI_WORKSPACE_LAUNCHER_ENTITLEMENT_TOKEN=...
|
||||
AI_WORKSPACE_LAUNCHER_ENTITLEMENT_REQUIRED=true
|
||||
```
|
||||
|
||||
The Launcher adapter is evaluated after app-owned adapters and may return `launcher`, `ops`, and `engine` app grants. A Launcher denial is the top-level service-access decision and clears MCP servers for that app in the run profile; app-owned adapters still own app-local scopes and runtime tokens.
|
||||
|
||||
The first HUB action ids exposed to assistants are:
|
||||
|
||||
- `hub.user.read_admin_summary`
|
||||
|
|
|
|||
|
|
@ -5,8 +5,14 @@ import { createHash, randomUUID } from "node:crypto";
|
|||
|
||||
const SECRET_TOKEN = "secret-run-token-for-smoke";
|
||||
const ACCESS_DENIED_TEXT = "Доступ к модулю ограничен, обратитесь к администратору системы.";
|
||||
const RUN_APP_ID_ALIASES = new Map([
|
||||
["hub", "launcher"],
|
||||
["nodedc-hub", "launcher"],
|
||||
["nodedc_launcher", "launcher"],
|
||||
["nodedc-launcher", "launcher"],
|
||||
]);
|
||||
const APP_ROUTING_CATALOG = [
|
||||
{ appId: "launcher", appTitle: "NODE.DC Launcher", surface: "launcher", skillId: "launcher-context", mcpServerNames: [], deniedText: ACCESS_DENIED_TEXT },
|
||||
{ appId: "launcher", appTitle: "NODE.DC Launcher", surface: "launcher", skillId: "launcher-context", appAliases: ["hub"], actionIdPrefixes: ["hub.", "launcher.", "access."], mcpServerNames: [], deniedText: ACCESS_DENIED_TEXT },
|
||||
{ appId: "engine", appTitle: "NODE.DC Engine / InJoin", surface: "engine", skillId: "engine-context", mcpServerNames: ["nodedc-engine", "nodedc-agent-core"], deniedText: ACCESS_DENIED_TEXT },
|
||||
{ appId: "ops", appTitle: "NODE.DC Ops / Tasker", surface: "ops", skillId: "ops-context", mcpServerNames: ["nodedc_ops_agent"], deniedText: ACCESS_DENIED_TEXT },
|
||||
];
|
||||
|
|
@ -60,6 +66,37 @@ const assistantActions = {
|
|||
};
|
||||
const appCatalog = buildRunProfileAppCatalog({ appGrants, mcpServers, assistantActions });
|
||||
const appAccess = summarizeRunAppAccess(appCatalog);
|
||||
const launcherAdapter = { id: "launcher", appId: "launcher", title: "NODE.DC Launcher" };
|
||||
const launcherGrants = normalizeEntitlementAdapterAppGrants({
|
||||
ok: true,
|
||||
appGrants: {
|
||||
hub: {
|
||||
appTitle: "NODE.DC Launcher",
|
||||
surface: "launcher",
|
||||
status: "granted",
|
||||
scopes: ["launcher:access:read", "launcher_admin_scope.any"],
|
||||
},
|
||||
},
|
||||
}, launcherAdapter);
|
||||
const catalogWithLauncherGrant = buildRunProfileAppCatalog({
|
||||
appGrants: { ...appGrants, ...launcherGrants },
|
||||
mcpServers,
|
||||
assistantActions,
|
||||
});
|
||||
const deniedByLauncher = mergeRunAppGrants(appGrants, normalizeEntitlementAdapterAppGrants({
|
||||
ok: true,
|
||||
appGrants: {
|
||||
ops: {
|
||||
appTitle: "NODE.DC Ops",
|
||||
surface: "ops",
|
||||
status: "denied",
|
||||
allowed: false,
|
||||
reason: "launcher_service_access_denied",
|
||||
mcpServers: [],
|
||||
},
|
||||
},
|
||||
}, launcherAdapter));
|
||||
const deniedMcpServers = runProfileMcpServersFromAppGrants({}, deniedByLauncher);
|
||||
const runProfile = {
|
||||
schemaVersion: "ai-workspace.run-profile.v1",
|
||||
runId: randomUUID(),
|
||||
|
|
@ -138,6 +175,12 @@ assert.equal(runProfile.appCatalog.find((app) => app.appId === "ops")?.status, "
|
|||
assert.deepEqual(runProfile.appCatalog.find((app) => app.appId === "ops")?.mcpServerNames, ["nodedc_ops_agent"]);
|
||||
assert.equal(runProfile.appCatalog.find((app) => app.appId === "launcher")?.status, "not-granted");
|
||||
assert.equal(runProfile.appCatalog.find((app) => app.appId === "launcher")?.deniedText, ACCESS_DENIED_TEXT);
|
||||
assert.equal(launcherGrants.launcher.appId, "launcher");
|
||||
assert.equal(launcherGrants.hub, undefined);
|
||||
assert.deepEqual(catalogWithLauncherGrant.find((app) => app.appId === "launcher")?.actionIds, ["hub.access_request.list_pending", "hub.user.read_admin_summary"]);
|
||||
assert.equal(deniedByLauncher.ops.status, "denied");
|
||||
assert.deepEqual(deniedByLauncher.ops.mcpServers, []);
|
||||
assert.deepEqual(deniedMcpServers, []);
|
||||
assert.deepEqual(runProfile.appAccess.grantedAppIds, ["ops"]);
|
||||
assert.deepEqual(runProfile.appAccess.deniedAppIds, ["engine", "launcher"]);
|
||||
assert.equal(JSON.stringify(publicProfile).includes(SECRET_TOKEN), false);
|
||||
|
|
@ -150,6 +193,8 @@ console.log(JSON.stringify({
|
|||
"token_scoped_ops_mcp_in_run_profile",
|
||||
"assistant_action_relay_in_run_profile",
|
||||
"app_routing_catalog_in_run_profile",
|
||||
"hub_actions_route_to_launcher",
|
||||
"launcher_denial_removes_ops_mcp",
|
||||
"denied_apps_have_standard_text",
|
||||
"public_run_profile_redacts_mcp_headers",
|
||||
"public_run_profile_redacts_assistant_action_gateway_token",
|
||||
|
|
@ -188,7 +233,7 @@ function normalizeEntitlementAdapterAppGrants(payload, currentAdapter) {
|
|||
|
||||
function normalizeEntitlementAdapterGrant(value, currentAdapter) {
|
||||
if (!isPlainObject(value)) return null;
|
||||
const appId = normalizeKey(value.appId || value.app_id || currentAdapter.appId);
|
||||
const appId = canonicalRunAppId(value.appId || value.app_id || currentAdapter.appId);
|
||||
if (!appId) return null;
|
||||
return {
|
||||
...value,
|
||||
|
|
@ -200,13 +245,60 @@ function normalizeEntitlementAdapterGrant(value, currentAdapter) {
|
|||
};
|
||||
}
|
||||
|
||||
function canonicalRunAppId(value) {
|
||||
const appId = normalizeKey(value);
|
||||
if (!appId) return "";
|
||||
return RUN_APP_ID_ALIASES.get(appId) || appId;
|
||||
}
|
||||
|
||||
function normalizeRunAppGrantsObject(value) {
|
||||
const out = {};
|
||||
if (!isPlainObject(value)) return out;
|
||||
for (const [key, rawGrant] of Object.entries(value)) {
|
||||
if (!isPlainObject(rawGrant)) continue;
|
||||
const appId = canonicalRunAppId(rawGrant.appId || rawGrant.app_id || key);
|
||||
if (!appId) continue;
|
||||
const existing = isPlainObject(out[appId]) ? out[appId] : {};
|
||||
const grant = {
|
||||
...existing,
|
||||
...rawGrant,
|
||||
appId,
|
||||
};
|
||||
grant.mcpServers = isRunAppGrantDenied(grant)
|
||||
? []
|
||||
: Object.hasOwn(rawGrant, "mcpServers") ? rawGrant.mcpServers : existing.mcpServers;
|
||||
out[appId] = grant;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function mergeRunAppGrants(base, next) {
|
||||
const out = normalizeRunAppGrantsObject(base);
|
||||
for (const [rawAppId, grant] of Object.entries(normalizeRunAppGrantsObject(next))) {
|
||||
const appId = canonicalRunAppId(grant.appId || rawAppId);
|
||||
if (!appId) continue;
|
||||
const existing = isPlainObject(out[appId]) ? out[appId] : {};
|
||||
const merged = {
|
||||
...existing,
|
||||
...grant,
|
||||
appId,
|
||||
};
|
||||
merged.mcpServers = isRunAppGrantDenied(merged)
|
||||
? []
|
||||
: Object.hasOwn(grant, "mcpServers") ? grant.mcpServers : existing.mcpServers;
|
||||
out[appId] = merged;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function runProfileMcpServersFromAppGrants(settings, appGrantsInput = {}) {
|
||||
const servers = [];
|
||||
const metadata = isPlainObject(settings?.metadata) ? settings.metadata : {};
|
||||
collectInstallerMcpServers(servers, metadata.mcpServers, {});
|
||||
const grants = isPlainObject(appGrantsInput) ? appGrantsInput : {};
|
||||
const grants = normalizeRunAppGrantsObject(appGrantsInput);
|
||||
for (const [appId, grant] of Object.entries(grants)) {
|
||||
if (!isPlainObject(grant)) continue;
|
||||
if (isRunAppGrantDenied(grant)) continue;
|
||||
collectInstallerMcpServers(servers, grant.mcpServers, {
|
||||
appId: optionalString(grant.appId) || normalizeKey(appId),
|
||||
appTitle: optionalString(grant.appTitle || grant.title),
|
||||
|
|
@ -222,11 +314,11 @@ function runProfileMcpServersFromAppGrants(settings, appGrantsInput = {}) {
|
|||
}
|
||||
|
||||
function summarizeRunAppGrants(metadata) {
|
||||
const grants = isPlainObject(metadata?.appGrants) ? metadata.appGrants : {};
|
||||
const grants = normalizeRunAppGrantsObject(metadata?.appGrants);
|
||||
const out = {};
|
||||
for (const [key, value] of Object.entries(grants)) {
|
||||
if (!isPlainObject(value)) continue;
|
||||
const appId = optionalString(value.appId) || normalizeKey(key);
|
||||
const appId = canonicalRunAppId(value.appId || key);
|
||||
if (!appId) continue;
|
||||
const denied = isRunAppGrantDenied(value);
|
||||
const mcpServers = Array.isArray(value.mcpServers)
|
||||
|
|
@ -256,7 +348,7 @@ function summarizeRunAppGrants(metadata) {
|
|||
}
|
||||
|
||||
function buildRunProfileAppCatalog({ appGrants, mcpServers, assistantActions }) {
|
||||
const grants = isPlainObject(appGrants) ? appGrants : {};
|
||||
const grants = normalizeRunAppGrantsObject(appGrants);
|
||||
const allMcpServerNames = uniqueStrings((Array.isArray(mcpServers) ? mcpServers : [])
|
||||
.map((server) => server?.serverName)
|
||||
.filter(Boolean));
|
||||
|
|
@ -276,7 +368,7 @@ function buildRunProfileAppCatalog({ appGrants, mcpServers, assistantActions })
|
|||
granted: status === "granted",
|
||||
denied: status !== "granted",
|
||||
deniedText: status !== "granted" ? entry.deniedText : null,
|
||||
actionIds: actionIds.filter((actionId) => String(actionId || "").startsWith(`${entry.appId}.`)),
|
||||
actionIds: actionIds.filter((actionId) => actionIdPrefixesForEntry(entry).some((prefix) => String(actionId || "").startsWith(prefix))),
|
||||
mcpServerNames: status === "granted"
|
||||
? uniqueStrings([
|
||||
...entry.mcpServerNames,
|
||||
|
|
@ -288,6 +380,14 @@ function buildRunProfileAppCatalog({ appGrants, mcpServers, assistantActions })
|
|||
});
|
||||
}
|
||||
|
||||
function actionIdPrefixesForEntry(entry) {
|
||||
return uniqueStrings([
|
||||
...(Array.isArray(entry.actionIdPrefixes) ? entry.actionIdPrefixes : []),
|
||||
`${entry.appId}.`,
|
||||
...(Array.isArray(entry.appAliases) ? entry.appAliases.map((alias) => `${normalizeKey(alias)}.`) : []),
|
||||
].filter(Boolean));
|
||||
}
|
||||
|
||||
function summarizeRunAppAccess(appCatalog) {
|
||||
const apps = Array.isArray(appCatalog) ? appCatalog : [];
|
||||
return {
|
||||
|
|
@ -306,7 +406,7 @@ function isRunAppGrantDenied(grant) {
|
|||
grant.allowed === false ||
|
||||
grant.granted === false ||
|
||||
grant.denied === true ||
|
||||
["denied", "disabled", "blocked", "revoked", "not_granted", "forbidden"].includes(status);
|
||||
["denied", "disabled", "blocked", "revoked", "not-granted", "not_granted", "forbidden"].includes(status);
|
||||
}
|
||||
|
||||
function collectInstallerMcpServers(target, value, defaults = {}) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@ const ASSISTANT_ACTION_TOOL_PROFILE_BASE = {
|
|||
},
|
||||
};
|
||||
const ACCESS_DENIED_TEXT = "Доступ к модулю ограничен, обратитесь к администратору системы.";
|
||||
const RUN_APP_ID_ALIASES = new Map([
|
||||
["hub", "launcher"],
|
||||
["nodedc-hub", "launcher"],
|
||||
["nodedc_launcher", "launcher"],
|
||||
["nodedc-launcher", "launcher"],
|
||||
]);
|
||||
const APP_ROUTING_CATALOG = [
|
||||
{
|
||||
appId: "launcher",
|
||||
|
|
@ -43,7 +49,9 @@ const APP_ROUTING_CATALOG = [
|
|||
"invites",
|
||||
"entitlements",
|
||||
],
|
||||
actionNamespaces: ["launcher.*", "access.*"],
|
||||
appAliases: ["hub"],
|
||||
actionNamespaces: ["hub.*", "launcher.*", "access.*"],
|
||||
actionIdPrefixes: ["hub.", "launcher.", "access."],
|
||||
mcpServerNames: [],
|
||||
requiredScopes: ["launcher:access:read"],
|
||||
deniedText: ACCESS_DENIED_TEXT,
|
||||
|
|
@ -2144,7 +2152,7 @@ function delay(ms) {
|
|||
async function resolveRunAppGrants({ owner, context, ownerSettings }) {
|
||||
const metadata = isPlainObject(ownerSettings?.metadata) ? ownerSettings.metadata : {};
|
||||
const staticAppGrants = isPlainObject(metadata.appGrants) ? metadata.appGrants : {};
|
||||
const appGrants = { ...staticAppGrants };
|
||||
const appGrants = normalizeRunAppGrantsObject(staticAppGrants);
|
||||
const adapterDiagnostics = [];
|
||||
const adapters = Array.isArray(config.entitlementAdapters) ? config.entitlementAdapters : [];
|
||||
if (!adapters.length) {
|
||||
|
|
@ -2177,8 +2185,10 @@ async function resolveRunAppGrants({ owner, context, ownerSettings }) {
|
|||
continue;
|
||||
}
|
||||
const adapterAppGrants = normalizeEntitlementAdapterAppGrants(result.payload, adapter);
|
||||
for (const [appId, grant] of Object.entries(adapterAppGrants)) {
|
||||
for (const [rawAppId, grant] of Object.entries(adapterAppGrants)) {
|
||||
if (!isPlainObject(grant)) continue;
|
||||
const appId = canonicalRunAppId(grant.appId || rawAppId);
|
||||
if (!appId) continue;
|
||||
const existing = isPlainObject(appGrants[appId]) ? appGrants[appId] : {};
|
||||
const merged = {
|
||||
...existing,
|
||||
|
|
@ -2217,6 +2227,27 @@ async function resolveRunAppGrants({ owner, context, ownerSettings }) {
|
|||
};
|
||||
}
|
||||
|
||||
function normalizeRunAppGrantsObject(value) {
|
||||
const out = {};
|
||||
if (!isPlainObject(value)) return out;
|
||||
for (const [key, rawGrant] of Object.entries(value)) {
|
||||
if (!isPlainObject(rawGrant)) continue;
|
||||
const appId = canonicalRunAppId(rawGrant.appId || rawGrant.app_id || key);
|
||||
if (!appId) continue;
|
||||
const existing = isPlainObject(out[appId]) ? out[appId] : {};
|
||||
const grant = {
|
||||
...existing,
|
||||
...rawGrant,
|
||||
appId,
|
||||
};
|
||||
grant.mcpServers = isRunAppGrantDenied(grant)
|
||||
? []
|
||||
: Object.hasOwn(rawGrant, "mcpServers") ? rawGrant.mcpServers : existing.mcpServers;
|
||||
out[appId] = grant;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
async function fetchRunEntitlementAdapter({ adapter, owner, context, ownerSettings }) {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), adapter.timeoutMs);
|
||||
|
|
@ -2288,7 +2319,7 @@ function normalizeEntitlementAdapterAppGrants(payload, adapter) {
|
|||
|
||||
function normalizeEntitlementAdapterGrant(value, adapter) {
|
||||
if (!isPlainObject(value)) return null;
|
||||
const appId = normalizeKey(value.appId || value.app_id || adapter.appId);
|
||||
const appId = canonicalRunAppId(value.appId || value.app_id || adapter.appId);
|
||||
if (!appId) return null;
|
||||
return {
|
||||
...value,
|
||||
|
|
@ -2300,9 +2331,15 @@ function normalizeEntitlementAdapterGrant(value, adapter) {
|
|||
};
|
||||
}
|
||||
|
||||
function canonicalRunAppId(value) {
|
||||
const appId = normalizeKey(value);
|
||||
if (!appId) return "";
|
||||
return RUN_APP_ID_ALIASES.get(appId) || appId;
|
||||
}
|
||||
|
||||
function runProfileMcpServersFromSettings(settings) {
|
||||
const metadata = isPlainObject(settings?.metadata) ? settings.metadata : {};
|
||||
const appGrants = isPlainObject(metadata.appGrants) ? metadata.appGrants : {};
|
||||
const appGrants = normalizeRunAppGrantsObject(metadata.appGrants);
|
||||
return runProfileMcpServersFromAppGrants(settings, appGrants);
|
||||
}
|
||||
|
||||
|
|
@ -2313,7 +2350,7 @@ function runProfileMcpServersFromAppGrants(settings, appGrantsInput = {}) {
|
|||
if (!hasEntitlementAdapters) {
|
||||
collectInstallerMcpServers(servers, metadata.mcpServers, {});
|
||||
}
|
||||
const appGrants = isPlainObject(appGrantsInput) ? appGrantsInput : {};
|
||||
const appGrants = normalizeRunAppGrantsObject(appGrantsInput);
|
||||
for (const [appId, grant] of Object.entries(appGrants)) {
|
||||
if (!isPlainObject(grant)) continue;
|
||||
if (isRunAppGrantDenied(grant)) continue;
|
||||
|
|
@ -2332,11 +2369,11 @@ function runProfileMcpServersFromAppGrants(settings, appGrantsInput = {}) {
|
|||
}
|
||||
|
||||
function summarizeRunAppGrants(metadata) {
|
||||
const appGrants = isPlainObject(metadata?.appGrants) ? metadata.appGrants : {};
|
||||
const appGrants = normalizeRunAppGrantsObject(metadata?.appGrants);
|
||||
const out = {};
|
||||
for (const [key, value] of Object.entries(appGrants)) {
|
||||
if (!isPlainObject(value)) continue;
|
||||
const appId = optionalString(value.appId) || normalizeKey(key);
|
||||
const appId = canonicalRunAppId(value.appId || key);
|
||||
if (!appId) continue;
|
||||
const denied = isRunAppGrantDenied(value);
|
||||
const mcpServers = Array.isArray(value.mcpServers)
|
||||
|
|
@ -2366,9 +2403,9 @@ function summarizeRunAppGrants(metadata) {
|
|||
}
|
||||
|
||||
function buildRunProfileAppCatalog({ appGrants, mcpServers, assistantActions }) {
|
||||
const grants = isPlainObject(appGrants) ? appGrants : {};
|
||||
const grants = normalizeRunAppGrantsObject(appGrants);
|
||||
const dynamicAppIds = Object.keys(grants)
|
||||
.map(normalizeKey)
|
||||
.map(canonicalRunAppId)
|
||||
.filter(Boolean)
|
||||
.filter((appId) => !APP_ROUTING_CATALOG.some((entry) => entry.appId === appId));
|
||||
const catalog = [
|
||||
|
|
@ -2380,6 +2417,7 @@ function buildRunProfileAppCatalog({ appGrants, mcpServers, assistantActions })
|
|||
skillId: `${appId}-context`,
|
||||
whenToUse: [],
|
||||
actionNamespaces: [`${appId}.*`],
|
||||
actionIdPrefixes: [`${appId}.`],
|
||||
mcpServerNames: [],
|
||||
requiredScopes: [],
|
||||
deniedText: ACCESS_DENIED_TEXT,
|
||||
|
|
@ -2417,6 +2455,11 @@ function buildRunProfileAppCatalog({ appGrants, mcpServers, assistantActions })
|
|||
...grantMcpServerNames,
|
||||
...(mcpByAppId.get(entry.appId) || []),
|
||||
]);
|
||||
const actionIdPrefixes = uniqueStrings([
|
||||
...(Array.isArray(entry.actionIdPrefixes) ? entry.actionIdPrefixes : []),
|
||||
`${entry.appId}.`,
|
||||
...(Array.isArray(entry.appAliases) ? entry.appAliases.map((alias) => `${normalizeKey(alias)}.`) : []),
|
||||
].filter(Boolean));
|
||||
return {
|
||||
schemaVersion: "ai-workspace.app-route.v1",
|
||||
appId: entry.appId,
|
||||
|
|
@ -2434,7 +2477,7 @@ function buildRunProfileAppCatalog({ appGrants, mcpServers, assistantActions })
|
|||
: null,
|
||||
whenToUse: uniqueStrings(entry.whenToUse),
|
||||
actionNamespaces: uniqueStrings(entry.actionNamespaces),
|
||||
actionIds: actionIds.filter((actionId) => String(actionId || "").startsWith(`${entry.appId}.`)),
|
||||
actionIds: actionIds.filter((actionId) => actionIdPrefixes.some((prefix) => String(actionId || "").startsWith(prefix))),
|
||||
mcpServerNames: status === "granted" ? advertisedMcpServerNames : [],
|
||||
requiredScopes: uniqueStrings(grant?.requiredScopes || grant?.required_scopes || entry.requiredScopes),
|
||||
scopes: uniqueStrings(Array.isArray(grant?.scopes) ? grant.scopes : []),
|
||||
|
|
@ -4009,12 +4052,35 @@ function parseEntitlementAdapters() {
|
|||
token: process.env.AI_WORKSPACE_ENGINE_ENTITLEMENT_TOKEN || process.env.NDC_AI_WORKSPACE_ENGINE_ENTITLEMENT_TOKEN,
|
||||
required: process.env.AI_WORKSPACE_ENGINE_ENTITLEMENT_REQUIRED || process.env.NDC_AI_WORKSPACE_ENGINE_ENTITLEMENT_REQUIRED,
|
||||
});
|
||||
collectEntitlementAdapter(adapters, "launcher", {
|
||||
url:
|
||||
process.env.AI_WORKSPACE_LAUNCHER_ENTITLEMENT_URL ||
|
||||
process.env.NDC_AI_WORKSPACE_LAUNCHER_ENTITLEMENT_URL ||
|
||||
process.env.AI_WORKSPACE_HUB_ENTITLEMENT_URL ||
|
||||
process.env.NDC_AI_WORKSPACE_HUB_ENTITLEMENT_URL,
|
||||
token:
|
||||
process.env.AI_WORKSPACE_LAUNCHER_ENTITLEMENT_TOKEN ||
|
||||
process.env.NDC_AI_WORKSPACE_LAUNCHER_ENTITLEMENT_TOKEN ||
|
||||
process.env.AI_WORKSPACE_HUB_ENTITLEMENT_TOKEN ||
|
||||
process.env.NDC_AI_WORKSPACE_HUB_ENTITLEMENT_TOKEN,
|
||||
required:
|
||||
process.env.AI_WORKSPACE_LAUNCHER_ENTITLEMENT_REQUIRED ||
|
||||
process.env.NDC_AI_WORKSPACE_LAUNCHER_ENTITLEMENT_REQUIRED ||
|
||||
process.env.AI_WORKSPACE_HUB_ENTITLEMENT_REQUIRED ||
|
||||
process.env.NDC_AI_WORKSPACE_HUB_ENTITLEMENT_REQUIRED,
|
||||
});
|
||||
|
||||
const byAppId = new Map();
|
||||
for (const adapter of adapters) {
|
||||
byAppId.set(adapter.appId, adapter);
|
||||
}
|
||||
return Array.from(byAppId.values());
|
||||
const order = new Map([
|
||||
["ops", 10],
|
||||
["engine", 20],
|
||||
["launcher", 90],
|
||||
]);
|
||||
return Array.from(byAppId.values())
|
||||
.sort((left, right) => (order.get(left.appId) || 50) - (order.get(right.appId) || 50));
|
||||
}
|
||||
|
||||
function parseJsonEnv(value) {
|
||||
|
|
@ -4045,7 +4111,7 @@ function collectEntitlementAdapter(target, defaultAppId, value) {
|
|||
|
||||
function sanitizeEntitlementAdapter(value, defaultAppId = "") {
|
||||
if (!isPlainObject(value)) return null;
|
||||
const appId = normalizeKey(value.appId || value.app_id || defaultAppId);
|
||||
const appId = canonicalRunAppId(value.appId || value.app_id || defaultAppId);
|
||||
const url = cleanHttpEndpoint(value.url || value.endpoint);
|
||||
if (!appId || !url) return null;
|
||||
const tokenEnv = optionalString(value.tokenEnv || value.token_env);
|
||||
|
|
|
|||
Loading…
Reference in New Issue