@notedge/panduck 0.0.0 → 0.0.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.
@@ -1,231 +1,231 @@
1
- import { stat } from "node:fs/promises";
2
-
3
- import type { Cli, ParsedOptions } from "@vmz/commander";
4
-
5
- import {
6
- assertReadableInput,
7
- buildBlockedReport,
8
- createContext,
9
- resolveFormats,
10
- } from "../context.js";
11
- import { emitReport } from "../diagnostics.js";
12
- import { ExitCode } from "../exit-codes.js";
13
- import { CliOptionError, parseInspectStage, readSharedOptions, readString } from "../options.js";
14
- import { configureConsoleBackend } from "../console-backend.js";
15
- import { createReport } from "../report.js";
16
- import { isStdinPath } from "../paths.js";
17
-
18
- export function registerInspectCommand(cli: Cli): void {
19
- cli.command("inspect", "cli.cmd.inspect")
20
- .option("--from <format>", "cli.opt.from")
21
- .option("--stage <stage>", "cli.opt.stage")
22
- .option("--path <layout-path>", "cli.opt.path")
23
- .option("--budget <limit>", "cli.opt.budget")
24
- .option("--json", "cli.opt.json")
25
- .action((options: ParsedOptions) => runInspect(options));
26
- }
27
-
28
- async function runInspect(options: ParsedOptions): Promise<number> {
29
- try {
30
- const shared = readSharedOptions(options);
31
- const inputPath = options._[0] ?? readString(options, "input");
32
- if (!inputPath) {
33
- return ExitCode.InvalidArgs;
34
- }
35
- const stage = parseInspectStage(readString(options, "stage"));
36
- const layoutPath = readString(options, "path");
37
- const ctx = createContext();
38
- configureConsoleBackend(ctx.bindings, shared.logFile);
39
- const resolved = resolveFormats(inputPath, undefined, shared);
40
-
41
- if (resolved.ambiguous) {
42
- const report = buildBlockedReport(
43
- ctx,
44
- "inspect",
45
- inputPath,
46
- resolved,
47
- "ambiguous input format",
48
- ExitCode.InputUnsupported,
49
- shared,
50
- );
51
- emitReport(report, shared.diagnostics, shared.json);
52
- return ExitCode.InputUnsupported;
53
- }
54
-
55
- try {
56
- await assertReadableInput(inputPath);
57
- } catch {
58
- const report = buildBlockedReport(
59
- ctx,
60
- "inspect",
61
- inputPath,
62
- resolved,
63
- "input is not readable",
64
- ExitCode.InputUnsupported,
65
- shared,
66
- );
67
- emitReport(report, shared.diagnostics, shared.json);
68
- return ExitCode.InputUnsupported;
69
- }
70
-
71
- const size = isStdinPath(inputPath) ? null : (await stat(inputPath)).size;
72
-
73
- if (stage === "decode" && resolved.from === "docx" && ctx.bindings?.inspectDecode) {
74
- try {
75
- const decode = ctx.bindings.inspectDecode(inputPath, layoutPath);
76
- const report = createReport({
77
- operation: "inspect",
78
- toolVersion: ctx.toolVersion,
79
- status: "success",
80
- inputs: [{ path: inputPath, format: decode.format }],
81
- detection: {
82
- format: decode.format,
83
- outer: decode.outer,
84
- inner: decode.inner,
85
- confidence: "verified",
86
- hints: resolved.hints,
87
- },
88
- pipeline: { stages: ["decode"] },
89
- decodedParts: decode.parts.map((part) => ({
90
- path: part.path,
91
- compression_method: part.compressionMethod,
92
- compressed_size: part.compressedSize,
93
- uncompressed_size: part.uncompressedSize,
94
- decoded_size: part.decodedSize,
95
- })),
96
- budgets: shared.budgets,
97
- determinism: { profile: shared.profile ?? "default", config: shared.config ?? null },
98
- statusPolicy: {
99
- loss: shared.loss,
100
- strict: shared.strict,
101
- exit_code: ExitCode.Success,
102
- },
103
- });
104
- if (size !== null) {
105
- report.budgets = { ...report.budgets, input_bytes: size };
106
- }
107
- emitReport(report, shared.diagnostics, shared.json);
108
- return ExitCode.Success;
109
- } catch (error) {
110
- const message = error instanceof Error ? error.message : String(error);
111
- const report = buildBlockedReport(
112
- ctx,
113
- "inspect",
114
- inputPath,
115
- resolved,
116
- message,
117
- ExitCode.IrOrWriterFailure,
118
- shared,
119
- );
120
- emitReport(report, shared.diagnostics, shared.json);
121
- return ExitCode.IrOrWriterFailure;
122
- }
123
- }
124
-
125
- if (stage === "index" && resolved.from === "docx" && ctx.bindings?.inspectIndex) {
126
- try {
127
- const index = ctx.bindings.inspectIndex(inputPath);
128
- const report = createReport({
129
- operation: "inspect",
130
- toolVersion: ctx.toolVersion,
131
- status: "success",
132
- inputs: [{ path: inputPath, format: index.format }],
133
- detection: {
134
- format: index.format,
135
- outer: index.outer,
136
- inner: index.inner,
137
- confidence: "verified",
138
- hints: resolved.hints,
139
- },
140
- pipeline: { stages: ["index"] },
141
- parts: index.parts,
142
- budgets: shared.budgets,
143
- determinism: { profile: shared.profile ?? "default", config: shared.config ?? null },
144
- statusPolicy: {
145
- loss: shared.loss,
146
- strict: shared.strict,
147
- exit_code: ExitCode.Success,
148
- },
149
- });
150
- if (size !== null) {
151
- report.budgets = { ...report.budgets, input_bytes: size };
152
- }
153
- emitReport(report, shared.diagnostics, shared.json);
154
- return ExitCode.Success;
155
- } catch (error) {
156
- const message = error instanceof Error ? error.message : String(error);
157
- const report = buildBlockedReport(
158
- ctx,
159
- "inspect",
160
- inputPath,
161
- resolved,
162
- message,
163
- ExitCode.IrOrWriterFailure,
164
- shared,
165
- );
166
- emitReport(report, shared.diagnostics, shared.json);
167
- return ExitCode.IrOrWriterFailure;
168
- }
169
- }
170
-
171
- const report = createReport({
172
- operation: "inspect",
173
- toolVersion: ctx.toolVersion,
174
- status: stage === "probe" ? "success" : "partial",
175
- inputs: [{ path: inputPath, format: resolved.from }],
176
- detection: {
177
- format: resolved.from,
178
- confidence: resolved.from ? "hint" : undefined,
179
- hints: resolved.hints,
180
- },
181
- pipeline: {
182
- stages: [stage],
183
- blocked_reason:
184
- stage === "probe"
185
- ? undefined
186
- : "deeper inspect stages require Acorn/Oak integration",
187
- },
188
- diagnostics:
189
- stage === "probe"
190
- ? []
191
- : [
192
- {
193
- owner: "panduck",
194
- severity: "warning",
195
- message: `inspect stage ${stage} is not implemented yet`,
196
- code: "panduck.inspect.not_implemented",
197
- },
198
- ],
199
- budgets: shared.budgets,
200
- determinism: { profile: shared.profile ?? "default", config: shared.config ?? null },
201
- statusPolicy: {
202
- loss: shared.loss,
203
- strict: shared.strict,
204
- exit_code: stage === "probe" ? ExitCode.Success : ExitCode.IrOrWriterFailure,
205
- },
206
- });
207
-
208
- if (layoutPath) {
209
- report.diagnostics?.push({
210
- owner: "acorn",
211
- severity: "warning",
212
- message: `layout path ${layoutPath} indexing is not implemented yet`,
213
- code: "acorn.layout.not_implemented",
214
- });
215
- }
216
-
217
- if (size !== null) {
218
- report.budgets = { ...report.budgets, input_bytes: size };
219
- }
220
-
221
- emitReport(report, shared.diagnostics, shared.json);
222
- return stage === "probe" ? ExitCode.Success : ExitCode.IrOrWriterFailure;
223
- } catch (error) {
224
- if (error instanceof CliOptionError) {
225
- console.error(error.message);
226
- return error.exitCode;
227
- }
228
- console.error(error instanceof Error ? error.message : String(error));
229
- return ExitCode.InvalidArgs;
230
- }
231
- }
1
+ import { stat } from "node:fs/promises";
2
+
3
+ import type { Cli, ParsedOptions } from "@vmz/commander";
4
+
5
+ import {
6
+ assertReadableInput,
7
+ buildBlockedReport,
8
+ createContext,
9
+ resolveFormats,
10
+ } from "../context.js";
11
+ import { emitReport } from "../diagnostics.js";
12
+ import { ExitCode } from "../exit-codes.js";
13
+ import { CliOptionError, parseInspectStage, readSharedOptions, readString } from "../options.js";
14
+ import { configureConsoleBackend } from "../console-backend.js";
15
+ import { createReport } from "../report.js";
16
+ import { isStdinPath } from "../paths.js";
17
+
18
+ export function registerInspectCommand(cli: Cli): void {
19
+ cli.command("inspect", "cli.cmd.inspect")
20
+ .option("--from <format>", "cli.opt.from")
21
+ .option("--stage <stage>", "cli.opt.stage")
22
+ .option("--path <layout-path>", "cli.opt.path")
23
+ .option("--budget <limit>", "cli.opt.budget")
24
+ .option("--json", "cli.opt.json")
25
+ .action((options: ParsedOptions) => runInspect(options));
26
+ }
27
+
28
+ async function runInspect(options: ParsedOptions): Promise<number> {
29
+ try {
30
+ const shared = readSharedOptions(options);
31
+ const inputPath = options._[0] ?? readString(options, "input");
32
+ if (!inputPath) {
33
+ return ExitCode.InvalidArgs;
34
+ }
35
+ const stage = parseInspectStage(readString(options, "stage"));
36
+ const layoutPath = readString(options, "path");
37
+ const ctx = createContext();
38
+ configureConsoleBackend(ctx.bindings, shared.logFile);
39
+ const resolved = resolveFormats(inputPath, undefined, shared);
40
+
41
+ if (resolved.ambiguous) {
42
+ const report = buildBlockedReport(
43
+ ctx,
44
+ "inspect",
45
+ inputPath,
46
+ resolved,
47
+ "ambiguous input format",
48
+ ExitCode.InputUnsupported,
49
+ shared,
50
+ );
51
+ emitReport(report, shared.diagnostics, shared.json);
52
+ return ExitCode.InputUnsupported;
53
+ }
54
+
55
+ try {
56
+ await assertReadableInput(inputPath);
57
+ } catch {
58
+ const report = buildBlockedReport(
59
+ ctx,
60
+ "inspect",
61
+ inputPath,
62
+ resolved,
63
+ "input is not readable",
64
+ ExitCode.InputUnsupported,
65
+ shared,
66
+ );
67
+ emitReport(report, shared.diagnostics, shared.json);
68
+ return ExitCode.InputUnsupported;
69
+ }
70
+
71
+ const size = isStdinPath(inputPath) ? null : (await stat(inputPath)).size;
72
+
73
+ if (stage === "decode" && resolved.from === "docx" && ctx.bindings?.inspectDecode) {
74
+ try {
75
+ const decode = ctx.bindings.inspectDecode(inputPath, layoutPath);
76
+ const report = createReport({
77
+ operation: "inspect",
78
+ toolVersion: ctx.toolVersion,
79
+ status: "success",
80
+ inputs: [{ path: inputPath, format: decode.format }],
81
+ detection: {
82
+ format: decode.format,
83
+ outer: decode.outer,
84
+ inner: decode.inner,
85
+ confidence: "verified",
86
+ hints: resolved.hints,
87
+ },
88
+ pipeline: { stages: ["decode"] },
89
+ decodedParts: decode.parts.map((part) => ({
90
+ path: part.path,
91
+ compression_method: part.compressionMethod,
92
+ compressed_size: part.compressedSize,
93
+ uncompressed_size: part.uncompressedSize,
94
+ decoded_size: part.decodedSize,
95
+ })),
96
+ budgets: shared.budgets,
97
+ determinism: { profile: shared.profile ?? "default", config: shared.config ?? null },
98
+ statusPolicy: {
99
+ loss: shared.loss,
100
+ strict: shared.strict,
101
+ exit_code: ExitCode.Success,
102
+ },
103
+ });
104
+ if (size !== null) {
105
+ report.budgets = { ...report.budgets, input_bytes: size };
106
+ }
107
+ emitReport(report, shared.diagnostics, shared.json);
108
+ return ExitCode.Success;
109
+ } catch (error) {
110
+ const message = error instanceof Error ? error.message : String(error);
111
+ const report = buildBlockedReport(
112
+ ctx,
113
+ "inspect",
114
+ inputPath,
115
+ resolved,
116
+ message,
117
+ ExitCode.IrOrWriterFailure,
118
+ shared,
119
+ );
120
+ emitReport(report, shared.diagnostics, shared.json);
121
+ return ExitCode.IrOrWriterFailure;
122
+ }
123
+ }
124
+
125
+ if (stage === "index" && resolved.from === "docx" && ctx.bindings?.inspectIndex) {
126
+ try {
127
+ const index = ctx.bindings.inspectIndex(inputPath);
128
+ const report = createReport({
129
+ operation: "inspect",
130
+ toolVersion: ctx.toolVersion,
131
+ status: "success",
132
+ inputs: [{ path: inputPath, format: index.format }],
133
+ detection: {
134
+ format: index.format,
135
+ outer: index.outer,
136
+ inner: index.inner,
137
+ confidence: "verified",
138
+ hints: resolved.hints,
139
+ },
140
+ pipeline: { stages: ["index"] },
141
+ parts: index.parts,
142
+ budgets: shared.budgets,
143
+ determinism: { profile: shared.profile ?? "default", config: shared.config ?? null },
144
+ statusPolicy: {
145
+ loss: shared.loss,
146
+ strict: shared.strict,
147
+ exit_code: ExitCode.Success,
148
+ },
149
+ });
150
+ if (size !== null) {
151
+ report.budgets = { ...report.budgets, input_bytes: size };
152
+ }
153
+ emitReport(report, shared.diagnostics, shared.json);
154
+ return ExitCode.Success;
155
+ } catch (error) {
156
+ const message = error instanceof Error ? error.message : String(error);
157
+ const report = buildBlockedReport(
158
+ ctx,
159
+ "inspect",
160
+ inputPath,
161
+ resolved,
162
+ message,
163
+ ExitCode.IrOrWriterFailure,
164
+ shared,
165
+ );
166
+ emitReport(report, shared.diagnostics, shared.json);
167
+ return ExitCode.IrOrWriterFailure;
168
+ }
169
+ }
170
+
171
+ const report = createReport({
172
+ operation: "inspect",
173
+ toolVersion: ctx.toolVersion,
174
+ status: stage === "probe" ? "success" : "partial",
175
+ inputs: [{ path: inputPath, format: resolved.from }],
176
+ detection: {
177
+ format: resolved.from,
178
+ confidence: resolved.from ? "hint" : undefined,
179
+ hints: resolved.hints,
180
+ },
181
+ pipeline: {
182
+ stages: [stage],
183
+ blocked_reason:
184
+ stage === "probe"
185
+ ? undefined
186
+ : "deeper inspect stages require Acorn/Oak integration",
187
+ },
188
+ diagnostics:
189
+ stage === "probe"
190
+ ? []
191
+ : [
192
+ {
193
+ owner: "panduck",
194
+ severity: "warning",
195
+ message: `inspect stage ${stage} is not implemented yet`,
196
+ code: "panduck.inspect.not_implemented",
197
+ },
198
+ ],
199
+ budgets: shared.budgets,
200
+ determinism: { profile: shared.profile ?? "default", config: shared.config ?? null },
201
+ statusPolicy: {
202
+ loss: shared.loss,
203
+ strict: shared.strict,
204
+ exit_code: stage === "probe" ? ExitCode.Success : ExitCode.IrOrWriterFailure,
205
+ },
206
+ });
207
+
208
+ if (layoutPath) {
209
+ report.diagnostics?.push({
210
+ owner: "acorn",
211
+ severity: "warning",
212
+ message: `layout path ${layoutPath} indexing is not implemented yet`,
213
+ code: "acorn.layout.not_implemented",
214
+ });
215
+ }
216
+
217
+ if (size !== null) {
218
+ report.budgets = { ...report.budgets, input_bytes: size };
219
+ }
220
+
221
+ emitReport(report, shared.diagnostics, shared.json);
222
+ return stage === "probe" ? ExitCode.Success : ExitCode.IrOrWriterFailure;
223
+ } catch (error) {
224
+ if (error instanceof CliOptionError) {
225
+ console.error(error.message);
226
+ return error.exitCode;
227
+ }
228
+ console.error(error instanceof Error ? error.message : String(error));
229
+ return ExitCode.InvalidArgs;
230
+ }
231
+ }