cortad 0.3.1-rc.5 → 0.3.1-rc.6
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/lib/cli.mjs +1 -1
- package/lib/verbs.mjs +1 -1
- package/lib/writes.mjs +10 -6
- package/local.mjs +4 -0
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -36,7 +36,7 @@ export async function main(argv, { root = process.cwd(), env = process.env, stdo
|
|
|
36
36
|
const children = [];
|
|
37
37
|
const pending = { read: () => readPending(project), write: (p) => writePending(project, p) };
|
|
38
38
|
// The key is read at every call: the MCP process outlives a connect that writes it after the process started.
|
|
39
|
-
const writes = { mark: (id) => markRun(writesDirOf(homeOf(project)), id), putBack: (
|
|
39
|
+
const writes = { mark: (id) => markRun(writesDirOf(homeOf(project)), id), putBack: () => putBack(writesDirOf(homeOf(project)), root) };
|
|
40
40
|
const verbs = makeVerbs({
|
|
41
41
|
api: `${origin.origin}/api`,
|
|
42
42
|
root,
|
package/lib/verbs.mjs
CHANGED
|
@@ -114,7 +114,7 @@ export function makeVerbs({ api, token, fetchImpl = fetch, startApp, pending, ro
|
|
|
114
114
|
}
|
|
115
115
|
// The run is over: what the app wrote into its folder during it is put back, said before the next step.
|
|
116
116
|
let back = "";
|
|
117
|
-
if (writes && finished(res.data)) { try { back = writes.putBack(
|
|
117
|
+
if (writes && finished(res.data)) { try { back = writes.putBack(); } catch { back = ""; } }
|
|
118
118
|
return { text: back ? withBack(runText(res.data), back) : runText(res.data), data: res.data };
|
|
119
119
|
};
|
|
120
120
|
|
package/lib/writes.mjs
CHANGED
|
@@ -8,19 +8,23 @@ import { dirname, join, resolve, sep } from "node:path";
|
|
|
8
8
|
|
|
9
9
|
export const writesDirOf = (home) => join(home, "writes");
|
|
10
10
|
|
|
11
|
-
// A run has started: from now until it ends, the hook records what the app writes.
|
|
12
|
-
|
|
11
|
+
// A run has started: from now until it ends, the hook records what the app writes. A record that
|
|
12
|
+
// is already open is kept when `keep` is set: an app started again mid-run keeps the copies made
|
|
13
|
+
// before it went down.
|
|
14
|
+
export function markRun(dir, jobId, { keep = false } = {}) {
|
|
15
|
+
if (keep && existsSync(join(dir, "run"))) return;
|
|
13
16
|
try { rmSync(dir, { recursive: true, force: true }); } catch { /* fresh */ }
|
|
14
17
|
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
15
18
|
writeFileSync(join(dir, "run"), String(jobId), { mode: 0o600 });
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
// The run has ended: every file
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
+
// The run has ended: every file written since the record began is put back, and the line says
|
|
22
|
+
// which. The record begins when the command starts the app (the first run starts itself on the
|
|
23
|
+
// server's side, so no verb marks it) and again at every run a verb starts, so what a person wrote
|
|
24
|
+
// between two runs is dropped from the record rather than undone.
|
|
25
|
+
export function putBack(dir, root) {
|
|
21
26
|
let rows = [];
|
|
22
27
|
try {
|
|
23
|
-
if (readFileSync(join(dir, "run"), "utf8").trim() !== String(jobId)) return "";
|
|
24
28
|
rows = readFileSync(join(dir, "written.jsonl"), "utf8").split("\n").filter(Boolean)
|
|
25
29
|
.map((line) => { try { return JSON.parse(line); } catch { return null; } }).filter(Boolean);
|
|
26
30
|
} catch { /* the run wrote nothing */ }
|
package/local.mjs
CHANGED
|
@@ -33,6 +33,7 @@ import { listingUrl } from "./lib/listing.mjs";
|
|
|
33
33
|
import { installPlan, missingDependency, startPlan, workspaces } from "./lib/start.mjs";
|
|
34
34
|
import { openSwitches } from "./lib/switches.mjs";
|
|
35
35
|
import { keepData } from "./lib/data.mjs";
|
|
36
|
+
import { markRun, writesDirOf } from "./lib/writes.mjs";
|
|
36
37
|
|
|
37
38
|
const argv = process.argv.slice(2);
|
|
38
39
|
// The two faces a coding agent uses after the first connect (lib/cli.mjs): the MCP server the
|
|
@@ -768,6 +769,9 @@ function dataOnce(started) {
|
|
|
768
769
|
|
|
769
770
|
async function start(waitMs) {
|
|
770
771
|
const { cmd, lifted } = await freed(launched.cmd, launched.lifted ?? {});
|
|
772
|
+
// The record of what the app writes opens with the app: the first run starts itself on the
|
|
773
|
+
// server's side, with no verb here to mark it, and a restart mid-run keeps the record open.
|
|
774
|
+
try { markRun(writesDirOf(homeOf(projectOf(root))), "session", { keep: true }); } catch { /* the app's writes go unrecorded */ }
|
|
771
775
|
child = spawn("/bin/sh", ["-c", cmd], { cwd: appDir, env: { ...envExports(envFiles), ...process.env, ...lifted, ...dataOnce(true).env, ...(capture ? capture.env(process.env) : {}), FORCE_COLOR: "0", ...(pinned.bin ? { PATH: `${pinned.bin}:${process.env.PATH ?? ""}` } : {}) }, stdio: ["ignore", "pipe", "pipe"], detached: true });
|
|
772
776
|
const mine = child;
|
|
773
777
|
let seen = "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortad",
|
|
3
|
-
"version": "0.3.1-rc.
|
|
3
|
+
"version": "0.3.1-rc.6",
|
|
4
4
|
"description": "Connects the AI app on your machine to Cortad for test conversations, and gives your coding agent the MCP and skill to run them. No dependencies.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cortad": "local.mjs"
|