@kodycodes/cli 1.3.0 → 1.5.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/README.md +67 -3
- package/dist/api-token.d.ts +14 -0
- package/dist/api-token.js +21 -0
- package/dist/api-token.js.map +1 -0
- package/dist/auth.js +2 -2
- package/dist/auth.js.map +1 -1
- package/dist/capability-proxy.d.ts +46 -0
- package/dist/capability-proxy.js +151 -0
- package/dist/capability-proxy.js.map +1 -0
- package/dist/cli.d.ts +23 -1
- package/dist/cli.js +117 -4
- package/dist/cli.js.map +1 -1
- package/dist/defaults.d.ts +3 -0
- package/dist/defaults.js +3 -0
- package/dist/defaults.js.map +1 -1
- package/dist/help.js +20 -4
- package/dist/help.js.map +1 -1
- package/dist/invoke-passthrough.d.ts +8 -0
- package/dist/invoke-passthrough.js +46 -0
- package/dist/invoke-passthrough.js.map +1 -0
- package/dist/local-execute.d.ts +28 -0
- package/dist/local-execute.js +286 -0
- package/dist/local-execute.js.map +1 -0
- package/dist/local-runtime-source.d.ts +32 -0
- package/dist/local-runtime-source.js +199 -0
- package/dist/local-runtime-source.js.map +1 -0
- package/dist/network-error.d.ts +2 -0
- package/dist/network-error.js +11 -0
- package/dist/network-error.js.map +1 -0
- package/dist/open-api-client.d.ts +40 -0
- package/dist/open-api-client.js +152 -0
- package/dist/open-api-client.js.map +1 -0
- package/dist/remote-execute.d.ts +28 -0
- package/dist/remote-execute.js +99 -0
- package/dist/remote-execute.js.map +1 -0
- package/dist/workerd-binary.d.ts +31 -0
- package/dist/workerd-binary.js +98 -0
- package/dist/workerd-binary.js.map +1 -0
- package/package.json +1 -1
- package/skills/kody/SKILL.md +11 -0
package/README.md
CHANGED
|
@@ -62,12 +62,73 @@ Or run via `npx @kodycodes/cli` without a global install.
|
|
|
62
62
|
| `kody login` | Browser OAuth (CIMD + PKCE) for the CLI itself. Stores access and refresh tokens. |
|
|
63
63
|
| `kody logout` | Deletes stored CLI credentials. |
|
|
64
64
|
| `kody status` | Shows CLI login state without printing secrets. |
|
|
65
|
-
| `kody whoami` | Confirms the CLI MCP connection and lists tools. |
|
|
66
|
-
| `kody search [query]` | Calls Kody `search` from the CLI (prefer the host MCP tool). |
|
|
67
|
-
| `kody execute` | Calls Kody `execute` from the CLI (`--invoke`, `--code`, `--file`, or stdin via `--file -`). |
|
|
65
|
+
| `kody whoami` | Confirms the CLI MCP connection and lists tools. With a scoped API token (and no login), shows token identity via the Open API. |
|
|
66
|
+
| `kody search [query]` | Calls Kody `search` from the CLI (prefer the host MCP tool). Token-only auth uses Open API `GET /v1/search`. |
|
|
67
|
+
| `kody execute` | Calls Kody `execute` from the CLI (`--invoke`, `--code`, `--file`, or stdin via `--file -`). With a scoped API token and no login (or with `--token`), cloud execute goes through CapabilityProxy → `kody.execute` — no `kody login`. Add `--local` to run the module on this machine instead. |
|
|
68
68
|
|
|
69
69
|
`--json` prints structured MCP results.
|
|
70
70
|
|
|
71
|
+
## Token-authenticated execute (no `kody login`)
|
|
72
|
+
|
|
73
|
+
Scoped API tokens (`kody_at_…`, from the MCP `api` tool `tokenCreate` or
|
|
74
|
+
`POST /v1/tokens`) authenticate the Open API and CapabilityProxy. They never
|
|
75
|
+
replace MCP OAuth on `/mcp`. The CLI uses that token path when you pass
|
|
76
|
+
`--token` / `KODY_API_TOKEN` and are not logged in (or when you pass `--token`
|
|
77
|
+
explicitly):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
export KODY_API_TOKEN=… # scopes: local-execute (+ search:read for search)
|
|
81
|
+
# Cloud execute — module runs in Kody's sandbox via CapabilityProxy → kody.execute
|
|
82
|
+
npx @kodycodes/cli execute --code 'export default async () => ({ ok: true })'
|
|
83
|
+
# Same token, module runs on this machine
|
|
84
|
+
npx @kodycodes/cli execute --local --file ./task.js --params '{"to":"me@example.com"}'
|
|
85
|
+
npx @kodycodes/cli search "what can you do"
|
|
86
|
+
npx @kodycodes/cli whoami
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- Missing token → prompt to set `--token` / `KODY_API_TOKEN` or run `kody login`.
|
|
90
|
+
- Wrong/expired token → 401 with a mint-fresh-token message.
|
|
91
|
+
- Token lacks `local-execute` (or the account flag is off) → 403 naming the
|
|
92
|
+
scope or `local-execute` feature flag before any module runs.
|
|
93
|
+
- Prefer the env var so the token stays out of shell history and `ps`.
|
|
94
|
+
|
|
95
|
+
## Local execute
|
|
96
|
+
|
|
97
|
+
`kody execute --local` runs the same execute module (default export called
|
|
98
|
+
with `--params`) on this machine instead of in Kody's cloud sandbox. Local CPU
|
|
99
|
+
is free; every `kody:runtime` call (`kody.*`, `kody.mcp.*`, `workflows`,
|
|
100
|
+
`packages`) is proxied to Kody's CapabilityProxy and metered like a cloud hop.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
export KODY_API_TOKEN=… # scoped token minted through the Kody `api` tool
|
|
104
|
+
npx @kodycodes/cli execute --local --file ./task.js --params '{"to":"me@example.com"}'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- **Auth:** a scoped API token from `--token` or `KODY_API_TOKEN` (prefer the
|
|
108
|
+
env var so the token stays out of shell history and `ps`). No `kody login`,
|
|
109
|
+
and the CLI never reads MCP OAuth tokens from other hosts. The token stays in
|
|
110
|
+
the CLI process; the sandbox only talks to a loopback bridge.
|
|
111
|
+
- **Runtime:** a pinned [workerd](https://github.com/cloudflare/workerd)
|
|
112
|
+
release, downloaded once from GitHub, sha256-verified, and cached under
|
|
113
|
+
`~/.cache/kody` (Linux, or `$XDG_CACHE_HOME/kody`) or
|
|
114
|
+
`~/Library/Caches/kody` (macOS). Override with `KODY_CACHE_DIR`, or point
|
|
115
|
+
`KODY_WORKERD_PATH` at your own binary. No Docker required. Linux and macOS
|
|
116
|
+
(x64/arm64) only for now.
|
|
117
|
+
- **API:** `https://api.kody.codes` by default (`--api-url` or `KODY_API_URL`
|
|
118
|
+
to override). The CLI calls `GET /v1/capability-proxy/session` before
|
|
119
|
+
starting workerd and `POST /v1/capability-proxy/call` with
|
|
120
|
+
`{ path, args, conversationId? }` for each runtime call.
|
|
121
|
+
- **Errors:** an expired/revoked token or an account without the
|
|
122
|
+
`local-execute` flag fails fast with a clear message before any code runs.
|
|
123
|
+
The token is only sent over https (plain http is allowed for localhost).
|
|
124
|
+
- **Lifetime:** no execution time limit locally; Ctrl-C (or SIGTERM) stops
|
|
125
|
+
workerd and removes the temporary module.
|
|
126
|
+
- **Not yet:** static `kody:@scope/package/export` imports and `--invoke`
|
|
127
|
+
(use cloud execute or `packages.invoke`). `packageStorage()`,
|
|
128
|
+
`packageSecrets`, `email`, and `events` stay unbound like ad hoc cloud
|
|
129
|
+
execute. Outbound `fetch` goes straight from this machine, including to
|
|
130
|
+
local-network hosts.
|
|
131
|
+
|
|
71
132
|
## Token storage
|
|
72
133
|
|
|
73
134
|
CLI credentials are stored in the OS secret store:
|
|
@@ -82,6 +143,9 @@ If the keychain is unavailable (common on headless Linux), the CLI writes a
|
|
|
82
143
|
|
|
83
144
|
Access tokens refresh automatically on expiry or HTTP 401.
|
|
84
145
|
|
|
146
|
+
`execute --local` and token-only cloud execute do not use stored CLI
|
|
147
|
+
credentials; they only read `--token` / `KODY_API_TOKEN`.
|
|
148
|
+
|
|
85
149
|
## Releases
|
|
86
150
|
|
|
87
151
|
This repo uses [semantic-release](https://github.com/semantic-release/semantic-release)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoped Open API / CapabilityProxy token (`kody_at_…`). Same source for
|
|
3
|
+
* `execute --local`, token-only cloud execute, and Open API search/whoami.
|
|
4
|
+
*/
|
|
5
|
+
export declare function readApiToken(values?: {
|
|
6
|
+
token?: string;
|
|
7
|
+
}, env?: NodeJS.ProcessEnv): string | null;
|
|
8
|
+
export declare function requireApiToken(values?: {
|
|
9
|
+
token?: string;
|
|
10
|
+
}, env?: NodeJS.ProcessEnv, purpose?: string): string;
|
|
11
|
+
/** True when the user passed `--token` (not only the env var). */
|
|
12
|
+
export declare function hasExplicitTokenFlag(values: {
|
|
13
|
+
token?: string;
|
|
14
|
+
}): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { apiTokenEnvVar } from './defaults.js';
|
|
2
|
+
/**
|
|
3
|
+
* Scoped Open API / CapabilityProxy token (`kody_at_…`). Same source for
|
|
4
|
+
* `execute --local`, token-only cloud execute, and Open API search/whoami.
|
|
5
|
+
*/
|
|
6
|
+
export function readApiToken(values = {}, env = process.env) {
|
|
7
|
+
const token = (values.token ?? env[apiTokenEnvVar] ?? '').trim();
|
|
8
|
+
return token.length > 0 ? token : null;
|
|
9
|
+
}
|
|
10
|
+
export function requireApiToken(values = {}, env = process.env, purpose = 'this command') {
|
|
11
|
+
const token = readApiToken(values, env);
|
|
12
|
+
if (!token) {
|
|
13
|
+
throw new Error(`${purpose} needs a scoped Kody API token: pass --token or set ${apiTokenEnvVar}. Mint one with the Kody \`api\` tool (tokenCreate) or POST /v1/tokens.`);
|
|
14
|
+
}
|
|
15
|
+
return token;
|
|
16
|
+
}
|
|
17
|
+
/** True when the user passed `--token` (not only the env var). */
|
|
18
|
+
export function hasExplicitTokenFlag(values) {
|
|
19
|
+
return typeof values.token === 'string' && values.token.trim().length > 0;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=api-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-token.js","sourceRoot":"","sources":["../src/api-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC3B,SAA6B,EAAE,EAC/B,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAChE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;AACvC,CAAC;AAED,MAAM,UAAU,eAAe,CAC9B,SAA6B,EAAE,EAC/B,MAAyB,OAAO,CAAC,GAAG,EACpC,UAAkB,cAAc;IAEhC,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACvC,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACd,GAAG,OAAO,uDAAuD,cAAc,yEAAyE,CACxJ,CAAA;IACF,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,oBAAoB,CAAC,MAA0B;IAC9D,OAAO,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAA;AAC1E,CAAC"}
|
package/dist/auth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createServer, } from 'node:http';
|
|
2
2
|
import { auth, } from '@modelcontextprotocol/client';
|
|
3
|
-
import { accessTokenSkewMs, cliRedirectUrl, defaultMcpUrl, defaultScopes, loginTimeoutMs, } from './defaults.js';
|
|
3
|
+
import { accessTokenSkewMs, apiTokenEnvVar, cliRedirectUrl, defaultMcpUrl, defaultScopes, loginTimeoutMs, } from './defaults.js';
|
|
4
4
|
import { createCliOAuthProvider } from './oauth-provider.js';
|
|
5
5
|
import { redactError } from './redact.js';
|
|
6
6
|
import { loadCredentials, saveCredentials, } from './store.js';
|
|
@@ -84,7 +84,7 @@ export async function ensureFreshCredentials(input) {
|
|
|
84
84
|
const mcpUrl = input.mcpUrl ?? defaultMcpUrl;
|
|
85
85
|
const credentials = loadCredentials(mcpUrl, input.backend);
|
|
86
86
|
if (!credentials) {
|
|
87
|
-
throw new Error(
|
|
87
|
+
throw new Error(`Not logged in. Run \`kody login\`, or pass a scoped API token with --token / ${apiTokenEnvVar} (search, whoami, and execute — including cloud execute without --local).`);
|
|
88
88
|
}
|
|
89
89
|
if (!isAccessTokenExpired(credentials, input.now)) {
|
|
90
90
|
return credentials;
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP contract for Kody's CapabilityProxy (Open API `/v1`). Local execute
|
|
3
|
+
* runs user code in workerd and forwards every `kody:runtime` call here with
|
|
4
|
+
* a scoped API token.
|
|
5
|
+
*
|
|
6
|
+
* - `GET /v1/capability-proxy/session` validates the token before workerd
|
|
7
|
+
* starts. 200 → `{ scopes?: string[], expiresAt?: string }`.
|
|
8
|
+
* - `POST /v1/capability-proxy/call` with `{ path, args, conversationId? }`
|
|
9
|
+
* where `path` is the runtime property path (`['kody', 'emailSend']`,
|
|
10
|
+
* `['kody', 'mcp', server, tool]`, `['workflows', 'create']`) and `args`
|
|
11
|
+
* is the positional argument list. 200 → `{ result }`.
|
|
12
|
+
*
|
|
13
|
+
* Errors use `{ error: { code, message } }` (a bare string is accepted too).
|
|
14
|
+
* 401 means the token is expired/revoked; 403 `feature_disabled` means the
|
|
15
|
+
* `local-execute` flag is off for the account.
|
|
16
|
+
*/
|
|
17
|
+
export declare const capabilityProxySessionPath = "v1/capability-proxy/session";
|
|
18
|
+
export declare const capabilityProxyCallPath = "v1/capability-proxy/call";
|
|
19
|
+
export type CapabilityProxySession = {
|
|
20
|
+
scopes: Array<string>;
|
|
21
|
+
expiresAt: string | null;
|
|
22
|
+
};
|
|
23
|
+
export type CapabilityProxyCall = {
|
|
24
|
+
path: Array<string>;
|
|
25
|
+
args: Array<unknown>;
|
|
26
|
+
conversationId?: string;
|
|
27
|
+
};
|
|
28
|
+
export type CapabilityProxyClientInput = {
|
|
29
|
+
apiUrl: string;
|
|
30
|
+
token: string;
|
|
31
|
+
fetchFn?: typeof fetch;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
};
|
|
34
|
+
export declare class CapabilityProxyError extends Error {
|
|
35
|
+
readonly status: number | null;
|
|
36
|
+
readonly code: string | null;
|
|
37
|
+
constructor(message: string, options?: {
|
|
38
|
+
status?: number | null;
|
|
39
|
+
code?: string | null;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** The bearer token must not cross the network in cleartext. */
|
|
43
|
+
export declare function assertTokenSafeApiUrl(apiUrl: string): void;
|
|
44
|
+
export declare function capabilityProxyUrl(apiUrl: string, path: string): URL;
|
|
45
|
+
export declare function openCapabilityProxySession(input: CapabilityProxyClientInput): Promise<CapabilityProxySession>;
|
|
46
|
+
export declare function callCapabilityProxy(input: CapabilityProxyClientInput & CapabilityProxyCall): Promise<unknown>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { apiTokenEnvVar, cliName } from './defaults.js';
|
|
2
|
+
import { describeNetworkError } from './network-error.js';
|
|
3
|
+
import { readPackageVersion } from './package-info.js';
|
|
4
|
+
/**
|
|
5
|
+
* HTTP contract for Kody's CapabilityProxy (Open API `/v1`). Local execute
|
|
6
|
+
* runs user code in workerd and forwards every `kody:runtime` call here with
|
|
7
|
+
* a scoped API token.
|
|
8
|
+
*
|
|
9
|
+
* - `GET /v1/capability-proxy/session` validates the token before workerd
|
|
10
|
+
* starts. 200 → `{ scopes?: string[], expiresAt?: string }`.
|
|
11
|
+
* - `POST /v1/capability-proxy/call` with `{ path, args, conversationId? }`
|
|
12
|
+
* where `path` is the runtime property path (`['kody', 'emailSend']`,
|
|
13
|
+
* `['kody', 'mcp', server, tool]`, `['workflows', 'create']`) and `args`
|
|
14
|
+
* is the positional argument list. 200 → `{ result }`.
|
|
15
|
+
*
|
|
16
|
+
* Errors use `{ error: { code, message } }` (a bare string is accepted too).
|
|
17
|
+
* 401 means the token is expired/revoked; 403 `feature_disabled` means the
|
|
18
|
+
* `local-execute` flag is off for the account.
|
|
19
|
+
*/
|
|
20
|
+
export const capabilityProxySessionPath = 'v1/capability-proxy/session';
|
|
21
|
+
export const capabilityProxyCallPath = 'v1/capability-proxy/call';
|
|
22
|
+
export class CapabilityProxyError extends Error {
|
|
23
|
+
status;
|
|
24
|
+
code;
|
|
25
|
+
constructor(message, options = {}) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'CapabilityProxyError';
|
|
28
|
+
this.status = options.status ?? null;
|
|
29
|
+
this.code = options.code ?? null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const loopbackHosts = new Set(['localhost', '127.0.0.1', '[::1]']);
|
|
33
|
+
/** The bearer token must not cross the network in cleartext. */
|
|
34
|
+
export function assertTokenSafeApiUrl(apiUrl) {
|
|
35
|
+
let url;
|
|
36
|
+
try {
|
|
37
|
+
url = new URL(apiUrl);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new Error(`Invalid Kody API URL: ${apiUrl}`);
|
|
41
|
+
}
|
|
42
|
+
if (url.protocol === 'https:')
|
|
43
|
+
return;
|
|
44
|
+
if (url.protocol === 'http:' && loopbackHosts.has(url.hostname))
|
|
45
|
+
return;
|
|
46
|
+
throw new Error(`Refusing to send the API token to ${url.origin}: use https (plain http is only allowed for localhost).`);
|
|
47
|
+
}
|
|
48
|
+
export function capabilityProxyUrl(apiUrl, path) {
|
|
49
|
+
const base = apiUrl.endsWith('/') ? apiUrl : `${apiUrl}/`;
|
|
50
|
+
return new URL(path, base);
|
|
51
|
+
}
|
|
52
|
+
export async function openCapabilityProxySession(input) {
|
|
53
|
+
const url = capabilityProxyUrl(input.apiUrl, capabilityProxySessionPath);
|
|
54
|
+
const response = await send(input, url, { method: 'GET' });
|
|
55
|
+
const body = await readJson(response);
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw describeFailure(response.status, body, url, 'session');
|
|
58
|
+
}
|
|
59
|
+
const record = isRecord(body) ? body : {};
|
|
60
|
+
return {
|
|
61
|
+
scopes: Array.isArray(record.scopes)
|
|
62
|
+
? record.scopes.filter((scope) => typeof scope === 'string')
|
|
63
|
+
: [],
|
|
64
|
+
expiresAt: typeof record.expiresAt === 'string' ? record.expiresAt : null,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export async function callCapabilityProxy(input) {
|
|
68
|
+
const url = capabilityProxyUrl(input.apiUrl, capabilityProxyCallPath);
|
|
69
|
+
const payload = { path: input.path, args: input.args };
|
|
70
|
+
if (input.conversationId !== undefined)
|
|
71
|
+
payload.conversationId = input.conversationId;
|
|
72
|
+
const response = await send(input, url, {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: { 'content-type': 'application/json' },
|
|
75
|
+
body: JSON.stringify(payload),
|
|
76
|
+
});
|
|
77
|
+
const body = await readJson(response);
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw describeFailure(response.status, body, url, 'call');
|
|
80
|
+
}
|
|
81
|
+
const failure = readErrorBody(body);
|
|
82
|
+
if (failure) {
|
|
83
|
+
throw new CapabilityProxyError(failure.message, { status: response.status, code: failure.code });
|
|
84
|
+
}
|
|
85
|
+
return isRecord(body) ? body.result : undefined;
|
|
86
|
+
}
|
|
87
|
+
async function send(input, url, init) {
|
|
88
|
+
const fetchFn = input.fetchFn ?? fetch;
|
|
89
|
+
try {
|
|
90
|
+
return await fetchFn(url, {
|
|
91
|
+
...init,
|
|
92
|
+
signal: input.signal,
|
|
93
|
+
headers: {
|
|
94
|
+
...init.headers,
|
|
95
|
+
accept: 'application/json',
|
|
96
|
+
authorization: `Bearer ${input.token}`,
|
|
97
|
+
'user-agent': `${cliName}/${readPackageVersion()}`,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
const reason = describeNetworkError(error);
|
|
103
|
+
throw new CapabilityProxyError(`Could not reach the Kody API at ${url.origin} (${reason}). Check --api-url / KODY_API_URL and your network.`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function readJson(response) {
|
|
107
|
+
const text = await response.text();
|
|
108
|
+
if (!text)
|
|
109
|
+
return null;
|
|
110
|
+
try {
|
|
111
|
+
return JSON.parse(text);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return text;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function readErrorBody(body) {
|
|
118
|
+
if (!isRecord(body) || body.error == null)
|
|
119
|
+
return null;
|
|
120
|
+
const { error } = body;
|
|
121
|
+
if (typeof error === 'string')
|
|
122
|
+
return { code: null, message: error };
|
|
123
|
+
if (isRecord(error)) {
|
|
124
|
+
const code = typeof error.code === 'string' ? error.code : null;
|
|
125
|
+
const message = typeof error.message === 'string' ? error.message : code ?? JSON.stringify(error);
|
|
126
|
+
return { code, message };
|
|
127
|
+
}
|
|
128
|
+
return { code: null, message: String(error) };
|
|
129
|
+
}
|
|
130
|
+
function describeFailure(status, body, url, stage) {
|
|
131
|
+
const failure = readErrorBody(body);
|
|
132
|
+
const code = failure?.code ?? null;
|
|
133
|
+
const detail = failure?.message ? ` Server said: ${failure.message}` : '';
|
|
134
|
+
if (status === 401) {
|
|
135
|
+
return new CapabilityProxyError(`Kody rejected the API token (expired, revoked, or malformed). Mint a fresh scoped token and pass it with --token or ${apiTokenEnvVar}.${detail}`, { status, code });
|
|
136
|
+
}
|
|
137
|
+
if (code === 'feature_disabled') {
|
|
138
|
+
return new CapabilityProxyError('CapabilityProxy is not enabled for this Kody account (feature flag `local-execute`). Use `kody login` for MCP cloud execute, or ask the Kody team for access.', { status, code });
|
|
139
|
+
}
|
|
140
|
+
if (status === 403 && stage === 'session') {
|
|
141
|
+
return new CapabilityProxyError(`The API token is not allowed to use CapabilityProxy${code ? ` (${code})` : ''}. Mint a token with the local-execute scope (needed for both \`execute --local\` and token-auth cloud execute).${detail}`, { status, code });
|
|
142
|
+
}
|
|
143
|
+
if (status === 404 && stage === 'session') {
|
|
144
|
+
return new CapabilityProxyError(`CapabilityProxy was not found at ${url.href}. Local execute may not be deployed on this Kody API yet; check --api-url / KODY_API_URL.`, { status, code });
|
|
145
|
+
}
|
|
146
|
+
return new CapabilityProxyError(failure?.message ?? `Kody API request failed with HTTP ${status} (${url.pathname}).`, { status, code });
|
|
147
|
+
}
|
|
148
|
+
function isRecord(value) {
|
|
149
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=capability-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-proxy.js","sourceRoot":"","sources":["../src/capability-proxy.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
export { readApiToken, requireApiToken as resolveApiToken } from './api-token.js';
|
|
1
2
|
export type CommandName = 'login' | 'logout' | 'status' | 'whoami' | 'search' | 'execute' | 'install' | 'skill' | 'help' | 'version';
|
|
3
|
+
export declare function apiUrlFrom(values: {
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
}, env?: NodeJS.ProcessEnv): string;
|
|
2
6
|
declare function parseKnown(args: Array<string>): {
|
|
3
7
|
values: {
|
|
4
8
|
[longOption: string]: string | boolean | undefined;
|
|
@@ -15,6 +19,9 @@ declare function parseKnown(args: Array<string>): {
|
|
|
15
19
|
invoke?: string | boolean | undefined;
|
|
16
20
|
params?: string | boolean | undefined;
|
|
17
21
|
'conversation-id'?: string | boolean | undefined;
|
|
22
|
+
local?: string | boolean | undefined;
|
|
23
|
+
token?: string | boolean | undefined;
|
|
24
|
+
'api-url'?: string | boolean | undefined;
|
|
18
25
|
project?: string | boolean | undefined;
|
|
19
26
|
'no-browser'?: string | boolean | undefined;
|
|
20
27
|
clients?: string | boolean | undefined;
|
|
@@ -48,4 +55,19 @@ export declare function buildExecuteToolArgs(input: {
|
|
|
48
55
|
paramsJson?: string;
|
|
49
56
|
conversationId?: string;
|
|
50
57
|
}): Record<string, unknown>;
|
|
51
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Prefer a scoped API token when the user passed `--token`, or when
|
|
60
|
+
* `KODY_API_TOKEN` is set and there is no stored `kody login` session.
|
|
61
|
+
* Logged-in MCP OAuth still wins over an env-only token so a leftover
|
|
62
|
+
* `KODY_API_TOKEN` does not hijack cloud MCP commands.
|
|
63
|
+
*/
|
|
64
|
+
export declare function shouldUseApiToken(input: {
|
|
65
|
+
tokenValues: {
|
|
66
|
+
token?: string;
|
|
67
|
+
};
|
|
68
|
+
mcpUrl: string;
|
|
69
|
+
allowEnvWithoutLogin: boolean;
|
|
70
|
+
env?: NodeJS.ProcessEnv;
|
|
71
|
+
/** Override session detection (tests). */
|
|
72
|
+
hasSession?: boolean;
|
|
73
|
+
}): boolean;
|
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
3
|
+
import { hasExplicitTokenFlag, readApiToken, requireApiToken, } from './api-token.js';
|
|
4
|
+
import { defaultApiUrl, defaultMcpUrl, modernMcpProtocolVersion } from './defaults.js';
|
|
4
5
|
import { usage } from './help.js';
|
|
5
6
|
import { ensureFreshCredentials, login } from './auth.js';
|
|
6
7
|
import { deleteCredentials, loadCredentials } from './store.js';
|
|
7
8
|
import { callKodyTool, formatToolResult, listKodyTools } from './mcp.js';
|
|
8
9
|
import { runInstall } from './install.js';
|
|
10
|
+
import { runLocalExecute } from './local-execute.js';
|
|
11
|
+
import { searchWithApiToken, whoamiWithApiToken } from './open-api-client.js';
|
|
12
|
+
import { runRemoteExecuteWithToken } from './remote-execute.js';
|
|
9
13
|
import { installSkill } from './skill.js';
|
|
10
14
|
import { readPackageVersion } from './package-info.js';
|
|
11
15
|
import { redactError } from './redact.js';
|
|
16
|
+
export { readApiToken, requireApiToken as resolveApiToken } from './api-token.js';
|
|
12
17
|
function mcpUrlFrom(values) {
|
|
13
18
|
return values.mcpUrl || process.env.KODY_MCP_URL || defaultMcpUrl;
|
|
14
19
|
}
|
|
20
|
+
export function apiUrlFrom(values, env = process.env) {
|
|
21
|
+
return values.apiUrl || env.KODY_API_URL || defaultApiUrl;
|
|
22
|
+
}
|
|
15
23
|
function parseKnown(args) {
|
|
16
24
|
return parseArgs({
|
|
17
25
|
args,
|
|
@@ -30,6 +38,9 @@ function parseKnown(args) {
|
|
|
30
38
|
invoke: { type: 'string' },
|
|
31
39
|
params: { type: 'string' },
|
|
32
40
|
'conversation-id': { type: 'string' },
|
|
41
|
+
local: { type: 'boolean' },
|
|
42
|
+
token: { type: 'string' },
|
|
43
|
+
'api-url': { type: 'string' },
|
|
33
44
|
project: { type: 'boolean' },
|
|
34
45
|
'no-browser': { type: 'boolean' },
|
|
35
46
|
clients: { type: 'string' },
|
|
@@ -73,14 +84,14 @@ export async function runCli(argv = process.argv.slice(2), io = {}) {
|
|
|
73
84
|
write(usage);
|
|
74
85
|
return 0;
|
|
75
86
|
}
|
|
76
|
-
return await dispatch(parsed, write);
|
|
87
|
+
return await dispatch(parsed, write, writeErr);
|
|
77
88
|
}
|
|
78
89
|
catch (error) {
|
|
79
90
|
writeErr(`${redactError(error).message}\n`);
|
|
80
91
|
return 1;
|
|
81
92
|
}
|
|
82
93
|
}
|
|
83
|
-
async function dispatch(parsed, write) {
|
|
94
|
+
async function dispatch(parsed, write, writeErr) {
|
|
84
95
|
const mcpUrl = mcpUrlFrom({
|
|
85
96
|
mcpUrl: typeof parsed.values['mcp-url'] === 'string' ? parsed.values['mcp-url'] : undefined,
|
|
86
97
|
});
|
|
@@ -135,6 +146,34 @@ async function dispatch(parsed, write) {
|
|
|
135
146
|
return 0;
|
|
136
147
|
}
|
|
137
148
|
case 'whoami': {
|
|
149
|
+
const tokenValues = tokenFlagValues(parsed.values);
|
|
150
|
+
if (shouldUseApiToken({ tokenValues, mcpUrl, allowEnvWithoutLogin: true })) {
|
|
151
|
+
const identity = await whoamiWithApiToken({
|
|
152
|
+
token: requireApiToken(tokenValues, process.env, 'whoami'),
|
|
153
|
+
apiUrl: apiUrlFrom({
|
|
154
|
+
apiUrl: typeof parsed.values['api-url'] === 'string'
|
|
155
|
+
? parsed.values['api-url']
|
|
156
|
+
: undefined,
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
if (json) {
|
|
160
|
+
write(`${JSON.stringify(identity, null, 2)}\n`);
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
const scopeLine = identity.token.scopes.join(', ') || '(none)';
|
|
164
|
+
const userLine = identity.user
|
|
165
|
+
? `${identity.user.displayName} <${identity.user.email}>`
|
|
166
|
+
: '(account:read not on this token)';
|
|
167
|
+
write([
|
|
168
|
+
`api: ${identity.apiUrl}`,
|
|
169
|
+
`auth: API token (${identity.token.id})`,
|
|
170
|
+
`user: ${userLine}`,
|
|
171
|
+
`scopes: ${scopeLine}`,
|
|
172
|
+
`expires: ${identity.token.expiresAt ?? 'unknown'}`,
|
|
173
|
+
'',
|
|
174
|
+
].join('\n'));
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
138
177
|
const credentials = await ensureFreshCredentials({ mcpUrl });
|
|
139
178
|
const tools = await listKodyTools({ mcpUrl });
|
|
140
179
|
if (json) {
|
|
@@ -151,6 +190,25 @@ async function dispatch(parsed, write) {
|
|
|
151
190
|
}
|
|
152
191
|
case 'search': {
|
|
153
192
|
const query = parsed.positionals.join(' ').trim();
|
|
193
|
+
const tokenValues = tokenFlagValues(parsed.values);
|
|
194
|
+
if (shouldUseApiToken({ tokenValues, mcpUrl, allowEnvWithoutLogin: true })) {
|
|
195
|
+
const result = await searchWithApiToken({
|
|
196
|
+
token: requireApiToken(tokenValues, process.env, 'search'),
|
|
197
|
+
apiUrl: apiUrlFrom({
|
|
198
|
+
apiUrl: typeof parsed.values['api-url'] === 'string'
|
|
199
|
+
? parsed.values['api-url']
|
|
200
|
+
: undefined,
|
|
201
|
+
}),
|
|
202
|
+
query: query || undefined,
|
|
203
|
+
entity: typeof parsed.values.entity === 'string' ? parsed.values.entity : undefined,
|
|
204
|
+
domain: typeof parsed.values.domain === 'string' ? parsed.values.domain : undefined,
|
|
205
|
+
limit: typeof parsed.values.limit === 'string'
|
|
206
|
+
? Number(parsed.values.limit)
|
|
207
|
+
: undefined,
|
|
208
|
+
});
|
|
209
|
+
write(formatToolResult(result, json));
|
|
210
|
+
return result.isError ? 1 : 0;
|
|
211
|
+
}
|
|
154
212
|
const args = {};
|
|
155
213
|
if (query)
|
|
156
214
|
args.query = query;
|
|
@@ -169,6 +227,11 @@ async function dispatch(parsed, write) {
|
|
|
169
227
|
const hasCodeFlag = typeof parsed.values.code === 'string';
|
|
170
228
|
const hasFileFlag = typeof parsed.values.file === 'string';
|
|
171
229
|
const positionalModule = parsed.positionals.join('\n').trim();
|
|
230
|
+
const local = parsed.values.local === true;
|
|
231
|
+
const tokenValues = tokenFlagValues(parsed.values);
|
|
232
|
+
if (local && invoke !== undefined) {
|
|
233
|
+
throw new Error('--invoke runs a saved package export in Kody; --local runs a module you provide (--code, --file, or a module string).');
|
|
234
|
+
}
|
|
172
235
|
if (executeSourcesConflict({ invoke, hasCodeFlag, hasFileFlag, positionalModule })) {
|
|
173
236
|
throw new Error('--invoke cannot be combined with --code, --file, or a module string.');
|
|
174
237
|
}
|
|
@@ -189,7 +252,36 @@ async function dispatch(parsed, write) {
|
|
|
189
252
|
? parsed.values['conversation-id']
|
|
190
253
|
: undefined,
|
|
191
254
|
});
|
|
192
|
-
const
|
|
255
|
+
const apiUrl = apiUrlFrom({
|
|
256
|
+
apiUrl: typeof parsed.values['api-url'] === 'string'
|
|
257
|
+
? parsed.values['api-url']
|
|
258
|
+
: undefined,
|
|
259
|
+
});
|
|
260
|
+
const useToken = local ||
|
|
261
|
+
shouldUseApiToken({
|
|
262
|
+
tokenValues,
|
|
263
|
+
mcpUrl,
|
|
264
|
+
allowEnvWithoutLogin: true,
|
|
265
|
+
});
|
|
266
|
+
const result = local
|
|
267
|
+
? await runLocalExecute({
|
|
268
|
+
code: args.code,
|
|
269
|
+
params: args.params,
|
|
270
|
+
conversationId: args.conversationId,
|
|
271
|
+
token: requireApiToken(tokenValues, process.env, 'execute --local'),
|
|
272
|
+
apiUrl,
|
|
273
|
+
onStatus: (message) => writeErr(`${message}\n`),
|
|
274
|
+
})
|
|
275
|
+
: useToken
|
|
276
|
+
? await runRemoteExecuteWithToken({
|
|
277
|
+
code: typeof args.code === 'string' ? args.code : undefined,
|
|
278
|
+
invoke,
|
|
279
|
+
params: args.params,
|
|
280
|
+
conversationId: args.conversationId,
|
|
281
|
+
token: requireApiToken(tokenValues, process.env, 'execute with an API token'),
|
|
282
|
+
apiUrl,
|
|
283
|
+
})
|
|
284
|
+
: await callKodyTool({ name: 'execute', args, mcpUrl });
|
|
193
285
|
write(formatToolResult(result, json));
|
|
194
286
|
return result.isError ? 1 : 0;
|
|
195
287
|
}
|
|
@@ -260,4 +352,25 @@ function attachExecuteCommonArgs(args, input) {
|
|
|
260
352
|
}
|
|
261
353
|
return args;
|
|
262
354
|
}
|
|
355
|
+
function tokenFlagValues(values) {
|
|
356
|
+
return {
|
|
357
|
+
token: typeof values.token === 'string' ? values.token : undefined,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Prefer a scoped API token when the user passed `--token`, or when
|
|
362
|
+
* `KODY_API_TOKEN` is set and there is no stored `kody login` session.
|
|
363
|
+
* Logged-in MCP OAuth still wins over an env-only token so a leftover
|
|
364
|
+
* `KODY_API_TOKEN` does not hijack cloud MCP commands.
|
|
365
|
+
*/
|
|
366
|
+
export function shouldUseApiToken(input) {
|
|
367
|
+
if (hasExplicitTokenFlag(input.tokenValues))
|
|
368
|
+
return true;
|
|
369
|
+
if (!input.allowEnvWithoutLogin)
|
|
370
|
+
return false;
|
|
371
|
+
if (!readApiToken(input.tokenValues, input.env))
|
|
372
|
+
return false;
|
|
373
|
+
const loggedIn = input.hasSession ?? loadCredentials(input.mcpUrl) != null;
|
|
374
|
+
return !loggedIn;
|
|
375
|
+
}
|
|
263
376
|
//# sourceMappingURL=cli.js.map
|