@curviate/cli 0.15.1 → 0.15.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
CHANGED
|
@@ -6,6 +6,26 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
6
6
|
Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html):
|
|
7
7
|
a new command or flag is a minor; a breaking command/flag/exit-code change is a major; a fix is a patch.
|
|
8
8
|
|
|
9
|
+
## [0.15.2] - 2026-07-12
|
|
10
|
+
|
|
11
|
+
A patch release fixing an interactive-terminal hang on `account link`.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **`account link --password-stdin` / `--li-at-stdin` no longer hang on an
|
|
16
|
+
interactive terminal.** These flags previously read stdin to EOF, which a
|
|
17
|
+
human paste + Enter never produces on a TTY — the command hung
|
|
18
|
+
indefinitely and the pasted secret echoed on-screen. The read is now
|
|
19
|
+
mode-aware: piped/redirected stdin (non-TTY) is unchanged (read to EOF,
|
|
20
|
+
trimmed); an interactive TTY now prints a single cue line, then reads one
|
|
21
|
+
no-echo line — paste + Enter resolves immediately, including a paste whose
|
|
22
|
+
clipboard content ends in a trailing newline. An empty line still falls
|
|
23
|
+
through to the normal resolution order (env var, then the password
|
|
24
|
+
prompt / `li_at` fail-fast).
|
|
25
|
+
- **`--preview` never blocks on a terminal read.** Under `--preview`, the
|
|
26
|
+
interactive stdin read is suppressed entirely, matching every other
|
|
27
|
+
preview-mode command.
|
|
28
|
+
|
|
9
29
|
## [0.15.1] - 2026-07-11
|
|
10
30
|
|
|
11
31
|
A patch release of agent-experience (AX) and developer-experience (DX)
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-U7Y4EXHT.js";
|
|
5
5
|
import {
|
|
6
6
|
readlineSync
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-H4KV7MIN.js";
|
|
8
8
|
import {
|
|
9
9
|
AUTH_NEEDED
|
|
10
10
|
} from "./chunk-ZE7QGR3F.js";
|
|
@@ -37,16 +37,33 @@ import {
|
|
|
37
37
|
import { defineCommand } from "citty";
|
|
38
38
|
|
|
39
39
|
// src/lib/credential-resolve.ts
|
|
40
|
+
var STDIN_TTY_CUE = "Reading secret from stdin (paste + Enter): ";
|
|
41
|
+
function defaultReadSingleLine() {
|
|
42
|
+
return readlineSync("", { mask: true });
|
|
43
|
+
}
|
|
40
44
|
async function resolveSecret(params) {
|
|
41
45
|
if (params.flagValue !== void 0 && params.flagValue !== "") {
|
|
42
46
|
return params.flagValue;
|
|
43
47
|
}
|
|
44
48
|
if (params.stdinRequested) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
if (params.isTTY) {
|
|
50
|
+
const stdinReadAllowed = params.allowInteractiveStdinRead ?? params.allowInteractive;
|
|
51
|
+
if (stdinReadAllowed !== false) {
|
|
52
|
+
params.out.stderr.write(STDIN_TTY_CUE);
|
|
53
|
+
const reader = params.readSingleLine ?? defaultReadSingleLine;
|
|
54
|
+
const raw = await reader(STDIN_TTY_CUE);
|
|
55
|
+
const trimmed = raw.trim();
|
|
56
|
+
if (trimmed !== "") {
|
|
57
|
+
return trimmed;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
const reader = params.readStdin ?? defaultReadStdin;
|
|
62
|
+
const raw = await reader();
|
|
63
|
+
const trimmed = raw.trim();
|
|
64
|
+
if (trimmed !== "") {
|
|
65
|
+
return trimmed;
|
|
66
|
+
}
|
|
50
67
|
}
|
|
51
68
|
}
|
|
52
69
|
const envValue = process.env[params.envVar];
|
|
@@ -262,12 +279,20 @@ async function buildAuthBody(flags, ctx) {
|
|
|
262
279
|
flagValue: flags.password,
|
|
263
280
|
stdinRequested: flags["password-stdin"],
|
|
264
281
|
envVar: "CURVIATE_LINKEDIN_PASSWORD",
|
|
282
|
+
isTTY: ctx.isTTY,
|
|
265
283
|
readStdin: ctx.readStdin,
|
|
284
|
+
readSingleLine: ctx.readSingleLine,
|
|
266
285
|
required: true,
|
|
267
|
-
// The
|
|
268
|
-
// (nothing meaningful to prompt toward yet otherwise) — and
|
|
269
|
-
// under --preview (a client-side render must not prompt or
|
|
286
|
+
// The masked-prompt/fail-fast tiers (3/4) only engage once --email is
|
|
287
|
+
// present (nothing meaningful to prompt toward yet otherwise) — and
|
|
288
|
+
// never under --preview (a client-side render must not prompt or
|
|
289
|
+
// exit).
|
|
270
290
|
allowInteractive: !ctx.previewMode && Boolean(flags.email),
|
|
291
|
+
// Tier-1b's own gate is preview-only, deliberately NOT email-gated: a
|
|
292
|
+
// user who passed --password-stdin on a TTY gets the read regardless
|
|
293
|
+
// of --email — an email-less body still fails downstream validation,
|
|
294
|
+
// as expected, rather than the flag being silently ignored.
|
|
295
|
+
allowInteractiveStdinRead: !ctx.previewMode,
|
|
271
296
|
failMessage: "no password \u2014 pass --password, --password-stdin, or set CURVIATE_LINKEDIN_PASSWORD",
|
|
272
297
|
prompt: { isTTY: ctx.isTTY, readline: ctx.readline, promptText: "LinkedIn password: " },
|
|
273
298
|
out: ctx.out
|
|
@@ -284,7 +309,9 @@ async function buildAuthBody(flags, ctx) {
|
|
|
284
309
|
flagValue: flags["li-at"],
|
|
285
310
|
stdinRequested: flags["li-at-stdin"],
|
|
286
311
|
envVar: "CURVIATE_LINKEDIN_LI_AT",
|
|
312
|
+
isTTY: ctx.isTTY,
|
|
287
313
|
readStdin: ctx.readStdin,
|
|
314
|
+
readSingleLine: ctx.readSingleLine,
|
|
288
315
|
required: true,
|
|
289
316
|
allowInteractive: !ctx.previewMode,
|
|
290
317
|
failMessage: "no li_at \u2014 pass --li-at, --li-at-stdin, or set CURVIATE_LINKEDIN_LI_AT",
|
|
@@ -330,6 +357,13 @@ function resolveCredentialIO(io) {
|
|
|
330
357
|
isOutputTTY: io.isOutputTTY ?? (process.stdout.isTTY ?? false),
|
|
331
358
|
readline: io.readline ?? readlineSync,
|
|
332
359
|
readStdin: io.readStdin ?? defaultReadStdin,
|
|
360
|
+
// Always the masked, no-echo raw-mode branch — never a bare readlineSync
|
|
361
|
+
// pass-through, which would default mask to false and echo the secret.
|
|
362
|
+
// Prompt is deliberately EMPTY, not `cue`: credential-resolve.ts already
|
|
363
|
+
// wrote STDIN_TTY_CUE to out.stderr before calling this reader, and
|
|
364
|
+
// readlineSync itself writes its `prompt` argument to stderr too — the
|
|
365
|
+
// cue text would otherwise print twice on a real terminal.
|
|
366
|
+
readSingleLine: io.readSingleLine ?? (() => readlineSync("", { mask: true })),
|
|
333
367
|
sleep: io.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms))),
|
|
334
368
|
now: io.now ?? (() => Date.now()),
|
|
335
369
|
open: io.open ?? defaultOpen
|
|
@@ -464,6 +498,7 @@ async function runAccountLink(client, flags, out, io = {}) {
|
|
|
464
498
|
isTTY: resolvedIo.isTTY,
|
|
465
499
|
readline: resolvedIo.readline,
|
|
466
500
|
readStdin: resolvedIo.readStdin,
|
|
501
|
+
readSingleLine: resolvedIo.readSingleLine,
|
|
467
502
|
previewMode: flags.preview ?? false
|
|
468
503
|
});
|
|
469
504
|
const body = {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/readline.ts
|
|
4
|
+
import { createInterface } from "readline";
|
|
5
|
+
async function readlineSync(prompt, opts = {}) {
|
|
6
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
process.stderr.write(prompt);
|
|
9
|
+
if (opts.mask && typeof stdin.setRawMode === "function") {
|
|
10
|
+
stdin.setRawMode(true);
|
|
11
|
+
stdin.resume();
|
|
12
|
+
stdin.setEncoding("utf8");
|
|
13
|
+
let input = "";
|
|
14
|
+
const onData = (chunk) => {
|
|
15
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
16
|
+
const char = chunk[i];
|
|
17
|
+
if (char === "\r" || char === "\n") {
|
|
18
|
+
stdin.setRawMode?.(false);
|
|
19
|
+
stdin.pause();
|
|
20
|
+
stdin.removeListener("data", onData);
|
|
21
|
+
process.stderr.write("\n");
|
|
22
|
+
resolve(input);
|
|
23
|
+
return;
|
|
24
|
+
} else if (char === "") {
|
|
25
|
+
stdin.setRawMode?.(false);
|
|
26
|
+
stdin.pause();
|
|
27
|
+
stdin.removeListener("data", onData);
|
|
28
|
+
reject(new Error("Interrupted."));
|
|
29
|
+
return;
|
|
30
|
+
} else if (char === "\x7F" || char === "\b") {
|
|
31
|
+
input = input.slice(0, -1);
|
|
32
|
+
} else {
|
|
33
|
+
input += char;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
stdin.on("data", onData);
|
|
38
|
+
} else {
|
|
39
|
+
const rl = createInterface({
|
|
40
|
+
input: stdin,
|
|
41
|
+
output: void 0,
|
|
42
|
+
// suppress default echo (prompt already on stderr)
|
|
43
|
+
terminal: false
|
|
44
|
+
});
|
|
45
|
+
rl.once("line", (line) => {
|
|
46
|
+
rl.close();
|
|
47
|
+
resolve(line.trim());
|
|
48
|
+
});
|
|
49
|
+
rl.once("error", reject);
|
|
50
|
+
rl.once("close", () => resolve(""));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
readlineSync
|
|
57
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -250,7 +250,7 @@ var main = defineCommand({
|
|
|
250
250
|
// Subcommand registry — names and descriptions are static for help rendering;
|
|
251
251
|
// the handler implementation is loaded lazily on first invocation.
|
|
252
252
|
subCommands: {
|
|
253
|
-
login: () => import("./login-
|
|
253
|
+
login: () => import("./login-GDLWR542.js").then((m) => m.loginCommand),
|
|
254
254
|
config: () => import("./config-P5CPF5WC.js").then((m) => m.configCommand),
|
|
255
255
|
// ---------------------------------------------------------------------------
|
|
256
256
|
// Noun groups — lazy-loaded on first invocation.
|
|
@@ -264,7 +264,7 @@ var main = defineCommand({
|
|
|
264
264
|
message: () => import("./message-2DLIQIE6.js").then((m) => m.messageCommand),
|
|
265
265
|
post: () => import("./post-GRIJ7X52.js").then((m) => m.postCommand),
|
|
266
266
|
comment: () => import("./comment-NCDXERSI.js").then((m) => m.commentCommand),
|
|
267
|
-
account: () => import("./account-
|
|
267
|
+
account: () => import("./account-PD3QLOTG.js").then((m) => m.accountCommand),
|
|
268
268
|
webhook: () => import("./webhook-CEP7SGP5.js").then((m) => m.webhookCommand),
|
|
269
269
|
"sales-nav": () => import("./sales-nav-NUMEYOEO.js").then((m) => m.salesNavCommand),
|
|
270
270
|
recruiter: () => import("./recruiter-CTYH7AYG.js").then((m) => m.recruiterCommand)
|
package/package.json
CHANGED
package/dist/chunk-MKXA2LAR.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/lib/readline.ts
|
|
4
|
-
import { createInterface } from "readline";
|
|
5
|
-
async function readlineSync(prompt, opts = {}) {
|
|
6
|
-
return new Promise((resolve, reject) => {
|
|
7
|
-
process.stderr.write(prompt);
|
|
8
|
-
if (opts.mask && typeof process.stdin.setRawMode === "function") {
|
|
9
|
-
process.stdin.setRawMode(true);
|
|
10
|
-
process.stdin.resume();
|
|
11
|
-
process.stdin.setEncoding("utf8");
|
|
12
|
-
let input = "";
|
|
13
|
-
const onData = (char) => {
|
|
14
|
-
if (char === "\r" || char === "\n") {
|
|
15
|
-
process.stdin.setRawMode(false);
|
|
16
|
-
process.stdin.pause();
|
|
17
|
-
process.stdin.removeListener("data", onData);
|
|
18
|
-
process.stderr.write("\n");
|
|
19
|
-
resolve(input);
|
|
20
|
-
} else if (char === "") {
|
|
21
|
-
process.stdin.setRawMode(false);
|
|
22
|
-
process.stdin.pause();
|
|
23
|
-
process.stdin.removeListener("data", onData);
|
|
24
|
-
reject(new Error("Interrupted."));
|
|
25
|
-
} else if (char === "\x7F" || char === "\b") {
|
|
26
|
-
input = input.slice(0, -1);
|
|
27
|
-
} else {
|
|
28
|
-
input += char;
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
process.stdin.on("data", onData);
|
|
32
|
-
} else {
|
|
33
|
-
const rl = createInterface({
|
|
34
|
-
input: process.stdin,
|
|
35
|
-
output: void 0,
|
|
36
|
-
// suppress default echo (prompt already on stderr)
|
|
37
|
-
terminal: false
|
|
38
|
-
});
|
|
39
|
-
rl.once("line", (line) => {
|
|
40
|
-
rl.close();
|
|
41
|
-
resolve(line.trim());
|
|
42
|
-
});
|
|
43
|
-
rl.once("error", reject);
|
|
44
|
-
rl.once("close", () => resolve(""));
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export {
|
|
50
|
-
readlineSync
|
|
51
|
-
};
|