@sleds/cli 0.1.19 → 0.1.20

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 (2) hide show
  1. package/dist/sleds.mjs +402 -7
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -737,6 +737,43 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
737
737
  const session = parseSeatSession2(raw.session);
738
738
  if (session)
739
739
  out.session = session;
740
+ if (isObject11(raw.cockpit) && raw.cockpit.kind === "cmux") {
741
+ const c = { kind: "cmux" };
742
+ if (isObject11(raw.cockpit.lines)) {
743
+ const lines = {};
744
+ for (const [k, v] of Object.entries(raw.cockpit.lines))
745
+ if (typeof v === "string")
746
+ lines[k] = v;
747
+ c.lines = lines;
748
+ }
749
+ if (raw.cockpit.progress === true)
750
+ c.progress = true;
751
+ const f = raw.cockpit.feed;
752
+ if (isObject11(f) && num6(f.at) !== undefined) {
753
+ const strs = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
754
+ const j = isObject11(f.jobs) ? f.jobs : {};
755
+ c.feed = {
756
+ at: num6(f.at),
757
+ handoffs: strs(f.handoffs),
758
+ jobs: {
759
+ scheduled: num6(j.scheduled) ?? 0,
760
+ running: num6(j.running) ?? 0,
761
+ delivered: num6(j.delivered) ?? 0,
762
+ failed: num6(j.failed) ?? 0
763
+ },
764
+ delivered: strs(f.delivered),
765
+ failed: strs(f.failed)
766
+ };
767
+ }
768
+ out.cockpit = c;
769
+ }
770
+ if (isObject11(raw.estimate)) {
771
+ const e = raw.estimate;
772
+ const project = typeof e.project === "string" ? e.project : undefined;
773
+ const [lo, hi, lm, hm, at] = [e.lowUsd, e.highUsd, e.lowMin, e.highMin, e.at].map(num6);
774
+ if (project && lo !== undefined && hi !== undefined && lm !== undefined && hm !== undefined && at !== undefined)
775
+ out.estimate = { project, lowUsd: lo, highUsd: hi, lowMin: lm, highMin: hm, at };
776
+ }
740
777
  return out;
741
778
  }, parseSeatSession2 = (v) => {
742
779
  if (!isObject11(v) || typeof v.id !== "string" || !v.id)
@@ -1500,6 +1537,43 @@ var parseSeatState = (raw) => {
1500
1537
  const session = parseSeatSession(raw.session);
1501
1538
  if (session)
1502
1539
  out.session = session;
1540
+ if (isObject5(raw.cockpit) && raw.cockpit.kind === "cmux") {
1541
+ const c = { kind: "cmux" };
1542
+ if (isObject5(raw.cockpit.lines)) {
1543
+ const lines = {};
1544
+ for (const [k, v] of Object.entries(raw.cockpit.lines))
1545
+ if (typeof v === "string")
1546
+ lines[k] = v;
1547
+ c.lines = lines;
1548
+ }
1549
+ if (raw.cockpit.progress === true)
1550
+ c.progress = true;
1551
+ const f = raw.cockpit.feed;
1552
+ if (isObject5(f) && num3(f.at) !== undefined) {
1553
+ const strs = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
1554
+ const j = isObject5(f.jobs) ? f.jobs : {};
1555
+ c.feed = {
1556
+ at: num3(f.at),
1557
+ handoffs: strs(f.handoffs),
1558
+ jobs: {
1559
+ scheduled: num3(j.scheduled) ?? 0,
1560
+ running: num3(j.running) ?? 0,
1561
+ delivered: num3(j.delivered) ?? 0,
1562
+ failed: num3(j.failed) ?? 0
1563
+ },
1564
+ delivered: strs(f.delivered),
1565
+ failed: strs(f.failed)
1566
+ };
1567
+ }
1568
+ out.cockpit = c;
1569
+ }
1570
+ if (isObject5(raw.estimate)) {
1571
+ const e = raw.estimate;
1572
+ const project = typeof e.project === "string" ? e.project : undefined;
1573
+ const [lo, hi, lm, hm, at] = [e.lowUsd, e.highUsd, e.lowMin, e.highMin, e.at].map(num3);
1574
+ if (project && lo !== undefined && hi !== undefined && lm !== undefined && hm !== undefined && at !== undefined)
1575
+ out.estimate = { project, lowUsd: lo, highUsd: hi, lowMin: lm, highMin: hm, at };
1576
+ }
1503
1577
  return out;
1504
1578
  };
1505
1579
  var parseSeatSession = (v) => {
@@ -1877,11 +1951,281 @@ var defer = async (o) => {
1877
1951
  };
1878
1952
 
1879
1953
  // src/commands/doctor.ts
1880
- init_config();
1881
- init_meter();
1882
1954
  import { execFileSync } from "node:child_process";
1883
1955
  import { existsSync as existsSync8, lstatSync, readlinkSync } from "node:fs";
1884
1956
  import { resolve as resolve3 } from "node:path";
1957
+
1958
+ // src/channels/cockpit.ts
1959
+ var detectCockpit = (env) => {
1960
+ if (env.CMUX_SOCKET_PATH || env.CMUX_WORKSPACE_ID || env.CMUX_SURFACE_ID) {
1961
+ const c = { kind: "cmux" };
1962
+ if (env.CMUX_WORKSPACE_ID)
1963
+ c.workspaceId = env.CMUX_WORKSPACE_ID;
1964
+ if (env.CMUX_SURFACE_ID)
1965
+ c.surfaceId = env.CMUX_SURFACE_ID;
1966
+ if (env.CMUX_SOCKET_PATH)
1967
+ c.socket = env.CMUX_SOCKET_PATH;
1968
+ return c;
1969
+ }
1970
+ return null;
1971
+ };
1972
+ var SEP2 = " · ";
1973
+ var COLOR = {
1974
+ ok: "#6b7280",
1975
+ approaching: "#d97706",
1976
+ wall: "#dc2626"
1977
+ };
1978
+ var PRIORITY = { ok: 20, approaching: 90, wall: 100 };
1979
+ var BAR_LABEL = {
1980
+ sevenDay: "wk",
1981
+ fiveHour: "5h",
1982
+ spendLimit: "spend",
1983
+ credits: "credits"
1984
+ };
1985
+ var EXITS = "/sled handoff · sleds defer · or continue on the workspace key";
1986
+ var resetIn = (resetsAt, now) => {
1987
+ const mins = Math.max(0, Math.round((resetsAt - now) / 60000));
1988
+ return mins >= 60 ? `${Math.floor(mins / 60)}h${mins % 60 ? `${mins % 60}m` : ""}` : `${mins}m`;
1989
+ };
1990
+ var cockpitSeatLine = (w, now = Date.now()) => {
1991
+ if (!w)
1992
+ return null;
1993
+ const bars = [
1994
+ ["wk", w.sevenDay],
1995
+ ["5h", w.fiveHour],
1996
+ ["spend", w.spendLimit]
1997
+ ].filter((x) => x[1] !== undefined);
1998
+ if (bars.length === 0)
1999
+ return null;
2000
+ const hot = bars.filter(([, b]) => b.usedPct >= APPROACHING_PCT).sort((a, b) => b[1].usedPct - a[1].usedPct)[0];
2001
+ if (!hot) {
2002
+ return { text: bars.map(([n, b]) => `${n} ${Math.round(b.usedPct)}%`).join(SEP2), stage: "ok" };
2003
+ }
2004
+ const [name, bar] = hot;
2005
+ const atWall = bar.usedPct >= 100;
2006
+ return {
2007
+ text: `${atWall ? "" : "⚠ "}${name} ${atWall ? "at the wall" : `${Math.round(bar.usedPct)}%`}${SEP2}resets ${resetIn(bar.resetsAt, now)}`,
2008
+ stage: atWall ? "wall" : "approaching",
2009
+ progress: { value: Math.min(1, bar.usedPct / 100), label: `seat ${name}` }
2010
+ };
2011
+ };
2012
+ var ws = (c) => c.workspaceId ? ["--workspace", c.workspaceId] : [];
2013
+ var keyed = (argv, c, key, text, prior, style, next) => {
2014
+ if (text) {
2015
+ next[key] = text;
2016
+ if (text !== prior)
2017
+ argv.push([
2018
+ "set-status",
2019
+ `sleds.${key}`,
2020
+ text,
2021
+ "--icon",
2022
+ style.icon,
2023
+ ...style.color ? ["--color", style.color] : [],
2024
+ "--priority",
2025
+ String(style.priority),
2026
+ ...ws(c)
2027
+ ]);
2028
+ } else if (prior) {
2029
+ argv.push(["clear-status", `sleds.${key}`, ...ws(c)]);
2030
+ }
2031
+ };
2032
+ var ESTIMATE_TTL_MS = 2 * 60 * 60 * 1000;
2033
+ var usd2 = (n) => n < 10 ? `$${n.toFixed(2)}` : `$${Math.round(n)}`;
2034
+ var hrs = (min) => min < 60 ? `${Math.round(min)}m` : `${(min / 60).toFixed(min < 600 ? 1 : 0)}h`;
2035
+ var cockpitEstimateLine = (e, now) => {
2036
+ if (!e || now - e.at > ESTIMATE_TTL_MS)
2037
+ return null;
2038
+ const proj = e.project.length > 28 ? `…${e.project.slice(-27)}` : e.project;
2039
+ return `est ${usd2(e.lowUsd)}–${usd2(e.highUsd)}${SEP2}${hrs(e.lowMin)}–${hrs(e.highMin)}${SEP2}${proj}`;
2040
+ };
2041
+ var cockpitFeedLines = (f) => {
2042
+ if (!f)
2043
+ return { handoff: null, jobs: null };
2044
+ const n = f.handoffs.length;
2045
+ const handoff = n ? `${n} hand-off${n === 1 ? "" : "s"} waiting${SEP2}/sled pickup` : null;
2046
+ const parts = [];
2047
+ if (f.jobs.running)
2048
+ parts.push(`${f.jobs.running} running`);
2049
+ if (f.jobs.scheduled)
2050
+ parts.push(`${f.jobs.scheduled} scheduled`);
2051
+ if (f.jobs.delivered)
2052
+ parts.push(`${f.jobs.delivered} done`);
2053
+ if (f.jobs.failed)
2054
+ parts.push(`${f.jobs.failed} failed`);
2055
+ return { handoff, jobs: parts.length ? `overnight: ${parts.join(SEP2)}` : null };
2056
+ };
2057
+ var renderCockpit = (cockpit, window, events, prior, now = Date.now(), extra = {}) => {
2058
+ const argv = [];
2059
+ const priorLines = prior?.lines ?? {};
2060
+ const lines = {};
2061
+ const state = { kind: cockpit.kind };
2062
+ const seat = cockpitSeatLine(window, now);
2063
+ keyed(argv, cockpit, "seat", seat?.text ?? null, priorLines.seat, {
2064
+ icon: "⛷",
2065
+ color: COLOR[seat?.stage ?? "ok"],
2066
+ priority: PRIORITY[seat?.stage ?? "ok"]
2067
+ }, lines);
2068
+ if (seat?.progress) {
2069
+ state.progress = true;
2070
+ argv.push([
2071
+ "set-progress",
2072
+ seat.progress.value.toFixed(2),
2073
+ "--label",
2074
+ seat.progress.label,
2075
+ ...ws(cockpit)
2076
+ ]);
2077
+ } else if (prior?.progress) {
2078
+ argv.push(["clear-progress", ...ws(cockpit)]);
2079
+ }
2080
+ keyed(argv, cockpit, "estimate", cockpitEstimateLine(extra.estimate, now), priorLines.estimate, { icon: "≈", priority: 30 }, lines);
2081
+ const feed = extra.feed ?? prior?.feed;
2082
+ const fl = cockpitFeedLines(feed);
2083
+ keyed(argv, cockpit, "handoff", fl.handoff, priorLines.handoff, { icon: "⇄", color: "#2563eb", priority: 60 }, lines);
2084
+ keyed(argv, cockpit, "jobs", fl.jobs, priorLines.jobs, { icon: "☾", priority: 25 }, lines);
2085
+ if (Object.keys(lines).length)
2086
+ state.lines = lines;
2087
+ if (feed)
2088
+ state.feed = feed;
2089
+ for (const e of events) {
2090
+ const bar = BAR_LABEL[e.bar];
2091
+ if (e.stage === "hit") {
2092
+ const sub = e.bar === "credits" ? "usage credits exhausted" : `resets ${resetIn(e.resetsAt, now)}`;
2093
+ argv.push([
2094
+ "notify",
2095
+ "--title",
2096
+ `SLEDS · seat ${bar} at the wall`,
2097
+ "--subtitle",
2098
+ sub,
2099
+ "--body",
2100
+ EXITS,
2101
+ ...ws(cockpit)
2102
+ ]);
2103
+ } else {
2104
+ argv.push([
2105
+ "log",
2106
+ "--level",
2107
+ "warning",
2108
+ "--source",
2109
+ "sleds",
2110
+ `${bar} bar ${Math.round(e.usedPct)}% — resets ${resetIn(e.resetsAt, now)}`,
2111
+ ...ws(cockpit)
2112
+ ]);
2113
+ }
2114
+ }
2115
+ if (extra.feed) {
2116
+ const before = prior?.feed;
2117
+ const newHandoffs = extra.feed.handoffs.filter((id) => !(before?.handoffs ?? []).includes(id));
2118
+ if (newHandoffs.length)
2119
+ argv.push([
2120
+ "notify",
2121
+ "--title",
2122
+ `SLEDS · ${newHandoffs.length === 1 ? "a hand-off is" : `${newHandoffs.length} hand-offs are`} waiting for you`,
2123
+ "--body",
2124
+ `/sled pickup ${newHandoffs[0]}`,
2125
+ ...ws(cockpit)
2126
+ ]);
2127
+ for (const id of extra.feed.delivered.filter((id2) => !(before?.delivered ?? []).includes(id2)))
2128
+ argv.push([
2129
+ "log",
2130
+ "--level",
2131
+ "success",
2132
+ "--source",
2133
+ "sleds",
2134
+ `overnight job done · /sled resume ${id}`,
2135
+ ...ws(cockpit)
2136
+ ]);
2137
+ for (const id of extra.feed.failed.filter((id2) => !(before?.failed ?? []).includes(id2)))
2138
+ argv.push([
2139
+ "notify",
2140
+ "--title",
2141
+ "SLEDS · overnight job failed",
2142
+ "--body",
2143
+ `sleds jobs · ${id}`,
2144
+ ...ws(cockpit)
2145
+ ]);
2146
+ }
2147
+ return { argv, state };
2148
+ };
2149
+ var FEED_MIN_INTERVAL_MS = 60000;
2150
+ var isObj = (v) => typeof v === "object" && v !== null;
2151
+ var feedFromJobs = (body, me, now) => {
2152
+ const f = {
2153
+ at: now,
2154
+ handoffs: [],
2155
+ jobs: { scheduled: 0, running: 0, delivered: 0, failed: 0 },
2156
+ delivered: [],
2157
+ failed: []
2158
+ };
2159
+ if (!isObj(body))
2160
+ return f;
2161
+ for (const h of Array.isArray(body.handoffs) ? body.handoffs.filter(isObj) : []) {
2162
+ const mine = me !== undefined && typeof h.from === "string" && h.from.toLowerCase() === me.toLowerCase();
2163
+ if (h.forMe === true && !mine && typeof h.handoffId === "string")
2164
+ f.handoffs.push(h.handoffId);
2165
+ }
2166
+ for (const j of Array.isArray(body.jobs) ? body.jobs.filter(isObj) : []) {
2167
+ const id = typeof j.jobId === "string" ? j.jobId : undefined;
2168
+ switch (j.status) {
2169
+ case "scheduled":
2170
+ case "queued":
2171
+ case "awaiting_approval":
2172
+ f.jobs.scheduled++;
2173
+ break;
2174
+ case "running":
2175
+ case "estimating":
2176
+ f.jobs.running++;
2177
+ break;
2178
+ case "delivered":
2179
+ f.jobs.delivered++;
2180
+ if (id)
2181
+ f.delivered.push(id);
2182
+ break;
2183
+ case "failed":
2184
+ f.jobs.failed++;
2185
+ if (id)
2186
+ f.failed.push(id);
2187
+ break;
2188
+ default:
2189
+ break;
2190
+ }
2191
+ }
2192
+ return f;
2193
+ };
2194
+ var fetchFeed = async (o, now) => {
2195
+ try {
2196
+ const res = await (o.fetchFn ?? fetch)(`${o.proxyUrl}/proxy/jobs`, {
2197
+ headers: { authorization: `Bearer ${o.token}` },
2198
+ signal: AbortSignal.timeout(o.timeoutMs ?? 1500)
2199
+ });
2200
+ if (!res.ok)
2201
+ return null;
2202
+ return feedFromJobs(await res.json(), o.email, now);
2203
+ } catch {
2204
+ return null;
2205
+ }
2206
+ };
2207
+ var runCockpit = async (cockpit, argv, opts = {}) => {
2208
+ const spawn = opts.spawn ?? Bun.spawn;
2209
+ const timeoutMs = opts.timeoutMs ?? 300;
2210
+ const bin = cockpit.kind === "cmux" ? "cmux" : cockpit.kind;
2211
+ await Promise.allSettled(argv.map(async (a) => {
2212
+ const proc = spawn([bin, ...a], { stdout: "ignore", stderr: "ignore", stdin: "ignore" });
2213
+ const timer = setTimeout(() => {
2214
+ try {
2215
+ proc.kill();
2216
+ } catch {}
2217
+ }, timeoutMs);
2218
+ try {
2219
+ await proc.exited;
2220
+ } finally {
2221
+ clearTimeout(timer);
2222
+ }
2223
+ }));
2224
+ };
2225
+
2226
+ // src/commands/doctor.ts
2227
+ init_config();
2228
+ init_meter();
1885
2229
  init_settings();
1886
2230
 
1887
2231
  // src/commands/musher.ts
@@ -2065,6 +2409,12 @@ async function runDoctor(o) {
2065
2409
  fix: skill ? undefined : "sleds init (re-run; keeps your token)"
2066
2410
  });
2067
2411
  }
2412
+ const cockpit = detectCockpit(env);
2413
+ checks.push({
2414
+ level: "ok",
2415
+ name: "cockpit",
2416
+ detail: cockpit ? `${cockpit.kind}${cockpit.workspaceId ? ` (workspace ${cockpit.workspaceId})` : ""} — seat line in the sidebar, ring at the wall` : "none detected (run inside cmux to mirror the seat into its sidebar)"
2417
+ });
2068
2418
  const nodeVersion = o.nodeVersion ?? process.version;
2069
2419
  checks.push({
2070
2420
  level: nodeOk(nodeVersion) ? "ok" : "warn",
@@ -4680,6 +5030,21 @@ var specEvent = async (o) => {
4680
5030
  if (b && typeof b.lowUsd === "number" && typeof b.highUsd === "number") {
4681
5031
  const min = (n) => typeof n === "number" ? Math.round(n) : "?";
4682
5032
  log(`sleds: ${project} sized at $${b.lowUsd.toFixed(2)}–${b.highUsd.toFixed(2)} · ${min(b.lowMinutes)}–${min(b.highMinutes)} min`);
5033
+ if (typeof b.lowMinutes === "number" && typeof b.highMinutes === "number") {
5034
+ try {
5035
+ writeSeatState(o.home, {
5036
+ ...readSeatState(o.home),
5037
+ estimate: {
5038
+ project,
5039
+ lowUsd: b.lowUsd,
5040
+ highUsd: b.highUsd,
5041
+ lowMin: b.lowMinutes,
5042
+ highMin: b.highMinutes,
5043
+ at: (o.now ?? Date.now)()
5044
+ }
5045
+ });
5046
+ } catch {}
5047
+ }
4683
5048
  }
4684
5049
  } catch {}
4685
5050
  return 0;
@@ -4818,6 +5183,7 @@ var status = async (o) => {
4818
5183
  };
4819
5184
 
4820
5185
  // src/commands/statusline.ts
5186
+ init_config();
4821
5187
  init_meter();
4822
5188
  var statusline = async (o) => {
4823
5189
  const log = o.log ?? console.log;
@@ -4826,9 +5192,10 @@ var statusline = async (o) => {
4826
5192
  const window = seatWindowFromStdin(input, now);
4827
5193
  const session = seatSessionFromStdin(input, now);
4828
5194
  let posts = [];
5195
+ let events = [];
4829
5196
  try {
4830
5197
  if (window)
4831
- posts = rememberSeat(o, window, session, now, input);
5198
+ ({ posts, events } = rememberSeat(o, window, session, now, input));
4832
5199
  else {
4833
5200
  const st = readSeatState(o.home);
4834
5201
  const credits = creditsEvent(o, st, input, now);
@@ -4840,8 +5207,10 @@ var statusline = async (o) => {
4840
5207
  };
4841
5208
  writeSeatState(o.home, credits.event ? markWarned(next, [credits.event]) : next);
4842
5209
  }
4843
- if (credits.event)
4844
- posts = announce(o, [credits.event]);
5210
+ if (credits.event) {
5211
+ events = [credits.event];
5212
+ posts = announce(o, events);
5213
+ }
4845
5214
  }
4846
5215
  } catch {}
4847
5216
  const seatFragment = formatSeatFragment(window, now);
@@ -4858,9 +5227,35 @@ var statusline = async (o) => {
4858
5227
  line = formatMeterLine(null, now, seatFragment);
4859
5228
  }
4860
5229
  log(line);
5230
+ posts.push(speakToCockpit(o, window, events, now));
4861
5231
  await Promise.allSettled(posts);
4862
5232
  return 0;
4863
5233
  };
5234
+ var speakToCockpit = async (o, window, events, now) => {
5235
+ const cockpit = detectCockpit(o.env ?? process.env);
5236
+ if (!cockpit)
5237
+ return;
5238
+ try {
5239
+ const st = readSeatState(o.home);
5240
+ let feed;
5241
+ const src = meterSource(o.home);
5242
+ if (src && now - (st.cockpit?.feed?.at ?? 0) >= FEED_MIN_INTERVAL_MS) {
5243
+ const got = await fetchFeed({ ...src, email: readConfig(o.home).email, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs }, now);
5244
+ if (got)
5245
+ feed = got;
5246
+ }
5247
+ const r = renderCockpit(cockpit, window, events, st.cockpit, now, {
5248
+ estimate: st.estimate,
5249
+ ...feed ? { feed } : {}
5250
+ });
5251
+ const changed = r.argv.length > 0 || feed !== undefined;
5252
+ if (!changed)
5253
+ return;
5254
+ writeSeatState(o.home, { ...readSeatState(o.home), cockpit: r.state });
5255
+ if (r.argv.length)
5256
+ await runCockpit(cockpit, r.argv, { spawn: o.spawn });
5257
+ } catch {}
5258
+ };
4864
5259
  var rememberSeat = (o, window, session, now, input) => {
4865
5260
  const prior = readSeatState(o.home);
4866
5261
  let state = { ...prior, window, ...session ? { session } : {} };
@@ -4879,7 +5274,7 @@ var rememberSeat = (o, window, session, now, input) => {
4879
5274
  state = markWarned(state, events);
4880
5275
  posts.push(...announce(o, events));
4881
5276
  writeSeatState(o.home, state);
4882
- return posts;
5277
+ return { posts, events };
4883
5278
  };
4884
5279
  var creditsEvent = (o, prior, input, now) => {
4885
5280
  const path = transcriptPathFromStdin(input);
@@ -5462,7 +5857,7 @@ var main = async (argv) => {
5462
5857
  case "version":
5463
5858
  case "--version":
5464
5859
  case "-v":
5465
- console.log("sleds 0.1.19");
5860
+ console.log("sleds 0.1.20");
5466
5861
  return 0;
5467
5862
  default:
5468
5863
  console.error(`unknown command: ${cmd}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleds/cli",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "description": "SLEDS CLI \u2014 the cost and limits of AI agent work, decided at the wall. Point Claude Code at a SLEDS proxy; estimate before you run; hold, switch model, or run overnight when you hit a limit.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",