@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 +59 -4
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +62 -83
- package/dist/extension.d.ts +10 -0
- package/dist/extension.js +1128 -17331
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -15,10 +15,10 @@ final-response polling path.
|
|
|
15
15
|
## Install
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
|
|
18
|
+
bun add -g @pstdio/pocketcoder-remote
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Or run without installing: `
|
|
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/
|
|
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
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
33
|
+
return resolve(moduleDir, moduleDir.endsWith(`${sep}src`) ? "extension.ts" : "extension.js");
|
|
55
34
|
}
|
|
56
35
|
function resolvePiInvocation(options = {}) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/bin.ts
|
|
78
55
|
function main() {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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 };
|