@chalksurf/cli 0.2.3 → 0.3.0
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 +15 -0
- package/dist/bin/chalksurf.js +8740 -164
- package/docs/agents.md +159 -3
- package/docs/exit-codes.md +2 -0
- package/docs/manual.md +50 -0
- package/docs/mcp.md +106 -0
- package/package.json +6 -4
- package/dist/commands/auth.js +0 -174
- package/dist/commands/exercise.js +0 -600
- package/dist/commands/job.js +0 -172
- package/dist/commands/org.js +0 -115
- package/dist/commands/profile.js +0 -97
- package/dist/commands/sheet.js +0 -772
- package/dist/lib/api-client.js +0 -86
- package/dist/lib/cli-error.js +0 -46
- package/dist/lib/command-options.js +0 -61
- package/dist/lib/config-store.js +0 -354
- package/dist/lib/import-files.js +0 -120
- package/dist/lib/import-output.js +0 -81
- package/dist/lib/manifest.js +0 -353
- package/dist/lib/output.js +0 -57
- package/dist/lib/prompt-secret.js +0 -32
- package/dist/lib/session.js +0 -72
- package/dist/lib/source-resolver.js +0 -272
- package/dist/lib/translation-languages.js +0 -16
- package/dist/lib/user-jobs.js +0 -107
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
export const buildCliImportSources = ({ resolvedSources, getExtra, }) => {
|
|
2
|
-
return resolvedSources.map((resolvedSource, sourceIndex) => ({
|
|
3
|
-
sourceIndex,
|
|
4
|
-
sourceInputIndex: resolvedSource.sourceInputIndex,
|
|
5
|
-
sourceId: resolvedSource.sourceInput.sourceId ?? null,
|
|
6
|
-
kind: resolvedSource.kind,
|
|
7
|
-
input: resolvedSource.input,
|
|
8
|
-
fileName: resolvedSource.fileName,
|
|
9
|
-
relativePath: resolvedSource.relativePath,
|
|
10
|
-
...(getExtra ? getExtra(resolvedSource) : {}),
|
|
11
|
-
}));
|
|
12
|
-
};
|
|
13
|
-
export const summarizeCliImportJobs = ({ jobs, sourceCount, sourceInputCount, timedOut, }) => {
|
|
14
|
-
const summary = {
|
|
15
|
-
sourceCount,
|
|
16
|
-
sourceInputCount,
|
|
17
|
-
jobCount: jobs.length,
|
|
18
|
-
queued: 0,
|
|
19
|
-
pending: 0,
|
|
20
|
-
inProgress: 0,
|
|
21
|
-
completed: 0,
|
|
22
|
-
failed: 0,
|
|
23
|
-
timedOut,
|
|
24
|
-
};
|
|
25
|
-
for (const job of jobs) {
|
|
26
|
-
if (job.status === 'queued') {
|
|
27
|
-
summary.queued += 1;
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (job.status === 'pending') {
|
|
31
|
-
summary.pending += 1;
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
if (job.status === 'in_progress') {
|
|
35
|
-
summary.inProgress += 1;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (job.status === 'completed') {
|
|
39
|
-
summary.completed += 1;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
summary.failed += 1;
|
|
43
|
-
}
|
|
44
|
-
return summary;
|
|
45
|
-
};
|
|
46
|
-
export const formatCliImportSummary = (summary) => {
|
|
47
|
-
const parts = [];
|
|
48
|
-
if (summary.queued > 0) {
|
|
49
|
-
parts.push(`${summary.queued} queued`);
|
|
50
|
-
}
|
|
51
|
-
if (summary.pending > 0) {
|
|
52
|
-
parts.push(`${summary.pending} pending`);
|
|
53
|
-
}
|
|
54
|
-
if (summary.inProgress > 0) {
|
|
55
|
-
parts.push(`${summary.inProgress} in progress`);
|
|
56
|
-
}
|
|
57
|
-
if (summary.completed > 0) {
|
|
58
|
-
parts.push(`${summary.completed} completed`);
|
|
59
|
-
}
|
|
60
|
-
if (summary.failed > 0) {
|
|
61
|
-
parts.push(`${summary.failed} failed`);
|
|
62
|
-
}
|
|
63
|
-
if (summary.timedOut) {
|
|
64
|
-
parts.push('timed out');
|
|
65
|
-
}
|
|
66
|
-
return `Summary: ${parts.join(', ') || '0 jobs'}.`;
|
|
67
|
-
};
|
|
68
|
-
export const formatCliImportTranslationJobs = (translationJobs) => {
|
|
69
|
-
if (!translationJobs || translationJobs.length === 0) {
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
return [
|
|
73
|
-
`Queued ${translationJobs.length} translation job${translationJobs.length === 1 ? '' : 's'}:`,
|
|
74
|
-
...translationJobs.map((job) => {
|
|
75
|
-
if (job.type === 'exercise_translation_generation') {
|
|
76
|
-
return `${job.jobId} exercise ${job.exerciseId} ${job.languages.join(', ')}`;
|
|
77
|
-
}
|
|
78
|
-
return `${job.jobId} sheet ${job.exerciseSheetId} ${job.language}`;
|
|
79
|
-
}),
|
|
80
|
-
];
|
|
81
|
-
};
|
package/dist/lib/manifest.js
DELETED
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
3
|
-
import { z } from 'zod';
|
|
4
|
-
import { CliCommandError } from './cli-error.js';
|
|
5
|
-
import { translationLanguages } from './translation-languages.js';
|
|
6
|
-
const isObject = (value) => {
|
|
7
|
-
return typeof value === 'object' && value !== null;
|
|
8
|
-
};
|
|
9
|
-
const normalizeOptionalString = (value) => {
|
|
10
|
-
if (typeof value !== 'string') {
|
|
11
|
-
return undefined;
|
|
12
|
-
}
|
|
13
|
-
const normalizedValue = value.trim();
|
|
14
|
-
return normalizedValue.length > 0 ? normalizedValue : undefined;
|
|
15
|
-
};
|
|
16
|
-
const translationLanguageListSchema = z
|
|
17
|
-
.array(z.enum(translationLanguages))
|
|
18
|
-
.min(1, { message: 'translateTo must include at least one language.' })
|
|
19
|
-
.refine((languages) => new Set(languages).size === languages.length, {
|
|
20
|
-
message: 'translateTo languages must be unique.',
|
|
21
|
-
});
|
|
22
|
-
const sheetImportComponentIdPattern = /^[a-zA-Z0-9_-]{1,80}$/;
|
|
23
|
-
const parseTranslateTo = ({ value, label }) => {
|
|
24
|
-
if (value == null) {
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
const parsedLanguages = translationLanguageListSchema.safeParse(value);
|
|
28
|
-
if (!parsedLanguages.success) {
|
|
29
|
-
const issue = parsedLanguages.error.issues[0];
|
|
30
|
-
throw new CliCommandError(`${label} has invalid translateTo: ${issue?.message ?? 'Invalid languages.'}`, 2);
|
|
31
|
-
}
|
|
32
|
-
return parsedLanguages.data;
|
|
33
|
-
};
|
|
34
|
-
const parseManifestObject = (manifestText) => {
|
|
35
|
-
let parsedManifest;
|
|
36
|
-
try {
|
|
37
|
-
parsedManifest = JSON.parse(manifestText);
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
throw new CliCommandError('Manifest must be valid JSON.', 2);
|
|
41
|
-
}
|
|
42
|
-
if (!isObject(parsedManifest)) {
|
|
43
|
-
throw new CliCommandError('Manifest must be a JSON object.', 2);
|
|
44
|
-
}
|
|
45
|
-
return parsedManifest;
|
|
46
|
-
};
|
|
47
|
-
const parseSourceManifestObject = (manifestText) => {
|
|
48
|
-
const parsedManifest = parseManifestObject(manifestText);
|
|
49
|
-
if (!Array.isArray(parsedManifest.sources) || parsedManifest.sources.length === 0) {
|
|
50
|
-
throw new CliCommandError('Manifest must include a non-empty sources array.', 2);
|
|
51
|
-
}
|
|
52
|
-
return parsedManifest;
|
|
53
|
-
};
|
|
54
|
-
const isValidTargetFolderPath = (value) => {
|
|
55
|
-
const normalizedSegments = value
|
|
56
|
-
.replaceAll('\\', '/')
|
|
57
|
-
.split('/')
|
|
58
|
-
.filter((segment) => segment.length > 0);
|
|
59
|
-
return normalizedSegments.length > 0 && normalizedSegments.every((segment) => segment !== '.' && segment !== '..');
|
|
60
|
-
};
|
|
61
|
-
const parseTargetFolderPath = ({ value, label }) => {
|
|
62
|
-
if (value === null) {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
const normalizedValue = normalizeOptionalString(value);
|
|
66
|
-
if (!normalizedValue) {
|
|
67
|
-
throw new CliCommandError(`${label} must include targetFolderPath as a string or null.`, 2);
|
|
68
|
-
}
|
|
69
|
-
if (!isValidTargetFolderPath(normalizedValue)) {
|
|
70
|
-
throw new CliCommandError(`${label} targetFolderPath must be a normalized folder path without "." or ".." segments.`, 2);
|
|
71
|
-
}
|
|
72
|
-
return normalizedValue;
|
|
73
|
-
};
|
|
74
|
-
const parseOptionalTargetFolderPath = ({ value, label }) => {
|
|
75
|
-
if (value == null) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
return parseTargetFolderPath({ value, label });
|
|
79
|
-
};
|
|
80
|
-
const parseSheetImportComponents = ({ value, sheetIndex, }) => {
|
|
81
|
-
if (value == null) {
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
85
|
-
throw new CliCommandError(`Manifest sheet at index ${sheetIndex} components must be a non-empty array.`, 2);
|
|
86
|
-
}
|
|
87
|
-
const seenComponentIds = new Set();
|
|
88
|
-
return value.map((componentValue, componentIndex) => {
|
|
89
|
-
const label = `Manifest sheet at index ${sheetIndex} component at index ${componentIndex}`;
|
|
90
|
-
if (!isObject(componentValue)) {
|
|
91
|
-
throw new CliCommandError(`${label} must be an object.`, 2);
|
|
92
|
-
}
|
|
93
|
-
const componentId = normalizeOptionalString(componentValue.componentId);
|
|
94
|
-
if (!componentId || !sheetImportComponentIdPattern.test(componentId)) {
|
|
95
|
-
throw new CliCommandError(`${label} must include a componentId using 1-80 letters, numbers, underscores, or hyphens.`, 2);
|
|
96
|
-
}
|
|
97
|
-
if (seenComponentIds.has(componentId)) {
|
|
98
|
-
throw new CliCommandError(`Manifest sheet at index ${sheetIndex} componentId "${componentId}" must be unique.`, 2);
|
|
99
|
-
}
|
|
100
|
-
seenComponentIds.add(componentId);
|
|
101
|
-
const description = normalizeOptionalString(componentValue.description);
|
|
102
|
-
if (!description) {
|
|
103
|
-
throw new CliCommandError(`${label} must include a non-empty description.`, 2);
|
|
104
|
-
}
|
|
105
|
-
return {
|
|
106
|
-
componentId,
|
|
107
|
-
description,
|
|
108
|
-
targetFolderPath: parseOptionalTargetFolderPath({
|
|
109
|
-
value: componentValue.targetFolderPath,
|
|
110
|
-
label,
|
|
111
|
-
}),
|
|
112
|
-
title: normalizeOptionalString(componentValue.title),
|
|
113
|
-
translateTo: parseTranslateTo({
|
|
114
|
-
value: componentValue.translateTo,
|
|
115
|
-
label,
|
|
116
|
-
}),
|
|
117
|
-
};
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
const parseManifestMetadata = ({ parsedManifest, topLevelFields, }) => {
|
|
121
|
-
const wait = typeof parsedManifest.wait === 'boolean' ? parsedManifest.wait : undefined;
|
|
122
|
-
return {
|
|
123
|
-
organizationId: normalizeOptionalString(parsedManifest.organizationId),
|
|
124
|
-
wait,
|
|
125
|
-
...(topLevelFields.includes('exerciseId')
|
|
126
|
-
? { exerciseId: normalizeOptionalString(parsedManifest.exerciseId) }
|
|
127
|
-
: {}),
|
|
128
|
-
...(topLevelFields.includes('exerciseSheetId')
|
|
129
|
-
? { exerciseSheetId: normalizeOptionalString(parsedManifest.exerciseSheetId) }
|
|
130
|
-
: {}),
|
|
131
|
-
};
|
|
132
|
-
};
|
|
133
|
-
const assertUniqueSourceIds = (sources) => {
|
|
134
|
-
const seenSourceIds = new Set();
|
|
135
|
-
for (const source of sources) {
|
|
136
|
-
if (!source.sourceId) {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (seenSourceIds.has(source.sourceId)) {
|
|
140
|
-
throw new CliCommandError(`Manifest sourceId "${source.sourceId}" must be unique.`, 2);
|
|
141
|
-
}
|
|
142
|
-
seenSourceIds.add(source.sourceId);
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
const isInteractiveStdin = (stdin) => {
|
|
146
|
-
return stdin.isTTY === true;
|
|
147
|
-
};
|
|
148
|
-
const readTextFromStdin = async (stdin) => {
|
|
149
|
-
let text = '';
|
|
150
|
-
for await (const chunk of stdin) {
|
|
151
|
-
text += typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8');
|
|
152
|
-
}
|
|
153
|
-
return text;
|
|
154
|
-
};
|
|
155
|
-
const parseBaseManifestSource = (value, index) => {
|
|
156
|
-
if (!isObject(value)) {
|
|
157
|
-
throw new CliCommandError(`Manifest source at index ${index} must be an object.`, 2);
|
|
158
|
-
}
|
|
159
|
-
const kind = normalizeOptionalString(value.kind);
|
|
160
|
-
const sourceId = normalizeOptionalString(value.sourceId);
|
|
161
|
-
if ('title' in value || 'translateTo' in value) {
|
|
162
|
-
throw new CliCommandError(`Manifest source at index ${index} cannot include title or translateTo for this command.`, 2);
|
|
163
|
-
}
|
|
164
|
-
if (kind === 'local') {
|
|
165
|
-
const path = normalizeOptionalString(value.path);
|
|
166
|
-
if (!path) {
|
|
167
|
-
throw new CliCommandError(`Manifest local source at index ${index} must include a path.`, 2);
|
|
168
|
-
}
|
|
169
|
-
return {
|
|
170
|
-
kind: 'local',
|
|
171
|
-
path,
|
|
172
|
-
relativePath: normalizeOptionalString(value.relativePath),
|
|
173
|
-
sourceId,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
if (kind === 'directory') {
|
|
177
|
-
const path = normalizeOptionalString(value.path);
|
|
178
|
-
if (!path) {
|
|
179
|
-
throw new CliCommandError(`Manifest directory source at index ${index} must include a path.`, 2);
|
|
180
|
-
}
|
|
181
|
-
return {
|
|
182
|
-
kind: 'directory',
|
|
183
|
-
path,
|
|
184
|
-
relativeRoot: normalizeOptionalString(value.relativeRoot),
|
|
185
|
-
sourceId,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
if (kind === 'url') {
|
|
189
|
-
const url = normalizeOptionalString(value.url);
|
|
190
|
-
if (!url) {
|
|
191
|
-
throw new CliCommandError(`Manifest URL source at index ${index} must include a url.`, 2);
|
|
192
|
-
}
|
|
193
|
-
return {
|
|
194
|
-
kind: 'url',
|
|
195
|
-
url,
|
|
196
|
-
relativePath: normalizeOptionalString(value.relativePath),
|
|
197
|
-
sourceId,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
throw new CliCommandError(`Manifest source at index ${index} has unsupported kind "${String(value.kind)}".`, 2);
|
|
201
|
-
};
|
|
202
|
-
const parseExerciseImportManifest = (manifestText) => {
|
|
203
|
-
const parsedManifest = parseSourceManifestObject(manifestText);
|
|
204
|
-
const sources = parsedManifest.sources.map((source, index) => parseBaseManifestSource(source, index));
|
|
205
|
-
assertUniqueSourceIds(sources);
|
|
206
|
-
return {
|
|
207
|
-
...parseManifestMetadata({
|
|
208
|
-
parsedManifest,
|
|
209
|
-
topLevelFields: ['exerciseSheetId'],
|
|
210
|
-
}),
|
|
211
|
-
translateTo: parseTranslateTo({
|
|
212
|
-
value: parsedManifest.translateTo,
|
|
213
|
-
label: 'Manifest',
|
|
214
|
-
}),
|
|
215
|
-
sources,
|
|
216
|
-
};
|
|
217
|
-
};
|
|
218
|
-
const parseExerciseSolutionImportManifest = (manifestText) => {
|
|
219
|
-
const parsedManifest = parseSourceManifestObject(manifestText);
|
|
220
|
-
const sources = parsedManifest.sources.map((source, index) => parseBaseManifestSource(source, index));
|
|
221
|
-
assertUniqueSourceIds(sources);
|
|
222
|
-
return {
|
|
223
|
-
...parseManifestMetadata({
|
|
224
|
-
parsedManifest,
|
|
225
|
-
topLevelFields: ['exerciseId'],
|
|
226
|
-
}),
|
|
227
|
-
sources,
|
|
228
|
-
};
|
|
229
|
-
};
|
|
230
|
-
const parseExerciseSheetSolutionImportManifest = (manifestText) => {
|
|
231
|
-
const parsedManifest = parseSourceManifestObject(manifestText);
|
|
232
|
-
const sources = parsedManifest.sources.map((source, index) => parseBaseManifestSource(source, index));
|
|
233
|
-
assertUniqueSourceIds(sources);
|
|
234
|
-
return {
|
|
235
|
-
...parseManifestMetadata({
|
|
236
|
-
parsedManifest,
|
|
237
|
-
topLevelFields: ['exerciseSheetId'],
|
|
238
|
-
}),
|
|
239
|
-
sources,
|
|
240
|
-
};
|
|
241
|
-
};
|
|
242
|
-
const parseSheetImportManifest = (manifestText) => {
|
|
243
|
-
const parsedManifest = parseManifestObject(manifestText);
|
|
244
|
-
if ('sources' in parsedManifest) {
|
|
245
|
-
throw new CliCommandError('Sheet import manifests must use top-level sheets[]. Top-level sources[] is no longer supported.', 2);
|
|
246
|
-
}
|
|
247
|
-
if (!Array.isArray(parsedManifest.sheets) || parsedManifest.sheets.length === 0) {
|
|
248
|
-
throw new CliCommandError('Manifest must include a non-empty sheets array.', 2);
|
|
249
|
-
}
|
|
250
|
-
const sources = [];
|
|
251
|
-
const sheetGroups = [];
|
|
252
|
-
parsedManifest.sheets.forEach((sheetValue, sheetIndex) => {
|
|
253
|
-
if (!isObject(sheetValue)) {
|
|
254
|
-
throw new CliCommandError(`Manifest sheet at index ${sheetIndex} must be an object.`, 2);
|
|
255
|
-
}
|
|
256
|
-
if (!Array.isArray(sheetValue.sources) || sheetValue.sources.length === 0) {
|
|
257
|
-
throw new CliCommandError(`Manifest sheet at index ${sheetIndex} must include a non-empty sources array.`, 2);
|
|
258
|
-
}
|
|
259
|
-
const sourceInputIndexes = sheetValue.sources.map((sourceValue) => {
|
|
260
|
-
const flattenedSourceIndex = sources.length;
|
|
261
|
-
const parsedSource = parseBaseManifestSource(sourceValue, flattenedSourceIndex);
|
|
262
|
-
sources.push(parsedSource);
|
|
263
|
-
return flattenedSourceIndex;
|
|
264
|
-
});
|
|
265
|
-
const components = parseSheetImportComponents({ value: sheetValue.components, sheetIndex });
|
|
266
|
-
if (components) {
|
|
267
|
-
if ('targetFolderPath' in sheetValue || 'title' in sheetValue || 'translateTo' in sheetValue) {
|
|
268
|
-
throw new CliCommandError(`Manifest sheet at index ${sheetIndex} must set targetFolderPath, title, and translateTo on components when components are provided.`, 2);
|
|
269
|
-
}
|
|
270
|
-
sheetGroups.push({
|
|
271
|
-
sourceInputIndexes,
|
|
272
|
-
components,
|
|
273
|
-
});
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
sheetGroups.push({
|
|
277
|
-
sourceInputIndexes,
|
|
278
|
-
targetFolderPath: parseTargetFolderPath({
|
|
279
|
-
value: sheetValue.targetFolderPath,
|
|
280
|
-
label: `Manifest sheet at index ${sheetIndex}`,
|
|
281
|
-
}),
|
|
282
|
-
title: normalizeOptionalString(sheetValue.title),
|
|
283
|
-
translateTo: parseTranslateTo({
|
|
284
|
-
value: sheetValue.translateTo,
|
|
285
|
-
label: `Manifest sheet at index ${sheetIndex}`,
|
|
286
|
-
}),
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
assertUniqueSourceIds(sources);
|
|
290
|
-
return {
|
|
291
|
-
...parseManifestMetadata({
|
|
292
|
-
parsedManifest,
|
|
293
|
-
topLevelFields: [],
|
|
294
|
-
}),
|
|
295
|
-
sources,
|
|
296
|
-
sheetGroups,
|
|
297
|
-
};
|
|
298
|
-
};
|
|
299
|
-
const loadManifest = async ({ cwd, manifestPath, parseManifest, stdin, }) => {
|
|
300
|
-
if (manifestPath === '-') {
|
|
301
|
-
if (isInteractiveStdin(stdin)) {
|
|
302
|
-
throw new CliCommandError('No manifest was piped on stdin. Pipe JSON into "--manifest -".', 2);
|
|
303
|
-
}
|
|
304
|
-
return parseManifest(await readTextFromStdin(stdin));
|
|
305
|
-
}
|
|
306
|
-
const resolvedManifestPath = resolve(cwd, manifestPath);
|
|
307
|
-
let fileStats;
|
|
308
|
-
try {
|
|
309
|
-
fileStats = await stat(resolvedManifestPath);
|
|
310
|
-
}
|
|
311
|
-
catch (error) {
|
|
312
|
-
if (error.code === 'ENOENT') {
|
|
313
|
-
throw new CliCommandError(`Manifest file "${manifestPath}" does not exist.`, 2);
|
|
314
|
-
}
|
|
315
|
-
throw error;
|
|
316
|
-
}
|
|
317
|
-
if (!fileStats.isFile()) {
|
|
318
|
-
throw new CliCommandError(`Manifest path "${manifestPath}" is not a file.`, 2);
|
|
319
|
-
}
|
|
320
|
-
return parseManifest(await readFile(resolvedManifestPath, 'utf8'));
|
|
321
|
-
};
|
|
322
|
-
export const loadExerciseImportManifest = async ({ cwd, manifestPath, stdin, }) => {
|
|
323
|
-
return await loadManifest({
|
|
324
|
-
cwd,
|
|
325
|
-
manifestPath,
|
|
326
|
-
parseManifest: parseExerciseImportManifest,
|
|
327
|
-
stdin,
|
|
328
|
-
});
|
|
329
|
-
};
|
|
330
|
-
export const loadExerciseSolutionImportManifest = async ({ cwd, manifestPath, stdin, }) => {
|
|
331
|
-
return await loadManifest({
|
|
332
|
-
cwd,
|
|
333
|
-
manifestPath,
|
|
334
|
-
parseManifest: parseExerciseSolutionImportManifest,
|
|
335
|
-
stdin,
|
|
336
|
-
});
|
|
337
|
-
};
|
|
338
|
-
export const loadExerciseSheetSolutionImportManifest = async ({ cwd, manifestPath, stdin, }) => {
|
|
339
|
-
return await loadManifest({
|
|
340
|
-
cwd,
|
|
341
|
-
manifestPath,
|
|
342
|
-
parseManifest: parseExerciseSheetSolutionImportManifest,
|
|
343
|
-
stdin,
|
|
344
|
-
});
|
|
345
|
-
};
|
|
346
|
-
export const loadSheetImportManifest = async ({ cwd, manifestPath, stdin, }) => {
|
|
347
|
-
return await loadManifest({
|
|
348
|
-
cwd,
|
|
349
|
-
manifestPath,
|
|
350
|
-
parseManifest: parseSheetImportManifest,
|
|
351
|
-
stdin,
|
|
352
|
-
});
|
|
353
|
-
};
|
package/dist/lib/output.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
const cliJsonSchemaVersion = 'v1';
|
|
2
|
-
const ensureTrailingNewline = (value) => {
|
|
3
|
-
return value.endsWith('\n') ? value : `${value}\n`;
|
|
4
|
-
};
|
|
5
|
-
const writeLine = (writer, value) => {
|
|
6
|
-
writer.write(ensureTrailingNewline(value));
|
|
7
|
-
};
|
|
8
|
-
export const createOutput = ({ json, stdout, stderr, }) => {
|
|
9
|
-
let warnings = [];
|
|
10
|
-
const consumeWarnings = () => {
|
|
11
|
-
const nextWarnings = warnings;
|
|
12
|
-
warnings = [];
|
|
13
|
-
return nextWarnings;
|
|
14
|
-
};
|
|
15
|
-
return {
|
|
16
|
-
json,
|
|
17
|
-
print: (value, humanFormatter, options) => {
|
|
18
|
-
if (json) {
|
|
19
|
-
writeLine(stdout, JSON.stringify({
|
|
20
|
-
schemaVersion: cliJsonSchemaVersion,
|
|
21
|
-
command: options.command,
|
|
22
|
-
ok: options.ok ?? true,
|
|
23
|
-
result: value,
|
|
24
|
-
...(options.error ? { error: options.error } : {}),
|
|
25
|
-
warnings: consumeWarnings(),
|
|
26
|
-
}, null, 2));
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const message = typeof humanFormatter === 'function' ? humanFormatter(value) : humanFormatter;
|
|
30
|
-
writeLine(stdout, message);
|
|
31
|
-
},
|
|
32
|
-
printError: ({ command, error, result }) => {
|
|
33
|
-
if (json) {
|
|
34
|
-
writeLine(stdout, JSON.stringify({
|
|
35
|
-
schemaVersion: cliJsonSchemaVersion,
|
|
36
|
-
command,
|
|
37
|
-
ok: false,
|
|
38
|
-
...(result === undefined ? {} : { result }),
|
|
39
|
-
error,
|
|
40
|
-
warnings: consumeWarnings(),
|
|
41
|
-
}, null, 2));
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
writeLine(stderr, error.message);
|
|
45
|
-
},
|
|
46
|
-
info: (message) => {
|
|
47
|
-
if (json) {
|
|
48
|
-
warnings.push(message);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
writeLine(stderr, message);
|
|
52
|
-
},
|
|
53
|
-
error: (message) => {
|
|
54
|
-
writeLine(stderr, message);
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { createInterface } from 'node:readline/promises';
|
|
2
|
-
import { Writable } from 'node:stream';
|
|
3
|
-
export const createPromptSecret = ({ input = process.stdin, output = process.stderr, } = {}) => {
|
|
4
|
-
return async (message) => {
|
|
5
|
-
let muted = false;
|
|
6
|
-
const maskedOutput = new Writable({
|
|
7
|
-
write(chunk, encoding, callback) {
|
|
8
|
-
if (!muted) {
|
|
9
|
-
output.write(chunk, encoding);
|
|
10
|
-
}
|
|
11
|
-
callback();
|
|
12
|
-
},
|
|
13
|
-
});
|
|
14
|
-
const readline = createInterface({
|
|
15
|
-
input,
|
|
16
|
-
output: maskedOutput,
|
|
17
|
-
terminal: true,
|
|
18
|
-
});
|
|
19
|
-
try {
|
|
20
|
-
output.write(message);
|
|
21
|
-
muted = true;
|
|
22
|
-
const value = await readline.question('');
|
|
23
|
-
muted = false;
|
|
24
|
-
output.write('\n');
|
|
25
|
-
return value.trim();
|
|
26
|
-
}
|
|
27
|
-
finally {
|
|
28
|
-
muted = false;
|
|
29
|
-
readline.close();
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
};
|
package/dist/lib/session.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { createApiClient } from './api-client.js';
|
|
2
|
-
import { CliCommandError } from './cli-error.js';
|
|
3
|
-
import { chalksurfBaseUrlEnvVar, chalksurfOrganizationIdEnvVar, chalksurfTokenEnvVar, resolveBaseUrl, resolveToken, } from './config-store.js';
|
|
4
|
-
const normalizeOptionalString = (value) => {
|
|
5
|
-
const trimmedValue = value?.trim();
|
|
6
|
-
return trimmedValue ? trimmedValue : undefined;
|
|
7
|
-
};
|
|
8
|
-
export const mapApiErrorToCliError = (error) => {
|
|
9
|
-
const message = error instanceof Error ? error.message : 'Unknown API error';
|
|
10
|
-
if (message.includes('UNAUTHORIZED')) {
|
|
11
|
-
return new CliCommandError('Authentication failed. The CLI token is invalid or revoked.', 3);
|
|
12
|
-
}
|
|
13
|
-
return new CliCommandError(`API request failed: ${message}`, 5);
|
|
14
|
-
};
|
|
15
|
-
export const requireResolvedBaseUrl = ({ flagValue, env, config, }) => {
|
|
16
|
-
const resolvedBaseUrl = resolveBaseUrl({ flagValue, env, config });
|
|
17
|
-
if (!resolvedBaseUrl.value) {
|
|
18
|
-
throw new CliCommandError(`Missing ChalkSurf base URL. Pass --base-url, set ${chalksurfBaseUrlEnvVar}, or log in first.`, 2);
|
|
19
|
-
}
|
|
20
|
-
return resolvedBaseUrl;
|
|
21
|
-
};
|
|
22
|
-
export const requireResolvedToken = ({ env, config, }) => {
|
|
23
|
-
const resolvedToken = resolveToken({ env, config });
|
|
24
|
-
if (!resolvedToken.value) {
|
|
25
|
-
throw new CliCommandError(`Not authenticated. Set ${chalksurfTokenEnvVar} or run "chalksurf auth login".`, 3);
|
|
26
|
-
}
|
|
27
|
-
return resolvedToken;
|
|
28
|
-
};
|
|
29
|
-
export const resolveRequestedOrganizationId = ({ config, env, fallbackValue, flagValue, }) => {
|
|
30
|
-
return (normalizeOptionalString(flagValue) ??
|
|
31
|
-
normalizeOptionalString(fallbackValue) ??
|
|
32
|
-
normalizeOptionalString(env[chalksurfOrganizationIdEnvVar]) ??
|
|
33
|
-
normalizeOptionalString(config.organizationId));
|
|
34
|
-
};
|
|
35
|
-
export const createResolvedApiClient = async ({ context, baseUrlFlagValue, organizationFallbackValue, organizationFlagValue, profileName, }) => {
|
|
36
|
-
const config = await context.configStore.loadProfile({ profileName });
|
|
37
|
-
const resolvedBaseUrl = requireResolvedBaseUrl({
|
|
38
|
-
flagValue: baseUrlFlagValue,
|
|
39
|
-
env: context.env,
|
|
40
|
-
config,
|
|
41
|
-
});
|
|
42
|
-
const resolvedToken = requireResolvedToken({
|
|
43
|
-
env: context.env,
|
|
44
|
-
config,
|
|
45
|
-
});
|
|
46
|
-
const organizationId = resolveRequestedOrganizationId({
|
|
47
|
-
flagValue: organizationFlagValue,
|
|
48
|
-
fallbackValue: organizationFallbackValue,
|
|
49
|
-
env: context.env,
|
|
50
|
-
config,
|
|
51
|
-
});
|
|
52
|
-
return {
|
|
53
|
-
apiClient: createApiClient({
|
|
54
|
-
baseUrl: resolvedBaseUrl.value,
|
|
55
|
-
token: resolvedToken.value,
|
|
56
|
-
organizationId,
|
|
57
|
-
}),
|
|
58
|
-
organizationId,
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
export const formatUserIdentity = (profile) => {
|
|
62
|
-
if (profile.name && profile.email) {
|
|
63
|
-
return `${profile.name} <${profile.email}>`;
|
|
64
|
-
}
|
|
65
|
-
if (profile.email) {
|
|
66
|
-
return profile.email;
|
|
67
|
-
}
|
|
68
|
-
if (profile.name) {
|
|
69
|
-
return profile.name;
|
|
70
|
-
}
|
|
71
|
-
return profile.id;
|
|
72
|
-
};
|