@lnar/cli 0.0.1-dev.f1de48c → 0.0.1

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 (72) hide show
  1. package/dist/api-client.d.ts +19 -0
  2. package/dist/api-client.js +38 -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/browser.d.ts +1 -0
  8. package/dist/browser.js +68 -0
  9. package/dist/browser.js.map +1 -0
  10. package/dist/capture-client.d.ts +13 -0
  11. package/dist/capture-client.js +37 -0
  12. package/dist/capture-client.js.map +1 -0
  13. package/dist/capture-worker.d.ts +14 -0
  14. package/dist/capture-worker.js +105 -0
  15. package/dist/capture-worker.js.map +1 -0
  16. package/dist/cli.js +21 -0
  17. package/dist/cli.js.map +1 -1
  18. package/dist/commands/daemon.js +27 -0
  19. package/dist/commands/daemon.js.map +1 -1
  20. package/dist/commands/login.d.ts +2 -0
  21. package/dist/commands/login.js +19 -9
  22. package/dist/commands/login.js.map +1 -1
  23. package/dist/commands/record.d.ts +7 -0
  24. package/dist/commands/record.js +117 -0
  25. package/dist/commands/record.js.map +1 -0
  26. package/dist/commands/up.js +19 -0
  27. package/dist/commands/up.js.map +1 -1
  28. package/dist/recording/bundle.d.ts +25 -0
  29. package/dist/recording/bundle.js +55 -0
  30. package/dist/recording/bundle.js.map +1 -0
  31. package/dist/recording/capture.d.ts +53 -0
  32. package/dist/recording/capture.js +163 -0
  33. package/dist/recording/capture.js.map +1 -0
  34. package/dist/recording/session.d.ts +30 -0
  35. package/dist/recording/session.js +47 -0
  36. package/dist/recording/session.js.map +1 -0
  37. package/dist/recording/types.d.ts +59 -0
  38. package/dist/recording/types.js +8 -0
  39. package/dist/recording/types.js.map +1 -0
  40. package/dist/run-client.d.ts +19 -0
  41. package/dist/run-client.js +44 -0
  42. package/dist/run-client.js.map +1 -0
  43. package/dist/run-worker.d.ts +25 -0
  44. package/dist/run-worker.js +85 -0
  45. package/dist/run-worker.js.map +1 -0
  46. package/dist/runtime/actions.d.ts +13 -0
  47. package/dist/runtime/actions.js +107 -0
  48. package/dist/runtime/actions.js.map +1 -0
  49. package/dist/runtime/client.d.ts +3 -0
  50. package/dist/runtime/client.js +85 -0
  51. package/dist/runtime/client.js.map +1 -0
  52. package/dist/runtime/login.d.ts +20 -0
  53. package/dist/runtime/login.js +34 -0
  54. package/dist/runtime/login.js.map +1 -0
  55. package/dist/runtime/loop.d.ts +28 -0
  56. package/dist/runtime/loop.js +68 -0
  57. package/dist/runtime/loop.js.map +1 -0
  58. package/dist/runtime/playbook.d.ts +49 -0
  59. package/dist/runtime/playbook.js +73 -0
  60. package/dist/runtime/playbook.js.map +1 -0
  61. package/dist/runtime/runner.d.ts +20 -0
  62. package/dist/runtime/runner.js +54 -0
  63. package/dist/runtime/runner.js.map +1 -0
  64. package/dist/runtime/types.d.ts +69 -0
  65. package/dist/runtime/types.js +10 -0
  66. package/dist/runtime/types.js.map +1 -0
  67. package/dist/service/files.d.ts +14 -2
  68. package/dist/service/files.js +16 -4
  69. package/dist/service/files.js.map +1 -1
  70. package/dist/service/install.js +11 -3
  71. package/dist/service/install.js.map +1 -1
  72. package/package.json +12 -2
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 録画セッションのオーケストレーション。
3
+ *
4
+ * ローカルブラウザを起動 → ユーザー操作を記録 → 停止シグナルで終了 → バンドル内容を返す。
5
+ * キーフレームはメモリ上の Buffer として保持し、ディスクには一切書かない。
6
+ * 停止条件 (`waitForStop`) は呼び出し側から注入し、本体をテスト可能に保つ。
7
+ */
8
+ import { chromium } from 'playwright';
9
+ import { buildManifest } from './bundle.js';
10
+ import { attachRecorder, Recorder } from './capture.js';
11
+ const DEFAULT_VIEWPORT = { width: 1280, height: 800 };
12
+ /** 録画を実行し、バンドル内容 (メモリ上) を返す。 */
13
+ export async function recordSession(options) {
14
+ const viewport = options.viewport ?? DEFAULT_VIEWPORT;
15
+ // 動画 / trace は記録しない (Playwright が必ずディスクへ書き出すため、
16
+ // 録画内容を一瞬もローカルに残さない方針と両立しない)。
17
+ const context = await chromium.launchPersistentContext(options.profileDir, {
18
+ headless: options.headless ?? false,
19
+ viewport,
20
+ });
21
+ const recorder = new Recorder();
22
+ try {
23
+ await attachRecorder(context, recorder);
24
+ const page = context.pages()[0] ?? (await context.newPage());
25
+ if (options.startUrl) {
26
+ await page.goto(options.startUrl, { waitUntil: 'domcontentloaded' });
27
+ }
28
+ await options.waitForStop(page, context);
29
+ await context.close();
30
+ const manifest = buildManifest({
31
+ name: options.name,
32
+ startUrl: options.startUrl,
33
+ createdAt: options.createdAt ?? new Date().toISOString(),
34
+ viewport,
35
+ actions: recorder.actions,
36
+ screenshots: recorder.screenshots,
37
+ });
38
+ return { manifest, actions: recorder.actions, frames: recorder.frames };
39
+ }
40
+ finally {
41
+ // 例外時も確実に閉じる (close 済みなら no-op 相当)。
42
+ if (context.browser()?.isConnected()) {
43
+ await context.close().catch(() => { });
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/recording/session.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAuB,QAAQ,EAAa,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAiB,QAAQ,EAAE,MAAM,cAAc,CAAC;AAoBvE,MAAM,gBAAgB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAW,CAAC;AAE/D,gCAAgC;AAChC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA6B;IAC/D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,gBAAgB,CAAC;IAEtD,gDAAgD;IAChD,8BAA8B;IAC9B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,uBAAuB,CAAC,OAAO,CAAC,UAAU,EAAE;QACzE,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,KAAK;QACnC,QAAQ;KACT,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,IAAI,CAAC;QACH,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAExC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7D,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEzC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QAEtB,MAAM,QAAQ,GAAG,aAAa,CAAC;YAC7B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACxD,QAAQ;YACR,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,WAAW,EAAE,QAAQ,CAAC,WAAW;SAClC,CAAC,CAAC;QAEH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC1E,CAAC;YAAS,CAAC;QACT,oCAAoC;QACpC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC;YACrC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -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>;
@@ -0,0 +1,107 @@
1
+ const NORM = 1000;
2
+ function toPixel(value, size) {
3
+ return Math.round(((value ?? 0) / NORM) * size);
4
+ }
5
+ function scaleScroll(magnitude, size) {
6
+ return magnitude ?? Math.round(size / 3);
7
+ }
8
+ async function applyScroll(page, args, vp) {
9
+ // スクロール原点が未指定なら (0,0) ではなくビューポート中央を使う。
10
+ const x = args.x === undefined ? Math.round(vp.width / 2) : toPixel(args.x, vp.width);
11
+ const y = args.y === undefined ? Math.round(vp.height / 2) : toPixel(args.y, vp.height);
12
+ const dir = args.direction ?? 'down';
13
+ const dx = dir === 'right'
14
+ ? scaleScroll(args.magnitude_in_pixels, vp.width)
15
+ : dir === 'left'
16
+ ? -scaleScroll(args.magnitude_in_pixels, vp.width)
17
+ : 0;
18
+ const dy = dir === 'down'
19
+ ? scaleScroll(args.magnitude_in_pixels, vp.height)
20
+ : dir === 'up'
21
+ ? -scaleScroll(args.magnitude_in_pixels, vp.height)
22
+ : 0;
23
+ await page.mouse.move(x, y);
24
+ await page.mouse.wheel(dx, dy);
25
+ }
26
+ /** 1 つの function_call を実行する。未知のアクションは警告にとどめループを止めない。 */
27
+ export async function executeAction(page, step) {
28
+ const args = step.arguments;
29
+ const vp = page.viewportSize() ?? { width: 1280, height: 800 };
30
+ const x = toPixel(args.x, vp.width);
31
+ const y = toPixel(args.y, vp.height);
32
+ // クリック系は座標必須。未指定で (0,0) を誤クリックしないよう no-op で返す。
33
+ const needsXY = step.name === 'click' || step.name === 'double_click' || step.name === 'right_click';
34
+ if (needsXY && (args.x === undefined || args.y === undefined)) {
35
+ console.error(`[actions] ${step.name}: 座標未指定のためスキップ`);
36
+ return {
37
+ name: step.name,
38
+ callId: step.id,
39
+ detail: { url: page.url(), skipped: 'missing coordinates' },
40
+ };
41
+ }
42
+ switch (step.name) {
43
+ case 'click':
44
+ await page.mouse.click(x, y);
45
+ break;
46
+ case 'double_click':
47
+ await page.mouse.dblclick(x, y);
48
+ break;
49
+ case 'right_click':
50
+ await page.mouse.click(x, y, { button: 'right' });
51
+ break;
52
+ case 'type':
53
+ if (args.text)
54
+ await page.keyboard.type(args.text);
55
+ if (args.press_enter)
56
+ await page.keyboard.press('Enter');
57
+ break;
58
+ case 'navigate':
59
+ if (args.url)
60
+ await page.goto(args.url, { waitUntil: 'domcontentloaded' });
61
+ break;
62
+ case 'scroll':
63
+ await applyScroll(page, args, vp);
64
+ break;
65
+ case 'press_key':
66
+ if (args.key)
67
+ await page.keyboard.press(args.key);
68
+ break;
69
+ case 'hotkey':
70
+ if (args.keys && args.keys.length > 0)
71
+ await page.keyboard.press(args.keys.join('+'));
72
+ break;
73
+ case 'drag_and_drop': {
74
+ if (args.start_x === undefined ||
75
+ args.start_y === undefined ||
76
+ args.end_x === undefined ||
77
+ args.end_y === undefined) {
78
+ console.error('[actions] drag_and_drop: 座標未指定のためスキップ');
79
+ return {
80
+ name: step.name,
81
+ callId: step.id,
82
+ detail: { url: page.url(), skipped: 'missing coordinates' },
83
+ };
84
+ }
85
+ await page.mouse.move(toPixel(args.start_x, vp.width), toPixel(args.start_y, vp.height));
86
+ await page.mouse.down();
87
+ await page.mouse.move(toPixel(args.end_x, vp.width), toPixel(args.end_y, vp.height));
88
+ await page.mouse.up();
89
+ break;
90
+ }
91
+ case 'go_back':
92
+ await page.goBack({ waitUntil: 'domcontentloaded' }).catch(() => { });
93
+ break;
94
+ case 'go_forward':
95
+ await page.goForward({ waitUntil: 'domcontentloaded' }).catch(() => { });
96
+ break;
97
+ case 'wait':
98
+ await page.waitForTimeout((args.seconds ?? 1) * 1000);
99
+ break;
100
+ case 'take_screenshot':
101
+ break; // 毎ターン loop が取得するため追加操作なし
102
+ default:
103
+ console.error(`[actions] 未対応のアクション: ${String(step.name)}`);
104
+ }
105
+ return { name: step.name, callId: step.id, detail: { url: page.url() } };
106
+ }
107
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/runtime/actions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ComputerUseClient } from './types.js';
2
+ /** GEMINI_API_KEY / GOOGLE_API_KEY から認証する computer use クライアントを生成する。 */
3
+ export declare function createGeminiClient(apiKey?: string): ComputerUseClient;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `@google/genai` (>= 2.0.0) の computer use interactions を ComputerUseClient に適合させる。
3
+ *
4
+ * 2.x は新 `steps` スキーマ。レスポンス:
5
+ * - function_call: { type:'function_call', id, name, arguments }
6
+ * - 最終テキスト : { type:'model_output', content:[{type:'text', text}] } (+ output_text)
7
+ * - thought ステップも混在 (無視)。完了は status==='completed'。
8
+ */
9
+ import { GoogleGenAI } from '@google/genai';
10
+ const MODEL = process.env.LNAR_COMPUTER_USE_MODEL ?? 'gemini-3.5-flash';
11
+ const TOOLS = [{ type: 'computer_use', environment: 'browser' }];
12
+ /** model_output / text ステップからテキストを取り出す。 */
13
+ function extractStepText(step) {
14
+ if (typeof step.text === 'string')
15
+ return step.text;
16
+ if (Array.isArray(step.content)) {
17
+ return step.content
18
+ .map((c) => {
19
+ const part = (c ?? {});
20
+ return typeof part.text === 'string' ? part.text : '';
21
+ })
22
+ .filter(Boolean)
23
+ .join('');
24
+ }
25
+ return '';
26
+ }
27
+ function normalize(raw) {
28
+ const obj = (raw ?? {});
29
+ const id = typeof obj.id === 'string' ? obj.id : '';
30
+ const rawSteps = Array.isArray(obj.steps)
31
+ ? obj.steps
32
+ : Array.isArray(obj.outputs)
33
+ ? obj.outputs
34
+ : [];
35
+ const steps = [];
36
+ for (const s of rawSteps) {
37
+ const step = (s ?? {});
38
+ if (step.type === 'function_call' && typeof step.name === 'string') {
39
+ steps.push({
40
+ type: 'function_call',
41
+ id: typeof step.id === 'string' ? step.id : '',
42
+ name: step.name,
43
+ arguments: (step.arguments ?? {}),
44
+ safety_decision: step.safety_decision,
45
+ });
46
+ }
47
+ else if (step.type === 'model_output' || step.type === 'text') {
48
+ const text = extractStepText(step);
49
+ if (text)
50
+ steps.push({ type: 'text', text });
51
+ }
52
+ }
53
+ if (!steps.some((s) => s.type === 'text') &&
54
+ typeof obj.output_text === 'string' &&
55
+ obj.output_text) {
56
+ steps.push({ type: 'text', text: obj.output_text });
57
+ }
58
+ if (!id) {
59
+ throw new Error('Gemini interactions API のレスポンスに id がありません。SDK バージョン/権限を確認してください。');
60
+ }
61
+ return { id, steps };
62
+ }
63
+ /** GEMINI_API_KEY / GOOGLE_API_KEY から認証する computer use クライアントを生成する。 */
64
+ export function createGeminiClient(apiKey) {
65
+ const key = apiKey ?? process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY;
66
+ if (!key) {
67
+ throw new Error('GEMINI_API_KEY (または GOOGLE_API_KEY) が未設定です。');
68
+ }
69
+ const ai = new GoogleGenAI({ apiKey: key });
70
+ const interactions = ai.interactions;
71
+ return {
72
+ async createInitial(input) {
73
+ return normalize(await interactions.create({ model: MODEL, input, tools: TOOLS }));
74
+ },
75
+ async continueFrom(previousInteractionId, input) {
76
+ return normalize(await interactions.create({
77
+ model: MODEL,
78
+ previous_interaction_id: previousInteractionId,
79
+ input,
80
+ tools: TOOLS,
81
+ }));
82
+ },
83
+ };
84
+ }
85
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * オンデマンドのログイン受け渡し (Demo-to-MCP / Phase 4)。
3
+ *
4
+ * MCP サーバー文脈ではターミナル対話ができない (stdio は MCP プロトコル専有) ため、
5
+ * ローカルの可視ブラウザでユーザーがログインするのを **ポーリングで検知して自動再開**する。
6
+ * 認証情報はサーバー側に一切保存しない。
7
+ */
8
+ import type { Page } from 'playwright';
9
+ /** 現在ページがログイン待ちかを簡易判定する。 */
10
+ export declare function detectLoginWall(page: Page): Promise<boolean>;
11
+ export interface WaitForLoginOptions {
12
+ readonly timeoutMs?: number;
13
+ readonly pollMs?: number;
14
+ readonly log?: (message: string) => void;
15
+ }
16
+ /**
17
+ * ログイン壁が解消されるまで待つ。可視ブラウザでユーザーがログインを完了すると解消する。
18
+ * 解消したら true、タイムアウトしたら false。
19
+ */
20
+ export declare function waitForLoginToClear(page: Page, options?: WaitForLoginOptions): Promise<boolean>;
@@ -0,0 +1,34 @@
1
+ const LOGIN_URL_HINTS = ['login', 'signin', 'sign-in', 'auth', 'sso', 'oauth'];
2
+ /** 現在ページがログイン待ちかを簡易判定する。 */
3
+ export async function detectLoginWall(page) {
4
+ const url = page.url().toLowerCase();
5
+ if (LOGIN_URL_HINTS.some((h) => url.includes(h)))
6
+ return true;
7
+ try {
8
+ return (await page.locator('input[type="password"]:visible').count()) > 0;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ /**
15
+ * ログイン壁が解消されるまで待つ。可視ブラウザでユーザーがログインを完了すると解消する。
16
+ * 解消したら true、タイムアウトしたら false。
17
+ */
18
+ export async function waitForLoginToClear(page, options = {}) {
19
+ const timeoutMs = options.timeoutMs ?? 5 * 60 * 1000;
20
+ const pollMs = options.pollMs ?? 1500;
21
+ const log = options.log ?? (() => { });
22
+ log(`🔐 ログインが必要です。ブラウザ画面でログインを完了してください: ${page.url()}`);
23
+ const start = Date.now();
24
+ while (Date.now() - start < timeoutMs) {
25
+ if (!(await detectLoginWall(page))) {
26
+ log('✅ ログインを検出しました。実行を再開します。');
27
+ return true;
28
+ }
29
+ await page.waitForTimeout(pollMs);
30
+ }
31
+ log('⏱️ ログイン待機がタイムアウトしました。');
32
+ return false;
33
+ }
34
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/runtime/login.ts"],"names":[],"mappings":"AASA,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AAE/E,4BAA4B;AAC5B,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAU;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IAC5E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAQD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAU,EACV,UAA+B,EAAE;IAEjC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IACrD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC;IACtC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAEtC,GAAG,CAAC,sCAAsC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,CAAC,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACnC,GAAG,CAAC,yBAAyB,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IACD,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAC7B,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Gemini computer use エージェントループ (Demo-to-MCP / Phase 4)。
3
+ *
4
+ * スクショ+指示 → function_call → Playwright 実行 → 新スクショ → 次ターン。
5
+ * ログイン要求 / 安全確認はフック (LoopHooks) 経由で外部に委ねる (MCP/CLI で差し替え)。
6
+ */
7
+ import type { Page } from 'playwright';
8
+ import { type ComputerUseClient } from './types.js';
9
+ export interface LoopHooks {
10
+ /** 各ターンのアクション通知 (ログ用)。 */
11
+ onAction?(turn: number, name: string, intent: string | undefined): void;
12
+ /** ログイン壁検出時に呼ばれ、解消を待つ。resolve で続行。 */
13
+ onLoginRequired(page: Page): Promise<void>;
14
+ /** safety_decision の確認。true で承認 (safety_acknowledgement)、false で中断。 */
15
+ onSafetyDecision(explanation: string): Promise<boolean>;
16
+ }
17
+ export interface RunOptions {
18
+ readonly task: string;
19
+ readonly maxTurns: number;
20
+ }
21
+ export interface RunOutcome {
22
+ readonly completed: boolean;
23
+ readonly turns: number;
24
+ readonly finalText: string | null;
25
+ readonly aborted: boolean;
26
+ }
27
+ /** タスクを computer use ループで実行する。 */
28
+ export declare function runTask(client: ComputerUseClient, page: Page, options: RunOptions, hooks: LoopHooks): Promise<RunOutcome>;