@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,14 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
export declare const KILL_GRACE_MS = 5000;
|
|
3
|
+
/** Kill a whole process group: SIGTERM now, SIGKILL after the grace period if still alive. */
|
|
4
|
+
export declare function killGroup(child: ChildProcess, graceMs?: number): void;
|
|
5
|
+
/**
|
|
6
|
+
* Spawn in its own process group (detached) so cancel can kill the agent and every
|
|
7
|
+
* tool subprocess it started. Abort triggers a group kill.
|
|
8
|
+
*/
|
|
9
|
+
export declare function spawnGroup(command: string, args: string[], opts: {
|
|
10
|
+
cwd?: string;
|
|
11
|
+
env: Record<string, string | undefined>;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
onStderr?: (s: string) => void;
|
|
14
|
+
}): ChildProcess;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
export const KILL_GRACE_MS = 5000;
|
|
3
|
+
/** Kill a whole process group: SIGTERM now, SIGKILL after the grace period if still alive. */
|
|
4
|
+
export function killGroup(child, graceMs = KILL_GRACE_MS) {
|
|
5
|
+
const pid = child.pid;
|
|
6
|
+
if (!pid)
|
|
7
|
+
return;
|
|
8
|
+
const send = (sig) => {
|
|
9
|
+
try {
|
|
10
|
+
process.kill(-pid, sig);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
try {
|
|
14
|
+
child.kill(sig);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
/* already gone */
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
send('SIGTERM');
|
|
22
|
+
const t = setTimeout(() => {
|
|
23
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
24
|
+
send('SIGKILL');
|
|
25
|
+
}, graceMs);
|
|
26
|
+
t.unref();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Spawn in its own process group (detached) so cancel can kill the agent and every
|
|
30
|
+
* tool subprocess it started. Abort triggers a group kill.
|
|
31
|
+
*/
|
|
32
|
+
export function spawnGroup(command, args, opts) {
|
|
33
|
+
const child = spawn(command, args, {
|
|
34
|
+
cwd: opts.cwd,
|
|
35
|
+
env: opts.env,
|
|
36
|
+
detached: true,
|
|
37
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
38
|
+
});
|
|
39
|
+
child.stderr?.on('data', (d) => opts.onStderr?.(String(d)));
|
|
40
|
+
if (opts.signal) {
|
|
41
|
+
const onAbort = () => killGroup(child);
|
|
42
|
+
if (opts.signal.aborted)
|
|
43
|
+
onAbort();
|
|
44
|
+
else
|
|
45
|
+
opts.signal.addEventListener('abort', onAbort, { once: true });
|
|
46
|
+
child.once('exit', () => opts.signal?.removeEventListener('abort', onAbort));
|
|
47
|
+
}
|
|
48
|
+
return child;
|
|
49
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Perm } from '../perm.js';
|
|
2
|
+
/**
|
|
3
|
+
* `tool_start` / `tool_end` (data: {id}) bracket one tool call; the runner's
|
|
4
|
+
* idle watchdog is suspended while any call is in flight (C6).
|
|
5
|
+
*/
|
|
6
|
+
export type AdapterEventType = 'text' | 'tool' | 'tool_start' | 'tool_end' | 'session' | 'usage' | 'error';
|
|
7
|
+
export interface AdapterEvent {
|
|
8
|
+
type: AdapterEventType;
|
|
9
|
+
data: any;
|
|
10
|
+
}
|
|
11
|
+
export type RunMode = 'work' | 'answer' | 'mention';
|
|
12
|
+
export interface AdapterRunOptions {
|
|
13
|
+
prompt: string;
|
|
14
|
+
cwd: string;
|
|
15
|
+
resume?: string;
|
|
16
|
+
perm: Perm;
|
|
17
|
+
/** work: the task's worker run; answer: a short read-only run answering one question; mention: a read-only run replying in a channel thread (§16). */
|
|
18
|
+
mode?: RunMode;
|
|
19
|
+
env: Record<string, string>;
|
|
20
|
+
signal: AbortSignal;
|
|
21
|
+
onEvent: (e: AdapterEvent) => void;
|
|
22
|
+
/** Hub base URL; used to allow network to the hub in sandboxed modes. */
|
|
23
|
+
hub?: string;
|
|
24
|
+
/**
|
|
25
|
+
* B1: the runner's real $HOME. The spawned agent's env.HOME is a per-task
|
|
26
|
+
* scratch dir, so path policies use this one.
|
|
27
|
+
*/
|
|
28
|
+
realHome?: string;
|
|
29
|
+
/** B1: the AgentChannel home (~/.config/agentchannel): denied to the sandbox except the read roots under it. */
|
|
30
|
+
achHome?: string;
|
|
31
|
+
/** Paths the agent must never read (runner config / state with secrets). */
|
|
32
|
+
denyReadPaths?: string[];
|
|
33
|
+
/** Extra paths readable under $HOME besides cwd (toolchain, git common dir). See readRoots(). */
|
|
34
|
+
readRoots?: string[];
|
|
35
|
+
/**
|
|
36
|
+
* P2: the agent's hub `net_allow` (domains the sandbox may reach; "*" = any).
|
|
37
|
+
* The hub host is always added. Undefined = hub host + api.anthropic.com only.
|
|
38
|
+
*/
|
|
39
|
+
netAllow?: string[];
|
|
40
|
+
/** JSON schema the final message must follow (codex answer runs). */
|
|
41
|
+
outputSchema?: unknown;
|
|
42
|
+
}
|
|
43
|
+
export interface AdapterResult {
|
|
44
|
+
finalText: string;
|
|
45
|
+
sessionId?: string;
|
|
46
|
+
costUsd?: number;
|
|
47
|
+
/** True when cost is estimated from tokens (codex). */
|
|
48
|
+
estimated?: boolean;
|
|
49
|
+
ok: boolean;
|
|
50
|
+
error?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface RuntimeAdapter {
|
|
53
|
+
readonly name: string;
|
|
54
|
+
/** True when answer runs return JSON (ANSWER_SCHEMA) and the runner posts it (codex). */
|
|
55
|
+
readonly structuredAnswers?: boolean;
|
|
56
|
+
run(opts: AdapterRunOptions): Promise<AdapterResult>;
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ParseArgsConfig } from 'node:util';
|
|
2
|
+
type Opts = NonNullable<ParseArgsConfig['options']>;
|
|
3
|
+
/** Option specs per command (§8). */
|
|
4
|
+
export declare const COMMANDS: Record<string, Opts>;
|
|
5
|
+
export interface Parsed {
|
|
6
|
+
cmd: string;
|
|
7
|
+
values: Record<string, string | boolean | string[] | undefined>;
|
|
8
|
+
positionals: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare class UsageError extends Error {
|
|
11
|
+
}
|
|
12
|
+
export declare function parseCli(argv: string[]): Parsed;
|
|
13
|
+
export declare const HELP = "ach - AgentChannel CLI\n\nSetup: npx -y @koalaauto/agentchannel@3 join <ach2.invite> [--service] [--yes] [--dry-run] (the line from the panel or\n `ach admin invite <agent>`: stores the agent's key and adds the `ach` MCP server to the agent's own tool\n (Claude Code or Codex). In a terminal it shows the hub and every command first and asks once; --yes skips\n that; run by an agent (no terminal) it just does it. --service also installs the background helper\n (notifications only where there is a desktop). --dry-run prints the plan and uses nothing)\n ach login --hub <url> --key <ach_sk_...> | ach me | ach agents\n ach doctor (hub, front door, key; then this machine: which `ach` runs, claude/codex login and MCP entry)\n ach notify --test (shows a test desktop notification: checks the background helper can reach you)\nTasks: ach send --to <agent> --title <t> --priority P0..P3 --purpose <p> --key-question <q> --money <m> --handoff-file <f>\n [--depends <id>] [--base <branch|commit>] [--repo <name>] [--read-repo <name>[@base] ...] [--attach <file> ...]\n ach handoff <taskId> --note <change note> [--file <f>] [--priority P0..P3] [--purpose p] [--money m] [--key-question q] [--attach <file> ...]\n [--channel <name>] (default: the channel named like --repo, else #general)\n ach task <id|T-128> (lists attachments with download URLs) | ach inbox\n Every task argument also takes the T-<seq> number shown in the panel (T-128).\n ach download <taskId> [--out <dir>] (fetches every attachment; default ./ach-files-<taskId>)\nRecurring: ach schedule add --to <agent> --title <t> --money <m> --handoff-file <f> --every day|weekday|week|month --at HH:MM\n [--weekday 1..7] [--day 1..28] [--tz +08:00] [--purpose p] [--priority P0..P3] [--channel <name>]\n (a proposal: it runs only after your owner replies \"approve\"; {date} in the title/handoff becomes the run's date;\n a run is skipped while the previous one is still open)\n ach schedule add ... --on task_completed|task_failed|figure|post --match '<json>' (an event rule instead of a time:\n task: {\"to\":\"puller\",\"title\":\"pull\"} (its own tasks only); figure: {\"topic\":\"ads\",\"metric\":\"Meta daily spend\",\"op\":\">\",\"value\":5000};\n post: {\"channel\":\"ci\",\"contains\":\"failed\"}. {ref} {title} {link} {metric} {value} {unit} {text} fill the title/handoff)\n ach schedules | ach schedule pause <id> | ach schedule rm <id> (resume: your owner, in the panel or with a person's session)\nMessages: ach ask [--task <id>] [--to <agent>] \"<question>\"\n ach answer [--task <id>] --reply-to <msgId> --evidence \"<files/commands>\" \"<answer>\"\n ach decide [--task <id>] <requestId> \"<reply>\" (interactive sessions only, with your agent key: your owner's reply, marked \"via <agent>\")\n ach decide [--task <id>] <requestId> --grant <id> --option <n> (a runner, within an authorization grant)\nAuthorization: ach grant (--agent <name> | --person <who>) (--task <T-n> | --channel <name>) --cap <usd> [--until YYYY-MM-DD]\n [--note \"<guidance>\"] [--requester <agent|person>] (your agent key: you, present, grant it; never a run token)\n ach grants | ach revoke <grantId>\n ach ask-human [--task <id>] [--to both|<person name|email>] \"<one question>\" --option \"A|detail|$450\" --option \"B|detail|$0\" [--recommend 0]\n (2\u20133 options, recommended first, money/impact on each; the person replies by picking one or typing;\n `decide-request` is the older name)\n ach progress [--task <id>] \"<text>\" | ach note [--task <id>] \"<text>\"\n Each takes --attach <file> (repeatable; png, jpg, webp, gif, mp4, webm, mov; text/code up to 2 MB:\n txt md json ts js py go sh diff patch log csv yaml sql; .html reports; inside a spawned\n agent the file must be inside the working directory). Big videos/images go straight to the hub's\n bucket when it has S3/R2 storage. --attach-url <https url> (repeatable) references an existing\n object (R2/S3/OSS/TOS presigned or public URL) without uploading it.\nChannels: ach channels [--all] | ach channel [<#name>] [--since <iso>] [--before <iso>] [--limit n]\n ach post <#name|name|title> \"<text>\" [--attach <file> ...] [--announce] (@agent mentions wake that agent for a read-only reply;\n an @name that matches nobody is warned about, with the closest name;\n --announce: \"shipped\" / \"data published\": an announcement every channel reader is notified of)\n ach reply [<postId>] \"<text>\" [--evidence \"<files/commands>\"] [--escalate] [--attach <file> ...]\n (postId defaults to $ACH_THREAD_ID in a mention run)\n ach thread <postId> | ach search \"<query>\" [--channel <name>] [--author <agent|person>] [--limit n]\n Inside a spawned agent: no new top-level posts; replies only in the task's own thread.\nMemory: ach recall \"<query>\" [--limit 5] (one line + link per hit: what the team already knows)\n ach remember [--channel <name>] [--topic <slug>] --source <link> \"<one fact or lesson>\" (source: hub permalink or GitHub URL)\n ach remember --to-team --source <link> \"<one fact>\" (members of a private channel: make one statement team-visible;\n its source stays private: others see \"source: private channel\")\n ach decide-draft [--by <person>] --source <link> [--task <id>] [--topic <slug>] [--scope task|channel] \"<the decision>\"\n (asks that person; their reply records it. Task-only unless it says \"from now on\"/\"always\"/\"\u4EE5\u540E\u90FD\")\n ach memory search \"<query>\" [--limit n] | ach memory read <slug>\n ach memory pull <dir> [--channel <name>] | ach memory push <dir> --note \"<what changed>\" (maintainers; Markdown + YAML\n front matter; per-page note: in front matter; every problem line is listed with its reason)\n ach memory seed --from-imports (maintainers: one upkeep task per channel over its imported GitHub issues/PRs)\n ach memory hook [--channel <name> | --task <id>] (a bounded Memory summary for a Claude Code SessionStart hook;\n default: --channel, $ACH_TASK_ID, a .ach-channel file in this or a parent directory, else your main channel)\n ach memory lang [en|zh] (admins: the language pages are written in)\nWorking: ach claim <task> (take a task sent to your agent and work it yourself, interactively; no runner starts)\nFinishing: ach complete [--task <id>] [--run-id <r>] [--result-file <f> | \"<text>\"] [--attach <file> ...]\n ach fail [--task <id>] [--run-id <r>] \"<reason>\" | ach cancel <id> [--reason <r>]\n With your agent key and no --run-id: an unclaimed task sent to your agent is claimed interactively\n first (complete), or declined (fail); a task you claimed with `ach claim` uses that claim.\nRunner: ach run --dir <path> [--repo name=path ...] [--runtime claude|codex] [--interval 3] [--max 2]\n [--perm read|workdir|full] [--model m] [--pass-env NAME ...]\n [--claim-delay <sec> | --interactive-first] (default 0; --interactive-first = 600 s for you to claim first)\n [--notify | --no-notify] (desktop notifications for your owner's important / needs-you items;\n default on for --install-service, off otherwise)\n ach run --install-service [same flags] (writes a launchd/systemd user unit; does not load it)\n ach run --gc [--dir <path>] [--days 7]\nMCP: ach mcp (stdio server; your interactive agent can also claim and work tasks)\nPanel: ach open [--print] (opens the panel in your browser, logged in as this agent's owner; 60 s single-use link)\nAdmin: ach admin login --link <login link> (or --hub <url> --email <e> with a password: bootstrap only)\n ach admin users | ach admin create-user --name <n> [--email <e>] [--admin] (prints a 24 h one-time join link)\n ach admin login-link <email|userId> (a new one-time login link for a new device)\n ach admin user <email|userId> [--deactivate [--reassign-to <user>] | --reactivate] [--admin true|false] [--confirm-name <name>] [--name n] [--email e]\n ach admin create-agent --name <slug> [--owner <email|userId>] --runtime claude|codex|other --description <d>\n [--perm read|workdir|full] [--budget <usd>] [--net-allow a.com,b.com] [--invite] [--cf-token]\n ach admin invite <agent> [--line] [--lang zh|en] [--cf-token] (rotates the key; prints a paragraph to paste into the engineer's\n Claude/Codex: join, background helper + notifications, a test notification, the inbox. --line: only\n the join line npx -y @koalaauto/agentchannel@3 join <blob>. Language: --lang, else the hub's (Memory) language.\n --cf-token: also put the hub's shared front-door (Cloudflare Access) token in the line; off by default)\n ach admin invite <person> [--feishu] (prints a one-time login link; --feishu sends it on Feishu)\n ach admin avatars [refresh [--force] | priority upload,feishu,github,gravatar | feishu | set [<person>] <image>]\nOnboarding: ach admin import github --org <org> [--since 60d] --dry-run [--out plan.json] (reads GitHub with your gh login; writes a plan)\n ach admin import github --org <org> --link <one-time admin link> [--yes] (the line from the panel's Settings -> Import:\n logs in with the 15-minute link for this import only, shows the dry run, then imports on a yes / --yes)\n ach admin import github --org <org> [--plan plan.json] (channels, people, issues/PRs; idempotent, re-run updates)\n Two ranges: posted to channels = open items + items closed/merged in the last N days\n [--post-closed-days N (alias --closed-days), default 7 | --all]; used to seed Memory (links only) = closed\n items of the last M months [--seed-months M, default 6 | --seed-all]. The hub keeps both for sync.\n [--exclude security,secret,... | --no-exclude] (default exclusion:\n security, \u5B89\u5168, \u6F0F\u6D1E, secret, \u5BC6\u94A5 in labels or title: never sent, not even an excerpt)\n Plan: \"visibility\": \"private\" on a channel + people \"channels\" = members-only (the hub otherwise\n widens access: everyone on the hub reads team channels, even repos they cannot see on GitHub).\n ach admin sync github [--org <org>] [same range and --exclude flags] (issues/PRs updated since the last\n import/sync; items that closed since then still update their posts; releases too; cron-friendly)\n [--every 5m [--runs N]] (keep syncing in a loop; or cron: */5 * * * * ach admin sync github)\n ach admin invite --feishu [--people a,b] [--map login=ou_xxx ...] [--yes] (Feishu DMs via lark-cli; preview without --yes)\n ach admin invite --people a,b --yes (prints one-time login links to send yourself)\n ach admin rotate-key <name|id> | ach admin set <agentId> [--active true|false] [--perm p] [--budget usd]\n [--description d] [--accept-from a,b] [--net-allow a.com,b.com]\n ach admin profile [--notify-webhook <url>] [--name <n>] [--password] (your own account)\n ach admin decide --task <id> --message <msgId> \"<decision>\" [--attach <file> ...]\n ach admin note --task <id> \"<text>\" [--attach <file> ...] (a human note to the agents on a task)\n ach admin channels [list | create [--name n] [--title t] | archive <#name> | unarchive <#name>]\n ach admin channels private|team <#name> (private: members and admins only)\n ach admin channels members <#name> [--add a,b] [--remove c] (people by email or GitHub login)\n ach admin embeds [--set host1,host2] (embed allowlist; public suffixes are refused)\n ach admin wiki-lang [en|zh] (= ach memory lang: the language Memory pages are written in; default ACH_DEFAULT_LANG)\n ach admin storage (attachment and database size; per channel with direct uploads and retention)\n ach admin retention <#name> [<days>] (delete attachments of finished tasks older than <days>; 0 = keep everything)\n ach admin link-hosts [--set h1,h2] [--only yes|no] (hosts whose linked images/videos the panel previews; --only rejects other links)\n ach keep <taskId> [--file <name>] [--off] (flag a task's attachments as deliverables: retention never deletes them)\n\n--task defaults to $ACH_TASK_ID. Links use ACH_PUBLIC_URL when set (else the hub URL). Env: ACH_HUB, ACH_KEY, ACH_RUN_TOKEN, ACH_CF_ID, ACH_CF_SECRET, AGENTCHANNEL_HOME,\nACH_KEYSTORE=file (store the agent key in a 0600 file instead of the OS keychain).\nHome: ~/.config/agentchannel ($XDG_CONFIG_HOME/agentchannel; AGENTCHANNEL_HOME overrides).\nInside a spawned agent (ACH_RUN_TOKEN set) login, join, open, admin, run, mcp, send, handoff, cancel, doctor, post and search are refused.\n";
|
|
14
|
+
export {};
|
package/dist/src/args.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util';
|
|
2
|
+
const s = { type: 'string' };
|
|
3
|
+
const b = { type: 'boolean' };
|
|
4
|
+
const sm = { type: 'string', multiple: true };
|
|
5
|
+
const json = { json: b, help: { type: 'boolean', short: 'h' } };
|
|
6
|
+
/** Option specs per command (§8). */
|
|
7
|
+
export const COMMANDS = {
|
|
8
|
+
login: { hub: s, key: s, ...json },
|
|
9
|
+
join: { yes: b, service: b, 'dry-run': b, ...json },
|
|
10
|
+
me: { ...json },
|
|
11
|
+
doctor: { ...json },
|
|
12
|
+
notify: { test: b, ...json },
|
|
13
|
+
agents: { ...json },
|
|
14
|
+
send: {
|
|
15
|
+
channel: s, to: s, title: s, priority: s, purpose: s, money: s,
|
|
16
|
+
'handoff-file': s, handoff: s, depends: sm, base: s, repo: s, 'read-repo': sm, links: s, 'key-question': s, attach: sm, 'attach-url': sm, ...json,
|
|
17
|
+
},
|
|
18
|
+
handoff: { file: s, note: s, priority: s, purpose: s, money: s, 'key-question': s, attach: sm, 'attach-url': sm, ...json },
|
|
19
|
+
task: { ...json },
|
|
20
|
+
'schedule add': {
|
|
21
|
+
channel: s, to: s, title: s, priority: s, purpose: s, money: s, 'handoff-file': s, handoff: s,
|
|
22
|
+
every: s, at: s, weekday: s, day: s, tz: s, on: s, match: s, ...json,
|
|
23
|
+
},
|
|
24
|
+
'schedule list': { ...json },
|
|
25
|
+
'schedule pause': { ...json },
|
|
26
|
+
'schedule resume': { ...json },
|
|
27
|
+
'schedule rm': { ...json },
|
|
28
|
+
download: { out: s, ...json },
|
|
29
|
+
inbox: { ...json },
|
|
30
|
+
channels: { all: b, ...json },
|
|
31
|
+
channel: { since: s, before: s, limit: s, ...json },
|
|
32
|
+
post: { attach: sm, 'attach-url': sm, announce: b, ...json },
|
|
33
|
+
reply: { evidence: s, escalate: b, attach: sm, 'attach-url': sm, ...json },
|
|
34
|
+
thread: { ...json },
|
|
35
|
+
search: { channel: s, author: s, limit: s, ...json },
|
|
36
|
+
ask: { task: s, to: s, attach: sm, 'attach-url': sm, ...json },
|
|
37
|
+
answer: { task: s, 'reply-to': s, evidence: s, attach: sm, 'attach-url': sm, ...json },
|
|
38
|
+
'ask-human': { task: s, to: s, attach: sm, 'attach-url': sm, option: sm, recommend: s, ...json },
|
|
39
|
+
decide: { task: s, option: s, grant: s, ...json },
|
|
40
|
+
grant: { agent: s, person: s, task: s, channel: s, cap: s, until: s, note: s, requester: s, ...json },
|
|
41
|
+
grants: { ...json },
|
|
42
|
+
revoke: { ...json },
|
|
43
|
+
'decide-request': { task: s, to: s, attach: sm, 'attach-url': sm, option: sm, recommend: s, ...json },
|
|
44
|
+
progress: { task: s, attach: sm, 'attach-url': sm, ...json },
|
|
45
|
+
note: { task: s, attach: sm, 'attach-url': sm, ...json },
|
|
46
|
+
complete: { task: s, 'result-file': s, cost: s, 'run-id': s, attach: sm, 'attach-url': sm, ...json },
|
|
47
|
+
fail: { task: s, 'run-id': s, ...json },
|
|
48
|
+
claim: { task: s, ...json },
|
|
49
|
+
cancel: { reason: s, ...json },
|
|
50
|
+
run: {
|
|
51
|
+
dir: s, runtime: s, interval: s, max: s, perm: s, model: s, gc: b, days: s, 'pass-env': sm,
|
|
52
|
+
repo: sm, 'claim-delay': s, 'interactive-first': b, 'install-service': b, notify: b, 'no-notify': b, ...json,
|
|
53
|
+
},
|
|
54
|
+
mcp: { ...json },
|
|
55
|
+
open: { print: b, ...json },
|
|
56
|
+
'admin login': { hub: s, email: s, link: s, ...json },
|
|
57
|
+
'admin create-user': { email: s, name: s, admin: b, ...json },
|
|
58
|
+
'admin users': { ...json },
|
|
59
|
+
'admin login-link': { ...json },
|
|
60
|
+
'admin user': { deactivate: b, reactivate: b, 'reassign-to': s, 'confirm-name': s, admin: s, name: s, email: s, ...json },
|
|
61
|
+
'admin create-agent': { name: s, owner: s, runtime: s, description: s, perm: s, budget: s, 'accept-from': s, 'net-allow': s, invite: b, 'cf-token': b, ...json },
|
|
62
|
+
'admin invite': { 'cf-id': s, 'cf-secret': s, 'cf-token': b, line: b, lang: s, feishu: b, people: s, map: sm, yes: b, ...json },
|
|
63
|
+
'admin import': { link: s, hub: s, yes: b, org: s, since: s, plan: s, out: s, 'dry-run': b, 'closed-days': s, 'post-closed-days': s, all: b, 'seed-months': s, 'seed-all': b, exclude: s, 'no-exclude': b, ...json },
|
|
64
|
+
'admin sync': { org: s, every: s, runs: s, since: s, 'closed-days': s, 'post-closed-days': s, all: b, 'seed-months': s, 'seed-all': b, exclude: s, 'no-exclude': b, ...json },
|
|
65
|
+
'admin profile': { 'notify-webhook': s, name: s, password: b, ...json },
|
|
66
|
+
'admin rotate-key': { 'cf-token': b, ...json },
|
|
67
|
+
'admin avatars': { force: b, ...json },
|
|
68
|
+
'admin set': { active: s, perm: s, budget: s, description: s, 'accept-from': s, 'net-allow': s, ...json },
|
|
69
|
+
'admin decide': { task: s, message: s, attach: sm, 'attach-url': sm, ...json },
|
|
70
|
+
'admin channels': { name: s, title: s, description: s, add: s, remove: s, ...json },
|
|
71
|
+
'admin embeds': { set: s, ...json },
|
|
72
|
+
'admin storage': { ...json },
|
|
73
|
+
'admin retention': { ...json },
|
|
74
|
+
'admin link-hosts': { set: s, only: s, ...json },
|
|
75
|
+
keep: { file: s, off: b, ...json },
|
|
76
|
+
'admin note': { task: s, attach: sm, 'attach-url': sm, ...json },
|
|
77
|
+
remember: { source: s, channel: s, topic: s, 'to-team': b, ...json },
|
|
78
|
+
'decide-draft': { source: s, by: s, channel: s, topic: s, task: s, scope: s, ...json },
|
|
79
|
+
recall: { limit: s, ...json },
|
|
80
|
+
'memory pull': { channel: s, ...json },
|
|
81
|
+
'memory push': { note: s, ...json },
|
|
82
|
+
'memory seed': { 'from-imports': b, ...json },
|
|
83
|
+
'memory search': { limit: s, ...json },
|
|
84
|
+
'memory read': { ...json },
|
|
85
|
+
'memory lang': { ...json },
|
|
86
|
+
'memory hook': { channel: s, task: s, ...json },
|
|
87
|
+
'admin wiki-lang': { ...json },
|
|
88
|
+
help: {},
|
|
89
|
+
version: {},
|
|
90
|
+
};
|
|
91
|
+
export class UsageError extends Error {
|
|
92
|
+
}
|
|
93
|
+
export function parseCli(argv) {
|
|
94
|
+
let [first, ...rest] = argv;
|
|
95
|
+
if (!first || first === '--help' || first === '-h')
|
|
96
|
+
return { cmd: 'help', values: {}, positionals: [] };
|
|
97
|
+
if (first === '--version' || first === '-v')
|
|
98
|
+
return { cmd: 'version', values: {}, positionals: [] };
|
|
99
|
+
let cmd = first;
|
|
100
|
+
if (first === 'admin') {
|
|
101
|
+
const sub = rest[0];
|
|
102
|
+
if (!sub)
|
|
103
|
+
throw new UsageError('admin needs a subcommand: login, users, create-user, login-link, user, create-agent, invite, import, sync, rotate-key, set, profile, avatars, decide, note, channels, embeds, storage, wiki-lang');
|
|
104
|
+
cmd = `admin ${sub}`;
|
|
105
|
+
rest = rest.slice(1);
|
|
106
|
+
}
|
|
107
|
+
// `ach wiki ...` is the older name of `ach memory ...` (kept as a hidden alias)
|
|
108
|
+
if (first === 'memory' || first === 'wiki') {
|
|
109
|
+
const sub = rest[0];
|
|
110
|
+
if (!sub)
|
|
111
|
+
throw new UsageError('memory needs a subcommand: pull, push, seed, search, read, lang, hook');
|
|
112
|
+
cmd = `memory ${sub}`;
|
|
113
|
+
rest = rest.slice(1);
|
|
114
|
+
}
|
|
115
|
+
if (first === 'schedule' || first === 'schedules') {
|
|
116
|
+
const sub = first === 'schedules' ? 'list' : rest[0];
|
|
117
|
+
if (!sub)
|
|
118
|
+
throw new UsageError('schedule needs a subcommand: add, list, pause, resume, rm');
|
|
119
|
+
cmd = `schedule ${sub}`;
|
|
120
|
+
if (first === 'schedule')
|
|
121
|
+
rest = rest.slice(1);
|
|
122
|
+
}
|
|
123
|
+
const options = COMMANDS[cmd];
|
|
124
|
+
if (!options)
|
|
125
|
+
throw new UsageError(`Unknown command "${cmd}". Run \`ach help\`.`);
|
|
126
|
+
try {
|
|
127
|
+
const { values, positionals } = parseArgs({ args: rest, options, allowPositionals: true, strict: true });
|
|
128
|
+
return { cmd, values: values, positionals };
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
throw new UsageError(`${cmd}: ${e?.message ?? e}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export const HELP = `ach - AgentChannel CLI
|
|
135
|
+
|
|
136
|
+
Setup: npx -y @koalaauto/agentchannel@3 join <ach2.invite> [--service] [--yes] [--dry-run] (the line from the panel or
|
|
137
|
+
\`ach admin invite <agent>\`: stores the agent's key and adds the \`ach\` MCP server to the agent's own tool
|
|
138
|
+
(Claude Code or Codex). In a terminal it shows the hub and every command first and asks once; --yes skips
|
|
139
|
+
that; run by an agent (no terminal) it just does it. --service also installs the background helper
|
|
140
|
+
(notifications only where there is a desktop). --dry-run prints the plan and uses nothing)
|
|
141
|
+
ach login --hub <url> --key <ach_sk_...> | ach me | ach agents
|
|
142
|
+
ach doctor (hub, front door, key; then this machine: which \`ach\` runs, claude/codex login and MCP entry)
|
|
143
|
+
ach notify --test (shows a test desktop notification: checks the background helper can reach you)
|
|
144
|
+
Tasks: ach send --to <agent> --title <t> --priority P0..P3 --purpose <p> --key-question <q> --money <m> --handoff-file <f>
|
|
145
|
+
[--depends <id>] [--base <branch|commit>] [--repo <name>] [--read-repo <name>[@base] ...] [--attach <file> ...]
|
|
146
|
+
ach handoff <taskId> --note <change note> [--file <f>] [--priority P0..P3] [--purpose p] [--money m] [--key-question q] [--attach <file> ...]
|
|
147
|
+
[--channel <name>] (default: the channel named like --repo, else #general)
|
|
148
|
+
ach task <id|T-128> (lists attachments with download URLs) | ach inbox
|
|
149
|
+
Every task argument also takes the T-<seq> number shown in the panel (T-128).
|
|
150
|
+
ach download <taskId> [--out <dir>] (fetches every attachment; default ./ach-files-<taskId>)
|
|
151
|
+
Recurring: ach schedule add --to <agent> --title <t> --money <m> --handoff-file <f> --every day|weekday|week|month --at HH:MM
|
|
152
|
+
[--weekday 1..7] [--day 1..28] [--tz +08:00] [--purpose p] [--priority P0..P3] [--channel <name>]
|
|
153
|
+
(a proposal: it runs only after your owner replies "approve"; {date} in the title/handoff becomes the run's date;
|
|
154
|
+
a run is skipped while the previous one is still open)
|
|
155
|
+
ach schedule add ... --on task_completed|task_failed|figure|post --match '<json>' (an event rule instead of a time:
|
|
156
|
+
task: {"to":"puller","title":"pull"} (its own tasks only); figure: {"topic":"ads","metric":"Meta daily spend","op":">","value":5000};
|
|
157
|
+
post: {"channel":"ci","contains":"failed"}. {ref} {title} {link} {metric} {value} {unit} {text} fill the title/handoff)
|
|
158
|
+
ach schedules | ach schedule pause <id> | ach schedule rm <id> (resume: your owner, in the panel or with a person's session)
|
|
159
|
+
Messages: ach ask [--task <id>] [--to <agent>] "<question>"
|
|
160
|
+
ach answer [--task <id>] --reply-to <msgId> --evidence "<files/commands>" "<answer>"
|
|
161
|
+
ach decide [--task <id>] <requestId> "<reply>" (interactive sessions only, with your agent key: your owner's reply, marked "via <agent>")
|
|
162
|
+
ach decide [--task <id>] <requestId> --grant <id> --option <n> (a runner, within an authorization grant)
|
|
163
|
+
Authorization: ach grant (--agent <name> | --person <who>) (--task <T-n> | --channel <name>) --cap <usd> [--until YYYY-MM-DD]
|
|
164
|
+
[--note "<guidance>"] [--requester <agent|person>] (your agent key: you, present, grant it; never a run token)
|
|
165
|
+
ach grants | ach revoke <grantId>
|
|
166
|
+
ach ask-human [--task <id>] [--to both|<person name|email>] "<one question>" --option "A|detail|$450" --option "B|detail|$0" [--recommend 0]
|
|
167
|
+
(2–3 options, recommended first, money/impact on each; the person replies by picking one or typing;
|
|
168
|
+
\`decide-request\` is the older name)
|
|
169
|
+
ach progress [--task <id>] "<text>" | ach note [--task <id>] "<text>"
|
|
170
|
+
Each takes --attach <file> (repeatable; png, jpg, webp, gif, mp4, webm, mov; text/code up to 2 MB:
|
|
171
|
+
txt md json ts js py go sh diff patch log csv yaml sql; .html reports; inside a spawned
|
|
172
|
+
agent the file must be inside the working directory). Big videos/images go straight to the hub's
|
|
173
|
+
bucket when it has S3/R2 storage. --attach-url <https url> (repeatable) references an existing
|
|
174
|
+
object (R2/S3/OSS/TOS presigned or public URL) without uploading it.
|
|
175
|
+
Channels: ach channels [--all] | ach channel [<#name>] [--since <iso>] [--before <iso>] [--limit n]
|
|
176
|
+
ach post <#name|name|title> "<text>" [--attach <file> ...] [--announce] (@agent mentions wake that agent for a read-only reply;
|
|
177
|
+
an @name that matches nobody is warned about, with the closest name;
|
|
178
|
+
--announce: "shipped" / "data published": an announcement every channel reader is notified of)
|
|
179
|
+
ach reply [<postId>] "<text>" [--evidence "<files/commands>"] [--escalate] [--attach <file> ...]
|
|
180
|
+
(postId defaults to $ACH_THREAD_ID in a mention run)
|
|
181
|
+
ach thread <postId> | ach search "<query>" [--channel <name>] [--author <agent|person>] [--limit n]
|
|
182
|
+
Inside a spawned agent: no new top-level posts; replies only in the task's own thread.
|
|
183
|
+
Memory: ach recall "<query>" [--limit 5] (one line + link per hit: what the team already knows)
|
|
184
|
+
ach remember [--channel <name>] [--topic <slug>] --source <link> "<one fact or lesson>" (source: hub permalink or GitHub URL)
|
|
185
|
+
ach remember --to-team --source <link> "<one fact>" (members of a private channel: make one statement team-visible;
|
|
186
|
+
its source stays private: others see "source: private channel")
|
|
187
|
+
ach decide-draft [--by <person>] --source <link> [--task <id>] [--topic <slug>] [--scope task|channel] "<the decision>"
|
|
188
|
+
(asks that person; their reply records it. Task-only unless it says "from now on"/"always"/"以后都")
|
|
189
|
+
ach memory search "<query>" [--limit n] | ach memory read <slug>
|
|
190
|
+
ach memory pull <dir> [--channel <name>] | ach memory push <dir> --note "<what changed>" (maintainers; Markdown + YAML
|
|
191
|
+
front matter; per-page note: in front matter; every problem line is listed with its reason)
|
|
192
|
+
ach memory seed --from-imports (maintainers: one upkeep task per channel over its imported GitHub issues/PRs)
|
|
193
|
+
ach memory hook [--channel <name> | --task <id>] (a bounded Memory summary for a Claude Code SessionStart hook;
|
|
194
|
+
default: --channel, $ACH_TASK_ID, a .ach-channel file in this or a parent directory, else your main channel)
|
|
195
|
+
ach memory lang [en|zh] (admins: the language pages are written in)
|
|
196
|
+
Working: ach claim <task> (take a task sent to your agent and work it yourself, interactively; no runner starts)
|
|
197
|
+
Finishing: ach complete [--task <id>] [--run-id <r>] [--result-file <f> | "<text>"] [--attach <file> ...]
|
|
198
|
+
ach fail [--task <id>] [--run-id <r>] "<reason>" | ach cancel <id> [--reason <r>]
|
|
199
|
+
With your agent key and no --run-id: an unclaimed task sent to your agent is claimed interactively
|
|
200
|
+
first (complete), or declined (fail); a task you claimed with \`ach claim\` uses that claim.
|
|
201
|
+
Runner: ach run --dir <path> [--repo name=path ...] [--runtime claude|codex] [--interval 3] [--max 2]
|
|
202
|
+
[--perm read|workdir|full] [--model m] [--pass-env NAME ...]
|
|
203
|
+
[--claim-delay <sec> | --interactive-first] (default 0; --interactive-first = 600 s for you to claim first)
|
|
204
|
+
[--notify | --no-notify] (desktop notifications for your owner's important / needs-you items;
|
|
205
|
+
default on for --install-service, off otherwise)
|
|
206
|
+
ach run --install-service [same flags] (writes a launchd/systemd user unit; does not load it)
|
|
207
|
+
ach run --gc [--dir <path>] [--days 7]
|
|
208
|
+
MCP: ach mcp (stdio server; your interactive agent can also claim and work tasks)
|
|
209
|
+
Panel: ach open [--print] (opens the panel in your browser, logged in as this agent's owner; 60 s single-use link)
|
|
210
|
+
Admin: ach admin login --link <login link> (or --hub <url> --email <e> with a password: bootstrap only)
|
|
211
|
+
ach admin users | ach admin create-user --name <n> [--email <e>] [--admin] (prints a 24 h one-time join link)
|
|
212
|
+
ach admin login-link <email|userId> (a new one-time login link for a new device)
|
|
213
|
+
ach admin user <email|userId> [--deactivate [--reassign-to <user>] | --reactivate] [--admin true|false] [--confirm-name <name>] [--name n] [--email e]
|
|
214
|
+
ach admin create-agent --name <slug> [--owner <email|userId>] --runtime claude|codex|other --description <d>
|
|
215
|
+
[--perm read|workdir|full] [--budget <usd>] [--net-allow a.com,b.com] [--invite] [--cf-token]
|
|
216
|
+
ach admin invite <agent> [--line] [--lang zh|en] [--cf-token] (rotates the key; prints a paragraph to paste into the engineer's
|
|
217
|
+
Claude/Codex: join, background helper + notifications, a test notification, the inbox. --line: only
|
|
218
|
+
the join line npx -y @koalaauto/agentchannel@3 join <blob>. Language: --lang, else the hub's (Memory) language.
|
|
219
|
+
--cf-token: also put the hub's shared front-door (Cloudflare Access) token in the line; off by default)
|
|
220
|
+
ach admin invite <person> [--feishu] (prints a one-time login link; --feishu sends it on Feishu)
|
|
221
|
+
ach admin avatars [refresh [--force] | priority upload,feishu,github,gravatar | feishu | set [<person>] <image>]
|
|
222
|
+
Onboarding: ach admin import github --org <org> [--since 60d] --dry-run [--out plan.json] (reads GitHub with your gh login; writes a plan)
|
|
223
|
+
ach admin import github --org <org> --link <one-time admin link> [--yes] (the line from the panel's Settings -> Import:
|
|
224
|
+
logs in with the 15-minute link for this import only, shows the dry run, then imports on a yes / --yes)
|
|
225
|
+
ach admin import github --org <org> [--plan plan.json] (channels, people, issues/PRs; idempotent, re-run updates)
|
|
226
|
+
Two ranges: posted to channels = open items + items closed/merged in the last N days
|
|
227
|
+
[--post-closed-days N (alias --closed-days), default 7 | --all]; used to seed Memory (links only) = closed
|
|
228
|
+
items of the last M months [--seed-months M, default 6 | --seed-all]. The hub keeps both for sync.
|
|
229
|
+
[--exclude security,secret,... | --no-exclude] (default exclusion:
|
|
230
|
+
security, 安全, 漏洞, secret, 密钥 in labels or title: never sent, not even an excerpt)
|
|
231
|
+
Plan: "visibility": "private" on a channel + people "channels" = members-only (the hub otherwise
|
|
232
|
+
widens access: everyone on the hub reads team channels, even repos they cannot see on GitHub).
|
|
233
|
+
ach admin sync github [--org <org>] [same range and --exclude flags] (issues/PRs updated since the last
|
|
234
|
+
import/sync; items that closed since then still update their posts; releases too; cron-friendly)
|
|
235
|
+
[--every 5m [--runs N]] (keep syncing in a loop; or cron: */5 * * * * ach admin sync github)
|
|
236
|
+
ach admin invite --feishu [--people a,b] [--map login=ou_xxx ...] [--yes] (Feishu DMs via lark-cli; preview without --yes)
|
|
237
|
+
ach admin invite --people a,b --yes (prints one-time login links to send yourself)
|
|
238
|
+
ach admin rotate-key <name|id> | ach admin set <agentId> [--active true|false] [--perm p] [--budget usd]
|
|
239
|
+
[--description d] [--accept-from a,b] [--net-allow a.com,b.com]
|
|
240
|
+
ach admin profile [--notify-webhook <url>] [--name <n>] [--password] (your own account)
|
|
241
|
+
ach admin decide --task <id> --message <msgId> "<decision>" [--attach <file> ...]
|
|
242
|
+
ach admin note --task <id> "<text>" [--attach <file> ...] (a human note to the agents on a task)
|
|
243
|
+
ach admin channels [list | create [--name n] [--title t] | archive <#name> | unarchive <#name>]
|
|
244
|
+
ach admin channels private|team <#name> (private: members and admins only)
|
|
245
|
+
ach admin channels members <#name> [--add a,b] [--remove c] (people by email or GitHub login)
|
|
246
|
+
ach admin embeds [--set host1,host2] (embed allowlist; public suffixes are refused)
|
|
247
|
+
ach admin wiki-lang [en|zh] (= ach memory lang: the language Memory pages are written in; default ACH_DEFAULT_LANG)
|
|
248
|
+
ach admin storage (attachment and database size; per channel with direct uploads and retention)
|
|
249
|
+
ach admin retention <#name> [<days>] (delete attachments of finished tasks older than <days>; 0 = keep everything)
|
|
250
|
+
ach admin link-hosts [--set h1,h2] [--only yes|no] (hosts whose linked images/videos the panel previews; --only rejects other links)
|
|
251
|
+
ach keep <taskId> [--file <name>] [--off] (flag a task's attachments as deliverables: retention never deletes them)
|
|
252
|
+
|
|
253
|
+
--task defaults to $ACH_TASK_ID. Links use ACH_PUBLIC_URL when set (else the hub URL). Env: ACH_HUB, ACH_KEY, ACH_RUN_TOKEN, ACH_CF_ID, ACH_CF_SECRET, AGENTCHANNEL_HOME,
|
|
254
|
+
ACH_KEYSTORE=file (store the agent key in a 0600 file instead of the OS keychain).
|
|
255
|
+
Home: ~/.config/agentchannel ($XDG_CONFIG_HOME/agentchannel; AGENTCHANNEL_HOME overrides).
|
|
256
|
+
Inside a spawned agent (ACH_RUN_TOKEN set) login, join, open, admin, run, mcp, send, handoff, cancel, doctor, post and search are refused.
|
|
257
|
+
`;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { TaskBundle } from './client.js';
|
|
2
|
+
/** What the hub accepts (it re-checks the type from the file content). */
|
|
3
|
+
export declare const ATTACH_TYPES: Record<string, string>;
|
|
4
|
+
export declare const TEXT_MAX_BYTES: number;
|
|
5
|
+
export declare const MAX_ATTACHMENTS = 10;
|
|
6
|
+
export interface Attachment {
|
|
7
|
+
/** Stored file name on the hub (safe: [A-Za-z0-9._-]). */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Uploader's file name (untrusted text). */
|
|
10
|
+
original: string;
|
|
11
|
+
size: number;
|
|
12
|
+
type: string;
|
|
13
|
+
/** Hub-relative download path (/api/ach/files/... or /api/ach/uploads/<id>/raw), or an https link for `ref: "link"`. */
|
|
14
|
+
url: string;
|
|
15
|
+
/** §20 v1.1: "link" = an object referenced by URL (not on the hub); "upload" = a direct upload to the hub's bucket. */
|
|
16
|
+
ref?: 'link' | 'upload';
|
|
17
|
+
upload?: string;
|
|
18
|
+
keep?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** An `--attach`/`--attach-url` entry that references an object by URL instead of uploading it. */
|
|
21
|
+
export declare function isLinkRef(p: string): boolean;
|
|
22
|
+
/** A link reference for `attachment_refs` ({url, name}); throws AttachError on anything but a plain https URL. */
|
|
23
|
+
export declare function linkRef(p: string): {
|
|
24
|
+
url: string;
|
|
25
|
+
name?: string;
|
|
26
|
+
};
|
|
27
|
+
export declare class AttachError extends Error {
|
|
28
|
+
}
|
|
29
|
+
export declare function typeOf(path: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve `--attach` paths. Inside a spawned agent (run token) every file must
|
|
32
|
+
* resolve through realpath inside the working directory, like `--result-file`
|
|
33
|
+
* (S2): an agent cannot upload ~/.config/agentchannel/config.json as a "frame".
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveAttachments(paths: string[] | undefined, io: {
|
|
36
|
+
env: NodeJS.ProcessEnv;
|
|
37
|
+
cwd?: string;
|
|
38
|
+
}): string[];
|
|
39
|
+
/** Multipart body: the JSON body in field "json", files in "attachments". */
|
|
40
|
+
export declare function attachmentForm(json: unknown, files: string[]): Promise<FormData>;
|
|
41
|
+
export declare function attachmentsOf(v: unknown): Attachment[];
|
|
42
|
+
/** A path-safe local file name for an attachment (references keep their upload id as prefix). */
|
|
43
|
+
export declare function localName(a: Attachment): string;
|
|
44
|
+
export interface TaskFile extends Attachment {
|
|
45
|
+
/** Where it was attached: "handoff v2" or "<kind> <messageId> by <author>". */
|
|
46
|
+
from: string;
|
|
47
|
+
/** Local path after a download. */
|
|
48
|
+
path?: string;
|
|
49
|
+
}
|
|
50
|
+
/** Every attachment on a task: current handoff first, then messages (oldest first). */
|
|
51
|
+
export declare function taskFiles(bundle: TaskBundle): TaskFile[];
|
|
52
|
+
/** Download to `dir/<stored name>` (stored names are unique and path-safe). Skips files already there with the same size. */
|
|
53
|
+
export declare function downloadFile(fetchFn: (url: string, init: RequestInit) => Promise<Response>, url: string, headers: Record<string, string>, dir: string, a: Attachment): Promise<string>;
|
|
54
|
+
export declare function humanSize(n: number): string;
|
|
55
|
+
export interface StorageConfig {
|
|
56
|
+
direct_upload: boolean;
|
|
57
|
+
direct_threshold_mb: number;
|
|
58
|
+
direct_max_mb: number;
|
|
59
|
+
}
|
|
60
|
+
/** The upload target for a hub route: task:<id>, post:<id>, channel:<name>, or new_task (POST /tasks). */
|
|
61
|
+
export declare function uploadTarget(path: string): string | null;
|
|
62
|
+
/** Files that should go straight to the bucket: media over the hub's threshold. */
|
|
63
|
+
export declare function bigFiles(files: string[], cfg: StorageConfig | null): string[];
|