@sleds/cli 0.1.11 → 0.1.15

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 +1423 -562
  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) => withSeatEnv(`sleds ${sub}`, home, env), withSeatEnv = (command, 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();
@@ -49,10 +32,9 @@ var sledsCommand = (sub, home, env = process.env) => {
49
32
  parts.push(`SLEDS_HOME=${shellWord(resolve(home, sh))}`);
50
33
  if (cc)
51
34
  parts.push(`CLAUDE_CONFIG_DIR=${shellWord(resolve(home, cc))}`);
52
- parts.push(`sleds ${sub}`);
35
+ parts.push(command);
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,556 @@ 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 join11 } 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) => join11(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;
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;
599
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((resolve5) => {
754
+ if (stdin.isTTY) {
755
+ resolve5("");
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
+ resolve5(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 { basename, 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 musherLabel = (env = process.env) => {
803
+ const sh = env.SLEDS_HOME?.trim();
804
+ if (!sh)
805
+ return MUSHER_LABEL;
806
+ const tag = basename(sh).replace(/^\.+/, "").replace(/[^A-Za-z0-9_-]+/g, "-") || "seat";
807
+ return `${MUSHER_LABEL}.${tag}`;
600
808
  };
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))}`);
809
+ var musherPlistPath = (home, env = process.env) => join4(home, "Library", "LaunchAgents", `${musherLabel(env)}.plist`);
810
+ var musherLogPath = (configDir2) => join4(configDir2, "musher.log");
811
+ var xmlEscape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
812
+ var musherPath = (home, execPath, currentPath = "") => {
813
+ const parts = [
814
+ dirname2(execPath),
815
+ join4(home, ".sleds", "bin"),
816
+ join4(home, ".local", "bin"),
817
+ join4(home, ".bun", "bin"),
818
+ "/opt/homebrew/bin",
819
+ "/usr/local/bin",
820
+ "/usr/bin",
821
+ "/bin",
822
+ ...currentPath.split(":").filter(Boolean)
823
+ ];
824
+ return [...new Set(parts)].join(":");
825
+ };
826
+ var buildMusherPlist = (o) => {
827
+ const label = o.label ?? MUSHER_LABEL;
828
+ const args = [...o.sledsArgv, "musher", "tick"].map((a) => ` <string>${xmlEscape(a)}</string>`).join(`
829
+ `);
830
+ const env = Object.entries(o.env).filter(([, v]) => v !== "").map(([k, v]) => ` <key>${xmlEscape(k)}</key>
831
+ <string>${xmlEscape(v)}</string>`).join(`
832
+ `);
833
+ return `<?xml version="1.0" encoding="UTF-8"?>
834
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
835
+ <plist version="1.0">
836
+ <dict>
837
+ <key>Label</key>
838
+ <string>${label}</string>
839
+ <key>ProgramArguments</key>
840
+ <array>
841
+ ${args}
842
+ </array>
843
+ <key>EnvironmentVariables</key>
844
+ <dict>
845
+ ${env}
846
+ </dict>
847
+ <key>StartInterval</key>
848
+ <integer>${MUSHER_INTERVAL_S}</integer>
849
+ <key>RunAtLoad</key>
850
+ <true/>
851
+ <key>StandardOutPath</key>
852
+ <string>${xmlEscape(o.logPath)}</string>
853
+ <key>StandardErrorPath</key>
854
+ <string>${xmlEscape(o.logPath)}</string>
855
+ </dict>
856
+ </plist>
857
+ `;
858
+ };
859
+ var RESUME_PROMPT = [
860
+ "SLEDS musher: the usage limit that stopped this session has reset, and the seat's owner",
861
+ "scheduled this unattended run to continue it. Pick up exactly where the session left off:",
862
+ "finish the task that was in flight, run the checks you would normally run, follow the repo's",
863
+ "own conventions for branches and commits, and do not start unrelated work. If you are blocked",
864
+ "on a decision only the owner can make, stop there and say so. Finish with a summary under",
865
+ "200 words: what was done, what remains, anything the owner must decide."
866
+ ].join(" ");
867
+ var NO_SESSION_PROMPT = [
868
+ "SLEDS musher: a deferred job started on this seat, but no Claude Code session was recorded",
869
+ "to resume. Do not guess at work. Reply with one line saying so, and stop."
870
+ ].join(" ");
871
+ var claudeArgs = (o) => [
872
+ "-p",
873
+ o.sessionId ? RESUME_PROMPT : NO_SESSION_PROMPT,
874
+ ...o.sessionId ? ["--resume", o.sessionId] : [],
875
+ ...o.model ? ["--model", o.model] : [],
876
+ "--output-format",
877
+ "json",
878
+ "--permission-mode",
879
+ o.permissionMode
880
+ ];
881
+ var parseClaudeOutput = (stdout, exitCode) => {
882
+ const lines = stdout.split(`
883
+ `).map((l) => l.trim()).filter(Boolean);
884
+ for (let i = lines.length - 1;i >= 0; i--) {
885
+ const line = lines[i] ?? "";
886
+ if (!line.startsWith("{"))
887
+ continue;
888
+ try {
889
+ const j = JSON.parse(line);
890
+ if (j.type !== "result" && typeof j.result !== "string")
891
+ continue;
892
+ const isError = j.is_error === true || typeof j.subtype === "string" && j.subtype !== "success";
893
+ const text = typeof j.result === "string" ? j.result : "";
894
+ const models = j.modelUsage;
895
+ const model = typeof j.model === "string" ? j.model : models && typeof models === "object" ? Object.keys(models)[0] : undefined;
896
+ return {
897
+ ok: !isError && exitCode === 0,
898
+ summary: (text || (isError ? "Claude Code reported an error" : "")).slice(0, SUMMARY_MAX),
899
+ ...typeof j.session_id === "string" ? { sessionId: j.session_id } : {},
900
+ ...model ? { model } : {},
901
+ ...typeof j.duration_ms === "number" ? { durationMs: j.duration_ms } : {},
902
+ ...typeof j.total_cost_usd === "number" ? { costUsd: j.total_cost_usd } : {}
903
+ };
904
+ } catch {}
905
+ }
906
+ const tail = lines.slice(-5).join(" ").slice(0, SUMMARY_MAX);
907
+ return { ok: false, summary: tail || `claude exited ${exitCode ?? "without a code"}` };
908
+ };
909
+ var defaultExecCapture = (cmd, args, o) => {
910
+ const r = spawnSync(cmd, [...args], {
911
+ cwd: o.cwd,
912
+ encoding: "utf8",
913
+ timeout: o.timeoutMs,
914
+ maxBuffer: 64 * 1024 * 1024,
915
+ stdio: ["ignore", "pipe", "pipe"],
916
+ env: { ...process.env, SLEDS_MUSHER: "1" }
917
+ });
918
+ return {
919
+ stdout: r.stdout ?? "",
920
+ stderr: r.stderr ?? "",
921
+ status: r.status,
922
+ ...r.error ? { error: r.error.message } : {}
923
+ };
924
+ };
925
+ var musherClient = (o) => {
926
+ const fetchFn = o.fetchFn ?? fetch;
927
+ const headers = { authorization: `Bearer ${o.token}`, "content-type": "application/json" };
928
+ const call = async (path, body) => {
929
+ try {
930
+ const res = await fetchFn(`${o.proxyUrl}${path}`, {
931
+ method: body === undefined ? "GET" : "POST",
932
+ headers,
933
+ ...body === undefined ? {} : { body: JSON.stringify(body) },
934
+ signal: AbortSignal.timeout(o.timeoutMs ?? 1e4)
935
+ });
936
+ if (!res.ok)
937
+ return null;
938
+ return await res.json();
939
+ } catch {
940
+ return null;
628
941
  }
629
- if (seat?.usedCreditsUsd !== undefined)
630
- lines.push(`${"credits".padEnd(12)}${usd(seat.usedCreditsUsd)} spent | ${MONO}`);
942
+ };
943
+ return {
944
+ list: () => call("/proxy/musher"),
945
+ start: (jobId, local) => call("/proxy/musher/start", { jobId, ...local }),
946
+ finish: async (input) => await call("/proxy/musher/finish", input) !== null,
947
+ defer: (input) => call("/proxy/defer", input),
948
+ attestation: (jobId) => call(`/proxy/attest/${encodeURIComponent(jobId)}`)
949
+ };
950
+ };
951
+ var gitHead = (cwd, exec) => {
952
+ if (!cwd)
953
+ return null;
954
+ const r = exec("git", ["rev-parse", "HEAD"], { cwd, timeoutMs: 5000 });
955
+ const sha = r.stdout.trim();
956
+ return r.status === 0 && /^[0-9a-f]{40}$/.test(sha) ? sha : null;
957
+ };
958
+ var gitNewCommits = (cwd, before, after, exec) => {
959
+ if (!cwd || !after || after === before)
960
+ return [];
961
+ if (!before)
962
+ return [after];
963
+ const r = exec("git", ["rev-list", `${before}..${after}`], { cwd, timeoutMs: 5000 });
964
+ if (r.status !== 0)
965
+ return [after];
966
+ return r.stdout.split(`
967
+ `).map((l) => l.trim()).filter((l) => /^[0-9a-f]{40}$/.test(l)).slice(0, 40);
968
+ };
969
+ var collectOutputs = (commits, summary) => {
970
+ const out = commits.map((c) => `git:${c}`);
971
+ for (const m of summary.matchAll(/https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/pull\/\d+/g))
972
+ if (!out.includes(`pr:${m[0]}`))
973
+ out.push(`pr:${m[0]}`);
974
+ return out.slice(0, 50);
975
+ };
976
+ var runJob = async (o) => {
977
+ const log = o.log ?? (() => {});
978
+ const sessionId = o.job.claudeSessionId ?? o.fallbackSession?.id ?? null;
979
+ const cwd = o.job.cwd ?? o.fallbackSession?.cwd ?? undefined;
980
+ const started = await o.client.start(o.job.jobId, {
981
+ ...sessionId ? { claudeSessionId: sessionId } : {},
982
+ ...cwd ? { cwd } : {},
983
+ host: o.host
984
+ });
985
+ if (!started)
986
+ return { kind: "skipped", reason: "control plane unreachable" };
987
+ if (!started.ok)
988
+ return { kind: "skipped", reason: started.reason };
989
+ const model = o.job.model ?? started.model ?? o.fallbackSession?.model ?? null;
990
+ const args = claudeArgs({ sessionId, model, permissionMode: o.permissionMode });
991
+ log(`musher: running job ${o.job.jobId} (${sessionId ? `resume ${sessionId}` : "no session"}${model ? ` · ${model}` : ""}${cwd ? ` · ${cwd}` : ""})`);
992
+ const exec = o.exec ?? defaultExecCapture;
993
+ const headBefore = gitHead(cwd, exec);
994
+ const t0 = Date.now();
995
+ const r = exec(o.claudePath, args, { cwd, timeoutMs: o.timeoutMs });
996
+ const durationMs = Date.now() - t0;
997
+ const result = r.error ? { ok: false, summary: `could not run ${o.claudePath}: ${r.error}` } : parseClaudeOutput(r.stdout, r.status);
998
+ if (!r.error && r.status === null)
999
+ result.summary = `timed out after ${Math.round(o.timeoutMs / 60000)} min. ${result.summary}`.trim();
1000
+ const outputs = collectOutputs(gitNewCommits(cwd, headBefore, gitHead(cwd, exec), exec), result.summary);
1001
+ await o.client.finish({
1002
+ jobId: o.job.jobId,
1003
+ runId: started.runId,
1004
+ ok: result.ok,
1005
+ summary: result.summary,
1006
+ ...outputs.length ? { outputs } : {},
1007
+ ...result.model ?? model ? { model: result.model ?? model } : {},
1008
+ durationMs: result.durationMs ?? durationMs,
1009
+ ...result.sessionId ?? sessionId ? { claudeSessionId: result.sessionId ?? sessionId } : {}
1010
+ });
1011
+ log(`musher: job ${o.job.jobId} ${result.ok ? "delivered" : "failed"} in ${Math.round(durationMs / 1000)}s`);
1012
+ return { kind: "ran", ok: result.ok, summary: result.summary, durationMs, outputs };
1013
+ };
1014
+ var verifyAttestation = (payload, sig, publicKeyB64) => {
1015
+ try {
1016
+ const pub = createPublicKey({
1017
+ key: Buffer.from(publicKeyB64, "base64"),
1018
+ format: "der",
1019
+ type: "spki"
1020
+ });
1021
+ const raw = Buffer.from(sig.replace(/-/g, "+").replace(/_/g, "/"), "base64");
1022
+ return cryptoVerify(null, Buffer.from(payload, "utf8"), pub, raw);
1023
+ } catch {
1024
+ return false;
631
1025
  }
632
- lines.push("---");
633
- lines.push(`Open dashboard | href=${DASHBOARD_URL}`);
634
- lines.push("Refresh | refresh=true");
635
- return lines;
636
1026
  };
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, `'\\''`)}'`;
1027
+ var parsePayload = (payload) => {
1028
+ const out = {};
1029
+ for (const line of payload.split(`
1030
+ `).slice(1)) {
1031
+ const eq = line.indexOf("=");
1032
+ if (eq > 0)
1033
+ out[line.slice(0, eq)] = line.slice(eq + 1);
1034
+ }
1035
+ return out;
1036
+ };
658
1037
 
659
1038
  // src/seat.ts
1039
+ init_config();
1040
+ init_meter();
660
1041
  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";
1042
+ import { join as join5 } from "node:path";
662
1043
  var SEAT_POST_MIN_DELTA_PCT = 1;
663
1044
  var SEAT_POST_MIN_INTERVAL_MS = 5 * 60000;
664
1045
  var SEAT_POST_TIMEOUT_MS = 1500;
@@ -743,7 +1124,7 @@ var formatSeatFragment = (w) => {
743
1124
  return null;
744
1125
  return `seat ${parts.join(SEP)}`;
745
1126
  };
746
- var seatStatePath = (home) => join4(configDir(home), "seat.json");
1127
+ var seatStatePath = (home) => join5(configDir(home), "seat.json");
747
1128
  var parseWindowStored = (v) => {
748
1129
  if (!isObject4(v))
749
1130
  return;
@@ -778,8 +1159,56 @@ var parseSeatState = (raw) => {
778
1159
  const lastWallResetsAt = num2(raw.lastWallResetsAt);
779
1160
  if (lastWallResetsAt !== undefined)
780
1161
  out.lastWallResetsAt = lastWallResetsAt;
1162
+ const session = parseSeatSession(raw.session);
1163
+ if (session)
1164
+ out.session = session;
781
1165
  return out;
782
1166
  };
1167
+ var parseSeatSession = (v) => {
1168
+ if (!isObject4(v) || typeof v.id !== "string" || !v.id)
1169
+ return;
1170
+ const at = num2(v.at);
1171
+ if (at === undefined)
1172
+ return;
1173
+ const out = { id: v.id, at };
1174
+ if (typeof v.cwd === "string" && v.cwd)
1175
+ out.cwd = v.cwd;
1176
+ if (typeof v.model === "string" && v.model)
1177
+ out.model = v.model;
1178
+ return out;
1179
+ };
1180
+ var seatSessionFromStdin = (text, now) => {
1181
+ const trimmed = text.trim();
1182
+ if (!trimmed)
1183
+ return null;
1184
+ try {
1185
+ const raw = JSON.parse(trimmed);
1186
+ if (!isObject4(raw) || typeof raw.session_id !== "string" || !raw.session_id)
1187
+ return null;
1188
+ const out = { id: raw.session_id, at: now };
1189
+ if (typeof raw.cwd === "string" && raw.cwd)
1190
+ out.cwd = raw.cwd;
1191
+ const model = isObject4(raw.model) ? raw.model.id : raw.model;
1192
+ if (typeof model === "string" && model)
1193
+ out.model = model;
1194
+ return out;
1195
+ } catch {
1196
+ return null;
1197
+ }
1198
+ };
1199
+ var bindingResetAt = (w, now) => {
1200
+ if (!w)
1201
+ return;
1202
+ const bars = [
1203
+ ...w.fiveHour ? [w.fiveHour] : [],
1204
+ ...w.sevenDay ? [w.sevenDay] : [],
1205
+ ...w.spendLimit ? [w.spendLimit] : [],
1206
+ ...Object.values(w.models ?? {})
1207
+ ].filter((b) => b.resetsAt > now);
1208
+ const full = bars.filter((b) => b.usedPct >= 100);
1209
+ const pick = (full.length ? full : bars).map((b) => b.resetsAt);
1210
+ return pick.length ? Math.min(...pick) : undefined;
1211
+ };
783
1212
  var readSeatState = (home) => {
784
1213
  const p = seatStatePath(home);
785
1214
  if (!existsSync4(p))
@@ -837,187 +1266,55 @@ var seatWallDue = (state, w) => {
837
1266
  };
838
1267
  var seatWindowBody = (w) => ({
839
1268
  capturedAt: w.capturedAt,
840
- ...w.fiveHour ? { fiveHour: w.fiveHour } : {},
841
- ...w.sevenDay ? { sevenDay: w.sevenDay } : {},
842
- ...w.spendLimit ? { spendLimit: w.spendLimit } : {}
843
- });
844
- var postJson = async (o, path, body) => {
845
- const fetchFn = o.fetchFn ?? fetch;
846
- try {
847
- const res = await fetchFn(`${o.proxyUrl}${path}`, {
848
- method: "POST",
849
- headers: { authorization: `Bearer ${o.token}`, "content-type": "application/json" },
850
- body: JSON.stringify(body),
851
- signal: AbortSignal.timeout(o.timeoutMs ?? SEAT_POST_TIMEOUT_MS)
852
- });
853
- return res.ok;
854
- } catch {
855
- return false;
856
- }
857
- };
858
- var postSeatWindow = (o, w) => postJson(o, "/proxy/seat", seatWindowBody(w));
859
- 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.";
862
- var seatWallLine = (hasUpstreamKey) => hasUpstreamKey ? SEAT_WALL_LINE : SEAT_WALL_LINE_NO_KEY;
863
- var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve3) => {
864
- if (stdin.isTTY) {
865
- resolve3("");
866
- return;
867
- }
868
- let data = "";
869
- let settled = false;
870
- const finish = () => {
871
- if (settled)
872
- return;
873
- settled = true;
874
- clearTimeout(timer);
875
- resolve3(data);
876
- };
877
- const timer = setTimeout(finish, timeoutMs);
878
- stdin.setEncoding("utf8");
879
- stdin.on("data", (chunk) => {
880
- data += chunk;
881
- });
882
- stdin.on("end", finish);
883
- stdin.on("error", finish);
884
- stdin.resume();
885
- });
886
-
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) => {
1269
+ ...w.fiveHour ? { fiveHour: w.fiveHour } : {},
1270
+ ...w.sevenDay ? { sevenDay: w.sevenDay } : {},
1271
+ ...w.spendLimit ? { spendLimit: w.spendLimit } : {}
1272
+ });
1273
+ var postJson = async (o, path, body) => {
1274
+ const fetchFn = o.fetchFn ?? fetch;
989
1275
  try {
990
- return lstatSync(p).isSymbolicLink();
1276
+ const res = await fetchFn(`${o.proxyUrl}${path}`, {
1277
+ method: "POST",
1278
+ headers: { authorization: `Bearer ${o.token}`, "content-type": "application/json" },
1279
+ body: JSON.stringify(body),
1280
+ signal: AbortSignal.timeout(o.timeoutMs ?? SEAT_POST_TIMEOUT_MS)
1281
+ });
1282
+ return res.ok;
991
1283
  } catch {
992
1284
  return false;
993
1285
  }
994
1286
  };
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;
1287
+ var postSeatWindow = (o, w) => postJson(o, "/proxy/seat", seatWindowBody(w));
1288
+ var postSeatWall = (o, input) => postJson(o, "/proxy/seat-wall", input);
1289
+ 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.";
1290
+ 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.";
1291
+ var seatWallLine = (hasUpstreamKey) => hasUpstreamKey ? SEAT_WALL_LINE : SEAT_WALL_LINE_NO_KEY;
1292
+ var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve3) => {
1293
+ if (stdin.isTTY) {
1294
+ resolve3("");
1295
+ return;
1016
1296
  }
1017
- }
1018
- var pluginPath2 = (home, name) => join6(configDir(home, {}), "swiftbar", pluginName2(name));
1297
+ let data = "";
1298
+ let settled = false;
1299
+ const finish = () => {
1300
+ if (settled)
1301
+ return;
1302
+ settled = true;
1303
+ clearTimeout(timer);
1304
+ resolve3(data);
1305
+ };
1306
+ const timer = setTimeout(finish, timeoutMs);
1307
+ stdin.setEncoding("utf8");
1308
+ stdin.on("data", (chunk) => {
1309
+ data += chunk;
1310
+ });
1311
+ stdin.on("end", finish);
1312
+ stdin.on("error", finish);
1313
+ stdin.resume();
1314
+ });
1019
1315
 
1020
1316
  // src/switch.ts
1317
+ init_settings();
1021
1318
  var isObject5 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
1022
1319
  var envOf2 = (settings) => isObject5(settings.env) ? settings.env : {};
1023
1320
  var capturePriorEnv = (settings) => {
@@ -1092,7 +1389,141 @@ var formatDuration = (ms) => {
1092
1389
  return m === 0 ? `${h}h` : `${h}h ${m}m`;
1093
1390
  };
1094
1391
 
1392
+ // src/commands/defer.ts
1393
+ var parseAt = (v, now) => {
1394
+ const rel = /^\+(\d+)([hm])$/.exec(v.trim());
1395
+ if (rel) {
1396
+ const n = Number.parseInt(rel[1] ?? "0", 10);
1397
+ return now + n * (rel[2] === "h" ? 3600000 : 60000);
1398
+ }
1399
+ const t = Date.parse(v);
1400
+ return Number.isFinite(t) ? t : undefined;
1401
+ };
1402
+ var parseFlags = (argv) => {
1403
+ const flags = {};
1404
+ for (let i = 0;i < argv.length; i++) {
1405
+ const a = argv[i] ?? "";
1406
+ if (!a.startsWith("--"))
1407
+ continue;
1408
+ const eq = a.indexOf("=");
1409
+ if (eq > 0)
1410
+ flags[a.slice(2, eq)] = a.slice(eq + 1);
1411
+ else {
1412
+ const next = argv[i + 1];
1413
+ if (next !== undefined && !next.startsWith("--")) {
1414
+ flags[a.slice(2)] = next;
1415
+ i++;
1416
+ } else
1417
+ flags[a.slice(2)] = "true";
1418
+ }
1419
+ }
1420
+ return flags;
1421
+ };
1422
+ var defer = async (o) => {
1423
+ const log = o.log ?? console.log;
1424
+ const error = o.error ?? console.error;
1425
+ const now = (o.now ?? Date.now)();
1426
+ const flags = parseFlags(o.argv);
1427
+ const client = o.client ?? (() => {
1428
+ const src = meterSource(o.home);
1429
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
1430
+ })();
1431
+ if (!client) {
1432
+ error("No seat on this machine. Run `sleds init` first.");
1433
+ return 1;
1434
+ }
1435
+ const state = readSeatState(o.home);
1436
+ const piped = o.stdin ? seatSessionFromStdin(o.stdin, now) : null;
1437
+ const remembered = piped ?? state.session;
1438
+ const sessionId = flags.session ?? remembered?.id;
1439
+ const cwd = flags.cwd ?? remembered?.cwd;
1440
+ const model = flags.model ?? remembered?.model;
1441
+ let resetAt;
1442
+ if (flags.at) {
1443
+ resetAt = parseAt(flags.at, now);
1444
+ if (resetAt === undefined) {
1445
+ error(`--at takes +2h, +90m or an ISO timestamp (got ${flags.at})`);
1446
+ return 1;
1447
+ }
1448
+ } else
1449
+ resetAt = bindingResetAt(state.window, now);
1450
+ if (!sessionId)
1451
+ log("No Claude Code session remembered on this seat yet; the job will say so when it starts. Pass --session <id> to pick one.");
1452
+ const r = await client.defer({
1453
+ ...resetAt !== undefined ? { resetAt } : {},
1454
+ ...model ? { model } : {},
1455
+ ...sessionId ? { claudeSessionId: sessionId } : {},
1456
+ ...cwd ? { cwd } : {},
1457
+ host: o.host ?? hostname()
1458
+ });
1459
+ if (!r) {
1460
+ error("Could not reach the SLEDS proxy; nothing was scheduled.");
1461
+ return 1;
1462
+ }
1463
+ const when = r.scheduledAt <= now + 60000 ? "as soon as the musher ticks (within 5 min)" : `${formatLocal(r.scheduledAt)} (in ${formatDuration(r.scheduledAt - now)})`;
1464
+ log(`Deferred. This Mac resumes ${sessionId ? `session ${sessionId.slice(0, 8)}…` : "the work"}${model ? ` on ${model}` : ""} ${when}.`);
1465
+ log(` job ${r.jobId}${cwd ? ` · ${cwd}` : ""} · runs on your seat, no key · sleds musher --status to watch`);
1466
+ const cfg = readConfig(o.home);
1467
+ if (process.platform === "darwin" && !existsSync5(musherPlistPath(o.home)) && cfg.musher?.agent !== "launchd")
1468
+ 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.`);
1469
+ log(" Keep this Mac awake at that time (System Settings → Battery → prevent sleeping on power adapter).");
1470
+ return 0;
1471
+ };
1472
+
1473
+ // src/commands/doctor.ts
1474
+ init_config();
1475
+ init_meter();
1476
+ import { execFileSync } from "node:child_process";
1477
+ import { existsSync as existsSync7, lstatSync, readlinkSync } from "node:fs";
1478
+ import { resolve as resolve3 } from "node:path";
1479
+ init_settings();
1480
+
1481
+ // src/commands/musher.ts
1482
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync5 } from "node:fs";
1483
+ init_config();
1484
+ init_meter();
1485
+ var defaultWhich = (cmd) => {
1486
+ const path = process.env.PATH ?? "";
1487
+ for (const dir of path.split(":").filter(Boolean)) {
1488
+ const p = `${dir}/${cmd}`;
1489
+ if (existsSync6(p))
1490
+ return p;
1491
+ }
1492
+ return null;
1493
+ };
1494
+ var findClaude = (home, configured, which) => {
1495
+ if (configured && existsSync6(configured))
1496
+ return configured;
1497
+ const found = which("claude");
1498
+ if (found)
1499
+ return found;
1500
+ for (const p of [
1501
+ `${home}/.local/bin/claude`,
1502
+ "/opt/homebrew/bin/claude",
1503
+ "/usr/local/bin/claude",
1504
+ `${home}/.npm-global/bin/claude`
1505
+ ])
1506
+ if (existsSync6(p))
1507
+ return p;
1508
+ return null;
1509
+ };
1510
+ var musherAgentState = (home, env = process.env) => {
1511
+ const plist = musherPlistPath(home, env);
1512
+ if (!existsSync6(plist))
1513
+ return { installed: false, thisSeat: false };
1514
+ try {
1515
+ const text = readFileSync5(plist, "utf8");
1516
+ const want = env.SLEDS_HOME?.trim();
1517
+ const has = /<key>SLEDS_HOME<\/key>\s*<string>([^<]*)<\/string>/.exec(text)?.[1];
1518
+ return { installed: true, thisSeat: (want ?? undefined) === (has ?? undefined) };
1519
+ } catch {
1520
+ return { installed: true, thisSeat: false };
1521
+ }
1522
+ };
1523
+
1095
1524
  // src/commands/status.ts
1525
+ init_config();
1526
+ init_settings();
1096
1527
  var checkHealth = async (url, fetchFn = fetch, timeoutMs = 1000) => {
1097
1528
  try {
1098
1529
  const res = await fetchFn(`${url}/healthz`, { signal: AbortSignal.timeout(timeoutMs) });
@@ -1110,16 +1541,6 @@ function nodeOk(version, min = NODE_MIN) {
1110
1541
  const [x, y, z] = parse(min);
1111
1542
  return (a ?? 0) !== (x ?? 0) ? (a ?? 0) > (x ?? 0) : (b ?? 0) !== (y ?? 0) ? (b ?? 0) > (y ?? 0) : (c ?? 0) >= (z ?? 0);
1112
1543
  }
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
1544
  var MARK = { ok: "ok ", warn: "warn", fail: "FAIL" };
1124
1545
  function formatDoctor(checks) {
1125
1546
  const lines = checks.map((c) => `${MARK[c.level]} ${c.name.padEnd(16)}${c.detail}`);
@@ -1135,9 +1556,9 @@ function formatDoctor(checks) {
1135
1556
  lines.push(fails === 0 && warns === 0 ? "All good." : `${fails} problem${fails === 1 ? "" : "s"}, ${warns} warning${warns === 1 ? "" : "s"}.`);
1136
1557
  return lines;
1137
1558
  }
1138
- function defaultWhich(cmd) {
1559
+ function defaultWhich2(cmd) {
1139
1560
  try {
1140
- return execFileSync3("which", ["-a", cmd], {
1561
+ return execFileSync("which", ["-a", cmd], {
1141
1562
  encoding: "utf8",
1142
1563
  stdio: ["ignore", "pipe", "ignore"]
1143
1564
  }).split(`
@@ -1146,16 +1567,6 @@ function defaultWhich(cmd) {
1146
1567
  return [];
1147
1568
  }
1148
1569
  }
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
1570
  async function runDoctor(o) {
1160
1571
  const env = o.env ?? process.env;
1161
1572
  const checks = [];
@@ -1166,7 +1577,7 @@ async function runDoctor(o) {
1166
1577
  ].filter(Boolean);
1167
1578
  const cfgDir = configDir(home, env);
1168
1579
  const cfg = readConfig(home);
1169
- const cfgExists = existsSync6(configPath(home));
1580
+ const cfgExists = existsSync7(configPath(home));
1170
1581
  if (!cfgExists) {
1171
1582
  checks.push({
1172
1583
  level: "fail",
@@ -1182,7 +1593,7 @@ async function runDoctor(o) {
1182
1593
  });
1183
1594
  }
1184
1595
  const sPath = settingsPath(home);
1185
- const settings = existsSync6(sPath) ? readSettings(home) : null;
1596
+ const settings = existsSync7(sPath) ? readSettings(home) : null;
1186
1597
  const pointed = settings ? readProxy(settings) : null;
1187
1598
  if (!settings) {
1188
1599
  checks.push({
@@ -1226,7 +1637,7 @@ async function runDoctor(o) {
1226
1637
  detail: `seat-event ${seat2 ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"}`,
1227
1638
  fix: seat2 && spec ? undefined : "sleds hooks"
1228
1639
  });
1229
- const want = env.SLEDS_HOME?.trim() ? resolve5(home, env.SLEDS_HOME.trim()) : undefined;
1640
+ const want = env.SLEDS_HOME?.trim() ? resolve3(home, env.SLEDS_HOME.trim()) : undefined;
1230
1641
  const cmds = sledsCommandsIn(settings);
1231
1642
  const wrong = cmds.filter((c) => {
1232
1643
  const got = bakedHome(c);
@@ -1240,7 +1651,7 @@ async function runDoctor(o) {
1240
1651
  fix: wrong.length === 0 ? undefined : `${overrides.length ? `${overrides.join(" ")} ` : ""}sleds hooks (re-writes the status line + hooks with this seat baked in)`
1241
1652
  });
1242
1653
  }
1243
- const skill = existsSync6(skillPath(home));
1654
+ const skill = existsSync7(skillPath(home));
1244
1655
  checks.push({
1245
1656
  level: skill ? "ok" : "warn",
1246
1657
  name: "skill",
@@ -1255,10 +1666,10 @@ async function runDoctor(o) {
1255
1666
  detail: `${nodeVersion}${nodeOk(nodeVersion) ? "" : ` — seat-sync needs ≥ ${NODE_MIN} (node:sqlite)`}`,
1256
1667
  fix: nodeOk(nodeVersion) ? undefined : `brew install node@22 && echo 'export PATH="/opt/homebrew/opt/node@22/bin:$PATH"' >> ~/.zshrc`
1257
1668
  });
1258
- const which = (o.which ?? defaultWhich)("sleds");
1669
+ const which = (o.which ?? defaultWhich2)("sleds");
1259
1670
  const stale = which.filter((p) => {
1260
1671
  try {
1261
- const target = lstatSync2(p).isSymbolicLink() ? readlinkSync(p) : p;
1672
+ const target = lstatSync(p).isSymbolicLink() ? readlinkSync(p) : p;
1262
1673
  return /\/src\/index\.ts$/.test(target) || /\.bun\/bin\/sleds$/.test(p);
1263
1674
  } catch {
1264
1675
  return false;
@@ -1273,49 +1684,68 @@ async function runDoctor(o) {
1273
1684
  });
1274
1685
  else if (which.length > 0)
1275
1686
  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
1687
  const seat = readSeatState(home);
1300
1688
  const age = seat.window ? Math.round((Date.now() - seat.window.capturedAt) / 60000) : null;
1301
- if (plists.length === 0)
1302
- checks.push({
1689
+ checks.push({
1690
+ level: age === null ? "warn" : "ok",
1691
+ name: "seat quota",
1692
+ 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)}%` : ""}`,
1693
+ fix: age === null ? "open claude, say hi, exit — the status line posts the seat's quota" : undefined
1694
+ });
1695
+ if ((o.platform ?? process.platform) === "darwin") {
1696
+ const agent = musherAgentState(home, env);
1697
+ checks.push(!agent.installed ? {
1303
1698
  level: "warn",
1304
- name: "seat-sync",
1305
- detail: "no timer installed (seat bars stay empty)",
1306
- fix: "sleds seat-sync --install"
1699
+ name: "musher",
1700
+ detail: "agent not installed — `sleds defer` jobs would wait for `sleds run`",
1701
+ fix: "sleds musher --install"
1702
+ } : !agent.thisSeat ? {
1703
+ level: "warn",
1704
+ name: "musher",
1705
+ detail: "agent installed for a different seat (SLEDS_HOME differs)",
1706
+ fix: "sleds musher --install"
1707
+ } : {
1708
+ level: "ok",
1709
+ name: "musher",
1710
+ detail: `launchd agent ${musherLabel(env)}, every 5 min`
1307
1711
  });
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)"
1712
+ const claude = findClaude(home, readConfig(home).musher?.claudePath, (c) => (o.which ?? defaultWhich2)(c)[0] ?? null);
1713
+ checks.push(claude ? { level: "ok", name: "claude cli", detail: claude } : {
1714
+ level: "warn",
1715
+ name: "claude cli",
1716
+ detail: "`claude` not on PATH — the musher cannot run Claude Code",
1717
+ fix: 'sleds musher --claude "$(which claude)"'
1315
1718
  });
1719
+ const sleep = (o.pmset ?? defaultPmset)();
1720
+ if (sleep !== null)
1721
+ checks.push(sleep.acSleepMinutes === 0 ? {
1722
+ level: "ok",
1723
+ name: "awake",
1724
+ detail: "does not sleep on power — deferred jobs can start"
1725
+ } : {
1726
+ level: "warn",
1727
+ name: "awake",
1728
+ detail: `sleeps after ${sleep.acSleepMinutes} min on power — a deferred job needs the Mac awake at the reset`,
1729
+ fix: "System Settings → Battery → Options → Prevent automatic sleeping on power adapter (or: sudo pmset -c sleep 0)"
1730
+ });
1316
1731
  }
1317
1732
  return checks;
1318
1733
  }
1734
+ function parsePmset(text) {
1735
+ const ac = text.split(/AC Power:/i)[1];
1736
+ const block = ac ?? text;
1737
+ const m = /^\s*sleep\s+(\d+)/m.exec(block);
1738
+ if (!m)
1739
+ return null;
1740
+ return { acSleepMinutes: Number.parseInt(m[1] ?? "0", 10) };
1741
+ }
1742
+ var defaultPmset = () => {
1743
+ try {
1744
+ return parsePmset(execFileSync("pmset", ["-g", "custom"], { encoding: "utf8", timeout: 2000 }));
1745
+ } catch {
1746
+ return null;
1747
+ }
1748
+ };
1319
1749
  var doctor = async (o) => {
1320
1750
  const log = o.log ?? console.log;
1321
1751
  const checks = await runDoctor(o);
@@ -1325,6 +1755,7 @@ var doctor = async (o) => {
1325
1755
  };
1326
1756
 
1327
1757
  // src/commands/hooks.ts
1758
+ init_settings();
1328
1759
  var hooks = (o) => {
1329
1760
  const log = o.log ?? console.log;
1330
1761
  const error = o.error ?? console.error;
@@ -1347,6 +1778,8 @@ var hooks = (o) => {
1347
1778
  };
1348
1779
 
1349
1780
  // src/commands/init.ts
1781
+ init_config();
1782
+ init_settings();
1350
1783
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "node:fs";
1351
1784
  import { createInterface } from "node:readline";
1352
1785
 
@@ -1383,6 +1816,8 @@ When the proxy is active it answers a walled request with its options. The user
1383
1816
  /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
1817
  \`\`\`
1385
1818
 
1819
+ 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\`.
1820
+
1386
1821
  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
1822
 
1388
1823
  ## Window reset
@@ -1397,9 +1832,9 @@ then restart \`claude\`. Settings go back to exactly what they were.
1397
1832
 
1398
1833
  ## Check
1399
1834
 
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.
1835
+ \`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
1836
 
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\`.
1837
+ 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
1838
 
1404
1839
  ## /sled — estimate the task, offer overnight
1405
1840
 
@@ -1474,6 +1909,7 @@ From Cowork or any MCP client (\`sleds mcp\`): the \`handoffs\` tool lists what
1474
1909
  `;
1475
1910
 
1476
1911
  // src/commands/hooks.ts
1912
+ init_settings();
1477
1913
  var hooks2 = (o) => {
1478
1914
  const log = o.log ?? console.log;
1479
1915
  const error = o.error ?? console.error;
@@ -1497,11 +1933,11 @@ var hooks2 = (o) => {
1497
1933
 
1498
1934
  // src/commands/init.ts
1499
1935
  var DEFAULT_PROXY_URL = "http://localhost:8787";
1500
- var promptStdin = (question) => new Promise((resolve6) => {
1936
+ var promptStdin = (question) => new Promise((resolve4) => {
1501
1937
  const rl = createInterface({ input: process.stdin, output: process.stderr });
1502
1938
  rl.question(question, (answer) => {
1503
1939
  rl.close();
1504
- resolve6(answer.trim());
1940
+ resolve4(answer.trim());
1505
1941
  });
1506
1942
  });
1507
1943
  async function signupIndividual(proxyUrl, email, credential, fetchFn = fetch) {
@@ -1520,7 +1956,12 @@ async function signupRequest(proxyUrl, payload, fetchFn) {
1520
1956
  });
1521
1957
  const body = await res.json().catch(() => ({}));
1522
1958
  if (res.ok && typeof body.token === "string")
1523
- return { ok: true, token: body.token, created: body.created === true };
1959
+ return {
1960
+ ok: true,
1961
+ token: body.token,
1962
+ created: body.created === true,
1963
+ verifyPending: body.verifyPending === true
1964
+ };
1524
1965
  return {
1525
1966
  ok: false,
1526
1967
  reason: typeof body.reason === "string" ? body.reason : `http_${res.status}`,
@@ -1606,7 +2047,7 @@ var resolveToken = async (o) => {
1606
2047
  if (typed)
1607
2048
  return { token: typed, source: "prompt" };
1608
2049
  }
1609
- const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? DEFAULT_PROXY_URL);
2050
+ const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? readConfig(o.home).proxyUrl ?? DEFAULT_PROXY_URL);
1610
2051
  if (!url)
1611
2052
  return null;
1612
2053
  const log = o.log ?? console.log;
@@ -1631,6 +2072,8 @@ var resolveToken = async (o) => {
1631
2072
  return null;
1632
2073
  }
1633
2074
  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."}`);
2075
+ if (r2.verifyPending)
2076
+ 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
2077
  log("Invite your team on the dashboard (Members → invite by email, Admin → Mint a seat); each person runs `sleds init --token …`.");
1635
2078
  return { token: r2.token, source: "signup" };
1636
2079
  }
@@ -1662,7 +2105,7 @@ var init = async (o) => {
1662
2105
  error("Invalid seat token: expected something like sled_xxxxxxxx.");
1663
2106
  return 2;
1664
2107
  }
1665
- const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? DEFAULT_PROXY_URL);
2108
+ const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? readConfig(o.home).proxyUrl ?? DEFAULT_PROXY_URL);
1666
2109
  if (!url) {
1667
2110
  error(`Invalid proxy URL: ${JSON.stringify(o.url ?? env.SLEDS_PROXY_URL ?? "")}. Expected https://proxy.sleds.ai.`);
1668
2111
  return 2;
@@ -1712,6 +2155,11 @@ var init = async (o) => {
1712
2155
  mkdirSync6(skillDir(o.home), { recursive: true });
1713
2156
  writeFileSync6(skillPath(o.home), SKILL_MD);
1714
2157
  wrote.push(`installed skill at ${skillPath(o.home)}`);
2158
+ if (o.installMusher) {
2159
+ const r = o.installMusher();
2160
+ if (r)
2161
+ wrote.push(r);
2162
+ }
1715
2163
  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
2164
  if (finalWiring === "proxy-always") {
1717
2165
  log(`Claude Code now runs through SLEDS on the org key. (${orgLine})`);
@@ -1730,7 +2178,7 @@ var init = async (o) => {
1730
2178
  log("");
1731
2179
  log("Claude Code keeps running on your seat; the status line reports its quota to SLEDS on every turn.");
1732
2180
  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.");
2181
+ 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
2182
  } else {
1735
2183
  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
2184
  }
@@ -1739,6 +2187,7 @@ var init = async (o) => {
1739
2187
  };
1740
2188
 
1741
2189
  // src/mcp.ts
2190
+ init_meter();
1742
2191
  var PROTOCOL_VERSION = "2025-06-18";
1743
2192
  var SERVER_INFO = { name: "sleds", version: "0.0.1" };
1744
2193
  var MCP_TIMEOUT_MS = 5000;
@@ -2272,6 +2721,8 @@ var mcp = async (o) => {
2272
2721
  };
2273
2722
 
2274
2723
  // src/commands/meter.ts
2724
+ init_config();
2725
+ init_meter();
2275
2726
  var meter = async (o) => {
2276
2727
  const log = o.log ?? console.log;
2277
2728
  const now = (o.now ?? Date.now)();
@@ -2290,14 +2741,235 @@ var meter = async (o) => {
2290
2741
  return 0;
2291
2742
  };
2292
2743
 
2744
+ // src/commands/musher.ts
2745
+ init_config();
2746
+ init_meter();
2747
+ import { spawnSync as spawnSync2 } from "node:child_process";
2748
+ import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
2749
+ import { hostname as hostname2 } from "node:os";
2750
+ import { dirname as dirname3 } from "node:path";
2751
+ var defaultWhich3 = (cmd) => {
2752
+ const path = process.env.PATH ?? "";
2753
+ for (const dir of path.split(":").filter(Boolean)) {
2754
+ const p = `${dir}/${cmd}`;
2755
+ if (existsSync8(p))
2756
+ return p;
2757
+ }
2758
+ return null;
2759
+ };
2760
+ var findClaude2 = (home, configured, which) => {
2761
+ if (configured && existsSync8(configured))
2762
+ return configured;
2763
+ const found = which("claude");
2764
+ if (found)
2765
+ return found;
2766
+ for (const p of [
2767
+ `${home}/.local/bin/claude`,
2768
+ "/opt/homebrew/bin/claude",
2769
+ "/usr/local/bin/claude",
2770
+ `${home}/.npm-global/bin/claude`
2771
+ ])
2772
+ if (existsSync8(p))
2773
+ return p;
2774
+ return null;
2775
+ };
2776
+ var musherInstall = (o) => {
2777
+ const log = o.log ?? console.log;
2778
+ const error = o.error ?? console.error;
2779
+ const platform = o.platform ?? process.platform;
2780
+ if (platform !== "darwin") {
2781
+ error(`sleds musher --install needs launchd (macOS). On ${platform}, run \`sleds musher tick\` from cron every 5 minutes.`);
2782
+ return 1;
2783
+ }
2784
+ const env = o.env ?? process.env;
2785
+ const plist = musherPlistPath(o.home, env);
2786
+ const label = musherLabel(env);
2787
+ const dir = configDir(o.home, env);
2788
+ mkdirSync7(dirname3(plist), { recursive: true });
2789
+ mkdirSync7(dir, { recursive: true, mode: 448 });
2790
+ const execPath = o.sledsArgv[0] ?? process.execPath;
2791
+ const agentEnv = {
2792
+ PATH: musherPath(o.home, execPath, env.PATH ?? ""),
2793
+ HOME: o.home,
2794
+ ...env.SLEDS_HOME ? { SLEDS_HOME: env.SLEDS_HOME } : {},
2795
+ ...env.CLAUDE_CONFIG_DIR ? { CLAUDE_CONFIG_DIR: env.CLAUDE_CONFIG_DIR } : {}
2796
+ };
2797
+ const exec = o.exec ?? defaultExecSync;
2798
+ if (existsSync8(plist))
2799
+ exec("launchctl", ["unload", plist]);
2800
+ writeFileSync7(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
2801
+ const r = exec("launchctl", ["load", plist]);
2802
+ if (!r.ok) {
2803
+ error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
2804
+ return 1;
2805
+ }
2806
+ const cfg = readConfig(o.home);
2807
+ writeConfig(o.home, { ...cfg, musher: { ...cfg.musher ?? {}, agent: "launchd" } });
2808
+ const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
2809
+ log(`musher installed: launchd agent ${label}, every 5 min · log ${musherLogPath(dir)}`);
2810
+ if (!claude)
2811
+ log("warn: `claude` not found on PATH — set it with `sleds musher --claude /path/to/claude`");
2812
+ return 0;
2813
+ };
2814
+ var musherUninstall = (o) => {
2815
+ const log = o.log ?? console.log;
2816
+ const env = o.env ?? process.env;
2817
+ const plist = musherPlistPath(o.home, env);
2818
+ const exec = o.exec ?? defaultExecSync;
2819
+ if (existsSync8(plist)) {
2820
+ exec("launchctl", ["unload", plist]);
2821
+ rmSync2(plist, { force: true });
2822
+ log(`musher removed: ${musherLabel(env)}`);
2823
+ } else
2824
+ log("musher was not installed.");
2825
+ const cfg = readConfig(o.home);
2826
+ if (cfg.musher)
2827
+ writeConfig(o.home, { ...cfg, musher: { ...cfg.musher, agent: "none" } });
2828
+ return 0;
2829
+ };
2830
+ var formatMusherStatus = (list, now) => {
2831
+ const lines = [];
2832
+ for (const j of list.running)
2833
+ lines.push(`running ${j.jobId}${j.startedAt ? ` · since ${formatLocal(j.startedAt)} (${formatDuration(now - j.startedAt)})` : ""}`);
2834
+ for (const j of list.due)
2835
+ lines.push(`due now ${j.jobId}${j.model ? ` · ${j.model}` : ""} · was ${formatLocal(j.scheduledAt)}`);
2836
+ for (const j of list.upcoming)
2837
+ lines.push(`waiting ${j.jobId}${j.model ? ` · ${j.model}` : ""} · starts ${formatLocal(j.scheduledAt)} (in ${formatDuration(j.scheduledAt - now)})`);
2838
+ if (lines.length === 0)
2839
+ lines.push("nothing deferred on this seat.");
2840
+ return lines;
2841
+ };
2842
+ var clientFor = (o) => {
2843
+ if (o.client)
2844
+ return o.client;
2845
+ const src = meterSource(o.home);
2846
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
2847
+ };
2848
+ var musherStatus = async (o) => {
2849
+ const log = o.log ?? console.log;
2850
+ const now = (o.now ?? Date.now)();
2851
+ const env = o.env ?? process.env;
2852
+ const plist = musherPlistPath(o.home, env);
2853
+ const cfg = readConfig(o.home);
2854
+ log(existsSync8(plist) ? `agent ${musherLabel(env)} installed (${plist})` : "agent not installed — sleds musher --install");
2855
+ const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
2856
+ log(`claude ${claude ?? "not found — sleds musher --claude /path/to/claude"}`);
2857
+ log(`mode ${cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
2858
+ const client = clientFor(o);
2859
+ if (!client) {
2860
+ log("seat not initialised (sleds init)");
2861
+ return 1;
2862
+ }
2863
+ const list = await client.list();
2864
+ if (!list) {
2865
+ log("proxy unreachable");
2866
+ return 1;
2867
+ }
2868
+ for (const line of formatMusherStatus(list, now))
2869
+ log(line);
2870
+ return 0;
2871
+ };
2872
+ var musherTick = async (o) => {
2873
+ const log = o.log ?? console.log;
2874
+ const client = clientFor(o);
2875
+ if (!client) {
2876
+ log("musher: seat not initialised; nothing to do");
2877
+ return 1;
2878
+ }
2879
+ const list = await client.list();
2880
+ if (!list) {
2881
+ log("musher: proxy unreachable; will try again next tick");
2882
+ return 0;
2883
+ }
2884
+ if (list.due.length === 0)
2885
+ return 0;
2886
+ const cfg = readConfig(o.home);
2887
+ const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3) ?? "claude";
2888
+ const seat = readSeatState(o.home);
2889
+ for (const job of list.due) {
2890
+ const out = await runJob({
2891
+ job,
2892
+ client,
2893
+ fallbackSession: seat.session,
2894
+ host: o.host ?? hostname2(),
2895
+ claudePath: claude,
2896
+ permissionMode: cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE,
2897
+ timeoutMs: (cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES) * 60000,
2898
+ exec: o.execCapture,
2899
+ log
2900
+ });
2901
+ if (out.kind === "skipped")
2902
+ log(`musher: job ${job.jobId} skipped — ${out.reason}`);
2903
+ }
2904
+ return 0;
2905
+ };
2906
+ var musherConfigure = (o, flags) => {
2907
+ const log = o.log ?? console.log;
2908
+ const cfg = readConfig(o.home);
2909
+ const musher = { ...cfg.musher ?? {} };
2910
+ if (flags.claude)
2911
+ musher.claudePath = flags.claude;
2912
+ if (flags.mode)
2913
+ musher.permissionMode = flags.mode;
2914
+ if (flags.timeout) {
2915
+ const n = Number.parseInt(flags.timeout, 10);
2916
+ if (!Number.isFinite(n) || n <= 0) {
2917
+ (o.error ?? console.error)("--timeout takes minutes, a positive number");
2918
+ return 1;
2919
+ }
2920
+ musher.timeoutMinutes = n;
2921
+ }
2922
+ writeConfig(o.home, { ...cfg, musher });
2923
+ log(`musher settings: claude ${musher.claudePath ?? "(PATH)"} · mode ${musher.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${musher.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
2924
+ return 0;
2925
+ };
2926
+ var defaultExecSync = (cmd, args) => {
2927
+ const r = spawnSync2(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
2928
+ if (r.error)
2929
+ return { ok: false, stderr: r.error.message };
2930
+ return { ok: r.status === 0, stderr: r.stderr ?? "" };
2931
+ };
2932
+ var musher = async (o) => {
2933
+ const args = [...o.argv];
2934
+ const flags = {};
2935
+ const positional = [];
2936
+ for (let i = 0;i < args.length; i++) {
2937
+ const a = args[i] ?? "";
2938
+ if (a.startsWith("--")) {
2939
+ const next = args[i + 1];
2940
+ if (next !== undefined && !next.startsWith("--")) {
2941
+ flags[a.slice(2)] = next;
2942
+ i++;
2943
+ } else
2944
+ flags[a.slice(2)] = "true";
2945
+ } else
2946
+ positional.push(a);
2947
+ }
2948
+ if (flags.install)
2949
+ return musherInstall(o);
2950
+ if (flags.uninstall)
2951
+ return musherUninstall(o);
2952
+ if (flags.claude || flags.mode || flags.timeout)
2953
+ return musherConfigure(o, flags);
2954
+ if (positional[0] === "tick")
2955
+ return musherTick(o);
2956
+ if (flags.status || positional.length === 0)
2957
+ return musherStatus(o);
2958
+ (o.error ?? console.error)(`sleds musher: unknown argument ${positional[0]}`);
2959
+ return 1;
2960
+ };
2961
+
2962
+ // src/commands/off.ts
2963
+ init_config();
2964
+
2293
2965
  // 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";
2966
+ import { existsSync as existsSync9, mkdirSync as mkdirSync8, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
2967
+ import { join as join7 } from "node:path";
2296
2968
  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;");
2969
+ var plistPath = (home) => join7(home, "Library", "LaunchAgents", `${REVERT_LABEL}.plist`);
2970
+ var xmlEscape2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2299
2971
  var buildLaunchdPlist = (sledsArgv, deadline) => {
2300
- const args = [...sledsArgv, "off"].map((a) => ` <string>${xmlEscape(a)}</string>`).join(`
2972
+ const args = [...sledsArgv, "off"].map((a) => ` <string>${xmlEscape2(a)}</string>`).join(`
2301
2973
  `);
2302
2974
  return `<?xml version="1.0" encoding="UTF-8"?>
2303
2975
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -2333,9 +3005,9 @@ var scheduleRevert = (d, deadlineMs) => {
2333
3005
  if (d.platform === "darwin") {
2334
3006
  const p = plistPath(d.home);
2335
3007
  try {
2336
- mkdirSync7(join8(d.home, "Library", "LaunchAgents"), { recursive: true });
3008
+ mkdirSync8(join7(d.home, "Library", "LaunchAgents"), { recursive: true });
2337
3009
  d.exec("launchctl", ["unload", p]);
2338
- writeFileSync7(p, buildLaunchdPlist(d.sledsArgv, deadline));
3010
+ writeFileSync8(p, buildLaunchdPlist(d.sledsArgv, deadline));
2339
3011
  const r = d.exec("launchctl", ["load", p]);
2340
3012
  if (r.ok)
2341
3013
  return { kind: "launchd", detail: `launchd agent ${REVERT_LABEL}` };
@@ -2362,9 +3034,9 @@ var scheduleRevert = (d, deadlineMs) => {
2362
3034
  var cancelRevert = (d, kind) => {
2363
3035
  if (kind === "launchd" || d.platform === "darwin") {
2364
3036
  const p = plistPath(d.home);
2365
- if (existsSync7(p)) {
3037
+ if (existsSync9(p)) {
2366
3038
  d.exec("launchctl", ["unload", p]);
2367
- rmSync(p, { force: true });
3039
+ rmSync3(p, { force: true });
2368
3040
  }
2369
3041
  }
2370
3042
  if (kind === "systemd" || d.platform === "linux") {
@@ -2373,6 +3045,7 @@ var cancelRevert = (d, kind) => {
2373
3045
  };
2374
3046
 
2375
3047
  // src/commands/off.ts
3048
+ init_settings();
2376
3049
  var off = (o) => {
2377
3050
  const log = o.log ?? console.log;
2378
3051
  const cfg = readConfig(o.home);
@@ -2400,7 +3073,13 @@ var off = (o) => {
2400
3073
  return 0;
2401
3074
  };
2402
3075
 
3076
+ // src/commands/on.ts
3077
+ init_config();
3078
+ init_settings();
3079
+
2403
3080
  // src/commands/init.ts
3081
+ init_config();
3082
+ init_settings();
2404
3083
  var DEFAULT_PROXY_URL2 = "http://localhost:8787";
2405
3084
 
2406
3085
  // src/commands/on.ts
@@ -2480,7 +3159,70 @@ var on = (o) => {
2480
3159
  return 0;
2481
3160
  };
2482
3161
 
3162
+ // src/commands/run.ts
3163
+ init_config();
3164
+ init_meter();
3165
+ import { hostname as hostname3 } from "node:os";
3166
+ var run = async (o) => {
3167
+ const log = o.log ?? console.log;
3168
+ const error = o.error ?? console.error;
3169
+ if (!o.jobId) {
3170
+ error("usage: sleds run <jobId>");
3171
+ return 2;
3172
+ }
3173
+ const client = o.client ?? (() => {
3174
+ const src = meterSource(o.home);
3175
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
3176
+ })();
3177
+ if (!client) {
3178
+ error("No seat on this machine. Run `sleds init` first.");
3179
+ return 1;
3180
+ }
3181
+ const list = await client.list();
3182
+ if (!list) {
3183
+ error("Could not reach the SLEDS proxy.");
3184
+ return 1;
3185
+ }
3186
+ const job = [...list.due, ...list.upcoming].find((j) => j.jobId === o.jobId);
3187
+ if (!job) {
3188
+ error(`Job ${o.jobId} is not a scheduled local job on this seat (sleds musher --status lists them).`);
3189
+ return 1;
3190
+ }
3191
+ const cfg = readConfig(o.home);
3192
+ const claude = findClaude(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich);
3193
+ if (!claude) {
3194
+ error("`claude` not found. Install Claude Code, or `sleds musher --claude /path/to/claude`.");
3195
+ return 1;
3196
+ }
3197
+ const out = await runJob({
3198
+ job,
3199
+ client,
3200
+ fallbackSession: readSeatState(o.home).session,
3201
+ host: o.host ?? hostname3(),
3202
+ claudePath: claude,
3203
+ permissionMode: cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE,
3204
+ timeoutMs: (cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES) * 60000,
3205
+ exec: o.execCapture,
3206
+ log
3207
+ });
3208
+ if (out.kind === "skipped") {
3209
+ error(`Not run: ${out.reason}`);
3210
+ return 1;
3211
+ }
3212
+ log(out.summary);
3213
+ if (out.ok) {
3214
+ const a = await client.attestation(job.jobId);
3215
+ if (a)
3216
+ log(a.attestation.trailer);
3217
+ else
3218
+ log(`(attestation pending — sleds verify ${job.jobId})`);
3219
+ }
3220
+ return out.ok ? 0 : 1;
3221
+ };
3222
+
2483
3223
  // src/commands/seatEvent.ts
3224
+ init_config();
3225
+ init_meter();
2484
3226
  var HOOK_STDIN_TIMEOUT_MS = 1000;
2485
3227
  var NOTIFICATION_TITLE = "sleds";
2486
3228
  var isObject7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
@@ -2533,15 +3275,15 @@ var seatEvent = async (o) => {
2533
3275
  };
2534
3276
 
2535
3277
  // 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";
3278
+ import { execFileSync as execFileSync2 } from "node:child_process";
3279
+ import { existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync8, readdirSync as readdirSync2, writeFileSync as writeFileSync9 } from "node:fs";
2538
3280
  import { platform } from "node:os";
2539
- import { join as join10 } from "node:path";
3281
+ import { join as join9 } from "node:path";
2540
3282
 
2541
3283
  // src/cookieDb.ts
2542
- import { copyFileSync, rmSync as rmSync2 } from "node:fs";
3284
+ import { copyFileSync, rmSync as rmSync4 } from "node:fs";
2543
3285
  import { tmpdir } from "node:os";
2544
- import { join as join9 } from "node:path";
3286
+ import { join as join8 } from "node:path";
2545
3287
  var QUERY = "SELECT name, hex(encrypted_value) AS ev FROM cookies WHERE host_key LIKE '%claude.ai%'";
2546
3288
  var isBun = () => typeof globalThis.Bun !== "undefined";
2547
3289
  async function queryRows(dbPath) {
@@ -2570,18 +3312,21 @@ function nodeSqlite() {
2570
3312
  return mod;
2571
3313
  }
2572
3314
  async function readCookieRows(dbPath) {
2573
- const copy = join9(tmpdir(), `sleds-cookies-${process.pid}-${Date.now()}.sqlite`);
3315
+ const copy = join8(tmpdir(), `sleds-cookies-${process.pid}-${Date.now()}.sqlite`);
2574
3316
  copyFileSync(dbPath, copy);
2575
3317
  try {
2576
3318
  const rows = await queryRows(copy);
2577
3319
  return rows.map((r) => ({ name: r.name, encrypted_value: Buffer.from(r.ev, "hex") }));
2578
3320
  } finally {
2579
3321
  try {
2580
- rmSync2(copy, { force: true });
3322
+ rmSync4(copy, { force: true });
2581
3323
  } catch {}
2582
3324
  }
2583
3325
  }
2584
3326
 
3327
+ // src/commands/seatSync.ts
3328
+ init_meter();
3329
+
2585
3330
  // src/seatSync.ts
2586
3331
  import { createDecipheriv, pbkdf2Sync } from "node:crypto";
2587
3332
  var BROWSERS = {
@@ -2727,13 +3472,13 @@ function parseUsageResponse(raw, now = Date.now()) {
2727
3472
  const resetsAt = parseResetsAt(v.resets_at);
2728
3473
  if (usedPct === undefined || usedPct < 0 || resetsAt === undefined)
2729
3474
  continue;
2730
- const bar2 = { usedPct, resetsAt };
3475
+ const bar = { usedPct, resetsAt };
2731
3476
  if (k === "five_hour")
2732
- out.fiveHour = bar2;
3477
+ out.fiveHour = bar;
2733
3478
  else if (k === "seven_day")
2734
- out.sevenDay = bar2;
3479
+ out.sevenDay = bar;
2735
3480
  else
2736
- out.models[k] = bar2;
3481
+ out.models[k] = bar;
2737
3482
  }
2738
3483
  const hasAny = out.fiveHour || out.sevenDay || Object.keys(out.models).length > 0 || out.usedCreditsUsd !== undefined;
2739
3484
  return hasAny ? out : null;
@@ -2843,7 +3588,7 @@ function findFile(root, name) {
2843
3588
  const stack = [root];
2844
3589
  while (stack.length > 0) {
2845
3590
  const dir = stack.pop();
2846
- if (!dir || !existsSync8(dir))
3591
+ if (!dir || !existsSync10(dir))
2847
3592
  continue;
2848
3593
  let entries;
2849
3594
  try {
@@ -2852,7 +3597,7 @@ function findFile(root, name) {
2852
3597
  continue;
2853
3598
  }
2854
3599
  for (const e of entries) {
2855
- const p = join10(dir, e.name);
3600
+ const p = join9(dir, e.name);
2856
3601
  if (e.isDirectory())
2857
3602
  stack.push(p);
2858
3603
  else if (e.name === name)
@@ -2863,8 +3608,8 @@ function findFile(root, name) {
2863
3608
  }
2864
3609
  function findCookieDb(home, source) {
2865
3610
  const b = BROWSERS[source.browser];
2866
- const root = join10(home, APP_SUPPORT, b.appDir);
2867
- if (!existsSync8(root))
3611
+ const root = join9(home, APP_SUPPORT, b.appDir);
3612
+ if (!existsSync10(root))
2868
3613
  throw new Error(`no ${source.browser} data at ${root} (is it installed?)`);
2869
3614
  if (!b.profiled) {
2870
3615
  const db2 = findFile(root, "Cookies");
@@ -2872,15 +3617,15 @@ function findCookieDb(home, source) {
2872
3617
  throw new Error(`no cookie store under ${root}`);
2873
3618
  return db2;
2874
3619
  }
2875
- const localStatePath = join10(root, "Local State");
2876
- const localState = existsSync8(localStatePath) ? readFileSync6(localStatePath, "utf8") : null;
3620
+ const localStatePath = join9(root, "Local State");
3621
+ const localState = existsSync10(localStatePath) ? readFileSync8(localStatePath, "utf8") : null;
2877
3622
  const wanted = source.profile ?? "Default";
2878
3623
  const { dir, ambiguous } = resolveProfileDir(localState, wanted);
2879
3624
  if (ambiguous)
2880
3625
  throw new Error(`"${wanted}" names ${ambiguous.length} ${source.browser} profiles (${ambiguous.join(", ")}) — pass the directory or the account email instead`);
2881
3626
  if (!dir)
2882
3627
  throw new Error(`no ${source.browser} profile named "${wanted}" (check chrome://version)`);
2883
- const db = cookieDbCandidates(join10(root, dir)).find((p) => existsSync8(p));
3628
+ const db = cookieDbCandidates(join9(root, dir)).find((p) => existsSync10(p));
2884
3629
  if (!db)
2885
3630
  throw new Error(`profile "${dir}" has no cookie store yet (open claude.ai in it once)`);
2886
3631
  return db;
@@ -2890,7 +3635,7 @@ async function readCookieRowsBun(home, source) {
2890
3635
  }
2891
3636
  function keychainKey(service) {
2892
3637
  try {
2893
- return execFileSync4("security", ["find-generic-password", "-w", "-s", service], {
3638
+ return execFileSync2("security", ["find-generic-password", "-w", "-s", service], {
2894
3639
  encoding: "utf8"
2895
3640
  }).trim();
2896
3641
  } catch {
@@ -2902,7 +3647,7 @@ function decryptJar(rows, safeStoragePw) {
2902
3647
  }
2903
3648
  var curlUsage = (org, cookie) => {
2904
3649
  try {
2905
- const out = execFileSync4("curl", usageCurlArgs(org, cookie), {
3650
+ const out = execFileSync2("curl", usageCurlArgs(org, cookie), {
2906
3651
  encoding: "utf8",
2907
3652
  maxBuffer: 8000000
2908
3653
  });
@@ -2911,8 +3656,8 @@ var curlUsage = (org, cookie) => {
2911
3656
  return { status: 0, body: "" };
2912
3657
  }
2913
3658
  };
2914
- function fetchSeatUsage(jar, org, now, run) {
2915
- const { status, body } = run(org, cookieHeader(jar, org));
3659
+ function fetchSeatUsage(jar, org, now, run2) {
3660
+ const { status, body } = run2(org, cookieHeader(jar, org));
2916
3661
  if (status !== 200)
2917
3662
  return { usage: null, status };
2918
3663
  try {
@@ -2923,7 +3668,7 @@ function fetchSeatUsage(jar, org, now, run) {
2923
3668
  }
2924
3669
  function launchAgentPlist(bun, script, home, source) {
2925
3670
  const label = launchLabel(source);
2926
- const log = join10(home, ".config", "sleds", `${label.slice("ai.sleds.".length)}.log`);
3671
+ const log = join9(home, ".config", "sleds", `${label.slice("ai.sleds.".length)}.log`);
2927
3672
  const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2928
3673
  const args = [bun, script, "seat-sync", ...sourceArgs(source)].map((a) => ` <string>${esc(a)}</string>`).join(`
2929
3674
  `);
@@ -2945,32 +3690,32 @@ ${args}
2945
3690
  `;
2946
3691
  }
2947
3692
  function plistPath2(home, source) {
2948
- return join10(home, "Library", "LaunchAgents", `${launchLabel(source)}.plist`);
3693
+ return join9(home, "Library", "LaunchAgents", `${launchLabel(source)}.plist`);
2949
3694
  }
2950
- function install2(home, argv, source, log) {
3695
+ function install(home, argv, source, log) {
2951
3696
  const bun = argv[0] ?? process.execPath;
2952
3697
  const script = argv[1] ?? "";
2953
3698
  if (!script) {
2954
3699
  log("cannot resolve the sleds script path to install the timer");
2955
3700
  return 1;
2956
3701
  }
2957
- const dir = join10(home, "Library", "LaunchAgents");
2958
- mkdirSync8(dir, { recursive: true });
3702
+ const dir = join9(home, "Library", "LaunchAgents");
3703
+ mkdirSync9(dir, { recursive: true });
2959
3704
  const path = plistPath2(home, source);
2960
- writeFileSync8(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
3705
+ writeFileSync9(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
2961
3706
  try {
2962
- execFileSync4("launchctl", ["unload", path], { stdio: "ignore" });
3707
+ execFileSync2("launchctl", ["unload", path], { stdio: "ignore" });
2963
3708
  } catch {}
2964
- execFileSync4("launchctl", ["load", "-w", path]);
3709
+ execFileSync2("launchctl", ["load", "-w", path]);
2965
3710
  log(`seat-sync timer installed (every ${SYNC_INTERVAL_S / 60} min) → ${path}`);
2966
3711
  return 0;
2967
3712
  }
2968
- function uninstall2(home, source, log) {
3713
+ function uninstall(home, source, log) {
2969
3714
  const path = plistPath2(home, source);
2970
3715
  try {
2971
- execFileSync4("launchctl", ["unload", "-w", path], { stdio: "ignore" });
3716
+ execFileSync2("launchctl", ["unload", "-w", path], { stdio: "ignore" });
2972
3717
  } catch {}
2973
- log(existsSync8(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
3718
+ log(existsSync10(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
2974
3719
  return 0;
2975
3720
  }
2976
3721
  var seatSync = async (o) => {
@@ -2983,9 +3728,9 @@ var seatSync = async (o) => {
2983
3728
  }
2984
3729
  const { source } = parsed;
2985
3730
  if (source.uninstall)
2986
- return uninstall2(o.home, source, log);
3731
+ return uninstall(o.home, source, log);
2987
3732
  if (source.install)
2988
- return install2(o.home, [process.execPath, process.argv[1] ?? ""], source, log);
3733
+ return install(o.home, [process.execPath, process.argv[1] ?? ""], source, log);
2989
3734
  if (platform() !== "darwin" && !o.readCookieRows) {
2990
3735
  log("seat-sync is macOS-only for now.");
2991
3736
  return 2;
@@ -3065,8 +3810,9 @@ async function postSeat(proxyUrl, token, body, fetchFn) {
3065
3810
  }
3066
3811
 
3067
3812
  // src/commands/specEvent.ts
3068
- import { readFileSync as readFileSync7, statSync } from "node:fs";
3069
- import { basename, isAbsolute, relative } from "node:path";
3813
+ init_meter();
3814
+ import { readFileSync as readFileSync9, statSync } from "node:fs";
3815
+ import { basename as basename2, isAbsolute, relative } from "node:path";
3070
3816
  var HOOK_STDIN_TIMEOUT_MS2 = 1000;
3071
3817
  var ESTIMATE_TIMEOUT_MS = 4000;
3072
3818
  var MIN_SPEC_CHARS = 200;
@@ -3079,12 +3825,12 @@ function isSpecPath(path) {
3079
3825
  return false;
3080
3826
  if (NOT_SPEC.test(path))
3081
3827
  return false;
3082
- return SPEC_DIRS.test(path) || SPEC_NAMES.test(basename(path));
3828
+ return SPEC_DIRS.test(path) || SPEC_NAMES.test(basename2(path));
3083
3829
  }
3084
3830
  function specTitle(body, path) {
3085
3831
  const m = /^#\s+(.+?)\s*$/m.exec(body);
3086
3832
  const t = m?.[1]?.trim();
3087
- return (t && t.length > 0 ? t : basename(path).replace(/\.(md|mdx)$/i, "")).slice(0, 120);
3833
+ return (t && t.length > 0 ? t : basename2(path).replace(/\.(md|mdx)$/i, "")).slice(0, 120);
3088
3834
  }
3089
3835
  function looksLikeSpec(body) {
3090
3836
  if (body.trim().length < MIN_SPEC_CHARS)
@@ -3121,7 +3867,7 @@ function readFileOrNull(path) {
3121
3867
  try {
3122
3868
  if (statSync(path).size > 2000000)
3123
3869
  return null;
3124
- return readFileSync7(path, "utf8");
3870
+ return readFileSync9(path, "utf8");
3125
3871
  } catch {
3126
3872
  return null;
3127
3873
  }
@@ -3142,7 +3888,7 @@ var specEvent = async (o) => {
3142
3888
  const spec = {
3143
3889
  title: specTitle(body, input.filePath),
3144
3890
  body: body.slice(0, MAX_SPEC_CHARS),
3145
- repoHint: input.cwd ? basename(input.cwd) : undefined
3891
+ repoHint: input.cwd ? basename2(input.cwd) : undefined
3146
3892
  };
3147
3893
  try {
3148
3894
  const res = await (o.fetchFn ?? fetch)(`${src.proxyUrl}/proxy/estimate`, {
@@ -3164,7 +3910,9 @@ var specEvent = async (o) => {
3164
3910
  };
3165
3911
 
3166
3912
  // src/commands/status.ts
3167
- import { existsSync as existsSync9 } from "node:fs";
3913
+ init_config();
3914
+ init_settings();
3915
+ import { existsSync as existsSync11 } from "node:fs";
3168
3916
  var checkHealth2 = async (url, fetchFn = fetch, timeoutMs = 1000) => {
3169
3917
  try {
3170
3918
  const res = await fetchFn(`${url}/healthz`, { signal: AbortSignal.timeout(timeoutMs) });
@@ -3236,7 +3984,7 @@ var status = async (o) => {
3236
3984
  const log = o.log ?? console.log;
3237
3985
  const nowMs = (o.now ?? Date.now)();
3238
3986
  const cfg = readConfig(o.home);
3239
- const settings = existsSync9(settingsPath(o.home)) ? readSettings(o.home) : {};
3987
+ const settings = existsSync11(settingsPath(o.home)) ? readSettings(o.home) : {};
3240
3988
  const inSettings = readProxy(settings);
3241
3989
  const proxyUrl = inSettings?.url ?? cfg.proxyUrl;
3242
3990
  const token = inSettings?.token ?? cfg.token;
@@ -3290,15 +4038,19 @@ var status = async (o) => {
3290
4038
  };
3291
4039
 
3292
4040
  // src/commands/statusline.ts
4041
+ init_meter();
3293
4042
  var statusline = async (o) => {
3294
4043
  const log = o.log ?? console.log;
3295
4044
  const now = (o.now ?? Date.now)();
3296
4045
  const input = o.stdin ?? await readStdin(STDIN_TIMEOUT_MS);
3297
4046
  const window = seatWindowFromStdin(input, now);
4047
+ const session = seatSessionFromStdin(input, now);
3298
4048
  let posts = [];
3299
4049
  try {
3300
4050
  if (window)
3301
- posts = rememberSeat(o, window, now);
4051
+ posts = rememberSeat(o, window, session, now);
4052
+ else if (session)
4053
+ writeSeatState(o.home, { ...readSeatState(o.home), session });
3302
4054
  } catch {}
3303
4055
  const seatFragment = formatSeatFragment(window);
3304
4056
  let line;
@@ -3317,9 +4069,9 @@ var statusline = async (o) => {
3317
4069
  await Promise.allSettled(posts);
3318
4070
  return 0;
3319
4071
  };
3320
- var rememberSeat = (o, window, now) => {
4072
+ var rememberSeat = (o, window, session, now) => {
3321
4073
  const prior = readSeatState(o.home);
3322
- let state = { ...prior, window };
4074
+ let state = { ...prior, window, ...session ? { session } : {} };
3323
4075
  const src = meterSource(o.home);
3324
4076
  const posts = [];
3325
4077
  if (src) {
@@ -3342,11 +4094,18 @@ var rememberSeat = (o, window, now) => {
3342
4094
  };
3343
4095
 
3344
4096
  // src/commands/uninstall.ts
3345
- import { existsSync as existsSync10, rmSync as rmSync3 } from "node:fs";
3346
- var uninstall3 = (o) => {
4097
+ init_config();
4098
+ init_meter();
4099
+ import { spawnSync as spawnSync3 } from "node:child_process";
4100
+ import { existsSync as existsSync12, rmSync as rmSync5 } from "node:fs";
4101
+ init_settings();
4102
+ var defaultUnload = (plist) => {
4103
+ spawnSync3("launchctl", ["unload", plist], { stdio: "ignore" });
4104
+ };
4105
+ var uninstall2 = (o) => {
3347
4106
  const log = o.log ?? console.log;
3348
4107
  const removed = [];
3349
- if (existsSync10(settingsPath(o.home))) {
4108
+ if (existsSync12(settingsPath(o.home))) {
3350
4109
  let settings = readSettings(o.home);
3351
4110
  let changed = false;
3352
4111
  if (hasEitherKey(settings)) {
@@ -3378,16 +4137,22 @@ var uninstall3 = (o) => {
3378
4137
  writeSettings(o.home, settings);
3379
4138
  }
3380
4139
  for (const file of [meterCachePath(o.home), seatStatePath(o.home)]) {
3381
- if (existsSync10(file)) {
3382
- rmSync3(file, { force: true });
4140
+ if (existsSync12(file)) {
4141
+ rmSync5(file, { force: true });
3383
4142
  removed.push(file);
3384
4143
  }
3385
4144
  }
3386
4145
  const skill = skillDir(o.home);
3387
- if (existsSync10(skill)) {
3388
- rmSync3(skill, { recursive: true, force: true });
4146
+ if (existsSync12(skill)) {
4147
+ rmSync5(skill, { recursive: true, force: true });
3389
4148
  removed.push(skill);
3390
4149
  }
4150
+ const plist = musherPlistPath(o.home);
4151
+ if (existsSync12(plist)) {
4152
+ (o.unloadAgent ?? defaultUnload)(plist);
4153
+ rmSync5(plist, { force: true });
4154
+ removed.push(`launchd agent ${musherLabel()}`);
4155
+ }
3391
4156
  if (removed.length === 0) {
3392
4157
  log("Nothing to remove; SLEDS was not installed.");
3393
4158
  } else {
@@ -3405,15 +4170,78 @@ var hasEitherKey = (settings) => {
3405
4170
  return BASE_URL_KEY in env || AUTH_TOKEN_KEY in env;
3406
4171
  };
3407
4172
 
4173
+ // src/commands/verify.ts
4174
+ init_meter();
4175
+ var jobIdFromTarget = (target) => {
4176
+ const t = target.trim();
4177
+ const m = /SLEDS-Attest:.*\bjob=([^\s]+)/.exec(t);
4178
+ if (m?.[1])
4179
+ return m[1];
4180
+ return /^[A-Za-z0-9_-]{3,64}$/.test(t) ? t : null;
4181
+ };
4182
+ var verify = async (o) => {
4183
+ const log = o.log ?? console.log;
4184
+ const error = o.error ?? console.error;
4185
+ const jobId = o.target ? jobIdFromTarget(o.target) : null;
4186
+ if (!jobId) {
4187
+ error("usage: sleds verify <jobId> (or paste the SLEDS-Attest line)");
4188
+ return 2;
4189
+ }
4190
+ const client = o.client ?? (() => {
4191
+ const src = meterSource(o.home);
4192
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
4193
+ })();
4194
+ if (!client) {
4195
+ error("No seat on this machine. Run `sleds init` first.");
4196
+ return 1;
4197
+ }
4198
+ const a = await client.attestation(jobId);
4199
+ if (!a) {
4200
+ error(`No attestation for ${jobId} (not delivered, not signed, not your org, or the proxy is down).`);
4201
+ return 1;
4202
+ }
4203
+ if (!a.publicKey) {
4204
+ error(`Attestation found but its key ${a.attestation.keyId} is not on file — cannot verify.`);
4205
+ return 1;
4206
+ }
4207
+ const ok = verifyAttestation(a.attestation.payload, a.attestation.sig, a.publicKey);
4208
+ const f = parsePayload(a.attestation.payload);
4209
+ log(`${ok ? "VERIFIED" : "INVALID "} job ${jobId}`);
4210
+ log(` model ${f.model ?? a.model ?? "unknown"}`);
4211
+ log(` engine ${f.engine ?? a.engine}${f.seat ? ` · seat ${f.seat}` : ""}`);
4212
+ log(` finished ${f.finished ?? "?"} · signed ${formatLocal(a.attestation.signedAt)} · key ${a.attestation.keyId}`);
4213
+ if (a.outputs.length)
4214
+ log(` outputs ${a.outputs.join(" ")}`);
4215
+ log(` ${a.attestation.trailer}`);
4216
+ if (!ok)
4217
+ log(" The signature does not match the payload under the org's public key.");
4218
+ return ok ? 0 : 1;
4219
+ };
4220
+
3408
4221
  // src/schedule.ts
3409
- import { spawnSync } from "node:child_process";
4222
+ import { spawnSync as spawnSync4 } from "node:child_process";
3410
4223
  var defaultExec = (cmd, args) => {
3411
- const r = spawnSync(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
4224
+ const r = spawnSync4(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
3412
4225
  if (r.error)
3413
4226
  return { ok: false, stderr: r.error.message };
3414
4227
  return { ok: r.status === 0, stderr: r.stderr ?? "" };
3415
4228
  };
3416
4229
 
4230
+ // src/settings.ts
4231
+ import { dirname as dirname4, join as join10, resolve as resolve4 } from "node:path";
4232
+ var withSeatEnv2 = (command, home, env = process.env) => {
4233
+ const parts = [];
4234
+ const sh = env.SLEDS_HOME?.trim();
4235
+ const cc = env.CLAUDE_CONFIG_DIR?.trim();
4236
+ if (sh)
4237
+ parts.push(`SLEDS_HOME=${shellWord2(resolve4(home, sh))}`);
4238
+ if (cc)
4239
+ parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(resolve4(home, cc))}`);
4240
+ parts.push(command);
4241
+ return parts.join(" ");
4242
+ };
4243
+ var shellWord2 = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
4244
+
3417
4245
  // src/index.ts
3418
4246
  var USAGE = `usage: sleds <command> [options]
3419
4247
 
@@ -3434,14 +4262,21 @@ commands:
3434
4262
  --until-reset <when> revert at this time: +5h, +90m, or an ISO timestamp
3435
4263
  --reason "<text>" note shown by \`sleds status\`
3436
4264
  --no-schedule record the deadline but do not install a launchd/systemd revert
4265
+ defer Hit a limit? Resume this session on this Mac at the reset \u2014 runs itself on your
4266
+ seat, no key needed (the musher). Uses the last session the status line saw.
4267
+ --at +2h | <ISO> start then instead of the binding reset
4268
+ --session <id> --cwd <dir> --model <id> override what was remembered
4269
+ musher The agent that runs deferred jobs here: --install / --uninstall / --status,
4270
+ --claude <path>, --mode <permission-mode> (default acceptEdits), --timeout <min>.
4271
+ run <jobId> Run one deferred job on this Mac now (the manual musher).
4272
+ verify <jobId | SLEDS-Attest line> Check a finished job's attestation: which model built it,
4273
+ signed by the org's key, verified locally.
3437
4274
  off Switch Claude Code back to your seat. Restores whatever \`on\` replaced.
3438
4275
  uninstall Remove the proxy settings, skill and status line. Keeps ~/.sleds-token.
3439
4276
  status Show the switch state, deadline, proxy health and wall mode.
3440
4277
  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.
4278
+ Node version, stale links, the seat's last quota report.
3442
4279
  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
4280
  mcp Run the MCP server on stdio: tools usage, estimate, overnight, jobs.
3446
4281
  Register with \`claude mcp add sleds -- sleds mcp\` (Cowork sees the same server).
3447
4282
  seat-sync Read the seat's subscription usage (weekly/5h limits, per-model, credits) from a
@@ -3492,17 +4327,24 @@ var stringFlag = (flags, name) => {
3492
4327
  const v = flags[name];
3493
4328
  return typeof v === "string" ? v : undefined;
3494
4329
  };
4330
+ var hookStdin = async () => {
4331
+ if (process.stdin.isTTY)
4332
+ return;
4333
+ const { readStdin: readStdin3 } = await Promise.resolve().then(() => (init_seat(), exports_seat));
4334
+ const text = await readStdin3(300);
4335
+ return text.trim() ? text : undefined;
4336
+ };
3495
4337
  var sledsArgv = () => {
3496
4338
  const script = process.argv[1];
3497
4339
  return script ? [process.execPath, script] : [process.execPath];
3498
4340
  };
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(" ");
4341
+ var shellQuote = (s) => /^[\w./-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
4342
+ var statusLineCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "statusline"].map(shellQuote).join(" "), home);
4343
+ var seatEventCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "seat-event"].map(shellQuote).join(" "), home);
4344
+ var specEventCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "spec-event"].map(shellQuote).join(" "), home);
3503
4345
  var main = async (argv) => {
3504
4346
  const { cmd, flags } = parseArgs(argv);
3505
- const home = homedir3();
4347
+ const home = homedir();
3506
4348
  const scheduler = {
3507
4349
  platform: platform2(),
3508
4350
  home,
@@ -3522,6 +4364,19 @@ var main = async (argv) => {
3522
4364
  noProbe: flags["no-probe"] === true,
3523
4365
  proxyAlways: flags["proxy-always"] === true,
3524
4366
  seatFirst: flags["seat-first"] === true,
4367
+ installMusher: () => {
4368
+ if (platform2() !== "darwin")
4369
+ return null;
4370
+ const lines = [];
4371
+ const code = musherInstall({
4372
+ home,
4373
+ argv: [],
4374
+ sledsArgv: sledsArgv(),
4375
+ log: (l) => lines.push(l),
4376
+ error: (l) => lines.push(l)
4377
+ });
4378
+ return code === 0 ? lines[0] ?? "installed the musher agent" : `musher not installed: ${lines.join(" ")}`;
4379
+ },
3525
4380
  prompt: promptStdin,
3526
4381
  statusLineCommand: statusLineCommand(sledsArgv()),
3527
4382
  seatEventCommand: seatEventCommand(sledsArgv()),
@@ -3548,16 +4403,22 @@ var main = async (argv) => {
3548
4403
  });
3549
4404
  case "off":
3550
4405
  return off({ home, scheduler });
4406
+ case "defer":
4407
+ return defer({ home, argv: process.argv.slice(3), stdin: await hookStdin() });
4408
+ case "musher":
4409
+ return musher({ home, argv: process.argv.slice(3), sledsArgv: sledsArgv() });
4410
+ case "run":
4411
+ return run({ home, jobId: process.argv[3] });
4412
+ case "verify":
4413
+ return verify({ home, target: process.argv.slice(3).join(" ") || undefined });
3551
4414
  case "uninstall":
3552
- return uninstall3({ home });
4415
+ return uninstall2({ home });
3553
4416
  case "doctor":
3554
4417
  return doctor({ home });
3555
4418
  case "status":
3556
4419
  return status({ home });
3557
4420
  case "meter":
3558
4421
  return meter({ home });
3559
- case "bar":
3560
- return bar({ home, argv: process.argv.slice(3) });
3561
4422
  case "mcp":
3562
4423
  return mcp({ home });
3563
4424
  case "seat-sync":
@@ -3572,7 +4433,7 @@ var main = async (argv) => {
3572
4433
  case "version":
3573
4434
  case "--version":
3574
4435
  case "-v":
3575
- console.log(`sleds ${"0.1.11"}`);
4436
+ console.log("sleds 0.1.15");
3576
4437
  return 0;
3577
4438
  default:
3578
4439
  console.error(`unknown command: ${cmd}