Add --url flag to ccc-client and ccc-queue
Overrides CONDUIT_URL env var. Resolved through load_client_config and threaded into create_conduit_client as base_url parameter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
17
README.md
17
README.md
@@ -59,24 +59,26 @@ The full `secrets.json` stays on the host. `agent-secrets.json` goes into the co
|
|||||||
ccc-server --secrets secrets.json
|
ccc-server --secrets secrets.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Environment variables:
|
Server environment variables:
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|----------|---------|-------------|
|
|----------|---------|-------------|
|
||||||
| `CONDUIT_PORT` | `3015` | Port to listen on |
|
| `CONDUIT_PORT` | `3015` | Port to listen on |
|
||||||
|
| `CONDUIT_BIND` | `127.0.0.1` | Address to bind to |
|
||||||
| `CONDUIT_ROOT` | `/workspace` | Workspace root for path resolution |
|
| `CONDUIT_ROOT` | `/workspace` | Workspace root for path resolution |
|
||||||
|
|
||||||
### Client (container / agent)
|
### Client (container / agent)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ccc-client --secrets agent-secrets.json --user agent '{"action": "list-actions"}'
|
ccc-client --secrets agent-secrets.json --user agent --url http://192.168.2.99:3015 '{"action": "list-actions"}'
|
||||||
ccc-client --secrets agent-secrets.json --user agent '{"action": "edit-file", "filename": "/workspace/foo.mjs"}'
|
ccc-client --secrets agent-secrets.json --user agent '{"action": "edit-file", "filename": "/workspace/foo.mjs"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
`--secrets` and `--user` can also be set via environment variables:
|
`--secrets`, `--user`, and `--url` can also be set via environment variables:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export CCC_SECRETS=/path/to/agent-secrets.json
|
export CCC_SECRETS=/path/to/agent-secrets.json
|
||||||
export CCC_USER=agent
|
export CCC_USER=agent
|
||||||
|
export CONDUIT_URL=http://192.168.2.99:3015
|
||||||
ccc-client '{"action": "list-actions"}'
|
ccc-client '{"action": "list-actions"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -89,7 +91,7 @@ ccc-client '{"action": "edit-file",' '"filename": "/workspace/foo.mjs"}'
|
|||||||
### Queue manager (host)
|
### Queue manager (host)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ccc-queue --secrets secrets.json --user user
|
ccc-queue --secrets secrets.json --user user --url http://192.168.2.99:3015
|
||||||
```
|
```
|
||||||
|
|
||||||
Opens an interactive TUI showing pending actions:
|
Opens an interactive TUI showing pending actions:
|
||||||
@@ -108,7 +110,12 @@ Opens an interactive TUI showing pending actions:
|
|||||||
[y] approve [n] deny [r] refresh [q] quit
|
[y] approve [n] deny [r] refresh [q] quit
|
||||||
```
|
```
|
||||||
|
|
||||||
Supports `CCC_SECRETS` and `CCC_USER` env vars the same as the client.
|
Client environment variables:
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `CCC_SECRETS` | — | Path to secrets file |
|
||||||
|
| `CCC_USER` | — | Username to authenticate as |
|
||||||
|
| `CONDUIT_URL` | `http://localhost:3015` | Server URL |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { create_conduit_client } from '../client/conduit.mjs';
|
|||||||
|
|
||||||
const POLL_INTERVAL = 2000;
|
const POLL_INTERVAL = 2000;
|
||||||
|
|
||||||
const { username, secret } = load_client_config(process.argv);
|
const { username, secret, url } = load_client_config(process.argv);
|
||||||
const client = create_conduit_client(username, secret);
|
const client = create_conduit_client(username, secret, url);
|
||||||
|
|
||||||
const screen = blessed.screen({
|
const screen = blessed.screen({
|
||||||
smartCSR: true,
|
smartCSR: true,
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
|
|
||||||
import { sign_request } from "./auth.mjs";
|
import { sign_request } from "./auth.mjs";
|
||||||
|
|
||||||
const BASE_URL = process.env.CONDUIT_URL || "http://localhost:3015";
|
const DEFAULT_URL = 'http://localhost:3015';
|
||||||
|
|
||||||
export function create_conduit_client(username, secret) {
|
export function create_conduit_client(username, secret, base_url = process.env.CONDUIT_URL || DEFAULT_URL) {
|
||||||
function auth_headers(body_string) {
|
function auth_headers(body_string) {
|
||||||
return sign_request(secret, username, body_string);
|
return sign_request(secret, username, body_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function call_action(action, params = {}) {
|
async function call_action(action, params = {}) {
|
||||||
const body_string = JSON.stringify({ action, ...params });
|
const body_string = JSON.stringify({ action, ...params });
|
||||||
const res = await fetch(`${BASE_URL}/action`, {
|
const res = await fetch(`${base_url}/action`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json", ...auth_headers(body_string) },
|
headers: { "Content-Type": "application/json", ...auth_headers(body_string) },
|
||||||
body: body_string,
|
body: body_string,
|
||||||
@@ -25,14 +25,14 @@ export function create_conduit_client(username, secret) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function get_queue() {
|
async function get_queue() {
|
||||||
const res = await fetch(`${BASE_URL}/queue`, {
|
const res = await fetch(`${base_url}/queue`, {
|
||||||
headers: auth_headers(''),
|
headers: auth_headers(''),
|
||||||
});
|
});
|
||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolve_queue_item(id, decision) {
|
async function resolve_queue_item(id, decision) {
|
||||||
const res = await fetch(`${BASE_URL}/queue/${id}/${decision}`, {
|
const res = await fetch(`${base_url}/queue/${id}/${decision}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: auth_headers(''),
|
headers: auth_headers(''),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
// Resolve client config from CLI args or environment variables.
|
// Resolve client config from CLI args or environment variables.
|
||||||
// Precedence: CLI args > env vars > error
|
// Precedence: CLI args > env vars > defaults
|
||||||
//
|
//
|
||||||
// Env vars:
|
// Env vars:
|
||||||
// CCC_SECRETS path to secrets file
|
// CCC_SECRETS path to secrets file
|
||||||
// CCC_USER username to authenticate as
|
// CCC_USER username to authenticate as
|
||||||
|
// CONDUIT_URL server URL
|
||||||
|
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
|
|
||||||
|
const DEFAULT_URL = 'http://localhost:3015';
|
||||||
|
|
||||||
function get_arg(argv, flag) {
|
function get_arg(argv, flag) {
|
||||||
const i = argv.indexOf(flag);
|
const i = argv.indexOf(flag);
|
||||||
return i !== -1 ? argv[i + 1] : null;
|
return i !== -1 ? argv[i + 1] : null;
|
||||||
@@ -15,6 +18,7 @@ function get_arg(argv, flag) {
|
|||||||
export function load_client_config(argv) {
|
export function load_client_config(argv) {
|
||||||
const secrets_path = get_arg(argv, '--secrets') || process.env.CCC_SECRETS;
|
const secrets_path = get_arg(argv, '--secrets') || process.env.CCC_SECRETS;
|
||||||
const username = get_arg(argv, '--user') || process.env.CCC_USER;
|
const username = get_arg(argv, '--user') || process.env.CCC_USER;
|
||||||
|
const url = get_arg(argv, '--url') || process.env.CONDUIT_URL || DEFAULT_URL;
|
||||||
|
|
||||||
if (!secrets_path) {
|
if (!secrets_path) {
|
||||||
console.error('Secrets file required: --secrets <path> or CCC_SECRETS=<path>');
|
console.error('Secrets file required: --secrets <path> or CCC_SECRETS=<path>');
|
||||||
@@ -39,14 +43,14 @@ export function load_client_config(argv) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { username, secret: user_entry.secret };
|
return { username, secret: user_entry.secret, url };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_remaining(argv) {
|
export function get_remaining(argv) {
|
||||||
const result = [];
|
const result = [];
|
||||||
let i = 2;
|
let i = 2;
|
||||||
while (i < argv.length) {
|
while (i < argv.length) {
|
||||||
if (argv[i] === '--secrets' || argv[i] === '--user') {
|
if (argv[i] === '--secrets' || argv[i] === '--user' || argv[i] === '--url') {
|
||||||
i += 2;
|
i += 2;
|
||||||
} else {
|
} else {
|
||||||
result.push(argv[i]);
|
result.push(argv[i]);
|
||||||
|
|||||||
@@ -7,11 +7,9 @@
|
|||||||
import { sign_request } from './auth.mjs';
|
import { sign_request } from './auth.mjs';
|
||||||
import { load_client_config, get_remaining } from './config.mjs';
|
import { load_client_config, get_remaining } from './config.mjs';
|
||||||
|
|
||||||
const BASE_URL = process.env.CONDUIT_URL || 'http://localhost:3015';
|
async function call_action(payload, username, secret, url) {
|
||||||
|
|
||||||
async function call_action(payload, username, secret) {
|
|
||||||
const body_string = JSON.stringify(payload);
|
const body_string = JSON.stringify(payload);
|
||||||
const res = await fetch(`${BASE_URL}/action`, {
|
const res = await fetch(`${url}/action`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json', ...sign_request(secret, username, body_string) },
|
headers: { 'Content-Type': 'application/json', ...sign_request(secret, username, body_string) },
|
||||||
body: body_string,
|
body: body_string,
|
||||||
@@ -21,7 +19,7 @@ async function call_action(payload, username, secret) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const { username, secret } = load_client_config(process.argv);
|
const { username, secret, url } = load_client_config(process.argv);
|
||||||
const remaining = get_remaining(process.argv);
|
const remaining = get_remaining(process.argv);
|
||||||
|
|
||||||
if (!remaining.length) {
|
if (!remaining.length) {
|
||||||
@@ -37,7 +35,7 @@ async function main() {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { status, body } = await call_action(payload, username, secret);
|
const { status, body } = await call_action(payload, username, secret, url);
|
||||||
console.log(JSON.stringify(body, null, 2));
|
console.log(JSON.stringify(body, null, 2));
|
||||||
process.exit(status >= 400 ? 1 : 0);
|
process.exit(status >= 400 ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user