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 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.3-7C3AED?style=flat-square" alt="Version"></a>
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 'agent'`)
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.stack ?? err.message : 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",
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",