jinzd-ai-cli 0.4.8 → 0.4.10
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/{auth-TWWF22YZ.js → auth-MSUWO6SE.js} +1 -1
- package/dist/{chunk-G3YURBV7.js → chunk-4EUTAZ6Q.js} +1 -1
- package/dist/{chunk-CPLT6CD3.js → chunk-BYNY5JPB.js} +18 -4
- package/dist/{chunk-GDMXHX27.js → chunk-IIVTIEWB.js} +1 -1
- package/dist/{chunk-T2W7CI24.js → chunk-J3J44O4N.js} +2 -2
- package/dist/{chunk-56MYFIG4.js → chunk-WC6IEG2I.js} +1 -1
- package/dist/{hub-V27PRAPU.js → hub-B3YT75IB.js} +1 -1
- package/dist/index.js +7 -7
- package/dist/{run-tests-ENUQXQG7.js → run-tests-VDZCQA4L.js} +1 -1
- package/dist/{run-tests-L2NQAT2M.js → run-tests-YBNVSXTV.js} +1 -1
- package/dist/{server-JYSNPBYK.js → server-YLN3YUUI.js} +81 -26
- package/dist/{task-orchestrator-OXDO5UWG.js → task-orchestrator-W56DGIZ4.js} +2 -2
- package/dist/web/client/app.js +13 -2
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/web/auth.ts
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFileSync } from "fs";
|
|
5
5
|
import { join } from "path";
|
|
6
|
-
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
6
|
+
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
7
7
|
var USERS_FILE = "users.json";
|
|
8
8
|
var TOKEN_EXPIRY_HOURS = 24 * 7;
|
|
9
9
|
var USERS_DIR = "users";
|
|
@@ -50,7 +50,8 @@ var AuthManager = class {
|
|
|
50
50
|
passwordHash,
|
|
51
51
|
salt,
|
|
52
52
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
53
|
-
dataDir
|
|
53
|
+
dataDir,
|
|
54
|
+
hashVersion: 2
|
|
54
55
|
};
|
|
55
56
|
this.db.users.push(user);
|
|
56
57
|
this.save();
|
|
@@ -61,12 +62,20 @@ var AuthManager = class {
|
|
|
61
62
|
username = username.trim().toLowerCase();
|
|
62
63
|
const user = this.db.users.find((u) => u.username === username);
|
|
63
64
|
if (!user) return null;
|
|
64
|
-
const
|
|
65
|
+
const isLegacy = !user.hashVersion || user.hashVersion < 2;
|
|
66
|
+
const hash = isLegacy ? this.hashPasswordLegacy(password, user.salt) : this.hashPassword(password, user.salt);
|
|
65
67
|
const a = Buffer.from(hash, "utf-8");
|
|
66
68
|
const b = Buffer.from(user.passwordHash, "utf-8");
|
|
67
69
|
if (a.length !== b.length || !timingSafeEqual(a, b)) {
|
|
68
70
|
return null;
|
|
69
71
|
}
|
|
72
|
+
if (isLegacy) {
|
|
73
|
+
const newSalt = randomBytes(16).toString("hex");
|
|
74
|
+
user.passwordHash = this.hashPassword(password, newSalt);
|
|
75
|
+
user.salt = newSalt;
|
|
76
|
+
user.hashVersion = 2;
|
|
77
|
+
this.save();
|
|
78
|
+
}
|
|
70
79
|
return this.createToken(username);
|
|
71
80
|
}
|
|
72
81
|
/** Verify a token. Returns username or null. */
|
|
@@ -117,6 +126,7 @@ var AuthManager = class {
|
|
|
117
126
|
const salt = randomBytes(16).toString("hex");
|
|
118
127
|
user.passwordHash = this.hashPassword(newPassword, salt);
|
|
119
128
|
user.salt = salt;
|
|
129
|
+
user.hashVersion = 2;
|
|
120
130
|
this.save();
|
|
121
131
|
return null;
|
|
122
132
|
}
|
|
@@ -180,9 +190,13 @@ var AuthManager = class {
|
|
|
180
190
|
mkdirSync(this.baseDir, { recursive: true });
|
|
181
191
|
writeFileSync(this.usersFile, JSON.stringify(db, null, 2), "utf-8");
|
|
182
192
|
}
|
|
183
|
-
|
|
193
|
+
/** Legacy hash — kept only for migrating old users (v0.2.x) */
|
|
194
|
+
hashPasswordLegacy(password, salt) {
|
|
184
195
|
return createHmac("sha256", salt).update(password).digest("hex");
|
|
185
196
|
}
|
|
197
|
+
hashPassword(password, salt) {
|
|
198
|
+
return pbkdf2Sync(password, salt, 1e5, 64, "sha512").toString("hex");
|
|
199
|
+
}
|
|
186
200
|
createToken(username) {
|
|
187
201
|
const payload = {
|
|
188
202
|
username,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
EnvLoader,
|
|
4
4
|
schemaToJsonSchema
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-IIVTIEWB.js";
|
|
6
6
|
import {
|
|
7
7
|
APP_NAME,
|
|
8
8
|
CONFIG_DIR_NAME,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
MCP_TOOL_PREFIX,
|
|
16
16
|
PLUGINS_DIR_NAME,
|
|
17
17
|
VERSION
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-4EUTAZ6Q.js";
|
|
19
19
|
|
|
20
20
|
// src/config/config-manager.ts
|
|
21
21
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
@@ -381,7 +381,7 @@ ${content}`);
|
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
383
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
384
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
384
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-W56DGIZ4.js");
|
|
385
385
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
386
386
|
let interrupted = false;
|
|
387
387
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
saveDevState,
|
|
24
24
|
sessionHasMeaningfulContent,
|
|
25
25
|
setupProxy
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-J3J44O4N.js";
|
|
27
27
|
import {
|
|
28
28
|
ToolRegistry,
|
|
29
29
|
askUserContext,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
theme,
|
|
39
39
|
truncateOutput,
|
|
40
40
|
undoStack
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-IIVTIEWB.js";
|
|
42
42
|
import {
|
|
43
43
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
44
44
|
AUTHOR,
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
REPO_URL,
|
|
59
59
|
SKILLS_DIR_NAME,
|
|
60
60
|
VERSION
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-4EUTAZ6Q.js";
|
|
62
62
|
|
|
63
63
|
// src/index.ts
|
|
64
64
|
import { program } from "commander";
|
|
@@ -1914,7 +1914,7 @@ ${hint}` : "")
|
|
|
1914
1914
|
description: "Run project tests and show structured report",
|
|
1915
1915
|
usage: "/test [command|filter]",
|
|
1916
1916
|
async execute(args, _ctx) {
|
|
1917
|
-
const { executeTests } = await import("./run-tests-
|
|
1917
|
+
const { executeTests } = await import("./run-tests-YBNVSXTV.js");
|
|
1918
1918
|
const argStr = args.join(" ").trim();
|
|
1919
1919
|
let testArgs = {};
|
|
1920
1920
|
if (argStr) {
|
|
@@ -5524,11 +5524,11 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
5524
5524
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
5525
5525
|
process.exit(1);
|
|
5526
5526
|
}
|
|
5527
|
-
const { startWebServer } = await import("./server-
|
|
5527
|
+
const { startWebServer } = await import("./server-YLN3YUUI.js");
|
|
5528
5528
|
await startWebServer({ port, host: options.host });
|
|
5529
5529
|
});
|
|
5530
5530
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
5531
|
-
const { AuthManager } = await import("./auth-
|
|
5531
|
+
const { AuthManager } = await import("./auth-MSUWO6SE.js");
|
|
5532
5532
|
const config = new ConfigManager();
|
|
5533
5533
|
const auth = new AuthManager(config.getConfigDir());
|
|
5534
5534
|
if (!action || action === "list") {
|
|
@@ -5757,7 +5757,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
5757
5757
|
}),
|
|
5758
5758
|
config.get("customProviders")
|
|
5759
5759
|
);
|
|
5760
|
-
const { startHub } = await import("./hub-
|
|
5760
|
+
const { startHub } = await import("./hub-B3YT75IB.js");
|
|
5761
5761
|
await startHub(
|
|
5762
5762
|
{
|
|
5763
5763
|
topic: topic ?? "",
|
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
renderDiff,
|
|
19
19
|
runHook,
|
|
20
20
|
setupProxy
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-J3J44O4N.js";
|
|
22
22
|
import {
|
|
23
23
|
AuthManager
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-BYNY5JPB.js";
|
|
25
25
|
import {
|
|
26
26
|
ToolRegistry,
|
|
27
27
|
askUserContext,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
spawnAgentContext,
|
|
33
33
|
truncateOutput,
|
|
34
34
|
undoStack
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-IIVTIEWB.js";
|
|
36
36
|
import {
|
|
37
37
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
38
38
|
CONTEXT_FILE_CANDIDATES,
|
|
@@ -44,14 +44,14 @@ import {
|
|
|
44
44
|
PLAN_MODE_SYSTEM_ADDON,
|
|
45
45
|
SKILLS_DIR_NAME,
|
|
46
46
|
VERSION
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-4EUTAZ6Q.js";
|
|
48
48
|
|
|
49
49
|
// src/web/server.ts
|
|
50
50
|
import express from "express";
|
|
51
51
|
import { createServer } from "http";
|
|
52
52
|
import { WebSocketServer } from "ws";
|
|
53
|
-
import { join as join3, dirname, resolve as resolve2, relative } from "path";
|
|
54
|
-
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
53
|
+
import { join as join3, dirname, resolve as resolve2, relative, sep } from "path";
|
|
54
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, realpathSync } from "fs";
|
|
55
55
|
import { networkInterfaces } from "os";
|
|
56
56
|
|
|
57
57
|
// src/web/tool-executor-web.ts
|
|
@@ -996,8 +996,6 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
|
|
|
996
996
|
if (found) {
|
|
997
997
|
this.sessions.loadSession(found.id);
|
|
998
998
|
this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
|
|
999
|
-
if (found.provider) this.currentProvider = found.provider;
|
|
1000
|
-
if (found.model) this.currentModel = found.model;
|
|
1001
999
|
this.send({ type: "info", message: `Loaded session: ${found.id.slice(0, 8)} "${found.title ?? ""}" (${found.messageCount} messages)` });
|
|
1002
1000
|
this.sendSessionMessages();
|
|
1003
1001
|
this.sendStatus();
|
|
@@ -1440,7 +1438,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1440
1438
|
case "test": {
|
|
1441
1439
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
1442
1440
|
try {
|
|
1443
|
-
const { executeTests } = await import("./run-tests-
|
|
1441
|
+
const { executeTests } = await import("./run-tests-YBNVSXTV.js");
|
|
1444
1442
|
const argStr = args.join(" ").trim();
|
|
1445
1443
|
let testArgs = {};
|
|
1446
1444
|
if (argStr) {
|
|
@@ -2062,6 +2060,30 @@ async function startWebServer(options = {}) {
|
|
|
2062
2060
|
const app = express();
|
|
2063
2061
|
const server = createServer(app);
|
|
2064
2062
|
const wss = new WebSocketServer({ server });
|
|
2063
|
+
const authManager = new AuthManager(config.getConfigDir());
|
|
2064
|
+
if (authManager.isEnabled()) {
|
|
2065
|
+
console.log(` Auth: ${authManager.listUsers().length} user(s) registered`);
|
|
2066
|
+
} else {
|
|
2067
|
+
console.log(` Auth: disabled (no users registered)`);
|
|
2068
|
+
}
|
|
2069
|
+
const requireAuth = (req, res, next) => {
|
|
2070
|
+
if (!authManager.isEnabled()) return next();
|
|
2071
|
+
const authHeader = req.headers.authorization;
|
|
2072
|
+
const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
|
|
2073
|
+
const cookieToken = parseCookie(req.headers.cookie ?? "")["aicli_token"];
|
|
2074
|
+
const effectiveToken = token ?? cookieToken;
|
|
2075
|
+
if (!effectiveToken) {
|
|
2076
|
+
res.status(401).json({ error: "Authentication required" });
|
|
2077
|
+
return;
|
|
2078
|
+
}
|
|
2079
|
+
const username = authManager.verifyToken(effectiveToken);
|
|
2080
|
+
if (!username) {
|
|
2081
|
+
res.status(401).json({ error: "Invalid or expired token" });
|
|
2082
|
+
return;
|
|
2083
|
+
}
|
|
2084
|
+
req._authUser = username;
|
|
2085
|
+
next();
|
|
2086
|
+
};
|
|
2065
2087
|
const moduleDir = getModuleDir();
|
|
2066
2088
|
let clientDir = join3(moduleDir, "web", "client");
|
|
2067
2089
|
if (!existsSync4(clientDir)) {
|
|
@@ -2105,13 +2127,21 @@ async function startWebServer(options = {}) {
|
|
|
2105
2127
|
}
|
|
2106
2128
|
const token = authManager.login(username, password);
|
|
2107
2129
|
console.log(` \u2713 User registered via API: ${username}`);
|
|
2130
|
+
res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
|
|
2108
2131
|
res.json({ success: true, token, username });
|
|
2109
2132
|
});
|
|
2110
|
-
app.get("/api/files", (req, res) => {
|
|
2133
|
+
app.get("/api/files", requireAuth, (req, res) => {
|
|
2111
2134
|
const cwd = process.cwd();
|
|
2112
2135
|
const prefix = req.query.prefix || "";
|
|
2113
2136
|
const targetDir = join3(cwd, prefix);
|
|
2114
|
-
|
|
2137
|
+
try {
|
|
2138
|
+
const canonicalTarget = realpathSync(resolve2(targetDir));
|
|
2139
|
+
const canonicalCwd = realpathSync(resolve2(cwd));
|
|
2140
|
+
if (!canonicalTarget.startsWith(canonicalCwd + sep) && canonicalTarget !== canonicalCwd) {
|
|
2141
|
+
res.json({ files: [] });
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
} catch {
|
|
2115
2145
|
res.json({ files: [] });
|
|
2116
2146
|
return;
|
|
2117
2147
|
}
|
|
@@ -2128,7 +2158,7 @@ async function startWebServer(options = {}) {
|
|
|
2128
2158
|
res.json({ files: [] });
|
|
2129
2159
|
}
|
|
2130
2160
|
});
|
|
2131
|
-
app.get("/api/sessions", (_req, res) => {
|
|
2161
|
+
app.get("/api/sessions", requireAuth, (_req, res) => {
|
|
2132
2162
|
try {
|
|
2133
2163
|
const list = sessions.listSessions();
|
|
2134
2164
|
res.json({
|
|
@@ -2145,7 +2175,7 @@ async function startWebServer(options = {}) {
|
|
|
2145
2175
|
res.json({ sessions: [] });
|
|
2146
2176
|
}
|
|
2147
2177
|
});
|
|
2148
|
-
app.get("/api/file-content", (req, res) => {
|
|
2178
|
+
app.get("/api/file-content", requireAuth, (req, res) => {
|
|
2149
2179
|
const filePath = req.query.path;
|
|
2150
2180
|
if (!filePath) {
|
|
2151
2181
|
res.json({ error: "Missing path" });
|
|
@@ -2153,8 +2183,15 @@ async function startWebServer(options = {}) {
|
|
|
2153
2183
|
}
|
|
2154
2184
|
const cwd = process.cwd();
|
|
2155
2185
|
const fullPath = resolve2(join3(cwd, filePath));
|
|
2156
|
-
|
|
2157
|
-
|
|
2186
|
+
try {
|
|
2187
|
+
const canonicalFull = realpathSync(fullPath);
|
|
2188
|
+
const canonicalCwd = realpathSync(resolve2(cwd));
|
|
2189
|
+
if (!canonicalFull.startsWith(canonicalCwd + sep) && canonicalFull !== canonicalCwd) {
|
|
2190
|
+
res.json({ error: "Access denied" });
|
|
2191
|
+
return;
|
|
2192
|
+
}
|
|
2193
|
+
} catch {
|
|
2194
|
+
res.json({ error: "File not found" });
|
|
2158
2195
|
return;
|
|
2159
2196
|
}
|
|
2160
2197
|
try {
|
|
@@ -2165,16 +2202,10 @@ async function startWebServer(options = {}) {
|
|
|
2165
2202
|
}
|
|
2166
2203
|
const content = readFileSync4(fullPath, "utf-8");
|
|
2167
2204
|
res.json({ content, size: stat.size });
|
|
2168
|
-
} catch
|
|
2169
|
-
res.json({ error:
|
|
2205
|
+
} catch {
|
|
2206
|
+
res.json({ error: "Cannot read file" });
|
|
2170
2207
|
}
|
|
2171
2208
|
});
|
|
2172
|
-
const authManager = new AuthManager(config.getConfigDir());
|
|
2173
|
-
if (authManager.isEnabled()) {
|
|
2174
|
-
console.log(` Auth: ${authManager.listUsers().length} user(s) registered`);
|
|
2175
|
-
} else {
|
|
2176
|
-
console.log(` Auth: disabled (no users registered)`);
|
|
2177
|
-
}
|
|
2178
2209
|
const userResources = /* @__PURE__ */ new Map();
|
|
2179
2210
|
function getUserShared(username) {
|
|
2180
2211
|
const cached = userResources.get(username);
|
|
@@ -2203,7 +2234,8 @@ async function startWebServer(options = {}) {
|
|
|
2203
2234
|
const qMark = urlStr.indexOf("?");
|
|
2204
2235
|
const params = new URLSearchParams(qMark >= 0 ? urlStr.slice(qMark + 1) : "");
|
|
2205
2236
|
const tabId = params.get("tabId") || `tab-${Date.now()}`;
|
|
2206
|
-
const
|
|
2237
|
+
const cookies = parseCookie(req.headers.cookie ?? "");
|
|
2238
|
+
const token = cookies["aicli_token"] || "";
|
|
2207
2239
|
const existing = handlers.get(tabId);
|
|
2208
2240
|
if (existing) {
|
|
2209
2241
|
existing.onDisconnect();
|
|
@@ -2257,7 +2289,22 @@ async function startWebServer(options = {}) {
|
|
|
2257
2289
|
handler = new SessionHandler(ws, userShared);
|
|
2258
2290
|
handlers.set(tabId, handler);
|
|
2259
2291
|
console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
2260
|
-
ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username }));
|
|
2292
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username, setCookie: true }));
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
if (action === "token") {
|
|
2296
|
+
const { token: clientToken } = parsed;
|
|
2297
|
+
const verifiedUser = authManager.verifyToken(clientToken);
|
|
2298
|
+
if (!verifiedUser) {
|
|
2299
|
+
ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Invalid or expired token" }));
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
authenticatedUser = verifiedUser;
|
|
2303
|
+
const userShared = getUserShared(verifiedUser);
|
|
2304
|
+
handler = new SessionHandler(ws, userShared);
|
|
2305
|
+
handlers.set(tabId, handler);
|
|
2306
|
+
console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
|
|
2307
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, token: clientToken, username: verifiedUser }));
|
|
2261
2308
|
return;
|
|
2262
2309
|
}
|
|
2263
2310
|
if (action === "login") {
|
|
@@ -2271,7 +2318,7 @@ async function startWebServer(options = {}) {
|
|
|
2271
2318
|
handler = new SessionHandler(ws, userShared);
|
|
2272
2319
|
handlers.set(tabId, handler);
|
|
2273
2320
|
console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
2274
|
-
ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username }));
|
|
2321
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username, setCookie: true }));
|
|
2275
2322
|
return;
|
|
2276
2323
|
}
|
|
2277
2324
|
ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Unknown auth action" }));
|
|
@@ -2366,6 +2413,14 @@ function loadProjectMcpConfig() {
|
|
|
2366
2413
|
return null;
|
|
2367
2414
|
}
|
|
2368
2415
|
}
|
|
2416
|
+
function parseCookie(cookie) {
|
|
2417
|
+
const result = {};
|
|
2418
|
+
for (const part of cookie.split(";")) {
|
|
2419
|
+
const [key, ...rest] = part.trim().split("=");
|
|
2420
|
+
if (key) result[key.trim()] = rest.join("=").trim();
|
|
2421
|
+
}
|
|
2422
|
+
return result;
|
|
2423
|
+
}
|
|
2369
2424
|
async function openBrowser(url) {
|
|
2370
2425
|
try {
|
|
2371
2426
|
const { exec } = await import("child_process");
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IIVTIEWB.js";
|
|
8
8
|
import {
|
|
9
9
|
SUBAGENT_ALLOWED_TOOLS
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4EUTAZ6Q.js";
|
|
11
11
|
|
|
12
12
|
// src/hub/task-orchestrator.ts
|
|
13
13
|
import { createInterface } from "readline";
|
package/dist/web/client/app.js
CHANGED
|
@@ -76,14 +76,19 @@ let reconnectDelay = 1000; // Start at 1s, exponential backoff
|
|
|
76
76
|
|
|
77
77
|
function connect() {
|
|
78
78
|
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
// Security: do NOT put token in URL (visible in logs/referrer). Cookie is sent automatically.
|
|
80
|
+
// For reconnection, also send token as first message in case cookie is blocked.
|
|
81
|
+
ws = new WebSocket(`${protocol}//${location.host}?tabId=${tabId}`);
|
|
81
82
|
|
|
82
83
|
ws.onopen = () => {
|
|
83
84
|
connected = true;
|
|
84
85
|
reconnectDelay = 1000; // Reset backoff on success
|
|
85
86
|
connectionStatus.textContent = '🟢 Connected';
|
|
86
87
|
connectionStatus.className = 'status-connected';
|
|
88
|
+
// Send stored token for auth (in case cookie is not available)
|
|
89
|
+
if (authToken) {
|
|
90
|
+
ws.send(JSON.stringify({ type: 'auth', action: 'token', token: authToken }));
|
|
91
|
+
}
|
|
87
92
|
requestSessionList();
|
|
88
93
|
checkAuthStatus();
|
|
89
94
|
// Start heartbeat ping every 30s
|
|
@@ -1886,6 +1891,11 @@ function handleAuthResult(msg) {
|
|
|
1886
1891
|
authUsername = msg.username;
|
|
1887
1892
|
localStorage.setItem('aicli-auth-token', msg.token);
|
|
1888
1893
|
localStorage.setItem('aicli-auth-user', msg.username);
|
|
1894
|
+
// Set cookie for secure WebSocket auth (httpOnly cookie set by server on HTTP login,
|
|
1895
|
+
// but for WS-only login, set a JS cookie as fallback)
|
|
1896
|
+
if (msg.setCookie && msg.token) {
|
|
1897
|
+
document.cookie = `aicli_token=${encodeURIComponent(msg.token)}; path=/; max-age=${7 * 24 * 3600}; SameSite=Strict`;
|
|
1898
|
+
}
|
|
1889
1899
|
hideAuthScreen();
|
|
1890
1900
|
// Request session list now that we're authenticated
|
|
1891
1901
|
requestSessionList();
|
|
@@ -1913,6 +1923,7 @@ function handleLogout() {
|
|
|
1913
1923
|
authToken = '';
|
|
1914
1924
|
authUsername = '';
|
|
1915
1925
|
localStorage.removeItem('aicli-auth-token');
|
|
1926
|
+
document.cookie = 'aicli_token=; path=/; max-age=0'; // Clear auth cookie
|
|
1916
1927
|
localStorage.removeItem('aicli-auth-user');
|
|
1917
1928
|
sessionStorage.removeItem('aicli-active-session');
|
|
1918
1929
|
// Reconnect — server will send auth_required
|