@dev-anywhere/proxy 0.1.6 → 0.1.8
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/{chunk-TX6HNHDB.js → chunk-2SBGSJLQ.js} +2 -2
- package/dist/{chunk-RFBTVZ2X.js → chunk-2XO3KLWW.js} +27 -338
- package/dist/chunk-2XO3KLWW.js.map +1 -0
- package/dist/chunk-BLWDLNT6.js +346 -0
- package/dist/chunk-BLWDLNT6.js.map +1 -0
- package/dist/chunk-GTTLWHIG.js +84 -0
- package/dist/chunk-GTTLWHIG.js.map +1 -0
- package/dist/{chunk-JGGDVMY5.js → chunk-ORZTFYXR.js} +2 -21
- package/dist/chunk-ORZTFYXR.js.map +1 -0
- package/dist/{chunk-ODK6N2NP.js → chunk-PDX6QFJ7.js} +2 -2
- package/dist/chunk-R4S6OFIZ.js +195 -0
- package/dist/chunk-R4S6OFIZ.js.map +1 -0
- package/dist/index.js +25 -13
- package/dist/index.js.map +1 -1
- package/dist/relay-token-KQPEQVP7.js +81 -0
- package/dist/relay-token-KQPEQVP7.js.map +1 -0
- package/dist/serve.js +54 -200
- package/dist/serve.js.map +1 -1
- package/dist/session-worker.js +3 -2
- package/dist/session-worker.js.map +1 -1
- package/dist/{terminal-ES6I5W32.js → terminal-4MDRBCL4.js} +13 -9
- package/dist/{terminal-ES6I5W32.js.map → terminal-4MDRBCL4.js.map} +1 -1
- package/package.json +3 -3
- package/dist/chunk-JGGDVMY5.js.map +0 -1
- package/dist/chunk-RFBTVZ2X.js.map +0 -1
- /package/dist/{chunk-TX6HNHDB.js.map → chunk-2SBGSJLQ.js.map} +0 -0
- /package/dist/{chunk-ODK6N2NP.js.map → chunk-PDX6QFJ7.js.map} +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
VALID_LOG_LEVELS,
|
|
4
|
+
loadProxyRuntimeEnv,
|
|
5
|
+
serviceLogger
|
|
6
|
+
} from "./chunk-GTTLWHIG.js";
|
|
7
|
+
import {
|
|
8
|
+
CONFIG_PATH,
|
|
9
|
+
PROFILE_NAME,
|
|
10
|
+
defaultHookPortForProfile
|
|
11
|
+
} from "./chunk-2XO3KLWW.js";
|
|
12
|
+
|
|
13
|
+
// src/common/config.ts
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
15
|
+
import { dirname, isAbsolute } from "path";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
var LogLevelSchema = z.enum(VALID_LOG_LEVELS);
|
|
18
|
+
var RelayTargetSchema = z.object({
|
|
19
|
+
url: z.string().optional(),
|
|
20
|
+
proxyToken: z.string().optional()
|
|
21
|
+
}).strict();
|
|
22
|
+
var ProxyProfileSchema = z.object({
|
|
23
|
+
relay: z.string().optional()
|
|
24
|
+
}).strict();
|
|
25
|
+
var AgentCliSchema = z.object({
|
|
26
|
+
claudeBin: z.string().optional(),
|
|
27
|
+
codexBin: z.string().optional(),
|
|
28
|
+
claudeBinHistory: z.array(z.string()).optional(),
|
|
29
|
+
codexBinHistory: z.array(z.string()).optional()
|
|
30
|
+
}).strict();
|
|
31
|
+
var ProxyConfigFileSchema = z.object({
|
|
32
|
+
defaultProfile: z.string().optional(),
|
|
33
|
+
profiles: z.record(z.string(), ProxyProfileSchema),
|
|
34
|
+
relays: z.record(z.string(), RelayTargetSchema),
|
|
35
|
+
agentCli: AgentCliSchema.optional(),
|
|
36
|
+
previewRoots: z.array(z.string()).optional(),
|
|
37
|
+
logLevel: LogLevelSchema.optional()
|
|
38
|
+
}).strict();
|
|
39
|
+
function readConfigFile() {
|
|
40
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
41
|
+
throw new Error(`Dev Anywhere config not found at ${CONFIG_PATH}. Run "dev-anywhere init".`);
|
|
42
|
+
}
|
|
43
|
+
let raw;
|
|
44
|
+
try {
|
|
45
|
+
raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
46
|
+
} catch (err) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`${CONFIG_PATH} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
49
|
+
{ cause: err }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const parsed = ProxyConfigFileSchema.safeParse(raw);
|
|
53
|
+
if (!parsed.success) {
|
|
54
|
+
const issues = parsed.error.issues.map(
|
|
55
|
+
(issue) => ` ${issue.path.length > 0 ? issue.path.join(".") : "(root)"}: ${issue.message}`
|
|
56
|
+
).join("\n");
|
|
57
|
+
throw new Error(`Invalid config at ${CONFIG_PATH}:
|
|
58
|
+
${issues}`);
|
|
59
|
+
}
|
|
60
|
+
return parsed.data;
|
|
61
|
+
}
|
|
62
|
+
function agentCliField(provider) {
|
|
63
|
+
return provider === "claude" ? "claudeBin" : "codexBin";
|
|
64
|
+
}
|
|
65
|
+
function agentCliHistoryField(provider) {
|
|
66
|
+
return provider === "claude" ? "claudeBinHistory" : "codexBinHistory";
|
|
67
|
+
}
|
|
68
|
+
function validateAgentCliPath(path) {
|
|
69
|
+
const normalized = path.trim();
|
|
70
|
+
if (!normalized) throw new Error("\u8BF7\u8F93\u5165 CLI \u8DEF\u5F84");
|
|
71
|
+
if (!isAbsolute(normalized)) throw new Error("CLI \u8DEF\u5F84\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");
|
|
72
|
+
return normalized;
|
|
73
|
+
}
|
|
74
|
+
function uniqueAbsolutePaths(paths) {
|
|
75
|
+
const seen = /* @__PURE__ */ new Set();
|
|
76
|
+
const result = [];
|
|
77
|
+
for (const path of paths) {
|
|
78
|
+
const normalized = path?.trim();
|
|
79
|
+
if (!normalized || !isAbsolute(normalized) || seen.has(normalized)) continue;
|
|
80
|
+
seen.add(normalized);
|
|
81
|
+
result.push(normalized);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
function resolveRelayConfig(fromFile, requestedRelayName) {
|
|
86
|
+
const profile = fromFile.profiles[PROFILE_NAME];
|
|
87
|
+
if (!profile) {
|
|
88
|
+
const available = Object.keys(fromFile.profiles).sort();
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Unknown profile "${PROFILE_NAME}". Available profiles: ${available.length > 0 ? available.join(", ") : "(none)"}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
const relayName = requestedRelayName?.trim() || profile.relay?.trim();
|
|
94
|
+
if (!relayName) {
|
|
95
|
+
throw new Error(`Profile "${PROFILE_NAME}" must specify a relay.`);
|
|
96
|
+
}
|
|
97
|
+
const relay = fromFile.relays[relayName];
|
|
98
|
+
if (!relay) {
|
|
99
|
+
const available = Object.keys(fromFile.relays).sort();
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Unknown relay "${relayName}". Available relays: ${available.length > 0 ? available.join(", ") : "(none)"}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
relayName,
|
|
106
|
+
relayNameSource: requestedRelayName?.trim() ? "cli" : "profile",
|
|
107
|
+
relay
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function loadConfig(options) {
|
|
111
|
+
const env = loadProxyRuntimeEnv();
|
|
112
|
+
const fromFile = readConfigFile();
|
|
113
|
+
const agentCli = fromFile.agentCli ?? {};
|
|
114
|
+
const resolved = resolveRelayConfig(fromFile, options?.relayName);
|
|
115
|
+
const claudeBin = env.claudeBin ?? agentCli.claudeBin;
|
|
116
|
+
const codexBin = env.codexBin ?? agentCli.codexBin;
|
|
117
|
+
const config = {
|
|
118
|
+
profileName: PROFILE_NAME,
|
|
119
|
+
relayName: resolved.relayName,
|
|
120
|
+
relayUrl: env.relayUrl ?? resolved.relay.url,
|
|
121
|
+
relayToken: env.relayProxyToken ?? resolved.relay.proxyToken,
|
|
122
|
+
hookPort: env.hookPort ?? defaultHookPortForProfile(PROFILE_NAME),
|
|
123
|
+
claudeBin,
|
|
124
|
+
codexBin,
|
|
125
|
+
previewRoots: uniqueAbsolutePaths(fromFile.previewRoots ?? []),
|
|
126
|
+
agentCliSuggestions: {
|
|
127
|
+
claude: uniqueAbsolutePaths([
|
|
128
|
+
env.claudeBin,
|
|
129
|
+
agentCli.claudeBin,
|
|
130
|
+
...agentCli.claudeBinHistory ?? []
|
|
131
|
+
]),
|
|
132
|
+
codex: uniqueAbsolutePaths([
|
|
133
|
+
env.codexBin,
|
|
134
|
+
agentCli.codexBin,
|
|
135
|
+
...agentCli.codexBinHistory ?? []
|
|
136
|
+
])
|
|
137
|
+
},
|
|
138
|
+
sources: {
|
|
139
|
+
relayName: resolved.relayNameSource,
|
|
140
|
+
relayUrl: env.relayUrl ? "env" : resolved.relay.url ? "file" : "none",
|
|
141
|
+
relayToken: env.relayProxyToken ? "env" : resolved.relay.proxyToken ? "file" : "none",
|
|
142
|
+
hookPort: env.hookPort !== void 0 ? "env" : "default",
|
|
143
|
+
claudeBin: env.claudeBin ? "env" : agentCli.claudeBin ? "file" : "none",
|
|
144
|
+
codexBin: env.codexBin ? "env" : agentCli.codexBin ? "file" : "none"
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
serviceLogger.info(
|
|
148
|
+
{
|
|
149
|
+
profile: config.profileName,
|
|
150
|
+
relayName: config.relayName,
|
|
151
|
+
relayNameSource: config.sources.relayName,
|
|
152
|
+
relayUrl: config.relayUrl ?? "(unset)",
|
|
153
|
+
relayUrlSource: config.sources.relayUrl,
|
|
154
|
+
relayTokenSource: config.sources.relayToken,
|
|
155
|
+
hookPort: config.hookPort,
|
|
156
|
+
hookPortSource: config.sources.hookPort,
|
|
157
|
+
claudeBinSource: config.sources.claudeBin,
|
|
158
|
+
codexBinSource: config.sources.codexBin
|
|
159
|
+
},
|
|
160
|
+
"Config loaded"
|
|
161
|
+
);
|
|
162
|
+
return config;
|
|
163
|
+
}
|
|
164
|
+
function buildProviderEnv(config, baseEnv = process.env) {
|
|
165
|
+
return {
|
|
166
|
+
...baseEnv,
|
|
167
|
+
...config.claudeBin ? { CLAUDE_BIN: config.claudeBin } : {},
|
|
168
|
+
...config.codexBin ? { CODEX_BIN: config.codexBin } : {}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function updateAgentCliConfig(config, provider, path) {
|
|
172
|
+
const field = agentCliField(provider);
|
|
173
|
+
const historyField = agentCliHistoryField(provider);
|
|
174
|
+
const history = uniqueAbsolutePaths([path, ...config[historyField] ?? []]).slice(0, 8);
|
|
175
|
+
return {
|
|
176
|
+
...config,
|
|
177
|
+
[field]: path,
|
|
178
|
+
[historyField]: history
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function saveAgentCliPath(provider, path) {
|
|
182
|
+
const normalized = validateAgentCliPath(path);
|
|
183
|
+
const fromFile = readConfigFile();
|
|
184
|
+
fromFile.agentCli = updateAgentCliConfig(fromFile.agentCli ?? {}, provider, normalized);
|
|
185
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
186
|
+
writeFileSync(CONFIG_PATH, `${JSON.stringify(fromFile, null, 2)}
|
|
187
|
+
`, "utf-8");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export {
|
|
191
|
+
loadConfig,
|
|
192
|
+
buildProviderEnv,
|
|
193
|
+
saveAgentCliPath
|
|
194
|
+
};
|
|
195
|
+
//# sourceMappingURL=chunk-R4S6OFIZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/common/config.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute } from \"node:path\";\nimport { z } from \"zod\";\nimport { CONFIG_PATH, PROFILE_NAME, defaultHookPortForProfile } from \"./paths.js\";\nimport { serviceLogger } from \"./logger.js\";\nimport { VALID_LOG_LEVELS, loadProxyRuntimeEnv } from \"./runtime-env.js\";\nimport type { ProviderId } from \"../providers/types.js\";\n\nexport type { LogLevel } from \"./runtime-env.js\";\n\nexport interface ProxyConfig {\n profileName: string;\n relayName: string;\n relayUrl?: string;\n // /proxy 端点的预共享 token, 和 relay 侧 RELAY_PROXY_TOKEN 对应. 公网 relay 必须设置\n relayToken?: string;\n hookPort?: number;\n claudeBin?: string;\n codexBin?: string;\n previewRoots: string[];\n agentCliSuggestions: Record<ProviderId, string[]>;\n sources: {\n relayName: \"cli\" | \"profile\";\n relayUrl: \"env\" | \"file\" | \"none\";\n relayToken: \"env\" | \"file\" | \"none\";\n hookPort: \"env\" | \"default\";\n claudeBin: \"env\" | \"file\" | \"none\";\n codexBin: \"env\" | \"file\" | \"none\";\n };\n}\n\nconst LogLevelSchema = z.enum(VALID_LOG_LEVELS);\n// LogLevel 由 runtime-env.ts 定义并 re-export,schema 用同一组字面量保证两边对齐。\n\nconst RelayTargetSchema = z\n .object({\n url: z.string().optional(),\n proxyToken: z.string().optional(),\n })\n .strict();\n\nconst ProxyProfileSchema = z\n .object({\n relay: z.string().optional(),\n })\n .strict();\n\nconst AgentCliSchema = z\n .object({\n claudeBin: z.string().optional(),\n codexBin: z.string().optional(),\n claudeBinHistory: z.array(z.string()).optional(),\n codexBinHistory: z.array(z.string()).optional(),\n })\n .strict();\n\n// .strict() 在顶层捕获拼错的字段(\"relayss\" / \"profile\"),但 profiles/relays 内部\n// 是 record(用户定义键),不限制键名。\nconst ProxyConfigFileSchema = z\n .object({\n defaultProfile: z.string().optional(),\n profiles: z.record(z.string(), ProxyProfileSchema),\n relays: z.record(z.string(), RelayTargetSchema),\n agentCli: AgentCliSchema.optional(),\n previewRoots: z.array(z.string()).optional(),\n logLevel: LogLevelSchema.optional(),\n })\n .strict();\n\ntype ProxyConfigFile = z.infer<typeof ProxyConfigFileSchema>;\ntype RelayTargetConfig = z.infer<typeof RelayTargetSchema>;\ntype AgentCliConfig = z.infer<typeof AgentCliSchema>;\n\nfunction readConfigFile(): ProxyConfigFile {\n if (!existsSync(CONFIG_PATH)) {\n throw new Error(`Dev Anywhere config not found at ${CONFIG_PATH}. Run \"dev-anywhere init\".`);\n }\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(CONFIG_PATH, \"utf-8\"));\n } catch (err) {\n throw new Error(\n `${CONFIG_PATH} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n const parsed = ProxyConfigFileSchema.safeParse(raw);\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map(\n (issue) => ` ${issue.path.length > 0 ? issue.path.join(\".\") : \"(root)\"}: ${issue.message}`,\n )\n .join(\"\\n\");\n throw new Error(`Invalid config at ${CONFIG_PATH}:\\n${issues}`);\n }\n return parsed.data;\n}\n\nfunction agentCliField(provider: ProviderId): \"claudeBin\" | \"codexBin\" {\n return provider === \"claude\" ? \"claudeBin\" : \"codexBin\";\n}\n\nfunction agentCliHistoryField(provider: ProviderId): \"claudeBinHistory\" | \"codexBinHistory\" {\n return provider === \"claude\" ? \"claudeBinHistory\" : \"codexBinHistory\";\n}\n\nfunction validateAgentCliPath(path: string): string {\n const normalized = path.trim();\n if (!normalized) throw new Error(\"请输入 CLI 路径\");\n if (!isAbsolute(normalized)) throw new Error(\"CLI 路径必须是绝对路径\");\n return normalized;\n}\n\nfunction uniqueAbsolutePaths(paths: Array<string | undefined>): string[] {\n const seen = new Set<string>();\n const result: string[] = [];\n for (const path of paths) {\n const normalized = path?.trim();\n if (!normalized || !isAbsolute(normalized) || seen.has(normalized)) continue;\n seen.add(normalized);\n result.push(normalized);\n }\n return result;\n}\n\nfunction resolveRelayConfig(\n fromFile: ProxyConfigFile,\n requestedRelayName?: string,\n): {\n relayName: string;\n relayNameSource: ProxyConfig[\"sources\"][\"relayName\"];\n relay: RelayTargetConfig;\n} {\n const profile = fromFile.profiles[PROFILE_NAME];\n if (!profile) {\n const available = Object.keys(fromFile.profiles).sort();\n throw new Error(\n `Unknown profile \"${PROFILE_NAME}\". Available profiles: ${available.length > 0 ? available.join(\", \") : \"(none)\"}`,\n );\n }\n\n const relayName = requestedRelayName?.trim() || profile.relay?.trim();\n if (!relayName) {\n throw new Error(`Profile \"${PROFILE_NAME}\" must specify a relay.`);\n }\n\n const relay = fromFile.relays[relayName];\n if (!relay) {\n const available = Object.keys(fromFile.relays).sort();\n throw new Error(\n `Unknown relay \"${relayName}\". Available relays: ${available.length > 0 ? available.join(\", \") : \"(none)\"}`,\n );\n }\n\n return {\n relayName,\n relayNameSource: requestedRelayName?.trim() ? \"cli\" : \"profile\",\n relay,\n };\n}\n\nexport function loadConfig(options?: { relayName?: string }): ProxyConfig {\n const env = loadProxyRuntimeEnv();\n const fromFile = readConfigFile();\n const agentCli = fromFile.agentCli ?? {};\n const resolved = resolveRelayConfig(fromFile, options?.relayName);\n const claudeBin = env.claudeBin ?? agentCli.claudeBin;\n const codexBin = env.codexBin ?? agentCli.codexBin;\n const config: ProxyConfig = {\n profileName: PROFILE_NAME,\n relayName: resolved.relayName,\n relayUrl: env.relayUrl ?? resolved.relay.url,\n relayToken: env.relayProxyToken ?? resolved.relay.proxyToken,\n hookPort: env.hookPort ?? defaultHookPortForProfile(PROFILE_NAME),\n claudeBin,\n codexBin,\n previewRoots: uniqueAbsolutePaths(fromFile.previewRoots ?? []),\n agentCliSuggestions: {\n claude: uniqueAbsolutePaths([\n env.claudeBin,\n agentCli.claudeBin,\n ...(agentCli.claudeBinHistory ?? []),\n ]),\n codex: uniqueAbsolutePaths([\n env.codexBin,\n agentCli.codexBin,\n ...(agentCli.codexBinHistory ?? []),\n ]),\n },\n sources: {\n relayName: resolved.relayNameSource,\n relayUrl: env.relayUrl ? \"env\" : resolved.relay.url ? \"file\" : \"none\",\n relayToken: env.relayProxyToken ? \"env\" : resolved.relay.proxyToken ? \"file\" : \"none\",\n hookPort: env.hookPort !== undefined ? \"env\" : \"default\",\n claudeBin: env.claudeBin ? \"env\" : agentCli.claudeBin ? \"file\" : \"none\",\n codexBin: env.codexBin ? \"env\" : agentCli.codexBin ? \"file\" : \"none\",\n },\n };\n\n serviceLogger.info(\n {\n profile: config.profileName,\n relayName: config.relayName,\n relayNameSource: config.sources.relayName,\n relayUrl: config.relayUrl ?? \"(unset)\",\n relayUrlSource: config.sources.relayUrl,\n relayTokenSource: config.sources.relayToken,\n hookPort: config.hookPort,\n hookPortSource: config.sources.hookPort,\n claudeBinSource: config.sources.claudeBin,\n codexBinSource: config.sources.codexBin,\n },\n \"Config loaded\",\n );\n\n return config;\n}\n\nexport function buildProviderEnv(\n config: ProxyConfig,\n baseEnv: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n return {\n ...baseEnv,\n ...(config.claudeBin ? { CLAUDE_BIN: config.claudeBin } : {}),\n ...(config.codexBin ? { CODEX_BIN: config.codexBin } : {}),\n };\n}\n\nfunction updateAgentCliConfig(\n config: AgentCliConfig,\n provider: ProviderId,\n path: string,\n): AgentCliConfig {\n const field = agentCliField(provider);\n const historyField = agentCliHistoryField(provider);\n const history = uniqueAbsolutePaths([path, ...(config[historyField] ?? [])]).slice(0, 8);\n return {\n ...config,\n [field]: path,\n [historyField]: history,\n };\n}\n\nexport function saveAgentCliPath(provider: ProviderId, path: string): void {\n const normalized = validateAgentCliPath(path);\n const fromFile = readConfigFile();\n fromFile.agentCli = updateAgentCliConfig(fromFile.agentCli ?? {}, provider, normalized);\n mkdirSync(dirname(CONFIG_PATH), { recursive: true });\n writeFileSync(CONFIG_PATH, `${JSON.stringify(fromFile, null, 2)}\\n`, \"utf-8\");\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
daemonRelayArgs,
|
|
4
4
|
setDesiredDaemonRelay
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-2SBGSJLQ.js";
|
|
6
6
|
import {
|
|
7
7
|
spawnScript
|
|
8
8
|
} from "./chunk-ZUWAB67J.js";
|
|
9
|
+
import {
|
|
10
|
+
createIpcReader,
|
|
11
|
+
serializeIpc
|
|
12
|
+
} from "./chunk-BLWDLNT6.js";
|
|
9
13
|
import {
|
|
10
14
|
CONFIG_PATH,
|
|
11
15
|
PID_PATH,
|
|
@@ -13,12 +17,10 @@ import {
|
|
|
13
17
|
SERVICE_LOG_PATH,
|
|
14
18
|
SOCK_PATH,
|
|
15
19
|
STOPPED_PATH,
|
|
16
|
-
createIpcReader,
|
|
17
20
|
ensureProfileWorkspace,
|
|
18
21
|
initWorkspace,
|
|
19
|
-
isInitialized
|
|
20
|
-
|
|
21
|
-
} from "./chunk-RFBTVZ2X.js";
|
|
22
|
+
isInitialized
|
|
23
|
+
} from "./chunk-2XO3KLWW.js";
|
|
22
24
|
|
|
23
25
|
// src/index.ts
|
|
24
26
|
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -127,17 +129,17 @@ function showStatus() {
|
|
|
127
129
|
log(`Daemon: profile ${config.profile ?? PROFILE_NAME}`);
|
|
128
130
|
log(`Relay: ${config.relayName} (${config.relayNameSource})`);
|
|
129
131
|
log(`Config: relay ${config.relayUrl ?? "(unset)"} (${config.relayUrlSource})`);
|
|
130
|
-
const
|
|
131
|
-
if (!
|
|
132
|
+
const relay2 = msg.relay;
|
|
133
|
+
if (!relay2) {
|
|
132
134
|
log("Relay: not configured");
|
|
133
|
-
} else if (
|
|
134
|
-
log(`Relay: connected (proxy: ${
|
|
135
|
+
} else if (relay2.connected) {
|
|
136
|
+
log(`Relay: connected (proxy: ${relay2.proxyId})`);
|
|
135
137
|
log(
|
|
136
|
-
` queue depth: ${
|
|
138
|
+
` queue depth: ${relay2.queueDepth}, reconnect attempts: ${relay2.reconnectAttempt}`
|
|
137
139
|
);
|
|
138
140
|
} else {
|
|
139
141
|
log(
|
|
140
|
-
`Relay: disconnected (proxy: ${
|
|
142
|
+
`Relay: disconnected (proxy: ${relay2.proxyId}, reconnecting: attempt ${relay2.reconnectAttempt}, queued: ${relay2.queueDepth})`
|
|
141
143
|
);
|
|
142
144
|
}
|
|
143
145
|
log("");
|
|
@@ -232,12 +234,11 @@ async function startDaemon(options) {
|
|
|
232
234
|
}
|
|
233
235
|
process.exit(1);
|
|
234
236
|
}
|
|
235
|
-
var program = new Command("dev-anywhere").description("Dev Anywhere - transparent local AI CLI proxy with remote control").version(pkg.version).option("--profile <name>", "Use an isolated local proxy profile").allowUnknownOption().allowExcessArguments().action(async () => {
|
|
237
|
+
var program = new Command("dev-anywhere").description("Dev Anywhere - transparent local AI CLI proxy with remote control").version(pkg.version, "-v, --version").option("--profile <name>", "Use an isolated local proxy profile").allowUnknownOption().allowExcessArguments().action(async () => {
|
|
236
238
|
if (!isInitialized()) {
|
|
237
239
|
console.error(`Dev Anywhere is not initialized. Run "dev-anywhere init" first.`);
|
|
238
240
|
process.exit(1);
|
|
239
241
|
}
|
|
240
|
-
const { startTerminal } = await import("./terminal-ES6I5W32.js");
|
|
241
242
|
let invocation;
|
|
242
243
|
try {
|
|
243
244
|
invocation = extractAgentInvocation(cliArgsWithoutProfile);
|
|
@@ -245,6 +246,7 @@ var program = new Command("dev-anywhere").description("Dev Anywhere - transparen
|
|
|
245
246
|
console.error(err instanceof Error ? err.message : String(err));
|
|
246
247
|
process.exit(1);
|
|
247
248
|
}
|
|
249
|
+
const { startTerminal } = await import("./terminal-4MDRBCL4.js");
|
|
248
250
|
const { provider, args } = invocation;
|
|
249
251
|
await startTerminal(args, provider);
|
|
250
252
|
});
|
|
@@ -292,6 +294,16 @@ serve.command("restart").description("Restart the background service").option("-
|
|
|
292
294
|
await startDaemon({ relayName: opts.relay });
|
|
293
295
|
});
|
|
294
296
|
program.addCommand(serve);
|
|
297
|
+
var relay = new Command("relay").description("Inspect and manage relay configuration");
|
|
298
|
+
relay.command("token").description("Print the relay's current client token (auth: proxy token)").option("--relay <name>", "Use a named relay from config").action(async (opts) => {
|
|
299
|
+
if (!isInitialized()) {
|
|
300
|
+
console.error(`Dev Anywhere is not initialized. Run "dev-anywhere init" first.`);
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
const { runRelayTokenCommand } = await import("./relay-token-KQPEQVP7.js");
|
|
304
|
+
await runRelayTokenCommand({ relayName: opts.relay });
|
|
305
|
+
});
|
|
306
|
+
program.addCommand(relay);
|
|
295
307
|
program.command("init").description("Initialize dev-anywhere workspace (~/.dev-anywhere)").action(() => {
|
|
296
308
|
if (isInitialized()) {
|
|
297
309
|
console.log(`Already initialized. Config at ${CONFIG_PATH}`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/cli-args.ts"],"sourcesContent":["import { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { connect } from \"node:net\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\nimport { Command } from \"commander\";\nimport {\n PID_PATH,\n SOCK_PATH,\n STOPPED_PATH,\n SERVICE_LOG_PATH,\n CONFIG_PATH,\n PROFILE_NAME,\n ensureProfileWorkspace,\n isInitialized,\n initWorkspace,\n} from \"./common/paths.js\";\nimport { spawnScript } from \"./common/env.js\";\nimport { daemonRelayArgs, setDesiredDaemonRelay } from \"./common/daemon-env.js\";\nimport { createIpcReader, serializeIpc } from \"./ipc/ipc-protocol.js\";\nimport { extractAgentInvocation, normalizeCliArgs, stripProxyProfileArgs } from \"./cli-args.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\")) as {\n version: string;\n};\n\nfunction stopService(): boolean {\n if (!existsSync(PID_PATH)) {\n console.error(\"Service is not running (no PID file)\");\n return false;\n }\n const pid = parseInt(readFileSync(PID_PATH, \"utf-8\").trim(), 10);\n try {\n process.kill(pid, \"SIGTERM\");\n console.log(`Service stopped (PID ${pid})`);\n } catch {\n console.error(`Process ${pid} not found, cleaning up stale files`);\n }\n if (existsSync(PID_PATH)) unlinkSync(PID_PATH);\n if (existsSync(SOCK_PATH)) unlinkSync(SOCK_PATH);\n writeFileSync(STOPPED_PATH, String(Date.now()));\n return true;\n}\n\nfunction showStatus(): Promise<number> {\n return new Promise((resolve) => {\n let lines = 0;\n const log = (s: string) => {\n console.log(s);\n lines++;\n };\n\n if (!existsSync(PID_PATH)) {\n log(`Profile: ${PROFILE_NAME}`);\n log(\"Service: not running\");\n resolve(lines);\n return;\n }\n const pid = parseInt(readFileSync(PID_PATH, \"utf-8\").trim(), 10);\n let alive = false;\n try {\n process.kill(pid, 0);\n alive = true;\n } catch {\n // process.kill(pid, 0) 抛错表示进程不存在\n }\n\n if (!alive) {\n log(\"Service: dead (stale PID file)\");\n resolve(lines);\n return;\n }\n\n log(`Profile: ${PROFILE_NAME}`);\n log(`Service: running (PID ${pid})`);\n log(`Socket: ${SOCK_PATH}`);\n log(`Log: ${SERVICE_LOG_PATH}`);\n\n const sock = connect(SOCK_PATH);\n sock.on(\"error\", () => {\n log(\"Sessions: unable to connect\");\n sock.destroy();\n resolve(lines);\n });\n sock.on(\"connect\", () => {\n createIpcReader(sock, (msg) => {\n if (msg.type === \"service_status_response\") {\n const config = msg.config;\n log(`Daemon: profile ${config.profile ?? PROFILE_NAME}`);\n log(`Relay: ${config.relayName} (${config.relayNameSource})`);\n log(`Config: relay ${config.relayUrl ?? \"(unset)\"} (${config.relayUrlSource})`);\n const relay = msg.relay;\n if (!relay) {\n log(\"Relay: not configured\");\n } else if (relay.connected) {\n log(`Relay: connected (proxy: ${relay.proxyId})`);\n log(\n ` queue depth: ${relay.queueDepth}, reconnect attempts: ${relay.reconnectAttempt}`,\n );\n } else {\n log(\n `Relay: disconnected (proxy: ${relay.proxyId}, reconnecting: attempt ${relay.reconnectAttempt}, queued: ${relay.queueDepth})`,\n );\n }\n log(\"\");\n\n // 显示会话列表\n const sessions = msg.sessions;\n if (sessions.length === 0) {\n log(\"Sessions: none\");\n } else {\n log(`Sessions: ${sessions.length}`);\n for (const s of sessions) {\n log(` ${s.id} ${s.mode} ${s.state} worker: ${s.hasWorker ? \"yes\" : \"no\"}`);\n }\n }\n sock.destroy();\n resolve(lines);\n }\n });\n sock.write(serializeIpc({ type: \"service_status_request\" }));\n });\n });\n}\n\nconst DAEMON_STARTUP_TIMEOUT_MS = 30_000;\nconst DAEMON_STARTUP_POLL_MS = 200;\n\n// 轮询 SOCK_PATH 直到可连接,作为 serve 的 readiness 信号。\n// serve.ts 里 server.listen(SOCK_PATH) 是启动序列的最后一步,连上即代表 ready。\nasync function waitForServeReady(timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const connected = await new Promise<boolean>((resolve) => {\n const sock = connect(SOCK_PATH);\n sock.once(\"connect\", () => {\n sock.destroy();\n resolve(true);\n });\n sock.once(\"error\", () => resolve(false));\n });\n if (connected) return true;\n await sleep(DAEMON_STARTUP_POLL_MS);\n }\n return false;\n}\n\nasync function startDaemon(options?: { relayName?: string }): Promise<void> {\n ensureProfileWorkspace();\n if (existsSync(PID_PATH)) {\n const pid = parseInt(readFileSync(PID_PATH, \"utf-8\").trim(), 10);\n try {\n process.kill(pid, 0);\n console.error(`Service is already running (PID ${pid})`);\n return;\n } catch {\n // process.kill(pid, 0) 抛错表示进程不存在,继续启动\n }\n }\n if (existsSync(STOPPED_PATH)) unlinkSync(STOPPED_PATH);\n\n // stderr 走 pipe 由父 CLI 订阅:子进程 ready 前(pino logger 未接管)的启动错误\n // 会被捕获;ready 后父 detach,pino 接管所有输出到 service.log。\n // start 命令必须等 daemon socket 可连接后再退出;否则用户会看到“启动成功”,实际服务还没就绪。\n const serveArgs = [\"--profile\", PROFILE_NAME, ...daemonRelayArgs(options?.relayName)];\n const child = spawnScript(new URL(\"./serve\", import.meta.url), serveArgs, {\n env: { ...process.env },\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n unref: false,\n });\n\n const stderrChunks: Buffer[] = [];\n child.stderr!.on(\"data\", (chunk: Buffer) => {\n stderrChunks.push(chunk);\n });\n\n // race: readiness handshake vs. 子进程先挂。子进程 ready 前就 exit 说明启动硬失败,\n // 不必再等到 30s 超时才报错。\n type Outcome =\n | { kind: \"ready\" }\n | { kind: \"timeout\" }\n | { kind: \"exited\"; code: number | null; signal: NodeJS.Signals | null };\n\n const readyOutcome: Promise<Outcome> = waitForServeReady(DAEMON_STARTUP_TIMEOUT_MS).then((ok) =>\n ok ? { kind: \"ready\" as const } : { kind: \"timeout\" as const },\n );\n const exitOutcome: Promise<Outcome> = new Promise((resolve) => {\n // 设 listener 前已经 exit 的边界:Node 记在 exitCode 上\n if (child.exitCode !== null) {\n resolve({ kind: \"exited\", code: child.exitCode, signal: child.signalCode });\n return;\n }\n child.once(\"exit\", (code, signal) => resolve({ kind: \"exited\", code, signal }));\n });\n\n const result = await Promise.race([readyOutcome, exitOutcome]);\n\n if (result.kind === \"ready\") {\n console.log(`Service started in background (PID ${child.pid})`);\n // ready 后 detach:摘 stderr 订阅 + destroy pipe + unref 子进程。\n // 单独 child.unref() 不够,父侧的 stderr pipe fd 还在事件循环里会让父 CLI 永不退出;\n // 必须 destroy 掉 pipe 才能真正释放 refcount。pino 已接管子进程的输出到 service.log。\n child.stderr!.removeAllListeners(\"data\");\n child.stderr!.destroy();\n child.unref();\n return;\n }\n\n // 失败路径:timeout 或 exited\n const stderrOutput = Buffer.concat(stderrChunks).toString(\"utf-8\").trim();\n if (result.kind === \"exited\") {\n console.error(`Service exited during startup (code=${result.code}, signal=${result.signal}).`);\n } else {\n console.error(`Service failed to become ready within ${DAEMON_STARTUP_TIMEOUT_MS / 1000}s.`);\n try {\n process.kill(child.pid!, \"SIGTERM\");\n } catch {\n // 子进程可能已自己退出,kill 失败不影响后续退出码\n }\n }\n if (stderrOutput) {\n console.error(\"--- child stderr ---\");\n console.error(stderrOutput);\n }\n process.exit(1);\n}\n\nconst program = new Command(\"dev-anywhere\")\n .description(\"Dev Anywhere - transparent local AI CLI proxy with remote control\")\n .version(pkg.version)\n .option(\"--profile <name>\", \"Use an isolated local proxy profile\")\n .allowUnknownOption()\n .allowExcessArguments()\n .action(async () => {\n if (!isInitialized()) {\n console.error(`Dev Anywhere is not initialized. Run \"dev-anywhere init\" first.`);\n process.exit(1);\n }\n // 延迟导入 terminal: CLI 的其他子命令(init/stop/status)不需要 PTY + xterm 相关依赖,\n // tsup 基于 dynamic import 自动代码分裂,避免所有命令都为 terminal 付出 14KB 额外启动成本。\n const { startTerminal } = await import(\"./terminal.js\");\n let invocation: ReturnType<typeof extractAgentInvocation>;\n try {\n invocation = extractAgentInvocation(cliArgsWithoutProfile);\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n const { provider, args } = invocation;\n await startTerminal(args, provider);\n });\n\n// serve 子命令组\nconst serve = new Command(\"serve\")\n .description(\"Manage the dev-anywhere background service\")\n .option(\"--profile <name>\", \"Use an isolated local proxy profile\")\n .option(\"-d, --daemon\", \"Run in background\")\n .action(async (opts) => {\n if (!isInitialized()) {\n console.error(`Dev Anywhere is not initialized. Run \"dev-anywhere init\" first.`);\n process.exit(1);\n }\n if (opts.daemon) {\n setDesiredDaemonRelay(undefined);\n await startDaemon();\n } else {\n // 延迟导入 serve: daemon 模式只需要 startDaemon(纯 spawn),不需要加载 70KB 的 serve bundle\n const { startService } = await import(\"./serve.js\");\n await startService();\n }\n });\n\nserve\n .command(\"start\")\n .description(\"Start the background service\")\n .option(\"--relay <name>\", \"Use a named relay from config\")\n .action(async (opts) => {\n if (!isInitialized()) {\n console.error(`Dev Anywhere is not initialized. Run \"dev-anywhere init\" first.`);\n process.exit(1);\n }\n setDesiredDaemonRelay(opts.relay);\n await startDaemon({ relayName: opts.relay });\n });\n\nserve\n .command(\"status\")\n .description(\"Show service status and active sessions\")\n .option(\"-w, --watch\", \"Continuous monitoring mode\")\n .option(\"-n, --interval <seconds>\", \"Refresh interval in seconds\", \"2\")\n .action(async (opts) => {\n if (opts.watch) {\n const intervalMs = Number(opts.interval) * 1000;\n let lastLines = await showStatus();\n setInterval(async () => {\n if (lastLines > 0) {\n process.stdout.write(`\\x1B[${lastLines}A\\x1B[J`);\n }\n lastLines = await showStatus();\n }, intervalMs);\n } else {\n await showStatus();\n }\n });\n\nserve\n .command(\"stop\")\n .description(\"Stop the background service\")\n .action(() => {\n stopService();\n });\n\nserve\n .command(\"restart\")\n .description(\"Restart the background service\")\n .option(\"--relay <name>\", \"Use a named relay from config\")\n .action(async (opts) => {\n setDesiredDaemonRelay(opts.relay);\n stopService();\n await startDaemon({ relayName: opts.relay });\n });\n\nprogram.addCommand(serve);\n\nprogram\n .command(\"init\")\n .description(\"Initialize dev-anywhere workspace (~/.dev-anywhere)\")\n .action(() => {\n if (isInitialized()) {\n console.log(`Already initialized. Config at ${CONFIG_PATH}`);\n return;\n }\n initWorkspace();\n console.log(\"Initialized ~/.dev-anywhere/\");\n console.log(`Edit ${CONFIG_PATH} to configure relay server URL.`);\n });\n\n// pnpm run dev -- args 会在参数前插入 \"--\"。根脚本和用户命令都可能再加一层\n// 分隔符,所以这里过滤所有前导分隔符,再交给 Commander 和 provider 参数解析。\nconst cliArgs = normalizeCliArgs(process.argv.slice(2));\nconst cliArgsWithoutProfile = stripProxyProfileArgs(cliArgs);\n\nprogram.parse(cliArgsWithoutProfile, { from: \"user\" });\n","import type { ProviderId } from \"./providers/index.js\";\n\nexport function normalizeCliArgs(args: string[]): string[] {\n const normalized = [...args];\n while (normalized[0] === \"--\") {\n normalized.shift();\n }\n return normalized;\n}\n\nexport function stripProxyProfileArgs(args: string[]): string[] {\n const result: string[] = [];\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"claude\" || arg === \"codex\") {\n result.push(...args.slice(i));\n break;\n }\n if (arg === \"--profile\") {\n i++;\n continue;\n }\n if (arg.startsWith(\"--profile=\")) {\n continue;\n }\n result.push(arg);\n }\n return result;\n}\n\nexport function extractAgentInvocation(args: string[]): { provider: ProviderId; args: string[] } {\n const [agent, ...providerArgs] = args;\n if (agent !== \"claude\" && agent !== \"codex\") {\n throw new Error(\n 'Missing Agent CLI. Use \"dev-anywhere claude ...\" or \"dev-anywhere codex ...\".',\n );\n }\n return { provider: agent, args: providerArgs };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/cli-args.ts"],"sourcesContent":["import { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { connect } from \"node:net\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\nimport { Command } from \"commander\";\nimport {\n PID_PATH,\n SOCK_PATH,\n STOPPED_PATH,\n SERVICE_LOG_PATH,\n CONFIG_PATH,\n PROFILE_NAME,\n ensureProfileWorkspace,\n isInitialized,\n initWorkspace,\n} from \"./common/paths.js\";\nimport { spawnScript } from \"./common/env.js\";\nimport { daemonRelayArgs, setDesiredDaemonRelay } from \"./common/daemon-env.js\";\nimport { createIpcReader, serializeIpc } from \"./ipc/ipc-protocol.js\";\nimport { extractAgentInvocation, normalizeCliArgs, stripProxyProfileArgs } from \"./cli-args.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\")) as {\n version: string;\n};\n\nfunction stopService(): boolean {\n if (!existsSync(PID_PATH)) {\n console.error(\"Service is not running (no PID file)\");\n return false;\n }\n const pid = parseInt(readFileSync(PID_PATH, \"utf-8\").trim(), 10);\n try {\n process.kill(pid, \"SIGTERM\");\n console.log(`Service stopped (PID ${pid})`);\n } catch {\n console.error(`Process ${pid} not found, cleaning up stale files`);\n }\n if (existsSync(PID_PATH)) unlinkSync(PID_PATH);\n if (existsSync(SOCK_PATH)) unlinkSync(SOCK_PATH);\n writeFileSync(STOPPED_PATH, String(Date.now()));\n return true;\n}\n\nfunction showStatus(): Promise<number> {\n return new Promise((resolve) => {\n let lines = 0;\n const log = (s: string) => {\n console.log(s);\n lines++;\n };\n\n if (!existsSync(PID_PATH)) {\n log(`Profile: ${PROFILE_NAME}`);\n log(\"Service: not running\");\n resolve(lines);\n return;\n }\n const pid = parseInt(readFileSync(PID_PATH, \"utf-8\").trim(), 10);\n let alive = false;\n try {\n process.kill(pid, 0);\n alive = true;\n } catch {\n // process.kill(pid, 0) 抛错表示进程不存在\n }\n\n if (!alive) {\n log(\"Service: dead (stale PID file)\");\n resolve(lines);\n return;\n }\n\n log(`Profile: ${PROFILE_NAME}`);\n log(`Service: running (PID ${pid})`);\n log(`Socket: ${SOCK_PATH}`);\n log(`Log: ${SERVICE_LOG_PATH}`);\n\n const sock = connect(SOCK_PATH);\n sock.on(\"error\", () => {\n log(\"Sessions: unable to connect\");\n sock.destroy();\n resolve(lines);\n });\n sock.on(\"connect\", () => {\n createIpcReader(sock, (msg) => {\n if (msg.type === \"service_status_response\") {\n const config = msg.config;\n log(`Daemon: profile ${config.profile ?? PROFILE_NAME}`);\n log(`Relay: ${config.relayName} (${config.relayNameSource})`);\n log(`Config: relay ${config.relayUrl ?? \"(unset)\"} (${config.relayUrlSource})`);\n const relay = msg.relay;\n if (!relay) {\n log(\"Relay: not configured\");\n } else if (relay.connected) {\n log(`Relay: connected (proxy: ${relay.proxyId})`);\n log(\n ` queue depth: ${relay.queueDepth}, reconnect attempts: ${relay.reconnectAttempt}`,\n );\n } else {\n log(\n `Relay: disconnected (proxy: ${relay.proxyId}, reconnecting: attempt ${relay.reconnectAttempt}, queued: ${relay.queueDepth})`,\n );\n }\n log(\"\");\n\n // 显示会话列表\n const sessions = msg.sessions;\n if (sessions.length === 0) {\n log(\"Sessions: none\");\n } else {\n log(`Sessions: ${sessions.length}`);\n for (const s of sessions) {\n log(` ${s.id} ${s.mode} ${s.state} worker: ${s.hasWorker ? \"yes\" : \"no\"}`);\n }\n }\n sock.destroy();\n resolve(lines);\n }\n });\n sock.write(serializeIpc({ type: \"service_status_request\" }));\n });\n });\n}\n\nconst DAEMON_STARTUP_TIMEOUT_MS = 30_000;\nconst DAEMON_STARTUP_POLL_MS = 200;\n\n// 轮询 SOCK_PATH 直到可连接,作为 serve 的 readiness 信号。\n// serve.ts 里 server.listen(SOCK_PATH) 是启动序列的最后一步,连上即代表 ready。\nasync function waitForServeReady(timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const connected = await new Promise<boolean>((resolve) => {\n const sock = connect(SOCK_PATH);\n sock.once(\"connect\", () => {\n sock.destroy();\n resolve(true);\n });\n sock.once(\"error\", () => resolve(false));\n });\n if (connected) return true;\n await sleep(DAEMON_STARTUP_POLL_MS);\n }\n return false;\n}\n\nasync function startDaemon(options?: { relayName?: string }): Promise<void> {\n ensureProfileWorkspace();\n if (existsSync(PID_PATH)) {\n const pid = parseInt(readFileSync(PID_PATH, \"utf-8\").trim(), 10);\n try {\n process.kill(pid, 0);\n console.error(`Service is already running (PID ${pid})`);\n return;\n } catch {\n // process.kill(pid, 0) 抛错表示进程不存在,继续启动\n }\n }\n if (existsSync(STOPPED_PATH)) unlinkSync(STOPPED_PATH);\n\n // stderr 走 pipe 由父 CLI 订阅:子进程 ready 前(pino logger 未接管)的启动错误\n // 会被捕获;ready 后父 detach,pino 接管所有输出到 service.log。\n // start 命令必须等 daemon socket 可连接后再退出;否则用户会看到“启动成功”,实际服务还没就绪。\n const serveArgs = [\"--profile\", PROFILE_NAME, ...daemonRelayArgs(options?.relayName)];\n const child = spawnScript(new URL(\"./serve\", import.meta.url), serveArgs, {\n env: { ...process.env },\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n unref: false,\n });\n\n const stderrChunks: Buffer[] = [];\n child.stderr!.on(\"data\", (chunk: Buffer) => {\n stderrChunks.push(chunk);\n });\n\n // race: readiness handshake vs. 子进程先挂。子进程 ready 前就 exit 说明启动硬失败,\n // 不必再等到 30s 超时才报错。\n type Outcome =\n | { kind: \"ready\" }\n | { kind: \"timeout\" }\n | { kind: \"exited\"; code: number | null; signal: NodeJS.Signals | null };\n\n const readyOutcome: Promise<Outcome> = waitForServeReady(DAEMON_STARTUP_TIMEOUT_MS).then((ok) =>\n ok ? { kind: \"ready\" as const } : { kind: \"timeout\" as const },\n );\n const exitOutcome: Promise<Outcome> = new Promise((resolve) => {\n // 设 listener 前已经 exit 的边界:Node 记在 exitCode 上\n if (child.exitCode !== null) {\n resolve({ kind: \"exited\", code: child.exitCode, signal: child.signalCode });\n return;\n }\n child.once(\"exit\", (code, signal) => resolve({ kind: \"exited\", code, signal }));\n });\n\n const result = await Promise.race([readyOutcome, exitOutcome]);\n\n if (result.kind === \"ready\") {\n console.log(`Service started in background (PID ${child.pid})`);\n // ready 后 detach:摘 stderr 订阅 + destroy pipe + unref 子进程。\n // 单独 child.unref() 不够,父侧的 stderr pipe fd 还在事件循环里会让父 CLI 永不退出;\n // 必须 destroy 掉 pipe 才能真正释放 refcount。pino 已接管子进程的输出到 service.log。\n child.stderr!.removeAllListeners(\"data\");\n child.stderr!.destroy();\n child.unref();\n return;\n }\n\n // 失败路径:timeout 或 exited\n const stderrOutput = Buffer.concat(stderrChunks).toString(\"utf-8\").trim();\n if (result.kind === \"exited\") {\n console.error(`Service exited during startup (code=${result.code}, signal=${result.signal}).`);\n } else {\n console.error(`Service failed to become ready within ${DAEMON_STARTUP_TIMEOUT_MS / 1000}s.`);\n try {\n process.kill(child.pid!, \"SIGTERM\");\n } catch {\n // 子进程可能已自己退出,kill 失败不影响后续退出码\n }\n }\n if (stderrOutput) {\n console.error(\"--- child stderr ---\");\n console.error(stderrOutput);\n }\n process.exit(1);\n}\n\nconst program = new Command(\"dev-anywhere\")\n .description(\"Dev Anywhere - transparent local AI CLI proxy with remote control\")\n .version(pkg.version, \"-v, --version\")\n .option(\"--profile <name>\", \"Use an isolated local proxy profile\")\n .allowUnknownOption()\n .allowExcessArguments()\n .action(async () => {\n if (!isInitialized()) {\n console.error(`Dev Anywhere is not initialized. Run \"dev-anywhere init\" first.`);\n process.exit(1);\n }\n // 参数校验放在 dynamic import 之前:错误参数路径不应触发 terminal 模块加载,\n // 避免无谓地拉起 PTY/xterm/logger 这些重资源(也避免 logger 文件 IO 副作用)。\n let invocation: ReturnType<typeof extractAgentInvocation>;\n try {\n invocation = extractAgentInvocation(cliArgsWithoutProfile);\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n // 延迟导入 terminal: CLI 的其他子命令(init/stop/status)不需要 PTY + xterm 相关依赖,\n // tsup 基于 dynamic import 自动代码分裂,避免所有命令都为 terminal 付出 14KB 额外启动成本。\n const { startTerminal } = await import(\"./terminal.js\");\n const { provider, args } = invocation;\n await startTerminal(args, provider);\n });\n\n// serve 子命令组\nconst serve = new Command(\"serve\")\n .description(\"Manage the dev-anywhere background service\")\n .option(\"--profile <name>\", \"Use an isolated local proxy profile\")\n .option(\"-d, --daemon\", \"Run in background\")\n .action(async (opts) => {\n if (!isInitialized()) {\n console.error(`Dev Anywhere is not initialized. Run \"dev-anywhere init\" first.`);\n process.exit(1);\n }\n if (opts.daemon) {\n setDesiredDaemonRelay(undefined);\n await startDaemon();\n } else {\n // 延迟导入 serve: daemon 模式只需要 startDaemon(纯 spawn),不需要加载 70KB 的 serve bundle\n const { startService } = await import(\"./serve.js\");\n await startService();\n }\n });\n\nserve\n .command(\"start\")\n .description(\"Start the background service\")\n .option(\"--relay <name>\", \"Use a named relay from config\")\n .action(async (opts) => {\n if (!isInitialized()) {\n console.error(`Dev Anywhere is not initialized. Run \"dev-anywhere init\" first.`);\n process.exit(1);\n }\n setDesiredDaemonRelay(opts.relay);\n await startDaemon({ relayName: opts.relay });\n });\n\nserve\n .command(\"status\")\n .description(\"Show service status and active sessions\")\n .option(\"-w, --watch\", \"Continuous monitoring mode\")\n .option(\"-n, --interval <seconds>\", \"Refresh interval in seconds\", \"2\")\n .action(async (opts) => {\n if (opts.watch) {\n const intervalMs = Number(opts.interval) * 1000;\n let lastLines = await showStatus();\n setInterval(async () => {\n if (lastLines > 0) {\n process.stdout.write(`\\x1B[${lastLines}A\\x1B[J`);\n }\n lastLines = await showStatus();\n }, intervalMs);\n } else {\n await showStatus();\n }\n });\n\nserve\n .command(\"stop\")\n .description(\"Stop the background service\")\n .action(() => {\n stopService();\n });\n\nserve\n .command(\"restart\")\n .description(\"Restart the background service\")\n .option(\"--relay <name>\", \"Use a named relay from config\")\n .action(async (opts) => {\n setDesiredDaemonRelay(opts.relay);\n stopService();\n await startDaemon({ relayName: opts.relay });\n });\n\nprogram.addCommand(serve);\n\nconst relay = new Command(\"relay\").description(\"Inspect and manage relay configuration\");\n\nrelay\n .command(\"token\")\n .description(\"Print the relay's current client token (auth: proxy token)\")\n .option(\"--relay <name>\", \"Use a named relay from config\")\n .action(async (opts) => {\n if (!isInitialized()) {\n console.error(`Dev Anywhere is not initialized. Run \"dev-anywhere init\" first.`);\n process.exit(1);\n }\n const { runRelayTokenCommand } = await import(\"./relay-token.js\");\n await runRelayTokenCommand({ relayName: opts.relay });\n });\n\nprogram.addCommand(relay);\n\nprogram\n .command(\"init\")\n .description(\"Initialize dev-anywhere workspace (~/.dev-anywhere)\")\n .action(() => {\n if (isInitialized()) {\n console.log(`Already initialized. Config at ${CONFIG_PATH}`);\n return;\n }\n initWorkspace();\n console.log(\"Initialized ~/.dev-anywhere/\");\n console.log(`Edit ${CONFIG_PATH} to configure relay server URL.`);\n });\n\n// pnpm run dev -- args 会在参数前插入 \"--\"。根脚本和用户命令都可能再加一层\n// 分隔符,所以这里过滤所有前导分隔符,再交给 Commander 和 provider 参数解析。\nconst cliArgs = normalizeCliArgs(process.argv.slice(2));\nconst cliArgsWithoutProfile = stripProxyProfileArgs(cliArgs);\n\nprogram.parse(cliArgsWithoutProfile, { from: \"user\" });\n","import type { ProviderId } from \"./providers/index.js\";\n\nexport function normalizeCliArgs(args: string[]): string[] {\n const normalized = [...args];\n while (normalized[0] === \"--\") {\n normalized.shift();\n }\n return normalized;\n}\n\nexport function stripProxyProfileArgs(args: string[]): string[] {\n const result: string[] = [];\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"claude\" || arg === \"codex\") {\n result.push(...args.slice(i));\n break;\n }\n if (arg === \"--profile\") {\n i++;\n continue;\n }\n if (arg.startsWith(\"--profile=\")) {\n continue;\n }\n result.push(arg);\n }\n return result;\n}\n\nexport function extractAgentInvocation(args: string[]): { provider: ProviderId; args: string[] } {\n const [agent, ...providerArgs] = args;\n if (agent !== \"claude\" && agent !== \"codex\") {\n throw new Error(\n 'Missing Agent CLI. Use \"dev-anywhere claude ...\" or \"dev-anywhere codex ...\".',\n );\n }\n return { provider: agent, args: providerArgs };\n}\n"],"mappings":"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","names":["relay"]}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadConfig
|
|
4
|
+
} from "./chunk-R4S6OFIZ.js";
|
|
5
|
+
import "./chunk-GTTLWHIG.js";
|
|
6
|
+
import "./chunk-2XO3KLWW.js";
|
|
7
|
+
|
|
8
|
+
// src/relay-token.ts
|
|
9
|
+
function toHttpUrl(relayUrl) {
|
|
10
|
+
return relayUrl.replace(/^ws:/i, "http:").replace(/^wss:/i, "https:").replace(/\/$/, "");
|
|
11
|
+
}
|
|
12
|
+
async function runRelayTokenCommand(options) {
|
|
13
|
+
let config;
|
|
14
|
+
try {
|
|
15
|
+
config = loadConfig({ relayName: options.relayName });
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const { relayName, relayUrl, relayToken } = config;
|
|
21
|
+
if (!relayUrl) {
|
|
22
|
+
console.error(
|
|
23
|
+
`Relay "${relayName}" has no URL configured. Edit ~/.dev-anywhere/config.json or set RELAY_URL.`
|
|
24
|
+
);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
if (!relayToken) {
|
|
28
|
+
console.error(
|
|
29
|
+
`Relay "${relayName}" has no proxy token configured. The admin endpoint requires one.`
|
|
30
|
+
);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
const adminUrl = `${toHttpUrl(relayUrl)}/admin/client-token`;
|
|
34
|
+
let res;
|
|
35
|
+
try {
|
|
36
|
+
res = await fetch(adminUrl, {
|
|
37
|
+
headers: { authorization: `Bearer ${relayToken}` },
|
|
38
|
+
cache: "no-store"
|
|
39
|
+
});
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error(`Request to ${adminUrl} failed: ${err instanceof Error ? err.message : err}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
if (res.status === 401) {
|
|
45
|
+
console.error(
|
|
46
|
+
`Relay rejected the proxy token (HTTP 401). Verify ~/.dev-anywhere/config.json matches the relay's RELAY_PROXY_TOKEN.`
|
|
47
|
+
);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
if (res.status === 204) {
|
|
51
|
+
const result = { status: "no_client_token" };
|
|
52
|
+
printResult(relayName, relayUrl, result);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const body = await res.text().catch(() => "");
|
|
57
|
+
console.error(`Unexpected response from relay (HTTP ${res.status}): ${body}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const json = await res.json();
|
|
61
|
+
if (typeof json.clientToken !== "string" || json.clientToken.length === 0) {
|
|
62
|
+
console.error(`Relay returned a malformed payload: ${JSON.stringify(json)}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
printResult(relayName, relayUrl, { status: "ok", clientToken: json.clientToken });
|
|
66
|
+
}
|
|
67
|
+
function printResult(relayName, relayUrl, result) {
|
|
68
|
+
const httpBase = toHttpUrl(relayUrl);
|
|
69
|
+
console.log(`Relay: ${relayName} (${relayUrl})`);
|
|
70
|
+
if (result.status === "no_client_token") {
|
|
71
|
+
console.log(`Token: (not configured \u2014 /client endpoint is open)`);
|
|
72
|
+
console.log(`URL: ${httpBase}/`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(`Token: ${result.clientToken}`);
|
|
76
|
+
console.log(`URL: ${httpBase}/?relayToken=${encodeURIComponent(result.clientToken)}`);
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
runRelayTokenCommand
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=relay-token-KQPEQVP7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/relay-token.ts"],"sourcesContent":["// `dev-anywhere relay token` 实现:用本地 proxyToken 向已配置的 relay\n// 请求当前生效的 client token,避免运维者必须 ssh 到 VPS 读 .env。\nimport { loadConfig } from \"./common/config.js\";\n\ninterface FetchClientTokenResult {\n status: \"ok\" | \"no_client_token\";\n clientToken?: string;\n}\n\nfunction toHttpUrl(relayUrl: string): string {\n return relayUrl.replace(/^ws:/i, \"http:\").replace(/^wss:/i, \"https:\").replace(/\\/$/, \"\");\n}\n\nexport async function runRelayTokenCommand(options: { relayName?: string }): Promise<void> {\n let config: ReturnType<typeof loadConfig>;\n try {\n config = loadConfig({ relayName: options.relayName });\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n\n const { relayName, relayUrl, relayToken } = config;\n if (!relayUrl) {\n console.error(\n `Relay \"${relayName}\" has no URL configured. Edit ~/.dev-anywhere/config.json or set RELAY_URL.`,\n );\n process.exit(1);\n }\n if (!relayToken) {\n console.error(\n `Relay \"${relayName}\" has no proxy token configured. The admin endpoint requires one.`,\n );\n process.exit(1);\n }\n\n const adminUrl = `${toHttpUrl(relayUrl)}/admin/client-token`;\n let res: Response;\n try {\n res = await fetch(adminUrl, {\n headers: { authorization: `Bearer ${relayToken}` },\n cache: \"no-store\",\n });\n } catch (err) {\n console.error(`Request to ${adminUrl} failed: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n\n if (res.status === 401) {\n console.error(\n `Relay rejected the proxy token (HTTP 401). Verify ~/.dev-anywhere/config.json matches the relay's RELAY_PROXY_TOKEN.`,\n );\n process.exit(1);\n }\n if (res.status === 204) {\n const result: FetchClientTokenResult = { status: \"no_client_token\" };\n printResult(relayName, relayUrl, result);\n return;\n }\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n console.error(`Unexpected response from relay (HTTP ${res.status}): ${body}`);\n process.exit(1);\n }\n\n const json = (await res.json()) as { clientToken?: unknown };\n if (typeof json.clientToken !== \"string\" || json.clientToken.length === 0) {\n console.error(`Relay returned a malformed payload: ${JSON.stringify(json)}`);\n process.exit(1);\n }\n printResult(relayName, relayUrl, { status: \"ok\", clientToken: json.clientToken });\n}\n\nfunction printResult(relayName: string, relayUrl: string, result: FetchClientTokenResult): void {\n const httpBase = toHttpUrl(relayUrl);\n console.log(`Relay: ${relayName} (${relayUrl})`);\n if (result.status === \"no_client_token\") {\n console.log(`Token: (not configured — /client endpoint is open)`);\n console.log(`URL: ${httpBase}/`);\n return;\n }\n console.log(`Token: ${result.clientToken}`);\n console.log(`URL: ${httpBase}/?relayToken=${encodeURIComponent(result.clientToken!)}`);\n}\n"],"mappings":";;;;;;;;AASA,SAAS,UAAU,UAA0B;AAC3C,SAAO,SAAS,QAAQ,SAAS,OAAO,EAAE,QAAQ,UAAU,QAAQ,EAAE,QAAQ,OAAO,EAAE;AACzF;AAEA,eAAsB,qBAAqB,SAAgD;AACzF,MAAI;AACJ,MAAI;AACF,aAAS,WAAW,EAAE,WAAW,QAAQ,UAAU,CAAC;AAAA,EACtD,SAAS,KAAK;AACZ,YAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,EAAE,WAAW,UAAU,WAAW,IAAI;AAC5C,MAAI,CAAC,UAAU;AACb,YAAQ;AAAA,MACN,UAAU,SAAS;AAAA,IACrB;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,YAAY;AACf,YAAQ;AAAA,MACN,UAAU,SAAS;AAAA,IACrB;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,WAAW,GAAG,UAAU,QAAQ,CAAC;AACvC,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,UAAU;AAAA,MAC1B,SAAS,EAAE,eAAe,UAAU,UAAU,GAAG;AAAA,MACjD,OAAO;AAAA,IACT,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,MAAM,cAAc,QAAQ,YAAY,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AAC1F,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,IAAI,WAAW,KAAK;AACtB,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,IAAI,WAAW,KAAK;AACtB,UAAM,SAAiC,EAAE,QAAQ,kBAAkB;AACnE,gBAAY,WAAW,UAAU,MAAM;AACvC;AAAA,EACF;AACA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,YAAQ,MAAM,wCAAwC,IAAI,MAAM,MAAM,IAAI,EAAE;AAC5E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,MAAI,OAAO,KAAK,gBAAgB,YAAY,KAAK,YAAY,WAAW,GAAG;AACzE,YAAQ,MAAM,uCAAuC,KAAK,UAAU,IAAI,CAAC,EAAE;AAC3E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,cAAY,WAAW,UAAU,EAAE,QAAQ,MAAM,aAAa,KAAK,YAAY,CAAC;AAClF;AAEA,SAAS,YAAY,WAAmB,UAAkB,QAAsC;AAC9F,QAAM,WAAW,UAAU,QAAQ;AACnC,UAAQ,IAAI,UAAU,SAAS,KAAK,QAAQ,GAAG;AAC/C,MAAI,OAAO,WAAW,mBAAmB;AACvC,YAAQ,IAAI,yDAAoD;AAChE,YAAQ,IAAI,UAAU,QAAQ,GAAG;AACjC;AAAA,EACF;AACA,UAAQ,IAAI,UAAU,OAAO,WAAW,EAAE;AAC1C,UAAQ,IAAI,UAAU,QAAQ,gBAAgB,mBAAmB,OAAO,WAAY,CAAC,EAAE;AACzF;","names":[]}
|