@agentshouse/kit 0.2.1-ce145e9aadbf6 → 0.2.1-cfe80fd914176
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/README.md +8 -9
- package/dist/acp.js +26 -13
- package/dist/agents.js +50 -28
- package/dist/api.js +30 -2
- package/dist/app-git.js +64 -0
- package/dist/bootstrap.json +4 -4
- package/dist/bridge.js +32 -39
- package/dist/clis.js +70 -69
- package/dist/conversations.js +279 -231
- package/dist/copies.js +3 -5
- package/dist/end.js +8 -0
- package/dist/events.js +587 -0
- package/dist/files.js +14 -0
- package/dist/house-main.js +28 -10
- package/dist/instructions.js +1 -1
- package/dist/kit-main.js +3 -0
- package/dist/login.js +4 -0
- package/dist/logout.js +3 -0
- package/dist/parts.js +64 -0
- package/dist/refusals.js +12 -0
- package/dist/resident.js +3 -3
- package/dist/secret-input.js +3 -2
- package/dist/shell.js +3 -0
- package/dist/sign-in.js +33 -9
- package/dist/stream.js +29 -4
- package/dist/transcription.js +54 -6
- package/dist/update.js +2 -0
- package/package.json +1 -1
- package/dist/blocks.js +0 -51
package/dist/clis.js
CHANGED
|
@@ -1,57 +1,50 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { constants, readFileSync } from 'node:fs';
|
|
3
|
+
import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { homedir, userInfo } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
5
|
+
import { delimiter, isAbsolute, join } from 'node:path';
|
|
6
6
|
import { kitHome } from './home.js';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
async function executable(path) {
|
|
8
|
+
try {
|
|
9
|
+
await access(path, constants.X_OK);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function allowHouseInCodex(path) {
|
|
17
|
+
const candidates = [...new Set(path.split(delimiter).filter((directory) => isAbsolute(directory)))].map((directory) => join(directory, 'house'));
|
|
18
|
+
const houses = [];
|
|
19
|
+
for (const candidate of candidates)
|
|
20
|
+
if (await executable(candidate))
|
|
21
|
+
houses.push(candidate);
|
|
9
22
|
const rules = join(process.env.CODEX_HOME || join(homedir(), '.codex'), 'rules');
|
|
10
23
|
await mkdir(rules, { recursive: true });
|
|
11
|
-
await writeFile(join(rules, 'house.rules'),
|
|
24
|
+
await writeFile(join(rules, 'house.rules'), `prefix_rule(pattern = ["house"], decision = "allow")\nhost_executable(name = "house", paths = ${JSON.stringify(houses)})\n`);
|
|
12
25
|
}
|
|
13
26
|
async function nothing() { }
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
return undefined;
|
|
17
|
-
return params.update._meta?.codex?.threadStatus?.type;
|
|
18
|
-
}
|
|
19
|
-
function asyncTask({ method, params }) {
|
|
20
|
-
const update = params?.update;
|
|
21
|
-
if (method !== 'session/update' || update?.asyncTaskId === undefined)
|
|
22
|
-
return null;
|
|
23
|
-
if (update.sessionUpdate === 'async_task_spawned')
|
|
24
|
-
return { id: update.asyncTaskId, running: true };
|
|
25
|
-
if (update.sessionUpdate === 'async_task_state_update' && !RUNNING_JOB.has(update.state ?? '')) {
|
|
26
|
-
return { id: update.asyncTaskId, running: false };
|
|
27
|
-
}
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
function airPhase({ _meta }) {
|
|
31
|
-
const phase = _meta?.jetbrains?.air?.phase;
|
|
32
|
-
if (phase === 'commentary')
|
|
33
|
-
return 'note';
|
|
34
|
-
if (phase === 'final_answer')
|
|
35
|
-
return 'answer';
|
|
36
|
-
return null;
|
|
27
|
+
function bridgeInClaudeSandbox(bridge) {
|
|
28
|
+
return process.platform === 'darwin' ? { allowUnixSockets: [bridge] } : { allowAllUnixSockets: true };
|
|
37
29
|
}
|
|
38
30
|
export const CLIS = {
|
|
39
31
|
'codex-acp': {
|
|
40
32
|
bin: 'codex',
|
|
41
33
|
minimum: '0.159.1',
|
|
42
34
|
install: 'curl -fsSL https://chatgpt.com/codex/install.sh | sh',
|
|
43
|
-
adapter: {
|
|
35
|
+
adapter: {
|
|
36
|
+
package: '@agentclientprotocol/codex-acp',
|
|
37
|
+
bin: 'codex-acp',
|
|
38
|
+
executable: 'CODEX_PATH',
|
|
39
|
+
env: { CODEX_CONFIG: JSON.stringify({ features: { shell_zsh_fork: true } }) },
|
|
40
|
+
},
|
|
44
41
|
args: [],
|
|
45
42
|
fullAccess: 'agent-full-access',
|
|
46
|
-
sessionMeta: {},
|
|
43
|
+
sessionMeta: () => ({}),
|
|
47
44
|
allowHouse: allowHouseInCodex,
|
|
48
45
|
signedIn: { command: ['login', 'status'] },
|
|
49
46
|
login: { args: ['login', '--device-auth'], code: 'show' },
|
|
50
|
-
|
|
51
|
-
turnStarted: (notice) => threadStatus(notice) === 'active',
|
|
52
|
-
turnEnded: (notice) => threadStatus(notice) === 'idle',
|
|
53
|
-
job: asyncTask,
|
|
54
|
-
phase: airPhase,
|
|
47
|
+
air: ['asyncTasks', 'nativeSubagentSessions'],
|
|
55
48
|
},
|
|
56
49
|
'claude-agent-acp': {
|
|
57
50
|
bin: 'claude',
|
|
@@ -65,17 +58,19 @@ export const CLIS = {
|
|
|
65
58
|
},
|
|
66
59
|
args: [],
|
|
67
60
|
fullAccess: 'bypassPermissions',
|
|
68
|
-
sessionMeta:
|
|
61
|
+
sessionMeta: (bridge) => ({
|
|
62
|
+
claudeCode: {
|
|
63
|
+
options: {
|
|
64
|
+
allowedTools: ['Bash(house:*)'],
|
|
65
|
+
settings: { sandbox: { network: bridgeInClaudeSandbox(bridge) } },
|
|
66
|
+
},
|
|
67
|
+
emitRawSDKMessages: ['background_tasks_changed', 'session_state_changed', 'task_started', 'task_notification'].map((subtype) => ({ type: 'system', subtype })),
|
|
68
|
+
},
|
|
69
|
+
}),
|
|
69
70
|
allowHouse: nothing,
|
|
70
71
|
signedIn: { command: ['auth', 'status'] },
|
|
71
72
|
login: { args: ['auth', 'login', '--claudeai'], code: 'collect', rejected: 'Invalid code' },
|
|
72
|
-
|
|
73
|
-
turnStarted: () => false,
|
|
74
|
-
turnEnded: ({ method, params }) => method === 'session/update' &&
|
|
75
|
-
params?.update?.sessionUpdate === 'usage_update' &&
|
|
76
|
-
params.update._meta?.['_claude/origin'] !== undefined,
|
|
77
|
-
job: asyncTask,
|
|
78
|
-
phase: () => null,
|
|
73
|
+
air: ['asyncTasks', 'sessionFailure'],
|
|
79
74
|
},
|
|
80
75
|
'grok-build': {
|
|
81
76
|
bin: 'grok',
|
|
@@ -84,28 +79,20 @@ export const CLIS = {
|
|
|
84
79
|
adapter: null,
|
|
85
80
|
args: ['agent', '--always-approve', '--no-leader', 'stdio'],
|
|
86
81
|
fullAccess: null,
|
|
87
|
-
sessionMeta: {},
|
|
82
|
+
sessionMeta: () => ({}),
|
|
88
83
|
allowHouse: nothing,
|
|
89
84
|
signedIn: { initializeMeta: 'defaultAuthMethodId' },
|
|
90
85
|
login: { args: ['login', '--device-auth'], code: 'show' },
|
|
91
|
-
|
|
92
|
-
turnStarted: ({ method, params }) => method === '_x.ai/queue/changed' && params?.runningPromptId !== undefined,
|
|
93
|
-
turnEnded: ({ method, params }) => method === '_x.ai/session_notification' && params?.update?.sessionUpdate === 'turn_completed',
|
|
94
|
-
job: ({ method, params }) => {
|
|
95
|
-
if (method === '_x.ai/task_backgrounded')
|
|
96
|
-
return { id: params.update.task_id, running: true };
|
|
97
|
-
if (method === '_x.ai/task_completed')
|
|
98
|
-
return { id: params.update.task_snapshot.task_id, running: false };
|
|
99
|
-
return null;
|
|
100
|
-
},
|
|
101
|
-
phase: () => null,
|
|
86
|
+
air: ['asyncTasks'],
|
|
102
87
|
},
|
|
103
88
|
};
|
|
104
89
|
const KIT_PACKAGE = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
105
90
|
export const KIT_VERSION = KIT_PACKAGE.version;
|
|
106
91
|
const PROXY = /^https?_proxy$/i;
|
|
107
92
|
const VERSION = /\d+\.\d+\.\d+(?:-[0-9A-Za-z.]+)?/;
|
|
93
|
+
// An install still running after fifteen minutes is ended and reported as failed, so a hung download cannot hold its CLI's report.
|
|
108
94
|
const INSTALL_MS = 15 * 60_000;
|
|
95
|
+
// A login shell or a version read is ended after half a minute, so a profile that waits for input cannot stall the report.
|
|
109
96
|
const READ_MS = 30_000;
|
|
110
97
|
const CAUSE_CHARACTERS = 4000;
|
|
111
98
|
function prefix(kind) {
|
|
@@ -133,8 +120,22 @@ export function run(command, args, timeoutMs, detached = false, signal) {
|
|
|
133
120
|
timeout: timeoutMs,
|
|
134
121
|
env: withoutProxy(),
|
|
135
122
|
detached,
|
|
136
|
-
signal,
|
|
123
|
+
signal: detached ? undefined : signal,
|
|
137
124
|
});
|
|
125
|
+
const stop = () => {
|
|
126
|
+
try {
|
|
127
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
128
|
+
}
|
|
129
|
+
catch { }
|
|
130
|
+
};
|
|
131
|
+
if (detached && signal?.aborted)
|
|
132
|
+
stop();
|
|
133
|
+
if (detached)
|
|
134
|
+
signal?.addEventListener('abort', stop, { once: true });
|
|
135
|
+
const ended = (ran) => {
|
|
136
|
+
signal?.removeEventListener('abort', stop);
|
|
137
|
+
resolve(ran);
|
|
138
|
+
};
|
|
138
139
|
let stdout = '';
|
|
139
140
|
let output = '';
|
|
140
141
|
child.stdout.setEncoding('utf8').on('data', (chunk) => {
|
|
@@ -144,25 +145,25 @@ export function run(command, args, timeoutMs, detached = false, signal) {
|
|
|
144
145
|
child.stderr.setEncoding('utf8').on('data', (chunk) => {
|
|
145
146
|
output += chunk;
|
|
146
147
|
});
|
|
147
|
-
child.on('error', (error) =>
|
|
148
|
-
child.on('close', (status) =>
|
|
148
|
+
child.on('error', (error) => ended({ status: null, stdout, output: error.message }));
|
|
149
|
+
child.on('close', (status) => ended({ status, stdout, output }));
|
|
149
150
|
});
|
|
150
151
|
}
|
|
151
152
|
export function failureOf(ran, program) {
|
|
152
153
|
return ran.output.trim().slice(-CAUSE_CHARACTERS) || `${program} exited with ${ran.status ?? 'a signal'}`;
|
|
153
154
|
}
|
|
154
|
-
export async function installAdapter(kind) {
|
|
155
|
+
export async function installAdapter(kind, signal) {
|
|
155
156
|
const adapter = CLIS[kind].adapter;
|
|
156
157
|
if (adapter === null)
|
|
157
158
|
return null;
|
|
158
159
|
const release = KIT_PACKAGE.peerDependencies[adapter.package];
|
|
159
160
|
if ((await installedAdapter(kind)) === release)
|
|
160
161
|
return null;
|
|
161
|
-
const ran = await run('npm', ['install', '--prefix', prefix(kind), '--no-save', '--no-audit', '--no-fund', '--omit=optional', `${adapter.package}@${release}`], INSTALL_MS);
|
|
162
|
+
const ran = await run('npm', ['install', '--prefix', prefix(kind), '--no-save', '--no-audit', '--no-fund', '--omit=optional', `${adapter.package}@${release}`], INSTALL_MS, true, signal);
|
|
162
163
|
return ran.status === 0 ? null : failureOf(ran, 'npm');
|
|
163
164
|
}
|
|
164
|
-
export async function installCli(kind) {
|
|
165
|
-
const ran = await run('bash', ['-o', 'pipefail', '-c', CLIS[kind].install], INSTALL_MS, true);
|
|
165
|
+
export async function installCli(kind, signal) {
|
|
166
|
+
const ran = await run('bash', ['-o', 'pipefail', '-c', CLIS[kind].install], INSTALL_MS, true, signal);
|
|
166
167
|
return ran.status === 0 ? null : failureOf(ran, 'the install');
|
|
167
168
|
}
|
|
168
169
|
function loginShell() {
|
|
@@ -171,11 +172,11 @@ function loginShell() {
|
|
|
171
172
|
const FOUND = 'house-kit-cli ';
|
|
172
173
|
const LOOKUP = `found=$(command -v "$1") || exit 0; case $found in /*) ;; *) found=$(pwd -P)/$found ;; esac; printf "${FOUND}%s\\n" "$found"`;
|
|
173
174
|
const USER_BIN = 'case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) PATH="$PATH:$HOME/.local/bin" ;; esac; export PATH';
|
|
174
|
-
function inLoginShell(script, argument = '') {
|
|
175
|
-
return run(loginShell(), ['-l', '-i', '-c', `exec /bin/sh -c '${USER_BIN}; ${script}' sh ${argument}`], READ_MS, true);
|
|
175
|
+
function inLoginShell(script, argument = '', signal) {
|
|
176
|
+
return run(loginShell(), ['-l', '-i', '-c', `exec /bin/sh -c '${USER_BIN}; ${script}' sh ${argument}`], READ_MS, true, signal);
|
|
176
177
|
}
|
|
177
|
-
export async function locate(kind) {
|
|
178
|
-
const ran = await inLoginShell(LOOKUP, CLIS[kind].bin);
|
|
178
|
+
export async function locate(kind, signal) {
|
|
179
|
+
const ran = await inLoginShell(LOOKUP, CLIS[kind].bin, signal);
|
|
179
180
|
return ran.stdout.split('\n').findLast((line) => line.startsWith(FOUND))?.slice(FOUND.length) ?? null;
|
|
180
181
|
}
|
|
181
182
|
export async function loginPath() {
|
|
@@ -183,8 +184,8 @@ export async function loginPath() {
|
|
|
183
184
|
const ran = await inLoginShell(`printf "${marker}%s\\n" "$PATH"`);
|
|
184
185
|
return ran.stdout.split('\n').findLast((line) => line.startsWith(marker)).slice(marker.length);
|
|
185
186
|
}
|
|
186
|
-
export async function versionOf(path) {
|
|
187
|
-
return VERSION.exec((await run(path, ['--version'], READ_MS)).stdout)?.[0] ?? null;
|
|
187
|
+
export async function versionOf(path, signal) {
|
|
188
|
+
return VERSION.exec((await run(path, ['--version'], READ_MS, false, signal)).stdout)?.[0] ?? null;
|
|
188
189
|
}
|
|
189
190
|
function numbers(version) {
|
|
190
191
|
return version.split(/[.-]/, 3).map(Number);
|