@drisp/cli 0.5.23 → 0.5.26

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.
@@ -8,7 +8,7 @@ import {
8
8
  installWorkflowPlugins,
9
9
  resolveWorkflow,
10
10
  writeGlobalConfig
11
- } from "./chunk-JHSADKDJ.js";
11
+ } from "./chunk-73V7GXV6.js";
12
12
 
13
13
  // src/setup/steps/WorkflowInstallWizard.tsx
14
14
  import { useState, useEffect, useCallback, useRef } from "react";
@@ -92,4 +92,4 @@ function WorkflowInstallWizard({ source, onDone }) {
92
92
  export {
93
93
  WorkflowInstallWizard as default
94
94
  };
95
- //# sourceMappingURL=WorkflowInstallWizard-CCCUR6KA.js.map
95
+ //# sourceMappingURL=WorkflowInstallWizard-5FENJHBW.js.map
@@ -3,23 +3,24 @@ import {
3
3
  loadOrCreateToken,
4
4
  requireTokenForBind,
5
5
  timingSafeTokenEqual
6
- } from "./chunk-MRAM6EYI.js";
6
+ } from "./chunk-4NX4WFPB.js";
7
7
  import {
8
8
  CHANNEL_REQUEST_ID_REGEX,
9
9
  createUdsServerTransport,
10
+ createWsServerTransport,
10
11
  generateChannelRequestId,
11
12
  isLoopbackHost,
12
13
  isValidChannelRequestId,
14
+ openVersionedDb,
13
15
  refreshDashboardAccessToken,
14
16
  resolveGatewayPaths,
15
17
  resolveListenSpec,
16
- traceGatewayFrame,
17
18
  trackGatewayRuntimeExpired,
18
19
  trackGatewayRuntimeRebind,
19
20
  trackGatewayTransportConnect,
20
21
  trackGatewayTransportDisconnect,
21
22
  writeGatewayTrace
22
- } from "./chunk-BTY7MYYT.js";
23
+ } from "./chunk-3U37HT4T.js";
23
24
 
24
25
  // src/gateway/daemon.ts
25
26
  import fs4 from "fs";
@@ -2440,7 +2441,7 @@ var cachedVersion = null;
2440
2441
  function readVersion() {
2441
2442
  if (cachedVersion !== null) return cachedVersion;
2442
2443
  try {
2443
- const injected = "0.5.23";
2444
+ const injected = "0.5.26";
2444
2445
  if (typeof injected === "string" && injected.length > 0) {
2445
2446
  cachedVersion = injected;
2446
2447
  return cachedVersion;
@@ -3463,6 +3464,33 @@ var DispatchPipeline = class {
3463
3464
  }
3464
3465
  };
3465
3466
 
3467
+ // src/gateway/listenerPlan.ts
3468
+ function planListener(spec, token) {
3469
+ requireTokenForBind(spec, token);
3470
+ if (spec.kind === "uds") {
3471
+ return {
3472
+ transport: { kind: "uds", socketPath: spec.socketPath },
3473
+ warnings: []
3474
+ };
3475
+ }
3476
+ const warnings = [];
3477
+ if (spec.insecure && !spec.tls && !isLoopbackHost(spec.host)) {
3478
+ warnings.push(
3479
+ `athena-gateway: WARNING --insecure is set on a non-loopback bind (${spec.host}:${spec.port}); token travels in plaintext. Use only behind TLS-terminating reverse proxy or Tailscale/WireGuard tunnel.`
3480
+ );
3481
+ }
3482
+ return {
3483
+ transport: {
3484
+ kind: "tcp",
3485
+ host: spec.host,
3486
+ port: spec.port,
3487
+ allowNonLoopback: spec.insecure || Boolean(spec.tls),
3488
+ ...spec.tls ? { tls: spec.tls } : {}
3489
+ },
3490
+ warnings
3491
+ };
3492
+ }
3493
+
3466
3494
  // src/gateway/lock.ts
3467
3495
  import fs2 from "fs";
3468
3496
  import path2 from "path";
@@ -3758,31 +3786,9 @@ function questionFingerprint(req) {
3758
3786
 
3759
3787
  // src/gateway/state/db.ts
3760
3788
  import fs3 from "fs";
3761
- import path3 from "path";
3762
- import Database from "better-sqlite3";
3763
3789
  var GATEWAY_STATE_VERSION = 2;
3764
- function openGatewayState(dbPath) {
3765
- if (dbPath !== ":memory:") {
3766
- fs3.mkdirSync(path3.dirname(dbPath), { recursive: true, mode: 448 });
3767
- }
3768
- const db = new Database(dbPath);
3769
- db.exec("PRAGMA journal_mode = WAL");
3770
- db.exec("PRAGMA foreign_keys = ON");
3771
- initGatewayStateSchema(db);
3772
- if (dbPath !== ":memory:" && process.platform !== "win32") {
3773
- try {
3774
- fs3.chmodSync(dbPath, 384);
3775
- } catch {
3776
- }
3777
- }
3778
- return db;
3779
- }
3780
- function initGatewayStateSchema(db) {
3790
+ var migrateGatewayState = (db, fromVersion) => {
3781
3791
  db.exec(`
3782
- CREATE TABLE IF NOT EXISTS schema_version (
3783
- version INTEGER NOT NULL
3784
- );
3785
-
3786
3792
  -- Inbound chat messages parked while no runtime is registered. Drained
3787
3793
  -- in FIFO id order on session.register. Idempotency key prevents the
3788
3794
  -- same provider message from being parked twice if an adapter retries.
@@ -3814,244 +3820,50 @@ function initGatewayStateSchema(db) {
3814
3820
  CREATE INDEX IF NOT EXISTS ix_channel_outbox_due
3815
3821
  ON channel_outbox(next_attempt_at);
3816
3822
  `);
3817
- const existing = db.prepare("SELECT version FROM schema_version").get();
3818
- if (existing && existing.version > GATEWAY_STATE_VERSION) {
3819
- throw new Error(
3820
- `Gateway state DB has newer schema version ${existing.version} (expected <= ${GATEWAY_STATE_VERSION}). Update athena-cli.`
3821
- );
3822
- }
3823
- if (!existing) {
3824
- db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(
3825
- GATEWAY_STATE_VERSION
3826
- );
3827
- return;
3828
- }
3823
+ if (fromVersion === void 0) return;
3829
3824
  const inboundColumns = db.prepare("PRAGMA table_info(inbound_queue)").all();
3830
3825
  if (!inboundColumns.some((column) => column.name === "attachment_id")) {
3831
3826
  db.prepare("ALTER TABLE inbound_queue ADD COLUMN attachment_id TEXT").run();
3832
3827
  }
3833
- if (existing.version < GATEWAY_STATE_VERSION) {
3828
+ if (fromVersion < GATEWAY_STATE_VERSION) {
3834
3829
  db.prepare("UPDATE schema_version SET version = ?").run(
3835
3830
  GATEWAY_STATE_VERSION
3836
3831
  );
3837
3832
  }
3838
- }
3839
-
3840
- // src/gateway/transport/tlsWs.ts
3841
- import { WebSocketServer } from "ws";
3842
- import { createServer as createHttpsServer } from "https";
3843
- import { readFileSync as readFileSync3 } from "fs";
3844
- function createWsServerTransport(opts) {
3845
- if (!opts.allowNonLoopback && !isLoopbackHost(opts.host)) {
3846
- throw new Error(`gateway: refusing non-loopback bind without --insecure`);
3847
- }
3848
- let endpoint = null;
3849
- const scheme = opts.tls ? "wss" : "ws";
3850
- const rateLimit = createConnectRateLimiter(opts.rateLimitPerMin ?? 10);
3851
- return {
3852
- kind: "ws",
3853
- endpoint: () => {
3854
- if (!endpoint) {
3855
- throw new Error("gateway: WS transport has not started listening");
3856
- }
3857
- return endpoint;
3858
- },
3859
- listen: (onConnection) => new Promise((resolve, reject) => {
3860
- const verifyClient = (info) => rateLimit.allow(info.req.socket.remoteAddress ?? "unknown");
3861
- const { wss, httpsServer } = createWss({
3862
- host: opts.host,
3863
- port: opts.port,
3864
- tls: opts.tls,
3865
- verifyClient
3866
- });
3867
- const onError = (err) => reject(err);
3868
- wss.once("error", onError);
3869
- if (httpsServer) httpsServer.once("error", onError);
3870
- const onListening = () => {
3871
- wss.off("error", onError);
3872
- if (httpsServer) httpsServer.off("error", onError);
3873
- const addr = httpsServer ? httpsServer.address() : wss.address();
3874
- if (typeof addr === "string" || addr === null) {
3875
- wss.close();
3876
- httpsServer?.close();
3877
- reject(
3878
- new Error("gateway: WS listener did not expose TCP address")
3879
- );
3880
- return;
3881
- }
3882
- endpoint = {
3883
- host: opts.host,
3884
- port: addr.port,
3885
- url: `${scheme}://${opts.host}:${addr.port}`
3886
- };
3887
- resolve({
3888
- close: () => new Promise((closeResolve) => {
3889
- for (const client of wss.clients) client.terminate();
3890
- wss.close(() => {
3891
- if (httpsServer) httpsServer.close(() => closeResolve());
3892
- else closeResolve();
3893
- });
3894
- })
3895
- });
3896
- };
3897
- if (httpsServer) httpsServer.once("listening", onListening);
3898
- else wss.once("listening", onListening);
3899
- const pingIntervalMs = opts.pingIntervalMs ?? 15e3;
3900
- const pongTimeoutMs = opts.pongTimeoutMs ?? 3e4;
3901
- wss.on("connection", (ws) => {
3902
- attachHeartbeat(ws, pingIntervalMs, pongTimeoutMs);
3903
- onConnection(createWsConnection(ws, `${scheme}:${opts.host}`));
3904
- });
3905
- })
3906
- };
3907
- }
3908
- function loadTlsOptions(tls) {
3909
- return {
3910
- cert: readFileSync3(tls.certPath),
3911
- key: readFileSync3(tls.keyPath)
3912
- };
3913
- }
3914
- function createWss(input) {
3915
- if (input.tls) {
3916
- const httpsServer = createHttpsServer(loadTlsOptions(input.tls));
3917
- httpsServer.listen({ host: input.host, port: input.port });
3918
- return {
3919
- wss: new WebSocketServer({
3920
- server: httpsServer,
3921
- verifyClient: input.verifyClient
3922
- }),
3923
- httpsServer
3924
- };
3925
- }
3926
- return {
3927
- wss: new WebSocketServer({
3928
- host: input.host,
3929
- port: input.port,
3930
- verifyClient: input.verifyClient
3931
- }),
3932
- httpsServer: null
3933
- };
3934
- }
3935
- function createConnectRateLimiter(maxPerMin) {
3936
- if (maxPerMin <= 0) return { allow: () => true };
3937
- const buckets = /* @__PURE__ */ new Map();
3938
- let pruneCountdown = 256;
3939
- const prune = (cutoff) => {
3940
- for (const [ip, arr] of buckets) {
3941
- const fresh = arr.filter((t) => t > cutoff);
3942
- if (fresh.length === 0) buckets.delete(ip);
3943
- else if (fresh.length !== arr.length) buckets.set(ip, fresh);
3944
- }
3945
- };
3946
- return {
3947
- allow(ip) {
3948
- const now = Date.now();
3949
- const cutoff = now - 6e4;
3950
- pruneCountdown -= 1;
3951
- if (pruneCountdown <= 0) {
3952
- prune(cutoff);
3953
- pruneCountdown = 256;
3954
- }
3955
- const recent = (buckets.get(ip) ?? []).filter((t) => t > cutoff);
3956
- if (recent.length >= maxPerMin) {
3957
- buckets.set(ip, recent);
3958
- return false;
3959
- }
3960
- recent.push(now);
3961
- buckets.set(ip, recent);
3962
- return true;
3963
- }
3964
- };
3965
- }
3966
- function attachHeartbeat(ws, pingIntervalMs, pongTimeoutMs) {
3967
- if (pingIntervalMs <= 0) return;
3968
- let pongTimer = null;
3969
- const clearPongTimer = () => {
3970
- if (pongTimer) {
3971
- clearTimeout(pongTimer);
3972
- pongTimer = null;
3973
- }
3974
- };
3975
- ws.on("pong", clearPongTimer);
3976
- const interval = setInterval(() => {
3977
- if (ws.readyState !== ws.OPEN) return;
3978
- try {
3979
- ws.ping();
3980
- } catch {
3981
- return;
3982
- }
3983
- if (!pongTimer) {
3984
- pongTimer = setTimeout(() => ws.terminate(), pongTimeoutMs);
3985
- }
3986
- }, pingIntervalMs);
3987
- const stop = () => {
3988
- clearInterval(interval);
3989
- clearPongTimer();
3990
- };
3991
- ws.on("close", stop);
3992
- ws.on("error", stop);
3993
- }
3994
- function createWsConnection(ws, peer) {
3995
- const frameHandlers = /* @__PURE__ */ new Set();
3996
- const closeHandlers = /* @__PURE__ */ new Set();
3997
- const errorHandlers = /* @__PURE__ */ new Set();
3998
- ws.on("message", (data) => {
3999
- let parsed;
3833
+ };
3834
+ function openGatewayState(dbPath) {
3835
+ const db = openVersionedDb(dbPath, {
3836
+ version: GATEWAY_STATE_VERSION,
3837
+ migrate: migrateGatewayState,
3838
+ foreignKeys: true,
3839
+ ensureDir: true,
3840
+ dirMode: 448,
3841
+ onNewerVersion: (found, expected) => new Error(
3842
+ `Gateway state DB has newer schema version ${found} (expected <= ${expected}). Update athena-cli.`
3843
+ )
3844
+ });
3845
+ if (dbPath !== ":memory:" && process.platform !== "win32") {
4000
3846
  try {
4001
- parsed = JSON.parse(data.toString());
3847
+ fs3.chmodSync(dbPath, 384);
4002
3848
  } catch {
4003
- ws.close();
4004
- return;
4005
3849
  }
4006
- traceGatewayFrame("ws", peer, "in", parsed);
4007
- for (const handler of frameHandlers) handler(parsed);
4008
- });
4009
- ws.on("error", (err) => {
4010
- for (const handler of errorHandlers) handler(err);
4011
- });
4012
- ws.on("close", () => {
4013
- for (const handler of closeHandlers) handler();
4014
- });
4015
- return {
4016
- kind: "ws",
4017
- peer,
4018
- send: (frame) => {
4019
- if (ws.readyState !== ws.OPEN) return;
4020
- traceGatewayFrame("ws", peer, "out", frame);
4021
- ws.send(JSON.stringify(frame));
4022
- },
4023
- close: () => ws.close(),
4024
- onFrame: (cb) => {
4025
- frameHandlers.add(cb);
4026
- return () => frameHandlers.delete(cb);
4027
- },
4028
- onClose: (cb) => {
4029
- closeHandlers.add(cb);
4030
- return () => closeHandlers.delete(cb);
4031
- },
4032
- onError: (cb) => {
4033
- errorHandlers.add(cb);
4034
- return () => errorHandlers.delete(cb);
4035
- }
4036
- };
3850
+ }
3851
+ return db;
4037
3852
  }
4038
3853
 
4039
3854
  // src/gateway/daemon.ts
4040
- function buildListenerStatus(spec, resolvedPort) {
4041
- if (spec.kind === "uds") {
4042
- return { kind: "uds", socketPath: spec.socketPath };
3855
+ function describeToStatus(desc, spec) {
3856
+ if (desc.kind === "uds") {
3857
+ return { kind: "uds", socketPath: desc.socketPath };
4043
3858
  }
4044
- const port = resolvedPort ?? spec.port;
4045
- const tls = Boolean(spec.tls);
4046
- const scheme = tls ? "wss" : "ws";
4047
3859
  return {
4048
3860
  kind: "tcp",
4049
- host: spec.host,
4050
- port,
4051
- url: `${scheme}://${spec.host}:${port}`,
4052
- tls,
4053
- insecure: spec.insecure,
4054
- loopback: isLoopbackHost(spec.host)
3861
+ host: desc.host,
3862
+ port: desc.port,
3863
+ url: desc.url,
3864
+ tls: desc.tls,
3865
+ insecure: spec.kind === "tcp" ? spec.insecure : false,
3866
+ loopback: isLoopbackHost(desc.host)
4055
3867
  };
4056
3868
  }
4057
3869
  async function startDaemon(opts) {
@@ -4070,7 +3882,7 @@ async function startDaemon(opts) {
4070
3882
  const lock = acquireLock(paths.lockPath);
4071
3883
  const token = loadOrCreateToken(paths.tokenPath);
4072
3884
  const listenSpec = opts.listenSpec ?? resolveListenSpec({ paths });
4073
- requireTokenForBind(listenSpec, token);
3885
+ const listenerPlan = planListener(listenSpec, token);
4074
3886
  const listenerHints = {
4075
3887
  transport: listenSpec.kind === "tcp" ? "ws" : "uds",
4076
3888
  tls: listenSpec.kind === "tcp" && Boolean(listenSpec.tls),
@@ -4122,29 +3934,31 @@ async function startDaemon(opts) {
4122
3934
  logRegistrations: !opts.silent
4123
3935
  });
4124
3936
  }
3937
+ let transport;
4125
3938
  const handler = createDispatcher({
4126
3939
  startedAt,
4127
3940
  pipeline,
4128
3941
  channelManager,
4129
3942
  relayCoordinator,
4130
- getListener: () => listenerStatus ?? buildListenerStatus(listenSpec, null),
3943
+ getListener: () => listenerStatus ?? describeToStatus(transport.describe(), listenSpec),
4131
3944
  reloadChannels
4132
3945
  });
4133
3946
  let server;
4134
3947
  let listener;
4135
3948
  try {
4136
- const transport = listenSpec.kind === "tcp" ? createWsServerTransport({
4137
- host: listenSpec.host,
4138
- port: listenSpec.port,
4139
- allowNonLoopback: listenSpec.insecure || Boolean(listenSpec.tls),
4140
- ...listenSpec.tls ? { tls: listenSpec.tls } : {}
4141
- }) : void 0;
3949
+ const tcpPlan = listenerPlan.transport.kind === "tcp" ? listenerPlan.transport : null;
3950
+ transport = tcpPlan ? createWsServerTransport({
3951
+ host: tcpPlan.host,
3952
+ port: tcpPlan.port,
3953
+ allowNonLoopback: tcpPlan.allowNonLoopback,
3954
+ ...tcpPlan.tls ? { tls: tcpPlan.tls } : {}
3955
+ }) : createUdsServerTransport({ socketPath: paths.socketPath });
4142
3956
  server = await startControlServer({
4143
3957
  socketPath: paths.socketPath,
4144
3958
  token,
4145
3959
  startedAt,
4146
3960
  handler,
4147
- ...transport !== void 0 ? { transport } : {},
3961
+ transport,
4148
3962
  onConnect: (ctx) => {
4149
3963
  connectionOpenedAt.set(ctx.connectionId, Date.now());
4150
3964
  trackGatewayTransportConnect({
@@ -4165,19 +3979,14 @@ async function startDaemon(opts) {
4165
3979
  pipeline.notifyConnectionClosed(ctx.connectionId);
4166
3980
  }
4167
3981
  });
4168
- if (listenSpec.kind === "tcp") {
4169
- const endpoint = transport.endpoint();
4170
- listener = {
4171
- kind: "tcp",
4172
- host: endpoint.host,
4173
- port: endpoint.port,
4174
- url: endpoint.url
4175
- };
4176
- listenerStatus = buildListenerStatus(listenSpec, endpoint.port);
4177
- } else {
4178
- listener = { kind: "uds", socketPath: listenSpec.socketPath };
4179
- listenerStatus = buildListenerStatus(listenSpec, null);
4180
- }
3982
+ const description = transport.describe();
3983
+ listenerStatus = describeToStatus(description, listenSpec);
3984
+ listener = description.kind === "uds" ? { kind: "uds", socketPath: description.socketPath } : {
3985
+ kind: "tcp",
3986
+ host: description.host,
3987
+ port: description.port,
3988
+ url: description.url
3989
+ };
4181
3990
  } catch (err) {
4182
3991
  lock.release();
4183
3992
  throw err;
@@ -4187,11 +3996,8 @@ async function startDaemon(opts) {
4187
3996
  process.stdout.write(`athena-gateway: ok pid=${pid} ${target}
4188
3997
  `);
4189
3998
  }
4190
- if (listenSpec.kind === "tcp" && listenSpec.insecure && !listenSpec.tls && !isLoopbackHost(listenSpec.host)) {
4191
- process.stderr.write(
4192
- `athena-gateway: WARNING --insecure is set on a non-loopback bind (${listenSpec.host}:${listenSpec.port}); token travels in plaintext. Use only behind TLS-terminating reverse proxy or Tailscale/WireGuard tunnel.
4193
- `
4194
- );
3999
+ for (const warning of listenerPlan.warnings) {
4000
+ process.stderr.write(warning + "\n");
4195
4001
  }
4196
4002
  let stopping = false;
4197
4003
  const stop = async () => {