@integrity-labs/xero-broker 0.1.8 → 0.1.10
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/index.js +28 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21166,18 +21166,33 @@ var XeroBrokerClient = class {
|
|
|
21166
21166
|
}
|
|
21167
21167
|
});
|
|
21168
21168
|
}
|
|
21169
|
-
|
|
21169
|
+
// CS-1962: a GET has no body, so `requireTeamOrAgent` can only resolve the
|
|
21170
|
+
// agent from an `agent_id` QUERY param. Without it the middleware falls
|
|
21171
|
+
// through to the team path and 400s with "missing X-Team-Slug", so neither
|
|
21172
|
+
// poll nor await could ever return a decision (the POSTs worked because
|
|
21173
|
+
// they carry agent_id in the body). Same fix as cloud-broker's ENG-4739.
|
|
21174
|
+
agentQuery() {
|
|
21175
|
+
if (!this.agentId) {
|
|
21176
|
+
throw makeBrokerError(400, "XeroBrokerClient decision polling requires agentId");
|
|
21177
|
+
}
|
|
21178
|
+
return { agent_id: this.agentId };
|
|
21179
|
+
}
|
|
21180
|
+
async pollDecision(args) {
|
|
21170
21181
|
return this.request(
|
|
21171
21182
|
"GET",
|
|
21172
|
-
`/xero-broker/requests/${encodeURIComponent(args.request_id)}
|
|
21183
|
+
`/xero-broker/requests/${encodeURIComponent(args.request_id)}`,
|
|
21184
|
+
{ query: this.agentQuery() }
|
|
21173
21185
|
);
|
|
21174
21186
|
}
|
|
21175
|
-
awaitDecision(args) {
|
|
21187
|
+
async awaitDecision(args) {
|
|
21176
21188
|
return this.request(
|
|
21177
21189
|
"GET",
|
|
21178
21190
|
`/xero-broker/requests/${encodeURIComponent(args.request_id)}/await`,
|
|
21179
21191
|
{
|
|
21180
|
-
query:
|
|
21192
|
+
query: {
|
|
21193
|
+
...this.agentQuery(),
|
|
21194
|
+
...args.max_wait_seconds != null ? { max_wait_seconds: String(args.max_wait_seconds) } : {}
|
|
21195
|
+
}
|
|
21181
21196
|
}
|
|
21182
21197
|
);
|
|
21183
21198
|
}
|
|
@@ -21207,17 +21222,22 @@ var XERO_WRITE_VERBS = [
|
|
|
21207
21222
|
// product in its catalogue, so its invoice lines are free text that never
|
|
21208
21223
|
// depletes stock.
|
|
21209
21224
|
"xero.item.create",
|
|
21210
|
-
"xero.item.update"
|
|
21225
|
+
"xero.item.update",
|
|
21226
|
+
// CS-2000: repeating invoice templates. Without them a monthly retainer is
|
|
21227
|
+
// raised and emailed by hand each month, and a template left in DRAFT or with
|
|
21228
|
+
// auto-send off silently generates nothing.
|
|
21229
|
+
"xero.repeating_invoice.create",
|
|
21230
|
+
"xero.repeating_invoice.update"
|
|
21211
21231
|
];
|
|
21212
21232
|
var verbSchema = external_exports.enum(XERO_WRITE_VERBS).describe(
|
|
21213
|
-
"The Xero write operation to perform. One of: xero.payment.create (pay an invoice/bill \u2014 moves money), xero.bill.create (create a payable), xero.bill.approve (DRAFT/SUBMITTED \u2192 AUTHORISED), xero.invoice.void (irreversible), xero.invoice.update (edit line items / unit amounts / tax treatment on an existing invoice or bill), xero.invoice.create (raise an ACCREC customer/sales invoice - `reference` is REQUIRED and is the idempotency key), xero.contact.merge (irreversible), xero.bank_transaction.delete (irreversible), xero.manual_journal.create (post a multi-line GL entry \u2014 reclasses, accruals, corrections; debits must equal credits), xero.item.create (add an inventory item to the catalogue - `code` is REQUIRED and is the idempotency key), xero.item.update (edit an existing inventory item - only the fields you send change)."
|
|
21233
|
+
"The Xero write operation to perform. One of: xero.payment.create (pay an invoice/bill \u2014 moves money), xero.bill.create (create a payable), xero.bill.approve (DRAFT/SUBMITTED \u2192 AUTHORISED), xero.invoice.void (irreversible), xero.invoice.update (edit line items / unit amounts / tax treatment on an existing invoice or bill), xero.invoice.create (raise an ACCREC customer/sales invoice - `reference` is REQUIRED and is the idempotency key), xero.contact.merge (irreversible), xero.bank_transaction.delete (irreversible), xero.manual_journal.create (post a multi-line GL entry \u2014 reclasses, accruals, corrections; debits must equal credits), xero.item.create (add an inventory item to the catalogue - `code` is REQUIRED and is the idempotency key), xero.item.update (edit an existing inventory item - only the fields you send change), xero.repeating_invoice.create (create an ACCREC repeating invoice template that raises an invoice every week/month - `reference` is REQUIRED and is the idempotency key), xero.repeating_invoice.update (change the template status DRAFT/AUTHORISED, reference, end date or auto-send settings - never deletes, never changes lines)."
|
|
21214
21234
|
);
|
|
21215
21235
|
var payloadSchema = external_exports.object({
|
|
21216
21236
|
tenant_id: external_exports.string().min(1).max(128).describe(
|
|
21217
21237
|
"Xero organisation id (the Xero-Tenant-Id header value) the write lands in. NOT the Augmented team. Required \u2014 a missing tenant risks writing to the wrong set of books."
|
|
21218
21238
|
)
|
|
21219
21239
|
}).passthrough().describe(
|
|
21220
|
-
"Verb-specific fields. xero.payment.create: { invoice_id, account_id, amount, currency?, counterparty? }. xero.bill.create: { contact_id, amount, currency?, counterparty?, line_items_preview, line_items?: [{ description, unit_amount, quantity?, account_code?, tax_type? }], line_amount_types? (Exclusive|Inclusive|NoTax), reference?, date? (YYYY-MM-DD), due_date? (YYYY-MM-DD), account_code? } \u2014 `amount` MUST equal the sum of unit_amount x quantity across line_items (the approval card shows `amount`, so a mismatch is refused rather than silently rewritten); `currency` is sent to Xero as CurrencyCode. Send line_items to raise a CODED payable in ONE approval; omit it and the bill is a single synthesized line from line_items_preview + amount, which posts as an uncoded lump dated today. line_amount_types matters: Xero defaults to Exclusive, so a GST-INCLUSIVE total sent without it is read as a subtotal and the bill total comes out wrong. xero.bill.approve: { invoice_id, amount?, currency?, counterparty? }. xero.invoice.void: { invoice_id, invoice_number?, amount?, counterparty? }. xero.invoice.create: { contact_id, reference (REQUIRED - it is the idempotency key; must not contain a double quote or backslash), line_items: [{ description, unit_amount, quantity?, account_code?, tax_type?, item_code? }], line_amount_types? (Exclusive|Inclusive|NoTax, default Exclusive), date? (YYYY-MM-DD), due_date? (YYYY-MM-DD), status? (DRAFT|AUTHORISED, default DRAFT), amount?, currency?, counterparty? } \u2014 raises an ACCREC customer/sales invoice. `reference` is required because a duplicate ACCREC invoice is a second bill against a customer: the executor reconciles on it before creating, so a retry adopts the existing invoice instead of raising another. AUTHORISED is the status a customer can be sent, so it must be asked for rather than inherited. `amount` MUST equal the sum of unit_amount x quantity across line_items - the tax-EXCLUSIVE SUBTOTAL under the default Exclusive treatment, the total under Inclusive/NoTax - because the approval card shows `amount` and a mismatch is REFUSED rather than silently rewritten. Do not send the gross tax-inclusive figure on an Exclusive invoice; it cannot be verified and it will be refused. xero.invoice.update: { invoice_id, line_items?: [{ description, unit_amount, quantity?, account_code?, tax_type?, item_code? }], line_amount_types? (Exclusive|Inclusive|NoTax), reference?, date?, due_date?, invoice_number?, counterparty?, amount?, currency? } \u2014 WARNING: line_items REPLACES the entire line set on the invoice, so send every line the invoice should end up with, EACH WITH ITS item_code where the org tracks inventory \u2014 a resent line without its item_code loses the item association, so a one-line correction can silently strip inventory tracking off the whole invoice; omit the field entirely to leave the lines untouched. Use line_amount_types to fix a wrong tax treatment (e.g. a GST-inclusive total mistakenly entered on a Tax Exclusive line). `amount` here is the DISPLAY-ONLY NEW TOTAL shown on the card and is NOT checked against line_items - unlike xero.invoice.create and xero.bill.create - because it is the gross figure and the gross depends on tax rates this payload does not carry. Said explicitly so the absence of a check is a documented decision rather than something to be read as a gap. xero.contact.merge: { source_contact_id, target_contact_id, source_name?, target_name? }. xero.bank_transaction.delete: { bank_transaction_id, amount?, counterparty? }. xero.manual_journal.create: { narration, journal_lines: [{ account_code, line_amount (positive=debit, negative=credit), description?, tax_type? }], date? (YYYY-MM-DD), status? (DRAFT|POSTED, default DRAFT), amount?, currency? } \u2014 lines must balance to zero. xero.item.create: { code (REQUIRED, max 30 chars, unique per organisation, no double quote or backslash - the idempotency key), name (REQUIRED, max 50 chars), description? (sales description), purchase_description?, is_sold?, is_purchased?, sales_details?: { unit_price?, account_code?, tax_type? }, purchase_details?: { unit_price?, account_code?, cogs_account_code?, tax_type? }, is_tracked_as_inventory?, inventory_asset_account_code? } \u2014 adds an item invoice and bill lines can reference by item_code. The executor looks `code` up first: an existing item with the same code, the same name AND every field you set is adopted (a retry); an existing item with the same code but a different name, or with different tracking/accounts/prices, is refused (pick another code, or use xero.item.update on that item). A TRACKED item (is_tracked_as_inventory: true) REQUIRES inventory_asset_account_code AND purchase_details.cogs_account_code, or Xero rejects it. xero.item.update: { item_id (REQUIRED, the Xero ItemID from list-items; the approval card identifies the item by this id), code? (renames the code - refused if another item already uses it), name?, description?, purchase_description?, is_sold?, is_purchased?, sales_details?, purchase_details?, is_tracked_as_inventory?, inventory_asset_account_code? } \u2014 only the fields you send change; details blocks merge field by field, so sending sales_details: { unit_price } keeps the existing sales account. Always include tenant_id."
|
|
21240
|
+
"Verb-specific fields. xero.payment.create: { invoice_id, account_id, amount, currency?, counterparty? }. xero.bill.create: { contact_id, amount, currency?, counterparty?, line_items_preview, line_items?: [{ description, unit_amount, quantity?, account_code?, tax_type? }], line_amount_types? (Exclusive|Inclusive|NoTax), reference?, date? (YYYY-MM-DD), due_date? (YYYY-MM-DD), account_code? } \u2014 `amount` MUST equal the sum of unit_amount x quantity across line_items (the approval card shows `amount`, so a mismatch is refused rather than silently rewritten); `currency` is sent to Xero as CurrencyCode. Send line_items to raise a CODED payable in ONE approval; omit it and the bill is a single synthesized line from line_items_preview + amount, which posts as an uncoded lump dated today. line_amount_types matters: Xero defaults to Exclusive, so a GST-INCLUSIVE total sent without it is read as a subtotal and the bill total comes out wrong. xero.bill.approve: { invoice_id, amount?, currency?, counterparty? }. xero.invoice.void: { invoice_id, invoice_number?, amount?, counterparty? }. xero.invoice.create: { contact_id, reference (REQUIRED - it is the idempotency key; must not contain a double quote or backslash), line_items: [{ description, unit_amount, quantity?, account_code?, tax_type?, item_code? }], line_amount_types? (Exclusive|Inclusive|NoTax, default Exclusive), date? (YYYY-MM-DD), due_date? (YYYY-MM-DD), status? (DRAFT|AUTHORISED, default DRAFT), amount?, currency?, counterparty? } \u2014 raises an ACCREC customer/sales invoice. `reference` is required because a duplicate ACCREC invoice is a second bill against a customer: the executor reconciles on it before creating, so a retry adopts the existing invoice instead of raising another. AUTHORISED is the status a customer can be sent, so it must be asked for rather than inherited. `amount` MUST equal the sum of unit_amount x quantity across line_items - the tax-EXCLUSIVE SUBTOTAL under the default Exclusive treatment, the total under Inclusive/NoTax - because the approval card shows `amount` and a mismatch is REFUSED rather than silently rewritten. Do not send the gross tax-inclusive figure on an Exclusive invoice; it cannot be verified and it will be refused. xero.invoice.update: { invoice_id, line_items?: [{ description, unit_amount, quantity?, account_code?, tax_type?, item_code? }], line_amount_types? (Exclusive|Inclusive|NoTax), reference?, date?, due_date?, invoice_number?, counterparty?, amount?, currency? } \u2014 WARNING: line_items REPLACES the entire line set on the invoice, so send every line the invoice should end up with, EACH WITH ITS item_code where the org tracks inventory \u2014 a resent line without its item_code loses the item association, so a one-line correction can silently strip inventory tracking off the whole invoice; omit the field entirely to leave the lines untouched. Use line_amount_types to fix a wrong tax treatment (e.g. a GST-inclusive total mistakenly entered on a Tax Exclusive line). `amount` here is the DISPLAY-ONLY NEW TOTAL shown on the card and is NOT checked against line_items - unlike xero.invoice.create and xero.bill.create - because it is the gross figure and the gross depends on tax rates this payload does not carry. Said explicitly so the absence of a check is a documented decision rather than something to be read as a gap. xero.contact.merge: { source_contact_id, target_contact_id, source_name?, target_name? }. xero.bank_transaction.delete: { bank_transaction_id, amount?, counterparty? }. xero.manual_journal.create: { narration, journal_lines: [{ account_code, line_amount (positive=debit, negative=credit), description?, tax_type? }], date? (YYYY-MM-DD), status? (DRAFT|POSTED, default DRAFT), amount?, currency? } \u2014 lines must balance to zero. xero.item.create: { code (REQUIRED, max 30 chars, unique per organisation, no double quote or backslash - the idempotency key), name (REQUIRED, max 50 chars), description? (sales description), purchase_description?, is_sold?, is_purchased?, sales_details?: { unit_price?, account_code?, tax_type? }, purchase_details?: { unit_price?, account_code?, cogs_account_code?, tax_type? }, is_tracked_as_inventory?, inventory_asset_account_code? } \u2014 adds an item invoice and bill lines can reference by item_code. The executor looks `code` up first: an existing item with the same code, the same name AND every field you set is adopted (a retry); an existing item with the same code but a different name, or with different tracking/accounts/prices, is refused (pick another code, or use xero.item.update on that item). A TRACKED item (is_tracked_as_inventory: true) REQUIRES inventory_asset_account_code AND purchase_details.cogs_account_code, or Xero rejects it. xero.item.update: { item_id (REQUIRED, the Xero ItemID from list-items; the approval card identifies the item by this id), code? (renames the code - refused if another item already uses it), name?, description?, purchase_description?, is_sold?, is_purchased?, sales_details?, purchase_details?, is_tracked_as_inventory?, inventory_asset_account_code? } \u2014 only the fields you send change; details blocks merge field by field, so sending sales_details: { unit_price } keeps the existing sales account. xero.repeating_invoice.create: { contact_id, reference (REQUIRED - the idempotency key; no double quote or backslash), schedule: { period (1 = every month/week), unit (WEEKLY|MONTHLY), due_date (number, read through due_date_type), due_date_type (DAYSAFTERBILLDATE|DAYSAFTERBILLMONTH|DAYSAFTERINVOICEDATE|DAYSAFTERINVOICEMONTH|OFCURRENTMONTH|OFFOLLOWINGMONTH), start_date (YYYY-MM-DD, the FIRST invoice date; its day of month is the day every invoice is dated), end_date? }, line_items: [{ description, unit_amount, quantity?, account_code?, tax_type?, item_code? }], line_amount_types? (default Exclusive), status? (DRAFT|AUTHORISED, default DRAFT - a DRAFT template generates NOTHING), approved_for_sending? (true = Xero EMAILS each generated invoice to the customer unattended; only takes effect when AUTHORISED), send_copy?, include_pdf?, mark_as_sent?, amount? (the PER-INVOICE subtotal; refused if it does not match the lines), currency?, counterparty? } - e.g. $2,000 + GST on the 4th of each month: unit MONTHLY, period 1, start_date the next 4th, line_amount_types Exclusive, one line unit_amount 2000 with the GST tax_type. A template already carrying the reference is adopted ONLY if it is identical to this request (a retry; a duplicate template bills twice every period); one that differs in anything (contact, status, lines, schedule, send settings) is refused - to activate or change an EXISTING template use xero.repeating_invoice.update, not create. xero.repeating_invoice.update: { repeating_invoice_id (the RepeatingInvoiceID from list-repeating-invoices), status? (DRAFT|AUTHORISED only), reference?, end_date?, approved_for_sending?, send_copy?, include_pdf?, mark_as_sent?, counterparty? } - send only what changes; line_items, the schedule (other than end_date) and deletion are NOT supported: create a new template instead. Always include tenant_id."
|
|
21221
21241
|
);
|
|
21222
21242
|
var reasonSchema = external_exports.string().min(1).max(2e3).describe(
|
|
21223
21243
|
"Why the agent needs to perform this write. Surfaced verbatim on the approval card for the human reviewer. Mandatory."
|
|
@@ -21255,7 +21275,7 @@ var awaitDecisionSchema = external_exports.object({
|
|
|
21255
21275
|
// package.json
|
|
21256
21276
|
var package_default = {
|
|
21257
21277
|
name: "@integrity-labs/xero-broker",
|
|
21258
|
-
version: "0.1.
|
|
21278
|
+
version: "0.1.10",
|
|
21259
21279
|
description: "Xero Broker \u2014 MCP server that routes money- and ledger-touching Xero writes through approval-core (HITL). Sibling to cloud-broker / channel-broker; the only write path for an agent once the vendor xero-mcp-server's write tools are stripped. ENG-4922.",
|
|
21260
21280
|
type: "module",
|
|
21261
21281
|
bin: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@integrity-labs/xero-broker",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Xero Broker — MCP server that routes money- and ledger-touching Xero writes through approval-core (HITL). Sibling to cloud-broker / channel-broker; the only write path for an agent once the vendor xero-mcp-server's write tools are stripped. ENG-4922.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|