@chipschat/opencode 1.0.13 → 1.0.15
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 +13 -4
- package/dist/THIRD-PARTY-NOTICES.md +3 -3
- package/dist/main.js +337 -77
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ each model call and tells the agent what changed, so an edit applies from its ne
|
|
|
92
92
|
|
|
93
93
|
- The agent's own, which a person may change from its page: its folder's `AGENTS.md`, shown empty
|
|
94
94
|
until there is one, and any file `instructions` finds in its folder.
|
|
95
|
-
- The harness's own set, on its page: `~/
|
|
95
|
+
- The harness's own set, on its page: `~/Chips/Agents/opencode/AGENTS.md`, which every agent
|
|
96
96
|
reads and a person may change; any `AGENTS.md` above it; and your own, with the files
|
|
97
97
|
`instructions` names elsewhere, which your own OpenCode reads too, so they are shown and not
|
|
98
98
|
changed.
|
|
@@ -163,7 +163,7 @@ first. The steps below run the bridge from this checkout instead, to work on it.
|
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
The bridge runs OpenCode's server itself (`opencode serve` on `127.0.0.1`, 4096 when it is
|
|
166
|
-
free), with a password it makes and keeps in
|
|
166
|
+
free), with a password it makes and keeps in `~/Chips/.bridges/opencode/server.json`, readable by you
|
|
167
167
|
only: nobody starts a server or makes up a password. While the bridge runs, a server that stops
|
|
168
168
|
is started again; when the bridge stops, so does its server.
|
|
169
169
|
|
|
@@ -177,13 +177,22 @@ first. The steps below run the bridge from this checkout instead, to work on it.
|
|
|
177
177
|
two answer twice. The key Chips gives it is kept; a new link from Chips replaces it. Or join with
|
|
178
178
|
`CHIPS_URL` and a code a person allows.
|
|
179
179
|
|
|
180
|
-
3. Optionally, name the agents in
|
|
180
|
+
3. Optionally, name the agents in `~/Chips/.bridges/opencode/config.json` (see `config.example.json`).
|
|
181
181
|
Without a config it is OpenCode's `build` agent, named OpenCode.
|
|
182
182
|
|
|
183
|
-
Each agent's sessions work in a folder of its own, `~/
|
|
183
|
+
Each agent's sessions work in a folder of its own, `~/Chips/Agents/opencode/<its name>`, made
|
|
184
184
|
the first time it is spoken to and kept after a rename; `folders` moves them. A `directory`
|
|
185
185
|
puts every session in that one project instead, a repository the agents share.
|
|
186
186
|
|
|
187
|
+
Everything the bridge keeps is under `~/Chips` (Chips ADR 0283): its config, sessions, server
|
|
188
|
+
password and state in `~/Chips/.bridges/opencode`, the agents' folders in
|
|
189
|
+
`~/Chips/Agents/opencode`, where its own server runs from. Until 1.0.15 they were
|
|
190
|
+
`~/.chips-opencode` and `~/chips-opencode/agents`. The first start of 1.0.15 moves them and the
|
|
191
|
+
folder paths it saved, and once OpenCode answers, asks it to move each session of a folder that
|
|
192
|
+
moved (`POST /api/session/:id/move`), with everything said in it: OpenCode won't use a session
|
|
193
|
+
whose folder is gone. It renames and never copies; where something is at the new place already it
|
|
194
|
+
moves in what isn't there yet and says in its log what it left.
|
|
195
|
+
|
|
187
196
|
**A server of your own.** To use an `opencode serve` you run yourself, give its password in the
|
|
188
197
|
environment variable `passwordEnv` names (`OPENCODE_PASSWORD` by default, never in the file),
|
|
189
198
|
and its `url` in the config when it isn't `127.0.0.1:4096`. `npm run keep` takes the password
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Third-party notices
|
|
2
2
|
|
|
3
|
-
`dist/main.js` in @chipschat/opencode 1.0.
|
|
3
|
+
`dist/main.js` in @chipschat/opencode 1.0.15 carries these packages, bundled in. Each one's
|
|
4
4
|
licence follows.
|
|
5
5
|
|
|
6
|
-
## @chipschat/harness 1.0.
|
|
6
|
+
## @chipschat/harness 1.0.2 (Apache-2.0)
|
|
7
7
|
|
|
8
8
|
Apache License
|
|
9
9
|
Version 2.0, January 2004
|
|
@@ -207,7 +207,7 @@ Apache License
|
|
|
207
207
|
See the License for the specific language governing permissions and
|
|
208
208
|
limitations under the License.
|
|
209
209
|
|
|
210
|
-
## @chipschat/protocol 1.0.
|
|
210
|
+
## @chipschat/protocol 1.0.2 (Apache-2.0)
|
|
211
211
|
|
|
212
212
|
Apache License
|
|
213
213
|
Version 2.0, January 2004
|
package/dist/main.js
CHANGED
|
@@ -8,9 +8,8 @@ var __export = (target, all) => {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
// src/main.ts
|
|
11
|
-
import { existsSync as
|
|
12
|
-
import {
|
|
13
|
-
import { dirname as dirname5, join as join10, resolve as resolve4 } from "node:path";
|
|
11
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
|
|
12
|
+
import { dirname as dirname6, join as join10 } from "node:path";
|
|
14
13
|
|
|
15
14
|
// node_modules/zod/v4/classic/external.js
|
|
16
15
|
var external_exports = {};
|
|
@@ -5106,7 +5105,7 @@ function isRecursive(inst, stack, resolve5) {
|
|
|
5106
5105
|
}
|
|
5107
5106
|
return answer;
|
|
5108
5107
|
};
|
|
5109
|
-
const
|
|
5108
|
+
const merge3 = (answer) => {
|
|
5110
5109
|
if (answer > result)
|
|
5111
5110
|
result = answer;
|
|
5112
5111
|
};
|
|
@@ -5115,7 +5114,7 @@ function isRecursive(inst, stack, resolve5) {
|
|
|
5115
5114
|
switch (kind) {
|
|
5116
5115
|
case "object": {
|
|
5117
5116
|
const raw = rawShape(def);
|
|
5118
|
-
|
|
5117
|
+
merge3(raw ? shape(raw, true) : ASSUMED);
|
|
5119
5118
|
check2(def.catchall);
|
|
5120
5119
|
break;
|
|
5121
5120
|
}
|
|
@@ -5165,7 +5164,7 @@ function isRecursive(inst, stack, resolve5) {
|
|
|
5165
5164
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
5166
5165
|
case "lazy": {
|
|
5167
5166
|
const inner = def._cachedInner ?? (resolve5 ? inst._zod.innerType : void 0);
|
|
5168
|
-
|
|
5167
|
+
merge3(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
5169
5168
|
break;
|
|
5170
5169
|
}
|
|
5171
5170
|
// a leaf by choice: `parts` are regex fragments, not data positions
|
|
@@ -20527,7 +20526,7 @@ var contextLine = external_exports.object({
|
|
|
20527
20526
|
}).describe("One line of a room: something said, or something that happened to who is in it.");
|
|
20528
20527
|
var roomContext = external_exports.object({
|
|
20529
20528
|
lines: external_exports.array(contextLine).max(CONTEXT_CEILING.lines).describe("Oldest first, the newest that fit the budget."),
|
|
20530
|
-
since_seq: external_exports.int().nonnegative().describe("Where they start: after this `seq`. With a message, where the
|
|
20529
|
+
since_seq: external_exports.int().nonnegative().describe("Where they start: after this `seq`. With a message, where the harness's own last turn in the room began, one that said something or passed with NO_REPLY (ADR 0105), else its joining; from the pull route, 0."),
|
|
20531
20530
|
truncated: external_exports.boolean().describe("Whether older lines after `since_seq` were left out to fit the budget."),
|
|
20532
20531
|
before: external_exports.int().positive().describe("A cursor: `GET /v1/spaces/:id/context?before=` this reads what came before these lines."),
|
|
20533
20532
|
files: roomFiles.max(ROOM_FILES_IN_CONTEXT).optional().describe("The room's files at their current version, newest first, the 20 newest (ADR 0099): with a message, as of it, its own files included; from the pull route, as of now. Each with a URL for this harness. Absent from a Chips before files, and when the room has none."),
|
|
@@ -21675,6 +21674,7 @@ var pairingPayload = external_exports.object({
|
|
|
21675
21674
|
var JOIN_POLL_SECONDS = 3;
|
|
21676
21675
|
var joinKind = external_exports.enum(["harness", "device", "invite", "pairing"]).describe("A harness asking for a key; a device asking to be paired; a person joining by an invite, or a device pairing by a code, that waits for whoever handed it out (ADR 0232).");
|
|
21677
21676
|
var keyCode = external_exports.string().regex(/^[0-9]{6}$/, "six digits");
|
|
21677
|
+
var joinNonce = external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/, "32 bytes, base64url");
|
|
21678
21678
|
var requestCode = external_exports.union([pairingCode, keyCode]);
|
|
21679
21679
|
var joinState = external_exports.enum(["waiting", "allowed", "denied", "expired", "redeemed"]).describe("`waiting` for a person; `allowed`, and not yet collected; `denied` by a person, or withdrawn; `expired` before it was collected; `redeemed`, which spends it.");
|
|
21680
21680
|
var joinStartedBy = external_exports.enum(["requester", "person"]).describe("Who started it: the requester, with a code to compare, or a person, with a link that is allowed already.");
|
|
@@ -21710,12 +21710,15 @@ var joinAsk = external_exports.object({
|
|
|
21710
21710
|
software: words2(120).optional(),
|
|
21711
21711
|
machine: words2(120).optional(),
|
|
21712
21712
|
wants: capabilities.optional().describe("For a harness: what it asks to be allowed."),
|
|
21713
|
-
public_key: external_exports.string().regex(/^[A-Za-z0-9+/]{40,64}={0,2}$/).optional().describe("For a device: the public half of its Ed25519 key, SPKI DER, base64.")
|
|
21713
|
+
public_key: external_exports.string().regex(/^[A-Za-z0-9+/]{40,64}={0,2}$/).optional().describe("For a device: the public half of its Ed25519 key, SPKI DER, base64."),
|
|
21714
|
+
commit: joinNonce.optional().describe("For a device (S-1, ADR 0278): the SHA-256 of `joinCommitText` of a nonce it keeps, base64url. It sends the nonce with its first poll, once it holds the server's, and both ends work the code out.")
|
|
21714
21715
|
}).describe("A request to join.");
|
|
21715
21716
|
var joinAsked = external_exports.object({
|
|
21716
21717
|
request_id: id("join"),
|
|
21717
21718
|
secret: external_exports.string().min(16).max(200).describe("The requester's alone: it polls with it."),
|
|
21718
|
-
code: requestCode,
|
|
21719
|
+
code: requestCode.optional().describe("The code to show. Absent for a device that sent `commit`: it works its own out."),
|
|
21720
|
+
server_key: external_exports.string().min(1).max(128).optional().describe("For a device that sent `commit`: the server's tunnel key, which the code is worked out from and the device pins once it is allowed (ADR 0191, 0278)."),
|
|
21721
|
+
nonce: joinNonce.optional().describe("For a device that sent `commit`: the server's nonce, for the code (ADR 0278)."),
|
|
21719
21722
|
expires_in: external_exports.int().positive().describe("Seconds until it expires."),
|
|
21720
21723
|
interval: external_exports.int().positive().describe("Seconds between polls.")
|
|
21721
21724
|
});
|
|
@@ -22647,6 +22650,17 @@ var jobChangeFrame = external_exports.object({
|
|
|
22647
22650
|
asked_at: isoTimestamp
|
|
22648
22651
|
}).describe("A person switched one of a harness's own jobs, or changed how often it runs (ADR 0252). Sent to a harness's socket that said `jobs` in its `hello`, now or never: it is not in the log. The harness answers with its copy of all its jobs, `answers` or `refuses` naming `change_id`.");
|
|
22649
22652
|
|
|
22653
|
+
// node_modules/@chipschat/protocol/dist/src/read-marks.js
|
|
22654
|
+
var READ_MARKS_MAX = 1e3;
|
|
22655
|
+
var readMarks = external_exports.record(id("space"), external_exports.int().nonnegative().describe("The `seq` the room is read up to.")).refine((marks) => Object.keys(marks).length <= READ_MARKS_MAX, {
|
|
22656
|
+
message: `at most ${READ_MARKS_MAX} rooms`
|
|
22657
|
+
}).describe("How far each room is read, by room id.");
|
|
22658
|
+
var readMarksBody = external_exports.object({ spaces: readMarks });
|
|
22659
|
+
var readFrame = external_exports.object({
|
|
22660
|
+
frame: external_exports.literal("read"),
|
|
22661
|
+
spaces: readMarks.describe("The rooms whose marks moved, and where to.")
|
|
22662
|
+
}).describe("A person read further in some rooms, on one of their devices (ADR 0262). Sent to that person's sockets that said `reads` in their `hello`, never to anyone else's, now or never: `GET /v1/me/read` reads them again.");
|
|
22663
|
+
|
|
22650
22664
|
// node_modules/@chipschat/protocol/dist/src/frames.js
|
|
22651
22665
|
var clientHelloFrame = external_exports.object({
|
|
22652
22666
|
frame: external_exports.literal("hello"),
|
|
@@ -22663,7 +22677,7 @@ var clientHelloFrame = external_exports.object({
|
|
|
22663
22677
|
autonomy: autonomyLevel.optional().describe("The level the harness believes it has.")
|
|
22664
22678
|
})).max(64).optional().describe("Not read by Chips: a connection is its token's, and the server's `hello` names the token's harness and its agents (ADR 0087). Absent for a person's client."),
|
|
22665
22679
|
since: external_exports.int().nonnegative().optional().describe("Not read by Chips: a stream resumes from `subscribe`'s `since`."),
|
|
22666
|
-
accepts: external_exports.array(external_exports.string().min(1).max(40)).max(32).optional().describe("Server frames beyond the base set this client understands, by the names in `SERVER_EXTENSIONS`. A name the server does not know is ignored rather than refused, so a client may name a frame a newer server sends and still be answered by an older one (ADR 0211, Phase 3). A client that says `events` is sent its replay in pages rather than one frame per event; one that says `presence` is sent an agent's state as it changes (ADR 0083); a harness's socket that says `restart` is sent `restart` when a person asks it to restart (ADR 0094); an owner's or admin's that says `join` is sent each join request as it changes (ADR 0122); a harness's that says `context` is sent `context.edit` when a person changes one of its context files (ADR 0134); a harness's that says `models` is sent `model.change` when a person changes an agent's model or effort (ADR 0140); a harness's that says `heartbeats` is sent `heartbeat.change` when a person switches an agent's heartbeat or changes how often it beats (ADR 0143); a harness's that says `context_windows` is sent `context_window.compact` and `context_window.change` when a person asks an agent to compact now or changes the limit it compacts itself at (ADR 0146); a harness's that says `jobs` is sent `job.change` when a person switches one of the jobs it runs on its own (ADR 0252); a person's that says `folders` is sent `folder.open` when they ask, from another of their devices, for a room's folder to be opened in Finder (ADR 0225), and `folder.run` when they ask for its project to be run or stopped there (ADR 0226); a person's that says `folder_runs` is sent `folder.run_state` when their Mac says how a run of theirs stands (ADR 0226); one that says nothing is sent what it was always sent, since an unknown frame is dropped in silence."),
|
|
22680
|
+
accepts: external_exports.array(external_exports.string().min(1).max(40)).max(32).optional().describe("Server frames beyond the base set this client understands, by the names in `SERVER_EXTENSIONS`. A name the server does not know is ignored rather than refused, so a client may name a frame a newer server sends and still be answered by an older one (ADR 0211, Phase 3). A client that says `events` is sent its replay in pages rather than one frame per event; one that says `presence` is sent an agent's state as it changes (ADR 0083); a harness's socket that says `restart` is sent `restart` when a person asks it to restart (ADR 0094); an owner's or admin's that says `join` is sent each join request as it changes (ADR 0122); a harness's that says `context` is sent `context.edit` when a person changes one of its context files (ADR 0134); a harness's that says `models` is sent `model.change` when a person changes an agent's model or effort (ADR 0140); a harness's that says `heartbeats` is sent `heartbeat.change` when a person switches an agent's heartbeat or changes how often it beats (ADR 0143); a harness's that says `context_windows` is sent `context_window.compact` and `context_window.change` when a person asks an agent to compact now or changes the limit it compacts itself at (ADR 0146); a harness's that says `jobs` is sent `job.change` when a person switches one of the jobs it runs on its own (ADR 0252); a person's that says `folders` is sent `folder.open` when they ask, from another of their devices, for a room's folder to be opened in Finder (ADR 0225), and `folder.run` when they ask for its project to be run or stopped there (ADR 0226); a person's that says `folder_runs` is sent `folder.run_state` when their Mac says how a run of theirs stands (ADR 0226); a person's that says `reads` is sent `read` when they read further on another of their devices (ADR 0262); one that says nothing is sent what it was always sent, since an unknown frame is dropped in silence."),
|
|
22667
22681
|
context: contextBudget.optional().describe("How much of a room a harness is handed with each message for it (ADR 0088): 40 lines and 16,000 characters when absent, 200 and 32,000 at most. For this connection. A person's client is handed none.")
|
|
22668
22682
|
}).describe("The first frame a client sends.");
|
|
22669
22683
|
var subscribeFrame = external_exports.object({
|
|
@@ -22789,6 +22803,7 @@ var serverFrame = external_exports.discriminatedUnion("frame", [
|
|
|
22789
22803
|
folderOpenFrame,
|
|
22790
22804
|
folderRunFrame,
|
|
22791
22805
|
folderRunStateFrame,
|
|
22806
|
+
readFrame,
|
|
22792
22807
|
contextEditFrame,
|
|
22793
22808
|
modelChangeFrame,
|
|
22794
22809
|
heartbeatChangeFrame,
|
|
@@ -22840,9 +22855,37 @@ var HTML_POLICY = "default-src 'none'; img-src data: blob:; font-src data:; medi
|
|
|
22840
22855
|
var PAGE_SANDBOX = "allow-scripts allow-popups allow-popups-to-escape-sandbox";
|
|
22841
22856
|
var PAGE_HEADER_POLICY = `${HTML_POLICY}; sandbox ${PAGE_SANDBOX}`;
|
|
22842
22857
|
|
|
22843
|
-
// node_modules/@chipschat/
|
|
22844
|
-
|
|
22845
|
-
|
|
22858
|
+
// node_modules/@chipschat/protocol/dist/src/push.js
|
|
22859
|
+
var PUSH_WORDS = {
|
|
22860
|
+
/** An ask of an agent's that waits on a person (ADR 0045). */
|
|
22861
|
+
decision: {
|
|
22862
|
+
title: "A decision is waiting",
|
|
22863
|
+
body: "Approve or deny from here.",
|
|
22864
|
+
categoryId: "decision"
|
|
22865
|
+
},
|
|
22866
|
+
/** A message that @s the person, or was sent to them (ADR 0158). */
|
|
22867
|
+
messageForYou: { title: "A message for you", body: "Open Chips to read it." },
|
|
22868
|
+
/** A message in a room they hear (ADR 0158). */
|
|
22869
|
+
message: { title: "A new message", body: "Open Chips to read it." },
|
|
22870
|
+
/** A join request for its maker or an owner (ADR 0122). */
|
|
22871
|
+
join: { title: "Something asks to join", body: "Check its code, then allow it or not." },
|
|
22872
|
+
/** The referee paused agents talking among themselves (ADR 0097). */
|
|
22873
|
+
held: { title: "Chips paused the agents", body: "Open the space to let them carry on." },
|
|
22874
|
+
warning: { title: "The agents need a look", body: "They have been talking among themselves." },
|
|
22875
|
+
/** A guest's ask to go direct, and its answers (ADR 0211). */
|
|
22876
|
+
directAsked: {
|
|
22877
|
+
title: "A guest wants to connect directly",
|
|
22878
|
+
body: "Share your server with them in Tailscale."
|
|
22879
|
+
},
|
|
22880
|
+
directShared: { title: "Your server is shared with you", body: "Open Chips to go direct." },
|
|
22881
|
+
directDone: {
|
|
22882
|
+
title: "Connected directly",
|
|
22883
|
+
body: "A guest you shared your server with is direct now."
|
|
22884
|
+
},
|
|
22885
|
+
/** An owner is turning the relay off (ADR 0211, Phase 4). */
|
|
22886
|
+
relayOff: { title: "The relay is going off", body: "Go direct to keep reaching the server." }
|
|
22887
|
+
};
|
|
22888
|
+
var KNOWN = new Set(Object.values(PUSH_WORDS).map((words4) => JSON.stringify([words4.title, words4.body, "categoryId" in words4 ? words4.categoryId : null])));
|
|
22846
22889
|
|
|
22847
22890
|
// node_modules/@chipschat/harness/dist/asks.js
|
|
22848
22891
|
var Asks = class {
|
|
@@ -22877,10 +22920,7 @@ var Asks = class {
|
|
|
22877
22920
|
const from = event["from"];
|
|
22878
22921
|
if (!this.listener.listening() || from?.kind !== "person")
|
|
22879
22922
|
return false;
|
|
22880
|
-
this.hand(spaceId, room, {
|
|
22881
|
-
messageId: String(event["message_id"]),
|
|
22882
|
-
text: String(event["text"] ?? "")
|
|
22883
|
-
});
|
|
22923
|
+
this.hand(spaceId, room, { messageId: String(event["message_id"]), text: String(event["text"] ?? "") }, event);
|
|
22884
22924
|
return true;
|
|
22885
22925
|
}
|
|
22886
22926
|
/**
|
|
@@ -22916,11 +22956,42 @@ var Asks = class {
|
|
|
22916
22956
|
this.rooms.set(spaceId, room);
|
|
22917
22957
|
return room;
|
|
22918
22958
|
}
|
|
22919
|
-
hand(spaceId, room, first) {
|
|
22959
|
+
hand(spaceId, room, first, message) {
|
|
22920
22960
|
room.first = first.messageId;
|
|
22921
|
-
this.listener.hand({
|
|
22961
|
+
this.listener.hand({
|
|
22962
|
+
spaceId,
|
|
22963
|
+
askedBy: room.askedBy,
|
|
22964
|
+
firstMessage: first,
|
|
22965
|
+
...message === void 0 ? {} : { message }
|
|
22966
|
+
});
|
|
22922
22967
|
}
|
|
22923
22968
|
};
|
|
22969
|
+
function firstTurnOf(asked, agentId2) {
|
|
22970
|
+
const { message } = asked;
|
|
22971
|
+
if (message === void 0)
|
|
22972
|
+
return void 0;
|
|
22973
|
+
const room = message.room;
|
|
22974
|
+
const notes = room?.notes ?? {};
|
|
22975
|
+
const [place, ...more] = Object.keys(notes);
|
|
22976
|
+
if (room === void 0 || place === void 0 || more.length > 0) {
|
|
22977
|
+
return { ...message, for: [agentId2] };
|
|
22978
|
+
}
|
|
22979
|
+
const placed = (each) => each.participant_id === place ? { ...each, participant_id: agentId2 } : each;
|
|
22980
|
+
const note = notes[place];
|
|
22981
|
+
const rules = room.rules?.[place];
|
|
22982
|
+
return {
|
|
22983
|
+
...message,
|
|
22984
|
+
for: [agentId2],
|
|
22985
|
+
room: {
|
|
22986
|
+
...room,
|
|
22987
|
+
members: room.members.map(placed),
|
|
22988
|
+
notes: note === void 0 ? {} : { [agentId2]: note },
|
|
22989
|
+
...room.rules === void 0 ? {} : { rules: rules === void 0 ? {} : { [agentId2]: rules } },
|
|
22990
|
+
...room.working === void 0 ? {} : { working: room.working.map(placed) },
|
|
22991
|
+
...room.idle === void 0 ? {} : { idle: room.idle.map(placed) }
|
|
22992
|
+
}
|
|
22993
|
+
};
|
|
22994
|
+
}
|
|
22924
22995
|
function firstWords(history) {
|
|
22925
22996
|
for (const event of history) {
|
|
22926
22997
|
const actor2 = event["actor"];
|
|
@@ -22931,6 +23002,10 @@ function firstWords(history) {
|
|
|
22931
23002
|
return void 0;
|
|
22932
23003
|
}
|
|
22933
23004
|
|
|
23005
|
+
// node_modules/@chipschat/harness/dist/connect.js
|
|
23006
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
23007
|
+
import { basename } from "node:path";
|
|
23008
|
+
|
|
22934
23009
|
// node_modules/@chipschat/harness/dist/events.js
|
|
22935
23010
|
var Emitter = class {
|
|
22936
23011
|
handlers = /* @__PURE__ */ new Map();
|
|
@@ -23322,8 +23397,8 @@ var Project = class {
|
|
|
23322
23397
|
const end2 = all.findIndex((line, at) => at > start && line.startsWith(">>>>>>> "));
|
|
23323
23398
|
if (start < 0 || end2 < 0)
|
|
23324
23399
|
return void 0;
|
|
23325
|
-
const
|
|
23326
|
-
return
|
|
23400
|
+
const shown2 = all.slice(Math.max(0, start - 1), end2 + 2);
|
|
23401
|
+
return shown2.length <= MARKED_LINES ? shown2.join("\n") : [...shown2.slice(0, MARKED_LINES - 1), "\u2026"].join("\n");
|
|
23327
23402
|
} catch {
|
|
23328
23403
|
return void 0;
|
|
23329
23404
|
}
|
|
@@ -25228,7 +25303,7 @@ async function ask(url2, options, send, machine) {
|
|
|
25228
25303
|
if (asked.status !== 201)
|
|
25229
25304
|
throw await refused(asked);
|
|
25230
25305
|
const { request_id, secret, code, interval } = await asked.json();
|
|
25231
|
-
const show = options.onCode ?? ((
|
|
25306
|
+
const show = options.onCode ?? ((shown2, where) => process.stderr.write(`Allow ${options.name} in Chips at ${where}. Its code is ${shown2}.
|
|
25232
25307
|
`));
|
|
25233
25308
|
show(code, url2);
|
|
25234
25309
|
for (; ; ) {
|
|
@@ -25795,9 +25870,9 @@ import { globSync, readFileSync as readFileSync5, statSync } from "node:fs";
|
|
|
25795
25870
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
25796
25871
|
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join5, relative, sep as sep2 } from "node:path";
|
|
25797
25872
|
var AGENTS_MD = "AGENTS.md";
|
|
25798
|
-
function placesOf(env,
|
|
25799
|
-
const config3 = env["OPENCODE_CONFIG_DIR"] || join5(env["XDG_CONFIG_HOME"] || join5(
|
|
25800
|
-
return { home
|
|
25873
|
+
function placesOf(env, home) {
|
|
25874
|
+
const config3 = env["OPENCODE_CONFIG_DIR"] || join5(env["XDG_CONFIG_HOME"] || join5(home, ".config"), "opencode");
|
|
25875
|
+
return { home, config: config3 };
|
|
25801
25876
|
}
|
|
25802
25877
|
function hashOf(text2) {
|
|
25803
25878
|
return createHash2("sha256").update(text2, "utf8").digest("hex");
|
|
@@ -25825,7 +25900,7 @@ function upTo(dir, stop2) {
|
|
|
25825
25900
|
}
|
|
25826
25901
|
return dirs;
|
|
25827
25902
|
}
|
|
25828
|
-
function namedFiles(sources, folder2, root,
|
|
25903
|
+
function namedFiles(sources, folder2, root, home) {
|
|
25829
25904
|
const found = [];
|
|
25830
25905
|
const glob = (pattern, cwd) => {
|
|
25831
25906
|
try {
|
|
@@ -25836,7 +25911,7 @@ function namedFiles(sources, folder2, root, home3) {
|
|
|
25836
25911
|
};
|
|
25837
25912
|
for (const source2 of sources) {
|
|
25838
25913
|
if (/^https?:\/\//i.test(source2)) continue;
|
|
25839
|
-
const expanded = source2.startsWith("~/") ? join5(
|
|
25914
|
+
const expanded = source2.startsWith("~/") ? join5(home, source2.slice(2)) : source2;
|
|
25840
25915
|
const matches = isAbsolute2(expanded) ? glob(expanded.slice(dirname4(expanded).length + 1), dirname4(expanded)) : upTo(folder2, within(folder2, root) ? root : folder2).flatMap((dir) => glob(expanded, dir));
|
|
25841
25916
|
for (const path of matches.sort()) if (isFile(path) && !found.includes(path)) found.push(path);
|
|
25842
25917
|
}
|
|
@@ -25856,15 +25931,15 @@ var ContextFiles = class {
|
|
|
25856
25931
|
}
|
|
25857
25932
|
places;
|
|
25858
25933
|
folders;
|
|
25859
|
-
/** `~/
|
|
25934
|
+
/** `~/Chips/Agents/opencode/AGENTS.md`, or the whole path where it isn't in the home. */
|
|
25860
25935
|
tilde(path) {
|
|
25861
|
-
const { home
|
|
25862
|
-
return within(path,
|
|
25936
|
+
const { home } = this.places;
|
|
25937
|
+
return within(path, home) ? `~${path.slice(home.length)}` : path;
|
|
25863
25938
|
}
|
|
25864
25939
|
/** A shared file's name: where it is, a step at a time (`~ › .config › opencode › AGENTS.md`), with no slash. */
|
|
25865
25940
|
crumbs(path) {
|
|
25866
|
-
const { home
|
|
25867
|
-
const steps = within(path,
|
|
25941
|
+
const { home } = this.places;
|
|
25942
|
+
const steps = within(path, home) ? ["~", ...relative(home, path).split(sep2)] : ["/", ...path.split(sep2)];
|
|
25868
25943
|
return steps.filter((step) => step !== "").join(" \u203A ").slice(0, 255);
|
|
25869
25944
|
}
|
|
25870
25945
|
/**
|
|
@@ -26173,6 +26248,13 @@ var OpenCodeClient = class {
|
|
|
26173
26248
|
const metadata = data["metadata"];
|
|
26174
26249
|
return { id: id2, metadata: typeof metadata === "object" && metadata !== null ? metadata : {} };
|
|
26175
26250
|
}
|
|
26251
|
+
/**
|
|
26252
|
+
* A session moved to another folder (2.0.25), with everything said in it; the session's context
|
|
26253
|
+
* says it moved. It works where the old folder is gone, as after Chips ADR 0283's move.
|
|
26254
|
+
*/
|
|
26255
|
+
async moveSession(sessionId, directory) {
|
|
26256
|
+
await this.call("POST", `/api/session/${encodeURIComponent(sessionId)}/move`, { directory });
|
|
26257
|
+
}
|
|
26176
26258
|
/** A session's title, or its metadata, which this replaces whole. */
|
|
26177
26259
|
async update(sessionId, changes) {
|
|
26178
26260
|
await this.call("PATCH", `/api/session/${encodeURIComponent(sessionId)}`, {
|
|
@@ -26644,10 +26726,10 @@ var Bridge = class {
|
|
|
26644
26726
|
}
|
|
26645
26727
|
/** The Main agent into the harness's own room, in the place of whichever of ours is there. */
|
|
26646
26728
|
async goHome(main2, known) {
|
|
26647
|
-
const { chips: chips2, home
|
|
26648
|
-
if (
|
|
26649
|
-
const
|
|
26650
|
-
await chips2.agents.attach(main2.agentId, { spaceId:
|
|
26729
|
+
const { chips: chips2, home } = this.options;
|
|
26730
|
+
if (home === void 0 || main2.spaces.includes(home)) return;
|
|
26731
|
+
const there2 = known.find((agent) => agent.agentId !== main2.agentId && agent.spaces.includes(home));
|
|
26732
|
+
await chips2.agents.attach(main2.agentId, { spaceId: home, replacing: there2?.agentId ?? chips2.harnessId }).catch((error62) => this.log(`${main2.name} stays out of its home: ${String(error62)}`));
|
|
26651
26733
|
}
|
|
26652
26734
|
/**
|
|
26653
26735
|
* Trouble while the server doesn't answer, and fine once it does (ADR 0083). Nothing while a
|
|
@@ -26728,7 +26810,7 @@ var Bridge = class {
|
|
|
26728
26810
|
await this.sendContexts();
|
|
26729
26811
|
const context = await chips2.roomContext(spaceId, { limit: 20 }).catch(() => void 0);
|
|
26730
26812
|
const said = context?.lines.find((line) => line.message_id === firstMessage.messageId)?.from;
|
|
26731
|
-
const message = {
|
|
26813
|
+
const message = firstTurnOf(asked, agent.agentId) ?? {
|
|
26732
26814
|
type: "message.new",
|
|
26733
26815
|
space_id: spaceId,
|
|
26734
26816
|
message_id: firstMessage.messageId,
|
|
@@ -26762,7 +26844,7 @@ var Bridge = class {
|
|
|
26762
26844
|
* that isn't one of the harness's has no copy, nor has an ask the room stopped waiting for.
|
|
26763
26845
|
*/
|
|
26764
26846
|
async makeCopy(fork) {
|
|
26765
|
-
const { chips: chips2, opencode: opencode2, state: state2, saveState } = this.options;
|
|
26847
|
+
const { chips: chips2, opencode: opencode2, state: state2, saveState: saveState2 } = this.options;
|
|
26766
26848
|
const spaceId = fork.space_id;
|
|
26767
26849
|
if (spaceId === void 0) return;
|
|
26768
26850
|
const late = Date.now() - Date.parse(fork.ts);
|
|
@@ -26793,7 +26875,7 @@ var Bridge = class {
|
|
|
26793
26875
|
const folder2 = await this.folderFor({ who, agent });
|
|
26794
26876
|
kept = { ...copied === void 0 ? {} : { session: copied.id }, ...folder2 === void 0 ? {} : { folder: folder2 } };
|
|
26795
26877
|
state2.forks = { ...state2.forks, [fork.id]: kept };
|
|
26796
|
-
|
|
26878
|
+
saveState2(state2);
|
|
26797
26879
|
}
|
|
26798
26880
|
const copy = await chips2.agents.register({
|
|
26799
26881
|
key: `opencode:fork:${fork.id}`,
|
|
@@ -26806,7 +26888,7 @@ var Bridge = class {
|
|
|
26806
26888
|
if (kept.folder !== void 0) state2.folders = { ...state2.folders, [copy.agentId]: kept.folder };
|
|
26807
26889
|
const pick2 = state2.models?.[who];
|
|
26808
26890
|
if (pick2 !== void 0) state2.models = { ...state2.models, [copy.agentId]: pick2 };
|
|
26809
|
-
|
|
26891
|
+
saveState2(state2);
|
|
26810
26892
|
if (copied !== void 0) {
|
|
26811
26893
|
await opencode2.update(copied.id, {
|
|
26812
26894
|
title: `Chips space ${spaceId}`,
|
|
@@ -26871,7 +26953,7 @@ var Bridge = class {
|
|
|
26871
26953
|
async modelChanged(change) {
|
|
26872
26954
|
const whose = await this.whoseIs(change.agent_id);
|
|
26873
26955
|
if (whose === void 0) throw new Refuse("This harness has no such agent.");
|
|
26874
|
-
const { opencode: opencode2, state: state2, saveState } = this.options;
|
|
26956
|
+
const { opencode: opencode2, state: state2, saveState: saveState2 } = this.options;
|
|
26875
26957
|
let pick2;
|
|
26876
26958
|
try {
|
|
26877
26959
|
pick2 = pickOf(change, await opencode2.models());
|
|
@@ -26882,7 +26964,7 @@ var Bridge = class {
|
|
|
26882
26964
|
if (pick2 === null) delete models[whose.who];
|
|
26883
26965
|
else models[whose.who] = pick2;
|
|
26884
26966
|
state2.models = models;
|
|
26885
|
-
|
|
26967
|
+
saveState2(state2);
|
|
26886
26968
|
const name = await this.nameOf(whose);
|
|
26887
26969
|
this.log(`${name} runs on ${pick2 === null ? "its default" : `${pick2.model}${pick2.effort === void 0 ? "" : ` at ${pick2.effort}`}`}, from Chips`);
|
|
26888
26970
|
const copy = await this.modelCopyOf(change.agent_id);
|
|
@@ -26920,7 +27002,7 @@ var Bridge = class {
|
|
|
26920
27002
|
*/
|
|
26921
27003
|
async readingOf(folder2) {
|
|
26922
27004
|
const { opencode: opencode2 } = this.options;
|
|
26923
|
-
const { home
|
|
27005
|
+
const { home } = this.places;
|
|
26924
27006
|
let root = this.roots.get(folder2);
|
|
26925
27007
|
if (root === void 0) {
|
|
26926
27008
|
root = await opencode2.projectRoot(folder2).catch(() => folder2);
|
|
@@ -26931,7 +27013,7 @@ var Bridge = class {
|
|
|
26931
27013
|
() => false
|
|
26932
27014
|
);
|
|
26933
27015
|
const sources = await this.readsNamed ? await opencode2.instructions(folder2).catch(() => []) : [];
|
|
26934
|
-
return { stop: within(folder2,
|
|
27016
|
+
return { stop: within(folder2, home) ? home : root, named: namedFiles(sources, folder2, root, home) };
|
|
26935
27017
|
}
|
|
26936
27018
|
/** Each agent of the harness's, with the folder its sessions work in and what OpenCode reads there. */
|
|
26937
27019
|
async agentReadings() {
|
|
@@ -27277,7 +27359,7 @@ var Bridge = class {
|
|
|
27277
27359
|
}
|
|
27278
27360
|
/** The words into the room's session: the one it had, or a new one when OpenCode no longer has it. */
|
|
27279
27361
|
async give({ id: id2, who, agent, spaceId }, said, rules) {
|
|
27280
|
-
const { opencode: opencode2, state: state2, saveState } = this.options;
|
|
27362
|
+
const { opencode: opencode2, state: state2, saveState: saveState2 } = this.options;
|
|
27281
27363
|
let kept = state2.sessions[id2];
|
|
27282
27364
|
if (kept === void 0 && this.copying.size > 0) {
|
|
27283
27365
|
await Promise.allSettled(this.copying.values());
|
|
@@ -27301,7 +27383,7 @@ var Bridge = class {
|
|
|
27301
27383
|
metadata: { chips_space: spaceId, chips_agent: who }
|
|
27302
27384
|
});
|
|
27303
27385
|
state2.sessions[id2] = made;
|
|
27304
|
-
|
|
27386
|
+
saveState2(state2);
|
|
27305
27387
|
return { sessionId: made, given: await this.prompted(made, said(made), rules) };
|
|
27306
27388
|
}
|
|
27307
27389
|
/**
|
|
@@ -27309,14 +27391,14 @@ var Bridge = class {
|
|
|
27309
27391
|
* kept by whose it is, so a rename doesn't move it. None without `folders`: the project's.
|
|
27310
27392
|
*/
|
|
27311
27393
|
async folderFor({ who, agent }) {
|
|
27312
|
-
const { folders: root, state: state2, saveState } = this.options;
|
|
27394
|
+
const { folders: root, state: state2, saveState: saveState2 } = this.options;
|
|
27313
27395
|
if (root === void 0) return void 0;
|
|
27314
27396
|
let folder2 = state2.folders?.[who];
|
|
27315
27397
|
if (folder2 === void 0) {
|
|
27316
27398
|
const name = await this.nameOf({ who, agent });
|
|
27317
27399
|
folder2 = freeFolder(root, name, Object.values(state2.folders ?? {}));
|
|
27318
27400
|
state2.folders = { ...state2.folders, [who]: folder2 };
|
|
27319
|
-
|
|
27401
|
+
saveState2(state2);
|
|
27320
27402
|
}
|
|
27321
27403
|
mkdirSync4(folder2, { recursive: true });
|
|
27322
27404
|
return folder2;
|
|
@@ -27968,12 +28050,55 @@ function lastLine(said) {
|
|
|
27968
28050
|
}
|
|
27969
28051
|
|
|
27970
28052
|
// src/keep.ts
|
|
27971
|
-
import {
|
|
27972
|
-
import { basename as basename3, join as join7, resolve as resolve2 } from "node:path";
|
|
28053
|
+
import { basename as basename3 } from "node:path";
|
|
27973
28054
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28055
|
+
|
|
28056
|
+
// src/places.ts
|
|
28057
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
28058
|
+
import { homedir as homedir5 } from "node:os";
|
|
28059
|
+
import { isAbsolute as isAbsolute3, join as join7, relative as relative2, resolve as resolve2, sep as sep3 } from "node:path";
|
|
28060
|
+
function placesOf2(home = homedir5()) {
|
|
28061
|
+
return {
|
|
28062
|
+
settings: join7(home, "Chips", ".bridges", "opencode"),
|
|
28063
|
+
agents: join7(home, "Chips", "Agents", "opencode"),
|
|
28064
|
+
oldSettings: join7(home, ".chips-opencode"),
|
|
28065
|
+
oldAgents: join7(home, "chips-opencode", "agents")
|
|
28066
|
+
};
|
|
28067
|
+
}
|
|
28068
|
+
var fromHome = (path, home = homedir5()) => path === "~" ? home : path.startsWith("~/") ? join7(home, path.slice(2)) : path;
|
|
28069
|
+
function inside2(root, path) {
|
|
28070
|
+
const under = relative2(root, path);
|
|
28071
|
+
return under === ".." || under.startsWith(`..${sep3}`) || isAbsolute3(under) ? void 0 : under;
|
|
28072
|
+
}
|
|
28073
|
+
function nowAt(path, before2, after) {
|
|
28074
|
+
const under = inside2(before2, path);
|
|
28075
|
+
if (under === void 0) return path;
|
|
28076
|
+
const moved = join7(after, under);
|
|
28077
|
+
return existsSync5(path) && !existsSync5(moved) ? path : moved;
|
|
28078
|
+
}
|
|
28079
|
+
var configOf = (word, places, home) => resolve2(fromHome(word ?? join7(places.settings, "config.json"), home));
|
|
28080
|
+
function configTargetOf(word, home = homedir5()) {
|
|
28081
|
+
const places = placesOf2(home);
|
|
28082
|
+
const path = configOf(word, places, home);
|
|
28083
|
+
const under = inside2(places.oldSettings, path);
|
|
28084
|
+
return under === void 0 ? path : join7(places.settings, under);
|
|
28085
|
+
}
|
|
28086
|
+
function configPathOf(word, home = homedir5()) {
|
|
28087
|
+
const places = placesOf2(home);
|
|
28088
|
+
const target = configTargetOf(word, home);
|
|
28089
|
+
const under = inside2(places.settings, target);
|
|
28090
|
+
const old = under === void 0 ? void 0 : join7(places.oldSettings, under);
|
|
28091
|
+
return old !== void 0 && existsSync5(old) && !existsSync5(target) ? old : target;
|
|
28092
|
+
}
|
|
28093
|
+
function foldersPathOf(path, home = homedir5()) {
|
|
28094
|
+
const places = placesOf2(home);
|
|
28095
|
+
return nowAt(resolve2(fromHome(path, home)), places.oldAgents, places.agents);
|
|
28096
|
+
}
|
|
28097
|
+
|
|
28098
|
+
// src/keep.ts
|
|
27974
28099
|
var flags = process.argv.slice(2);
|
|
27975
28100
|
var words3 = flags.filter((word) => !word.startsWith("--"));
|
|
27976
|
-
var whole = (word) =>
|
|
28101
|
+
var whole = (word) => configPathOf(word);
|
|
27977
28102
|
var here = fileURLToPath2(new URL("..", import.meta.url)).replace(/\/$/, "");
|
|
27978
28103
|
var self = fileURLToPath2(import.meta.url);
|
|
27979
28104
|
var run = self.endsWith(".js") ? [self] : ["--import", "tsx", "src/main.ts"];
|
|
@@ -28018,12 +28143,147 @@ function keepIfAsked(label2, url2, names, log2, before2 = () => void 0) {
|
|
|
28018
28143
|
);
|
|
28019
28144
|
}
|
|
28020
28145
|
|
|
28146
|
+
// src/move.ts
|
|
28147
|
+
import { lstatSync, mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync6, renameSync as renameSync2, rmdirSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
28148
|
+
import { homedir as homedir6 } from "node:os";
|
|
28149
|
+
import { dirname as dirname5, join as join8, resolve as resolve3 } from "node:path";
|
|
28150
|
+
var there = (path) => {
|
|
28151
|
+
try {
|
|
28152
|
+
lstatSync(path);
|
|
28153
|
+
return true;
|
|
28154
|
+
} catch {
|
|
28155
|
+
return false;
|
|
28156
|
+
}
|
|
28157
|
+
};
|
|
28158
|
+
var isFolder = (path) => lstatSync(path).isDirectory();
|
|
28159
|
+
var shown = (path, home) => path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
|
|
28160
|
+
function merge2(from, to, log2, home = homedir6(), quiet = false) {
|
|
28161
|
+
if (!there(from)) return;
|
|
28162
|
+
try {
|
|
28163
|
+
if (!there(to)) {
|
|
28164
|
+
mkdirSync5(dirname5(to), { recursive: true });
|
|
28165
|
+
renameSync2(from, to);
|
|
28166
|
+
if (!quiet) log2(`moved ${shown(from, home)} to ${shown(to, home)}`);
|
|
28167
|
+
return;
|
|
28168
|
+
}
|
|
28169
|
+
if (!isFolder(from) || !isFolder(to)) {
|
|
28170
|
+
log2(`${shown(from, home)} was left where it is: ${shown(to, home)} is there already`);
|
|
28171
|
+
return;
|
|
28172
|
+
}
|
|
28173
|
+
for (const entry of readdirSync(from)) merge2(join8(from, entry), join8(to, entry), log2, home, true);
|
|
28174
|
+
if (readdirSync(from).length === 0) rmdirSync(from);
|
|
28175
|
+
if (!quiet) log2(`moved what was in ${shown(from, home)} into ${shown(to, home)}`);
|
|
28176
|
+
} catch (error62) {
|
|
28177
|
+
log2(`${shown(from, home)} couldn't be moved to ${shown(to, home)}, so it is used where it is: ${error62.message}`);
|
|
28178
|
+
}
|
|
28179
|
+
}
|
|
28180
|
+
function removeIfEmpty(path) {
|
|
28181
|
+
try {
|
|
28182
|
+
if (readdirSync(path).length === 0) rmdirSync(path);
|
|
28183
|
+
} catch {
|
|
28184
|
+
}
|
|
28185
|
+
}
|
|
28186
|
+
function pairOf(path, places) {
|
|
28187
|
+
const old = inside2(places.oldAgents, path);
|
|
28188
|
+
if (old !== void 0) return { old: path, now: join8(places.agents, old) };
|
|
28189
|
+
const now = inside2(places.agents, path);
|
|
28190
|
+
return now === void 0 ? void 0 : { old: join8(places.oldAgents, now), now: path };
|
|
28191
|
+
}
|
|
28192
|
+
function writeJson(path, value) {
|
|
28193
|
+
const next = `${path}.next`;
|
|
28194
|
+
writeFileSync4(next, `${JSON.stringify(value, null, 2)}
|
|
28195
|
+
`, { mode: 384 });
|
|
28196
|
+
renameSync2(next, path);
|
|
28197
|
+
}
|
|
28198
|
+
var readJson = (path) => {
|
|
28199
|
+
try {
|
|
28200
|
+
const read = JSON.parse(readFileSync6(path, "utf8"));
|
|
28201
|
+
return typeof read === "object" && read !== null ? read : void 0;
|
|
28202
|
+
} catch {
|
|
28203
|
+
return void 0;
|
|
28204
|
+
}
|
|
28205
|
+
};
|
|
28206
|
+
function moveOld(configPath2, log2, home = homedir6()) {
|
|
28207
|
+
const places = placesOf2(home);
|
|
28208
|
+
const settings = dirname5(configPath2);
|
|
28209
|
+
const under = inside2(places.settings, settings);
|
|
28210
|
+
if (under !== void 0) {
|
|
28211
|
+
mkdirSync5(dirname5(dirname5(places.settings)), { recursive: true });
|
|
28212
|
+
mkdirSync5(dirname5(places.settings), { recursive: true, mode: 448 });
|
|
28213
|
+
merge2(join8(places.oldSettings, under), settings, log2, home);
|
|
28214
|
+
}
|
|
28215
|
+
const config3 = readJson(configPath2);
|
|
28216
|
+
const said = config3?.["folders"];
|
|
28217
|
+
const root = typeof said === "string" && said !== "" ? resolve3(fromHome(said, home)) : places.agents;
|
|
28218
|
+
const pair = pairOf(root, places);
|
|
28219
|
+
if (pair !== void 0) merge2(pair.old, pair.now, log2, home);
|
|
28220
|
+
if (config3 !== void 0 && typeof said === "string") {
|
|
28221
|
+
const moved = nowAt(root, places.oldAgents, places.agents);
|
|
28222
|
+
if (moved !== root) writeJson(configPath2, { ...config3, folders: `~/${moved.slice(home.length + 1)}` });
|
|
28223
|
+
}
|
|
28224
|
+
const statePath2 = join8(settings, "sessions.json");
|
|
28225
|
+
const state2 = readJson(statePath2);
|
|
28226
|
+
if (state2 === void 0) return;
|
|
28227
|
+
const moving = { ...state2.moving };
|
|
28228
|
+
let changed = false;
|
|
28229
|
+
const folders = { ...state2.folders };
|
|
28230
|
+
for (const [who, folder2] of Object.entries(folders)) {
|
|
28231
|
+
const moved = nowAt(folder2, places.oldAgents, places.agents);
|
|
28232
|
+
if (moved === folder2) continue;
|
|
28233
|
+
folders[who] = moved;
|
|
28234
|
+
for (const [room, session] of Object.entries(state2.sessions ?? {})) {
|
|
28235
|
+
if (room.slice(0, room.lastIndexOf("|")) === who) moving[session] = moved;
|
|
28236
|
+
}
|
|
28237
|
+
changed = true;
|
|
28238
|
+
}
|
|
28239
|
+
const forks = { ...state2.forks };
|
|
28240
|
+
for (const [ask2, fork] of Object.entries(forks)) {
|
|
28241
|
+
if (fork.folder === void 0) continue;
|
|
28242
|
+
const moved = nowAt(fork.folder, places.oldAgents, places.agents);
|
|
28243
|
+
if (moved === fork.folder) continue;
|
|
28244
|
+
forks[ask2] = { ...fork, folder: moved };
|
|
28245
|
+
if (fork.session !== void 0) moving[fork.session] = moved;
|
|
28246
|
+
changed = true;
|
|
28247
|
+
}
|
|
28248
|
+
if (changed) {
|
|
28249
|
+
writeJson(statePath2, {
|
|
28250
|
+
...state2,
|
|
28251
|
+
folders,
|
|
28252
|
+
...state2.forks === void 0 ? {} : { forks },
|
|
28253
|
+
...Object.keys(moving).length === 0 ? {} : { moving }
|
|
28254
|
+
});
|
|
28255
|
+
}
|
|
28256
|
+
removeIfEmpty(places.oldSettings);
|
|
28257
|
+
removeIfEmpty(places.oldAgents);
|
|
28258
|
+
removeIfEmpty(dirname5(places.oldAgents));
|
|
28259
|
+
}
|
|
28260
|
+
async function moveSessions(state2, opencode2, save, log2, home = homedir6()) {
|
|
28261
|
+
const moving = state2.moving;
|
|
28262
|
+
if (moving === void 0) return;
|
|
28263
|
+
for (const [session, folder2] of Object.entries(moving)) {
|
|
28264
|
+
try {
|
|
28265
|
+
await opencode2.moveSession(session, folder2);
|
|
28266
|
+
log2(`moved OpenCode's session ${session} to ${shown(folder2, home)}`);
|
|
28267
|
+
delete moving[session];
|
|
28268
|
+
} catch (error62) {
|
|
28269
|
+
if (!(error62 instanceof OpenCodeError)) {
|
|
28270
|
+
log2(`OpenCode's session ${session} wasn't moved to ${shown(folder2, home)}, for now: ${String(error62)}`);
|
|
28271
|
+
continue;
|
|
28272
|
+
}
|
|
28273
|
+
log2(`OpenCode wouldn't move its session ${session} to ${shown(folder2, home)}: ${error62.message}`);
|
|
28274
|
+
delete moving[session];
|
|
28275
|
+
}
|
|
28276
|
+
}
|
|
28277
|
+
if (Object.keys(moving).length === 0) delete state2.moving;
|
|
28278
|
+
save(state2);
|
|
28279
|
+
}
|
|
28280
|
+
|
|
28021
28281
|
// src/server.ts
|
|
28022
28282
|
import { spawn } from "node:child_process";
|
|
28023
28283
|
import { randomBytes } from "node:crypto";
|
|
28024
|
-
import { accessSync as accessSync2, constants as constants2, existsSync as
|
|
28284
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
28025
28285
|
import { createServer } from "node:net";
|
|
28026
|
-
import { delimiter as delimiter2, join as
|
|
28286
|
+
import { delimiter as delimiter2, join as join9 } from "node:path";
|
|
28027
28287
|
import { createInterface } from "node:readline";
|
|
28028
28288
|
var ServerError = class extends Error {
|
|
28029
28289
|
constructor(message, missing = false) {
|
|
@@ -28034,10 +28294,10 @@ var ServerError = class extends Error {
|
|
|
28034
28294
|
name = "ServerError";
|
|
28035
28295
|
};
|
|
28036
28296
|
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
28037
|
-
var onPath = (command, env) => command.includes("/") ?
|
|
28297
|
+
var onPath = (command, env) => command.includes("/") ? existsSync6(command) : (env["PATH"] ?? "").split(delimiter2).some((folder2) => {
|
|
28038
28298
|
if (folder2 === "") return false;
|
|
28039
28299
|
try {
|
|
28040
|
-
accessSync2(
|
|
28300
|
+
accessSync2(join9(folder2, command), constants2.X_OK);
|
|
28041
28301
|
return true;
|
|
28042
28302
|
} catch {
|
|
28043
28303
|
return false;
|
|
@@ -28071,9 +28331,9 @@ async function answers2(url2, password) {
|
|
|
28071
28331
|
return typeof info === "string" ? info : "ok";
|
|
28072
28332
|
}
|
|
28073
28333
|
function keptIn(path) {
|
|
28074
|
-
if (!
|
|
28334
|
+
if (!existsSync6(path)) return void 0;
|
|
28075
28335
|
try {
|
|
28076
|
-
const kept = JSON.parse(
|
|
28336
|
+
const kept = JSON.parse(readFileSync7(path, "utf8"));
|
|
28077
28337
|
return typeof kept.port === "number" && typeof kept.password === "string" ? kept : void 0;
|
|
28078
28338
|
} catch {
|
|
28079
28339
|
return void 0;
|
|
@@ -28102,7 +28362,7 @@ async function ownServer(options) {
|
|
|
28102
28362
|
const env = options.env ?? process.env;
|
|
28103
28363
|
const command = options.command ?? "opencode";
|
|
28104
28364
|
const log2 = options.log ?? (() => void 0);
|
|
28105
|
-
const path =
|
|
28365
|
+
const path = join9(options.folder, "server.json");
|
|
28106
28366
|
let kept = keptIn(path);
|
|
28107
28367
|
let before2 = false;
|
|
28108
28368
|
if (kept !== void 0) {
|
|
@@ -28124,7 +28384,7 @@ async function ownServer(options) {
|
|
|
28124
28384
|
let again2;
|
|
28125
28385
|
const said = [];
|
|
28126
28386
|
const start = () => {
|
|
28127
|
-
|
|
28387
|
+
writeFileSync5(path, `${JSON.stringify({ port, password }, null, 2)}
|
|
28128
28388
|
`, { mode: 384 });
|
|
28129
28389
|
const started = spawn(command, ["serve", "--hostname", "127.0.0.1", "--port", String(port)], {
|
|
28130
28390
|
cwd: options.cwd,
|
|
@@ -28226,9 +28486,8 @@ async function ownServer(options) {
|
|
|
28226
28486
|
}
|
|
28227
28487
|
|
|
28228
28488
|
// src/setup.ts
|
|
28229
|
-
import { existsSync as
|
|
28230
|
-
import {
|
|
28231
|
-
import { join as join9, resolve as resolve3 } from "node:path";
|
|
28489
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
|
|
28490
|
+
import { resolve as resolve4 } from "node:path";
|
|
28232
28491
|
var SetupError = class extends Error {
|
|
28233
28492
|
constructor(message, words4 = {}) {
|
|
28234
28493
|
super(message);
|
|
@@ -28245,9 +28504,8 @@ var hostOf = (url2) => {
|
|
|
28245
28504
|
}
|
|
28246
28505
|
};
|
|
28247
28506
|
var STILL_GOOD = "The link is still good: run the same command again once that's done.";
|
|
28248
|
-
var home = (path) => path.startsWith("~/") ? join9(homedir6(), path.slice(2)) : path;
|
|
28249
28507
|
function readConfig(path, env = process.env) {
|
|
28250
|
-
const raw =
|
|
28508
|
+
const raw = existsSync7(path) ? JSON.parse(readFileSync8(path, "utf8")) : {};
|
|
28251
28509
|
const passwordEnv = typeof raw["passwordEnv"] === "string" ? raw["passwordEnv"] : "OPENCODE_PASSWORD";
|
|
28252
28510
|
const password = filledIn(env[passwordEnv]) ? env[passwordEnv] : void 0;
|
|
28253
28511
|
const ownServer2 = password === void 0 && typeof raw["url"] !== "string";
|
|
@@ -28274,8 +28532,8 @@ function readConfig(path, env = process.env) {
|
|
|
28274
28532
|
password,
|
|
28275
28533
|
passwordEnv,
|
|
28276
28534
|
ownServer: ownServer2,
|
|
28277
|
-
directory: typeof raw["directory"] === "string" ?
|
|
28278
|
-
folders:
|
|
28535
|
+
directory: typeof raw["directory"] === "string" ? resolve4(fromHome(raw["directory"])) : void 0,
|
|
28536
|
+
folders: foldersPathOf(typeof raw["folders"] === "string" ? raw["folders"] : placesOf2().agents),
|
|
28279
28537
|
agents
|
|
28280
28538
|
};
|
|
28281
28539
|
}
|
|
@@ -28303,17 +28561,17 @@ async function checkOpenCode(config3, client) {
|
|
|
28303
28561
|
}
|
|
28304
28562
|
|
|
28305
28563
|
// src/version.ts
|
|
28306
|
-
import { readFileSync as
|
|
28307
|
-
var VERSION = JSON.parse(
|
|
28564
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
28565
|
+
var VERSION = JSON.parse(readFileSync9(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
28308
28566
|
|
|
28309
28567
|
// src/main.ts
|
|
28310
|
-
var home2 = (path) => path.startsWith("~/") ? join10(homedir7(), path.slice(2)) : path;
|
|
28311
28568
|
var link = process.env["CHIPS_JOIN_LINK"] || void 0;
|
|
28312
28569
|
var LABEL = "chat.chips.opencode";
|
|
28313
28570
|
stopIfAsked(LABEL);
|
|
28314
|
-
var configPath = resolve4(home2(words3[0] ?? "~/.chips-opencode/config.json"));
|
|
28315
28571
|
var log = (line) => process.stdout.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
28316
28572
|
`);
|
|
28573
|
+
moveOld(configTargetOf(words3[0]), log);
|
|
28574
|
+
var configPath = configPathOf(words3[0]);
|
|
28317
28575
|
var stop = (error62) => {
|
|
28318
28576
|
if (!(error62 instanceof SetupError)) throw error62;
|
|
28319
28577
|
reportStep("failed", error62.words);
|
|
@@ -28327,14 +28585,14 @@ try {
|
|
|
28327
28585
|
} catch (error62) {
|
|
28328
28586
|
config2 = stop(error62);
|
|
28329
28587
|
}
|
|
28330
|
-
var folder =
|
|
28331
|
-
|
|
28588
|
+
var folder = dirname6(configPath);
|
|
28589
|
+
mkdirSync6(folder, { recursive: true, mode: 448 });
|
|
28332
28590
|
var statePath = join10(folder, "sessions.json");
|
|
28333
28591
|
var server;
|
|
28334
28592
|
if (link !== void 0) reportStep("checking", { detail: "Looking for OpenCode" });
|
|
28335
28593
|
if (config2.ownServer) {
|
|
28336
|
-
const cwd = config2.directory ??
|
|
28337
|
-
|
|
28594
|
+
const cwd = config2.directory ?? config2.folders;
|
|
28595
|
+
mkdirSync6(cwd, { recursive: true });
|
|
28338
28596
|
try {
|
|
28339
28597
|
server = await ownServer({ folder, cwd, log });
|
|
28340
28598
|
} catch (error62) {
|
|
@@ -28378,7 +28636,10 @@ var joined = await join3({
|
|
|
28378
28636
|
onCode: (code) => log(`Allow this harness in Chips: ${code}`)
|
|
28379
28637
|
}).catch(refused2);
|
|
28380
28638
|
keepIfAsked(LABEL, joined.url, [config2.passwordEnv], log, () => server?.stop());
|
|
28381
|
-
var state =
|
|
28639
|
+
var state = existsSync8(statePath) ? JSON.parse(readFileSync10(statePath, "utf8")) : { sessions: {} };
|
|
28640
|
+
var saveState = (next) => writeFileSync6(statePath, `${JSON.stringify(next, null, 2)}
|
|
28641
|
+
`, { mode: 384 });
|
|
28642
|
+
if (offered !== void 0) await moveSessions(state, opencode, saveState, log);
|
|
28382
28643
|
var bridge;
|
|
28383
28644
|
var chips = await connect({
|
|
28384
28645
|
url: joined.url,
|
|
@@ -28400,8 +28661,7 @@ bridge = new Bridge({
|
|
|
28400
28661
|
agents: config2.agents,
|
|
28401
28662
|
home: joined.spaceId,
|
|
28402
28663
|
state,
|
|
28403
|
-
saveState
|
|
28404
|
-
`, { mode: 384 }),
|
|
28664
|
+
saveState,
|
|
28405
28665
|
log,
|
|
28406
28666
|
restartServer: server?.restart,
|
|
28407
28667
|
...config2.directory === void 0 ? { folders: config2.folders } : { directory: config2.directory }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chipschat/opencode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "OpenCode as a Chips harness, on its V2 server (opencode serve): each OpenCode agent a Chips agent, each room an OpenCode session.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"release": "npm run build && npm test && npm pack"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@chipschat/conformance": "1.0.
|
|
37
|
-
"@chipschat/harness": "1.0.
|
|
38
|
-
"@chipschat/protocol": "1.0.
|
|
36
|
+
"@chipschat/conformance": "1.0.2",
|
|
37
|
+
"@chipschat/harness": "1.0.2",
|
|
38
|
+
"@chipschat/protocol": "1.0.2",
|
|
39
39
|
"@types/node": "22.19.0",
|
|
40
40
|
"esbuild": "0.27.7",
|
|
41
41
|
"tsx": "4.21.0",
|