noxctl 0.1.0 → 0.3.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 (155) hide show
  1. package/README.md +279 -29
  2. package/dist/auth.d.ts +15 -7
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +128 -22
  5. package/dist/auth.js.map +1 -1
  6. package/dist/cli.d.ts +2 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +1640 -70
  9. package/dist/cli.js.map +1 -1
  10. package/dist/completions.d.ts +13 -0
  11. package/dist/completions.d.ts.map +1 -0
  12. package/dist/completions.js +150 -0
  13. package/dist/completions.js.map +1 -0
  14. package/dist/config-paths.d.ts +2 -0
  15. package/dist/config-paths.d.ts.map +1 -0
  16. package/dist/config-paths.js +7 -0
  17. package/dist/config-paths.js.map +1 -0
  18. package/dist/credentials-store.d.ts +13 -3
  19. package/dist/credentials-store.d.ts.map +1 -1
  20. package/dist/credentials-store.js +197 -62
  21. package/dist/credentials-store.js.map +1 -1
  22. package/dist/date-periods.d.ts +14 -0
  23. package/dist/date-periods.d.ts.map +1 -0
  24. package/dist/date-periods.js +103 -0
  25. package/dist/date-periods.js.map +1 -0
  26. package/dist/formatter.d.ts +11 -2
  27. package/dist/formatter.d.ts.map +1 -1
  28. package/dist/formatter.js +27 -4
  29. package/dist/formatter.js.map +1 -1
  30. package/dist/fortnox-client.d.ts +1 -0
  31. package/dist/fortnox-client.d.ts.map +1 -1
  32. package/dist/fortnox-client.js +28 -5
  33. package/dist/fortnox-client.js.map +1 -1
  34. package/dist/index.d.ts +15 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +92 -2
  37. package/dist/index.js.map +1 -1
  38. package/dist/keychain-target.d.ts +32 -0
  39. package/dist/keychain-target.d.ts.map +1 -0
  40. package/dist/keychain-target.js +349 -0
  41. package/dist/keychain-target.js.map +1 -0
  42. package/dist/operations/analytics.d.ts +84 -0
  43. package/dist/operations/analytics.d.ts.map +1 -0
  44. package/dist/operations/analytics.js +154 -0
  45. package/dist/operations/analytics.js.map +1 -0
  46. package/dist/operations/contracts.d.ts +23 -0
  47. package/dist/operations/contracts.d.ts.map +1 -0
  48. package/dist/operations/contracts.js +52 -0
  49. package/dist/operations/contracts.js.map +1 -0
  50. package/dist/operations/costcenters.d.ts +20 -0
  51. package/dist/operations/costcenters.d.ts.map +1 -0
  52. package/dist/operations/costcenters.js +38 -0
  53. package/dist/operations/costcenters.js.map +1 -0
  54. package/dist/operations/customers.d.ts.map +1 -1
  55. package/dist/operations/customers.js +12 -2
  56. package/dist/operations/customers.js.map +1 -1
  57. package/dist/operations/financial-years.d.ts +16 -0
  58. package/dist/operations/financial-years.d.ts.map +1 -0
  59. package/dist/operations/financial-years.js +32 -0
  60. package/dist/operations/financial-years.js.map +1 -0
  61. package/dist/operations/invoice-payments.d.ts +21 -0
  62. package/dist/operations/invoice-payments.d.ts.map +1 -0
  63. package/dist/operations/invoice-payments.js +40 -0
  64. package/dist/operations/invoice-payments.js.map +1 -0
  65. package/dist/operations/offers.d.ts +25 -0
  66. package/dist/operations/offers.d.ts.map +1 -0
  67. package/dist/operations/offers.js +48 -0
  68. package/dist/operations/offers.js.map +1 -0
  69. package/dist/operations/orders.d.ts +24 -0
  70. package/dist/operations/orders.d.ts.map +1 -0
  71. package/dist/operations/orders.js +44 -0
  72. package/dist/operations/orders.js.map +1 -0
  73. package/dist/operations/pricelists.d.ts +36 -0
  74. package/dist/operations/pricelists.d.ts.map +1 -0
  75. package/dist/operations/pricelists.js +52 -0
  76. package/dist/operations/pricelists.js.map +1 -0
  77. package/dist/operations/projects.d.ts +19 -0
  78. package/dist/operations/projects.d.ts.map +1 -0
  79. package/dist/operations/projects.js +33 -0
  80. package/dist/operations/projects.js.map +1 -0
  81. package/dist/operations/supplier-invoice-payments.d.ts +20 -0
  82. package/dist/operations/supplier-invoice-payments.d.ts.map +1 -0
  83. package/dist/operations/supplier-invoice-payments.js +34 -0
  84. package/dist/operations/supplier-invoice-payments.js.map +1 -0
  85. package/dist/operations/taxreductions.d.ts +19 -0
  86. package/dist/operations/taxreductions.d.ts.map +1 -0
  87. package/dist/operations/taxreductions.js +28 -0
  88. package/dist/operations/taxreductions.js.map +1 -0
  89. package/dist/operations/vouchers.d.ts +15 -0
  90. package/dist/operations/vouchers.d.ts.map +1 -1
  91. package/dist/operations/vouchers.js +97 -0
  92. package/dist/operations/vouchers.js.map +1 -1
  93. package/dist/profile-name.d.ts +9 -0
  94. package/dist/profile-name.d.ts.map +1 -0
  95. package/dist/profile-name.js +58 -0
  96. package/dist/profile-name.js.map +1 -0
  97. package/dist/profiles.d.ts +55 -0
  98. package/dist/profiles.d.ts.map +1 -0
  99. package/dist/profiles.js +210 -0
  100. package/dist/profiles.js.map +1 -0
  101. package/dist/tools/analytics.d.ts +3 -0
  102. package/dist/tools/analytics.d.ts.map +1 -0
  103. package/dist/tools/analytics.js +52 -0
  104. package/dist/tools/analytics.js.map +1 -0
  105. package/dist/tools/bookkeeping.d.ts.map +1 -1
  106. package/dist/tools/bookkeeping.js +38 -2
  107. package/dist/tools/bookkeeping.js.map +1 -1
  108. package/dist/tools/contracts.d.ts +3 -0
  109. package/dist/tools/contracts.d.ts.map +1 -0
  110. package/dist/tools/contracts.js +136 -0
  111. package/dist/tools/contracts.js.map +1 -0
  112. package/dist/tools/costcenters.d.ts +3 -0
  113. package/dist/tools/costcenters.d.ts.map +1 -0
  114. package/dist/tools/costcenters.js +79 -0
  115. package/dist/tools/costcenters.js.map +1 -0
  116. package/dist/tools/financial-years.d.ts +3 -0
  117. package/dist/tools/financial-years.d.ts.map +1 -0
  118. package/dist/tools/financial-years.js +33 -0
  119. package/dist/tools/financial-years.js.map +1 -0
  120. package/dist/tools/invoice-payments.d.ts +3 -0
  121. package/dist/tools/invoice-payments.d.ts.map +1 -0
  122. package/dist/tools/invoice-payments.js +60 -0
  123. package/dist/tools/invoice-payments.js.map +1 -0
  124. package/dist/tools/offers.d.ts +3 -0
  125. package/dist/tools/offers.d.ts.map +1 -0
  126. package/dist/tools/offers.js +130 -0
  127. package/dist/tools/offers.js.map +1 -0
  128. package/dist/tools/orders.d.ts +3 -0
  129. package/dist/tools/orders.d.ts.map +1 -0
  130. package/dist/tools/orders.js +114 -0
  131. package/dist/tools/orders.js.map +1 -0
  132. package/dist/tools/pricelists.d.ts +3 -0
  133. package/dist/tools/pricelists.d.ts.map +1 -0
  134. package/dist/tools/pricelists.js +104 -0
  135. package/dist/tools/pricelists.js.map +1 -0
  136. package/dist/tools/projects.d.ts +3 -0
  137. package/dist/tools/projects.d.ts.map +1 -0
  138. package/dist/tools/projects.js +80 -0
  139. package/dist/tools/projects.js.map +1 -0
  140. package/dist/tools/status.d.ts.map +1 -1
  141. package/dist/tools/status.js +6 -1
  142. package/dist/tools/status.js.map +1 -1
  143. package/dist/tools/supplier-invoice-payments.d.ts +3 -0
  144. package/dist/tools/supplier-invoice-payments.d.ts.map +1 -0
  145. package/dist/tools/supplier-invoice-payments.js +60 -0
  146. package/dist/tools/supplier-invoice-payments.js.map +1 -0
  147. package/dist/tools/taxreductions.d.ts +3 -0
  148. package/dist/tools/taxreductions.d.ts.map +1 -0
  149. package/dist/tools/taxreductions.js +51 -0
  150. package/dist/tools/taxreductions.js.map +1 -0
  151. package/dist/views.d.ts +28 -0
  152. package/dist/views.d.ts.map +1 -1
  153. package/dist/views.js +234 -0
  154. package/dist/views.js.map +1 -1
  155. package/package.json +1 -1
@@ -3,11 +3,60 @@ import fsSync from 'node:fs';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { execFileSync, spawnSync } from 'node:child_process';
6
- const CREDENTIALS_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '~', '.fortnox-mcp');
7
- const LEGACY_CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, 'credentials.json');
8
- const WINDOWS_CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, 'credentials.dpapi');
6
+ import { DEFAULT_PROFILE, LEGACY_KEYCHAIN_ACCOUNT, keychainAccount, sanitizeForFilename, validateProfileName, } from './profile-name.js';
7
+ import { configDir } from './config-paths.js';
8
+ import { activeKeychainPath, readDedicatedSecret } from './keychain-target.js';
9
+ function legacyCredentialsFile() {
10
+ return path.join(configDir(), 'credentials.json');
11
+ }
12
+ function legacyWindowsCredentialsFile() {
13
+ return path.join(configDir(), 'credentials.dpapi');
14
+ }
9
15
  const SERVICE_NAME = 'fortnox-mcp';
10
- const ACCOUNT_NAME = 'default';
16
+ function normalizeProfile(profile) {
17
+ const validated = validateProfileName(profile).toLowerCase();
18
+ return { normalized: validated, isDefault: validated === DEFAULT_PROFILE };
19
+ }
20
+ function windowsCredentialsFile(profile) {
21
+ return path.join(configDir(), `credentials.${sanitizeForFilename(profile)}.dpapi`);
22
+ }
23
+ // Dual-write to the legacy slot during the 0.2.x compatibility window so an
24
+ // older 0.1 binary can still read credentials written by this one. Flip to
25
+ // `false` in 0.3.0 and delete the legacy reader branch below.
26
+ // REMOVE IN 0.3.0
27
+ export const LEGACY_DUAL_WRITE = true;
28
+ function parseBlobMeta(blob) {
29
+ try {
30
+ const parsed = JSON.parse(blob);
31
+ const schema = typeof parsed.schema_version === 'number' ? parsed.schema_version : 1;
32
+ const epoch = typeof parsed.last_write_epoch === 'number' ? parsed.last_write_epoch : 0;
33
+ return { schema, epoch };
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ function pickHigher(newBlob, legacyBlob) {
40
+ const nMeta = parseBlobMeta(newBlob);
41
+ const lMeta = parseBlobMeta(legacyBlob);
42
+ if (nMeta && !lMeta)
43
+ return { blob: newBlob, picked: 'new' };
44
+ if (!nMeta && lMeta)
45
+ return { blob: legacyBlob, picked: 'legacy' };
46
+ if (!nMeta && !lMeta)
47
+ return { blob: newBlob, picked: 'new' };
48
+ const n = nMeta;
49
+ const l = lMeta;
50
+ if (n.schema > l.schema)
51
+ return { blob: newBlob, picked: 'new' };
52
+ if (l.schema > n.schema)
53
+ return { blob: legacyBlob, picked: 'legacy' };
54
+ if (n.epoch > l.epoch)
55
+ return { blob: newBlob, picked: 'new' };
56
+ if (l.epoch > n.epoch)
57
+ return { blob: legacyBlob, picked: 'legacy' };
58
+ return { blob: newBlob, picked: 'new' };
59
+ }
11
60
  function decodeHexIfNeeded(value) {
12
61
  // macOS `security -w` returns hex-encoded output when the password
13
62
  // contains control characters (e.g. newlines from pretty-printed JSON).
@@ -24,21 +73,33 @@ function decodeHexIfNeeded(value) {
24
73
  }
25
74
  return value;
26
75
  }
27
- function loadMacSecret() {
76
+ function loadMacSecret(account) {
77
+ // Dedicated-keychain mode: read prompt-free from the YubiKey-locked keychain.
78
+ // A locked keychain throws KeychainLockedError (propagated so the caller can
79
+ // tell the user to run `noxctl keychain unlock`), not a silent null.
80
+ const dedicated = activeKeychainPath();
81
+ if (dedicated)
82
+ return readDedicatedSecret(account, dedicated);
28
83
  try {
29
- const raw = execFileSync('security', ['find-generic-password', '-a', ACCOUNT_NAME, '-s', SERVICE_NAME, '-w'], { encoding: 'utf-8' }).trim();
84
+ const raw = execFileSync('security', ['find-generic-password', '-a', account, '-s', SERVICE_NAME, '-w'], { encoding: 'utf-8' }).trim();
30
85
  return decodeHexIfNeeded(raw);
31
86
  }
32
87
  catch {
33
88
  return null;
34
89
  }
35
90
  }
36
- function saveMacSecret(secret) {
91
+ function saveMacSecret(account, secret) {
37
92
  // macOS `security add-generic-password -w` requires the password as a CLI
38
93
  // argument, which is briefly visible via `ps`. Instead, use an inline Swift
39
94
  // script that reads the secret from stdin and writes to the Keychain via
40
95
  // the Security framework — the secret never appears in process arguments.
41
96
  const scriptPath = path.join(os.tmpdir(), `noxctl-keychain-${process.pid}.swift`);
97
+ // In dedicated-keychain mode, target that keychain file; otherwise the
98
+ // default (login) keychain. The path is passed as argv (not interpolated) so
99
+ // an odd path can't break or inject into the Swift source.
100
+ const keychainPath = activeKeychainPath() ?? '';
101
+ // account is either LEGACY_KEYCHAIN_ACCOUNT ("default") or `profile:<validated>`.
102
+ // Validation in profile-name.ts restricts the character set so embedding is safe.
42
103
  const swiftScript = `
43
104
  import Foundation
44
105
  import Security
@@ -47,28 +108,38 @@ let data = FileHandle.standardInput.readDataToEndOfFile()
47
108
  guard let password = String(data: data, encoding: .utf8) else { exit(1) }
48
109
 
49
110
  let service = "${SERVICE_NAME}"
50
- let account = "${ACCOUNT_NAME}"
111
+ let account = "${account}"
112
+
113
+ let kcPath = CommandLine.arguments.count > 1 ? CommandLine.arguments[1] : ""
114
+ var useKeychain: SecKeychain? = nil
115
+ if !kcPath.isEmpty {
116
+ var kc: SecKeychain?
117
+ if SecKeychainOpen(kcPath, &kc) == errSecSuccess { useKeychain = kc }
118
+ else { exit(1) }
119
+ }
51
120
 
52
- let deleteQuery: [String: Any] = [
121
+ var deleteQuery: [String: Any] = [
53
122
  kSecClass as String: kSecClassGenericPassword,
54
123
  kSecAttrService as String: service,
55
124
  kSecAttrAccount as String: account
56
125
  ]
126
+ if let kc = useKeychain { deleteQuery[kSecMatchSearchList as String] = [kc] }
57
127
  SecItemDelete(deleteQuery as CFDictionary)
58
128
 
59
129
  let pwData = password.data(using: String.Encoding.utf8)!
60
- let addQuery: [String: Any] = [
130
+ var addQuery: [String: Any] = [
61
131
  kSecClass as String: kSecClassGenericPassword,
62
132
  kSecAttrService as String: service,
63
133
  kSecAttrAccount as String: account,
64
134
  kSecValueData as String: pwData
65
135
  ]
136
+ if let kc = useKeychain { addQuery[kSecUseKeychain as String] = kc }
66
137
  let status = SecItemAdd(addQuery as CFDictionary, nil)
67
138
  if status != errSecSuccess { exit(1) }
68
139
  `;
69
140
  try {
70
141
  fsSync.writeFileSync(scriptPath, swiftScript, { mode: 0o600 });
71
- const result = spawnSync('swift', [scriptPath], {
142
+ const result = spawnSync('swift', [scriptPath, keychainPath], {
72
143
  input: secret,
73
144
  encoding: 'utf-8',
74
145
  });
@@ -76,18 +147,18 @@ if status != errSecSuccess { exit(1) }
76
147
  throw new Error(result.stderr || 'Swift keychain helper failed');
77
148
  }
78
149
  }
79
- catch {
80
- // Fallback to security CLI if Swift is unavailable or fails
81
- execFileSync('security', [
82
- 'add-generic-password',
83
- '-a',
84
- ACCOUNT_NAME,
85
- '-s',
86
- SERVICE_NAME,
87
- '-w',
88
- secret,
89
- '-U',
90
- ]);
150
+ catch (err) {
151
+ // Fail closed rather than fall back to `security add-generic-password -w
152
+ // <secret>`: that CLI only accepts the password via the argv `-w` slot
153
+ // (briefly visible to other processes via `ps`) or an interactive prompt,
154
+ // so a fallback would leak the credential into process arguments — exactly
155
+ // what the stdin-based Swift writer above exists to avoid. `swift` ships
156
+ // with the Xcode Command Line Tools, so guide the user to install them.
157
+ const detail = err instanceof Error ? err.message : String(err);
158
+ throw new Error(`Could not store credentials securely: the Swift Keychain helper failed (${detail}). ` +
159
+ `The 'swift' toolchain is required to write credentials without exposing them in ` +
160
+ `process arguments — install the Xcode Command Line Tools with 'xcode-select --install' ` +
161
+ `and try again.`);
91
162
  }
92
163
  finally {
93
164
  try {
@@ -98,26 +169,28 @@ if status != errSecSuccess { exit(1) }
98
169
  }
99
170
  }
100
171
  }
101
- function loadLinuxSecret() {
172
+ function loadLinuxSecret(account) {
102
173
  try {
103
- return execFileSync('secret-tool', ['lookup', 'service', SERVICE_NAME, 'account', ACCOUNT_NAME], { encoding: 'utf-8' }).trim();
174
+ return execFileSync('secret-tool', ['lookup', 'service', SERVICE_NAME, 'account', account], {
175
+ encoding: 'utf-8',
176
+ }).trim();
104
177
  }
105
178
  catch {
106
179
  return null;
107
180
  }
108
181
  }
109
- function saveLinuxSecret(secret) {
110
- execFileSync('secret-tool', ['store', '--label=Fortnox MCP credentials', 'service', SERVICE_NAME, 'account', ACCOUNT_NAME], { input: secret });
182
+ function saveLinuxSecret(account, secret) {
183
+ execFileSync('secret-tool', ['store', '--label=Fortnox MCP credentials', 'service', SERVICE_NAME, 'account', account], { input: secret });
111
184
  }
112
- function loadWindowsSecret() {
185
+ function loadWindowsSecret(file) {
113
186
  try {
114
187
  return execFileSync('powershell', [
115
188
  '-NoProfile',
116
189
  '-NonInteractive',
117
190
  '-Command',
118
191
  [
119
- `if (-not (Test-Path '${WINDOWS_CREDENTIALS_FILE}')) { exit 0 }`,
120
- `$protected = [Convert]::FromBase64String([IO.File]::ReadAllText('${WINDOWS_CREDENTIALS_FILE}'))`,
192
+ `if (-not (Test-Path '${file}')) { exit 0 }`,
193
+ `$protected = [Convert]::FromBase64String([IO.File]::ReadAllText('${file}'))`,
121
194
  '$bytes = [System.Security.Cryptography.ProtectedData]::Unprotect($protected, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)',
122
195
  '[Text.Encoding]::UTF8.GetString($bytes)',
123
196
  ].join('; '),
@@ -127,7 +200,7 @@ function loadWindowsSecret() {
127
200
  return null;
128
201
  }
129
202
  }
130
- function saveWindowsSecret(secret) {
203
+ function saveWindowsSecret(file, secret) {
131
204
  // Read the secret from stdin instead of embedding it in the PowerShell
132
205
  // command string, which would be visible via `ps` / Task Manager.
133
206
  const result = spawnSync('powershell', [
@@ -135,89 +208,151 @@ function saveWindowsSecret(secret) {
135
208
  '-NonInteractive',
136
209
  '-Command',
137
210
  [
138
- `[IO.Directory]::CreateDirectory('${CREDENTIALS_DIR}') | Out-Null`,
211
+ `[IO.Directory]::CreateDirectory('${configDir()}') | Out-Null`,
139
212
  '$plain = [Console]::In.ReadToEnd()',
140
213
  '$bytes = [Text.Encoding]::UTF8.GetBytes($plain)',
141
214
  '$protected = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)',
142
- `[IO.File]::WriteAllText('${WINDOWS_CREDENTIALS_FILE}', [Convert]::ToBase64String($protected), [Text.Encoding]::UTF8)`,
215
+ `[IO.File]::WriteAllText('${file}', [Convert]::ToBase64String($protected), [Text.Encoding]::UTF8)`,
143
216
  ].join('; '),
144
217
  ], { input: secret, encoding: 'utf-8' });
145
218
  if (result.status !== 0) {
146
219
  throw new Error(`Failed to save credentials: ${result.stderr || 'unknown error'}`);
147
220
  }
148
221
  }
149
- async function loadLegacySecret() {
222
+ async function loadLegacyPlaintextSecret() {
150
223
  try {
151
- return await fs.readFile(LEGACY_CREDENTIALS_FILE, 'utf-8');
224
+ return await fs.readFile(legacyCredentialsFile(), 'utf-8');
152
225
  }
153
226
  catch {
154
227
  return null;
155
228
  }
156
229
  }
157
- async function removeLegacySecret() {
230
+ async function removeLegacyPlaintextSecret() {
158
231
  try {
159
- await fs.rm(LEGACY_CREDENTIALS_FILE, { force: true });
232
+ await fs.rm(legacyCredentialsFile(), { force: true });
160
233
  }
161
234
  catch {
162
235
  // ignore cleanup failures
163
236
  }
164
237
  }
165
- export async function loadCredentialBlob() {
238
+ function loadFromBackend(account, windowsFile) {
166
239
  if (process.platform === 'darwin')
167
- return loadMacSecret() ?? (await loadLegacySecret());
240
+ return loadMacSecret(account);
168
241
  if (process.platform === 'win32')
169
- return loadWindowsSecret() ?? (await loadLegacySecret());
170
- return loadLinuxSecret() ?? (await loadLegacySecret());
242
+ return loadWindowsSecret(windowsFile);
243
+ return loadLinuxSecret(account);
171
244
  }
172
- export async function saveCredentialBlob(secret) {
245
+ export async function loadCredentialBlob(profile = DEFAULT_PROFILE) {
246
+ const { normalized, isDefault } = normalizeProfile(profile);
247
+ if (!isDefault) {
248
+ const blob = loadFromBackend(keychainAccount(normalized), windowsCredentialsFile(normalized));
249
+ return { blob, source: blob ? 'new' : null, legacyBlob: null };
250
+ }
251
+ const newBlob = loadFromBackend(keychainAccount(normalized), windowsCredentialsFile(normalized));
252
+ const legacyBlob = loadFromBackend(LEGACY_KEYCHAIN_ACCOUNT, legacyWindowsCredentialsFile());
253
+ if (newBlob && legacyBlob) {
254
+ const { blob, picked } = pickHigher(newBlob, legacyBlob);
255
+ return {
256
+ blob,
257
+ source: picked === 'new' ? 'both-new-preferred' : 'both-legacy-preferred',
258
+ legacyBlob,
259
+ };
260
+ }
261
+ if (newBlob)
262
+ return { blob: newBlob, source: 'new', legacyBlob: null };
263
+ if (legacyBlob)
264
+ return { blob: legacyBlob, source: 'legacy', legacyBlob };
265
+ const plaintext = await loadLegacyPlaintextSecret();
266
+ if (plaintext)
267
+ return { blob: plaintext, source: 'legacy-plaintext', legacyBlob: plaintext };
268
+ return { blob: null, source: null, legacyBlob: null };
269
+ }
270
+ function writeToBackend(account, windowsFile, secret) {
173
271
  if (process.platform === 'darwin') {
174
- saveMacSecret(secret);
272
+ saveMacSecret(account, secret);
175
273
  }
176
274
  else if (process.platform === 'win32') {
177
- saveWindowsSecret(secret);
275
+ saveWindowsSecret(windowsFile, secret);
178
276
  }
179
277
  else {
180
- saveLinuxSecret(secret);
278
+ saveLinuxSecret(account, secret);
279
+ }
280
+ }
281
+ export async function saveCredentialBlob(secret, profile = DEFAULT_PROFILE, options = {}) {
282
+ const { normalized, isDefault } = normalizeProfile(profile);
283
+ // Primary write: always the new per-profile slot (profile:<name> or
284
+ // credentials.<name>.dpapi). For default this is `profile:default` /
285
+ // `credentials.default.dpapi`.
286
+ writeToBackend(keychainAccount(normalized), windowsCredentialsFile(normalized), secret);
287
+ // Compatibility dual-write: for the default profile only, and only when the
288
+ // caller observed a legacy blob during load. This keeps an older 0.1 binary
289
+ // functional during the 0.2.x window without silently creating a legacy slot
290
+ // for users who never had one. Best-effort — a failure here must not break
291
+ // the auth flow, since the authoritative new-slot write already succeeded.
292
+ // REMOVE IN 0.3.0.
293
+ if (LEGACY_DUAL_WRITE && isDefault && options.alsoWriteLegacy) {
294
+ try {
295
+ writeToBackend(LEGACY_KEYCHAIN_ACCOUNT, legacyWindowsCredentialsFile(), secret);
296
+ }
297
+ catch (err) {
298
+ const msg = err instanceof Error ? err.message : String(err);
299
+ console.warn(`Warning: could not mirror credentials to legacy slot: ${msg}`);
300
+ }
301
+ }
302
+ if (isDefault) {
303
+ await removeLegacyPlaintextSecret();
181
304
  }
182
- await removeLegacySecret();
183
305
  }
184
- function deleteMacSecret() {
306
+ function deleteMacSecret(account) {
307
+ const keychainPath = activeKeychainPath();
185
308
  try {
186
- execFileSync('security', ['delete-generic-password', '-a', ACCOUNT_NAME, '-s', SERVICE_NAME]);
309
+ execFileSync('security', [
310
+ 'delete-generic-password',
311
+ '-a',
312
+ account,
313
+ '-s',
314
+ SERVICE_NAME,
315
+ ...(keychainPath ? [keychainPath] : []),
316
+ ]);
187
317
  return true;
188
318
  }
189
319
  catch {
190
320
  return false;
191
321
  }
192
322
  }
193
- function deleteLinuxSecret() {
323
+ function deleteLinuxSecret(account) {
194
324
  try {
195
- execFileSync('secret-tool', ['clear', 'service', SERVICE_NAME, 'account', ACCOUNT_NAME]);
325
+ execFileSync('secret-tool', ['clear', 'service', SERVICE_NAME, 'account', account]);
196
326
  return true;
197
327
  }
198
328
  catch {
199
329
  return false;
200
330
  }
201
331
  }
202
- async function deleteWindowsSecret() {
332
+ async function deleteWindowsSecret(file) {
203
333
  try {
204
- await fs.rm(WINDOWS_CREDENTIALS_FILE, { force: true });
334
+ await fs.rm(file, { force: true });
205
335
  return true;
206
336
  }
207
337
  catch {
208
338
  return false;
209
339
  }
210
340
  }
211
- export async function deleteCredentialBlob() {
212
- let deleted = false;
341
+ async function deleteAtAccount(account, windowsFile) {
213
342
  if (process.platform === 'darwin')
214
- deleted = deleteMacSecret();
215
- else if (process.platform === 'win32')
216
- deleted = await deleteWindowsSecret();
217
- else
218
- deleted = deleteLinuxSecret();
219
- // Also clean up legacy file if it exists
220
- await removeLegacySecret();
221
- return deleted;
343
+ return deleteMacSecret(account);
344
+ if (process.platform === 'win32')
345
+ return deleteWindowsSecret(windowsFile);
346
+ return deleteLinuxSecret(account);
347
+ }
348
+ export async function deleteCredentialBlob(profile = DEFAULT_PROFILE) {
349
+ const { normalized, isDefault } = normalizeProfile(profile);
350
+ if (isDefault) {
351
+ const legacyDeleted = await deleteAtAccount(LEGACY_KEYCHAIN_ACCOUNT, legacyWindowsCredentialsFile());
352
+ const newDeleted = await deleteAtAccount(keychainAccount(normalized), windowsCredentialsFile(normalized));
353
+ await removeLegacyPlaintextSecret();
354
+ return legacyDeleted || newDeleted;
355
+ }
356
+ return deleteAtAccount(keychainAccount(normalized), windowsCredentialsFile(normalized));
222
357
  }
223
358
  //# sourceMappingURL=credentials-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"credentials-store.js","sourceRoot":"","sources":["../src/credentials-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"credentials-store.js","sourceRoot":"","sources":["../src/credentials-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface DateRange {
2
+ from: string;
3
+ to: string;
4
+ }
5
+ export declare function resolvePeriod(period: string, today?: Date): DateRange;
6
+ export declare function applyPeriod(opts: {
7
+ period?: string;
8
+ from?: string;
9
+ to?: string;
10
+ }): {
11
+ from?: string;
12
+ to?: string;
13
+ };
14
+ //# sourceMappingURL=date-periods.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-periods.d.ts","sourceRoot":"","sources":["../src/date-periods.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAyDD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,IAAiB,GAAG,SAAS,CA8CjF;AAGD,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAMA"}
@@ -0,0 +1,103 @@
1
+ // Natural date-period parsing for CLI --period arguments.
2
+ //
3
+ // Periods are CALENDAR-year based. Fortnox supports broken fiscal years;
4
+ // a fiscal-year-aware design is tracked separately — until then "Q1" always
5
+ // means January–March of the named (or current) calendar year.
6
+ const EN_MONTHS = [
7
+ 'january',
8
+ 'february',
9
+ 'march',
10
+ 'april',
11
+ 'may',
12
+ 'june',
13
+ 'july',
14
+ 'august',
15
+ 'september',
16
+ 'october',
17
+ 'november',
18
+ 'december',
19
+ ];
20
+ const SV_MONTHS = [
21
+ 'januari',
22
+ 'februari',
23
+ 'mars',
24
+ 'april',
25
+ 'maj',
26
+ 'juni',
27
+ 'juli',
28
+ 'augusti',
29
+ 'september',
30
+ 'oktober',
31
+ 'november',
32
+ 'december',
33
+ ];
34
+ function iso(year, month, day) {
35
+ return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
36
+ }
37
+ function lastDayOfMonth(year, month) {
38
+ return new Date(year, month, 0).getDate();
39
+ }
40
+ function monthRange(year, month) {
41
+ return { from: iso(year, month, 1), to: iso(year, month, lastDayOfMonth(year, month)) };
42
+ }
43
+ function quarterRange(year, quarter) {
44
+ const startMonth = (quarter - 1) * 3 + 1;
45
+ const endMonth = startMonth + 2;
46
+ return {
47
+ from: iso(year, startMonth, 1),
48
+ to: iso(year, endMonth, lastDayOfMonth(year, endMonth)),
49
+ };
50
+ }
51
+ function yearRange(year) {
52
+ return { from: iso(year, 1, 1), to: iso(year, 12, 31) };
53
+ }
54
+ export function resolvePeriod(period, today = new Date()) {
55
+ const input = period.trim().toLowerCase();
56
+ const year = today.getFullYear();
57
+ const month = today.getMonth() + 1; // 1-based
58
+ const quarter = Math.ceil(month / 3);
59
+ // Q1..Q4, optionally year-qualified (2025-Q3 or 2025Q3)
60
+ const quarterMatch = /^(?:(\d{4})-?)?q([1-4])$/.exec(input);
61
+ if (quarterMatch) {
62
+ return quarterRange(quarterMatch[1] ? parseInt(quarterMatch[1], 10) : year, parseInt(quarterMatch[2], 10));
63
+ }
64
+ // Bare year
65
+ if (/^\d{4}$/.test(input))
66
+ return yearRange(parseInt(input, 10));
67
+ // Month names (English and Swedish), current calendar year
68
+ const enIdx = EN_MONTHS.indexOf(input);
69
+ if (enIdx !== -1)
70
+ return monthRange(year, enIdx + 1);
71
+ const svIdx = SV_MONTHS.indexOf(input);
72
+ if (svIdx !== -1)
73
+ return monthRange(year, svIdx + 1);
74
+ switch (input) {
75
+ case 'this-quarter':
76
+ return quarterRange(year, quarter);
77
+ case 'last-quarter':
78
+ return quarter === 1 ? quarterRange(year - 1, 4) : quarterRange(year, quarter - 1);
79
+ case 'this-month':
80
+ return monthRange(year, month);
81
+ case 'last-month':
82
+ return month === 1 ? monthRange(year - 1, 12) : monthRange(year, month - 1);
83
+ case 'this-year':
84
+ return yearRange(year);
85
+ case 'last-year':
86
+ return yearRange(year - 1);
87
+ case 'ytd':
88
+ return { from: iso(year, 1, 1), to: iso(year, month, today.getDate()) };
89
+ }
90
+ throw new Error(`Unrecognized period "${period}". Supported: Q1–Q4, 2025-Q3, month names (march/mars), ` +
91
+ 'this-quarter, last-quarter, this-month, last-month, this-year, last-year, ytd, or a bare year. ' +
92
+ 'Periods are calendar-year based.');
93
+ }
94
+ // Shared by CLI actions: merge --period into from/to, rejecting ambiguous mixes.
95
+ export function applyPeriod(opts) {
96
+ if (!opts.period)
97
+ return { from: opts.from, to: opts.to };
98
+ if (opts.from || opts.to) {
99
+ throw new Error('--period cannot be combined with --from/--to.');
100
+ }
101
+ return resolvePeriod(opts.period);
102
+ }
103
+ //# sourceMappingURL=date-periods.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-periods.js","sourceRoot":"","sources":["../src/date-periods.ts"],"names":[],"mappings":"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"}
@@ -57,6 +57,15 @@ export declare function isJsonMode(programOpts: {
57
57
  fd: 1;
58
58
  }): boolean;
59
59
  export declare function outputList(data: Record<string, unknown>[], columns: Column[], json: boolean, rawData: unknown, meta?: Record<string, unknown>): void;
60
- export declare function outputDetail(record: Record<string, unknown>, columns: Column[], json: boolean): void;
61
- export declare function outputConfirmation(message: string, json: boolean, rawData: unknown, columns?: Column[]): void;
60
+ export declare function outputDetail(record: Record<string, unknown>, columns: Column[], json: boolean, jsonEnvelopeKey?: string): void;
61
+ export interface ErrorEnvelope {
62
+ error: {
63
+ status?: number;
64
+ message: string;
65
+ hint?: string;
66
+ source: 'fortnox-api' | 'noxctl';
67
+ };
68
+ }
69
+ export declare function errorEnvelope(err: unknown): ErrorEnvelope;
70
+ export declare function outputConfirmation(message: string, json: boolean, rawData: unknown, columns?: Column[], jsonEnvelopeKey?: string): void;
62
71
  //# sourceMappingURL=formatter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"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"}
package/dist/formatter.js CHANGED
@@ -180,16 +180,39 @@ export function outputList(data, columns, json, rawData, meta) {
180
180
  if (metaLine)
181
181
  console.log(metaLine);
182
182
  }
183
- export function outputDetail(record, columns, json) {
183
+ export function outputDetail(record, columns, json, jsonEnvelopeKey) {
184
184
  if (json) {
185
- console.log(JSON.stringify(record, null, 2));
185
+ const payload = jsonEnvelopeKey ? { [jsonEnvelopeKey]: record } : record;
186
+ console.log(JSON.stringify(payload, null, 2));
186
187
  return;
187
188
  }
188
189
  console.log(formatDetail(record, columns));
189
190
  }
190
- export function outputConfirmation(message, json, rawData, columns) {
191
+ // Duck-typed rather than instanceof so the formatter stays decoupled from
192
+ // fortnox-client (and survives errors crossing module-instance boundaries).
193
+ export function errorEnvelope(err) {
194
+ if (err instanceof Error &&
195
+ err.name === 'FortnoxApiError' &&
196
+ 'statusCode' in err &&
197
+ 'fortnoxMessage' in err) {
198
+ const apiErr = err;
199
+ return {
200
+ error: {
201
+ status: apiErr.statusCode,
202
+ message: apiErr.fortnoxMessage,
203
+ ...(apiErr.hint ? { hint: apiErr.hint } : {}),
204
+ source: 'fortnox-api',
205
+ },
206
+ };
207
+ }
208
+ return {
209
+ error: { message: err instanceof Error ? err.message : String(err), source: 'noxctl' },
210
+ };
211
+ }
212
+ export function outputConfirmation(message, json, rawData, columns, jsonEnvelopeKey) {
191
213
  if (json) {
192
- console.log(JSON.stringify(rawData, null, 2));
214
+ const payload = jsonEnvelopeKey ? { [jsonEnvelopeKey]: rawData } : rawData;
215
+ console.log(JSON.stringify(payload, null, 2));
193
216
  return;
194
217
  }
195
218
  console.log(message);