office-core 0.1.0
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/.runtime-dist/scripts/bundle-host-package.js +46 -0
- package/.runtime-dist/scripts/demo-multi-agent.js +130 -0
- package/.runtime-dist/scripts/home-agent-host.js +1403 -0
- package/.runtime-dist/scripts/host-doctor.js +28 -0
- package/.runtime-dist/scripts/host-login.js +32 -0
- package/.runtime-dist/scripts/host-menu.js +227 -0
- package/.runtime-dist/scripts/host-open.js +20 -0
- package/.runtime-dist/scripts/install-host.js +108 -0
- package/.runtime-dist/scripts/lib/host-config.js +171 -0
- package/.runtime-dist/scripts/lib/local-runner.js +287 -0
- package/.runtime-dist/scripts/office-cli.js +698 -0
- package/.runtime-dist/scripts/run-local-project.js +277 -0
- package/.runtime-dist/src/auth/session-token.js +62 -0
- package/.runtime-dist/src/discord/outbox-ledger.js +56 -0
- package/.runtime-dist/src/do/AgentDO.js +205 -0
- package/.runtime-dist/src/do/GatewayShardDO.js +9 -0
- package/.runtime-dist/src/do/ProjectDO.js +829 -0
- package/.runtime-dist/src/do/TaskDO.js +356 -0
- package/.runtime-dist/src/index.js +123 -0
- package/.runtime-dist/src/project/office-view.js +405 -0
- package/.runtime-dist/src/project/read-model.js +79 -0
- package/.runtime-dist/src/routes/agents-bootstrap.js +9 -0
- package/.runtime-dist/src/routes/agents-descriptor.js +12 -0
- package/.runtime-dist/src/routes/agents-events.js +17 -0
- package/.runtime-dist/src/routes/agents-heartbeat.js +21 -0
- package/.runtime-dist/src/routes/agents-task-context.js +17 -0
- package/.runtime-dist/src/routes/bundles.js +198 -0
- package/.runtime-dist/src/routes/local-host.js +49 -0
- package/.runtime-dist/src/routes/projects.js +119 -0
- package/.runtime-dist/src/routes/tasks.js +67 -0
- package/.runtime-dist/src/task/reducer.js +464 -0
- package/.runtime-dist/src/types/project.js +1 -0
- package/.runtime-dist/src/types/protocol.js +3 -0
- package/.runtime-dist/src/types/runtime.js +1 -0
- package/README.md +148 -0
- package/bin/double-penetration-host.mjs +83 -0
- package/package.json +48 -0
- package/public/index.html +1581 -0
- package/public/install-host.ps1 +64 -0
- package/scripts/run-runtime-script.mjs +43 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
6
|
+
export async function runLocalRunner(input) {
|
|
7
|
+
if (input.runner === "codex") {
|
|
8
|
+
return runCodex(input);
|
|
9
|
+
}
|
|
10
|
+
return runClaude(input);
|
|
11
|
+
}
|
|
12
|
+
export function resolveRunnerCommand(kind, override) {
|
|
13
|
+
if (override?.trim()) {
|
|
14
|
+
return override.trim();
|
|
15
|
+
}
|
|
16
|
+
if (process.platform !== "win32") {
|
|
17
|
+
return kind;
|
|
18
|
+
}
|
|
19
|
+
const executableName = `${kind}.exe`;
|
|
20
|
+
const byExactExe = queryWhere(executableName);
|
|
21
|
+
if (byExactExe.length > 0) {
|
|
22
|
+
return byExactExe[0];
|
|
23
|
+
}
|
|
24
|
+
const byCommandName = queryWhere(kind);
|
|
25
|
+
const preferred = byCommandName.find((candidate) => candidate.toLowerCase().endsWith(".exe"));
|
|
26
|
+
if (preferred) {
|
|
27
|
+
return preferred;
|
|
28
|
+
}
|
|
29
|
+
const fallbacks = buildWindowsFallbacks(kind);
|
|
30
|
+
const direct = fallbacks.find((candidate) => existsSync(candidate));
|
|
31
|
+
if (direct) {
|
|
32
|
+
return direct;
|
|
33
|
+
}
|
|
34
|
+
return executableName;
|
|
35
|
+
}
|
|
36
|
+
export function probeRunnerAvailability(kind, override) {
|
|
37
|
+
const command = resolveRunnerCommand(kind, override);
|
|
38
|
+
if (process.platform === "win32") {
|
|
39
|
+
return { available: existsSync(command) || queryWhere(command).length > 0, command };
|
|
40
|
+
}
|
|
41
|
+
return { available: true, command };
|
|
42
|
+
}
|
|
43
|
+
async function runCodex(input) {
|
|
44
|
+
const outputDir = await mkdtemp(path.join(os.tmpdir(), "office-codex-"));
|
|
45
|
+
const command = resolveRunnerCommand("codex", process.env.CODEX_CMD);
|
|
46
|
+
const args = [
|
|
47
|
+
"exec",
|
|
48
|
+
"--json",
|
|
49
|
+
"--skip-git-repo-check",
|
|
50
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
51
|
+
"-C",
|
|
52
|
+
input.workdir,
|
|
53
|
+
"-",
|
|
54
|
+
];
|
|
55
|
+
const effort = normalizeEffort(input.effort);
|
|
56
|
+
if (effort) {
|
|
57
|
+
args.splice(args.length - 1, 0, "-c", `model_reasoning_effort="${effort}"`);
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
return await runJsonProcess(command, args, input.workdir, input.prompt, "codex", input);
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
await rm(outputDir, { recursive: true, force: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function runClaude(input) {
|
|
67
|
+
const command = resolveRunnerCommand("claude", process.env.CLAUDE_CMD);
|
|
68
|
+
const args = [
|
|
69
|
+
"-p",
|
|
70
|
+
"--verbose",
|
|
71
|
+
"--output-format",
|
|
72
|
+
"stream-json",
|
|
73
|
+
"--include-partial-messages",
|
|
74
|
+
"--dangerously-skip-permissions",
|
|
75
|
+
"--add-dir",
|
|
76
|
+
input.workdir,
|
|
77
|
+
];
|
|
78
|
+
const effort = normalizeEffort(input.effort);
|
|
79
|
+
if (effort) {
|
|
80
|
+
args.push("--effort", effort);
|
|
81
|
+
}
|
|
82
|
+
return runJsonProcess(command, args, input.workdir, input.prompt, "claude", input);
|
|
83
|
+
}
|
|
84
|
+
function queryWhere(command) {
|
|
85
|
+
const result = spawnSync("where.exe", [command], {
|
|
86
|
+
encoding: "utf8",
|
|
87
|
+
windowsHide: true,
|
|
88
|
+
});
|
|
89
|
+
if (result.status !== 0) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
return String(result.stdout ?? "")
|
|
93
|
+
.split(/\r?\n/)
|
|
94
|
+
.map((line) => line.trim())
|
|
95
|
+
.filter(Boolean);
|
|
96
|
+
}
|
|
97
|
+
function buildWindowsFallbacks(kind) {
|
|
98
|
+
const localAppData = process.env.LOCALAPPDATA ?? "";
|
|
99
|
+
const appData = process.env.APPDATA ?? "";
|
|
100
|
+
const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
|
|
101
|
+
if (kind === "codex") {
|
|
102
|
+
return [
|
|
103
|
+
path.join(localAppData, "OpenAI", "Codex", "bin", "codex.exe"),
|
|
104
|
+
path.join(localAppData, "Programs", "OpenAI", "Codex", "bin", "codex.exe"),
|
|
105
|
+
path.join(appData, "npm", "codex.cmd"),
|
|
106
|
+
path.join(programFiles, "WindowsApps", "OpenAI.Codex_26.325.3894.0_x64__2p2nqsd0c76g0", "app", "resources", "codex.exe"),
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
return [
|
|
110
|
+
path.join(process.env.USERPROFILE ?? "", ".local", "bin", "claude.exe"),
|
|
111
|
+
path.join(appData, "npm", "claude.cmd"),
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
async function runJsonProcess(command, args, cwd, stdinText, parserKind, input) {
|
|
115
|
+
const timeout_ms = input.timeout_ms ?? 15 * 60 * 1000;
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const child = spawn(command, args, {
|
|
118
|
+
cwd,
|
|
119
|
+
env: process.env,
|
|
120
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
121
|
+
windowsHide: true,
|
|
122
|
+
});
|
|
123
|
+
let stdout = "";
|
|
124
|
+
let stderr = "";
|
|
125
|
+
let stdoutBuffer = "";
|
|
126
|
+
let resultText = "";
|
|
127
|
+
let sawPartial = false;
|
|
128
|
+
const timer = setTimeout(() => {
|
|
129
|
+
child.kill();
|
|
130
|
+
reject(new Error(`Runner timed out after ${timeout_ms}ms`));
|
|
131
|
+
}, timeout_ms);
|
|
132
|
+
child.stdin.write(stdinText);
|
|
133
|
+
child.stdin.end();
|
|
134
|
+
child.stdout.on("data", (chunk) => {
|
|
135
|
+
const text = String(chunk);
|
|
136
|
+
stdout += text;
|
|
137
|
+
stdoutBuffer += text;
|
|
138
|
+
let newlineIndex = stdoutBuffer.indexOf("\n");
|
|
139
|
+
while (newlineIndex >= 0) {
|
|
140
|
+
const line = stdoutBuffer.slice(0, newlineIndex).trim();
|
|
141
|
+
stdoutBuffer = stdoutBuffer.slice(newlineIndex + 1);
|
|
142
|
+
if (line) {
|
|
143
|
+
const parsed = parseRunnerJsonLine(parserKind, line, input);
|
|
144
|
+
if (parsed.status) {
|
|
145
|
+
input.onStatus?.(parsed.status);
|
|
146
|
+
}
|
|
147
|
+
if (parsed.partial) {
|
|
148
|
+
sawPartial = true;
|
|
149
|
+
resultText += parsed.partial;
|
|
150
|
+
input.onPartial?.(parsed.partial);
|
|
151
|
+
}
|
|
152
|
+
if (parsed.finalText) {
|
|
153
|
+
resultText = parsed.finalText;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
newlineIndex = stdoutBuffer.indexOf("\n");
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
child.stderr.on("data", (chunk) => {
|
|
160
|
+
stderr += String(chunk);
|
|
161
|
+
});
|
|
162
|
+
child.on("error", (error) => {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
reject(error);
|
|
165
|
+
});
|
|
166
|
+
child.on("close", (code) => {
|
|
167
|
+
clearTimeout(timer);
|
|
168
|
+
if (code !== 0) {
|
|
169
|
+
reject(new Error(summarizeRunnerFailure(code, stdout, stderr)));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const trailing = stdoutBuffer.trim();
|
|
173
|
+
if (trailing) {
|
|
174
|
+
const parsed = parseRunnerJsonLine(parserKind, trailing, input);
|
|
175
|
+
if (parsed.status) {
|
|
176
|
+
input.onStatus?.(parsed.status);
|
|
177
|
+
}
|
|
178
|
+
if (parsed.partial) {
|
|
179
|
+
sawPartial = true;
|
|
180
|
+
resultText += parsed.partial;
|
|
181
|
+
input.onPartial?.(parsed.partial);
|
|
182
|
+
}
|
|
183
|
+
if (parsed.finalText) {
|
|
184
|
+
resultText = parsed.finalText;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!resultText.trim() && !sawPartial) {
|
|
188
|
+
resultText = extractFallbackText(parserKind, stdout, stderr);
|
|
189
|
+
}
|
|
190
|
+
resolve(resultText.trim());
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function normalizeEffort(value) {
|
|
195
|
+
if (value === "low" || value === "medium" || value === "high" || value === "max") {
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
function parseRunnerJsonLine(kind, line, input) {
|
|
201
|
+
try {
|
|
202
|
+
const payload = JSON.parse(line);
|
|
203
|
+
if (kind === "claude") {
|
|
204
|
+
return parseClaudeJsonLine(payload);
|
|
205
|
+
}
|
|
206
|
+
return parseCodexJsonLine(payload, input);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return {};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function parseClaudeJsonLine(payload) {
|
|
213
|
+
if (payload?.type === "stream_event") {
|
|
214
|
+
const event = payload.event;
|
|
215
|
+
if (event?.type === "content_block_delta" && event.delta?.type === "text_delta") {
|
|
216
|
+
return { partial: String(event.delta.text ?? "") };
|
|
217
|
+
}
|
|
218
|
+
if (event?.type === "message_start") {
|
|
219
|
+
return { status: "thinking" };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (payload?.type === "assistant") {
|
|
223
|
+
const finalText = Array.isArray(payload.message?.content)
|
|
224
|
+
? payload.message.content
|
|
225
|
+
.filter((entry) => entry?.type === "text")
|
|
226
|
+
.map((entry) => String(entry.text ?? ""))
|
|
227
|
+
.join("")
|
|
228
|
+
: "";
|
|
229
|
+
return finalText ? { finalText } : {};
|
|
230
|
+
}
|
|
231
|
+
if (payload?.type === "result" && typeof payload.result === "string") {
|
|
232
|
+
return { finalText: payload.result };
|
|
233
|
+
}
|
|
234
|
+
return {};
|
|
235
|
+
}
|
|
236
|
+
function parseCodexJsonLine(payload, input) {
|
|
237
|
+
if (payload?.type === "thread.started") {
|
|
238
|
+
return { status: "starting" };
|
|
239
|
+
}
|
|
240
|
+
if (payload?.type === "turn.started") {
|
|
241
|
+
return { status: "thinking" };
|
|
242
|
+
}
|
|
243
|
+
if (payload?.type === "item.completed" && payload.item?.type === "agent_message") {
|
|
244
|
+
const finalText = String(payload.item.text ?? "").trim();
|
|
245
|
+
return finalText ? { finalText } : {};
|
|
246
|
+
}
|
|
247
|
+
if (payload?.type === "turn.completed") {
|
|
248
|
+
return { status: "complete" };
|
|
249
|
+
}
|
|
250
|
+
if (payload?.type === "item.started" && payload.item?.type) {
|
|
251
|
+
return { status: String(payload.item.type) };
|
|
252
|
+
}
|
|
253
|
+
return {};
|
|
254
|
+
}
|
|
255
|
+
function extractFallbackText(kind, stdout, stderr) {
|
|
256
|
+
if (kind === "claude") {
|
|
257
|
+
const resultMatch = stdout.match(/"result":"([^"]*)"/);
|
|
258
|
+
if (resultMatch?.[1]) {
|
|
259
|
+
return resultMatch[1];
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return (stdout || stderr).trim();
|
|
263
|
+
}
|
|
264
|
+
function summarizeRunnerFailure(code, stdout, stderr) {
|
|
265
|
+
const combined = `${stdout}\n${stderr}`.trim();
|
|
266
|
+
const lines = combined.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
267
|
+
const usageLine = lines.find((line) => /usage limit|out of extra usage|try again at/i.test(line));
|
|
268
|
+
if (usageLine) {
|
|
269
|
+
return `Runner unavailable: ${usageLine}`;
|
|
270
|
+
}
|
|
271
|
+
const errorMessageLine = lines.find((line) => /"message":|^error[:\s]/i.test(line));
|
|
272
|
+
if (errorMessageLine) {
|
|
273
|
+
const cleaned = errorMessageLine
|
|
274
|
+
.replace(/^error[:\s]*/i, "")
|
|
275
|
+
.replace(/^.*"message":"?/, "")
|
|
276
|
+
.replace(/"}?[,]?$/, "")
|
|
277
|
+
.trim();
|
|
278
|
+
if (cleaned) {
|
|
279
|
+
return `Runner failed: ${cleaned}`;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return `Runner exited with code ${code}${combined ? `: ${truncateOneLine(combined)}` : ""}`;
|
|
283
|
+
}
|
|
284
|
+
function truncateOneLine(value, max = 220) {
|
|
285
|
+
const single = value.replace(/\s+/g, " ").trim();
|
|
286
|
+
return single.length <= max ? single : `${single.slice(0, max - 1)}…`;
|
|
287
|
+
}
|