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.
- package/LICENSE +21 -0
- package/README.md +326 -0
- package/dist/auth.d.ts +29 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +267 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1012 -0
- package/dist/cli.js.map +1 -0
- package/dist/credentials-store.d.ts +4 -0
- package/dist/credentials-store.d.ts.map +1 -0
- package/dist/credentials-store.js +223 -0
- package/dist/credentials-store.js.map +1 -0
- package/dist/formatter.d.ts +62 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +200 -0
- package/dist/formatter.js.map +1 -0
- package/dist/fortnox-client.d.ts +27 -0
- package/dist/fortnox-client.d.ts.map +1 -0
- package/dist/fortnox-client.js +178 -0
- package/dist/fortnox-client.js.map +1 -0
- package/dist/identifiers.d.ts +4 -0
- package/dist/identifiers.d.ts.map +1 -0
- package/dist/identifiers.js +16 -0
- package/dist/identifiers.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/operations/accounts.d.ts +18 -0
- package/dist/operations/accounts.d.ts.map +1 -0
- package/dist/operations/accounts.js +28 -0
- package/dist/operations/accounts.js.map +1 -0
- package/dist/operations/articles.d.ts +20 -0
- package/dist/operations/articles.d.ts.map +1 -0
- package/dist/operations/articles.js +36 -0
- package/dist/operations/articles.js.map +1 -0
- package/dist/operations/company.d.ts +2 -0
- package/dist/operations/company.d.ts.map +1 -0
- package/dist/operations/company.js +6 -0
- package/dist/operations/company.js.map +1 -0
- package/dist/operations/customers.d.ts +20 -0
- package/dist/operations/customers.d.ts.map +1 -0
- package/dist/operations/customers.js +37 -0
- package/dist/operations/customers.js.map +1 -0
- package/dist/operations/financial-reports.d.ts +37 -0
- package/dist/operations/financial-reports.d.ts.map +1 -0
- package/dist/operations/financial-reports.js +193 -0
- package/dist/operations/financial-reports.js.map +1 -0
- package/dist/operations/invoices.d.ts +32 -0
- package/dist/operations/invoices.d.ts.map +1 -0
- package/dist/operations/invoices.js +74 -0
- package/dist/operations/invoices.js.map +1 -0
- package/dist/operations/supplier-invoices.d.ts +23 -0
- package/dist/operations/supplier-invoices.d.ts.map +1 -0
- package/dist/operations/supplier-invoices.js +36 -0
- package/dist/operations/supplier-invoices.js.map +1 -0
- package/dist/operations/suppliers.d.ts +20 -0
- package/dist/operations/suppliers.d.ts.map +1 -0
- package/dist/operations/suppliers.js +36 -0
- package/dist/operations/suppliers.js.map +1 -0
- package/dist/operations/tax.d.ts +26 -0
- package/dist/operations/tax.d.ts.map +1 -0
- package/dist/operations/tax.js +47 -0
- package/dist/operations/tax.js.map +1 -0
- package/dist/operations/vouchers.d.ts +22 -0
- package/dist/operations/vouchers.d.ts.map +1 -0
- package/dist/operations/vouchers.js +41 -0
- package/dist/operations/vouchers.js.map +1 -0
- package/dist/tool-output.d.ts +16 -0
- package/dist/tool-output.d.ts.map +1 -0
- package/dist/tool-output.js +48 -0
- package/dist/tool-output.js.map +1 -0
- package/dist/tools/articles.d.ts +3 -0
- package/dist/tools/articles.d.ts.map +1 -0
- package/dist/tools/articles.js +75 -0
- package/dist/tools/articles.js.map +1 -0
- package/dist/tools/bookkeeping.d.ts +3 -0
- package/dist/tools/bookkeeping.d.ts.map +1 -0
- package/dist/tools/bookkeeping.js +82 -0
- package/dist/tools/bookkeeping.js.map +1 -0
- package/dist/tools/company.d.ts +3 -0
- package/dist/tools/company.d.ts.map +1 -0
- package/dist/tools/company.js +13 -0
- package/dist/tools/company.js.map +1 -0
- package/dist/tools/customers.d.ts +3 -0
- package/dist/tools/customers.d.ts.map +1 -0
- package/dist/tools/customers.js +75 -0
- package/dist/tools/customers.js.map +1 -0
- package/dist/tools/financial-reports.d.ts +3 -0
- package/dist/tools/financial-reports.d.ts.map +1 -0
- package/dist/tools/financial-reports.js +35 -0
- package/dist/tools/financial-reports.js.map +1 -0
- package/dist/tools/invoices.d.ts +3 -0
- package/dist/tools/invoices.d.ts.map +1 -0
- package/dist/tools/invoices.js +160 -0
- package/dist/tools/invoices.js.map +1 -0
- package/dist/tools/status.d.ts +3 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/status.js +109 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/tools/supplier-invoices.d.ts +3 -0
- package/dist/tools/supplier-invoices.d.ts.map +1 -0
- package/dist/tools/supplier-invoices.js +88 -0
- package/dist/tools/supplier-invoices.js.map +1 -0
- package/dist/tools/suppliers.d.ts +3 -0
- package/dist/tools/suppliers.d.ts.map +1 -0
- package/dist/tools/suppliers.js +77 -0
- package/dist/tools/suppliers.js.map +1 -0
- package/dist/tools/tax.d.ts +3 -0
- package/dist/tools/tax.d.ts.map +1 -0
- package/dist/tools/tax.js +18 -0
- package/dist/tools/tax.js.map +1 -0
- package/dist/views.d.ts +19 -0
- package/dist/views.d.ts.map +1 -0
- package/dist/views.js +165 -0
- package/dist/views.js.map +1 -0
- 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, '&')
|
|
154
|
+
.replace(/</g, '<')
|
|
155
|
+
.replace(/>/g, '>')
|
|
156
|
+
.replace(/"/g, '"')
|
|
157
|
+
.replace(/'/g, ''');
|
|
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
|
package/dist/auth.js.map
ADDED
|
@@ -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