| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- import { Command } from "commander";
- import { access, readFile, writeFile, mkdir } from "node:fs/promises";
- import { join, basename, resolve } from "node:path";
- import { log, logError, GLOBAL_ENV_PATH } from "../utils.js";
- async function hasGlobalConfig(): Promise<boolean> {
- try {
- const content = await readFile(GLOBAL_ENV_PATH, "utf-8");
- return content.includes("INKOS_LLM_API_KEY=") && !content.includes("your-api-key-here");
- } catch {
- return false;
- }
- }
- export const initCommand = new Command("init")
- .description("Initialize an InkOS project (current directory by default)")
- .argument("[name]", "Project name (creates subdirectory). Omit to init current directory.")
- .option("--lang <language>", "Default writing language: zh (Chinese) or en (English)", "zh")
- .action(async (name: string | undefined, opts: { lang?: string }) => {
- const projectDir = name ? resolve(process.cwd(), name) : process.cwd();
- const projectName = basename(projectDir);
- try {
- await mkdir(projectDir, { recursive: true });
- // Check if inkos.json already exists
- const configPath = join(projectDir, "inkos.json");
- try {
- await access(configPath);
- throw new Error(`inkos.json already exists in ${projectDir}. Use a different directory or delete the existing project.`);
- } catch (e) {
- if (e instanceof Error && e.message.includes("already exists")) throw e;
- // File doesn't exist, good
- }
- await mkdir(join(projectDir, "books"), { recursive: true });
- await mkdir(join(projectDir, "radar"), { recursive: true });
- const config = {
- name: projectName,
- version: "0.1.0",
- language: opts.lang ?? "zh",
- llm: {
- provider: process.env.INKOS_LLM_PROVIDER ?? "openai",
- baseUrl: process.env.INKOS_LLM_BASE_URL ?? "",
- model: process.env.INKOS_LLM_MODEL ?? "",
- },
- notify: [],
- daemon: {
- schedule: {
- radarCron: "0 */6 * * *",
- writeCron: "*/15 * * * *",
- },
- maxConcurrentBooks: 3,
- },
- };
- await writeFile(
- join(projectDir, "inkos.json"),
- JSON.stringify(config, null, 2),
- "utf-8",
- );
- await Promise.all([
- writeFile(join(projectDir, ".nvmrc"), "22\n", "utf-8"),
- writeFile(join(projectDir, ".node-version"), "22\n", "utf-8"),
- ]);
- const global = await hasGlobalConfig();
- if (global) {
- await writeFile(
- join(projectDir, ".env"),
- [
- "# Project-level LLM overrides (optional)",
- "# Global config at ~/.inkos/.env will be used by default.",
- "# Uncomment below to override for this project only:",
- "# INKOS_LLM_PROVIDER=openai",
- "# INKOS_LLM_BASE_URL=",
- "# INKOS_LLM_API_KEY=",
- "# INKOS_LLM_MODEL=",
- "",
- "# Web search (optional):",
- "# TAVILY_API_KEY=tvly-xxxxx",
- ].join("\n"),
- "utf-8",
- );
- } else {
- await writeFile(
- join(projectDir, ".env"),
- [
- "# LLM Configuration",
- "# Tip: Run 'inkos config set-global' to set once for all projects.",
- "# Provider: openai (OpenAI / compatible proxy), anthropic (Anthropic native)",
- "INKOS_LLM_PROVIDER=openai",
- "INKOS_LLM_BASE_URL=",
- "INKOS_LLM_API_KEY=",
- "INKOS_LLM_MODEL=",
- "",
- "# Optional parameters (defaults shown):",
- "# INKOS_LLM_TEMPERATURE=0.7",
- "# INKOS_LLM_MAX_TOKENS=8192",
- "# INKOS_LLM_THINKING_BUDGET=0 # Anthropic extended thinking budget",
- "# INKOS_LLM_API_FORMAT=chat # chat (default) or responses (OpenAI Responses API)",
- "",
- "# Web search (optional, for auditor era-research):",
- "# TAVILY_API_KEY=tvly-xxxxx # Free at tavily.com (1000 searches/month)",
- "",
- "# Anthropic example:",
- "# INKOS_LLM_PROVIDER=anthropic",
- "# INKOS_LLM_PROVIDER=anthropic",
- "# INKOS_LLM_BASE_URL=",
- "# INKOS_LLM_MODEL=",
- ].join("\n"),
- "utf-8",
- );
- }
- await writeFile(
- join(projectDir, ".gitignore"),
- [".env", "node_modules/", ".DS_Store"].join("\n"),
- "utf-8",
- );
- log(`Project initialized at ${projectDir}`);
- log("");
- const isEnglish = (opts.lang ?? "zh") === "en";
- const exampleCreate = isEnglish
- ? " inkos book create --title 'My Novel' --genre progression --platform royalroad --lang en"
- : " inkos book create --title '我的小说' --genre xuanhuan --platform tomato";
- if (global) {
- log("Global LLM config detected. Ready to go!");
- log("");
- log("Next steps:");
- if (name) log(` cd ${name}`);
- log(exampleCreate);
- } else {
- log("Next steps:");
- if (name) log(` cd ${name}`);
- log(" # Option 1: Set global config (recommended, one-time):");
- log(" inkos config set-global --provider openai --base-url <your-api-url> --api-key <your-key> --model <your-model>");
- log(" # Option 2: Edit .env for this project only");
- log("");
- log(exampleCreate);
- }
- log(" inkos write next <book-id>");
- } catch (e) {
- logError(`Failed to initialize project: ${e}`);
- process.exit(1);
- }
- });
|