@zqagent/core 0.2.11 → 0.2.12
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/index.cjs +3015 -2785
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +1921 -1691
- package/dist/cli/index.js.map +1 -1
- package/dist/server/index.cjs +212 -110
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +212 -110
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -553,7 +553,7 @@ var init_wire = __esm({
|
|
|
553
553
|
init_esm_shims();
|
|
554
554
|
import "dotenv/config";
|
|
555
555
|
import * as fs34 from "fs";
|
|
556
|
-
import * as
|
|
556
|
+
import * as readline3 from "readline";
|
|
557
557
|
import * as path43 from "path";
|
|
558
558
|
import { exec } from "child_process";
|
|
559
559
|
import { promisify } from "util";
|
|
@@ -10496,7 +10496,8 @@ function renderTerminalTutorial() {
|
|
|
10496
10496
|
|
|
10497
10497
|
// src/cli/link.ts
|
|
10498
10498
|
init_esm_shims();
|
|
10499
|
-
import * as
|
|
10499
|
+
import * as path28 from "path";
|
|
10500
|
+
import * as readline2 from "readline";
|
|
10500
10501
|
|
|
10501
10502
|
// src/server/store.ts
|
|
10502
10503
|
init_esm_shims();
|
|
@@ -10904,1358 +10905,657 @@ async function hubEnrollByAccount(baseUrl, input) {
|
|
|
10904
10905
|
return { hubUrl: baseUrl.replace(/\/$/, ""), ...data };
|
|
10905
10906
|
}
|
|
10906
10907
|
|
|
10907
|
-
// src/cli/
|
|
10908
|
+
// src/cli/account-enroll.ts
|
|
10908
10909
|
init_esm_shims();
|
|
10909
|
-
import
|
|
10910
|
-
import * as
|
|
10911
|
-
import * as os5 from "os";
|
|
10912
|
-
import * as path22 from "path";
|
|
10910
|
+
import * as path25 from "path";
|
|
10911
|
+
import * as readline from "readline/promises";
|
|
10913
10912
|
|
|
10914
|
-
// src/
|
|
10913
|
+
// src/server/projects.ts
|
|
10915
10914
|
init_esm_shims();
|
|
10916
|
-
import * as
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
import
|
|
10921
|
-
|
|
10922
|
-
|
|
10923
|
-
|
|
10915
|
+
import * as path24 from "path";
|
|
10916
|
+
|
|
10917
|
+
// src/server/session.ts
|
|
10918
|
+
init_esm_shims();
|
|
10919
|
+
import * as path23 from "path";
|
|
10920
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
10921
|
+
|
|
10922
|
+
// src/server/mcpConfig.ts
|
|
10923
|
+
init_esm_shims();
|
|
10924
|
+
import fs18 from "fs";
|
|
10925
|
+
import path21 from "path";
|
|
10926
|
+
function isValidMcpConfig(v) {
|
|
10927
|
+
return !!v && typeof v === "object" && typeof v.name === "string" && (v.type === "stdio" || v.type === "http");
|
|
10928
|
+
}
|
|
10929
|
+
function readConfigFile(file) {
|
|
10930
|
+
if (!file) return [];
|
|
10924
10931
|
try {
|
|
10925
|
-
|
|
10926
|
-
const
|
|
10927
|
-
|
|
10928
|
-
|
|
10932
|
+
if (!fs18.existsSync(file)) return [];
|
|
10933
|
+
const raw = JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
10934
|
+
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.servers) ? raw.servers : [];
|
|
10935
|
+
return list.filter(isValidMcpConfig);
|
|
10936
|
+
} catch (err) {
|
|
10937
|
+
console.error(`[mcp] \u8BFB\u53D6\u914D\u7F6E\u5931\u8D25 ${file}: ${err.message}`);
|
|
10938
|
+
return [];
|
|
10929
10939
|
}
|
|
10930
|
-
return "dev";
|
|
10931
10940
|
}
|
|
10932
|
-
function
|
|
10933
|
-
if (process.env.
|
|
10934
|
-
const
|
|
10935
|
-
|
|
10941
|
+
function hasServerMcpConfig() {
|
|
10942
|
+
if (readConfigFile(process.env.MCP_CONFIG_PATH).length > 0) return true;
|
|
10943
|
+
const workspace = process.env.WORKSPACE;
|
|
10944
|
+
if (!workspace) return false;
|
|
10945
|
+
return readConfigFile(path21.join(workspace, ".agent", "mcp.json")).length > 0;
|
|
10936
10946
|
}
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
const idx = line.lastIndexOf(":");
|
|
10943
|
-
if (idx < 0) continue;
|
|
10944
|
-
const unitPath = line.slice(idx + 1).trim();
|
|
10945
|
-
if (unitPath.split("/").includes(unitName)) return "systemd";
|
|
10947
|
+
function loadServerMcpConfigs(tenant) {
|
|
10948
|
+
const fromEnv = readConfigFile(process.env.MCP_CONFIG_PATH);
|
|
10949
|
+
if (fromEnv.length > 0) return fromEnv;
|
|
10950
|
+
if (Array.isArray(tenant.mcpServers) && tenant.mcpServers.length > 0) {
|
|
10951
|
+
return tenant.mcpServers.filter(isValidMcpConfig);
|
|
10946
10952
|
}
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
return uid === 0 ? "system" : `gui/${uid}`;
|
|
10952
|
-
}
|
|
10953
|
-
function launchdLabel() {
|
|
10954
|
-
return process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL;
|
|
10953
|
+
const workspace = tenant.workspaceRoot ?? process.env.WORKSPACE;
|
|
10954
|
+
if (workspace)
|
|
10955
|
+
return readConfigFile(path21.join(workspace, ".agent", "mcp.json"));
|
|
10956
|
+
return [];
|
|
10955
10957
|
}
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10958
|
+
|
|
10959
|
+
// src/server/nodeLlm.ts
|
|
10960
|
+
init_esm_shims();
|
|
10961
|
+
var GLOBAL_PROVIDER_ID = "__env__";
|
|
10962
|
+
function envProvider() {
|
|
10963
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
10964
|
+
if (!apiKey) return null;
|
|
10965
|
+
return {
|
|
10966
|
+
id: GLOBAL_PROVIDER_ID,
|
|
10967
|
+
name: process.env.OPENAI_MODEL || process.env.MODEL || "\u5168\u5C40 LLM",
|
|
10968
|
+
type: "env",
|
|
10969
|
+
baseURL: process.env.OPENAI_BASE_URL ?? "",
|
|
10970
|
+
model: process.env.OPENAI_MODEL || process.env.MODEL || "",
|
|
10971
|
+
apiKey
|
|
10972
|
+
};
|
|
10961
10973
|
}
|
|
10962
|
-
function
|
|
10963
|
-
|
|
10964
|
-
if (pidMatch && Number(pidMatch[1]) === pid) return "launchd";
|
|
10965
|
-
const program = /^\s*program = (.+?)\s*$/m.exec(output);
|
|
10966
|
-
if (program) {
|
|
10967
|
-
const shown = program[1].replace(/^"|"$/g, "");
|
|
10968
|
-
if (path21.resolve(shown) === path21.resolve(execPath)) return "launchd";
|
|
10969
|
-
}
|
|
10970
|
-
if (!sea && /^\s*state = /m.test(output)) return "launchd";
|
|
10971
|
-
return null;
|
|
10974
|
+
function normReasoningEffort(v) {
|
|
10975
|
+
return v === "low" || v === "medium" || v === "high" ? v : null;
|
|
10972
10976
|
}
|
|
10973
|
-
function
|
|
10974
|
-
|
|
10975
|
-
const
|
|
10976
|
-
|
|
10977
|
-
const
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
out[m[1]] = m[2];
|
|
10977
|
+
function normalizeModelList(v) {
|
|
10978
|
+
if (!Array.isArray(v)) return void 0;
|
|
10979
|
+
const out = [];
|
|
10980
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10981
|
+
for (const x of v) {
|
|
10982
|
+
const s = String(x ?? "").trim();
|
|
10983
|
+
if (!s || seen.has(s)) continue;
|
|
10984
|
+
seen.add(s);
|
|
10985
|
+
out.push(s);
|
|
10986
|
+
if (out.length >= 500) break;
|
|
10984
10987
|
}
|
|
10985
10988
|
return out;
|
|
10986
10989
|
}
|
|
10987
|
-
function
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
|
|
10993
|
-
|
|
10994
|
-
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
|
|
10998
|
-
|
|
10990
|
+
function nodeLlmView(cfg) {
|
|
10991
|
+
const g = envProvider();
|
|
10992
|
+
const providers = [
|
|
10993
|
+
...g ? [
|
|
10994
|
+
{
|
|
10995
|
+
id: g.id,
|
|
10996
|
+
name: g.name,
|
|
10997
|
+
type: g.type,
|
|
10998
|
+
baseURL: g.baseURL,
|
|
10999
|
+
model: g.model,
|
|
11000
|
+
hasApiKey: true,
|
|
11001
|
+
global: true
|
|
11002
|
+
}
|
|
11003
|
+
] : [],
|
|
11004
|
+
...cfg.providers.map((p) => ({
|
|
11005
|
+
id: p.id,
|
|
11006
|
+
name: p.name,
|
|
11007
|
+
type: p.type ?? "",
|
|
11008
|
+
baseURL: p.baseURL,
|
|
11009
|
+
model: p.model,
|
|
11010
|
+
hasApiKey: Boolean(p.apiKey),
|
|
11011
|
+
models: p.models ?? [],
|
|
11012
|
+
nonChat: p.nonChat ?? [],
|
|
11013
|
+
modelMeta: p.modelMeta ?? [],
|
|
11014
|
+
reasoningEffort: p.reasoningEffort ?? null,
|
|
11015
|
+
global: false
|
|
11016
|
+
}))
|
|
11017
|
+
];
|
|
11018
|
+
return {
|
|
11019
|
+
defaultProviderId: cfg.defaultProviderId ?? null,
|
|
11020
|
+
providers,
|
|
11021
|
+
/** 未显式设置默认时,是否由环境变量全局配置兜底 */
|
|
11022
|
+
globalIsDefault: !cfg.defaultProviderId && Boolean(g)
|
|
11023
|
+
};
|
|
10999
11024
|
}
|
|
11000
|
-
function
|
|
11001
|
-
|
|
11002
|
-
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
|
|
11025
|
+
function applyNodeLlmPatch(cfg, raw) {
|
|
11026
|
+
const next = { providers: cfg.providers, defaultProviderId: cfg.defaultProviderId };
|
|
11027
|
+
if (Array.isArray(raw?.providers)) {
|
|
11028
|
+
const old = new Map(cfg.providers.map((p) => [p.id, p]));
|
|
11029
|
+
next.providers = raw.providers.filter((x) => Boolean(x) && typeof x === "object").filter((x) => x.global !== true && String(x.id ?? "") !== GLOBAL_PROVIDER_ID).map((x) => {
|
|
11030
|
+
const id = String(x.id ?? "").trim();
|
|
11031
|
+
if (!id) throw new ApiError(400, "bad_request", "\u4F9B\u5E94\u5546 id \u4E0D\u80FD\u4E3A\u7A7A");
|
|
11032
|
+
const prev = old.get(id);
|
|
11033
|
+
const apiKey = String(x.apiKey ?? "").trim();
|
|
11034
|
+
const models = normalizeModelList(x.models) ?? prev?.models;
|
|
11035
|
+
const nonChat = normalizeModelList(x.nonChat) ?? prev?.nonChat;
|
|
11036
|
+
const modelMeta = normalizeModelMeta(x.modelMeta) ?? prev?.modelMeta;
|
|
11037
|
+
return {
|
|
11038
|
+
id,
|
|
11039
|
+
name: String(x.name ?? prev?.name ?? id).trim(),
|
|
11040
|
+
type: x.type == null ? prev?.type : String(x.type),
|
|
11041
|
+
baseURL: String(x.baseURL ?? prev?.baseURL ?? "").trim(),
|
|
11042
|
+
model: String(x.model ?? prev?.model ?? "").trim(),
|
|
11043
|
+
apiKey: apiKey || prev?.apiKey,
|
|
11044
|
+
reasoningEffort: x.reasoningEffort === void 0 ? prev?.reasoningEffort ?? null : normReasoningEffort(x.reasoningEffort),
|
|
11045
|
+
...models ? { models } : {},
|
|
11046
|
+
...nonChat ? { nonChat } : {},
|
|
11047
|
+
...modelMeta ? { modelMeta } : {}
|
|
11048
|
+
};
|
|
11049
|
+
});
|
|
11016
11050
|
}
|
|
11017
|
-
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
if (
|
|
11021
|
-
|
|
11022
|
-
}
|
|
11023
|
-
function parseServiceEnvironment(raw) {
|
|
11024
|
-
const out = {};
|
|
11025
|
-
for (const token of raw.trim().split(/\s+/)) {
|
|
11026
|
-
const idx = token.indexOf("=");
|
|
11027
|
-
if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
|
|
11051
|
+
if (typeof raw?.defaultProviderId === "string") {
|
|
11052
|
+
const id = raw.defaultProviderId.trim();
|
|
11053
|
+
next.defaultProviderId = id || void 0;
|
|
11054
|
+
} else if (raw?.defaultProviderId === null) {
|
|
11055
|
+
next.defaultProviderId = void 0;
|
|
11028
11056
|
}
|
|
11029
|
-
return
|
|
11057
|
+
return next;
|
|
11030
11058
|
}
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
|
|
11034
|
-
|
|
11035
|
-
const res = spawnSync2(
|
|
11036
|
-
"systemctl",
|
|
11037
|
-
["show", serviceName, "--property=Environment", "--value", "--no-pager"],
|
|
11038
|
-
{ encoding: "utf8", timeout: 3e3 }
|
|
11039
|
-
);
|
|
11040
|
-
if (res.status !== 0 || !res.stdout) return {};
|
|
11041
|
-
return parseServiceEnvironment(res.stdout);
|
|
11042
|
-
} catch {
|
|
11043
|
-
return {};
|
|
11044
|
-
}
|
|
11059
|
+
function findNodeProvider(store, id) {
|
|
11060
|
+
if (!id) return null;
|
|
11061
|
+
if (id === GLOBAL_PROVIDER_ID) return envProvider();
|
|
11062
|
+
return store.loadNodeLlm().providers.find((p) => p.id === id) ?? null;
|
|
11045
11063
|
}
|
|
11046
|
-
function
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
return cachedLaunchdEnv[key];
|
|
11051
|
-
}
|
|
11052
|
-
if (process.platform !== "linux") return void 0;
|
|
11053
|
-
if (serviceManager() !== "systemd") return void 0;
|
|
11054
|
-
cachedServiceEnv ??= readServiceEnvironment();
|
|
11055
|
-
return cachedServiceEnv[key];
|
|
11056
|
-
}
|
|
11057
|
-
var cachedLaunchdEnv = null;
|
|
11058
|
-
function readLaunchdEnvironment() {
|
|
11059
|
-
try {
|
|
11060
|
-
return parseLaunchdEnvironment(fs18.readFileSync(launchdPlistPath(), "utf8"));
|
|
11061
|
-
} catch {
|
|
11062
|
-
return {};
|
|
11063
|
-
}
|
|
11064
|
+
function nodeDefaultProvider(store) {
|
|
11065
|
+
const cfg = store.loadNodeLlm();
|
|
11066
|
+
if (cfg.providers.length === 0) return null;
|
|
11067
|
+
return cfg.providers.find((p) => p.id === cfg.defaultProviderId) ?? cfg.providers[0] ?? null;
|
|
11064
11068
|
}
|
|
11065
|
-
function
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
|
|
11069
|
+
function resolveSessionProvider(store, tenant, providerId) {
|
|
11070
|
+
const own = tenant.provider ?? {};
|
|
11071
|
+
if (providerId) {
|
|
11072
|
+
const picked = findNodeProvider(store, providerId);
|
|
11073
|
+
if (picked) {
|
|
11074
|
+
return {
|
|
11075
|
+
apiKey: picked.apiKey || own.apiKey,
|
|
11076
|
+
baseURL: picked.baseURL || own.baseURL,
|
|
11077
|
+
model: picked.model,
|
|
11078
|
+
providerId,
|
|
11079
|
+
reasoningEffort: picked.reasoningEffort ?? null,
|
|
11080
|
+
...picked.modelMeta ? { modelMeta: picked.modelMeta } : {}
|
|
11081
|
+
};
|
|
11082
|
+
}
|
|
11071
11083
|
}
|
|
11072
|
-
if (
|
|
11084
|
+
if (own.apiKey) return { ...own, model: tenant.model };
|
|
11085
|
+
const def = nodeDefaultProvider(store);
|
|
11086
|
+
if (def?.apiKey) {
|
|
11073
11087
|
return {
|
|
11074
|
-
|
|
11075
|
-
|
|
11088
|
+
apiKey: def.apiKey,
|
|
11089
|
+
baseURL: own.baseURL || def.baseURL,
|
|
11090
|
+
model: def.model || tenant.model,
|
|
11091
|
+
reasoningEffort: def.reasoningEffort ?? null,
|
|
11092
|
+
...def.modelMeta ? { modelMeta: def.modelMeta } : {}
|
|
11076
11093
|
};
|
|
11077
11094
|
}
|
|
11078
|
-
|
|
11079
|
-
return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
|
|
11080
|
-
}
|
|
11081
|
-
return { ok: true };
|
|
11095
|
+
return { ...own, model: tenant.model };
|
|
11082
11096
|
}
|
|
11083
11097
|
|
|
11084
|
-
// src/
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
var
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
}
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
}
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
|
|
11098
|
+
// src/server/usage.ts
|
|
11099
|
+
init_esm_shims();
|
|
11100
|
+
import * as fs19 from "fs";
|
|
11101
|
+
import * as path22 from "path";
|
|
11102
|
+
var LEDGER_RETENTION_MONTHS = 12;
|
|
11103
|
+
var USAGE_SCHEMA = 1;
|
|
11104
|
+
var DEFAULT_PRICING = {
|
|
11105
|
+
schema: USAGE_SCHEMA,
|
|
11106
|
+
base: "USD",
|
|
11107
|
+
rate: { CNY: 7.2 },
|
|
11108
|
+
vendors: {
|
|
11109
|
+
openai: {
|
|
11110
|
+
"gpt-4o": { input: 2.5, output: 10, cacheRead: 1.25 },
|
|
11111
|
+
"gpt-4o-mini": { input: 0.15, output: 0.6, cacheRead: 0.075 },
|
|
11112
|
+
"gpt-4.1": { input: 2, output: 8, cacheRead: 0.5 }
|
|
11113
|
+
},
|
|
11114
|
+
deepseek: {
|
|
11115
|
+
"deepseek-flash": {
|
|
11116
|
+
input: 0.27,
|
|
11117
|
+
output: 1.1,
|
|
11118
|
+
cacheRead: 0.027,
|
|
11119
|
+
cacheWrite: 0.27
|
|
11120
|
+
},
|
|
11121
|
+
"deepseek-reasoner": {
|
|
11122
|
+
input: 0.55,
|
|
11123
|
+
output: 2.19,
|
|
11124
|
+
cacheRead: 0.055,
|
|
11125
|
+
cacheWrite: 0.55
|
|
11126
|
+
},
|
|
11127
|
+
"deepseek-*": {
|
|
11128
|
+
input: 0.27,
|
|
11129
|
+
output: 1.1,
|
|
11130
|
+
cacheRead: 0.027,
|
|
11131
|
+
cacheWrite: 0.27
|
|
11132
|
+
}
|
|
11133
|
+
},
|
|
11134
|
+
qwen: {
|
|
11135
|
+
"qwen-plus": { input: 0.4, output: 1.2 },
|
|
11136
|
+
"qwen-turbo": { input: 0.05, output: 0.2 }
|
|
11137
|
+
},
|
|
11138
|
+
zhipu: { "glm-4-plus": { input: 0.7, output: 0.7 } },
|
|
11139
|
+
moonshot: { "moonshot-v1-8k": { input: 1.68, output: 1.68 } },
|
|
11140
|
+
xiaomi: {},
|
|
11141
|
+
custom: {},
|
|
11142
|
+
env: {}
|
|
11143
|
+
},
|
|
11144
|
+
fallback: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
11145
|
+
};
|
|
11146
|
+
function zeroAgg() {
|
|
11147
|
+
return {
|
|
11148
|
+
calls: 0,
|
|
11149
|
+
input: 0,
|
|
11150
|
+
output: 0,
|
|
11151
|
+
cacheRead: 0,
|
|
11152
|
+
cacheWrite: 0,
|
|
11153
|
+
costUsd: 0,
|
|
11154
|
+
unpriced: 0
|
|
11126
11155
|
};
|
|
11127
|
-
if (explicitDataDir) push({ dataDir: path22.resolve(explicitDataDir), ...fallback });
|
|
11128
|
-
for (const inst of discovered) if (inst.managed) push(inst);
|
|
11129
|
-
for (const inst of discovered) if (!inst.managed) push(inst);
|
|
11130
|
-
if (!explicitDataDir) push({ dataDir: path22.resolve(DEFAULT_DATA_DIR), ...fallback });
|
|
11131
|
-
return out;
|
|
11132
|
-
}
|
|
11133
|
-
function isWindowsAbsolute(p) {
|
|
11134
|
-
return /^[A-Za-z]:[\\/]/.test(p) || /^\\\\/.test(p);
|
|
11135
|
-
}
|
|
11136
|
-
function isAbsolutePath(p) {
|
|
11137
|
-
return path22.isAbsolute(p) || isWindowsAbsolute(p);
|
|
11138
11156
|
}
|
|
11139
|
-
function
|
|
11140
|
-
|
|
11157
|
+
function addAgg(into, tokens, costUsd) {
|
|
11158
|
+
into.calls += 1;
|
|
11159
|
+
into.input += tokens.input;
|
|
11160
|
+
into.output += tokens.output;
|
|
11161
|
+
into.cacheRead += tokens.cacheRead;
|
|
11162
|
+
into.cacheWrite += tokens.cacheWrite;
|
|
11163
|
+
if (costUsd == null) into.unpriced += 1;
|
|
11164
|
+
else into.costUsd += costUsd;
|
|
11141
11165
|
}
|
|
11142
|
-
function
|
|
11143
|
-
|
|
11144
|
-
const m = /--data-dir(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(.+?))(?=\s+--\w|\s*$)/.exec(line);
|
|
11145
|
-
const rawDataDir = (m?.[1] ?? m?.[2] ?? m?.[3] ?? "").trim();
|
|
11146
|
-
if (!rawDataDir || !isAbsolutePath(rawDataDir)) return null;
|
|
11147
|
-
const host = /--host(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(\S+))/.exec(line);
|
|
11148
|
-
const rawHost = (host?.[1] ?? host?.[2] ?? host?.[3] ?? DEFAULT_SERVE_HOST).trim();
|
|
11149
|
-
const portMatch = /--port(?:=|\s+)(\d+)/.exec(line);
|
|
11150
|
-
const port = Number(portMatch?.[1] ?? DEFAULT_SERVE_PORT);
|
|
11151
|
-
return {
|
|
11152
|
-
dataDir: normalizePath(rawDataDir),
|
|
11153
|
-
host: rawHost === "0.0.0.0" || rawHost === "::" || rawHost === "" ? DEFAULT_SERVE_HOST : rawHost,
|
|
11154
|
-
port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT
|
|
11155
|
-
};
|
|
11166
|
+
function utcDay(iso) {
|
|
11167
|
+
return iso.slice(0, 10);
|
|
11156
11168
|
}
|
|
11157
|
-
function
|
|
11158
|
-
|
|
11159
|
-
return fs19.readlinkSync(`/proc/${pid}/cwd`);
|
|
11160
|
-
} catch {
|
|
11161
|
-
return null;
|
|
11162
|
-
}
|
|
11169
|
+
function monthOf(iso) {
|
|
11170
|
+
return iso.slice(0, 7);
|
|
11163
11171
|
}
|
|
11164
|
-
function
|
|
11165
|
-
const out =
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
|
|
11170
|
-
if (eq > 0) out[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
11171
|
-
}
|
|
11172
|
-
} catch {
|
|
11172
|
+
function retainedMonths(now = /* @__PURE__ */ new Date()) {
|
|
11173
|
+
const out = /* @__PURE__ */ new Set();
|
|
11174
|
+
const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
11175
|
+
for (let i = 0; i < LEDGER_RETENTION_MONTHS; i += 1) {
|
|
11176
|
+
out.add(d.toISOString().slice(0, 7));
|
|
11177
|
+
d.setUTCMonth(d.getUTCMonth() - 1);
|
|
11173
11178
|
}
|
|
11174
11179
|
return out;
|
|
11175
11180
|
}
|
|
11176
|
-
function
|
|
11177
|
-
|
|
11178
|
-
|
|
11179
|
-
|
|
11181
|
+
function atomicWrite(file, content) {
|
|
11182
|
+
fs19.mkdirSync(path22.dirname(file), { recursive: true });
|
|
11183
|
+
const tmp = `${file}.tmp`;
|
|
11184
|
+
fs19.writeFileSync(tmp, content, "utf-8");
|
|
11185
|
+
fs19.renameSync(tmp, file);
|
|
11180
11186
|
}
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
const serviceName = env3.ZQAGENT_SERVICE_NAME?.trim() || "zqagent";
|
|
11186
|
-
return serviceManagerFromCgroup(cgroup, serviceName) !== null;
|
|
11187
|
-
} catch {
|
|
11188
|
-
return false;
|
|
11187
|
+
var UsageLedger = class {
|
|
11188
|
+
constructor(root) {
|
|
11189
|
+
this.root = root;
|
|
11190
|
+
this.dir = path22.join(root, "usage");
|
|
11189
11191
|
}
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
|
|
11192
|
+
root;
|
|
11193
|
+
dir;
|
|
11194
|
+
rollup;
|
|
11195
|
+
pricing;
|
|
11196
|
+
rollupDirty = false;
|
|
11197
|
+
rollupTimer = null;
|
|
11198
|
+
inited = false;
|
|
11199
|
+
get ledgerDir() {
|
|
11200
|
+
return this.dir;
|
|
11198
11201
|
}
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
const pid = Number(name);
|
|
11202
|
-
if (pid === process.pid) continue;
|
|
11203
|
-
let argv;
|
|
11204
|
-
let env3;
|
|
11205
|
-
try {
|
|
11206
|
-
argv = fs19.readFileSync(`/proc/${pid}/cmdline`, "utf-8").split("\0").filter(Boolean);
|
|
11207
|
-
env3 = processEnv(pid);
|
|
11208
|
-
} catch {
|
|
11209
|
-
continue;
|
|
11210
|
-
}
|
|
11211
|
-
const parsed = parseServeArgs(argv);
|
|
11212
|
-
if (!parsed) continue;
|
|
11213
|
-
const dataDir = resolveDataDir(parsed.dataDir, processCwd(pid), env3);
|
|
11214
|
-
if (!dataDir) continue;
|
|
11215
|
-
out.push({
|
|
11216
|
-
dataDir,
|
|
11217
|
-
host: parsed.host,
|
|
11218
|
-
port: parsed.port,
|
|
11219
|
-
managed: isManagedBySystemd(pid, env3)
|
|
11220
|
-
});
|
|
11202
|
+
ledgerFile(month) {
|
|
11203
|
+
return path22.join(this.ledgerDir, `ledger-${month}.jsonl`);
|
|
11221
11204
|
}
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
function discoverFromPs() {
|
|
11225
|
-
const out = [];
|
|
11226
|
-
const res = spawnSync3("ps", ["-axo", "command="], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
11227
|
-
if (res.status !== 0 || !res.stdout) return out;
|
|
11228
|
-
for (const line of res.stdout.split("\n")) {
|
|
11229
|
-
const inst = parseServeCommandLine(line);
|
|
11230
|
-
if (inst) out.push(inst);
|
|
11205
|
+
get rollupFile() {
|
|
11206
|
+
return path22.join(this.ledgerDir, "rollup.json");
|
|
11231
11207
|
}
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
function discoverFromWindows() {
|
|
11235
|
-
const out = [];
|
|
11236
|
-
const script = `[Console]::OutputEncoding=[Text.Encoding]::UTF8; Get-CimInstance Win32_Process -Filter "Name='zqagent.exe' OR Name='node.exe'" | ForEach-Object { $_.CommandLine }`;
|
|
11237
|
-
const res = spawnSync3(
|
|
11238
|
-
"powershell.exe",
|
|
11239
|
-
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
11240
|
-
{ encoding: "utf-8", timeout: 15e3, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
|
|
11241
|
-
);
|
|
11242
|
-
if (res.status !== 0 || !res.stdout) return out;
|
|
11243
|
-
for (const line of res.stdout.replace(/^\uFEFF/, "").split(/\r?\n/)) {
|
|
11244
|
-
const inst = parseServeCommandLine(line);
|
|
11245
|
-
if (inst) out.push(inst);
|
|
11208
|
+
get pricingFile() {
|
|
11209
|
+
return path22.join(this.ledgerDir, "pricing.json");
|
|
11246
11210
|
}
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
const close = xml.indexOf("</array>", open);
|
|
11258
|
-
if (close < 0) return [];
|
|
11259
|
-
return [...xml.slice(open + "<array>".length, close).matchAll(/<string>([^<]*)<\/string>/g)].map(
|
|
11260
|
-
(m) => decodeXmlEntities(m[1])
|
|
11261
|
-
);
|
|
11262
|
-
}
|
|
11263
|
-
function launchdServiceInstances() {
|
|
11264
|
-
if (process.platform !== "darwin") return [];
|
|
11265
|
-
const label = process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL2;
|
|
11266
|
-
const plists = [
|
|
11267
|
-
`/Library/LaunchDaemons/${label}.plist`,
|
|
11268
|
-
path22.join(os5.homedir(), "Library", "LaunchAgents", `${label}.plist`)
|
|
11269
|
-
];
|
|
11270
|
-
const out = [];
|
|
11271
|
-
for (const plist of plists) {
|
|
11272
|
-
let xml;
|
|
11211
|
+
// ===================== 初始化 =====================
|
|
11212
|
+
init(now = /* @__PURE__ */ new Date()) {
|
|
11213
|
+
if (this.inited) return;
|
|
11214
|
+
fs19.mkdirSync(this.ledgerDir, { recursive: true });
|
|
11215
|
+
this.pricing = this.loadPricing();
|
|
11216
|
+
this.rollup = this.loadRollup(now);
|
|
11217
|
+
this.pruneLedgers(now);
|
|
11218
|
+
this.inited = true;
|
|
11219
|
+
}
|
|
11220
|
+
loadPricing() {
|
|
11273
11221
|
try {
|
|
11274
|
-
|
|
11222
|
+
const raw = fs19.readFileSync(this.pricingFile, "utf-8");
|
|
11223
|
+
const parsed = JSON.parse(raw);
|
|
11224
|
+
const vendors = {};
|
|
11225
|
+
for (const v of /* @__PURE__ */ new Set([
|
|
11226
|
+
...Object.keys(DEFAULT_PRICING.vendors),
|
|
11227
|
+
...Object.keys(parsed.vendors ?? {})
|
|
11228
|
+
])) {
|
|
11229
|
+
vendors[v] = {
|
|
11230
|
+
...DEFAULT_PRICING.vendors[v] ?? {},
|
|
11231
|
+
...(parsed.vendors ?? {})[v] ?? {}
|
|
11232
|
+
};
|
|
11233
|
+
}
|
|
11234
|
+
return {
|
|
11235
|
+
schema: USAGE_SCHEMA,
|
|
11236
|
+
base: "USD",
|
|
11237
|
+
rate: { ...DEFAULT_PRICING.rate, ...parsed.rate ?? {} },
|
|
11238
|
+
vendors,
|
|
11239
|
+
fallback: { ...DEFAULT_PRICING.fallback, ...parsed.fallback ?? {} }
|
|
11240
|
+
};
|
|
11275
11241
|
} catch {
|
|
11276
|
-
|
|
11242
|
+
try {
|
|
11243
|
+
atomicWrite(this.pricingFile, JSON.stringify(DEFAULT_PRICING, null, 2));
|
|
11244
|
+
} catch {
|
|
11245
|
+
}
|
|
11246
|
+
return JSON.parse(JSON.stringify(DEFAULT_PRICING));
|
|
11277
11247
|
}
|
|
11278
|
-
const parsed = parseServeArgs(parseLaunchdProgramArguments(xml));
|
|
11279
|
-
if (!parsed?.dataDir || !isAbsolutePath(parsed.dataDir)) continue;
|
|
11280
|
-
out.push({
|
|
11281
|
-
dataDir: normalizePath(parsed.dataDir),
|
|
11282
|
-
host: parsed.host,
|
|
11283
|
-
port: parsed.port,
|
|
11284
|
-
managed: true
|
|
11285
|
-
});
|
|
11286
11248
|
}
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11249
|
+
loadRollup(now) {
|
|
11250
|
+
try {
|
|
11251
|
+
const raw = fs19.readFileSync(this.rollupFile, "utf-8");
|
|
11252
|
+
const parsed = JSON.parse(raw);
|
|
11253
|
+
return {
|
|
11254
|
+
schema: USAGE_SCHEMA,
|
|
11255
|
+
since: parsed.since ?? now.toISOString(),
|
|
11256
|
+
legacy: parsed.legacy,
|
|
11257
|
+
total: { ...zeroAgg(), ...parsed.total },
|
|
11258
|
+
byDay: parsed.byDay ?? {},
|
|
11259
|
+
byModel: parsed.byModel ?? {},
|
|
11260
|
+
byProject: parsed.byProject ?? {}
|
|
11261
|
+
};
|
|
11262
|
+
} catch {
|
|
11263
|
+
return {
|
|
11264
|
+
schema: USAGE_SCHEMA,
|
|
11265
|
+
since: now.toISOString(),
|
|
11266
|
+
total: zeroAgg(),
|
|
11267
|
+
byDay: {},
|
|
11268
|
+
byModel: {},
|
|
11269
|
+
byProject: {}
|
|
11270
|
+
};
|
|
11305
11271
|
}
|
|
11306
11272
|
}
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
if (process.platform !== "linux") return [];
|
|
11312
|
-
const unit = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME2;
|
|
11313
|
-
const configHome = process.env.XDG_CONFIG_HOME?.trim() || path22.join(os5.homedir(), ".config");
|
|
11314
|
-
const units = [
|
|
11315
|
-
`/etc/systemd/system/${unit}.service`,
|
|
11316
|
-
path22.join(configHome, "systemd", "user", `${unit}.service`)
|
|
11317
|
-
];
|
|
11318
|
-
const out = [];
|
|
11319
|
-
for (const file of units) {
|
|
11320
|
-
let text;
|
|
11273
|
+
/** 删除超出保留期的账本文件,并裁剪 byDay */
|
|
11274
|
+
pruneLedgers(now) {
|
|
11275
|
+
const keep = retainedMonths(now);
|
|
11276
|
+
let changed = false;
|
|
11321
11277
|
try {
|
|
11322
|
-
|
|
11278
|
+
for (const f of fs19.readdirSync(this.ledgerDir)) {
|
|
11279
|
+
const m = /^ledger-(\d{4}-\d{2})\.jsonl$/.exec(f);
|
|
11280
|
+
if (m && !keep.has(m[1])) {
|
|
11281
|
+
fs19.unlinkSync(path22.join(this.ledgerDir, f));
|
|
11282
|
+
}
|
|
11283
|
+
}
|
|
11323
11284
|
} catch {
|
|
11324
|
-
continue;
|
|
11325
11285
|
}
|
|
11326
|
-
const
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
}
|
|
11331
|
-
function windowsServiceInstances() {
|
|
11332
|
-
if (process.platform !== "win32") return [];
|
|
11333
|
-
const appdata = process.env.APPDATA?.trim();
|
|
11334
|
-
if (!appdata) return [];
|
|
11335
|
-
const base = path22.join(appdata, "zqagent");
|
|
11336
|
-
try {
|
|
11337
|
-
const raw = fs19.readFileSync(path22.join(base, "runtime", "service.json"), "utf-8");
|
|
11338
|
-
const v = JSON.parse(raw);
|
|
11339
|
-
const dataDir = v.dataDir && isAbsolutePath(v.dataDir) ? v.dataDir : base;
|
|
11340
|
-
const port = Number(v.port);
|
|
11341
|
-
return [
|
|
11342
|
-
{
|
|
11343
|
-
dataDir: normalizePath(dataDir),
|
|
11344
|
-
host: DEFAULT_SERVE_HOST,
|
|
11345
|
-
port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT,
|
|
11346
|
-
managed: true
|
|
11286
|
+
for (const day of Object.keys(this.rollup.byDay)) {
|
|
11287
|
+
if (!keep.has(day.slice(0, 7))) {
|
|
11288
|
+
delete this.rollup.byDay[day];
|
|
11289
|
+
changed = true;
|
|
11347
11290
|
}
|
|
11348
|
-
];
|
|
11349
|
-
} catch {
|
|
11350
|
-
return [];
|
|
11351
|
-
}
|
|
11352
|
-
}
|
|
11353
|
-
function mergeServeInstances(discovered, managed) {
|
|
11354
|
-
const out = [];
|
|
11355
|
-
const push = (inst) => {
|
|
11356
|
-
if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
|
|
11357
|
-
};
|
|
11358
|
-
for (const inst of discovered) {
|
|
11359
|
-
const hit = managed.find((m) => m.dataDir === inst.dataDir);
|
|
11360
|
-
push(hit ? { ...inst, managed: true } : inst);
|
|
11361
|
-
}
|
|
11362
|
-
for (const inst of managed) push(inst);
|
|
11363
|
-
return out;
|
|
11364
|
-
}
|
|
11365
|
-
function discoverRunningServeInstances() {
|
|
11366
|
-
const found = process.platform === "linux" ? discoverFromProc() : process.platform === "win32" ? discoverFromWindows() : discoverFromPs();
|
|
11367
|
-
const declared = process.platform === "linux" ? systemdServiceInstances() : process.platform === "win32" ? windowsServiceInstances() : launchdServiceInstances();
|
|
11368
|
-
return mergeServeInstances(found, declared);
|
|
11369
|
-
}
|
|
11370
|
-
|
|
11371
|
-
// src/cli/link.ts
|
|
11372
|
-
function expandHome(p) {
|
|
11373
|
-
const home = process.env.HOME || process.env.USERPROFILE;
|
|
11374
|
-
if (!home) return p;
|
|
11375
|
-
if (p === "~") return home;
|
|
11376
|
-
if (p.startsWith("~/") || p.startsWith("~\\")) return path23.join(home, p.slice(2));
|
|
11377
|
-
return p;
|
|
11378
|
-
}
|
|
11379
|
-
function installDataDirHint() {
|
|
11380
|
-
if (process.platform === "darwin") return "$HOME/Library/Application Support/zqagent";
|
|
11381
|
-
if (process.platform === "win32") return "%APPDATA%\\zqagent";
|
|
11382
|
-
return "/var/lib/zqagent\uFF08\u9700 root\uFF1B\u666E\u901A\u7528\u6237\u7528 ~/.zqagent_data\uFF09";
|
|
11383
|
-
}
|
|
11384
|
-
function parseLinkArgs(argv) {
|
|
11385
|
-
const out = {};
|
|
11386
|
-
for (let i = 0; i < argv.length; i++) {
|
|
11387
|
-
const a = argv[i];
|
|
11388
|
-
const val = argv[i + 1];
|
|
11389
|
-
if (a === "--hub") {
|
|
11390
|
-
out.hubUrl = val;
|
|
11391
|
-
i++;
|
|
11392
|
-
} else if (a === "--code") {
|
|
11393
|
-
out.code = val;
|
|
11394
|
-
i++;
|
|
11395
|
-
} else if (a === "--data-dir") {
|
|
11396
|
-
out.dataDir = val;
|
|
11397
|
-
i++;
|
|
11398
|
-
} else if (a === "--label") {
|
|
11399
|
-
out.label = val;
|
|
11400
|
-
i++;
|
|
11401
|
-
} else if (a === "--host") {
|
|
11402
|
-
out.host = val;
|
|
11403
|
-
i++;
|
|
11404
|
-
} else if (a === "--port") {
|
|
11405
|
-
out.port = Number(val);
|
|
11406
|
-
i++;
|
|
11407
|
-
} else if (a === "-h" || a === "--help") {
|
|
11408
|
-
out.help = true;
|
|
11409
11291
|
}
|
|
11292
|
+
if (changed) this.saveRollupNow();
|
|
11410
11293
|
}
|
|
11411
|
-
|
|
11412
|
-
|
|
11413
|
-
|
|
11414
|
-
let id = store.loadNodeId();
|
|
11415
|
-
if (!id) {
|
|
11416
|
-
id = newId();
|
|
11417
|
-
store.saveNodeId(id);
|
|
11418
|
-
}
|
|
11419
|
-
return id;
|
|
11420
|
-
}
|
|
11421
|
-
async function probeServer(host, port, adminKey) {
|
|
11422
|
-
const ctrl = new AbortController();
|
|
11423
|
-
const timer = setTimeout(() => ctrl.abort(), 1500);
|
|
11424
|
-
try {
|
|
11425
|
-
const res = await fetch(`http://${host}:${port}/api/v1/fleet/status`, {
|
|
11426
|
-
headers: { Authorization: `Bearer ${adminKey}` },
|
|
11427
|
-
signal: ctrl.signal
|
|
11428
|
-
});
|
|
11429
|
-
return res.ok;
|
|
11430
|
-
} catch {
|
|
11431
|
-
return false;
|
|
11432
|
-
} finally {
|
|
11433
|
-
clearTimeout(timer);
|
|
11294
|
+
// ===================== 价格 =====================
|
|
11295
|
+
getPricing() {
|
|
11296
|
+
return this.pricing;
|
|
11434
11297
|
}
|
|
11435
|
-
|
|
11436
|
-
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
|
|
11440
|
-
|
|
11441
|
-
|
|
11442
|
-
|
|
11443
|
-
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
let payload = null;
|
|
11447
|
-
try {
|
|
11448
|
-
payload = text ? JSON.parse(text) : null;
|
|
11449
|
-
} catch {
|
|
11298
|
+
/** 更新价格/汇率(整表覆盖式合并) */
|
|
11299
|
+
updatePricing(patch) {
|
|
11300
|
+
this.pricing = {
|
|
11301
|
+
schema: USAGE_SCHEMA,
|
|
11302
|
+
base: "USD",
|
|
11303
|
+
rate: { ...this.pricing.rate, ...patch.rate ?? {} },
|
|
11304
|
+
vendors: { ...this.pricing.vendors, ...patch.vendors ?? {} },
|
|
11305
|
+
fallback: { ...this.pricing.fallback, ...patch.fallback ?? {} }
|
|
11306
|
+
};
|
|
11307
|
+
atomicWrite(this.pricingFile, JSON.stringify(this.pricing, null, 2));
|
|
11308
|
+
return this.pricing;
|
|
11450
11309
|
}
|
|
11451
|
-
|
|
11452
|
-
|
|
11310
|
+
priceFor(vendor, model) {
|
|
11311
|
+
const table = this.pricing.vendors[vendor];
|
|
11312
|
+
if (table) {
|
|
11313
|
+
if (table[model]) return table[model];
|
|
11314
|
+
for (const key of Object.keys(table)) {
|
|
11315
|
+
if (key.endsWith("*") && model.startsWith(key.slice(0, -1)))
|
|
11316
|
+
return table[key];
|
|
11317
|
+
}
|
|
11318
|
+
}
|
|
11319
|
+
for (const v of Object.values(this.pricing.vendors)) {
|
|
11320
|
+
if (v[model]) return v[model];
|
|
11321
|
+
}
|
|
11322
|
+
for (const v of Object.values(this.pricing.vendors)) {
|
|
11323
|
+
for (const key of Object.keys(v)) {
|
|
11324
|
+
if (key.endsWith("*") && model.startsWith(key.slice(0, -1)))
|
|
11325
|
+
return v[key];
|
|
11326
|
+
}
|
|
11327
|
+
}
|
|
11328
|
+
return null;
|
|
11453
11329
|
}
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
);
|
|
11463
|
-
warnings.push(
|
|
11464
|
-
"\u6258\u7BA1\u670D\u52A1\u8BFB\u4E0D\u5230\u8FD9\u4EFD\u51ED\u636E\uFF08\u91CD\u542F\u540E\u4E5F\u4E0D\u4F1A\u81EA\u52A8\u4E0A\u7EBF\uFF09\uFF0C\u8BF7\u7528 --data-dir \u6307\u5411\u5B83\u7684\u6570\u636E\u76EE\u5F55\u91CD\u65B0\u63A5\u5165\u3002"
|
|
11465
|
-
);
|
|
11330
|
+
/** 成本(USD):input 已含 cacheRead,故按非缓存部分计价 */
|
|
11331
|
+
costOf(vendor, model, t) {
|
|
11332
|
+
const p = this.priceFor(vendor, model);
|
|
11333
|
+
if (!p) return null;
|
|
11334
|
+
const cached3 = Math.min(t.cacheRead, t.input);
|
|
11335
|
+
const fresh = Math.max(0, t.input - cached3);
|
|
11336
|
+
const M = 1e6;
|
|
11337
|
+
const cost = (fresh * p.input + cached3 * (p.cacheRead ?? p.input) + t.output * p.output + t.cacheWrite * (p.cacheWrite ?? 0)) / M;
|
|
11338
|
+
return Number.isFinite(cost) ? cost : null;
|
|
11466
11339
|
}
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11340
|
+
convert(usd, currency) {
|
|
11341
|
+
if (!currency || currency === "USD") return usd;
|
|
11342
|
+
const r = this.pricing.rate[currency];
|
|
11343
|
+
return typeof r === "number" && r > 0 ? usd * r : usd;
|
|
11470
11344
|
}
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
const store2 = new Store(inst.dataDir);
|
|
11482
|
-
const adminKey = process.env.ADMIN_API_KEY ?? store2.loadAdminKey();
|
|
11483
|
-
if (!adminKey) continue;
|
|
11484
|
-
if (!await probeServer(inst.host, inst.port, adminKey)) continue;
|
|
11485
|
-
const status = await enrollViaServer(inst.host, inst.port, adminKey, opts);
|
|
11486
|
-
return {
|
|
11487
|
-
mode: "hot",
|
|
11488
|
-
...status,
|
|
11489
|
-
dataDir: inst.dataDir,
|
|
11490
|
-
managed: inst.managed,
|
|
11491
|
-
warnings: hotWarnings(inst, running)
|
|
11345
|
+
// ===================== 记账 =====================
|
|
11346
|
+
/** 追加一条用量(串行 + appendFileSync;同步返回,便于在事件回调里调用) */
|
|
11347
|
+
record(input) {
|
|
11348
|
+
this.ensureInit();
|
|
11349
|
+
const costUsd = this.costOf(input.vendor, input.model, input);
|
|
11350
|
+
const rec = {
|
|
11351
|
+
...input,
|
|
11352
|
+
id: input.id ?? `${input.sessionId}:${input.ts}:${Math.random().toString(36).slice(2, 8)}`,
|
|
11353
|
+
costUsd,
|
|
11354
|
+
priced: costUsd != null
|
|
11492
11355
|
};
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
const nodeId = ensureNodeId(store);
|
|
11499
|
-
const cred = await hubEnrollByCode(opts.hubUrl, {
|
|
11500
|
-
code: opts.code,
|
|
11501
|
-
nodeId,
|
|
11502
|
-
label: opts.label
|
|
11503
|
-
});
|
|
11504
|
-
try {
|
|
11505
|
-
store.saveFleetCredential({
|
|
11506
|
-
...cred,
|
|
11507
|
-
label: opts.label ?? cred.label,
|
|
11508
|
-
enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11509
|
-
});
|
|
11510
|
-
} catch (e) {
|
|
11511
|
-
const err = e;
|
|
11512
|
-
if (err.code === "EACCES" || err.code === "EPERM") {
|
|
11513
|
-
throw new Error(
|
|
11514
|
-
`\u65E0\u6CD5\u5199\u5165\u6570\u636E\u76EE\u5F55 ${dataDir}\uFF08${err.code}: ${err.message}\uFF09
|
|
11515
|
-
\u8BE5\u76EE\u5F55\u5F53\u524D\u7528\u6237\u6CA1\u6709\u5199\u6743\u9650\u3002\u4E24\u79CD\u6539\u6CD5\uFF1A
|
|
11516
|
-
1) \u53BB\u6389 --data-dir\uFF0C\u8BA9 link \u81EA\u52A8\u53D1\u73B0\u672C\u673A\u8FD0\u884C\u7684\u8282\u70B9\uFF08\u63A8\u8350\uFF09\uFF1B
|
|
11517
|
-
2) \u7528\u5F53\u524D\u7528\u6237\u53EF\u5199\u7684\u76EE\u5F55\uFF0C\u672C\u5E73\u53F0\u5B89\u88C5\u811A\u672C\u9ED8\u8BA4\uFF1A${installDataDirHint()}
|
|
11518
|
-
\uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF09`
|
|
11519
|
-
);
|
|
11356
|
+
const line = JSON.stringify(rec) + "\n";
|
|
11357
|
+
try {
|
|
11358
|
+
fs19.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
|
|
11359
|
+
} catch (e) {
|
|
11360
|
+
console.error(`[usage] \u5199\u8D26\u672C\u5931\u8D25: ${String(e)}`);
|
|
11520
11361
|
}
|
|
11521
|
-
|
|
11362
|
+
addAgg(this.rollup.total, rec, costUsd);
|
|
11363
|
+
const day = this.rollup.byDay[utcDay(rec.ts)] ?? (this.rollup.byDay[utcDay(rec.ts)] = zeroAgg());
|
|
11364
|
+
addAgg(day, rec, costUsd);
|
|
11365
|
+
const mk = `${rec.vendor}/${rec.model}`;
|
|
11366
|
+
const bm = this.rollup.byModel[mk] ?? (this.rollup.byModel[mk] = zeroAgg());
|
|
11367
|
+
addAgg(bm, rec, costUsd);
|
|
11368
|
+
const bp = this.rollup.byProject[rec.projectId] ?? (this.rollup.byProject[rec.projectId] = zeroAgg());
|
|
11369
|
+
addAgg(bp, rec, costUsd);
|
|
11370
|
+
this.scheduleRollupSave();
|
|
11371
|
+
return rec;
|
|
11522
11372
|
}
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
function printLinkHelp() {
|
|
11538
|
-
cliLog("\u7528\u6CD5: zqagent link --hub <url> --code <\u6CE8\u518C\u7801> [--label \u540D\u79F0]");
|
|
11539
|
-
cliLog(" \u5728 Hub \u7528\u6237\u4E2D\u5FC3\u751F\u6210\u4E00\u6B21\u6027\u6CE8\u518C\u7801\u540E\uFF0C\u4E8E\u8282\u70B9\u673A\u5668\u6267\u884C\u672C\u547D\u4EE4\u5373\u53EF\u63A5\u5165\u3002");
|
|
11540
|
-
cliLog(" \u672C\u673A\u5DF2\u88C5\u8282\u70B9\u670D\u52A1\uFF08install.sh / \u684C\u9762\u7AEF\uFF09\u65F6\u4F1A\u81EA\u52A8\u53D1\u73B0\u5B83\u7684\u6570\u636E\u76EE\u5F55\u5E76\u70ED\u63A5\u5165\uFF0C");
|
|
11541
|
-
cliLog(" \u4E0D\u9700\u8981\u624B\u52A8\u6307\u5B9A --data-dir\uFF1B\u670D\u52A1\u6CA1\u5728\u8FD0\u884C\u65F6\u4F1A\u5199\u5165\u5B83\u7684\u76EE\u5F55\uFF0C\u4E0B\u6B21\u542F\u52A8\u5373\u63A5\u5165\u3002");
|
|
11542
|
-
cliLog(" \u4EC5\u5F53\u672C\u673A\u5B8C\u5168\u6CA1\u6709\u670D\u52A1\u65F6\u624D\u9700\u8981 --data-dir \u6307\u5B9A\u76EE\u5F55\uFF08\u4E0B\u6B21\u542F\u52A8 serve \u65F6\u751F\u6548\uFF09\u3002");
|
|
11543
|
-
}
|
|
11544
|
-
async function runLinkCommand(argv) {
|
|
11545
|
-
const args = parseLinkArgs(argv);
|
|
11546
|
-
if (args.help) {
|
|
11547
|
-
printLinkHelp();
|
|
11548
|
-
return true;
|
|
11373
|
+
/** 写入历史基线(仅一次;迁移自既有消息 usage) */
|
|
11374
|
+
seedLegacy(agg, throughTs) {
|
|
11375
|
+
this.ensureInit();
|
|
11376
|
+
if (this.rollup.legacy) return;
|
|
11377
|
+
this.rollup.legacy = { ...agg, throughTs };
|
|
11378
|
+
if (throughTs < this.rollup.since) this.rollup.since = throughTs;
|
|
11379
|
+
this.rollup.total.calls += agg.calls;
|
|
11380
|
+
this.rollup.total.input += agg.input;
|
|
11381
|
+
this.rollup.total.output += agg.output;
|
|
11382
|
+
this.rollup.total.cacheRead += agg.cacheRead;
|
|
11383
|
+
this.rollup.total.cacheWrite += agg.cacheWrite;
|
|
11384
|
+
this.rollup.total.costUsd += agg.costUsd;
|
|
11385
|
+
this.rollup.total.unpriced += agg.unpriced;
|
|
11386
|
+
this.saveRollupNow();
|
|
11549
11387
|
}
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
if (!hubUrl || !code) {
|
|
11553
|
-
cliErr("\u7F3A\u5C11 --hub \u6216 --code\u3002");
|
|
11554
|
-
printLinkHelp();
|
|
11555
|
-
return false;
|
|
11388
|
+
ensureInit() {
|
|
11389
|
+
if (!this.inited) this.init();
|
|
11556
11390
|
}
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
cliLog("");
|
|
11573
|
-
for (const w of r.warnings) cliLog(`\u26A0\uFE0F ${w}`);
|
|
11574
|
-
}
|
|
11575
|
-
} else {
|
|
11576
|
-
cliLog("\u2705 \u63A5\u5165\u51ED\u636E\u5DF2\u4FDD\u5B58\uFF08\u672C\u673A\u6682\u65E0\u8FD0\u884C\u4E2D\u7684 server\uFF09");
|
|
11577
|
-
cliLog(` \u8282\u70B9: ${r.nodeId}`);
|
|
11578
|
-
cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"}`);
|
|
11579
|
-
cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}`);
|
|
11580
|
-
cliLog(" \u4E0B\u6B21\u542F\u52A8 server \u5373\u81EA\u52A8\u63A5\u5165\uFF1B\u6216\u8FD0\u884C zqagent serve \u5E38\u9A7B\u4E3A\u8282\u70B9\u3002");
|
|
11581
|
-
if (r.targetedService) {
|
|
11582
|
-
cliLog("");
|
|
11583
|
-
cliLog("\u2705 \u51ED\u636E\u5DF2\u5199\u5165\u672C\u673A\u6258\u7BA1\u670D\u52A1\u7684\u6570\u636E\u76EE\u5F55\uFF0C\u670D\u52A1\u542F\u52A8\u65F6\u81EA\u52A8\u63A5\u5165\uFF0C\u65E0\u9700 --data-dir\u3002");
|
|
11584
|
-
cliLog(" \u7ACB\u5373\u751F\u6548\uFF1A\u91CD\u542F\u8BE5\u670D\u52A1\uFF08Linux: systemctl restart zqagent\uFF1B");
|
|
11585
|
-
cliLog(" macOS: launchctl kickstart -k gui/$(id -u)/com.zqagent.node\uFF09\u3002");
|
|
11586
|
-
} else if (r.runningServiceDirs.length > 0) {
|
|
11587
|
-
cliLog("");
|
|
11588
|
-
cliLog("\u26A0\uFE0F \u672C\u673A\u6709\u6B63\u5728\u8FD0\u884C\u7684\u8282\u70B9\uFF0C\u4F46\u6570\u636E\u76EE\u5F55\u4E0E\u672C\u6B21\u5199\u5165\u7684\u4E0D\u4E00\u81F4\uFF1A");
|
|
11589
|
-
cliLog(` \u672C\u6B21\u5199\u5165: ${r.dataDir}`);
|
|
11590
|
-
for (const d of r.runningServiceDirs) cliLog(` \u670D\u52A1\u4F7F\u7528: ${d}`);
|
|
11591
|
-
cliLog(" \u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1\u8BFB\u4E0D\u5230\u8FD9\u4EFD\u51ED\u636E\uFF0CHub \u4F1A\u4E00\u76F4\u663E\u793A\u672A\u63A5\u5165\u3002");
|
|
11592
|
-
cliLog(" \u8BF7\u786E\u8BA4\u670D\u52A1\u7684\u6570\u636E\u76EE\u5F55\u540E\u91CD\u65B0\u6267\u884C\uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF09\uFF1A");
|
|
11593
|
-
cliLog(` zqagent link --hub <Hub\u5730\u5740> --code <\u65B0\u63A5\u5165\u7801> --data-dir "${r.runningServiceDirs[0]}"`);
|
|
11594
|
-
} else if (!r.explicitDataDir) {
|
|
11595
|
-
cliLog("");
|
|
11596
|
-
cliLog("\u26A0\uFE0F \u672A\u53D1\u73B0\u672C\u673A\u8282\u70B9\u670D\u52A1\uFF0C\u51ED\u636E\u5199\u5165\u4E86\u5F53\u524D\u76EE\u5F55\u4E0B\u7684 .zqagent_data\u3002");
|
|
11597
|
-
cliLog(" \u5982\u679C\u8FD9\u53F0\u673A\u5668\u6253\u7B97\u957F\u671F\u8FD0\u884C\u8282\u70B9\uFF0C\u8BF7\u5148\u5B89\u88C5\u5F00\u673A\u670D\u52A1\uFF0C\u4E4B\u540E link \u4F1A\u81EA\u52A8\u5199\u5165\u5B83\u7684\u76EE\u5F55\uFF1A");
|
|
11598
|
-
cliLog(" curl -fsSL https://agent.cd-zq.com/install.sh | sh");
|
|
11599
|
-
cliLog(` \uFF08\u670D\u52A1\u9ED8\u8BA4\u6570\u636E\u76EE\u5F55\uFF1A${installDataDirHint()}\uFF09`);
|
|
11600
|
-
}
|
|
11391
|
+
scheduleRollupSave() {
|
|
11392
|
+
this.rollupDirty = true;
|
|
11393
|
+
if (this.rollupTimer) return;
|
|
11394
|
+
this.rollupTimer = setTimeout(() => {
|
|
11395
|
+
this.rollupTimer = null;
|
|
11396
|
+
if (this.rollupDirty) this.saveRollupNow();
|
|
11397
|
+
}, 1500);
|
|
11398
|
+
this.rollupTimer.unref?.();
|
|
11399
|
+
}
|
|
11400
|
+
saveRollupNow() {
|
|
11401
|
+
this.rollupDirty = false;
|
|
11402
|
+
try {
|
|
11403
|
+
atomicWrite(this.rollupFile, JSON.stringify(this.rollup, null, 2));
|
|
11404
|
+
} catch (e) {
|
|
11405
|
+
console.error(`[usage] \u5199\u6C47\u603B\u5931\u8D25: ${String(e)}`);
|
|
11601
11406
|
}
|
|
11602
|
-
return true;
|
|
11603
|
-
} catch (e) {
|
|
11604
|
-
cliErr(`\u63A5\u5165\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
|
|
11605
|
-
return false;
|
|
11606
11407
|
}
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
// src/server/runtime.ts
|
|
11613
|
-
init_esm_shims();
|
|
11614
|
-
import * as fs31 from "fs";
|
|
11615
|
-
import * as path38 from "path";
|
|
11616
|
-
|
|
11617
|
-
// src/server/session.ts
|
|
11618
|
-
init_esm_shims();
|
|
11619
|
-
import * as path26 from "path";
|
|
11620
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
11621
|
-
|
|
11622
|
-
// src/server/mcpConfig.ts
|
|
11623
|
-
init_esm_shims();
|
|
11624
|
-
import fs20 from "fs";
|
|
11625
|
-
import path24 from "path";
|
|
11626
|
-
function isValidMcpConfig(v) {
|
|
11627
|
-
return !!v && typeof v === "object" && typeof v.name === "string" && (v.type === "stdio" || v.type === "http");
|
|
11628
|
-
}
|
|
11629
|
-
function readConfigFile(file) {
|
|
11630
|
-
if (!file) return [];
|
|
11631
|
-
try {
|
|
11632
|
-
if (!fs20.existsSync(file)) return [];
|
|
11633
|
-
const raw = JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
11634
|
-
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.servers) ? raw.servers : [];
|
|
11635
|
-
return list.filter(isValidMcpConfig);
|
|
11636
|
-
} catch (err) {
|
|
11637
|
-
console.error(`[mcp] \u8BFB\u53D6\u914D\u7F6E\u5931\u8D25 ${file}: ${err.message}`);
|
|
11638
|
-
return [];
|
|
11408
|
+
/** 读汇总(含历史基线;总量口径) */
|
|
11409
|
+
getRollup() {
|
|
11410
|
+
this.ensureInit();
|
|
11411
|
+
return this.rollup;
|
|
11639
11412
|
}
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
}
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11413
|
+
// ===================== 查询 =====================
|
|
11414
|
+
/** 把一批记录聚合为 {total, byDay, byModel, byProject}(用于带 project/session 过滤的查询) */
|
|
11415
|
+
aggregate(rows) {
|
|
11416
|
+
const total = zeroAgg();
|
|
11417
|
+
const byDay = {};
|
|
11418
|
+
const byModel = {};
|
|
11419
|
+
const byProject = {};
|
|
11420
|
+
for (const rec of rows) {
|
|
11421
|
+
addAgg(total, rec, rec.costUsd);
|
|
11422
|
+
const d = byDay[utcDay(rec.ts)] ?? (byDay[utcDay(rec.ts)] = zeroAgg());
|
|
11423
|
+
addAgg(d, rec, rec.costUsd);
|
|
11424
|
+
const mk = `${rec.vendor}/${rec.model}`;
|
|
11425
|
+
const bm = byModel[mk] ?? (byModel[mk] = zeroAgg());
|
|
11426
|
+
addAgg(bm, rec, rec.costUsd);
|
|
11427
|
+
const bp = byProject[rec.projectId] ?? (byProject[rec.projectId] = zeroAgg());
|
|
11428
|
+
addAgg(bp, rec, rec.costUsd);
|
|
11429
|
+
}
|
|
11430
|
+
return { total, byDay, byModel, byProject };
|
|
11652
11431
|
}
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
11659
|
-
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
if (!apiKey) return null;
|
|
11665
|
-
return {
|
|
11666
|
-
id: GLOBAL_PROVIDER_ID,
|
|
11667
|
-
name: process.env.OPENAI_MODEL || process.env.MODEL || "\u5168\u5C40 LLM",
|
|
11668
|
-
type: "env",
|
|
11669
|
-
baseURL: process.env.OPENAI_BASE_URL ?? "",
|
|
11670
|
-
model: process.env.OPENAI_MODEL || process.env.MODEL || "",
|
|
11671
|
-
apiKey
|
|
11672
|
-
};
|
|
11673
|
-
}
|
|
11674
|
-
function normReasoningEffort(v) {
|
|
11675
|
-
return v === "low" || v === "medium" || v === "high" ? v : null;
|
|
11676
|
-
}
|
|
11677
|
-
function normalizeModelList(v) {
|
|
11678
|
-
if (!Array.isArray(v)) return void 0;
|
|
11679
|
-
const out = [];
|
|
11680
|
-
const seen = /* @__PURE__ */ new Set();
|
|
11681
|
-
for (const x of v) {
|
|
11682
|
-
const s = String(x ?? "").trim();
|
|
11683
|
-
if (!s || seen.has(s)) continue;
|
|
11684
|
-
seen.add(s);
|
|
11685
|
-
out.push(s);
|
|
11686
|
-
if (out.length >= 500) break;
|
|
11432
|
+
/** 取范围内全部明细(内部分页拉全,供过滤聚合/导出使用) */
|
|
11433
|
+
allRecords(opts) {
|
|
11434
|
+
const all = [];
|
|
11435
|
+
let offset = 0;
|
|
11436
|
+
for (; ; ) {
|
|
11437
|
+
const page = this.records({ ...opts, limit: 1e3, offset });
|
|
11438
|
+
all.push(...page.rows);
|
|
11439
|
+
offset += 1e3;
|
|
11440
|
+
if (offset >= page.total) break;
|
|
11441
|
+
}
|
|
11442
|
+
return all;
|
|
11687
11443
|
}
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11444
|
+
summary(opts) {
|
|
11445
|
+
this.ensureInit();
|
|
11446
|
+
const currency = (opts.currency ?? "USD").toUpperCase();
|
|
11447
|
+
const rate = this.convert(1, currency);
|
|
11448
|
+
const from = opts.from ?? "";
|
|
11449
|
+
const to = opts.to ?? "";
|
|
11450
|
+
const groupBy = opts.groupBy;
|
|
11451
|
+
if (opts.projectId || opts.sessionId) {
|
|
11452
|
+
const rows = this.allRecords({
|
|
11453
|
+
from: opts.from,
|
|
11454
|
+
to: opts.to,
|
|
11455
|
+
projectId: opts.projectId,
|
|
11456
|
+
sessionId: opts.sessionId
|
|
11457
|
+
});
|
|
11458
|
+
const agg = this.aggregate(rows);
|
|
11459
|
+
const groups2 = [];
|
|
11460
|
+
if (groupBy === "day") {
|
|
11461
|
+
for (const [k, v] of Object.entries(agg.byDay).sort(
|
|
11462
|
+
(a, b) => a[0].localeCompare(b[0])
|
|
11463
|
+
))
|
|
11464
|
+
groups2.push({ key: k, agg: v });
|
|
11465
|
+
} else if (groupBy === "model") {
|
|
11466
|
+
for (const [k, v] of Object.entries(agg.byModel))
|
|
11467
|
+
groups2.push({ key: k, agg: v });
|
|
11468
|
+
groups2.sort(
|
|
11469
|
+
(a, b) => b.agg.costUsd - a.agg.costUsd || b.agg.input - a.agg.input
|
|
11470
|
+
);
|
|
11471
|
+
} else if (groupBy === "project") {
|
|
11472
|
+
for (const [k, v] of Object.entries(agg.byProject))
|
|
11473
|
+
groups2.push({ key: k, agg: v });
|
|
11702
11474
|
}
|
|
11703
|
-
] : [],
|
|
11704
|
-
...cfg.providers.map((p) => ({
|
|
11705
|
-
id: p.id,
|
|
11706
|
-
name: p.name,
|
|
11707
|
-
type: p.type ?? "",
|
|
11708
|
-
baseURL: p.baseURL,
|
|
11709
|
-
model: p.model,
|
|
11710
|
-
hasApiKey: Boolean(p.apiKey),
|
|
11711
|
-
models: p.models ?? [],
|
|
11712
|
-
nonChat: p.nonChat ?? [],
|
|
11713
|
-
modelMeta: p.modelMeta ?? [],
|
|
11714
|
-
reasoningEffort: p.reasoningEffort ?? null,
|
|
11715
|
-
global: false
|
|
11716
|
-
}))
|
|
11717
|
-
];
|
|
11718
|
-
return {
|
|
11719
|
-
defaultProviderId: cfg.defaultProviderId ?? null,
|
|
11720
|
-
providers,
|
|
11721
|
-
/** 未显式设置默认时,是否由环境变量全局配置兜底 */
|
|
11722
|
-
globalIsDefault: !cfg.defaultProviderId && Boolean(g)
|
|
11723
|
-
};
|
|
11724
|
-
}
|
|
11725
|
-
function applyNodeLlmPatch(cfg, raw) {
|
|
11726
|
-
const next = { providers: cfg.providers, defaultProviderId: cfg.defaultProviderId };
|
|
11727
|
-
if (Array.isArray(raw?.providers)) {
|
|
11728
|
-
const old = new Map(cfg.providers.map((p) => [p.id, p]));
|
|
11729
|
-
next.providers = raw.providers.filter((x) => Boolean(x) && typeof x === "object").filter((x) => x.global !== true && String(x.id ?? "") !== GLOBAL_PROVIDER_ID).map((x) => {
|
|
11730
|
-
const id = String(x.id ?? "").trim();
|
|
11731
|
-
if (!id) throw new ApiError(400, "bad_request", "\u4F9B\u5E94\u5546 id \u4E0D\u80FD\u4E3A\u7A7A");
|
|
11732
|
-
const prev = old.get(id);
|
|
11733
|
-
const apiKey = String(x.apiKey ?? "").trim();
|
|
11734
|
-
const models = normalizeModelList(x.models) ?? prev?.models;
|
|
11735
|
-
const nonChat = normalizeModelList(x.nonChat) ?? prev?.nonChat;
|
|
11736
|
-
const modelMeta = normalizeModelMeta(x.modelMeta) ?? prev?.modelMeta;
|
|
11737
|
-
return {
|
|
11738
|
-
id,
|
|
11739
|
-
name: String(x.name ?? prev?.name ?? id).trim(),
|
|
11740
|
-
type: x.type == null ? prev?.type : String(x.type),
|
|
11741
|
-
baseURL: String(x.baseURL ?? prev?.baseURL ?? "").trim(),
|
|
11742
|
-
model: String(x.model ?? prev?.model ?? "").trim(),
|
|
11743
|
-
apiKey: apiKey || prev?.apiKey,
|
|
11744
|
-
reasoningEffort: x.reasoningEffort === void 0 ? prev?.reasoningEffort ?? null : normReasoningEffort(x.reasoningEffort),
|
|
11745
|
-
...models ? { models } : {},
|
|
11746
|
-
...nonChat ? { nonChat } : {},
|
|
11747
|
-
...modelMeta ? { modelMeta } : {}
|
|
11748
|
-
};
|
|
11749
|
-
});
|
|
11750
|
-
}
|
|
11751
|
-
if (typeof raw?.defaultProviderId === "string") {
|
|
11752
|
-
const id = raw.defaultProviderId.trim();
|
|
11753
|
-
next.defaultProviderId = id || void 0;
|
|
11754
|
-
} else if (raw?.defaultProviderId === null) {
|
|
11755
|
-
next.defaultProviderId = void 0;
|
|
11756
|
-
}
|
|
11757
|
-
return next;
|
|
11758
|
-
}
|
|
11759
|
-
function findNodeProvider(store, id) {
|
|
11760
|
-
if (!id) return null;
|
|
11761
|
-
if (id === GLOBAL_PROVIDER_ID) return envProvider();
|
|
11762
|
-
return store.loadNodeLlm().providers.find((p) => p.id === id) ?? null;
|
|
11763
|
-
}
|
|
11764
|
-
function nodeDefaultProvider(store) {
|
|
11765
|
-
const cfg = store.loadNodeLlm();
|
|
11766
|
-
if (cfg.providers.length === 0) return null;
|
|
11767
|
-
return cfg.providers.find((p) => p.id === cfg.defaultProviderId) ?? cfg.providers[0] ?? null;
|
|
11768
|
-
}
|
|
11769
|
-
function resolveSessionProvider(store, tenant, providerId) {
|
|
11770
|
-
const own = tenant.provider ?? {};
|
|
11771
|
-
if (providerId) {
|
|
11772
|
-
const picked = findNodeProvider(store, providerId);
|
|
11773
|
-
if (picked) {
|
|
11774
11475
|
return {
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
...picked.modelMeta ? { modelMeta: picked.modelMeta } : {}
|
|
11476
|
+
currency,
|
|
11477
|
+
rate,
|
|
11478
|
+
since: this.rollup.since,
|
|
11479
|
+
total: agg.total,
|
|
11480
|
+
groups: groups2
|
|
11781
11481
|
};
|
|
11782
11482
|
}
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
|
|
11786
|
-
|
|
11483
|
+
const groups = [];
|
|
11484
|
+
let total = { ...this.rollup.total };
|
|
11485
|
+
if (groupBy === "day") {
|
|
11486
|
+
const picked = Object.entries(this.rollup.byDay).filter(([k]) => (!from || k >= from) && (!to || k <= to)).sort((a, b) => a[0].localeCompare(b[0]));
|
|
11487
|
+
const agg = zeroAgg();
|
|
11488
|
+
for (const [k, v] of picked) {
|
|
11489
|
+
groups.push({ key: k, agg: v });
|
|
11490
|
+
agg.calls += v.calls;
|
|
11491
|
+
agg.input += v.input;
|
|
11492
|
+
agg.output += v.output;
|
|
11493
|
+
agg.cacheRead += v.cacheRead;
|
|
11494
|
+
agg.cacheWrite += v.cacheWrite;
|
|
11495
|
+
agg.costUsd += v.costUsd;
|
|
11496
|
+
agg.unpriced += v.unpriced;
|
|
11497
|
+
}
|
|
11498
|
+
total = agg;
|
|
11499
|
+
} else if (groupBy === "model") {
|
|
11500
|
+
for (const [k, v] of Object.entries(this.rollup.byModel))
|
|
11501
|
+
groups.push({ key: k, agg: v });
|
|
11502
|
+
groups.sort(
|
|
11503
|
+
(a, b) => b.agg.costUsd - a.agg.costUsd || b.agg.input - a.agg.input
|
|
11504
|
+
);
|
|
11505
|
+
} else if (groupBy === "project") {
|
|
11506
|
+
for (const [k, v] of Object.entries(this.rollup.byProject))
|
|
11507
|
+
groups.push({ key: k, agg: v });
|
|
11508
|
+
groups.sort(
|
|
11509
|
+
(a, b) => b.agg.costUsd - a.agg.costUsd || b.agg.input - a.agg.input
|
|
11510
|
+
);
|
|
11511
|
+
}
|
|
11787
11512
|
return {
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11513
|
+
currency,
|
|
11514
|
+
rate,
|
|
11515
|
+
since: this.rollup.since,
|
|
11516
|
+
total,
|
|
11517
|
+
legacy: this.rollup.legacy,
|
|
11518
|
+
groups
|
|
11793
11519
|
};
|
|
11794
11520
|
}
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
base: "USD",
|
|
11807
|
-
rate: { CNY: 7.2 },
|
|
11808
|
-
vendors: {
|
|
11809
|
-
openai: {
|
|
11810
|
-
"gpt-4o": { input: 2.5, output: 10, cacheRead: 1.25 },
|
|
11811
|
-
"gpt-4o-mini": { input: 0.15, output: 0.6, cacheRead: 0.075 },
|
|
11812
|
-
"gpt-4.1": { input: 2, output: 8, cacheRead: 0.5 }
|
|
11813
|
-
},
|
|
11814
|
-
deepseek: {
|
|
11815
|
-
"deepseek-flash": {
|
|
11816
|
-
input: 0.27,
|
|
11817
|
-
output: 1.1,
|
|
11818
|
-
cacheRead: 0.027,
|
|
11819
|
-
cacheWrite: 0.27
|
|
11820
|
-
},
|
|
11821
|
-
"deepseek-reasoner": {
|
|
11822
|
-
input: 0.55,
|
|
11823
|
-
output: 2.19,
|
|
11824
|
-
cacheRead: 0.055,
|
|
11825
|
-
cacheWrite: 0.55
|
|
11826
|
-
},
|
|
11827
|
-
"deepseek-*": {
|
|
11828
|
-
input: 0.27,
|
|
11829
|
-
output: 1.1,
|
|
11830
|
-
cacheRead: 0.027,
|
|
11831
|
-
cacheWrite: 0.27
|
|
11832
|
-
}
|
|
11833
|
-
},
|
|
11834
|
-
qwen: {
|
|
11835
|
-
"qwen-plus": { input: 0.4, output: 1.2 },
|
|
11836
|
-
"qwen-turbo": { input: 0.05, output: 0.2 }
|
|
11837
|
-
},
|
|
11838
|
-
zhipu: { "glm-4-plus": { input: 0.7, output: 0.7 } },
|
|
11839
|
-
moonshot: { "moonshot-v1-8k": { input: 1.68, output: 1.68 } },
|
|
11840
|
-
xiaomi: {},
|
|
11841
|
-
custom: {},
|
|
11842
|
-
env: {}
|
|
11843
|
-
},
|
|
11844
|
-
fallback: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
11845
|
-
};
|
|
11846
|
-
function zeroAgg() {
|
|
11847
|
-
return {
|
|
11848
|
-
calls: 0,
|
|
11849
|
-
input: 0,
|
|
11850
|
-
output: 0,
|
|
11851
|
-
cacheRead: 0,
|
|
11852
|
-
cacheWrite: 0,
|
|
11853
|
-
costUsd: 0,
|
|
11854
|
-
unpriced: 0
|
|
11855
|
-
};
|
|
11856
|
-
}
|
|
11857
|
-
function addAgg(into, tokens, costUsd) {
|
|
11858
|
-
into.calls += 1;
|
|
11859
|
-
into.input += tokens.input;
|
|
11860
|
-
into.output += tokens.output;
|
|
11861
|
-
into.cacheRead += tokens.cacheRead;
|
|
11862
|
-
into.cacheWrite += tokens.cacheWrite;
|
|
11863
|
-
if (costUsd == null) into.unpriced += 1;
|
|
11864
|
-
else into.costUsd += costUsd;
|
|
11865
|
-
}
|
|
11866
|
-
function utcDay(iso) {
|
|
11867
|
-
return iso.slice(0, 10);
|
|
11868
|
-
}
|
|
11869
|
-
function monthOf(iso) {
|
|
11870
|
-
return iso.slice(0, 7);
|
|
11871
|
-
}
|
|
11872
|
-
function retainedMonths(now = /* @__PURE__ */ new Date()) {
|
|
11873
|
-
const out = /* @__PURE__ */ new Set();
|
|
11874
|
-
const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
11875
|
-
for (let i = 0; i < LEDGER_RETENTION_MONTHS; i += 1) {
|
|
11876
|
-
out.add(d.toISOString().slice(0, 7));
|
|
11877
|
-
d.setUTCMonth(d.getUTCMonth() - 1);
|
|
11878
|
-
}
|
|
11879
|
-
return out;
|
|
11880
|
-
}
|
|
11881
|
-
function atomicWrite(file, content) {
|
|
11882
|
-
fs21.mkdirSync(path25.dirname(file), { recursive: true });
|
|
11883
|
-
const tmp = `${file}.tmp`;
|
|
11884
|
-
fs21.writeFileSync(tmp, content, "utf-8");
|
|
11885
|
-
fs21.renameSync(tmp, file);
|
|
11886
|
-
}
|
|
11887
|
-
var UsageLedger = class {
|
|
11888
|
-
constructor(root) {
|
|
11889
|
-
this.root = root;
|
|
11890
|
-
this.dir = path25.join(root, "usage");
|
|
11891
|
-
}
|
|
11892
|
-
root;
|
|
11893
|
-
dir;
|
|
11894
|
-
rollup;
|
|
11895
|
-
pricing;
|
|
11896
|
-
rollupDirty = false;
|
|
11897
|
-
rollupTimer = null;
|
|
11898
|
-
inited = false;
|
|
11899
|
-
get ledgerDir() {
|
|
11900
|
-
return this.dir;
|
|
11901
|
-
}
|
|
11902
|
-
ledgerFile(month) {
|
|
11903
|
-
return path25.join(this.ledgerDir, `ledger-${month}.jsonl`);
|
|
11904
|
-
}
|
|
11905
|
-
get rollupFile() {
|
|
11906
|
-
return path25.join(this.ledgerDir, "rollup.json");
|
|
11907
|
-
}
|
|
11908
|
-
get pricingFile() {
|
|
11909
|
-
return path25.join(this.ledgerDir, "pricing.json");
|
|
11910
|
-
}
|
|
11911
|
-
// ===================== 初始化 =====================
|
|
11912
|
-
init(now = /* @__PURE__ */ new Date()) {
|
|
11913
|
-
if (this.inited) return;
|
|
11914
|
-
fs21.mkdirSync(this.ledgerDir, { recursive: true });
|
|
11915
|
-
this.pricing = this.loadPricing();
|
|
11916
|
-
this.rollup = this.loadRollup(now);
|
|
11917
|
-
this.pruneLedgers(now);
|
|
11918
|
-
this.inited = true;
|
|
11919
|
-
}
|
|
11920
|
-
loadPricing() {
|
|
11921
|
-
try {
|
|
11922
|
-
const raw = fs21.readFileSync(this.pricingFile, "utf-8");
|
|
11923
|
-
const parsed = JSON.parse(raw);
|
|
11924
|
-
const vendors = {};
|
|
11925
|
-
for (const v of /* @__PURE__ */ new Set([
|
|
11926
|
-
...Object.keys(DEFAULT_PRICING.vendors),
|
|
11927
|
-
...Object.keys(parsed.vendors ?? {})
|
|
11928
|
-
])) {
|
|
11929
|
-
vendors[v] = {
|
|
11930
|
-
...DEFAULT_PRICING.vendors[v] ?? {},
|
|
11931
|
-
...(parsed.vendors ?? {})[v] ?? {}
|
|
11932
|
-
};
|
|
11933
|
-
}
|
|
11934
|
-
return {
|
|
11935
|
-
schema: USAGE_SCHEMA,
|
|
11936
|
-
base: "USD",
|
|
11937
|
-
rate: { ...DEFAULT_PRICING.rate, ...parsed.rate ?? {} },
|
|
11938
|
-
vendors,
|
|
11939
|
-
fallback: { ...DEFAULT_PRICING.fallback, ...parsed.fallback ?? {} }
|
|
11940
|
-
};
|
|
11941
|
-
} catch {
|
|
11521
|
+
/** 明细(流式读取范围内月份的账本文件) */
|
|
11522
|
+
records(opts) {
|
|
11523
|
+
this.ensureInit();
|
|
11524
|
+
const from = opts.from ?? "";
|
|
11525
|
+
const to = opts.to ?? "";
|
|
11526
|
+
const months = this.monthsInRange(from, to);
|
|
11527
|
+
const rows = [];
|
|
11528
|
+
for (const month of months) {
|
|
11529
|
+
const file = this.ledgerFile(month);
|
|
11530
|
+
if (!fs19.existsSync(file)) continue;
|
|
11531
|
+
let content = "";
|
|
11942
11532
|
try {
|
|
11943
|
-
|
|
11533
|
+
content = fs19.readFileSync(file, "utf-8");
|
|
11944
11534
|
} catch {
|
|
11535
|
+
continue;
|
|
11945
11536
|
}
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
return {
|
|
11954
|
-
schema: USAGE_SCHEMA,
|
|
11955
|
-
since: parsed.since ?? now.toISOString(),
|
|
11956
|
-
legacy: parsed.legacy,
|
|
11957
|
-
total: { ...zeroAgg(), ...parsed.total },
|
|
11958
|
-
byDay: parsed.byDay ?? {},
|
|
11959
|
-
byModel: parsed.byModel ?? {},
|
|
11960
|
-
byProject: parsed.byProject ?? {}
|
|
11961
|
-
};
|
|
11962
|
-
} catch {
|
|
11963
|
-
return {
|
|
11964
|
-
schema: USAGE_SCHEMA,
|
|
11965
|
-
since: now.toISOString(),
|
|
11966
|
-
total: zeroAgg(),
|
|
11967
|
-
byDay: {},
|
|
11968
|
-
byModel: {},
|
|
11969
|
-
byProject: {}
|
|
11970
|
-
};
|
|
11971
|
-
}
|
|
11972
|
-
}
|
|
11973
|
-
/** 删除超出保留期的账本文件,并裁剪 byDay */
|
|
11974
|
-
pruneLedgers(now) {
|
|
11975
|
-
const keep = retainedMonths(now);
|
|
11976
|
-
let changed = false;
|
|
11977
|
-
try {
|
|
11978
|
-
for (const f of fs21.readdirSync(this.ledgerDir)) {
|
|
11979
|
-
const m = /^ledger-(\d{4}-\d{2})\.jsonl$/.exec(f);
|
|
11980
|
-
if (m && !keep.has(m[1])) {
|
|
11981
|
-
fs21.unlinkSync(path25.join(this.ledgerDir, f));
|
|
11537
|
+
for (const line of content.split("\n")) {
|
|
11538
|
+
if (!line.trim()) continue;
|
|
11539
|
+
let rec;
|
|
11540
|
+
try {
|
|
11541
|
+
rec = JSON.parse(line);
|
|
11542
|
+
} catch {
|
|
11543
|
+
continue;
|
|
11982
11544
|
}
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
11990
|
-
}
|
|
11991
|
-
}
|
|
11992
|
-
if (changed) this.saveRollupNow();
|
|
11993
|
-
}
|
|
11994
|
-
// ===================== 价格 =====================
|
|
11995
|
-
getPricing() {
|
|
11996
|
-
return this.pricing;
|
|
11997
|
-
}
|
|
11998
|
-
/** 更新价格/汇率(整表覆盖式合并) */
|
|
11999
|
-
updatePricing(patch) {
|
|
12000
|
-
this.pricing = {
|
|
12001
|
-
schema: USAGE_SCHEMA,
|
|
12002
|
-
base: "USD",
|
|
12003
|
-
rate: { ...this.pricing.rate, ...patch.rate ?? {} },
|
|
12004
|
-
vendors: { ...this.pricing.vendors, ...patch.vendors ?? {} },
|
|
12005
|
-
fallback: { ...this.pricing.fallback, ...patch.fallback ?? {} }
|
|
12006
|
-
};
|
|
12007
|
-
atomicWrite(this.pricingFile, JSON.stringify(this.pricing, null, 2));
|
|
12008
|
-
return this.pricing;
|
|
12009
|
-
}
|
|
12010
|
-
priceFor(vendor, model) {
|
|
12011
|
-
const table = this.pricing.vendors[vendor];
|
|
12012
|
-
if (table) {
|
|
12013
|
-
if (table[model]) return table[model];
|
|
12014
|
-
for (const key of Object.keys(table)) {
|
|
12015
|
-
if (key.endsWith("*") && model.startsWith(key.slice(0, -1)))
|
|
12016
|
-
return table[key];
|
|
12017
|
-
}
|
|
12018
|
-
}
|
|
12019
|
-
for (const v of Object.values(this.pricing.vendors)) {
|
|
12020
|
-
if (v[model]) return v[model];
|
|
12021
|
-
}
|
|
12022
|
-
for (const v of Object.values(this.pricing.vendors)) {
|
|
12023
|
-
for (const key of Object.keys(v)) {
|
|
12024
|
-
if (key.endsWith("*") && model.startsWith(key.slice(0, -1)))
|
|
12025
|
-
return v[key];
|
|
11545
|
+
const day = utcDay(rec.ts);
|
|
11546
|
+
if (from && day < from) continue;
|
|
11547
|
+
if (to && day > to) continue;
|
|
11548
|
+
if (opts.sessionId && rec.sessionId !== opts.sessionId) continue;
|
|
11549
|
+
if (opts.projectId && rec.projectId !== opts.projectId) continue;
|
|
11550
|
+
if (opts.model && rec.model !== opts.model) continue;
|
|
11551
|
+
rows.push(rec);
|
|
12026
11552
|
}
|
|
12027
11553
|
}
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
if (!p) return null;
|
|
12034
|
-
const cached3 = Math.min(t.cacheRead, t.input);
|
|
12035
|
-
const fresh = Math.max(0, t.input - cached3);
|
|
12036
|
-
const M = 1e6;
|
|
12037
|
-
const cost = (fresh * p.input + cached3 * (p.cacheRead ?? p.input) + t.output * p.output + t.cacheWrite * (p.cacheWrite ?? 0)) / M;
|
|
12038
|
-
return Number.isFinite(cost) ? cost : null;
|
|
12039
|
-
}
|
|
12040
|
-
convert(usd, currency) {
|
|
12041
|
-
if (!currency || currency === "USD") return usd;
|
|
12042
|
-
const r = this.pricing.rate[currency];
|
|
12043
|
-
return typeof r === "number" && r > 0 ? usd * r : usd;
|
|
12044
|
-
}
|
|
12045
|
-
// ===================== 记账 =====================
|
|
12046
|
-
/** 追加一条用量(串行 + appendFileSync;同步返回,便于在事件回调里调用) */
|
|
12047
|
-
record(input) {
|
|
12048
|
-
this.ensureInit();
|
|
12049
|
-
const costUsd = this.costOf(input.vendor, input.model, input);
|
|
12050
|
-
const rec = {
|
|
12051
|
-
...input,
|
|
12052
|
-
id: input.id ?? `${input.sessionId}:${input.ts}:${Math.random().toString(36).slice(2, 8)}`,
|
|
12053
|
-
costUsd,
|
|
12054
|
-
priced: costUsd != null
|
|
12055
|
-
};
|
|
12056
|
-
const line = JSON.stringify(rec) + "\n";
|
|
12057
|
-
try {
|
|
12058
|
-
fs21.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
|
|
12059
|
-
} catch (e) {
|
|
12060
|
-
console.error(`[usage] \u5199\u8D26\u672C\u5931\u8D25: ${String(e)}`);
|
|
12061
|
-
}
|
|
12062
|
-
addAgg(this.rollup.total, rec, costUsd);
|
|
12063
|
-
const day = this.rollup.byDay[utcDay(rec.ts)] ?? (this.rollup.byDay[utcDay(rec.ts)] = zeroAgg());
|
|
12064
|
-
addAgg(day, rec, costUsd);
|
|
12065
|
-
const mk = `${rec.vendor}/${rec.model}`;
|
|
12066
|
-
const bm = this.rollup.byModel[mk] ?? (this.rollup.byModel[mk] = zeroAgg());
|
|
12067
|
-
addAgg(bm, rec, costUsd);
|
|
12068
|
-
const bp = this.rollup.byProject[rec.projectId] ?? (this.rollup.byProject[rec.projectId] = zeroAgg());
|
|
12069
|
-
addAgg(bp, rec, costUsd);
|
|
12070
|
-
this.scheduleRollupSave();
|
|
12071
|
-
return rec;
|
|
12072
|
-
}
|
|
12073
|
-
/** 写入历史基线(仅一次;迁移自既有消息 usage) */
|
|
12074
|
-
seedLegacy(agg, throughTs) {
|
|
12075
|
-
this.ensureInit();
|
|
12076
|
-
if (this.rollup.legacy) return;
|
|
12077
|
-
this.rollup.legacy = { ...agg, throughTs };
|
|
12078
|
-
if (throughTs < this.rollup.since) this.rollup.since = throughTs;
|
|
12079
|
-
this.rollup.total.calls += agg.calls;
|
|
12080
|
-
this.rollup.total.input += agg.input;
|
|
12081
|
-
this.rollup.total.output += agg.output;
|
|
12082
|
-
this.rollup.total.cacheRead += agg.cacheRead;
|
|
12083
|
-
this.rollup.total.cacheWrite += agg.cacheWrite;
|
|
12084
|
-
this.rollup.total.costUsd += agg.costUsd;
|
|
12085
|
-
this.rollup.total.unpriced += agg.unpriced;
|
|
12086
|
-
this.saveRollupNow();
|
|
12087
|
-
}
|
|
12088
|
-
ensureInit() {
|
|
12089
|
-
if (!this.inited) this.init();
|
|
12090
|
-
}
|
|
12091
|
-
scheduleRollupSave() {
|
|
12092
|
-
this.rollupDirty = true;
|
|
12093
|
-
if (this.rollupTimer) return;
|
|
12094
|
-
this.rollupTimer = setTimeout(() => {
|
|
12095
|
-
this.rollupTimer = null;
|
|
12096
|
-
if (this.rollupDirty) this.saveRollupNow();
|
|
12097
|
-
}, 1500);
|
|
12098
|
-
this.rollupTimer.unref?.();
|
|
12099
|
-
}
|
|
12100
|
-
saveRollupNow() {
|
|
12101
|
-
this.rollupDirty = false;
|
|
12102
|
-
try {
|
|
12103
|
-
atomicWrite(this.rollupFile, JSON.stringify(this.rollup, null, 2));
|
|
12104
|
-
} catch (e) {
|
|
12105
|
-
console.error(`[usage] \u5199\u6C47\u603B\u5931\u8D25: ${String(e)}`);
|
|
12106
|
-
}
|
|
12107
|
-
}
|
|
12108
|
-
/** 读汇总(含历史基线;总量口径) */
|
|
12109
|
-
getRollup() {
|
|
12110
|
-
this.ensureInit();
|
|
12111
|
-
return this.rollup;
|
|
12112
|
-
}
|
|
12113
|
-
// ===================== 查询 =====================
|
|
12114
|
-
/** 把一批记录聚合为 {total, byDay, byModel, byProject}(用于带 project/session 过滤的查询) */
|
|
12115
|
-
aggregate(rows) {
|
|
12116
|
-
const total = zeroAgg();
|
|
12117
|
-
const byDay = {};
|
|
12118
|
-
const byModel = {};
|
|
12119
|
-
const byProject = {};
|
|
12120
|
-
for (const rec of rows) {
|
|
12121
|
-
addAgg(total, rec, rec.costUsd);
|
|
12122
|
-
const d = byDay[utcDay(rec.ts)] ?? (byDay[utcDay(rec.ts)] = zeroAgg());
|
|
12123
|
-
addAgg(d, rec, rec.costUsd);
|
|
12124
|
-
const mk = `${rec.vendor}/${rec.model}`;
|
|
12125
|
-
const bm = byModel[mk] ?? (byModel[mk] = zeroAgg());
|
|
12126
|
-
addAgg(bm, rec, rec.costUsd);
|
|
12127
|
-
const bp = byProject[rec.projectId] ?? (byProject[rec.projectId] = zeroAgg());
|
|
12128
|
-
addAgg(bp, rec, rec.costUsd);
|
|
12129
|
-
}
|
|
12130
|
-
return { total, byDay, byModel, byProject };
|
|
12131
|
-
}
|
|
12132
|
-
/** 取范围内全部明细(内部分页拉全,供过滤聚合/导出使用) */
|
|
12133
|
-
allRecords(opts) {
|
|
12134
|
-
const all = [];
|
|
12135
|
-
let offset = 0;
|
|
12136
|
-
for (; ; ) {
|
|
12137
|
-
const page = this.records({ ...opts, limit: 1e3, offset });
|
|
12138
|
-
all.push(...page.rows);
|
|
12139
|
-
offset += 1e3;
|
|
12140
|
-
if (offset >= page.total) break;
|
|
12141
|
-
}
|
|
12142
|
-
return all;
|
|
12143
|
-
}
|
|
12144
|
-
summary(opts) {
|
|
12145
|
-
this.ensureInit();
|
|
12146
|
-
const currency = (opts.currency ?? "USD").toUpperCase();
|
|
12147
|
-
const rate = this.convert(1, currency);
|
|
12148
|
-
const from = opts.from ?? "";
|
|
12149
|
-
const to = opts.to ?? "";
|
|
12150
|
-
const groupBy = opts.groupBy;
|
|
12151
|
-
if (opts.projectId || opts.sessionId) {
|
|
12152
|
-
const rows = this.allRecords({
|
|
12153
|
-
from: opts.from,
|
|
12154
|
-
to: opts.to,
|
|
12155
|
-
projectId: opts.projectId,
|
|
12156
|
-
sessionId: opts.sessionId
|
|
12157
|
-
});
|
|
12158
|
-
const agg = this.aggregate(rows);
|
|
12159
|
-
const groups2 = [];
|
|
12160
|
-
if (groupBy === "day") {
|
|
12161
|
-
for (const [k, v] of Object.entries(agg.byDay).sort(
|
|
12162
|
-
(a, b) => a[0].localeCompare(b[0])
|
|
12163
|
-
))
|
|
12164
|
-
groups2.push({ key: k, agg: v });
|
|
12165
|
-
} else if (groupBy === "model") {
|
|
12166
|
-
for (const [k, v] of Object.entries(agg.byModel))
|
|
12167
|
-
groups2.push({ key: k, agg: v });
|
|
12168
|
-
groups2.sort(
|
|
12169
|
-
(a, b) => b.agg.costUsd - a.agg.costUsd || b.agg.input - a.agg.input
|
|
12170
|
-
);
|
|
12171
|
-
} else if (groupBy === "project") {
|
|
12172
|
-
for (const [k, v] of Object.entries(agg.byProject))
|
|
12173
|
-
groups2.push({ key: k, agg: v });
|
|
12174
|
-
}
|
|
12175
|
-
return {
|
|
12176
|
-
currency,
|
|
12177
|
-
rate,
|
|
12178
|
-
since: this.rollup.since,
|
|
12179
|
-
total: agg.total,
|
|
12180
|
-
groups: groups2
|
|
12181
|
-
};
|
|
12182
|
-
}
|
|
12183
|
-
const groups = [];
|
|
12184
|
-
let total = { ...this.rollup.total };
|
|
12185
|
-
if (groupBy === "day") {
|
|
12186
|
-
const picked = Object.entries(this.rollup.byDay).filter(([k]) => (!from || k >= from) && (!to || k <= to)).sort((a, b) => a[0].localeCompare(b[0]));
|
|
12187
|
-
const agg = zeroAgg();
|
|
12188
|
-
for (const [k, v] of picked) {
|
|
12189
|
-
groups.push({ key: k, agg: v });
|
|
12190
|
-
agg.calls += v.calls;
|
|
12191
|
-
agg.input += v.input;
|
|
12192
|
-
agg.output += v.output;
|
|
12193
|
-
agg.cacheRead += v.cacheRead;
|
|
12194
|
-
agg.cacheWrite += v.cacheWrite;
|
|
12195
|
-
agg.costUsd += v.costUsd;
|
|
12196
|
-
agg.unpriced += v.unpriced;
|
|
12197
|
-
}
|
|
12198
|
-
total = agg;
|
|
12199
|
-
} else if (groupBy === "model") {
|
|
12200
|
-
for (const [k, v] of Object.entries(this.rollup.byModel))
|
|
12201
|
-
groups.push({ key: k, agg: v });
|
|
12202
|
-
groups.sort(
|
|
12203
|
-
(a, b) => b.agg.costUsd - a.agg.costUsd || b.agg.input - a.agg.input
|
|
12204
|
-
);
|
|
12205
|
-
} else if (groupBy === "project") {
|
|
12206
|
-
for (const [k, v] of Object.entries(this.rollup.byProject))
|
|
12207
|
-
groups.push({ key: k, agg: v });
|
|
12208
|
-
groups.sort(
|
|
12209
|
-
(a, b) => b.agg.costUsd - a.agg.costUsd || b.agg.input - a.agg.input
|
|
12210
|
-
);
|
|
12211
|
-
}
|
|
12212
|
-
return {
|
|
12213
|
-
currency,
|
|
12214
|
-
rate,
|
|
12215
|
-
since: this.rollup.since,
|
|
12216
|
-
total,
|
|
12217
|
-
legacy: this.rollup.legacy,
|
|
12218
|
-
groups
|
|
12219
|
-
};
|
|
12220
|
-
}
|
|
12221
|
-
/** 明细(流式读取范围内月份的账本文件) */
|
|
12222
|
-
records(opts) {
|
|
12223
|
-
this.ensureInit();
|
|
12224
|
-
const from = opts.from ?? "";
|
|
12225
|
-
const to = opts.to ?? "";
|
|
12226
|
-
const months = this.monthsInRange(from, to);
|
|
12227
|
-
const rows = [];
|
|
12228
|
-
for (const month of months) {
|
|
12229
|
-
const file = this.ledgerFile(month);
|
|
12230
|
-
if (!fs21.existsSync(file)) continue;
|
|
12231
|
-
let content = "";
|
|
12232
|
-
try {
|
|
12233
|
-
content = fs21.readFileSync(file, "utf-8");
|
|
12234
|
-
} catch {
|
|
12235
|
-
continue;
|
|
12236
|
-
}
|
|
12237
|
-
for (const line of content.split("\n")) {
|
|
12238
|
-
if (!line.trim()) continue;
|
|
12239
|
-
let rec;
|
|
12240
|
-
try {
|
|
12241
|
-
rec = JSON.parse(line);
|
|
12242
|
-
} catch {
|
|
12243
|
-
continue;
|
|
12244
|
-
}
|
|
12245
|
-
const day = utcDay(rec.ts);
|
|
12246
|
-
if (from && day < from) continue;
|
|
12247
|
-
if (to && day > to) continue;
|
|
12248
|
-
if (opts.sessionId && rec.sessionId !== opts.sessionId) continue;
|
|
12249
|
-
if (opts.projectId && rec.projectId !== opts.projectId) continue;
|
|
12250
|
-
if (opts.model && rec.model !== opts.model) continue;
|
|
12251
|
-
rows.push(rec);
|
|
12252
|
-
}
|
|
12253
|
-
}
|
|
12254
|
-
rows.sort((a, b) => a.ts < b.ts ? 1 : -1);
|
|
12255
|
-
const total = rows.length;
|
|
12256
|
-
const offset = Math.max(0, opts.offset ?? 0);
|
|
12257
|
-
const limit = Math.max(1, Math.min(1e3, opts.limit ?? 200));
|
|
12258
|
-
return { rows: rows.slice(offset, offset + limit), total };
|
|
11554
|
+
rows.sort((a, b) => a.ts < b.ts ? 1 : -1);
|
|
11555
|
+
const total = rows.length;
|
|
11556
|
+
const offset = Math.max(0, opts.offset ?? 0);
|
|
11557
|
+
const limit = Math.max(1, Math.min(1e3, opts.limit ?? 200));
|
|
11558
|
+
return { rows: rows.slice(offset, offset + limit), total };
|
|
12259
11559
|
}
|
|
12260
11560
|
/** 导出 CSV(范围同 records) */
|
|
12261
11561
|
exportCsv(opts) {
|
|
@@ -12339,10 +11639,10 @@ var UsageLedger = class {
|
|
|
12339
11639
|
const seen = /* @__PURE__ */ new Set();
|
|
12340
11640
|
for (const month of this.monthsInRange("", "")) {
|
|
12341
11641
|
const file = this.ledgerFile(month);
|
|
12342
|
-
if (!
|
|
11642
|
+
if (!fs19.existsSync(file)) continue;
|
|
12343
11643
|
let content = "";
|
|
12344
11644
|
try {
|
|
12345
|
-
content =
|
|
11645
|
+
content = fs19.readFileSync(file, "utf-8");
|
|
12346
11646
|
} catch {
|
|
12347
11647
|
continue;
|
|
12348
11648
|
}
|
|
@@ -13653,7 +12953,7 @@ data: ${JSON.stringify(entry.data)}
|
|
|
13653
12953
|
softThresholdPct: parseRatio(process.env.COMPACTION_SOFT_THRESHOLD_PCT),
|
|
13654
12954
|
capTokens: parseOptionalPositiveNumber(process.env.COMPACTION_CAP_TOKENS) ?? null,
|
|
13655
12955
|
// 超预算工具结果按租户/工作区隔离落盘(多租户共用一个状态目录会混内容)
|
|
13656
|
-
toolResultDir:
|
|
12956
|
+
toolResultDir: path23.join(workspace, ".agent", "tool-results")
|
|
13657
12957
|
});
|
|
13658
12958
|
engine.setApprover(this._makeApprover(rt));
|
|
13659
12959
|
const mcpConfigs = loadServerMcpConfigs(tenant);
|
|
@@ -13817,142 +13117,922 @@ data: ${JSON.stringify(entry.data)}
|
|
|
13817
13117
|
if (rec.savedAt && rec.savedAt > throughTs) throughTs = rec.savedAt;
|
|
13818
13118
|
}
|
|
13819
13119
|
}
|
|
13820
|
-
if (agg.calls > 0) ledger.seedLegacy(agg, throughTs);
|
|
13821
|
-
return { seeded: agg.calls > 0, calls: agg.calls };
|
|
13120
|
+
if (agg.calls > 0) ledger.seedLegacy(agg, throughTs);
|
|
13121
|
+
return { seeded: agg.calls > 0, calls: agg.calls };
|
|
13122
|
+
}
|
|
13123
|
+
};
|
|
13124
|
+
var ApiError = class extends Error {
|
|
13125
|
+
constructor(status, code, message) {
|
|
13126
|
+
super(message);
|
|
13127
|
+
this.status = status;
|
|
13128
|
+
this.code = code;
|
|
13129
|
+
}
|
|
13130
|
+
status;
|
|
13131
|
+
code;
|
|
13132
|
+
};
|
|
13133
|
+
|
|
13134
|
+
// src/server/projects.ts
|
|
13135
|
+
var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
|
|
13136
|
+
function toProjectSummary(store, tenant) {
|
|
13137
|
+
return {
|
|
13138
|
+
id: tenant.id,
|
|
13139
|
+
name: tenant.name ?? tenant.id,
|
|
13140
|
+
model: tenant.model,
|
|
13141
|
+
baseURL: tenant.provider?.baseURL ?? "",
|
|
13142
|
+
hasApiKey: Boolean(tenant.provider?.apiKey),
|
|
13143
|
+
workspaceRoot: tenant.workspaceRoot ?? null,
|
|
13144
|
+
sessionCount: store.listSessions(tenant.id).length
|
|
13145
|
+
};
|
|
13146
|
+
}
|
|
13147
|
+
function providerViews(tenant) {
|
|
13148
|
+
return {
|
|
13149
|
+
activeProviderId: tenant.activeProviderId ?? null,
|
|
13150
|
+
providers: (tenant.llmProviders ?? []).map((p) => ({
|
|
13151
|
+
id: p.id,
|
|
13152
|
+
name: p.name,
|
|
13153
|
+
type: p.type ?? "",
|
|
13154
|
+
baseURL: p.baseURL,
|
|
13155
|
+
model: p.model,
|
|
13156
|
+
models: p.models ?? [],
|
|
13157
|
+
modelMeta: p.modelMeta ?? [],
|
|
13158
|
+
hasApiKey: Boolean(p.apiKey)
|
|
13159
|
+
}))
|
|
13160
|
+
};
|
|
13161
|
+
}
|
|
13162
|
+
function applyProvidersPatch(tenant, raw) {
|
|
13163
|
+
if (Array.isArray(raw.providers)) {
|
|
13164
|
+
const existing = new Map((tenant.llmProviders ?? []).map((p) => [p.id, p]));
|
|
13165
|
+
const next = [];
|
|
13166
|
+
for (const item of raw.providers) {
|
|
13167
|
+
if (!item || typeof item !== "object") continue;
|
|
13168
|
+
const o = item;
|
|
13169
|
+
const id = String(o.id ?? "").trim() || newId();
|
|
13170
|
+
const prev = existing.get(id);
|
|
13171
|
+
const apiKey = typeof o.apiKey === "string" && o.apiKey.trim() ? o.apiKey.trim() : prev?.apiKey;
|
|
13172
|
+
const models = normalizeModelList(o.models) ?? prev?.models;
|
|
13173
|
+
const modelMeta = normalizeModelMeta(o.modelMeta) ?? prev?.modelMeta;
|
|
13174
|
+
next.push({
|
|
13175
|
+
id,
|
|
13176
|
+
name: String(o.name ?? "").trim() || id,
|
|
13177
|
+
type: typeof o.type === "string" ? o.type : prev?.type,
|
|
13178
|
+
baseURL: String(o.baseURL ?? "").trim(),
|
|
13179
|
+
model: String(o.model ?? "").trim(),
|
|
13180
|
+
...apiKey ? { apiKey } : {},
|
|
13181
|
+
...models ? { models } : {},
|
|
13182
|
+
...modelMeta ? { modelMeta } : {}
|
|
13183
|
+
});
|
|
13184
|
+
}
|
|
13185
|
+
tenant.llmProviders = next;
|
|
13186
|
+
}
|
|
13187
|
+
if (typeof raw.activeProviderId === "string") {
|
|
13188
|
+
const activeId = raw.activeProviderId.trim();
|
|
13189
|
+
tenant.activeProviderId = activeId || void 0;
|
|
13190
|
+
const entry = (tenant.llmProviders ?? []).find((p) => p.id === activeId);
|
|
13191
|
+
if (entry) {
|
|
13192
|
+
tenant.provider = tenant.provider ?? {};
|
|
13193
|
+
tenant.provider.baseURL = entry.baseURL || void 0;
|
|
13194
|
+
if (entry.apiKey) tenant.provider.apiKey = entry.apiKey;
|
|
13195
|
+
if (entry.model) tenant.model = entry.model;
|
|
13196
|
+
}
|
|
13197
|
+
}
|
|
13198
|
+
}
|
|
13199
|
+
function allProjectSummaries(store) {
|
|
13200
|
+
return store.listTenants().map((id) => store.loadTenant(id)).filter((t) => t !== null).map((t) => toProjectSummary(store, t));
|
|
13201
|
+
}
|
|
13202
|
+
function lastUserMessage(rec) {
|
|
13203
|
+
const sent = (rec.lastInput ?? "").trim();
|
|
13204
|
+
if (sent) {
|
|
13205
|
+
return sent.length > LAST_USER_MESSAGE_MAX ? `${sent.slice(0, LAST_USER_MESSAGE_MAX)}\u2026` : sent;
|
|
13206
|
+
}
|
|
13207
|
+
const msgs = rec.messages;
|
|
13208
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
13209
|
+
const m = msgs[i];
|
|
13210
|
+
if (!m || m.role !== "user" || typeof m.content !== "string") continue;
|
|
13211
|
+
const text = m.content.trim();
|
|
13212
|
+
if (!text) continue;
|
|
13213
|
+
return text.length > LAST_USER_MESSAGE_MAX ? `${text.slice(0, LAST_USER_MESSAGE_MAX)}\u2026` : text;
|
|
13214
|
+
}
|
|
13215
|
+
return null;
|
|
13216
|
+
}
|
|
13217
|
+
function lastCompletedAt(rec) {
|
|
13218
|
+
if (rec.lastCompletedAt) return rec.lastCompletedAt;
|
|
13219
|
+
if (rec.status !== "completed") return null;
|
|
13220
|
+
const msgs = rec.messages;
|
|
13221
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
13222
|
+
const m = msgs[i];
|
|
13223
|
+
if (!m || m.role !== "assistant") continue;
|
|
13224
|
+
if (typeof m.ts === "number" && Number.isFinite(m.ts)) {
|
|
13225
|
+
return new Date(m.ts).toISOString();
|
|
13226
|
+
}
|
|
13227
|
+
return null;
|
|
13228
|
+
}
|
|
13229
|
+
return null;
|
|
13230
|
+
}
|
|
13231
|
+
function allSessionStates(store, manager) {
|
|
13232
|
+
const out = [];
|
|
13233
|
+
for (const id of store.listTenants()) {
|
|
13234
|
+
const t = store.loadTenant(id);
|
|
13235
|
+
if (!t) continue;
|
|
13236
|
+
try {
|
|
13237
|
+
for (const s of manager.listSessions(t.id)) {
|
|
13238
|
+
out.push({
|
|
13239
|
+
projectId: t.id,
|
|
13240
|
+
projectName: t.name ?? t.id,
|
|
13241
|
+
sessionId: s.sessionId,
|
|
13242
|
+
name: s.name ?? null,
|
|
13243
|
+
status: s.status,
|
|
13244
|
+
lastUserMessage: lastUserMessage(s),
|
|
13245
|
+
lastCompletedAt: lastCompletedAt(s)
|
|
13246
|
+
});
|
|
13247
|
+
}
|
|
13248
|
+
} catch {
|
|
13249
|
+
}
|
|
13250
|
+
}
|
|
13251
|
+
return out;
|
|
13252
|
+
}
|
|
13253
|
+
function ensureNodeId(store) {
|
|
13254
|
+
let id = store.loadNodeId();
|
|
13255
|
+
if (!id) {
|
|
13256
|
+
id = newId();
|
|
13257
|
+
store.saveNodeId(id);
|
|
13258
|
+
}
|
|
13259
|
+
return id;
|
|
13260
|
+
}
|
|
13261
|
+
function createProject(store, input) {
|
|
13262
|
+
const raw = input ?? {};
|
|
13263
|
+
const id = String(raw.id ?? "").trim() || newId();
|
|
13264
|
+
if (store.loadTenant(id)) {
|
|
13265
|
+
throw new ApiError(409, "project_exists", `\u9879\u76EE ${id} \u5DF2\u5B58\u5728`);
|
|
13266
|
+
}
|
|
13267
|
+
const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path24.resolve(raw.workspaceRoot.trim()) : void 0;
|
|
13268
|
+
const explicitName = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : "";
|
|
13269
|
+
const defaultName = workspaceRoot ? path24.basename(workspaceRoot) : id;
|
|
13270
|
+
const tenant = {
|
|
13271
|
+
id,
|
|
13272
|
+
name: explicitName || defaultName,
|
|
13273
|
+
apiKey: generateApiKey(),
|
|
13274
|
+
model: typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : DEFAULT_MODEL(),
|
|
13275
|
+
provider: {
|
|
13276
|
+
apiKey: typeof raw.providerApiKey === "string" && raw.providerApiKey.trim() ? raw.providerApiKey.trim() : void 0,
|
|
13277
|
+
baseURL: typeof raw.baseURL === "string" && raw.baseURL.trim() ? raw.baseURL.trim() : void 0
|
|
13278
|
+
},
|
|
13279
|
+
workspaceRoot,
|
|
13280
|
+
allowedCommands: Array.isArray(raw.allowedCommands) ? raw.allowedCommands.map((c) => String(c)) : []
|
|
13281
|
+
};
|
|
13282
|
+
const def = nodeDefaultProvider(store);
|
|
13283
|
+
if (def) {
|
|
13284
|
+
if (!tenant.provider?.apiKey && def.apiKey) tenant.provider = { ...tenant.provider, apiKey: def.apiKey };
|
|
13285
|
+
if (!tenant.provider?.baseURL && def.baseURL) tenant.provider = { ...tenant.provider, baseURL: def.baseURL };
|
|
13286
|
+
if (!(typeof raw.model === "string" && raw.model.trim()) && def.model) tenant.model = def.model;
|
|
13287
|
+
const nodeCfg = store.loadNodeLlm();
|
|
13288
|
+
tenant.llmProviders = nodeCfg.providers.map((p) => ({ ...p }));
|
|
13289
|
+
tenant.activeProviderId = nodeCfg.defaultProviderId ?? nodeCfg.providers[0]?.id;
|
|
13290
|
+
}
|
|
13291
|
+
store.saveTenant(tenant);
|
|
13292
|
+
return tenant;
|
|
13293
|
+
}
|
|
13294
|
+
function buildNodeInfo(store, opts) {
|
|
13295
|
+
return {
|
|
13296
|
+
nodeId: opts.nodeId ?? ensureNodeId(store),
|
|
13297
|
+
platform: process.platform,
|
|
13298
|
+
mode: process.env.NODE_MODE ?? "standalone",
|
|
13299
|
+
version: "0.2.12",
|
|
13300
|
+
admin: opts.admin,
|
|
13301
|
+
projects: opts.projects
|
|
13302
|
+
};
|
|
13303
|
+
}
|
|
13304
|
+
|
|
13305
|
+
// src/server/fleet-config.ts
|
|
13306
|
+
init_esm_shims();
|
|
13307
|
+
function fleetConfigFromEnv(store) {
|
|
13308
|
+
const fleetToken = process.env.FLEET_TOKEN;
|
|
13309
|
+
const fleetSecret = process.env.FLEET_SECRET;
|
|
13310
|
+
if (!fleetToken || !fleetSecret) return null;
|
|
13311
|
+
return {
|
|
13312
|
+
hubUrl: process.env.HUB_URL,
|
|
13313
|
+
fleetToken,
|
|
13314
|
+
fleetSecret,
|
|
13315
|
+
nodeId: ensureNodeId(store),
|
|
13316
|
+
gatewayUrl: process.env.FLEET_GATEWAY_URL
|
|
13317
|
+
};
|
|
13318
|
+
}
|
|
13319
|
+
function resolveFleetConfig(store) {
|
|
13320
|
+
const saved = store.loadFleetCredential();
|
|
13321
|
+
if (saved?.fleetToken && saved.fleetSecret) {
|
|
13322
|
+
return { ...saved, nodeId: saved.nodeId || ensureNodeId(store) };
|
|
13323
|
+
}
|
|
13324
|
+
return fleetConfigFromEnv(store);
|
|
13325
|
+
}
|
|
13326
|
+
|
|
13327
|
+
// src/cli/account-enroll.ts
|
|
13328
|
+
var SKIP_ANSWERS = /* @__PURE__ */ new Set(["-", "skip", "none", "no"]);
|
|
13329
|
+
function resolveDefaultHub(hubUrl, env3 = process.env) {
|
|
13330
|
+
return (hubUrl ?? env3.HUB_URL ?? DEFAULT_HUB_URL).trim().replace(/\/$/, "");
|
|
13331
|
+
}
|
|
13332
|
+
function parseHubAnswer(answer, fallback) {
|
|
13333
|
+
const a = answer.trim();
|
|
13334
|
+
if (SKIP_ANSWERS.has(a.toLowerCase())) return null;
|
|
13335
|
+
return (a || fallback).replace(/\/$/, "");
|
|
13336
|
+
}
|
|
13337
|
+
async function promptHubUrl(rl2, hubUrl) {
|
|
13338
|
+
const fallback = resolveDefaultHub(hubUrl);
|
|
13339
|
+
const answer = await rl2.question(`Hub \u5730\u5740 [${fallback}]\uFF1A`);
|
|
13340
|
+
return parseHubAnswer(answer, fallback);
|
|
13341
|
+
}
|
|
13342
|
+
async function promptAccountLogin(rl2, hubUrl) {
|
|
13343
|
+
cliLog("\u767B\u5F55\u65B9\u5F0F\uFF1A1) \u90AE\u7BB1 + \u5BC6\u7801 2) \u624B\u673A\u53F7 + \u5BC6\u7801 3) \u624B\u673A\u53F7 + \u77ED\u4FE1\u9A8C\u8BC1\u7801");
|
|
13344
|
+
const mode = (await rl2.question("\u8BF7\u9009\u62E9 [1]\uFF1A")).trim() || "1";
|
|
13345
|
+
const input = {};
|
|
13346
|
+
if (mode === "1") {
|
|
13347
|
+
input.email = (await rl2.question("\u90AE\u7BB1\uFF1A")).trim();
|
|
13348
|
+
input.password = await askHidden(rl2, "\u5BC6\u7801\uFF1A");
|
|
13349
|
+
if (!input.email || !input.password) {
|
|
13350
|
+
cliErr("\u90AE\u7BB1\u4E0E\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
13351
|
+
return null;
|
|
13352
|
+
}
|
|
13353
|
+
} else if (mode === "2") {
|
|
13354
|
+
input.phone = (await rl2.question("\u624B\u673A\u53F7\uFF1A")).trim();
|
|
13355
|
+
input.password = await askHidden(rl2, "\u5BC6\u7801\uFF1A");
|
|
13356
|
+
if (!input.phone || !input.password) {
|
|
13357
|
+
cliErr("\u624B\u673A\u53F7\u4E0E\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
13358
|
+
return null;
|
|
13359
|
+
}
|
|
13360
|
+
} else if (mode === "3") {
|
|
13361
|
+
input.phone = (await rl2.question("\u624B\u673A\u53F7\uFF1A")).trim();
|
|
13362
|
+
if (!input.phone) {
|
|
13363
|
+
cliErr("\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
13364
|
+
return null;
|
|
13365
|
+
}
|
|
13366
|
+
try {
|
|
13367
|
+
const sent = await hubSendSms(resolveDefaultHub(hubUrl), {
|
|
13368
|
+
phone: input.phone,
|
|
13369
|
+
purpose: "login"
|
|
13370
|
+
});
|
|
13371
|
+
if (sent.debugCode) cliLog(`\uFF08\u8C03\u8BD5\u6A21\u5F0F\u9A8C\u8BC1\u7801\uFF1A${sent.debugCode}\uFF09`);
|
|
13372
|
+
else cliLog("\u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0C5 \u5206\u949F\u5185\u6709\u6548\u3002");
|
|
13373
|
+
} catch (e) {
|
|
13374
|
+
cliErr(`\u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
13375
|
+
cliLog(" \u53EF\u6539\u7528\u65B9\u5F0F 1/2\uFF08\u5BC6\u7801\u767B\u5F55\uFF09\uFF0C\u6216\u5728\u7F51\u9875\u7AEF\u751F\u6210\u63A5\u5165\u7801\u540E\u6267\u884C\uFF1A");
|
|
13376
|
+
cliLog(" zqagent link --hub <Hub\u5730\u5740> --code <\u63A5\u5165\u7801>");
|
|
13377
|
+
return null;
|
|
13378
|
+
}
|
|
13379
|
+
input.code = (await rl2.question("\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF1A")).trim();
|
|
13380
|
+
if (!input.code) {
|
|
13381
|
+
cliErr("\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
13382
|
+
return null;
|
|
13383
|
+
}
|
|
13384
|
+
} else {
|
|
13385
|
+
cliErr("\u672A\u77E5\u7684\u767B\u5F55\u65B9\u5F0F\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
13386
|
+
return null;
|
|
13387
|
+
}
|
|
13388
|
+
return input;
|
|
13389
|
+
}
|
|
13390
|
+
async function promptLinkAccount(rl2, hubUrl, opts = {}) {
|
|
13391
|
+
const fallback = resolveDefaultHub(hubUrl);
|
|
13392
|
+
let resolved = fallback;
|
|
13393
|
+
if (opts.askHub !== false) {
|
|
13394
|
+
const answer = await promptHubUrl(rl2, fallback);
|
|
13395
|
+
if (!answer) {
|
|
13396
|
+
cliLog("\u5DF2\u8DF3\u8FC7\u63A5\u5165\uFF1A\u8282\u70B9\u4FDD\u6301\u7EAF\u672C\u5730\u8FD0\u884C\u3002");
|
|
13397
|
+
return null;
|
|
13398
|
+
}
|
|
13399
|
+
resolved = answer;
|
|
13400
|
+
}
|
|
13401
|
+
const auth = await promptAccountLogin(rl2, resolved);
|
|
13402
|
+
if (!auth) return null;
|
|
13403
|
+
return { hubUrl: resolved, auth };
|
|
13404
|
+
}
|
|
13405
|
+
async function promptPassword(rl2, prompt = "\u5BC6\u7801\uFF1A") {
|
|
13406
|
+
return askHidden(rl2, prompt);
|
|
13407
|
+
}
|
|
13408
|
+
function resolveDataDir(dataDir) {
|
|
13409
|
+
return path25.resolve(dataDir ?? process.env.DATA_DIR ?? DEFAULT_DATA_DIR);
|
|
13410
|
+
}
|
|
13411
|
+
async function maybeAccountEnroll(opts = {}) {
|
|
13412
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
13413
|
+
let store;
|
|
13414
|
+
try {
|
|
13415
|
+
store = new Store(resolveDataDir(opts.dataDir));
|
|
13416
|
+
if (resolveFleetConfig(store)) return;
|
|
13417
|
+
} catch (e) {
|
|
13418
|
+
cliErr(`\u63A5\u5165\u68C0\u67E5\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
13419
|
+
return;
|
|
13420
|
+
}
|
|
13421
|
+
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
13422
|
+
try {
|
|
13423
|
+
await runWizard(rl2, store, opts);
|
|
13424
|
+
} catch (e) {
|
|
13425
|
+
cliErr(`\u63A5\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
13426
|
+
cliLog(" \u53EF\u7A0D\u540E\u5728\u63A7\u5236\u53F0\u751F\u6210\u63A5\u5165\u7801\uFF0C\u518D\u6267\u884C\uFF1Azqagent link --hub <Hub\u5730\u5740> --code <\u63A5\u5165\u7801>");
|
|
13427
|
+
} finally {
|
|
13428
|
+
rl2.close();
|
|
13429
|
+
}
|
|
13430
|
+
}
|
|
13431
|
+
async function runWizard(rl2, store, opts) {
|
|
13432
|
+
cliLog("");
|
|
13433
|
+
cliLog("\u672C\u673A\u5C1A\u672A\u63A5\u5165 Hub\u3002\u53EF\u7528 Hub \u8D26\u53F7\u63A5\u5165\uFF08\u8282\u70B9\u9ED8\u8BA4\u8FDB\u5165\u300C\u672A\u5206\u7EC4\u300D\uFF09\uFF1B\u8F93\u5165 - \u8DF3\u8FC7\u4FDD\u6301\u7EAF\u672C\u5730\u3002");
|
|
13434
|
+
const prompted = await promptLinkAccount(rl2, opts.hubUrl);
|
|
13435
|
+
if (!prompted) return;
|
|
13436
|
+
const nodeId = ensureNodeId(store);
|
|
13437
|
+
const cred = await hubEnrollByAccount(prompted.hubUrl, {
|
|
13438
|
+
...prompted.auth,
|
|
13439
|
+
nodeId,
|
|
13440
|
+
label: opts.label
|
|
13441
|
+
});
|
|
13442
|
+
store.saveFleetCredential({
|
|
13443
|
+
...cred,
|
|
13444
|
+
label: opts.label ?? cred.label,
|
|
13445
|
+
enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13446
|
+
});
|
|
13447
|
+
cliLog(`\u2705 \u5DF2\u63A5\u5165 Hub\uFF1A\u8282\u70B9 ${cred.nodeId}\uFF08\u7F51\u5173 ${cred.gatewayUrl ?? "\u672A\u4E0B\u53D1"}\uFF09`);
|
|
13448
|
+
cliLog(" \u51ED\u636E\u5DF2\u4FDD\u5B58\u5230 fleet.json\uFF0C\u672C\u6B21\u542F\u52A8\u5373\u8FDE\u63A5 Hub\u3002");
|
|
13449
|
+
}
|
|
13450
|
+
async function askHidden(rl2, prompt) {
|
|
13451
|
+
if (!process.stdin.isTTY) return (await rl2.question(prompt)).trim();
|
|
13452
|
+
return new Promise((resolve24) => {
|
|
13453
|
+
const saved = process.stdin.listeners("data");
|
|
13454
|
+
process.stdin.removeAllListeners("data");
|
|
13455
|
+
cliWrite(prompt);
|
|
13456
|
+
rl2.pause();
|
|
13457
|
+
process.stdin.setRawMode(true);
|
|
13458
|
+
process.stdin.resume();
|
|
13459
|
+
let buf = "";
|
|
13460
|
+
const cleanup = () => {
|
|
13461
|
+
process.stdin.removeListener("data", onData);
|
|
13462
|
+
for (const listener of saved) {
|
|
13463
|
+
process.stdin.on("data", listener);
|
|
13464
|
+
}
|
|
13465
|
+
process.stdin.setRawMode(true);
|
|
13466
|
+
rl2.resume();
|
|
13467
|
+
};
|
|
13468
|
+
const onData = (chunk) => {
|
|
13469
|
+
const s = chunk.toString().replace(/\x1b\[[0-9;?]*[A-Za-z~]/g, "").replace(/\x1bO[A-Za-z]/g, "");
|
|
13470
|
+
for (const ch of s) {
|
|
13471
|
+
if (ch === "\r" || ch === "\n") {
|
|
13472
|
+
cleanup();
|
|
13473
|
+
cliWrite("\n");
|
|
13474
|
+
resolve24(buf);
|
|
13475
|
+
return;
|
|
13476
|
+
}
|
|
13477
|
+
if (ch === "") {
|
|
13478
|
+
cleanup();
|
|
13479
|
+
cliWrite("\n");
|
|
13480
|
+
resolve24("");
|
|
13481
|
+
return;
|
|
13482
|
+
}
|
|
13483
|
+
if (ch === "\x7F" || ch === "\b") {
|
|
13484
|
+
if (buf.length > 0) {
|
|
13485
|
+
buf = buf.slice(0, -1);
|
|
13486
|
+
cliWrite("\b \b");
|
|
13487
|
+
}
|
|
13488
|
+
} else if (ch >= " ") {
|
|
13489
|
+
buf += ch;
|
|
13490
|
+
cliWrite("*");
|
|
13491
|
+
}
|
|
13492
|
+
}
|
|
13493
|
+
};
|
|
13494
|
+
process.stdin.on("data", onData);
|
|
13495
|
+
});
|
|
13496
|
+
}
|
|
13497
|
+
|
|
13498
|
+
// src/cli/serve-instances.ts
|
|
13499
|
+
init_esm_shims();
|
|
13500
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
13501
|
+
import * as fs21 from "fs";
|
|
13502
|
+
import * as os5 from "os";
|
|
13503
|
+
import * as path27 from "path";
|
|
13504
|
+
|
|
13505
|
+
// src/update/platform.ts
|
|
13506
|
+
init_esm_shims();
|
|
13507
|
+
import * as fs20 from "fs";
|
|
13508
|
+
import * as os4 from "os";
|
|
13509
|
+
import * as path26 from "path";
|
|
13510
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
13511
|
+
import { createRequire as createRequire3 } from "module";
|
|
13512
|
+
function currentVersion() {
|
|
13513
|
+
const injected = "0.2.12"?.trim();
|
|
13514
|
+
if (injected) return injected;
|
|
13515
|
+
try {
|
|
13516
|
+
const req2 = createRequire3(import.meta.url);
|
|
13517
|
+
const pkg = req2("../../package.json");
|
|
13518
|
+
if (pkg?.version) return pkg.version;
|
|
13519
|
+
} catch {
|
|
13520
|
+
}
|
|
13521
|
+
return "dev";
|
|
13522
|
+
}
|
|
13523
|
+
function isSeaRuntime() {
|
|
13524
|
+
if (process.env.ZQAGENT_SEA === "1") return true;
|
|
13525
|
+
const base = path26.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
13526
|
+
return base !== "node" && base !== "nodejs";
|
|
13527
|
+
}
|
|
13528
|
+
var DEFAULT_SERVICE_NAME = "zqagent";
|
|
13529
|
+
var DEFAULT_SERVICE_LABEL = "com.zqagent.node";
|
|
13530
|
+
function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
|
|
13531
|
+
const unitName = `${serviceName}.service`;
|
|
13532
|
+
for (const line of cgroup.split("\n")) {
|
|
13533
|
+
const idx = line.lastIndexOf(":");
|
|
13534
|
+
if (idx < 0) continue;
|
|
13535
|
+
const unitPath = line.slice(idx + 1).trim();
|
|
13536
|
+
if (unitPath.split("/").includes(unitName)) return "systemd";
|
|
13537
|
+
}
|
|
13538
|
+
return null;
|
|
13539
|
+
}
|
|
13540
|
+
function launchdDomain() {
|
|
13541
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : -1;
|
|
13542
|
+
return uid === 0 ? "system" : `gui/${uid}`;
|
|
13543
|
+
}
|
|
13544
|
+
function launchdLabel() {
|
|
13545
|
+
return process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL;
|
|
13546
|
+
}
|
|
13547
|
+
function launchdPlistPath() {
|
|
13548
|
+
const label = launchdLabel();
|
|
13549
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : -1;
|
|
13550
|
+
if (uid === 0) return `/Library/LaunchDaemons/${label}.plist`;
|
|
13551
|
+
return path26.join(os4.homedir(), "Library", "LaunchAgents", `${label}.plist`);
|
|
13552
|
+
}
|
|
13553
|
+
function serviceManagerFromLaunchctl(output, pid, execPath, sea = true) {
|
|
13554
|
+
const pidMatch = /^\s*pid = (\d+)\s*$/m.exec(output);
|
|
13555
|
+
if (pidMatch && Number(pidMatch[1]) === pid) return "launchd";
|
|
13556
|
+
const program = /^\s*program = (.+?)\s*$/m.exec(output);
|
|
13557
|
+
if (program) {
|
|
13558
|
+
const shown = program[1].replace(/^"|"$/g, "");
|
|
13559
|
+
if (path26.resolve(shown) === path26.resolve(execPath)) return "launchd";
|
|
13560
|
+
}
|
|
13561
|
+
if (!sea && /^\s*state = /m.test(output)) return "launchd";
|
|
13562
|
+
return null;
|
|
13563
|
+
}
|
|
13564
|
+
function parseLaunchdEnvironment(xml) {
|
|
13565
|
+
const out = {};
|
|
13566
|
+
const marker = xml.indexOf("<key>EnvironmentVariables</key>");
|
|
13567
|
+
if (marker < 0) return out;
|
|
13568
|
+
const open = xml.indexOf("<dict>", marker);
|
|
13569
|
+
if (open < 0) return out;
|
|
13570
|
+
const close = xml.indexOf("</dict>", open);
|
|
13571
|
+
if (close < 0) return out;
|
|
13572
|
+
const body = xml.slice(open + "<dict>".length, close);
|
|
13573
|
+
for (const m of body.matchAll(/<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g)) {
|
|
13574
|
+
out[m[1]] = m[2];
|
|
13575
|
+
}
|
|
13576
|
+
return out;
|
|
13577
|
+
}
|
|
13578
|
+
function detectServiceManager() {
|
|
13579
|
+
if (process.platform === "darwin") return detectLaunchd();
|
|
13580
|
+
if (process.platform !== "linux") return null;
|
|
13581
|
+
const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
|
|
13582
|
+
try {
|
|
13583
|
+
return serviceManagerFromCgroup(
|
|
13584
|
+
fs20.readFileSync("/proc/self/cgroup", "utf8"),
|
|
13585
|
+
serviceName
|
|
13586
|
+
);
|
|
13587
|
+
} catch {
|
|
13588
|
+
return null;
|
|
13589
|
+
}
|
|
13590
|
+
}
|
|
13591
|
+
function detectLaunchd() {
|
|
13592
|
+
try {
|
|
13593
|
+
const res = spawnSync2(
|
|
13594
|
+
"launchctl",
|
|
13595
|
+
["print", `${launchdDomain()}/${launchdLabel()}`],
|
|
13596
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
13597
|
+
);
|
|
13598
|
+
if (res.status !== 0 || !res.stdout) return null;
|
|
13599
|
+
return serviceManagerFromLaunchctl(
|
|
13600
|
+
res.stdout,
|
|
13601
|
+
process.pid,
|
|
13602
|
+
process.execPath,
|
|
13603
|
+
isSeaRuntime()
|
|
13604
|
+
);
|
|
13605
|
+
} catch {
|
|
13606
|
+
return null;
|
|
13607
|
+
}
|
|
13608
|
+
}
|
|
13609
|
+
function serviceManager() {
|
|
13610
|
+
const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
|
|
13611
|
+
if (v === "systemd" || v === "launchd") return v;
|
|
13612
|
+
return detectServiceManager();
|
|
13613
|
+
}
|
|
13614
|
+
function parseServiceEnvironment(raw) {
|
|
13615
|
+
const out = {};
|
|
13616
|
+
for (const token of raw.trim().split(/\s+/)) {
|
|
13617
|
+
const idx = token.indexOf("=");
|
|
13618
|
+
if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
|
|
13619
|
+
}
|
|
13620
|
+
return out;
|
|
13621
|
+
}
|
|
13622
|
+
var cachedServiceEnv = null;
|
|
13623
|
+
function readServiceEnvironment() {
|
|
13624
|
+
const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
|
|
13625
|
+
try {
|
|
13626
|
+
const res = spawnSync2(
|
|
13627
|
+
"systemctl",
|
|
13628
|
+
["show", serviceName, "--property=Environment", "--value", "--no-pager"],
|
|
13629
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
13630
|
+
);
|
|
13631
|
+
if (res.status !== 0 || !res.stdout) return {};
|
|
13632
|
+
return parseServiceEnvironment(res.stdout);
|
|
13633
|
+
} catch {
|
|
13634
|
+
return {};
|
|
13635
|
+
}
|
|
13636
|
+
}
|
|
13637
|
+
function serviceEnvValue(key) {
|
|
13638
|
+
if (process.platform === "darwin") {
|
|
13639
|
+
if (serviceManager() !== "launchd") return void 0;
|
|
13640
|
+
cachedLaunchdEnv ??= readLaunchdEnvironment();
|
|
13641
|
+
return cachedLaunchdEnv[key];
|
|
13642
|
+
}
|
|
13643
|
+
if (process.platform !== "linux") return void 0;
|
|
13644
|
+
if (serviceManager() !== "systemd") return void 0;
|
|
13645
|
+
cachedServiceEnv ??= readServiceEnvironment();
|
|
13646
|
+
return cachedServiceEnv[key];
|
|
13647
|
+
}
|
|
13648
|
+
var cachedLaunchdEnv = null;
|
|
13649
|
+
function readLaunchdEnvironment() {
|
|
13650
|
+
try {
|
|
13651
|
+
return parseLaunchdEnvironment(fs20.readFileSync(launchdPlistPath(), "utf8"));
|
|
13652
|
+
} catch {
|
|
13653
|
+
return {};
|
|
13654
|
+
}
|
|
13655
|
+
}
|
|
13656
|
+
function selfUpdateCapability(hasPublicKey) {
|
|
13657
|
+
if (!isSeaRuntime()) {
|
|
13658
|
+
return {
|
|
13659
|
+
ok: false,
|
|
13660
|
+
reason: "\u5F53\u524D\u4E0D\u662F\u5355\u6587\u4EF6\uFF08SEA\uFF09\u5F62\u6001\uFF1A\u8BF7\u7528 npm \u5347\u7EA7\uFF08npm i -g @zqagent/core@latest\uFF09"
|
|
13661
|
+
};
|
|
13662
|
+
}
|
|
13663
|
+
if (!serviceManager()) {
|
|
13664
|
+
return {
|
|
13665
|
+
ok: false,
|
|
13666
|
+
reason: "\u672A\u88AB systemd/launchd \u6258\u7BA1\uFF1A\u8BF7\u7528 deploy/service/install.sh \u5B89\u88C5\uFF0C\u6216\u624B\u52A8\u5347\u7EA7"
|
|
13667
|
+
};
|
|
13668
|
+
}
|
|
13669
|
+
if (!hasPublicKey) {
|
|
13670
|
+
return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
|
|
13671
|
+
}
|
|
13672
|
+
return { ok: true };
|
|
13673
|
+
}
|
|
13674
|
+
|
|
13675
|
+
// src/cli/serve-instances.ts
|
|
13676
|
+
var DEFAULT_SERVE_HOST = "127.0.0.1";
|
|
13677
|
+
var DEFAULT_SERVE_PORT = 8787;
|
|
13678
|
+
var DEFAULT_SERVICE_NAME2 = "zqagent";
|
|
13679
|
+
var DEFAULT_SERVICE_LABEL2 = "com.zqagent.node";
|
|
13680
|
+
function serveSubcommandIndex(argv) {
|
|
13681
|
+
for (let i = 1; i < argv.length; i++) {
|
|
13682
|
+
if (argv[i].startsWith("-")) continue;
|
|
13683
|
+
return argv[i] === "serve" ? i : -1;
|
|
13684
|
+
}
|
|
13685
|
+
return -1;
|
|
13686
|
+
}
|
|
13687
|
+
function parseServeArgs(argv) {
|
|
13688
|
+
const at = serveSubcommandIndex(argv);
|
|
13689
|
+
if (at < 0) return null;
|
|
13690
|
+
let dataDir = null;
|
|
13691
|
+
let host = DEFAULT_SERVE_HOST;
|
|
13692
|
+
let port = DEFAULT_SERVE_PORT;
|
|
13693
|
+
for (let i = at + 1; i < argv.length; i++) {
|
|
13694
|
+
const arg = argv[i];
|
|
13695
|
+
if (arg === "--data-dir" && argv[i + 1]) {
|
|
13696
|
+
dataDir = argv[++i];
|
|
13697
|
+
} else if (arg.startsWith("--data-dir=")) {
|
|
13698
|
+
dataDir = arg.slice("--data-dir=".length);
|
|
13699
|
+
} else if (arg === "--host" && argv[i + 1]) {
|
|
13700
|
+
host = argv[++i];
|
|
13701
|
+
} else if (arg.startsWith("--host=")) {
|
|
13702
|
+
host = arg.slice("--host=".length);
|
|
13703
|
+
} else if (arg === "--port" && argv[i + 1]) {
|
|
13704
|
+
port = Number(argv[++i]);
|
|
13705
|
+
} else if (arg.startsWith("--port=")) {
|
|
13706
|
+
port = Number(arg.slice("--port=".length));
|
|
13707
|
+
}
|
|
13708
|
+
}
|
|
13709
|
+
if (!Number.isFinite(port) || port <= 0) port = DEFAULT_SERVE_PORT;
|
|
13710
|
+
if (host === "0.0.0.0" || host === "::" || host === "") host = DEFAULT_SERVE_HOST;
|
|
13711
|
+
return { dataDir, host, port };
|
|
13712
|
+
}
|
|
13713
|
+
function buildHotCandidates(explicitDataDir, discovered, fallback) {
|
|
13714
|
+
const out = [];
|
|
13715
|
+
const push = (inst) => {
|
|
13716
|
+
if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
|
|
13717
|
+
};
|
|
13718
|
+
if (explicitDataDir) push({ dataDir: path27.resolve(explicitDataDir), ...fallback });
|
|
13719
|
+
for (const inst of discovered) if (inst.managed) push(inst);
|
|
13720
|
+
for (const inst of discovered) if (!inst.managed) push(inst);
|
|
13721
|
+
if (!explicitDataDir) push({ dataDir: path27.resolve(DEFAULT_DATA_DIR), ...fallback });
|
|
13722
|
+
return out;
|
|
13723
|
+
}
|
|
13724
|
+
function isWindowsAbsolute(p) {
|
|
13725
|
+
return /^[A-Za-z]:[\\/]/.test(p) || /^\\\\/.test(p);
|
|
13726
|
+
}
|
|
13727
|
+
function isAbsolutePath(p) {
|
|
13728
|
+
return path27.isAbsolute(p) || isWindowsAbsolute(p);
|
|
13729
|
+
}
|
|
13730
|
+
function normalizePath(p) {
|
|
13731
|
+
return isWindowsAbsolute(p) ? path27.win32.normalize(p) : path27.normalize(p);
|
|
13732
|
+
}
|
|
13733
|
+
function parseServeCommandLine(line) {
|
|
13734
|
+
if (!/(?:^|\s)["']?serve["']?(?=\s|$)/.test(line)) return null;
|
|
13735
|
+
const m = /--data-dir(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(.+?))(?=\s+--\w|\s*$)/.exec(line);
|
|
13736
|
+
const rawDataDir = (m?.[1] ?? m?.[2] ?? m?.[3] ?? "").trim();
|
|
13737
|
+
if (!rawDataDir || !isAbsolutePath(rawDataDir)) return null;
|
|
13738
|
+
const host = /--host(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(\S+))/.exec(line);
|
|
13739
|
+
const rawHost = (host?.[1] ?? host?.[2] ?? host?.[3] ?? DEFAULT_SERVE_HOST).trim();
|
|
13740
|
+
const portMatch = /--port(?:=|\s+)(\d+)/.exec(line);
|
|
13741
|
+
const port = Number(portMatch?.[1] ?? DEFAULT_SERVE_PORT);
|
|
13742
|
+
return {
|
|
13743
|
+
dataDir: normalizePath(rawDataDir),
|
|
13744
|
+
host: rawHost === "0.0.0.0" || rawHost === "::" || rawHost === "" ? DEFAULT_SERVE_HOST : rawHost,
|
|
13745
|
+
port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT
|
|
13746
|
+
};
|
|
13747
|
+
}
|
|
13748
|
+
function processCwd(pid) {
|
|
13749
|
+
try {
|
|
13750
|
+
return fs21.readlinkSync(`/proc/${pid}/cwd`);
|
|
13751
|
+
} catch {
|
|
13752
|
+
return null;
|
|
13753
|
+
}
|
|
13754
|
+
}
|
|
13755
|
+
function processEnv(pid) {
|
|
13756
|
+
const out = {};
|
|
13757
|
+
try {
|
|
13758
|
+
const raw = fs21.readFileSync(`/proc/${pid}/environ`, "utf-8");
|
|
13759
|
+
for (const entry of raw.split("\0")) {
|
|
13760
|
+
const eq = entry.indexOf("=");
|
|
13761
|
+
if (eq > 0) out[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
13762
|
+
}
|
|
13763
|
+
} catch {
|
|
13764
|
+
}
|
|
13765
|
+
return out;
|
|
13766
|
+
}
|
|
13767
|
+
function resolveDataDir2(rawDataDir, cwd, env3) {
|
|
13768
|
+
const pick = rawDataDir ?? env3.DATA_DIR ?? null;
|
|
13769
|
+
if (pick) return path27.isAbsolute(pick) ? path27.normalize(pick) : cwd ? path27.resolve(cwd, pick) : null;
|
|
13770
|
+
return cwd ? path27.resolve(cwd, DEFAULT_DATA_DIR) : null;
|
|
13771
|
+
}
|
|
13772
|
+
function isManagedBySystemd(pid, env3) {
|
|
13773
|
+
if (env3.ZQAGENT_SERVICE_MANAGER?.trim()) return true;
|
|
13774
|
+
try {
|
|
13775
|
+
const cgroup = fs21.readFileSync(`/proc/${pid}/cgroup`, "utf-8");
|
|
13776
|
+
const serviceName = env3.ZQAGENT_SERVICE_NAME?.trim() || "zqagent";
|
|
13777
|
+
return serviceManagerFromCgroup(cgroup, serviceName) !== null;
|
|
13778
|
+
} catch {
|
|
13779
|
+
return false;
|
|
13780
|
+
}
|
|
13781
|
+
}
|
|
13782
|
+
function discoverFromProc() {
|
|
13783
|
+
const out = [];
|
|
13784
|
+
let pids;
|
|
13785
|
+
try {
|
|
13786
|
+
pids = fs21.readdirSync("/proc");
|
|
13787
|
+
} catch {
|
|
13788
|
+
return out;
|
|
13789
|
+
}
|
|
13790
|
+
for (const name of pids) {
|
|
13791
|
+
if (!/^\d+$/.test(name)) continue;
|
|
13792
|
+
const pid = Number(name);
|
|
13793
|
+
if (pid === process.pid) continue;
|
|
13794
|
+
let argv;
|
|
13795
|
+
let env3;
|
|
13796
|
+
try {
|
|
13797
|
+
argv = fs21.readFileSync(`/proc/${pid}/cmdline`, "utf-8").split("\0").filter(Boolean);
|
|
13798
|
+
env3 = processEnv(pid);
|
|
13799
|
+
} catch {
|
|
13800
|
+
continue;
|
|
13801
|
+
}
|
|
13802
|
+
const parsed = parseServeArgs(argv);
|
|
13803
|
+
if (!parsed) continue;
|
|
13804
|
+
const dataDir = resolveDataDir2(parsed.dataDir, processCwd(pid), env3);
|
|
13805
|
+
if (!dataDir) continue;
|
|
13806
|
+
out.push({
|
|
13807
|
+
dataDir,
|
|
13808
|
+
host: parsed.host,
|
|
13809
|
+
port: parsed.port,
|
|
13810
|
+
managed: isManagedBySystemd(pid, env3)
|
|
13811
|
+
});
|
|
13822
13812
|
}
|
|
13823
|
-
|
|
13824
|
-
|
|
13825
|
-
|
|
13826
|
-
|
|
13827
|
-
|
|
13828
|
-
|
|
13813
|
+
return out;
|
|
13814
|
+
}
|
|
13815
|
+
function discoverFromPs() {
|
|
13816
|
+
const out = [];
|
|
13817
|
+
const res = spawnSync3("ps", ["-axo", "command="], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
13818
|
+
if (res.status !== 0 || !res.stdout) return out;
|
|
13819
|
+
for (const line of res.stdout.split("\n")) {
|
|
13820
|
+
const inst = parseServeCommandLine(line);
|
|
13821
|
+
if (inst) out.push(inst);
|
|
13829
13822
|
}
|
|
13830
|
-
|
|
13831
|
-
code;
|
|
13832
|
-
};
|
|
13833
|
-
|
|
13834
|
-
// src/server/routes.ts
|
|
13835
|
-
init_esm_shims();
|
|
13836
|
-
import { createServer } from "http";
|
|
13837
|
-
import * as path31 from "path";
|
|
13838
|
-
|
|
13839
|
-
// src/server/projects.ts
|
|
13840
|
-
init_esm_shims();
|
|
13841
|
-
import * as path27 from "path";
|
|
13842
|
-
var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
|
|
13843
|
-
function toProjectSummary(store, tenant) {
|
|
13844
|
-
return {
|
|
13845
|
-
id: tenant.id,
|
|
13846
|
-
name: tenant.name ?? tenant.id,
|
|
13847
|
-
model: tenant.model,
|
|
13848
|
-
baseURL: tenant.provider?.baseURL ?? "",
|
|
13849
|
-
hasApiKey: Boolean(tenant.provider?.apiKey),
|
|
13850
|
-
workspaceRoot: tenant.workspaceRoot ?? null,
|
|
13851
|
-
sessionCount: store.listSessions(tenant.id).length
|
|
13852
|
-
};
|
|
13823
|
+
return out;
|
|
13853
13824
|
}
|
|
13854
|
-
function
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
13825
|
+
function discoverFromWindows() {
|
|
13826
|
+
const out = [];
|
|
13827
|
+
const script = `[Console]::OutputEncoding=[Text.Encoding]::UTF8; Get-CimInstance Win32_Process -Filter "Name='zqagent.exe' OR Name='node.exe'" | ForEach-Object { $_.CommandLine }`;
|
|
13828
|
+
const res = spawnSync3(
|
|
13829
|
+
"powershell.exe",
|
|
13830
|
+
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
13831
|
+
{ encoding: "utf-8", timeout: 15e3, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
|
|
13832
|
+
);
|
|
13833
|
+
if (res.status !== 0 || !res.stdout) return out;
|
|
13834
|
+
for (const line of res.stdout.replace(/^\uFEFF/, "").split(/\r?\n/)) {
|
|
13835
|
+
const inst = parseServeCommandLine(line);
|
|
13836
|
+
if (inst) out.push(inst);
|
|
13837
|
+
}
|
|
13838
|
+
return out;
|
|
13868
13839
|
}
|
|
13869
|
-
function
|
|
13870
|
-
|
|
13871
|
-
|
|
13872
|
-
|
|
13873
|
-
|
|
13874
|
-
|
|
13875
|
-
|
|
13876
|
-
|
|
13877
|
-
|
|
13878
|
-
|
|
13879
|
-
|
|
13880
|
-
|
|
13881
|
-
|
|
13882
|
-
|
|
13883
|
-
|
|
13884
|
-
|
|
13885
|
-
|
|
13886
|
-
|
|
13887
|
-
|
|
13888
|
-
|
|
13889
|
-
|
|
13890
|
-
|
|
13840
|
+
function decodeXmlEntities(s) {
|
|
13841
|
+
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
13842
|
+
}
|
|
13843
|
+
function parseLaunchdProgramArguments(xml) {
|
|
13844
|
+
const marker = xml.indexOf("<key>ProgramArguments</key>");
|
|
13845
|
+
if (marker < 0) return [];
|
|
13846
|
+
const open = xml.indexOf("<array>", marker);
|
|
13847
|
+
if (open < 0) return [];
|
|
13848
|
+
const close = xml.indexOf("</array>", open);
|
|
13849
|
+
if (close < 0) return [];
|
|
13850
|
+
return [...xml.slice(open + "<array>".length, close).matchAll(/<string>([^<]*)<\/string>/g)].map(
|
|
13851
|
+
(m) => decodeXmlEntities(m[1])
|
|
13852
|
+
);
|
|
13853
|
+
}
|
|
13854
|
+
function launchdServiceInstances() {
|
|
13855
|
+
if (process.platform !== "darwin") return [];
|
|
13856
|
+
const label = process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL2;
|
|
13857
|
+
const plists = [
|
|
13858
|
+
`/Library/LaunchDaemons/${label}.plist`,
|
|
13859
|
+
path27.join(os5.homedir(), "Library", "LaunchAgents", `${label}.plist`)
|
|
13860
|
+
];
|
|
13861
|
+
const out = [];
|
|
13862
|
+
for (const plist of plists) {
|
|
13863
|
+
let xml;
|
|
13864
|
+
try {
|
|
13865
|
+
xml = fs21.readFileSync(plist, "utf-8");
|
|
13866
|
+
} catch {
|
|
13867
|
+
continue;
|
|
13891
13868
|
}
|
|
13892
|
-
|
|
13869
|
+
const parsed = parseServeArgs(parseLaunchdProgramArguments(xml));
|
|
13870
|
+
if (!parsed?.dataDir || !isAbsolutePath(parsed.dataDir)) continue;
|
|
13871
|
+
out.push({
|
|
13872
|
+
dataDir: normalizePath(parsed.dataDir),
|
|
13873
|
+
host: parsed.host,
|
|
13874
|
+
port: parsed.port,
|
|
13875
|
+
managed: true
|
|
13876
|
+
});
|
|
13893
13877
|
}
|
|
13894
|
-
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
|
|
13878
|
+
return out;
|
|
13879
|
+
}
|
|
13880
|
+
function parseSystemdUnit(text) {
|
|
13881
|
+
let dataDir = null;
|
|
13882
|
+
let host = DEFAULT_SERVE_HOST;
|
|
13883
|
+
let port = DEFAULT_SERVE_PORT;
|
|
13884
|
+
for (const line of text.split("\n")) {
|
|
13885
|
+
const trimmed = line.trim();
|
|
13886
|
+
if (trimmed.startsWith("WorkingDirectory=")) {
|
|
13887
|
+
const v = trimmed.slice("WorkingDirectory=".length).trim();
|
|
13888
|
+
if (v && isAbsolutePath(v)) dataDir = v;
|
|
13889
|
+
} else if (trimmed.startsWith("ExecStart=")) {
|
|
13890
|
+
const tokens = trimmed.slice("ExecStart=".length).trim().split(/\s+/).map((t) => t.replace(/^["']|["']$/g, ""));
|
|
13891
|
+
const parsed = parseServeArgs(tokens);
|
|
13892
|
+
if (!parsed) continue;
|
|
13893
|
+
if (parsed.dataDir && isAbsolutePath(parsed.dataDir)) dataDir = parsed.dataDir;
|
|
13894
|
+
host = parsed.host;
|
|
13895
|
+
port = parsed.port;
|
|
13903
13896
|
}
|
|
13904
13897
|
}
|
|
13898
|
+
if (!dataDir) return null;
|
|
13899
|
+
return { dataDir: normalizePath(dataDir), host, port, managed: true };
|
|
13905
13900
|
}
|
|
13906
|
-
function
|
|
13907
|
-
|
|
13901
|
+
function systemdServiceInstances() {
|
|
13902
|
+
if (process.platform !== "linux") return [];
|
|
13903
|
+
const unit = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME2;
|
|
13904
|
+
const configHome = process.env.XDG_CONFIG_HOME?.trim() || path27.join(os5.homedir(), ".config");
|
|
13905
|
+
const units = [
|
|
13906
|
+
`/etc/systemd/system/${unit}.service`,
|
|
13907
|
+
path27.join(configHome, "systemd", "user", `${unit}.service`)
|
|
13908
|
+
];
|
|
13909
|
+
const out = [];
|
|
13910
|
+
for (const file of units) {
|
|
13911
|
+
let text;
|
|
13912
|
+
try {
|
|
13913
|
+
text = fs21.readFileSync(file, "utf-8");
|
|
13914
|
+
} catch {
|
|
13915
|
+
continue;
|
|
13916
|
+
}
|
|
13917
|
+
const inst = parseSystemdUnit(text);
|
|
13918
|
+
if (inst) out.push(inst);
|
|
13919
|
+
}
|
|
13920
|
+
return out;
|
|
13921
|
+
}
|
|
13922
|
+
function windowsServiceInstances() {
|
|
13923
|
+
if (process.platform !== "win32") return [];
|
|
13924
|
+
const appdata = process.env.APPDATA?.trim();
|
|
13925
|
+
if (!appdata) return [];
|
|
13926
|
+
const base = path27.join(appdata, "zqagent");
|
|
13927
|
+
try {
|
|
13928
|
+
const raw = fs21.readFileSync(path27.join(base, "runtime", "service.json"), "utf-8");
|
|
13929
|
+
const v = JSON.parse(raw);
|
|
13930
|
+
const dataDir = v.dataDir && isAbsolutePath(v.dataDir) ? v.dataDir : base;
|
|
13931
|
+
const port = Number(v.port);
|
|
13932
|
+
return [
|
|
13933
|
+
{
|
|
13934
|
+
dataDir: normalizePath(dataDir),
|
|
13935
|
+
host: DEFAULT_SERVE_HOST,
|
|
13936
|
+
port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT,
|
|
13937
|
+
managed: true
|
|
13938
|
+
}
|
|
13939
|
+
];
|
|
13940
|
+
} catch {
|
|
13941
|
+
return [];
|
|
13942
|
+
}
|
|
13943
|
+
}
|
|
13944
|
+
function mergeServeInstances(discovered, managed) {
|
|
13945
|
+
const out = [];
|
|
13946
|
+
const push = (inst) => {
|
|
13947
|
+
if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
|
|
13948
|
+
};
|
|
13949
|
+
for (const inst of discovered) {
|
|
13950
|
+
const hit = managed.find((m) => m.dataDir === inst.dataDir);
|
|
13951
|
+
push(hit ? { ...inst, managed: true } : inst);
|
|
13952
|
+
}
|
|
13953
|
+
for (const inst of managed) push(inst);
|
|
13954
|
+
return out;
|
|
13955
|
+
}
|
|
13956
|
+
function discoverRunningServeInstances() {
|
|
13957
|
+
const found = process.platform === "linux" ? discoverFromProc() : process.platform === "win32" ? discoverFromWindows() : discoverFromPs();
|
|
13958
|
+
const declared = process.platform === "linux" ? systemdServiceInstances() : process.platform === "win32" ? windowsServiceInstances() : launchdServiceInstances();
|
|
13959
|
+
return mergeServeInstances(found, declared);
|
|
13960
|
+
}
|
|
13961
|
+
|
|
13962
|
+
// src/cli/link.ts
|
|
13963
|
+
function expandHome(p) {
|
|
13964
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
13965
|
+
if (!home) return p;
|
|
13966
|
+
if (p === "~") return home;
|
|
13967
|
+
if (p.startsWith("~/") || p.startsWith("~\\")) return path28.join(home, p.slice(2));
|
|
13968
|
+
return p;
|
|
13969
|
+
}
|
|
13970
|
+
function installDataDirHint() {
|
|
13971
|
+
if (process.platform === "darwin") return "$HOME/Library/Application Support/zqagent";
|
|
13972
|
+
if (process.platform === "win32") return "%APPDATA%\\zqagent";
|
|
13973
|
+
return "/var/lib/zqagent\uFF08\u9700 root\uFF1B\u666E\u901A\u7528\u6237\u7528 ~/.zqagent_data\uFF09";
|
|
13908
13974
|
}
|
|
13909
|
-
function
|
|
13910
|
-
const
|
|
13911
|
-
|
|
13912
|
-
|
|
13913
|
-
|
|
13914
|
-
|
|
13915
|
-
|
|
13916
|
-
|
|
13917
|
-
|
|
13918
|
-
|
|
13919
|
-
|
|
13920
|
-
|
|
13921
|
-
|
|
13922
|
-
|
|
13975
|
+
function buildAccountAuth(flags, env3 = process.env) {
|
|
13976
|
+
const username = (flags.username ?? env3.ZQAGENT_HUB_USERNAME ?? "").trim();
|
|
13977
|
+
let email = (flags.email ?? "").trim();
|
|
13978
|
+
let phone = (flags.phone ?? "").trim();
|
|
13979
|
+
if (username && !email && !phone) {
|
|
13980
|
+
if (username.includes("@")) email = username;
|
|
13981
|
+
else phone = username;
|
|
13982
|
+
}
|
|
13983
|
+
if (!email && !phone) return null;
|
|
13984
|
+
const password = flags.password ?? env3.ZQAGENT_HUB_PASSWORD ?? "";
|
|
13985
|
+
const smsCode = (flags.smsCode ?? env3.ZQAGENT_HUB_SMS_CODE ?? "").trim();
|
|
13986
|
+
return {
|
|
13987
|
+
email: email ? email.toLowerCase() : void 0,
|
|
13988
|
+
phone: phone || void 0,
|
|
13989
|
+
password: password || void 0,
|
|
13990
|
+
smsCode: smsCode || void 0
|
|
13991
|
+
};
|
|
13923
13992
|
}
|
|
13924
|
-
function
|
|
13925
|
-
|
|
13926
|
-
if (rec.status !== "completed") return null;
|
|
13927
|
-
const msgs = rec.messages;
|
|
13928
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
13929
|
-
const m = msgs[i];
|
|
13930
|
-
if (!m || m.role !== "assistant") continue;
|
|
13931
|
-
if (typeof m.ts === "number" && Number.isFinite(m.ts)) {
|
|
13932
|
-
return new Date(m.ts).toISOString();
|
|
13933
|
-
}
|
|
13934
|
-
return null;
|
|
13935
|
-
}
|
|
13936
|
-
return null;
|
|
13993
|
+
function isCompleteAccount(account) {
|
|
13994
|
+
return Boolean(account && (account.password || account.smsCode));
|
|
13937
13995
|
}
|
|
13938
|
-
function
|
|
13939
|
-
const out =
|
|
13940
|
-
for (
|
|
13941
|
-
const
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
|
|
13949
|
-
|
|
13950
|
-
|
|
13951
|
-
|
|
13952
|
-
|
|
13953
|
-
|
|
13954
|
-
|
|
13955
|
-
}
|
|
13996
|
+
function parseLinkArgs(argv) {
|
|
13997
|
+
const out = {};
|
|
13998
|
+
for (let i = 0; i < argv.length; i++) {
|
|
13999
|
+
const a = argv[i];
|
|
14000
|
+
const val = argv[i + 1];
|
|
14001
|
+
if (a === "--hub") {
|
|
14002
|
+
out.hubUrl = val;
|
|
14003
|
+
i++;
|
|
14004
|
+
} else if (a === "--code") {
|
|
14005
|
+
out.code = val;
|
|
14006
|
+
i++;
|
|
14007
|
+
} else if (a === "--username" || a === "--user") {
|
|
14008
|
+
out.username = val;
|
|
14009
|
+
i++;
|
|
14010
|
+
} else if (a === "--email") {
|
|
14011
|
+
out.email = val;
|
|
14012
|
+
i++;
|
|
14013
|
+
} else if (a === "--phone") {
|
|
14014
|
+
out.phone = val;
|
|
14015
|
+
i++;
|
|
14016
|
+
} else if (a === "--password") {
|
|
14017
|
+
out.password = val;
|
|
14018
|
+
i++;
|
|
14019
|
+
} else if (a === "--sms-code") {
|
|
14020
|
+
out.smsCode = val;
|
|
14021
|
+
i++;
|
|
14022
|
+
} else if (a === "--data-dir") {
|
|
14023
|
+
out.dataDir = val;
|
|
14024
|
+
i++;
|
|
14025
|
+
} else if (a === "--label") {
|
|
14026
|
+
out.label = val;
|
|
14027
|
+
i++;
|
|
14028
|
+
} else if (a === "--host") {
|
|
14029
|
+
out.host = val;
|
|
14030
|
+
i++;
|
|
14031
|
+
} else if (a === "--port") {
|
|
14032
|
+
out.port = Number(val);
|
|
14033
|
+
i++;
|
|
14034
|
+
} else if (a === "-h" || a === "--help") {
|
|
14035
|
+
out.help = true;
|
|
13956
14036
|
}
|
|
13957
14037
|
}
|
|
13958
14038
|
return out;
|
|
@@ -13965,50 +14045,285 @@ function ensureNodeId2(store) {
|
|
|
13965
14045
|
}
|
|
13966
14046
|
return id;
|
|
13967
14047
|
}
|
|
13968
|
-
function
|
|
13969
|
-
const
|
|
13970
|
-
const
|
|
13971
|
-
|
|
13972
|
-
|
|
14048
|
+
async function probeServer(host, port, adminKey) {
|
|
14049
|
+
const ctrl = new AbortController();
|
|
14050
|
+
const timer = setTimeout(() => ctrl.abort(), 1500);
|
|
14051
|
+
try {
|
|
14052
|
+
const res = await fetch(`http://${host}:${port}/api/v1/fleet/status`, {
|
|
14053
|
+
headers: { Authorization: `Bearer ${adminKey}` },
|
|
14054
|
+
signal: ctrl.signal
|
|
14055
|
+
});
|
|
14056
|
+
return res.ok;
|
|
14057
|
+
} catch {
|
|
14058
|
+
return false;
|
|
14059
|
+
} finally {
|
|
14060
|
+
clearTimeout(timer);
|
|
13973
14061
|
}
|
|
13974
|
-
|
|
13975
|
-
|
|
13976
|
-
const
|
|
13977
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
model: typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : DEFAULT_MODEL(),
|
|
13982
|
-
provider: {
|
|
13983
|
-
apiKey: typeof raw.providerApiKey === "string" && raw.providerApiKey.trim() ? raw.providerApiKey.trim() : void 0,
|
|
13984
|
-
baseURL: typeof raw.baseURL === "string" && raw.baseURL.trim() ? raw.baseURL.trim() : void 0
|
|
14062
|
+
}
|
|
14063
|
+
async function enrollViaServer(host, port, adminKey, opts) {
|
|
14064
|
+
const res = await fetch(`http://${host}:${port}/api/v1/fleet/enroll`, {
|
|
14065
|
+
method: "POST",
|
|
14066
|
+
headers: {
|
|
14067
|
+
"Content-Type": "application/json",
|
|
14068
|
+
Authorization: `Bearer ${adminKey}`
|
|
13985
14069
|
},
|
|
13986
|
-
|
|
13987
|
-
|
|
14070
|
+
body: JSON.stringify({
|
|
14071
|
+
hubUrl: opts.hubUrl,
|
|
14072
|
+
code: opts.code,
|
|
14073
|
+
email: opts.account?.email,
|
|
14074
|
+
phone: opts.account?.phone,
|
|
14075
|
+
password: opts.account?.password,
|
|
14076
|
+
smsCode: opts.account?.smsCode,
|
|
14077
|
+
label: opts.label
|
|
14078
|
+
})
|
|
14079
|
+
});
|
|
14080
|
+
const text = await res.text();
|
|
14081
|
+
let payload = null;
|
|
14082
|
+
try {
|
|
14083
|
+
payload = text ? JSON.parse(text) : null;
|
|
14084
|
+
} catch {
|
|
14085
|
+
}
|
|
14086
|
+
if (!res.ok) {
|
|
14087
|
+
throw new Error(payload?.error?.message ?? `HTTP ${res.status}`);
|
|
14088
|
+
}
|
|
14089
|
+
return payload?.data;
|
|
14090
|
+
}
|
|
14091
|
+
function hotWarnings(inst, running) {
|
|
14092
|
+
const warnings = [];
|
|
14093
|
+
const managed = running.filter((r) => r.managed && r.dataDir !== inst.dataDir);
|
|
14094
|
+
if (!inst.managed && managed.length > 0) {
|
|
14095
|
+
warnings.push(
|
|
14096
|
+
`\u51ED\u636E\u5199\u5165\u4E86\u975E\u6258\u7BA1\u5B9E\u4F8B ${inst.dataDir}\uFF0C\u4F46\u672C\u673A\u6258\u7BA1\u670D\u52A1\u4F7F\u7528\u7684\u662F ${managed.map((m) => m.dataDir).join("\u3001")}\u3002`
|
|
14097
|
+
);
|
|
14098
|
+
warnings.push(
|
|
14099
|
+
"\u6258\u7BA1\u670D\u52A1\u8BFB\u4E0D\u5230\u8FD9\u4EFD\u51ED\u636E\uFF08\u91CD\u542F\u540E\u4E5F\u4E0D\u4F1A\u81EA\u52A8\u4E0A\u7EBF\uFF09\uFF0C\u8BF7\u7528 --data-dir \u6307\u5411\u5B83\u7684\u6570\u636E\u76EE\u5F55\u91CD\u65B0\u63A5\u5165\u3002"
|
|
14100
|
+
);
|
|
14101
|
+
}
|
|
14102
|
+
const others = [...new Set(running.map((r) => r.dataDir).filter((d) => d !== inst.dataDir))];
|
|
14103
|
+
if (others.length > 0) {
|
|
14104
|
+
warnings.push(`\u672C\u673A\u8FD8\u6709\u5176\u5B83\u8FD0\u884C\u4E2D\u7684\u8282\u70B9\u5B9E\u4F8B\uFF08\u672A\u5199\u5165\u51ED\u636E\uFF09\uFF1A${others.join("\u3001")}\u3002`);
|
|
14105
|
+
}
|
|
14106
|
+
return warnings;
|
|
14107
|
+
}
|
|
14108
|
+
async function linkNode(opts) {
|
|
14109
|
+
if (!opts.code && !isCompleteAccount(opts.account ?? null)) {
|
|
14110
|
+
throw new Error("\u7F3A\u5C11\u63A5\u5165\u7801\uFF0C\u6216 Hub \u8D26\u53F7\u5BC6\u7801/\u77ED\u4FE1\u9A8C\u8BC1\u7801");
|
|
14111
|
+
}
|
|
14112
|
+
const hubUrl = opts.hubUrl.trim().replace(/\/$/, "");
|
|
14113
|
+
if (!hubUrl) throw new Error("\u7F3A\u5C11 Hub \u5730\u5740");
|
|
14114
|
+
const rawDataDir = opts.dataDir?.trim() || process.env.DATA_DIR?.trim() || void 0;
|
|
14115
|
+
const explicitDataDir = rawDataDir ? expandHome(rawDataDir) : void 0;
|
|
14116
|
+
const host = opts.host?.trim() || process.env.HOST?.trim() || DEFAULT_SERVE_HOST;
|
|
14117
|
+
const port = opts.port ?? Number(process.env.PORT ?? DEFAULT_SERVE_PORT);
|
|
14118
|
+
const running = discoverRunningServeInstances();
|
|
14119
|
+
const candidates = buildHotCandidates(explicitDataDir, running, { host, port });
|
|
14120
|
+
for (const inst of candidates) {
|
|
14121
|
+
const store2 = new Store(inst.dataDir);
|
|
14122
|
+
const adminKey = process.env.ADMIN_API_KEY ?? store2.loadAdminKey();
|
|
14123
|
+
if (!adminKey) continue;
|
|
14124
|
+
if (!await probeServer(inst.host, inst.port, adminKey)) continue;
|
|
14125
|
+
const status = await enrollViaServer(inst.host, inst.port, adminKey, { ...opts, hubUrl });
|
|
14126
|
+
return {
|
|
14127
|
+
mode: "hot",
|
|
14128
|
+
...status,
|
|
14129
|
+
dataDir: inst.dataDir,
|
|
14130
|
+
managed: inst.managed,
|
|
14131
|
+
warnings: hotWarnings(inst, running)
|
|
14132
|
+
};
|
|
14133
|
+
}
|
|
14134
|
+
const managedDir = running.find((r) => r.managed)?.dataDir;
|
|
14135
|
+
const dataDir = path28.resolve(explicitDataDir ?? managedDir ?? DEFAULT_DATA_DIR);
|
|
14136
|
+
const targetedService = !explicitDataDir && Boolean(managedDir) && dataDir === managedDir;
|
|
14137
|
+
const store = new Store(dataDir);
|
|
14138
|
+
const nodeId = ensureNodeId2(store);
|
|
14139
|
+
const account = opts.account;
|
|
14140
|
+
const cred = account && isCompleteAccount(account) ? await hubEnrollByAccount(hubUrl, {
|
|
14141
|
+
email: account.email,
|
|
14142
|
+
phone: account.phone,
|
|
14143
|
+
password: account.password,
|
|
14144
|
+
code: account.smsCode,
|
|
14145
|
+
nodeId,
|
|
14146
|
+
label: opts.label
|
|
14147
|
+
}) : await hubEnrollByCode(hubUrl, {
|
|
14148
|
+
code: opts.code,
|
|
14149
|
+
nodeId,
|
|
14150
|
+
label: opts.label
|
|
14151
|
+
});
|
|
14152
|
+
try {
|
|
14153
|
+
store.saveFleetCredential({
|
|
14154
|
+
...cred,
|
|
14155
|
+
label: opts.label ?? cred.label,
|
|
14156
|
+
enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
14157
|
+
});
|
|
14158
|
+
} catch (e) {
|
|
14159
|
+
const err = e;
|
|
14160
|
+
if (err.code === "EACCES" || err.code === "EPERM") {
|
|
14161
|
+
throw new Error(
|
|
14162
|
+
`\u65E0\u6CD5\u5199\u5165\u6570\u636E\u76EE\u5F55 ${dataDir}\uFF08${err.code}: ${err.message}\uFF09
|
|
14163
|
+
\u8BE5\u76EE\u5F55\u5F53\u524D\u7528\u6237\u6CA1\u6709\u5199\u6743\u9650\u3002\u4E24\u79CD\u6539\u6CD5\uFF1A
|
|
14164
|
+
1) \u53BB\u6389 --data-dir\uFF0C\u8BA9 link \u81EA\u52A8\u53D1\u73B0\u672C\u673A\u8FD0\u884C\u7684\u8282\u70B9\uFF08\u63A8\u8350\uFF09\uFF1B
|
|
14165
|
+
2) \u7528\u5F53\u524D\u7528\u6237\u53EF\u5199\u7684\u76EE\u5F55\uFF0C\u672C\u5E73\u53F0\u5B89\u88C5\u811A\u672C\u9ED8\u8BA4\uFF1A${installDataDirHint()}
|
|
14166
|
+
\uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF1B\u8D26\u53F7\u65B9\u5F0F\u53EF\u91CD\u590D\u4F7F\u7528\uFF09`
|
|
14167
|
+
);
|
|
14168
|
+
}
|
|
14169
|
+
throw e;
|
|
14170
|
+
}
|
|
14171
|
+
const otherRunningDirs = [
|
|
14172
|
+
...new Set(running.map((r) => r.dataDir).filter((d) => d !== dataDir))
|
|
14173
|
+
];
|
|
14174
|
+
return {
|
|
14175
|
+
mode: "saved",
|
|
14176
|
+
nodeId,
|
|
14177
|
+
gatewayUrl: cred.gatewayUrl,
|
|
14178
|
+
label: opts.label,
|
|
14179
|
+
dataDir,
|
|
14180
|
+
explicitDataDir: Boolean(explicitDataDir),
|
|
14181
|
+
runningServiceDirs: otherRunningDirs,
|
|
14182
|
+
targetedService
|
|
13988
14183
|
};
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
-
|
|
13996
|
-
|
|
14184
|
+
}
|
|
14185
|
+
function printLinkHelp() {
|
|
14186
|
+
cliLog("\u7528\u6CD5: zqagent link [--hub <url>] [--username <\u90AE\u7BB1/\u624B\u673A\u53F7> --password <\u5BC6\u7801>] [--label \u540D\u79F0]");
|
|
14187
|
+
cliLog(" zqagent link [--hub <url>] --code <\u4E00\u6B21\u6027\u63A5\u5165\u7801> [--label \u540D\u79F0]");
|
|
14188
|
+
cliLog(" \u65B9\u5F0F\u4E00\uFF08\u63A8\u8350\uFF09\uFF1A\u7528 Hub \u5DF2\u6CE8\u518C\u7684\u8D26\u53F7\u76F4\u63A5\u63A5\u5165\uFF1A");
|
|
14189
|
+
cliLog(" zqagent link --hub https://hub.cd-zq.com --username you@example.com --password '<\u5BC6\u7801>'");
|
|
14190
|
+
cliLog(" zqagent link --hub https://hub.cd-zq.com --username 13800000000 --password '<\u5BC6\u7801>'");
|
|
14191
|
+
cliLog(" zqagent link --hub https://hub.cd-zq.com --username 13800000000 --sms-code <\u9A8C\u8BC1\u7801>");
|
|
14192
|
+
cliLog(" \u65B9\u5F0F\u4E8C\uFF1A\u5728 Hub\u300C\u8282\u70B9\u7BA1\u7406\u300D\u751F\u6210\u4E00\u6B21\u6027\u63A5\u5165\u7801\uFF0815 \u5206\u949F\u6709\u6548\uFF09\uFF1A");
|
|
14193
|
+
cliLog(" zqagent link --hub https://hub.cd-zq.com --code <\u63A5\u5165\u7801>");
|
|
14194
|
+
cliLog(" \u4E0D\u5E26\u51ED\u636E\u53C2\u6570\u65F6\u8FDB\u5165\u4EA4\u4E92\u5411\u5BFC\uFF08\u9700\u7EC8\u7AEF\uFF09\uFF1B\u5BC6\u7801\u4E5F\u53EF\u7528 ZQAGENT_HUB_PASSWORD \u63D0\u4F9B\u3002");
|
|
14195
|
+
cliLog(" \u672C\u673A\u5DF2\u88C5\u8282\u70B9\u670D\u52A1\uFF08install.sh / \u684C\u9762\u7AEF\uFF09\u65F6\u4F1A\u81EA\u52A8\u53D1\u73B0\u5B83\u7684\u6570\u636E\u76EE\u5F55\u5E76\u70ED\u63A5\u5165\uFF0C");
|
|
14196
|
+
cliLog(" \u4E0D\u9700\u8981\u624B\u52A8\u6307\u5B9A --data-dir\uFF1B\u670D\u52A1\u6CA1\u5728\u8FD0\u884C\u65F6\u4F1A\u5199\u5165\u5B83\u7684\u76EE\u5F55\uFF0C\u4E0B\u6B21\u542F\u52A8\u5373\u63A5\u5165\u3002");
|
|
14197
|
+
cliLog(" \u4EC5\u5F53\u672C\u673A\u5B8C\u5168\u6CA1\u6709\u670D\u52A1\u65F6\u624D\u9700\u8981 --data-dir \u6307\u5B9A\u76EE\u5F55\uFF08\u4E0B\u6B21\u542F\u52A8 serve \u65F6\u751F\u6548\uFF09\u3002");
|
|
14198
|
+
}
|
|
14199
|
+
async function runLinkCommand(argv, ctx = {}) {
|
|
14200
|
+
const args = parseLinkArgs(argv);
|
|
14201
|
+
if (args.help) {
|
|
14202
|
+
printLinkHelp();
|
|
14203
|
+
return true;
|
|
14204
|
+
}
|
|
14205
|
+
const explicitHub = Boolean((args.hubUrl ?? process.env.HUB_URL ?? "").trim());
|
|
14206
|
+
let hubUrl = (args.hubUrl ?? process.env.HUB_URL ?? DEFAULT_HUB_URL).trim().replace(/\/$/, "");
|
|
14207
|
+
const code = (args.code ?? "").trim();
|
|
14208
|
+
let account = buildAccountAuth(args);
|
|
14209
|
+
const interactive = ctx.interactive ?? (process.stdin.isTTY && process.stdout.isTTY);
|
|
14210
|
+
if (code && account) {
|
|
14211
|
+
cliErr("--code \u4E0E Hub \u8D26\u53F7\uFF08--username/--email/--phone/--password\uFF09\u53EA\u80FD\u4E8C\u9009\u4E00\u3002");
|
|
14212
|
+
return false;
|
|
14213
|
+
}
|
|
14214
|
+
if (!account && (args.password || args.smsCode)) {
|
|
14215
|
+
cliErr("\u63D0\u4F9B\u4E86\u5BC6\u7801/\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF0C\u4F46\u7F3A\u5C11\u8D26\u53F7\uFF1A\u8BF7\u52A0 --username\u3001--email \u6216 --phone\u3002");
|
|
14216
|
+
return false;
|
|
14217
|
+
}
|
|
14218
|
+
const asPrompter = (rl2) => ({
|
|
14219
|
+
question: (q) => new Promise((resolve24) => rl2.question(q, resolve24)),
|
|
14220
|
+
pause: () => rl2.pause(),
|
|
14221
|
+
resume: () => rl2.resume()
|
|
14222
|
+
});
|
|
14223
|
+
let ownedRl = null;
|
|
14224
|
+
const getRl = () => {
|
|
14225
|
+
if (ctx.rl) return asPrompter(ctx.rl);
|
|
14226
|
+
if (!interactive) return null;
|
|
14227
|
+
ownedRl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
14228
|
+
return asPrompter(ownedRl);
|
|
14229
|
+
};
|
|
14230
|
+
const closeOwnedRl = () => {
|
|
14231
|
+
if (ownedRl) ownedRl.close();
|
|
14232
|
+
};
|
|
14233
|
+
try {
|
|
14234
|
+
if (!code && !isCompleteAccount(account)) {
|
|
14235
|
+
const rl2 = getRl();
|
|
14236
|
+
if (!rl2) {
|
|
14237
|
+
cliErr("\u7F3A\u5C11\u63A5\u5165\u51ED\u636E\uFF1A\u8BF7\u7528 --username/--password\uFF0C\u6216 --code <\u63A5\u5165\u7801>\u3002");
|
|
14238
|
+
printLinkHelp();
|
|
14239
|
+
return false;
|
|
14240
|
+
}
|
|
14241
|
+
if (account) {
|
|
14242
|
+
const password = await promptPassword(rl2);
|
|
14243
|
+
if (!password) {
|
|
14244
|
+
cliErr("\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u53D6\u6D88\u63A5\u5165\u3002");
|
|
14245
|
+
return false;
|
|
14246
|
+
}
|
|
14247
|
+
account = { ...account, password };
|
|
14248
|
+
} else {
|
|
14249
|
+
const prompted = await promptLinkAccount(rl2, hubUrl, { askHub: !explicitHub });
|
|
14250
|
+
if (!prompted) return true;
|
|
14251
|
+
hubUrl = prompted.hubUrl;
|
|
14252
|
+
account = {
|
|
14253
|
+
email: prompted.auth.email,
|
|
14254
|
+
phone: prompted.auth.phone,
|
|
14255
|
+
password: prompted.auth.password,
|
|
14256
|
+
smsCode: prompted.auth.code
|
|
14257
|
+
};
|
|
14258
|
+
}
|
|
14259
|
+
}
|
|
14260
|
+
const r = await linkNode({
|
|
14261
|
+
hubUrl,
|
|
14262
|
+
code: code || void 0,
|
|
14263
|
+
account: account ?? void 0,
|
|
14264
|
+
dataDir: args.dataDir,
|
|
14265
|
+
label: args.label,
|
|
14266
|
+
host: args.host,
|
|
14267
|
+
port: args.port
|
|
14268
|
+
});
|
|
14269
|
+
if (r.mode === "hot") {
|
|
14270
|
+
cliLog("\u2705 \u5DF2\u63A5\u5165 Hub\uFF08\u70ED\u63A5\u5165\uFF0C\u514D\u91CD\u542F\uFF09");
|
|
14271
|
+
cliLog(` \u8282\u70B9: ${r.nodeId}${r.label ? ` (${r.label})` : ""}`);
|
|
14272
|
+
cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"} \u5728\u7EBF: ${r.online ? "\u662F" : "\u8FDE\u63A5\u4E2D\u2026"}`);
|
|
14273
|
+
cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}${r.managed ? "\uFF08\u6258\u7BA1\u670D\u52A1\uFF0C\u5F00\u673A\u81EA\u542F\uFF09" : ""}`);
|
|
14274
|
+
if (r.warnings.length > 0) {
|
|
14275
|
+
cliLog("");
|
|
14276
|
+
for (const w of r.warnings) cliLog(`\u26A0\uFE0F ${w}`);
|
|
14277
|
+
}
|
|
14278
|
+
} else {
|
|
14279
|
+
cliLog("\u2705 \u63A5\u5165\u51ED\u636E\u5DF2\u4FDD\u5B58\uFF08\u672C\u673A\u6682\u65E0\u8FD0\u884C\u4E2D\u7684 server\uFF09");
|
|
14280
|
+
cliLog(` \u8282\u70B9: ${r.nodeId}`);
|
|
14281
|
+
cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"}`);
|
|
14282
|
+
cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}`);
|
|
14283
|
+
cliLog(" \u4E0B\u6B21\u542F\u52A8 server \u5373\u81EA\u52A8\u63A5\u5165\uFF1B\u6216\u8FD0\u884C zqagent serve \u5E38\u9A7B\u4E3A\u8282\u70B9\u3002");
|
|
14284
|
+
if (r.targetedService) {
|
|
14285
|
+
cliLog("");
|
|
14286
|
+
cliLog("\u2705 \u51ED\u636E\u5DF2\u5199\u5165\u672C\u673A\u6258\u7BA1\u670D\u52A1\u7684\u6570\u636E\u76EE\u5F55\uFF0C\u670D\u52A1\u542F\u52A8\u65F6\u81EA\u52A8\u63A5\u5165\uFF0C\u65E0\u9700 --data-dir\u3002");
|
|
14287
|
+
cliLog(" \u7ACB\u5373\u751F\u6548\uFF1A\u91CD\u542F\u8BE5\u670D\u52A1\uFF08Linux: systemctl restart zqagent\uFF1B");
|
|
14288
|
+
cliLog(" macOS: launchctl kickstart -k gui/$(id -u)/com.zqagent.node\uFF09\u3002");
|
|
14289
|
+
} else if (r.runningServiceDirs.length > 0) {
|
|
14290
|
+
cliLog("");
|
|
14291
|
+
cliLog("\u26A0\uFE0F \u672C\u673A\u6709\u6B63\u5728\u8FD0\u884C\u7684\u8282\u70B9\uFF0C\u4F46\u6570\u636E\u76EE\u5F55\u4E0E\u672C\u6B21\u5199\u5165\u7684\u4E0D\u4E00\u81F4\uFF1A");
|
|
14292
|
+
cliLog(` \u672C\u6B21\u5199\u5165: ${r.dataDir}`);
|
|
14293
|
+
for (const d of r.runningServiceDirs) cliLog(` \u670D\u52A1\u4F7F\u7528: ${d}`);
|
|
14294
|
+
cliLog(" \u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1\u8BFB\u4E0D\u5230\u8FD9\u4EFD\u51ED\u636E\uFF0CHub \u4F1A\u4E00\u76F4\u663E\u793A\u672A\u63A5\u5165\u3002");
|
|
14295
|
+
cliLog(" \u8BF7\u786E\u8BA4\u670D\u52A1\u7684\u6570\u636E\u76EE\u5F55\u540E\u91CD\u65B0\u6267\u884C\uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF1B\u8D26\u53F7\u65B9\u5F0F\u53EF\u91CD\u590D\u4F7F\u7528\uFF09\uFF1A");
|
|
14296
|
+
cliLog(` zqagent link --hub <Hub\u5730\u5740> --username <\u90AE\u7BB1/\u624B\u673A\u53F7> --password '<\u5BC6\u7801>' --data-dir "${r.runningServiceDirs[0]}"`);
|
|
14297
|
+
} else if (!r.explicitDataDir) {
|
|
14298
|
+
cliLog("");
|
|
14299
|
+
cliLog("\u26A0\uFE0F \u672A\u53D1\u73B0\u672C\u673A\u8282\u70B9\u670D\u52A1\uFF0C\u51ED\u636E\u5199\u5165\u4E86\u5F53\u524D\u76EE\u5F55\u4E0B\u7684 .zqagent_data\u3002");
|
|
14300
|
+
cliLog(" \u5982\u679C\u8FD9\u53F0\u673A\u5668\u6253\u7B97\u957F\u671F\u8FD0\u884C\u8282\u70B9\uFF0C\u8BF7\u5148\u5B89\u88C5\u5F00\u673A\u670D\u52A1\uFF0C\u4E4B\u540E link \u4F1A\u81EA\u52A8\u5199\u5165\u5B83\u7684\u76EE\u5F55\uFF1A");
|
|
14301
|
+
cliLog(" curl -fsSL https://agent.cd-zq.com/install.sh | sh");
|
|
14302
|
+
cliLog(` \uFF08\u670D\u52A1\u9ED8\u8BA4\u6570\u636E\u76EE\u5F55\uFF1A${installDataDirHint()}\uFF09`);
|
|
14303
|
+
}
|
|
14304
|
+
}
|
|
14305
|
+
return true;
|
|
14306
|
+
} catch (e) {
|
|
14307
|
+
cliErr(`\u63A5\u5165\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
|
|
14308
|
+
return false;
|
|
14309
|
+
} finally {
|
|
14310
|
+
closeOwnedRl();
|
|
13997
14311
|
}
|
|
13998
|
-
store.saveTenant(tenant);
|
|
13999
|
-
return tenant;
|
|
14000
|
-
}
|
|
14001
|
-
function buildNodeInfo(store, opts) {
|
|
14002
|
-
return {
|
|
14003
|
-
nodeId: opts.nodeId ?? ensureNodeId2(store),
|
|
14004
|
-
platform: process.platform,
|
|
14005
|
-
mode: process.env.NODE_MODE ?? "standalone",
|
|
14006
|
-
version: "0.2.11",
|
|
14007
|
-
admin: opts.admin,
|
|
14008
|
-
projects: opts.projects
|
|
14009
|
-
};
|
|
14010
14312
|
}
|
|
14011
14313
|
|
|
14314
|
+
// src/cli/serve.ts
|
|
14315
|
+
init_esm_shims();
|
|
14316
|
+
|
|
14317
|
+
// src/server/runtime.ts
|
|
14318
|
+
init_esm_shims();
|
|
14319
|
+
import * as fs31 from "fs";
|
|
14320
|
+
import * as path39 from "path";
|
|
14321
|
+
|
|
14322
|
+
// src/server/routes.ts
|
|
14323
|
+
init_esm_shims();
|
|
14324
|
+
import { createServer } from "http";
|
|
14325
|
+
import * as path32 from "path";
|
|
14326
|
+
|
|
14012
14327
|
// src/server/auth.ts
|
|
14013
14328
|
init_esm_shims();
|
|
14014
14329
|
import * as crypto4 from "crypto";
|
|
@@ -14184,13 +14499,13 @@ async function handleHubProxy(req2, res) {
|
|
|
14184
14499
|
init_esm_shims();
|
|
14185
14500
|
import * as fs22 from "fs";
|
|
14186
14501
|
import * as os6 from "os";
|
|
14187
|
-
import * as
|
|
14502
|
+
import * as path29 from "path";
|
|
14188
14503
|
var MAX_ENTRIES = 2e3;
|
|
14189
14504
|
var DEFAULT_SEARCH_LIMIT = 200;
|
|
14190
14505
|
var MAX_SEARCH_LIMIT = 1e3;
|
|
14191
14506
|
function listDirs(input) {
|
|
14192
14507
|
const base = (input ?? "").trim() || os6.homedir();
|
|
14193
|
-
const target =
|
|
14508
|
+
const target = path29.resolve(base);
|
|
14194
14509
|
let st;
|
|
14195
14510
|
try {
|
|
14196
14511
|
st = fs22.statSync(target);
|
|
@@ -14205,7 +14520,7 @@ function listDirs(input) {
|
|
|
14205
14520
|
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
|
|
14206
14521
|
}
|
|
14207
14522
|
const entries = dirents.map((d) => {
|
|
14208
|
-
const full =
|
|
14523
|
+
const full = path29.join(target, d.name);
|
|
14209
14524
|
let isDir = d.isDirectory();
|
|
14210
14525
|
if (d.isSymbolicLink()) {
|
|
14211
14526
|
try {
|
|
@@ -14218,7 +14533,7 @@ function listDirs(input) {
|
|
|
14218
14533
|
entries.sort(
|
|
14219
14534
|
(a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
|
|
14220
14535
|
);
|
|
14221
|
-
const parentDir =
|
|
14536
|
+
const parentDir = path29.dirname(target);
|
|
14222
14537
|
return {
|
|
14223
14538
|
path: target,
|
|
14224
14539
|
parent: parentDir !== target ? parentDir : null,
|
|
@@ -14249,7 +14564,7 @@ var NAME_CACHE_MAX_DIRS = 4;
|
|
|
14249
14564
|
var NAME_CACHE_MAX_ENTRIES = 5e4;
|
|
14250
14565
|
var nameCache = /* @__PURE__ */ new Map();
|
|
14251
14566
|
function invalidateDirNames(target) {
|
|
14252
|
-
nameCache.delete(
|
|
14567
|
+
nameCache.delete(path29.resolve(target));
|
|
14253
14568
|
}
|
|
14254
14569
|
async function loadDirNames(target) {
|
|
14255
14570
|
const now = Date.now();
|
|
@@ -14284,7 +14599,7 @@ async function loadDirNames(target) {
|
|
|
14284
14599
|
return entries;
|
|
14285
14600
|
}
|
|
14286
14601
|
async function toDirEntry(dir, e) {
|
|
14287
|
-
const full =
|
|
14602
|
+
const full = path29.join(dir, e.name);
|
|
14288
14603
|
let isDir = e.isDir;
|
|
14289
14604
|
if (e.isLink) {
|
|
14290
14605
|
try {
|
|
@@ -14296,7 +14611,7 @@ async function toDirEntry(dir, e) {
|
|
|
14296
14611
|
}
|
|
14297
14612
|
async function searchDirs(input, query, limit) {
|
|
14298
14613
|
const base = (input ?? "").trim() || os6.homedir();
|
|
14299
|
-
const target =
|
|
14614
|
+
const target = path29.resolve(base);
|
|
14300
14615
|
let st;
|
|
14301
14616
|
try {
|
|
14302
14617
|
st = await fs22.promises.stat(target);
|
|
@@ -14329,7 +14644,7 @@ async function searchDirs(input, query, limit) {
|
|
|
14329
14644
|
);
|
|
14330
14645
|
const hit = scored.slice(0, max);
|
|
14331
14646
|
const entries = await Promise.all(hit.map(({ entry }) => toDirEntry(target, entry)));
|
|
14332
|
-
const parentDir =
|
|
14647
|
+
const parentDir = path29.dirname(target);
|
|
14333
14648
|
return {
|
|
14334
14649
|
path: target,
|
|
14335
14650
|
parent: parentDir !== target ? parentDir : null,
|
|
@@ -14340,13 +14655,13 @@ async function searchDirs(input, query, limit) {
|
|
|
14340
14655
|
};
|
|
14341
14656
|
}
|
|
14342
14657
|
function makeDirIn(parentInput, name) {
|
|
14343
|
-
const parent =
|
|
14658
|
+
const parent = path29.resolve((parentInput ?? "").trim() || os6.homedir());
|
|
14344
14659
|
const clean = (name ?? "").trim();
|
|
14345
14660
|
if (!clean) throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u4E3A\u7A7A");
|
|
14346
14661
|
if (clean === "." || clean === "..") throw new Error("\u76EE\u5F55\u540D\u975E\u6CD5");
|
|
14347
|
-
const target =
|
|
14348
|
-
const rel =
|
|
14349
|
-
if (!rel || rel.startsWith("..") ||
|
|
14662
|
+
const target = path29.resolve(parent, clean);
|
|
14663
|
+
const rel = path29.relative(parent, target);
|
|
14664
|
+
if (!rel || rel.startsWith("..") || path29.isAbsolute(rel)) {
|
|
14350
14665
|
throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u8D8A\u51FA\u5F53\u524D\u76EE\u5F55");
|
|
14351
14666
|
}
|
|
14352
14667
|
let st = null;
|
|
@@ -14362,25 +14677,25 @@ function makeDirIn(parentInput, name) {
|
|
|
14362
14677
|
throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
|
|
14363
14678
|
}
|
|
14364
14679
|
invalidateDirNames(parent);
|
|
14365
|
-
return { name:
|
|
14680
|
+
return { name: path29.basename(target), path: target, type: "dir" };
|
|
14366
14681
|
}
|
|
14367
14682
|
|
|
14368
14683
|
// src/server/projectFiles.ts
|
|
14369
14684
|
init_esm_shims();
|
|
14370
14685
|
import * as fs23 from "fs";
|
|
14371
|
-
import * as
|
|
14686
|
+
import * as path30 from "path";
|
|
14372
14687
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
14373
14688
|
var MAX_DOWNLOAD_BYTES = 16 * 1024 * 1024;
|
|
14374
14689
|
var MAX_ENTRIES2 = 2e3;
|
|
14375
14690
|
function toPosix(p) {
|
|
14376
|
-
return p.split(
|
|
14691
|
+
return p.split(path30.sep).join("/");
|
|
14377
14692
|
}
|
|
14378
14693
|
function projectRoot(workspaceRoot) {
|
|
14379
14694
|
const r = (workspaceRoot ?? "").trim();
|
|
14380
14695
|
if (!r) {
|
|
14381
14696
|
throw new Error("\u9879\u76EE\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF08workspaceRoot\uFF09\uFF0C\u65E0\u6CD5\u6D4F\u89C8\u6587\u4EF6");
|
|
14382
14697
|
}
|
|
14383
|
-
const abs =
|
|
14698
|
+
const abs = path30.resolve(r);
|
|
14384
14699
|
if (!fs23.existsSync(abs)) {
|
|
14385
14700
|
throw new Error(`\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728: ${abs}`);
|
|
14386
14701
|
}
|
|
@@ -14405,7 +14720,7 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
14405
14720
|
try {
|
|
14406
14721
|
st = fs23.statSync(dir);
|
|
14407
14722
|
} catch {
|
|
14408
|
-
throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(
|
|
14723
|
+
throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path30.relative(base, dir)) || "/"}`);
|
|
14409
14724
|
}
|
|
14410
14725
|
if (!st.isDirectory()) throw new Error("\u76EE\u6807\u4E0D\u662F\u76EE\u5F55");
|
|
14411
14726
|
let dirents;
|
|
@@ -14415,7 +14730,7 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
14415
14730
|
throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09");
|
|
14416
14731
|
}
|
|
14417
14732
|
const entries = dirents.map((d) => {
|
|
14418
|
-
const full =
|
|
14733
|
+
const full = path30.join(dir, d.name);
|
|
14419
14734
|
let isDir = d.isDirectory();
|
|
14420
14735
|
if (d.isSymbolicLink()) {
|
|
14421
14736
|
try {
|
|
@@ -14433,7 +14748,7 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
14433
14748
|
}
|
|
14434
14749
|
return {
|
|
14435
14750
|
name: d.name,
|
|
14436
|
-
path: toPosix(
|
|
14751
|
+
path: toPosix(path30.relative(base, full)),
|
|
14437
14752
|
type: isDir ? "dir" : "file",
|
|
14438
14753
|
size,
|
|
14439
14754
|
mtime
|
|
@@ -14442,12 +14757,12 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
14442
14757
|
entries.sort(
|
|
14443
14758
|
(a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
|
|
14444
14759
|
);
|
|
14445
|
-
const relDir = toPosix(
|
|
14446
|
-
const parentAbs =
|
|
14760
|
+
const relDir = toPosix(path30.relative(base, dir));
|
|
14761
|
+
const parentAbs = path30.dirname(dir);
|
|
14447
14762
|
return {
|
|
14448
14763
|
root: base,
|
|
14449
14764
|
path: relDir,
|
|
14450
|
-
parent: isWithinRoot(base, parentAbs) ? toPosix(
|
|
14765
|
+
parent: isWithinRoot(base, parentAbs) ? toPosix(path30.relative(base, parentAbs)) : null,
|
|
14451
14766
|
entries: entries.slice(0, MAX_ENTRIES2)
|
|
14452
14767
|
};
|
|
14453
14768
|
}
|
|
@@ -14466,7 +14781,7 @@ function readProjectFile(workspaceRoot, relPath) {
|
|
|
14466
14781
|
const truncated = buf.length > MAX_FILE_BYTES;
|
|
14467
14782
|
const slice = truncated ? buf.subarray(0, MAX_FILE_BYTES) : buf;
|
|
14468
14783
|
return {
|
|
14469
|
-
path: toPosix(
|
|
14784
|
+
path: toPosix(path30.relative(base, file)),
|
|
14470
14785
|
size: st.size,
|
|
14471
14786
|
binary,
|
|
14472
14787
|
truncated,
|
|
@@ -14488,8 +14803,8 @@ function downloadProjectFile(workspaceRoot, relPath) {
|
|
|
14488
14803
|
}
|
|
14489
14804
|
const buf = fs23.readFileSync(file);
|
|
14490
14805
|
return {
|
|
14491
|
-
path: toPosix(
|
|
14492
|
-
name:
|
|
14806
|
+
path: toPosix(path30.relative(base, file)),
|
|
14807
|
+
name: path30.basename(file),
|
|
14493
14808
|
size: buf.length,
|
|
14494
14809
|
base64: buf.toString("base64")
|
|
14495
14810
|
};
|
|
@@ -14511,12 +14826,12 @@ function writeProjectFile(workspaceRoot, relPath, content) {
|
|
|
14511
14826
|
throw new Error("\u5185\u5BB9\u8D85\u8FC7 2MB \u4E0A\u9650\uFF0C\u65E0\u6CD5\u4FDD\u5B58");
|
|
14512
14827
|
}
|
|
14513
14828
|
try {
|
|
14514
|
-
fs23.mkdirSync(
|
|
14829
|
+
fs23.mkdirSync(path30.dirname(file), { recursive: true });
|
|
14515
14830
|
fs23.writeFileSync(file, buf);
|
|
14516
14831
|
} catch (e) {
|
|
14517
14832
|
throw new Error(`\u5199\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
14518
14833
|
}
|
|
14519
|
-
return { path: toPosix(
|
|
14834
|
+
return { path: toPosix(path30.relative(base, file)), size: buf.length };
|
|
14520
14835
|
}
|
|
14521
14836
|
function createProjectDir(workspaceRoot, relPath) {
|
|
14522
14837
|
const base = projectRoot(workspaceRoot);
|
|
@@ -14528,7 +14843,7 @@ function createProjectDir(workspaceRoot, relPath) {
|
|
|
14528
14843
|
} catch (e) {
|
|
14529
14844
|
throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
14530
14845
|
}
|
|
14531
|
-
return { path: toPosix(
|
|
14846
|
+
return { path: toPosix(path30.relative(base, dir)) };
|
|
14532
14847
|
}
|
|
14533
14848
|
function createProjectFile(workspaceRoot, relPath, content = "") {
|
|
14534
14849
|
const base = projectRoot(workspaceRoot);
|
|
@@ -14538,12 +14853,12 @@ function createProjectFile(workspaceRoot, relPath, content = "") {
|
|
|
14538
14853
|
if (fs23.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
|
|
14539
14854
|
const buf = Buffer.from(String(content ?? ""), "utf-8");
|
|
14540
14855
|
try {
|
|
14541
|
-
fs23.mkdirSync(
|
|
14856
|
+
fs23.mkdirSync(path30.dirname(file), { recursive: true });
|
|
14542
14857
|
fs23.writeFileSync(file, buf);
|
|
14543
14858
|
} catch (e) {
|
|
14544
14859
|
throw new Error(`\u521B\u5EFA\u6587\u4EF6\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
14545
14860
|
}
|
|
14546
|
-
return { path: toPosix(
|
|
14861
|
+
return { path: toPosix(path30.relative(base, file)), size: buf.length };
|
|
14547
14862
|
}
|
|
14548
14863
|
function searchProjectFiles(workspaceRoot, query) {
|
|
14549
14864
|
const base = projectRoot(workspaceRoot);
|
|
@@ -14577,7 +14892,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
14577
14892
|
let isDir = d.isDirectory();
|
|
14578
14893
|
if (d.isSymbolicLink()) {
|
|
14579
14894
|
try {
|
|
14580
|
-
isDir = fs23.statSync(
|
|
14895
|
+
isDir = fs23.statSync(path30.join(dir, d.name)).isDirectory();
|
|
14581
14896
|
} catch {
|
|
14582
14897
|
isDir = false;
|
|
14583
14898
|
}
|
|
@@ -14585,7 +14900,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
14585
14900
|
if (d.name.toLowerCase().includes(q)) {
|
|
14586
14901
|
out.push({ path: rel, type: isDir ? "dir" : "file" });
|
|
14587
14902
|
}
|
|
14588
|
-
if (isDir) walk(
|
|
14903
|
+
if (isDir) walk(path30.join(dir, d.name), rel);
|
|
14589
14904
|
}
|
|
14590
14905
|
};
|
|
14591
14906
|
walk(base, "");
|
|
@@ -14595,7 +14910,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
14595
14910
|
// src/server/projectOps.ts
|
|
14596
14911
|
init_esm_shims();
|
|
14597
14912
|
import * as os7 from "os";
|
|
14598
|
-
import * as
|
|
14913
|
+
import * as path31 from "path";
|
|
14599
14914
|
var MAX_STDOUT2 = 2e4;
|
|
14600
14915
|
var MAX_STDERR2 = 1e4;
|
|
14601
14916
|
var MAX_BUFFER3 = 1024 * 1024 * 5;
|
|
@@ -14609,7 +14924,7 @@ function resolveInvocation2(spec, prepared) {
|
|
|
14609
14924
|
}
|
|
14610
14925
|
function workRoot(workspaceRoot) {
|
|
14611
14926
|
const r = (workspaceRoot ?? "").trim();
|
|
14612
|
-
return r ?
|
|
14927
|
+
return r ? path31.resolve(r) : os7.homedir();
|
|
14613
14928
|
}
|
|
14614
14929
|
async function runProjectCommand(workspaceRoot, command, timeoutSeconds) {
|
|
14615
14930
|
const cmd = String(command ?? "");
|
|
@@ -14996,7 +15311,7 @@ function buildNodeHost(store, manager, diskPath) {
|
|
|
14996
15311
|
arch: process.arch,
|
|
14997
15312
|
release: os8.release(),
|
|
14998
15313
|
uptimeSec: Math.round(os8.uptime()),
|
|
14999
|
-
version: "0.2.
|
|
15314
|
+
version: "0.2.12",
|
|
15000
15315
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
15001
15316
|
cpu: {
|
|
15002
15317
|
count: cpus2.length,
|
|
@@ -15366,7 +15681,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
15366
15681
|
ok(res, opts.fleet.status());
|
|
15367
15682
|
}
|
|
15368
15683
|
},
|
|
15369
|
-
//
|
|
15684
|
+
// 运行时热接入:凭一次性注册码或 Hub 账号接入(落盘 fleet.json + 立即连接,免重启)
|
|
15370
15685
|
{
|
|
15371
15686
|
method: "POST",
|
|
15372
15687
|
pattern: ["fleet", "enroll"],
|
|
@@ -15378,15 +15693,42 @@ function createApp(store, manager, opts = {}) {
|
|
|
15378
15693
|
const raw = body ?? {};
|
|
15379
15694
|
const hubUrl = String(raw.hubUrl ?? "").trim().replace(/\/$/, "");
|
|
15380
15695
|
const code = String(raw.code ?? "").trim();
|
|
15381
|
-
|
|
15382
|
-
|
|
15696
|
+
const email = String(raw.email ?? "").trim();
|
|
15697
|
+
const phone = String(raw.phone ?? "").trim();
|
|
15698
|
+
const password = String(raw.password ?? "");
|
|
15699
|
+
const smsCode = String(raw.smsCode ?? "").trim();
|
|
15700
|
+
const hasAccount = Boolean(email || phone);
|
|
15701
|
+
if (!hubUrl) {
|
|
15702
|
+
throw new ApiError(400, "bad_request", "hubUrl \u5FC5\u586B");
|
|
15703
|
+
}
|
|
15704
|
+
if (code && hasAccount) {
|
|
15705
|
+
throw new ApiError(400, "bad_request", "code \u4E0E\u8D26\u53F7\u51ED\u636E\u53EA\u80FD\u4E8C\u9009\u4E00");
|
|
15706
|
+
}
|
|
15707
|
+
if (!code && !hasAccount) {
|
|
15708
|
+
throw new ApiError(400, "bad_request", "\u9700\u8981 code\uFF0C\u6216 email/phone + password/smsCode");
|
|
15709
|
+
}
|
|
15710
|
+
if (hasAccount && !password && !smsCode) {
|
|
15711
|
+
throw new ApiError(400, "bad_request", "\u8D26\u53F7\u63A5\u5165\u9700\u8981 password \u6216 smsCode");
|
|
15383
15712
|
}
|
|
15384
15713
|
const label = typeof raw.label === "string" ? raw.label.trim() : void 0;
|
|
15385
15714
|
try {
|
|
15386
|
-
ok(
|
|
15715
|
+
ok(
|
|
15716
|
+
res,
|
|
15717
|
+
await opts.fleet.enroll({
|
|
15718
|
+
hubUrl,
|
|
15719
|
+
code: code || void 0,
|
|
15720
|
+
account: hasAccount ? {
|
|
15721
|
+
email: email || void 0,
|
|
15722
|
+
phone: phone || void 0,
|
|
15723
|
+
password: password || void 0,
|
|
15724
|
+
smsCode: smsCode || void 0
|
|
15725
|
+
} : void 0,
|
|
15726
|
+
label
|
|
15727
|
+
})
|
|
15728
|
+
);
|
|
15387
15729
|
} catch (e) {
|
|
15388
15730
|
const code2 = e.code;
|
|
15389
|
-
const status = code2 === "code_expired" ? 410 : code2 === "code_used" || code2 === "node_taken" ? 409 : code2 === "not_found" ? 404 : code2 === "plan_limit" || code2 === "subscription_required" ? 402 : 400;
|
|
15731
|
+
const status = code2 === "code_expired" ? 410 : code2 === "unauthorized" ? 401 : code2 === "account_disabled" ? 403 : code2 === "code_used" || code2 === "node_taken" ? 409 : code2 === "not_found" ? 404 : code2 === "plan_limit" || code2 === "subscription_required" ? 402 : 400;
|
|
15390
15732
|
const msg = e instanceof Error ? e.message : String(e);
|
|
15391
15733
|
throw new ApiError(
|
|
15392
15734
|
status,
|
|
@@ -15571,7 +15913,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
15571
15913
|
applyProvidersPatch(tenant, raw);
|
|
15572
15914
|
if (typeof raw.workspaceRoot === "string") {
|
|
15573
15915
|
const ws = raw.workspaceRoot.trim();
|
|
15574
|
-
const next = ws ?
|
|
15916
|
+
const next = ws ? path32.resolve(ws) : void 0;
|
|
15575
15917
|
if (tenant.workspaceRoot) {
|
|
15576
15918
|
if (next !== tenant.workspaceRoot) {
|
|
15577
15919
|
throw new ApiError(
|
|
@@ -16558,7 +16900,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
16558
16900
|
json(res, 200, {
|
|
16559
16901
|
status: "ok",
|
|
16560
16902
|
// 版本让运维/探针能确认线上跑的是哪个构建(更新与回滚据此判断)
|
|
16561
|
-
version: "0.2.
|
|
16903
|
+
version: "0.2.12",
|
|
16562
16904
|
features: { mcp: hasServerMcpConfig() }
|
|
16563
16905
|
});
|
|
16564
16906
|
return;
|
|
@@ -17861,7 +18203,7 @@ function primaryIp() {
|
|
|
17861
18203
|
var FleetNodeBridge = class {
|
|
17862
18204
|
constructor(opts) {
|
|
17863
18205
|
this.opts = opts;
|
|
17864
|
-
this.nodeId = opts.nodeId ??
|
|
18206
|
+
this.nodeId = opts.nodeId ?? ensureNodeId(opts.deps.store);
|
|
17865
18207
|
}
|
|
17866
18208
|
opts;
|
|
17867
18209
|
/** 是否成功连接过(区分「一直连不上」与「连上后断线」) */
|
|
@@ -18196,7 +18538,7 @@ var FleetNodeBridge = class {
|
|
|
18196
18538
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
18197
18539
|
platform: process.platform,
|
|
18198
18540
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
18199
|
-
version: "0.2.
|
|
18541
|
+
version: "0.2.12",
|
|
18200
18542
|
ip: primaryIp()
|
|
18201
18543
|
};
|
|
18202
18544
|
}
|
|
@@ -18251,7 +18593,7 @@ var FleetNodeBridge = class {
|
|
|
18251
18593
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
18252
18594
|
platform: process.platform,
|
|
18253
18595
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
18254
|
-
version: "0.2.
|
|
18596
|
+
version: "0.2.12",
|
|
18255
18597
|
ip: primaryIp(),
|
|
18256
18598
|
projects,
|
|
18257
18599
|
totals,
|
|
@@ -18439,28 +18781,6 @@ var FleetNodeBridge = class {
|
|
|
18439
18781
|
}
|
|
18440
18782
|
};
|
|
18441
18783
|
|
|
18442
|
-
// src/server/fleet-config.ts
|
|
18443
|
-
init_esm_shims();
|
|
18444
|
-
function fleetConfigFromEnv(store) {
|
|
18445
|
-
const fleetToken = process.env.FLEET_TOKEN;
|
|
18446
|
-
const fleetSecret = process.env.FLEET_SECRET;
|
|
18447
|
-
if (!fleetToken || !fleetSecret) return null;
|
|
18448
|
-
return {
|
|
18449
|
-
hubUrl: process.env.HUB_URL,
|
|
18450
|
-
fleetToken,
|
|
18451
|
-
fleetSecret,
|
|
18452
|
-
nodeId: ensureNodeId2(store),
|
|
18453
|
-
gatewayUrl: process.env.FLEET_GATEWAY_URL
|
|
18454
|
-
};
|
|
18455
|
-
}
|
|
18456
|
-
function resolveFleetConfig(store) {
|
|
18457
|
-
const saved = store.loadFleetCredential();
|
|
18458
|
-
if (saved?.fleetToken && saved.fleetSecret) {
|
|
18459
|
-
return { ...saved, nodeId: saved.nodeId || ensureNodeId2(store) };
|
|
18460
|
-
}
|
|
18461
|
-
return fleetConfigFromEnv(store);
|
|
18462
|
-
}
|
|
18463
|
-
|
|
18464
18784
|
// src/server/notify/index.ts
|
|
18465
18785
|
init_esm_shims();
|
|
18466
18786
|
|
|
@@ -19021,7 +19341,7 @@ init_esm_shims();
|
|
|
19021
19341
|
// src/server/qq/bindings.ts
|
|
19022
19342
|
init_esm_shims();
|
|
19023
19343
|
import * as fs25 from "fs";
|
|
19024
|
-
import * as
|
|
19344
|
+
import * as path33 from "path";
|
|
19025
19345
|
function conversationKey(target) {
|
|
19026
19346
|
switch (target.kind) {
|
|
19027
19347
|
case "c2c":
|
|
@@ -19060,7 +19380,7 @@ var QqBindingsStore = class _QqBindingsStore {
|
|
|
19060
19380
|
cache = null;
|
|
19061
19381
|
/** 基于 DATA_DIR 构造 */
|
|
19062
19382
|
static in(dataDir) {
|
|
19063
|
-
return new _QqBindingsStore(
|
|
19383
|
+
return new _QqBindingsStore(path33.join(dataDir, "qq", "bindings.json"));
|
|
19064
19384
|
}
|
|
19065
19385
|
load() {
|
|
19066
19386
|
if (this.cache) return this.cache;
|
|
@@ -19097,7 +19417,7 @@ var QqBindingsStore = class _QqBindingsStore {
|
|
|
19097
19417
|
return null;
|
|
19098
19418
|
}
|
|
19099
19419
|
persist() {
|
|
19100
|
-
fs25.mkdirSync(
|
|
19420
|
+
fs25.mkdirSync(path33.dirname(this.file), { recursive: true });
|
|
19101
19421
|
fs25.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
19102
19422
|
}
|
|
19103
19423
|
};
|
|
@@ -19511,7 +19831,7 @@ ${lines.join("\n")}`, msgId);
|
|
|
19511
19831
|
// src/server/qq/QqManager.ts
|
|
19512
19832
|
init_esm_shims();
|
|
19513
19833
|
import * as fs26 from "fs";
|
|
19514
|
-
import * as
|
|
19834
|
+
import * as path34 from "path";
|
|
19515
19835
|
var QqManager = class {
|
|
19516
19836
|
constructor(opts) {
|
|
19517
19837
|
this.opts = opts;
|
|
@@ -19520,7 +19840,7 @@ var QqManager = class {
|
|
|
19520
19840
|
adapter = null;
|
|
19521
19841
|
config = null;
|
|
19522
19842
|
get configFile() {
|
|
19523
|
-
return
|
|
19843
|
+
return path34.join(this.opts.dataDir, "qq", "config.json");
|
|
19524
19844
|
}
|
|
19525
19845
|
/** 启动:读配置(env 优先,其次文件),有则建连接 */
|
|
19526
19846
|
start() {
|
|
@@ -19613,7 +19933,7 @@ var QqManager = class {
|
|
|
19613
19933
|
return null;
|
|
19614
19934
|
}
|
|
19615
19935
|
_persistConfig(cfg) {
|
|
19616
|
-
fs26.mkdirSync(
|
|
19936
|
+
fs26.mkdirSync(path34.dirname(this.configFile), { recursive: true });
|
|
19617
19937
|
fs26.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
|
|
19618
19938
|
}
|
|
19619
19939
|
};
|
|
@@ -19663,7 +19983,7 @@ var PairingCodeStore = class {
|
|
|
19663
19983
|
// src/update/controller.ts
|
|
19664
19984
|
init_esm_shims();
|
|
19665
19985
|
import * as fs30 from "fs";
|
|
19666
|
-
import * as
|
|
19986
|
+
import * as path38 from "path";
|
|
19667
19987
|
|
|
19668
19988
|
// src/update/downloader.ts
|
|
19669
19989
|
init_esm_shims();
|
|
@@ -19672,7 +19992,7 @@ import * as fs27 from "fs";
|
|
|
19672
19992
|
import { Readable as Readable2 } from "stream";
|
|
19673
19993
|
import { pipeline as pipeline2 } from "stream/promises";
|
|
19674
19994
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
19675
|
-
import * as
|
|
19995
|
+
import * as path35 from "path";
|
|
19676
19996
|
|
|
19677
19997
|
// src/update/manifest.ts
|
|
19678
19998
|
init_esm_shims();
|
|
@@ -19849,7 +20169,7 @@ async function fetchManifest(url, publicKey, opts = {}) {
|
|
|
19849
20169
|
async function downloadBinary(bin, destPath, log = () => {
|
|
19850
20170
|
}) {
|
|
19851
20171
|
const partPath = `${destPath}.part`;
|
|
19852
|
-
fs27.mkdirSync(
|
|
20172
|
+
fs27.mkdirSync(path35.dirname(destPath), { recursive: true });
|
|
19853
20173
|
fs27.rmSync(partPath, { force: true });
|
|
19854
20174
|
let res;
|
|
19855
20175
|
try {
|
|
@@ -19893,7 +20213,7 @@ async function downloadBinary(bin, destPath, log = () => {
|
|
|
19893
20213
|
fs27.rmSync(destPath, { force: true });
|
|
19894
20214
|
fs27.renameSync(partPath, destPath);
|
|
19895
20215
|
fs27.chmodSync(destPath, 493);
|
|
19896
|
-
log(`\u5DF2\u4E0B\u8F7D ${
|
|
20216
|
+
log(`\u5DF2\u4E0B\u8F7D ${path35.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
|
|
19897
20217
|
return { bytes };
|
|
19898
20218
|
}
|
|
19899
20219
|
function selfCheck(binPath, expectedVersion) {
|
|
@@ -19918,17 +20238,17 @@ function selfCheck(binPath, expectedVersion) {
|
|
|
19918
20238
|
// src/update/installer.ts
|
|
19919
20239
|
init_esm_shims();
|
|
19920
20240
|
import * as fs29 from "fs";
|
|
19921
|
-
import * as
|
|
20241
|
+
import * as path37 from "path";
|
|
19922
20242
|
|
|
19923
20243
|
// src/update/state.ts
|
|
19924
20244
|
init_esm_shims();
|
|
19925
20245
|
import * as fs28 from "fs";
|
|
19926
|
-
import * as
|
|
20246
|
+
import * as path36 from "path";
|
|
19927
20247
|
function updatesDir(dataDir) {
|
|
19928
|
-
return
|
|
20248
|
+
return path36.join(dataDir, "updates");
|
|
19929
20249
|
}
|
|
19930
20250
|
function statePath(dataDir) {
|
|
19931
|
-
return
|
|
20251
|
+
return path36.join(updatesDir(dataDir), "state.json");
|
|
19932
20252
|
}
|
|
19933
20253
|
function loadState(dataDir) {
|
|
19934
20254
|
try {
|
|
@@ -19955,12 +20275,12 @@ function patchState(dataDir, patch) {
|
|
|
19955
20275
|
|
|
19956
20276
|
// src/update/installer.ts
|
|
19957
20277
|
function backupDir(dataDir) {
|
|
19958
|
-
return
|
|
20278
|
+
return path37.join(updatesDir(dataDir), "backup");
|
|
19959
20279
|
}
|
|
19960
20280
|
function backupCurrent(dataDir, execPath, version) {
|
|
19961
20281
|
const dir = backupDir(dataDir);
|
|
19962
20282
|
fs29.mkdirSync(dir, { recursive: true });
|
|
19963
|
-
const target =
|
|
20283
|
+
const target = path37.join(dir, `zqagent-${version}${process.platform === "win32" ? ".exe" : ""}`);
|
|
19964
20284
|
fs29.copyFileSync(execPath, target);
|
|
19965
20285
|
fs29.chmodSync(target, 493);
|
|
19966
20286
|
return target;
|
|
@@ -19992,7 +20312,7 @@ function installStaged(opts) {
|
|
|
19992
20312
|
const from = currentVersion();
|
|
19993
20313
|
log(`\u6B63\u5728\u5B89\u88C5 v${opts.version}\uFF08\u5F53\u524D v${from}\uFF09`);
|
|
19994
20314
|
const backupPath = backupCurrent(opts.dataDir, opts.execPath, from);
|
|
19995
|
-
log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${
|
|
20315
|
+
log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${path37.basename(backupPath)}`);
|
|
19996
20316
|
replaceExecutable(opts.execPath, opts.stagedPath);
|
|
19997
20317
|
return patchState(opts.dataDir, {
|
|
19998
20318
|
stagedVersion: void 0,
|
|
@@ -20036,7 +20356,7 @@ function rollback(dataDir, execPath, log = () => {
|
|
|
20036
20356
|
}
|
|
20037
20357
|
function readRollbackTarget(dataDir) {
|
|
20038
20358
|
try {
|
|
20039
|
-
const raw = fs29.readFileSync(
|
|
20359
|
+
const raw = fs29.readFileSync(path37.join(updatesDir(dataDir), "state.json"), "utf-8");
|
|
20040
20360
|
const s = JSON.parse(raw);
|
|
20041
20361
|
if (s.backupPath && s.backupVersion) return s;
|
|
20042
20362
|
} catch {
|
|
@@ -20213,7 +20533,7 @@ var UpdateController = class {
|
|
|
20213
20533
|
if (!bin) return `\u6E05\u5355\u6CA1\u6709\u5F53\u524D\u5E73\u53F0\uFF08${platformKey()}\uFF09\u7684\u4EA7\u7269`;
|
|
20214
20534
|
let stagedPath = state.stagedPath;
|
|
20215
20535
|
if (!(state.stagedVersion === manifest.version && stagedPath && fs30.existsSync(stagedPath))) {
|
|
20216
|
-
stagedPath =
|
|
20536
|
+
stagedPath = path38.join(
|
|
20217
20537
|
updatesDir(this.opts.dataDir),
|
|
20218
20538
|
"staged",
|
|
20219
20539
|
`zqagent-${manifest.version}${process.platform === "win32" ? ".exe" : ""}`
|
|
@@ -20388,7 +20708,7 @@ function seedDemoTenant(store) {
|
|
|
20388
20708
|
apiKey: env2("OPENAI_API_KEY"),
|
|
20389
20709
|
baseURL: env2("OPENAI_BASE_URL")
|
|
20390
20710
|
},
|
|
20391
|
-
workspaceRoot: workspace ?
|
|
20711
|
+
workspaceRoot: workspace ? path39.resolve(workspace) : void 0,
|
|
20392
20712
|
allowedCommands: []
|
|
20393
20713
|
};
|
|
20394
20714
|
store.saveTenant(demo);
|
|
@@ -20442,9 +20762,9 @@ function syncTenantModels(store) {
|
|
|
20442
20762
|
tenant.model = model;
|
|
20443
20763
|
changed = true;
|
|
20444
20764
|
}
|
|
20445
|
-
if (workspace && tenant.workspaceRoot !==
|
|
20446
|
-
console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${
|
|
20447
|
-
tenant.workspaceRoot =
|
|
20765
|
+
if (workspace && tenant.workspaceRoot !== path39.resolve(workspace)) {
|
|
20766
|
+
console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${path39.resolve(workspace)}`);
|
|
20767
|
+
tenant.workspaceRoot = path39.resolve(workspace);
|
|
20448
20768
|
changed = true;
|
|
20449
20769
|
}
|
|
20450
20770
|
if (apiKey && tenant.provider.apiKey !== apiKey) {
|
|
@@ -20505,7 +20825,7 @@ function listenWithPortFallback(server, startPort, host, dataDir, limit = PORT_F
|
|
|
20505
20825
|
}
|
|
20506
20826
|
try {
|
|
20507
20827
|
fs31.mkdirSync(dataDir, { recursive: true });
|
|
20508
|
-
fs31.writeFileSync(
|
|
20828
|
+
fs31.writeFileSync(path39.join(dataDir, "server-port"), `${actualPort}
|
|
20509
20829
|
`);
|
|
20510
20830
|
} catch {
|
|
20511
20831
|
}
|
|
@@ -20516,7 +20836,7 @@ function listenWithPortFallback(server, startPort, host, dataDir, limit = PORT_F
|
|
|
20516
20836
|
});
|
|
20517
20837
|
}
|
|
20518
20838
|
function startServer(opts = {}) {
|
|
20519
|
-
const dataDir =
|
|
20839
|
+
const dataDir = path39.resolve(opts.dataDir ?? env2("DATA_DIR") ?? DEFAULT_DATA_DIR);
|
|
20520
20840
|
const port = opts.port ?? Number(env2("PORT") ?? 8787);
|
|
20521
20841
|
const host = opts.host ?? env2("HOST") ?? "127.0.0.1";
|
|
20522
20842
|
exitWhenOrphaned();
|
|
@@ -20546,7 +20866,7 @@ function startServer(opts = {}) {
|
|
|
20546
20866
|
}
|
|
20547
20867
|
const adminKey = ensureAdminKey();
|
|
20548
20868
|
const secretKey = env2("SECRET_KEY");
|
|
20549
|
-
const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(
|
|
20869
|
+
const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(path39.join(dataDir, "secret.key"));
|
|
20550
20870
|
if (secretKey && !secretBox) {
|
|
20551
20871
|
console.warn("[crypto] SECRET_KEY \u975E\u6CD5\uFF08\u9700 64 \u4F4D hex\uFF09\uFF0C\u901A\u77E5\u51ED\u636E\u5C06\u9000\u5316\u4E3A\u660E\u6587");
|
|
20552
20872
|
}
|
|
@@ -20555,7 +20875,7 @@ function startServer(opts = {}) {
|
|
|
20555
20875
|
let fleet = null;
|
|
20556
20876
|
let fleetCred = null;
|
|
20557
20877
|
function connectFleet(cred) {
|
|
20558
|
-
const localId =
|
|
20878
|
+
const localId = ensureNodeId(store);
|
|
20559
20879
|
const nodeId = (cred.nodeId ?? "").trim() || localId;
|
|
20560
20880
|
if (nodeId !== localId) store.saveNodeId(nodeId);
|
|
20561
20881
|
fleet?.close();
|
|
@@ -20583,7 +20903,7 @@ function startServer(opts = {}) {
|
|
|
20583
20903
|
function fleetStatus() {
|
|
20584
20904
|
return {
|
|
20585
20905
|
linked: fleet !== null,
|
|
20586
|
-
nodeId:
|
|
20906
|
+
nodeId: ensureNodeId(store),
|
|
20587
20907
|
hubUrl: fleetCred?.hubUrl,
|
|
20588
20908
|
gatewayUrl: fleetCred?.gatewayUrl ?? env2("FLEET_GATEWAY_URL"),
|
|
20589
20909
|
online: fleet?.isConnected() ?? false,
|
|
@@ -20595,8 +20915,15 @@ function startServer(opts = {}) {
|
|
|
20595
20915
|
const fleetController = {
|
|
20596
20916
|
status: fleetStatus,
|
|
20597
20917
|
async enroll(input) {
|
|
20598
|
-
const nodeId =
|
|
20599
|
-
const cred = await
|
|
20918
|
+
const nodeId = ensureNodeId(store);
|
|
20919
|
+
const cred = input.account ? await hubEnrollByAccount(input.hubUrl, {
|
|
20920
|
+
email: input.account.email,
|
|
20921
|
+
phone: input.account.phone,
|
|
20922
|
+
password: input.account.password,
|
|
20923
|
+
code: input.account.smsCode,
|
|
20924
|
+
nodeId,
|
|
20925
|
+
label: input.label
|
|
20926
|
+
}) : await hubEnrollByCode(input.hubUrl, {
|
|
20600
20927
|
code: input.code,
|
|
20601
20928
|
nodeId,
|
|
20602
20929
|
label: input.label
|
|
@@ -20622,7 +20949,7 @@ function startServer(opts = {}) {
|
|
|
20622
20949
|
gatewayUrl: fleetCred.gatewayUrl ?? env2("FLEET_GATEWAY_URL"),
|
|
20623
20950
|
fleetToken: fleetCred.fleetToken,
|
|
20624
20951
|
fleetSecret: fleetCred.fleetSecret,
|
|
20625
|
-
nodeId:
|
|
20952
|
+
nodeId: ensureNodeId(store)
|
|
20626
20953
|
};
|
|
20627
20954
|
}
|
|
20628
20955
|
};
|
|
@@ -20775,7 +21102,7 @@ function printReady(info, qq) {
|
|
|
20775
21102
|
`zqagent server \u76D1\u542C http://${info.host}:${info.port}`,
|
|
20776
21103
|
`\u6570\u636E\u76EE\u5F55: ${info.dataDir}`,
|
|
20777
21104
|
`\u5BA1\u6279\u6302\u8D77\u8D85\u65F6: ${env2("APPROVAL_TIMEOUT_SECONDS") ?? 900}s\uFF08\u8D85\u65F6\u540E\u56DE\u6536\u5185\u5B58\u4EFB\u52A1\uFF0C\u53EF\u7ECF resume \u6062\u590D\uFF09`,
|
|
20778
|
-
env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${
|
|
21105
|
+
env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${path39.join(info.dataDir, "admin.json")}\uFF09` : `\u7BA1\u7406\u5458\u5BC6\u94A5: ${info.adminKey}\uFF08\u63A7\u5236\u53F0\u591A\u9879\u76EE\u5165\u53E3\uFF1A/api/v1/projects\uFF1B\u9879\u76EE\u7EA7\u63A5\u53E3\u52A0 X-Project-Id \u5934\uFF09`
|
|
20779
21106
|
];
|
|
20780
21107
|
if (info.fleetNodeId) {
|
|
20781
21108
|
lines.push(
|
|
@@ -20794,152 +21121,6 @@ function printReady(info, qq) {
|
|
|
20794
21121
|
for (const l of lines) console.log(l);
|
|
20795
21122
|
}
|
|
20796
21123
|
|
|
20797
|
-
// src/cli/account-enroll.ts
|
|
20798
|
-
init_esm_shims();
|
|
20799
|
-
import * as path39 from "path";
|
|
20800
|
-
import * as readline from "readline/promises";
|
|
20801
|
-
var SKIP_ANSWERS = /* @__PURE__ */ new Set(["-", "skip", "none", "no"]);
|
|
20802
|
-
function resolveDefaultHub(hubUrl, env3 = process.env) {
|
|
20803
|
-
return (hubUrl ?? env3.HUB_URL ?? DEFAULT_HUB_URL).trim().replace(/\/$/, "");
|
|
20804
|
-
}
|
|
20805
|
-
function parseHubAnswer(answer, fallback) {
|
|
20806
|
-
const a = answer.trim();
|
|
20807
|
-
if (SKIP_ANSWERS.has(a.toLowerCase())) return null;
|
|
20808
|
-
return (a || fallback).replace(/\/$/, "");
|
|
20809
|
-
}
|
|
20810
|
-
function resolveDataDir2(dataDir) {
|
|
20811
|
-
return path39.resolve(dataDir ?? process.env.DATA_DIR ?? DEFAULT_DATA_DIR);
|
|
20812
|
-
}
|
|
20813
|
-
async function maybeAccountEnroll(opts = {}) {
|
|
20814
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
20815
|
-
let store;
|
|
20816
|
-
try {
|
|
20817
|
-
store = new Store(resolveDataDir2(opts.dataDir));
|
|
20818
|
-
if (resolveFleetConfig(store)) return;
|
|
20819
|
-
} catch (e) {
|
|
20820
|
-
cliErr(`\u63A5\u5165\u68C0\u67E5\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
20821
|
-
return;
|
|
20822
|
-
}
|
|
20823
|
-
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
20824
|
-
try {
|
|
20825
|
-
await runWizard(rl2, store, opts);
|
|
20826
|
-
} catch (e) {
|
|
20827
|
-
cliErr(`\u63A5\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
20828
|
-
cliLog(" \u53EF\u7A0D\u540E\u5728\u63A7\u5236\u53F0\u751F\u6210\u63A5\u5165\u7801\uFF0C\u518D\u6267\u884C\uFF1Azqagent link --hub <Hub\u5730\u5740> --code <\u63A5\u5165\u7801>");
|
|
20829
|
-
} finally {
|
|
20830
|
-
rl2.close();
|
|
20831
|
-
}
|
|
20832
|
-
}
|
|
20833
|
-
async function runWizard(rl2, store, opts) {
|
|
20834
|
-
cliLog("");
|
|
20835
|
-
cliLog("\u672C\u673A\u5C1A\u672A\u63A5\u5165 Hub\u3002\u53EF\u7528 Hub \u8D26\u53F7\u63A5\u5165\uFF08\u8282\u70B9\u9ED8\u8BA4\u8FDB\u5165\u300C\u672A\u5206\u7EC4\u300D\uFF09\uFF1B\u8F93\u5165 - \u8DF3\u8FC7\u4FDD\u6301\u7EAF\u672C\u5730\u3002");
|
|
20836
|
-
const fallback = resolveDefaultHub(opts.hubUrl);
|
|
20837
|
-
const answer = await rl2.question(`Hub \u5730\u5740 [${fallback}]\uFF1A`);
|
|
20838
|
-
const hubUrl = parseHubAnswer(answer, fallback);
|
|
20839
|
-
if (!hubUrl) {
|
|
20840
|
-
cliLog("\u5DF2\u8DF3\u8FC7\u63A5\u5165\uFF1A\u8282\u70B9\u4FDD\u6301\u7EAF\u672C\u5730\u8FD0\u884C\u3002");
|
|
20841
|
-
return;
|
|
20842
|
-
}
|
|
20843
|
-
cliLog("\u767B\u5F55\u65B9\u5F0F\uFF1A1) \u90AE\u7BB1 + \u5BC6\u7801 2) \u624B\u673A\u53F7 + \u5BC6\u7801 3) \u624B\u673A\u53F7 + \u77ED\u4FE1\u9A8C\u8BC1\u7801");
|
|
20844
|
-
const mode = (await rl2.question("\u8BF7\u9009\u62E9 [1]\uFF1A")).trim() || "1";
|
|
20845
|
-
const input = {};
|
|
20846
|
-
if (mode === "1") {
|
|
20847
|
-
input.email = (await rl2.question("\u90AE\u7BB1\uFF1A")).trim();
|
|
20848
|
-
input.password = await askHidden(rl2, "\u5BC6\u7801\uFF1A");
|
|
20849
|
-
if (!input.email || !input.password) {
|
|
20850
|
-
cliErr("\u90AE\u7BB1\u4E0E\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
20851
|
-
return;
|
|
20852
|
-
}
|
|
20853
|
-
} else if (mode === "2") {
|
|
20854
|
-
input.phone = (await rl2.question("\u624B\u673A\u53F7\uFF1A")).trim();
|
|
20855
|
-
input.password = await askHidden(rl2, "\u5BC6\u7801\uFF1A");
|
|
20856
|
-
if (!input.phone || !input.password) {
|
|
20857
|
-
cliErr("\u624B\u673A\u53F7\u4E0E\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
20858
|
-
return;
|
|
20859
|
-
}
|
|
20860
|
-
} else if (mode === "3") {
|
|
20861
|
-
input.phone = (await rl2.question("\u624B\u673A\u53F7\uFF1A")).trim();
|
|
20862
|
-
if (!input.phone) {
|
|
20863
|
-
cliErr("\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
20864
|
-
return;
|
|
20865
|
-
}
|
|
20866
|
-
try {
|
|
20867
|
-
const sent = await hubSendSms(hubUrl, { phone: input.phone, purpose: "login" });
|
|
20868
|
-
if (sent.debugCode) cliLog(`\uFF08\u8C03\u8BD5\u6A21\u5F0F\u9A8C\u8BC1\u7801\uFF1A${sent.debugCode}\uFF09`);
|
|
20869
|
-
else cliLog("\u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0C5 \u5206\u949F\u5185\u6709\u6548\u3002");
|
|
20870
|
-
} catch (e) {
|
|
20871
|
-
cliErr(`\u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
20872
|
-
cliLog(" \u53EF\u6539\u7528\u65B9\u5F0F 1/2\uFF08\u5BC6\u7801\u767B\u5F55\uFF09\uFF0C\u6216\u5728\u7F51\u9875\u7AEF\u751F\u6210\u63A5\u5165\u7801\u540E\u6267\u884C\uFF1A");
|
|
20873
|
-
cliLog(" zqagent link --hub <Hub\u5730\u5740> --code <\u63A5\u5165\u7801>");
|
|
20874
|
-
return;
|
|
20875
|
-
}
|
|
20876
|
-
input.code = (await rl2.question("\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF1A")).trim();
|
|
20877
|
-
if (!input.code) {
|
|
20878
|
-
cliErr("\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
20879
|
-
return;
|
|
20880
|
-
}
|
|
20881
|
-
} else {
|
|
20882
|
-
cliErr("\u672A\u77E5\u7684\u767B\u5F55\u65B9\u5F0F\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
|
|
20883
|
-
return;
|
|
20884
|
-
}
|
|
20885
|
-
const nodeId = ensureNodeId2(store);
|
|
20886
|
-
const cred = await hubEnrollByAccount(hubUrl, { ...input, nodeId, label: opts.label });
|
|
20887
|
-
store.saveFleetCredential({
|
|
20888
|
-
...cred,
|
|
20889
|
-
label: opts.label ?? cred.label,
|
|
20890
|
-
enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20891
|
-
});
|
|
20892
|
-
cliLog(`\u2705 \u5DF2\u63A5\u5165 Hub\uFF1A\u8282\u70B9 ${cred.nodeId}\uFF08\u7F51\u5173 ${cred.gatewayUrl ?? "\u672A\u4E0B\u53D1"}\uFF09`);
|
|
20893
|
-
cliLog(" \u51ED\u636E\u5DF2\u4FDD\u5B58\u5230 fleet.json\uFF0C\u672C\u6B21\u542F\u52A8\u5373\u8FDE\u63A5 Hub\u3002");
|
|
20894
|
-
}
|
|
20895
|
-
async function askHidden(rl2, prompt) {
|
|
20896
|
-
if (!process.stdin.isTTY) return (await rl2.question(prompt)).trim();
|
|
20897
|
-
return new Promise((resolve24) => {
|
|
20898
|
-
const saved = process.stdin.listeners("data");
|
|
20899
|
-
process.stdin.removeAllListeners("data");
|
|
20900
|
-
cliWrite(prompt);
|
|
20901
|
-
rl2.pause();
|
|
20902
|
-
process.stdin.setRawMode(true);
|
|
20903
|
-
process.stdin.resume();
|
|
20904
|
-
let buf = "";
|
|
20905
|
-
const cleanup = () => {
|
|
20906
|
-
process.stdin.removeListener("data", onData);
|
|
20907
|
-
for (const listener of saved) {
|
|
20908
|
-
process.stdin.on("data", listener);
|
|
20909
|
-
}
|
|
20910
|
-
process.stdin.setRawMode(true);
|
|
20911
|
-
rl2.resume();
|
|
20912
|
-
};
|
|
20913
|
-
const onData = (chunk) => {
|
|
20914
|
-
const s = chunk.toString().replace(/\x1b\[[0-9;?]*[A-Za-z~]/g, "").replace(/\x1bO[A-Za-z]/g, "");
|
|
20915
|
-
for (const ch of s) {
|
|
20916
|
-
if (ch === "\r" || ch === "\n") {
|
|
20917
|
-
cleanup();
|
|
20918
|
-
cliWrite("\n");
|
|
20919
|
-
resolve24(buf);
|
|
20920
|
-
return;
|
|
20921
|
-
}
|
|
20922
|
-
if (ch === "") {
|
|
20923
|
-
cleanup();
|
|
20924
|
-
cliWrite("\n");
|
|
20925
|
-
resolve24("");
|
|
20926
|
-
return;
|
|
20927
|
-
}
|
|
20928
|
-
if (ch === "\x7F" || ch === "\b") {
|
|
20929
|
-
if (buf.length > 0) {
|
|
20930
|
-
buf = buf.slice(0, -1);
|
|
20931
|
-
cliWrite("\b \b");
|
|
20932
|
-
}
|
|
20933
|
-
} else if (ch >= " ") {
|
|
20934
|
-
buf += ch;
|
|
20935
|
-
cliWrite("*");
|
|
20936
|
-
}
|
|
20937
|
-
}
|
|
20938
|
-
};
|
|
20939
|
-
process.stdin.on("data", onData);
|
|
20940
|
-
});
|
|
20941
|
-
}
|
|
20942
|
-
|
|
20943
21124
|
// src/cli/serve.ts
|
|
20944
21125
|
function parse2(argv) {
|
|
20945
21126
|
const out = {};
|
|
@@ -20952,6 +21133,21 @@ function parse2(argv) {
|
|
|
20952
21133
|
} else if (a === "--enroll-code" || a === "--code") {
|
|
20953
21134
|
out.code = v;
|
|
20954
21135
|
i++;
|
|
21136
|
+
} else if (a === "--username" || a === "--user") {
|
|
21137
|
+
out.username = v;
|
|
21138
|
+
i++;
|
|
21139
|
+
} else if (a === "--email") {
|
|
21140
|
+
out.email = v;
|
|
21141
|
+
i++;
|
|
21142
|
+
} else if (a === "--phone") {
|
|
21143
|
+
out.phone = v;
|
|
21144
|
+
i++;
|
|
21145
|
+
} else if (a === "--password") {
|
|
21146
|
+
out.password = v;
|
|
21147
|
+
i++;
|
|
21148
|
+
} else if (a === "--sms-code") {
|
|
21149
|
+
out.smsCode = v;
|
|
21150
|
+
i++;
|
|
20955
21151
|
} else if (a === "--label") {
|
|
20956
21152
|
out.label = v;
|
|
20957
21153
|
i++;
|
|
@@ -20972,7 +21168,32 @@ async function runServeCommand(argv) {
|
|
|
20972
21168
|
const args = parse2(argv);
|
|
20973
21169
|
const hubUrl = (args.hubUrl ?? process.env.HUB_URL ?? "").trim();
|
|
20974
21170
|
const code = (args.code ?? process.env.HUB_ENROLL_CODE ?? "").trim();
|
|
20975
|
-
|
|
21171
|
+
const account = buildAccountAuth(args);
|
|
21172
|
+
if (code && account) {
|
|
21173
|
+
cliErr("--enroll-code \u4E0E Hub \u8D26\u53F7\u53C2\u6570\uFF08--username/--password \u7B49\uFF09\u53EA\u80FD\u4E8C\u9009\u4E00\u3002");
|
|
21174
|
+
return;
|
|
21175
|
+
}
|
|
21176
|
+
if (account && !isCompleteAccount(account) && !process.stdin.isTTY) {
|
|
21177
|
+
cliErr("\u8D26\u53F7\u53C2\u6570\u7F3A\u5C11\u5BC6\u7801\u6216\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF1A\u975E\u4EA4\u4E92\u73AF\u5883\u8BF7\u8865 --password \u6216 --sms-code\u3002");
|
|
21178
|
+
}
|
|
21179
|
+
if (!code && isCompleteAccount(account)) {
|
|
21180
|
+
try {
|
|
21181
|
+
const r = await linkNode({
|
|
21182
|
+
hubUrl: resolveDefaultHub(args.hubUrl || void 0),
|
|
21183
|
+
account,
|
|
21184
|
+
dataDir: args.dataDir,
|
|
21185
|
+
label: args.label,
|
|
21186
|
+
host: args.host,
|
|
21187
|
+
port: args.port
|
|
21188
|
+
});
|
|
21189
|
+
cliLog(
|
|
21190
|
+
r.mode === "hot" ? `\u2705 \u5DF2\u901A\u8FC7 Hub \u8D26\u53F7\u63A5\u5165\uFF08\u8282\u70B9 ${r.nodeId}\uFF09` : `\u2705 \u5DF2\u4FDD\u5B58 Hub \u63A5\u5165\u51ED\u636E\uFF08\u8282\u70B9 ${r.nodeId}\uFF09`
|
|
21191
|
+
);
|
|
21192
|
+
} catch (e) {
|
|
21193
|
+
cliErr(`Hub \u8D26\u53F7\u63A5\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
21194
|
+
}
|
|
21195
|
+
}
|
|
21196
|
+
if (!(hubUrl && code) && !isCompleteAccount(account)) {
|
|
20976
21197
|
await maybeAccountEnroll({ dataDir: args.dataDir, hubUrl: args.hubUrl, label: args.label });
|
|
20977
21198
|
}
|
|
20978
21199
|
const handle = startServer({
|
|
@@ -21547,7 +21768,7 @@ function cycleMode(engine) {
|
|
|
21547
21768
|
engine.permissions.mode = next;
|
|
21548
21769
|
return next;
|
|
21549
21770
|
}
|
|
21550
|
-
var rl =
|
|
21771
|
+
var rl = readline3.createInterface({
|
|
21551
21772
|
input: process.stdin,
|
|
21552
21773
|
output: process.stdout
|
|
21553
21774
|
});
|
|
@@ -22027,7 +22248,7 @@ async function offerTerminalSetup(engine, canUseAgent) {
|
|
|
22027
22248
|
}
|
|
22028
22249
|
var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set(["link", "serve", "update"]);
|
|
22029
22250
|
function cliVersion() {
|
|
22030
|
-
const env3 = "0.2.
|
|
22251
|
+
const env3 = "0.2.12"?.trim();
|
|
22031
22252
|
if (env3) return env3;
|
|
22032
22253
|
try {
|
|
22033
22254
|
const req2 = createRequire4(import.meta.url);
|
|
@@ -22045,11 +22266,13 @@ function printCliUsage() {
|
|
|
22045
22266
|
cliLog(" zqagent serve [\u9009\u9879] \u4EE5 headless \u65B9\u5F0F\u5E38\u9A7B\u4E3A\u53EF\u88AB Hub \u7BA1\u7406\u7684\u8282\u70B9");
|
|
22046
22267
|
cliLog(" zqagent update [--status|--check|--now|--rollback]");
|
|
22047
22268
|
cliLog(" \u67E5\u770B/\u624B\u52A8\u89E6\u53D1\u8282\u70B9\u81EA\u52A8\u66F4\u65B0");
|
|
22048
|
-
cliLog(" zqagent link --hub <url> --
|
|
22049
|
-
cliLog("
|
|
22269
|
+
cliLog(" zqagent link [--hub <url>] [--username <\u90AE\u7BB1/\u624B\u673A\u53F7> --password <\u5BC6\u7801>]");
|
|
22270
|
+
cliLog(" [--code <\u63A5\u5165\u7801>] [--label \u540D\u79F0]");
|
|
22271
|
+
cliLog(" \u628A\u672C\u673A\u8282\u70B9\u63A5\u5165 Hub\uFF08\u8D26\u53F7\u76F4\u8FDE\u6216\u63A5\u5165\u7801\uFF1B\u672C\u673A\u6709 server \u65F6\u70ED\u63A5\u5165\uFF09");
|
|
22050
22272
|
cliLog("");
|
|
22051
22273
|
cliLog("serve \u9009\u9879\uFF1A--port <n> --host <addr> --data-dir <dir>");
|
|
22052
22274
|
cliLog(" --hub <url> --enroll-code <\u7801> --label <\u540D\u79F0> \uFF08\u542F\u52A8\u5373\u63A5\u5165\uFF09");
|
|
22275
|
+
cliLog(" --hub <url> --username <\u90AE\u7BB1/\u624B\u673A\u53F7> --password <\u5BC6\u7801>\uFF08\u8D26\u53F7\u63A5\u5165\uFF09");
|
|
22053
22276
|
cliLog("\u5176\u5B83\u9009\u9879\uFF1A--resume <\u4F1A\u8BDDID> --debug --help/-h --version/-v");
|
|
22054
22277
|
}
|
|
22055
22278
|
async function main() {
|
|
@@ -22066,7 +22289,10 @@ async function main() {
|
|
|
22066
22289
|
process.exit(0);
|
|
22067
22290
|
}
|
|
22068
22291
|
if (subcommand === "link") {
|
|
22069
|
-
const ok2 = await runLinkCommand(process.argv.slice(3)
|
|
22292
|
+
const ok2 = await runLinkCommand(process.argv.slice(3), {
|
|
22293
|
+
rl,
|
|
22294
|
+
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
22295
|
+
});
|
|
22070
22296
|
rl.close();
|
|
22071
22297
|
process.exit(ok2 ? 0 : 1);
|
|
22072
22298
|
}
|
|
@@ -22967,7 +23193,11 @@ async function main() {
|
|
|
22967
23193
|
case "/link": {
|
|
22968
23194
|
const code = parts[1];
|
|
22969
23195
|
const hub = parts[2] ?? process.env.HUB_URL;
|
|
22970
|
-
if (!code
|
|
23196
|
+
if (!code) {
|
|
23197
|
+
await runLinkCommand([], { rl, interactive: true });
|
|
23198
|
+
break;
|
|
23199
|
+
}
|
|
23200
|
+
if (!hub) {
|
|
22971
23201
|
cliLog("\u7528\u6CD5: /link <\u6CE8\u518C\u7801> [Hub\u5730\u5740]\uFF08Hub\u5730\u5740\u4E5F\u53EF\u7531 HUB_URL \u73AF\u5883\u53D8\u91CF\u63D0\u4F9B\uFF09");
|
|
22972
23202
|
break;
|
|
22973
23203
|
}
|