@funnelchat-hq/sandbox 0.2.0 → 0.3.1
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 +166 -53
- package/dist/{chunk-M5E6NAXM.js → chunk-FUK4UDIM.js} +15834 -6810
- package/dist/cli.js +2 -1
- package/dist/index.d.ts +117 -5
- package/dist/index.js +1 -1
- package/funnelchat-sandbox.config.example.json +2 -0
- package/package.json +1 -1
- package/spec/procedencia.json +32 -28
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { ConfigError, parseArgs, VERSION, DEFAULT_PORT, DEFAULT_CONFIG_FILE, resolveConfig, createSandbox } from './chunk-
|
|
2
|
+
import { ConfigError, parseArgs, VERSION, DEFAULT_PORT, DEFAULT_CONFIG_FILE, resolveConfig, createSandbox } from './chunk-FUK4UDIM.js';
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
5
|
var HELP = `funnelchat-sandbox ${VERSION}
|
|
@@ -21,6 +21,7 @@ Options:
|
|
|
21
21
|
--no-rate-limit Do not apply the per-number pace.
|
|
22
22
|
--template-approval <s> Seconds until a new template is approved (default 5; "-" leaves it pending).
|
|
23
23
|
--meta-connect Turn on the Meta sign-up by link (off by default, like production).
|
|
24
|
+
--no-group-actions Turn off the send to a group by API (on by default, like production).
|
|
24
25
|
-q, --quiet No line per request and per delivery.
|
|
25
26
|
-h, --help This help.
|
|
26
27
|
-v, --version The version.
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ interface PresetSubscription {
|
|
|
10
10
|
/** Optional: a fixed id (UUID). */
|
|
11
11
|
id?: string;
|
|
12
12
|
}
|
|
13
|
+
/** A workspace created at start (and after every reset) besides the default one, with fixed keys. */
|
|
14
|
+
interface PresetWorkspace {
|
|
15
|
+
name: string;
|
|
16
|
+
/** Keys of that workspace (`<id>|fc_live_<letters and digits>`). Any other key belongs to the default workspace. */
|
|
17
|
+
keys: string[];
|
|
18
|
+
}
|
|
13
19
|
interface SandboxConfig {
|
|
14
20
|
port: number;
|
|
15
21
|
host: string;
|
|
@@ -39,8 +45,10 @@ interface SandboxConfig {
|
|
|
39
45
|
};
|
|
40
46
|
/** Seconds until a template created through the API goes from `PENDING` to `APPROVED`. `null`: it stays pending. */
|
|
41
47
|
templateApprovalSeconds: number | null;
|
|
42
|
-
/** Subscriptions to create at start and after every reset. */
|
|
48
|
+
/** Subscriptions to create at start and after every reset, in the default workspace. */
|
|
43
49
|
subscriptions: PresetSubscription[];
|
|
50
|
+
/** Workspaces to create at start and after every reset, besides the default one (ids 2, 3… in this order). */
|
|
51
|
+
workspaces: PresetWorkspace[];
|
|
44
52
|
/**
|
|
45
53
|
* The number quota of the account and the Meta sign-up switch; `POST /_sandbox/numbers` changes them while it runs.
|
|
46
54
|
* QR and Meta of the same phone count once; the two example numbers already use two places.
|
|
@@ -60,6 +68,12 @@ interface SandboxConfig {
|
|
|
60
68
|
/** `POST /connect-links` per minute (accounts: 10); one more answers Laravel's `429 Too Many Attempts.`. */
|
|
61
69
|
linksPerMinute: number;
|
|
62
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* The group actions by API (`POST /groups/{group}/messages`, `GET /groups/actions/{action}`). `true` (the default,
|
|
73
|
+
* like production, where communities' kill switch `API_GROUP_ACTIONS_ENABLED` is on). `false` imitates the switch
|
|
74
|
+
* off: every key gets `403 route_not_available`.
|
|
75
|
+
*/
|
|
76
|
+
groupActions: boolean;
|
|
63
77
|
/**
|
|
64
78
|
* Where the sign-up page is reachable, for the `url` of a link (`<publicUrl>/connect/<token>`). Never taken from the
|
|
65
79
|
* request's `Host`. `null`: the address the sandbox listens on.
|
|
@@ -86,8 +100,20 @@ interface MessageError {
|
|
|
86
100
|
type DeviceKind = 'cloud' | 'qr';
|
|
87
101
|
/** The aggregated health of an official number (accounts `MetaHealthStatusService`). */
|
|
88
102
|
type HealthState = 'available' | 'limited' | 'blocked' | 'unknown';
|
|
103
|
+
/**
|
|
104
|
+
* A workspace of the account (accounts `workspaces`). Every API key belongs to one and only sees and creates there
|
|
105
|
+
* (accounts v1.5.97, workspaces-api-keys). What has no workspace (`null`) counts as the default one's.
|
|
106
|
+
*/
|
|
107
|
+
interface Workspace {
|
|
108
|
+
id: number;
|
|
109
|
+
name: string;
|
|
110
|
+
isDefault: boolean;
|
|
111
|
+
createdAt: Date;
|
|
112
|
+
}
|
|
89
113
|
interface Device {
|
|
90
114
|
id: number;
|
|
115
|
+
/** The workspace the number is in; `null` counts as the default one. */
|
|
116
|
+
workspaceId: number | null;
|
|
91
117
|
alias: string;
|
|
92
118
|
/** Digits only, without `+`. `null` while a number created by QR has not been scanned. */
|
|
93
119
|
number: string | null;
|
|
@@ -114,6 +140,8 @@ type ConnectLinkStatus = 'pending' | 'completed' | 'failed' | 'expired' | 'revok
|
|
|
114
140
|
/** A sign-up link (contract A1 `connect-links`): it creates (or reconnects) a number when the page completes. */
|
|
115
141
|
interface ConnectLink {
|
|
116
142
|
id: number;
|
|
143
|
+
/** The workspace of the key that created it: the number it creates lands there. */
|
|
144
|
+
workspaceId: number | null;
|
|
117
145
|
type: 'meta' | 'qr';
|
|
118
146
|
status: ConnectLinkStatus;
|
|
119
147
|
/** sha256 of the token: the raw token is only in the `url` of the create answer. */
|
|
@@ -146,6 +174,8 @@ interface IdempotentAnswer {
|
|
|
146
174
|
}
|
|
147
175
|
interface Contact {
|
|
148
176
|
id: number;
|
|
177
|
+
/** The workspace the contact is in; `null` counts as the default one. */
|
|
178
|
+
workspaceId: number | null;
|
|
149
179
|
name: string | null;
|
|
150
180
|
email: string | null;
|
|
151
181
|
/** Digits only: the API stores the phone without `+`. */
|
|
@@ -207,9 +237,51 @@ interface CustomField {
|
|
|
207
237
|
createdAt: Date;
|
|
208
238
|
updatedAt: Date;
|
|
209
239
|
}
|
|
240
|
+
/** A participant of a group: the phone in digits, and whether it is an admin. */
|
|
241
|
+
interface GroupParticipant {
|
|
242
|
+
phone: string;
|
|
243
|
+
name: string | null;
|
|
244
|
+
admin: boolean;
|
|
245
|
+
}
|
|
246
|
+
/** A WhatsApp group of a number of the account (communities `groups`). */
|
|
247
|
+
interface Group {
|
|
248
|
+
id: number;
|
|
249
|
+
/** The WhatsApp id of the group, `120363…@g.us`: the `{group}` of the routes and `group.uid` of the events. */
|
|
250
|
+
uid: string;
|
|
251
|
+
name: string;
|
|
252
|
+
description: string;
|
|
253
|
+
/** The number that is in the group (a QR number: the sandbox account has no official groups). */
|
|
254
|
+
deviceId: number;
|
|
255
|
+
/** The workspace the group is in; `null` counts as the default one. */
|
|
256
|
+
workspaceId: number | null;
|
|
257
|
+
participants: GroupParticipant[];
|
|
258
|
+
link: string;
|
|
259
|
+
createdAt: Date;
|
|
260
|
+
updatedAt: Date;
|
|
261
|
+
}
|
|
262
|
+
/** `queued` → `running` → `completed` | `failed` (communities `ApiGroupAction::publicStatus()`). */
|
|
263
|
+
type GroupActionStatus = 'queued' | 'running' | 'completed' | 'failed';
|
|
264
|
+
/** A group action by API (communities `api_group_actions`): here, only `send_message`. */
|
|
265
|
+
interface GroupAction {
|
|
266
|
+
uuid: string;
|
|
267
|
+
type: 'send_message';
|
|
268
|
+
status: GroupActionStatus;
|
|
269
|
+
deviceId: number;
|
|
270
|
+
groupId: number;
|
|
271
|
+
/** The workspace of the key that called (`call_context.workspace_id`). */
|
|
272
|
+
workspaceId: number;
|
|
273
|
+
result: Record<string, unknown> | null;
|
|
274
|
+
reason: string | null;
|
|
275
|
+
createdAt: Date;
|
|
276
|
+
startedAt: Date | null;
|
|
277
|
+
completedAt: Date | null;
|
|
278
|
+
failedAt: Date | null;
|
|
279
|
+
}
|
|
210
280
|
type TemplateStatus = 'PENDING' | 'APPROVED' | 'REJECTED' | 'PAUSED' | 'DISABLED';
|
|
211
281
|
interface Template {
|
|
212
282
|
id: number;
|
|
283
|
+
/** The workspace it belongs to; `null` counts as the default one. */
|
|
284
|
+
workspaceId: number | null;
|
|
213
285
|
uid: string;
|
|
214
286
|
name: string;
|
|
215
287
|
body: string;
|
|
@@ -235,6 +307,8 @@ interface Template {
|
|
|
235
307
|
type SubscriptionStatus = 'active' | 'paused' | 'disabled';
|
|
236
308
|
interface Subscription {
|
|
237
309
|
id: string;
|
|
310
|
+
/** The workspace it belongs to: it only gets that workspace's events. `null` counts as the default one. */
|
|
311
|
+
workspaceId: number | null;
|
|
238
312
|
url: string;
|
|
239
313
|
description: string | null;
|
|
240
314
|
events: string[];
|
|
@@ -322,6 +396,18 @@ interface RequestLogEntry {
|
|
|
322
396
|
requestBody: unknown;
|
|
323
397
|
responseBody: unknown;
|
|
324
398
|
}
|
|
399
|
+
/** An event the sandbox emitted, delivered or not: the panel shows them, so a status is seen without a receiver. */
|
|
400
|
+
interface EmittedEvent {
|
|
401
|
+
envelope: {
|
|
402
|
+
id: string;
|
|
403
|
+
type: string;
|
|
404
|
+
version: number;
|
|
405
|
+
occurred_at: string;
|
|
406
|
+
data: unknown;
|
|
407
|
+
};
|
|
408
|
+
/** How many subscriptions got it. */
|
|
409
|
+
subscriptions: number;
|
|
410
|
+
}
|
|
325
411
|
/** A failure armed with `POST /_sandbox/failures` or the `X-Sandbox-*` headers. */
|
|
326
412
|
interface ArmedFailure {
|
|
327
413
|
id: number;
|
|
@@ -345,6 +431,9 @@ declare class Sequence {
|
|
|
345
431
|
}
|
|
346
432
|
declare class Store {
|
|
347
433
|
readonly historyLimit: number;
|
|
434
|
+
workspaces: Map<number, Workspace>;
|
|
435
|
+
/** The sandbox keys minted for a workspace (`POST /_sandbox/workspaces`, `/_sandbox/keys`): any other key is the default one's. */
|
|
436
|
+
keys: Map<string, number>;
|
|
348
437
|
devices: Map<number, Device>;
|
|
349
438
|
contacts: Map<number, Contact>;
|
|
350
439
|
messages: Map<number, Message>;
|
|
@@ -354,11 +443,14 @@ declare class Store {
|
|
|
354
443
|
subscriptions: Map<string, Subscription>;
|
|
355
444
|
deliveries: Delivery[];
|
|
356
445
|
requests: RequestLogEntry[];
|
|
446
|
+
events: EmittedEvent[];
|
|
357
447
|
failures: ArmedFailure[];
|
|
358
448
|
forms: Map<number, FormVersion>;
|
|
359
449
|
formFamilies: Map<number, FormFamily>;
|
|
360
450
|
formRuns: Map<number, FormRun>;
|
|
361
451
|
connectLinks: Map<number, ConnectLink>;
|
|
452
|
+
groups: Map<number, Group>;
|
|
453
|
+
groupActions: Map<string, GroupAction>;
|
|
362
454
|
/** When each `POST /connect-links` arrived in the last minute: the per-minute limit. */
|
|
363
455
|
linkAttempts: number[];
|
|
364
456
|
/** Answers by `Idempotency-Key`, per resource (`devices`, `connect-links`): one account. */
|
|
@@ -389,6 +481,8 @@ declare class Store {
|
|
|
389
481
|
/** Salt of the deterministic event ids: new on every reset, so ids never repeat across sessions. */
|
|
390
482
|
eventSalt: string;
|
|
391
483
|
readonly ids: {
|
|
484
|
+
workspace: Sequence;
|
|
485
|
+
apiKey: Sequence;
|
|
392
486
|
contact: Sequence;
|
|
393
487
|
message: Sequence;
|
|
394
488
|
tag: Sequence;
|
|
@@ -403,9 +497,13 @@ declare class Store {
|
|
|
403
497
|
formRun: Sequence;
|
|
404
498
|
device: Sequence;
|
|
405
499
|
connectLink: Sequence;
|
|
500
|
+
group: Sequence;
|
|
406
501
|
};
|
|
407
502
|
constructor(historyLimit?: number);
|
|
503
|
+
/** The default workspace of the account: where what has no workspace belongs. */
|
|
504
|
+
get defaultWorkspace(): Workspace;
|
|
408
505
|
logRequest(entry: Omit<RequestLogEntry, 'id'>): RequestLogEntry;
|
|
506
|
+
logEvent(entry: EmittedEvent): void;
|
|
409
507
|
addDelivery(entry: Omit<Delivery, 'id'>): Delivery;
|
|
410
508
|
findContactByPhone(phone: string): Contact | undefined;
|
|
411
509
|
contactMessages(contactId: number): Message[];
|
|
@@ -425,12 +523,20 @@ declare class Store {
|
|
|
425
523
|
* `429` is retried, any other answer that is not `2xx` is final;
|
|
426
524
|
* - a delivery that runs out of attempts counts one consecutive failure; at 5 the subscription is disabled
|
|
427
525
|
* (`too_many_failures`); a success resets the count;
|
|
428
|
-
* - each attempt checks again that the subscription exists, is `active` and still wants the type
|
|
526
|
+
* - each attempt checks again that the subscription exists, is `active` and still wants the type;
|
|
527
|
+
* - an event reaches only the subscriptions of its workspace, and `data.workspace_id` says which one (accounts
|
|
528
|
+
* v1.5.97 `EventWorkspaceRouting`, Q35 of 04-11): the workspace the emitter declares when it is one of the account,
|
|
529
|
+
* the default one otherwise. A subscription without a workspace counts as the default one's.
|
|
429
530
|
*/
|
|
430
531
|
|
|
431
532
|
interface EmitOptions {
|
|
432
533
|
id?: string;
|
|
433
534
|
occurredAt?: Date;
|
|
535
|
+
/**
|
|
536
|
+
* The workspace the event belongs to: the number's for `device.*`, the contact's or the group's for the rest.
|
|
537
|
+
* Missing, `null` or not a workspace of the account: the default one.
|
|
538
|
+
*/
|
|
539
|
+
workspaceId?: number | null;
|
|
434
540
|
}
|
|
435
541
|
interface Envelope$1 {
|
|
436
542
|
id: string;
|
|
@@ -451,7 +557,10 @@ declare class Webhooks {
|
|
|
451
557
|
get maxAttempts(): number;
|
|
452
558
|
/** Builds the envelope, exactly as it is delivered. */
|
|
453
559
|
envelope(type: string, data: unknown, options?: EmitOptions): Envelope$1;
|
|
454
|
-
/**
|
|
560
|
+
/**
|
|
561
|
+
* Emits one event to every active subscription of its workspace that wants it. Returns the envelope and how many
|
|
562
|
+
* got it.
|
|
563
|
+
*/
|
|
455
564
|
emit(type: string, data: unknown, options?: EmitOptions): {
|
|
456
565
|
envelope: Envelope$1;
|
|
457
566
|
subscriptions: number;
|
|
@@ -460,7 +569,10 @@ declare class Webhooks {
|
|
|
460
569
|
send(subscription: Subscription, type: string, body: string): Promise<SendResult>;
|
|
461
570
|
private shouldRetry;
|
|
462
571
|
private attempt;
|
|
463
|
-
/**
|
|
572
|
+
/**
|
|
573
|
+
* `POST /api/v1/webhooks/{id}/test`: one synchronous `webhook.test`, no retries, no counters touched. Its
|
|
574
|
+
* `data.workspace_id` is the subscription's workspace.
|
|
575
|
+
*/
|
|
464
576
|
test(subscription: Subscription): Promise<Record<string, unknown>>;
|
|
465
577
|
}
|
|
466
578
|
|
|
@@ -577,4 +689,4 @@ declare const FORM_TEMPLATES: FormTemplate[];
|
|
|
577
689
|
|
|
578
690
|
declare const VERSION: string;
|
|
579
691
|
|
|
580
|
-
export { type CatalogEntry, type ConfigInput, type CreateSandboxOptions, DELIVERY_HEADER, EDGE_ERRORS, EVENT_HEADER, FAILURE_REASONS, FORM_TEMPLATES, META_ERRORS, type PresetSubscription, REJECTIONS, REQUEST_ID_HEADER, SEND_FILE_TYPES, SIGNATURE_HEADER, SUPPORTED_EXTENSIONS, type Sandbox, type SandboxConfig, VERSION, catalog, createSandbox, defaultConfig, mergeConfig, signature };
|
|
692
|
+
export { type CatalogEntry, type ConfigInput, type CreateSandboxOptions, DELIVERY_HEADER, EDGE_ERRORS, EVENT_HEADER, FAILURE_REASONS, FORM_TEMPLATES, META_ERRORS, type PresetSubscription, type PresetWorkspace, REJECTIONS, REQUEST_ID_HEADER, SEND_FILE_TYPES, SIGNATURE_HEADER, SUPPORTED_EXTENSIONS, type Sandbox, type SandboxConfig, VERSION, catalog, createSandbox, defaultConfig, mergeConfig, signature };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { DELIVERY_HEADER, EDGE_ERRORS, EVENT_HEADER, FAILURE_REASONS, FORM_TEMPLATES, META_ERRORS, REJECTIONS, REQUEST_ID_HEADER, SEND_FILE_TYPES, SIGNATURE_HEADER, SUPPORTED_EXTENSIONS, VERSION, catalog, createSandbox, defaultConfig, mergeConfig, signature } from './chunk-
|
|
1
|
+
export { DELIVERY_HEADER, EDGE_ERRORS, EVENT_HEADER, FAILURE_REASONS, FORM_TEMPLATES, META_ERRORS, REJECTIONS, REQUEST_ID_HEADER, SEND_FILE_TYPES, SIGNATURE_HEADER, SUPPORTED_EXTENSIONS, VERSION, catalog, createSandbox, defaultConfig, mergeConfig, signature } from './chunk-FUK4UDIM.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
"rules": { "window": true, "antiblock": true, "rateLimit": true },
|
|
6
6
|
"templateApprovalSeconds": 5,
|
|
7
7
|
"numbers": { "limit": 5, "included": true, "trial": false, "metaConnect": false, "linksPerMinute": 10 },
|
|
8
|
+
"groupActions": true,
|
|
8
9
|
"publicUrl": null,
|
|
10
|
+
"workspaces": [{ "name": "Cliente B", "keys": ["2|fc_live_clienteB"] }],
|
|
9
11
|
"subscriptions": [
|
|
10
12
|
{
|
|
11
13
|
"url": "http://localhost:3000/hooks/funnelchat",
|
package/package.json
CHANGED
package/spec/procedencia.json
CHANGED
|
@@ -2,63 +2,67 @@
|
|
|
2
2
|
"description": "Where each file of spec/ came from. Written by `npm run generate -- --spec <file>` or `-- --pending <file>`; do not edit it by hand. spec/pending-sandbox.json belongs to this repo: its sources are inside it.",
|
|
3
3
|
"snapshot": {
|
|
4
4
|
"file": "spec/funnelchat.json",
|
|
5
|
-
"sha256": "
|
|
6
|
-
"bytes":
|
|
5
|
+
"sha256": "8228ea7369daed0655b779ca6af381355a6c1cc13dd702ed6e9c322779bafe94",
|
|
6
|
+
"bytes": 1214988,
|
|
7
7
|
"source": {
|
|
8
8
|
"repository": "git@github.com:Funnelchat20/developers-portal.git",
|
|
9
|
-
"path": "
|
|
10
|
-
"branch": "
|
|
11
|
-
"commit": "
|
|
12
|
-
"commit_date":
|
|
9
|
+
"path": "generated/specs/funnelchat-completa.json",
|
|
10
|
+
"branch": "main",
|
|
11
|
+
"commit": "f5464516",
|
|
12
|
+
"commit_date": null,
|
|
13
13
|
"file_tracked_in_git": false,
|
|
14
|
-
"tracked_files_clean":
|
|
15
|
-
"file_modified_at": "2026-
|
|
14
|
+
"tracked_files_clean": null,
|
|
15
|
+
"file_modified_at": "2026-11-05T16:59:01.577Z",
|
|
16
|
+
"note": "Built by `npm run build` of developers-portal at the merge of PR #37, deployed as v0.6.2 on 05-11. Not the published /specs/ file, which carries only the visible operations. communities' snapshot was taken with the group actions on."
|
|
16
17
|
},
|
|
17
|
-
"imported_at": "2026-
|
|
18
|
+
"imported_at": "2026-11-05T17:01:38.054Z",
|
|
18
19
|
"spec": {
|
|
19
20
|
"openapi": "3.1.0",
|
|
20
21
|
"title": "Funnelchat API",
|
|
21
22
|
"version": "v0",
|
|
22
|
-
"paths":
|
|
23
|
-
"operations":
|
|
24
|
-
"webhooks":
|
|
23
|
+
"paths": 217,
|
|
24
|
+
"operations": 264,
|
|
25
|
+
"webhooks": 29,
|
|
25
26
|
"domains": [
|
|
26
27
|
{
|
|
27
28
|
"domain": "conversations",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
29
|
+
"tag": "v1.19.21",
|
|
30
|
+
"sha": "f2937a71",
|
|
31
|
+
"branch": null,
|
|
32
|
+
"snapshot_at": null
|
|
31
33
|
},
|
|
32
34
|
{
|
|
33
35
|
"domain": "communities",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
36
|
+
"tag": "v2.35.5",
|
|
37
|
+
"sha": "2be913d4",
|
|
38
|
+
"branch": null,
|
|
39
|
+
"snapshot_at": null
|
|
37
40
|
},
|
|
38
41
|
{
|
|
39
42
|
"domain": "accounts",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
+
"tag": "v1.5.97",
|
|
44
|
+
"sha": "558332bcf",
|
|
45
|
+
"branch": null,
|
|
46
|
+
"snapshot_at": null
|
|
43
47
|
}
|
|
44
48
|
]
|
|
45
49
|
}
|
|
46
50
|
},
|
|
47
51
|
"pending": {
|
|
48
52
|
"file": "spec/pending.json",
|
|
49
|
-
"sha256": "
|
|
50
|
-
"bytes":
|
|
53
|
+
"sha256": "051680bd82944c0e248d20d1499d89e61a231c6ba3ef6a5d864cf18a97aae1ad",
|
|
54
|
+
"bytes": 112110,
|
|
51
55
|
"source": {
|
|
52
56
|
"repository": "git@github.com:Funnelchat20/funnelchat-node.git",
|
|
53
57
|
"path": "spec/pending.json",
|
|
54
|
-
"branch": "feat/
|
|
55
|
-
"commit": "
|
|
56
|
-
"commit_date": "2026-
|
|
58
|
+
"branch": "feat/enviar-por-telefono",
|
|
59
|
+
"commit": "a8f83509d4a40d6116a9c0487bf0aee639948273",
|
|
60
|
+
"commit_date": "2026-11-05T14:02:36-03:00",
|
|
57
61
|
"file_tracked_in_git": true,
|
|
58
62
|
"tracked_files_clean": true,
|
|
59
|
-
"file_modified_at": "2026-
|
|
63
|
+
"file_modified_at": "2026-11-05T17:01:15.771Z"
|
|
60
64
|
},
|
|
61
|
-
"imported_at": "2026-
|
|
65
|
+
"imported_at": "2026-11-05T17:03:20.911Z",
|
|
62
66
|
"note": "The pending layer of the Node SDK, unchanged. What belongs to the sandbox goes in spec/pending-sandbox.json."
|
|
63
67
|
}
|
|
64
68
|
}
|