@deepraxis/cli 0.1.1-dev.926893 → 0.1.1-dev.a766ddd
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/COMMANDS.md +425 -423
- package/README.md +27 -24
- package/dist/praxis.js +138 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,11 +19,11 @@ The official command line for [知行云 Praxis](https://deepraxis.com), the clo
|
|
|
19
19
|
```sh
|
|
20
20
|
npm install -g @deepraxis/cli
|
|
21
21
|
praxis login
|
|
22
|
-
praxis model chat completion create --model deepseek-flash \
|
|
22
|
+
praxis api model chat completion create --model deepseek-flash \
|
|
23
23
|
--data '{"messages":[{"role":"user","content":"Hello!"}]}'
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Every operation of the Praxis API is a command
|
|
26
|
+
Every operation of the Praxis API is a command under `praxis api`: about 350 of them across models, agents,
|
|
27
27
|
sandboxes, cloud browsers, web search, databases, knowledge bases, files, media, automation
|
|
28
28
|
and hosted applications. The command tree is generated from the same contract as the API and
|
|
29
29
|
the SDK, so a new API capability reaches the CLI on the day it ships.
|
|
@@ -36,7 +36,7 @@ the SDK, so a new API capability reaches the CLI on the day it ships.
|
|
|
36
36
|
AI coding agent runs it, and clean JSON in scripts. The CLI picks the mode by itself.
|
|
37
37
|
- **Signs in through the browser.** `praxis login` approves the session in your browser and
|
|
38
38
|
keeps the credential in the system keychain, never in a dotfile when a keychain exists.
|
|
39
|
-
- **Predictable grammar.** `praxis <domain> <resource> <verb> [id]`, the same shape everywhere,
|
|
39
|
+
- **Predictable grammar.** `praxis api <domain> <resource> <verb> [id]`, the same shape everywhere,
|
|
40
40
|
with `--help` on every level.
|
|
41
41
|
|
|
42
42
|
## Install
|
|
@@ -68,7 +68,7 @@ Belong to several organizations? Every call runs in the active one:
|
|
|
68
68
|
```sh
|
|
69
69
|
praxis org list
|
|
70
70
|
praxis org switch <organization_id>
|
|
71
|
-
praxis model list --org <organization_id> # just this call
|
|
71
|
+
praxis api model list --org <organization_id> # just this call
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
For CI and other headless environments, create an API key in the
|
|
@@ -84,12 +84,12 @@ export PRAXIS_API_KEY=<your-api-key>
|
|
|
84
84
|
### Models
|
|
85
85
|
|
|
86
86
|
```sh
|
|
87
|
-
praxis model list
|
|
88
|
-
praxis model chat completion create --model deepseek-flash \
|
|
87
|
+
praxis api model list
|
|
88
|
+
praxis api model chat completion create --model deepseek-flash \
|
|
89
89
|
--data '{"messages":[{"role":"user","content":"A haiku"}]}'
|
|
90
|
-
praxis model embedding create --model <embedding_model> \
|
|
90
|
+
praxis api model embedding create --model <embedding_model> \
|
|
91
91
|
--data '{"input":"hello world"}'
|
|
92
|
-
praxis model image generation create --model <image_model> \
|
|
92
|
+
praxis api model image generation create --model <image_model> \
|
|
93
93
|
--prompt "a lighthouse at dawn"
|
|
94
94
|
```
|
|
95
95
|
|
|
@@ -98,53 +98,56 @@ praxis model image generation create --model <image_model> \
|
|
|
98
98
|
Define an agent once, then talk to it in sessions:
|
|
99
99
|
|
|
100
100
|
```sh
|
|
101
|
-
praxis agent create --data '{
|
|
101
|
+
praxis api agent create --data '{
|
|
102
102
|
"name": "helper",
|
|
103
103
|
"model": {"id": "deepseek-flash"},
|
|
104
104
|
"tools": [{"type": "agent_toolset_20260401"}]
|
|
105
105
|
}'
|
|
106
|
-
praxis agent session create --data '{"agent": "<agent_id>"}'
|
|
107
|
-
praxis agent session event send <session_id> --data '{
|
|
106
|
+
praxis api agent session create --data '{"agent": "<agent_id>"}'
|
|
107
|
+
praxis api agent session event send <session_id> --data '{
|
|
108
108
|
"events": [{
|
|
109
109
|
"type": "user.message",
|
|
110
110
|
"content": [{"type": "text", "text": "What can you do?"}]
|
|
111
111
|
}]
|
|
112
112
|
}'
|
|
113
|
-
praxis agent session event stream --session <session_id>
|
|
113
|
+
praxis api agent session event stream --session <session_id>
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
### Sandboxes
|
|
117
117
|
|
|
118
118
|
```sh
|
|
119
|
-
praxis sandbox create --template-id base
|
|
120
|
-
praxis sandbox command run <sandbox_id> \
|
|
119
|
+
praxis api sandbox create --template-id base
|
|
120
|
+
praxis api sandbox command run <sandbox_id> \
|
|
121
121
|
--cmd "python3 -c 'print(6 * 7)'"
|
|
122
|
-
praxis sandbox delete <sandbox_id> --yes
|
|
122
|
+
praxis api sandbox delete <sandbox_id> --yes
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
### Search, data and automation
|
|
126
126
|
|
|
127
127
|
```sh
|
|
128
|
-
praxis search create --query "AI agent frameworks" --max-results 5
|
|
129
|
-
praxis database create --name notes
|
|
130
|
-
praxis automation run create --data '{
|
|
128
|
+
praxis api search create --query "AI agent frameworks" --max-results 5
|
|
129
|
+
praxis api database create --name notes
|
|
130
|
+
praxis api automation run create --data '{
|
|
131
131
|
"source": "export default async (i) => ({ answer: i.n + 1 })",
|
|
132
132
|
"language": "ts", "input": {"n": 41}, "wait": 30
|
|
133
133
|
}'
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
Browse everything with `praxis --help`, `praxis <domain> --help`, or the full
|
|
136
|
+
Browse everything with `praxis --help`, `praxis api --help`, `praxis api <domain> --help`, or the full
|
|
137
137
|
[command reference](https://unpkg.com/@deepraxis/cli/COMMANDS.md) that ships in the package.
|
|
138
138
|
|
|
139
139
|
## How commands work
|
|
140
140
|
|
|
141
|
+
There are two layers. Top-level commands are workflows and session commands (`login`, `logout`,
|
|
142
|
+
`whoami`, `org`, `skill`). Every API operation lives under `praxis api`, so the two never collide.
|
|
143
|
+
|
|
141
144
|
```text
|
|
142
|
-
praxis sandbox command run sbx_123 --cmd "ls" --json
|
|
143
|
-
|
|
145
|
+
praxis api sandbox command run sbx_123 --cmd "ls" --json
|
|
146
|
+
domain resource verb target options global
|
|
144
147
|
```
|
|
145
148
|
|
|
146
149
|
- **One target, positionally.** The object a command acts on comes right after the verb. Parent
|
|
147
|
-
objects are options: `praxis database table list --database <database_id>`.
|
|
150
|
+
objects are options: `praxis api database table list --database <database_id>`.
|
|
148
151
|
- **Request bodies.** Simple fields have their own options. Anything else goes in `--data`, as
|
|
149
152
|
inline JSON, a file (`--data @body.json`) or stdin (`--data -`). Field options win over the
|
|
150
153
|
same field in `--data`.
|
|
@@ -152,7 +155,7 @@ praxis sandbox command run sbx_123 --cmd "ls" --json
|
|
|
152
155
|
resumes from a page you saved.
|
|
153
156
|
- **Safety.** Deleting and other destructive commands ask for confirmation, or need `--yes`
|
|
154
157
|
when no one can answer, for example in a script.
|
|
155
|
-
- **Escape hatch.** `praxis api <METHOD> <path> [--data …]` calls any endpoint with your
|
|
158
|
+
- **Escape hatch.** `praxis api <METHOD> <path> [--data …]` (an uppercase method, such as `praxis api GET /model/v1/models`) calls any endpoint with your
|
|
156
159
|
current credential, including ones that are new since your installed version.
|
|
157
160
|
|
|
158
161
|
## Output for people, AI agents and scripts
|
|
@@ -167,7 +170,7 @@ Errors follow the same rule. In JSON they are one object on stderr with `code`,
|
|
|
167
170
|
`hint`, `status` and `request_id`, so a script or an agent can tell a typo from a quota limit.
|
|
168
171
|
|
|
169
172
|
```sh
|
|
170
|
-
praxis model list --json | jq -r '.data[].id'
|
|
173
|
+
praxis api model list --json | jq -r '.data[].id'
|
|
171
174
|
```
|
|
172
175
|
|
|
173
176
|
## Teach your AI agent to use Praxis
|
package/dist/praxis.js
CHANGED
|
@@ -1973,7 +1973,7 @@ clears to null; an empty string is stored as an empty string.`
|
|
|
1973
1973
|
resource: "agent.sessions",
|
|
1974
1974
|
method: "create",
|
|
1975
1975
|
summary: "Create session",
|
|
1976
|
-
description: `Create a session: one conversation with an agent. Pass agent as an agent id or {type:"agent", id, version?}; an id alone, or an object without version, pins the agent's active_version (the version chosen with agents.versions.apply, not necessarily the newest saved one). Unlike Anthropic Managed Agents, omit environment_id to use the default environment saved in the agent version (light for a new agent), unless computer_id (explicit, or the agent's default) selects a Computer; pass an environment id for a sandbox recipe. For service agents, explicit null allocates no machine, so codemode, bash and other machine tools are unavailable. Optional model must be one of the agent's allowed models; optional thinking is {effort: low|medium|high|xhigh|max|none} or null, where null inherits the agent default and then the model default (none requires the model to allow disabling thinking). Selections are saved before initial_events run; invalid ones return 400 invalid_request_error. Unlike Anthropic Managed Agents, the only inline agent override is system ({type:"agent_with_overrides", id, system}); to change the model, tools, MCP servers or skills, save a version instead. Supports vault_ids, up to 50, owned by the current organization and not archived; credential injection is deferred to a later version. budget is {type:"limit", max_list_cost:{amount, currency:"USD"}} where amount is a positive
|
|
1976
|
+
description: `Create a session: one conversation with an agent. Pass agent as an agent id or {type:"agent", id, version?}; an id alone, or an object without version, pins the agent's active_version (the version chosen with agents.versions.apply, not necessarily the newest saved one). Unlike Anthropic Managed Agents, omit environment_id to use the default environment saved in the agent version (light for a new agent), unless computer_id (explicit, or the agent's default) selects a Computer; pass an environment id for a sandbox recipe. For service agents, explicit null allocates no machine, so codemode, bash and other machine tools are unavailable. Optional model must be one of the agent's allowed models; optional thinking is {effort: low|medium|high|xhigh|max|none} or null, where null inherits the agent default and then the model default (none requires the model to allow disabling thinking). Selections are saved before initial_events run; invalid ones return 400 invalid_request_error. Unlike Anthropic Managed Agents, the only inline agent override is system ({type:"agent_with_overrides", id, system}); to change the model, tools, MCP servers or skills, save a version instead. Supports vault_ids, up to 50, owned by the current organization and not archived; credential injection is deferred to a later version. budget is {type:"limit", max_list_cost:{amount, currency:"USD"}} where amount is a positive decimal string of US dollars with at most 2 decimal places ("20.00" is USD 20.00); omitted budget defaults to "20.00" and null removes the limit. Up to 500 file resources can be attached. Use initial_events to send a first user.message in this request, or send messages afterwards with sessions.events.send. The returned status can still be idle before initial events run; read agent.message and the subsequent session.status_idle event to confirm completion.`,
|
|
1977
1977
|
http: "POST",
|
|
1978
1978
|
path: "/agent/v1/sessions",
|
|
1979
1979
|
streaming: "no",
|
|
@@ -2549,9 +2549,9 @@ Available only on /agent/v1. Use the message_id field from own-face events, not
|
|
|
2549
2549
|
query: [
|
|
2550
2550
|
{
|
|
2551
2551
|
name: "limit",
|
|
2552
|
-
type: "
|
|
2552
|
+
type: "integer",
|
|
2553
2553
|
required: false,
|
|
2554
|
-
description: "Number of
|
|
2554
|
+
description: "Number of resources per page, from 1 to 100; defaults to 100."
|
|
2555
2555
|
},
|
|
2556
2556
|
{
|
|
2557
2557
|
name: "page",
|
|
@@ -9610,7 +9610,8 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
|
|
|
9610
9610
|
{
|
|
9611
9611
|
name: "file_id",
|
|
9612
9612
|
type: "string",
|
|
9613
|
-
required: true
|
|
9613
|
+
required: true,
|
|
9614
|
+
description: "File ID to copy into this library; passed as the upload source instead of file bytes."
|
|
9614
9615
|
},
|
|
9615
9616
|
{
|
|
9616
9617
|
name: "public_id",
|
|
@@ -25061,7 +25062,7 @@ async function publish(client2, input) {
|
|
|
25061
25062
|
const timeout = input.timeout_ms ?? 120000;
|
|
25062
25063
|
if (!Number.isFinite(timeout) || timeout < 0)
|
|
25063
25064
|
throw new RangeError("timeout_ms must be non-negative and finite");
|
|
25064
|
-
const
|
|
25065
|
+
const moduleTypes = [
|
|
25065
25066
|
"application/javascript+module",
|
|
25066
25067
|
"text/javascript",
|
|
25067
25068
|
"application/javascript",
|
|
@@ -25070,9 +25071,30 @@ async function publish(client2, input) {
|
|
|
25070
25071
|
"application/wasm",
|
|
25071
25072
|
"application/octet-stream",
|
|
25072
25073
|
"application/x-python"
|
|
25073
|
-
]
|
|
25074
|
-
|
|
25075
|
-
|
|
25074
|
+
];
|
|
25075
|
+
const moduleType = (type) => {
|
|
25076
|
+
const found = moduleTypes.find((candidate) => candidate === (type ?? "application/javascript+module"));
|
|
25077
|
+
if (!found)
|
|
25078
|
+
throw new PraxisError({ code: "INVALID_PARAMS", message: `Unsupported module content_type: ${type}` });
|
|
25079
|
+
return found;
|
|
25080
|
+
};
|
|
25081
|
+
if (input.main && input.modules)
|
|
25082
|
+
throw new PraxisError({ code: "INVALID_PARAMS", message: "Pass either main or modules, not both" });
|
|
25083
|
+
if (input.main_module !== undefined && !input.modules)
|
|
25084
|
+
throw new PraxisError({ code: "INVALID_PARAMS", message: "main_module requires modules; with main the entry is main.name" });
|
|
25085
|
+
if (input.modules && input.modules.length === 0)
|
|
25086
|
+
throw new PraxisError({ code: "INVALID_PARAMS", message: "modules must not be empty" });
|
|
25087
|
+
const entryModules = input.main ? [{ name: input.main.name, content_type: moduleType(input.main.content_type), content_base64: base64(new TextEncoder().encode(input.main.content)) }] : input.modules?.map((module) => ({
|
|
25088
|
+
name: module.name,
|
|
25089
|
+
content_type: moduleType(module.content_type),
|
|
25090
|
+
content_base64: base64(typeof module.content === "string" ? new TextEncoder().encode(module.content) : module.content)
|
|
25091
|
+
}));
|
|
25092
|
+
if (new Set(entryModules?.map((module) => module.name)).size !== (entryModules?.length ?? 0))
|
|
25093
|
+
throw new PraxisError({ code: "INVALID_PARAMS", message: "Duplicate module name" });
|
|
25094
|
+
const mainModule = input.main ? input.main.name : input.modules ? input.main_module ?? input.modules[0].name : undefined;
|
|
25095
|
+
if (input.modules && !input.modules.some((module) => module.name === mainModule)) {
|
|
25096
|
+
throw new PraxisError({ code: "INVALID_PARAMS", message: `main_module ${mainModule} is not among modules` });
|
|
25097
|
+
}
|
|
25076
25098
|
const files = Object.entries(input.files);
|
|
25077
25099
|
if (files.length > 1e5)
|
|
25078
25100
|
throw new PraxisError({ code: "LIMIT_EXCEEDED", message: "At most 100000 asset paths can be published" });
|
|
@@ -25125,7 +25147,7 @@ async function publish(client2, input) {
|
|
|
25125
25147
|
const removed = Object.keys(previous).filter((key) => !Object.hasOwn(manifest, key));
|
|
25126
25148
|
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(manifest))));
|
|
25127
25149
|
const manifestHash = Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
25128
|
-
const staticOnly = !["main", "bindings", "compatibility_date", "compatibility_flags", "assets_config"].some((key) => Object.hasOwn(input, key));
|
|
25150
|
+
const staticOnly = !["main", "modules", "main_module", "bindings", "compatibility_date", "compatibility_flags", "assets_config"].some((key) => Object.hasOwn(input, key));
|
|
25129
25151
|
const unchanged = staticOnly && !!currentVersion && manifestHash === currentVersion.praxis.assets_manifest?.manifest_hash;
|
|
25130
25152
|
const hashes = new Set(Object.values(manifest).map((entry) => entry.hash));
|
|
25131
25153
|
const currentResult = {
|
|
@@ -25181,11 +25203,7 @@ async function publish(client2, input) {
|
|
|
25181
25203
|
assets: versionAssets,
|
|
25182
25204
|
...input.compatibility_flags ? { compatibility_flags: input.compatibility_flags } : {},
|
|
25183
25205
|
...input.bindings ? { bindings: input.bindings } : {},
|
|
25184
|
-
...
|
|
25185
|
-
name: input.main.name,
|
|
25186
|
-
content_type: contentType,
|
|
25187
|
-
content_base64: base64(new TextEncoder().encode(input.main.content))
|
|
25188
|
-
}] } : {}
|
|
25206
|
+
...entryModules ? { main_module: mainModule, modules: entryModules } : {}
|
|
25189
25207
|
} });
|
|
25190
25208
|
let deployment = result.deployment;
|
|
25191
25209
|
if (!deployment)
|
|
@@ -25316,7 +25334,7 @@ function decideTabReuse(targetUrl, tabs) {
|
|
|
25316
25334
|
if (best === undefined || rank < best.rank || rank === best.rank && (tab.active || !best.tab.active))
|
|
25317
25335
|
best = { tab, rank };
|
|
25318
25336
|
}
|
|
25319
|
-
return best?.tab;
|
|
25337
|
+
return best?.tab ?? tabs.find((tab) => isBlankTab(tab.url));
|
|
25320
25338
|
}
|
|
25321
25339
|
function buildConnectUrl(parts) {
|
|
25322
25340
|
const url = new URL("/browser/v1/connect", parts.api);
|
|
@@ -25334,6 +25352,16 @@ function pickAgentContext(contexts, agentId) {
|
|
|
25334
25352
|
return [...own].sort((x, y) => (x.createdAt ?? "").localeCompare(y.createdAt ?? "") || x.id.localeCompare(y.id))[0];
|
|
25335
25353
|
}
|
|
25336
25354
|
var agentContextName = (agentId) => `agent-${agentId}`.replace(/[^A-Za-z0-9._:-]/g, "-").slice(0, 128);
|
|
25355
|
+
function isBlankTab(value) {
|
|
25356
|
+
if (value === "" || value === "about:blank")
|
|
25357
|
+
return true;
|
|
25358
|
+
try {
|
|
25359
|
+
const url = new URL(value);
|
|
25360
|
+
return (url.protocol === "https:" || url.protocol === "http:") && url.pathname === "/start" && (url.hostname.startsWith("console-") || url.hostname === "console.deepraxis.com");
|
|
25361
|
+
} catch {
|
|
25362
|
+
return false;
|
|
25363
|
+
}
|
|
25364
|
+
}
|
|
25337
25365
|
|
|
25338
25366
|
// ../../packages/sdk/typescript/src/helpers/browser-connect.ts
|
|
25339
25367
|
var PLAYWRIGHT_CORE_VERSION = "1.63.0";
|
|
@@ -25519,10 +25547,11 @@ class Praxis {
|
|
|
25519
25547
|
if (name === "database")
|
|
25520
25548
|
Object.assign(resource, { count: (input) => count(transport, input) });
|
|
25521
25549
|
if (name === "browser") {
|
|
25550
|
+
const host = globalThis.process;
|
|
25522
25551
|
Object.assign(resource, { connect: (options2) => connectBrowser({
|
|
25523
25552
|
contexts: resource.contexts,
|
|
25524
|
-
env:
|
|
25525
|
-
builtin:
|
|
25553
|
+
env: host?.env ?? {},
|
|
25554
|
+
builtin: host?.getBuiltinModule?.bind(host)
|
|
25526
25555
|
}, options2) });
|
|
25527
25556
|
}
|
|
25528
25557
|
if (name === "application")
|
|
@@ -25591,6 +25620,15 @@ function commandWords(resource, method) {
|
|
|
25591
25620
|
const verb = kebab(method.replace(/^retrieve(?=[A-Z]|$)/, "get"));
|
|
25592
25621
|
return [domain, ...resources, verb];
|
|
25593
25622
|
}
|
|
25623
|
+
var API_WORD = "api";
|
|
25624
|
+
var HTTP_METHOD = /^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$/;
|
|
25625
|
+
var domainOf = (command) => command.words[1];
|
|
25626
|
+
function movedTo(commands, words) {
|
|
25627
|
+
if (!words.length || words[0] === API_WORD)
|
|
25628
|
+
return;
|
|
25629
|
+
const moved = [API_WORD, ...words];
|
|
25630
|
+
return commands.some((c) => moved.every((w, i) => c.words[i] === w)) ? `praxis ${moved.join(" ")}` : undefined;
|
|
25631
|
+
}
|
|
25594
25632
|
function commandTree(operations2) {
|
|
25595
25633
|
const commands = [];
|
|
25596
25634
|
const seen = new Set;
|
|
@@ -25606,7 +25644,7 @@ function commandTree(operations2) {
|
|
|
25606
25644
|
if (["login", "logout", "whoami", "org", "api", "skill"].includes(words[0]))
|
|
25607
25645
|
throw new Error(`Reserved command: ${key}`);
|
|
25608
25646
|
seen.add(key);
|
|
25609
|
-
commands.push({ words, op, ...method !== op.method ? { alias: method } : {} });
|
|
25647
|
+
commands.push({ words: [API_WORD, ...words], op, ...method !== op.method ? { alias: method } : {} });
|
|
25610
25648
|
}
|
|
25611
25649
|
}
|
|
25612
25650
|
for (const key of Object.keys(commandOverrides))
|
|
@@ -25902,24 +25940,68 @@ function help(commands, prefix, command) {
|
|
|
25902
25940
|
`);
|
|
25903
25941
|
}
|
|
25904
25942
|
const matching = commands.filter((c) => prefix.every((w, i) => c.words[i] === w));
|
|
25943
|
+
const global = [
|
|
25944
|
+
"Global options:",
|
|
25945
|
+
...globalOptions.map((s) => ` ${describeOption(s)}`),
|
|
25946
|
+
" -h is an alias of --help",
|
|
25947
|
+
"PRAXIS_ENV=develop|production (default: develop); no --env option.",
|
|
25948
|
+
""
|
|
25949
|
+
];
|
|
25950
|
+
const sample = matching.find((c) => c.words.at(-1) === "list") ?? matching[0];
|
|
25951
|
+
if (!prefix.length) {
|
|
25952
|
+
const nouns = [...new Set(workflows.map((w) => w.words[0]))];
|
|
25953
|
+
const summaries = new Map(workflows.map((w) => [w.words[0], w.words.length === 1 ? w.summary : undefined]));
|
|
25954
|
+
const width = Math.max(...nouns.map((n) => n.length));
|
|
25955
|
+
return [
|
|
25956
|
+
"praxis <command> [options]",
|
|
25957
|
+
"",
|
|
25958
|
+
"Workflows:",
|
|
25959
|
+
...nouns.map((n) => ` ${n.padEnd(width)} ${summaries.get(n) ?? workflows.filter((w) => w.words[0] === n).map((w) => w.words[1]).join(", ")}`),
|
|
25960
|
+
"",
|
|
25961
|
+
"API:",
|
|
25962
|
+
` praxis ${API_WORD} <domain> <resource> <action> [options] one command per API operation`,
|
|
25963
|
+
` praxis ${API_WORD} <METHOD> <path> [--data JSON|@file|-] raw request`,
|
|
25964
|
+
` Domains: ${domains(commands).join(", ")}`,
|
|
25965
|
+
"",
|
|
25966
|
+
...global,
|
|
25967
|
+
"Examples:",
|
|
25968
|
+
` ${sample ? example(sample) : "praxis --help"}`
|
|
25969
|
+
].join(`
|
|
25970
|
+
`);
|
|
25971
|
+
}
|
|
25905
25972
|
const children = [...new Set(matching.map((c) => c.words[prefix.length]).filter(Boolean))].sort();
|
|
25906
|
-
|
|
25907
|
-
children.unshift("login", "logout", "whoami", "org", "api", "skill");
|
|
25973
|
+
const raw = prefix.length === 1 && prefix[0] === API_WORD;
|
|
25908
25974
|
return [
|
|
25909
25975
|
`praxis${prefix.length ? " " + prefix.join(" ") : ""} <command> [options]`,
|
|
25910
25976
|
"",
|
|
25977
|
+
...raw ? ["Domains:"] : [],
|
|
25911
25978
|
...children.map((s) => ` ${s}`),
|
|
25912
25979
|
"",
|
|
25913
|
-
"
|
|
25914
|
-
...
|
|
25915
|
-
" -h is an alias of --help",
|
|
25916
|
-
"PRAXIS_ENV=develop|production (default: develop); no --env option.",
|
|
25917
|
-
"",
|
|
25980
|
+
...raw ? ["Raw request:", ` praxis ${API_WORD} <METHOD> <path> [--data JSON|@file|-]`, ""] : [],
|
|
25981
|
+
...global,
|
|
25918
25982
|
"Examples:",
|
|
25919
|
-
` ${
|
|
25983
|
+
...raw ? [` praxis ${API_WORD} GET /model/v1/models`] : [],
|
|
25984
|
+
` ${sample ? example(sample) : "praxis --help"}`
|
|
25920
25985
|
].join(`
|
|
25921
25986
|
`);
|
|
25922
25987
|
}
|
|
25988
|
+
function domains(commands) {
|
|
25989
|
+
return [...new Set(commands.map((c) => c.words[1]))].sort();
|
|
25990
|
+
}
|
|
25991
|
+
var workflows = [
|
|
25992
|
+
{ words: ["login"], usage: "login", summary: "Sign in with a browser device code." },
|
|
25993
|
+
{ words: ["logout"], usage: "logout", summary: "Remove the saved login from the keychain and fallback file." },
|
|
25994
|
+
{ words: ["whoami"], usage: "whoami", summary: "Show the signed-in identity and credential storage location." },
|
|
25995
|
+
{ words: ["org", "list"], usage: "org list", summary: "List organizations available to the login." },
|
|
25996
|
+
{ words: ["org", "switch"], usage: "org switch <organization_id>", summary: "Switch the login organization." },
|
|
25997
|
+
{ words: ["skill"], usage: "skill [--install <dir>]", summary: "Print the AI skill, or write <dir>/praxis-cli/SKILL.md." }
|
|
25998
|
+
];
|
|
25999
|
+
var workflowNouns = [...new Set(workflows.filter((w) => w.words.length > 1).map((w) => w.words[0]))];
|
|
26000
|
+
function nounHelp(noun) {
|
|
26001
|
+
const mine = workflows.filter((w) => w.words[0] === noun);
|
|
26002
|
+
return [...mine.map((w) => `praxis ${w.usage}`), "", "Examples:", ` praxis ${mine[0].usage}`].join(`
|
|
26003
|
+
`);
|
|
26004
|
+
}
|
|
25923
26005
|
|
|
25924
26006
|
// src/config/env.ts
|
|
25925
26007
|
var DEFAULT_ENV = "develop";
|
|
@@ -26286,7 +26368,7 @@ function commandSummary(command) {
|
|
|
26286
26368
|
const chars = [...description];
|
|
26287
26369
|
return chars.length > 120 ? chars.slice(0, 119).join("") + "…" : description;
|
|
26288
26370
|
}
|
|
26289
|
-
const words =
|
|
26371
|
+
const words = command.words.slice(1);
|
|
26290
26372
|
const verb = (words.pop() ?? "use").replace(/-/g, " ");
|
|
26291
26373
|
if (verb === "list" && words.length && !words.at(-1).endsWith("s"))
|
|
26292
26374
|
words[words.length - 1] += "s";
|
|
@@ -26312,21 +26394,17 @@ function skillDocument(commands) {
|
|
|
26312
26394
|
"- Use `--limit N` to limit a server page for SDK-paginated commands. Other lists apply it only to displayed rows; `--param-limit` still sends the API parameter when available. `--all` follows supported pagination and cannot be combined with `--limit`.",
|
|
26313
26395
|
"- Error JSON has an `error` object: `code` identifies the failure, `message` describes it, `hint` suggests recovery, `env` identifies the environment, `command` identifies the command, `status` is the HTTP status, and `request_id` correlates diagnostics. Unavailable fields are null. Exit codes: 0 success, 1 runtime/API error, 2 usage error.",
|
|
26314
26396
|
"- Append `--help` to a command for its optional parameters. Required scalar body options below can also be supplied as fields in `--data`.",
|
|
26397
|
+
"- Two layers: top-level commands (`login`, `org`, `skill`, …) are workflows and session commands; every API operation is under `praxis api <domain> …`. `praxis api <METHOD> <path>` sends a raw request (the method is uppercase).",
|
|
26315
26398
|
"",
|
|
26316
26399
|
"## Account and CLI",
|
|
26317
26400
|
"",
|
|
26318
|
-
|
|
26319
|
-
"praxis
|
|
26320
|
-
"praxis whoami — Show the signed-in identity and credential storage location.",
|
|
26321
|
-
"praxis org list — List organizations available to the login.",
|
|
26322
|
-
"praxis org switch <organization_id> — Switch the login organization.",
|
|
26323
|
-
"praxis api <METHOD> <path> — Call an API path directly. (--data for a request body)",
|
|
26324
|
-
"praxis skill — Print this skill; use --install <dir> to write <dir>/praxis-cli/SKILL.md."
|
|
26401
|
+
...workflows.map((w) => `praxis ${w.usage} — ${w.summary.replace("Print the AI skill, or write", "Print this skill; use --install <dir> to write")}`),
|
|
26402
|
+
"praxis api <METHOD> <path> — Call an API path directly. (--data for a request body)"
|
|
26325
26403
|
];
|
|
26326
26404
|
let domain;
|
|
26327
26405
|
for (const command of commands) {
|
|
26328
|
-
if (command
|
|
26329
|
-
domain = command
|
|
26406
|
+
if (domainOf(command) !== domain) {
|
|
26407
|
+
domain = domainOf(command);
|
|
26330
26408
|
lines.push("", `## ${domain}`, "");
|
|
26331
26409
|
}
|
|
26332
26410
|
const target = targetParam(command.op);
|
|
@@ -27071,9 +27149,9 @@ function startProgress(ctx, write = (text) => writeOutput({ stderr: text })) {
|
|
|
27071
27149
|
}
|
|
27072
27150
|
|
|
27073
27151
|
// src/main.ts
|
|
27074
|
-
var version = "0.1.1-dev.
|
|
27152
|
+
var version = "0.1.1-dev.a766ddd";
|
|
27075
27153
|
var commands = commandTree(operations);
|
|
27076
|
-
var manual = [
|
|
27154
|
+
var manual = [...workflows.map((w) => w.words), [API_WORD]];
|
|
27077
27155
|
function route(argv) {
|
|
27078
27156
|
const words = [], args = [];
|
|
27079
27157
|
for (let i = 0;i < argv.length; i++) {
|
|
@@ -27089,7 +27167,11 @@ function route(argv) {
|
|
|
27089
27167
|
continue;
|
|
27090
27168
|
}
|
|
27091
27169
|
words.push(arg);
|
|
27170
|
+
if (words.length === 1 && words[0] === API_WORD && HTTP_METHOD.test(argv[i + 1] ?? ""))
|
|
27171
|
+
return { words, args: [...args, ...argv.slice(i + 1)] };
|
|
27092
27172
|
const command = commands.find((c) => c.words.length === words.length && c.words.every((w, n) => w === words[n]));
|
|
27173
|
+
if (words.length === 1 && words[0] === API_WORD && argv[i + 1] !== undefined && !argv[i + 1].startsWith("-"))
|
|
27174
|
+
continue;
|
|
27093
27175
|
if (command || manual.some((c) => c.length === words.length && c.every((w, n) => w === words[n])))
|
|
27094
27176
|
return { words, args: [...args, ...argv.slice(i + 1)], command };
|
|
27095
27177
|
}
|
|
@@ -27162,7 +27244,7 @@ async function main(argv, injected = {}) {
|
|
|
27162
27244
|
const selected = route(argv);
|
|
27163
27245
|
ctx.command = commandLine(selected.words);
|
|
27164
27246
|
ctx.resource = selected.words.at(-2) ?? selected.words[0];
|
|
27165
|
-
const specs = [...globalOptions, ...selected.command ? [...operationOptions(selected.command.op), ...selected.command.op.body ? [dataOption] : []] : selected.words
|
|
27247
|
+
const specs = [...globalOptions, ...selected.command ? [...operationOptions(selected.command.op), ...selected.command.op.body ? [dataOption] : []] : selected.words.join(" ") === API_WORD ? [dataOption] : selected.words[0] === "skill" ? [{ name: "install", type: "string" }] : []];
|
|
27166
27248
|
const outputFlags = globalsOf(parseArgs(outputModeArgs(selected.args, specs), globalOptions));
|
|
27167
27249
|
ctx.json = Boolean(outputFlags.json);
|
|
27168
27250
|
Object.assign(ctx, decideOutputMode({ format: outputFlags.format, json: ctx.json, stdoutIsTTY: ports.stdoutIsTTY, env: ports.env }));
|
|
@@ -27176,15 +27258,22 @@ async function main(argv, injected = {}) {
|
|
|
27176
27258
|
return 0;
|
|
27177
27259
|
}
|
|
27178
27260
|
if (!selected.words.length || !selected.command && !manual.some((c) => c.join(" ") === selected.words.join(" "))) {
|
|
27179
|
-
if (selected.words.length && !commands.some((c) => selected.words.every((w, i) => c.words[i] === w)) && selected.words.join(" ")
|
|
27180
|
-
|
|
27261
|
+
if (selected.words.length && !commands.some((c) => selected.words.every((w, i) => c.words[i] === w)) && !workflowNouns.includes(selected.words.join(" "))) {
|
|
27262
|
+
const moved = movedTo(commands, selected.words);
|
|
27263
|
+
throw new UsageError(`Unknown command: ${selected.words.join(" ")}`, moved ? `Generated commands moved under \`praxis api\`. Did you mean: ${moved}?` : selected.words[0] === API_WORD ? "Run `praxis api --help` to list API domains." : "Run `praxis --help`.");
|
|
27264
|
+
}
|
|
27181
27265
|
if (parsed.positional.length)
|
|
27182
27266
|
throw new UsageError("Unexpected arguments");
|
|
27183
|
-
|
|
27184
|
-
|
|
27185
|
-
|
|
27186
|
-
|
|
27187
|
-
|
|
27267
|
+
const noun = selected.words.join(" ");
|
|
27268
|
+
emit({ kind: "message", channel: "stdout", text: workflowNouns.includes(noun) ? nounHelp(noun) : help(commands, selected.words) });
|
|
27269
|
+
return 0;
|
|
27270
|
+
}
|
|
27271
|
+
if (selected.words.join(" ") === API_WORD && !parsed.positional.length && !flags.help) {
|
|
27272
|
+
emit({ kind: "message", channel: "stdout", text: help(commands, [API_WORD]) });
|
|
27273
|
+
return 0;
|
|
27274
|
+
}
|
|
27275
|
+
if (flags.help && selected.words.join(" ") === API_WORD && !parsed.positional.length) {
|
|
27276
|
+
emit({ kind: "message", channel: "stdout", text: help(commands, [API_WORD]) });
|
|
27188
27277
|
return 0;
|
|
27189
27278
|
}
|
|
27190
27279
|
if (flags.help && selected.words[0] === "skill") {
|
|
@@ -27318,7 +27407,9 @@ Examples:
|
|
|
27318
27407
|
emit({ kind: "object", data: await auth.switchOrganization(parsed.positional[0]) });
|
|
27319
27408
|
break;
|
|
27320
27409
|
case "api": {
|
|
27321
|
-
const method = parsed.positional[0]
|
|
27410
|
+
const method = parsed.positional[0];
|
|
27411
|
+
if (!HTTP_METHOD.test(method))
|
|
27412
|
+
throw new UsageError(`Unknown command: api ${method}`, "Run `praxis api --help` to list API domains; raw requests use an uppercase method, such as `praxis api GET /model/v1/models`.");
|
|
27322
27413
|
await confirm(method.toLowerCase());
|
|
27323
27414
|
if (!quiet) {
|
|
27324
27415
|
stopProgress();
|