@mahekara28/context-relay 0.0.0-stage → 0.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +128 -2
- package/README.python.md +246 -0
- package/dist/adapters/capture.d.ts +4 -0
- package/dist/adapters/capture.js +286 -0
- package/dist/adapters/capture.js.map +1 -0
- package/dist/adapters/config.d.ts +11 -0
- package/dist/adapters/config.js +139 -0
- package/dist/adapters/config.js.map +1 -0
- package/dist/adapters/manager.d.ts +57 -0
- package/dist/adapters/manager.js +220 -0
- package/dist/adapters/manager.js.map +1 -0
- package/dist/adapters/normalize.d.ts +9 -0
- package/dist/adapters/normalize.js +166 -0
- package/dist/adapters/normalize.js.map +1 -0
- package/dist/adapters/rpc.d.ts +12 -0
- package/dist/adapters/rpc.js +100 -0
- package/dist/adapters/rpc.js.map +1 -0
- package/dist/adapters/types.d.ts +59 -0
- package/dist/adapters/types.js +51 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +427 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +11 -0
- package/dist/client.js +136 -0
- package/dist/client.js.map +1 -0
- package/dist/delivery.d.ts +9 -0
- package/dist/delivery.js +35 -0
- package/dist/delivery.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.js +109 -0
- package/dist/mcp.js.map +1 -0
- package/dist/model.d.ts +169 -0
- package/dist/model.js +79 -0
- package/dist/model.js.map +1 -0
- package/dist/proposals.d.ts +65 -0
- package/dist/proposals.js +134 -0
- package/dist/proposals.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.js +375 -0
- package/dist/server.js.map +1 -0
- package/dist/store.d.ts +33 -0
- package/dist/store.js +393 -0
- package/dist/store.js.map +1 -0
- package/dist/ui/app.js +845 -0
- package/dist/ui/assets/THIRD_PARTY_LICENSE.txt +25 -0
- package/dist/ui/assets/claude.svg +1 -0
- package/dist/ui/assets/codex.svg +1 -0
- package/dist/ui/assets/goose.svg +1 -0
- package/dist/ui/assets/opencode.svg +1 -0
- package/dist/ui/assets/relay.svg +1 -0
- package/dist/ui/index.html +23 -0
- package/dist/ui/style.css +989 -0
- package/dist/util.d.ts +16 -0
- package/dist/util.js +86 -0
- package/dist/util.js.map +1 -0
- package/docs/HARNESS_CONNECTIONS.md +53 -0
- package/docs/NPM_QUICKSTART.md +150 -0
- package/docs/WORKSPACE.md +61 -0
- package/docs/releases/NPM_PUBLISHING.md +60 -0
- package/package.json +78 -4
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Artifact } from "./model.js";
|
|
2
|
+
export declare const id: () => `${string}-${string}-${string}-${string}-${string}`;
|
|
3
|
+
export declare const now: () => string;
|
|
4
|
+
export declare function canonical(value: unknown): string;
|
|
5
|
+
export declare function digest(value: unknown): string;
|
|
6
|
+
export declare function dataDirectory(explicit?: string): string;
|
|
7
|
+
export declare function privateDirectory(path: string): void;
|
|
8
|
+
export declare function realDirectory(path: string): string;
|
|
9
|
+
export declare function projectFile(root: string, file: string): string;
|
|
10
|
+
export declare function artifact(root: string, file: string): Artifact;
|
|
11
|
+
export declare function gitState(root: string): {
|
|
12
|
+
head: string | null;
|
|
13
|
+
branch: string | null;
|
|
14
|
+
};
|
|
15
|
+
/** Best-effort redaction before persistence. Does not guarantee removal of every secret. */
|
|
16
|
+
export declare function redact(text: string): string;
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, statSync, } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { RelayError } from "./model.js";
|
|
7
|
+
export const id = () => randomUUID();
|
|
8
|
+
export const now = () => new Date().toISOString();
|
|
9
|
+
export function canonical(value) {
|
|
10
|
+
if (Array.isArray(value))
|
|
11
|
+
return "[" + value.map(canonical).join(",") + "]";
|
|
12
|
+
if (value !== null && typeof value === "object")
|
|
13
|
+
return ("{" +
|
|
14
|
+
Object.entries(value)
|
|
15
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
16
|
+
.map(([k, v]) => JSON.stringify(k) + ":" + canonical(v))
|
|
17
|
+
.join(",") +
|
|
18
|
+
"}");
|
|
19
|
+
return JSON.stringify(value);
|
|
20
|
+
}
|
|
21
|
+
export function digest(value) {
|
|
22
|
+
return ("sha256:" + createHash("sha256").update(canonical(value)).digest("hex"));
|
|
23
|
+
}
|
|
24
|
+
export function dataDirectory(explicit) {
|
|
25
|
+
return resolve(explicit ??
|
|
26
|
+
process.env.CONTEXT_RELAY_HOME ??
|
|
27
|
+
join(homedir(), ".context-relay"));
|
|
28
|
+
}
|
|
29
|
+
export function privateDirectory(path) {
|
|
30
|
+
if (existsSync(path) &&
|
|
31
|
+
(lstatSync(path).isSymbolicLink() || !statSync(path).isDirectory()))
|
|
32
|
+
throw new RelayError("unsafe_path", "Data directory must be a real directory.");
|
|
33
|
+
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
34
|
+
}
|
|
35
|
+
export function realDirectory(path) {
|
|
36
|
+
const root = realpathSync(resolve(path));
|
|
37
|
+
if (!statSync(root).isDirectory())
|
|
38
|
+
throw new RelayError("invalid_project", "Project root must be a directory.");
|
|
39
|
+
return root;
|
|
40
|
+
}
|
|
41
|
+
export function projectFile(root, file) {
|
|
42
|
+
if (isAbsolute(file))
|
|
43
|
+
throw new RelayError("outside_project", "Use a project-relative file path.");
|
|
44
|
+
const target = realpathSync(resolve(root, file));
|
|
45
|
+
const rel = relative(root, target);
|
|
46
|
+
if (rel === ".." || rel.startsWith(".." + sep) || isAbsolute(rel))
|
|
47
|
+
throw new RelayError("outside_project", "File escapes the selected project.");
|
|
48
|
+
return target;
|
|
49
|
+
}
|
|
50
|
+
export function artifact(root, file) {
|
|
51
|
+
const target = projectFile(root, file);
|
|
52
|
+
const info = statSync(target);
|
|
53
|
+
if (!info.isFile() || info.size > 10 * 1024 * 1024)
|
|
54
|
+
throw new RelayError("invalid_artifact", "Artifacts must be files no larger than 10 MiB.");
|
|
55
|
+
const content = readFileSync(target);
|
|
56
|
+
return {
|
|
57
|
+
path: relative(root, target).split(sep).join("/"),
|
|
58
|
+
sha256: "sha256:" + createHash("sha256").update(content).digest("hex"),
|
|
59
|
+
bytes: content.length,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function gitState(root) {
|
|
63
|
+
const run = (args) => {
|
|
64
|
+
try {
|
|
65
|
+
return (execFileSync("git", ["-C", root, ...args], {
|
|
66
|
+
encoding: "utf8",
|
|
67
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
68
|
+
timeout: 3000,
|
|
69
|
+
}).trim() || null);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
head: run(["rev-parse", "--verify", "HEAD"]),
|
|
77
|
+
branch: run(["branch", "--show-current"]),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** Best-effort redaction before persistence. Does not guarantee removal of every secret. */
|
|
81
|
+
export function redact(text) {
|
|
82
|
+
return text
|
|
83
|
+
.replace(/\b(?:ghp_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-[A-Za-z0-9_-]{16,})\b/g, "[REDACTED]")
|
|
84
|
+
.replace(/(\b(?:authorization\s*:\s*bearer|api[_-]?key\s*[:=]|token\s*[:=]|password\s*[:=])\s*)["']?[^\s"',;]+/gi, "$1[REDACTED]");
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../packages/relay/src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAiB,MAAM,YAAY,CAAC;AAEvD,MAAM,CAAC,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC;AACrC,MAAM,CAAC,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClD,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IAC5E,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAC7C,OAAO,CACL,GAAG;YACH,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;iBAClB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;iBAChD,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;iBACvD,IAAI,CAAC,GAAG,CAAC;YACZ,GAAG,CACJ,CAAC;IACJ,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AACD,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,OAAO,CACL,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACxE,CAAC;AACJ,CAAC;AACD,MAAM,UAAU,aAAa,CAAC,QAAiB;IAC7C,OAAO,OAAO,CACZ,QAAQ;QACN,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAC9B,IAAI,CAAC,OAAO,EAAE,EAAE,gBAAgB,CAAC,CACpC,CAAC;AACJ,CAAC;AACD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IACE,UAAU,CAAC,IAAI,CAAC;QAChB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAEnE,MAAM,IAAI,UAAU,CAClB,aAAa,EACb,0CAA0C,CAC3C,CAAC;IACJ,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACpD,CAAC;AACD,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACzC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE;QAC/B,MAAM,IAAI,UAAU,CAClB,iBAAiB,EACjB,mCAAmC,CACpC,CAAC;IACJ,OAAO,IAAI,CAAC;AACd,CAAC;AACD,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,IAAY;IACpD,IAAI,UAAU,CAAC,IAAI,CAAC;QAClB,MAAM,IAAI,UAAU,CAClB,iBAAiB,EACjB,mCAAmC,CACpC,CAAC;IACJ,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC;QAC/D,MAAM,IAAI,UAAU,CAClB,iBAAiB,EACjB,oCAAoC,CACrC,CAAC;IACJ,OAAO,MAAM,CAAC;AAChB,CAAC;AACD,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,IAAY;IACjD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI;QAChD,MAAM,IAAI,UAAU,CAClB,kBAAkB,EAClB,gDAAgD,CACjD,CAAC;IACJ,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACjD,MAAM,EAAE,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QACtE,KAAK,EAAE,OAAO,CAAC,MAAM;KACtB,CAAC;AACJ,CAAC;AACD,MAAM,UAAU,QAAQ,CAAC,IAAY;IAInC,MAAM,GAAG,GAAG,CAAC,IAAc,EAAE,EAAE;QAC7B,IAAI,CAAC;YACH,OAAO,CACL,YAAY,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE;gBACzC,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;gBACnC,OAAO,EAAE,IAAI;aACd,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAClB,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IACF,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QAC5C,MAAM,EAAE,GAAG,CAAC,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;KAC1C,CAAC;AACJ,CAAC;AACD,4FAA4F;AAC5F,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,OAAO,IAAI;SACR,OAAO,CACN,kFAAkF,EAClF,YAAY,CACb;SACA,OAAO,CACN,wGAAwG,EACxG,cAAc,CACf,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Harness connections — npm release 0.2
|
|
2
|
+
|
|
3
|
+
Relay has two independent integration paths: **capture** reads native session history; **MCP** lets a destination agent inspect and accept a reviewed handoff. Installing an MCP server does not expose a harness's chat history.
|
|
4
|
+
|
|
5
|
+
| Harness | Capture path | MCP setup | Continuation |
|
|
6
|
+
| --- | --- | --- | --- |
|
|
7
|
+
| Claude Code | Project-filtered JSONL under `~/.claude/projects` | Project `.mcp.json` | Open a new chat and ask it to inspect the offered handoff |
|
|
8
|
+
| Codex | Read-only app-server JSON-RPC; paginated completed turns | Project `.codex/config.toml` | Open a trusted project and a new chat; request the handoff |
|
|
9
|
+
| OpenCode | Loopback session/messages HTTP API | Project `opencode.json` | Start a new session and request the handoff |
|
|
10
|
+
| Goose | `session list` and `session export --format json` | `.relay/goose-mcp.json` command manifest | Start Goose with the displayed stdio extension command |
|
|
11
|
+
|
|
12
|
+
These adapters have synthetic protocol/format tests. They are not a certification of every installed harness version. Native APIs and transcript formats can change; unsupported responses produce visible errors. Relay never edits native chat databases. Completed visible messages and tool results are captured; reasoning blocks and unfinished assistant turns are excluded.
|
|
13
|
+
|
|
14
|
+
## Connect and capture
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
relay project add --root /absolute/path/to/project
|
|
18
|
+
relay connect --project PROJECT_ID --harness claude
|
|
19
|
+
relay connections --project PROJECT_ID
|
|
20
|
+
relay discover --id CONNECTION_ID
|
|
21
|
+
relay sync --id CONNECTION_ID
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Use `--capture-only` to leave MCP settings untouched. Use `--source /path/to/claude/projects` for an alternative transcript root, `--executable /path/to/codex` or `--executable /path/to/goose` for another binary, or `--source http://127.0.0.1:4096` for OpenCode. OpenCode must already be serving; if authentication is enabled, set `OPENCODE_SERVER_PASSWORD` (and optionally `OPENCODE_SERVER_USERNAME`) in the Relay service environment before starting it. Secrets are not stored in connection records.
|
|
25
|
+
|
|
26
|
+
Capture runs as a background job. The CLI waits for its final result and returns a nonzero exit code on a failed job or any session error. The API exposes progress, errors and cooperative cancellation at session boundaries. Restarted jobs are marked interrupted; recapture is idempotent. Histories are capped at 200 sessions per job, 16 MiB per source response and 20,000 normalized events per import. Changed events with previously captured IDs are rejected atomically rather than silently rewriting history.
|
|
27
|
+
|
|
28
|
+
Capturing is explicit: Relay does not scan your home directory until you connect and request discovery or capture. Project root matching prevents unrelated sessions from being imported. Review source material before exporting it; built-in credential redaction is best effort, not a secret scanner.
|
|
29
|
+
|
|
30
|
+
## Disconnect
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
relay disconnect --id CONNECTION_ID
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Relay removes only its recorded MCP entry. Other settings remain. If that entry was edited externally, disconnect refuses to overwrite it. Captured history and checkpoints remain available. Reconnecting with `--capture-only` removes the previously managed MCP entry. Goose's manifest is a command reference, not an automatic change to its global extension registry.
|
|
37
|
+
|
|
38
|
+
## Sources and compatibility contract
|
|
39
|
+
|
|
40
|
+
- [Claude Code hooks and transcript paths](https://code.claude.com/docs/en/hooks)
|
|
41
|
+
- [Codex app-server protocol](https://learn.chatgpt.com/docs/app-server): `initialize`, `thread/list`, `thread/read`, `thread/turns/list` with `itemsView: full`.
|
|
42
|
+
- [OpenCode SDK and session API](https://opencode.ai/docs/sdk/): session metadata and completed message parts.
|
|
43
|
+
- [Goose CLI commands](https://github.com/aaif-goose/goose/blob/main/documentation/docs/guides/goose-cli-commands.md): JSON list/export support is required.
|
|
44
|
+
|
|
45
|
+
Relay does not claim to restore hidden model state, automatically merge Git changes, or move files between machines. A handoff carries reviewed instructions, references, file digests and pending work. The receiving agent still verifies its environment and performs the work.
|
|
46
|
+
|
|
47
|
+
## Interactive continuation in release 0.3
|
|
48
|
+
|
|
49
|
+
`relay launch --id HANDOFF_ID` opens a fresh Claude Code or Codex interactive session in the registered project with the continuation prompt as one argument. The handoff must be offered and current, and the destination must have a managed MCP connection. It does not use approval-bypass flags or accept the handoff itself. Native model calls may begin in that harness when the user invokes launch; these launches have not been used as live task evaluations in this release. Windows requires a native `.exe` path. OpenCode and Goose retain the explicit prompt/export workflow. See [Claude CLI](https://code.claude.com/docs/en/cli-reference) and [Codex CLI reference](https://developers.openai.com/codex/cli/reference).
|
|
50
|
+
|
|
51
|
+
Goose native JSON exports serialize `Session.conversation` as a message array; Relay honors `metadata.userVisible` and preserves tool-response roles. The parser also accepts older normalized `messages` exports. The export's working directory is rechecked before import. Source contract: [Goose session export implementation](https://github.com/aaif-goose/goose/blob/main/crates/goose-cli/src/commands/session.rs) and [message types](https://github.com/aaif-goose/goose/blob/main/crates/goose-provider-types/src/conversation/message.rs).
|
|
52
|
+
|
|
53
|
+
OpenCode projects using `opencode.jsonc` must use capture-only and configure MCP manually; Relay refuses to create a competing JSON config that might shadow existing settings.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Node CLI and local context service
|
|
2
|
+
|
|
3
|
+
The npm runtime is available as the public `@mahekara28/context-relay` beta. It requires
|
|
4
|
+
**Node 22.16 or newer** and npm, with no Python prerequisite. Node may print an
|
|
5
|
+
experimental SQLite warning on older supported releases. The existing Python
|
|
6
|
+
API continues to use its own database and release version.
|
|
7
|
+
|
|
8
|
+
## Install from npm
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install --global @mahekara28/context-relay@beta
|
|
12
|
+
relay --help
|
|
13
|
+
relay ui
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Use `@0.3.0-beta.1` instead of `@beta` to install this exact version. Beta updates
|
|
17
|
+
are explicit: rerun the installation command when you want to update.
|
|
18
|
+
|
|
19
|
+
### Try with npx
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npx @mahekara28/context-relay@beta demo
|
|
23
|
+
npx @mahekara28/context-relay@beta ui
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`npx` downloads the published package into npm's cache and runs its `relay`
|
|
27
|
+
executable. It requires no global installation or Git checkout. Use the full
|
|
28
|
+
scoped name; the unscoped `context-relay` package is a different project.
|
|
29
|
+
Global and npx invocations use the same default local data directory. Add
|
|
30
|
+
`--data-dir /path/to/workspace` to keep a separate profile.
|
|
31
|
+
|
|
32
|
+
To stop the service and remove a global installation:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
relay stop
|
|
36
|
+
npm uninstall --global @mahekara28/context-relay
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Uninstalling the CLI does not delete your saved local workspace.
|
|
40
|
+
|
|
41
|
+
### Build from source (contributors)
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
git clone https://github.com/mahekara28/context-relay.git
|
|
45
|
+
cd context-relay
|
|
46
|
+
npm ci --ignore-scripts
|
|
47
|
+
npm run build
|
|
48
|
+
npm pack
|
|
49
|
+
npm install --global ./mahekara28-context-relay-0.3.0-beta.1.tgz
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Alternatively use `node dist/cli.js` from the checkout wherever this guide uses
|
|
53
|
+
`relay`. See the [publication procedure](releases/NPM_PUBLISHING.md) for maintainers.
|
|
54
|
+
|
|
55
|
+
## Try the local workflow
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
relay init
|
|
59
|
+
relay demo
|
|
60
|
+
relay sessions
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`demo` creates clearly labeled synthetic webhook conversations in the Relay data
|
|
64
|
+
directory. It prints a checkpoint ID and an offered handoff ID. Substitute those
|
|
65
|
+
IDs below:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
relay checkpoint packet --id CHECKPOINT_ID
|
|
69
|
+
relay handoff show --id HANDOFF_ID
|
|
70
|
+
relay handoff accept --id HANDOFF_ID --recipient codex
|
|
71
|
+
relay status
|
|
72
|
+
relay stop
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Acceptance checks the named recipient, packet digest, captured source revisions,
|
|
76
|
+
selected artifact contents and Git HEAD. It does not prove the coding task is
|
|
77
|
+
complete. New captured events require a fresh checkpoint and offer.
|
|
78
|
+
|
|
79
|
+
## Register your own project
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
relay project add --root /absolute/path/to/project
|
|
83
|
+
relay project list
|
|
84
|
+
relay import --file session.json
|
|
85
|
+
relay checkpoint create --file checkpoint.json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
A normalized session contains `projectId`, `harness` (`claude`, `codex`, `opencode`,
|
|
89
|
+
`goose` or `manual`), `externalId`, `title`, and `events`. Each event has a stable
|
|
90
|
+
`id`, `role`, and `text`; optional fields are `kind` and `timestamp`. Re-importing
|
|
91
|
+
identical events is safe. Reusing an event ID for changed content fails the whole
|
|
92
|
+
batch. Event text receives best-effort credential redaction before persistence.
|
|
93
|
+
|
|
94
|
+
A checkpoint contains `projectId`, `title`, `goal`, `sources` (session ID and
|
|
95
|
+
expected revision), and optionally `instructions`, `criteria`, `openWork`,
|
|
96
|
+
`evidence`, `files`, `parents`, and `resolutions`. Each instruction has a `key`,
|
|
97
|
+
`text`, and source event references. Inspect the exported Zod schemas in
|
|
98
|
+
[`model.ts`](../packages/relay/src/model.ts) for the complete contract. Files are
|
|
99
|
+
project-relative; references do not transfer their bytes.
|
|
100
|
+
|
|
101
|
+
The packet budget is **UTF-8 bytes**, not model tokens. Required instructions,
|
|
102
|
+
goal, criteria and open work must fit. Optional evidence can be omitted with a
|
|
103
|
+
count. The contract is `relay.packet/v1`, independently versioned from the
|
|
104
|
+
Python task packet.
|
|
105
|
+
|
|
106
|
+
## MCP
|
|
107
|
+
|
|
108
|
+
Register the project first, then configure your MCP client to run:
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
relay mcp --project PROJECT_ID --harness codex
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The stdio bridge talks to the same local service as the CLI. It is scoped to that
|
|
115
|
+
project and binds handoff acceptance to the configured harness label. Tools:
|
|
116
|
+
`relay_sessions`, `relay_session_events`, `relay_checkpoints`, `relay_packet`,
|
|
117
|
+
`relay_handoff_inspect`, and `relay_handoff_accept`. A checkpoint resource and
|
|
118
|
+
`continue_task` prompt are also available. MCP does not capture transcripts or
|
|
119
|
+
inject context automatically. Source text remains untrusted evidence.
|
|
120
|
+
|
|
121
|
+
## Storage and lifecycle
|
|
122
|
+
|
|
123
|
+
State is local plaintext SQLite in `~/.context-relay`. Use `--data-dir PATH` or
|
|
124
|
+
`CONTEXT_RELAY_HOME` for an isolated profile. Newly created directories and token
|
|
125
|
+
files have private modes where supported by the OS. Keep the profile private;
|
|
126
|
+
local harness labels are not remote-user authentication.
|
|
127
|
+
|
|
128
|
+
The service binds only to `127.0.0.1` on an available port. API requests require a
|
|
129
|
+
local bearer token and reject foreign Host/Origin headers. The CLI reads its
|
|
130
|
+
private connection descriptor. `relay ui` prints a local browser URL containing
|
|
131
|
+
the access token in its fragment; treat that URL as a credential. Do not expose
|
|
132
|
+
the service through a public proxy. `relay serve` runs in the foreground;
|
|
133
|
+
`relay init` starts a background process. `relay doctor` can recover a lock only
|
|
134
|
+
when its recorded process no longer exists. It never kills another process.
|
|
135
|
+
|
|
136
|
+
## Release gates
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
npm test
|
|
140
|
+
npm run test:package
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The transport suite uses an actual MCP SDK stdio client. The package gate installs
|
|
144
|
+
the tarball into a temporary directory and checks startup, checkpoint creation,
|
|
145
|
+
acceptance, shutdown, persisted state and restart. CI covers Linux, macOS and
|
|
146
|
+
Windows. Live model evaluation remains separate from these offline checks.
|
|
147
|
+
|
|
148
|
+
## Browser workspace and connections
|
|
149
|
+
|
|
150
|
+
Run `relay ui` for the bundled local UI, or `relay ui --no-open` to print its local access URL. Follow the [workspace guide](WORKSPACE.md) for source review, merge decisions, handoff export and optional Nebius drafts. The [connection guide](HARNESS_CONNECTIONS.md) documents native history capture separately from MCP access. Use `relay launch --id HANDOFF_ID` for a configured Claude Code/Codex interactive continuation; use `relay handoff export --id HANDOFF_ID` for Markdown.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Local workspace operations
|
|
2
|
+
|
|
3
|
+
## Start, inspect, stop
|
|
4
|
+
|
|
5
|
+
`relay ui` starts this profile's service if needed and opens its loopback browser URL. `relay ui --no-open` prints the same URL for manual opening. Treat that URL as a local access credential. The fragment is removed as soon as the page loads; API requests use an Authorization header.
|
|
6
|
+
|
|
7
|
+
`relay status` reports the service instance. `relay doctor` diagnoses the profile and can recover a lock whose process no longer exists. `relay stop` closes connections, drains capture jobs at source boundaries and closes SQLite. No other process is killed. Concurrent capture for the same connection is rejected. Service logs live under the data directory and do not include request bodies or provider keys.
|
|
8
|
+
|
|
9
|
+
Use a dedicated `--data-dir` or `CONTEXT_RELAY_HOME` for separate experiments. Keep the same profile option when launching CLI commands from the UI. Back up the entire data directory **after stopping the service**; SQLite may have WAL/SHM sidecars while running. A newer schema is rejected by an older binary. There is no downgrade migration.
|
|
10
|
+
|
|
11
|
+
## Review and continuation
|
|
12
|
+
|
|
13
|
+
1. Add an existing project directory and connect a harness.
|
|
14
|
+
2. Discover matching native sessions and choose which to capture.
|
|
15
|
+
3. Select event evidence in **Threads**, then open **Review handoff**.
|
|
16
|
+
4. Fill the goal and current instructions; each instruction needs a cited event. List acceptance criteria, open work and optional project-relative file paths.
|
|
17
|
+
5. Confirm your review, select the recipient and save. The checkpoint is immutable. A failed offer can leave the saved checkpoint available for a new review.
|
|
18
|
+
6. In **Handoffs**, copy the continuation prompt or export Markdown. For configured Claude Code/Codex, `relay launch --id HANDOFF_ID` starts a fresh interactive terminal session in the project. It preserves native approvals and does not accept the handoff automatically. On Windows, launch requires a native `.exe` configured with `--executable`; otherwise use the copied prompt.
|
|
19
|
+
7. The receiving agent inspects and accepts through MCP. Acceptance is not a successful test result.
|
|
20
|
+
|
|
21
|
+
Review drafts stay in browser memory, with a navigation-away warning. A page reload discards an unsaved draft. Replacing a draft or changing projects asks before discarding it. Browser state is not an authoritative checkpoint. The UI requires a new explicit review after editing fields or applying a model proposal.
|
|
22
|
+
|
|
23
|
+
**Freshness has a precise scope:** captured source revisions, selected file digests and Git HEAD. It cannot detect native messages that have not yet been captured. Capture again before a consequential handoff. A revision change blocks acceptance; review a new checkpoint rather than editing the old one. Captured character growth is not a model context-window measurement.
|
|
24
|
+
|
|
25
|
+
## Merge behavior
|
|
26
|
+
|
|
27
|
+
Select two or more checkpoints, then **Merge selected**. Relay combines references, criteria, open work and file paths. The first goal is a starting point for review. Conflicting instruction keys require a selected resolution; revise the resulting text and sources as needed. The new checkpoint records parents and resolutions. Original checkpoints, native histories and Git files are preserved.
|
|
28
|
+
|
|
29
|
+
A new review uses latest **captured** source revisions and checks current files when saved. Users must inspect differences; this is not an automatic semantic merge or Git merge.
|
|
30
|
+
|
|
31
|
+
## Nebius proposals
|
|
32
|
+
|
|
33
|
+
The provider is optional and keyless operation is the default. Supply a Token Factory key and a model ID available to your account through the service environment:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
# Set NEBIUS_API_KEY securely in your shell/secret manager first.
|
|
37
|
+
export NEBIUS_MODEL='YOUR_AVAILABLE_MODEL_ID'
|
|
38
|
+
relay stop # if this profile is already running
|
|
39
|
+
relay ui
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
On PowerShell use `$env:NEBIUS_MODEL = 'YOUR_AVAILABLE_MODEL_ID'`. Relay does not save the API key in SQLite, configuration JSON, browser state or logs. A service already running must restart to inherit new environment variables.
|
|
43
|
+
|
|
44
|
+
**Preview → explicit consent → request → schema/citation validation → review.** Only the selected, redacted source events are sent to `https://api.tokenfactory.nebius.com/v1/chat/completions`. Their exact payload is displayed before consent. No filenames, full history or unrelated sessions are added automatically. Model output is returned as a proposal, never as a checkpoint or acceptance.
|
|
45
|
+
|
|
46
|
+
Limits: 40 events, 48,000 UTF-8 input bytes, 3,000 requested output tokens, 30-second timeout, 128,000 response bytes, no automatic retries. A model must support OpenAI-compatible chat completions with JSON-object output. Truncation, malformed JSON, schema errors, duplicate instruction keys and citations outside the selection are rejected. Costs and availability depend on the selected model; Relay does not estimate them. Provider error bodies are not exposed. Sources: [Nebius Token Factory](https://dev.nebius.com/token-factory).
|
|
47
|
+
|
|
48
|
+
## Troubleshooting
|
|
49
|
+
|
|
50
|
+
| Symptom | Next step |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| No sessions found | Check project root, explicit source, harness installation/API availability and the connection guide. |
|
|
53
|
+
| Changed source ID | The native history changed after capture. Export it under a distinct source identity; Relay refuses to rewrite old evidence silently. |
|
|
54
|
+
| Capture interrupted | Restart capture; already imported events are idempotent. Review partial errors. |
|
|
55
|
+
| Config conflict | Inspect the existing managed MCP entry. Relay refuses to overwrite external edits. |
|
|
56
|
+
| Authentication expired after a restart/port change | Run `relay ui` to open the current service URL. |
|
|
57
|
+
| Handoff is stale | Recapture, inspect changed source/files, and save a newly reviewed checkpoint. |
|
|
58
|
+
| Nebius not configured | Set both environment variables before starting the service; manual review remains available. |
|
|
59
|
+
| Browser cannot copy | Relay shows the prompt in a selectable text area. |
|
|
60
|
+
|
|
61
|
+
The local workspace is intended for one OS user. API tokens and 0700/0600 permissions protect local state where supported, but the database is not encrypted. The package has no cloud sync, multi-user tenancy, scheduled capture or automatic native thread rewriting.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Publishing the npm beta
|
|
2
|
+
|
|
3
|
+
Package: **`@mahekara28/context-relay`**. Registry: **https://registry.npmjs.org/**.
|
|
4
|
+
Publisher: npm user **`mahekara28`**, independently authenticated from GitHub.
|
|
5
|
+
The unscoped `context-relay` package belongs to another project.
|
|
6
|
+
|
|
7
|
+
## Release procedure
|
|
8
|
+
|
|
9
|
+
1. Use an isolated npm profile for this account. In the maintainer's dedicated
|
|
10
|
+
workspace, use its `mahek-npm` launcher for login, identity checks and publishing.
|
|
11
|
+
Never borrow another account's npm credentials or commit `.npmrc` tokens.
|
|
12
|
+
2. Confirm `npm whoami` returns `mahekara28` in that profile. Complete npm's web
|
|
13
|
+
login / two-factor challenge if requested. A GitHub PAT is not npm access.
|
|
14
|
+
3. Keep `package.json`, the lockfile, runtime version and docs aligned. Check that
|
|
15
|
+
the version has not already been published. Published versions cannot be replaced.
|
|
16
|
+
4. Run `npm run typecheck`, `npm test`, `npm run test:package`,
|
|
17
|
+
`npm run test:browser` and `npm run test:site`. Review the final PR's CI on Linux,
|
|
18
|
+
macOS and Windows. The package gate uses anonymous, temporary npm config, cache
|
|
19
|
+
and global prefix, exercises the installed command and npm exec/npx, then
|
|
20
|
+
verifies the service, UI, demo, handoff and persistence.
|
|
21
|
+
5. Pack once, inspect the file list and preserve that exact tarball:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm pack --json --pack-destination /path/to/release-artifacts
|
|
25
|
+
npm publish /path/to/release-artifacts/mahekara28-context-relay-0.3.0-beta.1.tgz --dry-run --access public --tag beta
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
6. Publish the inspected tarball using the isolated authenticated npm launcher:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm publish /path/to/release-artifacts/mahekara28-context-relay-0.3.0-beta.1.tgz --access public --tag beta
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`publishConfig` defaults to the public npm registry, public access and the
|
|
35
|
+
`beta` tag. Keep prereleases on `beta`; promoting a stable version to `latest`
|
|
36
|
+
is a separate release decision. Never put credentials or OTPs in Git history.
|
|
37
|
+
|
|
38
|
+
7. Read registry metadata and compare `dist.integrity` with the inspected tarball.
|
|
39
|
+
In an anonymous temporary environment, verify global installation plus
|
|
40
|
+
`npx @mahekara28/context-relay@beta --version`, `--help`, and a demo/UI lifecycle.
|
|
41
|
+
Only then merge/deploy documentation that advertises registry installation.
|
|
42
|
+
|
|
43
|
+
For subsequent automated releases, npm supports [trusted publishing from GitHub
|
|
44
|
+
Actions](https://docs.npmjs.com/trusted-publishers/). Configure that trust explicitly
|
|
45
|
+
on npm before enabling a publishing workflow; the current npm CI builds and tests
|
|
46
|
+
artifacts and does not publish automatically.
|
|
47
|
+
|
|
48
|
+
## Consumer commands
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
npm install --global @mahekara28/context-relay@beta
|
|
52
|
+
relay ui
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npx @mahekara28/context-relay@beta ui
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Replace `@beta` with `@0.3.0-beta.1` to pin this release. Node 22.16+ is required;
|
|
60
|
+
Git, Python, a frontend build and a model API key are not installation prerequisites.
|
package/package.json
CHANGED
|
@@ -1,6 +1,80 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mahekara28/context-relay",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.0-beta.1",
|
|
4
|
+
"description": "Review and carry working context between coding agent sessions.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Mahek Ara",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public",
|
|
10
|
+
"registry": "https://registry.npmjs.org/",
|
|
11
|
+
"tag": "beta"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ai-agents",
|
|
15
|
+
"context",
|
|
16
|
+
"mcp",
|
|
17
|
+
"cli",
|
|
18
|
+
"claude-code",
|
|
19
|
+
"codex",
|
|
20
|
+
"opencode",
|
|
21
|
+
"goose"
|
|
22
|
+
],
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/mahekara28/context-relay/issues"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/mahekara28/context-relay.git"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"relay": "dist/cli.js"
|
|
32
|
+
},
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist/*.js",
|
|
41
|
+
"dist/*.d.ts",
|
|
42
|
+
"dist/*.js.map",
|
|
43
|
+
"dist/adapters/*.js",
|
|
44
|
+
"dist/adapters/*.d.ts",
|
|
45
|
+
"dist/adapters/*.js.map",
|
|
46
|
+
"dist/ui",
|
|
47
|
+
"LICENSE",
|
|
48
|
+
"docs/NPM_QUICKSTART.md",
|
|
49
|
+
"docs/HARNESS_CONNECTIONS.md",
|
|
50
|
+
"docs/WORKSPACE.md",
|
|
51
|
+
"docs/releases/NPM_PUBLISHING.md"
|
|
52
|
+
],
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=22.16.0"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc -p tsconfig.json && node scripts/npm/build.mjs",
|
|
58
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
59
|
+
"test": "npm run build && node --test packages/relay/test/*.test.mjs",
|
|
60
|
+
"test:package": "node scripts/npm/package-smoke.mjs",
|
|
61
|
+
"prepack": "npm run build",
|
|
62
|
+
"test:browser": "npm run build && playwright test",
|
|
63
|
+
"demo:render": "npm run build && node scripts/npm/render-demo.mjs",
|
|
64
|
+
"site:build": "node scripts/site/build.mjs",
|
|
65
|
+
"site:preview": "node scripts/site/preview.mjs",
|
|
66
|
+
"site:social": "node scripts/site/render-social.mjs",
|
|
67
|
+
"test:site": "npm run site:build && playwright test --config playwright.site.config.mjs"
|
|
68
|
+
},
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"@modelcontextprotocol/sdk": "1.32.2",
|
|
71
|
+
"zod": "4.6.7"
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@playwright/test": "1.64.1",
|
|
75
|
+
"@types/node": "24.10.0",
|
|
76
|
+
"prettier": "3.6.2",
|
|
77
|
+
"typescript": "5.9.3"
|
|
78
|
+
},
|
|
79
|
+
"homepage": "https://mahekara28.github.io/context-relay/"
|
|
80
|
+
}
|