noxctl 0.1.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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +326 -0
  3. package/dist/auth.d.ts +29 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +267 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +1012 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/credentials-store.d.ts +4 -0
  12. package/dist/credentials-store.d.ts.map +1 -0
  13. package/dist/credentials-store.js +223 -0
  14. package/dist/credentials-store.js.map +1 -0
  15. package/dist/formatter.d.ts +62 -0
  16. package/dist/formatter.d.ts.map +1 -0
  17. package/dist/formatter.js +200 -0
  18. package/dist/formatter.js.map +1 -0
  19. package/dist/fortnox-client.d.ts +27 -0
  20. package/dist/fortnox-client.d.ts.map +1 -0
  21. package/dist/fortnox-client.js +178 -0
  22. package/dist/fortnox-client.js.map +1 -0
  23. package/dist/identifiers.d.ts +4 -0
  24. package/dist/identifiers.d.ts.map +1 -0
  25. package/dist/identifiers.js +16 -0
  26. package/dist/identifiers.js.map +1 -0
  27. package/dist/index.d.ts +4 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +43 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/operations/accounts.d.ts +18 -0
  32. package/dist/operations/accounts.d.ts.map +1 -0
  33. package/dist/operations/accounts.js +28 -0
  34. package/dist/operations/accounts.js.map +1 -0
  35. package/dist/operations/articles.d.ts +20 -0
  36. package/dist/operations/articles.d.ts.map +1 -0
  37. package/dist/operations/articles.js +36 -0
  38. package/dist/operations/articles.js.map +1 -0
  39. package/dist/operations/company.d.ts +2 -0
  40. package/dist/operations/company.d.ts.map +1 -0
  41. package/dist/operations/company.js +6 -0
  42. package/dist/operations/company.js.map +1 -0
  43. package/dist/operations/customers.d.ts +20 -0
  44. package/dist/operations/customers.d.ts.map +1 -0
  45. package/dist/operations/customers.js +37 -0
  46. package/dist/operations/customers.js.map +1 -0
  47. package/dist/operations/financial-reports.d.ts +37 -0
  48. package/dist/operations/financial-reports.d.ts.map +1 -0
  49. package/dist/operations/financial-reports.js +193 -0
  50. package/dist/operations/financial-reports.js.map +1 -0
  51. package/dist/operations/invoices.d.ts +32 -0
  52. package/dist/operations/invoices.d.ts.map +1 -0
  53. package/dist/operations/invoices.js +74 -0
  54. package/dist/operations/invoices.js.map +1 -0
  55. package/dist/operations/supplier-invoices.d.ts +23 -0
  56. package/dist/operations/supplier-invoices.d.ts.map +1 -0
  57. package/dist/operations/supplier-invoices.js +36 -0
  58. package/dist/operations/supplier-invoices.js.map +1 -0
  59. package/dist/operations/suppliers.d.ts +20 -0
  60. package/dist/operations/suppliers.d.ts.map +1 -0
  61. package/dist/operations/suppliers.js +36 -0
  62. package/dist/operations/suppliers.js.map +1 -0
  63. package/dist/operations/tax.d.ts +26 -0
  64. package/dist/operations/tax.d.ts.map +1 -0
  65. package/dist/operations/tax.js +47 -0
  66. package/dist/operations/tax.js.map +1 -0
  67. package/dist/operations/vouchers.d.ts +22 -0
  68. package/dist/operations/vouchers.d.ts.map +1 -0
  69. package/dist/operations/vouchers.js +41 -0
  70. package/dist/operations/vouchers.js.map +1 -0
  71. package/dist/tool-output.d.ts +16 -0
  72. package/dist/tool-output.d.ts.map +1 -0
  73. package/dist/tool-output.js +48 -0
  74. package/dist/tool-output.js.map +1 -0
  75. package/dist/tools/articles.d.ts +3 -0
  76. package/dist/tools/articles.d.ts.map +1 -0
  77. package/dist/tools/articles.js +75 -0
  78. package/dist/tools/articles.js.map +1 -0
  79. package/dist/tools/bookkeeping.d.ts +3 -0
  80. package/dist/tools/bookkeeping.d.ts.map +1 -0
  81. package/dist/tools/bookkeeping.js +82 -0
  82. package/dist/tools/bookkeeping.js.map +1 -0
  83. package/dist/tools/company.d.ts +3 -0
  84. package/dist/tools/company.d.ts.map +1 -0
  85. package/dist/tools/company.js +13 -0
  86. package/dist/tools/company.js.map +1 -0
  87. package/dist/tools/customers.d.ts +3 -0
  88. package/dist/tools/customers.d.ts.map +1 -0
  89. package/dist/tools/customers.js +75 -0
  90. package/dist/tools/customers.js.map +1 -0
  91. package/dist/tools/financial-reports.d.ts +3 -0
  92. package/dist/tools/financial-reports.d.ts.map +1 -0
  93. package/dist/tools/financial-reports.js +35 -0
  94. package/dist/tools/financial-reports.js.map +1 -0
  95. package/dist/tools/invoices.d.ts +3 -0
  96. package/dist/tools/invoices.d.ts.map +1 -0
  97. package/dist/tools/invoices.js +160 -0
  98. package/dist/tools/invoices.js.map +1 -0
  99. package/dist/tools/status.d.ts +3 -0
  100. package/dist/tools/status.d.ts.map +1 -0
  101. package/dist/tools/status.js +109 -0
  102. package/dist/tools/status.js.map +1 -0
  103. package/dist/tools/supplier-invoices.d.ts +3 -0
  104. package/dist/tools/supplier-invoices.d.ts.map +1 -0
  105. package/dist/tools/supplier-invoices.js +88 -0
  106. package/dist/tools/supplier-invoices.js.map +1 -0
  107. package/dist/tools/suppliers.d.ts +3 -0
  108. package/dist/tools/suppliers.d.ts.map +1 -0
  109. package/dist/tools/suppliers.js +77 -0
  110. package/dist/tools/suppliers.js.map +1 -0
  111. package/dist/tools/tax.d.ts +3 -0
  112. package/dist/tools/tax.d.ts.map +1 -0
  113. package/dist/tools/tax.js +18 -0
  114. package/dist/tools/tax.js.map +1 -0
  115. package/dist/views.d.ts +19 -0
  116. package/dist/views.d.ts.map +1 -0
  117. package/dist/views.js +165 -0
  118. package/dist/views.js.map +1 -0
  119. package/package.json +60 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Magnus Gille
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,326 @@
1
+ # noxctl
2
+
3
+ Command-line interface (CLI) and Model Context Protocol (MCP) server for Fortnox — manage invoices, customers, bookkeeping, and VAT (Value Added Tax) from the terminal or from AI (Artificial Intelligence) agents like Claude Code.
4
+
5
+ ```
6
+ noxctl init # interactive setup wizard
7
+ noxctl company info # verify connection
8
+ noxctl customers list # list customers
9
+ noxctl invoices list --filter unpaid # unpaid invoices
10
+ noxctl -o json invoices list | jq . # JSON output for scripting/AI
11
+ ```
12
+
13
+ ## Status
14
+
15
+ noxctl is an independent open-source project. It is not affiliated with, endorsed by, or certified by Fortnox AB.
16
+
17
+ Use it with your own Fortnox account and developer credentials. You are responsible for complying with Fortnox terms, Swedish bookkeeping/tax rules, and your own privacy obligations.
18
+
19
+ ## Prerequisites
20
+
21
+ - **Node.js** 20+
22
+ - **Fortnox account** with API (Application Programming Interface) access
23
+ - **Your own Fortnox developer app** with the required scopes enabled
24
+ - **Linux only:** `secret-tool` available for secure credential storage
25
+
26
+ Fortnox product plans, API activation requirements, and integration licensing can change. Verify the current Fortnox requirements before publishing or relying on this setup for business-critical work.
27
+
28
+ ## Setup
29
+
30
+ ### 1. Create a Fortnox app
31
+
32
+ > **Tip:** Run `npx noxctl init` (npx is a tool included with npm, the Node Package Manager) for an interactive setup wizard that guides you through all of these steps.
33
+
34
+ 1. Go to [developer.fortnox.se](https://developer.fortnox.se/) and click **Integrationer** / **Integrations**
35
+ 2. Create a new app (integration)
36
+ 3. On the **OAuth (Open Authorization)** tab:
37
+ - Set **Redirect URI (Uniform Resource Identifier)** to `http://localhost:9876/callback`
38
+ - Check **"Möjliggör auktorisering som servicekonto"** / **"Enable service account authorization"** (recommended)
39
+ - Copy your **Client ID** and **Client Secret**
40
+ 4. On the **Integration** tab, enable these scopes under **Behörigheter** / **Permissions**:
41
+
42
+ | Swedish (SV) | English (EN) |
43
+ |----------------------|---------------------|
44
+ | Artikel | Article |
45
+ | Bokföring | Bookkeeping |
46
+ | Faktura | Invoice |
47
+ | Företagsinformation | Company Information |
48
+ | Inställningar | Settings |
49
+ | Kund | Customer |
50
+
51
+ 5. Save the integration
52
+
53
+ You are creating and authorizing your own Fortnox app here. noxctl does not ship shared Fortnox credentials and does not bypass Fortnox's authorization model.
54
+
55
+ ### 2. Authenticate
56
+
57
+ Run the interactive setup wizard:
58
+
59
+ ```bash
60
+ npx noxctl init
61
+ ```
62
+
63
+ If running from a local clone:
64
+
65
+ ```bash
66
+ npm run build
67
+ node dist/cli.js init
68
+ ```
69
+
70
+ The wizard will prompt for your Client ID and Client Secret (masked input), run the OAuth flow, verify the connection, and optionally register the MCP server with Claude Code.
71
+
72
+ After authorization, credentials are stored in the OS (Operating System) secure store:
73
+
74
+ - **macOS:** Keychain (`security`)
75
+ - **Linux:** Secret Service via `secret-tool`
76
+ - **Windows:** DPAPI (Data Protection API)-protected user store
77
+
78
+ Token management is automatic after setup — no environment variables needed going forward.
79
+
80
+ - **With service account:** Uses client credentials flow with `TenantId` — no refresh tokens to manage. The tenant ID is fetched automatically during setup.
81
+ - **Without service account (default):** Uses standard OAuth2 refresh token flow.
82
+
83
+ ### 3. Register as MCP server (optional — for Claude Desktop/Web)
84
+
85
+ If you use Claude Desktop or claude.ai, register the MCP server so those environments can access Fortnox. Claude Code can use the CLI directly, so this step is optional there.
86
+
87
+ ```bash
88
+ claude mcp add fortnox -- npx noxctl serve
89
+ ```
90
+
91
+ If you are running from a local clone instead of npm:
92
+
93
+ ```bash
94
+ claude mcp add fortnox -- node /absolute/path/to/noxctl/dist/cli.js serve
95
+ ```
96
+
97
+ ### 4. Verify the connection
98
+
99
+ ```bash
100
+ noxctl company info
101
+ ```
102
+
103
+ If running from source:
104
+
105
+ ```bash
106
+ node dist/cli.js company info
107
+ ```
108
+
109
+ You should see your company name, organisation number, and address. If this works, you're all set.
110
+
111
+ ## Tools
112
+
113
+ Every operation is available both as a CLI command and as an MCP tool. The CLI is the primary interface; the MCP server exposes the same operations to AI agents.
114
+
115
+ ### Customers
116
+
117
+ | CLI | MCP tool | Description |
118
+ |-----|----------|-------------|
119
+ | `noxctl customers list [--search <term>]` | `fortnox_list_customers` | List/search customers |
120
+ | `noxctl customers get <number>` | `fortnox_get_customer` | Get a single customer by customer number |
121
+ | `noxctl customers create --name <name>` | `fortnox_create_customer` | Create a new customer (mutation) |
122
+ | `noxctl customers update <number> --input <file>` | `fortnox_update_customer` | Update an existing customer (mutation) |
123
+
124
+ ### Invoices
125
+
126
+ | CLI | MCP tool | Description |
127
+ |-----|----------|-------------|
128
+ | `noxctl invoices list [--filter <status>] [--customer <number>]` | `fortnox_list_invoices` | List/filter invoices. Filters: `cancelled`, `fullypaid`, `unpaid`, `unpaidoverdue`, `unbooked` |
129
+ | `noxctl invoices get <docNumber>` | `fortnox_get_invoice` | Get a single invoice by document number |
130
+ | `noxctl invoices create --customer <number> --input <file>` | `fortnox_create_invoice` | Create an invoice with line items (mutation) |
131
+ | `noxctl invoices update <docNumber> --input <file>` | `fortnox_update_invoice` | Update an invoice that has not been bookkeept (mutation) |
132
+ | `noxctl invoices send <docNumber> [--method email\|print\|einvoice] [--subject <s>] [--body <s>] [--bcc <email>]` | `fortnox_send_invoice` | Send invoice via email (default), print, or e-invoice. Supports email subject, body, and BCC (Blind Carbon Copy) (mutation) |
133
+ | `noxctl invoices bookkeep <docNumber>` | `fortnox_bookkeep_invoice` | Book an invoice (mutation) |
134
+ | `noxctl invoices credit <docNumber>` | `fortnox_credit_invoice` | Credit an invoice (mutation) |
135
+
136
+ ### Bookkeeping
137
+
138
+ | CLI | MCP tool | Description |
139
+ |-----|----------|-------------|
140
+ | `noxctl vouchers list [--series <s>] [--from <date>] [--to <date>]` | `fortnox_list_vouchers` | List vouchers, optionally filtered by series and date range |
141
+ | `noxctl vouchers create --input <file>` | `fortnox_create_voucher` | Create a voucher with debit/credit rows (mutation) |
142
+ | `noxctl accounts list [--search <term>]` | `fortnox_list_accounts` | View chart of accounts, search by name or number |
143
+
144
+ ### Tax
145
+
146
+ | CLI | MCP tool | Description |
147
+ |-----|----------|-------------|
148
+ | `noxctl tax report --from <date> --to <date>` | `fortnox_tax_report` | Informational VAT summary for a period. Reconcile against Fortnox before filing. Dates in `YYYY-MM-DD` format |
149
+
150
+ ### Company
151
+
152
+ | CLI | MCP tool | Description |
153
+ |-----|----------|-------------|
154
+ | `noxctl company info` | `fortnox_company_info` | Company name, org number, address, and settings |
155
+
156
+ ### Utility
157
+
158
+ | CLI | MCP tool | Description |
159
+ |-----|----------|-------------|
160
+ | `noxctl init` | — | Interactive setup wizard — connects to Fortnox, stores credentials, optionally registers MCP server |
161
+ | `noxctl doctor` | `fortnox_status` | Validate setup: Node version, credentials, token status, API connectivity, and scopes |
162
+ | `noxctl logout` | — | Remove stored credentials from the OS keychain |
163
+
164
+ ## CLI output
165
+
166
+ By default, `noxctl` uses **table output** on interactive terminals and **JSON** when piped or redirected. Override with `-o`:
167
+
168
+ ```bash
169
+ noxctl invoices list # table on terminal, JSON when piped
170
+ noxctl -o json invoices list # force JSON (JavaScript Object Notation)
171
+ noxctl -o table invoices list # force table
172
+ noxctl invoices list | jq . # auto-JSON (piped)
173
+ ```
174
+
175
+ When running from a local clone instead of an installed binary, replace `noxctl` with `node dist/cli.js`.
176
+
177
+ ## Mutation safety
178
+
179
+ Mutating commands require confirmation before executing. On interactive terminals, the CLI prompts with `[y/N]`. In non-interactive contexts (piped input, CI), pass `--yes` explicitly or the command will fail safely.
180
+
181
+ CLI:
182
+
183
+ ```bash
184
+ noxctl invoices send 1001 # prompts: "Send invoice 1001 via email. Continue? [y/N]"
185
+ noxctl invoices send 1001 --yes # skip prompt (scripting/AI)
186
+ noxctl invoices send 1001 --dry-run # preview without sending
187
+ noxctl customers update 42 --input customer.json # prompts for confirmation
188
+ noxctl vouchers create --input voucher.json --dry-run # preview payload
189
+ ```
190
+
191
+ MCP tools:
192
+
193
+ - Mutating tools require `confirm: true`
194
+ - Use `dryRun: true` to preview a request without sending it
195
+ - Raw Fortnox JSON is opt-in via `includeRaw: true`
196
+ - `includeRaw: true` can expose more accounting and personal data to AI transcripts, logs, and terminals than the summarized default output
197
+
198
+ ## Privacy and AI Use
199
+
200
+ If you use noxctl through Claude, MCP clients, or other AI tooling, customer, supplier, invoice, and bookkeeping data may leave the local Fortnox UI context and enter third-party systems.
201
+
202
+ - Keep `includeRaw` off unless you truly need the full payload
203
+ - Review your AI provider's retention, logging, and processor terms
204
+ - Make sure your GDPR setup covers this use, including processor agreements and any required third-country transfer assessment
205
+ - Prefer synthetic data when testing prompts, demos, and examples
206
+
207
+ See [PRIVACY.md](PRIVACY.md) for the project-specific privacy notes.
208
+
209
+ ## Tax and Accounting Limits
210
+
211
+ noxctl can help you inspect Fortnox data and submit operations you choose to confirm. It does not make legal judgments for you.
212
+
213
+ - The VAT report is an informational summary, not a filed declaration
214
+ - Swedish bookkeeping responsibility remains with the company owner or board
215
+ - Review invoices, vouchers, and VAT totals before confirming or filing anything
216
+ - Reconcile VAT figures against Fortnox's own momsrapport and your accounting records before submitting to Skatteverket
217
+
218
+ ## Examples
219
+
220
+ Ask Claude naturally — works in both Swedish and English:
221
+
222
+ - "Skapa en faktura till kund 42 för 10 konsulttimmar á 1200 kr"
223
+ - "Create an invoice for customer 42: 10 consulting hours at 1200 SEK (Swedish Krona)"
224
+ - "Visa alla obetalda fakturor" / "Show all unpaid invoices"
225
+ - "Vad har vi för utgående moms Q1 2025?" / "What's our outgoing VAT for Q1 2025?"
226
+ - "Bokför kontorsmaterial för 1250 kr inkl moms" / "Book office supplies for 1250 SEK incl VAT"
227
+ - "Skicka faktura 1001 via e-post" / "Send invoice 1001 by email"
228
+
229
+ ## Troubleshooting
230
+
231
+ **"FORTNOX_CLIENT_ID and FORTNOX_CLIENT_SECRET must be set"**
232
+
233
+ Environment variables were not passed to the command. Use `export` to set them in your shell first:
234
+
235
+ ```bash
236
+ export FORTNOX_CLIENT_ID=<your-id>
237
+ export FORTNOX_CLIENT_SECRET=<your-secret>
238
+ ```
239
+
240
+ Then run setup again. This avoids issues with long commands wrapping across lines.
241
+
242
+ **"Not authenticated. Run `noxctl init`"**
243
+
244
+ Credentials are missing or were not saved. Re-run the setup step. On macOS, check that Keychain Access is not blocking the `security` command. On Linux, ensure `secret-tool` is installed (`sudo apt install libsecret-tools`).
245
+
246
+ **403 Forbidden from Fortnox API**
247
+
248
+ Your app is missing required scopes. Go to [developer.fortnox.se](https://developer.fortnox.se/), open your app, and check that these are enabled under **Behörigheter** / **Permissions**:
249
+
250
+ | Swedish (SV) | English (EN) |
251
+ |----------------------|---------------------|
252
+ | Artikel | Article |
253
+ | Bokföring | Bookkeeping |
254
+ | Faktura | Invoice |
255
+ | Företagsinformation | Company Information |
256
+ | Inställningar | Settings |
257
+ | Kund | Customer |
258
+
259
+ **"Token refresh failed"**
260
+
261
+ Your refresh token may have expired or been revoked. Re-run setup to re-authenticate.
262
+
263
+ **Port 9876 already in use**
264
+
265
+ Another process is using the OAuth callback port. Close it or wait for a previous setup attempt to finish, then try again.
266
+
267
+ ## Development
268
+
269
+ ```bash
270
+ npm install
271
+ npm run build # compile TypeScript
272
+ npm test # run tests
273
+ npm run test:watch # watch mode
274
+ npm run lint # ESLint with typescript-eslint
275
+ npm run format # format
276
+ ```
277
+
278
+ ## Architecture
279
+
280
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for details.
281
+
282
+ ## Security
283
+
284
+ - Credentials stored in the OS secure store, not plaintext repo or home-directory JSON
285
+ - No secrets in environment variables after initial setup
286
+ - OAuth callback is bound to loopback and validated with a per-run OAuth `state`
287
+ - OAuth/client-credentials secrets are never emitted in tool responses
288
+ - Mutating actions require explicit confirmation or `dryRun`
289
+ - MCP responses are summarized by default; raw Fortnox JSON is opt-in
290
+ - Retries are limited to idempotent requests
291
+
292
+ ## Glossary
293
+
294
+ | Abbreviation | Full term |
295
+ |---|---|
296
+ | AI | Artificial Intelligence |
297
+ | API | Application Programming Interface |
298
+ | BCC | Blind Carbon Copy — an email field that sends a copy to someone without other recipients seeing it |
299
+ | CLI | Command Line Interface — a text-based way to interact with software by typing commands |
300
+ | DPAPI | Data Protection API — a Windows feature for encrypting stored credentials |
301
+ | JSON | JavaScript Object Notation — a widely used format for structured data |
302
+ | MCP | Model Context Protocol — a standard that lets AI assistants use external tools and data sources |
303
+ | MIT | Massachusetts Institute of Technology — refers to a permissive open-source software license |
304
+ | npm | Node Package Manager — a tool for installing and managing JavaScript packages |
305
+ | npx | A tool included with npm for running packages without installing them globally |
306
+ | OAuth | Open Authorization — a standard protocol for granting apps limited access to accounts without sharing passwords |
307
+ | OS | Operating System — the software that runs your computer (e.g. macOS, Windows, Linux) |
308
+ | SEK | Swedish Krona — the currency of Sweden |
309
+ | URI | Uniform Resource Identifier — an address that identifies a resource (similar to a web link) |
310
+ | VAT | Value Added Tax — a consumption tax added at each stage of production |
311
+
312
+ ## Disclaimer
313
+
314
+ noxctl is an independent open-source project — not affiliated with, endorsed by, or certified by Fortnox AB.
315
+
316
+ **You are responsible for your own bookkeeping.** Under Swedish law (Bokföringslagen), the company owner or board bears full responsibility for the correctness of all accounting records, regardless of what tools are used. noxctl is an instrument that executes your instructions — review entries before confirming, especially when using `--yes` or `confirm: true`.
317
+
318
+ **Privacy note:** When using noxctl with AI assistants, customer and supplier data may enter third-party AI systems. Use `includeRaw: false` unless full payloads are necessary, and make sure your use complies with GDPR, processor agreements, and any required transfer assessments.
319
+
320
+ **Tax note:** The VAT report is an informational summary only. Reconcile it against Fortnox and your accounting records before submitting anything to Skatteverket.
321
+
322
+ **Fortnox API access** requires your own developer credentials and Fortnox setup. You must comply with the applicable Fortnox terms. noxctl does not redistribute any Fortnox-owned code or data.
323
+
324
+ ## License
325
+
326
+ MIT
package/dist/auth.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ export declare const SCOPES = "article customer invoice supplier supplierinvoice bookkeeping companyinformation settings";
2
+ export interface FortnoxCredentials {
3
+ client_id: string;
4
+ client_secret: string;
5
+ access_token: string;
6
+ refresh_token: string;
7
+ expires_at: number;
8
+ tenant_id?: string;
9
+ }
10
+ export interface FortnoxAppConfig {
11
+ clientId: string;
12
+ clientSecret: string;
13
+ serviceAccount?: boolean;
14
+ }
15
+ export declare function loadCredentials(): Promise<FortnoxCredentials | null>;
16
+ export declare function saveCredentials(creds: FortnoxCredentials): Promise<void>;
17
+ export declare function exchangeCodeForTokens(code: string, redirectUri: string, config: FortnoxAppConfig): Promise<{
18
+ access_token: string;
19
+ refresh_token: string;
20
+ expires_in: number;
21
+ }>;
22
+ export declare function getTokenViaClientCredentials(creds: FortnoxCredentials): Promise<FortnoxCredentials>;
23
+ export declare function refreshAccessToken(creds: FortnoxCredentials): Promise<FortnoxCredentials>;
24
+ export declare function getValidToken(): Promise<string>;
25
+ export declare function fetchTenantId(accessToken: string): Promise<string | undefined>;
26
+ export declare function escapeHtml(value: string): string;
27
+ export declare function buildAuthorizationUrl(config: FortnoxAppConfig, redirectUri: string, state: string): string;
28
+ export declare function runOAuthSetup(config: FortnoxAppConfig): Promise<void>;
29
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,MAAM,8FAC0E,CAAC;AAE9F,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAQ1E;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9E;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA2B9E;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAuC7B;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoC/F;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAwBrD;AAED,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAepF;AAgBD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOhD;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,MAAM,CAeR;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6G3E"}
package/dist/auth.js ADDED
@@ -0,0 +1,267 @@
1
+ import { createServer } from 'node:http';
2
+ import { randomBytes } from 'node:crypto';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { loadCredentialBlob, saveCredentialBlob } from './credentials-store.js';
5
+ const FORTNOX_AUTH_URL = 'https://apps.fortnox.se/oauth-v1/auth';
6
+ const FORTNOX_TOKEN_URL = 'https://apps.fortnox.se/oauth-v1/token';
7
+ const CALLBACK_HOST = '127.0.0.1';
8
+ export const SCOPES = 'article customer invoice supplier supplierinvoice bookkeeping companyinformation settings';
9
+ export async function loadCredentials() {
10
+ try {
11
+ const data = await loadCredentialBlob();
12
+ if (!data)
13
+ return null;
14
+ return JSON.parse(data);
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ export async function saveCredentials(creds) {
21
+ await saveCredentialBlob(JSON.stringify(creds));
22
+ }
23
+ export async function exchangeCodeForTokens(code, redirectUri, config) {
24
+ const body = new URLSearchParams({
25
+ grant_type: 'authorization_code',
26
+ code,
27
+ redirect_uri: redirectUri,
28
+ });
29
+ const response = await fetch(FORTNOX_TOKEN_URL, {
30
+ method: 'POST',
31
+ headers: {
32
+ 'Content-Type': 'application/x-www-form-urlencoded',
33
+ Authorization: 'Basic ' + Buffer.from(`${config.clientId}:${config.clientSecret}`).toString('base64'),
34
+ },
35
+ body: body.toString(),
36
+ });
37
+ if (!response.ok) {
38
+ const text = await response.text();
39
+ throw new Error(`Token exchange failed (${response.status}): ${text}`);
40
+ }
41
+ return (await response.json());
42
+ }
43
+ export async function getTokenViaClientCredentials(creds) {
44
+ if (!creds.tenant_id) {
45
+ throw new Error('No tenant_id available — cannot use client credentials flow');
46
+ }
47
+ const body = new URLSearchParams({
48
+ grant_type: 'client_credentials',
49
+ scope: SCOPES,
50
+ });
51
+ const response = await fetch(FORTNOX_TOKEN_URL, {
52
+ method: 'POST',
53
+ headers: {
54
+ 'Content-Type': 'application/x-www-form-urlencoded',
55
+ Authorization: 'Basic ' + Buffer.from(`${creds.client_id}:${creds.client_secret}`).toString('base64'),
56
+ TenantId: creds.tenant_id,
57
+ },
58
+ body: body.toString(),
59
+ });
60
+ if (!response.ok) {
61
+ const text = await response.text();
62
+ throw new Error(`Client credentials token request failed (${response.status}): ${text}`);
63
+ }
64
+ const data = (await response.json());
65
+ const updated = {
66
+ ...creds,
67
+ access_token: data.access_token,
68
+ expires_at: Date.now() + data.expires_in * 1000,
69
+ };
70
+ await saveCredentials(updated);
71
+ return updated;
72
+ }
73
+ export async function refreshAccessToken(creds) {
74
+ const body = new URLSearchParams({
75
+ grant_type: 'refresh_token',
76
+ refresh_token: creds.refresh_token,
77
+ });
78
+ const response = await fetch(FORTNOX_TOKEN_URL, {
79
+ method: 'POST',
80
+ headers: {
81
+ 'Content-Type': 'application/x-www-form-urlencoded',
82
+ Authorization: 'Basic ' + Buffer.from(`${creds.client_id}:${creds.client_secret}`).toString('base64'),
83
+ },
84
+ body: body.toString(),
85
+ });
86
+ if (!response.ok) {
87
+ const text = await response.text();
88
+ throw new Error(`Token refresh failed (${response.status}): ${text}`);
89
+ }
90
+ const data = (await response.json());
91
+ const updated = {
92
+ ...creds,
93
+ access_token: data.access_token,
94
+ refresh_token: data.refresh_token,
95
+ expires_at: Date.now() + data.expires_in * 1000,
96
+ };
97
+ await saveCredentials(updated);
98
+ return updated;
99
+ }
100
+ export async function getValidToken() {
101
+ const creds = await loadCredentials();
102
+ if (!creds) {
103
+ throw new Error('Not authenticated. Run `noxctl init` to connect your Fortnox account.');
104
+ }
105
+ // Token still valid — use it
106
+ if (Date.now() <= creds.expires_at - 5 * 60 * 1000) {
107
+ return creds.access_token;
108
+ }
109
+ // Prefer client credentials when tenant_id is available (no refresh token management needed)
110
+ if (creds.tenant_id) {
111
+ try {
112
+ const refreshed = await getTokenViaClientCredentials(creds);
113
+ return refreshed.access_token;
114
+ }
115
+ catch {
116
+ // Fall through to refresh_token flow
117
+ }
118
+ }
119
+ // Fallback: standard refresh token flow
120
+ const refreshed = await refreshAccessToken(creds);
121
+ return refreshed.access_token;
122
+ }
123
+ export async function fetchTenantId(accessToken) {
124
+ const response = await fetch('https://api.fortnox.se/3/companyinformation', {
125
+ headers: {
126
+ Authorization: `Bearer ${accessToken}`,
127
+ Accept: 'application/json',
128
+ },
129
+ });
130
+ if (!response.ok)
131
+ return undefined;
132
+ const data = (await response.json());
133
+ return data.CompanyInformation?.DatabaseNumber;
134
+ }
135
+ function openBrowser(url) {
136
+ try {
137
+ if (process.platform === 'darwin') {
138
+ execFileSync('open', [url]);
139
+ }
140
+ else if (process.platform === 'win32') {
141
+ execFileSync('cmd', ['/c', 'start', url]);
142
+ }
143
+ else {
144
+ execFileSync('xdg-open', [url]);
145
+ }
146
+ }
147
+ catch {
148
+ console.log(`\nOpen this URL in your browser:\n${url}\n`);
149
+ }
150
+ }
151
+ export function escapeHtml(value) {
152
+ return value
153
+ .replace(/&/g, '&amp;')
154
+ .replace(/</g, '&lt;')
155
+ .replace(/>/g, '&gt;')
156
+ .replace(/"/g, '&quot;')
157
+ .replace(/'/g, '&#39;');
158
+ }
159
+ export function buildAuthorizationUrl(config, redirectUri, state) {
160
+ const params = new URLSearchParams({
161
+ client_id: config.clientId,
162
+ redirect_uri: redirectUri,
163
+ scope: SCOPES,
164
+ state,
165
+ response_type: 'code',
166
+ access_type: 'offline',
167
+ });
168
+ if (config.serviceAccount) {
169
+ params.set('account_type', 'service');
170
+ }
171
+ return `${FORTNOX_AUTH_URL}?${params.toString()}`;
172
+ }
173
+ export async function runOAuthSetup(config) {
174
+ const PORT = 9876;
175
+ const REDIRECT_URI = `http://localhost:${PORT}/callback`;
176
+ const oauthState = randomBytes(24).toString('hex');
177
+ return new Promise((resolve, reject) => {
178
+ let settled = false;
179
+ const finish = (err) => {
180
+ if (settled)
181
+ return;
182
+ settled = true;
183
+ if (err) {
184
+ reject(err);
185
+ return;
186
+ }
187
+ resolve();
188
+ };
189
+ const server = createServer(async (req, res) => {
190
+ const url = new URL(req.url || '/', `http://localhost:${PORT}`);
191
+ if (url.pathname === '/callback') {
192
+ const code = url.searchParams.get('code');
193
+ const error = url.searchParams.get('error');
194
+ const state = url.searchParams.get('state');
195
+ if (error) {
196
+ const errorDesc = url.searchParams.get('error_description') || '';
197
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
198
+ res.end(`<h1>Autentisering misslyckades</h1><p>${escapeHtml(error)}</p><p>${escapeHtml(errorDesc)}</p>`);
199
+ server.close();
200
+ finish(new Error(`OAuth error: ${error}${errorDesc ? ` — ${errorDesc}` : ''}`));
201
+ return;
202
+ }
203
+ if (state !== oauthState) {
204
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
205
+ res.end('<h1>Ogiltig OAuth-state</h1><p>Försök igen från noxctl init.</p>');
206
+ server.close();
207
+ finish(new Error('OAuth state mismatch'));
208
+ return;
209
+ }
210
+ if (!code) {
211
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
212
+ res.end('<h1>Ingen auktoriseringskod mottagen</h1>');
213
+ server.close();
214
+ finish(new Error('No authorization code received'));
215
+ return;
216
+ }
217
+ try {
218
+ const tokens = await exchangeCodeForTokens(code, REDIRECT_URI, config);
219
+ // Fetch tenant_id for client credentials flow
220
+ console.log('Fetching tenant ID...');
221
+ const tenantId = await fetchTenantId(tokens.access_token);
222
+ const creds = {
223
+ client_id: config.clientId,
224
+ client_secret: config.clientSecret,
225
+ access_token: tokens.access_token,
226
+ refresh_token: tokens.refresh_token,
227
+ expires_at: Date.now() + tokens.expires_in * 1000,
228
+ tenant_id: tenantId,
229
+ };
230
+ await saveCredentials(creds);
231
+ const tenantMsg = tenantId
232
+ ? ' Client credentials flow enabled.'
233
+ : ' (Tenant ID not found — using refresh token flow.)';
234
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
235
+ res.end('<h1>Klart!</h1><p>Fortnox MCP är nu kopplat till ditt konto. Du kan stänga den här fliken.</p>');
236
+ console.log(`\nSetup complete! Credentials saved.${tenantMsg}\n`);
237
+ console.log('Register in Claude Code with:');
238
+ console.log(' claude mcp add fortnox -- noxctl serve\n');
239
+ finish();
240
+ }
241
+ catch (err) {
242
+ res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
243
+ const message = err instanceof Error ? err.message : String(err);
244
+ res.end(`<h1>Något gick fel</h1><p>${escapeHtml(message)}</p>`);
245
+ finish(err instanceof Error ? err : new Error(message));
246
+ }
247
+ finally {
248
+ server.close();
249
+ }
250
+ }
251
+ else {
252
+ res.writeHead(404);
253
+ res.end('Not found');
254
+ }
255
+ });
256
+ server.listen(PORT, CALLBACK_HOST, () => {
257
+ const authUrl = buildAuthorizationUrl(config, REDIRECT_URI, oauthState);
258
+ console.log('Opening Fortnox login in your browser...');
259
+ openBrowser(authUrl);
260
+ console.log(`\nWaiting for authentication on http://${CALLBACK_HOST}:${PORT}...`);
261
+ });
262
+ server.on('error', (err) => {
263
+ finish(new Error(`Could not start callback server: ${err.message}`));
264
+ });
265
+ });
266
+ }
267
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map