arcroom 0.0.1-beta → 0.0.3-beta
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/agents.js +1 -1
- package/dist/git.js +2 -1
- package/dist/home.js +16 -1
- package/dist/index.js +6 -0
- package/dist/init.js +23 -4
- package/package.json +1 -1
package/dist/agents.js
CHANGED
|
@@ -112,7 +112,7 @@ export function opencodePlugin(entry, project, node = process.execPath) {
|
|
|
112
112
|
import { spawnSync } from 'node:child_process';
|
|
113
113
|
|
|
114
114
|
const run = (payload) => {
|
|
115
|
-
const r = spawnSync(${JSON.stringify(node)}, ${args}, { input: JSON.stringify(payload), encoding: 'utf8' });
|
|
115
|
+
const r = spawnSync(${JSON.stringify(node)}, ${args}, { input: JSON.stringify(payload), encoding: 'utf8', windowsHide: true });
|
|
116
116
|
try { return JSON.parse(r.stdout || '{}'); } catch { return {}; }
|
|
117
117
|
};
|
|
118
118
|
|
package/dist/git.js
CHANGED
|
@@ -31,6 +31,7 @@ export function git(args, opts = {}) {
|
|
|
31
31
|
const bin = gitPath();
|
|
32
32
|
if (!bin)
|
|
33
33
|
return { status: null, stdout: undefined };
|
|
34
|
-
|
|
34
|
+
// hooks run with no console of their own: without windowsHide each git call flashes a window on Windows
|
|
35
|
+
const r = spawnSync(bin, [...SAFE, ...args], { encoding: 'utf8', windowsHide: true, ...opts });
|
|
35
36
|
return { status: r.status, stdout: r.stdout };
|
|
36
37
|
}
|
package/dist/home.js
CHANGED
|
@@ -41,6 +41,15 @@ export function loadConfig() {
|
|
|
41
41
|
return { url: DEFAULT_URL, apiKey: '', ...(existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : {}) };
|
|
42
42
|
}
|
|
43
43
|
const LOCAL = new Set(['localhost', '127.0.0.1', '[::1]']);
|
|
44
|
+
/** Whether `url` points at this machine (a developer's local API). */
|
|
45
|
+
export const isLocalUrl = (url) => {
|
|
46
|
+
try {
|
|
47
|
+
return LOCAL.has(new URL(url).hostname);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
44
53
|
/** The API URL, refused unless it is https:// or this machine: the key and the traces never cross a network in clear text. */
|
|
45
54
|
export function apiUrl(url) {
|
|
46
55
|
const u = new URL(url);
|
|
@@ -54,11 +63,17 @@ export function apiUrl(url) {
|
|
|
54
63
|
* secret to the team.
|
|
55
64
|
*/
|
|
56
65
|
export async function api(cfg, path, body, timeoutMs = 10_000) {
|
|
57
|
-
const
|
|
66
|
+
const base = apiUrl(cfg.url);
|
|
67
|
+
const res = await fetch(base + path, {
|
|
58
68
|
method: body ? 'POST' : 'GET',
|
|
59
69
|
headers: { Authorization: `Bearer ${cfg.apiKey}`, 'Content-Type': 'application/json' },
|
|
60
70
|
body: body ? JSON.stringify(redact(body)) : undefined,
|
|
61
71
|
signal: AbortSignal.timeout(timeoutMs),
|
|
72
|
+
}).catch((e) => {
|
|
73
|
+
// a timeout stays as it is (lookup and the MCP tools check for TimeoutError); a refused or unknown host reads as one line
|
|
74
|
+
const cause = e.cause;
|
|
75
|
+
const why = cause?.code ?? cause?.errors?.[0]?.code ?? cause?.message ?? e.message;
|
|
76
|
+
throw e.name === 'TypeError' ? new Error(`could not reach ${base} (${why})`) : e;
|
|
62
77
|
});
|
|
63
78
|
const json = (await res.json());
|
|
64
79
|
// the key was revoked, its person removed or its account deleted: ArcRoom stops on this machine until a new key
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,12 @@ import { mcp } from './mcp.js';
|
|
|
13
13
|
import { readStats, statusText } from './signs.js';
|
|
14
14
|
import { sessionUsage, tokensOf } from './usage.js';
|
|
15
15
|
const [cmd, ...args] = process.argv.slice(2);
|
|
16
|
+
// A command someone typed that fails prints one line, not a stack trace (the hook, daemon and MCP server keep their own handling).
|
|
17
|
+
if (!['hook', 'daemon', 'mcp', 'status'].includes(cmd))
|
|
18
|
+
process.on('uncaughtException', (e) => {
|
|
19
|
+
console.error(`arcroom${cmd ? ` ${cmd}` : ''}: ${e.message}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
});
|
|
16
22
|
if (cmd === 'init')
|
|
17
23
|
await init(args);
|
|
18
24
|
else if (cmd === 'hook')
|
package/dist/init.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createInterface } from 'node:readline/promises';
|
|
|
6
6
|
import { AGENTS, CURSOR_HOOKS, opencodePlugin } from './agents.js';
|
|
7
7
|
import { stopDaemon } from './daemon.js';
|
|
8
8
|
import { onPath } from './git.js';
|
|
9
|
-
import { apiUrl, ARCROOM_HOME, DEFAULT_URL } from './home.js';
|
|
9
|
+
import { apiUrl, ARCROOM_HOME, DEFAULT_URL, isLocalUrl, keyRefused } from './home.js';
|
|
10
10
|
const EVENTS = ['SessionStart', 'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'Stop', 'SessionEnd'];
|
|
11
11
|
const TOOL_EVENTS = new Set(['PreToolUse', 'PostToolUse']);
|
|
12
12
|
const MARK = '.arcroom/runtime'; // identifies our hook entries in a settings file
|
|
@@ -43,6 +43,21 @@ const writeJson = (file, value) => {
|
|
|
43
43
|
mkdirSync(dirname(file), { recursive: true });
|
|
44
44
|
writeFileSync(file, JSON.stringify(value, null, 2) + '\n');
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* The key and API URL init uses. Re-running init reuses what was saved, except a key the API refused (otherwise a new
|
|
48
|
+
* key could never get in) and a local URL left from development (it moves to the live API and says so).
|
|
49
|
+
*/
|
|
50
|
+
export function initChoice(o) {
|
|
51
|
+
const refused = !!o.saved.apiKey && keyRefused({ apiKey: o.saved.apiKey, url: o.saved.url ?? DEFAULT_URL });
|
|
52
|
+
const key = o.argKey ?? o.envKey ?? (refused ? undefined : o.saved.apiKey);
|
|
53
|
+
const dropLocal = !o.argUrl && !!o.saved.url && isLocalUrl(o.saved.url);
|
|
54
|
+
const url = o.argUrl ?? (dropLocal ? DEFAULT_URL : o.saved.url ?? DEFAULT_URL);
|
|
55
|
+
const notes = [
|
|
56
|
+
...(refused && !o.argKey && !o.envKey ? ['The saved key was refused by the API. Paste a new key (web app, Settings → API keys).'] : []),
|
|
57
|
+
...(dropLocal ? [`The saved API was ${o.saved.url}; using ${DEFAULT_URL} now (arcroom init --url ${o.saved.url} keeps a local API).`] : []),
|
|
58
|
+
];
|
|
59
|
+
return { key, url, notes };
|
|
60
|
+
}
|
|
46
61
|
export async function init(args) {
|
|
47
62
|
const arg = (name) => (args.includes(name) ? args[args.indexOf(name) + 1] : undefined);
|
|
48
63
|
const agent = (arg('--agent') ?? 'claude');
|
|
@@ -52,10 +67,13 @@ export async function init(args) {
|
|
|
52
67
|
}
|
|
53
68
|
const configFile = join(ARCROOM_HOME, 'config.json');
|
|
54
69
|
const saved = existsSync(configFile) ? JSON.parse(readFileSync(configFile, 'utf8')) : {}; // re-running init reuses saved values
|
|
55
|
-
|
|
70
|
+
const choice = initChoice({ argKey: arg('--key'), envKey: process.env.ARCROOM_API_KEY, argUrl: arg('--url'), saved });
|
|
71
|
+
let key = choice.key;
|
|
72
|
+
const url = choice.url;
|
|
56
73
|
if (arg('--key'))
|
|
57
74
|
console.error('Note: a key typed on the command line stays in your shell history. Next time run arcroom init and paste it when asked, or set ARCROOM_API_KEY.');
|
|
58
|
-
const
|
|
75
|
+
for (const note of choice.notes)
|
|
76
|
+
console.error(note);
|
|
59
77
|
try {
|
|
60
78
|
apiUrl(url);
|
|
61
79
|
}
|
|
@@ -138,9 +156,10 @@ function addLocalMcp(entry) {
|
|
|
138
156
|
const run = (args) => claude.endsWith('.cmd') // an npm install on Windows: a batch file runs through cmd, every argument quoted
|
|
139
157
|
? spawnSync(process.env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', `"${[claude, ...args].map((a) => `"${a}"`).join(' ')}"`], {
|
|
140
158
|
windowsVerbatimArguments: true,
|
|
159
|
+
windowsHide: true,
|
|
141
160
|
encoding: 'utf8',
|
|
142
161
|
})
|
|
143
|
-
: spawnSync(claude, args, { encoding: 'utf8' });
|
|
162
|
+
: spawnSync(claude, args, { encoding: 'utf8', windowsHide: true });
|
|
144
163
|
run(['mcp', 'remove', '--scope', 'local', 'arcroom']); // none yet is fine
|
|
145
164
|
return run(['mcp', 'add', '--scope', 'local', 'arcroom', '--', NODE, entry, 'mcp']).status === 0;
|
|
146
165
|
}
|
package/package.json
CHANGED