fullstackgtm 0.34.0 → 0.38.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/INSTALL_FOR_AGENTS.md +8 -0
  3. package/README.md +39 -0
  4. package/dist/acquireLinkedIn.d.ts +41 -0
  5. package/dist/acquireLinkedIn.js +57 -0
  6. package/dist/acquireMeter.d.ts +67 -0
  7. package/dist/acquireMeter.js +145 -0
  8. package/dist/acquireSeen.d.ts +5 -0
  9. package/dist/acquireSeen.js +54 -0
  10. package/dist/assign.d.ts +83 -0
  11. package/dist/assign.js +146 -0
  12. package/dist/bin.js +14 -2
  13. package/dist/cli.js +817 -26
  14. package/dist/connectors/hubspot.js +140 -0
  15. package/dist/connectors/linkedin.d.ts +78 -0
  16. package/dist/connectors/linkedin.js +199 -0
  17. package/dist/connectors/prospectSources.d.ts +91 -0
  18. package/dist/connectors/prospectSources.js +227 -0
  19. package/dist/enrich.d.ts +107 -0
  20. package/dist/enrich.js +315 -5
  21. package/dist/format.d.ts +3 -1
  22. package/dist/format.js +14 -2
  23. package/dist/health.d.ts +71 -0
  24. package/dist/health.js +172 -0
  25. package/dist/icp.d.ts +96 -0
  26. package/dist/icp.js +256 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +2 -0
  29. package/dist/mappings.js +3 -0
  30. package/dist/reassign.d.ts +11 -2
  31. package/dist/reassign.js +13 -6
  32. package/dist/runReport.d.ts +9 -0
  33. package/dist/runReport.js +66 -0
  34. package/dist/types.d.ts +25 -1
  35. package/docs/api.md +53 -3
  36. package/docs/architecture.md +11 -1
  37. package/docs/dx-punch-list.md +87 -0
  38. package/docs/linkedin-connector-spec.md +87 -0
  39. package/llms.txt +38 -1
  40. package/package.json +1 -1
  41. package/skills/fullstackgtm/SKILL.md +5 -3
  42. package/src/acquireLinkedIn.ts +83 -0
  43. package/src/acquireMeter.ts +186 -0
  44. package/src/acquireSeen.ts +57 -0
  45. package/src/assign.ts +193 -0
  46. package/src/bin.ts +17 -4
  47. package/src/cli.ts +965 -25
  48. package/src/connectors/hubspot.ts +145 -0
  49. package/src/connectors/linkedin.ts +272 -0
  50. package/src/connectors/prospectSources.ts +324 -0
  51. package/src/enrich.ts +411 -5
  52. package/src/format.ts +20 -2
  53. package/src/health.ts +238 -0
  54. package/src/icp.ts +313 -0
  55. package/src/index.ts +18 -0
  56. package/src/mappings.ts +3 -0
  57. package/src/reassign.ts +24 -8
  58. package/src/runReport.ts +76 -0
  59. package/src/types.ts +32 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Opt-in CLI → hosted-app observability. When the user has paired the CLI
3
+ * (`login --via <url>` → broker token), each command fire-and-forgets a run
4
+ * record to the hosted app's `/api/cli/run`, authenticated by the broker token.
5
+ *
6
+ * Invariants: the CLI never requires web login; this does nothing when unpaired;
7
+ * it never throws and never changes the CLI's exit code or output. Reporting is
8
+ * pure added value for users who granted it.
9
+ */
10
+ import { getCredential } from "./credentials.ts";
11
+
12
+ type RunEvent = { ts: number; type: string; detail?: string };
13
+
14
+ let counts: Record<string, unknown> | undefined;
15
+ const events: RunEvent[] = [];
16
+
17
+ /** A command annotates its headline metrics (merged). */
18
+ export function reportCounts(values: Record<string, unknown>): void {
19
+ counts = { ...(counts ?? {}), ...values };
20
+ }
21
+
22
+ /** A command annotates a structured event (plan saved, meter charged, …). */
23
+ export function reportEvent(type: string, detail?: string): void {
24
+ events.push({ ts: Date.now(), type, detail });
25
+ }
26
+
27
+ // Setup/inspection verbs aren't interesting as "runs" — skip the noise.
28
+ const SKIP_COMMANDS = new Set([
29
+ "login",
30
+ "logout",
31
+ "doctor",
32
+ "help",
33
+ "profiles",
34
+ "--help",
35
+ "-h",
36
+ "--version",
37
+ "-v",
38
+ ]);
39
+
40
+ /**
41
+ * Send the run record if the CLI is paired. Best-effort: a 4s-capped POST that
42
+ * swallows every error. Call once per process from the entry point.
43
+ */
44
+ export async function flushRunReport(
45
+ args: string[],
46
+ status: "success" | "partial" | "error",
47
+ startedAt: number,
48
+ error?: string,
49
+ ): Promise<void> {
50
+ const command = args[0];
51
+ if (!command || SKIP_COMMANDS.has(command)) return;
52
+ const broker = getCredential("broker");
53
+ if (!broker?.baseUrl || !broker.accessToken) return; // opt-in: only when paired
54
+
55
+ const sub = args[1] && !args[1].startsWith("-") ? ` ${args[1]}` : "";
56
+ const finishedAt = Date.now();
57
+ try {
58
+ await fetch(`${broker.baseUrl.replace(/\/+$/, "")}/api/cli/run`, {
59
+ method: "POST",
60
+ headers: { Authorization: `Bearer ${broker.accessToken}`, "Content-Type": "application/json" },
61
+ body: JSON.stringify({
62
+ command: `${command}${sub}`,
63
+ status,
64
+ startedAt,
65
+ finishedAt,
66
+ durationMs: finishedAt - startedAt,
67
+ counts,
68
+ events: events.length ? events : undefined,
69
+ error,
70
+ }),
71
+ signal: AbortSignal.timeout(4000),
72
+ });
73
+ } catch {
74
+ // Observability is best-effort; never affect the CLI outcome.
75
+ }
76
+ }
package/src/types.ts CHANGED
@@ -43,11 +43,41 @@ export type PatchOperationType =
43
43
  | "link_record"
44
44
  | "archive_record"
45
45
  | "create_task"
46
+ // Create a NET-NEW record (a sourced lead). beforeValue is null (nothing
47
+ // existed); afterValue is a CreateRecordPayload. The connector re-resolves
48
+ // on matchKey at apply time (search is the source of truth; the plan-time
49
+ // snapshot can be stale) and creates only on a confirmed miss, so apply is
50
+ // resolve-first and never double-creates a record a concurrent writer added.
51
+ // Emitted only by `enrich acquire`, and metered against the acquire budget.
52
+ | "create_record"
46
53
  // Merge a duplicate group into a survivor. beforeValue is the group's
47
54
  // record ids; afterValue is the survivor id (requires_human_survivor_selection
48
55
  // until a human picks). IRREVERSIBLE on every provider that supports it.
49
56
  | "merge_records";
50
57
 
58
+ /**
59
+ * The afterValue of a `create_record` operation. The connector re-resolves on
60
+ * `matchKey`/`matchValue` at apply time and creates only on a confirmed miss.
61
+ * `estCostUsd` is the acquire meter's per-record charge, recorded against the
62
+ * budget on a successful create.
63
+ */
64
+ export type CreateRecordPayload = {
65
+ properties: Record<string, string>;
66
+ matchKey: string;
67
+ matchValue: string;
68
+ source: string;
69
+ estCostUsd?: number;
70
+ associateCompanyName?: string;
71
+ /**
72
+ * Owner to stamp on the new record (canonical owner id). The connector maps
73
+ * it to the CRM's owner property (HubSpot `hubspot_owner_id`) at create time
74
+ * so a lead is never born ownerless. Absent = leave unassigned.
75
+ */
76
+ ownerId?: string;
77
+ /** Audit label for how the owner was chosen (e.g. "fixed", "territory:0"). */
78
+ assignedBy?: string;
79
+ };
80
+
51
81
  export type AuditFindingSeverity = "info" | "warning" | "critical";
52
82
 
53
83
  /**
@@ -186,6 +216,8 @@ export type CanonicalContact = {
186
216
  email?: string;
187
217
  phone?: string;
188
218
  title?: string;
219
+ /** LinkedIn profile URL — the strongest cross-system identity key for dedup. */
220
+ linkedin?: string;
189
221
  ownerId?: string;
190
222
  lastActivityAt?: string;
191
223
  lastSyncAt?: string;