@codegrammer/co-od 0.1.3 → 0.1.5
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/init.d.ts +1 -0
- package/dist/commands/init.js +168 -0
- package/dist/commands/start.d.ts +11 -0
- package/dist/commands/start.js +240 -0
- package/dist/index.js +14 -2
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import * as api from "../api-client.js";
|
|
2
|
+
import { getAdapter } from "../adapters/index.js";
|
|
3
|
+
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
4
|
+
import { join, basename } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
function parseArgs(args) {
|
|
7
|
+
const parsed = {
|
|
8
|
+
dir: process.cwd(),
|
|
9
|
+
provider: "claude",
|
|
10
|
+
};
|
|
11
|
+
for (let i = 0; i < args.length; i++) {
|
|
12
|
+
if (args[i] === "--dir" && args[i + 1]) {
|
|
13
|
+
parsed.dir = args[++i];
|
|
14
|
+
}
|
|
15
|
+
else if (args[i] === "--name" && args[i + 1]) {
|
|
16
|
+
parsed.name = args[++i];
|
|
17
|
+
}
|
|
18
|
+
else if (args[i] === "--provider" && args[i + 1]) {
|
|
19
|
+
parsed.provider = args[++i];
|
|
20
|
+
}
|
|
21
|
+
else if (args[i] === "--server" && args[i + 1]) {
|
|
22
|
+
parsed.server = args[++i];
|
|
23
|
+
}
|
|
24
|
+
else if (!args[i].startsWith("--") && !parsed.name) {
|
|
25
|
+
parsed.name = args[i];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
function timestamp() {
|
|
31
|
+
return new Date().toISOString().slice(11, 19);
|
|
32
|
+
}
|
|
33
|
+
export async function run(args) {
|
|
34
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
35
|
+
console.error(`Usage: co-od init [name] [--dir <path>] [--provider claude|codex|openclaw]
|
|
36
|
+
|
|
37
|
+
Sets up a new co-ode project:
|
|
38
|
+
1. Creates a room on co-od.dev
|
|
39
|
+
2. Links it to your local project directory
|
|
40
|
+
3. Adds a default agent
|
|
41
|
+
4. Generates a .co-od config file
|
|
42
|
+
5. Ready to go — start giving tasks
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
co-od init # Uses current dir name as room name
|
|
46
|
+
co-od init "my-saas-app" # Custom room name
|
|
47
|
+
co-od init --provider codex # Use Codex as default agent
|
|
48
|
+
co-od init --dir ~/projects/app # Different directory
|
|
49
|
+
`);
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
const parsed = parseArgs(args);
|
|
53
|
+
if (parsed.server) {
|
|
54
|
+
process.env.CO_ODE_SERVER = parsed.server;
|
|
55
|
+
}
|
|
56
|
+
// Ensure logged in
|
|
57
|
+
const token = api.getSessionToken();
|
|
58
|
+
if (!token) {
|
|
59
|
+
console.error(`[${timestamp()}] Not logged in. Run: co-od login`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
const projectDir = parsed.dir;
|
|
63
|
+
const roomName = parsed.name || basename(projectDir);
|
|
64
|
+
console.error(`\n co-od init\n`);
|
|
65
|
+
console.error(` project: ${projectDir}`);
|
|
66
|
+
console.error(` room: ${roomName}`);
|
|
67
|
+
console.error(` provider: ${parsed.provider}\n`);
|
|
68
|
+
// Step 1: Check provider is available
|
|
69
|
+
console.error(` [1/5] Checking ${parsed.provider} CLI...`);
|
|
70
|
+
const adapter = getAdapter(parsed.provider);
|
|
71
|
+
const available = await adapter.available();
|
|
72
|
+
if (!available) {
|
|
73
|
+
console.error(` ✗ ${adapter.name} not found on PATH`);
|
|
74
|
+
console.error(` Install it first, then run co-od init again.`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
console.error(` ✓ ${adapter.name} available`);
|
|
78
|
+
// Step 2: Create room
|
|
79
|
+
console.error(` [2/5] Creating room "${roomName}"...`);
|
|
80
|
+
let roomId;
|
|
81
|
+
try {
|
|
82
|
+
const res = await api.post("/api/rooms", { name: roomName });
|
|
83
|
+
roomId = res.roomId || res.room?._id || "";
|
|
84
|
+
if (!roomId)
|
|
85
|
+
throw new Error("No room ID returned");
|
|
86
|
+
console.error(` ✓ Room created: ${roomId.slice(0, 12)}...`);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.error(` ✗ Failed to create room: ${err}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
// Step 3: Add default agent
|
|
93
|
+
console.error(` [3/5] Adding default agent...`);
|
|
94
|
+
let agentId;
|
|
95
|
+
try {
|
|
96
|
+
const agentName = parsed.provider === "codex" ? "Codex Builder"
|
|
97
|
+
: parsed.provider === "openclaw" ? "OpenClaw Agent"
|
|
98
|
+
: "Claude Builder";
|
|
99
|
+
const res = await api.post(`/api/rooms/${roomId}/agents`, {
|
|
100
|
+
name: agentName,
|
|
101
|
+
type: "builder",
|
|
102
|
+
executionMode: parsed.provider === "codex" ? "local_codex" : "local_claude_code",
|
|
103
|
+
provider: parsed.provider === "codex" ? "openai"
|
|
104
|
+
: parsed.provider === "openclaw" ? "openclaw"
|
|
105
|
+
: "anthropic",
|
|
106
|
+
permissions: {
|
|
107
|
+
"fs.read": true,
|
|
108
|
+
"fs.write": true,
|
|
109
|
+
"fs.applyPatch": true,
|
|
110
|
+
"exec.run": true,
|
|
111
|
+
"net.egress": false,
|
|
112
|
+
"ports.expose": false,
|
|
113
|
+
"secrets.read": false,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
agentId = res.agentId || "";
|
|
117
|
+
console.error(` ✓ Agent "${agentName}" added`);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.error(` ✗ Failed to add agent: ${err}`);
|
|
121
|
+
agentId = "";
|
|
122
|
+
}
|
|
123
|
+
// Step 4: Save config
|
|
124
|
+
console.error(` [4/5] Saving config...`);
|
|
125
|
+
const config = {
|
|
126
|
+
roomId,
|
|
127
|
+
roomName,
|
|
128
|
+
projectDir,
|
|
129
|
+
provider: parsed.provider,
|
|
130
|
+
agentId,
|
|
131
|
+
server: api.getBaseUrl(),
|
|
132
|
+
createdAt: new Date().toISOString(),
|
|
133
|
+
};
|
|
134
|
+
// Save to project dir
|
|
135
|
+
const configPath = join(projectDir, ".co-od.json");
|
|
136
|
+
try {
|
|
137
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
138
|
+
console.error(` ✓ ${configPath}`);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
console.error(` ✗ Could not write ${configPath}`);
|
|
142
|
+
}
|
|
143
|
+
// Also save room mapping to global config
|
|
144
|
+
const globalConfigDir = join(homedir(), ".co-od");
|
|
145
|
+
const roomsMapPath = join(globalConfigDir, "rooms.json");
|
|
146
|
+
try {
|
|
147
|
+
mkdirSync(globalConfigDir, { recursive: true });
|
|
148
|
+
let rooms = {};
|
|
149
|
+
if (existsSync(roomsMapPath)) {
|
|
150
|
+
rooms = JSON.parse(require("node:fs").readFileSync(roomsMapPath, "utf-8"));
|
|
151
|
+
}
|
|
152
|
+
rooms[roomId] = { roomId, projectDir, name: roomName };
|
|
153
|
+
writeFileSync(roomsMapPath, JSON.stringify(rooms, null, 2) + "\n", "utf-8");
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Non-fatal
|
|
157
|
+
}
|
|
158
|
+
// Step 5: Done
|
|
159
|
+
console.error(` [5/5] Ready!\n`);
|
|
160
|
+
console.error(` Your room is live. Next steps:\n`);
|
|
161
|
+
console.error(` co-od run ${roomId.slice(0, 12)} "describe your task" # run a single task`);
|
|
162
|
+
console.error(` co-od daemon ${roomId.slice(0, 12)} --auto-execute # autonomous mode`);
|
|
163
|
+
console.error(` co-od share # invite teammates\n`);
|
|
164
|
+
console.error(` Or open in browser:`);
|
|
165
|
+
console.error(` ${api.getBaseUrl()}/rooms/${roomId}\n`);
|
|
166
|
+
// Output JSON for scripting
|
|
167
|
+
console.log(JSON.stringify(config, null, 2));
|
|
168
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `npx co-od` (no args) — the zero-config magic start.
|
|
3
|
+
*
|
|
4
|
+
* 1. Check login → open browser if needed
|
|
5
|
+
* 2. Detect project dir + available CLIs
|
|
6
|
+
* 3. Find or create room for this project
|
|
7
|
+
* 4. Start local bridge in background
|
|
8
|
+
* 5. Open browser to room
|
|
9
|
+
* 6. Done — user types a task in the browser
|
|
10
|
+
*/
|
|
11
|
+
export declare function run(_args: string[]): Promise<void>;
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `npx co-od` (no args) — the zero-config magic start.
|
|
3
|
+
*
|
|
4
|
+
* 1. Check login → open browser if needed
|
|
5
|
+
* 2. Detect project dir + available CLIs
|
|
6
|
+
* 3. Find or create room for this project
|
|
7
|
+
* 4. Start local bridge in background
|
|
8
|
+
* 5. Open browser to room
|
|
9
|
+
* 6. Done — user types a task in the browser
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { join, basename } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { spawn } from "node:child_process";
|
|
15
|
+
import * as api from "../api-client.js";
|
|
16
|
+
import { getAdapter } from "../adapters/index.js";
|
|
17
|
+
function log(msg) {
|
|
18
|
+
console.error(` ${msg}`);
|
|
19
|
+
}
|
|
20
|
+
function detectProvider() {
|
|
21
|
+
// Check what's available, prefer claude > codex > openclaw
|
|
22
|
+
const checks = [
|
|
23
|
+
{ name: "claude", bin: "claude" },
|
|
24
|
+
{ name: "codex", bin: "codex" },
|
|
25
|
+
{ name: "openclaw", bin: "zeroclaw" },
|
|
26
|
+
{ name: "openclaw", bin: "openclaw" },
|
|
27
|
+
];
|
|
28
|
+
const paths = (process.env.PATH || "").split(":");
|
|
29
|
+
for (const { name, bin } of checks) {
|
|
30
|
+
if (paths.some((dir) => existsSync(join(dir, bin))))
|
|
31
|
+
return name;
|
|
32
|
+
}
|
|
33
|
+
return "claude"; // default, will fail with helpful message later
|
|
34
|
+
}
|
|
35
|
+
async function ensureLoggedIn() {
|
|
36
|
+
const token = api.getSessionToken();
|
|
37
|
+
if (token)
|
|
38
|
+
return true;
|
|
39
|
+
log("Not logged in. Opening browser...\n");
|
|
40
|
+
// Run login command
|
|
41
|
+
const { run: loginRun } = await import("./login.js");
|
|
42
|
+
await loginRun([]);
|
|
43
|
+
// Check again
|
|
44
|
+
return !!api.getSessionToken();
|
|
45
|
+
}
|
|
46
|
+
function findExistingConfig(dir) {
|
|
47
|
+
const configPath = join(dir, ".co-od.json");
|
|
48
|
+
if (!existsSync(configPath))
|
|
49
|
+
return null;
|
|
50
|
+
try {
|
|
51
|
+
const data = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
52
|
+
if (data.roomId)
|
|
53
|
+
return { roomId: data.roomId, roomName: data.roomName || basename(dir) };
|
|
54
|
+
}
|
|
55
|
+
catch { /* ignore */ }
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function startBridgeBackground() {
|
|
59
|
+
// Check if bridge is already running
|
|
60
|
+
try {
|
|
61
|
+
const controller = new AbortController();
|
|
62
|
+
const timeout = setTimeout(() => controller.abort(), 1000);
|
|
63
|
+
// Can't use fetch synchronously, so spawn a quick check
|
|
64
|
+
const check = spawn("sh", ["-c", "curl -sf http://127.0.0.1:4786/health > /dev/null 2>&1"], {
|
|
65
|
+
stdio: "ignore",
|
|
66
|
+
});
|
|
67
|
+
check.on("close", (code) => {
|
|
68
|
+
clearTimeout(timeout);
|
|
69
|
+
if (code === 0) {
|
|
70
|
+
log("✓ local bridge already running");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Not running — start it
|
|
74
|
+
spawnBridge();
|
|
75
|
+
});
|
|
76
|
+
check.on("error", () => {
|
|
77
|
+
clearTimeout(timeout);
|
|
78
|
+
spawnBridge();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
spawnBridge();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function spawnBridge() {
|
|
86
|
+
// Find the bridge script — check common locations
|
|
87
|
+
const possiblePaths = [
|
|
88
|
+
join(process.cwd(), "services/local-bridge/src/index.ts"),
|
|
89
|
+
join(process.cwd(), "node_modules/.bin/co-od-bridge"),
|
|
90
|
+
// Monorepo structure
|
|
91
|
+
join(process.cwd(), "../../services/local-bridge/src/index.ts"),
|
|
92
|
+
];
|
|
93
|
+
// For now, just try to start via the co-ode monorepo if we're in it
|
|
94
|
+
const monorepoRoot = findMonorepoRoot(process.cwd());
|
|
95
|
+
if (monorepoRoot) {
|
|
96
|
+
const bridgePath = join(monorepoRoot, "services/local-bridge/src/index.ts");
|
|
97
|
+
if (existsSync(bridgePath)) {
|
|
98
|
+
const child = spawn("npx", ["ts-node", "--transpile-only", bridgePath], {
|
|
99
|
+
cwd: join(monorepoRoot, "services/local-bridge"),
|
|
100
|
+
stdio: "ignore",
|
|
101
|
+
detached: true,
|
|
102
|
+
env: { ...process.env, LOCAL_BRIDGE_PROJECT_ROOT: process.cwd() },
|
|
103
|
+
});
|
|
104
|
+
child.unref();
|
|
105
|
+
log("✓ local bridge started in background (port 4786)");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
log("⚠ local bridge not started — run your bridge manually or install the co-od bridge package");
|
|
110
|
+
}
|
|
111
|
+
function findMonorepoRoot(from) {
|
|
112
|
+
let dir = from;
|
|
113
|
+
for (let i = 0; i < 5; i++) {
|
|
114
|
+
if (existsSync(join(dir, "services/local-bridge")))
|
|
115
|
+
return dir;
|
|
116
|
+
const parent = join(dir, "..");
|
|
117
|
+
if (parent === dir)
|
|
118
|
+
break;
|
|
119
|
+
dir = parent;
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
async function openBrowser(url) {
|
|
124
|
+
try {
|
|
125
|
+
const open = (await import("open")).default;
|
|
126
|
+
await open(url);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
log(`Open in browser: ${url}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export async function run(_args) {
|
|
133
|
+
const dir = process.cwd();
|
|
134
|
+
const projectName = basename(dir);
|
|
135
|
+
console.error(`
|
|
136
|
+
┌─────────────────────────────┐
|
|
137
|
+
│ co-od │
|
|
138
|
+
│ agents + humans, │
|
|
139
|
+
│ one workspace │
|
|
140
|
+
└─────────────────────────────┘
|
|
141
|
+
`);
|
|
142
|
+
// Step 1: Login
|
|
143
|
+
log("[1/5] Checking authentication...");
|
|
144
|
+
const loggedIn = await ensureLoggedIn();
|
|
145
|
+
if (!loggedIn) {
|
|
146
|
+
log("✗ Login required. Run: co-od login");
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
log("✓ logged in\n");
|
|
150
|
+
// Step 2: Detect environment
|
|
151
|
+
log("[2/5] Detecting environment...");
|
|
152
|
+
const provider = detectProvider();
|
|
153
|
+
const adapter = getAdapter(provider);
|
|
154
|
+
const available = await adapter.available();
|
|
155
|
+
if (available) {
|
|
156
|
+
log(`✓ ${adapter.name} CLI available`);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
log(`⚠ no agent CLI found — install Claude Code, Codex, or ZeroClaw`);
|
|
160
|
+
log(` brew install claude (or) npm install -g @openai/codex`);
|
|
161
|
+
}
|
|
162
|
+
log(`✓ project: ${dir}\n`);
|
|
163
|
+
// Step 3: Find or create room
|
|
164
|
+
log("[3/5] Setting up room...");
|
|
165
|
+
const existing = findExistingConfig(dir);
|
|
166
|
+
let roomId;
|
|
167
|
+
let roomName;
|
|
168
|
+
if (existing) {
|
|
169
|
+
roomId = existing.roomId;
|
|
170
|
+
roomName = existing.roomName;
|
|
171
|
+
log(`✓ found existing room: ${roomName}`);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// Create new room
|
|
175
|
+
roomName = projectName;
|
|
176
|
+
try {
|
|
177
|
+
const res = await api.post("/api/rooms", { name: roomName });
|
|
178
|
+
roomId = res.roomId || res.room?._id || "";
|
|
179
|
+
if (!roomId)
|
|
180
|
+
throw new Error("No room ID");
|
|
181
|
+
log(`✓ room created: ${roomName}`);
|
|
182
|
+
// Add default agent
|
|
183
|
+
const agentName = provider === "codex" ? "Codex Builder"
|
|
184
|
+
: provider === "openclaw" ? "OpenClaw Agent"
|
|
185
|
+
: "Claude Builder";
|
|
186
|
+
try {
|
|
187
|
+
await api.post(`/api/rooms/${roomId}/agents`, {
|
|
188
|
+
name: agentName,
|
|
189
|
+
type: "builder",
|
|
190
|
+
executionMode: provider === "codex" ? "local_codex" : "local_claude_code",
|
|
191
|
+
provider: provider === "codex" ? "openai" : provider === "openclaw" ? "openclaw" : "anthropic",
|
|
192
|
+
permissions: { "fs.read": true, "fs.write": true, "fs.applyPatch": true, "exec.run": true, "net.egress": false, "ports.expose": false, "secrets.read": false },
|
|
193
|
+
});
|
|
194
|
+
log(`✓ agent "${agentName}" added`);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
log(`⚠ could not add agent (add one manually in the UI)`);
|
|
198
|
+
}
|
|
199
|
+
// Save config
|
|
200
|
+
const config = { roomId, roomName, projectDir: dir, provider, server: api.getBaseUrl(), createdAt: new Date().toISOString() };
|
|
201
|
+
try {
|
|
202
|
+
writeFileSync(join(dir, ".co-od.json"), JSON.stringify(config, null, 2) + "\n");
|
|
203
|
+
log(`✓ config saved: .co-od.json`);
|
|
204
|
+
}
|
|
205
|
+
catch { /* non-fatal */ }
|
|
206
|
+
// Save to global rooms map
|
|
207
|
+
try {
|
|
208
|
+
const globalDir = join(homedir(), ".co-od");
|
|
209
|
+
mkdirSync(globalDir, { recursive: true });
|
|
210
|
+
const mapPath = join(globalDir, "rooms.json");
|
|
211
|
+
let rooms = {};
|
|
212
|
+
if (existsSync(mapPath))
|
|
213
|
+
rooms = JSON.parse(readFileSync(mapPath, "utf-8"));
|
|
214
|
+
rooms[roomId] = { roomId, projectDir: dir, name: roomName };
|
|
215
|
+
writeFileSync(mapPath, JSON.stringify(rooms, null, 2) + "\n");
|
|
216
|
+
}
|
|
217
|
+
catch { /* non-fatal */ }
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
log(`✗ failed to create room: ${err}`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
console.error("");
|
|
225
|
+
// Step 4: Start bridge
|
|
226
|
+
log("[4/5] Starting local bridge...");
|
|
227
|
+
startBridgeBackground();
|
|
228
|
+
console.error("");
|
|
229
|
+
// Step 5: Open browser
|
|
230
|
+
log("[5/5] Opening workspace...\n");
|
|
231
|
+
const url = `${api.getBaseUrl()}/rooms/${roomId}`;
|
|
232
|
+
await openBrowser(url);
|
|
233
|
+
console.error(` ✓ Ready! Your workspace is open at:`);
|
|
234
|
+
console.error(` ${url}\n`);
|
|
235
|
+
console.error(` Type a task in the browser to get started.`);
|
|
236
|
+
console.error(` Or use the CLI:\n`);
|
|
237
|
+
console.error(` co-od run ${roomId.slice(0, 12)} "your task here"`);
|
|
238
|
+
console.error(` co-od daemon ${roomId.slice(0, 12)} --auto-execute`);
|
|
239
|
+
console.error(` co-od share\n`);
|
|
240
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
const VERSION = "0.1.0";
|
|
4
4
|
const COMMANDS = {
|
|
5
|
+
init: { desc: "Set up a new co-ode project", usage: "co-od init [name] [--dir <path>] [--provider claude|codex|openclaw]" },
|
|
5
6
|
login: { desc: "Authenticate with co-ode", usage: "co-od login [--token <t>]" },
|
|
6
7
|
rooms: { desc: "List your rooms", usage: "co-od rooms [--json]" },
|
|
7
8
|
run: { desc: "Execute a single task in a room", usage: "co-od run <room> <goal> [--provider claude|codex|openclaw] [--dir <path>] [--json]" },
|
|
8
9
|
join: { desc: "Join a room as an interactive agent", usage: "co-od join <room> [--invite-token <tok>] [--dir <path>]" },
|
|
9
|
-
daemon: { desc: "Autonomous watch mode for a room", usage: "co-od daemon <room> [--as <name>] [--auto-execute] [--
|
|
10
|
+
daemon: { desc: "Autonomous watch mode for a room", usage: "co-od daemon <room> [--as <name>] [--auto-execute] [--handoff-to <agent>]" },
|
|
10
11
|
share: { desc: "Generate a relay code for teammates", usage: "co-od share [--provider claude|codex|openclaw]" },
|
|
11
12
|
connect: { desc: "Connect to a relay via code", usage: "co-od connect <code>" },
|
|
12
13
|
status: { desc: "Show current login and running state", usage: "co-od status [--json]" },
|
|
@@ -37,10 +38,16 @@ async function main() {
|
|
|
37
38
|
console.log(VERSION);
|
|
38
39
|
process.exit(0);
|
|
39
40
|
}
|
|
40
|
-
if (args.includes("--help") || args.includes("-h")
|
|
41
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
41
42
|
printHelp();
|
|
42
43
|
process.exit(0);
|
|
43
44
|
}
|
|
45
|
+
// No args = magic zero-config start
|
|
46
|
+
if (args.length === 0) {
|
|
47
|
+
const { run } = await import("./commands/start.js");
|
|
48
|
+
await run([]);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
44
51
|
const command = args[0];
|
|
45
52
|
const commandArgs = args.slice(1);
|
|
46
53
|
// Show command-specific help
|
|
@@ -58,6 +65,11 @@ async function main() {
|
|
|
58
65
|
}
|
|
59
66
|
// Route to command
|
|
60
67
|
switch (command) {
|
|
68
|
+
case "init": {
|
|
69
|
+
const { run } = await import("./commands/init.js");
|
|
70
|
+
await run(commandArgs);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
61
73
|
case "login": {
|
|
62
74
|
const { run } = await import("./commands/login.js");
|
|
63
75
|
await run(commandArgs);
|