@ours.network/codex 1.2.0-nightly.7 → 1.2.0-nightly.8

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/dist/profile.mjs CHANGED
@@ -1,116 +1,11 @@
1
1
  // src/profile.mjs
2
- import { join } from "node:path";
3
- import { homedir as homedir2 } from "node:os";
4
2
  import { randomUUID } from "node:crypto";
5
- import { assertDaemonStateDir, attachOursClient as attachOursClient2, resolveDaemonConfig } from "@ours.network/sdk";
3
+ import { attachOursClient as attachOursClient2 } from "@ours.network/sdk";
6
4
 
7
5
  // ../core/src/host-profile.ts
8
- import { existsSync, lstatSync, readFileSync } from "node:fs";
9
- import { homedir } from "node:os";
10
- import { resolve } from "node:path";
11
- import { normalizeDaemonEndpoint } from "@ours.network/sdk/client";
12
- var PROFILE_KEYS = ["endpoint", "expectedInstanceId", "credentialPath"];
13
- var LEGACY_SELECTION_KEYS = ["port", "stateDir", "apiToken", "apiVisibility"];
14
- var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
15
- var CONFLICTING_ENV = ["OURS_API_TOKEN", "OURS_PORT", "OURS_STATE_DIR", "OURS_DAEMON_ID"];
16
- function profileError(reason) {
17
- return new Error(`Invalid external host profile: ${reason}`);
18
- }
19
- function validateHostProfile(value) {
20
- if (!value || typeof value !== "object" || Array.isArray(value)) {
21
- throw profileError("expected a complete host profile object.");
22
- }
23
- const record = value;
24
- const present = PROFILE_KEYS.filter((key) => Object.hasOwn(record, key));
25
- if (present.length !== PROFILE_KEYS.length) {
26
- throw profileError("expected a complete host profile tuple: endpoint, expectedInstanceId, credentialPath.");
27
- }
28
- const mixed = LEGACY_SELECTION_KEYS.filter((key) => Object.hasOwn(record, key));
29
- if (mixed.length) throw profileError(`legacy selection keys cannot be mixed with a host profile (${mixed.join(", ")}).`);
30
- const endpoint = record.endpoint;
31
- const expectedInstanceId = record.expectedInstanceId;
32
- const credentialPath = record.credentialPath;
33
- if (typeof endpoint !== "string" || endpoint.trim() !== endpoint || endpoint === "") {
34
- throw profileError("endpoint must be a non-empty HTTP or HTTPS base URL.");
35
- }
36
- if (typeof expectedInstanceId !== "string" || !UUID.test(expectedInstanceId)) {
37
- throw profileError("expectedInstanceId must be a lowercase UUID.");
38
- }
39
- if (typeof credentialPath !== "string" || credentialPath === "" || !credentialPath.startsWith("/") || resolve(credentialPath) !== credentialPath) {
40
- throw profileError("credentialPath must be a normalized absolute path.");
41
- }
42
- let url;
43
- try {
44
- url = new URL(endpoint);
45
- } catch {
46
- throw profileError("endpoint must be an HTTP or HTTPS base URL.");
47
- }
48
- if (url.protocol !== "http:" && url.protocol !== "https:" || url.username || url.password || /[\s\\?#]/.test(endpoint) || url.search || url.hash) {
49
- throw profileError("endpoint must be an HTTP or HTTPS base URL without credentials, query, or fragment.");
50
- }
51
- return { endpoint: normalizeDaemonEndpoint(endpoint), expectedInstanceId, credentialPath };
52
- }
53
- function readProfileObject(configPath) {
54
- let text;
55
- try {
56
- text = readFileSync(configPath, "utf8");
57
- } catch {
58
- throw new Error(`Cannot read host profile ${JSON.stringify(configPath)}.`);
59
- }
60
- let value;
61
- try {
62
- value = JSON.parse(text);
63
- } catch {
64
- throw profileError(`config ${JSON.stringify(configPath)} is not valid JSON.`);
65
- }
66
- if (!value || typeof value !== "object" || Array.isArray(value)) throw profileError(`config ${JSON.stringify(configPath)} must be an object.`);
67
- return value;
68
- }
69
- function assertPrivateRegularFile(configPath) {
70
- let stat;
71
- try {
72
- stat = lstatSync(configPath);
73
- } catch {
74
- throw new Error(`Cannot read host profile ${JSON.stringify(configPath)}.`);
75
- }
76
- if (!stat.isFile()) throw profileError(`config ${JSON.stringify(configPath)} must be a regular file.`);
77
- const currentUid = process.getuid?.();
78
- if (currentUid === void 0 || stat.uid !== currentUid) throw profileError(`config ${JSON.stringify(configPath)} must be owned by the current user.`);
79
- if ((stat.mode & 63) !== 0) throw profileError(`config ${JSON.stringify(configPath)} must have private permissions.`);
80
- }
81
- function readHostProfile(configPath) {
82
- const record = readProfileObject(configPath);
83
- const found = PROFILE_KEYS.filter((key) => Object.hasOwn(record, key));
84
- if (found.length === 0) return null;
85
- assertPrivateRegularFile(configPath);
86
- return validateHostProfile(record);
87
- }
88
- function nonempty(env, key) {
89
- return (env[key] ?? "").trim() !== "";
90
- }
6
+ import { readGatewayClientProfile, validateGatewayClientProfile } from "@ours.network/sdk/client";
91
7
  function hostProfileSelectionFromEnv(env = process.env) {
92
- const explicit = (env.OURS_CONFIG ?? "").trim();
93
- const managedPath = resolve(env.HOME || homedir(), ".ours-client", "profile.json");
94
- let configPath = explicit;
95
- if (!configPath) {
96
- try {
97
- lstatSync(managedPath);
98
- configPath = managedPath;
99
- } catch (error) {
100
- if (error.code !== "ENOENT") {
101
- throw new Error(`Cannot read host profile ${JSON.stringify(managedPath)}.`);
102
- }
103
- configPath = resolve(homedir(), ".ours", "config.json");
104
- if (!existsSync(configPath)) return null;
105
- }
106
- }
107
- const profile = readHostProfile(configPath);
108
- if (profile === null) {
109
- if (resolve(configPath) === managedPath) throw profileError("managed client config must contain a complete host profile tuple.");
110
- return null;
111
- }
112
- const conflicting = CONFLICTING_ENV.filter((key) => nonempty(env, key));
113
- if (conflicting.length) throw profileError(`${conflicting.join(", ")} conflicts with host-profile mode.`);
8
+ const { configPath, ...profile } = readGatewayClientProfile(env);
114
9
  return { profile, configPath };
115
10
  }
116
11
 
@@ -142,53 +37,18 @@ function parseOursArgs(argv = []) {
142
37
  async function resolveDaemonProfile({ argv = [], env = process.env, fetch: fetchImpl = globalThis.fetch, attach = attachOursClient2 } = {}) {
143
38
  const parsed = parseOursArgs(argv);
144
39
  const hostSelection = hostProfileSelectionFromEnv(env);
145
- if (hostSelection !== null) {
146
- const { profile: hostProfile, configPath } = hostSelection;
147
- if (parsed.port != null) throw new Error("ours: --ours-port conflicts with host-profile mode");
148
- const client = await attach({ ...hostProfile, sessionMode: "external", leaseToken: randomUUID(), env: {} });
149
- let info2;
150
- try {
151
- info2 = await client.version();
152
- await client.identities();
153
- await client.unread();
154
- } finally {
155
- await client.close();
156
- }
157
- return { profile: hostProfile, info: info2, baseUrl: hostProfile.endpoint, configPath, codexArgs: parsed.codexArgs };
158
- }
159
- const selection = resolveDaemonConfig({
160
- ...parsed.port == null ? {} : { port: parsed.port },
161
- env,
162
- homeDir: env.HOME || homedir2()
163
- });
164
- await assertDaemonStateDir(selection, { fetch: fetchImpl, timeoutMs: 2e3 });
165
- const baseUrl = selection.baseUrl.value;
166
- const token = selection.token?.value ?? null;
167
- const headers = token ? { "x-ours-api-token": token } : {};
168
- const response = await fetchImpl(`${baseUrl}/info`, { signal: AbortSignal.timeout(2e3) });
169
- if (!response.ok) throw new Error(`ours daemon at ${baseUrl} returned HTTP ${response.status}`);
170
- const info = await response.json();
171
- if (info?.name !== "ours" || !Number.isInteger(info?.protocol) || info.protocol < 1) {
172
- throw new Error(`incompatible service at ${baseUrl}; expected an ours daemon with notification protocol 1`);
173
- }
174
- for (const path of ["/identities", "/unread"]) {
175
- const capability = await fetchImpl(`${baseUrl}${path}`, { headers, signal: AbortSignal.timeout(2e3) });
176
- if (capability.status === 401 || capability.status === 403) {
177
- throw new Error(`ours daemon authentication failed at ${baseUrl}; supply the matching coherent selection`);
178
- }
179
- if (!capability.ok) throw new Error(`selected daemon lacks ${path} (HTTP ${capability.status})`);
40
+ const { profile: hostProfile, configPath } = hostSelection;
41
+ if (parsed.port != null) throw new Error("ours: --ours-port conflicts with host-profile mode");
42
+ const client = await attach({ ...hostProfile, sessionMode: "external", leaseToken: randomUUID(), env: {} });
43
+ let info;
44
+ try {
45
+ info = await client.version();
46
+ await client.identities();
47
+ await client.unread();
48
+ } finally {
49
+ await client.close();
180
50
  }
181
- return {
182
- port: selection.port.value,
183
- stateDir: selection.expectStateDir,
184
- token,
185
- visibility: "owner",
186
- source: selection.baseUrl.source,
187
- info,
188
- baseUrl,
189
- configPath: selection.configPath?.value ?? join(selection.expectStateDir, "config.json"),
190
- codexArgs: parsed.codexArgs
191
- };
51
+ return { profile: hostProfile, info, baseUrl: hostProfile.endpoint, configPath, codexArgs: parsed.codexArgs };
192
52
  }
193
53
  export {
194
54
  parseOursArgs,
package/install.sh CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # Install the native ours.network plugin into the OpenAI Codex CLI:
3
- # 1. ensure the ours operator CLI and MCP adapter are installed; start the shared daemon
3
+ # 1. verify the installed gateway client profile before modifying plugin configuration
4
4
  # 2. add/upgrade adapt-toolkit/ours-codex-marketplace
5
5
  # 3. install the native `ours` plugin (skills, MCP servers, and hooks)
6
6
  # 4. back up and remove installer-owned legacy config only after verification
@@ -10,7 +10,6 @@
10
10
  # SKILLS_DIR skills root (default ~/.agents/skills)
11
11
  # CODEX_CONFIG config.toml path (default $CODEX_DIR/config.toml)
12
12
  # CODEX_AGENTS AGENTS.md path (default $CODEX_DIR/AGENTS.md)
13
- # OURS_INSTALL_SKIP_DAEMON=1 skip daemon install/start
14
13
  set -euo pipefail
15
14
 
16
15
  SELFDIR="$(cd "$(dirname "$0")" && pwd)"
@@ -21,33 +20,21 @@ SKILLS_DIR="${SKILLS_DIR:-$HOME/.agents/skills}"
21
20
 
22
21
  say(){ printf 'ours-install: %s\n' "$1"; }
23
22
 
24
- # Ensure the ours daemon is on @latest (UPGRADE, not install-if-missing): an already-present
25
- # daemon must still be pulled up to the newest published version. Record the CLI version
26
- # before/after; start if not running, restart only if the version actually changed.
27
- ensure_daemon_latest(){
28
- if [ "${OURS_INSTALL_SKIP_DAEMON:-}" = "1" ]; then say "skipping daemon step (OURS_INSTALL_SKIP_DAEMON=1)"; return 0; fi
29
- local before after
30
- before="$(ours version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
31
- say "ensuring @ours.network/cli@latest and @ours.network/mcp@latest…"
32
- npm i -g @ours.network/cli@latest @ours.network/mcp@latest
33
- after="$(ours version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
34
- if ! ours daemon status >/dev/null 2>&1; then
35
- say "starting the ours daemon…"
36
- if ! ours daemon start; then say "could not start the ours daemon; installation stopped."; return 1; fi
37
- elif [ -n "$before" ] && [ "$before" != "$after" ]; then
38
- say "operator CLI upgraded (v${before} → v${after}) — restarting its daemon…"
39
- if ! ours daemon restart; then
40
- say "restart failed; trying a clean start…"
41
- if ! ours daemon start; then say "could not restart or start the upgraded daemon; installation stopped."; return 1; fi
42
- fi
43
- else
44
- say "daemon already current (v${after:-unknown})."
23
+ # Client installation never installs, starts or restarts server services.
24
+ require_gateway_client(){
25
+ if ! command -v ours >/dev/null 2>&1 || ! command -v ours-mcp >/dev/null 2>&1; then
26
+ say "Install gateway clients with ours-install client first; ours and ours-mcp are required."
27
+ return 1
28
+ fi
29
+ local contract
30
+ if ! contract="$(ours-mcp verify-client-profile)" || [ "$contract" != "ours.gateway-client-profile-v1" ]; then
31
+ say "Configure the shared gateway profile with ours-install client first; plugin setup was left unchanged."
32
+ return 1
45
33
  fi
46
- say "operator CLI: $(command -v ours) (v${after:-unknown}); MCP adapter: $(command -v ours-mcp)"
47
34
  }
48
35
 
49
- # --- 1) daemon (ensure @latest + restart on change) ---
50
- ensure_daemon_latest
36
+ # --- 1) shared gateway client preflight ---
37
+ require_gateway_client
51
38
 
52
39
  # --- native Codex plugin ------------------------------------------------------
53
40
  # Codex owns the plugin cache and hook-trust workflow. Only remove the legacy
@@ -73,7 +60,7 @@ if [ "${OURS_CODEX_SKIP_NATIVE:-}" != "1" ]; then
73
60
  CODEX_DIR="$CODEX_DIR" SKILLS_DIR="$SKILLS_DIR" node "$SELFDIR/bin/codex-legacy-cleanup.mjs"
74
61
  say "native plugin installed. Review and trust its hooks in Codex, then start a new thread."
75
62
  say "standard mode: codex"
76
- say "live mode: ours-codex${OURS_PORT:+ --ours-port $OURS_PORT}"
63
+ say "live mode: ours-codex"
77
64
  exit 0
78
65
  fi
79
66
 
@@ -93,11 +80,5 @@ CODEX_CONFIG="$CODEX_CONFIG" node "$SELFDIR/bin/codex-config-install.mjs"
93
80
  CODEX_AGENTS="$CODEX_AGENTS" node "$SELFDIR/bin/codex-agents-install.mjs"
94
81
 
95
82
  say "done. The ours MCP server + skill are live for the next Codex session."
96
- # --- version echo: show the user they are on latest ---
97
- if [ "${OURS_INSTALL_SKIP_DAEMON:-}" != "1" ]; then
98
- say "versions:"
99
- say " MCP adapter: $(ours-mcp --version 2>/dev/null | head -1 || echo 'unknown')"
100
- say " plugin: $(npm ls -g @ours.network/codex 2>/dev/null | grep -oE '@ours\.network/codex@[0-9][0-9.]*' | head -1 || echo '@ours.network/codex (not a global install)')"
101
- fi
102
83
  say "next: bind (or create) an identity, then the ours skill tails ours-mcp watch (or polls"
103
84
  say " get_messages) in-session so you react to new mail while you work."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/codex",
3
- "version": "1.2.0-nightly.7",
3
+ "version": "1.2.0-nightly.8",
4
4
  "description": "Native Codex plugin for secure ours.network messaging and explicitly armed, session-scoped live mail wake.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",
@@ -46,10 +46,10 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@modelcontextprotocol/sdk": "^1.29.0",
49
- "@ours.network/sdk": "3.8.1-nightly.11",
49
+ "@ours.network/sdk": "3.8.1-nightly.13",
50
50
  "ws": "^8.21.0",
51
51
  "zod": "^3.25.76",
52
- "@ours.network/mcp": "1.2.0-nightly.7"
52
+ "@ours.network/mcp": "1.2.0-nightly.8"
53
53
  },
54
54
  "devDependencies": {
55
55
  "esbuild": "^0.24.2"
@@ -108,12 +108,12 @@ existing file.
108
108
  **control-plane monitoring proxy is not available in this release** — there is no tool
109
109
  to call. See "Control plane" below.
110
110
 
111
- - **Configuration.** Port, state dir, broker, and GC interval are configurable.
112
- More than one daemon may run when each uses a distinct port and state directory.
113
- Select live mode with `ours-codex --ours-port <port>` or `OURS_CONFIG`. Never
114
- self-configure on your own initiative: surface the
115
- need, explain the impact, and act only on the user's explicit yes. Details:
116
- `references/configuration.md`.
111
+ - **Configuration.** Every client selects the HTTP gateway through
112
+ `~/.ours-client/profile.json` (or the whole-profile `OURS_CONFIG` override).
113
+ `serverUrl` is the base URL; daemon and Cowork use `/daemon` and `/cowork`
114
+ beneath it. Import the issued credential with `ours-install client`.
115
+ Local daemon ports, state directories, and Unix sockets are not client routes.
116
+ Missing or invalid configuration fails closed. See `references/configuration.md`.
117
117
 
118
118
  ## ours-fleet — persistent harness roles
119
119
 
@@ -1,26 +1,24 @@
1
- # ours daemon configuration
1
+ # Shared gateway client configuration
2
2
 
3
- ours-mcp is a client of one already-running shared daemon. The operator CLI owns
4
- configuration and lifecycle:
3
+ Fleet, ours CLI, MCP and harness hooks select one server through
4
+ `~/.ours-client/profile.json`. `serverUrl` names the HTTP(S) gateway, with
5
+ optional base path. Daemon operations use `/daemon`; Cowork uses `/cowork`.
6
+ The profile also pins `expectedInstanceId` and an absolute `credentialPath`.
7
+ An optional `endpoint` must equal `serverUrl + "/daemon"`.
5
8
 
6
- ```sh
7
- ours config show --json
8
- ours config setup --port 3050 --state-dir "$HOME/.ours"
9
- ours daemon start
10
- ours daemon status --json
11
- ```
9
+ Import a prepared private profile with `ours-install client --config /absolute/profile.json`.
10
+ Use `ours config show --json` to inspect connection metadata without reading the token.
11
+ Keep the profile directory mode 0700 and files mode 0600. `OURS_CONFIG` may
12
+ select the same whole profile for all clients. Remove legacy `OURS_PORT`,
13
+ `OURS_STATE_DIR`, `OURS_API_TOKEN` and `OURS_DAEMON_ID` overrides.
12
14
 
13
- The MCP adapter and `ours-codex` use the published SDK's coherent selection. The
14
- wholly default selection is port 3050 with state directory `~/.ours`. For another
15
- daemon, set `OURS_CONFIG`, or set matching `OURS_PORT` and `OURS_STATE_DIR`. A
16
- token or endpoint selection must be paired with its state directory. `/state-dir`
17
- is verified before credentials are sent.
15
+ Missing profiles, rejected credentials and unavailable gateways fail closed.
16
+ Clients never read daemon state, start a daemon, or try a local port/socket.
17
+ Hooks use the same gateway and return a harmless no-op on failure. Their
18
+ application-local identity/session files do not select another server.
18
19
 
19
- The adapter never starts a daemon and never falls back to an embedded one.
20
- `OURS_INSTANCE`, `--application`, and old ours-mcp daemon variables are errors.
21
- Standard and live Codex mode use the same selection; live mode only adds an
22
- explicitly armed, session-scoped wake monitor.
23
-
24
- Changing daemon configuration or restarting the shared daemon affects every
25
- connected application. Explain that blast radius and obtain the user's consent
26
- before making operator-level changes.
20
+ For full-stack setup, a custom public port (such as 4050), systemd propagation
21
+ and migration, see the [gateway setup guide](https://github.com/adapt-toolkit/ours-network/blob/prerelease/packages/installer/GATEWAY_SETUP.md).
22
+ The coordinated gateway changes require approved matching package releases;
23
+ current npm nightly is not automatically evidence that these changes are installed.
24
+ Server administration uses `ours-install server` on the server host.
@@ -35,16 +35,6 @@ const safeUnread = (value) => (Array.isArray(value?.identities) ? value.identiti
35
35
  return [{ name: entry.name, count, files, recent }];
36
36
  });
37
37
 
38
- async function applicationIdentityNames(env, selection) {
39
- const path = env.OURS_MCP_CONFIG || join(homedir(), '.ours-mcp', 'config.json');
40
- const value = JSON.parse(await readFile(path, 'utf8'));
41
- if (value?.version !== 1 || !value.daemons || typeof value.daemons !== 'object') return new Set();
42
- const identities = selection.instanceId
43
- ? value.instances?.[selection.instanceId]?.identities
44
- : value.daemons[resolve(selection.stateDir)]?.identities;
45
- return new Set(Array.isArray(identities) ? identities.filter((name) => typeof name === 'string') : []);
46
- }
47
-
48
38
  function renderContext(unread, pin) {
49
39
  const lines = [];
50
40
  if (unread.length) {
@@ -78,9 +68,6 @@ export async function handleHook(payload, { env = process.env, fetch: fetchImpl
78
68
  if (event === 'SessionStart' && socket && capability && payload.session_id && payload.cwd) {
79
69
  await send(socket, capability, { command: 'register_session', sessionId: payload.session_id, threadId: payload.session_id, cwd: payload.cwd });
80
70
  }
81
- let port = env.OURS_PORT || '3050';
82
- let selectedStateDir = resolve(env.OURS_STATE_DIR || join(homedir(), '.ours'));
83
- let selectedToken = env.OURS_API_TOKEN || '';
84
71
  let hostProfile;
85
72
  try { hostProfile = hostProfileFromEnv(env); }
86
73
  catch { return { continue: true }; }
@@ -94,41 +81,7 @@ export async function handleHook(payload, { env = process.env, fetch: fetchImpl
94
81
  return context ? { continue: true, hookSpecificOutput: { hookEventName: event, additionalContext: context } } : { continue: true };
95
82
  }
96
83
 
97
- // Standard Codex does not pass through ours-codex's resolved environment.
98
- // Resolve the same coherent SDK selection here so SessionStart and
99
- // UserPromptSubmit inspect the same daemon as the stdio proxy.
100
- try {
101
- const selected = await profileResolver({ env, fetch: fetchImpl });
102
- if (selected.profile) {
103
- let client;
104
- try {
105
- client = await clientFactory({ ...selected.profile, sessionMode: 'external', leaseToken: `codex-hook-${process.pid}`, env: {} });
106
- const visible = await applicationIdentityNames(env, { instanceId: selected.profile.expectedInstanceId });
107
- const unread = safeUnread(await client.unread()).filter((entry) => visible.has(entry.name));
108
- const pin = await findPin(payload.cwd || process.cwd());
109
- const context = renderContext(unread, pin);
110
- return context ? { continue: true, hookSpecificOutput: { hookEventName: event, additionalContext: context } } : { continue: true };
111
- } finally { await client?.close(); }
112
- }
113
- port = String(selected.port);
114
- selectedToken = selected.token || '';
115
- selectedStateDir = resolve(selected.stateDir);
116
- } catch {
117
- /* proxy/launcher owns legacy diagnostics; hooks emit a benign no-op */
118
- }
119
- const headers = selectedToken ? { 'x-ours-api-token': selectedToken } : {};
120
- let unread = [];
121
- try {
122
- const response = await fetchImpl(`http://127.0.0.1:${port}/unread`, { headers, signal: AbortSignal.timeout(1500) });
123
- if (response.ok) {
124
- const visible = await applicationIdentityNames(env, { stateDir: selectedStateDir });
125
- unread = safeUnread(await response.json()).filter((entry) => visible.has(entry.name));
126
- }
127
- } catch { /* daemon diagnostics belong to launcher/proxy */ }
128
- const pin = await findPin(payload.cwd || process.cwd());
129
- const context = renderContext(unread, pin);
130
- if (!context) return { continue: true };
131
- return { continue: true, hookSpecificOutput: { hookEventName: event, additionalContext: context } };
84
+ return { continue: true };
132
85
  } catch { return { continue: true }; }
133
86
  }
134
87
 
package/src/launcher.mjs CHANGED
@@ -31,15 +31,7 @@ export function launcherEnvironment(env, profile, control) {
31
31
  // call. The bin shim preserves an explicit value instead of replacing it
32
32
  // with whichever child happened to spawn it.
33
33
  };
34
- if (profile.profile) {
35
- for (const key of ['OURS_PORT', 'OURS_API_TOKEN', 'OURS_STATE_DIR', 'OURS_CLIENT_PID', 'OURS_AUTOSTART', 'OURS_DAEMON_ID']) delete out[key];
36
- } else {
37
- out.OURS_PORT = String(profile.port);
38
- out.OURS_AUTOSTART = '0';
39
- out.OURS_CLIENT_PID = String(process.pid);
40
- if (profile.token) out.OURS_API_TOKEN = profile.token;
41
- else delete out.OURS_API_TOKEN;
42
- }
34
+ for (const key of ['OURS_PORT', 'OURS_API_TOKEN', 'OURS_STATE_DIR', 'OURS_CLIENT_PID', 'OURS_AUTOSTART', 'OURS_DAEMON_ID', 'OURS_DAEMON_URL', 'OURS_DAEMON_CREDENTIAL_PATH']) delete out[key];
43
35
  return out;
44
36
  }
45
37
 
package/src/profile.mjs CHANGED
@@ -1,8 +1,6 @@
1
- import { join } from 'node:path';
2
- import { homedir } from 'node:os';
3
1
  import { randomUUID } from 'node:crypto';
4
2
 
5
- import { assertDaemonStateDir, attachOursClient, resolveDaemonConfig } from '@ours.network/sdk';
3
+ import { attachOursClient } from '@ours.network/sdk';
6
4
  import { hostProfileSelectionFromEnv } from '../../core/src/host-client/index.ts';
7
5
 
8
6
  function validPort(value) {
@@ -31,7 +29,6 @@ export function parseOursArgs(argv = []) {
31
29
  export async function resolveDaemonProfile({ argv = [], env = process.env, fetch: fetchImpl = globalThis.fetch, attach = attachOursClient } = {}) {
32
30
  const parsed = parseOursArgs(argv);
33
31
  const hostSelection = hostProfileSelectionFromEnv(env);
34
- if (hostSelection !== null) {
35
32
  const { profile: hostProfile, configPath } = hostSelection;
36
33
  if (parsed.port != null) throw new Error('ours: --ours-port conflicts with host-profile mode');
37
34
  const client = await attach({ ...hostProfile, sessionMode: 'external', leaseToken: randomUUID(), env: {} });
@@ -42,40 +39,4 @@ export async function resolveDaemonProfile({ argv = [], env = process.env, fetch
42
39
  await client.unread();
43
40
  } finally { await client.close(); }
44
41
  return { profile: hostProfile, info, baseUrl: hostProfile.endpoint, configPath, codexArgs: parsed.codexArgs };
45
- }
46
- const selection = resolveDaemonConfig({
47
- ...(parsed.port == null ? {} : { port: parsed.port }),
48
- env,
49
- homeDir: env.HOME || homedir(),
50
- });
51
- await assertDaemonStateDir(selection, { fetch: fetchImpl, timeoutMs: 2000 });
52
-
53
- const baseUrl = selection.baseUrl.value;
54
- const token = selection.token?.value ?? null;
55
- const headers = token ? { 'x-ours-api-token': token } : {};
56
- const response = await fetchImpl(`${baseUrl}/info`, { signal: AbortSignal.timeout(2000) });
57
- if (!response.ok) throw new Error(`ours daemon at ${baseUrl} returned HTTP ${response.status}`);
58
- const info = await response.json();
59
- if (info?.name !== 'ours' || !Number.isInteger(info?.protocol) || info.protocol < 1) {
60
- throw new Error(`incompatible service at ${baseUrl}; expected an ours daemon with notification protocol 1`);
61
- }
62
- for (const path of ['/identities', '/unread']) {
63
- const capability = await fetchImpl(`${baseUrl}${path}`, { headers, signal: AbortSignal.timeout(2000) });
64
- if (capability.status === 401 || capability.status === 403) {
65
- throw new Error(`ours daemon authentication failed at ${baseUrl}; supply the matching coherent selection`);
66
- }
67
- if (!capability.ok) throw new Error(`selected daemon lacks ${path} (HTTP ${capability.status})`);
68
- }
69
-
70
- return {
71
- port: selection.port.value,
72
- stateDir: selection.expectStateDir,
73
- token,
74
- visibility: 'owner',
75
- source: selection.baseUrl.source,
76
- info,
77
- baseUrl,
78
- configPath: selection.configPath?.value ?? join(selection.expectStateDir, 'config.json'),
79
- codexArgs: parsed.codexArgs,
80
- };
81
42
  }