@agent-idle/cli 0.1.3 → 0.1.4
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/index.js +64 -16
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -177,6 +177,18 @@ function resolveConvexUrl() {
|
|
|
177
177
|
}
|
|
178
178
|
var DEFAULT_CONVEX_URL = resolveConvexUrl();
|
|
179
179
|
var AUTH_URL = process.env.AGENT_IDLE_AUTH_URL ?? BUILD_AUTH_URL ?? "http://localhost:1420";
|
|
180
|
+
function cliAuthUrl(userCode2) {
|
|
181
|
+
try {
|
|
182
|
+
const url = new URL(AUTH_URL);
|
|
183
|
+
url.searchParams.set("login", "1");
|
|
184
|
+
if (userCode2) url.searchParams.set("code", userCode2);
|
|
185
|
+
return url.toString();
|
|
186
|
+
} catch {
|
|
187
|
+
const join2 = AUTH_URL.includes("?") ? "&" : "?";
|
|
188
|
+
const code = userCode2 ? `&code=${encodeURIComponent(userCode2)}` : "";
|
|
189
|
+
return `${AUTH_URL}${join2}login=1${code}`;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
180
192
|
function ensureDir(path, mode) {
|
|
181
193
|
const dir = dirname(path);
|
|
182
194
|
mkdirSync(dir, { recursive: true, ...mode !== void 0 ? { mode } : {} });
|
|
@@ -244,19 +256,6 @@ async function pingDaemon() {
|
|
|
244
256
|
return false;
|
|
245
257
|
}
|
|
246
258
|
}
|
|
247
|
-
async function daemonToken() {
|
|
248
|
-
try {
|
|
249
|
-
const controller = new AbortController();
|
|
250
|
-
const timer = setTimeout(() => controller.abort(), 800);
|
|
251
|
-
const res = await fetch(`${DAEMON_URL}/token`, { signal: controller.signal });
|
|
252
|
-
clearTimeout(timer);
|
|
253
|
-
if (!res.ok) return null;
|
|
254
|
-
const { token } = await res.json();
|
|
255
|
-
return token ?? null;
|
|
256
|
-
} catch {
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
259
|
|
|
261
260
|
// src/outbox.ts
|
|
262
261
|
import { appendFileSync, chmodSync as chmodSync2, existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -1279,7 +1278,46 @@ import { cancel, isCancel, multiselect } from "@clack/prompts";
|
|
|
1279
1278
|
|
|
1280
1279
|
// src/auth.ts
|
|
1281
1280
|
import { spawn as spawn2 } from "child_process";
|
|
1281
|
+
import { randomBytes, randomUUID as randomUUID2, webcrypto } from "crypto";
|
|
1282
|
+
import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
|
|
1283
|
+
import { makeFunctionReference as makeFunctionReference2 } from "convex/server";
|
|
1282
1284
|
var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1285
|
+
var createDeviceAuth = makeFunctionReference2("deviceAuth:create");
|
|
1286
|
+
var pollDeviceAuth = makeFunctionReference2("deviceAuth:poll");
|
|
1287
|
+
function userCode() {
|
|
1288
|
+
return randomBytes(5).toString("hex").toUpperCase();
|
|
1289
|
+
}
|
|
1290
|
+
function b64ToBytes(value) {
|
|
1291
|
+
return Buffer.from(value, "base64");
|
|
1292
|
+
}
|
|
1293
|
+
async function createDeviceKeyPair() {
|
|
1294
|
+
const keyPair = await webcrypto.subtle.generateKey(
|
|
1295
|
+
{
|
|
1296
|
+
name: "RSA-OAEP",
|
|
1297
|
+
modulusLength: 2048,
|
|
1298
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
1299
|
+
hash: "SHA-256"
|
|
1300
|
+
},
|
|
1301
|
+
true,
|
|
1302
|
+
["encrypt", "decrypt"]
|
|
1303
|
+
);
|
|
1304
|
+
const publicJwk = await webcrypto.subtle.exportKey("jwk", keyPair.publicKey);
|
|
1305
|
+
return { publicKeyJwk: JSON.stringify(publicJwk), privateKey: keyPair.privateKey };
|
|
1306
|
+
}
|
|
1307
|
+
async function decryptToken(payload, privateKey) {
|
|
1308
|
+
const rawKey = await webcrypto.subtle.decrypt(
|
|
1309
|
+
{ name: "RSA-OAEP" },
|
|
1310
|
+
privateKey,
|
|
1311
|
+
b64ToBytes(payload.encryptedKey)
|
|
1312
|
+
);
|
|
1313
|
+
const key = await webcrypto.subtle.importKey("raw", rawKey, { name: "AES-GCM" }, false, ["decrypt"]);
|
|
1314
|
+
const plaintext = await webcrypto.subtle.decrypt(
|
|
1315
|
+
{ name: "AES-GCM", iv: b64ToBytes(payload.iv) },
|
|
1316
|
+
key,
|
|
1317
|
+
b64ToBytes(payload.ciphertext)
|
|
1318
|
+
);
|
|
1319
|
+
return new TextDecoder().decode(plaintext);
|
|
1320
|
+
}
|
|
1283
1321
|
function openBrowser(url) {
|
|
1284
1322
|
const [bin, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
1285
1323
|
try {
|
|
@@ -1300,21 +1338,31 @@ async function signIn() {
|
|
|
1300
1338
|
spawnDaemon();
|
|
1301
1339
|
const settleBy = Date.now() + 5e3;
|
|
1302
1340
|
while (Date.now() < settleBy && !await pingDaemon()) await sleep3(250);
|
|
1341
|
+
const client = new ConvexHttpClient2(resolveConvexUrl());
|
|
1342
|
+
const deviceId = randomUUID2();
|
|
1343
|
+
const code = userCode();
|
|
1344
|
+
const { publicKeyJwk, privateKey } = await createDeviceKeyPair();
|
|
1345
|
+
await client.mutation(createDeviceAuth, { deviceId, userCode: code, publicKeyJwk });
|
|
1346
|
+
const authUrl = cliAuthUrl(code);
|
|
1303
1347
|
console.log(`
|
|
1304
|
-
Opening ${
|
|
1348
|
+
Opening ${authUrl} to sign in (GitHub or email + password)\u2026`);
|
|
1349
|
+
console.log(`Your device code is ${code}.`);
|
|
1305
1350
|
console.log(
|
|
1306
1351
|
"If it doesn't open, visit that URL manually. Waiting for sign-in\u2026 (Ctrl-C to cancel)"
|
|
1307
1352
|
);
|
|
1308
|
-
openBrowser(
|
|
1353
|
+
openBrowser(authUrl);
|
|
1309
1354
|
const deadline = Date.now() + 12e4;
|
|
1310
1355
|
while (Date.now() < deadline) {
|
|
1311
1356
|
await sleep3(1500);
|
|
1312
|
-
|
|
1357
|
+
const res = await client.mutation(pollDeviceAuth, { deviceId });
|
|
1358
|
+
if (res.status === "approved") {
|
|
1359
|
+
writeToken(await decryptToken(res.encryptedToken, privateKey));
|
|
1313
1360
|
console.log(
|
|
1314
1361
|
"\u2713 Signed in \u2014 shared session established for the app, CLI, and daemon."
|
|
1315
1362
|
);
|
|
1316
1363
|
return;
|
|
1317
1364
|
}
|
|
1365
|
+
if (res.status === "expired") break;
|
|
1318
1366
|
}
|
|
1319
1367
|
console.log(
|
|
1320
1368
|
"Timed out waiting for sign-in. Re-run `agent-idle setup` once you've signed in."
|