fullstackgtm 0.45.0 → 0.47.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/CHANGELOG.md +115 -1
- package/INSTALL_FOR_AGENTS.md +13 -11
- package/README.md +27 -18
- package/dist/backfill.d.ts +86 -0
- package/dist/backfill.js +251 -0
- package/dist/bin.js +4 -1
- package/dist/cli/auth.d.ts +2 -0
- package/dist/cli/auth.js +119 -12
- package/dist/cli/backfill.d.ts +1 -0
- package/dist/cli/backfill.js +125 -0
- package/dist/cli/backfillRuns.d.ts +1 -0
- package/dist/cli/backfillRuns.js +187 -0
- package/dist/cli/call.js +23 -9
- package/dist/cli/enrich.js +28 -10
- package/dist/cli/fix.js +9 -7
- package/dist/cli/help.js +60 -23
- package/dist/cli/plans.js +13 -11
- package/dist/cli/shared.d.ts +4 -3
- package/dist/cli/shared.js +31 -20
- package/dist/cli/ui.d.ts +21 -0
- package/dist/cli/ui.js +53 -1
- package/dist/cli.js +37 -41
- package/dist/connector.d.ts +8 -0
- package/dist/connector.js +104 -21
- package/dist/connectors/hubspot.d.ts +8 -1
- package/dist/connectors/hubspot.js +406 -13
- package/dist/connectors/hubspotAuth.js +5 -1
- package/dist/connectors/linkedin.js +14 -14
- package/dist/connectors/salesforce.d.ts +8 -1
- package/dist/connectors/salesforce.js +163 -2
- package/dist/connectors/stripe.d.ts +37 -0
- package/dist/connectors/stripe.js +103 -31
- package/dist/enrich.d.ts +7 -0
- package/dist/enrich.js +7 -0
- package/dist/health.d.ts +11 -69
- package/dist/health.js +4 -134
- package/dist/healthScore.d.ts +71 -0
- package/dist/healthScore.js +143 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/llm.d.ts +29 -0
- package/dist/llm.js +206 -0
- package/dist/market.d.ts +39 -1
- package/dist/market.js +116 -44
- package/dist/marketClassify.d.ts +9 -1
- package/dist/marketClassify.js +10 -1
- package/dist/marketTaxonomy.d.ts +6 -1
- package/dist/marketTaxonomy.js +4 -2
- package/dist/mcp-bin.js +29 -0
- package/dist/mcp.js +117 -4
- package/dist/progress.d.ts +96 -0
- package/dist/progress.js +142 -0
- package/dist/runReport.d.ts +24 -0
- package/dist/runReport.js +139 -4
- package/dist/types.d.ts +33 -1
- package/docs/api.md +4 -2
- package/docs/architecture.md +2 -0
- package/docs/linkedin-connector-spec.md +1 -1
- package/llms.txt +3 -3
- package/package.json +10 -3
- package/skills/fullstackgtm/SKILL.md +1 -0
- package/src/backfill.ts +340 -0
- package/src/bin.ts +5 -1
- package/src/cli/auth.ts +135 -15
- package/src/cli/backfill.ts +156 -0
- package/src/cli/backfillRuns.ts +198 -0
- package/src/cli/call.ts +26 -10
- package/src/cli/enrich.ts +33 -10
- package/src/cli/fix.ts +11 -10
- package/src/cli/help.ts +61 -23
- package/src/cli/plans.ts +15 -14
- package/src/cli/shared.ts +44 -27
- package/src/cli/ui.ts +72 -1
- package/src/cli.ts +38 -41
- package/src/connector.ts +110 -16
- package/src/connectors/hubspot.ts +423 -14
- package/src/connectors/hubspotAuth.ts +5 -1
- package/src/connectors/linkedin.ts +14 -14
- package/src/connectors/salesforce.ts +168 -3
- package/src/connectors/stripe.ts +154 -34
- package/src/enrich.ts +13 -0
- package/src/health.ts +14 -213
- package/src/healthScore.ts +223 -0
- package/src/index.ts +28 -1
- package/src/llm.ts +239 -0
- package/src/market.ts +157 -44
- package/src/marketClassify.ts +18 -1
- package/src/marketTaxonomy.ts +10 -2
- package/src/mcp-bin.ts +32 -0
- package/src/mcp.ts +140 -6
- package/src/progress.ts +197 -0
- package/src/runReport.ts +159 -4
- package/src/types.ts +35 -2
- package/docs/dx-punch-list.md +0 -87
- package/docs/roadmap-to-1.0.md +0 -211
package/src/connector.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { dedupeKey } from "./dedupe.ts";
|
|
2
|
+
import { APPLY_STAGES, type ProgressEmitter } from "./progress.ts";
|
|
2
3
|
import { requiresHumanInput } from "./rules.ts";
|
|
3
4
|
import type {
|
|
4
5
|
CanonicalGtmSnapshot,
|
|
@@ -112,6 +113,13 @@ export type ApplyPatchPlanOptions = {
|
|
|
112
113
|
* full operation count.
|
|
113
114
|
*/
|
|
114
115
|
onOperation?: (progress: ApplyProgress) => void;
|
|
116
|
+
/**
|
|
117
|
+
* Shared progress vocabulary (src/progress.ts): the run emits the
|
|
118
|
+
* APPLY_STAGES (preflight → operations → results), one `opResult` per
|
|
119
|
+
* resolved operation (id + status only — no values), and `items` over the
|
|
120
|
+
* plan's operations. Additive alongside `onOperation`; presentation only.
|
|
121
|
+
*/
|
|
122
|
+
progress?: ProgressEmitter;
|
|
115
123
|
};
|
|
116
124
|
|
|
117
125
|
export type ApplyProgress = {
|
|
@@ -130,6 +138,17 @@ function normalizeForComparison(value: unknown): string | null {
|
|
|
130
138
|
return String(value);
|
|
131
139
|
}
|
|
132
140
|
|
|
141
|
+
function isApplyBatchCandidate(operation: PatchOperation): boolean {
|
|
142
|
+
return (
|
|
143
|
+
((operation.operation === "set_field" || operation.operation === "clear_field") && Boolean(operation.field)) ||
|
|
144
|
+
operation.operation === "archive_record"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function sameApplyBatchKind(a: PatchOperation, b: PatchOperation): boolean {
|
|
149
|
+
return a.operation === b.operation && a.objectType === b.objectType;
|
|
150
|
+
}
|
|
151
|
+
|
|
133
152
|
/**
|
|
134
153
|
* Apply an approved subset of a patch plan through a connector.
|
|
135
154
|
*
|
|
@@ -148,6 +167,9 @@ export async function applyPatchPlan(
|
|
|
148
167
|
}
|
|
149
168
|
|
|
150
169
|
const startedAt = new Date().toISOString();
|
|
170
|
+
const emitStage = (stage: (typeof APPLY_STAGES)[number]) =>
|
|
171
|
+
options.progress?.stage(stage, APPLY_STAGES.indexOf(stage), APPLY_STAGES.length);
|
|
172
|
+
emitStage("preflight");
|
|
151
173
|
const approved = new Set(options.approvedOperationIds);
|
|
152
174
|
const checkConflicts =
|
|
153
175
|
options.checkConflicts ?? typeof connector.readField === "function";
|
|
@@ -224,6 +246,7 @@ export async function applyPatchPlan(
|
|
|
224
246
|
: "Operation was not approved.",
|
|
225
247
|
});
|
|
226
248
|
}
|
|
249
|
+
emitStage("results");
|
|
227
250
|
return {
|
|
228
251
|
planId: plan.id,
|
|
229
252
|
provider: connector.provider,
|
|
@@ -255,6 +278,11 @@ export async function applyPatchPlan(
|
|
|
255
278
|
if (checkConflicts && connector.readField) {
|
|
256
279
|
for (const operation of plan.operations) {
|
|
257
280
|
if (!approved.has(operation.id) || conflicts.has(operation.id)) continue;
|
|
281
|
+
// Generic batch connectors (Salesforce) do their own batched CAS read for
|
|
282
|
+
// eligible field writes, so don't pre-read those one-by-one here.
|
|
283
|
+
if (connector.applyBatch && isApplyBatchCandidate(operation) && !operation.groupId && !operation.preconditions?.length) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
258
286
|
let conflict: PatchOperationResult | null = null;
|
|
259
287
|
if (operation.field && FIELD_WRITE_OPERATIONS.has(operation.operation)) {
|
|
260
288
|
const current = await connector.readField(
|
|
@@ -336,25 +364,35 @@ export async function applyPatchPlan(
|
|
|
336
364
|
const resultsBefore = results.length;
|
|
337
365
|
let lastNotified = results.length;
|
|
338
366
|
const notifyProgress = () => {
|
|
339
|
-
if (!options.onOperation || results.length === lastNotified) return;
|
|
340
|
-
lastNotified
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
367
|
+
if ((!options.onOperation && !options.progress) || results.length === lastNotified) return;
|
|
368
|
+
while (lastNotified < results.length) {
|
|
369
|
+
const last = results[lastNotified];
|
|
370
|
+
lastNotified += 1;
|
|
371
|
+
if (last.status === "applied") progressCounts.applied += 1;
|
|
372
|
+
else if (last.status === "failed") progressCounts.failed += 1;
|
|
373
|
+
else if (last.status === "conflict") progressCounts.conflicts += 1;
|
|
374
|
+
else progressCounts.skipped += 1;
|
|
375
|
+
// Shared vocabulary: operation id + status only — a conflict/failure
|
|
376
|
+
// detail can echo field values, which never leave the machine.
|
|
377
|
+
options.progress?.opResult(last.operationId, last.status);
|
|
378
|
+
options.progress?.items(results.length - resultsBefore, plan.operations.length);
|
|
379
|
+
if (options.onOperation) {
|
|
380
|
+
try {
|
|
381
|
+
options.onOperation({
|
|
382
|
+
completed: lastNotified - resultsBefore,
|
|
383
|
+
total: plan.operations.length,
|
|
384
|
+
...progressCounts,
|
|
385
|
+
});
|
|
386
|
+
} catch {
|
|
387
|
+
// progress is presentation-only
|
|
388
|
+
}
|
|
389
|
+
}
|
|
354
390
|
}
|
|
355
391
|
};
|
|
356
392
|
|
|
357
|
-
|
|
393
|
+
emitStage("operations");
|
|
394
|
+
for (let opIndex = 0; opIndex < plan.operations.length; opIndex++) {
|
|
395
|
+
const operation = plan.operations[opIndex];
|
|
358
396
|
// Report the previous iteration's result (guarded: no-op if it pushed
|
|
359
397
|
// nothing). One-iteration lag keeps this a two-line hook instead of a
|
|
360
398
|
// notify after all ten push sites; the loop-exit call below flushes the last.
|
|
@@ -422,6 +460,61 @@ export async function applyPatchPlan(
|
|
|
422
460
|
continue;
|
|
423
461
|
}
|
|
424
462
|
|
|
463
|
+
if (connector.applyBatch && isApplyBatchCandidate(operation) && (checkConflicts || operation.operation === "archive_record") && !operation.groupId && !operation.preconditions?.length) {
|
|
464
|
+
const chunk: PatchOperation[] = [];
|
|
465
|
+
const applyBatchLimit = Math.max(1, connector.applyBatchLimit ?? 200);
|
|
466
|
+
for (let scan = opIndex; scan < plan.operations.length && chunk.length < applyBatchLimit; scan++) {
|
|
467
|
+
const candidate = plan.operations[scan];
|
|
468
|
+
if (!sameApplyBatchKind(operation, candidate) || !isApplyBatchCandidate(candidate)) break;
|
|
469
|
+
const candidateOverride = options.valueOverrides?.[candidate.id];
|
|
470
|
+
if (
|
|
471
|
+
!approved.has(candidate.id) ||
|
|
472
|
+
(requiresHumanInput(candidate.afterValue) && candidateOverride === undefined) ||
|
|
473
|
+
conflicts.has(candidate.id) ||
|
|
474
|
+
Boolean(candidate.groupId) ||
|
|
475
|
+
Boolean(candidate.preconditions?.length) ||
|
|
476
|
+
(staleByFilter && staleByFilter.has(candidate.objectId)) ||
|
|
477
|
+
irreversibleStale.has(candidate.id)
|
|
478
|
+
) {
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
chunk.push(candidateOverride === undefined ? candidate : { ...candidate, afterValue: candidateOverride });
|
|
482
|
+
}
|
|
483
|
+
if (chunk.length > 1) {
|
|
484
|
+
try {
|
|
485
|
+
const batchResults = await connector.applyBatch(chunk);
|
|
486
|
+
const byOperationId = new Map(batchResults.map((result) => [result.operationId, result]));
|
|
487
|
+
for (const batchOperation of chunk) {
|
|
488
|
+
const result = byOperationId.get(batchOperation.id) ?? {
|
|
489
|
+
operationId: batchOperation.id,
|
|
490
|
+
status: "failed" as const,
|
|
491
|
+
detail: "Batch connector did not return a result for this operation.",
|
|
492
|
+
};
|
|
493
|
+
results.push(result);
|
|
494
|
+
if (result.status === "applied" || result.status === "failed") attempted += 1;
|
|
495
|
+
if (result.status === "applied") applied += 1;
|
|
496
|
+
}
|
|
497
|
+
const appliedInBatch = batchResults.filter((result) => result.status === "applied").length;
|
|
498
|
+
appliedSinceRecheck += appliedInBatch;
|
|
499
|
+
if (appliedInBatch > 0 && (applied === appliedInBatch || appliedSinceRecheck >= recheckEvery)) {
|
|
500
|
+
appliedSinceRecheck = 0;
|
|
501
|
+
await refreshSnapshotChecks();
|
|
502
|
+
}
|
|
503
|
+
} catch (error) {
|
|
504
|
+
for (const batchOperation of chunk) {
|
|
505
|
+
results.push({
|
|
506
|
+
operationId: batchOperation.id,
|
|
507
|
+
status: "failed",
|
|
508
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
509
|
+
});
|
|
510
|
+
attempted += 1;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
opIndex += chunk.length - 1;
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
425
518
|
const resolved: PatchOperation =
|
|
426
519
|
override === undefined ? operation : { ...operation, afterValue: override };
|
|
427
520
|
|
|
@@ -449,6 +542,7 @@ export async function applyPatchPlan(
|
|
|
449
542
|
}
|
|
450
543
|
}
|
|
451
544
|
notifyProgress();
|
|
545
|
+
emitStage("results");
|
|
452
546
|
|
|
453
547
|
return {
|
|
454
548
|
planId: plan.id,
|