@zackbart/connecta 0.35.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +8 -4
- package/bin/version.mjs +1 -1
- package/dist/auth/downstream-oauth.js +71 -79
- package/dist/auth/static-oauth.js +104 -26
- package/dist/connectors/api-connector.d.ts +17 -0
- package/dist/connectors/api-connector.js +3 -1
- package/dist/http/client.js +1 -1
- package/dist/http/paging.js +1 -2
- package/dist/http/services.js +1 -1
- package/dist/http/tools.js +0 -4
- package/dist/operator-ui/contract.d.ts +1 -0
- package/dist/operator-ui/generated.js +4 -4
- package/dist/providers/_shared/google/kit.js +29 -139
- package/dist/providers/_shared/rest/kit.js +4 -404
- package/dist/providers/docs/tools.js +1 -2
- package/dist/providers/drive/api.js +1 -1
- package/dist/providers/forms/tools.js +1 -2
- package/dist/providers/gmail/tools.js +2 -2
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/quickbooks/api.js +299 -0
- package/dist/providers/quickbooks/index.d.ts +13 -0
- package/dist/providers/quickbooks/index.js +93 -0
- package/dist/providers/quickbooks/lib/query.js +118 -0
- package/dist/providers/quickbooks/lib/writes.js +168 -0
- package/dist/providers/quickbooks/safety.js +222 -0
- package/dist/providers/quickbooks/skill.generated.js +5 -0
- package/dist/providers/quickbooks/tools.js +117 -0
- package/dist/providers/sheets/tools.js +1 -2
- package/dist/providers/slides/tools.js +2 -2
- package/dist/providers/vercel/index.js +1 -2
- package/dist/routes/oauth.js +3 -0
- package/dist/runtime/call.js +0 -4
- package/dist/skills.js +0 -1
- package/dist/types.d.ts +2 -0
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +0 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/README.md +1 -0
- package/documentation/architecture.md +134 -301
- package/documentation/auth.md +47 -17
- package/documentation/integrating.md +1 -1
- package/documentation/meta-tools.md +1 -1
- package/documentation/operator-ui.md +16 -3
- package/documentation/providers.md +382 -0
- package/package.json +7 -8
- package/templates/node/package.json +1 -1
- package/dist/operator-ui/agents.d.ts +0 -75
- package/dist/operator-ui/agents.js +0 -114
- package/dist/operator-ui/config-summary.js +0 -566
- package/dist/operator-ui/connection-map.js +0 -378
- package/dist/operator-ui/endpoints.js +0 -212
- package/dist/operator-ui/fix-prompts.d.ts +0 -18
- package/dist/operator-ui/fix-prompts.js +0 -79
- package/dist/operator-ui/health.js +0 -52
- package/dist/operator-ui/member-page.js +0 -97
- package/dist/operator-ui/overview.js +0 -126
- package/dist/operator-ui/setup-commands.js +0 -47
- package/dist/operator-ui/view.d.ts +0 -378
- package/dist/operator-ui/view.js +0 -631
- package/dist/providers/_shared/rest/dispatch.js +0 -21
- package/dist/providers/_shared/rest/tools.js +0 -592
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { ConnectorCallError } from "../../../errors.js";
|
|
3
|
+
import { Refusal, StatusFailure } from "../../../http/errors.js";
|
|
4
|
+
import { Budget } from "../../../http/services.js";
|
|
5
|
+
import { write } from "../../../http/tools.js";
|
|
6
|
+
import { companyInfo, CustomerNames, customerWrite, invoiceWrite, PublicId, recordReads, SyncToken } from "../api.js";
|
|
7
|
+
import { namedRecord, NamedRecordShape, object } from "../safety.js";
|
|
8
|
+
import { matches, reportProblem } from "./query.js";
|
|
9
|
+
const RequestId = Schema.String.check(matches("^[A-Za-z0-9][A-Za-z0-9_-]{0,49}$")).annotate({
|
|
10
|
+
description: "Stable identifier for this exact intent, unique across all requests in this company; at most 50 ASCII letters, digits, underscores or hyphens. Never reuse for changed content.",
|
|
11
|
+
});
|
|
12
|
+
const DateOnly = Schema.String.check(matches("^\\d{4}-\\d{2}-\\d{2}$"));
|
|
13
|
+
// Decimal strings avoid binary arithmetic and caller-side cents conversion. Conversion only encodes a JSON number.
|
|
14
|
+
const Amount = Schema.String.check(matches("^(?:0|[1-9]\\d{0,8})\\.\\d{2}$")).annotate({
|
|
15
|
+
description: "Positive decimal currency units with exactly two fractional digits, at most 999999999.99; never integer cents. One service unit per line.",
|
|
16
|
+
});
|
|
17
|
+
const InvoiceInput = Schema.Struct({
|
|
18
|
+
requestId: RequestId,
|
|
19
|
+
customerId: PublicId.annotate({ description: "Caller-observed active Customer Id in this company." }),
|
|
20
|
+
currency: Schema.Literal("USD").annotate({
|
|
21
|
+
description: "Caller-observed customer currency, restricted to USD for this initial two-decimal write surface. Vendor computes taxes and totals.",
|
|
22
|
+
}),
|
|
23
|
+
exchangeRate: Schema.String.check(matches("^(?:0|[1-9]\\d{0,5})(?:\\.\\d{1,6})?$")).annotate({
|
|
24
|
+
description: "Explicit positive home-currency units per transaction-currency unit, at most six decimal places. Use 1 for home currency; no rate is fetched or defaulted.",
|
|
25
|
+
}),
|
|
26
|
+
txnDate: DateOnly.annotate({ description: "Explicit accounting date, YYYY-MM-DD." }),
|
|
27
|
+
dueDate: DateOnly.annotate({ description: "Explicit due date, YYYY-MM-DD, on or after txnDate." }),
|
|
28
|
+
docNumber: Schema.String.check(matches("^[A-Za-z0-9][A-Za-z0-9_-]{0,20}$")).annotate({
|
|
29
|
+
description: "Explicit reconciliation label, at most 21 characters; not AUTO_GENERATE. Labels may repeat, so reconcile customer, currency and amounts too.",
|
|
30
|
+
}),
|
|
31
|
+
lines: Schema.Array(Schema.Struct({
|
|
32
|
+
itemId: PublicId.annotate({
|
|
33
|
+
description: "Caller-observed active Service item Id; inventory and other item types are refused.",
|
|
34
|
+
}),
|
|
35
|
+
amount: Amount,
|
|
36
|
+
})).check(Schema.isMinLength(1), Schema.isMaxLength(20)),
|
|
37
|
+
});
|
|
38
|
+
const refuse = (message) => Effect.fail(new Refusal({ error: new ConnectorCallError("invalid_args", message) }));
|
|
39
|
+
const version = { minorversion: "75" };
|
|
40
|
+
const charge = Effect.flatMap(Budget, (budget) => budget.charge).pipe(Effect.catch((error) => error instanceof ConnectorCallError ? Effect.fail(new Refusal({ error })) : Effect.die(error)));
|
|
41
|
+
/** Totals and tax are vendor-computed; the authored references, dates and service prices must agree. */
|
|
42
|
+
function matchesInvoice(record, args) {
|
|
43
|
+
const lines = Array.isArray(record["Line"]) ? record["Line"] : [];
|
|
44
|
+
const services = lines.filter((line) => object(line)["DetailType"] === "SalesItemLineDetail");
|
|
45
|
+
const expected = args.lines
|
|
46
|
+
.map((line) => JSON.stringify([line.itemId, Number(line.amount), 1, Number(line.amount)]))
|
|
47
|
+
.sort();
|
|
48
|
+
const actual = services
|
|
49
|
+
.map((line) => {
|
|
50
|
+
const row = object(line), detail = object(row["SalesItemLineDetail"]);
|
|
51
|
+
return JSON.stringify([object(detail["ItemRef"])["value"], row["Amount"], detail["Qty"], detail["UnitPrice"]]);
|
|
52
|
+
})
|
|
53
|
+
.sort();
|
|
54
|
+
return (object(record["CustomerRef"])["value"] === args.customerId &&
|
|
55
|
+
object(record["CurrencyRef"])["value"] === args.currency &&
|
|
56
|
+
record["DocNumber"] === args.docNumber &&
|
|
57
|
+
record["TxnDate"] === args.txnDate &&
|
|
58
|
+
record["DueDate"] === args.dueDate &&
|
|
59
|
+
(record["ExchangeRate"] === Number(args.exchangeRate) ||
|
|
60
|
+
(record["ExchangeRate"] === undefined && Number(args.exchangeRate) === 1)) &&
|
|
61
|
+
lines.every((line) => ["SalesItemLineDetail", "SubTotalLineDetail"].includes(String(object(line)["DetailType"]))) &&
|
|
62
|
+
JSON.stringify(actual) === JSON.stringify(expected));
|
|
63
|
+
}
|
|
64
|
+
export function quickbooksWrites(connection, realmId) {
|
|
65
|
+
return [
|
|
66
|
+
write(connection, {
|
|
67
|
+
name: "create_customer",
|
|
68
|
+
description: "Create a customer once with a DisplayName and optional CompanyName. No contact, tax, bank or payment fields. Reconcile an unknown outcome by exact DisplayName before another intent.",
|
|
69
|
+
destructive: true,
|
|
70
|
+
idempotent: false,
|
|
71
|
+
input: Schema.Struct({ requestId: RequestId, ...CustomerNames }),
|
|
72
|
+
output: NamedRecordShape,
|
|
73
|
+
run: ({ requestId, ...body }) => connection
|
|
74
|
+
.send(customerWrite, { query: { ...version, requestid: requestId }, payload: body })
|
|
75
|
+
.pipe(Effect.map(({ data }) => namedRecord(object(data)["Customer"]))),
|
|
76
|
+
}),
|
|
77
|
+
write(connection, {
|
|
78
|
+
name: "update_customer",
|
|
79
|
+
description: "Sparsely update only DisplayName and optional CompanyName using caller-observed Id and SyncToken. A stale version fails; no version fetch or overwrite follows. Omitted CompanyName is unchanged.",
|
|
80
|
+
destructive: true,
|
|
81
|
+
idempotent: false,
|
|
82
|
+
input: Schema.Struct({ requestId: RequestId, Id: PublicId, SyncToken, ...CustomerNames }),
|
|
83
|
+
output: NamedRecordShape,
|
|
84
|
+
run: ({ requestId, ...body }) => connection
|
|
85
|
+
.send(customerWrite, { query: { ...version, requestid: requestId }, payload: { ...body, sparse: true } })
|
|
86
|
+
.pipe(Effect.map(({ data }) => namedRecord(object(data)["Customer"]))),
|
|
87
|
+
}),
|
|
88
|
+
write(connection, {
|
|
89
|
+
name: "create_invoice",
|
|
90
|
+
destructive: true,
|
|
91
|
+
idempotent: false,
|
|
92
|
+
description: "Create a real US-company USD accounting invoice, never a draft, once after checking company, active customer/currency and Service items. Dates, DocNumber, exchange rate and one-unit line prices are explicit. EmailStatus is fixed NotSet and online payment flags are false; no email/send operation. Vendor computes taxes/totals. Reconcile unknown outcomes through reads; never blindly create again.",
|
|
93
|
+
input: InvoiceInput,
|
|
94
|
+
output: NamedRecordShape,
|
|
95
|
+
writeSteps: {
|
|
96
|
+
company: "once",
|
|
97
|
+
customer: "once",
|
|
98
|
+
items: {
|
|
99
|
+
maxItems: 20,
|
|
100
|
+
items: (args) => [...new Set(args.lines.map((line) => line.itemId))],
|
|
101
|
+
request: (item) => ({ method: "GET", path: `/item/${item}`, template: "/item/:id", query: version }),
|
|
102
|
+
},
|
|
103
|
+
write: "once",
|
|
104
|
+
},
|
|
105
|
+
run: (args) => Effect.gen(function* () {
|
|
106
|
+
const problem = reportProblem("ProfitAndLoss", { start_date: args.txnDate, end_date: args.dueDate });
|
|
107
|
+
if (problem)
|
|
108
|
+
return yield* refuse("Invoice dates must be real YYYY-MM-DD dates, with dueDate on or after txnDate.");
|
|
109
|
+
if (args.docNumber === "AUTO_GENERATE" ||
|
|
110
|
+
Number(args.exchangeRate) <= 0 ||
|
|
111
|
+
args.lines.some((line) => Number(line.amount) <= 0))
|
|
112
|
+
return yield* refuse("Use an explicit DocNumber and positive exchange rate and line amounts.");
|
|
113
|
+
const company = object(object((yield* connection.send(companyInfo, { params: { id: realmId }, query: version }, { step: "company", effect: "read" })).data)["CompanyInfo"]);
|
|
114
|
+
if (company["Id"] !== realmId || company["Country"] !== "US")
|
|
115
|
+
return yield* refuse("Invoice creation is restricted to US companies, where the reviewed invoice-autosend contract applies.");
|
|
116
|
+
yield* charge;
|
|
117
|
+
const customer = object(object((yield* connection.send(recordReads["customers"], { params: { id: args.customerId }, query: version }, { step: "customer", effect: "read" })).data)["Customer"]);
|
|
118
|
+
if (customer["Id"] !== args.customerId ||
|
|
119
|
+
customer["Active"] !== true ||
|
|
120
|
+
object(customer["CurrencyRef"])["value"] !== args.currency)
|
|
121
|
+
return yield* refuse("The customer must be active and have the supplied currency; reread before creating an invoice.");
|
|
122
|
+
for (const itemId of new Set(args.lines.map((line) => line.itemId))) {
|
|
123
|
+
yield* charge;
|
|
124
|
+
const item = object(object((yield* connection.send(recordReads["items"], { params: { id: itemId }, query: version }, { step: "items", item: itemId, effect: "read" })).data)["Item"]);
|
|
125
|
+
if (item["Id"] !== itemId || item["Active"] !== true || item["Type"] !== "Service")
|
|
126
|
+
return yield* refuse("Every invoice item must be an active Service item; reread before creating an invoice.");
|
|
127
|
+
}
|
|
128
|
+
yield* charge;
|
|
129
|
+
const { data, status } = yield* connection.send(invoiceWrite, {
|
|
130
|
+
query: { ...version, requestid: args.requestId },
|
|
131
|
+
payload: {
|
|
132
|
+
CustomerRef: { value: args.customerId },
|
|
133
|
+
CurrencyRef: { value: args.currency },
|
|
134
|
+
ExchangeRate: Number(args.exchangeRate),
|
|
135
|
+
TxnDate: args.txnDate,
|
|
136
|
+
DueDate: args.dueDate,
|
|
137
|
+
DocNumber: args.docNumber,
|
|
138
|
+
Line: args.lines.map((line) => ({
|
|
139
|
+
Amount: Number(line.amount),
|
|
140
|
+
DetailType: "SalesItemLineDetail",
|
|
141
|
+
SalesItemLineDetail: {
|
|
142
|
+
ItemRef: { value: line.itemId },
|
|
143
|
+
Qty: 1,
|
|
144
|
+
UnitPrice: Number(line.amount),
|
|
145
|
+
},
|
|
146
|
+
})),
|
|
147
|
+
EmailStatus: "NotSet",
|
|
148
|
+
AllowIPNPayment: false,
|
|
149
|
+
AllowOnlinePayment: false,
|
|
150
|
+
AllowOnlineCreditCardPayment: false,
|
|
151
|
+
AllowOnlineACHPayment: false,
|
|
152
|
+
AllowOnlinePayPalPayment: false,
|
|
153
|
+
AllowOnlineAffirmPayment: false,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
const invoice = object(object(data)["Invoice"]);
|
|
157
|
+
if (!matchesInvoice(invoice, args))
|
|
158
|
+
return yield* Effect.fail(new StatusFailure({
|
|
159
|
+
status,
|
|
160
|
+
headers: new Headers(),
|
|
161
|
+
body: undefined,
|
|
162
|
+
operation: { method: "POST", template: "/invoice" },
|
|
163
|
+
}));
|
|
164
|
+
return namedRecord(invoice);
|
|
165
|
+
}),
|
|
166
|
+
}),
|
|
167
|
+
];
|
|
168
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { ConnectorCallError } from "../../errors.js";
|
|
3
|
+
import { withholdEmbeddedUrls } from "../_shared/rest/value-safety.js";
|
|
4
|
+
import { QUICKBOOKS_INDEX } from "./api.js";
|
|
5
|
+
const scalars = [
|
|
6
|
+
"Id",
|
|
7
|
+
"SyncToken",
|
|
8
|
+
"DisplayName",
|
|
9
|
+
"Name",
|
|
10
|
+
"CompanyName",
|
|
11
|
+
"Active",
|
|
12
|
+
"FullyQualifiedName",
|
|
13
|
+
"AccountType",
|
|
14
|
+
"AccountSubType",
|
|
15
|
+
"Classification",
|
|
16
|
+
"CurrentBalance",
|
|
17
|
+
"CurrentBalanceWithSubAccounts",
|
|
18
|
+
"Type",
|
|
19
|
+
"UnitPrice",
|
|
20
|
+
"PurchaseCost",
|
|
21
|
+
"QtyOnHand",
|
|
22
|
+
"TrackQtyOnHand",
|
|
23
|
+
"TxnDate",
|
|
24
|
+
"DueDate",
|
|
25
|
+
"DocNumber",
|
|
26
|
+
"TotalAmt",
|
|
27
|
+
"Balance",
|
|
28
|
+
"UnappliedAmt",
|
|
29
|
+
"ExchangeRate",
|
|
30
|
+
"HomeTotalAmt",
|
|
31
|
+
"HomeBalance",
|
|
32
|
+
"Amount",
|
|
33
|
+
"LineNum",
|
|
34
|
+
"DetailType",
|
|
35
|
+
"Qty",
|
|
36
|
+
"UnitBasedOn",
|
|
37
|
+
"PercentBased",
|
|
38
|
+
"DiscountPercent",
|
|
39
|
+
"DiscountAmt",
|
|
40
|
+
"TotalTax",
|
|
41
|
+
"TaxInclusiveAmt",
|
|
42
|
+
"PayType",
|
|
43
|
+
];
|
|
44
|
+
const references = [
|
|
45
|
+
"CurrencyRef",
|
|
46
|
+
"CustomerRef",
|
|
47
|
+
"VendorRef",
|
|
48
|
+
"ParentRef",
|
|
49
|
+
"AccountRef",
|
|
50
|
+
"IncomeAccountRef",
|
|
51
|
+
"ExpenseAccountRef",
|
|
52
|
+
"AssetAccountRef",
|
|
53
|
+
"APAccountRef",
|
|
54
|
+
"ARAccountRef",
|
|
55
|
+
"DepositToAccountRef",
|
|
56
|
+
"ItemRef",
|
|
57
|
+
"ClassRef",
|
|
58
|
+
"DepartmentRef",
|
|
59
|
+
"SalesTermRef",
|
|
60
|
+
];
|
|
61
|
+
const details = [
|
|
62
|
+
"SalesItemLineDetail",
|
|
63
|
+
"AccountBasedExpenseLineDetail",
|
|
64
|
+
"ItemBasedExpenseLineDetail",
|
|
65
|
+
"DiscountLineDetail",
|
|
66
|
+
"SubTotalLineDetail",
|
|
67
|
+
"GroupLineDetail",
|
|
68
|
+
"TxnTaxDetail",
|
|
69
|
+
];
|
|
70
|
+
const optional = (names) => Object.fromEntries(names.map((key) => [key, Schema.optionalKey(Schema.Unknown)]));
|
|
71
|
+
export const RecordShape = Schema.Struct({
|
|
72
|
+
...optional(scalars),
|
|
73
|
+
...optional(references),
|
|
74
|
+
...optional(details),
|
|
75
|
+
MetaData: Schema.optionalKey(Schema.Unknown),
|
|
76
|
+
Line: Schema.optionalKey(Schema.Unknown),
|
|
77
|
+
LinkedTxn: Schema.optionalKey(Schema.Unknown),
|
|
78
|
+
});
|
|
79
|
+
export const CompanyShape = Schema.Struct(optional(["Id", "SyncToken", "CompanyName", "LegalName", "Country", "FiscalYearStartMonth", "SupportedLanguages"]));
|
|
80
|
+
const namedKeys = [
|
|
81
|
+
"Id",
|
|
82
|
+
"SyncToken",
|
|
83
|
+
"DisplayName",
|
|
84
|
+
"Name",
|
|
85
|
+
"CompanyName",
|
|
86
|
+
"Active",
|
|
87
|
+
"AccountType",
|
|
88
|
+
"CurrentBalance",
|
|
89
|
+
"UnitPrice",
|
|
90
|
+
"QtyOnHand",
|
|
91
|
+
"PayType",
|
|
92
|
+
"Type",
|
|
93
|
+
"TxnDate",
|
|
94
|
+
"DueDate",
|
|
95
|
+
"DocNumber",
|
|
96
|
+
"TotalAmt",
|
|
97
|
+
"Balance",
|
|
98
|
+
"UnappliedAmt",
|
|
99
|
+
"CurrencyRef",
|
|
100
|
+
"ExchangeRate",
|
|
101
|
+
"CustomerRef",
|
|
102
|
+
"VendorRef",
|
|
103
|
+
"MetaData",
|
|
104
|
+
"Line",
|
|
105
|
+
"LinkedTxn",
|
|
106
|
+
];
|
|
107
|
+
export const NamedRecordShape = Schema.Struct(optional(namedKeys));
|
|
108
|
+
export const namedRecord = (value) => Object.fromEntries(Object.entries(object(value)).filter(([key]) => namedKeys.includes(key)));
|
|
109
|
+
export const ReportShape = Schema.Struct({
|
|
110
|
+
Header: Schema.optionalKey(Schema.Unknown),
|
|
111
|
+
Columns: Schema.optionalKey(Schema.Unknown),
|
|
112
|
+
Rows: Schema.optionalKey(Schema.Unknown),
|
|
113
|
+
});
|
|
114
|
+
export const object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
115
|
+
const cleanText = (text) => withholdEmbeddedUrls(text).replace(/https?:\/\/[^\s<>"']+/gi, "[redacted]");
|
|
116
|
+
const scalar = (value) => typeof value === "string"
|
|
117
|
+
? cleanText(value)
|
|
118
|
+
: (typeof value === "number" && Number.isFinite(value)) || typeof value === "boolean"
|
|
119
|
+
? value
|
|
120
|
+
: undefined;
|
|
121
|
+
function pick(value, keys) {
|
|
122
|
+
const source = object(value);
|
|
123
|
+
return Object.fromEntries(keys.flatMap((key) => source[key] === undefined || scalar(source[key]) === undefined ? [] : [[key, scalar(source[key])]]));
|
|
124
|
+
}
|
|
125
|
+
/** Closed accounting projection: unknown fields never become a way around privacy. SyncToken is a version, not a credential. */
|
|
126
|
+
export function safeRecord(value) {
|
|
127
|
+
const source = object(value), out = pick(source, scalars);
|
|
128
|
+
for (const key of references)
|
|
129
|
+
if (source[key])
|
|
130
|
+
out[key] = pick(source[key], ["value", "name", "type"]);
|
|
131
|
+
if (source["MetaData"])
|
|
132
|
+
out["MetaData"] = pick(source["MetaData"], ["CreateTime", "LastUpdatedTime"]);
|
|
133
|
+
for (const key of details)
|
|
134
|
+
if (source[key])
|
|
135
|
+
out[key] = safeRecord(source[key]);
|
|
136
|
+
if (Array.isArray(source["Line"]))
|
|
137
|
+
out["Line"] = source["Line"].map(safeRecord);
|
|
138
|
+
if (Array.isArray(source["LinkedTxn"]))
|
|
139
|
+
out["LinkedTxn"] = source["LinkedTxn"].map((row) => pick(row, ["TxnId", "TxnType", "TxnLineId"]));
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
// Report trees retain vendor hierarchy, column metadata and empty strings. No index has a fixed metric meaning.
|
|
143
|
+
function reportTree(value) {
|
|
144
|
+
if (Array.isArray(value))
|
|
145
|
+
return value.map(reportTree);
|
|
146
|
+
if (typeof value !== "object" || value === null)
|
|
147
|
+
return scalar(value);
|
|
148
|
+
const keys = new Set([
|
|
149
|
+
"Header",
|
|
150
|
+
"Columns",
|
|
151
|
+
"Column",
|
|
152
|
+
"Rows",
|
|
153
|
+
"Row",
|
|
154
|
+
"Summary",
|
|
155
|
+
"ColData",
|
|
156
|
+
"ColTitle",
|
|
157
|
+
"ColType",
|
|
158
|
+
"MetaData",
|
|
159
|
+
"Name",
|
|
160
|
+
"Value",
|
|
161
|
+
"Option",
|
|
162
|
+
"Time",
|
|
163
|
+
"ReportName",
|
|
164
|
+
"DateMacro",
|
|
165
|
+
"ReportBasis",
|
|
166
|
+
"StartPeriod",
|
|
167
|
+
"EndPeriod",
|
|
168
|
+
"SummarizeColumnsBy",
|
|
169
|
+
"Currency",
|
|
170
|
+
"value",
|
|
171
|
+
"id",
|
|
172
|
+
"type",
|
|
173
|
+
"group",
|
|
174
|
+
]);
|
|
175
|
+
return Object.fromEntries(Object.entries(value)
|
|
176
|
+
.filter(([key]) => keys.has(key))
|
|
177
|
+
.map(([key, entry]) => [key, reportTree(entry)]));
|
|
178
|
+
}
|
|
179
|
+
export function quickbooksSafety(options) {
|
|
180
|
+
return {
|
|
181
|
+
reviewed: {
|
|
182
|
+
resolve: (method, path) => {
|
|
183
|
+
if (/[%;\\?#]/.test(path) || path.split("/").some((part) => part === "." || part === ".."))
|
|
184
|
+
throw new ConnectorCallError("invalid_args", "Use a canonical reviewed QuickBooks path.");
|
|
185
|
+
const { op } = QUICKBOOKS_INDEX.resolve(method, path);
|
|
186
|
+
if (op.path === "/companyinfo/{id}" && path !== `/companyinfo/${options.realmId}`)
|
|
187
|
+
throw new ConnectorCallError("invalid_args", "The company must match the configured realmId.");
|
|
188
|
+
return { method: op.method, template: op.path };
|
|
189
|
+
},
|
|
190
|
+
withholdsErrors: () => true,
|
|
191
|
+
redact: (body, operation) => {
|
|
192
|
+
const root = object(body);
|
|
193
|
+
if (operation.template.startsWith("/reports/"))
|
|
194
|
+
return reportTree(root);
|
|
195
|
+
if (operation.template.startsWith("/companyinfo/"))
|
|
196
|
+
return {
|
|
197
|
+
CompanyInfo: pick(root["CompanyInfo"], [
|
|
198
|
+
"Id",
|
|
199
|
+
"SyncToken",
|
|
200
|
+
"CompanyName",
|
|
201
|
+
"LegalName",
|
|
202
|
+
"Country",
|
|
203
|
+
"FiscalYearStartMonth",
|
|
204
|
+
"SupportedLanguages",
|
|
205
|
+
]),
|
|
206
|
+
};
|
|
207
|
+
if (operation.template === "/query") {
|
|
208
|
+
const query = object(root["QueryResponse"]);
|
|
209
|
+
return {
|
|
210
|
+
QueryResponse: Object.fromEntries(Object.entries(query)
|
|
211
|
+
.filter(([key]) => ["Customer", "Vendor", "Account", "Item", "Invoice", "Bill", "Payment", "BillPayment"].includes(key) && Array.isArray(query[key]))
|
|
212
|
+
.map(([key, rows]) => [key, rows.map(safeRecord)])),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return Object.fromEntries(Object.entries(root)
|
|
216
|
+
.filter(([key]) => ["Customer", "Vendor", "Account", "Item", "Invoice", "Bill", "Payment", "BillPayment"].includes(key))
|
|
217
|
+
.map(([key, record]) => [key, safeRecord(record)]));
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
sensitiveFields: { names: [], choice: "drop" },
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
|
+
export const skill = {
|
|
3
|
+
"content": "## Accounting identity and joins\n\nAn Id identifies a record inside this company and record family. DocNumber is\na human label, may repeat, and is not an Id. SyncToken is the public record\nversion used for concurrency control, not an OAuth credential. References carry\nthe target Id in value; invoice CustomerRef joins Customer, bill VendorRef\njoins Vendor, and payment Line.LinkedTxn joins invoices or bills.\n\nAccounting Payment and BillPayment records describe received and paid money;\nreading them does not execute a payment. Desktop, Payments, Payroll, attachments,\nand unreviewed accounting entities are outside this connection.\n\n## Money, time and reports\n\nAmounts use decimal currency units, never integer cents. Preserve CurrencyRef\nand ExchangeRate when combining transactions; never sum unlike currencies.\nTxnDate is the company's accounting date, not a UTC instant. MetaData timestamps\ncarry their own offset. Customer and vendor searches omit inactive records\nunless the Active filter explicitly selects them.\n\nReports are trees with Header, Columns, nested Rows and section summaries.\nRead column types and metadata together with row values; column order and\nsection labels can change. Empty amount strings mean no value, not zero.\nDo not flatten away section identity or add totals to their component rows.\nCheck the returned period, basis, currency and NoReportData option before\ncomparing reports. Aging is an as-of snapshot, rather than a transaction range.\n\nReturn aggregates and the few identifying records the task needs; avoid\ncopying full customer or vendor directories into the conversation.\n\n\n## Named writes and reconciliation\n\nThe connector defaults to read-only. Deployment code must set `readOnly: false`\nto expose named writes; endpoint and pool read-only policies still apply. The\nAPI hatch remains read-only. `create_customer` accepts DisplayName and optional\nCompanyName. `update_customer` requires caller-observed Id and SyncToken and\nchanges only those naming fields with sparse:true. Omitted CompanyName stays\nunchanged. A stale-version conflict never fetches a newer version or overwrites\nit. Read and review the current record before proposing a new update.\nDisplayName must be unique across customers, vendors and employees.\n\n`create_invoice` creates a real accounting transaction, never a draft, for a\nverified US company and a customer using USD. This bounds the no-email claim\nto the reviewed US autosend contract and money to two-decimal USD units. First\nobserve an active customer with its CurrencyRef and active Service item Ids.\nSupply currency USD, a positive explicit exchange rate, accounting and\ndue dates, a reconciliation DocNumber and 1–20 lines. Each line is one service\nunit with an explicit positive amount in decimal currency units, supplied as a\nstring with exactly two fractional digits. No prices, dates or exchange rates\nare defaulted or fetched. Preflight reads verify the fixed company Country is US\nand recheck customer currency and item activity/type; they are not an atomic snapshot. Vendor validation can still\nreject changed references. Taxes and totals are computed by QuickBooks: inspect\nthe returned currency, exchange rate and totals; line sums are not a promise of\nan untaxed total. Locale/company tax requirements may reject this narrow body.\nNo customer/item creation happens implicitly.\nEach additional request charges the call-admission budget before dispatch.\nContradictory returned references, dates, rate or service prices produce an\nunknown outcome; reconcile through reads rather than repeating the write.\n\nInvoice email status is fixed to NotSet; online, IPN, credit-card, ACH, PayPal\nand Affirm payment flags are fixed false. No email field or send operation is\nexposed. Company invoice-autosend opt-in does not supply the documented\nNeedToSend request state. This prevents this creation request from asking Intuit\nto email the invoice, including when the customer has an email on file.\nInvoice updates are unavailable: preserving an existing send-ready state has\nnot been established safe, while resetting it would change payment settings.\nDeletes, voids, email delivery, charges, refunds, other accounting writes and\narbitrary mutations are unavailable.\n\nEvery write requires a caller-generated `requestId`: 1–50 ASCII letters,\ndigits, underscores or hyphens, starting with a letter or digit. Keep it stable\nfor the exact intent and unique across requests/tools for this company. It is\nsent in Intuit's requestid query parameter with minorversion 75, rather than an\nidempotency header. Intuit documents recognizing the same content and ID and\nreturning the original response; no retention duration is established. This\nnever permits automatic replay. A dispatched write is sent once even after\n401, timeout, server failure or an invalid success response.\n\nAfter an unknown customer-create outcome, search exact DisplayName, including\ninactive records when needed, and inspect the match. For an invoice, search\nDocNumber plus CustomerRef and inspect currency, dates, lines and computed\namounts. DocNumber can repeat, so it alone proves nothing. Reconcile and report\nambiguity before considering another create; never generate a fresh requestId\nand blindly repeat a potentially committed intent. Unknown update outcomes\nneed a read of the known Id and version. A requestId is not a lookup endpoint or\na permanent duplicate-prevention guarantee.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `quickbooks_api_details`, `quickbooks_api_search`, `search_records`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. Paged `list_*` and `search_*` reads take `limit` (default 25, at most 100). The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `quickbooks_api_search` finds this API's operations and reads no account data, `quickbooks_api_details` shows one operation's contract, and `quickbooks_api_read` calls a reviewed read. An operation without a reviewed verdict is refused. 3 operations are refused by design; use `quickbooks_api_details` with the method and path to inspect the original operation summary and its separate refusal. Use details for refused methods outside the read/write call schemas; those methods remain unavailable.\n- **Writes:** a write is sent once and never retried. When its outcome is unknown, read the target before repeating it. Writes take no idempotency key.\n- **Errors:** codes come from the vendor's statuses and error facts, never its prose. An unknown id, or one these credentials cannot reach, fails as `connector_call_failed`; this API does not tell the two apart. `conflict` means: Intuit fault 5010 for a stale SyncToken; Intuit fault 6240 for a duplicate DisplayName.\n- **Withheld values:** a value withheld for safety reads `\"[redacted]\"`. Writes refuse whole marker values and withheld links; longer prose may mention the marker. Paths and queries refuse any containment.",
|
|
4
|
+
"instructionsHeading": "Company instructions"
|
|
5
|
+
};
|
|
@@ -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
|
+
}
|
|
@@ -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,
|
|
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.
|
|
@@ -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 {
|
|
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,
|
|
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 };
|
|
@@ -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
|
-
|
|
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
|
},
|
package/dist/routes/oauth.js
CHANGED
|
@@ -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)))
|
package/dist/runtime/call.js
CHANGED
|
@@ -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);
|
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/
|
|
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
|
}
|