@cortices/agent 0.4.23

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/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # @cortices/agent
2
+
3
+ Connect your AI coding agents to [Cortices](https://cortices.io) — a real-time dashboard to monitor, chat with, and manage your agents from anywhere. Works behind NAT with no port forwarding needed.
4
+
5
+ ## Supported Agents
6
+
7
+ | Agent | Type Flag | Requirement |
8
+ |-------|-----------|-------------|
9
+ | Claude Code | `--agent claude` (default) | [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) |
10
+ | OpenAI Codex | `--agent codex` | [Codex CLI](https://github.com/openai/codex) |
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ # Install globally
16
+ npm install -g @cortices/agent
17
+
18
+ # Run your agent (sign up at https://cortices.io for an API key)
19
+ cortices --api-key ck_your_key_here --dir ~/my-project
20
+ ```
21
+
22
+ Your agent appears in your [dashboard](https://cortices.io/dashboard) instantly.
23
+
24
+ ## Options
25
+
26
+ ```
27
+ --api-key KEY Your Cortices API key (required)
28
+ --dir DIR Working directory (default: current directory)
29
+ --name NAME Agent name shown in dashboard (default: directory name)
30
+ --agent TYPE Agent backend: claude (default), codex
31
+ --interactive Require approval for each tool call from the dashboard
32
+ --verbose, -v Show detailed debug logs
33
+ ```
34
+
35
+ ## Install as a Background Service
36
+
37
+ Run your agent as a persistent service that starts on boot and auto-restarts on failure. Requires **systemd** (Linux) or **launchd** (macOS).
38
+
39
+ ```bash
40
+ # Install
41
+ cortices install --api-key ck_xxx --dir /path/to/project --name my-agent
42
+
43
+ # Manage
44
+ cortices list # List installed agents
45
+ cortices status NAME # Check agent status
46
+ cortices uninstall NAME # Remove agent
47
+ ```
48
+
49
+ ### Viewing Logs
50
+
51
+ ```bash
52
+ # Linux
53
+ tail -f ~/.cortices/logs/NAME.log
54
+
55
+ # macOS
56
+ tail -f ~/Library/Logs/Cortices/NAME.log
57
+ ```
58
+
59
+ ### No systemd/launchd?
60
+
61
+ If your system doesn't support systemd or launchd (e.g. containers, WSL1), run the agent directly in a tmux or screen session:
62
+
63
+ ```bash
64
+ tmux new -s my-agent
65
+ cortices --api-key ck_xxx --dir /path/to/project --name my-agent
66
+ # Ctrl+B, D to detach
67
+ ```
68
+
69
+ Note: without a service manager, restart and upgrade commands from the dashboard will disconnect the agent and it won't auto-restart. You'll need to manually re-run it.
70
+
71
+ ## LLM Authentication
72
+
73
+ Configure your agent's LLM credentials from the dashboard **Vault** (lock icon on the agent page).
74
+
75
+ **Subscription login** — Use your existing Claude Pro/Max or ChatGPT Plus/Pro subscription. Log in on the agent machine first:
76
+
77
+ ```bash
78
+ # Claude Code
79
+ claude auth login
80
+
81
+ # Codex
82
+ codex login
83
+ ```
84
+
85
+ **API key** — Paste your key in the Vault. Supported providers depend on agent type:
86
+
87
+ | Agent | Providers |
88
+ |-------|-----------|
89
+ | Claude Code | Anthropic API, OpenRouter, AWS Bedrock, Custom |
90
+ | Codex | OpenAI API, OpenRouter, Custom |
91
+
92
+ ## Permission Modes
93
+
94
+ - **Auto-approve (default)** — the agent executes tools automatically; you watch from the dashboard
95
+ - **Interactive (`--interactive`)** — every tool call requires your approval before it runs (Claude Code only)
96
+
97
+ ## Requirements
98
+
99
+ - Node.js 18+
100
+ - The CLI for your chosen agent type ([Claude Code](https://docs.anthropic.com/en/docs/claude-code) or [Codex](https://github.com/openai/codex))
101
+
102
+ ## Learn More
103
+
104
+ Visit [cortices.io](https://cortices.io) to get started.
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, resolve } from 'path'
4
+ import { fileURLToPath } from 'url'
5
+ import { existsSync } from 'fs'
6
+ import { execFileSync } from 'child_process'
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url))
9
+ const bytecode = resolve(__dirname, '..', 'dist', 'claude-daemon.cjs') // bytecode loader
10
+ const bundled = resolve(__dirname, '..', 'dist', 'claude-daemon.mjs') // esbuild ESM
11
+ const source = resolve(__dirname, '..', 'claude-daemon.ts') // dev source
12
+
13
+ try {
14
+ if (existsSync(bytecode)) {
15
+ execFileSync('node', [bytecode, ...process.argv.slice(2)], {
16
+ stdio: 'inherit', env: process.env,
17
+ })
18
+ } else if (existsSync(bundled)) {
19
+ execFileSync('node', [bundled, ...process.argv.slice(2)], {
20
+ stdio: 'inherit', env: process.env,
21
+ })
22
+ } else {
23
+ execFileSync('npx', ['tsx', source, ...process.argv.slice(2)], {
24
+ stdio: 'inherit', env: process.env,
25
+ })
26
+ }
27
+ } catch (e) {
28
+ process.exit(e.status || 1)
29
+ }
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, resolve } from 'path'
4
+ import { fileURLToPath } from 'url'
5
+ import { existsSync } from 'fs'
6
+ import { execFileSync } from 'child_process'
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url))
9
+ const bytecode = resolve(__dirname, '..', 'dist', 'codex-daemon.cjs') // bytecode loader
10
+ const bundled = resolve(__dirname, '..', 'dist', 'codex-daemon.mjs') // esbuild ESM
11
+ const source = resolve(__dirname, '..', 'codex-daemon.ts') // dev source
12
+
13
+ try {
14
+ if (existsSync(bytecode)) {
15
+ execFileSync('node', [bytecode, ...process.argv.slice(2)], {
16
+ stdio: 'inherit', env: process.env,
17
+ })
18
+ } else if (existsSync(bundled)) {
19
+ execFileSync('node', [bundled, ...process.argv.slice(2)], {
20
+ stdio: 'inherit', env: process.env,
21
+ })
22
+ } else {
23
+ execFileSync('npx', ['tsx', source, ...process.argv.slice(2)], {
24
+ stdio: 'inherit', env: process.env,
25
+ })
26
+ }
27
+ } catch (e) {
28
+ process.exit(e.status || 1)
29
+ }
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, resolve } from 'path'
4
+ import { fileURLToPath } from 'url'
5
+ import { existsSync } from 'fs'
6
+ import { execFileSync } from 'child_process'
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url))
9
+ const bytecode = resolve(__dirname, '..', 'dist', 'cortices-daemon.cjs')
10
+ const bundled = resolve(__dirname, '..', 'dist', 'cortices-daemon.mjs')
11
+ const source = resolve(__dirname, '..', 'cortices-daemon.ts')
12
+
13
+ try {
14
+ if (existsSync(bytecode)) {
15
+ execFileSync('node', [bytecode, ...process.argv.slice(2)], {
16
+ stdio: 'inherit', env: process.env,
17
+ })
18
+ } else if (existsSync(bundled)) {
19
+ execFileSync('node', [bundled, ...process.argv.slice(2)], {
20
+ stdio: 'inherit', env: process.env,
21
+ })
22
+ } else {
23
+ execFileSync('npx', ['tsx', source, ...process.argv.slice(2)], {
24
+ stdio: 'inherit', env: process.env,
25
+ })
26
+ }
27
+ } catch (e) {
28
+ process.exit(e.status || 1)
29
+ }
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, resolve } from 'path'
4
+ import { fileURLToPath } from 'url'
5
+ import { existsSync } from 'fs'
6
+ import { execFileSync } from 'child_process'
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url))
9
+ const bytecode = resolve(__dirname, '..', 'dist', 'opencode-daemon.cjs') // bytecode loader
10
+ const bundled = resolve(__dirname, '..', 'dist', 'opencode-daemon.mjs') // esbuild ESM
11
+ const source = resolve(__dirname, '..', 'opencode-daemon.ts') // dev source
12
+
13
+ try {
14
+ if (existsSync(bytecode)) {
15
+ execFileSync('node', [bytecode, ...process.argv.slice(2)], {
16
+ stdio: 'inherit', env: process.env,
17
+ })
18
+ } else if (existsSync(bundled)) {
19
+ execFileSync('node', [bundled, ...process.argv.slice(2)], {
20
+ stdio: 'inherit', env: process.env,
21
+ })
22
+ } else {
23
+ execFileSync('npx', ['tsx', source, ...process.argv.slice(2)], {
24
+ stdio: 'inherit', env: process.env,
25
+ })
26
+ }
27
+ } catch (e) {
28
+ process.exit(e.status || 1)
29
+ }
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, resolve } from 'path'
4
+ import { fileURLToPath } from 'url'
5
+ import { existsSync } from 'fs'
6
+ import { execFileSync } from 'child_process'
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url))
9
+
10
+ const args = process.argv.slice(2)
11
+ const SERVICE_COMMANDS = ['install', 'uninstall', 'restart', 'list', 'status']
12
+ const TOOL_COMMANDS = ['schedule', 'plan', 'memory', 'notify']
13
+
14
+ // Show top-level help
15
+ if (args.length === 0 || args[0] === '-h' || args[0] === '--help') {
16
+ console.log(`cortices — AI agent dashboard CLI
17
+
18
+ Usage:
19
+ cortices <command> [options]
20
+ cortices [options] Run agent in foreground (default: claude)
21
+
22
+ Service commands:
23
+ install Install agent as a background service
24
+ uninstall Remove agent service
25
+ restart Restart agent service(s)
26
+ upgrade Check for updates and upgrade all agents
27
+ list List installed agent services
28
+ status Show agent service status
29
+
30
+ Agent tools:
31
+ schedule Manage scheduled tasks
32
+ plan Manage long-running plans
33
+ memory Manage user memories
34
+ notify Send notifications to dashboard
35
+
36
+ Run options:
37
+ --agent TYPE Agent backend: claude (default), codex, opencode, native
38
+ --dir DIR Working directory (default: current directory)
39
+ --name NAME Agent name shown in dashboard
40
+ --api-key KEY Cortices API key for authentication
41
+ --interactive Start with interactive permission mode
42
+
43
+ Examples:
44
+ cortices install --api-key ck_xxx --dir /path/to/project --name my-agent
45
+ cortices restart my-agent
46
+ cortices list
47
+ cortices schedule list
48
+ cortices notify send --title "Build failed" --severity warning
49
+ cortices --api-key ck_xxx --dir /path/to/project # run in foreground`)
50
+ process.exit(0)
51
+ }
52
+
53
+ // ── Agent tool subcommands ──────────────────────────────────────────────────
54
+ if (args[0] && TOOL_COMMANDS.includes(args[0])) {
55
+ const tool = args[0]
56
+ const toolArgs = args.slice(1)
57
+
58
+ // Resolve script: prefer compiled dist, fall back to source
59
+ const scriptMjs = resolve(__dirname, '..', 'dist', 'scripts', `${tool}.mjs`)
60
+ const scriptTs = resolve(__dirname, '..', 'scripts', `${tool}.ts`)
61
+
62
+ try {
63
+ if (existsSync(scriptMjs)) {
64
+ execFileSync('node', [scriptMjs, ...toolArgs], {
65
+ stdio: 'inherit', env: process.env,
66
+ })
67
+ } else if (existsSync(scriptTs)) {
68
+ execFileSync('npx', ['tsx', scriptTs, ...toolArgs], {
69
+ stdio: 'inherit', env: process.env,
70
+ })
71
+ } else {
72
+ console.error(`Script not found for '${tool}'. Run 'npm run build' in the agent directory.`)
73
+ process.exit(1)
74
+ }
75
+ } catch (e) {
76
+ process.exit(e.status || 1)
77
+ }
78
+ process.exit(0)
79
+ }
80
+
81
+ // Determine agent type from --agent flag (default: claude)
82
+ let agentType = 'claude'
83
+ const agentIdx = args.indexOf('--agent')
84
+ if (agentIdx !== -1 && args[agentIdx + 1]) {
85
+ agentType = args[agentIdx + 1].toLowerCase()
86
+ args.splice(agentIdx, 2) // remove --agent and its value from args
87
+ }
88
+
89
+ const VALID_TYPES = ['claude', 'codex', 'opencode', 'native']
90
+ if (!VALID_TYPES.includes(agentType)) {
91
+ console.error(`Unknown agent type: ${agentType}. Use: ${VALID_TYPES.join(', ')}`)
92
+ process.exit(1)
93
+ }
94
+
95
+ // 'native' type uses cortices-daemon.ts, others use <type>-daemon.ts
96
+ const daemonName = agentType === 'native' ? 'cortices-daemon' : `${agentType}-daemon`
97
+ const bytecode = resolve(__dirname, '..', 'dist', `${daemonName}.cjs`)
98
+ const bundled = resolve(__dirname, '..', 'dist', `${daemonName}.mjs`)
99
+ const source = resolve(__dirname, '..', `${daemonName}.ts`)
100
+
101
+ try {
102
+ if (existsSync(bytecode)) {
103
+ execFileSync('node', [bytecode, ...args], {
104
+ stdio: 'inherit', env: process.env,
105
+ })
106
+ } else if (existsSync(bundled)) {
107
+ execFileSync('node', [bundled, ...args], {
108
+ stdio: 'inherit', env: process.env,
109
+ })
110
+ } else {
111
+ execFileSync('npx', ['tsx', source, ...args], {
112
+ stdio: 'inherit', env: process.env,
113
+ })
114
+ }
115
+ } catch (e) {
116
+ process.exit(e.status || 1)
117
+ }