@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.0-beta.20
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/AUTHORING.md +487 -0
- package/CHANGELOG.md +85 -0
- package/README.md +49 -3
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-check.ts +44 -59
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +27 -52
- package/bin/apifuse-pack-check.ts +14 -0
- package/bin/apifuse-pack-smoke.ts +36 -81
- package/bin/apifuse-pack-types.ts +305 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +659 -111
- package/bin/apifuse-submit-check.ts +546 -48
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/apifuse.ts +1 -1
- package/bin/submit-check-delimited-text.ts +50 -0
- package/bin/submit-check-xml-semantics.ts +204 -0
- package/bin/submit-check-xml.ts +134 -0
- package/dist/auth-turn/index.d.ts +3 -3
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +9 -18
- package/dist/ceremonies/index.d.ts +1 -1
- package/dist/ceremonies/index.js +8 -18
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +8 -0
- package/dist/cli/create.d.ts +3 -0
- package/dist/cli/create.js +34 -35
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/dist/config/loader.d.ts +176 -8
- package/dist/config/loader.js +424 -95
- package/dist/contract-serialization.d.ts +2 -2
- package/dist/contract-serialization.js +3 -6
- package/dist/contract-types.d.ts +2 -2
- package/dist/contract.d.ts +3 -3
- package/dist/contract.js +4 -6
- package/dist/define.d.ts +9 -1
- package/dist/define.js +208 -125
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/error-resolution.d.ts +2 -0
- package/dist/error-resolution.js +90 -0
- package/dist/errors.d.ts +22 -1
- package/dist/errors.js +88 -0
- package/dist/fixture-sanitization.d.ts +26 -0
- package/dist/fixture-sanitization.js +216 -0
- package/dist/i18n/catalog.d.ts +2 -2
- package/dist/i18n/catalog.js +4 -10
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/keys.d.ts +2 -2
- package/dist/index.d.ts +46 -42
- package/dist/index.js +41 -37
- package/dist/lint.js +8 -15
- package/dist/native-address.d.ts +43 -0
- package/dist/native-address.js +281 -0
- package/dist/native-egress-policy.d.ts +31 -0
- package/dist/native-egress-policy.js +288 -0
- package/dist/observability.d.ts +5 -2
- package/dist/observability.js +48 -1
- package/dist/provider.d.ts +12 -11
- package/dist/provider.js +10 -9
- package/dist/public-schema-field-lint.d.ts +1 -1
- package/dist/recipes/gov-api.js +1 -1
- package/dist/runtime/auth-flow.d.ts +1 -1
- package/dist/runtime/auth-flow.js +3 -3
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +15 -29
- package/dist/runtime/cache.d.ts +1 -1
- package/dist/runtime/cache.js +4 -8
- package/dist/runtime/choice.d.ts +1 -1
- package/dist/runtime/choice.js +31 -35
- package/dist/runtime/credential.d.ts +1 -1
- package/dist/runtime/credential.js +1 -1
- package/dist/runtime/env.d.ts +1 -1
- package/dist/runtime/executor.d.ts +1 -1
- package/dist/runtime/executor.js +31 -4
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +387 -47
- package/dist/runtime/insights.d.ts +1 -1
- package/dist/runtime/insights.js +6 -13
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +345 -22
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/native-network.d.ts +127 -0
- package/dist/runtime/native-network.js +1298 -0
- package/dist/runtime/otlp.d.ts +1 -1
- package/dist/runtime/perf.d.ts +1 -1
- package/dist/runtime/provider.d.ts +1 -1
- package/dist/runtime/provider.js +1 -2
- package/dist/runtime/proxy-errors.d.ts +1 -1
- package/dist/runtime/proxy-errors.js +9 -7
- package/dist/runtime/proxy-nodemaven.d.ts +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- package/dist/runtime/proxy-retry-policy.d.ts +2 -2
- package/dist/runtime/proxy-retry-policy.js +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- package/dist/runtime/redirects.d.ts +29 -0
- package/dist/runtime/redirects.js +36 -0
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +68 -1
- package/dist/runtime/request-options.js +548 -0
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +2 -2
- package/dist/runtime/state.js +15 -4
- package/dist/runtime/stealth.d.ts +5 -3
- package/dist/runtime/stealth.js +423 -143
- package/dist/runtime/stt.d.ts +1 -1
- package/dist/runtime/stt.js +11 -15
- package/dist/runtime/trace.d.ts +2 -2
- package/dist/runtime/trace.js +2 -4
- package/dist/runtime/waterfall.d.ts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +7 -15
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/index.d.ts +7 -7
- package/dist/server/index.js +6 -6
- package/dist/server/self-test-input-tokens.d.ts +2 -1
- package/dist/server/self-test-input-tokens.js +18 -14
- package/dist/server/self-test-redaction.d.ts +1 -1
- package/dist/server/self-test-redaction.js +1 -1
- package/dist/server/self-test.d.ts +104 -3
- package/dist/server/self-test.js +673 -115
- package/dist/server/serve.d.ts +115 -4
- package/dist/server/serve.js +792 -127
- package/dist/server/types.d.ts +30 -9
- package/dist/server/types.js +8 -7
- package/dist/stateful/errors.d.ts +14 -0
- package/dist/stateful/errors.js +14 -0
- package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
- package/dist/stateful/http-provider-event-emitter.js +237 -0
- package/dist/stateful/http-session-owner-registry.d.ts +44 -0
- package/dist/stateful/http-session-owner-registry.js +210 -0
- package/dist/stateful/index.d.ts +18 -0
- package/dist/stateful/index.js +18 -0
- package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
- package/dist/stateful/provider-event-delivery-failures.js +43 -0
- package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
- package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
- package/dist/stateful/provider-event-pipeline.d.ts +50 -0
- package/dist/stateful/provider-event-pipeline.js +1 -0
- package/dist/stateful/provider-events.d.ts +101 -0
- package/dist/stateful/provider-events.js +289 -0
- package/dist/stateful/session-key.d.ts +15 -0
- package/dist/stateful/session-key.js +86 -0
- package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
- package/dist/stateful/stateful-provider-adapter-context.js +42 -0
- package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
- package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
- package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
- package/dist/stateful/stateful-provider-adapter.js +287 -0
- package/dist/stateful/stateful-provider-observability.d.ts +62 -0
- package/dist/stateful/stateful-provider-observability.js +161 -0
- package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
- package/dist/stateful/stateful-provider-runtime-context.js +60 -0
- package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
- package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
- package/dist/stateful/stateful-provider-session-routing.js +353 -0
- package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
- package/dist/stateful/stateful-provider-session-runtime.js +245 -0
- package/dist/stateful-signing.d.ts +18 -0
- package/dist/stateful-signing.js +27 -0
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- package/dist/stream.d.ts +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/testing/run.d.ts +32 -2
- package/dist/testing/run.js +477 -33
- package/dist/types.d.ts +323 -11
- package/dist/types.js +1 -0
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +16 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +24 -78
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +42 -35
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/config/loader.ts +652 -204
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +368 -504
- package/src/dev.ts +4 -9
- package/src/error-resolution.ts +91 -0
- package/src/errors.ts +110 -5
- package/src/fixture-sanitization.ts +247 -0
- package/src/i18n/catalog.ts +10 -32
- package/src/i18n/index.ts +2 -2
- package/src/i18n/keys.ts +5 -11
- package/src/index.ts +111 -42
- package/src/lint.ts +89 -159
- package/src/native-address.ts +340 -0
- package/src/native-egress-policy.ts +358 -0
- package/src/observability.ts +51 -1
- package/src/provider.ts +65 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +5 -7
- package/src/runtime/browser.ts +64 -187
- package/src/runtime/cache.ts +20 -67
- package/src/runtime/choice.ts +79 -132
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +43 -20
- package/src/runtime/http.ts +494 -57
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +495 -66
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/native-network.ts +1686 -0
- package/src/runtime/otlp.ts +12 -23
- package/src/runtime/perf.ts +1 -1
- package/src/runtime/provider.ts +4 -9
- package/src/runtime/proxy-errors.ts +29 -42
- package/src/runtime/proxy-nodemaven.ts +221 -0
- package/src/runtime/proxy-retry-policy.ts +3 -3
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +679 -9
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +41 -110
- package/src/runtime/stealth.ts +505 -154
- package/src/runtime/stt.ts +38 -94
- package/src/runtime/trace.ts +14 -44
- package/src/runtime/waterfall.ts +5 -18
- package/src/schema.ts +23 -84
- package/src/serve.ts +1 -1
- package/src/server/index.ts +29 -7
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +857 -132
- package/src/server/serve.ts +1141 -324
- package/src/server/types.ts +12 -19
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +23 -0
- package/src/stateful/http-provider-event-emitter.ts +314 -0
- package/src/stateful/http-session-owner-registry.ts +306 -0
- package/src/stateful/index.ts +18 -0
- package/src/stateful/provider-event-delivery-failures.ts +80 -0
- package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
- package/src/stateful/provider-event-pipeline.ts +61 -0
- package/src/stateful/provider-events.ts +462 -0
- package/src/stateful/session-key.ts +111 -0
- package/src/stateful/stateful-provider-adapter-context.ts +59 -0
- package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
- package/src/stateful/stateful-provider-adapter.ts +562 -0
- package/src/stateful/stateful-provider-observability.ts +261 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
- package/src/stateful/stateful-provider-runtime-context.ts +92 -0
- package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
- package/src/stateful/stateful-provider-session-routing.ts +555 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +10 -26
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +10 -2
- package/src/testing/run.ts +652 -79
- package/src/types.ts +369 -11
- package/src/user-input.ts +118 -0
- package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
- package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
- /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
package/src/auth.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthError, ProviderError } from "./errors";
|
|
1
|
+
import { AuthError, ProviderError } from "./errors.js";
|
|
2
2
|
import type {
|
|
3
3
|
AuthAbortData,
|
|
4
4
|
AuthConfig,
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
CredentialDeclaration,
|
|
11
11
|
FlowContext,
|
|
12
12
|
ProviderLocaleKeyInput,
|
|
13
|
-
} from "./types";
|
|
13
|
+
} from "./types.js";
|
|
14
14
|
|
|
15
15
|
const CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY = "__credentialsAuthChallenge";
|
|
16
16
|
const DEFAULT_COMPLETE_TURN_ID = "auth.complete";
|
|
@@ -33,11 +33,7 @@ function isSensitiveAuthDataKey(key: string): boolean {
|
|
|
33
33
|
return SENSITIVE_ABORT_DATA_KEY_PATTERN.test(normalizedAuthDataKey(key));
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export type CredentialsAuthFieldType =
|
|
37
|
-
| "string"
|
|
38
|
-
| "email"
|
|
39
|
-
| "password"
|
|
40
|
-
| "otp";
|
|
36
|
+
export type CredentialsAuthFieldType = "string" | "email" | "password" | "otp";
|
|
41
37
|
|
|
42
38
|
export interface CredentialsAuthField {
|
|
43
39
|
type?: CredentialsAuthFieldType;
|
|
@@ -138,10 +134,9 @@ function abortData(options: {
|
|
|
138
134
|
};
|
|
139
135
|
}
|
|
140
136
|
|
|
141
|
-
export function createAuthFlowHelpers(
|
|
142
|
-
readonly signal?: AbortSignal;
|
|
143
|
-
|
|
144
|
-
} = {}): AuthFlowTerminalContext {
|
|
137
|
+
export function createAuthFlowHelpers(
|
|
138
|
+
options: { readonly signal?: AbortSignal; readonly deadline?: string } = {},
|
|
139
|
+
): AuthFlowTerminalContext {
|
|
145
140
|
return {
|
|
146
141
|
...(options.signal ? { signal: options.signal } : {}),
|
|
147
142
|
...(options.deadline ? { deadline: options.deadline } : {}),
|
|
@@ -156,16 +151,7 @@ export function createAuthFlowHelpers(options: {
|
|
|
156
151
|
},
|
|
157
152
|
};
|
|
158
153
|
},
|
|
159
|
-
abort({
|
|
160
|
-
code,
|
|
161
|
-
message,
|
|
162
|
-
retry,
|
|
163
|
-
actionHint,
|
|
164
|
-
fieldErrors,
|
|
165
|
-
data,
|
|
166
|
-
turnId,
|
|
167
|
-
expiresAt,
|
|
168
|
-
}) {
|
|
154
|
+
abort({ code, message, retry, actionHint, fieldErrors, data, turnId, expiresAt }) {
|
|
169
155
|
return {
|
|
170
156
|
kind: "abort",
|
|
171
157
|
...authTurnBase({ turnId, defaultTurnId: DEFAULT_ABORT_TURN_ID, expiresAt }),
|
|
@@ -190,8 +176,7 @@ export function createAuthFlowHelpers(options: {
|
|
|
190
176
|
...(options.hintKey ? { hintKey: options.hintKey } : {}),
|
|
191
177
|
...(options.timing ? { timing: options.timing } : {}),
|
|
192
178
|
...(options.data ? { data: options.data } : {}),
|
|
193
|
-
expectedInput:
|
|
194
|
-
options.expectedInput ?? expectedInputFromFields(options.fields ?? {}),
|
|
179
|
+
expectedInput: options.expectedInput ?? expectedInputFromFields(options.fields ?? {}),
|
|
195
180
|
};
|
|
196
181
|
},
|
|
197
182
|
nextPoll(options = {}) {
|
|
@@ -223,9 +208,7 @@ export class AuthAbortError extends AuthError {
|
|
|
223
208
|
}
|
|
224
209
|
}
|
|
225
210
|
|
|
226
|
-
export interface CredentialsAuthCompleteResult<
|
|
227
|
-
TCredentialKeys extends readonly string[],
|
|
228
|
-
> {
|
|
211
|
+
export interface CredentialsAuthCompleteResult<TCredentialKeys extends readonly string[]> {
|
|
229
212
|
credential: CredentialsAuthCredential<TCredentialKeys>;
|
|
230
213
|
/** Additional non-credential auth-flow data to return alongside credential. */
|
|
231
214
|
data?: Record<string, unknown>;
|
|
@@ -247,9 +230,7 @@ export interface CredentialsAuthChallengeRequest<TChallengeId extends string = s
|
|
|
247
230
|
export type CredentialsAuthLoginResult<
|
|
248
231
|
TCredentialKeys extends readonly string[],
|
|
249
232
|
TChallengeId extends string = string,
|
|
250
|
-
> =
|
|
251
|
-
| CredentialsAuthCompleteResult<TCredentialKeys>
|
|
252
|
-
| CredentialsAuthChallengeRequest<TChallengeId>;
|
|
233
|
+
> = CredentialsAuthCompleteResult<TCredentialKeys> | CredentialsAuthChallengeRequest<TChallengeId>;
|
|
253
234
|
|
|
254
235
|
export interface CredentialsAuthChallengeDefinition<
|
|
255
236
|
TFields extends CredentialsAuthFields,
|
|
@@ -351,12 +332,8 @@ function expectedInputFromFields(fields: CredentialsAuthFields): Record<string,
|
|
|
351
332
|
...(field.type === "password" ? { format: "password" } : {}),
|
|
352
333
|
...(field.type === "otp" ? { format: "otp" } : {}),
|
|
353
334
|
...(field.labelKey ? { nameKey: field.labelKey } : {}),
|
|
354
|
-
...(field.descriptionKey
|
|
355
|
-
|
|
356
|
-
: {}),
|
|
357
|
-
...(field.placeholderKey
|
|
358
|
-
? { placeholderKey: field.placeholderKey }
|
|
359
|
-
: {}),
|
|
335
|
+
...(field.descriptionKey ? { descriptionKey: field.descriptionKey } : {}),
|
|
336
|
+
...(field.placeholderKey ? { placeholderKey: field.placeholderKey } : {}),
|
|
360
337
|
...(field.sensitive || field.type === "password" || field.type === "otp"
|
|
361
338
|
? { sensitive: true }
|
|
362
339
|
: {}),
|
|
@@ -437,10 +414,7 @@ function getPendingChallenge(ctx: FlowContext): PendingCredentialsAuthChallenge
|
|
|
437
414
|
};
|
|
438
415
|
}
|
|
439
416
|
|
|
440
|
-
function setPendingChallenge(
|
|
441
|
-
ctx: FlowContext,
|
|
442
|
-
challenge: PendingCredentialsAuthChallenge,
|
|
443
|
-
): void {
|
|
417
|
+
function setPendingChallenge(ctx: FlowContext, challenge: PendingCredentialsAuthChallenge): void {
|
|
444
418
|
ctx.context.set(CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, challenge);
|
|
445
419
|
}
|
|
446
420
|
|
|
@@ -473,9 +447,7 @@ function retryTurn(
|
|
|
473
447
|
turnId: retryTurnId,
|
|
474
448
|
expectedInput,
|
|
475
449
|
data: {
|
|
476
|
-
fieldErrors: Object.fromEntries(
|
|
477
|
-
missing.map((name) => [name, "Required"]),
|
|
478
|
-
),
|
|
450
|
+
fieldErrors: Object.fromEntries(missing.map((name) => [name, "Required"])),
|
|
479
451
|
fieldErrorKeys: Object.fromEntries(
|
|
480
452
|
missing.map((name) => [name, "auth.credentials.fieldRequired"]),
|
|
481
453
|
),
|
|
@@ -499,18 +471,12 @@ function completeTurn<TCredentialKeys extends readonly string[]>(
|
|
|
499
471
|
typeof result.credential !== "object" ||
|
|
500
472
|
Array.isArray(result.credential)
|
|
501
473
|
) {
|
|
502
|
-
throw new ProviderError(
|
|
503
|
-
|
|
504
|
-
{
|
|
505
|
-
|
|
506
|
-
fix: "Return { credential: { ... } } from defineCredentialsAuth handlers. Gateway persists only auth.flow complete data.credential into the connection.",
|
|
507
|
-
},
|
|
508
|
-
);
|
|
474
|
+
throw new ProviderError("Credentials auth login completed without a credential object", {
|
|
475
|
+
code: "credentials_auth_missing_credential",
|
|
476
|
+
fix: "Return { credential: { ... } } from defineCredentialsAuth handlers. Gateway persists only auth.flow complete data.credential into the connection.",
|
|
477
|
+
});
|
|
509
478
|
}
|
|
510
|
-
assertCredentialKeys(
|
|
511
|
-
credentialKeys,
|
|
512
|
-
result.credential as Record<string, unknown>,
|
|
513
|
-
);
|
|
479
|
+
assertCredentialKeys(credentialKeys, result.credential as Record<string, unknown>);
|
|
514
480
|
return {
|
|
515
481
|
kind: "complete",
|
|
516
482
|
turnId: result.turnId ?? defaultTurnId,
|
|
@@ -523,24 +489,18 @@ function completeTurn<TCredentialKeys extends readonly string[]>(
|
|
|
523
489
|
}
|
|
524
490
|
|
|
525
491
|
function challengeTurn(
|
|
526
|
-
definition: CredentialsAuthChallengeDefinition<
|
|
527
|
-
CredentialsAuthFields,
|
|
528
|
-
readonly string[],
|
|
529
|
-
string
|
|
530
|
-
>,
|
|
492
|
+
definition: CredentialsAuthChallengeDefinition<CredentialsAuthFields, readonly string[], string>,
|
|
531
493
|
request: CredentialsAuthChallengeRequest<string>,
|
|
532
494
|
): AuthTurn {
|
|
533
|
-
const expectedInput = definition.fields
|
|
534
|
-
? expectedInputFromFields(definition.fields)
|
|
535
|
-
: undefined;
|
|
495
|
+
const expectedInput = definition.fields ? expectedInputFromFields(definition.fields) : undefined;
|
|
536
496
|
return {
|
|
537
497
|
kind: expectedInput ? "form" : "pending",
|
|
538
498
|
turnId: request.turnId ?? definition.turnId ?? `credentials.${request.challengeId}`,
|
|
539
499
|
...(request.expiresAt ? { expiresAt: request.expiresAt } : {}),
|
|
540
|
-
...(request.hintKey ?? definition.hintKey
|
|
500
|
+
...((request.hintKey ?? definition.hintKey)
|
|
541
501
|
? { hintKey: request.hintKey ?? definition.hintKey }
|
|
542
502
|
: {}),
|
|
543
|
-
...(request.timing ?? definition.timing
|
|
503
|
+
...((request.timing ?? definition.timing)
|
|
544
504
|
? { timing: request.timing ?? definition.timing }
|
|
545
505
|
: {}),
|
|
546
506
|
...(expectedInput ? { expectedInput } : {}),
|
|
@@ -630,13 +590,7 @@ async function continuePendingChallenge<TCredentialKeys extends readonly string[
|
|
|
630
590
|
normalizeInput(definition.fields, rawInput),
|
|
631
591
|
pending.state,
|
|
632
592
|
);
|
|
633
|
-
return await resolveAuthResult(
|
|
634
|
-
ctx,
|
|
635
|
-
credentialKeys,
|
|
636
|
-
challenges,
|
|
637
|
-
result,
|
|
638
|
-
completeTurnId,
|
|
639
|
-
);
|
|
593
|
+
return await resolveAuthResult(ctx, credentialKeys, challenges, result, completeTurnId);
|
|
640
594
|
}
|
|
641
595
|
|
|
642
596
|
async function pollPendingChallenge<TCredentialKeys extends readonly string[]>(
|
|
@@ -669,13 +623,7 @@ async function pollPendingChallenge<TCredentialKeys extends readonly string[]>(
|
|
|
669
623
|
`credentials.${pending.challengeId}.pending`,
|
|
670
624
|
};
|
|
671
625
|
}
|
|
672
|
-
return await resolveAuthResult(
|
|
673
|
-
ctx,
|
|
674
|
-
credentialKeys,
|
|
675
|
-
challenges,
|
|
676
|
-
result,
|
|
677
|
-
completeTurnId,
|
|
678
|
-
);
|
|
626
|
+
return await resolveAuthResult(ctx, credentialKeys, challenges, result, completeTurnId);
|
|
679
627
|
}
|
|
680
628
|
|
|
681
629
|
export function defineCredentialsAuth<
|
|
@@ -697,7 +645,7 @@ export function defineCredentialsAuth<
|
|
|
697
645
|
): DefinedCredentialsAuth {
|
|
698
646
|
if (Object.keys(options.fields).length === 0) {
|
|
699
647
|
throw new ProviderError("defineCredentialsAuth requires at least one field", {
|
|
700
|
-
fix:
|
|
648
|
+
fix: 'Pass fields such as { email: { type: "email" }, password: { type: "password" } }.',
|
|
701
649
|
});
|
|
702
650
|
}
|
|
703
651
|
|
|
@@ -737,10 +685,7 @@ export function defineCredentialsAuth<
|
|
|
737
685
|
return retryTurn(expectedInput, missing, retryTurnId);
|
|
738
686
|
}
|
|
739
687
|
|
|
740
|
-
const result = await options.login(
|
|
741
|
-
ctx,
|
|
742
|
-
normalizeInput(options.fields, rawInput),
|
|
743
|
-
);
|
|
688
|
+
const result = await options.login(ctx, normalizeInput(options.fields, rawInput));
|
|
744
689
|
return await resolveAuthResult(
|
|
745
690
|
ctx,
|
|
746
691
|
options.credentialKeys,
|
|
@@ -776,10 +721,7 @@ export function defineCredentialsAuth<
|
|
|
776
721
|
},
|
|
777
722
|
context: {
|
|
778
723
|
keys: Array.from(
|
|
779
|
-
new Set([
|
|
780
|
-
CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY,
|
|
781
|
-
...(options.contextKeys ?? []),
|
|
782
|
-
]),
|
|
724
|
+
new Set([CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, ...(options.contextKeys ?? [])]),
|
|
783
725
|
),
|
|
784
726
|
},
|
|
785
727
|
};
|
package/src/ceremonies/index.ts
CHANGED
|
@@ -2,19 +2,14 @@ import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
|
2
2
|
|
|
3
3
|
import Ajv2020 from "ajv/dist/2020.js";
|
|
4
4
|
|
|
5
|
-
import { AUTH_TURN_SCHEMA, type KnownAuthTurnKind } from "../auth-turn";
|
|
5
|
+
import { AUTH_TURN_SCHEMA, type KnownAuthTurnKind } from "../auth-turn/index.js";
|
|
6
6
|
import {
|
|
7
7
|
FlowExpiredError,
|
|
8
8
|
ProviderSecretError,
|
|
9
9
|
TurnValidationError,
|
|
10
10
|
ValidationError,
|
|
11
|
-
} from "../errors";
|
|
12
|
-
import type {
|
|
13
|
-
AuthFlowDefinition,
|
|
14
|
-
AuthFlowInputHandler,
|
|
15
|
-
AuthTurn,
|
|
16
|
-
FlowContext,
|
|
17
|
-
} from "../types";
|
|
11
|
+
} from "../errors.js";
|
|
12
|
+
import type { AuthFlowDefinition, AuthFlowInputHandler, AuthTurn, FlowContext } from "../types.js";
|
|
18
13
|
|
|
19
14
|
type TurnKind = KnownAuthTurnKind;
|
|
20
15
|
|
|
@@ -45,10 +40,7 @@ function ensureRecord(value: unknown): JsonObject {
|
|
|
45
40
|
return isRecord(value) ? value : {};
|
|
46
41
|
}
|
|
47
42
|
|
|
48
|
-
function createTurn(
|
|
49
|
-
kind: TurnKind,
|
|
50
|
-
options: Omit<AuthTurn, "kind" | "turnId"> = {},
|
|
51
|
-
): AuthTurn {
|
|
43
|
+
function createTurn(kind: TurnKind, options: Omit<AuthTurn, "kind" | "turnId"> = {}): AuthTurn {
|
|
52
44
|
return {
|
|
53
45
|
kind,
|
|
54
46
|
turnId: randomUUID(),
|
|
@@ -69,11 +61,7 @@ function getRequiredEnv(ctx: FlowContext, key: string): string {
|
|
|
69
61
|
}
|
|
70
62
|
|
|
71
63
|
function toBase64Url(input: Buffer): string {
|
|
72
|
-
return input
|
|
73
|
-
.toString("base64")
|
|
74
|
-
.replace(/\+/g, "-")
|
|
75
|
-
.replace(/\//g, "_")
|
|
76
|
-
.replace(/=+$/g, "");
|
|
64
|
+
return input.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
77
65
|
}
|
|
78
66
|
|
|
79
67
|
function createCodeVerifier(): string {
|
|
@@ -130,10 +118,7 @@ async function runCeremonyHandler(
|
|
|
130
118
|
}
|
|
131
119
|
}
|
|
132
120
|
|
|
133
|
-
function getString(
|
|
134
|
-
input: Record<string, unknown>,
|
|
135
|
-
key: string,
|
|
136
|
-
): string | undefined {
|
|
121
|
+
function getString(input: Record<string, unknown>, key: string): string | undefined {
|
|
137
122
|
const value = input[key];
|
|
138
123
|
return typeof value === "string" ? value : undefined;
|
|
139
124
|
}
|
|
@@ -142,10 +127,7 @@ function getNestedRecord(ctx: FlowContext, key: string): JsonObject {
|
|
|
142
127
|
return ensureRecord(ctx.context.get(key));
|
|
143
128
|
}
|
|
144
129
|
|
|
145
|
-
function buildJsonSchemaForm(
|
|
146
|
-
expectedInput: JsonObject,
|
|
147
|
-
hint: string,
|
|
148
|
-
): AuthTurn {
|
|
130
|
+
function buildJsonSchemaForm(expectedInput: JsonObject, hint: string): AuthTurn {
|
|
149
131
|
return createTurn("form", {
|
|
150
132
|
hint,
|
|
151
133
|
expectedInput: withDeclaredFormFieldOrder(expectedInput),
|
|
@@ -160,10 +142,7 @@ function withDeclaredFormFieldOrder(expectedInput: JsonObject): JsonObject {
|
|
|
160
142
|
}
|
|
161
143
|
|
|
162
144
|
const existingOrder = expectedInput[FORM_FIELD_ORDER_EXTENSION];
|
|
163
|
-
if (
|
|
164
|
-
Array.isArray(existingOrder) &&
|
|
165
|
-
existingOrder.every((value) => typeof value === "string")
|
|
166
|
-
) {
|
|
145
|
+
if (Array.isArray(existingOrder) && existingOrder.every((value) => typeof value === "string")) {
|
|
167
146
|
return expectedInput;
|
|
168
147
|
}
|
|
169
148
|
|
|
@@ -176,9 +155,7 @@ function withDeclaredFormFieldOrder(expectedInput: JsonObject): JsonObject {
|
|
|
176
155
|
export function validateCeremonyOutput(turn: unknown): AuthTurn {
|
|
177
156
|
if (!validateAuthTurn(turn)) {
|
|
178
157
|
const detail = validateAuthTurn.errors
|
|
179
|
-
?.map(
|
|
180
|
-
(error) => `${error.instancePath || "$"} ${error.message ?? "invalid"}`,
|
|
181
|
-
)
|
|
158
|
+
?.map((error) => `${error.instancePath || "$"} ${error.message ?? "invalid"}`)
|
|
182
159
|
.join("; ");
|
|
183
160
|
throw new TurnValidationError(detail || "Invalid AuthTurn output");
|
|
184
161
|
}
|
|
@@ -214,10 +191,7 @@ export function createOAuth2Ceremony(options: {
|
|
|
214
191
|
if (options.usePKCE) {
|
|
215
192
|
const verifier = createCodeVerifier();
|
|
216
193
|
ctx.context.set(OAUTH2_PKCE_VERIFIER_KEY, verifier);
|
|
217
|
-
authorizeUrl.searchParams.set(
|
|
218
|
-
"code_challenge",
|
|
219
|
-
createCodeChallenge(verifier),
|
|
220
|
-
);
|
|
194
|
+
authorizeUrl.searchParams.set("code_challenge", createCodeChallenge(verifier));
|
|
221
195
|
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
222
196
|
}
|
|
223
197
|
|
|
@@ -270,10 +244,7 @@ export function createOAuth2Ceremony(options: {
|
|
|
270
244
|
ctx,
|
|
271
245
|
input,
|
|
272
246
|
),
|
|
273
|
-
abort: async () =>
|
|
274
|
-
validateCeremonyOutput(
|
|
275
|
-
createTurn("abort", { hint: "OAuth flow aborted." }),
|
|
276
|
-
),
|
|
247
|
+
abort: async () => validateCeremonyOutput(createTurn("abort", { hint: "OAuth flow aborted." })),
|
|
277
248
|
};
|
|
278
249
|
}
|
|
279
250
|
|
|
@@ -329,26 +300,19 @@ export function createDeviceFlowCeremony(options: {
|
|
|
329
300
|
client_id: getRequiredEnv(ctx, options.clientIdEnvKey),
|
|
330
301
|
...(options.clientSecretEnvKey
|
|
331
302
|
? {
|
|
332
|
-
client_secret: getRequiredEnv(
|
|
333
|
-
ctx,
|
|
334
|
-
options.clientSecretEnvKey,
|
|
335
|
-
),
|
|
303
|
+
client_secret: getRequiredEnv(ctx, options.clientSecretEnvKey),
|
|
336
304
|
}
|
|
337
305
|
: {}),
|
|
338
306
|
});
|
|
339
307
|
const data = ensureRecord(response.data);
|
|
340
308
|
const errorCode = getString(data, "error");
|
|
341
309
|
|
|
342
|
-
if (
|
|
343
|
-
errorCode === "authorization_pending" ||
|
|
344
|
-
errorCode === "slow_down"
|
|
345
|
-
) {
|
|
310
|
+
if (errorCode === "authorization_pending" || errorCode === "slow_down") {
|
|
346
311
|
return createTurn("poll", {
|
|
347
312
|
data,
|
|
348
313
|
hint: "Authorization pending.",
|
|
349
314
|
timing: {
|
|
350
|
-
suggestedPollIntervalMs:
|
|
351
|
-
errorCode === "slow_down" ? 10_000 : 5_000,
|
|
315
|
+
suggestedPollIntervalMs: errorCode === "slow_down" ? 10_000 : 5_000,
|
|
352
316
|
maxWaitMs: 120_000,
|
|
353
317
|
},
|
|
354
318
|
});
|
|
@@ -367,9 +331,7 @@ export function createDeviceFlowCeremony(options: {
|
|
|
367
331
|
ctx,
|
|
368
332
|
),
|
|
369
333
|
abort: async () =>
|
|
370
|
-
validateCeremonyOutput(
|
|
371
|
-
createTurn("abort", { hint: "Device flow aborted." }),
|
|
372
|
-
),
|
|
334
|
+
validateCeremonyOutput(createTurn("abort", { hint: "Device flow aborted." })),
|
|
373
335
|
};
|
|
374
336
|
}
|
|
375
337
|
|
|
@@ -437,9 +399,7 @@ export function createWebAuthnCeremony(options: {
|
|
|
437
399
|
input,
|
|
438
400
|
),
|
|
439
401
|
abort: async () =>
|
|
440
|
-
validateCeremonyOutput(
|
|
441
|
-
createTurn("abort", { hint: "WebAuthn ceremony aborted." }),
|
|
442
|
-
),
|
|
402
|
+
validateCeremonyOutput(createTurn("abort", { hint: "WebAuthn ceremony aborted." })),
|
|
443
403
|
};
|
|
444
404
|
}
|
|
445
405
|
|
|
@@ -526,9 +486,7 @@ export function createMagicLinkCeremony(options: {
|
|
|
526
486
|
ctx,
|
|
527
487
|
),
|
|
528
488
|
abort: async () =>
|
|
529
|
-
validateCeremonyOutput(
|
|
530
|
-
createTurn("abort", { hint: "Magic link flow aborted." }),
|
|
531
|
-
),
|
|
489
|
+
validateCeremonyOutput(createTurn("abort", { hint: "Magic link flow aborted." })),
|
|
532
490
|
};
|
|
533
491
|
}
|
|
534
492
|
|
|
@@ -548,7 +506,7 @@ export function createFormCeremony(options: {
|
|
|
548
506
|
continue: (ctx, input = {}) =>
|
|
549
507
|
runCeremonyHandler(
|
|
550
508
|
async () => {
|
|
551
|
-
const { prevalidate } = await import("../runtime/prevalidate");
|
|
509
|
+
const { prevalidate } = await import("../runtime/prevalidate.js");
|
|
552
510
|
const result = prevalidate(options.schema, input);
|
|
553
511
|
if (!result.valid) {
|
|
554
512
|
throw new ValidationError("Form input failed validation.", {
|
|
@@ -558,9 +516,7 @@ export function createFormCeremony(options: {
|
|
|
558
516
|
|
|
559
517
|
return createTurn("complete", {
|
|
560
518
|
data: {
|
|
561
|
-
credential: options.mapCredential
|
|
562
|
-
? options.mapCredential(input)
|
|
563
|
-
: input,
|
|
519
|
+
credential: options.mapCredential ? options.mapCredential(input) : input,
|
|
564
520
|
},
|
|
565
521
|
hint: "Form completed.",
|
|
566
522
|
});
|
|
@@ -570,15 +526,11 @@ export function createFormCeremony(options: {
|
|
|
570
526
|
input,
|
|
571
527
|
),
|
|
572
528
|
abort: async () =>
|
|
573
|
-
validateCeremonyOutput(
|
|
574
|
-
createTurn("abort", { hint: "Form ceremony aborted." }),
|
|
575
|
-
),
|
|
529
|
+
validateCeremonyOutput(createTurn("abort", { hint: "Form ceremony aborted." })),
|
|
576
530
|
};
|
|
577
531
|
}
|
|
578
532
|
|
|
579
|
-
export function combineCeremonies(
|
|
580
|
-
...ceremonies: AuthFlowDefinition[]
|
|
581
|
-
): AuthFlowDefinition {
|
|
533
|
+
export function combineCeremonies(...ceremonies: AuthFlowDefinition[]): AuthFlowDefinition {
|
|
582
534
|
function getStage(ctx: FlowContext): number {
|
|
583
535
|
const rawStage = ctx.context.get(COMBINED_STAGE_KEY);
|
|
584
536
|
return typeof rawStage === "number" ? rawStage : 0;
|
|
@@ -630,9 +582,7 @@ export function combineCeremonies(
|
|
|
630
582
|
async () => {
|
|
631
583
|
const current = ceremonies[getStage(ctx)];
|
|
632
584
|
if (!current?.poll) {
|
|
633
|
-
throw new ValidationError(
|
|
634
|
-
"Current ceremony does not support polling.",
|
|
635
|
-
);
|
|
585
|
+
throw new ValidationError("Current ceremony does not support polling.");
|
|
636
586
|
}
|
|
637
587
|
return await current.poll(ctx);
|
|
638
588
|
},
|
|
@@ -680,9 +630,7 @@ export function createSwitchCeremony(options: {
|
|
|
680
630
|
async () => {
|
|
681
631
|
const storedChoice = ctx.context.get(SWITCH_SELECTION_KEY);
|
|
682
632
|
const choice =
|
|
683
|
-
typeof storedChoice === "string"
|
|
684
|
-
? storedChoice
|
|
685
|
-
: getString(input, "choice");
|
|
633
|
+
typeof storedChoice === "string" ? storedChoice : getString(input, "choice");
|
|
686
634
|
|
|
687
635
|
if (!choice || !options.choices[choice]) {
|
|
688
636
|
throw new ValidationError("A valid choice is required.");
|
|
@@ -710,9 +658,7 @@ export function createSwitchCeremony(options: {
|
|
|
710
658
|
|
|
711
659
|
const ceremony = options.choices[choice];
|
|
712
660
|
if (!ceremony?.poll) {
|
|
713
|
-
throw new ValidationError(
|
|
714
|
-
"Selected ceremony does not support polling.",
|
|
715
|
-
);
|
|
661
|
+
throw new ValidationError("Selected ceremony does not support polling.");
|
|
716
662
|
}
|
|
717
663
|
|
|
718
664
|
return await ceremony.poll(ctx);
|
package/src/cli/commands.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type ApifuseCommandName =
|
|
|
2
2
|
| "create"
|
|
3
3
|
| "dev"
|
|
4
4
|
| "check"
|
|
5
|
+
| "sync-assets"
|
|
5
6
|
| "submit-check"
|
|
6
7
|
| "bounty-check"
|
|
7
8
|
| "record"
|
|
@@ -46,6 +47,14 @@ export const COMMAND_MANIFEST: Record<
|
|
|
46
47
|
examples: ["apifuse check .", "apifuse check providers/korea-air-quality"],
|
|
47
48
|
modulePath: "./apifuse-check",
|
|
48
49
|
},
|
|
50
|
+
"sync-assets": {
|
|
51
|
+
name: "sync-assets",
|
|
52
|
+
summary:
|
|
53
|
+
"Regenerate SDK-managed agent prompt assets (AGENTS.md, .agents/skills, symlinks, manifest) for the installed SDK version.",
|
|
54
|
+
usage: "apifuse sync-assets [path] [--check]",
|
|
55
|
+
examples: ["apifuse sync-assets .", "apifuse sync-assets . --check"],
|
|
56
|
+
modulePath: "./apifuse-sync-assets",
|
|
57
|
+
},
|
|
49
58
|
"submit-check": {
|
|
50
59
|
name: "submit-check",
|
|
51
60
|
summary:
|
|
@@ -103,6 +112,7 @@ export const COMMAND_ORDER: ApifuseCommandName[] = [
|
|
|
103
112
|
"create",
|
|
104
113
|
"dev",
|
|
105
114
|
"check",
|
|
115
|
+
"sync-assets",
|
|
106
116
|
"submit-check",
|
|
107
117
|
"record",
|
|
108
118
|
"test",
|
package/src/cli/create.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { mkdir, readFile, rm, symlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
@@ -8,6 +8,11 @@ import { cancel, intro, isCancel, note, outro, select, text } from "@clack/promp
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import packageJson from "../../package.json";
|
|
11
|
+
import {
|
|
12
|
+
buildPromptAssetManifest,
|
|
13
|
+
buildPromptAssetPlanEntries,
|
|
14
|
+
PROMPT_ASSET_MANIFEST_PATH,
|
|
15
|
+
} from "./prompt-assets.js";
|
|
11
16
|
|
|
12
17
|
export const PROVIDER_NAME_REGEX = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
13
18
|
export const CATEGORY_OPTIONS = [
|
|
@@ -64,7 +69,10 @@ export type CreateResolvedOptions = {
|
|
|
64
69
|
|
|
65
70
|
export type ProviderPlanFile = {
|
|
66
71
|
path: string;
|
|
72
|
+
/** File content, or the symlink target (relative path) for kind "symlink". */
|
|
67
73
|
content: string;
|
|
74
|
+
/** Absent kind means "file" (backward compatible with older consumers). */
|
|
75
|
+
kind?: "file" | "symlink";
|
|
68
76
|
};
|
|
69
77
|
|
|
70
78
|
export type ProviderCreatePlan = {
|
|
@@ -410,11 +418,16 @@ export async function buildProviderCreatePlan(
|
|
|
410
418
|
);
|
|
411
419
|
}
|
|
412
420
|
|
|
421
|
+
// Exact pin, not a caret range: the prompt-asset freshness gate requires
|
|
422
|
+
// manifest.sdkVersion === installed SDK version, and the manifest records
|
|
423
|
+
// this CLI's own version. A caret range would let `bun install` resolve a
|
|
424
|
+
// newer SDK than the create binary, making a fresh scaffold fail its own
|
|
425
|
+
// gate. `sync-assets` after an explicit SDK upgrade re-records the version.
|
|
413
426
|
const sdkSpecifier =
|
|
414
427
|
options.sdkSpecifier ??
|
|
415
428
|
(options.preset === "monorepo" && resolvedWorkspaceRoot
|
|
416
429
|
? "workspace:*"
|
|
417
|
-
:
|
|
430
|
+
: packageJson.version);
|
|
418
431
|
const relativeProviderRoot = relative(cwd, providerRoot) || options.name;
|
|
419
432
|
const nextDevCommand = `cd ${relativeProviderRoot} && bun run dev`;
|
|
420
433
|
const packageName =
|
|
@@ -538,40 +551,23 @@ export async function buildProviderCreatePlan(
|
|
|
538
551
|
PROVIDER_ID: options.name,
|
|
539
552
|
}),
|
|
540
553
|
},
|
|
541
|
-
{
|
|
542
|
-
path: resolve(providerRoot, "AGENTS.md"),
|
|
543
|
-
content: await renderTemplate("AGENTS.md.tpl", {}),
|
|
544
|
-
},
|
|
545
|
-
{
|
|
546
|
-
path: resolve(providerRoot, "CLAUDE.md"),
|
|
547
|
-
content: await renderTemplate("CLAUDE.md.tpl", {}),
|
|
548
|
-
},
|
|
549
|
-
{
|
|
550
|
-
path: resolve(providerRoot, "skills", "normalization-standards", "SKILL.md"),
|
|
551
|
-
content: await renderTemplate("skills/normalization-standards/SKILL.md.tpl", {}),
|
|
552
|
-
},
|
|
553
|
-
{
|
|
554
|
-
path: resolve(providerRoot, "skills", "upstream-contract-verification", "SKILL.md"),
|
|
555
|
-
content: await renderTemplate("skills/upstream-contract-verification/SKILL.md.tpl", {}),
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
path: resolve(providerRoot, "skills", "fixtures-and-recording", "SKILL.md"),
|
|
559
|
-
content: await renderTemplate("skills/fixtures-and-recording/SKILL.md.tpl", {}),
|
|
560
|
-
},
|
|
561
|
-
{
|
|
562
|
-
path: resolve(providerRoot, "skills", "pagination-and-counts", "SKILL.md"),
|
|
563
|
-
content: await renderTemplate("skills/pagination-and-counts/SKILL.md.tpl", {}),
|
|
564
|
-
},
|
|
565
|
-
{
|
|
566
|
-
path: resolve(providerRoot, "skills", "health-checks-and-fail-closed", "SKILL.md"),
|
|
567
|
-
content: await renderTemplate("skills/health-checks-and-fail-closed/SKILL.md.tpl", {}),
|
|
568
|
-
},
|
|
569
|
-
{
|
|
570
|
-
path: resolve(providerRoot, "skills", "upstream-notes", "README.md"),
|
|
571
|
-
content: await renderTemplate("skills/upstream-notes/README.md.tpl", {}),
|
|
572
|
-
},
|
|
573
554
|
];
|
|
574
555
|
|
|
556
|
+
const promptAssetEntries = await buildPromptAssetPlanEntries(renderTemplate);
|
|
557
|
+
for (const entry of promptAssetEntries) {
|
|
558
|
+
files.push({
|
|
559
|
+
path: resolve(providerRoot, entry.path),
|
|
560
|
+
content: entry.content,
|
|
561
|
+
...(entry.kind === "symlink" ? { kind: "symlink" as const } : {}),
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
// Manifest last: writePlan writes in order, so a crash mid-scaffold never
|
|
565
|
+
// leaves a manifest that claims assets which were not written.
|
|
566
|
+
files.push({
|
|
567
|
+
path: resolve(providerRoot, PROMPT_ASSET_MANIFEST_PATH),
|
|
568
|
+
content: buildPromptAssetManifest(promptAssetEntries, packageJson.version),
|
|
569
|
+
});
|
|
570
|
+
|
|
575
571
|
return {
|
|
576
572
|
displayName: options.displayName,
|
|
577
573
|
files,
|
|
@@ -614,6 +610,7 @@ function renderPackageJson(input: { packageName: string; sdkSpecifier: string })
|
|
|
614
610
|
check: "apifuse check . && bun run type-check",
|
|
615
611
|
"type-check": "tsc --noEmit",
|
|
616
612
|
"submit-check": "apifuse submit-check . --markdown submission-report.md",
|
|
613
|
+
"sync-assets": "apifuse sync-assets .",
|
|
617
614
|
test: "apifuse test .",
|
|
618
615
|
record: "apifuse record .",
|
|
619
616
|
start: "bun start.ts",
|
|
@@ -869,6 +866,13 @@ function isApifuseInternalWorkspaceRoot(workspaceRoot: string): boolean {
|
|
|
869
866
|
async function writePlan(plan: ProviderCreatePlan): Promise<void> {
|
|
870
867
|
for (const file of plan.files) {
|
|
871
868
|
await mkdir(dirname(file.path), { recursive: true });
|
|
869
|
+
if (file.kind === "symlink") {
|
|
870
|
+
// rm operates on the link itself (lstat semantics) and tolerates
|
|
871
|
+
// directories, so whatever occupies the path is replaced by the link.
|
|
872
|
+
await rm(file.path, { recursive: true, force: true });
|
|
873
|
+
await symlink(file.content, file.path);
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
872
876
|
await writeFile(file.path, file.content);
|
|
873
877
|
}
|
|
874
878
|
}
|
|
@@ -940,7 +944,10 @@ function printResult(plan: ProviderCreatePlan, jsonMode: boolean, dryRun: boolea
|
|
|
940
944
|
},
|
|
941
945
|
validationCommands: plan.validationCommands,
|
|
942
946
|
nextDevCommand: plan.nextDevCommand,
|
|
943
|
-
files: plan.files.map((file) =>
|
|
947
|
+
files: plan.files.map((file) => {
|
|
948
|
+
const relativePath = relative(plan.providerRoot, file.path) || file.path;
|
|
949
|
+
return file.kind === "symlink" ? `${relativePath} -> ${file.content} (symlink)` : relativePath;
|
|
950
|
+
}),
|
|
944
951
|
};
|
|
945
952
|
|
|
946
953
|
if (jsonMode) {
|