@intactfile/cli 0.1.0 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +68 -3
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -7,12 +7,13 @@ import { mkdirSync, writeFileSync } from "node:fs";
7
7
  import { basename, dirname, extname, join, parse } from "node:path";
8
8
  import { createInterface } from "node:readline";
9
9
  import { Command } from "commander";
10
- import { ApiError, balance, clearAuth, consume, EntitlementError, getToken, login as apiLogin, magic as apiMagic, recover as apiRecover, repairPath, diagnosePath, requireBusiness, setToken, UnsupportedError, } from "@intactfile/node-core";
10
+ import { ApiError, balance, clearAuth, consume, EntitlementError, ensureLicense, getLicenseStatus, getToken, login as apiLogin, magic as apiMagic, recover as apiRecover, repairPath, diagnosePath, requireBusiness, setToken, UnsupportedError, } from "@intactfile/node-core";
11
+ import { generateRepairReportPdf } from "@intactfile/report";
11
12
  const program = new Command();
12
13
  program
13
14
  .name("intactfile")
14
15
  .description("Repair corrupted files locally — your files never leave your machine.")
15
- .version("0.1.0");
16
+ .version("0.2.0");
16
17
  /* ------------------------------- helpers ------------------------------- */
17
18
  function ask(query) {
18
19
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -42,6 +43,8 @@ const OUT_EXT = {
42
43
  png: ".png",
43
44
  sqlite: ".sqlite",
44
45
  archive: "",
46
+ audio: ".wav",
47
+ avi: ".avi",
45
48
  };
46
49
  function defaultOut(input, family) {
47
50
  const p = parse(input);
@@ -85,6 +88,34 @@ function writeResult(input, out, r) {
85
88
  writeFileSync(dest, r.bytes);
86
89
  return [dest];
87
90
  }
91
+ /** Map a repair result into the report package's minimal input shape. */
92
+ function toReportInput(input, r) {
93
+ const report = r.report;
94
+ const num = (v) => {
95
+ const n = typeof v === "number" ? v : v == null ? NaN : Number(v);
96
+ return Number.isFinite(n) ? n : undefined;
97
+ };
98
+ const base = {
99
+ fileName: basename(input),
100
+ family: r.family,
101
+ outcome: report.outcome != null ? String(report.outcome) : undefined,
102
+ damageClass: report.damageClass != null ? String(report.damageClass) : undefined,
103
+ bytesIn: num(report.bytesIn),
104
+ bytesOut: num(report.bytesOut),
105
+ notes: Array.isArray(report.notes) ? report.notes.map((n) => String(n)) : undefined,
106
+ };
107
+ if (r.kind === "archive") {
108
+ base.files = r.files.map((f) => ({ name: f.name, status: f.status }));
109
+ }
110
+ return base;
111
+ }
112
+ /** Generate and write a white-label PDF repair report; prints the path. */
113
+ async function writeReportPdf(input, r, path, company) {
114
+ const bytes = await generateRepairReportPdf(toReportInput(input, r), company ? { company } : {});
115
+ mkdirSync(dirname(path), { recursive: true });
116
+ writeFileSync(path, bytes);
117
+ console.log(`✓ Report: ${path}`);
118
+ }
88
119
  /* -------------------------------- login -------------------------------- */
89
120
  program
90
121
  .command("login")
@@ -147,6 +178,27 @@ program
147
178
  handle(e);
148
179
  }
149
180
  });
181
+ program
182
+ .command("license")
183
+ .description("Fetch and apply your offline Business license (used by repair and the SDK).")
184
+ .option("--refresh", "force a fresh license even if the cached one is still valid")
185
+ .action(async (opts) => {
186
+ try {
187
+ await requireBusiness();
188
+ const token = getToken();
189
+ if (token)
190
+ await ensureLicense(token, { force: !!opts.refresh });
191
+ const status = await getLicenseStatus();
192
+ if (!status.licensed) {
193
+ return die(`Couldn't apply a license (${status.reason ?? "unknown"}).`);
194
+ }
195
+ console.log(`✓ License active (${status.tier}) — valid until ${status.expiresAt?.slice(0, 10)}.`);
196
+ console.log(" The CLI, MCP server, and SDK use it automatically; it works offline until then.");
197
+ }
198
+ catch (e) {
199
+ handle(e);
200
+ }
201
+ });
150
202
  function summarize(sub) {
151
203
  if (!sub)
152
204
  return console.log("Plan: none (no active subscription).");
@@ -182,13 +234,18 @@ program
182
234
  .option("-o, --out <path>", "output path (or directory for archives)")
183
235
  .option("-r, --reference <clip>", "healthy reference clip/photo (missing-moov video, JPEG donor)")
184
236
  .option("-p, --password <pw>", "archive password (RAR/7z)")
237
+ .option("--report <path.pdf>", "also write a white-label PDF repair report to this path")
238
+ .option("--company <name>", "company name for the white-label report header")
185
239
  .action(async (file, opts) => {
186
240
  try {
187
241
  const sub = await requireBusiness();
188
242
  if (sub.monthlyRemaining <= 0) {
189
243
  return die("Monthly repair allowance exhausted — it resets on your billing cycle.");
190
244
  }
191
- const r = await repairPath(file, { referencePath: opts.reference, password: opts.password });
245
+ const r = await repairPath(file, {
246
+ referencePath: opts.reference,
247
+ password: opts.password,
248
+ });
192
249
  printReport(r.family, r.report);
193
250
  if (!r.usable) {
194
251
  return die("Couldn't produce a usable result — nothing was charged.");
@@ -196,6 +253,8 @@ program
196
253
  const written = writeResult(file, opts.out, r);
197
254
  await meter();
198
255
  console.log(`✓ Wrote:\n${written.map((w) => ` ${w}`).join("\n")}`);
256
+ if (opts.report)
257
+ await writeReportPdf(file, r, opts.report, opts.company);
199
258
  }
200
259
  catch (e) {
201
260
  handle(e);
@@ -206,6 +265,8 @@ program
206
265
  .command("batch <files...>")
207
266
  .description("Repair up to 100 files (Business plan). Each is metered separately.")
208
267
  .option("-o, --outdir <dir>", "directory to write results into")
268
+ .option("--report-dir <dir>", "also write a white-label PDF repair report per file into this dir")
269
+ .option("--company <name>", "company name for the white-label report header")
209
270
  .action(async (files, opts) => {
210
271
  try {
211
272
  await requireBusiness();
@@ -234,6 +295,10 @@ program
234
295
  const written = writeResult(file, out, r);
235
296
  await meter();
236
297
  console.log(`✓ ${basename(file)} → ${written[0] ?? "(extracted)"}`);
298
+ if (opts.reportDir) {
299
+ const pdfPath = join(opts.reportDir, `${parse(file).name}.report.pdf`);
300
+ await writeReportPdf(file, r, pdfPath, opts.company);
301
+ }
237
302
  ok++;
238
303
  }
239
304
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intactfile/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "IntactFile CLI — repair corrupted files locally (video, ZIP/Office, PDF, JPEG, PNG, SQLite, RAR/7z). Your files never leave your machine. Business plan for repairs; diagnosis is free.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -15,7 +15,8 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "commander": "^12.1.0",
18
- "@intactfile/node-core": "0.1.0"
18
+ "@intactfile/node-core": "0.2.0",
19
+ "@intactfile/report": "0.1.0"
19
20
  },
20
21
  "devDependencies": {
21
22
  "@types/node": "^22.15.0",