n8n-nodes-confirm8 0.22.0 → 0.24.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.
@@ -1,9 +1,14 @@
1
1
  import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
2
- export declare class Confirm8AgentTool implements INodeType {
2
+ /**
3
+ * CONFIRM8 OS TOOL (CHUMBED)
4
+ * - resource is ALWAYS "order"
5
+ * - operation is ALWAYS "getAll" OR "get" (derived only from presence of recordId)
6
+ * - dates are ALWAYS ISO YYYY-MM-DD (validated / computed here)
7
+ *
8
+ * This is the recommended single-tool version to use inside an AI Agent.
9
+ * The agent never sees 'resource' or 'operation' parameters, so it cannot fill them wrong.
10
+ */
11
+ export declare class Confirm8OSTool implements INodeType {
3
12
  description: INodeTypeDescription;
4
- /**
5
- * Parse natural date queries into filter JSON
6
- */
7
- private parseDateQuery;
8
13
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
9
14
  }
@@ -1,59 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Confirm8AgentTool = void 0;
4
- const n8n_workflow_1 = require("n8n-workflow");
5
- // -----------------------------------------------------------------------------
6
- // STRICT ALLOWED VALUES (used both for UI + for agent/tool schema guidance)
7
- // IMPORTANT: Any agent calling this tool MUST use ONLY these exact tokens.
8
- // -----------------------------------------------------------------------------
9
- const VALID_RESOURCES = [
10
- "user",
11
- "client",
12
- "item",
13
- "itemType",
14
- "task",
15
- "service",
16
- "product",
17
- "order",
18
- "modality",
19
- "ticket",
20
- "property",
21
- ];
22
- const VALID_OPERATIONS = [
23
- "getAll",
24
- "get",
25
- "create",
26
- "update",
27
- "activate",
28
- "deactivate",
29
- ];
30
- const AGENT_USAGE_NOTE = `
31
- CRITICAL (Agents):
32
- - You MUST return ONLY the option *value* token (not the label).
33
- - Allowed resource values: ${VALID_RESOURCES.join(", ")}
34
- - Allowed operation values: ${VALID_OPERATIONS.join(", ")}
35
- Examples:
36
- - "Ordem de Serviço" / "OS" / "Work Order" => resource="order"
37
- - To list/search => operation="getAll"
38
- Anything outside these lists will throw a fatal error.
39
- `.trim();
40
- class Confirm8AgentTool {
3
+ exports.Confirm8OSTool = void 0;
4
+ const confirm8_utils_1 = require("./confirm8.utils");
5
+ /**
6
+ * CONFIRM8 OS TOOL (CHUMBED)
7
+ * - resource is ALWAYS "order"
8
+ * - operation is ALWAYS "getAll" OR "get" (derived only from presence of recordId)
9
+ * - dates are ALWAYS ISO YYYY-MM-DD (validated / computed here)
10
+ *
11
+ * This is the recommended single-tool version to use inside an AI Agent.
12
+ * The agent never sees 'resource' or 'operation' parameters, so it cannot fill them wrong.
13
+ */
14
+ class Confirm8OSTool {
41
15
  constructor() {
42
16
  this.description = {
43
- displayName: "Confirm8 AI Tool",
44
- name: "confirm8AgentTool",
17
+ displayName: "Confirm8 OS (Chumbed AI Tool)",
18
+ name: "confirm8OsTool",
45
19
  icon: "file:tool.svg",
46
20
  group: ["transform"],
47
21
  version: 1,
48
- description: 'AI tool for Confirm8. IMPORTANT: For "OS/ordem de serviço" use resource="order". For dates use filters with start_date/end_date. Example: {"start_date":{"gte":"2025-11-23"},"end_date":{"lte":"2025-11-25"}}',
49
- defaults: {
50
- name: "Confirm8 AI Tool",
51
- },
52
- usableAsTool: true,
22
+ description: "Work Orders (OS) tool for Confirm8. CHUMBED: resource/order is fixed; operation is derived internally (getAll vs get). Dates are always ISO YYYY-MM-DD.",
23
+ defaults: { name: "Confirm8 OS" },
53
24
  inputs: ["main"],
54
25
  outputs: ["main"],
55
26
  properties: [
56
- // API Configuration
57
27
  {
58
28
  displayName: "Base URL",
59
29
  name: "baseUrl",
@@ -71,208 +41,97 @@ class Confirm8AgentTool {
71
41
  required: true,
72
42
  },
73
43
  {
74
- displayName: "X-API-DOMAIN",
44
+ displayName: "API Domain",
75
45
  name: "apiDomain",
76
46
  type: "string",
77
47
  default: "",
78
48
  required: true,
79
49
  },
80
50
  {
81
- displayName: "X-APIKEY-TOKEN",
51
+ displayName: "API Key Token",
82
52
  name: "apiKeyToken",
83
53
  type: "string",
84
54
  typeOptions: { password: true },
85
55
  default: "",
86
56
  required: true,
87
57
  },
88
- // Tool parameters
89
58
  {
90
- displayName: "Resource",
91
- name: "resource",
92
- type: "options",
93
- options: [
94
- {
95
- name: "User",
96
- value: "user",
97
- description: "Users/Employees/Funcionários",
98
- },
99
- {
100
- name: "Client",
101
- value: "client",
102
- description: "Clients/Customers/Clientes",
103
- },
104
- { name: "Item", value: "item", description: "Items/Itens" },
105
- {
106
- name: "Item Type",
107
- value: "itemType",
108
- description: "Item Types/Tipos",
109
- },
110
- {
111
- name: "Task",
112
- value: "task",
113
- description: "Tasks/Tarefas/Checklists",
114
- },
115
- {
116
- name: "Service",
117
- value: "service",
118
- description: "Services/Serviços",
119
- },
120
- {
121
- name: "Product",
122
- value: "product",
123
- description: "Products/Produtos",
124
- },
125
- {
126
- name: "Order (OS)",
127
- value: "order",
128
- description: "Work Orders/OS/Ordens de Serviço/WOS",
129
- },
130
- {
131
- name: "Modality",
132
- value: "modality",
133
- description: "Modalities/Modalidades",
134
- },
135
- { name: "Ticket", value: "ticket", description: "Tickets/Chamados" },
136
- {
137
- name: "Property",
138
- value: "property",
139
- description: "Properties/Propriedades",
140
- },
141
- ],
142
- default: "user",
143
- description: AGENT_USAGE_NOTE,
59
+ displayName: "Session ID",
60
+ name: "sessionId",
61
+ type: "string",
62
+ default: "",
144
63
  },
145
64
  {
146
- displayName: "Operation",
147
- name: "operation",
148
- type: "options",
149
- options: [
150
- {
151
- name: "Get All (List)",
152
- value: "getAll",
153
- description: AGENT_USAGE_NOTE,
154
- },
155
- {
156
- name: "Get (Single)",
157
- value: "get",
158
- description: AGENT_USAGE_NOTE,
159
- },
160
- {
161
- name: "Create",
162
- value: "create",
163
- description: AGENT_USAGE_NOTE,
164
- },
165
- {
166
- name: "Update",
167
- value: "update",
168
- description: AGENT_USAGE_NOTE,
169
- },
170
- {
171
- name: "Activate",
172
- value: "activate",
173
- description: AGENT_USAGE_NOTE,
174
- },
175
- {
176
- name: "Deactivate",
177
- value: "deactivate",
178
- description: AGENT_USAGE_NOTE,
179
- },
180
- ],
181
- default: "getAll",
182
- description: AGENT_USAGE_NOTE,
65
+ displayName: "User Query (Optional)",
66
+ name: "chatinput",
67
+ type: "string",
68
+ default: "",
183
69
  },
70
+ // If filled, tool performs GET by ID; otherwise, GET ALL (list)
184
71
  {
185
- displayName: "Record ID",
72
+ displayName: "OS ID (recordId)",
186
73
  name: "recordId",
187
74
  type: "string",
188
75
  default: "",
189
- description: "Record ID (only for get/update/activate/deactivate)",
76
+ description: "If provided, fetches a single OS (get). If empty, lists OS (getAll).",
190
77
  },
78
+ // Listing options (only relevant when recordId is empty)
191
79
  {
192
- displayName: "Data",
193
- name: "data",
80
+ displayName: "Date Range",
81
+ name: "dateRange",
82
+ type: "options",
83
+ default: "thisWeek",
84
+ displayOptions: { show: { recordId: [""] } },
85
+ options: [
86
+ { name: "This week (Mon..Sun)", value: "thisWeek" },
87
+ { name: "Custom", value: "custom" },
88
+ ],
89
+ },
90
+ {
91
+ displayName: "Start Date (YYYY-MM-DD)",
92
+ name: "startDate",
194
93
  type: "string",
195
94
  default: "",
196
- description: "JSON data (only for create/update)",
95
+ displayOptions: { show: { dateRange: ["custom"], recordId: [""] } },
197
96
  },
198
97
  {
199
- displayName: "Filters",
200
- name: "filters",
98
+ displayName: "End Date (YYYY-MM-DD)",
99
+ name: "endDate",
201
100
  type: "string",
202
101
  default: "",
203
- description: 'Filters as JSON. For dates: {"start_date":{"gte":"YYYY-MM-DD"},"end_date":{"lte":"YYYY-MM-DD"}}. For status: {"status":{"eq":"complete"}}',
102
+ displayOptions: { show: { dateRange: ["custom"], recordId: [""] } },
103
+ },
104
+ {
105
+ displayName: "Date Field",
106
+ name: "dateField",
107
+ type: "options",
108
+ default: "created_at",
109
+ displayOptions: { show: { recordId: [""] } },
110
+ options: [
111
+ { name: "created_at (recommended)", value: "created_at" },
112
+ { name: "date", value: "date" },
113
+ { name: "data", value: "data" },
114
+ ],
115
+ },
116
+ {
117
+ displayName: "Per Page",
118
+ name: "perPage",
119
+ type: "number",
120
+ default: 50,
121
+ displayOptions: { show: { recordId: [""] } },
122
+ typeOptions: { minValue: 1, maxValue: 200 },
123
+ },
124
+ {
125
+ displayName: "Page",
126
+ name: "page",
127
+ type: "number",
128
+ default: 1,
129
+ displayOptions: { show: { recordId: [""] } },
130
+ typeOptions: { minValue: 1 },
204
131
  },
205
132
  ],
206
133
  };
207
134
  }
208
- /**
209
- * Parse natural date queries into filter JSON
210
- */
211
- parseDateQuery(query) {
212
- const now = new Date();
213
- const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
214
- query = query.toLowerCase().trim();
215
- // Helper to format date as YYYY-MM-DD
216
- const formatDate = (date) => {
217
- return date.toISOString().split("T")[0];
218
- };
219
- // Today
220
- if (query.includes("hoje") || query.includes("today")) {
221
- const endOfDay = new Date(today);
222
- endOfDay.setHours(23, 59, 59, 999);
223
- return {
224
- start_date: { gte: formatDate(today) },
225
- end_date: { lte: formatDate(endOfDay) },
226
- };
227
- }
228
- // This week / semana atual
229
- if (query.includes("semana") || query.includes("week")) {
230
- const dayOfWeek = today.getDay();
231
- const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; // Monday is start
232
- const weekStart = new Date(today);
233
- weekStart.setDate(today.getDate() + diff);
234
- const weekEnd = new Date(weekStart);
235
- weekEnd.setDate(weekStart.getDate() + 6);
236
- return {
237
- start_date: { gte: formatDate(weekStart) },
238
- end_date: { lte: formatDate(weekEnd) },
239
- };
240
- }
241
- // This month / mês atual
242
- if (query.includes("mês") ||
243
- query.includes("mes") ||
244
- query.includes("month")) {
245
- const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
246
- const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
247
- return {
248
- start_date: { gte: formatDate(monthStart) },
249
- end_date: { lte: formatDate(monthEnd) },
250
- };
251
- }
252
- // Last N days
253
- const lastDaysMatch = query.match(/últimos?\s+(\d+)\s+dias?|last\s+(\d+)\s+days?/);
254
- if (lastDaysMatch) {
255
- const days = parseInt(lastDaysMatch[1] || lastDaysMatch[2]);
256
- const startDate = new Date(today);
257
- startDate.setDate(today.getDate() - days);
258
- return {
259
- start_date: { gte: formatDate(startDate) },
260
- end_date: { lte: formatDate(today) },
261
- };
262
- }
263
- // Next N days
264
- const nextDaysMatch = query.match(/próximos?\s+(\d+)\s+dias?|next\s+(\d+)\s+days?/);
265
- if (nextDaysMatch) {
266
- const days = parseInt(nextDaysMatch[1] || nextDaysMatch[2]);
267
- const endDate = new Date(today);
268
- endDate.setDate(today.getDate() + days);
269
- return {
270
- start_date: { gte: formatDate(today) },
271
- end_date: { lte: formatDate(endDate) },
272
- };
273
- }
274
- return null;
275
- }
276
135
  async execute() {
277
136
  const items = this.getInputData();
278
137
  const returnData = [];
@@ -282,381 +141,98 @@ class Confirm8AgentTool {
282
141
  const bearerToken = this.getNodeParameter("bearerToken", i);
283
142
  const apiDomain = this.getNodeParameter("apiDomain", i);
284
143
  const apiKeyToken = this.getNodeParameter("apiKeyToken", i);
285
- let resource = this.getNodeParameter("resource", i, "");
286
- let operation = this.getNodeParameter("operation", i, "");
287
- const recordId = this.getNodeParameter("recordId", i, "");
288
- // Parse data
289
- let data = {};
290
- const dataParam = this.getNodeParameter("data", i, "");
291
- if (dataParam) {
292
- try {
293
- data = JSON.parse(dataParam);
294
- }
295
- catch (e) {
296
- // Ignore
297
- }
298
- }
299
- // Parse filters
300
- let filters = {};
301
- const filtersParam = this.getNodeParameter("filters", i, "");
302
- if (filtersParam) {
303
- try {
304
- // Try to parse as JSON first
305
- filters = JSON.parse(filtersParam);
306
- }
307
- catch (e) {
308
- // If not JSON, try to parse as natural language date query
309
- const parsedDate = this.parseDateQuery(filtersParam);
310
- if (parsedDate) {
311
- filters = parsedDate;
312
- }
313
- }
314
- }
315
- // Aggressive normalization - EXPAND THIS
316
- const resourceMap = {
317
- // Standard
318
- users: "user",
319
- usuarios: "user",
320
- usuários: "user",
321
- employees: "user",
322
- funcionários: "user",
323
- funcionarios: "user",
324
- clients: "client",
325
- clientes: "client",
326
- customers: "client",
327
- items: "item",
328
- itens: "item",
329
- itemtypes: "itemType",
330
- tipos: "itemType",
331
- tasks: "task",
332
- tarefas: "task",
333
- services: "service",
334
- serviços: "service",
335
- servicos: "service",
336
- products: "product",
337
- produtos: "product",
338
- modalities: "modality",
339
- modalidades: "modality",
340
- tickets: "ticket",
341
- chamados: "ticket",
342
- properties: "property",
343
- propriedades: "property",
344
- // CRITICAL: OS mappings
345
- os: "order",
346
- "o.s": "order",
347
- "o.s.": "order",
348
- orders: "order",
349
- ordens: "order",
350
- ordem: "order",
351
- "ordem de serviço": "order",
352
- "ordem de servico": "order",
353
- "ordens de serviço": "order",
354
- "ordens de servico": "order",
355
- wos: "order",
356
- "work order": "order",
357
- "work orders": "order",
358
- pedidos: "order",
359
- pedido: "order",
360
- };
361
- const operationMap = {
362
- // List/Get All
363
- list: "getAll",
364
- listar: "getAll",
365
- mostrar: "getAll",
366
- buscar: "getAll",
367
- busque: "getAll",
368
- "buscar todos": "getAll",
369
- "buscar todas": "getAll",
370
- "buscar todo": "getAll",
371
- "buscar toda": "getAll",
372
- "get all": "getAll",
373
- "show all": "getAll",
374
- "list all": "getAll",
375
- todos: "getAll",
376
- todas: "getAll",
377
- "obter todos": "getAll",
378
- "obter todas": "getAll",
379
- "pegar todos": "getAll",
380
- "pegar todas": "getAll",
381
- // Get Single
382
- obter: "get",
383
- pegar: "get",
384
- ver: "get",
385
- visualizar: "get",
386
- // Create
387
- criar: "create",
388
- adicionar: "create",
389
- add: "create",
390
- novo: "create",
391
- nova: "create",
392
- new: "create",
393
- // Update
394
- atualizar: "update",
395
- modificar: "update",
396
- modify: "update",
397
- editar: "update",
398
- edit: "update",
399
- // Activate/Deactivate
400
- ativar: "activate",
401
- enable: "activate",
402
- habilitar: "activate",
403
- desativar: "deactivate",
404
- disable: "deactivate",
405
- desabilitar: "deactivate",
406
- };
407
- // Normalize - clean and map
408
- const normalizeKey = (value) => value
409
- .toLowerCase()
410
- .normalize("NFD")
411
- .replace(/\p{Diacritic}/gu, "")
412
- .replace(/[^a-z0-9\s.\-_/]/g, " ")
413
- .trim()
414
- .replace(/\s+/g, " ");
415
- const cleanResource = normalizeKey(resource);
416
- const cleanOperation = normalizeKey(operation);
417
- resource = resourceMap[cleanResource] || resource;
418
- operation = operationMap[cleanOperation] || operation;
419
- // If still not valid, throw clear error
420
- const validResources = VALID_RESOURCES;
421
- const validOperations = VALID_OPERATIONS;
422
- if (!validResources.includes(resource)) {
423
- throw new Error(`Invalid resource: "${resource}". Must be one of: ${validResources.join(", ")}. ` + `For OS/Ordem de Serviço use "order".`);
424
- }
425
- if (!validOperations.includes(operation)) {
426
- throw new Error(`Invalid operation: "${operation}". Must be one of: ${validOperations.join(", ")}. ` + `To list/search use "getAll".`);
427
- }
428
- // Resource config with relations
429
- const resourceConfig = {
430
- user: {
431
- endpoint: "users",
432
- relations: [
433
- "clients",
434
- "attachments",
435
- "permissions",
436
- "device",
437
- "employee",
438
- ],
439
- },
440
- client: {
441
- endpoint: "clients",
442
- relations: [
443
- "wos",
444
- "items",
445
- "employees",
446
- "headquarter",
447
- "files",
448
- "userGroup",
449
- "properties",
450
- ],
451
- },
452
- item: {
453
- endpoint: "items",
454
- relations: [
455
- "client",
456
- "item_type",
457
- "properties",
458
- "parent",
459
- "children",
460
- "collects",
461
- "wos",
462
- ],
463
- },
464
- itemType: {
465
- endpoint: "itemTypes",
466
- relations: ["properties"],
467
- },
468
- task: {
469
- endpoint: "tasks",
470
- relations: ["itemType", "activities", "wos", "modalities"],
471
- },
472
- service: {
473
- endpoint: "services",
474
- relations: ["task"],
475
- },
476
- product: {
477
- endpoint: "products",
478
- relations: [],
479
- },
480
- order: {
481
- endpoint: "wos",
482
- relations: [
483
- "client",
484
- "modalities",
485
- "tasks",
486
- "pivot_tasks",
487
- "products",
488
- "users",
489
- "items",
490
- "tickets",
491
- "services",
492
- "collects",
493
- "attachments",
494
- ],
495
- },
496
- modality: {
497
- endpoint: "modalities",
498
- relations: ["tasks"],
499
- },
500
- ticket: {
501
- endpoint: "tickets",
502
- relations: [
503
- "client",
504
- "subject_category",
505
- "category",
506
- "status",
507
- "attachments",
508
- "item",
509
- "owner",
510
- "priority",
511
- "users",
512
- "orders",
513
- "properties",
514
- ],
515
- },
516
- property: {
517
- endpoint: "properties",
518
- relations: [],
519
- },
520
- };
521
- const config = resourceConfig[resource];
522
- const baseEndpoint = config.endpoint;
523
- let endpoint = "";
524
- let method = "GET";
525
- let body = {};
526
- // Build endpoint
527
- switch (operation) {
528
- case "getAll":
529
- endpoint = `/v3/${baseEndpoint}`;
530
- break;
531
- case "get":
532
- if (!recordId)
533
- throw new Error("recordId required");
534
- endpoint = `/v3/${baseEndpoint}/${recordId}`;
535
- break;
536
- case "create":
537
- endpoint = `/v3/${baseEndpoint}`;
538
- method = "POST";
539
- body = data;
540
- break;
541
- case "update":
542
- if (!recordId)
543
- throw new Error("recordId required");
544
- endpoint = `/v3/${baseEndpoint}/${recordId}`;
545
- method = "PUT";
546
- body = data;
547
- break;
548
- case "activate":
549
- if (!recordId)
550
- throw new Error("recordId required");
551
- endpoint = `/v3/${baseEndpoint}/${recordId}/active`;
552
- method = [
553
- "client",
554
- "item",
555
- "itemType",
556
- "task",
557
- "service",
558
- "product",
559
- "order",
560
- "modality",
561
- ].includes(resource)
562
- ? "PUT"
563
- : "PATCH";
564
- break;
565
- case "deactivate":
566
- if (!recordId)
567
- throw new Error("recordId required");
568
- endpoint = `/v3/${baseEndpoint}/${recordId}/inactive`;
569
- method = [
570
- "client",
571
- "item",
572
- "itemType",
573
- "task",
574
- "service",
575
- "product",
576
- "order",
577
- "modality",
578
- ].includes(resource)
579
- ? "PUT"
580
- : "PATCH";
581
- break;
582
- }
583
- // Build query string
584
- const queryParams = [];
585
- // Add relations (only GET)
586
- if (method === "GET" && config.relations.length > 0) {
587
- config.relations.forEach((relation) => {
588
- queryParams.push(`relations=${relation}`);
144
+ const sessionId = this.getNodeParameter("sessionId", i, "");
145
+ const chatinput = this.getNodeParameter("chatinput", i, "");
146
+ const recordId = this.getNodeParameter("recordId", i, "").trim();
147
+ // CHUMBED RESOURCE
148
+ const resource = "order";
149
+ const config = confirm8_utils_1.RESOURCE_ENDPOINTS[resource];
150
+ // Decide operation internally
151
+ const operation = recordId ? "get" : "getAll";
152
+ if (operation === "get") {
153
+ const endpoint = `/v3/${config.endpoint}/${encodeURIComponent(recordId)}`;
154
+ const query = config.relations?.length
155
+ ? `?${config.relations.map((r) => `relations=${encodeURIComponent(r)}`).join("&")}`
156
+ : "";
157
+ const data = await confirm8_utils_1.confirm8Request.call(this, i, {
158
+ baseUrl,
159
+ bearerToken,
160
+ apiDomain,
161
+ apiKeyToken,
162
+ method: "GET",
163
+ endpoint,
164
+ query,
589
165
  });
590
- }
591
- // Add filters
592
- if (Object.keys(filters).length > 0) {
593
- Object.keys(filters).forEach((field) => {
594
- const operators = filters[field];
595
- Object.keys(operators).forEach((operator) => {
596
- const value = operators[operator];
597
- queryParams.push(`filters[${field}][${operator}]=${encodeURIComponent(String(value))}`);
598
- });
166
+ returnData.push({
167
+ json: {
168
+ success: true,
169
+ fixed: { resource, operation },
170
+ request: { sessionId, chatinput, recordId, endpoint },
171
+ data,
172
+ },
173
+ pairedItem: { item: i },
599
174
  });
175
+ continue;
600
176
  }
601
- // Full URL
602
- let fullUrl = `${baseUrl}${endpoint}`;
603
- if (queryParams.length > 0) {
604
- fullUrl += "?" + queryParams.join("&");
177
+ // operation === getAll
178
+ const dateRange = this.getNodeParameter("dateRange", i);
179
+ const dateField = this.getNodeParameter("dateField", i);
180
+ let start;
181
+ let end;
182
+ if (dateRange === "custom") {
183
+ start = this.getNodeParameter("startDate", i) || "";
184
+ end = this.getNodeParameter("endDate", i) || "";
185
+ if (!start || !end) {
186
+ throw new Error("When Date Range is Custom, startDate and endDate are required.");
187
+ }
188
+ (0, confirm8_utils_1.assertIsoDate)(start, "startDate");
189
+ (0, confirm8_utils_1.assertIsoDate)(end, "endDate");
605
190
  }
606
- // Make request
607
- const options = {
608
- method,
609
- uri: fullUrl,
610
- headers: {
611
- Authorization: `Bearer ${bearerToken}`,
612
- "X-API-DOMAIN": apiDomain,
613
- "X-APIKEY-TOKEN": apiKeyToken,
614
- "Content-Type": "application/json",
615
- },
616
- json: true,
617
- };
618
- if (method !== "GET" &&
619
- method !== "DELETE" &&
620
- Object.keys(body).length > 0) {
621
- options.body = body;
191
+ else {
192
+ const r = (0, confirm8_utils_1.getThisWeekRange)(new Date());
193
+ start = r.start;
194
+ end = r.end;
622
195
  }
623
- const responseData = await this.helpers.request(options);
196
+ const endpoint = `/v3/${config.endpoint}`;
197
+ const filters = { [dateField]: { gte: start, lte: end } };
198
+ const page = this.getNodeParameter("page", i);
199
+ const perPage = this.getNodeParameter("perPage", i);
200
+ const query = (0, confirm8_utils_1.buildQueryParams)(config.relations, filters);
201
+ const pagination = `&page=${encodeURIComponent(String(page))}&per_page=${encodeURIComponent(String(perPage))}`;
202
+ const finalQuery = query ? `${query}${pagination}` : `?page=${page}&per_page=${perPage}`;
203
+ const data = await confirm8_utils_1.confirm8Request.call(this, i, {
204
+ baseUrl,
205
+ bearerToken,
206
+ apiDomain,
207
+ apiKeyToken,
208
+ method: "GET",
209
+ endpoint,
210
+ query: finalQuery,
211
+ });
624
212
  returnData.push({
625
213
  json: {
626
214
  success: true,
627
- normalized: {
628
- originalResource: this.getNodeParameter("resource", i, ""),
629
- originalOperation: this.getNodeParameter("operation", i, ""),
630
- normalizedResource: resource,
631
- normalizedOperation: operation,
632
- },
633
- operation,
634
- resource,
635
- endpoint,
636
- fullUrl,
637
- relations: config.relations,
638
- filters: filters,
639
- method,
640
- data: responseData,
215
+ fixed: { resource, operation },
216
+ request: {
217
+ sessionId,
218
+ chatinput,
219
+ endpoint,
220
+ dateField,
221
+ startDate: start,
222
+ endDate: end,
223
+ page,
224
+ perPage,
225
+ },
226
+ data,
641
227
  },
642
228
  pairedItem: { item: i },
643
229
  });
644
230
  }
645
- catch (error) {
646
- if (this.continueOnFail()) {
647
- returnData.push({
648
- json: {
649
- success: false,
650
- error: error.message || "Unknown error",
651
- },
652
- pairedItem: { item: i },
653
- });
654
- continue;
655
- }
656
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
231
+ catch (err) {
232
+ throw (0, confirm8_utils_1.toNodeError)(this.getNode(), err, i);
657
233
  }
658
234
  }
659
235
  return [returnData];
660
236
  }
661
237
  }
662
- exports.Confirm8AgentTool = Confirm8AgentTool;
238
+ exports.Confirm8OSTool = Confirm8OSTool;
@@ -0,0 +1,32 @@
1
+ import { IDataObject, NodeOperationError, IExecuteFunctions } from "n8n-workflow";
2
+ export declare const VALID_RESOURCES: readonly ["user", "client", "item", "itemType", "task", "service", "product", "order", "modality", "ticket", "property"];
3
+ export declare const VALID_OPERATIONS: readonly ["getAll", "get", "create", "update", "activate", "deactivate"];
4
+ export type Confirm8Resource = (typeof VALID_RESOURCES)[number];
5
+ export type Confirm8Operation = (typeof VALID_OPERATIONS)[number];
6
+ export declare const RESOURCE_ENDPOINTS: Record<Confirm8Resource, {
7
+ endpoint: string;
8
+ relations: string[];
9
+ }>;
10
+ export declare function assertIsoDate(dateStr: string, label: string): void;
11
+ export declare function formatIsoDate(d: Date): string;
12
+ /**
13
+ * Week starts on Monday (pt-BR business default).
14
+ * Returns inclusive start/end in YYYY-MM-DD.
15
+ */
16
+ export declare function getThisWeekRange(now?: Date): {
17
+ start: string;
18
+ end: string;
19
+ };
20
+ /** Build query params for relations + filters in Confirm8 format. */
21
+ export declare function buildQueryParams(relations: string[], filters?: IDataObject): string;
22
+ export declare function confirm8Request(this: IExecuteFunctions, i: number, args: {
23
+ baseUrl: string;
24
+ bearerToken: string;
25
+ apiDomain: string;
26
+ apiKeyToken: string;
27
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
28
+ endpoint: string;
29
+ query?: string;
30
+ body?: IDataObject;
31
+ }): Promise<any>;
32
+ export declare function toNodeError(node: any, err: unknown, i: number): NodeOperationError;
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESOURCE_ENDPOINTS = exports.VALID_OPERATIONS = exports.VALID_RESOURCES = void 0;
4
+ exports.assertIsoDate = assertIsoDate;
5
+ exports.formatIsoDate = formatIsoDate;
6
+ exports.getThisWeekRange = getThisWeekRange;
7
+ exports.buildQueryParams = buildQueryParams;
8
+ exports.confirm8Request = confirm8Request;
9
+ exports.toNodeError = toNodeError;
10
+ const n8n_workflow_1 = require("n8n-workflow");
11
+ exports.VALID_RESOURCES = [
12
+ "user",
13
+ "client",
14
+ "item",
15
+ "itemType",
16
+ "task",
17
+ "service",
18
+ "product",
19
+ "order",
20
+ "modality",
21
+ "ticket",
22
+ "property",
23
+ ];
24
+ exports.VALID_OPERATIONS = [
25
+ "getAll",
26
+ "get",
27
+ "create",
28
+ "update",
29
+ "activate",
30
+ "deactivate",
31
+ ];
32
+ exports.RESOURCE_ENDPOINTS = {
33
+ user: {
34
+ endpoint: "users",
35
+ relations: ["clients", "attachments", "permissions", "device", "employee"],
36
+ },
37
+ client: {
38
+ endpoint: "clients",
39
+ relations: [
40
+ "address",
41
+ "contacts",
42
+ "items",
43
+ "tickets",
44
+ "orders",
45
+ "properties",
46
+ "attachments",
47
+ ],
48
+ },
49
+ item: {
50
+ endpoint: "items",
51
+ relations: ["client", "itemType", "properties", "attachments"],
52
+ },
53
+ itemType: { endpoint: "item-types", relations: ["properties"] },
54
+ task: {
55
+ endpoint: "tasks",
56
+ relations: ["itemType", "modality", "services", "products"],
57
+ },
58
+ service: { endpoint: "services", relations: ["task"] },
59
+ product: { endpoint: "products", relations: [] },
60
+ // IMPORTANT: Confirm8 Work Orders endpoint is 'wos'
61
+ order: {
62
+ endpoint: "wos",
63
+ relations: [
64
+ "client",
65
+ "modalities",
66
+ "tasks",
67
+ "pivot_tasks",
68
+ "products",
69
+ "users",
70
+ "items",
71
+ "tickets",
72
+ "services",
73
+ "collects",
74
+ "attachments",
75
+ ],
76
+ },
77
+ modality: { endpoint: "modalities", relations: ["tasks"] },
78
+ ticket: {
79
+ endpoint: "tickets",
80
+ relations: ["client", "subject_category", "category", "user", "attachments"],
81
+ },
82
+ property: { endpoint: "properties", relations: ["client", "item", "itemType"] },
83
+ };
84
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
85
+ function assertIsoDate(dateStr, label) {
86
+ if (!ISO_DATE.test(dateStr)) {
87
+ throw new Error(`${label} must be in YYYY-MM-DD format. Received: "${dateStr}"`);
88
+ }
89
+ }
90
+ function formatIsoDate(d) {
91
+ const y = d.getFullYear();
92
+ const m = String(d.getMonth() + 1).padStart(2, "0");
93
+ const day = String(d.getDate()).padStart(2, "0");
94
+ return `${y}-${m}-${day}`;
95
+ }
96
+ /**
97
+ * Week starts on Monday (pt-BR business default).
98
+ * Returns inclusive start/end in YYYY-MM-DD.
99
+ */
100
+ function getThisWeekRange(now = new Date()) {
101
+ const d = new Date(now);
102
+ d.setHours(0, 0, 0, 0);
103
+ // JS: Sunday=0 ... Saturday=6
104
+ const day = d.getDay();
105
+ const diffToMonday = day === 0 ? -6 : 1 - day; // if Sunday -> go back 6 days
106
+ const startDate = new Date(d);
107
+ startDate.setDate(d.getDate() + diffToMonday);
108
+ const endDate = new Date(startDate);
109
+ endDate.setDate(startDate.getDate() + 6);
110
+ return { start: formatIsoDate(startDate), end: formatIsoDate(endDate) };
111
+ }
112
+ /** Build query params for relations + filters in Confirm8 format. */
113
+ function buildQueryParams(relations, filters) {
114
+ const queryParams = [];
115
+ if (relations?.length) {
116
+ for (const rel of relations)
117
+ queryParams.push(`relations=${encodeURIComponent(rel)}`);
118
+ }
119
+ if (filters && Object.keys(filters).length) {
120
+ for (const field of Object.keys(filters)) {
121
+ const operators = filters[field];
122
+ for (const op of Object.keys(operators)) {
123
+ const value = operators[op];
124
+ queryParams.push(`filters[${encodeURIComponent(field)}][${encodeURIComponent(op)}]=${encodeURIComponent(String(value))}`);
125
+ }
126
+ }
127
+ }
128
+ return queryParams.length ? `?${queryParams.join("&")}` : "";
129
+ }
130
+ function confirm8Request(i, args) {
131
+ const { baseUrl, bearerToken, apiDomain, apiKeyToken, method, endpoint, query, body } = args;
132
+ const uri = `${baseUrl}${endpoint}${query ?? ""}`;
133
+ const options = {
134
+ method,
135
+ uri,
136
+ headers: {
137
+ Authorization: `Bearer ${bearerToken}`,
138
+ "X-API-DOMAIN": apiDomain,
139
+ "X-APIKEY-TOKEN": apiKeyToken,
140
+ "Content-Type": "application/json",
141
+ },
142
+ json: true,
143
+ };
144
+ if (body && method !== "GET" && method !== "DELETE") {
145
+ options.body = body;
146
+ }
147
+ return this.helpers.request(options);
148
+ }
149
+ function toNodeError(node, err, i) {
150
+ if (err instanceof n8n_workflow_1.NodeOperationError)
151
+ return err;
152
+ const message = err instanceof Error ? err.message : String(err);
153
+ return new n8n_workflow_1.NodeOperationError(node, message, { itemIndex: i });
154
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-confirm8",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "Simple n8n node for Confirm8 API - no credentials needed",
5
5
  "license": "MIT",
6
6
  "author": "Bill Hebert",