fss-link 1.6.13 → 1.7.6
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/bundle/fss-link-main.js +4255 -0
- package/bundle/fss-link.js +21 -4234
- package/docs/README.md +6 -5
- package/docs/TOOLS.md +229 -9
- package/package.json +54 -38
- package/scripts/check-publish.js +20 -9
- package/scripts/copy_bundle_assets.js +10 -1
- package/scripts/{postinstall-message.js → postinstall-message.cjs} +18 -0
- package/scripts/version.js +17 -9
package/docs/README.md
CHANGED
|
@@ -171,14 +171,15 @@ Reference files directly in your message with `@`:
|
|
|
171
171
|
|
|
172
172
|
## Testing
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
Full workspace test suites are safe with the `**/dist/**` excludes in all vitest configs. The historical OOM crash was caused by vitest picking up duplicate compiled `.js` test files from `packages/*/dist/`; this has been fixed. See [VITEST-SAFETY.md](../VITEST-SAFETY.md) for full guidance and emergency kill procedures.
|
|
175
175
|
|
|
176
176
|
```bash
|
|
177
|
-
#
|
|
178
|
-
npx vitest run packages/cli/src/config/database.test.ts
|
|
179
|
-
|
|
180
|
-
# Safe: run one workspace
|
|
177
|
+
# Run all tests for a workspace
|
|
181
178
|
npm run test --workspace=packages/cli
|
|
179
|
+
npm run test --workspace=packages/core
|
|
180
|
+
|
|
181
|
+
# Run a single test file
|
|
182
|
+
npx vitest run packages/cli/src/config/database.test.ts
|
|
182
183
|
```
|
|
183
184
|
|
|
184
185
|
## Documentation
|
package/docs/TOOLS.md
CHANGED
|
@@ -148,6 +148,10 @@ Fetches a URL and returns its content. Handles HTML (extracted as readable text)
|
|
|
148
148
|
| Parameter | Type | Description |
|
|
149
149
|
|-----------|------|-------------|
|
|
150
150
|
| `url` | string | URL to fetch |
|
|
151
|
+
| `prompt` | string | Instructions for what to extract from the page |
|
|
152
|
+
| `include_images` | boolean | When true, extract and return image URLs, alt text, and context found on the page |
|
|
153
|
+
|
|
154
|
+
For persistent research with file output, use `web_scraper` instead.
|
|
151
155
|
|
|
152
156
|
### web_search
|
|
153
157
|
|
|
@@ -156,9 +160,12 @@ Searches the web using a search engine API.
|
|
|
156
160
|
| Parameter | Type | Description |
|
|
157
161
|
|-----------|------|-------------|
|
|
158
162
|
| `query` | string | Search query |
|
|
159
|
-
| `
|
|
163
|
+
| `max_results` | integer | Maximum results to return (1–20, default 5) |
|
|
164
|
+
| `search_depth` | string | `"basic"` (fast, default) or `"advanced"` (AI-powered, costs more) |
|
|
165
|
+
|
|
166
|
+
Requires a search API key (Tavily or Brave Search — configured automatically, Tavily preferred).
|
|
160
167
|
|
|
161
|
-
|
|
168
|
+
For persistent research with file output, use `web_scraper` instead.
|
|
162
169
|
|
|
163
170
|
### fetch_image
|
|
164
171
|
|
|
@@ -185,16 +192,182 @@ Specialized parsers for structured documents. These extract text, tables, metada
|
|
|
185
192
|
|
|
186
193
|
| Tool | Formats | What It Extracts |
|
|
187
194
|
|------|---------|-----------------|
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
| `
|
|
191
|
-
| `
|
|
195
|
+
| `pdf_parser` | PDF | Text, page structure, metadata, visual page rendering via `view_page` |
|
|
196
|
+
| `excel_parser` | XLSX, XLS, CSV | Sheets, cells, formulas, structure, multi-sheet processing |
|
|
197
|
+
| `word_parser` | DOCX, DOC, MD | Text, headings, tables, styles, embedded images |
|
|
198
|
+
| `email_parser` | EML, MSG, MBOX | Headers, body, attachments metadata, multi-message .mbox |
|
|
192
199
|
| `read_data` | CSV, JSON, YAML, XML, TOML | Parsed data with schema inference |
|
|
193
200
|
|
|
201
|
+
**pdf_parser extras:**
|
|
202
|
+
- `mode="preview"` — light deterministic overview (page count, metadata, first 40 lines) without dumping body text
|
|
203
|
+
- `view_page=N` — renders a specific page as an image for visual inspection (sees layout, tables, diagrams, signatures)
|
|
204
|
+
- `pages=[3, 5, 7]` or `page_range={start, end}` — extract specific pages with boundary detection confidence labels
|
|
205
|
+
|
|
206
|
+
**excel_parser extras:**
|
|
207
|
+
- `read_all_sheets` — read all sheets or just the first one
|
|
208
|
+
- `range="A1:C10"` — extract a specific cell range
|
|
209
|
+
- `include_formatting` — include cell formatting information
|
|
210
|
+
- `preserve_formulas` — preserve Excel formulas in output
|
|
211
|
+
|
|
212
|
+
**word_parser extras:**
|
|
213
|
+
- `extract_images` — extract embedded images from the document
|
|
214
|
+
- `preserve_formatting` — preserve document formatting
|
|
215
|
+
|
|
216
|
+
**email_parser extras:**
|
|
217
|
+
- `max_messages` — maximum number of messages to parse from .mbox files (default: 50, max: 500)
|
|
218
|
+
- `prefer_html` — prefer HTML body over plain text when both are present
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Audio
|
|
223
|
+
|
|
224
|
+
### read_audio
|
|
225
|
+
|
|
226
|
+
Read an audio file and return its metadata and/or a segment-level transcript.
|
|
227
|
+
|
|
228
|
+
| Parameter | Type | Description |
|
|
229
|
+
|-----------|------|-------------|
|
|
230
|
+
| `absolute_path` | string | Absolute path to the audio file (mp3, wav, flac, ogg, m4a, etc.) |
|
|
231
|
+
| `mode` | string | `metadata` for format/tag info only, `transcribe` for transcript only, or `full` (default) for both. |
|
|
232
|
+
|
|
233
|
+
- Extracts duration, sample rate, channels, codec, bitrate, and embedded tags (title, artist, album, year, genre)
|
|
234
|
+
- Transcription requires a Whisper-compatible server at `FSS_WHISPER_URL` (default `localhost:11453`, timeout 120s via `FSS_WHISPER_TIMEOUT`)
|
|
235
|
+
- Returns segment-level transcript with timestamps and derived silence regions (gaps ≥ 0.5s)
|
|
236
|
+
- Supports abort/cancellation during transcription
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Image Generation & Editing
|
|
241
|
+
|
|
242
|
+
### generate_image
|
|
243
|
+
|
|
244
|
+
Create new images from text descriptions via ComfyUI (FLUX.2 Klein). Single or batch mode. Upscaling is applied by generating at full resolution.
|
|
245
|
+
|
|
246
|
+
| Parameter | Type | Description |
|
|
247
|
+
|-----------|------|-------------|
|
|
248
|
+
| `prompt` | string | Image generation prompt (single mode). Describe the image you want in detail. |
|
|
249
|
+
| `batch_content` | string | Markdown batch content (batch mode). Use `###` headings as prompts, optional bullet overrides below each (seed, steps, negative_prompt, etc). |
|
|
250
|
+
| `negative_prompt` | string | What to avoid in the image (single mode only). |
|
|
251
|
+
| `width` | integer | Base width before upscaling (default: 1024). |
|
|
252
|
+
| `height` | integer | Base height before upscaling (default: 1024). |
|
|
253
|
+
| `steps` | integer | Sampling steps (default: 20 for 9b model). Higher = more detail, slower. |
|
|
254
|
+
| `cfg_scale` | number | CFG scale (default: 1.0 for FLUX.2). Lower = more creative, higher = follows prompt more strictly. |
|
|
255
|
+
| `seed` | integer | Random seed for reproducibility (-1 = random). |
|
|
256
|
+
| `model` | string | Workflow preset: `9b` (FLUX.2 Klein 9B, best quality), `4b` (FLUX.2 Klein 4B, faster), `flux-q8` (quantized 8-bit). Default: `9b`. |
|
|
257
|
+
| `output_dir` | string | Output directory. Defaults to current working directory. |
|
|
258
|
+
| `output_filename` | string | Custom filename for the output image. Auto-generated if omitted. |
|
|
259
|
+
|
|
260
|
+
### edit_image
|
|
261
|
+
|
|
262
|
+
Edit an existing image using ComfyUI Qwen-Edit workflow with instruction-based modifications and optional reference images. ALWAYS creates a NEW output file — the original image is NEVER modified or overwritten.
|
|
263
|
+
|
|
264
|
+
| Parameter | Type | Description |
|
|
265
|
+
|-----------|------|-------------|
|
|
266
|
+
| `input_image` | string | Input image file path to edit (required). |
|
|
267
|
+
| `prompt` | string | Edit instruction describing what to change (required). Be specific about what to keep and what to change. |
|
|
268
|
+
| `reference_images` | array | Optional reference image paths for style or character consistency. Can be repeated up to 3×. |
|
|
269
|
+
| `steps` | integer | Sampling steps (default: 4 for Lightning; 8 for more detail). |
|
|
270
|
+
| `cfg` | number | CFG scale (default: 1.0 — Lightning works at 1.0). |
|
|
271
|
+
| `seed` | integer | Random seed for reproducibility (-1 = random). |
|
|
272
|
+
| `output_dir` | string | Output directory for the new edited image. Defaults to current working directory. |
|
|
273
|
+
| `output_filename` | string | Custom filename for the output image. Auto-generated if omitted. |
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Web Scraping
|
|
278
|
+
|
|
279
|
+
### web_scraper
|
|
280
|
+
|
|
281
|
+
Scrape web pages, search the web, or crawl sites. Saves extracted content to files.
|
|
282
|
+
|
|
283
|
+
| Parameter | Type | Description |
|
|
284
|
+
|-----------|------|-------------|
|
|
285
|
+
| `operation` | string | Type of operation: `"scrape"` (direct URLs), `"crawl"` (recursive), `"search"` (search-driven), `"summarise"` (synthesise previous scrapes with LLM). |
|
|
286
|
+
| `sources` | array | List of URLs or file paths to process (required for scrape/crawl operations). |
|
|
287
|
+
| `searchQuery` | string | Search query for search-driven scraping (required for search operation). |
|
|
288
|
+
| `outputDir` | string | Directory where scraped content will be saved (default: `~/.fss-link/scraper/`). |
|
|
289
|
+
| `maxResults` | number | Maximum number of search results to process (for search operation, default: 10, max: 50). |
|
|
290
|
+
| `maxDepth` | number | Maximum crawl depth for "crawl" operation (default: 2, max: 10). |
|
|
291
|
+
| `maxPages` | number | Maximum pages to crawl (budget cap, default: 20, max: 100). |
|
|
292
|
+
| `topic` | string | Filter results by topic relevance (keyword matching). Pages not relevant to this topic are dropped. |
|
|
293
|
+
| `minQuality` | number | Minimum content quality score 0–1 (default: 0.3). Drops empty/boilerplate pages. |
|
|
294
|
+
| `collectMedia` | boolean | Collect images from scraped pages. Downloads to media/ subfolder. |
|
|
295
|
+
| `framing` | string | Perspective/lens for synthesis (summarise operation only). Not a question — a framing directive that guides how sources are synthesised. |
|
|
296
|
+
|
|
297
|
+
- Supports topic filtering, quality thresholding, search result ranking
|
|
298
|
+
- Media collection (opt-in) downloads images from scraped pages
|
|
299
|
+
- Rate limiting, robots.txt compliance built in
|
|
300
|
+
- Uses Brave/Tavily search APIs when available
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Structured Data Inspection
|
|
305
|
+
|
|
306
|
+
### inspect_structured
|
|
307
|
+
|
|
308
|
+
Inspect structured data files (JSON, JSONL/NDJSON, YAML, TOML, XML, CSV, TSV). Returns the file's shape — keys, types, array lengths, depth — with addressable paths, instead of dumping raw content. Far cheaper than `read_file` for large structured files where syntax noise overwhelms the actual data.
|
|
309
|
+
|
|
310
|
+
| Parameter | Type | Description |
|
|
311
|
+
|-----------|------|-------------|
|
|
312
|
+
| `absolute_path` | string | Absolute path to the structured data file. |
|
|
313
|
+
| `mode` | string | `"overview"` (default) returns the structural shape with addressable paths. `"extract"` returns one subtree, located by `path`. |
|
|
314
|
+
| `path` | string | Path to extract (extract mode only). Dot/bracket notation, e.g. `"Properties.Children[0]"` or `'items[0]["name"]'`. |
|
|
315
|
+
| `max_depth` | integer | Overview only — descend at most this many levels (default 4). |
|
|
316
|
+
| `max_array_samples` | integer | Overview only — number of array elements to expand inline (default 3). |
|
|
317
|
+
|
|
318
|
+
Supported formats: `.json`, `.jsonl`, `.ndjson`, `.yaml`, `.yml`, `.toml`, `.xml`, `.csv`, `.tsv`
|
|
319
|
+
|
|
194
320
|
---
|
|
195
321
|
|
|
196
322
|
## Agent Utilities
|
|
197
323
|
|
|
324
|
+
### wait_for
|
|
325
|
+
|
|
326
|
+
Wait for a long-running background process to complete by polling for one of two completion signals: a file appearing on disk, or a regex matching in a log file.
|
|
327
|
+
|
|
328
|
+
| Parameter | Type | Description |
|
|
329
|
+
|-----------|------|-------------|
|
|
330
|
+
| `wait_for_file` | object | Wait for a file to appear at a given absolute path. Set `stable_for_seconds` to require the file's size AND modification time to stop changing before counting as complete. |
|
|
331
|
+
| `wait_for_log_match` | object | Wait for a regex to match somewhere in the tail of a log file. Use when the output file appears mid-process or has unpredictable size. |
|
|
332
|
+
| `max_total_seconds` | number | Hard cap on total wait time, in seconds. Minimum 2, maximum 3600 (1 hour). |
|
|
333
|
+
| `interval_seconds` | number | Seconds between checks. Default 5, minimum 2. |
|
|
334
|
+
| `progress_pattern` | string | Optional regex for live progress display. If not set, the tool auto-detects common patterns (segments, percent, step counter, timing). |
|
|
335
|
+
| `progress_log_path` | string | Optional path for progress tracking. Defaults to `wait_for_log_match.log_path`. |
|
|
336
|
+
|
|
337
|
+
**`wait_for_file` properties:**
|
|
338
|
+
|
|
339
|
+
| Property | Type | Description |
|
|
340
|
+
|----------|------|-------------|
|
|
341
|
+
| `path` | string | Absolute path to the target file. |
|
|
342
|
+
| `stable_for_seconds` | number | File must exist and its size AND mtime must be unchanged for this many seconds. |
|
|
343
|
+
|
|
344
|
+
**`wait_for_log_match` properties:**
|
|
345
|
+
|
|
346
|
+
| Property | Type | Description |
|
|
347
|
+
|----------|------|-------------|
|
|
348
|
+
| `log_path` | string | Absolute path to the log file. |
|
|
349
|
+
| `pattern` | string | JavaScript regex matched against the last 64 KB of the log. Anchor tightly with `^` and `$`. |
|
|
350
|
+
| `min_matches` | number | Minimum matches required before declaring done. Default 1. Use >1 for noisy logs. |
|
|
351
|
+
| `anchored_to_end` | number | Require the match to appear in the last N lines. Ensures completion signal is at the END of output. |
|
|
352
|
+
|
|
353
|
+
**Auto-progress detection:** When `progress_pattern` is not set, the tool auto-detects patterns from the log: segment counters (`158/162 segments`), percentages (`75%`), step counters (`Step 15/20`), timing (`12.3s / 45s`), and generic counters (`42 of 100`).
|
|
354
|
+
|
|
355
|
+
**ETA estimation:** The tool tracks progress snapshots and displays estimated time remaining (e.g., `~3m 12s remaining`) based on the current progress rate.
|
|
356
|
+
|
|
357
|
+
**Progress delta:** Shows how progress changed since last check (e.g., `158/162 → 159/162`).
|
|
358
|
+
|
|
359
|
+
**Example:** Wait for a build log to show "Build completed successfully" in the last 5 lines:
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
wait_for_log_match:
|
|
363
|
+
log_path: /path/to/build.log
|
|
364
|
+
pattern: "^Build completed successfully$"
|
|
365
|
+
min_matches: 1
|
|
366
|
+
anchored_to_end: 5
|
|
367
|
+
max_total_seconds: 300
|
|
368
|
+
interval_seconds: 5
|
|
369
|
+
```
|
|
370
|
+
|
|
198
371
|
### workplan_update (formerly `todo_write`)
|
|
199
372
|
|
|
200
373
|
Manages a structured work plan for the current session. Helps the agent track multi-step work.
|
|
@@ -214,6 +387,33 @@ Usage guidelines:
|
|
|
214
387
|
|
|
215
388
|
Reads and writes to the agent's persistent memory system. Stores user preferences, project context, and learned patterns across sessions.
|
|
216
389
|
|
|
390
|
+
### enter_worktree
|
|
391
|
+
|
|
392
|
+
Creates an isolated git worktree at `<projectRoot>/.fss-link/worktrees/<slug>` and returns its absolute path so subsequent file edits, shell commands, and other tools can operate inside it.
|
|
393
|
+
|
|
394
|
+
| Parameter | Type | Description |
|
|
395
|
+
|-----------|------|-------------|
|
|
396
|
+
| `name` | string | Optional slug (letters, digits, dot, underscore, hyphen; max 64 chars). Auto-generated when omitted. |
|
|
397
|
+
|
|
398
|
+
- Creates a new branch `<prefix><slug>` based on the current branch
|
|
399
|
+
- The worktree persists across the session until `exit_worktree` is invoked
|
|
400
|
+
- Multiple agents can work in parallel in different worktrees without interfering with each other or the main checkout
|
|
401
|
+
- **Only invoke when the user explicitly asks for a worktree** — not for routine bug fixes or feature work
|
|
402
|
+
|
|
403
|
+
### exit_worktree
|
|
404
|
+
|
|
405
|
+
Exits a worktree previously created by `enter_worktree`.
|
|
406
|
+
|
|
407
|
+
| Parameter | Type | Description |
|
|
408
|
+
|-----------|------|-------------|
|
|
409
|
+
| `name` | string | Slug of the worktree to exit (must match the name used in `enter_worktree`). |
|
|
410
|
+
| `action` | string | `"keep"` preserves the worktree on disk; `"remove"` deletes it and its branch. |
|
|
411
|
+
| `discard_changes` | boolean | When `action="remove"`, must be true to delete a worktree with uncommitted changes. |
|
|
412
|
+
|
|
413
|
+
- `action='keep'` — preserves the worktree directory and branch on disk so it can be revisited later
|
|
414
|
+
- `action='remove'` — deletes the worktree directory and branch. Refuses to run if the worktree contains uncommitted changes unless `discard_changes: true` is set
|
|
415
|
+
- Only invoke when the user explicitly asks to leave or clean up a worktree
|
|
416
|
+
|
|
217
417
|
---
|
|
218
418
|
|
|
219
419
|
## TTS (Text-to-Speech)
|
|
@@ -230,11 +430,31 @@ Synthesizes speech from text using the FSS-TTS server.
|
|
|
230
430
|
|
|
231
431
|
| Parameter | Type | Description |
|
|
232
432
|
|-----------|------|-------------|
|
|
233
|
-
| `text` | string | Text to speak |
|
|
234
|
-
| `voice` | string | Voice
|
|
433
|
+
| `text` | string | Text to speak (max 5000 characters) |
|
|
434
|
+
| `voice` | string | Voice personality to use (Bella, Emma, George, Lewis, Sarah, Michael, Adam, Isabella, Nicole). Default: Bella. |
|
|
435
|
+
| `wait_for_playback` | boolean | If true, block until the TTS server has finished playing the audio before returning. Default false (fire-and-forget). Essential in non-interactive (`-p`) mode. |
|
|
235
436
|
|
|
236
437
|
Requires the FSS-TTS server running at `FSS_TTS_URL` (default `localhost:11450`).
|
|
237
438
|
|
|
439
|
+
### tts_narrate
|
|
440
|
+
|
|
441
|
+
Play multiple voice segments sequentially — Bella says the intro, Emma continues, George adds a comment, etc. Each segment plays fully before the next begins. Use for multi-voice explanations, handoffs between speakers, or conversational narration.
|
|
442
|
+
|
|
443
|
+
| Parameter | Type | Description |
|
|
444
|
+
|-----------|------|-------------|
|
|
445
|
+
| `segments` | array | Ordered list of `{voice, text}` objects to play sequentially |
|
|
446
|
+
|
|
447
|
+
Each segment:
|
|
448
|
+
|
|
449
|
+
| Property | Type | Description |
|
|
450
|
+
|----------|------|-------------|
|
|
451
|
+
| `voice` | string | Voice name (Bella, Emma, George, Lewis, Sarah, Michael, Adam, Isabella, Nicole) |
|
|
452
|
+
| `text` | string | Text for this segment (max 1000 chars) |
|
|
453
|
+
|
|
454
|
+
- Minimum 2 segments, maximum 10
|
|
455
|
+
- Each segment plays fully before the next begins
|
|
456
|
+
- Supports abort/cancellation mid-sequence
|
|
457
|
+
|
|
238
458
|
---
|
|
239
459
|
|
|
240
460
|
## MCP (Model Context Protocol)
|
|
@@ -264,5 +484,5 @@ All file-modifying tools (edit, write_file) enforce:
|
|
|
264
484
|
- **Workspace boundary checks** — can't write outside your project directory
|
|
265
485
|
- **Diff previews** — see exactly what will change before approving
|
|
266
486
|
- **Confirmation prompts** — unless in auto_edit or YOLO mode
|
|
267
|
-
- **Tool result size limits** —
|
|
487
|
+
- **Tool result size limits** — shell output capped at 32 KB, read-file at 4,000 lines, core tool results at 40K chars to prevent context overflow
|
|
268
488
|
- **Error messages include the failed path** — so the agent knows exactly what went wrong and doesn't retry blindly
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fss-link",
|
|
3
|
-
"version": "1.6
|
|
3
|
+
"version": "1.7.6",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20.0.0"
|
|
6
6
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "git+https://github.com/FSSCoding/fss-link.git"
|
|
14
14
|
},
|
|
15
15
|
"config": {
|
|
16
|
-
"sandboxImageUri": "ghcr.io/fsscoding/fss-link:1.
|
|
16
|
+
"sandboxImageUri": "ghcr.io/fsscoding/fss-link:1.7.5"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"start": "node scripts/start.js",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"format": "prettier --experimental-cli --write .",
|
|
45
45
|
"typecheck": "npm run typecheck --workspaces --if-present",
|
|
46
46
|
"preflight": "npm run clean && npm ci && npm run format && npm run lint:ci && npm run build && npm run typecheck && npm run test:ci",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
47
|
+
"postinstall": "node scripts/postinstall-message.cjs",
|
|
48
|
+
"prepare": "patch-package || true",
|
|
49
49
|
"prepare:package": "node scripts/prepare-package.js",
|
|
50
50
|
"release:version": "node scripts/version.js",
|
|
51
51
|
"telemetry": "node scripts/telemetry.js",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"scripts/install-linux.sh",
|
|
72
72
|
"scripts/install-macos.sh",
|
|
73
73
|
"scripts/install-windows.ps1",
|
|
74
|
-
"scripts/postinstall-message.
|
|
74
|
+
"scripts/postinstall-message.cjs",
|
|
75
75
|
"scripts/prebundle-sync-dist.js",
|
|
76
76
|
"scripts/prepare-package.js",
|
|
77
77
|
"scripts/sandbox_command.js",
|
|
@@ -82,59 +82,83 @@
|
|
|
82
82
|
"LICENSE"
|
|
83
83
|
],
|
|
84
84
|
"devDependencies": {
|
|
85
|
-
"@types/
|
|
86
|
-
"@
|
|
87
|
-
"@types/micromatch": "^4.0.9",
|
|
88
|
-
"@types/mime-types": "^3.0.1",
|
|
89
|
-
"@types/minimatch": "^5.1.2",
|
|
90
|
-
"@types/mock-fs": "^4.13.4",
|
|
91
|
-
"@types/qrcode-terminal": "^0.12.2",
|
|
92
|
-
"@types/shell-quote": "^1.7.5",
|
|
93
|
-
"@types/sql.js": "^1.4.9",
|
|
94
|
-
"@types/turndown": "^5.0.5",
|
|
95
|
-
"@types/jsdom": "^21.1.7",
|
|
96
|
-
"@types/mozilla__readability": "^0.4.2",
|
|
97
|
-
"@types/update-notifier": "^6.0.8",
|
|
98
|
-
"@types/uuid": "^10.0.0",
|
|
99
|
-
"@vitest/coverage-v8": "^4.0.0",
|
|
85
|
+
"@types/ink-testing-library": "^1.0.4",
|
|
86
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
100
87
|
"concurrently": "^9.2.0",
|
|
101
88
|
"cross-env": "^7.0.3",
|
|
102
|
-
"esbuild": "^0.25.0",
|
|
103
89
|
"eslint": "^9.24.0",
|
|
104
90
|
"eslint-config-prettier": "^10.1.2",
|
|
105
91
|
"eslint-plugin-import": "^2.31.0",
|
|
106
92
|
"eslint-plugin-license-header": "^0.8.0",
|
|
93
|
+
"eslint-plugin-no-only-tests": "^3.4.0",
|
|
107
94
|
"eslint-plugin-react": "^7.37.5",
|
|
108
95
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
96
|
+
"eslint-plugin-vitest": "^0.5.4",
|
|
109
97
|
"globals": "^16.0.0",
|
|
98
|
+
"ink": "^6.8.0",
|
|
99
|
+
"ink-testing-library": "^4.0.0",
|
|
110
100
|
"json": "^11.0.0",
|
|
111
101
|
"lodash": "^4.17.21",
|
|
112
|
-
"memfs": "^4.
|
|
102
|
+
"memfs": "^4.57.3",
|
|
113
103
|
"mnemonist": "^0.40.3",
|
|
114
104
|
"mock-fs": "^5.5.0",
|
|
115
|
-
"msw": "^2.
|
|
105
|
+
"msw": "^2.14.6",
|
|
106
|
+
"patch-package": "^8.0.1",
|
|
116
107
|
"prettier": "^3.5.3",
|
|
108
|
+
"react": "^19.2.6",
|
|
117
109
|
"react-devtools-core": "^7.0.1",
|
|
110
|
+
"react-dom": "^19.2.6",
|
|
118
111
|
"tsx": "^4.20.3",
|
|
119
|
-
"typescript
|
|
120
|
-
"
|
|
112
|
+
"typescript": "^5.9.3",
|
|
113
|
+
"typescript-eslint": "^8.30.1"
|
|
121
114
|
},
|
|
122
115
|
"dependencies": {
|
|
123
116
|
"@google/genai": "1.13.0",
|
|
124
117
|
"@iarna/toml": "^2.2.5",
|
|
125
118
|
"@modelcontextprotocol/sdk": "^1.15.1",
|
|
126
119
|
"@mozilla/readability": "^0.6.0",
|
|
120
|
+
"@testing-library/react": "^16.3.2",
|
|
121
|
+
"@types/command-exists": "^1.2.3",
|
|
122
|
+
"@types/dompurify": "^3.0.5",
|
|
123
|
+
"@types/express": "^5.0.3",
|
|
124
|
+
"@types/fs-extra": "^11.0.4",
|
|
125
|
+
"@types/html-to-text": "^9.0.4",
|
|
126
|
+
"@types/js-yaml": "^4.0.9",
|
|
127
|
+
"@types/jsdom": "^21.1.7",
|
|
128
|
+
"@types/marked": "^5.0.2",
|
|
129
|
+
"@types/micromatch": "^4.0.9",
|
|
130
|
+
"@types/mime-types": "^3.0.1",
|
|
131
|
+
"@types/minimatch": "^5.1.2",
|
|
132
|
+
"@types/mock-fs": "^4.13.4",
|
|
133
|
+
"@types/mozilla__readability": "^0.4.2",
|
|
134
|
+
"@types/node": "^25.9.1",
|
|
135
|
+
"@types/pdf-parse": "^1.1.5",
|
|
136
|
+
"@types/picomatch": "^4.0.3",
|
|
137
|
+
"@types/proper-lockfile": "^4.1.4",
|
|
138
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
139
|
+
"@types/react": "^19.2.15",
|
|
140
|
+
"@types/react-dom": "^19.2.3",
|
|
141
|
+
"@types/shell-quote": "^1.7.5",
|
|
142
|
+
"@types/sql.js": "^1.4.9",
|
|
143
|
+
"@types/turndown": "^5.0.6",
|
|
144
|
+
"@types/update-notifier": "^6.0.8",
|
|
145
|
+
"@types/uuid": "^10.0.0",
|
|
146
|
+
"@types/vscode": "^1.99.0",
|
|
147
|
+
"@types/ws": "^8.5.10",
|
|
148
|
+
"@types/xml2js": "^0.4.14",
|
|
149
|
+
"@types/yargs": "^17.0.33",
|
|
150
|
+
"@types/yauzl": "^2.10.3",
|
|
127
151
|
"axios": "^1.11.0",
|
|
128
152
|
"chalk": "^5.3.0",
|
|
129
153
|
"cheerio": "^1.1.2",
|
|
130
154
|
"command-exists": "^1.2.9",
|
|
131
155
|
"diff": "^9.0.0",
|
|
132
156
|
"dotenv": "^17.1.0",
|
|
157
|
+
"esbuild": "^0.25.0",
|
|
133
158
|
"exceljs": "^4.4.0",
|
|
134
159
|
"fs-extra": "^11.3.1",
|
|
135
160
|
"glob": "^13.0.6",
|
|
136
161
|
"highlight.js": "^11.11.1",
|
|
137
|
-
"ink": "^6.1.1",
|
|
138
162
|
"ink-big-text": "^2.0.0",
|
|
139
163
|
"ink-gradient": "^3.0.0",
|
|
140
164
|
"ink-link": "^4.1.0",
|
|
@@ -144,13 +168,13 @@
|
|
|
144
168
|
"jsdom": "^27.0.0",
|
|
145
169
|
"lowlight": "^3.3.0",
|
|
146
170
|
"mime-types": "^3.0.1",
|
|
171
|
+
"music-metadata": "^11.12.3",
|
|
147
172
|
"open": "^10.1.2",
|
|
148
173
|
"p-limit": "^7.1.1",
|
|
149
174
|
"pdf-to-img": "^6.0.0",
|
|
150
175
|
"proper-lockfile": "^4.1.2",
|
|
176
|
+
"punycode.js": "^2.3.1",
|
|
151
177
|
"qrcode-terminal": "^0.12.0",
|
|
152
|
-
"react": "^19.2.0",
|
|
153
|
-
"react-dom": "^19.2.0",
|
|
154
178
|
"read-package-up": "^11.0.0",
|
|
155
179
|
"sharp": "^0.34.4",
|
|
156
180
|
"shell-quote": "^1.8.3",
|
|
@@ -164,21 +188,13 @@
|
|
|
164
188
|
"turndown": "^7.2.0",
|
|
165
189
|
"turndown-plugin-gfm": "^1.0.2",
|
|
166
190
|
"undici": "^7.10.0",
|
|
191
|
+
"vite": "^8.0.14",
|
|
192
|
+
"vitest": "^4.1.7",
|
|
167
193
|
"xml2js": "^0.6.0",
|
|
168
194
|
"yargs": "^17.7.2",
|
|
169
195
|
"yauzl": "^3.0.0",
|
|
170
196
|
"zod": "^4.0.0"
|
|
171
197
|
},
|
|
172
|
-
"overrides": {
|
|
173
|
-
"archiver": "^8.0.0",
|
|
174
|
-
"unzipper": "^0.12.3",
|
|
175
|
-
"fast-csv": "^5.0.7",
|
|
176
|
-
"encoding-sniffer": "^1.0.2",
|
|
177
|
-
"gaxios": "^7.1.4",
|
|
178
|
-
"exceljs": {
|
|
179
|
-
"uuid": "^9.0.1"
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
198
|
"optionalDependencies": {
|
|
183
199
|
"@lydell/node-pty": "1.1.0",
|
|
184
200
|
"@lydell/node-pty-darwin-arm64": "1.1.0",
|
package/scripts/check-publish.js
CHANGED
|
@@ -28,6 +28,10 @@ if (process.cwd().includes('packages')) {
|
|
|
28
28
|
|
|
29
29
|
// Check 2: Bundle exists
|
|
30
30
|
const bundlePath = path.join(projectRoot, 'bundle/fss-link.js');
|
|
31
|
+
// The bin (bundle/fss-link.js) is a tiny bootstrap; the real code lives in
|
|
32
|
+
// bundle/fss-link-main.js, which the bootstrap dynamically imports. Size and
|
|
33
|
+
// version-content checks must target the main bundle, not the bootstrap.
|
|
34
|
+
const mainBundlePath = path.join(projectRoot, 'bundle/fss-link-main.js');
|
|
31
35
|
if (!fs.existsSync(bundlePath)) {
|
|
32
36
|
console.error('❌ ERROR: bundle/fss-link.js not found');
|
|
33
37
|
console.error(' Run: npm run bundle\n');
|
|
@@ -56,23 +60,30 @@ if (!fs.existsSync(bundlePath)) {
|
|
|
56
60
|
console.log('✅ Bundle has correct shebang');
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
// Check 5:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
console.error(
|
|
63
|
-
console.error(' Expected: > 1 MB');
|
|
64
|
-
console.error(' This may indicate an incomplete build\n');
|
|
63
|
+
// Check 5: Main bundle exists and is a reasonable size (> 1MB)
|
|
64
|
+
if (!fs.existsSync(mainBundlePath)) {
|
|
65
|
+
console.error('❌ ERROR: bundle/fss-link-main.js not found');
|
|
66
|
+
console.error(' The bootstrap bin imports it — Run: npm run bundle\n');
|
|
65
67
|
errors++;
|
|
66
68
|
} else {
|
|
67
|
-
|
|
69
|
+
const mainStats = fs.statSync(mainBundlePath);
|
|
70
|
+
const sizeMB = (mainStats.size / (1024 * 1024)).toFixed(2);
|
|
71
|
+
if (mainStats.size < 1024 * 1024) {
|
|
72
|
+
console.error(`❌ ERROR: Main bundle suspiciously small (${sizeMB} MB)`);
|
|
73
|
+
console.error(' Expected: > 1 MB');
|
|
74
|
+
console.error(' This may indicate an incomplete build\n');
|
|
75
|
+
errors++;
|
|
76
|
+
} else {
|
|
77
|
+
console.log(`✅ Main bundle size reasonable (${sizeMB} MB)`);
|
|
78
|
+
}
|
|
68
79
|
}
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
// Check 6: Version in bundle matches package.json
|
|
72
83
|
const pkgPath = path.join(projectRoot, 'package.json');
|
|
73
84
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
74
|
-
if (fs.existsSync(
|
|
75
|
-
const bundleContent = fs.readFileSync(
|
|
85
|
+
if (fs.existsSync(mainBundlePath)) {
|
|
86
|
+
const bundleContent = fs.readFileSync(mainBundlePath, 'utf8');
|
|
76
87
|
// Check for version in various formats: "version":"1.2.8", version = "1.2.8", etc.
|
|
77
88
|
const versionPatterns = [
|
|
78
89
|
`"version":"${pkg.version}"`,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// See the License for the specific language governing permissions and
|
|
18
18
|
// limitations under the License.
|
|
19
19
|
|
|
20
|
-
import { copyFileSync, existsSync, mkdirSync } from 'fs';
|
|
20
|
+
import { copyFileSync, existsSync, mkdirSync, chmodSync } from 'fs';
|
|
21
21
|
import { dirname, join, basename } from 'path';
|
|
22
22
|
import { fileURLToPath } from 'url';
|
|
23
23
|
import { glob } from 'glob';
|
|
@@ -31,6 +31,15 @@ if (!existsSync(bundleDir)) {
|
|
|
31
31
|
mkdirSync(bundleDir);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// Install the bootstrap as the published bin (bundle/fss-link.js). It forces
|
|
35
|
+
// React's production build, then dynamically imports the esbuild output
|
|
36
|
+
// (bundle/fss-link-main.js). See fss-docs/DESIGN-NOTE-react-production-mode.md.
|
|
37
|
+
const bootstrapSrc = join(root, 'packages', 'cli', 'bootstrap.mjs');
|
|
38
|
+
const bootstrapDest = join(bundleDir, 'fss-link.js');
|
|
39
|
+
copyFileSync(bootstrapSrc, bootstrapDest);
|
|
40
|
+
chmodSync(bootstrapDest, 0o755);
|
|
41
|
+
console.log('Bootstrap bin installed at bundle/fss-link.js');
|
|
42
|
+
|
|
34
43
|
// Find and copy all .sb files from packages to the root of the bundle directory
|
|
35
44
|
const sbFiles = glob.sync('packages/**/*.sb', { cwd: root });
|
|
36
45
|
for (const file of sbFiles) {
|
|
@@ -7,6 +7,24 @@
|
|
|
7
7
|
// FSS Link postinstall guidance — printed after npm install -g fss-link
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
// Patch tr46 to use punycode.js instead of the deprecated built-in punycode module.
|
|
14
|
+
// This eliminates the Node 22 DEP0040 deprecation warning from the
|
|
15
|
+
// jsdom → whatwg-url → tr46 dependency chain.
|
|
16
|
+
try {
|
|
17
|
+
const tr46Path = path.join(__dirname, '..', 'node_modules', 'tr46', 'index.js');
|
|
18
|
+
if (fs.existsSync(tr46Path)) {
|
|
19
|
+
const content = fs.readFileSync(tr46Path, 'utf8');
|
|
20
|
+
if (content.includes('require("punycode/")')) {
|
|
21
|
+
fs.writeFileSync(tr46Path, content.replace('require("punycode/")', 'require("punycode.js")'));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// Non-fatal — tr46 may not be installed or patch already applied
|
|
26
|
+
}
|
|
27
|
+
|
|
10
28
|
const lines = [
|
|
11
29
|
'',
|
|
12
30
|
'╔════════════════════════════════════════╗',
|
package/scripts/version.js
CHANGED
|
@@ -36,15 +36,22 @@ if (!versionType) {
|
|
|
36
36
|
// 2. Bump the version in the root and all workspace package.json files.
|
|
37
37
|
run(`npm version ${versionType} --no-git-tag-version --allow-same-version`);
|
|
38
38
|
|
|
39
|
-
// 3. Get all workspaces and filter out the
|
|
39
|
+
// 3. Get all workspaces and filter out the ones we don't want to version.
|
|
40
40
|
const workspacesToExclude = [];
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
);
|
|
41
|
+
const rootPkg = readJson(resolve(process.cwd(), 'package.json'));
|
|
42
|
+
const workspaceDirs = rootPkg.workspaces || [];
|
|
43
|
+
const workspacesToVersion = [];
|
|
44
|
+
for (const dir of workspaceDirs) {
|
|
45
|
+
const pkgPath = resolve(process.cwd(), dir, 'package.json');
|
|
46
|
+
try {
|
|
47
|
+
const wsPkg = readJson(pkgPath);
|
|
48
|
+
if (!workspacesToExclude.includes(wsPkg.name)) {
|
|
49
|
+
workspacesToVersion.push(wsPkg.name);
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
// Skip directories without package.json
|
|
53
|
+
}
|
|
54
|
+
}
|
|
48
55
|
|
|
49
56
|
for (const workspaceName of workspacesToVersion) {
|
|
50
57
|
run(
|
|
@@ -78,6 +85,7 @@ if (cliPackageJson.config?.sandboxImageUri) {
|
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
// 8. Run `npm install` to update package-lock.json.
|
|
81
|
-
|
|
88
|
+
// --ignore-scripts prevents the prepare hook (bundle) from firing before deps exist.
|
|
89
|
+
run('npm install --ignore-scripts');
|
|
82
90
|
|
|
83
91
|
console.log(`Successfully bumped versions to v${newVersion}.`);
|