Files
FlowPilot/shared/contribution-registry.js
T
2026-05-23 04:29:37 +08:00

255 lines
9.1 KiB
JavaScript

(function attachMultiPageContributionRegistry(root, factory) {
root.MultiPageContributionRegistry = factory(root);
})(typeof self !== 'undefined' ? self : globalThis, function createContributionRegistryModule(root) {
const flowRegistryApi = root?.MultiPageFlowRegistry || {};
const DEFAULT_FLOW_ID = flowRegistryApi.DEFAULT_FLOW_ID || 'openai';
const DEFAULT_KIRO_TARGET_ID = flowRegistryApi.DEFAULT_KIRO_TARGET_ID || 'kiro-rs';
const DEFAULT_OPENAI_TARGET_ID = flowRegistryApi.DEFAULT_OPENAI_TARGET_ID || 'cpa';
const ADAPTER_DEFINITIONS = Object.freeze({
'openai-oauth': Object.freeze({
id: 'openai-oauth',
flowId: 'openai',
artifactKind: 'openai-oauth',
trigger: 'interactive-oauth',
label: 'OpenAI OAuth 贡献',
defaultTargetId: DEFAULT_OPENAI_TARGET_ID,
sensitiveFieldPaths: Object.freeze([
'credentials.access_token',
'credentials.refresh_token',
'credentials.id_token',
]),
}),
'openai-codex-file': Object.freeze({
id: 'openai-codex-file',
flowId: 'openai',
artifactKind: 'codex',
trigger: 'manual-upload',
label: 'OpenAI Codex 文件贡献',
defaultTargetId: 'codex2api',
sensitiveFieldPaths: Object.freeze([
'credentials.access_token',
'credentials.refresh_token',
'credentials.id_token',
]),
}),
'openai-sub2api-file': Object.freeze({
id: 'openai-sub2api-file',
flowId: 'openai',
artifactKind: 'sub2api',
trigger: 'manual-upload',
label: 'OpenAI Sub2API 文件贡献',
defaultTargetId: 'sub2api',
sensitiveFieldPaths: Object.freeze([
'credentials.accounts[].credentials.access_token',
'credentials.accounts[].credentials.refresh_token',
'credentials.accounts[].credentials.id_token',
]),
}),
'kiro-builder-id': Object.freeze({
id: 'kiro-builder-id',
flowId: 'kiro',
artifactKind: 'kiro-builder-id',
trigger: 'after-desktop-authorize',
label: 'Kiro Builder ID 贡献',
defaultTargetId: DEFAULT_KIRO_TARGET_ID,
sensitiveFieldPaths: Object.freeze([
'credentials.refreshToken',
'credentials.clientSecret',
'metadata.proxyPassword',
]),
}),
});
const FLOW_ADAPTER_IDS = Object.freeze({
openai: Object.freeze(['openai-oauth', 'openai-codex-file', 'openai-sub2api-file']),
kiro: Object.freeze(['kiro-builder-id']),
});
const CONTRIBUTION_TUTORIAL_ENTRIES = Object.freeze({
openai: Object.freeze({
id: 'openai-contribution-tutorial',
flowId: 'openai',
label: '贡献/使用教程',
portalPath: '/tutorial',
defaultTargetId: DEFAULT_OPENAI_TARGET_ID,
contributionAdapterId: 'openai-oauth',
action: 'open-portal-and-enable-contribution',
}),
kiro: Object.freeze({
id: 'kiro-contribution-tutorial',
flowId: 'kiro',
label: '贡献/使用教程',
portalPath: '/tutorial',
defaultTargetId: DEFAULT_KIRO_TARGET_ID,
contributionAdapterId: 'kiro-builder-id',
action: 'open-portal-and-enable-contribution',
}),
});
function normalizeString(value = '') {
return String(value || '').trim();
}
function normalizeFlowId(value = '', fallback = DEFAULT_FLOW_ID) {
const normalized = normalizeString(value).toLowerCase();
if (normalized && Object.prototype.hasOwnProperty.call(FLOW_ADAPTER_IDS, normalized)) {
return normalized;
}
if (!normalized && typeof flowRegistryApi.normalizeFlowId === 'function') {
return flowRegistryApi.normalizeFlowId(value, fallback);
}
return normalized || normalizeString(fallback).toLowerCase() || DEFAULT_FLOW_ID;
}
function normalizeAdapterId(value = '') {
return normalizeString(value).toLowerCase();
}
function normalizeTargetId(flowId = DEFAULT_FLOW_ID, targetId = '') {
if (typeof flowRegistryApi.normalizeTargetId === 'function') {
return flowRegistryApi.normalizeTargetId(flowId, targetId);
}
const normalizedFlowId = normalizeFlowId(flowId, DEFAULT_FLOW_ID);
const normalizedTargetId = normalizeString(targetId).toLowerCase();
if (normalizedTargetId) {
return normalizedTargetId;
}
return normalizedFlowId === 'kiro' ? DEFAULT_KIRO_TARGET_ID : DEFAULT_OPENAI_TARGET_ID;
}
function cloneAdapter(adapter) {
if (!adapter) {
return null;
}
return {
...adapter,
sensitiveFieldPaths: Array.isArray(adapter.sensitiveFieldPaths)
? adapter.sensitiveFieldPaths.slice()
: [],
};
}
function getAdapterDefinition(adapterId = '') {
return cloneAdapter(ADAPTER_DEFINITIONS[normalizeAdapterId(adapterId)] || null);
}
function getContributionAdapterIds(flowId = DEFAULT_FLOW_ID) {
const normalizedFlowId = normalizeFlowId(flowId, DEFAULT_FLOW_ID);
return (FLOW_ADAPTER_IDS[normalizedFlowId] || []).slice();
}
function getContributionAdapters(flowId = DEFAULT_FLOW_ID) {
return getContributionAdapterIds(flowId)
.map((adapterId) => getAdapterDefinition(adapterId))
.filter(Boolean);
}
function getDefaultContributionAdapterId(flowId = DEFAULT_FLOW_ID) {
return getContributionAdapterIds(flowId)[0] || '';
}
function buildPortalPageUrl(portalBaseUrl = '', portalPath = '/tutorial', params = {}) {
const baseUrl = normalizeString(portalBaseUrl).replace(/\/+$/, '') || 'https://flowpilot.qlhazycoder.top';
const path = normalizeString(portalPath) || '/tutorial';
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
const query = Object.entries(params)
.map(([key, value]) => [normalizeString(key), normalizeString(value)])
.filter(([key, value]) => key && value)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
return `${baseUrl}${normalizedPath}${query ? `?${query}` : ''}`;
}
function getContributionTutorialEntry(flowId = DEFAULT_FLOW_ID, options = {}) {
const normalizedFlowId = normalizeFlowId(flowId, DEFAULT_FLOW_ID);
const definition = CONTRIBUTION_TUTORIAL_ENTRIES[normalizedFlowId] || null;
if (!definition) {
return null;
}
const requestedAdapterId = normalizeAdapterId(options.adapterId);
const adapterId = requestedAdapterId && hasContributionAdapter(normalizedFlowId, requestedAdapterId)
? requestedAdapterId
: definition.contributionAdapterId;
const targetId = normalizeTargetId(
normalizedFlowId,
options.targetId || definition.defaultTargetId
);
return {
...definition,
targetId,
contributionAdapterId: adapterId,
portalUrl: buildPortalPageUrl(options.portalBaseUrl, definition.portalPath, {
flow: normalizedFlowId,
target: targetId,
}),
};
}
function hasContributionAdapter(flowId = DEFAULT_FLOW_ID, adapterId = '') {
const normalizedAdapterId = normalizeAdapterId(adapterId);
return Boolean(normalizedAdapterId) && getContributionAdapterIds(flowId).includes(normalizedAdapterId);
}
function hasPublicationTargets(flowId = DEFAULT_FLOW_ID) {
if (typeof flowRegistryApi.getPublicationTargetDefinitions !== 'function') {
return false;
}
const definitions = flowRegistryApi.getPublicationTargetDefinitions(flowId) || {};
return Object.keys(definitions).length > 0;
}
function expectsContributionAdapter(flowId = DEFAULT_FLOW_ID) {
const normalizedFlowId = normalizeFlowId(flowId, DEFAULT_FLOW_ID);
const capabilities = typeof flowRegistryApi.getFlowCapabilities === 'function'
? (flowRegistryApi.getFlowCapabilities(normalizedFlowId) || {})
: {};
return Boolean(
hasPublicationTargets(normalizedFlowId)
|| capabilities.supportsAccountContribution
|| capabilities.supportsContributionMode
|| getContributionAdapterIds(normalizedFlowId).length
);
}
function getPublishedContributionFlowIds(flowIds = undefined) {
const ids = Array.isArray(flowIds)
? flowIds
: (typeof flowRegistryApi.getRegisteredFlowIds === 'function'
? flowRegistryApi.getRegisteredFlowIds()
: Object.keys(FLOW_ADAPTER_IDS));
return ids
.map((flowId) => normalizeString(flowId).toLowerCase())
.filter(Boolean)
.filter((flowId) => expectsContributionAdapter(flowId));
}
function assertPublishedFlowsHaveContributionAdapters(flowIds = undefined) {
const ids = Array.isArray(flowIds)
? flowIds.map((flowId) => normalizeString(flowId).toLowerCase()).filter(Boolean)
: getPublishedContributionFlowIds();
const missing = ids
.filter((flowId) => getContributionAdapterIds(flowId).length === 0);
if (missing.length) {
throw new Error(`缺少账号贡献适配器:${missing.join(', ')}`);
}
return true;
}
return {
ADAPTER_DEFINITIONS,
CONTRIBUTION_TUTORIAL_ENTRIES,
FLOW_ADAPTER_IDS,
assertPublishedFlowsHaveContributionAdapters,
getContributionTutorialEntry,
getAdapterDefinition,
getContributionAdapterIds,
getContributionAdapters,
getDefaultContributionAdapterId,
getPublishedContributionFlowIds,
hasContributionAdapter,
normalizeAdapterId,
normalizeFlowId,
};
});