agents-city 0.1.0 → 0.2.0
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/.claude-plugin/marketplace.json +1 -1
- package/bin/hall.html +103 -54
- package/bin/navegador.mjs +59 -1
- package/bin/serve.py +21 -0
- package/bin/test-navegador.py +8 -1
- package/bin/test-seat.py +1 -1
- package/bin/test-serve.py +15 -9
- package/city/web/dist-hall/hall.js +514 -13
- package/city/web/src/bienvenida.ts +520 -0
- package/city/web/src/hall.ts +159 -13
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/channel/bus.js +1 -1
- package/plugin/channel/bus.ts +1 -1
- package/plugin/channel/runtime/codex.ts +1 -1
- package/plugin/channel/runtime-gateway.js +1 -1
- package/plugin/scripts/cities.py +34 -0
package/bin/hall.html
CHANGED
|
@@ -16,62 +16,66 @@
|
|
|
16
16
|
the local tool and the public site are visibly the same product: its paper,
|
|
17
17
|
its ink, its violet. Nothing below this block names a colour directly. */
|
|
18
18
|
:root{
|
|
19
|
-
/*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
--
|
|
26
|
-
--
|
|
19
|
+
/* Day is the default: the palette of agentscity.net, so the tool on your
|
|
20
|
+
machine and the public site are one product from the first second. Night
|
|
21
|
+
is one click away and remembered — it is the map's own light, not the
|
|
22
|
+
app's. */
|
|
23
|
+
--suelo:#f3f0e8; --suelo2:#fbfaf6; --suelo3:#ffffff;
|
|
24
|
+
--linea:#e2ddd0; --linea2:#cdc6b6;
|
|
25
|
+
--tinta:#1d2229; --tinta2:#515861; --tinta3:#7b8187;
|
|
26
|
+
--lampara:#7048e8; --carta:#dd5d3e; --verde:#5f8f2b;
|
|
27
|
+
--panel:#fbfaf6; --burbuja:#ffffff; --burbuja-mia:#f4efff;
|
|
28
|
+
--borde-acento:#c9b8ff; --sobre-lampara:#ffffff; --lampara-viva:#5331bd;
|
|
29
|
+
--chip-luz:52% 86%; --chip-tinta:#1d2229;
|
|
30
|
+
--tono-palabra:#6b46f0; --tono-evidencia:#2b7ab8; --tono-verificacion:#1c8d78;
|
|
27
31
|
--hud:"Chakra Petch",system-ui,sans-serif; --texto:"Newsreader",Georgia,serif;
|
|
28
32
|
--mono:"IBM Plex Mono",ui-monospace,monospace;
|
|
29
|
-
--sombra:
|
|
33
|
+
--sombra:0 18px 40px -30px #1d222945;
|
|
30
34
|
|
|
31
|
-
/*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
--
|
|
35
|
-
--
|
|
36
|
-
--
|
|
37
|
-
--
|
|
38
|
-
--
|
|
39
|
-
--
|
|
40
|
-
--
|
|
41
|
-
--
|
|
42
|
-
--
|
|
43
|
-
--
|
|
35
|
+
/* Night, declared once: the city's own light, an isometric map at 3am. The
|
|
36
|
+
two rules below only MAP these; a palette written twice drifts twice. */
|
|
37
|
+
--noche-suelo:#131a24; --noche-suelo2:#1b2431; --noche-suelo3:#222d3c;
|
|
38
|
+
--noche-linea:#26313f; --noche-linea2:#38495f;
|
|
39
|
+
--noche-tinta:#e8e2d4; --noche-tinta2:#9aa7b8; --noche-tinta3:#6b7889;
|
|
40
|
+
--noche-lampara:#f5c451; --noche-carta:#e2604f; --noche-verde:#4ec98a;
|
|
41
|
+
--noche-panel:#101720; --noche-burbuja:#171f2a; --noche-burbuja-mia:#27291f;
|
|
42
|
+
--noche-borde-acento:#65552d; --noche-sobre-lampara:#1a1408;
|
|
43
|
+
--noche-lampara-viva:#ffd469;
|
|
44
|
+
--noche-chip-luz:36% 30%; --noche-chip-tinta:#f3eee4;
|
|
45
|
+
--noche-tono-palabra:#aa93ff; --noche-tono-evidencia:#61ace9;
|
|
46
|
+
--noche-tono-verificacion:#50ccb1;
|
|
47
|
+
--noche-sombra:none;
|
|
44
48
|
}
|
|
45
49
|
/* One skin's worth of mapping, used by the explicit choice and by the system
|
|
46
50
|
preference alike. An explicit choice always outranks the machine's. */
|
|
47
|
-
:root[data-tema="
|
|
48
|
-
--suelo:var(--
|
|
49
|
-
--linea:var(--
|
|
50
|
-
--tinta:var(--
|
|
51
|
-
--lampara:var(--
|
|
52
|
-
--panel:var(--
|
|
53
|
-
--
|
|
54
|
-
--
|
|
55
|
-
--
|
|
56
|
-
--
|
|
57
|
-
--tono-
|
|
58
|
-
--
|
|
51
|
+
:root[data-tema="oscuro"]{
|
|
52
|
+
--suelo:var(--noche-suelo); --suelo2:var(--noche-suelo2); --suelo3:var(--noche-suelo3);
|
|
53
|
+
--linea:var(--noche-linea); --linea2:var(--noche-linea2);
|
|
54
|
+
--tinta:var(--noche-tinta); --tinta2:var(--noche-tinta2); --tinta3:var(--noche-tinta3);
|
|
55
|
+
--lampara:var(--noche-lampara); --carta:var(--noche-carta); --verde:var(--noche-verde);
|
|
56
|
+
--panel:var(--noche-panel); --burbuja:var(--noche-burbuja);
|
|
57
|
+
--burbuja-mia:var(--noche-burbuja-mia);
|
|
58
|
+
--borde-acento:var(--noche-borde-acento); --sobre-lampara:var(--noche-sobre-lampara);
|
|
59
|
+
--lampara-viva:var(--noche-lampara-viva);
|
|
60
|
+
--chip-luz:var(--noche-chip-luz); --chip-tinta:var(--noche-chip-tinta);
|
|
61
|
+
--tono-palabra:var(--noche-tono-palabra); --tono-evidencia:var(--noche-tono-evidencia);
|
|
62
|
+
--tono-verificacion:var(--noche-tono-verificacion);
|
|
63
|
+
--sombra:var(--noche-sombra);
|
|
59
64
|
}
|
|
60
|
-
@media(prefers-color-scheme:
|
|
61
|
-
:root:not([data-tema="
|
|
62
|
-
--suelo:var(--
|
|
63
|
-
--linea:var(--
|
|
64
|
-
--tinta:var(--
|
|
65
|
-
--lampara:var(--
|
|
66
|
-
--panel:var(--
|
|
67
|
-
--
|
|
68
|
-
--
|
|
69
|
-
--
|
|
70
|
-
--
|
|
71
|
-
--tono-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
--sombra:var(--dia-sombra);
|
|
65
|
+
@media(prefers-color-scheme:dark){
|
|
66
|
+
:root:not([data-tema="claro"]){
|
|
67
|
+
--suelo:var(--noche-suelo); --suelo2:var(--noche-suelo2); --suelo3:var(--noche-suelo3);
|
|
68
|
+
--linea:var(--noche-linea); --linea2:var(--noche-linea2);
|
|
69
|
+
--tinta:var(--noche-tinta); --tinta2:var(--noche-tinta2); --tinta3:var(--noche-tinta3);
|
|
70
|
+
--lampara:var(--noche-lampara); --carta:var(--noche-carta); --verde:var(--noche-verde);
|
|
71
|
+
--panel:var(--noche-panel); --burbuja:var(--noche-burbuja);
|
|
72
|
+
--burbuja-mia:var(--noche-burbuja-mia);
|
|
73
|
+
--borde-acento:var(--noche-borde-acento); --sobre-lampara:var(--noche-sobre-lampara);
|
|
74
|
+
--lampara-viva:var(--noche-lampara-viva);
|
|
75
|
+
--chip-luz:var(--noche-chip-luz); --chip-tinta:var(--noche-chip-tinta);
|
|
76
|
+
--tono-palabra:var(--noche-tono-palabra); --tono-evidencia:var(--noche-tono-evidencia);
|
|
77
|
+
--tono-verificacion:var(--noche-tono-verificacion);
|
|
78
|
+
--sombra:var(--noche-sombra);
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
*{box-sizing:border-box}
|
|
@@ -114,8 +118,9 @@ nav .pie a{font-family:var(--hud);font-size:12px;color:var(--tinta3);text-decora
|
|
|
114
118
|
border:1px solid var(--borde-acento);background:var(--burbuja-mia);
|
|
115
119
|
padding:7px 9px;border-radius:4px}
|
|
116
120
|
.avisoVersion[hidden]{display:none}
|
|
117
|
-
.temaBoton{
|
|
118
|
-
color:var(--tinta2);font:11px var(--mono);padding:
|
|
121
|
+
.temaBoton{margin-top:12px;border:1px solid var(--linea2);background:var(--suelo3);
|
|
122
|
+
color:var(--tinta2);font:11px var(--mono);padding:5px 11px;border-radius:999px;
|
|
123
|
+
cursor:pointer;box-shadow:var(--sombra)}
|
|
119
124
|
.temaBoton:hover{color:var(--lampara);border-color:var(--lampara)}
|
|
120
125
|
nav .pie a:hover{color:var(--lampara)}
|
|
121
126
|
#ciudades select{width:100%;background:var(--suelo);border:1px solid var(--linea2);
|
|
@@ -305,6 +310,51 @@ input[type=text]:focus,select:focus{outline:none;border-color:var(--lampara)}
|
|
|
305
310
|
color:var(--lampara);font:12px var(--mono);padding:7px 14px;border-radius:5px;
|
|
306
311
|
cursor:pointer}
|
|
307
312
|
.rpgAlta:hover{background:var(--suelo);border-color:var(--lampara)}
|
|
313
|
+
|
|
314
|
+
/* ── the guided first run ────────────────────────────────────────────────── */
|
|
315
|
+
.bv{display:flex;flex-direction:column;gap:26px;max-width:min(760px,100%)}
|
|
316
|
+
.bv .prosa{max-width:100%}
|
|
317
|
+
.bv code.mono{word-break:break-all}
|
|
318
|
+
/* The live rail has nothing to show before a city exists, and it costs half
|
|
319
|
+
the screen exactly when the guide needs it. */
|
|
320
|
+
body.enGuia #livePanel,body.enGuia #liveResize{display:none}
|
|
321
|
+
body.enGuia #app{grid-template-columns:250px minmax(0,1fr)}
|
|
322
|
+
.bvPasos{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
|
|
323
|
+
.bvPunto{display:flex;align-items:center;gap:6px;font:10px var(--hud);letter-spacing:.1em;
|
|
324
|
+
text-transform:uppercase;color:var(--tinta3)}
|
|
325
|
+
.bvPunto i{width:8px;height:8px;border-radius:50%;background:var(--linea2);display:block}
|
|
326
|
+
.bvPunto+.bvPunto{margin-left:10px}
|
|
327
|
+
.bvPunto.aqui{color:var(--lampara)}.bvPunto.aqui i{background:var(--lampara)}
|
|
328
|
+
.bvPunto.hecho i{background:var(--verde)}
|
|
329
|
+
.bvHola{display:grid;grid-template-columns:auto 1fr;gap:26px;align-items:start}
|
|
330
|
+
@media(max-width:820px){.bvHola{grid-template-columns:1fr}}
|
|
331
|
+
.bvArte{display:flex;flex-direction:column;gap:2px;padding-top:6px}
|
|
332
|
+
.bvCasa{width:92px;height:83px;margin-bottom:-26px;filter:drop-shadow(var(--sombra))}
|
|
333
|
+
.bvPaso{display:flex;flex-direction:column;gap:14px}
|
|
334
|
+
.bvPaso h1{font-size:26px;line-height:1.15}
|
|
335
|
+
.bvEtiqueta{font:600 10px var(--hud);letter-spacing:.14em;text-transform:uppercase;
|
|
336
|
+
color:var(--tinta3)}
|
|
337
|
+
.bvRejilla{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px}
|
|
338
|
+
.bvRejilla.bvTres{grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
|
|
339
|
+
.bvOpcion{text-align:left;display:flex;flex-direction:column;gap:4px;padding:12px 13px;
|
|
340
|
+
border:1px solid var(--linea2);border-radius:7px;background:var(--suelo2);cursor:pointer}
|
|
341
|
+
.bvOpcion b{font-family:var(--hud);font-size:13px;color:var(--tinta)}
|
|
342
|
+
.bvOpcion span{font-size:12px;line-height:1.45;color:var(--tinta3)}
|
|
343
|
+
.bvOpcion:hover{border-color:var(--lampara)}
|
|
344
|
+
.bvOpcion.elegida{border-color:var(--lampara);background:var(--burbuja-mia);
|
|
345
|
+
box-shadow:inset 3px 0 0 var(--lampara)}
|
|
346
|
+
.bvChips{display:flex;flex-wrap:wrap;gap:6px}
|
|
347
|
+
.bvChip{border:1px solid var(--linea2);background:var(--suelo2);color:var(--tinta2);
|
|
348
|
+
font:11px var(--mono);padding:5px 10px;border-radius:999px;cursor:pointer}
|
|
349
|
+
.bvChip:hover{border-color:var(--lampara)}
|
|
350
|
+
.bvChip.elegida{border-color:var(--lampara);background:var(--burbuja-mia);color:var(--tinta)}
|
|
351
|
+
.bvFichas{display:flex;flex-direction:column;gap:6px}
|
|
352
|
+
.bvFicha{display:flex;gap:10px;align-items:baseline;padding:9px 12px;border-radius:6px;
|
|
353
|
+
border:1px solid var(--linea);background:var(--suelo2)}
|
|
354
|
+
.bvFicha b{font-family:var(--hud);font-size:13px}
|
|
355
|
+
.bvFicha span{font:11px var(--mono);color:var(--tinta3)}
|
|
356
|
+
.bvBotones{display:flex;gap:9px;flex-wrap:wrap;margin-top:4px}
|
|
357
|
+
.bvSiguientes{display:flex;gap:9px;flex-wrap:wrap}
|
|
308
358
|
/* The instruction editor: one file, one reader, saved where the agent lives. */
|
|
309
359
|
#editorIns{position:fixed;inset:0;background:rgba(6,10,15,.72);z-index:60;
|
|
310
360
|
display:flex;align-items:center;justify-content:center}
|
|
@@ -442,15 +492,14 @@ input[type=text]:focus,select:focus{outline:none;border-color:var(--lampara)}
|
|
|
442
492
|
<span class="badge">town hall</span>
|
|
443
493
|
<h2>Agents City</h2>
|
|
444
494
|
<p>Manage one autonomous city: its seat, repo agents, roads and map. Everything here is a plain file you can also inspect by hand.</p>
|
|
495
|
+
<button type="button" id="temaBoton" class="temaBoton"
|
|
496
|
+
title="Daylight is the default; night is the map's own light">☾ Night</button>
|
|
445
497
|
</div>
|
|
446
498
|
<ol id="rail"></ol>
|
|
447
499
|
<div id="ciudades" style="padding:0 18px"></div>
|
|
448
500
|
<div class="pie">
|
|
449
501
|
<p id="avisoVersion" class="avisoVersion" hidden></p>
|
|
450
502
|
<a href="#" id="alWizard">Create another city →</a>
|
|
451
|
-
<button type="button" id="temaBoton" class="temaBoton"
|
|
452
|
-
title="Switch between the night city and the daylight palette of agentscity.net">
|
|
453
|
-
☾ Night</button>
|
|
454
503
|
<span class="dato" id="dondeDatos"></span>
|
|
455
504
|
</div>
|
|
456
505
|
</nav>
|
package/bin/navegador.mjs
CHANGED
|
@@ -108,6 +108,7 @@ function comprueba(texto, bien, detalle = '') {
|
|
|
108
108
|
|
|
109
109
|
async function main() {
|
|
110
110
|
const url = process.argv[2];
|
|
111
|
+
const ciudadVacia = process.argv[3] || '';
|
|
111
112
|
if (!url) {
|
|
112
113
|
console.error('usage: navegador.mjs <hall url>');
|
|
113
114
|
process.exit(2);
|
|
@@ -228,12 +229,69 @@ async function main() {
|
|
|
228
229
|
JSON.stringify(modal),
|
|
229
230
|
);
|
|
230
231
|
|
|
232
|
+
// The guided first run, checked against a city with nobody in it: that is
|
|
233
|
+
// the only state where it appears, and it is the screen that used to say
|
|
234
|
+
// "Not drawn yet" to somebody arriving for the first time.
|
|
235
|
+
if (ciudadVacia) {
|
|
236
|
+
await cdp.manda('Page.navigate', {
|
|
237
|
+
url: url + '&city=' + encodeURIComponent(ciudadVacia),
|
|
238
|
+
});
|
|
239
|
+
for (let i = 0; i < 60; i += 1) {
|
|
240
|
+
const listo = await cdp.evalua('!!document.getElementById("rail") && !!window.PASE');
|
|
241
|
+
if (listo) break;
|
|
242
|
+
await dormir(250);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const guia = await cdp.evalua(`(async () => {
|
|
246
|
+
for (let i = 0; i < 40; i++) {
|
|
247
|
+
if (document.querySelector('.bvPasos')) break;
|
|
248
|
+
await new Promise(r => setTimeout(r, 250));
|
|
249
|
+
}
|
|
250
|
+
const pasos = [...document.querySelectorAll('.bvPunto')].map(p => p.textContent.trim());
|
|
251
|
+
const b = document.querySelector('[data-bv="siguiente"]');
|
|
252
|
+
if (b) b.click();
|
|
253
|
+
await new Promise(r => setTimeout(r, 900));
|
|
254
|
+
return {
|
|
255
|
+
pasos,
|
|
256
|
+
titulo: document.querySelector('.bvPaso h1')?.textContent?.trim() ?? '',
|
|
257
|
+
opciones: document.querySelectorAll('.bvOpcion').length,
|
|
258
|
+
};
|
|
259
|
+
})()`);
|
|
260
|
+
comprueba(
|
|
261
|
+
'a city with no agents opens the guide, not an empty map',
|
|
262
|
+
(guia?.pasos?.length ?? 0) >= 4,
|
|
263
|
+
JSON.stringify(guia),
|
|
264
|
+
);
|
|
265
|
+
comprueba(
|
|
266
|
+
'and its first question offers the work domains to choose from',
|
|
267
|
+
/work happens here/i.test(guia?.titulo ?? '') && (guia?.opciones ?? 0) > 3,
|
|
268
|
+
JSON.stringify(guia),
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// Cities: a person must be able to start and retire one without a terminal.
|
|
272
|
+
const ciudades = await cdp.evalua(`(async () => {
|
|
273
|
+
const ir = [...document.querySelectorAll('nav li')].find(l => /cities/i.test(l.textContent));
|
|
274
|
+
if (!ir) return { falta: true };
|
|
275
|
+
ir.click();
|
|
276
|
+
await new Promise(r => setTimeout(r, 700));
|
|
277
|
+
return {
|
|
278
|
+
crear: !!document.getElementById('creaCiudad'),
|
|
279
|
+
campo: !!document.getElementById('nuevaCiudad'),
|
|
280
|
+
filas: document.querySelectorAll('.lista .fila').length,
|
|
281
|
+
};
|
|
282
|
+
})()`);
|
|
283
|
+
comprueba(
|
|
284
|
+
'the Cities section can start a new city',
|
|
285
|
+
ciudades?.crear === true && ciudades?.campo === true,
|
|
286
|
+
JSON.stringify(ciudades),
|
|
287
|
+
);
|
|
288
|
+
|
|
231
289
|
const errores = await cdp.evalua('window.__erroresDePagina ? window.__erroresDePagina.length : 0');
|
|
232
290
|
comprueba('the page raised no uncaught errors while we drove it', errores === 0, String(errores));
|
|
233
291
|
} finally {
|
|
234
292
|
proceso.kill('SIGKILL');
|
|
235
293
|
}
|
|
236
|
-
console.log(`\n ${fallos ? `${fallos} failed` : 'browser ok'} —
|
|
294
|
+
console.log(`\n ${fallos ? `${fallos} failed` : 'browser ok'} — 14 checks\n`);
|
|
237
295
|
process.exit(fallos ? 1 : 0);
|
|
238
296
|
}
|
|
239
297
|
|
package/bin/serve.py
CHANGED
|
@@ -679,6 +679,7 @@ class Manejador(http.server.BaseHTTPRequestHandler):
|
|
|
679
679
|
"/api/mapa": "p_mapa",
|
|
680
680
|
"/api/sesion": "p_sesion",
|
|
681
681
|
"/api/ciudades": "p_ciudades",
|
|
682
|
+
"/api/ciudad-archiva": "p_archiva_ciudad",
|
|
682
683
|
"/api/roads": "p_roads",
|
|
683
684
|
"/api/agente": "p_agente",
|
|
684
685
|
"/api/agentes": "p_agentes",
|
|
@@ -1049,6 +1050,26 @@ class Manejador(http.server.BaseHTTPRequestHandler):
|
|
|
1049
1050
|
{"ok": True, "city": datos, "address": cities.direccion(usuario, datos)}
|
|
1050
1051
|
)
|
|
1051
1052
|
|
|
1053
|
+
def p_archiva_ciudad(self, q, cuerpo):
|
|
1054
|
+
"""Take one city out of use — recoverably, and never the last one.
|
|
1055
|
+
|
|
1056
|
+
Not a delete: `cities.archiva` MOVES the folder into the owner's
|
|
1057
|
+
backups. A city is somebody's cards, deliberations and map, and a
|
|
1058
|
+
product that erases that on one click will erase the wrong one.
|
|
1059
|
+
"""
|
|
1060
|
+
pedida = str(cuerpo.get("city") or "")
|
|
1061
|
+
usuario = seat.quien_soy()
|
|
1062
|
+
datos = cities.resuelve(pedida, usuario) if pedida else ""
|
|
1063
|
+
if not datos:
|
|
1064
|
+
return self.responde({"error": f"no city called {pedida!r}"}, codigo=404)
|
|
1065
|
+
try:
|
|
1066
|
+
copia = cities.archiva(datos, usuario)
|
|
1067
|
+
except (OSError, ValueError) as e:
|
|
1068
|
+
return self.responde({"error": str(e)}, codigo=409)
|
|
1069
|
+
return self.responde(
|
|
1070
|
+
{"ok": True, "backup": copia, "ciudades": lista_con(cities.actual(usuario))}
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1052
1073
|
def p_roads(self, q, cuerpo):
|
|
1053
1074
|
origen = self.ciudad(q)
|
|
1054
1075
|
accion = str(cuerpo.get("action") or "")
|
package/bin/test-navegador.py
CHANGED
|
@@ -95,13 +95,20 @@ def main():
|
|
|
95
95
|
)
|
|
96
96
|
ciudad_de_prueba(datos)
|
|
97
97
|
|
|
98
|
+
# A SECOND city, deliberately empty: the guide only appears where there is
|
|
99
|
+
# nothing yet, and the first fixture has an agent so the sheets can render.
|
|
100
|
+
try:
|
|
101
|
+
vacia = serve.cities.crea("navtest", "sin-nadie", usar=False)
|
|
102
|
+
except (OSError, ValueError):
|
|
103
|
+
vacia = ""
|
|
104
|
+
|
|
98
105
|
servidor = serve.Servidor(("127.0.0.1", 0), serve.Manejador)
|
|
99
106
|
hilo = threading.Thread(target=servidor.serve_forever, daemon=True)
|
|
100
107
|
hilo.start()
|
|
101
108
|
url = f"http://127.0.0.1:{servidor.server_port}/?PASE={serve.PASE}"
|
|
102
109
|
try:
|
|
103
110
|
salida = subprocess.run(
|
|
104
|
-
["node", os.path.join(AQUI, "navegador.mjs"), url],
|
|
111
|
+
["node", os.path.join(AQUI, "navegador.mjs"), url, vacia],
|
|
105
112
|
capture_output=True,
|
|
106
113
|
text=True,
|
|
107
114
|
timeout=300,
|
package/bin/test-seat.py
CHANGED
|
@@ -518,7 +518,7 @@ def maquinas_hostiles():
|
|
|
518
518
|
"· an old cached Claude plugin is updated through Claude's own CLI",
|
|
519
519
|
llamadas
|
|
520
520
|
and llamadas[0][:4] == ["claude", "plugin", "update", "city@agents-city"]
|
|
521
|
-
and "0.
|
|
521
|
+
and "0.2.0" in salida.getvalue(),
|
|
522
522
|
f"calls={llamadas!r} output={salida.getvalue()!r}",
|
|
523
523
|
)
|
|
524
524
|
|
package/bin/test-serve.py
CHANGED
|
@@ -176,18 +176,24 @@ def main():
|
|
|
176
176
|
cabecera = pagina.split("*{box-sizing", 1)[0]
|
|
177
177
|
cuerpo_css = pagina.split("*{box-sizing", 1)[1].split("</style>", 1)[0]
|
|
178
178
|
afirma(
|
|
179
|
-
"· the
|
|
180
|
-
"--
|
|
181
|
-
and "--
|
|
182
|
-
and "--
|
|
183
|
-
cabecera[-
|
|
179
|
+
"· daylight is the default skin, in the tokens of agentscity.net",
|
|
180
|
+
"--suelo:#f3f0e8" in cabecera
|
|
181
|
+
and "--tinta:#1d2229" in cabecera
|
|
182
|
+
and "--lampara:#7048e8" in cabecera,
|
|
183
|
+
cabecera[-500:],
|
|
184
|
+
)
|
|
185
|
+
afirma(
|
|
186
|
+
"· night is declared once and only mapped, never written twice",
|
|
187
|
+
"--noche-suelo:#131a24" in cabecera
|
|
188
|
+
and cabecera.count("--noche-suelo:#131a24") == 1,
|
|
189
|
+
cabecera[-500:],
|
|
184
190
|
)
|
|
185
191
|
afirma(
|
|
186
192
|
"· an explicit choice outranks the machine's preference",
|
|
187
|
-
'[data-tema="
|
|
188
|
-
and "prefers-color-scheme:
|
|
189
|
-
and ':root:not([data-tema="
|
|
190
|
-
cabecera[-
|
|
193
|
+
'[data-tema="oscuro"]' in cabecera
|
|
194
|
+
and "prefers-color-scheme:dark" in cabecera
|
|
195
|
+
and ':root:not([data-tema="claro"])' in cabecera,
|
|
196
|
+
cabecera[-700:],
|
|
191
197
|
)
|
|
192
198
|
import re as _re
|
|
193
199
|
|