akari-video 0.1.94 → 0.1.95

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akari-video",
3
- "version": "0.1.94",
3
+ "version": "0.1.95",
4
4
  "description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -234,7 +234,7 @@ function isFiniteNumber(value) {
234
234
  const VALUE_OPTIONS = new Set([
235
235
  "--project", "--engine", "--reading-file", "--script-file",
236
236
  "--t", "--gain-db", "--id", "--speaker", "--profile",
237
- "--voice", "--style", "--speed", "--text", "--caption-ref", "--irodori-url",
237
+ "--voice", "--style", "--speed", "--text", "--caption-ref", "--irodori-url", "--out",
238
238
  ]);
239
239
  const FLAG_OPTIONS = new Set(["--dry-run", "--yes", "--apply", "--json"]);
240
240
 
@@ -252,7 +252,7 @@ function parseArguments(argv) {
252
252
  id: null,
253
253
  speaker: "3",
254
254
  profile: null,
255
- voice: null, style: null, speed: null, text: null, captionRef: null, irodoriUrl: null, json: false,
255
+ voice: null, style: null, speed: null, text: null, captionRef: null, irodoriUrl: null, out: null, json: false,
256
256
  dryRun: false,
257
257
  yes: false,
258
258
  apply: false,
@@ -281,6 +281,7 @@ function parseArguments(argv) {
281
281
  case "--text": options.text = value; break;
282
282
  case "--caption-ref": options.captionRef = value; break;
283
283
  case "--irodori-url": options.irodoriUrl = value; break;
284
+ case "--out": options.out = value; break;
284
285
  default: break;
285
286
  }
286
287
  } else if (argument === "--dry-run") {
@@ -336,6 +337,26 @@ function parseArguments(argv) {
336
337
  }
337
338
 
338
339
  options.project = path.resolve(options.project);
340
+ if (options.out !== null) {
341
+ const out = options.out.replace(/\\/gu, '/');
342
+ if (options.apply || options.id || !/^assets\/generated\/candidates\/[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*\.(?:wav|mp3)$/u.test(out)
343
+ || out.split('/').some(part => part === '..') || !out.endsWith(`.${extensionFor(options.engine)}`)) {
344
+ throw new PublicError('--out はプロジェクト内の未使用候補音声パスを指定してください', 2);
345
+ }
346
+ const directory = path.dirname(path.join(options.project, out));
347
+ const root = fs.realpathSync(options.project);
348
+ for (let parent = path.dirname(directory); parent !== options.project && parent.startsWith(`${options.project}${path.sep}`); parent = path.dirname(parent)) {
349
+ if (fs.existsSync(parent) && !fs.realpathSync(parent).startsWith(`${root}${path.sep}`)) {
350
+ throw new PublicError('候補の出力先がプロジェクト外です', 2);
351
+ }
352
+ }
353
+ fs.mkdirSync(directory, { recursive: true });
354
+ if (!fs.realpathSync(directory).startsWith(`${root}${path.sep}`)
355
+ || fs.existsSync(path.join(options.project, out)) || fs.existsSync(path.join(options.project, `${out}.meta.json`))) {
356
+ throw new PublicError('候補の出力先がプロジェクト外、または既存ファイルです', 2);
357
+ }
358
+ options.out = out;
359
+ }
339
360
  return options;
340
361
  }
341
362
 
@@ -1008,8 +1029,8 @@ async function runGenerate(options, io) {
1008
1029
  return 0;
1009
1030
  }
1010
1031
 
1011
- const id = options.id ?? computeNextId(options.project);
1012
- const relativePath = relativeOutputPath(id, options.engine);
1032
+ const id = options.out ? null : options.id ?? computeNextId(options.project);
1033
+ const relativePath = options.out ?? relativeOutputPath(id, options.engine);
1013
1034
  const outputPath = path.join(options.project, relativePath);
1014
1035
 
1015
1036
  let audioBuffer;
@@ -1107,7 +1128,8 @@ async function runGenerate(options, io) {
1107
1128
  }
1108
1129
 
1109
1130
  fs.mkdirSync(path.dirname(outputPath), { recursive: true });
1110
- fs.writeFileSync(outputPath, audioBuffer);
1131
+ if (options.out) fs.writeFileSync(outputPath, audioBuffer, { flag: 'wx' });
1132
+ else fs.writeFileSync(outputPath, audioBuffer);
1111
1133
  const duration = durationForAudio(audioBuffer, outputPath, options.engine, warnings);
1112
1134
 
1113
1135
  const entry = {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akari-video",
3
- "version": "0.1.94",
3
+ "version": "0.1.95",
4
4
  "description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。 [akari-video npm vendor: bin/akari.mjs is reference-only. These CLI entrypoints are not included in the akari-video npm package. Use `akari doctor --json` and run the path reported in `render_cut.path`. Full installations provide it in a monorepo checkout, ~/.akari/app, /Applications/AKARI Video.app/Contents/Resources/packages, or %LOCALAPPDATA%\\Programs\\@akari-videoshell\\resources\\packages.]",
5
5
  "type": "module",
6
6
  "files": [