@yunazgr/pi-companion 0.3.0 → 0.3.2
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 +23 -3
- package/docs/automations.md +45 -1
- package/package.json +1 -1
- package/skills/companion-automations/SKILL.md +2 -0
- package/src/automation-mcp.ts +1 -1
- package/src/automation.ts +9 -3
- package/src/bridge.ts +5 -1
- package/src/protocol.ts +1 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Lightweight, local-first remote control for Pi sessions.
|
|
4
4
|
|
|
5
|
-
**New in 0.3.
|
|
5
|
+
**New in 0.3.2:** responsive automation and run-history tables, rounded status chips, matching clay-icon headers, and a single-column automation workspace with full-width mobile controls. **Edit automation** scrolls to and focuses the inline editor. Pull to refresh Overview, Sessions, Automations, or Settings—or use the Refresh button; unsaved settings stay intact. Camera-policy guidance now explains HTTPS proxy configuration. Existing rich results, staged job editing, optional retries, and answers-only automation sessions are retained. See [automations](docs/automations.md).
|
|
6
6
|
|
|
7
7
|
Pi Companion is deliberately not another agent runtime. Pi owns execution and conversation state. A single Rust daemon owns session discovery, pairing, temporary file exchange, browser fan-out, and the embedded web UI.
|
|
8
8
|
|
|
@@ -15,7 +15,7 @@ The UI is a static SvelteKit application. There is no Node runtime in production
|
|
|
15
15
|
<img src="docs/mobile.webp" alt="Session detail on a phone" width="28%" />
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
|
-
Screenshots use demo sessions, not private conversation data, and are regenerated with `node tools/screenshots.mjs` (see [Running locally from source](#running-locally-from-source)). See the [session menu](docs/session-menu.webp), [desktop session rows](docs/session-list-desktop.webp) and [mobile session list](docs/session-list.webp), plus [attachment previews](docs/attachments.webp), [file-grouped changes](docs/changes.webp) and [connection recovery](docs/connection-error.webp).
|
|
18
|
+
Screenshots use demo sessions, not private conversation data, and are regenerated with `node tools/screenshots.mjs` (see [Running locally from source](#running-locally-from-source)). See the [session menu](docs/session-menu.webp), [desktop session rows](docs/session-list-desktop.webp) and [mobile session list](docs/session-list.webp), plus [attachment previews](docs/attachments.webp), [file-grouped changes](docs/changes.webp) and [connection recovery](docs/connection-error.webp). Explore the new [automation list](docs/automations.webp), [summary](docs/automation-detail.webp), [run history](docs/automation-history.webp), [job editor](docs/automation-editor.webp), and [rich results](docs/automation-result.webp).
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
@@ -151,6 +151,12 @@ Pi's `companion_ask_user` tool asks one to four questions at once. Each question
|
|
|
151
151
|
|
|
152
152
|
Dialogs from other extensions (`ctx.ui.select`, `ctx.ui.confirm`, `ctx.ui.input`) are relayed to the same sheet while the terminal dialog stays open: whichever side answers first wins and the other closes. Question tools that draw their own `ctx.ui.custom` picker are relayed through a small adapter: pi-jar's `jar_ask` is supported, and a companion answer completes its terminal picker. Other `ctx.ui.custom` components and `ctx.ui.editor` stay terminal-only because they cannot be answered from outside. Pending questions are part of the session snapshot, so a browser that connects later still sees them.
|
|
153
153
|
|
|
154
|
+
## Refreshing your workspace
|
|
155
|
+
|
|
156
|
+
On Overview, Sessions, Automations, and Settings, pull down from the top of the page and release when prompted to refresh current data. Each page also provides a keyboard-accessible **Refresh** button with progress and error feedback. This refreshes data without reloading the app; Settings preserves unsaved edits. Gestures inside inputs and nested scrollable lists are left alone.
|
|
157
|
+
|
|
158
|
+
The Sessions, Devices, and Settings pages now share the automation page’s clay-icon header style. See [device management](docs/devices.webp), [settings](docs/settings.webp), and [mobile settings](docs/settings-mobile.webp).
|
|
159
|
+
|
|
154
160
|
## Notifications, camera and catching up
|
|
155
161
|
|
|
156
162
|
Settings → **Notifications and camera** (on every device, not just the console) asks the browser for both permissions from a tap, as browsers require, and shows whether each is allowed, blocked or unavailable. Overview also offers a one-time "Turn on notifications" banner.
|
|
@@ -254,6 +260,18 @@ which destroys one temporary file by opaque id.
|
|
|
254
260
|
|
|
255
261
|
This means the agent can consume uploaded artifacts using its normal file capabilities while Pi Companion retains ownership of upload placement and cleanup.
|
|
256
262
|
|
|
263
|
+
### Camera access through Cloudflare or another HTTPS proxy
|
|
264
|
+
|
|
265
|
+
Camera scanning works through an HTTPS tunnel; Companion serves `Permissions-Policy: camera=(self)`. A proxy response-header rule that replaces it with `camera=()` blocks the camera before the browser can ask permission. JavaScript cannot override that restriction.
|
|
266
|
+
|
|
267
|
+
For a Cloudflare **Modify Response Header** rule scoped to your Companion hostname (for example, `http.host eq "companion.readynaz.com"`), set `Permissions-Policy` to:
|
|
268
|
+
|
|
269
|
+
```text
|
|
270
|
+
geolocation=(), camera=(self), microphone=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=()
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Exclude the Companion hostname from any broader rule that still sets `camera=()`, and check Workers or other proxies for duplicate overrides. Keep Cloudflare Access authentication enabled and tunnel only the paired-device listener. Open Companion directly, not inside an iframe. After signing in, verify the final page response in browser DevTools → Network: its camera directive must be `camera=(self)`. The Access login redirect is not the app response. Reload the app (and restart an older daemon after upgrading), then allow Camera in browser site permissions. Manual pairing-code entry remains available.
|
|
274
|
+
|
|
257
275
|
## Workspace and tunnel connection errors
|
|
258
276
|
|
|
259
277
|
If a tunnel closes, the workspace stops, or your device loses its network, Companion shows an actionable **Workspace connection interrupted** notice with **Retry now** and reconnect instructions. On initial connection failure, it shows **Workspace unavailable**, not a misleading empty session list or “session not found.” Gateway errors (including HTTP 502/503/504 and tunnel-provider errors) are translated into readable messages rather than raw HTML or JSON parse errors. A browser cannot always distinguish a closed tunnel from a daemon, DNS or network failure, so these messages describe possible causes rather than claiming certainty.
|
|
@@ -354,7 +372,7 @@ Clone the repository, then:
|
|
|
354
372
|
|
|
355
373
|
`npm run serve` builds the embedded Svelte UI and starts the Rust daemon in the foreground. It prints:
|
|
356
374
|
|
|
357
|
-
Pi Companion v0.3.
|
|
375
|
+
Pi Companion v0.3.2
|
|
358
376
|
|
|
359
377
|
Console http://127.0.0.1:43721
|
|
360
378
|
Paired devices http://127.0.0.1:43722
|
|
@@ -392,6 +410,8 @@ To regenerate the README screenshots from demo sessions (your running daemon is
|
|
|
392
410
|
npm run ui:build && cargo build --release --manifest-path server/Cargo.toml
|
|
393
411
|
node tools/screenshots.mjs
|
|
394
412
|
|
|
413
|
+
The generator also refreshes automation list/detail/history/editor/results, automated session, and [slash-command suggestions](docs/slash-commands.webp) screenshots. Type `/` in a shared session to search its available extension commands, skills, and prompt templates; use ↑/↓ and Tab/Enter to choose, then send when ready. Built-in terminal-only commands and ordinary tool names are not included. Automated answers-only sessions never expose this picker.
|
|
414
|
+
|
|
395
415
|
It needs Playwright and `cwebp`. Set `PLAYWRIGHT=/path/to/node_modules/playwright/index.mjs` if Playwright isn't installed in this repo, and `CHROME=/path/to/chrome` to use an existing browser.
|
|
396
416
|
|
|
397
417
|
To test the same behavior as a published install, stop any development daemon first and install the package normally:
|
package/docs/automations.md
CHANGED
|
@@ -1,7 +1,48 @@
|
|
|
1
|
-
# Automations (0.3.
|
|
1
|
+
# Automations (0.3.2)
|
|
2
2
|
|
|
3
3
|
Automations are persisted, named JSON scripts run by the Companion daemon. Open **Automations** in the navigation to view definitions, start/stop runs, and inspect timestamped run history. Click a run to view its captured output and Pi summary. Desktop console users can create, edit, delete, and enable/disable definitions; mobile and paired-device users can only inspect and start/stop them.
|
|
4
4
|
|
|
5
|
+
## Workspace & editor
|
|
6
|
+
|
|
7
|
+
The claymorphic automation collection uses the same responsive tabular style as overview sessions, with rounded status chips, name search, segmented Enabled/Disabled filters, ten-item pagination, and a scrollable row region. Redundant statistic tiles are removed. On phones, rows stack their labelled fields without horizontal scrolling.
|
|
8
|
+
|
|
9
|
+
The detail workspace is a single-column bento layout with a clay back button and full-width run control below its title. **Summary** and **Run history** tabs fill the container. History is a newest-first responsive table with start time, duration, status filters, and ten-item pagination over each automation’s retained history (latest 30 finished runs by default, configurable 1–1000).
|
|
10
|
+
|
|
11
|
+
Choose **Edit automation** to reveal the editor at the top of Summary, automatically scroll it into view, and focus its name field. This keeps the routine context nearby without navigating to another page. Nothing changes until you save. Desktop console editing remains required.
|
|
12
|
+
|
|
13
|
+
Overview, Sessions, Automations, and Settings support pull-to-refresh from the top of the page and an accessible **Refresh** button. Pull down on non-interactive page content, then release when prompted. Nested scroll regions and form controls keep their own gestures. Settings refresh never discards unsaved edits.
|
|
14
|
+
|
|
15
|
+
The job editor separates name, enablement, UTC cron, optional retry policy, preconditions, actions, and post-actions. Each step is a reorderable JSON DSL object with Command/Pi templates. Saving validates the definition, cron ranges, action types, arguments, absolute directories, timeouts, step count, and retry bounds; the daemon validates again before persistence. Only computer desktop administrators can author definitions.
|
|
16
|
+
|
|
17
|
+
Run results have a formatted and raw view. Formatted results split daemon execution phases, render sanitized Markdown (including tables, links, code, and diagrams), recognise whole/fenced JSON, and render Atlassian Document Format (`type: "doc"`) or `{ "adf": ... }` documents. ADF supports headings, lists, marks, tables, panels, and safe links; unsupported nodes preserve their text, and attachments are placeholders. HTML/scripts and executable link protocols are not trusted. Raw output remains available for inspection and copying.
|
|
18
|
+
|
|
19
|
+
## Screenshots
|
|
20
|
+
|
|
21
|
+
Screenshots use only demo data. The same generator refreshes the existing overview, session, and mobile screenshots too.
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+

|
|
25
|
+

|
|
26
|
+

|
|
27
|
+

|
|
28
|
+

|
|
29
|
+

|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
## History retention
|
|
35
|
+
|
|
36
|
+
`historyLimit` is an optional integer **1–1000**, default **30**. Each automation retains its latest N finished runs, replacing older history as newer runs finish. Lowering the limit prunes older runs immediately on save; active runs are retained until finished. The same policy is applied on daemon startup. Deleted run results cannot be recovered from the dashboard. Run history pages show **10 items** at a time.
|
|
37
|
+
|
|
38
|
+
Automations also appear below running sessions on the overview. Automated sessions are labelled **Automated · answers only** in session cards, tables, and the session workspace. Their browser controls allow question answers but not steering, planning, uploads, or code changes; backend restrictions remain authoritative.
|
|
39
|
+
|
|
40
|
+
## Optional retries
|
|
41
|
+
|
|
42
|
+
`maxRetries` is an optional integer **0–5** (default **0**, no retries). `retryIntervalSeconds` is an optional integer **1–86400** (default **10 seconds**). Only a failed main action is retried; completed steps do not repeat, and precondition skips and finalizers are never retried. Stop cancels an active process or retry delay. Save-time validation requires the total retry-delay budget to be shorter than the shortest daily UTC trigger gap (a conservative bound for schedules limited to selected dates). Before each wait, the daemon also skips a retry that would reach the next scheduled trigger. It cannot guarantee action duration; overlapping runs remain prohibited. Attempt diagnostics are included in run output, and finalizers execute after the final outcome unless stopped.
|
|
43
|
+
|
|
44
|
+
**Retries can duplicate side effects**, including model calls and remote submissions after ambiguous failures. Enable them only for retry-safe work; the PR-review automation should keep retries off. Existing definitions without either field retain their original behavior.
|
|
45
|
+
|
|
5
46
|
## JSON DSL
|
|
6
47
|
|
|
7
48
|
```json
|
|
@@ -9,6 +50,9 @@ Automations are persisted, named JSON scripts run by the Companion daemon. Open
|
|
|
9
50
|
"name": "Daily repository check",
|
|
10
51
|
"enabled": true,
|
|
11
52
|
"schedule": "0 9 * * 1-5",
|
|
53
|
+
"maxRetries": 0,
|
|
54
|
+
"retryIntervalSeconds": 10,
|
|
55
|
+
"historyLimit": 30,
|
|
12
56
|
"preconditions": [
|
|
13
57
|
{ "type": "command", "command": "git", "args": ["rev-parse", "--is-inside-work-tree"], "cwd": "/absolute/project" }
|
|
14
58
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yunazgr/pi-companion",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Local-first remote control for Pi sessions: live activity feed, steering, git diff, file drop and phone pairing from a single Rust daemon.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -32,6 +32,8 @@ Read the current definition with `get` before `update`, `enable`, or `disable`.
|
|
|
32
32
|
- `schedule` is a five-field **UTC** cron expression; `null` means manual only. The daemon must be running for scheduling. There is no catch-up while it is stopped.
|
|
33
33
|
- Commands use executable plus argument array, **no implicit shell**. Shell syntax must never be passed as an executable. Avoid explicit shells unless the user requested and authorized them.
|
|
34
34
|
- Preconditions gate execution. Actions run sequentially. Post-actions are the cleanup/finalization stage; inspect the run result to determine failures.
|
|
35
|
+
- Optional `maxRetries` (integer 0–5, default 0) and `retryIntervalSeconds` (integer 1–86400, default 10) retry only the failed main action, never completed steps, preconditions, or finalizers. Stop cancels a retry delay. Retries may duplicate side effects or model usage: obtain authorization, and keep them off for non-idempotent remote submissions.
|
|
36
|
+
- Optional `historyLimit` (integer 1–1000, default 30) keeps the latest N finished runs per automation. Lowering it immediately removes older history on save. Retry delays must fit before the next scheduled trigger.
|
|
35
37
|
- A Pi action starts a daemon-owned, read-only Companion session: users can answer asks, but cannot steer, prompt, upload, request changes, or edit its plan. The agent itself still has ordinary Pi tool permissions.
|
|
36
38
|
- Pi must be installed on PATH and configured with model credentials. Its final assistant output is saved in run history.
|
|
37
39
|
|
package/src/automation-mcp.ts
CHANGED
|
@@ -31,7 +31,7 @@ async function receive(line: string) {
|
|
|
31
31
|
case "initialize": {
|
|
32
32
|
const versions = ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
33
33
|
const requested = message.params?.protocolVersion;
|
|
34
|
-
reply({ protocolVersion: versions.includes(String(requested)) ? requested : versions[0], capabilities: { tools: {} }, serverInfo: { name: "pi-companion-automations", version: "0.3.
|
|
34
|
+
reply({ protocolVersion: versions.includes(String(requested)) ? requested : versions[0], capabilities: { tools: {} }, serverInfo: { name: "pi-companion-automations", version: "0.3.2" } });
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
case "ping": reply({}); return;
|
package/src/automation.ts
CHANGED
|
@@ -10,6 +10,9 @@ export type AutomationDefinition = {
|
|
|
10
10
|
preconditions: AutomationAction[];
|
|
11
11
|
actions: AutomationAction[];
|
|
12
12
|
postActions: AutomationAction[];
|
|
13
|
+
historyLimit?: number;
|
|
14
|
+
maxRetries?: number;
|
|
15
|
+
retryIntervalSeconds?: number;
|
|
13
16
|
};
|
|
14
17
|
export type AutomationParams = {
|
|
15
18
|
action: "list" | "get" | "create" | "update" | "delete" | "enable" | "disable" | "start" | "stop" | "runs" | "run";
|
|
@@ -48,7 +51,10 @@ export const automationTool = {
|
|
|
48
51
|
schedule: { type: ["string", "null"], description: "Five-field UTC cron; null for manual only." },
|
|
49
52
|
preconditions: { type: "array", items: actionSchema },
|
|
50
53
|
actions: { type: "array", minItems: 1, items: actionSchema },
|
|
51
|
-
postActions: { type: "array", items: actionSchema }
|
|
54
|
+
postActions: { type: "array", items: actionSchema },
|
|
55
|
+
historyLimit: { type: "integer", minimum: 1, maximum: 1000, description: "Retain latest N finished runs; default 30." },
|
|
56
|
+
maxRetries: { type: "integer", minimum: 0, maximum: 5, description: "Optional retries of the failed main action only; default 0. May repeat side effects." },
|
|
57
|
+
retryIntervalSeconds: { type: "integer", minimum: 1, maximum: 86400, description: "Optional delay between retries; default 10 seconds; total retry delay must fit before the next scheduled trigger." }
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
}
|
|
@@ -92,8 +98,8 @@ export async function executeAutomation(params: AutomationParams, signal?: Abort
|
|
|
92
98
|
case "enable": case "disable": {
|
|
93
99
|
const current = await request(path) as AutomationDefinition;
|
|
94
100
|
// PUT only definition fields, never server-generated metadata.
|
|
95
|
-
const { name, schedule, preconditions, actions, postActions } = current;
|
|
96
|
-
return request(path, "PUT", { name, schedule, preconditions, actions, postActions, enabled: params.action === "enable" });
|
|
101
|
+
const { name, schedule, preconditions, actions, postActions, maxRetries, retryIntervalSeconds, historyLimit } = current;
|
|
102
|
+
return request(path, "PUT", { name, schedule, preconditions, actions, postActions, maxRetries, retryIntervalSeconds, historyLimit, enabled: params.action === "enable" });
|
|
97
103
|
}
|
|
98
104
|
case "start": case "stop": return request(path + "/" + params.action, "POST");
|
|
99
105
|
case "runs": return request(path + "/runs");
|
package/src/bridge.ts
CHANGED
|
@@ -70,7 +70,11 @@ export class CompanionBridge implements AskChannel {
|
|
|
70
70
|
shortTitle: name?.trim() || cwd.split(/[\\/]/).filter(Boolean).pop() || "Pi",
|
|
71
71
|
mainModel: ctx.model?.id ?? null,
|
|
72
72
|
effort: ctx.thinkingLevel ?? this.pi.getThinkingLevel?.() ?? null,
|
|
73
|
-
telemetry: this.telemetry.snapshot(ctx)
|
|
73
|
+
telemetry: this.telemetry.snapshot(ctx),
|
|
74
|
+
// Expose only executable slash commands, not local source paths or credentials.
|
|
75
|
+
commands: (this.pi.getCommands?.() ?? []).slice(0, 500).map(command => ({
|
|
76
|
+
name: command.name, description: command.description?.slice(0, 500), source: command.source
|
|
77
|
+
}))
|
|
74
78
|
};
|
|
75
79
|
const patch: Partial<SessionSnapshot> = {};
|
|
76
80
|
for (const key of Object.keys(next) as Array<keyof typeof next>) {
|
package/src/protocol.ts
CHANGED
|
@@ -61,6 +61,7 @@ export type SessionSnapshot = {
|
|
|
61
61
|
effort?: string | null;
|
|
62
62
|
telemetry?: SessionTelemetry;
|
|
63
63
|
remoteEnabled: boolean;
|
|
64
|
+
commands?: { name: string; description?: string; source: 'extension' | 'prompt' | 'skill' }[];
|
|
64
65
|
connectedAt: string;
|
|
65
66
|
/** Questions waiting for an answer. Kept in the snapshot so late-joining browsers see them. */
|
|
66
67
|
asks: AskRequest[];
|