@zackbart/connecta 0.35.0 → 0.36.1

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.
Files changed (102) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +8 -4
  3. package/bin/version.mjs +1 -1
  4. package/dist/auth/downstream-oauth.js +71 -79
  5. package/dist/auth/static-oauth.js +104 -26
  6. package/dist/connectors/api-connector.d.ts +17 -0
  7. package/dist/connectors/api-connector.js +3 -1
  8. package/dist/http/client.js +1 -1
  9. package/dist/http/hatch-paging.js +2 -2
  10. package/dist/http/paging.js +3 -89
  11. package/dist/http/provider.js +1 -2
  12. package/dist/http/services.js +1 -1
  13. package/dist/http/tools.js +1 -27
  14. package/dist/operator-ui/contract.d.ts +1 -0
  15. package/dist/operator-ui/generated.js +4 -4
  16. package/dist/program-writes.js +0 -1
  17. package/dist/providers/_shared/google/kit.js +29 -139
  18. package/dist/providers/_shared/rest/kit.js +4 -404
  19. package/dist/providers/_shared/sensitive-fields.js +0 -16
  20. package/dist/providers/breeze/api.js +2 -2
  21. package/dist/providers/ccb/api.js +1 -1
  22. package/dist/providers/cloudflare/api.js +8 -8
  23. package/dist/providers/cloudflare/lib/config.d.ts +2 -1
  24. package/dist/providers/docs/api.js +1 -1
  25. package/dist/providers/docs/tools.js +1 -2
  26. package/dist/providers/drive/api.js +2 -2
  27. package/dist/providers/drive/lib/hatch.js +1 -1
  28. package/dist/providers/drive/tools.js +1 -1
  29. package/dist/providers/forms/api.js +1 -1
  30. package/dist/providers/forms/lib/hatch.js +1 -1
  31. package/dist/providers/forms/tools.js +2 -3
  32. package/dist/providers/github/api.js +1 -1
  33. package/dist/providers/github/lib/discovery.js +2 -2
  34. package/dist/providers/gmail/api.js +1 -1
  35. package/dist/providers/gmail/tools.js +2 -2
  36. package/dist/providers/granola/api.js +155 -0
  37. package/dist/providers/granola/index.d.ts +10 -0
  38. package/dist/providers/granola/index.js +68 -0
  39. package/dist/providers/granola/openapi.generated.js +30 -0
  40. package/dist/providers/granola/safety.js +37 -0
  41. package/dist/providers/granola/skill.generated.js +5 -0
  42. package/dist/providers/granola/tools.js +2 -0
  43. package/dist/providers/overflow/api.js +26 -26
  44. package/dist/providers/planning-center/api.js +4 -4
  45. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  46. package/dist/providers/planning-center/lib/json-api.js +2 -2
  47. package/dist/providers/quickbooks/api.js +299 -0
  48. package/dist/providers/quickbooks/index.d.ts +13 -0
  49. package/dist/providers/quickbooks/index.js +93 -0
  50. package/dist/providers/quickbooks/lib/query.js +118 -0
  51. package/dist/providers/quickbooks/lib/writes.js +168 -0
  52. package/dist/providers/quickbooks/safety.js +222 -0
  53. package/dist/providers/quickbooks/skill.generated.js +5 -0
  54. package/dist/providers/quickbooks/tools.js +117 -0
  55. package/dist/providers/sheets/api.js +1 -1
  56. package/dist/providers/sheets/lib/hatch.js +2 -2
  57. package/dist/providers/sheets/lib/requests.js +1 -1
  58. package/dist/providers/sheets/tools.js +1 -2
  59. package/dist/providers/slides/api.js +1 -1
  60. package/dist/providers/slides/tools.js +2 -2
  61. package/dist/providers/stripe/api.js +7 -7
  62. package/dist/providers/tithely/api.js +2 -2
  63. package/dist/providers/vercel/api.js +12 -12
  64. package/dist/providers/vercel/index.js +1 -2
  65. package/dist/providers/vercel/lib/pin.js +1 -1
  66. package/dist/routes/mcp.js +1 -1
  67. package/dist/routes/oauth.js +3 -0
  68. package/dist/runtime/admission.js +0 -12
  69. package/dist/runtime/call.js +0 -4
  70. package/dist/skills.js +0 -1
  71. package/dist/storage/keys.js +2 -3
  72. package/dist/types.d.ts +2 -0
  73. package/dist/ui.d.ts +1 -1
  74. package/dist/ui.js +0 -1
  75. package/dist/version.d.ts +1 -1
  76. package/dist/version.js +1 -1
  77. package/documentation/README.md +1 -0
  78. package/documentation/architecture.md +249 -366
  79. package/documentation/auth.md +56 -19
  80. package/documentation/code-mode.md +6 -6
  81. package/documentation/integrating.md +1 -1
  82. package/documentation/meta-tools.md +6 -3
  83. package/documentation/operator-tests.md +11 -2
  84. package/documentation/operator-ui.md +17 -5
  85. package/documentation/providers.md +492 -0
  86. package/package.json +12 -9
  87. package/templates/node/package.json +1 -1
  88. package/dist/operator-ui/agents.d.ts +0 -75
  89. package/dist/operator-ui/agents.js +0 -114
  90. package/dist/operator-ui/config-summary.js +0 -566
  91. package/dist/operator-ui/connection-map.js +0 -378
  92. package/dist/operator-ui/endpoints.js +0 -212
  93. package/dist/operator-ui/fix-prompts.d.ts +0 -18
  94. package/dist/operator-ui/fix-prompts.js +0 -79
  95. package/dist/operator-ui/health.js +0 -52
  96. package/dist/operator-ui/member-page.js +0 -97
  97. package/dist/operator-ui/overview.js +0 -126
  98. package/dist/operator-ui/setup-commands.js +0 -47
  99. package/dist/operator-ui/view.d.ts +0 -378
  100. package/dist/operator-ui/view.js +0 -631
  101. package/dist/providers/_shared/rest/dispatch.js +0 -21
  102. package/dist/providers/_shared/rest/tools.js +0 -592
@@ -0,0 +1,117 @@
1
+ import { quickbooksWrites } from "./lib/writes.js";
2
+ import { Effect, Schema } from "effect";
3
+ import { ConnectorCallError } from "../../errors.js";
4
+ import { Refusal } from "../../http/errors.js";
5
+ import { get as getTool, list } from "../../http/tools.js";
6
+ import { companyInfo, ENTITIES, queryRecords, REPORTS, recordReads, reportReads } from "./api.js";
7
+ import { CompanyShape, namedRecord, NamedRecordShape, object, ReportShape } from "./safety.js";
8
+ import { buildSearch, Filters, matches, reportParameters, reportProblem, REPORT_QUERY } from "./lib/query.js";
9
+ const Entity = Schema.Literals([
10
+ "customers",
11
+ "vendors",
12
+ "accounts",
13
+ "items",
14
+ "invoices",
15
+ "bills",
16
+ "payments",
17
+ "bill_payments",
18
+ ]);
19
+ const Id = Schema.String.check(matches("^[1-9]\\d{0,30}$"));
20
+ const query = { minorversion: "75" };
21
+ const checked = (run) => Effect.try({
22
+ try: run,
23
+ catch: (error) => new Refusal({
24
+ error: error instanceof ConnectorCallError
25
+ ? error
26
+ : new ConnectorCallError("invalid_args", "Invalid QuickBooks arguments."),
27
+ }),
28
+ });
29
+ export function quickbooksTools({ connection, pageSize, options, }) {
30
+ return [
31
+ ...quickbooksWrites(connection, options.realmId),
32
+ getTool(connection, {
33
+ name: "get_company_info",
34
+ description: "Read the configured company's accounting identity without contact or tax identifiers.",
35
+ input: Schema.Struct({}),
36
+ output: CompanyShape,
37
+ fields: true,
38
+ run: () => connection
39
+ .send(companyInfo, { params: { id: options.realmId }, query })
40
+ .pipe(Effect.map(({ data }) => object(object(data)["CompanyInfo"]))),
41
+ }),
42
+ list(connection, {
43
+ name: "search_records",
44
+ description: "Search reviewed accounting records using bounded structured filters. Customer/vendor names are exact DisplayName matches; record ids and references join invoices, bills and accounting payments. No raw SQL. Inactive master records need an explicit Active filter.",
45
+ filters: {
46
+ entity: Entity.annotate({
47
+ description: "Accounting record family; payments are accounting records, not the Payments API.",
48
+ }),
49
+ filters: Schema.optionalKey(Filters.annotate({
50
+ description: "At most 8 AND filters. Master records: Id, Active, LastUpdatedTime and DisplayName (customers/vendors) or Name (accounts/items). Transactions: Id, LastUpdatedTime, TxnDate and CustomerRef or VendorRef. Invoices/bills/bill payments also DocNumber; invoices/bills also DueDate and Balance.",
51
+ })),
52
+ },
53
+ row: NamedRecordShape,
54
+ limit: {
55
+ default: pageSize,
56
+ max: 100,
57
+ description: "Rows per page, default 25, at most 100. A bounded extra row detects continuation.",
58
+ },
59
+ cursor: "bound",
60
+ cursorScope: { realmId: options.realmId, environment: options.environment },
61
+ page: (args, cursor) => Effect.gen(function* () {
62
+ const entity = ENTITIES[args.entity];
63
+ const start = cursor === undefined ? 1 : Number(cursor);
64
+ const sql = yield* checked(() => buildSearch(entity, args.filters ?? [], start, args.limit + 1));
65
+ const { data } = yield* connection.send(queryRecords, { query: { ...query, query: sql } }, { flags: { [entity]: true } });
66
+ const rows = object(object(data)["QueryResponse"])[entity];
67
+ const all = Array.isArray(rows) ? rows : [];
68
+ const more = all.length > args.limit;
69
+ return {
70
+ rows: all.slice(0, args.limit).map(namedRecord),
71
+ page: { hasMore: more, nextCursor: more ? String(start + args.limit) : null },
72
+ };
73
+ }),
74
+ }),
75
+ getTool(connection, {
76
+ name: "get_record",
77
+ description: "Read one reviewed accounting record by Id. SyncToken is its public version identifier; DocNumber is a separate human label. Contact details and payment credentials are withheld.",
78
+ input: Schema.Struct({
79
+ entity: Entity,
80
+ id: Id.annotate({ description: "Record Id from search_records, never DocNumber." }),
81
+ }),
82
+ output: NamedRecordShape,
83
+ fields: true,
84
+ run: (args) => connection
85
+ .sendPath({
86
+ method: "GET",
87
+ path: `/${ENTITIES[args.entity].toLowerCase()}/${args.id}`,
88
+ template: recordReads[args.entity].path,
89
+ query: [["minorversion", "75"]],
90
+ })
91
+ .pipe(Effect.map(({ data }) => namedRecord(object(data)[ENTITIES[args.entity]]))),
92
+ }),
93
+ getTool(connection, {
94
+ name: "get_report",
95
+ description: "Read Profit & Loss, Balance Sheet, Cash Flow or AR/AP aging summary. Preserve Header, Columns and nested Rows; empty amount strings mean no value, never zero. P&L/CashFlow support start_date; P&L/BalanceSheet support accounting_method. All accept end_date (aging as-of date). Reports retain vendor currency; no currency conversion.",
96
+ input: Schema.Struct({ report: Schema.Literals(REPORTS), ...REPORT_QUERY }),
97
+ output: ReportShape,
98
+ fields: true,
99
+ run: (args) => Effect.gen(function* () {
100
+ const { report, ...raw } = args;
101
+ const parameters = reportParameters(report, Object.fromEntries(Object.entries(raw).filter(([, value]) => value !== undefined)));
102
+ yield* checked(() => {
103
+ const problem = reportProblem(report, parameters);
104
+ if (problem)
105
+ throw new ConnectorCallError("invalid_args", problem);
106
+ });
107
+ const { data } = yield* connection.sendPath({
108
+ method: "GET",
109
+ path: `/reports/${report}`,
110
+ template: reportReads[report].path,
111
+ query: Object.entries({ ...parameters, minorversion: "75" }),
112
+ });
113
+ return object(data);
114
+ }),
115
+ }),
116
+ ];
117
+ }
@@ -15,7 +15,7 @@ export const SHEETS = {
15
15
  };
16
16
  const SpreadsheetId = { spreadsheetId: Schema.String };
17
17
  const optionalString = Schema.optionalKey(Schema.String);
18
- export const getSpreadsheet = get("getSpreadsheet", "/spreadsheets/:spreadsheetId", {
18
+ const getSpreadsheet = get("getSpreadsheet", "/spreadsheets/:spreadsheetId", {
19
19
  params: SpreadsheetId,
20
20
  query: { includeGridData: optionalString, fields: optionalString },
21
21
  success: Schema.Unknown,
@@ -23,7 +23,7 @@ export const sheetsIndex = new OperationIndex(sheetsDiscovery, {
23
23
  pathMatching: "uri-template",
24
24
  });
25
25
  /** Decode range colons only. Encoded actions, ids and unreserved bytes still fail the shared backstop. */
26
- export function sheetsPath(path) {
26
+ function sheetsPath(path) {
27
27
  const match = /^(\/spreadsheets\/[A-Za-z0-9_-]+\/values\/)([^/?#]+)$/.exec(path);
28
28
  if (!match)
29
29
  return path;
@@ -37,7 +37,7 @@ export function sheetsPath(path) {
37
37
  }
38
38
  const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
39
39
  /** Same scalar, range and write-cell ceilings as the named workflows, including filter-selected writes. */
40
- export function sheetsRequestProblem(call) {
40
+ function sheetsRequestProblem(call) {
41
41
  const body = record(call.body);
42
42
  if (call.op.path === "/spreadsheets/{spreadsheetId}:batchUpdate") {
43
43
  const requests = body["requests"];
@@ -1,4 +1,4 @@
1
- export const SHEETS_REQUEST_KINDS = [
1
+ const SHEETS_REQUEST_KINDS = [
2
2
  "addBanding",
3
3
  "addChart",
4
4
  "addCommentReply",
@@ -5,7 +5,6 @@ import { Refusal } from "../../http/errors.js";
5
5
  import { googleClient, googleFailureMessage, googleOutcomeOf } from "../_shared/google/kit.js";
6
6
  import { clampText, jsonBytes, RESULT_BUDGET_BYTES } from "../_shared/google/result-size.js";
7
7
  import { encodeForAgent, redactBeforeCut } from "../../sent-secrets.js";
8
- import { SHEETS } from "./api.js";
9
8
  import { read, write } from "../../http/tools.js";
10
9
  import { boundCursor } from "../../http/paging.js";
11
10
  import { fieldsInput, fieldsProblem, selectFields } from "../../http/fields.js";
@@ -336,7 +335,7 @@ const SPREADSHEET_FIELDS = [
336
335
  ].join(",");
337
336
  // --- Tools ------------------------------------------------------------------------
338
337
  export function sheetsTools({ connection }) {
339
- const client = googleClient(connection, SHEETS.provider, { standard: true, index: sheetsIndex });
338
+ const client = googleClient(connection, sheetsIndex);
340
339
  const readOnly = { readOnlyHint: true };
341
340
  // Additive: nothing that existed is lost. Writes require a writable
342
341
  // endpoint: call_destructive_tool or a program. The host controls approval.
@@ -23,7 +23,7 @@ const ReadQuery = {
23
23
  commentsViewMode: Schema.optionalKey(Schema.String),
24
24
  };
25
25
  // Discovery calls this one parameter `presentationsId` (plural).
26
- export const getPresentation = get("getPresentation", "/presentations/:presentationsId", {
26
+ const getPresentation = get("getPresentation", "/presentations/:presentationsId", {
27
27
  params: { presentationsId: Schema.String },
28
28
  query: ReadQuery,
29
29
  success: Schema.Unknown,
@@ -5,7 +5,7 @@ import { RESULT_BUDGET_BYTES, clampRedactedText, clampText, jsonBytes } from "..
5
5
  import { googleClient, googleOutcomeOf, googleFailureMessage } from "../_shared/google/kit.js";
6
6
  import { encodeForAgent, redactBeforeCut, redactSentSecrets } from "../../sent-secrets.js";
7
7
  import { REQUEST_KINDS, COMMENT_KINDS, COMMENTS_UNSAVED } from "./lib/requests.js";
8
- import { SLIDES, SLIDES_INDEX } from "./api.js";
8
+ import { SLIDES_INDEX } from "./api.js";
9
9
  import * as contracts from "./lib/schemas.js";
10
10
  import { read, write } from "../../http/tools.js";
11
11
  import { Operation } from "../../runtime/call.js";
@@ -1228,7 +1228,7 @@ function postToThread(connection, client, ctx, tool, args, post) {
1228
1228
  });
1229
1229
  }
1230
1230
  export function slidesTools({ connection }) {
1231
- const client = googleClient(connection, SLIDES.provider, { standard: true, index: SLIDES_INDEX });
1231
+ const client = googleClient(connection, SLIDES_INDEX);
1232
1232
  const readOnly = { readOnlyHint: true };
1233
1233
  const presentationPath = (id) => `/presentations/${encodeURIComponent(id)}`;
1234
1234
  const nextPage = (cursor) => cursor === null ? { hasMore: false } : { hasMore: true, nextCursor: cursor };
@@ -115,7 +115,7 @@ export const READ_CAP_MS = 60_000;
115
115
  * /v1/tax/calculations` is deliberately absent: it persists a Calculation
116
116
  * object a later Tax Transaction can reference, and Stripe bills each call.
117
117
  */
118
- export const READ_POSTS = [
118
+ const READ_POSTS = [
119
119
  ["POST", "/v1/invoices/create_preview", "Computes an upcoming invoice; Stripe persists nothing."],
120
120
  ];
121
121
  function invalid(message) {
@@ -126,7 +126,7 @@ function invalid(message) {
126
126
  * The key's own prefix carries its mode, so a live key in a sandbox
127
127
  * connector (or the reverse) is refused before any request leaves.
128
128
  */
129
- export function stripeKeyProblem(key, mode) {
129
+ function stripeKeyProblem(key, mode) {
130
130
  if (/^(?:sk|rk)_org_/.test(key)) {
131
131
  return "This is a Stripe organization key, which needs a Stripe-Context header this connector does not send. Use one account's secret or restricted key.";
132
132
  }
@@ -213,7 +213,7 @@ function expansions(call) {
213
213
  * secret's value, or a full Issuing card number or CVC, which an Issuing
214
214
  * authorization, transaction, or token can embed as well as the card itself.
215
215
  */
216
- export function refuse(call) {
216
+ function refuse(call) {
217
217
  const expanded = expansions(call);
218
218
  if (/^\/v1\/apps\/secrets(?:\/|$)/.test(call.path) && expanded.some((path) => /(?:^|\.)payload$/.test(path))) {
219
219
  return "Stripe Apps secret values are not returned through Connecta. Read the secret's metadata without expanding payload.";
@@ -225,7 +225,7 @@ export function refuse(call) {
225
225
  return undefined;
226
226
  }
227
227
  export const stripeIndex = new OperationIndex(openapi, { vendor: "stripe", title: "Stripe" });
228
- export const stripeSafety = valueSafety(STRIPE_VALUE_SAFETY, () => stripeIndex);
228
+ const stripeSafety = valueSafety(STRIPE_VALUE_SAFETY, () => stripeIndex);
229
229
  function operationRefusal(op) {
230
230
  return (checkServer(op.server) ??
231
231
  stripeSafety.refusal(op.method, op.path) ??
@@ -268,11 +268,11 @@ export const safety = () => ({
268
268
  reviewed: restSafety(stripeSafety, stripeIndex),
269
269
  sensitiveFields: { names: [], choice: "drop" },
270
270
  });
271
- export const getAccount = get("getAccount", "/v1/account", {
271
+ const getAccount = get("getAccount", "/v1/account", {
272
272
  headers: Schema.Struct({ "Stripe-Account": Schema.optionalKey(Schema.String) }),
273
273
  success: Schema.Unknown,
274
274
  });
275
- export const getBalance = get("getBalance", "/v1/balance", { success: Schema.Unknown });
275
+ const getBalance = get("getBalance", "/v1/balance", { success: Schema.Unknown });
276
276
  const RESPONSE_MEDIA = { "GET /v1/quotes/{quote}/pdf": ["application/pdf"] };
277
277
  export const StripeApi = {
278
278
  ...indexedVendor("stripe", stripeIndex, [getAccount, getBalance]),
@@ -317,7 +317,7 @@ export function connectedAccount(options) {
317
317
  throw new Error('stripe() connectedAccount must be a Stripe account id ("acct_...").');
318
318
  return account;
319
319
  }
320
- export function checkServer(server) {
320
+ function checkServer(server) {
321
321
  if (server === undefined || server === FILES_ORIGIN)
322
322
  return undefined;
323
323
  return server === METER_EVENTS_ORIGIN
@@ -162,7 +162,7 @@ export const listOrganizations = get("listOrganizations", "/organizations-list",
162
162
  query: PageQuery,
163
163
  success: Rows(OrganizationRecord),
164
164
  }).annotate(Summary, "List all organizations these keys manage");
165
- export const getOrganization = get("getOrganization", "/organizations/:organization_id", {
165
+ const getOrganization = get("getOrganization", "/organizations/:organization_id", {
166
166
  params: id("organization_id"),
167
167
  success: Single("organization_id", OrganizationRecord),
168
168
  }).annotate(Summary, "Retrieve an organization");
@@ -174,7 +174,7 @@ export const listAllAccounts = get("listAllAccounts", "/accounts-list-all", {
174
174
  query: PageQuery,
175
175
  success: Rows(AccountRecord),
176
176
  }).annotate(Summary, "List all donor accounts");
177
- export const getAccount = get("getAccount", "/accounts/:account_id", {
177
+ const getAccount = get("getAccount", "/accounts/:account_id", {
178
178
  params: id("account_id"),
179
179
  success: Single("account_id", AccountRecord),
180
180
  }).annotate(Summary, "Retrieve a donor account");
@@ -17,7 +17,7 @@ import { pinAdmission, pinOptions, pinQuery, pinRoute, readPinnedTeam } from "./
17
17
  export const MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
18
18
  export const READ_CAP_MS = 60_000;
19
19
  /** POSTs that only read, admitted by `vercel_api_read`. */
20
- export const READ_POSTS = [
20
+ const READ_POSTS = [
21
21
  ["POST", "/v2/observability/query", "Runs an Observability metrics query; Vercel persists nothing."],
22
22
  ["POST", "/v1/registrar/domains/availability", "Checks availability for a list of domains; buys nothing."],
23
23
  ["POST", "/v1/registrar/domains/price", "Quotes registrar prices for a list of domains; buys nothing."],
@@ -39,7 +39,7 @@ export function invalid(message) {
39
39
  export function compact(value) {
40
40
  return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
41
41
  }
42
- export function truthy(value) {
42
+ function truthy(value) {
43
43
  return value === true || value === 1 || (typeof value === "string" && /^(?:1|true)$/i.test(value.trim()));
44
44
  }
45
45
  /**
@@ -155,34 +155,34 @@ class VercelIndex extends OperationIndex {
155
155
  }
156
156
  }
157
157
  export const vercelIndex = new VercelIndex(discoveryIndex(openapi), { vendor: "vercel", title: "Vercel" });
158
- export const vercelSafety = valueSafety(VERCEL_VALUE_SAFETY, () => vercelIndex);
158
+ const vercelSafety = valueSafety(VERCEL_VALUE_SAFETY, () => vercelIndex);
159
159
  const Query = Schema.Record(Schema.String, Schema.Union([Schema.String, Schema.Array(Schema.String)]));
160
160
  const params = (...names) => Schema.Struct(Object.fromEntries(names.map((name) => [name, Schema.String])));
161
- export const currentUser = get("currentUser", "/v2/user", { success: Schema.Unknown });
162
- export const teams = get("teams", "/v2/teams", { query: Query, success: Schema.Unknown });
163
- export const projectEnv = get("projectEnv", "/v10/projects/:idOrName/env", {
161
+ const currentUser = get("currentUser", "/v2/user", { success: Schema.Unknown });
162
+ const teams = get("teams", "/v2/teams", { query: Query, success: Schema.Unknown });
163
+ const projectEnv = get("projectEnv", "/v10/projects/:idOrName/env", {
164
164
  params: params("idOrName"),
165
165
  query: Query,
166
166
  success: Schema.Unknown,
167
167
  });
168
- export const upsertEnv = post("upsertEnv", "/v10/projects/:idOrName/env", {
168
+ const upsertEnv = post("upsertEnv", "/v10/projects/:idOrName/env", {
169
169
  params: params("idOrName"),
170
170
  query: Query,
171
171
  payload: Schema.Unknown,
172
172
  success: Schema.Unknown,
173
173
  });
174
- export const updateEnv = patch("updateEnv", "/v9/projects/:idOrName/env/:id", {
174
+ const updateEnv = patch("updateEnv", "/v9/projects/:idOrName/env/:id", {
175
175
  params: params("idOrName", "id"),
176
176
  query: Query,
177
177
  payload: Schema.Unknown,
178
178
  success: Schema.Unknown,
179
179
  });
180
- export const buildLogs = get("buildLogs", "/v3/deployments/:idOrUrl/events", {
180
+ const buildLogs = get("buildLogs", "/v3/deployments/:idOrUrl/events", {
181
181
  params: params("idOrUrl"),
182
182
  query: Query,
183
183
  success: Schema.Unknown,
184
184
  });
185
- export const runtimeLogs = get("runtimeLogs", "/v1/projects/:projectId/deployments/:deploymentId/runtime-logs", {
185
+ const runtimeLogs = get("runtimeLogs", "/v1/projects/:projectId/deployments/:deploymentId/runtime-logs", {
186
186
  params: params("projectId", "deploymentId"),
187
187
  query: Query,
188
188
  success: Schema.Unknown,
@@ -294,7 +294,7 @@ export const refuse = (call) => {
294
294
  }
295
295
  return undefined;
296
296
  };
297
- export function scope(call, teamId, pin) {
297
+ function scope(call, teamId, pin) {
298
298
  if (pin)
299
299
  return { ...call, query: pinQuery(vercelIndex, call, pin) };
300
300
  const query = { ...call.query };
@@ -374,7 +374,7 @@ export function queryOf(call) {
374
374
  export function queryPairs(call) {
375
375
  return Object.entries(queryOf(call)).flatMap(([key, value]) => (Array.isArray(value) ? value : [value]).map((value) => [key, value]));
376
376
  }
377
- export function sendVercel(connection, call, options, sendOptions) {
377
+ function sendVercel(connection, call, options, sendOptions) {
378
378
  return checked(() => {
379
379
  const server = checkServer(call.op.server);
380
380
  if (server)
@@ -1,7 +1,6 @@
1
1
  import { keys, optionsOf, variants } from "../../config-schema.js";
2
2
  import { defineApiProvider } from "../../http/provider.js";
3
3
  import { CALL_ADMISSION } from "../../provider.js";
4
- import { removedHostedMcp } from "../_shared/rest/dispatch.js";
5
4
  import { authenticate, MAX_RESPONSE_BYTES, READ_CAP_MS, probe, result, safety, vercelErrors, VercelApi, usageGuide, transformGuide, VERCEL_VERDICTS, VERCEL_PAGES, vercelBinding, vercelIndex, } from "./api.js";
6
5
  import { VERCEL_API_BASE_URL } from "./lib/config.js";
7
6
  import { pinOptions, PIN_WRITE_STEPS } from "./lib/pin.js";
@@ -38,7 +37,7 @@ export const vercel = defineApiProvider({
38
37
  vendor: VercelApi,
39
38
  baseUrl: (options) => {
40
39
  if (options.auth.type !== "token")
41
- removedHostedMcp();
40
+ throw new Error("remoteMcp() with ConnectaConfig.classification for hosted MCP connections; use a distinct id beside the API connector.");
42
41
  pinOptions(options);
43
42
  return options.baseUrl ?? VERCEL_API_BASE_URL;
44
43
  },
@@ -34,7 +34,7 @@ const object = (value) => value !== null && typeof value === "object" && !Array.
34
34
  const fail = (reason) => {
35
35
  throw new ConnectorCallError("provider_permission_denied", `Vercel team pin: ${reason} Not sent to the requested resource.`, { retryable: false });
36
36
  };
37
- export function validatePin(options) {
37
+ function validatePin(options) {
38
38
  if (options.pin === undefined)
39
39
  return;
40
40
  const id = options.pin?.teamId;
@@ -268,7 +268,7 @@ function scopedToBody(handler, signal) {
268
268
  * The request's admission permit, owned by its Scope.
269
269
  *
270
270
  * Admission goes through the controller's Promise rather than its Effect
271
- * program (`acquireScoped`), on purpose. The pool is shared by every request
271
+ * program, on purpose. The pool is shared by every request
272
272
  * in the isolate, and a queued request is handed its permit from inside the
273
273
  * request whose body just ended. A fiber resumed there would authorize and
274
274
  * serve this request in that one's I/O context, which workerd refuses;
@@ -217,6 +217,9 @@ async function finishOAuthCallback(context) {
217
217
  if (["state", "iss", "code", "error"].some((name) => url.searchParams.getAll(name).length > 1))
218
218
  return refused();
219
219
  try {
220
+ if (connector.verifyCallbackParameters &&
221
+ !(await connector.verifyCallbackParameters(url.searchParams, connectorContext)))
222
+ return refused();
220
223
  const issuer = url.searchParams.get("iss");
221
224
  if (connector.verifyCallbackIssuer) {
222
225
  if (!(await connector.verifyCallbackIssuer(issuer, connectorContext)))
@@ -7,7 +7,6 @@
7
7
  // read its private bookkeeping, and one that emits nothing into the `.d.ts`.
8
8
  // Promise callers keep `acquire()`, which runs the same program through
9
9
  // runEdge; Effect callers come through this module and never see a Promise.
10
- import { Effect } from "effect";
11
10
  let program;
12
11
  /**
13
12
  * Install the controller's admission program. Called once, by
@@ -32,14 +31,3 @@ export function admit(controller, options = {}) {
32
31
  }
33
32
  return program(controller, options.signal, options.wait !== false);
34
33
  }
35
- /**
36
- * A lease owned by the enclosing Scope: released when the scope closes, on
37
- * success, failure, or interruption alike. The wait itself stays
38
- * interruptible — a queued request whose caller left must not hold its place
39
- * in line until the queue timeout.
40
- */
41
- export function acquireScoped(controller, options = {}) {
42
- return Effect.acquireRelease(admit(controller, options), (lease) => Effect.sync(() => lease.release()), {
43
- interruptible: true,
44
- });
45
- }
@@ -292,9 +292,6 @@ export function deriveView(view, overrides) {
292
292
  views.set(derived, entry);
293
293
  return derived;
294
294
  }
295
- export function viewCaller(view) {
296
- return serviceFor(view, Caller);
297
- }
298
295
  export function viewLease(view) {
299
296
  return views.get(view)?.call;
300
297
  }
@@ -337,7 +334,6 @@ export function contextView(lease, operation) {
337
334
  lease.close();
338
335
  return view;
339
336
  }
340
- export const callView = (operation, lease) => contextView(lease, operation);
341
337
  export function chargeView(view) {
342
338
  const budget = recordedService(view, Budget);
343
339
  return budget ? runEdge(budget.charge) : Promise.reject(ended());
package/dist/skills.js CHANGED
@@ -32,7 +32,6 @@ export function endpointInstructions(description, native) {
32
32
  export function nativeInstructions(executeCode) {
33
33
  return `Each tool here is one operation; annotations classify reads/writes. Call known tools directly; ${executeCode ? "combine work in execute_code using the program addresses in tool descriptions." : "find tools with your host's search."} Read-only endpoints omit and refuse writes. Never repeat a write for output. Give auth handoffs to the user/operator. Follow nextAction to page with ${executeCode ? "connecta.result in execute_code" : "read_result"}. skills lists guidance.`;
34
34
  }
35
- export { USAGE_SKILL } from "./usage-guide.js";
36
35
  /** True when at least one of `connectors` carries a usage guide. */
37
36
  export function hasConnectorGuides(connectors) {
38
37
  return connectors.some((connector) => connectorGuide(connector) !== undefined);
@@ -2,9 +2,8 @@
2
2
  //
3
3
  // A deployment has one KVStorage (D1 on Workers, SQLite on Node, memory in
4
4
  // tests), and every subsystem shares it. Sharing is safe because no two
5
- // families claim overlapping keys, and that is only checkable if every key is
6
- // built here: `test/storage-keys.node.test.ts` fails when another source file
7
- // spells one of these prefixes itself.
5
+ // families claim overlapping keys. Build every key here; persistence journeys
6
+ // check actual isolation through the deployment and its physical database.
8
7
  //
9
8
  // Each family records:
10
9
  //
package/dist/types.d.ts CHANGED
@@ -432,6 +432,8 @@ export interface Connector {
432
432
  * pending URL could complete consent with their own account.
433
433
  */
434
434
  verifyState?(state: string | null, ctx: ConnectorContext): Promise<boolean>;
435
+ /** Validate provider callback parameters after state verification and before issuer validation, including errors. */
436
+ verifyCallbackParameters?(params: URLSearchParams, ctx: ConnectorContext): Promise<boolean>;
435
437
  /** Validate a callback issuer against its verified consent, including error responses. */
436
438
  verifyCallbackIssuer?(issuer: string | null, ctx: ConnectorContext): Promise<boolean>;
437
439
  /**
package/dist/ui.d.ts CHANGED
@@ -6,7 +6,7 @@ import type { RegistryView } from "./registry.js";
6
6
  import type { ConnectaBranding, Connector, ConnectorStatus, ToolDef, UiAuthConfig } from "./types.js";
7
7
  export { filterUiConnectors, type CredentialManagementCapability, type UiConnector, type UiData, } from "./operator-ui/model.js";
8
8
  export { CONNECTA_FAVICON_SVG, resolveBranding, isSafeHttpUrl, isSafeHttpsUrl, isSafeIconHref } from "./branding.js";
9
- export type OperatorPage = import("./operator-ui/view.js").OperatorPage;
9
+ export type OperatorPage = import("./operator-ui/contract.js").OperatorPage;
10
10
  export declare function operatorPageForPath(path: string): OperatorPage | undefined;
11
11
  export declare function operatorPageTitle(page: OperatorPage, configuredTitle: string): string;
12
12
  /** Report the verdict the registry stored, independent of whether an endpoint is read-only. */
package/dist/ui.js CHANGED
@@ -164,7 +164,6 @@ _nonce, page = "endpoints") {
164
164
  productName: brand.productName,
165
165
  brandMark: brandMark(branding, true),
166
166
  productDescription: brand.description,
167
- productOperatorLabel: brand.productName + " operator",
168
167
  })}</script><script defer src="${OPERATOR_UI_SCRIPT_PATH}"></script>`,
169
168
  });
170
169
  }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const CONNECTA_VERSION = "0.35.0";
1
+ export declare const CONNECTA_VERSION = "0.36.1";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Generated from package.json by scripts/generate-version.mjs.
2
2
  // Workers-safe: no runtime filesystem read or deployment override.
3
- export const CONNECTA_VERSION = "0.35.0";
3
+ export const CONNECTA_VERSION = "0.36.1";
@@ -12,6 +12,7 @@ For a new deployment, start with [Deploying Connecta](./deploying.md).
12
12
  | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
13
13
  | Add or remove a connector | [Change the connector roster](./integrating.md#add-or-remove-a-connector) |
14
14
  | Set up a maintained provider | [Maintained provider setup](./providers.md) |
15
+ | Inspect maintained API sources and tools | [API sources and tool inventory](./providers.md#api-sources-and-tool-inventory) |
15
16
  | Organize an agent's pools and tiers | [Organizing an agent's pools](./auth.md#organizing-an-agents-pools) |
16
17
  | Add a person (maintainer or pool member) | [Add a person](./auth.md#add-a-person), [member page](./operator-ui.md#use-the-member-page) |
17
18
  | Connect a shared credential | [Connect a shared credential](./auth.md#connect-a-shared-credential) |