ugly-app 0.1.203 → 0.1.205
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/dist/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/server/ai/providers/UglyBotImageGenProvider.d.ts.map +1 -1
- package/dist/server/ai/providers/UglyBotImageGenProvider.js +3 -0
- package/dist/server/ai/providers/UglyBotImageGenProvider.js.map +1 -1
- package/dist/server/ai/providers/UglyBotTextGenProvider.d.ts.map +1 -1
- package/dist/server/ai/providers/UglyBotTextGenProvider.js +5 -2
- package/dist/server/ai/providers/UglyBotTextGenProvider.js.map +1 -1
- package/dist/vite/index.d.ts +1 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +1 -0
- package/dist/vite/index.js.map +1 -1
- package/dist/vite/tunnelPlugin.d.ts +14 -0
- package/dist/vite/tunnelPlugin.d.ts.map +1 -0
- package/dist/vite/tunnelPlugin.js +221 -0
- package/dist/vite/tunnelPlugin.js.map +1 -0
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
- package/src/server/ai/providers/UglyBotImageGenProvider.ts +8 -1
- package/src/server/ai/providers/UglyBotTextGenProvider.ts +8 -3
- package/src/vite/index.ts +1 -0
- package/src/vite/tunnelPlugin.ts +285 -0
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.205";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UglyBotImageGenProvider.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/UglyBotImageGenProvider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"UglyBotImageGenProvider.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/UglyBotImageGenProvider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiDrE,eAAO,MAAM,uBAAuB,EAAE,gBAkBrC,CAAC"}
|
|
@@ -24,6 +24,9 @@ async function callUglyBotRequest(op, input, userId) {
|
|
|
24
24
|
throw new Error(`[UglyBot] ${op} failed: ${res.status} ${body}`);
|
|
25
25
|
}
|
|
26
26
|
const data = (await res.json());
|
|
27
|
+
// ugly.bot returns { url } for image gen
|
|
28
|
+
if (data.url)
|
|
29
|
+
return data.url;
|
|
27
30
|
return data.result;
|
|
28
31
|
}
|
|
29
32
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UglyBotImageGenProvider.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/UglyBotImageGenProvider.ts"],"names":[],"mappings":"AAGA,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAExC,KAAK,UAAU,kBAAkB,CAC/B,EAAU,EACV,KAAc,EACd,MAAe;IAEf,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC5C,IAAI,CAAC,QAAQ;QACX,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;IAEJ,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,YAAY,UAAU,EAAE;YACjD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,QAAQ,EAAE;gBACrC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,EAAE;gBACF,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAI,KAAgB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK;gBACxD,SAAS,EAAE,QAAQ;aACpB,CAAC;YACF,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,YAAY,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"UglyBotImageGenProvider.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/UglyBotImageGenProvider.ts"],"names":[],"mappings":"AAGA,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAExC,KAAK,UAAU,kBAAkB,CAC/B,EAAU,EACV,KAAc,EACd,MAAe;IAEf,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC5C,IAAI,CAAC,QAAQ;QACX,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;IAEJ,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,YAAY,UAAU,EAAE;YACjD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,QAAQ,EAAE;gBACrC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,EAAE;gBACF,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAI,KAAgB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK;gBACxD,SAAS,EAAE,QAAQ;aACpB,CAAC;YACF,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,YAAY,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAK7B,CAAC;QACF,yCAAyC;QACzC,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC,GAAG,CAAC;QAC9B,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAqB;IACvD,IAAI,EAAE,SAAS;IACf,SAAS,EAAE,gBAAgB;IAE3B,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,OAAyB;QACtD,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,YAAY,CAAC;QAC7C,OAAO,kBAAkB,CACvB,UAAU,EACV;YACE,KAAK;YACL,MAAM;YACN,OAAO,EAAE;gBACP,WAAW,EAAE,OAAO,EAAE,WAAW;aAClC;SACF,EACD,OAAO,EAAE,MAAM,CACG,CAAC;IACvB,CAAC;CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UglyBotTextGenProvider.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/UglyBotTextGenProvider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,eAAe,EAGhB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"UglyBotTextGenProvider.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/UglyBotTextGenProvider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,eAAe,EAGhB,MAAM,aAAa,CAAC;AAwDrB,eAAO,MAAM,sBAAsB,EAAE,eAwEpC,CAAC"}
|
|
@@ -32,6 +32,9 @@ async function callUglyBotRequest(op, input, userId) {
|
|
|
32
32
|
throw new Error(`${res.status}: ${body}`);
|
|
33
33
|
}
|
|
34
34
|
const data = (await res.json());
|
|
35
|
+
// ugly.bot returns { message: { role, content } } for text gen
|
|
36
|
+
if (data.message?.content !== undefined)
|
|
37
|
+
return data.message.content;
|
|
35
38
|
return data.result;
|
|
36
39
|
}
|
|
37
40
|
finally {
|
|
@@ -47,8 +50,8 @@ export const uglyBotTextGenProvider = {
|
|
|
47
50
|
vision: false,
|
|
48
51
|
maxContextTokens: 128_000,
|
|
49
52
|
},
|
|
50
|
-
costPerMillionInputTokens: 0,
|
|
51
|
-
costPerMillionOutputTokens: 0,
|
|
53
|
+
costPerMillionInputTokens: 0.10,
|
|
54
|
+
costPerMillionOutputTokens: 0.30,
|
|
52
55
|
async generate(messages, options) {
|
|
53
56
|
const model = options?.model ?? 'llama_4_scout';
|
|
54
57
|
return callUglyBotRequest('textGen', {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UglyBotTextGenProvider.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/UglyBotTextGenProvider.ts"],"names":[],"mappings":"AAUA,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAExC,KAAK,UAAU,kBAAkB,CAC/B,EAAU,EACV,KAAc,EACd,MAAe;IAEf,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC5C,IAAI,CAAC,QAAQ;QACX,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;IAEJ,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,YAAY,UAAU,EAAE;YACjD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,QAAQ,EAAE;gBACrC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,EAAE;gBACF,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAI,KAAgB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK;gBACxD,SAAS,EAAE,QAAQ;aACpB,CAAC;YACF,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvB,OAAO,CAAC,KAAK,CACX,iCAAiC,EAAE,uCAAuC;oBAC1E,6BAA6B;oBAC7B,WAAW,IAAI,EAAE,CAClB,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"UglyBotTextGenProvider.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/UglyBotTextGenProvider.ts"],"names":[],"mappings":"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"}
|
package/dist/vite/index.d.ts
CHANGED
package/dist/vite/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/vite/index.js
CHANGED
package/dist/vite/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
interface TunnelPluginOptions {
|
|
3
|
+
/** The ugly.bot server URL. Defaults to 'wss://ugly.bot'. */
|
|
4
|
+
serverUrl?: string;
|
|
5
|
+
/** Project ID. If omitted, read from package.json name field. */
|
|
6
|
+
projectId?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Vite plugin that auto-connects a dev tunnel to ugly.bot on startup.
|
|
10
|
+
* Relays visitor HTTP requests and WebSocket connections to the local dev server.
|
|
11
|
+
*/
|
|
12
|
+
export declare function tunnelPlugin(options?: TunnelPluginOptions): Plugin;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=tunnelPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnelPlugin.d.ts","sourceRoot":"","sources":["../../src/vite/tunnelPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAMlD,UAAU,mBAAmB;IAC3B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CA2QtE"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import WebSocket from 'ws';
|
|
5
|
+
/**
|
|
6
|
+
* Vite plugin that auto-connects a dev tunnel to ugly.bot on startup.
|
|
7
|
+
* Relays visitor HTTP requests and WebSocket connections to the local dev server.
|
|
8
|
+
*/
|
|
9
|
+
export function tunnelPlugin(options = {}) {
|
|
10
|
+
let server;
|
|
11
|
+
let ws = null;
|
|
12
|
+
let reconnectTimer = null;
|
|
13
|
+
let reconnectDelay = 1000;
|
|
14
|
+
let registered = false;
|
|
15
|
+
let subdomain = '';
|
|
16
|
+
function getAuthToken() {
|
|
17
|
+
try {
|
|
18
|
+
const authPath = join(homedir(), '.ugly-bot', 'auth.json');
|
|
19
|
+
const auth = JSON.parse(readFileSync(authPath, 'utf-8'));
|
|
20
|
+
return auth.token ?? null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function getProjectId() {
|
|
27
|
+
if (options.projectId)
|
|
28
|
+
return options.projectId;
|
|
29
|
+
try {
|
|
30
|
+
const pkgPath = join(process.cwd(), 'package.json');
|
|
31
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
32
|
+
return (pkg.name ?? 'unknown')
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
35
|
+
.slice(0, 30);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return 'unknown';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function connect() {
|
|
42
|
+
const token = getAuthToken();
|
|
43
|
+
if (!token) {
|
|
44
|
+
server.config.logger.warn('\x1b[33m ➜ Tunnel: not connected (run `npx ugly-app login` first)\x1b[0m');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const serverUrl = options.serverUrl ?? 'wss://ugly.bot';
|
|
48
|
+
try {
|
|
49
|
+
ws = new WebSocket(`${serverUrl}/proxy-tunnel`, {
|
|
50
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
scheduleReconnect();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
ws.on('open', () => {
|
|
58
|
+
reconnectDelay = 1000; // Reset backoff on successful connection
|
|
59
|
+
ws.send(JSON.stringify({ type: 'register', projectId: getProjectId() }));
|
|
60
|
+
});
|
|
61
|
+
ws.on('message', (rawData) => {
|
|
62
|
+
try {
|
|
63
|
+
const msg = JSON.parse(rawData.toString());
|
|
64
|
+
if (msg.type === 'registered') {
|
|
65
|
+
registered = true;
|
|
66
|
+
subdomain = msg.subdomain;
|
|
67
|
+
server.config.logger.info(`\x1b[36m ➜ Tunnel: https://${subdomain}.ugly.bot\x1b[0m`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (msg.type === 'http-request') {
|
|
71
|
+
handleHttpRequest(msg);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (msg.type === 'ws-open') {
|
|
75
|
+
handleWsOpen(msg);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (msg.type === 'ws-frame') {
|
|
79
|
+
handleWsFrame(msg);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (msg.type === 'ws-close') {
|
|
83
|
+
handleWsClose(msg);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Ignore malformed messages
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
ws.on('close', (code) => {
|
|
92
|
+
registered = false;
|
|
93
|
+
if (code === 4001) {
|
|
94
|
+
server.config.logger.warn('\x1b[33m ➜ Tunnel: replaced by another session\x1b[0m');
|
|
95
|
+
}
|
|
96
|
+
scheduleReconnect();
|
|
97
|
+
});
|
|
98
|
+
ws.on('error', () => {
|
|
99
|
+
// error event is always followed by close, reconnect handled there
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function scheduleReconnect() {
|
|
103
|
+
if (reconnectTimer)
|
|
104
|
+
return;
|
|
105
|
+
reconnectTimer = setTimeout(() => {
|
|
106
|
+
reconnectTimer = null;
|
|
107
|
+
reconnectDelay = Math.min(reconnectDelay * 2, 30_000);
|
|
108
|
+
connect();
|
|
109
|
+
}, reconnectDelay);
|
|
110
|
+
}
|
|
111
|
+
// --- Local WebSocket connections for WS relay ---
|
|
112
|
+
const localWsSockets = new Map();
|
|
113
|
+
function handleHttpRequest(msg) {
|
|
114
|
+
const port = server.config.server.port ?? 5173;
|
|
115
|
+
const url = `http://localhost:${port}${msg.url}`;
|
|
116
|
+
const headers = { ...msg.headers };
|
|
117
|
+
// Remove host header — let fetch set it for localhost
|
|
118
|
+
delete headers['host'];
|
|
119
|
+
const fetchOptions = {
|
|
120
|
+
method: msg.method,
|
|
121
|
+
headers,
|
|
122
|
+
body: msg.body ? Buffer.from(msg.body, 'base64') : undefined,
|
|
123
|
+
};
|
|
124
|
+
fetch(url, fetchOptions)
|
|
125
|
+
.then(async (res) => {
|
|
126
|
+
const responseHeaders = {};
|
|
127
|
+
res.headers.forEach((value, key) => {
|
|
128
|
+
responseHeaders[key] = value;
|
|
129
|
+
});
|
|
130
|
+
const bodyBuffer = await res.arrayBuffer();
|
|
131
|
+
const body = bodyBuffer.byteLength > 0
|
|
132
|
+
? Buffer.from(bodyBuffer).toString('base64')
|
|
133
|
+
: undefined;
|
|
134
|
+
ws?.send(JSON.stringify({
|
|
135
|
+
type: 'http-response',
|
|
136
|
+
id: msg.id,
|
|
137
|
+
status: res.status,
|
|
138
|
+
headers: responseHeaders,
|
|
139
|
+
body,
|
|
140
|
+
}));
|
|
141
|
+
})
|
|
142
|
+
.catch(() => {
|
|
143
|
+
ws?.send(JSON.stringify({
|
|
144
|
+
type: 'http-response',
|
|
145
|
+
id: msg.id,
|
|
146
|
+
status: 502,
|
|
147
|
+
headers: {},
|
|
148
|
+
body: Buffer.from('Local server error').toString('base64'),
|
|
149
|
+
}));
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function handleWsOpen(msg) {
|
|
153
|
+
const port = server.config.server.port ?? 5173;
|
|
154
|
+
const url = `ws://localhost:${port}${msg.url}`;
|
|
155
|
+
const localWs = new WebSocket(url);
|
|
156
|
+
localWs.on('open', () => {
|
|
157
|
+
localWsSockets.set(msg.id, localWs);
|
|
158
|
+
ws?.send(JSON.stringify({ type: 'ws-open-ok', id: msg.id }));
|
|
159
|
+
});
|
|
160
|
+
localWs.on('error', () => {
|
|
161
|
+
ws?.send(JSON.stringify({ type: 'ws-open-error', id: msg.id, status: 502 }));
|
|
162
|
+
});
|
|
163
|
+
localWs.on('message', (data, isBinary) => {
|
|
164
|
+
const encoded = isBinary
|
|
165
|
+
? data.toString('base64')
|
|
166
|
+
: data.toString();
|
|
167
|
+
ws?.send(JSON.stringify({
|
|
168
|
+
type: 'ws-frame',
|
|
169
|
+
id: msg.id,
|
|
170
|
+
data: encoded,
|
|
171
|
+
binary: isBinary,
|
|
172
|
+
}));
|
|
173
|
+
});
|
|
174
|
+
localWs.on('close', (code) => {
|
|
175
|
+
localWsSockets.delete(msg.id);
|
|
176
|
+
ws?.send(JSON.stringify({ type: 'ws-close', id: msg.id, code }));
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function handleWsFrame(msg) {
|
|
180
|
+
const localWs = localWsSockets.get(msg.id);
|
|
181
|
+
if (!localWs || localWs.readyState !== WebSocket.OPEN)
|
|
182
|
+
return;
|
|
183
|
+
if (msg.binary) {
|
|
184
|
+
localWs.send(Buffer.from(msg.data, 'base64'));
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
localWs.send(msg.data);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function handleWsClose(msg) {
|
|
191
|
+
const localWs = localWsSockets.get(msg.id);
|
|
192
|
+
if (localWs) {
|
|
193
|
+
localWsSockets.delete(msg.id);
|
|
194
|
+
localWs.close(msg.code ?? 1000);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
name: 'ugly-app-tunnel',
|
|
199
|
+
apply: 'serve', // Only runs in dev mode
|
|
200
|
+
configureServer(devServer) {
|
|
201
|
+
server = devServer;
|
|
202
|
+
// Connect after server is listening
|
|
203
|
+
devServer.httpServer?.once('listening', () => {
|
|
204
|
+
connect();
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
buildEnd() {
|
|
208
|
+
if (ws && ws.readyState <= 1) {
|
|
209
|
+
ws.close(1000, 'Dev server stopped');
|
|
210
|
+
}
|
|
211
|
+
if (reconnectTimer) {
|
|
212
|
+
clearTimeout(reconnectTimer);
|
|
213
|
+
}
|
|
214
|
+
for (const localWs of localWsSockets.values()) {
|
|
215
|
+
localWs.close(1000);
|
|
216
|
+
}
|
|
217
|
+
localWsSockets.clear();
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=tunnelPlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnelPlugin.js","sourceRoot":"","sources":["../../src/vite/tunnelPlugin.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.205";
|
|
@@ -34,7 +34,14 @@ async function callUglyBotRequest(
|
|
|
34
34
|
const body = await res.text().catch(() => '');
|
|
35
35
|
throw new Error(`[UglyBot] ${op} failed: ${res.status} ${body}`);
|
|
36
36
|
}
|
|
37
|
-
const data = (await res.json()) as {
|
|
37
|
+
const data = (await res.json()) as {
|
|
38
|
+
result?: unknown;
|
|
39
|
+
url?: string;
|
|
40
|
+
base64?: string;
|
|
41
|
+
mime?: string;
|
|
42
|
+
};
|
|
43
|
+
// ugly.bot returns { url } for image gen
|
|
44
|
+
if (data.url) return data.url;
|
|
38
45
|
return data.result;
|
|
39
46
|
} finally {
|
|
40
47
|
clearTimeout(timer);
|
|
@@ -50,7 +50,12 @@ async function callUglyBotRequest(
|
|
|
50
50
|
}
|
|
51
51
|
throw new Error(`${res.status}: ${body}`);
|
|
52
52
|
}
|
|
53
|
-
const data = (await res.json()) as {
|
|
53
|
+
const data = (await res.json()) as {
|
|
54
|
+
result?: unknown;
|
|
55
|
+
message?: { role: string; content: string };
|
|
56
|
+
};
|
|
57
|
+
// ugly.bot returns { message: { role, content } } for text gen
|
|
58
|
+
if (data.message?.content !== undefined) return data.message.content;
|
|
54
59
|
return data.result;
|
|
55
60
|
} finally {
|
|
56
61
|
clearTimeout(timer);
|
|
@@ -66,8 +71,8 @@ export const uglyBotTextGenProvider: TextGenProvider = {
|
|
|
66
71
|
vision: false,
|
|
67
72
|
maxContextTokens: 128_000,
|
|
68
73
|
},
|
|
69
|
-
costPerMillionInputTokens: 0,
|
|
70
|
-
costPerMillionOutputTokens: 0,
|
|
74
|
+
costPerMillionInputTokens: 0.10,
|
|
75
|
+
costPerMillionOutputTokens: 0.30,
|
|
71
76
|
|
|
72
77
|
async generate(
|
|
73
78
|
messages: Message[],
|
package/src/vite/index.ts
CHANGED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import type { Plugin, ViteDevServer } from 'vite';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import WebSocket from 'ws';
|
|
6
|
+
|
|
7
|
+
interface TunnelPluginOptions {
|
|
8
|
+
/** The ugly.bot server URL. Defaults to 'wss://ugly.bot'. */
|
|
9
|
+
serverUrl?: string;
|
|
10
|
+
/** Project ID. If omitted, read from package.json name field. */
|
|
11
|
+
projectId?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Vite plugin that auto-connects a dev tunnel to ugly.bot on startup.
|
|
16
|
+
* Relays visitor HTTP requests and WebSocket connections to the local dev server.
|
|
17
|
+
*/
|
|
18
|
+
export function tunnelPlugin(options: TunnelPluginOptions = {}): Plugin {
|
|
19
|
+
let server: ViteDevServer;
|
|
20
|
+
let ws: WebSocket | null = null;
|
|
21
|
+
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
22
|
+
let reconnectDelay = 1000;
|
|
23
|
+
let registered = false;
|
|
24
|
+
let subdomain = '';
|
|
25
|
+
|
|
26
|
+
function getAuthToken(): string | null {
|
|
27
|
+
try {
|
|
28
|
+
const authPath = join(homedir(), '.ugly-bot', 'auth.json');
|
|
29
|
+
const auth = JSON.parse(readFileSync(authPath, 'utf-8'));
|
|
30
|
+
return auth.token ?? null;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getProjectId(): string {
|
|
37
|
+
if (options.projectId) return options.projectId;
|
|
38
|
+
try {
|
|
39
|
+
const pkgPath = join(process.cwd(), 'package.json');
|
|
40
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
41
|
+
return (pkg.name ?? 'unknown')
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
44
|
+
.slice(0, 30);
|
|
45
|
+
} catch {
|
|
46
|
+
return 'unknown';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function connect(): void {
|
|
51
|
+
const token = getAuthToken();
|
|
52
|
+
if (!token) {
|
|
53
|
+
server.config.logger.warn(
|
|
54
|
+
'\x1b[33m ➜ Tunnel: not connected (run `npx ugly-app login` first)\x1b[0m',
|
|
55
|
+
);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const serverUrl = options.serverUrl ?? 'wss://ugly.bot';
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
ws = new WebSocket(`${serverUrl}/proxy-tunnel`, {
|
|
63
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
64
|
+
});
|
|
65
|
+
} catch {
|
|
66
|
+
scheduleReconnect();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
ws.on('open', () => {
|
|
71
|
+
reconnectDelay = 1000; // Reset backoff on successful connection
|
|
72
|
+
ws!.send(JSON.stringify({ type: 'register', projectId: getProjectId() }));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
ws.on('message', (rawData) => {
|
|
76
|
+
try {
|
|
77
|
+
const msg = JSON.parse(rawData.toString());
|
|
78
|
+
|
|
79
|
+
if (msg.type === 'registered') {
|
|
80
|
+
registered = true;
|
|
81
|
+
subdomain = msg.subdomain;
|
|
82
|
+
server.config.logger.info(
|
|
83
|
+
`\x1b[36m ➜ Tunnel: https://${subdomain}.ugly.bot\x1b[0m`,
|
|
84
|
+
);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (msg.type === 'http-request') {
|
|
89
|
+
handleHttpRequest(msg);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (msg.type === 'ws-open') {
|
|
94
|
+
handleWsOpen(msg);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (msg.type === 'ws-frame') {
|
|
99
|
+
handleWsFrame(msg);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (msg.type === 'ws-close') {
|
|
104
|
+
handleWsClose(msg);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
// Ignore malformed messages
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
ws.on('close', (code) => {
|
|
113
|
+
registered = false;
|
|
114
|
+
if (code === 4001) {
|
|
115
|
+
server.config.logger.warn(
|
|
116
|
+
'\x1b[33m ➜ Tunnel: replaced by another session\x1b[0m',
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
scheduleReconnect();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
ws.on('error', () => {
|
|
123
|
+
// error event is always followed by close, reconnect handled there
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function scheduleReconnect(): void {
|
|
128
|
+
if (reconnectTimer) return;
|
|
129
|
+
reconnectTimer = setTimeout(() => {
|
|
130
|
+
reconnectTimer = null;
|
|
131
|
+
reconnectDelay = Math.min(reconnectDelay * 2, 30_000);
|
|
132
|
+
connect();
|
|
133
|
+
}, reconnectDelay);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// --- Local WebSocket connections for WS relay ---
|
|
137
|
+
const localWsSockets = new Map<string, WebSocket>();
|
|
138
|
+
|
|
139
|
+
function handleHttpRequest(msg: {
|
|
140
|
+
id: string;
|
|
141
|
+
method: string;
|
|
142
|
+
url: string;
|
|
143
|
+
headers: Record<string, string>;
|
|
144
|
+
body?: string;
|
|
145
|
+
}): void {
|
|
146
|
+
const port = server.config.server.port ?? 5173;
|
|
147
|
+
const url = `http://localhost:${port}${msg.url}`;
|
|
148
|
+
|
|
149
|
+
const headers = { ...msg.headers };
|
|
150
|
+
// Remove host header — let fetch set it for localhost
|
|
151
|
+
delete headers['host'];
|
|
152
|
+
|
|
153
|
+
const fetchOptions: RequestInit = {
|
|
154
|
+
method: msg.method,
|
|
155
|
+
headers,
|
|
156
|
+
body: msg.body ? Buffer.from(msg.body, 'base64') : undefined,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
fetch(url, fetchOptions)
|
|
160
|
+
.then(async (res) => {
|
|
161
|
+
const responseHeaders: Record<string, string> = {};
|
|
162
|
+
res.headers.forEach((value, key) => {
|
|
163
|
+
responseHeaders[key] = value;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const bodyBuffer = await res.arrayBuffer();
|
|
167
|
+
const body =
|
|
168
|
+
bodyBuffer.byteLength > 0
|
|
169
|
+
? Buffer.from(bodyBuffer).toString('base64')
|
|
170
|
+
: undefined;
|
|
171
|
+
|
|
172
|
+
ws?.send(
|
|
173
|
+
JSON.stringify({
|
|
174
|
+
type: 'http-response',
|
|
175
|
+
id: msg.id,
|
|
176
|
+
status: res.status,
|
|
177
|
+
headers: responseHeaders,
|
|
178
|
+
body,
|
|
179
|
+
}),
|
|
180
|
+
);
|
|
181
|
+
})
|
|
182
|
+
.catch(() => {
|
|
183
|
+
ws?.send(
|
|
184
|
+
JSON.stringify({
|
|
185
|
+
type: 'http-response',
|
|
186
|
+
id: msg.id,
|
|
187
|
+
status: 502,
|
|
188
|
+
headers: {},
|
|
189
|
+
body: Buffer.from('Local server error').toString('base64'),
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function handleWsOpen(msg: {
|
|
196
|
+
id: string;
|
|
197
|
+
url: string;
|
|
198
|
+
headers: Record<string, string>;
|
|
199
|
+
}): void {
|
|
200
|
+
const port = server.config.server.port ?? 5173;
|
|
201
|
+
const url = `ws://localhost:${port}${msg.url}`;
|
|
202
|
+
|
|
203
|
+
const localWs = new WebSocket(url);
|
|
204
|
+
|
|
205
|
+
localWs.on('open', () => {
|
|
206
|
+
localWsSockets.set(msg.id, localWs);
|
|
207
|
+
ws?.send(JSON.stringify({ type: 'ws-open-ok', id: msg.id }));
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
localWs.on('error', () => {
|
|
211
|
+
ws?.send(
|
|
212
|
+
JSON.stringify({ type: 'ws-open-error', id: msg.id, status: 502 }),
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
localWs.on('message', (data, isBinary) => {
|
|
217
|
+
const encoded = isBinary
|
|
218
|
+
? (data as Buffer).toString('base64')
|
|
219
|
+
: data.toString();
|
|
220
|
+
ws?.send(
|
|
221
|
+
JSON.stringify({
|
|
222
|
+
type: 'ws-frame',
|
|
223
|
+
id: msg.id,
|
|
224
|
+
data: encoded,
|
|
225
|
+
binary: isBinary,
|
|
226
|
+
}),
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
localWs.on('close', (code) => {
|
|
231
|
+
localWsSockets.delete(msg.id);
|
|
232
|
+
ws?.send(JSON.stringify({ type: 'ws-close', id: msg.id, code }));
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function handleWsFrame(msg: {
|
|
237
|
+
id: string;
|
|
238
|
+
data: string;
|
|
239
|
+
binary: boolean;
|
|
240
|
+
}): void {
|
|
241
|
+
const localWs = localWsSockets.get(msg.id);
|
|
242
|
+
if (!localWs || localWs.readyState !== WebSocket.OPEN) return;
|
|
243
|
+
|
|
244
|
+
if (msg.binary) {
|
|
245
|
+
localWs.send(Buffer.from(msg.data, 'base64'));
|
|
246
|
+
} else {
|
|
247
|
+
localWs.send(msg.data);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function handleWsClose(msg: { id: string; code?: number }): void {
|
|
252
|
+
const localWs = localWsSockets.get(msg.id);
|
|
253
|
+
if (localWs) {
|
|
254
|
+
localWsSockets.delete(msg.id);
|
|
255
|
+
localWs.close(msg.code ?? 1000);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
name: 'ugly-app-tunnel',
|
|
261
|
+
apply: 'serve', // Only runs in dev mode
|
|
262
|
+
|
|
263
|
+
configureServer(devServer) {
|
|
264
|
+
server = devServer;
|
|
265
|
+
|
|
266
|
+
// Connect after server is listening
|
|
267
|
+
devServer.httpServer?.once('listening', () => {
|
|
268
|
+
connect();
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
buildEnd() {
|
|
273
|
+
if (ws && ws.readyState <= 1) {
|
|
274
|
+
ws.close(1000, 'Dev server stopped');
|
|
275
|
+
}
|
|
276
|
+
if (reconnectTimer) {
|
|
277
|
+
clearTimeout(reconnectTimer);
|
|
278
|
+
}
|
|
279
|
+
for (const localWs of localWsSockets.values()) {
|
|
280
|
+
localWs.close(1000);
|
|
281
|
+
}
|
|
282
|
+
localWsSockets.clear();
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|