zrocclaw 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+
2
+ 
3
+ > zrocclaw@0.0.6 build /Users/zroc/Desktop/Zroc/AI/zrocclaw/apps/gateway
4
+ > tsup
5
+
6
+ CLI Building entry: src/server.ts
7
+ CLI Using tsconfig: tsconfig.json
8
+ CLI tsup v8.5.1
9
+ CLI Using tsup config: /Users/zroc/Desktop/Zroc/AI/zrocclaw/apps/gateway/tsup.config.ts
10
+ CLI Target: node18
11
+ CLI Cleaning output folder
12
+ CJS Build start
13
+ CJS dist/server.js 7.68 KB
14
+ CJS ⚡️ Build success in 7ms
package/bin/zrocclaw.js CHANGED
@@ -1,2 +1,69 @@
1
1
  #!/usr/bin/env node
2
- require('../dist/index.js');
2
+
3
+ const path = require('path');
4
+ // Set PM2_HOME to a local directory to avoid permission issues in sandboxed environments
5
+ if (!process.env.PM2_HOME) {
6
+ process.env.PM2_HOME = path.resolve(__dirname, '../../.pm2');
7
+ }
8
+
9
+ const { program } = require('commander');
10
+ const pm2 = require('pm2');
11
+ const pkg = require('../package.json');
12
+
13
+ const APP_NAME = 'zrocclaw';
14
+ const SCRIPT_PATH = path.resolve(__dirname, '../dist/server.js');
15
+
16
+ program
17
+ .name('zrocclaw')
18
+ .description('ZrocClaw Gateway CLI')
19
+ .version(pkg.version);
20
+
21
+ program
22
+ .command('start')
23
+ .description('启动网关服务')
24
+ .action(() => {
25
+ pm2.connect((err) => {
26
+ if (err) {
27
+ console.error('连接 PM2 失败:', err);
28
+ process.exit(2);
29
+ }
30
+
31
+ pm2.start({
32
+ name: APP_NAME,
33
+ script: SCRIPT_PATH,
34
+ env: {
35
+ NODE_ENV: 'production'
36
+ }
37
+ }, (err, apps) => {
38
+ pm2.disconnect();
39
+ if (err) {
40
+ console.error('网关服务启动失败:', err.message || err);
41
+ process.exit(2);
42
+ }
43
+ console.log(`✅ 网关服务 [${APP_NAME}] 已成功启动!`);
44
+ });
45
+ });
46
+ });
47
+
48
+ program
49
+ .command('stop')
50
+ .description('关闭网关服务')
51
+ .action(() => {
52
+ pm2.connect((err) => {
53
+ if (err) {
54
+ console.error('连接 PM2 失败:', err);
55
+ process.exit(2);
56
+ }
57
+
58
+ pm2.stop(APP_NAME, (err, apps) => {
59
+ pm2.disconnect();
60
+ if (err) {
61
+ console.error('网关服务关闭失败 (可能服务并未运行):', err.message || err);
62
+ process.exit(2);
63
+ }
64
+ console.log(`⛔ 网关服务 [${APP_NAME}] 已成功关闭!`);
65
+ });
66
+ });
67
+ });
68
+
69
+ program.parse(process.argv);
package/dist/server.js ADDED
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/app.ts
26
+ var import_express3 = __toESM(require("express"));
27
+ var import_cors = __toESM(require("cors"));
28
+ var import_helmet = __toESM(require("helmet"));
29
+
30
+ // src/routes/config.ts
31
+ var import_express = require("express");
32
+ var import_fileManager = require("@zrocclaw/core/fileManager");
33
+ var import_promises = __toESM(require("fs/promises"));
34
+ var import_path = __toESM(require("path"));
35
+ var router = (0, import_express.Router)();
36
+ router.get("/", (req, res) => {
37
+ res.json({ configPath: (0, import_fileManager.getConfigPath)() });
38
+ });
39
+ var configDir = (0, import_fileManager.getConfigPath)();
40
+ var configFilePath = import_path.default.join(configDir, "model.json");
41
+ router.get("/model", async (req, res) => {
42
+ try {
43
+ try {
44
+ await import_promises.default.access(configFilePath);
45
+ const data = await import_promises.default.readFile(configFilePath, "utf-8");
46
+ res.json(JSON.parse(data));
47
+ } catch (error) {
48
+ if (error.code === "ENOENT") {
49
+ await import_promises.default.mkdir(configDir, { recursive: true });
50
+ await import_promises.default.writeFile(configFilePath, "{}", "utf-8");
51
+ res.json({});
52
+ } else {
53
+ throw error;
54
+ }
55
+ }
56
+ } catch (error) {
57
+ console.error("\u83B7\u53D6\u6A21\u578B\u5931\u8D25:", error);
58
+ res.status(500).json({ error: "\u83B7\u53D6\u6A21\u578B\u5931\u8D25" });
59
+ }
60
+ });
61
+ router.post("/model", async (req, res) => {
62
+ try {
63
+ await import_promises.default.mkdir(configDir, { recursive: true });
64
+ let currentConfig = {};
65
+ try {
66
+ const data = await import_promises.default.readFile(configFilePath, "utf-8");
67
+ currentConfig = JSON.parse(data);
68
+ } catch (error) {
69
+ if (error.code !== "ENOENT") {
70
+ throw error;
71
+ }
72
+ }
73
+ const newConfig = { ...currentConfig, ...req.body };
74
+ await import_promises.default.writeFile(configFilePath, JSON.stringify(newConfig, null, 2), "utf-8");
75
+ res.json(newConfig);
76
+ } catch (error) {
77
+ console.error("\u4FEE\u6539\u6A21\u578B\u5931\u8D25:", error);
78
+ res.status(500).json({ error: "\u4FEE\u6539\u6A21\u578B\u5931\u8D25" });
79
+ }
80
+ });
81
+ var config_default = router;
82
+
83
+ // src/routes/chat.ts
84
+ var import_express2 = require("express");
85
+ var import_agents = require("@zrocclaw/core/agents");
86
+ var import_fileManager3 = require("@zrocclaw/core/fileManager");
87
+ var import_promises2 = __toESM(require("fs/promises"));
88
+ var import_path3 = __toESM(require("path"));
89
+ var import_crypto = __toESM(require("crypto"));
90
+
91
+ // src/models/session.ts
92
+ var import_fileManager2 = require("@zrocclaw/core/fileManager");
93
+ var import_fs = __toESM(require("fs"));
94
+ var import_path2 = __toESM(require("path"));
95
+ var SessionModel = class _SessionModel {
96
+ constructor() {
97
+ this.filePath = import_path2.default.join((0, import_fileManager2.getConfigPath)(), "session.json");
98
+ this.data = {
99
+ sessionId: null,
100
+ sessionData: {}
101
+ };
102
+ this.load();
103
+ }
104
+ static getInstance() {
105
+ if (!_SessionModel.instance) {
106
+ _SessionModel.instance = new _SessionModel();
107
+ }
108
+ return _SessionModel.instance;
109
+ }
110
+ load() {
111
+ if (import_fs.default.existsSync(this.filePath)) {
112
+ try {
113
+ const fileContent = import_fs.default.readFileSync(this.filePath, "utf-8");
114
+ const parsed = JSON.parse(fileContent);
115
+ this.data = { ...this.data, ...parsed };
116
+ } catch (error) {
117
+ console.error("Failed to parse session.json:", error);
118
+ }
119
+ } else {
120
+ this.save();
121
+ }
122
+ }
123
+ save() {
124
+ try {
125
+ const dir = import_path2.default.dirname(this.filePath);
126
+ if (!import_fs.default.existsSync(dir)) {
127
+ import_fs.default.mkdirSync(dir, { recursive: true });
128
+ }
129
+ import_fs.default.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), "utf-8");
130
+ } catch (error) {
131
+ console.error("Failed to save session.json:", error);
132
+ }
133
+ }
134
+ getSessionId() {
135
+ return this.data.sessionId;
136
+ }
137
+ setSessionId(id) {
138
+ this.data.sessionId = id;
139
+ this.save();
140
+ }
141
+ getSessionData() {
142
+ return this.data.sessionData;
143
+ }
144
+ setSessionData(data) {
145
+ this.data.sessionData = data;
146
+ this.save();
147
+ }
148
+ };
149
+ var sessionModel = SessionModel.getInstance();
150
+ var session_default = sessionModel;
151
+
152
+ // src/routes/chat.ts
153
+ var configDir2 = (0, import_fileManager3.getConfigPath)();
154
+ var configFilePath2 = import_path3.default.join(configDir2, "model.json");
155
+ var router2 = (0, import_express2.Router)();
156
+ router2.get("/", (req, res) => {
157
+ res.json({ status: "OK" });
158
+ });
159
+ router2.post("/stream", async (req, res) => {
160
+ try {
161
+ const query = req.body.query;
162
+ const thread_id = req.body.thread_id;
163
+ if (!query || !thread_id) {
164
+ return res.status(400).json({ error: "Missing query or thread_id in request body" });
165
+ }
166
+ const configContent = await import_promises2.default.readFile(configFilePath2, "utf-8");
167
+ const config = JSON.parse(configContent);
168
+ const modelConfig = config.defaultModel;
169
+ if (!modelConfig) {
170
+ return res.status(500).json({ error: "defaultModel not found in config" });
171
+ }
172
+ res.setHeader("Content-Type", "text/event-stream");
173
+ res.setHeader("Cache-Control", "no-cache");
174
+ res.setHeader("Connection", "keep-alive");
175
+ const stream = (0, import_agents.browser_stream_invoke)(query, thread_id, modelConfig);
176
+ for await (const chunk of stream) {
177
+ res.write(chunk);
178
+ }
179
+ res.end();
180
+ } catch (error) {
181
+ console.error("Stream error:", error);
182
+ if (!res.headersSent) {
183
+ res.status(500).json({ error: "Internal server error" });
184
+ } else {
185
+ res.end();
186
+ }
187
+ }
188
+ });
189
+ router2.get("/session", (req, res) => {
190
+ const isNew = req.query.new === "true";
191
+ let sessionId = session_default.getSessionId();
192
+ if (!sessionId || isNew) {
193
+ sessionId = import_crypto.default.randomUUID();
194
+ session_default.setSessionId(sessionId);
195
+ }
196
+ res.json({ sessionId });
197
+ });
198
+ var chat_default = router2;
199
+
200
+ // src/app.ts
201
+ var import_dotenv = __toESM(require("dotenv"));
202
+ var import_path4 = __toESM(require("path"));
203
+ import_dotenv.default.config();
204
+ var app = (0, import_express3.default)();
205
+ app.use((0, import_helmet.default)());
206
+ app.use((0, import_cors.default)());
207
+ app.use(import_express3.default.json());
208
+ app.get("/health", (req, res) => {
209
+ res.json({ status: "OK", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
210
+ });
211
+ app.use("/config", config_default);
212
+ app.use("/chat", chat_default);
213
+ if (true) {
214
+ app.use("/web", import_express3.default.static(import_path4.default.join(__dirname, "../src/static")));
215
+ }
216
+ var app_default = app;
217
+
218
+ // src/server.ts
219
+ var PORT = 18302;
220
+ app_default.listen(PORT, () => {
221
+ console.log(`Gateway is running at http://localhost:${PORT}`);
222
+ console.log(`Environment: ${"production"}`);
223
+ });
package/nodemon.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "watch": ["src", "../../packages/core/src"],
3
+ "ext": "ts,json",
4
+ "ignore": ["src/**/*.spec.ts"],
5
+ "exec": "ts-node src/server.ts"
6
+ }
package/package.json CHANGED
@@ -1,25 +1,39 @@
1
1
  {
2
2
  "name": "zrocclaw",
3
- "version": "0.0.3",
4
- "description": "个人专属浏览器AI助手命令行工具",
3
+ "version": "0.0.6",
4
+ "description": "ZrocClaw Gateway Service",
5
+ "main": "src/server.ts",
5
6
  "bin": {
6
- "zrocclaw": "bin/zrocclaw.js"
7
+ "zrocclaw": "./bin/zrocclaw.js"
7
8
  },
8
- "files": [
9
- "dist",
10
- "bin",
11
- "README.md"
12
- ],
13
9
  "scripts": {
14
- "dev": "tsc --watch",
15
- "build": "tsc"
10
+ "test": "echo \"Error: no test specified\" && exit 1",
11
+ "dev": "nodemon src/server.ts",
12
+ "start": "node dist/server.js",
13
+ "build": "tsup"
16
14
  },
15
+ "keywords": [],
16
+ "author": "ZrocLee",
17
+ "license": "ISC",
18
+ "packageManager": "pnpm@10.30.3",
17
19
  "dependencies": {
18
- "@zrocclaw/gateway": "^1.0.0",
19
- "commander": "^12.1.0"
20
+ "@zrocclaw/core": "workspace:*",
21
+ "commander": "^14.0.3",
22
+ "cors": "^2.8.6",
23
+ "dotenv": "^17.3.1",
24
+ "express": "^5.2.1",
25
+ "helmet": "^8.1.0",
26
+ "http-proxy-middleware": "^3.0.5",
27
+ "pm2": "^6.0.14"
20
28
  },
21
29
  "devDependencies": {
22
- "@types/node": "^24.12.0",
30
+ "@types/cors": "^2.8.19",
31
+ "@types/express": "^5.0.6",
32
+ "@types/http-proxy-middleware": "^1.0.0",
33
+ "@types/node": "^25.5.0",
34
+ "nodemon": "^3.1.14",
35
+ "ts-node": "^10.9.2",
36
+ "tsup": "^8.5.1",
23
37
  "typescript": "^5.9.3"
24
38
  }
25
- }
39
+ }
package/src/app.ts ADDED
@@ -0,0 +1,35 @@
1
+ // src/app.ts
2
+ import express from "express";
3
+ import cors from "cors"; // 可选,如果需要跨域
4
+ import helmet from "helmet"; // 可选,安全头
5
+ import configRouter from "./routes/config";
6
+ import chatRouter from "./routes/chat";
7
+ import dotenv from "dotenv";
8
+ import path from "path";
9
+
10
+ dotenv.config(); // 加载 .env 文件
11
+
12
+ const app = express();
13
+
14
+ // 基础中间件
15
+ app.use(helmet()); // 安全相关
16
+ app.use(cors()); // 允许跨域
17
+ app.use(express.json()); // 解析 JSON 请求体
18
+
19
+ // 健康检查端点
20
+ app.get("/health", (req, res) => {
21
+ res.json({ status: "OK", timestamp: new Date().toISOString() });
22
+ });
23
+
24
+ // 配置路由
25
+ app.use("/config", configRouter);
26
+ app.use("/chat", chatRouter);
27
+
28
+ // 静态文件服务:将 Vue 打包后的 dist 目录挂载到根路径
29
+ // 注意:生产环境才使用静态文件服务,开发时通常由 Vite 自己处理
30
+ if (process.env.NODE_ENV === "production") {
31
+ // 静态资源存放在 src/static(Web 构建后复制过来)
32
+ app.use("/web", express.static(path.join(__dirname, "../src/static")));
33
+ }
34
+
35
+ export default app;
@@ -0,0 +1,78 @@
1
+ import { getConfigPath } from '@zrocclaw/core/fileManager';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ interface SessionData {
6
+ sessionId: string | null;
7
+ sessionData: Record<string, any>;
8
+ }
9
+
10
+ class SessionModel {
11
+ private static instance: SessionModel;
12
+ private filePath: string;
13
+ private data: SessionData;
14
+
15
+ private constructor() {
16
+ this.filePath = path.join(getConfigPath(), 'session.json');
17
+ this.data = {
18
+ sessionId: null,
19
+ sessionData: {},
20
+ };
21
+ this.load();
22
+ }
23
+
24
+ public static getInstance(): SessionModel {
25
+ if (!SessionModel.instance) {
26
+ SessionModel.instance = new SessionModel();
27
+ }
28
+ return SessionModel.instance;
29
+ }
30
+
31
+ private load(): void {
32
+ if (fs.existsSync(this.filePath)) {
33
+ try {
34
+ const fileContent = fs.readFileSync(this.filePath, 'utf-8');
35
+ const parsed = JSON.parse(fileContent);
36
+ this.data = { ...this.data, ...parsed };
37
+ } catch (error) {
38
+ console.error('Failed to parse session.json:', error);
39
+ }
40
+ } else {
41
+ // 文件不存在就创建一个
42
+ this.save();
43
+ }
44
+ }
45
+
46
+ private save(): void {
47
+ try {
48
+ const dir = path.dirname(this.filePath);
49
+ if (!fs.existsSync(dir)) {
50
+ fs.mkdirSync(dir, { recursive: true });
51
+ }
52
+ fs.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), 'utf-8');
53
+ } catch (error) {
54
+ console.error('Failed to save session.json:', error);
55
+ }
56
+ }
57
+
58
+ public getSessionId(): string | null {
59
+ return this.data.sessionId;
60
+ }
61
+
62
+ public setSessionId(id: string | null): void {
63
+ this.data.sessionId = id;
64
+ this.save();
65
+ }
66
+
67
+ public getSessionData(): Record<string, any> {
68
+ return this.data.sessionData;
69
+ }
70
+
71
+ public setSessionData(data: Record<string, any>): void {
72
+ this.data.sessionData = data;
73
+ this.save();
74
+ }
75
+ }
76
+
77
+ export const sessionModel = SessionModel.getInstance();
78
+ export default sessionModel;
@@ -0,0 +1,67 @@
1
+ import { Router } from 'express';
2
+ import { browser_stream_invoke } from '@zrocclaw/core/agents'
3
+ import { getConfigPath } from '@zrocclaw/core/fileManager';
4
+ import fs from 'fs/promises';
5
+ import path from 'path';
6
+ import crypto from 'crypto';
7
+ import sessionModel from '../models/session';
8
+
9
+ const configDir = getConfigPath();
10
+ const configFilePath = path.join(configDir, 'model.json');
11
+
12
+
13
+
14
+ const router = Router();
15
+ router.get('/', (req, res) => {
16
+ res.json({ status: "OK" });
17
+ });
18
+
19
+ router.post('/stream', async (req, res) => {
20
+ try {
21
+ const query = req.body.query as string;
22
+ const thread_id = req.body.thread_id as string;
23
+
24
+ if (!query || !thread_id) {
25
+ return res.status(400).json({ error: "Missing query or thread_id in request body" });
26
+ }
27
+
28
+ const configContent = await fs.readFile(configFilePath, 'utf-8');
29
+ const config = JSON.parse(configContent);
30
+ const modelConfig = config.defaultModel;
31
+
32
+ if (!modelConfig) {
33
+ return res.status(500).json({ error: "defaultModel not found in config" });
34
+ }
35
+
36
+ res.setHeader('Content-Type', 'text/event-stream');
37
+ res.setHeader('Cache-Control', 'no-cache');
38
+ res.setHeader('Connection', 'keep-alive');
39
+
40
+ const stream = browser_stream_invoke(query, thread_id, modelConfig);
41
+
42
+ for await (const chunk of stream) {
43
+ res.write(chunk);
44
+ }
45
+
46
+ res.end();
47
+ } catch (error) {
48
+ console.error("Stream error:", error);
49
+ if (!res.headersSent) {
50
+ res.status(500).json({ error: "Internal server error" });
51
+ } else {
52
+ res.end();
53
+ }
54
+ }
55
+ });
56
+
57
+ router.get('/session', (req, res) => {
58
+ const isNew = req.query.new === 'true';
59
+ let sessionId = sessionModel.getSessionId();
60
+ if (!sessionId || isNew) {
61
+ sessionId = crypto.randomUUID();
62
+ sessionModel.setSessionId(sessionId);
63
+ }
64
+ res.json({ sessionId });
65
+ });
66
+
67
+ export default router;
@@ -0,0 +1,67 @@
1
+ import { Router } from 'express';
2
+ import { getConfigPath } from '@zrocclaw/core/fileManager';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+
6
+ const router = Router();
7
+
8
+ router.get('/', (req, res) => {
9
+ res.json({ configPath: getConfigPath()});
10
+ });
11
+
12
+
13
+ const configDir = getConfigPath();
14
+ const configFilePath = path.join(configDir, 'model.json');
15
+
16
+ // 新增:获取模型信息
17
+ router.get('/model', async (req, res) => {
18
+ try {
19
+
20
+ try {
21
+ await fs.access(configFilePath);
22
+ const data = await fs.readFile(configFilePath, 'utf-8');
23
+ res.json(JSON.parse(data));
24
+ } catch (error: any) {
25
+ if (error.code === 'ENOENT') {
26
+ // 文件不存在,创建并返回 {}
27
+ await fs.mkdir(configDir, { recursive: true });
28
+ await fs.writeFile(configFilePath, '{}', 'utf-8');
29
+ res.json({});
30
+ } else {
31
+ throw error;
32
+ }
33
+ }
34
+ } catch (error) {
35
+ console.error('获取模型失败:', error);
36
+ res.status(500).json({ error: '获取模型失败' });
37
+ }
38
+ });
39
+
40
+ // 新增:修改模型信息
41
+ router.post('/model', async (req, res) => {
42
+ try {
43
+ // 确保目录存在
44
+ await fs.mkdir(configDir, { recursive: true });
45
+
46
+ // 读取现有配置
47
+ let currentConfig = {};
48
+ try {
49
+ const data = await fs.readFile(configFilePath, 'utf-8');
50
+ currentConfig = JSON.parse(data);
51
+ } catch (error: any) {
52
+ if (error.code !== 'ENOENT') {
53
+ throw error;
54
+ }
55
+ }
56
+
57
+ // 合并并写入新配置
58
+ const newConfig = { ...currentConfig, ...req.body };
59
+ await fs.writeFile(configFilePath, JSON.stringify(newConfig, null, 2), 'utf-8');
60
+ res.json(newConfig);
61
+ } catch (error) {
62
+ console.error('修改模型失败:', error);
63
+ res.status(500).json({ error: '修改模型失败' });
64
+ }
65
+ });
66
+
67
+ export default router;
package/src/server.ts ADDED
@@ -0,0 +1,9 @@
1
+ // src/server.ts
2
+ import app from './app';
3
+
4
+ const PORT = 18302;
5
+
6
+ app.listen(PORT, () => {
7
+ console.log(`Gateway is running at http://localhost:${PORT}`);
8
+ console.log(`Environment: ${process.env.NODE_ENV}`);
9
+ });