@edukors/ai-cli 0.0.0-stage → 0.1.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.
Files changed (106) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +162 -2
  3. package/dist/cli/main.d.ts +2 -0
  4. package/dist/cli/main.js +256 -0
  5. package/dist/cli/main.js.map +1 -0
  6. package/dist/constants.d.ts +2 -0
  7. package/dist/constants.js +3 -0
  8. package/dist/constants.js.map +1 -0
  9. package/dist/diagnostics.d.ts +28 -0
  10. package/dist/diagnostics.js +35 -0
  11. package/dist/diagnostics.js.map +1 -0
  12. package/dist/engine.d.ts +157 -0
  13. package/dist/engine.js +395 -0
  14. package/dist/engine.js.map +1 -0
  15. package/dist/errors.d.ts +27 -0
  16. package/dist/errors.js +59 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/events.d.ts +95 -0
  19. package/dist/events.js +8 -0
  20. package/dist/events.js.map +1 -0
  21. package/dist/host/main.d.ts +2 -0
  22. package/dist/host/main.js +30 -0
  23. package/dist/host/main.js.map +1 -0
  24. package/dist/host/stdio.d.ts +34 -0
  25. package/dist/host/stdio.js +254 -0
  26. package/dist/host/stdio.js.map +1 -0
  27. package/dist/index.d.ts +34 -0
  28. package/dist/index.js +21 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/mcp/config.d.ts +19 -0
  31. package/dist/mcp/config.js +63 -0
  32. package/dist/mcp/config.js.map +1 -0
  33. package/dist/mcp/external.d.ts +55 -0
  34. package/dist/mcp/external.js +129 -0
  35. package/dist/mcp/external.js.map +1 -0
  36. package/dist/mcp/toolServer.d.ts +30 -0
  37. package/dist/mcp/toolServer.js +102 -0
  38. package/dist/mcp/toolServer.js.map +1 -0
  39. package/dist/platform/discovery.d.ts +23 -0
  40. package/dist/platform/discovery.js +64 -0
  41. package/dist/platform/discovery.js.map +1 -0
  42. package/dist/platform/env.d.ts +12 -0
  43. package/dist/platform/env.js +104 -0
  44. package/dist/platform/env.js.map +1 -0
  45. package/dist/platform/login.d.ts +16 -0
  46. package/dist/platform/login.js +42 -0
  47. package/dist/platform/login.js.map +1 -0
  48. package/dist/platform/paths.d.ts +19 -0
  49. package/dist/platform/paths.js +46 -0
  50. package/dist/platform/paths.js.map +1 -0
  51. package/dist/platform/process.d.ts +55 -0
  52. package/dist/platform/process.js +186 -0
  53. package/dist/platform/process.js.map +1 -0
  54. package/dist/provider.d.ts +156 -0
  55. package/dist/provider.js +2 -0
  56. package/dist/provider.js.map +1 -0
  57. package/dist/providers/claude/adapter.d.ts +43 -0
  58. package/dist/providers/claude/adapter.js +398 -0
  59. package/dist/providers/claude/adapter.js.map +1 -0
  60. package/dist/providers/claude/events.d.ts +36 -0
  61. package/dist/providers/claude/events.js +90 -0
  62. package/dist/providers/claude/events.js.map +1 -0
  63. package/dist/providers/codex/adapter.d.ts +28 -0
  64. package/dist/providers/codex/adapter.js +497 -0
  65. package/dist/providers/codex/adapter.js.map +1 -0
  66. package/dist/providers/codex/rpc.d.ts +17 -0
  67. package/dist/providers/codex/rpc.js +75 -0
  68. package/dist/providers/codex/rpc.js.map +1 -0
  69. package/dist/providers.d.ts +9 -0
  70. package/dist/providers.js +15 -0
  71. package/dist/providers.js.map +1 -0
  72. package/dist/redact.d.ts +7 -0
  73. package/dist/redact.js +48 -0
  74. package/dist/redact.js.map +1 -0
  75. package/dist/registry/lock.d.ts +18 -0
  76. package/dist/registry/lock.js +83 -0
  77. package/dist/registry/lock.js.map +1 -0
  78. package/dist/registry/registry.d.ts +38 -0
  79. package/dist/registry/registry.js +119 -0
  80. package/dist/registry/registry.js.map +1 -0
  81. package/dist/session.d.ts +126 -0
  82. package/dist/session.js +333 -0
  83. package/dist/session.js.map +1 -0
  84. package/dist/settings.d.ts +8 -0
  85. package/dist/settings.js +7 -0
  86. package/dist/settings.js.map +1 -0
  87. package/dist/skills.d.ts +24 -0
  88. package/dist/skills.js +91 -0
  89. package/dist/skills.js.map +1 -0
  90. package/dist/testing/fakeProvider.d.ts +22 -0
  91. package/dist/testing/fakeProvider.js +266 -0
  92. package/dist/testing/fakeProvider.js.map +1 -0
  93. package/dist/testing/index.d.ts +2 -0
  94. package/dist/testing/index.js +2 -0
  95. package/dist/testing/index.js.map +1 -0
  96. package/dist/tools.d.ts +45 -0
  97. package/dist/tools.js +32 -0
  98. package/dist/tools.js.map +1 -0
  99. package/dist/version.d.ts +2 -0
  100. package/dist/version.js +3 -0
  101. package/dist/version.js.map +1 -0
  102. package/fixtures/fake-ai.mjs +230 -0
  103. package/fixtures/fake-claude.mjs +62 -0
  104. package/fixtures/fake-codex.mjs +281 -0
  105. package/fixtures/fake-mcp-server.mjs +14 -0
  106. package/package.json +74 -4
@@ -0,0 +1,16 @@
1
+ import type { AccountContext } from "../provider.js";
2
+ import { type CommandSpec, type RunResult } from "./process.js";
3
+ /**
4
+ * Runs a provider's sign-in command. Such commands open the browser and wait
5
+ * for it; when the browser cannot be opened they print an address instead,
6
+ * and some then wait for a code pasted on their input. The address goes to
7
+ * the application (`context.login.prompt`), and a code the user pastes goes
8
+ * to the command's input, so the whole flow works without a terminal.
9
+ */
10
+ export interface LoginCommandOptions {
11
+ /** The sign-in address among everything the command prints. */
12
+ urlPattern: RegExp;
13
+ acceptsCode: boolean;
14
+ timeoutMs?: number;
15
+ }
16
+ export declare function runLoginCommand(spec: CommandSpec, context: AccountContext, options: LoginCommandOptions): Promise<RunResult>;
@@ -0,0 +1,42 @@
1
+ import { AiCliError } from "../errors.js";
2
+ import { ManagedProcess, readLines } from "./process.js";
3
+ const MAX_OUTPUT = 64 << 10;
4
+ export async function runLoginCommand(spec, context, options) {
5
+ const proc = new ManagedProcess(spec);
6
+ const spawnError = new Promise((resolve) => proc.child.once("error", resolve));
7
+ let stdout = "";
8
+ let offered = false;
9
+ const onLine = (line) => {
10
+ if (stdout.length < MAX_OUTPUT)
11
+ stdout += line + "\n";
12
+ if (offered)
13
+ return;
14
+ const url = line.match(options.urlPattern)?.[0];
15
+ if (!url)
16
+ return;
17
+ offered = true;
18
+ // Wait for the code before offering the address: an application may answer at once.
19
+ if (options.acceptsCode && context.login) {
20
+ context.login.code().then((code) => proc.child.stdin.write(`${code}\n`), () => undefined);
21
+ }
22
+ context.login?.prompt({ url, acceptsCode: options.acceptsCode });
23
+ };
24
+ readLines(proc.child.stdout, onLine);
25
+ readLines(proc.child.stderr, onLine);
26
+ let timedOut = false;
27
+ const timer = setTimeout(() => {
28
+ timedOut = true;
29
+ void proc.terminate(500);
30
+ }, options.timeoutMs ?? 10 * 60_000);
31
+ const onAbort = () => void proc.terminate(500);
32
+ context.signal?.addEventListener("abort", onAbort, { once: true });
33
+ const info = await proc.exited;
34
+ clearTimeout(timer);
35
+ context.signal?.removeEventListener("abort", onAbort);
36
+ if (info.code === null && info.signal === null)
37
+ throw new AiCliError("PROVIDER_START_FAILED", `could not start ${spec.file}`, { cause: await spawnError });
38
+ if (context.signal?.aborted)
39
+ throw new AiCliError("CANCELLED", "sign-in cancelled");
40
+ return { code: info.code, stdout, stderr: proc.stderr.toString(), timedOut };
41
+ }
42
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/platform/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The per-OS-user EDUKORS data directory, shared by every EDUKORS application
3
+ * on this computer. Resolved by platform convention; `EDUKORS_AI_DATA_DIR`
4
+ * overrides it (tests, portable installs).
5
+ */
6
+ export declare function defaultDataDir(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string;
7
+ /** Stable, filesystem-safe key for an arbitrary document identifier. */
8
+ export declare const documentKey: (documentId: string) => string;
9
+ export declare class DataPaths {
10
+ readonly root: string;
11
+ /** Always absolute: provider processes run in other working directories. */
12
+ constructor(root: string);
13
+ get registryFile(): string;
14
+ get locksDir(): string;
15
+ /** Stored relative to the root in the registry, so the root may move. */
16
+ accountHomeRelative(accountId: string): string;
17
+ resolve(relative: string): string;
18
+ sessionDir(appId: string, documentId: string, accountId: string): string;
19
+ }
@@ -0,0 +1,46 @@
1
+ import { createHash } from "node:crypto";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, join, resolve } from "node:path";
4
+ /**
5
+ * The per-OS-user EDUKORS data directory, shared by every EDUKORS application
6
+ * on this computer. Resolved by platform convention; `EDUKORS_AI_DATA_DIR`
7
+ * overrides it (tests, portable installs).
8
+ */
9
+ export function defaultDataDir(env = process.env, platform = process.platform) {
10
+ const override = env.EDUKORS_AI_DATA_DIR;
11
+ if (override)
12
+ return resolve(override);
13
+ const home = env.HOME || env.USERPROFILE || homedir();
14
+ if (platform === "darwin")
15
+ return join(home, "Library", "Application Support", "EDUKORS");
16
+ // Local, not roaming: account directories hold machine-bound provider state.
17
+ if (platform === "win32")
18
+ return join(env.LOCALAPPDATA || join(home, "AppData", "Local"), "EDUKORS");
19
+ return join(env.XDG_DATA_HOME && isAbsolute(env.XDG_DATA_HOME) ? env.XDG_DATA_HOME : join(home, ".local", "share"), "edukors");
20
+ }
21
+ /** Stable, filesystem-safe key for an arbitrary document identifier. */
22
+ export const documentKey = (documentId) => createHash("sha256").update(documentId).digest("hex").slice(0, 24);
23
+ export class DataPaths {
24
+ root;
25
+ /** Always absolute: provider processes run in other working directories. */
26
+ constructor(root) {
27
+ this.root = resolve(root);
28
+ }
29
+ get registryFile() {
30
+ return join(this.root, "ai", "registry.json");
31
+ }
32
+ get locksDir() {
33
+ return join(this.root, "ai", "locks");
34
+ }
35
+ /** Stored relative to the root in the registry, so the root may move. */
36
+ accountHomeRelative(accountId) {
37
+ return join("ai", "accounts", accountId);
38
+ }
39
+ resolve(relative) {
40
+ return join(this.root, relative);
41
+ }
42
+ sessionDir(appId, documentId, accountId) {
43
+ return join(this.root, "ai", "sessions", appId, documentKey(documentId), accountId);
44
+ }
45
+ }
46
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/platform/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEtD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAE,WAA4B,OAAO,CAAC,QAAQ;IAC/G,MAAM,QAAQ,GAAG,GAAG,CAAC,mBAAmB,CAAC;IACzC,IAAI,QAAQ;QAAE,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,WAAW,IAAI,OAAO,EAAE,CAAC;IACtD,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,SAAS,CAAC,CAAC;IAC1F,6EAA6E;IAC7E,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC;IACrG,OAAO,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC;AACjI,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,UAAkB,EAAU,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAE9H,MAAM,OAAO,SAAS;IACX,IAAI,CAAS;IAEtB,4EAA4E;IAC5E,YAAY,IAAY;QACtB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,yEAAyE;IACzE,mBAAmB,CAAC,SAAiB;QACnC,OAAO,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,CAAC,QAAgB;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,UAAU,CAAC,KAAa,EAAE,UAAkB,EAAE,SAAiB;QAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC;IACtF,CAAC;CACF"}
@@ -0,0 +1,55 @@
1
+ import { type ChildProcessWithoutNullStreams } from "node:child_process";
2
+ import type { Readable } from "node:stream";
3
+ export interface CommandSpec {
4
+ file: string;
5
+ args?: string[];
6
+ env?: Record<string, string>;
7
+ cwd?: string;
8
+ }
9
+ /** Keeps the last `limit` characters of a stream. */
10
+ export declare class Tail {
11
+ private readonly limit;
12
+ private text;
13
+ constructor(limit?: number);
14
+ push(chunk: string): void;
15
+ toString(): string;
16
+ }
17
+ export interface ExitInfo {
18
+ code: number | null;
19
+ signal: NodeJS.Signals | null;
20
+ }
21
+ export declare class ManagedProcess {
22
+ readonly child: ChildProcessWithoutNullStreams;
23
+ readonly stderr: Tail;
24
+ readonly exited: Promise<ExitInfo>;
25
+ private exitInfo;
26
+ constructor(spec: CommandSpec);
27
+ get running(): boolean;
28
+ /** Writes one JSON object followed by a newline. Returns false if the process is gone. */
29
+ writeLine(message: unknown): boolean;
30
+ /** Ends the whole process tree: a polite signal, then a forced one after `graceMs`. */
31
+ terminate(graceMs?: number): Promise<ExitInfo>;
32
+ }
33
+ /** Kills every process this library started. Safe to call from `process.on("exit")`. */
34
+ export declare function killAllSync(): void;
35
+ export interface RunResult {
36
+ code: number | null;
37
+ stdout: string;
38
+ stderr: string;
39
+ timedOut: boolean;
40
+ }
41
+ /** Runs a short command to completion with a timeout and bounded output. Never rejects for a non-zero exit. */
42
+ export declare function runCommand(spec: CommandSpec & {
43
+ timeoutMs?: number;
44
+ maxOutput?: number;
45
+ signal?: AbortSignal;
46
+ input?: string;
47
+ }): Promise<RunResult>;
48
+ /**
49
+ * Splits a stream into lines. A line longer than `maxLineBytes` is discarded
50
+ * and reported, so a misbehaving process cannot exhaust memory.
51
+ */
52
+ export declare function readLines(stream: Readable, onLine: (line: string) => void, options?: {
53
+ maxLineBytes?: number;
54
+ onOverflow?: () => void;
55
+ }): void;
@@ -0,0 +1,186 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { AiCliError } from "../errors.js";
3
+ /**
4
+ * Process lifecycle for provider CLIs: bounded output, timeouts and reliable
5
+ * termination of the whole process tree.
6
+ */
7
+ const WINDOWS = process.platform === "win32";
8
+ /** Windows batch shims (npm installs `.cmd` files) cannot be spawned without a shell. */
9
+ const needsShell = (file) => WINDOWS && /\.(cmd|bat)$/i.test(file);
10
+ /** Quotes an argument for cmd.exe. Arguments come from adapters, never from users. */
11
+ const cmdQuote = (arg) => (/^[\w\-./:=@\\]+$/.test(arg) ? arg : `"${arg.replace(/(\\*)"/g, '$1$1\\"').replace(/%/g, "%%")}"`);
12
+ function spawnRaw(spec) {
13
+ const args = spec.args ?? [];
14
+ const shell = needsShell(spec.file);
15
+ return spawn(shell ? `"${spec.file}"` : spec.file, shell ? args.map(cmdQuote) : args, {
16
+ env: spec.env ?? process.env,
17
+ cwd: spec.cwd,
18
+ shell,
19
+ windowsHide: true,
20
+ // Own process group on POSIX, so the whole tree can be signalled.
21
+ detached: !WINDOWS,
22
+ stdio: ["pipe", "pipe", "pipe"],
23
+ });
24
+ }
25
+ /** Keeps the last `limit` characters of a stream. */
26
+ export class Tail {
27
+ limit;
28
+ text = "";
29
+ constructor(limit = 16_384) {
30
+ this.limit = limit;
31
+ }
32
+ push(chunk) {
33
+ this.text += chunk;
34
+ if (this.text.length > this.limit)
35
+ this.text = this.text.slice(-this.limit);
36
+ }
37
+ toString() {
38
+ return this.text;
39
+ }
40
+ }
41
+ const live = new Set();
42
+ export class ManagedProcess {
43
+ child;
44
+ stderr = new Tail();
45
+ exited;
46
+ exitInfo = null;
47
+ constructor(spec) {
48
+ this.child = spawnRaw(spec);
49
+ this.child.stderr.setEncoding("utf8").on("data", (chunk) => this.stderr.push(chunk));
50
+ this.child.stdin.on("error", () => undefined);
51
+ live.add(this);
52
+ this.exited = new Promise((resolve) => {
53
+ const done = (info) => {
54
+ if (this.exitInfo)
55
+ return;
56
+ this.exitInfo = info;
57
+ live.delete(this);
58
+ resolve(info);
59
+ };
60
+ this.child.once("exit", (code, signal) => done({ code, signal }));
61
+ this.child.once("error", () => done({ code: null, signal: null }));
62
+ });
63
+ }
64
+ get running() {
65
+ return this.exitInfo === null;
66
+ }
67
+ /** Writes one JSON object followed by a newline. Returns false if the process is gone. */
68
+ writeLine(message) {
69
+ if (!this.running || this.child.stdin.destroyed)
70
+ return false;
71
+ this.child.stdin.write(JSON.stringify(message) + "\n");
72
+ return true;
73
+ }
74
+ /** Ends the whole process tree: a polite signal, then a forced one after `graceMs`. */
75
+ async terminate(graceMs = 2_000) {
76
+ if (!this.running)
77
+ return this.exitInfo;
78
+ try {
79
+ this.child.stdin.end();
80
+ }
81
+ catch {
82
+ /* already closed */
83
+ }
84
+ killTree(this.child.pid, "SIGTERM");
85
+ const timer = new Promise((r) => setTimeout(() => r(null), graceMs).unref());
86
+ if ((await Promise.race([this.exited, timer])) === null)
87
+ killTree(this.child.pid, "SIGKILL");
88
+ return this.exited;
89
+ }
90
+ }
91
+ function killTree(pid, signal) {
92
+ if (!pid)
93
+ return;
94
+ if (WINDOWS) {
95
+ spawnSync("taskkill", ["/pid", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" });
96
+ return;
97
+ }
98
+ try {
99
+ process.kill(-pid, signal);
100
+ }
101
+ catch {
102
+ try {
103
+ process.kill(pid, signal);
104
+ }
105
+ catch {
106
+ /* already gone */
107
+ }
108
+ }
109
+ }
110
+ /** Kills every process this library started. Safe to call from `process.on("exit")`. */
111
+ export function killAllSync() {
112
+ for (const proc of live)
113
+ killTree(proc.child.pid, "SIGKILL");
114
+ live.clear();
115
+ }
116
+ process.once("exit", killAllSync);
117
+ /** Runs a short command to completion with a timeout and bounded output. Never rejects for a non-zero exit. */
118
+ export async function runCommand(spec) {
119
+ const maxOutput = spec.maxOutput ?? 1 << 20;
120
+ let proc;
121
+ try {
122
+ proc = new ManagedProcess(spec);
123
+ }
124
+ catch (e) {
125
+ throw new AiCliError("PROVIDER_START_FAILED", `could not start ${spec.file}`, { cause: e });
126
+ }
127
+ let stdout = "";
128
+ proc.child.stdout.setEncoding("utf8").on("data", (chunk) => {
129
+ if (stdout.length < maxOutput)
130
+ stdout += chunk.slice(0, maxOutput - stdout.length);
131
+ });
132
+ if (spec.input !== undefined)
133
+ proc.child.stdin.end(spec.input);
134
+ let timedOut = false;
135
+ const timer = setTimeout(() => {
136
+ timedOut = true;
137
+ void proc.terminate(500);
138
+ }, spec.timeoutMs ?? 15_000);
139
+ const onAbort = () => void proc.terminate(500);
140
+ spec.signal?.addEventListener("abort", onAbort, { once: true });
141
+ const spawnError = new Promise((resolve) => proc.child.once("error", resolve));
142
+ const info = await proc.exited;
143
+ clearTimeout(timer);
144
+ spec.signal?.removeEventListener("abort", onAbort);
145
+ if (info.code === null && info.signal === null) {
146
+ throw new AiCliError("PROVIDER_START_FAILED", `could not start ${spec.file}`, { cause: await spawnError });
147
+ }
148
+ if (spec.signal?.aborted)
149
+ throw new AiCliError("CANCELLED", "cancelled");
150
+ return { code: info.code, stdout, stderr: proc.stderr.toString(), timedOut };
151
+ }
152
+ /**
153
+ * Splits a stream into lines. A line longer than `maxLineBytes` is discarded
154
+ * and reported, so a misbehaving process cannot exhaust memory.
155
+ */
156
+ export function readLines(stream, onLine, options = {}) {
157
+ const max = options.maxLineBytes ?? 8 << 20;
158
+ let buffer = "";
159
+ let discarding = false;
160
+ stream.setEncoding("utf8");
161
+ stream.on("data", (chunk) => {
162
+ let start = 0;
163
+ for (let i = chunk.indexOf("\n"); i !== -1; i = chunk.indexOf("\n", start)) {
164
+ const piece = chunk.slice(start, i);
165
+ if (!discarding)
166
+ onLine((buffer + piece).replace(/\r$/, ""));
167
+ buffer = "";
168
+ discarding = false;
169
+ start = i + 1;
170
+ }
171
+ if (discarding)
172
+ return;
173
+ buffer += chunk.slice(start);
174
+ if (buffer.length > max) {
175
+ buffer = "";
176
+ discarding = true;
177
+ options.onOverflow?.();
178
+ }
179
+ });
180
+ stream.on("end", () => {
181
+ if (buffer && !discarding)
182
+ onLine(buffer);
183
+ buffer = "";
184
+ });
185
+ }
186
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/platform/process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
1
+ import type { ActivityPhase, Usage } from "./events.js";
2
+ import type { ScopedLog } from "./diagnostics.js";
3
+ import type { ToolResult, ToolSpec } from "./tools.js";
4
+ /**
5
+ * The contract each provider adapter implements. Adapters translate one
6
+ * provider's CLI and protocol into this; the library handles accounts,
7
+ * session identity, late events, tool routing and errors around it.
8
+ *
9
+ * Optional operations are capabilities, not simulations: an adapter that
10
+ * cannot report usage says so, and the library reports usage as unavailable.
11
+ */
12
+ export type DetectionStatus = "ready" | "missing" | "incompatible" | "error";
13
+ export interface Detection {
14
+ providerId: string;
15
+ status: DetectionStatus;
16
+ executable?: string;
17
+ version?: string;
18
+ /** The minimum version this adapter supports, when the installed one is too old. */
19
+ requiredVersion?: string;
20
+ detail?: string;
21
+ }
22
+ export interface Capabilities {
23
+ /** How the user signs in. `external` means the provider's own tooling must be used. */
24
+ login: "browser" | "terminal" | "external";
25
+ /** Account state can be read without sending a model request. */
26
+ accountStatus: boolean;
27
+ /** Separate accounts of this provider are verified to be isolated. */
28
+ multiAccount: boolean;
29
+ logout: boolean;
30
+ /** Conversations can be resumed after the provider process ends. */
31
+ persistentSessions: boolean;
32
+ models: boolean;
33
+ efforts: boolean;
34
+ usage: boolean;
35
+ tools: boolean;
36
+ questions: boolean;
37
+ /** Model or effort can change in an open session. */
38
+ liveOptions: boolean;
39
+ }
40
+ export type AccountState = "connected" | "disconnected" | "expired" | "unknown";
41
+ export interface AccountStatus {
42
+ state: AccountState;
43
+ /** A display identity (e-mail, organization) only when the provider exposes one. */
44
+ identity?: string;
45
+ detail?: string;
46
+ }
47
+ export interface ModelInfo {
48
+ id: string;
49
+ label?: string;
50
+ efforts?: string[];
51
+ default?: boolean;
52
+ }
53
+ export interface SessionOptions {
54
+ model?: string;
55
+ effort?: string;
56
+ }
57
+ /** An address the user can open to sign in, offered while a sign-in runs. */
58
+ export interface LoginPrompt {
59
+ url: string;
60
+ /** The page ends with a code the user pastes back (see `AiCli.submitLoginCode`). */
61
+ acceptsCode: boolean;
62
+ }
63
+ /** How a sign-in reaches the user while it runs. Present only during `connectAccount`. */
64
+ export interface LoginChannel {
65
+ prompt(prompt: LoginPrompt): void;
66
+ /** Resolves with the code the user pasted; rejects if the sign-in is cancelled. */
67
+ code(): Promise<string>;
68
+ }
69
+ /** What an adapter receives for anything account-related. */
70
+ export interface AccountContext {
71
+ accountId: string;
72
+ label: string;
73
+ /** An application-managed directory reserved for this account's provider configuration and credentials. */
74
+ homeDir: string;
75
+ detection: Detection;
76
+ log: ScopedLog;
77
+ signal?: AbortSignal;
78
+ login?: LoginChannel;
79
+ }
80
+ /** One of the user's MCP servers, already connected by the library, as a provider offers it. */
81
+ export interface ExternalServer {
82
+ /** Letters, digits, `_` and `-`: the name the model sees (`mcp__<name>__<tool>`). */
83
+ name: string;
84
+ tools: ToolSpec[];
85
+ }
86
+ export interface SessionContext extends AccountContext {
87
+ appId: string;
88
+ documentId: string;
89
+ /** Scratch space for this application, document and account; never shared between documents. */
90
+ workDir: string;
91
+ instructions: string;
92
+ tools: ToolSpec[];
93
+ /** The user's MCP servers; calls to them go through `TurnSink.callExternal`. */
94
+ externalServers: ExternalServer[];
95
+ /** A folder of skills (`<name>/SKILL.md`) the application offers; no other skill is enabled. */
96
+ skillsDir?: string;
97
+ options: SessionOptions;
98
+ /** The provider's own session identifier to resume, when the capability allows it. */
99
+ resumeId?: string;
100
+ }
101
+ export interface SessionHooks {
102
+ /** The provider process ended outside of a request. */
103
+ exited(error: Error): void;
104
+ }
105
+ export interface TurnInput {
106
+ requestId: string;
107
+ text: string;
108
+ /** Latest document context the application chose to include with this request. */
109
+ context?: string;
110
+ }
111
+ /**
112
+ * Bound to a single request. After the request is interrupted or replaced,
113
+ * every call becomes a no-op (or is refused), so late output from a provider
114
+ * cannot reach the application.
115
+ */
116
+ export interface TurnSink {
117
+ readonly signal: AbortSignal;
118
+ text(delta: string): void;
119
+ activity(phase: ActivityPhase, detail?: string): void;
120
+ usage(usage: Omit<Usage, "observedAt"> & {
121
+ observedAt?: string;
122
+ }): void;
123
+ callTool(name: string, args: unknown): Promise<ToolResult>;
124
+ /** A tool of one of the user's MCP servers, authorized by the application first. */
125
+ callExternal(server: string, tool: string, args: unknown): Promise<ToolResult>;
126
+ ask(question: {
127
+ prompt: string;
128
+ options?: string[];
129
+ }): Promise<string>;
130
+ }
131
+ export type TurnOutcome = "completed" | "cancelled";
132
+ export interface ProviderSession {
133
+ /** The provider's own conversation id, used to resume. */
134
+ readonly resumeId: string | undefined;
135
+ /** Resolves when the turn ends; rejects with an AiCliError on failure. */
136
+ send(input: TurnInput, sink: TurnSink): Promise<TurnOutcome>;
137
+ /** Asks the provider to stop; the pending `send` should settle soon after. */
138
+ interrupt(): Promise<void>;
139
+ updateOptions?(options: SessionOptions): Promise<void>;
140
+ /** Releases everything, including the process tree. Must settle any pending `send`. */
141
+ close(): Promise<void>;
142
+ }
143
+ export interface ProviderAdapter {
144
+ readonly id: string;
145
+ readonly displayName: string;
146
+ /** Where the user installs the CLI from, for guided setup. */
147
+ readonly installUrl?: string;
148
+ detect(log: ScopedLog): Promise<Detection>;
149
+ capabilities(detection: Detection): Capabilities;
150
+ connectAccount(context: AccountContext): Promise<AccountStatus>;
151
+ accountStatus(context: AccountContext): Promise<AccountStatus>;
152
+ logout?(context: AccountContext): Promise<void>;
153
+ listModels?(context: AccountContext): Promise<ModelInfo[]>;
154
+ readUsage?(context: AccountContext): Promise<Usage | null>;
155
+ openSession(context: SessionContext, hooks: SessionHooks): Promise<ProviderSession>;
156
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ import type { Options, Query, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
2
+ import type { ProviderAdapter } from "../../provider.js";
3
+ /**
4
+ * Claude Code, through the Claude Agent SDK driving the user's own `claude`.
5
+ *
6
+ * Each account is a Claude Code configuration directory of its own
7
+ * (CLAUDE_CONFIG_DIR, the account's managed home). Claude Code keys its
8
+ * macOS Keychain entry by that directory, which is what isolates accounts;
9
+ * this is to be confirmed with live accounts (docs/compatibility.md).
10
+ *
11
+ * The session gets the application's tools (in-process MCP server) and
12
+ * AskUserQuestion, nothing else by default: no shell, no files, no personal
13
+ * settings, skills or MCP servers.
14
+ */
15
+ export interface ClaudeProviderOptions {
16
+ /** Absolute path to `claude`, from advanced settings. */
17
+ executable?: string;
18
+ minVersion?: string;
19
+ /** Claude Code's built-in tools offered besides the application's. */
20
+ builtInTools?: string[];
21
+ /** Extra variables for the provider process (advanced settings, tests). */
22
+ env?: Record<string, string>;
23
+ /** For tests: an SDK stand-in. */
24
+ loadSdk?: () => Promise<ClaudeSdk>;
25
+ /** For tests: where to look for `claude`. */
26
+ searchEnv?: NodeJS.ProcessEnv;
27
+ startupTimeoutMs?: number;
28
+ }
29
+ export interface ClaudeSdk {
30
+ query(params: {
31
+ prompt: AsyncIterable<SDKUserMessage>;
32
+ options?: Options;
33
+ }): Query;
34
+ }
35
+ /** Logged out, Claude Code still reports `tokenSource: "none"`; a source counts only when it names one. */
36
+ export declare function signedIn(account: {
37
+ email?: string;
38
+ subscriptionType?: string;
39
+ tokenSource?: string;
40
+ apiKeySource?: string;
41
+ apiProvider?: string;
42
+ } | null | undefined): boolean;
43
+ export declare function createClaudeProvider(options?: ClaudeProviderOptions): ProviderAdapter;