@pontia/pi-client-plugin 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,49 +1,41 @@
1
1
  # @pontia/pi-client-plugin
2
2
 
3
- Connect Pi coding-agent sessions to Pontia.
3
+ Connect your pi coding-agent sessions to Pontia so you can follow and continue your work from the web dashboard.
4
4
 
5
5
  ## Requirements
6
6
 
7
7
  - [Pontia](https://pontia.dev/) running locally
8
- - Pi CLI
8
+ - pi CLI
9
9
  - tmux
10
10
 
11
11
  ## Install
12
12
 
13
+ If you selected the pi integration during `pontia init`, the plugin is already installed.
14
+
15
+ To install it separately:
16
+
13
17
  ```bash
14
18
  pi install npm:@pontia/pi-client-plugin
15
19
  ```
16
20
 
17
- ## Configure
18
-
19
- Set the Pi command in `$PONTIA_HOME/config.toml`:
21
+ The plugin loads automatically when pi starts.
20
22
 
21
- ```toml
22
- [runtime.pi]
23
- tui_command = "pi"
24
- ```
25
-
26
- Then launch Pi from Pontia, or run it in a tmux pane inside a workspace registered with Pontia. The extension loads automatically.
23
+ ## Get started
27
24
 
28
- ## Troubleshooting
25
+ Open the Pontia dashboard and create a pi session. You can then view the session and continue the conversation from the dashboard.
29
26
 
30
- If a session does not appear in Pontia, verify that:
27
+ You can also start pi in a tmux pane inside a workspace registered with Pontia.
31
28
 
32
- - Pontia is running
33
- - the current workspace is registered with Pontia
34
- - Pi is running inside tmux
29
+ For Pontia installation and setup, see the [getting started guide](../../README.md#get-started).
35
30
 
36
- Extension logs are available at:
37
-
38
- ```text
39
- ${PONTIA_HOME:-$HOME/.pontia}/state/pi-hook.log
40
- ```
31
+ ## Troubleshooting
41
32
 
42
- Follow the log while reproducing an issue:
33
+ If a session does not appear in the dashboard, check that:
43
34
 
44
- ```bash
45
- tail -f "${PONTIA_HOME:-$HOME/.pontia}/state/pi-hook.log"
46
- ```
35
+ - Pontia is running (`pontia status`).
36
+ - The workspace is registered with Pontia.
37
+ - pi is running inside tmux.
38
+ - If you just installed the plugin, you have restarted pi.
47
39
 
48
40
  ## License
49
41
 
package/package.json CHANGED
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "@pontia/pi-client-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Pi extension that connects coding-agent sessions to Pontia.",
5
+ "keywords": [
6
+ "pi-package",
7
+ "pontia-client-plugin"
8
+ ],
9
+ "homepage": "https://pontia.dev/",
10
+ "bugs": {
11
+ "url": "https://github.com/anthod0/pontia/issues"
12
+ },
5
13
  "license": "Apache-2.0",
6
14
  "author": {
7
15
  "name": "anthod0",
@@ -12,39 +20,38 @@
12
20
  "url": "git+https://github.com/anthod0/pontia.git",
13
21
  "directory": "clients/pi"
14
22
  },
15
- "homepage": "https://pontia.dev/",
16
- "bugs": {
17
- "url": "https://github.com/anthod0/pontia/issues"
18
- },
19
- "type": "module",
20
23
  "files": [
21
24
  "src",
22
25
  "README.md",
23
26
  "LICENSE"
24
27
  ],
28
+ "type": "module",
25
29
  "publishConfig": {
26
30
  "access": "public"
27
31
  },
28
- "keywords": [
29
- "pi-package",
30
- "pontia-client-plugin"
31
- ],
32
- "pi": {
33
- "extensions": [
34
- "./src/index.ts"
35
- ]
36
- },
37
- "peerDependencies": {
38
- "@mariozechner/pi-coding-agent": "*"
32
+ "scripts": {
33
+ "fmt": "oxfmt",
34
+ "fmt:check": "oxfmt --check",
35
+ "test": "vitest run",
36
+ "typecheck": "tsc --noEmit"
39
37
  },
40
38
  "devDependencies": {
41
- "@mariozechner/pi-coding-agent": "*",
39
+ "@earendil-works/pi-coding-agent": ">=0.86.1 <1",
42
40
  "@types/node": "^22.15.3",
41
+ "oxfmt": "^0.70.0",
43
42
  "typescript": "^5.8.3",
44
43
  "vitest": "^3.1.2"
45
44
  },
46
- "scripts": {
47
- "test": "vitest run",
48
- "typecheck": "tsc --noEmit"
49
- }
50
- }
45
+ "peerDependencies": {
46
+ "@earendil-works/pi-coding-agent": ">=0.86.1 <1"
47
+ },
48
+ "pi": {
49
+ "extensions": [
50
+ "./src/index.ts"
51
+ ]
52
+ },
53
+ "trustedDependencies": [
54
+ "esbuild",
55
+ "koffi"
56
+ ]
57
+ }
package/src/context.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import { join } from "node:path";
2
- import { pontiaHomeFromEnv, resolvePontiaConnection } from "./discovery.js";
3
- import type { SessionContext } from "./session.js";
4
2
 
5
3
  export interface TurnContext {
6
4
  sessionId: string;
@@ -9,112 +7,10 @@ export interface TurnContext {
9
7
  input?: string;
10
8
  inboxMessageId?: string;
11
9
  clientType: "pi";
12
- internalEventUrl: string;
13
10
  }
14
11
 
15
- export type LoadTurnContextResult =
16
- | { ok: true; context: TurnContext; logFile: string }
17
- | { ok: false; reason: string; logFile?: string; silent?: boolean };
18
-
19
12
  export type EnvLike = Record<string, string | undefined>;
20
13
 
21
- export interface LoadTurnContextOptions {
22
- fetch?: typeof fetch;
23
- sessionContext?: SessionContext;
24
- }
25
-
26
14
  export function defaultHookLogFile(pontiaHome: string): string {
27
15
  return join(pontiaHome, "state", "pi-hook.log");
28
16
  }
29
-
30
- function asRecord(value: unknown): Record<string, unknown> | undefined {
31
- return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
32
- }
33
-
34
- function optionalString(value: unknown): string | undefined {
35
- return typeof value === "string" && value.length > 0 ? value : undefined;
36
- }
37
-
38
- function claimUrl(internalEventUrl: string, sessionId: string): string | undefined {
39
- try {
40
- const url = new URL(internalEventUrl);
41
- url.pathname = url.pathname.replace(/\/events\/?$/, `/sessions/${encodeURIComponent(sessionId)}/current-turn/claim`);
42
- return url.toString();
43
- } catch {
44
- return undefined;
45
- }
46
- }
47
-
48
- function contextFromRecord(record: Record<string, unknown>, logFile: string, discoveredInternalEventUrl?: string): LoadTurnContextResult {
49
- const errors: string[] = [];
50
- const sessionId = optionalString(record.session_id);
51
- const turnId = optionalString(record.turn_id);
52
- const clientType = optionalString(record.client_type);
53
- const internalEventUrl = optionalString(record.internal_event_url) ?? discoveredInternalEventUrl;
54
- const runtimeInstanceId = optionalString(record.runtime_instance_id);
55
- const input = optionalString(record.input);
56
- const inboxMessageId = optionalString(record.inbox_message_id);
57
-
58
- if (!sessionId) errors.push("session_id is required");
59
- if (clientType !== "pi") errors.push("client_type must be pi");
60
- if (!internalEventUrl) errors.push("internal_event_url or pontia connection from PONTIA_HOME/config.toml is required");
61
- if (!runtimeInstanceId) errors.push("runtime_instance_id is required");
62
-
63
- if (errors.length > 0) {
64
- return { ok: false, reason: errors.join("; "), logFile };
65
- }
66
-
67
- return {
68
- ok: true,
69
- logFile,
70
- context: {
71
- sessionId: sessionId!,
72
- turnId,
73
- runtimeInstanceId: runtimeInstanceId!,
74
- input,
75
- inboxMessageId,
76
- clientType: "pi",
77
- internalEventUrl: internalEventUrl!,
78
- },
79
- };
80
- }
81
-
82
- async function claimTurnContext(
83
- pontiaHome: string,
84
- logFile: string,
85
- fetchImpl: typeof fetch,
86
- sessionContext?: SessionContext,
87
- ): Promise<LoadTurnContextResult | undefined> {
88
- if (!sessionContext) return undefined;
89
- const connection = await resolvePontiaConnection({ pontiaHome, fetch: fetchImpl });
90
- const internalEventUrl = sessionContext.internalEventUrl ?? connection?.internalEventUrl;
91
- const url = claimUrl(internalEventUrl, sessionContext.sessionId);
92
- if (!url) return undefined;
93
-
94
- try {
95
- const response = await fetchImpl(url, {
96
- method: "POST",
97
- headers: { "Content-Type": "application/json" },
98
- body: JSON.stringify({ runtime_instance_id: sessionContext.runtimeInstanceId, client_type: "pi" }),
99
- });
100
- if (!response.ok) return undefined;
101
- const body = (await response.json()) as unknown;
102
- const data = asRecord(body)?.data;
103
- const currentTurn = asRecord(asRecord(data)?.current_turn);
104
- if (!currentTurn) return { ok: false, reason: "no pending current turn", logFile, silent: true };
105
- return contextFromRecord(currentTurn, logFile, internalEventUrl);
106
- } catch {
107
- return undefined;
108
- }
109
- }
110
-
111
- export async function loadTurnContext(env: EnvLike = process.env, options: LoadTurnContextOptions = {}): Promise<LoadTurnContextResult> {
112
- const pontiaHome = pontiaHomeFromEnv(env);
113
- if (!pontiaHome) {
114
- return { ok: false, reason: "Pontia home must resolve to a non-root absolute path", silent: true };
115
- }
116
- const logFile = defaultHookLogFile(pontiaHome);
117
- const claimed = await claimTurnContext(pontiaHome, logFile, options.fetch ?? fetch, options.sessionContext);
118
- if (claimed) return claimed;
119
- return { ok: false, reason: "current turn claim unavailable", logFile, silent: true };
120
- }