@longtable/cli 0.1.30 → 0.1.32
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/README.md +16 -13
- package/dist/cli.js +464 -493
- package/dist/project-session.d.ts +104 -2
- package/dist/project-session.js +293 -11
- package/dist/prompt-aliases.js +5 -5
- package/dist/prompt-renderer.d.ts +11 -0
- package/dist/prompt-renderer.js +130 -0
- package/dist/search/publisher-access.js +1 -1
- package/dist/search/sources.js +2 -2
- package/package.json +8 -7
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { createInterface } from "node:readline/promises";
|
|
3
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
4
|
+
import { cancel, isCancel, multiselect, note, select, text } from "@clack/prompts";
|
|
5
|
+
function isInteractiveTerminal() {
|
|
6
|
+
return Boolean(input.isTTY && output.isTTY);
|
|
7
|
+
}
|
|
8
|
+
function guardCancel(value) {
|
|
9
|
+
if (isCancel(value)) {
|
|
10
|
+
cancel("LongTable cancelled.");
|
|
11
|
+
throw new Error("LongTable cancelled.");
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
function renderChoices(choices) {
|
|
16
|
+
return choices
|
|
17
|
+
.map((choice, index) => `${index + 1}. ${choice.label} - ${choice.description}`)
|
|
18
|
+
.join("\n");
|
|
19
|
+
}
|
|
20
|
+
let fallbackReadline;
|
|
21
|
+
let fallbackInputLines;
|
|
22
|
+
function getFallbackReadline() {
|
|
23
|
+
fallbackReadline ??= createInterface({ input, output });
|
|
24
|
+
return fallbackReadline;
|
|
25
|
+
}
|
|
26
|
+
async function askLine(prompt) {
|
|
27
|
+
if (!input.isTTY) {
|
|
28
|
+
output.write(prompt);
|
|
29
|
+
fallbackInputLines ??= readFileSync(0, "utf8").split(/\r?\n/);
|
|
30
|
+
return fallbackInputLines.shift() ?? "";
|
|
31
|
+
}
|
|
32
|
+
return getFallbackReadline().question(prompt);
|
|
33
|
+
}
|
|
34
|
+
async function promptChoiceByNumber(prompt, choices) {
|
|
35
|
+
while (true) {
|
|
36
|
+
const answer = await askLine(`${prompt}\n${renderChoices(choices)}\nSelect one number: `);
|
|
37
|
+
const numeric = Number(answer.trim());
|
|
38
|
+
if (!Number.isInteger(numeric) || numeric < 1 || numeric > choices.length) {
|
|
39
|
+
console.log("Invalid selection. Enter one of the listed numbers.");
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const choice = choices[numeric - 1];
|
|
43
|
+
if (choice.fallbackToText) {
|
|
44
|
+
const freeText = await askLine("Type your custom value: ");
|
|
45
|
+
if (!freeText.trim()) {
|
|
46
|
+
console.log("Custom value cannot be empty.");
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
return freeText.trim();
|
|
50
|
+
}
|
|
51
|
+
return choice.id;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function promptTextLine(prompt, required) {
|
|
55
|
+
while (true) {
|
|
56
|
+
const answer = (await askLine(`${prompt}\n> `)).trim();
|
|
57
|
+
if (!required) {
|
|
58
|
+
return answer || undefined;
|
|
59
|
+
}
|
|
60
|
+
if (answer) {
|
|
61
|
+
return answer;
|
|
62
|
+
}
|
|
63
|
+
console.log("This answer cannot be empty.");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function createPromptRenderer() {
|
|
67
|
+
return {
|
|
68
|
+
async text(prompt, options) {
|
|
69
|
+
const required = options?.required ?? false;
|
|
70
|
+
if (!isInteractiveTerminal()) {
|
|
71
|
+
return promptTextLine(prompt, required);
|
|
72
|
+
}
|
|
73
|
+
return guardCancel(await text({
|
|
74
|
+
message: prompt,
|
|
75
|
+
placeholder: options?.placeholder,
|
|
76
|
+
validate: required
|
|
77
|
+
? (value) => value?.trim() ? undefined : "This answer cannot be empty."
|
|
78
|
+
: undefined
|
|
79
|
+
}))?.trim() || undefined;
|
|
80
|
+
},
|
|
81
|
+
async select(prompt, choices) {
|
|
82
|
+
if (!isInteractiveTerminal()) {
|
|
83
|
+
return promptChoiceByNumber(prompt, choices);
|
|
84
|
+
}
|
|
85
|
+
const selected = guardCancel(await select({
|
|
86
|
+
message: prompt,
|
|
87
|
+
options: choices.map((choice) => ({
|
|
88
|
+
value: choice.id,
|
|
89
|
+
label: choice.label,
|
|
90
|
+
hint: choice.description
|
|
91
|
+
})),
|
|
92
|
+
maxItems: 7
|
|
93
|
+
}));
|
|
94
|
+
const choice = choices.find((candidate) => candidate.id === selected);
|
|
95
|
+
if (choice?.fallbackToText) {
|
|
96
|
+
return guardCancel(await text({
|
|
97
|
+
message: "Type your custom value",
|
|
98
|
+
validate: (value) => value?.trim() ? undefined : "This answer cannot be empty."
|
|
99
|
+
})).trim();
|
|
100
|
+
}
|
|
101
|
+
return selected;
|
|
102
|
+
},
|
|
103
|
+
async multiselect(prompt, choices) {
|
|
104
|
+
if (!isInteractiveTerminal()) {
|
|
105
|
+
const answer = await askLine(`${prompt}\nType comma-separated ids or leave blank for auto.\n> `);
|
|
106
|
+
return answer
|
|
107
|
+
.split(",")
|
|
108
|
+
.map((part) => part.trim())
|
|
109
|
+
.filter(Boolean);
|
|
110
|
+
}
|
|
111
|
+
return guardCancel(await multiselect({
|
|
112
|
+
message: prompt,
|
|
113
|
+
options: choices.map((choice) => ({
|
|
114
|
+
value: choice.id,
|
|
115
|
+
label: choice.label,
|
|
116
|
+
hint: choice.description
|
|
117
|
+
})),
|
|
118
|
+
required: false,
|
|
119
|
+
maxItems: 7
|
|
120
|
+
}));
|
|
121
|
+
},
|
|
122
|
+
note(message, title) {
|
|
123
|
+
if (isInteractiveTerminal()) {
|
|
124
|
+
note(message, title);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
console.log(title ? `${title}\n${message}` : message);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -151,7 +151,7 @@ export async function discoverCrossrefTdm(doi, env = process.env, httpFetch = de
|
|
|
151
151
|
const response = await httpFetch(url, {
|
|
152
152
|
headers: {
|
|
153
153
|
accept: "application/json",
|
|
154
|
-
"user-agent": "LongTable/0.1.
|
|
154
|
+
"user-agent": "LongTable/0.1.31 (https://github.com/HosungYou/LongTable)"
|
|
155
155
|
}
|
|
156
156
|
});
|
|
157
157
|
if (!response.ok) {
|
package/dist/search/sources.js
CHANGED
|
@@ -155,7 +155,7 @@ async function fetchJson(context, url) {
|
|
|
155
155
|
const response = await context.fetch(url, {
|
|
156
156
|
headers: {
|
|
157
157
|
"accept": "application/json",
|
|
158
|
-
"user-agent": "LongTable/0.1.
|
|
158
|
+
"user-agent": "LongTable/0.1.31 (https://github.com/HosungYou/LongTable)"
|
|
159
159
|
}
|
|
160
160
|
});
|
|
161
161
|
if (!response.ok) {
|
|
@@ -167,7 +167,7 @@ async function fetchText(context, url) {
|
|
|
167
167
|
const response = await context.fetch(url, {
|
|
168
168
|
headers: {
|
|
169
169
|
"accept": "application/xml, text/xml, application/atom+xml, text/plain",
|
|
170
|
-
"user-agent": "LongTable/0.1.
|
|
170
|
+
"user-agent": "LongTable/0.1.31 (https://github.com/HosungYou/LongTable)"
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
173
|
if (!response.ok) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longtable/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Researcher-facing LongTable CLI",
|
|
6
6
|
"type": "module",
|
|
@@ -28,12 +28,13 @@
|
|
|
28
28
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@
|
|
32
|
-
"@longtable/
|
|
33
|
-
"@longtable/
|
|
34
|
-
"@longtable/
|
|
35
|
-
"@longtable/provider-
|
|
36
|
-
"@longtable/
|
|
31
|
+
"@clack/prompts": "^1.2.0",
|
|
32
|
+
"@longtable/checkpoints": "0.1.32",
|
|
33
|
+
"@longtable/core": "0.1.32",
|
|
34
|
+
"@longtable/memory": "0.1.32",
|
|
35
|
+
"@longtable/provider-claude": "0.1.32",
|
|
36
|
+
"@longtable/provider-codex": "0.1.32",
|
|
37
|
+
"@longtable/setup": "0.1.32"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"@types/node": "^22.10.1",
|