@dp-pcs/ogp 0.7.2 → 0.8.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.
- package/README.md +59 -12
- package/dist/cli/config.d.ts +4 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +45 -2
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/expose.d.ts +4 -1
- package/dist/cli/expose.d.ts.map +1 -1
- package/dist/cli/expose.js +7 -106
- package/dist/cli/expose.js.map +1 -1
- package/dist/cli/install.d.ts +1 -0
- package/dist/cli/install.d.ts.map +1 -1
- package/dist/cli/install.js +8 -2
- package/dist/cli/install.js.map +1 -1
- package/dist/cli/project.d.ts +24 -0
- package/dist/cli/project.d.ts.map +1 -1
- package/dist/cli/project.js +68 -15
- package/dist/cli/project.js.map +1 -1
- package/dist/cli/tunnel.d.ts +65 -0
- package/dist/cli/tunnel.d.ts.map +1 -0
- package/dist/cli/tunnel.js +413 -0
- package/dist/cli/tunnel.js.map +1 -0
- package/dist/cli.js +21 -8
- package/dist/cli.js.map +1 -1
- package/dist/daemon/contribution-signing.d.ts +49 -0
- package/dist/daemon/contribution-signing.d.ts.map +1 -0
- package/dist/daemon/contribution-signing.js +91 -0
- package/dist/daemon/contribution-signing.js.map +1 -0
- package/dist/daemon/message-handler.js +41 -18
- package/dist/daemon/message-handler.js.map +1 -1
- package/dist/daemon/openclaw-bridge.d.ts +6 -0
- package/dist/daemon/openclaw-bridge.d.ts.map +1 -1
- package/dist/daemon/openclaw-bridge.js +27 -12
- package/dist/daemon/openclaw-bridge.js.map +1 -1
- package/dist/daemon/peers.d.ts.map +1 -1
- package/dist/daemon/peers.js +19 -0
- package/dist/daemon/peers.js.map +1 -1
- package/dist/daemon/projects.d.ts +20 -0
- package/dist/daemon/projects.d.ts.map +1 -1
- package/dist/daemon/projects.js +70 -0
- package/dist/daemon/projects.js.map +1 -1
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +43 -2
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/state-lock.d.ts +23 -0
- package/dist/daemon/state-lock.d.ts.map +1 -0
- package/dist/daemon/state-lock.js +115 -0
- package/dist/daemon/state-lock.js.map +1 -0
- package/package.json +13 -3
- package/scripts/completion.bash +25 -6
- package/scripts/completion.zsh +26 -8
- package/skills/ogp-expose/SKILL.md +40 -10
- package/docs/RC1-FEDERATION-TEST-CHECKLIST.md +0 -477
- package/docs/case-studies/CRASH_RESOLUTION_20260407.md +0 -190
- package/docs/case-studies/OpenClaw_Hermes_Status_Report_20260407.md +0 -142
- package/docs/case-studies/OpenClaw_Stability_Fix_Summary.md +0 -209
- package/docs/case-studies/README.md +0 -40
- package/docs/case-studies/crash_observations.md +0 -250
- package/docs/nat-hole-punch-spike.md +0 -399
- package/docs/project-intent-testing.md +0 -97
- package/scripts/render-ogp-overview-video.mjs +0 -454
- package/scripts/test-migration-execute.js +0 -74
- package/scripts/test-migration.js +0 -42
- package/scripts/test-project-intents.mjs +0 -614
|
@@ -1,454 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { execFileSync } from 'node:child_process';
|
|
3
|
-
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'node:fs';
|
|
4
|
-
import { join, resolve } from 'node:path';
|
|
5
|
-
|
|
6
|
-
const outDir = 'artifacts/ogp-overview-video';
|
|
7
|
-
const slideDir = join(outDir, 'slides');
|
|
8
|
-
mkdirSync(slideDir, { recursive: true });
|
|
9
|
-
|
|
10
|
-
const renderSlidesOnly = process.argv.includes('--slides-only');
|
|
11
|
-
|
|
12
|
-
const width = 1920;
|
|
13
|
-
const height = 1080;
|
|
14
|
-
const fps = 30;
|
|
15
|
-
const clipDuration = 6.8;
|
|
16
|
-
const transitionDuration = 0.6;
|
|
17
|
-
|
|
18
|
-
const palette = {
|
|
19
|
-
ink: '#10131c',
|
|
20
|
-
panel: '#171d2a',
|
|
21
|
-
panel2: '#1d2738',
|
|
22
|
-
line: '#3c485f',
|
|
23
|
-
text: '#f4f7fb',
|
|
24
|
-
muted: '#9eacc2',
|
|
25
|
-
teal: '#37d2c3',
|
|
26
|
-
amber: '#f3bb55',
|
|
27
|
-
coral: '#ff6f61',
|
|
28
|
-
blue: '#7ca7ff',
|
|
29
|
-
green: '#84d977'
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const slides = [
|
|
33
|
-
{
|
|
34
|
-
kicker: '@dp-pcs/ogp',
|
|
35
|
-
title: 'Open Gateway Protocol',
|
|
36
|
-
subtitle: 'BGP-style peering for AI gateways: signed, scoped, human-approved federation.',
|
|
37
|
-
body: [
|
|
38
|
-
'Agents can collaborate across different gateways without sharing credentials, memory, or raw context.',
|
|
39
|
-
'The gateway is the trust boundary.'
|
|
40
|
-
],
|
|
41
|
-
visual: 'network',
|
|
42
|
-
narration: 'Open Gateway Protocol, or OGP, is federation for AI gateways. The simple version: your assistant can call your coworker\'s assistant directly, without either of you copy-pasting messages or sharing credentials.'
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
kicker: 'The Problem',
|
|
46
|
-
title: 'Useful agents are trapped in local silos',
|
|
47
|
-
subtitle: 'OpenClaw, Hermes, and future gateways all have their own tools, memory, and security boundaries.',
|
|
48
|
-
body: [
|
|
49
|
-
'Today, collaboration usually means a human relays messages by hand.',
|
|
50
|
-
'That is slow, lossy, and impossible to audit at scale.'
|
|
51
|
-
],
|
|
52
|
-
visual: 'silos',
|
|
53
|
-
narration: 'The problem is that useful agents live inside local silos. OpenClaw, Hermes, and other gateways have their own tools and memory. Humans end up acting as the relay, which is slow and hard to audit.'
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
kicker: 'The Model',
|
|
57
|
-
title: 'Peer relationships, not central authority',
|
|
58
|
-
subtitle: 'Each gateway owns a keypair and forms bilateral relationships with peers.',
|
|
59
|
-
body: [
|
|
60
|
-
'Discovery starts at /.well-known/ogp.',
|
|
61
|
-
'A request becomes usable only after human approval.'
|
|
62
|
-
],
|
|
63
|
-
visual: 'handshake',
|
|
64
|
-
narration: 'OGP uses peer relationships instead of central authority. Each gateway owns a keypair. Federation starts with public discovery, then a signed request, then explicit human approval.'
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
kicker: 'Trust Boundary',
|
|
68
|
-
title: 'Agents stay contained inside their gateway',
|
|
69
|
-
subtitle: 'Peers send signed intents. They do not get direct access to tools, memory, shells, or model sessions.',
|
|
70
|
-
body: [
|
|
71
|
-
'Every cross-gateway message is attributable to a peer key.',
|
|
72
|
-
'Every inbound action runs through policy before it reaches an agent.'
|
|
73
|
-
],
|
|
74
|
-
visual: 'boundary',
|
|
75
|
-
narration: 'The important design choice is containment. Agents never leave their own gateway. A peer sends a signed intent, and the receiving gateway decides whether that intent is allowed before it reaches an agent.'
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
kicker: 'Scope Control',
|
|
79
|
-
title: 'Three layers of no',
|
|
80
|
-
subtitle: 'Capabilities say what a gateway can support. Grants say what this peer may use. Runtime enforcement checks every message.',
|
|
81
|
-
body: [
|
|
82
|
-
'Per-peer intents, topic limits, and rate limits make trust granular.',
|
|
83
|
-
'Revocation is unilateral and immediate.'
|
|
84
|
-
],
|
|
85
|
-
visual: 'layers',
|
|
86
|
-
narration: 'Permissions are not all-or-nothing. OGP has three layers: what the gateway can support, what this specific peer is granted, and what the runtime doorman allows for each message.'
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
kicker: 'Agent-Comms',
|
|
90
|
-
title: 'Structured messages between assistants',
|
|
91
|
-
subtitle: 'Topic routing, priorities, conversation IDs, and signed replies make remote collaboration practical.',
|
|
92
|
-
body: [
|
|
93
|
-
'Use cases include memory questions, project status, task handoffs, and peer-to-peer debugging.',
|
|
94
|
-
'Replies can return by callback or polling.'
|
|
95
|
-
],
|
|
96
|
-
visual: 'messages',
|
|
97
|
-
narration: 'On top of federation, OGP has agent-comms. That gives assistants topic routing, priorities, conversation threads, and signed replies, so remote collaboration can be structured instead of just chat pasted into chat.'
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
kicker: 'Projects',
|
|
101
|
-
title: 'Collaboration context without shared tooling',
|
|
102
|
-
subtitle: 'Project intents move high-level facts: join, contribute, query, and status.',
|
|
103
|
-
body: [
|
|
104
|
-
'A peer can use Beads, Linear, a notebook, or nothing at all.',
|
|
105
|
-
'OGP moves concise project facts across the federation boundary.'
|
|
106
|
-
],
|
|
107
|
-
visual: 'project',
|
|
108
|
-
narration: 'Projects are optional collaboration boundaries on top of federation. They do not require everyone to use the same task tool. OGP moves concise facts like joins, contributions, queries, and status.'
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
kicker: 'Multi-Framework',
|
|
112
|
-
title: 'One protocol, multiple runtimes',
|
|
113
|
-
subtitle: 'The reference daemon runs alongside OpenClaw, Hermes, or standalone setups with isolated state.',
|
|
114
|
-
body: [
|
|
115
|
-
'Meta-config supports multiple framework homes and daemon ports.',
|
|
116
|
-
'The wire protocol stays gateway-neutral.'
|
|
117
|
-
],
|
|
118
|
-
visual: 'frameworks',
|
|
119
|
-
narration: 'The reference implementation already supports multiple runtimes. It can run beside OpenClaw, Hermes, or a standalone gateway. The local adapter changes, but the wire protocol stays neutral.'
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
kicker: 'Where It Is Going',
|
|
123
|
-
title: 'One trust relationship, many agent personas',
|
|
124
|
-
subtitle: 'The v0.7 design advertises multiple addressable personas under one gateway keypair.',
|
|
125
|
-
body: [
|
|
126
|
-
'Junior, Sterling, Apollo, or any specialist can be visible per peer and per scope.',
|
|
127
|
-
'No extra federation handshake for every agent.'
|
|
128
|
-
],
|
|
129
|
-
visual: 'personas',
|
|
130
|
-
narration: 'The next design step is multi-agent personas. One human-level trust relationship can expose multiple addressable assistants, with grants that decide which peer can reach which persona.'
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
kicker: 'Demo Takeaway',
|
|
134
|
-
title: 'OGP turns agent collaboration into infrastructure',
|
|
135
|
-
subtitle: 'Signed messages, scoped grants, audit trails, and revocation give personal and team agents a real federation layer.',
|
|
136
|
-
body: [
|
|
137
|
-
'Not a chat bridge. Not a shared account. A protocol boundary.',
|
|
138
|
-
'Built in TypeScript, shipping as @dp-pcs/ogp.'
|
|
139
|
-
],
|
|
140
|
-
visual: 'final',
|
|
141
|
-
narration: 'The takeaway: OGP turns agent collaboration into infrastructure. Not a chat bridge. Not a shared account. A protocol boundary with signed messages, scoped grants, audit trails, and revocation.'
|
|
142
|
-
}
|
|
143
|
-
];
|
|
144
|
-
|
|
145
|
-
function esc(value) {
|
|
146
|
-
return String(value)
|
|
147
|
-
.replaceAll('&', '&')
|
|
148
|
-
.replaceAll('<', '<')
|
|
149
|
-
.replaceAll('>', '>')
|
|
150
|
-
.replaceAll('"', '"');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function wrapText(text, maxChars) {
|
|
154
|
-
const words = text.split(/\s+/);
|
|
155
|
-
const lines = [];
|
|
156
|
-
let line = '';
|
|
157
|
-
for (const word of words) {
|
|
158
|
-
const next = line ? `${line} ${word}` : word;
|
|
159
|
-
if (next.length > maxChars && line) {
|
|
160
|
-
lines.push(line);
|
|
161
|
-
line = word;
|
|
162
|
-
} else {
|
|
163
|
-
line = next;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (line) lines.push(line);
|
|
167
|
-
return lines;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function textBlock(lines, x, y, maxChars, size, fill, gap = Math.round(size * 1.35), weight = 500) {
|
|
171
|
-
const out = [];
|
|
172
|
-
let cursor = y;
|
|
173
|
-
for (const source of lines) {
|
|
174
|
-
for (const line of wrapText(source, maxChars)) {
|
|
175
|
-
out.push(`<text x="${x}" y="${cursor}" font-size="${size}" font-weight="${weight}" fill="${fill}">${esc(line)}</text>`);
|
|
176
|
-
cursor += gap;
|
|
177
|
-
}
|
|
178
|
-
cursor += Math.round(gap * 0.35);
|
|
179
|
-
}
|
|
180
|
-
return out.join('\n');
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function node(x, y, label, accent, sub = '') {
|
|
184
|
-
return `
|
|
185
|
-
<rect x="${x - 150}" y="${y - 72}" width="300" height="144" rx="18" fill="${palette.panel}" stroke="${accent}" stroke-width="3"/>
|
|
186
|
-
<circle cx="${x - 100}" cy="${y - 20}" r="16" fill="${accent}"/>
|
|
187
|
-
<text x="${x - 70}" y="${y - 18}" font-size="28" font-weight="800" fill="${palette.text}">${esc(label)}</text>
|
|
188
|
-
<text x="${x - 100}" y="${y + 30}" font-size="20" fill="${palette.muted}">${esc(sub)}</text>
|
|
189
|
-
`;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function arrow(x1, y1, x2, y2, color = palette.teal) {
|
|
193
|
-
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
194
|
-
const head = 18;
|
|
195
|
-
const hx1 = x2 - head * Math.cos(angle - Math.PI / 7);
|
|
196
|
-
const hy1 = y2 - head * Math.sin(angle - Math.PI / 7);
|
|
197
|
-
const hx2 = x2 - head * Math.cos(angle + Math.PI / 7);
|
|
198
|
-
const hy2 = y2 - head * Math.sin(angle + Math.PI / 7);
|
|
199
|
-
return `
|
|
200
|
-
<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${color}" stroke-width="5" stroke-linecap="round"/>
|
|
201
|
-
<path d="M ${x2} ${y2} L ${hx1} ${hy1} L ${hx2} ${hy2} Z" fill="${color}"/>
|
|
202
|
-
`;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function visual(name) {
|
|
206
|
-
switch (name) {
|
|
207
|
-
case 'network':
|
|
208
|
-
return `
|
|
209
|
-
${node(1230, 390, 'Gateway A', palette.teal, 'David + Junior')}
|
|
210
|
-
${node(1600, 390, 'Gateway B', palette.amber, 'Stan + agent')}
|
|
211
|
-
${arrow(1385, 390, 1445, 390, palette.blue)}
|
|
212
|
-
${arrow(1445, 430, 1385, 430, palette.coral)}
|
|
213
|
-
<text x="1362" y="330" font-size="22" fill="${palette.muted}">signed OGP</text>
|
|
214
|
-
<rect x="1140" y="575" width="560" height="160" rx="20" fill="${palette.panel2}" stroke="${palette.line}"/>
|
|
215
|
-
<text x="1190" y="635" font-size="28" font-weight="800" fill="${palette.text}">No central relay</text>
|
|
216
|
-
<text x="1190" y="690" font-size="24" fill="${palette.muted}">No shared credentials. No copied context.</text>
|
|
217
|
-
`;
|
|
218
|
-
case 'silos':
|
|
219
|
-
return `
|
|
220
|
-
<rect x="1110" y="270" width="240" height="470" rx="28" fill="${palette.panel}" stroke="${palette.teal}" stroke-width="3"/>
|
|
221
|
-
<rect x="1440" y="270" width="240" height="470" rx="28" fill="${palette.panel}" stroke="${palette.amber}" stroke-width="3"/>
|
|
222
|
-
<text x="1170" y="350" font-size="32" font-weight="800" fill="${palette.text}">OpenClaw</text>
|
|
223
|
-
<text x="1512" y="350" font-size="32" font-weight="800" fill="${palette.text}">Hermes</text>
|
|
224
|
-
<line x1="1350" y1="510" x2="1440" y2="510" stroke="${palette.coral}" stroke-width="5" stroke-dasharray="16 16"/>
|
|
225
|
-
<text x="1295" y="575" font-size="24" fill="${palette.coral}">human relay</text>
|
|
226
|
-
<text x="1150" y="670" font-size="22" fill="${palette.muted}">tools</text>
|
|
227
|
-
<text x="1150" y="710" font-size="22" fill="${palette.muted}">memory</text>
|
|
228
|
-
<text x="1485" y="670" font-size="22" fill="${palette.muted}">tools</text>
|
|
229
|
-
<text x="1485" y="710" font-size="22" fill="${palette.muted}">memory</text>
|
|
230
|
-
`;
|
|
231
|
-
case 'handshake':
|
|
232
|
-
return `
|
|
233
|
-
${node(1090, 310, '1. Discover', palette.blue, '/.well-known/ogp')}
|
|
234
|
-
${node(1435, 500, '2. Request', palette.teal, 'signed body')}
|
|
235
|
-
${node(1090, 690, '3. Approve', palette.amber, 'human gate')}
|
|
236
|
-
${arrow(1235, 340, 1295, 455, palette.blue)}
|
|
237
|
-
${arrow(1295, 545, 1235, 655, palette.amber)}
|
|
238
|
-
`;
|
|
239
|
-
case 'boundary':
|
|
240
|
-
return `
|
|
241
|
-
<rect x="1110" y="250" width="630" height="500" rx="30" fill="${palette.panel}" stroke="${palette.teal}" stroke-width="4"/>
|
|
242
|
-
<text x="1170" y="330" font-size="36" font-weight="900" fill="${palette.text}">Gateway policy boundary</text>
|
|
243
|
-
<rect x="1190" y="400" width="200" height="110" rx="16" fill="${palette.panel2}" stroke="${palette.line}"/>
|
|
244
|
-
<text x="1245" y="468" font-size="30" font-weight="800" fill="${palette.text}">Doorman</text>
|
|
245
|
-
<rect x="1490" y="400" width="160" height="110" rx="16" fill="${palette.panel2}" stroke="${palette.line}"/>
|
|
246
|
-
<text x="1534" y="468" font-size="30" font-weight="800" fill="${palette.text}">Agent</text>
|
|
247
|
-
${arrow(1030, 455, 1180, 455, palette.coral)}
|
|
248
|
-
${arrow(1395, 455, 1480, 455, palette.green)}
|
|
249
|
-
<text x="1035" y="410" font-size="22" fill="${palette.muted}">signed intent</text>
|
|
250
|
-
<text x="1400" y="410" font-size="22" fill="${palette.muted}">allowed</text>
|
|
251
|
-
`;
|
|
252
|
-
case 'layers':
|
|
253
|
-
return [0, 1, 2].map((i) => {
|
|
254
|
-
const labels = ['Capabilities', 'Peer grants', 'Runtime doorman'];
|
|
255
|
-
const subs = ['what I can support', 'what you may use', 'this request passes?'];
|
|
256
|
-
const colors = [palette.blue, palette.amber, palette.teal];
|
|
257
|
-
const y = 300 + i * 155;
|
|
258
|
-
return `<rect x="1110" y="${y}" width="620" height="105" rx="18" fill="${palette.panel}" stroke="${colors[i]}" stroke-width="3"/>
|
|
259
|
-
<text x="1160" y="${y + 44}" font-size="30" font-weight="900" fill="${palette.text}">${labels[i]}</text>
|
|
260
|
-
<text x="1160" y="${y + 82}" font-size="23" fill="${palette.muted}">${subs[i]}</text>`;
|
|
261
|
-
}).join('\n');
|
|
262
|
-
case 'messages':
|
|
263
|
-
return `
|
|
264
|
-
<rect x="1110" y="265" width="620" height="475" rx="26" fill="${palette.panel}" stroke="${palette.line}"/>
|
|
265
|
-
<text x="1170" y="340" font-size="30" font-weight="900" fill="${palette.text}">agent-comms</text>
|
|
266
|
-
<text x="1170" y="410" font-size="25" fill="${palette.teal}">topic: project-alpha</text>
|
|
267
|
-
<text x="1170" y="470" font-size="25" fill="${palette.amber}">priority: high</text>
|
|
268
|
-
<text x="1170" y="530" font-size="25" fill="${palette.blue}">conversationId: sprint-42</text>
|
|
269
|
-
<text x="1170" y="590" font-size="25" fill="${palette.green}">reply: signed callback</text>
|
|
270
|
-
<rect x="1190" y="645" width="460" height="46" rx="12" fill="${palette.panel2}"/>
|
|
271
|
-
<text x="1220" y="676" font-size="22" fill="${palette.muted}">structured enough to automate</text>
|
|
272
|
-
`;
|
|
273
|
-
case 'project':
|
|
274
|
-
return `
|
|
275
|
-
<rect x="1120" y="300" width="580" height="390" rx="28" fill="${palette.panel}" stroke="${palette.line}"/>
|
|
276
|
-
${['project.join', 'project.contribute', 'project.query', 'project.status'].map((label, i) => {
|
|
277
|
-
const y = 365 + i * 75;
|
|
278
|
-
const c = [palette.teal, palette.amber, palette.blue, palette.green][i];
|
|
279
|
-
return `<circle cx="1180" cy="${y}" r="15" fill="${c}"/><text x="1225" y="${y + 9}" font-size="30" font-weight="800" fill="${palette.text}">${label}</text>`;
|
|
280
|
-
}).join('\n')}
|
|
281
|
-
`;
|
|
282
|
-
case 'frameworks':
|
|
283
|
-
return `
|
|
284
|
-
${node(1120, 350, 'OpenClaw', palette.teal, '~/.ogp-openclaw')}
|
|
285
|
-
${node(1510, 350, 'Hermes', palette.amber, '~/.ogp-hermes')}
|
|
286
|
-
<rect x="1208" y="590" width="540" height="130" rx="22" fill="${palette.panel2}" stroke="${palette.blue}" stroke-width="3"/>
|
|
287
|
-
<text x="1270" y="645" font-size="34" font-weight="900" fill="${palette.text}">Same OGP wire protocol</text>
|
|
288
|
-
<text x="1270" y="690" font-size="23" fill="${palette.muted}">adapter changes, federation stays stable</text>
|
|
289
|
-
`;
|
|
290
|
-
case 'personas':
|
|
291
|
-
return `
|
|
292
|
-
<rect x="1090" y="250" width="670" height="500" rx="30" fill="${palette.panel}" stroke="${palette.teal}" stroke-width="4"/>
|
|
293
|
-
<text x="1160" y="320" font-size="32" font-weight="900" fill="${palette.text}">David's gateway keypair</text>
|
|
294
|
-
${['Junior - primary', 'Sterling - finance', 'Apollo - research'].map((label, i) => {
|
|
295
|
-
const y = 405 + i * 95;
|
|
296
|
-
const c = [palette.teal, palette.amber, palette.blue][i];
|
|
297
|
-
return `<rect x="1190" y="${y}" width="440" height="62" rx="16" fill="${palette.panel2}" stroke="${c}" stroke-width="2"/>
|
|
298
|
-
<text x="1230" y="${y + 41}" font-size="28" font-weight="800" fill="${palette.text}">${label}</text>`;
|
|
299
|
-
}).join('\n')}
|
|
300
|
-
<text x="1160" y="705" font-size="24" fill="${palette.muted}">peer x intent x persona grants</text>
|
|
301
|
-
`;
|
|
302
|
-
case 'final':
|
|
303
|
-
return `
|
|
304
|
-
<circle cx="1385" cy="500" r="220" fill="${palette.panel}" stroke="${palette.teal}" stroke-width="5"/>
|
|
305
|
-
<text x="1286" y="482" font-size="54" font-weight="950" fill="${palette.text}">OGP</text>
|
|
306
|
-
<text x="1190" y="545" font-size="28" fill="${palette.muted}">signed scoped federation</text>
|
|
307
|
-
<text x="1185" y="680" font-size="26" fill="${palette.amber}">npm install -g @dp-pcs/ogp</text>
|
|
308
|
-
`;
|
|
309
|
-
default:
|
|
310
|
-
return '';
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function svg(slide, index) {
|
|
315
|
-
const progress = `${String(index + 1).padStart(2, '0')} / ${String(slides.length).padStart(2, '0')}`;
|
|
316
|
-
const titleLines = wrapText(slide.title, 32);
|
|
317
|
-
const titleSvg = titleLines.map((line, i) =>
|
|
318
|
-
`<text x="140" y="${230 + i * 82}" font-size="76" font-weight="950" fill="${palette.text}">${esc(line)}</text>`
|
|
319
|
-
).join('\n');
|
|
320
|
-
const subtitleY = titleLines.length > 1 ? 390 : 306;
|
|
321
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
322
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
323
|
-
<defs>
|
|
324
|
-
<radialGradient id="glow" cx="68%" cy="35%" r="55%">
|
|
325
|
-
<stop offset="0%" stop-color="#25445b"/>
|
|
326
|
-
<stop offset="55%" stop-color="#141b27"/>
|
|
327
|
-
<stop offset="100%" stop-color="${palette.ink}"/>
|
|
328
|
-
</radialGradient>
|
|
329
|
-
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
|
330
|
-
<feDropShadow dx="0" dy="22" stdDeviation="20" flood-color="#000000" flood-opacity="0.28"/>
|
|
331
|
-
</filter>
|
|
332
|
-
</defs>
|
|
333
|
-
<rect width="${width}" height="${height}" fill="url(#glow)"/>
|
|
334
|
-
<path d="M 0 980 C 330 890 630 1030 960 945 C 1320 852 1590 975 1920 870 L 1920 1080 L 0 1080 Z" fill="#0b0f17" opacity="0.88"/>
|
|
335
|
-
<g font-family="Avenir Next, Helvetica Neue, Arial, sans-serif">
|
|
336
|
-
<text x="140" y="120" font-size="24" font-weight="800" fill="${palette.teal}">${esc(slide.kicker.toUpperCase())}</text>
|
|
337
|
-
${titleSvg}
|
|
338
|
-
${textBlock([slide.subtitle], 140, subtitleY, 48, 34, palette.muted, 46, 500)}
|
|
339
|
-
<g transform="translate(0, 40)">
|
|
340
|
-
${textBlock(slide.body, 170, 520, 49, 28, palette.text, 40, 600)}
|
|
341
|
-
</g>
|
|
342
|
-
<g filter="url(#shadow)">
|
|
343
|
-
${visual(slide.visual)}
|
|
344
|
-
</g>
|
|
345
|
-
<text x="140" y="980" font-size="22" fill="${palette.muted}">Open Gateway Protocol</text>
|
|
346
|
-
<text x="1700" y="980" font-size="22" fill="${palette.muted}">${progress}</text>
|
|
347
|
-
</g>
|
|
348
|
-
</svg>`;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if (renderSlidesOnly) {
|
|
352
|
-
rmSync(slideDir, { recursive: true, force: true });
|
|
353
|
-
}
|
|
354
|
-
mkdirSync(slideDir, { recursive: true });
|
|
355
|
-
|
|
356
|
-
const pngs = [];
|
|
357
|
-
for (let i = 0; i < slides.length; i++) {
|
|
358
|
-
const base = `slide-${String(i + 1).padStart(2, '0')}`;
|
|
359
|
-
const svgPath = join(slideDir, `${base}.svg`);
|
|
360
|
-
const pngPath = join(slideDir, `${base}.png`);
|
|
361
|
-
writeFileSync(svgPath, svg(slides[i], i));
|
|
362
|
-
pngs.push(pngPath);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const storyboard = slides.map((slide, i) => [
|
|
366
|
-
`## ${i + 1}. ${slide.title}`,
|
|
367
|
-
`Kicker: ${slide.kicker}`,
|
|
368
|
-
`Subtitle: ${slide.subtitle}`,
|
|
369
|
-
'',
|
|
370
|
-
slide.body.map((item) => `- ${item}`).join('\n'),
|
|
371
|
-
'',
|
|
372
|
-
`Narration: ${slide.narration}`,
|
|
373
|
-
''
|
|
374
|
-
].join('\n')).join('\n');
|
|
375
|
-
writeFileSync(join(outDir, 'storyboard.md'), `# OGP Overview Demo Storyboard\n\n${storyboard}`);
|
|
376
|
-
|
|
377
|
-
const narrationText = slides.map((slide) => slide.narration).join('\n\n');
|
|
378
|
-
writeFileSync(join(outDir, 'narration.txt'), narrationText);
|
|
379
|
-
|
|
380
|
-
if (renderSlidesOnly) {
|
|
381
|
-
console.log(`Wrote SVG slides and narration scaffold to ${outDir}`);
|
|
382
|
-
process.exit(0);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
for (const png of pngs) {
|
|
386
|
-
if (!existsSync(png)) {
|
|
387
|
-
throw new Error(`Missing PNG slide ${png}. Run SVG-to-PNG capture before final rendering.`);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
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'];
|
|
410
|
-
for (const png of pngs) {
|
|
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;
|
|
435
|
-
}
|
|
436
|
-
|
|
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),
|
|
442
|
-
'-c:v', 'libx264',
|
|
443
|
-
'-movflags', '+faststart',
|
|
444
|
-
'-pix_fmt', 'yuv420p',
|
|
445
|
-
animatedVideo
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
execFileSync('ffmpeg', ffmpegArgs, { stdio: 'inherit' });
|
|
449
|
-
|
|
450
|
-
if (!existsSync(animatedVideo)) {
|
|
451
|
-
throw new Error(`Render failed: ${animatedVideo} was not created`);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
console.log(`Rendered ${animatedVideo}`);
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Test script for executing migration
|
|
5
|
-
* Usage: node scripts/test-migration-execute.js [--dry-run]
|
|
6
|
-
*
|
|
7
|
-
* Use --dry-run to see what would happen without actually executing
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { detectExistingInstallations, executeMigration } from '../dist/shared/migration.js';
|
|
11
|
-
import { loadMetaConfig } from '../dist/shared/meta-config.js';
|
|
12
|
-
|
|
13
|
-
const dryRun = process.argv.includes('--dry-run');
|
|
14
|
-
|
|
15
|
-
console.log('=== OGP Migration Execution Test ===\n');
|
|
16
|
-
|
|
17
|
-
if (dryRun) {
|
|
18
|
-
console.log('DRY RUN MODE - No changes will be made\n');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Detect existing installations
|
|
22
|
-
const plan = detectExistingInstallations();
|
|
23
|
-
|
|
24
|
-
if (!plan.needed) {
|
|
25
|
-
console.log('No migration needed.');
|
|
26
|
-
process.exit(0);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
console.log('Detected Installations:');
|
|
30
|
-
for (const install of plan.existingInstalls) {
|
|
31
|
-
console.log(` - ${install.path} (${install.framework})`);
|
|
32
|
-
}
|
|
33
|
-
console.log('');
|
|
34
|
-
|
|
35
|
-
console.log('Migration Plan:');
|
|
36
|
-
for (const action of plan.actions) {
|
|
37
|
-
console.log(` ${action.type.toUpperCase()}: ${action.description}`);
|
|
38
|
-
if (action.from && action.to) {
|
|
39
|
-
console.log(` ${action.from} -> ${action.to}`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
console.log('');
|
|
43
|
-
|
|
44
|
-
if (dryRun) {
|
|
45
|
-
console.log('Dry run complete. Use without --dry-run to execute migration.');
|
|
46
|
-
process.exit(0);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Execute migration
|
|
50
|
-
console.log('Executing migration...\n');
|
|
51
|
-
try {
|
|
52
|
-
await executeMigration(plan);
|
|
53
|
-
console.log('\n✓ Migration completed successfully!\n');
|
|
54
|
-
|
|
55
|
-
// Load and display the new meta config
|
|
56
|
-
const metaConfig = loadMetaConfig();
|
|
57
|
-
console.log('Meta Configuration:');
|
|
58
|
-
console.log(` Version: ${metaConfig.version}`);
|
|
59
|
-
console.log(` Default Framework: ${metaConfig.default}`);
|
|
60
|
-
console.log(' Frameworks:');
|
|
61
|
-
for (const framework of metaConfig.frameworks) {
|
|
62
|
-
console.log(` - ${framework.id} (${framework.name})`);
|
|
63
|
-
console.log(` Enabled: ${framework.enabled}`);
|
|
64
|
-
console.log(` Config Dir: ${framework.configDir}`);
|
|
65
|
-
console.log(` Daemon Port: ${framework.daemonPort}`);
|
|
66
|
-
console.log(` Display Name: ${framework.displayName}`);
|
|
67
|
-
console.log('');
|
|
68
|
-
}
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.error('\n✗ Migration failed:', error.message);
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
console.log('=== Test Complete ===');
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Test script for migration detection
|
|
5
|
-
* Usage: node scripts/test-migration.js
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { detectExistingInstallations, checkMigrationStatus } from '../dist/shared/migration.js';
|
|
9
|
-
|
|
10
|
-
console.log('=== OGP Migration Detection Test ===\n');
|
|
11
|
-
|
|
12
|
-
// Check migration status
|
|
13
|
-
const status = checkMigrationStatus();
|
|
14
|
-
console.log('Migration Status:');
|
|
15
|
-
console.log(` Needed: ${status.migrationNeeded}`);
|
|
16
|
-
console.log(` Summary: ${status.summary}\n`);
|
|
17
|
-
|
|
18
|
-
if (status.plan) {
|
|
19
|
-
const plan = status.plan;
|
|
20
|
-
|
|
21
|
-
console.log('Detected Installations:');
|
|
22
|
-
for (const install of plan.existingInstalls) {
|
|
23
|
-
console.log(` - ${install.path}`);
|
|
24
|
-
console.log(` Framework: ${install.framework}`);
|
|
25
|
-
console.log(` Display Name: ${install.config.displayName}`);
|
|
26
|
-
console.log(` Daemon Port: ${install.config.daemonPort}`);
|
|
27
|
-
console.log(` Platform: ${install.config.platform || '(not set)'}`);
|
|
28
|
-
console.log('');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
console.log('Migration Actions:');
|
|
32
|
-
for (const action of plan.actions) {
|
|
33
|
-
console.log(` ${action.type.toUpperCase()}: ${action.description}`);
|
|
34
|
-
if (action.from && action.to) {
|
|
35
|
-
console.log(` ${action.from} -> ${action.to}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
console.log('No migration plan needed.');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
console.log('\n=== Test Complete ===');
|