@ekwo-ai/mcp 0.4.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/README.md +253 -0
- package/dist/backend.d.ts +24 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +105 -0
- package/dist/backend.js.map +1 -0
- package/dist/bin.d.ts +15 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +75 -0
- package/dist/bin.js.map +1 -0
- package/dist/columns.d.ts +59 -0
- package/dist/columns.d.ts.map +1 -0
- package/dist/columns.js +422 -0
- package/dist/columns.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +102 -0
- package/dist/config.js.map +1 -0
- package/dist/format.d.ts +21 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +49 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/postgrest.d.ts +35 -0
- package/dist/postgrest.d.ts.map +1 -0
- package/dist/postgrest.js +152 -0
- package/dist/postgrest.js.map +1 -0
- package/dist/rounding.d.ts +25 -0
- package/dist/rounding.d.ts.map +1 -0
- package/dist/rounding.js +35 -0
- package/dist/rounding.js.map +1 -0
- package/dist/schema.d.ts +28 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +51 -0
- package/dist/schema.js.map +1 -0
- package/dist/server.d.ts +33 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +521 -0
- package/dist/server.js.map +1 -0
- package/dist/sql.d.ts +36 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +214 -0
- package/dist/sql.js.map +1 -0
- package/dist/tools/modules.d.ts +61 -0
- package/dist/tools/modules.d.ts.map +1 -0
- package/dist/tools/modules.js +335 -0
- package/dist/tools/modules.js.map +1 -0
- package/dist/tools/read.d.ts +233 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +853 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/write.d.ts +343 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +814 -0
- package/dist/tools/write.js.map +1 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# @ekwo-ai/mcp
|
|
2
|
+
|
|
3
|
+
The [Model Context Protocol](https://modelcontextprotocol.io) server for
|
|
4
|
+
[Ekwo OS](https://github.com/Ekwo-ai/ekwo-os). It lets an AI assistant keep the
|
|
5
|
+
books in your own Postgres: read the ledger, raise an invoice, post it, match
|
|
6
|
+
a payment, pull the VAT return or the French FEC — **as you**, under the row
|
|
7
|
+
level security of your own installation.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npx @ekwo-ai/mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
It speaks MCP over stdio and is started by a client, never by hand.
|
|
14
|
+
|
|
15
|
+
## What it is, and what it is not
|
|
16
|
+
|
|
17
|
+
The server holds no privileges of its own. It signs in as the person using it,
|
|
18
|
+
or is handed their access token, and everything it can do afterwards is
|
|
19
|
+
exactly what that person can do: a viewer reads and cannot write, a member of
|
|
20
|
+
one company cannot see another, a locked period refuses a posting. None of
|
|
21
|
+
that is checked in this package — the policies and the triggers in the schema
|
|
22
|
+
decide, and this server reports what they answered.
|
|
23
|
+
|
|
24
|
+
Three things it will never do:
|
|
25
|
+
|
|
26
|
+
- **Write a ledger line.** Every entry comes out of `post_document`,
|
|
27
|
+
`post_payment`, `post_entry` or `reconcile`, which carry the accounting
|
|
28
|
+
rules. Direct inserts are for the objects a person types: contacts, draft
|
|
29
|
+
documents and their lines, payments, bank transactions.
|
|
30
|
+
- **Delete or edit a posted entry.** There is no unpost, and no tool that
|
|
31
|
+
removes one. A mistake is corrected with a credit note, which is how
|
|
32
|
+
accounting has always worked. `unreconcile` is the only undo here, and
|
|
33
|
+
matching changes no account.
|
|
34
|
+
- **Use a `service_role` key.** It would work, and that is the objection: it
|
|
35
|
+
bypasses every policy, so the assistant would answer for companies its user
|
|
36
|
+
was never invited to. The server refuses to start with one.
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
### The recommended route: PostgREST, as the signed-in user
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"ekwo": {
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["-y", "@ekwo-ai/mcp"],
|
|
48
|
+
"env": {
|
|
49
|
+
"SUPABASE_URL": "https://YOURREF.supabase.co",
|
|
50
|
+
"SUPABASE_ANON_KEY": "your anon (publishable) key",
|
|
51
|
+
"EKWO_EMAIL": "you@example.com",
|
|
52
|
+
"EKWO_PASSWORD": "your password"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
That block goes in `claude_desktop_config.json` for Claude Desktop, or in
|
|
60
|
+
`.mcp.json` at the root of a project for Claude Code. `EKWO_ACCESS_TOKEN`
|
|
61
|
+
replaces the address and the password when you already hold a session; with
|
|
62
|
+
the password, the session is kept in memory and refreshed, and nothing is
|
|
63
|
+
written to disk.
|
|
64
|
+
|
|
65
|
+
### The fallback: a direct Postgres connection
|
|
66
|
+
|
|
67
|
+
For a self-hosted installation with no PostgREST in front of the database, or
|
|
68
|
+
for tests.
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"env": {
|
|
73
|
+
"EKWO_DB_URL": "postgresql://…",
|
|
74
|
+
"EKWO_ACT_AS_USER_ID": "the auth.users id this server acts for"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`EKWO_ACT_AS_USER_ID` is **required**, and that is the whole point of this
|
|
80
|
+
mode. A database connection is nobody: `auth.uid()` is null, row level
|
|
81
|
+
security is bypassed rather than satisfied, and a server running that way
|
|
82
|
+
would be a way round the policies rather than a client of them. So every
|
|
83
|
+
query runs inside a transaction that sets `request.jwt.claims` to that user
|
|
84
|
+
and switches to the `authenticated` role, and the policies bind exactly as
|
|
85
|
+
they do over the API. This mode needs the `postgres` package installed
|
|
86
|
+
alongside the server; the recommended route needs no driver at all.
|
|
87
|
+
|
|
88
|
+
| Variable | Meaning |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `SUPABASE_URL` | `https://<ref>.supabase.co` |
|
|
91
|
+
| `SUPABASE_ANON_KEY` | The anon (publishable) key. A `service_role` key is refused. |
|
|
92
|
+
| `EKWO_EMAIL` / `EKWO_PASSWORD` | The user this assistant acts as |
|
|
93
|
+
| `EKWO_ACCESS_TOKEN` | A session already in hand, instead of the two above |
|
|
94
|
+
| `EKWO_DB_URL` | A direct Postgres connection, for a self-hosted installation |
|
|
95
|
+
| `EKWO_ACT_AS_USER_ID` | Required with `EKWO_DB_URL`: the `auth.users` id to act for |
|
|
96
|
+
|
|
97
|
+
## The tools
|
|
98
|
+
|
|
99
|
+
Every write names its company explicitly.
|
|
100
|
+
|
|
101
|
+
| Tool | What it does |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `list_companies` | The companies you are a member of, with your role on each |
|
|
104
|
+
| `get_company` | Financial years, lock dates, journals, default accounts |
|
|
105
|
+
| `list_accounts` | The accounts a company works with, by code prefix, type or name. `include_all` for the whole chart |
|
|
106
|
+
| `search_contacts` | Customers and suppliers, by name, type or VAT number |
|
|
107
|
+
| `search_products` | The catalogue: code, unit, price, account and tax of what is sold and bought |
|
|
108
|
+
| `list_documents` | Invoices, credit notes and quotes, filtered |
|
|
109
|
+
| `get_document` | One document with its lines and the entry it produced |
|
|
110
|
+
| `list_bank_accounts` | The bank accounts of a company, with the journal and ledger account behind each |
|
|
111
|
+
| `list_bank_transactions` | Statement lines, pending by default |
|
|
112
|
+
| `trial_balance` | Opening, movements and closing per account |
|
|
113
|
+
| `general_ledger` | Every posted line of an account, with a running balance |
|
|
114
|
+
| `aged_balance` | What is still owed, bucketed by age, read from the ledger |
|
|
115
|
+
| `vat_return` | The boxes for a period, summed from the ledger |
|
|
116
|
+
| `ec_sales_list` | The recapitulative statement of intra-Community supplies: one line per customer VAT number and per nature |
|
|
117
|
+
| `portfolio_upcoming_filings` | *Portfolio* = the companies you may read: for an accounting firm, its clients ([`docs/firms.md`](../../docs/firms.md)). What falls due between two dates in every company you hold `filings.read` on. One row per company at least: a pack that names no deadline is listed without a date, and says so |
|
|
118
|
+
| `portfolio_filings_touched_since` | Declarations that have gone and whose period received entries afterwards, across the same companies, with the company named |
|
|
119
|
+
| `list_statements` / `financial_statement` | The schemes a company can be presented on, and one statement |
|
|
120
|
+
| `generate_fec` | The French FEC as text, with its checks and its filename |
|
|
121
|
+
| `read_audit_log` | Who changed what and when: the configuration of a company, and the acts that change a state. Append-only; nothing writes it |
|
|
122
|
+
| `get_preferences` | What you prefer, and the language chain to read labels with |
|
|
123
|
+
| `list_invitations` | Who has been invited into a company and not yet joined |
|
|
124
|
+
| `list_api_keys` | The machine keys of a company, and what each may do |
|
|
125
|
+
| `describe_pack` | Which country packs this installation holds: their version, how much anyone has read them, and the register of texts each was built from — title, official publisher, link and the day it was opened |
|
|
126
|
+
| `status` | Schema version, instance, connection, companies |
|
|
127
|
+
| `create_contact` | A customer, supplier or other third party |
|
|
128
|
+
| `create_product` | A catalogue row: code, name, unit, price, account, tax |
|
|
129
|
+
| `update_product` | Changes one, or retires it with `active: false` |
|
|
130
|
+
| `pin_accounts` | Adds accounts to the working chart a company sees first, or takes one back out with `pinned: false` |
|
|
131
|
+
| `create_document` | A draft invoice, credit note or quote, with its lines. With `client_ref`, calling twice creates once |
|
|
132
|
+
| `update_document_lines` | Replaces the lines of a **draft** |
|
|
133
|
+
| `post_document` | Books it. Cannot be undone. `dry_run: true` returns the entry the database would write, and writes nothing |
|
|
134
|
+
| `record_payment` | Books money in or out and matches it against open invoices — or, with `document_id`, against that document alone, which then names the contact and the direction. With `client_ref`, recording twice records once |
|
|
135
|
+
| `reconcile` / `unreconcile` | Matches two ledger lines, or undoes one matching |
|
|
136
|
+
| `create_bank_account` | Registers an account from its IBAN and wires it to the bank journal. Running it twice with the same IBAN creates nothing |
|
|
137
|
+
| `create_bank_transaction` | One statement line by hand, for an installation with no feed |
|
|
138
|
+
| `import_bank_statement` | A statement file (`camt.053`, `coda`, `cfonb120`) into statements and pending lines. Books nothing; the same file twice creates nothing; an unknown account or a statement that does not add up is refused by name, a missing statement is signalled |
|
|
139
|
+
| `lock_period` | Moves the accounting and VAT lock dates. Needs `company.write`. |
|
|
140
|
+
| `opening_balance` | The trial balance of whatever kept the books before, as the opening entry |
|
|
141
|
+
| `close_fiscal_year` / `reopen_fiscal_year` | Closes a year the way the country pack says, or reverses a close run too early |
|
|
142
|
+
| `create_company` | A company on a country pack, with its chart and its first financial year. An instance-level act |
|
|
143
|
+
| `update_company_profile` | What a company says about itself on its documents |
|
|
144
|
+
| `set_preferences` | Your own language, timezone, formats and default company |
|
|
145
|
+
| `invite_member` / `revoke_invitation` | Invites an address into a company, or withdraws the invitation. The token is shown once |
|
|
146
|
+
| `create_api_key` / `revoke_api_key` | A key for a machine, scoped to one company and a list of capabilities |
|
|
147
|
+
|
|
148
|
+
**`list_accounts` answers with the working chart, not the whole one.** A
|
|
149
|
+
country pack transcribes the regulation — 353 accounts in Belgium, 1 026 in
|
|
150
|
+
Luxembourg — and a company works with a few dozen of them, so the default is
|
|
151
|
+
what `accounts_in_use()` returns: the accounts carrying posted entries, those
|
|
152
|
+
the company's own settings or an enabled module point at, and those somebody
|
|
153
|
+
pinned, minus the deprecated ones. Every answer carries a `scope` field saying
|
|
154
|
+
which it used. `in_use_from` and `in_use_to` narrow the movements to a period;
|
|
155
|
+
`include_all` returns the whole chart; `include_deprecated` returns it with the
|
|
156
|
+
retired accounts too; and `ekwo://companies/{id}/chart` was already the
|
|
157
|
+
resource that carries everything. None of this restricts anything: a document
|
|
158
|
+
line may name any account of the chart that is not deprecated, and every write
|
|
159
|
+
tool still accepts one.
|
|
160
|
+
|
|
161
|
+
`post_document`, `record_payment`, `update_document_lines`, `unreconcile`,
|
|
162
|
+
`lock_period`, `opening_balance`, `close_fiscal_year`, `reopen_fiscal_year`,
|
|
163
|
+
`revoke_invitation` and `revoke_api_key` are annotated destructive in the
|
|
164
|
+
protocol, so a client can ask before calling them.
|
|
165
|
+
|
|
166
|
+
**What a tool may do is the capability the user holds**, not the tool's own
|
|
167
|
+
right: the server acts as the person it signed in as, so `post_document` works
|
|
168
|
+
for an accountant and is refused to a viewer, by the database, with the
|
|
169
|
+
database's own words. `get_company` returns `your_capabilities` for exactly
|
|
170
|
+
that reason.
|
|
171
|
+
|
|
172
|
+
**The modules.** A module of this installation gets its own tools, under the
|
|
173
|
+
prefix its `module.json` declares, and the server reads `public.modules` at
|
|
174
|
+
startup to know which: `assets_list`, `assets_create`, `assets_schedule`,
|
|
175
|
+
`assets_run_depreciation`, `assets_dispose`, `budgets_list`,
|
|
176
|
+
`budgets_upsert_lines`, `budgets_variance`. A module that is not installed is
|
|
177
|
+
not offered, because a tool a model cannot use is worse than a tool it cannot
|
|
178
|
+
see. PostgREST serves a module's schema only once the project exposes it, and
|
|
179
|
+
the refusal it answers with is a profile error that says nothing useful — so
|
|
180
|
+
every module tool turns it into the sentence that names the setting.
|
|
181
|
+
|
|
182
|
+
**Resources.** `ekwo://companies/{id}/chart` is the whole chart of accounts;
|
|
183
|
+
`ekwo://companies/{id}/taxes` is every tax with the ledger account and the
|
|
184
|
+
declaration box each of its postings feeds.
|
|
185
|
+
|
|
186
|
+
**Prompts.** `close_month` walks the month-end checklist — drafts, unmatched
|
|
187
|
+
bank lines, the balance, the VAT, what is still open. `prepare_vat_return`
|
|
188
|
+
pulls the boxes and ties them back to the ledger before anything is filed.
|
|
189
|
+
|
|
190
|
+
## Conventions
|
|
191
|
+
|
|
192
|
+
- **Amounts are decimal strings.** `"1210.00"`, never a float. They go in that
|
|
193
|
+
way and come back that way, because `numeric` is exact and a float is not.
|
|
194
|
+
- **Dates are ISO**, `2026-06-15`. Identifiers are uuids.
|
|
195
|
+
- **Totals are computed by the database.** `create_document` returns the draft
|
|
196
|
+
with the totals the schema derived, not with anything the caller supplied.
|
|
197
|
+
- **Refusals travel unchanged.** `period_locked:`, `entry_unbalanced:`,
|
|
198
|
+
`document_total_mismatch:` and the rest arrive with the message the database
|
|
199
|
+
raised, plus one sentence saying what it means. They are answers, not
|
|
200
|
+
obstacles to route around.
|
|
201
|
+
- **A product fills a line in and never constrains it.** A line naming
|
|
202
|
+
`product_code` takes the catalogue's text, description, unit, price, account
|
|
203
|
+
and tax; anything the line carries wins over that. What is already posted is
|
|
204
|
+
never touched when the catalogue changes, and a product referenced by a line
|
|
205
|
+
is retired with `active: false` rather than deleted.
|
|
206
|
+
- **A missing tax is a missing tax.** A line with no tax books a base with no
|
|
207
|
+
VAT box, which is not the same as 0 %. A missing *account* is different: it
|
|
208
|
+
can only mean "resolve it", because a product line with no account is
|
|
209
|
+
refused by a check constraint. So a line may leave `account_code` out, and
|
|
210
|
+
the database fills it — the company default, then the country model.
|
|
211
|
+
|
|
212
|
+
## Testing it by hand
|
|
213
|
+
|
|
214
|
+
The automated tests run every tool against the real schema in Postgres
|
|
215
|
+
compiled to WebAssembly (`tests/mcp/`), including the refusals. Two things
|
|
216
|
+
they cannot run: PostgREST and GoTrue. To exercise those, on a project you can
|
|
217
|
+
throw away:
|
|
218
|
+
|
|
219
|
+
```sh
|
|
220
|
+
npx ekwo init --country BE --org "Scratch" --company "Scratch BV" … # a real project
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Then point a client at it — in Claude Desktop, the JSON block above — and:
|
|
224
|
+
|
|
225
|
+
1. **"List my companies."** The company you created, with `your_role: owner`.
|
|
226
|
+
2. **"What are the journals and the lock dates?"** `get_company`.
|
|
227
|
+
3. **"Create a customer called Dumont, then invoice them 1 000 € plus 21 %
|
|
228
|
+
VAT for consulting."** `create_contact`, then `create_document`; the answer
|
|
229
|
+
carries `amount_total: "1210.00"` computed by the database.
|
|
230
|
+
Or with a catalogue: **"add a product CONS-JOUR, a consulting day at 500 €
|
|
231
|
+
on 704000 at 21 %, then invoice Dumont two of them"** — `create_product`,
|
|
232
|
+
then `create_document` with `product_code` and nothing else on the line.
|
|
233
|
+
4. **"Post it."** `post_document`. The entry books 704 / 451 / 400 and takes a
|
|
234
|
+
number like `SAL/2026/0001`.
|
|
235
|
+
5. **"They paid 500 € on the 10th."** `record_payment`, which books the bank
|
|
236
|
+
line and matches it; the invoice becomes partially paid.
|
|
237
|
+
6. **"Show me the trial balance and the VAT for the quarter."**
|
|
238
|
+
`trial_balance` and `vat_return`.
|
|
239
|
+
7. **"Lock June."** `lock_period`, then try to post something dated in June:
|
|
240
|
+
the refusal comes back as `period_locked:`.
|
|
241
|
+
|
|
242
|
+
A payment needs somewhere to book the bank side. On a company installed from a
|
|
243
|
+
country model the bank and cash journals already point at their account
|
|
244
|
+
(`550000` and `570000` in Belgium, `512000` and `530000` in France), so
|
|
245
|
+
`record_payment` works with nothing else set up. `create_bank_account` names
|
|
246
|
+
the real account — the IBAN is the one thing nobody can derive — and wires it
|
|
247
|
+
to the journal; `bank_account_id` on the payment then says which one the money
|
|
248
|
+
moved on, which is what you need with several accounts in one journal. Until a
|
|
249
|
+
company has one, `ekwo doctor` says so.
|
|
250
|
+
|
|
251
|
+
## Licence
|
|
252
|
+
|
|
253
|
+
[AGPL-3.0-only](../../LICENSE) © Ekwo AI.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one interface every tool is written against.
|
|
3
|
+
*
|
|
4
|
+
* There are two ways to reach an Ekwo database and neither of them is a
|
|
5
|
+
* privileged one. The recommended route is PostgREST with the signed-in
|
|
6
|
+
* user's own token, which is how the schema is meant to be read and written:
|
|
7
|
+
* row level security decides, exactly as it would for that person in a
|
|
8
|
+
* browser. The second is a direct Postgres connection, for a self-hosted
|
|
9
|
+
* installation and for the tests — and even there the claims and the role are
|
|
10
|
+
* set on every call, so the same policies apply.
|
|
11
|
+
*
|
|
12
|
+
* Tools therefore never see a client, a token or a connection string. They
|
|
13
|
+
* see `select`, `insert`, `update`, `remove` and `rpc`, and what comes back
|
|
14
|
+
* is whatever the policies let through.
|
|
15
|
+
*/
|
|
16
|
+
import { BooksError, columnName, identifier, qualified, socleCode, type Backend, type Filter, type Order, type Row, type SelectQuery, type Value } from '@ekwo-ai/core';
|
|
17
|
+
export { columnName, identifier, qualified, socleCode };
|
|
18
|
+
export type { Backend, Filter, Order, Row, SelectQuery, Value };
|
|
19
|
+
/** The core's error, under the name this package has always exported. */
|
|
20
|
+
export declare const EkwoMcpError: typeof BooksError;
|
|
21
|
+
export type EkwoMcpError = BooksError;
|
|
22
|
+
/** The socle error, with the sentence that explains it when we have one. */
|
|
23
|
+
export declare function explain(message: string): EkwoMcpError;
|
|
24
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,UAAU,EACV,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,KAAK,OAAO,EACZ,KAAK,MAAM,EACX,KAAK,KAAK,EACV,KAAK,GAAG,EACR,KAAK,WAAW,EAChB,KAAK,KAAK,EACX,MAAM,eAAe,CAAC;AAKvB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AACxD,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;AAEhE,yEAAyE;AACzE,eAAO,MAAM,YAAY,mBAAa,CAAC;AACvC,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC;AAuEtC,4EAA4E;AAC5E,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAcrD"}
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one interface every tool is written against.
|
|
3
|
+
*
|
|
4
|
+
* There are two ways to reach an Ekwo database and neither of them is a
|
|
5
|
+
* privileged one. The recommended route is PostgREST with the signed-in
|
|
6
|
+
* user's own token, which is how the schema is meant to be read and written:
|
|
7
|
+
* row level security decides, exactly as it would for that person in a
|
|
8
|
+
* browser. The second is a direct Postgres connection, for a self-hosted
|
|
9
|
+
* installation and for the tests — and even there the claims and the role are
|
|
10
|
+
* set on every call, so the same policies apply.
|
|
11
|
+
*
|
|
12
|
+
* Tools therefore never see a client, a token or a connection string. They
|
|
13
|
+
* see `select`, `insert`, `update`, `remove` and `rpc`, and what comes back
|
|
14
|
+
* is whatever the policies let through.
|
|
15
|
+
*/
|
|
16
|
+
import { BooksError, columnName, identifier, qualified, socleCode, } from '@ekwo-ai/core';
|
|
17
|
+
// Moved to the core, where the command line reads them too: what a refusal is
|
|
18
|
+
// called, what a backend is and what an error of this layer looks like cannot
|
|
19
|
+
// be decided twice. Still exported from here, under the names they always had.
|
|
20
|
+
export { columnName, identifier, qualified, socleCode };
|
|
21
|
+
/** The core's error, under the name this package has always exported. */
|
|
22
|
+
export const EkwoMcpError = BooksError;
|
|
23
|
+
/**
|
|
24
|
+
* What each socle refusal means, in one sentence.
|
|
25
|
+
*
|
|
26
|
+
* Only the codes a client of this server can actually provoke are listed. An
|
|
27
|
+
* unknown code is not a failure of this table: the raw message is already the
|
|
28
|
+
* answer, and the sentence is the part that was optional.
|
|
29
|
+
*/
|
|
30
|
+
const HINTS = {
|
|
31
|
+
period_locked: 'The company has an accounting lock date on or after this date. Ask the owner to move lock_date, or book on a later date.',
|
|
32
|
+
tax_period_locked: 'The company has a VAT lock date covering this date. Anything carrying a declaration box is frozen there.',
|
|
33
|
+
fiscal_year_closed: 'The financial year covering this date is closed. Reopen it, or book in an open year.',
|
|
34
|
+
entry_unbalanced: 'The debit and the credit of the entry differ. Nothing was posted.',
|
|
35
|
+
entry_empty: 'The entry has no lines.',
|
|
36
|
+
document_empty: 'The document has no billable line, so there is nothing to book.',
|
|
37
|
+
document_already_posted: 'This document has already been booked. Read it back rather than posting it twice.',
|
|
38
|
+
document_already_booked: 'This document already points at an entry. Read it back rather than posting it twice.',
|
|
39
|
+
document_cancelled: 'A cancelled document cannot be booked.',
|
|
40
|
+
entry_posted: 'A posted entry is immutable, in an open period too: its lines, its date, its state, and it is not deleted. Undo it with a reversal entry that names it in reversed_entry_id.',
|
|
41
|
+
entry_posted_by_hand: 'An entry becomes posted through post_entry, which draws its number from the journal counter, dates the posting and checks the period. Setting the state by hand is refused unless the row is exactly what post_entry would have written.',
|
|
42
|
+
document_posted_by_hand: 'A document becomes posted through post_document, which builds its entry. Setting the state by hand on an entry that was not built for this document is refused.',
|
|
43
|
+
entry_born_posted: 'An entry is created as a draft and posted with post_entry. One that is already posted arrives only with a company loaded from an archive, by import_company.',
|
|
44
|
+
document_posted: 'This document was issued, and what produced an entry does not change afterwards: not its lines, not its figures, not its state, and it is not deleted. Correct it with a credit note that names it in reversed_document_id, and issue another.',
|
|
45
|
+
document_born_posted: 'A document is created as a draft and posted with post_document. One that is already posted arrives only with a company loaded from an archive, by import_company.',
|
|
46
|
+
document_posted_without_entry: 'A document becomes posted through post_document, which builds its entry. Setting the state by hand is refused.',
|
|
47
|
+
document_amount_paid_is_derived: 'What a document was settled by comes from the matching of its entry. Match a payment or a credit note against it instead of writing the figure.',
|
|
48
|
+
document_payment_state_is_derived: 'The settlement state follows from what was matched against the document. It is never written.',
|
|
49
|
+
document_not_accountable: 'Quotes and purchase orders are not booked. Turn it into an invoice first.',
|
|
50
|
+
document_total_mismatch: 'The header total disagrees with what the lines book. The lines are right by construction, so the header is what needs fixing.',
|
|
51
|
+
tax_not_in_force: 'That tax is not applicable on the accounting date. Pick the tax in force for that period.',
|
|
52
|
+
unsupported_tax_amount_type: 'Only percentage taxes can be posted; a fixed-amount tax has no basis to spread.',
|
|
53
|
+
no_counterpart_account: 'No receivable or payable account is set, either on the contact or as a company default.',
|
|
54
|
+
no_journal: 'No journal was given and the company has no default for this kind of document.',
|
|
55
|
+
unknown_bank_account: 'The statement is of an account this company does not have, and an import never creates one: an account nobody decided is mapped to no journal. create_bank_account adds it; then import the file again.',
|
|
56
|
+
unbalanced_statement: 'The opening balance plus the booked lines is not the closing balance the bank declared, so a line is missing or altered. Nothing was imported; get the file again from the bank.',
|
|
57
|
+
statement_without_balances: 'Without an opening and a closing balance nothing proves the lines are all there. Nothing was imported.',
|
|
58
|
+
unreadable_statement_line: 'A booked line cannot be held as it is — no amount, no date, another currency than the account, or more decimals than the ledger keeps. Nothing was imported.',
|
|
59
|
+
statement_conflict: 'A statement with this identifier and date was already imported with other balances. The bank may have reissued it; nothing was changed.',
|
|
60
|
+
statement_currency_mismatch: 'The statement is in another currency than the bank account it belongs to. Nothing is converted on import.',
|
|
61
|
+
bank_account_mismatch: 'The bank account named is not the account the statement is of.',
|
|
62
|
+
no_bank_account: 'The payment names no bank account and its journal has no default account. create_bank_account adds one and wires it to the journal.',
|
|
63
|
+
missing_account: 'The line names no account, and the company and its country model have no default for this kind of document. Give account_code on the line, or set the company default.',
|
|
64
|
+
payment_already_booked: 'This payment already has an entry.',
|
|
65
|
+
payment_cancelled: 'A cancelled payment cannot be booked.',
|
|
66
|
+
reconcile_same_side: 'Matching pairs a debit with a credit; both lines are on the same side.',
|
|
67
|
+
reconcile_account_mismatch: 'The two lines are on different accounts.',
|
|
68
|
+
reconcile_over_debit: 'The amount is larger than what is still open on the debit line.',
|
|
69
|
+
reconcile_over_credit: 'The amount is larger than what is still open on the credit line.',
|
|
70
|
+
reconcile_nothing_left: 'Both lines are already fully matched.',
|
|
71
|
+
account_not_reconcilable: 'That account is not reconcilable, so nothing on it can be matched.',
|
|
72
|
+
unknown_document: 'No document with that id is visible to you.',
|
|
73
|
+
unknown_entry_line: 'No ledger line with that id is visible to you.',
|
|
74
|
+
unknown_reconciliation: 'No matching with that id is visible to you.',
|
|
75
|
+
unknown_payment: 'No payment with that id is visible to you.',
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Check constraints that a client can legitimately provoke, in the words a
|
|
79
|
+
* model can act on.
|
|
80
|
+
*
|
|
81
|
+
* Postgres names the constraint and nothing else — "violates check constraint
|
|
82
|
+
* document_lines_product_has_account" says which rule broke and not what to
|
|
83
|
+
* do. These few are the ones reachable through a tool, so they get the same
|
|
84
|
+
* shape as a socle raise: an identifier, then a sentence.
|
|
85
|
+
*/
|
|
86
|
+
const CONSTRAINTS = {
|
|
87
|
+
document_lines_product_has_account: 'missing_account: a line has no account, and neither the product, the company nor the country model supplies a default for this kind of document',
|
|
88
|
+
};
|
|
89
|
+
/** The socle error, with the sentence that explains it when we have one. */
|
|
90
|
+
export function explain(message) {
|
|
91
|
+
for (const [constraint, rewritten] of Object.entries(CONSTRAINTS)) {
|
|
92
|
+
if (message.includes(constraint)) {
|
|
93
|
+
const code = socleCode(rewritten);
|
|
94
|
+
const hint = code === undefined ? undefined : HINTS[code];
|
|
95
|
+
return new EkwoMcpError(rewritten, {
|
|
96
|
+
...(code !== undefined ? { code } : {}),
|
|
97
|
+
...(hint !== undefined ? { hint } : {}),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const code = socleCode(message);
|
|
102
|
+
const hint = code === undefined ? undefined : HINTS[code];
|
|
103
|
+
return new EkwoMcpError(message, { ...(code !== undefined ? { code } : {}), ...(hint !== undefined ? { hint } : {}) });
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `npx @ekwo-ai/mcp`, over stdio.
|
|
4
|
+
*
|
|
5
|
+
* Everything comes from the environment, because that is how an MCP client
|
|
6
|
+
* launches a server: a command and a block of variables in
|
|
7
|
+
* `claude_desktop_config.json` or `.mcp.json`. Nothing is read from a file
|
|
8
|
+
* and nothing is written to one — a credential cache would save one paste and
|
|
9
|
+
* turn the first accidental `git add .` into a disclosure.
|
|
10
|
+
*
|
|
11
|
+
* stdout belongs to the protocol. Anything this process has to say to a human
|
|
12
|
+
* goes to stderr, or the client sees a parse error instead of a server.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=bin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG"}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `npx @ekwo-ai/mcp`, over stdio.
|
|
4
|
+
*
|
|
5
|
+
* Everything comes from the environment, because that is how an MCP client
|
|
6
|
+
* launches a server: a command and a block of variables in
|
|
7
|
+
* `claude_desktop_config.json` or `.mcp.json`. Nothing is read from a file
|
|
8
|
+
* and nothing is written to one — a credential cache would save one paste and
|
|
9
|
+
* turn the first accidental `git add .` into a disclosure.
|
|
10
|
+
*
|
|
11
|
+
* stdout belongs to the protocol. Anything this process has to say to a human
|
|
12
|
+
* goes to stderr, or the client sees a parse error instead of a server.
|
|
13
|
+
*/
|
|
14
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
15
|
+
import { openBackend, readConfig } from './config.js';
|
|
16
|
+
import { assertSchemaSupported } from './schema.js';
|
|
17
|
+
import { SERVER_VERSION, buildServer } from './server.js';
|
|
18
|
+
import { installedModules } from './tools/modules.js';
|
|
19
|
+
async function main() {
|
|
20
|
+
const argument = process.argv[2];
|
|
21
|
+
if (argument === '--help' || argument === '-h') {
|
|
22
|
+
process.stderr.write(HELP);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (argument === '--version' || argument === '-v') {
|
|
26
|
+
process.stderr.write(`${SERVER_VERSION}\n`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const config = readConfig(process.env);
|
|
30
|
+
const backend = await openBackend(config);
|
|
31
|
+
// Before anything else: a database older than this server is refused by
|
|
32
|
+
// name rather than answered from wrong assumptions.
|
|
33
|
+
const schemaVersion = await assertSchemaSupported(backend);
|
|
34
|
+
// What this installation carries, from the registry table. A database that
|
|
35
|
+
// predates the module framework answers with nothing, and the socle's own
|
|
36
|
+
// tools are all a client then sees.
|
|
37
|
+
const modules = await installedModules(backend);
|
|
38
|
+
const server = buildServer(backend, { modules });
|
|
39
|
+
const shutdown = async () => {
|
|
40
|
+
await server.close().catch(() => { });
|
|
41
|
+
await backend.close().catch(() => { });
|
|
42
|
+
process.exit(0);
|
|
43
|
+
};
|
|
44
|
+
process.on('SIGINT', () => void shutdown());
|
|
45
|
+
process.on('SIGTERM', () => void shutdown());
|
|
46
|
+
await server.connect(new StdioServerTransport());
|
|
47
|
+
process.stderr.write(`ekwo-mcp: connected over ${config.mode === 'sql' ? 'a direct Postgres connection' : 'PostgREST as the signed-in user'}` +
|
|
48
|
+
`, schema ${schemaVersion}` +
|
|
49
|
+
`${modules.length > 0 ? `, modules: ${modules.join(', ')}` : ''}\n`);
|
|
50
|
+
}
|
|
51
|
+
const HELP = `ekwo-mcp — the Model Context Protocol server for Ekwo OS.
|
|
52
|
+
|
|
53
|
+
It speaks MCP over stdio and is started by a client, not by hand. Configure it
|
|
54
|
+
in claude_desktop_config.json or .mcp.json with:
|
|
55
|
+
|
|
56
|
+
SUPABASE_URL https://<ref>.supabase.co
|
|
57
|
+
SUPABASE_ANON_KEY the anon (publishable) key — never the service_role key
|
|
58
|
+
EKWO_EMAIL the user this assistant acts as
|
|
59
|
+
EKWO_PASSWORD their password
|
|
60
|
+
EKWO_ACCESS_TOKEN an access token, instead of the two above
|
|
61
|
+
|
|
62
|
+
For a self-hosted database, without PostgREST in front of it:
|
|
63
|
+
|
|
64
|
+
EKWO_DB_URL postgresql://…
|
|
65
|
+
EKWO_ACT_AS_USER_ID the auth.users id this server acts for — required,
|
|
66
|
+
because a database connection is nobody
|
|
67
|
+
|
|
68
|
+
Row level security applies either way. This server has no privileges of its
|
|
69
|
+
own and refuses a service_role key.
|
|
70
|
+
`;
|
|
71
|
+
main().catch((error) => {
|
|
72
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,KAAK,UAAU,IAAI;IACjB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,cAAc,IAAI,CAAC,CAAC;QAC5C,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,wEAAwE;IACxE,oDAAoD;IACpD,MAAM,aAAa,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC3D,2EAA2E;IAC3E,0EAA0E;IAC1E,oCAAoC;IACpC,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAE7C,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4BAA4B,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,iCAAiC,EAAE;QACtH,YAAY,aAAa,EAAE;QAC3B,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CACtE,CAAC;AACJ,CAAC;AAED,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;CAmBZ,CAAC;AAEF,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each tool reads back, written once.
|
|
3
|
+
*
|
|
4
|
+
* Two rules run through every list. A `numeric` column is asked for as
|
|
5
|
+
* `amount::text`, so the exact decimal arrives rather than a float; a `date`
|
|
6
|
+
* or a timestamp likewise, so `2026-06-15` is a string on both routes instead
|
|
7
|
+
* of whatever a driver decided a date object should be. Everything else —
|
|
8
|
+
* uuid, text, boolean, integer — crosses unchanged.
|
|
9
|
+
*/
|
|
10
|
+
export declare const COMPANY: string[];
|
|
11
|
+
export declare const FISCAL_YEAR: string[];
|
|
12
|
+
export declare const ACCOUNT: string[];
|
|
13
|
+
export declare const JOURNAL: string[];
|
|
14
|
+
export declare const CONTACT: string[];
|
|
15
|
+
export declare const TAX: string[];
|
|
16
|
+
export declare const DOCUMENT: string[];
|
|
17
|
+
/**
|
|
18
|
+
* The mentions the law of a country puts on one document, from the view that
|
|
19
|
+
* decides which of them apply. `text_i18n` crosses as it is: a renderer picks
|
|
20
|
+
* the language, and the server has no business choosing one.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DOCUMENT_LEGAL_MENTION: string[];
|
|
23
|
+
/**
|
|
24
|
+
* What the country of a document requires of it, beside the mentions: the
|
|
25
|
+
* payment term the law sets, how the number is built, when the tax falls due
|
|
26
|
+
* and how the document is exchanged. Read from `country_defaults`, which
|
|
27
|
+
* holds null wherever the pack has said nothing — and null is the answer, not
|
|
28
|
+
* an invitation to substitute another country's.
|
|
29
|
+
*/
|
|
30
|
+
export declare const COUNTRY_DOCUMENT_RULES: string[];
|
|
31
|
+
/**
|
|
32
|
+
* The header of a document, from the view that assembles it: the seller, the
|
|
33
|
+
* buyer, the amounts, where it is paid and what the country of the document
|
|
34
|
+
* requires. `document_line_items` is the lines and `document_legal_mentions`
|
|
35
|
+
* the sentences — three reads, and no client holding a copy of the
|
|
36
|
+
* letterhead.
|
|
37
|
+
*/
|
|
38
|
+
export declare const DOCUMENT_HEADER: string[];
|
|
39
|
+
export declare const PRODUCT: string[];
|
|
40
|
+
export declare const DOCUMENT_LINE: string[];
|
|
41
|
+
export declare const ENTRY: string[];
|
|
42
|
+
export declare const ENTRY_LINE: string[];
|
|
43
|
+
export declare const BANK_ACCOUNT: string[];
|
|
44
|
+
export declare const BANK_TRANSACTION: string[];
|
|
45
|
+
export declare const PAYMENT: string[];
|
|
46
|
+
/**
|
|
47
|
+
* An invitation, without its secret. `token_hash` is deliberately absent: it
|
|
48
|
+
* is of no use to a client, and a list of hashes is a thing to leak rather
|
|
49
|
+
* than a thing to show.
|
|
50
|
+
*/
|
|
51
|
+
/** What one person chose for themselves. Null everywhere is the fresh state. */
|
|
52
|
+
export declare const USER_PREFERENCES: string[];
|
|
53
|
+
export declare const INVITATION: string[];
|
|
54
|
+
/** A machine key, without its hash. The secret is returned once, elsewhere. */
|
|
55
|
+
export declare const API_KEY: string[];
|
|
56
|
+
export declare const INSTANCE: string[];
|
|
57
|
+
export declare const RECONCILIATION: string[];
|
|
58
|
+
export declare const AUDIT_LOG: string[];
|
|
59
|
+
//# sourceMappingURL=columns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../src/columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,OAAO,UAqCnB,CAAC;AAEF,eAAO,MAAM,WAAW,UAAoE,CAAC;AAE7F,eAAO,MAAM,OAAO,UASnB,CAAC;AAEF,eAAO,MAAM,OAAO,UAAmD,CAAC;AAExE,eAAO,MAAM,OAAO,UAYnB,CAAC;AAEF,eAAO,MAAM,GAAG,UAsBf,CAAC;AAEF,eAAO,MAAM,QAAQ,UAqBpB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,UAOlC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,UAWlC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,UAmE3B,CAAC;AAEF,eAAO,MAAM,OAAO,UAgBnB,CAAC;AAEF,eAAO,MAAM,aAAa,UAiBzB,CAAC;AAEF,eAAO,MAAM,KAAK,UAejB,CAAC;AAEF,eAAO,MAAM,UAAU,UAsBtB,CAAC;AAEF,eAAO,MAAM,YAAY,UAUxB,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAiB5B,CAAC;AAEF,eAAO,MAAM,OAAO,UAcnB,CAAC;AAEF;;;;GAIG;AACH,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,UAS5B,CAAC;AAEF,eAAO,MAAM,UAAU,UAYtB,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,OAAO,UAWnB,CAAC;AAEF,eAAO,MAAM,QAAQ,UAQpB,CAAC;AAEF,eAAO,MAAM,cAAc,UAa1B,CAAC;AAEF,eAAO,MAAM,SAAS,UAarB,CAAC"}
|