@apifuse/provider-sdk 2.1.0-beta.9 → 2.2.0-beta.10
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 +240 -0
- package/CHANGELOG.md +93 -0
- package/README.md +26 -10
- package/SUBMISSION.md +11 -12
- 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 +36 -0
- package/bin/apifuse-pack-smoke.ts +22 -81
- package/bin/apifuse-pack-types.ts +266 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +53 -70
- package/bin/apifuse-submit-check.ts +2177 -353
- 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/auth-turn.v1.schema.json +89 -0
- package/dist/auth-turn/fixtures/invalid/empty-kind.json +4 -0
- package/dist/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
- package/dist/auth-turn/fixtures/invalid/missing-kind.json +3 -0
- package/dist/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
- package/dist/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
- package/dist/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
- package/dist/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
- package/dist/auth-turn/fixtures/valid/abort.json +8 -0
- package/dist/auth-turn/fixtures/valid/challenge.json +17 -0
- package/dist/auth-turn/fixtures/valid/complete.json +13 -0
- package/dist/auth-turn/fixtures/valid/form.json +14 -0
- package/dist/auth-turn/fixtures/valid/message.json +13 -0
- package/dist/auth-turn/fixtures/valid/multi_choice.json +15 -0
- package/dist/auth-turn/fixtures/valid/pending.json +5 -0
- package/dist/auth-turn/fixtures/valid/poll.json +9 -0
- package/dist/auth-turn/fixtures/valid/redirect.json +16 -0
- package/dist/auth-turn/fixtures/valid/retry.json +8 -0
- package/dist/auth-turn/fixtures/valid/unknown-kind.json +7 -0
- package/dist/auth-turn/index.d.ts +195 -0
- package/dist/auth-turn/index.js +133 -0
- package/dist/auth.d.ts +76 -0
- package/dist/auth.js +427 -0
- package/dist/ceremonies/index.d.ts +1 -1
- package/dist/ceremonies/index.js +14 -48
- 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 +47 -33
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
- package/dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
- package/dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
- package/dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
- package/dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
- package/dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
- package/dist/cli/templates/provider/.dockerignore.tpl +22 -0
- package/dist/cli/templates/provider/.gitignore.tpl +22 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +96 -0
- package/dist/cli/templates/provider/Dockerfile.tpl +7 -0
- package/dist/cli/templates/provider/README.md.tpl +163 -0
- package/dist/cli/templates/provider/dev.ts.tpl +5 -0
- package/dist/cli/templates/provider/domain/README.md.tpl +3 -0
- package/dist/cli/templates/provider/index.test.ts.tpl +13 -0
- package/dist/cli/templates/provider/index.ts.tpl +15 -0
- package/dist/cli/templates/provider/mappers/README.md.tpl +3 -0
- package/dist/cli/templates/provider/meta.ts.tpl +7 -0
- package/dist/cli/templates/provider/operations/index.ts.tpl +5 -0
- package/dist/cli/templates/provider/operations/ping.ts.tpl +24 -0
- package/dist/cli/templates/provider/schemas/ping.ts.tpl +24 -0
- package/dist/cli/templates/provider/start.ts.tpl +5 -0
- package/dist/cli/templates/provider/upstream/README.md.tpl +3 -0
- package/dist/config/loader.d.ts +149 -8
- package/dist/config/loader.js +378 -89
- 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 +5 -6
- package/dist/define.d.ts +13 -1
- package/dist/define.js +245 -178
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/errors.d.ts +4 -1
- package/dist/errors.js +48 -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 +44 -41
- package/dist/index.js +39 -36
- package/dist/lint.d.ts +1 -0
- package/dist/lint.js +35 -15
- package/dist/provider.d.ts +11 -9
- package/dist/provider.js +9 -8
- 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 +4 -2
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +214 -25
- 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 +15 -3
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +160 -344
- 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 +56 -19
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- 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 +35 -0
- package/dist/runtime/proxy-nodemaven.js +128 -0
- package/dist/runtime/proxy-retry-policy.d.ts +40 -0
- package/dist/runtime/proxy-retry-policy.js +326 -0
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +1 -1
- 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 +7 -4
- package/dist/runtime/stealth.js +257 -215
- 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 -3
- package/dist/server/index.js +6 -2
- package/dist/server/self-test-input-tokens.d.ts +1 -0
- package/dist/server/self-test-input-tokens.js +37 -0
- package/dist/server/self-test-redaction.d.ts +20 -0
- package/dist/server/self-test-redaction.js +70 -0
- package/dist/server/self-test-token.d.ts +30 -0
- package/dist/server/self-test-token.js +50 -0
- package/dist/server/self-test.d.ts +199 -0
- package/dist/server/self-test.js +1113 -0
- package/dist/server/serve.d.ts +14 -3
- package/dist/server/serve.js +135 -64
- package/dist/server/types.d.ts +10 -9
- package/dist/server/types.js +3 -7
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- 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 +1 -1
- package/dist/testing/run.js +12 -15
- package/dist/types.d.ts +237 -1
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +16 -5
- package/src/auth-turn/auth-turn.v1.schema.json +89 -0
- package/src/auth-turn/fixtures/invalid/empty-kind.json +4 -0
- package/src/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
- package/src/auth-turn/fixtures/invalid/missing-kind.json +3 -0
- package/src/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
- package/src/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
- package/src/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
- package/src/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
- package/src/auth-turn/fixtures/valid/abort.json +8 -0
- package/src/auth-turn/fixtures/valid/challenge.json +17 -0
- package/src/auth-turn/fixtures/valid/complete.json +13 -0
- package/src/auth-turn/fixtures/valid/form.json +14 -0
- package/src/auth-turn/fixtures/valid/message.json +13 -0
- package/src/auth-turn/fixtures/valid/multi_choice.json +15 -0
- package/src/auth-turn/fixtures/valid/pending.json +5 -0
- package/src/auth-turn/fixtures/valid/poll.json +9 -0
- package/src/auth-turn/fixtures/valid/redirect.json +16 -0
- package/src/auth-turn/fixtures/valid/retry.json +8 -0
- package/src/auth-turn/fixtures/valid/unknown-kind.json +7 -0
- package/src/auth-turn/index.ts +177 -0
- package/src/auth.ts +728 -0
- package/src/ceremonies/index.ts +33 -121
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +69 -99
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
- package/src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
- package/src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
- package/src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
- package/src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
- package/src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +96 -0
- package/src/cli/templates/provider/README.md.tpl +7 -4
- package/src/config/loader.ts +543 -208
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +13 -28
- package/src/define.ts +397 -528
- package/src/dev.ts +4 -9
- package/src/errors.ts +58 -5
- 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 +64 -41
- package/src/lint.ts +122 -159
- package/src/provider.ts +40 -9
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +6 -6
- package/src/runtime/browser.ts +320 -151
- 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 +23 -20
- package/src/runtime/http.ts +216 -539
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +78 -65
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- 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 +178 -0
- package/src/runtime/proxy-retry-policy.ts +469 -0
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +4 -13
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +41 -110
- package/src/runtime/stealth.ts +331 -369
- 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 +44 -3
- package/src/server/self-test-input-tokens.ts +46 -0
- package/src/server/self-test-redaction.ts +97 -0
- package/src/server/self-test-token.ts +70 -0
- package/src/server/self-test.ts +1450 -0
- package/src/server/serve.ts +206 -216
- package/src/server/types.ts +7 -19
- package/src/stealth/profiles.ts +10 -26
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +2 -2
- package/src/testing/run.ts +24 -64
- package/src/types.ts +274 -1
- package/src/user-input.ts +118 -0
package/dist/auth.js
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import { AuthError, ProviderError } from "./errors.js";
|
|
2
|
+
const CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY = "__credentialsAuthChallenge";
|
|
3
|
+
const DEFAULT_COMPLETE_TURN_ID = "auth.complete";
|
|
4
|
+
const DEFAULT_ABORT_TURN_ID = "auth.abort";
|
|
5
|
+
const DEFAULT_FORM_TURN_ID = "auth.form";
|
|
6
|
+
const DEFAULT_POLL_TURN_ID = "auth.poll";
|
|
7
|
+
const SENSITIVE_ABORT_DATA_KEY_PATTERN = /(authorization|cookie|credential|header|html|password|secret|session|token|apikey)/i;
|
|
8
|
+
function normalizedAuthDataKey(key) {
|
|
9
|
+
return key.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
10
|
+
}
|
|
11
|
+
function isPlainAuthJsonObject(value) {
|
|
12
|
+
const prototype = Object.getPrototypeOf(value);
|
|
13
|
+
return prototype === Object.prototype || prototype === null;
|
|
14
|
+
}
|
|
15
|
+
function isSensitiveAuthDataKey(key) {
|
|
16
|
+
return SENSITIVE_ABORT_DATA_KEY_PATTERN.test(normalizedAuthDataKey(key));
|
|
17
|
+
}
|
|
18
|
+
function assertSafeAuthJson(value, path) {
|
|
19
|
+
if (value === null)
|
|
20
|
+
return;
|
|
21
|
+
if (typeof value === "string" || typeof value === "boolean")
|
|
22
|
+
return;
|
|
23
|
+
if (typeof value === "number") {
|
|
24
|
+
if (Number.isFinite(value))
|
|
25
|
+
return;
|
|
26
|
+
throw new AuthError(`Auth abort ${path} must be a finite number`, {
|
|
27
|
+
code: "auth_abort_unsafe_data",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
for (const [index, item] of value.entries()) {
|
|
32
|
+
assertSafeAuthJson(item, `${path}[${index}]`);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (value instanceof Error) {
|
|
37
|
+
throw new AuthError(`Auth abort ${path} must not include Error objects`, {
|
|
38
|
+
code: "auth_abort_unsafe_data",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (typeof value !== "object") {
|
|
42
|
+
throw new AuthError(`Auth abort ${path} must be JSON-safe`, {
|
|
43
|
+
code: "auth_abort_unsafe_data",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (!isPlainAuthJsonObject(value)) {
|
|
47
|
+
throw new AuthError(`Auth abort ${path} must be a plain JSON object`, {
|
|
48
|
+
code: "auth_abort_unsafe_data",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
for (const [key, item] of Object.entries(value)) {
|
|
52
|
+
if (isSensitiveAuthDataKey(key)) {
|
|
53
|
+
throw new AuthError(`Auth abort ${path}.${key} must not include secrets`, {
|
|
54
|
+
code: "auth_abort_unsafe_data",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
assertSafeAuthJson(item, `${path}.${key}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function assertSafeAuthData(data, path) {
|
|
61
|
+
assertSafeAuthJson(data, path);
|
|
62
|
+
}
|
|
63
|
+
function authTurnBase(options) {
|
|
64
|
+
return {
|
|
65
|
+
turnId: options.turnId ?? options.defaultTurnId,
|
|
66
|
+
...(options.expiresAt ? { expiresAt: options.expiresAt } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function abortData(options) {
|
|
70
|
+
if (options.actionHint !== undefined) {
|
|
71
|
+
assertSafeAuthJson(options.actionHint, "actionHint");
|
|
72
|
+
}
|
|
73
|
+
if (options.data !== undefined) {
|
|
74
|
+
assertSafeAuthData(options.data, "data");
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
code: options.code,
|
|
78
|
+
...(options.message ? { message: options.message } : {}),
|
|
79
|
+
...(options.retry ? { retry: options.retry } : {}),
|
|
80
|
+
...(options.actionHint !== undefined ? { actionHint: options.actionHint } : {}),
|
|
81
|
+
...(options.fieldErrors ? { fieldErrors: options.fieldErrors } : {}),
|
|
82
|
+
...(options.data ? { details: options.data } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export function createAuthFlowHelpers(options = {}) {
|
|
86
|
+
return {
|
|
87
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
88
|
+
...(options.deadline ? { deadline: options.deadline } : {}),
|
|
89
|
+
complete({ credential, metadata, data, turnId, expiresAt }) {
|
|
90
|
+
return {
|
|
91
|
+
kind: "complete",
|
|
92
|
+
...authTurnBase({ turnId, defaultTurnId: DEFAULT_COMPLETE_TURN_ID, expiresAt }),
|
|
93
|
+
data: {
|
|
94
|
+
...(data ?? {}),
|
|
95
|
+
credential,
|
|
96
|
+
...(metadata ? { metadata } : {}),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
abort({ code, message, retry, actionHint, fieldErrors, data, turnId, expiresAt }) {
|
|
101
|
+
return {
|
|
102
|
+
kind: "abort",
|
|
103
|
+
...authTurnBase({ turnId, defaultTurnId: DEFAULT_ABORT_TURN_ID, expiresAt }),
|
|
104
|
+
data: abortData({
|
|
105
|
+
code,
|
|
106
|
+
message,
|
|
107
|
+
retry,
|
|
108
|
+
actionHint,
|
|
109
|
+
fieldErrors,
|
|
110
|
+
data,
|
|
111
|
+
}),
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
nextForm(options) {
|
|
115
|
+
return {
|
|
116
|
+
kind: "form",
|
|
117
|
+
...authTurnBase({
|
|
118
|
+
turnId: options.turnId,
|
|
119
|
+
defaultTurnId: DEFAULT_FORM_TURN_ID,
|
|
120
|
+
expiresAt: options.expiresAt,
|
|
121
|
+
}),
|
|
122
|
+
...(options.hintKey ? { hintKey: options.hintKey } : {}),
|
|
123
|
+
...(options.timing ? { timing: options.timing } : {}),
|
|
124
|
+
...(options.data ? { data: options.data } : {}),
|
|
125
|
+
expectedInput: options.expectedInput ?? expectedInputFromFields(options.fields ?? {}),
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
nextPoll(options = {}) {
|
|
129
|
+
return {
|
|
130
|
+
kind: "poll",
|
|
131
|
+
...authTurnBase({
|
|
132
|
+
turnId: options.turnId,
|
|
133
|
+
defaultTurnId: DEFAULT_POLL_TURN_ID,
|
|
134
|
+
expiresAt: options.expiresAt,
|
|
135
|
+
}),
|
|
136
|
+
...(options.hintKey ? { hintKey: options.hintKey } : {}),
|
|
137
|
+
...(options.timing ? { timing: options.timing } : {}),
|
|
138
|
+
...(options.data ? { data: options.data } : {}),
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export class AuthAbortError extends AuthError {
|
|
144
|
+
turn;
|
|
145
|
+
constructor(options) {
|
|
146
|
+
super(options.message ?? options.code, {
|
|
147
|
+
code: options.code,
|
|
148
|
+
retryable: options.retry === "retry",
|
|
149
|
+
});
|
|
150
|
+
this.name = "AuthAbortError";
|
|
151
|
+
this.turn = createAuthFlowHelpers().abort(options);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export function credentialsAuthChallenge(challengeId, options = {}) {
|
|
155
|
+
return {
|
|
156
|
+
kind: "challenge",
|
|
157
|
+
challengeId,
|
|
158
|
+
...options,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function expectedInputFromFields(fields) {
|
|
162
|
+
return {
|
|
163
|
+
type: "object",
|
|
164
|
+
properties: Object.fromEntries(Object.entries(fields).map(([name, field]) => [
|
|
165
|
+
name,
|
|
166
|
+
{
|
|
167
|
+
type: "string",
|
|
168
|
+
...(field.type === "email" ? { format: "email" } : {}),
|
|
169
|
+
...(field.type === "password" ? { format: "password" } : {}),
|
|
170
|
+
...(field.type === "otp" ? { format: "otp" } : {}),
|
|
171
|
+
...(field.labelKey ? { nameKey: field.labelKey } : {}),
|
|
172
|
+
...(field.descriptionKey ? { descriptionKey: field.descriptionKey } : {}),
|
|
173
|
+
...(field.placeholderKey ? { placeholderKey: field.placeholderKey } : {}),
|
|
174
|
+
...(field.sensitive || field.type === "password" || field.type === "otp"
|
|
175
|
+
? { sensitive: true }
|
|
176
|
+
: {}),
|
|
177
|
+
},
|
|
178
|
+
])),
|
|
179
|
+
required: Object.entries(fields)
|
|
180
|
+
.filter(([, field]) => field.required !== false)
|
|
181
|
+
.map(([name]) => name),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function collectMissingFields(fields, input) {
|
|
185
|
+
return Object.entries(fields)
|
|
186
|
+
.filter(([, field]) => field.required !== false)
|
|
187
|
+
.map(([name]) => name)
|
|
188
|
+
.filter((name) => {
|
|
189
|
+
const value = input?.[name];
|
|
190
|
+
return typeof value !== "string" || value.trim().length === 0;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function normalizeInput(fields, input) {
|
|
194
|
+
const result = {};
|
|
195
|
+
for (const name of Object.keys(fields)) {
|
|
196
|
+
const value = input?.[name];
|
|
197
|
+
result[name] = typeof value === "string" ? value : "";
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
function assertCredentialKeys(credentialKeys, credential) {
|
|
202
|
+
const missing = credentialKeys.filter((key) => {
|
|
203
|
+
const value = credential[key];
|
|
204
|
+
return typeof value !== "string" || value.length === 0;
|
|
205
|
+
});
|
|
206
|
+
if (missing.length > 0) {
|
|
207
|
+
throw new ProviderError(`Credentials auth login completed without required credential key(s): ${missing.join(", ")}`, {
|
|
208
|
+
code: "credentials_auth_missing_credential_keys",
|
|
209
|
+
fix: "Return every credentialKeys entry from defineCredentialsAuth({ login }) as result.credential. Gateway persists only auth.flow complete data.credential into the connection.",
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function getPendingChallenge(ctx) {
|
|
214
|
+
const value = ctx.context.get(CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY);
|
|
215
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
216
|
+
return null;
|
|
217
|
+
const record = value;
|
|
218
|
+
if (typeof record.challengeId !== "string")
|
|
219
|
+
return null;
|
|
220
|
+
const state = record.state && typeof record.state === "object" && !Array.isArray(record.state)
|
|
221
|
+
? record.state
|
|
222
|
+
: {};
|
|
223
|
+
return {
|
|
224
|
+
challengeId: record.challengeId,
|
|
225
|
+
state,
|
|
226
|
+
...(typeof record.turnId === "string" ? { turnId: record.turnId } : {}),
|
|
227
|
+
...(typeof record.hintKey === "string" ? { hintKey: record.hintKey } : {}),
|
|
228
|
+
...(typeof record.expiresAt === "string" ? { expiresAt: record.expiresAt } : {}),
|
|
229
|
+
...(record.data && typeof record.data === "object" && !Array.isArray(record.data)
|
|
230
|
+
? { data: record.data }
|
|
231
|
+
: {}),
|
|
232
|
+
...(record.timing && typeof record.timing === "object" && !Array.isArray(record.timing)
|
|
233
|
+
? { timing: record.timing }
|
|
234
|
+
: {}),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function setPendingChallenge(ctx, challenge) {
|
|
238
|
+
ctx.context.set(CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, challenge);
|
|
239
|
+
}
|
|
240
|
+
function clearPendingChallenge(ctx) {
|
|
241
|
+
ctx.context.set(CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, null);
|
|
242
|
+
}
|
|
243
|
+
function pendingToChallengeRequest(pending) {
|
|
244
|
+
return {
|
|
245
|
+
kind: "challenge",
|
|
246
|
+
challengeId: pending.challengeId,
|
|
247
|
+
state: pending.state,
|
|
248
|
+
...(pending.turnId ? { turnId: pending.turnId } : {}),
|
|
249
|
+
...(pending.hintKey ? { hintKey: pending.hintKey } : {}),
|
|
250
|
+
...(pending.expiresAt ? { expiresAt: pending.expiresAt } : {}),
|
|
251
|
+
...(pending.data ? { data: pending.data } : {}),
|
|
252
|
+
...(pending.timing ? { timing: pending.timing } : {}),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function retryTurn(expectedInput, missing, retryTurnId) {
|
|
256
|
+
return {
|
|
257
|
+
kind: "retry",
|
|
258
|
+
turnId: retryTurnId,
|
|
259
|
+
expectedInput,
|
|
260
|
+
data: {
|
|
261
|
+
fieldErrors: Object.fromEntries(missing.map((name) => [name, "Required"])),
|
|
262
|
+
fieldErrorKeys: Object.fromEntries(missing.map((name) => [name, "auth.credentials.fieldRequired"])),
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function isChallengeRequest(result) {
|
|
267
|
+
return "kind" in result && result.kind === "challenge";
|
|
268
|
+
}
|
|
269
|
+
function completeTurn(credentialKeys, result, defaultTurnId) {
|
|
270
|
+
if (!result.credential ||
|
|
271
|
+
typeof result.credential !== "object" ||
|
|
272
|
+
Array.isArray(result.credential)) {
|
|
273
|
+
throw new ProviderError("Credentials auth login completed without a credential object", {
|
|
274
|
+
code: "credentials_auth_missing_credential",
|
|
275
|
+
fix: "Return { credential: { ... } } from defineCredentialsAuth handlers. Gateway persists only auth.flow complete data.credential into the connection.",
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
assertCredentialKeys(credentialKeys, result.credential);
|
|
279
|
+
return {
|
|
280
|
+
kind: "complete",
|
|
281
|
+
turnId: result.turnId ?? defaultTurnId,
|
|
282
|
+
...(result.expiresAt ? { expiresAt: result.expiresAt } : {}),
|
|
283
|
+
data: {
|
|
284
|
+
...(result.data ?? {}),
|
|
285
|
+
credential: result.credential,
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function challengeTurn(definition, request) {
|
|
290
|
+
const expectedInput = definition.fields ? expectedInputFromFields(definition.fields) : undefined;
|
|
291
|
+
return {
|
|
292
|
+
kind: expectedInput ? "form" : "pending",
|
|
293
|
+
turnId: request.turnId ?? definition.turnId ?? `credentials.${request.challengeId}`,
|
|
294
|
+
...(request.expiresAt ? { expiresAt: request.expiresAt } : {}),
|
|
295
|
+
...((request.hintKey ?? definition.hintKey)
|
|
296
|
+
? { hintKey: request.hintKey ?? definition.hintKey }
|
|
297
|
+
: {}),
|
|
298
|
+
...((request.timing ?? definition.timing)
|
|
299
|
+
? { timing: request.timing ?? definition.timing }
|
|
300
|
+
: {}),
|
|
301
|
+
...(expectedInput ? { expectedInput } : {}),
|
|
302
|
+
data: {
|
|
303
|
+
...(request.data ?? {}),
|
|
304
|
+
challengeId: request.challengeId,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
async function resolveAuthResult(ctx, credentialKeys, challenges, result, completeTurnId) {
|
|
309
|
+
if (!result || typeof result !== "object") {
|
|
310
|
+
throw new AuthError("Credentials auth login did not return a result", {
|
|
311
|
+
code: "credentials_auth_invalid_login_result",
|
|
312
|
+
fix: "Return { credential: { ... } } or credentialsAuthChallenge(...) from defineCredentialsAuth handlers.",
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (!isChallengeRequest(result)) {
|
|
316
|
+
clearPendingChallenge(ctx);
|
|
317
|
+
return completeTurn(credentialKeys, result, completeTurnId);
|
|
318
|
+
}
|
|
319
|
+
const definition = challenges[result.challengeId];
|
|
320
|
+
if (!definition) {
|
|
321
|
+
throw new ProviderError(`Credentials auth requested unknown challenge "${result.challengeId}"`, {
|
|
322
|
+
code: "credentials_auth_unknown_challenge",
|
|
323
|
+
fix: `Add challenges.${result.challengeId} to defineCredentialsAuth({ challenges }).`,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
setPendingChallenge(ctx, {
|
|
327
|
+
challengeId: result.challengeId,
|
|
328
|
+
state: result.state ?? {},
|
|
329
|
+
...(result.turnId ? { turnId: result.turnId } : {}),
|
|
330
|
+
...(result.hintKey ? { hintKey: result.hintKey } : {}),
|
|
331
|
+
...(result.expiresAt ? { expiresAt: result.expiresAt } : {}),
|
|
332
|
+
...(result.data ? { data: result.data } : {}),
|
|
333
|
+
...(result.timing ? { timing: result.timing } : {}),
|
|
334
|
+
});
|
|
335
|
+
return challengeTurn(definition, result);
|
|
336
|
+
}
|
|
337
|
+
async function continuePendingChallenge(ctx, credentialKeys, challenges, pending, rawInput, completeTurnId) {
|
|
338
|
+
const definition = challenges[pending.challengeId];
|
|
339
|
+
if (!definition) {
|
|
340
|
+
throw new ProviderError(`Credentials auth has pending unknown challenge "${pending.challengeId}"`, { code: "credentials_auth_unknown_pending_challenge" });
|
|
341
|
+
}
|
|
342
|
+
if (!definition.fields || !definition.verify) {
|
|
343
|
+
return challengeTurn(definition, pendingToChallengeRequest(pending));
|
|
344
|
+
}
|
|
345
|
+
const missing = collectMissingFields(definition.fields, rawInput);
|
|
346
|
+
const expectedInput = expectedInputFromFields(definition.fields);
|
|
347
|
+
if (missing.length > 0) {
|
|
348
|
+
return retryTurn(expectedInput, missing, definition.retryTurnId ?? `credentials.${pending.challengeId}.retry`);
|
|
349
|
+
}
|
|
350
|
+
const result = await definition.verify(ctx, normalizeInput(definition.fields, rawInput), pending.state);
|
|
351
|
+
return await resolveAuthResult(ctx, credentialKeys, challenges, result, completeTurnId);
|
|
352
|
+
}
|
|
353
|
+
async function pollPendingChallenge(ctx, credentialKeys, challenges, pending, completeTurnId) {
|
|
354
|
+
const definition = challenges[pending.challengeId];
|
|
355
|
+
if (!definition) {
|
|
356
|
+
throw new ProviderError(`Credentials auth has pending unknown challenge "${pending.challengeId}"`, { code: "credentials_auth_unknown_pending_challenge" });
|
|
357
|
+
}
|
|
358
|
+
if (!definition.poll) {
|
|
359
|
+
return challengeTurn(definition, pendingToChallengeRequest(pending));
|
|
360
|
+
}
|
|
361
|
+
const result = await definition.poll(ctx, pending.state);
|
|
362
|
+
if (!result) {
|
|
363
|
+
return {
|
|
364
|
+
...challengeTurn(definition, pendingToChallengeRequest(pending)),
|
|
365
|
+
turnId: definition.pendingTurnId ??
|
|
366
|
+
definition.turnId ??
|
|
367
|
+
`credentials.${pending.challengeId}.pending`,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
return await resolveAuthResult(ctx, credentialKeys, challenges, result, completeTurnId);
|
|
371
|
+
}
|
|
372
|
+
export function defineCredentialsAuth(options) {
|
|
373
|
+
if (Object.keys(options.fields).length === 0) {
|
|
374
|
+
throw new ProviderError("defineCredentialsAuth requires at least one field", {
|
|
375
|
+
fix: 'Pass fields such as { email: { type: "email" }, password: { type: "password" } }.',
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const expectedInput = expectedInputFromFields(options.fields);
|
|
379
|
+
const retryTurnId = options.retryTurnId ?? "credentials.retry";
|
|
380
|
+
const completeTurnId = options.completeTurnId ?? "credentials.complete";
|
|
381
|
+
const challenges = (options.challenges ?? {});
|
|
382
|
+
return {
|
|
383
|
+
auth: {
|
|
384
|
+
mode: "credentials",
|
|
385
|
+
flow: {
|
|
386
|
+
start: async () => ({
|
|
387
|
+
kind: "form",
|
|
388
|
+
turnId: options.startTurnId ?? "credentials.start",
|
|
389
|
+
...(options.hintKey ? { hintKey: options.hintKey } : {}),
|
|
390
|
+
expectedInput,
|
|
391
|
+
}),
|
|
392
|
+
continue: async (ctx, rawInput) => {
|
|
393
|
+
const pending = getPendingChallenge(ctx);
|
|
394
|
+
if (pending) {
|
|
395
|
+
return await continuePendingChallenge(ctx, options.credentialKeys, challenges, pending, rawInput, completeTurnId);
|
|
396
|
+
}
|
|
397
|
+
const missing = collectMissingFields(options.fields, rawInput);
|
|
398
|
+
if (missing.length > 0) {
|
|
399
|
+
return retryTurn(expectedInput, missing, retryTurnId);
|
|
400
|
+
}
|
|
401
|
+
const result = await options.login(ctx, normalizeInput(options.fields, rawInput));
|
|
402
|
+
return await resolveAuthResult(ctx, options.credentialKeys, challenges, result, completeTurnId);
|
|
403
|
+
},
|
|
404
|
+
poll: async (ctx) => {
|
|
405
|
+
const pending = getPendingChallenge(ctx);
|
|
406
|
+
if (!pending) {
|
|
407
|
+
return {
|
|
408
|
+
kind: "pending",
|
|
409
|
+
turnId: "credentials.noPendingChallenge",
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
return await pollPendingChallenge(ctx, options.credentialKeys, challenges, pending, completeTurnId);
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
credential: {
|
|
417
|
+
keys: Array.from(options.credentialKeys),
|
|
418
|
+
...(options.storesReusableSecret === undefined
|
|
419
|
+
? {}
|
|
420
|
+
: { storesReusableSecret: options.storesReusableSecret }),
|
|
421
|
+
...(options.justification ? { justification: options.justification } : {}),
|
|
422
|
+
},
|
|
423
|
+
context: {
|
|
424
|
+
keys: Array.from(new Set([CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, ...(options.contextKeys ?? [])])),
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthFlowDefinition, AuthTurn } from "../types";
|
|
1
|
+
import type { AuthFlowDefinition, AuthTurn } from "../types.js";
|
|
2
2
|
type JsonObject = Record<string, unknown>;
|
|
3
3
|
export declare function validateCeremonyOutput(turn: unknown): AuthTurn;
|
|
4
4
|
export declare function createOAuth2Ceremony(options: {
|
package/dist/ceremonies/index.js
CHANGED
|
@@ -1,36 +1,12 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
kind: { type: "string", minLength: 1 },
|
|
11
|
-
turnId: { type: "string", minLength: 1 },
|
|
12
|
-
expiresAt: { type: "string", minLength: 1 },
|
|
13
|
-
data: {
|
|
14
|
-
type: "object",
|
|
15
|
-
additionalProperties: true,
|
|
16
|
-
},
|
|
17
|
-
expectedInput: {
|
|
18
|
-
type: "object",
|
|
19
|
-
additionalProperties: true,
|
|
20
|
-
},
|
|
21
|
-
hint: { type: "string" },
|
|
22
|
-
hintKey: { type: "string" },
|
|
23
|
-
timing: {
|
|
24
|
-
type: "object",
|
|
25
|
-
additionalProperties: false,
|
|
26
|
-
properties: {
|
|
27
|
-
suggestedPollIntervalMs: { type: "number", minimum: 1 },
|
|
28
|
-
maxWaitMs: { type: "number", minimum: 1 },
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
const validateAuthTurn = ajv.compile(authTurnSchema);
|
|
2
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
3
|
+
import { AUTH_TURN_SCHEMA } from "../auth-turn/index.js";
|
|
4
|
+
import { FlowExpiredError, ProviderSecretError, TurnValidationError, ValidationError, } from "../errors.js";
|
|
5
|
+
const ajv = new Ajv2020({ allErrors: true, strict: true, strictSchema: true });
|
|
6
|
+
// Runtime ceremony-output validation derives from the exported versioned
|
|
7
|
+
// contract: it compiles the exact AUTH_TURN_SCHEMA document shipped at
|
|
8
|
+
// dist/auth-turn/auth-turn.v1.schema.json, so the two cannot drift.
|
|
9
|
+
const validateAuthTurn = ajv.compile(AUTH_TURN_SCHEMA);
|
|
34
10
|
const OAUTH2_STATE_KEY = "__oauth2_state";
|
|
35
11
|
const OAUTH2_PKCE_VERIFIER_KEY = "__oauth2_pkce_verifier";
|
|
36
12
|
const DEVICE_FLOW_KEY = "__device_flow";
|
|
@@ -62,11 +38,7 @@ function getRequiredEnv(ctx, key) {
|
|
|
62
38
|
return value;
|
|
63
39
|
}
|
|
64
40
|
function toBase64Url(input) {
|
|
65
|
-
return input
|
|
66
|
-
.toString("base64")
|
|
67
|
-
.replace(/\+/g, "-")
|
|
68
|
-
.replace(/\//g, "_")
|
|
69
|
-
.replace(/=+$/g, "");
|
|
41
|
+
return input.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
70
42
|
}
|
|
71
43
|
function createCodeVerifier() {
|
|
72
44
|
return toBase64Url(randomBytes(32));
|
|
@@ -124,8 +96,7 @@ function withDeclaredFormFieldOrder(expectedInput) {
|
|
|
124
96
|
return expectedInput;
|
|
125
97
|
}
|
|
126
98
|
const existingOrder = expectedInput[FORM_FIELD_ORDER_EXTENSION];
|
|
127
|
-
if (Array.isArray(existingOrder) &&
|
|
128
|
-
existingOrder.every((value) => typeof value === "string")) {
|
|
99
|
+
if (Array.isArray(existingOrder) && existingOrder.every((value) => typeof value === "string")) {
|
|
129
100
|
return expectedInput;
|
|
130
101
|
}
|
|
131
102
|
return {
|
|
@@ -242,8 +213,7 @@ export function createDeviceFlowCeremony(options) {
|
|
|
242
213
|
});
|
|
243
214
|
const data = ensureRecord(response.data);
|
|
244
215
|
const errorCode = getString(data, "error");
|
|
245
|
-
if (errorCode === "authorization_pending" ||
|
|
246
|
-
errorCode === "slow_down") {
|
|
216
|
+
if (errorCode === "authorization_pending" || errorCode === "slow_down") {
|
|
247
217
|
return createTurn("poll", {
|
|
248
218
|
data,
|
|
249
219
|
hint: "Authorization pending.",
|
|
@@ -369,7 +339,7 @@ export function createFormCeremony(options) {
|
|
|
369
339
|
return {
|
|
370
340
|
start: async () => validateCeremonyOutput(buildJsonSchemaForm(options.schema, options.hint ?? "Provide the required input to continue.")),
|
|
371
341
|
continue: (ctx, input = {}) => runCeremonyHandler(async () => {
|
|
372
|
-
const { prevalidate } = await import("../runtime/prevalidate");
|
|
342
|
+
const { prevalidate } = await import("../runtime/prevalidate.js");
|
|
373
343
|
const result = prevalidate(options.schema, input);
|
|
374
344
|
if (!result.valid) {
|
|
375
345
|
throw new ValidationError("Form input failed validation.", {
|
|
@@ -378,9 +348,7 @@ export function createFormCeremony(options) {
|
|
|
378
348
|
}
|
|
379
349
|
return createTurn("complete", {
|
|
380
350
|
data: {
|
|
381
|
-
credential: options.mapCredential
|
|
382
|
-
? options.mapCredential(input)
|
|
383
|
-
: input,
|
|
351
|
+
credential: options.mapCredential ? options.mapCredential(input) : input,
|
|
384
352
|
},
|
|
385
353
|
hint: "Form completed.",
|
|
386
354
|
});
|
|
@@ -452,9 +420,7 @@ export function createSwitchCeremony(options) {
|
|
|
452
420
|
})),
|
|
453
421
|
continue: (ctx, input = {}) => runCeremonyHandler(async () => {
|
|
454
422
|
const storedChoice = ctx.context.get(SWITCH_SELECTION_KEY);
|
|
455
|
-
const choice = typeof storedChoice === "string"
|
|
456
|
-
? storedChoice
|
|
457
|
-
: getString(input, "choice");
|
|
423
|
+
const choice = typeof storedChoice === "string" ? storedChoice : getString(input, "choice");
|
|
458
424
|
if (!choice || !options.choices[choice]) {
|
|
459
425
|
throw new ValidationError("A valid choice is required.");
|
|
460
426
|
}
|
package/dist/cli/commands.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ApifuseCommandName = "create" | "dev" | "check" | "submit-check" | "bounty-check" | "record" | "test" | "perf";
|
|
1
|
+
export type ApifuseCommandName = "create" | "dev" | "check" | "sync-assets" | "submit-check" | "bounty-check" | "record" | "test" | "perf";
|
|
2
2
|
export type ApifuseCommandManifest = {
|
|
3
3
|
name: ApifuseCommandName;
|
|
4
4
|
summary: string;
|
package/dist/cli/commands.js
CHANGED
|
@@ -23,6 +23,13 @@ export const COMMAND_MANIFEST = {
|
|
|
23
23
|
examples: ["apifuse check .", "apifuse check providers/korea-air-quality"],
|
|
24
24
|
modulePath: "./apifuse-check",
|
|
25
25
|
},
|
|
26
|
+
"sync-assets": {
|
|
27
|
+
name: "sync-assets",
|
|
28
|
+
summary: "Regenerate SDK-managed agent prompt assets (AGENTS.md, .agents/skills, symlinks, manifest) for the installed SDK version.",
|
|
29
|
+
usage: "apifuse sync-assets [path] [--check]",
|
|
30
|
+
examples: ["apifuse sync-assets .", "apifuse sync-assets . --check"],
|
|
31
|
+
modulePath: "./apifuse-sync-assets",
|
|
32
|
+
},
|
|
26
33
|
"submit-check": {
|
|
27
34
|
name: "submit-check",
|
|
28
35
|
summary: "Score provider bounty submission readiness and emit checklist evidence.",
|
|
@@ -73,6 +80,7 @@ export const COMMAND_ORDER = [
|
|
|
73
80
|
"create",
|
|
74
81
|
"dev",
|
|
75
82
|
"check",
|
|
83
|
+
"sync-assets",
|
|
76
84
|
"submit-check",
|
|
77
85
|
"record",
|
|
78
86
|
"test",
|
package/dist/cli/create.d.ts
CHANGED
|
@@ -26,7 +26,10 @@ export type CreateResolvedOptions = {
|
|
|
26
26
|
};
|
|
27
27
|
export type ProviderPlanFile = {
|
|
28
28
|
path: string;
|
|
29
|
+
/** File content, or the symlink target (relative path) for kind "symlink". */
|
|
29
30
|
content: string;
|
|
31
|
+
/** Absent kind means "file" (backward compatible with older consumers). */
|
|
32
|
+
kind?: "file" | "symlink";
|
|
30
33
|
};
|
|
31
34
|
export type ProviderCreatePlan = {
|
|
32
35
|
displayName: string;
|