palmier 1.1.21 → 1.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -19
- package/dist/agent-tools.d.ts +1 -1
- package/dist/agent-tools.js +36 -48
- package/dist/agents/agent-instructions.md +3 -1
- package/dist/agents/agent.d.ts +3 -8
- package/dist/agents/agent.js +0 -1
- package/dist/agents/aider.js +2 -7
- package/dist/agents/claude.js +2 -11
- package/dist/agents/cline.js +2 -7
- package/dist/agents/codex.js +2 -4
- package/dist/agents/copilot.js +2 -10
- package/dist/agents/cursor.js +2 -7
- package/dist/agents/deepagents.js +2 -7
- package/dist/agents/droid.js +2 -7
- package/dist/agents/goose.js +2 -4
- package/dist/agents/grok.js +2 -7
- package/dist/agents/hermes.js +2 -7
- package/dist/agents/kimi.js +2 -6
- package/dist/agents/kiro.js +2 -7
- package/dist/agents/openclaw.js +1 -1
- package/dist/agents/opencode.js +2 -7
- package/dist/agents/qoder.js +2 -7
- package/dist/agents/qwen.js +2 -4
- package/dist/agents/shared-prompt.js +3 -8
- package/dist/agents/wizard.js +0 -1
- package/dist/commands/pair.js +2 -4
- package/dist/commands/run.js +3 -15
- package/dist/commands/serve.js +5 -6
- package/dist/index.js +0 -22
- package/dist/nats-client.d.ts +1 -1
- package/dist/nats-client.js +1 -1
- package/dist/password-store.d.ts +1 -3
- package/dist/password-store.js +1 -7
- package/dist/pwa/assets/index-BPB3G_2J.css +1 -0
- package/dist/pwa/assets/{index-rqAuAC0q.js → index-De8hDN5I.js} +42 -46
- package/dist/pwa/assets/{web-ew6AFUex.js → web-BQ0rMZ_X.js} +1 -1
- package/dist/pwa/assets/{web-DQDmufih.js → web-miCfAJBc.js} +1 -1
- package/dist/pwa/assets/{web-Dp5Zj6HJ.js → web-o_D8jqAS.js} +1 -1
- package/dist/pwa/index.html +2 -2
- package/dist/rpc-handler.js +14 -15
- package/dist/task.d.ts +10 -0
- package/dist/task.js +42 -0
- package/dist/transports/http-transport.d.ts +1 -1
- package/dist/transports/http-transport.js +1 -3
- package/dist/transports/nats-transport.d.ts +1 -1
- package/dist/transports/nats-transport.js +3 -5
- package/dist/types.d.ts +0 -2
- package/package.json +2 -2
- package/dist/commands/passwords.d.ts +0 -3
- package/dist/commands/passwords.js +0 -25
- package/dist/pwa/assets/index-ByB7B0d2.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{W as t}from"./index-
|
|
1
|
+
import{W as t}from"./index-De8hDN5I.js";class s extends t{constructor(){super(),this.handleVisibilityChange=()=>{const e={isActive:document.hidden!==!0};this.notifyListeners("appStateChange",e),document.hidden?this.notifyListeners("pause",null):this.notifyListeners("resume",null)},document.addEventListener("visibilitychange",this.handleVisibilityChange,!1)}exitApp(){throw this.unimplemented("Not implemented on web.")}async getInfo(){throw this.unimplemented("Not implemented on web.")}async getLaunchUrl(){return{url:""}}async getState(){return{isActive:document.hidden!==!0}}async minimizeApp(){throw this.unimplemented("Not implemented on web.")}async toggleBackButtonHandler(){throw this.unimplemented("Not implemented on web.")}async getAppLanguage(){return{value:navigator.language.split("-")[0].toLowerCase()}}}export{s as AppWeb};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{W as p}from"./index-
|
|
1
|
+
import{W as p}from"./index-De8hDN5I.js";class f extends p{constructor(){super(...arguments),this.group="CapacitorStorage"}async configure({group:e}){typeof e=="string"&&(this.group=e)}async get(e){return{value:this.impl.getItem(this.applyPrefix(e.key))}}async set(e){this.impl.setItem(this.applyPrefix(e.key),e.value)}async remove(e){this.impl.removeItem(this.applyPrefix(e.key))}async keys(){return{keys:this.rawKeys().map(t=>t.substring(this.prefix.length))}}async clear(){for(const e of this.rawKeys())this.impl.removeItem(e)}async migrate(){var e;const t=[],s=[],n="_cap_",o=Object.keys(this.impl).filter(i=>i.indexOf(n)===0);for(const i of o){const r=i.substring(n.length),a=(e=this.impl.getItem(i))!==null&&e!==void 0?e:"",{value:l}=await this.get({key:r});typeof l=="string"?s.push(r):(await this.set({key:r,value:a}),t.push(r))}return{migrated:t,existing:s}}async removeOld(){const e="_cap_",t=Object.keys(this.impl).filter(s=>s.indexOf(e)===0);for(const s of t)this.impl.removeItem(s)}get impl(){return window.localStorage}get prefix(){return this.group==="NativeStorage"?"":`${this.group}.`}rawKeys(){return Object.keys(this.impl).filter(e=>e.indexOf(this.prefix)===0)}applyPrefix(e){return this.prefix+e}}export{f as PreferencesWeb};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{W as i}from"./index-
|
|
1
|
+
import{W as i}from"./index-De8hDN5I.js";function o(){const t=window.navigator.connection||window.navigator.mozConnection||window.navigator.webkitConnection;let n="unknown";const e=t?t.type||t.effectiveType:null;if(e&&typeof e=="string")switch(e){case"bluetooth":case"cellular":n="cellular";break;case"none":n="none";break;case"ethernet":case"wifi":case"wimax":n="wifi";break;case"other":case"unknown":n="unknown";break;case"slow-2g":case"2g":case"3g":n="cellular";break;case"4g":n="wifi";break}return n}class s extends i{constructor(){super(),this.handleOnline=()=>{const e={connected:!0,connectionType:o()};this.notifyListeners("networkStatusChange",e)},this.handleOffline=()=>{const n={connected:!1,connectionType:"none"};this.notifyListeners("networkStatusChange",n)},typeof window<"u"&&(window.addEventListener("online",this.handleOnline),window.addEventListener("offline",this.handleOffline))}async getStatus(){if(!window.navigator)throw this.unavailable("Browser does not support the Network Information API");const n=window.navigator.onLine,e=o();return{connected:n,connectionType:n?e:"none"}}}const r=new s;export{r as Network,s as NetworkWeb};
|
package/dist/pwa/index.html
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
9
9
|
<title>Palmier</title>
|
|
10
10
|
<meta name="description" content="Your personal AI agent runs on your own computer, with local data and credential storage. Use your existing AI subscription and connect your calendar, contacts, notifications, and more." />
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-De8hDN5I.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BPB3G_2J.css">
|
|
13
13
|
<link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
package/dist/rpc-handler.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import * as fs from "fs";
|
|
3
3
|
import * as path from "path";
|
|
4
|
-
import { listTasks, parseTaskFile, taskDisplayName, writeTaskFile, getTaskDir, readTaskStatus, writeTaskStatus, readHistory, deleteHistoryEntry, appendTaskList, removeFromTaskList, isTaskInList,
|
|
4
|
+
import { listTasks, parseTaskFile, taskDisplayName, writeTaskFile, getTaskDir, readTaskStatus, writeTaskStatus, readHistory, deleteHistoryEntry, appendTaskList, removeFromTaskList, isTaskInList, createPendingRun, readPendingRun, appendRunMessage, getRunDir, writeFollowupStatus, readFollowupStatus, deleteFollowupStatus, isRunStarred, setRunStarred, formatAgentModelComment, extractAgentModelComment } from "./task.js";
|
|
5
5
|
import { resolvePending, getPending, listPending } from "./pending-requests.js";
|
|
6
6
|
import { getPlatform } from "./platform/index.js";
|
|
7
7
|
import { onChildDone, resolveCommand, spawnCommand } from "./spawn-command.js";
|
|
@@ -136,6 +136,8 @@ async function generateName(projectRoot, userPrompt, agentName) {
|
|
|
136
136
|
return userPrompt;
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
+
// Longer than the slowest observed launch (~5s on Windows) before the run process claims its run.
|
|
140
|
+
const DUPLICATE_RUN_WINDOW_MS = 15_000;
|
|
139
141
|
/** Active follow-up child processes, keyed by "taskId:runId". */
|
|
140
142
|
const activeFollowups = new Map();
|
|
141
143
|
/**
|
|
@@ -253,7 +255,6 @@ export function createRpcHandler(config) {
|
|
|
253
255
|
requires_confirmation: params.requires_confirmation ?? true,
|
|
254
256
|
...(params.schedule_type ? { schedule_type: params.schedule_type } : {}),
|
|
255
257
|
...(params.schedule_values?.length ? { schedule_values: params.schedule_values } : {}),
|
|
256
|
-
...(params.yolo_mode ? { yolo_mode: true } : {}),
|
|
257
258
|
...(params.foreground_mode ? { foreground_mode: true } : {}),
|
|
258
259
|
...(params.command ? { command: params.command } : {}),
|
|
259
260
|
},
|
|
@@ -297,11 +298,6 @@ export function createRpcHandler(config) {
|
|
|
297
298
|
existing.frontmatter.schedule_enabled = params.schedule_enabled;
|
|
298
299
|
if (params.requires_confirmation !== undefined)
|
|
299
300
|
existing.frontmatter.requires_confirmation = params.requires_confirmation;
|
|
300
|
-
if (params.yolo_mode !== undefined) {
|
|
301
|
-
existing.frontmatter.yolo_mode = params.yolo_mode || undefined;
|
|
302
|
-
if (params.yolo_mode)
|
|
303
|
-
delete existing.frontmatter.permissions;
|
|
304
|
-
}
|
|
305
301
|
if (params.foreground_mode !== undefined)
|
|
306
302
|
existing.frontmatter.foreground_mode = params.foreground_mode || undefined;
|
|
307
303
|
if (params.command !== undefined) {
|
|
@@ -356,7 +352,6 @@ export function createRpcHandler(config) {
|
|
|
356
352
|
schedule_enabled: false,
|
|
357
353
|
requires_confirmation: params.requires_confirmation ?? false,
|
|
358
354
|
one_off: true,
|
|
359
|
-
...(params.yolo_mode ? { yolo_mode: true } : {}),
|
|
360
355
|
...(params.foreground_mode ? { foreground_mode: true } : {}),
|
|
361
356
|
...(params.command ? { command: params.command } : {}),
|
|
362
357
|
},
|
|
@@ -364,8 +359,7 @@ export function createRpcHandler(config) {
|
|
|
364
359
|
writeTaskFile(taskDir, task);
|
|
365
360
|
// One-off run: do NOT append to tasks.jsonl.
|
|
366
361
|
const oneoffAgentVersion = config.agents?.find((a) => a.key === params.agent)?.version;
|
|
367
|
-
const runId =
|
|
368
|
-
appendHistory(config.projectRoot, { task_id: id, run_id: runId });
|
|
362
|
+
const runId = createPendingRun(config.projectRoot, taskDir, id, name, params.agent, oneoffAgentVersion);
|
|
369
363
|
const platform = getPlatform();
|
|
370
364
|
platform.installTaskTimer(config, task);
|
|
371
365
|
try {
|
|
@@ -382,17 +376,22 @@ export function createRpcHandler(config) {
|
|
|
382
376
|
case "task.run": {
|
|
383
377
|
const params = request.params;
|
|
384
378
|
const runTaskDir = getTaskDir(config.projectRoot, params.id);
|
|
379
|
+
const launchingRunId = readPendingRun(runTaskDir);
|
|
380
|
+
if (launchingRunId && Date.now() - Number(launchingRunId) < DUPLICATE_RUN_WINDOW_MS) {
|
|
381
|
+
console.log(`[task.run] Task ${params.id} run ${launchingRunId} is still launching, ignoring duplicate request`);
|
|
382
|
+
return { ok: true, task_id: params.id, run_id: launchingRunId };
|
|
383
|
+
}
|
|
385
384
|
let taskRunId;
|
|
386
385
|
try {
|
|
387
386
|
const platform = getPlatform();
|
|
387
|
+
// Must stay synchronous up to here so a concurrent duplicate sees the pending run.
|
|
388
|
+
const runTask = parseTaskFile(runTaskDir);
|
|
389
|
+
const taskRunAgentVersion = config.agents?.find((a) => a.key === runTask.frontmatter.agent)?.version;
|
|
390
|
+
taskRunId = createPendingRun(config.projectRoot, runTaskDir, params.id, taskDisplayName(runTask.frontmatter), runTask.frontmatter.agent, taskRunAgentVersion);
|
|
388
391
|
if (platform.isTaskRunning(params.id)) {
|
|
389
392
|
console.log(`[task.run] Task ${params.id} is already running, killing stale process`);
|
|
390
393
|
await platform.stopTask(params.id);
|
|
391
394
|
}
|
|
392
|
-
const runTask = parseTaskFile(runTaskDir);
|
|
393
|
-
const taskRunAgentVersion = config.agents?.find((a) => a.key === runTask.frontmatter.agent)?.version;
|
|
394
|
-
taskRunId = createRunDir(runTaskDir, taskDisplayName(runTask.frontmatter), Date.now(), runTask.frontmatter.agent, taskRunAgentVersion);
|
|
395
|
-
appendHistory(config.projectRoot, { task_id: params.id, run_id: taskRunId });
|
|
396
395
|
await platform.startTask(params.id);
|
|
397
396
|
return { ok: true, task_id: params.id, run_id: taskRunId };
|
|
398
397
|
}
|
|
@@ -435,7 +434,7 @@ export function createRpcHandler(config) {
|
|
|
435
434
|
name: taskDisplayName(followupTask.frontmatter), run_id: params.run_id,
|
|
436
435
|
});
|
|
437
436
|
const followupAgent = getAgent(followupTask.frontmatter.agent);
|
|
438
|
-
const { args: cmdArgs, stdin, env: followupAgentEnv, files: followupFiles } = followupAgent.getTaskRunCommandLine(followupTask, params.message
|
|
437
|
+
const { args: cmdArgs, stdin, env: followupAgentEnv, files: followupFiles } = followupAgent.getTaskRunCommandLine(followupTask, params.message);
|
|
439
438
|
const cmd = followupAgent.command;
|
|
440
439
|
if (followupFiles) {
|
|
441
440
|
for (const f of followupFiles)
|
package/dist/task.d.ts
CHANGED
|
@@ -21,6 +21,16 @@ export declare function isRunStarred(runDir: string): boolean;
|
|
|
21
21
|
export declare function setRunStarred(runDir: string, starred: boolean): void;
|
|
22
22
|
/** Returns the run ID (timestamp string used as directory name). */
|
|
23
23
|
export declare function createRunDir(taskDir: string, taskName: string, startTime: number, agent?: string, agentVersion?: string): string;
|
|
24
|
+
/** The run dir created for the next run process that it has not claimed yet, or null. */
|
|
25
|
+
export declare function readPendingRun(taskDir: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Create a run dir (plus its history entry) for the run process about to be
|
|
28
|
+
* launched to claim. A previous pending run that was never claimed is discarded
|
|
29
|
+
* so it doesn't linger as an empty session.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createPendingRun(projectRoot: string, taskDir: string, taskId: string, taskName: string, agent?: string, agentVersion?: string): string;
|
|
32
|
+
/** Take ownership of the pending run; null if there is none or another process took it first. */
|
|
33
|
+
export declare function claimPendingRun(taskDir: string): string | null;
|
|
24
34
|
export declare function getRunDir(taskDir: string, runId: string): string;
|
|
25
35
|
export declare function appendRunMessage(taskDir: string, runId: string, msg: ConversationMessage): void;
|
|
26
36
|
export declare function beginStreamingMessage(taskDir: string, runId: string, time: number, stream?: "stdout" | "stderr"): StreamingMessageWriter;
|
package/dist/task.js
CHANGED
|
@@ -155,6 +155,48 @@ export function createRunDir(taskDir, taskName, startTime, agent, agentVersion)
|
|
|
155
155
|
fs.writeFileSync(path.join(runDir, "TASKRUN.md"), content, "utf-8");
|
|
156
156
|
return runId;
|
|
157
157
|
}
|
|
158
|
+
const PENDING_RUN_FILE = "pending-run";
|
|
159
|
+
function isUnclaimedRun(taskDir, runId) {
|
|
160
|
+
if (!/^\d+$/.test(runId) || !fs.existsSync(path.join(taskDir, runId, "TASKRUN.md")))
|
|
161
|
+
return false;
|
|
162
|
+
return !readRunMessages(taskDir, runId).some((m) => m.role === "status");
|
|
163
|
+
}
|
|
164
|
+
/** The run dir created for the next run process that it has not claimed yet, or null. */
|
|
165
|
+
export function readPendingRun(taskDir) {
|
|
166
|
+
let runId;
|
|
167
|
+
try {
|
|
168
|
+
runId = fs.readFileSync(path.join(taskDir, PENDING_RUN_FILE), "utf-8").trim();
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
return isUnclaimedRun(taskDir, runId) ? runId : null;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Create a run dir (plus its history entry) for the run process about to be
|
|
177
|
+
* launched to claim. A previous pending run that was never claimed is discarded
|
|
178
|
+
* so it doesn't linger as an empty session.
|
|
179
|
+
*/
|
|
180
|
+
export function createPendingRun(projectRoot, taskDir, taskId, taskName, agent, agentVersion) {
|
|
181
|
+
const stale = readPendingRun(taskDir);
|
|
182
|
+
if (stale)
|
|
183
|
+
deleteHistoryEntry(projectRoot, taskId, stale);
|
|
184
|
+
const runId = createRunDir(taskDir, taskName, Date.now(), agent, agentVersion);
|
|
185
|
+
appendHistory(projectRoot, { task_id: taskId, run_id: runId });
|
|
186
|
+
fs.writeFileSync(path.join(taskDir, PENDING_RUN_FILE), runId, "utf-8");
|
|
187
|
+
return runId;
|
|
188
|
+
}
|
|
189
|
+
/** Take ownership of the pending run; null if there is none or another process took it first. */
|
|
190
|
+
export function claimPendingRun(taskDir) {
|
|
191
|
+
const runId = readPendingRun(taskDir);
|
|
192
|
+
try {
|
|
193
|
+
fs.unlinkSync(path.join(taskDir, PENDING_RUN_FILE));
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
return runId;
|
|
199
|
+
}
|
|
158
200
|
export function getRunDir(taskDir, runId) {
|
|
159
201
|
return path.join(taskDir, runId);
|
|
160
202
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { type NatsConnection } from "nats";
|
|
1
|
+
import { type NatsConnection } from "@nats-io/transport-node";
|
|
2
2
|
import type { HostConfig, RpcMessage } from "../types.js";
|
|
3
3
|
export declare function startHttpTransport(config: HostConfig, handleRpc: (req: RpcMessage) => Promise<unknown>, port: number, getNc: () => NatsConnection | undefined, pairingCode?: string, onReady?: () => void): Promise<void>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as http from "node:http";
|
|
2
2
|
import * as os from "os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { StringCodec } from "nats";
|
|
5
4
|
import { validateClient, addClient } from "../client-store.js";
|
|
6
5
|
import { registerPending } from "../pending-requests.js";
|
|
7
6
|
import * as fs from "node:fs";
|
|
@@ -106,9 +105,8 @@ export async function startHttpTransport(config, handleRpc, port, getNc, pairing
|
|
|
106
105
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
107
106
|
}
|
|
108
107
|
async function publishEvent(taskId, payload) {
|
|
109
|
-
const sc = StringCodec();
|
|
110
108
|
const subject = `host-event.${config.hostId}.${taskId}`;
|
|
111
|
-
getNc()?.publish(subject,
|
|
109
|
+
getNc()?.publish(subject, JSON.stringify(payload));
|
|
112
110
|
broadcastSseEvent({ task_id: taskId, ...payload });
|
|
113
111
|
}
|
|
114
112
|
function makeToolContext(taskId) {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { type NatsConnection } from "nats";
|
|
1
|
+
import { type NatsConnection } from "@nats-io/transport-node";
|
|
2
2
|
import type { HostConfig, RpcMessage } from "../types.js";
|
|
3
3
|
export declare function startNatsTransport(config: HostConfig, handleRpc: (req: RpcMessage) => Promise<unknown>, nc: NatsConnection): Promise<void>;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { StringCodec } from "nats";
|
|
2
1
|
export async function startNatsTransport(config, handleRpc, nc) {
|
|
3
|
-
const sc = StringCodec();
|
|
4
2
|
const subject = `host.${config.hostId}.rpc.>`;
|
|
5
3
|
console.log(`[nats] Subscribing to: ${subject}`);
|
|
6
4
|
const sub = nc.subscribe(subject);
|
|
@@ -19,7 +17,7 @@ export async function startNatsTransport(config, handleRpc, nc) {
|
|
|
19
17
|
const method = rpcIdx >= 0 ? subjectTokens.slice(rpcIdx + 1).join(".") : "";
|
|
20
18
|
let params = {};
|
|
21
19
|
if (msg.data && msg.data.length > 0) {
|
|
22
|
-
const raw =
|
|
20
|
+
const raw = msg.string().trim();
|
|
23
21
|
if (raw.length > 0) {
|
|
24
22
|
try {
|
|
25
23
|
params = JSON.parse(raw);
|
|
@@ -27,7 +25,7 @@ export async function startNatsTransport(config, handleRpc, nc) {
|
|
|
27
25
|
catch {
|
|
28
26
|
console.error(`[nats] Failed to parse RPC params for ${method}`);
|
|
29
27
|
if (msg.reply) {
|
|
30
|
-
msg.respond(
|
|
28
|
+
msg.respond(JSON.stringify({ error: "Invalid JSON" }));
|
|
31
29
|
}
|
|
32
30
|
return;
|
|
33
31
|
}
|
|
@@ -47,7 +45,7 @@ export async function startNatsTransport(config, handleRpc, nc) {
|
|
|
47
45
|
}
|
|
48
46
|
console.log(`[nats] RPC done: ${method}`, JSON.stringify(response).slice(0, 200));
|
|
49
47
|
if (msg.reply) {
|
|
50
|
-
msg.respond(
|
|
48
|
+
msg.respond(JSON.stringify(response));
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
async function consumeSubscription(subscription) {
|
package/dist/types.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ export interface HostConfig {
|
|
|
9
9
|
key: string;
|
|
10
10
|
label: string;
|
|
11
11
|
supportsPermissions?: boolean;
|
|
12
|
-
supportsYolo?: boolean;
|
|
13
12
|
npmPackage?: string;
|
|
14
13
|
/** Runtime marker for "managed by Palmier" — present iff Palmier installed/manages this agent. */
|
|
15
14
|
version?: string;
|
|
@@ -42,7 +41,6 @@ export interface TaskFrontmatter {
|
|
|
42
41
|
schedule_values?: string[];
|
|
43
42
|
schedule_enabled: boolean;
|
|
44
43
|
requires_confirmation: boolean;
|
|
45
|
-
yolo_mode?: boolean;
|
|
46
44
|
foreground_mode?: boolean;
|
|
47
45
|
permissions?: RequiredPermission[];
|
|
48
46
|
command?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "palmier",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.23",
|
|
4
4
|
"description": "Palmier host CLI - provisions, executes tasks, and serves NATS RPC",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Hongxu Cai",
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"start": "node dist/index.js"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
+
"@nats-io/transport-node": "^3.4.0",
|
|
39
40
|
"commander": "^13.1.0",
|
|
40
41
|
"cross-spawn": "^7.0.6",
|
|
41
42
|
"dotenv": "^16.4.7",
|
|
42
|
-
"nats": "^2.29.1",
|
|
43
43
|
"yaml": "^2.7.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { listPasswords, deletePassword, clearPasswords, normalizeOrigin } from "../password-store.js";
|
|
2
|
-
export async function passwordsListCommand() {
|
|
3
|
-
const entries = listPasswords();
|
|
4
|
-
if (entries.length === 0) {
|
|
5
|
-
console.log("No saved passwords.");
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
console.log(`${entries.length} saved password(s):\n`);
|
|
9
|
-
for (const e of entries) {
|
|
10
|
-
console.log(` ${e.origin} ${e.username}`);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export async function passwordsDeleteCommand(origin, username) {
|
|
14
|
-
if (deletePassword(normalizeOrigin(origin), username)) {
|
|
15
|
-
console.log("Password deleted.");
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
console.error("No matching saved password.");
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export async function passwordsClearCommand() {
|
|
23
|
-
const count = clearPasswords();
|
|
24
|
-
console.log(`Cleared ${count} saved password(s).`);
|
|
25
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@font-face{font-family:Plus Jakarta Sans Variable;font-style:normal;font-display:swap;font-weight:200 800;src:url(data:font/woff2;base64,d09GMgABAAAAAAa0ABQAAAAADOwAAAZHAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbbhwoP0hWQVJtP01WQVJGBmA/U1RBVIEcAGQvXBEICoMkgmcLFgAwhEwBNgIkAyYEIAWGXAdiDAcbOgtRlHLSRcD8TEzkdoghPRuspKefbVnJIM5yRfA8vZv9uTNJSCZY21Scrigr6qyJ/C3sE1OFKuVP3e+lxUTbYio1zb/kFdk2bufY2BlhWERRBrcDUBhB5GEpd2Cy+MDxqf9zLPX+XVsk5r+s50d6IpWLatqbdT6f2MAikiOKQocbjya2QT0DqPDX4R0jQBgAUAiCRhDA0EkvYeLk9X3I2bEWI8jBAihIANm+kUWQBgggWChcBBdFhXlAUgOCAgBA0AgKjUJ8HKvjddFCKSgAVklEBYAeEJgCQJH0qB3B+neDg4sAA4hAD0AVIEAA0IACJGeaUjMIIIhBUE/zNABjTQMBwEVRcwcYBUCObLERABAgjAA1M/ZSa9hSi4OlpsnZ2KBOVUsAZlRGl1W0NZ6gSwhQPIAFs7YFL0QS91vRQgdgx2VDAnIegKpQvwAJIoLgooBAGtCJcHizg0TDiz8vhPgIcVXaDJu37ZyDAIjFy4AAiC1JAA0olJwN6nFAACQk/zfQVUuA3t5ELKMT9hpBSRj+HBSggYCIAIEkUoC4PCIQIWHGA8+IStxf0PW15ntP22gc+Wo+BLyQAHECQiAVd+c3Ba8gZ4NOACBaLOhsbIMkAXEmuHsSAvkf4oJaAogH6nEQAzoBIEihUAwAA0AACAObAGcANYAAABaqgMmpWAhiiVyrvNyqFV8tMQ5Iyq6r9Lf7W82i8ILl2cv8zbfdJ5lnPzSnvt/XXPfRR/5pH3xA+u5uS0798EPJ3Fft33w36Wc+8E/7aIB/9fvvb1pE9erNXLB9mkw//cSg8Cnx03VGXDvZnRzKqV9Xkhn7eRHb3wVjHpg19nkZpX9bBU+vnHegrsuQcbW9Bi7oO33h+8SU0Tly/MbeYUuG1cftVz6oNxrW0qH1iezq446fkDFo/rgvTkqt7zmteFzncZ0uxXti09FFF2z+C7CODpjUudOE5NSeb3bv/mbPKcmOEzpWQ3VBgBAuj/AOY3qMm7UvgA8qo3+qvAUBBAX0wADQG4AG+jCKaSxkBe/zvcTG8jRpRZgqhCog4sd++cOo7J+/RdvaaMoc78Ri/PNPSclx6fDvG1Kt3qQFi1rq+5EaavTtt9TVvfvucYsA5wc4oESU+E1ikF9TkrOkIsIH5Fawx7SBNLnMcWlM3skBluCmQNs7GeyFvYVDamsqwhxx18n/+WdxzDgOLU1AqbUlb3m+KOT9+ONvY/7XlrdLS//5v7nN97XHj0VzJpJvz4spq0V7ioXDuv2YrSxtF/KZmvbWfKtorXSh8dfvgGu+ev/nFSaXM6jyuCE/f/EpmuP7Rqu8R98vP/+tXUdrOgKlZP9Q/s+fRF9+3CHZd4EugfPv158me9X7v/6aSMCxMJmIr//65bNXngl7+uiPP5oKx+y27D8trp+psUzMacpm80XlnYeMAct0LynxhNYgaP3dtn8/LBAi+ksr+7NvjjtJV+UXrz0uLXV1x08AZRN1S2A8EaVE5afk/dfGjz//+fdHn36Xdttz2d9/z+bawZ8/slNN1aYtq+ZPGzNuPGdwOsfYflwmHCkPgs5DcKizvdpS/3gEUMW9/lvQvWp1bGTW1fpngC/uSVYAfH3TeSusL8TtlHUD4KAAEHjahk4YpuffGwEBN/dUmpHqdHyJ3Ap8O3UlQ4gCNrMRwLBBKiskL6a+RoBi/9XGlgc8L4/CUejQxaiIyqmomdhktOhfgDbDfaNDb4+yKIPd6IgmzDa0CByijWFmL2dlSRKagTIWXeIU9HDphoZiJeBTjAefHMxDhVSRFUgVcOkW3EGMahYVMFjWHhMluB2wAcbHYqF1LpsDF9C6s+CI2fDgh4wSuFEyGadjXAmIk3CugIRibLIti9ZtC8S4VSqfikGqPaoI122XyRYLBmsOmdiiTpqK1OklUQzpMcZmQRQV4M4oJCMkfRQXK+qvjifUcQd1bRdetW/LWjacYxvcttnVjWg5h0q4xw6rZyejSpZVZ78LzC4uyDNRQ4bymHSTMyM+SZ7D75mg/7YTlmNz7W8T00h0VEiGKB+F7iWYZFvSTiA4LVxttm2ATt5EoUWLJbY4EnLGrfsvEROlHtzlKn3H9VUT5tU/2dt3/EBv7foYzV/W4upyj04woO/gh6Vwwt3WGQAA) format("woff2-variations");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Plus Jakarta Sans Variable;font-style:normal;font-display:swap;font-weight:200 800;src:url(/assets/plus-jakarta-sans-vietnamese-wght-normal-qRpaaN48.woff2) format("woff2-variations");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Plus Jakarta Sans Variable;font-style:normal;font-display:swap;font-weight:200 800;src:url(/assets/plus-jakarta-sans-latin-ext-wght-normal-DmpS2jIq.woff2) format("woff2-variations");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Plus Jakarta Sans Variable;font-style:normal;font-display:swap;font-weight:200 800;src:url(/assets/plus-jakarta-sans-latin-wght-normal-eXO_dkmS.woff2) format("woff2-variations");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--color-bg: #F7F8FC;--color-surface: #ffffff;--color-primary: #2E5CE5;--color-primary-hover: #1D4ED8;--color-primary-subtle: #EEF2FF;--color-secondary: #64748B;--color-secondary-hover: #475569;--color-text: #1A1F36;--color-text-secondary: #697386;--color-muted: #94A3B8;--color-success: #22C55E;--color-error: #EF4444;--color-error-bg: #FEF2F2;--color-warning: #F59E0B;--color-border: #E2E8F0;--color-border-subtle: #F1F5F9;--color-hover: rgba(15, 23, 42, .06);--color-overlay: rgba(15, 23, 42, .5);--color-input-focus: rgba(46, 92, 229, .15);--shadow-xs: 0 1px 2px rgba(15, 23, 42, .05);--shadow-sm: 0 1px 3px rgba(15, 23, 42, .06), 0 1px 2px rgba(15, 23, 42, .04);--shadow-md: 0 4px 6px -1px rgba(15, 23, 42, .07), 0 2px 4px -2px rgba(15, 23, 42, .05);--shadow-lg: 0 10px 15px -3px rgba(15, 23, 42, .08), 0 4px 6px -4px rgba(15, 23, 42, .04);--shadow-xl: 0 20px 25px -5px rgba(15, 23, 42, .1), 0 8px 10px -6px rgba(15, 23, 42, .06);--radius-sm: 8px;--radius-md: 12px;--radius-lg: 16px;--radius-full: 50%;--font-sans: "Plus Jakarta Sans Variable", "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--space-xs: 4px;--space-sm: 8px;--space-md: 16px;--space-lg: 24px;--space-xl: 32px;--space-2xl: 48px;--transition-fast: .12s ease;--transition-base: .2s ease}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;-webkit-text-size-adjust:100%}body{font-family:var(--font-sans);background:var(--color-bg);color:var(--color-text);line-height:1.6;min-height:100dvh;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overscroll-behavior-y:contain}.pull-to-refresh{position:fixed;top:-48px;left:0;right:0;height:48px;display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:40;transition:opacity .15s}.pull-to-refresh-badge{width:40px;height:40px;border-radius:50%;background:var(--color-surface);border:1px solid var(--color-border);box-shadow:var(--shadow-md);display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary)}#root{min-height:100dvh}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:1px solid transparent;border-radius:var(--radius-sm);font-family:var(--font-sans);font-size:.875rem;font-weight:600;padding:10px 18px;cursor:pointer;transition:all var(--transition-base);-webkit-tap-highlight-color:transparent;letter-spacing:-.01em;line-height:1.25}.btn:disabled{opacity:.45;cursor:not-allowed}.btn:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.btn-primary{background:var(--color-primary);color:#fff;box-shadow:0 1px 2px #2e5ce54d,inset 0 1px #ffffff1a}.btn-primary:hover:not(:disabled){background:var(--color-primary-hover);box-shadow:0 2px 4px #2e5ce559,inset 0 1px #ffffff1a;transform:translateY(-.5px)}.btn-primary:active:not(:disabled){transform:translateY(0);box-shadow:0 1px 2px #2e5ce54d}.btn-secondary{background:var(--color-surface);color:var(--color-text);border-color:var(--color-border);box-shadow:var(--shadow-xs)}.btn-secondary:hover:not(:disabled){background:var(--color-border-subtle);border-color:#cbd5e1}.btn-danger{background:var(--color-error);color:#fff;box-shadow:0 1px 2px #ef44444d}.btn-danger:hover:not(:disabled){background:#dc2626;box-shadow:0 2px 4px #ef444459}.btn-link{background:none;border:none;color:var(--color-primary);padding:0;font-weight:500;box-shadow:none}.btn-link:hover:not(:disabled){text-decoration:underline;background:none;box-shadow:none}.btn-spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite;vertical-align:middle;margin-right:6px}.btn-spinner-current{border-color:#7f7f7f40;border-top-color:currentColor}.btn-sm{font-size:.8125rem;padding:6px 12px;border-radius:6px}.btn-full{width:100%}.form-label{display:flex;flex-direction:column;gap:6px;font-size:.8125rem;font-weight:600;color:var(--color-text);margin-bottom:var(--space-md);letter-spacing:-.01em}.form-input,.form-textarea,.form-select{font-family:var(--font-sans);font-size:.9375rem;padding:10px 12px;border:1.5px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface);color:var(--color-text);transition:border-color var(--transition-fast),box-shadow var(--transition-fast);width:100%}.form-input:focus,.form-textarea:focus,.form-select:focus{outline:none;border-color:var(--color-primary);box-shadow:0 0 0 3px var(--color-input-focus)}.form-input-mono{font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.8125rem}.form-input::placeholder,.form-textarea::placeholder{color:var(--color-muted)}.form-textarea{resize:vertical;min-height:80px;line-height:1.5}.form-error{background:var(--color-error-bg);color:var(--color-error);border:1px solid #FECACA;border-radius:var(--radius-sm);padding:var(--space-sm) var(--space-md);font-size:.8125rem;font-weight:500;margin-bottom:var(--space-md)}.form-warning{background:#fef3c7;color:#92400e;border:1px solid #FDE68A;border-radius:var(--radius-sm);padding:var(--space-sm) var(--space-md);font-size:.8125rem;font-weight:500;margin-bottom:var(--space-md)}.dashboard{min-height:100dvh;display:flex;flex-direction:column;padding-bottom:0}.dashboard-main{flex:1;padding:var(--space-md);width:100%}.status-dot{display:inline-block;width:8px;height:8px;border-radius:var(--radius-full);flex-shrink:0;box-shadow:0 0 0 2px #fffc}.status-spinner{display:inline-flex;align-items:center;gap:2px;flex-shrink:0;height:8px}.status-spinner:before,.status-spinner>span,.status-spinner:after{content:"";display:block;width:3px;height:3px;border-radius:var(--radius-full);background-color:var(--color-success);animation:marching-dot 1.2s ease-in-out infinite}.status-spinner:before{animation-delay:0s}.status-spinner>span{animation-delay:.2s}.status-spinner:after{animation-delay:.4s}@keyframes marching-dot{0%,60%,to{opacity:.25;transform:scale(.8)}30%{opacity:1;transform:scale(1)}}.pair-page{display:flex;align-items:flex-start;justify-content:center;min-height:100dvh;padding:var(--space-xl) var(--space-md);background:var(--color-bg)}.pair-card{width:100%;max-width:420px;display:flex;flex-direction:column;gap:var(--space-lg)}.pair-header{text-align:center}.pair-title{font-size:1.5rem;font-weight:700;color:var(--color-text);letter-spacing:-.02em}.pair-subtitle{margin-top:var(--space-xs);font-size:.875rem;color:var(--color-text-secondary)}.pair-instructions{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-md)}.pair-instruction-block{display:flex;flex-direction:column;gap:var(--space-sm)}.pair-instruction-heading{font-size:.8125rem;font-weight:600;color:var(--color-text);letter-spacing:-.01em}.pair-steps{list-style:none;counter-reset:pair-step;display:flex;flex-direction:column;gap:6px;padding:0}.pair-steps li{counter-increment:pair-step;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.5;display:flex;flex-wrap:wrap;gap:0 var(--space-sm)}.pair-steps li:before{content:counter(pair-step) ".";font-weight:600;color:var(--color-muted);min-width:16px;flex-shrink:0}.pair-steps li .pair-command{flex-basis:100%}.pair-steps code{font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.75rem;background:var(--color-bg);border:1px solid var(--color-border);border-radius:4px;padding:1px 5px;color:var(--color-text);white-space:nowrap}.pair-command{display:block;margin-top:4px;padding:6px 10px!important;border-radius:var(--radius-sm)!important;overflow-x:auto;-webkit-overflow-scrolling:touch}.pair-platform-label{display:block;flex-basis:100%;margin-top:8px;font-size:.75rem;font-weight:600;color:var(--color-muted)}.pair-instruction-divider{height:1px;background:var(--color-border)}.pair-form{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-lg);display:flex;flex-direction:column;gap:var(--space-md);box-shadow:var(--shadow-sm)}.pair-code-input{font-size:1.5rem!important;letter-spacing:.25em;text-align:center;padding:14px 12px!important;text-transform:uppercase}.pair-code-input::placeholder{letter-spacing:.25em;opacity:.35}.pair-label-hint{font-weight:400;color:var(--color-muted);font-size:.75rem}.pair-error{font-size:.8125rem;color:var(--color-error);background:var(--color-error-bg);padding:var(--space-sm) var(--space-md);border-radius:var(--radius-sm);line-height:1.4}.loading-state,.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--space-2xl) 0;gap:var(--space-sm)}.empty-state-text{font-size:1rem;font-weight:600;color:var(--color-text-secondary)}.empty-state-hint{font-size:.8125rem;color:var(--color-muted)}.revoked-state{display:flex;flex-direction:column;align-items:center;text-align:center;padding:var(--space-2xl) var(--space-lg);margin:var(--space-md) 0;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);animation:revokedFadeIn .35s ease}@keyframes revokedFadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.revoked-icon{width:56px;height:56px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-full);background:var(--color-error-bg);color:var(--color-error);margin-bottom:var(--space-md)}.revoked-title{font-size:1.125rem;font-weight:700;color:var(--color-text);margin-bottom:var(--space-xs)}.revoked-description{font-size:.875rem;line-height:1.6;color:var(--color-text-secondary);max-width:320px;margin-bottom:var(--space-lg)}.revoked-command{padding:8px 16px;background:var(--color-bg);border:1px solid var(--color-border);border-radius:var(--radius-sm);margin-bottom:var(--space-lg)}.revoked-command code{font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.8125rem;color:var(--color-text);letter-spacing:-.01em}.revoked-actions{display:flex;gap:var(--space-sm);width:100%;max-width:280px}.revoked-actions .btn{flex:1}.spinner{width:24px;height:24px;border:2.5px solid var(--color-border);border-top-color:var(--color-primary);border-radius:var(--radius-full);animation:spin .7s linear infinite}.spinner-lg{width:40px;height:40px;border-width:3px}.capability-toggles-loading{display:flex;justify-content:center;padding:var(--space-lg)}@keyframes spin{to{transform:rotate(360deg)}}.session-composer{background:var(--color-surface);border-radius:var(--radius-md);border:1px solid var(--color-border);box-shadow:var(--shadow-sm);padding:var(--space-sm);margin-bottom:var(--space-md);display:flex;flex-direction:column;gap:var(--space-sm)}.session-composer:focus-within{border-color:var(--color-primary);box-shadow:0 0 0 2px var(--color-input-focus)}.session-composer-textarea{width:100%;border:none;resize:vertical;outline:none;background:transparent;color:var(--color-text);font-family:var(--font-sans);font-size:.9375rem;line-height:1.5;padding:var(--space-xs) var(--space-sm);min-height:3.5em}.session-composer-textarea::placeholder{color:var(--color-muted)}.session-composer-controls{display:flex;align-items:center;gap:var(--space-sm);padding-left:var(--space-xs)}.session-composer-yolo{display:inline-flex;align-items:center;gap:4px;font-size:.8rem;color:var(--color-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none}.session-composer-yolo input{margin:0;cursor:pointer}.session-composer-controls .chat-send-btn{margin-left:auto}.fab{position:fixed;right:var(--space-lg);bottom:calc(var(--space-lg) + env(safe-area-inset-bottom,0px));width:56px;height:56px;border-radius:50%;border:none;background:var(--color-primary);color:#fff;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:var(--shadow-lg);transition:background var(--transition-base),transform var(--transition-fast),box-shadow var(--transition-base);z-index:50;-webkit-tap-highlight-color:transparent}.fab:hover{background:var(--color-primary-hover);box-shadow:var(--shadow-xl);transform:translateY(-1px)}.fab:active{transform:translateY(0)}.fab:focus-visible{outline:2px solid var(--color-primary);outline-offset:3px}.section-label{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--color-muted);margin:var(--space-md) 0 var(--space-xs)}.agent-picker-section-inline{display:flex;align-items:center;gap:var(--space-xs)}.agent-picker-section-inline .form-select{width:auto;min-width:0;font-size:.8rem;padding:4px 8px}.agent-picker-section-inline .agent-picker-single{font-size:.8rem;color:var(--color-text)}.task-list{display:flex;flex-direction:column;gap:10px;padding-bottom:calc(56px + var(--space-lg) * 2 + env(safe-area-inset-bottom,0px))}.task-card{background:var(--color-surface);border-radius:var(--radius-md);border:1px solid var(--color-border);box-shadow:var(--shadow-sm);padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-sm);cursor:pointer;transition:box-shadow var(--transition-base),border-color var(--transition-base);-webkit-tap-highlight-color:transparent}.task-card:hover{box-shadow:var(--shadow-md);border-color:#cbd5e1}.task-card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm)}.task-card-title-row{display:flex;align-items:center;gap:10px;min-width:0;flex:1}.task-card-name{font-size:.9375rem;font-weight:600;letter-spacing:-.01em;color:var(--color-text);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.task-card-meta{display:flex;flex-wrap:wrap;gap:var(--space-sm);font-size:.75rem;color:var(--color-text-secondary)}.task-card-agent{color:var(--color-text-tertiary)}.task-card-last-event-link{color:var(--color-primary);cursor:pointer;text-decoration:underline}.task-card-actions{display:flex;gap:var(--space-xs);flex-shrink:0}.task-card-menu{position:relative}.task-card-menu-btn{background:none;border:none;color:var(--color-text-secondary);font-size:1.5rem;min-width:44px;min-height:44px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);cursor:pointer;line-height:1}.task-card-menu-btn:hover{background:var(--color-hover);color:var(--color-text)}.task-card-menu-dropdown{position:absolute;right:0;top:100%;margin-top:4px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);box-shadow:var(--shadow-md);min-width:150px;z-index:10;overflow:hidden}.task-card-menu-dropdown button{display:flex;align-items:center;gap:var(--space-sm);width:100%;text-align:left;padding:var(--space-sm) var(--space-md);background:none;border:none;font-size:.9375rem;color:var(--color-text);cursor:pointer;white-space:nowrap}.task-card-menu-dropdown button:hover{background:var(--color-hover)}.task-card-menu-dropdown .menu-item-danger{color:var(--color-error)}.menu-icon{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;font-size:.875rem;flex-shrink:0;line-height:1}.bottom-sheet-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-overlay);z-index:1000;display:flex;align-items:flex-end;justify-content:center;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);animation:fadeIn var(--transition-fast)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.bottom-sheet{background:var(--color-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;width:100%;max-width:480px;padding:var(--space-sm) var(--space-md) var(--space-xl);box-shadow:var(--shadow-xl);animation:sheetSlideUp .25s cubic-bezier(.16,1,.3,1)}@keyframes sheetSlideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}.bottom-sheet-handle{width:36px;height:4px;background:var(--color-border);border-radius:2px;margin:0 auto var(--space-md)}.bottom-sheet-title{font-size:.9375rem;font-weight:600;color:var(--color-text);padding:0 var(--space-xs) var(--space-md);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bottom-sheet-actions{display:flex;flex-direction:column}.bottom-sheet-actions button{display:flex;align-items:center;gap:var(--space-md);width:100%;text-align:left;padding:var(--space-md);background:none;border:none;border-radius:var(--radius-sm);font-size:.9375rem;font-family:var(--font-sans);color:var(--color-text);cursor:pointer;transition:background var(--transition-fast)}.bottom-sheet-actions button:active{background:var(--color-hover)}.bottom-sheet-actions .menu-item-danger{color:var(--color-error)}.task-form-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:transparent;display:flex;align-items:stretch;justify-content:center;z-index:100;padding:0;overflow:hidden;overscroll-behavior:contain}.task-form{background:var(--color-surface);border-radius:0;padding:var(--space-lg) var(--space-md);width:100%;max-width:none;max-height:none;overflow-y:auto;animation:slideUp .25s cubic-bezier(.16,1,.3,1);display:flex;flex-direction:column;gap:var(--space-md)}@media(min-width:600px){.task-form-overlay{background:var(--color-overlay);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);align-items:center;padding:var(--space-md)}.task-form{border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);max-width:540px;max-height:90dvh;padding:var(--space-lg)}}@keyframes slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.task-form-header{display:flex;align-items:center;justify-content:space-between}.task-form h2{font-size:1.0625rem;font-weight:700;letter-spacing:-.02em;margin-bottom:0}.plan-actions{display:flex;gap:var(--space-sm);flex-wrap:wrap}.permissions-dialog{display:flex;flex-direction:column;gap:var(--space-md);overflow:hidden;flex:1;min-height:0}.permissions-dialog h2{margin:0;font-size:1.125rem}.permissions-dialog-scroll{flex:1;min-height:0;overflow-y:auto}.permissions-empty{color:var(--color-text-secondary);font-size:.875rem;font-style:italic;margin:var(--space-md) 0}.permissions-section{border-top:1px solid var(--color-border);padding-top:var(--space-md);margin-top:var(--space-md)}.permissions-section h3{margin:0 0 var(--space-xs) 0;font-size:.95rem}.permissions-list{margin:0;padding-left:1.25em}.permission-item{margin-bottom:var(--space-sm);display:flex;flex-direction:column}.permission-tool{font-weight:600;font-family:var(--font-mono, monospace)}.permission-desc{color:var(--color-text-secondary);font-size:.9em}.permissions-dialog-actions{display:flex;gap:var(--space-sm);align-items:center;justify-content:flex-end}@media(min-width:600px){.permissions-dialog{max-width:none}}.result-times{display:flex;flex-direction:column;gap:var(--space-xs);font-size:.8125rem;color:var(--color-text-secondary);margin-bottom:var(--space-sm)}.skeleton-line{height:.875rem;border-radius:var(--radius-sm);background:var(--color-border);animation:skeleton-pulse 1.2s ease-in-out infinite}@keyframes skeleton-pulse{0%,to{opacity:.4}50%{opacity:1}}.granted-permissions-row{flex-basis:100%}.granted-permissions-row .btn-link{padding:0}.schedule-section{display:flex;flex-direction:column;gap:var(--space-sm)}.schedule-section-title{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--color-muted);margin:0}.schedule-section-hint{font-weight:400;text-transform:none;letter-spacing:0;color:var(--color-text-secondary);margin-left:6px}.schedule-reactive{display:flex;flex-direction:column;gap:var(--space-xs)}.yolo-inline{display:inline-flex;align-items:center;gap:4px;font-size:.8rem;color:var(--color-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap}.yolo-inline input{margin:0;cursor:pointer}.yolo-warning{flex-basis:100%;margin:0;font-size:.75rem;color:var(--color-text-secondary);line-height:1.4}.trigger-row-card{display:flex;align-items:center;gap:8px;padding:4px 0}.trigger-row-content{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0}.trigger-row-top{display:flex;align-items:center;gap:8px}.trigger-row-top .form-select{flex:0 0 auto;width:auto}.trigger-row-top .form-input{flex:1}.trigger-details{display:flex;gap:6px}.trigger-details .form-input[type=date]{flex:1;min-width:0}.trigger-details .form-input[type=time]{flex:0 0 auto;width:auto}.schedule-section>.form-select,.trigger-row-card .form-select,.trigger-row-card .form-input{margin-bottom:0;font-size:.8125rem;padding:6px 8px;height:32px;box-sizing:border-box;min-width:0}.schedule-section>.form-select{width:100%}.trigger-row-card .form-select,.trigger-row-card .form-input{flex:1}.trigger-remove-btn{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:none;border-radius:var(--radius-full);background:transparent;color:var(--color-muted);font-size:1.1rem;line-height:1;cursor:pointer;transition:all var(--transition-fast);flex-shrink:0}.trigger-remove-btn:hover{background:var(--color-error-bg);color:var(--color-error)}.trigger-add-btn{display:inline-flex;align-items:center;gap:4px;border:1px dashed var(--color-border);border-radius:var(--radius-sm);background:transparent;color:var(--color-text-secondary);font-family:var(--font-sans);font-size:.8125rem;font-weight:500;padding:8px 14px;cursor:pointer;transition:all var(--transition-fast);width:100%;justify-content:center}.trigger-add-btn:hover{border-color:var(--color-primary);color:var(--color-primary);background:var(--color-primary-subtle)}.form-select{width:auto;min-width:80px}.toggles-section{display:flex;flex-direction:column;gap:10px;margin-bottom:var(--space-md)}.toggle-label{display:flex;align-items:center;gap:10px;font-size:.875rem;font-weight:500;cursor:pointer;-webkit-tap-highlight-color:transparent;color:var(--color-text)}.toggle-label input[type=checkbox]{width:16px;height:16px;accent-color:var(--color-primary);border-radius:4px}.toggles-group{display:flex;flex-direction:column;gap:var(--space-xs)}.command-section,.command-section-active .toggle-label,.task-title-field,.task-title-help{margin-bottom:0}.command-help-text{font-size:.75rem;color:var(--color-text-secondary);line-height:1.4;margin:6px 0 10px}.command-section-active .form-input{font-size:.8125rem;padding:6px 8px;height:32px;box-sizing:border-box}.form-actions{display:flex;gap:var(--space-sm);position:sticky;bottom:0;background:var(--color-surface);padding:var(--space-sm) 0}.confirm-modal-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-overlay);display:flex;align-items:center;justify-content:center;z-index:1000;padding:var(--space-md);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.confirm-modal{background:var(--color-surface);border-radius:var(--radius-lg);border:1px solid var(--color-border);padding:var(--space-xl);width:100%;max-width:380px;text-align:center;box-shadow:var(--shadow-xl);animation:slideUp .25s cubic-bezier(.16,1,.3,1)}.confirm-modal-title{font-size:1.125rem;font-weight:700;letter-spacing:-.02em;margin-bottom:var(--space-xs)}.confirm-modal-subtitle{font-size:.8rem;color:var(--color-muted);margin-bottom:var(--space-sm)}.confirm-modal-message{font-size:.9375rem;color:var(--color-text-secondary);margin-bottom:var(--space-lg);line-height:1.5}.confirm-modal-actions{display:flex;gap:var(--space-sm);justify-content:center}.confirm-modal-actions .btn{flex:1;padding:10px var(--space-lg);font-size:.875rem}.choice-options-stacked{flex-direction:column}.permission-modal{text-align:left;max-width:400px}.permission-modal .confirm-modal-title{text-align:center}.permission-modal .confirm-modal-message{text-align:center;word-break:break-word}.permission-list{display:flex;flex-direction:column;gap:var(--space-xs);margin-bottom:var(--space-lg)}.permission-item{display:flex;flex-direction:column;gap:2px;padding:var(--space-sm);background:var(--color-hover);border-radius:var(--radius-sm)}.permission-name{font-size:.8125rem;font-weight:600;color:var(--color-text)}.permission-desc{font-size:.75rem;color:var(--color-text-secondary);line-height:1.4}.permission-actions{display:flex;gap:var(--space-sm)}.permission-actions .btn{flex:1;padding:10px var(--space-md);font-size:.875rem}.permission-abort-link{display:block;width:100%;margin-top:var(--space-sm);background:none;border:none;color:var(--color-error);font-size:.8125rem;cursor:pointer;text-align:center;padding:var(--space-xs) 0;opacity:.8}.permission-abort-link:hover:not(:disabled){opacity:1;text-decoration:underline}.permission-abort-link:disabled{opacity:.45;cursor:not-allowed}.input-modal{text-align:left;max-width:400px}.input-modal .confirm-modal-title{text-align:center}.input-modal .confirm-modal-message{text-align:center;word-break:break-word}.input-list{display:flex;flex-direction:column;gap:var(--space-md);margin-bottom:var(--space-lg)}.input-item{display:flex;flex-direction:column;gap:var(--space-xs)}.input-label{font-size:.8125rem;font-weight:600;color:var(--color-text)}.input-field{width:100%;padding:var(--space-sm) var(--space-md);font-size:.875rem;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface);color:var(--color-text);box-sizing:border-box}.input-field:focus{outline:none;border-color:var(--color-primary);box-shadow:0 0 0 2px rgba(var(--color-primary-rgb, 99, 102, 241),.2)}.input-save-toggle{margin-bottom:var(--space-lg)}.input-actions{display:flex;gap:var(--space-sm)}.input-actions .btn{flex:1;padding:10px var(--space-md);font-size:.875rem}.passwords-dialog{text-align:left;max-width:420px}.passwords-dialog .confirm-modal-title{text-align:center}.passwords-dialog-scroll{max-height:min(50vh,400px);overflow-y:auto;margin-bottom:var(--space-lg);scrollbar-width:thin;scrollbar-color:var(--color-border) transparent}.passwords-empty{font-size:.875rem;color:var(--color-muted);text-align:center;padding:var(--space-md) 0;line-height:1.5}.passwords-list{list-style:none;margin:0;padding:0}.password-item{display:flex;align-items:center;gap:var(--space-sm);padding:var(--space-sm) 0;border-bottom:1px solid var(--color-border)}.password-item:last-child{border-bottom:none}.password-item-text{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.password-item-origin{font-size:.875rem;font-weight:500;color:var(--color-text);overflow-wrap:anywhere}.password-item-username{font-size:.8125rem;color:var(--color-muted);overflow-wrap:anywhere}.password-delete-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:none;cursor:pointer;color:var(--color-muted);border-radius:6px;flex-shrink:0;opacity:.7;transition:all var(--transition-fast)}.password-delete-btn:hover{color:var(--color-error);background:var(--color-error-bg);opacity:1}.host-picker-inline{margin:0 calc(var(--space-sm) * -1)}.host-picker-list{max-height:240px;overflow-y:auto;overflow-x:hidden;padding:2px var(--space-sm);scrollbar-width:thin;scrollbar-color:var(--color-border) transparent}.host-picker-list::-webkit-scrollbar{width:6px}.host-picker-list::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:3px}.host-picker-item-wrapper{position:relative}.host-picker-item{position:relative;display:flex;align-items:center;gap:8px;padding:9px 12px 9px 14px;margin:1px 0;border:none;background:none;cursor:pointer;font-family:var(--font-sans);font-size:.8125rem;color:var(--color-text-secondary);text-align:left;border-radius:8px;transition:background var(--transition-fast),color var(--transition-fast);-webkit-tap-highlight-color:transparent}.host-picker-item:hover{background:var(--color-border-subtle);color:var(--color-text)}.host-picker-item-active{background:var(--color-primary-subtle);color:var(--color-text);font-weight:500}.host-picker-item-active:hover{background:var(--color-primary-subtle)}.host-picker-item-active:before{content:"";position:absolute;left:4px;top:9px;bottom:9px;width:2px;border-radius:2px;background:var(--color-primary)}.host-picker-item-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:6px}.host-picker-pending{font-size:.6875rem;font-weight:500;color:var(--color-muted);background:var(--color-border-subtle);padding:1px 6px;border-radius:4px;flex-shrink:0}.host-picker-item-actions{flex-shrink:0;width:24px;display:flex;align-items:center;justify-content:center}.host-picker-edit-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;cursor:pointer;color:var(--color-muted);border-radius:6px;flex-shrink:0;opacity:.7;transition:all var(--transition-fast)}.host-picker-item:hover .host-picker-edit-btn,.host-picker-edit-btn:focus-visible{opacity:1}.host-picker-edit-btn:hover{color:var(--color-primary);background:#2e5ce51f;opacity:1}.host-picker-rename-input{font-size:.8125rem!important;padding:3px 6px!important;flex:1;min-width:0}.host-picker-delete-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;cursor:pointer;color:var(--color-muted);border-radius:6px;flex-shrink:0;opacity:.7;transition:all var(--transition-fast)}@media(hover:hover)and (pointer:fine){.host-picker-delete-btn{opacity:0}.host-picker-item:hover .host-picker-delete-btn,.host-picker-delete-btn:focus-visible{opacity:.7}}.host-picker-delete-btn:hover{color:var(--color-error);background:var(--color-error-bg);opacity:1}.hamburger-btn{display:flex;align-items:center;justify-content:center;width:40px;height:40px;padding:0;border:none;background:none;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--radius-sm);transition:background var(--transition-fast),color var(--transition-fast);-webkit-tap-highlight-color:transparent}.hamburger-btn:hover{background:var(--color-border-subtle);color:var(--color-text)}.hamburger-btn:active{background:var(--color-primary-subtle);color:var(--color-primary)}.drawer-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-overlay);z-index:100;animation:drawerFadeIn .2s ease}.drawer-panel{position:fixed;top:0;left:0;bottom:0;width:296px;max-width:84vw;background:var(--color-surface);box-shadow:var(--shadow-xl);z-index:101;display:flex;flex-direction:column;overflow-y:auto;overscroll-behavior:contain;animation:drawerSlideIn .25s ease;scrollbar-width:thin;scrollbar-color:var(--color-border) transparent}.drawer-panel::-webkit-scrollbar{width:6px}.drawer-panel::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:3px}.drawer-close-btn{position:absolute;top:8px;right:8px;z-index:1;display:flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:none;background:none;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--radius-sm);transition:background var(--transition-fast)}.drawer-close-btn:hover{background:var(--color-border-subtle);color:var(--color-text)}.drawer-section{padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-sm)}.drawer-section+.drawer-section{padding-top:0}.drawer-section:has(>.host-picker-inline){padding-bottom:var(--space-xs)}.drawer-section-label{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--color-muted);margin:0 0 var(--space-xs)}.drawer-section-hint{font-size:.8125rem;color:var(--color-muted);line-height:1.45;margin:0 0 var(--space-sm) 0}.pair-checkbox{display:flex;align-items:flex-start;gap:var(--space-sm);padding:var(--space-sm) 0;cursor:pointer}.pair-checkbox input[type=checkbox]{margin-top:.2rem;flex-shrink:0}.pair-checkbox-text{display:flex;flex-direction:column;gap:.15rem}.pair-checkbox-title{font-size:.9375rem;font-weight:500}.pair-checkbox-hint{font-size:.8125rem;color:var(--color-muted);line-height:1.4}.drawer-toggle-group{display:flex;flex-direction:column;gap:var(--space-sm)}.drawer-toggle-group-divided{border-top:1px solid var(--color-border);padding-top:var(--space-sm);margin-top:var(--space-xs)}.drawer-toggle{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm)}.drawer-toggle-label{font-size:.85rem;color:var(--color-text)}.toggle-switch{position:relative;width:40px;height:22px;border-radius:11px;border:none;background:var(--color-border);cursor:pointer;padding:0;transition:background .2s;flex-shrink:0}.toggle-switch-on{background:var(--color-primary)}.toggle-switch-thumb{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;transition:transform .2s}.toggle-switch-on .toggle-switch-thumb{transform:translate(18px)}.toggle-switch:disabled{opacity:.5;cursor:not-allowed}.drawer-footer{margin-top:auto;padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-md);border-top:1px solid var(--color-border-subtle)}.drawer-version{font-size:.75rem;color:var(--color-muted);font-variant-numeric:tabular-nums}.drawer-agents{display:flex;flex-direction:column;gap:var(--space-sm)}.drawer-agents-section{display:flex;flex-direction:column;gap:3px}.drawer-agents-heading{font-size:.6875rem;font-weight:600;color:var(--color-muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}.drawer-agents-row{display:flex;justify-content:space-between;align-items:baseline;gap:var(--space-sm);font-size:.75rem;color:var(--color-text-secondary)}.drawer-agents-version{font-variant-numeric:tabular-nums;color:var(--color-muted)}.drawer-host-info{display:flex;flex-direction:column;gap:3px}.drawer-host-time{font-size:.75rem;color:var(--color-text-secondary);font-variant-numeric:tabular-nums}.drawer-legal{display:flex;align-items:center;flex-wrap:wrap;gap:0 var(--space-xs);font-size:.6875rem;color:var(--color-muted);letter-spacing:.02em}.drawer-legal a{color:var(--color-muted);text-decoration:none;transition:color var(--transition-fast)}.drawer-legal a:hover{color:var(--color-primary)}.drawer-legal-sep{opacity:.55}.drawer-divider{height:1px;background:var(--color-border-subtle);margin:0 var(--space-md)}@keyframes drawerFadeIn{0%{opacity:0}to{opacity:1}}@keyframes drawerFadeOut{0%{opacity:1}to{opacity:0}}@keyframes drawerSlideIn{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes drawerSlideOut{0%{transform:translate(0)}to{transform:translate(-100%)}}.drawer-overlay-closing{animation:drawerFadeOut .2s ease forwards}.drawer-panel-closing{animation:drawerSlideOut .2s ease forwards}.app-header{position:sticky;top:0;z-index:10;background:color-mix(in srgb,var(--color-surface) 92%,transparent);border-bottom:1px solid var(--color-border);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.app-title-bar{position:relative;display:flex;align-items:center;justify-content:center;padding:8px 0}.app-title-bar .hamburger-btn{position:absolute;left:4px;top:50%;transform:translateY(-50%)}.app-title{margin:0;font-size:1rem;font-weight:700;letter-spacing:-.01em;color:#1e3a8a}.conn-status{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:inline-flex;color:#60a5fa}.conn-status--lan{color:#2563eb}.conn-status--disconnected{color:#dc2626}.conn-status-btn{background:none;border:none;padding:4px 8px;cursor:pointer;line-height:1;display:inline-flex;align-items:center;justify-content:center;border-radius:6px;color:inherit}.conn-status-btn:hover,.conn-status-btn:focus-visible{background:var(--color-bg);outline:none}.conn-status-popover{position:absolute;top:calc(100% + 6px);right:0;background:var(--color-text);color:var(--color-surface);font-size:.75rem;font-weight:500;padding:6px 10px;border-radius:6px;white-space:nowrap;pointer-events:none;opacity:0;transform:translateY(-2px);transition:opacity .12s,transform .12s;z-index:20}.conn-status:hover .conn-status-popover,.conn-status-popover--open{opacity:1;transform:translateY(0)}.conn-status--connecting .conn-status-btn>svg{animation:conn-status-pulse 1.2s ease-in-out infinite}@keyframes conn-status-pulse{0%,to{opacity:.35}50%{opacity:1}}.tab-bar{display:flex;align-items:center}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:12px 0;border:none;background:none;font-family:var(--font-sans);font-size:.875rem;font-weight:600;color:var(--color-text-secondary);cursor:pointer;border-bottom:2px solid transparent;transition:color var(--transition-fast),border-color var(--transition-fast)}.tab-icon{flex-shrink:0}.tab-btn:hover{color:var(--color-text)}.tab-btn-active{color:var(--color-primary);border-bottom-color:var(--color-primary)}.sessions-view{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center}.sessions-card{background:var(--color-surface);border-radius:var(--radius-md);border:1px solid var(--color-border);box-shadow:var(--shadow-sm);padding:var(--space-md);display:flex;align-items:center;gap:var(--space-sm);cursor:pointer;transition:box-shadow var(--transition-base),border-color var(--transition-base);-webkit-tap-highlight-color:transparent}.sessions-card:hover{box-shadow:var(--shadow-md);border-color:#cbd5e1}.sessions-card-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--space-xs)}.sessions-card-name{font-size:.9375rem;font-weight:600;letter-spacing:-.01em;color:var(--color-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sessions-card-meta{display:flex;flex-wrap:wrap;gap:var(--space-sm);font-size:.75rem;color:var(--color-text-secondary)}.sessions-card-chevron{flex-shrink:0;align-self:center;color:var(--color-text-secondary);font-size:1.25rem;line-height:1;opacity:.4;transition:opacity var(--transition-base)}.sessions-card:hover .sessions-card-chevron{opacity:.8}.star-button{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;margin:-8px 0;border:none;border-radius:var(--radius-full);background:none;cursor:pointer;color:var(--color-muted);transition:color var(--transition-fast),background var(--transition-fast);-webkit-tap-highlight-color:transparent}.star-button:hover{background:var(--color-hover)}.star-button--on{color:var(--color-warning)}.star-button:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.sessions-filter-chip{display:inline-flex;align-items:center;gap:var(--space-xs);padding:4px 10px;font-size:.8125rem;color:var(--color-text-secondary);background:var(--color-bg);border:1px solid var(--color-border);border-radius:999px}.sessions-filter-chip button{display:inline-flex;align-items:center;justify-content:center;border:none;background:none;cursor:pointer;padding:0;color:var(--color-text-secondary);font-size:1rem;line-height:1}.sessions-filter-chip button:hover{color:var(--color-text)}.run-detail{display:flex;flex-direction:column;gap:var(--space-md);background:var(--color-surface);border-radius:var(--radius-md);border:1px solid var(--color-border);box-shadow:var(--shadow-sm);padding:var(--space-md)}.run-detail-header{display:flex;align-items:center;justify-content:space-between}.run-detail-back{display:inline-flex;align-items:center;gap:var(--space-xs);align-self:flex-start;border:none;background:none;cursor:pointer;padding:0;color:var(--color-text-secondary);font-size:.8125rem}.run-detail-back:hover{color:var(--color-text)}.run-detail-back--footer{align-self:center;margin-top:var(--space-sm);padding:8px 16px;border:1px solid var(--color-border);border-radius:var(--radius-md)}.run-detail-back--footer:hover{background:var(--color-surface-hover, var(--color-bg))}.chat-thread{display:flex;flex-direction:column;gap:var(--space-sm);overflow-y:auto;flex:1;min-height:0}.chat-message{display:flex;flex-direction:column;gap:var(--space-xs);max-width:85%;min-width:0;padding:var(--space-sm) var(--space-md);border-radius:var(--radius-md);font-size:.8125rem;line-height:1.6}.chat-message--assistant{align-self:flex-start;background:var(--color-surface);border:1px solid var(--color-border)}.chat-message--user{align-self:flex-end;background:var(--color-primary-subtle);border:1px solid var(--color-border-subtle)}.chat-message--assistant.chat-message--stderr .chat-message-content,.chat-message--assistant.chat-message--stderr .chat-message-content pre,.chat-message--assistant.chat-message--stderr .chat-message-content code{color:var(--color-error)}.chat-message-agent{font-size:.6875rem;font-weight:500;color:var(--color-text-tertiary);margin-bottom:var(--space-xs)}.chat-message-agent-version{font-weight:400;opacity:.7}.chat-message-content{color:var(--color-text);overflow-wrap:break-word}.chat-message-content h1,.chat-message-content h2,.chat-message-content h3,.chat-message-content h4{margin:.75em 0 .25em;font-weight:600}.chat-message-content h1{font-size:1.1rem}.chat-message-content h2{font-size:1rem}.chat-message-content h3{font-size:.9375rem}.chat-message-content p{margin:.5em 0}.chat-message-content ul,.chat-message-content ol{margin:.5em 0;padding-left:1.5em}.chat-message-content code{font-size:.8em;background:var(--color-hover);padding:.15em .35em;border-radius:4px}.chat-message-content pre{background:var(--color-bg);border-radius:var(--radius-sm);padding:var(--space-sm);overflow-x:auto;margin:.5em 0}.chat-message-content pre code{background:none;padding:0}.chat-message-content table{border-collapse:collapse;width:100%;margin:.5em 0}.chat-message-content th,.chat-message-content td{border:1px solid var(--color-border);padding:.35em .6em;text-align:left}.chat-message-content th{background:var(--color-bg);font-weight:600}.chat-message-meta{display:flex;align-items:center;gap:var(--space-xs);font-size:.6875rem;color:var(--color-muted)}.chat-message-type{font-weight:600;text-transform:uppercase;letter-spacing:.04em;font-size:.625rem}.chat-message-attachments{display:flex;flex-wrap:wrap;gap:var(--space-xs)}.chat-attachment-chip{display:inline-flex;align-items:center;padding:.2em .6em;font-size:.75rem;background:var(--color-bg);border:1px solid var(--color-border);border-radius:var(--radius-sm);cursor:pointer;color:var(--color-primary);font-weight:500}.chat-attachment-chip:hover{background:var(--color-primary-subtle)}.chat-message-report{margin-top:var(--space-xs);padding:var(--space-sm);background:var(--color-bg);border-radius:var(--radius-sm);border:1px solid var(--color-border);font-size:.8125rem;line-height:1.6;color:var(--color-text-secondary)}.chat-status{display:flex;flex-direction:column;align-items:center;gap:var(--space-xs);font-size:.6875rem;color:var(--color-muted);padding:var(--space-xs) 0}.chat-status>div{display:flex;align-items:center;gap:var(--space-xs)}.chat-status--error{color:var(--color-error)}.chat-status-detail{margin-top:var(--space-xs);padding:var(--space-sm);background:var(--color-bg);border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:.75rem;font-family:SF Mono,Fira Code,monospace;white-space:pre-wrap;word-break:break-word;max-width:100%;max-height:200px;overflow-y:auto;color:var(--color-text-secondary)}.chat-status-time{color:var(--color-muted)}.chat-stop-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border-radius:50%;flex-shrink:0;margin-left:auto;background:var(--color-error);color:#fff;border-color:var(--color-error)}.chat-stop-btn:hover:not(:disabled){background:#dc2626;border-color:#dc2626}.chat-input-bar{display:flex;gap:var(--space-xs);padding:var(--space-sm) 0;flex-shrink:0}.chat-input{flex:1;min-width:0;padding:var(--space-sm) var(--space-md);border:1px solid var(--color-border);border-radius:var(--radius-md);font-size:.8125rem;outline:none;background:var(--color-surface);color:var(--color-text)}.chat-input:focus{border-color:var(--color-primary);box-shadow:0 0 0 2px var(--color-primary-subtle)}.chat-input:disabled{opacity:.6}.chat-send-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border-radius:50%;flex-shrink:0}.report-dialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;background:var(--color-overlay);display:flex;align-items:stretch;justify-content:center}.report-dialog{display:flex;flex-direction:column;width:100%;max-width:800px;background:var(--color-surface);overflow:hidden}.report-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-sm) var(--space-md);border-bottom:1px solid var(--color-border);flex-shrink:0}.report-dialog-title{font-size:.875rem;font-weight:600;color:var(--color-text)}.report-dialog-close{display:flex;align-items:center;justify-content:center;border:none;background:none;cursor:pointer;color:var(--color-text-secondary);padding:4px;border-radius:var(--radius-sm)}.report-dialog-close:hover{background:var(--color-hover);color:var(--color-text)}.report-dialog-body{flex:1;overflow-y:auto;padding:var(--space-md);font-size:.8125rem;line-height:1.6;color:var(--color-text)}.report-dialog-body h1,.report-dialog-body h2,.report-dialog-body h3,.report-dialog-body h4{margin:.75em 0 .25em;font-weight:600}.report-dialog-body h1{font-size:1.1rem}.report-dialog-body h2{font-size:1rem}.report-dialog-body h3{font-size:.9375rem}.report-dialog-body p{margin:.5em 0}.report-dialog-body img{max-width:100%;height:auto}.report-dialog-body ul,.report-dialog-body ol{margin:.5em 0;padding-left:1.5em}.report-dialog-body code{font-size:.8em;background:var(--color-hover);padding:.15em .35em;border-radius:4px}.report-dialog-body pre{background:var(--color-bg);border-radius:var(--radius-sm);padding:var(--space-sm);overflow-x:auto;margin:.5em 0}.report-dialog-body pre code{background:none;padding:0}.report-dialog-body table{border-collapse:collapse;width:100%;margin:.5em 0}.report-dialog-body th,.report-dialog-body td{border:1px solid var(--color-border);padding:.35em .6em;text-align:left}.report-dialog-body th{background:var(--color-bg);font-weight:600}.report-dialog-body--frame{display:flex;padding:0}.file-viewer-frame{flex:1;width:100%;border:none;background:#fff}.file-viewer-text{margin:0;white-space:pre-wrap;word-break:break-word;font-size:.75rem}.file-viewer-loading{display:flex;flex-direction:column;gap:var(--space-sm)}.file-viewer-message{color:var(--color-text-secondary)}.file-viewer-download{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-sm)}.md-file-link{cursor:pointer}.md-file-image{max-width:100%;height:auto;cursor:zoom-in}.md-image-loading{display:block;width:100%;max-width:240px;height:6rem}.md-image-missing{font-style:italic;color:var(--color-text-secondary)}.chat-typing-indicator{display:flex;align-items:center;gap:4px;padding:4px 0}.chat-typing-indicator span{width:6px;height:6px;border-radius:50%;background:var(--color-muted);animation:chat-typing 1.4s infinite}.chat-typing-indicator span:nth-child(2){animation-delay:.2s}.chat-typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes chat-typing{0%,60%,to{opacity:.3;transform:scale(.8)}30%{opacity:1;transform:scale(1)}}.pair-consent{font-size:.75rem;color:var(--color-muted);text-align:center;line-height:1.5}.pair-consent a{color:var(--color-muted);text-decoration:underline}.pair-consent a:hover{color:var(--color-text-secondary)}.dashboard-content{display:flex;flex-direction:column;flex:1;min-height:100dvh;max-width:800px;margin:0 auto;width:100%}.drawer-panel-desktop{position:sticky;top:0;height:100dvh;width:320px;min-width:320px;background:var(--color-surface);border-right:1px solid var(--color-border);display:flex;flex-direction:column;overflow-y:auto;overscroll-behavior:contain;animation:none}@media(min-width:768px){.dashboard{flex-direction:row}.dashboard-main{padding:var(--space-lg)}.fab{right:max(var(--space-lg),calc((100vw - 1080px)/2))}}.swipe-row{position:relative;overflow:hidden;touch-action:pan-y}.swipe-row-action{position:absolute;top:0;right:0;bottom:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;background:var(--color-error, #dc2626);color:#fff;border:none;font-size:.75rem;font-weight:600;cursor:pointer;padding:0}.swipe-row-action--hidden{visibility:hidden;transition:visibility 0s linear .2s}.swipe-row-action-label{font-size:.75rem;line-height:1}.swipe-row-action:focus-visible{outline:2px solid var(--color-accent, #2E5CE5);outline-offset:-4px}.swipe-row-content{position:relative;background:var(--color-surface);transition:transform .2s cubic-bezier(.22,1,.36,1);will-change:transform}.swipe-row-content-dragging{transition:none}.app-filter-help{margin-top:var(--space-xs)}.app-filter-trigger{display:inline-block;padding:0;font-size:.875rem;font-weight:500;text-align:left}.app-filter-selected{display:inline-flex;align-items:center;gap:var(--space-sm);padding:6px 10px;border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);max-width:100%}.app-filter-selected-name{font-size:.9375rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-filter-selected-pkg{font-size:.75rem;color:var(--color-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-filter-selected-clear{display:flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;margin-left:auto;border:none;background:none;color:var(--color-muted);font-size:.875rem;cursor:pointer;border-radius:var(--radius-sm)}.app-filter-selected-clear:hover{background:var(--color-border-subtle);color:var(--color-text)}.app-filter-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-overlay);display:flex;align-items:center;justify-content:center;z-index:1100;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);padding:var(--space-md)}.app-filter-dialog{background:var(--color-surface);width:100%;max-width:480px;max-height:80dvh;border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);display:flex;flex-direction:column;overflow:hidden}.app-filter-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-md) var(--space-lg);border-bottom:1px solid var(--color-border)}.app-filter-header h2{margin:0;font-size:1rem;font-weight:600}.app-filter-close{display:flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:none;background:none;color:var(--color-muted);cursor:pointer;border-radius:var(--radius-sm)}.app-filter-close:hover{background:var(--color-border-subtle);color:var(--color-text)}.app-filter-search{margin:var(--space-md) var(--space-lg) 0;width:calc(100% - 2 * var(--space-lg))}.app-filter-list{flex:1;overflow-y:auto;list-style:none;margin:0;padding:var(--space-xs) 0 var(--space-md)}.app-filter-row{display:flex;align-items:center;gap:var(--space-md);padding:10px var(--space-lg);cursor:pointer;-webkit-user-select:none;user-select:none}.app-filter-row:hover{background:var(--color-hover, rgba(0, 0, 0, .04))}.app-filter-row-labels{display:flex;flex-direction:column;min-width:0;flex:1}.app-filter-row-name{font-size:.9375rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-filter-row-pkg{font-size:.75rem;color:var(--color-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-filter-empty{padding:var(--space-lg);text-align:center;color:var(--color-muted);font-size:.875rem}.app-filter-skeleton{cursor:default}.app-filter-skeleton-bar{flex:1;height:14px;border-radius:4px;background:linear-gradient(90deg,var(--color-border-subtle) 25%,var(--color-border) 50%,var(--color-border-subtle) 75%);background-size:200% 100%;animation:appFilterShimmer 1.2s ease-in-out infinite}@keyframes appFilterShimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.pair-setup{min-height:100dvh;display:flex;justify-content:center;padding:var(--space-lg);background:var(--color-bg)}.pair-setup-inner{width:100%;max-width:480px;display:flex;flex-direction:column;gap:var(--space-lg)}.pair-setup-title{margin:0;font-size:1.25rem;font-weight:600;line-height:1.35}.pair-setup-description{margin:0;color:var(--color-muted);font-size:.875rem}.pair-setup-loading{padding:var(--space-xl, 32px) var(--space-lg);min-height:200px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-md);color:var(--color-muted);font-size:.95rem}.pair-setup-actions{margin-top:var(--space-md)}
|