@xuda.io/ai_module 1.1.5616 → 1.1.5618

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,601 @@
1
+ from __future__ import annotations
2
+
3
+ import html
4
+ import re
5
+ import shutil
6
+ from pathlib import Path
7
+
8
+
9
+ ROOT = Path("/var/xuda/tmp/codex-test/boaz-wiki")
10
+ SOURCE = ROOT / "boaz_avrahami_updated_with_academic_recognition.md"
11
+ IMAGE = ROOT / "BOAZ_AVRAHAMI_67.png"
12
+ PDF = Path("/var/xuda/tmp/codex-test/boaz-wiki-attachments/Boaz_Globes_CRM_2004.pdf")
13
+ OUT = Path("/tmp/boaz-wiki-site")
14
+
15
+
16
+ def slugify(index: int) -> str:
17
+ return f"section-{index:02d}"
18
+
19
+
20
+ def inline_markup(text: str) -> str:
21
+ text = html.unescape(text)
22
+ links: list[tuple[str, str]] = []
23
+
24
+ def replace_link(match: re.Match[str]) -> str:
25
+ token = f"__LINK_{len(links)}__"
26
+ label = html.escape(match.group(1), quote=False)
27
+ url = html.escape(match.group(2), quote=True)
28
+ links.append(
29
+ (
30
+ token,
31
+ f'<a class="inline-link" href="{url}" target="_blank" rel="noopener noreferrer">{label}</a>',
32
+ )
33
+ )
34
+ return token
35
+
36
+ text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", replace_link, text)
37
+ text = html.escape(text, quote=False)
38
+ text = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", text)
39
+ text = re.sub(r"`(.+?)`", r"<code>\1</code>", text)
40
+ for token, anchor in links:
41
+ text = text.replace(token, anchor)
42
+ return text
43
+
44
+
45
+ def paragraph(lines: list[str]) -> str:
46
+ return f"<p>{inline_markup(' '.join(line.strip() for line in lines))}</p>"
47
+
48
+
49
+ def markdown_to_html(markdown: str) -> tuple[str, list[dict[str, str]]]:
50
+ lines = markdown.splitlines()
51
+ first_title = next((i for i, line in enumerate(lines) if line.strip().startswith("# ")), 0)
52
+ lines = lines[first_title:]
53
+
54
+ html_parts: list[str] = []
55
+ headings: list[dict[str, str]] = []
56
+ paragraph_lines: list[str] = []
57
+ list_items: list[str] = []
58
+ section_index = 0
59
+
60
+ def flush_paragraph() -> None:
61
+ nonlocal paragraph_lines
62
+ if paragraph_lines:
63
+ html_parts.append(paragraph(paragraph_lines))
64
+ paragraph_lines = []
65
+
66
+ def flush_list() -> None:
67
+ nonlocal list_items
68
+ if list_items:
69
+ html_parts.append("<ul>" + "".join(f"<li>{item}</li>" for item in list_items) + "</ul>")
70
+ list_items = []
71
+
72
+ for raw in lines:
73
+ line = raw.strip()
74
+
75
+ if not line:
76
+ flush_paragraph()
77
+ flush_list()
78
+ continue
79
+
80
+ heading = re.match(r"^(#{1,4})\s+(.+)$", line)
81
+ bullet = re.match(r"^\*\s+(.+)$", line)
82
+
83
+ if heading:
84
+ flush_paragraph()
85
+ flush_list()
86
+ level = len(heading.group(1))
87
+ title = html.unescape(heading.group(2)).strip()
88
+ if level == 1:
89
+ html_parts.append(f'<h1 id="top">{inline_markup(title)}</h1>')
90
+ else:
91
+ section_index += 1
92
+ sid = slugify(section_index)
93
+ if level in (2, 3):
94
+ headings.append({"level": str(level), "title": title, "id": sid})
95
+ html_parts.append(f'<h{level} id="{sid}">{inline_markup(title)}</h{level}>')
96
+ continue
97
+
98
+ if bullet:
99
+ flush_paragraph()
100
+ list_items.append(inline_markup(bullet.group(1)))
101
+ continue
102
+
103
+ flush_list()
104
+ paragraph_lines.append(line)
105
+
106
+ flush_paragraph()
107
+ flush_list()
108
+ return "\n".join(html_parts), headings
109
+
110
+
111
+ def build() -> None:
112
+ OUT.mkdir(parents=True, exist_ok=True)
113
+ assets = OUT / "assets"
114
+ assets.mkdir(exist_ok=True)
115
+ shutil.copyfile(IMAGE, assets / "boaz-avrahami.png")
116
+ shutil.copyfile(PDF, assets / "boaz-globes-crm-2004.pdf")
117
+
118
+ source = SOURCE.read_text(encoding="utf-8")
119
+ note = ""
120
+ body_lines = []
121
+ for line in source.splitlines():
122
+ if line.startswith(">"):
123
+ note = line[1:].strip()
124
+ else:
125
+ body_lines.append(line)
126
+
127
+ article_html, headings = markdown_to_html("\n".join(body_lines))
128
+ toc_items = "\n".join(
129
+ f'<a class="toc-link level-{item["level"]}" href="#{item["id"]}">{html.escape(item["title"])}</a>'
130
+ for item in headings
131
+ )
132
+
133
+ (assets / "styles.css").write_text(CSS, encoding="utf-8")
134
+ (assets / "site.js").write_text(JS, encoding="utf-8")
135
+
136
+ page = HTML_TEMPLATE.format(
137
+ article=article_html,
138
+ toc=toc_items,
139
+ note=inline_markup(note),
140
+ )
141
+ (OUT / "index.html").write_text(page, encoding="utf-8")
142
+
143
+
144
+ HTML_TEMPLATE = """<!doctype html>
145
+ <html lang="he" dir="ltr">
146
+ <head>
147
+ <meta charset="utf-8">
148
+ <meta name="viewport" content="width=device-width, initial-scale=1">
149
+ <title>בועז אברהמי | ערך ויקי</title>
150
+ <meta name="description" content="ערך ביוגרפי בסגנון ויקי על בועז אברהמי: יזמות טכנולוגית, מערכות מידע, מוזיקה, XUDA, NewDeal ופעילות מקצועית.">
151
+ <meta property="og:title" content="בועז אברהמי | ערך ויקי">
152
+ <meta property="og:description" content="ביוגרפיה אנציקלופדית של בועז אברהמי ופעילותו בתחומי התוכנה, היזמות, המוזיקה והבינה המלאכותית.">
153
+ <meta property="og:image" content="/assets/boaz-avrahami.png">
154
+ <link rel="stylesheet" href="/assets/styles.css">
155
+ <script src="/assets/site.js" defer></script>
156
+ </head>
157
+ <body>
158
+ <div class="progress" aria-hidden="true"><span></span></div>
159
+ <header class="site-header">
160
+ <a class="brand" href="#top" aria-label="בועז אברהמי">
161
+ <span class="brand-mark">BA</span>
162
+ <span>בועז אברהמי</span>
163
+ </a>
164
+ <nav aria-label="ניווט ראשי">
165
+ <a href="#section-04">טכנולוגיה</a>
166
+ <a href="#section-13">XUDA</a>
167
+ <a href="#section-18">השפעה</a>
168
+ </nav>
169
+ </header>
170
+
171
+ <main class="page-shell">
172
+ <aside class="toc-panel" aria-label="תוכן עניינים">
173
+ <div class="toc-tools">
174
+ <label for="toc-search">חיפוש בערך</label>
175
+ <input id="toc-search" type="search" placeholder="חפש נושא, חברה או שנה">
176
+ </div>
177
+ <div class="toc-list">
178
+ {toc}
179
+ </div>
180
+ </aside>
181
+
182
+ <article class="article">
183
+ <div class="source-note">{note}</div>
184
+ {article}
185
+ </article>
186
+
187
+ <aside class="infobox" aria-label="פרטים ביוגרפיים">
188
+ <img src="/assets/boaz-avrahami.png" alt="בועז אברהמי">
189
+ <h2>בועז אברהמי</h2>
190
+ <dl>
191
+ <div><dt>נולד</dt><dd>7 במרץ 1967, ירושלים</dd></div>
192
+ <div><dt>תחומי פעילות</dt><dd>תוכנה, מערכות מידע, יזמות, מוזיקה, AI</dd></div>
193
+ <div><dt>חברות מרכזיות</dt><dd>שיגרה, CouchYou, DocxSite, Ioshka Media, XUDA</dd></div>
194
+ <div><dt>מומחיות</dt><dd>CRM, Workflow, אוטומציית תהליכים, Enterprise AI</dd></div>
195
+ <div><dt>הכרה מקצועית</dt><dd>Seattle Pacific University, 2011</dd></div>
196
+ </dl>
197
+ </aside>
198
+ </main>
199
+ </body>
200
+ </html>
201
+ """
202
+
203
+
204
+ CSS = """
205
+ :root {
206
+ --ink: #202225;
207
+ --muted: #5d6672;
208
+ --line: #d8dee7;
209
+ --paper: #fbfbf8;
210
+ --panel: #ffffff;
211
+ --blue: #245f8f;
212
+ --red: #9b3c32;
213
+ --green: #406d54;
214
+ --gold: #a2772c;
215
+ --shadow: 0 18px 45px rgba(32, 34, 37, .08);
216
+ }
217
+
218
+ * { box-sizing: border-box; }
219
+ html { scroll-behavior: smooth; }
220
+ body {
221
+ margin: 0;
222
+ background: var(--paper);
223
+ color: var(--ink);
224
+ font-family: Arial, "Noto Sans Hebrew", "Segoe UI", sans-serif;
225
+ line-height: 1.72;
226
+ overflow-x: hidden;
227
+ direction: ltr;
228
+ }
229
+
230
+ .progress {
231
+ position: fixed;
232
+ inset: 0 0 auto;
233
+ height: 4px;
234
+ z-index: 20;
235
+ background: transparent;
236
+ }
237
+ .progress span {
238
+ display: block;
239
+ width: 0;
240
+ height: 100%;
241
+ background: linear-gradient(90deg, var(--blue), var(--green), var(--gold));
242
+ }
243
+
244
+ .site-header {
245
+ direction: rtl;
246
+ position: sticky;
247
+ top: 0;
248
+ z-index: 10;
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: space-between;
252
+ gap: 24px;
253
+ padding: 12px 32px;
254
+ border-bottom: 1px solid var(--line);
255
+ background: rgba(251, 251, 248, .95);
256
+ backdrop-filter: blur(12px);
257
+ }
258
+ .brand {
259
+ display: inline-flex;
260
+ align-items: center;
261
+ gap: 10px;
262
+ color: var(--ink);
263
+ text-decoration: none;
264
+ font-weight: 700;
265
+ }
266
+ .brand-mark {
267
+ direction: ltr;
268
+ display: grid;
269
+ place-items: center;
270
+ width: 38px;
271
+ height: 38px;
272
+ border: 1px solid var(--ink);
273
+ background: var(--ink);
274
+ color: #fff;
275
+ font-size: 13px;
276
+ }
277
+ .site-header nav {
278
+ display: flex;
279
+ gap: 18px;
280
+ flex-wrap: wrap;
281
+ }
282
+ .site-header nav a {
283
+ color: var(--muted);
284
+ text-decoration: none;
285
+ font-size: 14px;
286
+ }
287
+ .site-header nav a:hover { color: var(--blue); }
288
+
289
+ .page-shell {
290
+ direction: rtl;
291
+ display: grid;
292
+ grid-template-columns: minmax(210px, 280px) minmax(0, 860px) minmax(260px, 330px);
293
+ gap: 28px;
294
+ align-items: start;
295
+ max-width: 1540px;
296
+ margin: 0 auto;
297
+ padding: 28px 28px 64px;
298
+ }
299
+
300
+ .toc-panel,
301
+ .infobox {
302
+ position: sticky;
303
+ top: 86px;
304
+ border: 1px solid var(--line);
305
+ background: var(--panel);
306
+ box-shadow: var(--shadow);
307
+ }
308
+
309
+ .toc-panel {
310
+ max-height: calc(100vh - 108px);
311
+ overflow: auto;
312
+ padding: 16px;
313
+ }
314
+ .toc-tools label {
315
+ display: block;
316
+ margin-bottom: 8px;
317
+ color: var(--muted);
318
+ font-size: 13px;
319
+ font-weight: 700;
320
+ }
321
+ .toc-tools input {
322
+ width: 100%;
323
+ border: 1px solid var(--line);
324
+ padding: 10px 11px;
325
+ background: #f7f8fa;
326
+ color: var(--ink);
327
+ font: inherit;
328
+ }
329
+ .toc-list {
330
+ display: grid;
331
+ gap: 2px;
332
+ margin-top: 14px;
333
+ }
334
+ .toc-link {
335
+ border-right: 3px solid transparent;
336
+ padding: 7px 9px;
337
+ color: var(--ink);
338
+ text-decoration: none;
339
+ font-size: 14px;
340
+ }
341
+ .toc-link.level-3 {
342
+ padding-right: 22px;
343
+ color: var(--muted);
344
+ font-size: 13px;
345
+ }
346
+ .toc-link:hover,
347
+ .toc-link.active {
348
+ border-color: var(--blue);
349
+ background: #eef4f8;
350
+ color: var(--blue);
351
+ }
352
+
353
+ .article {
354
+ min-width: 0;
355
+ border: 1px solid var(--line);
356
+ background: var(--panel);
357
+ padding: 38px 48px 52px;
358
+ box-shadow: var(--shadow);
359
+ }
360
+ .source-note {
361
+ border-right: 4px solid var(--gold);
362
+ margin-bottom: 24px;
363
+ padding: 10px 14px;
364
+ background: #faf6ec;
365
+ color: #65502a;
366
+ font-size: 14px;
367
+ }
368
+ .article h1 {
369
+ margin: 0 0 16px;
370
+ padding-bottom: 16px;
371
+ border-bottom: 1px solid var(--line);
372
+ font-size: 42px;
373
+ line-height: 1.15;
374
+ }
375
+ .article h2 {
376
+ margin: 42px 0 14px;
377
+ padding-bottom: 8px;
378
+ border-bottom: 1px solid var(--line);
379
+ color: var(--blue);
380
+ font-size: 26px;
381
+ line-height: 1.3;
382
+ }
383
+ .article h3 {
384
+ margin: 30px 0 10px;
385
+ color: var(--red);
386
+ font-size: 20px;
387
+ line-height: 1.35;
388
+ }
389
+ .article p,
390
+ .article li {
391
+ font-size: 17px;
392
+ }
393
+ .article a {
394
+ color: var(--blue);
395
+ text-decoration: underline;
396
+ text-underline-offset: 2px;
397
+ text-decoration-thickness: 1px;
398
+ }
399
+ .article a:hover {
400
+ color: var(--red);
401
+ }
402
+ .article p {
403
+ margin: 0 0 15px;
404
+ }
405
+ .article ul {
406
+ margin: 0 0 18px;
407
+ padding: 0 24px 0 0;
408
+ }
409
+ .article strong { color: #14171a; }
410
+
411
+ .infobox {
412
+ overflow: hidden;
413
+ }
414
+ .infobox img {
415
+ display: block;
416
+ width: 100%;
417
+ max-width: 100%;
418
+ aspect-ratio: 4 / 5;
419
+ object-fit: cover;
420
+ object-position: center top;
421
+ border-bottom: 1px solid var(--line);
422
+ }
423
+ .infobox h2 {
424
+ margin: 0;
425
+ padding: 16px 18px 6px;
426
+ font-size: 22px;
427
+ }
428
+ .infobox dl {
429
+ margin: 0;
430
+ padding: 8px 18px 18px;
431
+ }
432
+ .infobox div {
433
+ display: grid;
434
+ grid-template-columns: 82px minmax(0, 1fr);
435
+ gap: 12px;
436
+ padding: 10px 0;
437
+ border-top: 1px solid #eef1f4;
438
+ }
439
+ .infobox dt {
440
+ color: var(--muted);
441
+ font-weight: 700;
442
+ font-size: 13px;
443
+ }
444
+ .infobox dd {
445
+ margin: 0;
446
+ font-size: 14px;
447
+ min-width: 0;
448
+ overflow-wrap: anywhere;
449
+ }
450
+
451
+ mark {
452
+ background: #ffe28a;
453
+ color: var(--ink);
454
+ padding: 0 2px;
455
+ }
456
+
457
+ @media (max-width: 1180px) {
458
+ .page-shell {
459
+ grid-template-columns: minmax(0, 1fr) minmax(250px, 310px);
460
+ }
461
+ .toc-panel {
462
+ position: relative;
463
+ top: 0;
464
+ grid-column: 1 / -1;
465
+ max-height: none;
466
+ }
467
+ .toc-list {
468
+ grid-template-columns: repeat(2, minmax(0, 1fr));
469
+ }
470
+ }
471
+
472
+ @media (max-width: 820px) {
473
+ .site-header {
474
+ display: grid;
475
+ grid-template-columns: 1fr;
476
+ align-items: flex-start;
477
+ padding: 10px 72px 10px 16px;
478
+ overflow: hidden;
479
+ }
480
+ .brand {
481
+ justify-self: start;
482
+ }
483
+ .brand-mark {
484
+ display: none;
485
+ }
486
+ .site-header nav {
487
+ display: grid;
488
+ grid-template-columns: repeat(3, minmax(0, 1fr));
489
+ width: 100%;
490
+ gap: 8px;
491
+ }
492
+ .site-header nav a {
493
+ min-width: 0;
494
+ text-align: center;
495
+ }
496
+ .page-shell {
497
+ display: flex;
498
+ flex-direction: column;
499
+ width: 100%;
500
+ max-width: 100%;
501
+ padding: 16px 72px 42px 14px;
502
+ overflow: hidden;
503
+ direction: ltr;
504
+ }
505
+ .article,
506
+ .toc-panel,
507
+ .infobox {
508
+ width: auto;
509
+ min-width: 0;
510
+ max-width: 100%;
511
+ align-self: stretch;
512
+ position: static;
513
+ direction: rtl;
514
+ }
515
+ .article {
516
+ order: 3;
517
+ padding: 26px 20px 34px;
518
+ }
519
+ .infobox {
520
+ order: 1;
521
+ }
522
+ .infobox div {
523
+ grid-template-columns: 1fr;
524
+ gap: 3px;
525
+ }
526
+ .toc-panel {
527
+ order: 2;
528
+ }
529
+ .toc-list {
530
+ grid-template-columns: 1fr;
531
+ }
532
+ .article h1 {
533
+ font-size: 34px;
534
+ }
535
+ .article h2 {
536
+ font-size: 23px;
537
+ }
538
+ }
539
+ """
540
+
541
+
542
+ JS = """
543
+ const progress = document.querySelector('.progress span');
544
+ const links = [...document.querySelectorAll('.toc-link')];
545
+ const headings = links
546
+ .map(link => document.querySelector(link.getAttribute('href')))
547
+ .filter(Boolean);
548
+
549
+ function updateProgress() {
550
+ const height = document.documentElement.scrollHeight - window.innerHeight;
551
+ const percent = height > 0 ? (window.scrollY / height) * 100 : 0;
552
+ progress.style.width = `${percent}%`;
553
+
554
+ let current = headings[0];
555
+ for (const heading of headings) {
556
+ if (heading.getBoundingClientRect().top < 140) current = heading;
557
+ }
558
+ links.forEach(link => {
559
+ link.classList.toggle('active', current && link.getAttribute('href') === `#${current.id}`);
560
+ });
561
+ }
562
+
563
+ function clearMarks() {
564
+ document.querySelectorAll('mark').forEach(mark => {
565
+ mark.replaceWith(document.createTextNode(mark.textContent));
566
+ });
567
+ }
568
+
569
+ document.getElementById('toc-search').addEventListener('input', event => {
570
+ clearMarks();
571
+ const query = event.target.value.trim();
572
+ const article = document.querySelector('.article');
573
+ links.forEach(link => {
574
+ link.hidden = query && !link.textContent.includes(query);
575
+ });
576
+ if (!query) return;
577
+
578
+ const walker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
579
+ const matches = [];
580
+ while (walker.nextNode()) {
581
+ const node = walker.currentNode;
582
+ const index = node.nodeValue.indexOf(query);
583
+ if (index >= 0 && matches.length < 20) matches.push([node, index]);
584
+ }
585
+ for (const [node, index] of matches) {
586
+ const range = document.createRange();
587
+ range.setStart(node, index);
588
+ range.setEnd(node, index + query.length);
589
+ const mark = document.createElement('mark');
590
+ range.surroundContents(mark);
591
+ }
592
+ document.querySelector('mark')?.scrollIntoView({ block: 'center', behavior: 'smooth' });
593
+ });
594
+
595
+ window.addEventListener('scroll', updateProgress, { passive: true });
596
+ updateProgress();
597
+ """
598
+
599
+
600
+ if __name__ == "__main__":
601
+ build()
@@ -0,0 +1,47 @@
1
+ # Full Stack VPS — AI API Instructions
2
+
3
+ These instructions tell you, the AI, how to work with a user's Full Stack VPS and
4
+ the tools it exposes. They apply whenever a "Full Stack VPS" tool is attached to
5
+ your agent.
6
+
7
+ ## What a Full Stack VPS is
8
+ A developer box in the user's xuda account, provisioned like a standard VPS and
9
+ preloaded with the xuda CLI and codex. The developer builds apps and tools on it
10
+ with the xuda CLI (source-first .xu files, not React or Vue) and vibe-codes with
11
+ codex. Every tool the developer builds or installs on the box is exposed to you as
12
+ an API over the xuda CPI.
13
+
14
+ ## The tools you are given
15
+ When the user attaches a Full Stack VPS, you receive that box's methods as callable
16
+ tools over MCP. They come in two families:
17
+ 1. Ecosystem methods — the xuda capabilities the box has enabled: the box itself
18
+ (file system + services), data, drive, plugins, ai, version control, logs, ai
19
+ agents, contacts, email, and deploy and hosting.
20
+ 2. Custom tools — plugins the developer built or installed on the box. Each takes
21
+ typed parameters and returns a JSON response.
22
+
23
+ Tool names map to the method or plugin id. The tool descriptions and parameter
24
+ schemas are authoritative; follow them exactly.
25
+
26
+ ## How to use them
27
+ - Prefer the box's custom tools for app-specific intent. Fall back to ecosystem
28
+ methods for generic platform actions (read data, send email, manage contacts,
29
+ read logs, deploy, manage files on the box).
30
+ - Pass only parameters defined in a tool's schema. Do not invent parameters or ids.
31
+ - Resolve references (app, table, contact, file ids) by reading first with a list
32
+ or get method before writing.
33
+ - Work in the order discover, read, act. Surface the returned data or response to
34
+ the user faithfully.
35
+
36
+ ## Safety and scope
37
+ - Every tool is already scoped to this user's box and key. Never attempt methods
38
+ that were not provided to you.
39
+ - For destructive or outward-facing actions (delete, deploy, send, charge), confirm
40
+ intent with the user before calling, and report the exact effect afterward.
41
+ - Never reveal API keys, tokens, secrets, or credentials in your output.
42
+ - If a tool errors, report the error plainly. Do not retry blindly or fabricate a
43
+ success.
44
+
45
+ ## Output
46
+ - Be concise. Return the substance of the tool result, not a narration of every
47
+ call.
package/globals.mjs ADDED
@@ -0,0 +1,6 @@
1
+ export let klona;
2
+
3
+ export async function initGlobals() {
4
+ klona = await import('klona');
5
+ // init other heavy libs...
6
+ }