agentcord 0.1.8 → 0.1.9

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/dist/cli.js CHANGED
@@ -4,7 +4,7 @@
4
4
  var command = process.argv[2];
5
5
  switch (command) {
6
6
  case "setup": {
7
- const { runSetup } = await import("./setup-PKUBTHQH.js");
7
+ const { runSetup } = await import("./setup-FO4HRB3B.js");
8
8
  await runSetup();
9
9
  break;
10
10
  }
@@ -18,7 +18,7 @@ switch (command) {
18
18
  console.log("Run \x1B[36magentcord setup\x1B[0m to configure.\n");
19
19
  process.exit(1);
20
20
  }
21
- const { startBot } = await import("./bot-MUOL7CRV.js");
21
+ const { startBot } = await import("./bot-MOK6APSV.js");
22
22
  console.log("agentcord starting...");
23
23
  await startBot();
24
24
  break;
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/providers/codex-provider.ts
4
+ import { writeFileSync, readFileSync, unlinkSync, existsSync, mkdtempSync } from "fs";
5
+ import { join } from "path";
6
+ import { tmpdir } from "os";
7
+ var Codex;
8
+ async function loadSdk() {
9
+ if (Codex) return;
10
+ const mod = await import("@openai/codex-sdk");
11
+ Codex = mod.Codex;
12
+ }
13
+ var SENTINEL_START = "<!-- agentcord-persona-start -->";
14
+ var SENTINEL_END = "<!-- agentcord-persona-end -->";
15
+ function injectAgentsMd(directory, parts) {
16
+ if (parts.length === 0) return null;
17
+ const agentsPath = join(directory, "AGENTS.md");
18
+ const injected = `${SENTINEL_START}
19
+ ${parts.join("\n\n")}
20
+ ${SENTINEL_END}`;
21
+ let original = null;
22
+ if (existsSync(agentsPath)) {
23
+ original = readFileSync(agentsPath, "utf-8");
24
+ const cleaned = original.replace(new RegExp(`${escapeRegex(SENTINEL_START)}[\\s\\S]*?${escapeRegex(SENTINEL_END)}\\n?`), "");
25
+ writeFileSync(agentsPath, cleaned + "\n" + injected + "\n", "utf-8");
26
+ } else {
27
+ writeFileSync(agentsPath, injected + "\n", "utf-8");
28
+ }
29
+ return original;
30
+ }
31
+ function restoreAgentsMd(directory, original) {
32
+ const agentsPath = join(directory, "AGENTS.md");
33
+ if (original === null) {
34
+ try {
35
+ unlinkSync(agentsPath);
36
+ } catch {
37
+ }
38
+ } else if (original !== void 0) {
39
+ writeFileSync(agentsPath, original, "utf-8");
40
+ }
41
+ }
42
+ function escapeRegex(s) {
43
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
44
+ }
45
+ function writeImagesToTemp(blocks) {
46
+ const textParts = [];
47
+ const localImages = [];
48
+ for (const block of blocks) {
49
+ if (block.type === "text") {
50
+ textParts.push(block.text);
51
+ } else if (block.type === "image") {
52
+ const dir = mkdtempSync(join(tmpdir(), "agentcord-img-"));
53
+ const ext = block.source.media_type.split("/")[1] || "png";
54
+ const filePath = join(dir, `image.${ext}`);
55
+ writeFileSync(filePath, Buffer.from(block.source.data, "base64"));
56
+ localImages.push({ type: "local_image", path: filePath });
57
+ } else if (block.type === "local_image") {
58
+ localImages.push(block);
59
+ }
60
+ }
61
+ return { textParts, localImages };
62
+ }
63
+ var CodexProvider = class {
64
+ name = "codex";
65
+ supports(feature) {
66
+ return [
67
+ "command_execution",
68
+ "file_changes",
69
+ "reasoning",
70
+ "todo_list",
71
+ "continue"
72
+ ].includes(feature);
73
+ }
74
+ async *sendPrompt(prompt, options) {
75
+ await loadSdk();
76
+ let input;
77
+ if (typeof prompt === "string") {
78
+ input = prompt;
79
+ } else {
80
+ const { textParts, localImages } = writeImagesToTemp(prompt);
81
+ const inputParts = [];
82
+ for (const img of localImages) {
83
+ inputParts.push({ type: "local_image", path: img.path });
84
+ }
85
+ if (textParts.length > 0) {
86
+ inputParts.push({ type: "text", text: textParts.join("\n") });
87
+ }
88
+ input = inputParts.length === 1 && inputParts[0].type === "text" ? inputParts[0].text : inputParts;
89
+ }
90
+ let originalAgents = null;
91
+ try {
92
+ originalAgents = injectAgentsMd(options.directory, options.systemPromptParts);
93
+ const codex = new Codex();
94
+ const threadOptions = {
95
+ workingDirectory: options.directory,
96
+ skipGitRepoCheck: true
97
+ };
98
+ if (options.model) threadOptions.model = options.model;
99
+ const thread = options.providerSessionId ? codex.resumeThread(options.providerSessionId, threadOptions) : codex.startThread(threadOptions);
100
+ const { events } = await thread.runStreamed(input);
101
+ yield* this.translateEvents(events, options.abortController);
102
+ } finally {
103
+ restoreAgentsMd(options.directory, originalAgents);
104
+ }
105
+ }
106
+ async *continueSession(options) {
107
+ await loadSdk();
108
+ if (!options.providerSessionId) {
109
+ yield { type: "error", message: "No session to continue \u2014 no previous thread ID." };
110
+ return;
111
+ }
112
+ let originalAgents = null;
113
+ try {
114
+ originalAgents = injectAgentsMd(options.directory, options.systemPromptParts);
115
+ const codex = new Codex();
116
+ const threadOptions = {
117
+ workingDirectory: options.directory,
118
+ skipGitRepoCheck: true
119
+ };
120
+ if (options.model) threadOptions.model = options.model;
121
+ const thread = codex.resumeThread(options.providerSessionId, threadOptions);
122
+ const { events } = await thread.runStreamed("Continue from where you left off.");
123
+ yield* this.translateEvents(events, options.abortController);
124
+ } finally {
125
+ restoreAgentsMd(options.directory, originalAgents);
126
+ }
127
+ }
128
+ async *translateEvents(events, abortController) {
129
+ const messageText = /* @__PURE__ */ new Map();
130
+ const startTime = Date.now();
131
+ try {
132
+ for await (const event of events) {
133
+ if (abortController.signal.aborted) break;
134
+ switch (event.type) {
135
+ case "thread.started":
136
+ yield { type: "session_init", providerSessionId: event.thread_id };
137
+ break;
138
+ case "item.started":
139
+ case "item.updated": {
140
+ const item = event.item;
141
+ if (!item) break;
142
+ if (item.type === "agent_message") {
143
+ const prev = messageText.get(item.id) || "";
144
+ const text = item.text || "";
145
+ if (text.length > prev.length) {
146
+ yield { type: "text_delta", text: text.slice(prev.length) };
147
+ messageText.set(item.id, text);
148
+ }
149
+ }
150
+ if (item.type === "reasoning" && event.type === "item.updated") {
151
+ const text = item.summary || item.content || "";
152
+ if (text) {
153
+ yield { type: "reasoning", text };
154
+ }
155
+ }
156
+ break;
157
+ }
158
+ case "item.completed": {
159
+ const item = event.item;
160
+ if (!item) break;
161
+ switch (item.type) {
162
+ case "agent_message": {
163
+ const prev = messageText.get(item.id) || "";
164
+ const text = item.text || "";
165
+ if (text.length > prev.length) {
166
+ yield { type: "text_delta", text: text.slice(prev.length) };
167
+ }
168
+ messageText.delete(item.id);
169
+ break;
170
+ }
171
+ case "command_execution":
172
+ yield {
173
+ type: "command_execution",
174
+ command: item.command || "",
175
+ output: item.output || "",
176
+ exitCode: item.exit_code ?? item.exitCode ?? null,
177
+ status: item.status || "completed"
178
+ };
179
+ break;
180
+ case "file_change": {
181
+ const changes = (item.changes || item.files || []).map((f) => ({
182
+ filePath: f.file_path || f.filePath || f.path || "",
183
+ changeKind: f.change_kind || f.changeKind || f.action || "update"
184
+ }));
185
+ if (changes.length > 0) {
186
+ yield { type: "file_change", changes };
187
+ }
188
+ break;
189
+ }
190
+ case "reasoning": {
191
+ const text = item.summary || item.content || "";
192
+ if (text) {
193
+ yield { type: "reasoning", text };
194
+ }
195
+ break;
196
+ }
197
+ case "todo_list": {
198
+ const items = (item.items || item.todos || []).map((t) => ({
199
+ text: t.text || t.description || "",
200
+ completed: t.completed ?? t.done ?? false
201
+ }));
202
+ if (items.length > 0) {
203
+ yield { type: "todo_list", items };
204
+ }
205
+ break;
206
+ }
207
+ case "mcp_tool_call":
208
+ yield {
209
+ type: "tool_start",
210
+ toolName: `${item.server}/${item.tool}`,
211
+ toolInput: JSON.stringify(item.arguments || item.input || {})
212
+ };
213
+ if (item.status === "completed" || item.status === "failed") {
214
+ yield {
215
+ type: "tool_result",
216
+ toolName: `${item.server}/${item.tool}`,
217
+ result: typeof item.output === "string" ? item.output : JSON.stringify(item.output || ""),
218
+ isError: item.status === "failed"
219
+ };
220
+ }
221
+ break;
222
+ case "error":
223
+ yield { type: "error", message: item.message || "Unknown error" };
224
+ break;
225
+ }
226
+ break;
227
+ }
228
+ case "turn.completed": {
229
+ const usage = event.usage;
230
+ const inputTokens = usage?.input_tokens || 0;
231
+ const outputTokens = usage?.output_tokens || 0;
232
+ const costUsd = (inputTokens * 2 + outputTokens * 8) / 1e6;
233
+ yield {
234
+ type: "result",
235
+ success: true,
236
+ costUsd,
237
+ durationMs: Date.now() - startTime,
238
+ numTurns: 1,
239
+ errors: []
240
+ };
241
+ break;
242
+ }
243
+ case "turn.failed":
244
+ yield {
245
+ type: "result",
246
+ success: false,
247
+ costUsd: 0,
248
+ durationMs: Date.now() - startTime,
249
+ numTurns: 1,
250
+ errors: [event.error || "Turn failed"]
251
+ };
252
+ break;
253
+ case "error":
254
+ yield { type: "error", message: event.message || "Unknown error" };
255
+ break;
256
+ }
257
+ }
258
+ } catch (err) {
259
+ if (!abortController.signal.aborted) {
260
+ yield { type: "error", message: err.message || "Codex stream error" };
261
+ }
262
+ }
263
+ }
264
+ };
265
+ export {
266
+ CodexProvider
267
+ };
@@ -300,6 +300,21 @@ async function runSetup() {
300
300
  p.log.info(`You can re-run setup with: ${cyan("npm run setup")}`);
301
301
  }
302
302
  }
303
+ const installCodex = await p.confirm({
304
+ message: "Install OpenAI Codex support? (requires @openai/codex-sdk)",
305
+ initialValue: false
306
+ });
307
+ if (p.isCancel(installCodex)) cancelled();
308
+ if (installCodex) {
309
+ s.start("Installing @openai/codex-sdk...");
310
+ try {
311
+ execSync("npm install @openai/codex-sdk", { cwd: process.cwd(), stdio: "pipe" });
312
+ s.stop(green("Codex SDK installed"));
313
+ } catch (err) {
314
+ s.stop(`Failed to install Codex SDK: ${err.message}`);
315
+ p.log.warn(`You can install it manually: ${cyan("npm install @openai/codex-sdk")}`);
316
+ }
317
+ }
303
318
  const installDaemon = await p.confirm({
304
319
  message: "Start agentcord as a background service? (auto-starts on boot, restarts on crash)",
305
320
  initialValue: true
@@ -317,7 +332,7 @@ async function runSetup() {
317
332
  }
318
333
  }
319
334
  const nextSteps = [
320
- `Use ${bold("/claude new <name> <directory>")} in Discord to create your first session.`
335
+ `Use ${bold("/session new <name>")} in Discord to create your first session.`
321
336
  ];
322
337
  if (!installDaemon) {
323
338
  nextSteps.unshift(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentcord",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "description": "Discord bot for managing AI coding agent sessions (Claude Code, Codex, and more)",
6
6
  "bin": {
@@ -53,6 +53,9 @@
53
53
  "dotenv": "^17.2.4",
54
54
  "sharp": "^0.34.5"
55
55
  },
56
+ "optionalDependencies": {
57
+ "@openai/codex-sdk": "^0.98.0"
58
+ },
56
59
  "devDependencies": {
57
60
  "@types/node": "^22.10.0",
58
61
  "tsup": "^8.5.1",