anywhere-ai 0.0.26 → 0.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +2 -2
- package/dist/server.js +107 -56
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12,11 +12,11 @@ import qrcode from "qrcode";
|
|
|
12
12
|
var args = process.argv.slice(2);
|
|
13
13
|
var command = args.find((a) => !a.startsWith("-"));
|
|
14
14
|
if (args.includes("--version") || args.includes("-v")) {
|
|
15
|
-
console.log(`anywhere-ai v${"0.0.
|
|
15
|
+
console.log(`anywhere-ai v${"0.0.28"}`);
|
|
16
16
|
process.exit(0);
|
|
17
17
|
}
|
|
18
18
|
if (args.includes("--help") || args.includes("-h") || command === "help") {
|
|
19
|
-
console.log(`anywhere-ai v${"0.0.
|
|
19
|
+
console.log(`anywhere-ai v${"0.0.28"} \u2014 Mobile coding agent
|
|
20
20
|
|
|
21
21
|
Usage: anywhere-ai [command] [options]
|
|
22
22
|
|
package/dist/server.js
CHANGED
|
@@ -67,6 +67,8 @@ async function getCwdFromJSONL(sessionId) {
|
|
|
67
67
|
}
|
|
68
68
|
var activeSessions = /* @__PURE__ */ new Set();
|
|
69
69
|
var pendingPermissions = /* @__PURE__ */ new Map();
|
|
70
|
+
var permissionToolUseIDs = /* @__PURE__ */ new Map();
|
|
71
|
+
var permissionInputs = /* @__PURE__ */ new Map();
|
|
70
72
|
function getAssistantText(msg) {
|
|
71
73
|
if (msg.type !== "assistant") return null;
|
|
72
74
|
return msg.message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
@@ -83,22 +85,33 @@ var permReqCounter = 0;
|
|
|
83
85
|
function makeCanUseTool(sessionHint, permissionMode) {
|
|
84
86
|
return async (toolName, input, options) => {
|
|
85
87
|
const requestId = `perm_${++permReqCounter}_${Date.now()}`;
|
|
88
|
+
console.log(`[perm] canUseTool: ${toolName} requestId=${requestId}`);
|
|
86
89
|
const req = {
|
|
87
90
|
requestId,
|
|
88
91
|
toolName,
|
|
89
92
|
input,
|
|
90
93
|
description: input.description,
|
|
91
94
|
decisionReason: options.decisionReason,
|
|
92
|
-
suggestions: options.suggestions
|
|
95
|
+
suggestions: options.suggestions,
|
|
96
|
+
toolUseID: options.toolUseID
|
|
93
97
|
};
|
|
94
98
|
const cb = permissionCallbacks.get(sessionHint);
|
|
95
|
-
if (cb)
|
|
99
|
+
if (cb) {
|
|
100
|
+
cb(req);
|
|
101
|
+
} else {
|
|
102
|
+
console.log(`[perm] WARNING: no callback for session=${sessionHint}, requestId=${requestId}`);
|
|
103
|
+
}
|
|
104
|
+
if (options.toolUseID) permissionToolUseIDs.set(requestId, options.toolUseID);
|
|
105
|
+
permissionInputs.set(requestId, input);
|
|
96
106
|
return new Promise((resolve) => {
|
|
97
107
|
pendingPermissions.set(requestId, resolve);
|
|
98
108
|
const timeout = setTimeout(
|
|
99
109
|
() => {
|
|
100
110
|
if (pendingPermissions.has(requestId)) {
|
|
101
111
|
pendingPermissions.delete(requestId);
|
|
112
|
+
permissionToolUseIDs.delete(requestId);
|
|
113
|
+
permissionInputs.delete(requestId);
|
|
114
|
+
console.log(`[perm] TIMEOUT: ${requestId}`);
|
|
102
115
|
resolve({
|
|
103
116
|
behavior: "deny",
|
|
104
117
|
message: "Permission request timed out"
|
|
@@ -111,13 +124,18 @@ function makeCanUseTool(sessionHint, permissionMode) {
|
|
|
111
124
|
clearTimeout(timeout);
|
|
112
125
|
if (pendingPermissions.has(requestId)) {
|
|
113
126
|
pendingPermissions.delete(requestId);
|
|
127
|
+
permissionToolUseIDs.delete(requestId);
|
|
128
|
+
console.log(`[perm] ABORT: ${requestId}`);
|
|
114
129
|
resolve({ behavior: "deny", message: "Request aborted" });
|
|
130
|
+
} else {
|
|
115
131
|
}
|
|
116
132
|
});
|
|
117
133
|
const origResolve = resolve;
|
|
118
134
|
pendingPermissions.set(requestId, (result) => {
|
|
119
135
|
clearTimeout(timeout);
|
|
120
136
|
pendingPermissions.delete(requestId);
|
|
137
|
+
permissionToolUseIDs.delete(requestId);
|
|
138
|
+
console.log(`[perm] resolved: ${requestId} \u2192 ${result.behavior}`);
|
|
121
139
|
origResolve(result);
|
|
122
140
|
});
|
|
123
141
|
});
|
|
@@ -145,21 +163,22 @@ var createChat = async ({
|
|
|
145
163
|
model,
|
|
146
164
|
permission,
|
|
147
165
|
repo,
|
|
148
|
-
prompt
|
|
166
|
+
prompt,
|
|
167
|
+
tempId
|
|
149
168
|
}) => {
|
|
150
169
|
const repoPath = repo ? await setupRepo(repo) : void 0;
|
|
151
|
-
const
|
|
170
|
+
const id = tempId || `temp_${Date.now()}`;
|
|
152
171
|
const mode = permission || "acceptEdits";
|
|
153
172
|
const session = await withCwd(
|
|
154
173
|
repoPath,
|
|
155
174
|
() => unstable_v2_createSession({
|
|
156
175
|
model: model || "claude-opus-4-6",
|
|
157
176
|
permissionMode: mode,
|
|
158
|
-
canUseTool: makeCanUseTool(
|
|
177
|
+
canUseTool: makeCanUseTool(id, mode)
|
|
159
178
|
})
|
|
160
179
|
);
|
|
161
|
-
if (repoPath) sessionCwds.set(
|
|
162
|
-
return { session, tempId, repoPath };
|
|
180
|
+
if (repoPath) sessionCwds.set(id, repoPath);
|
|
181
|
+
return { session, tempId: id, repoPath };
|
|
163
182
|
};
|
|
164
183
|
var sendMessage = async ({
|
|
165
184
|
prompt,
|
|
@@ -173,31 +192,26 @@ var getSession = async ({
|
|
|
173
192
|
permission,
|
|
174
193
|
cwd
|
|
175
194
|
}) => {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const requestedPermission = permission || "acceptEdits";
|
|
180
|
-
const configChanged = session && currentConfig && (requestedModel !== currentConfig.model || requestedPermission !== currentConfig.permission);
|
|
181
|
-
if (session && configChanged) {
|
|
182
|
-
session.close();
|
|
195
|
+
const existing = sessions.get(sessionId);
|
|
196
|
+
if (existing) {
|
|
197
|
+
existing.close();
|
|
183
198
|
sessions.delete(sessionId);
|
|
184
199
|
sessionConfig.delete(sessionId);
|
|
185
|
-
session = void 0;
|
|
186
|
-
}
|
|
187
|
-
if (!session) {
|
|
188
|
-
const sessionCwd = cwd || sessionCwds.get(sessionId) || await getCwdFromJSONL(sessionId);
|
|
189
|
-
session = await withCwd(
|
|
190
|
-
sessionCwd,
|
|
191
|
-
() => unstable_v2_resumeSession(sessionId, {
|
|
192
|
-
model: requestedModel,
|
|
193
|
-
permissionMode: requestedPermission,
|
|
194
|
-
canUseTool: makeCanUseTool(sessionId, requestedPermission)
|
|
195
|
-
})
|
|
196
|
-
);
|
|
197
|
-
sessions.set(sessionId, session);
|
|
198
|
-
sessionConfig.set(sessionId, { model: requestedModel, permission: requestedPermission });
|
|
199
|
-
if (sessionCwd) sessionCwds.set(sessionId, sessionCwd);
|
|
200
200
|
}
|
|
201
|
+
const requestedModel = model || "claude-opus-4-6";
|
|
202
|
+
const requestedPermission = permission || "acceptEdits";
|
|
203
|
+
const sessionCwd = cwd || sessionCwds.get(sessionId) || await getCwdFromJSONL(sessionId);
|
|
204
|
+
const session = await withCwd(
|
|
205
|
+
sessionCwd,
|
|
206
|
+
() => unstable_v2_resumeSession(sessionId, {
|
|
207
|
+
model: requestedModel,
|
|
208
|
+
permissionMode: requestedPermission,
|
|
209
|
+
canUseTool: makeCanUseTool(sessionId, requestedPermission)
|
|
210
|
+
})
|
|
211
|
+
);
|
|
212
|
+
sessions.set(sessionId, session);
|
|
213
|
+
sessionConfig.set(sessionId, { model: requestedModel, permission: requestedPermission });
|
|
214
|
+
if (sessionCwd) sessionCwds.set(sessionId, sessionCwd);
|
|
201
215
|
return session;
|
|
202
216
|
};
|
|
203
217
|
|
|
@@ -365,13 +379,14 @@ chats.post("/new", async (c) => {
|
|
|
365
379
|
const { prompt, model, permission, repo } = await c.req.json();
|
|
366
380
|
if (!prompt) return c.json({ error: "Prompt is required" }, 400);
|
|
367
381
|
try {
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
382
|
+
const tempId = `temp_${Date.now()}`;
|
|
383
|
+
let finalSessionId = "";
|
|
384
|
+
const pendingPermQueue = [];
|
|
385
|
+
let sseStream = null;
|
|
386
|
+
const permCallback = async (req) => {
|
|
387
|
+
if (sseStream) {
|
|
373
388
|
try {
|
|
374
|
-
await
|
|
389
|
+
await sseStream.writeSSE({
|
|
375
390
|
data: JSON.stringify({
|
|
376
391
|
type: "permission_request",
|
|
377
392
|
...req,
|
|
@@ -381,14 +396,31 @@ chats.post("/new", async (c) => {
|
|
|
381
396
|
} catch (e) {
|
|
382
397
|
console.error("Failed to send permission SSE:", e);
|
|
383
398
|
}
|
|
384
|
-
}
|
|
385
|
-
|
|
399
|
+
} else {
|
|
400
|
+
pendingPermQueue.push(req);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
setPermissionCallback(tempId, permCallback);
|
|
404
|
+
const { session, repoPath } = await createChat({ model, permission, repo, prompt, tempId });
|
|
405
|
+
await sendMessage({ prompt, session });
|
|
406
|
+
return streamSSE(c, async (stream) => {
|
|
407
|
+
sseStream = stream;
|
|
408
|
+
for (const req of pendingPermQueue) {
|
|
409
|
+
try {
|
|
410
|
+
await stream.writeSSE({
|
|
411
|
+
data: JSON.stringify({ type: "permission_request", ...req, sessionId: finalSessionId || "pending" })
|
|
412
|
+
});
|
|
413
|
+
} catch (e) {
|
|
414
|
+
console.error(`[perm] flush failed: ${req.requestId}`, e);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
pendingPermQueue.length = 0;
|
|
386
418
|
try {
|
|
387
419
|
for await (const msg of session.stream()) {
|
|
388
420
|
if (msg.session_id && !finalSessionId) {
|
|
389
421
|
finalSessionId = msg.session_id;
|
|
390
422
|
activeSessions.add(finalSessionId);
|
|
391
|
-
setPermissionCallback(finalSessionId,
|
|
423
|
+
setPermissionCallback(finalSessionId, permCallback);
|
|
392
424
|
}
|
|
393
425
|
const text = getAssistantText(msg);
|
|
394
426
|
if (text)
|
|
@@ -433,26 +465,48 @@ chats.post("/:id/message", async (c) => {
|
|
|
433
465
|
if (!prompt || !sessionId || /[\/\\]/.test(sessionId))
|
|
434
466
|
return c.json({ error: "Invalid request" }, 400);
|
|
435
467
|
try {
|
|
468
|
+
const pendingPermQueue = [];
|
|
469
|
+
let sseStream = null;
|
|
470
|
+
setPermissionCallback(sessionId, async (req) => {
|
|
471
|
+
if (sseStream) {
|
|
472
|
+
try {
|
|
473
|
+
await sseStream.writeSSE({
|
|
474
|
+
data: JSON.stringify({ type: "permission_request", ...req, sessionId })
|
|
475
|
+
});
|
|
476
|
+
} catch (e) {
|
|
477
|
+
console.error(`[perm] SSE write failed: ${req.requestId}`, e);
|
|
478
|
+
}
|
|
479
|
+
} else {
|
|
480
|
+
pendingPermQueue.push(req);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
436
483
|
const session = await getSession({ sessionId, model, permission });
|
|
437
484
|
if (!session) return c.json({ error: "Session is required" }, 400);
|
|
438
485
|
await sendMessage({ prompt, session });
|
|
439
486
|
return streamSSE(c, async (stream) => {
|
|
440
487
|
activeSessions.add(sessionId);
|
|
441
|
-
|
|
488
|
+
sseStream = stream;
|
|
489
|
+
for (const req of pendingPermQueue) {
|
|
442
490
|
try {
|
|
443
491
|
await stream.writeSSE({
|
|
444
|
-
data: JSON.stringify({
|
|
445
|
-
type: "permission_request",
|
|
446
|
-
...req,
|
|
447
|
-
sessionId
|
|
448
|
-
})
|
|
492
|
+
data: JSON.stringify({ type: "permission_request", ...req, sessionId })
|
|
449
493
|
});
|
|
450
494
|
} catch (e) {
|
|
451
|
-
console.error(
|
|
495
|
+
console.error(`[perm] flush failed: ${req.requestId}`, e);
|
|
452
496
|
}
|
|
453
|
-
}
|
|
497
|
+
}
|
|
498
|
+
pendingPermQueue.length = 0;
|
|
454
499
|
try {
|
|
455
500
|
for await (const msg of session.stream()) {
|
|
501
|
+
const blocks = msg.message?.content;
|
|
502
|
+
if (Array.isArray(blocks)) {
|
|
503
|
+
for (const b of blocks) {
|
|
504
|
+
if (b.type === "tool_result" && b.is_error) {
|
|
505
|
+
const resultText = Array.isArray(b.content) ? b.content.filter((r) => r.type === "text").map((r) => r.text).join("").slice(0, 1e3) : String(b.content ?? "").slice(0, 1e3);
|
|
506
|
+
console.log(`[stream] tool_result ERROR: ${resultText.slice(0, 500)}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
456
510
|
const text = getAssistantText(msg);
|
|
457
511
|
if (text)
|
|
458
512
|
await stream.writeSSE({
|
|
@@ -470,6 +524,7 @@ chats.post("/:id/message", async (c) => {
|
|
|
470
524
|
generateChatName(prompt, sessionId).catch(() => {
|
|
471
525
|
});
|
|
472
526
|
} finally {
|
|
527
|
+
sseStream = null;
|
|
473
528
|
setPermissionCallback(sessionId, null);
|
|
474
529
|
activeSessions.delete(sessionId);
|
|
475
530
|
}
|
|
@@ -484,13 +539,13 @@ chats.post("/:id/permission", async (c) => {
|
|
|
484
539
|
if (!requestId || !behavior)
|
|
485
540
|
return c.json({ error: "requestId and behavior are required" }, 400);
|
|
486
541
|
const resolve = pendingPermissions.get(requestId);
|
|
487
|
-
if (!resolve)
|
|
542
|
+
if (!resolve) {
|
|
543
|
+
console.log(`[perm] no pending request: ${requestId}`);
|
|
488
544
|
return c.json({ error: "No pending permission request found" }, 404);
|
|
545
|
+
}
|
|
489
546
|
if (behavior === "allow") {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
...updatedPermissions ? { updatedPermissions } : {}
|
|
493
|
-
});
|
|
547
|
+
const originalInput = permissionInputs.get(requestId) || {};
|
|
548
|
+
resolve({ behavior: "allow", updatedInput: originalInput, ...updatedPermissions ? { updatedPermissions } : {} });
|
|
494
549
|
} else {
|
|
495
550
|
resolve({ behavior: "deny", message: "User denied permission" });
|
|
496
551
|
}
|
|
@@ -842,19 +897,15 @@ gh.get("/repos", async (c) => {
|
|
|
842
897
|
gh.get("/pr", async (c) => {
|
|
843
898
|
try {
|
|
844
899
|
const cwd = c.req.query("cwd");
|
|
845
|
-
console.log("[gh/pr GET] cwd:", cwd);
|
|
846
900
|
const root = await getRepoRoot(cwd);
|
|
847
|
-
console.log("[gh/pr GET] resolved root:", root);
|
|
848
901
|
if (!root) return c.json({ exists: false });
|
|
849
902
|
const { stdout } = await execAsync2(
|
|
850
903
|
"gh pr view --json number,title,url",
|
|
851
904
|
{ cwd: root }
|
|
852
905
|
);
|
|
853
906
|
const pr = JSON.parse(stdout);
|
|
854
|
-
console.log("[gh/pr GET] found PR:", pr);
|
|
855
907
|
return c.json({ exists: true, pr });
|
|
856
|
-
} catch
|
|
857
|
-
console.log("[gh/pr GET] no PR found:", err?.stderr?.trim() || err?.message);
|
|
908
|
+
} catch {
|
|
858
909
|
return c.json({ exists: false });
|
|
859
910
|
}
|
|
860
911
|
});
|