kassza 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +283 -18
- package/agents/README.md +20 -5
- package/agents/api.md +165 -2
- package/agents/pitfalls.md +16 -3
- package/agents/recipes.md +215 -45
- package/agents/skills/kassza/SKILL.md +11 -8
- package/dist/cli.cjs +552 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +552 -0
- package/dist/client-6MkeuFqD.d.ts +268 -0
- package/dist/client-B5FryKxJ.d.cts +268 -0
- package/dist/{client-CsmGIFRS.cjs → client-C-PdfL_P.cjs} +235 -597
- package/dist/{client-CL3fuH6A.js → client-CfaS1-Jx.js} +35 -403
- package/dist/context-BxQlIlug.d.ts +425 -0
- package/dist/context-DXNbTPwz.d.cts +425 -0
- package/dist/{create-once-CqdYmQD8.js → create-once-Cz621l8r.js} +3 -3
- package/dist/{create-once-D0pwuenl.cjs → create-once-DgMtKKcG.cjs} +8 -2
- package/dist/{crypto-B8lERvR1.cjs → crypto-Dpn4FHQ6.cjs} +9 -0
- package/dist/{crypto-DK_osBVy.js → crypto-usDg8L2K.js} +4 -1
- package/dist/data-link/index.cjs +232 -0
- package/dist/data-link/index.d.cts +79 -0
- package/dist/data-link/index.d.ts +79 -0
- package/dist/data-link/index.js +224 -0
- package/dist/delegation/index.cjs +34 -33
- package/dist/delegation/index.d.cts +2 -1
- package/dist/delegation/index.d.ts +2 -1
- package/dist/delegation/index.js +3 -2
- package/dist/get-Byc9T5WO.d.cts +151 -0
- package/dist/get-Byc9T5WO.d.ts +151 -0
- package/dist/get-Csm2n0rZ.cjs +445 -0
- package/dist/get-DT3JEgQ6.js +380 -0
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -3
- package/dist/{issue-BsyYdrIG.cjs → issue-AGRohSm7.cjs} +48 -665
- package/dist/{issue-C6Fcjvln.js → issue-DtSLpVdJ.js} +5 -502
- package/dist/mcp/index.cjs +10 -0
- package/dist/mcp/index.d.cts +71 -0
- package/dist/mcp/index.d.ts +71 -0
- package/dist/mcp/index.js +2 -0
- package/dist/money/index.cjs +1 -1
- package/dist/money/index.js +1 -1
- package/dist/{money-DrhGkWHW.cjs → money-BxytJW6f.cjs} +25 -9
- package/dist/{money-DAA7OwiO.js → money-t7uy1n91.js} +25 -9
- package/dist/nav/index.cjs +1046 -0
- package/dist/nav/index.d.cts +247 -0
- package/dist/nav/index.d.ts +247 -0
- package/dist/nav/index.js +1012 -0
- package/dist/nav-BEwcX7-N.cjs +203 -0
- package/dist/nav-CK1WgYo7.js +156 -0
- package/dist/parse-BJj-aqKy.cjs +380 -0
- package/dist/parse-C608ehqM.js +291 -0
- package/dist/payments/barion.cjs +1 -1
- package/dist/payments/barion.d.cts +2 -2
- package/dist/payments/barion.d.ts +2 -2
- package/dist/payments/barion.js +1 -1
- package/dist/payments/index.cjs +2 -2
- package/dist/payments/index.d.cts +3 -3
- package/dist/payments/index.d.ts +3 -3
- package/dist/payments/index.js +2 -2
- package/dist/payments/paypal.cjs +1 -1
- package/dist/payments/paypal.d.cts +2 -2
- package/dist/payments/paypal.d.ts +2 -2
- package/dist/payments/paypal.js +1 -1
- package/dist/payments/revolut.cjs +2 -2
- package/dist/payments/revolut.d.cts +2 -2
- package/dist/payments/revolut.d.ts +2 -2
- package/dist/payments/revolut.js +2 -2
- package/dist/payments/simplepay.cjs +2 -2
- package/dist/payments/simplepay.d.cts +2 -2
- package/dist/payments/simplepay.d.ts +2 -2
- package/dist/payments/simplepay.js +2 -2
- package/dist/payments/stripe.cjs +2 -2
- package/dist/payments/stripe.d.cts +2 -2
- package/dist/payments/stripe.d.ts +2 -2
- package/dist/payments/stripe.js +2 -2
- package/dist/reports/index.cjs +11 -164
- package/dist/reports/index.js +1 -154
- package/dist/request-body-CH6ZGOrq.cjs +34 -0
- package/dist/request-body-uZHMRlQr.js +29 -0
- package/dist/serialize-BXOww1Uq.cjs +286 -0
- package/dist/serialize-DSE_o9RK.js +215 -0
- package/dist/server-C6PoNkdD.cjs +1557 -0
- package/dist/server-Cd8HB661.js +1504 -0
- package/dist/testing/index.cjs +1476 -3
- package/dist/testing/index.d.cts +194 -2
- package/dist/testing/index.d.ts +194 -2
- package/dist/testing/index.js +1477 -6
- package/dist/{types-COf1LAD-.d.cts → types-BVeR7KyU.d.cts} +1 -1
- package/dist/{types-COf1LAD-.d.ts → types-BVeR7KyU.d.ts} +1 -1
- package/dist/{webhook-C76bV-4R.cjs → webhook-81giRMdQ.cjs} +3 -25
- package/dist/{webhook-q2OzNwd7.d.ts → webhook-CpCMHhn2.d.ts} +1 -1
- package/dist/{webhook-CfaDgcL0.d.cts → webhook-CpMJuP6c.d.cts} +1 -1
- package/dist/{webhook-D4lvtex1.js → webhook-CrEGohbg.js} +3 -25
- package/llms.txt +4 -4
- package/package.json +43 -1
- package/dist/client-CickRRFr.d.ts +0 -838
- package/dist/client-DvWZoX_E.d.cts +0 -838
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const require_errors = require("./errors-DxeuTslP.cjs");
|
|
3
|
+
const require_client = require("./client-C-PdfL_P.cjs");
|
|
4
|
+
const require_serialize = require("./serialize-BXOww1Uq.cjs");
|
|
5
|
+
const require_issue = require("./issue-AGRohSm7.cjs");
|
|
6
|
+
const require_nav = require("./nav-BEwcX7-N.cjs");
|
|
7
|
+
const require_server = require("./server-C6PoNkdD.cjs");
|
|
8
|
+
let node_fs_promises = require("node:fs/promises");
|
|
9
|
+
let node_readline = require("node:readline");
|
|
10
|
+
//#region src/cli/args.ts
|
|
11
|
+
var CliUsageError = class extends Error {
|
|
12
|
+
name = "CliUsageError";
|
|
13
|
+
};
|
|
14
|
+
function splitFlag(token) {
|
|
15
|
+
const body = token.slice(2);
|
|
16
|
+
const separator = body.indexOf("=");
|
|
17
|
+
return separator === -1 ? {
|
|
18
|
+
name: body,
|
|
19
|
+
inline: void 0
|
|
20
|
+
} : {
|
|
21
|
+
name: body.slice(0, separator),
|
|
22
|
+
inline: body.slice(separator + 1)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function parseArgs(argv, spec = {}) {
|
|
26
|
+
const booleans = new Set(spec.booleans ?? []);
|
|
27
|
+
const values = new Set(spec.values ?? []);
|
|
28
|
+
const positionals = [];
|
|
29
|
+
const flags = /* @__PURE__ */ new Map();
|
|
30
|
+
for (let index = 0; index < argv.length; index++) {
|
|
31
|
+
const token = argv[index] ?? "";
|
|
32
|
+
if (token === "--") {
|
|
33
|
+
positionals.push(...argv.slice(index + 1));
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
if (!token.startsWith("--") || token === "-") {
|
|
37
|
+
positionals.push(token);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const { name, inline } = splitFlag(token);
|
|
41
|
+
if (booleans.has(name)) {
|
|
42
|
+
if (inline !== void 0) throw new CliUsageError(`A --${name} kapcsolónak nincs értéke.`);
|
|
43
|
+
flags.set(name, true);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (!values.has(name)) throw new CliUsageError(`Ismeretlen kapcsoló: --${name}`);
|
|
47
|
+
const value = inline ?? argv[index + 1];
|
|
48
|
+
if (value === void 0 || inline === void 0 && value.startsWith("--")) throw new CliUsageError(`A --${name} kapcsolóhoz érték kell.`);
|
|
49
|
+
if (inline === void 0) index++;
|
|
50
|
+
flags.set(name, value);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
positionals,
|
|
54
|
+
flags
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function flagValue(args, name) {
|
|
58
|
+
const value = args.flags.get(name);
|
|
59
|
+
return typeof value === "string" ? value : void 0;
|
|
60
|
+
}
|
|
61
|
+
function hasFlag(args, name) {
|
|
62
|
+
return args.flags.get(name) === true;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/cli/io.ts
|
|
66
|
+
var CliError = class extends Error {
|
|
67
|
+
name = "CliError";
|
|
68
|
+
};
|
|
69
|
+
const AGENT_KEY_ENV = "SZAMLAZZ_AGENT_KEY";
|
|
70
|
+
const ALLOW_WRITE_ENV = "KASSZA_MCP_ALLOW_WRITE";
|
|
71
|
+
function agentKeyOf(io) {
|
|
72
|
+
const key = io.env[AGENT_KEY_ENV]?.trim();
|
|
73
|
+
if (!key) throw new require_errors.SzamlazzError(`Hiányzik az Agent kulcs: állítsd be a ${AGENT_KEY_ENV} környezeti változót.`, { category: "configuration" });
|
|
74
|
+
return key;
|
|
75
|
+
}
|
|
76
|
+
function cliKassza(io, defaults = {}) {
|
|
77
|
+
return require_client.createKassza({
|
|
78
|
+
agentKey: agentKeyOf(io),
|
|
79
|
+
defaults,
|
|
80
|
+
...io.fetch ? { fetch: io.fetch } : {}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
async function readText(io, path) {
|
|
84
|
+
if (path === "-") return io.readStdin();
|
|
85
|
+
try {
|
|
86
|
+
return await io.readFile(path);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new CliError(`A(z) ${path} fájl nem olvasható: ${error instanceof Error ? error.message : String(error)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function readJson(io, path) {
|
|
92
|
+
const text = await readText(io, path);
|
|
93
|
+
try {
|
|
94
|
+
return JSON.parse(text);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
throw new CliError(`A(z) ${path} nem érvényes JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function isRecord(value) {
|
|
100
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
101
|
+
}
|
|
102
|
+
async function readDefaults(io, path) {
|
|
103
|
+
if (path === void 0) return {};
|
|
104
|
+
const value = await readJson(io, path);
|
|
105
|
+
if (!isRecord(value)) throw new CliError(`A(z) ${path} fájlnak KasszaDefaults objektumot kell tartalmaznia.`);
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
function printJson(io, value) {
|
|
109
|
+
io.stdout(`${JSON.stringify(value, (_key, entry) => entry instanceof Uint8Array ? void 0 : entry, 2)}\n`);
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/cli/commands.ts
|
|
113
|
+
const MASKED_CREDENTIALS = [require_serialize.el("szamlaagentkulcs", "***")];
|
|
114
|
+
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
115
|
+
async function verifyCommand(io) {
|
|
116
|
+
if (await cliKassza(io).verifyCredentials()) {
|
|
117
|
+
io.stdout("Az Agent kulcs érvényes: a Számlázz.hu elfogadta.\n");
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
io.stderr("A Számlázz.hu elutasította az Agent kulcsot (3). Ellenőrizd, hogy kisbetűs-e, és jó fiókhoz tartozik-e.\n");
|
|
121
|
+
return 1;
|
|
122
|
+
}
|
|
123
|
+
function assertDocumentShape(input, type) {
|
|
124
|
+
if (!isRecord(input)) throw new CliError("A bizonylat adatainak objektumnak kell lenniük.");
|
|
125
|
+
if (!Array.isArray(input.items)) throw new CliError("Az items mezőnek tételek tömbjének kell lennie.");
|
|
126
|
+
if (type === "invoice" && !isRecord(input.buyer)) throw new CliError("Számlához a buyer (vevő) objektum kötelező.");
|
|
127
|
+
}
|
|
128
|
+
function previewSource(value, defaults) {
|
|
129
|
+
if (!isRecord(value)) throw new CliError("A bemeneti JSON-nak objektumnak kell lennie.");
|
|
130
|
+
if (isRecord(value.input)) {
|
|
131
|
+
const embedded = isRecord(value.defaults) ? value.defaults : {};
|
|
132
|
+
return {
|
|
133
|
+
defaults: defaults ?? embedded,
|
|
134
|
+
input: value.input
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
defaults: defaults ?? {},
|
|
139
|
+
input: value
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async function xmlPreviewCommand(io, args) {
|
|
143
|
+
const path = args.positionals[2];
|
|
144
|
+
if (path === void 0) throw new CliUsageError("Add meg a bemeneti JSON fájlt: kassza xml preview <fájl.json|->");
|
|
145
|
+
const type = flagValue(args, "type") ?? "invoice";
|
|
146
|
+
if (type !== "invoice" && type !== "receipt") throw new CliUsageError(`A --type értéke invoice vagy receipt lehet, kapott: ${type}`);
|
|
147
|
+
const defaultsPath = flagValue(args, "defaults");
|
|
148
|
+
const source = previewSource(await readJson(io, path), defaultsPath === void 0 ? void 0 : await readDefaults(io, defaultsPath));
|
|
149
|
+
assertDocumentShape(source.input, type);
|
|
150
|
+
const xml = type === "invoice" ? require_client.buildCreateInvoiceXml(MASKED_CREDENTIALS, source.defaults.invoice ?? {}, source.input, io.now?.() ?? /* @__PURE__ */ new Date()) : require_issue.buildCreateReceiptXml(MASKED_CREDENTIALS, source.defaults.receipt ?? {}, source.input);
|
|
151
|
+
io.stdout(xml);
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
function invoiceReferenceOf(args) {
|
|
155
|
+
const number = args.positionals[2];
|
|
156
|
+
const orderNumber = flagValue(args, "order");
|
|
157
|
+
const externalId = flagValue(args, "external");
|
|
158
|
+
if ([
|
|
159
|
+
number,
|
|
160
|
+
orderNumber,
|
|
161
|
+
externalId
|
|
162
|
+
].filter((value) => value !== void 0).length !== 1) throw new CliUsageError("Pontosan egyet adj meg: kassza invoice get <számlaszám> | --order <rendelésszám> | --external <azonosító>");
|
|
163
|
+
if (number !== void 0) return number;
|
|
164
|
+
if (orderNumber !== void 0) return { orderNumber };
|
|
165
|
+
return { externalId: externalId ?? "" };
|
|
166
|
+
}
|
|
167
|
+
async function invoiceGetCommand(io, args) {
|
|
168
|
+
const reference = invoiceReferenceOf(args);
|
|
169
|
+
const invoice = await cliKassza(io).invoices.find(reference, { includePdf: false });
|
|
170
|
+
if (!invoice) {
|
|
171
|
+
io.stderr("Nincs ilyen számla a Számlázz.hu fiókban.\n");
|
|
172
|
+
return 1;
|
|
173
|
+
}
|
|
174
|
+
printJson(io, invoice);
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
async function receiptGetCommand(io, args) {
|
|
178
|
+
const number = args.positionals[2];
|
|
179
|
+
const orderNumber = flagValue(args, "order");
|
|
180
|
+
if (number === void 0 === (orderNumber === void 0)) throw new CliUsageError("Pontosan egyet adj meg: kassza receipt get <nyugtaszám> | --order <rendelésszám>");
|
|
181
|
+
const receipt = await cliKassza(io).receipts.find(number !== void 0 ? {
|
|
182
|
+
receiptNumber: number,
|
|
183
|
+
downloadPdf: false
|
|
184
|
+
} : {
|
|
185
|
+
orderNumber: orderNumber ?? "",
|
|
186
|
+
downloadPdf: false
|
|
187
|
+
});
|
|
188
|
+
if (!receipt) {
|
|
189
|
+
io.stderr("Nincs ilyen nyugta a Számlázz.hu fiókban.\n");
|
|
190
|
+
return 1;
|
|
191
|
+
}
|
|
192
|
+
printJson(io, receipt);
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
function optionalDateFlag(args, name) {
|
|
196
|
+
const value = flagValue(args, name);
|
|
197
|
+
if (value !== void 0 && !DATE_PATTERN.test(value)) throw new CliUsageError(`A --${name} értékét ÉÉÉÉ-HH-NN formában add meg, kapott: ${value}`);
|
|
198
|
+
return value;
|
|
199
|
+
}
|
|
200
|
+
function receiptListOf(value, path) {
|
|
201
|
+
const list = isRecord(value) && Array.isArray(value.receipts) ? value.receipts : value;
|
|
202
|
+
if (!Array.isArray(list)) throw new CliError(`A(z) ${path} fájlban nyugták tömbjét (vagy { "receipts": [...] } objektumot) várjuk.`);
|
|
203
|
+
return list.map((entry, index) => {
|
|
204
|
+
if (!(isRecord(entry) && typeof entry.number === "string" && typeof entry.issueDate === "string" && typeof entry.currency === "string" && (entry.type === "receipt" || entry.type === "reversal") && Array.isArray(entry.items))) throw new CliError(`A(z) ${index + 1}. elem nem kassza nyugta (number, issueDate, currency, type és items mező kell).`);
|
|
205
|
+
return entry;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async function navSummaryCommand(io, args) {
|
|
209
|
+
const path = flagValue(args, "file");
|
|
210
|
+
if (path === void 0) throw new CliUsageError("Add meg a nyugtákat tartalmazó JSON fájlt: --file <nyugták.json|->");
|
|
211
|
+
const from = optionalDateFlag(args, "from");
|
|
212
|
+
const to = optionalDateFlag(args, "to");
|
|
213
|
+
if (from !== void 0 && to !== void 0 && from > to) throw new CliUsageError("A --from nem lehet későbbi a --to dátumnál.");
|
|
214
|
+
const receipts = receiptListOf(await readJson(io, path), path).filter((receipt) => (from === void 0 || receipt.issueDate >= from) && (to === void 0 || receipt.issueDate <= to));
|
|
215
|
+
const reports = require_nav.navDailyReports(receipts, { includeTest: hasFlag(args, "include-test") });
|
|
216
|
+
if (hasFlag(args, "json")) {
|
|
217
|
+
printJson(io, reports);
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
io.stdout(reports.length === 0 ? "Nincs összesíthető nyugta a megadott időszakban.\n" : `${reports.map((report) => require_nav.describeNavReport(report)).join("\n\n")}\n`);
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/cli/doctor.ts
|
|
225
|
+
const MINIMUM_NODE_MAJOR = 22;
|
|
226
|
+
const CLOCK_OK_SECONDS = 2;
|
|
227
|
+
const CLOCK_WARN_SECONDS = 30;
|
|
228
|
+
const DATE_HEADER_PRECISION_MS = 500;
|
|
229
|
+
const secondsFormat = new Intl.NumberFormat("hu-HU", { maximumFractionDigits: 1 });
|
|
230
|
+
function check(id, status, message) {
|
|
231
|
+
return {
|
|
232
|
+
id,
|
|
233
|
+
status,
|
|
234
|
+
message
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function nodeCheck(version) {
|
|
238
|
+
const major = Number(version.replace(/^v/, "").split(".")[0]);
|
|
239
|
+
if (Number.isInteger(major) && major >= MINIMUM_NODE_MAJOR) return check("node", "ok", `Node.js ${version}`);
|
|
240
|
+
return check("node", "fail", `Node.js ${version}: a kasszához legalább Node.js ${MINIMUM_NODE_MAJOR} kell.`);
|
|
241
|
+
}
|
|
242
|
+
function timezoneCheck() {
|
|
243
|
+
try {
|
|
244
|
+
new Intl.DateTimeFormat("hu-HU", { timeZone: "Europe/Budapest" }).format(/* @__PURE__ */ new Date());
|
|
245
|
+
return check("timezone", "ok", "Időzóna-adatok: Europe/Budapest elérhető.");
|
|
246
|
+
} catch {
|
|
247
|
+
return check("timezone", "fail", "Időzóna-adatok: az Europe/Budapest időzóna nem érhető el (teljes ICU-val fordított Node.js kell).");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function keyCheck(raw) {
|
|
251
|
+
const key = raw?.trim();
|
|
252
|
+
if (!key) return { check: check("agent-key", "fail", `Agent kulcs: hiányzik a ${AGENT_KEY_ENV} környezeti változó.`) };
|
|
253
|
+
if (key !== key.toLowerCase()) return { check: check("agent-key", "fail", "Agent kulcs: nagybetűt tartalmaz. A Számlázz.hu csak kisbetűs kulcsot fogad el, másold ki újra.") };
|
|
254
|
+
if (key !== raw) return {
|
|
255
|
+
key,
|
|
256
|
+
check: check("agent-key", "warn", `Agent kulcs: ${key.length} karakter, de az elején vagy a végén szóköz van (a kassza levágja).`)
|
|
257
|
+
};
|
|
258
|
+
return {
|
|
259
|
+
key,
|
|
260
|
+
check: check("agent-key", "ok", `Agent kulcs: ${key.length} karakter, kisbetűs.`)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function capturingFetch(base, now, samples) {
|
|
264
|
+
const wrapped = async (input, init) => {
|
|
265
|
+
const sentAt = now().getTime();
|
|
266
|
+
const response = await base(input, init);
|
|
267
|
+
samples.push({
|
|
268
|
+
date: response.headers.get("date") ?? void 0,
|
|
269
|
+
sentAt,
|
|
270
|
+
receivedAt: now().getTime(),
|
|
271
|
+
setCookie: response.headers.get("set-cookie") !== null
|
|
272
|
+
});
|
|
273
|
+
return response;
|
|
274
|
+
};
|
|
275
|
+
return wrapped;
|
|
276
|
+
}
|
|
277
|
+
async function authCheck(kassza) {
|
|
278
|
+
try {
|
|
279
|
+
return await kassza.verifyCredentials() ? check("auth", "ok", "Hitelesítés: a Számlázz.hu elfogadta az Agent kulcsot.") : check("auth", "fail", "Hitelesítés: a Számlázz.hu elutasította az Agent kulcsot (3).");
|
|
280
|
+
} catch (error) {
|
|
281
|
+
return check("auth", "fail", `Hitelesítés: a próba nem sikerült. ${error instanceof Error ? error.message : String(error)}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function clockCheck(samples) {
|
|
285
|
+
const sample = samples.find((entry) => entry.date !== void 0);
|
|
286
|
+
const serverTime = sample?.date === void 0 ? NaN : Date.parse(sample.date);
|
|
287
|
+
if (!sample || Number.isNaN(serverTime)) return check("clock", "skip", "Óra: a Számlázz.hu válaszában nem volt Date fejléc.");
|
|
288
|
+
const skewSeconds = ((sample.sentAt + sample.receivedAt) / 2 - (serverTime + DATE_HEADER_PRECISION_MS)) / 1e3;
|
|
289
|
+
const absolute = Math.abs(skewSeconds);
|
|
290
|
+
const direction = skewSeconds > 0 ? "siet" : "késik";
|
|
291
|
+
const amount = `${secondsFormat.format(absolute)} mp`;
|
|
292
|
+
if (absolute <= CLOCK_OK_SECONDS) return check("clock", "ok", `Óra: legfeljebb ${amount} eltérés a Számlázz.hu idejétől.`);
|
|
293
|
+
const risk = `éjfél körül ${amount} ideig rossz napra kerülhet a bizonylat kelte (352-es hiba). Szinkronizáld az órát (NTP).`;
|
|
294
|
+
return absolute <= CLOCK_WARN_SECONDS ? check("clock", "warn", `Óra: a gép órája ${amount}-et ${direction}; ${risk}`) : check("clock", "fail", `Óra: a gép órája ${amount}-et ${direction}; ${risk}`);
|
|
295
|
+
}
|
|
296
|
+
function sessionCheck(samples) {
|
|
297
|
+
if (samples.length === 0) return check("session", "skip", "Munkamenet: nem volt válasz.");
|
|
298
|
+
return samples.some((sample) => sample.setCookie) ? check("session", "ok", "Munkamenet: a Számlázz.hu munkamenet-sütit adott, a kassza újrahasznosítja.") : check("session", "warn", "Munkamenet: nem érkezett munkamenet-süti. Ha proxy vagy tűzfal szűri a Set-Cookie fejlécet, minden kérés új bejelentkezés lesz.");
|
|
299
|
+
}
|
|
300
|
+
async function accountCheck(kassza, options) {
|
|
301
|
+
try {
|
|
302
|
+
if (options.invoiceNumber !== void 0) {
|
|
303
|
+
const invoice = await kassza.invoices.find(options.invoiceNumber, { includePdf: false });
|
|
304
|
+
if (!invoice) return check("account", "warn", `Fiók típusa: a(z) ${options.invoiceNumber} számla nem található.`);
|
|
305
|
+
return accountOf(invoice.header.test);
|
|
306
|
+
}
|
|
307
|
+
if (options.receiptNumber !== void 0) {
|
|
308
|
+
const receipt = await kassza.receipts.find({
|
|
309
|
+
receiptNumber: options.receiptNumber,
|
|
310
|
+
downloadPdf: false
|
|
311
|
+
});
|
|
312
|
+
if (!receipt) return check("account", "warn", `Fiók típusa: a(z) ${options.receiptNumber} nyugta nem található.`);
|
|
313
|
+
return accountOf(receipt.isTest);
|
|
314
|
+
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
return check("account", "warn", `Fiók típusa: nem sikerült megállapítani. ${error instanceof Error ? error.message : String(error)}`);
|
|
317
|
+
}
|
|
318
|
+
return check("account", "skip", "Fiók típusa: add meg egy meglévő bizonylat számát (--invoice vagy --receipt), abból kiderül, hogy teszt- vagy éles fiók.");
|
|
319
|
+
}
|
|
320
|
+
function accountOf(test) {
|
|
321
|
+
if (test === true) return check("account", "ok", "Fiók típusa: tesztfiók.");
|
|
322
|
+
if (test === false) return check("account", "ok", "Fiók típusa: éles fiók, a kiállított bizonylatok valódiak.");
|
|
323
|
+
return check("account", "skip", "Fiók típusa: a bizonylatból nem derült ki.");
|
|
324
|
+
}
|
|
325
|
+
async function runDoctor(io, options = {}) {
|
|
326
|
+
const now = io.now ?? (() => /* @__PURE__ */ new Date());
|
|
327
|
+
const { check: keyResult, key } = keyCheck(io.env[AGENT_KEY_ENV]);
|
|
328
|
+
const checks = [
|
|
329
|
+
nodeCheck(io.nodeVersion),
|
|
330
|
+
timezoneCheck(),
|
|
331
|
+
keyResult
|
|
332
|
+
];
|
|
333
|
+
if (key === void 0) return [...checks, check("auth", "skip", "Hitelesítés: Agent kulcs nélkül nem próbálható.")];
|
|
334
|
+
const samples = [];
|
|
335
|
+
const kassza = require_client.createKassza({
|
|
336
|
+
agentKey: key,
|
|
337
|
+
fetch: capturingFetch(io.fetch ?? globalThis.fetch, now, samples),
|
|
338
|
+
maxAttempts: 1
|
|
339
|
+
});
|
|
340
|
+
const auth = await authCheck(kassza);
|
|
341
|
+
const account = auth.status === "ok" ? await accountCheck(kassza, options) : check("account", "skip", "Fiók típusa: sikertelen hitelesítés miatt kimaradt.");
|
|
342
|
+
return [
|
|
343
|
+
...checks,
|
|
344
|
+
auth,
|
|
345
|
+
clockCheck(samples),
|
|
346
|
+
sessionCheck(samples),
|
|
347
|
+
account
|
|
348
|
+
];
|
|
349
|
+
}
|
|
350
|
+
const SYMBOLS = {
|
|
351
|
+
ok: "✓",
|
|
352
|
+
warn: "!",
|
|
353
|
+
fail: "✗",
|
|
354
|
+
skip: "-"
|
|
355
|
+
};
|
|
356
|
+
function formatDoctor(checks) {
|
|
357
|
+
const failures = checks.filter((entry) => entry.status === "fail").length;
|
|
358
|
+
const warnings = checks.filter((entry) => entry.status === "warn").length;
|
|
359
|
+
return [
|
|
360
|
+
...checks.map((entry) => `${SYMBOLS[entry.status]} ${entry.message}`),
|
|
361
|
+
"",
|
|
362
|
+
`Összegzés: ${failures} hiba, ${warnings} figyelmeztetés.`,
|
|
363
|
+
""
|
|
364
|
+
].join("\n");
|
|
365
|
+
}
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/cli/mcp.ts
|
|
368
|
+
const TRUTHY = /* @__PURE__ */ new Set([
|
|
369
|
+
"1",
|
|
370
|
+
"true",
|
|
371
|
+
"yes",
|
|
372
|
+
"igen"
|
|
373
|
+
]);
|
|
374
|
+
function writeAllowed(io, args) {
|
|
375
|
+
if (hasFlag(args, "allow-write")) return true;
|
|
376
|
+
const value = io.env[ALLOW_WRITE_ENV]?.trim().toLowerCase();
|
|
377
|
+
return value !== void 0 && TRUTHY.has(value);
|
|
378
|
+
}
|
|
379
|
+
async function mcpCommand(io, args) {
|
|
380
|
+
const allowWrite = writeAllowed(io, args);
|
|
381
|
+
const agentKey = io.env[AGENT_KEY_ENV]?.trim();
|
|
382
|
+
const server = require_server.createKasszaMcpServer({
|
|
383
|
+
...agentKey ? { agentKey } : {},
|
|
384
|
+
...io.fetch ? { fetch: io.fetch } : {},
|
|
385
|
+
...io.now ? { now: io.now } : {},
|
|
386
|
+
defaults: await readDefaults(io, flagValue(args, "defaults")),
|
|
387
|
+
allowWrite
|
|
388
|
+
});
|
|
389
|
+
io.stderr(`kassza MCP szerver fut (${allowWrite ? "írás engedélyezve" : "csak olvasás"}).${agentKey ? "" : ` Figyelem: hiányzik a ${AGENT_KEY_ENV}, a Számlázz.hu-t hívó eszközök hibát adnak.`}\n`);
|
|
390
|
+
const connection = server.connect();
|
|
391
|
+
for await (const line of io.stdinLines()) {
|
|
392
|
+
const response = await connection.handleLine(line);
|
|
393
|
+
if (response !== void 0) io.stdout(`${response}\n`);
|
|
394
|
+
}
|
|
395
|
+
return 0;
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/cli/run.ts
|
|
399
|
+
const CLI_USAGE = `kassza ${require_server.KASSZA_VERSION}: Számlázz.hu Számla Agent parancssor
|
|
400
|
+
|
|
401
|
+
Használat: kassza <parancs> [kapcsolók]
|
|
402
|
+
|
|
403
|
+
Parancsok:
|
|
404
|
+
doctor [--invoice <szám> | --receipt <szám>] [--json]
|
|
405
|
+
A környezet, az Agent kulcs, az óra és a Számlázz.hu kapcsolat ellenőrzése.
|
|
406
|
+
verify
|
|
407
|
+
Az Agent kulcs ellenőrzése a Számlázz.hu-n.
|
|
408
|
+
xml preview <fájl.json|-> [--type invoice|receipt] [--defaults <fájl.json>]
|
|
409
|
+
Kiírja azt az Agent XML-t, amit a kassza küldene (az Agent kulcs nélkül).
|
|
410
|
+
invoice get <számlaszám> | --order <rendelésszám> | --external <azonosító>
|
|
411
|
+
Számla lekérdezése JSON-ban.
|
|
412
|
+
receipt get <nyugtaszám> | --order <rendelésszám>
|
|
413
|
+
Nyugta lekérdezése JSON-ban.
|
|
414
|
+
nav summary --file <nyugták.json|-> [--from ÉÉÉÉ-HH-NN] [--to ÉÉÉÉ-HH-NN] [--include-test] [--json]
|
|
415
|
+
Napi, áfakategóriánkénti NAV nyugta-összesítő kassza nyugtákból.
|
|
416
|
+
mcp [--allow-write] [--defaults <fájl.json>]
|
|
417
|
+
MCP szerver stdio-n (Claude, Cursor és más MCP kliensek számára).
|
|
418
|
+
|
|
419
|
+
Környezeti változók:
|
|
420
|
+
SZAMLAZZ_AGENT_KEY a Számla Agent kulcs (kisbetűs)
|
|
421
|
+
KASSZA_MCP_ALLOW_WRITE 1 esetén az MCP szerver kiállíthat és sztornózhat
|
|
422
|
+
`;
|
|
423
|
+
const SPECS = {
|
|
424
|
+
doctor: {
|
|
425
|
+
booleans: ["json"],
|
|
426
|
+
values: ["invoice", "receipt"]
|
|
427
|
+
},
|
|
428
|
+
verify: {},
|
|
429
|
+
"xml preview": { values: ["type", "defaults"] },
|
|
430
|
+
"invoice get": { values: ["order", "external"] },
|
|
431
|
+
"receipt get": { values: ["order"] },
|
|
432
|
+
"nav summary": {
|
|
433
|
+
booleans: ["include-test", "json"],
|
|
434
|
+
values: [
|
|
435
|
+
"file",
|
|
436
|
+
"from",
|
|
437
|
+
"to"
|
|
438
|
+
]
|
|
439
|
+
},
|
|
440
|
+
mcp: {
|
|
441
|
+
booleans: ["allow-write"],
|
|
442
|
+
values: ["defaults"]
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
function commandOf(argv) {
|
|
446
|
+
const [first, second] = argv;
|
|
447
|
+
if (first === void 0) return void 0;
|
|
448
|
+
const pair = second === void 0 ? void 0 : `${first} ${second}`;
|
|
449
|
+
if (pair !== void 0 && pair in SPECS) return pair;
|
|
450
|
+
return first in SPECS ? first : void 0;
|
|
451
|
+
}
|
|
452
|
+
async function doctorCommand(io, args) {
|
|
453
|
+
const checks = await runDoctor(io, {
|
|
454
|
+
invoiceNumber: flagValue(args, "invoice"),
|
|
455
|
+
receiptNumber: flagValue(args, "receipt")
|
|
456
|
+
});
|
|
457
|
+
if (hasFlag(args, "json")) printJson(io, { checks });
|
|
458
|
+
else io.stdout(`kassza doctor ${require_server.KASSZA_VERSION}\n${formatDoctor(checks)}`);
|
|
459
|
+
return checks.some((check) => check.status === "fail") ? 1 : 0;
|
|
460
|
+
}
|
|
461
|
+
const MAX_POSITIONALS = {
|
|
462
|
+
doctor: 1,
|
|
463
|
+
verify: 1,
|
|
464
|
+
"xml preview": 3,
|
|
465
|
+
"invoice get": 3,
|
|
466
|
+
"receipt get": 3,
|
|
467
|
+
"nav summary": 2,
|
|
468
|
+
mcp: 1
|
|
469
|
+
};
|
|
470
|
+
function assertArity(command, positionals) {
|
|
471
|
+
const extra = positionals.slice(MAX_POSITIONALS[command] ?? positionals.length);
|
|
472
|
+
if (extra.length > 0) throw new CliUsageError(`Fölösleges argumentum: ${extra.join(" ")}`);
|
|
473
|
+
}
|
|
474
|
+
async function dispatch(io, command, argv) {
|
|
475
|
+
const args = parseArgs(argv, SPECS[command]);
|
|
476
|
+
assertArity(command, args.positionals);
|
|
477
|
+
if (command === "doctor") return doctorCommand(io, args);
|
|
478
|
+
switch (command) {
|
|
479
|
+
case "verify": return verifyCommand(io);
|
|
480
|
+
case "xml preview": return xmlPreviewCommand(io, args);
|
|
481
|
+
case "invoice get": return invoiceGetCommand(io, args);
|
|
482
|
+
case "receipt get": return receiptGetCommand(io, args);
|
|
483
|
+
case "nav summary": return navSummaryCommand(io, args);
|
|
484
|
+
default: return mcpCommand(io, args);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function errorMessage(error) {
|
|
488
|
+
if (require_errors.isSzamlazzError(error)) return error.hint ? `${error.message}\nTipp: ${error.hint}` : error.message;
|
|
489
|
+
return error instanceof Error ? error.message : String(error);
|
|
490
|
+
}
|
|
491
|
+
async function runCli(io) {
|
|
492
|
+
const argv = io.argv;
|
|
493
|
+
const first = argv[0];
|
|
494
|
+
if (first === void 0) {
|
|
495
|
+
io.stderr(CLI_USAGE);
|
|
496
|
+
return 2;
|
|
497
|
+
}
|
|
498
|
+
if (first === "help" || first === "--help" || first === "-h") {
|
|
499
|
+
io.stdout(CLI_USAGE);
|
|
500
|
+
return 0;
|
|
501
|
+
}
|
|
502
|
+
if (first === "--version" || first === "-v") {
|
|
503
|
+
io.stdout(`${require_server.KASSZA_VERSION}\n`);
|
|
504
|
+
return 0;
|
|
505
|
+
}
|
|
506
|
+
const command = commandOf(argv);
|
|
507
|
+
if (command === void 0) {
|
|
508
|
+
io.stderr(`Ismeretlen parancs: ${argv.slice(0, 2).join(" ")}\n\n${CLI_USAGE}`);
|
|
509
|
+
return 2;
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
return await dispatch(io, command, argv);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
if (error instanceof CliUsageError) {
|
|
515
|
+
io.stderr(`${error.message}\nSegítség: kassza help\n`);
|
|
516
|
+
return 2;
|
|
517
|
+
}
|
|
518
|
+
if (error instanceof CliError || require_errors.isSzamlazzError(error)) {
|
|
519
|
+
io.stderr(`${errorMessage(error)}\n`);
|
|
520
|
+
return 1;
|
|
521
|
+
}
|
|
522
|
+
io.stderr(`Váratlan hiba: ${errorMessage(error)}\n`);
|
|
523
|
+
return 1;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
//#endregion
|
|
527
|
+
//#region src/cli/bin.ts
|
|
528
|
+
async function readStdin() {
|
|
529
|
+
let text = "";
|
|
530
|
+
process.stdin.setEncoding("utf8");
|
|
531
|
+
for await (const chunk of process.stdin) text += chunk;
|
|
532
|
+
return text;
|
|
533
|
+
}
|
|
534
|
+
runCli({
|
|
535
|
+
argv: process.argv.slice(2),
|
|
536
|
+
env: process.env,
|
|
537
|
+
nodeVersion: process.versions.node,
|
|
538
|
+
stdout: (text) => process.stdout.write(text),
|
|
539
|
+
stderr: (text) => process.stderr.write(text),
|
|
540
|
+
readFile: (path) => (0, node_fs_promises.readFile)(path, "utf8"),
|
|
541
|
+
readStdin,
|
|
542
|
+
stdinLines: () => (0, node_readline.createInterface)({
|
|
543
|
+
input: process.stdin,
|
|
544
|
+
crlfDelay: Number.POSITIVE_INFINITY
|
|
545
|
+
})
|
|
546
|
+
}).then((code) => {
|
|
547
|
+
process.exitCode = code;
|
|
548
|
+
}, (error) => {
|
|
549
|
+
process.stderr.write(`Váratlan hiba: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
550
|
+
process.exitCode = 1;
|
|
551
|
+
});
|
|
552
|
+
//#endregion
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|