@itpay/cli 2.1.60 → 2.1.61

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/src/main.js CHANGED
@@ -3841,7 +3841,8 @@ __name(withAsyncFileLock, "withAsyncFileLock");
3841
3841
  import { createHash, randomBytes } from "node:crypto";
3842
3842
  import { createServer } from "node:http";
3843
3843
  function hubOAuthConfig(env = process.env) {
3844
- const issuer = (env.ITPAY_HUB_OAUTH_ISSUER ?? "http://localhost:24444").replace(/\/+$/, "");
3844
+ const devHub = (env.ITPAY_HUB_URL ?? "").replace(/\/+$/, "") === "https://market-v4-dev.itpay.ai";
3845
+ const issuer = (env.ITPAY_HUB_OAUTH_ISSUER ?? (devHub ? "https://auth-v4-dev.itpay.ai" : "http://localhost:24444")).replace(/\/+$/, "");
3845
3846
  return {
3846
3847
  authorizeURL: `${issuer}/oauth2/auth`,
3847
3848
  tokenURL: `${issuer}/oauth2/token`,
@@ -5568,6 +5569,26 @@ __name(renderPaymentHandoff, "renderPaymentHandoff");
5568
5569
 
5569
5570
  // src/commands/hub_guidance.ts
5570
5571
  function writeCommandEnvelope(value, options = {}) {
5572
+ const qualify = /* @__PURE__ */ __name((action) => {
5573
+ if (!action.command.startsWith("itpay ")) return action;
5574
+ const assignments = [];
5575
+ for (const name of ["ITPAY_HUB_URL", "ITPAY_HUB_OAUTH_ISSUER", "ITPAY_HUB_OAUTH_CLIENT", "ITPAY_HUB_OAUTH_SCOPE", "ITPAY_STATE_DIR"]) {
5576
+ const raw = process.env[name];
5577
+ if (!raw) continue;
5578
+ if (name === "ITPAY_HUB_URL" || name === "ITPAY_HUB_OAUTH_ISSUER") {
5579
+ try {
5580
+ const u = new URL(raw);
5581
+ if (!["http:", "https:"].includes(u.protocol) || u.username || u.password || u.search || u.hash) return null;
5582
+ } catch {
5583
+ return null;
5584
+ }
5585
+ }
5586
+ const quoted = "'" + raw.replaceAll("'", `'"'"'`) + "'";
5587
+ assignments.push(name + "=" + quoted);
5588
+ }
5589
+ return { ...action, command: [...assignments, qualifyItPayCommand(action.command, declaredAgentType())].join(" ") };
5590
+ }, "qualify");
5591
+ value = { ...value, next: value.next ? qualify(value.next) : null, recovery: value.recovery.map(qualify).filter((action) => action !== null) };
5571
5592
  const out = options.output ?? ((text2) => {
5572
5593
  process.stdout.write(text2);
5573
5594
  });
@@ -5650,7 +5671,9 @@ async function runHubInspect(operationID2, options) {
5650
5671
  input_schema: desc.input_schema ?? null
5651
5672
  },
5652
5673
  instruction: desc.status !== "published" ? "\u8BE5\u64CD\u4F5C\u5F53\u524D\u4E0D\u53EF\u8FD0\u884C\uFF1B\u4E0D\u8981\u53D1\u8D77 run\u3002" : "\u4F7F\u7528 current_publication_id \u7ED1\u5B9A\u8FD0\u884C\uFF1B\u5E26 effect_class=external_write/purchase \u7684\u64CD\u4F5C\u9700\u8981\u7528\u6237\u786E\u8BA4\u3002",
5653
- next: desc.status === "published" ? { command: `itpay api run ${desc.operation_id} --publication ${desc.current_publication_id} --input '<JSON>'${options.jsonOutput ? " --json" : ""}`, reason: "\u53D1\u5E03\u7ED1\u5B9A\u8FD0\u884C" } : null,
5674
+ // Actual user input is still required; help is executable without calling
5675
+ // the supplier, while a placeholder is not a runnable action.
5676
+ next: desc.status === "published" ? { command: "itpay api run --help", reason: "\u6309 input_schema \u548C\u7528\u6237\u9700\u6C42\u51C6\u5907\u771F\u5B9E\u8F93\u5165\uFF1B\u6B64\u5E2E\u52A9\u547D\u4EE4\u4E0D\u6267\u884C API" } : null,
5654
5677
  recovery: []
5655
5678
  }, {
5656
5679
  jsonOutput: Boolean(options.jsonOutput),
@@ -5746,42 +5769,68 @@ async function runHubRun(operationID2, options) {
5746
5769
  throw new Error(`idempotency_conflict: request ${task.request_id} already exists with different input/publication/credential selection \u2014 use a new --request-id or "itpay api resume ${task.request_id}"`);
5747
5770
  }
5748
5771
  if (!created && task.run_id) {
5749
- const rec2 = await waitForRun(client, task.run_id, options.waitSeconds ?? 0);
5750
- store.update(task.request_id, { status: rec2.execution_state === "succeeded" ? "succeeded" : rec2.execution_state === "failed" ? "failed" : "dispatched" });
5751
- renderRun(rec2, options.jsonOutput, "replayed");
5772
+ const rec = await waitForRun(client, task.run_id, options.waitSeconds ?? 0);
5773
+ store.update(task.request_id, { status: rec.execution_state === "succeeded" ? "succeeded" : rec.execution_state === "failed" ? "failed" : "dispatched" });
5774
+ renderRun(rec, options.jsonOutput, "replayed");
5752
5775
  return;
5753
5776
  }
5754
5777
  if (!created && task.intent_id) {
5755
5778
  await renderBlockedTask(task, null, options.jsonOutput);
5756
5779
  return;
5757
5780
  }
5758
- const body = {
5759
- expected_publication_id: task.publication_id,
5760
- input: task.input,
5761
- ...task.credential_id ? { credential_id: task.credential_id } : {},
5762
- ...task.source_credentials ? { source_credentials: task.source_credentials } : {}
5763
- };
5764
- if (options.maxCharge) {
5765
- body.max_charge = { currency: "CNY", scale: 6, amount_atoms: options.maxCharge };
5766
- }
5767
- store.update(task.request_id, { status: "dispatched" });
5768
- const admission = await client.run(operationID2, body, store.idempotencyKey(task.request_id));
5769
- if (!isRunReceipt(admission)) {
5770
- store.update(task.request_id, { status: "blocked", intent_id: admission.intent_id, blocking_reason: admission.blocking_reason });
5771
- await renderBlockedTask(task, admission, options.jsonOutput);
5772
- return;
5781
+ await dispatchOriginalTask(client, store, task, options);
5782
+ }
5783
+ __name(runHubRun, "runHubRun");
5784
+ var HubTaskRequestError = class extends Error {
5785
+ static {
5786
+ __name(this, "HubTaskRequestError");
5773
5787
  }
5774
- let rec = admission;
5775
- store.update(task.request_id, { run_id: rec.run_id });
5776
- if (options.waitSeconds) {
5777
- rec = await waitForRun(client, rec.run_id, options.waitSeconds);
5788
+ requestID;
5789
+ references;
5790
+ code;
5791
+ constructor(error, task) {
5792
+ super(error instanceof Error ? error.message : "original Hub request did not return a receipt");
5793
+ this.requestID = task.request_id;
5794
+ this.references = { request_id: task.request_id, ...task.run_id ? { run_id: task.run_id } : {}, ...task.intent_id ? { intent_id: task.intent_id } : {} };
5795
+ this.code = error instanceof HttpError ? error.code : ["authorization_required", "idempotency_conflict", "invalid_state"].find((code) => this.message.startsWith(code + ":")) ?? "hub_request_unknown";
5796
+ }
5797
+ };
5798
+ async function dispatchOriginalTask(client, store, task, options) {
5799
+ try {
5800
+ const selections = { ...task.credential_id ? { credential_id: task.credential_id } : {}, ...task.source_credentials ? { source_credentials: task.source_credentials } : {} };
5801
+ if (task.input_digest !== digestTask(task.operation_id, task.publication_id, task.input, task.max_charge_atoms, selections)) {
5802
+ throw new Error("idempotency_conflict: stored request facts do not match the original digest; preserve this task for review");
5803
+ }
5804
+ const body = {
5805
+ expected_publication_id: task.publication_id,
5806
+ input: task.input,
5807
+ ...task.credential_id ? { credential_id: task.credential_id } : {},
5808
+ ...task.source_credentials ? { source_credentials: task.source_credentials } : {}
5809
+ };
5810
+ if (task.max_charge_atoms) {
5811
+ body.max_charge = { currency: "CNY", scale: 6, amount_atoms: task.max_charge_atoms };
5812
+ }
5813
+ store.update(task.request_id, { status: "dispatched" });
5814
+ const admission = await client.run(task.operation_id, body, store.idempotencyKey(task.request_id));
5815
+ if (!isRunReceipt(admission)) {
5816
+ store.update(task.request_id, { status: "blocked", intent_id: admission.intent_id, blocking_reason: admission.blocking_reason });
5817
+ await renderBlockedTask(task, admission, options.jsonOutput);
5818
+ return;
5819
+ }
5820
+ let rec = admission;
5821
+ store.update(task.request_id, { run_id: rec.run_id });
5822
+ if (options.waitSeconds) {
5823
+ rec = await waitForRun(client, rec.run_id, options.waitSeconds);
5824
+ }
5825
+ store.update(task.request_id, {
5826
+ status: rec.execution_state === "succeeded" ? "succeeded" : rec.execution_state === "failed" ? "failed" : "dispatched"
5827
+ });
5828
+ renderRun(rec, options.jsonOutput);
5829
+ } catch (error) {
5830
+ throw new HubTaskRequestError(error, store.get(task.request_id) ?? task);
5778
5831
  }
5779
- store.update(task.request_id, {
5780
- status: rec.execution_state === "succeeded" ? "succeeded" : rec.execution_state === "failed" ? "failed" : "dispatched"
5781
- });
5782
- renderRun(rec, options.jsonOutput);
5783
5832
  }
5784
- __name(runHubRun, "runHubRun");
5833
+ __name(dispatchOriginalTask, "dispatchOriginalTask");
5785
5834
  function renderRun(rec, jsonOutput, note) {
5786
5835
  const charged = rec.actual_charge ? atomsToYuan(rec.actual_charge.amount_atoms) : "\xA50.000000";
5787
5836
  writeCommandEnvelope({
@@ -6013,25 +6062,33 @@ async function runHubResume(requestID, options) {
6013
6062
  if (!task) {
6014
6063
  throw new Error(`not_found: unknown task ${requestID} \u2014 check "itpay api tasks"`);
6015
6064
  }
6016
- if (task.run_id) {
6017
- const rec = await waitForRun(client, task.run_id, options.waitSeconds ?? 0);
6018
- store.update(requestID, { status: rec.execution_state === "succeeded" ? "succeeded" : rec.execution_state === "failed" ? "failed" : "dispatched" });
6019
- renderRun(rec, options.jsonOutput);
6020
- return;
6021
- }
6022
- if (task.intent_id) {
6023
- const res = await client.continueIntent(task.intent_id, `cli-continue-${requestID}`);
6024
- if (res.run) {
6025
- store.update(requestID, { run_id: res.run.run_id, status: res.run.execution_state === "succeeded" ? "succeeded" : "dispatched" });
6026
- renderRun(res.run, options.jsonOutput);
6027
- } else {
6028
- const state = res.blocking_reason ?? res.state ?? res.status ?? "blocked";
6029
- store.update(requestID, { blocking_reason: state });
6030
- await renderBlockedTask(task, { ...res, intent_id: task.intent_id, blocking_reason: state }, options.jsonOutput);
6065
+ try {
6066
+ if (task.run_id) {
6067
+ const rec = await waitForRun(client, task.run_id, options.waitSeconds ?? 0);
6068
+ store.update(requestID, { status: rec.execution_state === "succeeded" ? "succeeded" : rec.execution_state === "failed" ? "failed" : "dispatched" });
6069
+ renderRun(rec, options.jsonOutput);
6070
+ return;
6031
6071
  }
6032
- return;
6072
+ if (task.intent_id) {
6073
+ const res = await client.continueIntent(task.intent_id, `cli-continue-${requestID}`);
6074
+ if (res.run) {
6075
+ store.update(requestID, { run_id: res.run.run_id, status: res.run.execution_state === "succeeded" ? "succeeded" : "dispatched" });
6076
+ renderRun(res.run, options.jsonOutput);
6077
+ } else {
6078
+ const state = res.blocking_reason ?? res.state ?? res.status ?? "blocked";
6079
+ store.update(requestID, { blocking_reason: state });
6080
+ await renderBlockedTask(task, { ...res, intent_id: task.intent_id, blocking_reason: state }, options.jsonOutput);
6081
+ }
6082
+ return;
6083
+ }
6084
+ if (!["pending", "dispatched", "unknown"].includes(task.status)) {
6085
+ throw new HubTaskRequestError(new Error("invalid_state: original task has no authoritative run or intent; preserve it for review"), task);
6086
+ }
6087
+ await dispatchOriginalTask(client, store, task, options);
6088
+ } catch (error) {
6089
+ if (error instanceof HubTaskRequestError) throw error;
6090
+ throw new HubTaskRequestError(error, store.get(requestID) ?? task);
6033
6091
  }
6034
- throw new Error(`invalid_state: task ${requestID} has no run or intent \u2014 it never reached the server`);
6035
6092
  }
6036
6093
  __name(runHubResume, "runHubResume");
6037
6094
  async function runHubTasks(options) {
@@ -6134,7 +6191,12 @@ __name(runHubConnect, "runHubConnect");
6134
6191
 
6135
6192
  // src/commands/api_commands.ts
6136
6193
  function reportCLIError(error, contract) {
6137
- writeCommandEnvelope({ status: "error", error: { code: error instanceof HttpError ? error.code : contract?.code ?? "command_failed", message: error instanceof Error ? error.message : String(error) }, instruction: contract?.instruction ?? "Check the command and the current Hub connection; resume known tasks before retrying writes.", next: null, recovery: contract?.recovery ?? [] }, { jsonOutput: contract?.jsonOutput ?? process.argv.includes("--json") });
6194
+ const code = error instanceof HubTaskRequestError ? error.code : error instanceof HttpError ? error.code : error instanceof Error && error.message.startsWith("authorization_required:") ? "authorization_required" : contract?.code ?? "command_failed";
6195
+ const auth = code === "authorization_required" || code === "unauthenticated";
6196
+ const taskID = error instanceof HubTaskRequestError ? error.requestID : void 0;
6197
+ const references = error instanceof HubTaskRequestError ? error.references : void 0;
6198
+ const recover = taskID ? { command: qualifyItPayCommand(references?.run_id ? `itpay api run-status ${references.run_id}${contract?.jsonOutput ? " --json" : ""}` : `itpay api resume ${taskID}${contract?.jsonOutput ? " --json" : ""}`, declaredAgentType()), reason: "\u8BFB\u53D6\u6216\u6062\u590D\u540C\u4E00\u539F\u4EFB\u52A1\uFF0C\u4E0D\u521B\u5EFA\u66FF\u4EE3\u8BF7\u6C42" } : null;
6199
+ writeCommandEnvelope({ status: "error", error: { code, message: error instanceof Error ? error.message : String(error) }, ...references ? { result: references } : {}, instruction: auth ? "\u5148\u5B8C\u6210 ItPay API \u6B63\u5E38\u8FDE\u63A5\uFF1B\u4FDD\u7559\u539F\u8EAB\u4EFD\u548C\u4EFB\u52A1\uFF0C\u4E0D\u8981\u521B\u5EFA\u66FF\u4EE3\u8BF7\u6C42\u3002" : ["idempotency_conflict", "invalid_state"].includes(code) ? "\u539F\u4EFB\u52A1\u8BB0\u5F55\u4E0D\u4E00\u81F4\uFF1B\u4FDD\u7559\u8BB0\u5F55\u6838\u5BF9\uFF0C\u4E0D\u80FD\u4FEE\u6539\u51BB\u7ED3\u53C2\u6570\u7EE7\u7EED\u53D1\u9001\u3002" : taskID ? "\u539F\u8BF7\u6C42\u5C1A\u672A\u53D6\u5F97\u53EF\u7528\u56DE\u6267\uFF1B\u8FD9\u4E0D\u4EE3\u8868\u672A\u5230\u8FBE\u670D\u52A1\u5668\u3002\u4FDD\u7559\u539F\u8BF7\u6C42\u6062\u590D\uFF0C\u4E0D\u91CD\u65B0\u8C03\u7528\u6216\u5145\u503C\u3002" : contract?.instruction ?? "Check the command and current Hub connection; preserve original tasks.", next: auth ? { command: qualifyItPayCommand(`itpay api connect${contract?.jsonOutput ? " --json" : ""}`, declaredAgentType()), reason: "\u6B63\u5E38\u6388\u6743\u540E\u7EE7\u7EED\u539F\u4EFB\u52A1" } : ["idempotency_conflict", "invalid_state", "forbidden", "publication_changed", "credential_revalidation_required"].includes(code) ? null : recover, recovery: auth && recover ? [recover] : contract?.recovery ?? [] }, { jsonOutput: contract?.jsonOutput ?? process.argv.includes("--json") });
6138
6200
  process.exitCode = 1;
6139
6201
  }
6140
6202
  __name(reportCLIError, "reportCLIError");
@@ -12880,7 +12942,7 @@ function railInvocationProjection(response, requestedCapability, input, itemCoun
12880
12942
  __name(railInvocationProjection, "railInvocationProjection");
12881
12943
 
12882
12944
  // src/services/rail/recovery.ts
12883
- var RAIL_LOCATION_EVIDENCE_REASONS = /* @__PURE__ */ new Set(["endpoint_kind_conflict", "place_identity_uncertain", "endpoint_adcode_name_conflict", "endpoint_poi_name_conflict", "endpoint_station_name_code_conflict", "location_evidence_required", "coordinate_name_mismatch", "area_scope_too_broad", "address_required", "administrative_area_unresolved", "station_location_evidence_required", "coordinate_identity_unverified"]);
12945
+ var RAIL_LOCATION_EVIDENCE_REASONS = /* @__PURE__ */ new Set(["station_jurisdiction_evidence_required", "endpoint_kind_conflict", "place_identity_uncertain", "endpoint_adcode_name_conflict", "endpoint_poi_name_conflict", "endpoint_station_name_code_conflict", "location_evidence_required", "coordinate_name_mismatch", "area_scope_too_broad", "address_required", "administrative_area_unresolved", "station_location_evidence_required", "coordinate_identity_unverified"]);
12884
12946
  var RAIL_QUOTE_FAILURE_GUIDANCE = {
12885
12947
  quote_source_unavailable: "\u5B9E\u65F6\u8F66\u7968\u6570\u636E\u6682\u4E0D\u53EF\u7528\uFF1B\u4FDD\u7559\u539F\u9009\u62E9\uFF0C\u7A0D\u540E\u6309\u7528\u6237\u610F\u613F\u91CD\u65B0\u6838\u9A8C\uFF0C\u4E0D\u521B\u5EFA\u66FF\u4EE3\u8BA2\u5355\u3002",
12886
12948
  quote_train_changed: "\u6240\u9009\u8F66\u6B21\u7684\u65F6\u523B\u6216\u8FD0\u884C\u4FE1\u606F\u5DF2\u53D8\u5316\uFF1B\u5411\u7528\u6237\u8BF4\u660E\u5E76\u5F81\u8BE2\u662F\u5426\u91CD\u65B0\u67E5\u7968\u3002",
@@ -13000,6 +13062,9 @@ function railWorkflowEnvelope(model) {
13000
13062
  const affected = model.workflow?.failure?.endpoint ?? model.workflow?.dependency?.endpoint;
13001
13063
  const query = model.workflow?.query_input ?? {};
13002
13064
  const endpoints = query.endpoints && typeof query.endpoints === "object" ? query.endpoints : {};
13065
+ if (code === "station_jurisdiction_evidence_required") {
13066
+ return { status: "system_evidence_required", result: { service_execution_id: id, reason: code, failed_step: model.workflow?.failure?.step_id, failure: model.workflow?.failure, query, ...affected === "origin" || affected === "destination" ? { affected_endpoint: affected, original_place: endpoints[affected]?.text ?? query[affected] } : {} }, instruction: "\u5730\u56FE\u5730\u70B9\u672A\u80FD\u56DE\u5339\u539F\u94C1\u8DEF\u7AD9\u8EAB\u4EFD\uFF0C\u9700\u670D\u52A1\u7EF4\u62A4\u65B9\u6838\u5BF9\u5730\u56FE\u4E0E\u94C1\u8DEF\u76EE\u5F55\u4E8B\u5B9E\u3002\u4FDD\u7559\u5B8C\u6574\u539F\u9700\u6C42\u53CA\u5DF2\u89E3\u6790\u53E6\u4E00\u7AEF\uFF1B\u4E0D\u8981\u6C42\u7528\u6237\u6539\u5730\u5740\uFF0C\u4E0D\u6362kind\u3001\u7AD9\u3001\u65E5\u671F\u6216\u731C\u5750\u6807\u3002\u5F53\u524D\u65E0\u53EF\u6267\u884C\u6062\u590D\uFF0C\u505C\u6B62\uFF1B\u4E0D\u91CD\u65B0\u67E5\u7968\u6216\u521B\u5EFA\u8D2D\u4E70\u3002", next: null, recovery: [] };
13067
+ }
13003
13068
  const stationPair = [endpoints.origin, endpoints.destination].every((endpoint) => endpoint?.kind === "station" && typeof endpoint.text === "string" && endpoint.text.trim() && typeof endpoint.station_code === "string" && /^[A-Z]{3}$/.test(endpoint.station_code));
13004
13069
  if (code === "station_location_evidence_required") {
13005
13070
  const exactInput = stationPair && typeof query.travel_date === "string" ? { origin: endpoints.origin.text, destination: endpoints.destination.text, travel_date: query.travel_date } : void 0;
@@ -16639,7 +16704,7 @@ import { createHash as createHash12 } from "node:crypto";
16639
16704
 
16640
16705
  // src/state/skill_identity.generated.ts
16641
16706
  var CANONICAL_SKILL_SHA256 = "dcbce8347f9ed4a95b89bbd680bbfb2c5308f1081e1af1554f413b950eb902dc";
16642
- var CANONICAL_API_SKILL_SHA256 = "a00535a771acc0d3bd325316380d0a58d2291a013c792c5edccae65694647b45";
16707
+ var CANONICAL_API_SKILL_SHA256 = "70af9cc2363b1c60174e4159e296b64c183c833a20930e8612c265bb69c238e6";
16643
16708
 
16644
16709
  // src/commands/skill.ts
16645
16710
  import { existsSync as existsSync9, readFileSync as readFileSync16 } from "node:fs";
@@ -1,4 +1,4 @@
1
- const CLI_VERSION = "2.1.60";
1
+ const CLI_VERSION = "2.1.61";
2
2
  const API_CONTRACT_REVISION = "sha256:f20af3780a0e12a79a6ca9507ba65167a4018338495af66d87931e55619b618a";
3
3
  export {
4
4
  API_CONTRACT_REVISION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itpay/cli",
3
- "version": "2.1.60",
3
+ "version": "2.1.61",
4
4
  "description": "The ItPay CLI for services, orders, and human-authorized purchased content.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -148,5 +148,5 @@
148
148
  "services",
149
149
  "api"
150
150
  ],
151
- "gitHead": "9eb053fdeeb9edecba3310f915096e96fee3b96e"
151
+ "gitHead": "928231fd3c11229cd9116a36fcbf3ff1eef6402f"
152
152
  }
@@ -11,6 +11,8 @@ For a new task, prefer the official pinned ItPay CLI when execution is permitted
11
11
 
12
12
  Use this Skill for API tasks; Services/railway instructions are not prerequisites. Use the installed pinned launcher and `itpay api --help`; `hub` is its compatibility alias. Keep the actual host/Agent Type, ITPAY_HUB_URL and permitted ITPAY_STATE_DIR across every command/session. Never switch accounts, spoof a host or clear identity to recover quota.
13
13
 
14
+ For an explicitly authorized **v4 dev API** task, select `ITPAY_HUB_URL=https://market-v4-dev.itpay.ai` before the first API command and preserve it with the permitted state directory. Its OAuth issuer is `https://auth-v4-dev.itpay.ai`; the CLI selects that issuer for this exact dev Hub unless explicitly configured otherwise. CLI resource is the Hub root, not the hosted MCP `/mcp` URL. For example, after the user selects v4 dev: `ITPAY_HUB_URL=https://market-v4-dev.itpay.ai itpay api connect --json`. Never select or switch dev merely because a request failed, and never infer the API identity from Services configuration. Local defaults and other explicit Hub/issuer configurations remain separate.
15
+
14
16
  Connect with the existing `itpay api connect` browser OAuth flow when authentication is required. The human enters verification privately. Existing credentials and task files belong to their endpoint/resource; sharing an ItPay account does not merge OAuth grants, API budgets, Services devices or data ACLs. Never request tokens or codes in chat. Use `api doctor` only for an actual connection problem, not before every task.
15
17
 
16
18
  Discover the published operation, inspect its current publication, input schema, effects, price and limits, then choose it only if it expresses all user constraints. Read normal text/JSON directly; do not write a parser or infer operation IDs from names. Read the exact command's help as needed. Templates containing placeholders are not executable next actions.