@rip-lang/swarm 1.2.5 → 1.2.7

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.
Files changed (3) hide show
  1. package/README.md +16 -0
  2. package/package.json +1 -1
  3. package/swarm.rip +44 -22
package/README.md CHANGED
@@ -214,3 +214,19 @@ swarm { setup, perform }
214
214
  rip download-tests.rip tests.txt -w 40
215
215
  # 15,000 tests across 40 workers — finishes in minutes
216
216
  ```
217
+
218
+ ## Troubleshooting
219
+
220
+ ### Progress bar text appears black in VS Code / Cursor
221
+
222
+ VS Code's terminal has a "minimum contrast ratio" feature that overrides
223
+ foreground colors. This can turn white progress text black. To fix it,
224
+ add this to your VS Code or Cursor settings:
225
+
226
+ ```json
227
+ "terminal.integrated.minimumContrastRatio": 1
228
+ ```
229
+
230
+ This disables the contrast adjustment and lets ANSI colors render as
231
+ intended. The progress display works correctly in standard terminals
232
+ (iTerm2, Terminal.app, etc.) without any changes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rip-lang/swarm",
3
- "version": "1.2.5",
3
+ "version": "1.2.7",
4
4
  "description": "Parallel job runner with worker threads — setup once, swarm many",
5
5
  "type": "module",
6
6
  "main": "swarm.rip",
package/swarm.rip CHANGED
@@ -87,8 +87,9 @@ hex = (str) ->
87
87
  [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)].join(';')
88
88
  _hex[str] = result
89
89
 
90
- fg = (rgb) -> if rgb then "\x1b[38;2;#{hex(rgb)}m" else "\x1b[39m"
91
- bg = (rgb) -> if rgb then "\x1b[48;2;#{hex(rgb)}m" else "\x1b[49m"
90
+ fg = (rgb) -> if rgb then "\x1b[38;2;#{hex(rgb)}m" else "\x1b[39m"
91
+ bg = (rgb) -> if rgb then "\x1b[48;2;#{hex(rgb)}m" else "\x1b[49m"
92
+ fgw =! fg("fff")
92
93
 
93
94
  # ==============================================================================
94
95
  # Progress display
@@ -113,13 +114,14 @@ draw = (state) ->
113
114
  ppct = (done + died) / jobs
114
115
  most = Math.max(...Object.values(info), 1)
115
116
 
116
- # worker bars
117
+ # worker bars — set colors once, just move cursor per row
118
+ write fgw + bg("5383ec")
117
119
  for slot, count of info
118
120
  tpct = count / most
119
121
  cols = Math.floor(ppct * tpct * wide)
120
- write go(parseInt(slot) + 1, len + 5) + fg("fff") + bg("5383ec") + char.repeat(cols) + fg() + bg()
122
+ write go(parseInt(slot) + 1, len + 5) + char.repeat(cols)
121
123
 
122
- # summary bar
124
+ # summary bar — one atomic write
123
125
  dpct = done / jobs
124
126
  lpct = live / jobs
125
127
  gcol = Math.floor(dpct * wide)
@@ -129,12 +131,11 @@ draw = (state) ->
129
131
 
130
132
  out = [
131
133
  go(row, len + 5)
132
- fg("fff")
133
- bg("58a65c") + char.repeat(gcol) # green (done)
134
- bg("f1bf42") + char.repeat(ycol) # yellow (live)
135
- bg("d85140") + " ".repeat(rcol) # red (rest)
134
+ bg("58a65c") + char.repeat(gcol) # green (done)
135
+ bg("f1bf42") + char.repeat(ycol) # yellow (live)
136
+ bg("d85140") + " ".repeat(rcol) # red (rest)
136
137
  go(row, len + 5 + wide + 3)
137
- bg("5383ec") + " #{(ppct * 100).toFixed(1)}% " # blue (pct)
138
+ bg("5383ec") + " #{(ppct * 100).toFixed(1)}% " # blue (pct)
138
139
  if done > 0 then bg() + " " + bg("58a65c") + " #{done}/#{jobs} done "
139
140
  if died > 0 then bg() + " " + bg("d85140") + " #{died} died "
140
141
  ].filter(Boolean).join('') + fg() + bg()
@@ -166,17 +167,17 @@ export swarm = (opts = {}) ->
166
167
 
167
168
  if workers < 1
168
169
  console.error 'error: workers must be at least 1'
169
- process.exit(1)
170
+ exit(1)
170
171
 
171
172
  if doreset
172
173
  rmSync(_dir, { recursive: true, force: true })
173
174
  console.log 'removed .swarm directory'
174
- process.exit(0)
175
+ exit(0)
175
176
 
176
177
  # run setup
177
178
  unless typeof opts.perform is 'function'
178
179
  console.error 'error: perform() function is required'
179
- process.exit(1)
180
+ exit(1)
180
181
 
181
182
  context = {}
182
183
  if typeof opts.setup is 'function'
@@ -187,13 +188,13 @@ export swarm = (opts = {}) ->
187
188
  # read task list
188
189
  unless existsSync(_todo)
189
190
  console.error 'error: no .swarm/todo directory found (did setup run?)'
190
- process.exit(1)
191
+ exit(1)
191
192
 
192
193
  tasks = readdirSync(_todo).sort().map (f) -> join(_todo, f)
193
194
 
194
195
  if tasks.length is 0
195
196
  console.log 'no tasks to process'
196
- process.exit(0)
197
+ exit(0)
197
198
 
198
199
  jobs = tasks.length
199
200
 
@@ -213,12 +214,28 @@ export swarm = (opts = {}) ->
213
214
  catch
214
215
  null
215
216
 
217
+ # pre-flight: check workers can resolve modules from script directory
218
+ dir = dirname(scriptPath)
219
+ found = false
220
+ loop
221
+ if existsSync(join(dir, 'node_modules', '@rip-lang', 'swarm'))
222
+ found = true
223
+ break
224
+ parent = dirname(dir)
225
+ break if parent is dir
226
+ dir = parent
227
+ unless found
228
+ console.error "swarm: no node_modules found — workers won't be able to load modules"
229
+ console.error "hint: run 'bun add @rip-lang/all' in your project directory"
230
+ exit 1
231
+
216
232
  # state
217
- live = 0
218
- done = 0
219
- died = 0
220
- info = {}
221
- taskIdx = 0
233
+ live = 0
234
+ done = 0
235
+ died = 0
236
+ info = {}
237
+ taskIdx = 0
238
+ fatal = false
222
239
  inflight = {} # slot → taskPath (track in-flight tasks for crash recovery)
223
240
  lastTask = {} # slot → last completed task name (for display)
224
241
 
@@ -226,7 +243,7 @@ export swarm = (opts = {}) ->
226
243
  process.on 'SIGINT', ->
227
244
  cursor(true) unless quiet
228
245
  write go(workers + 5, 1) + "\n" unless quiet
229
- process.exit(1)
246
+ exit(1)
230
247
  unless quiet
231
248
  process.on 'SIGWINCH', ->
232
249
  drawFrame(workers)
@@ -276,6 +293,11 @@ export swarm = (opts = {}) ->
276
293
  w.on 'message', (msg) ->
277
294
  switch msg.type
278
295
  when 'error'
296
+ unless fatal
297
+ fatal = true
298
+ cursor(true) unless quiet
299
+ console.error "\nswarm: #{msg.error}"
300
+ console.error "hint: run 'bun add @rip-lang/all' in your project directory" if msg.error.includes('Cannot find module')
279
301
  writeFileSync('.swarm/errors.log', "worker #{slot} startup: #{msg.error}\n", { flag: 'a' }) if existsSync(_dir)
280
302
  when 'ready'
281
303
  dispatchNext(w, slot)
@@ -332,7 +354,7 @@ export swarm = (opts = {}) ->
332
354
  s = parseInt(slot)
333
355
  n = info[slot]
334
356
  rate = if secs > 0 then (n / secs).toFixed(1) else '—'
335
- write go(s + 1, len + 5) + fg("fff") + bg("5383ec") + char.repeat(wide) + fg() + bg() + " │ #{n} jobs @ #{rate}/sec" + clear(true)
357
+ write go(s + 1, len + 5) + fgw + bg("5383ec") + char.repeat(wide) + fg() + bg() + " │ #{n} jobs @ #{rate}/sec" + clear(true)
336
358
  draw({ live: 0, done, died, jobs, workers, info })
337
359
  cursor(true)
338
360
  write go(workers + 5, 1)