@fancyboi999/open-tag-daemon 0.4.0 → 0.5.1

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/cli.mjs +135 -4
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -5329,6 +5329,132 @@ var cursorRuntime = {
5329
5329
  }
5330
5330
  };
5331
5331
 
5332
+ // src/daemon/demoRuntime.ts
5333
+ import { randomUUID as randomUUID2 } from "node:crypto";
5334
+ var DEMO_NOTICE = "I'm a **demo agent** running on a no-op runtime \u2014 I don't call any LLM, so I can't do real work here. To get a real AI teammate, self-host open-tag and connect a runtime like Claude Code, Codex, or your own API key. See [getopentag.com](https://getopentag.com) to get started.";
5335
+ var GREETINGS = ["Hi there! \u{1F44B}", "Hello again! \u{1F44B}", "Hey! \u{1F44B}", "Thanks for your message! \u{1F44B}", "Received! \u{1F44B}"];
5336
+ function extractBody(text) {
5337
+ const m = /\] [^:]+: (.+)$/s.exec(text);
5338
+ return m ? m[1].trim() : "";
5339
+ }
5340
+ function extractTarget(text) {
5341
+ const m = /^\[target=(\S+)/.exec(text);
5342
+ return m ? m[1] : null;
5343
+ }
5344
+ function buildReply(userBody, turnIndex) {
5345
+ const greeting = GREETINGS[turnIndex % GREETINGS.length];
5346
+ const snippet = userBody.slice(0, 120);
5347
+ const tail = userBody.length > 120 ? "\u2026" : "";
5348
+ const echo = snippet ? `
5349
+
5350
+ > You said: "${snippet}${tail}"` : "";
5351
+ return `${greeting} ${DEMO_NOTICE}${echo}`;
5352
+ }
5353
+ function makeTransport(serverUrl2, agentToken, agentId) {
5354
+ const base = serverUrl2.replace(/\/$/, "");
5355
+ const headers = {
5356
+ "Authorization": `Bearer ${agentToken}`,
5357
+ "x-agent-id": agentId,
5358
+ "Content-Type": "application/json"
5359
+ };
5360
+ return {
5361
+ async check() {
5362
+ const r = await fetch(`${base}/agent-api/message/check`, { headers });
5363
+ const d = await r.json();
5364
+ return d.messages ?? [];
5365
+ },
5366
+ async send(target, content) {
5367
+ await fetch(`${base}/agent-api/message/send`, {
5368
+ method: "POST",
5369
+ headers,
5370
+ body: JSON.stringify({ target, content })
5371
+ });
5372
+ }
5373
+ };
5374
+ }
5375
+ var DemoRun = class {
5376
+ constructor(opts, cb, transport) {
5377
+ this.opts = opts;
5378
+ this.cb = cb;
5379
+ this.sessionId = opts.sessionId || randomUUID2();
5380
+ this.transport = transport ?? makeTransport(
5381
+ opts.env.OPEN_TAG_SERVER_URL ?? "",
5382
+ opts.env.OPEN_TAG_AGENT_TOKEN ?? "",
5383
+ opts.env.OPEN_TAG_AGENT_ID ?? ""
5384
+ );
5385
+ cb.onSession(this.sessionId);
5386
+ this.enqueue(opts.initialPrompt);
5387
+ }
5388
+ opts;
5389
+ cb;
5390
+ sessionId;
5391
+ queue = [];
5392
+ turnBusy = false;
5393
+ stopped = false;
5394
+ turnIndex = 0;
5395
+ transport;
5396
+ enqueue(text) {
5397
+ if (this.stopped) return;
5398
+ this.queue.push(text);
5399
+ this.pump();
5400
+ }
5401
+ pump() {
5402
+ if (this.stopped || this.turnBusy || this.queue.length === 0) return;
5403
+ this.runTurn(this.queue.shift());
5404
+ }
5405
+ // Each turn: check messages → for each pending target, build + post a demo reply → mark online.
5406
+ // Uses setImmediate to stay non-blocking before the await chain takes over.
5407
+ runTurn(_prompt) {
5408
+ this.turnBusy = true;
5409
+ this.cb.onActivity("working", "turn");
5410
+ setImmediate(() => void this.executeTurn());
5411
+ }
5412
+ async executeTurn() {
5413
+ if (this.stopped) {
5414
+ this.turnBusy = false;
5415
+ return;
5416
+ }
5417
+ try {
5418
+ const messages = await this.transport.check();
5419
+ if (!messages.length) {
5420
+ this.cb.onActivity("online", "");
5421
+ this.turnBusy = false;
5422
+ this.pump();
5423
+ return;
5424
+ }
5425
+ const byTarget = /* @__PURE__ */ new Map();
5426
+ for (const m of messages) {
5427
+ const target = extractTarget(m.text);
5428
+ if (!target) continue;
5429
+ byTarget.set(target, extractBody(m.text));
5430
+ }
5431
+ for (const [target, body] of byTarget) {
5432
+ const reply = buildReply(body, this.turnIndex++);
5433
+ this.cb.onTrajectory([{ kind: "text", text: reply }]);
5434
+ await this.transport.send(target, reply);
5435
+ }
5436
+ } catch (e) {
5437
+ this.cb.log.warn("demo: turn failed", { detail: String(e?.message ?? e) });
5438
+ }
5439
+ if (!this.stopped) {
5440
+ this.cb.onActivity("online", "");
5441
+ this.turnBusy = false;
5442
+ this.pump();
5443
+ }
5444
+ }
5445
+ stop() {
5446
+ this.stopped = true;
5447
+ }
5448
+ };
5449
+ var demoRuntime = {
5450
+ name: "demo",
5451
+ experimental: true,
5452
+ start(opts, cb) {
5453
+ const run = new DemoRun(opts, cb);
5454
+ return { deliver: (text) => run.enqueue(text), stop: () => run.stop() };
5455
+ }
5456
+ };
5457
+
5332
5458
  // src/daemon/runtimes.ts
5333
5459
  function has(tool) {
5334
5460
  try {
@@ -5340,9 +5466,9 @@ function has(tool) {
5340
5466
  }
5341
5467
  function detectRuntimes() {
5342
5468
  const found = ["claude", "codex", "copilot", "kimi", "opencode", "pi", "cursor-agent"].filter(has).map((t) => t === "cursor-agent" ? "cursor" : t);
5343
- return found;
5469
+ return [...found, "demo"];
5344
5470
  }
5345
- var REG = { claude: claudeRuntime, codex: codexRuntime, copilot: copilotRuntime, opencode: opencodeRuntime, kimi: kimiRuntime, pi: piRuntime, cursor: cursorRuntime };
5471
+ var REG = { claude: claudeRuntime, codex: codexRuntime, copilot: copilotRuntime, opencode: opencodeRuntime, kimi: kimiRuntime, pi: piRuntime, cursor: cursorRuntime, demo: demoRuntime };
5346
5472
  function getRuntime(name) {
5347
5473
  return REG[name] ?? null;
5348
5474
  }
@@ -5867,6 +5993,8 @@ async function listModels(runtime) {
5867
5993
  const models = parseCodexModels(r.stdout);
5868
5994
  return models.length ? models : null;
5869
5995
  }
5996
+ case "demo":
5997
+ return [{ id: "demo", label: "Demo", provider: "demo", default: true }];
5870
5998
  default:
5871
5999
  return null;
5872
6000
  }
@@ -5909,8 +6037,11 @@ conn = new Connection(serverUrl, apiKey, (msg) => {
5909
6037
  case "ready:ack":
5910
6038
  if (typeof msg.machineId === "string" && msg.machineId) saveMachineId(msg.machineId);
5911
6039
  break;
6040
+ // Agent dials the same server URL this daemon connected with (proven reachable), overriding the
6041
+ // server-reported config.serverUrl (SELF_URL = localhost:PORT on the server box — wrong whenever the
6042
+ // daemon runs on a different host than the server, e.g. local daemon ↔ getopentag.com).
5912
6043
  case "agent:start":
5913
- void mgr.start(msg.agentId, { ...msg.config });
6044
+ void mgr.start(msg.agentId, { ...msg.config, serverUrl });
5914
6045
  break;
5915
6046
  case "agent:deliver":
5916
6047
  mgr.deliver(msg.agentId, msg.from ?? "someone", msg.target ?? "", !!msg.mentioned, { targetName: msg.targetName, msgShort: msg.msgShort, isTask: msg.isTask });
@@ -5954,7 +6085,7 @@ conn = new Connection(serverUrl, apiKey, (msg) => {
5954
6085
  runningAgents: mgr.running(),
5955
6086
  hostname: os4.hostname(),
5956
6087
  os: `${os4.platform()} ${os4.arch()}`,
5957
- daemonVersion: "0.4.0",
6088
+ daemonVersion: "0.5.1",
5958
6089
  machineId: readMachineId()
5959
6090
  // Stable identity: empty on first connection; server sends it back via ready:ack for persistence.
5960
6091
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fancyboi999/open-tag-daemon",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "open-tag compute-plane daemon — connect any machine to an open-tag server so its agents run there. No repo clone needed.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",