@lnar/cli 0.0.1-dev.46c4eb5 → 0.0.1-dev.47196fc
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/dist/api-client.d.ts +19 -0
- package/dist/api-client.js +38 -0
- package/dist/api-client.js.map +1 -1
- package/dist/auth.d.ts +20 -0
- package/dist/auth.js +74 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.js +16 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/daemon.js +13 -0
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/record.d.ts +7 -0
- package/dist/commands/record.js +117 -0
- package/dist/commands/record.js.map +1 -0
- package/dist/recording/bundle.d.ts +25 -0
- package/dist/recording/bundle.js +55 -0
- package/dist/recording/bundle.js.map +1 -0
- package/dist/recording/capture.d.ts +53 -0
- package/dist/recording/capture.js +163 -0
- package/dist/recording/capture.js.map +1 -0
- package/dist/recording/session.d.ts +30 -0
- package/dist/recording/session.js +47 -0
- package/dist/recording/session.js.map +1 -0
- package/dist/recording/types.d.ts +59 -0
- package/dist/recording/types.js +8 -0
- package/dist/recording/types.js.map +1 -0
- package/dist/run-client.d.ts +19 -0
- package/dist/run-client.js +44 -0
- package/dist/run-client.js.map +1 -0
- package/dist/run-worker.d.ts +25 -0
- package/dist/run-worker.js +85 -0
- package/dist/run-worker.js.map +1 -0
- package/dist/runtime/actions.d.ts +13 -0
- package/dist/runtime/actions.js +107 -0
- package/dist/runtime/actions.js.map +1 -0
- package/dist/runtime/client.d.ts +3 -0
- package/dist/runtime/client.js +85 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/login.d.ts +20 -0
- package/dist/runtime/login.js +34 -0
- package/dist/runtime/login.js.map +1 -0
- package/dist/runtime/loop.d.ts +28 -0
- package/dist/runtime/loop.js +68 -0
- package/dist/runtime/loop.js.map +1 -0
- package/dist/runtime/playbook.d.ts +49 -0
- package/dist/runtime/playbook.js +73 -0
- package/dist/runtime/playbook.js.map +1 -0
- package/dist/runtime/runner.d.ts +20 -0
- package/dist/runtime/runner.js +54 -0
- package/dist/runtime/runner.js.map +1 -0
- package/dist/runtime/types.d.ts +69 -0
- package/dist/runtime/types.js +10 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/service/files.d.ts +14 -2
- package/dist/service/files.js +16 -4
- package/dist/service/files.js.map +1 -1
- package/dist/service/install.js +11 -3
- package/dist/service/install.js.map +1 -1
- package/package.json +11 -1
|
@@ -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>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { executeAction } from './actions.js';
|
|
2
|
+
import { detectLoginWall } from './login.js';
|
|
3
|
+
import { isFunctionCall, } from './types.js';
|
|
4
|
+
async function screenshotBase64(page) {
|
|
5
|
+
return (await page.screenshot({ type: 'png' })).toString('base64');
|
|
6
|
+
}
|
|
7
|
+
function buildFunctionResult(result, screenshot, acknowledged) {
|
|
8
|
+
const detail = acknowledged ? { ...result.detail, safety_acknowledgement: true } : result.detail;
|
|
9
|
+
return {
|
|
10
|
+
type: 'function_result',
|
|
11
|
+
name: result.name,
|
|
12
|
+
call_id: result.callId,
|
|
13
|
+
result: [
|
|
14
|
+
{ type: 'text', text: JSON.stringify(detail) },
|
|
15
|
+
{ type: 'image', data: screenshot, mime_type: 'image/png' },
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function collectText(interaction) {
|
|
20
|
+
const texts = interaction.steps
|
|
21
|
+
.filter((s) => s.type === 'text')
|
|
22
|
+
.map((s) => (s.type === 'text' ? s.text : ''))
|
|
23
|
+
.filter(Boolean);
|
|
24
|
+
return texts.length > 0 ? texts.join('\n') : null;
|
|
25
|
+
}
|
|
26
|
+
/** タスクを computer use ループで実行する。 */
|
|
27
|
+
export async function runTask(client, page, options, hooks) {
|
|
28
|
+
const initialShot = await screenshotBase64(page);
|
|
29
|
+
let interaction = await client.createInitial([
|
|
30
|
+
{ type: 'text', text: options.task },
|
|
31
|
+
{ type: 'image', data: initialShot, mime_type: 'image/png' },
|
|
32
|
+
]);
|
|
33
|
+
for (let turn = 1; turn <= options.maxTurns; turn++) {
|
|
34
|
+
const calls = interaction.steps.filter(isFunctionCall);
|
|
35
|
+
if (calls.length === 0) {
|
|
36
|
+
return {
|
|
37
|
+
completed: true,
|
|
38
|
+
turns: turn - 1,
|
|
39
|
+
finalText: collectText(interaction),
|
|
40
|
+
aborted: false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const responses = [];
|
|
44
|
+
for (const call of calls) {
|
|
45
|
+
let acknowledged = false;
|
|
46
|
+
if (call.safety_decision?.decision === 'require_confirmation') {
|
|
47
|
+
acknowledged = await hooks.onSafetyDecision(call.safety_decision.explanation);
|
|
48
|
+
if (!acknowledged) {
|
|
49
|
+
return { completed: false, turns: turn, finalText: null, aborted: true };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
hooks.onAction?.(turn, call.name, call.arguments.intent);
|
|
53
|
+
const result = await executeAction(page, call);
|
|
54
|
+
if (await detectLoginWall(page)) {
|
|
55
|
+
await hooks.onLoginRequired(page);
|
|
56
|
+
}
|
|
57
|
+
responses.push(buildFunctionResult(result, await screenshotBase64(page), acknowledged));
|
|
58
|
+
}
|
|
59
|
+
interaction = await client.continueFrom(interaction.id, responses);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
completed: false,
|
|
63
|
+
turns: options.maxTurns,
|
|
64
|
+
finalText: collectText(interaction),
|
|
65
|
+
aborted: false,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/runtime/loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* タスク手順書 (playbook) の型と、実行時プロンプトの組み立て (Demo-to-MCP / Phase 4)。
|
|
3
|
+
*
|
|
4
|
+
* バックエンド (api/src/services/recording_analysis.Playbook) と同じ構造。
|
|
5
|
+
* 解析で得た playbook を実行時パラメータと合成して computer use への指示文を作る。
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
export declare const PlaybookStepSchema: z.ZodObject<{
|
|
9
|
+
order: z.ZodNumber;
|
|
10
|
+
action: z.ZodString;
|
|
11
|
+
target: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
12
|
+
intent: z.ZodString;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export declare const PlaybookParameterSchema: z.ZodObject<{
|
|
15
|
+
name: z.ZodString;
|
|
16
|
+
description: z.ZodString;
|
|
17
|
+
example: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export declare const PlaybookAuthPointSchema: z.ZodObject<{
|
|
20
|
+
step_order: z.ZodNumber;
|
|
21
|
+
site: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
22
|
+
note: z.ZodString;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
export declare const PlaybookSchema: z.ZodObject<{
|
|
25
|
+
summary: z.ZodString;
|
|
26
|
+
steps: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
27
|
+
order: z.ZodNumber;
|
|
28
|
+
action: z.ZodString;
|
|
29
|
+
target: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
30
|
+
intent: z.ZodString;
|
|
31
|
+
}, z.core.$strip>>>;
|
|
32
|
+
parameters: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
33
|
+
name: z.ZodString;
|
|
34
|
+
description: z.ZodString;
|
|
35
|
+
example: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
36
|
+
}, z.core.$strip>>>;
|
|
37
|
+
auth_points: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
38
|
+
step_order: z.ZodNumber;
|
|
39
|
+
site: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
40
|
+
note: z.ZodString;
|
|
41
|
+
}, z.core.$strip>>>;
|
|
42
|
+
success_criteria: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
export type Playbook = z.infer<typeof PlaybookSchema>;
|
|
45
|
+
export type PlaybookParameter = z.infer<typeof PlaybookParameterSchema>;
|
|
46
|
+
/** JSON (文字列 or オブジェクト) を検証して Playbook にする。 */
|
|
47
|
+
export declare function parsePlaybook(input: unknown): Playbook;
|
|
48
|
+
/** playbook + 実行時パラメータから computer use への指示文を組み立てる。 */
|
|
49
|
+
export declare function buildTaskPrompt(playbook: Playbook, params: Record<string, string>, purpose?: string | null): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* タスク手順書 (playbook) の型と、実行時プロンプトの組み立て (Demo-to-MCP / Phase 4)。
|
|
3
|
+
*
|
|
4
|
+
* バックエンド (api/src/services/recording_analysis.Playbook) と同じ構造。
|
|
5
|
+
* 解析で得た playbook を実行時パラメータと合成して computer use への指示文を作る。
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
export const PlaybookStepSchema = z.object({
|
|
9
|
+
order: z.number(),
|
|
10
|
+
action: z.string(),
|
|
11
|
+
target: z.string().nullish(),
|
|
12
|
+
intent: z.string(),
|
|
13
|
+
});
|
|
14
|
+
export const PlaybookParameterSchema = z.object({
|
|
15
|
+
name: z.string(),
|
|
16
|
+
description: z.string(),
|
|
17
|
+
example: z.string().nullish(),
|
|
18
|
+
});
|
|
19
|
+
export const PlaybookAuthPointSchema = z.object({
|
|
20
|
+
step_order: z.number(),
|
|
21
|
+
site: z.string().nullish(),
|
|
22
|
+
note: z.string(),
|
|
23
|
+
});
|
|
24
|
+
export const PlaybookSchema = z.object({
|
|
25
|
+
summary: z.string(),
|
|
26
|
+
steps: z.array(PlaybookStepSchema).default([]),
|
|
27
|
+
parameters: z.array(PlaybookParameterSchema).default([]),
|
|
28
|
+
auth_points: z.array(PlaybookAuthPointSchema).default([]),
|
|
29
|
+
success_criteria: z.array(z.string()).default([]),
|
|
30
|
+
});
|
|
31
|
+
/** JSON (文字列 or オブジェクト) を検証して Playbook にする。 */
|
|
32
|
+
export function parsePlaybook(input) {
|
|
33
|
+
const obj = typeof input === 'string' ? JSON.parse(input) : input;
|
|
34
|
+
return PlaybookSchema.parse(obj);
|
|
35
|
+
}
|
|
36
|
+
/** playbook + 実行時パラメータから computer use への指示文を組み立てる。 */
|
|
37
|
+
export function buildTaskPrompt(playbook, params, purpose) {
|
|
38
|
+
const lines = [];
|
|
39
|
+
lines.push(`# タスク\n${playbook.summary}`);
|
|
40
|
+
const trimmedPurpose = purpose?.trim();
|
|
41
|
+
if (trimmedPurpose) {
|
|
42
|
+
lines.push(`\n# 目的\n${trimmedPurpose}`);
|
|
43
|
+
}
|
|
44
|
+
if (playbook.steps.length > 0) {
|
|
45
|
+
lines.push('\n# 手順');
|
|
46
|
+
for (const s of [...playbook.steps].sort((a, b) => a.order - b.order)) {
|
|
47
|
+
const target = s.target ? ` (${s.target})` : '';
|
|
48
|
+
lines.push(`${s.order}. [${s.action}] ${s.intent}${target}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (playbook.parameters.length > 0) {
|
|
52
|
+
lines.push('\n# 入力パラメータ (この実行での値)');
|
|
53
|
+
for (const p of playbook.parameters) {
|
|
54
|
+
const value = params[p.name];
|
|
55
|
+
lines.push(`- ${p.name}: ${value ?? '(未指定)'} — ${p.description}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (playbook.auth_points.length > 0) {
|
|
59
|
+
lines.push('\n# ログインが必要な箇所');
|
|
60
|
+
for (const a of playbook.auth_points) {
|
|
61
|
+
lines.push(`- 手順${a.step_order}${a.site ? ` (${a.site})` : ''}: ${a.note}`);
|
|
62
|
+
}
|
|
63
|
+
lines.push('ログイン画面が出たら、自分で資格情報を入力せずユーザーのログイン完了を待ってから続行してください。');
|
|
64
|
+
}
|
|
65
|
+
if (playbook.success_criteria.length > 0) {
|
|
66
|
+
lines.push('\n# 完了条件');
|
|
67
|
+
for (const c of playbook.success_criteria)
|
|
68
|
+
lines.push(`- ${c}`);
|
|
69
|
+
}
|
|
70
|
+
lines.push('\n上記の手順とパラメータに従ってタスクを実行し、完了したら結果を簡潔に報告してください。');
|
|
71
|
+
return lines.join('\n');
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=playbook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playbook.js","sourceRoot":"","sources":["../../src/runtime/playbook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type RunOutcome } from './loop.js';
|
|
2
|
+
import { type Playbook } from './playbook.js';
|
|
3
|
+
import type { ComputerUseClient } from './types.js';
|
|
4
|
+
export type { RunOutcome } from './loop.js';
|
|
5
|
+
export interface RunPlaybookOptions {
|
|
6
|
+
readonly apiKey?: string;
|
|
7
|
+
readonly headless?: boolean;
|
|
8
|
+
readonly profileDir?: string;
|
|
9
|
+
readonly maxTurns?: number;
|
|
10
|
+
readonly startUrl?: string | null;
|
|
11
|
+
/** 作業の目的。指示文の先頭に含め、エージェントの意図理解を助ける。 */
|
|
12
|
+
readonly purpose?: string | null;
|
|
13
|
+
readonly logger?: (message: string) => void;
|
|
14
|
+
/** safety_decision を自動承認するか (既定 false=中断)。 */
|
|
15
|
+
readonly autoAcknowledgeSafety?: boolean;
|
|
16
|
+
/** テスト用にクライアントを差し替える。 */
|
|
17
|
+
readonly client?: ComputerUseClient;
|
|
18
|
+
}
|
|
19
|
+
/** playbook を実行し、結果を返す。 */
|
|
20
|
+
export declare function runPlaybook(playbook: Playbook, params: Record<string, string>, options?: RunPlaybookOptions): Promise<RunOutcome>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* playbook を実行時パラメータでローカルブラウザ実行する (Demo-to-MCP / Phase 4)。
|
|
3
|
+
*
|
|
4
|
+
* ローカルの永続プロファイルでブラウザを起動 → computer use ループ。ログイン要求は
|
|
5
|
+
* 可視ブラウザでユーザーが対応し、ポーリングで自動再開する。認証情報は保存しない。
|
|
6
|
+
*/
|
|
7
|
+
import { homedir } from 'node:os';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { chromium } from 'playwright';
|
|
10
|
+
import { createGeminiClient } from './client.js';
|
|
11
|
+
import { waitForLoginToClear } from './login.js';
|
|
12
|
+
import { runTask } from './loop.js';
|
|
13
|
+
import { buildTaskPrompt } from './playbook.js';
|
|
14
|
+
const DEFAULT_VIEWPORT = { width: 1280, height: 800 };
|
|
15
|
+
function defaultProfileDir() {
|
|
16
|
+
return join(homedir(), '.config', 'lnar', 'run-profile');
|
|
17
|
+
}
|
|
18
|
+
/** playbook を実行し、結果を返す。 */
|
|
19
|
+
export async function runPlaybook(playbook, params, options = {}) {
|
|
20
|
+
const log = options.logger ?? (() => { });
|
|
21
|
+
const client = options.client ?? createGeminiClient(options.apiKey);
|
|
22
|
+
const task = buildTaskPrompt(playbook, params, options.purpose);
|
|
23
|
+
const context = await chromium.launchPersistentContext(options.profileDir ?? defaultProfileDir(), { headless: options.headless ?? false, viewport: DEFAULT_VIEWPORT });
|
|
24
|
+
try {
|
|
25
|
+
const page = context.pages()[0] ?? (await context.newPage());
|
|
26
|
+
if (options.startUrl) {
|
|
27
|
+
await page.goto(options.startUrl, { waitUntil: 'domcontentloaded' });
|
|
28
|
+
}
|
|
29
|
+
const hooks = {
|
|
30
|
+
onAction: (turn, name, intent) => log(`[turn ${turn}] ${name}${intent ? ` — ${intent}` : ''}`),
|
|
31
|
+
onLoginRequired: async (p) => {
|
|
32
|
+
// タイムアウト時は false。ログイン未完了のまま続行すると不安定になるため中断する。
|
|
33
|
+
const cleared = await waitForLoginToClear(p, { log });
|
|
34
|
+
if (!cleared) {
|
|
35
|
+
throw new Error('ログイン待機がタイムアウトしました。タスクを中断します。');
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
onSafetyDecision: async (explanation) => {
|
|
39
|
+
if (options.autoAcknowledgeSafety) {
|
|
40
|
+
log(`⚠️ safety: ${explanation} (auto-acknowledged)`);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
log(`⚠️ safety: ${explanation} (拒否して中断)`);
|
|
44
|
+
return false;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
return await runTask(client, page, { task, maxTurns: options.maxTurns ?? 40 }, hooks);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
if (context.browser()?.isConnected())
|
|
51
|
+
await context.close().catch(() => { });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/runtime/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini computer use (browser) のランタイム型 (Demo-to-MCP / Phase 4)。
|
|
3
|
+
*
|
|
4
|
+
* Phase 0 スパイクで実 API 検証済みの形を本体へ昇格したもの。
|
|
5
|
+
* `@google/genai` >= 2.0.0 の interactions API (`steps` スキーマ) に対応する。
|
|
6
|
+
*/
|
|
7
|
+
export type ActionName = 'click' | 'double_click' | 'right_click' | 'type' | 'navigate' | 'scroll' | 'press_key' | 'hotkey' | 'drag_and_drop' | 'go_back' | 'go_forward' | 'take_screenshot' | 'wait';
|
|
8
|
+
export interface SafetyDecision {
|
|
9
|
+
readonly decision: 'require_confirmation' | string;
|
|
10
|
+
readonly explanation: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ActionArguments {
|
|
13
|
+
readonly x?: number;
|
|
14
|
+
readonly y?: number;
|
|
15
|
+
readonly start_x?: number;
|
|
16
|
+
readonly start_y?: number;
|
|
17
|
+
readonly end_x?: number;
|
|
18
|
+
readonly end_y?: number;
|
|
19
|
+
readonly text?: string;
|
|
20
|
+
readonly press_enter?: boolean;
|
|
21
|
+
readonly url?: string;
|
|
22
|
+
readonly direction?: 'up' | 'down' | 'left' | 'right';
|
|
23
|
+
readonly magnitude_in_pixels?: number;
|
|
24
|
+
readonly key?: string;
|
|
25
|
+
readonly keys?: ReadonlyArray<string>;
|
|
26
|
+
readonly seconds?: number;
|
|
27
|
+
readonly intent?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface FunctionCallStep {
|
|
30
|
+
readonly type: 'function_call';
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly name: ActionName;
|
|
33
|
+
readonly arguments: ActionArguments;
|
|
34
|
+
readonly safety_decision?: SafetyDecision;
|
|
35
|
+
}
|
|
36
|
+
export interface TextStep {
|
|
37
|
+
readonly type: 'text';
|
|
38
|
+
readonly text: string;
|
|
39
|
+
}
|
|
40
|
+
export type InteractionStep = FunctionCallStep | TextStep;
|
|
41
|
+
export interface Interaction {
|
|
42
|
+
readonly id: string;
|
|
43
|
+
readonly steps: ReadonlyArray<InteractionStep>;
|
|
44
|
+
}
|
|
45
|
+
export type InteractionInput = {
|
|
46
|
+
readonly type: 'text';
|
|
47
|
+
readonly text: string;
|
|
48
|
+
} | {
|
|
49
|
+
readonly type: 'image';
|
|
50
|
+
readonly data: string;
|
|
51
|
+
readonly mime_type: 'image/png';
|
|
52
|
+
} | {
|
|
53
|
+
readonly type: 'function_result';
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly call_id: string;
|
|
56
|
+
readonly result: ReadonlyArray<{
|
|
57
|
+
readonly type: 'text';
|
|
58
|
+
readonly text: string;
|
|
59
|
+
} | {
|
|
60
|
+
readonly type: 'image';
|
|
61
|
+
readonly data: string;
|
|
62
|
+
readonly mime_type: 'image/png';
|
|
63
|
+
}>;
|
|
64
|
+
};
|
|
65
|
+
export interface ComputerUseClient {
|
|
66
|
+
createInitial(input: ReadonlyArray<InteractionInput>): Promise<Interaction>;
|
|
67
|
+
continueFrom(previousInteractionId: string, input: ReadonlyArray<InteractionInput>): Promise<Interaction>;
|
|
68
|
+
}
|
|
69
|
+
export declare function isFunctionCall(step: InteractionStep): step is FunctionCallStep;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini computer use (browser) のランタイム型 (Demo-to-MCP / Phase 4)。
|
|
3
|
+
*
|
|
4
|
+
* Phase 0 スパイクで実 API 検証済みの形を本体へ昇格したもの。
|
|
5
|
+
* `@google/genai` >= 2.0.0 の interactions API (`steps` スキーマ) に対応する。
|
|
6
|
+
*/
|
|
7
|
+
export function isFunctionCall(step) {
|
|
8
|
+
return step.type === 'function_call';
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiFH,MAAM,UAAU,cAAc,CAAC,IAAqB;IAClD,OAAO,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC;AACvC,CAAC"}
|
package/dist/service/files.d.ts
CHANGED
|
@@ -11,8 +11,20 @@ export interface ServiceTarget {
|
|
|
11
11
|
/** lnar の CLI スクリプト絶対パス (= process.argv[1]) */
|
|
12
12
|
scriptPath: string;
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* macOS のログ出力先 (絶対パス 2 本)。launchd の plist は `~` を展開しないため、
|
|
16
|
+
* 呼び出し側で解決した絶対パスのディレクトリを受け取る。
|
|
17
|
+
* `/tmp` を避け、ユーザー専用の `~/Library/Logs/lnar/` 配下に置く。
|
|
18
|
+
*/
|
|
19
|
+
export declare function macOsLogPaths(logDir: string): {
|
|
20
|
+
stdout: string;
|
|
21
|
+
stderr: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* macOS launchd LaunchAgent plist。RunAtLoad + KeepAlive で常駐させる。
|
|
25
|
+
* `logDir` はログを書き出すディレクトリの絶対パス (例: ~/Library/Logs/lnar)。
|
|
26
|
+
*/
|
|
27
|
+
export declare function macOsPlist(target: ServiceTarget, logDir: string): string;
|
|
16
28
|
/** Linux systemd user unit。`systemctl --user enable --now` で起動。 */
|
|
17
29
|
export declare function linuxSystemdUnit(target: ServiceTarget): string;
|
|
18
30
|
/**
|
package/dist/service/files.js
CHANGED
|
@@ -5,8 +5,20 @@
|
|
|
5
5
|
* `service/install.ts` に分離して、ここはテストしやすい純粋関数のみ。
|
|
6
6
|
*/
|
|
7
7
|
export const SERVICE_LABEL = 'ai.lnar.daemon';
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* macOS のログ出力先 (絶対パス 2 本)。launchd の plist は `~` を展開しないため、
|
|
10
|
+
* 呼び出し側で解決した絶対パスのディレクトリを受け取る。
|
|
11
|
+
* `/tmp` を避け、ユーザー専用の `~/Library/Logs/lnar/` 配下に置く。
|
|
12
|
+
*/
|
|
13
|
+
export function macOsLogPaths(logDir) {
|
|
14
|
+
return { stdout: `${logDir}/daemon.log`, stderr: `${logDir}/daemon.err.log` };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* macOS launchd LaunchAgent plist。RunAtLoad + KeepAlive で常駐させる。
|
|
18
|
+
* `logDir` はログを書き出すディレクトリの絶対パス (例: ~/Library/Logs/lnar)。
|
|
19
|
+
*/
|
|
20
|
+
export function macOsPlist(target, logDir) {
|
|
21
|
+
const log = macOsLogPaths(logDir);
|
|
10
22
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
11
23
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
12
24
|
<plist version="1.0">
|
|
@@ -20,8 +32,8 @@ export function macOsPlist(target) {
|
|
|
20
32
|
</array>
|
|
21
33
|
<key>RunAtLoad</key><true/>
|
|
22
34
|
<key>KeepAlive</key><true/>
|
|
23
|
-
<key>StandardOutPath</key><string
|
|
24
|
-
<key>StandardErrorPath</key><string
|
|
35
|
+
<key>StandardOutPath</key><string>${log.stdout}</string>
|
|
36
|
+
<key>StandardErrorPath</key><string>${log.stderr}</string>
|
|
25
37
|
<key>EnvironmentVariables</key>
|
|
26
38
|
<dict>
|
|
27
39
|
<key>PATH</key>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/service/files.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAA;AAS7C,
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/service/files.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAA;AAS7C;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAA;AAC/E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,MAAqB,EAAE,MAAc;IAC9D,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IACjC,OAAO;;;;4BAImB,aAAa;;;cAG3B,MAAM,CAAC,QAAQ;cACf,MAAM,CAAC,UAAU;;;;;sCAKO,GAAG,CAAC,MAAM;wCACR,GAAG,CAAC,MAAM;;;;;;;;CAQjD,CAAA;AACD,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,gBAAgB,CAAC,MAAqB;IACpD,OAAO;;;;;;YAMG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,UAAU;;;;;;;;CAQ/C,CAAA;AACD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,MAAqB,EAAE,QAAgB;IACpE,gDAAgD;IAChD,MAAM,MAAM,GAAG,CAAC,CAAS,EAAU,EAAE,CACnC,CAAC;SACE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAC5B,6CAA6C;IAC7C,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAA;IAC1D,OAAO;;;;;;;;gBAQO,MAAM,CAAC,QAAQ,CAAC;;;;;gBAKhB,MAAM,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8Bf,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;mBACrB,UAAU;;;;CAI5B,CAAA;AACD,CAAC"}
|