@mmmbuto/nexuscrew 0.8.23 → 0.8.25

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.
@@ -75,11 +75,20 @@ function publicPeeringRoutes(deps = {}) {
75
75
  if (st.nodeId === b.instanceId || st.nodes.some((n) => n.nodeId === b.instanceId)
76
76
  || pending.some((row) => row.instanceId === b.instanceId)) return res.status(409).json({ error: 'peer duplicato' });
77
77
  const reservedNames = new Set(pending.map((row) => row.name).filter(validPeerName));
78
- if (nodesStore.getNode(st, b.name) || reservedNames.has(b.name)) {
78
+ // `localhost` e' sintatticamente uno slug valido, ma su Termux non e'
79
+ // un handle di route: piu device espongono lo stesso hostname. Trattalo
80
+ // come conflitto risolvibile anche quando e' il primo, senza consumare
81
+ // l'invite; il client puo' riprovare col suffisso stabile proposto.
82
+ if (b.name === 'localhost' || nodesStore.getNode(st, b.name) || reservedNames.has(b.name)) {
83
+ const usedNames = [...st.nodes.map((node) => node.name), ...reservedNames];
84
+ const suggestedName = nodesStore.deriveNodeHandle(
85
+ b.label || b.name, b.name, b.instanceId, usedNames,
86
+ );
79
87
  return res.status(409).json({
80
88
  error: `nome peer gia' in uso: ${b.name}`,
81
89
  code: 'peer-name-conflict',
82
- hint: 'rimuovi il record stale oppure scegli un nome univoco e riprova con lo stesso invito',
90
+ suggestedName,
91
+ hint: `usa l'handle proposto "${suggestedName}" e riprova con lo stesso invito`,
83
92
  });
84
93
  }
85
94
  const name = b.name;
@@ -71,8 +71,8 @@ const EDIT_KEYS = new Set(['label', 'ssh', 'sshPort', 'autostart', 'visibility',
71
71
  // NON usa ciecamente hostname: se l'hostname e' vuoto o 'localhost' (tipico di
72
72
  // chiavi di test / host dietro tunnel) propone un fallback neutro. L'utente puo'
73
73
  // sempre sovrascriverlo: questo e' solo il valore iniziale del campo.
74
- function defaultDeviceName() {
75
- const h = String(os.hostname() || '').trim();
74
+ function defaultDeviceName(hostname = os.hostname()) {
75
+ const h = String(hostname || '').trim();
76
76
  if (!h || /^localhost$/i.test(h)) return 'NexusCrew';
77
77
  return h.slice(0, nodesStore.LABEL_MAX);
78
78
  }
@@ -143,6 +143,7 @@ function readConfigFile(p) {
143
143
  function settingsRoutes(deps = {}) {
144
144
  const cfg = deps.cfg || {};
145
145
  const seams = cfg.settingsSeams || {};
146
+ const localHostname = typeof seams.hostname === 'function' ? seams.hostname : os.hostname;
146
147
  // tokenStore/closeSessions iniettati da server.js per la semantica di invalidazione
147
148
  // live (audit F7 / §4b(3)). Assenti nei test unitari puri su routes -> la rotazione
148
149
  // scrive solo il file (come prima), senza reload in-memory.
@@ -217,6 +218,10 @@ function settingsRoutes(deps = {}) {
217
218
  boot: bootState({ platform, execImpl: seams.execImpl, uid: seams.uid, home }).enabled,
218
219
  };
219
220
  const updateStatus = updater && typeof updater.status === 'function' ? updater.status() : null;
221
+ const localStore = nodesStore.loadStore(nodesPath);
222
+ const localNodeId = localStore && nodesStore.NODE_ID_RE.test(localStore.nodeId)
223
+ ? localStore.nodeId : null;
224
+ const deviceName = defaultDeviceName(localHostname());
220
225
  const out = {
221
226
  roles: readRoles(configPath),
222
227
  firstRun,
@@ -227,7 +232,11 @@ function settingsRoutes(deps = {}) {
227
232
  autoUpdate: updateStatus ? updateStatus.enabled : fileCfg.autoUpdate !== false,
228
233
  // Nome dispositivo proposto per i form di pairing (etichetta umana, non
229
234
  // lo slug). La UI lo precompila e lascia editing libero.
230
- deviceName: defaultDeviceName(),
235
+ deviceName,
236
+ nodeId: localNodeId,
237
+ localName: localNodeId
238
+ ? nodesStore.deriveNodeHandle(deviceName, localHostname(), localNodeId)
239
+ : null,
231
240
  };
232
241
  if (updateStatus) out.update = updateStatus;
233
242
  send(res, 200, out);
@@ -385,8 +394,8 @@ function settingsRoutes(deps = {}) {
385
394
  // prima di paired:true. Qui resta solo la registrazione dietro mutGate;
386
395
  // token/credenziali MAI nel payload (redazione nel handler estratto).
387
396
  r.post('/nodes/pair', mutGate, createPairHandler({
388
- send, validName, defaultDeviceName,
389
- nodesPath, configPath, home, seams, runtimePort,
397
+ send, validName, defaultDeviceName: () => defaultDeviceName(localHostname()),
398
+ localHostname, nodesPath, configPath, home, seams, runtimePort,
390
399
  }));
391
400
 
392
401
  // --- POST /nodes — nodes add (riusa nodesCmds.nodesAdd) --------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmmbuto/nexuscrew",
3
- "version": "0.8.23",
3
+ "version": "0.8.25",
4
4
  "description": "Faithful browser tmux client — attach to live sessions over a real PTY, localhost-only, mobile-easy",
5
5
  "main": "lib/server.js",
6
6
  "bin": {
@@ -12,6 +12,7 @@
12
12
  "skills/",
13
13
  "frontend/dist/",
14
14
  "frontend/index.html",
15
+ "CHANGELOG.md",
15
16
  "LICENSE",
16
17
  "README.md",
17
18
  "NOTICE"
@@ -19,7 +20,7 @@
19
20
  "scripts": {
20
21
  "start": "node bin/nexuscrew.js serve",
21
22
  "dev": "node bin/nexuscrew.js serve",
22
- "build": "cd frontend && npm install && npm run build && cd ..",
23
+ "build": "npm --prefix frontend ci && npm --prefix frontend run build",
23
24
  "test": "node tests/run-isolated.js && npm --prefix frontend test"
24
25
  },
25
26
  "dependencies": {
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: alibaba-token-media
3
+ description: "Generate or edit images with Wan 2.7 and create videos with HappyHorse through an Alibaba Qwen Cloud Token Plan Personal subscription. Use when the user asks Claude Code, Codex, Codex-VL, or Pi to draw, generate, or edit an image with wan2.7-image or wan2.7-image-pro; create text-to-video or image-to-video media with happyhorse-1.1-t2v or happyhorse-1.1-i2v; inspect a submitted HappyHorse task; or validate the Token Plan media setup. Do not use this skill for chat, visual understanding, batch automation, backend services, or pay-as-you-go DashScope APIs."
4
+ ---
5
+
6
+ # Alibaba Token Media
7
+
8
+ Use the bundled, dependency-free CLI to access only the media models included
9
+ with the Lite guardrails of Alibaba Token Plan Personal. The same skill and Python script work
10
+ with Claude Code, Codex, Codex-VL, and Pi. Invoke the script directly from Pi;
11
+ do not assume that Pi provides a native MCP client.
12
+
13
+ Keep this workflow interactive. Every generation consumes plan Credits, and
14
+ the plan is not for unattended batch or backend workloads.
15
+
16
+ ## Safety contract
17
+
18
+ - Read the credential only from `ALIBABA_CODE_API_KEY`. Never request it in
19
+ chat, pass it on the command line, print it, persist it, or send it through
20
+ an agent bus.
21
+ - Use only the fixed Token Plan host and exact model allowlist implemented by
22
+ the script. Never fall back to DashScope pay-as-you-go endpoints.
23
+ - Run `status` and then a `--dry-run` before the first real request in a
24
+ session. Dry runs never require or reveal the key.
25
+ - Show the user the normalized model, operation, size or duration, resolution,
26
+ and confirmation flags before spending Credits. A real image request needs
27
+ `--confirm-credit-use`; a real video request additionally needs
28
+ `--confirm-expensive`.
29
+ - Require `--confirm-high-cost` for Wan Pro, 2K or 4K images, 1080P video, or
30
+ video longer than five seconds. Keep Lite defaults at one 1K base image or
31
+ one 720P three-second video.
32
+ - Never batch, enable sequential image sets, retry a generation POST
33
+ automatically, or run more than one generation submit concurrently. A task
34
+ status GET may be retried, but never with a tight polling loop.
35
+ - Accept local input only as a regular, non-symlink JPEG, PNG, BMP, or WEBP
36
+ file below 20 MiB and under the current user's home. Image-to-video accepts
37
+ JPEG, PNG, or WEBP first frames, not BMP. Accept remote input only
38
+ over public HTTPS; reject localhost, private IP literals, credentials, and
39
+ non-standard ports.
40
+ - Download signed result URLs immediately. Never paste signed URLs into chat,
41
+ logs, source files, or an agent bus; they expire after 24 hours.
42
+
43
+ ## Locate the CLI
44
+
45
+ Resolve `scripts/alibaba_token_media.py` relative to this `SKILL.md`, then run
46
+ it with Python 3:
47
+
48
+ ```bash
49
+ python3 <skill-dir>/scripts/alibaba_token_media.py status
50
+ ```
51
+
52
+ Keep this packaged skill as the source of truth; do not copy the script into a
53
+ project before using it.
54
+
55
+ ## Generate or edit an image
56
+
57
+ Start with a sanitized preview:
58
+
59
+ ```bash
60
+ python3 <skill-dir>/scripts/alibaba_token_media.py image \
61
+ --prompt "A quiet alpine lake at sunrise" \
62
+ --dry-run
63
+ ```
64
+
65
+ After explicit approval, repeat without `--dry-run` and add
66
+ `--confirm-credit-use`. For an edit, add one to nine `--image` values. Each
67
+ value may be a permitted local image under the current user's home or a public
68
+ HTTPS URL:
69
+
70
+ ```bash
71
+ python3 <skill-dir>/scripts/alibaba_token_media.py image \
72
+ --prompt "Preserve the subject; replace the background with a night city" \
73
+ --image /absolute/path/input.png \
74
+ --confirm-credit-use
75
+ ```
76
+
77
+ Use `--model wan2.7-image-pro --size 2K --confirm-high-cost` only after the
78
+ user selects that higher-cost variant. `4K` is allowed only for Pro
79
+ text-to-image with no input images. The script fixes `n=1` and disables image
80
+ sets. Image watermarking is off by default and enabled only with `--watermark`.
81
+
82
+ ## Submit a video
83
+
84
+ Text-to-video Lite preview:
85
+
86
+ ```bash
87
+ python3 <skill-dir>/scripts/alibaba_token_media.py video-submit \
88
+ --prompt "A paper model city slowly lights up at dusk" \
89
+ --dry-run
90
+ ```
91
+
92
+ For a real request, add both `--confirm-credit-use` and
93
+ `--confirm-expensive`. For image-to-video, select `happyhorse-1.1-i2v` and
94
+ supply exactly one first-frame image with `--image`. Do not supply `--ratio`
95
+ for image-to-video; its aspect ratio follows the input. Video watermarking is
96
+ on by default and can be disabled explicitly with `--no-watermark`.
97
+
98
+ ```bash
99
+ python3 <skill-dir>/scripts/alibaba_token_media.py video-submit \
100
+ --model happyhorse-1.1-i2v \
101
+ --image /absolute/path/first-frame.png \
102
+ --prompt "The camera slowly moves forward" \
103
+ --confirm-credit-use --confirm-expensive
104
+ ```
105
+
106
+ Return the task ID after a real submit. Do not hide submission success behind
107
+ polling.
108
+
109
+ ## Check and download a video
110
+
111
+ Query a task once:
112
+
113
+ ```bash
114
+ python3 <skill-dir>/scripts/alibaba_token_media.py video-status <task-id>
115
+ ```
116
+
117
+ When it is `SUCCEEDED`, repeat with `--download`. If recurring monitoring is
118
+ requested and the client exposes a loop facility, use an interval of at least
119
+ five minutes. Do not implement a private tight polling loop.
120
+
121
+ ```bash
122
+ python3 <skill-dir>/scripts/alibaba_token_media.py video-status <task-id> \
123
+ --download
124
+ ```
125
+
126
+ Default downloads go to
127
+ `~/Downloads/alibaba-token-plan/YYYY-MM-DD/` with unique names and no
128
+ overwrite. An explicit `--output` must remain under the current user's home.
129
+
130
+ ## Reference
131
+
132
+ Read [references/api-contract.md](references/api-contract.md) before changing
133
+ payloads, limits, endpoints, or model handling.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Alibaba Token Media"
3
+ short_description: "Generate Wan images and HappyHorse videos"
4
+ default_prompt: "Use $alibaba-token-media to generate or edit media safely with my Alibaba Token Plan."
@@ -0,0 +1,97 @@
1
+ # Alibaba Token Plan media API contract
2
+
3
+ Verified against the official Qwen Cloud documentation on 2026-07-19.
4
+
5
+ ## Fixed subscription surface
6
+
7
+ - Host: `https://token-plan.ap-southeast-1.maas.aliyuncs.com`
8
+ - Credential environment variable: `ALIBABA_CODE_API_KEY`
9
+ - Authorization: `Bearer <token>`
10
+ - Supported invocation surfaces: Claude Code, Codex, Codex-VL, and Pi through
11
+ the packaged skill and CLI. Pi is not assumed to support MCP natively.
12
+ - Token Plan Personal Lite guardrails: interactive use only, one submit at a
13
+ time, no batch generation, and no automatic retry of a generation POST.
14
+ - Never substitute the DashScope pay-as-you-go host.
15
+
16
+ The Token Plan documentation says coding tools cannot configure media models
17
+ as chat models. They must use a skill, command, agent, or equivalent extension.
18
+ The Personal plan is intended for interactive programming and agent-tool
19
+ work, not unattended scripts, backend services, or batch processing.
20
+
21
+ ## Wan 2.7 image generation and editing
22
+
23
+ Synchronous endpoint:
24
+
25
+ `POST /api/v1/services/aigc/multimodal-generation/generation`
26
+
27
+ Allowed plan models:
28
+
29
+ - `wan2.7-image`
30
+ - `wan2.7-image-pro`
31
+
32
+ Request shape:
33
+
34
+ ```json
35
+ {
36
+ "model": "wan2.7-image",
37
+ "input": {
38
+ "messages": [{
39
+ "role": "user",
40
+ "content": [{"text": "prompt"}, {"image": "data-or-https-url"}]
41
+ }]
42
+ },
43
+ "parameters": {
44
+ "n": 1,
45
+ "size": "1K",
46
+ "enable_sequential": false,
47
+ "watermark": false,
48
+ "thinking_mode": true
49
+ }
50
+ }
51
+ ```
52
+
53
+ The content array requires exactly one text object and accepts zero to nine
54
+ image objects. Prompt maximum is 5,000 characters. Inputs may be JPEG, PNG,
55
+ BMP, or WEBP and are limited to 20 MiB each. `wan2.7-image` supports 1K and 2K;
56
+ Pro also supports 4K for text-to-image only. The output image URL is under
57
+ `output.choices[*].message.content[*].image` and expires after 24 hours.
58
+
59
+ ## HappyHorse video generation
60
+
61
+ Submit endpoint:
62
+
63
+ `POST /api/v1/services/aigc/video-generation/video-synthesis`
64
+
65
+ Required submission header: `X-DashScope-Async: enable`.
66
+
67
+ Allowed plan models:
68
+
69
+ - `happyhorse-1.1-t2v`
70
+ - `happyhorse-1.1-i2v`
71
+
72
+ Text-to-video input contains `prompt`; parameters contain `resolution`,
73
+ `ratio`, and `duration`. Image-to-video input additionally contains exactly one
74
+ `media` item with `type: first_frame` and an HTTPS or Base64 image in `url`;
75
+ its parameters omit `ratio` because output follows the first frame. The CLI
76
+ enables video watermarking by default; image watermarking is disabled by
77
+ default. Image-to-video first frames accept JPEG, PNG, or WEBP, not BMP.
78
+
79
+ Duration is an integer from 3 through 15 seconds. Resolution is 720P or 1080P.
80
+ Text-to-video ratios are 16:9, 9:16, 1:1, 4:3, 3:4, 4:5, 5:4, 9:21, or 21:9.
81
+ The response task ID is `output.task_id`.
82
+
83
+ Status endpoint:
84
+
85
+ `GET /api/v1/tasks/{task_id}`
86
+
87
+ Terminal states are `SUCCEEDED`, `FAILED`, `CANCELED`, and `UNKNOWN`. On
88
+ success, `output.video_url` is an H.264 MP4 signed URL valid for 24 hours.
89
+
90
+ ## Official sources
91
+
92
+ - https://docs.qwencloud.com/token-plan/best-practices/integrate-multimodal-gen
93
+ - https://docs.qwencloud.com/token-plan/personal/token-plan-personal-overview
94
+ - https://docs.qwencloud.com/api-reference/image-generation/wan27-image-gen-edit/synchronous
95
+ - https://docs.qwencloud.com/api-reference/video-generation/happyhorse-text-to-video/create-task
96
+ - https://docs.qwencloud.com/api-reference/video-generation/happyhorse-image-to-video/create-task
97
+ - https://docs.qwencloud.com/api-reference/video-generation/happyhorse-text-to-video/query-result