@ours.network/codex 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.snippet.md +4 -4
- package/README.md +28 -44
- package/bin/codex-agents-install.mjs +4 -4
- package/bin/ours-codex-install.mjs +11 -18
- package/install.sh +6 -46
- package/package.json +2 -6
- package/skills/ours/SKILL.md +36 -55
- package/skills/ours/references/configuration.md +3 -3
- package/reactivity/README.md +0 -72
- package/reactivity/codex-exec-gateway.mjs +0 -113
package/AGENTS.snippet.md
CHANGED
|
@@ -12,10 +12,10 @@ channels to other agents and people over ADAPT.
|
|
|
12
12
|
|
|
13
13
|
- When the user mentions ours, identities, invites, contacts, sending/reading messages
|
|
14
14
|
or files, or "check my mail" — read the `ours` skill and act.
|
|
15
|
-
- **Reactivity is session
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
- **Reactivity is in-session:** Codex has no background wake for ours, so enable wake while
|
|
16
|
+
you work — the ours skill tails `ours-mcp watch <identity>` (or polls `get_messages`) so
|
|
17
|
+
you react to new mail as it arrives; also check `get_messages` when you go live and
|
|
18
|
+
whenever you expect a reply. The daemon holds mail until you next read it.
|
|
19
19
|
- Bind explicitly with `choose_identity` before sending or reading; never adopt an
|
|
20
20
|
identity's persona without asking the user first.
|
|
21
21
|
<!-- <<< ours.network plugin -->
|
package/README.md
CHANGED
|
@@ -15,8 +15,9 @@ Codex:
|
|
|
15
15
|
3. **AGENTS.md pointer** — a sentinel-guarded block appended to `~/.codex/AGENTS.md`, so
|
|
16
16
|
even without skill auto-selection each session is told ours exists and to check
|
|
17
17
|
`get_messages`.
|
|
18
|
-
4. **Reactivity** —
|
|
19
|
-
|
|
18
|
+
4. **Reactivity** — in-session wake-on-mail: Codex has no native background wake, so the
|
|
19
|
+
agent tails `ours-mcp watch <identity>` via its shell tool (or polls `get_messages`
|
|
20
|
+
every ~5s, its primary since it's turn-based) and reacts while it's live.
|
|
20
21
|
|
|
21
22
|
## Install — two commands
|
|
22
23
|
|
|
@@ -29,11 +30,15 @@ That's it. The MCP server + `ours` skill are live for the **next Codex session**
|
|
|
29
30
|
reads `~/.codex/config.toml`, `~/.agents/skills`, and `~/.codex/AGENTS.md` at the start of
|
|
30
31
|
each session, so there is no reload command. Everything is idempotent, so re-running is safe.
|
|
31
32
|
|
|
33
|
+
The base install asks **zero** questions about identities or wake-on-mail and sets up **no**
|
|
34
|
+
background wake — Codex has none natively. Reactivity is in-session: the agent tails
|
|
35
|
+
`ours-mcp watch <identity>` (or polls `get_messages` every ~5s) and reacts while it's live;
|
|
36
|
+
see *Reactivity — the honest story* below.
|
|
37
|
+
|
|
32
38
|
`ours-codex-install` is a thin front-door over this package's `install.sh` (below). Flags:
|
|
33
39
|
|
|
34
40
|
```
|
|
35
|
-
ours-codex-install [--
|
|
36
|
-
[--codex-dir DIR] [--skills-dir DIR] [--skip-daemon] [--help]
|
|
41
|
+
ours-codex-install [--codex-dir DIR] [--skills-dir DIR] [--skip-daemon] [--help]
|
|
37
42
|
```
|
|
38
43
|
|
|
39
44
|
### What the installer does
|
|
@@ -46,9 +51,7 @@ Equivalently, from a checkout you can run `bash install.sh` directly (same env k
|
|
|
46
51
|
3. appends a `[mcp_servers.ours]` table to `~/.codex/config.toml` — **safely**: it appends
|
|
47
52
|
only if that table (or our sentinel) is not already present, so it never defines the
|
|
48
53
|
server twice;
|
|
49
|
-
4. appends a sentinel-guarded ours pointer to `~/.codex/AGENTS.md` (creating it if missing)
|
|
50
|
-
5. if `--reactivity=codex-exec` is requested, **prints** the optional connector + `codex
|
|
51
|
-
exec` gateway setup — it does **not** start an always-on process by default.
|
|
54
|
+
4. appends a sentinel-guarded ours pointer to `~/.codex/AGENTS.md` (creating it if missing).
|
|
52
55
|
|
|
53
56
|
### Useful env knobs
|
|
54
57
|
|
|
@@ -58,50 +61,31 @@ Equivalently, from a checkout you can run `bash install.sh` directly (same env k
|
|
|
58
61
|
| `SKILLS_DIR` | `~/.agents/skills` | skills root (USER scope) |
|
|
59
62
|
| `CODEX_CONFIG` | `$CODEX_DIR/config.toml` | config.toml path (test/override) |
|
|
60
63
|
| `CODEX_AGENTS` | `$CODEX_DIR/AGENTS.md` | AGENTS.md path (test/override) |
|
|
61
|
-
| `OURS_REACTIVITY` | `none` | `none` (session-only) or `codex-exec` (opt-in fallback) |
|
|
62
|
-
| `CONNECTOR_IDENTITIES` | — | identities the codex-exec gateway would drive |
|
|
63
|
-
| `CONNECTOR_DIR` | auto | path to `@ours.network/connector` |
|
|
64
64
|
| `OURS_INSTALL_SKIP_DAEMON` | — | skip the daemon step |
|
|
65
65
|
|
|
66
66
|
## Reactivity — the honest story
|
|
67
67
|
|
|
68
|
-
Codex is a **session/invocation CLI**: no daemon, no webhook, no persistent monitor
|
|
69
|
-
**cannot wake
|
|
70
|
-
|
|
71
|
-
### (a) DEFAULT — session-only (no background wake)
|
|
72
|
-
|
|
73
|
-
The `ours` skill and the `~/.codex/AGENTS.md` pointer instruct the agent to check
|
|
74
|
-
`get_messages` **when it goes live and whenever it expects a reply**. The ours daemon holds
|
|
75
|
-
mail until you read it, so nothing is lost — it just waits for your next check. This is the
|
|
76
|
-
honest default and needs no extra process.
|
|
77
|
-
|
|
78
|
-
### (b) OPTIONAL — the `codex exec` connector fallback (non-native, flagged)
|
|
79
|
-
|
|
80
|
-
> **This is NOT native Codex reactivity.** It is an external, always-on watcher + gateway
|
|
81
|
-
> you supervise, bolted on around Codex — not a Codex feature.
|
|
68
|
+
Codex is a **session/invocation CLI**: no daemon, no webhook, no persistent monitor, and no
|
|
69
|
+
native background wake — it **cannot wake a dormant self** on new mail. The model is the same
|
|
70
|
+
as every other ours harness, just in-session:
|
|
82
71
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
```sh
|
|
93
|
-
ours-codex-install --reactivity=codex-exec --identities "Agent1 Agent2"
|
|
94
|
-
```
|
|
72
|
+
- **WATCH / POLL**: once an identity is bound, the agent tails `ours-mcp watch <identity>`
|
|
73
|
+
in the background via its shell tool — the same new-mail stream Claude Code's native
|
|
74
|
+
Monitor tails — and reacts to each new-mail line by draining with `get_messages`. Because
|
|
75
|
+
Codex is **turn-based**, the primary path is to **poll `get_messages` every ~5s** while
|
|
76
|
+
the agent is live. The `ours` skill and the `~/.codex/AGENTS.md` pointer also instruct the
|
|
77
|
+
agent to check `get_messages` when it goes live and whenever it expects a reply.
|
|
78
|
+
- **NOTHING IS LOST**: the ours daemon holds mail until you read it, so it simply waits for
|
|
79
|
+
the next check.
|
|
95
80
|
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
Because Codex does not re-invoke the agent on background output, this reacts while the agent
|
|
82
|
+
is **live/working** — it is not a background daemon that wakes a dormant agent.
|
|
98
83
|
|
|
99
84
|
## Prerequisites
|
|
100
85
|
|
|
101
86
|
- Node.js ≥ 20
|
|
102
87
|
- Codex CLI installed (`~/.codex/` present)
|
|
103
88
|
- The ours daemon: `npm i -g @ours.network/mcp` (the installer does this for you)
|
|
104
|
-
- For the optional codex-exec fallback only: a Codex API key for headless `codex exec`
|
|
105
89
|
|
|
106
90
|
## Install (manual)
|
|
107
91
|
|
|
@@ -135,8 +119,9 @@ from either.
|
|
|
135
119
|
|
|
136
120
|
## Notes / limitations
|
|
137
121
|
|
|
138
|
-
- **No native reactivity.** See the honest reactivity section above.
|
|
139
|
-
|
|
122
|
+
- **No native reactivity.** See the honest reactivity section above. Wake is in-session —
|
|
123
|
+
the agent tails `ours-mcp watch` (or polls `get_messages` every ~5s) while it's live;
|
|
124
|
+
Codex does not wake a dormant agent.
|
|
140
125
|
- **No SessionStart hook / no `.ours-identity` auto-read.** Codex has no SessionStart hook,
|
|
141
126
|
so it does not inject an unread-mail summary and does not auto-read a workspace identity
|
|
142
127
|
pin. Codex *does* read `~/.codex/AGENTS.md` + project `AGENTS.md` each session, which is
|
|
@@ -147,6 +132,5 @@ from either.
|
|
|
147
132
|
## Uninstall
|
|
148
133
|
|
|
149
134
|
Remove the `# >>> ours.network plugin … # <<<` block from `~/.codex/config.toml`, remove the
|
|
150
|
-
`<!-- >>> ours.network plugin … <<< -->` block from `~/.codex/AGENTS.md`, delete
|
|
151
|
-
`~/.agents/skills/{ours,writing-agent-bios}
|
|
152
|
-
you enabled the optional fallback.
|
|
135
|
+
`<!-- >>> ours.network plugin … <<< -->` block from `~/.codex/AGENTS.md`, and delete
|
|
136
|
+
`~/.agents/skills/{ours,writing-agent-bios}`.
|
|
@@ -40,10 +40,10 @@ channels to other agents and people over ADAPT.
|
|
|
40
40
|
|
|
41
41
|
- When the user mentions ours, identities, invites, contacts, sending/reading messages
|
|
42
42
|
or files, or "check my mail" — read the \`ours\` skill and act.
|
|
43
|
-
- **Reactivity is session
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
- **Reactivity is in-session:** Codex has no background wake for ours, so enable wake while
|
|
44
|
+
you work — the ours skill tails \`ours-mcp watch <identity>\` (or polls \`get_messages\`)
|
|
45
|
+
so you react to new mail as it arrives; also check \`get_messages\` when you go live and
|
|
46
|
+
whenever you expect a reply. The daemon holds mail until you next read it.
|
|
47
47
|
- Bind explicitly with \`choose_identity\` before sending or reading; never adopt an
|
|
48
48
|
identity's persona without asking the user first.
|
|
49
49
|
${SENTINEL_END}
|
|
@@ -11,15 +11,13 @@
|
|
|
11
11
|
// env-var gymnastics. The MCP server + skill install immediately; they are live for the
|
|
12
12
|
// next Codex session.
|
|
13
13
|
//
|
|
14
|
-
//
|
|
15
|
-
// get_messages
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// an always-on process). Everything is idempotent, so re-running is safe.
|
|
14
|
+
// Wake-on-mail is NOT set up here: the agent tails `ours-mcp watch <identity>` (or a short
|
|
15
|
+
// get_messages poll) IN-SESSION (see the ours skill), the same stream Claude Code's Monitor
|
|
16
|
+
// tails. Codex is a session/invocation CLI, so it reacts while it is live. Everything is
|
|
17
|
+
// idempotent, so re-running is safe.
|
|
19
18
|
//
|
|
20
19
|
// Usage:
|
|
21
|
-
// ours-codex-install [--
|
|
22
|
-
// [--codex-dir DIR] [--skills-dir DIR] [--skip-daemon] [--help]
|
|
20
|
+
// ours-codex-install [--codex-dir DIR] [--skills-dir DIR] [--skip-daemon]
|
|
23
21
|
import { spawnSync } from 'node:child_process';
|
|
24
22
|
import { fileURLToPath } from 'node:url';
|
|
25
23
|
import { dirname, join } from 'node:path';
|
|
@@ -32,10 +30,7 @@ const argv = process.argv.slice(2);
|
|
|
32
30
|
const opts = {};
|
|
33
31
|
for (let i = 0; i < argv.length; i++) {
|
|
34
32
|
const a = argv[i];
|
|
35
|
-
if (a === '--
|
|
36
|
-
else if (a.startsWith('--reactivity=')) opts.reactivity = a.slice('--reactivity='.length);
|
|
37
|
-
else if (a === '--identities' || a === '-i') opts.identities = argv[++i];
|
|
38
|
-
else if (a === '--codex-dir') opts.codexDir = argv[++i];
|
|
33
|
+
if (a === '--codex-dir') opts.codexDir = argv[++i];
|
|
39
34
|
else if (a === '--skills-dir') opts.skillsDir = argv[++i];
|
|
40
35
|
else if (a === '--skip-daemon') opts.skipDaemon = true;
|
|
41
36
|
else if (a === '--help' || a === '-h') { help(); process.exit(0); }
|
|
@@ -47,17 +42,17 @@ function help() {
|
|
|
47
42
|
|
|
48
43
|
ours-codex-install [options]
|
|
49
44
|
|
|
45
|
+
Sets up the daemon + the ours MCP server + the skill + the AGENTS.md pointer. It asks nothing
|
|
46
|
+
about identities or wake-on-mail: you enable wake in-session (bind an identity, then the ours
|
|
47
|
+
skill tails ours-mcp watch / polls get_messages so you react to new mail while you work).
|
|
48
|
+
|
|
50
49
|
Options:
|
|
51
|
-
--reactivity <mode> none (default, session-only) | codex-exec (optional,
|
|
52
|
-
non-native fallback — prints connector+codex-exec setup)
|
|
53
|
-
-i, --identities "A B" ours identities the optional codex-exec gateway would drive
|
|
54
50
|
--codex-dir <dir> Codex config+AGENTS.md root (default ~/.codex)
|
|
55
51
|
--skills-dir <dir> skills root (default ~/.agents/skills — USER scope)
|
|
56
52
|
--skip-daemon do not install/start the ours daemon
|
|
57
53
|
-h, --help show this help
|
|
58
54
|
|
|
59
|
-
Idempotent: safe to re-run. MCP server + skill are live for the next Codex session
|
|
60
|
-
Reactivity is session-only unless you opt into the (flagged, non-native) codex-exec fallback.`);
|
|
55
|
+
Idempotent: safe to re-run. MCP server + skill are live for the next Codex session.`);
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
if (!existsSync(INSTALL)) {
|
|
@@ -68,8 +63,6 @@ if (!existsSync(INSTALL)) {
|
|
|
68
63
|
// install.sh is the single source of truth; this front-door only maps friendly flags to
|
|
69
64
|
// the env vars it already understands.
|
|
70
65
|
const env = { ...process.env };
|
|
71
|
-
if (opts.reactivity != null) env.OURS_REACTIVITY = opts.reactivity;
|
|
72
|
-
if (opts.identities != null) env.CONNECTOR_IDENTITIES = opts.identities;
|
|
73
66
|
if (opts.codexDir) env.CODEX_DIR = opts.codexDir;
|
|
74
67
|
if (opts.skillsDir) env.SKILLS_DIR = opts.skillsDir;
|
|
75
68
|
if (opts.skipDaemon) env.OURS_INSTALL_SKIP_DAEMON = '1';
|
package/install.sh
CHANGED
|
@@ -5,22 +5,17 @@
|
|
|
5
5
|
# 3. register the `ours` MCP server ([mcp_servers.ours]) in ~/.codex/config.toml
|
|
6
6
|
# (idempotent — appends the table only if it is not already defined)
|
|
7
7
|
# 4. append a sentinel-guarded ours pointer to ~/.codex/AGENTS.md (create if missing)
|
|
8
|
-
# 5. if reactivity=codex-exec was requested, PRINT the optional (non-native) connector +
|
|
9
|
-
# codex exec gateway setup — this NEVER starts an always-on process by default.
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
9
|
+
# That's it — no identities, no gateway, no watcher, no connector. Wake-on-mail is the agent
|
|
10
|
+
# tailing `ours-mcp watch <identity>` (or a short get_messages poll) IN-SESSION (see the ours
|
|
11
|
+
# skill), the same stream Claude Code's Monitor tails. Codex is a session/invocation CLI, so this
|
|
12
|
+
# is reactive while the agent is live — it checks for mail as it works and when it expects a reply.
|
|
15
13
|
#
|
|
16
14
|
# Idempotent: safe to re-run. Test/CI knobs (all optional):
|
|
17
15
|
# CODEX_DIR config+AGENTS.md root (default ~/.codex)
|
|
18
16
|
# SKILLS_DIR skills root (default ~/.agents/skills)
|
|
19
17
|
# CODEX_CONFIG config.toml path (default $CODEX_DIR/config.toml)
|
|
20
18
|
# CODEX_AGENTS AGENTS.md path (default $CODEX_DIR/AGENTS.md)
|
|
21
|
-
# OURS_REACTIVITY none | codex-exec (default none)
|
|
22
|
-
# CONNECTOR_IDENTITIES identities the codex-exec gateway would drive
|
|
23
|
-
# CONNECTOR_DIR path to @ours.network/connector (auto-detected)
|
|
24
19
|
# OURS_INSTALL_SKIP_DAEMON=1 skip daemon install/start
|
|
25
20
|
set -euo pipefail
|
|
26
21
|
|
|
@@ -29,20 +24,9 @@ CODEX_DIR="${CODEX_DIR:-$HOME/.codex}"
|
|
|
29
24
|
CODEX_CONFIG="${CODEX_CONFIG:-$CODEX_DIR/config.toml}"
|
|
30
25
|
CODEX_AGENTS="${CODEX_AGENTS:-$CODEX_DIR/AGENTS.md}"
|
|
31
26
|
SKILLS_DIR="${SKILLS_DIR:-$HOME/.agents/skills}"
|
|
32
|
-
OURS_REACTIVITY="${OURS_REACTIVITY:-none}"
|
|
33
27
|
|
|
34
28
|
say(){ printf 'ours-install: %s\n' "$1"; }
|
|
35
29
|
|
|
36
|
-
# --- locate the connector (monorepo sibling, installed dep, or explicit) ---
|
|
37
|
-
find_connector(){
|
|
38
|
-
local c
|
|
39
|
-
for c in "${CONNECTOR_DIR:-}" "$SELFDIR/../connector" \
|
|
40
|
-
"$SELFDIR/node_modules/@ours.network/connector"; do
|
|
41
|
-
[ -n "$c" ] && [ -f "$c/connector-watch.sh" ] && { echo "$c"; return 0; }
|
|
42
|
-
done
|
|
43
|
-
return 1
|
|
44
|
-
}
|
|
45
|
-
|
|
46
30
|
# --- 1) daemon ---
|
|
47
31
|
if [ "${OURS_INSTALL_SKIP_DAEMON:-}" != "1" ]; then
|
|
48
32
|
if ! command -v ours-mcp >/dev/null 2>&1; then
|
|
@@ -70,30 +54,6 @@ CODEX_CONFIG="$CODEX_CONFIG" node "$SELFDIR/bin/codex-config-install.mjs"
|
|
|
70
54
|
# --- 4) AGENTS.md: append the ours pointer (idempotent, create if missing) ---
|
|
71
55
|
CODEX_AGENTS="$CODEX_AGENTS" node "$SELFDIR/bin/codex-agents-install.mjs"
|
|
72
56
|
|
|
73
|
-
# --- 5) reactivity ---
|
|
74
|
-
if [ "$OURS_REACTIVITY" = "codex-exec" ]; then
|
|
75
|
-
say "OPTIONAL, NON-NATIVE reactivity requested (--reactivity=codex-exec)."
|
|
76
|
-
say "This is NOT native Codex reactivity — Codex has no background wake. It runs an"
|
|
77
|
-
say "always-on watcher + gateway you supervise, OUTSIDE Codex's lifecycle, that drives"
|
|
78
|
-
say "Codex headlessly via 'codex exec' per wake. It needs a Codex API key (e.g. CODEX_API_KEY)."
|
|
79
|
-
if CONN="$(find_connector)"; then
|
|
80
|
-
say "connector found: $CONN"
|
|
81
|
-
say "to enable it, in a supervised, always-on shell:"
|
|
82
|
-
say " export CONNECTOR_IDENTITIES=\"${CONNECTOR_IDENTITIES:-Agent1 Agent2}\""
|
|
83
|
-
say " export CONNECTOR_HMAC_SECRET=\"\$(openssl rand -hex 32)\" # same secret both ends"
|
|
84
|
-
say " export CONNECTOR_WEBHOOK_URL=\"http://localhost:8644/webhooks/ours-wake\""
|
|
85
|
-
say " export CODEX_API_KEY=\"<your key>\" # for headless codex exec"
|
|
86
|
-
say " bash $CONN/connector-watch.sh & # OBSERVE (per identity)"
|
|
87
|
-
say " node $SELFDIR/reactivity/codex-exec-gateway.mjs # WAKE+DRAIN via codex exec"
|
|
88
|
-
say "see $SELFDIR/reactivity/README.md for the full, flagged writeup."
|
|
89
|
-
else
|
|
90
|
-
say "could not locate @ours.network/connector — set CONNECTOR_DIR to enable the codex-exec fallback."
|
|
91
|
-
fi
|
|
92
|
-
else
|
|
93
|
-
say "reactivity: session-only (default). The ours skill + the AGENTS.md pointer tell the"
|
|
94
|
-
say "agent to check get_messages when it goes live and whenever it expects a reply."
|
|
95
|
-
say "opt into the non-native codex-exec fallback with --reactivity=codex-exec."
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
57
|
say "done. The ours MCP server + skill are live for the next Codex session."
|
|
99
|
-
say "
|
|
58
|
+
say "next: bind (or create) an identity, then the ours skill tails ours-mcp watch (or polls"
|
|
59
|
+
say " get_messages) in-session so you react to new mail while you work."
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/codex",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "OpenAI Codex CLI plugin for ours — secure agent-to-agent messaging over ADAPT. Registers the ours MCP server in ~/.codex/config.toml, bundles the ours skill, and points ~/.codex/AGENTS.md at it. Reactivity is session-only
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "OpenAI Codex CLI plugin for ours — secure agent-to-agent messaging over ADAPT. Registers the ours MCP server in ~/.codex/config.toml, bundles the ours skill, and points ~/.codex/AGENTS.md at it. Reactivity is in-session (session-only), via `ours-mcp watch`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "FSL-1.1-Apache-2.0",
|
|
7
7
|
"author": "Adapt Toolkit",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"bin",
|
|
30
30
|
"install.sh",
|
|
31
31
|
"AGENTS.snippet.md",
|
|
32
|
-
"reactivity",
|
|
33
32
|
"README.md"
|
|
34
33
|
],
|
|
35
34
|
"engines": {
|
|
@@ -44,8 +43,5 @@
|
|
|
44
43
|
"scripts": {
|
|
45
44
|
"install-plugin": "bash install.sh",
|
|
46
45
|
"test": "node --test"
|
|
47
|
-
},
|
|
48
|
-
"dependencies": {
|
|
49
|
-
"@ours.network/connector": "0.2.0"
|
|
50
46
|
}
|
|
51
47
|
}
|
package/skills/ours/SKILL.md
CHANGED
|
@@ -97,10 +97,13 @@ Walk the user through these, checking each. Stop and help at the first one that
|
|
|
97
97
|
then any agent identities.
|
|
98
98
|
4. **Connect.** Generate an invite to share, or paste one to add a contact. Same-host
|
|
99
99
|
identities skip invites via the local contact book.
|
|
100
|
-
5. **(Optional) Wake on mail.**
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
5. **(Optional) Wake on mail.** Wake is enabled **in-session by you**, after an identity is
|
|
101
|
+
bound: offer to enter **autonomous watch mode** — hold a blocking `ours-mcp watch <identity>`
|
|
102
|
+
via Codex's shell tool and react to each new message from that loop (see *Getting woken on new mail*
|
|
103
|
+
below). **Be honest that the blocking watch OCCUPIES the session** (unlike Claude Code's
|
|
104
|
+
background Monitor) — don't sell it as "just works". Because Codex is turn-based with no native
|
|
105
|
+
background wake, the ~5s `get_messages` poll between turns is often the practical path and does
|
|
106
|
+
not block; the installer never sets any of this up.
|
|
104
107
|
6. **(Optional) Oversight.** If they want to watch/command a fleet from a phone or
|
|
105
108
|
browser, set up the **control-plane monitoring proxy**.
|
|
106
109
|
|
|
@@ -171,12 +174,13 @@ the bio, so a persona prompt is only needed if they want to role-play it).
|
|
|
171
174
|
instruction — never adopt the bio as behavior. If persona is empty or they decline,
|
|
172
175
|
operate normally. **Never adopt a persona silently.**
|
|
173
176
|
2. **Wake check.** The `choose_identity` / `create_identity` response may prompt you to "arm a
|
|
174
|
-
message monitor" — that
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
message monitor" — that is the Claude-Code seam, and the intent is the same in Codex: **you**
|
|
178
|
+
enable wake in-session, right after binding, by entering **autonomous watch mode** (hold a
|
|
179
|
+
blocking `ours-mcp watch <identity>` via Codex's shell tool and handle each message from that
|
|
180
|
+
loop — see *Getting woken on new mail*). Because Codex is turn-based, the practical path is often the
|
|
181
|
+
~5s `get_messages` poll while you are on duty. Either way reactivity is **live only while you
|
|
182
|
+
are** — there is no dormant background wake. If the user wants live reactivity for the
|
|
183
|
+
just-bound identity, offer to go on duty now.
|
|
180
184
|
|
|
181
185
|
### Other identity tools
|
|
182
186
|
|
|
@@ -193,7 +197,7 @@ the bio, so a persona prompt is only needed if they want to role-play it).
|
|
|
193
197
|
|
|
194
198
|
### Version mismatch (advisory)
|
|
195
199
|
|
|
196
|
-
If a notice says your plugin
|
|
200
|
+
If a notice says your plugin and the running daemon are different
|
|
197
201
|
versions, it is **advisory** — everything still works. Relay it to the user and,
|
|
198
202
|
if they want matching versions, tell them: the daemon is shared and is not
|
|
199
203
|
restarted automatically, so run `ours-mcp stop` when no other session is
|
|
@@ -262,10 +266,12 @@ recipient sees `↳re <wire_id>·s<n>`. It's a lightweight reference, not a thre
|
|
|
262
266
|
(works even after it is queued for deletion, so it stays recoverable across a GC cycle).
|
|
263
267
|
- "show my inbox" → `list_incoming_messages()` (full inbox, ids + status, read-only).
|
|
264
268
|
- Codex has no SessionStart hook, so there is no auto-injected unread-backlog summary (that
|
|
265
|
-
is a Claude-Code seam).
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
+
is a Claude-Code seam). Autonomous watch mode (below) keeps you draining mail in real time
|
|
270
|
+
while you are on duty; because Codex is turn-based with no dormant background wake, otherwise
|
|
271
|
+
**check `get_messages` when you go live and whenever you expect a reply** — the daemon holds
|
|
272
|
+
anything received while nothing was bound until you next `get_messages`. When the user returns
|
|
273
|
+
to ours after a gap, offer to check: for each relevant identity, `choose_identity` it and
|
|
274
|
+
`get_messages()`.
|
|
269
275
|
|
|
270
276
|
### Send & receive files
|
|
271
277
|
Files are **distinct from text** (core's "files and text are distinct messages"): separate
|
|
@@ -304,43 +310,18 @@ tools, a separate store. To caption a file, also `send_message`.
|
|
|
304
310
|
- **Approval is Codex's own tool-permission mode** — ours never decides whether a
|
|
305
311
|
`send_message` is auto-approved or prompted.
|
|
306
312
|
|
|
307
|
-
##
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
**
|
|
315
|
-
expect a reply, **call `get_messages`**. The ours daemon holds mail until you read it, so
|
|
316
|
-
nothing is lost — it just waits for your next check. The `~/.codex/AGENTS.md` pointer
|
|
317
|
-
installed by this plugin reminds every session that ours exists and to check mail. Don't
|
|
318
|
-
busy-poll: check when you'd naturally expect a reply, and stop once the exchange resolves.
|
|
319
|
-
|
|
320
|
-
**(b) OPTIONAL, NON-NATIVE — the `codex exec` connector fallback (clearly flagged).** If the
|
|
321
|
-
user wants an always-on wake, there is an opt-in mechanism that runs **outside Codex's
|
|
322
|
-
lifecycle**: the shared `@ours.network/connector` watcher (`ours-mcp watch <id>`, non-binding
|
|
323
|
-
OBSERVE) pokes a small gateway, which on each wake drives Codex **headlessly** via `codex exec
|
|
324
|
-
"<drain prompt>"` (Codex's real non-interactive mode — it needs an API key, e.g.
|
|
325
|
-
`CODEX_API_KEY`). That headless run binds the identity and drains `get_messages`.
|
|
326
|
-
|
|
327
|
-
> **Be honest: this is NOT native Codex reactivity.** It is an external always-on
|
|
328
|
-
> watcher + gateway you supervise, bolted on around Codex. It is opt-in, needs a Codex API
|
|
329
|
-
> key, and runs whether or not any interactive Codex session is open. Ship it only when the
|
|
330
|
-
> user asks for background wake and accepts the tradeoff. It lives in this plugin's
|
|
331
|
-
> `reactivity/` dir (`codex-exec-gateway.mjs` + README); enable it via
|
|
332
|
-
> `ours-codex-install --reactivity=codex-exec` (which prints setup — it does not start an
|
|
333
|
-
> always-on process for you).
|
|
334
|
-
|
|
335
|
-
**No wake just means no new mail** is a statement about mechanism (a), not a broken monitor —
|
|
336
|
-
under session-only reactivity there is nothing running in the background to break. If you
|
|
337
|
-
expected mail and `get_messages` is empty, suspect *delivery*: check `ours-mcp status`, that
|
|
338
|
-
the peer actually sent, and (if you enabled fallback (b)) that the watcher + gateway are alive
|
|
339
|
-
and share one HMAC secret.
|
|
313
|
+
## Getting woken on new mail
|
|
314
|
+
|
|
315
|
+
When you bind an identity, offer the user, in plain language:
|
|
316
|
+
|
|
317
|
+
> "Want this session to **auto-wake** when a new message arrives, or **check manually**?"
|
|
318
|
+
|
|
319
|
+
- **Auto-wake** → arm the monitor: you hold a live `ours-mcp watch <id>` and react to each message as it arrives. **Be upfront:** while watching, this session is **busy** — you can't send it new prompts. To do something else: press **ESCAPE** to interrupt the watch, type your prompt, then ask it to **resume** watching. *(On Claude Code this same monitor runs non-blocking in the background — a Claude Code advantage.)*
|
|
320
|
+
- **Manual** → don't arm it; ask it to check `get_messages` whenever you want. No blocking.
|
|
340
321
|
|
|
341
322
|
## Control plane — bind a monitoring proxy (human oversight of a fleet)
|
|
342
323
|
|
|
343
|
-
This is **separate** from the
|
|
324
|
+
This is **separate** from the wake-on-mail watch above. The control plane lets a **person's
|
|
344
325
|
web-messenger account** (the ours web messenger, shipping as part of the upcoming ours-control-plane)
|
|
345
326
|
oversee and command all agents under this host's **Human identity** from a **Control
|
|
346
327
|
Panel**: view a **live monitoring feed** of monitored agents' traffic, create agents, edit
|
|
@@ -389,9 +370,9 @@ requests, and each agent's monitoring ON/off. Works whenever the Human identity
|
|
|
389
370
|
event (sender + id + date) to `$OURS_STATE_DIR/<identity>/notifications.log` (the wake
|
|
390
371
|
signal `ours-mcp watch` reads) and refreshes a body-free `unread.json`. Text lives in the
|
|
391
372
|
packet and leaves it solely via `get_messages`.
|
|
392
|
-
- **The wake
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
373
|
+
- **The wake signal is uniform.** `ours-mcp watch <identity>` is the common stream; each harness
|
|
374
|
+
drives it in-session. Claude Code uses its native `Monitor` tool; **Codex uses autonomous watch
|
|
375
|
+
mode** — the agent holds a blocking `ours-mcp watch` via Codex's shell tool (or, since Codex is
|
|
376
|
+
turn-based, polls `get_messages` every ~5s) and reacts from that loop (see *Getting woken on new mail*).
|
|
377
|
+
The ours daemon, identities, and tools are identical across harnesses — only how the agent runs
|
|
378
|
+
the watch differs.
|
|
@@ -12,9 +12,9 @@ The daemon is a **shared, host-wide singleton** reachable only on `127.0.0.1`
|
|
|
12
12
|
| GC interval (ms) | `OURS_GC_INTERVAL_MS` | `gcIntervalMs` | `3600000` |
|
|
13
13
|
| Auto-start daemon | `OURS_AUTOSTART` | `autoStart` | `false` |
|
|
14
14
|
|
|
15
|
-
**The port is shared.** Any process that dials the daemon
|
|
16
|
-
|
|
17
|
-
binds the same port
|
|
15
|
+
**The port is shared.** Any process that dials the daemon — the `ours-mcp proxy` MCP
|
|
16
|
+
server and `ours-mcp watch` — connects to `127.0.0.1:<OURS_PORT>` and the daemon
|
|
17
|
+
binds the same port (both read `OURS_PORT`/`config.json`). Change it **once in
|
|
18
18
|
shared config**, never per-side, or a dialer won't find the daemon.
|
|
19
19
|
|
|
20
20
|
**Changing config (consent-first — never on your own initiative):**
|
package/reactivity/README.md
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# ours × Codex — OPTIONAL, non-native reactivity (`codex exec` gateway)
|
|
2
|
-
|
|
3
|
-
> **This is NOT native Codex reactivity.** Codex is a session/invocation CLI — no daemon,
|
|
4
|
-
> no webhook, no persistent monitor. The honest default for ours-on-Codex is
|
|
5
|
-
> **session-only**: the `ours` skill and the `~/.codex/AGENTS.md` pointer tell the agent to
|
|
6
|
-
> check `get_messages` when it goes live and whenever it expects a reply. This directory is
|
|
7
|
-
> an **opt-in** for people who want an external always-on wake and accept that it is bolted
|
|
8
|
-
> on, outside Codex's lifecycle — not a Codex feature.
|
|
9
|
-
|
|
10
|
-
## What it is
|
|
11
|
-
|
|
12
|
-
A small adaptation of `@ours.network/connector`'s reference gateway
|
|
13
|
-
(`connector-reference-handler.mjs`). Same webhook contract and per-identity coalescing +
|
|
14
|
-
backstop, but the DRAIN spawns **`codex exec`** — Codex's real non-interactive mode — with a
|
|
15
|
-
prompt to bind the woken identity and read/act on ours mail via the `ours` MCP tools.
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
ours-mcp watch <id> ──▶ connector-watch.sh ──HMAC POST──▶ codex-exec-gateway.mjs
|
|
19
|
-
(notifications.log) (OBSERVE, per id) /webhooks/ (WAKE + DRAIN)
|
|
20
|
-
ours-wake │
|
|
21
|
-
▼
|
|
22
|
-
codex exec --sandbox workspace-write
|
|
23
|
-
"<drain prompt>" → ours MCP tools → get_messages → acts
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
- **OBSERVE** — reuse the connector's `connector-watch.sh` (one `ours-mcp watch <id>` per
|
|
27
|
-
identity, non-binding, non-draining; pokes the gateway on each new message).
|
|
28
|
-
- **WAKE + DRAIN** — this file. HMAC-verifies the poke, then runs a **headless Codex** bound
|
|
29
|
-
to that identity (its sole drainer — ours binding is exclusive per identity).
|
|
30
|
-
|
|
31
|
-
## Requirements
|
|
32
|
-
|
|
33
|
-
- A Codex **API key** for automation (e.g. `CODEX_API_KEY`) — `codex exec` is non-interactive.
|
|
34
|
-
- The ours daemon running and the `ours` MCP server registered in `~/.codex/config.toml`
|
|
35
|
-
(the plugin's `install.sh` does this).
|
|
36
|
-
- A supervisor for **two always-on processes** you run yourself: the connector watcher and
|
|
37
|
-
this gateway. Neither is started by default.
|
|
38
|
-
|
|
39
|
-
## Run it
|
|
40
|
-
|
|
41
|
-
```sh
|
|
42
|
-
export CONNECTOR_IDENTITIES="Agent1 Agent2" # identities to drive
|
|
43
|
-
export CONNECTOR_HMAC_SECRET="$(openssl rand -hex 32)" # SAME secret both ends
|
|
44
|
-
export CONNECTOR_WEBHOOK_URL="http://localhost:8644/webhooks/ours-wake"
|
|
45
|
-
export CODEX_API_KEY="<your key>" # for headless codex exec
|
|
46
|
-
|
|
47
|
-
bash <connector>/connector-watch.sh & # OBSERVE (per identity)
|
|
48
|
-
node ./codex-exec-gateway.mjs # WAKE + DRAIN via `codex exec`
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Config (env, all overridable)
|
|
52
|
-
|
|
53
|
-
| var | default | purpose |
|
|
54
|
-
|---|---|---|
|
|
55
|
-
| `CONNECTOR_IDENTITIES` | `Peer` | space-separated identities this gateway drains |
|
|
56
|
-
| `CONNECTOR_HMAC_SECRET` | — | shared HMAC secret (must match the watcher; no default) |
|
|
57
|
-
| `CONNECTOR_WEBHOOK_URL` | `http://localhost:8644/webhooks/ours-wake` | webhook the watcher pokes |
|
|
58
|
-
| `CONNECTOR_EVENT` | `ours_wake` | event name (header + body) |
|
|
59
|
-
| `CONNECTOR_BACKSTOP_SECS` | `420` | per-identity missed-wake backstop interval |
|
|
60
|
-
| `CODEX_BIN` | `codex` | Codex CLI binary |
|
|
61
|
-
| `CODEX_SANDBOX` | `workspace-write` | `codex exec --sandbox` mode |
|
|
62
|
-
|
|
63
|
-
The gateway **refuses to start** unless `CONNECTOR_HMAC_SECRET` is a non-default value.
|
|
64
|
-
|
|
65
|
-
## Caveats
|
|
66
|
-
|
|
67
|
-
- Each wake is a **fresh Codex invocation** — there is no persistent session state between
|
|
68
|
-
wakes beyond what ours + the workspace persist. Cost/latency scale with wake volume.
|
|
69
|
-
- This runs Codex with a real API key and a writable sandbox. Review the drain prompt and
|
|
70
|
-
the sandbox mode before pointing it at anything sensitive.
|
|
71
|
-
- If you don't need external wake, don't run this — session-only reactivity is the default
|
|
72
|
-
and needs nothing here.
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// OPTIONAL, NON-NATIVE reactivity gateway for the OpenAI Codex CLI.
|
|
3
|
-
//
|
|
4
|
-
// ┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
5
|
-
// │ THIS IS NOT NATIVE CODEX REACTIVITY. Codex is a session/invocation CLI: no │
|
|
6
|
-
// │ daemon, no webhook, no persistent monitor. This gateway lives OUTSIDE Codex's own │
|
|
7
|
-
// │ lifecycle — you supervise it as an always-on process. On each ours wake it drives │
|
|
8
|
-
// │ Codex HEADLESSLY via `codex exec "<drain prompt>"`, which is Codex's real │
|
|
9
|
-
// │ non-interactive mode and needs an API key (e.g. CODEX_API_KEY) for automation. │
|
|
10
|
-
// │ The DEFAULT ours-on-Codex reactivity is session-only (the agent checks │
|
|
11
|
-
// │ get_messages when live / when it expects a reply). Use this only if you want an │
|
|
12
|
-
// │ external always-on wake mechanism and accept that it is bolted on, not native. │
|
|
13
|
-
// └─────────────────────────────────────────────────────────────────────────────────┘
|
|
14
|
-
//
|
|
15
|
-
// It is a small adaptation of @ours.network/connector's connector-reference-handler.mjs:
|
|
16
|
-
// same HMAC-verified webhook contract, same per-identity coalescing + backstop, but the
|
|
17
|
-
// per-identity DRAIN spawns `codex exec` with a prompt to read + act on ours mail (via the
|
|
18
|
-
// ours MCP tools that install.sh registered in ~/.codex/config.toml), instead of the
|
|
19
|
-
// inline JSON-RPC get_messages drain.
|
|
20
|
-
//
|
|
21
|
-
// Pair it with the connector's watcher for OBSERVE:
|
|
22
|
-
// bash <connector>/connector-watch.sh # ours-mcp watch <id> → HMAC POST per new message
|
|
23
|
-
// This file is the WAKE+DRAIN side.
|
|
24
|
-
//
|
|
25
|
-
// SOLE-DRAINER per identity: ours binding is exclusive per identity, so the codex exec run
|
|
26
|
-
// bound to <id> is the only drainer of <id>. N identities = N sole-drained inboxes on ONE
|
|
27
|
-
// shared ours daemon.
|
|
28
|
-
//
|
|
29
|
-
// Contract (config-overridable, must match the connector):
|
|
30
|
-
// POST <CONNECTOR_WEBHOOK_URL> body: {"event_type":"<CONNECTOR_EVENT>","event":"<CONNECTOR_EVENT>","identity":"<id>"}
|
|
31
|
-
// header: X-GitHub-Event: <CONNECTOR_EVENT>
|
|
32
|
-
// header: X-Hub-Signature-256: sha256=<hex HMAC-SHA256(body, CONNECTOR_HMAC_SECRET)>
|
|
33
|
-
// reply: <CONNECTOR_WEBHOOK_OK_CODE> (200) accept; 401 bad signature; 400 unknown identity.
|
|
34
|
-
// Refuses to start unless CONNECTOR_HMAC_SECRET is set to a non-default value.
|
|
35
|
-
import http from 'node:http';
|
|
36
|
-
import crypto from 'node:crypto';
|
|
37
|
-
import { spawn } from 'node:child_process';
|
|
38
|
-
|
|
39
|
-
process.on('unhandledRejection', e => console.error('[codex-gw] unhandledRejection:', e?.message || e));
|
|
40
|
-
process.on('uncaughtException', e => console.error('[codex-gw] uncaughtException:', e?.message || e));
|
|
41
|
-
|
|
42
|
-
const WURL = new URL(process.env.CONNECTOR_WEBHOOK_URL || 'http://localhost:8644/webhooks/ours-wake');
|
|
43
|
-
const URL_PATH = WURL.pathname, PORT = Number(WURL.port || 8644);
|
|
44
|
-
const SECRET = process.env.CONNECTOR_HMAC_SECRET || '';
|
|
45
|
-
if (!SECRET || SECRET === 'CHANGE_ME_local_webhook_hmac') {
|
|
46
|
-
console.error('[codex-gw] refusing to start: set CONNECTOR_HMAC_SECRET to a non-default value ' +
|
|
47
|
-
'(missing or the placeholder default is insecure — anyone could forge a wake).');
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
const OK = Number(process.env.CONNECTOR_WEBHOOK_OK_CODE || 200);
|
|
51
|
-
// Codex non-interactive binary + args. `codex exec` is Codex's headless mode; we run with a
|
|
52
|
-
// workspace-write sandbox so the agent can act, and pass the drain prompt as the final arg.
|
|
53
|
-
const CODEX_BIN = process.env.CODEX_BIN || 'codex';
|
|
54
|
-
const CODEX_SANDBOX = process.env.CODEX_SANDBOX || 'workspace-write';
|
|
55
|
-
const IDENTITIES = new Set((process.env.CONNECTOR_IDENTITIES || process.env.CONNECTOR_IDENTITY || 'Peer').split(/\s+/).filter(Boolean));
|
|
56
|
-
const BACKSTOP_MS = Number(process.env.CONNECTOR_BACKSTOP_SECS || 420) * 1000;
|
|
57
|
-
|
|
58
|
-
const state = new Map(); // id -> {draining, again}
|
|
59
|
-
for (const id of IDENTITIES) state.set(id, { draining: false, again: false });
|
|
60
|
-
|
|
61
|
-
// The prompt Codex runs headlessly. It leans on the ours MCP tools (registered in
|
|
62
|
-
// ~/.codex/config.toml) + the ours skill: bind <id>, then read and act on new mail.
|
|
63
|
-
function drainPrompt(id) {
|
|
64
|
-
return `New ours.network mail arrived for identity "${id}". Use the ours skill and the ` +
|
|
65
|
-
`ours MCP tools: bind that identity with choose_identity({ name: "${id}" }) if it is not ` +
|
|
66
|
-
`already bound, then call get_messages to read the new message(s) and act on them. Reply ` +
|
|
67
|
-
`over ours (send_message) if a reply is expected. Do not adopt the identity's persona.`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function codexExec(id) { // one headless Codex run bound to <id> (its sole drainer)
|
|
71
|
-
return new Promise((resolve) => {
|
|
72
|
-
const args = ['exec', '--sandbox', CODEX_SANDBOX, drainPrompt(id)];
|
|
73
|
-
const proc = spawn(CODEX_BIN, args, { env: { ...process.env }, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
74
|
-
let out = '';
|
|
75
|
-
proc.stdout.on('data', d => { out += d; });
|
|
76
|
-
proc.stderr.on('data', d => console.error(`[codex-gw:${id}] ${String(d).trimEnd()}`));
|
|
77
|
-
proc.on('error', e => { console.error(`[codex-gw:${id}] codex spawn error:`, e.message); resolve(''); });
|
|
78
|
-
proc.on('close', code => {
|
|
79
|
-
if (out.trim()) console.log(`[codex-gw:${id}] codex exec (rc=${code}):\n${out.trim()}`);
|
|
80
|
-
resolve(out);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function drain(id) { // coalesced per-identity
|
|
86
|
-
const s = state.get(id); if (!s) return;
|
|
87
|
-
if (s.draining) { s.again = true; return; }
|
|
88
|
-
s.draining = true;
|
|
89
|
-
do {
|
|
90
|
-
s.again = false;
|
|
91
|
-
try { await codexExec(id); } catch (e) { console.error(`[codex-gw:${id}] drain error:`, e?.message || e); }
|
|
92
|
-
} while (s.again);
|
|
93
|
-
s.draining = false;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
http.createServer((req, res) => {
|
|
97
|
-
if (req.method !== 'POST' || req.url !== URL_PATH) { res.writeHead(404).end(); return; }
|
|
98
|
-
let body = ''; req.on('data', c => body += c); req.on('end', () => {
|
|
99
|
-
const sig = (req.headers['x-hub-signature-256'] || '').replace(/^sha256=/, '');
|
|
100
|
-
const good = crypto.createHmac('sha256', SECRET).update(body).digest('hex');
|
|
101
|
-
const okSig = sig.length === good.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(good));
|
|
102
|
-
if (!okSig) { res.writeHead(401).end('bad sig'); return; }
|
|
103
|
-
let id; try { id = JSON.parse(body).identity; } catch {}
|
|
104
|
-
if (!id || !IDENTITIES.has(id)) { res.writeHead(400).end('unknown identity'); return; }
|
|
105
|
-
res.writeHead(OK).end(); // ack fast; drain async + coalesced
|
|
106
|
-
drain(id).catch(e => console.error(`[codex-gw:${id}] drain error`, e));
|
|
107
|
-
});
|
|
108
|
-
}).listen(PORT, () => console.log(
|
|
109
|
-
`[codex-gw] NON-NATIVE codex-exec gateway on :${PORT}${URL_PATH} — sole-drainer for [${[...IDENTITIES].join(', ')}]`));
|
|
110
|
-
|
|
111
|
-
// per-identity missed-wake backstop (coalesced with wakes; each run is a fresh codex exec
|
|
112
|
-
// that no-ops cheaply if there is no new mail).
|
|
113
|
-
setInterval(() => { for (const id of IDENTITIES) drain(id).catch(() => {}); }, BACKSTOP_MS);
|