docs: add PR template + node presence beacon
This commit is contained in:
committed by
Peter Steinberger
parent
476bbd2915
commit
672700f2b3
@@ -205,3 +205,71 @@ git worktree remove /tmp/issue-99
|
||||
6. **Parallel is OK** — run many Codex processes at once for batch work
|
||||
7. **NEVER start Codex in ~/clawd/** — it'll read your soul docs and get weird ideas about the org chart! Use the target project dir or /tmp for blank slate chats
|
||||
8. **NEVER checkout branches in ~/Projects/clawdis/** — that's the LIVE Clawdis instance! Clone to /tmp or use git worktree for PR reviews
|
||||
|
||||
---
|
||||
|
||||
## PR Template (The Razor Standard)
|
||||
|
||||
When submitting PRs to external repos, use this format for quality & maintainer-friendliness:
|
||||
|
||||
```markdown
|
||||
## Original Prompt
|
||||
[Exact request/problem statement]
|
||||
|
||||
## What this does
|
||||
[High-level description]
|
||||
|
||||
**Features:**
|
||||
- [Key feature 1]
|
||||
- [Key feature 2]
|
||||
|
||||
**Example usage:**
|
||||
```bash
|
||||
# Example
|
||||
command example
|
||||
```
|
||||
```
|
||||
|
||||
## Feature intent (maintainer-friendly)
|
||||
[Why useful, how it fits, workflows it enables]
|
||||
|
||||
## Prompt history (timestamped)
|
||||
- YYYY-MM-DD HH:MM UTC: [Step 1]
|
||||
- YYYY-MM-DD HH:MM UTC: [Step 2]
|
||||
|
||||
## How I tested
|
||||
**Manual verification:**
|
||||
1. [Test step] - Output: `[result]`
|
||||
2. [Test step] - Result: [result]
|
||||
|
||||
**Files tested:**
|
||||
- [Detail]
|
||||
- [Edge cases]
|
||||
|
||||
## Session logs (implementation)
|
||||
- [What was researched]
|
||||
- [What was discovered]
|
||||
- [Time spent]
|
||||
|
||||
## Implementation details
|
||||
**New files:**
|
||||
- `path/file.ts` - [description]
|
||||
|
||||
**Modified files:**
|
||||
- `path/file.ts` - [change]
|
||||
|
||||
**Technical notes:**
|
||||
- [Detail 1]
|
||||
- [Detail 2]
|
||||
|
||||
---
|
||||
*Submitted by Razor 🥷 - Mariano's AI agent*
|
||||
```
|
||||
|
||||
**Key principles:**
|
||||
1. Human-written description (no AI slop)
|
||||
2. Feature intent for maintainers
|
||||
3. Timestamped prompt history
|
||||
4. Session logs if using Codex/agent
|
||||
|
||||
**Example:** https://github.com/steipete/bird/pull/22
|
||||
|
||||
@@ -936,6 +936,70 @@ export async function startGatewayServer(
|
||||
? bridgeHost
|
||||
: undefined;
|
||||
|
||||
const nodePresenceTimers = new Map<string, ReturnType<typeof setInterval>>();
|
||||
|
||||
const stopNodePresenceTimer = (nodeId: string) => {
|
||||
const timer = nodePresenceTimers.get(nodeId);
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
nodePresenceTimers.delete(nodeId);
|
||||
};
|
||||
|
||||
const beaconNodePresence = (node: {
|
||||
nodeId: string;
|
||||
displayName?: string;
|
||||
remoteIp?: string;
|
||||
version?: string;
|
||||
platform?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
}, reason: string) => {
|
||||
const host = node.displayName?.trim() || node.nodeId;
|
||||
const rawIp = node.remoteIp?.trim();
|
||||
const ip = rawIp && !isLoopbackAddress(rawIp) ? rawIp : undefined;
|
||||
const version = node.version?.trim() || "unknown";
|
||||
const platform = node.platform?.trim() || undefined;
|
||||
const deviceFamily = node.deviceFamily?.trim() || undefined;
|
||||
const modelIdentifier = node.modelIdentifier?.trim() || undefined;
|
||||
const text = `Node: ${host}${ip ? ` (${ip})` : ""} · app ${version} · last input 0s ago · mode remote · reason ${reason}`;
|
||||
upsertPresence(node.nodeId, {
|
||||
host,
|
||||
ip,
|
||||
version,
|
||||
platform,
|
||||
deviceFamily,
|
||||
modelIdentifier,
|
||||
mode: "remote",
|
||||
reason,
|
||||
lastInputSeconds: 0,
|
||||
instanceId: node.nodeId,
|
||||
text,
|
||||
});
|
||||
presenceVersion += 1;
|
||||
broadcast(
|
||||
"presence",
|
||||
{ presence: listSystemPresence() },
|
||||
{
|
||||
dropIfSlow: true,
|
||||
stateVersion: {
|
||||
presence: presenceVersion,
|
||||
health: healthVersion,
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const startNodePresenceTimer = (node: { nodeId: string }) => {
|
||||
stopNodePresenceTimer(node.nodeId);
|
||||
nodePresenceTimers.set(
|
||||
node.nodeId,
|
||||
setInterval(() => {
|
||||
beaconNodePresence(node, "periodic");
|
||||
}, 180_000),
|
||||
);
|
||||
};
|
||||
|
||||
if (bridgeEnabled && bridgePort > 0 && bridgeHost) {
|
||||
try {
|
||||
const started = await startNodeBridgeServer({
|
||||
@@ -946,38 +1010,8 @@ export async function startGatewayServer(
|
||||
canvasHostHost: canvasHostHostForBridge,
|
||||
onRequest: (nodeId, req) => handleBridgeRequest(nodeId, req),
|
||||
onAuthenticated: async (node) => {
|
||||
const host = node.displayName?.trim() || node.nodeId;
|
||||
const ip = node.remoteIp?.trim();
|
||||
const version = node.version?.trim() || "unknown";
|
||||
const platform = node.platform?.trim() || undefined;
|
||||
const deviceFamily = node.deviceFamily?.trim() || undefined;
|
||||
const modelIdentifier = node.modelIdentifier?.trim() || undefined;
|
||||
const text = `Node: ${host}${ip ? ` (${ip})` : ""} · app ${version} · last input 0s ago · mode remote · reason node-connected`;
|
||||
upsertPresence(node.nodeId, {
|
||||
host,
|
||||
ip,
|
||||
version,
|
||||
platform,
|
||||
deviceFamily,
|
||||
modelIdentifier,
|
||||
mode: "remote",
|
||||
reason: "node-connected",
|
||||
lastInputSeconds: 0,
|
||||
instanceId: node.nodeId,
|
||||
text,
|
||||
});
|
||||
presenceVersion += 1;
|
||||
broadcast(
|
||||
"presence",
|
||||
{ presence: listSystemPresence() },
|
||||
{
|
||||
dropIfSlow: true,
|
||||
stateVersion: {
|
||||
presence: presenceVersion,
|
||||
health: healthVersion,
|
||||
},
|
||||
},
|
||||
);
|
||||
beaconNodePresence(node, "node-connected");
|
||||
startNodePresenceTimer(node);
|
||||
|
||||
try {
|
||||
const cfg = await loadVoiceWakeConfig();
|
||||
@@ -992,38 +1026,8 @@ export async function startGatewayServer(
|
||||
},
|
||||
onDisconnected: (node) => {
|
||||
bridgeUnsubscribeAll(node.nodeId);
|
||||
const host = node.displayName?.trim() || node.nodeId;
|
||||
const ip = node.remoteIp?.trim();
|
||||
const version = node.version?.trim() || "unknown";
|
||||
const platform = node.platform?.trim() || undefined;
|
||||
const deviceFamily = node.deviceFamily?.trim() || undefined;
|
||||
const modelIdentifier = node.modelIdentifier?.trim() || undefined;
|
||||
const text = `Node: ${host}${ip ? ` (${ip})` : ""} · app ${version} · last input 0s ago · mode remote · reason node-disconnected`;
|
||||
upsertPresence(node.nodeId, {
|
||||
host,
|
||||
ip,
|
||||
version,
|
||||
platform,
|
||||
deviceFamily,
|
||||
modelIdentifier,
|
||||
mode: "remote",
|
||||
reason: "node-disconnected",
|
||||
lastInputSeconds: 0,
|
||||
instanceId: node.nodeId,
|
||||
text,
|
||||
});
|
||||
presenceVersion += 1;
|
||||
broadcast(
|
||||
"presence",
|
||||
{ presence: listSystemPresence() },
|
||||
{
|
||||
dropIfSlow: true,
|
||||
stateVersion: {
|
||||
presence: presenceVersion,
|
||||
health: healthVersion,
|
||||
},
|
||||
},
|
||||
);
|
||||
stopNodePresenceTimer(node.nodeId);
|
||||
beaconNodePresence(node, "node-disconnected");
|
||||
},
|
||||
onEvent: handleBridgeEvent,
|
||||
onPairRequested: (request) => {
|
||||
|
||||
Reference in New Issue
Block a user