@orchagent/cli 0.3.63 → 0.3.65
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/commands/index.js +2 -0
- package/dist/commands/init.js +452 -16
- package/dist/commands/publish.js +138 -9
- package/dist/commands/run.js +72 -0
- package/dist/commands/secrets.js +174 -0
- package/dist/commands/templates/github-weekly-summary.js +884 -0
- package/dist/lib/api.js +15 -6
- package/package.json +1 -1
package/dist/commands/index.js
CHANGED
|
@@ -34,6 +34,7 @@ const service_1 = require("./service");
|
|
|
34
34
|
const transfer_1 = require("./transfer");
|
|
35
35
|
const pull_1 = require("./pull");
|
|
36
36
|
const logs_1 = require("./logs");
|
|
37
|
+
const secrets_1 = require("./secrets");
|
|
37
38
|
function registerCommands(program) {
|
|
38
39
|
(0, login_1.registerLoginCommand)(program);
|
|
39
40
|
(0, whoami_1.registerWhoamiCommand)(program);
|
|
@@ -68,4 +69,5 @@ function registerCommands(program) {
|
|
|
68
69
|
(0, transfer_1.registerTransferCommand)(program);
|
|
69
70
|
(0, pull_1.registerPullCommand)(program);
|
|
70
71
|
(0, logs_1.registerLogsCommand)(program);
|
|
72
|
+
(0, secrets_1.registerSecretsCommand)(program);
|
|
71
73
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.registerInitCommand = registerInitCommand;
|
|
|
7
7
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const errors_1 = require("../lib/errors");
|
|
10
|
+
const github_weekly_summary_1 = require("./templates/github-weekly-summary");
|
|
10
11
|
const MANIFEST_TEMPLATE = `{
|
|
11
12
|
"name": "my-agent",
|
|
12
13
|
"description": "A simple AI agent",
|
|
@@ -86,15 +87,73 @@ if __name__ == "__main__":
|
|
|
86
87
|
main()
|
|
87
88
|
`;
|
|
88
89
|
function readmeTemplate(agentName, flavor) {
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
if (flavor === 'discord') {
|
|
91
|
+
return `# ${agentName}
|
|
92
|
+
|
|
93
|
+
An always-on Discord bot powered by Claude.
|
|
94
|
+
|
|
95
|
+
## Setup
|
|
96
|
+
|
|
97
|
+
### 1. Create a Discord bot
|
|
98
|
+
|
|
99
|
+
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
|
|
100
|
+
2. Create a new application, then go to **Bot** and copy the bot token
|
|
101
|
+
3. Under **Privileged Gateway Intents**, enable **Message Content Intent**
|
|
102
|
+
4. Go to **OAuth2 > URL Generator**, select \`bot\` scope, then invite to your server
|
|
103
|
+
|
|
104
|
+
### 2. Get channel IDs
|
|
105
|
+
|
|
106
|
+
Enable Developer Mode in Discord (Settings > Advanced), then right-click a channel and copy its ID.
|
|
107
|
+
|
|
108
|
+
### 3. Local development
|
|
109
|
+
|
|
110
|
+
\`\`\`sh
|
|
111
|
+
cp .env.example .env
|
|
112
|
+
# Fill in DISCORD_BOT_TOKEN, ANTHROPIC_API_KEY, DISCORD_CHANNEL_IDS
|
|
113
|
+
|
|
114
|
+
pip install -r requirements.txt
|
|
115
|
+
python main.py
|
|
116
|
+
\`\`\`
|
|
117
|
+
|
|
118
|
+
### 4. Deploy
|
|
119
|
+
|
|
120
|
+
\`\`\`sh
|
|
121
|
+
orch publish
|
|
122
|
+
|
|
123
|
+
# Add secrets in your workspace (web dashboard > Settings > Secrets):
|
|
124
|
+
# DISCORD_BOT_TOKEN — your bot token
|
|
125
|
+
# DISCORD_CHANNEL_IDS — comma-separated channel IDs
|
|
126
|
+
|
|
127
|
+
orch service deploy
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
## Customization
|
|
131
|
+
|
|
132
|
+
Edit \`main.py\` to customize:
|
|
133
|
+
|
|
134
|
+
- **SYSTEM_PROMPT** — controls how the bot responds
|
|
135
|
+
- **MODEL** / **MAX_TOKENS** — override via env vars
|
|
136
|
+
|
|
137
|
+
## Environment Variables
|
|
138
|
+
|
|
139
|
+
| Variable | Required | Description |
|
|
140
|
+
|----------|----------|-------------|
|
|
141
|
+
| \`DISCORD_BOT_TOKEN\` | Yes | Discord bot token (workspace secret) |
|
|
142
|
+
| \`ANTHROPIC_API_KEY\` | Auto | Injected by orchagent via \`supported_providers\` |
|
|
143
|
+
| \`DISCORD_CHANNEL_IDS\` | Yes | Comma-separated channel IDs (workspace secret) |
|
|
144
|
+
| \`MODEL\` | No | Claude model (default: claude-sonnet-4-5-20250929) |
|
|
145
|
+
| \`MAX_TOKENS\` | No | Max response tokens (default: 1024) |
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
const inputField = flavor === 'managed_loop' || flavor === 'orchestrator' ? 'task' : 'input';
|
|
149
|
+
const inputDescription = flavor === 'managed_loop' || flavor === 'orchestrator' ? 'The task to perform' : 'The input to process';
|
|
91
150
|
const cloudExample = flavor === 'code_runtime'
|
|
92
151
|
? `orchagent run ${agentName} --data '{"input": "Hello world"}'`
|
|
93
152
|
: `orchagent run ${agentName} --data '{"${inputField}": "Hello world"}'`;
|
|
94
153
|
const localExample = flavor === 'code_runtime'
|
|
95
154
|
? `orchagent run ${agentName} --local --data '{"input": "Hello world"}'`
|
|
96
155
|
: `orchagent run ${agentName} --local --data '{"${inputField}": "Hello world"}'`;
|
|
97
|
-
|
|
156
|
+
let readme = `# ${agentName}
|
|
98
157
|
|
|
99
158
|
A brief description of what this agent does.
|
|
100
159
|
|
|
@@ -124,6 +183,20 @@ ${localExample}
|
|
|
124
183
|
|-------|------|-------------|
|
|
125
184
|
| \`result\` | string | The agent's response |
|
|
126
185
|
`;
|
|
186
|
+
if (flavor === 'orchestrator') {
|
|
187
|
+
readme += `
|
|
188
|
+
## Dependencies
|
|
189
|
+
|
|
190
|
+
This orchestrator calls other agents. Update \`manifest.dependencies\` in \`orchagent.json\` with your actual dependencies.
|
|
191
|
+
|
|
192
|
+
**Publish order:** Publish dependency agents first, then this orchestrator.
|
|
193
|
+
|
|
194
|
+
| Dependency | Version | Description |
|
|
195
|
+
|------------|---------|-------------|
|
|
196
|
+
| \`org/agent-name\` | v1 | TODO: describe what this agent does |
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
199
|
+
return readme;
|
|
127
200
|
}
|
|
128
201
|
const AGENT_PROMPT_TEMPLATE = `You are a helpful AI agent.
|
|
129
202
|
|
|
@@ -157,6 +230,221 @@ const AGENT_SCHEMA_TEMPLATE = `{
|
|
|
157
230
|
}
|
|
158
231
|
}
|
|
159
232
|
`;
|
|
233
|
+
const ORCHESTRATOR_MAIN_PY = `"""
|
|
234
|
+
orchagent orchestrator entrypoint.
|
|
235
|
+
|
|
236
|
+
Reads JSON input from stdin, calls dependency agents via the orchagent SDK,
|
|
237
|
+
and writes JSON output to stdout.
|
|
238
|
+
|
|
239
|
+
Usage:
|
|
240
|
+
echo '{"task": "do something"}' | python main.py
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
import asyncio
|
|
244
|
+
import json
|
|
245
|
+
import sys
|
|
246
|
+
|
|
247
|
+
from orchagent import AgentClient
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def main():
|
|
251
|
+
# Read JSON input from stdin
|
|
252
|
+
raw = sys.stdin.read()
|
|
253
|
+
try:
|
|
254
|
+
data = json.loads(raw) if raw.strip() else {}
|
|
255
|
+
except json.JSONDecodeError:
|
|
256
|
+
print(json.dumps({"error": "Invalid JSON input"}))
|
|
257
|
+
sys.exit(1)
|
|
258
|
+
|
|
259
|
+
task = data.get("task", "")
|
|
260
|
+
|
|
261
|
+
# --- Your orchestration logic here ---
|
|
262
|
+
# The AgentClient reads ORCHAGENT_SERVICE_KEY from the environment automatically.
|
|
263
|
+
# Do NOT add ORCHAGENT_SERVICE_KEY to required_secrets — the gateway injects it.
|
|
264
|
+
client = AgentClient()
|
|
265
|
+
|
|
266
|
+
# Call a dependency agent (must be listed in manifest.dependencies)
|
|
267
|
+
result = asyncio.run(
|
|
268
|
+
client.call("org/agent-name@v1", {"input": task})
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# You can chain multiple calls, run them in parallel, or add conditional logic:
|
|
272
|
+
#
|
|
273
|
+
# Sequential:
|
|
274
|
+
# result2 = asyncio.run(client.call("org/another-agent@v1", {"input": result}))
|
|
275
|
+
#
|
|
276
|
+
# Parallel:
|
|
277
|
+
# r1, r2 = asyncio.run(asyncio.gather(
|
|
278
|
+
# client.call("org/agent-a@v1", {"input": task}),
|
|
279
|
+
# client.call("org/agent-b@v1", {"input": task}),
|
|
280
|
+
# ))
|
|
281
|
+
# --- End orchestration logic ---
|
|
282
|
+
|
|
283
|
+
# Write JSON output to stdout
|
|
284
|
+
print(json.dumps({"result": result, "success": True}))
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
if __name__ == "__main__":
|
|
288
|
+
main()
|
|
289
|
+
`;
|
|
290
|
+
const ORCHESTRATOR_REQUIREMENTS = `orchagent-sdk>=0.1.0
|
|
291
|
+
`;
|
|
292
|
+
const DISCORD_MAIN_PY = `"""
|
|
293
|
+
Discord bot agent — powered by Claude.
|
|
294
|
+
|
|
295
|
+
Listens for messages in configured channels and responds using the Anthropic API.
|
|
296
|
+
|
|
297
|
+
Local development:
|
|
298
|
+
1. Copy .env.example to .env and fill in your tokens
|
|
299
|
+
2. pip install -r requirements.txt
|
|
300
|
+
3. python main.py
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
import asyncio
|
|
304
|
+
import logging
|
|
305
|
+
import os
|
|
306
|
+
import sys
|
|
307
|
+
|
|
308
|
+
import anthropic
|
|
309
|
+
import discord
|
|
310
|
+
|
|
311
|
+
# ---------------------------------------------------------------------------
|
|
312
|
+
# Configuration
|
|
313
|
+
# ---------------------------------------------------------------------------
|
|
314
|
+
|
|
315
|
+
DISCORD_BOT_TOKEN = os.environ.get("DISCORD_BOT_TOKEN", "")
|
|
316
|
+
ANTHROPIC_API_KEY = os.environ.get("ANTHROPIC_API_KEY", "")
|
|
317
|
+
DISCORD_CHANNEL_IDS = os.environ.get("DISCORD_CHANNEL_IDS", "")
|
|
318
|
+
|
|
319
|
+
MODEL = os.environ.get("MODEL", "claude-sonnet-4-5-20250929")
|
|
320
|
+
MAX_TOKENS = int(os.environ.get("MAX_TOKENS", "1024"))
|
|
321
|
+
|
|
322
|
+
logging.basicConfig(
|
|
323
|
+
level=logging.INFO,
|
|
324
|
+
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
|
325
|
+
stream=sys.stdout,
|
|
326
|
+
)
|
|
327
|
+
logger = logging.getLogger("discord-bot")
|
|
328
|
+
|
|
329
|
+
SYSTEM_PROMPT = """\\
|
|
330
|
+
You are a helpful assistant in a Discord server.
|
|
331
|
+
|
|
332
|
+
Be concise and friendly. Use code blocks for code examples.
|
|
333
|
+
Keep responses under 1800 characters (Discord limit is 2000)."""
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# ---------------------------------------------------------------------------
|
|
337
|
+
# Anthropic API
|
|
338
|
+
# ---------------------------------------------------------------------------
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def ask_claude(client: anthropic.Anthropic, user_message: str) -> str:
|
|
342
|
+
"""Send a message to Claude and return the response."""
|
|
343
|
+
response = client.messages.create(
|
|
344
|
+
model=MODEL,
|
|
345
|
+
max_tokens=MAX_TOKENS,
|
|
346
|
+
system=SYSTEM_PROMPT,
|
|
347
|
+
messages=[{"role": "user", "content": user_message}],
|
|
348
|
+
)
|
|
349
|
+
return response.content[0].text
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
# ---------------------------------------------------------------------------
|
|
353
|
+
# Discord bot
|
|
354
|
+
# ---------------------------------------------------------------------------
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def parse_channel_ids(raw: str) -> set[int]:
|
|
358
|
+
"""Parse comma-separated channel IDs from env var."""
|
|
359
|
+
return {int(x.strip()) for x in raw.split(",") if x.strip().isdigit()}
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class Bot(discord.Client):
|
|
363
|
+
def __init__(self, anthropic_client: anthropic.Anthropic, allowed_channels: set[int]):
|
|
364
|
+
intents = discord.Intents.default()
|
|
365
|
+
intents.message_content = True
|
|
366
|
+
super().__init__(intents=intents)
|
|
367
|
+
self.anthropic_client = anthropic_client
|
|
368
|
+
self.allowed_channels = allowed_channels
|
|
369
|
+
|
|
370
|
+
async def on_ready(self):
|
|
371
|
+
logger.info("Bot connected as %s", self.user)
|
|
372
|
+
|
|
373
|
+
async def on_message(self, message: discord.Message):
|
|
374
|
+
if message.author.bot or not message.content.strip():
|
|
375
|
+
return
|
|
376
|
+
|
|
377
|
+
# Only respond in allowed channels (or threads within them)
|
|
378
|
+
channel_id = message.channel.id
|
|
379
|
+
parent_id = getattr(message.channel, "parent_id", None)
|
|
380
|
+
if channel_id not in self.allowed_channels and parent_id not in self.allowed_channels:
|
|
381
|
+
return
|
|
382
|
+
|
|
383
|
+
logger.info("Message from %s: %.100s", message.author, message.content)
|
|
384
|
+
|
|
385
|
+
async with message.channel.typing():
|
|
386
|
+
try:
|
|
387
|
+
answer = await asyncio.to_thread(
|
|
388
|
+
ask_claude, self.anthropic_client, message.content
|
|
389
|
+
)
|
|
390
|
+
except anthropic.APIError as exc:
|
|
391
|
+
logger.error("Anthropic API error: %s", exc)
|
|
392
|
+
await message.reply("Sorry, I ran into an issue. Please try again.")
|
|
393
|
+
return
|
|
394
|
+
|
|
395
|
+
if len(answer) > 1900:
|
|
396
|
+
answer = answer[:1897] + "..."
|
|
397
|
+
|
|
398
|
+
await message.reply(answer)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
# ---------------------------------------------------------------------------
|
|
402
|
+
# Entry point
|
|
403
|
+
# ---------------------------------------------------------------------------
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def main():
|
|
407
|
+
if not DISCORD_BOT_TOKEN:
|
|
408
|
+
logger.error("DISCORD_BOT_TOKEN not set")
|
|
409
|
+
sys.exit(1)
|
|
410
|
+
if not ANTHROPIC_API_KEY:
|
|
411
|
+
logger.error("ANTHROPIC_API_KEY not set")
|
|
412
|
+
sys.exit(1)
|
|
413
|
+
if not DISCORD_CHANNEL_IDS:
|
|
414
|
+
logger.error("DISCORD_CHANNEL_IDS not set — add comma-separated channel IDs")
|
|
415
|
+
sys.exit(1)
|
|
416
|
+
|
|
417
|
+
allowed = parse_channel_ids(DISCORD_CHANNEL_IDS)
|
|
418
|
+
if not allowed:
|
|
419
|
+
logger.error("No valid channel IDs in DISCORD_CHANNEL_IDS=%r", DISCORD_CHANNEL_IDS)
|
|
420
|
+
sys.exit(1)
|
|
421
|
+
|
|
422
|
+
logger.info("Starting bot — model: %s, channels: %s", MODEL, allowed)
|
|
423
|
+
|
|
424
|
+
client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
|
|
425
|
+
bot = Bot(client, allowed)
|
|
426
|
+
bot.run(DISCORD_BOT_TOKEN, log_handler=None)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
if __name__ == "__main__":
|
|
430
|
+
main()
|
|
431
|
+
`;
|
|
432
|
+
const DISCORD_REQUIREMENTS = `discord.py>=2.3.0,<3.0.0
|
|
433
|
+
anthropic>=0.40.0,<1.0.0
|
|
434
|
+
`;
|
|
435
|
+
const DISCORD_ENV_EXAMPLE = `# Required — get your bot token from https://discord.com/developers/applications
|
|
436
|
+
DISCORD_BOT_TOKEN=
|
|
437
|
+
|
|
438
|
+
# Required for local dev — auto-injected in production via supported_providers
|
|
439
|
+
ANTHROPIC_API_KEY=
|
|
440
|
+
|
|
441
|
+
# Required — comma-separated Discord channel IDs where the bot should respond
|
|
442
|
+
DISCORD_CHANNEL_IDS=
|
|
443
|
+
|
|
444
|
+
# Optional — customize the model and response length
|
|
445
|
+
# MODEL=claude-sonnet-4-5-20250929
|
|
446
|
+
# MAX_TOKENS=1024
|
|
447
|
+
`;
|
|
160
448
|
const SKILL_TEMPLATE = `---
|
|
161
449
|
name: my-skill
|
|
162
450
|
description: When to use this skill
|
|
@@ -189,14 +477,42 @@ function registerInitCommand(program) {
|
|
|
189
477
|
.description('Initialize a new agent project')
|
|
190
478
|
.argument('[name]', 'Agent name (default: current directory name)')
|
|
191
479
|
.option('--type <type>', 'Type: prompt, tool, agent, or skill (legacy aliases: agentic, code)', 'prompt')
|
|
480
|
+
.option('--orchestrator', 'Create an orchestrator agent with dependency scaffolding and SDK boilerplate')
|
|
192
481
|
.option('--run-mode <mode>', 'Run mode for agents: on_demand or always_on', 'on_demand')
|
|
482
|
+
.option('--template <name>', 'Start from a template (available: github-weekly-summary, discord)')
|
|
193
483
|
.action(async (name, options) => {
|
|
194
484
|
const cwd = process.cwd();
|
|
195
|
-
|
|
485
|
+
let runMode = (options.runMode || 'on_demand').trim().toLowerCase();
|
|
196
486
|
if (!['on_demand', 'always_on'].includes(runMode)) {
|
|
197
487
|
throw new errors_1.CliError("Invalid --run-mode. Use 'on_demand' or 'always_on'.");
|
|
198
488
|
}
|
|
199
|
-
|
|
489
|
+
let initMode = resolveInitFlavor(options.type);
|
|
490
|
+
if (options.orchestrator) {
|
|
491
|
+
if (initMode.type === 'skill') {
|
|
492
|
+
throw new errors_1.CliError('Cannot use --orchestrator with --type skill. Orchestrators are agent-type agents that call other agents.');
|
|
493
|
+
}
|
|
494
|
+
initMode = { type: 'agent', flavor: 'orchestrator' };
|
|
495
|
+
}
|
|
496
|
+
if (options.template) {
|
|
497
|
+
const template = options.template.trim().toLowerCase();
|
|
498
|
+
const validTemplates = ['discord', 'github-weekly-summary'];
|
|
499
|
+
if (!validTemplates.includes(template)) {
|
|
500
|
+
throw new errors_1.CliError(`Unknown --template '${template}'. Available templates: ${validTemplates.join(', ')}`);
|
|
501
|
+
}
|
|
502
|
+
if (options.orchestrator) {
|
|
503
|
+
throw new errors_1.CliError('Cannot use --template with --orchestrator.');
|
|
504
|
+
}
|
|
505
|
+
if (initMode.type === 'skill') {
|
|
506
|
+
throw new errors_1.CliError('Cannot use --template with --type skill.');
|
|
507
|
+
}
|
|
508
|
+
if (template === 'discord') {
|
|
509
|
+
initMode = { type: 'agent', flavor: 'discord' };
|
|
510
|
+
runMode = 'always_on';
|
|
511
|
+
}
|
|
512
|
+
else if (template === 'github-weekly-summary') {
|
|
513
|
+
initMode = { type: 'agent', flavor: 'github_weekly_summary' };
|
|
514
|
+
}
|
|
515
|
+
}
|
|
200
516
|
// When a name is provided, create a subdirectory for the project
|
|
201
517
|
const targetDir = name ? path_1.default.join(cwd, name) : cwd;
|
|
202
518
|
const agentName = name || path_1.default.basename(cwd);
|
|
@@ -234,6 +550,61 @@ function registerInitCommand(program) {
|
|
|
234
550
|
}
|
|
235
551
|
return;
|
|
236
552
|
}
|
|
553
|
+
// Handle github-weekly-summary template separately (own file set + output)
|
|
554
|
+
if (initMode.flavor === 'github_weekly_summary') {
|
|
555
|
+
const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
|
|
556
|
+
// Check if already initialized
|
|
557
|
+
try {
|
|
558
|
+
await promises_1.default.access(manifestPath);
|
|
559
|
+
throw new errors_1.CliError(`Already initialized (orchagent.json exists in ${name ? name + '/' : 'current directory'})`);
|
|
560
|
+
}
|
|
561
|
+
catch (err) {
|
|
562
|
+
if (err.code !== 'ENOENT') {
|
|
563
|
+
throw err;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const sub = (s) => s.replace(/\{\{name\}\}/g, agentName);
|
|
567
|
+
// Create prompts/ subdirectory
|
|
568
|
+
await promises_1.default.mkdir(path_1.default.join(targetDir, 'prompts'), { recursive: true });
|
|
569
|
+
// Write all files
|
|
570
|
+
await promises_1.default.writeFile(manifestPath, sub(github_weekly_summary_1.TEMPLATE_MANIFEST));
|
|
571
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.py'), github_weekly_summary_1.TEMPLATE_MAIN_PY);
|
|
572
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'config.py'), github_weekly_summary_1.TEMPLATE_CONFIG_PY);
|
|
573
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'github_fetcher.py'), github_weekly_summary_1.TEMPLATE_GITHUB_FETCHER_PY);
|
|
574
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'activity_store.py'), github_weekly_summary_1.TEMPLATE_ACTIVITY_STORE_PY);
|
|
575
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'analyst.py'), github_weekly_summary_1.TEMPLATE_ANALYST_PY);
|
|
576
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'models.py'), github_weekly_summary_1.TEMPLATE_MODELS_PY);
|
|
577
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'requirements.txt'), github_weekly_summary_1.TEMPLATE_REQUIREMENTS_TXT);
|
|
578
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'prompts', 'weekly_summary.md'), github_weekly_summary_1.TEMPLATE_WEEKLY_SUMMARY_PROMPT);
|
|
579
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, '.env.example'), sub(github_weekly_summary_1.TEMPLATE_ENV_EXAMPLE));
|
|
580
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'README.md'), sub(github_weekly_summary_1.TEMPLATE_README));
|
|
581
|
+
const prefix = name ? name + '/' : '';
|
|
582
|
+
process.stdout.write(`\nInitialized github-weekly-summary agent "${agentName}" in ${targetDir}\n`);
|
|
583
|
+
process.stdout.write(`\nFiles created:\n`);
|
|
584
|
+
process.stdout.write(` ${prefix}orchagent.json Agent manifest\n`);
|
|
585
|
+
process.stdout.write(` ${prefix}main.py Entrypoint\n`);
|
|
586
|
+
process.stdout.write(` ${prefix}config.py Config loader\n`);
|
|
587
|
+
process.stdout.write(` ${prefix}github_fetcher.py GitHub API client\n`);
|
|
588
|
+
process.stdout.write(` ${prefix}activity_store.py Stats computation\n`);
|
|
589
|
+
process.stdout.write(` ${prefix}analyst.py LLM summary generator\n`);
|
|
590
|
+
process.stdout.write(` ${prefix}models.py Data models\n`);
|
|
591
|
+
process.stdout.write(` ${prefix}requirements.txt Python dependencies\n`);
|
|
592
|
+
process.stdout.write(` ${prefix}prompts/weekly_summary.md LLM prompt template\n`);
|
|
593
|
+
process.stdout.write(` ${prefix}.env.example Secret reference\n`);
|
|
594
|
+
process.stdout.write(` ${prefix}README.md Setup guide\n`);
|
|
595
|
+
process.stdout.write(`\nNext steps:\n`);
|
|
596
|
+
const s = name ? 2 : 1;
|
|
597
|
+
if (name) {
|
|
598
|
+
process.stdout.write(` 1. cd ${name}\n`);
|
|
599
|
+
}
|
|
600
|
+
process.stdout.write(` ${s}. orch github connect Connect your GitHub account\n`);
|
|
601
|
+
process.stdout.write(` ${s + 1}. orch publish Publish the agent\n`);
|
|
602
|
+
process.stdout.write(` ${s + 2}. Add secrets in web dashboard ORCHAGENT_API_KEY, DISCORD_WEBHOOK_URL, ANTHROPIC_API_KEY, GITHUB_REPOS\n`);
|
|
603
|
+
process.stdout.write(` ${s + 3}. orch run <org>/${agentName} --cloud Test it\n`);
|
|
604
|
+
process.stdout.write(` ${s + 4}. orch schedule create <org>/${agentName} --cron "0 9 * * 1" Schedule weekly\n`);
|
|
605
|
+
process.stdout.write(`\n See README.md for full setup guide.\n`);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
237
608
|
const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
|
|
238
609
|
const promptPath = path_1.default.join(targetDir, 'prompt.md');
|
|
239
610
|
const schemaPath = path_1.default.join(targetDir, 'schema.json');
|
|
@@ -247,7 +618,7 @@ function registerInitCommand(program) {
|
|
|
247
618
|
throw err;
|
|
248
619
|
}
|
|
249
620
|
}
|
|
250
|
-
if (initMode.flavor !== 'code_runtime' && runMode === 'always_on') {
|
|
621
|
+
if (initMode.flavor !== 'code_runtime' && initMode.flavor !== 'orchestrator' && initMode.flavor !== 'discord' && runMode === 'always_on') {
|
|
251
622
|
throw new errors_1.CliError("run_mode=always_on requires runtime.command in orchagent.json (e.g. \"runtime\": { \"command\": \"python main.py\" }). Use --type tool for code-runtime agents.");
|
|
252
623
|
}
|
|
253
624
|
// Create manifest and type-specific files
|
|
@@ -255,19 +626,53 @@ function registerInitCommand(program) {
|
|
|
255
626
|
manifest.name = agentName;
|
|
256
627
|
manifest.type = initMode.type;
|
|
257
628
|
manifest.run_mode = runMode;
|
|
258
|
-
if (initMode.flavor === '
|
|
629
|
+
if (initMode.flavor === 'orchestrator') {
|
|
630
|
+
manifest.description = 'An orchestrator agent that coordinates other agents';
|
|
631
|
+
manifest.runtime = { command: 'python main.py' };
|
|
632
|
+
manifest.manifest = {
|
|
633
|
+
manifest_version: 1,
|
|
634
|
+
dependencies: [{ id: 'org/agent-name', version: 'v1' }],
|
|
635
|
+
max_hops: 3,
|
|
636
|
+
timeout_ms: 120000,
|
|
637
|
+
per_call_downstream_cap: 50,
|
|
638
|
+
};
|
|
639
|
+
manifest.required_secrets = [];
|
|
640
|
+
}
|
|
641
|
+
else if (initMode.flavor === 'managed_loop') {
|
|
259
642
|
manifest.description = 'An AI agent with tool use';
|
|
260
643
|
manifest.supported_providers = ['anthropic'];
|
|
261
644
|
manifest.loop = { max_turns: 25 };
|
|
262
645
|
manifest.required_secrets = [];
|
|
263
646
|
}
|
|
647
|
+
else if (initMode.flavor === 'discord') {
|
|
648
|
+
manifest.description = 'An always-on Discord bot powered by Claude';
|
|
649
|
+
manifest.runtime = { command: 'python main.py' };
|
|
650
|
+
manifest.supported_providers = ['anthropic'];
|
|
651
|
+
manifest.required_secrets = ['DISCORD_BOT_TOKEN', 'DISCORD_CHANNEL_IDS'];
|
|
652
|
+
manifest.tags = ['discord', 'always-on'];
|
|
653
|
+
}
|
|
264
654
|
else if (initMode.flavor === 'code_runtime') {
|
|
265
655
|
manifest.description = 'A code-runtime agent';
|
|
266
656
|
manifest.runtime = { command: 'python main.py' };
|
|
267
657
|
manifest.required_secrets = [];
|
|
268
658
|
}
|
|
269
659
|
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
270
|
-
if (initMode.flavor === '
|
|
660
|
+
if (initMode.flavor === 'orchestrator') {
|
|
661
|
+
const entrypointPath = path_1.default.join(targetDir, 'main.py');
|
|
662
|
+
const requirementsPath = path_1.default.join(targetDir, 'requirements.txt');
|
|
663
|
+
await promises_1.default.writeFile(entrypointPath, ORCHESTRATOR_MAIN_PY);
|
|
664
|
+
await promises_1.default.writeFile(requirementsPath, ORCHESTRATOR_REQUIREMENTS);
|
|
665
|
+
await promises_1.default.writeFile(schemaPath, AGENT_SCHEMA_TEMPLATE);
|
|
666
|
+
}
|
|
667
|
+
else if (initMode.flavor === 'discord') {
|
|
668
|
+
const entrypointPath = path_1.default.join(targetDir, 'main.py');
|
|
669
|
+
const requirementsPath = path_1.default.join(targetDir, 'requirements.txt');
|
|
670
|
+
const envExamplePath = path_1.default.join(targetDir, '.env.example');
|
|
671
|
+
await promises_1.default.writeFile(entrypointPath, DISCORD_MAIN_PY);
|
|
672
|
+
await promises_1.default.writeFile(requirementsPath, DISCORD_REQUIREMENTS);
|
|
673
|
+
await promises_1.default.writeFile(envExamplePath, DISCORD_ENV_EXAMPLE);
|
|
674
|
+
}
|
|
675
|
+
else if (initMode.flavor === 'code_runtime') {
|
|
271
676
|
const entrypointPath = path_1.default.join(targetDir, 'main.py');
|
|
272
677
|
await promises_1.default.writeFile(entrypointPath, CODE_TEMPLATE_PY);
|
|
273
678
|
await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
|
|
@@ -286,19 +691,50 @@ function registerInitCommand(program) {
|
|
|
286
691
|
process.stdout.write(`Initialized agent "${agentName}" in ${targetDir}\n`);
|
|
287
692
|
process.stdout.write(`\nFiles created:\n`);
|
|
288
693
|
const prefix = name ? name + '/' : '';
|
|
289
|
-
process.stdout.write(` ${prefix}orchagent.json
|
|
290
|
-
if (initMode.flavor === '
|
|
291
|
-
process.stdout.write(` ${prefix}main.py
|
|
694
|
+
process.stdout.write(` ${prefix}orchagent.json - Agent configuration\n`);
|
|
695
|
+
if (initMode.flavor === 'orchestrator') {
|
|
696
|
+
process.stdout.write(` ${prefix}main.py - Orchestrator entrypoint (SDK calls)\n`);
|
|
697
|
+
process.stdout.write(` ${prefix}requirements.txt - Python dependencies (orchagent-sdk)\n`);
|
|
698
|
+
}
|
|
699
|
+
else if (initMode.flavor === 'discord') {
|
|
700
|
+
process.stdout.write(` ${prefix}main.py - Discord bot (discord.py + Anthropic)\n`);
|
|
701
|
+
process.stdout.write(` ${prefix}requirements.txt - Python dependencies\n`);
|
|
702
|
+
process.stdout.write(` ${prefix}.env.example - Environment variables template\n`);
|
|
703
|
+
}
|
|
704
|
+
else if (initMode.flavor === 'code_runtime') {
|
|
705
|
+
process.stdout.write(` ${prefix}main.py - Agent entrypoint (stdin/stdout JSON)\n`);
|
|
292
706
|
}
|
|
293
707
|
else {
|
|
294
|
-
process.stdout.write(` ${prefix}prompt.md
|
|
708
|
+
process.stdout.write(` ${prefix}prompt.md - Prompt template\n`);
|
|
295
709
|
}
|
|
296
|
-
|
|
297
|
-
|
|
710
|
+
if (initMode.flavor !== 'discord') {
|
|
711
|
+
process.stdout.write(` ${prefix}schema.json - Input/output schemas\n`);
|
|
712
|
+
}
|
|
713
|
+
process.stdout.write(` ${prefix}README.md - Agent documentation\n`);
|
|
298
714
|
process.stdout.write(` Run mode: ${runMode}\n`);
|
|
299
|
-
process.stdout.write(` Execution: ${initMode.flavor}\n`);
|
|
715
|
+
process.stdout.write(` Execution: ${initMode.flavor === 'orchestrator' ? 'code_runtime (orchestrator)' : initMode.flavor === 'discord' ? 'code_runtime (discord)' : initMode.flavor}\n`);
|
|
300
716
|
process.stdout.write(`\nNext steps:\n`);
|
|
301
|
-
if (initMode.flavor === '
|
|
717
|
+
if (initMode.flavor === 'orchestrator') {
|
|
718
|
+
const stepNum = name ? 2 : 1;
|
|
719
|
+
if (name) {
|
|
720
|
+
process.stdout.write(` 1. cd ${name}\n`);
|
|
721
|
+
}
|
|
722
|
+
process.stdout.write(` ${stepNum}. Update manifest.dependencies in orchagent.json with your actual agents\n`);
|
|
723
|
+
process.stdout.write(` ${stepNum + 1}. Edit main.py with your orchestration logic\n`);
|
|
724
|
+
process.stdout.write(` ${stepNum + 2}. Publish dependency agents first, then: orchagent publish\n`);
|
|
725
|
+
}
|
|
726
|
+
else if (initMode.flavor === 'discord') {
|
|
727
|
+
const stepNum = name ? 2 : 1;
|
|
728
|
+
if (name) {
|
|
729
|
+
process.stdout.write(` 1. cd ${name}\n`);
|
|
730
|
+
}
|
|
731
|
+
process.stdout.write(` ${stepNum}. Create a Discord bot at https://discord.com/developers/applications\n`);
|
|
732
|
+
process.stdout.write(` ${stepNum + 1}. Enable Message Content Intent in bot settings\n`);
|
|
733
|
+
process.stdout.write(` ${stepNum + 2}. Copy .env.example to .env and fill in your tokens\n`);
|
|
734
|
+
process.stdout.write(` ${stepNum + 3}. Test locally: pip install -r requirements.txt && python main.py\n`);
|
|
735
|
+
process.stdout.write(` ${stepNum + 4}. Deploy: orch publish\n`);
|
|
736
|
+
}
|
|
737
|
+
else if (initMode.flavor === 'code_runtime') {
|
|
302
738
|
const stepNum = name ? 2 : 1;
|
|
303
739
|
if (name) {
|
|
304
740
|
process.stdout.write(` 1. cd ${name}\n`);
|