@kwirthmagnify/kwirth-docs-pinocchio 0.2.31
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/_sidebar.md +21 -0
- package/admin/01-setup.md +98 -0
- package/admin/02-ai-config.md +125 -0
- package/admin/03-tools.md +131 -0
- package/admin/04-prompts.md +147 -0
- package/admin/05-rbac.md +89 -0
- package/admin/06-limits.md +113 -0
- package/images/admin-ai-llm.png +0 -0
- package/images/admin-ai-provider.png +0 -0
- package/images/import-export.png +0 -0
- package/images/playground-call.png +0 -0
- package/images/playground-in.png +0 -0
- package/images/playground-llm.png +0 -0
- package/images/playground-out.png +0 -0
- package/images/tool-selector.png +0 -0
- package/images/triggers-dialog.png +0 -0
- package/images/ui-clear-dialog.png +0 -0
- package/images/ui-config-menu.png +0 -0
- package/images/ui-tab.png +0 -0
- package/index.html +64 -0
- package/index.md +42 -0
- package/kwirth-dark.css +458 -0
- package/package.json +9 -0
- package/serve.cmd +1 -0
- package/serve.mjs +84 -0
- package/user/00-how-it-works.md +88 -0
- package/user/01-introduction.md +60 -0
- package/user/02-ui-tour.md +96 -0
- package/user/03-concepts.md +160 -0
- package/user/04-triggers.md +96 -0
- package/user/05-findings.md +108 -0
- package/user/06-playground.md +134 -0
- package/user/07-import-export.md +83 -0
package/kwirth-dark.css
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/* Kwirth Dark Theme for Docsify — matches homepage style */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--theme-color: #f5820a;
|
|
5
|
+
--bg: #060a12;
|
|
6
|
+
--bg2: #0d1420;
|
|
7
|
+
--bg3: #111827;
|
|
8
|
+
--card: #0f1929;
|
|
9
|
+
--border: #1e2d40;
|
|
10
|
+
--orange: #f5820a;
|
|
11
|
+
--orange2: #ff9a2e;
|
|
12
|
+
--teal: #2dd4bf;
|
|
13
|
+
--blue: #58a6ff;
|
|
14
|
+
--green: #3fb950;
|
|
15
|
+
--yellow: #e3b341;
|
|
16
|
+
--red: #ff7b72;
|
|
17
|
+
--text: #e6edf3;
|
|
18
|
+
--muted: #8b949e;
|
|
19
|
+
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
20
|
+
--mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* ── Base ─────────────────────────────────────────────────────── */
|
|
24
|
+
|
|
25
|
+
body {
|
|
26
|
+
background: var(--bg) !important;
|
|
27
|
+
color: var(--text);
|
|
28
|
+
font-family: var(--font);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* ── Sidebar ──────────────────────────────────────────────────── */
|
|
32
|
+
|
|
33
|
+
.sidebar {
|
|
34
|
+
background: var(--bg2) !important;
|
|
35
|
+
border-right: 1px solid var(--border) !important;
|
|
36
|
+
color: var(--text);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.sidebar > h1 {
|
|
40
|
+
color: var(--orange) !important;
|
|
41
|
+
font-weight: 700;
|
|
42
|
+
letter-spacing: -0.3px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.sidebar > h1 a {
|
|
46
|
+
color: var(--orange) !important;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.sidebar ul {
|
|
50
|
+
padding: 0 12px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.sidebar ul li {
|
|
54
|
+
list-style: none;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.sidebar ul li a {
|
|
58
|
+
color: var(--muted) !important;
|
|
59
|
+
font-size: 0.875rem;
|
|
60
|
+
padding: 5px 10px;
|
|
61
|
+
border-radius: 6px;
|
|
62
|
+
display: block;
|
|
63
|
+
transition: all 0.2s;
|
|
64
|
+
text-decoration: none;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.sidebar ul li a:hover,
|
|
68
|
+
.sidebar ul li a.active {
|
|
69
|
+
color: var(--orange) !important;
|
|
70
|
+
background: rgba(245, 130, 10, 0.08) !important;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.sidebar ul li ul li a {
|
|
74
|
+
color: var(--muted) !important;
|
|
75
|
+
padding-left: 20px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.sidebar ul li ul li a:hover,
|
|
79
|
+
.sidebar ul li ul li a.active {
|
|
80
|
+
color: var(--orange) !important;
|
|
81
|
+
background: rgba(245, 130, 10, 0.08) !important;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.sidebar-toggle {
|
|
85
|
+
background: var(--bg2) !important;
|
|
86
|
+
border-right: 1px solid var(--border) !important;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.sidebar-toggle span {
|
|
90
|
+
background-color: var(--muted) !important;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* ── App Nav ──────────────────────────────────────────────────── */
|
|
94
|
+
|
|
95
|
+
nav.app-nav {
|
|
96
|
+
background: rgba(6, 10, 18, 0.92) !important;
|
|
97
|
+
backdrop-filter: blur(12px);
|
|
98
|
+
border-bottom: 1px solid var(--border) !important;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
nav.app-nav a {
|
|
102
|
+
color: var(--muted) !important;
|
|
103
|
+
font-size: 0.875rem;
|
|
104
|
+
padding: 6px 12px;
|
|
105
|
+
border-radius: 6px;
|
|
106
|
+
transition: all 0.2s;
|
|
107
|
+
text-decoration: none;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
nav.app-nav a:hover {
|
|
111
|
+
color: var(--text) !important;
|
|
112
|
+
background: var(--border) !important;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* ── Content / Markdown ───────────────────────────────────────── */
|
|
116
|
+
|
|
117
|
+
.content {
|
|
118
|
+
background: var(--bg) !important;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.markdown-section {
|
|
122
|
+
color: var(--text) !important;
|
|
123
|
+
max-width: 860px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.markdown-section h1 {
|
|
127
|
+
color: var(--orange) !important;
|
|
128
|
+
font-weight: 800;
|
|
129
|
+
letter-spacing: -0.5px;
|
|
130
|
+
border-bottom: 1px solid var(--border);
|
|
131
|
+
padding-bottom: 14px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.markdown-section h2 {
|
|
135
|
+
color: var(--text) !important;
|
|
136
|
+
font-weight: 700;
|
|
137
|
+
border-bottom: 1px solid var(--border);
|
|
138
|
+
padding-bottom: 10px;
|
|
139
|
+
letter-spacing: -0.3px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.markdown-section h3,
|
|
143
|
+
.markdown-section h4,
|
|
144
|
+
.markdown-section h5,
|
|
145
|
+
.markdown-section h6 {
|
|
146
|
+
color: var(--text) !important;
|
|
147
|
+
font-weight: 600;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.markdown-section a {
|
|
151
|
+
color: var(--orange) !important;
|
|
152
|
+
text-decoration: none;
|
|
153
|
+
transition: color 0.2s;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.markdown-section a:hover {
|
|
157
|
+
color: var(--orange2) !important;
|
|
158
|
+
text-decoration: underline;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.markdown-section strong {
|
|
162
|
+
color: var(--text);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.markdown-section em {
|
|
166
|
+
color: var(--muted);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.markdown-section hr {
|
|
170
|
+
border: none;
|
|
171
|
+
border-top: 1px solid var(--border);
|
|
172
|
+
margin: 32px 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* ── Code Blocks ─────────────────────────────────────────────── */
|
|
176
|
+
|
|
177
|
+
.markdown-section code {
|
|
178
|
+
background: var(--bg3) !important;
|
|
179
|
+
color: var(--teal) !important;
|
|
180
|
+
padding: 2px 6px;
|
|
181
|
+
border-radius: 4px;
|
|
182
|
+
font-family: var(--mono);
|
|
183
|
+
font-size: 0.875em;
|
|
184
|
+
border: 1px solid var(--border);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.markdown-section pre {
|
|
188
|
+
background: var(--bg2) !important;
|
|
189
|
+
border: 1px solid var(--border) !important;
|
|
190
|
+
border-radius: 10px !important;
|
|
191
|
+
padding: 20px 24px !important;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.markdown-section pre code {
|
|
195
|
+
background: transparent !important;
|
|
196
|
+
border: none !important;
|
|
197
|
+
color: var(--text) !important;
|
|
198
|
+
padding: 0 !important;
|
|
199
|
+
font-size: 0.875rem;
|
|
200
|
+
line-height: 1.7;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ── Blockquotes ─────────────────────────────────────────────── */
|
|
204
|
+
|
|
205
|
+
.markdown-section blockquote {
|
|
206
|
+
border-left: 4px solid var(--orange) !important;
|
|
207
|
+
background: rgba(245, 130, 10, 0.05) !important;
|
|
208
|
+
color: var(--muted) !important;
|
|
209
|
+
padding: 14px 20px;
|
|
210
|
+
border-radius: 0 8px 8px 0;
|
|
211
|
+
margin: 20px 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.markdown-section blockquote p {
|
|
215
|
+
color: var(--muted) !important;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* ── Tables ──────────────────────────────────────────────────── */
|
|
219
|
+
|
|
220
|
+
.markdown-section table {
|
|
221
|
+
display: table;
|
|
222
|
+
border-collapse: collapse;
|
|
223
|
+
width: auto;
|
|
224
|
+
max-width: 100%;
|
|
225
|
+
border: 1px solid var(--border);
|
|
226
|
+
border-radius: 8px;
|
|
227
|
+
overflow: hidden;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.markdown-section table th {
|
|
231
|
+
background: var(--card) !important;
|
|
232
|
+
color: var(--text) !important;
|
|
233
|
+
border: 1px solid var(--border) !important;
|
|
234
|
+
font-weight: 700;
|
|
235
|
+
text-align: left;
|
|
236
|
+
padding: 12px 16px;
|
|
237
|
+
font-size: 0.85rem;
|
|
238
|
+
text-transform: uppercase;
|
|
239
|
+
letter-spacing: 0.5px;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.markdown-section table td {
|
|
243
|
+
border: 1px solid var(--border) !important;
|
|
244
|
+
padding: 10px 16px;
|
|
245
|
+
color: var(--text) !important;
|
|
246
|
+
font-size: 0.9rem;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.markdown-section table tr {
|
|
250
|
+
background: var(--bg) !important;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.markdown-section table tr:nth-child(2n) {
|
|
254
|
+
background: var(--bg2) !important;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.markdown-section table tr:hover {
|
|
258
|
+
background: var(--card) !important;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* ── Docsify callouts (!> warn, ?> tip) ──────────────────────── */
|
|
262
|
+
|
|
263
|
+
.markdown-section .warn,
|
|
264
|
+
.markdown-section .warning,
|
|
265
|
+
.markdown-section blockquote.warn,
|
|
266
|
+
.markdown-section blockquote.warning {
|
|
267
|
+
background: rgba(245, 130, 10, 0.08) !important;
|
|
268
|
+
border-left: 4px solid var(--orange) !important;
|
|
269
|
+
border-radius: 0 8px 8px 0;
|
|
270
|
+
padding: 14px 20px !important;
|
|
271
|
+
margin: 20px 0 !important;
|
|
272
|
+
color: var(--text) !important;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.markdown-section .warn p,
|
|
276
|
+
.markdown-section .warning p {
|
|
277
|
+
color: var(--text) !important;
|
|
278
|
+
margin: 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.markdown-section .tip,
|
|
282
|
+
.markdown-section blockquote.tip {
|
|
283
|
+
background: rgba(45, 212, 191, 0.08) !important;
|
|
284
|
+
border-left: 4px solid var(--teal) !important;
|
|
285
|
+
border-radius: 0 8px 8px 0;
|
|
286
|
+
padding: 14px 20px !important;
|
|
287
|
+
margin: 20px 0 !important;
|
|
288
|
+
color: var(--text) !important;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.markdown-section .tip p {
|
|
292
|
+
color: var(--text) !important;
|
|
293
|
+
margin: 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* ── Lists ───────────────────────────────────────────────────── */
|
|
297
|
+
|
|
298
|
+
.markdown-section ul li::before {
|
|
299
|
+
color: var(--orange);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.markdown-section ol li {
|
|
303
|
+
color: var(--text);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* ── Search ──────────────────────────────────────────────────── */
|
|
307
|
+
|
|
308
|
+
.search {
|
|
309
|
+
border-bottom: 1px solid var(--border) !important;
|
|
310
|
+
padding: 10px 16px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.search input {
|
|
314
|
+
background: var(--card) !important;
|
|
315
|
+
border: 1px solid var(--border) !important;
|
|
316
|
+
color: var(--text) !important;
|
|
317
|
+
border-radius: 6px !important;
|
|
318
|
+
padding: 8px 12px !important;
|
|
319
|
+
font-size: 0.875rem;
|
|
320
|
+
width: 100%;
|
|
321
|
+
outline: none !important;
|
|
322
|
+
box-shadow: none !important;
|
|
323
|
+
transition: border-color 0.2s;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.search input:focus {
|
|
327
|
+
border-color: var(--orange) !important;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.search input::placeholder {
|
|
331
|
+
color: var(--muted);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.search .clear-button svg {
|
|
335
|
+
stroke: var(--muted);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.search .results-panel {
|
|
339
|
+
background: var(--card) !important;
|
|
340
|
+
border: 1px solid var(--border) !important;
|
|
341
|
+
border-radius: 8px !important;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.search .matching-post {
|
|
345
|
+
border-bottom: 1px solid var(--border) !important;
|
|
346
|
+
padding: 12px 16px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.search .matching-post a {
|
|
350
|
+
color: var(--orange) !important;
|
|
351
|
+
font-weight: 600;
|
|
352
|
+
text-decoration: none;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.search .matching-post p {
|
|
356
|
+
color: var(--muted) !important;
|
|
357
|
+
font-size: 0.85rem;
|
|
358
|
+
margin: 4px 0 0;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* ── Progress bar (NProgress) ────────────────────────────────── */
|
|
362
|
+
|
|
363
|
+
#nprogress .bar {
|
|
364
|
+
background: var(--orange) !important;
|
|
365
|
+
height: 2px !important;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#nprogress .peg {
|
|
369
|
+
box-shadow: 0 0 10px var(--orange), 0 0 5px var(--orange) !important;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
#nprogress .spinner-icon {
|
|
373
|
+
border-top-color: var(--orange) !important;
|
|
374
|
+
border-left-color: var(--orange) !important;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* ── Scrollbar ───────────────────────────────────────────────── */
|
|
378
|
+
|
|
379
|
+
::-webkit-scrollbar {
|
|
380
|
+
width: 6px;
|
|
381
|
+
height: 6px;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
::-webkit-scrollbar-track {
|
|
385
|
+
background: var(--bg);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
::-webkit-scrollbar-thumb {
|
|
389
|
+
background: var(--border);
|
|
390
|
+
border-radius: 3px;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
::-webkit-scrollbar-thumb:hover {
|
|
394
|
+
background: var(--orange);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/* ── Screenshots (guide uses plain markdown images) ──────────── */
|
|
398
|
+
|
|
399
|
+
.markdown-section img {
|
|
400
|
+
display: block;
|
|
401
|
+
margin: 16px auto;
|
|
402
|
+
border: 1px solid var(--border) !important;
|
|
403
|
+
border-radius: 8px;
|
|
404
|
+
max-width: 100%;
|
|
405
|
+
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/* ── Sidebar section headers (collapsible groups) ────────────── */
|
|
409
|
+
|
|
410
|
+
/* The bold top-level items ("Using Excubitor", "Administering Excubitor")
|
|
411
|
+
render as <strong>; style them as clean section headers, not big links. */
|
|
412
|
+
.sidebar-nav > ul > li > strong,
|
|
413
|
+
.sidebar-nav > ul > li > p > strong {
|
|
414
|
+
display: block;
|
|
415
|
+
padding: 6px 10px 6px 24px;
|
|
416
|
+
margin-top: 6px;
|
|
417
|
+
color: var(--muted) !important;
|
|
418
|
+
font-size: 0.72rem;
|
|
419
|
+
font-weight: 700;
|
|
420
|
+
text-transform: uppercase;
|
|
421
|
+
letter-spacing: 0.8px;
|
|
422
|
+
cursor: pointer;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.sidebar-nav > ul > li > strong:hover,
|
|
426
|
+
.sidebar-nav > ul > li > p > strong:hover {
|
|
427
|
+
color: var(--orange) !important;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* child links a touch smaller and tighter under a section */
|
|
431
|
+
.sidebar-nav > ul > li > ul > li > a {
|
|
432
|
+
font-size: 0.85rem;
|
|
433
|
+
padding: 4px 10px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/* ── Collapsible sections (custom, driven by .has-children/.open) ─ */
|
|
437
|
+
|
|
438
|
+
.sidebar-nav > ul > li.has-children > ul { display: none; }
|
|
439
|
+
.sidebar-nav > ul > li.has-children.open > ul { display: block; }
|
|
440
|
+
|
|
441
|
+
.sidebar-nav > ul > li.has-children > strong::before,
|
|
442
|
+
.sidebar-nav > ul > li.has-children > p > strong::before {
|
|
443
|
+
content: '\203A'; /* › chevron */
|
|
444
|
+
display: inline-block;
|
|
445
|
+
width: 12px;
|
|
446
|
+
margin-left: -16px;
|
|
447
|
+
margin-right: 4px;
|
|
448
|
+
text-align: center;
|
|
449
|
+
font-size: 0.9rem;
|
|
450
|
+
color: var(--muted);
|
|
451
|
+
transition: transform 0.15s ease;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.sidebar-nav > ul > li.has-children.open > strong::before,
|
|
455
|
+
.sidebar-nav > ul > li.has-children.open > p > strong::before {
|
|
456
|
+
transform: rotate(90deg);
|
|
457
|
+
color: var(--orange);
|
|
458
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extensionType": "docs",
|
|
3
|
+
"targetType": "plugin",
|
|
4
|
+
"id": "pinocchio",
|
|
5
|
+
"name": "@kwirthmagnify/kwirth-docs-pinocchio",
|
|
6
|
+
"displayName": "kwirth Pinocchio — Guide",
|
|
7
|
+
"version": "0.2.31",
|
|
8
|
+
"description": "User and administrator guide for the kwirth Pinocchio plugin"
|
|
9
|
+
}
|
package/serve.cmd
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
node serve.mjs 4000
|
package/serve.mjs
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import http from 'http'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import { fileURLToPath } from 'url'
|
|
6
|
+
|
|
7
|
+
const __dir = path.dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
|
|
9
|
+
// Usage: node serve.mjs [port] [path] or node serve.mjs [path] [port]
|
|
10
|
+
// Accepts port (number) and path (string) in any order after the script name.
|
|
11
|
+
let PORT = 4000
|
|
12
|
+
let ROOT = __dir
|
|
13
|
+
|
|
14
|
+
for (const arg of process.argv.slice(2)) {
|
|
15
|
+
const n = parseInt(arg, 10)
|
|
16
|
+
if (!isNaN(n) && String(n) === arg.trim()) {
|
|
17
|
+
PORT = n
|
|
18
|
+
} else {
|
|
19
|
+
ROOT = path.isAbsolute(arg) ? arg : path.resolve(__dir, arg)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const MIME = {
|
|
24
|
+
'.html': 'text/html; charset=utf-8',
|
|
25
|
+
'.css': 'text/css',
|
|
26
|
+
'.js': 'text/javascript',
|
|
27
|
+
'.mjs': 'text/javascript',
|
|
28
|
+
'.json': 'application/json',
|
|
29
|
+
'.png': 'image/png',
|
|
30
|
+
'.jpg': 'image/jpeg',
|
|
31
|
+
'.jpeg': 'image/jpeg',
|
|
32
|
+
'.gif': 'image/gif',
|
|
33
|
+
'.svg': 'image/svg+xml',
|
|
34
|
+
'.ico': 'image/x-icon',
|
|
35
|
+
'.woff': 'font/woff',
|
|
36
|
+
'.woff2':'font/woff2',
|
|
37
|
+
'.md': 'text/plain; charset=utf-8',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const server = http.createServer((req, res) => {
|
|
41
|
+
let urlPath = decodeURIComponent(req.url.split('?')[0])
|
|
42
|
+
if (urlPath === '/') urlPath = '/index.html'
|
|
43
|
+
|
|
44
|
+
let filePath = path.join(ROOT, urlPath)
|
|
45
|
+
|
|
46
|
+
// If no extension and no trailing slash, try .html then directory index
|
|
47
|
+
if (!path.extname(filePath)) {
|
|
48
|
+
if (fs.existsSync(filePath + '.html')) {
|
|
49
|
+
filePath = filePath + '.html'
|
|
50
|
+
} else if (fs.existsSync(path.join(filePath, 'index.html'))) {
|
|
51
|
+
filePath = path.join(filePath, 'index.html')
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
|
|
56
|
+
const idx = path.join(filePath.replace(/\/$/, ''), 'index.html')
|
|
57
|
+
if (fs.existsSync(idx)) {
|
|
58
|
+
filePath = idx
|
|
59
|
+
} else {
|
|
60
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
61
|
+
res.end(`404 Not Found: ${urlPath}`)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const ext = path.extname(filePath).toLowerCase()
|
|
67
|
+
const mime = MIME[ext] || 'application/octet-stream'
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const data = fs.readFileSync(filePath)
|
|
71
|
+
res.writeHead(200, { 'Content-Type': mime, 'Cache-Control': 'no-cache' })
|
|
72
|
+
res.end(data)
|
|
73
|
+
console.log(` 200 ${urlPath}`)
|
|
74
|
+
} catch {
|
|
75
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' })
|
|
76
|
+
res.end('500 Internal Server Error')
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
server.listen(PORT, () => {
|
|
81
|
+
console.log(`\n pinocchio docs → http://localhost:${PORT}`)
|
|
82
|
+
console.log(` serving → ${ROOT}\n`)
|
|
83
|
+
console.log(' Press Ctrl+C to stop.\n')
|
|
84
|
+
})
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Cómo funciona
|
|
2
|
+
|
|
3
|
+
Pinocchio es un **canal** de kwirth. Cuando lo abres, el backend arranca una instancia del canal que se
|
|
4
|
+
**suscribe a tres providers** y se queda escuchando. Cada evento que llega se compara con tus **triggers**;
|
|
5
|
+
si encaja, se construye una llamada al LLM y el resultado vuelve a tu pantalla.
|
|
6
|
+
|
|
7
|
+
## El bucle completo
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
+---------------------+
|
|
11
|
+
| events (k8s) |--- ADDED / MODIFIED / DELETED de Pod, Deployment, ...
|
|
12
|
+
+---------------------+
|
|
13
|
+
| business (HTTP) |--- POST /provider/business {space, type, data}
|
|
14
|
+
+---------------------+
|
|
15
|
+
| metrics |--- NO dispara nada: alimenta el contexto de las tools
|
|
16
|
+
+---------------------+
|
|
17
|
+
|
|
|
18
|
+
v
|
|
19
|
+
+---------------------+ ¿hay un trigger cuyo tipo, kind y evento
|
|
20
|
+
| match de triggers | encajen? ¿tiene una version enabled?
|
|
21
|
+
+---------------------+
|
|
22
|
+
| si
|
|
23
|
+
v
|
|
24
|
+
+---------------------+ system = version.system
|
|
25
|
+
| render del prompt | prompt = nunjucks(version.prompt, objeto)
|
|
26
|
+
+---------------------+ o JSON.stringify(objeto)
|
|
27
|
+
|
|
|
28
|
+
v
|
|
29
|
+
+---------------------+ el modelo puede llamar a tools (list_namespaces,
|
|
30
|
+
| LLM + tools | get_pod_logs, get_rollout_history, ...) hasta
|
|
31
|
+
+---------------------+ agotar `steps`
|
|
32
|
+
|
|
|
33
|
+
v
|
|
34
|
+
+---------------------+ findings[] + resource + PSS + score_summary +
|
|
35
|
+
| salida estructurada| global_risk + next_steps + report
|
|
36
|
+
+---------------------+
|
|
37
|
+
|
|
|
38
|
+
v
|
|
39
|
+
+---------------------+
|
|
40
|
+
| tu pestaña | y se guarda en el back (ultimos 50 analisis)
|
|
41
|
+
+---------------------+
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Los tres providers
|
|
45
|
+
|
|
46
|
+
Al arrancar el canal, Pinocchio se suscribe a:
|
|
47
|
+
|
|
48
|
+
| Provider | Para qué lo usa |
|
|
49
|
+
|------------|----------------------------------------------------------------------------------|
|
|
50
|
+
| `events` | **Dispara** los triggers de tipo `artifact`. Escucha los kinds de la lista de abajo. |
|
|
51
|
+
| `business` | **Dispara** los triggers de tipo `business`, y es también el canal por el que el Playground inyecta eventos. |
|
|
52
|
+
| `metrics` | **No dispara nada.** Se guarda un buffer de las últimas 100 lecturas que se pasa como contexto a las tools de uso (`get_cluster_usage`, `get_prev_node_usage`, …). |
|
|
53
|
+
|
|
54
|
+
Los kinds que escucha el provider `events` están fijados en el plugin:
|
|
55
|
+
|
|
56
|
+
`Pod`, `Deployment`, `DaemonSet`, `StatefulSet`, `ReplicaSet`, `Job`, `CronJob`,
|
|
57
|
+
`ReplicationController`, `Service`, `Ingress`, `HTTPRoute`
|
|
58
|
+
|
|
59
|
+
De la suscripción a `business`, el canal sólo pide tres pares espacio/tipo:
|
|
60
|
+
`customers.status`, `branches.status` y `launch.immediate`. Esto importa y se explica en
|
|
61
|
+
[El Playground](06-playground.md).
|
|
62
|
+
|
|
63
|
+
## Qué NO se analiza al arrancar
|
|
64
|
+
|
|
65
|
+
Cuando abres el canal, Kubernetes reemite como `ADDED` **todos** los objetos que ya existen. Si Pinocchio
|
|
66
|
+
los analizase, una sola apertura del canal costaría cientos de llamadas al modelo.
|
|
67
|
+
|
|
68
|
+
Para evitarlo, ante un `ADDED` el canal compara el `metadata.creationTimestamp` del objeto con la hora de
|
|
69
|
+
arranque del canal: si el objeto es **anterior**, lo descarta y deja una traza de aviso en el log del
|
|
70
|
+
backend. Sólo se analiza lo que nace **después** de que el canal esté escuchando.
|
|
71
|
+
|
|
72
|
+
> Consecuencia práctica: si quieres analizar algo que ya existe, no te vale con abrir el canal. Recrea el
|
|
73
|
+
> recurso, provoca un `MODIFIED`, o usa el [Playground](06-playground.md) pegando su YAML/JSON a mano.
|
|
74
|
+
|
|
75
|
+
## Dónde vive el estado
|
|
76
|
+
|
|
77
|
+
| Qué | Dónde |
|
|
78
|
+
|------------------------------|----------------------------------------------------------------------|
|
|
79
|
+
| Triggers y estado del Playground | Almacén del canal, clave `pinocchio-config` |
|
|
80
|
+
| Providers de IA (con las API keys) | Almacén **común** de kwirth, `kwirth-ai-providers` (Secret) |
|
|
81
|
+
| LLMs | Almacén **común** de kwirth, `kwirth-ai-llms` (ConfigMap) |
|
|
82
|
+
| Análisis producidos | Sólo en **memoria** del canal, los últimos 50 |
|
|
83
|
+
|
|
84
|
+
Los dos almacenes comunes los comparte Pinocchio con el resto de plugins de kwirth que usan IA: no son
|
|
85
|
+
suyos. Ver [Providers y modelos de IA](../admin/02-ai-config.md).
|
|
86
|
+
|
|
87
|
+
Los análisis **no se persisten**. Al reconectar, el back te reenvía de golpe los que tenga en memoria; si el
|
|
88
|
+
backend se reinicia, se pierden.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Introducción y modelo mental
|
|
2
|
+
|
|
3
|
+
## Qué problema resuelve
|
|
4
|
+
|
|
5
|
+
Un clúster de Kubernetes acepta prácticamente cualquier cosa que le mandes. Un `Deployment` con
|
|
6
|
+
`privileged: true`, sin `resources`, con `latest` como tag y montando el socket de Docker se despliega sin
|
|
7
|
+
una sola queja. Los controles que lo evitarían —Pod Security Standards, políticas de admisión, un repaso
|
|
8
|
+
manual del YAML— o no están, o están en modo aviso, o nadie los lee.
|
|
9
|
+
|
|
10
|
+
Pinocchio ataca ese hueco por otro lado: **pone un modelo de lenguaje a leer cada recurso que nace o cambia**,
|
|
11
|
+
con las instrucciones que tú le has escrito, y con capacidad de **consultar el clúster** para contrastar lo
|
|
12
|
+
que ve.
|
|
13
|
+
|
|
14
|
+
## El modelo mental en una frase
|
|
15
|
+
|
|
16
|
+
> Un **trigger** es una regla del tipo *"cuando pase ESTO, pregúntale ESTO al modelo, dándole ESTAS
|
|
17
|
+
> herramientas"*.
|
|
18
|
+
|
|
19
|
+
Todo lo demás en Pinocchio existe para servir a esa frase:
|
|
20
|
+
|
|
21
|
+
- **Cuando pase ESTO** → el tipo de trigger (`artifact` o `business`), el `kind` y el evento de Kubernetes.
|
|
22
|
+
- **Pregúntale ESTO** → el *system* y el *prompt*, que es una plantilla jinja sobre el objeto que llegó.
|
|
23
|
+
- **Con ESTAS herramientas** → las *tools*, funciones reales que consultan el clúster, y los *steps*, cuántas
|
|
24
|
+
veces puede encadenarlas antes de tener que responder.
|
|
25
|
+
|
|
26
|
+
## En qué se parece y en qué no a un chat de IA
|
|
27
|
+
|
|
28
|
+
| | Chat de IA | Pinocchio |
|
|
29
|
+
|-----------------------|----------------------------------------|--------------------------------------------------------|
|
|
30
|
+
| Quién inicia | Tú, escribiendo | Un evento del clúster o de negocio |
|
|
31
|
+
| Qué ve el modelo | Lo que pegues en la ventana | El objeto real + lo que consulte con sus tools |
|
|
32
|
+
| Formato de salida | Texto libre | JSON estructurado: findings, PSS, riesgo, informe |
|
|
33
|
+
| Cuándo ocurre | Cuando te acuerdas | Siempre, mientras el canal esté abierto |
|
|
34
|
+
|
|
35
|
+
La tercera fila es la que más cambia el resultado. Pinocchio no le pide al modelo "dime qué te parece":
|
|
36
|
+
le **impone un esquema de respuesta** con findings clasificados por severidad, categoría y confianza, un
|
|
37
|
+
nivel PSS actual y objetivo, un recuento por severidad y un informe en markdown. El modelo tiene que
|
|
38
|
+
rellenar esos campos, y por eso la salida se puede pintar, ordenar y comparar en vez de leerse a mano.
|
|
39
|
+
|
|
40
|
+
## Los dos tipos de trigger
|
|
41
|
+
|
|
42
|
+
**`artifact`** — el evento es un **objeto de Kubernetes**. Es el caso principal y el único que produce la
|
|
43
|
+
salida estructurada completa (findings, PSS, informe). Ejemplo: *"cada vez que se cree un Deployment,
|
|
44
|
+
audítalo contra los Pod Security Standards"*.
|
|
45
|
+
|
|
46
|
+
**`business`** — el evento es un **JSON arbitrario** que un sistema externo ha enviado por HTTP al provider
|
|
47
|
+
`business`. La salida es sólo un texto de respuesta. Ejemplo: *"cuando llegue un aviso de pico de pedidos,
|
|
48
|
+
mira las métricas del clúster y dime si aguantamos"*.
|
|
49
|
+
|
|
50
|
+
Los dos se configuran en el mismo diálogo y se prueban en el mismo Playground, pero se comportan de forma
|
|
51
|
+
bastante distinta. La diferencia está detallada en [Triggers, versiones y prompts](03-concepts.md).
|
|
52
|
+
|
|
53
|
+
## Qué NO es Pinocchio
|
|
54
|
+
|
|
55
|
+
- **No es un escáner de vulnerabilidades.** No mira CVEs de las imágenes. Para eso está el provider `trivy`.
|
|
56
|
+
- **No bloquea nada.** Los triggers tienen un campo *Action* con valores `inform`/`cancel`/`repair`, pero
|
|
57
|
+
hoy sólo se comporta como `inform`: analiza y te lo cuenta. Ver [Límites conocidos](../admin/06-limits.md).
|
|
58
|
+
- **No recuerda.** Cada análisis es una llamada independiente. El modelo no ve los análisis anteriores.
|
|
59
|
+
- **No es gratis.** Cada disparo es una llamada de pago a tu proveedor de IA, y con tools activadas puede
|
|
60
|
+
ser una cadena de varias. Ver [Tools y pasos del agente](../admin/03-tools.md).
|