@lnar/cli 0.0.1-dev.27db913 → 0.0.1-dev.3982ccb

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.
Files changed (53) hide show
  1. package/dist/api-client.d.ts +19 -0
  2. package/dist/api-client.js +40 -0
  3. package/dist/api-client.js.map +1 -1
  4. package/dist/auth.d.ts +20 -0
  5. package/dist/auth.js +74 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/cli.js +25 -0
  8. package/dist/cli.js.map +1 -1
  9. package/dist/commands/daemon.js +13 -0
  10. package/dist/commands/daemon.js.map +1 -1
  11. package/dist/commands/record.d.ts +11 -0
  12. package/dist/commands/record.js +136 -0
  13. package/dist/commands/record.js.map +1 -0
  14. package/dist/recording/bundle.d.ts +23 -0
  15. package/dist/recording/bundle.js +41 -0
  16. package/dist/recording/bundle.js.map +1 -0
  17. package/dist/recording/capture.d.ts +33 -0
  18. package/dist/recording/capture.js +173 -0
  19. package/dist/recording/capture.js.map +1 -0
  20. package/dist/recording/session.d.ts +27 -0
  21. package/dist/recording/session.js +81 -0
  22. package/dist/recording/session.js.map +1 -0
  23. package/dist/recording/types.d.ts +59 -0
  24. package/dist/recording/types.js +8 -0
  25. package/dist/recording/types.js.map +1 -0
  26. package/dist/run-client.d.ts +19 -0
  27. package/dist/run-client.js +44 -0
  28. package/dist/run-client.js.map +1 -0
  29. package/dist/run-worker.d.ts +25 -0
  30. package/dist/run-worker.js +85 -0
  31. package/dist/run-worker.js.map +1 -0
  32. package/dist/runtime/actions.d.ts +13 -0
  33. package/dist/runtime/actions.js +107 -0
  34. package/dist/runtime/actions.js.map +1 -0
  35. package/dist/runtime/client.d.ts +3 -0
  36. package/dist/runtime/client.js +85 -0
  37. package/dist/runtime/client.js.map +1 -0
  38. package/dist/runtime/login.d.ts +20 -0
  39. package/dist/runtime/login.js +34 -0
  40. package/dist/runtime/login.js.map +1 -0
  41. package/dist/runtime/loop.d.ts +28 -0
  42. package/dist/runtime/loop.js +68 -0
  43. package/dist/runtime/loop.js.map +1 -0
  44. package/dist/runtime/playbook.d.ts +49 -0
  45. package/dist/runtime/playbook.js +73 -0
  46. package/dist/runtime/playbook.js.map +1 -0
  47. package/dist/runtime/runner.d.ts +20 -0
  48. package/dist/runtime/runner.js +54 -0
  49. package/dist/runtime/runner.js.map +1 -0
  50. package/dist/runtime/types.d.ts +69 -0
  51. package/dist/runtime/types.js +10 -0
  52. package/dist/runtime/types.js.map +1 -0
  53. package/package.json +9 -1
@@ -0,0 +1,173 @@
1
+ /**
2
+ * ローカルブラウザのユーザー操作をキャプチャする。
3
+ *
4
+ * 設計:
5
+ * - ページ内に capture-phase のリスナを注入し、click / change / submit / keydown を
6
+ * `window.__lnarRecord` (exposeBinding) 経由で Node 側へ送る。
7
+ * - Node 側 (Recorder) で録画開始からの相対時刻を付与し、キーフレームを保存する。
8
+ * - main frame のナビゲーションは Playwright の framenavigated で記録する。
9
+ *
10
+ * 機微フィールド (password 等) の値は **ページ内でマスク** してから送るため、生の
11
+ * 認証情報は Node 側にもバンドルにも残らない。
12
+ */
13
+ import { writeFile } from 'node:fs/promises';
14
+ import { join } from 'node:path';
15
+ /** 全ページに注入する記録スクリプト (self-contained・依存なし)。 */
16
+ export const INIT_SCRIPT = `() => {
17
+ if (window.__lnarRecorderInstalled) return;
18
+ window.__lnarRecorderInstalled = true;
19
+ var MAXV = 300;
20
+ function esc(s){ return (window.CSS && CSS.escape) ? CSS.escape(s) : String(s).replace(/[^a-zA-Z0-9_-]/g, '\\\\$&'); }
21
+ function selectorFor(el){
22
+ if (!el || el.nodeType !== 1) return undefined;
23
+ var tid = el.getAttribute && el.getAttribute('data-testid');
24
+ if (tid) return '[data-testid="' + esc(tid) + '"]';
25
+ if (el.id) return '#' + esc(el.id);
26
+ var parts = [], cur = el, depth = 0;
27
+ while (cur && cur.nodeType === 1 && depth < 4) {
28
+ var part = cur.tagName.toLowerCase();
29
+ var parent = cur.parentElement;
30
+ if (parent) {
31
+ var sibs = Array.prototype.filter.call(parent.children, function(c){ return c.tagName === cur.tagName; });
32
+ if (sibs.length > 1) part += ':nth-of-type(' + (sibs.indexOf(cur) + 1) + ')';
33
+ }
34
+ parts.unshift(part);
35
+ cur = cur.parentElement;
36
+ depth++;
37
+ }
38
+ return parts.join(' > ');
39
+ }
40
+ function labelFor(el){
41
+ if (!el || !el.getAttribute) return undefined;
42
+ var a = el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('name');
43
+ if (a) return String(a).slice(0, 80);
44
+ var t = (el.innerText || el.value || '').trim();
45
+ return t ? t.slice(0, 80) : undefined;
46
+ }
47
+ function isSensitive(el){
48
+ var type = ((el.getAttribute && el.getAttribute('type')) || '').toLowerCase();
49
+ if (type === 'password') return true;
50
+ var ac = ((el.getAttribute && el.getAttribute('autocomplete')) || '').toLowerCase();
51
+ if (/password|cc-|one-time-code/.test(ac)) return true;
52
+ var idn = (((el.name || '') + ' ' + (el.id || '')).toLowerCase());
53
+ return /pass|otp|secret|token|card|cvv|ssn/.test(idn);
54
+ }
55
+ function send(p){ try { window.__lnarRecord(JSON.stringify(p)); } catch (e) {} }
56
+ document.addEventListener('click', function(e){
57
+ var el = e.target;
58
+ send({ kind: 'click', x: Math.round(e.clientX), y: Math.round(e.clientY), selector: selectorFor(el), label: labelFor(el) });
59
+ }, true);
60
+ document.addEventListener('change', function(e){
61
+ var el = e.target;
62
+ if (!el || !('value' in el)) return;
63
+ var sens = isSensitive(el);
64
+ send({ kind: 'input', selector: selectorFor(el), label: labelFor(el), sensitive: sens, value: sens ? '***' : String(el.value).slice(0, MAXV) });
65
+ }, true);
66
+ document.addEventListener('submit', function(e){
67
+ send({ kind: 'submit', selector: selectorFor(e.target), label: labelFor(e.target) });
68
+ }, true);
69
+ document.addEventListener('keydown', function(e){
70
+ if (e.key === 'Enter' || e.key === 'Tab' || e.key === 'Escape') {
71
+ send({ kind: 'keydown', key: e.key, selector: selectorFor(e.target) });
72
+ }
73
+ }, true);
74
+ }`;
75
+ /**
76
+ * 録画状態を保持し、ページ内イベント / ナビゲーションを RecordedAction に変換する。
77
+ */
78
+ export class Recorder {
79
+ options;
80
+ actions = [];
81
+ screenshots = [];
82
+ startMs;
83
+ now;
84
+ maxScreenshots;
85
+ shotCount = 0;
86
+ constructor(options) {
87
+ this.options = options;
88
+ this.now = options.now ?? (() => Date.now());
89
+ this.maxScreenshots = options.maxScreenshots ?? 200;
90
+ this.startMs = this.now();
91
+ }
92
+ async capture(page) {
93
+ if (this.shotCount >= this.maxScreenshots)
94
+ return undefined;
95
+ // 採番は await の前に同期的に予約する。クリック/遷移/入力が近接して発火すると
96
+ // capture が並行実行されるため、ここで予約しないと同じ番号を取り合い、ファイルを
97
+ // 上書きして manifest に重複が出る (実データで確認済みの不具合)。
98
+ const index = ++this.shotCount;
99
+ const name = `${String(index).padStart(4, '0')}.png`;
100
+ try {
101
+ const bytes = await page.screenshot({ type: 'png' });
102
+ await writeFile(join(this.options.screenshotsDir, name), bytes);
103
+ this.screenshots.push(name);
104
+ return name;
105
+ }
106
+ catch {
107
+ return undefined; // ナビゲーション中などは握り潰す (予約番号は欠番になる)
108
+ }
109
+ }
110
+ /** ページ内イベントを受けてアクションを記録する。 */
111
+ async handleInPage(page, payloadJson) {
112
+ let payload;
113
+ try {
114
+ payload = JSON.parse(payloadJson);
115
+ }
116
+ catch {
117
+ return;
118
+ }
119
+ const screenshot = await this.capture(page);
120
+ this.actions.push({
121
+ type: payload.kind,
122
+ atMs: this.now() - this.startMs,
123
+ url: page.url(),
124
+ selector: payload.selector,
125
+ label: payload.label,
126
+ value: payload.value,
127
+ sensitive: payload.sensitive,
128
+ x: payload.x,
129
+ y: payload.y,
130
+ key: payload.key,
131
+ screenshot,
132
+ });
133
+ }
134
+ /** main frame のナビゲーションを記録する。 */
135
+ async handleNavigate(page, url) {
136
+ const screenshot = await this.capture(page);
137
+ this.actions.push({
138
+ type: 'navigate',
139
+ atMs: this.now() - this.startMs,
140
+ url,
141
+ screenshot,
142
+ });
143
+ }
144
+ }
145
+ /**
146
+ * context にレコーダを取り付ける。exposeBinding → addInitScript の順で行うこと。
147
+ * 取り付け後に作成/遷移したページが記録対象になる。
148
+ */
149
+ export async function attachRecorder(context, recorder) {
150
+ await context.exposeBinding('__lnarRecord', (source, payloadJson) => {
151
+ // 戻り値を待たない (ページ側 send は fire-and-forget)。
152
+ void recorder.handleInPage(source.page, payloadJson);
153
+ });
154
+ // INIT_SCRIPT は関数式。addInitScript は文字列をソースとして評価するだけで呼び出さ
155
+ // ないため、自己実行する形で渡す。
156
+ await context.addInitScript({ content: `(${INIT_SCRIPT})()` });
157
+ const wirePage = (page) => {
158
+ let lastUrl = '';
159
+ page.on('framenavigated', (frame) => {
160
+ if (frame !== page.mainFrame())
161
+ return;
162
+ const url = frame.url();
163
+ if (!url || url === 'about:blank' || url === lastUrl)
164
+ return;
165
+ lastUrl = url;
166
+ void recorder.handleNavigate(page, url);
167
+ });
168
+ };
169
+ for (const page of context.pages())
170
+ wirePage(page);
171
+ context.on('page', wirePage);
172
+ }
173
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/recording/capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { type BrowserContext, type Page } from 'playwright';
2
+ import type { RecordedAction, RecordingManifest } from './types.js';
3
+ export interface RecordSessionOptions {
4
+ readonly outDir: string;
5
+ readonly name: string;
6
+ readonly startUrl: string | null;
7
+ readonly profileDir: string;
8
+ readonly viewport?: {
9
+ readonly width: number;
10
+ readonly height: number;
11
+ };
12
+ readonly headless?: boolean;
13
+ readonly createdAt?: string;
14
+ /** 動画を記録する (既定 false)。解析では未使用・サイズ大のため既定で取得しない。 */
15
+ readonly recordVideo?: boolean;
16
+ /** Playwright trace を記録する (既定 false)。同上の理由で既定で取得しない。 */
17
+ readonly recordTrace?: boolean;
18
+ /** 停止シグナル。resolve でバンドル化に進む。 */
19
+ readonly waitForStop: (page: Page, context: BrowserContext) => Promise<void>;
20
+ }
21
+ export interface RecordSessionResult {
22
+ readonly dir: string;
23
+ readonly manifest: RecordingManifest;
24
+ readonly actions: ReadonlyArray<RecordedAction>;
25
+ }
26
+ /** 録画を実行し、バンドルディレクトリへ書き出して結果を返す。 */
27
+ export declare function recordSession(options: RecordSessionOptions): Promise<RecordSessionResult>;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 録画セッションのオーケストレーション。
3
+ *
4
+ * ローカルブラウザを起動 → ユーザー操作を記録 → 停止シグナルで終了 → バンドル書き出し。
5
+ * 停止条件 (`waitForStop`) は呼び出し側から注入し、本体をテスト可能に保つ。
6
+ */
7
+ import { copyFile, mkdir } from 'node:fs/promises';
8
+ import { join } from 'node:path';
9
+ import { chromium } from 'playwright';
10
+ import { buildManifest, writeBundleMetadata } from './bundle.js';
11
+ import { attachRecorder, Recorder } from './capture.js';
12
+ const DEFAULT_VIEWPORT = { width: 1280, height: 800 };
13
+ /** 録画を実行し、バンドルディレクトリへ書き出して結果を返す。 */
14
+ export async function recordSession(options) {
15
+ const viewport = options.viewport ?? DEFAULT_VIEWPORT;
16
+ const screenshotsDir = join(options.outDir, 'screenshots');
17
+ await mkdir(screenshotsDir, { recursive: true });
18
+ const context = await chromium.launchPersistentContext(options.profileDir, {
19
+ headless: options.headless ?? false,
20
+ viewport,
21
+ // 動画は既定で記録しない (S3 へ送らない / プライバシー・サイズ対策)。
22
+ recordVideo: options.recordVideo
23
+ ? { dir: join(options.outDir, '.video-tmp'), size: viewport }
24
+ : undefined,
25
+ });
26
+ const recorder = new Recorder({ screenshotsDir });
27
+ let traceSaved = false;
28
+ try {
29
+ if (options.recordTrace) {
30
+ await context.tracing.start({ screenshots: true, snapshots: true });
31
+ }
32
+ await attachRecorder(context, recorder);
33
+ const page = context.pages()[0] ?? (await context.newPage());
34
+ if (options.startUrl) {
35
+ await page.goto(options.startUrl, { waitUntil: 'domcontentloaded' });
36
+ }
37
+ await options.waitForStop(page, context);
38
+ if (options.recordTrace) {
39
+ try {
40
+ await context.tracing.stop({ path: join(options.outDir, 'trace.zip') });
41
+ traceSaved = true;
42
+ }
43
+ catch {
44
+ traceSaved = false;
45
+ }
46
+ }
47
+ // 動画は context.close() でフラッシュされる。close 前に参照を取得しておく。
48
+ const video = options.recordVideo ? page.video() : null;
49
+ await context.close();
50
+ let videoName = null;
51
+ if (video) {
52
+ try {
53
+ const src = await video.path();
54
+ await copyFile(src, join(options.outDir, 'video.webm'));
55
+ videoName = 'video.webm';
56
+ }
57
+ catch {
58
+ videoName = null;
59
+ }
60
+ }
61
+ const manifest = buildManifest({
62
+ name: options.name,
63
+ startUrl: options.startUrl,
64
+ createdAt: options.createdAt ?? new Date().toISOString(),
65
+ viewport,
66
+ actions: recorder.actions,
67
+ video: videoName,
68
+ trace: traceSaved ? 'trace.zip' : null,
69
+ screenshots: recorder.screenshots,
70
+ });
71
+ await writeBundleMetadata(options.outDir, manifest, recorder.actions);
72
+ return { dir: options.outDir, manifest, actions: recorder.actions };
73
+ }
74
+ finally {
75
+ // 例外時も確実に閉じる (close 済みなら no-op 相当)。
76
+ if (context.browser()?.isConnected()) {
77
+ await context.close().catch(() => { });
78
+ }
79
+ }
80
+ }
81
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/recording/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * デモ録画 (Demo-to-MCP / Phase 1) の型。
3
+ *
4
+ * ユーザーがローカルブラウザで実演した操作を構造化して保存し、後段 (Phase 2) で
5
+ * gemini-3.5-flash が「タスク手順書 (playbook)」へ解析するための素材にする。
6
+ */
7
+ /** ユーザー操作 1 件。座標はキャプチャ時のビューポートピクセル。 */
8
+ export interface RecordedAction {
9
+ /** 操作種別。 */
10
+ readonly type: 'navigate' | 'click' | 'input' | 'submit' | 'keydown';
11
+ /** 録画開始からの相対ミリ秒。 */
12
+ readonly atMs: number;
13
+ /** 操作時点の URL。 */
14
+ readonly url: string;
15
+ /** 対象要素の CSS セレクタ (best-effort)。navigate では無い。 */
16
+ readonly selector?: string;
17
+ /** 対象要素の可視テキスト/ラベル (要素特定のヒント)。 */
18
+ readonly label?: string;
19
+ /** 入力値。password 等の機微フィールドは "***" にマスクされる。 */
20
+ readonly value?: string;
21
+ /** 入力フィールドが機微 (password 等) だったか。 */
22
+ readonly sensitive?: boolean;
23
+ /** click のビューポート座標。 */
24
+ readonly x?: number;
25
+ readonly y?: number;
26
+ /** keydown のキー。 */
27
+ readonly key?: string;
28
+ /** このアクション直後に保存したキーフレーム画像のファイル名。 */
29
+ readonly screenshot?: string;
30
+ }
31
+ /** 録画バンドルのメタデータ (manifest.json)。API へ JSON で送る。 */
32
+ export interface RecordingManifest {
33
+ /** スキーマバージョン。 */
34
+ readonly version: 1;
35
+ /** 録画の表示名。 */
36
+ readonly name: string;
37
+ /** 録画開始 URL。 */
38
+ readonly startUrl: string | null;
39
+ /** ISO8601 の作成時刻。 */
40
+ readonly createdAt: string;
41
+ /** 記録時のビューポート。座標スケールの基準。 */
42
+ readonly viewport: {
43
+ readonly width: number;
44
+ readonly height: number;
45
+ };
46
+ /** 記録したアクション総数。 */
47
+ readonly actionCount: number;
48
+ /** 動画ファイル名 (取得できた場合)。 */
49
+ readonly video: string | null;
50
+ /** Playwright trace.zip ファイル名 (取得できた場合)。 */
51
+ readonly trace: string | null;
52
+ /** キーフレーム画像ファイル名の一覧。 */
53
+ readonly screenshots: ReadonlyArray<string>;
54
+ }
55
+ /** 録画 1 回の成果物 (バンドル内容)。 */
56
+ export interface RecordingBundle {
57
+ readonly manifest: RecordingManifest;
58
+ readonly actions: ReadonlyArray<RecordedAction>;
59
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * デモ録画 (Demo-to-MCP / Phase 1) の型。
3
+ *
4
+ * ユーザーがローカルブラウザで実演した操作を構造化して保存し、後段 (Phase 2) で
5
+ * gemini-3.5-flash が「タスク手順書 (playbook)」へ解析するための素材にする。
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/recording/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,19 @@
1
+ export interface PendingRun {
2
+ id: string;
3
+ recording_id: string;
4
+ name: string;
5
+ /** 録画の開始 URL。ここからブラウザを開いて実行を始める。 */
6
+ start_url?: string | null;
7
+ /** 作業の目的。実行時の指示文に含める。 */
8
+ purpose?: string | null;
9
+ params: Record<string, unknown> | null;
10
+ /** 解析済み playbook (parsePlaybook で検証して使う)。 */
11
+ playbook: unknown;
12
+ }
13
+ /** 自分が実行すべき pending run の一覧 (playbook 同梱)。 */
14
+ export declare const listPendingRuns: (baseUrl: string, apiKey: string) => Promise<PendingRun[]>;
15
+ /** ローカル実行用の Gemini API キーを取得する (K1)。 */
16
+ export declare const getRunCredential: (baseUrl: string, apiKey: string) => Promise<string>;
17
+ export declare const claimRun: (baseUrl: string, apiKey: string, runId: string, hostname: string) => Promise<void>;
18
+ export declare const reportRunResult: (baseUrl: string, apiKey: string, runId: string, resultText: string) => Promise<void>;
19
+ export declare const reportRunFailed: (baseUrl: string, apiKey: string, runId: string, error: string) => Promise<void>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 録画 run-job の REST クライアント (Demo-to-MCP / ハイブリッド R2)。
3
+ *
4
+ * daemon が「自分宛の pending ジョブ取得 → claim → ローカル実行 → 結果報告」を行うための
5
+ * API 呼び出し群。Gemini 資格情報 (K1) もここで取得する。
6
+ */
7
+ import { ApiError } from './api-client.js';
8
+ const authHeaders = (apiKey) => ({
9
+ Authorization: `Bearer ${apiKey}`,
10
+ });
11
+ async function jsonOrThrow(response) {
12
+ if (!response.ok) {
13
+ throw new ApiError(response.status, await response.text().catch(() => ''));
14
+ }
15
+ return response.json();
16
+ }
17
+ /** 自分が実行すべき pending run の一覧 (playbook 同梱)。 */
18
+ export const listPendingRuns = async (baseUrl, apiKey) => {
19
+ const url = new URL('/v1/recordings/runs/pending', baseUrl).toString();
20
+ const response = await fetch(url, { headers: authHeaders(apiKey) });
21
+ return (await jsonOrThrow(response));
22
+ };
23
+ /** ローカル実行用の Gemini API キーを取得する (K1)。 */
24
+ export const getRunCredential = async (baseUrl, apiKey) => {
25
+ const url = new URL('/v1/recordings/runs/credential', baseUrl).toString();
26
+ const response = await fetch(url, { headers: authHeaders(apiKey) });
27
+ const body = (await jsonOrThrow(response));
28
+ return body.api_key;
29
+ };
30
+ const postRun = async (baseUrl, apiKey, path, body) => {
31
+ const url = new URL(path, baseUrl).toString();
32
+ const response = await fetch(url, {
33
+ method: 'POST',
34
+ headers: { ...authHeaders(apiKey), 'Content-Type': 'application/json' },
35
+ body: JSON.stringify(body),
36
+ });
37
+ await jsonOrThrow(response);
38
+ };
39
+ export const claimRun = (baseUrl, apiKey, runId, hostname) => postRun(baseUrl, apiKey, `/v1/recordings/runs/${runId}/claim`, { hostname });
40
+ export const reportRunResult = (baseUrl, apiKey, runId, resultText) => postRun(baseUrl, apiKey, `/v1/recordings/runs/${runId}/result`, {
41
+ result_text: resultText,
42
+ });
43
+ export const reportRunFailed = (baseUrl, apiKey, runId, error) => postRun(baseUrl, apiKey, `/v1/recordings/runs/${runId}/failed`, { error });
44
+ //# sourceMappingURL=run-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-client.js","sourceRoot":"","sources":["../src/run-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAe3C,MAAM,WAAW,GAAG,CAAC,MAAc,EAA0B,EAAE,CAAC,CAAC;IAC/D,aAAa,EAAE,UAAU,MAAM,EAAE;CAClC,CAAC,CAAC;AAEH,KAAK,UAAU,WAAW,CAAC,QAAkB;IAC3C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,8CAA8C;AAC9C,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,OAAe,EAAE,MAAc,EAAyB,EAAE;IAC9F,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACvE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpE,OAAO,CAAC,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAiB,CAAC;AACvD,CAAC,CAAC;AAEF,wCAAwC;AACxC,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,OAAe,EAAE,MAAc,EAAmB,EAAE;IACzF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,gCAAgC,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC1E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,CAAC,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAwB,CAAC;IAClE,OAAO,IAAI,CAAC,OAAO,CAAC;AACtB,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,KAAK,EACnB,OAAe,EACf,MAAc,EACd,IAAY,EACZ,IAA6B,EACd,EAAE;IACjB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC9C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE;QACvE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IACH,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,OAAe,EACf,MAAc,EACd,KAAa,EACb,QAAgB,EACD,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,uBAAuB,KAAK,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;AAEjG,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,OAAe,EACf,MAAc,EACd,KAAa,EACb,UAAkB,EACH,EAAE,CACjB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,uBAAuB,KAAK,SAAS,EAAE;IAC9D,WAAW,EAAE,UAAU;CACxB,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,OAAe,EACf,MAAc,EACd,KAAa,EACb,KAAa,EACE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,uBAAuB,KAAK,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 録画 run-job のローカル実行ワーカー (Demo-to-MCP / ハイブリッド R2)。
3
+ *
4
+ * daemon から呼ばれ、自分宛の pending run を順に
5
+ * claim → 鍵取得(K1) → playbook をローカルブラウザ実行 → 結果/失敗を報告
6
+ * する。computer use の Gemini 呼び出しはローカルから直接行われ、スクショは
7
+ * Lnar を経由しない。
8
+ *
9
+ * 依存 (API クライアント / 実行器) は注入可能にしてテストしやすくしている。
10
+ */
11
+ import { claimRun, getRunCredential, listPendingRuns, type PendingRun, reportRunFailed, reportRunResult } from './run-client.js';
12
+ import { runPlaybook } from './runtime/runner.js';
13
+ export interface RunWorkerDeps {
14
+ listPendingRuns: typeof listPendingRuns;
15
+ claimRun: typeof claimRun;
16
+ getRunCredential: typeof getRunCredential;
17
+ reportRunResult: typeof reportRunResult;
18
+ reportRunFailed: typeof reportRunFailed;
19
+ runPlaybook: typeof runPlaybook;
20
+ log?: (message: string) => void;
21
+ }
22
+ /** pending run を 1 件実行して結果を報告する。 */
23
+ export declare function executeRun(baseUrl: string, token: string, hostname: string, run: PendingRun, deps: RunWorkerDeps): Promise<void>;
24
+ /** 自分宛の pending run をすべて実行する。実行件数を返す。 */
25
+ export declare function executePendingRuns(baseUrl: string, token: string, hostname: string, deps?: RunWorkerDeps): Promise<number>;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * 録画 run-job のローカル実行ワーカー (Demo-to-MCP / ハイブリッド R2)。
3
+ *
4
+ * daemon から呼ばれ、自分宛の pending run を順に
5
+ * claim → 鍵取得(K1) → playbook をローカルブラウザ実行 → 結果/失敗を報告
6
+ * する。computer use の Gemini 呼び出しはローカルから直接行われ、スクショは
7
+ * Lnar を経由しない。
8
+ *
9
+ * 依存 (API クライアント / 実行器) は注入可能にしてテストしやすくしている。
10
+ */
11
+ import { ApiError } from './api-client.js';
12
+ import { claimRun, getRunCredential, listPendingRuns, reportRunFailed, reportRunResult, } from './run-client.js';
13
+ import { parsePlaybook } from './runtime/playbook.js';
14
+ import { runPlaybook } from './runtime/runner.js';
15
+ const defaultDeps = () => ({
16
+ listPendingRuns,
17
+ claimRun,
18
+ getRunCredential,
19
+ reportRunResult,
20
+ reportRunFailed,
21
+ runPlaybook,
22
+ });
23
+ /** run.params (任意値) を文字列パラメータに正規化する。 */
24
+ function toStringParams(params) {
25
+ const out = {};
26
+ for (const [k, v] of Object.entries(params ?? {}))
27
+ out[k] = String(v);
28
+ return out;
29
+ }
30
+ function outcomeMessage(outcome) {
31
+ if (outcome.completed)
32
+ return outcome.finalText ?? 'タスクを完了しました。';
33
+ if (outcome.aborted)
34
+ return 'タスクは安全確認の拒否により中断されました。';
35
+ return `タスクは上限ターン数に達しました (${outcome.turns} turns)。`;
36
+ }
37
+ /** pending run を 1 件実行して結果を報告する。 */
38
+ export async function executeRun(baseUrl, token, hostname, run, deps) {
39
+ const log = deps.log ?? (() => { });
40
+ // claim は排他取得。競合 (409 = 別 daemon が先に取得) のときだけスキップする
41
+ // (走っている run を上書きしないため、failed 報告もしない)。それ以外 (認証切れ/
42
+ // ネットワーク/5xx 等) は握りつぶさず上位へ伝播し、daemon 側の try/catch で
43
+ // 可視化する (黙ってスキップすると run が pending のまま詰まり障害検知が遅れる)。
44
+ try {
45
+ await deps.claimRun(baseUrl, token, run.id, hostname);
46
+ }
47
+ catch (err) {
48
+ if (err instanceof ApiError && err.status === 409) {
49
+ log(`run ${run.id} claim skipped (409 conflict)`);
50
+ return;
51
+ }
52
+ throw err;
53
+ }
54
+ // claim 後の実行・報告。ここでの失敗は run を failed として報告する。
55
+ try {
56
+ const apiKey = await deps.getRunCredential(baseUrl, token);
57
+ const playbook = parsePlaybook(run.playbook);
58
+ const outcome = await deps.runPlaybook(playbook, toStringParams(run.params), {
59
+ apiKey,
60
+ logger: log,
61
+ startUrl: run.start_url ?? null,
62
+ purpose: run.purpose ?? null,
63
+ });
64
+ if (outcome.completed) {
65
+ await deps.reportRunResult(baseUrl, token, run.id, outcomeMessage(outcome));
66
+ }
67
+ else {
68
+ await deps.reportRunFailed(baseUrl, token, run.id, outcomeMessage(outcome));
69
+ }
70
+ }
71
+ catch (err) {
72
+ const message = err instanceof Error ? err.message : String(err);
73
+ log(`run ${run.id} failed: ${message}`);
74
+ await deps.reportRunFailed(baseUrl, token, run.id, message).catch(() => { });
75
+ }
76
+ }
77
+ /** 自分宛の pending run をすべて実行する。実行件数を返す。 */
78
+ export async function executePendingRuns(baseUrl, token, hostname, deps = defaultDeps()) {
79
+ const runs = await deps.listPendingRuns(baseUrl, token);
80
+ for (const run of runs) {
81
+ await executeRun(baseUrl, token, hostname, run, deps);
82
+ }
83
+ return runs.length;
84
+ }
85
+ //# sourceMappingURL=run-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-worker.js","sourceRoot":"","sources":["../src/run-worker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Gemini の function_call を Playwright 操作へ変換して実行する。
3
+ * 座標は正規化 (0-999) で来るため実ビューポートへスケールする。
4
+ */
5
+ import type { Page } from 'playwright';
6
+ import type { FunctionCallStep } from './types.js';
7
+ export interface ActionResult {
8
+ readonly name: string;
9
+ readonly callId: string;
10
+ readonly detail: Record<string, unknown>;
11
+ }
12
+ /** 1 つの function_call を実行する。未知のアクションは警告にとどめループを止めない。 */
13
+ export declare function executeAction(page: Page, step: FunctionCallStep): Promise<ActionResult>;