@koalaauto/agentchannel 0.0.0-stage → 3.0.0-rc.1
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/LICENSE +202 -0
- package/README.md +154 -2
- package/dist/src/adapters/claude.d.ts +65 -0
- package/dist/src/adapters/claude.js +243 -0
- package/dist/src/adapters/codex.d.ts +74 -0
- package/dist/src/adapters/codex.js +237 -0
- package/dist/src/adapters/fake.d.ts +65 -0
- package/dist/src/adapters/fake.js +105 -0
- package/dist/src/adapters/index.d.ts +11 -0
- package/dist/src/adapters/index.js +26 -0
- package/dist/src/adapters/paths.d.ts +35 -0
- package/dist/src/adapters/paths.js +113 -0
- package/dist/src/adapters/procgroup.d.ts +14 -0
- package/dist/src/adapters/procgroup.js +49 -0
- package/dist/src/adapters/types.d.ts +57 -0
- package/dist/src/adapters/types.js +1 -0
- package/dist/src/args.d.ts +14 -0
- package/dist/src/args.js +257 -0
- package/dist/src/attachments.d.ts +63 -0
- package/dist/src/attachments.js +227 -0
- package/dist/src/attention.d.ts +85 -0
- package/dist/src/attention.js +262 -0
- package/dist/src/avatars.d.ts +40 -0
- package/dist/src/avatars.js +90 -0
- package/dist/src/cli.d.ts +45 -0
- package/dist/src/cli.js +1822 -0
- package/dist/src/client.d.ts +520 -0
- package/dist/src/client.js +449 -0
- package/dist/src/config.d.ts +62 -0
- package/dist/src/config.js +121 -0
- package/dist/src/desknotify.d.ts +154 -0
- package/dist/src/desknotify.js +426 -0
- package/dist/src/doctor.d.ts +75 -0
- package/dist/src/doctor.js +375 -0
- package/dist/src/env.d.ts +31 -0
- package/dist/src/env.js +52 -0
- package/dist/src/import.d.ts +245 -0
- package/dist/src/import.js +1008 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +13 -0
- package/dist/src/invite.d.ts +103 -0
- package/dist/src/invite.js +229 -0
- package/dist/src/invite_person.d.ts +16 -0
- package/dist/src/invite_person.js +75 -0
- package/dist/src/keystore.d.ts +55 -0
- package/dist/src/keystore.js +108 -0
- package/dist/src/mcp.d.ts +62 -0
- package/dist/src/mcp.js +503 -0
- package/dist/src/names.d.ts +30 -0
- package/dist/src/names.js +108 -0
- package/dist/src/netproxy.d.ts +20 -0
- package/dist/src/netproxy.js +36 -0
- package/dist/src/onboard.d.ts +105 -0
- package/dist/src/onboard.js +270 -0
- package/dist/src/perm.d.ts +12 -0
- package/dist/src/perm.js +20 -0
- package/dist/src/prompts.d.ts +166 -0
- package/dist/src/prompts.js +409 -0
- package/dist/src/runner/runner.d.ts +212 -0
- package/dist/src/runner/runner.js +1148 -0
- package/dist/src/runner/state.d.ts +68 -0
- package/dist/src/runner/state.js +80 -0
- package/dist/src/runner/workspace.d.ts +70 -0
- package/dist/src/runner/workspace.js +265 -0
- package/dist/src/service.d.ts +29 -0
- package/dist/src/service.js +101 -0
- package/dist/src/version.d.ts +3 -0
- package/dist/src/version.js +3 -0
- package/dist/src/wiki.d.ts +74 -0
- package/dist/src/wiki.js +222 -0
- package/package.json +48 -4
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feishu avatars are fetched on the admin's machine (lark-cli is logged in
|
|
3
|
+
* there, the hub is not) and uploaded to the hub, which caches them; the panel
|
|
4
|
+
* only ever loads avatars from the hub. GitHub and Gravatar are fetched by the
|
|
5
|
+
* hub itself (POST /api/ach/admin/avatars/refresh, and weekly).
|
|
6
|
+
*/
|
|
7
|
+
export const AVATAR_MAX_BYTES = 200 * 1024;
|
|
8
|
+
export const AVATAR_SOURCES = ['upload', 'feishu', 'github', 'gravatar'];
|
|
9
|
+
/** png | jpeg | gif | webp from the first bytes, else '' (never SVG). */
|
|
10
|
+
export function imageKind(b) {
|
|
11
|
+
if (b.length < 12)
|
|
12
|
+
return '';
|
|
13
|
+
if (b[0] === 0x89 && b[1] === 0x50 && b[2] === 0x4e && b[3] === 0x47)
|
|
14
|
+
return 'png';
|
|
15
|
+
if (b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff)
|
|
16
|
+
return 'jpeg';
|
|
17
|
+
if (b[0] === 0x47 && b[1] === 0x49 && b[2] === 0x46 && b[3] === 0x38)
|
|
18
|
+
return 'gif';
|
|
19
|
+
if (b[0] === 0x52 && b[1] === 0x49 && b[2] === 0x46 && b[3] === 0x46 && b[8] === 0x57 && b[9] === 0x45 && b[10] === 0x42 && b[11] === 0x50)
|
|
20
|
+
return 'webp';
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
const EXT = { png: 'png', jpeg: 'jpg', gif: 'gif', webp: 'webp' };
|
|
24
|
+
/** The best avatar URL in a Feishu user object (search or contact result). */
|
|
25
|
+
export function feishuAvatarUrl(u) {
|
|
26
|
+
const a = u?.avatar ?? {};
|
|
27
|
+
const url = a.avatar_240 || a.avatar_640 || a.avatar_72 || a.avatar_origin || u?.avatar_url || '';
|
|
28
|
+
return typeof url === 'string' && /^https:\/\//.test(url) ? url : '';
|
|
29
|
+
}
|
|
30
|
+
/** Look a person up in Feishu by email (or open_id) with lark-cli; returns the avatar URL or ''. */
|
|
31
|
+
export async function feishuAvatarFor(exec, p) {
|
|
32
|
+
const q = String(p.email || p.name || '').trim();
|
|
33
|
+
if (!q)
|
|
34
|
+
return '';
|
|
35
|
+
const r = await exec('lark-cli', ['contact', '+search-user', '--query', q, '--as', 'user', '--exclude-external-users', '--format', 'json']);
|
|
36
|
+
if (r.code !== 0)
|
|
37
|
+
return '';
|
|
38
|
+
try {
|
|
39
|
+
const j = JSON.parse(r.stdout);
|
|
40
|
+
const users = j?.data?.users ?? j?.users ?? [];
|
|
41
|
+
const hit = users.find((u) => p.feishu_open_id && u.open_id === p.feishu_open_id) ??
|
|
42
|
+
users.find((u) => p.email && [u.email, u.enterprise_email].some((e) => String(e ?? '').toLowerCase() === String(p.email).toLowerCase()));
|
|
43
|
+
return hit ? feishuAvatarUrl(hit) : '';
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Download an image (<= 200 KB, PNG/JPEG/GIF/WebP). */
|
|
50
|
+
export async function downloadImage(url, fetchFn) {
|
|
51
|
+
const res = await fetchFn(url, { method: 'GET', headers: { accept: 'image/*' } });
|
|
52
|
+
if (!res.ok)
|
|
53
|
+
throw new Error(`HTTP ${res.status}`);
|
|
54
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
55
|
+
if (bytes.length > AVATAR_MAX_BYTES)
|
|
56
|
+
throw new Error('larger than 200 KB');
|
|
57
|
+
const kind = imageKind(bytes);
|
|
58
|
+
if (!kind)
|
|
59
|
+
throw new Error('not a PNG/JPEG/GIF/WebP image');
|
|
60
|
+
return { bytes, ext: EXT[kind] };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Upload Feishu avatars for these people (hub ids). Returns how many were
|
|
64
|
+
* uploaded; problems are logged, never fatal (an avatar is cosmetic).
|
|
65
|
+
*/
|
|
66
|
+
export async function syncFeishuAvatars(admin, people, o) {
|
|
67
|
+
const fetchFn = o.fetch ?? ((u, i) => fetch(u, i));
|
|
68
|
+
let n = 0;
|
|
69
|
+
for (const p of people) {
|
|
70
|
+
try {
|
|
71
|
+
const url = await feishuAvatarFor(o.exec, p);
|
|
72
|
+
if (!url)
|
|
73
|
+
continue;
|
|
74
|
+
const img = await downloadImage(url, fetchFn);
|
|
75
|
+
await admin.sendImage('POST', `/api/ach/admin/avatars/users/${encodeURIComponent(p.id)}`, img.bytes, `feishu.${img.ext}`, { source: 'feishu' });
|
|
76
|
+
n++;
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
o.log(`avatar for ${p.name || p.id}: ${e?.message ?? e}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return n;
|
|
83
|
+
}
|
|
84
|
+
export function parsePriority(s) {
|
|
85
|
+
const list = s.split(',').map((x) => x.trim().toLowerCase()).filter(Boolean);
|
|
86
|
+
for (const x of list)
|
|
87
|
+
if (!AVATAR_SOURCES.includes(x))
|
|
88
|
+
throw new Error(`unknown avatar source "${x}" (use ${AVATAR_SOURCES.join(', ')})`);
|
|
89
|
+
return list;
|
|
90
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type FetchLike } from './client.js';
|
|
3
|
+
export { VERSION } from './version.js';
|
|
4
|
+
export interface Io {
|
|
5
|
+
env: NodeJS.ProcessEnv;
|
|
6
|
+
/** Working directory for relative file arguments (default process.cwd()). */
|
|
7
|
+
cwd?: string;
|
|
8
|
+
out: (s: string) => void;
|
|
9
|
+
err: (s: string) => void;
|
|
10
|
+
fetch?: FetchLike;
|
|
11
|
+
readPassword?: (prompt: string) => Promise<string>;
|
|
12
|
+
/** `ach open`: opens a URL in the browser (tests inject a fake). */
|
|
13
|
+
openUrl?: (url: string) => Promise<void>;
|
|
14
|
+
/** `ach doctor` status commands (tests inject a fake). */
|
|
15
|
+
exec?: import('./doctor.js').ExecLike;
|
|
16
|
+
/** Asks a yes/no question (`ach join`); undefined = no terminal to ask in. */
|
|
17
|
+
ask?: (question: string) => Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
/** Runs the opener and waits for it: a non-zero exit (no browser, no display) is a failure, not "opened". */
|
|
20
|
+
export declare function runOpener(cmd: string, args: string[], spawn?: typeof import('node:child_process').spawn): Promise<void>;
|
|
21
|
+
/** A login link may be pasted as the full URL or as the bare token. */
|
|
22
|
+
export declare function loginTokenOf(s: string): string;
|
|
23
|
+
/** The channel named in a .ach-channel file in `dir` or a parent directory (for `ach memory hook`). */
|
|
24
|
+
export declare function channelFile(dir: string): string | undefined;
|
|
25
|
+
export declare const RUN_TOKEN_FORBIDDEN: string[];
|
|
26
|
+
/** Commands refused when the env looks like a spawned agent whose ACH_RUN_TOKEN was unset (advisory). */
|
|
27
|
+
export declare const SPAWNED_FORBIDDEN: string[];
|
|
28
|
+
export declare function spawnedAgentEnv(env: NodeJS.ProcessEnv): boolean;
|
|
29
|
+
export declare function forbiddenUnderRunToken(cmd: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Read a file argument. Inside a spawned agent the file must resolve (through
|
|
32
|
+
* realpath, so symlinks cannot escape) inside the current directory (S2):
|
|
33
|
+
* `ach complete --result-file ~/.agentchannel/config.json` must not leak secrets.
|
|
34
|
+
*/
|
|
35
|
+
export declare function readArgFile(file: string, io: Pick<Io, 'env' | 'cwd'>): string;
|
|
36
|
+
export declare function main(argv: string[], io?: Io): Promise<number>;
|
|
37
|
+
/**
|
|
38
|
+
* Write <home>/bin/ach, a shim that runs this exact CLI with this node, so spawned
|
|
39
|
+
* agents always find a matching `ach` on PATH (even when the runner was started
|
|
40
|
+
* with npx or `node dist/src/cli.js`). Returns the dir to prepend to PATH.
|
|
41
|
+
*/
|
|
42
|
+
export declare function writeAchShim(home: string): string | undefined;
|
|
43
|
+
/** B7: `ach run` exit code when the hub answers 401 (systemd: RestartPreventExitStatus=78). */
|
|
44
|
+
export declare const AUTH_EXIT_CODE = 78;
|
|
45
|
+
export declare const AUTH_EXIT_MESSAGE = "ach run: key rotated or agent deactivated \u2014 run ach join";
|