@fitlab-ai/agent-infra 0.9.12 → 0.9.13
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 +11 -11
- package/README.zh-CN.md +11 -11
- package/dist/bin/internal-cli.js +24 -19
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +9 -9
- package/dist/lib/internal/platform-context.js +2 -2
- package/dist/lib/internal/platform-issue.js +8 -9
- package/dist/lib/internal/platform-pr-review.js +10 -10
- package/dist/lib/internal/platform-pr.js +63 -24
- package/dist/lib/internal/platform-release-notes.js +3 -3
- package/dist/lib/internal/pr-review-grade.js +11 -9
- package/dist/lib/internal/release-workflow.js +4 -4
- package/dist/lib/internal/sandbox-control.js +30 -4
- package/dist/lib/internal/task-activity.js +3 -3
- package/dist/lib/internal/task-artifact.js +76 -24
- package/dist/lib/internal/task-create.js +2 -2
- package/dist/lib/internal/task-event.js +6 -3
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-invalidation.js +60 -0
- package/dist/lib/internal/task-ledger.js +9 -6
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-override.js +4 -4
- package/dist/lib/internal/task-review.js +3 -3
- package/dist/lib/internal/task-verify.js +6 -6
- package/dist/lib/internal/task-warning.js +3 -3
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +1 -1
- package/dist/lib/platform/github-provider.js +903 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +94 -41
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +551 -0
- package/dist/lib/platform/pr-checks.js +119 -104
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +520 -50
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +487 -0
- package/dist/lib/platform/provider-contract.js +95 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +504 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +783 -537
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +11 -12
- package/dist/lib/platform/verification-sync.js +112 -238
- package/dist/lib/pr-review/evidence-grading.js +19 -6
- package/dist/lib/run/index.js +63 -23
- package/dist/lib/sandbox/control/client.js +96 -15
- package/dist/lib/sandbox/control/executor.js +3 -3
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +398 -25
- package/dist/lib/sandbox/control/state.js +204 -1
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +33 -5
- package/dist/lib/task/capabilities.js +239 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-operation.js +12 -1
- package/dist/lib/task/control-authority.js +8 -5
- package/dist/lib/task/create-service.js +8 -8
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +1 -1
- package/dist/lib/task/events.js +297 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +58 -36
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/local-artifact-finalization.js +374 -0
- package/dist/lib/task/orchestration.js +49 -93
- package/dist/lib/task/pr-delivery-fact.js +102 -101
- package/dist/lib/task/review-fingerprint.js +24 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-engine.js +43 -16
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/init.ts +1 -1
- package/lib/internal/platform-checks.ts +3 -3
- package/lib/internal/platform-comment.ts +8 -8
- package/lib/internal/platform-context.ts +2 -2
- package/lib/internal/platform-issue.ts +7 -8
- package/lib/internal/platform-pr-review.ts +9 -9
- package/lib/internal/platform-pr.ts +47 -22
- package/lib/internal/platform-release-notes.ts +3 -3
- package/lib/internal/pr-review-grade.ts +10 -8
- package/lib/internal/release-workflow.ts +4 -4
- package/lib/internal/sandbox-control.ts +27 -3
- package/lib/internal/task-activity.ts +3 -3
- package/lib/internal/task-artifact.ts +65 -22
- package/lib/internal/task-create.ts +2 -2
- package/lib/internal/task-event.ts +6 -3
- package/lib/internal/task-finalization.ts +3 -3
- package/lib/internal/task-invalidation.ts +39 -0
- package/lib/internal/task-ledger.ts +11 -8
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-override.ts +6 -6
- package/lib/internal/task-review.ts +3 -3
- package/lib/internal/task-verify.ts +6 -6
- package/lib/internal/task-warning.ts +3 -3
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +2 -2
- package/lib/platform/github-provider.ts +910 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +97 -47
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +645 -0
- package/lib/platform/pr-checks.ts +126 -106
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +560 -53
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +536 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +529 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +826 -543
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +11 -13
- package/lib/platform/verification-sync.ts +112 -292
- package/lib/pr-review/evidence-grading.ts +25 -8
- package/lib/run/index.ts +57 -22
- package/lib/sandbox/control/client.ts +105 -15
- package/lib/sandbox/control/executor.ts +4 -4
- package/lib/sandbox/control/protocol.ts +113 -0
- package/lib/sandbox/control/server.ts +431 -23
- package/lib/sandbox/control/state.ts +251 -1
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +32 -6
- package/lib/task/capabilities.ts +262 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-operation.ts +12 -1
- package/lib/task/control-authority.ts +11 -8
- package/lib/task/create-service.ts +8 -8
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +6 -2
- package/lib/task/events.ts +296 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +70 -46
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/local-artifact-finalization.ts +563 -0
- package/lib/task/orchestration.ts +52 -85
- package/lib/task/pr-delivery-fact.ts +131 -148
- package/lib/task/review-fingerprint.ts +23 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-engine.ts +52 -17
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +1 -1
- package/lib/version.ts +15 -1
- package/package.json +7 -1
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
- package/templates/.agents/rules/local-artifact-repair.en.md +9 -2
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +9 -2
- package/templates/.agents/rules/pr-sync.en.md +10 -4
- package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/skills/analyze-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/code-task/SKILL.en.md +27 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +29 -10
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +21 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.en.md +4 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +4 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +4 -2
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +18 -1
- package/templates/.agents/templates/task.zh-CN.md +18 -1
- package/templates/.github/workflows/pr-label.yml +7 -26
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
PLATFORM_PROVIDER_CONTRACT_VERSION,
|
|
10
|
+
isPlatformProviderFactory,
|
|
11
|
+
validatePlatformProvider
|
|
12
|
+
} from './provider-contract.ts';
|
|
13
|
+
import type {
|
|
14
|
+
JsonValue,
|
|
15
|
+
PlatformError,
|
|
16
|
+
PlatformProvider,
|
|
17
|
+
PlatformProviderFactoryInput
|
|
18
|
+
} from './provider-contract.ts';
|
|
19
|
+
import { createGitHubProvider } from './github-provider.ts';
|
|
20
|
+
import { createNoneProvider } from './none-provider.ts';
|
|
21
|
+
import { wrapProviderOperations } from './provider-validation.ts';
|
|
22
|
+
import type { PlatformClient } from './context.ts';
|
|
23
|
+
|
|
24
|
+
type ProviderLoaderOptions = {
|
|
25
|
+
cwd?: string;
|
|
26
|
+
platformType?: string;
|
|
27
|
+
client?: PlatformClient;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type LoadedPlatformProvider = {
|
|
31
|
+
provider: PlatformProvider;
|
|
32
|
+
providerType: string;
|
|
33
|
+
repositoryRoot: string;
|
|
34
|
+
workingDirectory: string;
|
|
35
|
+
sourceIdentity: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type ProviderLoadResult =
|
|
39
|
+
| { ok: true; value: LoadedPlatformProvider }
|
|
40
|
+
| { ok: false; error: PlatformError };
|
|
41
|
+
|
|
42
|
+
type ResolvedExternalSource = {
|
|
43
|
+
sourceIdentity: string;
|
|
44
|
+
importUrl: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const sessions = new Map<string, Promise<PlatformProvider>>();
|
|
48
|
+
|
|
49
|
+
function findRepositoryRoot(cwd: string): string {
|
|
50
|
+
try {
|
|
51
|
+
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
52
|
+
cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
|
|
53
|
+
}).trim();
|
|
54
|
+
} catch {
|
|
55
|
+
return cwd;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function failure(
|
|
60
|
+
providerType: string,
|
|
61
|
+
phase: string,
|
|
62
|
+
code: string,
|
|
63
|
+
message: string
|
|
64
|
+
): ProviderLoadResult {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
error: { code, message, retryable: false, providerType, phase }
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isJsonValue(value: unknown): value is JsonValue {
|
|
72
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;
|
|
73
|
+
if (typeof value === 'number') return Number.isFinite(value);
|
|
74
|
+
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
75
|
+
if (!value || typeof value !== 'object') return false;
|
|
76
|
+
return Object.values(value).every(isJsonValue);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function canonicalJson(value: JsonValue): string {
|
|
80
|
+
if (Array.isArray(value)) return '[' + value.map(canonicalJson).join(',') + ']';
|
|
81
|
+
if (value && typeof value === 'object') {
|
|
82
|
+
return '{' + Object.keys(value).sort().map((key) =>
|
|
83
|
+
JSON.stringify(key) + ':' + canonicalJson(value[key]!)
|
|
84
|
+
).join(',') + '}';
|
|
85
|
+
}
|
|
86
|
+
return JSON.stringify(value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function configFingerprint(config: Readonly<Record<string, JsonValue>>): string {
|
|
90
|
+
return createHash('sha256').update(canonicalJson(config)).digest('hex');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function readPlatformConfig(repositoryRoot: string): Record<string, unknown> {
|
|
94
|
+
try {
|
|
95
|
+
const file = path.join(repositoryRoot, '.agents', '.airc.json');
|
|
96
|
+
const value: unknown = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
97
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
98
|
+
? value as Record<string, unknown>
|
|
99
|
+
: {};
|
|
100
|
+
} catch {
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function selectedEntry(
|
|
106
|
+
config: Record<string, unknown>,
|
|
107
|
+
providerType: string
|
|
108
|
+
): { source?: unknown; config?: unknown } {
|
|
109
|
+
const platform = config.platform && typeof config.platform === 'object'
|
|
110
|
+
? config.platform as Record<string, unknown>
|
|
111
|
+
: {};
|
|
112
|
+
const providers = platform.providers && typeof platform.providers === 'object' && !Array.isArray(platform.providers)
|
|
113
|
+
? platform.providers as Record<string, unknown>
|
|
114
|
+
: {};
|
|
115
|
+
const entry = providers[providerType];
|
|
116
|
+
return entry && typeof entry === 'object' && !Array.isArray(entry)
|
|
117
|
+
? entry as { source?: unknown; config?: unknown }
|
|
118
|
+
: {};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function resolveExternalSource(
|
|
122
|
+
repositoryRoot: string,
|
|
123
|
+
source: string,
|
|
124
|
+
providerType: string
|
|
125
|
+
): { ok: false; error: PlatformError } | { ok: true; value: ResolvedExternalSource } {
|
|
126
|
+
try {
|
|
127
|
+
const isPath = source.startsWith('.') || path.isAbsolute(source) || /^[A-Za-z]:[\\/]/u.test(source);
|
|
128
|
+
const resolved = isPath
|
|
129
|
+
? path.resolve(repositoryRoot, source)
|
|
130
|
+
: createRequire(path.join(repositoryRoot, 'package.json')).resolve(source);
|
|
131
|
+
const realPath = fs.realpathSync(resolved);
|
|
132
|
+
const importUrl = pathToFileURL(realPath).href;
|
|
133
|
+
return { ok: true, value: { sourceIdentity: importUrl, importUrl } };
|
|
134
|
+
} catch {
|
|
135
|
+
return {
|
|
136
|
+
ok: false,
|
|
137
|
+
error: {
|
|
138
|
+
code: 'PLATFORM_PROVIDER_SOURCE_RESOLUTION_FAILED',
|
|
139
|
+
message: 'Selected provider source could not be resolved',
|
|
140
|
+
retryable: false,
|
|
141
|
+
providerType,
|
|
142
|
+
phase: 'source-resolution'
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function instantiateProvider(
|
|
149
|
+
providerType: string,
|
|
150
|
+
repositoryRoot: string,
|
|
151
|
+
importUrl: string | null,
|
|
152
|
+
config: Readonly<Record<string, JsonValue>>,
|
|
153
|
+
client?: PlatformClient
|
|
154
|
+
): Promise<PlatformProvider> {
|
|
155
|
+
const input: PlatformProviderFactoryInput = {
|
|
156
|
+
providerType,
|
|
157
|
+
contractVersion: PLATFORM_PROVIDER_CONTRACT_VERSION,
|
|
158
|
+
repositoryRoot,
|
|
159
|
+
config
|
|
160
|
+
};
|
|
161
|
+
if (providerType === 'github') return createGitHubProvider(input, client as never);
|
|
162
|
+
if (providerType === 'none') return createNoneProvider(input);
|
|
163
|
+
|
|
164
|
+
let moduleValue: Record<string, unknown>;
|
|
165
|
+
try {
|
|
166
|
+
moduleValue = await import(importUrl!);
|
|
167
|
+
} catch {
|
|
168
|
+
throw Object.assign(new Error('Selected provider source could not be imported'), {
|
|
169
|
+
code: 'PLATFORM_PROVIDER_IMPORT_FAILED'
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const factory = moduleValue.default;
|
|
173
|
+
if (!isPlatformProviderFactory(factory)) {
|
|
174
|
+
throw Object.assign(new Error('Selected provider must default-export an async factory'), {
|
|
175
|
+
code: 'PLATFORM_PROVIDER_EXPORT_INVALID'
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
let provider: unknown;
|
|
179
|
+
try {
|
|
180
|
+
const candidate = factory(input);
|
|
181
|
+
if (!candidate || typeof (candidate as PromiseLike<unknown>).then !== 'function') {
|
|
182
|
+
throw Object.assign(new Error('Provider factory must return a Promise'), {
|
|
183
|
+
code: 'PLATFORM_PROVIDER_EXPORT_INVALID'
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
provider = await candidate;
|
|
187
|
+
} catch (cause) {
|
|
188
|
+
if (cause && typeof cause === 'object' && 'code' in cause
|
|
189
|
+
&& cause.code === 'PLATFORM_PROVIDER_EXPORT_INVALID') throw cause;
|
|
190
|
+
throw Object.assign(new Error('Provider factory failed'), {
|
|
191
|
+
code: 'PLATFORM_PROVIDER_FACTORY_FAILED'
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (provider && typeof provider === 'object' && 'type' in provider && provider.type !== providerType) {
|
|
195
|
+
throw Object.assign(new Error('Provider type does not match platform.type'), {
|
|
196
|
+
code: 'PLATFORM_PROVIDER_TYPE_MISMATCH'
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (provider && typeof provider === 'object' && 'contractVersion' in provider
|
|
200
|
+
&& provider.contractVersion !== PLATFORM_PROVIDER_CONTRACT_VERSION) {
|
|
201
|
+
throw Object.assign(new Error('Provider contract version is unsupported'), {
|
|
202
|
+
code: 'PLATFORM_PROVIDER_VERSION_UNSUPPORTED'
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const validated = validatePlatformProvider(provider, providerType);
|
|
206
|
+
if (!validated.ok) {
|
|
207
|
+
throw Object.assign(new Error(validated.error.message), {
|
|
208
|
+
code: validated.error.code
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return wrapProviderOperations(validated.value);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function loadPlatformProvider(options: ProviderLoaderOptions = {}): Promise<ProviderLoadResult> {
|
|
215
|
+
const workingDirectory = path.resolve(options.cwd || process.cwd());
|
|
216
|
+
const repositoryRoot = path.resolve(findRepositoryRoot(workingDirectory));
|
|
217
|
+
const config = readPlatformConfig(repositoryRoot);
|
|
218
|
+
const platform = config.platform && typeof config.platform === 'object'
|
|
219
|
+
? config.platform as Record<string, unknown>
|
|
220
|
+
: {};
|
|
221
|
+
const providerType = options.platformType
|
|
222
|
+
?? (typeof platform.type === 'string' ? platform.type : '');
|
|
223
|
+
if (!providerType) return failure('', 'config', 'PLATFORM_PROVIDER_CONFIG_INVALID', 'platform.type must be a non-empty string');
|
|
224
|
+
|
|
225
|
+
const entry = selectedEntry(config, providerType);
|
|
226
|
+
const rawConfig = entry.config === undefined ? {} : entry.config;
|
|
227
|
+
if (!rawConfig || typeof rawConfig !== 'object' || Array.isArray(rawConfig) || !isJsonValue(rawConfig)) {
|
|
228
|
+
return failure(providerType, 'config', 'PLATFORM_PROVIDER_CONFIG_INVALID', 'Selected provider config must be a JSON object');
|
|
229
|
+
}
|
|
230
|
+
const providerConfig = rawConfig as Record<string, JsonValue>;
|
|
231
|
+
let sourceIdentity: string;
|
|
232
|
+
let importUrl: string | null = null;
|
|
233
|
+
if (providerType === 'github' || providerType === 'none') {
|
|
234
|
+
sourceIdentity = 'builtin:' + providerType + '@' + PLATFORM_PROVIDER_CONTRACT_VERSION;
|
|
235
|
+
} else {
|
|
236
|
+
if (typeof entry.source !== 'string' || !entry.source.trim()) {
|
|
237
|
+
return failure(providerType, 'config', 'PLATFORM_PROVIDER_SOURCE_MISSING', 'Selected provider source is required');
|
|
238
|
+
}
|
|
239
|
+
const resolved = resolveExternalSource(repositoryRoot, entry.source, providerType);
|
|
240
|
+
if (!resolved.ok) return resolved;
|
|
241
|
+
sourceIdentity = resolved.value.sourceIdentity;
|
|
242
|
+
importUrl = resolved.value.importUrl;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const key = [
|
|
246
|
+
repositoryRoot,
|
|
247
|
+
providerType,
|
|
248
|
+
sourceIdentity,
|
|
249
|
+
configFingerprint(providerConfig)
|
|
250
|
+
].join('\u0000');
|
|
251
|
+
const reuseSession = !(providerType === 'github' && options.client);
|
|
252
|
+
let session = reuseSession ? sessions.get(key) : undefined;
|
|
253
|
+
if (!session) {
|
|
254
|
+
session = instantiateProvider(providerType, repositoryRoot, importUrl, providerConfig, options.client);
|
|
255
|
+
if (reuseSession) {
|
|
256
|
+
sessions.set(key, session);
|
|
257
|
+
session.catch(() => {
|
|
258
|
+
if (sessions.get(key) === session) sessions.delete(key);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
const provider = await session;
|
|
264
|
+
return {
|
|
265
|
+
ok: true,
|
|
266
|
+
value: { provider, providerType, repositoryRoot, workingDirectory, sourceIdentity }
|
|
267
|
+
};
|
|
268
|
+
} catch (cause) {
|
|
269
|
+
const code = cause && typeof cause === 'object' && 'code' in cause && typeof cause.code === 'string'
|
|
270
|
+
? cause.code
|
|
271
|
+
: 'PLATFORM_PROVIDER_FACTORY_FAILED';
|
|
272
|
+
const phase = code === 'PLATFORM_PROVIDER_IMPORT_FAILED'
|
|
273
|
+
? 'import'
|
|
274
|
+
: code === 'PLATFORM_PROVIDER_EXPORT_INVALID'
|
|
275
|
+
? 'export'
|
|
276
|
+
: code === 'PLATFORM_PROVIDER_TYPE_MISMATCH' || code === 'PLATFORM_PROVIDER_VERSION_UNSUPPORTED' || code === 'PLATFORM_PROVIDER_CONTRACT_INVALID'
|
|
277
|
+
? 'validation'
|
|
278
|
+
: 'factory';
|
|
279
|
+
const messages: Record<string, string> = {
|
|
280
|
+
PLATFORM_PROVIDER_IMPORT_FAILED: 'Selected provider source could not be imported',
|
|
281
|
+
PLATFORM_PROVIDER_EXPORT_INVALID: 'Selected provider must default-export an async factory',
|
|
282
|
+
PLATFORM_PROVIDER_TYPE_MISMATCH: 'Provider type does not match platform.type',
|
|
283
|
+
PLATFORM_PROVIDER_VERSION_UNSUPPORTED: 'Provider contract version is unsupported',
|
|
284
|
+
PLATFORM_PROVIDER_CONTRACT_INVALID: 'Provider does not satisfy the platform provider contract',
|
|
285
|
+
PLATFORM_PROVIDER_FACTORY_FAILED: 'Selected provider factory failed'
|
|
286
|
+
};
|
|
287
|
+
return failure(providerType, phase, code, messages[code] ?? 'Selected provider failed to load');
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function clearProviderSessions(): void {
|
|
292
|
+
sessions.clear();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export {
|
|
296
|
+
canonicalJson,
|
|
297
|
+
clearProviderSessions,
|
|
298
|
+
configFingerprint,
|
|
299
|
+
findRepositoryRoot,
|
|
300
|
+
loadPlatformProvider
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export type {
|
|
304
|
+
LoadedPlatformProvider,
|
|
305
|
+
ProviderLoadResult,
|
|
306
|
+
ProviderLoaderOptions
|
|
307
|
+
};
|