crewx-agent-cli 0.2.0
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/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/adapters.d.ts +67 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +1085 -0
- package/dist/adapters.js.map +1 -0
- package/dist/api.d.ts +162 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +287 -0
- package/dist/api.js.map +1 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +117 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -0
- package/dist/daemon.d.ts +47 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +522 -0
- package/dist/daemon.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +618 -0
- package/dist/index.js.map +1 -0
- package/dist/join.d.ts +10 -0
- package/dist/join.d.ts.map +1 -0
- package/dist/join.js +51 -0
- package/dist/join.js.map +1 -0
- package/dist/prompt.d.ts +4 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +135 -0
- package/dist/prompt.js.map +1 -0
- package/dist/ui.d.ts +7 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +19 -0
- package/dist/ui.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CrewX contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# CrewX CLI
|
|
2
|
+
|
|
3
|
+
Connect an existing Codex, Claude Code, Pi, Hermes, or OpenClaw installation to a CrewX workspace. The runner stays on your machine, so the agent uses its existing login, configuration, tools, and access to the selected working directory.
|
|
4
|
+
|
|
5
|
+
## One-command join
|
|
6
|
+
|
|
7
|
+
Create an agent profile in CrewX, generate a token, and copy the command from its connection panel:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd /path/to/project
|
|
11
|
+
npx --yes crewx-agent-cli@latest --join <join-code>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Node.js 22 or newer is required. The command verifies the connection, opens an agent session, and polls for work in the foreground. Closing the terminal or pressing `Ctrl+C` disconnects it. CrewX does not install or start a background daemon automatically; use a service manager such as `launchd`, systemd, or a container supervisor if that lifecycle is wanted.
|
|
15
|
+
|
|
16
|
+
Join mode does not write `$XDG_CONFIG_HOME/crewx/config.json`. Useful options include:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx --yes crewx-agent-cli@latest --join <join-code> --cwd /path/to/project
|
|
20
|
+
npx --yes crewx-agent-cli@latest --join <join-code> --once
|
|
21
|
+
npx --yes crewx-agent-cli@latest --join <join-code> --adapter codex
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The versioned join payload is base64url-encoded JSON containing the server URL, adapter, and raw `crwx_…` bearer token. Base64url is transport encoding, not encryption. CrewX shows the generated token once, but the credential inside the command is reusable until its configured expiry or revocation. Anyone who obtains the command can decode and use it, and the command may remain in shell history. Treat it as a secret and revoke the token from the agent profile if it is exposed.
|
|
25
|
+
|
|
26
|
+
The server stores only the token digest. During a join-mode run, the CLI keeps the decoded credential in memory and supplies the CrewX URL and token to child agents as `CREWX_URL` and `CREWX_TOKEN` so they can use the scoped `crewx task`, `doc`, `memory`, and `integration` commands. A locally executed agent has the same trust boundary as the user account running it.
|
|
27
|
+
|
|
28
|
+
## Persistent installation
|
|
29
|
+
|
|
30
|
+
From this monorepo:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm --filter crewx-agent-protocol build
|
|
34
|
+
pnpm --filter crewx-agent-cli build
|
|
35
|
+
pnpm --filter crewx-agent-cli link --global
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
After `crewx-agent-cli` is published to npm, install it globally and save a named machine connection:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install --global crewx-agent-cli
|
|
42
|
+
crewx connect --url https://your-crewx.example --token crwx_… --name "My Mac"
|
|
43
|
+
crewx doctor
|
|
44
|
+
crewx daemon --adapter codex
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The saved connection lives at `$XDG_CONFIG_HOME/crewx/config.json` (or `~/.config/crewx/config.json`) with mode `0600`. `CREWX_URL`, `CREWX_TOKEN`, and `CREWX_ADAPTER` override saved settings and are useful in explicitly secured services and CI. The CLI includes operating-system certificate authorities in Node's trust store, so locally trusted HTTPS sites such as Laravel Herd work without disabling certificate verification.
|
|
48
|
+
|
|
49
|
+
Both join mode and `crewx daemon` run in the foreground. Start either command inside a safe fallback directory. For each assignment, CrewX uses the project channel's local path first, the agent profile's default working directory second, and `--cwd` last. Use `--once` for one poll and `--poll-interval 5000` to change the polling cadence.
|
|
50
|
+
|
|
51
|
+
## Built-in adapters
|
|
52
|
+
|
|
53
|
+
- **Codex** runs `codex exec --json … -`. CrewX maps read-only, workspace-write, and full-access profiles to the corresponding Codex sandbox and passes the prompt on stdin.
|
|
54
|
+
- **Claude Code** runs `claude --print --output-format stream-json --verbose`. CrewX maps its profile to plan mode, non-interactive workspace edits, or skipped permission prompts and passes the prompt on stdin.
|
|
55
|
+
- **Pi** runs `pi --mode json --no-approve`. CrewX deliberately ignores project-local Pi settings, extensions, skills, prompts, and themes on each daemon run. Read-only and chat-only profiles receive a restricted tool allowlist. Global Pi configuration still applies. Pi itself is not an operating-system sandbox.
|
|
56
|
+
- **Hermes** runs `hermes acp` and speaks ACP v1 over stdio for session creation, model selection, prompt delivery, progress, permission requests, and cancellation. Workspace-write maps to Hermes `accept_edits`; full access maps to `dont_ask`. Hermes does not expose a trustworthy read-only or chat-only ACP mode, so CrewX rejects those profiles instead of presenting a false boundary. CrewX passes a configured model to Hermes for resolution.
|
|
57
|
+
- **OpenClaw** runs an explicitly selected persistent agent with JSON output, a collision-resistant CrewX assignment session key, and a mode-`0600` temporary prompt file that is removed after the run. Immediately before spawning, CrewX runs `openclaw agents list --json` and `openclaw sandbox explain` for the exact task session. The configured agent workspace must resolve to the assignment directory; a sandboxed session must mount that exact directory read-write at its runtime workdir. OpenClaw owns its persistent tool policy and approval behavior, and has no per-run `--cwd`, so configure a dedicated agent with `openclaw agents add <name> --workspace "/path/to/project" --non-interactive --json` when needed.
|
|
58
|
+
|
|
59
|
+
`crewx doctor` probes all five binaries. Runtime profile changes are read before every assignment, so supported model, permission, chat-only, working-directory, token, and deactivation changes do not require reconnecting. Chat-only and deactivated agents cannot receive task runs.
|
|
60
|
+
|
|
61
|
+
## Custom commands
|
|
62
|
+
|
|
63
|
+
Advanced users can replace the built-in invocation without editing CrewX:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx --yes crewx-agent-cli@latest --join <join-code> \
|
|
67
|
+
--coding-cmd 'my-agent --non-interactive {prompt}'
|
|
68
|
+
|
|
69
|
+
crewx daemon --adapter codex --coding-cmd 'my-agent --format text'
|
|
70
|
+
crewx run codex "Review this repository" --coding-cmd 'my-agent {prompt} --quiet'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
CrewX uses a quote-aware argv parser and launches the resulting executable with `shell: false`. The prompt replaces one exact `{prompt}` argument or is appended as one argument when the placeholder is absent; it is never interpolated into a shell string. Pipes, redirection, variable expansion, and command substitution are therefore not interpreted by CrewX.
|
|
74
|
+
|
|
75
|
+
This prevents prompt text from becoming shell syntax, but it does not make an arbitrary executable safe. The selected program inherits the runner's operating-system permissions and environment, including CrewX connection variables in join mode. Do not use commands from an untrusted profile or run them in an untrusted working directory. Explicitly wrapping a command in a shell recreates the shell-risk boundary and is not recommended.
|
|
76
|
+
|
|
77
|
+
## Shared work commands
|
|
78
|
+
|
|
79
|
+
The same connection token lets humans and running agents maintain shared CrewX work from the terminal. JSON output is designed for local agents:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
crewx task list --assigned-to-me --json
|
|
83
|
+
crewx task create --title "Verify the release" --description "Run the smoke suite" --assign-to-me --json
|
|
84
|
+
crewx task update 42 --status done --result "Smoke suite passed" --json
|
|
85
|
+
|
|
86
|
+
crewx doc list --json
|
|
87
|
+
crewx doc create --title "Release runbook" --content "# Release runbook" --json
|
|
88
|
+
crewx doc update 17 --expected-version 1 --content "# Updated runbook" --summary "Add release steps" --json
|
|
89
|
+
|
|
90
|
+
crewx memory list --scope channel --channel 12 --json
|
|
91
|
+
crewx memory create --title "Release convention" --content "Run the browser smoke test before merging" --channel 12 --json
|
|
92
|
+
crewx memory update 9 --importance 5 --json
|
|
93
|
+
|
|
94
|
+
crewx integration list --json
|
|
95
|
+
crewx integration search slack "release blocker" --json
|
|
96
|
+
crewx integration search notion "deployment runbook" --json
|
|
97
|
+
crewx integration search google_drive "quarterly plan" --json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Agents may update only tasks assigned to their own profile. Documents must be agent-readable and unprotected, and every document update requires the current version from `doc list`; stale updates fail instead of overwriting newer work. Agents may edit only memories they recorded, while channel-scoped memory is limited to channels they can see. Provider access tokens are encrypted by the web application and are never returned to the CLI; integration commands expose normalized search results only.
|
|
101
|
+
|
|
102
|
+
## One-shot local runs
|
|
103
|
+
|
|
104
|
+
The same adapters can be exercised without a CrewX connection:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
crewx run codex "Summarize this repository"
|
|
108
|
+
printf 'Review the current diff' | crewx run claude
|
|
109
|
+
crewx run pi "Find the failing test" --cwd ./project
|
|
110
|
+
crewx run hermes "Explain the architecture"
|
|
111
|
+
crewx run openclaw "Check the release plan"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Review reruns
|
|
115
|
+
|
|
116
|
+
Tasks can require human approval. When an agent reports completion, CrewX holds the task in review. Approving completes it; requesting changes records the review note, moves it back to in-progress, and queues that note for the same agent as its next iteration. Existing task and pull-request context stays attached. A new GitHub push also resets a prior approval and returns the task to review.
|
|
117
|
+
|
|
118
|
+
Run `crewx --help` or `crewx <command> --help` for every option.
|
|
119
|
+
|
|
120
|
+
## CrewX agent API
|
|
121
|
+
|
|
122
|
+
The CLI targets `/api/agent/v1`, including connection lifecycle plus `/tasks`, `/documents`, `/memories`, and `/integrations`, authenticated with the bearer connection token. Protocol lifecycle payloads are runtime-validated by `crewx-agent-protocol`.
|
|
123
|
+
|
|
124
|
+
CrewX's implementation choices were informed by a clean-room static inspection of the public `hilos-agent` package. CrewX is an independent implementation and does not include Hilos source code.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type ChildProcessWithoutNullStreams, type SpawnOptionsWithoutStdio } from 'node:child_process';
|
|
2
|
+
import { type AgentAdapter } from 'crewx-agent-protocol';
|
|
3
|
+
export interface AdapterInvocation {
|
|
4
|
+
command: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
output: 'jsonl' | 'json' | 'text';
|
|
7
|
+
prompt: {
|
|
8
|
+
type: 'stdin';
|
|
9
|
+
} | {
|
|
10
|
+
type: 'argument';
|
|
11
|
+
index: number;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'file';
|
|
14
|
+
flag: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface AgentRuntimeProfile {
|
|
18
|
+
model?: string;
|
|
19
|
+
permissionPreset?: 'read_only' | 'standard' | 'full_access';
|
|
20
|
+
chatOnly?: boolean;
|
|
21
|
+
sessionKey?: string;
|
|
22
|
+
runtimeAgentId?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ParsedAdapterLine {
|
|
25
|
+
raw: unknown;
|
|
26
|
+
message?: string;
|
|
27
|
+
role?: 'assistant' | 'tool';
|
|
28
|
+
eventType?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface AdapterRunResult {
|
|
31
|
+
exitCode: number | null;
|
|
32
|
+
signal: NodeJS.Signals | null;
|
|
33
|
+
messages: string[];
|
|
34
|
+
stdout: string[];
|
|
35
|
+
stderr: string[];
|
|
36
|
+
}
|
|
37
|
+
export type SpawnProcess = (command: string, args: readonly string[], options: SpawnOptionsWithoutStdio) => ChildProcessWithoutNullStreams;
|
|
38
|
+
export declare function parseAdapter(value: string): AgentAdapter;
|
|
39
|
+
/** Adapter commands are represented as argv and are never evaluated by a shell. */
|
|
40
|
+
export declare function buildAdapterInvocation(adapter: AgentAdapter, profile?: AgentRuntimeProfile, codingCommand?: string): AdapterInvocation;
|
|
41
|
+
export declare function parseCodingCommand(value: string): string[];
|
|
42
|
+
export declare function parseAdapterLine(adapter: AgentAdapter, line: string): ParsedAdapterLine;
|
|
43
|
+
export declare function verifyOpenClawRuntime(options: Pick<RunAdapterOptions, 'cwd' | 'environment' | 'spawnProcess'> & {
|
|
44
|
+
profile: AgentRuntimeProfile;
|
|
45
|
+
}): Promise<void>;
|
|
46
|
+
export interface RunAdapterOptions {
|
|
47
|
+
adapter: AgentAdapter;
|
|
48
|
+
prompt: string;
|
|
49
|
+
cwd: string;
|
|
50
|
+
profile?: AgentRuntimeProfile | undefined;
|
|
51
|
+
codingCommand?: string | undefined;
|
|
52
|
+
environment?: NodeJS.ProcessEnv | undefined;
|
|
53
|
+
timeoutMs?: number | undefined;
|
|
54
|
+
maxOutputBytes?: number | undefined;
|
|
55
|
+
signal?: AbortSignal | undefined;
|
|
56
|
+
spawnProcess?: SpawnProcess | undefined;
|
|
57
|
+
onStdout?: ((line: string, parsed: ParsedAdapterLine) => void) | undefined;
|
|
58
|
+
onMessage?: ((message: string, role: 'assistant' | 'tool') => void) | undefined;
|
|
59
|
+
onStderr?: ((line: string) => void) | undefined;
|
|
60
|
+
}
|
|
61
|
+
export declare function runAdapter(options: RunAdapterOptions): Promise<AdapterRunResult>;
|
|
62
|
+
export declare function probeAdapter(adapter: AgentAdapter, spawnProcess?: SpawnProcess, timeoutMs?: number): Promise<{
|
|
63
|
+
available: boolean;
|
|
64
|
+
version?: string;
|
|
65
|
+
error?: string;
|
|
66
|
+
}>;
|
|
67
|
+
//# sourceMappingURL=adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}
|