aweshare 0.4.7 → 0.4.9
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.ai.md +346 -0
- package/README.md +23 -11
- package/apps/agent/dist/cli.js +45 -32
- package/apps/agent/dist/cli.js.map +1 -1
- package/apps/agent/dist/hub.d.ts +6 -3
- package/apps/agent/dist/hub.js.map +1 -1
- package/apps/agent/dist/table.d.ts +7 -4
- package/apps/agent/dist/table.js +8 -5
- package/apps/agent/dist/table.js.map +1 -1
- package/apps/hub/dist/admin.js +11 -9
- package/apps/hub/dist/admin.js.map +1 -1
- package/apps/hub/dist/cli.js +80 -37
- package/apps/hub/dist/cli.js.map +1 -1
- package/apps/hub/dist/config.d.ts +10 -1
- package/apps/hub/dist/config.js +25 -15
- package/apps/hub/dist/config.js.map +1 -1
- package/apps/hub/dist/db.d.ts +7 -4
- package/apps/hub/dist/db.js +17 -5
- package/apps/hub/dist/db.js.map +1 -1
- package/apps/hub/dist/local.d.ts +3 -3
- package/apps/hub/dist/local.js +7 -7
- package/apps/hub/dist/local.js.map +1 -1
- package/apps/hub/dist/server.js +10 -2
- package/apps/hub/dist/server.js.map +1 -1
- package/apps/hub/dist/table.d.ts +8 -4
- package/apps/hub/dist/table.js +9 -6
- package/apps/hub/dist/table.js.map +1 -1
- package/package.json +1 -1
package/README.ai.md
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# aweshare Bootstrap Protocol
|
|
2
|
+
|
|
3
|
+
This document is for AI coding agents. Help the user install and configure `aweshare`, a local-first AI capability relay: producers share local Ollama/vLLM or authorized OpenAI/Anthropic backends through a self-hosted hub; consumers point standard OpenAI/Anthropic SDKs at the hub and call models by `namespace/alias`.
|
|
4
|
+
|
|
5
|
+
## Long-Running Services and Secrets
|
|
6
|
+
|
|
7
|
+
Unlike ordinary CLIs, aweshare's two core processes are **long-running services**:
|
|
8
|
+
|
|
9
|
+
- Never launch them from this agent — not `aweshare hub serve`, not `aweshare producer start` (foreground blocks the session; even `--background` starts a shared service, which is the user's call). The same applies to deploying the hub as a container (`docker run` / `docker compose up` of `ghcr.io/wehuman01/aweshare`). Give the user the commands and let them run them in their own terminal.
|
|
10
|
+
- Upstream API keys live only in `~/.aweshare/secrets.json` (0600) on the producer's machine. Never print them, never echo them into logs or chat. `aweshare producer config show` redacts them.
|
|
11
|
+
- Some tokens are printed exactly once (the hub admin token from `hub init`, an `asc_` consumer token from `consumer join`, invite codes from `hub invite`). Prefer having the user run those commands so the secret lands in their terminal; if you must run one, tell the user to save the value immediately.
|
|
12
|
+
- Before helping share an OpenAI/Anthropic backend, state the compliance boundary: relaying a personal-subscription key (coding plans included) to third parties likely violates the upstream's terms; self-hosted open models have no such issue. When in doubt, don't share.
|
|
13
|
+
|
|
14
|
+
## Language Behavior
|
|
15
|
+
|
|
16
|
+
- Reply in the user's language when possible.
|
|
17
|
+
- If the user asks in Chinese, continue in Chinese.
|
|
18
|
+
- If the user asks in English, continue in English.
|
|
19
|
+
|
|
20
|
+
## Step 0: Identify the user's role
|
|
21
|
+
|
|
22
|
+
aweshare has three roles with different setups. Ask which one applies (more than one is possible):
|
|
23
|
+
|
|
24
|
+
- **Hub operator** — runs the public hub on an always-on server; mints invite codes.
|
|
25
|
+
- **Producer** — shares models from their own machine through a hub.
|
|
26
|
+
- **Consumer** — calls shared models with a standard SDK from any machine.
|
|
27
|
+
|
|
28
|
+
Then follow Steps 1–2 (identical for everyone) and the matching path in Step 4.
|
|
29
|
+
|
|
30
|
+
## Step 1: Install aweshare CLI
|
|
31
|
+
|
|
32
|
+
### Prerequisites
|
|
33
|
+
|
|
34
|
+
- Node >= 22 (`node --version`)
|
|
35
|
+
|
|
36
|
+
If Node.js is missing, tell the user to install it from https://nodejs.org/.
|
|
37
|
+
|
|
38
|
+
### Steps
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install -g aweshare
|
|
42
|
+
aweshare -v
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Expected output: a version number (e.g. `0.4.8`).
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Step 2: Install the aweshare skill
|
|
50
|
+
|
|
51
|
+
Install the skill so the agent can operate aweshare in this and future sessions. Choose one of the following options.
|
|
52
|
+
|
|
53
|
+
### Option A: Via aweskill (recommended if aweskill is available)
|
|
54
|
+
|
|
55
|
+
Use this option if the user already has aweskill installed, or is willing to install it. This gives full skill management — install, update, projection, backup.
|
|
56
|
+
|
|
57
|
+
#### Prerequisites
|
|
58
|
+
|
|
59
|
+
- Node.js >= 20 (`node --version`) — required by aweskill
|
|
60
|
+
- npm available (`npm --version`)
|
|
61
|
+
|
|
62
|
+
#### Steps
|
|
63
|
+
|
|
64
|
+
##### A1. Install aweskill (if not already installed)
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install -g aweskill
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
##### A2. Initialize the aweskill central store (if not already done)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
aweskill store init
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
##### A3. Install the aweshare skill from GitHub
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
aweskill install wehuman01/aweshare
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
##### A4. Identify the current agent
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
aweskill agent supported
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Look for lines marked with `✓`. Common agent ids: `claude-code`, `cursor`, `codex`, `gemini-cli`, `windsurf`, `opencode`, `qwen-code`.
|
|
89
|
+
|
|
90
|
+
If you cannot determine the agent id, ask the user.
|
|
91
|
+
|
|
92
|
+
##### A5. Project the aweshare skill to this agent
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
aweskill agent add skill aweshare --global --agent <agent-id>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
##### A6. Verify
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
aweskill agent list --global --agent <agent-id>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Expected: `aweshare` shows as `linked`.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### Option B: Direct copy (no aweskill needed)
|
|
109
|
+
|
|
110
|
+
Use this option if the user does not have aweskill. This copies the SKILL.md file directly into the agent's skill directory.
|
|
111
|
+
|
|
112
|
+
#### Prerequisites
|
|
113
|
+
|
|
114
|
+
- `curl` or `wget` available
|
|
115
|
+
|
|
116
|
+
#### Steps
|
|
117
|
+
|
|
118
|
+
##### B1. Identify the current agent's skill directory
|
|
119
|
+
|
|
120
|
+
Determine which agent is running and its global skill directory:
|
|
121
|
+
|
|
122
|
+
| Agent | Skill directory |
|
|
123
|
+
|---|---|
|
|
124
|
+
| Claude Code | `~/.claude/skills/aweshare/` |
|
|
125
|
+
| Codex | `~/.codex/skills/aweshare/` |
|
|
126
|
+
| Cursor | `.cursor/skills/aweshare/` (project-level) |
|
|
127
|
+
| Gemini CLI | `~/.gemini/skills/aweshare/` |
|
|
128
|
+
| Windsurf | `~/.windsurf/skills/aweshare/` |
|
|
129
|
+
| OpenCode | `~/.opencode/skills/aweshare/` |
|
|
130
|
+
| Qwen Code | `~/.qwen/skills/aweshare/` |
|
|
131
|
+
|
|
132
|
+
If the agent is not in this list, ask the user where to place the skill file.
|
|
133
|
+
|
|
134
|
+
##### B2. Download and place SKILL.md
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
mkdir -p <skill-directory>
|
|
138
|
+
curl -fsSL https://raw.githubusercontent.com/wehuman01/aweshare/main/resources/skills/aweshare/SKILL.md -o <skill-directory>/SKILL.md
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Replace `<skill-directory>` with the path from step B1.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Step 3: Detect the current setup (read-only, safe to run)
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
aweshare producer config path # where config.toml / secrets.json live
|
|
149
|
+
aweshare producer config show # config with token + secrets redacted
|
|
150
|
+
aweshare producer doctor --status # background producer state + recent log, instant, no network
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Report the findings to the user:
|
|
154
|
+
|
|
155
|
+
- No config at `~/.aweshare/config.toml` — fresh machine, continue with Step 4.
|
|
156
|
+
- Config present — a producer is (or was) set up here; `doctor --status` tells whether a background instance is running.
|
|
157
|
+
- Full `aweshare producer doctor` (without `--status`) adds network probes of the configured backends and the hub plus the recent log. Diagnostic traffic only — run it freely after setup, fix the first FAIL, re-run until green.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Step 4: Role-specific setup
|
|
162
|
+
|
|
163
|
+
Interactive or service-starting commands stay in the user's terminal. Everything else you can do.
|
|
164
|
+
|
|
165
|
+
### Producer — shares models from this machine
|
|
166
|
+
|
|
167
|
+
1. Join the hub (either works):
|
|
168
|
+
- The user redeems their invite code — you may run it, it is non-interactive and writes `~/.aweshare/config.toml` + empty `secrets.json`:
|
|
169
|
+
```bash
|
|
170
|
+
aweshare producer join --hub https://hub.example.com --code asi_...
|
|
171
|
+
```
|
|
172
|
+
- Or the operator handed over a token directly:
|
|
173
|
+
```bash
|
|
174
|
+
aweshare producer init --hub https://hub.example.com --token asp_...
|
|
175
|
+
```
|
|
176
|
+
2. Edit `config.toml` yourself: set `hubUrl`/`token`, define `[[backends]]` (protocol + baseUrl convention: `openai` baseUrl includes `/v1`; `anthropic` excludes `/v1`; `responses` includes the version path) and `[[offerings]]` (alias namespace = producer name, one upstream model each).
|
|
177
|
+
3. Upstream keys go into `secrets.json` under the backend's `keyRef` name. **Do not ask the user to paste keys into chat** — ask them to edit `secrets.json` themselves, then continue.
|
|
178
|
+
4. Verify:
|
|
179
|
+
```bash
|
|
180
|
+
aweshare producer doctor # instance, config, backend probes, hub — fix the first FAIL
|
|
181
|
+
```
|
|
182
|
+
5. Tell the user (do not run):
|
|
183
|
+
> Run `aweshare producer start` in your terminal — or `aweshare producer start --background` to detach it (logs to `~/.aweshare/producer.log`). Stopping later: `aweshare producer stop`.
|
|
184
|
+
|
|
185
|
+
After config edits on a running producer, `aweshare producer reload` applies the new catalog without a restart (both processes also stat-poll their config every 2s and hot-apply valid edits; `hubUrl`/`token` still need a restart).
|
|
186
|
+
|
|
187
|
+
### Consumer — calls shared models
|
|
188
|
+
|
|
189
|
+
1. Have the user redeem their invite in their own terminal — it prints the `asc_` token exactly once:
|
|
190
|
+
```bash
|
|
191
|
+
aweshare consumer join --hub https://hub.example.com --code asi_...
|
|
192
|
+
```
|
|
193
|
+
(No aweshare installed? One curl works: `POST https://hub.example.com/invites/v1/redeem` with `{"code":"asi_..."}`.)
|
|
194
|
+
2. Configure the tool with the printed env vars:
|
|
195
|
+
```bash
|
|
196
|
+
# Claude Code / Anthropic SDK — the key is the asc_ consumer key, NOT any upstream x-api-key
|
|
197
|
+
export ANTHROPIC_BASE_URL=https://hub.example.com
|
|
198
|
+
export ANTHROPIC_AUTH_TOKEN=asc_...
|
|
199
|
+
claude --model peng/sonnet
|
|
200
|
+
|
|
201
|
+
# OpenAI SDK / Codex
|
|
202
|
+
export OPENAI_BASE_URL=https://hub.example.com/v1
|
|
203
|
+
export OPENAI_API_KEY=asc_...
|
|
204
|
+
```
|
|
205
|
+
3. Discovery (read-only, safe to run once you have the token):
|
|
206
|
+
```bash
|
|
207
|
+
aweshare consumer list --hub https://hub.example.com --token asc_...
|
|
208
|
+
```
|
|
209
|
+
Every producer, alias, protocol, status, caps, live occupancy and remaining daily tokens.
|
|
210
|
+
4. A first smoke-test request (`curl ... /v1/chat/completions` with `"ping"`) is a real model call — only run it when the user asks.
|
|
211
|
+
|
|
212
|
+
Remind the consumer: prompts and responses transit the hub in plaintext — only use a hub they trust. If Claude Code ignores env config, a stale OAuth login is overriding it — switch with `/login`.
|
|
213
|
+
|
|
214
|
+
### Hub operator — runs the hub on a server
|
|
215
|
+
|
|
216
|
+
1. Choose npm or Docker (Docker is the better default on a VPS). Both `hub serve` and container deployment run in the **user's** terminal:
|
|
217
|
+
```bash
|
|
218
|
+
# npm
|
|
219
|
+
npm install -g aweshare
|
|
220
|
+
aweshare hub init # data in ~/.aweshare-hub; prints the admin token ONCE — save it
|
|
221
|
+
aweshare hub serve # listens on :8787; put Caddy/nginx TLS in front
|
|
222
|
+
|
|
223
|
+
# docker
|
|
224
|
+
docker run -d --name aweshare-hub --restart unless-stopped \
|
|
225
|
+
-p 127.0.0.1:8787:8787 -v "$PWD/data:/data" ghcr.io/wehuman01/aweshare:latest
|
|
226
|
+
docker exec aweshare-hub aweshare hub init
|
|
227
|
+
```
|
|
228
|
+
2. After `hub init` (wherever it ran), mint invites — you may run these:
|
|
229
|
+
```bash
|
|
230
|
+
aweshare hub invite --name peng # bound producer code → asi_...
|
|
231
|
+
aweshare hub invite --count 10 # unbound batch, name+email at redeem
|
|
232
|
+
aweshare hub invite --role consumer --name alice # bound consumer code → asi_...
|
|
233
|
+
```
|
|
234
|
+
Consumers redeem codes themselves (`aweshare consumer join`); producers via `aweshare producer join`.
|
|
235
|
+
3. Administering a remote hub: run admin commands on the server (`ssh` + CLI, or `docker exec aweshare-hub aweshare hub ...`), or locally with `AWESHARE_HUB_URL=https://<hub-host>` plus the admin-token file in a local data dir — otherwise they fail with "no admin token" / connection refused.
|
|
236
|
+
4. Guardrails you can tune on request: per-consumer `aweshare hub limits NAME [--rps N] [--tpm N] [--max-total-tokens N] ...`, suspension `aweshare hub revoke --id N` / `restore --id N` (reversible, invite-keyed).
|
|
237
|
+
5. Security notes for the operator: keep :8787 off the public internet behind TLS; a redeemed consumer key can call **every** offering on the hub; tokens/codes stored plaintext-recoverable (`hub list --reveal` / `--token`) means a data-dir leak exposes identities — guard it.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Step 5: Verify and tune (safe to run)
|
|
242
|
+
|
|
243
|
+
After setup:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
aweshare producer doctor # full diagnosis, ordered to find the first failing link
|
|
247
|
+
aweshare producer list # registered offerings, live status, caps, occupancy, config drift
|
|
248
|
+
aweshare hub list producers # roster + last seen (admin context only)
|
|
249
|
+
aweshare hub usage # who used how much, aggregated per consumer × model
|
|
250
|
+
aweshare self-update --check # installed vs npm latest
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
On the user's request you may also:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
aweshare hub limits alice --tpm 60000 --max-total-tokens 5000000 # cap a consumer
|
|
257
|
+
aweshare hub usage --details --since 24h # per-request log
|
|
258
|
+
aweshare producer reload # apply config edits without restart
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Useful commands
|
|
262
|
+
|
|
263
|
+
Read-only commands (safe to run in agent):
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
aweshare -v # installed version
|
|
267
|
+
aweshare producer config path # config/secrets locations
|
|
268
|
+
aweshare producer config show # config, secrets redacted
|
|
269
|
+
aweshare producer doctor [--status] # diagnosis (with --status: instant, no network)
|
|
270
|
+
aweshare producer list [--json] # this producer's offerings + drift
|
|
271
|
+
aweshare producer usage [...] # who used this producer's models
|
|
272
|
+
aweshare consumer list --hub URL --token asc_... # hub catalog view
|
|
273
|
+
aweshare hub list [invites|producers|consumers] [--json] # rosters / invite lifecycle
|
|
274
|
+
aweshare hub limits NAME # bare call views current overrides
|
|
275
|
+
aweshare hub usage [--details] [--group-by consumer|alias] [--since 7d]
|
|
276
|
+
aweshare self-update --check # versions only
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Local changes (run on user request):
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
aweshare producer init [--hub URL] [--token asp_...] # write templates; no-op if they exist
|
|
283
|
+
aweshare producer join --hub URL --code asi_... # redeem a producer invite into the config
|
|
284
|
+
aweshare producer config edit # open config.toml in $EDITOR
|
|
285
|
+
aweshare producer reload # re-read config + re-register offerings
|
|
286
|
+
aweshare hub init # create data dir + admin token (printed once!)
|
|
287
|
+
aweshare hub invite [--role producer|consumer] ... # mint one-time codes (printed once)
|
|
288
|
+
aweshare hub limits NAME [--rps N] [--burst N] [--max-concurrent N] [--tpm N] [--max-total-tokens N] [--clear]
|
|
289
|
+
aweshare hub revoke --id N / restore --id N # reversible suspension, by invite handle
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
User-only commands (long-running, TTY-confirming, or one-time-secret-printing):
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
aweshare producer start [--background] / stop # the relay service
|
|
296
|
+
aweshare hub serve # the hub
|
|
297
|
+
aweshare consumer join --hub URL --code asi_... # prints the asc_ token once
|
|
298
|
+
aweshare self-update # y/n confirm; non-interactive: npm install -g aweshare
|
|
299
|
+
docker run / docker compose up # hub deployment
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Safety Rules
|
|
303
|
+
|
|
304
|
+
- Never start or stop the long-running services (`hub serve`, `producer start`/`stop`, hub containers) inside the agent — deployment is the user's call. Inspect a detached producer with `producer doctor --status`.
|
|
305
|
+
- Never read or print `secrets.json` values; use `producer config show` (redacted) when showing config. Never ask the user to paste an upstream API key into chat — point them at the file instead.
|
|
306
|
+
- Commands that print a secret exactly once (`hub init`, `consumer join`, `hub invite`) are best run by the user; if you run one, flag the value as save-now.
|
|
307
|
+
- State the trust boundary before anyone joins a hub: consumer traffic transits the hub in plaintext; a redeemed consumer key can call every offering. State the compliance boundary before anyone shares a key: personal-subscription keys forwarded to third parties likely violate upstream terms — the producer bears the consequences.
|
|
308
|
+
- Read state through the CLI (`config show`, `list`, `doctor`); do not hand-edit SQLite or pidfiles. Config TOML edits are fine — they hot-reload.
|
|
309
|
+
- Offering aliases must be `namespace/name`, lowercase, namespace matching the producer token's name. One offering = one upstream model; the old `maxConcurrency` key fails validation with a rename hint (use `maxConcurrencyPerUser`).
|
|
310
|
+
- If any command fails, report the exact command and error message (codes like `403 HUB_FULL`, `409 INVITE_ROLE_MISMATCH`, `401 TOKEN_REVOKED` carry meaning — pass them through). Do not silently retry.
|
|
311
|
+
|
|
312
|
+
## Final Step
|
|
313
|
+
|
|
314
|
+
After setup, tell the user to invoke skills (`/` in Claude Code, `$` in Codex, or the equivalent in other agents) and check if `aweshare` appears in the list. If it does, the skill is ready to use immediately. If not, the user should restart the agent.
|
|
315
|
+
|
|
316
|
+
> aweshare is installed and configured. Invoke skills (type `/` or `$` depending on your agent) and look for `aweshare` — if it appears, you're good to go. If not, restart the agent. Then you can ask me things like:
|
|
317
|
+
>
|
|
318
|
+
> - "Share my local Ollama qwen2.5:7b as peng/qwen2.5.7b."
|
|
319
|
+
> - "Why is my producer offline?" (I'll run `producer doctor` and fix the first FAIL.)
|
|
320
|
+
> - "Cap alice at 60k tokens per minute."
|
|
321
|
+
> - "Who used my models this week?"
|
|
322
|
+
|
|
323
|
+
If the user is speaking Chinese, use this version instead:
|
|
324
|
+
|
|
325
|
+
> aweshare 已安装并配置完成。请调用 skills(输入 `/` 或 `$`,取决于你的 agent),看看列表中是否出现了 `aweshare`。如果出现了,说明已就绪可以直接使用。如果没有,请重启 agent 后再试。然后你可以继续问我,例如:
|
|
326
|
+
>
|
|
327
|
+
> - “把我本地的 Ollama qwen2.5:7b 以 peng/qwen2.5.7b 共享出去。”
|
|
328
|
+
> - “我的 producer 为什么掉线了?”(我会跑 `producer doctor` 并修复第一个 FAIL。)
|
|
329
|
+
> - “给 alice 限流到每分钟 6 万 token。”
|
|
330
|
+
> - “这周谁用了我的模型?”
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Next Steps
|
|
335
|
+
|
|
336
|
+
### Community hub — no server of your own
|
|
337
|
+
|
|
338
|
+
The project's developer runs an invite-based community hub at https://aweshare.wehuman.top. If the user wants to share or consume without operating a hub, guide them through the tutorial: https://github.com/wehuman01/aweshare/blob/main/docs/community-hub/README.md — requesting an invite at peng@wehuman.top, joining as producer or consumer, and the first connection. State the trust rule before they join: their traffic transits that hub in plaintext.
|
|
339
|
+
|
|
340
|
+
### awewarm — keep shared subscription windows warm
|
|
341
|
+
|
|
342
|
+
If the user shares a coding-plan-backed endpoint through aweshare, pair it with [awewarm](https://github.com/wehuman01/awewarm): it schedules one minimal request per window so the underlying subscription quota never cools down mid-day. Its agent doc lives at https://github.com/wehuman01/awewarm/blob/main/README.ai.md.
|
|
343
|
+
|
|
344
|
+
### aweswitch — agent profile switching
|
|
345
|
+
|
|
346
|
+
Consumers who juggle several providers can use [aweswitch](https://github.com/Webioinfo01/aweswitch) to switch Claude Code / Codex / OpenCode profiles; pointing one of those profiles at an aweshare hub is just another provider entry.
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<a href="https://ko-fi.com/mugpeng"><img src="https://img.shields.io/badge/Ko--fi-Buy%20me%20a%20coffee-FF5E5B?style=flat-square&logo=ko-fi&logoColor=white" alt="Ko-fi"></a>
|
|
15
15
|
</p>
|
|
16
16
|
<p>
|
|
17
|
-
<a href="https://github.com/wehuman01/aweshare-source/releases"><img src="https://img.shields.io/badge/version-0.4.
|
|
17
|
+
<a href="https://github.com/wehuman01/aweshare-source/releases"><img src="https://img.shields.io/badge/version-0.4.9-7C3AED?style=flat-square" alt="Version"></a>
|
|
18
18
|
<a href="https://github.com/wehuman01/aweshare"><img src="https://img.shields.io/badge/node-%E2%89%A522-0EA5E9?style=flat-square" alt="Node"></a>
|
|
19
19
|
<a href="https://github.com/wehuman01/aweshare/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-proprietary-E34F26?style=flat-square" alt="License"></a>
|
|
20
20
|
<a href="https://www.npmjs.com/package/aweshare"><img src="https://img.shields.io/badge/npm-aweshare-7C3AED?style=flat-square" alt="npm package"></a>
|
|
@@ -69,7 +69,19 @@ Consumer (standard SDK, zero changes) Producer side
|
|
|
69
69
|
|
|
70
70
|
Published as [`aweshare`](https://www.npmjs.com/package/aweshare) on npm (requires Node ≥ 22) and as a Docker image (`ghcr.io/wehuman01/aweshare`). No clone needed.
|
|
71
71
|
|
|
72
|
-
###
|
|
72
|
+
### Let an AI agent set it up
|
|
73
|
+
|
|
74
|
+
Working in Claude Code, Codex, or another coding agent? Tell it:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
Read https://github.com/wehuman01/aweshare/blob/main/README.ai.md and follow it to install and configure aweshare.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The agent installs the CLI and the skill, asks whether you are a hub operator, producer, or consumer, and does everything that is safe to automate — editing configs, minting invites, running `producer doctor`. Steps that print one-time tokens (`hub init`, `consumer join`) or start long-running services (`hub serve`, `producer start`) stay in your terminal. After setup you can ask things like "share my local Ollama model as peng/qwen2.5.7b", "why is my producer offline?", or "who used my models this week?".
|
|
81
|
+
|
|
82
|
+
### Manual setup
|
|
83
|
+
|
|
84
|
+
#### 1. Start the hub (operator, one VPS)
|
|
73
85
|
|
|
74
86
|
**npm** (simplest):
|
|
75
87
|
|
|
@@ -79,7 +91,7 @@ aweshare hub init # data in ~/.aweshare-hub; prints the admin token — s
|
|
|
79
91
|
aweshare hub serve # listens on :8787 (put Caddy/nginx TLS in front)
|
|
80
92
|
```
|
|
81
93
|
|
|
82
|
-
The hub can also host models itself — no producer machine needed. Add `[[backends]]`/`[[offerings]]` sections to `~/.aweshare-hub/config.toml` (same format as a producer's config, alias namespace `hub/…`), put the upstream keys in `~/.aweshare-hub/secrets.json`, and run:
|
|
94
|
+
The hub can also host models itself — no producer machine needed. Add `[[backends]]`/`[[offerings]]` sections to `~/.aweshare-hub/config.produce.toml` (same format as a producer's config, alias namespace `hub/…`), put the upstream keys in `~/.aweshare-hub/secrets.json`, and run:
|
|
83
95
|
|
|
84
96
|
```bash
|
|
85
97
|
aweshare hub produce # serve + the hub's own models; consumers dial hub/<name> like any offering
|
|
@@ -132,7 +144,7 @@ The producer's `name` becomes their alias namespace (the `peng/` in `peng/gpt-4o
|
|
|
132
144
|
|
|
133
145
|
The operator owns admission: one-time invite codes for both roles, the only admission path (every identity carries its invite handle for its whole lifecycle). A redeemed consumer key may call **every** offering on the hub — if you let someone in, they can use what is shared. Guardrails: per-consumer `hub limits` (rate, concurrency, token budgets), per-offering caps (`maxConcurrentUsers`, `dailyTokens`) and `hub revoke` suspension, all enforced by the hub.
|
|
134
146
|
|
|
135
|
-
|
|
147
|
+
#### 2. Producer first run (in this order)
|
|
136
148
|
|
|
137
149
|
```bash
|
|
138
150
|
npm install -g aweshare # ⓪ once, on the producer machine (Node ≥ 22)
|
|
@@ -153,7 +165,7 @@ aweshare producer start # foreground; add --background to detach it
|
|
|
153
165
|
# and stopped with 'aweshare producer stop'
|
|
154
166
|
```
|
|
155
167
|
|
|
156
|
-
|
|
168
|
+
#### 3. Consumer first run (in this order)
|
|
157
169
|
|
|
158
170
|
```bash
|
|
159
171
|
# ⓪ redeem your invite code (skip if the operator handed you an asc_ key directly)
|
|
@@ -278,11 +290,11 @@ Honest limits: token-based caps count what upstreams report — Ollama streams r
|
|
|
278
290
|
| `GET /v1/catalog` | every offering on the hub — producer, alias, protocol, status, the per-offering caps, live in-flight occupancy (`activeUsers`/`activeRequests`) and today's used/remaining tokens (discovery view for `aweshare consumer list`) |
|
|
279
291
|
| `GET /healthz` | liveness |
|
|
280
292
|
| `GET /admin/v1/offerings` | registered offerings with live status, caps, in-flight occupancy and today's used tokens — admin sees everything, a producer token only its own slice (`aweshare producer list`) |
|
|
281
|
-
| `/admin/v1/*` | token/limit/usage management (admin or producer token) · usage: `GET /admin/v1/usage` (newest-first log) and `GET /admin/v1/usage/summary` (`group=consumer\|alias`, `since=30m\|12h\|7d`, `consumer`/`producer`/`alias` filters; every role sees its own slice) · consumer limit overrides: `GET`/`PUT`/`DELETE /admin/v1/consumers/{name}/limits` (admin only) |
|
|
293
|
+
| `/admin/v1/*` | token/limit/usage management (admin or producer token) · usage: `GET /admin/v1/usage` (newest-first log) and `GET /admin/v1/usage/summary` (`group=consumer-alias\|consumer\|alias`, `since=30m\|12h\|7d\|all`, default 7d, `consumer`/`producer`/`alias` filters; every role sees its own slice) · consumer limit overrides: `GET`/`PUT`/`DELETE /admin/v1/consumers/{name}/limits` (admin only) |
|
|
282
294
|
|
|
283
295
|
Error semantics: `401` invalid key · `401 TOKEN_REVOKED` suspended token (ask the operator to restore it) · `403 HUB_FULL` producer capacity reached · `404` unknown alias · `400 PROTOCOL_MISMATCH` protocol/alias mismatch · `429` rate limit, TPM or producer concurrency cap (`PRODUCER_MAX_USERS` = distinct-consumer cap; `QUOTA_EXCEEDED` = lifetime or daily token budget hit) · `502` upstream/tunnel failure (upstream 4xx/5xx passes through verbatim) · `503` producer offline / backend degraded · `504` timeout. Errors carry `{error:{code,message,requestId}}`; the requestId spans both sides' logs.
|
|
284
296
|
|
|
285
|
-
Usage metering: one row per request (alias, real model, status, duration, byte counts, best-effort token counts), **zero content stored**.
|
|
297
|
+
Usage metering: one row per request (alias, real model, status, duration, byte counts, best-effort token counts), **zero content stored**. `aweshare hub usage` (and `aweshare producer usage` on a producer's machine, scoped to its own models) answers "who used how much" by default: server-side aggregation on the hub's SQLite, one row per consumer × model — a person's rows stay together, busiest person and busiest model first — with request/error counts, best-effort token totals, an explicit unknown-token count (streaming backends that report no counts) and mean duration. The window defaults to 7 days and is printed with the table (`--since 30m\|12h\|7d\|…\|all`); `--group-by consumer` rolls up to per-person totals, `--group-by alias` to per-model totals. `--details` switches to the per-request log (`GET /admin/v1/usage`; admin sees everything, producers and consumers their own slice, rows carry the consumer/producer names).
|
|
286
298
|
|
|
287
299
|
## Command reference
|
|
288
300
|
|
|
@@ -294,11 +306,11 @@ Both sides at a glance — details in the sections above.
|
|
|
294
306
|
|---|---|
|
|
295
307
|
| `aweshare hub init` | create data dir + admin token (printed once) |
|
|
296
308
|
| `aweshare hub serve [--host H] [--port N]` | run the hub |
|
|
297
|
-
| `aweshare hub produce [--host H] [--port N]` | run the hub with its own models attached — same as `serve`; the `[[backends]]`/`[[offerings]]` sections of
|
|
309
|
+
| `aweshare hub produce [--host H] [--port N]` | run the hub with its own models attached — same as `serve`; the `[[backends]]`/`[[offerings]]` sections of `config.produce.toml` become `hub/…` offerings served in-process (keys in the data dir's secrets.json; edits hot-reload) |
|
|
298
310
|
| `aweshare hub invite [--role producer\|consumer] [--name NAME] [--count N] [--expires-in D]` | mint one-time invite codes (`asi_…`, printed once, expire after 7 d by default; re-view with `list --reveal`); producer codes: bound (`--name`) or unbound (name + email at redeem, `--count` batches); consumer codes: always bound to one name |
|
|
299
311
|
| `aweshare hub list [invites\|producers\|consumers] [--reveal] [--token] [--json]` | read hub state: invite lifecycle (ROLE + who redeemed, `--reveal` codes, `--token` the minted tokens), or the producer/consumer rosters with status and last seen |
|
|
300
312
|
| `aweshare hub limits NAME [--rps N] [--burst N] [--max-concurrent N] [--tpm N] [--max-total-tokens N] [--clear] [--json]` | show, merge or clear one consumer's limit overrides (unset keys keep the hub-wide defaults) |
|
|
301
|
-
| `aweshare hub usage [
|
|
313
|
+
| `aweshare hub usage [--details] [--consumer NAME] [--producer NAME] [--alias ns/model] [--group-by consumer-alias\|consumer\|alias] [--since 7d\|all] [--limit N] [--json]` | who used how much (default): aggregate per consumer × model, a person's rows together, busiest first — requests, errors, rate, best-effort token totals, unknown-token count, mean duration; window defaults to 7d and is printed with the table · `--details`: per-request log, newest first, zero content stored, each row naming its consumer |
|
|
302
314
|
| `aweshare hub revoke --id N` · `aweshare hub restore --id N` | kill an invite / undo — a redeemed code suspends the producer it minted, restore revives both |
|
|
303
315
|
|
|
304
316
|
Token issuance runs through invites (both roles). `limits` and `usage` are thin wrappers over the admin REST API (`/admin/v1/*`, see Endpoints and errors) — curl works too.
|
|
@@ -314,7 +326,7 @@ Token issuance runs through invites (both roles). `limits` and `usage` are thin
|
|
|
314
326
|
| `aweshare producer config path` · `config show` · `config edit` | locate / inspect (secrets redacted) / edit the config |
|
|
315
327
|
| `aweshare producer doctor [--status]` | diagnose end to end: background instance, config, backend probes, hub (including how many of your offerings are registered), recent log (`--status` skips the network probes for an instant answer) |
|
|
316
328
|
| `aweshare producer list [--json]` | what this producer has registered on the hub — alias, protocol, live status, caps, live occupancy (`IN USE`, distinct consumers in flight right now), today's token use — plus the local background instance state and drift against config.toml (hubUrl/token come from config.toml) |
|
|
317
|
-
| `aweshare producer usage [
|
|
329
|
+
| `aweshare producer usage [--details] [--consumer NAME] [--alias ns/model] [--group-by consumer-alias\|consumer\|alias] [--since 7d\|all] [--limit N] [--json]` | who used this producer's models (the producer token scopes the hub's metering to its own slice): aggregate per consumer × model by default — a person's rows together, busiest first, window defaults to 7d · `--details`: per-request log, newest first, each row naming its consumer |
|
|
318
330
|
| `aweshare producer start [--background]` | connect and relay (long-running; `--background` detaches it — logs to `~/.aweshare/producer.log`, pid to `producer.pid`) |
|
|
319
331
|
| `aweshare producer reload` | signal the background producer (SIGHUP) to re-read `config.toml` + `secrets.json` and re-register its offerings on the open tunnel — no disconnect; a broken config keeps the previous values |
|
|
320
332
|
| `aweshare producer stop` | stop the background producer (SIGTERM, SIGKILL after 10s) and clean up its pidfile |
|
|
@@ -336,7 +348,7 @@ The hub reads `config.toml` from its data dir (`~/.aweshare-hub/config.toml`; Do
|
|
|
336
348
|
|
|
337
349
|
**Hot reload:** every tunable in the table except host/port applies live on `SIGHUP` (`kill -HUP <pid>`; Docker: `docker kill -s HUP aweshare-hub`) — the new file is validated first, and a broken edit is logged while the previous values keep serving. Env vars are fixed at process start, so keys pinned by `AWESHARE_*` ignore the reloaded file (same precedence as startup); host/port need a restart. Producer-side offerings and caps reload via `aweshare producer reload`.
|
|
338
350
|
|
|
339
|
-
**Hub-hosted models (`hub produce`):**
|
|
351
|
+
**Hub-hosted models (`hub produce`):** `config.produce.toml` carries `[[backends]]` and `[[offerings]]` sections (producer format; alias namespace `hub/…` — bare names are auto-prefixed) with upstream keys in `secrets.json` next to it (chmod 600). `config.toml` remains exclusively for Hub runtime settings. Those offerings appear in the catalog under producer `hub` and are served by the hub process directly — no tunnel, and they never count against `AWESHARE_MAX_PRODUCERS`. Caps (`maxConcurrencyPerUser`, `maxConcurrentUsers`, `dailyTokens`), usage metering and consumer limits apply exactly as for remote producers. The built-in `hub` producer is not an identity (no token, no invite, cannot be revoked); it appears in `hub list producers` — status `built-in` — only while it carries offerings. Catalog and key edits hot-reload like the tunables; a broken catalog keeps the previous one and is logged.
|
|
340
352
|
|
|
341
353
|
| Env var | Default | Purpose |
|
|
342
354
|
|---|---|---|
|
package/apps/agent/dist/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ Commands:
|
|
|
31
31
|
doctor Diagnose: instance, config, backends, hub (--status for the quick view).
|
|
32
32
|
list Show what this producer has registered on the hub.
|
|
33
33
|
status Summarize this producer's config, process and registered models.
|
|
34
|
-
usage Who used this producer's models:
|
|
34
|
+
usage Who used this producer's models: summary (default) or --details.
|
|
35
35
|
start Connect, register offerings, relay (--background to daemonize).
|
|
36
36
|
reload Force the background producer to re-read its config now.
|
|
37
37
|
stop Stop the background producer and clean up its pidfile.`;
|
|
@@ -94,25 +94,25 @@ Options:
|
|
|
94
94
|
|
|
95
95
|
Options:
|
|
96
96
|
-h, --help Show this message and exit.`,
|
|
97
|
-
usage: `Usage: aweshare producer usage [
|
|
97
|
+
usage: `Usage: aweshare producer usage [OPTIONS]
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
Who used this producer's models: aggregated on the hub, one row per
|
|
100
|
+
consumer × model (default) — a person's rows stay together, busiest person
|
|
101
|
+
and busiest model first. The producer token scopes everything to this
|
|
102
|
+
producer's slice; narrow further with --consumer/--alias.
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
totals and mean duration. --since is required there: aggregates answer
|
|
106
|
-
"how much", not "when".
|
|
104
|
+
--details switches to the request log instead: newest first, one row per
|
|
105
|
+
request, zero content stored, each row naming the consumer that made it.
|
|
107
106
|
|
|
108
107
|
Options:
|
|
109
|
-
--
|
|
110
|
-
--
|
|
111
|
-
--
|
|
112
|
-
--group-by KEY
|
|
113
|
-
--since DUR
|
|
114
|
-
--
|
|
115
|
-
|
|
108
|
+
--details Show the per-request log instead of the summary.
|
|
109
|
+
--consumer NAME Only rows for this consumer.
|
|
110
|
+
--alias ALIAS Only rows for this alias (one of your offerings).
|
|
111
|
+
--group-by KEY consumer × model (default), consumer or alias.
|
|
112
|
+
--since DUR Window: 30m, 12h, 7d (default), 30d… or all.
|
|
113
|
+
--limit N --details only: rows to show, 1..1000 (default 100).
|
|
114
|
+
--json Print the raw API response instead of a table.
|
|
115
|
+
-h, --help Show this message and exit.`,
|
|
116
116
|
start: `Usage: aweshare producer start [OPTIONS]
|
|
117
117
|
|
|
118
118
|
Connect to the hub, register offerings, relay requests.
|
|
@@ -153,7 +153,7 @@ const COMMAND_FLAGS = {
|
|
|
153
153
|
doctor: ['status'],
|
|
154
154
|
list: ['json'],
|
|
155
155
|
status: [],
|
|
156
|
-
usage: ['consumer', 'alias', 'limit', 'group-by', 'since', 'json'],
|
|
156
|
+
usage: ['consumer', 'alias', 'limit', 'group-by', 'since', 'details', 'json'],
|
|
157
157
|
start: ['background'],
|
|
158
158
|
reload: [],
|
|
159
159
|
};
|
|
@@ -374,30 +374,37 @@ async function runStatus(dir) {
|
|
|
374
374
|
/** Usage views: detail log (default) or `summary` aggregate. The producer
|
|
375
375
|
* token scopes both to this producer's models on the hub side. */
|
|
376
376
|
async function runUsage(dir, flags, positional) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
fail(`unexpected argument '${positional[1]}' (see: aweshare producer usage -h)`);
|
|
380
|
-
if (sub !== undefined && sub !== 'summary')
|
|
381
|
-
fail(`unknown usage view '${sub}' — expected 'summary' or nothing (see: aweshare producer usage -h)`);
|
|
377
|
+
if (positional[0])
|
|
378
|
+
fail(`unexpected argument '${positional[0]}' — summaries are the default view now; the request log is --details (see: aweshare producer usage -h)`);
|
|
382
379
|
const { config } = loadAgentConfig(dir);
|
|
383
380
|
const consumer = flags.get('consumer');
|
|
384
381
|
const alias = flags.get('alias');
|
|
385
|
-
if (
|
|
386
|
-
const since = flags.get('since');
|
|
387
|
-
if (!since)
|
|
388
|
-
fail('--since is required for summaries (e.g. --since 7d) — aggregates need an explicit window');
|
|
382
|
+
if (flags.get('details') !== 'true') {
|
|
389
383
|
if (flags.get('limit') !== undefined)
|
|
390
|
-
fail('--limit applies to
|
|
391
|
-
const group = flags.get('group-by') ?? 'consumer';
|
|
384
|
+
fail('--limit applies to --details, not the summary');
|
|
385
|
+
const group = flags.get('group-by') ?? 'consumer-alias';
|
|
386
|
+
if (group !== 'consumer-alias' && group !== 'consumer' && group !== 'alias')
|
|
387
|
+
fail(`--group-by must be 'consumer', 'alias' or 'consumer-alias' (default), got '${group}'`);
|
|
388
|
+
const since = flags.get('since') ?? '7d';
|
|
389
|
+
if (since !== 'all' && !/^\d+[smhd]$/.test(since))
|
|
390
|
+
fail(`--since must look like 30m, 12h, 7d or all, got '${since}'`);
|
|
391
|
+
const usageGroup = group;
|
|
392
392
|
const rows = await fetchUsageSummary(config.hubUrl, config.token, {
|
|
393
|
-
group:
|
|
393
|
+
group: usageGroup,
|
|
394
394
|
since,
|
|
395
395
|
consumer,
|
|
396
396
|
alias,
|
|
397
397
|
});
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
398
|
+
if (flags.get('json') === 'true') {
|
|
399
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const window = since === 'all'
|
|
403
|
+
? 'all time'
|
|
404
|
+
: `since ${new Date(Date.now() - sinceMs(since)).toISOString().slice(0, 10)} (${since})`;
|
|
405
|
+
const grouping = group === 'alias' ? 'alias' : group === 'consumer' ? 'consumer' : 'consumer × model';
|
|
406
|
+
console.log(`usage ${window} — group: ${grouping}`);
|
|
407
|
+
console.log(renderUsageSummary(rows, usageGroup));
|
|
401
408
|
return;
|
|
402
409
|
}
|
|
403
410
|
let limit;
|
|
@@ -419,6 +426,12 @@ async function runUsage(dir, flags, positional) {
|
|
|
419
426
|
names.set(r.consumer_id, r.consumer_name ?? `#${r.consumer_id}`);
|
|
420
427
|
console.log(renderUsage(rows, (id) => names.get(id) ?? `#${id}`));
|
|
421
428
|
}
|
|
429
|
+
/** '30m' / '12h' / '7d' → milliseconds (the window header's date math). */
|
|
430
|
+
function sinceMs(raw) {
|
|
431
|
+
const m = /^(\d+)([smhd])$/.exec(raw);
|
|
432
|
+
const unit = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 }[m?.[2]];
|
|
433
|
+
return Number(m?.[1]) * unit;
|
|
434
|
+
}
|
|
422
435
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
423
436
|
async function runStart(dir, flags) {
|
|
424
437
|
// Validate before daemonizing so config errors hit the terminal, not the log file.
|