fullstackgtm 0.55.0 → 0.55.2
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 +21 -0
- package/CONTRIBUTING.md +6 -0
- package/dist/cli/enrich.js +14 -3
- package/dist/cli/help.js +2 -2
- package/dist/cli/shared.d.ts +9 -0
- package/dist/cli/shared.js +37 -18
- package/package.json +2 -2
- package/src/cli/enrich.ts +13 -3
- package/src/cli/help.ts +2 -2
- package/src/cli/shared.ts +37 -14
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ The path to 1.0 is planned in [docs/roadmap-to-1.0.md](https://github.com/fullst
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.55.2] — 2026-07-13
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The OSS repository now publishes a representative contributor test suite
|
|
15
|
+
covering CLI contracts, planning, configuration, HTTP safety, and security,
|
|
16
|
+
while hosted, live-integration, and adversarial release tests remain private.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- OSS mirror generation and `npm test` now fail when zero public tests are
|
|
21
|
+
discovered, preventing false-green contributor and release checks.
|
|
22
|
+
|
|
23
|
+
## [0.55.1] — 2026-07-13
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Interactive masked login prompts no longer repaint an empty
|
|
28
|
+
`0 characters received` line after Enter is pressed. Submission now settles
|
|
29
|
+
the renderer before `readline` clears its internal input buffer.
|
|
30
|
+
|
|
10
31
|
## [0.55.0] — 2026-07-13
|
|
11
32
|
|
|
12
33
|
### Added
|
package/CONTRIBUTING.md
CHANGED
|
@@ -64,6 +64,12 @@ Linux, macOS, and Windows; source-level tests remain on Node 22.6+.
|
|
|
64
64
|
> (a `pretest` guard) rather than silently passing with zero tests. On the
|
|
65
65
|
> published mirror, `tests/` is present and `npm test` works there.
|
|
66
66
|
|
|
67
|
+
The public mirror includes a representative suite covering supported CLI,
|
|
68
|
+
planning, configuration, HTTP, and security behavior. Additional hosted-product,
|
|
69
|
+
live-integration, and adversarial release tests remain in the private monorepo;
|
|
70
|
+
both suites gate releases. The public test selection is reviewed explicitly in
|
|
71
|
+
`oss/public-tests.txt`, and `npm test` fails if the mirror contains zero tests.
|
|
72
|
+
|
|
67
73
|
**Node:** the published runtime supports Node ≥ 20 (`engines`), but the test
|
|
68
74
|
runner needs Node **≥ 22.6** for `--experimental-strip-types`. Develop on 22.6+.
|
|
69
75
|
|
package/dist/cli/enrich.js
CHANGED
|
@@ -44,7 +44,7 @@ enrich append [--source apollo] [--objects companies,contacts] [--save] [--conf
|
|
|
44
44
|
enrich refresh [--source apollo] [--stale-days <n>] [--save] [--config <path>]
|
|
45
45
|
[source options] [--run-label <label>] [--json]
|
|
46
46
|
enrich ingest <file.csv|payload.json|spool.jsonl|spool-dir> --source clay [--run-label <label>] [--objects companies|contacts] [--config <path>]
|
|
47
|
-
enrich acquire [--source <id>] [--max <new-leads>] [--scan-limit <candidates>] [--list <id>] [--assign-owner <id>] [--save] [--config <path>] [--json]
|
|
47
|
+
enrich acquire [--source <id>] [--max <new-leads>] [--scan-limit <candidates>] [--company-domain <domain>] [--list <id>] [--assign-owner <id>] [--save] [--config <path>] [--json]
|
|
48
48
|
enrich status [--runs] [--source <id>] [--config <path>] [--json]
|
|
49
49
|
|
|
50
50
|
acquire creates NET-NEW leads from a staged prospect list (ingest first):
|
|
@@ -515,8 +515,14 @@ async function acquireFromApi(config, source, rest, icp, snapshot, seen, priorRu
|
|
|
515
515
|
: disc.provider === "clay"
|
|
516
516
|
? (icp ? icpToClayPeopleFilters(icp) : (disc.filters ?? {}))
|
|
517
517
|
: {};
|
|
518
|
+
const targetCompanyDomain = option(rest, "--company-domain")?.trim().toLowerCase().replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "");
|
|
519
|
+
if (targetCompanyDomain) {
|
|
520
|
+
if (disc.provider !== "clay")
|
|
521
|
+
throw new Error("enrich acquire: --company-domain currently requires --source clay");
|
|
522
|
+
filters = { ...filters, company_identifier: [targetCompanyDomain] };
|
|
523
|
+
}
|
|
518
524
|
const queryFingerprint = createHash("sha256")
|
|
519
|
-
.update(JSON.stringify({ source, provider: disc.provider, listId, filters, icp: icp ?? null }))
|
|
525
|
+
.update(JSON.stringify({ source, provider: disc.provider, listId, filters, icp: icp ?? null, targetCompanyDomain: targetCompanyDomain ?? null }))
|
|
520
526
|
.digest("hex");
|
|
521
527
|
const checkpointKey = {
|
|
522
528
|
provider: disc.provider,
|
|
@@ -599,7 +605,7 @@ async function acquireFromApi(config, source, rest, icp, snapshot, seen, priorRu
|
|
|
599
605
|
if (!cursor) {
|
|
600
606
|
const route = clayRoutes[clayRouteIndex];
|
|
601
607
|
if (route)
|
|
602
|
-
filters = route.filters;
|
|
608
|
+
filters = { ...route.filters, ...(targetCompanyDomain ? { company_identifier: [targetCompanyDomain] } : {}) };
|
|
603
609
|
progress.note(`creating Clay people-search iterator · ${route?.id ?? "configured"}`);
|
|
604
610
|
cursor = await createClaySearch({ apiKey: providerKey("clay"), sourceType: "people", filters });
|
|
605
611
|
}
|
|
@@ -643,6 +649,8 @@ async function acquireFromApi(config, source, rest, icp, snapshot, seen, priorRu
|
|
|
643
649
|
}
|
|
644
650
|
discovered += page.length;
|
|
645
651
|
progress.items(discovered, scanLimit);
|
|
652
|
+
if (targetCompanyDomain)
|
|
653
|
+
page = page.filter((prospect) => normalizedCompanyDomain(prospect.companyDomain) === targetCompanyDomain);
|
|
646
654
|
if (page.length === 0) {
|
|
647
655
|
exhausted = true;
|
|
648
656
|
break;
|
|
@@ -885,6 +893,9 @@ function printAcquireOutput(options) {
|
|
|
885
893
|
console.log("The meter is charged only when a create lands at apply.");
|
|
886
894
|
}
|
|
887
895
|
}
|
|
896
|
+
function normalizedCompanyDomain(value) {
|
|
897
|
+
return (value ?? "").trim().toLowerCase().replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "");
|
|
898
|
+
}
|
|
888
899
|
function hostedRunsUrl() {
|
|
889
900
|
const baseUrl = getCredential("broker")?.baseUrl?.replace(/\/+$/, "");
|
|
890
901
|
return baseUrl ? `${baseUrl}/dashboard/runs` : null;
|
package/dist/cli/help.js
CHANGED
|
@@ -630,7 +630,7 @@ export const COMMAND_FLAGS = {
|
|
|
630
630
|
dedupe: [...SOURCE_FLAGS, "--key", "--keep", "--reason", "--max-operations", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
631
631
|
reassign: [...SOURCE_FLAGS, "--from", "--assign-unowned", "--to", "--objects", "--where", "--except-deal-stage", "--include-closed-deals", "--reason", "--max-operations", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
632
632
|
backfill: [...SOURCE_FLAGS, "--since", "--pipeline", "--match-property", "--skip-unmatched", "--save", "--dry-run", "--verbose", "--json"],
|
|
633
|
-
enrich: [...SOURCE_FLAGS, "--config", "--source", "--icp", "--input", "--provider", "--list", "--stale-days", "--assign-owner", "--objects", "--max", "--scan-limit", "--staged-run", "--run-label", "--label", "--runs", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
633
|
+
enrich: [...SOURCE_FLAGS, "--config", "--source", "--icp", "--input", "--provider", "--list", "--stale-days", "--assign-owner", "--objects", "--max", "--scan-limit", "--company-domain", "--staged-run", "--run-label", "--label", "--runs", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
634
634
|
call: [...SOURCE_FLAGS, "--transcript", "--title", "--source", "--model", "--deterministic", "--heuristics", "--llm", "--verbose", "--json", "--ndjson", "--out", "--call", "--call-type", "--rubric", "--list", "--list-rubrics", "--attendees", "--domain", "--deal", "--save", "--dry-run"],
|
|
635
635
|
suggest: [...SOURCE_FLAGS, "--plan-id", "--plan", "--json", "--out"],
|
|
636
636
|
plans: ["--status", "--operations", "--value", "--values-from", "--min-confidence", "--include-creates", "--acknowledge-uncertain-writes", "--verbose", "--json"],
|
|
@@ -645,7 +645,7 @@ export const COMMAND_FLAGS = {
|
|
|
645
645
|
schedule: ["--cron", "--label", "--provider", "--trigger", "--timer", "--runs", "--verbose", "--json"],
|
|
646
646
|
};
|
|
647
647
|
export const FLAGS_WITH_VALUES = new Set([
|
|
648
|
-
"--account", "--account-id", "--accounts", "--acv", "--acv-basis", "--after", "--anchor", "--api-key", "--approve", "--archive", "--assign-owner", "--attendees", "--before", "--bucket", "--buyers-per-account", "--call", "--call-type", "--calls", "--capture-run", "--category", "--channel", "--client", "--client-id", "--client-secret", "--config", "--connector", "--connector-opt", "--contact", "--cron", "--cross-checks", "--deal", "--deal-period", "--diff", "--domain", "--email", "--except-deal-stage", "--format", "--from", "--from-judge", "--golden", "--guard", "--icp", "--in", "--input", "--instance-url", "--keep", "--key", "--keywords", "--label", "--list", "--login-url", "--match", "--match-property", "--max", "--max-accounts", "--max-claims", "--max-credits", "--max-examples", "--max-operations", "--max-results", "--max-searches", "--max-usd", "--min-accuracy", "--min-confidence", "--min-mentions", "--min-score", "--model", "--name", "--objects", "--operations", "--out", "--pipeline", "--plan", "--plan-id", "--policy", "--port", "--prepared-by", "--prior-run", "--profile", "--promote-lift", "--prompt", "--provider", "--reason", "--require", "--result", "--rubric", "--rule", "--rules", "--run", "--run-label", "--runs", "--scan-limit", "--scopes", "--seed", "--set", "--signals-from", "--since", "--snapshot", "--source", "--staged-run", "--stale-days", "--status", "--task-account", "--task-deal", "--timer", "--title", "--to", "--today", "--token", "--token-env", "--touch", "--transcript", "--trigger", "--usd-per-credit", "--value", "--values-from", "--vendor", "--via", "--watchlist", "--where",
|
|
648
|
+
"--account", "--account-id", "--accounts", "--acv", "--acv-basis", "--after", "--anchor", "--api-key", "--approve", "--archive", "--assign-owner", "--attendees", "--before", "--bucket", "--buyers-per-account", "--call", "--call-type", "--calls", "--capture-run", "--category", "--channel", "--client", "--client-id", "--client-secret", "--company-domain", "--config", "--connector", "--connector-opt", "--contact", "--cron", "--cross-checks", "--deal", "--deal-period", "--diff", "--domain", "--email", "--except-deal-stage", "--format", "--from", "--from-judge", "--golden", "--guard", "--icp", "--in", "--input", "--instance-url", "--keep", "--key", "--keywords", "--label", "--list", "--login-url", "--match", "--match-property", "--max", "--max-accounts", "--max-claims", "--max-credits", "--max-examples", "--max-operations", "--max-results", "--max-searches", "--max-usd", "--min-accuracy", "--min-confidence", "--min-mentions", "--min-score", "--model", "--name", "--objects", "--operations", "--out", "--pipeline", "--plan", "--plan-id", "--policy", "--port", "--prepared-by", "--prior-run", "--profile", "--promote-lift", "--prompt", "--provider", "--reason", "--require", "--result", "--rubric", "--rule", "--rules", "--run", "--run-label", "--runs", "--scan-limit", "--scopes", "--seed", "--set", "--signals-from", "--since", "--snapshot", "--source", "--staged-run", "--stale-days", "--status", "--task-account", "--task-deal", "--timer", "--title", "--to", "--today", "--token", "--token-env", "--touch", "--transcript", "--trigger", "--usd-per-credit", "--value", "--values-from", "--vendor", "--via", "--watchlist", "--where",
|
|
649
649
|
]);
|
|
650
650
|
// Lifecycle-grouped front door. One line per verb, organized by the
|
|
651
651
|
// Prevent→Detect→Remediate→Verify loop so a new user sees ~6 jobs, not 22 verbs.
|
package/dist/cli/shared.d.ts
CHANGED
|
@@ -65,6 +65,15 @@ export declare function loadIcp(args: string[]): Icp | undefined;
|
|
|
65
65
|
* e.g. `echo "$TOKEN" | fullstackgtm login hubspot`
|
|
66
66
|
* - interactive: prompted on the TTY with the input muted
|
|
67
67
|
*/
|
|
68
|
+
export declare function createSecretMaskRenderer(options: {
|
|
69
|
+
label: string;
|
|
70
|
+
lineLength: () => number;
|
|
71
|
+
write: (value: string) => void;
|
|
72
|
+
}): {
|
|
73
|
+
mute(): void;
|
|
74
|
+
settle(): void;
|
|
75
|
+
write(value: string): void;
|
|
76
|
+
};
|
|
68
77
|
export declare function readSecret(label: string): Promise<string>;
|
|
69
78
|
export declare function isOptionValue(args: string[], arg: string): boolean;
|
|
70
79
|
export declare function readPackageInfo(): {
|
package/dist/cli/shared.js
CHANGED
|
@@ -288,6 +288,35 @@ export function loadIcp(args) {
|
|
|
288
288
|
* e.g. `echo "$TOKEN" | fullstackgtm login hubspot`
|
|
289
289
|
* - interactive: prompted on the TTY with the input muted
|
|
290
290
|
*/
|
|
291
|
+
export function createSecretMaskRenderer(options) {
|
|
292
|
+
let muted = false;
|
|
293
|
+
let renderQueued = false;
|
|
294
|
+
let settled = false;
|
|
295
|
+
return {
|
|
296
|
+
mute() { muted = true; },
|
|
297
|
+
settle() { settled = true; },
|
|
298
|
+
write(value) {
|
|
299
|
+
if (!muted) {
|
|
300
|
+
options.write(value);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (settled || renderQueued)
|
|
304
|
+
return;
|
|
305
|
+
renderQueued = true;
|
|
306
|
+
queueMicrotask(() => {
|
|
307
|
+
renderQueued = false;
|
|
308
|
+
// readline clears `line` during close. Submission settles this renderer
|
|
309
|
+
// first so an Enter-triggered repaint cannot redraw an empty prompt
|
|
310
|
+
// after the final mask/newline ("0 characters received").
|
|
311
|
+
if (settled)
|
|
312
|
+
return;
|
|
313
|
+
const length = options.lineLength();
|
|
314
|
+
const mask = `${"•".repeat(Math.min(length, 12))}${length > 12 ? "…" : ""}`;
|
|
315
|
+
options.write(`\r\u001b[2K${options.label}: ${mask} (${length} character${length === 1 ? "" : "s"} received)`);
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
291
320
|
export async function readSecret(label) {
|
|
292
321
|
if (!process.stdin.isTTY) {
|
|
293
322
|
const chunks = [];
|
|
@@ -311,30 +340,20 @@ export async function readSecret(label) {
|
|
|
311
340
|
output: process.stderr,
|
|
312
341
|
terminal: true,
|
|
313
342
|
});
|
|
314
|
-
let muted = false;
|
|
315
343
|
const mutable = rl;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (renderQueued)
|
|
323
|
-
return;
|
|
324
|
-
renderQueued = true;
|
|
325
|
-
queueMicrotask(() => {
|
|
326
|
-
renderQueued = false;
|
|
327
|
-
const length = mutable.line?.length ?? 0;
|
|
328
|
-
const mask = `${"•".repeat(Math.min(length, 12))}${length > 12 ? "…" : ""}`;
|
|
329
|
-
process.stderr.write(`\r\u001b[2K${label}: ${mask} (${length} character${length === 1 ? "" : "s"} received)`);
|
|
330
|
-
});
|
|
331
|
-
};
|
|
344
|
+
const renderer = createSecretMaskRenderer({
|
|
345
|
+
label,
|
|
346
|
+
lineLength: () => mutable.line?.length ?? 0,
|
|
347
|
+
write: (value) => process.stderr.write(value),
|
|
348
|
+
});
|
|
349
|
+
mutable._writeToOutput = (value) => renderer.write(value);
|
|
332
350
|
rl.question(`${label}: `, (answer) => {
|
|
351
|
+
renderer.settle();
|
|
333
352
|
rl.close();
|
|
334
353
|
process.stderr.write("\n");
|
|
335
354
|
resolveSecret(answer.trim());
|
|
336
355
|
});
|
|
337
|
-
|
|
356
|
+
renderer.mute();
|
|
338
357
|
});
|
|
339
358
|
}
|
|
340
359
|
export function isOptionValue(args, arg) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fullstackgtm",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.2",
|
|
4
4
|
"description": "Open-source agentic GTM ops framework: canonical GTM data model, pluggable deterministic audits, reviewable dry-run patch plans, approval-gated write-back with conflict detection, and cross-system entity resolution. HubSpot, Salesforce, and Stripe connectors included.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Full Stack GTM LLC <ryan@fullstackgtm.com> (https://fullstackgtm.com)",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"clean": "node scripts/clean.mjs",
|
|
49
49
|
"build": "npm run clean && tsc -p tsconfig.build.json",
|
|
50
|
-
"pretest": "
|
|
50
|
+
"pretest": "node scripts/verify-public-tests.mjs",
|
|
51
51
|
"test": "node --experimental-strip-types --test tests/*.test.ts",
|
|
52
52
|
"verify:package": "npm run build && node scripts/verify-packed-install.mjs --with-peers",
|
|
53
53
|
"prepublishOnly": "npm run verify:package"
|
package/src/cli/enrich.ts
CHANGED
|
@@ -50,7 +50,7 @@ enrich append [--source apollo] [--objects companies,contacts] [--save] [--conf
|
|
|
50
50
|
enrich refresh [--source apollo] [--stale-days <n>] [--save] [--config <path>]
|
|
51
51
|
[source options] [--run-label <label>] [--json]
|
|
52
52
|
enrich ingest <file.csv|payload.json|spool.jsonl|spool-dir> --source clay [--run-label <label>] [--objects companies|contacts] [--config <path>]
|
|
53
|
-
enrich acquire [--source <id>] [--max <new-leads>] [--scan-limit <candidates>] [--list <id>] [--assign-owner <id>] [--save] [--config <path>] [--json]
|
|
53
|
+
enrich acquire [--source <id>] [--max <new-leads>] [--scan-limit <candidates>] [--company-domain <domain>] [--list <id>] [--assign-owner <id>] [--save] [--config <path>] [--json]
|
|
54
54
|
enrich status [--runs] [--source <id>] [--config <path>] [--json]
|
|
55
55
|
|
|
56
56
|
acquire creates NET-NEW leads from a staged prospect list (ingest first):
|
|
@@ -596,8 +596,13 @@ async function acquireFromApi(
|
|
|
596
596
|
: disc.provider === "clay"
|
|
597
597
|
? (icp ? icpToClayPeopleFilters(icp) : (disc.filters ?? {}))
|
|
598
598
|
: {};
|
|
599
|
+
const targetCompanyDomain = option(rest, "--company-domain")?.trim().toLowerCase().replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "");
|
|
600
|
+
if (targetCompanyDomain) {
|
|
601
|
+
if (disc.provider !== "clay") throw new Error("enrich acquire: --company-domain currently requires --source clay");
|
|
602
|
+
filters = { ...filters, company_identifier: [targetCompanyDomain] };
|
|
603
|
+
}
|
|
599
604
|
const queryFingerprint = createHash("sha256")
|
|
600
|
-
.update(JSON.stringify({ source, provider: disc.provider, listId, filters, icp: icp ?? null }))
|
|
605
|
+
.update(JSON.stringify({ source, provider: disc.provider, listId, filters, icp: icp ?? null, targetCompanyDomain: targetCompanyDomain ?? null }))
|
|
601
606
|
.digest("hex");
|
|
602
607
|
const checkpointKey: AcquireCheckpointKey = {
|
|
603
608
|
provider: disc.provider,
|
|
@@ -690,7 +695,7 @@ async function acquireFromApi(
|
|
|
690
695
|
while (true) {
|
|
691
696
|
if (!cursor) {
|
|
692
697
|
const route = clayRoutes[clayRouteIndex];
|
|
693
|
-
if (route) filters = route.filters;
|
|
698
|
+
if (route) filters = { ...route.filters, ...(targetCompanyDomain ? { company_identifier: [targetCompanyDomain] } : {}) };
|
|
694
699
|
progress.note(`creating Clay people-search iterator · ${route?.id ?? "configured"}`);
|
|
695
700
|
cursor = await createClaySearch({ apiKey: providerKey("clay"), sourceType: "people", filters });
|
|
696
701
|
}
|
|
@@ -730,6 +735,7 @@ async function acquireFromApi(
|
|
|
730
735
|
}
|
|
731
736
|
discovered += page.length;
|
|
732
737
|
progress.items(discovered, scanLimit);
|
|
738
|
+
if (targetCompanyDomain) page = page.filter((prospect) => normalizedCompanyDomain(prospect.companyDomain) === targetCompanyDomain);
|
|
733
739
|
if (page.length === 0) {
|
|
734
740
|
exhausted = true;
|
|
735
741
|
break;
|
|
@@ -1011,6 +1017,10 @@ function printAcquireOutput(options: {
|
|
|
1011
1017
|
}
|
|
1012
1018
|
}
|
|
1013
1019
|
|
|
1020
|
+
function normalizedCompanyDomain(value: string | undefined): string {
|
|
1021
|
+
return (value ?? "").trim().toLowerCase().replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "");
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1014
1024
|
function hostedRunsUrl(): string | null {
|
|
1015
1025
|
const baseUrl = getCredential("broker")?.baseUrl?.replace(/\/+$/, "");
|
|
1016
1026
|
return baseUrl ? `${baseUrl}/dashboard/runs` : null;
|
package/src/cli/help.ts
CHANGED
|
@@ -692,7 +692,7 @@ export const COMMAND_FLAGS: Record<string, string[]> = {
|
|
|
692
692
|
dedupe: [...SOURCE_FLAGS, "--key", "--keep", "--reason", "--max-operations", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
693
693
|
reassign: [...SOURCE_FLAGS, "--from", "--assign-unowned", "--to", "--objects", "--where", "--except-deal-stage", "--include-closed-deals", "--reason", "--max-operations", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
694
694
|
backfill: [...SOURCE_FLAGS, "--since", "--pipeline", "--match-property", "--skip-unmatched", "--save", "--dry-run", "--verbose", "--json"],
|
|
695
|
-
enrich: [...SOURCE_FLAGS, "--config", "--source", "--icp", "--input", "--provider", "--list", "--stale-days", "--assign-owner", "--objects", "--max", "--scan-limit", "--staged-run", "--run-label", "--label", "--runs", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
695
|
+
enrich: [...SOURCE_FLAGS, "--config", "--source", "--icp", "--input", "--provider", "--list", "--stale-days", "--assign-owner", "--objects", "--max", "--scan-limit", "--company-domain", "--staged-run", "--run-label", "--label", "--runs", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
696
696
|
call: [...SOURCE_FLAGS, "--transcript", "--title", "--source", "--model", "--deterministic", "--heuristics", "--llm", "--verbose", "--json", "--ndjson", "--out", "--call", "--call-type", "--rubric", "--list", "--list-rubrics", "--attendees", "--domain", "--deal", "--save", "--dry-run"],
|
|
697
697
|
suggest: [...SOURCE_FLAGS, "--plan-id", "--plan", "--json", "--out"],
|
|
698
698
|
plans: ["--status", "--operations", "--value", "--values-from", "--min-confidence", "--include-creates", "--acknowledge-uncertain-writes", "--verbose", "--json"],
|
|
@@ -708,7 +708,7 @@ export const COMMAND_FLAGS: Record<string, string[]> = {
|
|
|
708
708
|
};
|
|
709
709
|
|
|
710
710
|
export const FLAGS_WITH_VALUES = new Set([
|
|
711
|
-
"--account", "--account-id", "--accounts", "--acv", "--acv-basis", "--after", "--anchor", "--api-key", "--approve", "--archive", "--assign-owner", "--attendees", "--before", "--bucket", "--buyers-per-account", "--call", "--call-type", "--calls", "--capture-run", "--category", "--channel", "--client", "--client-id", "--client-secret", "--config", "--connector", "--connector-opt", "--contact", "--cron", "--cross-checks", "--deal", "--deal-period", "--diff", "--domain", "--email", "--except-deal-stage", "--format", "--from", "--from-judge", "--golden", "--guard", "--icp", "--in", "--input", "--instance-url", "--keep", "--key", "--keywords", "--label", "--list", "--login-url", "--match", "--match-property", "--max", "--max-accounts", "--max-claims", "--max-credits", "--max-examples", "--max-operations", "--max-results", "--max-searches", "--max-usd", "--min-accuracy", "--min-confidence", "--min-mentions", "--min-score", "--model", "--name", "--objects", "--operations", "--out", "--pipeline", "--plan", "--plan-id", "--policy", "--port", "--prepared-by", "--prior-run", "--profile", "--promote-lift", "--prompt", "--provider", "--reason", "--require", "--result", "--rubric", "--rule", "--rules", "--run", "--run-label", "--runs", "--scan-limit", "--scopes", "--seed", "--set", "--signals-from", "--since", "--snapshot", "--source", "--staged-run", "--stale-days", "--status", "--task-account", "--task-deal", "--timer", "--title", "--to", "--today", "--token", "--token-env", "--touch", "--transcript", "--trigger", "--usd-per-credit", "--value", "--values-from", "--vendor", "--via", "--watchlist", "--where",
|
|
711
|
+
"--account", "--account-id", "--accounts", "--acv", "--acv-basis", "--after", "--anchor", "--api-key", "--approve", "--archive", "--assign-owner", "--attendees", "--before", "--bucket", "--buyers-per-account", "--call", "--call-type", "--calls", "--capture-run", "--category", "--channel", "--client", "--client-id", "--client-secret", "--company-domain", "--config", "--connector", "--connector-opt", "--contact", "--cron", "--cross-checks", "--deal", "--deal-period", "--diff", "--domain", "--email", "--except-deal-stage", "--format", "--from", "--from-judge", "--golden", "--guard", "--icp", "--in", "--input", "--instance-url", "--keep", "--key", "--keywords", "--label", "--list", "--login-url", "--match", "--match-property", "--max", "--max-accounts", "--max-claims", "--max-credits", "--max-examples", "--max-operations", "--max-results", "--max-searches", "--max-usd", "--min-accuracy", "--min-confidence", "--min-mentions", "--min-score", "--model", "--name", "--objects", "--operations", "--out", "--pipeline", "--plan", "--plan-id", "--policy", "--port", "--prepared-by", "--prior-run", "--profile", "--promote-lift", "--prompt", "--provider", "--reason", "--require", "--result", "--rubric", "--rule", "--rules", "--run", "--run-label", "--runs", "--scan-limit", "--scopes", "--seed", "--set", "--signals-from", "--since", "--snapshot", "--source", "--staged-run", "--stale-days", "--status", "--task-account", "--task-deal", "--timer", "--title", "--to", "--today", "--token", "--token-env", "--touch", "--transcript", "--trigger", "--usd-per-credit", "--value", "--values-from", "--vendor", "--via", "--watchlist", "--where",
|
|
712
712
|
]);
|
|
713
713
|
|
|
714
714
|
// Lifecycle-grouped front door. One line per verb, organized by the
|
package/src/cli/shared.ts
CHANGED
|
@@ -336,6 +336,35 @@ export function loadIcp(args: string[]): Icp | undefined {
|
|
|
336
336
|
* e.g. `echo "$TOKEN" | fullstackgtm login hubspot`
|
|
337
337
|
* - interactive: prompted on the TTY with the input muted
|
|
338
338
|
*/
|
|
339
|
+
export function createSecretMaskRenderer(options: {
|
|
340
|
+
label: string;
|
|
341
|
+
lineLength: () => number;
|
|
342
|
+
write: (value: string) => void;
|
|
343
|
+
}) {
|
|
344
|
+
let muted = false;
|
|
345
|
+
let renderQueued = false;
|
|
346
|
+
let settled = false;
|
|
347
|
+
return {
|
|
348
|
+
mute() { muted = true; },
|
|
349
|
+
settle() { settled = true; },
|
|
350
|
+
write(value: string) {
|
|
351
|
+
if (!muted) { options.write(value); return; }
|
|
352
|
+
if (settled || renderQueued) return;
|
|
353
|
+
renderQueued = true;
|
|
354
|
+
queueMicrotask(() => {
|
|
355
|
+
renderQueued = false;
|
|
356
|
+
// readline clears `line` during close. Submission settles this renderer
|
|
357
|
+
// first so an Enter-triggered repaint cannot redraw an empty prompt
|
|
358
|
+
// after the final mask/newline ("0 characters received").
|
|
359
|
+
if (settled) return;
|
|
360
|
+
const length = options.lineLength();
|
|
361
|
+
const mask = `${"•".repeat(Math.min(length, 12))}${length > 12 ? "…" : ""}`;
|
|
362
|
+
options.write(`\r\u001b[2K${options.label}: ${mask} (${length} character${length === 1 ? "" : "s"} received)`);
|
|
363
|
+
});
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
339
368
|
export async function readSecret(label: string): Promise<string> {
|
|
340
369
|
if (!process.stdin.isTTY) {
|
|
341
370
|
const chunks: Buffer[] = [];
|
|
@@ -362,26 +391,20 @@ export async function readSecret(label: string): Promise<string> {
|
|
|
362
391
|
output: process.stderr,
|
|
363
392
|
terminal: true,
|
|
364
393
|
});
|
|
365
|
-
let muted = false;
|
|
366
394
|
const mutable = rl as unknown as { _writeToOutput?: (value: string) => void; line?: string };
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
renderQueued = false;
|
|
374
|
-
const length = mutable.line?.length ?? 0;
|
|
375
|
-
const mask = `${"•".repeat(Math.min(length, 12))}${length > 12 ? "…" : ""}`;
|
|
376
|
-
process.stderr.write(`\r\u001b[2K${label}: ${mask} (${length} character${length === 1 ? "" : "s"} received)`);
|
|
377
|
-
});
|
|
378
|
-
};
|
|
395
|
+
const renderer = createSecretMaskRenderer({
|
|
396
|
+
label,
|
|
397
|
+
lineLength: () => mutable.line?.length ?? 0,
|
|
398
|
+
write: (value) => process.stderr.write(value),
|
|
399
|
+
});
|
|
400
|
+
mutable._writeToOutput = (value: string) => renderer.write(value);
|
|
379
401
|
rl.question(`${label}: `, (answer) => {
|
|
402
|
+
renderer.settle();
|
|
380
403
|
rl.close();
|
|
381
404
|
process.stderr.write("\n");
|
|
382
405
|
resolveSecret(answer.trim());
|
|
383
406
|
});
|
|
384
|
-
|
|
407
|
+
renderer.mute();
|
|
385
408
|
});
|
|
386
409
|
}
|
|
387
410
|
|