@cs-object/cli 0.1.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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +195 -0
- package/dist/chunk-7XCJ6AF3.js +607 -0
- package/dist/chunk-KYWIVB2V.js +341 -0
- package/dist/chunk-MOM6Q2VG.js +44 -0
- package/dist/chunk-OYQDZWT7.js +145 -0
- package/dist/chunk-UCPKZ67Z.js +88 -0
- package/dist/cli.js +262 -0
- package/dist/dev-server-BVY4Q6R6.js +732 -0
- package/dist/development.js +166 -0
- package/dist/html-AAIMIB2S.js +9 -0
- package/dist/pdf-WXWGZ6NC.js +9 -0
- package/package.json +60 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
UsageError,
|
|
4
|
+
help,
|
|
5
|
+
parseCliArgs
|
|
6
|
+
} from "./chunk-OYQDZWT7.js";
|
|
7
|
+
import {
|
|
8
|
+
initialReport,
|
|
9
|
+
usageReport,
|
|
10
|
+
verifyCommand
|
|
11
|
+
} from "./chunk-KYWIVB2V.js";
|
|
12
|
+
import {
|
|
13
|
+
stringifyJson
|
|
14
|
+
} from "./chunk-MOM6Q2VG.js";
|
|
15
|
+
|
|
16
|
+
// src/cli.ts
|
|
17
|
+
import { CommandReportSchema } from "@cs-object/core";
|
|
18
|
+
|
|
19
|
+
// src/verified-arguments.ts
|
|
20
|
+
import { resolve } from "path";
|
|
21
|
+
function parseVerifiedArgs(command2, args2) {
|
|
22
|
+
const [source, ...options] = args2;
|
|
23
|
+
if (!source || source.startsWith("--")) {
|
|
24
|
+
throw new UsageError("A source file is required");
|
|
25
|
+
}
|
|
26
|
+
const forwarded = ["--source", source];
|
|
27
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28
|
+
let referencePath;
|
|
29
|
+
let outPath;
|
|
30
|
+
let retainEvidence = true;
|
|
31
|
+
let checkLayout = false;
|
|
32
|
+
for (let index = 0; index < options.length; ) {
|
|
33
|
+
const key = options[index];
|
|
34
|
+
if (key === "--no-evidence" || key === "--check-layout") {
|
|
35
|
+
if (key === "--no-evidence" && command2 === "verify" || key === "--check-layout" && command2 !== "html") {
|
|
36
|
+
throw new UsageError(`Unknown option ${key}`);
|
|
37
|
+
}
|
|
38
|
+
if (seen.has(key)) {
|
|
39
|
+
throw new UsageError(`${key} was provided more than once`);
|
|
40
|
+
}
|
|
41
|
+
seen.add(key);
|
|
42
|
+
if (key === "--no-evidence") retainEvidence = false;
|
|
43
|
+
else checkLayout = true;
|
|
44
|
+
index += 1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const value = options[index + 1];
|
|
48
|
+
if (![
|
|
49
|
+
"--function",
|
|
50
|
+
"--input",
|
|
51
|
+
"--reference",
|
|
52
|
+
"--format",
|
|
53
|
+
...command2 !== "verify" ? ["--out"] : []
|
|
54
|
+
].includes(key)) {
|
|
55
|
+
throw new UsageError(`Unknown option ${key}`);
|
|
56
|
+
}
|
|
57
|
+
if (!value || value.startsWith("--")) {
|
|
58
|
+
throw new UsageError(`${key} requires a value`);
|
|
59
|
+
}
|
|
60
|
+
if (key !== "--input" && seen.has(key)) {
|
|
61
|
+
throw new UsageError(`${key} was provided more than once`);
|
|
62
|
+
}
|
|
63
|
+
seen.add(key);
|
|
64
|
+
if (key === "--format") {
|
|
65
|
+
if (value !== "json") {
|
|
66
|
+
throw new UsageError("--format must be json");
|
|
67
|
+
}
|
|
68
|
+
} else if (key === "--reference") {
|
|
69
|
+
referencePath = resolve(value);
|
|
70
|
+
} else if (key === "--out") {
|
|
71
|
+
outPath = resolve(value);
|
|
72
|
+
} else {
|
|
73
|
+
forwarded.push(key, value);
|
|
74
|
+
}
|
|
75
|
+
index += 2;
|
|
76
|
+
}
|
|
77
|
+
if (command2 !== "verify" && !outPath) {
|
|
78
|
+
throw new UsageError("--out is required");
|
|
79
|
+
}
|
|
80
|
+
const parsed = parseCliArgs([...forwarded, "--out", outPath ?? "."]);
|
|
81
|
+
const common = {
|
|
82
|
+
sourcePath: parsed.sourcePath,
|
|
83
|
+
functionName: parsed.functionName,
|
|
84
|
+
inputs: parsed.inputs,
|
|
85
|
+
referencePath
|
|
86
|
+
};
|
|
87
|
+
if (command2 === "html") {
|
|
88
|
+
return {
|
|
89
|
+
...common,
|
|
90
|
+
command: command2,
|
|
91
|
+
outPath: parsed.outPath,
|
|
92
|
+
retainEvidence,
|
|
93
|
+
checkLayout
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (command2 === "pdf") {
|
|
97
|
+
return { ...common, command: command2, outPath: parsed.outPath, retainEvidence };
|
|
98
|
+
}
|
|
99
|
+
return { ...common, command: command2 };
|
|
100
|
+
}
|
|
101
|
+
function verifiedHelp(command2) {
|
|
102
|
+
if (command2 === "html")
|
|
103
|
+
return `Usage: cso html <file.cso.py> --function <name> [--input <name=number>] [--reference <file.json>] --out <path.html> [--format json] [--no-evidence] [--check-layout]
|
|
104
|
+
|
|
105
|
+
Verify one captured execution and atomically publish standalone HTML with embedded CSS/assets.
|
|
106
|
+
--check-layout uses Chromium to check assets, presentation and screen/print overflow before publication.
|
|
107
|
+
Without --check-layout, no browser runs and rendering is not_applicable. Visual inspection stays pending.
|
|
108
|
+
--no-evidence omits the sibling evidence bundle; verification and atomic replacement still apply.
|
|
109
|
+
Inputs, reference matching, JSON reports, PYTHON and exit codes follow cso verify.
|
|
110
|
+
Paths resolve against the caller directory. --help executes neither Python nor Chromium.
|
|
111
|
+
`;
|
|
112
|
+
return `Usage: cso ${command2} <file.cso.py> --function <name> [--input <name=number>] [--reference <file.json>]${command2 === "pdf" ? " --out <path.pdf>" : ""} [--format json]${command2 === "pdf" ? " [--no-evidence]" : ""}
|
|
113
|
+
|
|
114
|
+
${command2 === "verify" ? "Execute once with installed cso-python and independently evaluate every documented result." : "Verify once, render the captured execution, and atomically replace the PDF. Evidence is retained unless --no-evidence is set."}
|
|
115
|
+
Repeat --input for distinct parameters. Duplicate, unknown and invalid inputs fail.
|
|
116
|
+
--reference captures one explicit versioned reference file. Missing matches are not_applicable.
|
|
117
|
+
--format json emits one public CommandReport, including on errors.
|
|
118
|
+
${command2 === "pdf" ? "--no-evidence writes only the PDF; no sibling evidence bundle or stderr locator.\n" : ""}--help shows this command help without executing Python.
|
|
119
|
+
Paths resolve against the caller directory. PYTHON selects the installed interpreter.
|
|
120
|
+
Exit codes: 0 success, 1 verification/generation failure, 2 invalid usage.
|
|
121
|
+
Tolerance: abs(actual - expected) <= max(1e-9, 1e-12 * max(abs(actual), abs(expected))).
|
|
122
|
+
Document, rendering and visual checks do not apply to verify.
|
|
123
|
+
${command2 === "pdf" ? "PDF evidence is retained at <output>.evidence/<manifest-hash>/ unless --no-evidence is set; its locator/hash goes to stderr.\nGenerated PDFs have pending visual inspection. Evidence alone is not proof of PDF publication.\n" : ""}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/bindings.ts
|
|
127
|
+
import { spawnSync } from "child_process";
|
|
128
|
+
var bindingsHelp = `Usage: cso bindings <directory> [--check]
|
|
129
|
+
|
|
130
|
+
Generate importable _cso_bindings modules and typed signatures from .cso.py files.
|
|
131
|
+
--check reports stale or missing files without writing. Calculations are not executed.
|
|
132
|
+
Python-only equivalent: python -m cso_python bindings <directory> [--check]
|
|
133
|
+
Set PYTHON to the interpreter with cso-python installed.
|
|
134
|
+
Exit codes: 0 current/generated, 1 stale/source/write failure, 2 invalid usage.
|
|
135
|
+
`;
|
|
136
|
+
function bindingsCommand(args2) {
|
|
137
|
+
const environment = { ...process.env };
|
|
138
|
+
for (const key of ["PYTHONPATH", "PYTHONHOME", "NODE_PATH"]) {
|
|
139
|
+
Reflect.deleteProperty(environment, key);
|
|
140
|
+
}
|
|
141
|
+
const result = spawnSync(
|
|
142
|
+
process.env.PYTHON ?? "python3",
|
|
143
|
+
["-I", "-m", "cso_python", "bindings", ...args2],
|
|
144
|
+
{ env: environment, maxBuffer: 64 * 1024 * 1024 }
|
|
145
|
+
);
|
|
146
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
147
|
+
if (result.error || result.signal) {
|
|
148
|
+
process.stdout.write(
|
|
149
|
+
`${JSON.stringify({
|
|
150
|
+
ok: false,
|
|
151
|
+
diagnostics: [
|
|
152
|
+
{
|
|
153
|
+
code: "PYTHON_PROCESS_FAILED",
|
|
154
|
+
message: result.error?.message ?? `Python terminated by ${result.signal}`
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
})}
|
|
158
|
+
`
|
|
159
|
+
);
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
162
|
+
process.stdout.write(result.stdout);
|
|
163
|
+
return result.status ?? 1;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/cli.ts
|
|
167
|
+
var [command, ...args] = process.argv.slice(2);
|
|
168
|
+
if (command === "--help" || command === void 0 || [
|
|
169
|
+
"verify",
|
|
170
|
+
"pdf",
|
|
171
|
+
"html",
|
|
172
|
+
"bindings",
|
|
173
|
+
"dev",
|
|
174
|
+
"dev-export",
|
|
175
|
+
"dev-render"
|
|
176
|
+
].includes(command) && args.includes("--help")) {
|
|
177
|
+
process.stdout.write(
|
|
178
|
+
command === "verify" || command === "pdf" || command === "html" ? verifiedHelp(command) : command === "dev" ? (await import("./dev-server-BVY4Q6R6.js")).devHelp : command === "bindings" ? bindingsHelp : help
|
|
179
|
+
);
|
|
180
|
+
} else if (command === "dev") {
|
|
181
|
+
try {
|
|
182
|
+
const { devCommand } = await import("./dev-server-BVY4Q6R6.js");
|
|
183
|
+
await devCommand(args);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
process.stderr.write(
|
|
186
|
+
`${error instanceof Error ? error.message : String(error)}
|
|
187
|
+
`
|
|
188
|
+
);
|
|
189
|
+
process.exitCode = error instanceof UsageError ? 2 : 1;
|
|
190
|
+
}
|
|
191
|
+
} else if (command === "bindings") {
|
|
192
|
+
process.exitCode = bindingsCommand(args);
|
|
193
|
+
} else if (command === "verify" || command === "pdf" || command === "html") {
|
|
194
|
+
let outcome;
|
|
195
|
+
try {
|
|
196
|
+
const options = parseVerifiedArgs(command, args);
|
|
197
|
+
if (options.command === "html") {
|
|
198
|
+
const { htmlCommand } = await import("./html-AAIMIB2S.js");
|
|
199
|
+
outcome = await htmlCommand(options);
|
|
200
|
+
} else if (options.command === "pdf") {
|
|
201
|
+
const { pdfCommand } = await import("./pdf-WXWGZ6NC.js");
|
|
202
|
+
outcome = await pdfCommand(options);
|
|
203
|
+
} else {
|
|
204
|
+
outcome = verifyCommand(options);
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
const report = initialReport(command);
|
|
208
|
+
report.diagnostics.push({
|
|
209
|
+
code: error instanceof UsageError ? "INVALID_USAGE" : "COMMAND_FAILED",
|
|
210
|
+
stage: error instanceof UsageError ? "usage" : "execution",
|
|
211
|
+
message: error instanceof Error ? error.message : String(error)
|
|
212
|
+
});
|
|
213
|
+
outcome = {
|
|
214
|
+
report: CommandReportSchema.parse(report),
|
|
215
|
+
exitCode: error instanceof UsageError ? 2 : 1
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (outcome.evidence)
|
|
219
|
+
process.stderr.write(`CSO evidence ${stringifyJson(outcome.evidence)}
|
|
220
|
+
`);
|
|
221
|
+
process.stdout.write(
|
|
222
|
+
outcome.stdoutBytes ?? `${stringifyJson(outcome.report)}
|
|
223
|
+
`
|
|
224
|
+
);
|
|
225
|
+
process.exitCode = outcome.exitCode;
|
|
226
|
+
} else if (["dev-export", "dev-render"].includes(command)) {
|
|
227
|
+
try {
|
|
228
|
+
const options = parseCliArgs(args);
|
|
229
|
+
const development = await import("./development.js");
|
|
230
|
+
if (command === "dev-export") {
|
|
231
|
+
development.writeAtomically(
|
|
232
|
+
options.outPath,
|
|
233
|
+
`${stringifyJson(development.runPythonExporter(options))}
|
|
234
|
+
`
|
|
235
|
+
);
|
|
236
|
+
} else {
|
|
237
|
+
await development.renderAnnotatedPythonPdf(options);
|
|
238
|
+
}
|
|
239
|
+
process.stderr.write(
|
|
240
|
+
`Unverified development ${command === "dev-export" ? "CSO" : "PDF"} written to ${options.outPath}
|
|
241
|
+
`
|
|
242
|
+
);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
process.stderr.write(
|
|
245
|
+
`${error instanceof Error ? error.message : String(error)}
|
|
246
|
+
`
|
|
247
|
+
);
|
|
248
|
+
process.exitCode = error instanceof UsageError ? 2 : 1;
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
const report = usageReport({ kind: "unknown", command });
|
|
252
|
+
if (args.some(
|
|
253
|
+
(value, index) => value === "--format" && args[index + 1] === "json"
|
|
254
|
+
)) {
|
|
255
|
+
process.stdout.write(`${stringifyJson(report)}
|
|
256
|
+
`);
|
|
257
|
+
} else {
|
|
258
|
+
process.stderr.write(`${report.diagnostics[0]?.message}
|
|
259
|
+
${help}`);
|
|
260
|
+
}
|
|
261
|
+
process.exitCode = 2;
|
|
262
|
+
}
|