@appchy/jarvis 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +129 -12
- package/dist/bin.js.map +1 -1
- package/package.json +3 -3
package/dist/bin.js
CHANGED
|
@@ -400,6 +400,7 @@ function claudeCodeProvider(config = {}) {
|
|
|
400
400
|
} : {},
|
|
401
401
|
...config.disallowedTools?.length ? { disallowedTools: config.disallowedTools } : {},
|
|
402
402
|
...config.abortController ? { abortController: config.abortController } : {},
|
|
403
|
+
...config.thinking ? { thinking: config.thinking } : {},
|
|
403
404
|
// Session resume/persistence options
|
|
404
405
|
...config.session?.sessionId ? { sessionId: config.session.sessionId } : {},
|
|
405
406
|
...config.session?.resume ? { resume: config.session.resume } : {},
|
|
@@ -569,6 +570,7 @@ function claudeCodeProvider(config = {}) {
|
|
|
569
570
|
} : {},
|
|
570
571
|
...config.disallowedTools?.length ? { disallowedTools: config.disallowedTools } : {},
|
|
571
572
|
...config.abortController ? { abortController: config.abortController } : {},
|
|
573
|
+
...config.thinking ? { thinking: config.thinking } : {},
|
|
572
574
|
// Session resume/persistence options
|
|
573
575
|
...config.session?.sessionId ? { sessionId: config.session.sessionId } : {},
|
|
574
576
|
...config.session?.resume ? { resume: config.session.resume } : {},
|
|
@@ -2018,6 +2020,8 @@ function createWsProgressHandlers(deps) {
|
|
|
2018
2020
|
let currentThinkingId = null;
|
|
2019
2021
|
let currentTextId = null;
|
|
2020
2022
|
let currentTextContent = "";
|
|
2023
|
+
let planFileContent = null;
|
|
2024
|
+
let planFilePath = null;
|
|
2021
2025
|
async function onMessage(msg) {
|
|
2022
2026
|
broadcast({ type: "agent:onMessage", taskId, message: msg });
|
|
2023
2027
|
}
|
|
@@ -2045,10 +2049,27 @@ function createWsProgressHandlers(deps) {
|
|
|
2045
2049
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2046
2050
|
if (toolName === "askUser") return waitForInput(input);
|
|
2047
2051
|
if (toolName === "ExitPlanMode") {
|
|
2052
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
2053
|
+
if (planFileContent) {
|
|
2054
|
+
await onMessage({
|
|
2055
|
+
id: `plan-${crypto2.randomUUID()}`,
|
|
2056
|
+
role: "assistant",
|
|
2057
|
+
type: "ui",
|
|
2058
|
+
content: planFileContent,
|
|
2059
|
+
ui: {
|
|
2060
|
+
uri: "ui://agent/plan",
|
|
2061
|
+
title: "Plan",
|
|
2062
|
+
data: { content: planFileContent, path: planFilePath }
|
|
2063
|
+
},
|
|
2064
|
+
isPartial: false,
|
|
2065
|
+
createdAt: now2
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2048
2068
|
return waitForInput({
|
|
2049
2069
|
...input,
|
|
2050
2070
|
type: "plan",
|
|
2051
|
-
reason: input.input?.allowedPrompts || "Plan ready for review"
|
|
2071
|
+
reason: input.input?.allowedPrompts || "Plan ready for review",
|
|
2072
|
+
output: planFileContent ? { content: planFileContent, path: planFilePath } : input.output
|
|
2052
2073
|
});
|
|
2053
2074
|
}
|
|
2054
2075
|
if (toolName === "EnterPlanMode") {
|
|
@@ -2108,6 +2129,11 @@ function createWsProgressHandlers(deps) {
|
|
|
2108
2129
|
isComplete: false
|
|
2109
2130
|
});
|
|
2110
2131
|
}
|
|
2132
|
+
if (block.type === "tool_use" && block.name === "Write" && typeof block.input?.file_path === "string" && block.input.file_path.endsWith(".md")) {
|
|
2133
|
+
const input = block.input;
|
|
2134
|
+
planFileContent = input.content ?? null;
|
|
2135
|
+
planFilePath = input.file_path ?? null;
|
|
2136
|
+
}
|
|
2111
2137
|
if (block.type === "tool_use" && block.id && block.name) {
|
|
2112
2138
|
if (currentTextId) {
|
|
2113
2139
|
broadcast({
|
|
@@ -2233,6 +2259,8 @@ function createAgent(deps) {
|
|
|
2233
2259
|
worktreePath: wsPath,
|
|
2234
2260
|
messageCount: msg.messages.length
|
|
2235
2261
|
});
|
|
2262
|
+
const askModeTools = msg.permissionMode === "ask" ? ["Edit", "Write", "MultiEdit", "NotebookEdit"] : [];
|
|
2263
|
+
const allDisallowed = [...msg.disallowedTools ?? [], ...askModeTools];
|
|
2236
2264
|
const provider2 = claudeCodeProvider({
|
|
2237
2265
|
...deps.useSubscription ? { useSubscription: true } : { apiKey: deps.anthropicApiKey },
|
|
2238
2266
|
systemPrompt: systemMsg?.content ?? "",
|
|
@@ -2247,7 +2275,8 @@ function createAgent(deps) {
|
|
|
2247
2275
|
onProgress: handlers.handleProgress,
|
|
2248
2276
|
onUserInput: handlers.handleUserInput,
|
|
2249
2277
|
permissionMode: msg.permissionMode === "plan" ? "plan" : "default",
|
|
2250
|
-
...
|
|
2278
|
+
...allDisallowed.length ? { disallowedTools: allDisallowed } : {},
|
|
2279
|
+
...msg.thinking ? { thinking: msg.thinking } : {},
|
|
2251
2280
|
...msg.session ? { session: msg.session } : {}
|
|
2252
2281
|
});
|
|
2253
2282
|
const ctx = { requestId: taskId, userId: deps.userId };
|
|
@@ -2303,7 +2332,7 @@ function createAgent(deps) {
|
|
|
2303
2332
|
deps.broadcast({
|
|
2304
2333
|
type: "agent:output",
|
|
2305
2334
|
taskId,
|
|
2306
|
-
output: { success: false, content: "", error: "Aborted" }
|
|
2335
|
+
output: { success: false, content: "", error: "Aborted", sessionId: msg.session?.resume }
|
|
2307
2336
|
});
|
|
2308
2337
|
} else {
|
|
2309
2338
|
const error = err instanceof Error ? err.message : String(err);
|
|
@@ -4804,8 +4833,99 @@ function clearPid() {
|
|
|
4804
4833
|
} catch {
|
|
4805
4834
|
}
|
|
4806
4835
|
}
|
|
4836
|
+
async function waitForBrowserAuth(port) {
|
|
4837
|
+
const http = await import("http");
|
|
4838
|
+
return new Promise((resolve, reject) => {
|
|
4839
|
+
const timeout = setTimeout(() => {
|
|
4840
|
+
server.close();
|
|
4841
|
+
reject(new Error("Browser authentication timed out (2 minutes)"));
|
|
4842
|
+
}, 12e4);
|
|
4843
|
+
const server = http.createServer((req, res) => {
|
|
4844
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
4845
|
+
if (url.pathname === "/callback") {
|
|
4846
|
+
const token = url.searchParams.get("token");
|
|
4847
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
4848
|
+
res.setHeader("Access-Control-Allow-Methods", "GET");
|
|
4849
|
+
if (token) {
|
|
4850
|
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
4851
|
+
res.end("OK");
|
|
4852
|
+
clearTimeout(timeout);
|
|
4853
|
+
server.close();
|
|
4854
|
+
resolve(token);
|
|
4855
|
+
} else {
|
|
4856
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
4857
|
+
res.end("Missing token");
|
|
4858
|
+
}
|
|
4859
|
+
return;
|
|
4860
|
+
}
|
|
4861
|
+
res.writeHead(404);
|
|
4862
|
+
res.end();
|
|
4863
|
+
});
|
|
4864
|
+
server.listen(port, "127.0.0.1");
|
|
4865
|
+
});
|
|
4866
|
+
}
|
|
4867
|
+
async function findFreePort() {
|
|
4868
|
+
const net = await import("net");
|
|
4869
|
+
return new Promise((resolve, reject) => {
|
|
4870
|
+
const server = net.createServer();
|
|
4871
|
+
server.listen(0, "127.0.0.1", () => {
|
|
4872
|
+
const addr = server.address();
|
|
4873
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
4874
|
+
server.close(() => resolve(port));
|
|
4875
|
+
});
|
|
4876
|
+
server.on("error", reject);
|
|
4877
|
+
});
|
|
4878
|
+
}
|
|
4879
|
+
async function browserAuth(appUrl) {
|
|
4880
|
+
const config = loadConfig();
|
|
4881
|
+
const callbackPort = await findFreePort();
|
|
4882
|
+
const loginUrl = `${appUrl}/login?callback=cli&port=${callbackPort}`;
|
|
4883
|
+
console.log();
|
|
4884
|
+
console.log("Opening browser for sign in...");
|
|
4885
|
+
console.log(` If it doesn't open, visit: ${loginUrl}`);
|
|
4886
|
+
console.log();
|
|
4887
|
+
const { exec: exec3 } = await import("child_process");
|
|
4888
|
+
const platform = process.platform;
|
|
4889
|
+
const openCmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
|
|
4890
|
+
exec3(`${openCmd} "${loginUrl}"`);
|
|
4891
|
+
console.log("Waiting for authentication...");
|
|
4892
|
+
try {
|
|
4893
|
+
const token = await waitForBrowserAuth(callbackPort);
|
|
4894
|
+
const parsed = parseConnectToken(token);
|
|
4895
|
+
saveConfig({
|
|
4896
|
+
...config,
|
|
4897
|
+
apiUrl: parsed.apiUrl,
|
|
4898
|
+
token: parsed.jwt,
|
|
4899
|
+
refreshToken: parsed.refreshToken,
|
|
4900
|
+
userId: parsed.userId,
|
|
4901
|
+
envId: parsed.envId,
|
|
4902
|
+
connectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4903
|
+
});
|
|
4904
|
+
console.log();
|
|
4905
|
+
console.log(`Signed in as ${parsed.userId}`);
|
|
4906
|
+
return true;
|
|
4907
|
+
} catch (err) {
|
|
4908
|
+
console.error(`Authentication failed: ${err instanceof Error ? err.message : err}`);
|
|
4909
|
+
return false;
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4912
|
+
var DEFAULT_APP_URL = process.env.APP_URL ?? "http://localhost:3000";
|
|
4913
|
+
async function ensureSetup() {
|
|
4914
|
+
const config = loadConfig();
|
|
4915
|
+
if (config?.token || config?.anthropicApiKey || config?.useSubscription) {
|
|
4916
|
+
return true;
|
|
4917
|
+
}
|
|
4918
|
+
return browserAuth(config?.apiUrl ?? DEFAULT_APP_URL);
|
|
4919
|
+
}
|
|
4807
4920
|
function createCli() {
|
|
4808
4921
|
const program = new Command().name("jarvis").description("Jarvis local agent \u2014 runs Claude Code on your machine").version(PKG_VERSION);
|
|
4922
|
+
program.argument("[message]", "Optional initial message to send").option("--model <id>", "Model (e.g., claude-opus-4-20250514)").option("--mode <mode>", "Permission mode: supervised|auto|plan|yolo").option("--thinking <config>", "Thinking: adaptive|enabled|disabled").option("--thinking-budget <n>", "Token budget when thinking=enabled").option("--max-turns <n>", "Max turns per task").option("-p, --port <port>", "Agent server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--no-server", "Connect to existing server, don't auto-start").action(async (message, opts) => {
|
|
4923
|
+
if (message && program.commands.some((c) => c.name() === message)) {
|
|
4924
|
+
return;
|
|
4925
|
+
}
|
|
4926
|
+
await ensureSetup();
|
|
4927
|
+
await launchChat(opts);
|
|
4928
|
+
});
|
|
4809
4929
|
program.command("connect <token>").description("Connect to Jarvis cloud using a token from the web UI").option("-w, --workspace <path>", "Workspace root path for repo operations").action((token, opts) => {
|
|
4810
4930
|
try {
|
|
4811
4931
|
const parsed = parseConnectToken(token);
|
|
@@ -4833,6 +4953,7 @@ function createCli() {
|
|
|
4833
4953
|
}
|
|
4834
4954
|
});
|
|
4835
4955
|
program.command("start").description("Start the local agent (runs as background daemon)").option("-p, --port <port>", "Local WS server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--api-key <key>", "Anthropic API key (for local-only use)").option("--no-upstream", "Don't connect to cloud (local-only mode)").option("--foreground", "Run in foreground (don't daemonize)").action(async (opts) => {
|
|
4956
|
+
await ensureSetup();
|
|
4836
4957
|
const config = loadConfig();
|
|
4837
4958
|
const port = parseInt(opts.port, 10);
|
|
4838
4959
|
const alreadyRunning = await isPortInUse(port);
|
|
@@ -4842,17 +4963,16 @@ function createCli() {
|
|
|
4842
4963
|
return;
|
|
4843
4964
|
}
|
|
4844
4965
|
const workspacePath = opts.workspace ?? config?.workspacePath ?? process.cwd();
|
|
4845
|
-
const anthropicApiKey = opts.apiKey ?? config?.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
4846
4966
|
const userId = config?.userId ?? process.env.JARVIS_USER_ID ?? "local";
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4967
|
+
const explicitApiKey = opts.apiKey ?? config?.anthropicApiKey;
|
|
4968
|
+
const useSubscription = !explicitApiKey;
|
|
4969
|
+
const anthropicApiKey = explicitApiKey;
|
|
4850
4970
|
if (opts.foreground) {
|
|
4851
4971
|
await startAgent({
|
|
4852
4972
|
port,
|
|
4853
4973
|
workspacePath,
|
|
4854
4974
|
anthropicApiKey,
|
|
4855
|
-
useSubscription
|
|
4975
|
+
useSubscription,
|
|
4856
4976
|
userId,
|
|
4857
4977
|
upstream: (() => {
|
|
4858
4978
|
const apiUrl = process.env.JARVIS_UPSTREAM_URL ?? config?.apiUrl;
|
|
@@ -4958,7 +5078,7 @@ function createCli() {
|
|
|
4958
5078
|
console.log("Starting agent...");
|
|
4959
5079
|
await program.parseAsync(["node", "jarvis", "start"]);
|
|
4960
5080
|
});
|
|
4961
|
-
program.command("logs").description("View agent logs").option("-f, --follow", "Follow log output (like tail -f)").option("-n, --lines <n>", "Number of lines to show", "50").action((opts) => {
|
|
5081
|
+
program.command("logs").description("View agent logs").option("-f, --follow", "Follow log output (like tail -f)", true).option("-n, --lines <n>", "Number of lines to show", "50").action((opts) => {
|
|
4962
5082
|
if (!fs7.existsSync(LOG_FILE)) {
|
|
4963
5083
|
console.log("No log file found. Start the agent first: jarvis start");
|
|
4964
5084
|
return;
|
|
@@ -4977,9 +5097,6 @@ function createCli() {
|
|
|
4977
5097
|
process.stdout.write(content);
|
|
4978
5098
|
}
|
|
4979
5099
|
});
|
|
4980
|
-
program.command("chat").description("Interactive TUI \u2014 chat with Jarvis in your terminal").option("--model <id>", "Model (e.g., claude-opus-4-20250514)").option("--mode <mode>", "Permission mode: supervised|auto|plan|yolo").option("--thinking <config>", "Thinking: adaptive|enabled|disabled").option("--thinking-budget <n>", "Token budget when thinking=enabled").option("--max-turns <n>", "Max turns per task").option("-p, --port <port>", "Agent server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--no-server", "Connect to existing server, don't auto-start").action(async (opts) => {
|
|
4981
|
-
await launchChat(opts);
|
|
4982
|
-
});
|
|
4983
5100
|
program.command("status").description("Show agent configuration and connection status").action(async () => {
|
|
4984
5101
|
const config = loadConfig();
|
|
4985
5102
|
const pid = readPid();
|