akari-video 0.1.31 → 0.1.33
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 +3 -15
- package/bin/akari.mjs +3 -1
- package/package.json +1 -1
- package/src/capture-command.mjs +0 -35
- package/src/clean-command.mjs +191 -0
- package/src/clean-manifest.mjs +435 -0
- package/src/cli.mjs +6 -2
- package/src/doctor-command.mjs +0 -1
- package/src/messages.mjs +1 -1
- package/src/runtime-diagnostics.mjs +1 -48
- package/vendor/.akari-capability-sources.json +4 -0
- package/vendor/docs/contract-2026-07-22-render-basics.md +4 -3
- package/vendor/docs/contract-2026-07-25-project-structure-v0.md +41 -0
- package/vendor/docs/contract-2026-08-02-preview-parity.md +12 -1
- package/vendor/docs/contract-2026-08-03-caption-display-encoding-qc-v1.md +56 -0
- package/vendor/docs/contract-2026-08-18-v1-render-parity.md +2 -3
- package/vendor/docs/contract-2026-08-28-gpu-export-v0.md +37 -10
- package/vendor/docs/contract-2026-08-28-osr-export-v0.md +105 -9
- package/vendor/docs/contract-2026-08-29-media-inspect-cli-v0.md +2 -1
- package/vendor/docs/contract-2026-08-30-edit-json-v2-object-tree-v0.md +24 -0
- package/vendor/docs/contract-2026-08-30-motion-and-keyframes-v0.md +3 -0
- package/vendor/docs/contract-2026-09-02-captions-style-preset-v0.md +84 -0
- package/vendor/docs/contract-2026-09-02-export-verify-declared-vs-measured-v0.md +105 -0
- package/vendor/docs/contract-2026-09-02-item-caption-anchor-v0.md +102 -0
- package/vendor/docs/contract-2026-09-02-transcript-unrecognized-spans-v0.md +57 -0
- package/vendor/packages/akari-launcher/README.md +3 -15
- package/vendor/packages/akari-launcher/package.json +1 -1
- package/vendor/packages/edit-lint/README.md +30 -0
- package/vendor/packages/edit-lint/src/edit-lint.mjs +466 -4
- package/vendor/packages/edit-store/lib/caption-display.d.ts +16 -0
- package/vendor/packages/edit-store/lib/caption-display.js +53 -2
- package/vendor/packages/edit-store/lib/caption-store.d.ts +2 -0
- package/vendor/packages/edit-store/lib/caption-store.js +42 -5
- package/vendor/packages/edit-store/lib/caption-style-preset.d.ts +16 -0
- package/vendor/packages/edit-store/lib/caption-style-preset.js +87 -0
- package/vendor/packages/edit-store/lib/caption-words-rederive.d.ts +34 -0
- package/vendor/packages/edit-store/lib/caption-words-rederive.js +205 -0
- package/vendor/packages/edit-store/lib/cut-ranges.d.ts +17 -0
- package/vendor/packages/edit-store/lib/cut-ranges.js +206 -0
- package/vendor/packages/edit-store/lib/edit-v2.d.ts +9 -0
- package/vendor/packages/edit-store/lib/generated/edit-v2-keys.d.ts +6 -6
- package/vendor/packages/edit-store/lib/generated/edit-v2-keys.js +6 -1
- package/vendor/packages/edit-store/lib/generated/textstyle-catalog.d.ts +2 -0
- package/vendor/packages/edit-store/lib/generated/textstyle-catalog.js +251 -0
- package/vendor/packages/edit-store/lib/index.d.ts +6 -0
- package/vendor/packages/edit-store/lib/index.js +6 -0
- package/vendor/packages/edit-store/lib/internal-model.d.ts +3 -0
- package/vendor/packages/edit-store/lib/internal-model.js +5 -1
- package/vendor/packages/edit-store/lib/item-anchor.d.ts +53 -0
- package/vendor/packages/edit-store/lib/item-anchor.js +154 -0
- package/vendor/packages/edit-store/lib/timeline-map.d.ts +5 -0
- package/vendor/packages/edit-store/lib/timeline-map.js +23 -0
- package/vendor/packages/edit-store/lib/track-z.d.ts +41 -0
- package/vendor/packages/edit-store/lib/track-z.js +65 -0
- package/vendor/packages/edit-store/lib/tree-ops.d.ts +13 -0
- package/vendor/packages/edit-store/lib/tree-ops.js +23 -0
- package/vendor/packages/edit-store/lib/webview-kernel.d.ts +3 -0
- package/vendor/packages/edit-store/lib/webview-kernel.js +1906 -3
- package/vendor/packages/edit-store/package.json +2 -1
- package/vendor/packages/gpu-export/README.ja.md +97 -0
- package/vendor/packages/gpu-export/README.md +110 -0
- package/vendor/packages/osr-export/README.md +7 -1
- package/vendor/packages/render-cut/package.json +1 -5
- package/vendor/packages/schemas/analysis.schema.json +16 -0
- package/vendor/packages/schemas/bin/validate-captions.mjs +56 -1
- package/vendor/packages/schemas/captions.schema.json +26 -2
- package/vendor/packages/schemas/edit.schema.json +26 -1
- package/vendor/packages/schemas/engine-capabilities.json +103 -0
- package/vendor/packages/schemas/test/analysis-observations.test.mjs +30 -0
- package/vendor/packages/schemas/test/edit-v2-schema.test.mjs +37 -0
- package/vendor/packages/schemas/test/engine-capabilities.test.mjs +71 -0
- package/vendor/packages/schemas/test/validate-captions.test.mjs +84 -0
- package/vendor/skills/analyze-footage/bin/face-expression/browser.mjs +2 -2
- package/vendor/skills/analyze-footage/bin/face-expression/face-expression.mjs +1 -1
- package/vendor/skills/analyze-footage/vision-tracks.md +2 -1
- package/vendor/skills/beat-sync-edit/pitfalls.md +2 -2
- package/vendor/skills/edit-lint/SKILL.md +8 -2
- package/vendor/skills/edit-plan/SKILL.md +1 -0
- package/vendor/skills/edit-plan/workflow.md +1 -0
- package/vendor/skills/overlay-authoring/3d.md +4 -5
- package/vendor/skills/overlay-authoring/thumbnail.md +3 -20
- package/vendor/skills/render-cut/SKILL.md +4 -4
- package/vendor/skills/setup-library/SKILL.md +2 -2
- package/vendor/skills/setup-library/tools-check.md +0 -13
- package/vendor/skills/verify/SKILL.md +2 -1
- package/vendor/templates/project-default/.akari/work/keep/.gitkeep +0 -0
- package/vendor/templates/project-default/.akari/work/tmp/.gitkeep +0 -0
- package/src/chrome-command.mjs +0 -100
package/README.md
CHANGED
|
@@ -50,21 +50,9 @@ DL・sha256 検証・適用まで実行。それ以外(npm グローバル / g
|
|
|
50
50
|
配布物の取得ができる。`src/store-command.mjs`)/
|
|
51
51
|
`akari assets <list|fetch|sync|...>`(素材カタログの一覧・取得・同期。
|
|
52
52
|
`packages/asset-resolver` の CLI への薄い委譲で、カタログ合成・entitlements 判定・
|
|
53
|
-
sha256 検証・fail-closed は resolver 側の責務のまま。`src/assets-command.mjs
|
|
54
|
-
|
|
55
|
-
|
|
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 アプリの配布物には必要なモジュールが同梱されている。
|
|
53
|
+
sha256 検証・fail-closed は resolver 側の責務のまま。`src/assets-command.mjs`)/
|
|
54
|
+
`akari clean [project-dir] [--dry-run] [--yes] [--json]`(使い捨ての中間ファイル、保持する
|
|
55
|
+
正本、判断が必要なものを容量付きで一覧する。既定は一覧のみで、削除可能なものだけを承認後に削除)。
|
|
68
56
|
|
|
69
57
|
`akari` に渡した引数はそのまま `opencode` に転送する(例: `akari --continue` は
|
|
70
58
|
`opencode --continue` を起動する)。
|
package/bin/akari.mjs
CHANGED
|
@@ -11,6 +11,7 @@ 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 { runCleanCommand } from '../src/clean-command.mjs';
|
|
14
15
|
import { runDoctorCommand } from '../src/doctor-command.mjs';
|
|
15
16
|
import { resolveRuntimePaths } from '../src/runtime-diagnostics.mjs';
|
|
16
17
|
import { maybeApplyPendingUpdateOnLaunch, resolveInstalledVersionInfo } from '../src/update-check.mjs';
|
|
@@ -59,7 +60,7 @@ try {
|
|
|
59
60
|
|
|
60
61
|
// `akari update` / `akari init` / `akari new` / `akari narration` / `akari internal` /
|
|
61
62
|
// `akari sounds` / `akari status` / `akari accept` / `akari capability` / `akari store` /
|
|
62
|
-
// `akari assets` は claude へ転送せず、専用のサブコマンドとして扱う(契約 §4-1 /
|
|
63
|
+
// `akari assets` / `akari clean` は claude へ転送せず、専用のサブコマンドとして扱う(契約 §4-1 /
|
|
63
64
|
// タスク契約 launcher-init(内部リポ)/ 音源カタログ既定化のオーナー裁定 2026-08-03 /
|
|
64
65
|
// AKARI Store 連携 / タスク契約 2026-08-09-agent-assets-discovery)。
|
|
65
66
|
// それ以外の引数はすべて従来どおり claude へ転送する。
|
|
@@ -78,6 +79,7 @@ const invoke = (argv[0] === '--version' || argv[0] === '-v') ? printVersion()
|
|
|
78
79
|
: argv[0] === 'store' ? runStoreCommand(argv.slice(1))
|
|
79
80
|
: argv[0] === 'assets' ? runAssetsCommand(argv.slice(1))
|
|
80
81
|
: argv[0] === 'migrate' ? runMigrateCommand(argv.slice(1))
|
|
82
|
+
: argv[0] === 'clean' ? runCleanCommand(argv.slice(1))
|
|
81
83
|
: run(argv);
|
|
82
84
|
|
|
83
85
|
const result = await invoke.catch((error) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
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": {
|
package/src/capture-command.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
5
3
|
|
|
6
4
|
import { resolveLauncherAssets } from "./repo-assets.mjs";
|
|
7
5
|
|
|
@@ -13,15 +11,6 @@ export async function runCaptureCommand(argv, options = {}) {
|
|
|
13
11
|
return { exitCode: 1 };
|
|
14
12
|
}
|
|
15
13
|
|
|
16
|
-
const isHelp = argv.includes("--help") || argv.includes("-h");
|
|
17
|
-
if (!isHelp && captureNeedsChrome(argv, options.platform ?? process.platform)) {
|
|
18
|
-
const browser = await resolveBrowserDiagnostics(assets, options);
|
|
19
|
-
if (!await browser.findChromePath()) {
|
|
20
|
-
logError(await browser.describeChromeNotFound());
|
|
21
|
-
return { exitCode: 1 };
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
14
|
const spawn = options.spawn ?? spawnSync;
|
|
26
15
|
const result = spawn(process.execPath, [assets.captureScript, ...argv], {
|
|
27
16
|
stdio: "inherit",
|
|
@@ -29,27 +18,3 @@ export async function runCaptureCommand(argv, options = {}) {
|
|
|
29
18
|
});
|
|
30
19
|
return { exitCode: typeof result.status === "number" ? result.status : 1 };
|
|
31
20
|
}
|
|
32
|
-
|
|
33
|
-
export function captureNeedsChrome(argv, platform = process.platform) {
|
|
34
|
-
let engine = "auto";
|
|
35
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
36
|
-
if (argv[index] === "--engine" && argv[index + 1] !== undefined) engine = argv[index + 1];
|
|
37
|
-
else if (argv[index].startsWith("--engine=")) engine = argv[index].slice("--engine=".length);
|
|
38
|
-
}
|
|
39
|
-
return engine === "legacy" || (engine === "auto" && !["darwin", "win32"].includes(platform));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function resolveBrowserDiagnostics(assets, options) {
|
|
43
|
-
if (options.findChromePath && options.describeChromeNotFound) {
|
|
44
|
-
return {
|
|
45
|
-
findChromePath: options.findChromePath,
|
|
46
|
-
describeChromeNotFound: options.describeChromeNotFound,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
const modulePath = join(assets.repoRoot, "packages", "render-cut", "src", "render-cut.mjs");
|
|
50
|
-
const module = await import(pathToFileURL(modulePath).href);
|
|
51
|
-
return {
|
|
52
|
-
findChromePath: options.findChromePath ?? module.findChromePath,
|
|
53
|
-
describeChromeNotFound: options.describeChromeNotFound ?? module.describeChromeNotFound,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { lstat, rm } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createInterface } from 'node:readline/promises';
|
|
4
|
+
|
|
5
|
+
import { classifyProject } from './clean-manifest.mjs';
|
|
6
|
+
|
|
7
|
+
export async function runCleanCommand(args, options = {}) {
|
|
8
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
9
|
+
const error = options.error ?? ((line) => console.error(line));
|
|
10
|
+
const cwd = options.cwd ?? process.cwd();
|
|
11
|
+
const parsed = parseArguments(args, cwd);
|
|
12
|
+
if (!parsed.ok) {
|
|
13
|
+
error(parsed.message);
|
|
14
|
+
for (const line of cleanHelp()) error(line);
|
|
15
|
+
return { exitCode: 2 };
|
|
16
|
+
}
|
|
17
|
+
if (parsed.help) {
|
|
18
|
+
for (const line of cleanHelp()) log(line);
|
|
19
|
+
return { exitCode: 0 };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const statPath = options.lstat ?? lstat;
|
|
23
|
+
try {
|
|
24
|
+
const editStat = await statPath(path.join(parsed.projectRoot, 'edit.json'));
|
|
25
|
+
if (!editStat.isFile()) throw Object.assign(new Error('not a file'), { code: 'ENOENT' });
|
|
26
|
+
} catch {
|
|
27
|
+
error(`edit.json が見つかりません: ${parsed.projectRoot}`);
|
|
28
|
+
return { exitCode: 2 };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let classification;
|
|
32
|
+
try {
|
|
33
|
+
classification = await (options.classify ?? classifyProject)(parsed.projectRoot, {
|
|
34
|
+
now: options.now,
|
|
35
|
+
lstat: options.lstat,
|
|
36
|
+
readdir: options.readdir,
|
|
37
|
+
readFile: options.readFile,
|
|
38
|
+
});
|
|
39
|
+
} catch (cause) {
|
|
40
|
+
error(`プロジェクトを調べられません: ${messageOf(cause)}`);
|
|
41
|
+
return { exitCode: 2 };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (parsed.json) {
|
|
45
|
+
log(JSON.stringify(classification));
|
|
46
|
+
} else {
|
|
47
|
+
for (const line of formatClassification(classification)) log(line);
|
|
48
|
+
for (const warning of provenanceWarnings(classification)) log(warning);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (parsed.dryRun || classification.disposable.length === 0) {
|
|
52
|
+
if (!parsed.json && classification.disposable.length === 0) log('削除対象はありません。');
|
|
53
|
+
return { exitCode: 0, classification, removed: [], failures: [] };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!parsed.yes) {
|
|
57
|
+
const isTTY = options.isTTY ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
58
|
+
if (!isTTY) {
|
|
59
|
+
error('非 TTY では明示承認が必要です。一覧を確認し、--yes を付けて再実行してください。');
|
|
60
|
+
return { exitCode: 2, classification, removed: [], failures: [] };
|
|
61
|
+
}
|
|
62
|
+
const answer = options.prompt ? await options.prompt() : await promptForConfirmation();
|
|
63
|
+
const accepted = typeof answer === 'boolean' ? answer : /^(?:y|yes)$/iu.test(String(answer).trim());
|
|
64
|
+
if (!accepted) {
|
|
65
|
+
if (!parsed.json) log('削除しませんでした。プロジェクトは変更されていません。');
|
|
66
|
+
return { exitCode: 0, classification, removed: [], failures: [] };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const remove = options.remove ?? ((target) => rm(target, { recursive: true, force: true }));
|
|
71
|
+
const removed = [];
|
|
72
|
+
const failures = [];
|
|
73
|
+
for (const entry of classification.disposable) {
|
|
74
|
+
const target = path.resolve(parsed.projectRoot, entry.path);
|
|
75
|
+
if (!isInside(parsed.projectRoot, target) || target === parsed.projectRoot) {
|
|
76
|
+
const failure = { path: entry.path, code: 'OUTSIDE_PROJECT' };
|
|
77
|
+
failures.push(failure);
|
|
78
|
+
error(`削除に失敗しました: ${entry.path} (${failure.code})`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
await remove(target, { recursive: true, force: true });
|
|
83
|
+
removed.push(entry.path);
|
|
84
|
+
} catch (cause) {
|
|
85
|
+
const code = errorCode(cause);
|
|
86
|
+
failures.push({ path: entry.path, code });
|
|
87
|
+
error(`削除に失敗しました: ${entry.path} (${code})`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (failures.length > 0) {
|
|
92
|
+
error('一部を削除できませんでした。AKARI Video と書き出し処理を終了してから再実行してください。');
|
|
93
|
+
return { exitCode: 1, classification, removed, failures };
|
|
94
|
+
}
|
|
95
|
+
if (!parsed.json) log(`削除しました: ${removed.length} 件`);
|
|
96
|
+
return { exitCode: 0, classification, removed, failures };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function parseArguments(args, cwd = process.cwd()) {
|
|
100
|
+
const allowed = new Set(['--dry-run', '--yes', '--json', '--help']);
|
|
101
|
+
const flags = args.filter((value) => value.startsWith('-'));
|
|
102
|
+
const unknown = [...new Set(flags.filter((value) => !allowed.has(value)))];
|
|
103
|
+
if (unknown.length > 0) return { ok: false, message: `未知のオプションです: ${unknown.join(', ')}` };
|
|
104
|
+
const positional = args.filter((value) => !value.startsWith('-'));
|
|
105
|
+
if (positional.length > 1) return { ok: false, message: '引数のプロジェクトディレクトリは 1 つだけ指定できます。' };
|
|
106
|
+
const flagSet = new Set(flags);
|
|
107
|
+
return {
|
|
108
|
+
ok: true,
|
|
109
|
+
help: flagSet.has('--help'),
|
|
110
|
+
yes: flagSet.has('--yes'),
|
|
111
|
+
dryRun: flagSet.has('--dry-run'),
|
|
112
|
+
json: flagSet.has('--json'),
|
|
113
|
+
projectRoot: path.resolve(cwd, positional[0] ?? '.'),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function cleanHelp() {
|
|
118
|
+
return [
|
|
119
|
+
'使い方: akari clean [dir] [--dry-run] [--yes] [--json] [--help]',
|
|
120
|
+
'',
|
|
121
|
+
'使い捨ての中間ファイル、保持する正本、判断が必要なものを一覧します。',
|
|
122
|
+
'既定では一覧だけを表示し、削除前には必ず確認します。',
|
|
123
|
+
'',
|
|
124
|
+
' --dry-run 一覧だけを表示して削除しない',
|
|
125
|
+
' --yes 一覧を表示した後、削除可能なものだけを削除',
|
|
126
|
+
' --json 分類結果を JSON で表示',
|
|
127
|
+
' --help このヘルプを表示',
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function formatClassification(classification) {
|
|
132
|
+
const lines = [];
|
|
133
|
+
for (const [className, heading] of [
|
|
134
|
+
['disposable', '削除可能:'],
|
|
135
|
+
['keep', '保持:'],
|
|
136
|
+
['undecided', '判断保留:'],
|
|
137
|
+
]) {
|
|
138
|
+
lines.push(heading);
|
|
139
|
+
const entries = classification[className];
|
|
140
|
+
if (entries.length === 0) lines.push(' (なし)');
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
const provenance = entry.provenance
|
|
143
|
+
? ` / 由来: ${entry.provenance.origin} / 生成器: ${entry.provenance.generator}`
|
|
144
|
+
: '';
|
|
145
|
+
lines.push(` ${entry.path} | ${entry.files} ファイル | ${formatBytes(entry.bytes)} | ${entry.reason}${provenance}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
lines.push(`削除可能 合計 ${formatBytes(classification.totals.disposable.bytes)}`);
|
|
149
|
+
return lines;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function formatBytes(bytes) {
|
|
153
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
154
|
+
const units = ['KB', 'MB', 'GB'];
|
|
155
|
+
let value = bytes / 1024;
|
|
156
|
+
let unit = units[0];
|
|
157
|
+
for (let index = 1; index < units.length && value >= 1024; index += 1) {
|
|
158
|
+
value /= 1024;
|
|
159
|
+
unit = units[index];
|
|
160
|
+
}
|
|
161
|
+
return `${value.toFixed(1)} ${unit}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function provenanceWarnings(classification) {
|
|
165
|
+
return classification.keep
|
|
166
|
+
.filter((entry) => entry.provenance?.origin_exists === false)
|
|
167
|
+
.map((entry) => `[警告] 由来の計画ファイルが見当たりません: ${entry.provenance.origin}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function promptForConfirmation() {
|
|
171
|
+
const readline = createInterface({ input: process.stdin, output: process.stdout });
|
|
172
|
+
try {
|
|
173
|
+
return await readline.question('削除可能な中間ファイルだけを削除しますか? [y/N] ');
|
|
174
|
+
} finally {
|
|
175
|
+
readline.close();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isInside(root, candidate) {
|
|
180
|
+
const relative = path.relative(root, candidate);
|
|
181
|
+
return relative !== '' && !relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function errorCode(error) {
|
|
185
|
+
if (error && typeof error === 'object') return String(error.code ?? error.errno ?? messageOf(error));
|
|
186
|
+
return messageOf(error);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function messageOf(error) {
|
|
190
|
+
return error instanceof Error ? error.message : String(error);
|
|
191
|
+
}
|