@tiangong-ai/cli 0.0.18 → 0.0.20

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 (47) hide show
  1. package/AGENTS.md +8 -2
  2. package/README.md +81 -12
  3. package/dist/cli.js +3 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/research/commands.js +13 -5
  6. package/dist/research/commands.js.map +1 -1
  7. package/dist/research/config.d.ts +1 -0
  8. package/dist/research/config.js +18 -1
  9. package/dist/research/config.js.map +1 -1
  10. package/dist/research/orchestration.d.ts +3 -0
  11. package/dist/research/orchestration.js +236 -0
  12. package/dist/research/orchestration.js.map +1 -0
  13. package/dist/research/types.d.ts +1 -1
  14. package/dist/research/workspace/broker.d.ts +5 -0
  15. package/dist/research/workspace/broker.js +353 -0
  16. package/dist/research/workspace/broker.js.map +1 -0
  17. package/dist/research/workspace/capabilities.d.ts +10 -0
  18. package/dist/research/workspace/capabilities.js +291 -0
  19. package/dist/research/workspace/capabilities.js.map +1 -0
  20. package/dist/research/workspace/constants.d.ts +8 -0
  21. package/dist/research/workspace/constants.js +37 -0
  22. package/dist/research/workspace/constants.js.map +1 -0
  23. package/dist/research/workspace/context.d.ts +3 -0
  24. package/dist/research/workspace/context.js +77 -0
  25. package/dist/research/workspace/context.js.map +1 -0
  26. package/dist/research/workspace/executor.d.ts +12 -0
  27. package/dist/research/workspace/executor.js +353 -0
  28. package/dist/research/workspace/executor.js.map +1 -0
  29. package/dist/research/workspace/journal.d.ts +7 -0
  30. package/dist/research/workspace/journal.js +104 -0
  31. package/dist/research/workspace/journal.js.map +1 -0
  32. package/dist/research/workspace/projects.d.ts +9 -0
  33. package/dist/research/workspace/projects.js +230 -0
  34. package/dist/research/workspace/projects.js.map +1 -0
  35. package/dist/research/workspace/runtime.d.ts +27 -0
  36. package/dist/research/workspace/runtime.js +519 -0
  37. package/dist/research/workspace/runtime.js.map +1 -0
  38. package/dist/research/workspace/storage.d.ts +25 -0
  39. package/dist/research/workspace/storage.js +218 -0
  40. package/dist/research/workspace/storage.js.map +1 -0
  41. package/dist/research/workspace/types.d.ts +181 -0
  42. package/dist/research/workspace/types.js +2 -0
  43. package/dist/research/workspace/types.js.map +1 -0
  44. package/dist/research/workspace/workspace.d.ts +12 -0
  45. package/dist/research/workspace/workspace.js +340 -0
  46. package/dist/research/workspace/workspace.js.map +1 -0
  47. package/package.json +3 -2
@@ -0,0 +1,353 @@
1
+ import { spawn } from "node:child_process";
2
+ import { access, mkdir, realpath, writeFile } from "node:fs/promises";
3
+ import { constants as fsConstants } from "node:fs";
4
+ import { homedir, platform } from "node:os";
5
+ import { isAbsolute, join } from "node:path";
6
+ import { CliError } from "../../errors.js";
7
+ const MAX_CAPTURE_BYTES = 5 * 1024 * 1024;
8
+ const CREDENTIAL_ENV_NAME = /(^|[_-])(authorization|auth|cookie|credential|password|passwd|private[_-]?key|secret|token|access[_-]?key|api[_-]?key)([_-]|$)/i;
9
+ export async function executeAgent(request) {
10
+ validateAgentBinary(request.route);
11
+ const temporaryDirectory = join(request.capsuleRoot, "tmp");
12
+ await mkdir(temporaryDirectory, { recursive: true, mode: 0o700 });
13
+ const secrets = configuredSecrets(request.environment);
14
+ const invocation = await buildInvocation(request.route, request.prompt, request.brokerUrl, request.capsuleRoot);
15
+ const sandboxed = await sandboxInvocation(invocation.binary, invocation.args, request.capsuleRoot, request.projectRoot, request.workspaceRoot);
16
+ const started = process.hrtime.bigint();
17
+ const completed = await spawnCaptured({
18
+ binary: sandboxed.binary,
19
+ args: sandboxed.args,
20
+ cwd: request.projectRoot,
21
+ env: sanitizedEnvironment(request.environment, temporaryDirectory),
22
+ timeoutMs: request.timeoutSeconds * 1000,
23
+ });
24
+ const wallSeconds = Number(process.hrtime.bigint() - started) / 1_000_000_000;
25
+ const parsed = parseAgentResult(request.route.agent, completed.stdout, completed.stderr);
26
+ const redacted = redactResult(parsed.stdout, parsed.stderr, secrets);
27
+ const exitCode = redacted.exposed
28
+ ? 86
29
+ : completed.exitCode !== 0
30
+ ? completed.exitCode
31
+ : parsed.parseFailed
32
+ ? 3
33
+ : 0;
34
+ return {
35
+ exitCode,
36
+ stdout: redacted.stdout,
37
+ stderr: redacted.stderr,
38
+ tokens: redacted.exposed ? 0 : parsed.tokens,
39
+ costUsd: redacted.exposed ? 0 : parsed.costUsd,
40
+ wallSeconds,
41
+ };
42
+ }
43
+ async function buildInvocation(route, prompt, brokerUrl, capsuleRoot) {
44
+ if (route.agent === "codex") {
45
+ const args = [
46
+ "exec",
47
+ "--ignore-user-config",
48
+ "--ignore-rules",
49
+ "--strict-config",
50
+ "--skip-git-repo-check",
51
+ "--ephemeral",
52
+ "--color",
53
+ "never",
54
+ "--sandbox",
55
+ "workspace-write",
56
+ "-c",
57
+ 'web_search="disabled"',
58
+ "--json",
59
+ ];
60
+ if (brokerUrl) {
61
+ args.push("-c", `mcp_servers.research_broker.url=${JSON.stringify(brokerUrl)}`, "-c", "mcp_servers.research_broker.required=true", "-c", 'mcp_servers.research_broker.enabled_tools=["fetch_candidate_source"]');
62
+ }
63
+ if (route.model)
64
+ args.push("--model", route.model);
65
+ args.push(prompt);
66
+ return { binary: route.binary, args };
67
+ }
68
+ const args = [
69
+ "-p",
70
+ prompt,
71
+ "--output-format",
72
+ "json",
73
+ "--permission-mode",
74
+ "acceptEdits",
75
+ "--no-session-persistence",
76
+ "--setting-sources",
77
+ "",
78
+ "--no-chrome",
79
+ "--disable-slash-commands",
80
+ "--tools",
81
+ "Read,Write,Edit,Glob,Grep",
82
+ ];
83
+ const allowedTools = ["Read", "Write", "Edit", "Glob", "Grep"];
84
+ if (brokerUrl) {
85
+ const mcpConfigPath = join(capsuleRoot, "research-mcp.json");
86
+ await writeFile(mcpConfigPath, `${JSON.stringify({
87
+ mcpServers: {
88
+ research_broker: { type: "http", url: brokerUrl },
89
+ },
90
+ })}\n`, { encoding: "utf8", mode: 0o600 });
91
+ allowedTools.push("mcp__research_broker__fetch_candidate_source");
92
+ args.push("--strict-mcp-config", "--mcp-config", mcpConfigPath);
93
+ }
94
+ args.push("--allowedTools", allowedTools.join(","));
95
+ if (route.model)
96
+ args.push("--model", route.model);
97
+ return { binary: route.binary, args };
98
+ }
99
+ async function sandboxInvocation(binary, args, capsuleRoot, projectRoot, workspaceRoot) {
100
+ const configuredCredentialPath = join(workspaceRoot, ".tiangong-research", ".env");
101
+ const workspaceCredentialPath = await realpath(configuredCredentialPath).catch(() => configuredCredentialPath);
102
+ if (platform() === "darwin") {
103
+ const sandbox = "/usr/bin/sandbox-exec";
104
+ await requireExecutable(sandbox, "macOS sandbox-exec");
105
+ const profile = join(capsuleRoot, "execution.sb");
106
+ const policy = [
107
+ "(version 1)",
108
+ "(deny default)",
109
+ "(allow process*)",
110
+ "(allow network*)",
111
+ `(allow file-read* (require-not (literal ${sandboxString(workspaceCredentialPath)})))`,
112
+ "(allow sysctl-read)",
113
+ "(allow mach-lookup)",
114
+ "(allow ipc-posix*)",
115
+ `(allow file-write* (literal "/dev/null") (subpath ${sandboxString(capsuleRoot)}))`,
116
+ "",
117
+ ].join("\n");
118
+ await writeFile(profile, policy, { encoding: "utf8", mode: 0o600 });
119
+ return { binary: sandbox, args: ["-f", profile, binary, ...args] };
120
+ }
121
+ if (platform() === "linux") {
122
+ const bubblewrap = await firstExecutable([
123
+ "/usr/bin/bwrap",
124
+ "/usr/local/bin/bwrap",
125
+ "/bin/bwrap",
126
+ ]);
127
+ if (!bubblewrap) {
128
+ throw new CliError("Linux research execution requires Bubblewrap.", {
129
+ code: "RESEARCH_SANDBOX_UNAVAILABLE",
130
+ exitCode: 3,
131
+ });
132
+ }
133
+ const sandboxArgs = [
134
+ "--die-with-parent",
135
+ "--new-session",
136
+ "--ro-bind",
137
+ "/",
138
+ "/",
139
+ "--bind",
140
+ capsuleRoot,
141
+ capsuleRoot,
142
+ "--proc",
143
+ "/proc",
144
+ "--dev",
145
+ "/dev",
146
+ "--chdir",
147
+ projectRoot,
148
+ ];
149
+ if (await pathIsReadable(workspaceCredentialPath)) {
150
+ sandboxArgs.push("--ro-bind", "/dev/null", workspaceCredentialPath);
151
+ }
152
+ sandboxArgs.push(binary, ...args);
153
+ return {
154
+ binary: bubblewrap,
155
+ args: sandboxArgs,
156
+ };
157
+ }
158
+ throw new CliError(`Unsupported research execution platform: ${platform()}`, {
159
+ code: "RESEARCH_SANDBOX_UNAVAILABLE",
160
+ exitCode: 3,
161
+ });
162
+ }
163
+ async function pathIsReadable(path) {
164
+ try {
165
+ await access(path, fsConstants.R_OK);
166
+ return true;
167
+ }
168
+ catch {
169
+ return false;
170
+ }
171
+ }
172
+ async function spawnCaptured(input) {
173
+ return new Promise((resolvePromise, reject) => {
174
+ const child = spawn(input.binary, input.args, {
175
+ cwd: input.cwd,
176
+ env: input.env,
177
+ shell: false,
178
+ detached: platform() !== "win32",
179
+ stdio: ["ignore", "pipe", "pipe"],
180
+ });
181
+ const stdout = [];
182
+ const stderr = [];
183
+ let capturedBytes = 0;
184
+ let settled = false;
185
+ const terminate = () => {
186
+ if (!child.pid)
187
+ return;
188
+ try {
189
+ if (platform() === "win32")
190
+ child.kill("SIGKILL");
191
+ else
192
+ process.kill(-child.pid, "SIGKILL");
193
+ }
194
+ catch {
195
+ child.kill("SIGKILL");
196
+ }
197
+ };
198
+ const timer = setTimeout(() => terminate(), input.timeoutMs);
199
+ const capture = (target, chunk) => {
200
+ capturedBytes += chunk.length;
201
+ if (capturedBytes > MAX_CAPTURE_BYTES) {
202
+ terminate();
203
+ return;
204
+ }
205
+ target.push(chunk);
206
+ };
207
+ child.stdout.on("data", (chunk) => capture(stdout, chunk));
208
+ child.stderr.on("data", (chunk) => capture(stderr, chunk));
209
+ child.on("error", (error) => {
210
+ if (settled)
211
+ return;
212
+ settled = true;
213
+ clearTimeout(timer);
214
+ reject(error);
215
+ });
216
+ child.on("close", (code, signal) => {
217
+ if (settled)
218
+ return;
219
+ settled = true;
220
+ clearTimeout(timer);
221
+ const detail = signal ? `\nprocess terminated by ${signal}` : "";
222
+ resolvePromise({
223
+ exitCode: typeof code === "number" ? code : 70,
224
+ stdout: Buffer.concat(stdout).toString("utf8"),
225
+ stderr: `${Buffer.concat(stderr).toString("utf8")}${detail}`,
226
+ });
227
+ });
228
+ });
229
+ }
230
+ function parseAgentResult(agent, stdout, stderr) {
231
+ if (agent === "codex") {
232
+ let tokens = 0;
233
+ const messages = [];
234
+ let parsedEvents = 0;
235
+ for (const line of stdout.split(/\r?\n/)) {
236
+ if (!line.trim())
237
+ continue;
238
+ try {
239
+ const event = JSON.parse(line);
240
+ parsedEvents += 1;
241
+ if (event.type === "turn.completed" && isRecord(event.usage)) {
242
+ tokens = numeric(event.usage.input_tokens) + numeric(event.usage.output_tokens);
243
+ }
244
+ if (event.type === "item.completed" && isRecord(event.item)) {
245
+ if (event.item.type === "agent_message" && typeof event.item.text === "string") {
246
+ messages.push(event.item.text);
247
+ }
248
+ }
249
+ }
250
+ catch {
251
+ continue;
252
+ }
253
+ }
254
+ return {
255
+ stdout: messages.length ? messages.join("\n") : stdout,
256
+ stderr,
257
+ tokens,
258
+ costUsd: 0,
259
+ parseFailed: parsedEvents === 0,
260
+ };
261
+ }
262
+ try {
263
+ const value = JSON.parse(stdout);
264
+ const usage = isRecord(value.usage) ? value.usage : {};
265
+ return {
266
+ stdout: typeof value.result === "string" ? value.result : stdout,
267
+ stderr,
268
+ tokens: numeric(usage.input_tokens) +
269
+ numeric(usage.output_tokens) +
270
+ numeric(usage.cache_creation_input_tokens) +
271
+ numeric(usage.cache_read_input_tokens),
272
+ costUsd: numeric(value.total_cost_usd),
273
+ parseFailed: false,
274
+ };
275
+ }
276
+ catch {
277
+ return { stdout, stderr, tokens: 0, costUsd: 0, parseFailed: true };
278
+ }
279
+ }
280
+ function sanitizedEnvironment(source, temporaryDirectory) {
281
+ const environment = {};
282
+ for (const [name, value] of Object.entries(source)) {
283
+ if (!value || CREDENTIAL_ENV_NAME.test(name))
284
+ continue;
285
+ environment[name] = value;
286
+ }
287
+ environment.HOME = homedir();
288
+ environment.NO_COLOR = "1";
289
+ environment.TMPDIR = temporaryDirectory;
290
+ environment.TMP = temporaryDirectory;
291
+ environment.TEMP = temporaryDirectory;
292
+ return environment;
293
+ }
294
+ function configuredSecrets(source) {
295
+ return [
296
+ ...new Set(Object.entries(source)
297
+ .filter(([name, value]) => CREDENTIAL_ENV_NAME.test(name) && typeof value === "string" && value.length >= 8)
298
+ .map(([, value]) => value)),
299
+ ].sort((left, right) => right.length - left.length);
300
+ }
301
+ function redactResult(stdout, stderr, secrets) {
302
+ let safeStdout = stdout;
303
+ let safeStderr = stderr;
304
+ let exposed = false;
305
+ for (const secret of secrets) {
306
+ if (safeStdout.includes(secret) || safeStderr.includes(secret))
307
+ exposed = true;
308
+ safeStdout = safeStdout.replaceAll(secret, "[REDACTED]");
309
+ safeStderr = safeStderr.replaceAll(secret, "[REDACTED]");
310
+ }
311
+ return { stdout: safeStdout, stderr: safeStderr, exposed };
312
+ }
313
+ function validateAgentBinary(route) {
314
+ if (!isAbsolute(route.binary) && route.binary !== route.agent) {
315
+ throw new CliError(`Configured ${route.agent} binary must be absolute or exactly '${route.agent}'.`, {
316
+ code: "RESEARCH_EXECUTOR_INVALID",
317
+ exitCode: 2,
318
+ });
319
+ }
320
+ }
321
+ async function requireExecutable(path, label) {
322
+ try {
323
+ await access(path, fsConstants.X_OK);
324
+ }
325
+ catch {
326
+ throw new CliError(`${label} is unavailable: ${path}`, {
327
+ code: "RESEARCH_SANDBOX_UNAVAILABLE",
328
+ exitCode: 3,
329
+ });
330
+ }
331
+ }
332
+ async function firstExecutable(candidates) {
333
+ for (const candidate of candidates) {
334
+ try {
335
+ await access(candidate, fsConstants.X_OK);
336
+ return candidate;
337
+ }
338
+ catch {
339
+ continue;
340
+ }
341
+ }
342
+ return undefined;
343
+ }
344
+ function sandboxString(value) {
345
+ return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
346
+ }
347
+ function isRecord(value) {
348
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
349
+ }
350
+ function numeric(value) {
351
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
352
+ }
353
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../src/research/workspace/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { JournalEvent } from "./types.js";
2
+ export declare function appendJournalEvent(journalPath: string, type: string, scope: string, payload: Record<string, unknown>): Promise<JournalEvent>;
3
+ export declare function readJournal(journalPath: string): Promise<JournalEvent[]>;
4
+ export declare function verifyJournal(journalPath: string): Promise<{
5
+ events: number;
6
+ head: string;
7
+ }>;
@@ -0,0 +1,104 @@
1
+ import { appendFile, readFile } from "node:fs/promises";
2
+ import { CliError } from "../../errors.js";
3
+ import { canonicalJson, pathExists, sha256Text } from "./storage.js";
4
+ const GENESIS_HASH = "0".repeat(64);
5
+ const journalQueues = new Map();
6
+ export async function appendJournalEvent(journalPath, type, scope, payload) {
7
+ const previousWrite = journalQueues.get(journalPath) ?? Promise.resolve();
8
+ let releaseWrite = () => undefined;
9
+ const currentWrite = new Promise((resolvePromise) => {
10
+ releaseWrite = resolvePromise;
11
+ });
12
+ const queuedWrite = previousWrite.catch(() => undefined).then(() => currentWrite);
13
+ journalQueues.set(journalPath, queuedWrite);
14
+ await previousWrite.catch(() => undefined);
15
+ try {
16
+ return await appendJournalEventNow(journalPath, type, scope, payload);
17
+ }
18
+ finally {
19
+ releaseWrite();
20
+ if (journalQueues.get(journalPath) === queuedWrite)
21
+ journalQueues.delete(journalPath);
22
+ }
23
+ }
24
+ async function appendJournalEventNow(journalPath, type, scope, payload) {
25
+ const events = await readJournal(journalPath);
26
+ verifyJournalEvents(events);
27
+ const previous = events.at(-1);
28
+ const unsigned = {
29
+ schemaVersion: 1,
30
+ sequence: events.length + 1,
31
+ timestamp: new Date().toISOString(),
32
+ type,
33
+ scope,
34
+ payload,
35
+ previousHash: previous?.hash ?? GENESIS_HASH,
36
+ };
37
+ const event = { ...unsigned, hash: sha256Text(canonicalJson(unsigned)) };
38
+ await appendFile(journalPath, `${JSON.stringify(event)}\n`, { encoding: "utf8", mode: 0o600 });
39
+ return event;
40
+ }
41
+ export async function readJournal(journalPath) {
42
+ if (!(await pathExists(journalPath)))
43
+ return [];
44
+ const content = await readFile(journalPath, "utf8");
45
+ if (!content.trim())
46
+ return [];
47
+ return content
48
+ .split(/\r?\n/)
49
+ .filter((line) => line.trim())
50
+ .map((line, index) => {
51
+ try {
52
+ return JSON.parse(line);
53
+ }
54
+ catch (error) {
55
+ throw new CliError(`Research journal line ${index + 1} is invalid JSON.`, {
56
+ code: "RESEARCH_JOURNAL_INVALID",
57
+ exitCode: 2,
58
+ details: { error: String(error) },
59
+ });
60
+ }
61
+ });
62
+ }
63
+ export async function verifyJournal(journalPath) {
64
+ const events = await readJournal(journalPath);
65
+ verifyJournalEvents(events);
66
+ return { events: events.length, head: events.at(-1)?.hash ?? GENESIS_HASH };
67
+ }
68
+ function verifyJournalEvents(events) {
69
+ let previousHash = GENESIS_HASH;
70
+ for (const [index, event] of events.entries()) {
71
+ if (event.schemaVersion !== 1 ||
72
+ event.sequence !== index + 1 ||
73
+ event.previousHash !== previousHash ||
74
+ typeof event.timestamp !== "string" ||
75
+ typeof event.type !== "string" ||
76
+ typeof event.scope !== "string" ||
77
+ !event.payload ||
78
+ typeof event.payload !== "object" ||
79
+ Array.isArray(event.payload)) {
80
+ throw new CliError(`Research journal event ${index + 1} is malformed.`, {
81
+ code: "RESEARCH_JOURNAL_INVALID",
82
+ exitCode: 2,
83
+ });
84
+ }
85
+ const unsigned = {
86
+ schemaVersion: event.schemaVersion,
87
+ sequence: event.sequence,
88
+ timestamp: event.timestamp,
89
+ type: event.type,
90
+ scope: event.scope,
91
+ payload: event.payload,
92
+ previousHash: event.previousHash,
93
+ };
94
+ const expected = sha256Text(canonicalJson(unsigned));
95
+ if (event.hash !== expected) {
96
+ throw new CliError(`Research journal event ${event.sequence} failed its hash check.`, {
97
+ code: "RESEARCH_JOURNAL_TAMPERED",
98
+ exitCode: 2,
99
+ });
100
+ }
101
+ previousHash = event.hash;
102
+ }
103
+ }
104
+ //# sourceMappingURL=journal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal.js","sourceRoot":"","sources":["../../../src/research/workspace/journal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ProjectInput, ProjectState, WorkPackage } from "./types.js";
2
+ export declare function initializeProject(root: string, projectId: string, question: string): Promise<ProjectState>;
3
+ export declare function addProjectInput(root: string, projectId: string, inputPath: string, role: ProjectInput["role"]): Promise<ProjectInput>;
4
+ export declare function loadProject(root: string, projectId: string): Promise<ProjectState>;
5
+ export declare function listProjects(root: string): Promise<ProjectState[]>;
6
+ export declare function saveProject(root: string, project: ProjectState): Promise<void>;
7
+ export declare function refreshProject(project: ProjectState): ProjectState;
8
+ export declare function nextReadyPackage(project: ProjectState): WorkPackage | undefined;
9
+ export declare function packageById(project: ProjectState, packageId: string): WorkPackage;