@sleds/cli 0.1.11 → 0.1.14

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 +4 -1
  2. package/dist/sleds.mjs +1355 -524
  3. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -1,47 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  // @bun
3
3
  import { createRequire } from "node:module";
4
+ var __defProp = Object.defineProperty;
5
+ var __returnValue = (v) => v;
6
+ function __exportSetter(name, newValue) {
7
+ this[name] = __returnValue.bind(null, newValue);
8
+ }
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, {
12
+ get: all[name],
13
+ enumerable: true,
14
+ configurable: true,
15
+ set: __exportSetter.bind(all, name)
16
+ });
17
+ };
18
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
4
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
20
 
6
- // src/index.ts
7
- import { realpathSync } from "fs";
8
- import { homedir as homedir3, platform as platform2 } from "os";
9
- import { fileURLToPath } from "url";
10
-
11
- // src/commands/bar.ts
12
- import { execFileSync } from "node:child_process";
13
- import {
14
- chmodSync as chmodSync2,
15
- existsSync as existsSync5,
16
- lstatSync,
17
- mkdirSync as mkdirSync5,
18
- symlinkSync,
19
- unlinkSync,
20
- writeFileSync as writeFileSync5
21
- } from "node:fs";
22
- import { homedir } from "node:os";
23
- import { join as join5, resolve as resolve3 } from "node:path";
24
-
25
- // src/meter.ts
26
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "node:fs";
27
- import { join as join3 } from "node:path";
28
-
29
- // src/config.ts
30
- import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
31
- import { join as join2, resolve as resolve2 } from "node:path";
32
-
33
21
  // src/settings.ts
34
22
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
35
23
  import { dirname, join, resolve } from "node:path";
36
- var BASE_URL_KEY = "ANTHROPIC_BASE_URL";
37
- var AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN";
38
- var claudeDir = (home, env = process.env) => {
24
+ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN", claudeDir = (home, env = process.env) => {
39
25
  const o = env.CLAUDE_CONFIG_DIR?.trim();
40
26
  return o ? resolve(home, o) : join(home, ".claude");
41
- };
42
- var hasHomeOverride = (env = process.env) => Boolean(env.SLEDS_HOME?.trim() || env.CLAUDE_CONFIG_DIR?.trim());
43
- var settingsPath = (home) => join(claudeDir(home), "settings.json");
44
- var sledsCommand = (sub, home, env = process.env) => {
27
+ }, hasHomeOverride = (env = process.env) => Boolean(env.SLEDS_HOME?.trim() || env.CLAUDE_CONFIG_DIR?.trim()), settingsPath = (home) => join(claudeDir(home), "settings.json"), sledsCommand = (sub, home, env = process.env) => {
45
28
  const parts = [];
46
29
  const sh = env.SLEDS_HOME?.trim();
47
30
  const cc = env.CLAUDE_CONFIG_DIR?.trim();
@@ -51,8 +34,7 @@ var sledsCommand = (sub, home, env = process.env) => {
51
34
  parts.push(`CLAUDE_CONFIG_DIR=${shellWord(resolve(home, cc))}`);
52
35
  parts.push(`sleds ${sub}`);
53
36
  return parts.join(" ");
54
- };
55
- var sledsCommandsIn = (settings) => {
37
+ }, sledsCommandsIn = (settings) => {
56
38
  const out = [];
57
39
  const visit = (v) => {
58
40
  if (Array.isArray(v)) {
@@ -70,74 +52,50 @@ var sledsCommandsIn = (settings) => {
70
52
  visit(settings[STATUS_LINE_KEY]);
71
53
  visit(settings.hooks);
72
54
  return out;
73
- };
74
- var bakedHome = (command) => command.match(/(?:^|\s)SLEDS_HOME=('([^']*)'|(\S+))/)?.[2] ?? command.match(/(?:^|\s)SLEDS_HOME=(\S+)/)?.[1];
75
- var shellWord = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
76
- var backupPath = (home) => join(claudeDir(home), "settings.json.sleds-backup");
77
- var tokenPath = (home, env = process.env) => {
55
+ }, bakedHome = (command) => command.match(/(?:^|\s)SLEDS_HOME=('([^']*)'|(\S+))/)?.[2] ?? command.match(/(?:^|\s)SLEDS_HOME=(\S+)/)?.[1], shellWord = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`, backupPath = (home) => join(claudeDir(home), "settings.json.sleds-backup"), tokenPath = (home, env = process.env) => {
78
56
  const o = env.SLEDS_HOME?.trim();
79
57
  return o ? join(resolve(home, o), ".sleds-token") : join(home, ".sleds-token");
80
- };
81
- var skillDir = (home) => join(claudeDir(home), "skills", "sled");
82
- var skillPath = (home) => join(skillDir(home), "SKILL.md");
83
- var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
84
- var envOf = (settings) => {
58
+ }, skillDir = (home) => join(claudeDir(home), "skills", "sled"), skillPath = (home) => join(skillDir(home), "SKILL.md"), isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v), envOf = (settings) => {
85
59
  const env = settings.env;
86
60
  return isObject(env) ? env : {};
87
- };
88
- var applyProxy = (settings, cfg) => ({
61
+ }, applyProxy = (settings, cfg) => ({
89
62
  ...settings,
90
63
  env: { ...envOf(settings), [BASE_URL_KEY]: cfg.url, [AUTH_TOKEN_KEY]: cfg.token }
91
- });
92
- var removeProxy = (settings) => {
64
+ }), removeProxy = (settings) => {
93
65
  if (!isObject(settings.env))
94
66
  return { ...settings };
95
67
  const { [BASE_URL_KEY]: _url, [AUTH_TOKEN_KEY]: _token, ...rest } = settings.env;
96
68
  return { ...settings, env: rest };
97
- };
98
- var readProxy = (settings) => {
69
+ }, readProxy = (settings) => {
99
70
  const env = envOf(settings);
100
71
  const url = env[BASE_URL_KEY];
101
72
  const token = env[AUTH_TOKEN_KEY];
102
73
  if (typeof url !== "string" || typeof token !== "string")
103
74
  return null;
104
75
  return { url, token };
105
- };
106
- var STATUS_LINE_KEY = "statusLine";
107
- var statusLineEntry = (command) => ({ type: "command", command });
108
- var isSledsStatusLine = (value) => isObject(value) && value.type === "command" && typeof value.command === "string" && /\bstatusline\b/.test(value.command);
109
- var applyStatusLine = (settings, command) => {
76
+ }, STATUS_LINE_KEY = "statusLine", statusLineEntry = (command) => ({ type: "command", command }), isSledsStatusLine = (value) => isObject(value) && value.type === "command" && typeof value.command === "string" && /\bstatusline\b/.test(value.command), applyStatusLine = (settings, command) => {
110
77
  const current = settings[STATUS_LINE_KEY];
111
78
  const prior = current === undefined || isSledsStatusLine(current) ? undefined : current;
112
79
  return { settings: { ...settings, [STATUS_LINE_KEY]: statusLineEntry(command) }, prior };
113
- };
114
- var removeStatusLine = (settings, prior) => {
80
+ }, removeStatusLine = (settings, prior) => {
115
81
  if (!isSledsStatusLine(settings[STATUS_LINE_KEY]))
116
82
  return { ...settings };
117
83
  const { [STATUS_LINE_KEY]: _ours, ...rest } = settings;
118
84
  return prior === undefined ? rest : { ...rest, [STATUS_LINE_KEY]: prior };
119
- };
120
- var HOOKS_KEY = "hooks";
121
- var NOTIFICATION_EVENT = "Notification";
122
- var SEAT_EVENT_MATCHER = "quota_auto_resume_fired|quota_auto_resume_stale|quota_auto_resume_disabled";
123
- var SEAT_EVENT_TIMEOUT_S = 10;
124
- var isSledsSeatHook = (entry) => {
85
+ }, HOOKS_KEY = "hooks", NOTIFICATION_EVENT = "Notification", SEAT_EVENT_MATCHER = "quota_auto_resume_fired|quota_auto_resume_stale|quota_auto_resume_disabled", SEAT_EVENT_TIMEOUT_S = 10, isSledsSeatHook = (entry) => {
125
86
  if (!isObject(entry) || !Array.isArray(entry.hooks))
126
87
  return false;
127
88
  return entry.hooks.some((h) => isObject(h) && h.type === "command" && typeof h.command === "string" && /\bseat-event\b/.test(h.command));
128
- };
129
- var seatHookEntry = (command) => ({
89
+ }, seatHookEntry = (command) => ({
130
90
  matcher: SEAT_EVENT_MATCHER,
131
91
  hooks: [{ type: "command", command, timeout: SEAT_EVENT_TIMEOUT_S }]
132
- });
133
- var notificationHooks = (settings) => {
92
+ }), notificationHooks = (settings) => {
134
93
  const hooks = settings[HOOKS_KEY];
135
94
  if (!isObject(hooks))
136
95
  return [];
137
96
  const list = hooks[NOTIFICATION_EVENT];
138
97
  return Array.isArray(list) ? list : [];
139
- };
140
- var withNotificationHooks = (settings, list) => {
98
+ }, withNotificationHooks = (settings, list) => {
141
99
  const hooks = isObject(settings[HOOKS_KEY]) ? settings[HOOKS_KEY] : {};
142
100
  if (list.length === 0) {
143
101
  const { [NOTIFICATION_EVENT]: _gone, ...restHooks } = hooks;
@@ -148,41 +106,31 @@ var withNotificationHooks = (settings, list) => {
148
106
  return { ...settings, [HOOKS_KEY]: restHooks };
149
107
  }
150
108
  return { ...settings, [HOOKS_KEY]: { ...hooks, [NOTIFICATION_EVENT]: list } };
151
- };
152
- var applySeatHook = (settings, command) => {
109
+ }, applySeatHook = (settings, command) => {
153
110
  const list = notificationHooks(settings);
154
111
  const idx = list.findIndex(isSledsSeatHook);
155
112
  const entry = seatHookEntry(command);
156
113
  const next = idx === -1 ? [...list, entry] : list.map((e, i) => i === idx ? entry : e);
157
114
  return withNotificationHooks(settings, next);
158
- };
159
- var removeSeatHook = (settings) => {
115
+ }, removeSeatHook = (settings) => {
160
116
  const list = notificationHooks(settings);
161
117
  if (!list.some(isSledsSeatHook))
162
118
  return { ...settings };
163
119
  return withNotificationHooks(settings, list.filter((e) => !isSledsSeatHook(e)));
164
- };
165
- var hasSeatHook = (settings) => notificationHooks(settings).some(isSledsSeatHook);
166
- var POST_TOOL_EVENT = "PostToolUse";
167
- var SPEC_EVENT_MATCHER = "Write|Edit|MultiEdit";
168
- var SPEC_EVENT_TIMEOUT_S = 5;
169
- var isSledsSpecHook = (entry) => {
120
+ }, hasSeatHook = (settings) => notificationHooks(settings).some(isSledsSeatHook), POST_TOOL_EVENT = "PostToolUse", SPEC_EVENT_MATCHER = "Write|Edit|MultiEdit", SPEC_EVENT_TIMEOUT_S = 5, isSledsSpecHook = (entry) => {
170
121
  if (!isObject(entry) || !Array.isArray(entry.hooks))
171
122
  return false;
172
123
  return entry.hooks.some((h) => isObject(h) && h.type === "command" && typeof h.command === "string" && /\bspec-event\b/.test(h.command));
173
- };
174
- var specHookEntry = (command) => ({
124
+ }, specHookEntry = (command) => ({
175
125
  matcher: SPEC_EVENT_MATCHER,
176
126
  hooks: [{ type: "command", command, timeout: SPEC_EVENT_TIMEOUT_S }]
177
- });
178
- var eventHooks = (settings, event) => {
127
+ }), eventHooks = (settings, event) => {
179
128
  const hooks = settings[HOOKS_KEY];
180
129
  if (!isObject(hooks))
181
130
  return [];
182
131
  const list = hooks[event];
183
132
  return Array.isArray(list) ? list : [];
184
- };
185
- var withEventHooks = (settings, event, list) => {
133
+ }, withEventHooks = (settings, event, list) => {
186
134
  const hooks = isObject(settings[HOOKS_KEY]) ? settings[HOOKS_KEY] : {};
187
135
  if (list.length === 0) {
188
136
  const { [event]: _gone, ...restHooks } = hooks;
@@ -193,27 +141,19 @@ var withEventHooks = (settings, event, list) => {
193
141
  return { ...settings, [HOOKS_KEY]: restHooks };
194
142
  }
195
143
  return { ...settings, [HOOKS_KEY]: { ...hooks, [event]: list } };
196
- };
197
- var applySpecHook = (settings, command) => {
144
+ }, applySpecHook = (settings, command) => {
198
145
  const list = eventHooks(settings, POST_TOOL_EVENT);
199
146
  const idx = list.findIndex(isSledsSpecHook);
200
147
  const entry = specHookEntry(command);
201
148
  const next = idx === -1 ? [...list, entry] : list.map((e, i) => i === idx ? entry : e);
202
149
  return withEventHooks(settings, POST_TOOL_EVENT, next);
203
- };
204
- var removeSpecHook = (settings) => {
150
+ }, removeSpecHook = (settings) => {
205
151
  const list = eventHooks(settings, POST_TOOL_EVENT);
206
152
  if (!list.some(isSledsSpecHook))
207
153
  return { ...settings };
208
154
  return withEventHooks(settings, POST_TOOL_EVENT, list.filter((e) => !isSledsSpecHook(e)));
209
- };
210
- var hasSpecHook = (settings) => eventHooks(settings, POST_TOOL_EVENT).some(isSledsSpecHook);
211
- var isSeatToken = (token) => /^sled_[A-Za-z0-9_-]+$/.test(token);
212
- var maskToken = (token) => token.length <= 9 ? `${token.slice(0, 5)}…` : `${token.slice(0, 9)}…`;
213
- var serialize = (settings) => `${JSON.stringify(settings, null, 2)}
214
- `;
215
- var hasClaudeCode = (home) => existsSync(claudeDir(home));
216
- var readSettings = (home) => {
155
+ }, hasSpecHook = (settings) => eventHooks(settings, POST_TOOL_EVENT).some(isSledsSpecHook), isSeatToken = (token) => /^sled_[A-Za-z0-9_-]+$/.test(token), maskToken = (token) => token.length <= 9 ? `${token.slice(0, 5)}…` : `${token.slice(0, 9)}…`, serialize = (settings) => `${JSON.stringify(settings, null, 2)}
156
+ `, hasClaudeCode = (home) => existsSync(claudeDir(home)), readSettings = (home) => {
217
157
  const p = settingsPath(home);
218
158
  if (!existsSync(p))
219
159
  return {};
@@ -221,44 +161,39 @@ var readSettings = (home) => {
221
161
  if (!isObject(parsed))
222
162
  throw new Error(`${p} is not a JSON object`);
223
163
  return parsed;
224
- };
225
- var writeSettings = (home, settings) => {
164
+ }, writeSettings = (home, settings) => {
226
165
  mkdirSync(claudeDir(home), { recursive: true });
227
166
  const target = settingsPath(home);
228
167
  const tmp = `${target}.${process.pid}.tmp`;
229
168
  writeFileSync(tmp, serialize(settings));
230
169
  renameSync(tmp, target);
231
- };
232
- var backupSettings = (home) => {
170
+ }, backupSettings = (home) => {
233
171
  const src = settingsPath(home);
234
172
  const dst = backupPath(home);
235
173
  if (!existsSync(src) || existsSync(dst))
236
174
  return false;
237
175
  writeFileSync(dst, readFileSync(src));
238
176
  return true;
239
- };
240
- var readTokenFile = (home) => {
177
+ }, readTokenFile = (home) => {
241
178
  const p = tokenPath(home);
242
179
  if (!existsSync(p))
243
180
  return null;
244
181
  const t = readFileSync(p, "utf8").trim();
245
182
  return t.length > 0 ? t : null;
246
- };
247
- var writeTokenFile = (home, token) => {
183
+ }, writeTokenFile = (home, token) => {
248
184
  mkdirSync(dirname(tokenPath(home)), { recursive: true, mode: 448 });
249
185
  writeFileSync(tokenPath(home), `${token}
250
186
  `, { mode: 384 });
251
187
  };
188
+ var init_settings = () => {};
252
189
 
253
190
  // src/config.ts
191
+ import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
192
+ import { join as join2, resolve as resolve2 } from "node:path";
254
193
  var configDir = (home, env = process.env) => {
255
194
  const o = env.SLEDS_HOME?.trim();
256
195
  return o ? resolve2(home, o) : join2(home, ".config", "sleds");
257
- };
258
- var configPath = (home) => join2(configDir(home), "config.json");
259
- var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
260
- var optString = (v) => typeof v === "string" ? v : undefined;
261
- var parsePriorEnv = (v) => {
196
+ }, configPath = (home) => join2(configDir(home), "config.json"), isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), optString = (v) => typeof v === "string" ? v : undefined, parsePriorEnv = (v) => {
262
197
  if (!isObject2(v))
263
198
  return;
264
199
  const out = {};
@@ -271,9 +206,7 @@ var parsePriorEnv = (v) => {
271
206
  if (typeof v.hadEnv === "boolean")
272
207
  out.hadEnv = v.hadEnv;
273
208
  return out;
274
- };
275
- var parseSchedule = (v) => v === "launchd" || v === "systemd" || v === "manual" ? v : undefined;
276
- var parseConfig = (raw) => {
209
+ }, parseSchedule = (v) => v === "launchd" || v === "systemd" || v === "manual" ? v : undefined, parseConfig = (raw) => {
277
210
  if (!isObject2(raw))
278
211
  return {};
279
212
  const cfg = {};
@@ -310,9 +243,26 @@ var parseConfig = (raw) => {
310
243
  const org = parseOrgInfo(raw.org);
311
244
  if (org)
312
245
  cfg.org = org;
246
+ const musher = parseMusher(raw.musher);
247
+ if (musher)
248
+ cfg.musher = musher;
313
249
  return cfg;
314
- };
315
- var parseOrgInfo = (raw) => {
250
+ }, parseMusher = (raw) => {
251
+ if (!isObject2(raw))
252
+ return;
253
+ const out = {};
254
+ const pm = optString(raw.permissionMode);
255
+ const cp = optString(raw.claudePath);
256
+ if (pm)
257
+ out.permissionMode = pm;
258
+ if (cp)
259
+ out.claudePath = cp;
260
+ if (typeof raw.timeoutMinutes === "number" && raw.timeoutMinutes > 0)
261
+ out.timeoutMinutes = raw.timeoutMinutes;
262
+ if (raw.agent === "launchd" || raw.agent === "none")
263
+ out.agent = raw.agent;
264
+ return out;
265
+ }, parseOrgInfo = (raw) => {
316
266
  if (!isObject2(raw) || !isObject2(raw.capabilities))
317
267
  return;
318
268
  const name = optString(raw.name);
@@ -334,14 +284,12 @@ var parseOrgInfo = (raw) => {
334
284
  },
335
285
  mode: raw.mode === "proxy-always" ? "proxy-always" : "seat-first"
336
286
  };
337
- };
338
- var readConfig = (home) => {
287
+ }, readConfig = (home) => {
339
288
  const p = configPath(home);
340
289
  if (!existsSync2(p))
341
290
  return {};
342
291
  return parseConfig(JSON.parse(readFileSync2(p, "utf8")));
343
- };
344
- var writeConfig = (home, cfg) => {
292
+ }, writeConfig = (home, cfg) => {
345
293
  const dir = configDir(home);
346
294
  mkdirSync2(dir, { recursive: true, mode: 448 });
347
295
  const target = configPath(home);
@@ -351,14 +299,14 @@ var writeConfig = (home, cfg) => {
351
299
  renameSync2(tmp, target);
352
300
  chmodSync(target, 384);
353
301
  };
302
+ var init_config = __esm(() => {
303
+ init_settings();
304
+ });
354
305
 
355
306
  // src/meter.ts
356
- var METER_CACHE_TTL_MS = 1e4;
357
- var METER_TIMEOUT_MS = 1500;
358
- var isObject3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
359
- var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
360
- var str = (v) => typeof v === "string" ? v : undefined;
361
- var parseMeter = (raw) => {
307
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "node:fs";
308
+ import { join as join3 } from "node:path";
309
+ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, str = (v) => typeof v === "string" ? v : undefined, parseMeter = (raw) => {
362
310
  if (!isObject3(raw))
363
311
  return null;
364
312
  const seat = isObject3(raw.seat) ? str(raw.seat.email) : undefined;
@@ -413,18 +361,15 @@ var parseMeter = (raw) => {
413
361
  if (acc !== undefined)
414
362
  out.estimateAccuracyPct = acc;
415
363
  return out;
416
- };
417
- var usd = (n) => {
364
+ }, usd = (n) => {
418
365
  const abs = Math.abs(n);
419
366
  if (abs >= 1000)
420
367
  return `${n < 0 ? "-" : ""}$${Math.round(abs).toLocaleString("en-US")}`;
421
368
  return `${n < 0 ? "-" : ""}$${abs.toFixed(2)}`;
422
- };
423
- var usdRange = (low, high) => {
369
+ }, usdRange = (low, high) => {
424
370
  const d = high >= 10 ? 0 : 2;
425
371
  return `$${low.toFixed(d)}–${high.toFixed(d)}`;
426
- };
427
- var shortDuration = (ms) => {
372
+ }, shortDuration = (ms) => {
428
373
  const total = Math.max(0, Math.round(ms / 60000));
429
374
  const d = Math.floor(total / (24 * 60));
430
375
  const h = Math.floor(total % (24 * 60) / 60);
@@ -434,18 +379,7 @@ var shortDuration = (ms) => {
434
379
  if (h > 0)
435
380
  return `${h}h${String(m).padStart(2, "0")}m`;
436
381
  return `${m}m`;
437
- };
438
- var KIND_WORDS = {
439
- rate_limit: "rate limit hit",
440
- weekly_cap: "usage limit reached",
441
- budget: "budget exhausted",
442
- provider_down: "provider unavailable"
443
- };
444
- var kindInWords = (kind) => KIND_WORDS[kind] ?? kind.replace(/_/g, " ");
445
- var SEP = " · ";
446
- var PREFIX = "sleds ";
447
- var OFFLINE_LINE = `${PREFIX}offline`;
448
- var jobParts = (m) => {
382
+ }, KIND_WORDS, kindInWords = (kind) => KIND_WORDS[kind] ?? kind.replace(/_/g, " "), SEP = " · ", PREFIX = "sleds ", OFFLINE_LINE, jobParts = (m) => {
449
383
  const parts = [];
450
384
  const [low, high] = m.jobs.queuedEstimateUsd;
451
385
  if (m.jobs.queued > 0) {
@@ -457,8 +391,7 @@ var jobParts = (m) => {
457
391
  if (m.jobs.deliveredToday > 0)
458
392
  parts.push(`${m.jobs.deliveredToday} delivered`);
459
393
  return parts;
460
- };
461
- var formatMeterLine = (m, now, seatFragment = null) => {
394
+ }, formatMeterLine = (m, now, seatFragment = null) => {
462
395
  if (!m)
463
396
  return seatFragment ? `${OFFLINE_LINE}${SEP}${seatFragment}` : OFFLINE_LINE;
464
397
  const parts = [`${usd(m.today.spentUsd)} today`];
@@ -473,10 +406,7 @@ var formatMeterLine = (m, now, seatFragment = null) => {
473
406
  parts.push(`allowance ${usd(m.allowance.remainingUsd)}`);
474
407
  parts.push(...jobParts(m));
475
408
  return `${PREFIX}${parts.join(SEP)}`;
476
- };
477
- var row = (label, value) => `${label.padEnd(12)}${value}`;
478
- var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
479
- var formatMeter = (m, now) => {
409
+ }, row = (label, value) => `${label.padEnd(12)}${value}`, plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`, formatMeter = (m, now) => {
480
410
  const lines = [row("Seat:", m.seat.email)];
481
411
  lines.push(row("Built today:", `${usd(m.today.spentUsd)}${SEP}${plural(m.today.requests, "request")}`));
482
412
  lines.push(row("Month:", m.month.ceilingUsd !== undefined ? `${usd(m.month.spentUsd)} of ${usd(m.month.ceilingUsd)} ceiling` : usd(m.month.spentUsd)));
@@ -500,9 +430,7 @@ var formatMeter = (m, now) => {
500
430
  if (m.estimateAccuracyPct !== undefined)
501
431
  lines.push(row("Estimates:", `${Math.round(m.estimateAccuracyPct)}% landed in band`));
502
432
  return lines;
503
- };
504
- var meterCachePath = (home) => join3(configDir(home), "meter.json");
505
- var readMeterCache = (home, now) => {
433
+ }, meterCachePath = (home) => join3(configDir(home), "meter.json"), readMeterCache = (home, now) => {
506
434
  const p = meterCachePath(home);
507
435
  if (!existsSync3(p))
508
436
  return null;
@@ -517,8 +445,7 @@ var readMeterCache = (home, now) => {
517
445
  } catch {
518
446
  return null;
519
447
  }
520
- };
521
- var writeMeterCache = (home, meter, now) => {
448
+ }, writeMeterCache = (home, meter, now) => {
522
449
  const dir = configDir(home);
523
450
  mkdirSync3(dir, { recursive: true, mode: 448 });
524
451
  const target = meterCachePath(home);
@@ -527,8 +454,7 @@ var writeMeterCache = (home, meter, now) => {
527
454
  writeFileSync3(tmp, `${JSON.stringify(file)}
528
455
  `, { mode: 384 });
529
456
  renameSync3(tmp, target);
530
- };
531
- var fetchMeter = async (o) => {
457
+ }, fetchMeter = async (o) => {
532
458
  const fetchFn = o.fetchFn ?? fetch;
533
459
  try {
534
460
  const res = await fetchFn(`${o.proxyUrl}/proxy/meter`, {
@@ -541,16 +467,14 @@ var fetchMeter = async (o) => {
541
467
  } catch {
542
468
  return null;
543
469
  }
544
- };
545
- var meterSource = (home) => {
470
+ }, meterSource = (home) => {
546
471
  const cfg = readConfig(home);
547
472
  const settings = existsSync3(settingsPath(home)) ? readSettings(home) : {};
548
473
  const inSettings = readProxy(settings);
549
474
  const proxyUrl = inSettings?.url ?? cfg.proxyUrl;
550
475
  const token = inSettings?.token ?? cfg.token;
551
476
  return proxyUrl && token ? { proxyUrl, token } : null;
552
- };
553
- var loadMeter = async (o) => {
477
+ }, loadMeter = async (o) => {
554
478
  const now = o.now ?? Date.now();
555
479
  const cached = readMeterCache(o.home, now);
556
480
  if (cached)
@@ -566,99 +490,548 @@ var loadMeter = async (o) => {
566
490
  }
567
491
  return fresh;
568
492
  };
493
+ var init_meter = __esm(() => {
494
+ init_config();
495
+ init_settings();
496
+ KIND_WORDS = {
497
+ rate_limit: "rate limit hit",
498
+ weekly_cap: "usage limit reached",
499
+ budget: "budget exhausted",
500
+ provider_down: "provider unavailable"
501
+ };
502
+ OFFLINE_LINE = `${PREFIX}offline`;
503
+ });
569
504
 
570
- // src/bar.ts
571
- var SEAT_BAR_FRESH_MS = 6 * 60 * 60 * 1000;
572
- var DASHBOARD_URL = "https://app.sleds.ai";
573
- var WARM_PCT = 75;
574
- var HOT_PCT = 90;
575
- var MONO = "font=Menlo size=12";
576
- var HOT_COLOR = "#ff453a";
577
- var WARM_COLOR = "#ff9f0a";
578
- var freshSeat = (w, now) => {
579
- if (!w)
505
+ // src/seat.ts
506
+ var exports_seat = {};
507
+ __export(exports_seat, {
508
+ SEAT_POST_MIN_DELTA_PCT: () => SEAT_POST_MIN_DELTA_PCT2,
509
+ SEAT_POST_MIN_INTERVAL_MS: () => SEAT_POST_MIN_INTERVAL_MS2,
510
+ SEAT_POST_TIMEOUT_MS: () => SEAT_POST_TIMEOUT_MS2,
511
+ SEAT_WALL_LINE: () => SEAT_WALL_LINE2,
512
+ SEAT_WALL_LINE_NO_KEY: () => SEAT_WALL_LINE_NO_KEY2,
513
+ STDIN_TIMEOUT_MS: () => STDIN_TIMEOUT_MS2,
514
+ bindingResetAt: () => bindingResetAt2,
515
+ formatSeatFragment: () => formatSeatFragment2,
516
+ markPosted: () => markPosted2,
517
+ parseResetsAt: () => parseResetsAt2,
518
+ parseSeatState: () => parseSeatState2,
519
+ parseSeatWindow: () => parseSeatWindow2,
520
+ postSeatWall: () => postSeatWall2,
521
+ postSeatWindow: () => postSeatWindow2,
522
+ readSeatState: () => readSeatState2,
523
+ readStdin: () => readStdin2,
524
+ seatSessionFromStdin: () => seatSessionFromStdin2,
525
+ seatStatePath: () => seatStatePath2,
526
+ seatWallDue: () => seatWallDue2,
527
+ seatWallLine: () => seatWallLine2,
528
+ seatWindowBody: () => seatWindowBody2,
529
+ seatWindowFromStdin: () => seatWindowFromStdin2,
530
+ shouldPostSeat: () => shouldPostSeat2,
531
+ writeSeatState: () => writeSeatState2
532
+ });
533
+ import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync5, writeFileSync as writeFileSync10 } from "node:fs";
534
+ import { join as join10 } from "node:path";
535
+ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_MS2 = 1500, STDIN_TIMEOUT_MS2 = 300, isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num5 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, EPOCH_MS_THRESHOLD2 = 100000000000, parseResetsAt2 = (v) => {
536
+ const n = num5(v);
537
+ if (n !== undefined) {
538
+ if (n <= 0)
539
+ return;
540
+ return n >= EPOCH_MS_THRESHOLD2 ? Math.round(n) : Math.round(n * 1000);
541
+ }
542
+ if (typeof v === "string" && v.trim() !== "") {
543
+ const parsed = Date.parse(v);
544
+ return Number.isFinite(parsed) ? parsed : undefined;
545
+ }
546
+ return;
547
+ }, parseBar2 = (v) => {
548
+ if (!isObject9(v))
549
+ return;
550
+ const usedPct = num5(v.used_percentage) ?? num5(v.usedPct);
551
+ const resetsAt = parseResetsAt2(v.resets_at ?? v.resetsAt);
552
+ if (usedPct === undefined || usedPct < 0 || resetsAt === undefined)
553
+ return;
554
+ return { usedPct, resetsAt };
555
+ }, parseSeatWindow2 = (raw, capturedAt) => {
556
+ if (!isObject9(raw))
580
557
  return null;
581
- return now - w.capturedAt < SEAT_BAR_FRESH_MS ? w : null;
582
- };
583
- var modelLabel = (bucket) => bucket.replace(/^(seven_day|five_hour|weekly|daily)_/, "").toLowerCase();
584
- var seatBars = (w) => {
558
+ const limits = isObject9(raw.rate_limits) ? raw.rate_limits : raw;
559
+ const fiveHour = parseBar2(limits.five_hour ?? limits.fiveHour);
560
+ const sevenDay = parseBar2(limits.seven_day ?? limits.sevenDay);
561
+ const spendLimit = parseBar2(limits.spend_limit ?? limits.spendLimit);
562
+ const models = {};
563
+ if (isObject9(limits.models))
564
+ for (const [k, v] of Object.entries(limits.models)) {
565
+ const bar = parseBar2(v);
566
+ if (bar)
567
+ models[k] = bar;
568
+ }
569
+ const usedCreditsUsd = num5(limits.usedCreditsUsd);
570
+ const hasModels = Object.keys(models).length > 0;
571
+ if (!fiveHour && !sevenDay && !spendLimit && !hasModels && usedCreditsUsd === undefined)
572
+ return null;
573
+ const out = { capturedAt };
574
+ if (fiveHour)
575
+ out.fiveHour = fiveHour;
576
+ if (sevenDay)
577
+ out.sevenDay = sevenDay;
578
+ if (spendLimit)
579
+ out.spendLimit = spendLimit;
580
+ if (hasModels)
581
+ out.models = models;
582
+ if (usedCreditsUsd !== undefined)
583
+ out.usedCreditsUsd = usedCreditsUsd;
584
+ return out;
585
+ }, seatWindowFromStdin2 = (text, capturedAt) => {
586
+ const trimmed = text.trim();
587
+ if (!trimmed)
588
+ return null;
589
+ try {
590
+ return parseSeatWindow2(JSON.parse(trimmed), capturedAt);
591
+ } catch {
592
+ return null;
593
+ }
594
+ }, formatSeatFragment2 = (w) => {
585
595
  if (!w)
586
- return [];
587
- const out = [];
596
+ return null;
597
+ const parts = [];
588
598
  if (w.sevenDay)
589
- out.push({ label: "wk", bar: w.sevenDay });
599
+ parts.push(`wk ${Math.round(w.sevenDay.usedPct)}%`);
590
600
  if (w.fiveHour)
591
- out.push({ label: "5h", bar: w.fiveHour });
601
+ parts.push(`5h ${Math.round(w.fiveHour.usedPct)}%`);
592
602
  if (w.spendLimit)
593
- out.push({ label: "spend", bar: w.spendLimit });
594
- for (const k of Object.keys(w.models ?? {}).sort()) {
595
- const bar = w.models?.[k];
596
- if (bar)
597
- out.push({ label: modelLabel(k), bar });
603
+ parts.push(`spend ${Math.round(w.spendLimit.usedPct)}%`);
604
+ if (parts.length === 0)
605
+ return null;
606
+ return `seat ${parts.join(SEP)}`;
607
+ }, seatStatePath2 = (home) => join10(configDir(home), "seat.json"), parseWindowStored2 = (v) => {
608
+ if (!isObject9(v))
609
+ return;
610
+ const capturedAt = num5(v.capturedAt);
611
+ if (capturedAt === undefined)
612
+ return;
613
+ return parseSeatWindow2(v, capturedAt) ?? undefined;
614
+ }, parseSeatState2 = (raw) => {
615
+ if (!isObject9(raw))
616
+ return {};
617
+ const out = {};
618
+ const window = parseWindowStored2(raw.window);
619
+ if (window)
620
+ out.window = window;
621
+ const lastPostedAt = num5(raw.lastPostedAt);
622
+ if (lastPostedAt !== undefined)
623
+ out.lastPostedAt = lastPostedAt;
624
+ if (isObject9(raw.lastPosted)) {
625
+ const lp = {};
626
+ const f = num5(raw.lastPosted.fiveHour);
627
+ const s = num5(raw.lastPosted.sevenDay);
628
+ const sp = num5(raw.lastPosted.spendLimit);
629
+ if (f !== undefined)
630
+ lp.fiveHour = f;
631
+ if (s !== undefined)
632
+ lp.sevenDay = s;
633
+ if (sp !== undefined)
634
+ lp.spendLimit = sp;
635
+ out.lastPosted = lp;
598
636
  }
637
+ const lastWallResetsAt = num5(raw.lastWallResetsAt);
638
+ if (lastWallResetsAt !== undefined)
639
+ out.lastWallResetsAt = lastWallResetsAt;
640
+ const session = parseSeatSession2(raw.session);
641
+ if (session)
642
+ out.session = session;
599
643
  return out;
644
+ }, parseSeatSession2 = (v) => {
645
+ if (!isObject9(v) || typeof v.id !== "string" || !v.id)
646
+ return;
647
+ const at = num5(v.at);
648
+ if (at === undefined)
649
+ return;
650
+ const out = { id: v.id, at };
651
+ if (typeof v.cwd === "string" && v.cwd)
652
+ out.cwd = v.cwd;
653
+ if (typeof v.model === "string" && v.model)
654
+ out.model = v.model;
655
+ return out;
656
+ }, seatSessionFromStdin2 = (text, now) => {
657
+ const trimmed = text.trim();
658
+ if (!trimmed)
659
+ return null;
660
+ try {
661
+ const raw = JSON.parse(trimmed);
662
+ if (!isObject9(raw) || typeof raw.session_id !== "string" || !raw.session_id)
663
+ return null;
664
+ const out = { id: raw.session_id, at: now };
665
+ if (typeof raw.cwd === "string" && raw.cwd)
666
+ out.cwd = raw.cwd;
667
+ const model = isObject9(raw.model) ? raw.model.id : raw.model;
668
+ if (typeof model === "string" && model)
669
+ out.model = model;
670
+ return out;
671
+ } catch {
672
+ return null;
673
+ }
674
+ }, bindingResetAt2 = (w, now) => {
675
+ if (!w)
676
+ return;
677
+ const bars = [
678
+ ...w.fiveHour ? [w.fiveHour] : [],
679
+ ...w.sevenDay ? [w.sevenDay] : [],
680
+ ...w.spendLimit ? [w.spendLimit] : [],
681
+ ...Object.values(w.models ?? {})
682
+ ].filter((b) => b.resetsAt > now);
683
+ const full = bars.filter((b) => b.usedPct >= 100);
684
+ const pick = (full.length ? full : bars).map((b) => b.resetsAt);
685
+ return pick.length ? Math.min(...pick) : undefined;
686
+ }, readSeatState2 = (home) => {
687
+ const p = seatStatePath2(home);
688
+ if (!existsSync13(p))
689
+ return {};
690
+ try {
691
+ return parseSeatState2(JSON.parse(readFileSync10(p, "utf8")));
692
+ } catch {
693
+ return {};
694
+ }
695
+ }, writeSeatState2 = (home, state) => {
696
+ const dir = configDir(home);
697
+ mkdirSync10(dir, { recursive: true, mode: 448 });
698
+ const target = seatStatePath2(home);
699
+ const tmp = `${target}.${process.pid}.tmp`;
700
+ writeFileSync10(tmp, `${JSON.stringify(state)}
701
+ `, { mode: 384 });
702
+ renameSync5(tmp, target);
703
+ }, pctOf2 = (b) => b?.usedPct, shouldPostSeat2 = (state, next, now) => {
704
+ if (state.lastPostedAt === undefined || !state.lastPosted)
705
+ return true;
706
+ if (now - state.lastPostedAt >= SEAT_POST_MIN_INTERVAL_MS2)
707
+ return true;
708
+ const pairs = [
709
+ [state.lastPosted.fiveHour, pctOf2(next.fiveHour)],
710
+ [state.lastPosted.sevenDay, pctOf2(next.sevenDay)],
711
+ [state.lastPosted.spendLimit, pctOf2(next.spendLimit)]
712
+ ];
713
+ for (const [prev, cur] of pairs) {
714
+ if (prev === undefined && cur === undefined)
715
+ continue;
716
+ if (prev === undefined || cur === undefined)
717
+ return true;
718
+ if (Math.abs(cur - prev) >= SEAT_POST_MIN_DELTA_PCT2)
719
+ return true;
720
+ }
721
+ return false;
722
+ }, markPosted2 = (state, w, now) => {
723
+ const lastPosted = {};
724
+ if (w.fiveHour)
725
+ lastPosted.fiveHour = w.fiveHour.usedPct;
726
+ if (w.sevenDay)
727
+ lastPosted.sevenDay = w.sevenDay.usedPct;
728
+ if (w.spendLimit)
729
+ lastPosted.spendLimit = w.spendLimit.usedPct;
730
+ return { ...state, window: w, lastPostedAt: now, lastPosted };
731
+ }, seatWallDue2 = (state, w) => {
732
+ if (!w?.sevenDay || w.sevenDay.usedPct < 100)
733
+ return false;
734
+ return state.lastWallResetsAt === undefined || state.lastWallResetsAt < w.sevenDay.resetsAt;
735
+ }, seatWindowBody2 = (w) => ({
736
+ capturedAt: w.capturedAt,
737
+ ...w.fiveHour ? { fiveHour: w.fiveHour } : {},
738
+ ...w.sevenDay ? { sevenDay: w.sevenDay } : {},
739
+ ...w.spendLimit ? { spendLimit: w.spendLimit } : {}
740
+ }), postJson2 = async (o, path, body) => {
741
+ const fetchFn = o.fetchFn ?? fetch;
742
+ try {
743
+ const res = await fetchFn(`${o.proxyUrl}${path}`, {
744
+ method: "POST",
745
+ headers: { authorization: `Bearer ${o.token}`, "content-type": "application/json" },
746
+ body: JSON.stringify(body),
747
+ signal: AbortSignal.timeout(o.timeoutMs ?? SEAT_POST_TIMEOUT_MS2)
748
+ });
749
+ return res.ok;
750
+ } catch {
751
+ return false;
752
+ }
753
+ }, postSeatWindow2 = (o, w) => postJson2(o, "/proxy/seat", seatWindowBody2(w)), postSeatWall2 = (o, input) => postJson2(o, "/proxy/seat-wall", input), SEAT_WALL_LINE2 = "Seat limit hit. sleds on --until-reset to continue on the org key, or sleds defer to resume here at the reset.", SEAT_WALL_LINE_NO_KEY2 = "Seat limit hit. sleds defer resumes this session on this Mac at the reset; /sled 5 to hand off — this org has no platform key to switch to.", seatWallLine2 = (hasUpstreamKey) => hasUpstreamKey ? SEAT_WALL_LINE2 : SEAT_WALL_LINE_NO_KEY2, readStdin2 = (timeoutMs, stdin = process.stdin) => new Promise((resolve4) => {
754
+ if (stdin.isTTY) {
755
+ resolve4("");
756
+ return;
757
+ }
758
+ let data = "";
759
+ let settled = false;
760
+ const finish = () => {
761
+ if (settled)
762
+ return;
763
+ settled = true;
764
+ clearTimeout(timer);
765
+ resolve4(data);
766
+ };
767
+ const timer = setTimeout(finish, timeoutMs);
768
+ stdin.setEncoding("utf8");
769
+ stdin.on("data", (chunk) => {
770
+ data += chunk;
771
+ });
772
+ stdin.on("end", finish);
773
+ stdin.on("error", finish);
774
+ stdin.resume();
775
+ });
776
+ var init_seat = __esm(() => {
777
+ init_config();
778
+ init_meter();
779
+ SEAT_POST_MIN_INTERVAL_MS2 = 5 * 60000;
780
+ });
781
+
782
+ // src/index.ts
783
+ import { realpathSync } from "fs";
784
+ import { homedir, platform as platform2 } from "os";
785
+ import { fileURLToPath } from "url";
786
+
787
+ // src/commands/defer.ts
788
+ init_config();
789
+ init_meter();
790
+ import { existsSync as existsSync5 } from "node:fs";
791
+ import { hostname } from "node:os";
792
+
793
+ // src/musher.ts
794
+ import { spawnSync } from "node:child_process";
795
+ import { createPublicKey, verify as cryptoVerify } from "node:crypto";
796
+ import { dirname as dirname2, join as join4 } from "node:path";
797
+ var MUSHER_LABEL = "ai.sleds.musher";
798
+ var MUSHER_INTERVAL_S = 300;
799
+ var DEFAULT_PERMISSION_MODE = "acceptEdits";
800
+ var DEFAULT_TIMEOUT_MINUTES = 90;
801
+ var SUMMARY_MAX = 2000;
802
+ var musherPlistPath = (home) => join4(home, "Library", "LaunchAgents", `${MUSHER_LABEL}.plist`);
803
+ var musherLogPath = (configDir2) => join4(configDir2, "musher.log");
804
+ var xmlEscape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
805
+ var musherPath = (home, execPath, currentPath = "") => {
806
+ const parts = [
807
+ dirname2(execPath),
808
+ join4(home, ".sleds", "bin"),
809
+ join4(home, ".local", "bin"),
810
+ join4(home, ".bun", "bin"),
811
+ "/opt/homebrew/bin",
812
+ "/usr/local/bin",
813
+ "/usr/bin",
814
+ "/bin",
815
+ ...currentPath.split(":").filter(Boolean)
816
+ ];
817
+ return [...new Set(parts)].join(":");
600
818
  };
601
- var hottest = (w) => seatBars(w).reduce((best, cur) => !best || cur.bar.usedPct > best.bar.usedPct ? cur : best, null);
602
- var toneOf = (pct) => pct === undefined ? null : pct >= HOT_PCT ? "hot" : pct >= WARM_PCT ? "warm" : null;
603
- var colorParam = (tone) => tone === "hot" ? ` color=${HOT_COLOR}` : tone === "warm" ? ` color=${WARM_COLOR}` : "";
604
- var barTitle = (m, seat) => {
605
- const head = m ? usd(m.today.spentUsd) : "offline";
606
- const wk = seat?.sevenDay ? `wk ${Math.round(seat.sevenDay.usedPct)}%` : null;
607
- const top = hottest(seat);
608
- const risk = top && top.label !== "wk" && toneOf(top.bar.usedPct) ? `${top.label} ${Math.round(top.bar.usedPct)}%` : null;
609
- return [`sleds ${head}`, ...wk ? [wk] : [], ...risk ? [risk] : []].join(SEP);
610
- };
611
- var resetIn = (bar, now) => bar.resetsAt > now ? `${SEP}resets in ${shortDuration(bar.resetsAt - now)}` : "";
612
- var formatBar = (m, seat, now) => {
613
- const tone = toneOf(hottest(seat)?.bar.usedPct);
614
- const lines = [`${barTitle(m, seat)}${colorParam(tone)}`, "---"];
615
- if (m) {
616
- for (const row2 of formatMeter(m, now))
617
- lines.push(`${row2} | ${MONO}`);
618
- } else {
619
- lines.push(`offline${SEP}proxy did not answer | ${MONO}`);
620
- }
621
- const bars = seatBars(seat);
622
- if (bars.length > 0 || seat?.usedCreditsUsd !== undefined) {
623
- lines.push("---");
624
- lines.push(`Seat limits${seat ? `${SEP}${shortDuration(now - seat.capturedAt)} ago` : ""} | ${MONO}`);
625
- for (const { label, bar } of bars) {
626
- const pct = Math.round(bar.usedPct);
627
- lines.push(`${label.padEnd(12)}${String(pct).padStart(3)}%${resetIn(bar, now)} | ${MONO}${colorParam(toneOf(bar.usedPct))}`);
819
+ var buildMusherPlist = (o) => {
820
+ const args = [...o.sledsArgv, "musher", "tick"].map((a) => ` <string>${xmlEscape(a)}</string>`).join(`
821
+ `);
822
+ const env = Object.entries(o.env).filter(([, v]) => v !== "").map(([k, v]) => ` <key>${xmlEscape(k)}</key>
823
+ <string>${xmlEscape(v)}</string>`).join(`
824
+ `);
825
+ return `<?xml version="1.0" encoding="UTF-8"?>
826
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
827
+ <plist version="1.0">
828
+ <dict>
829
+ <key>Label</key>
830
+ <string>${MUSHER_LABEL}</string>
831
+ <key>ProgramArguments</key>
832
+ <array>
833
+ ${args}
834
+ </array>
835
+ <key>EnvironmentVariables</key>
836
+ <dict>
837
+ ${env}
838
+ </dict>
839
+ <key>StartInterval</key>
840
+ <integer>${MUSHER_INTERVAL_S}</integer>
841
+ <key>RunAtLoad</key>
842
+ <true/>
843
+ <key>StandardOutPath</key>
844
+ <string>${xmlEscape(o.logPath)}</string>
845
+ <key>StandardErrorPath</key>
846
+ <string>${xmlEscape(o.logPath)}</string>
847
+ </dict>
848
+ </plist>
849
+ `;
850
+ };
851
+ var RESUME_PROMPT = [
852
+ "SLEDS musher: the usage limit that stopped this session has reset, and the seat's owner",
853
+ "scheduled this unattended run to continue it. Pick up exactly where the session left off:",
854
+ "finish the task that was in flight, run the checks you would normally run, follow the repo's",
855
+ "own conventions for branches and commits, and do not start unrelated work. If you are blocked",
856
+ "on a decision only the owner can make, stop there and say so. Finish with a summary under",
857
+ "200 words: what was done, what remains, anything the owner must decide."
858
+ ].join(" ");
859
+ var NO_SESSION_PROMPT = [
860
+ "SLEDS musher: a deferred job started on this seat, but no Claude Code session was recorded",
861
+ "to resume. Do not guess at work. Reply with one line saying so, and stop."
862
+ ].join(" ");
863
+ var claudeArgs = (o) => [
864
+ "-p",
865
+ o.sessionId ? RESUME_PROMPT : NO_SESSION_PROMPT,
866
+ ...o.sessionId ? ["--resume", o.sessionId] : [],
867
+ ...o.model ? ["--model", o.model] : [],
868
+ "--output-format",
869
+ "json",
870
+ "--permission-mode",
871
+ o.permissionMode
872
+ ];
873
+ var parseClaudeOutput = (stdout, exitCode) => {
874
+ const lines = stdout.split(`
875
+ `).map((l) => l.trim()).filter(Boolean);
876
+ for (let i = lines.length - 1;i >= 0; i--) {
877
+ const line = lines[i] ?? "";
878
+ if (!line.startsWith("{"))
879
+ continue;
880
+ try {
881
+ const j = JSON.parse(line);
882
+ if (j.type !== "result" && typeof j.result !== "string")
883
+ continue;
884
+ const isError = j.is_error === true || typeof j.subtype === "string" && j.subtype !== "success";
885
+ const text = typeof j.result === "string" ? j.result : "";
886
+ const models = j.modelUsage;
887
+ const model = typeof j.model === "string" ? j.model : models && typeof models === "object" ? Object.keys(models)[0] : undefined;
888
+ return {
889
+ ok: !isError && exitCode === 0,
890
+ summary: (text || (isError ? "Claude Code reported an error" : "")).slice(0, SUMMARY_MAX),
891
+ ...typeof j.session_id === "string" ? { sessionId: j.session_id } : {},
892
+ ...model ? { model } : {},
893
+ ...typeof j.duration_ms === "number" ? { durationMs: j.duration_ms } : {},
894
+ ...typeof j.total_cost_usd === "number" ? { costUsd: j.total_cost_usd } : {}
895
+ };
896
+ } catch {}
897
+ }
898
+ const tail = lines.slice(-5).join(" ").slice(0, SUMMARY_MAX);
899
+ return { ok: false, summary: tail || `claude exited ${exitCode ?? "without a code"}` };
900
+ };
901
+ var defaultExecCapture = (cmd, args, o) => {
902
+ const r = spawnSync(cmd, [...args], {
903
+ cwd: o.cwd,
904
+ encoding: "utf8",
905
+ timeout: o.timeoutMs,
906
+ maxBuffer: 64 * 1024 * 1024,
907
+ stdio: ["ignore", "pipe", "pipe"],
908
+ env: { ...process.env, SLEDS_MUSHER: "1" }
909
+ });
910
+ return {
911
+ stdout: r.stdout ?? "",
912
+ stderr: r.stderr ?? "",
913
+ status: r.status,
914
+ ...r.error ? { error: r.error.message } : {}
915
+ };
916
+ };
917
+ var musherClient = (o) => {
918
+ const fetchFn = o.fetchFn ?? fetch;
919
+ const headers = { authorization: `Bearer ${o.token}`, "content-type": "application/json" };
920
+ const call = async (path, body) => {
921
+ try {
922
+ const res = await fetchFn(`${o.proxyUrl}${path}`, {
923
+ method: body === undefined ? "GET" : "POST",
924
+ headers,
925
+ ...body === undefined ? {} : { body: JSON.stringify(body) },
926
+ signal: AbortSignal.timeout(o.timeoutMs ?? 1e4)
927
+ });
928
+ if (!res.ok)
929
+ return null;
930
+ return await res.json();
931
+ } catch {
932
+ return null;
628
933
  }
629
- if (seat?.usedCreditsUsd !== undefined)
630
- lines.push(`${"credits".padEnd(12)}${usd(seat.usedCreditsUsd)} spent | ${MONO}`);
934
+ };
935
+ return {
936
+ list: () => call("/proxy/musher"),
937
+ start: (jobId, local) => call("/proxy/musher/start", { jobId, ...local }),
938
+ finish: async (input) => await call("/proxy/musher/finish", input) !== null,
939
+ defer: (input) => call("/proxy/defer", input),
940
+ attestation: (jobId) => call(`/proxy/attest/${encodeURIComponent(jobId)}`)
941
+ };
942
+ };
943
+ var gitHead = (cwd, exec) => {
944
+ if (!cwd)
945
+ return null;
946
+ const r = exec("git", ["rev-parse", "HEAD"], { cwd, timeoutMs: 5000 });
947
+ const sha = r.stdout.trim();
948
+ return r.status === 0 && /^[0-9a-f]{40}$/.test(sha) ? sha : null;
949
+ };
950
+ var gitNewCommits = (cwd, before, after, exec) => {
951
+ if (!cwd || !after || after === before)
952
+ return [];
953
+ if (!before)
954
+ return [after];
955
+ const r = exec("git", ["rev-list", `${before}..${after}`], { cwd, timeoutMs: 5000 });
956
+ if (r.status !== 0)
957
+ return [after];
958
+ return r.stdout.split(`
959
+ `).map((l) => l.trim()).filter((l) => /^[0-9a-f]{40}$/.test(l)).slice(0, 40);
960
+ };
961
+ var collectOutputs = (commits, summary) => {
962
+ const out = commits.map((c) => `git:${c}`);
963
+ for (const m of summary.matchAll(/https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/pull\/\d+/g))
964
+ if (!out.includes(`pr:${m[0]}`))
965
+ out.push(`pr:${m[0]}`);
966
+ return out.slice(0, 50);
967
+ };
968
+ var runJob = async (o) => {
969
+ const log = o.log ?? (() => {});
970
+ const sessionId = o.job.claudeSessionId ?? o.fallbackSession?.id ?? null;
971
+ const cwd = o.job.cwd ?? o.fallbackSession?.cwd ?? undefined;
972
+ const started = await o.client.start(o.job.jobId, {
973
+ ...sessionId ? { claudeSessionId: sessionId } : {},
974
+ ...cwd ? { cwd } : {},
975
+ host: o.host
976
+ });
977
+ if (!started)
978
+ return { kind: "skipped", reason: "control plane unreachable" };
979
+ if (!started.ok)
980
+ return { kind: "skipped", reason: started.reason };
981
+ const model = o.job.model ?? started.model ?? o.fallbackSession?.model ?? null;
982
+ const args = claudeArgs({ sessionId, model, permissionMode: o.permissionMode });
983
+ log(`musher: running job ${o.job.jobId} (${sessionId ? `resume ${sessionId}` : "no session"}${model ? ` · ${model}` : ""}${cwd ? ` · ${cwd}` : ""})`);
984
+ const exec = o.exec ?? defaultExecCapture;
985
+ const headBefore = gitHead(cwd, exec);
986
+ const t0 = Date.now();
987
+ const r = exec(o.claudePath, args, { cwd, timeoutMs: o.timeoutMs });
988
+ const durationMs = Date.now() - t0;
989
+ const result = r.error ? { ok: false, summary: `could not run ${o.claudePath}: ${r.error}` } : parseClaudeOutput(r.stdout, r.status);
990
+ if (!r.error && r.status === null)
991
+ result.summary = `timed out after ${Math.round(o.timeoutMs / 60000)} min. ${result.summary}`.trim();
992
+ const outputs = collectOutputs(gitNewCommits(cwd, headBefore, gitHead(cwd, exec), exec), result.summary);
993
+ await o.client.finish({
994
+ jobId: o.job.jobId,
995
+ runId: started.runId,
996
+ ok: result.ok,
997
+ summary: result.summary,
998
+ ...outputs.length ? { outputs } : {},
999
+ ...result.model ?? model ? { model: result.model ?? model } : {},
1000
+ durationMs: result.durationMs ?? durationMs,
1001
+ ...result.sessionId ?? sessionId ? { claudeSessionId: result.sessionId ?? sessionId } : {}
1002
+ });
1003
+ log(`musher: job ${o.job.jobId} ${result.ok ? "delivered" : "failed"} in ${Math.round(durationMs / 1000)}s`);
1004
+ return { kind: "ran", ok: result.ok, summary: result.summary, durationMs, outputs };
1005
+ };
1006
+ var verifyAttestation = (payload, sig, publicKeyB64) => {
1007
+ try {
1008
+ const pub = createPublicKey({
1009
+ key: Buffer.from(publicKeyB64, "base64"),
1010
+ format: "der",
1011
+ type: "spki"
1012
+ });
1013
+ const raw = Buffer.from(sig.replace(/-/g, "+").replace(/_/g, "/"), "base64");
1014
+ return cryptoVerify(null, Buffer.from(payload, "utf8"), pub, raw);
1015
+ } catch {
1016
+ return false;
631
1017
  }
632
- lines.push("---");
633
- lines.push(`Open dashboard | href=${DASHBOARD_URL}`);
634
- lines.push("Refresh | refresh=true");
635
- return lines;
636
1018
  };
637
- var pluginScript = (nodePath, cliPath, env = {}) => [
638
- "#!/bin/bash",
639
- "# SLEDS menu-bar meter for SwiftBar — written by `sleds bar --install`; re-run it after moving the CLI.",
640
- "# <swiftbar.title>SLEDS</swiftbar.title>",
641
- "# <swiftbar.hideAbout>true</swiftbar.hideAbout>",
642
- "# <swiftbar.hideRunInTerminal>true</swiftbar.hideRunInTerminal>",
643
- "# <swiftbar.hideSwiftBar>true</swiftbar.hideSwiftBar>",
644
- ...env.sledsHome ? [`export SLEDS_HOME=${shellQuote(env.sledsHome)}`] : ["unset SLEDS_HOME"],
645
- ...env.claudeConfigDir ? [`export CLAUDE_CONFIG_DIR=${shellQuote(env.claudeConfigDir)}`] : ["unset CLAUDE_CONFIG_DIR"],
646
- 'export PATH="$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"',
647
- `if [ -x ${shellQuote(nodePath)} ] && [ -f ${shellQuote(cliPath)} ]; then`,
648
- ` exec ${shellQuote(nodePath)} ${shellQuote(cliPath)} bar`,
649
- "fi",
650
- "if command -v sleds >/dev/null 2>&1; then exec sleds bar; fi",
651
- 'echo "sleds not found"',
652
- 'echo "---"',
653
- 'echo "Run \\`npx @sleds/cli bar --install\\` again. | font=Menlo size=12"',
654
- ""
655
- ].join(`
656
- `);
657
- var shellQuote = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
1019
+ var parsePayload = (payload) => {
1020
+ const out = {};
1021
+ for (const line of payload.split(`
1022
+ `).slice(1)) {
1023
+ const eq = line.indexOf("=");
1024
+ if (eq > 0)
1025
+ out[line.slice(0, eq)] = line.slice(eq + 1);
1026
+ }
1027
+ return out;
1028
+ };
658
1029
 
659
1030
  // src/seat.ts
1031
+ init_config();
1032
+ init_meter();
660
1033
  import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
661
- import { join as join4 } from "node:path";
1034
+ import { join as join5 } from "node:path";
662
1035
  var SEAT_POST_MIN_DELTA_PCT = 1;
663
1036
  var SEAT_POST_MIN_INTERVAL_MS = 5 * 60000;
664
1037
  var SEAT_POST_TIMEOUT_MS = 1500;
@@ -743,7 +1116,7 @@ var formatSeatFragment = (w) => {
743
1116
  return null;
744
1117
  return `seat ${parts.join(SEP)}`;
745
1118
  };
746
- var seatStatePath = (home) => join4(configDir(home), "seat.json");
1119
+ var seatStatePath = (home) => join5(configDir(home), "seat.json");
747
1120
  var parseWindowStored = (v) => {
748
1121
  if (!isObject4(v))
749
1122
  return;
@@ -775,10 +1148,58 @@ var parseSeatState = (raw) => {
775
1148
  lp.spendLimit = sp;
776
1149
  out.lastPosted = lp;
777
1150
  }
778
- const lastWallResetsAt = num2(raw.lastWallResetsAt);
779
- if (lastWallResetsAt !== undefined)
780
- out.lastWallResetsAt = lastWallResetsAt;
781
- return out;
1151
+ const lastWallResetsAt = num2(raw.lastWallResetsAt);
1152
+ if (lastWallResetsAt !== undefined)
1153
+ out.lastWallResetsAt = lastWallResetsAt;
1154
+ const session = parseSeatSession(raw.session);
1155
+ if (session)
1156
+ out.session = session;
1157
+ return out;
1158
+ };
1159
+ var parseSeatSession = (v) => {
1160
+ if (!isObject4(v) || typeof v.id !== "string" || !v.id)
1161
+ return;
1162
+ const at = num2(v.at);
1163
+ if (at === undefined)
1164
+ return;
1165
+ const out = { id: v.id, at };
1166
+ if (typeof v.cwd === "string" && v.cwd)
1167
+ out.cwd = v.cwd;
1168
+ if (typeof v.model === "string" && v.model)
1169
+ out.model = v.model;
1170
+ return out;
1171
+ };
1172
+ var seatSessionFromStdin = (text, now) => {
1173
+ const trimmed = text.trim();
1174
+ if (!trimmed)
1175
+ return null;
1176
+ try {
1177
+ const raw = JSON.parse(trimmed);
1178
+ if (!isObject4(raw) || typeof raw.session_id !== "string" || !raw.session_id)
1179
+ return null;
1180
+ const out = { id: raw.session_id, at: now };
1181
+ if (typeof raw.cwd === "string" && raw.cwd)
1182
+ out.cwd = raw.cwd;
1183
+ const model = isObject4(raw.model) ? raw.model.id : raw.model;
1184
+ if (typeof model === "string" && model)
1185
+ out.model = model;
1186
+ return out;
1187
+ } catch {
1188
+ return null;
1189
+ }
1190
+ };
1191
+ var bindingResetAt = (w, now) => {
1192
+ if (!w)
1193
+ return;
1194
+ const bars = [
1195
+ ...w.fiveHour ? [w.fiveHour] : [],
1196
+ ...w.sevenDay ? [w.sevenDay] : [],
1197
+ ...w.spendLimit ? [w.spendLimit] : [],
1198
+ ...Object.values(w.models ?? {})
1199
+ ].filter((b) => b.resetsAt > now);
1200
+ const full = bars.filter((b) => b.usedPct >= 100);
1201
+ const pick = (full.length ? full : bars).map((b) => b.resetsAt);
1202
+ return pick.length ? Math.min(...pick) : undefined;
782
1203
  };
783
1204
  var readSeatState = (home) => {
784
1205
  const p = seatStatePath(home);
@@ -857,8 +1278,8 @@ var postJson = async (o, path, body) => {
857
1278
  };
858
1279
  var postSeatWindow = (o, w) => postJson(o, "/proxy/seat", seatWindowBody(w));
859
1280
  var postSeatWall = (o, input) => postJson(o, "/proxy/seat-wall", input);
860
- var SEAT_WALL_LINE = "Seat limit hit. sleds on --until-reset to continue on the org key.";
861
- var SEAT_WALL_LINE_NO_KEY = "Seat limit hit. Hold until the reset, or /sled 5 to hand off — this org has no platform key to switch to.";
1281
+ var SEAT_WALL_LINE = "Seat limit hit. sleds on --until-reset to continue on the org key, or sleds defer to resume here at the reset.";
1282
+ var SEAT_WALL_LINE_NO_KEY = "Seat limit hit. sleds defer resumes this session on this Mac at the reset; /sled 5 to hand off — this org has no platform key to switch to.";
862
1283
  var seatWallLine = (hasUpstreamKey) => hasUpstreamKey ? SEAT_WALL_LINE : SEAT_WALL_LINE_NO_KEY;
863
1284
  var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve3) => {
864
1285
  if (stdin.isTTY) {
@@ -884,140 +1305,8 @@ var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve3) =>
884
1305
  stdin.resume();
885
1306
  });
886
1307
 
887
- // src/commands/bar.ts
888
- function parseInstallArgs(argv, env) {
889
- const flag = (name2) => {
890
- const i = argv.indexOf(`--${name2}`);
891
- const v = i >= 0 ? argv[i + 1] : undefined;
892
- return v && !v.startsWith("--") ? v : undefined;
893
- };
894
- const sledsHome = flag("home") ?? (env.SLEDS_HOME?.trim() || undefined);
895
- const claudeConfigDir = flag("claude-config") ?? (env.CLAUDE_CONFIG_DIR?.trim() || undefined);
896
- const name = flag("name");
897
- return {
898
- ...sledsHome ? { sledsHome } : {},
899
- ...claudeConfigDir ? { claudeConfigDir } : {},
900
- ...name ? { name } : {}
901
- };
902
- }
903
- var PLUGIN_NAME = "sleds.1m.sh";
904
- var pluginName = (name) => name ? `sleds-${name}.1m.sh` : PLUGIN_NAME;
905
- var bar = async (o) => {
906
- const log = o.log ?? console.log;
907
- const argv = o.argv ?? [];
908
- if (argv.includes("--install"))
909
- return install(o, log);
910
- if (argv.includes("--uninstall"))
911
- return uninstall(o, log);
912
- const now = (o.now ?? Date.now)();
913
- const seat = freshSeat(readSeatState(o.home).window, now);
914
- if (!meterSource(o.home)) {
915
- log("sleds not set up");
916
- log("---");
917
- log("Run `sleds init` first | font=Menlo size=12");
918
- return 0;
919
- }
920
- const m = await loadMeter({ home: o.home, now, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs });
921
- for (const line of formatBar(m, seat, now))
922
- log(line);
923
- return 0;
924
- };
925
- function swiftBarPluginDir() {
926
- try {
927
- const out = execFileSync("defaults", ["read", "com.ameba.SwiftBar", "PluginDirectory"], {
928
- encoding: "utf8",
929
- stdio: ["ignore", "pipe", "ignore"]
930
- }).trim();
931
- return out ? out.replace(/^~(?=$|\/)/, homedir()) : null;
932
- } catch {
933
- return null;
934
- }
935
- }
936
- var pluginPath = (home, name) => join5(configDir(home, {}), "swiftbar", pluginName(name));
937
- function install(o, log) {
938
- const opts = parseInstallArgs(o.argv ?? [], o.env ?? process.env);
939
- const script = pluginPath(o.home, opts.name);
940
- mkdirSync5(join5(script, ".."), { recursive: true, mode: 448 });
941
- writeFileSync5(script, pluginScript(o.nodePath ?? process.execPath, o.cliPath ?? process.argv[1] ?? "", {
942
- ...opts.sledsHome ? { sledsHome: resolve3(o.home, opts.sledsHome) } : {},
943
- ...opts.claudeConfigDir ? { claudeConfigDir: resolve3(o.home, opts.claudeConfigDir) } : {}
944
- }), {
945
- mode: 493
946
- });
947
- chmodSync2(script, 493);
948
- log(`wrote ${script}`);
949
- if (opts.sledsHome || opts.claudeConfigDir)
950
- log(`tracks the seat at ${[opts.sledsHome && `SLEDS_HOME=${opts.sledsHome}`, opts.claudeConfigDir && `CLAUDE_CONFIG_DIR=${opts.claudeConfigDir}`].filter(Boolean).join(" ")}`);
951
- const dir = (o.swiftBarPluginDir ?? swiftBarPluginDir)();
952
- if (!dir) {
953
- log("SwiftBar is not set up yet: `brew install --cask swiftbar`, launch it once and pick a plugin");
954
- log("folder, then run `sleds bar --install` again — or symlink the file above into that folder.");
955
- return 0;
956
- }
957
- const link = join5(dir, pluginName(opts.name));
958
- try {
959
- mkdirSync5(dir, { recursive: true });
960
- if (existsSync5(link) || isDanglingLink(link))
961
- unlinkSync(link);
962
- symlinkSync(script, link);
963
- log(`linked ${link}`);
964
- log("SwiftBar picks it up within a minute; the item reads `sleds $… · wk …%`.");
965
- return 0;
966
- } catch (e) {
967
- log(`could not link into ${dir}: ${e instanceof Error ? e.message : String(e)}`);
968
- log(`symlink it yourself: ln -sf ${script} ${link}`);
969
- return 1;
970
- }
971
- }
972
- function uninstall(o, log) {
973
- const { name } = parseInstallArgs(o.argv ?? [], {});
974
- const dir = (o.swiftBarPluginDir ?? swiftBarPluginDir)();
975
- const link = dir ? join5(dir, pluginName(name)) : null;
976
- if (link && (existsSync5(link) || isDanglingLink(link))) {
977
- unlinkSync(link);
978
- log(`removed ${link}`);
979
- } else
980
- log("no SwiftBar plugin link to remove");
981
- const script = pluginPath(o.home, name);
982
- if (existsSync5(script)) {
983
- unlinkSync(script);
984
- log(`removed ${script}`);
985
- }
986
- return 0;
987
- }
988
- var isDanglingLink = (p) => {
989
- try {
990
- return lstatSync(p).isSymbolicLink();
991
- } catch {
992
- return false;
993
- }
994
- };
995
-
996
- // src/commands/doctor.ts
997
- import { execFileSync as execFileSync3 } from "node:child_process";
998
- import { existsSync as existsSync6, lstatSync as lstatSync2, readFileSync as readFileSync5, readdirSync, readlinkSync } from "node:fs";
999
- import { join as join7, resolve as resolve5 } from "node:path";
1000
-
1001
- // src/commands/bar.ts
1002
- import { execFileSync as execFileSync2 } from "node:child_process";
1003
- import { homedir as homedir2 } from "node:os";
1004
- import { join as join6, resolve as resolve4 } from "node:path";
1005
- var PLUGIN_NAME2 = "sleds.1m.sh";
1006
- var pluginName2 = (name) => name ? `sleds-${name}.1m.sh` : PLUGIN_NAME2;
1007
- function swiftBarPluginDir2() {
1008
- try {
1009
- const out = execFileSync2("defaults", ["read", "com.ameba.SwiftBar", "PluginDirectory"], {
1010
- encoding: "utf8",
1011
- stdio: ["ignore", "pipe", "ignore"]
1012
- }).trim();
1013
- return out ? out.replace(/^~(?=$|\/)/, homedir2()) : null;
1014
- } catch {
1015
- return null;
1016
- }
1017
- }
1018
- var pluginPath2 = (home, name) => join6(configDir(home, {}), "swiftbar", pluginName2(name));
1019
-
1020
1308
  // src/switch.ts
1309
+ init_settings();
1021
1310
  var isObject5 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
1022
1311
  var envOf2 = (settings) => isObject5(settings.env) ? settings.env : {};
1023
1312
  var capturePriorEnv = (settings) => {
@@ -1092,7 +1381,141 @@ var formatDuration = (ms) => {
1092
1381
  return m === 0 ? `${h}h` : `${h}h ${m}m`;
1093
1382
  };
1094
1383
 
1384
+ // src/commands/defer.ts
1385
+ var parseAt = (v, now) => {
1386
+ const rel = /^\+(\d+)([hm])$/.exec(v.trim());
1387
+ if (rel) {
1388
+ const n = Number.parseInt(rel[1] ?? "0", 10);
1389
+ return now + n * (rel[2] === "h" ? 3600000 : 60000);
1390
+ }
1391
+ const t = Date.parse(v);
1392
+ return Number.isFinite(t) ? t : undefined;
1393
+ };
1394
+ var parseFlags = (argv) => {
1395
+ const flags = {};
1396
+ for (let i = 0;i < argv.length; i++) {
1397
+ const a = argv[i] ?? "";
1398
+ if (!a.startsWith("--"))
1399
+ continue;
1400
+ const eq = a.indexOf("=");
1401
+ if (eq > 0)
1402
+ flags[a.slice(2, eq)] = a.slice(eq + 1);
1403
+ else {
1404
+ const next = argv[i + 1];
1405
+ if (next !== undefined && !next.startsWith("--")) {
1406
+ flags[a.slice(2)] = next;
1407
+ i++;
1408
+ } else
1409
+ flags[a.slice(2)] = "true";
1410
+ }
1411
+ }
1412
+ return flags;
1413
+ };
1414
+ var defer = async (o) => {
1415
+ const log = o.log ?? console.log;
1416
+ const error = o.error ?? console.error;
1417
+ const now = (o.now ?? Date.now)();
1418
+ const flags = parseFlags(o.argv);
1419
+ const client = o.client ?? (() => {
1420
+ const src = meterSource(o.home);
1421
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
1422
+ })();
1423
+ if (!client) {
1424
+ error("No seat on this machine. Run `sleds init` first.");
1425
+ return 1;
1426
+ }
1427
+ const state = readSeatState(o.home);
1428
+ const piped = o.stdin ? seatSessionFromStdin(o.stdin, now) : null;
1429
+ const remembered = piped ?? state.session;
1430
+ const sessionId = flags.session ?? remembered?.id;
1431
+ const cwd = flags.cwd ?? remembered?.cwd;
1432
+ const model = flags.model ?? remembered?.model;
1433
+ let resetAt;
1434
+ if (flags.at) {
1435
+ resetAt = parseAt(flags.at, now);
1436
+ if (resetAt === undefined) {
1437
+ error(`--at takes +2h, +90m or an ISO timestamp (got ${flags.at})`);
1438
+ return 1;
1439
+ }
1440
+ } else
1441
+ resetAt = bindingResetAt(state.window, now);
1442
+ if (!sessionId)
1443
+ log("No Claude Code session remembered on this seat yet; the job will say so when it starts. Pass --session <id> to pick one.");
1444
+ const r = await client.defer({
1445
+ ...resetAt !== undefined ? { resetAt } : {},
1446
+ ...model ? { model } : {},
1447
+ ...sessionId ? { claudeSessionId: sessionId } : {},
1448
+ ...cwd ? { cwd } : {},
1449
+ host: o.host ?? hostname()
1450
+ });
1451
+ if (!r) {
1452
+ error("Could not reach the SLEDS proxy; nothing was scheduled.");
1453
+ return 1;
1454
+ }
1455
+ const when = r.scheduledAt <= now + 60000 ? "as soon as the musher ticks (within 5 min)" : `${formatLocal(r.scheduledAt)} (in ${formatDuration(r.scheduledAt - now)})`;
1456
+ log(`Deferred. This Mac resumes ${sessionId ? `session ${sessionId.slice(0, 8)}…` : "the work"}${model ? ` on ${model}` : ""} ${when}.`);
1457
+ log(` job ${r.jobId}${cwd ? ` · ${cwd}` : ""} · runs on your seat, no key · sleds musher --status to watch`);
1458
+ const cfg = readConfig(o.home);
1459
+ if (process.platform === "darwin" && !existsSync5(musherPlistPath(o.home)) && cfg.musher?.agent !== "launchd")
1460
+ log(` The musher agent is not installed on this Mac: run \`sleds musher --install\` once, or \`sleds run ${r.jobId}\` by hand at the reset.`);
1461
+ log(" Keep this Mac awake at that time (System Settings → Battery → prevent sleeping on power adapter).");
1462
+ return 0;
1463
+ };
1464
+
1465
+ // src/commands/doctor.ts
1466
+ init_config();
1467
+ init_meter();
1468
+ import { execFileSync } from "node:child_process";
1469
+ import { existsSync as existsSync7, lstatSync, readlinkSync } from "node:fs";
1470
+ import { resolve as resolve3 } from "node:path";
1471
+ init_settings();
1472
+
1473
+ // src/commands/musher.ts
1474
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync5 } from "node:fs";
1475
+ init_config();
1476
+ init_meter();
1477
+ var defaultWhich = (cmd) => {
1478
+ const path = process.env.PATH ?? "";
1479
+ for (const dir of path.split(":").filter(Boolean)) {
1480
+ const p = `${dir}/${cmd}`;
1481
+ if (existsSync6(p))
1482
+ return p;
1483
+ }
1484
+ return null;
1485
+ };
1486
+ var findClaude = (home, configured, which) => {
1487
+ if (configured && existsSync6(configured))
1488
+ return configured;
1489
+ const found = which("claude");
1490
+ if (found)
1491
+ return found;
1492
+ for (const p of [
1493
+ `${home}/.local/bin/claude`,
1494
+ "/opt/homebrew/bin/claude",
1495
+ "/usr/local/bin/claude",
1496
+ `${home}/.npm-global/bin/claude`
1497
+ ])
1498
+ if (existsSync6(p))
1499
+ return p;
1500
+ return null;
1501
+ };
1502
+ var musherAgentState = (home, env = process.env) => {
1503
+ const plist = musherPlistPath(home);
1504
+ if (!existsSync6(plist))
1505
+ return { installed: false, thisSeat: false };
1506
+ try {
1507
+ const text = readFileSync5(plist, "utf8");
1508
+ const want = env.SLEDS_HOME?.trim();
1509
+ const has = /<key>SLEDS_HOME<\/key>\s*<string>([^<]*)<\/string>/.exec(text)?.[1];
1510
+ return { installed: true, thisSeat: (want ?? undefined) === (has ?? undefined) };
1511
+ } catch {
1512
+ return { installed: true, thisSeat: false };
1513
+ }
1514
+ };
1515
+
1095
1516
  // src/commands/status.ts
1517
+ init_config();
1518
+ init_settings();
1096
1519
  var checkHealth = async (url, fetchFn = fetch, timeoutMs = 1000) => {
1097
1520
  try {
1098
1521
  const res = await fetchFn(`${url}/healthz`, { signal: AbortSignal.timeout(timeoutMs) });
@@ -1110,16 +1533,6 @@ function nodeOk(version, min = NODE_MIN) {
1110
1533
  const [x, y, z] = parse(min);
1111
1534
  return (a ?? 0) !== (x ?? 0) ? (a ?? 0) > (x ?? 0) : (b ?? 0) !== (y ?? 0) ? (b ?? 0) > (y ?? 0) : (c ?? 0) >= (z ?? 0);
1112
1535
  }
1113
- function pluginEnv(body) {
1114
- const out = {};
1115
- const home = /^export SLEDS_HOME='([^']*)'/m.exec(body);
1116
- const cfg = /^export CLAUDE_CONFIG_DIR='([^']*)'/m.exec(body);
1117
- if (home?.[1])
1118
- out.sledsHome = home[1];
1119
- if (cfg?.[1])
1120
- out.claudeConfigDir = cfg[1];
1121
- return out;
1122
- }
1123
1536
  var MARK = { ok: "ok ", warn: "warn", fail: "FAIL" };
1124
1537
  function formatDoctor(checks) {
1125
1538
  const lines = checks.map((c) => `${MARK[c.level]} ${c.name.padEnd(16)}${c.detail}`);
@@ -1135,9 +1548,9 @@ function formatDoctor(checks) {
1135
1548
  lines.push(fails === 0 && warns === 0 ? "All good." : `${fails} problem${fails === 1 ? "" : "s"}, ${warns} warning${warns === 1 ? "" : "s"}.`);
1136
1549
  return lines;
1137
1550
  }
1138
- function defaultWhich(cmd) {
1551
+ function defaultWhich2(cmd) {
1139
1552
  try {
1140
- return execFileSync3("which", ["-a", cmd], {
1553
+ return execFileSync("which", ["-a", cmd], {
1141
1554
  encoding: "utf8",
1142
1555
  stdio: ["ignore", "pipe", "ignore"]
1143
1556
  }).split(`
@@ -1146,16 +1559,6 @@ function defaultWhich(cmd) {
1146
1559
  return [];
1147
1560
  }
1148
1561
  }
1149
- function defaultLaunchctl() {
1150
- try {
1151
- return execFileSync3("launchctl", ["list"], {
1152
- encoding: "utf8",
1153
- stdio: ["ignore", "pipe", "ignore"]
1154
- });
1155
- } catch {
1156
- return "";
1157
- }
1158
- }
1159
1562
  async function runDoctor(o) {
1160
1563
  const env = o.env ?? process.env;
1161
1564
  const checks = [];
@@ -1166,7 +1569,7 @@ async function runDoctor(o) {
1166
1569
  ].filter(Boolean);
1167
1570
  const cfgDir = configDir(home, env);
1168
1571
  const cfg = readConfig(home);
1169
- const cfgExists = existsSync6(configPath(home));
1572
+ const cfgExists = existsSync7(configPath(home));
1170
1573
  if (!cfgExists) {
1171
1574
  checks.push({
1172
1575
  level: "fail",
@@ -1182,7 +1585,7 @@ async function runDoctor(o) {
1182
1585
  });
1183
1586
  }
1184
1587
  const sPath = settingsPath(home);
1185
- const settings = existsSync6(sPath) ? readSettings(home) : null;
1588
+ const settings = existsSync7(sPath) ? readSettings(home) : null;
1186
1589
  const pointed = settings ? readProxy(settings) : null;
1187
1590
  if (!settings) {
1188
1591
  checks.push({
@@ -1226,7 +1629,7 @@ async function runDoctor(o) {
1226
1629
  detail: `seat-event ${seat2 ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"}`,
1227
1630
  fix: seat2 && spec ? undefined : "sleds hooks"
1228
1631
  });
1229
- const want = env.SLEDS_HOME?.trim() ? resolve5(home, env.SLEDS_HOME.trim()) : undefined;
1632
+ const want = env.SLEDS_HOME?.trim() ? resolve3(home, env.SLEDS_HOME.trim()) : undefined;
1230
1633
  const cmds = sledsCommandsIn(settings);
1231
1634
  const wrong = cmds.filter((c) => {
1232
1635
  const got = bakedHome(c);
@@ -1240,7 +1643,7 @@ async function runDoctor(o) {
1240
1643
  fix: wrong.length === 0 ? undefined : `${overrides.length ? `${overrides.join(" ")} ` : ""}sleds hooks (re-writes the status line + hooks with this seat baked in)`
1241
1644
  });
1242
1645
  }
1243
- const skill = existsSync6(skillPath(home));
1646
+ const skill = existsSync7(skillPath(home));
1244
1647
  checks.push({
1245
1648
  level: skill ? "ok" : "warn",
1246
1649
  name: "skill",
@@ -1255,10 +1658,10 @@ async function runDoctor(o) {
1255
1658
  detail: `${nodeVersion}${nodeOk(nodeVersion) ? "" : ` — seat-sync needs ≥ ${NODE_MIN} (node:sqlite)`}`,
1256
1659
  fix: nodeOk(nodeVersion) ? undefined : `brew install node@22 && echo 'export PATH="/opt/homebrew/opt/node@22/bin:$PATH"' >> ~/.zshrc`
1257
1660
  });
1258
- const which = (o.which ?? defaultWhich)("sleds");
1661
+ const which = (o.which ?? defaultWhich2)("sleds");
1259
1662
  const stale = which.filter((p) => {
1260
1663
  try {
1261
- const target = lstatSync2(p).isSymbolicLink() ? readlinkSync(p) : p;
1664
+ const target = lstatSync(p).isSymbolicLink() ? readlinkSync(p) : p;
1262
1665
  return /\/src\/index\.ts$/.test(target) || /\.bun\/bin\/sleds$/.test(p);
1263
1666
  } catch {
1264
1667
  return false;
@@ -1273,49 +1676,64 @@ async function runDoctor(o) {
1273
1676
  });
1274
1677
  else if (which.length > 0)
1275
1678
  checks.push({ level: "ok", name: "path", detail: which[0] ?? "" });
1276
- const pluginBody = existsSync6(pluginPath2(home)) ? readFileSync5(pluginPath2(home), "utf8") : null;
1277
- const dir = (o.swiftBarPluginDir ?? swiftBarPluginDir2)();
1278
- if (pluginBody) {
1279
- const baked = pluginEnv(pluginBody);
1280
- const linked = dir ? existsSync6(join7(dir, PLUGIN_NAME2)) : false;
1281
- const seatDesc = baked.sledsHome || baked.claudeConfigDir ? `seat at ${[baked.sledsHome && `SLEDS_HOME=${baked.sledsHome}`, baked.claudeConfigDir && `CLAUDE_CONFIG_DIR=${baked.claudeConfigDir}`].filter(Boolean).join(" ")}` : "default seat";
1282
- checks.push({
1283
- level: linked ? "ok" : "warn",
1284
- name: "menu bar",
1285
- detail: linked ? `${PLUGIN_NAME2} linked · ${seatDesc}` : `plugin written but not linked into SwiftBar${dir ? ` (${dir})` : " (SwiftBar not set up)"}`,
1286
- fix: linked ? undefined : "sleds bar --install"
1287
- });
1288
- } else {
1289
- checks.push({
1290
- level: "warn",
1291
- name: "menu bar",
1292
- detail: "no SwiftBar plugin",
1293
- fix: "sleds bar --install"
1294
- });
1295
- }
1296
- const agents = join7(home, "Library", "LaunchAgents");
1297
- const plists = existsSync6(agents) ? readdirSync(agents).filter((f) => f.startsWith("ai.sleds.") && f.endsWith(".plist")) : [];
1298
- const loaded = (o.launchctlList ?? defaultLaunchctl)();
1299
1679
  const seat = readSeatState(home);
1300
1680
  const age = seat.window ? Math.round((Date.now() - seat.window.capturedAt) / 60000) : null;
1301
- if (plists.length === 0)
1302
- checks.push({
1681
+ checks.push({
1682
+ level: age === null ? "warn" : "ok",
1683
+ name: "seat quota",
1684
+ detail: age === null ? "no report yet — run one Claude Code turn on this seat" : `last report ${age < 1 ? "just now" : `${age}m ago`} via the status line${seat.window?.sevenDay ? ` · wk ${Math.round(seat.window.sevenDay.usedPct)}%` : ""}`,
1685
+ fix: age === null ? "open claude, say hi, exit — the status line posts the seat's quota" : undefined
1686
+ });
1687
+ if ((o.platform ?? process.platform) === "darwin") {
1688
+ const agent = musherAgentState(home, env);
1689
+ checks.push(!agent.installed ? {
1303
1690
  level: "warn",
1304
- name: "seat-sync",
1305
- detail: "no timer installed (seat bars stay empty)",
1306
- fix: "sleds seat-sync --install"
1307
- });
1308
- else {
1309
- const running = plists.filter((p) => loaded.includes(p.replace(/\.plist$/, "")));
1310
- checks.push({
1311
- level: running.length === plists.length ? age !== null && age < 6 * 60 ? "ok" : "warn" : "warn",
1312
- name: "seat-sync",
1313
- detail: `${running.length}/${plists.length} timer${plists.length === 1 ? "" : "s"} loaded${age !== null ? ` · last read ${age}m ago` : " · no read yet"}`,
1314
- fix: running.length === plists.length && age !== null && age < 6 * 60 ? undefined : "sleds seat-sync (run once by hand and read its message)"
1691
+ name: "musher",
1692
+ detail: "agent not installed — `sleds defer` jobs would wait for `sleds run`",
1693
+ fix: "sleds musher --install"
1694
+ } : !agent.thisSeat ? {
1695
+ level: "warn",
1696
+ name: "musher",
1697
+ detail: "agent installed for a different seat (SLEDS_HOME differs)",
1698
+ fix: "sleds musher --install"
1699
+ } : { level: "ok", name: "musher", detail: `launchd agent ${MUSHER_LABEL}, every 5 min` });
1700
+ const claude = findClaude(home, readConfig(home).musher?.claudePath, (c) => (o.which ?? defaultWhich2)(c)[0] ?? null);
1701
+ checks.push(claude ? { level: "ok", name: "claude cli", detail: claude } : {
1702
+ level: "warn",
1703
+ name: "claude cli",
1704
+ detail: "`claude` not on PATH — the musher cannot run Claude Code",
1705
+ fix: 'sleds musher --claude "$(which claude)"'
1315
1706
  });
1707
+ const sleep = (o.pmset ?? defaultPmset)();
1708
+ if (sleep !== null)
1709
+ checks.push(sleep.acSleepMinutes === 0 ? {
1710
+ level: "ok",
1711
+ name: "awake",
1712
+ detail: "does not sleep on power — deferred jobs can start"
1713
+ } : {
1714
+ level: "warn",
1715
+ name: "awake",
1716
+ detail: `sleeps after ${sleep.acSleepMinutes} min on power — a deferred job needs the Mac awake at the reset`,
1717
+ fix: "System Settings → Battery → Options → Prevent automatic sleeping on power adapter (or: sudo pmset -c sleep 0)"
1718
+ });
1316
1719
  }
1317
1720
  return checks;
1318
1721
  }
1722
+ function parsePmset(text) {
1723
+ const ac = text.split(/AC Power:/i)[1];
1724
+ const block = ac ?? text;
1725
+ const m = /^\s*sleep\s+(\d+)/m.exec(block);
1726
+ if (!m)
1727
+ return null;
1728
+ return { acSleepMinutes: Number.parseInt(m[1] ?? "0", 10) };
1729
+ }
1730
+ var defaultPmset = () => {
1731
+ try {
1732
+ return parsePmset(execFileSync("pmset", ["-g", "custom"], { encoding: "utf8", timeout: 2000 }));
1733
+ } catch {
1734
+ return null;
1735
+ }
1736
+ };
1319
1737
  var doctor = async (o) => {
1320
1738
  const log = o.log ?? console.log;
1321
1739
  const checks = await runDoctor(o);
@@ -1325,6 +1743,7 @@ var doctor = async (o) => {
1325
1743
  };
1326
1744
 
1327
1745
  // src/commands/hooks.ts
1746
+ init_settings();
1328
1747
  var hooks = (o) => {
1329
1748
  const log = o.log ?? console.log;
1330
1749
  const error = o.error ?? console.error;
@@ -1347,6 +1766,8 @@ var hooks = (o) => {
1347
1766
  };
1348
1767
 
1349
1768
  // src/commands/init.ts
1769
+ init_config();
1770
+ init_settings();
1350
1771
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "node:fs";
1351
1772
  import { createInterface } from "node:readline";
1352
1773
 
@@ -1383,6 +1804,8 @@ When the proxy is active it answers a walled request with its options. The user
1383
1804
  /sled 6 defer to reset: the job runs itself when the seat resets — same model, done in minutes, full price (offered when the wall has a reset and the org has a key)
1384
1805
  \`\`\`
1385
1806
 
1807
+ No key (Team plan, seat-first)? The musher still runs: \`sleds defer\` in a terminal schedules this Mac to resume the current session itself at the reset — \`claude --resume\` on the seat's own quota, the model pinned. \`sleds musher --status\` lists what is waiting; \`sleds run <jobId>\` runs one now; the Mac must be awake at the reset (\`sleds doctor\` checks). When the user says "defer this" or "pick it up at the reset" and the org has no key, tell them to run \`sleds defer\`.
1808
+
1386
1809
  A hand-off carries a brief (the packet): what, note, cost so far, how to pick it up. The \`handoffs\` MCP tool and the dashboard's Jobs page show it; read it before taking one.
1387
1810
 
1388
1811
  ## Window reset
@@ -1397,9 +1820,9 @@ then restart \`claude\`. Settings go back to exactly what they were.
1397
1820
 
1398
1821
  ## Check
1399
1822
 
1400
- \`sleds status\` — on/off, since, reverts at, proxy health, wall mode, and the org's shape (\`Org:\` / \`Wiring:\`). An org with no platform key runs seat-first and its walls offer hold (\`/sled 2\`) and hand-off (\`/sled 5\`) only — continue, overnight and switch need a key an admin adds on the dashboard.
1823
+ \`sleds status\` — on/off, since, reverts at, proxy health, wall mode, and the org's shape (\`Org:\` / \`Wiring:\`). An org with no platform key runs seat-first and its walls offer hold (\`/sled 2\`), \`sleds defer\` (this Mac resumes at the reset) and hand-off (\`/sled 5\`) — continue, overnight and switch need a key an admin adds on the dashboard.
1401
1824
 
1402
- The status line's \`seat wk 52% · 5h 12%\` is the user's own Pro/Max window as Claude Code reports it (weekly, then 5-hour); \`window\` is the org key's headroom through the proxy. At \`wk 100%\` the answer is \`sleds on --until-reset\`.
1825
+ The status line's \`seat wk 52% · 5h 12%\` is the user's own Pro/Max window as Claude Code reports it (weekly, then 5-hour); \`window\` is the org key's headroom through the proxy. At \`wk 100%\` the answer is \`sleds on --until-reset\` (org key) or \`sleds defer\` (this Mac resumes at the reset, no key).
1403
1826
 
1404
1827
  ## /sled — estimate the task, offer overnight
1405
1828
 
@@ -1474,6 +1897,7 @@ From Cowork or any MCP client (\`sleds mcp\`): the \`handoffs\` tool lists what
1474
1897
  `;
1475
1898
 
1476
1899
  // src/commands/hooks.ts
1900
+ init_settings();
1477
1901
  var hooks2 = (o) => {
1478
1902
  const log = o.log ?? console.log;
1479
1903
  const error = o.error ?? console.error;
@@ -1497,11 +1921,11 @@ var hooks2 = (o) => {
1497
1921
 
1498
1922
  // src/commands/init.ts
1499
1923
  var DEFAULT_PROXY_URL = "http://localhost:8787";
1500
- var promptStdin = (question) => new Promise((resolve6) => {
1924
+ var promptStdin = (question) => new Promise((resolve4) => {
1501
1925
  const rl = createInterface({ input: process.stdin, output: process.stderr });
1502
1926
  rl.question(question, (answer) => {
1503
1927
  rl.close();
1504
- resolve6(answer.trim());
1928
+ resolve4(answer.trim());
1505
1929
  });
1506
1930
  });
1507
1931
  async function signupIndividual(proxyUrl, email, credential, fetchFn = fetch) {
@@ -1520,7 +1944,12 @@ async function signupRequest(proxyUrl, payload, fetchFn) {
1520
1944
  });
1521
1945
  const body = await res.json().catch(() => ({}));
1522
1946
  if (res.ok && typeof body.token === "string")
1523
- return { ok: true, token: body.token, created: body.created === true };
1947
+ return {
1948
+ ok: true,
1949
+ token: body.token,
1950
+ created: body.created === true,
1951
+ verifyPending: body.verifyPending === true
1952
+ };
1524
1953
  return {
1525
1954
  ok: false,
1526
1955
  reason: typeof body.reason === "string" ? body.reason : `http_${res.status}`,
@@ -1606,7 +2035,7 @@ var resolveToken = async (o) => {
1606
2035
  if (typed)
1607
2036
  return { token: typed, source: "prompt" };
1608
2037
  }
1609
- const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? DEFAULT_PROXY_URL);
2038
+ const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? readConfig(o.home).proxyUrl ?? DEFAULT_PROXY_URL);
1610
2039
  if (!url)
1611
2040
  return null;
1612
2041
  const log = o.log ?? console.log;
@@ -1631,6 +2060,8 @@ var resolveToken = async (o) => {
1631
2060
  return null;
1632
2061
  }
1633
2062
  log(`Created ${team} with you as owner (${email}).${credential2 ? " Your platform key is stored encrypted as the org's upstream key." : " No platform key yet — add one on the dashboard (Admin → Keys) to unlock continue / overnight / switch at a wall."}`);
2063
+ if (r2.verifyPending)
2064
+ log(`Check ${email} for a verification link and click it — inviting members unlocks then (an unverified team is removed after 48 h; your own seat works meanwhile).`);
1634
2065
  log("Invite your team on the dashboard (Members → invite by email, Admin → Mint a seat); each person runs `sleds init --token …`.");
1635
2066
  return { token: r2.token, source: "signup" };
1636
2067
  }
@@ -1662,7 +2093,7 @@ var init = async (o) => {
1662
2093
  error("Invalid seat token: expected something like sled_xxxxxxxx.");
1663
2094
  return 2;
1664
2095
  }
1665
- const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? DEFAULT_PROXY_URL);
2096
+ const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? readConfig(o.home).proxyUrl ?? DEFAULT_PROXY_URL);
1666
2097
  if (!url) {
1667
2098
  error(`Invalid proxy URL: ${JSON.stringify(o.url ?? env.SLEDS_PROXY_URL ?? "")}. Expected https://proxy.sleds.ai.`);
1668
2099
  return 2;
@@ -1712,6 +2143,11 @@ var init = async (o) => {
1712
2143
  mkdirSync6(skillDir(o.home), { recursive: true });
1713
2144
  writeFileSync6(skillPath(o.home), SKILL_MD);
1714
2145
  wrote.push(`installed skill at ${skillPath(o.home)}`);
2146
+ if (o.installMusher) {
2147
+ const r = o.installMusher();
2148
+ if (r)
2149
+ wrote.push(r);
2150
+ }
1715
2151
  const orgLine = org ? `${org.name} · ${org.plan}${org.capabilities.upstreamKey ? ` · ${org.capabilities.upstreamSource === "own" ? "org key" : "shared key"}` : " · no platform key"}` : "org unknown (proxy did not answer /proxy/meter)";
1716
2152
  if (finalWiring === "proxy-always") {
1717
2153
  log(`Claude Code now runs through SLEDS on the org key. (${orgLine})`);
@@ -1730,7 +2166,7 @@ var init = async (o) => {
1730
2166
  log("");
1731
2167
  log("Claude Code keeps running on your seat; the status line reports its quota to SLEDS on every turn.");
1732
2168
  if (org && !org.capabilities.upstreamKey) {
1733
- log("This org has no platform key, so at a wall the choices are hold until reset or hand off to a teammate. An admin can add a key on the dashboard to unlock continue / overnight / switch.");
2169
+ log("This org has no platform key, so at a wall the choices are hold until reset, `sleds defer` (this Mac resumes the session itself at the reset), or hand off to a teammate. An admin can add a key on the dashboard to unlock continue / overnight / switch.");
1734
2170
  } else {
1735
2171
  log("At a wall: `sleds on --until-reset +5h` (or /sled 1) switches to the org key through the proxy until the reset; `sleds off` puts you back.");
1736
2172
  }
@@ -1739,6 +2175,7 @@ var init = async (o) => {
1739
2175
  };
1740
2176
 
1741
2177
  // src/mcp.ts
2178
+ init_meter();
1742
2179
  var PROTOCOL_VERSION = "2025-06-18";
1743
2180
  var SERVER_INFO = { name: "sleds", version: "0.0.1" };
1744
2181
  var MCP_TIMEOUT_MS = 5000;
@@ -2272,6 +2709,8 @@ var mcp = async (o) => {
2272
2709
  };
2273
2710
 
2274
2711
  // src/commands/meter.ts
2712
+ init_config();
2713
+ init_meter();
2275
2714
  var meter = async (o) => {
2276
2715
  const log = o.log ?? console.log;
2277
2716
  const now = (o.now ?? Date.now)();
@@ -2290,14 +2729,232 @@ var meter = async (o) => {
2290
2729
  return 0;
2291
2730
  };
2292
2731
 
2732
+ // src/commands/musher.ts
2733
+ init_config();
2734
+ init_meter();
2735
+ import { spawnSync as spawnSync2 } from "node:child_process";
2736
+ import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
2737
+ import { hostname as hostname2 } from "node:os";
2738
+ import { dirname as dirname3 } from "node:path";
2739
+ var defaultWhich3 = (cmd) => {
2740
+ const path = process.env.PATH ?? "";
2741
+ for (const dir of path.split(":").filter(Boolean)) {
2742
+ const p = `${dir}/${cmd}`;
2743
+ if (existsSync8(p))
2744
+ return p;
2745
+ }
2746
+ return null;
2747
+ };
2748
+ var findClaude2 = (home, configured, which) => {
2749
+ if (configured && existsSync8(configured))
2750
+ return configured;
2751
+ const found = which("claude");
2752
+ if (found)
2753
+ return found;
2754
+ for (const p of [
2755
+ `${home}/.local/bin/claude`,
2756
+ "/opt/homebrew/bin/claude",
2757
+ "/usr/local/bin/claude",
2758
+ `${home}/.npm-global/bin/claude`
2759
+ ])
2760
+ if (existsSync8(p))
2761
+ return p;
2762
+ return null;
2763
+ };
2764
+ var musherInstall = (o) => {
2765
+ const log = o.log ?? console.log;
2766
+ const error = o.error ?? console.error;
2767
+ const platform = o.platform ?? process.platform;
2768
+ if (platform !== "darwin") {
2769
+ error(`sleds musher --install needs launchd (macOS). On ${platform}, run \`sleds musher tick\` from cron every 5 minutes.`);
2770
+ return 1;
2771
+ }
2772
+ const env = o.env ?? process.env;
2773
+ const plist = musherPlistPath(o.home);
2774
+ const dir = configDir(o.home, env);
2775
+ mkdirSync7(dirname3(plist), { recursive: true });
2776
+ mkdirSync7(dir, { recursive: true, mode: 448 });
2777
+ const execPath = o.sledsArgv[0] ?? process.execPath;
2778
+ const agentEnv = {
2779
+ PATH: musherPath(o.home, execPath, env.PATH ?? ""),
2780
+ HOME: o.home,
2781
+ ...env.SLEDS_HOME ? { SLEDS_HOME: env.SLEDS_HOME } : {},
2782
+ ...env.CLAUDE_CONFIG_DIR ? { CLAUDE_CONFIG_DIR: env.CLAUDE_CONFIG_DIR } : {}
2783
+ };
2784
+ const exec = o.exec ?? defaultExecSync;
2785
+ if (existsSync8(plist))
2786
+ exec("launchctl", ["unload", plist]);
2787
+ writeFileSync7(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir) }));
2788
+ const r = exec("launchctl", ["load", plist]);
2789
+ if (!r.ok) {
2790
+ error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
2791
+ return 1;
2792
+ }
2793
+ const cfg = readConfig(o.home);
2794
+ writeConfig(o.home, { ...cfg, musher: { ...cfg.musher ?? {}, agent: "launchd" } });
2795
+ const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
2796
+ log(`musher installed: launchd agent ${MUSHER_LABEL}, every ${5} min · log ${musherLogPath(dir)}`);
2797
+ if (!claude)
2798
+ log("warn: `claude` not found on PATH — set it with `sleds musher --claude /path/to/claude`");
2799
+ return 0;
2800
+ };
2801
+ var musherUninstall = (o) => {
2802
+ const log = o.log ?? console.log;
2803
+ const plist = musherPlistPath(o.home);
2804
+ const exec = o.exec ?? defaultExecSync;
2805
+ if (existsSync8(plist)) {
2806
+ exec("launchctl", ["unload", plist]);
2807
+ rmSync2(plist, { force: true });
2808
+ log(`musher removed: ${MUSHER_LABEL}`);
2809
+ } else
2810
+ log("musher was not installed.");
2811
+ const cfg = readConfig(o.home);
2812
+ if (cfg.musher)
2813
+ writeConfig(o.home, { ...cfg, musher: { ...cfg.musher, agent: "none" } });
2814
+ return 0;
2815
+ };
2816
+ var formatMusherStatus = (list, now) => {
2817
+ const lines = [];
2818
+ for (const j of list.running)
2819
+ lines.push(`running ${j.jobId}${j.startedAt ? ` · since ${formatLocal(j.startedAt)} (${formatDuration(now - j.startedAt)})` : ""}`);
2820
+ for (const j of list.due)
2821
+ lines.push(`due now ${j.jobId}${j.model ? ` · ${j.model}` : ""} · was ${formatLocal(j.scheduledAt)}`);
2822
+ for (const j of list.upcoming)
2823
+ lines.push(`waiting ${j.jobId}${j.model ? ` · ${j.model}` : ""} · starts ${formatLocal(j.scheduledAt)} (in ${formatDuration(j.scheduledAt - now)})`);
2824
+ if (lines.length === 0)
2825
+ lines.push("nothing deferred on this seat.");
2826
+ return lines;
2827
+ };
2828
+ var clientFor = (o) => {
2829
+ if (o.client)
2830
+ return o.client;
2831
+ const src = meterSource(o.home);
2832
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
2833
+ };
2834
+ var musherStatus = async (o) => {
2835
+ const log = o.log ?? console.log;
2836
+ const now = (o.now ?? Date.now)();
2837
+ const plist = musherPlistPath(o.home);
2838
+ const cfg = readConfig(o.home);
2839
+ log(existsSync8(plist) ? `agent ${MUSHER_LABEL} installed (${plist})` : "agent not installed — sleds musher --install");
2840
+ const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
2841
+ log(`claude ${claude ?? "not found — sleds musher --claude /path/to/claude"}`);
2842
+ log(`mode ${cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
2843
+ const client = clientFor(o);
2844
+ if (!client) {
2845
+ log("seat not initialised (sleds init)");
2846
+ return 1;
2847
+ }
2848
+ const list = await client.list();
2849
+ if (!list) {
2850
+ log("proxy unreachable");
2851
+ return 1;
2852
+ }
2853
+ for (const line of formatMusherStatus(list, now))
2854
+ log(line);
2855
+ return 0;
2856
+ };
2857
+ var musherTick = async (o) => {
2858
+ const log = o.log ?? console.log;
2859
+ const client = clientFor(o);
2860
+ if (!client) {
2861
+ log("musher: seat not initialised; nothing to do");
2862
+ return 1;
2863
+ }
2864
+ const list = await client.list();
2865
+ if (!list) {
2866
+ log("musher: proxy unreachable; will try again next tick");
2867
+ return 0;
2868
+ }
2869
+ if (list.due.length === 0)
2870
+ return 0;
2871
+ const cfg = readConfig(o.home);
2872
+ const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3) ?? "claude";
2873
+ const seat = readSeatState(o.home);
2874
+ for (const job of list.due) {
2875
+ const out = await runJob({
2876
+ job,
2877
+ client,
2878
+ fallbackSession: seat.session,
2879
+ host: o.host ?? hostname2(),
2880
+ claudePath: claude,
2881
+ permissionMode: cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE,
2882
+ timeoutMs: (cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES) * 60000,
2883
+ exec: o.execCapture,
2884
+ log
2885
+ });
2886
+ if (out.kind === "skipped")
2887
+ log(`musher: job ${job.jobId} skipped — ${out.reason}`);
2888
+ }
2889
+ return 0;
2890
+ };
2891
+ var musherConfigure = (o, flags) => {
2892
+ const log = o.log ?? console.log;
2893
+ const cfg = readConfig(o.home);
2894
+ const musher = { ...cfg.musher ?? {} };
2895
+ if (flags.claude)
2896
+ musher.claudePath = flags.claude;
2897
+ if (flags.mode)
2898
+ musher.permissionMode = flags.mode;
2899
+ if (flags.timeout) {
2900
+ const n = Number.parseInt(flags.timeout, 10);
2901
+ if (!Number.isFinite(n) || n <= 0) {
2902
+ (o.error ?? console.error)("--timeout takes minutes, a positive number");
2903
+ return 1;
2904
+ }
2905
+ musher.timeoutMinutes = n;
2906
+ }
2907
+ writeConfig(o.home, { ...cfg, musher });
2908
+ log(`musher settings: claude ${musher.claudePath ?? "(PATH)"} · mode ${musher.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${musher.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
2909
+ return 0;
2910
+ };
2911
+ var defaultExecSync = (cmd, args) => {
2912
+ const r = spawnSync2(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
2913
+ if (r.error)
2914
+ return { ok: false, stderr: r.error.message };
2915
+ return { ok: r.status === 0, stderr: r.stderr ?? "" };
2916
+ };
2917
+ var musher = async (o) => {
2918
+ const args = [...o.argv];
2919
+ const flags = {};
2920
+ const positional = [];
2921
+ for (let i = 0;i < args.length; i++) {
2922
+ const a = args[i] ?? "";
2923
+ if (a.startsWith("--")) {
2924
+ const next = args[i + 1];
2925
+ if (next !== undefined && !next.startsWith("--")) {
2926
+ flags[a.slice(2)] = next;
2927
+ i++;
2928
+ } else
2929
+ flags[a.slice(2)] = "true";
2930
+ } else
2931
+ positional.push(a);
2932
+ }
2933
+ if (flags.install)
2934
+ return musherInstall(o);
2935
+ if (flags.uninstall)
2936
+ return musherUninstall(o);
2937
+ if (flags.claude || flags.mode || flags.timeout)
2938
+ return musherConfigure(o, flags);
2939
+ if (positional[0] === "tick")
2940
+ return musherTick(o);
2941
+ if (flags.status || positional.length === 0)
2942
+ return musherStatus(o);
2943
+ (o.error ?? console.error)(`sleds musher: unknown argument ${positional[0]}`);
2944
+ return 1;
2945
+ };
2946
+
2947
+ // src/commands/off.ts
2948
+ init_config();
2949
+
2293
2950
  // src/schedule.ts
2294
- import { existsSync as existsSync7, mkdirSync as mkdirSync7, rmSync, writeFileSync as writeFileSync7 } from "node:fs";
2295
- import { join as join8 } from "node:path";
2951
+ import { existsSync as existsSync9, mkdirSync as mkdirSync8, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
2952
+ import { join as join7 } from "node:path";
2296
2953
  var REVERT_LABEL = "ai.sleds.revert";
2297
- var plistPath = (home) => join8(home, "Library", "LaunchAgents", `${REVERT_LABEL}.plist`);
2298
- var xmlEscape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2954
+ var plistPath = (home) => join7(home, "Library", "LaunchAgents", `${REVERT_LABEL}.plist`);
2955
+ var xmlEscape2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2299
2956
  var buildLaunchdPlist = (sledsArgv, deadline) => {
2300
- const args = [...sledsArgv, "off"].map((a) => ` <string>${xmlEscape(a)}</string>`).join(`
2957
+ const args = [...sledsArgv, "off"].map((a) => ` <string>${xmlEscape2(a)}</string>`).join(`
2301
2958
  `);
2302
2959
  return `<?xml version="1.0" encoding="UTF-8"?>
2303
2960
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -2333,9 +2990,9 @@ var scheduleRevert = (d, deadlineMs) => {
2333
2990
  if (d.platform === "darwin") {
2334
2991
  const p = plistPath(d.home);
2335
2992
  try {
2336
- mkdirSync7(join8(d.home, "Library", "LaunchAgents"), { recursive: true });
2993
+ mkdirSync8(join7(d.home, "Library", "LaunchAgents"), { recursive: true });
2337
2994
  d.exec("launchctl", ["unload", p]);
2338
- writeFileSync7(p, buildLaunchdPlist(d.sledsArgv, deadline));
2995
+ writeFileSync8(p, buildLaunchdPlist(d.sledsArgv, deadline));
2339
2996
  const r = d.exec("launchctl", ["load", p]);
2340
2997
  if (r.ok)
2341
2998
  return { kind: "launchd", detail: `launchd agent ${REVERT_LABEL}` };
@@ -2362,9 +3019,9 @@ var scheduleRevert = (d, deadlineMs) => {
2362
3019
  var cancelRevert = (d, kind) => {
2363
3020
  if (kind === "launchd" || d.platform === "darwin") {
2364
3021
  const p = plistPath(d.home);
2365
- if (existsSync7(p)) {
3022
+ if (existsSync9(p)) {
2366
3023
  d.exec("launchctl", ["unload", p]);
2367
- rmSync(p, { force: true });
3024
+ rmSync3(p, { force: true });
2368
3025
  }
2369
3026
  }
2370
3027
  if (kind === "systemd" || d.platform === "linux") {
@@ -2373,6 +3030,7 @@ var cancelRevert = (d, kind) => {
2373
3030
  };
2374
3031
 
2375
3032
  // src/commands/off.ts
3033
+ init_settings();
2376
3034
  var off = (o) => {
2377
3035
  const log = o.log ?? console.log;
2378
3036
  const cfg = readConfig(o.home);
@@ -2400,7 +3058,13 @@ var off = (o) => {
2400
3058
  return 0;
2401
3059
  };
2402
3060
 
3061
+ // src/commands/on.ts
3062
+ init_config();
3063
+ init_settings();
3064
+
2403
3065
  // src/commands/init.ts
3066
+ init_config();
3067
+ init_settings();
2404
3068
  var DEFAULT_PROXY_URL2 = "http://localhost:8787";
2405
3069
 
2406
3070
  // src/commands/on.ts
@@ -2480,7 +3144,70 @@ var on = (o) => {
2480
3144
  return 0;
2481
3145
  };
2482
3146
 
3147
+ // src/commands/run.ts
3148
+ init_config();
3149
+ init_meter();
3150
+ import { hostname as hostname3 } from "node:os";
3151
+ var run = async (o) => {
3152
+ const log = o.log ?? console.log;
3153
+ const error = o.error ?? console.error;
3154
+ if (!o.jobId) {
3155
+ error("usage: sleds run <jobId>");
3156
+ return 2;
3157
+ }
3158
+ const client = o.client ?? (() => {
3159
+ const src = meterSource(o.home);
3160
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
3161
+ })();
3162
+ if (!client) {
3163
+ error("No seat on this machine. Run `sleds init` first.");
3164
+ return 1;
3165
+ }
3166
+ const list = await client.list();
3167
+ if (!list) {
3168
+ error("Could not reach the SLEDS proxy.");
3169
+ return 1;
3170
+ }
3171
+ const job = [...list.due, ...list.upcoming].find((j) => j.jobId === o.jobId);
3172
+ if (!job) {
3173
+ error(`Job ${o.jobId} is not a scheduled local job on this seat (sleds musher --status lists them).`);
3174
+ return 1;
3175
+ }
3176
+ const cfg = readConfig(o.home);
3177
+ const claude = findClaude(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich);
3178
+ if (!claude) {
3179
+ error("`claude` not found. Install Claude Code, or `sleds musher --claude /path/to/claude`.");
3180
+ return 1;
3181
+ }
3182
+ const out = await runJob({
3183
+ job,
3184
+ client,
3185
+ fallbackSession: readSeatState(o.home).session,
3186
+ host: o.host ?? hostname3(),
3187
+ claudePath: claude,
3188
+ permissionMode: cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE,
3189
+ timeoutMs: (cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES) * 60000,
3190
+ exec: o.execCapture,
3191
+ log
3192
+ });
3193
+ if (out.kind === "skipped") {
3194
+ error(`Not run: ${out.reason}`);
3195
+ return 1;
3196
+ }
3197
+ log(out.summary);
3198
+ if (out.ok) {
3199
+ const a = await client.attestation(job.jobId);
3200
+ if (a)
3201
+ log(a.attestation.trailer);
3202
+ else
3203
+ log(`(attestation pending — sleds verify ${job.jobId})`);
3204
+ }
3205
+ return out.ok ? 0 : 1;
3206
+ };
3207
+
2483
3208
  // src/commands/seatEvent.ts
3209
+ init_config();
3210
+ init_meter();
2484
3211
  var HOOK_STDIN_TIMEOUT_MS = 1000;
2485
3212
  var NOTIFICATION_TITLE = "sleds";
2486
3213
  var isObject7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
@@ -2533,15 +3260,15 @@ var seatEvent = async (o) => {
2533
3260
  };
2534
3261
 
2535
3262
  // src/commands/seatSync.ts
2536
- import { execFileSync as execFileSync4 } from "node:child_process";
2537
- import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync8 } from "node:fs";
3263
+ import { execFileSync as execFileSync2 } from "node:child_process";
3264
+ import { existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync8, readdirSync as readdirSync2, writeFileSync as writeFileSync9 } from "node:fs";
2538
3265
  import { platform } from "node:os";
2539
- import { join as join10 } from "node:path";
3266
+ import { join as join9 } from "node:path";
2540
3267
 
2541
3268
  // src/cookieDb.ts
2542
- import { copyFileSync, rmSync as rmSync2 } from "node:fs";
3269
+ import { copyFileSync, rmSync as rmSync4 } from "node:fs";
2543
3270
  import { tmpdir } from "node:os";
2544
- import { join as join9 } from "node:path";
3271
+ import { join as join8 } from "node:path";
2545
3272
  var QUERY = "SELECT name, hex(encrypted_value) AS ev FROM cookies WHERE host_key LIKE '%claude.ai%'";
2546
3273
  var isBun = () => typeof globalThis.Bun !== "undefined";
2547
3274
  async function queryRows(dbPath) {
@@ -2570,18 +3297,21 @@ function nodeSqlite() {
2570
3297
  return mod;
2571
3298
  }
2572
3299
  async function readCookieRows(dbPath) {
2573
- const copy = join9(tmpdir(), `sleds-cookies-${process.pid}-${Date.now()}.sqlite`);
3300
+ const copy = join8(tmpdir(), `sleds-cookies-${process.pid}-${Date.now()}.sqlite`);
2574
3301
  copyFileSync(dbPath, copy);
2575
3302
  try {
2576
3303
  const rows = await queryRows(copy);
2577
3304
  return rows.map((r) => ({ name: r.name, encrypted_value: Buffer.from(r.ev, "hex") }));
2578
3305
  } finally {
2579
3306
  try {
2580
- rmSync2(copy, { force: true });
3307
+ rmSync4(copy, { force: true });
2581
3308
  } catch {}
2582
3309
  }
2583
3310
  }
2584
3311
 
3312
+ // src/commands/seatSync.ts
3313
+ init_meter();
3314
+
2585
3315
  // src/seatSync.ts
2586
3316
  import { createDecipheriv, pbkdf2Sync } from "node:crypto";
2587
3317
  var BROWSERS = {
@@ -2727,13 +3457,13 @@ function parseUsageResponse(raw, now = Date.now()) {
2727
3457
  const resetsAt = parseResetsAt(v.resets_at);
2728
3458
  if (usedPct === undefined || usedPct < 0 || resetsAt === undefined)
2729
3459
  continue;
2730
- const bar2 = { usedPct, resetsAt };
3460
+ const bar = { usedPct, resetsAt };
2731
3461
  if (k === "five_hour")
2732
- out.fiveHour = bar2;
3462
+ out.fiveHour = bar;
2733
3463
  else if (k === "seven_day")
2734
- out.sevenDay = bar2;
3464
+ out.sevenDay = bar;
2735
3465
  else
2736
- out.models[k] = bar2;
3466
+ out.models[k] = bar;
2737
3467
  }
2738
3468
  const hasAny = out.fiveHour || out.sevenDay || Object.keys(out.models).length > 0 || out.usedCreditsUsd !== undefined;
2739
3469
  return hasAny ? out : null;
@@ -2843,7 +3573,7 @@ function findFile(root, name) {
2843
3573
  const stack = [root];
2844
3574
  while (stack.length > 0) {
2845
3575
  const dir = stack.pop();
2846
- if (!dir || !existsSync8(dir))
3576
+ if (!dir || !existsSync10(dir))
2847
3577
  continue;
2848
3578
  let entries;
2849
3579
  try {
@@ -2852,7 +3582,7 @@ function findFile(root, name) {
2852
3582
  continue;
2853
3583
  }
2854
3584
  for (const e of entries) {
2855
- const p = join10(dir, e.name);
3585
+ const p = join9(dir, e.name);
2856
3586
  if (e.isDirectory())
2857
3587
  stack.push(p);
2858
3588
  else if (e.name === name)
@@ -2863,8 +3593,8 @@ function findFile(root, name) {
2863
3593
  }
2864
3594
  function findCookieDb(home, source) {
2865
3595
  const b = BROWSERS[source.browser];
2866
- const root = join10(home, APP_SUPPORT, b.appDir);
2867
- if (!existsSync8(root))
3596
+ const root = join9(home, APP_SUPPORT, b.appDir);
3597
+ if (!existsSync10(root))
2868
3598
  throw new Error(`no ${source.browser} data at ${root} (is it installed?)`);
2869
3599
  if (!b.profiled) {
2870
3600
  const db2 = findFile(root, "Cookies");
@@ -2872,15 +3602,15 @@ function findCookieDb(home, source) {
2872
3602
  throw new Error(`no cookie store under ${root}`);
2873
3603
  return db2;
2874
3604
  }
2875
- const localStatePath = join10(root, "Local State");
2876
- const localState = existsSync8(localStatePath) ? readFileSync6(localStatePath, "utf8") : null;
3605
+ const localStatePath = join9(root, "Local State");
3606
+ const localState = existsSync10(localStatePath) ? readFileSync8(localStatePath, "utf8") : null;
2877
3607
  const wanted = source.profile ?? "Default";
2878
3608
  const { dir, ambiguous } = resolveProfileDir(localState, wanted);
2879
3609
  if (ambiguous)
2880
3610
  throw new Error(`"${wanted}" names ${ambiguous.length} ${source.browser} profiles (${ambiguous.join(", ")}) — pass the directory or the account email instead`);
2881
3611
  if (!dir)
2882
3612
  throw new Error(`no ${source.browser} profile named "${wanted}" (check chrome://version)`);
2883
- const db = cookieDbCandidates(join10(root, dir)).find((p) => existsSync8(p));
3613
+ const db = cookieDbCandidates(join9(root, dir)).find((p) => existsSync10(p));
2884
3614
  if (!db)
2885
3615
  throw new Error(`profile "${dir}" has no cookie store yet (open claude.ai in it once)`);
2886
3616
  return db;
@@ -2890,7 +3620,7 @@ async function readCookieRowsBun(home, source) {
2890
3620
  }
2891
3621
  function keychainKey(service) {
2892
3622
  try {
2893
- return execFileSync4("security", ["find-generic-password", "-w", "-s", service], {
3623
+ return execFileSync2("security", ["find-generic-password", "-w", "-s", service], {
2894
3624
  encoding: "utf8"
2895
3625
  }).trim();
2896
3626
  } catch {
@@ -2902,7 +3632,7 @@ function decryptJar(rows, safeStoragePw) {
2902
3632
  }
2903
3633
  var curlUsage = (org, cookie) => {
2904
3634
  try {
2905
- const out = execFileSync4("curl", usageCurlArgs(org, cookie), {
3635
+ const out = execFileSync2("curl", usageCurlArgs(org, cookie), {
2906
3636
  encoding: "utf8",
2907
3637
  maxBuffer: 8000000
2908
3638
  });
@@ -2911,8 +3641,8 @@ var curlUsage = (org, cookie) => {
2911
3641
  return { status: 0, body: "" };
2912
3642
  }
2913
3643
  };
2914
- function fetchSeatUsage(jar, org, now, run) {
2915
- const { status, body } = run(org, cookieHeader(jar, org));
3644
+ function fetchSeatUsage(jar, org, now, run2) {
3645
+ const { status, body } = run2(org, cookieHeader(jar, org));
2916
3646
  if (status !== 200)
2917
3647
  return { usage: null, status };
2918
3648
  try {
@@ -2923,7 +3653,7 @@ function fetchSeatUsage(jar, org, now, run) {
2923
3653
  }
2924
3654
  function launchAgentPlist(bun, script, home, source) {
2925
3655
  const label = launchLabel(source);
2926
- const log = join10(home, ".config", "sleds", `${label.slice("ai.sleds.".length)}.log`);
3656
+ const log = join9(home, ".config", "sleds", `${label.slice("ai.sleds.".length)}.log`);
2927
3657
  const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2928
3658
  const args = [bun, script, "seat-sync", ...sourceArgs(source)].map((a) => ` <string>${esc(a)}</string>`).join(`
2929
3659
  `);
@@ -2945,32 +3675,32 @@ ${args}
2945
3675
  `;
2946
3676
  }
2947
3677
  function plistPath2(home, source) {
2948
- return join10(home, "Library", "LaunchAgents", `${launchLabel(source)}.plist`);
3678
+ return join9(home, "Library", "LaunchAgents", `${launchLabel(source)}.plist`);
2949
3679
  }
2950
- function install2(home, argv, source, log) {
3680
+ function install(home, argv, source, log) {
2951
3681
  const bun = argv[0] ?? process.execPath;
2952
3682
  const script = argv[1] ?? "";
2953
3683
  if (!script) {
2954
3684
  log("cannot resolve the sleds script path to install the timer");
2955
3685
  return 1;
2956
3686
  }
2957
- const dir = join10(home, "Library", "LaunchAgents");
2958
- mkdirSync8(dir, { recursive: true });
3687
+ const dir = join9(home, "Library", "LaunchAgents");
3688
+ mkdirSync9(dir, { recursive: true });
2959
3689
  const path = plistPath2(home, source);
2960
- writeFileSync8(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
3690
+ writeFileSync9(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
2961
3691
  try {
2962
- execFileSync4("launchctl", ["unload", path], { stdio: "ignore" });
3692
+ execFileSync2("launchctl", ["unload", path], { stdio: "ignore" });
2963
3693
  } catch {}
2964
- execFileSync4("launchctl", ["load", "-w", path]);
3694
+ execFileSync2("launchctl", ["load", "-w", path]);
2965
3695
  log(`seat-sync timer installed (every ${SYNC_INTERVAL_S / 60} min) → ${path}`);
2966
3696
  return 0;
2967
3697
  }
2968
- function uninstall2(home, source, log) {
3698
+ function uninstall(home, source, log) {
2969
3699
  const path = plistPath2(home, source);
2970
3700
  try {
2971
- execFileSync4("launchctl", ["unload", "-w", path], { stdio: "ignore" });
3701
+ execFileSync2("launchctl", ["unload", "-w", path], { stdio: "ignore" });
2972
3702
  } catch {}
2973
- log(existsSync8(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
3703
+ log(existsSync10(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
2974
3704
  return 0;
2975
3705
  }
2976
3706
  var seatSync = async (o) => {
@@ -2983,9 +3713,9 @@ var seatSync = async (o) => {
2983
3713
  }
2984
3714
  const { source } = parsed;
2985
3715
  if (source.uninstall)
2986
- return uninstall2(o.home, source, log);
3716
+ return uninstall(o.home, source, log);
2987
3717
  if (source.install)
2988
- return install2(o.home, [process.execPath, process.argv[1] ?? ""], source, log);
3718
+ return install(o.home, [process.execPath, process.argv[1] ?? ""], source, log);
2989
3719
  if (platform() !== "darwin" && !o.readCookieRows) {
2990
3720
  log("seat-sync is macOS-only for now.");
2991
3721
  return 2;
@@ -3065,7 +3795,8 @@ async function postSeat(proxyUrl, token, body, fetchFn) {
3065
3795
  }
3066
3796
 
3067
3797
  // src/commands/specEvent.ts
3068
- import { readFileSync as readFileSync7, statSync } from "node:fs";
3798
+ init_meter();
3799
+ import { readFileSync as readFileSync9, statSync } from "node:fs";
3069
3800
  import { basename, isAbsolute, relative } from "node:path";
3070
3801
  var HOOK_STDIN_TIMEOUT_MS2 = 1000;
3071
3802
  var ESTIMATE_TIMEOUT_MS = 4000;
@@ -3121,7 +3852,7 @@ function readFileOrNull(path) {
3121
3852
  try {
3122
3853
  if (statSync(path).size > 2000000)
3123
3854
  return null;
3124
- return readFileSync7(path, "utf8");
3855
+ return readFileSync9(path, "utf8");
3125
3856
  } catch {
3126
3857
  return null;
3127
3858
  }
@@ -3164,7 +3895,9 @@ var specEvent = async (o) => {
3164
3895
  };
3165
3896
 
3166
3897
  // src/commands/status.ts
3167
- import { existsSync as existsSync9 } from "node:fs";
3898
+ init_config();
3899
+ init_settings();
3900
+ import { existsSync as existsSync11 } from "node:fs";
3168
3901
  var checkHealth2 = async (url, fetchFn = fetch, timeoutMs = 1000) => {
3169
3902
  try {
3170
3903
  const res = await fetchFn(`${url}/healthz`, { signal: AbortSignal.timeout(timeoutMs) });
@@ -3236,7 +3969,7 @@ var status = async (o) => {
3236
3969
  const log = o.log ?? console.log;
3237
3970
  const nowMs = (o.now ?? Date.now)();
3238
3971
  const cfg = readConfig(o.home);
3239
- const settings = existsSync9(settingsPath(o.home)) ? readSettings(o.home) : {};
3972
+ const settings = existsSync11(settingsPath(o.home)) ? readSettings(o.home) : {};
3240
3973
  const inSettings = readProxy(settings);
3241
3974
  const proxyUrl = inSettings?.url ?? cfg.proxyUrl;
3242
3975
  const token = inSettings?.token ?? cfg.token;
@@ -3290,15 +4023,19 @@ var status = async (o) => {
3290
4023
  };
3291
4024
 
3292
4025
  // src/commands/statusline.ts
4026
+ init_meter();
3293
4027
  var statusline = async (o) => {
3294
4028
  const log = o.log ?? console.log;
3295
4029
  const now = (o.now ?? Date.now)();
3296
4030
  const input = o.stdin ?? await readStdin(STDIN_TIMEOUT_MS);
3297
4031
  const window = seatWindowFromStdin(input, now);
4032
+ const session = seatSessionFromStdin(input, now);
3298
4033
  let posts = [];
3299
4034
  try {
3300
4035
  if (window)
3301
- posts = rememberSeat(o, window, now);
4036
+ posts = rememberSeat(o, window, session, now);
4037
+ else if (session)
4038
+ writeSeatState(o.home, { ...readSeatState(o.home), session });
3302
4039
  } catch {}
3303
4040
  const seatFragment = formatSeatFragment(window);
3304
4041
  let line;
@@ -3317,9 +4054,9 @@ var statusline = async (o) => {
3317
4054
  await Promise.allSettled(posts);
3318
4055
  return 0;
3319
4056
  };
3320
- var rememberSeat = (o, window, now) => {
4057
+ var rememberSeat = (o, window, session, now) => {
3321
4058
  const prior = readSeatState(o.home);
3322
- let state = { ...prior, window };
4059
+ let state = { ...prior, window, ...session ? { session } : {} };
3323
4060
  const src = meterSource(o.home);
3324
4061
  const posts = [];
3325
4062
  if (src) {
@@ -3342,11 +4079,18 @@ var rememberSeat = (o, window, now) => {
3342
4079
  };
3343
4080
 
3344
4081
  // src/commands/uninstall.ts
3345
- import { existsSync as existsSync10, rmSync as rmSync3 } from "node:fs";
3346
- var uninstall3 = (o) => {
4082
+ init_config();
4083
+ init_meter();
4084
+ import { spawnSync as spawnSync3 } from "node:child_process";
4085
+ import { existsSync as existsSync12, rmSync as rmSync5 } from "node:fs";
4086
+ init_settings();
4087
+ var defaultUnload = (plist) => {
4088
+ spawnSync3("launchctl", ["unload", plist], { stdio: "ignore" });
4089
+ };
4090
+ var uninstall2 = (o) => {
3347
4091
  const log = o.log ?? console.log;
3348
4092
  const removed = [];
3349
- if (existsSync10(settingsPath(o.home))) {
4093
+ if (existsSync12(settingsPath(o.home))) {
3350
4094
  let settings = readSettings(o.home);
3351
4095
  let changed = false;
3352
4096
  if (hasEitherKey(settings)) {
@@ -3378,16 +4122,22 @@ var uninstall3 = (o) => {
3378
4122
  writeSettings(o.home, settings);
3379
4123
  }
3380
4124
  for (const file of [meterCachePath(o.home), seatStatePath(o.home)]) {
3381
- if (existsSync10(file)) {
3382
- rmSync3(file, { force: true });
4125
+ if (existsSync12(file)) {
4126
+ rmSync5(file, { force: true });
3383
4127
  removed.push(file);
3384
4128
  }
3385
4129
  }
3386
4130
  const skill = skillDir(o.home);
3387
- if (existsSync10(skill)) {
3388
- rmSync3(skill, { recursive: true, force: true });
4131
+ if (existsSync12(skill)) {
4132
+ rmSync5(skill, { recursive: true, force: true });
3389
4133
  removed.push(skill);
3390
4134
  }
4135
+ const plist = musherPlistPath(o.home);
4136
+ if (existsSync12(plist)) {
4137
+ (o.unloadAgent ?? defaultUnload)(plist);
4138
+ rmSync5(plist, { force: true });
4139
+ removed.push(`launchd agent ${MUSHER_LABEL}`);
4140
+ }
3391
4141
  if (removed.length === 0) {
3392
4142
  log("Nothing to remove; SLEDS was not installed.");
3393
4143
  } else {
@@ -3405,10 +4155,58 @@ var hasEitherKey = (settings) => {
3405
4155
  return BASE_URL_KEY in env || AUTH_TOKEN_KEY in env;
3406
4156
  };
3407
4157
 
4158
+ // src/commands/verify.ts
4159
+ init_meter();
4160
+ var jobIdFromTarget = (target) => {
4161
+ const t = target.trim();
4162
+ const m = /SLEDS-Attest:.*\bjob=([^\s]+)/.exec(t);
4163
+ if (m?.[1])
4164
+ return m[1];
4165
+ return /^[A-Za-z0-9_-]{3,64}$/.test(t) ? t : null;
4166
+ };
4167
+ var verify = async (o) => {
4168
+ const log = o.log ?? console.log;
4169
+ const error = o.error ?? console.error;
4170
+ const jobId = o.target ? jobIdFromTarget(o.target) : null;
4171
+ if (!jobId) {
4172
+ error("usage: sleds verify <jobId> (or paste the SLEDS-Attest line)");
4173
+ return 2;
4174
+ }
4175
+ const client = o.client ?? (() => {
4176
+ const src = meterSource(o.home);
4177
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
4178
+ })();
4179
+ if (!client) {
4180
+ error("No seat on this machine. Run `sleds init` first.");
4181
+ return 1;
4182
+ }
4183
+ const a = await client.attestation(jobId);
4184
+ if (!a) {
4185
+ error(`No attestation for ${jobId} (not delivered, not signed, not your org, or the proxy is down).`);
4186
+ return 1;
4187
+ }
4188
+ if (!a.publicKey) {
4189
+ error(`Attestation found but its key ${a.attestation.keyId} is not on file — cannot verify.`);
4190
+ return 1;
4191
+ }
4192
+ const ok = verifyAttestation(a.attestation.payload, a.attestation.sig, a.publicKey);
4193
+ const f = parsePayload(a.attestation.payload);
4194
+ log(`${ok ? "VERIFIED" : "INVALID "} job ${jobId}`);
4195
+ log(` model ${f.model ?? a.model ?? "unknown"}`);
4196
+ log(` engine ${f.engine ?? a.engine}${f.seat ? ` · seat ${f.seat}` : ""}`);
4197
+ log(` finished ${f.finished ?? "?"} · signed ${formatLocal(a.attestation.signedAt)} · key ${a.attestation.keyId}`);
4198
+ if (a.outputs.length)
4199
+ log(` outputs ${a.outputs.join(" ")}`);
4200
+ log(` ${a.attestation.trailer}`);
4201
+ if (!ok)
4202
+ log(" The signature does not match the payload under the org's public key.");
4203
+ return ok ? 0 : 1;
4204
+ };
4205
+
3408
4206
  // src/schedule.ts
3409
- import { spawnSync } from "node:child_process";
4207
+ import { spawnSync as spawnSync4 } from "node:child_process";
3410
4208
  var defaultExec = (cmd, args) => {
3411
- const r = spawnSync(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
4209
+ const r = spawnSync4(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
3412
4210
  if (r.error)
3413
4211
  return { ok: false, stderr: r.error.message };
3414
4212
  return { ok: r.status === 0, stderr: r.stderr ?? "" };
@@ -3434,14 +4232,21 @@ commands:
3434
4232
  --until-reset <when> revert at this time: +5h, +90m, or an ISO timestamp
3435
4233
  --reason "<text>" note shown by \`sleds status\`
3436
4234
  --no-schedule record the deadline but do not install a launchd/systemd revert
4235
+ defer Hit a limit? Resume this session on this Mac at the reset \u2014 runs itself on your
4236
+ seat, no key needed (the musher). Uses the last session the status line saw.
4237
+ --at +2h | <ISO> start then instead of the binding reset
4238
+ --session <id> --cwd <dir> --model <id> override what was remembered
4239
+ musher The agent that runs deferred jobs here: --install / --uninstall / --status,
4240
+ --claude <path>, --mode <permission-mode> (default acceptEdits), --timeout <min>.
4241
+ run <jobId> Run one deferred job on this Mac now (the manual musher).
4242
+ verify <jobId | SLEDS-Attest line> Check a finished job's attestation: which model built it,
4243
+ signed by the org's key, verified locally.
3437
4244
  off Switch Claude Code back to your seat. Restores whatever \`on\` replaced.
3438
4245
  uninstall Remove the proxy settings, skill and status line. Keeps ~/.sleds-token.
3439
4246
  status Show the switch state, deadline, proxy health and wall mode.
3440
4247
  doctor Check the whole install: which seat this shell is, Claude settings, proxy, hooks,
3441
- Node version, stale links, the SwiftBar plugin, the seat-sync timer.
4248
+ Node version, stale links, the seat's last quota report.
3442
4249
  meter Show the pipeline: built today, queued with its band, delivered overnight.
3443
- bar Print the menu-bar meter in SwiftBar format; --install links it into SwiftBar
3444
- (--home/--claude-config pick the seat it tracks; --name <x> adds a second item).
3445
4250
  mcp Run the MCP server on stdio: tools usage, estimate, overnight, jobs.
3446
4251
  Register with \`claude mcp add sleds -- sleds mcp\` (Cowork sees the same server).
3447
4252
  seat-sync Read the seat's subscription usage (weekly/5h limits, per-model, credits) from a
@@ -3492,17 +4297,24 @@ var stringFlag = (flags, name) => {
3492
4297
  const v = flags[name];
3493
4298
  return typeof v === "string" ? v : undefined;
3494
4299
  };
4300
+ var hookStdin = async () => {
4301
+ if (process.stdin.isTTY)
4302
+ return;
4303
+ const { readStdin: readStdin3 } = await Promise.resolve().then(() => (init_seat(), exports_seat));
4304
+ const text = await readStdin3(300);
4305
+ return text.trim() ? text : undefined;
4306
+ };
3495
4307
  var sledsArgv = () => {
3496
4308
  const script = process.argv[1];
3497
4309
  return script ? [process.execPath, script] : [process.execPath];
3498
4310
  };
3499
- var shellQuote2 = (s) => /^[\w./-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
3500
- var statusLineCommand = (argv) => [...argv, "statusline"].map(shellQuote2).join(" ");
3501
- var seatEventCommand = (argv) => [...argv, "seat-event"].map(shellQuote2).join(" ");
3502
- var specEventCommand = (argv) => [...argv, "spec-event"].map(shellQuote2).join(" ");
4311
+ var shellQuote = (s) => /^[\w./-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
4312
+ var statusLineCommand = (argv) => [...argv, "statusline"].map(shellQuote).join(" ");
4313
+ var seatEventCommand = (argv) => [...argv, "seat-event"].map(shellQuote).join(" ");
4314
+ var specEventCommand = (argv) => [...argv, "spec-event"].map(shellQuote).join(" ");
3503
4315
  var main = async (argv) => {
3504
4316
  const { cmd, flags } = parseArgs(argv);
3505
- const home = homedir3();
4317
+ const home = homedir();
3506
4318
  const scheduler = {
3507
4319
  platform: platform2(),
3508
4320
  home,
@@ -3522,6 +4334,19 @@ var main = async (argv) => {
3522
4334
  noProbe: flags["no-probe"] === true,
3523
4335
  proxyAlways: flags["proxy-always"] === true,
3524
4336
  seatFirst: flags["seat-first"] === true,
4337
+ installMusher: () => {
4338
+ if (platform2() !== "darwin")
4339
+ return null;
4340
+ const lines = [];
4341
+ const code = musherInstall({
4342
+ home,
4343
+ argv: [],
4344
+ sledsArgv: sledsArgv(),
4345
+ log: (l) => lines.push(l),
4346
+ error: (l) => lines.push(l)
4347
+ });
4348
+ return code === 0 ? lines[0] ?? "installed the musher agent" : `musher not installed: ${lines.join(" ")}`;
4349
+ },
3525
4350
  prompt: promptStdin,
3526
4351
  statusLineCommand: statusLineCommand(sledsArgv()),
3527
4352
  seatEventCommand: seatEventCommand(sledsArgv()),
@@ -3548,16 +4373,22 @@ var main = async (argv) => {
3548
4373
  });
3549
4374
  case "off":
3550
4375
  return off({ home, scheduler });
4376
+ case "defer":
4377
+ return defer({ home, argv: process.argv.slice(3), stdin: await hookStdin() });
4378
+ case "musher":
4379
+ return musher({ home, argv: process.argv.slice(3), sledsArgv: sledsArgv() });
4380
+ case "run":
4381
+ return run({ home, jobId: process.argv[3] });
4382
+ case "verify":
4383
+ return verify({ home, target: process.argv.slice(3).join(" ") || undefined });
3551
4384
  case "uninstall":
3552
- return uninstall3({ home });
4385
+ return uninstall2({ home });
3553
4386
  case "doctor":
3554
4387
  return doctor({ home });
3555
4388
  case "status":
3556
4389
  return status({ home });
3557
4390
  case "meter":
3558
4391
  return meter({ home });
3559
- case "bar":
3560
- return bar({ home, argv: process.argv.slice(3) });
3561
4392
  case "mcp":
3562
4393
  return mcp({ home });
3563
4394
  case "seat-sync":
@@ -3572,7 +4403,7 @@ var main = async (argv) => {
3572
4403
  case "version":
3573
4404
  case "--version":
3574
4405
  case "-v":
3575
- console.log(`sleds ${"0.1.11"}`);
4406
+ console.log("sleds 0.1.14");
3576
4407
  return 0;
3577
4408
  default:
3578
4409
  console.error(`unknown command: ${cmd}