@joshski/dust 0.1.63 → 0.1.64

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/dust.js +302 -86
  2. package/package.json +1 -1
package/dist/dust.js CHANGED
@@ -3,14 +3,14 @@ import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // lib/cli/run.ts
6
- import { existsSync, statSync as statSync2 } from "node:fs";
6
+ import { existsSync, statSync as statSync3 } from "node:fs";
7
7
  import {
8
- chmod as chmod2,
9
- mkdir as mkdir2,
10
- readdir as readdir2,
11
- readFile as readFile2,
8
+ chmod as chmod3,
9
+ mkdir as mkdir3,
10
+ readdir as readdir3,
11
+ readFile as readFile3,
12
12
  rename,
13
- writeFile as writeFile2
13
+ writeFile as writeFile3
14
14
  } from "node:fs/promises";
15
15
 
16
16
  // lib/git/file-sorter.ts
@@ -275,7 +275,7 @@ async function loadSettings(cwd, fileSystem) {
275
275
  }
276
276
 
277
277
  // lib/version.ts
278
- var DUST_VERSION = "0.1.63";
278
+ var DUST_VERSION = "0.1.64";
279
279
 
280
280
  // lib/cli/dedent.ts
281
281
  function dedent(strings, ...values) {
@@ -1034,10 +1034,9 @@ async function audit(dependencies) {
1034
1034
  }
1035
1035
 
1036
1036
  // lib/cli/commands/bucket.ts
1037
- import { spawn as nodeSpawn3 } from "node:child_process";
1037
+ import { spawn as nodeSpawn4 } from "node:child_process";
1038
1038
  import { accessSync, statSync } from "node:fs";
1039
1039
  import { chmod, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
1040
- import { createServer as httpCreateServer } from "node:http";
1041
1040
  import { homedir } from "node:os";
1042
1041
 
1043
1042
  // lib/bucket/auth.ts
@@ -1156,6 +1155,40 @@ async function authenticate(authDeps) {
1156
1155
  });
1157
1156
  }
1158
1157
 
1158
+ // lib/bucket/auth-server.ts
1159
+ import { spawn as nodeSpawn } from "node:child_process";
1160
+ import { createServer as httpCreateServer } from "node:http";
1161
+ function createLocalServer(handler) {
1162
+ let resolvedPort = 0;
1163
+ const server = httpCreateServer(async (nodeRequest, nodeResponse) => {
1164
+ const url = new URL(nodeRequest.url ?? "/", `http://localhost:${resolvedPort}`);
1165
+ const request = new Request(url.toString(), {
1166
+ method: nodeRequest.method ?? "GET"
1167
+ });
1168
+ const response = handler(request);
1169
+ const body = await response.text();
1170
+ nodeResponse.writeHead(response.status, {
1171
+ "Content-Type": response.headers.get("content-type") ?? "text/plain"
1172
+ });
1173
+ nodeResponse.end(body);
1174
+ });
1175
+ server.listen(0, () => {
1176
+ const addr2 = server.address();
1177
+ if (addr2 && typeof addr2 === "object") {
1178
+ resolvedPort = addr2.port;
1179
+ }
1180
+ });
1181
+ const addr = server.address();
1182
+ if (addr && typeof addr === "object") {
1183
+ resolvedPort = addr.port;
1184
+ }
1185
+ return { port: resolvedPort, stop: () => server.close() };
1186
+ }
1187
+ function openBrowser(url) {
1188
+ const cmd = process.platform === "darwin" ? "open" : "xdg-open";
1189
+ nodeSpawn(cmd, [url], { stdio: "ignore", detached: true }).unref();
1190
+ }
1191
+
1159
1192
  // lib/bucket/events.ts
1160
1193
  var WS_OPEN = 1;
1161
1194
  function formatBucketEvent(event) {
@@ -1216,10 +1249,10 @@ function getReposDir(env, homeDir) {
1216
1249
  import { dirname as dirname2 } from "node:path";
1217
1250
 
1218
1251
  // lib/claude/spawn-claude-code.ts
1219
- import { spawn as nodeSpawn } from "node:child_process";
1252
+ import { spawn as nodeSpawn2 } from "node:child_process";
1220
1253
  import { createInterface as nodeCreateInterface } from "node:readline";
1221
1254
  var defaultDependencies = {
1222
- spawn: nodeSpawn,
1255
+ spawn: nodeSpawn2,
1223
1256
  createInterface: nodeCreateInterface
1224
1257
  };
1225
1258
  async function* spawnClaudeCode(prompt, options = {}, dependencies = defaultDependencies) {
@@ -1827,7 +1860,7 @@ function formatAgentEvent(event) {
1827
1860
  }
1828
1861
 
1829
1862
  // lib/cli/commands/loop.ts
1830
- import { spawn as nodeSpawn2 } from "node:child_process";
1863
+ import { spawn as nodeSpawn3 } from "node:child_process";
1831
1864
  import os from "node:os";
1832
1865
 
1833
1866
  // lib/artifacts/workflow-tasks.ts
@@ -2011,19 +2044,21 @@ function formatLoopEvent(event) {
2011
2044
  return `\uD83C\uDFC1 Reached max iterations (${event.maxIterations}). Exiting.`;
2012
2045
  }
2013
2046
  }
2014
- async function defaultPostEvent(url, payload) {
2015
- await fetch(url, {
2016
- method: "POST",
2017
- headers: { "Content-Type": "application/json" },
2018
- body: JSON.stringify(payload)
2019
- });
2047
+ function createPostEvent(fetchFn) {
2048
+ return async (url, payload) => {
2049
+ await fetchFn(url, {
2050
+ method: "POST",
2051
+ headers: { "Content-Type": "application/json" },
2052
+ body: JSON.stringify(payload)
2053
+ });
2054
+ };
2020
2055
  }
2021
2056
  function createDefaultDependencies() {
2022
2057
  return {
2023
- spawn: nodeSpawn2,
2058
+ spawn: nodeSpawn3,
2024
2059
  run,
2025
2060
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
2026
- postEvent: defaultPostEvent
2061
+ postEvent: createPostEvent(fetch)
2027
2062
  };
2028
2063
  }
2029
2064
  function createWireEventSender(eventsUrl, sessionId, postEvent, onError, getAgentSessionId, repository = "") {
@@ -2091,7 +2126,20 @@ async function runOneIteration(dependencies, loopDependencies, onLoopEvent, onAg
2091
2126
  const { context } = dependencies;
2092
2127
  const { spawn, run: run2 } = loopDependencies;
2093
2128
  const agentName = loopDependencies.agentType === "codex" ? "Codex" : "Claude";
2094
- const { onRawEvent, hooksInstalled = false, signal, logger = log } = options;
2129
+ const {
2130
+ onRawEvent,
2131
+ hooksInstalled = false,
2132
+ signal,
2133
+ logger = log,
2134
+ repositoryId
2135
+ } = options;
2136
+ const baseEnv = {
2137
+ DUST_UNATTENDED: "1",
2138
+ DUST_SKIP_AGENT: "1"
2139
+ };
2140
+ if (repositoryId) {
2141
+ baseEnv.DUST_REPOSITORY_ID = repositoryId;
2142
+ }
2095
2143
  log("syncing with remote");
2096
2144
  onLoopEvent({ type: "loop.syncing" });
2097
2145
  const pullResult = await gitPull(context.cwd, spawn);
@@ -2126,7 +2174,7 @@ Make sure the repository is in a clean state and synced with remote before finis
2126
2174
  spawnOptions: {
2127
2175
  cwd: context.cwd,
2128
2176
  dangerouslySkipPermissions: true,
2129
- env: { DUST_UNATTENDED: "1", DUST_SKIP_AGENT: "1" },
2177
+ env: baseEnv,
2130
2178
  signal
2131
2179
  },
2132
2180
  onRawEvent
@@ -2182,7 +2230,7 @@ ${instructions}`;
2182
2230
  spawnOptions: {
2183
2231
  cwd: context.cwd,
2184
2232
  dangerouslySkipPermissions: true,
2185
- env: { DUST_UNATTENDED: "1", DUST_SKIP_AGENT: "1" },
2233
+ env: baseEnv,
2186
2234
  signal
2187
2235
  },
2188
2236
  onRawEvent
@@ -2279,6 +2327,45 @@ async function loopClaude(dependencies, loopDependencies = createDefaultDependen
2279
2327
  // lib/bucket/repository-loop.ts
2280
2328
  var log2 = createLogger("dust:bucket:repository-loop");
2281
2329
  var FALLBACK_TIMEOUT_MS = 300000;
2330
+ function createLogCallbacks(logBuffer) {
2331
+ return {
2332
+ stdout: (msg) => appendLogLine(logBuffer, createLogLine(msg, "stdout")),
2333
+ stderr: (msg) => appendLogLine(logBuffer, createLogLine(msg, "stderr"))
2334
+ };
2335
+ }
2336
+ function flushAndLogMultiLine(partialLine, text, logBuffer) {
2337
+ if (partialLine) {
2338
+ appendLogLine(logBuffer, createLogLine(partialLine, "stdout"));
2339
+ }
2340
+ for (const segment of text.split(`
2341
+ `)) {
2342
+ appendLogLine(logBuffer, createLogLine(segment, "stdout"));
2343
+ }
2344
+ return "";
2345
+ }
2346
+ function buildEventMessage(parameters) {
2347
+ const msg = {
2348
+ sequence: parameters.sequence,
2349
+ timestamp: new Date().toISOString(),
2350
+ sessionId: parameters.sessionId,
2351
+ repository: parameters.repository,
2352
+ event: parameters.event
2353
+ };
2354
+ if (parameters.agentSessionId) {
2355
+ msg.agentSessionId = parameters.agentSessionId;
2356
+ }
2357
+ return msg;
2358
+ }
2359
+ function createWakeUpHandler(repoState, resolve) {
2360
+ const handler = () => {
2361
+ if (repoState.wakeUp !== handler) {
2362
+ return;
2363
+ }
2364
+ repoState.wakeUp = undefined;
2365
+ resolve();
2366
+ };
2367
+ return handler;
2368
+ }
2282
2369
  function createNoOpGlobScanner() {
2283
2370
  return {
2284
2371
  scan: async function* () {}
@@ -2288,12 +2375,13 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2288
2375
  const { spawn, run: run2, fileSystem, sleep } = repoDeps;
2289
2376
  const repoName = repoState.repository.name;
2290
2377
  const settings = await loadSettings(repoState.path, fileSystem);
2378
+ const logCallbacks = createLogCallbacks(repoState.logBuffer);
2291
2379
  const commandDeps = {
2292
2380
  arguments: [],
2293
2381
  context: {
2294
2382
  cwd: repoState.path,
2295
- stdout: (msg) => appendLogLine(repoState.logBuffer, createLogLine(msg, "stdout")),
2296
- stderr: (msg) => appendLogLine(repoState.logBuffer, createLogLine(msg, "stderr"))
2383
+ stdout: (msg) => logCallbacks.stdout(msg),
2384
+ stderr: (msg) => logCallbacks.stderr(msg)
2297
2385
  },
2298
2386
  fileSystem,
2299
2387
  globScanner: createNoOpGlobScanner(),
@@ -2313,14 +2401,7 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2313
2401
  partialLine = lines[lines.length - 1];
2314
2402
  },
2315
2403
  line: (text) => {
2316
- if (partialLine) {
2317
- appendLogLine(repoState.logBuffer, createLogLine(partialLine, "stdout"));
2318
- partialLine = "";
2319
- }
2320
- for (const segment of text.split(`
2321
- `)) {
2322
- appendLogLine(repoState.logBuffer, createLogLine(segment, "stdout"));
2323
- }
2404
+ partialLine = flushAndLogMultiLine(partialLine, text, repoState.logBuffer);
2324
2405
  }
2325
2406
  })
2326
2407
  };
@@ -2351,17 +2432,13 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2351
2432
  }
2352
2433
  if (sendEvent && sessionId) {
2353
2434
  sequence++;
2354
- const msg = {
2435
+ sendEvent(buildEventMessage({
2355
2436
  sequence,
2356
- timestamp: new Date().toISOString(),
2357
2437
  sessionId,
2358
2438
  repository: repoName,
2359
- event
2360
- };
2361
- if (agentSessionId) {
2362
- msg.agentSessionId = agentSessionId;
2363
- }
2364
- sendEvent(msg);
2439
+ event,
2440
+ agentSessionId
2441
+ }));
2365
2442
  }
2366
2443
  };
2367
2444
  const hooksInstalled = await manageGitHooks(commandDeps);
@@ -2379,6 +2456,7 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2379
2456
  result = await runOneIteration(commandDeps, loopDeps, onLoopEvent, onAgentEvent, {
2380
2457
  hooksInstalled,
2381
2458
  signal: abortController.signal,
2459
+ repositoryId: repoState.repository.id,
2382
2460
  onRawEvent: (rawEvent) => {
2383
2461
  onAgentEvent(rawEventToAgentEvent(rawEvent));
2384
2462
  }
@@ -2404,13 +2482,7 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2404
2482
  log2(`${repoName}: no tasks available, waiting`);
2405
2483
  logLine("Waiting for tasks...");
2406
2484
  await new Promise((resolve) => {
2407
- const wakeUpForThisWait = () => {
2408
- if (repoState.wakeUp !== wakeUpForThisWait) {
2409
- return;
2410
- }
2411
- repoState.wakeUp = undefined;
2412
- resolve();
2413
- };
2485
+ const wakeUpForThisWait = createWakeUpHandler(repoState, resolve);
2414
2486
  repoState.wakeUp = wakeUpForThisWait;
2415
2487
  sleep(FALLBACK_TIMEOUT_MS).then(() => {
2416
2488
  if (repoState.wakeUp === wakeUpForThisWait) {
@@ -2456,6 +2528,9 @@ function parseRepository(data) {
2456
2528
  if (typeof repositoryData.url === "string") {
2457
2529
  repo.url = repositoryData.url;
2458
2530
  }
2531
+ if (typeof repositoryData.id === "string") {
2532
+ repo.id = repositoryData.id;
2533
+ }
2459
2534
  return repo;
2460
2535
  }
2461
2536
  }
@@ -3019,36 +3094,6 @@ function defaultGetTerminalSize() {
3019
3094
  function defaultWriteStdout(data) {
3020
3095
  process.stdout.write(data);
3021
3096
  }
3022
- function defaultCreateServer(handler) {
3023
- let resolvedPort = 0;
3024
- const server = httpCreateServer(async (nodeRequest, nodeResponse) => {
3025
- const url = new URL(nodeRequest.url ?? "/", `http://localhost:${resolvedPort}`);
3026
- const request = new Request(url.toString(), {
3027
- method: nodeRequest.method ?? "GET"
3028
- });
3029
- const response = handler(request);
3030
- const body = await response.text();
3031
- nodeResponse.writeHead(response.status, {
3032
- "Content-Type": response.headers.get("content-type") ?? "text/plain"
3033
- });
3034
- nodeResponse.end(body);
3035
- });
3036
- server.listen(0, () => {
3037
- const addr2 = server.address();
3038
- if (addr2 && typeof addr2 === "object") {
3039
- resolvedPort = addr2.port;
3040
- }
3041
- });
3042
- const addr = server.address();
3043
- if (addr && typeof addr === "object") {
3044
- resolvedPort = addr.port;
3045
- }
3046
- return { port: resolvedPort, stop: () => server.close() };
3047
- }
3048
- function defaultOpenBrowser(url) {
3049
- const cmd = process.platform === "darwin" ? "open" : "xdg-open";
3050
- nodeSpawn3(cmd, [url], { stdio: "ignore", detached: true }).unref();
3051
- }
3052
3097
  function createAuthFileSystem(dependencies) {
3053
3098
  return {
3054
3099
  exists: (path) => {
@@ -3087,7 +3132,7 @@ function createDefaultBucketDependencies() {
3087
3132
  rename: (oldPath, newPath) => import("node:fs/promises").then((mod) => mod.rename(oldPath, newPath))
3088
3133
  });
3089
3134
  return {
3090
- spawn: nodeSpawn3,
3135
+ spawn: nodeSpawn4,
3091
3136
  createWebSocket: defaultCreateWebSocket,
3092
3137
  setupKeypress: defaultSetupKeypress,
3093
3138
  setupSignals: defaultSetupSignals,
@@ -3098,8 +3143,8 @@ function createDefaultBucketDependencies() {
3098
3143
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
3099
3144
  getReposDir: () => getReposDir(process.env, homedir()),
3100
3145
  auth: {
3101
- createServer: defaultCreateServer,
3102
- openBrowser: defaultOpenBrowser,
3146
+ createServer: createLocalServer,
3147
+ openBrowser,
3103
3148
  getHomeDir: () => homedir(),
3104
3149
  fileSystem: authFileSystem
3105
3150
  }
@@ -3464,6 +3509,176 @@ async function bucket(dependencies, bucketDeps = createDefaultBucketDependencies
3464
3509
  return { exitCode: 0 };
3465
3510
  }
3466
3511
 
3512
+ // lib/cli/commands/bucket-asset-upload.ts
3513
+ import { accessSync as accessSync2, statSync as statSync2 } from "node:fs";
3514
+ import { chmod as chmod2, mkdir as mkdir2, readdir as readdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
3515
+ import { homedir as homedir2 } from "node:os";
3516
+ import { extname } from "node:path";
3517
+ var MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
3518
+ var ALLOWED_EXTENSIONS = new Set([
3519
+ ".png",
3520
+ ".jpg",
3521
+ ".jpeg",
3522
+ ".gif",
3523
+ ".webp",
3524
+ ".svg",
3525
+ ".pdf",
3526
+ ".txt",
3527
+ ".json",
3528
+ ".csv",
3529
+ ".md",
3530
+ ".html",
3531
+ ".xml"
3532
+ ]);
3533
+ var MIME_TYPES = {
3534
+ ".png": "image/png",
3535
+ ".jpg": "image/jpeg",
3536
+ ".jpeg": "image/jpeg",
3537
+ ".gif": "image/gif",
3538
+ ".webp": "image/webp",
3539
+ ".svg": "image/svg+xml",
3540
+ ".pdf": "application/pdf",
3541
+ ".txt": "text/plain",
3542
+ ".json": "application/json",
3543
+ ".csv": "text/csv",
3544
+ ".md": "text/markdown",
3545
+ ".html": "text/html",
3546
+ ".xml": "application/xml"
3547
+ };
3548
+ function createDefaultUploadDependencies() {
3549
+ const authFileSystemDeps = {
3550
+ accessSync: accessSync2,
3551
+ statSync: statSync2,
3552
+ readFile: readFile2,
3553
+ writeFile: writeFile2,
3554
+ mkdir: mkdir2,
3555
+ readdir: readdir2,
3556
+ chmod: chmod2,
3557
+ rename: (oldPath, newPath) => import("node:fs/promises").then((mod) => mod.rename(oldPath, newPath))
3558
+ };
3559
+ const authFileSystem = createAuthFileSystem(authFileSystemDeps);
3560
+ return {
3561
+ auth: {
3562
+ createServer: createLocalServer,
3563
+ openBrowser,
3564
+ getHomeDir: () => homedir2(),
3565
+ fileSystem: authFileSystem
3566
+ },
3567
+ readFileBytes: async (path) => {
3568
+ const buffer = await Bun.file(path).arrayBuffer();
3569
+ return new Uint8Array(buffer);
3570
+ },
3571
+ getFileSize: async (path) => {
3572
+ const file = Bun.file(path);
3573
+ return file.size;
3574
+ },
3575
+ fileExists: async (path) => {
3576
+ const file = Bun.file(path);
3577
+ return file.exists();
3578
+ },
3579
+ uploadFile: async (url, token, fileBytes, contentType) => {
3580
+ const response = await fetch(url, {
3581
+ method: "POST",
3582
+ headers: {
3583
+ Authorization: `Bearer ${token}`,
3584
+ "Content-Type": contentType
3585
+ },
3586
+ body: new Blob([fileBytes])
3587
+ });
3588
+ if (!response.ok) {
3589
+ const text = await response.text();
3590
+ throw new Error(`Upload failed (${response.status}): ${text || response.statusText}`);
3591
+ }
3592
+ const body = await response.json();
3593
+ if (typeof body.url !== "string") {
3594
+ throw new Error("Server response missing URL");
3595
+ }
3596
+ return { url: body.url };
3597
+ }
3598
+ };
3599
+ }
3600
+ async function resolveToken2(authDeps, context) {
3601
+ const envToken = process.env.DUST_BUCKET_TOKEN;
3602
+ if (envToken) {
3603
+ return envToken;
3604
+ }
3605
+ const stored = await loadStoredToken(authDeps.fileSystem, authDeps.getHomeDir());
3606
+ if (stored) {
3607
+ return stored;
3608
+ }
3609
+ context.stdout("Opening browser to authenticate with dustbucket...");
3610
+ try {
3611
+ const token = await authenticate(authDeps);
3612
+ await storeToken(authDeps.fileSystem, authDeps.getHomeDir(), token);
3613
+ context.stdout("Authenticated successfully");
3614
+ return token;
3615
+ } catch (error) {
3616
+ context.stderr(`Authentication failed: ${error.message}`);
3617
+ return null;
3618
+ }
3619
+ }
3620
+ function getContentType(filePath) {
3621
+ const ext = extname(filePath).toLowerCase();
3622
+ return MIME_TYPES[ext] || "application/octet-stream";
3623
+ }
3624
+ function isAllowedExtension(filePath) {
3625
+ const ext = extname(filePath).toLowerCase();
3626
+ return ALLOWED_EXTENSIONS.has(ext);
3627
+ }
3628
+ function formatFileSize(bytes) {
3629
+ if (bytes < 1024)
3630
+ return `${bytes} bytes`;
3631
+ if (bytes < 1048576)
3632
+ return `${(bytes / 1024).toFixed(1)} KB`;
3633
+ return `${(bytes / 1048576).toFixed(1)} MB`;
3634
+ }
3635
+ async function bucketAssetUpload(dependencies, uploadDeps = createDefaultUploadDependencies(), env = process.env) {
3636
+ const { context } = dependencies;
3637
+ const filePath = dependencies.arguments[0];
3638
+ if (!filePath) {
3639
+ context.stderr("Usage: dust bucket asset upload <file-path>");
3640
+ return { exitCode: 1 };
3641
+ }
3642
+ const repositoryId = env.DUST_REPOSITORY_ID;
3643
+ if (!repositoryId) {
3644
+ context.stderr("Error: DUST_REPOSITORY_ID environment variable is not set.");
3645
+ context.stderr("This command must be run within a repository context (via `dust bucket`).");
3646
+ return { exitCode: 1 };
3647
+ }
3648
+ const exists = await uploadDeps.fileExists(filePath);
3649
+ if (!exists) {
3650
+ context.stderr(`File not found: ${filePath}`);
3651
+ return { exitCode: 1 };
3652
+ }
3653
+ if (!isAllowedExtension(filePath)) {
3654
+ const ext = extname(filePath).toLowerCase() || "(no extension)";
3655
+ const allowed = Array.from(ALLOWED_EXTENSIONS).join(", ");
3656
+ context.stderr(`Unsupported file type: ${ext}`);
3657
+ context.stderr(`Allowed types: ${allowed}`);
3658
+ return { exitCode: 1 };
3659
+ }
3660
+ const fileSize = await uploadDeps.getFileSize(filePath);
3661
+ if (fileSize > MAX_FILE_SIZE_BYTES) {
3662
+ context.stderr(`File too large: ${formatFileSize(fileSize)} (max ${formatFileSize(MAX_FILE_SIZE_BYTES)})`);
3663
+ return { exitCode: 1 };
3664
+ }
3665
+ const token = await resolveToken2(uploadDeps.auth, context);
3666
+ if (!token) {
3667
+ return { exitCode: 1 };
3668
+ }
3669
+ const fileBytes = await uploadDeps.readFileBytes(filePath);
3670
+ const contentType = getContentType(filePath);
3671
+ const uploadUrl = `${getDustbucketHost()}/api/assets?repositoryId=${encodeURIComponent(repositoryId)}`;
3672
+ try {
3673
+ const result = await uploadDeps.uploadFile(uploadUrl, token, fileBytes, contentType);
3674
+ context.stdout(result.url);
3675
+ return { exitCode: 0 };
3676
+ } catch (error) {
3677
+ context.stderr(`Upload failed: ${error.message}`);
3678
+ return { exitCode: 1 };
3679
+ }
3680
+ }
3681
+
3467
3682
  // lib/cli/process-runner.ts
3468
3683
  import { spawn } from "node:child_process";
3469
3684
  function createShellRunner(spawnFn) {
@@ -4710,10 +4925,10 @@ async function list(dependencies) {
4710
4925
  }
4711
4926
 
4712
4927
  // lib/codex/spawn-codex.ts
4713
- import { spawn as nodeSpawn4 } from "node:child_process";
4928
+ import { spawn as nodeSpawn5 } from "node:child_process";
4714
4929
  import { createInterface as nodeCreateInterface2 } from "node:readline";
4715
4930
  var defaultDependencies2 = {
4716
- spawn: nodeSpawn4,
4931
+ spawn: nodeSpawn5,
4717
4932
  createInterface: nodeCreateInterface2
4718
4933
  };
4719
4934
  async function* spawnCodex(prompt, options = {}, dependencies = defaultDependencies2) {
@@ -5232,6 +5447,7 @@ var commandRegistry = {
5232
5447
  agent,
5233
5448
  audit,
5234
5449
  bucket,
5450
+ "bucket asset upload": bucketAssetUpload,
5235
5451
  focus,
5236
5452
  "new task": newTask,
5237
5453
  "new principle": newPrinciple,
@@ -5320,10 +5536,10 @@ function createFileSystem(primitives) {
5320
5536
  rename: (oldPath, newPath) => primitives.rename(oldPath, newPath)
5321
5537
  };
5322
5538
  }
5323
- function createGlobScanner(readdir2) {
5539
+ function createGlobScanner(readdir3) {
5324
5540
  return {
5325
5541
  scan: async function* (dir) {
5326
- for (const entry of await readdir2(dir, { recursive: true })) {
5542
+ for (const entry of await readdir3(dir, { recursive: true })) {
5327
5543
  if (entry.endsWith(".md"))
5328
5544
  yield entry;
5329
5545
  }
@@ -5350,7 +5566,7 @@ async function wireEntry(fsPrimitives, processPrimitives, consolePrimitives) {
5350
5566
  }
5351
5567
 
5352
5568
  // lib/cli/run.ts
5353
- await wireEntry({ existsSync, statSync: statSync2, readFile: readFile2, writeFile: writeFile2, mkdir: mkdir2, readdir: readdir2, chmod: chmod2, rename }, {
5569
+ await wireEntry({ existsSync, statSync: statSync3, readFile: readFile3, writeFile: writeFile3, mkdir: mkdir3, readdir: readdir3, chmod: chmod3, rename }, {
5354
5570
  argv: process.argv,
5355
5571
  cwd: () => process.cwd(),
5356
5572
  exit: (code) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshski/dust",
3
- "version": "0.1.63",
3
+ "version": "0.1.64",
4
4
  "description": "Flow state for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {