fix: require gateway client id
# Conflicts: # apps/macos/Sources/Clawdbot/GatewayChannel.swift # docs/concepts/typebox.md # docs/gateway/index.md # src/commands/onboard-non-interactive.gateway-auth.test.ts # src/commands/onboard-non-interactive.lan-auto-token.test.ts # src/gateway/call.ts # src/gateway/client.ts # src/gateway/gateway.wizard.e2e.test.ts # src/gateway/probe.ts # src/gateway/protocol/schema.ts # src/gateway/server.auth.test.ts # src/gateway/server.health.test.ts # src/gateway/server.ts # src/gateway/test-helpers.ts # src/tui/gateway-chat.ts
This commit is contained in:
@@ -18,12 +18,12 @@ provide quick operator visibility.
|
|||||||
|
|
||||||
Presence entries are structured objects with fields like:
|
Presence entries are structured objects with fields like:
|
||||||
|
|
||||||
- `instanceId` (optional but strongly recommended): stable client identity
|
- `instanceId` (optional but strongly recommended): stable client identity (usually `connect.client.instanceId`)
|
||||||
- `host`: human‑friendly host name
|
- `host`: human‑friendly host name
|
||||||
- `ip`: best‑effort IP address
|
- `ip`: best‑effort IP address
|
||||||
- `version`: client version string
|
- `version`: client version string
|
||||||
- `deviceFamily` / `modelIdentifier`: hardware hints
|
- `deviceFamily` / `modelIdentifier`: hardware hints
|
||||||
- `mode`: `gateway`, `app`, `webchat`, `cli`, `node`, ...
|
- `mode`: `ui`, `webchat`, `cli`, `backend`, `probe`, `test`, `node`, ...
|
||||||
- `lastInputSeconds`: “seconds since last user input” (if known)
|
- `lastInputSeconds`: “seconds since last user input” (if known)
|
||||||
- `reason`: `self`, `connect`, `node-connected`, `periodic`, ...
|
- `reason`: `self`, `connect`, `node-connected`, `periodic`, ...
|
||||||
- `ts`: last update timestamp (ms since epoch)
|
- `ts`: last update timestamp (ms since epoch)
|
||||||
@@ -62,7 +62,7 @@ for that node and refreshes it periodically so it doesn’t expire.
|
|||||||
Presence entries are stored in a single in‑memory map:
|
Presence entries are stored in a single in‑memory map:
|
||||||
|
|
||||||
- Entries are keyed by a **presence key**.
|
- Entries are keyed by a **presence key**.
|
||||||
- The best key is a stable `instanceId` that survives restarts.
|
- The best key is a stable `instanceId` (from `connect.client.instanceId`) that survives restarts.
|
||||||
- Keys are case‑insensitive.
|
- Keys are case‑insensitive.
|
||||||
|
|
||||||
If a client reconnects without a stable `instanceId`, it may show up as a
|
If a client reconnects without a stable `instanceId`, it may show up as a
|
||||||
@@ -94,6 +94,6 @@ indicator (Active/Idle/Stale) based on the age of the last update.
|
|||||||
|
|
||||||
- To see the raw list, call `system-presence` against the Gateway.
|
- To see the raw list, call `system-presence` against the Gateway.
|
||||||
- If you see duplicates:
|
- If you see duplicates:
|
||||||
- confirm clients send a stable `instanceId` in the handshake
|
- confirm clients send a stable `client.instanceId` in the handshake
|
||||||
- confirm periodic beacons use the same `instanceId`
|
- confirm periodic beacons use the same `instanceId`
|
||||||
- check whether the connection‑derived entry is missing `instanceId` (duplicates are expected)
|
- check whether the connection‑derived entry is missing `instanceId` (duplicates are expected)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ CLAWDBOT_CONFIG_PATH=~/.clawdbot/b.json CLAWDBOT_STATE_DIR=~/.clawdbot-b clawdbo
|
|||||||
- After handshake:
|
- After handshake:
|
||||||
- Requests: `{type:"req", id, method, params}` → `{type:"res", id, ok, payload|error}`
|
- Requests: `{type:"req", id, method, params}` → `{type:"res", id, ok, payload|error}`
|
||||||
- Events: `{type:"event", event, payload, seq?, stateVersion?}`
|
- Events: `{type:"event", event, payload, seq?, stateVersion?}`
|
||||||
- Structured presence entries: `{host, ip, version, platform?, deviceFamily?, modelIdentifier?, mode, lastInputSeconds?, ts, reason?, tags?[], instanceId? }`.
|
- Structured presence entries: `{host, ip, version, platform?, deviceFamily?, modelIdentifier?, mode, lastInputSeconds?, ts, reason?, tags?[], instanceId? }` (for WS clients, `instanceId` comes from `connect.client.instanceId`).
|
||||||
- `agent` responses are two-stage: first `res` ack `{runId,status:"accepted"}`, then a final `res` `{runId,status:"ok"|"error",summary}` after the run finishes; streamed output arrives as `event:"agent"`.
|
- `agent` responses are two-stage: first `res` ack `{runId,status:"accepted"}`, then a final `res` `{runId,status:"ok"|"error",summary}` after the run finishes; streamed output arrives as `event:"agent"`.
|
||||||
|
|
||||||
## Methods (initial set)
|
## Methods (initial set)
|
||||||
@@ -124,7 +124,7 @@ CLAWDBOT_CONFIG_PATH=~/.clawdbot/b.json CLAWDBOT_STATE_DIR=~/.clawdbot-b clawdbo
|
|||||||
- `node.invoke` — invoke a command on a node (e.g. `canvas.*`, `camera.*`).
|
- `node.invoke` — invoke a command on a node (e.g. `canvas.*`, `camera.*`).
|
||||||
- `node.pair.*` — pairing lifecycle (`request`, `list`, `approve`, `reject`, `verify`).
|
- `node.pair.*` — pairing lifecycle (`request`, `list`, `approve`, `reject`, `verify`).
|
||||||
|
|
||||||
See also: [Presence](/concepts/presence) for how presence is produced/deduped and why `instanceId` matters.
|
See also: [Presence](/concepts/presence) for how presence is produced/deduped and why a stable `client.instanceId` matters.
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
- `agent` — streamed tool/output events from the agent run (seq-tagged).
|
- `agent` — streamed tool/output events from the agent run (seq-tagged).
|
||||||
|
|||||||
@@ -1588,8 +1588,10 @@ export async function startGatewayServer(
|
|||||||
const authMethod = authResult.method ?? "none";
|
const authMethod = authResult.method ?? "none";
|
||||||
|
|
||||||
const shouldTrackPresence = !isGatewayCliClient(connectParams.client);
|
const shouldTrackPresence = !isGatewayCliClient(connectParams.client);
|
||||||
|
const clientId = connectParams.client.id;
|
||||||
|
const instanceId = connectParams.client.instanceId;
|
||||||
const presenceKey = shouldTrackPresence
|
const presenceKey = shouldTrackPresence
|
||||||
? connectParams.client.instanceId || connId
|
? (instanceId ?? connId)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
logWs("in", "connect", {
|
logWs("in", "connect", {
|
||||||
@@ -1598,7 +1600,7 @@ export async function startGatewayServer(
|
|||||||
clientDisplayName: connectParams.client.displayName,
|
clientDisplayName: connectParams.client.displayName,
|
||||||
version: connectParams.client.version,
|
version: connectParams.client.version,
|
||||||
mode: connectParams.client.mode,
|
mode: connectParams.client.mode,
|
||||||
instanceId: connectParams.client.instanceId,
|
clientId,
|
||||||
platform: connectParams.client.platform,
|
platform: connectParams.client.platform,
|
||||||
auth: authMethod,
|
auth: authMethod,
|
||||||
});
|
});
|
||||||
@@ -1621,7 +1623,7 @@ export async function startGatewayServer(
|
|||||||
deviceFamily: connectParams.client.deviceFamily,
|
deviceFamily: connectParams.client.deviceFamily,
|
||||||
modelIdentifier: connectParams.client.modelIdentifier,
|
modelIdentifier: connectParams.client.modelIdentifier,
|
||||||
mode: connectParams.client.mode,
|
mode: connectParams.client.mode,
|
||||||
instanceId: connectParams.client.instanceId,
|
instanceId,
|
||||||
reason: "connect",
|
reason: "connect",
|
||||||
});
|
});
|
||||||
presenceVersion += 1;
|
presenceVersion += 1;
|
||||||
|
|||||||
@@ -543,6 +543,8 @@ export async function connectReq(
|
|||||||
version: string;
|
version: string;
|
||||||
platform: string;
|
platform: string;
|
||||||
mode: string;
|
mode: string;
|
||||||
|
deviceFamily?: string;
|
||||||
|
modelIdentifier?: string;
|
||||||
instanceId?: string;
|
instanceId?: string;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user