@drisp/cli 0.4.4 → 0.4.5

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.
@@ -4,7 +4,7 @@ import {
4
4
  loadOrCreateToken,
5
5
  requireTokenForBind,
6
6
  timingSafeTokenEqual
7
- } from "./chunk-6TJHAUNB.js";
7
+ } from "./chunk-M44KEGM7.js";
8
8
  import {
9
9
  CHANNEL_REQUEST_ID_REGEX,
10
10
  createUdsServerTransport,
@@ -353,11 +353,11 @@ function makeFrameId() {
353
353
  }
354
354
 
355
355
  // src/gateway/adapters/console/adapter.ts
356
- var CONSOLE_ID = "console";
356
+ var CONSOLE_DEFAULT_ID = "console";
357
357
  var CLIENT_NAME = "athena-cli";
358
358
  var CLIENT_VERSION = "0.0.0";
359
359
  var ConsoleAdapter = class {
360
- id = CONSOLE_ID;
360
+ id;
361
361
  capabilities = {
362
362
  chat: true,
363
363
  threads: true,
@@ -369,8 +369,9 @@ var ConsoleAdapter = class {
369
369
  ctx = null;
370
370
  pendingPermissions = /* @__PURE__ */ new Map();
371
371
  pendingQuestions = /* @__PURE__ */ new Map();
372
- constructor(opts) {
372
+ constructor(opts, id = CONSOLE_DEFAULT_ID) {
373
373
  this.opts = opts;
374
+ this.id = id;
374
375
  }
375
376
  async start(ctx) {
376
377
  if (this.client) {
@@ -502,7 +503,7 @@ var ConsoleAdapter = class {
502
503
  if (!entry) return;
503
504
  this.pendingPermissions.delete(channelRequestId);
504
505
  entry.signal.removeEventListener("abort", entry.abortListener);
505
- entry.resolve({ kind: "verdict", behavior: decision, channelId: CONSOLE_ID });
506
+ entry.resolve({ kind: "verdict", behavior: decision, channelId: this.id });
506
507
  }
507
508
  disposePermissions(reason = "auto_resolved") {
508
509
  for (const [id, entry] of [...this.pendingPermissions.entries()]) {
@@ -571,7 +572,7 @@ var ConsoleAdapter = class {
571
572
  entry.resolve({ kind: "cancelled", reason: "auto_resolved" });
572
573
  return;
573
574
  }
574
- entry.resolve({ kind: "answer", answers: filtered, channelId: CONSOLE_ID });
575
+ entry.resolve({ kind: "answer", answers: filtered, channelId: this.id });
575
576
  }
576
577
  disposeQuestions(reason = "auto_resolved") {
577
578
  for (const [id, entry] of [...this.pendingQuestions.entries()]) {
@@ -612,7 +613,7 @@ var ConsoleAdapter = class {
612
613
  this.ctx?.log("warn", "console.message.in dropped: missing messageId");
613
614
  return;
614
615
  }
615
- const inbound = normalizeInbound(frame, this.opts.runnerId);
616
+ const inbound = normalizeInbound(frame, this.opts.runnerId, this.id);
616
617
  if (!inbound || !this.ctx) return;
617
618
  try {
618
619
  this.ctx.emitInbound(inbound);
@@ -723,13 +724,13 @@ function isValidConsoleAddress(value) {
723
724
  }
724
725
  return true;
725
726
  }
726
- function normalizeInbound(frame, runnerId) {
727
+ function normalizeInbound(frame, runnerId, channelId) {
727
728
  if (typeof frame.text !== "string" || frame.text.length === 0) return null;
728
729
  const userId = frame.address.userId ?? "console-user";
729
730
  const idempotencyKey = typeof frame.idempotencyKey === "string" && frame.idempotencyKey.length > 0 ? frame.idempotencyKey : `console:${runnerId}:${frame.messageId}`;
730
731
  return {
731
732
  location: {
732
- channelId: CONSOLE_ID,
733
+ channelId,
733
734
  accountId: frame.address.workspaceId ?? runnerId,
734
735
  peer: { id: userId, kind: "user" },
735
736
  ...frame.address.threadId !== void 0 ? { thread: { id: frame.address.threadId } } : frame.address.conversationId !== void 0 ? { thread: { id: frame.address.conversationId } } : {}
@@ -810,8 +811,8 @@ var consoleModule = {
810
811
  };
811
812
  return { ok: true, config };
812
813
  },
813
- create(config) {
814
- return new ConsoleAdapter(config);
814
+ create(config, instanceId) {
815
+ return new ConsoleAdapter(config, instanceId);
815
816
  }
816
817
  };
817
818
  function isLoopbackUrl(url) {
@@ -1520,9 +1521,9 @@ function buildCancelText(reason) {
1520
1521
  }
1521
1522
 
1522
1523
  // src/gateway/adapters/telegram/adapter.ts
1523
- var TELEGRAM_ID = "telegram";
1524
+ var TELEGRAM_DEFAULT_ID = "telegram";
1524
1525
  var TelegramAdapter = class {
1525
- id = TELEGRAM_ID;
1526
+ id;
1526
1527
  capabilities = {
1527
1528
  chat: true,
1528
1529
  threads: true,
@@ -1538,8 +1539,9 @@ var TelegramAdapter = class {
1538
1539
  lastInboundAt;
1539
1540
  lastTransportOk = true;
1540
1541
  ctx = null;
1541
- constructor(opts) {
1542
+ constructor(opts, id = TELEGRAM_DEFAULT_ID) {
1542
1543
  this.opts = opts;
1544
+ this.id = id;
1543
1545
  this.relay = new TelegramRelay({
1544
1546
  resolveTarget: () => {
1545
1547
  if (this.opts.defaultChatId === void 0) return null;
@@ -1638,7 +1640,7 @@ var TelegramAdapter = class {
1638
1640
  this.markHealth(true);
1639
1641
  continue;
1640
1642
  }
1641
- const inbound = normalizeInbound2(update, allow);
1643
+ const inbound = normalizeInbound2(update, allow, this.id);
1642
1644
  if (!inbound) continue;
1643
1645
  this.lastInboundAt = inbound.receivedAt;
1644
1646
  this.markHealth(true);
@@ -1676,7 +1678,7 @@ var TelegramAdapter = class {
1676
1678
  }
1677
1679
  }
1678
1680
  };
1679
- function normalizeInbound2(update, allow) {
1681
+ function normalizeInbound2(update, allow, channelId) {
1680
1682
  const message = update.message ?? update.edited_message;
1681
1683
  if (!message) return null;
1682
1684
  const text = message.text;
@@ -1691,7 +1693,7 @@ function normalizeInbound2(update, allow) {
1691
1693
  const threadId = typeof message.message_thread_id === "number" ? String(message.message_thread_id) : void 0;
1692
1694
  return {
1693
1695
  location: {
1694
- channelId: TELEGRAM_ID,
1696
+ channelId,
1695
1697
  accountId,
1696
1698
  ...isPrivate ? { peer: { id: chatId, kind: "user" } } : {
1697
1699
  room: {
@@ -1746,8 +1748,8 @@ var telegramModule = {
1746
1748
  };
1747
1749
  return { ok: true, config };
1748
1750
  },
1749
- create(config) {
1750
- return new TelegramAdapter(config);
1751
+ create(config, instanceId) {
1752
+ return new TelegramAdapter(config, instanceId);
1751
1753
  }
1752
1754
  };
1753
1755
 
@@ -1762,18 +1764,18 @@ function findAdapterModule(name) {
1762
1764
 
1763
1765
  // src/gateway/adapters/factory.ts
1764
1766
  function instantiateAdapter(sidecar) {
1765
- const module = findAdapterModule(sidecar.name);
1767
+ const module = findAdapterModule(sidecar.kind);
1766
1768
  if (!module) {
1767
- return { ok: false, reason: `unknown channel: ${sidecar.name}` };
1769
+ return { ok: false, reason: `unknown channel kind: ${sidecar.kind}` };
1768
1770
  }
1769
1771
  const parsed = module.parseConfig({
1770
1772
  options: sidecar.options,
1771
1773
  allowedUserIds: sidecar.allowedUserIds
1772
1774
  });
1773
1775
  if (!parsed.ok) {
1774
- return { ok: false, reason: `${sidecar.name}: ${parsed.reason}` };
1776
+ return { ok: false, reason: `${sidecar.instanceId}: ${parsed.reason}` };
1775
1777
  }
1776
- return { ok: true, adapter: module.create(parsed.config) };
1778
+ return { ok: true, adapter: module.create(parsed.config, sidecar.instanceId) };
1777
1779
  }
1778
1780
 
1779
1781
  // src/gateway/channelManager.ts
@@ -2088,7 +2090,7 @@ var cachedVersion = null;
2088
2090
  function readVersion() {
2089
2091
  if (cachedVersion !== null) return cachedVersion;
2090
2092
  try {
2091
- const injected = "0.4.4";
2093
+ const injected = "0.4.5";
2092
2094
  if (typeof injected === "string" && injected.length > 0) {
2093
2095
  cachedVersion = injected;
2094
2096
  return cachedVersion;
@@ -3094,6 +3096,64 @@ function acquireLock(lockPath) {
3094
3096
  throw new Error(`failed to acquire gateway lock at ${lockPath}`);
3095
3097
  }
3096
3098
 
3099
+ // src/gateway/relay/pendingRegistry.ts
3100
+ var PendingRegistry = class {
3101
+ entries = /* @__PURE__ */ new Map();
3102
+ inspect(channelRequestId, kind, fingerprint, runtimeId) {
3103
+ const existing = this.entries.get(channelRequestId);
3104
+ if (!existing) return { kind: "absent" };
3105
+ if (existing.kind !== kind) return { kind: "collision", reason: "kind" };
3106
+ if (existing.fingerprint !== fingerprint) {
3107
+ return { kind: "collision", reason: "payload" };
3108
+ }
3109
+ if (existing.runtimeId !== runtimeId) {
3110
+ return { kind: "collision", reason: "owner" };
3111
+ }
3112
+ return { kind: "attach", entry: existing };
3113
+ }
3114
+ register(entry) {
3115
+ this.entries.set(entry.channelRequestId, entry);
3116
+ }
3117
+ settle(channelRequestId, result) {
3118
+ const entry = this.entries.get(channelRequestId);
3119
+ if (!entry || entry.settled) return false;
3120
+ entry.settled = true;
3121
+ this.entries.delete(channelRequestId);
3122
+ clearTimeout(entry.timer);
3123
+ for (const ctrl of entry.controllers) {
3124
+ if (!ctrl.signal.aborted) ctrl.abort();
3125
+ }
3126
+ entry.resolve(result);
3127
+ return true;
3128
+ }
3129
+ cancel(channelRequestId, reason, expectedRuntimeId) {
3130
+ const entry = this.entries.get(channelRequestId);
3131
+ if (!entry) return false;
3132
+ if (expectedRuntimeId !== void 0 && entry.runtimeId !== void 0 && entry.runtimeId !== expectedRuntimeId) {
3133
+ return false;
3134
+ }
3135
+ return this.settle(channelRequestId, { kind: "cancelled", reason });
3136
+ }
3137
+ disposeAll(reason) {
3138
+ for (const id of [...this.entries.keys()]) {
3139
+ this.cancel(id, reason, void 0);
3140
+ }
3141
+ }
3142
+ count() {
3143
+ return this.entries.size;
3144
+ }
3145
+ };
3146
+ function collisionMessage(channelRequestId, reason, newKind) {
3147
+ if (reason === "owner") {
3148
+ return `channel_request_owner_mismatch: ${channelRequestId} owned by a different runtime`;
3149
+ }
3150
+ if (reason === "kind") {
3151
+ const otherKind = newKind === "permission" ? "question" : "permission";
3152
+ return `channel_request_id_collision: ${channelRequestId} is bound to a ${otherKind} relay`;
3153
+ }
3154
+ return `channel_request_id_collision: ${channelRequestId} payload mismatch`;
3155
+ }
3156
+
3097
3157
  // src/gateway/relay/coordinator.ts
3098
3158
  var DEFAULT_RELAY_TTL_MS = 5 * 6e4;
3099
3159
  var RelayCoordinator = class {
@@ -3101,7 +3161,7 @@ var RelayCoordinator = class {
3101
3161
  defaultTtlMs;
3102
3162
  idFactory;
3103
3163
  log;
3104
- pending = /* @__PURE__ */ new Map();
3164
+ registry = new PendingRegistry();
3105
3165
  constructor(opts) {
3106
3166
  this.adapters = opts.adapters;
3107
3167
  this.defaultTtlMs = opts.defaultTtlMs ?? DEFAULT_RELAY_TTL_MS;
@@ -3110,180 +3170,151 @@ var RelayCoordinator = class {
3110
3170
  }
3111
3171
  requestPermission(req) {
3112
3172
  const channelRequestId = req.channelRequestId ?? this.idFactory();
3113
- const ttlMs = req.ttlMs ?? this.defaultTtlMs;
3114
3173
  const targets = this.adapters().filter(
3115
3174
  (a) => a.capabilities.relayPermission && typeof a.requestPermissionVerdict === "function"
3116
3175
  );
3117
3176
  if (targets.length === 0) {
3118
- return {
3119
- channelRequestId,
3120
- result: Promise.resolve({ kind: "no_relay" })
3121
- };
3177
+ return { channelRequestId, result: Promise.resolve({ kind: "no_relay" }) };
3122
3178
  }
3123
3179
  const fingerprint = permissionFingerprint(req);
3124
- const existing = this.pending.get(channelRequestId);
3125
- if (existing) {
3126
- if (existing.kind !== "permission") {
3127
- throw new Error(
3128
- `channel_request_id_collision: ${channelRequestId} is bound to a question relay`
3129
- );
3130
- }
3131
- if (existing.fingerprint !== fingerprint) {
3132
- throw new Error(
3133
- `channel_request_id_collision: ${channelRequestId} payload mismatch`
3134
- );
3135
- }
3136
- if (existing.runtimeId !== req.runtimeId) {
3137
- throw new Error(
3138
- `channel_request_owner_mismatch: ${channelRequestId} owned by a different runtime`
3139
- );
3140
- }
3141
- return { channelRequestId, result: existing.result };
3142
- }
3143
- const controllers = targets.map(() => new AbortController());
3144
- let resolveFn;
3145
- const result = new Promise((resolve) => {
3146
- resolveFn = resolve;
3147
- });
3148
- const timer = setTimeout(() => {
3149
- this.settle(channelRequestId, { kind: "cancelled", reason: "timeout" });
3150
- }, ttlMs);
3151
- if (typeof timer.unref === "function") timer.unref();
3152
- const entry = {
3153
- kind: "permission",
3180
+ const inspect = this.registry.inspect(
3154
3181
  channelRequestId,
3182
+ "permission",
3155
3183
  fingerprint,
3156
- ...req.runtimeId !== void 0 ? { runtimeId: req.runtimeId } : {},
3157
- controllers,
3158
- timer,
3159
- resolve: resolveFn,
3160
- result,
3161
- settled: false
3162
- };
3163
- this.pending.set(channelRequestId, entry);
3184
+ req.runtimeId
3185
+ );
3186
+ if (inspect.kind === "collision") {
3187
+ throw new Error(
3188
+ collisionMessage(channelRequestId, inspect.reason, "permission")
3189
+ );
3190
+ }
3191
+ if (inspect.kind === "attach") {
3192
+ return {
3193
+ channelRequestId,
3194
+ result: inspect.entry.result
3195
+ };
3196
+ }
3164
3197
  const fullReq = {
3165
3198
  channelRequestId,
3166
3199
  toolName: req.toolName,
3167
3200
  description: req.description,
3168
3201
  inputPreview: req.inputPreview
3169
3202
  };
3170
- targets.forEach((adapter, idx) => {
3171
- const ctrl = controllers[idx];
3172
- Promise.resolve().then(() => adapter.requestPermissionVerdict(fullReq, ctrl.signal)).then((res) => {
3173
- if (res.kind === "verdict") {
3174
- this.settle(channelRequestId, { ...res, channelId: adapter.id });
3175
- }
3176
- }).catch((err) => {
3177
- this.log?.(
3178
- "warn",
3179
- `adapter ${adapter.id} permission relay failed: ${err instanceof Error ? err.message : String(err)}`
3180
- );
3181
- });
3203
+ const result = this.broadcast({
3204
+ kind: "permission",
3205
+ channelRequestId,
3206
+ ttlMs: req.ttlMs ?? this.defaultTtlMs,
3207
+ runtimeId: req.runtimeId,
3208
+ fingerprint,
3209
+ targets,
3210
+ perAdapter: async (adapter, signal) => {
3211
+ const res = await adapter.requestPermissionVerdict(fullReq, signal);
3212
+ return res.kind === "verdict" ? { ...res, channelId: adapter.id } : null;
3213
+ }
3182
3214
  });
3183
3215
  return { channelRequestId, result };
3184
3216
  }
3185
3217
  requestQuestion(req) {
3186
3218
  const channelRequestId = req.channelRequestId ?? this.idFactory();
3187
- const ttlMs = req.ttlMs ?? this.defaultTtlMs;
3188
3219
  const targets = this.adapters().filter(
3189
3220
  (a) => a.capabilities.relayQuestion && typeof a.requestQuestionAnswer === "function"
3190
3221
  );
3191
3222
  if (targets.length === 0) {
3223
+ return { channelRequestId, result: Promise.resolve({ kind: "no_relay" }) };
3224
+ }
3225
+ const fingerprint = questionFingerprint(req);
3226
+ const inspect = this.registry.inspect(
3227
+ channelRequestId,
3228
+ "question",
3229
+ fingerprint,
3230
+ req.runtimeId
3231
+ );
3232
+ if (inspect.kind === "collision") {
3233
+ throw new Error(
3234
+ collisionMessage(channelRequestId, inspect.reason, "question")
3235
+ );
3236
+ }
3237
+ if (inspect.kind === "attach") {
3192
3238
  return {
3193
3239
  channelRequestId,
3194
- result: Promise.resolve({ kind: "no_relay" })
3240
+ result: inspect.entry.result
3195
3241
  };
3196
3242
  }
3197
- const fingerprint = questionFingerprint(req);
3198
- const existing = this.pending.get(channelRequestId);
3199
- if (existing) {
3200
- if (existing.kind !== "question") {
3201
- throw new Error(
3202
- `channel_request_id_collision: ${channelRequestId} is bound to a permission relay`
3203
- );
3204
- }
3205
- if (existing.fingerprint !== fingerprint) {
3206
- throw new Error(
3207
- `channel_request_id_collision: ${channelRequestId} payload mismatch`
3208
- );
3209
- }
3210
- if (existing.runtimeId !== req.runtimeId) {
3211
- throw new Error(
3212
- `channel_request_owner_mismatch: ${channelRequestId} owned by a different runtime`
3213
- );
3243
+ const fullReq = {
3244
+ channelRequestId,
3245
+ title: req.title,
3246
+ questions: req.questions
3247
+ };
3248
+ const result = this.broadcast({
3249
+ kind: "question",
3250
+ channelRequestId,
3251
+ ttlMs: req.ttlMs ?? this.defaultTtlMs,
3252
+ runtimeId: req.runtimeId,
3253
+ fingerprint,
3254
+ targets,
3255
+ perAdapter: async (adapter, signal) => {
3256
+ const res = await adapter.requestQuestionAnswer(fullReq, signal);
3257
+ return res.kind === "answer" ? { ...res, channelId: adapter.id } : null;
3214
3258
  }
3215
- return { channelRequestId, result: existing.result };
3216
- }
3259
+ });
3260
+ return { channelRequestId, result };
3261
+ }
3262
+ cancel(channelRequestId, reason, expectedRuntimeId) {
3263
+ return this.registry.cancel(channelRequestId, reason, expectedRuntimeId);
3264
+ }
3265
+ pendingCount() {
3266
+ return this.registry.count();
3267
+ }
3268
+ disposeAll(reason = "auto_resolved") {
3269
+ this.registry.disposeAll(reason);
3270
+ }
3271
+ broadcast(args) {
3272
+ const {
3273
+ kind,
3274
+ channelRequestId,
3275
+ ttlMs,
3276
+ runtimeId,
3277
+ fingerprint,
3278
+ targets,
3279
+ perAdapter
3280
+ } = args;
3217
3281
  const controllers = targets.map(() => new AbortController());
3218
3282
  let resolveFn;
3219
3283
  const result = new Promise((resolve) => {
3220
3284
  resolveFn = resolve;
3221
3285
  });
3222
3286
  const timer = setTimeout(() => {
3223
- this.settle(channelRequestId, { kind: "cancelled", reason: "timeout" });
3287
+ this.registry.settle(channelRequestId, {
3288
+ kind: "cancelled",
3289
+ reason: "timeout"
3290
+ });
3224
3291
  }, ttlMs);
3225
3292
  if (typeof timer.unref === "function") timer.unref();
3226
- const entry = {
3227
- kind: "question",
3293
+ this.registry.register({
3294
+ kind,
3228
3295
  channelRequestId,
3229
3296
  fingerprint,
3230
- ...req.runtimeId !== void 0 ? { runtimeId: req.runtimeId } : {},
3297
+ runtimeId,
3231
3298
  controllers,
3232
3299
  timer,
3233
3300
  resolve: resolveFn,
3234
3301
  result,
3235
3302
  settled: false
3236
- };
3237
- this.pending.set(channelRequestId, entry);
3238
- const fullReq = {
3239
- channelRequestId,
3240
- title: req.title,
3241
- questions: req.questions
3242
- };
3303
+ });
3243
3304
  targets.forEach((adapter, idx) => {
3244
3305
  const ctrl = controllers[idx];
3245
- Promise.resolve().then(() => adapter.requestQuestionAnswer(fullReq, ctrl.signal)).then((res) => {
3246
- if (res.kind === "answer") {
3247
- this.settle(channelRequestId, { ...res, channelId: adapter.id });
3306
+ Promise.resolve().then(() => perAdapter(adapter, ctrl.signal)).then((res) => {
3307
+ if (res !== null) {
3308
+ this.registry.settle(channelRequestId, res);
3248
3309
  }
3249
3310
  }).catch((err) => {
3250
3311
  this.log?.(
3251
3312
  "warn",
3252
- `adapter ${adapter.id} question relay failed: ${err instanceof Error ? err.message : String(err)}`
3313
+ `adapter ${adapter.id} ${kind} relay failed: ${err instanceof Error ? err.message : String(err)}`
3253
3314
  );
3254
3315
  });
3255
3316
  });
3256
- return { channelRequestId, result };
3257
- }
3258
- cancel(channelRequestId, reason, expectedRuntimeId) {
3259
- const entry = this.pending.get(channelRequestId);
3260
- if (!entry) return false;
3261
- if (expectedRuntimeId !== void 0 && entry.runtimeId !== void 0 && entry.runtimeId !== expectedRuntimeId) {
3262
- return false;
3263
- }
3264
- this.settle(channelRequestId, { kind: "cancelled", reason });
3265
- return true;
3266
- }
3267
- pendingCount() {
3268
- return this.pending.size;
3269
- }
3270
- disposeAll(reason = "auto_resolved") {
3271
- for (const id of [...this.pending.keys()]) {
3272
- this.cancel(id, reason);
3273
- }
3274
- }
3275
- settle(channelRequestId, result) {
3276
- const entry = this.pending.get(channelRequestId);
3277
- if (!entry || entry.settled) return;
3278
- entry.settled = true;
3279
- this.pending.delete(channelRequestId);
3280
- clearTimeout(entry.timer);
3281
- for (const ctrl of entry.controllers) {
3282
- if (!ctrl.signal.aborted) ctrl.abort();
3283
- }
3284
- entry.resolve(
3285
- result
3286
- );
3317
+ return result;
3287
3318
  }
3288
3319
  };
3289
3320
  function permissionFingerprint(req) {
@@ -3650,7 +3681,7 @@ async function startDaemon(opts) {
3650
3681
  reason: err.reason
3651
3682
  });
3652
3683
  }
3653
- const sidecarIds = new Set(sidecars.map((s) => s.name));
3684
+ const sidecarIds = new Set(sidecars.map((s) => s.instanceId));
3654
3685
  for (const channel of channelManager.listChannels()) {
3655
3686
  if (sidecarIds.has(channel.id)) continue;
3656
3687
  try {
@@ -3670,13 +3701,13 @@ async function startDaemon(opts) {
3670
3701
  }
3671
3702
  }
3672
3703
  for (const sidecar of sidecars) {
3673
- const existed = channelManager.listChannels().some((channel) => channel.id === sidecar.name);
3704
+ const existed = channelManager.listChannels().some((channel) => channel.id === sidecar.instanceId);
3674
3705
  if (existed) {
3675
3706
  try {
3676
- await channelManager.unregister(sidecar.name, "shutdown");
3707
+ await channelManager.unregister(sidecar.instanceId, "shutdown");
3677
3708
  } catch (err) {
3678
3709
  results.push({
3679
- id: sidecar.name,
3710
+ id: sidecar.instanceId,
3680
3711
  ok: false,
3681
3712
  action: "failed",
3682
3713
  reason: err instanceof Error ? err.message : String(err)
@@ -3687,7 +3718,7 @@ async function startDaemon(opts) {
3687
3718
  const built = instantiateAdapter(sidecar);
3688
3719
  if (!built.ok) {
3689
3720
  results.push({
3690
- id: sidecar.name,
3721
+ id: sidecar.instanceId,
3691
3722
  ok: false,
3692
3723
  action: "failed",
3693
3724
  reason: built.reason
@@ -3697,17 +3728,19 @@ async function startDaemon(opts) {
3697
3728
  try {
3698
3729
  await channelManager.register(built.adapter);
3699
3730
  results.push({
3700
- id: sidecar.name,
3731
+ id: sidecar.instanceId,
3701
3732
  ok: true,
3702
3733
  action: existed ? "replaced" : "registered"
3703
3734
  });
3704
3735
  if (!opts.silent) {
3705
- process.stdout.write(`athena-gateway: registered ${sidecar.name}
3706
- `);
3736
+ process.stdout.write(
3737
+ `athena-gateway: registered ${sidecar.instanceId}
3738
+ `
3739
+ );
3707
3740
  }
3708
3741
  } catch (err) {
3709
3742
  results.push({
3710
- id: sidecar.name,
3743
+ id: sidecar.instanceId,
3711
3744
  ok: false,
3712
3745
  action: "failed",
3713
3746
  reason: err instanceof Error ? err.message : String(err)
@@ -3728,7 +3761,7 @@ async function startDaemon(opts) {
3728
3761
  const built = instantiateAdapter(sidecar);
3729
3762
  if (!built.ok) {
3730
3763
  process.stderr.write(
3731
- `athena-gateway: ${sidecar.name}: ${built.reason}
3764
+ `athena-gateway: ${sidecar.instanceId}: ${built.reason}
3732
3765
  `
3733
3766
  );
3734
3767
  continue;
@@ -3736,12 +3769,14 @@ async function startDaemon(opts) {
3736
3769
  try {
3737
3770
  await channelManager.register(built.adapter);
3738
3771
  if (!opts.silent) {
3739
- process.stdout.write(`athena-gateway: registered ${sidecar.name}
3740
- `);
3772
+ process.stdout.write(
3773
+ `athena-gateway: registered ${sidecar.instanceId}
3774
+ `
3775
+ );
3741
3776
  }
3742
3777
  } catch (err) {
3743
3778
  process.stderr.write(
3744
- `athena-gateway: register ${sidecar.name} failed: ${err instanceof Error ? err.message : String(err)}
3779
+ `athena-gateway: register ${sidecar.instanceId} failed: ${err instanceof Error ? err.message : String(err)}
3745
3780
  `
3746
3781
  );
3747
3782
  }
@@ -139,14 +139,26 @@ function loadOne(name, filePath) {
139
139
  };
140
140
  }
141
141
  }
142
+ const kindRaw = obj["kind"];
143
+ if (kindRaw !== void 0 && (typeof kindRaw !== "string" || kindRaw.length === 0)) {
144
+ return { ok: false, reason: "kind must be a non-empty string" };
145
+ }
146
+ const kind = kindRaw ?? name;
147
+ const instanceIdRaw = obj["instance_id"];
148
+ if (instanceIdRaw !== void 0 && (typeof instanceIdRaw !== "string" || instanceIdRaw.length === 0)) {
149
+ return { ok: false, reason: "instance_id must be a non-empty string" };
150
+ }
151
+ const instanceId = instanceIdRaw ?? kind;
142
152
  const options = {};
143
153
  for (const [key, value] of Object.entries(obj)) {
144
- if (key === "allowed_user_ids") continue;
154
+ if (key === "allowed_user_ids" || key === "kind" || key === "instance_id") {
155
+ continue;
156
+ }
145
157
  options[key] = value;
146
158
  }
147
159
  return {
148
160
  ok: true,
149
- sidecar: { name, path: filePath, allowedUserIds, options }
161
+ sidecar: { name, path: filePath, kind, instanceId, allowedUserIds, options }
150
162
  };
151
163
  }
152
164
 
@@ -158,4 +170,4 @@ export {
158
170
  channelSidecarDir,
159
171
  loadChannelSidecars
160
172
  };
161
- //# sourceMappingURL=chunk-6TJHAUNB.js.map
173
+ //# sourceMappingURL=chunk-M44KEGM7.js.map