79 lines
3.2 KiB
JavaScript
79 lines
3.2 KiB
JavaScript
#!/usr/bin/env node
|
|
import { join } from 'node:path'
|
|
import { USAGE } from '#import-recipes/cli/USAGE.js'
|
|
import { buildRecipeIndex } from '#import-recipes/recipes/buildRecipeIndex.js'
|
|
import { createAnthropicClient } from '#import-recipes/extraction/createAnthropicClient.js'
|
|
import { formatImportSummary } from '#import-recipes/cli/formatImportSummary.js'
|
|
import { importOdtFiles } from '#import-recipes/cli/importOdtFiles.js'
|
|
import { listOdtFileNames } from '#import-recipes/cli/listOdtFileNames.js'
|
|
import { parseCliArguments } from '#import-recipes/cli/parseCliArguments.js'
|
|
import { previewOdtFiles } from '#import-recipes/cli/previewOdtFiles.js'
|
|
import { verifyApiAccess } from '#import-recipes/extraction/verifyApiAccess.js'
|
|
import { writeJsonFile } from '#import-recipes/cli/writeJsonFile.js'
|
|
|
|
// Bootstrap tool, run once by hand: a folder of .odt recipes in, one JSON file
|
|
// per recipe plus the index the app reads out. Returns the exit code rather than
|
|
// calling process.exit, so nothing is cut off mid-write.
|
|
async function main() {
|
|
const { source, out, dryRun, limit, problems } = parseCliArguments(process.argv.slice(2))
|
|
if (problems.length > 0 || source === null) {
|
|
for (const problem of problems) console.error(problem)
|
|
if (source === null) console.error('--source is required')
|
|
console.error(`\n${USAGE}`)
|
|
return 1
|
|
}
|
|
let fileNames = []
|
|
try {
|
|
fileNames = listOdtFileNames(source)
|
|
} catch (error) {
|
|
console.error(`Cannot read ${source}: ${error.message}`)
|
|
return 1
|
|
}
|
|
if (fileNames.length === 0) {
|
|
console.error(`No .odt file in ${source}.`)
|
|
return 1
|
|
}
|
|
const selected = limit === null ? fileNames : fileNames.slice(0, limit)
|
|
console.log(`${selected.length} of ${fileNames.length} .odt files in ${source}\n`)
|
|
|
|
if (dryRun) {
|
|
const { failures } = previewOdtFiles({ sourceDir: source, fileNames: selected })
|
|
// A dry run imports nothing, so it gets its own wording rather than the
|
|
// summary of a real run.
|
|
console.log(`\n${selected.length - failures.length} of ${selected.length} files readable, ${failures.length} not.`)
|
|
for (const { fileName, reason } of failures) console.log(` ${fileName} - ${reason}`)
|
|
console.log('\nDry run: the model was not called, nothing was written.')
|
|
return failures.length === 0 ? 0 : 1
|
|
}
|
|
|
|
const creation = createAnthropicClient()
|
|
if (!creation.ok) {
|
|
console.error(creation.message)
|
|
return 1
|
|
}
|
|
const access = await verifyApiAccess(creation.client)
|
|
if (!access.ok) {
|
|
console.error(access.message)
|
|
return 1
|
|
}
|
|
|
|
const { recipes, failures, usage } = await importOdtFiles({
|
|
client: creation.client,
|
|
sourceDir: source,
|
|
outDir: out,
|
|
fileNames: selected
|
|
})
|
|
const indexPath = join(out, 'index.json')
|
|
try {
|
|
writeJsonFile(indexPath, buildRecipeIndex({ recipes, generatedAt: new Date().toISOString() }))
|
|
} catch (error) {
|
|
console.error(`Could not write ${indexPath}: ${error.message}`)
|
|
return 1
|
|
}
|
|
console.log(`\n${formatImportSummary({ importedCount: recipes.length, failures })}`)
|
|
console.log(`\nWritten: ${join(out, 'recipes')}/ and ${indexPath}`)
|
|
console.log(`Tokens: ${usage.input} in, ${usage.output} out.`)
|
|
return failures.length === 0 ? 0 : 1
|
|
}
|
|
|
|
process.exitCode = await main()
|