aweshare 0.2.3 → 0.2.5
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 +4 -1
- package/bin/aweshare.mjs +15 -3
- package/bin/self-update.mjs +165 -0
- package/package.json +2 -1
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.2.
|
|
17
|
+
<a href="https://github.com/wehuman01/aweshare-source/releases"><img src="https://img.shields.io/badge/version-0.2.5-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-MIT-22C55E?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>
|
|
@@ -232,9 +232,12 @@ Usage metering: one row per request (alias, real model, status, duration, byte c
|
|
|
232
232
|
| `AWESHARE_CONSUMER_RPS` / `BURST` / `CONCURRENCY` | 10 / 20 / 8 | per-consumer limits |
|
|
233
233
|
| `AWESHARE_HEAD_TIMEOUT_MS` / `IDLE_TIMEOUT_MS` | 120000 / 300000 | response-head timeout / stream idle timeout |
|
|
234
234
|
| `AWESHARE_MAX_BODY_BYTES` | 32MB | request body cap |
|
|
235
|
+
| `AWESHARE_NO_UPDATE_CHECK` | unset | set to `1` to disable the passive update reminder |
|
|
235
236
|
|
|
236
237
|
Health: agent heartbeats every 15s, silent 45s = dead; backends with 2 consecutive AUTH/QUOTA failures auto-degrade (alias shows `degraded`, dispatch stops), 30s probes recover. A new connection with the same producer token replaces the old one (latest-wins).
|
|
237
238
|
|
|
239
|
+
Updating a npm install: `aweshare self-update` (asks before installing; `--check` only shows versions). The CLI also reminds you at most once a day when a newer npm release exists.
|
|
240
|
+
|
|
238
241
|
## Development
|
|
239
242
|
|
|
240
243
|
```bash
|
package/bin/aweshare.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Umbrella CLI: `aweshare hub ...` / `aweshare agent
|
|
2
|
+
// Umbrella CLI: `aweshare hub ...` / `aweshare agent ...` / `aweshare self-update`.
|
|
3
3
|
// Dev convenience — the real entry points live in apps/hub and apps/agent.
|
|
4
4
|
import { readFileSync } from 'node:fs'
|
|
5
|
+
import { maybeCheckForUpdate, runSelfUpdate } from './self-update.mjs'
|
|
5
6
|
|
|
6
7
|
const [, , cmd, ...rest] = process.argv
|
|
7
8
|
|
|
@@ -22,22 +23,33 @@ async function main() {
|
|
|
22
23
|
Usage:
|
|
23
24
|
aweshare hub <command> hub side: init, serve, token, grant, consumer, usage
|
|
24
25
|
aweshare agent <command> producer side: init, start, doctor, grant, revoke, list
|
|
26
|
+
aweshare self-update [--check] update the aweshare CLI itself (npm registry)
|
|
27
|
+
|
|
28
|
+
Environment:
|
|
29
|
+
AWESHARE_NO_UPDATE_CHECK=1 disable the passive update reminder
|
|
25
30
|
|
|
26
31
|
Install (published): npm install -g aweshare
|
|
27
32
|
From source: pnpm install && pnpm build, then pnpm link --global`)
|
|
28
33
|
return
|
|
29
34
|
}
|
|
35
|
+
if (cmd === 'self-update') {
|
|
36
|
+
await runSelfUpdate(rest)
|
|
37
|
+
return
|
|
38
|
+
}
|
|
30
39
|
const target = Object.hasOwn(routes, cmd) ? routes[cmd] : undefined
|
|
31
40
|
if (!target) {
|
|
32
|
-
console.error(`unknown target '${cmd}' — expected 'hub' or '
|
|
41
|
+
console.error(`unknown target '${cmd}' — expected 'hub', 'agent' or 'self-update'`)
|
|
33
42
|
process.exitCode = 1
|
|
34
43
|
return
|
|
35
44
|
}
|
|
45
|
+
const reminder = maybeCheckForUpdate(process.argv.slice(2)).catch(() => null)
|
|
36
46
|
process.argv = [process.argv[0], `aweshare-${cmd}`, ...rest]
|
|
37
47
|
await import(target)
|
|
48
|
+
const message = await reminder
|
|
49
|
+
if (message) console.error(message)
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
main().catch((err) => {
|
|
41
|
-
console.error(err instanceof Error ? err.
|
|
53
|
+
console.error(`error: ${err instanceof Error ? err.message : err}`)
|
|
42
54
|
process.exit(1)
|
|
43
55
|
})
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `aweshare self-update` — update the npm-installed aweshare CLI in place.
|
|
3
|
+
// Also exports the passive update reminder used by the umbrella CLI.
|
|
4
|
+
// Plain Node, no dependencies: this file ships in the tarball as-is.
|
|
5
|
+
import { execFile } from 'node:child_process'
|
|
6
|
+
import { readFileSync } from 'node:fs'
|
|
7
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
8
|
+
import { homedir } from 'node:os'
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import { createInterface } from 'node:readline'
|
|
11
|
+
import { fileURLToPath } from 'node:url'
|
|
12
|
+
|
|
13
|
+
const VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
|
|
14
|
+
.version
|
|
15
|
+
|
|
16
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000
|
|
17
|
+
const REMIND_INTERVAL_MS = 24 * 60 * 60 * 1000
|
|
18
|
+
|
|
19
|
+
function runCommand(command, args, { cwd, timeout = 120_000 } = {}) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
// npm ships as npm.cmd on Windows. Node 20+ (CVE-2024-27980) refuses to
|
|
22
|
+
// spawn .cmd/.bat shims without a shell, so the whole self-update flow
|
|
23
|
+
// fails on Windows unless we route through one.
|
|
24
|
+
execFile(
|
|
25
|
+
command,
|
|
26
|
+
args,
|
|
27
|
+
{ timeout, cwd, shell: process.platform === 'win32' },
|
|
28
|
+
(error, stdout, stderr) => {
|
|
29
|
+
if (error) {
|
|
30
|
+
reject(new Error(stderr.trim() || error.message))
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
resolve(stdout.trim())
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function getNpmLatestVersion(run = runCommand) {
|
|
40
|
+
return run('npm', ['view', 'aweshare', 'version'], { timeout: 60_000 })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Ask a y/n question on the terminal. Empty answer means yes. */
|
|
44
|
+
function confirm(question) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
47
|
+
resolve(null)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
51
|
+
rl.question(`${question} (y/n) `, (answer) => {
|
|
52
|
+
rl.close()
|
|
53
|
+
const trimmed = answer.trim().toLowerCase()
|
|
54
|
+
resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes')
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* `aweshare self-update [--check]`.
|
|
61
|
+
* Throws on failure; the umbrella CLI reports and exits non-zero.
|
|
62
|
+
*/
|
|
63
|
+
export async function runSelfUpdate(args, { run = runCommand, confirmFn = confirm } = {}) {
|
|
64
|
+
const checkOnly = args.includes('--check')
|
|
65
|
+
const latest = await getNpmLatestVersion(run)
|
|
66
|
+
|
|
67
|
+
console.log(`current version: ${VERSION}`)
|
|
68
|
+
console.log(`latest version: ${latest}`)
|
|
69
|
+
|
|
70
|
+
if (VERSION === latest) {
|
|
71
|
+
console.log('already up to date')
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
if (checkOnly) return
|
|
75
|
+
|
|
76
|
+
const ok = await confirmFn(`Update aweshare from ${VERSION} to ${latest}?`)
|
|
77
|
+
if (ok === null) {
|
|
78
|
+
throw new Error('stdin is not a terminal — run interactively, or use: npm install -g aweshare')
|
|
79
|
+
}
|
|
80
|
+
if (!ok) {
|
|
81
|
+
console.log('update cancelled')
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log('updating via npm...')
|
|
86
|
+
await run('npm', ['install', '-g', 'aweshare'], { timeout: 300_000 })
|
|
87
|
+
console.log(`updated to ${latest} — run 'aweshare -v' to verify`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Long-running or non-working commands never see the reminder. */
|
|
91
|
+
export function shouldSkipUpdateCheck(argv) {
|
|
92
|
+
if (argv.length === 0) return true
|
|
93
|
+
const [cmd, sub] = argv
|
|
94
|
+
if (['help', '-h', '--help', '-v', '--version'].includes(cmd)) return true
|
|
95
|
+
if (cmd === 'self-update') return true
|
|
96
|
+
if ((cmd === 'hub' && sub === 'serve') || (cmd === 'agent' && sub === 'start')) return true
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function defaultCacheFile() {
|
|
101
|
+
return path.join(homedir(), '.cache', 'aweshare', 'update-check.json')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function readJson(file) {
|
|
105
|
+
try {
|
|
106
|
+
return JSON.parse(await readFile(file, 'utf8'))
|
|
107
|
+
} catch {
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function writeJson(file, data) {
|
|
113
|
+
try {
|
|
114
|
+
await mkdir(path.dirname(file), { recursive: true })
|
|
115
|
+
await writeFile(file, `${JSON.stringify(data, null, 2)}\n`)
|
|
116
|
+
} catch {
|
|
117
|
+
// a failed cache write must never break the command it decorates
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Passive reminder: at most one npm lookup and one reminder per 24h.
|
|
123
|
+
* Returns the reminder text to print (on stderr) or null.
|
|
124
|
+
*/
|
|
125
|
+
export async function maybeCheckForUpdate(argv, { run = runCommand, cacheFile, now } = {}) {
|
|
126
|
+
if (process.env.AWESHARE_NO_UPDATE_CHECK === '1') return null
|
|
127
|
+
if (shouldSkipUpdateCheck(argv)) return null
|
|
128
|
+
|
|
129
|
+
cacheFile = cacheFile ?? defaultCacheFile()
|
|
130
|
+
now = now ?? Date.now()
|
|
131
|
+
let cache = await readJson(cacheFile)
|
|
132
|
+
|
|
133
|
+
let latest = null
|
|
134
|
+
if (cache && now - Date.parse(cache.lastChecked) < CHECK_INTERVAL_MS) {
|
|
135
|
+
latest = cache.latestVersion
|
|
136
|
+
} else {
|
|
137
|
+
try {
|
|
138
|
+
latest = await getNpmLatestVersion(run)
|
|
139
|
+
} catch {
|
|
140
|
+
return null
|
|
141
|
+
}
|
|
142
|
+
cache = {
|
|
143
|
+
lastChecked: new Date(now).toISOString(),
|
|
144
|
+
latestVersion: latest,
|
|
145
|
+
lastReminded: cache?.lastReminded ?? '',
|
|
146
|
+
}
|
|
147
|
+
await writeJson(cacheFile, cache)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!latest || latest === VERSION) return null
|
|
151
|
+
|
|
152
|
+
const lastRemindedMs = cache.lastReminded ? Date.parse(cache.lastReminded) : 0
|
|
153
|
+
if (now - lastRemindedMs < REMIND_INTERVAL_MS) return null
|
|
154
|
+
|
|
155
|
+
await writeJson(cacheFile, { ...cache, lastReminded: new Date(now).toISOString() })
|
|
156
|
+
return `Update available: ${VERSION} → ${latest}. Run \`aweshare self-update\` to update.`
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// direct execution: `node bin/self-update.mjs --check`
|
|
160
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
161
|
+
runSelfUpdate(process.argv.slice(2)).catch((err) => {
|
|
162
|
+
console.error(`error: ${err instanceof Error ? err.message : String(err)}`)
|
|
163
|
+
process.exit(1)
|
|
164
|
+
})
|
|
165
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aweshare",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Open-source, local-first AI capability relay",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"bin",
|
|
19
|
+
"!bin/*.test.mjs",
|
|
19
20
|
"apps/hub/dist",
|
|
20
21
|
"apps/hub/package.json",
|
|
21
22
|
"apps/agent/dist",
|