@7ots/cli 0.1.5 → 0.1.7
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 +15 -21
- package/cli/7ots.mjs +55 -5
- package/cli/lib/account.mjs +89 -0
- package/cli/lib/assistant.mjs +126 -16
- package/cli/lib/brain.mjs +41 -11
- package/cli/lib/card-shot.mjs +84 -0
- package/cli/lib/config.mjs +29 -5
- package/cli/lib/i18n.mjs +57 -0
- package/cli/lib/inferences.mjs +76 -0
- package/cli/lib/integrations.mjs +414 -0
- package/cli/lib/orquesta.mjs +53 -0
- package/cli/lib/pet-core.mjs +7 -10
- package/cli/lib/pet-server.mjs +373 -22
- package/cli/lib/prompts.mjs +31 -17
- package/cli/lib/prowl.mjs +110 -0
- package/cli/lib/screen.mjs +125 -0
- package/cli/lib/stt-local.py +14 -0
- package/cli/lib/stt.mjs +68 -0
- package/cli/lib/tts-local.mjs +110 -0
- package/cli/lib/tts-local.py +18 -0
- package/cli/lib/vm.mjs +309 -0
- package/cli/lib/watchers.mjs +151 -0
- package/cli/pet/card.html +294 -0
- package/cli/pet/electron/director.cjs +175 -0
- package/cli/pet/electron/main.cjs +354 -49
- package/cli/pet/pet.html +328 -15
- package/cli/pet/settings.html +995 -58
- package/dist/7ots.esm.js +56 -56
- package/dist/7ots.esm.js.map +3 -3
- package/dist/7ots.iife.js +56 -56
- package/dist/7ots.iife.js.map +3 -3
- package/llms.txt +31 -17
- package/package.json +17 -14
- package/server/llm.mjs +2 -0
- package/server/platform/connect.mjs +3 -1
- package/server/platform/db.mjs +82 -0
- package/server/platform/device.mjs +125 -0
- package/server/platform/orquesta.mjs +5 -0
- package/server/platform/routes.mjs +101 -1
- package/server/platform/runtime.mjs +2 -2
- package/server/platform/sites.mjs +146 -0
- package/server/platform/store.mjs +3 -1
- package/server/platform/vm.mjs +778 -0
- package/server/tts.mjs +17 -4
- package/src/character/Character.js +1 -1
- package/src/character/parts.js +10 -0
- package/src/character3d/Character3D.js +105 -2
- package/src/character3d/index.js +2 -1
- package/src/character3d/stage.js +15 -4
- package/src/i18n/messages/character.js +6 -3
- package/src/i18n/messages/platform.js +60 -3
package/server/tts.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* grok → xAI (POST https://api.x.ai/v1/tts). XAI_API_KEY, XAI_TTS_VOICE (eve, ara, leo, rex, sal o voz propia)
|
|
12
12
|
* fish → Fish Audio (POST https://api.fish.audio/v1/tts). FISH_API_KEY, FISH_VOICE_ID (reference_id;
|
|
13
13
|
* vacío = voz propia del modelo), FISH_MODEL (vacío = s2.1-pro; va en la cabecera `model`)
|
|
14
|
-
* openai → (compatibilidad) OPENAI_API_KEY, OPENAI_TTS_MODEL (gpt-4o-mini-tts), OPENAI_TTS_VOICE (alloy)
|
|
14
|
+
* openai → (compatibilidad) OPENAI_API_KEY (solo si LLM_BASE_URL es OpenAI o está vacío; si no, OPENAI_TTS_API_KEY), OPENAI_TTS_MODEL (gpt-4o-mini-tts), OPENAI_TTS_VOICE (alloy)
|
|
15
15
|
* none → 501: el widget usa la voz del navegador (speechSynthesis)
|
|
16
16
|
*
|
|
17
17
|
* La voz de la identidad del agente (server/identity.mjs → voice) manda sobre las variables:
|
|
@@ -39,10 +39,22 @@ export function ttsProvider(env = process.env) {
|
|
|
39
39
|
if (env.ELEVENLABS_API_KEY) return 'elevenlabs';
|
|
40
40
|
if (env.XAI_API_KEY) return 'grok';
|
|
41
41
|
if (env.FISH_API_KEY) return 'fish';
|
|
42
|
-
if (env
|
|
42
|
+
if (openaiTtsKey(env)) return 'openai';
|
|
43
43
|
return 'none';
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* ¿OPENAI_API_KEY sirve para la voz de OpenAI? No si el LLM apunta a otro servicio compatible
|
|
48
|
+
* (LLM_BASE_URL = DeepSeek, Groq, OpenRouter…): esa clave es de ese servicio y OpenAI la rechazaría
|
|
49
|
+
* (antes: auto-detección → 'openai' → 502 en cada frase). OPENAI_TTS_API_KEY la fuerza.
|
|
50
|
+
*/
|
|
51
|
+
export function openaiTtsKey(env = process.env) {
|
|
52
|
+
if (env.OPENAI_TTS_API_KEY) return env.OPENAI_TTS_API_KEY;
|
|
53
|
+
if (!env.OPENAI_API_KEY) return '';
|
|
54
|
+
const base = String(env.LLM_BASE_URL || '').trim();
|
|
55
|
+
return !base || /^https?:\/\/api\.openai\.com(\/|$)/i.test(base) ? env.OPENAI_API_KEY : '';
|
|
56
|
+
}
|
|
57
|
+
|
|
46
58
|
export async function synthesize({ text, voice = {}, env = process.env }) {
|
|
47
59
|
const provider = PROVIDERS.includes(voice.provider) ? voice.provider : voice.provider === 'browser' ? 'none' : ttsProvider(env);
|
|
48
60
|
const input = String(text || '').slice(0, 1500);
|
|
@@ -120,8 +132,9 @@ export async function synthesize({ text, voice = {}, env = process.env }) {
|
|
|
120
132
|
}
|
|
121
133
|
|
|
122
134
|
if (provider === 'openai') {
|
|
123
|
-
|
|
124
|
-
|
|
135
|
+
const key = openaiTtsKey(env);
|
|
136
|
+
if (!key) throw i18nError(501, 'server.tts.noKey', { provider: 'OpenAI', key: env.OPENAI_API_KEY ? 'OPENAI_TTS_API_KEY' : 'OPENAI_API_KEY' });
|
|
137
|
+
const openai = await openaiClient(key); // TTS siempre contra OpenAI (no LLM_BASE_URL)
|
|
125
138
|
const model = voice.model || env.OPENAI_TTS_MODEL || 'gpt-4o-mini-tts';
|
|
126
139
|
const res = await openai.audio.speech.create({
|
|
127
140
|
model,
|
|
@@ -208,7 +208,7 @@ export function randomCharacter(seed = Math.random() * 1e9) {
|
|
|
208
208
|
return `#${[f(0), f(8), f(4)].map((v) => v.toString(16).padStart(2, '0')).join('')}`;
|
|
209
209
|
};
|
|
210
210
|
const groups = new Map();
|
|
211
|
-
for (const a of ACCESSORIES) if (a.group !== 'extra') groups.set(a.group, [...(groups.get(a.group) || []), a]);
|
|
211
|
+
for (const a of ACCESSORIES) if (a.group !== 'extra' && a.random !== false) groups.set(a.group, [...(groups.get(a.group) || []), a]);
|
|
212
212
|
const acc = [];
|
|
213
213
|
for (const [, list] of groups) if (rnd() < 0.35) acc.push({ id: pick(list).id });
|
|
214
214
|
return normalizeCharacter({
|
package/src/character/parts.js
CHANGED
|
@@ -499,6 +499,16 @@ export const ACCESSORIES = [
|
|
|
499
499
|
{ id: 'headphones', label: 'Auriculares', group: 'head', anchor: 'eyes', color: '#111827', color2: '#ef4444', draw: (c) => `<path d="M${n(-c.w * 0.52)} 0Q${n(-c.w * 0.6)} ${n(-c.eyeDy - 16)} 0 ${n(-c.eyeDy - 16)}Q${n(c.w * 0.6)} ${n(-c.eyeDy - 16)} ${n(c.w * 0.52)} 0" stroke="${c.color}" stroke-width="8" fill="none" stroke-linecap="round"/>${[-1, 1].map((s) => `<rect x="${n(s * c.w * 0.52 - 11)}" y="-16" width="22" height="34" rx="10" fill="${c.color2}" ${S(3)}/>`).join('')}` },
|
|
500
500
|
{ id: 'hair', label: 'Tupé', group: 'head', anchor: 'top', color: '#78350f', draw: (c) => `<g transform="scale(${n(c.k)})"><path d="M-30 14Q-40 -20 -6 -26Q-10 -40 14 -36Q40 -30 34 14Q24 -8 0 -4Q-18 -2 -30 14Z" fill="${c.color}" ${S(3)}/></g>` },
|
|
501
501
|
{ id: 'sprout', label: 'Brote', group: 'head', anchor: 'top', color: '#22c55e', draw: (c) => `<g transform="scale(${n(c.k)})"><path d="M0 6V-20" stroke="#15803d" stroke-width="4" stroke-linecap="round"/><path d="M0 -18Q-24 -34 -28 -14Q-10 -8 0 -18Z" fill="${c.color}" ${S(2.5)}/><path d="M0 -20Q20 -38 28 -20Q12 -10 0 -20Z" fill="${c.color}" ${S(2.5)}/></g>` },
|
|
502
|
+
// Sombreros añadidos después: `random: false` los deja fuera de randomCharacter (si entraran en
|
|
503
|
+
// la lista del grupo, cada semilla ya creada sacaría otro ot). Se eligen a mano en el editor.
|
|
504
|
+
{ id: 'fedora', label: 'Fedora', group: 'head', anchor: 'top', random: false, color: '#57534e', color2: '#1c1917', draw: (c) => `<g transform="scale(${n(c.k)}) rotate(-6)"><path d="M-64 8Q-58 -2 -40 2Q0 10 40 2Q58 -2 64 8Q42 20 0 20Q-42 20 -64 8Z" fill="${c.color}" ${S(3)}/><path d="M-34 6Q-38 -30 -20 -38Q-6 -30 0 -34Q6 -30 20 -38Q38 -30 34 6Q0 14 -34 6Z" fill="${c.color}" ${S(3)}/><path d="M-35 -4Q0 4 35 -4L35 5Q0 13 -35 5Z" fill="${c.color2}"/><path d="M-24 -30Q-28 -14 -26 -6" stroke="#fff" stroke-width="3" fill="none" opacity=".18" stroke-linecap="round"/></g>` },
|
|
505
|
+
{ id: 'bowler', label: 'Bombín', group: 'head', anchor: 'top', random: false, color: '#1f2937', color2: '#78350f', draw: (c) => `<g transform="scale(${n(c.k)})"><path d="M-50 10Q-52 0 -42 3Q0 12 42 3Q52 0 50 10Q30 20 0 20Q-30 20 -50 10Z" fill="${c.color}" ${S(3)}/><path d="M-34 8Q-38 -44 0 -44Q38 -44 34 8Q0 15 -34 8Z" fill="${c.color}" ${S(3)}/><path d="M-35 -2Q0 6 35 -2L34 6Q0 13 -34 6Z" fill="${c.color2}"/><path d="M-20 -34Q-28 -20 -26 -8" stroke="#fff" stroke-width="3.5" fill="none" opacity=".2" stroke-linecap="round"/></g>` },
|
|
506
|
+
{ id: 'propeller', label: 'Gorra de hélice', group: 'head', anchor: 'top', random: false, color: '#3b82f6', color2: '#facc15', draw: (c) => `<g transform="scale(${n(c.k)})"><path d="M-44 14Q-46 -32 0 -34Q46 -32 44 14Z" fill="${c.color}" ${S(3)}/><path d="M-15 13Q-17 -22 0 -34Q17 -22 15 13Z" fill="${c.color2}"/><path d="M-44 14Q-46 -32 0 -34Q46 -32 44 14Z" fill="none" ${S(3)}/><path d="M-48 12Q0 22 48 12" stroke="${shade(c.color, -0.25)}" stroke-width="5" fill="none" stroke-linecap="round"/><path d="M0 -34V-46" stroke="#1f2937" stroke-width="3.5" stroke-linecap="round"/><ellipse cx="-17" cy="-48" rx="17" ry="5.5" fill="#ef4444" ${S(2)}/><ellipse cx="17" cy="-48" rx="17" ry="5.5" fill="#22c55e" ${S(2)}/><circle cy="-48" r="4.5" fill="${c.color2}" ${S(2)}/></g>` },
|
|
507
|
+
{ id: 'witch', label: 'Sombrero de bruja', group: 'head', anchor: 'top', random: false, color: '#312e81', color2: '#a855f7', draw: (c) => `<g transform="scale(${n(c.k)})"><ellipse cy="10" rx="62" ry="12" fill="${c.color}" ${S(3)}/><path d="M-34 8Q-22 -38 4 -66Q22 -86 46 -76Q22 -66 18 -40Q24 -12 34 8Q0 16 -34 8Z" fill="${c.color}" ${S(3)}/><path d="M-32 -2Q0 6 32 -2L34 8Q0 16 -34 8Z" fill="${c.color2}"/><rect x="-8" y="-2" width="16" height="12" rx="2" fill="none" stroke="#facc15" stroke-width="3"/></g>` },
|
|
508
|
+
{ id: 'fez', label: 'Fez', group: 'head', anchor: 'top', random: false, color: '#b91c1c', color2: '#111827', draw: (c) => `<g transform="scale(${n(c.k)}) rotate(6)"><path d="M-26 12L-20 -34Q0 -38 20 -34L26 12Q0 18 -26 12Z" fill="${c.color}" ${S(3)}/><ellipse cy="-34" rx="20" ry="5" fill="${shade(c.color, -0.18)}" ${S(2.5)}/><path d="M0 -36Q22 -38 26 -8" stroke="${c.color2}" stroke-width="3" fill="none" stroke-linecap="round"/><path d="M21 -10h10l3 16h-16Z" fill="${c.color2}"/><path d="M-14 -26V6" stroke="#fff" stroke-width="3" opacity=".15" stroke-linecap="round"/></g>` },
|
|
509
|
+
{ id: 'sombrero', label: 'Sombrero mexicano', group: 'head', anchor: 'top', random: false, color: '#eab308', color2: '#dc2626', draw: (c) => `<g transform="scale(${n(c.k)})"><path d="M-84 10Q-80 -6 -56 2Q0 16 56 2Q80 -6 84 10Q60 30 0 30Q-60 30 -84 10Z" fill="${c.color}" ${S(3)}/><path d="M-26 8Q-30 -40 0 -44Q30 -40 26 8Q0 16 -26 8Z" fill="${c.color}" ${S(3)}/><path d="M-27 -4L-18 3L-9 -4L0 3L9 -4L18 3L27 -4" stroke="${c.color2}" stroke-width="4" fill="none" stroke-linejoin="round"/>${[-70, -48, -24, 0, 24, 48, 70].map((x) => `<circle cx="${x}" cy="${n(22 - Math.abs(x) * 0.12)}" r="3.5" fill="${c.color2}"/>`).join('')}</g>` },
|
|
510
|
+
{ id: 'bucket', label: 'Gorro pescador', group: 'head', anchor: 'top', random: false, color: '#a3a35a', color2: '#65662f', draw: (c) => `<g transform="scale(${n(c.k)})"><path d="M-46 4Q0 14 46 4L60 20Q0 34 -60 20Z" fill="${shade(c.color, -0.08)}" ${S(3)}/><path d="M-36 -26Q0 -38 36 -26L44 6Q0 16 -44 6Z" fill="${c.color}" ${S(3)}/><path d="M-50 12Q0 24 50 12M-40 -1Q0 9 40 -1" stroke="${c.color2}" stroke-width="1.8" stroke-dasharray="4 3" fill="none"/></g>` },
|
|
511
|
+
{ id: 'jester', label: 'Bufón', group: 'head', anchor: 'top', random: false, color: '#7c3aed', color2: '#16a34a', draw: (c) => `<g transform="scale(${n(c.k)})"><path d="M-40 6Q-48 -28 -76 -26Q-56 -42 -26 -34Q-14 -20 -10 6Z" fill="${c.color}" ${S(3)}/><path d="M40 6Q48 -28 76 -26Q56 -42 26 -34Q14 -20 10 6Z" fill="${c.color}" ${S(3)}/><path d="M-16 6Q-18 -42 0 -64Q18 -42 16 6Z" fill="${c.color2}" ${S(3)}/>${[[-76, -26], [0, -64], [76, -26]].map(([x, y]) => `<circle cx="${x}" cy="${y}" r="6.5" fill="#facc15" ${S(2)}/>`).join('')}<rect x="-46" y="0" width="92" height="16" rx="8" fill="${shade(c.color, -0.2)}" ${S(3)}/></g>` },
|
|
502
512
|
// ── ojos ──
|
|
503
513
|
{ id: 'glasses', label: 'Gafas redondas', group: 'eyes', anchor: 'eyes', color: '#1f2937', draw: (c) => `${[-1, 1].map((s) => `<circle cx="${n(s * c.sp)}" r="${n(c.r * 1.55)}" fill="#fff" fill-opacity=".12" stroke="${c.color}" stroke-width="3.5"/>`).join('')}<path d="M${n(-c.sp + c.r * 1.55)} -2Q0 -8 ${n(c.sp - c.r * 1.55)} -2" stroke="${c.color}" stroke-width="3" fill="none"/>` },
|
|
504
514
|
{ id: 'square', label: 'Gafas cuadradas', group: 'eyes', anchor: 'eyes', color: '#1f2937', draw: (c) => `${[-1, 1].map((s) => `<rect x="${n(s * c.sp - c.r * 1.7)}" y="${n(-c.r * 1.3)}" width="${n(c.r * 3.4)}" height="${n(c.r * 2.6)}" rx="4" fill="#fff" fill-opacity=".12" stroke="${c.color}" stroke-width="4"/>`).join('')}<path d="M${n(-c.sp + c.r * 1.7)} -3H${n(c.sp - c.r * 1.7)}" stroke="${c.color}" stroke-width="4"/>` },
|
|
@@ -651,7 +651,99 @@ export class Character3D {
|
|
|
651
651
|
h.rotation.z = -0.12;
|
|
652
652
|
return onTop(h, 0.06);
|
|
653
653
|
}
|
|
654
|
-
case '
|
|
654
|
+
case 'fedora': case 'bowler': {
|
|
655
|
+
const fm = mat('felt', col);
|
|
656
|
+
const fed = id === 'fedora';
|
|
657
|
+
const h = G(
|
|
658
|
+
// ala (fedora: algo más ancha y levantada atrás)
|
|
659
|
+
M(new THREE.CylinderGeometry((fed ? 0.4 : 0.32) * kk, (fed ? 0.4 : 0.32) * kk, 0.022 * kk, 48), fm, null, fed ? [-0.08, 0, 0] : null),
|
|
660
|
+
fed
|
|
661
|
+
? M(new THREE.CylinderGeometry(0.19 * kk, 0.23 * kk, 0.24 * kk, 40), fm, [0, 0.12 * kk, 0], null, [1, 1, 0.85])
|
|
662
|
+
: M(this.sph(0.23 * kk, 40, 20, 0, TAU, 0, Math.PI / 2), fm, [0, 0.06 * kk, 0], null, [1, 1.05, 0.92]),
|
|
663
|
+
M(new THREE.CylinderGeometry(0.235 * kk, 0.235 * kk, 0.05 * kk, 40), mat('felt', col2), [0, 0.045 * kk, 0], null, [1, 1, fed ? 0.88 : 0.93]),
|
|
664
|
+
);
|
|
665
|
+
if (!fed) h.add(M(new THREE.CylinderGeometry(0.23 * kk, 0.23 * kk, 0.06 * kk, 40), fm, [0, 0.03 * kk, 0], null, [1, 1, 0.92]));
|
|
666
|
+
if (fed) h.add(M(this.sph(0.12 * kk, 24, 10), mat('felt', shade(col, -0.25)), [0, 0.24 * kk, 0], null, [1, 0.25, 0.6])); // pellizco de la copa
|
|
667
|
+
h.rotation.z = fed ? -0.14 : -0.06;
|
|
668
|
+
return onTop(h, 0.06);
|
|
669
|
+
}
|
|
670
|
+
case 'witch': {
|
|
671
|
+
const fm = mat('felt', col);
|
|
672
|
+
const o = G(M(new THREE.CylinderGeometry(0.46 * kk, 0.46 * kk, 0.02 * kk, 48), fm));
|
|
673
|
+
// cono doblado: tres tramos cada vez más finos e inclinados
|
|
674
|
+
let y = 0;
|
|
675
|
+
let x = 0;
|
|
676
|
+
const segs = [[0.25, 0.17, 0.3, 0], [0.17, 0.09, 0.22, -0.35], [0.09, 0.01, 0.2, -0.9]];
|
|
677
|
+
for (const [r0, r1, h, rz] of segs) {
|
|
678
|
+
const m = M(new THREE.CylinderGeometry(r1 * kk, r0 * kk, h * kk, 32), fm);
|
|
679
|
+
m.geometry.translate(0, (h / 2) * kk, 0);
|
|
680
|
+
m.position.set(x, y, 0);
|
|
681
|
+
m.rotation.z = rz;
|
|
682
|
+
o.add(m);
|
|
683
|
+
x += -Math.sin(rz) * h * kk;
|
|
684
|
+
y += Math.cos(rz) * h * kk;
|
|
685
|
+
}
|
|
686
|
+
o.add(M(new THREE.CylinderGeometry(0.245 * kk, 0.25 * kk, 0.06 * kk, 40), mat('felt', col2), [0, 0.04 * kk, 0]));
|
|
687
|
+
o.add(M(new THREE.BoxGeometry(0.08 * kk, 0.06 * kk, 0.02 * kk), mat('metal', '#facc15'), [0, 0.04 * kk, 0.25 * kk]));
|
|
688
|
+
o.rotation.z = 0.1;
|
|
689
|
+
return onTop(o, 0.05);
|
|
690
|
+
}
|
|
691
|
+
case 'fez': {
|
|
692
|
+
const o = G(
|
|
693
|
+
M(new THREE.CylinderGeometry(0.16 * kk, 0.2 * kk, 0.3 * kk, 36), mat('felt', col), [0, 0.15 * kk, 0]),
|
|
694
|
+
M(new THREE.CylinderGeometry(0.006 * kk, 0.006 * kk, 0.14 * kk, 6), mat('felt', col2), [0.07 * kk, 0.305 * kk, 0.02 * kk], [0, 0, Math.PI / 2]),
|
|
695
|
+
);
|
|
696
|
+
const tas = G(
|
|
697
|
+
M(new THREE.CylinderGeometry(0.006 * kk, 0.006 * kk, 0.2 * kk, 6), mat('felt', col2), [0, -0.1 * kk, 0]),
|
|
698
|
+
M(new THREE.ConeGeometry(0.035 * kk, 0.09 * kk, 12), mat('felt', col2), [0, -0.2 * kk, 0]),
|
|
699
|
+
);
|
|
700
|
+
tas.position.set(0.14 * kk, 0.3 * kk, 0.02 * kk);
|
|
701
|
+
tas.rotation.z = 0.2;
|
|
702
|
+
o.add(tas);
|
|
703
|
+
this.anims.push((sec) => { tas.rotation.z = 0.2 + Math.sin(sec * 2.4) * 0.12; });
|
|
704
|
+
o.rotation.z = -0.1;
|
|
705
|
+
return onTop(o, 0.06);
|
|
706
|
+
}
|
|
707
|
+
case 'sombrero': {
|
|
708
|
+
const fm = mat('felt', col);
|
|
709
|
+
const o = G(
|
|
710
|
+
M(this.tor(0.5 * kk, 0.035 * kk, 10, 56), fm, [0, 0.03 * kk, 0], [Math.PI / 2, 0, 0]),
|
|
711
|
+
M(new THREE.CylinderGeometry(0.52 * kk, 0.52 * kk, 0.02 * kk, 56), fm),
|
|
712
|
+
M(new THREE.CylinderGeometry(0.13 * kk, 0.21 * kk, 0.26 * kk, 36), fm, [0, 0.13 * kk, 0]),
|
|
713
|
+
M(this.sph(0.13 * kk, 32, 12, 0, TAU, 0, Math.PI / 2), fm, [0, 0.26 * kk, 0], null, [1, 0.7, 1]),
|
|
714
|
+
M(new THREE.CylinderGeometry(0.205 * kk, 0.215 * kk, 0.05 * kk, 36), mat('felt', col2), [0, 0.04 * kk, 0]),
|
|
715
|
+
);
|
|
716
|
+
for (let i = 0; i < 12; i++) {
|
|
717
|
+
const a = (i / 12) * TAU;
|
|
718
|
+
o.add(M(this.sph(0.025 * kk, 10, 8), mat('felt', col2), [Math.cos(a) * 0.53 * kk, 0.0, Math.sin(a) * 0.53 * kk]));
|
|
719
|
+
}
|
|
720
|
+
return onTop(o, 0.05);
|
|
721
|
+
}
|
|
722
|
+
case 'bucket': {
|
|
723
|
+
const fm = mat('felt', col);
|
|
724
|
+
const o = G(
|
|
725
|
+
M(new THREE.CylinderGeometry(0.21 * kk, 0.26 * kk, 0.2 * kk, 40), fm, [0, 0.1 * kk, 0]),
|
|
726
|
+
M(this.sph(0.21 * kk, 40, 10, 0, TAU, 0, Math.PI / 2), fm, [0, 0.2 * kk, 0], null, [1, 0.25, 1]),
|
|
727
|
+
M(new THREE.CylinderGeometry(0.27 * kk, 0.37 * kk, 0.06 * kk, 48), mat('felt', shade(col, -0.08)), [0, -0.01 * kk, 0]),
|
|
728
|
+
);
|
|
729
|
+
return onTop(o, 0.08);
|
|
730
|
+
}
|
|
731
|
+
case 'jester': {
|
|
732
|
+
const o = G(fur(new THREE.TorusGeometry(0.24 * kk, 0.04 * kk, 12, 40).rotateX(Math.PI / 2), shade(col, -0.2), 0.6));
|
|
733
|
+
const cols = [col, col2, col];
|
|
734
|
+
[-1, 0, 1].forEach((s, i) => {
|
|
735
|
+
const len = s ? 0.42 : 0.5;
|
|
736
|
+
const horn = G(M(new THREE.ConeGeometry(0.12 * kk, len * kk, 24), mat('felt', cols[i]), [0, (len / 2) * kk, 0]));
|
|
737
|
+
horn.add(M(this.sph(0.04 * kk, 14, 10), mat('metal', '#facc15'), [0, len * kk, 0]));
|
|
738
|
+
horn.position.set(s * 0.12 * kk, 0.02 * kk, 0);
|
|
739
|
+
horn.rotation.z = -s * 0.95;
|
|
740
|
+
o.add(horn);
|
|
741
|
+
const ph = i * 1.7;
|
|
742
|
+
this.anims.push((sec) => { horn.rotation.z = -s * 0.95 + Math.sin(sec * 1.8 + ph) * 0.06; });
|
|
743
|
+
});
|
|
744
|
+
return onTop(o, 0.05);
|
|
745
|
+
}
|
|
746
|
+
case 'beanie': case 'santa': case 'cap': case 'hardhat': case 'viking': case 'beret': case 'propeller': {
|
|
655
747
|
const cp = capFor(id === 'beret' ? 0.14 : 0.24);
|
|
656
748
|
const dome = this.sph(1, 40, 20, 0, TAU, 0, Math.PI / 2).scale(cp.w * 1.08, top.y - cp.y + 0.05 * kk, cp.d * 1.1);
|
|
657
749
|
const o = G();
|
|
@@ -663,8 +755,19 @@ export class Character3D {
|
|
|
663
755
|
} else if (id === 'beret') {
|
|
664
756
|
o.add(M(dome, mat('felt', col), null, [0, 0, -0.25], [1.25, 0.45, 1.2]));
|
|
665
757
|
} else {
|
|
666
|
-
const m = id === 'viking' ? mat('metal', '#a3a3a3') : id === 'cap' ? mat('felt', col) : mat('plastic', col);
|
|
758
|
+
const m = id === 'viking' ? mat('metal', '#a3a3a3') : id === 'cap' || id === 'propeller' ? mat('felt', col) : mat('plastic', col);
|
|
667
759
|
o.add(M(dome, m));
|
|
760
|
+
if (id === 'propeller') {
|
|
761
|
+
// gorra de hélice: gajo de color, varilla y hélice que gira despacio
|
|
762
|
+
const hh = top.y - cp.y + 0.05 * kk;
|
|
763
|
+
o.add(M(this.sph(1, 24, 12, Math.PI / 2 - 0.35, 0.7, 0, Math.PI / 2).scale(cp.w * 1.085, hh * 1.005, cp.d * 1.105), mat('felt', col2)));
|
|
764
|
+
o.add(M(new THREE.CylinderGeometry(0.008 * kk, 0.008 * kk, 0.09 * kk, 8), mat('metal', '#9ca3af'), [0, hh + 0.04 * kk, 0]));
|
|
765
|
+
const prop = G(M(this.sph(0.02 * kk, 12, 8), mat('plastic', col2)));
|
|
766
|
+
for (const [sx, c2] of [[-1, '#ef4444'], [1, '#22c55e']]) prop.add(M(this.sph(1, 16, 8), mat('plastic', c2), [sx * 0.085 * kk, 0, 0], [sx * 0.3, 0, 0], [0.085 * kk, 0.008 * kk, 0.028 * kk]));
|
|
767
|
+
prop.position.y = hh + 0.085 * kk;
|
|
768
|
+
o.add(prop);
|
|
769
|
+
this.anims.push((sec) => { prop.rotation.y = sec * 5; });
|
|
770
|
+
}
|
|
668
771
|
if (id === 'cap') o.add(M(new THREE.CylinderGeometry(cp.w * 0.75, cp.w * 0.75, 0.02 * kk, 32, 1, false, -Math.PI / 2, Math.PI), mat('felt', col2 === '#ffffff' ? shade(col, -0.2) : col2), [0, 0.01, cp.d * 0.9], null, [1, 1, 0.9]));
|
|
669
772
|
if (id === 'hardhat') o.add(M(new THREE.CylinderGeometry(cp.w * 1.25, cp.w * 1.25, 0.02 * kk, 40), mat('plastic', col), [0, 0, 0.02]));
|
|
670
773
|
if (id === 'viking') for (const sx of [-1, 1]) o.add(M(new THREE.ConeGeometry(0.06 * kk, 0.3 * kk, 16), mat('plastic', '#fef3c7'), [sx * cp.w * 1.05, (top.y - cp.y) * 0.5, 0], [0, 0, -sx * 1.0]));
|
package/src/character3d/index.js
CHANGED
|
@@ -41,7 +41,7 @@ const v3 = new THREE.Vector3();
|
|
|
41
41
|
|
|
42
42
|
/** Posición en pantalla (cliente) de la cara de un personaje en una vista. */
|
|
43
43
|
function screenOf(view, ch) {
|
|
44
|
-
const r = view.rect || view.el.getBoundingClientRect();
|
|
44
|
+
const r = view.vrect || view.rect || view.el.getBoundingClientRect(); // the projection spans the drawn area (bleed)
|
|
45
45
|
ch.root.updateMatrixWorld();
|
|
46
46
|
v3.set(0, ch.H * 0.6, 0).applyMatrix4(ch.root.matrixWorld).project(view.camera);
|
|
47
47
|
return { x: r.left + ((v3.x + 1) / 2) * r.width, y: r.top + ((1 - v3.y) / 2) * r.height };
|
|
@@ -111,6 +111,7 @@ export function createCharacter3D(el, spec, opts = {}) {
|
|
|
111
111
|
camera,
|
|
112
112
|
characters: [ch],
|
|
113
113
|
rect: null,
|
|
114
|
+
bleed: opts.bleed || 0,
|
|
114
115
|
tick(t, dt) {
|
|
115
116
|
const a = this.rect.width / this.rect.height;
|
|
116
117
|
if (Math.abs(a - aspect) > 1e-3) {
|
package/src/character3d/stage.js
CHANGED
|
@@ -37,7 +37,9 @@ export function getStage(opts = {}) {
|
|
|
37
37
|
|
|
38
38
|
class Stage {
|
|
39
39
|
constructor(opts) {
|
|
40
|
-
|
|
40
|
+
// supersample: render above the screen's own density (a small canvas, e.g. the desktop pet, looks
|
|
41
|
+
// crisp at 2× on a 1× screen for little cost)
|
|
42
|
+
this.maxDpr = opts.supersample ? opts.maxDpr || 2 : Math.min(opts.maxDpr || 1.5, window.devicePixelRatio || 1);
|
|
41
43
|
this.minDpr = Math.min(opts.minDpr || 0.75, this.maxDpr);
|
|
42
44
|
this.dpr = this.maxDpr;
|
|
43
45
|
this.adaptive = opts.adaptive !== false;
|
|
@@ -159,9 +161,18 @@ class Stage {
|
|
|
159
161
|
v.rect = rect;
|
|
160
162
|
v.tick(t, dt);
|
|
161
163
|
chars += v.characters.length;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
// bleed: draws past the element's box (the whole window with 'window') at the same scale, so effects
|
|
165
|
+
// that rise above the head or fly to the sides aren't cut at the box's edge
|
|
166
|
+
let e = rect;
|
|
167
|
+
if (v.bleed) {
|
|
168
|
+
const b = v.bleed === 'window' ? null : v.bleed;
|
|
169
|
+
e = b == null ? { left: 0, top: 0, width: this._w, height: this._h } : { left: rect.left - b, top: rect.top - b, width: rect.width + 2 * b, height: rect.height + 2 * b };
|
|
170
|
+
v.camera.setViewOffset(rect.width, rect.height, e.left - rect.left, e.top - rect.top, e.width, e.height);
|
|
171
|
+
}
|
|
172
|
+
v.vrect = e;
|
|
173
|
+
const bottom = this._h - (e.top + e.height);
|
|
174
|
+
r.setViewport(e.left, bottom, e.width, e.height);
|
|
175
|
+
r.setScissor(e.left, bottom, e.width, e.height);
|
|
165
176
|
r.render(v.scene, v.camera);
|
|
166
177
|
}
|
|
167
178
|
// estadísticas y resolución adaptativa
|
|
@@ -45,6 +45,7 @@ const es = {
|
|
|
45
45
|
party: 'Fiesta', chef: 'Chef', viking: 'Vikingo', grad: 'Graduación', beret: 'Boina', bow: 'Lazo', flower: 'Flor', halo: 'Aureola', horns: 'Cuernos',
|
|
46
46
|
antenna: 'Antenas', bunny: 'Orejas de conejo', catears: 'Orejas de gato', santa: 'Papá Noel', hardhat: 'Casco de obra', headband: 'Cinta ninja',
|
|
47
47
|
bandana: 'Pañuelo pirata', helmet: 'Casco espacial', headphones: 'Auriculares', hair: 'Tupé', sprout: 'Brote',
|
|
48
|
+
fedora: 'Fedora', bowler: 'Bombín', propeller: 'Gorra de hélice', witch: 'Sombrero de bruja', fez: 'Fez', sombrero: 'Sombrero mexicano', bucket: 'Gorro pescador', jester: 'Bufón',
|
|
48
49
|
glasses: 'Gafas redondas', square: 'Gafas cuadradas', nerd: 'Gafas empollón', sunglasses: 'Gafas de sol', aviator: 'Aviador', heartglasses: 'Gafas corazón',
|
|
49
50
|
starglasses: 'Gafas estrella', '3d': 'Gafas 3D', monocle: 'Monóculo', eyepatch: 'Parche', goggles: 'Gafas de piloto', visor: 'Visor',
|
|
50
51
|
mustache: 'Bigote', handlebar: 'Bigote manillar', beard: 'Barba', wizardbeard: 'Barba de mago', goatee: 'Perilla', pipe: 'Pipa', lollipop: 'Piruleta',
|
|
@@ -73,7 +74,7 @@ const es = {
|
|
|
73
74
|
},
|
|
74
75
|
},
|
|
75
76
|
editor: {
|
|
76
|
-
kinds: { label: 'Tipo de avatar', character: 'Personaje 2D', plush: 'Personaje 3D', image: '
|
|
77
|
+
kinds: { label: 'Tipo de avatar', character: 'Personaje 2D', plush: 'Personaje 3D', image: 'Tarjeta' },
|
|
77
78
|
tabs: { styles: 'Estilos', shape: 'Forma', color: 'Color y acabado', eyes: 'Ojos', mouth: 'Boca', face: 'Cara', acc: 'Accesorios' },
|
|
78
79
|
controls: 'Controles de edición',
|
|
79
80
|
preview: 'Vista previa del avatar',
|
|
@@ -214,6 +215,7 @@ const en = {
|
|
|
214
215
|
party: 'Party', chef: 'Chef', viking: 'Viking', grad: 'Graduation', beret: 'Beret', bow: 'Bow', flower: 'Flower', halo: 'Halo', horns: 'Horns',
|
|
215
216
|
antenna: 'Antennae', bunny: 'Bunny ears', catears: 'Cat ears', santa: 'Santa', hardhat: 'Hard hat', headband: 'Ninja headband',
|
|
216
217
|
bandana: 'Pirate bandana', helmet: 'Space helmet', headphones: 'Headphones', hair: 'Quiff', sprout: 'Sprout',
|
|
218
|
+
fedora: 'Fedora', bowler: 'Bowler hat', propeller: 'Propeller cap', witch: 'Witch hat', fez: 'Fez', sombrero: 'Sombrero', bucket: 'Bucket hat', jester: 'Jester hat',
|
|
217
219
|
glasses: 'Round glasses', square: 'Square glasses', nerd: 'Nerd glasses', sunglasses: 'Sunglasses', aviator: 'Aviator', heartglasses: 'Heart glasses',
|
|
218
220
|
starglasses: 'Star glasses', '3d': '3D glasses', monocle: 'Monocle', eyepatch: 'Eye patch', goggles: 'Pilot goggles', visor: 'Visor',
|
|
219
221
|
mustache: 'Mustache', handlebar: 'Handlebar mustache', beard: 'Beard', wizardbeard: 'Wizard beard', goatee: 'Goatee', pipe: 'Pipe', lollipop: 'Lollipop',
|
|
@@ -242,7 +244,7 @@ const en = {
|
|
|
242
244
|
},
|
|
243
245
|
},
|
|
244
246
|
editor: {
|
|
245
|
-
kinds: { label: 'Avatar type', character: '2D character', plush: '3D character', image: '
|
|
247
|
+
kinds: { label: 'Avatar type', character: '2D character', plush: '3D character', image: 'Card' },
|
|
246
248
|
tabs: { styles: 'Styles', shape: 'Shape', color: 'Color & finish', eyes: 'Eyes', mouth: 'Mouth', face: 'Face', acc: 'Accessories' },
|
|
247
249
|
controls: 'Editing controls',
|
|
248
250
|
preview: 'Avatar preview',
|
|
@@ -383,6 +385,7 @@ const pt = {
|
|
|
383
385
|
party: 'Festa', chef: 'Chef', viking: 'Viking', grad: 'Formatura', beret: 'Boina', bow: 'Laço', flower: 'Flor', halo: 'Auréola', horns: 'Chifres',
|
|
384
386
|
antenna: 'Antenas', bunny: 'Orelhas de coelho', catears: 'Orelhas de gato', santa: 'Papai Noel', hardhat: 'Capacete de obra', headband: 'Faixa ninja',
|
|
385
387
|
bandana: 'Bandana pirata', helmet: 'Capacete espacial', headphones: 'Fones de ouvido', hair: 'Topete', sprout: 'Broto',
|
|
388
|
+
fedora: 'Fedora', bowler: 'Chapéu-coco', propeller: 'Boné de hélice', witch: 'Chapéu de bruxa', fez: 'Fez', sombrero: 'Sombreiro mexicano', bucket: 'Chapéu de pescador', jester: 'Chapéu de bobo da corte',
|
|
386
389
|
glasses: 'Óculos redondos', square: 'Óculos quadrados', nerd: 'Óculos de nerd', sunglasses: 'Óculos de sol', aviator: 'Aviador', heartglasses: 'Óculos de coração',
|
|
387
390
|
starglasses: 'Óculos de estrela', '3d': 'Óculos 3D', monocle: 'Monóculo', eyepatch: 'Tapa-olho', goggles: 'Óculos de piloto', visor: 'Viseira',
|
|
388
391
|
mustache: 'Bigode', handlebar: 'Bigode guidão', beard: 'Barba', wizardbeard: 'Barba de mago', goatee: 'Cavanhaque', pipe: 'Cachimbo', lollipop: 'Pirulito',
|
|
@@ -411,7 +414,7 @@ const pt = {
|
|
|
411
414
|
},
|
|
412
415
|
},
|
|
413
416
|
editor: {
|
|
414
|
-
kinds: { label: 'Tipo de avatar', character: 'Personagem 2D', plush: 'Personagem 3D', image: '
|
|
417
|
+
kinds: { label: 'Tipo de avatar', character: 'Personagem 2D', plush: 'Personagem 3D', image: 'Cartão' },
|
|
415
418
|
tabs: { styles: 'Estilos', shape: 'Forma', color: 'Cor e acabamento', eyes: 'Olhos', mouth: 'Boca', face: 'Rosto', acc: 'Acessórios' },
|
|
416
419
|
controls: 'Controles de edição',
|
|
417
420
|
preview: 'Prévia do avatar',
|
|
@@ -73,7 +73,7 @@ const es = {
|
|
|
73
73
|
dailyLimit: 'Este ot ya mandó {max} tareas hoy (su límite diario)',
|
|
74
74
|
busy: 'Ya hay una tarea en marcha ({id}); espera a que acabe',
|
|
75
75
|
noAgent: 'Ese proyecto de Orquesta no tiene agente: configura uno en Orquesta ({url})',
|
|
76
|
-
noProject: 'Ese proyecto de Orquesta no existe o no
|
|
76
|
+
noProject: 'Ese proyecto de Orquesta no existe o no está compartido con 7ots',
|
|
77
77
|
rateLimited: 'Orquesta está recibiendo muchas peticiones; espera un momento',
|
|
78
78
|
submitFailed: 'Orquesta no aceptó la tarea (error {status})',
|
|
79
79
|
agentOffline: 'El agente de este proyecto está desconectado: la tarea queda en cola y empezará cuando vuelva',
|
|
@@ -83,6 +83,25 @@ const es = {
|
|
|
83
83
|
badAllow: 'No se entiende: {list}. Escribe @handles de apuchat o correos, sin comodines.',
|
|
84
84
|
pickProject: 'Elige un proyecto antes de activar las tareas',
|
|
85
85
|
},
|
|
86
|
+
vm: {
|
|
87
|
+
badCode: 'Ese código de enrolamiento no es válido o ya caducó; genera otro en 7ots.com',
|
|
88
|
+
revoked: 'Este servidor ya no está autorizado para este ot; vuelve a enrolarlo',
|
|
89
|
+
noVm: 'Este ot no tiene un servidor enrolado; agrégalo en 7ots.com → el ot → Asistente',
|
|
90
|
+
badKind: 'Tipo de trabajo desconocido',
|
|
91
|
+
kindOff: 'Este ot no acepta trabajos de tipo {kind}',
|
|
92
|
+
empty: 'El trabajo está vacío',
|
|
93
|
+
busy: 'Ya hay un trabajo en marcha ({id}); espera a que acabe',
|
|
94
|
+
dailyLimit: 'Este ot ya hizo {max} trabajos hoy (su límite diario)',
|
|
95
|
+
jobGone: 'Ese trabajo ya no existe o ya terminó',
|
|
96
|
+
llmLimit: 'Este trabajo ya usó sus {max} llamadas al modelo',
|
|
97
|
+
paidSoon: 'Los servidores de 7ots llegan pronto; mientras tanto usa tu propio servidor',
|
|
98
|
+
paidOff: 'Los servidores de 7ots aún no están activados para tu cuenta; mientras tanto usa tu propio servidor',
|
|
99
|
+
paidUnavailable: 'Los servidores de 7ots no están disponibles ahora mismo; inténtalo más tarde',
|
|
100
|
+
exists: 'Este ot ya tiene un servidor; elimínalo o revócalo antes de crear otro',
|
|
101
|
+
accountLimit: 'Tu cuenta ya tiene {max} servidor(es) de 7ots (su límite)',
|
|
102
|
+
full: 'No quedan servidores de 7ots disponibles por ahora; inténtalo más tarde',
|
|
103
|
+
badSize: 'Tamaño de servidor no disponible',
|
|
104
|
+
},
|
|
86
105
|
},
|
|
87
106
|
};
|
|
88
107
|
|
|
@@ -153,7 +172,7 @@ const en = {
|
|
|
153
172
|
dailyLimit: 'This ot already sent {max} tasks today (its daily limit)',
|
|
154
173
|
busy: 'A task is already running ({id}); wait for it to finish',
|
|
155
174
|
noAgent: 'That Orquesta project has no agent: set up an agent in Orquesta ({url})',
|
|
156
|
-
noProject: 'That Orquesta project does not exist or
|
|
175
|
+
noProject: 'That Orquesta project does not exist or is not shared with 7ots',
|
|
157
176
|
rateLimited: 'Orquesta is getting too many requests; wait a moment',
|
|
158
177
|
submitFailed: 'Orquesta did not accept the task (error {status})',
|
|
159
178
|
agentOffline: "This project's agent is offline: the task is queued and will start when it is back",
|
|
@@ -163,6 +182,25 @@ const en = {
|
|
|
163
182
|
badAllow: 'Not understood: {list}. Use apuchat @handles or email addresses, no wildcards.',
|
|
164
183
|
pickProject: 'Pick a project before enabling tasks',
|
|
165
184
|
},
|
|
185
|
+
vm: {
|
|
186
|
+
badCode: 'That enrollment code is not valid or has expired; make a new one on 7ots.com',
|
|
187
|
+
revoked: 'This server is no longer authorized for this ot; enroll it again',
|
|
188
|
+
noVm: 'This ot has no enrolled server; add one on 7ots.com → the ot → Assistant',
|
|
189
|
+
badKind: 'Unknown job kind',
|
|
190
|
+
kindOff: 'This ot does not take {kind} jobs',
|
|
191
|
+
empty: 'The job is empty',
|
|
192
|
+
busy: 'A job is already running ({id}); wait for it to finish',
|
|
193
|
+
dailyLimit: 'This ot already ran {max} jobs today (its daily limit)',
|
|
194
|
+
jobGone: 'That job no longer exists or already finished',
|
|
195
|
+
llmLimit: 'This job already used its {max} model calls',
|
|
196
|
+
paidSoon: '7ots servers are coming soon; meanwhile use your own server',
|
|
197
|
+
paidOff: '7ots servers are not enabled for your account yet; meanwhile use your own server',
|
|
198
|
+
paidUnavailable: '7ots servers are not available right now; try again later',
|
|
199
|
+
exists: 'This ot already has a server; delete or revoke it before creating another',
|
|
200
|
+
accountLimit: 'Your account already has {max} 7ots server(s) (its limit)',
|
|
201
|
+
full: 'No 7ots servers are available right now; try again later',
|
|
202
|
+
badSize: 'That server size is not available',
|
|
203
|
+
},
|
|
166
204
|
},
|
|
167
205
|
};
|
|
168
206
|
|
|
@@ -233,7 +271,7 @@ const pt = {
|
|
|
233
271
|
dailyLimit: 'Este ot já enviou {max} tarefas hoje (seu limite diário)',
|
|
234
272
|
busy: 'Já há uma tarefa em andamento ({id}); espere ela terminar',
|
|
235
273
|
noAgent: 'Esse projeto do Orquesta não tem agente: configure um agente no Orquesta ({url})',
|
|
236
|
-
noProject: 'Esse projeto do Orquesta não existe ou
|
|
274
|
+
noProject: 'Esse projeto do Orquesta não existe ou não está compartilhado com o 7ots',
|
|
237
275
|
rateLimited: 'O Orquesta está recebendo muitas solicitações; espere um momento',
|
|
238
276
|
submitFailed: 'O Orquesta não aceitou a tarefa (erro {status})',
|
|
239
277
|
agentOffline: 'O agente deste projeto está desconectado: a tarefa fica na fila e começa quando ele voltar',
|
|
@@ -243,6 +281,25 @@ const pt = {
|
|
|
243
281
|
badAllow: 'Não entendi: {list}. Use @handles do apuchat ou e-mails, sem curingas.',
|
|
244
282
|
pickProject: 'Escolha um projeto antes de ativar as tarefas',
|
|
245
283
|
},
|
|
284
|
+
vm: {
|
|
285
|
+
badCode: 'Esse código de registro não é válido ou expirou; gere outro no 7ots.com',
|
|
286
|
+
revoked: 'Este servidor não está mais autorizado para este ot; registre-o de novo',
|
|
287
|
+
noVm: 'Este ot não tem servidor registrado; adicione um no 7ots.com → o ot → Assistente',
|
|
288
|
+
badKind: 'Tipo de trabalho desconhecido',
|
|
289
|
+
kindOff: 'Este ot não aceita trabalhos do tipo {kind}',
|
|
290
|
+
empty: 'O trabalho está vazio',
|
|
291
|
+
busy: 'Já há um trabalho em andamento ({id}); espere ele terminar',
|
|
292
|
+
dailyLimit: 'Este ot já fez {max} trabalhos hoje (seu limite diário)',
|
|
293
|
+
jobGone: 'Esse trabalho não existe mais ou já terminou',
|
|
294
|
+
llmLimit: 'Este trabalho já usou suas {max} chamadas ao modelo',
|
|
295
|
+
paidSoon: 'Os servidores do 7ots chegam em breve; enquanto isso use seu próprio servidor',
|
|
296
|
+
paidOff: 'Os servidores do 7ots ainda não estão ativados para sua conta; enquanto isso use seu próprio servidor',
|
|
297
|
+
paidUnavailable: 'Os servidores do 7ots não estão disponíveis agora; tente mais tarde',
|
|
298
|
+
exists: 'Este ot já tem um servidor; exclua ou revogue antes de criar outro',
|
|
299
|
+
accountLimit: 'Sua conta já tem {max} servidor(es) do 7ots (seu limite)',
|
|
300
|
+
full: 'Não há servidores do 7ots disponíveis agora; tente mais tarde',
|
|
301
|
+
badSize: 'Esse tamanho de servidor não está disponível',
|
|
302
|
+
},
|
|
246
303
|
},
|
|
247
304
|
};
|
|
248
305
|
|