@pstdio/pocketcoder-remote 0.2.2 → 0.3.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
@@ -15,10 +15,10 @@ final-response polling path.
15
15
  ## Install
16
16
 
17
17
  ```sh
18
- npm install -g @pstdio/pocketcoder-remote # or: bun add -g @pstdio/pocketcoder-remote
18
+ bun add -g @pstdio/pocketcoder-remote
19
19
  ```
20
20
 
21
- Or run without installing: `npx @pstdio/pocketcoder-remote`.
21
+ Or run without installing: `bunx @pstdio/pocketcoder-remote`.
22
22
 
23
23
  ## Usage
24
24
 
@@ -55,7 +55,7 @@ you already use Pi, you can load the extension directly instead:
55
55
 
56
56
  ```sh
57
57
  POCKETCODER_URL=... POCKETCODER_KEY=... POCKETCODER_WORKSPACE_ID=... \
58
- pi --extension node_modules/@pstdio/pocketcoder-remote/src/extension.ts \
58
+ pi --extension node_modules/@pstdio/pocketcoder-remote/dist/extension.js \
59
59
  --provider pocketcoder-agentapi --model remote-agent --api-key local-ui \
60
60
  --no-tools --no-extensions --no-skills --no-context-files \
61
61
  --no-prompt-templates --no-session --offline
@@ -81,6 +81,60 @@ a normal local coding session is not supported. This package pins
81
81
  `@earendil-works/pi-coding-agent` to an exact version; running the extension
82
82
  under a different Pi version is untested.
83
83
 
84
+ ## Embed the Pi adapter with workspace resume
85
+
86
+ Products that can issue fresh workspace bootstrap input may pass the public SDK
87
+ resolver into the typed extension factory:
88
+
89
+ ```ts
90
+ import { createRemoteExtension } from "@pstdio/pocketcoder-remote/extension";
91
+ import {
92
+ PocketCoderClient,
93
+ WorkspaceTurnResolver,
94
+ } from "@pstdio/pocketcoder-sdk";
95
+
96
+ const client = new PocketCoderClient({
97
+ baseUrl: process.env.POCKETCODER_URL!,
98
+ apiKey: process.env.POCKETCODER_KEY!,
99
+ });
100
+
101
+ const resolver = new WorkspaceTurnResolver({
102
+ client,
103
+ resumeWorkspace: async ({ source, attemptId, signal }) => {
104
+ const launchInput = await issueWorkspaceBootstrap({ signal });
105
+ const result = await client.workspaces.resume(
106
+ source.id,
107
+ { external_id: `pi-${attemptId}`, launch_input: launchInput },
108
+ attemptId,
109
+ { signal },
110
+ );
111
+ return result.workspace;
112
+ },
113
+ });
114
+
115
+ export default createRemoteExtension({ resolver });
116
+ ```
117
+
118
+ The bootstrap input must be new, workspace-scoped, and expire with the resumed
119
+ workspace. The key needs `workspaces:read`, `workspaces:restore`, and
120
+ `services:relay`; attachments also need `attachments:write`.
121
+
122
+ Resume runs only for a user turn. The adapter uploads one captured attachment
123
+ batch to the resolved workspace and retries only a verified terminal failure
124
+ before the prompt is accepted. `/attach` entries remain queued until that
125
+ acceptance point. After acceptance, relay or history failures never replay the
126
+ prompt.
127
+
128
+ If the same Pi UI is still open, it changes its local target to the resumed
129
+ workspace for later turns, status, attachments, and `/workspace-cancel`. A user
130
+ workspace switch or session shutdown wins over a late result. No full history
131
+ is replayed into that live UI, and no A-to-B mapping is stored. Canceling the
132
+ caller only stops its wait; an accepted server resume may continue.
133
+
134
+ The package launcher uses the default extension without a resolver. It does not
135
+ resume automatically. A preserved workspace therefore returns the fixed
136
+ no-handler error until an embedded product supplies the callback.
137
+
84
138
  ## In-UI commands
85
139
 
86
140
  - `/workspace` — pick and switch to another ready workspace (replays its history)
@@ -98,7 +152,8 @@ the PocketCoder attachment API when the turn is sent:
98
152
  - queue one explicitly with `/attach <path>`.
99
153
 
100
154
  The agent receives each file's workspace path under `$HOME/.pcd/attachments`.
101
- If an upload fails the message is not sent. With a direct AgentAPI URL
155
+ If an upload fails the message is not sent, and explicit queued files remain
156
+ for the next safe attempt. With a direct AgentAPI URL
102
157
  (`POCKETCODER_AGENTAPI_URL`) managed uploads are unavailable — attachment
103
158
  gestures fail with an explanation while plain text keeps working.
104
159
 
package/dist/bin.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/bin.js CHANGED
@@ -1,98 +1,77 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __returnValue = (v) => v;
4
- function __exportSetter(name, newValue) {
5
- this[name] = __returnValue.bind(null, newValue);
6
- }
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, {
10
- get: all[name],
11
- enumerable: true,
12
- configurable: true,
13
- set: __exportSetter.bind(all, name)
14
- });
15
- };
16
-
17
- // src/bin.ts
18
2
  import { spawn } from "node:child_process";
19
-
20
- // src/launch.ts
21
3
  import { readFileSync } from "node:fs";
22
4
  import { dirname, resolve, sep } from "node:path";
23
5
  import { fileURLToPath } from "node:url";
24
- var PI_FLAGS = [
25
- "--provider",
26
- "pocketcoder-agentapi",
27
- "--model",
28
- "remote-agent",
29
- "--api-key",
30
- "local-ui",
31
- "--no-tools",
32
- "--no-extensions",
33
- "--no-skills",
34
- "--no-context-files",
35
- "--no-prompt-templates",
36
- "--no-session",
37
- "--offline"
6
+ //#region src/launch.ts
7
+ const PI_FLAGS = [
8
+ "--provider",
9
+ "pocketcoder-agentapi",
10
+ "--model",
11
+ "remote-agent",
12
+ "--api-key",
13
+ "local-ui",
14
+ "--no-tools",
15
+ "--no-extensions",
16
+ "--no-skills",
17
+ "--no-context-files",
18
+ "--no-prompt-templates",
19
+ "--no-session",
20
+ "--offline"
38
21
  ];
39
22
  function piBinPath(resolvePath) {
40
- const entry = resolvePath("@earendil-works/pi-coding-agent");
41
- const marker = `${sep}pi-coding-agent${sep}`;
42
- const index = entry.lastIndexOf(marker);
43
- if (index === -1) {
44
- throw new Error(`could not locate @earendil-works/pi-coding-agent from ${entry}`);
45
- }
46
- const packageRoot = entry.slice(0, index + marker.length - 1);
47
- const manifest = JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf8"));
48
- const bin = manifest.bin?.pi;
49
- if (!bin)
50
- throw new Error("@earendil-works/pi-coding-agent does not declare a pi bin");
51
- return resolve(packageRoot, bin);
23
+ const entry = resolvePath("@earendil-works/pi-coding-agent");
24
+ const marker = `${sep}pi-coding-agent${sep}`;
25
+ const index = entry.lastIndexOf(marker);
26
+ if (index === -1) throw new Error(`could not locate @earendil-works/pi-coding-agent from ${entry}`);
27
+ const packageRoot = entry.slice(0, index + marker.length - 1);
28
+ const bin = JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf8")).bin?.pi;
29
+ if (!bin) throw new Error("@earendil-works/pi-coding-agent does not declare a pi bin");
30
+ return resolve(packageRoot, bin);
52
31
  }
53
32
  function extensionPath(moduleDir) {
54
- return resolve(moduleDir, moduleDir.endsWith(`${sep}src`) ? "extension.ts" : "extension.js");
33
+ return resolve(moduleDir, moduleDir.endsWith(`${sep}src`) ? "extension.ts" : "extension.js");
55
34
  }
56
35
  function resolvePiInvocation(options = {}) {
57
- const env = options.env ?? process.env;
58
- if (!env.POCKETCODER_KEY) {
59
- throw new Error("POCKETCODER_KEY is required. Issue a machine key with `pcd keys issue` and export it, along with POCKETCODER_URL for your PocketCoder server.");
60
- }
61
- const resolvePath = options.resolvePath ?? ((specifier) => fileURLToPath(import.meta.resolve(specifier)));
62
- const childEnv = { ...env };
63
- delete childEnv.OPENAI_API_KEY;
64
- return {
65
- command: options.execPath ?? process.execPath,
66
- args: [
67
- piBinPath(resolvePath),
68
- ...PI_FLAGS,
69
- "--extension",
70
- extensionPath(dirname(fileURLToPath(import.meta.url))),
71
- ...options.argv ?? []
72
- ],
73
- env: childEnv
74
- };
36
+ const env = options.env ?? process.env;
37
+ if (!env.POCKETCODER_KEY) throw new Error("POCKETCODER_KEY is required. Issue a machine key with `pcd keys issue` and export it, along with POCKETCODER_URL for your PocketCoder server.");
38
+ const resolvePath = options.resolvePath ?? ((specifier) => fileURLToPath(import.meta.resolve(specifier)));
39
+ const childEnv = { ...env };
40
+ delete childEnv.OPENAI_API_KEY;
41
+ return {
42
+ command: options.execPath ?? process.execPath,
43
+ args: [
44
+ piBinPath(resolvePath),
45
+ ...PI_FLAGS,
46
+ "--extension",
47
+ extensionPath(dirname(fileURLToPath(import.meta.url))),
48
+ ...options.argv ?? []
49
+ ],
50
+ env: childEnv
51
+ };
75
52
  }
76
-
77
- // src/bin.ts
53
+ //#endregion
54
+ //#region src/bin.ts
78
55
  function main() {
79
- let invocation;
80
- try {
81
- invocation = resolvePiInvocation({ argv: process.argv.slice(2) });
82
- } catch (error) {
83
- console.error(`pocketcoder-remote: ${error instanceof Error ? error.message : String(error)}`);
84
- process.exit(1);
85
- }
86
- const child = spawn(invocation.command, invocation.args, {
87
- stdio: "inherit",
88
- env: invocation.env
89
- });
90
- child.on("error", (error) => {
91
- console.error(`pocketcoder-remote: failed to launch pi: ${error.message}`);
92
- process.exit(1);
93
- });
94
- child.on("exit", (code, signal) => {
95
- process.exit(code ?? (signal ? 1 : 0));
96
- });
56
+ let invocation;
57
+ try {
58
+ invocation = resolvePiInvocation({ argv: process.argv.slice(2) });
59
+ } catch (error) {
60
+ console.error(`pocketcoder-remote: ${error instanceof Error ? error.message : String(error)}`);
61
+ process.exit(1);
62
+ }
63
+ const child = spawn(invocation.command, invocation.args, {
64
+ stdio: "inherit",
65
+ env: invocation.env
66
+ });
67
+ child.on("error", (error) => {
68
+ console.error(`pocketcoder-remote: failed to launch pi: ${error.message}`);
69
+ process.exit(1);
70
+ });
71
+ child.on("exit", (code, signal) => {
72
+ process.exit(code ?? (signal ? 1 : 0));
73
+ });
97
74
  }
98
75
  main();
76
+ //#endregion
77
+ export {};
@@ -0,0 +1,10 @@
1
+ import { WorkspaceTurnResolver } from "@pstdio/pocketcoder-sdk";
2
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+ //#region src/extension.d.ts
4
+ interface RemoteExtensionOptions {
5
+ resolver?: WorkspaceTurnResolver;
6
+ }
7
+ declare function createRemoteExtension(options?: RemoteExtensionOptions): (pi: ExtensionAPI) => void;
8
+ declare const _default: (pi: ExtensionAPI) => void;
9
+ //#endregion
10
+ export { RemoteExtensionOptions, createRemoteExtension, _default as default };