@dp-pcs/ogp 0.7.0-rc.1 → 0.7.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.
@@ -0,0 +1,477 @@
1
+ # OGP RC.1 Federation Test Checklist
2
+
3
+ This is the real-world validation plan for `@dp-pcs/ogp@0.7.0-rc.1` across three gateways:
4
+
5
+ - `OpenClaw` on `https://ogp.sarcastek.com`
6
+ - `Hermes` on `https://hermes.sarcastek.com`
7
+ - `Cosmo` on the AWS machine as the hub
8
+ - `Aleph` on `https://ogp-aleph.aicoe.fit` operated by Stephen Barr
9
+
10
+ The goal is not just "can they federate once?" The goal is to prove the full `rc.1` surface area behaves correctly under normal operation, restart/recovery, and obvious failure modes.
11
+
12
+ ## Success Criteria
13
+
14
+ `rc.1` is in decent shape if all of these are true:
15
+
16
+ - all three gateways answer `/.well-known/ogp` and `/federation/ping`
17
+ - all three pairings can request, approve, exchange traffic, and remove cleanly
18
+ - peer health transitions make sense when a daemon goes down and comes back
19
+ - scoped access is enforced
20
+ - agent-comms works, including reply-wait paths
21
+ - project-intent flows work across real gateways, not just the local harness
22
+ - multi-agent/persona routing works where advertised
23
+ - rendezvous/invite flow works if you intend to rely on it
24
+ - restart behavior is acceptable, including daemon and tunnel recovery
25
+
26
+ ## Participants
27
+
28
+ Set these once in your shell while testing:
29
+
30
+ ```bash
31
+ export OC_URL="https://ogp.sarcastek.com"
32
+ export HERMES_URL="https://hermes.sarcastek.com"
33
+ export COSMO_URL="https://david-proctor.gw.clawporate.elelem.expert"
34
+ export ALEPH_URL="https://ogp-aleph.aicoe.fit"
35
+ ```
36
+
37
+ Useful topology:
38
+
39
+ | Pair | Why it matters |
40
+ | --- | --- |
41
+ | OpenClaw <-> Hermes | Cross-framework, same operator, easiest first smoke |
42
+ | OpenClaw <-> Cosmo | David local gateway to AWS hub |
43
+ | Hermes <-> Cosmo | David second local gateway to AWS hub |
44
+ | Aleph <-> Cosmo | Stephen gateway to AWS hub |
45
+
46
+ Primary `rc.1` goal for this round:
47
+
48
+ - prove both David and Stephen can federate with Cosmo cleanly
49
+ - prove Cosmo stays healthy as the shared external hub
50
+ - prove project-intent and agent-comms traffic survives the hub topology
51
+
52
+ Direct `OpenClaw/Hermes <-> Aleph` federation is optional for this round unless you specifically want a full mesh test.
53
+
54
+ ## 0. Preflight On Every Machine
55
+
56
+ Run this before any pairwise testing:
57
+
58
+ ```bash
59
+ ogp --version
60
+ ogp --for all status
61
+ ogp whoami
62
+ ogp config health-check show
63
+ curl -sS "$OC_URL/.well-known/ogp" | jq .
64
+ curl -sS "$HERMES_URL/.well-known/ogp" | jq .
65
+ curl -sS "$COSMO_URL/.well-known/ogp" | jq .
66
+ curl -sS "$ALEPH_URL/.well-known/ogp" | jq .
67
+ ```
68
+
69
+ Check:
70
+
71
+ - version is `0.7.0-rc.1` everywhere
72
+ - each daemon is actually listening on its configured port
73
+ - each public URL matches the gateway's configured `gatewayUrl`
74
+ - each gateway advertises `multi-agent-personas` in `capabilities.features`
75
+ - each gateway advertises the expected `agents[]`
76
+
77
+ ### Optional: speed up health-check testing
78
+
79
+ For testing, lower the heartbeat interval so you do not wait forever for state changes:
80
+
81
+ ```bash
82
+ ogp config health-check interval 30000
83
+ ogp config health-check timeout 5000
84
+ ogp config health-check max-failures 2
85
+ ogp stop --for all
86
+ ogp start --for all --background
87
+ ```
88
+
89
+ Expected result:
90
+
91
+ - health config shows `30s / 5s / 2 failures`
92
+ - after restart, `ogp --for all status` shows both daemons up
93
+
94
+ ## 1. Local Harness First
95
+
96
+ Before you spend time on the three-machine test, run the existing local project-intent harness in this repo:
97
+
98
+ ```bash
99
+ npm run build
100
+ npm test -- --run
101
+ npm run test:project-intents
102
+ ```
103
+
104
+ Optional stateful run:
105
+
106
+ ```bash
107
+ npm run test:project-intents -- --keep-state
108
+ ```
109
+
110
+ This catches obvious regressions in:
111
+
112
+ - federation request / approve
113
+ - project create / join / contribute / query / status-peer
114
+ - local state persistence
115
+
116
+ ## 2. Discovery And Liveness
117
+
118
+ Run these against every public gateway:
119
+
120
+ ```bash
121
+ ogp federation ping "$OC_URL"
122
+ ogp federation ping "$HERMES_URL"
123
+ ogp federation ping "$COSMO_URL"
124
+ ogp federation ping "$ALEPH_URL"
125
+
126
+ curl -sS "$OC_URL/.well-known/ogp" | jq '{version,displayName,gatewayUrl,features:.capabilities.features,agents}'
127
+ curl -sS "$HERMES_URL/.well-known/ogp" | jq '{version,displayName,gatewayUrl,features:.capabilities.features,agents}'
128
+ curl -sS "$COSMO_URL/.well-known/ogp" | jq '{version,displayName,gatewayUrl,features:.capabilities.features,agents}'
129
+ curl -sS "$ALEPH_URL/.well-known/ogp" | jq '{version,displayName,gatewayUrl,features:.capabilities.features,agents}'
130
+ ```
131
+
132
+ Expected result:
133
+
134
+ - ping succeeds for all three URLs
135
+ - each card shows `version: 0.7.0-rc.1`
136
+ - `gatewayUrl` in the card matches the public URL you hit
137
+ - `agents[]` looks correct for that gateway
138
+
139
+ ## 3. Federation Lifecycle
140
+
141
+ Run this for each required hub/spoke pair.
142
+
143
+ Example commands from OpenClaw -> Hermes:
144
+
145
+ ```bash
146
+ ogp --for openclaw federation request "$HERMES_URL" --alias hermes-local
147
+ ogp --for hermes federation list --status pending
148
+ ogp --for hermes federation approve hermes-local --intents message,agent-comms,project.join,project.contribute,project.query,project.status
149
+ ogp --for openclaw federation list
150
+ ogp --for hermes federation list
151
+ ogp --for openclaw federation status
152
+ ogp --for hermes federation status
153
+ ```
154
+
155
+ Repeat the same flow for:
156
+
157
+ - OpenClaw -> Cosmo
158
+ - Hermes -> Cosmo
159
+ - Aleph -> Cosmo
160
+
161
+ Expected result:
162
+
163
+ - requester sees the peer move to `approved`
164
+ - approver sees the requester move to `approved`
165
+ - status output shows the correct alias and gateway
166
+ - no peer lands in a weird half-approved state
167
+
168
+ ## 4. Health Checks And Heartbeat State
169
+
170
+ After all pairs are approved, inspect the health view:
171
+
172
+ ```bash
173
+ ogp --for openclaw federation status
174
+ ogp --for hermes federation status
175
+ ```
176
+
177
+ Look for:
178
+
179
+ - `healthy` peers
180
+ - a sensible `healthState` such as `established`
181
+ - recent inbound and outbound timestamps
182
+
183
+ ### Failure and recovery test
184
+
185
+ For one pair at a time, stop one side and watch the other side detect it:
186
+
187
+ ```bash
188
+ ogp --for hermes stop
189
+ ogp --for openclaw federation status
190
+ sleep 70
191
+ ogp --for openclaw federation status
192
+ ogp --for hermes start --background
193
+ sleep 70
194
+ ogp --for openclaw federation status
195
+ ogp --for hermes federation status
196
+ ```
197
+
198
+ Expected result:
199
+
200
+ - after enough failed heartbeats, the surviving peer marks the stopped peer degraded or down
201
+ - after restart, health recovers back to `established`
202
+ - `/.well-known/ogp` becomes reachable again after restart
203
+
204
+ Run the same stop/restart test with Cosmo, because that is the critical shared dependency.
205
+
206
+ ## 5. Identity And Persona Advertisement
207
+
208
+ Check that each peer advertises the expected identity and agents:
209
+
210
+ ```bash
211
+ curl -sS "$OC_URL/.well-known/ogp" | jq '.agents'
212
+ curl -sS "$HERMES_URL/.well-known/ogp" | jq '.agents'
213
+ curl -sS "$COSMO_URL/.well-known/ogp" | jq '.agents'
214
+ curl -sS "$ALEPH_URL/.well-known/ogp" | jq '.agents'
215
+ ```
216
+
217
+ Then change local identity on one gateway and push it to an approved peer:
218
+
219
+ ```bash
220
+ ogp config show-identity
221
+ ogp federation update-identity <peer-id>
222
+ ogp federation status
223
+ ```
224
+
225
+ Expected result:
226
+
227
+ - approved peers see the updated identity snapshot
228
+ - no federation relationship breaks just because identity fields changed
229
+
230
+ ## 6. Basic Message Traffic
231
+
232
+ Test raw message intent first:
233
+
234
+ ```bash
235
+ ogp federation send <peer-id> message '{"text":"hello from rc1"}'
236
+ ogp federation send <peer-id> task-request '{"task":"echo test","priority":"normal"}'
237
+ ogp federation send <peer-id> status-update '{"status":"completed","source":"rc1-manual-test"}'
238
+ ```
239
+
240
+ Expected result:
241
+
242
+ - receiver gets the message
243
+ - sender gets a successful transport result
244
+ - daemon log records the request cleanly
245
+
246
+ ## 7. Agent-Comms
247
+
248
+ First inspect policies:
249
+
250
+ ```bash
251
+ ogp agent-comms policies
252
+ ogp agent-comms activity --last 20
253
+ ```
254
+
255
+ Then test real agent-comms:
256
+
257
+ ```bash
258
+ ogp federation agent <peer-id> memory-management "Store this test marker"
259
+ ogp federation agent <peer-id> queries "What gateway are you on?" --wait --timeout 60000
260
+ ogp federation agent <peer-id> task-delegation "Return a short ack" --priority high --wait --timeout 60000
261
+ ```
262
+
263
+ Expected result:
264
+
265
+ - fire-and-forget delivery succeeds
266
+ - `--wait` returns a real reply path, not just transport success
267
+ - activity log records the interaction
268
+
269
+ ### Persona routing
270
+
271
+ If the peer advertises more than one agent:
272
+
273
+ ```bash
274
+ ogp federation agent <peer-id> queries "hello primary" --to-agent junior --wait
275
+ ogp federation agent <peer-id> queries "hello specialist" --to-agent apollo --wait
276
+ ```
277
+
278
+ Negative test:
279
+
280
+ ```bash
281
+ ogp federation agent <peer-id> queries "bad persona test" --to-agent definitely-not-real --wait
282
+ ```
283
+
284
+ Expected result:
285
+
286
+ - valid persona IDs route successfully
287
+ - invalid persona ID fails with a clear error
288
+
289
+ ## 8. Scope Enforcement
290
+
291
+ Approve or grant a peer with restricted scopes, then verify allowed vs denied behavior.
292
+
293
+ Example:
294
+
295
+ ```bash
296
+ ogp federation grant <peer-id> --intents message --rate 20/3600
297
+ ogp federation scopes <peer-id>
298
+ ```
299
+
300
+ Now try:
301
+
302
+ ```bash
303
+ ogp federation send <peer-id> message '{"text":"allowed"}'
304
+ ogp federation agent <peer-id> memory-management "should be denied"
305
+ ogp project query-peer <peer-id> some-project
306
+ ```
307
+
308
+ Expected result:
309
+
310
+ - `message` works
311
+ - `agent-comms` fails if not granted
312
+ - project intents fail if project scopes are not granted
313
+
314
+ Then restore the broader scope set and verify those flows work again.
315
+
316
+ ## 9. Project Intents
317
+
318
+ This is the highest-value cross-machine test after basic federation.
319
+
320
+ Recommended topology for this round:
321
+
322
+ - create the canonical shared project on `Cosmo`
323
+ - have `OpenClaw`, `Hermes`, and `Aleph` join it
324
+ - send contributions from each spoke into Cosmo
325
+ - verify that queries against Cosmo reflect the combined state
326
+
327
+ On the owner gateway:
328
+
329
+ ```bash
330
+ ogp project create rc1-shared "RC1 Shared Test" --description "Real multi-gateway test"
331
+ ogp project contribute rc1-shared note "owner bootstrap note" --local-only
332
+ ogp project status rc1-shared
333
+ ```
334
+
335
+ From a remote approved peer:
336
+
337
+ ```bash
338
+ ogp project request-join <owner-peer-id> rc1-shared "RC1 Shared Test" --description "Join from remote gateway"
339
+ ogp project query-peer <owner-peer-id> rc1-shared
340
+ ogp project send-contribution <owner-peer-id> rc1-shared task "Remote task from rc1 test"
341
+ ogp project send-contribution <owner-peer-id> rc1-shared decision "Remote decision from rc1 test" --metadata '{"confidence":"high"}'
342
+ ogp project status-peer <owner-peer-id> rc1-shared
343
+ ```
344
+
345
+ Run this with both remote peers joining the same project.
346
+
347
+ Expected result:
348
+
349
+ - pre-join query should fail or be denied
350
+ - join succeeds and the project membership becomes visible
351
+ - remote contributions show up on the owner gateway
352
+ - query after join returns the new entries
353
+ - status-peer succeeds at least at the transport level
354
+
355
+ Evidence:
356
+
357
+ - `ogp project status rc1-shared`
358
+ - `~/.ogp/projects.json`
359
+ - `~/.ogp-hermes/projects.json`
360
+ - Cosmo's `projects.json`
361
+ - Aleph's `projects.json`
362
+
363
+ ## 10. Rendezvous / Invite Flow
364
+
365
+ Only run this if you intend to use rendezvous in production.
366
+
367
+ From one gateway:
368
+
369
+ ```bash
370
+ ogp federation invite
371
+ ```
372
+
373
+ From another:
374
+
375
+ ```bash
376
+ ogp federation accept <token>
377
+ ```
378
+
379
+ Also test connect-by-pubkey:
380
+
381
+ ```bash
382
+ ogp federation connect <pubkey>
383
+ ```
384
+
385
+ Expected result:
386
+
387
+ - invite token resolves and creates the same approved peer state you get from direct request/approve
388
+ - connect-by-pubkey finds the peer and completes federation
389
+ - rendezvous registration stays alive while the daemon is up
390
+
391
+ ## 11. Removal And Re-Federation
392
+
393
+ For each pair:
394
+
395
+ ```bash
396
+ ogp federation remove <peer-id>
397
+ ogp federation list
398
+ ```
399
+
400
+ Expected result:
401
+
402
+ - removed peer is marked removed locally
403
+ - remote side receives the removal notification on a best-effort basis
404
+ - sending further traffic to the removed peer fails
405
+
406
+ Then immediately re-request federation and make sure the pair can recover cleanly.
407
+
408
+ ## 12. Restart, Reboot, And Tunnel Recovery
409
+
410
+ These are operational tests, not protocol tests, but they matter.
411
+
412
+ ### Daemon restart
413
+
414
+ ```bash
415
+ ogp --for all stop
416
+ ogp --for all start --background
417
+ ogp --for all status
418
+ ```
419
+
420
+ ### Public endpoint recovery
421
+
422
+ ```bash
423
+ curl -sS "$OC_URL/.well-known/ogp" | jq .version
424
+ curl -sS "$HERMES_URL/.well-known/ogp" | jq .version
425
+ curl -sS "$COSMO_URL/.well-known/ogp" | jq .version
426
+ curl -sS "$ALEPH_URL/.well-known/ogp" | jq .version
427
+ ```
428
+
429
+ ### Tunnel validation
430
+
431
+ Verify each public URL continues to map to the correct local daemon after restart.
432
+
433
+ ## 13. Known Local Risk To Explicitly Test
434
+
435
+ On David's macOS machine, the current LaunchAgent can fail if launchd cannot find `node` for the `ogp` wrapper. That is worth an explicit reboot/login test because it will absolutely look like "tunnel is up but OGP is dead."
436
+
437
+ Check:
438
+
439
+ ```bash
440
+ launchctl print gui/$(id -u)/com.dp-pcs.ogp
441
+ sed -n '1,80p' ~/.ogp/launchagent.log
442
+ ```
443
+
444
+ If this still shows `env: node: No such file or directory`, autostart is not trustworthy yet.
445
+
446
+ ## 14. Evidence To Save Per Test Run
447
+
448
+ For each major run, save:
449
+
450
+ ```bash
451
+ ogp --for all status
452
+ ogp --for openclaw federation list
453
+ ogp --for hermes federation list
454
+ ogp agent-comms activity --last 50
455
+ curl -sS "$OC_URL/.well-known/ogp" | jq .
456
+ curl -sS "$HERMES_URL/.well-known/ogp" | jq .
457
+ curl -sS "$COSMO_URL/.well-known/ogp" | jq .
458
+ curl -sS "$ALEPH_URL/.well-known/ogp" | jq .
459
+ ```
460
+
461
+ Also keep:
462
+
463
+ - `~/.ogp/daemon.log`
464
+ - `~/.ogp-hermes/daemon.log`
465
+ - each gateway's `peers.json`
466
+ - each gateway's `projects.json`
467
+
468
+ ## Minimal Ship Gate
469
+
470
+ If you do not have time to run everything, the minimum bar before treating `rc.1` as real is:
471
+
472
+ 1. all four public cards return `200`
473
+ 2. `OpenClaw <-> Cosmo`, `Hermes <-> Cosmo`, and `Aleph <-> Cosmo` can federate
474
+ 3. health changes on Cosmo stop/start are visible and recover from the spoke gateways
475
+ 4. one successful `agent-comms --wait` round-trip from each spoke to Cosmo
476
+ 5. one successful project join + contribution + query flow with Cosmo as the shared project owner
477
+ 6. one successful removal + re-federation cycle against Cosmo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dp-pcs/ogp",
3
- "version": "0.7.0-rc.1",
3
+ "version": "0.7.1",
4
4
  "description": "Open Gateway Protocol (OGP) - Peer-to-peer federation daemon for OpenClaw AI gateways with cryptographic signatures",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -3,12 +3,78 @@
3
3
  # OGP zsh completion script
4
4
  # Auto-generated by ogp completion install
5
5
 
6
+ _ogp_command_word() {
7
+ local i=2
8
+
9
+ while (( i <= $#words )); do
10
+ case "${words[i]}" in
11
+ --for)
12
+ (( i += 2 ))
13
+ continue
14
+ ;;
15
+ --for=*)
16
+ (( i += 1 ))
17
+ continue
18
+ ;;
19
+ -h|--help|-v|--version)
20
+ (( i += 1 ))
21
+ continue
22
+ ;;
23
+ -*)
24
+ (( i += 1 ))
25
+ continue
26
+ ;;
27
+ *)
28
+ print -r -- "${words[i]}"
29
+ return 0
30
+ ;;
31
+ esac
32
+ done
33
+
34
+ return 1
35
+ }
36
+
37
+ _ogp_subcommand_word() {
38
+ local parent="$1"
39
+ local seen_parent=0
40
+ local i=2
41
+
42
+ while (( i <= $#words )); do
43
+ case "${words[i]}" in
44
+ --for)
45
+ (( i += 2 ))
46
+ continue
47
+ ;;
48
+ --for=*)
49
+ (( i += 1 ))
50
+ continue
51
+ ;;
52
+ -*)
53
+ (( i += 1 ))
54
+ continue
55
+ ;;
56
+ esac
57
+
58
+ if (( seen_parent == 0 )); then
59
+ if [[ "${words[i]}" == "$parent" ]]; then
60
+ seen_parent=1
61
+ fi
62
+ (( i += 1 ))
63
+ continue
64
+ fi
65
+
66
+ print -r -- "${words[i]}"
67
+ return 0
68
+ done
69
+
70
+ return 1
71
+ }
72
+
6
73
  _ogp() {
7
- local -a frameworks peers
74
+ local -a frameworks
8
75
  local curcontext="$curcontext" state line
9
76
  typeset -A opt_args
10
77
 
11
- # Get frameworks for --for flag
12
78
  frameworks=(${(f)"$(ogp config list --quiet 2>/dev/null)"} all)
13
79
 
14
80
  _arguments -C \
@@ -40,7 +106,7 @@ _ogp() {
40
106
  ))'
41
107
  ;;
42
108
  args)
43
- case ${words[1]} in
109
+ case "$(_ogp_command_word)" in
44
110
  federation)
45
111
  _ogp_federation
46
112
  ;;
@@ -69,41 +135,44 @@ _ogp() {
69
135
 
70
136
  _ogp_federation() {
71
137
  local curcontext="$curcontext" state line
138
+ local subcmd="$(_ogp_subcommand_word federation)"
72
139
  typeset -A opt_args
73
140
 
74
- _arguments '1:subcommand:((
75
- list\:"List all peers"
76
- status\:"Show federation status and alias mappings"
77
- request\:"Send federation request to a peer"
78
- connect\:"Connect to peer by public key via rendezvous"
79
- invite\:"Generate invite token to share with peer"
80
- accept\:"Accept peer invite token and connect"
81
- approve\:"Approve pending federation request"
82
- reject\:"Reject pending federation request"
83
- remove\:"Remove peer from federation list"
84
- alias\:"Set user-friendly alias for peer"
85
- tag\:"Add tags to a peer (local categorization)"
86
- untag\:"Remove tags from a peer"
87
- update-identity\:"Send updated identity information to approved peer"
88
- ping\:"Ping peer gateway to test connectivity"
89
- send\:"Send message to federated peer"
90
- scopes\:"Show scope grants for peer"
91
- grant\:"Update scope grants for approved peer"
92
- agent\:"Send agent-comms message to peer"
93
- ))' \
94
- '*::arg:->args'
141
+ _arguments \
142
+ '1:subcommand:((
143
+ list\:"List all peers"
144
+ status\:"Show federation status and alias mappings"
145
+ request\:"Send federation request to a peer"
146
+ connect\:"Connect to peer by public key via rendezvous"
147
+ invite\:"Generate invite token to share with peer"
148
+ accept\:"Accept peer invite token and connect"
149
+ approve\:"Approve pending federation request"
150
+ reject\:"Reject pending federation request"
151
+ remove\:"Remove peer from federation list"
152
+ alias\:"Set user-friendly alias for peer"
153
+ tag\:"Add tags to a peer (local categorization)"
154
+ untag\:"Remove tags from a peer"
155
+ update-identity\:"Send updated identity information to approved peer"
156
+ ping\:"Ping peer gateway to test connectivity"
157
+ send\:"Send message to federated peer"
158
+ scopes\:"Show scope grants for peer"
159
+ grant\:"Update scope grants for approved peer"
160
+ agent\:"Send agent-comms message to peer"
161
+ ))' \
162
+ '*::arg:->args'
95
163
 
96
164
  case "$state" in
97
165
  args)
98
- case ${words[1]} in
166
+ case "$subcmd" in
99
167
  list)
100
168
  _arguments \
101
- '(-s --status)'{-s,--status}'[Filter by status]:status:(pending approved rejected removed)'
169
+ '(-s --status)'{-s,--status}'[Filter by status]:status:(pending approved rejected removed)' \
170
+ '(-t --tag)'{-t,--tag}'[Filter by tag]:tag:'
102
171
  ;;
103
172
  request)
104
173
  _arguments \
105
174
  '1:peer-url:' \
106
- '2:peer-id::' \
175
+ '2:alias::' \
107
176
  '(-a --alias)'{-a,--alias}'[User-friendly alias]:alias:' \
108
177
  '--petname[Deprecated - use --alias]:petname:'
109
178
  ;;
@@ -113,16 +182,29 @@ _ogp_federation() {
113
182
  '(-a --alias)'{-a,--alias}'[User-friendly alias]:alias:' \
114
183
  '--petname[Deprecated - use --alias]:petname:'
115
184
  ;;
116
- approve)
185
+ approve|grant)
117
186
  _arguments \
118
187
  '1:peer-id:' \
119
188
  '--intents[Comma-separated intents to grant]:intents:' \
120
- '--rate[Rate limit as requests/seconds]:rate:' \
189
+ '--rate[Rate limit as requests/windowSeconds]:rate:' \
121
190
  '--topics[Comma-separated topics for agent-comms]:topics:'
122
191
  ;;
123
- reject|remove|scopes|alias)
192
+ reject|remove|scopes|update-identity)
124
193
  _arguments '1:peer-id:'
125
194
  ;;
195
+ alias)
196
+ _arguments \
197
+ '1:peer-id:' \
198
+ '2:alias:'
199
+ ;;
200
+ tag|untag)
201
+ _arguments \
202
+ '1:peer-id:' \
203
+ '*:tag:'
204
+ ;;
205
+ ping)
206
+ _arguments '1:peer-url:'
207
+ ;;
126
208
  send)
127
209
  _arguments \
128
210
  '1:peer-id:' \
@@ -130,13 +212,6 @@ _ogp_federation() {
130
212
  '3:payload:' \
131
213
  '--to-agent[Target a specific persona on the peer]:persona:'
132
214
  ;;
133
- grant)
134
- _arguments \
135
- '1:peer-id:' \
136
- '--intents[Comma-separated intents to grant]:intents:' \
137
- '--rate[Rate limit as requests/seconds]:rate:' \
138
- '--topics[Comma-separated topics for agent-comms]:topics:'
139
- ;;
140
215
  agent)
141
216
  _arguments \
142
217
  '1:peer-id:' \
@@ -155,25 +230,27 @@ _ogp_federation() {
155
230
 
156
231
  _ogp_agent_comms() {
157
232
  local curcontext="$curcontext" state line
233
+ local subcmd="$(_ogp_subcommand_word agent-comms)"
158
234
  typeset -A opt_args
159
235
 
160
- _arguments '1:subcommand:((
161
- policies\:"Show response policies"
162
- configure\:"Configure response policies"
163
- add-topic\:"Add topic to peer response policy"
164
- set-topic\:"Set topic policy for peer"
165
- set-default\:"Set per-peer default level"
166
- remove-topic\:"Remove topic from peer policy"
167
- reset\:"Reset peer policy to global defaults"
168
- activity\:"Show agent-comms activity log"
169
- default\:"Set default response level for unknown topics"
170
- logging\:"Enable or disable activity logging"
171
- ))' \
172
- '*::arg:->args'
236
+ _arguments \
237
+ '1:subcommand:((
238
+ policies\:"Show response policies"
239
+ configure\:"Configure response policies"
240
+ add-topic\:"Add topic to peer response policy"
241
+ set-topic\:"Set topic policy for peer"
242
+ set-default\:"Set per-peer default level"
243
+ remove-topic\:"Remove topic from peer policy"
244
+ reset\:"Reset peer policy to global defaults"
245
+ activity\:"Show agent-comms activity log"
246
+ default\:"Set default response level for unknown topics"
247
+ logging\:"Enable or disable activity logging"
248
+ ))' \
249
+ '*::arg:->args'
173
250
 
174
251
  case "$state" in
175
252
  args)
176
- case ${words[1]} in
253
+ case "$subcmd" in
177
254
  policies|activity)
178
255
  _arguments '1:peer-id::'
179
256
  ;;
@@ -204,8 +281,13 @@ _ogp_agent_comms() {
204
281
  '1:peer-id:' \
205
282
  '2:level:(full summary escalate deny off)'
206
283
  ;;
207
- remove-topic|reset)
208
- _arguments '1:peer-id:' '2:topic::'
284
+ remove-topic)
285
+ _arguments \
286
+ '1:peer-id:' \
287
+ '2:topic:'
288
+ ;;
289
+ reset)
290
+ _arguments '1:peer-id:'
209
291
  ;;
210
292
  default)
211
293
  _arguments '1:level:(full summary escalate deny off)'
@@ -220,29 +302,32 @@ _ogp_agent_comms() {
220
302
 
221
303
  _ogp_config() {
222
304
  local curcontext="$curcontext" state line
223
- typeset -A opt_args
224
305
  local -a frameworks
306
+ local subcmd="$(_ogp_subcommand_word config)"
307
+ typeset -A opt_args
308
+
225
309
  frameworks=(${(f)"$(ogp config list --quiet 2>/dev/null)"})
226
310
 
227
- _arguments '1:subcommand:((
228
- show\:"Show all configured frameworks and default"
229
- set-default\:"Set default framework"
230
- list\:"List all frameworks"
231
- enable\:"Enable a framework"
232
- disable\:"Disable a framework"
233
- frameworks\:"Show all detected frameworks"
234
- health-check\:"Manage health check configuration"
235
- show-identity\:"Show current identity configuration"
236
- set-identity\:"Update identity information"
237
- set-tags\:"Set tags (replaces existing)"
238
- add-tag\:"Add a tag"
239
- remove-tag\:"Remove a tag"
240
- ))' \
241
- '*::arg:->args'
311
+ _arguments \
312
+ '1:subcommand:((
313
+ show\:"Show all configured frameworks and default"
314
+ set-default\:"Set default framework"
315
+ list\:"List all frameworks"
316
+ enable\:"Enable a framework"
317
+ disable\:"Disable a framework"
318
+ frameworks\:"Show all detected frameworks"
319
+ health-check\:"Manage health check configuration"
320
+ show-identity\:"Show current identity configuration"
321
+ set-identity\:"Update identity information"
322
+ set-tags\:"Set tags (replaces existing)"
323
+ add-tag\:"Add a tag"
324
+ remove-tag\:"Remove a tag"
325
+ ))' \
326
+ '*::arg:->args'
242
327
 
243
328
  case "$state" in
244
329
  args)
245
- case ${words[1]} in
330
+ case "$subcmd" in
246
331
  set-default|enable|disable)
247
332
  _arguments "1:framework:($frameworks)"
248
333
  ;;
@@ -258,32 +343,58 @@ _ogp_config() {
258
343
  '--agent-name[Agent name]:name:' \
259
344
  '--organization[Organization name]:org:'
260
345
  ;;
346
+ set-tags)
347
+ _arguments '*:tag:'
348
+ ;;
349
+ add-tag|remove-tag)
350
+ _arguments '1:tag:'
351
+ ;;
261
352
  esac
262
353
  ;;
263
354
  esac
264
355
  }
265
356
 
266
357
  _ogp_config_health_check() {
267
- _arguments '1:subcommand:((
268
- show\:"Show current health check configuration"
269
- interval\:"Set health check interval in milliseconds"
270
- timeout\:"Set health check timeout in milliseconds"
271
- max-failures\:"Set max consecutive failures before unhealthy"
272
- ))' \
273
- '*::arg:->args'
358
+ local curcontext="$curcontext" state line
359
+ local subcmd="$(_ogp_subcommand_word health-check)"
360
+ typeset -A opt_args
361
+
362
+ _arguments \
363
+ '1:subcommand:((
364
+ show\:"Show current health check configuration"
365
+ interval\:"Set health check interval in milliseconds"
366
+ timeout\:"Set health check timeout in milliseconds"
367
+ max-failures\:"Set max consecutive failures before unhealthy"
368
+ ))' \
369
+ '*::arg:->args'
370
+
371
+ case "$state" in
372
+ args)
373
+ case "$subcmd" in
374
+ interval|timeout|max-failures)
375
+ _arguments '1:value:'
376
+ ;;
377
+ esac
378
+ ;;
379
+ esac
274
380
  }
275
381
 
276
382
  _ogp_intent() {
277
- _arguments '1:subcommand:((
278
- register\:"Register new intent handler"
279
- list\:"List all registered intents"
280
- remove\:"Remove registered intent"
281
- ))' \
282
- '*::arg:->args'
383
+ local curcontext="$curcontext" state line
384
+ local subcmd="$(_ogp_subcommand_word intent)"
385
+ typeset -A opt_args
386
+
387
+ _arguments \
388
+ '1:subcommand:((
389
+ register\:"Register new intent handler"
390
+ list\:"List all registered intents"
391
+ remove\:"Remove registered intent"
392
+ ))' \
393
+ '*::arg:->args'
283
394
 
284
395
  case "$state" in
285
396
  args)
286
- case ${words[1]} in
397
+ case "$subcmd" in
287
398
  register)
288
399
  _arguments \
289
400
  '1:name:' \
@@ -299,25 +410,30 @@ _ogp_intent() {
299
410
  }
300
411
 
301
412
  _ogp_project() {
302
- _arguments '1:subcommand:((
303
- create\:"Create new project locally"
304
- join\:"Join existing project"
305
- list\:"List all local projects"
306
- remove\:"Remove local project"
307
- contribute\:"Add contribution to project"
308
- query\:"Query project contributions"
309
- status\:"Show project status overview"
310
- request-join\:"Request to join project from peer"
311
- send-contribution\:"Send contribution to peer project"
312
- query-peer\:"Query peer project contributions"
313
- status-peer\:"Request project status from peer"
314
- delete\:"Delete local project and all contributions"
315
- ))' \
316
- '*::arg:->args'
413
+ local curcontext="$curcontext" state line
414
+ local subcmd="$(_ogp_subcommand_word project)"
415
+ typeset -A opt_args
416
+
417
+ _arguments \
418
+ '1:subcommand:((
419
+ create\:"Create new project locally"
420
+ join\:"Join existing project"
421
+ list\:"List all local projects"
422
+ remove\:"Remove local project"
423
+ contribute\:"Add contribution to project"
424
+ query\:"Query project contributions"
425
+ status\:"Show project status overview"
426
+ request-join\:"Request to join project from peer"
427
+ send-contribution\:"Send contribution to peer project"
428
+ query-peer\:"Query peer project contributions"
429
+ status-peer\:"Request project status from peer"
430
+ delete\:"Delete local project and all contributions"
431
+ ))' \
432
+ '*::arg:->args'
317
433
 
318
434
  case "$state" in
319
435
  args)
320
- case ${words[1]} in
436
+ case "$subcmd" in
321
437
  create)
322
438
  _arguments \
323
439
  '1:project-id:' \
@@ -11,7 +11,9 @@ const renderSlidesOnly = process.argv.includes('--slides-only');
11
11
 
12
12
  const width = 1920;
13
13
  const height = 1080;
14
- const duration = 5.8;
14
+ const fps = 30;
15
+ const clipDuration = 6.8;
16
+ const transitionDuration = 0.6;
15
17
 
16
18
  const palette = {
17
19
  ink: '#10131c',
@@ -386,32 +388,67 @@ for (const png of pngs) {
386
388
  }
387
389
  }
388
390
 
389
- const concatPath = join(outDir, 'slides.ffconcat');
390
- const concatLines = ['ffconcat version 1.0'];
391
+ const motionProfiles = [
392
+ { dx: 26, dy: 14, phaseX: 0.0, phaseY: 0.5 },
393
+ { dx: -24, dy: 18, phaseX: 0.8, phaseY: 1.2 },
394
+ { dx: 20, dy: -16, phaseX: 1.4, phaseY: 0.3 },
395
+ { dx: -22, dy: -14, phaseX: 0.2, phaseY: 1.8 }
396
+ ];
397
+ const transitions = [
398
+ 'smoothleft',
399
+ 'fadeblack',
400
+ 'smoothup',
401
+ 'circleopen',
402
+ 'smoothright',
403
+ 'wipeleft',
404
+ 'fadegrays',
405
+ 'diagtl',
406
+ 'smoothdown'
407
+ ];
408
+
409
+ const ffmpegArgs = ['-y'];
391
410
  for (const png of pngs) {
392
- const absolutePng = resolve(png);
393
- concatLines.push(`file '${absolutePng.replaceAll("'", "'\\''")}'`);
394
- concatLines.push(`duration ${duration}`);
411
+ ffmpegArgs.push('-loop', '1', '-t', String(clipDuration), '-i', resolve(png));
412
+ }
413
+
414
+ const filterParts = [];
415
+ for (let i = 0; i < pngs.length; i++) {
416
+ const profile = motionProfiles[i % motionProfiles.length];
417
+ filterParts.push(
418
+ `[${i}:v]scale=2160:1215,` +
419
+ `crop=${width}:${height}:` +
420
+ `x='(in_w-out_w)/2+(${profile.dx})*sin(t*0.42+${profile.phaseX})':` +
421
+ `y='(in_h-out_h)/2+(${profile.dy})*cos(t*0.34+${profile.phaseY})',` +
422
+ `fps=${fps},trim=duration=${clipDuration},setpts=PTS-STARTPTS[v${i}]`
423
+ );
424
+ }
425
+
426
+ let currentLabel = 'v0';
427
+ for (let i = 1; i < pngs.length; i++) {
428
+ const offset = ((clipDuration - transitionDuration) * i).toFixed(3);
429
+ const nextLabel = i === pngs.length - 1 ? 'outv' : `x${i}`;
430
+ const transition = transitions[(i - 1) % transitions.length];
431
+ filterParts.push(
432
+ `[${currentLabel}][v${i}]xfade=transition=${transition}:duration=${transitionDuration}:offset=${offset}[${nextLabel}]`
433
+ );
434
+ currentLabel = nextLabel;
395
435
  }
396
- concatLines.push(`file '${resolve(pngs.at(-1)).replaceAll("'", "'\\''")}'`);
397
- writeFileSync(concatPath, concatLines.join('\n') + '\n');
398
436
 
399
- const finalVideo = join(outDir, 'ogp-overview-demo.mp4');
400
- execFileSync('ffmpeg', [
401
- '-y',
402
- '-f', 'concat',
403
- '-safe', '0',
404
- '-i', concatPath,
405
- '-vf', 'format=yuv420p,fps=30',
406
- '-r', '30',
437
+ const animatedVideo = join(outDir, 'ogp-overview-demo-animated.mp4');
438
+ ffmpegArgs.push(
439
+ '-filter_complex', filterParts.join(';'),
440
+ '-map', `[${currentLabel}]`,
441
+ '-r', String(fps),
407
442
  '-c:v', 'libx264',
408
443
  '-movflags', '+faststart',
409
444
  '-pix_fmt', 'yuv420p',
410
- finalVideo
411
- ], { stdio: 'inherit' });
445
+ animatedVideo
446
+ );
447
+
448
+ execFileSync('ffmpeg', ffmpegArgs, { stdio: 'inherit' });
412
449
 
413
- if (!existsSync(finalVideo)) {
414
- throw new Error(`Render failed: ${finalVideo} was not created`);
450
+ if (!existsSync(animatedVideo)) {
451
+ throw new Error(`Render failed: ${animatedVideo} was not created`);
415
452
  }
416
453
 
417
- console.log(`Rendered ${finalVideo}`);
454
+ console.log(`Rendered ${animatedVideo}`);