@yuandc/aica 0.1.1 → 0.1.2
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/acp/agent.js +1 -54
- package/dist/acp/client/acp-client.js +1 -102
- package/dist/acp/client/acp-content.js +1 -13
- package/dist/acp/client/acp-events.js +1 -106
- package/dist/acp/client/acp-process.js +1 -34
- package/dist/acp/client/acp-runtime-pool.js +1 -248
- package/dist/acp/client/context-usage.js +1 -29
- package/dist/acp/client/json-rpc.js +4 -128
- package/dist/acp/provider-types.js +0 -1
- package/dist/acp/providers/codex/codex-process.js +1 -51
- package/dist/acp/providers/codex/events.js +28 -1473
- package/dist/acp/providers/codex/permissions.js +1 -49
- package/dist/acp/providers/codex/provider.js +1 -376
- package/dist/acp/providers/codex-acp/adapter.js +5 -947
- package/dist/acp/providers/codex-acp/context-maintenance.js +5 -148
- package/dist/acp/providers/codex-acp/launch.js +1 -35
- package/dist/acp/providers/codex-acp/provider.js +1 -486
- package/dist/acp/providers/mimo/provider.js +5 -448
- package/dist/acp/providers/opencode/provider.js +4 -489
- package/dist/acp/providers/registry.js +1 -23
- package/dist/acp/standard-events.js +1 -167
- package/dist/commands/start.js +1 -137
- package/dist/commands/worker-auth.js +4 -100
- package/dist/commands/worker-project.js +1 -57
- package/dist/core/aca-config.js +1 -74
- package/dist/core/aca-server-client.js +1 -57
- package/dist/core/acp-event-coalescer.js +1 -108
- package/dist/core/acp-event-upload-filter.js +1 -16
- package/dist/core/acp-orphan-cleanup.js +1 -91
- package/dist/core/affected-files.js +2 -268
- package/dist/core/auth.js +1 -36
- package/dist/core/file-transfer-worker.js +1 -169
- package/dist/core/fs.js +2 -28
- package/dist/core/heartbeat.js +3 -578
- package/dist/core/job-permission-policy.js +1 -42
- package/dist/core/job-worker.js +6 -749
- package/dist/core/logger.js +3 -42
- package/dist/core/long-poll-worker.js +1 -26
- package/dist/core/machine-filesystem-worker.js +3 -352
- package/dist/core/paths.js +1 -26
- package/dist/core/process-identity.js +1 -34
- package/dist/core/process.js +2 -33
- package/dist/core/provider-health.js +1 -54
- package/dist/core/runtime-options.js +1 -38
- package/dist/core/worktree.js +1 -95
- package/dist/worker-cli.js +1 -28
- package/dist/worker-single-cli.js +1 -16
- package/package.json +1 -1
|
@@ -1,167 +1 @@
|
|
|
1
|
-
|
|
2
|
-
"agent_message_chunk",
|
|
3
|
-
"agent_progress_chunk",
|
|
4
|
-
"agent_thought_chunk",
|
|
5
|
-
"tool_call",
|
|
6
|
-
"tool_call_update",
|
|
7
|
-
"plan",
|
|
8
|
-
"plan_update",
|
|
9
|
-
"plan_removed",
|
|
10
|
-
"usage_update",
|
|
11
|
-
"session_info_update",
|
|
12
|
-
"available_commands_update",
|
|
13
|
-
"current_mode_update",
|
|
14
|
-
"config_option_update"
|
|
15
|
-
]);
|
|
16
|
-
export function createAgentMessageChunk(input) {
|
|
17
|
-
return createTextEvent("agent_message_chunk", "message", input);
|
|
18
|
-
}
|
|
19
|
-
export function createAgentProgressChunk(input) {
|
|
20
|
-
return createTextEvent("agent_progress_chunk", "progress", input);
|
|
21
|
-
}
|
|
22
|
-
export function createAgentThoughtChunk(input) {
|
|
23
|
-
return createTextEvent("agent_thought_chunk", "thought", input);
|
|
24
|
-
}
|
|
25
|
-
export function createToolCall(input) {
|
|
26
|
-
return createToolEvent({ ...input, sessionUpdate: "tool_call", status: input.status || "in_progress" });
|
|
27
|
-
}
|
|
28
|
-
export function createToolCallUpdate(input) {
|
|
29
|
-
return createToolEvent({ ...input, sessionUpdate: "tool_call_update" });
|
|
30
|
-
}
|
|
31
|
-
export function normalizeToolKind(value) {
|
|
32
|
-
const normalized = String(value || "").toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
33
|
-
if (["read", "view", "open", "list", "ls", "glob"].some((item) => normalized.includes(item)))
|
|
34
|
-
return "read";
|
|
35
|
-
if (["edit", "write", "patch", "update", "create"].some((item) => normalized.includes(item)))
|
|
36
|
-
return "edit";
|
|
37
|
-
if (normalized.includes("delete") || normalized.includes("remove"))
|
|
38
|
-
return "delete";
|
|
39
|
-
if (normalized.includes("move") || normalized.includes("rename"))
|
|
40
|
-
return "move";
|
|
41
|
-
if (["search", "grep", "find", "ripgrep", "rg"].some((item) => normalized.includes(item)))
|
|
42
|
-
return "search";
|
|
43
|
-
if (["bash", "shell", "execute", "exec", "terminal", "command", "run"].some((item) => normalized.includes(item)))
|
|
44
|
-
return "execute";
|
|
45
|
-
if (normalized.includes("think") || normalized.includes("reason"))
|
|
46
|
-
return "think";
|
|
47
|
-
if (["fetch", "web", "http", "url"].some((item) => normalized.includes(item)))
|
|
48
|
-
return "fetch";
|
|
49
|
-
if (normalized.includes("switch_mode") || normalized.includes("switchmode"))
|
|
50
|
-
return "switch_mode";
|
|
51
|
-
return "other";
|
|
52
|
-
}
|
|
53
|
-
export function normalizeToolStatus(value) {
|
|
54
|
-
const normalized = String(value || "").toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
55
|
-
if (["pending", "queued"].includes(normalized))
|
|
56
|
-
return "pending";
|
|
57
|
-
if (["running", "started", "in_progress", "inprogress", "working"].includes(normalized))
|
|
58
|
-
return "in_progress";
|
|
59
|
-
if (["error", "failed", "failure"].includes(normalized))
|
|
60
|
-
return "failed";
|
|
61
|
-
if (["cancelled", "canceled"].includes(normalized))
|
|
62
|
-
return "cancelled";
|
|
63
|
-
return "completed";
|
|
64
|
-
}
|
|
65
|
-
export function createTextChunkUpdate(sessionUpdate, text) {
|
|
66
|
-
return {
|
|
67
|
-
sessionUpdate,
|
|
68
|
-
content: {
|
|
69
|
-
type: "text",
|
|
70
|
-
text
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
export function assertStandardAgentEvent(event) {
|
|
75
|
-
if (!STANDARD_AGENT_EVENT_TYPES.has(event.type)) {
|
|
76
|
-
throw new Error(`Provider 输出了非标准事件类型: ${event.type}`);
|
|
77
|
-
}
|
|
78
|
-
const update = standardUpdateRecord(event);
|
|
79
|
-
if (event.type === "agent_message_chunk" || event.type === "agent_progress_chunk" || event.type === "agent_thought_chunk") {
|
|
80
|
-
if (update.sessionUpdate !== event.type)
|
|
81
|
-
throw new Error(`${event.type} 缺少匹配的 raw.update.sessionUpdate`);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (event.type === "tool_call" || event.type === "tool_call_update") {
|
|
85
|
-
if (update.sessionUpdate !== event.type)
|
|
86
|
-
throw new Error(`${event.type} 缺少匹配的 raw.update.sessionUpdate`);
|
|
87
|
-
assertNonEmptyString(update.toolCallId, `${event.type}.raw.update.toolCallId`);
|
|
88
|
-
assertNonEmptyString(update.kind, `${event.type}.raw.update.kind`);
|
|
89
|
-
assertNonEmptyString(update.status, `${event.type}.raw.update.status`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
export function standardUpdateRecord(event) {
|
|
93
|
-
const raw = event.raw;
|
|
94
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
95
|
-
throw new Error(`${event.type} 缺少 raw 对象`);
|
|
96
|
-
const update = raw.update;
|
|
97
|
-
if (!update || typeof update !== "object" || Array.isArray(update))
|
|
98
|
-
throw new Error(`${event.type} 缺少 raw.update 对象`);
|
|
99
|
-
return update;
|
|
100
|
-
}
|
|
101
|
-
export function firstString(...values) {
|
|
102
|
-
for (const value of values) {
|
|
103
|
-
if (typeof value === "string" && value.trim())
|
|
104
|
-
return value.trim();
|
|
105
|
-
}
|
|
106
|
-
return undefined;
|
|
107
|
-
}
|
|
108
|
-
export function removeUndefined(value) {
|
|
109
|
-
for (const key of Object.keys(value)) {
|
|
110
|
-
if (value[key] === undefined)
|
|
111
|
-
delete value[key];
|
|
112
|
-
}
|
|
113
|
-
return value;
|
|
114
|
-
}
|
|
115
|
-
function createTextEvent(type, label, input) {
|
|
116
|
-
return {
|
|
117
|
-
type,
|
|
118
|
-
label,
|
|
119
|
-
...(input.text ? { text: input.text } : {}),
|
|
120
|
-
raw: {
|
|
121
|
-
method: input.method,
|
|
122
|
-
params: input.params,
|
|
123
|
-
update: createTextChunkUpdate(type, input.text)
|
|
124
|
-
},
|
|
125
|
-
atMs: input.atMs ?? Date.now()
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
function createToolEvent(input) {
|
|
129
|
-
const sessionUpdate = input.sessionUpdate ?? "tool_call_update";
|
|
130
|
-
const kind = normalizeToolKind(input.kind);
|
|
131
|
-
const status = normalizeToolStatus(input.status);
|
|
132
|
-
const update = removeUndefined({
|
|
133
|
-
sessionUpdate,
|
|
134
|
-
toolCallId: input.toolCallId,
|
|
135
|
-
kind,
|
|
136
|
-
title: input.title,
|
|
137
|
-
status,
|
|
138
|
-
rawInput: input.rawInput,
|
|
139
|
-
rawOutput: input.rawOutput,
|
|
140
|
-
content: input.content,
|
|
141
|
-
locations: input.locations,
|
|
142
|
-
_meta: input.meta
|
|
143
|
-
});
|
|
144
|
-
return {
|
|
145
|
-
type: sessionUpdate,
|
|
146
|
-
label: sessionUpdate === "tool_call" ? "tool call" : "tool update",
|
|
147
|
-
status,
|
|
148
|
-
toolCallId: input.toolCallId,
|
|
149
|
-
raw: {
|
|
150
|
-
method: input.method,
|
|
151
|
-
params: input.params,
|
|
152
|
-
tool: removeUndefined({
|
|
153
|
-
id: input.toolCallId,
|
|
154
|
-
kind,
|
|
155
|
-
title: input.title,
|
|
156
|
-
rawInput: input.rawInput,
|
|
157
|
-
rawOutput: input.rawOutput
|
|
158
|
-
}),
|
|
159
|
-
update
|
|
160
|
-
},
|
|
161
|
-
atMs: input.atMs ?? Date.now()
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
function assertNonEmptyString(value, field) {
|
|
165
|
-
if (typeof value !== "string" || !value.trim())
|
|
166
|
-
throw new Error(`Provider 标准事件字段无效: ${field}`);
|
|
167
|
-
}
|
|
1
|
+
const u=new Set(["agent_message_chunk","agent_progress_chunk","agent_thought_chunk","tool_call","tool_call_update","plan","plan_update","plan_removed","usage_update","session_info_update","available_commands_update","current_mode_update","config_option_update"]);function _(e){return o("agent_message_chunk","message",e)}function g(e){return o("agent_progress_chunk","progress",e)}function h(e){return o("agent_thought_chunk","thought",e)}function m(e){return l({...e,sessionUpdate:"tool_call",status:e.status||"in_progress"})}function w(e){return l({...e,sessionUpdate:"tool_call_update"})}function c(e){const t=String(e||"").toLowerCase().replace(/[^a-z0-9]+/g,"_");return["read","view","open","list","ls","glob"].some(r=>t.includes(r))?"read":["edit","write","patch","update","create"].some(r=>t.includes(r))?"edit":t.includes("delete")||t.includes("remove")?"delete":t.includes("move")||t.includes("rename")?"move":["search","grep","find","ripgrep","rg"].some(r=>t.includes(r))?"search":["bash","shell","execute","exec","terminal","command","run"].some(r=>t.includes(r))?"execute":t.includes("think")||t.includes("reason")?"think":["fetch","web","http","url"].some(r=>t.includes(r))?"fetch":t.includes("switch_mode")||t.includes("switchmode")?"switch_mode":"other"}function i(e){const t=String(e||"").toLowerCase().replace(/[^a-z0-9]+/g,"_");return["pending","queued"].includes(t)?"pending":["running","started","in_progress","inprogress","working"].includes(t)?"in_progress":["error","failed","failure"].includes(t)?"failed":["cancelled","canceled"].includes(t)?"cancelled":"completed"}function p(e,t){return{sessionUpdate:e,content:{type:"text",text:t}}}function y(e){if(!u.has(e.type))throw new Error(`Provider 输出了非标准事件类型: ${e.type}`);const t=f(e);if(e.type==="agent_message_chunk"||e.type==="agent_progress_chunk"||e.type==="agent_thought_chunk"){if(t.sessionUpdate!==e.type)throw new Error(`${e.type} 缺少匹配的 raw.update.sessionUpdate`);return}if(e.type==="tool_call"||e.type==="tool_call_update"){if(t.sessionUpdate!==e.type)throw new Error(`${e.type} 缺少匹配的 raw.update.sessionUpdate`);a(t.toolCallId,`${e.type}.raw.update.toolCallId`),a(t.kind,`${e.type}.raw.update.kind`),a(t.status,`${e.type}.raw.update.status`)}}function f(e){const t=e.raw;if(!t||typeof t!="object"||Array.isArray(t))throw new Error(`${e.type} 缺少 raw 对象`);const r=t.update;if(!r||typeof r!="object"||Array.isArray(r))throw new Error(`${e.type} 缺少 raw.update 对象`);return r}function x(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim()}function s(e){for(const t of Object.keys(e))e[t]===void 0&&delete e[t];return e}function o(e,t,r){return{type:e,label:t,...r.text?{text:r.text}:{},raw:{method:r.method,params:r.params,update:p(e,r.text)},atMs:r.atMs??Date.now()}}function l(e){const t=e.sessionUpdate??"tool_call_update",r=c(e.kind),n=i(e.status),d=s({sessionUpdate:t,toolCallId:e.toolCallId,kind:r,title:e.title,status:n,rawInput:e.rawInput,rawOutput:e.rawOutput,content:e.content,locations:e.locations,_meta:e.meta});return{type:t,label:t==="tool_call"?"tool call":"tool update",status:n,toolCallId:e.toolCallId,raw:{method:e.method,params:e.params,tool:s({id:e.toolCallId,kind:r,title:e.title,rawInput:e.rawInput,rawOutput:e.rawOutput}),update:d},atMs:e.atMs??Date.now()}}function a(e,t){if(typeof e!="string"||!e.trim())throw new Error(`Provider 标准事件字段无效: ${t}`)}export{u as STANDARD_AGENT_EVENT_TYPES,y as assertStandardAgentEvent,_ as createAgentMessageChunk,g as createAgentProgressChunk,h as createAgentThoughtChunk,p as createTextChunkUpdate,m as createToolCall,w as createToolCallUpdate,x as firstString,c as normalizeToolKind,i as normalizeToolStatus,s as removeUndefined,f as standardUpdateRecord};
|
package/dist/commands/start.js
CHANGED
|
@@ -1,137 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import process from "node:process";
|
|
6
|
-
import { randomUUID } from "node:crypto";
|
|
7
|
-
import { Logger } from "../core/logger.js";
|
|
8
|
-
import { loadCredentials, saveApiKeyCredential } from "../core/auth.js";
|
|
9
|
-
import { getRuntimeStatePath } from "../core/paths.js";
|
|
10
|
-
import { writeJsonFile } from "../core/fs.js";
|
|
11
|
-
import { HEARTBEAT_INTERVAL_MS, startHeartbeatLoop } from "../core/heartbeat.js";
|
|
12
|
-
import { startJobWorkerLoop } from "../core/job-worker.js";
|
|
13
|
-
import { startFileTransferWorkerLoop } from "../core/file-transfer-worker.js";
|
|
14
|
-
import { startMachineFilesystemWorkerLoop } from "../core/machine-filesystem-worker.js";
|
|
15
|
-
import { cleanupOrphanedAcpProcesses } from "../core/acp-orphan-cleanup.js";
|
|
16
|
-
import { defaultAcpRuntimePool } from "../acp/client/acp-runtime-pool.js";
|
|
17
|
-
import { writePidFile, removePidFile } from "../core/process.js";
|
|
18
|
-
import { applyRuntimePathOptions } from "../core/runtime-options.js";
|
|
19
|
-
import { loadAcaConfig, updateAcaConfig } from "../core/aca-config.js";
|
|
20
|
-
import { ACA_WORKER_INSTANCE_ENV, ACA_WORKER_PID_ENV, ACA_WORKER_START_TICKS_ENV, readProcessStartTicks } from "../core/process-identity.js";
|
|
21
|
-
export function createStartCommand() {
|
|
22
|
-
return new Command("start")
|
|
23
|
-
.description("Start agent service in native mode")
|
|
24
|
-
.option("--aica-home <path>", "Runtime home directory (defaults to ~/.aica or AICA_HOME)")
|
|
25
|
-
.option("--aica-config <path>", "Config file path (defaults to <aica-home>/config.json)")
|
|
26
|
-
.option("--aca-home <path>", "Deprecated alias for --aica-home")
|
|
27
|
-
.option("--aca-config <path>", "Deprecated alias for --aica-config")
|
|
28
|
-
.option("--server <url>", "ACA Server URL override")
|
|
29
|
-
.option("--machine-id <id>", "Machine ID override for this runtime")
|
|
30
|
-
.option("--machine-name <name>", "Machine name to register (defaults to hostname)")
|
|
31
|
-
.option("--cli-types <types...>", "Specify CLI types to register, `claude` or `codex`")
|
|
32
|
-
.option("--debug", "enable debug output")
|
|
33
|
-
.option("--heartbeat-log", "output current timestamp every 5 seconds")
|
|
34
|
-
.option("--auth <api-key>", "Use a CLI API key for non-interactive authentication")
|
|
35
|
-
.action(async (options) => {
|
|
36
|
-
await runStart(options);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
async function runStart(options) {
|
|
40
|
-
applyRuntimePathOptions(options);
|
|
41
|
-
const workerInstanceId = process.env[ACA_WORKER_INSTANCE_ENV] || randomUUID();
|
|
42
|
-
const workerStartTicks = readProcessStartTicks(process.pid) || "";
|
|
43
|
-
process.env[ACA_WORKER_INSTANCE_ENV] = workerInstanceId;
|
|
44
|
-
process.env[ACA_WORKER_PID_ENV] = String(process.pid);
|
|
45
|
-
process.env[ACA_WORKER_START_TICKS_ENV] = workerStartTicks;
|
|
46
|
-
const logger = new Logger("start", options.debug ? "debug" : "info");
|
|
47
|
-
const loadedConfig = loadAcaConfig();
|
|
48
|
-
const configPatch = {
|
|
49
|
-
...(options.server?.trim() ? { serverUrl: options.server.trim() } : {}),
|
|
50
|
-
...(options.machineId ? { machineId: options.machineId.trim() } : {}),
|
|
51
|
-
...(options.machineName ? { machineName: options.machineName.trim() } : {})
|
|
52
|
-
};
|
|
53
|
-
const acaConfig = Object.keys(configPatch).length > 0 ? updateAcaConfig(configPatch) : loadedConfig;
|
|
54
|
-
const machineName = options.machineName?.trim() || acaConfig.machineName || os.hostname();
|
|
55
|
-
const credentials = options.auth ? saveApiKeyCredential(options.auth, machineName) : loadCredentials();
|
|
56
|
-
writePidFile(process.pid);
|
|
57
|
-
writeRuntimeState({
|
|
58
|
-
pid: process.pid,
|
|
59
|
-
workerInstanceId,
|
|
60
|
-
workerStartTicks,
|
|
61
|
-
phase: "running",
|
|
62
|
-
startupStage: "local-compatible",
|
|
63
|
-
machineId: acaConfig.machineId || credentials?.machine?.machineId || "local-machine",
|
|
64
|
-
machineName,
|
|
65
|
-
cliTypes: options.cliTypes ?? detectCliTypes(),
|
|
66
|
-
startedAt: new Date().toISOString(),
|
|
67
|
-
heartbeatIntervalMs: HEARTBEAT_INTERVAL_MS,
|
|
68
|
-
issues: [
|
|
69
|
-
{
|
|
70
|
-
severity: "info",
|
|
71
|
-
message: "aca is running in aca-server compatibility mode; ACP execution happens on this machine."
|
|
72
|
-
}
|
|
73
|
-
]
|
|
74
|
-
});
|
|
75
|
-
logger.info(`Starting aca local-compatible service on machine ${machineName}`);
|
|
76
|
-
logger.info(`PID ${process.pid}`);
|
|
77
|
-
const orphanCleanup = await cleanupOrphanedAcpProcesses();
|
|
78
|
-
if (orphanCleanup.terminated > 0) {
|
|
79
|
-
logger.warn(`terminated ${orphanCleanup.terminated} orphaned ACP process(es): ${orphanCleanup.pids.join(",")}`);
|
|
80
|
-
}
|
|
81
|
-
let shuttingDown = false;
|
|
82
|
-
let heartbeatTimer;
|
|
83
|
-
let jobWorker;
|
|
84
|
-
let fileTransferWorker;
|
|
85
|
-
let machineFilesystemWorker;
|
|
86
|
-
const shutdown = async (exitCode) => {
|
|
87
|
-
if (shuttingDown)
|
|
88
|
-
return;
|
|
89
|
-
shuttingDown = true;
|
|
90
|
-
logger.info("Shutting down");
|
|
91
|
-
if (heartbeatTimer)
|
|
92
|
-
clearInterval(heartbeatTimer);
|
|
93
|
-
fileTransferWorker?.stop();
|
|
94
|
-
machineFilesystemWorker?.stop();
|
|
95
|
-
await jobWorker?.stop().catch((error) => logger.warn(`job worker shutdown failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
96
|
-
const beforeDispose = defaultAcpRuntimePool.stats();
|
|
97
|
-
await defaultAcpRuntimePool.disposeAll().catch((error) => logger.warn(`ACP runtime shutdown failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
98
|
-
logger.info(`ACP runtimes stopped total=${beforeDispose.total} inUse=${beforeDispose.inUse} idle=${beforeDispose.idle}`);
|
|
99
|
-
writeRuntimeState({
|
|
100
|
-
pid: process.pid,
|
|
101
|
-
phase: "stopped",
|
|
102
|
-
stoppedAt: new Date().toISOString(),
|
|
103
|
-
issues: []
|
|
104
|
-
});
|
|
105
|
-
removePidFile();
|
|
106
|
-
process.exit(exitCode);
|
|
107
|
-
};
|
|
108
|
-
process.once("SIGINT", () => void shutdown(130));
|
|
109
|
-
process.once("SIGTERM", () => void shutdown(0));
|
|
110
|
-
if (options.heartbeatLog) {
|
|
111
|
-
setInterval(() => logger.info(`heartbeat: ${new Date().toISOString()}`), 5_000);
|
|
112
|
-
}
|
|
113
|
-
heartbeatTimer = startHeartbeatLoop(logger);
|
|
114
|
-
jobWorker = startJobWorkerLoop(logger);
|
|
115
|
-
fileTransferWorker = startFileTransferWorkerLoop(logger);
|
|
116
|
-
machineFilesystemWorker = startMachineFilesystemWorkerLoop(logger);
|
|
117
|
-
await new Promise(() => { });
|
|
118
|
-
}
|
|
119
|
-
function detectCliTypes() {
|
|
120
|
-
return ["codex", "claude"].filter((name) => commandExists(name));
|
|
121
|
-
}
|
|
122
|
-
function commandExists(name) {
|
|
123
|
-
const pathValue = process.env.PATH || "";
|
|
124
|
-
for (const dir of pathValue.split(":")) {
|
|
125
|
-
try {
|
|
126
|
-
if (dir && fs.existsSync(path.join(dir, name)))
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
// Ignore malformed PATH entries.
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
function writeRuntimeState(value) {
|
|
136
|
-
writeJsonFile(getRuntimeStatePath(), value);
|
|
137
|
-
}
|
|
1
|
+
import{Command as y}from"commander";import b from"node:fs";import T from"node:os";import P from"node:path";import t from"node:process";import{randomUUID as R}from"node:crypto";import{Logger as E}from"../core/logger.js";import{loadCredentials as k,saveApiKeyCredential as _}from"../core/auth.js";import{getRuntimeStatePath as N}from"../core/paths.js";import{writeJsonFile as D}from"../core/fs.js";import{HEARTBEAT_INTERVAL_MS as L,startHeartbeatLoop as O}from"../core/heartbeat.js";import{startJobWorkerLoop as $}from"../core/job-worker.js";import{startFileTransferWorkerLoop as W}from"../core/file-transfer-worker.js";import{startMachineFilesystemWorkerLoop as x}from"../core/machine-filesystem-worker.js";import{cleanupOrphanedAcpProcesses as M}from"../core/acp-orphan-cleanup.js";import{defaultAcpRuntimePool as A}from"../acp/client/acp-runtime-pool.js";import{writePidFile as U,removePidFile as j}from"../core/process.js";import{applyRuntimePathOptions as F}from"../core/runtime-options.js";import{loadAcaConfig as K,updateAcaConfig as V}from"../core/aca-config.js";import{ACA_WORKER_INSTANCE_ENV as I,ACA_WORKER_PID_ENV as H,ACA_WORKER_START_TICKS_ENV as G,readProcessStartTicks as J}from"../core/process-identity.js";function ge(){return new y("start").description("Start agent service in native mode").option("--aica-home <path>","Runtime home directory (defaults to ~/.aica or AICA_HOME)").option("--aica-config <path>","Config file path (defaults to <aica-home>/config.json)").option("--aca-home <path>","Deprecated alias for --aica-home").option("--aca-config <path>","Deprecated alias for --aica-config").option("--server <url>","ACA Server URL override").option("--machine-id <id>","Machine ID override for this runtime").option("--machine-name <name>","Machine name to register (defaults to hostname)").option("--cli-types <types...>","Specify CLI types to register, `claude` or `codex`").option("--debug","enable debug output").option("--heartbeat-log","output current timestamp every 5 seconds").option("--auth <api-key>","Use a CLI API key for non-interactive authentication").action(async e=>{await B(e)})}async function B(e){F(e);const r=t.env[I]||R(),o=J(t.pid)||"";t.env[I]=r,t.env[H]=String(t.pid),t.env[G]=o;const a=new E("start",e.debug?"debug":"info"),v=K(),p={...e.server?.trim()?{serverUrl:e.server.trim()}:{},...e.machineId?{machineId:e.machineId.trim()}:{},...e.machineName?{machineName:e.machineName.trim()}:{}},d=Object.keys(p).length>0?V(p):v,n=e.machineName?.trim()||d.machineName||T.hostname(),w=e.auth?_(e.auth,n):k();U(t.pid),S({pid:t.pid,workerInstanceId:r,workerStartTicks:o,phase:"running",startupStage:"local-compatible",machineId:d.machineId||w?.machine?.machineId||"local-machine",machineName:n,cliTypes:e.cliTypes??q(),startedAt:new Date().toISOString(),heartbeatIntervalMs:L,issues:[{severity:"info",message:"aca is running in aca-server compatibility mode; ACP execution happens on this machine."}]}),a.info(`Starting aca local-compatible service on machine ${n}`),a.info(`PID ${t.pid}`);const s=await M();s.terminated>0&&a.warn(`terminated ${s.terminated} orphaned ACP process(es): ${s.pids.join(",")}`);let f=!1,c,l,h,u;const g=async C=>{if(f)return;f=!0,a.info("Shutting down"),c&&clearInterval(c),h?.stop(),u?.stop(),await l?.stop().catch(i=>a.warn(`job worker shutdown failed: ${i instanceof Error?i.message:String(i)}`));const m=A.stats();await A.disposeAll().catch(i=>a.warn(`ACP runtime shutdown failed: ${i instanceof Error?i.message:String(i)}`)),a.info(`ACP runtimes stopped total=${m.total} inUse=${m.inUse} idle=${m.idle}`),S({pid:t.pid,phase:"stopped",stoppedAt:new Date().toISOString(),issues:[]}),j(),t.exit(C)};t.once("SIGINT",()=>{g(130)}),t.once("SIGTERM",()=>{g(0)}),e.heartbeatLog&&setInterval(()=>a.info(`heartbeat: ${new Date().toISOString()}`),5e3),c=O(a),l=$(a),h=W(a),u=x(a),await new Promise(()=>{})}function q(){return["codex","claude"].filter(e=>z(e))}function z(e){const r=t.env.PATH||"";for(const o of r.split(":"))try{if(o&&b.existsSync(P.join(o,e)))return!0}catch{}return!1}function S(e){D(N(),e)}export{ge as createStartCommand};
|
|
@@ -1,100 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { acaServerRequest } from "../core/aca-server-client.js";
|
|
6
|
-
import { applyRuntimePathOptions } from "../core/runtime-options.js";
|
|
7
|
-
import { DEFAULT_ACA_SERVER_URL, ensureMachineIdentity, updateAcaConfig } from "../core/aca-config.js";
|
|
8
|
-
import { getAcaConfigPath } from "../core/paths.js";
|
|
9
|
-
export function createWorkerAuthCommand() {
|
|
10
|
-
return new Command("login")
|
|
11
|
-
.description("登录 ACA Server 并生成 Worker 认证配置")
|
|
12
|
-
.option("--server <url>", "ACA Server URL")
|
|
13
|
-
.option("--username <name>", "用户名")
|
|
14
|
-
.option("--password <password>", "密码;不传入时交互式输入")
|
|
15
|
-
.option("--aica-home <path>", "Runtime home directory (defaults to ~/.aica)")
|
|
16
|
-
.option("--aica-config <path>", "Config file path")
|
|
17
|
-
.option("--aca-home <path>", "Deprecated alias for --aica-home")
|
|
18
|
-
.option("--aca-config <path>", "Deprecated alias for --aica-config")
|
|
19
|
-
.option("--machine-id <id>", "Machine ID")
|
|
20
|
-
.option("--machine-name <name>", "Machine display name")
|
|
21
|
-
.action(async (options) => {
|
|
22
|
-
applyRuntimePathOptions(options);
|
|
23
|
-
const server = options.server?.trim() || process.env.ACA_DEFAULT_SERVER_URL || DEFAULT_ACA_SERVER_URL;
|
|
24
|
-
const username = options.username?.trim() || await promptLine("用户名: ");
|
|
25
|
-
const password = options.password ?? await promptSecret("密码: ");
|
|
26
|
-
if (!username)
|
|
27
|
-
throw new Error("用户名不能为空。");
|
|
28
|
-
if (!password)
|
|
29
|
-
throw new Error("密码不能为空。");
|
|
30
|
-
const machine = ensureMachineIdentity({
|
|
31
|
-
machineId: options.machineId?.trim(),
|
|
32
|
-
machineName: options.machineName?.trim() || os.hostname()
|
|
33
|
-
});
|
|
34
|
-
const response = await acaServerRequest("POST", "/api/auth/login", {
|
|
35
|
-
username,
|
|
36
|
-
password,
|
|
37
|
-
machineId: machine.machineId,
|
|
38
|
-
machineName: machine.machineName
|
|
39
|
-
}, { serverUrl: server });
|
|
40
|
-
const config = updateAcaConfig({
|
|
41
|
-
serverUrl: server,
|
|
42
|
-
token: response.token,
|
|
43
|
-
username: response.username,
|
|
44
|
-
machineId: machine.machineId,
|
|
45
|
-
machineName: machine.machineName,
|
|
46
|
-
defaultWorkspaceId: "default"
|
|
47
|
-
});
|
|
48
|
-
console.log(`登录成功:${response.username}`);
|
|
49
|
-
console.log(`认证配置已保存:${getAcaConfigPath()}`);
|
|
50
|
-
console.log(`机器:${config.machineName} (${config.machineId})`);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
async function promptLine(message) {
|
|
54
|
-
if (!input.isTTY || !output.isTTY)
|
|
55
|
-
throw new Error("非交互环境请使用 --username 和 --password 参数。");
|
|
56
|
-
const reader = readline.createInterface({ input, output });
|
|
57
|
-
try {
|
|
58
|
-
return (await reader.question(message)).trim();
|
|
59
|
-
}
|
|
60
|
-
finally {
|
|
61
|
-
reader.close();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async function promptSecret(message) {
|
|
65
|
-
if (!input.isTTY || !output.isTTY || !input.setRawMode) {
|
|
66
|
-
throw new Error("非交互环境请使用 --password 参数。");
|
|
67
|
-
}
|
|
68
|
-
output.write(message);
|
|
69
|
-
input.setRawMode(true);
|
|
70
|
-
input.resume();
|
|
71
|
-
return new Promise((resolve, reject) => {
|
|
72
|
-
let value = "";
|
|
73
|
-
const onData = (chunk) => {
|
|
74
|
-
const char = chunk.toString("utf8");
|
|
75
|
-
if (char === "\u0003") {
|
|
76
|
-
cleanup();
|
|
77
|
-
output.write("\n");
|
|
78
|
-
reject(new Error("已取消登录。"));
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if (char === "\r" || char === "\n") {
|
|
82
|
-
cleanup();
|
|
83
|
-
output.write("\n");
|
|
84
|
-
resolve(value);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (char === "\u007f") {
|
|
88
|
-
value = value.slice(0, -1);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
value += char;
|
|
92
|
-
};
|
|
93
|
-
const cleanup = () => {
|
|
94
|
-
input.setRawMode?.(false);
|
|
95
|
-
input.pause();
|
|
96
|
-
input.off("data", onData);
|
|
97
|
-
};
|
|
98
|
-
input.on("data", onData);
|
|
99
|
-
});
|
|
100
|
-
}
|
|
1
|
+
import{Command as p}from"commander";import u from"node:os";import h from"node:readline/promises";import{stdin as a,stdout as i}from"node:process";import{acaServerRequest as f}from"../core/aca-server-client.js";import{applyRuntimePathOptions as d}from"../core/runtime-options.js";import{DEFAULT_ACA_SERVER_URL as l,ensureMachineIdentity as w,updateAcaConfig as g}from"../core/aca-config.js";import{getAcaConfigPath as A}from"../core/paths.js";function D(){return new p("login").description("登录 ACA Server 并生成 Worker 认证配置").option("--server <url>","ACA Server URL").option("--username <name>","用户名").option("--password <password>","密码;不传入时交互式输入").option("--aica-home <path>","Runtime home directory (defaults to ~/.aica)").option("--aica-config <path>","Config file path").option("--aca-home <path>","Deprecated alias for --aica-home").option("--aca-config <path>","Deprecated alias for --aica-config").option("--machine-id <id>","Machine ID").option("--machine-name <name>","Machine display name").action(async e=>{d(e);const r=e.server?.trim()||process.env.ACA_DEFAULT_SERVER_URL||l,c=e.username?.trim()||await R("用户名: "),n=e.password??await E("密码: ");if(!c)throw new Error("用户名不能为空。");if(!n)throw new Error("密码不能为空。");const o=w({machineId:e.machineId?.trim(),machineName:e.machineName?.trim()||u.hostname()}),t=await f("POST","/api/auth/login",{username:c,password:n,machineId:o.machineId,machineName:o.machineName},{serverUrl:r}),s=g({serverUrl:r,token:t.token,username:t.username,machineId:o.machineId,machineName:o.machineName,defaultWorkspaceId:"default"});console.log(`登录成功:${t.username}`),console.log(`认证配置已保存:${A()}`),console.log(`机器:${s.machineName} (${s.machineId})`)})}async function R(e){if(!a.isTTY||!i.isTTY)throw new Error("非交互环境请使用 --username 和 --password 参数。");const r=h.createInterface({input:a,output:i});try{return(await r.question(e)).trim()}finally{r.close()}}async function E(e){if(!a.isTTY||!i.isTTY||!a.setRawMode)throw new Error("非交互环境请使用 --password 参数。");return i.write(e),a.setRawMode(!0),a.resume(),new Promise((r,c)=>{let n="";const o=s=>{const m=s.toString("utf8");if(m===""){t(),i.write(`
|
|
2
|
+
`),c(new Error("已取消登录。"));return}if(m==="\r"||m===`
|
|
3
|
+
`){t(),i.write(`
|
|
4
|
+
`),r(n);return}if(m===""){n=n.slice(0,-1);return}n+=m},t=()=>{a.setRawMode?.(!1),a.pause(),a.off("data",o)};a.on("data",o)})}export{D as createWorkerAuthCommand};
|
|
@@ -1,57 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { acaServerRequest } from "../core/aca-server-client.js";
|
|
4
|
-
import { applyRuntimePathOptions } from "../core/runtime-options.js";
|
|
5
|
-
import { loadAcaConfig, upsertLocalProject } from "../core/aca-config.js";
|
|
6
|
-
export function createWorkerProjectCommand() {
|
|
7
|
-
return new Command("project")
|
|
8
|
-
.description("Manage remote projects without local SQLite")
|
|
9
|
-
.addCommand(new Command("add")
|
|
10
|
-
.description("Register a local project on ACA Server")
|
|
11
|
-
.argument("<root>", "Project root path")
|
|
12
|
-
.option("--aica-home <path>", "Runtime home directory (defaults to ~/.aica)")
|
|
13
|
-
.option("--aica-config <path>", "Config file path")
|
|
14
|
-
.option("--aca-home <path>", "Deprecated alias for --aica-home")
|
|
15
|
-
.option("--aca-config <path>", "Deprecated alias for --aica-config")
|
|
16
|
-
.option("--name <name>", "Project name")
|
|
17
|
-
.option("--workspace <id>", "ACA workspace ID")
|
|
18
|
-
.option("--type <project|chat-room>", "Project type", "project")
|
|
19
|
-
.action(async (root, options) => {
|
|
20
|
-
applyRuntimePathOptions(options);
|
|
21
|
-
if (options.type !== "project" && options.type !== "chat-room") {
|
|
22
|
-
throw new Error("Project type must be 'project' or 'chat-room'.");
|
|
23
|
-
}
|
|
24
|
-
const config = loadAcaConfig();
|
|
25
|
-
const rootPath = path.resolve(root);
|
|
26
|
-
const name = options.name?.trim() || path.basename(rootPath);
|
|
27
|
-
const response = await acaServerRequest("POST", "/api/projects", {
|
|
28
|
-
workspaceId: options.workspace || config.defaultWorkspaceId,
|
|
29
|
-
name,
|
|
30
|
-
rootPath,
|
|
31
|
-
source: "aica",
|
|
32
|
-
projectType: options.type === "chat-room" ? "chat_room" : "project"
|
|
33
|
-
});
|
|
34
|
-
const item = response.item;
|
|
35
|
-
upsertLocalProject({
|
|
36
|
-
projectId: item.project_id,
|
|
37
|
-
workspaceId: item.workspace_id,
|
|
38
|
-
name: item.name,
|
|
39
|
-
rootPath: item.root_path
|
|
40
|
-
});
|
|
41
|
-
console.log(`${item.project_id} ${item.name} ${item.root_path}`);
|
|
42
|
-
}))
|
|
43
|
-
.addCommand(new Command("list")
|
|
44
|
-
.description("List projects from ACA Server")
|
|
45
|
-
.option("--aica-home <path>", "Runtime home directory (defaults to ~/.aica)")
|
|
46
|
-
.option("--aica-config <path>", "Config file path")
|
|
47
|
-
.option("--aca-home <path>", "Deprecated alias for --aica-home")
|
|
48
|
-
.option("--aca-config <path>", "Deprecated alias for --aica-config")
|
|
49
|
-
.option("--workspace <id>", "ACA workspace ID")
|
|
50
|
-
.action(async (options) => {
|
|
51
|
-
applyRuntimePathOptions(options);
|
|
52
|
-
const query = options.workspace ? `?workspaceId=${encodeURIComponent(options.workspace)}` : "";
|
|
53
|
-
const response = await acaServerRequest("GET", `/api/projects${query}`);
|
|
54
|
-
for (const item of response.items)
|
|
55
|
-
console.log(`${item.project_id} workspace=${item.workspace_id} name=${item.name} root=${item.root_path}`);
|
|
56
|
-
}));
|
|
57
|
-
}
|
|
1
|
+
import{Command as c}from"commander";import p from"node:path";import{acaServerRequest as i}from"../core/aca-server-client.js";import{applyRuntimePathOptions as n}from"../core/runtime-options.js";import{loadAcaConfig as s,upsertLocalProject as d}from"../core/aca-config.js";function k(){return new c("project").description("Manage remote projects without local SQLite").addCommand(new c("add").description("Register a local project on ACA Server").argument("<root>","Project root path").option("--aica-home <path>","Runtime home directory (defaults to ~/.aica)").option("--aica-config <path>","Config file path").option("--aca-home <path>","Deprecated alias for --aica-home").option("--aca-config <path>","Deprecated alias for --aica-config").option("--name <name>","Project name").option("--workspace <id>","ACA workspace ID").option("--type <project|chat-room>","Project type","project").action(async(t,o)=>{if(n(o),o.type!=="project"&&o.type!=="chat-room")throw new Error("Project type must be 'project' or 'chat-room'.");const r=s(),e=p.resolve(t),m=o.name?.trim()||p.basename(e),a=(await i("POST","/api/projects",{workspaceId:o.workspace||r.defaultWorkspaceId,name:m,rootPath:e,source:"aica",projectType:o.type==="chat-room"?"chat_room":"project"})).item;d({projectId:a.project_id,workspaceId:a.workspace_id,name:a.name,rootPath:a.root_path}),console.log(`${a.project_id} ${a.name} ${a.root_path}`)})).addCommand(new c("list").description("List projects from ACA Server").option("--aica-home <path>","Runtime home directory (defaults to ~/.aica)").option("--aica-config <path>","Config file path").option("--aca-home <path>","Deprecated alias for --aica-home").option("--aca-config <path>","Deprecated alias for --aica-config").option("--workspace <id>","ACA workspace ID").action(async t=>{n(t);const o=t.workspace?`?workspaceId=${encodeURIComponent(t.workspace)}`:"",r=await i("GET",`/api/projects${o}`);for(const e of r.items)console.log(`${e.project_id} workspace=${e.workspace_id} name=${e.name} root=${e.root_path}`)}))}export{k as createWorkerProjectCommand};
|
package/dist/core/aca-config.js
CHANGED
|
@@ -1,74 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { getAcaConfigPath } from "./paths.js";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
4
|
-
import os from "node:os";
|
|
5
|
-
export const DEFAULT_ACA_SERVER_URL = "https://aca.kinghon.com.cn:8843/";
|
|
6
|
-
export function loadAcaConfig() {
|
|
7
|
-
const value = readJsonFile(getAcaConfigPath()) ?? {};
|
|
8
|
-
return {
|
|
9
|
-
serverUrl: value.serverUrl || process.env.ACA_DEFAULT_SERVER_URL || DEFAULT_ACA_SERVER_URL,
|
|
10
|
-
token: value.token,
|
|
11
|
-
username: value.username,
|
|
12
|
-
machineId: value.machineId || randomUUID(),
|
|
13
|
-
machineName: value.machineName || os.hostname(),
|
|
14
|
-
defaultWorkspaceId: value.defaultWorkspaceId || "default",
|
|
15
|
-
defaultProjectId: value.defaultProjectId,
|
|
16
|
-
projects: Array.isArray(value.projects) ? value.projects : []
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
export function ensureMachineIdentity(overrides = {}) {
|
|
20
|
-
const current = readJsonFile(getAcaConfigPath()) ?? {};
|
|
21
|
-
const config = {
|
|
22
|
-
...loadAcaConfig(),
|
|
23
|
-
...(overrides.machineId ? { machineId: overrides.machineId } : {}),
|
|
24
|
-
...(overrides.machineName ? { machineName: overrides.machineName } : {})
|
|
25
|
-
};
|
|
26
|
-
if (!current.machineId || overrides.machineId || overrides.machineName) {
|
|
27
|
-
saveAcaConfig(config);
|
|
28
|
-
}
|
|
29
|
-
return { machineId: config.machineId, machineName: config.machineName };
|
|
30
|
-
}
|
|
31
|
-
export function saveAcaConfig(config) {
|
|
32
|
-
writeJsonFile(getAcaConfigPath(), config);
|
|
33
|
-
}
|
|
34
|
-
export function updateAcaConfig(patch) {
|
|
35
|
-
const next = { ...loadAcaConfig(), ...patch };
|
|
36
|
-
saveAcaConfig(next);
|
|
37
|
-
return next;
|
|
38
|
-
}
|
|
39
|
-
export function upsertLocalProject(project) {
|
|
40
|
-
const config = loadAcaConfig();
|
|
41
|
-
const projects = config.projects.filter((item) => item.projectId !== project.projectId && item.name !== project.name);
|
|
42
|
-
projects.push(project);
|
|
43
|
-
const next = {
|
|
44
|
-
...config,
|
|
45
|
-
defaultWorkspaceId: project.workspaceId,
|
|
46
|
-
defaultProjectId: project.projectId,
|
|
47
|
-
projects
|
|
48
|
-
};
|
|
49
|
-
saveAcaConfig(next);
|
|
50
|
-
return next;
|
|
51
|
-
}
|
|
52
|
-
export function removeLocalProject(projectIdOrName) {
|
|
53
|
-
const config = loadAcaConfig();
|
|
54
|
-
const projects = config.projects.filter((item) => item.projectId !== projectIdOrName && item.name !== projectIdOrName);
|
|
55
|
-
const defaultProjectId = config.defaultProjectId === projectIdOrName || config.projects.find((item) => item.name === projectIdOrName)?.projectId === config.defaultProjectId
|
|
56
|
-
? projects[0]?.projectId
|
|
57
|
-
: config.defaultProjectId;
|
|
58
|
-
const next = {
|
|
59
|
-
...config,
|
|
60
|
-
defaultProjectId,
|
|
61
|
-
projects
|
|
62
|
-
};
|
|
63
|
-
saveAcaConfig(next);
|
|
64
|
-
return next;
|
|
65
|
-
}
|
|
66
|
-
export function findLocalProject(idOrName) {
|
|
67
|
-
const config = loadAcaConfig();
|
|
68
|
-
if (!idOrName && config.defaultProjectId) {
|
|
69
|
-
return config.projects.find((project) => project.projectId === config.defaultProjectId) ?? null;
|
|
70
|
-
}
|
|
71
|
-
if (!idOrName)
|
|
72
|
-
return config.projects[0] ?? null;
|
|
73
|
-
return config.projects.find((project) => project.projectId === idOrName || project.name === idOrName) ?? null;
|
|
74
|
-
}
|
|
1
|
+
import{readJsonFile as f,writeJsonFile as s}from"./fs.js";import{getAcaConfigPath as d}from"./paths.js";import{randomUUID as u}from"node:crypto";import m from"node:os";const p="https://aca.kinghon.com.cn:8843/";function o(){const e=f(d())??{};return{serverUrl:e.serverUrl||process.env.ACA_DEFAULT_SERVER_URL||p,token:e.token,username:e.username,machineId:e.machineId||u(),machineName:e.machineName||m.hostname(),defaultWorkspaceId:e.defaultWorkspaceId||"default",defaultProjectId:e.defaultProjectId,projects:Array.isArray(e.projects)?e.projects:[]}}function P(e={}){const t=f(d())??{},n={...o(),...e.machineId?{machineId:e.machineId}:{},...e.machineName?{machineName:e.machineName}:{}};return(!t.machineId||e.machineId||e.machineName)&&i(n),{machineId:n.machineId,machineName:n.machineName}}function i(e){s(d(),e)}function A(e){const t={...o(),...e};return i(t),t}function x(e){const t=o(),n=t.projects.filter(c=>c.projectId!==e.projectId&&c.name!==e.name);n.push(e);const r={...t,defaultWorkspaceId:e.workspaceId,defaultProjectId:e.projectId,projects:n};return i(r),r}function g(e){const t=o(),n=t.projects.filter(a=>a.projectId!==e&&a.name!==e),r=t.defaultProjectId===e||t.projects.find(a=>a.name===e)?.projectId===t.defaultProjectId?n[0]?.projectId:t.defaultProjectId,c={...t,defaultProjectId:r,projects:n};return i(c),c}function U(e){const t=o();return!e&&t.defaultProjectId?t.projects.find(n=>n.projectId===t.defaultProjectId)??null:e?t.projects.find(n=>n.projectId===e||n.name===e)??null:t.projects[0]??null}export{p as DEFAULT_ACA_SERVER_URL,P as ensureMachineIdentity,U as findLocalProject,o as loadAcaConfig,g as removeLocalProject,i as saveAcaConfig,A as updateAcaConfig,x as upsertLocalProject};
|
|
@@ -1,57 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { loadAcaConfig } from "./aca-config.js";
|
|
3
|
-
// 仅对 ACA Server 请求启用,避免使用 NODE_TLS_REJECT_UNAUTHORIZED=0 影响
|
|
4
|
-
// Codex、Mimo、OpenCode 以及其它外部 HTTPS 请求。
|
|
5
|
-
const insecureTlsDispatcher = new Agent({ connect: { rejectUnauthorized: false } });
|
|
6
|
-
export async function acaServerRequest(method, path, body, options = {}) {
|
|
7
|
-
const config = loadAcaConfig();
|
|
8
|
-
const serverUrl = (options.serverUrl || config.serverUrl).replace(/\/$/, "");
|
|
9
|
-
const token = options.token ?? config.token;
|
|
10
|
-
const headers = {
|
|
11
|
-
...(body === undefined ? {} : { "content-type": "application/json" }),
|
|
12
|
-
...(token ? { authorization: `Bearer ${token}` } : {})
|
|
13
|
-
};
|
|
14
|
-
const response = await fetch(`${serverUrl}${path}`, {
|
|
15
|
-
method,
|
|
16
|
-
headers,
|
|
17
|
-
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
18
|
-
...acaServerTlsInit(serverUrl)
|
|
19
|
-
});
|
|
20
|
-
const text = await response.text();
|
|
21
|
-
const value = text ? JSON.parse(text) : null;
|
|
22
|
-
if (!response.ok) {
|
|
23
|
-
const message = value && typeof value === "object" && "message" in value ? String(value.message) : `HTTP ${response.status}`;
|
|
24
|
-
throw new Error(message);
|
|
25
|
-
}
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
export async function acaServerStreamRequest(method, path, body, headers = {}, options = {}) {
|
|
29
|
-
const config = loadAcaConfig();
|
|
30
|
-
const serverUrl = (options.serverUrl || config.serverUrl).replace(/\/$/, "");
|
|
31
|
-
const token = options.token ?? config.token;
|
|
32
|
-
const response = await fetch(`${serverUrl}${path}`, {
|
|
33
|
-
method,
|
|
34
|
-
headers: {
|
|
35
|
-
...headers,
|
|
36
|
-
...(token ? { authorization: `Bearer ${token}` } : {})
|
|
37
|
-
},
|
|
38
|
-
body,
|
|
39
|
-
duplex: "half",
|
|
40
|
-
...acaServerTlsInit(serverUrl)
|
|
41
|
-
});
|
|
42
|
-
const text = await response.text();
|
|
43
|
-
const value = text ? JSON.parse(text) : null;
|
|
44
|
-
if (!response.ok) {
|
|
45
|
-
const message = value && typeof value === "object" && "message" in value ? String(value.message) : `HTTP ${response.status}`;
|
|
46
|
-
throw new Error(message);
|
|
47
|
-
}
|
|
48
|
-
return value;
|
|
49
|
-
}
|
|
50
|
-
function acaServerTlsInit(serverUrl) {
|
|
51
|
-
if (!/^https:/i.test(serverUrl) || !isTruthy(process.env.ACA_SERVER_INSECURE_TLS))
|
|
52
|
-
return {};
|
|
53
|
-
return { dispatcher: insecureTlsDispatcher };
|
|
54
|
-
}
|
|
55
|
-
function isTruthy(value) {
|
|
56
|
-
return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
|
|
57
|
-
}
|
|
1
|
+
import{Agent as g}from"undici";import{loadAcaConfig as p}from"./aca-config.js";const S=new g({connect:{rejectUnauthorized:!1}});async function $(t,f,n,i={}){const s=p(),o=(i.serverUrl||s.serverUrl).replace(/\/$/,""),a=i.token??s.token,u={...n===void 0?{}:{"content-type":"application/json"},...a?{authorization:`Bearer ${a}`}:{}},r=await fetch(`${o}${f}`,{method:t,headers:u,...n===void 0?{}:{body:JSON.stringify(n)},...h(o)}),c=await r.text(),e=c?JSON.parse(c):null;if(!r.ok){const l=e&&typeof e=="object"&&"message"in e?String(e.message):`HTTP ${r.status}`;throw new Error(l)}return e}async function d(t,f,n,i={},s={}){const o=p(),a=(s.serverUrl||o.serverUrl).replace(/\/$/,""),u=s.token??o.token,r=await fetch(`${a}${f}`,{method:t,headers:{...i,...u?{authorization:`Bearer ${u}`}:{}},body:n,duplex:"half",...h(a)}),c=await r.text(),e=c?JSON.parse(c):null;if(!r.ok){const l=e&&typeof e=="object"&&"message"in e?String(e.message):`HTTP ${r.status}`;throw new Error(l)}return e}function h(t){return!/^https:/i.test(t)||!m(process.env.ACA_SERVER_INSECURE_TLS)?{}:{dispatcher:S}}function m(t){return t==="1"||t?.toLowerCase()==="true"||t?.toLowerCase()==="yes"}export{$ as acaServerRequest,d as acaServerStreamRequest};
|