@tryarcanist/cli 0.1.25 → 0.1.26
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/dist/index.js +107 -34
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -198,6 +198,7 @@ function validateApiUrl(url) {
|
|
|
198
198
|
// src/runtime.ts
|
|
199
199
|
import { randomUUID } from "crypto";
|
|
200
200
|
import { createInterface } from "readline/promises";
|
|
201
|
+
var ANSI_CONTROL_SEQUENCE = /\u001b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
201
202
|
function getRuntimeOptions(command, options = {}) {
|
|
202
203
|
const globals = command?.optsWithGlobals?.();
|
|
203
204
|
const merged = { ...globals, ...options };
|
|
@@ -245,6 +246,70 @@ async function confirmOrThrow(message) {
|
|
|
245
246
|
rl.close();
|
|
246
247
|
}
|
|
247
248
|
}
|
|
249
|
+
async function readHiddenPrompt(prompt) {
|
|
250
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
251
|
+
throw new CliError("user", "No interactive terminal available. Re-run with --token-stdin or set ARCANIST_TOKEN.");
|
|
252
|
+
}
|
|
253
|
+
return new Promise((resolve, reject) => {
|
|
254
|
+
const stdin = process.stdin;
|
|
255
|
+
const inputChars = [];
|
|
256
|
+
let ansiCarry = "";
|
|
257
|
+
let settled = false;
|
|
258
|
+
const cleanup = () => {
|
|
259
|
+
stdin.removeListener("data", onData);
|
|
260
|
+
stdin.removeListener("error", onError);
|
|
261
|
+
if (stdin.isTTY) stdin.setRawMode(false);
|
|
262
|
+
stdin.pause();
|
|
263
|
+
};
|
|
264
|
+
const finish = () => {
|
|
265
|
+
if (settled) return;
|
|
266
|
+
settled = true;
|
|
267
|
+
cleanup();
|
|
268
|
+
process.stdout.write("\n");
|
|
269
|
+
resolve(inputChars.join(""));
|
|
270
|
+
};
|
|
271
|
+
const fail = (error) => {
|
|
272
|
+
if (settled) return;
|
|
273
|
+
settled = true;
|
|
274
|
+
cleanup();
|
|
275
|
+
process.stdout.write("\n");
|
|
276
|
+
reject(error);
|
|
277
|
+
};
|
|
278
|
+
const onData = (chunk) => {
|
|
279
|
+
const normalized = normalizePromptChunk(chunk, ansiCarry);
|
|
280
|
+
ansiCarry = normalized.carry;
|
|
281
|
+
const text = normalized.text;
|
|
282
|
+
for (const char of text) {
|
|
283
|
+
if (char === "\n" || char === "\r") {
|
|
284
|
+
finish();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (char === "") {
|
|
288
|
+
fail(new CliError("user", "Interrupted.", { exitCode: 130 }));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (char === "\x7F" || char === "\b") {
|
|
292
|
+
if (inputChars.length > 0) {
|
|
293
|
+
inputChars.pop();
|
|
294
|
+
process.stdout.write("\b \b");
|
|
295
|
+
}
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (isControlCharacter(char)) continue;
|
|
299
|
+
inputChars.push(char);
|
|
300
|
+
process.stdout.write("*");
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
const onError = (error) => {
|
|
304
|
+
fail(new CliError("user", `Failed to read input: ${error.message}`));
|
|
305
|
+
};
|
|
306
|
+
process.stdout.write(prompt);
|
|
307
|
+
stdin.resume();
|
|
308
|
+
if (stdin.isTTY) stdin.setRawMode(true);
|
|
309
|
+
stdin.on("data", onData);
|
|
310
|
+
stdin.on("error", onError);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
248
313
|
function printDeprecatedAlias(alias, replacement, command, options = {}) {
|
|
249
314
|
if (isQuiet(command, options)) return;
|
|
250
315
|
process.stderr.write(`Warning: \`arcanist ${alias}\` is deprecated; use \`arcanist ${replacement}\`.
|
|
@@ -258,6 +323,47 @@ function applyColorEnvironment(options) {
|
|
|
258
323
|
function randomIdempotencyKey() {
|
|
259
324
|
return randomUUID();
|
|
260
325
|
}
|
|
326
|
+
function normalizePromptChunk(chunk, carry = "") {
|
|
327
|
+
const raw = carry + (Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk);
|
|
328
|
+
let text = "";
|
|
329
|
+
let index = 0;
|
|
330
|
+
while (index < raw.length) {
|
|
331
|
+
const char = raw[index];
|
|
332
|
+
if (char !== "\x1B") {
|
|
333
|
+
text += char;
|
|
334
|
+
index += 1;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
const sequence = matchAnsiControlSequence(raw, index);
|
|
338
|
+
if (sequence.kind === "incomplete") {
|
|
339
|
+
return { text, carry: raw.slice(index) };
|
|
340
|
+
}
|
|
341
|
+
if (sequence.kind === "complete") {
|
|
342
|
+
index = sequence.nextIndex;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
index += 1;
|
|
346
|
+
}
|
|
347
|
+
return { text, carry: "" };
|
|
348
|
+
}
|
|
349
|
+
function matchAnsiControlSequence(text, startIndex) {
|
|
350
|
+
if (startIndex + 1 >= text.length) {
|
|
351
|
+
return { kind: "incomplete", nextIndex: startIndex };
|
|
352
|
+
}
|
|
353
|
+
if (text[startIndex + 1] !== "[") {
|
|
354
|
+
return { kind: "none", nextIndex: startIndex + 1 };
|
|
355
|
+
}
|
|
356
|
+
ANSI_CONTROL_SEQUENCE.lastIndex = startIndex;
|
|
357
|
+
const match = ANSI_CONTROL_SEQUENCE.exec(text);
|
|
358
|
+
if (match && match.index === startIndex) {
|
|
359
|
+
return { kind: "complete", nextIndex: startIndex + match[0].length };
|
|
360
|
+
}
|
|
361
|
+
return { kind: "incomplete", nextIndex: startIndex };
|
|
362
|
+
}
|
|
363
|
+
function isControlCharacter(char) {
|
|
364
|
+
const code = char.charCodeAt(0);
|
|
365
|
+
return code < 32 || code === 127;
|
|
366
|
+
}
|
|
261
367
|
|
|
262
368
|
// src/commands/auth.ts
|
|
263
369
|
async function whoamiCommand(options, command) {
|
|
@@ -1263,7 +1369,6 @@ async function createCommand(repoUrl, promptArg, options, command) {
|
|
|
1263
1369
|
}
|
|
1264
1370
|
|
|
1265
1371
|
// src/commands/login.ts
|
|
1266
|
-
import { createInterface as createInterface2 } from "readline";
|
|
1267
1372
|
async function loginCommand(options, command) {
|
|
1268
1373
|
const runtime = getRuntimeOptions(command, options);
|
|
1269
1374
|
let token;
|
|
@@ -1272,7 +1377,7 @@ async function loginCommand(options, command) {
|
|
|
1272
1377
|
} else if (runtime.token) {
|
|
1273
1378
|
token = runtime.token;
|
|
1274
1379
|
} else {
|
|
1275
|
-
token = await
|
|
1380
|
+
token = await readHiddenPrompt("Enter your CLI token: ");
|
|
1276
1381
|
}
|
|
1277
1382
|
if (!token) {
|
|
1278
1383
|
throw new CliError("user", "No token provided.");
|
|
@@ -1306,38 +1411,6 @@ async function loginCommand(options, command) {
|
|
|
1306
1411
|
if (!runtime.json && !runtime.quiet) console.warn("Warning: Could not reach API to verify token.");
|
|
1307
1412
|
}
|
|
1308
1413
|
}
|
|
1309
|
-
function promptHidden(prompt) {
|
|
1310
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1311
|
-
return Promise.reject(new CliError("user", "No interactive terminal available. Re-run with --token-stdin or set ARCANIST_TOKEN."));
|
|
1312
|
-
}
|
|
1313
|
-
return new Promise((resolve, reject) => {
|
|
1314
|
-
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
1315
|
-
process.stdout.write(prompt);
|
|
1316
|
-
const stdin = process.stdin;
|
|
1317
|
-
if (stdin.isTTY) stdin.setRawMode(true);
|
|
1318
|
-
let input = "";
|
|
1319
|
-
const onData = (char) => {
|
|
1320
|
-
const c = char.toString();
|
|
1321
|
-
if (c === "\n" || c === "\r") {
|
|
1322
|
-
if (stdin.isTTY) stdin.setRawMode(false);
|
|
1323
|
-
stdin.removeListener("data", onData);
|
|
1324
|
-
process.stdout.write("\n");
|
|
1325
|
-
rl.close();
|
|
1326
|
-
resolve(input);
|
|
1327
|
-
} else if (c === "") {
|
|
1328
|
-
if (stdin.isTTY) stdin.setRawMode(false);
|
|
1329
|
-
stdin.removeListener("data", onData);
|
|
1330
|
-
rl.close();
|
|
1331
|
-
reject(new CliError("user", "Interrupted.", { exitCode: 130 }));
|
|
1332
|
-
} else if (c === "\x7F" || c === "\b") {
|
|
1333
|
-
input = input.slice(0, -1);
|
|
1334
|
-
} else {
|
|
1335
|
-
input += c;
|
|
1336
|
-
}
|
|
1337
|
-
};
|
|
1338
|
-
stdin.on("data", onData);
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
1414
|
|
|
1342
1415
|
// src/commands/message.ts
|
|
1343
1416
|
async function messageCommand(sessionId, promptArg, options = {}, command) {
|