@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.
@@ -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
- if (new Set(headers).size !== headers.length)
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
- if (new Set(headers).size !== headers.length)
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
- if (new Set(headers).size !== headers.length)
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
- for (const requirement2 of transport.credentials ?? []) headers.set(requirement2.header, `${requirement2.prefix ?? ""}${values[requirement2.name]}`);
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 text4 = JSON.stringify(value, (_key, child) => {
40663
- if (typeof child === "string") {
40664
- let safe = child;
40665
- for (const secret of [...secrets].sort((a, b) => b.length - a.length)) if (secret && safe.includes(secret)) {
40666
- redacted = true;
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: JSON.parse(text4), redacted };
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
- for (const requirement2 of transport.credentials ?? []) headers.set(requirement2.header, `${requirement2.prefix ?? ""}${values2[requirement2.name]}`);
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 value in the extension's secure form. The form must show Saved. This entry is selected for this requesting session.
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);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apilift",
3
- "version": "1.7.2",
3
+ "version": "1.8.0",
4
4
  "description": "Use logged-in apps and discover missing actions with Apilift.",
5
5
  "author": {
6
6
  "name": "Apilift"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apilift",
3
- "version": "1.7.2",
3
+ "version": "1.8.0",
4
4
  "description": "Use logged-in apps and discover missing actions with Apilift.",
5
5
  "author": {
6
6
  "name": "Apilift"
@@ -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
- If verification reports a missing or changed credential, follow the returned `credentials add` setup link. The person enters the value through the secure form, and the agent then selects the intended saved entry for this session with the exact CLI continuation. 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. Re-run the same selected transport verification after human setup and inspect its meaning before accepting review.
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.7.2",
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": "9e4732871b0c2f7fcd0e7ef763da291488572b43",
40
+ "gitHead": "d71590d4126cee561a43649452a5f6f59d7961ab",
41
41
  "repository": {
42
42
  "type": "git",
43
43
  "url": "git+https://github.com/egor-sergeev/apilift.git",