akari-video 0.1.23 → 0.1.25
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/README.md +14 -0
- package/bin/akari.mjs +7 -3
- package/package.json +1 -1
- package/src/chrome-command.mjs +100 -0
- package/src/cli.mjs +22 -5
- package/src/doctor-command.mjs +65 -0
- package/src/messages.mjs +35 -10
- package/src/path-lookup.mjs +10 -20
- package/src/runtime-diagnostics.mjs +323 -0
- package/src/status-command.mjs +5 -1
- package/src/update-check.mjs +1 -1
- package/vendor/.akari-capability-sources.json +12 -2
- package/vendor/docs/contract-2026-07-20-edit-json-v1-narration.md +12 -0
- package/vendor/docs/contract-2026-07-22-render-basics.md +25 -1
- package/vendor/docs/contract-2026-07-23-analysis-person-matte.md +40 -0
- package/vendor/docs/contract-2026-07-25-r6-audio-tracks-and-trim.md +8 -0
- package/vendor/docs/contract-2026-08-02-preview-parity.md +252 -619
- package/vendor/docs/contract-2026-08-03-caption-display-encoding-qc-v1.md +2 -2
- package/vendor/docs/contract-2026-08-12-still-image-cut-source-v0.md +4 -9
- package/vendor/docs/contract-2026-08-18-v1-render-parity.md +4 -0
- package/vendor/docs/contract-2026-08-28-gpu-export-v0.md +133 -0
- package/vendor/docs/contract-2026-08-28-osr-export-v0.md +158 -0
- package/vendor/docs/contract-2026-08-28-v2-approximation-ledger.md +46 -0
- package/vendor/docs/contract-2026-08-28-v2-audio-roles-v0.md +123 -0
- package/vendor/packages/akari-launcher/README.md +14 -0
- package/vendor/packages/akari-launcher/package.json +3 -3
- package/vendor/packages/analysis-report/render-analysis-report.mjs +50 -6
- package/vendor/packages/analysis-report/template.html +11 -1
- package/vendor/packages/bake-layer/package.json +2 -2
- package/vendor/packages/chat-bridge/package.json +2 -2
- package/vendor/packages/decision-cards/package.json +2 -2
- package/vendor/packages/decision-log-report/README.md +50 -0
- package/vendor/packages/decision-log-report/package.json +13 -0
- package/vendor/packages/decision-log-report/render-decision-log-report.mjs +484 -0
- package/vendor/packages/decision-log-report/template.html +471 -0
- package/vendor/packages/edit-lint/README.md +44 -0
- package/vendor/packages/edit-lint/src/edit-lint.mjs +364 -25
- package/vendor/packages/edit-store/lib/audio-schedule.d.ts +62 -0
- package/vendor/packages/edit-store/lib/audio-schedule.js +284 -0
- package/vendor/packages/edit-store/lib/ducking.d.ts +25 -0
- package/vendor/packages/edit-store/lib/ducking.js +31 -0
- package/vendor/packages/edit-store/lib/edit-store.d.ts +3 -0
- package/vendor/packages/edit-store/lib/edit-v2.d.ts +5 -2
- package/vendor/packages/edit-store/lib/edit-v2.js +8 -1
- package/vendor/packages/edit-store/lib/index.d.ts +2 -0
- package/vendor/packages/edit-store/lib/index.js +2 -0
- package/vendor/packages/edit-store/lib/internal-model.js +18 -4
- package/vendor/packages/edit-store/lib/migrate/index.js +37 -9
- package/vendor/packages/edit-store/lib/migrate/legacy-parse.js +4 -0
- package/vendor/packages/edit-store/lib/webview-kernel.d.ts +2 -0
- package/vendor/packages/edit-store/lib/webview-kernel.js +304 -0
- package/vendor/packages/export-nle/package.json +2 -2
- package/vendor/packages/frame-engine/README.ja.md +24 -3
- package/vendor/packages/frame-engine/README.md +24 -3
- package/vendor/packages/frame-engine/package.json +9 -1
- package/vendor/packages/gpu-export/README.ja.md +30 -0
- package/vendor/packages/gpu-export/README.md +30 -0
- package/vendor/packages/gpu-export/package.json +30 -0
- package/vendor/packages/intake-form/package.json +2 -2
- package/vendor/packages/media-bin/src/alpha-intake.mjs +272 -0
- package/vendor/packages/media-bin/test/alpha-intake.test.mjs +114 -0
- package/vendor/packages/osr-export/README.md +73 -0
- package/vendor/packages/osr-export/package.json +29 -0
- package/vendor/packages/preview-server/package.json +4 -3
- package/vendor/packages/render-cut/package.json +2 -2
- package/vendor/packages/schemas/analysis.schema.json +11 -1
- package/vendor/packages/schemas/bin/validate-edit.mjs +2 -2
- package/vendor/packages/schemas/edit.schema.json +9 -4
- package/vendor/packages/schemas/examples/edit-narration-trim-valid/edit.json +33 -0
- package/vendor/packages/schemas/test/edit-narration-trim.test.mjs +30 -0
- package/vendor/packages/schemas/test/validate-edit.test.mjs +9 -0
- package/vendor/packages/template-render/package.json +2 -2
- package/vendor/skills/analyze-footage/bin/person-matte/mask-format.mjs +25 -0
- package/vendor/skills/analyze-footage/bin/person-matte/mask-from-alpha.mjs +253 -0
- package/vendor/skills/analyze-footage/bin/person-matte/mask-roundtrip.mjs +206 -0
- package/vendor/skills/analyze-footage/bin/person-matte/person-cutout.mjs +3 -0
- package/vendor/skills/analyze-footage/bin/person-matte/person-matte.mjs +119 -4
- package/vendor/skills/analyze-footage/person-matte.md +20 -0
- package/vendor/skills/analyze-footage/test/person-matte-mask.test.mjs +85 -0
- package/vendor/skills/analyze-project/decision-record.md +3 -0
- package/vendor/skills/edit-lint/SKILL.md +26 -5
- package/vendor/skills/edit-plan/SKILL.md +3 -1
- package/vendor/skills/edit-plan/approvals-and-generation.md +2 -1
- package/vendor/skills/edit-plan/execution.md +1 -0
- package/vendor/skills/edit-plan/report-guide.md +8 -0
- package/vendor/skills/edit-plan/workflow.md +3 -0
- package/vendor/skills/render-cut/SKILL.md +26 -4
- package/vendor/templates/project-default/.claude/skills/README.md +9 -7
- package/vendor/templates/project-default/AGENTS.md +23 -13
- package/vendor/templates/project-default/CLAUDE.md +27 -3
- package/vendor/packages/preview-engine/README.md +0 -271
- package/vendor/packages/preview-engine/package.json +0 -25
package/README.md
CHANGED
|
@@ -52,6 +52,20 @@ DL・sha256 検証・適用まで実行。それ以外(npm グローバル / g
|
|
|
52
52
|
`packages/asset-resolver` の CLI への薄い委譲で、カタログ合成・entitlements 判定・
|
|
53
53
|
sha256 検証・fail-closed は resolver 側の責務のまま。`src/assets-command.mjs`)。
|
|
54
54
|
|
|
55
|
+
### Chrome の導入
|
|
56
|
+
|
|
57
|
+
動画の書き出しに使う Chrome for Testing は、次のコマンドで導入できる。
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
akari chrome install
|
|
61
|
+
akari chrome install --cache-dir /path/to/cache
|
|
62
|
+
akari chrome install --build-id 150.0.7339.16
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
保存先の既定は `~/.cache/puppeteer`。`--cache-dir` で保存先、`--build-id` で導入する
|
|
66
|
+
buildId を指定できる。実行環境から `@puppeteer/browsers` を解決できる必要があり、
|
|
67
|
+
AKARI Video アプリの配布物には必要なモジュールが同梱されている。
|
|
68
|
+
|
|
55
69
|
`akari` に渡した引数はそのまま `opencode` に転送する(例: `akari --continue` は
|
|
56
70
|
`opencode --continue` を起動する)。
|
|
57
71
|
|
package/bin/akari.mjs
CHANGED
|
@@ -11,6 +11,8 @@ import { runCapabilityCommand } from '../src/capability-command.mjs';
|
|
|
11
11
|
import { runStoreCommand } from '../src/store-command.mjs';
|
|
12
12
|
import { runAssetsCommand } from '../src/assets-command.mjs';
|
|
13
13
|
import { runMigrateCommand } from '../src/migrate-command.mjs';
|
|
14
|
+
import { runDoctorCommand } from '../src/doctor-command.mjs';
|
|
15
|
+
import { resolveRuntimePaths } from '../src/runtime-diagnostics.mjs';
|
|
14
16
|
import { maybeApplyPendingUpdateOnLaunch, resolveInstalledVersionInfo } from '../src/update-check.mjs';
|
|
15
17
|
import { describeCliHelp, describeInstalledVersions } from '../src/messages.mjs';
|
|
16
18
|
|
|
@@ -21,7 +23,7 @@ async function printVersion() {
|
|
|
21
23
|
const versionInfo = resolveInstalledVersionInfo({ env: process.env });
|
|
22
24
|
// 1 行目は update / rollback の既存機械観測契約として CLI 版だけを維持する。
|
|
23
25
|
console.log(`v${versionInfo.cliVersion}`);
|
|
24
|
-
for (const line of describeInstalledVersions(versionInfo)) {
|
|
26
|
+
for (const line of describeInstalledVersions(versionInfo, resolveRuntimePaths({ env: process.env }))) {
|
|
25
27
|
console.log(line);
|
|
26
28
|
}
|
|
27
29
|
return { exitCode: 0 };
|
|
@@ -47,13 +49,14 @@ async function printCliHelp() {
|
|
|
47
49
|
// 読み直せば新版の内容を観測できる — 同一 argv での re-exec はしない設計判断
|
|
48
50
|
// (理由は report.md 参照))。失敗は他の起動時副作用と同じく握りつぶし、
|
|
49
51
|
// claude/opencode 起動やサブコマンド実行を止めない。
|
|
52
|
+
const argv = process.argv.slice(2);
|
|
53
|
+
const doctorJson = argv[0] === 'doctor' && argv.includes('--json');
|
|
50
54
|
try {
|
|
51
|
-
maybeApplyPendingUpdateOnLaunch({ env: process.env, log: (line) => console.log(line) });
|
|
55
|
+
maybeApplyPendingUpdateOnLaunch({ env: process.env, log: doctorJson ? () => {} : (line) => console.log(line) });
|
|
52
56
|
} catch (error) {
|
|
53
57
|
console.error(`自動更新の適用確認でエラーが発生しました(続行します): ${error instanceof Error ? error.message : String(error)}`);
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
const argv = process.argv.slice(2);
|
|
57
60
|
// `akari update` / `akari init` / `akari new` / `akari narration` / `akari internal` /
|
|
58
61
|
// `akari sounds` / `akari status` / `akari accept` / `akari capability` / `akari store` /
|
|
59
62
|
// `akari assets` は claude へ転送せず、専用のサブコマンドとして扱う(契約 §4-1 /
|
|
@@ -62,6 +65,7 @@ const argv = process.argv.slice(2);
|
|
|
62
65
|
// それ以外の引数はすべて従来どおり claude へ転送する。
|
|
63
66
|
const invoke = (argv[0] === '--version' || argv[0] === '-v') ? printVersion()
|
|
64
67
|
: (argv[0] === '--help' || argv[0] === '-h') ? printCliHelp()
|
|
68
|
+
: argv[0] === 'doctor' ? runDoctorCommand(argv.slice(1))
|
|
65
69
|
: argv[0] === 'update' ? runUpdateCommand(argv.slice(1))
|
|
66
70
|
: argv[0] === 'init' ? runInitCommand(argv.slice(1))
|
|
67
71
|
: argv[0] === 'new' ? runNewCommand(argv.slice(1))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
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": {
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
const usage = [
|
|
5
|
+
'使い方: akari chrome install [options]',
|
|
6
|
+
'',
|
|
7
|
+
'オプション:',
|
|
8
|
+
' --cache-dir <dir> Chrome の保存先(既定: ~/.cache/puppeteer)',
|
|
9
|
+
' --build-id <id> 導入する Chrome の buildId(既定: stable の最新版)',
|
|
10
|
+
' -h, --help このヘルプを表示',
|
|
11
|
+
].join('\n');
|
|
12
|
+
|
|
13
|
+
class PublicError extends Error {
|
|
14
|
+
constructor(message, exitCode = 1) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.exitCode = exitCode;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function resolveChromeCacheDir(homeDirectory = homedir(), cacheDir) {
|
|
21
|
+
return cacheDir ? resolve(cacheDir) : join(homeDirectory, '.cache', 'puppeteer');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseInstallArguments(argv, homeDirectory) {
|
|
25
|
+
const parsed = {
|
|
26
|
+
cacheDir: resolveChromeCacheDir(homeDirectory),
|
|
27
|
+
buildId: undefined,
|
|
28
|
+
};
|
|
29
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
30
|
+
const argument = argv[index];
|
|
31
|
+
if (argument !== '--cache-dir' && argument !== '--build-id') {
|
|
32
|
+
throw new PublicError(`不明な引数です: ${argument}`, 2);
|
|
33
|
+
}
|
|
34
|
+
const value = argv[index + 1];
|
|
35
|
+
if (value === undefined || value.startsWith('--')) {
|
|
36
|
+
throw new PublicError(`${argument} の値がありません`, 2);
|
|
37
|
+
}
|
|
38
|
+
index += 1;
|
|
39
|
+
if (argument === '--cache-dir') parsed.cacheDir = resolveChromeCacheDir(homeDirectory, value);
|
|
40
|
+
else parsed.buildId = value;
|
|
41
|
+
}
|
|
42
|
+
return parsed;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function runChromeCommand(argv, options = {}) {
|
|
46
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
47
|
+
const error = options.error ?? ((line) => console.error(line));
|
|
48
|
+
const loadBrowsers = options.loadBrowsers ?? (() => import('@puppeteer/browsers'));
|
|
49
|
+
const homeDirectory = options.homeDirectory ?? homedir();
|
|
50
|
+
|
|
51
|
+
if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h'
|
|
52
|
+
|| (argv[0] === 'install' && (argv.includes('--help') || argv.includes('-h')))) {
|
|
53
|
+
log(usage);
|
|
54
|
+
return { exitCode: 0 };
|
|
55
|
+
}
|
|
56
|
+
if (argv[0] !== 'install') {
|
|
57
|
+
error(`不明なサブコマンドです: ${argv[0]}\n${usage}`);
|
|
58
|
+
return { exitCode: 2 };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let parsed;
|
|
62
|
+
try {
|
|
63
|
+
parsed = parseInstallArguments(argv, homeDirectory);
|
|
64
|
+
} catch (cause) {
|
|
65
|
+
error(`${cause instanceof Error ? cause.message : String(cause)}\n${usage}`);
|
|
66
|
+
return { exitCode: cause instanceof PublicError ? cause.exitCode : 1 };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let browsers;
|
|
70
|
+
try {
|
|
71
|
+
browsers = await loadBrowsers();
|
|
72
|
+
} catch (cause) {
|
|
73
|
+
error([
|
|
74
|
+
`@puppeteer/browsers を読み込めないため Chrome を導入できません: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
75
|
+
'システムの node がある場合は `npx puppeteer browsers install chrome` でも導入できます。',
|
|
76
|
+
].join('\n'));
|
|
77
|
+
return { exitCode: 1 };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const platform = browsers.detectBrowserPlatform();
|
|
82
|
+
const buildId = parsed.buildId
|
|
83
|
+
?? await browsers.resolveBuildId('chrome', platform, 'stable');
|
|
84
|
+
const installed = await browsers.install({
|
|
85
|
+
browser: 'chrome',
|
|
86
|
+
buildId,
|
|
87
|
+
cacheDir: parsed.cacheDir,
|
|
88
|
+
downloadProgressCallback: 'default',
|
|
89
|
+
});
|
|
90
|
+
if (typeof installed?.executablePath !== 'string' || installed.executablePath.length === 0) {
|
|
91
|
+
throw new Error('導入後の実行ファイルを特定できませんでした。');
|
|
92
|
+
}
|
|
93
|
+
log(`buildId: ${buildId}`);
|
|
94
|
+
log(`実行ファイル: ${resolve(installed.executablePath)}`);
|
|
95
|
+
return { exitCode: 0 };
|
|
96
|
+
} catch (cause) {
|
|
97
|
+
error(`Chrome の導入に失敗しました: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
98
|
+
return { exitCode: 1 };
|
|
99
|
+
}
|
|
100
|
+
}
|
package/src/cli.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
4
4
|
|
|
5
5
|
import { resolveLauncherAssets } from './repo-assets.mjs';
|
|
6
6
|
import { detectProjectState } from './project-state.mjs';
|
|
7
|
-
import { findClaudeExecutable, findOpencodeExecutable } from './path-lookup.mjs';
|
|
7
|
+
import { findClaudeExecutable, findExecutable, findOpencodeExecutable } from './path-lookup.mjs';
|
|
8
8
|
import { loadTaskLabels } from './task-labels.mjs';
|
|
9
9
|
import { describeForceReinstall, describeInstalledVersions, describeIntake, claudeMissingGuidance, opencodeMissingGuidance, describeUpdateCacheFallback, describeUpdateCommand, describeVersionStatus, formatUpdateNotice } from './messages.mjs';
|
|
10
10
|
import { resolveEffectiveProjectRoot } from './first-run.mjs';
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
triggerBackgroundRefresh
|
|
23
23
|
} from './update-check.mjs';
|
|
24
24
|
import { applySelfUpdate, isRunningFromAppDir, rollbackSelfUpdate } from './self-update.mjs';
|
|
25
|
+
import { runChromeCommand } from './chrome-command.mjs';
|
|
26
|
+
import { resolveRuntimePaths } from './runtime-diagnostics.mjs';
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* `akari` ランチャーの本体。3 入口契約(ターミナル `akari` / セッション内 `/akari` /
|
|
@@ -33,6 +35,8 @@ import { applySelfUpdate, isRunningFromAppDir, rollbackSelfUpdate } from './self
|
|
|
33
35
|
* options 経由で差し替え可能にしてあり、node --test から実プロセスを起動せずに分岐を検証できる。
|
|
34
36
|
*/
|
|
35
37
|
export async function run(args, options = {}) {
|
|
38
|
+
if (args[0] === 'chrome') return runChromeCommand(args.slice(1), options);
|
|
39
|
+
|
|
36
40
|
const log = options.log ?? ((line) => console.log(line));
|
|
37
41
|
const assets = options.assets ?? resolveLauncherAssets();
|
|
38
42
|
const scaffold = options.scaffold ?? defaultScaffold;
|
|
@@ -95,7 +99,8 @@ export async function run(args, options = {}) {
|
|
|
95
99
|
} catch (error) {
|
|
96
100
|
log(`接続確認でエラーが発生しました(続行します): ${error instanceof Error ? error.message : String(error)}`);
|
|
97
101
|
}
|
|
98
|
-
|
|
102
|
+
const runtimeDiagnostics = options.runtimeDiagnostics ?? resolveRuntimePaths({ ...options, env, platform });
|
|
103
|
+
log(describeVersionStatus(versionInfo, readCacheSync(resolveCachePath(env)), runtimeDiagnostics));
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
// 新版通知(契約 §4-1): キャッシュの読み比較のみ・ネットワークには一切触れない
|
|
@@ -201,6 +206,11 @@ export async function runUpdateCommand(args, options = {}) {
|
|
|
201
206
|
const dismissRequested = args.includes('--dismiss');
|
|
202
207
|
const rollbackRequested = args.includes('--rollback');
|
|
203
208
|
const forceRequested = args.includes('--force');
|
|
209
|
+
const runtimeDiagnostics = options.runtimeDiagnostics ?? resolveRuntimePaths({ ...options, env });
|
|
210
|
+
const pathEnv = Object.hasOwn(env, 'PATH') ? (env.PATH ?? '') : (process.env.PATH ?? '');
|
|
211
|
+
const npmAvailable = options.npmAvailable
|
|
212
|
+
?? !!(options.findExecutable ?? findExecutable)('npm', pathEnv, options.platform ?? process.platform, env.PATHEXT);
|
|
213
|
+
const describeOptions = { runtimeDiagnostics, npmAvailable };
|
|
204
214
|
let usingCachedFeed = false;
|
|
205
215
|
|
|
206
216
|
if (rollbackRequested) {
|
|
@@ -214,7 +224,7 @@ export async function runUpdateCommand(args, options = {}) {
|
|
|
214
224
|
dismissed = true;
|
|
215
225
|
}
|
|
216
226
|
const finalCache = dismissed ? readCacheSync(cachePath) : cache;
|
|
217
|
-
for (const line of describeUpdateCommand({ currentVersion, versionInfo, cache: finalCache, dismissed })) {
|
|
227
|
+
for (const line of describeUpdateCommand({ currentVersion, versionInfo, cache: finalCache, dismissed, ...describeOptions })) {
|
|
218
228
|
log(line);
|
|
219
229
|
}
|
|
220
230
|
return { exitCode: 0 };
|
|
@@ -238,16 +248,22 @@ export async function runUpdateCommand(args, options = {}) {
|
|
|
238
248
|
&& (hasManagedApp || (options.isRunningFromAppDir ?? isRunningFromAppDir)({ env, launcherRoot: options.launcherRoot }));
|
|
239
249
|
|
|
240
250
|
if (!selfUpdateEligible) {
|
|
241
|
-
for (const line of describeUpdateCommand({ currentVersion, versionInfo, cache, dismissed: false, usingCachedFeed })) {
|
|
251
|
+
for (const line of describeUpdateCommand({ currentVersion, versionInfo, cache, dismissed: false, usingCachedFeed, ...describeOptions })) {
|
|
242
252
|
log(line);
|
|
243
253
|
}
|
|
254
|
+
if (forceRequested
|
|
255
|
+
&& versionInfo.installRefStatus === 'missing'
|
|
256
|
+
&& runtimeDiagnostics.render_cut.origin !== 'monorepo') {
|
|
257
|
+
log('この CLI からは install.sh 経路の本体を入れ直せません。');
|
|
258
|
+
log('導入するには `curl -fsSL https://raw.githubusercontent.com/AkariLabs/akari-video/main/install.sh | bash` を実行してください(デスクトップ版だけで使う場合は不要です)。');
|
|
259
|
+
}
|
|
244
260
|
return { exitCode: 0 };
|
|
245
261
|
}
|
|
246
262
|
|
|
247
263
|
if (usingCachedFeed) {
|
|
248
264
|
log(describeUpdateCacheFallback(cache));
|
|
249
265
|
}
|
|
250
|
-
for (const line of describeInstalledVersions(versionInfo)) {
|
|
266
|
+
for (const line of describeInstalledVersions(versionInfo, runtimeDiagnostics)) {
|
|
251
267
|
log(line);
|
|
252
268
|
}
|
|
253
269
|
log(`最新バージョン: v${feed.product}`);
|
|
@@ -265,6 +281,7 @@ export async function runUpdateCommand(args, options = {}) {
|
|
|
265
281
|
}
|
|
266
282
|
|
|
267
283
|
function resolveCommandVersionInfo(options, env) {
|
|
284
|
+
if (options.versionInfo) return options.versionInfo;
|
|
268
285
|
// 既存テスト/埋め込み利用の currentVersion 注入は CLI 版注入としても扱う。
|
|
269
286
|
const cliVersion = options.cliVersion ?? options.currentVersion ?? readOwnVersion();
|
|
270
287
|
if (options.currentVersion !== undefined) {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { doctorExitCode, resolveDoctorReport } from './runtime-diagnostics.mjs';
|
|
2
|
+
|
|
3
|
+
const usage = [
|
|
4
|
+
'使い方: akari doctor [--json]',
|
|
5
|
+
'',
|
|
6
|
+
'必須部品の実在、解決元、PATH を確認します。',
|
|
7
|
+
' --json 機械可読な診断結果を 1 オブジェクトで出力',
|
|
8
|
+
].join('\n');
|
|
9
|
+
|
|
10
|
+
export async function runDoctorCommand(argv, options = {}) {
|
|
11
|
+
const log = options.log ?? ((line) => process.stdout.write(`${line}\n`));
|
|
12
|
+
const error = options.error ?? ((line) => process.stderr.write(`${line}\n`));
|
|
13
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
14
|
+
log(usage);
|
|
15
|
+
return { exitCode: 0 };
|
|
16
|
+
}
|
|
17
|
+
const unknown = argv.find((argument) => argument !== '--json');
|
|
18
|
+
if (unknown) {
|
|
19
|
+
error(`不明な doctor オプションです: ${unknown}\n${usage}`);
|
|
20
|
+
return { exitCode: 2 };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const report = options.report ?? await (options.resolveReport ?? resolveDoctorReport)(options);
|
|
24
|
+
if (argv.includes('--json')) log(JSON.stringify(report));
|
|
25
|
+
else log(formatDoctorReport(report));
|
|
26
|
+
return { exitCode: doctorExitCode(report.verdict), report };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function formatDoctorReport(report) {
|
|
30
|
+
const rows = [
|
|
31
|
+
['cli', 'ok', `v${report.cli.version} — ${report.cli.entry_path}`],
|
|
32
|
+
['app_managed', report.app_managed.status, detail(report.app_managed.path, report.app_managed.version)],
|
|
33
|
+
['app_bundle', report.app_bundle.found ? 'found' : 'missing', detail(report.app_bundle.path, report.app_bundle.version)],
|
|
34
|
+
['render_cut', report.render_cut.origin, report.render_cut.path ?? '見つかりません'],
|
|
35
|
+
['edit_lint', report.edit_lint.origin, report.edit_lint.path ?? '見つかりません'],
|
|
36
|
+
['ffmpeg', report.ffmpeg.origin, report.ffmpeg.path ?? '見つかりません'],
|
|
37
|
+
['ffprobe', report.ffprobe.origin, report.ffprobe.path ?? '見つかりません'],
|
|
38
|
+
['chrome', report.chrome.found ? 'found' : 'missing', report.chrome.path ?? report.chrome.cache_dir],
|
|
39
|
+
['gpu_export', report.gpu_export?.available ? 'ok' : 'unavailable', report.gpu_export?.reason ?? '診断情報がありません'],
|
|
40
|
+
['path', report.path.on_path ? 'ok' : 'missing', report.path.cli_shim_dir],
|
|
41
|
+
];
|
|
42
|
+
const widths = [
|
|
43
|
+
Math.max('項目'.length, ...rows.map((row) => row[0].length)),
|
|
44
|
+
Math.max('状態'.length, ...rows.map((row) => row[1].length)),
|
|
45
|
+
];
|
|
46
|
+
const lines = [
|
|
47
|
+
'AKARI Video doctor',
|
|
48
|
+
'',
|
|
49
|
+
`${'項目'.padEnd(widths[0])} ${'状態'.padEnd(widths[1])} 詳細`,
|
|
50
|
+
`${'-'.repeat(widths[0])} ${'-'.repeat(widths[1])} ${'-'.repeat(20)}`,
|
|
51
|
+
...rows.map((row) => `${row[0].padEnd(widths[0])} ${row[1].padEnd(widths[1])} ${row[2]}`),
|
|
52
|
+
'',
|
|
53
|
+
`判定: ${report.verdict}`,
|
|
54
|
+
];
|
|
55
|
+
if (report.next_steps.length > 0) {
|
|
56
|
+
lines.push('次の手順:');
|
|
57
|
+
lines.push(...report.next_steps.map((step) => ` - ${step}`));
|
|
58
|
+
}
|
|
59
|
+
return lines.join('\n');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function detail(path, version) {
|
|
63
|
+
const location = path ?? '見つかりません';
|
|
64
|
+
return version ? `v${version} — ${location}` : location;
|
|
65
|
+
}
|
package/src/messages.mjs
CHANGED
|
@@ -71,7 +71,7 @@ export function formatUpdateNotice(status) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/** `akari doctor` 系出力に足す 1 行(現在版 + フィード取得状態)。 */
|
|
74
|
-
export function describeVersionStatus(versionOrInfo, cache) {
|
|
74
|
+
export function describeVersionStatus(versionOrInfo, cache, runtimeDiagnostics) {
|
|
75
75
|
if (typeof versionOrInfo === 'string') {
|
|
76
76
|
if (!cache?.feed) {
|
|
77
77
|
return `バージョン: v${versionOrInfo}(更新フィード: 未取得)`;
|
|
@@ -80,7 +80,7 @@ export function describeVersionStatus(versionOrInfo, cache) {
|
|
|
80
80
|
return `バージョン: v${versionOrInfo}(更新フィード: 取得済み・${fetchedAt} 時点)`;
|
|
81
81
|
}
|
|
82
82
|
const info = normalizeVersionInfo(versionOrInfo);
|
|
83
|
-
const installed = describeInstalledVersions(info).join(' / ');
|
|
83
|
+
const installed = describeInstalledVersions(info, runtimeDiagnostics).join(' / ');
|
|
84
84
|
if (!cache?.feed) {
|
|
85
85
|
return `${installed}(更新フィード: 未取得)`;
|
|
86
86
|
}
|
|
@@ -88,7 +88,7 @@ export function describeVersionStatus(versionOrInfo, cache) {
|
|
|
88
88
|
return `${installed}(更新フィード: 取得済み・${fetchedAt} 時点)`;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export function describeInstalledVersions(versionOrInfo) {
|
|
91
|
+
export function describeInstalledVersions(versionOrInfo, runtimeDiagnostics) {
|
|
92
92
|
const info = normalizeVersionInfo(versionOrInfo);
|
|
93
93
|
if (info.installRefNeedsRepair) {
|
|
94
94
|
const installRefPath = info.installRefPath ?? '~/.akari/app/.akari-install-ref';
|
|
@@ -99,6 +99,21 @@ export function describeInstalledVersions(versionOrInfo) {
|
|
|
99
99
|
];
|
|
100
100
|
}
|
|
101
101
|
if (!info.appVersion) {
|
|
102
|
+
if (info.installRefStatus === 'missing' && runtimeDiagnostics?.render_cut) {
|
|
103
|
+
const renderCut = runtimeDiagnostics.render_cut;
|
|
104
|
+
const availability = renderCut.origin === 'none'
|
|
105
|
+
? 'render-cut が見つからないため書き出しできません'
|
|
106
|
+
: `書き出しは ${humanRenderOrigin(renderCut.origin)} の render-cut を使います`;
|
|
107
|
+
const lines = [
|
|
108
|
+
`現在のバージョン: v${info.currentVersion}`,
|
|
109
|
+
`CLI バージョン: v${info.cliVersion}`,
|
|
110
|
+
`install.sh 経路の本体は未導入(${availability}。詳細: \`akari doctor\`)`,
|
|
111
|
+
];
|
|
112
|
+
if (renderCut.origin === 'none') {
|
|
113
|
+
lines.push('復旧するにはデスクトップ版を導入するか、install.sh 経路の本体を導入してください。');
|
|
114
|
+
}
|
|
115
|
+
return lines;
|
|
116
|
+
}
|
|
102
117
|
return [
|
|
103
118
|
`現在のバージョン: v${info.currentVersion}`,
|
|
104
119
|
`CLI バージョン: v${info.cliVersion}`,
|
|
@@ -112,11 +127,15 @@ export function describeInstalledVersions(versionOrInfo) {
|
|
|
112
127
|
return lines;
|
|
113
128
|
}
|
|
114
129
|
|
|
130
|
+
function humanRenderOrigin(origin) {
|
|
131
|
+
return origin === 'monorepo' ? '開発リポジトリ' : origin;
|
|
132
|
+
}
|
|
133
|
+
|
|
115
134
|
export function describeForceReinstall(versionOrInfo, targetVersion) {
|
|
116
135
|
const info = normalizeVersionInfo(versionOrInfo);
|
|
117
136
|
return info.installRefNeedsRepair
|
|
118
|
-
? `--force:
|
|
119
|
-
: `--force:
|
|
137
|
+
? `--force: 版を判定できない install.sh 経路の本体 → v${targetVersion} を入れ直します。`
|
|
138
|
+
: `--force: install.sh 経路の本体 v${info.currentVersion} → v${targetVersion} を入れ直します。`;
|
|
120
139
|
}
|
|
121
140
|
|
|
122
141
|
function normalizeVersionInfo(value) {
|
|
@@ -172,9 +191,9 @@ export function creatorRootPromptText(defaultPath) {
|
|
|
172
191
|
* `akari update` の出力本文(複数行)。フィード未取得・最新・新版ありで案内が変わる。
|
|
173
192
|
* `dismissed` は今回の実行で dismiss 記録を書いたかどうか(表示文言の切り替えのみに使う)。
|
|
174
193
|
*/
|
|
175
|
-
export function describeUpdateCommand({ currentVersion, versionInfo, cache, dismissed, usingCachedFeed = false }) {
|
|
194
|
+
export function describeUpdateCommand({ currentVersion, versionInfo, cache, dismissed, usingCachedFeed = false, runtimeDiagnostics, npmAvailable = true }) {
|
|
176
195
|
const info = versionInfo ?? normalizeVersionInfo(currentVersion);
|
|
177
|
-
const lines = versionInfo ? describeInstalledVersions(info) : [`現在のバージョン: v${currentVersion}`];
|
|
196
|
+
const lines = versionInfo ? describeInstalledVersions(info, runtimeDiagnostics) : [`現在のバージョン: v${currentVersion}`];
|
|
178
197
|
const feed = cache?.feed;
|
|
179
198
|
if (!feed) {
|
|
180
199
|
lines.push('最新情報をまだ取得できていません(オフライン、または初回起動直後の可能性があります)。');
|
|
@@ -202,8 +221,12 @@ export function describeUpdateCommand({ currentVersion, versionInfo, cache, dism
|
|
|
202
221
|
}
|
|
203
222
|
|
|
204
223
|
const tarballUrl = feed.components?.cli?.tarball?.url;
|
|
205
|
-
|
|
206
|
-
|
|
224
|
+
if (npmAvailable) {
|
|
225
|
+
lines.push('CLI を更新するには、次のコマンドを実行してください(自動実行はしません):');
|
|
226
|
+
lines.push(tarballUrl ? ` npm i -g ${tarballUrl}` : ' npm i -g akari-video@latest');
|
|
227
|
+
} else {
|
|
228
|
+
lines.push('npm が PATH に無いため、CLI の npm 更新コマンドは表示しません。');
|
|
229
|
+
}
|
|
207
230
|
lines.push(
|
|
208
231
|
dismissed
|
|
209
232
|
? `この版(v${feed.product})の通知は今後表示しません。`
|
|
@@ -303,7 +326,9 @@ export function describeCliHelp() {
|
|
|
303
326
|
' (引数なし) プロジェクトを開いて AI エージェントを起動(未作成なら自動作成)',
|
|
304
327
|
' store connect アカウント連携(無料の素材パックと購入済み素材が使えるようになる)',
|
|
305
328
|
' sounds 公式音源ライブラリを一括ダウンロード(無料)',
|
|
306
|
-
'
|
|
329
|
+
' chrome install 動画書き出しに使う Chrome をダウンロードして導入する',
|
|
330
|
+
' doctor [--json] 必須部品の実在と解決元を診断する',
|
|
331
|
+
' update [--force] 更新を確認する(--force で install.sh 経路の本体を入れ直す)',
|
|
307
332
|
' status 接続状態を確認する',
|
|
308
333
|
' migrate [dir] 古い edit.json を退避バックアップ付きで v2 へ変換',
|
|
309
334
|
'',
|
package/src/path-lookup.mjs
CHANGED
|
@@ -27,33 +27,23 @@ function resolveWindowsExtensions(pathExt) {
|
|
|
27
27
|
* を判定するため、拡張子なしファイル(npm の POSIX シムなど)は直接 spawn できない。
|
|
28
28
|
*/
|
|
29
29
|
export function findClaudeExecutable(pathEnv = process.env.PATH ?? '', platform = process.platform, pathExt = process.env.PATHEXT) {
|
|
30
|
-
|
|
31
|
-
const candidateNames = platform === 'win32'
|
|
32
|
-
? resolveWindowsExtensions(pathExt).map((extension) => `claude${extension.toLowerCase()}`)
|
|
33
|
-
: ['claude'];
|
|
34
|
-
|
|
35
|
-
for (const directory of directories) {
|
|
36
|
-
for (const name of candidateNames) {
|
|
37
|
-
const candidate = path.join(directory, name);
|
|
38
|
-
try {
|
|
39
|
-
accessSync(candidate, fsConstants.X_OK);
|
|
40
|
-
return candidate;
|
|
41
|
-
} catch {
|
|
42
|
-
// このディレクトリには無い。次を探す。
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
30
|
+
return findExecutable('claude', pathEnv, platform, pathExt);
|
|
47
31
|
}
|
|
48
32
|
|
|
49
33
|
/**
|
|
50
34
|
* PATH 上に `opencode` 実行ファイルがあるかを探す。`findClaudeExecutable` と同じ規約。
|
|
51
35
|
*/
|
|
52
36
|
export function findOpencodeExecutable(pathEnv = process.env.PATH ?? '', platform = process.platform, pathExt = process.env.PATHEXT) {
|
|
53
|
-
|
|
37
|
+
return findExecutable('opencode', pathEnv, platform, pathExt);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** PATH 上の任意のコマンドを探索する共有ヘルパー。doctor / update も同じ判定を使う。 */
|
|
41
|
+
export function findExecutable(command, pathEnv = process.env.PATH ?? '', platform = process.platform, pathExt = process.env.PATHEXT) {
|
|
42
|
+
const delimiter = platform === 'win32' ? ';' : path.delimiter;
|
|
43
|
+
const directories = pathEnv.split(delimiter).filter(Boolean);
|
|
54
44
|
const candidateNames = platform === 'win32'
|
|
55
|
-
? resolveWindowsExtensions(pathExt).map((extension) =>
|
|
56
|
-
: [
|
|
45
|
+
? resolveWindowsExtensions(pathExt).map((extension) => `${command}${extension.toLowerCase()}`)
|
|
46
|
+
: [command];
|
|
57
47
|
|
|
58
48
|
for (const directory of directories) {
|
|
59
49
|
for (const name of candidateNames) {
|