shadok-ai 0.15.43 → 0.15.44
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 +57 -3
- package/dist/backend.d.ts +138 -0
- package/dist/backend.js +220 -0
- package/dist/backend.js.map +1 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/first-agent.d.ts +6 -0
- package/dist/first-agent.js +8 -1
- package/dist/first-agent.js.map +1 -1
- package/dist/server.js +205 -15
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/public/index.html +251 -17
package/README.md
CHANGED
|
@@ -604,6 +604,55 @@ file *before* starting the container (`docker create` → `docker cp` →
|
|
|
604
604
|
`docker start`). That ordering is no longer necessary: shadok writes the file
|
|
605
605
|
itself, before any session spawns.
|
|
606
606
|
|
|
607
|
+
### Running on a self-hosted model
|
|
608
|
+
|
|
609
|
+
shadok drives Claude Code, and Claude Code can talk to any server that speaks
|
|
610
|
+
the Anthropic Messages API: Ollama, llama.cpp's `llama-server`, vLLM, or a
|
|
611
|
+
LiteLLM proxy in front of anything else. Point it there with
|
|
612
|
+
`ANTHROPIC_BASE_URL`, and agents on that backend need **no Claude sign-in** and
|
|
613
|
+
are **never held by the quota guard** (they don't spend your subscription).
|
|
614
|
+
|
|
615
|
+
Three ways to do it:
|
|
616
|
+
|
|
617
|
+
- **From the cockpit — the easy one.** The sign-in card a fresh instance opens
|
|
618
|
+
has a second side, **Another model**: pick a provider (Ollama, a llama.cpp
|
|
619
|
+
server, DeepSeek, Z.ai/GLM, Moonshot/Kimi, or any Anthropic-compatible
|
|
620
|
+
server), the address and model are pre-filled, the models a local server
|
|
621
|
+
serves are listed for you, and **Test** sends one real request before
|
|
622
|
+
anything is saved. The lead agent starts as soon as you save. Change it later
|
|
623
|
+
from the version menu, **⚙ Model backend…** (which also offers to go back to
|
|
624
|
+
the subscription). The choice is stored per launch directory in
|
|
625
|
+
`~/.shadok-ai/config.json` and takes precedence over the environment; running
|
|
626
|
+
agents keep the model they were started with until they are reloaded.
|
|
627
|
+
- **The whole instance, from the environment.** Launch shadok with the variables:
|
|
628
|
+
|
|
629
|
+
```bash
|
|
630
|
+
ANTHROPIC_BASE_URL=http://localhost:11434 ANTHROPIC_AUTH_TOKEN=ollama ANTHROPIC_MODEL=qwen3-coder:30b SHADOK_CONTEXT_WINDOW=64k npx shadok-ai
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
- **One role.** Store the same names as vault secrets and attach them to a
|
|
634
|
+
profile: agents on that profile use the local model, the rest stay on the
|
|
635
|
+
subscription. A profile's secrets win over the instance's environment.
|
|
636
|
+
|
|
637
|
+
| Variable | Meaning |
|
|
638
|
+
|---|---|
|
|
639
|
+
| `ANTHROPIC_BASE_URL` | The server. Anything that is not `*.anthropic.com` counts as a custom backend. |
|
|
640
|
+
| `ANTHROPIC_AUTH_TOKEN` | Sent as a bearer token. Any value when the server checks none. Prefer it to `ANTHROPIC_API_KEY`, which makes Claude Code ask for confirmation. |
|
|
641
|
+
| `ANTHROPIC_MODEL` | The model to run. Also fills every unset `ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL` and `ANTHROPIC_SMALL_FAST_MODEL`, so the model picker and Claude Code's background calls reach it too. |
|
|
642
|
+
| `SHADOK_CONTEXT_WINDOW` | The window the context gauge measures against (`64000`, `64k`, `1m`). Without it the gauge assumes Claude's 200k. |
|
|
643
|
+
|
|
644
|
+
Hugging Face Inference, Together, Groq and the other **OpenAI-only** APIs are
|
|
645
|
+
not in the list on purpose: Claude Code speaks only the Anthropic format, so
|
|
646
|
+
they need a translating proxy (LiteLLM, claude-code-router) in front — then
|
|
647
|
+
point *Another Anthropic-compatible server* at the proxy. The Test button tells
|
|
648
|
+
the two cases apart and says so.
|
|
649
|
+
|
|
650
|
+
Two things are on you. The model needs **reliable tool calling and a long
|
|
651
|
+
context**: Claude Code's own prompt and tool list take ~15–20k tokens before
|
|
652
|
+
your first word. And the server must actually *serve* that context: Ollama's
|
|
653
|
+
default is a few thousand tokens, so set `OLLAMA_CONTEXT_LENGTH` (64k is a sane
|
|
654
|
+
floor) or the agent will lose its instructions without any error.
|
|
655
|
+
|
|
607
656
|
### SSH identity in Docker
|
|
608
657
|
|
|
609
658
|
When shadok-ai runs **in a container** it gives itself an SSH key on first boot
|
|
@@ -859,10 +908,15 @@ right now), `/version`, `/autoupdate`,
|
|
|
859
908
|
returns the cwd to start the tweak agent in), `/media/<name>` (GET — serves a
|
|
860
909
|
pasted/dropped or Telegram attachment back for inline display; sandboxed, basename
|
|
861
910
|
only), and the sign-in group: `/auth` (GET
|
|
862
|
-
— `{loggedIn, email?, subscriptionType?, tokenExpiresAt?, accessExpiresAt?}`;
|
|
863
|
-
the expiries come from the credentials store, never a token value
|
|
911
|
+
— `{loggedIn, email?, subscriptionType?, tokenExpiresAt?, accessExpiresAt?, backend?, backendModel?}`;
|
|
912
|
+
the expiries come from the credentials store, never a token value; `backend:
|
|
913
|
+
"custom"` when the instance runs on another model), `/auth/login`
|
|
864
914
|
(POST — start a flow, returns `{url}`; DELETE — cancel it), `/auth/code`
|
|
865
|
-
(POST `{code}`)
|
|
915
|
+
(POST `{code}`); and the model-backend group (admin, browser only for writes):
|
|
916
|
+
`/backend` (GET — the backend in use without its key, plus the presets; PUT —
|
|
917
|
+
`{baseUrl, model, authToken?, contextWindow?}` or `{clear: true}`),
|
|
918
|
+
`/backend/test` (POST — one real `/v1/messages` round trip) and
|
|
919
|
+
`/backend/models` (POST — the models a server offers).
|
|
866
920
|
|
|
867
921
|
## Library
|
|
868
922
|
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which model backend an agent talks to: the Claude subscription, or a CUSTOM
|
|
3
|
+
* one — a self-hosted open model (Ollama, llama.cpp, vLLM) or a gateway
|
|
4
|
+
* (LiteLLM) that speaks the Anthropic Messages API.
|
|
5
|
+
*
|
|
6
|
+
* shadok never calls a model itself; it drives the `claude` TUI. So "plugging
|
|
7
|
+
* in another model" is Claude Code's own mechanism — `ANTHROPIC_BASE_URL` plus
|
|
8
|
+
* a token and a model name — and all shadok has to do is (a) carry those
|
|
9
|
+
* variables to the agent and (b) stop treating that agent as a subscription
|
|
10
|
+
* agent: no sign-in required, no quota guard, a context window that is not
|
|
11
|
+
* Claude's.
|
|
12
|
+
*
|
|
13
|
+
* Two sources, the same order as everything else an agent gets: the
|
|
14
|
+
* environment shadok was launched with (a whole instance on a local model),
|
|
15
|
+
* then the profile's vault secrets (one role on a local model, the rest on the
|
|
16
|
+
* subscription). Secrets win, like they do for every other variable.
|
|
17
|
+
*
|
|
18
|
+
* Pure: takes plain records, so the rules are tested without a process.
|
|
19
|
+
*/
|
|
20
|
+
/** The variables that select and configure a backend. Only these are carried
|
|
21
|
+
* over from shadok's own environment — never the whole env. */
|
|
22
|
+
export declare const BACKEND_VARS: readonly ["ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_API_KEY", "ANTHROPIC_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_SMALL_FAST_MODEL", "SHADOK_CONTEXT_WINDOW"];
|
|
23
|
+
export type Backend = {
|
|
24
|
+
kind: "subscription";
|
|
25
|
+
} | {
|
|
26
|
+
kind: "custom";
|
|
27
|
+
baseUrl: string;
|
|
28
|
+
model: string | null;
|
|
29
|
+
contextWindow: number | null;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Is `ANTHROPIC_BASE_URL` pointing somewhere that is NOT Anthropic?
|
|
33
|
+
*
|
|
34
|
+
* Anthropic's own host is excluded so that a user who happens to export the
|
|
35
|
+
* default URL is not silently taken off the sign-in and quota guards. A URL
|
|
36
|
+
* that does not parse is NOT treated as custom: guessing "custom" would drop
|
|
37
|
+
* the sign-in check for an agent that will then fail to reach anything.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isCustomBaseUrl(raw: string | undefined): boolean;
|
|
40
|
+
/** A positive integer window, or null. Accepts `64000`, `64k`, `1m`. */
|
|
41
|
+
export declare function parseContextWindow(raw: string | undefined): number | null;
|
|
42
|
+
/** Which backend an agent with this environment talks to. */
|
|
43
|
+
export declare function backendFor(env: Record<string, string | undefined>): Backend;
|
|
44
|
+
/**
|
|
45
|
+
* The backend variables to hand an agent, from shadok's own environment and
|
|
46
|
+
* the profile's secrets.
|
|
47
|
+
*
|
|
48
|
+
* Shadok's environment contributes ONLY when it points at a custom backend.
|
|
49
|
+
* A developer's shell commonly carries an `ANTHROPIC_API_KEY` for other work;
|
|
50
|
+
* forwarding it to a subscription instance would quietly move every agent onto
|
|
51
|
+
* API billing. (A node-pty agent inherits the whole env anyway — that was
|
|
52
|
+
* already true and is not widened here; a tmux pane does NOT reliably inherit
|
|
53
|
+
* it, since tmux hands a new session its server's environment, which is why
|
|
54
|
+
* the variables are passed explicitly.)
|
|
55
|
+
*
|
|
56
|
+
* On a custom backend with a model named, every alias that is not mapped is
|
|
57
|
+
* mapped to it, so `opus` in the picker and Claude Code's background
|
|
58
|
+
* small-model calls land on the one model the server serves.
|
|
59
|
+
*/
|
|
60
|
+
export declare function backendEnv(processEnv: Record<string, string | undefined>, secretEnv: Record<string, string>): Record<string, string>;
|
|
61
|
+
/** What config.json stores for one launch directory. */
|
|
62
|
+
export interface ConfiguredBackend {
|
|
63
|
+
baseUrl: string;
|
|
64
|
+
model: string;
|
|
65
|
+
/** Sent as a bearer token. Absent for a local server that checks none. */
|
|
66
|
+
authToken?: string;
|
|
67
|
+
/** For the gauge, e.g. 64000. */
|
|
68
|
+
contextWindow?: number;
|
|
69
|
+
}
|
|
70
|
+
/** The ready-made choices the onboarding card offers. ONLY servers that speak
|
|
71
|
+
* the Anthropic Messages API: Claude Code speaks nothing else, so an
|
|
72
|
+
* OpenAI-only provider (Hugging Face Inference, Together, Groq) needs a
|
|
73
|
+
* translating proxy such as LiteLLM in front, and goes through "custom". The
|
|
74
|
+
* model names are starting points — the Test button is what proves them. */
|
|
75
|
+
export declare const BACKEND_PRESETS: readonly [{
|
|
76
|
+
readonly id: "ollama";
|
|
77
|
+
readonly label: "Ollama (on this machine)";
|
|
78
|
+
readonly baseUrl: "http://localhost:11434";
|
|
79
|
+
readonly needsKey: false;
|
|
80
|
+
readonly model: "";
|
|
81
|
+
}, {
|
|
82
|
+
readonly id: "llamacpp";
|
|
83
|
+
readonly label: "llama.cpp server (on this machine)";
|
|
84
|
+
readonly baseUrl: "http://localhost:8080";
|
|
85
|
+
readonly needsKey: false;
|
|
86
|
+
readonly model: "";
|
|
87
|
+
}, {
|
|
88
|
+
readonly id: "deepseek";
|
|
89
|
+
readonly label: "DeepSeek";
|
|
90
|
+
readonly baseUrl: "https://api.deepseek.com/anthropic";
|
|
91
|
+
readonly needsKey: true;
|
|
92
|
+
readonly model: "deepseek-chat";
|
|
93
|
+
}, {
|
|
94
|
+
readonly id: "zai";
|
|
95
|
+
readonly label: "Z.ai (GLM)";
|
|
96
|
+
readonly baseUrl: "https://api.z.ai/api/anthropic";
|
|
97
|
+
readonly needsKey: true;
|
|
98
|
+
readonly model: "glm-4.6";
|
|
99
|
+
}, {
|
|
100
|
+
readonly id: "moonshot";
|
|
101
|
+
readonly label: "Moonshot (Kimi)";
|
|
102
|
+
readonly baseUrl: "https://api.moonshot.ai/anthropic";
|
|
103
|
+
readonly needsKey: true;
|
|
104
|
+
readonly model: "kimi-k2-0905-preview";
|
|
105
|
+
}, {
|
|
106
|
+
readonly id: "custom";
|
|
107
|
+
readonly label: "Another Anthropic-compatible server";
|
|
108
|
+
readonly baseUrl: "";
|
|
109
|
+
readonly needsKey: false;
|
|
110
|
+
readonly model: "";
|
|
111
|
+
}];
|
|
112
|
+
/** The env a configured backend hands an agent (before alias mapping). */
|
|
113
|
+
export declare function configuredBackendEnv(b: ConfiguredBackend): Record<string, string>;
|
|
114
|
+
/** The instance-wide source: the configured backend if any, else shadok's env. */
|
|
115
|
+
export declare function instanceEnv(processEnv: Record<string, string | undefined>, configured: ConfiguredBackend | null | undefined): Record<string, string | undefined>;
|
|
116
|
+
export type SaveVerdict = {
|
|
117
|
+
ok: true;
|
|
118
|
+
value: ConfiguredBackend;
|
|
119
|
+
} | {
|
|
120
|
+
ok: false;
|
|
121
|
+
error: string;
|
|
122
|
+
};
|
|
123
|
+
/** Validate a PUT /backend body. `savedToken` is kept when the body leaves the
|
|
124
|
+
* key empty for the SAME server — the field shows no secret, so an empty box
|
|
125
|
+
* means "unchanged", never "remove it". */
|
|
126
|
+
export declare function backendSaveVerdict(body: unknown, saved?: ConfiguredBackend | null): SaveVerdict;
|
|
127
|
+
/** `base` + `path`, without doubling or dropping a slash. */
|
|
128
|
+
export declare function joinUrl(base: string, p: string): string;
|
|
129
|
+
/** Model names out of an Ollama `/api/tags` or an OpenAI-style `/v1/models`
|
|
130
|
+
* answer; anything else is an empty list, never a throw. */
|
|
131
|
+
export declare function parseModelList(json: unknown): string[];
|
|
132
|
+
/** The smallest request that proves the whole chain: URL, key, model, and the
|
|
133
|
+
* Anthropic format itself. */
|
|
134
|
+
export declare function testRequestBody(model: string): Record<string, unknown>;
|
|
135
|
+
/** A test that failed, in words that say what to change. */
|
|
136
|
+
export declare function testFailure(status: number, bodyText: string): string;
|
|
137
|
+
/** Did a /v1/messages answer look like an Anthropic message? */
|
|
138
|
+
export declare function looksLikeMessage(json: unknown): boolean;
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which model backend an agent talks to: the Claude subscription, or a CUSTOM
|
|
3
|
+
* one — a self-hosted open model (Ollama, llama.cpp, vLLM) or a gateway
|
|
4
|
+
* (LiteLLM) that speaks the Anthropic Messages API.
|
|
5
|
+
*
|
|
6
|
+
* shadok never calls a model itself; it drives the `claude` TUI. So "plugging
|
|
7
|
+
* in another model" is Claude Code's own mechanism — `ANTHROPIC_BASE_URL` plus
|
|
8
|
+
* a token and a model name — and all shadok has to do is (a) carry those
|
|
9
|
+
* variables to the agent and (b) stop treating that agent as a subscription
|
|
10
|
+
* agent: no sign-in required, no quota guard, a context window that is not
|
|
11
|
+
* Claude's.
|
|
12
|
+
*
|
|
13
|
+
* Two sources, the same order as everything else an agent gets: the
|
|
14
|
+
* environment shadok was launched with (a whole instance on a local model),
|
|
15
|
+
* then the profile's vault secrets (one role on a local model, the rest on the
|
|
16
|
+
* subscription). Secrets win, like they do for every other variable.
|
|
17
|
+
*
|
|
18
|
+
* Pure: takes plain records, so the rules are tested without a process.
|
|
19
|
+
*/
|
|
20
|
+
/** The variables that select and configure a backend. Only these are carried
|
|
21
|
+
* over from shadok's own environment — never the whole env. */
|
|
22
|
+
export const BACKEND_VARS = [
|
|
23
|
+
"ANTHROPIC_BASE_URL",
|
|
24
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
25
|
+
"ANTHROPIC_API_KEY",
|
|
26
|
+
"ANTHROPIC_MODEL",
|
|
27
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
28
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
29
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
30
|
+
"ANTHROPIC_SMALL_FAST_MODEL",
|
|
31
|
+
"SHADOK_CONTEXT_WINDOW",
|
|
32
|
+
];
|
|
33
|
+
/** The aliases a model picker or Claude Code itself may resolve. Each needs a
|
|
34
|
+
* local name, or the request reaches the custom server as `claude-opus-…`, a
|
|
35
|
+
* model it has never heard of. */
|
|
36
|
+
const ALIAS_VARS = [
|
|
37
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
38
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
39
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
40
|
+
// Claude Code's background calls (titles, summaries) use the small model.
|
|
41
|
+
// Left unmapped, every one of them fails against a server that only has one.
|
|
42
|
+
"ANTHROPIC_SMALL_FAST_MODEL",
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* Is `ANTHROPIC_BASE_URL` pointing somewhere that is NOT Anthropic?
|
|
46
|
+
*
|
|
47
|
+
* Anthropic's own host is excluded so that a user who happens to export the
|
|
48
|
+
* default URL is not silently taken off the sign-in and quota guards. A URL
|
|
49
|
+
* that does not parse is NOT treated as custom: guessing "custom" would drop
|
|
50
|
+
* the sign-in check for an agent that will then fail to reach anything.
|
|
51
|
+
*/
|
|
52
|
+
export function isCustomBaseUrl(raw) {
|
|
53
|
+
const v = raw?.trim();
|
|
54
|
+
if (!v)
|
|
55
|
+
return false;
|
|
56
|
+
let host;
|
|
57
|
+
try {
|
|
58
|
+
const u = new URL(v);
|
|
59
|
+
// `localhost:11434` parses too — with `localhost:` as its SCHEME. Only an
|
|
60
|
+
// http(s) URL is a server Claude Code can reach.
|
|
61
|
+
if (u.protocol !== "http:" && u.protocol !== "https:")
|
|
62
|
+
return false;
|
|
63
|
+
host = u.hostname.toLowerCase();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return host !== "anthropic.com" && !host.endsWith(".anthropic.com");
|
|
69
|
+
}
|
|
70
|
+
/** A positive integer window, or null. Accepts `64000`, `64k`, `1m`. */
|
|
71
|
+
export function parseContextWindow(raw) {
|
|
72
|
+
const m = raw?.trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*([km]?)$/);
|
|
73
|
+
if (!m)
|
|
74
|
+
return null;
|
|
75
|
+
const n = Number(m[1]) * (m[2] === "k" ? 1_000 : m[2] === "m" ? 1_000_000 : 1);
|
|
76
|
+
return Number.isFinite(n) && n >= 1_000 ? Math.round(n) : null;
|
|
77
|
+
}
|
|
78
|
+
/** Which backend an agent with this environment talks to. */
|
|
79
|
+
export function backendFor(env) {
|
|
80
|
+
if (!isCustomBaseUrl(env.ANTHROPIC_BASE_URL))
|
|
81
|
+
return { kind: "subscription" };
|
|
82
|
+
return {
|
|
83
|
+
kind: "custom",
|
|
84
|
+
baseUrl: env.ANTHROPIC_BASE_URL.trim(),
|
|
85
|
+
model: env.ANTHROPIC_MODEL?.trim() || null,
|
|
86
|
+
contextWindow: parseContextWindow(env.SHADOK_CONTEXT_WINDOW),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The backend variables to hand an agent, from shadok's own environment and
|
|
91
|
+
* the profile's secrets.
|
|
92
|
+
*
|
|
93
|
+
* Shadok's environment contributes ONLY when it points at a custom backend.
|
|
94
|
+
* A developer's shell commonly carries an `ANTHROPIC_API_KEY` for other work;
|
|
95
|
+
* forwarding it to a subscription instance would quietly move every agent onto
|
|
96
|
+
* API billing. (A node-pty agent inherits the whole env anyway — that was
|
|
97
|
+
* already true and is not widened here; a tmux pane does NOT reliably inherit
|
|
98
|
+
* it, since tmux hands a new session its server's environment, which is why
|
|
99
|
+
* the variables are passed explicitly.)
|
|
100
|
+
*
|
|
101
|
+
* On a custom backend with a model named, every alias that is not mapped is
|
|
102
|
+
* mapped to it, so `opus` in the picker and Claude Code's background
|
|
103
|
+
* small-model calls land on the one model the server serves.
|
|
104
|
+
*/
|
|
105
|
+
export function backendEnv(processEnv, secretEnv) {
|
|
106
|
+
const out = {};
|
|
107
|
+
if (isCustomBaseUrl(processEnv.ANTHROPIC_BASE_URL)) {
|
|
108
|
+
for (const k of BACKEND_VARS) {
|
|
109
|
+
const v = processEnv[k];
|
|
110
|
+
if (v !== undefined && v !== "")
|
|
111
|
+
out[k] = v;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const k of BACKEND_VARS)
|
|
115
|
+
if (k in secretEnv)
|
|
116
|
+
out[k] = secretEnv[k];
|
|
117
|
+
const b = backendFor(out);
|
|
118
|
+
if (b.kind === "custom" && b.model) {
|
|
119
|
+
for (const k of ALIAS_VARS)
|
|
120
|
+
if (!out[k])
|
|
121
|
+
out[k] = b.model;
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
/** The ready-made choices the onboarding card offers. ONLY servers that speak
|
|
126
|
+
* the Anthropic Messages API: Claude Code speaks nothing else, so an
|
|
127
|
+
* OpenAI-only provider (Hugging Face Inference, Together, Groq) needs a
|
|
128
|
+
* translating proxy such as LiteLLM in front, and goes through "custom". The
|
|
129
|
+
* model names are starting points — the Test button is what proves them. */
|
|
130
|
+
export const BACKEND_PRESETS = [
|
|
131
|
+
{ id: "ollama", label: "Ollama (on this machine)", baseUrl: "http://localhost:11434", needsKey: false, model: "" },
|
|
132
|
+
{ id: "llamacpp", label: "llama.cpp server (on this machine)", baseUrl: "http://localhost:8080", needsKey: false, model: "" },
|
|
133
|
+
{ id: "deepseek", label: "DeepSeek", baseUrl: "https://api.deepseek.com/anthropic", needsKey: true, model: "deepseek-chat" },
|
|
134
|
+
{ id: "zai", label: "Z.ai (GLM)", baseUrl: "https://api.z.ai/api/anthropic", needsKey: true, model: "glm-4.6" },
|
|
135
|
+
{ id: "moonshot", label: "Moonshot (Kimi)", baseUrl: "https://api.moonshot.ai/anthropic", needsKey: true, model: "kimi-k2-0905-preview" },
|
|
136
|
+
{ id: "custom", label: "Another Anthropic-compatible server", baseUrl: "", needsKey: false, model: "" },
|
|
137
|
+
];
|
|
138
|
+
/** The env a configured backend hands an agent (before alias mapping). */
|
|
139
|
+
export function configuredBackendEnv(b) {
|
|
140
|
+
const env = { ANTHROPIC_BASE_URL: b.baseUrl, ANTHROPIC_MODEL: b.model };
|
|
141
|
+
// Claude Code wants SOME credential to skip its own sign-in; a local server
|
|
142
|
+
// ignores it. A placeholder is safer than leaving the field empty.
|
|
143
|
+
env.ANTHROPIC_AUTH_TOKEN = b.authToken || "none";
|
|
144
|
+
if (b.contextWindow)
|
|
145
|
+
env.SHADOK_CONTEXT_WINDOW = String(b.contextWindow);
|
|
146
|
+
return env;
|
|
147
|
+
}
|
|
148
|
+
/** The instance-wide source: the configured backend if any, else shadok's env. */
|
|
149
|
+
export function instanceEnv(processEnv, configured) {
|
|
150
|
+
return configured ? configuredBackendEnv(configured) : processEnv;
|
|
151
|
+
}
|
|
152
|
+
/** Validate a PUT /backend body. `savedToken` is kept when the body leaves the
|
|
153
|
+
* key empty for the SAME server — the field shows no secret, so an empty box
|
|
154
|
+
* means "unchanged", never "remove it". */
|
|
155
|
+
export function backendSaveVerdict(body, saved) {
|
|
156
|
+
const b = (body ?? {});
|
|
157
|
+
const baseUrl = typeof b.baseUrl === "string" ? b.baseUrl.trim().replace(/\/+$/, "") : "";
|
|
158
|
+
const model = typeof b.model === "string" ? b.model.trim() : "";
|
|
159
|
+
if (!isCustomBaseUrl(baseUrl))
|
|
160
|
+
return { ok: false, error: "the server address must be an http(s) URL, and not Anthropic's (that is the subscription)" };
|
|
161
|
+
if (!model)
|
|
162
|
+
return { ok: false, error: "name the model to run" };
|
|
163
|
+
const out = { baseUrl, model };
|
|
164
|
+
const token = typeof b.authToken === "string" ? b.authToken.trim() : "";
|
|
165
|
+
if (token)
|
|
166
|
+
out.authToken = token;
|
|
167
|
+
else if (saved?.authToken && saved.baseUrl === baseUrl)
|
|
168
|
+
out.authToken = saved.authToken;
|
|
169
|
+
const rawWindow = b.contextWindow;
|
|
170
|
+
if (rawWindow !== undefined && rawWindow !== null && String(rawWindow).trim() !== "") {
|
|
171
|
+
const w = parseContextWindow(String(rawWindow));
|
|
172
|
+
if (!w)
|
|
173
|
+
return { ok: false, error: "context window: a number of tokens such as 64000 or 64k" };
|
|
174
|
+
out.contextWindow = w;
|
|
175
|
+
}
|
|
176
|
+
return { ok: true, value: out };
|
|
177
|
+
}
|
|
178
|
+
/** `base` + `path`, without doubling or dropping a slash. */
|
|
179
|
+
export function joinUrl(base, p) {
|
|
180
|
+
return base.replace(/\/+$/, "") + "/" + p.replace(/^\/+/, "");
|
|
181
|
+
}
|
|
182
|
+
/** Model names out of an Ollama `/api/tags` or an OpenAI-style `/v1/models`
|
|
183
|
+
* answer; anything else is an empty list, never a throw. */
|
|
184
|
+
export function parseModelList(json) {
|
|
185
|
+
const j = (json ?? {});
|
|
186
|
+
const rows = Array.isArray(j.models) ? j.models : Array.isArray(j.data) ? j.data : [];
|
|
187
|
+
const names = rows
|
|
188
|
+
.map((r) => (r && typeof r === "object" ? (r.name ?? r.id) : null))
|
|
189
|
+
.filter((n) => typeof n === "string" && n.trim() !== "");
|
|
190
|
+
return [...new Set(names)].sort();
|
|
191
|
+
}
|
|
192
|
+
/** The smallest request that proves the whole chain: URL, key, model, and the
|
|
193
|
+
* Anthropic format itself. */
|
|
194
|
+
export function testRequestBody(model) {
|
|
195
|
+
return { model, max_tokens: 16, messages: [{ role: "user", content: "Reply with the single word: ok" }] };
|
|
196
|
+
}
|
|
197
|
+
/** A test that failed, in words that say what to change. */
|
|
198
|
+
export function testFailure(status, bodyText) {
|
|
199
|
+
let detail = "";
|
|
200
|
+
try {
|
|
201
|
+
const j = JSON.parse(bodyText);
|
|
202
|
+
detail = String(j?.error?.message ?? j?.error ?? j?.message ?? "").trim();
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
detail = bodyText.trim().slice(0, 200);
|
|
206
|
+
}
|
|
207
|
+
if (status === 401 || status === 403)
|
|
208
|
+
return "the server refused the API key" + (detail ? ` (${detail})` : "");
|
|
209
|
+
if (status === 404 && !/model/i.test(detail))
|
|
210
|
+
return "no Anthropic API at this address (/v1/messages not found) — an OpenAI-only server needs a proxy such as LiteLLM in front";
|
|
211
|
+
if (/model/i.test(detail))
|
|
212
|
+
return `the server does not know this model: ${detail}`;
|
|
213
|
+
return `the server answered ${status}` + (detail ? `: ${detail}` : "");
|
|
214
|
+
}
|
|
215
|
+
/** Did a /v1/messages answer look like an Anthropic message? */
|
|
216
|
+
export function looksLikeMessage(json) {
|
|
217
|
+
const j = json;
|
|
218
|
+
return !!j && j.type === "message" && Array.isArray(j.content);
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConfiguredBackend } from "./backend.js";
|
|
1
2
|
/**
|
|
2
3
|
* Global CLI config at ~/.shadok-ai/config.json (mode 600). The Telegram bot
|
|
3
4
|
* token is **per launch directory** (`tokens[cwd]`), so each instance you run
|
|
@@ -74,6 +75,14 @@ export interface Config {
|
|
|
74
75
|
* break-glass when every admin has lost their device (README).
|
|
75
76
|
*/
|
|
76
77
|
twoFactor?: Record<string, true>;
|
|
78
|
+
/**
|
|
79
|
+
* Per launch-directory model backend chosen at onboarding (or from the
|
|
80
|
+
* version menu), keyed by absolute cwd. Absent = the Claude subscription, or
|
|
81
|
+
* whatever ANTHROPIC_BASE_URL shadok was launched with. When present it is
|
|
82
|
+
* authoritative and REPLACES the environment wholesale — see src/backend.ts.
|
|
83
|
+
* May hold an API key, which is one more reason this file is 600.
|
|
84
|
+
*/
|
|
85
|
+
modelBackend?: Record<string, ConfiguredBackend>;
|
|
77
86
|
}
|
|
78
87
|
export declare const SHADOK_DIR: string;
|
|
79
88
|
export declare function loadConfig(): Config;
|
|
@@ -103,6 +112,10 @@ export declare function themeForCwd(cfg: Config, cwd: string): string | null;
|
|
|
103
112
|
* any unknown key clear the entry, so the instance falls back to the default.
|
|
104
113
|
*/
|
|
105
114
|
export declare function setThemeForCwd(cfg: Config, cwd: string, theme: string): void;
|
|
115
|
+
/** The model backend chosen for the instance launched in `cwd`, or null. */
|
|
116
|
+
export declare function backendForCwd(cfg: Config, cwd: string): ConfiguredBackend | null;
|
|
117
|
+
/** Set (or clear, with null) that backend and persist. */
|
|
118
|
+
export declare function setBackendForCwd(cfg: Config, cwd: string, b: ConfiguredBackend | null): void;
|
|
106
119
|
/** Is two-factor sign-in on for the instance launched in `cwd`? */
|
|
107
120
|
export declare function twoFactorForCwd(cfg: Config, cwd: string): boolean;
|
|
108
121
|
export declare function setTwoFactorForCwd(cfg: Config, cwd: string, on: boolean): void;
|
package/dist/config.js
CHANGED
|
@@ -74,6 +74,19 @@ export function setThemeForCwd(cfg, cwd, theme) {
|
|
|
74
74
|
delete cfg.cockpitTheme[cwd];
|
|
75
75
|
saveConfig(cfg);
|
|
76
76
|
}
|
|
77
|
+
/** The model backend chosen for the instance launched in `cwd`, or null. */
|
|
78
|
+
export function backendForCwd(cfg, cwd) {
|
|
79
|
+
return cfg.modelBackend?.[cwd] ?? null;
|
|
80
|
+
}
|
|
81
|
+
/** Set (or clear, with null) that backend and persist. */
|
|
82
|
+
export function setBackendForCwd(cfg, cwd, b) {
|
|
83
|
+
cfg.modelBackend ??= {};
|
|
84
|
+
if (b)
|
|
85
|
+
cfg.modelBackend[cwd] = b;
|
|
86
|
+
else
|
|
87
|
+
delete cfg.modelBackend[cwd];
|
|
88
|
+
saveConfig(cfg);
|
|
89
|
+
}
|
|
77
90
|
/** Is two-factor sign-in on for the instance launched in `cwd`? */
|
|
78
91
|
export function twoFactorForCwd(cfg, cwd) {
|
|
79
92
|
return cfg.twoFactor?.[cwd] === true;
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/first-agent.d.ts
CHANGED
|
@@ -29,6 +29,9 @@ export interface FirstAgentPlan {
|
|
|
29
29
|
export declare function firstAgentPlan(input: {
|
|
30
30
|
channelCount: number;
|
|
31
31
|
authState: AuthState;
|
|
32
|
+
/** The whole instance runs on a custom model backend (src/backend.ts): its
|
|
33
|
+
* agents carry their own token, so the Claude sign-in is irrelevant. */
|
|
34
|
+
customBackend?: boolean;
|
|
32
35
|
}): FirstAgentPlan;
|
|
33
36
|
/**
|
|
34
37
|
* What the cockpit is allowed to SAY while it has no agent to show.
|
|
@@ -81,6 +84,9 @@ export declare function ensureFirstAgent(deps: {
|
|
|
81
84
|
port: number;
|
|
82
85
|
cookie?: string;
|
|
83
86
|
cwd: string;
|
|
87
|
+
/** The instance runs on a custom model backend (src/backend.ts) — decided by
|
|
88
|
+
* the server, which owns both the env and the saved choice. */
|
|
89
|
+
customBackend?: boolean;
|
|
84
90
|
/** Called with the new session id once the server reports it ready. */
|
|
85
91
|
onReady: (sessionId: string, name: string) => void;
|
|
86
92
|
}): Promise<FirstAgentPlan["reason"]>;
|
package/dist/first-agent.js
CHANGED
|
@@ -26,6 +26,8 @@ export const FIRST_AGENT_NAME = "general";
|
|
|
26
26
|
export function firstAgentPlan(input) {
|
|
27
27
|
if (input.channelCount > 0)
|
|
28
28
|
return { spawn: false, reason: "channels-exist" };
|
|
29
|
+
if (input.customBackend)
|
|
30
|
+
return { spawn: true, reason: "first-boot", name: FIRST_AGENT_NAME, profile: BOSS_PROFILE_NAME };
|
|
29
31
|
// "unknown" is not "signed in": the probe failed. Waiting costs one boot,
|
|
30
32
|
// guessing costs an agent with no credentials — the zombie shape (invariant 27).
|
|
31
33
|
if (input.authState !== "signed-in")
|
|
@@ -71,9 +73,14 @@ let inFlight = false;
|
|
|
71
73
|
export async function ensureFirstAgent(deps) {
|
|
72
74
|
if (inFlight)
|
|
73
75
|
return "channels-exist";
|
|
76
|
+
// Instance-wide only: the lead agent's profile is not consulted, so a custom
|
|
77
|
+
// backend reaches it through the instance's source (env or saved choice).
|
|
78
|
+
const customBackend = deps.customBackend ?? false;
|
|
74
79
|
const plan = firstAgentPlan({
|
|
75
80
|
channelCount: loadChannels().length,
|
|
76
|
-
|
|
81
|
+
// No probe when it cannot change the answer (~850ms, and a process spawn).
|
|
82
|
+
authState: customBackend ? "unknown" : (await authStatus()).state,
|
|
83
|
+
customBackend,
|
|
77
84
|
});
|
|
78
85
|
if (!plan.spawn) {
|
|
79
86
|
settleFirstAgent(plan.reason);
|
package/dist/first-agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"first-agent.js","sourceRoot":"","sources":["../src/first-agent.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAkB,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD;;;;;;;;;;;GAWG;AAEH,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAS1C;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"first-agent.js","sourceRoot":"","sources":["../src/first-agent.ts"],"names":[],"mappings":"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"}
|