@apilift/cli 1.7.2 → 1.8.0
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/dist/apilift-collector.js +35 -12
- package/dist/apilift-native-host.js +33 -3
- package/dist/apilift.js +146 -37
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.codex-plugin/plugin.json +1 -1
- package/dist/plugin/skills/discover/references/documented-api.md +4 -2
- package/package.json +2 -2
|
@@ -27962,7 +27962,7 @@ function bodyIssues(v2, ctx) {
|
|
|
27962
27962
|
if (name.toLowerCase() === "content-type" && (v2.form !== void 0 || v2.body !== void 0) && value !== (v2.form !== void 0 ? "application/x-www-form-urlencoded" : "application/json"))
|
|
27963
27963
|
ctx.addIssue({ code: "custom", message: "Content-Type conflicts with declared body encoding" });
|
|
27964
27964
|
}
|
|
27965
|
-
var import__, resourceKinds, resourceKindSchema, segmentSchema, appSlugSchema, actionNameSchema, generatedNameSchema, inputNameSchema, pointerSchema, categorySlugs, categorySchema, title, description, requiredDescription, originSchema, httpUrlSchema, requestUrlSchema, ajv, jsonSchema, valueSchema, htmlFieldSchema, htmlExtractionSchema, continuationSchema, responseSchema, baseRequestSchema, readPreparationSchema, requestSchema, proofPointer, proofComparison, proofKey, proofSelection, proofSchema, appDefinitionSchema, entityDefinitionSchema, generatedSchema, executableActionSchema, unavailableActionSchema, actionDefinitionSchema, locatorSchema, observeSchema, injectSchema, credentialSchema, probeSchema, transportDefinitionSchema, browserContextDefinitionSchema, definitionSchemas;
|
|
27965
|
+
var import__, resourceKinds, resourceKindSchema, segmentSchema, appSlugSchema, actionNameSchema, generatedNameSchema, inputNameSchema, pointerSchema, categorySlugs, categorySchema, title, description, requiredDescription, originSchema, httpUrlSchema, requestUrlSchema, ajv, jsonSchema, valueSchema, htmlFieldSchema, htmlExtractionSchema, continuationSchema, responseSchema, baseRequestSchema, readPreparationSchema, requestSchema, proofPointer, proofComparison, proofKey, proofSelection, proofSchema, appDefinitionSchema, entityDefinitionSchema, generatedSchema, executableActionSchema, unavailableActionSchema, actionDefinitionSchema, locatorSchema, observeSchema, injectSchema, credentialSchema, basicComponent, httpBasicSchema, probeSchema, transportDefinitionSchema, browserContextDefinitionSchema, definitionSchemas;
|
|
27966
27966
|
var init_schema = __esm({
|
|
27967
27967
|
"../../packages/core/dist/definitions/schema.js"() {
|
|
27968
27968
|
"use strict";
|
|
@@ -28103,8 +28103,10 @@ var init_schema = __esm({
|
|
|
28103
28103
|
observeSchema = external_exports.strictObject({ kind: external_exports.literal("observe"), request: external_exports.strictObject({ method: external_exports.string(), urlPattern: external_exports.string() }), extract: external_exports.strictObject({ in: external_exports.enum(["request-header", "response-header", "response-body"]), name: external_exports.string().optional(), path: pointerSchema.optional() }), trigger: external_exports.union([external_exports.strictObject({ kind: external_exports.literal("navigate"), url: httpUrlSchema }), external_exports.strictObject({ kind: external_exports.literal("selector"), selector: external_exports.string() })]).optional(), cache: external_exports.literal("browser-session") });
|
|
28104
28104
|
injectSchema = external_exports.strictObject({ from: external_exports.union([locatorSchema, observeSchema]), to: external_exports.union([external_exports.strictObject({ in: external_exports.literal("header"), name: external_exports.string() }), external_exports.strictObject({ in: external_exports.literal("query"), name: external_exports.string() }), external_exports.strictObject({ in: external_exports.literal("body"), path: pointerSchema }), external_exports.strictObject({ in: external_exports.literal("form"), name: external_exports.string().min(1) })]) });
|
|
28105
28105
|
credentialSchema = external_exports.strictObject({ name: generatedNameSchema, header: external_exports.string().regex(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/), prefix: external_exports.string().max(80).refine((v2) => !/[\r\n]/.test(v2)).optional() });
|
|
28106
|
+
basicComponent = external_exports.union([generatedNameSchema, external_exports.strictObject({ literal: external_exports.string().max(128).refine((v2) => ![...v2].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127), "Control characters are not allowed") })]);
|
|
28107
|
+
httpBasicSchema = external_exports.strictObject({ username: basicComponent.optional(), password: basicComponent.optional() }).refine((v2) => Object.values(v2).some((value) => typeof value === "string"), "Name at least one saved credential field; the omitted component is empty");
|
|
28106
28108
|
probeSchema = external_exports.strictObject({ effect: external_exports.literal("read").optional(), request: requestSchema, response: responseSchema, inputs: jsonSchema.optional() });
|
|
28107
|
-
transportDefinitionSchema = external_exports.strictObject({ title, description: requiredDescription, mode: external_exports.enum(["direct", "browser"]), baseUrl: requestUrlSchema.optional(), allowedOrigins: external_exports.array(originSchema).min(1).max(100), browserContext: segmentSchema.optional(), credentials: external_exports.array(credentialSchema).max(16).optional(), inject: external_exports.array(injectSchema).optional(), identity: readPreparationSchema.extend({ paths: external_exports.array(pointerSchema).min(1).max(8) }).optional(), probes: external_exports.record(external_exports.string(), probeSchema) }).superRefine((v2, ctx) => {
|
|
28109
|
+
transportDefinitionSchema = external_exports.strictObject({ title, description: requiredDescription, mode: external_exports.enum(["direct", "browser"]), baseUrl: requestUrlSchema.optional(), allowedOrigins: external_exports.array(originSchema).min(1).max(100), browserContext: segmentSchema.optional(), credentials: external_exports.array(credentialSchema).max(16).optional(), httpBasic: httpBasicSchema.optional(), inject: external_exports.array(injectSchema).optional(), identity: readPreparationSchema.extend({ paths: external_exports.array(pointerSchema).min(1).max(8) }).optional(), probes: external_exports.record(external_exports.string(), probeSchema) }).superRefine((v2, ctx) => {
|
|
28108
28110
|
if (new Set(v2.allowedOrigins).size !== v2.allowedOrigins.length)
|
|
28109
28111
|
ctx.addIssue({ code: "custom", path: ["allowedOrigins"], message: "Origins must be distinct" });
|
|
28110
28112
|
if (v2.mode === "direct" && !v2.baseUrl)
|
|
@@ -28119,7 +28121,8 @@ var init_schema = __esm({
|
|
|
28119
28121
|
const names = v2.credentials.map((c) => c.name), headers = v2.credentials.map((c) => c.header.toLowerCase());
|
|
28120
28122
|
if (new Set(names).size !== names.length)
|
|
28121
28123
|
ctx.addIssue({ code: "custom", path: ["credentials"], message: "Credential names must be unique" });
|
|
28122
|
-
|
|
28124
|
+
const ordinaryHeaders = v2.credentials.filter((c) => !Object.values(v2.httpBasic ?? {}).includes(c.name)).map((c) => c.header.toLowerCase());
|
|
28125
|
+
if (new Set(ordinaryHeaders).size !== ordinaryHeaders.length)
|
|
28123
28126
|
ctx.addIssue({ code: "custom", path: ["credentials"], message: "Credential headers must be unique" });
|
|
28124
28127
|
for (const [i, header] of headers.entries())
|
|
28125
28128
|
if (["cookie", "host", "content-length", "origin", "referer"].includes(header))
|
|
@@ -28129,6 +28132,25 @@ var init_schema = __esm({
|
|
|
28129
28132
|
ctx.addIssue({ code: "custom", path: ["allowedOrigins", i], message: "Credentials require HTTPS origins" });
|
|
28130
28133
|
});
|
|
28131
28134
|
}
|
|
28135
|
+
if (v2.httpBasic) {
|
|
28136
|
+
if (v2.mode !== "direct")
|
|
28137
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic"], message: "HTTP Basic encoding requires direct mode" });
|
|
28138
|
+
const names = Object.values(v2.httpBasic).filter((value) => typeof value === "string");
|
|
28139
|
+
if (new Set(names).size !== names.length)
|
|
28140
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic"], message: "Basic username and password must use distinct saved fields" });
|
|
28141
|
+
for (const [slot, name] of Object.entries(v2.httpBasic)) {
|
|
28142
|
+
if (typeof name !== "string") {
|
|
28143
|
+
if (slot === "username" && name.literal.includes(":"))
|
|
28144
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic", slot], message: "Basic username cannot contain a colon" });
|
|
28145
|
+
continue;
|
|
28146
|
+
}
|
|
28147
|
+
const field = v2.credentials?.find((c) => c.name === name);
|
|
28148
|
+
if (!field || field.header.toLowerCase() !== "authorization" || field.prefix)
|
|
28149
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic", slot], message: "Reference a raw saved field with header Authorization and no prefix" });
|
|
28150
|
+
}
|
|
28151
|
+
if (v2.credentials?.some((c) => c.header.toLowerCase() === "authorization" && !names.includes(c.name)))
|
|
28152
|
+
ctx.addIssue({ code: "custom", path: ["credentials"], message: "Basic authentication owns Authorization; remove conflicting credentials" });
|
|
28153
|
+
}
|
|
28132
28154
|
for (const origin of v2.allowedOrigins)
|
|
28133
28155
|
if (!Object.hasOwn(v2.probes, origin))
|
|
28134
28156
|
ctx.addIssue({ code: "custom", path: ["probes"], message: `Missing probe for ${origin}` });
|
|
@@ -28173,10 +28195,19 @@ var init_rules = __esm({
|
|
|
28173
28195
|
}
|
|
28174
28196
|
});
|
|
28175
28197
|
|
|
28198
|
+
// ../../packages/core/dist/definitions/credential-contract.js
|
|
28199
|
+
var init_credential_contract = __esm({
|
|
28200
|
+
"../../packages/core/dist/definitions/credential-contract.js"() {
|
|
28201
|
+
"use strict";
|
|
28202
|
+
init_json();
|
|
28203
|
+
}
|
|
28204
|
+
});
|
|
28205
|
+
|
|
28176
28206
|
// ../../packages/core/dist/definitions/metadata.js
|
|
28177
28207
|
var init_metadata = __esm({
|
|
28178
28208
|
"../../packages/core/dist/definitions/metadata.js"() {
|
|
28179
28209
|
"use strict";
|
|
28210
|
+
init_credential_contract();
|
|
28180
28211
|
}
|
|
28181
28212
|
});
|
|
28182
28213
|
|
|
@@ -28221,14 +28252,6 @@ var init_categories = __esm({
|
|
|
28221
28252
|
}
|
|
28222
28253
|
});
|
|
28223
28254
|
|
|
28224
|
-
// ../../packages/core/dist/definitions/credential-contract.js
|
|
28225
|
-
var init_credential_contract = __esm({
|
|
28226
|
-
"../../packages/core/dist/definitions/credential-contract.js"() {
|
|
28227
|
-
"use strict";
|
|
28228
|
-
init_json();
|
|
28229
|
-
}
|
|
28230
|
-
});
|
|
28231
|
-
|
|
28232
28255
|
// ../../packages/core/dist/definitions/index.js
|
|
28233
28256
|
var init_definitions = __esm({
|
|
28234
28257
|
"../../packages/core/dist/definitions/index.js"() {
|
|
@@ -28438,7 +28461,7 @@ function createCliApi(baseUrl, fetcher = fetch, token, sessionId) {
|
|
|
28438
28461
|
const transport = async (input2, init) => {
|
|
28439
28462
|
const method = init?.method ?? "GET";
|
|
28440
28463
|
const headers = new Headers(init?.headers);
|
|
28441
|
-
headers.set("Apilift-Capabilities", "distributed-rollouts-v1,discovery-proof-v2,dynamic-bindings-v1,collection-proof-v3,web-responses-v1,bounded-catalog-v1");
|
|
28464
|
+
headers.set("Apilift-Capabilities", "distributed-rollouts-v1,discovery-proof-v2,dynamic-bindings-v1,collection-proof-v3,web-responses-v1,bounded-catalog-v1,http-basic-v1");
|
|
28442
28465
|
headers.set("Apilift-Client", `cli/${cliVersion}`);
|
|
28443
28466
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
28444
28467
|
if (sessionId) headers.set("Apilift-Session", sessionId);
|
|
@@ -29173,8 +29173,10 @@ var locatorSchema = external_exports.strictObject({ kind: external_exports.liter
|
|
|
29173
29173
|
var observeSchema = external_exports.strictObject({ kind: external_exports.literal("observe"), request: external_exports.strictObject({ method: external_exports.string(), urlPattern: external_exports.string() }), extract: external_exports.strictObject({ in: external_exports.enum(["request-header", "response-header", "response-body"]), name: external_exports.string().optional(), path: pointerSchema.optional() }), trigger: external_exports.union([external_exports.strictObject({ kind: external_exports.literal("navigate"), url: httpUrlSchema }), external_exports.strictObject({ kind: external_exports.literal("selector"), selector: external_exports.string() })]).optional(), cache: external_exports.literal("browser-session") });
|
|
29174
29174
|
var injectSchema = external_exports.strictObject({ from: external_exports.union([locatorSchema, observeSchema]), to: external_exports.union([external_exports.strictObject({ in: external_exports.literal("header"), name: external_exports.string() }), external_exports.strictObject({ in: external_exports.literal("query"), name: external_exports.string() }), external_exports.strictObject({ in: external_exports.literal("body"), path: pointerSchema }), external_exports.strictObject({ in: external_exports.literal("form"), name: external_exports.string().min(1) })]) });
|
|
29175
29175
|
var credentialSchema = external_exports.strictObject({ name: generatedNameSchema, header: external_exports.string().regex(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/), prefix: external_exports.string().max(80).refine((v) => !/[\r\n]/.test(v)).optional() });
|
|
29176
|
+
var basicComponent = external_exports.union([generatedNameSchema, external_exports.strictObject({ literal: external_exports.string().max(128).refine((v) => ![...v].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127), "Control characters are not allowed") })]);
|
|
29177
|
+
var httpBasicSchema = external_exports.strictObject({ username: basicComponent.optional(), password: basicComponent.optional() }).refine((v) => Object.values(v).some((value) => typeof value === "string"), "Name at least one saved credential field; the omitted component is empty");
|
|
29176
29178
|
var probeSchema = external_exports.strictObject({ effect: external_exports.literal("read").optional(), request: requestSchema, response: responseSchema, inputs: jsonSchema.optional() });
|
|
29177
|
-
var transportDefinitionSchema = external_exports.strictObject({ title, description: requiredDescription, mode: external_exports.enum(["direct", "browser"]), baseUrl: requestUrlSchema.optional(), allowedOrigins: external_exports.array(originSchema).min(1).max(100), browserContext: segmentSchema.optional(), credentials: external_exports.array(credentialSchema).max(16).optional(), inject: external_exports.array(injectSchema).optional(), identity: readPreparationSchema.extend({ paths: external_exports.array(pointerSchema).min(1).max(8) }).optional(), probes: external_exports.record(external_exports.string(), probeSchema) }).superRefine((v, ctx) => {
|
|
29179
|
+
var transportDefinitionSchema = external_exports.strictObject({ title, description: requiredDescription, mode: external_exports.enum(["direct", "browser"]), baseUrl: requestUrlSchema.optional(), allowedOrigins: external_exports.array(originSchema).min(1).max(100), browserContext: segmentSchema.optional(), credentials: external_exports.array(credentialSchema).max(16).optional(), httpBasic: httpBasicSchema.optional(), inject: external_exports.array(injectSchema).optional(), identity: readPreparationSchema.extend({ paths: external_exports.array(pointerSchema).min(1).max(8) }).optional(), probes: external_exports.record(external_exports.string(), probeSchema) }).superRefine((v, ctx) => {
|
|
29178
29180
|
if (new Set(v.allowedOrigins).size !== v.allowedOrigins.length)
|
|
29179
29181
|
ctx.addIssue({ code: "custom", path: ["allowedOrigins"], message: "Origins must be distinct" });
|
|
29180
29182
|
if (v.mode === "direct" && !v.baseUrl)
|
|
@@ -29189,7 +29191,8 @@ var transportDefinitionSchema = external_exports.strictObject({ title, descripti
|
|
|
29189
29191
|
const names2 = v.credentials.map((c) => c.name), headers = v.credentials.map((c) => c.header.toLowerCase());
|
|
29190
29192
|
if (new Set(names2).size !== names2.length)
|
|
29191
29193
|
ctx.addIssue({ code: "custom", path: ["credentials"], message: "Credential names must be unique" });
|
|
29192
|
-
|
|
29194
|
+
const ordinaryHeaders = v.credentials.filter((c) => !Object.values(v.httpBasic ?? {}).includes(c.name)).map((c) => c.header.toLowerCase());
|
|
29195
|
+
if (new Set(ordinaryHeaders).size !== ordinaryHeaders.length)
|
|
29193
29196
|
ctx.addIssue({ code: "custom", path: ["credentials"], message: "Credential headers must be unique" });
|
|
29194
29197
|
for (const [i, header] of headers.entries())
|
|
29195
29198
|
if (["cookie", "host", "content-length", "origin", "referer"].includes(header))
|
|
@@ -29199,6 +29202,25 @@ var transportDefinitionSchema = external_exports.strictObject({ title, descripti
|
|
|
29199
29202
|
ctx.addIssue({ code: "custom", path: ["allowedOrigins", i], message: "Credentials require HTTPS origins" });
|
|
29200
29203
|
});
|
|
29201
29204
|
}
|
|
29205
|
+
if (v.httpBasic) {
|
|
29206
|
+
if (v.mode !== "direct")
|
|
29207
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic"], message: "HTTP Basic encoding requires direct mode" });
|
|
29208
|
+
const names2 = Object.values(v.httpBasic).filter((value) => typeof value === "string");
|
|
29209
|
+
if (new Set(names2).size !== names2.length)
|
|
29210
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic"], message: "Basic username and password must use distinct saved fields" });
|
|
29211
|
+
for (const [slot, name] of Object.entries(v.httpBasic)) {
|
|
29212
|
+
if (typeof name !== "string") {
|
|
29213
|
+
if (slot === "username" && name.literal.includes(":"))
|
|
29214
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic", slot], message: "Basic username cannot contain a colon" });
|
|
29215
|
+
continue;
|
|
29216
|
+
}
|
|
29217
|
+
const field = v.credentials?.find((c) => c.name === name);
|
|
29218
|
+
if (!field || field.header.toLowerCase() !== "authorization" || field.prefix)
|
|
29219
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic", slot], message: "Reference a raw saved field with header Authorization and no prefix" });
|
|
29220
|
+
}
|
|
29221
|
+
if (v.credentials?.some((c) => c.header.toLowerCase() === "authorization" && !names2.includes(c.name)))
|
|
29222
|
+
ctx.addIssue({ code: "custom", path: ["credentials"], message: "Basic authentication owns Authorization; remove conflicting credentials" });
|
|
29223
|
+
}
|
|
29202
29224
|
for (const origin of v.allowedOrigins)
|
|
29203
29225
|
if (!Object.hasOwn(v.probes, origin))
|
|
29204
29226
|
ctx.addIssue({ code: "custom", path: ["probes"], message: `Missing probe for ${origin}` });
|
|
@@ -29497,6 +29519,13 @@ function validateDefinition(kind, name, value) {
|
|
|
29497
29519
|
|
|
29498
29520
|
// ../../packages/core/dist/definitions/analysis.js
|
|
29499
29521
|
var import__2 = __toESM(require__(), 1);
|
|
29522
|
+
|
|
29523
|
+
// ../../packages/core/dist/definitions/credential-contract.js
|
|
29524
|
+
function credentialAuthoringIssues(transport) {
|
|
29525
|
+
return (transport.credentials ?? []).flatMap((field, index) => field.header.toLowerCase() === "authorization" && /^Basic(?:\s|$)/iu.test(field.prefix ?? "") ? [{ path: `/credentials/${index}/prefix`, message: "Use raw saved credential fields with httpBasic and no prefix. Apilift encodes at dispatch; never ask the person to encode a key." }] : []);
|
|
29526
|
+
}
|
|
29527
|
+
|
|
29528
|
+
// ../../packages/core/dist/definitions/analysis.js
|
|
29500
29529
|
var ajv2 = new import__2.default({ strict: false, validateFormats: false });
|
|
29501
29530
|
|
|
29502
29531
|
// ../../packages/core/dist/definitions/categories.js
|
|
@@ -29963,7 +29992,7 @@ function createCliApi(baseUrl, fetcher = fetch, token, sessionId) {
|
|
|
29963
29992
|
const transport = async (input2, init) => {
|
|
29964
29993
|
const method = init?.method ?? "GET";
|
|
29965
29994
|
const headers = new Headers(init?.headers);
|
|
29966
|
-
headers.set("Apilift-Capabilities", "distributed-rollouts-v1,discovery-proof-v2,dynamic-bindings-v1,collection-proof-v3,web-responses-v1,bounded-catalog-v1");
|
|
29995
|
+
headers.set("Apilift-Capabilities", "distributed-rollouts-v1,discovery-proof-v2,dynamic-bindings-v1,collection-proof-v3,web-responses-v1,bounded-catalog-v1,http-basic-v1");
|
|
29967
29996
|
headers.set("Apilift-Client", `cli/${cliVersion}`);
|
|
29968
29997
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
29969
29998
|
if (sessionId) headers.set("Apilift-Session", sessionId);
|
|
@@ -31188,6 +31217,7 @@ function readCredentialDraft(filePath) {
|
|
|
31188
31217
|
throw new Error(`Repair the selected transport draft at ${filePath}: ${paths.join(", ") || "/definition"}. Run discover check for field guidance`);
|
|
31189
31218
|
}
|
|
31190
31219
|
const transport = checked.definition;
|
|
31220
|
+
if (credentialAuthoringIssues(transport).length) throw new Error(`Replace the encoded Basic credential contract in ${filePath}: declare raw saved fields and definition.httpBasic with no prefix. Apilift encodes at dispatch; do not ask the person to encode a key`);
|
|
31191
31221
|
if (!transport.description.trim()) throw new Error(`Research credential acquisition and edit /definition/description in ${filePath}. Run discover check before secure setup`);
|
|
31192
31222
|
return { transport, fingerprint: createHash4("sha256").update(canonicalJson(transport)).digest("hex") };
|
|
31193
31223
|
}
|
package/dist/apilift.js
CHANGED
|
@@ -28032,7 +28032,7 @@ function bodyIssues(v, ctx) {
|
|
|
28032
28032
|
if (name.toLowerCase() === "content-type" && (v.form !== void 0 || v.body !== void 0) && value !== (v.form !== void 0 ? "application/x-www-form-urlencoded" : "application/json"))
|
|
28033
28033
|
ctx.addIssue({ code: "custom", message: "Content-Type conflicts with declared body encoding" });
|
|
28034
28034
|
}
|
|
28035
|
-
var import__, resourceKinds, resourceKindSchema, segmentSchema, appSlugSchema, actionNameSchema, generatedNameSchema, inputNameSchema, pointerSchema, categorySlugs, categorySchema, title, description, requiredDescription, originSchema, httpUrlSchema, requestUrlSchema, ajv, jsonSchema, valueSchema, htmlFieldSchema, htmlExtractionSchema, continuationSchema, responseSchema, baseRequestSchema, readPreparationSchema, requestSchema, proofPointer, proofComparison, proofKey, proofSelection, proofSchema, appDefinitionSchema, entityDefinitionSchema, generatedSchema, executableActionSchema, unavailableActionSchema, actionDefinitionSchema, locatorSchema, observeSchema, injectSchema, credentialSchema, probeSchema, transportDefinitionSchema, browserContextDefinitionSchema, definitionSchemas;
|
|
28035
|
+
var import__, resourceKinds, resourceKindSchema, segmentSchema, appSlugSchema, actionNameSchema, generatedNameSchema, inputNameSchema, pointerSchema, categorySlugs, categorySchema, title, description, requiredDescription, originSchema, httpUrlSchema, requestUrlSchema, ajv, jsonSchema, valueSchema, htmlFieldSchema, htmlExtractionSchema, continuationSchema, responseSchema, baseRequestSchema, readPreparationSchema, requestSchema, proofPointer, proofComparison, proofKey, proofSelection, proofSchema, appDefinitionSchema, entityDefinitionSchema, generatedSchema, executableActionSchema, unavailableActionSchema, actionDefinitionSchema, locatorSchema, observeSchema, injectSchema, credentialSchema, basicComponent, httpBasicSchema, probeSchema, transportDefinitionSchema, browserContextDefinitionSchema, definitionSchemas;
|
|
28036
28036
|
var init_schema = __esm({
|
|
28037
28037
|
"../../packages/core/dist/definitions/schema.js"() {
|
|
28038
28038
|
"use strict";
|
|
@@ -28173,8 +28173,10 @@ var init_schema = __esm({
|
|
|
28173
28173
|
observeSchema = external_exports.strictObject({ kind: external_exports.literal("observe"), request: external_exports.strictObject({ method: external_exports.string(), urlPattern: external_exports.string() }), extract: external_exports.strictObject({ in: external_exports.enum(["request-header", "response-header", "response-body"]), name: external_exports.string().optional(), path: pointerSchema.optional() }), trigger: external_exports.union([external_exports.strictObject({ kind: external_exports.literal("navigate"), url: httpUrlSchema }), external_exports.strictObject({ kind: external_exports.literal("selector"), selector: external_exports.string() })]).optional(), cache: external_exports.literal("browser-session") });
|
|
28174
28174
|
injectSchema = external_exports.strictObject({ from: external_exports.union([locatorSchema, observeSchema]), to: external_exports.union([external_exports.strictObject({ in: external_exports.literal("header"), name: external_exports.string() }), external_exports.strictObject({ in: external_exports.literal("query"), name: external_exports.string() }), external_exports.strictObject({ in: external_exports.literal("body"), path: pointerSchema }), external_exports.strictObject({ in: external_exports.literal("form"), name: external_exports.string().min(1) })]) });
|
|
28175
28175
|
credentialSchema = external_exports.strictObject({ name: generatedNameSchema, header: external_exports.string().regex(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/), prefix: external_exports.string().max(80).refine((v) => !/[\r\n]/.test(v)).optional() });
|
|
28176
|
+
basicComponent = external_exports.union([generatedNameSchema, external_exports.strictObject({ literal: external_exports.string().max(128).refine((v) => ![...v].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127), "Control characters are not allowed") })]);
|
|
28177
|
+
httpBasicSchema = external_exports.strictObject({ username: basicComponent.optional(), password: basicComponent.optional() }).refine((v) => Object.values(v).some((value) => typeof value === "string"), "Name at least one saved credential field; the omitted component is empty");
|
|
28176
28178
|
probeSchema = external_exports.strictObject({ effect: external_exports.literal("read").optional(), request: requestSchema, response: responseSchema, inputs: jsonSchema.optional() });
|
|
28177
|
-
transportDefinitionSchema = external_exports.strictObject({ title, description: requiredDescription, mode: external_exports.enum(["direct", "browser"]), baseUrl: requestUrlSchema.optional(), allowedOrigins: external_exports.array(originSchema).min(1).max(100), browserContext: segmentSchema.optional(), credentials: external_exports.array(credentialSchema).max(16).optional(), inject: external_exports.array(injectSchema).optional(), identity: readPreparationSchema.extend({ paths: external_exports.array(pointerSchema).min(1).max(8) }).optional(), probes: external_exports.record(external_exports.string(), probeSchema) }).superRefine((v, ctx) => {
|
|
28179
|
+
transportDefinitionSchema = external_exports.strictObject({ title, description: requiredDescription, mode: external_exports.enum(["direct", "browser"]), baseUrl: requestUrlSchema.optional(), allowedOrigins: external_exports.array(originSchema).min(1).max(100), browserContext: segmentSchema.optional(), credentials: external_exports.array(credentialSchema).max(16).optional(), httpBasic: httpBasicSchema.optional(), inject: external_exports.array(injectSchema).optional(), identity: readPreparationSchema.extend({ paths: external_exports.array(pointerSchema).min(1).max(8) }).optional(), probes: external_exports.record(external_exports.string(), probeSchema) }).superRefine((v, ctx) => {
|
|
28178
28180
|
if (new Set(v.allowedOrigins).size !== v.allowedOrigins.length)
|
|
28179
28181
|
ctx.addIssue({ code: "custom", path: ["allowedOrigins"], message: "Origins must be distinct" });
|
|
28180
28182
|
if (v.mode === "direct" && !v.baseUrl)
|
|
@@ -28189,7 +28191,8 @@ var init_schema = __esm({
|
|
|
28189
28191
|
const names2 = v.credentials.map((c) => c.name), headers = v.credentials.map((c) => c.header.toLowerCase());
|
|
28190
28192
|
if (new Set(names2).size !== names2.length)
|
|
28191
28193
|
ctx.addIssue({ code: "custom", path: ["credentials"], message: "Credential names must be unique" });
|
|
28192
|
-
|
|
28194
|
+
const ordinaryHeaders = v.credentials.filter((c) => !Object.values(v.httpBasic ?? {}).includes(c.name)).map((c) => c.header.toLowerCase());
|
|
28195
|
+
if (new Set(ordinaryHeaders).size !== ordinaryHeaders.length)
|
|
28193
28196
|
ctx.addIssue({ code: "custom", path: ["credentials"], message: "Credential headers must be unique" });
|
|
28194
28197
|
for (const [i, header] of headers.entries())
|
|
28195
28198
|
if (["cookie", "host", "content-length", "origin", "referer"].includes(header))
|
|
@@ -28199,6 +28202,25 @@ var init_schema = __esm({
|
|
|
28199
28202
|
ctx.addIssue({ code: "custom", path: ["allowedOrigins", i], message: "Credentials require HTTPS origins" });
|
|
28200
28203
|
});
|
|
28201
28204
|
}
|
|
28205
|
+
if (v.httpBasic) {
|
|
28206
|
+
if (v.mode !== "direct")
|
|
28207
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic"], message: "HTTP Basic encoding requires direct mode" });
|
|
28208
|
+
const names2 = Object.values(v.httpBasic).filter((value) => typeof value === "string");
|
|
28209
|
+
if (new Set(names2).size !== names2.length)
|
|
28210
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic"], message: "Basic username and password must use distinct saved fields" });
|
|
28211
|
+
for (const [slot, name] of Object.entries(v.httpBasic)) {
|
|
28212
|
+
if (typeof name !== "string") {
|
|
28213
|
+
if (slot === "username" && name.literal.includes(":"))
|
|
28214
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic", slot], message: "Basic username cannot contain a colon" });
|
|
28215
|
+
continue;
|
|
28216
|
+
}
|
|
28217
|
+
const field = v.credentials?.find((c) => c.name === name);
|
|
28218
|
+
if (!field || field.header.toLowerCase() !== "authorization" || field.prefix)
|
|
28219
|
+
ctx.addIssue({ code: "custom", path: ["httpBasic", slot], message: "Reference a raw saved field with header Authorization and no prefix" });
|
|
28220
|
+
}
|
|
28221
|
+
if (v.credentials?.some((c) => c.header.toLowerCase() === "authorization" && !names2.includes(c.name)))
|
|
28222
|
+
ctx.addIssue({ code: "custom", path: ["credentials"], message: "Basic authentication owns Authorization; remove conflicting credentials" });
|
|
28223
|
+
}
|
|
28202
28224
|
for (const origin of v.allowedOrigins)
|
|
28203
28225
|
if (!Object.hasOwn(v.probes, origin))
|
|
28204
28226
|
ctx.addIssue({ code: "custom", path: ["probes"], message: `Missing probe for ${origin}` });
|
|
@@ -28439,7 +28461,7 @@ function executableProjection(kind, definition2) {
|
|
|
28439
28461
|
result4.inputs = inputDefault;
|
|
28440
28462
|
return result4;
|
|
28441
28463
|
}
|
|
28442
|
-
const keys = kind === "transport" ? ["mode", "baseUrl", "allowedOrigins", "browserContext", "credentials", "inject", "identity", "probes"] : ["pageOrigin", "startUrl"];
|
|
28464
|
+
const keys = kind === "transport" ? ["mode", "baseUrl", "allowedOrigins", "browserContext", "credentials", "httpBasic", "inject", "identity", "probes"] : ["pageOrigin", "startUrl"];
|
|
28443
28465
|
const result3 = {};
|
|
28444
28466
|
for (const key of keys) {
|
|
28445
28467
|
const value = definition2[key];
|
|
@@ -28588,6 +28610,46 @@ var init_rules = __esm({
|
|
|
28588
28610
|
}
|
|
28589
28611
|
});
|
|
28590
28612
|
|
|
28613
|
+
// ../../packages/core/dist/definitions/credential-contract.js
|
|
28614
|
+
function credentialAuthoringIssues(transport) {
|
|
28615
|
+
return (transport.credentials ?? []).flatMap((field, index2) => field.header.toLowerCase() === "authorization" && /^Basic(?:\s|$)/iu.test(field.prefix ?? "") ? [{ path: `/credentials/${index2}/prefix`, message: "Use raw saved credential fields with httpBasic and no prefix. Apilift encodes at dispatch; never ask the person to encode a key." }] : []);
|
|
28616
|
+
}
|
|
28617
|
+
function credentialContractKey(transport) {
|
|
28618
|
+
return canonicalJson({ mode: transport.mode, origins: [...transport.allowedOrigins].sort(), baseOrigin: transport.baseUrl ? new URL(transport.baseUrl).origin : null, browserContext: transport.browserContext ?? null, credentials: [...transport.credentials ?? []].sort((a, b) => a.name.localeCompare(b.name)), ...transport.httpBasic ? { httpBasic: transport.httpBasic } : {}, inject: transport.inject ?? [] });
|
|
28619
|
+
}
|
|
28620
|
+
function credentialHeaders(requirements, values, basic) {
|
|
28621
|
+
const headers = /* @__PURE__ */ Object.create(null);
|
|
28622
|
+
const read = (name) => {
|
|
28623
|
+
const value = values[name];
|
|
28624
|
+
if (typeof value !== "string" || !value || [...value].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127))
|
|
28625
|
+
throw new Error("Saved credential is missing or contains invalid characters; configure it again");
|
|
28626
|
+
return value;
|
|
28627
|
+
};
|
|
28628
|
+
for (const field of requirements) {
|
|
28629
|
+
if (basic && Object.values(basic).includes(field.name))
|
|
28630
|
+
continue;
|
|
28631
|
+
headers[field.header] = `${field.prefix ?? ""}${read(field.name)}`;
|
|
28632
|
+
}
|
|
28633
|
+
if (basic) {
|
|
28634
|
+
const component = (value) => typeof value === "string" ? read(value) : value?.literal ?? "";
|
|
28635
|
+
const username = component(basic.username), password = component(basic.password);
|
|
28636
|
+
if (username.includes(":"))
|
|
28637
|
+
throw new Error("HTTP Basic username cannot contain a colon; configure it again");
|
|
28638
|
+
const bytes = new TextEncoder().encode(`${username}:${password}`);
|
|
28639
|
+
let binary = "";
|
|
28640
|
+
for (const byte of bytes)
|
|
28641
|
+
binary += String.fromCharCode(byte);
|
|
28642
|
+
headers.Authorization = `Basic ${btoa(binary)}`;
|
|
28643
|
+
}
|
|
28644
|
+
return headers;
|
|
28645
|
+
}
|
|
28646
|
+
var init_credential_contract = __esm({
|
|
28647
|
+
"../../packages/core/dist/definitions/credential-contract.js"() {
|
|
28648
|
+
"use strict";
|
|
28649
|
+
init_json();
|
|
28650
|
+
}
|
|
28651
|
+
});
|
|
28652
|
+
|
|
28591
28653
|
// ../../packages/core/dist/definitions/metadata.js
|
|
28592
28654
|
function describedProperties(schema) {
|
|
28593
28655
|
if (!schema || typeof schema !== "object" || Array.isArray(schema))
|
|
@@ -28669,6 +28731,7 @@ function metadataIssues(kind, definition2) {
|
|
|
28669
28731
|
schemaMetadataIssues(definition2.response.schema, "/response/schema", issues, true);
|
|
28670
28732
|
}
|
|
28671
28733
|
if (kind === "transport" && "probes" in definition2) {
|
|
28734
|
+
issues.push(...credentialAuthoringIssues(definition2));
|
|
28672
28735
|
for (const [origin, probe] of Object.entries(definition2.probes)) {
|
|
28673
28736
|
if (probe.inputs !== void 0)
|
|
28674
28737
|
schemaMetadataIssues(probe.inputs, `/probes/${pointer(origin)}/inputs`, issues, true);
|
|
@@ -28688,6 +28751,7 @@ var metadataLimits, placeholders, pointer;
|
|
|
28688
28751
|
var init_metadata = __esm({
|
|
28689
28752
|
"../../packages/core/dist/definitions/metadata.js"() {
|
|
28690
28753
|
"use strict";
|
|
28754
|
+
init_credential_contract();
|
|
28691
28755
|
metadataLimits = { title: 80, app: 120, entity: 240, action: 600, transport: 1e3, "browser-context": 240, field: 600, editorial: 2e4 };
|
|
28692
28756
|
placeholders = /^(?:todo|tbd|replace me|pending .*documentation|resource identifier|native type discriminator|native result|one filter(?:\/reference)? value)[.!]?$/iu;
|
|
28693
28757
|
pointer = (value) => value.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
@@ -28765,17 +28829,6 @@ var init_categories = __esm({
|
|
|
28765
28829
|
}
|
|
28766
28830
|
});
|
|
28767
28831
|
|
|
28768
|
-
// ../../packages/core/dist/definitions/credential-contract.js
|
|
28769
|
-
function credentialContractKey(transport) {
|
|
28770
|
-
return canonicalJson({ mode: transport.mode, origins: [...transport.allowedOrigins].sort(), baseOrigin: transport.baseUrl ? new URL(transport.baseUrl).origin : null, browserContext: transport.browserContext ?? null, credentials: [...transport.credentials ?? []].sort((a, b) => a.name.localeCompare(b.name)), inject: transport.inject ?? [] });
|
|
28771
|
-
}
|
|
28772
|
-
var init_credential_contract = __esm({
|
|
28773
|
-
"../../packages/core/dist/definitions/credential-contract.js"() {
|
|
28774
|
-
"use strict";
|
|
28775
|
-
init_json();
|
|
28776
|
-
}
|
|
28777
|
-
});
|
|
28778
|
-
|
|
28779
28832
|
// ../../packages/core/dist/definitions/index.js
|
|
28780
28833
|
var init_definitions = __esm({
|
|
28781
28834
|
"../../packages/core/dist/definitions/index.js"() {
|
|
@@ -30768,6 +30821,7 @@ function draftTemplate(kind) {
|
|
|
30768
30821
|
"// verification.probeOrigin selects one origin key; verification.inputs supplies local sample values when the probe needs them.",
|
|
30769
30822
|
"// Keep verification.review pending until discover run succeeds; inspect its output, then choose accepted or rejected and explain verification.notes.",
|
|
30770
30823
|
"// Browser page-derived auth belongs in definition.inject, not definition.credentials. credentials declares separately saved human-provided secrets; leave it [] when browser login supplies access.",
|
|
30824
|
+
'// Direct HTTP Basic accepts original secrets: credentials: [{name:"api-key",header:"Authorization"}], httpBasic: {password:"api-key"}. The username is empty. For a key as username use httpBasic: {username:"api-key"}; for two saved fields name both. A documented fixed public label may use username: {literal:"api"}; actual secrets always name saved fields. Omitted components are empty. Do not set a prefix or ask the person to encode anything; Apilift constructs Basic authentication at dispatch.',
|
|
30771
30825
|
`// Invented notes app example inside definition (replace only with observed addresses and nonsecret prefix): ${JSON.stringify(browserLocatorExample)}`,
|
|
30772
30826
|
"// A locator hypothesis is not proof. Never copy a token value; verify the browser connection with discover run before accepting it.",
|
|
30773
30827
|
""
|
|
@@ -31205,7 +31259,7 @@ function createCliApi(baseUrl, fetcher = fetch, token, sessionId) {
|
|
|
31205
31259
|
const transport = async (input2, init) => {
|
|
31206
31260
|
const method = init?.method ?? "GET";
|
|
31207
31261
|
const headers = new Headers(init?.headers);
|
|
31208
|
-
headers.set("Apilift-Capabilities", "distributed-rollouts-v1,discovery-proof-v2,dynamic-bindings-v1,collection-proof-v3,web-responses-v1,bounded-catalog-v1");
|
|
31262
|
+
headers.set("Apilift-Capabilities", "distributed-rollouts-v1,discovery-proof-v2,dynamic-bindings-v1,collection-proof-v3,web-responses-v1,bounded-catalog-v1,http-basic-v1");
|
|
31209
31263
|
headers.set("Apilift-Client", `cli/${cliVersion}`);
|
|
31210
31264
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
31211
31265
|
if (sessionId) headers.set("Apilift-Session", sessionId);
|
|
@@ -38547,6 +38601,7 @@ function readCredentialDraft(filePath) {
|
|
|
38547
38601
|
throw new Error(`Repair the selected transport draft at ${filePath}: ${paths2.join(", ") || "/definition"}. Run discover check for field guidance`);
|
|
38548
38602
|
}
|
|
38549
38603
|
const transport = checked.definition;
|
|
38604
|
+
if (credentialAuthoringIssues(transport).length) throw new Error(`Replace the encoded Basic credential contract in ${filePath}: declare raw saved fields and definition.httpBasic with no prefix. Apilift encodes at dispatch; do not ask the person to encode a key`);
|
|
38550
38605
|
if (!transport.description.trim()) throw new Error(`Research credential acquisition and edit /definition/description in ${filePath}. Run discover check before secure setup`);
|
|
38551
38606
|
return { transport, fingerprint: createHash5("sha256").update(canonicalJson(transport)).digest("hex") };
|
|
38552
38607
|
}
|
|
@@ -38554,6 +38609,7 @@ var init_credential_draft = __esm({
|
|
|
38554
38609
|
"src/discovery/credential-draft.ts"() {
|
|
38555
38610
|
"use strict";
|
|
38556
38611
|
init_json();
|
|
38612
|
+
init_definitions();
|
|
38557
38613
|
init_envelope();
|
|
38558
38614
|
}
|
|
38559
38615
|
});
|
|
@@ -40510,7 +40566,12 @@ Inspect current action documentation: ${prefix2(`actions show ${appName} ${actio
|
|
|
40510
40566
|
const values = await store.read(selectedCredential, { workspaceId: installation.workspaceId, appId: bundle2.app.id, appTitle: bundle2.app.slug, transportName: action.transport, legacyCredentialContract: "legacyCredentialContract" in requirements ? requirements.legacyCredentialContract : void 0, requirements: transport.credentials ?? [], credentialContract: credentialContractKey(transport), destinations: transport.allowedOrigins });
|
|
40511
40567
|
responseSecrets.push(...Object.values(values));
|
|
40512
40568
|
const headers = new Headers(request2.init.headers);
|
|
40513
|
-
|
|
40569
|
+
const injected = credentialHeaders(transport.credentials ?? [], values, transport.httpBasic);
|
|
40570
|
+
responseSecrets.push(...retainedBasicSecrets(transport, injected));
|
|
40571
|
+
for (const [name, value] of Object.entries(injected)) {
|
|
40572
|
+
headers.set(name, value);
|
|
40573
|
+
responseSecrets.push(value);
|
|
40574
|
+
}
|
|
40514
40575
|
request2 = { ...request2, init: { ...request2.init, headers } };
|
|
40515
40576
|
} finally {
|
|
40516
40577
|
store.close();
|
|
@@ -40541,7 +40602,7 @@ ${action.effect !== "read" && primary.response.status >= 500 ? readRecovery : ""
|
|
|
40541
40602
|
} else {
|
|
40542
40603
|
const value = projected(decodeResponse(primary, action.response), action, generated);
|
|
40543
40604
|
outcome = "success";
|
|
40544
|
-
output2 = result(`${JSON.stringify(value, null, 2)}
|
|
40605
|
+
output2 = result(`${JSON.stringify(privateEvidence(value, responseSecrets).value, null, 2)}
|
|
40545
40606
|
`, true);
|
|
40546
40607
|
}
|
|
40547
40608
|
} catch (error62) {
|
|
@@ -40575,6 +40636,7 @@ Inspect saved entries: ${prefix2(`credentials list ${appName} ${action.transport
|
|
|
40575
40636
|
`, 5, !!dispatchedAt);
|
|
40576
40637
|
}
|
|
40577
40638
|
}
|
|
40639
|
+
output2 = { ...output2, stdout: privateEvidence(output2.stdout, responseSecrets).value, stderr: privateEvidence(output2.stderr, responseSecrets).value };
|
|
40578
40640
|
if (browserExecution.scope) await liveBrowser?.closeScope(browserExecution.scope.id).catch(() => {
|
|
40579
40641
|
});
|
|
40580
40642
|
const report = { id: reportId, connectionFingerprint: await hashJson({ transport: transportVersion.executableHash, credential: selectedCredential ?? null, browser: browserPage?.pageOrigin ?? null }), ...bundle2.receipt ? { receiptId: bundle2.receipt.id } : {}, appId: bundle2.app.id, actionName, actionVersion: actionVersion.version, closureHash: bundle2.closure.hash, purpose: "run", clientFamily: "cli", agent: session.agent, outcome, ...diagnostic ? { diagnostic } : {}, startedAt, ...dispatchedAt ? { dispatchedAt } : {}, finishedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -40647,8 +40709,35 @@ function checkSavedProof(action, body, primary, inputs) {
|
|
|
40647
40709
|
if (!action.proof) return void 0;
|
|
40648
40710
|
return confirmProof(action.proof, body, primary, inputs, body === null && action.proof.absent && !action.proof.select ? 404 : 200);
|
|
40649
40711
|
}
|
|
40712
|
+
function retainedBasicSecrets(transport, headers) {
|
|
40713
|
+
if (transport.httpBasic) return [];
|
|
40714
|
+
const header = Object.entries(headers).find(([name]) => name.toLowerCase() === "authorization")?.[1];
|
|
40715
|
+
const payload = /^Basic\s+([A-Za-z0-9+/]+={0,2})$/iu.exec(header ?? "")?.[1];
|
|
40716
|
+
if (!payload) return [];
|
|
40717
|
+
const bytes = Buffer.from(payload, "base64"), decoded = /* @__PURE__ */ new Set([bytes.toString("latin1")]);
|
|
40718
|
+
try {
|
|
40719
|
+
decoded.add(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
40720
|
+
} catch {
|
|
40721
|
+
}
|
|
40722
|
+
return [...decoded].flatMap((value) => {
|
|
40723
|
+
const at = value.indexOf(":");
|
|
40724
|
+
return at < 0 ? [] : [value, value.slice(0, at), value.slice(at + 1)].filter(Boolean);
|
|
40725
|
+
});
|
|
40726
|
+
}
|
|
40650
40727
|
function privateEvidence(value, credentials) {
|
|
40651
40728
|
const secrets = new Set(credentials.filter(Boolean));
|
|
40729
|
+
for (const value2 of [...secrets]) {
|
|
40730
|
+
const raw = value2.replace(/^[A-Za-z]{2,12} /u, "");
|
|
40731
|
+
for (const secret of [value2, raw]) {
|
|
40732
|
+
secrets.add(secret);
|
|
40733
|
+
secrets.add(encodeURIComponent(secret));
|
|
40734
|
+
secrets.add(encodeURIComponent(secret).replace(/%[0-9A-F]{2}/gu, (escape6) => escape6.toLowerCase()));
|
|
40735
|
+
secrets.add(new URLSearchParams([["", secret]]).toString().slice(1));
|
|
40736
|
+
secrets.add(JSON.stringify(secret).slice(1, -1));
|
|
40737
|
+
secrets.add(Buffer.from(secret, "utf8").toString("base64"));
|
|
40738
|
+
secrets.add(Buffer.from(secret, "utf8").toString("base64url"));
|
|
40739
|
+
}
|
|
40740
|
+
}
|
|
40652
40741
|
JSON.stringify(value, (_key, child) => {
|
|
40653
40742
|
if (typeof child === "string" && /^\s*</u.test(child)) for (const secret of htmlSecrets(child)) secrets.add(secret);
|
|
40654
40743
|
return child;
|
|
@@ -40659,18 +40748,25 @@ function privateEvidence(value, credentials) {
|
|
|
40659
40748
|
return child;
|
|
40660
40749
|
});
|
|
40661
40750
|
let redacted = JSON.stringify(value).includes("[REDACTED]");
|
|
40662
|
-
const
|
|
40663
|
-
|
|
40664
|
-
|
|
40665
|
-
|
|
40666
|
-
|
|
40667
|
-
safe = safe.split(secret).join("[REDACTED]");
|
|
40668
|
-
}
|
|
40669
|
-
return safe;
|
|
40751
|
+
const ordered = [...secrets].filter(Boolean).sort((a, b) => b.length - a.length);
|
|
40752
|
+
const hide2 = (text4) => {
|
|
40753
|
+
for (const secret of ordered) if (text4.includes(secret)) {
|
|
40754
|
+
redacted = true;
|
|
40755
|
+
text4 = text4.split(secret).join("[REDACTED]");
|
|
40670
40756
|
}
|
|
40757
|
+
return text4;
|
|
40758
|
+
};
|
|
40759
|
+
const visit4 = (child) => {
|
|
40760
|
+
if (typeof child === "string") return hide2(child);
|
|
40761
|
+
if (typeof child === "number" && secrets.has(String(child))) {
|
|
40762
|
+
redacted = true;
|
|
40763
|
+
return "[REDACTED]";
|
|
40764
|
+
}
|
|
40765
|
+
if (Array.isArray(child)) return child.map(visit4);
|
|
40766
|
+
if (child && typeof child === "object") return Object.fromEntries(Object.entries(child).map(([key, entry2]) => [hide2(key), visit4(entry2)]));
|
|
40671
40767
|
return child;
|
|
40672
|
-
}
|
|
40673
|
-
return { value:
|
|
40768
|
+
};
|
|
40769
|
+
return { value: visit4(value), redacted };
|
|
40674
40770
|
}
|
|
40675
40771
|
async function executeDraftVerification(input2) {
|
|
40676
40772
|
const { transport } = input2;
|
|
@@ -40762,7 +40858,12 @@ async function executeDraftVerification(input2) {
|
|
|
40762
40858
|
const values2 = await store.read(credential2, { workspaceId: installation.workspaceId, ...credentialDraftId ? { draftId: credentialDraftId } : {}, appId: input2.appId, appTitle: input2.appSlug, transportName: input2.kind === "action" && action && "transport" in action ? action.transport : input2.name, requirements: transport.credentials ?? [], credentialContract: credentialContractKey(transport), destinations: transport.allowedOrigins });
|
|
40763
40859
|
knownCredentials.push(...Object.values(values2));
|
|
40764
40860
|
const headers = new Headers(request2.init.headers);
|
|
40765
|
-
|
|
40861
|
+
const injected = credentialHeaders(transport.credentials ?? [], values2, transport.httpBasic);
|
|
40862
|
+
knownCredentials.push(...retainedBasicSecrets(transport, injected));
|
|
40863
|
+
for (const [name, value] of Object.entries(injected)) {
|
|
40864
|
+
headers.set(name, value);
|
|
40865
|
+
knownCredentials.push(value);
|
|
40866
|
+
}
|
|
40766
40867
|
request2 = { ...request2, init: { ...request2.init, headers } };
|
|
40767
40868
|
} finally {
|
|
40768
40869
|
store.close();
|
|
@@ -40832,7 +40933,7 @@ async function executeDraftVerification(input2) {
|
|
|
40832
40933
|
if (input2.kind === "transport") {
|
|
40833
40934
|
const selected = projected(decoded, { response: probe.response }, generated);
|
|
40834
40935
|
if (probe.response.schema && !ajv3.compile(probe.response.schema)(selected)) return { ok: false, message: "Probe response does not match its declared schema", exitCode: 12, dispatched };
|
|
40835
|
-
return { ok: true, output: selected, proof: "not-applicable", dispatched: true };
|
|
40936
|
+
return { ok: true, output: privateEvidence(selected, knownCredentials).value, proof: "not-applicable", dispatched: true };
|
|
40836
40937
|
}
|
|
40837
40938
|
const executable = action;
|
|
40838
40939
|
const value = projected(decoded, executable, generated);
|
|
@@ -40847,11 +40948,11 @@ async function executeDraftVerification(input2) {
|
|
|
40847
40948
|
} catch (error62) {
|
|
40848
40949
|
throw new ResponseContractFailure(error62 instanceof Error ? error62.message : "Readback did not confirm success");
|
|
40849
40950
|
}
|
|
40850
|
-
return { ok: true, output: value, proof: "passed", dispatched: true, ...context ? { context } : {} };
|
|
40951
|
+
return { ok: true, output: privateEvidence(value, knownCredentials).value, proof: "passed", dispatched: true, ...context ? { context } : {} };
|
|
40851
40952
|
}
|
|
40852
|
-
return { ok: true, output: value, proof: "not-applicable", dispatched: true, ...context ? { context } : {} };
|
|
40953
|
+
return { ok: true, output: privateEvidence(value, knownCredentials).value, proof: "not-applicable", dispatched: true, ...context ? { context } : {} };
|
|
40853
40954
|
} catch (error62) {
|
|
40854
|
-
return { ok: false, message: `${(error62 instanceof Error ? error62.message : "The test did not return").replace(/[.\s]+$/u, "")}. ${dispatched ? input2.kind === "transport" || action && "effect" in action && action.effect === "read" ? "The read did not return complete evidence" : "The request may have reached the app; inspect its state before another mutation" : "No app request was sent"}`, exitCode: error62 instanceof ResponseContractFailure ? 12 : dispatched || error62 instanceof BrowserControlError || ["extension-unavailable", "capture-server-unavailable", "native-host-upgrade-required"].includes(error62?.code ?? "") ? 5 : 2, dispatched };
|
|
40955
|
+
return { ok: false, message: privateEvidence(`${(error62 instanceof Error ? error62.message : "The test did not return").replace(/[.\s]+$/u, "")}. ${dispatched ? input2.kind === "transport" || action && "effect" in action && action.effect === "read" ? "The read did not return complete evidence" : "The request may have reached the app; inspect its state before another mutation" : "No app request was sent"}`, knownCredentials).value, exitCode: error62 instanceof ResponseContractFailure ? 12 : dispatched || error62 instanceof BrowserControlError || ["extension-unavailable", "capture-server-unavailable", "native-host-upgrade-required"].includes(error62?.code ?? "") ? 5 : 2, dispatched };
|
|
40855
40956
|
} finally {
|
|
40856
40957
|
if (browserExecution.scope) await liveBrowser?.closeScope(browserExecution.scope.id).catch(() => {
|
|
40857
40958
|
});
|
|
@@ -41501,17 +41602,20 @@ List current entries: ${command3(invocation, `credentials list ${selectedApp.slu
|
|
|
41501
41602
|
if (!current.requirements.length) return ok3(`This connection requires no stored credential.
|
|
41502
41603
|
${current.instructions}
|
|
41503
41604
|
`);
|
|
41605
|
+
if (credentialAuthoringIssues({ credentials: current.requirements }).length) return fail4(`This connection expects an encoded Basic value. Update it to raw saved fields with httpBasic before opening secure setup; do not ask the person to encode a key.
|
|
41606
|
+
Open ${command3(invocation, `apps update ${selectedApp.slug}`)}, then ${command3(invocation, `discover draft transport ${transportName}`)}.
|
|
41607
|
+
`, 2);
|
|
41504
41608
|
const expiresAt = new Date(Date.now() + 30 * 6e4).toISOString();
|
|
41505
41609
|
const draftId = ownedDraft && draftContract ? randomUUID8() : void 0;
|
|
41506
41610
|
if (draftId && ownedDraft && draftContract) store.registerDraft({ ...current, draftId, expiresAt, source: { draftId: ownedDraft.id, sessionId: session.id, fingerprint: draftContract.fingerprint } });
|
|
41507
41611
|
const link = { ...draftId ? { draftId } : {}, appId: selectedApp.id, appTitle: selectedApp.title, transportName, instructions: current.instructions, requirements: current.requirements, sessionId: session.id, expiresAt };
|
|
41508
41612
|
const url3 = `${siteUrl2}/dashboard/credentials/setup#${Buffer.from(canonicalJson(link)).toString("base64url")}`;
|
|
41509
41613
|
return ok3(`${selectedApp.title}'s ${transportName} connection needs a credential. The user must obtain and save it; do not ask them to paste it into chat or arguments.
|
|
41510
|
-
|
|
41614
|
+
${ownedDraft ? "Keep this transport draft selected and unchanged until the user confirms Saved. Editing it, discover defer, discard or close invalidates this link. Continue read-only research while waiting; switch drafts only after setup and verification.\n" : ""}
|
|
41511
41615
|
Give the user these instructions:
|
|
41512
41616
|
${current.instructions}
|
|
41513
41617
|
Open ${url3}
|
|
41514
|
-
Choose Configure credential in the dashboard, then choose a local label and enter the
|
|
41618
|
+
Choose Configure credential in the dashboard, then choose a local label and enter the original credential in the extension's secure form. The form must show Saved. This entry is selected for this requesting session.
|
|
41515
41619
|
|
|
41516
41620
|
After the user confirms saving, ${ownedDraft ? `continue verification: ${command3(invocation, "discover run")}.` : "continue the action you were preparing."} Saving does not prove the credential works.
|
|
41517
41621
|
`);
|
|
@@ -43584,15 +43688,20 @@ async function captureView(item, part, pointer6, scopeId, root2, invocation) {
|
|
|
43584
43688
|
` : "--part must be request or response.\n");
|
|
43585
43689
|
const body = await selectedBody(item, part, scopeId, root2, deadline);
|
|
43586
43690
|
const unavailable = part === "request" ? entry2.reqBodyState ?? entry2.requestMaterialUnavailable : entry2.resBodyState ?? entry2.bodyUnavailable;
|
|
43691
|
+
const redactionReason = unavailable === "redaction-unavailable" ? (() => {
|
|
43692
|
+
const reasons = String(entry2.requestMaterialUnavailable ?? "").split(",").filter((reason) => ["request-extra-info-unavailable", "response-extra-info-unavailable", "short-sensitive-fragment-unredactable"].includes(reason));
|
|
43693
|
+
return `Redaction could not safely retain this body${reasons.length ? `: ${reasons.join(", ")}` : ""}. ${reasons.includes("short-sensitive-fragment-unredactable") ? "Update Companion before another capture; setup connectivity checks do not test this redaction case. " : ""}Use other retained evidence or an authorized alternative route; do not repeat a mutation to recover its body.
|
|
43694
|
+
`;
|
|
43695
|
+
})() : "";
|
|
43587
43696
|
const artifact = entry2[part === "request" ? "reqBodyArtifact" : "resBodyArtifact"];
|
|
43588
43697
|
if ((artifact || part === "response" && entry2.pending) && body === null) return fail5(`Captured ${part} body for c${item.seq} is still collecting. Retry ${command4(invocation, `discover capture show c${item.seq}${part === "request" ? " --part request" : ""}`)}; no app request is sent.
|
|
43589
43698
|
`, 12);
|
|
43590
43699
|
if (typeof body !== "string" && part === "request") return ok4(`Captured request metadata for c${item.seq}; body is ${String(unavailable ?? "absent")}.
|
|
43591
|
-
${candidateLine(item)}
|
|
43700
|
+
${redactionReason}${candidateLine(item)}
|
|
43592
43701
|
${requestHeaderSummary(entry2)}${requestQuerySummary(entry2)}This reads retained evidence; it sends no app request.
|
|
43593
43702
|
`);
|
|
43594
43703
|
if (typeof body !== "string") return fail5(`Captured ${part} body for c${item.seq} is ${String(unavailable ?? "unavailable")}. Metadata: ${candidateLine(item)}
|
|
43595
|
-
`);
|
|
43704
|
+
${redactionReason}`);
|
|
43596
43705
|
let parsed;
|
|
43597
43706
|
try {
|
|
43598
43707
|
parsed = JSON.parse(body);
|
|
@@ -4,8 +4,10 @@ Search current first-party documentation for the selected operation and its auth
|
|
|
4
4
|
|
|
5
5
|
HTML responses, URL-encoded forms, fresh CSRF tokens from one read-only preparation request and declared read navigation are supported direct operations when authorization permits. These formats do not establish a missing API route. Read [operation-contract.md](operation-contract.md) for the extraction and preparation contracts before deciding that a documented operation needs browser fallback. Browser-managed authentication or missing request documentation can still require browser observation; JavaScript-only rendering, uploads and workflows containing multiple writes remain outside these contracts.
|
|
6
6
|
|
|
7
|
-
A direct connection is an independent `transport` draft. Give it a meaningful description, `mode: "direct"`, fixed `baseUrl`, explicit `allowedOrigins`, value-free `credentials` requirements and harmless read `probes` for its destinations. Research and write concrete HTTPS acquisition steps before testing an operation that needs a key. The app's `origin` is its primary UI location; it is not a substitute for a connection destination. A transport with several origins needs one successful, semantically reviewed `discover run` per origin by editing `verification.probeOrigin` between runs. For a read using POST or another non-GET method, declare its semantic read effect rather than deriving effect from method.
|
|
7
|
+
A direct connection is an independent `transport` draft. Give it a meaningful description, `mode: "direct"`, fixed `baseUrl`, explicit `allowedOrigins`, value-free `credentials` requirements and harmless read `probes` for its destinations. Research and write concrete HTTPS acquisition steps before testing an operation that needs a key. Verify the issuance URL and visible controls from current official instructions or observed UI; never invent a settings route. If issuance requires an account-specific URL, publish the verified sign-in URL and navigation steps rather than an account identifier. The app's `origin` is its primary UI location; it is not a substitute for a connection destination. A transport with several origins needs one successful, semantically reviewed `discover run` per origin by editing `verification.probeOrigin` between runs. For a read using POST or another non-GET method, declare its semantic read effect rather than deriving effect from method.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
For HTTP Basic, declare raw saved fields with `header: "Authorization"` and no prefix, then map their names in `definition.httpBasic`: `{password:"api-key"}` for an empty username, `{username:"api-key"}` for an empty password, or both named fields. Use `{literal:"api"}` for a documented fixed public component; never put an actual credential in a literal. Apilift encodes the values only at dispatch. The person enters the original key through the standard secure form; never ask them to Base64-encode, transform or expose it. A transport limitation does not establish that the documented operation is unavailable; assess other documented authentication before changing protocols.
|
|
10
|
+
|
|
11
|
+
If verification reports a missing or changed credential, follow the returned `credentials add` setup link. Keep its transport draft selected and unchanged until the person confirms Saved: editing or releasing it invalidates that link. Continue only independent read-only research while waiting; verify the credential before switching drafts. The secure form selects the saved entry for this requesting session; use the exact CLI continuation if selecting an existing entry. Do not ask for a key in chat or store it in a draft, command, environment variable, or capture. Acquisition instructions are required even if a key is already saved; they let the next user provision access. If the person reports a wrong acquisition link, inspect the app's actual key controls and correct the transport description before generating a replacement setup link. Re-run the same selected transport verification after human setup and inspect its meaning before accepting review.
|
|
10
12
|
|
|
11
13
|
A scoped harmless probe can take explicit local `verification.inputs`. Publish a reachable read action that supplies an app-owned identifier for later consumers; never embed an observed account identifier in a published probe or make the origin dynamic. Do not add a published context-variable resource. Begin browser fallback only after the requested documented operation, authorization, and provisioning route have been assessed. A local environment failure is not proof that the app lacks an API capability.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apilift/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=24.16.0 <27"
|
|
39
39
|
},
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "d71590d4126cee561a43649452a5f6f59d7961ab",
|
|
41
41
|
"repository": {
|
|
42
42
|
"type": "git",
|
|
43
43
|
"url": "git+https://github.com/egor-sergeev/apilift.git",
|