@lambdacurry/arbor 0.20.32 → 0.20.33
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/arbor.js +106 -12
- package/package.json +1 -1
package/dist/arbor.js
CHANGED
|
@@ -20993,11 +20993,70 @@ var ACTIONS = ACTION_DEFINITIONS.map((action) => {
|
|
|
20993
20993
|
});
|
|
20994
20994
|
|
|
20995
20995
|
// src/config.ts
|
|
20996
|
-
import {
|
|
20996
|
+
import {
|
|
20997
|
+
chmodSync,
|
|
20998
|
+
existsSync,
|
|
20999
|
+
mkdirSync,
|
|
21000
|
+
readFileSync,
|
|
21001
|
+
rmSync,
|
|
21002
|
+
statSync,
|
|
21003
|
+
writeFileSync
|
|
21004
|
+
} from "node:fs";
|
|
20997
21005
|
import { homedir } from "node:os";
|
|
20998
21006
|
import { dirname, join } from "node:path";
|
|
20999
21007
|
var CONFIG_PATH = process.env.ARBOR_CONFIG ?? join(homedir(), ".arbor", "config.json");
|
|
21000
21008
|
var DEFAULT_API_URL = process.env.ARBOR_API_URL ?? "https://arborthreads.com";
|
|
21009
|
+
var DEFAULT_CONFIG_PATH = join(homedir(), ".arbor", "config.json");
|
|
21010
|
+
var DEFAULT_TOKEN_OVERWRITE_REFUSAL = "refusing to overwrite ~/.arbor/config.json — a token already exists. Set ARBOR_CONFIG to an isolated path (e.g. ~/.arbor/<agent>.config.json) and retry.";
|
|
21011
|
+
var LOCK_TIMEOUT_MS = 5000;
|
|
21012
|
+
var LOCK_STALE_MS = 30000;
|
|
21013
|
+
var LOCK_POLL_MS = 20;
|
|
21014
|
+
function sleepSync(ms) {
|
|
21015
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
21016
|
+
}
|
|
21017
|
+
function acquireConfigLock(configPath) {
|
|
21018
|
+
const lockPath = `${configPath}.lock`;
|
|
21019
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
21020
|
+
for (;; ) {
|
|
21021
|
+
try {
|
|
21022
|
+
mkdirSync(lockPath);
|
|
21023
|
+
return () => {
|
|
21024
|
+
try {
|
|
21025
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
21026
|
+
} catch {}
|
|
21027
|
+
};
|
|
21028
|
+
} catch (err) {
|
|
21029
|
+
if (err?.code !== "EEXIST")
|
|
21030
|
+
throw err;
|
|
21031
|
+
try {
|
|
21032
|
+
if (Date.now() - statSync(lockPath).mtimeMs > LOCK_STALE_MS) {
|
|
21033
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
21034
|
+
continue;
|
|
21035
|
+
}
|
|
21036
|
+
} catch {
|
|
21037
|
+
continue;
|
|
21038
|
+
}
|
|
21039
|
+
if (Date.now() >= deadline) {
|
|
21040
|
+
throw new Error(`timed out waiting for another arbor process to finish writing ${configPath}. If none is running, remove ${lockPath}.`);
|
|
21041
|
+
}
|
|
21042
|
+
sleepSync(LOCK_POLL_MS);
|
|
21043
|
+
}
|
|
21044
|
+
}
|
|
21045
|
+
}
|
|
21046
|
+
function defaultConfigOverwriteBlocked(opts) {
|
|
21047
|
+
const isolated = opts?.isolated ?? Boolean(process.env.ARBOR_CONFIG);
|
|
21048
|
+
const path = opts?.path ?? DEFAULT_CONFIG_PATH;
|
|
21049
|
+
if (isolated)
|
|
21050
|
+
return false;
|
|
21051
|
+
if (!existsSync(path))
|
|
21052
|
+
return false;
|
|
21053
|
+
try {
|
|
21054
|
+
const cfg = JSON.parse(readFileSync(path, "utf8"));
|
|
21055
|
+
return Boolean(cfg.token);
|
|
21056
|
+
} catch {
|
|
21057
|
+
return false;
|
|
21058
|
+
}
|
|
21059
|
+
}
|
|
21001
21060
|
function loadConfig() {
|
|
21002
21061
|
const envToken = process.env.ARBOR_TOKEN || undefined;
|
|
21003
21062
|
const envUrl = process.env.ARBOR_API_URL || undefined;
|
|
@@ -21009,15 +21068,32 @@ function loadConfig() {
|
|
|
21009
21068
|
}
|
|
21010
21069
|
return { apiUrl: envUrl ?? DEFAULT_API_URL, token: envToken };
|
|
21011
21070
|
}
|
|
21012
|
-
function
|
|
21013
|
-
mkdirSync(dirname(
|
|
21014
|
-
|
|
21015
|
-
|
|
21016
|
-
|
|
21071
|
+
function writeConfigGuarded(opts) {
|
|
21072
|
+
mkdirSync(dirname(opts.configPath), { recursive: true });
|
|
21073
|
+
const release = acquireConfigLock(opts.configPath);
|
|
21074
|
+
try {
|
|
21075
|
+
if (!opts.allowDefaultTokenOverwrite && defaultConfigOverwriteBlocked({ isolated: opts.isolated, path: opts.defaultPath })) {
|
|
21076
|
+
throw new Error(DEFAULT_TOKEN_OVERWRITE_REFUSAL);
|
|
21077
|
+
}
|
|
21078
|
+
writeFileSync(opts.configPath, `${JSON.stringify(opts.cfg, null, 2)}
|
|
21079
|
+
`, { mode: 384 });
|
|
21080
|
+
chmodSync(opts.configPath, 384);
|
|
21081
|
+
} finally {
|
|
21082
|
+
release();
|
|
21083
|
+
}
|
|
21084
|
+
}
|
|
21085
|
+
function saveConfig(cfg, opts) {
|
|
21086
|
+
writeConfigGuarded({
|
|
21087
|
+
cfg,
|
|
21088
|
+
configPath: CONFIG_PATH,
|
|
21089
|
+
defaultPath: DEFAULT_CONFIG_PATH,
|
|
21090
|
+
isolated: Boolean(process.env.ARBOR_CONFIG),
|
|
21091
|
+
allowDefaultTokenOverwrite: opts?.allowDefaultTokenOverwrite
|
|
21092
|
+
});
|
|
21017
21093
|
}
|
|
21018
21094
|
function clearToken() {
|
|
21019
21095
|
const cfg = loadConfig();
|
|
21020
|
-
saveConfig({ apiUrl: cfg.apiUrl });
|
|
21096
|
+
saveConfig({ apiUrl: cfg.apiUrl }, { allowDefaultTokenOverwrite: true });
|
|
21021
21097
|
}
|
|
21022
21098
|
|
|
21023
21099
|
// src/errors.ts
|
|
@@ -21747,20 +21823,38 @@ async function runObjectVerb(positionals, flags, ctx) {
|
|
|
21747
21823
|
// src/connect.ts
|
|
21748
21824
|
var DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
21749
21825
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
21826
|
+
function bareConnectUrl(apiUrl, returnedUrl) {
|
|
21827
|
+
const fallback = `${apiUrl.replace(/\/$/, "")}/connect`;
|
|
21828
|
+
if (!returnedUrl)
|
|
21829
|
+
return fallback;
|
|
21830
|
+
try {
|
|
21831
|
+
return `${new URL(returnedUrl).origin}/connect`;
|
|
21832
|
+
} catch {
|
|
21833
|
+
return fallback;
|
|
21834
|
+
}
|
|
21835
|
+
}
|
|
21750
21836
|
async function connect(opts) {
|
|
21837
|
+
if (defaultConfigOverwriteBlocked()) {
|
|
21838
|
+
throw new Error(DEFAULT_TOKEN_OVERWRITE_REFUSAL);
|
|
21839
|
+
}
|
|
21751
21840
|
const apiUrl = opts.url ?? loadConfig().apiUrl;
|
|
21752
21841
|
const pollIntervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
21753
|
-
const body = JSON.stringify({ pairingKey: opts.pairingKey });
|
|
21754
21842
|
const headers = { "content-type": "application/json" };
|
|
21755
|
-
const
|
|
21843
|
+
const body = JSON.stringify({ pairingKey: opts.pairingKey });
|
|
21844
|
+
const initRes = await fetch(`${apiUrl}/api/agent/connect`, {
|
|
21845
|
+
method: "POST",
|
|
21846
|
+
headers,
|
|
21847
|
+
body
|
|
21848
|
+
});
|
|
21756
21849
|
const init = await initRes.json().catch(() => ({}));
|
|
21757
21850
|
if (!initRes.ok || !init.url || !init.userCode) {
|
|
21758
21851
|
throw new Error(`could not start pairing (${initRes.status}): ${init.error ?? "is the server reachable?"}`);
|
|
21759
21852
|
}
|
|
21853
|
+
const approvalUrl = bareConnectUrl(apiUrl, init.url);
|
|
21760
21854
|
process.stderr.write(`
|
|
21761
21855
|
Pairing against ${apiUrl}
|
|
21762
21856
|
` + ` Ask the person who will manage this agent${init.agentName ? ` (“${init.agentName}”)` : ""} to approve it:
|
|
21763
|
-
` + ` 1. open ${
|
|
21857
|
+
` + ` 1. open ${approvalUrl}
|
|
21764
21858
|
` + ` 2. enter the code: ${init.userCode}
|
|
21765
21859
|
|
|
21766
21860
|
` + ` Only approve an agent you intend to manage.
|
|
@@ -21835,7 +21929,7 @@ async function login(opts) {
|
|
|
21835
21929
|
});
|
|
21836
21930
|
const tok = await tokRes.json().catch(() => ({}));
|
|
21837
21931
|
if (tok.access_token) {
|
|
21838
|
-
saveConfig({ apiUrl, token: tok.access_token });
|
|
21932
|
+
saveConfig({ apiUrl, token: tok.access_token }, { allowDefaultTokenOverwrite: true });
|
|
21839
21933
|
return;
|
|
21840
21934
|
}
|
|
21841
21935
|
if (tok.error === "authorization_pending")
|
|
@@ -22037,7 +22131,7 @@ async function main() {
|
|
|
22037
22131
|
if (!token)
|
|
22038
22132
|
throw new UsageError("usage: arbor auth <token> [--url <api-url>]");
|
|
22039
22133
|
const url2 = stringFlag(flags.url, "url") ?? loadConfig().apiUrl;
|
|
22040
|
-
saveConfig({ apiUrl: url2, token });
|
|
22134
|
+
saveConfig({ apiUrl: url2, token }, { allowDefaultTokenOverwrite: true });
|
|
22041
22135
|
advise(` ✓ Token saved (${CONFIG_PATH}).
|
|
22042
22136
|
`, ctx);
|
|
22043
22137
|
await renderMe(ctx, "auth");
|
package/package.json
CHANGED