@drisp/cli 0.5.4 → 0.5.6

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.
@@ -2241,7 +2241,7 @@ var cachedVersion = null;
2241
2241
  function readVersion() {
2242
2242
  if (cachedVersion !== null) return cachedVersion;
2243
2243
  try {
2244
- const injected = "0.5.4";
2244
+ const injected = "0.5.6";
2245
2245
  if (typeof injected === "string" && injected.length > 0) {
2246
2246
  cachedVersion = injected;
2247
2247
  return cachedVersion;
@@ -2876,17 +2876,18 @@ var InboundQueue = class {
2876
2876
  const row = this.db.prepare("SELECT COUNT(*) as n FROM inbound_queue").get();
2877
2877
  return row.n;
2878
2878
  }
2879
- enqueue(inbound) {
2879
+ enqueue(inbound, key) {
2880
2880
  if (this.size() >= this.maxEntries) {
2881
2881
  return { kind: "rejected", reason: "queue_full" };
2882
2882
  }
2883
2883
  const stmt = this.db.prepare(
2884
2884
  `INSERT INTO inbound_queue
2885
- (channel_id, account_id, idempotency_key, payload_json, created_at)
2886
- VALUES (?, ?, ?, ?, ?)
2885
+ (attachment_id, channel_id, account_id, idempotency_key, payload_json, created_at)
2886
+ VALUES (?, ?, ?, ?, ?, ?)
2887
2887
  ON CONFLICT(channel_id, account_id, idempotency_key) DO NOTHING`
2888
2888
  );
2889
2889
  const result = stmt.run(
2890
+ key ?? null,
2890
2891
  inbound.location.channelId,
2891
2892
  inbound.location.accountId,
2892
2893
  inbound.idempotencyKey,
@@ -2898,12 +2899,17 @@ var InboundQueue = class {
2898
2899
  }
2899
2900
  return { kind: "queued", id: Number(result.lastInsertRowid) };
2900
2901
  }
2901
- /** Atomically read and remove all parked entries in FIFO order. */
2902
- drain() {
2902
+ /** Atomically read and remove parked entries for one attachment slot. */
2903
+ drain(key) {
2903
2904
  return this.db.transaction(() => {
2904
- const rows = this.db.prepare("SELECT id, payload_json FROM inbound_queue ORDER BY id ASC").all();
2905
+ const rows = this.db.prepare(
2906
+ `SELECT id, payload_json
2907
+ FROM inbound_queue
2908
+ WHERE attachment_id IS ?
2909
+ ORDER BY id ASC`
2910
+ ).all(key ?? null);
2905
2911
  if (rows.length > 0) {
2906
- this.db.prepare("DELETE FROM inbound_queue").run();
2912
+ this.db.prepare("DELETE FROM inbound_queue WHERE attachment_id IS ?").run(key ?? null);
2907
2913
  }
2908
2914
  return rows.map((r) => ({
2909
2915
  id: r.id,
@@ -3022,7 +3028,7 @@ var DispatchPipeline = class {
3022
3028
  const key = options.attachmentId;
3023
3029
  const current = this.bindingStore.getCurrentByAttachment(key);
3024
3030
  if (!current || !this.bindingStore.hasActiveBindingForAttachment(key)) {
3025
- const result = this.inboundQueue.enqueue(inbound);
3031
+ const result = this.inboundQueue.enqueue(inbound, key);
3026
3032
  if (result.kind === "queued") {
3027
3033
  this.log?.(
3028
3034
  "info",
@@ -3207,9 +3213,9 @@ var DispatchPipeline = class {
3207
3213
  }
3208
3214
  drainPending(key) {
3209
3215
  const current = this.bindingStore.getCurrentByAttachment(key);
3210
- if (!current || !this.bindingStore.hasActiveBinding(current.runtimeId))
3216
+ if (!current || !this.bindingStore.hasActiveBindingForAttachment(key))
3211
3217
  return;
3212
- const parked = this.inboundQueue.drain();
3218
+ const parked = this.inboundQueue.drain(key);
3213
3219
  let dispatched = 0;
3214
3220
  let dropped = 0;
3215
3221
  for (const { inbound } of parked) {
@@ -3548,7 +3554,7 @@ function questionFingerprint(req) {
3548
3554
  import fs3 from "fs";
3549
3555
  import path3 from "path";
3550
3556
  import Database from "better-sqlite3";
3551
- var GATEWAY_STATE_VERSION = 1;
3557
+ var GATEWAY_STATE_VERSION = 2;
3552
3558
  function openGatewayState(dbPath) {
3553
3559
  if (dbPath !== ":memory:") {
3554
3560
  fs3.mkdirSync(path3.dirname(dbPath), { recursive: true, mode: 448 });
@@ -3576,6 +3582,7 @@ function initGatewayStateSchema(db) {
3576
3582
  -- same provider message from being parked twice if an adapter retries.
3577
3583
  CREATE TABLE IF NOT EXISTS inbound_queue (
3578
3584
  id INTEGER PRIMARY KEY AUTOINCREMENT,
3585
+ attachment_id TEXT,
3579
3586
  channel_id TEXT NOT NULL,
3580
3587
  account_id TEXT NOT NULL,
3581
3588
  idempotency_key TEXT NOT NULL,
@@ -3611,6 +3618,16 @@ function initGatewayStateSchema(db) {
3611
3618
  db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(
3612
3619
  GATEWAY_STATE_VERSION
3613
3620
  );
3621
+ return;
3622
+ }
3623
+ const inboundColumns = db.prepare("PRAGMA table_info(inbound_queue)").all();
3624
+ if (!inboundColumns.some((column) => column.name === "attachment_id")) {
3625
+ db.prepare("ALTER TABLE inbound_queue ADD COLUMN attachment_id TEXT").run();
3626
+ }
3627
+ if (existing.version < GATEWAY_STATE_VERSION) {
3628
+ db.prepare("UPDATE schema_version SET version = ?").run(
3629
+ GATEWAY_STATE_VERSION
3630
+ );
3614
3631
  }
3615
3632
  }
3616
3633
 
@@ -12704,7 +12704,7 @@ function mergeRunSpecEnvIntoWorkflow(workflow, env) {
12704
12704
  async function executeRemoteAssignment({
12705
12705
  frame,
12706
12706
  client,
12707
- projectDir: fallbackProjectDir = process.cwd(),
12707
+ projectDir,
12708
12708
  log = () => {
12709
12709
  },
12710
12710
  runExecFn = runExec,
@@ -12746,7 +12746,6 @@ async function executeRemoteAssignment({
12746
12746
  send("error", { message: "remote assignment missing prompt" });
12747
12747
  return;
12748
12748
  }
12749
- const projectDir = spec.projectDir ?? fallbackProjectDir;
12750
12749
  let runtimeConfig;
12751
12750
  try {
12752
12751
  const workflowOverride = ensureRemoteWorkflowInstalled({
@@ -13248,7 +13247,7 @@ function createDashboardPairedExecution(options) {
13248
13247
  entry.record.status = "cancelled";
13249
13248
  entry.controller.abort();
13250
13249
  }
13251
- function handleAssignment(frame) {
13250
+ function handleAssignment(frame, input = {}) {
13252
13251
  if (active.has(frame.runId)) {
13253
13252
  const rejection = {
13254
13253
  reason: "duplicate",
@@ -13279,7 +13278,7 @@ function createDashboardPairedExecution(options) {
13279
13278
  const promise = executor({
13280
13279
  frame,
13281
13280
  client,
13282
- projectDir,
13281
+ projectDir: input.projectDir ?? projectDir,
13283
13282
  log,
13284
13283
  abortSignal: controller.signal,
13285
13284
  decisionInbox
@@ -13323,8 +13322,8 @@ function createDashboardPairedExecution(options) {
13323
13322
  }
13324
13323
  return false;
13325
13324
  },
13326
- admitAssignment(frame) {
13327
- return handleAssignment(frame);
13325
+ admitAssignment(frame, input) {
13326
+ return handleAssignment(frame, input);
13328
13327
  },
13329
13328
  rejectAssignment,
13330
13329
  snapshot() {
@@ -13352,10 +13351,125 @@ function createDashboardPairedExecution(options) {
13352
13351
  };
13353
13352
  }
13354
13353
 
13354
+ // src/app/dashboard/remoteWorkspaceResolver.ts
13355
+ import fs22 from "fs";
13356
+ import os13 from "os";
13357
+ import path20 from "path";
13358
+ function resolveRemoteWorkspace(frame, options = {}) {
13359
+ const spec = parseRemoteRunSpec(frame.runSpec);
13360
+ if (!spec) {
13361
+ return {
13362
+ kind: "rejected",
13363
+ rejection: {
13364
+ reason: "workspace_unresolved",
13365
+ message: "remote assignment missing prompt"
13366
+ }
13367
+ };
13368
+ }
13369
+ if (spec.projectDir) {
13370
+ return validateProjectDir(spec.projectDir, options.env);
13371
+ }
13372
+ const sessionId = spec.athenaSessionId ?? spec.sessionId;
13373
+ const runnerId = frame.runnerId ?? "legacy";
13374
+ const deploymentSlug = deploymentSlugFromUrl(options.dashboardUrl);
13375
+ const stateDir = daemonStatePaths(options.env).dir;
13376
+ const projectDir = sessionId ? path20.join(
13377
+ stateDir,
13378
+ "remote-workspaces",
13379
+ deploymentSlug,
13380
+ sanitizePathSegment(runnerId),
13381
+ "sessions",
13382
+ sanitizePathSegment(sessionId)
13383
+ ) : path20.join(
13384
+ stateDir,
13385
+ "remote-workspaces",
13386
+ deploymentSlug,
13387
+ sanitizePathSegment(runnerId),
13388
+ "runs",
13389
+ sanitizePathSegment(frame.runId)
13390
+ );
13391
+ try {
13392
+ fs22.mkdirSync(projectDir, { recursive: true, mode: 448 });
13393
+ if (process.platform !== "win32") {
13394
+ try {
13395
+ fs22.chmodSync(projectDir, 448);
13396
+ } catch {
13397
+ }
13398
+ }
13399
+ } catch (err) {
13400
+ return {
13401
+ kind: "rejected",
13402
+ rejection: {
13403
+ reason: "workspace_unresolved",
13404
+ message: `failed to create remote workspace: ${err instanceof Error ? err.message : String(err)}`
13405
+ }
13406
+ };
13407
+ }
13408
+ return validateProjectDir(projectDir, options.env);
13409
+ }
13410
+ function validateProjectDir(projectDir, env = process.env) {
13411
+ const resolved = path20.resolve(projectDir);
13412
+ if (!path20.isAbsolute(projectDir)) {
13413
+ return {
13414
+ kind: "rejected",
13415
+ rejection: {
13416
+ reason: "workspace_invalid",
13417
+ message: `remote workspace must be an absolute path: ${projectDir}`
13418
+ }
13419
+ };
13420
+ }
13421
+ const home = path20.resolve(env["HOME"] ?? os13.homedir());
13422
+ if (resolved === home) {
13423
+ return {
13424
+ kind: "rejected",
13425
+ rejection: {
13426
+ reason: "workspace_invalid",
13427
+ message: "remote workspace cannot be the user home directory"
13428
+ }
13429
+ };
13430
+ }
13431
+ let stat;
13432
+ try {
13433
+ stat = fs22.statSync(resolved);
13434
+ } catch {
13435
+ return {
13436
+ kind: "rejected",
13437
+ rejection: {
13438
+ reason: "workspace_invalid",
13439
+ message: `remote workspace does not exist: ${resolved}`
13440
+ }
13441
+ };
13442
+ }
13443
+ if (!stat.isDirectory()) {
13444
+ return {
13445
+ kind: "rejected",
13446
+ rejection: {
13447
+ reason: "workspace_invalid",
13448
+ message: `remote workspace is not a directory: ${resolved}`
13449
+ }
13450
+ };
13451
+ }
13452
+ return { kind: "resolved", projectDir: resolved };
13453
+ }
13454
+ function deploymentSlugFromUrl(dashboardUrl) {
13455
+ if (!dashboardUrl) return "unknown-dashboard";
13456
+ try {
13457
+ const url = new URL(dashboardUrl);
13458
+ return sanitizePathSegment(url.host);
13459
+ } catch {
13460
+ return sanitizePathSegment(dashboardUrl);
13461
+ }
13462
+ }
13463
+ function sanitizePathSegment(value) {
13464
+ const cleaned = value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "");
13465
+ return cleaned.length > 0 ? cleaned : "unknown";
13466
+ }
13467
+
13355
13468
  // src/app/dashboard/dashboardAssignmentIntake.ts
13356
13469
  function createDashboardAssignmentIntake(options) {
13357
13470
  const log = options.log ?? (() => {
13358
13471
  });
13472
+ const resolveWorkspace = options.resolveWorkspace ?? ((frame) => resolveRemoteWorkspace(frame));
13359
13473
  const pending = [];
13360
13474
  let ready = false;
13361
13475
  function handle(frame) {
@@ -13371,7 +13485,18 @@ function createDashboardAssignmentIntake(options) {
13371
13485
  });
13372
13486
  return;
13373
13487
  }
13374
- const outcome = options.execution.admitAssignment(frame);
13488
+ const workspace = resolveWorkspace(frame);
13489
+ if (workspace.kind === "rejected") {
13490
+ options.execution.rejectAssignment(frame.runId, workspace.rejection);
13491
+ options.client.sendAssignmentRejected({
13492
+ runId: frame.runId,
13493
+ ...workspace.rejection
13494
+ });
13495
+ return;
13496
+ }
13497
+ const outcome = options.execution.admitAssignment(frame, {
13498
+ projectDir: workspace.projectDir
13499
+ });
13375
13500
  if (outcome.kind === "accepted") {
13376
13501
  options.client.sendAssignmentAccepted(frame.runId);
13377
13502
  return;
@@ -13511,7 +13636,8 @@ async function runDashboardRuntimeDaemon(options = {}) {
13511
13636
  }
13512
13637
  },
13513
13638
  execution: pairedExecution,
13514
- log
13639
+ log,
13640
+ resolveWorkspace: (frame) => resolveRemoteWorkspace(frame, { dashboardUrl: currentDashboardUrl })
13515
13641
  });
13516
13642
  function nextReconnectDelay() {
13517
13643
  if (reconnectDelays.length === 0) return 0;
@@ -13659,9 +13785,9 @@ async function runDashboardRuntimeDaemon(options = {}) {
13659
13785
  next.close("attachment reconciliation failed");
13660
13786
  throw err;
13661
13787
  }
13662
- assignmentIntake.markReady();
13663
13788
  currentInstanceId = token.instanceId;
13664
13789
  currentDashboardUrl = config.dashboardUrl;
13790
+ assignmentIntake.markReady();
13665
13791
  reconnectAttempt = 0;
13666
13792
  scheduleRefresh(token.expiresInSec);
13667
13793
  pairedFeedPublisher.attachTransport(next);
@@ -13729,18 +13855,18 @@ async function runDashboardRuntimeDaemon(options = {}) {
13729
13855
  }
13730
13856
 
13731
13857
  // src/infra/daemon/pidLock.ts
13732
- import fs22 from "fs";
13858
+ import fs23 from "fs";
13733
13859
  function acquirePidLock(pidPath) {
13734
13860
  const ownPid = process.pid;
13735
13861
  for (let attempt = 0; attempt < 2; attempt += 1) {
13736
13862
  try {
13737
- const fd = fs22.openSync(pidPath, "wx", 384);
13863
+ const fd = fs23.openSync(pidPath, "wx", 384);
13738
13864
  try {
13739
- fs22.writeSync(fd, `${ownPid}
13865
+ fs23.writeSync(fd, `${ownPid}
13740
13866
  `);
13741
- fs22.fsyncSync(fd);
13867
+ fs23.fsyncSync(fd);
13742
13868
  } finally {
13743
- fs22.closeSync(fd);
13869
+ fs23.closeSync(fd);
13744
13870
  }
13745
13871
  return makeHandle(pidPath, ownPid);
13746
13872
  } catch (err) {
@@ -13754,7 +13880,7 @@ function acquirePidLock(pidPath) {
13754
13880
  }
13755
13881
  if (existing.state === "stale") {
13756
13882
  try {
13757
- fs22.unlinkSync(pidPath);
13883
+ fs23.unlinkSync(pidPath);
13758
13884
  } catch (err) {
13759
13885
  if (err.code !== "ENOENT") throw err;
13760
13886
  }
@@ -13768,7 +13894,7 @@ function acquirePidLock(pidPath) {
13768
13894
  function readPidLock(pidPath) {
13769
13895
  let raw;
13770
13896
  try {
13771
- raw = fs22.readFileSync(pidPath, "utf-8");
13897
+ raw = fs23.readFileSync(pidPath, "utf-8");
13772
13898
  } catch (err) {
13773
13899
  if (err.code === "ENOENT") {
13774
13900
  return { state: "absent" };
@@ -13792,9 +13918,9 @@ function makeHandle(pidPath, pid) {
13792
13918
  if (released) return;
13793
13919
  released = true;
13794
13920
  try {
13795
- const raw = fs22.readFileSync(pidPath, "utf-8").trim();
13921
+ const raw = fs23.readFileSync(pidPath, "utf-8").trim();
13796
13922
  if (raw === String(pid)) {
13797
- fs22.unlinkSync(pidPath);
13923
+ fs23.unlinkSync(pidPath);
13798
13924
  }
13799
13925
  } catch (err) {
13800
13926
  if (err.code !== "ENOENT") {
@@ -13820,7 +13946,7 @@ function isProcessAlive(pid) {
13820
13946
  }
13821
13947
 
13822
13948
  // src/infra/daemon/udsIpc.ts
13823
- import fs23 from "fs";
13949
+ import fs24 from "fs";
13824
13950
  import net2 from "net";
13825
13951
 
13826
13952
  // src/infra/daemon/udsFrameCodec.ts
@@ -13865,7 +13991,7 @@ async function startUdsServer(socketPath, handler, log) {
13865
13991
  });
13866
13992
  if (process.platform !== "win32") {
13867
13993
  try {
13868
- fs23.chmodSync(socketPath, 384);
13994
+ fs24.chmodSync(socketPath, 384);
13869
13995
  } catch {
13870
13996
  }
13871
13997
  }
@@ -13875,7 +14001,7 @@ async function startUdsServer(socketPath, handler, log) {
13875
14001
  server.close(() => resolve());
13876
14002
  });
13877
14003
  try {
13878
- fs23.unlinkSync(socketPath);
14004
+ fs24.unlinkSync(socketPath);
13879
14005
  } catch (err) {
13880
14006
  if (err.code !== "ENOENT") {
13881
14007
  }
@@ -13993,7 +14119,7 @@ async function sendUdsRequest(socketPath, request, options = {}) {
13993
14119
  async function unlinkStaleSocket(socketPath) {
13994
14120
  let stat;
13995
14121
  try {
13996
- stat = fs23.statSync(socketPath);
14122
+ stat = fs24.statSync(socketPath);
13997
14123
  } catch (err) {
13998
14124
  if (err.code === "ENOENT") return;
13999
14125
  throw err;
@@ -14020,7 +14146,7 @@ async function unlinkStaleSocket(socketPath) {
14020
14146
  `uds path ${socketPath} is in use by another process; aborting`
14021
14147
  );
14022
14148
  }
14023
- fs23.unlinkSync(socketPath);
14149
+ fs24.unlinkSync(socketPath);
14024
14150
  }
14025
14151
 
14026
14152
  export {
@@ -14098,4 +14224,4 @@ export {
14098
14224
  startUdsServer,
14099
14225
  sendUdsRequest
14100
14226
  };
14101
- //# sourceMappingURL=chunk-QVXHUJPH.js.map
14227
+ //# sourceMappingURL=chunk-ZU7M6YZW.js.map
package/dist/cli.js CHANGED
@@ -74,7 +74,7 @@ import {
74
74
  writeAttachmentMirror,
75
75
  writeGatewayClientConfig,
76
76
  wsClientOptionsForEndpoint
77
- } from "./chunk-QVXHUJPH.js";
77
+ } from "./chunk-ZU7M6YZW.js";
78
78
  import {
79
79
  generateId as generateId2
80
80
  } from "./chunk-BTKQ67RE.js";
@@ -15695,7 +15695,7 @@ var cachedVersion = null;
15695
15695
  function readPackageVersion() {
15696
15696
  if (cachedVersion !== null) return cachedVersion;
15697
15697
  try {
15698
- const injected = "0.5.4";
15698
+ const injected = "0.5.6";
15699
15699
  if (typeof injected === "string" && injected.length > 0) {
15700
15700
  cachedVersion = injected;
15701
15701
  return cachedVersion;
@@ -3,7 +3,7 @@ import {
3
3
  ensureDaemonStateDir,
4
4
  runDashboardRuntimeDaemon,
5
5
  startUdsServer
6
- } from "./chunk-QVXHUJPH.js";
6
+ } from "./chunk-ZU7M6YZW.js";
7
7
  import "./chunk-BTKQ67RE.js";
8
8
  import {
9
9
  readDashboardClientConfig,
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "hooks",
18
18
  "dashboard"
19
19
  ],
20
- "version": "0.5.4",
20
+ "version": "0.5.6",
21
21
  "license": "MIT",
22
22
  "bin": {
23
23
  "athena": "dist/cli.js",