fullstackgtm 0.55.0 → 0.55.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/CHANGELOG.md +8 -0
- package/dist/cli/shared.d.ts +9 -0
- package/dist/cli/shared.js +37 -18
- package/package.json +1 -1
- package/src/cli/shared.ts +37 -14
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ 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.1] — 2026-07-13
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Interactive masked login prompts no longer repaint an empty
|
|
15
|
+
`0 characters received` line after Enter is pressed. Submission now settles
|
|
16
|
+
the renderer before `readline` clears its internal input buffer.
|
|
17
|
+
|
|
10
18
|
## [0.55.0] — 2026-07-13
|
|
11
19
|
|
|
12
20
|
### Added
|
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.1",
|
|
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)",
|
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
|
|