@zetaloop/chappie 0.5.0 → 1.1.0

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.
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "chappie",
3
+ "description": "Connect ChatGPT and local agents across devices",
4
+ "mcpServers": {
5
+ "chappie": {
6
+ "command": "node",
7
+ "args": ["dist/cli.js", "codex"],
8
+ "env_vars": ["CODEX_HOME"],
9
+ "cwd": "."
10
+ }
11
+ },
12
+ "version": "1.1.0"
13
+ }
package/README.md CHANGED
@@ -1,56 +1,53 @@
1
1
  # Chappie
2
2
 
3
- Use ChatGPT to work through [Pi](https://github.com/earendil-works/pi): edit local files, run commands, call Pi extensions, exchange files and images, and move between sessions on one or more devices.
3
+ Use ChatGPT to work through Pi, Oh My Pi, OpenCode, or Codex: edit projects, run commands, and exchange files and images. Agent sessions can also work with each other, on the same computer or across devices.
4
4
 
5
5
  ## Setup
6
6
 
7
- Install the Pi package:
7
+ Install Chappie in each participating agent:
8
+
9
+ | Agent | Install |
10
+ | ------------------------------------------------- | ------------------------------------------------ |
11
+ | [Pi](https://github.com/earendil-works/pi) | `pi install npm:@zetaloop/chappie` |
12
+ | [Oh My Pi](https://github.com/can1357/oh-my-pi) | `omp plugin install @zetaloop/chappie` |
13
+ | [OpenCode](https://github.com/anomalyco/opencode) | `opencode plugin add @zetaloop/chappie` |
14
+ | [Codex](https://github.com/openai/codex) | [Plugin and provider setup](docs/setup.md#codex) |
15
+
16
+ The broker connects these sessions to ChatGPT and to each other. Install it on one device:
8
17
 
9
18
  ```sh
10
- pi install npm:@zetaloop/chappie
19
+ pnpm add -g @zetaloop/chappie@1
11
20
  ```
12
21
 
13
- Run Chappie as the MCP server managed by [otunnel](https://github.com/zetaloop/otunnel):
22
+ ## With ChatGPT
23
+
24
+ Run the broker through [otunnel](https://github.com/zetaloop/otunnel) with this MCP configuration:
14
25
 
15
26
  ```yaml
16
27
  mcp:
17
28
  commands:
18
29
  - channel: main
19
- command: pi --chappie
20
- ```
21
-
22
- Add the tunnel as a developer-mode app in ChatGPT, then start Pi in a project:
23
-
24
- ```sh
25
- pi --provider chappie --model chatgpt
30
+ command: chappie
26
31
  ```
27
32
 
28
- Call `init` from ChatGPT to connect the conversation to Pi. A conversation can resume an existing task with its Pi session ID, while `sessions` can find connected sessions by device, directory, or name.
29
-
30
- ## Usage
31
-
32
- Chappie exposes common coding tools directly and every active Pi tool through `tools` and `call`. `chat` sends an assistant message to Pi, Pi input accompanies later tool results, and `transfer` moves files between ChatGPT and Pi or between connected devices. `history` reads recent Pi messages and activity with timestamps. `ask` can present a persistent question in ChatGPT when webpage questions are enabled.
33
+ Add the tunnel as a developer-mode app in ChatGPT. Open a project in the agent with its Chappie model selected, then tell ChatGPT which project or session to use and what to do:
33
34
 
34
- See the [tool guide](docs/tools.md) for session selection, history, Pi tools, webpage questions, and file transfer.
35
+ > Use \@Chappie to review the recent changes in the website project.
35
36
 
36
- ## Configuration
37
+ Tool activity and assistant messages appear in the agent's interface. Messages entered there reach ChatGPT with later tool results. Attach files in ChatGPT for the agent to use, or ask for generated files to be sent back.
37
38
 
38
- `chappie.json` in Pi's agent directory configures Chappie.
39
+ To continue from another chat or branch, ask ChatGPT to reconnect to the same agent session.
39
40
 
40
- A broker can accept Pi sessions from other devices on the local network:
41
+ ## Between agents
41
42
 
42
- ```json
43
- { "listen": true }
44
- ```
43
+ An agent using its usual model can work through another agent session. For example, an assistant on macOS can run a build in a Windows session and retrieve the output files.
45
44
 
46
- Remote Pi sessions connect through the broker device's mDNS name:
45
+ Start `chappie` in a terminal, or use the broker already running through otunnel. On the controlling agent's device, enable local tools in `~/.chappie/config.json`:
47
46
 
48
47
  ```json
49
- { "connect": "<broker>.local" }
48
+ { "localTools": true }
50
49
  ```
51
50
 
52
- The default port is `24274`. Set `listen` to a port number or append `:port` to `connect` to use another one. Only the broker device runs otunnel; local and remote sessions appear in the same session list.
53
-
54
- Set `ask` to `false` to disable webpage questions.
51
+ Use the usual model in the controlling session and the Chappie model in the target session. Ask the controlling assistant to find that session and work on its project.
55
52
 
56
- Closely spaced initializations from the same ChatGPT conversation receive guidance to observe the ongoing work through `history` and explain its results. See [participation](docs/tools.md#participation).
53
+ See [setup and configuration](docs/setup.md) for device connections and agent-specific settings, and the [tool guide](docs/tools.md) for the available operations and their parameters.
@@ -0,0 +1,148 @@
1
+ import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
2
+ import {
3
+ array,
4
+ callsInput,
5
+ chatInput,
6
+ deliveryContent,
7
+ historyInput,
8
+ inputContent,
9
+ object,
10
+ resourceDescriptors,
11
+ string,
12
+ toolResult,
13
+ transferDescription,
14
+ transferInput
15
+ } from "./chunk-ASDHNBR2.js";
16
+
17
+ // src/local.ts
18
+ var definitions = [
19
+ tool(
20
+ "sessions",
21
+ "List connected Chappie sessions and identify this local session.",
22
+ object({
23
+ sessionId: string().optional()
24
+ }),
25
+ async (session, { sessionId }, signal) => textResult(await session.sessions(sessionId, signal))
26
+ ),
27
+ tool(
28
+ "remote_tools",
29
+ "Read native tool definitions from a Chappie session. Follow the instructions at globalAgents.path when provided.",
30
+ object({
31
+ sessionId: string().min(1),
32
+ names: array(string()).min(1).optional()
33
+ }),
34
+ async (session, { sessionId, names }, signal) => {
35
+ const { inputs, ...result } = await session.tools(
36
+ sessionId,
37
+ names,
38
+ signal
39
+ );
40
+ return textResult(result, inputs);
41
+ }
42
+ ),
43
+ tool(
44
+ "remote_call",
45
+ "Execute one native tool batch in a Chappie session using definitions from remote_tools.",
46
+ object({
47
+ sessionId: string().min(1),
48
+ calls: callsInput
49
+ }),
50
+ async (session, { sessionId, calls }, signal) => remoteResult(session, await session.call(sessionId, calls, signal))
51
+ ),
52
+ tool(
53
+ "remote_chat",
54
+ "Send an assistant message to a Chappie session, or reply to its model request using replyTo.",
55
+ chatInput.extend({ sessionId: string().min(1) }),
56
+ async (session, { sessionId, text, replyTo }, signal) => remoteResult(
57
+ session,
58
+ await session.chat(sessionId, text, replyTo, signal)
59
+ )
60
+ ),
61
+ tool(
62
+ "history",
63
+ "Read this session's history, or a Chappie session identified by sessionId. Use before/after to page and wait to follow remote progress.",
64
+ historyInput.extend({
65
+ sessionId: string().optional()
66
+ }),
67
+ async (session, { sessionId, ...range }, signal) => {
68
+ const { content, ...page } = await session.history(
69
+ range,
70
+ sessionId,
71
+ signal
72
+ );
73
+ return {
74
+ content: [...textResult(page).content, ...nativeContent(content)]
75
+ };
76
+ }
77
+ ),
78
+ tool(
79
+ "transfer",
80
+ transferDescription,
81
+ transferInput,
82
+ (session, args, signal, update) => session.transfer(
83
+ args,
84
+ signal,
85
+ (details) => update?.({ content: [], details })
86
+ )
87
+ )
88
+ ];
89
+ function localTools(session) {
90
+ return definitions.filter((tool2) => session.localTools || tool2.name === "transfer").map((tool2) => ({
91
+ ...tool2,
92
+ async execute(args, signal, update) {
93
+ const result = await tool2.execute(session, args, signal, update);
94
+ return {
95
+ ...result,
96
+ content: [...result.content, ...localDeliveries(session)]
97
+ };
98
+ }
99
+ }));
100
+ }
101
+ function localDeliveries(session) {
102
+ return nativeContent(deliveryContent(session.deliveries()));
103
+ }
104
+ function tool(name, description, parameters, execute) {
105
+ return {
106
+ name,
107
+ description,
108
+ parameters,
109
+ execute: (session, args, signal, update) => execute(session, parameters.parse(args), signal, update)
110
+ };
111
+ }
112
+ function textResult(value, inputs = []) {
113
+ return {
114
+ content: [
115
+ { type: "text", text: JSON.stringify(value) },
116
+ ...inputContent(inputs, "remote_chat")
117
+ ]
118
+ };
119
+ }
120
+ function remoteResult(session, result) {
121
+ const output = toolResult(result, "remote_chat");
122
+ const content = nativeContent(output.content);
123
+ if (output.isError)
124
+ throw new Error(
125
+ [...content, ...localDeliveries(session)].flatMap((block) => block.type === "text" ? [block.text] : []).join("\n")
126
+ );
127
+ return {
128
+ content,
129
+ ..."toolResults" in result ? {
130
+ details: {
131
+ resources: result.toolResults.flatMap(
132
+ (result2) => resourceDescriptors(result2.details)
133
+ )
134
+ }
135
+ } : {}
136
+ };
137
+ }
138
+ function nativeContent(content) {
139
+ return content.map(
140
+ (block) => block.type === "resource_link" ? { type: "text", text: JSON.stringify({ resource: block }) } : block
141
+ );
142
+ }
143
+
144
+ export {
145
+ definitions,
146
+ localTools,
147
+ localDeliveries
148
+ };