akari-video 0.1.7 → 0.1.8
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 +1 -1
- package/vendor/docs/contract-2026-08-02-setup-remote-v0.md +52 -5
- package/vendor/packages/akari-launcher/package.json +1 -1
- package/vendor/packages/decision-cards/report-helper.mjs +98 -6
- package/vendor/skills/setup-remote/SKILL.md +6 -3
- package/vendor/skills/setup-remote/serve-preview.md +26 -2
- package/vendor/skills/setup-remote/taildrop-inbox.md +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
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": {
|
|
@@ -25,7 +25,7 @@ OK / 差し戻しを言う」だけ — その閲覧と素材の受け渡しを
|
|
|
25
25
|
|
|
26
26
|
| 論点 | 決定 |
|
|
27
27
|
|---|---|
|
|
28
|
-
| 手段 | **Tailscale**(tailnet 限定)。閲覧 = `tailscale serve`
|
|
28
|
+
| 手段 | **Tailscale**(tailnet 限定)。閲覧 = `tailscale serve` でプレビューサーバーと承認レポートヘルパーを tailnet 限定 HTTPS 化。素材 = **Taildrop**(共有シートからデバイス直送。クラウド不経由・実用上サイズ無制限・画質劣化なし) |
|
|
29
29
|
| 不採用 / 保留 | Cloudflare Tunnel + Access(tailnet 外の人への共有が要件になったときの**別契約**。「公開してからゲートで守る」構造は既定にしない)。Cloudflare Pages(レポートはローカル随時生成のため噛み合わない) |
|
|
30
30
|
| 素材搬送路の優先順位 | **Taildrop(v0 で実装)** → Dropbox カメラアップロード → iCloud 写真 → Google Drive(順次。v0 では選択肢として提示のみ)。Google フォトは対象外(Library API 制限により自動取得不可) |
|
|
31
31
|
| 対応 OS | macOS / Windows 両対応を前提に書く(Tailscale / Taildrop とも両対応。手順の分岐は SKILL.md 側) |
|
|
@@ -37,7 +37,17 @@ OK / 差し戻しを言う」だけ — その閲覧と素材の受け渡しを
|
|
|
37
37
|
2. **導入ガイド — 人間手番の受け渡し**: インストーラーの sudo・GUI ログイン・スマホ側アプリ導入は
|
|
38
38
|
**自動化しない**(macOS の cask は .pkg で sudo 対話必須 — 実測済み)。スキルは
|
|
39
39
|
「ここはあなたの番」を明確に指示 → 完了を doctor で確認 → 次へ進む、を繰り返す構造とする
|
|
40
|
-
3. **serve 設定**: `tailscale serve`
|
|
40
|
+
3. **serve 設定**: `tailscale serve` で許可リスト(§3)の 2 件を tailnet 限定 HTTPS URL にする。
|
|
41
|
+
外部から見える口を安定させるため、**外部ポートを固定し内部ポートを写像する**形を既定とする:
|
|
42
|
+
|
|
43
|
+
| 外部 | 内部 | 中身 |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `https://<host>.ts.net/` | 4567 | プレビューサーバー |
|
|
46
|
+
| `https://<host>.ts.net:8443/` | ヘルパーの動的ポート | 承認レポート + `decisions.json` |
|
|
47
|
+
|
|
48
|
+
レポートヘルパーの内部ポートは起動ごとに変わりうるが、外部 URL は固定される(チャットに
|
|
49
|
+
貼ったリンクが起動のたびに死なない)。パス分岐(`--set-path /report`)はプレフィックス
|
|
50
|
+
除去の挙動を実測してから選択肢に加える — 未実測の間は既定にしない
|
|
41
51
|
4. **Taildrop 受信設定**: 受信先フォルダを固定し、作業場の `inbox/` へ接続する
|
|
42
52
|
(直接指定できる場合は直接、できない場合は受信箱 → `inbox/` への移送手順を案内)
|
|
43
53
|
5. **疎通確認**: 別デバイスから (a) serve URL が開ける (b) テストファイルを Taildrop 送信して
|
|
@@ -47,7 +57,13 @@ OK / 差し戻しを言う」だけ — その閲覧と素材の受け渡しを
|
|
|
47
57
|
|
|
48
58
|
- 既定は **tailnet 限定のみ**。公開インターネットへ出す `tailscale funnel` は**既定で使わない・
|
|
49
59
|
提案もしない**。ユーザーが明示的に要求した場合のみ、リスク説明 + 明示承認を経て扱う
|
|
50
|
-
- serve
|
|
60
|
+
- serve してよい対象は**次の許可リスト 2 件に限る**(2026-08-12 拡張):
|
|
61
|
+
- (a) **プレビューサーバー**(`akari --preview`、既定 4567)
|
|
62
|
+
- (b) **decision-cards レポートヘルパー**(`packages/decision-cards/report-helper.mjs`。
|
|
63
|
+
承認レポートの配信と `decisions.json` の read / write / commit を担う。`127.0.0.1` バインド)
|
|
64
|
+
|
|
65
|
+
これ以外のポート・アプリ・ファイルシステムは serve しない。許可リストを増やすときは
|
|
66
|
+
**本契約を改訂してから**行う(スキル側の判断で足さない)
|
|
51
67
|
- secrets を作業場・プロジェクトに書かない(creator-root v1 の原則。Tailscale の認証情報は
|
|
52
68
|
Tailscale 側が管理し、スキルは一切保持しない)
|
|
53
69
|
- スキルが勝手にインストールを完遂しない(§2-2 の人間手番)。ネットワーク設定の変更は
|
|
@@ -68,8 +84,9 @@ OK / 差し戻しを言う」だけ — その閲覧と素材の受け渡しを
|
|
|
68
84
|
|
|
69
85
|
- **L0**: SKILL.md の体裁・スキル lint が通る
|
|
70
86
|
- **L1**: doctor が 3 状態(未導入 / 未ログイン / 構成済み)を正しく判定する
|
|
71
|
-
- **L2**: 実機スモーク — 別デバイスから serve URL が開ける
|
|
72
|
-
`
|
|
87
|
+
- **L2**: 実機スモーク — 別デバイスから (a) プレビューの serve URL が開ける
|
|
88
|
+
(b) 承認レポートを開いて決定を保存 → `decisions.json` が実際に更新される
|
|
89
|
+
(c) Taildrop のテストファイルが `inbox/` に現れる(§2-5 の疎通確認そのもの)
|
|
73
90
|
|
|
74
91
|
## 6. 将来(非スコープの明示)
|
|
75
92
|
|
|
@@ -79,3 +96,33 @@ OK / 差し戻しを言う」だけ — その閲覧と素材の受け渡しを
|
|
|
79
96
|
- tailnet 外への共有(クライアント・協力者にブラウザだけで見せる): Cloudflare Tunnel +
|
|
80
97
|
Access(One-time PIN)系の別契約
|
|
81
98
|
- `inbox/` からの自動素材選択規約(直近 N 件)と create-project / analyze-footage への自動接続
|
|
99
|
+
|
|
100
|
+
## 7. L2 実測記録(2026-08-12・オーナー実機 iPhone + macOS)
|
|
101
|
+
|
|
102
|
+
**通った項目**:
|
|
103
|
+
|
|
104
|
+
| 検証 | 結果 |
|
|
105
|
+
|---|---|
|
|
106
|
+
| 別デバイスからのプレビュー閲覧 | ✓ tailnet 経由で表示。体感速度は「十分すぎる」(WireGuard 直結のため) |
|
|
107
|
+
| **レポート内に埋めた動画の再生** | ✓ iOS 実機で再生・**シーク成功**(H.264 640x360 / 6 秒) |
|
|
108
|
+
| Taildrop 着弾 | ✓ 受信先の実パス = **`~/Downloads/`**(macOS GUI 版・実測で確定) |
|
|
109
|
+
| Taildrop の無劣化性 | ✓ 92MB / HEVC / 1180x2556 / 60fps / **15.7Mbps のまま**再エンコードなし |
|
|
110
|
+
| Taildrop → 作業場 `inbox/` | ✓ **移送運用**で接続(`~/Akari/inbox/`)。macOS スタンドアロン版は受信先を設定変更できないため、直接指定ではなく移送を採る(§2-4 の想定どおり) |
|
|
111
|
+
| **承認の往復(`decisions.json`)** | ✓ スマホのタップが Mac のファイルへ書き戻ることを実測。単選カード(`thumbnail`: 既定 → 別候補)・整数スライダ(`direction.intensity`: 50 → 75)・**確定(`completedAt` 記入)** の 3 経路が通過。`byDefault: false` / `answeredAt` も期待どおり更新され、ファイルは `0600` を維持 |
|
|
112
|
+
|
|
113
|
+
- Taildrop の実測は「チャット添付は搬送路として筋が悪い」(§非スコープの根拠)を裏づけた —
|
|
114
|
+
同じ 92MB は Telegram Bot API 標準(受信 20MB)では原理的に通らない
|
|
115
|
+
- 受信物が HEVC だった点は、プレビューサーバー側の H.264 自動プロキシ層が実運用で必要になることの実例
|
|
116
|
+
|
|
117
|
+
**未達の項目(L2 は未クローズ)**:
|
|
118
|
+
|
|
119
|
+
| 未達 | 状況 |
|
|
120
|
+
|---|---|
|
|
121
|
+
| HTTPS | **`tailscale serve` の TLS が未成立**。tailnet 側で HTTPS は有効(`CertDomains` に当該ドメインあり)だが Let's Encrypt の ACME 注文が `invalid` で失敗。上記の実測は tailnet 内の平文 HTTP で代替した(WireGuard 内側のため機密性は保たれるが、secure context を要する機能は使えない)。**平文 HTTP を既定にはしない** |
|
|
122
|
+
|
|
123
|
+
## 8. 改訂履歴
|
|
124
|
+
|
|
125
|
+
| 日付 | 変更 | 理由 |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| 2026-08-02 | v0 初版 | — |
|
|
128
|
+
| 2026-08-12 | §3 serve 許可リストを 1 件 → 2 件へ拡張(decision-cards レポートヘルパーを追加)。§2-3 に外部ポート写像を明記。§5 L2 に承認往復の実測を追加 | 元のスコープ(§0「レポートを見て OK / 差し戻しを言う」)は最初から承認を含んでいたが、ハードルールが `serve = プレビューのみ` と狭すぎ、**承認レポートを配信する経路が契約上ふさがっていた**。スコープの拡大ではなくスコープとハードルールの不整合の是正 |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
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": {
|
|
@@ -186,10 +186,17 @@ function contentTypeFor(filePath) {
|
|
|
186
186
|
".jpg": "image/jpeg",
|
|
187
187
|
".js": "text/javascript; charset=utf-8",
|
|
188
188
|
".json": "application/json; charset=utf-8",
|
|
189
|
+
".m4a": "audio/mp4",
|
|
189
190
|
".mjs": "text/javascript; charset=utf-8",
|
|
191
|
+
".mov": "video/quicktime",
|
|
192
|
+
".mp3": "audio/mpeg",
|
|
193
|
+
".mp4": "video/mp4",
|
|
194
|
+
".ogg": "audio/ogg",
|
|
190
195
|
".png": "image/png",
|
|
191
196
|
".svg": "image/svg+xml",
|
|
192
197
|
".txt": "text/plain; charset=utf-8",
|
|
198
|
+
".wav": "audio/wav",
|
|
199
|
+
".webm": "video/webm",
|
|
193
200
|
".webp": "image/webp",
|
|
194
201
|
}[extension] ?? "application/octet-stream"
|
|
195
202
|
);
|
|
@@ -354,7 +361,58 @@ async function writeState(state) {
|
|
|
354
361
|
}
|
|
355
362
|
}
|
|
356
363
|
|
|
357
|
-
|
|
364
|
+
const SINGLE_RANGE_PATTERN = /^bytes=(\d*)-(\d*)$/;
|
|
365
|
+
|
|
366
|
+
// 単一バイトレンジだけを解釈する。複数レンジ・未知の単位・壊れた指定は null を返し、
|
|
367
|
+
// 呼び出し側は 200 全体配信へフォールバックする(RFC 9110 が許す挙動)。
|
|
368
|
+
// レンジが範囲外のときだけ { unsatisfiable: true } を返して 416 にする。
|
|
369
|
+
function parseRange(rangeHeader, size) {
|
|
370
|
+
if (typeof rangeHeader !== "string") {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const match = SINGLE_RANGE_PATTERN.exec(rangeHeader.trim());
|
|
375
|
+
if (match === null) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const [, rawStart, rawEnd] = match;
|
|
380
|
+
if (rawStart === "" && rawEnd === "") {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
let start;
|
|
385
|
+
let end;
|
|
386
|
+
|
|
387
|
+
if (rawStart === "") {
|
|
388
|
+
// bytes=-N — 末尾 N バイト
|
|
389
|
+
const suffixLength = Number(rawEnd);
|
|
390
|
+
if (!Number.isSafeInteger(suffixLength) || suffixLength <= 0) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
start = Math.max(0, size - suffixLength);
|
|
394
|
+
end = size - 1;
|
|
395
|
+
} else {
|
|
396
|
+
start = Number(rawStart);
|
|
397
|
+
end = rawEnd === "" ? size - 1 : Number(rawEnd);
|
|
398
|
+
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end)) {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
if (end > size - 1) {
|
|
402
|
+
end = size - 1;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (start >= size || start > end) {
|
|
407
|
+
return { unsatisfiable: true };
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return { start, end };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// iOS Safari は <video> に対してまず `Range: bytes=0-1` を投げ、206 が返らないと再生を諦める。
|
|
414
|
+
// レンジ対応は動画・音声をレポート内で再生させるための必須要件。
|
|
415
|
+
async function serveFile(response, filePath, contentType, rangeHeader) {
|
|
358
416
|
let fileStats;
|
|
359
417
|
try {
|
|
360
418
|
fileStats = await stat(filePath);
|
|
@@ -369,15 +427,44 @@ async function serveFile(response, filePath, contentType) {
|
|
|
369
427
|
throw new HttpError(404, "file not found");
|
|
370
428
|
}
|
|
371
429
|
|
|
372
|
-
|
|
430
|
+
const baseHeaders = {
|
|
373
431
|
"Content-Type": contentType ?? contentTypeFor(filePath),
|
|
374
|
-
"Content-Length": fileStats.size,
|
|
375
432
|
"Cache-Control": "no-store",
|
|
376
433
|
"X-Content-Type-Options": "nosniff",
|
|
434
|
+
"Accept-Ranges": "bytes",
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
if (fileStats.size === 0) {
|
|
438
|
+
response.writeHead(200, { ...baseHeaders, "Content-Length": 0 });
|
|
439
|
+
response.end();
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const range = parseRange(rangeHeader, fileStats.size);
|
|
444
|
+
|
|
445
|
+
if (range !== null && range.unsatisfiable === true) {
|
|
446
|
+
response.writeHead(416, {
|
|
447
|
+
...baseHeaders,
|
|
448
|
+
"Content-Length": 0,
|
|
449
|
+
"Content-Range": `bytes */${fileStats.size}`,
|
|
450
|
+
});
|
|
451
|
+
response.end();
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const start = range === null ? 0 : range.start;
|
|
456
|
+
const end = range === null ? fileStats.size - 1 : range.end;
|
|
457
|
+
|
|
458
|
+
response.writeHead(range === null ? 200 : 206, {
|
|
459
|
+
...baseHeaders,
|
|
460
|
+
"Content-Length": end - start + 1,
|
|
461
|
+
...(range === null
|
|
462
|
+
? {}
|
|
463
|
+
: { "Content-Range": `bytes ${start}-${end}/${fileStats.size}` }),
|
|
377
464
|
});
|
|
378
465
|
|
|
379
466
|
await new Promise((resolveStream, rejectStream) => {
|
|
380
|
-
const stream = createReadStream(filePath);
|
|
467
|
+
const stream = createReadStream(filePath, { start, end });
|
|
381
468
|
stream.on("error", rejectStream);
|
|
382
469
|
stream.on("end", resolveStream);
|
|
383
470
|
stream.pipe(response);
|
|
@@ -421,7 +508,12 @@ const server = createServer(async (request, response) => {
|
|
|
421
508
|
|
|
422
509
|
try {
|
|
423
510
|
if (request.method === "GET" && rawPath === "/") {
|
|
424
|
-
await serveFile(
|
|
511
|
+
await serveFile(
|
|
512
|
+
response,
|
|
513
|
+
reportPath,
|
|
514
|
+
"text/html; charset=utf-8",
|
|
515
|
+
request.headers.range,
|
|
516
|
+
);
|
|
425
517
|
return;
|
|
426
518
|
}
|
|
427
519
|
|
|
@@ -477,7 +569,7 @@ const server = createServer(async (request, response) => {
|
|
|
477
569
|
|
|
478
570
|
if (request.method === "GET") {
|
|
479
571
|
const staticPath = await resolveStaticPath(rawPath);
|
|
480
|
-
await serveFile(response, staticPath);
|
|
572
|
+
await serveFile(response, staticPath, undefined, request.headers.range);
|
|
481
573
|
return;
|
|
482
574
|
}
|
|
483
575
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: setup-remote
|
|
3
|
-
description: スマホなど別デバイスから承認レポート・プレビューを閲覧し、撮影素材を作業場へ送れるようにする遠隔セットアップスキル。Tailscale の状態を doctor で判定し、導入・ログイン(人間手番)→ tailscale serve でプレビューサーバー(既定 4567
|
|
3
|
+
description: スマホなど別デバイスから承認レポート・プレビューを閲覧し、撮影素材を作業場へ送れるようにする遠隔セットアップスキル。Tailscale の状態を doctor で判定し、導入・ログイン(人間手番)→ tailscale serve でプレビューサーバー(既定 4567)と承認レポートヘルパーを tailnet 限定 HTTPS 化 → Taildrop 受信先を作業場 inbox/ へ接続 → 別デバイスからの疎通確認までを一気通貫でガイドする。「スマホでレポートを見たい」「外から承認したい」「スマホから素材を送りたい」「遠隔セットアップして」で発動する。公開インターネットへの露出(funnel)は既定で扱わない。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# FORBIDDEN 級ハードルール
|
|
@@ -12,7 +12,10 @@ description: スマホなど別デバイスから承認レポート・プレビ
|
|
|
12
12
|
1. **`tailscale funnel`(公開インターネットへの露出)を実行・提案しない。** ユーザーが明示的に
|
|
13
13
|
funnel を要求した場合のみ、「URL を知る誰でも到達できる」リスクを説明し、明示承認を得てから扱う。
|
|
14
14
|
既定は常に tailnet 限定(私設ネットワーク内のみ)。
|
|
15
|
-
2. **serve
|
|
15
|
+
2. **serve してよいのは許可リストの 2 件だけ** — (a) プレビューサーバー(既定 4567)
|
|
16
|
+
(b) decision-cards レポートヘルパー(`report-helper.mjs`。承認レポート配信 + `decisions.json`)。
|
|
17
|
+
**ファイルシステム・他ポート・他アプリを serve しない。** 許可リストの拡張は契約改訂を要する
|
|
18
|
+
(スキルの判断で足さない)。
|
|
16
19
|
3. **インストール・ログイン・スマホ側アプリ導入を代行しない。** 人間の手番として明示的に依頼し、
|
|
17
20
|
完了を doctor(`bin/doctor.mjs`)の再実行で確認してから次工程へ進む。
|
|
18
21
|
4. **ネットワーク設定の変更(serve の on/off・受信先変更)は、実行するコマンドと効果・解除方法を
|
|
@@ -25,7 +28,7 @@ description: スマホなど別デバイスから承認レポート・プレビ
|
|
|
25
28
|
# 実行順リーフ
|
|
26
29
|
|
|
27
30
|
1. [doctor-and-install.md](doctor-and-install.md) — doctor 判定 → 状態別の導入・ログインガイド(macOS / Windows / スマホ)
|
|
28
|
-
2. [serve-preview.md](serve-preview.md) — プレビューサーバー起動 → `tailscale serve` 設定 → URL 確定
|
|
31
|
+
2. [serve-preview.md](serve-preview.md) — プレビューサーバー起動 → `tailscale serve` 設定 → URL 確定 →(承認をスマホで回す場合)レポートヘルパーの serve
|
|
29
32
|
3. [taildrop-inbox.md](taildrop-inbox.md) — Taildrop 受信先の固定 → 作業場 `inbox/` へ接続 → 疎通確認 → 完了レポート
|
|
30
33
|
|
|
31
34
|
詳細を先読みせず、現在の工程に対応するファイルだけを読む。人間手番の完了を doctor で確認せずに先へ進めない。
|
|
@@ -26,5 +26,29 @@ tailscale serve --bg 4567
|
|
|
26
26
|
|
|
27
27
|
- doctor 再実行 → `serve.configured: true` と `serveUrl` を確認
|
|
28
28
|
- スマホ(Tailscale オン)で `serveUrl` を開いてもらい、プレビューが表示されることを確認
|
|
29
|
-
-
|
|
30
|
-
Tailscale
|
|
29
|
+
- 開けない場合の切り分け順:
|
|
30
|
+
1. スマホ側の Tailscale がオンか(最頻出の原因)
|
|
31
|
+
2. **TLS ハンドシェイクで止まる場合は証明書**。管理コンソールで HTTPS Certificates が
|
|
32
|
+
有効か確認する。有効なのに失敗するなら `tailscale cert <dnsName>` を単体実行して
|
|
33
|
+
ACME のエラー本文を読む(発行が invalid なら tailnet 側の問題で、serve の設定ミスではない)
|
|
34
|
+
3. マシンがスリープしていないか(寝ると tailnet から消える)
|
|
35
|
+
|
|
36
|
+
## 4. 承認レポートを serve する(必要なとき)
|
|
37
|
+
|
|
38
|
+
承認ゲートをスマホで回す場合は、decision-cards レポートヘルパーも serve する。
|
|
39
|
+
**許可リストはプレビューサーバーとこのヘルパーの 2 件だけ**(ハードルール 2)。
|
|
40
|
+
|
|
41
|
+
1. ヘルパーを起動する(`<port>` は空きポート。標準出力に `HELPER: http://localhost:<port>/`):
|
|
42
|
+
```
|
|
43
|
+
node packages/decision-cards/report-helper.mjs <report.html のパス> --port <port>
|
|
44
|
+
```
|
|
45
|
+
2. serve 設定(承認必須 — ハードルール 4)。コマンド・効果・解除方法を提示して承認を得る:
|
|
46
|
+
```
|
|
47
|
+
tailscale serve --bg --https=8443 <port>
|
|
48
|
+
```
|
|
49
|
+
- 効果: `https://<マシン名>.<tailnet 名>.ts.net:8443` → `localhost:<port>`。tailnet 内限定
|
|
50
|
+
- 外部ポートを 8443 に固定するため、ヘルパーの内部ポートが起動ごとに変わっても
|
|
51
|
+
**チャットに貼った URL は死なない**
|
|
52
|
+
- 解除: `tailscale serve --https=8443 off`(プレビュー側は残る)
|
|
53
|
+
3. スマホで開き、カードを 1 つ操作 → `<report.html>.decisions.json` が実際に更新されることを
|
|
54
|
+
Mac 側で確認する。**表示できただけで完了と言わない**(ハードルール 6)
|
|
@@ -8,6 +8,10 @@ Taildrop = Tailscale のデバイス間ファイル直送(スマホの共有
|
|
|
8
8
|
- 受信先は環境により異なる(macOS / Windows の GUI 版は多くの環境で「ダウンロード」フォルダ。
|
|
9
9
|
アプリの設定で変更できる場合がある)。**推測で断定せず、§3 のテスト送信で実パスを確定する**
|
|
10
10
|
- CLI 運用(Linux 等)では `tailscale file get <受信先ディレクトリ>` で取り出す
|
|
11
|
+
- **実測(2026-08-12 / macOS スタンドアロン版 `io.tailscale.ipn.macsys` 1.98.10)**: 受信先は
|
|
12
|
+
`~/Downloads` で、`defaults read io.tailscale.ipn.macsys` に受信先を指すキーは存在しなかった。
|
|
13
|
+
この形態では受信先を `inbox/` へ直接向けられない前提で §2 の移送運用に進んでよい
|
|
14
|
+
(設定 UI に項目が見当たらない場合も同じ扱い)
|
|
11
15
|
|
|
12
16
|
## 2. 作業場 inbox/ への接続
|
|
13
17
|
|