@timbrix/cli 0.3.1 → 1.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/README.md CHANGED
@@ -90,6 +90,61 @@ timbrix webhooks delete <webhook-id> --org <organization-id>
90
90
  timbrix webhooks delete <webhook-id> --org <organization-id> --force
91
91
  ```
92
92
 
93
+ ### Products
94
+
95
+ ```bash
96
+ # List products/services for an organization
97
+ timbrix products list --org <organization-id>
98
+
99
+ # Get product details
100
+ timbrix products get <product-id> --org <organization-id>
101
+
102
+ # Create a product
103
+ timbrix products create --org <organization-id>
104
+ timbrix products create --org <organization-id> --description Ukelele --product-key 60131324 --price 345.60
105
+
106
+ # Update a product
107
+ timbrix products update <product-id> --org <organization-id>
108
+
109
+ # Bulk import products/services from a CSV file
110
+ timbrix products import --org <organization-id> --file ./products.csv
111
+
112
+ # Delete a product (PERMANENT)
113
+ timbrix products delete <product-id> --org <organization-id>
114
+ ```
115
+
116
+ ### Invoices (CFDI 4.0)
117
+
118
+ ```bash
119
+ # Stamp (timbrar) a new invoice — authenticates with an API key, not the login session
120
+ timbrix invoices create --org <organization-id> --api-key <api-key> --file invoice.json
121
+ timbrix invoices create --org <organization-id> --api-key <api-key> --file invoice.json --output invoice.xml
122
+
123
+ # List invoices for an organization (newest first)
124
+ timbrix invoices list --org <organization-id>
125
+ timbrix invoices list --org <organization-id> --page 2 --limit 50
126
+ ```
127
+
128
+ ### SAT Catalogs
129
+
130
+ ```bash
131
+ # Search SAT claves de producto o servicio (c_ClaveProdServ)
132
+ timbrix sat claves-prod-serv --query ukelele
133
+ timbrix sat claves-prod-serv -q "servicios de consultoria" --limit 10
134
+
135
+ # Search SAT claves de unidad de medida (c_ClaveUnidad)
136
+ timbrix sat claves-unidad --query pieza
137
+ timbrix sat claves-unidad -q kilogramo --limit 10
138
+
139
+ # List SAT régimenes fiscales
140
+ timbrix sat regimenes list
141
+ timbrix sat regimenes list --tipo-persona moral
142
+
143
+ # List SAT usos de CFDI
144
+ timbrix sat usos-cfdi list
145
+ timbrix sat usos-cfdi list --regimen 601
146
+ ```
147
+
93
148
  ### API Keys
94
149
 
95
150
  ```bash
@@ -252,7 +307,9 @@ src/
252
307
  │ ├── logout.ts
253
308
  │ ├── organizations/ # Organization management
254
309
  │ ├── webhooks/ # Webhook management
255
- └── api-keys/ # API key management
310
+ ├── api-keys/ # API key management
311
+ │ ├── products/ # Products/services + CSV import
312
+ │ └── sat/ # SAT catalog search (claves, régimenes, usos CFDI)
256
313
  ├── config/
257
314
  │ └── auth.ts # Auth configuration manager
258
315
  ├── utils/
@@ -0,0 +1,13 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class InvoicesCreate extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ org: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ "api-key": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/invoices/create.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAA;AAO5C,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,OAAO;IACjD,OAAgB,WAAW,SAG4C;IAEvE,OAAgB,QAAQ,WAGvB;IAED,OAAgB,KAAK;;;;;MAoBpB;IAEY,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA6ClC"}
@@ -0,0 +1,67 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { Command, Flags } from "@oclif/core";
3
+ import chalk from "chalk";
4
+ import ora from "ora";
5
+ import { createApiKeyClient } from "../../utils/client.js";
6
+ import { getErrorMessage } from "../../utils/error.js";
7
+ export default class InvoicesCreate extends Command {
8
+ static description = "Create and stamp (timbrar) a CFDI 4.0 invoice via the configured PAC. " +
9
+ "This command authenticates with --api-key instead of the `timbrix login` " +
10
+ "session — the key must belong to the organization passed via --org.";
11
+ static examples = [
12
+ "<%= config.bin %> <%= command.id %> --org org-id --api-key sk_live_... --file invoice.json",
13
+ "<%= config.bin %> <%= command.id %> --org org-id --api-key sk_live_... --file invoice.json --output invoice.xml",
14
+ ];
15
+ static flags = {
16
+ org: Flags.string({
17
+ char: "o",
18
+ description: "Organization ID (must match the API key's organization)",
19
+ required: true,
20
+ }),
21
+ "api-key": Flags.string({
22
+ description: "Timbrix API key (needs the write:invoices scope)",
23
+ env: "TIMBRIX_API_KEY",
24
+ required: true,
25
+ }),
26
+ file: Flags.string({
27
+ char: "f",
28
+ description: "Path to a JSON file with the invoice payload (series, folioNumber, date, paymentForm, use, customer|customerId, items)",
29
+ required: true,
30
+ }),
31
+ output: Flags.string({
32
+ description: "Path to save the timbrado CFDI XML (optional)",
33
+ }),
34
+ };
35
+ async run() {
36
+ const { flags } = await this.parse(InvoicesCreate);
37
+ const client = createApiKeyClient(flags["api-key"]);
38
+ let payload;
39
+ try {
40
+ payload = JSON.parse(readFileSync(flags.file, "utf-8"));
41
+ }
42
+ catch (error) {
43
+ this.error(chalk.red(`Failed to read invoice payload file: ${getErrorMessage(error)}`));
44
+ }
45
+ const spinner = ora("Stamping invoice...").start();
46
+ try {
47
+ const invoice = await client.invoices.create(flags.org, payload);
48
+ spinner.succeed(chalk.green("✓ Invoice stamped successfully!"));
49
+ this.log(chalk.gray("\nInvoice Details:"));
50
+ this.log(` ${chalk.bold("ID:")} ${invoice.id}`);
51
+ this.log(` ${chalk.bold("UUID:")} ${invoice.uuid}`);
52
+ this.log(` ${chalk.bold("Series/Folio:")} ${invoice.series}-${invoice.folioNumber}`);
53
+ this.log(` ${chalk.bold("Total:")} $${invoice.total.toFixed(2)}`);
54
+ if (flags.output) {
55
+ writeFileSync(flags.output, invoice.xml, "utf-8");
56
+ this.log(` ${chalk.bold("XML saved to:")} ${flags.output}`);
57
+ }
58
+ else {
59
+ this.log(chalk.gray("\nPass --output <path> to save the CFDI XML to a file."));
60
+ }
61
+ }
62
+ catch (error) {
63
+ spinner.fail(chalk.red(`Failed to stamp invoice: ${getErrorMessage(error)}`));
64
+ this.exit(1);
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class InvoicesList extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ org: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ page: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/invoices/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAA;AAQ5C,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,OAAO;IAC/C,OAAgB,WAAW,SACmC;IAE9D,OAAgB,QAAQ,WAGvB;IAED,OAAgB,KAAK;;;;MAcpB;IAEY,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA4DlC"}
@@ -0,0 +1,75 @@
1
+ import { Command, Flags } from "@oclif/core";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { createClient } from "../../utils/client.js";
5
+ import { getErrorMessage } from "../../utils/error.js";
6
+ import { createTable } from "../../utils/table.js";
7
+ export default class InvoicesList extends Command {
8
+ static description = "List invoices (CFDI 4.0) for an organization, newest first";
9
+ static examples = [
10
+ "<%= config.bin %> <%= command.id %> --org acme-corp-id",
11
+ "<%= config.bin %> <%= command.id %> --org acme-corp-id --page 2 --limit 50",
12
+ ];
13
+ static flags = {
14
+ org: Flags.string({
15
+ char: "o",
16
+ description: "Organization ID",
17
+ required: true,
18
+ }),
19
+ page: Flags.integer({
20
+ char: "p",
21
+ description: "Page number (1-indexed)",
22
+ }),
23
+ limit: Flags.integer({
24
+ char: "l",
25
+ description: "Results per page (1-100)",
26
+ }),
27
+ };
28
+ async run() {
29
+ const { flags } = await this.parse(InvoicesList);
30
+ const client = createClient();
31
+ const spinner = ora("Fetching invoices...").start();
32
+ try {
33
+ const result = await client.invoices.list(flags.org, {
34
+ page: flags.page,
35
+ limit: flags.limit,
36
+ });
37
+ spinner.stop();
38
+ if (result.data.length === 0) {
39
+ this.log(chalk.yellow("No invoices found"));
40
+ this.log(chalk.gray(`\nStamp an invoice with: timbrix invoices create --org ${flags.org} --api-key <key> --file invoice.json`));
41
+ return;
42
+ }
43
+ const table = createTable([
44
+ "ID",
45
+ "UUID",
46
+ "Series/Folio",
47
+ "Type",
48
+ "RFC Receptor",
49
+ "Total",
50
+ "Status",
51
+ "Created",
52
+ ]);
53
+ result.data.forEach((invoice) => {
54
+ table.push([
55
+ invoice.id.substring(0, 8) + "...",
56
+ invoice.uuid,
57
+ `${invoice.serie}-${invoice.folio}`,
58
+ invoice.tipoComprobante,
59
+ invoice.rfcReceptor,
60
+ `$${invoice.total.toFixed(2)} ${invoice.moneda}`,
61
+ invoice.status === "vigente"
62
+ ? chalk.green(invoice.status)
63
+ : chalk.red(invoice.status),
64
+ new Date(invoice.createdAt).toLocaleDateString(),
65
+ ]);
66
+ });
67
+ this.log(table.toString());
68
+ this.log(chalk.gray(`\nPage ${result.page} of ${result.totalPages} — ${result.total} invoice(s) total`));
69
+ }
70
+ catch (error) {
71
+ spinner.fail(chalk.red("Failed to fetch invoices"));
72
+ this.error(getErrorMessage(error));
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class ProductsImport extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ org: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../src/commands/products/import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAA;AAS5C,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,OAAO;IACjD,OAAgB,WAAW,SAAkD;IAE7E,OAAgB,QAAQ,WAEvB;IAED,OAAgB,KAAK;;;MAWpB;IAEY,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAiDlC"}
@@ -0,0 +1,62 @@
1
+ import { Command, Flags } from "@oclif/core";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { basename } from "node:path";
4
+ import chalk from "chalk";
5
+ import ora from "ora";
6
+ import { createClient } from "../../utils/client.js";
7
+ import { getErrorMessage } from "../../utils/error.js";
8
+ import { createTable } from "../../utils/table.js";
9
+ export default class ProductsImport extends Command {
10
+ static description = "Bulk import products/services from a CSV file";
11
+ static examples = [
12
+ "<%= config.bin %> <%= command.id %> --org acme-corp-id --file ./products.csv",
13
+ ];
14
+ static flags = {
15
+ org: Flags.string({
16
+ char: "o",
17
+ description: "Organization ID",
18
+ required: true,
19
+ }),
20
+ file: Flags.string({
21
+ char: "f",
22
+ description: "Path to the CSV file to import",
23
+ required: true,
24
+ }),
25
+ };
26
+ async run() {
27
+ const { flags } = await this.parse(ProductsImport);
28
+ const client = createClient();
29
+ if (!existsSync(flags.file)) {
30
+ this.error(chalk.red(`File not found: ${flags.file}`));
31
+ }
32
+ if (!flags.file.toLowerCase().endsWith(".csv")) {
33
+ this.error(chalk.red("File must be a .csv file"));
34
+ }
35
+ const spinner = ora("Importing products...").start();
36
+ try {
37
+ const buffer = readFileSync(flags.file);
38
+ const filename = basename(flags.file);
39
+ const blob = new Blob([buffer], { type: "text/csv" });
40
+ const result = await client.products.importCsv(flags.org, blob, filename);
41
+ spinner.succeed(chalk.green("✓ Import finished"));
42
+ this.log(chalk.gray("\nImport Summary:"));
43
+ this.log(` ${chalk.bold("Total rows:")} ${result.totalRows}`);
44
+ this.log(` ${chalk.bold("Created:")} ${chalk.green(result.successCount)}`);
45
+ this.log(` ${chalk.bold("Errors:")} ${result.errorCount > 0
46
+ ? chalk.red(result.errorCount)
47
+ : result.errorCount}`);
48
+ if (result.errors.length > 0) {
49
+ this.log(chalk.yellow("\nRow errors:"));
50
+ const table = createTable(["Row", "Message"]);
51
+ result.errors.forEach((rowError) => {
52
+ table.push([String(rowError.row), rowError.message]);
53
+ });
54
+ this.log(table.toString());
55
+ }
56
+ }
57
+ catch (error) {
58
+ spinner.fail(chalk.red("Failed to import products"));
59
+ this.error(getErrorMessage(error));
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class SatClavesProdServ extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ query: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=claves-prod-serv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claves-prod-serv.d.ts","sourceRoot":"","sources":["../../../src/commands/sat/claves-prod-serv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAA;AAQ5C,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,OAAO;IACpD,OAAgB,WAAW,SACmC;IAE9D,OAAgB,QAAQ,WAGvB;IAED,OAAgB,KAAK;;;MAUpB;IAEY,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA6ClC"}
@@ -0,0 +1,60 @@
1
+ import { Command, Flags } from "@oclif/core";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { createClient } from "../../utils/client.js";
5
+ import { getErrorMessage } from "../../utils/error.js";
6
+ import { createTable, truncate } from "../../utils/table.js";
7
+ export default class SatClavesProdServ extends Command {
8
+ static description = "Search SAT claves de producto o servicio (c_ClaveProdServ)";
9
+ static examples = [
10
+ "<%= config.bin %> <%= command.id %> --query ukelele",
11
+ '<%= config.bin %> <%= command.id %> -q "servicios de consultoria" --limit 10',
12
+ ];
13
+ static flags = {
14
+ query: Flags.string({
15
+ char: "q",
16
+ description: "Search text (partial match on código or descripción)",
17
+ required: true,
18
+ }),
19
+ limit: Flags.integer({
20
+ char: "l",
21
+ description: "Maximum number of results to return (1-50)",
22
+ }),
23
+ };
24
+ async run() {
25
+ const { flags } = await this.parse(SatClavesProdServ);
26
+ const client = createClient();
27
+ const spinner = ora("Searching claves de producto o servicio...").start();
28
+ try {
29
+ const result = await client.sat.searchClavesProdServ({
30
+ q: flags.query,
31
+ limit: flags.limit,
32
+ });
33
+ spinner.stop();
34
+ if (result.data.length === 0) {
35
+ this.log(chalk.yellow("No results found"));
36
+ return;
37
+ }
38
+ const table = createTable([
39
+ "Código",
40
+ "Descripción",
41
+ "IVA Trasladado",
42
+ "IEPS Trasladado",
43
+ ]);
44
+ result.data.forEach((clave) => {
45
+ table.push([
46
+ clave.codigo,
47
+ truncate(clave.descripcion, 60),
48
+ clave.incluyeIvaTrasladado ? "Sí" : "No",
49
+ clave.incluyeIepsTrasladado ? "Sí" : "No",
50
+ ]);
51
+ });
52
+ this.log(table.toString());
53
+ this.log(chalk.gray(`\nShowing ${result.data.length} of ${result.total} matching clave(s)`));
54
+ }
55
+ catch (error) {
56
+ spinner.fail(chalk.red("Failed to search claves de producto o servicio"));
57
+ this.error(getErrorMessage(error));
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class SatClavesUnidad extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ query: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=claves-unidad.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claves-unidad.d.ts","sourceRoot":"","sources":["../../../src/commands/sat/claves-unidad.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAA;AAQ5C,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,OAAO;IAClD,OAAgB,WAAW,SAC8B;IAEzD,OAAgB,QAAQ,WAGvB;IAED,OAAgB,KAAK;;;MAWpB;IAEY,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAwClC"}
@@ -0,0 +1,55 @@
1
+ import { Command, Flags } from "@oclif/core";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { createClient } from "../../utils/client.js";
5
+ import { getErrorMessage } from "../../utils/error.js";
6
+ import { createTable, truncate } from "../../utils/table.js";
7
+ export default class SatClavesUnidad extends Command {
8
+ static description = "Search SAT claves de unidad de medida (c_ClaveUnidad)";
9
+ static examples = [
10
+ "<%= config.bin %> <%= command.id %> --query pieza",
11
+ "<%= config.bin %> <%= command.id %> -q kilogramo --limit 10",
12
+ ];
13
+ static flags = {
14
+ query: Flags.string({
15
+ char: "q",
16
+ description: "Search text (partial match on código, nombre or descripción)",
17
+ required: true,
18
+ }),
19
+ limit: Flags.integer({
20
+ char: "l",
21
+ description: "Maximum number of results to return (1-100)",
22
+ }),
23
+ };
24
+ async run() {
25
+ const { flags } = await this.parse(SatClavesUnidad);
26
+ const client = createClient();
27
+ const spinner = ora("Searching claves de unidad de medida...").start();
28
+ try {
29
+ const result = await client.sat.searchClavesUnidad({
30
+ q: flags.query,
31
+ limit: flags.limit,
32
+ });
33
+ spinner.stop();
34
+ if (result.data.length === 0) {
35
+ this.log(chalk.yellow("No results found"));
36
+ return;
37
+ }
38
+ const table = createTable(["Código", "Nombre", "Descripción", "Símbolo"]);
39
+ result.data.forEach((clave) => {
40
+ table.push([
41
+ clave.codigo,
42
+ truncate(clave.nombre, 30),
43
+ truncate(clave.descripcion, 40),
44
+ clave.simbolo ?? "-",
45
+ ]);
46
+ });
47
+ this.log(table.toString());
48
+ this.log(chalk.gray(`\nShowing ${result.data.length} of ${result.total} matching clave(s)`));
49
+ }
50
+ catch (error) {
51
+ spinner.fail(chalk.red("Failed to search claves de unidad de medida"));
52
+ this.error(getErrorMessage(error));
53
+ }
54
+ }
55
+ }
@@ -3,4 +3,11 @@ import { Timbrix } from "@timbrix/sdk";
3
3
  * Create an authenticated Timbrix client
4
4
  */
5
5
  export declare function createClient(): Timbrix;
6
+ /**
7
+ * Create a Timbrix client authenticated with an API key rather than the
8
+ * logged-in session. Used by commands whose API endpoint resolves the
9
+ * organization from the API key (e.g. `invoices create`), which is a
10
+ * different auth model than the rest of the CLI's `timbrix login` session.
11
+ */
12
+ export declare function createApiKeyClient(apiKey: string): Timbrix;
6
13
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/utils/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAItC;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAetC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/utils/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAItC;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAetC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAK1D"}
@@ -16,3 +16,15 @@ export function createClient() {
16
16
  baseUrl,
17
17
  });
18
18
  }
19
+ /**
20
+ * Create a Timbrix client authenticated with an API key rather than the
21
+ * logged-in session. Used by commands whose API endpoint resolves the
22
+ * organization from the API key (e.g. `invoices create`), which is a
23
+ * different auth model than the rest of the CLI's `timbrix login` session.
24
+ */
25
+ export function createApiKeyClient(apiKey) {
26
+ return new Timbrix({
27
+ apiKey,
28
+ baseUrl: AuthManager.getBaseUrl(),
29
+ });
30
+ }
@@ -489,6 +489,117 @@
489
489
  "list.js"
490
490
  ]
491
491
  },
492
+ "invoices:create": {
493
+ "aliases": [],
494
+ "args": {},
495
+ "description": "Create and stamp (timbrar) a CFDI 4.0 invoice via the configured PAC. This command authenticates with --api-key instead of the `timbrix login` session — the key must belong to the organization passed via --org.",
496
+ "examples": [
497
+ "<%= config.bin %> <%= command.id %> --org org-id --api-key sk_live_... --file invoice.json",
498
+ "<%= config.bin %> <%= command.id %> --org org-id --api-key sk_live_... --file invoice.json --output invoice.xml"
499
+ ],
500
+ "flags": {
501
+ "org": {
502
+ "char": "o",
503
+ "description": "Organization ID (must match the API key's organization)",
504
+ "name": "org",
505
+ "required": true,
506
+ "hasDynamicHelp": false,
507
+ "multiple": false,
508
+ "type": "option"
509
+ },
510
+ "api-key": {
511
+ "description": "Timbrix API key (needs the write:invoices scope)",
512
+ "env": "TIMBRIX_API_KEY",
513
+ "name": "api-key",
514
+ "required": true,
515
+ "hasDynamicHelp": false,
516
+ "multiple": false,
517
+ "type": "option"
518
+ },
519
+ "file": {
520
+ "char": "f",
521
+ "description": "Path to a JSON file with the invoice payload (series, folioNumber, date, paymentForm, use, customer|customerId, items)",
522
+ "name": "file",
523
+ "required": true,
524
+ "hasDynamicHelp": false,
525
+ "multiple": false,
526
+ "type": "option"
527
+ },
528
+ "output": {
529
+ "description": "Path to save the timbrado CFDI XML (optional)",
530
+ "name": "output",
531
+ "hasDynamicHelp": false,
532
+ "multiple": false,
533
+ "type": "option"
534
+ }
535
+ },
536
+ "hasDynamicHelp": false,
537
+ "hiddenAliases": [],
538
+ "id": "invoices:create",
539
+ "pluginAlias": "@timbrix/cli",
540
+ "pluginName": "@timbrix/cli",
541
+ "pluginType": "core",
542
+ "strict": true,
543
+ "enableJsonFlag": false,
544
+ "isESM": true,
545
+ "relativePath": [
546
+ "dist",
547
+ "commands",
548
+ "invoices",
549
+ "create.js"
550
+ ]
551
+ },
552
+ "invoices:list": {
553
+ "aliases": [],
554
+ "args": {},
555
+ "description": "List invoices (CFDI 4.0) for an organization, newest first",
556
+ "examples": [
557
+ "<%= config.bin %> <%= command.id %> --org acme-corp-id",
558
+ "<%= config.bin %> <%= command.id %> --org acme-corp-id --page 2 --limit 50"
559
+ ],
560
+ "flags": {
561
+ "org": {
562
+ "char": "o",
563
+ "description": "Organization ID",
564
+ "name": "org",
565
+ "required": true,
566
+ "hasDynamicHelp": false,
567
+ "multiple": false,
568
+ "type": "option"
569
+ },
570
+ "page": {
571
+ "char": "p",
572
+ "description": "Page number (1-indexed)",
573
+ "name": "page",
574
+ "hasDynamicHelp": false,
575
+ "multiple": false,
576
+ "type": "option"
577
+ },
578
+ "limit": {
579
+ "char": "l",
580
+ "description": "Results per page (1-100)",
581
+ "name": "limit",
582
+ "hasDynamicHelp": false,
583
+ "multiple": false,
584
+ "type": "option"
585
+ }
586
+ },
587
+ "hasDynamicHelp": false,
588
+ "hiddenAliases": [],
589
+ "id": "invoices:list",
590
+ "pluginAlias": "@timbrix/cli",
591
+ "pluginName": "@timbrix/cli",
592
+ "pluginType": "core",
593
+ "strict": true,
594
+ "enableJsonFlag": false,
595
+ "isESM": true,
596
+ "relativePath": [
597
+ "dist",
598
+ "commands",
599
+ "invoices",
600
+ "list.js"
601
+ ]
602
+ },
492
603
  "oauth-apps:create": {
493
604
  "aliases": [],
494
605
  "args": {},
@@ -956,6 +1067,49 @@
956
1067
  "get.js"
957
1068
  ]
958
1069
  },
1070
+ "products:import": {
1071
+ "aliases": [],
1072
+ "args": {},
1073
+ "description": "Bulk import products/services from a CSV file",
1074
+ "examples": [
1075
+ "<%= config.bin %> <%= command.id %> --org acme-corp-id --file ./products.csv"
1076
+ ],
1077
+ "flags": {
1078
+ "org": {
1079
+ "char": "o",
1080
+ "description": "Organization ID",
1081
+ "name": "org",
1082
+ "required": true,
1083
+ "hasDynamicHelp": false,
1084
+ "multiple": false,
1085
+ "type": "option"
1086
+ },
1087
+ "file": {
1088
+ "char": "f",
1089
+ "description": "Path to the CSV file to import",
1090
+ "name": "file",
1091
+ "required": true,
1092
+ "hasDynamicHelp": false,
1093
+ "multiple": false,
1094
+ "type": "option"
1095
+ }
1096
+ },
1097
+ "hasDynamicHelp": false,
1098
+ "hiddenAliases": [],
1099
+ "id": "products:import",
1100
+ "pluginAlias": "@timbrix/cli",
1101
+ "pluginName": "@timbrix/cli",
1102
+ "pluginType": "core",
1103
+ "strict": true,
1104
+ "enableJsonFlag": false,
1105
+ "isESM": true,
1106
+ "relativePath": [
1107
+ "dist",
1108
+ "commands",
1109
+ "products",
1110
+ "import.js"
1111
+ ]
1112
+ },
959
1113
  "products:list": {
960
1114
  "aliases": [],
961
1115
  "args": {},
@@ -1111,6 +1265,92 @@
1111
1265
  "update.js"
1112
1266
  ]
1113
1267
  },
1268
+ "sat:claves-prod-serv": {
1269
+ "aliases": [],
1270
+ "args": {},
1271
+ "description": "Search SAT claves de producto o servicio (c_ClaveProdServ)",
1272
+ "examples": [
1273
+ "<%= config.bin %> <%= command.id %> --query ukelele",
1274
+ "<%= config.bin %> <%= command.id %> -q \"servicios de consultoria\" --limit 10"
1275
+ ],
1276
+ "flags": {
1277
+ "query": {
1278
+ "char": "q",
1279
+ "description": "Search text (partial match on código or descripción)",
1280
+ "name": "query",
1281
+ "required": true,
1282
+ "hasDynamicHelp": false,
1283
+ "multiple": false,
1284
+ "type": "option"
1285
+ },
1286
+ "limit": {
1287
+ "char": "l",
1288
+ "description": "Maximum number of results to return (1-50)",
1289
+ "name": "limit",
1290
+ "hasDynamicHelp": false,
1291
+ "multiple": false,
1292
+ "type": "option"
1293
+ }
1294
+ },
1295
+ "hasDynamicHelp": false,
1296
+ "hiddenAliases": [],
1297
+ "id": "sat:claves-prod-serv",
1298
+ "pluginAlias": "@timbrix/cli",
1299
+ "pluginName": "@timbrix/cli",
1300
+ "pluginType": "core",
1301
+ "strict": true,
1302
+ "enableJsonFlag": false,
1303
+ "isESM": true,
1304
+ "relativePath": [
1305
+ "dist",
1306
+ "commands",
1307
+ "sat",
1308
+ "claves-prod-serv.js"
1309
+ ]
1310
+ },
1311
+ "sat:claves-unidad": {
1312
+ "aliases": [],
1313
+ "args": {},
1314
+ "description": "Search SAT claves de unidad de medida (c_ClaveUnidad)",
1315
+ "examples": [
1316
+ "<%= config.bin %> <%= command.id %> --query pieza",
1317
+ "<%= config.bin %> <%= command.id %> -q kilogramo --limit 10"
1318
+ ],
1319
+ "flags": {
1320
+ "query": {
1321
+ "char": "q",
1322
+ "description": "Search text (partial match on código, nombre or descripción)",
1323
+ "name": "query",
1324
+ "required": true,
1325
+ "hasDynamicHelp": false,
1326
+ "multiple": false,
1327
+ "type": "option"
1328
+ },
1329
+ "limit": {
1330
+ "char": "l",
1331
+ "description": "Maximum number of results to return (1-100)",
1332
+ "name": "limit",
1333
+ "hasDynamicHelp": false,
1334
+ "multiple": false,
1335
+ "type": "option"
1336
+ }
1337
+ },
1338
+ "hasDynamicHelp": false,
1339
+ "hiddenAliases": [],
1340
+ "id": "sat:claves-unidad",
1341
+ "pluginAlias": "@timbrix/cli",
1342
+ "pluginName": "@timbrix/cli",
1343
+ "pluginType": "core",
1344
+ "strict": true,
1345
+ "enableJsonFlag": false,
1346
+ "isESM": true,
1347
+ "relativePath": [
1348
+ "dist",
1349
+ "commands",
1350
+ "sat",
1351
+ "claves-unidad.js"
1352
+ ]
1353
+ },
1114
1354
  "webhooks:create": {
1115
1355
  "aliases": [],
1116
1356
  "args": {},
@@ -1368,5 +1608,5 @@
1368
1608
  ]
1369
1609
  }
1370
1610
  },
1371
- "version": "0.3.1"
1611
+ "version": "1.1.0"
1372
1612
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timbrix/cli",
3
- "version": "0.3.1",
3
+ "version": "1.1.0",
4
4
  "description": "CLI tool for Timbrix API",
5
5
  "author": "Ivan Sotelo",
6
6
  "bin": {
@@ -36,7 +36,7 @@
36
36
  "ky": "^2.0.2",
37
37
  "ora": "^9.4.1",
38
38
  "update-notifier": "^7.3.1",
39
- "@timbrix/sdk": "0.3.1"
39
+ "@timbrix/sdk": "1.1.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^26.1.2",