symmetry-cli 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SymmetryProvider = void 0;
7
+ const node_stream_1 = require("node:stream");
8
+ const promises_1 = require("stream/promises");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const hyperswarm_1 = __importDefault(require("hyperswarm"));
11
+ const hypercore_crypto_1 = __importDefault(require("hypercore-crypto"));
12
+ const node_fs_1 = __importDefault(require("node:fs"));
13
+ const config_1 = require("./config");
14
+ const utils_1 = require("./utils");
15
+ const logger_1 = require("./logger");
16
+ const constants_1 = require("./constants");
17
+ class SymmetryProvider {
18
+ constructor(configPath) {
19
+ this._challenge = null;
20
+ this._conversationIndex = 0;
21
+ this._discoveryKey = null;
22
+ this._isPublic = false;
23
+ this._providerConnections = 0;
24
+ this._providerSwarm = null;
25
+ this._serverPeer = null;
26
+ logger_1.logger.info(`🔗 Initializing client using config file: ${configPath}`);
27
+ this._config = new config_1.ConfigManager(configPath);
28
+ this._isPublic = this._config.get("public");
29
+ }
30
+ async init() {
31
+ this._providerSwarm = new hyperswarm_1.default({
32
+ maxConnections: this._config.get("maxConnections"),
33
+ });
34
+ const keyPair = hypercore_crypto_1.default.keyPair(Buffer.alloc(32).fill(this._config.get("name")));
35
+ this._discoveryKey = hypercore_crypto_1.default.discoveryKey(keyPair.publicKey);
36
+ const discovery = this._providerSwarm.join(this._discoveryKey, {
37
+ server: true,
38
+ client: true,
39
+ });
40
+ await discovery.flushed();
41
+ this._providerSwarm.on("error", (err) => {
42
+ logger_1.logger.error(chalk_1.default.red("🚨 Swarm Error:"), err);
43
+ });
44
+ this._providerSwarm.on("connection", (peer) => {
45
+ logger_1.logger.info(`⚡️ New connection from peer: ${peer.rawStream.remoteHost}`);
46
+ this.listeners(peer);
47
+ });
48
+ logger_1.logger.info(`📁 Symmetry client initialized.`);
49
+ logger_1.logger.info(`🔑 Discovery key: ${this._discoveryKey.toString("hex")}`);
50
+ if (this._isPublic) {
51
+ logger_1.logger.info(chalk_1.default.white(`🔑 Server key: ${this._config.get("serverKey")}`));
52
+ logger_1.logger.info(chalk_1.default.white("🔗 Joining server, please wait."));
53
+ await this.joinServer();
54
+ }
55
+ process.on("SIGINT", async () => {
56
+ var _a;
57
+ await ((_a = this._providerSwarm) === null || _a === void 0 ? void 0 : _a.destroy());
58
+ process.exit(0);
59
+ });
60
+ process.on("uncaughtException", (err) => {
61
+ if (err.message === "connection reset by peer") {
62
+ this._providerConnections = Math.max(0, this._providerConnections - 1);
63
+ }
64
+ });
65
+ }
66
+ async joinServer() {
67
+ const serverSwarm = new hyperswarm_1.default();
68
+ const serverKey = Buffer.from(this._config.get("serverKey"));
69
+ serverSwarm.join(hypercore_crypto_1.default.discoveryKey(serverKey), {
70
+ client: true,
71
+ server: false,
72
+ });
73
+ serverSwarm.flush();
74
+ serverSwarm.on("connection", (peer) => {
75
+ var _a;
76
+ this._serverPeer = peer;
77
+ logger_1.logger.info(chalk_1.default.green("🔗 Connected to server."));
78
+ this._challenge = hypercore_crypto_1.default.randomBytes(32);
79
+ this._serverPeer.write((0, utils_1.createMessage)(constants_1.serverMessageKeys.challenge, {
80
+ challenge: this._challenge,
81
+ }));
82
+ this._serverPeer.write((0, utils_1.createMessage)(constants_1.serverMessageKeys.join, {
83
+ ...this._config.getAll(),
84
+ discoveryKey: (_a = this._discoveryKey) === null || _a === void 0 ? void 0 : _a.toString("hex"),
85
+ }));
86
+ this._serverPeer.on("data", async (buffer) => {
87
+ var _a;
88
+ if (!buffer)
89
+ return;
90
+ const data = (0, utils_1.safeParseJson)(buffer.toString());
91
+ if (data && data.key) {
92
+ switch (data.key) {
93
+ case constants_1.serverMessageKeys.challenge:
94
+ this.handleServerVerification(data.data);
95
+ break;
96
+ case constants_1.serverMessageKeys.ping:
97
+ (_a = this._serverPeer) === null || _a === void 0 ? void 0 : _a.write((0, utils_1.createMessage)(constants_1.serverMessageKeys.pong));
98
+ break;
99
+ }
100
+ }
101
+ });
102
+ });
103
+ }
104
+ getServerPublicKey(serverKeyHex) {
105
+ const publicKey = Buffer.from(serverKeyHex, "hex");
106
+ if (publicKey.length !== 32) {
107
+ throw new Error(`Expected a 32-byte public key, but got ${publicKey.length} bytes`);
108
+ }
109
+ return publicKey;
110
+ }
111
+ handleServerVerification(data) {
112
+ if (!this._challenge) {
113
+ console.log("No challenge set. Cannot verify.");
114
+ return;
115
+ }
116
+ const serverKeyHex = this._config.get("serverKey");
117
+ try {
118
+ const publicKey = this.getServerPublicKey(serverKeyHex);
119
+ const signatureBuffer = Buffer.from(data.signature.data, "base64");
120
+ const verified = hypercore_crypto_1.default.verify(this._challenge, signatureBuffer, publicKey);
121
+ if (verified) {
122
+ logger_1.logger.info(chalk_1.default.greenBright(`✅ Verification successful.`));
123
+ }
124
+ else {
125
+ logger_1.logger.error(`❌ Verification failed!`);
126
+ }
127
+ }
128
+ catch (error) {
129
+ console.error("Error during verification:", error);
130
+ }
131
+ }
132
+ listeners(peer) {
133
+ peer.on("data", async (buffer) => {
134
+ if (!buffer)
135
+ return;
136
+ const data = (0, utils_1.safeParseJson)(buffer.toString());
137
+ if (data && data.key) {
138
+ switch (data.key) {
139
+ case constants_1.serverMessageKeys.newConversation:
140
+ this._conversationIndex = this._conversationIndex + 1;
141
+ break;
142
+ case constants_1.serverMessageKeys.inference:
143
+ logger_1.logger.info(`📦 Inference message received from ${peer.rawStream.remoteHost}`);
144
+ await this.handleInferenceRequest(data, peer);
145
+ break;
146
+ }
147
+ }
148
+ });
149
+ }
150
+ async handleInferenceRequest(data, peer) {
151
+ const emitterKey = data.data.key;
152
+ const req = this.buildStreamRequest(data === null || data === void 0 ? void 0 : data.data.messages);
153
+ if (!req)
154
+ return;
155
+ const { requestOptions, requestBody } = req;
156
+ const { protocol, hostname, port, path, method, headers } = requestOptions;
157
+ const url = `${protocol}://${hostname}:${port}${path}`;
158
+ try {
159
+ const response = await fetch(url, {
160
+ method,
161
+ headers,
162
+ body: JSON.stringify(requestBody),
163
+ });
164
+ if (!response.ok) {
165
+ throw new Error(`Server responded with status code: ${response.status}`);
166
+ }
167
+ if (!response.body) {
168
+ throw new Error("Failed to get a ReadableStream from the response");
169
+ }
170
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
+ const responseStream = node_stream_1.Readable.fromWeb(response.body);
172
+ const peerStream = new node_stream_1.PassThrough();
173
+ responseStream.pipe(peerStream);
174
+ let completion = "";
175
+ const provider = this._config.get("apiProvider");
176
+ peer.write(JSON.stringify({
177
+ symmetryEmitterKey: emitterKey,
178
+ }));
179
+ const peerPipeline = (0, promises_1.pipeline)(peerStream, async function (source) {
180
+ for await (const chunk of source) {
181
+ if (peer.writable) {
182
+ completion += (0, utils_1.getChatDataFromProvider)(provider, (0, utils_1.safeParseStreamResponse)(chunk.toString()));
183
+ const write = peer.write(chunk);
184
+ if (!write) {
185
+ await new Promise((resolve) => peer.once("drain", resolve));
186
+ }
187
+ }
188
+ else {
189
+ break;
190
+ }
191
+ }
192
+ });
193
+ await Promise.resolve(peerPipeline);
194
+ peer.write((0, utils_1.createMessage)(constants_1.serverMessageKeys.inferenceEnded, data === null || data === void 0 ? void 0 : data.data.key));
195
+ if (this._config.get("dataCollectionEnabled") &&
196
+ data.data.key === constants_1.serverMessageKeys.inference) {
197
+ this.saveCompletion(completion, peer, data.data.messages);
198
+ }
199
+ }
200
+ catch (error) {
201
+ let errorMessage = "An error occurred during inference";
202
+ if (error instanceof Error)
203
+ errorMessage = error.message;
204
+ logger_1.logger.error(`🚨 ${errorMessage}`);
205
+ }
206
+ }
207
+ async saveCompletion(completion, peer, messages) {
208
+ node_fs_1.default.writeFile(`${this._config.get("path")}/${peer.publicKey.toString("hex")}-${this._conversationIndex}.json`, JSON.stringify([
209
+ ...messages,
210
+ {
211
+ role: "assistant",
212
+ content: completion,
213
+ },
214
+ ]), () => {
215
+ logger_1.logger.info(`📝 Completion saved to file`);
216
+ });
217
+ }
218
+ buildStreamRequest(messages) {
219
+ const requestOptions = {
220
+ hostname: this._config.get("apiHostname"),
221
+ port: Number(this._config.get("apiPort")),
222
+ path: this._config.get("apiPath"),
223
+ protocol: this._config.get("apiProtocol"),
224
+ method: "POST",
225
+ headers: {
226
+ "Content-Type": "application/json",
227
+ Authorization: `Bearer ${this._config.get("apiKey")}`,
228
+ },
229
+ };
230
+ const requestBody = {
231
+ model: this._config.get("modelName"),
232
+ messages: messages || undefined,
233
+ stream: true,
234
+ };
235
+ return { requestOptions, requestBody };
236
+ }
237
+ }
238
+ exports.SymmetryProvider = SymmetryProvider;
239
+ exports.default = SymmetryProvider;
package/dist/server.js ADDED
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SymmetryServer = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const corestore_1 = __importDefault(require("corestore"));
9
+ const hyperdrive_1 = __importDefault(require("hyperdrive"));
10
+ const hyperswarm_1 = __importDefault(require("hyperswarm"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const config_manager_1 = require("./config-manager");
13
+ const utils_1 = require("./utils");
14
+ const logger_1 = require("./logger");
15
+ const peer_repository_1 = require("./peer-repository");
16
+ const constants_1 = require("./constants");
17
+ const session_manager_1 = require("./session-manager");
18
+ const session_repository_1 = require("./session-repository");
19
+ const websocket_server_1 = require("./websocket-server");
20
+ class SymmetryServer {
21
+ constructor(configPath) {
22
+ logger_1.logger.info(`🔗 Initializing server using config file: ${configPath}`);
23
+ this._config = (0, config_manager_1.createConfigManager)(configPath, false);
24
+ if (!this._config.isServerConfig()) {
25
+ throw new Error("Invalid configuration for server");
26
+ }
27
+ this._peerRepository = new peer_repository_1.PeerRepository();
28
+ this._sessionRepository = new session_repository_1.SessionRepository();
29
+ this._sessionManager = new session_manager_1.SessionManager(this._sessionRepository, 5);
30
+ }
31
+ async init() {
32
+ var _a, _b;
33
+ const corePath = path_1.default.join(this._config.get("path"), "symmetry-core");
34
+ const store = new corestore_1.default(corePath);
35
+ const core = new hyperdrive_1.default(store);
36
+ const swarm = new hyperswarm_1.default();
37
+ await core.ready();
38
+ const discovery = swarm.join(core.discoveryKey, { server: true });
39
+ await discovery.flushed();
40
+ swarm.on("connection", (peer) => {
41
+ logger_1.logger.info(`🔗 New Connection: ${peer.rawStream.remoteHost}`);
42
+ store.replicate(peer);
43
+ this.listeners(peer);
44
+ });
45
+ this._wsServer = new websocket_server_1.WsServer(this._config.get("webSocketPort"), this._peerRepository, swarm);
46
+ logger_1.logger.info(`🔑 Discovery key: ${(_a = core.discoveryKey) === null || _a === void 0 ? void 0 : _a.toString("hex")}`);
47
+ logger_1.logger.info(`🔑 Drive key: ${(_b = core.key) === null || _b === void 0 ? void 0 : _b.toString("hex")}`);
48
+ logger_1.logger.info(chalk_1.default.green(`\u2713 Websocket server started: ws://localhost:${this._config.get("webSocketPort")}`));
49
+ logger_1.logger.info(chalk_1.default.green(`\u2713 Symmetry server started, waiting for connections...`));
50
+ }
51
+ listeners(peer) {
52
+ peer.on("error", (err) => err);
53
+ peer.on("close", () => {
54
+ const peerKey = peer.publicKey.toString("hex");
55
+ this._peerRepository.updateLastSeen(peerKey);
56
+ logger_1.logger.info(`🔗 Connection Closed: Peer ${peerKey.slice(0, 6)}...${peerKey.slice(-6)}`);
57
+ });
58
+ peer.on("data", (message) => {
59
+ const data = (0, utils_1.safeParseJson)(message.toString());
60
+ if (!data)
61
+ return;
62
+ if (data.key) {
63
+ switch (data === null || data === void 0 ? void 0 : data.key) {
64
+ case constants_1.serverMessageKeys.join:
65
+ this.join(peer, data.data);
66
+ break;
67
+ case constants_1.serverMessageKeys.requestProvider:
68
+ this.handlePeerSession(peer, data.data);
69
+ break;
70
+ case constants_1.serverMessageKeys.verifySession:
71
+ this.handlePeerSessionValidation(peer, data.data);
72
+ break;
73
+ }
74
+ }
75
+ });
76
+ }
77
+ async join(peer, message) {
78
+ const peerKey = peer.publicKey.toString("hex");
79
+ try {
80
+ await this._peerRepository.upsert({
81
+ ...message,
82
+ key: peerKey,
83
+ });
84
+ logger_1.logger.info(`👋 Peer provider joined ${peer.rawStream.remoteHost}`);
85
+ peer.write((0, utils_1.createMessage)(constants_1.serverMessageKeys.joinAck, {
86
+ status: "success",
87
+ key: peerKey,
88
+ }));
89
+ }
90
+ catch (error) {
91
+ let errorMessage = "";
92
+ if (error instanceof Error)
93
+ errorMessage = error.message;
94
+ logger_1.logger.error(`🚨 ${errorMessage}`);
95
+ }
96
+ }
97
+ async handlePeerSession(peer, randomPeerRequest) {
98
+ try {
99
+ const providerPeer = await this._peerRepository.getPeer(randomPeerRequest);
100
+ const sessionToken = await this._sessionManager.createSession(providerPeer.discovery_key);
101
+ peer.write((0, utils_1.createMessage)(constants_1.serverMessageKeys.providerDetails, {
102
+ providerId: providerPeer.key,
103
+ sessionToken,
104
+ }));
105
+ }
106
+ catch (error) {
107
+ let errorMessage = "";
108
+ if (error instanceof Error)
109
+ errorMessage = error.message;
110
+ logger_1.logger.error(`🚨 ${errorMessage}`);
111
+ }
112
+ }
113
+ async handlePeerSessionValidation(peer, message) {
114
+ if (!message.sessionToken)
115
+ return;
116
+ try {
117
+ const providerDiscoveryKey = await this._sessionManager.verifySession(message.sessionToken);
118
+ if (!providerDiscoveryKey)
119
+ return;
120
+ const providerPeer = await this._peerRepository.getByDiscoveryKey(providerDiscoveryKey);
121
+ if (!providerPeer)
122
+ return;
123
+ peer.write((0, utils_1.createMessage)(constants_1.serverMessageKeys.sessionValid, {
124
+ discoveryKey: providerPeer.discovery_key,
125
+ }));
126
+ await this._sessionManager.extendSession(message.sessionToken);
127
+ }
128
+ catch (error) {
129
+ logger_1.logger.error(`Session verification error: ${error instanceof Error ? error.message : "Unknown error"}`);
130
+ peer.write((0, utils_1.createMessage)(constants_1.serverMessageKeys.sessionValid, {
131
+ valid: false,
132
+ error: "Error verifying session",
133
+ }));
134
+ }
135
+ }
136
+ async cleanupSessions() {
137
+ await this._sessionManager.cleanupExpiredSessions();
138
+ }
139
+ }
140
+ exports.SymmetryServer = SymmetryServer;
141
+ module.exports = {
142
+ SymmetryServer,
143
+ };
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SessionManager = void 0;
7
+ const logger_1 = require("./logger");
8
+ const crypto_1 = __importDefault(require("crypto"));
9
+ class SessionManager {
10
+ constructor(sessionRepository, sessionDurationMinutes = 60) {
11
+ this.sessionRepository = sessionRepository;
12
+ this.sessionDuration = sessionDurationMinutes * 60 * 1000;
13
+ }
14
+ async createSession(providerId) {
15
+ const sessionId = crypto_1.default.randomUUID();
16
+ const now = new Date();
17
+ const session = {
18
+ id: sessionId,
19
+ providerId,
20
+ createdAt: now,
21
+ expiresAt: new Date(now.getTime() + this.sessionDuration),
22
+ };
23
+ await this.sessionRepository.create(session);
24
+ logger_1.logger.info(`🖇️ Session created for provider: ${providerId}`);
25
+ return sessionId;
26
+ }
27
+ async verifySession(sessionId) {
28
+ const session = await this.sessionRepository.get(sessionId);
29
+ if (!session) {
30
+ logger_1.logger.warning(`❌ Session not found: ${sessionId}`);
31
+ return null;
32
+ }
33
+ if (new Date() > session.expiresAt) {
34
+ logger_1.logger.warning(`🕛 Session expired: ${sessionId}`);
35
+ await this.sessionRepository.delete(sessionId);
36
+ return null;
37
+ }
38
+ return session.providerId;
39
+ }
40
+ async extendSession(sessionId) {
41
+ const session = await this.sessionRepository.get(sessionId);
42
+ if (!session) {
43
+ logger_1.logger.warning(`🚨 Cannot extend non-existent session: ${sessionId}`);
44
+ return false;
45
+ }
46
+ session.expiresAt = new Date(Date.now() + this.sessionDuration);
47
+ await this.sessionRepository.update(session);
48
+ logger_1.logger.info(`🎟️ Session extended: ${sessionId}`);
49
+ return true;
50
+ }
51
+ async deleteSession(sessionId) {
52
+ const result = await this.sessionRepository.delete(sessionId);
53
+ if (result) {
54
+ // cross bin emoji
55
+ logger_1.logger.info(`🗑 Session deleted: ${sessionId}`);
56
+ }
57
+ else {
58
+ logger_1.logger.warning(`🚨 Failed to delete session: ${sessionId}`);
59
+ }
60
+ return result;
61
+ }
62
+ async cleanupExpiredSessions() {
63
+ const deletedCount = await this.sessionRepository.deleteExpired();
64
+ logger_1.logger.info(`🕛 Cleaned up ${deletedCount} expired sessions`);
65
+ return deletedCount;
66
+ }
67
+ }
68
+ exports.SessionManager = SessionManager;
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionRepository = void 0;
4
+ const database_1 = require("./database");
5
+ class SessionRepository {
6
+ constructor() {
7
+ this.create = (session) => {
8
+ return new Promise((resolve, reject) => {
9
+ const sql = `INSERT INTO sessions (id, provider_id, created_at, expires_at) VALUES (?, ?, ?, ?)`;
10
+ this.db.run(sql, [
11
+ session.id,
12
+ session.providerId,
13
+ session.createdAt.toISOString(),
14
+ session.expiresAt.toISOString(),
15
+ ], function (err) {
16
+ if (err) {
17
+ reject(err);
18
+ }
19
+ else {
20
+ resolve();
21
+ }
22
+ });
23
+ });
24
+ };
25
+ this.get = (id) => {
26
+ return new Promise((resolve, reject) => {
27
+ const sql = `
28
+ SELECT s.id, s.provider_id as providerId, s.created_at as createdAt, s.expires_at as expiresAt
29
+ FROM sessions s
30
+ WHERE s.id = ?
31
+ `;
32
+ this.db.get(sql, [id], (err, row) => {
33
+ if (err) {
34
+ reject(err);
35
+ }
36
+ else if (row) {
37
+ resolve({
38
+ id: row.id,
39
+ providerId: row.providerId,
40
+ createdAt: new Date(row.createdAt),
41
+ expiresAt: new Date(row.expiresAt),
42
+ });
43
+ }
44
+ else {
45
+ resolve(null);
46
+ }
47
+ });
48
+ });
49
+ };
50
+ this.update = (session) => {
51
+ return new Promise((resolve, reject) => {
52
+ const sql = `UPDATE sessions SET provider_id = ?, created_at = ?, expires_at = ? WHERE id = ?`;
53
+ this.db.run(sql, [
54
+ session.providerId,
55
+ session.createdAt.toISOString(),
56
+ session.expiresAt.toISOString(),
57
+ session.id,
58
+ ], function (err) {
59
+ if (err) {
60
+ reject(err);
61
+ }
62
+ else if (this.changes === 0) {
63
+ reject(new Error("No session found with the provided id"));
64
+ }
65
+ else {
66
+ resolve();
67
+ }
68
+ });
69
+ });
70
+ };
71
+ this.delete = (id) => {
72
+ return new Promise((resolve, reject) => {
73
+ const sql = `DELETE FROM sessions WHERE id = ?`;
74
+ this.db.run(sql, [id], function (err) {
75
+ if (err) {
76
+ reject(err);
77
+ }
78
+ else {
79
+ resolve(this.changes > 0);
80
+ }
81
+ });
82
+ });
83
+ };
84
+ this.getAllActiveSessions = () => {
85
+ return new Promise((resolve, reject) => {
86
+ const sql = `
87
+ SELECT s.id, s.provider_id as providerId, s.created_at as createdAt, s.expires_at as expiresAt,
88
+ p.key as peer_key, p.discovery_key, p.model_name
89
+ FROM sessions s
90
+ LEFT JOIN peers p ON s.provider_id = p.id
91
+ WHERE s.expires_at > datetime('now')
92
+ `;
93
+ this.db.all(sql, [], (err, rows) => {
94
+ if (err) {
95
+ reject(err);
96
+ }
97
+ else {
98
+ resolve(rows.map((row) => ({
99
+ id: row.id,
100
+ providerId: row.providerId,
101
+ createdAt: new Date(row.createdAt),
102
+ expiresAt: new Date(row.expiresAt),
103
+ peer_key: row.peer_key,
104
+ discovery_key: row.discovery_key,
105
+ model_name: row.model_name,
106
+ })));
107
+ }
108
+ });
109
+ });
110
+ };
111
+ this.deleteExpired = () => {
112
+ return new Promise((resolve, reject) => {
113
+ const sql = `DELETE FROM sessions WHERE expires_at <= datetime('now')`;
114
+ this.db.run(sql, function (err) {
115
+ if (err) {
116
+ reject(err);
117
+ }
118
+ else {
119
+ resolve(this.changes);
120
+ }
121
+ });
122
+ });
123
+ };
124
+ this.db = database_1.database;
125
+ }
126
+ }
127
+ exports.SessionRepository = SessionRepository;
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const provider_1 = require("./provider");
11
+ const program = new commander_1.Command();
12
+ program
13
+ .version("1.0.0")
14
+ .description("symmetry cli")
15
+ .option("-c, --config <path>", "Path to config file", path_1.default.join(os_1.default.homedir(), ".config", "symmetry", "provider.yaml"))
16
+ .action(async () => {
17
+ const client = new provider_1.SymmetryProvider(program.opts().config);
18
+ await client.init();
19
+ });
20
+ program.parse(process.argv);
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/utils.js ADDED
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getChatDataFromProvider = void 0;
4
+ exports.safeParseJson = safeParseJson;
5
+ exports.createMessage = createMessage;
6
+ exports.isStreamWithDataPrefix = isStreamWithDataPrefix;
7
+ exports.safeParseStreamResponse = safeParseStreamResponse;
8
+ const constants_1 = require("./constants");
9
+ function safeParseJson(data) {
10
+ try {
11
+ return JSON.parse(data);
12
+ }
13
+ catch (e) {
14
+ return undefined;
15
+ }
16
+ }
17
+ function createMessage(key, data) {
18
+ return JSON.stringify({ key, data });
19
+ }
20
+ function isStreamWithDataPrefix(stringBuffer) {
21
+ return stringBuffer.startsWith('data:');
22
+ }
23
+ function safeParseStreamResponse(stringBuffer) {
24
+ try {
25
+ if (isStreamWithDataPrefix(stringBuffer)) {
26
+ return JSON.parse(stringBuffer.split('data:')[1]);
27
+ }
28
+ return JSON.parse(stringBuffer);
29
+ }
30
+ catch (e) {
31
+ return undefined;
32
+ }
33
+ }
34
+ const getChatDataFromProvider = (provider, data) => {
35
+ var _a, _b;
36
+ switch (provider) {
37
+ case constants_1.apiProviders.Ollama:
38
+ case constants_1.apiProviders.OpenWebUI:
39
+ return ((_a = data === null || data === void 0 ? void 0 : data.choices[0].delta) === null || _a === void 0 ? void 0 : _a.content)
40
+ ? data === null || data === void 0 ? void 0 : data.choices[0].delta.content
41
+ : '';
42
+ case constants_1.apiProviders.LlamaCpp:
43
+ return data === null || data === void 0 ? void 0 : data.content;
44
+ case constants_1.apiProviders.LiteLLM:
45
+ default:
46
+ if ((data === null || data === void 0 ? void 0 : data.choices[0].delta.content) === 'undefined')
47
+ return '';
48
+ return ((_b = data === null || data === void 0 ? void 0 : data.choices[0].delta) === null || _b === void 0 ? void 0 : _b.content)
49
+ ? data === null || data === void 0 ? void 0 : data.choices[0].delta.content
50
+ : '';
51
+ }
52
+ };
53
+ exports.getChatDataFromProvider = getChatDataFromProvider;