@chipschat/opencode 1.0.14 → 1.0.16
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 +1 -1
- package/dist/main.js +252 -63
- package/package.json +1 -1
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,6 +1,6 @@
|
|
|
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.16 carries these packages, bundled in. Each one's
|
|
4
4
|
licence follows.
|
|
5
5
|
|
|
6
6
|
## @chipschat/harness 1.0.2 (Apache-2.0)
|
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
|
|
@@ -23398,8 +23397,8 @@ var Project = class {
|
|
|
23398
23397
|
const end2 = all.findIndex((line, at) => at > start && line.startsWith(">>>>>>> "));
|
|
23399
23398
|
if (start < 0 || end2 < 0)
|
|
23400
23399
|
return void 0;
|
|
23401
|
-
const
|
|
23402
|
-
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");
|
|
23403
23402
|
} catch {
|
|
23404
23403
|
return void 0;
|
|
23405
23404
|
}
|
|
@@ -25304,7 +25303,7 @@ async function ask(url2, options, send, machine) {
|
|
|
25304
25303
|
if (asked.status !== 201)
|
|
25305
25304
|
throw await refused(asked);
|
|
25306
25305
|
const { request_id, secret, code, interval } = await asked.json();
|
|
25307
|
-
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}.
|
|
25308
25307
|
`));
|
|
25309
25308
|
show(code, url2);
|
|
25310
25309
|
for (; ; ) {
|
|
@@ -25871,9 +25870,9 @@ import { globSync, readFileSync as readFileSync5, statSync } from "node:fs";
|
|
|
25871
25870
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
25872
25871
|
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join5, relative, sep as sep2 } from "node:path";
|
|
25873
25872
|
var AGENTS_MD = "AGENTS.md";
|
|
25874
|
-
function placesOf(env,
|
|
25875
|
-
const config3 = env["OPENCODE_CONFIG_DIR"] || join5(env["XDG_CONFIG_HOME"] || join5(
|
|
25876
|
-
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 };
|
|
25877
25876
|
}
|
|
25878
25877
|
function hashOf(text2) {
|
|
25879
25878
|
return createHash2("sha256").update(text2, "utf8").digest("hex");
|
|
@@ -25901,7 +25900,7 @@ function upTo(dir, stop2) {
|
|
|
25901
25900
|
}
|
|
25902
25901
|
return dirs;
|
|
25903
25902
|
}
|
|
25904
|
-
function namedFiles(sources, folder2, root,
|
|
25903
|
+
function namedFiles(sources, folder2, root, home) {
|
|
25905
25904
|
const found = [];
|
|
25906
25905
|
const glob = (pattern, cwd) => {
|
|
25907
25906
|
try {
|
|
@@ -25912,7 +25911,7 @@ function namedFiles(sources, folder2, root, home3) {
|
|
|
25912
25911
|
};
|
|
25913
25912
|
for (const source2 of sources) {
|
|
25914
25913
|
if (/^https?:\/\//i.test(source2)) continue;
|
|
25915
|
-
const expanded = source2.startsWith("~/") ? join5(
|
|
25914
|
+
const expanded = source2.startsWith("~/") ? join5(home, source2.slice(2)) : source2;
|
|
25916
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));
|
|
25917
25916
|
for (const path of matches.sort()) if (isFile(path) && !found.includes(path)) found.push(path);
|
|
25918
25917
|
}
|
|
@@ -25932,15 +25931,15 @@ var ContextFiles = class {
|
|
|
25932
25931
|
}
|
|
25933
25932
|
places;
|
|
25934
25933
|
folders;
|
|
25935
|
-
/** `~/
|
|
25934
|
+
/** `~/Chips/Agents/opencode/AGENTS.md`, or the whole path where it isn't in the home. */
|
|
25936
25935
|
tilde(path) {
|
|
25937
|
-
const { home
|
|
25938
|
-
return within(path,
|
|
25936
|
+
const { home } = this.places;
|
|
25937
|
+
return within(path, home) ? `~${path.slice(home.length)}` : path;
|
|
25939
25938
|
}
|
|
25940
25939
|
/** A shared file's name: where it is, a step at a time (`~ › .config › opencode › AGENTS.md`), with no slash. */
|
|
25941
25940
|
crumbs(path) {
|
|
25942
|
-
const { home
|
|
25943
|
-
const steps = within(path,
|
|
25941
|
+
const { home } = this.places;
|
|
25942
|
+
const steps = within(path, home) ? ["~", ...relative(home, path).split(sep2)] : ["/", ...path.split(sep2)];
|
|
25944
25943
|
return steps.filter((step) => step !== "").join(" \u203A ").slice(0, 255);
|
|
25945
25944
|
}
|
|
25946
25945
|
/**
|
|
@@ -26249,6 +26248,13 @@ var OpenCodeClient = class {
|
|
|
26249
26248
|
const metadata = data["metadata"];
|
|
26250
26249
|
return { id: id2, metadata: typeof metadata === "object" && metadata !== null ? metadata : {} };
|
|
26251
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
|
+
}
|
|
26252
26258
|
/** A session's title, or its metadata, which this replaces whole. */
|
|
26253
26259
|
async update(sessionId, changes) {
|
|
26254
26260
|
await this.call("PATCH", `/api/session/${encodeURIComponent(sessionId)}`, {
|
|
@@ -26720,10 +26726,10 @@ var Bridge = class {
|
|
|
26720
26726
|
}
|
|
26721
26727
|
/** The Main agent into the harness's own room, in the place of whichever of ours is there. */
|
|
26722
26728
|
async goHome(main2, known) {
|
|
26723
|
-
const { chips: chips2, home
|
|
26724
|
-
if (
|
|
26725
|
-
const
|
|
26726
|
-
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)}`));
|
|
26727
26733
|
}
|
|
26728
26734
|
/**
|
|
26729
26735
|
* Trouble while the server doesn't answer, and fine once it does (ADR 0083). Nothing while a
|
|
@@ -26838,7 +26844,7 @@ var Bridge = class {
|
|
|
26838
26844
|
* that isn't one of the harness's has no copy, nor has an ask the room stopped waiting for.
|
|
26839
26845
|
*/
|
|
26840
26846
|
async makeCopy(fork) {
|
|
26841
|
-
const { chips: chips2, opencode: opencode2, state: state2, saveState } = this.options;
|
|
26847
|
+
const { chips: chips2, opencode: opencode2, state: state2, saveState: saveState2 } = this.options;
|
|
26842
26848
|
const spaceId = fork.space_id;
|
|
26843
26849
|
if (spaceId === void 0) return;
|
|
26844
26850
|
const late = Date.now() - Date.parse(fork.ts);
|
|
@@ -26869,7 +26875,7 @@ var Bridge = class {
|
|
|
26869
26875
|
const folder2 = await this.folderFor({ who, agent });
|
|
26870
26876
|
kept = { ...copied === void 0 ? {} : { session: copied.id }, ...folder2 === void 0 ? {} : { folder: folder2 } };
|
|
26871
26877
|
state2.forks = { ...state2.forks, [fork.id]: kept };
|
|
26872
|
-
|
|
26878
|
+
saveState2(state2);
|
|
26873
26879
|
}
|
|
26874
26880
|
const copy = await chips2.agents.register({
|
|
26875
26881
|
key: `opencode:fork:${fork.id}`,
|
|
@@ -26882,7 +26888,7 @@ var Bridge = class {
|
|
|
26882
26888
|
if (kept.folder !== void 0) state2.folders = { ...state2.folders, [copy.agentId]: kept.folder };
|
|
26883
26889
|
const pick2 = state2.models?.[who];
|
|
26884
26890
|
if (pick2 !== void 0) state2.models = { ...state2.models, [copy.agentId]: pick2 };
|
|
26885
|
-
|
|
26891
|
+
saveState2(state2);
|
|
26886
26892
|
if (copied !== void 0) {
|
|
26887
26893
|
await opencode2.update(copied.id, {
|
|
26888
26894
|
title: `Chips space ${spaceId}`,
|
|
@@ -26947,7 +26953,7 @@ var Bridge = class {
|
|
|
26947
26953
|
async modelChanged(change) {
|
|
26948
26954
|
const whose = await this.whoseIs(change.agent_id);
|
|
26949
26955
|
if (whose === void 0) throw new Refuse("This harness has no such agent.");
|
|
26950
|
-
const { opencode: opencode2, state: state2, saveState } = this.options;
|
|
26956
|
+
const { opencode: opencode2, state: state2, saveState: saveState2 } = this.options;
|
|
26951
26957
|
let pick2;
|
|
26952
26958
|
try {
|
|
26953
26959
|
pick2 = pickOf(change, await opencode2.models());
|
|
@@ -26958,7 +26964,7 @@ var Bridge = class {
|
|
|
26958
26964
|
if (pick2 === null) delete models[whose.who];
|
|
26959
26965
|
else models[whose.who] = pick2;
|
|
26960
26966
|
state2.models = models;
|
|
26961
|
-
|
|
26967
|
+
saveState2(state2);
|
|
26962
26968
|
const name = await this.nameOf(whose);
|
|
26963
26969
|
this.log(`${name} runs on ${pick2 === null ? "its default" : `${pick2.model}${pick2.effort === void 0 ? "" : ` at ${pick2.effort}`}`}, from Chips`);
|
|
26964
26970
|
const copy = await this.modelCopyOf(change.agent_id);
|
|
@@ -26996,7 +27002,7 @@ var Bridge = class {
|
|
|
26996
27002
|
*/
|
|
26997
27003
|
async readingOf(folder2) {
|
|
26998
27004
|
const { opencode: opencode2 } = this.options;
|
|
26999
|
-
const { home
|
|
27005
|
+
const { home } = this.places;
|
|
27000
27006
|
let root = this.roots.get(folder2);
|
|
27001
27007
|
if (root === void 0) {
|
|
27002
27008
|
root = await opencode2.projectRoot(folder2).catch(() => folder2);
|
|
@@ -27007,7 +27013,7 @@ var Bridge = class {
|
|
|
27007
27013
|
() => false
|
|
27008
27014
|
);
|
|
27009
27015
|
const sources = await this.readsNamed ? await opencode2.instructions(folder2).catch(() => []) : [];
|
|
27010
|
-
return { stop: within(folder2,
|
|
27016
|
+
return { stop: within(folder2, home) ? home : root, named: namedFiles(sources, folder2, root, home) };
|
|
27011
27017
|
}
|
|
27012
27018
|
/** Each agent of the harness's, with the folder its sessions work in and what OpenCode reads there. */
|
|
27013
27019
|
async agentReadings() {
|
|
@@ -27353,7 +27359,7 @@ var Bridge = class {
|
|
|
27353
27359
|
}
|
|
27354
27360
|
/** The words into the room's session: the one it had, or a new one when OpenCode no longer has it. */
|
|
27355
27361
|
async give({ id: id2, who, agent, spaceId }, said, rules) {
|
|
27356
|
-
const { opencode: opencode2, state: state2, saveState } = this.options;
|
|
27362
|
+
const { opencode: opencode2, state: state2, saveState: saveState2 } = this.options;
|
|
27357
27363
|
let kept = state2.sessions[id2];
|
|
27358
27364
|
if (kept === void 0 && this.copying.size > 0) {
|
|
27359
27365
|
await Promise.allSettled(this.copying.values());
|
|
@@ -27377,7 +27383,7 @@ var Bridge = class {
|
|
|
27377
27383
|
metadata: { chips_space: spaceId, chips_agent: who }
|
|
27378
27384
|
});
|
|
27379
27385
|
state2.sessions[id2] = made;
|
|
27380
|
-
|
|
27386
|
+
saveState2(state2);
|
|
27381
27387
|
return { sessionId: made, given: await this.prompted(made, said(made), rules) };
|
|
27382
27388
|
}
|
|
27383
27389
|
/**
|
|
@@ -27385,14 +27391,14 @@ var Bridge = class {
|
|
|
27385
27391
|
* kept by whose it is, so a rename doesn't move it. None without `folders`: the project's.
|
|
27386
27392
|
*/
|
|
27387
27393
|
async folderFor({ who, agent }) {
|
|
27388
|
-
const { folders: root, state: state2, saveState } = this.options;
|
|
27394
|
+
const { folders: root, state: state2, saveState: saveState2 } = this.options;
|
|
27389
27395
|
if (root === void 0) return void 0;
|
|
27390
27396
|
let folder2 = state2.folders?.[who];
|
|
27391
27397
|
if (folder2 === void 0) {
|
|
27392
27398
|
const name = await this.nameOf({ who, agent });
|
|
27393
27399
|
folder2 = freeFolder(root, name, Object.values(state2.folders ?? {}));
|
|
27394
27400
|
state2.folders = { ...state2.folders, [who]: folder2 };
|
|
27395
|
-
|
|
27401
|
+
saveState2(state2);
|
|
27396
27402
|
}
|
|
27397
27403
|
mkdirSync4(folder2, { recursive: true });
|
|
27398
27404
|
return folder2;
|
|
@@ -28044,12 +28050,60 @@ function lastLine(said) {
|
|
|
28044
28050
|
}
|
|
28045
28051
|
|
|
28046
28052
|
// src/keep.ts
|
|
28047
|
-
import {
|
|
28048
|
-
import { basename as basename3, join as join7, resolve as resolve2 } from "node:path";
|
|
28053
|
+
import { basename as basename3 } from "node:path";
|
|
28049
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
|
+
const whole2 = resolve2(fromHome(path, home));
|
|
28096
|
+
const under = inside2(places.agents, whole2);
|
|
28097
|
+
const old = under === void 0 ? void 0 : join7(places.oldAgents, under);
|
|
28098
|
+
if (old !== void 0 && existsSync5(old) && !existsSync5(whole2)) return old;
|
|
28099
|
+
return nowAt(whole2, places.oldAgents, places.agents);
|
|
28100
|
+
}
|
|
28101
|
+
|
|
28102
|
+
// src/keep.ts
|
|
28050
28103
|
var flags = process.argv.slice(2);
|
|
28051
28104
|
var words3 = flags.filter((word) => !word.startsWith("--"));
|
|
28052
|
-
var
|
|
28105
|
+
var handsOver = flags.includes("--keep");
|
|
28106
|
+
var whole = (word) => configTargetOf(word);
|
|
28053
28107
|
var here = fileURLToPath2(new URL("..", import.meta.url)).replace(/\/$/, "");
|
|
28054
28108
|
var self = fileURLToPath2(import.meta.url);
|
|
28055
28109
|
var run = self.endsWith(".js") ? [self] : ["--import", "tsx", "src/main.ts"];
|
|
@@ -28094,12 +28148,147 @@ function keepIfAsked(label2, url2, names, log2, before2 = () => void 0) {
|
|
|
28094
28148
|
);
|
|
28095
28149
|
}
|
|
28096
28150
|
|
|
28151
|
+
// src/move.ts
|
|
28152
|
+
import { lstatSync, mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync6, renameSync as renameSync2, rmdirSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
28153
|
+
import { homedir as homedir6 } from "node:os";
|
|
28154
|
+
import { dirname as dirname5, join as join8, resolve as resolve3 } from "node:path";
|
|
28155
|
+
var there = (path) => {
|
|
28156
|
+
try {
|
|
28157
|
+
lstatSync(path);
|
|
28158
|
+
return true;
|
|
28159
|
+
} catch {
|
|
28160
|
+
return false;
|
|
28161
|
+
}
|
|
28162
|
+
};
|
|
28163
|
+
var isFolder = (path) => lstatSync(path).isDirectory();
|
|
28164
|
+
var shown = (path, home) => path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
|
|
28165
|
+
function merge2(from, to, log2, home = homedir6(), quiet = false) {
|
|
28166
|
+
if (!there(from)) return;
|
|
28167
|
+
try {
|
|
28168
|
+
if (!there(to)) {
|
|
28169
|
+
mkdirSync5(dirname5(to), { recursive: true });
|
|
28170
|
+
renameSync2(from, to);
|
|
28171
|
+
if (!quiet) log2(`moved ${shown(from, home)} to ${shown(to, home)}`);
|
|
28172
|
+
return;
|
|
28173
|
+
}
|
|
28174
|
+
if (!isFolder(from) || !isFolder(to)) {
|
|
28175
|
+
log2(`${shown(from, home)} was left where it is: ${shown(to, home)} is there already`);
|
|
28176
|
+
return;
|
|
28177
|
+
}
|
|
28178
|
+
for (const entry of readdirSync(from)) merge2(join8(from, entry), join8(to, entry), log2, home, true);
|
|
28179
|
+
if (readdirSync(from).length === 0) rmdirSync(from);
|
|
28180
|
+
if (!quiet) log2(`moved what was in ${shown(from, home)} into ${shown(to, home)}`);
|
|
28181
|
+
} catch (error62) {
|
|
28182
|
+
log2(`${shown(from, home)} couldn't be moved to ${shown(to, home)}, so it is used where it is: ${error62.message}`);
|
|
28183
|
+
}
|
|
28184
|
+
}
|
|
28185
|
+
function removeIfEmpty(path) {
|
|
28186
|
+
try {
|
|
28187
|
+
if (readdirSync(path).length === 0) rmdirSync(path);
|
|
28188
|
+
} catch {
|
|
28189
|
+
}
|
|
28190
|
+
}
|
|
28191
|
+
function pairOf(path, places) {
|
|
28192
|
+
const old = inside2(places.oldAgents, path);
|
|
28193
|
+
if (old !== void 0) return { old: path, now: join8(places.agents, old) };
|
|
28194
|
+
const now = inside2(places.agents, path);
|
|
28195
|
+
return now === void 0 ? void 0 : { old: join8(places.oldAgents, now), now: path };
|
|
28196
|
+
}
|
|
28197
|
+
function writeJson(path, value) {
|
|
28198
|
+
const next = `${path}.next`;
|
|
28199
|
+
writeFileSync4(next, `${JSON.stringify(value, null, 2)}
|
|
28200
|
+
`, { mode: 384 });
|
|
28201
|
+
renameSync2(next, path);
|
|
28202
|
+
}
|
|
28203
|
+
var readJson = (path) => {
|
|
28204
|
+
try {
|
|
28205
|
+
const read = JSON.parse(readFileSync6(path, "utf8"));
|
|
28206
|
+
return typeof read === "object" && read !== null ? read : void 0;
|
|
28207
|
+
} catch {
|
|
28208
|
+
return void 0;
|
|
28209
|
+
}
|
|
28210
|
+
};
|
|
28211
|
+
function moveOld(configPath2, log2, home = homedir6()) {
|
|
28212
|
+
const places = placesOf2(home);
|
|
28213
|
+
const settings = dirname5(configPath2);
|
|
28214
|
+
const under = inside2(places.settings, settings);
|
|
28215
|
+
if (under !== void 0) {
|
|
28216
|
+
mkdirSync5(dirname5(dirname5(places.settings)), { recursive: true });
|
|
28217
|
+
mkdirSync5(dirname5(places.settings), { recursive: true, mode: 448 });
|
|
28218
|
+
merge2(join8(places.oldSettings, under), settings, log2, home);
|
|
28219
|
+
}
|
|
28220
|
+
const config3 = readJson(configPath2);
|
|
28221
|
+
const said = config3?.["folders"];
|
|
28222
|
+
const root = typeof said === "string" && said !== "" ? resolve3(fromHome(said, home)) : places.agents;
|
|
28223
|
+
const pair = pairOf(root, places);
|
|
28224
|
+
if (pair !== void 0) merge2(pair.old, pair.now, log2, home);
|
|
28225
|
+
if (config3 !== void 0 && typeof said === "string") {
|
|
28226
|
+
const moved = nowAt(root, places.oldAgents, places.agents);
|
|
28227
|
+
if (moved !== root) writeJson(configPath2, { ...config3, folders: `~/${moved.slice(home.length + 1)}` });
|
|
28228
|
+
}
|
|
28229
|
+
const statePath2 = join8(settings, "sessions.json");
|
|
28230
|
+
const state2 = readJson(statePath2);
|
|
28231
|
+
if (state2 === void 0) return;
|
|
28232
|
+
const moving = { ...state2.moving };
|
|
28233
|
+
let changed = false;
|
|
28234
|
+
const folders = { ...state2.folders };
|
|
28235
|
+
for (const [who, folder2] of Object.entries(folders)) {
|
|
28236
|
+
const moved = nowAt(folder2, places.oldAgents, places.agents);
|
|
28237
|
+
if (moved === folder2) continue;
|
|
28238
|
+
folders[who] = moved;
|
|
28239
|
+
for (const [room, session] of Object.entries(state2.sessions ?? {})) {
|
|
28240
|
+
if (room.slice(0, room.lastIndexOf("|")) === who) moving[session] = moved;
|
|
28241
|
+
}
|
|
28242
|
+
changed = true;
|
|
28243
|
+
}
|
|
28244
|
+
const forks = { ...state2.forks };
|
|
28245
|
+
for (const [ask2, fork] of Object.entries(forks)) {
|
|
28246
|
+
if (fork.folder === void 0) continue;
|
|
28247
|
+
const moved = nowAt(fork.folder, places.oldAgents, places.agents);
|
|
28248
|
+
if (moved === fork.folder) continue;
|
|
28249
|
+
forks[ask2] = { ...fork, folder: moved };
|
|
28250
|
+
if (fork.session !== void 0) moving[fork.session] = moved;
|
|
28251
|
+
changed = true;
|
|
28252
|
+
}
|
|
28253
|
+
if (changed) {
|
|
28254
|
+
writeJson(statePath2, {
|
|
28255
|
+
...state2,
|
|
28256
|
+
folders,
|
|
28257
|
+
...state2.forks === void 0 ? {} : { forks },
|
|
28258
|
+
...Object.keys(moving).length === 0 ? {} : { moving }
|
|
28259
|
+
});
|
|
28260
|
+
}
|
|
28261
|
+
removeIfEmpty(places.oldSettings);
|
|
28262
|
+
removeIfEmpty(places.oldAgents);
|
|
28263
|
+
removeIfEmpty(dirname5(places.oldAgents));
|
|
28264
|
+
}
|
|
28265
|
+
async function moveSessions(state2, opencode2, save, log2, home = homedir6()) {
|
|
28266
|
+
const moving = state2.moving;
|
|
28267
|
+
if (moving === void 0) return;
|
|
28268
|
+
for (const [session, folder2] of Object.entries(moving)) {
|
|
28269
|
+
try {
|
|
28270
|
+
await opencode2.moveSession(session, folder2);
|
|
28271
|
+
log2(`moved OpenCode's session ${session} to ${shown(folder2, home)}`);
|
|
28272
|
+
delete moving[session];
|
|
28273
|
+
} catch (error62) {
|
|
28274
|
+
if (!(error62 instanceof OpenCodeError)) {
|
|
28275
|
+
log2(`OpenCode's session ${session} wasn't moved to ${shown(folder2, home)}, for now: ${String(error62)}`);
|
|
28276
|
+
continue;
|
|
28277
|
+
}
|
|
28278
|
+
log2(`OpenCode wouldn't move its session ${session} to ${shown(folder2, home)}: ${error62.message}`);
|
|
28279
|
+
delete moving[session];
|
|
28280
|
+
}
|
|
28281
|
+
}
|
|
28282
|
+
if (Object.keys(moving).length === 0) delete state2.moving;
|
|
28283
|
+
save(state2);
|
|
28284
|
+
}
|
|
28285
|
+
|
|
28097
28286
|
// src/server.ts
|
|
28098
28287
|
import { spawn } from "node:child_process";
|
|
28099
28288
|
import { randomBytes } from "node:crypto";
|
|
28100
|
-
import { accessSync as accessSync2, constants as constants2, existsSync as
|
|
28289
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
28101
28290
|
import { createServer } from "node:net";
|
|
28102
|
-
import { delimiter as delimiter2, join as
|
|
28291
|
+
import { delimiter as delimiter2, join as join9 } from "node:path";
|
|
28103
28292
|
import { createInterface } from "node:readline";
|
|
28104
28293
|
var ServerError = class extends Error {
|
|
28105
28294
|
constructor(message, missing = false) {
|
|
@@ -28110,10 +28299,10 @@ var ServerError = class extends Error {
|
|
|
28110
28299
|
name = "ServerError";
|
|
28111
28300
|
};
|
|
28112
28301
|
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
28113
|
-
var onPath = (command, env) => command.includes("/") ?
|
|
28302
|
+
var onPath = (command, env) => command.includes("/") ? existsSync6(command) : (env["PATH"] ?? "").split(delimiter2).some((folder2) => {
|
|
28114
28303
|
if (folder2 === "") return false;
|
|
28115
28304
|
try {
|
|
28116
|
-
accessSync2(
|
|
28305
|
+
accessSync2(join9(folder2, command), constants2.X_OK);
|
|
28117
28306
|
return true;
|
|
28118
28307
|
} catch {
|
|
28119
28308
|
return false;
|
|
@@ -28147,9 +28336,9 @@ async function answers2(url2, password) {
|
|
|
28147
28336
|
return typeof info === "string" ? info : "ok";
|
|
28148
28337
|
}
|
|
28149
28338
|
function keptIn(path) {
|
|
28150
|
-
if (!
|
|
28339
|
+
if (!existsSync6(path)) return void 0;
|
|
28151
28340
|
try {
|
|
28152
|
-
const kept = JSON.parse(
|
|
28341
|
+
const kept = JSON.parse(readFileSync7(path, "utf8"));
|
|
28153
28342
|
return typeof kept.port === "number" && typeof kept.password === "string" ? kept : void 0;
|
|
28154
28343
|
} catch {
|
|
28155
28344
|
return void 0;
|
|
@@ -28178,7 +28367,7 @@ async function ownServer(options) {
|
|
|
28178
28367
|
const env = options.env ?? process.env;
|
|
28179
28368
|
const command = options.command ?? "opencode";
|
|
28180
28369
|
const log2 = options.log ?? (() => void 0);
|
|
28181
|
-
const path =
|
|
28370
|
+
const path = join9(options.folder, "server.json");
|
|
28182
28371
|
let kept = keptIn(path);
|
|
28183
28372
|
let before2 = false;
|
|
28184
28373
|
if (kept !== void 0) {
|
|
@@ -28200,7 +28389,7 @@ async function ownServer(options) {
|
|
|
28200
28389
|
let again2;
|
|
28201
28390
|
const said = [];
|
|
28202
28391
|
const start = () => {
|
|
28203
|
-
|
|
28392
|
+
writeFileSync5(path, `${JSON.stringify({ port, password }, null, 2)}
|
|
28204
28393
|
`, { mode: 384 });
|
|
28205
28394
|
const started = spawn(command, ["serve", "--hostname", "127.0.0.1", "--port", String(port)], {
|
|
28206
28395
|
cwd: options.cwd,
|
|
@@ -28302,9 +28491,8 @@ async function ownServer(options) {
|
|
|
28302
28491
|
}
|
|
28303
28492
|
|
|
28304
28493
|
// src/setup.ts
|
|
28305
|
-
import { existsSync as
|
|
28306
|
-
import {
|
|
28307
|
-
import { join as join9, resolve as resolve3 } from "node:path";
|
|
28494
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
|
|
28495
|
+
import { resolve as resolve4 } from "node:path";
|
|
28308
28496
|
var SetupError = class extends Error {
|
|
28309
28497
|
constructor(message, words4 = {}) {
|
|
28310
28498
|
super(message);
|
|
@@ -28321,9 +28509,8 @@ var hostOf = (url2) => {
|
|
|
28321
28509
|
}
|
|
28322
28510
|
};
|
|
28323
28511
|
var STILL_GOOD = "The link is still good: run the same command again once that's done.";
|
|
28324
|
-
var home = (path) => path.startsWith("~/") ? join9(homedir6(), path.slice(2)) : path;
|
|
28325
28512
|
function readConfig(path, env = process.env) {
|
|
28326
|
-
const raw =
|
|
28513
|
+
const raw = existsSync7(path) ? JSON.parse(readFileSync8(path, "utf8")) : {};
|
|
28327
28514
|
const passwordEnv = typeof raw["passwordEnv"] === "string" ? raw["passwordEnv"] : "OPENCODE_PASSWORD";
|
|
28328
28515
|
const password = filledIn(env[passwordEnv]) ? env[passwordEnv] : void 0;
|
|
28329
28516
|
const ownServer2 = password === void 0 && typeof raw["url"] !== "string";
|
|
@@ -28350,8 +28537,8 @@ function readConfig(path, env = process.env) {
|
|
|
28350
28537
|
password,
|
|
28351
28538
|
passwordEnv,
|
|
28352
28539
|
ownServer: ownServer2,
|
|
28353
|
-
directory: typeof raw["directory"] === "string" ?
|
|
28354
|
-
folders:
|
|
28540
|
+
directory: typeof raw["directory"] === "string" ? resolve4(fromHome(raw["directory"])) : void 0,
|
|
28541
|
+
folders: foldersPathOf(typeof raw["folders"] === "string" ? raw["folders"] : placesOf2().agents),
|
|
28355
28542
|
agents
|
|
28356
28543
|
};
|
|
28357
28544
|
}
|
|
@@ -28379,17 +28566,17 @@ async function checkOpenCode(config3, client) {
|
|
|
28379
28566
|
}
|
|
28380
28567
|
|
|
28381
28568
|
// src/version.ts
|
|
28382
|
-
import { readFileSync as
|
|
28383
|
-
var VERSION = JSON.parse(
|
|
28569
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
28570
|
+
var VERSION = JSON.parse(readFileSync9(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
28384
28571
|
|
|
28385
28572
|
// src/main.ts
|
|
28386
|
-
var home2 = (path) => path.startsWith("~/") ? join10(homedir7(), path.slice(2)) : path;
|
|
28387
28573
|
var link = process.env["CHIPS_JOIN_LINK"] || void 0;
|
|
28388
28574
|
var LABEL = "chat.chips.opencode";
|
|
28389
28575
|
stopIfAsked(LABEL);
|
|
28390
|
-
var configPath = resolve4(home2(words3[0] ?? "~/.chips-opencode/config.json"));
|
|
28391
28576
|
var log = (line) => process.stdout.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
28392
28577
|
`);
|
|
28578
|
+
if (!handsOver) moveOld(configTargetOf(words3[0]), log);
|
|
28579
|
+
var configPath = configPathOf(words3[0]);
|
|
28393
28580
|
var stop = (error62) => {
|
|
28394
28581
|
if (!(error62 instanceof SetupError)) throw error62;
|
|
28395
28582
|
reportStep("failed", error62.words);
|
|
@@ -28403,14 +28590,14 @@ try {
|
|
|
28403
28590
|
} catch (error62) {
|
|
28404
28591
|
config2 = stop(error62);
|
|
28405
28592
|
}
|
|
28406
|
-
var folder =
|
|
28407
|
-
|
|
28593
|
+
var folder = dirname6(configPath);
|
|
28594
|
+
mkdirSync6(folder, { recursive: true, mode: 448 });
|
|
28408
28595
|
var statePath = join10(folder, "sessions.json");
|
|
28409
28596
|
var server;
|
|
28410
28597
|
if (link !== void 0) reportStep("checking", { detail: "Looking for OpenCode" });
|
|
28411
28598
|
if (config2.ownServer) {
|
|
28412
|
-
const cwd = config2.directory ??
|
|
28413
|
-
|
|
28599
|
+
const cwd = config2.directory ?? config2.folders;
|
|
28600
|
+
mkdirSync6(cwd, { recursive: true });
|
|
28414
28601
|
try {
|
|
28415
28602
|
server = await ownServer({ folder, cwd, log });
|
|
28416
28603
|
} catch (error62) {
|
|
@@ -28454,7 +28641,10 @@ var joined = await join3({
|
|
|
28454
28641
|
onCode: (code) => log(`Allow this harness in Chips: ${code}`)
|
|
28455
28642
|
}).catch(refused2);
|
|
28456
28643
|
keepIfAsked(LABEL, joined.url, [config2.passwordEnv], log, () => server?.stop());
|
|
28457
|
-
var state =
|
|
28644
|
+
var state = existsSync8(statePath) ? JSON.parse(readFileSync10(statePath, "utf8")) : { sessions: {} };
|
|
28645
|
+
var saveState = (next) => writeFileSync6(statePath, `${JSON.stringify(next, null, 2)}
|
|
28646
|
+
`, { mode: 384 });
|
|
28647
|
+
if (offered !== void 0) await moveSessions(state, opencode, saveState, log);
|
|
28458
28648
|
var bridge;
|
|
28459
28649
|
var chips = await connect({
|
|
28460
28650
|
url: joined.url,
|
|
@@ -28476,8 +28666,7 @@ bridge = new Bridge({
|
|
|
28476
28666
|
agents: config2.agents,
|
|
28477
28667
|
home: joined.spaceId,
|
|
28478
28668
|
state,
|
|
28479
|
-
saveState
|
|
28480
|
-
`, { mode: 384 }),
|
|
28669
|
+
saveState,
|
|
28481
28670
|
log,
|
|
28482
28671
|
restartServer: server?.restart,
|
|
28483
28672
|
...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.16",
|
|
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": {
|