@agentconnect.md/daemon 1.0.0-rc.321 → 1.0.0-rc.323
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.
- package/dist/{acp-host-Dsm0i9DH.js → acp-host-BEjpql6l.js} +2 -2
- package/dist/{acp-host-Dsm0i9DH.js.map → acp-host-BEjpql6l.js.map} +1 -1
- package/dist/{agent-list-Cs_EgUh5.js → agent-list-Ckqv2x26.js} +2 -2
- package/dist/{agent-list-Cs_EgUh5.js.map → agent-list-Ckqv2x26.js.map} +1 -1
- package/dist/{chat-Cq-5NzYK.js → chat-MTttQBSF.js} +3 -3
- package/dist/{chat-Cq-5NzYK.js.map → chat-MTttQBSF.js.map} +1 -1
- package/dist/{dist-BG425zHm.js → dist-D8I6ZfGL.js} +2 -1
- package/dist/dist-D8I6ZfGL.js.map +1 -0
- package/dist/{gh-token-VZeW2R2c.js → gh-token-CeVzSIMp.js} +2 -2
- package/dist/{gh-token-VZeW2R2c.js.map → gh-token-CeVzSIMp.js.map} +1 -1
- package/dist/index.js +5 -5
- package/dist/{load-agents-CROyMyq9.js → load-agents-C2pFahVp.js} +3 -2
- package/dist/load-agents-C2pFahVp.js.map +1 -0
- package/dist/{login-DYBS48no.js → login-D6xoGjQX.js} +4 -4
- package/dist/{login-DYBS48no.js.map → login-D6xoGjQX.js.map} +1 -1
- package/dist/{run-foreground-wCItuoBV.js → run-foreground-B5Kfk1KO.js} +93 -41
- package/dist/{run-foreground-wCItuoBV.js.map → run-foreground-B5Kfk1KO.js.map} +1 -1
- package/dist/run-foreground-D0Q64Gar.js +2 -0
- package/package.json +1 -1
- package/dist/dist-BG425zHm.js.map +0 -1
- package/dist/load-agents-CROyMyq9.js.map +0 -1
- package/dist/run-foreground-BwuBQd47.js +0 -2
package/dist/index.js
CHANGED
|
@@ -68492,9 +68492,9 @@ const telemetry = startDaemonOpenTelemetry({ serviceVersion: DAEMON_VERSION });
|
|
|
68492
68492
|
const [{ Command }, { fileURLToPath }, { runChat }, { runLogin }, { runForeground }, { resolveController }, { acquireSingletonLock }, { resolveRoot }] = await Promise.all([
|
|
68493
68493
|
import("./commander-B5QVEFzH.js"),
|
|
68494
68494
|
import("node:url"),
|
|
68495
|
-
import("./chat-
|
|
68496
|
-
import("./login-
|
|
68497
|
-
import("./run-foreground-
|
|
68495
|
+
import("./chat-MTttQBSF.js"),
|
|
68496
|
+
import("./login-D6xoGjQX.js"),
|
|
68497
|
+
import("./run-foreground-D0Q64Gar.js"),
|
|
68498
68498
|
import("./service-6w0WKhdx.js"),
|
|
68499
68499
|
import("./lock-CA9Bm8_s.js"),
|
|
68500
68500
|
import("./paths-CcRUjFjy.js")
|
|
@@ -68650,13 +68650,13 @@ program.command("git-credential").description("Git credential helper backed by t
|
|
|
68650
68650
|
await runGitCredential(action, agentId);
|
|
68651
68651
|
});
|
|
68652
68652
|
program.command("gh-token").description("Fetch a per-repo GH_TOKEN from the local daemon (gh wrapper backend)").argument("<agentId>", "agent whose credentials to serve").argument("[repo]", "target repo (owner/repo, host/owner/repo, or URL); absent = workspace").action(async (agentId, repo) => {
|
|
68653
|
-
const { runGhToken } = await import("./gh-token-
|
|
68653
|
+
const { runGhToken } = await import("./gh-token-CeVzSIMp.js");
|
|
68654
68654
|
await runGhToken(agentId, repo);
|
|
68655
68655
|
});
|
|
68656
68656
|
program.command("agent").description("Inspect local agent directories").command("list").description("List agents discovered under --agents-dir (id, status, runtime, name, dir)").action(async () => {
|
|
68657
68657
|
const opts = program.opts();
|
|
68658
68658
|
try {
|
|
68659
|
-
const { runAgentList } = await import("./agent-list-
|
|
68659
|
+
const { runAgentList } = await import("./agent-list-Ckqv2x26.js");
|
|
68660
68660
|
await runAgentList({
|
|
68661
68661
|
agentsDir: opts.agentsDir,
|
|
68662
68662
|
configPath: opts.config,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { _ as record, a as array, c as discriminatedUnion, d as literal, m as object, n as _enum, o as boolean, p as number, v as string } from "./schemas-CQkO9hC5.js";
|
|
2
|
-
import { m as RelayRosterEntry } from "./dist-
|
|
2
|
+
import { m as RelayRosterEntry } from "./dist-D8I6ZfGL.js";
|
|
3
3
|
import { l as resolveRoot, r as defaultAgentsDir, t as configPath } from "./paths-DEcUO8lC.js";
|
|
4
4
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
@@ -219,6 +219,7 @@ const CronDefSchema = object({
|
|
|
219
219
|
const AgentSchema = object({
|
|
220
220
|
id: string(),
|
|
221
221
|
name: string(),
|
|
222
|
+
displayName: string().optional(),
|
|
222
223
|
status: _enum([
|
|
223
224
|
"active",
|
|
224
225
|
"inactive",
|
|
@@ -398,4 +399,4 @@ function selectAgent(agentsDir, name) {
|
|
|
398
399
|
//#endregion
|
|
399
400
|
export { agentChildEnv as a, persistRelays as c, selectAgent as i, ConfigSchema as l, findAgentFiles as n, loadConfig as o, loadAgents as r, persistDaemonId as s, discoverAgents as t };
|
|
400
401
|
|
|
401
|
-
//# sourceMappingURL=load-agents-
|
|
402
|
+
//# sourceMappingURL=load-agents-C2pFahVp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-agents-C2pFahVp.js","names":["z.array","z.object","z.string","z\n .object","z.enum","z.literal","z\n .object","z.boolean","z.number","z.record","z\n .number","z.discriminatedUnion","z.object","z.literal","z.string","z.enum","z.array","z\n .object","z.boolean"],"sources":["../src/config/config-schema.ts","../src/config/load-config.ts","../src/agents/agent-schema.ts","../src/agents/agent-env.ts","../src/agents/load-agents.ts"],"sourcesContent":["import { z } from 'zod'\nimport { RelayRosterEntry } from '@agentconnect.md/protocol'\n\n/** The `{name, value}[]` shape shared by runtime env, MCP env, and MCP headers. */\nconst NameValueList = z.array(z.object({ name: z.string(), value: z.string() })).default([])\n\nexport const RuntimeDefSchema = z.object({\n command: z.string(),\n args: z.array(z.string()).default([]),\n env: NameValueList\n})\nexport type RuntimeDef = z.infer<typeof RuntimeDefSchema>\n\n// A daemon-configured MCP server, keyed by name in `config.mcpServers`. The name\n// is what an agent's `mcpServers` list references, and what the probe snapshot\n// reports to the CP — definitions (command/url/headers) never leave the daemon.\n// The name \"agentconnect\" is reserved for the daemon's own injected bridge entry.\nexport const McpServerDefSchema = z\n .object({\n transport: z.enum(['stdio', 'http', 'sse']).default('stdio'),\n // stdio transport: the executable to spawn (required for stdio).\n command: z.string().optional(),\n args: z.array(z.string()).default([]),\n env: NameValueList,\n // http/sse transports: the server endpoint (required for http/sse).\n url: z.string().optional(),\n headers: NameValueList\n })\n .superRefine((def, ctx) => {\n if (def.transport === 'stdio' && !def.command)\n ctx.addIssue({ code: 'custom', path: ['command'], message: 'a stdio MCP server requires \"command\"' })\n if (def.transport !== 'stdio' && !def.url)\n ctx.addIssue({ code: 'custom', path: ['url'], message: `a ${def.transport} MCP server requires \"url\"` })\n })\nexport type McpServerDef = z.infer<typeof McpServerDefSchema>\n\nexport const ConfigSchema = z.object({\n version: z.literal(1),\n daemonId: z.string().optional(),\n // Base URL of the Web App console; used to build the §9.1 \"details\" deep link\n // (`<webAppUrl>/sessions/<sessionId>`). Unset ⇒ the daemon adopts the URL the CP sends\n // down on `auth/ok` (the CP is authoritative for its own console origin); only truly\n // absent when neither is set. A local config value wins over the CP-provided one.\n webAppUrl: z.string().optional(),\n controlPlane: z\n .object({\n enabled: z.boolean().default(true),\n url: z.string().optional(),\n key: z.string().optional(), // CP API key (opaque); sent as `apiKey` on the auth frame\n heartbeatMs: z.number().int().default(15000)\n })\n .default({ enabled: false, heartbeatMs: 15000 }),\n agentsDir: z.string().optional(), // resolved against root if absent\n runtimes: z.record(z.string(), RuntimeDefSchema).optional(),\n // MCP servers this daemon can attach to agent sessions (health-probed + reported\n // to the CP as facts; agents opt in by name via their `mcpServers` list).\n mcpServers: z.record(z.string(), McpServerDefSchema).optional(),\n // Relay roster the CP last published (shared-bot-relay.md §5). Persisted whole so\n // the daemon can re-dial its relays at boot while the CP is unreachable (graceful\n // degradation); the CP's register/ok snapshot re-converges it authoritatively once\n // connected. CP-owned — overwritten on every roster converge, not hand-edited.\n relays: z.array(RelayRosterEntry).default([]),\n logging: z\n .object({ level: z.enum(['trace', 'debug', 'info', 'warn', 'error']).default('info') })\n .default({ level: 'info' }),\n limits: z\n .object({\n maxAgents: z.number().int().default(8),\n maxConcurrentSessions: z.number().int().default(32),\n agentIdleTimeoutMs: z.number().int().default(900_000),\n // How often the idle sweep runs: reaps idle ACP adapter children back to\n // `provisioned` (§7.2) and TTL-closes idle sessions (§7.3). Keep well below\n // agentIdleTimeoutMs so a host lingers at most one interval past its window.\n idleSweepMs: z.number().int().default(60_000),\n // SIGTERM/daemon-drain grace window: in-flight turns get this long to finish\n // before the daemon cancels stragglers and tears children down (§2.5/§5.3).\n shutdownDrainMs: z.number().int().default(25_000),\n // §7.3 force-cancel backstop: after `!stop` we send session/cancel and wait\n // this long; if the turn still hasn't yielded, we force-stop the host.\n cancelBackstopMs: z.number().int().default(30_000),\n // How many times to (re)try launching an agent's ACP host — spawn + the\n // `initialize` handshake — before giving up and surfacing the failure to the\n // session. Covers transient failures (a resource race, a slow cold start). A\n // deterministic failure (missing binary) just burns all attempts then reports.\n agentStartAttempts: z.number().int().min(1).default(3),\n // Fixed backoff between agent-start attempts.\n agentStartBackoffMs: z.number().int().min(0).default(500),\n // Cap (bytes) for inlining an inbound attachment into the ACP prompt.\n // Files larger than this are passed as a resource_link pointer, never\n // downloaded/base64'd — bounds daemon RSS and the prompt frame size.\n maxAttachmentBytes: z\n .number()\n .int()\n .default(8 * 1024 * 1024)\n })\n .default({\n maxAgents: 8,\n maxConcurrentSessions: 32,\n agentIdleTimeoutMs: 900_000,\n idleSweepMs: 60_000,\n shutdownDrainMs: 25_000,\n cancelBackstopMs: 30_000,\n agentStartAttempts: 3,\n agentStartBackoffMs: 500,\n maxAttachmentBytes: 8 * 1024 * 1024\n })\n})\nexport type Config = z.infer<typeof ConfigSchema>\n","import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport type { RelayRosterEntry } from '@agentconnect.md/protocol'\nimport { ConfigSchema, type Config } from './config-schema.js'\nimport { resolveRoot, configPath, defaultAgentsDir } from '../paths.js'\n\nexport interface FlatOverrides {\n cpUrl?: string\n cpKey?: string\n noCp?: boolean\n daemonId?: string\n logLevel?: Config['logging']['level']\n agentsDir?: string\n maxAgents?: number\n}\n\nexport function loadConfig(\n opts: { root?: string; configPath?: string; overrides?: FlatOverrides; optional?: boolean; autoCreate?: boolean } = {}\n): Config {\n const root = resolveRoot(opts.root)\n const file = opts.configPath ?? configPath(root)\n // `optional` (used by `chat`) lets the daemon run with zero config: a missing\n // config.json yields the schema defaults, and runtimes fall back to the ACP registry.\n // `autoCreate` (used by `run`) goes a step further and writes that empty config to\n // disk so the daemon runs fully local (control plane disabled by default) and the\n // user has a file to edit later — no `agentconnect login` required.\n let raw: unknown\n if (existsSync(file)) {\n raw = JSON.parse(readFileSync(file, 'utf8'))\n } else if (opts.autoCreate) {\n raw = { version: 1 }\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(raw, null, 2) + '\\n')\n } else if (opts.optional) {\n raw = { version: 1 }\n } else {\n throw new Error(`config not found: ${file} (create it, pass --config, or run \\`agentconnect login\\`)`)\n }\n const cfg = ConfigSchema.parse(raw) // throws on invalid\n\n const o = opts.overrides ?? {}\n if (o.daemonId) cfg.daemonId = o.daemonId\n if (o.logLevel) cfg.logging.level = o.logLevel\n if (o.maxAgents !== undefined) cfg.limits.maxAgents = o.maxAgents\n if (o.cpUrl) cfg.controlPlane.url = o.cpUrl\n if (o.cpKey) cfg.controlPlane.key = o.cpKey\n // Passing --cp-url/--cp-key implies \"connect to the CP\" (it defaults off),\n // so the one-line onboarding command works without a config edit. --no-cp wins.\n if (o.cpUrl || o.cpKey) cfg.controlPlane.enabled = true\n if (o.noCp) cfg.controlPlane.enabled = false\n\n cfg.agentsDir = o.agentsDir ?? cfg.agentsDir ?? defaultAgentsDir(root)\n return cfg\n}\n\n/**\n * Persist a (freshly-minted) `daemonId` back into config.json so it is stable\n * per install. Best-effort: a write failure is swallowed (the daemon still runs\n * with the in-memory id this session).\n */\nexport function persistDaemonId(root: string | undefined, daemonId: string): void {\n try {\n const file = configPath(resolveRoot(root))\n const raw = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : { version: 1 }\n raw.daemonId = daemonId\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(raw, null, 2) + '\\n')\n } catch {\n // ignore — non-fatal\n }\n}\n\n/**\n * Persist the CP-published relay roster back into config.json so the daemon can\n * re-dial its relays at boot while the CP is unreachable (graceful degradation).\n * Whole-set (CP-owned): overwrites any prior value, so a swept relay is cleared.\n * Best-effort — a write failure is swallowed (the in-memory roster still drives\n * this session's dials).\n */\nexport function persistRelays(root: string | undefined, relays: RelayRosterEntry[]): void {\n try {\n const file = configPath(resolveRoot(root))\n const raw = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : { version: 1 }\n raw.relays = relays\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(raw, null, 2) + '\\n')\n } catch {\n // ignore — non-fatal\n }\n}\n","import { z } from 'zod'\n\nexport const BindMatchSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('mention') }),\n z.object({ kind: z.literal('dm') }),\n z.object({ kind: z.literal('keyword'), value: z.string() }),\n z.object({ kind: z.literal('auto') })\n])\nexport type BindMatch = z.infer<typeof BindMatchSchema>\n\nexport const BindRuleConfigSchema = z.object({\n channel: z.string().optional(), // absent = any channel\n thread: z.string().optional(),\n match: BindMatchSchema\n})\nexport type BindRuleConfig = z.infer<typeof BindRuleConfigSchema>\n\nexport const SlackConfigSchema = z.object({\n // 'direct' (default, and the shape of every pre-shared-bot agent.json): the daemon\n // opens the Socket Mode connection itself. 'shared': the bot's inbound lives on a\n // relay, so the daemon holds xoxb ONLY (send path) and opens no socket — routing is\n // arbitrated in the relay and delivered pre-addressed. See shared-bot-relay.md §7.3.\n mode: z.enum(['direct', 'shared']).default('direct'),\n botToken: z.string(),\n appToken: z.string().optional(), // direct only (Socket Mode); absent for shared\n signingSecret: z.string().optional(),\n botUserId: z.string().optional(), // filled at connect via auth.test if absent; provided by CP for shared\n allowedUserIds: z.array(z.string()).default([]),\n bindRules: z.array(BindRuleConfigSchema).default([])\n})\nexport type SlackConfig = z.infer<typeof SlackConfigSchema>\n\nexport const TelegramConfigSchema = z.object({\n botToken: z.string(), // BotFather \"123456:ABC…\" (single token; no app token / signing secret)\n botUserId: z.string().optional(), // numeric bot id, filled at connect via getMe if absent\n botUsername: z.string().optional(), // @username without the '@', for mention detection; filled via getMe\n allowedUserIds: z.array(z.string()).default([]),\n bindRules: z.array(BindRuleConfigSchema).default([])\n})\nexport type TelegramConfig = z.infer<typeof TelegramConfigSchema>\n\nexport const DiscordConfigSchema = z.object({\n botToken: z.string(), // Discord Gateway bot token (single token; no app token / signing secret)\n applicationId: z.string().optional(), // public client id for the invite URL (not used to connect)\n botUserId: z.string().optional(), // numeric bot user id, filled at connect via the ready event if absent\n allowedUserIds: z.array(z.string()).default([]),\n bindRules: z.array(BindRuleConfigSchema).default([])\n})\nexport type DiscordConfig = z.infer<typeof DiscordConfigSchema>\n\nexport const IntegrationSchema = z.discriminatedUnion('platform', [\n z.object({\n id: z.string(),\n platform: z.literal('slack'),\n slack: SlackConfigSchema\n }),\n z.object({\n id: z.string(),\n platform: z.literal('telegram'),\n telegram: TelegramConfigSchema\n }),\n z.object({\n id: z.string(),\n platform: z.literal('discord'),\n discord: DiscordConfigSchema\n })\n])\nexport type Integration = z.infer<typeof IntegrationSchema>\n\n/** A scheduled trigger for THIS agent: every `schedule` tick, prompt the agent\n * with `trigger`. `target` is optional output routing — when present the daemon\n * posts the trigger into that channel and the session replies in its thread;\n * absent ⇒ headless fire (no platform output). `origin:\"cp\"` marks CP-pushed\n * entries (written by cron/upsert, pruned by drop.crons); hand-authored entries\n * have no origin and are never touched by the CP. */\nexport const CronDefSchema = z.object({\n id: z.string(),\n schedule: z.string(),\n // integrationId picks which of the agent's integrations posts the anchor;\n // absent (legacy defs) ⇒ the agent's first integration.\n target: z\n .object({\n platform: z.enum(['slack', 'telegram', 'discord']),\n channel: z.string(),\n integrationId: z.string().optional()\n })\n .optional(),\n trigger: z.string(),\n enabled: z.boolean().default(true),\n origin: z.literal('cp').optional()\n})\nexport type CronDef = z.infer<typeof CronDefSchema>\n\nexport const AgentSchema = z.object({\n id: z.string(),\n name: z.string(),\n // Optional human-facing bot name. `name` remains the stable agent identifier;\n // the CP may set or clear this field independently via AgentSpec.displayName.\n displayName: z.string().optional(),\n status: z.enum(['active', 'inactive', 'paused']).default('active'),\n // Operational message-processing toggle, orthogonal to `status` (which gates\n // whether the agent is loaded/placed at all). When true the agent still loads and\n // connects its platform bot, but the daemon skips ALL turn dispatch (platform,\n // webchat, cron) — see Daemon.dispatch. Turning it on also cancels in-flight\n // turns and drops their queued follow-ups (including their durable inbox rows, so\n // restart cannot resurrect them), without tearing down the warm host or ACP sessions.\n // Silent: skipped turns are dropped, not recorded. A flip remains\n // a soft-only reconcile change (no host/session teardown).\n pause: z.boolean().default(false),\n runtime: z.string(),\n // System-prompt seed + runtime knobs. Settable in agent.json and overlaid by\n // the CP spec (agent/upsert + register/ok roster) — see cp/cp-agent-registry.ts.\n description: z.string().optional(),\n reasoningEffort: z.string().optional(),\n executionMode: z.string().optional(),\n // Runtime fast mode (ACP `model_config` toggle, claude/codex). Absent ⇒ leave\n // the runtime's own default; the daemon only pushes an explicit on/off.\n fastMode: z.boolean().optional(),\n // Runtime permission/approval mode (ACP `mode` selector). The values are\n // runtime-owned strings: claude-acp uses default/acceptEdits/auto/dontAsk/plan,\n // codex-acp uses read-only/agent/agent-full-access.\n permissionMode: z.string().default('default'),\n runtimeOverrides: z\n .object({\n model: z.string().optional(),\n env: z.array(z.object({ name: z.string(), value: z.string() })).default([])\n })\n .optional(),\n // Names of daemon-configured MCP servers (daemon config `mcpServers`) to attach\n // at ACP session/new|load, after the daemon's own bridge entry. Empty ⇒ none;\n // unknown names are skipped with a warn (see mcp/resolve-servers.ts).\n mcpServers: z.array(z.string()).default([]),\n // Agent→agent call authorization (design §2.5), replicated from the CP so the\n // daemon enforces it LOCALLY when another agent uses `messageAgent` to wake this\n // one. `all` (the default) ⇒ any org peer may call; `selected` ⇒ only agents in\n // `allowedCallerAgentIds`. Absent callPolicy ⇒ treated as `all` (backward-compat;\n // see §6.5 for the fail-closed alternative — noted as a P1 gap).\n callPolicy: z.enum(['all', 'selected']).default('all'),\n allowedCallerAgentIds: z.array(z.string()).default([]),\n // Which memory backend this agent uses (see agents/memory-provider.ts). Absent ⇒\n // managed (the default): our <agent-root>/memory/ directory. `native` redirects\n // the runtime's own memory under the agent root; `external` is not yet built.\n memory: z.object({ provider: z.enum(['native', 'managed', 'external']).default('managed') }).optional(),\n workspace: z.object({\n mode: z.enum(['git-repo', 'from-scratch']),\n path: z.string(),\n gitRepo: z.string().optional(), // full cloneable address (e.g. https://github.com/acme/infra)\n gitBranch: z.string().default('main'),\n // Remote-git credential mode. Absent ⇒ anonymous (public repos). 'github-app' ⇒\n // clone/fetch/push authenticate via the local credential helper backed by\n // CP-minted short-lived installation tokens — nothing durable on this host.\n gitCredential: z.enum(['github-app']).optional(),\n pullOnNewSession: z.boolean().default(true),\n skills: z.array(z.string()).default([])\n }),\n integrations: z.array(IntegrationSchema).default([]),\n // zod 4: nested .default({}) does not apply inner field defaults — use explicit full literal\n output: z.object({ mode: z.enum(['low', 'medium', 'high']).default('low') }).default({ mode: 'low' }),\n permissions: z\n .object({ policy: z.enum(['ask', 'auto']).default('ask'), autoApprove: z.array(z.string()).default([]) })\n .default({ policy: 'ask', autoApprove: [] }),\n crons: z.array(CronDefSchema).default([])\n})\nexport type Agent = z.infer<typeof AgentSchema>\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Agent } from './agent-schema.js'\n\n// Minimal .env parser: KEY=VALUE per line, `#` comments, blank lines,\n// optional `export ` prefix, matching single/double quotes stripped.\n// No variable expansion within the file. Last duplicate key wins.\nexport function parseDotenv(text: string): Record<string, string> {\n const out: Record<string, string> = {}\n for (const rawLine of text.split('\\n')) {\n const line = rawLine.replace(/\\r$/, '').trim()\n if (!line || line.startsWith('#')) continue\n const body = line.startsWith('export ') ? line.slice(7).trim() : line\n const eq = body.indexOf('=')\n if (eq === -1) continue\n const key = body.slice(0, eq).trim()\n if (!key) continue\n let value = body.slice(eq + 1).trim()\n const q = value[0]\n if (value.length >= 2 && (q === '\"' || q === \"'\") && value.at(-1) === q) {\n value = value.slice(1, -1)\n }\n out[key] = value\n }\n return out\n}\n\n// Parse `<dir>/.env`, or {} if the file does not exist.\nexport function loadDotenv(dir: string): Record<string, string> {\n const file = join(dir, '.env')\n if (!existsSync(file)) return {}\n return parseDotenv(readFileSync(file, 'utf8'))\n}\n\nconst VAR_RE = /\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g\n\nfunction interpolateString(\n value: string,\n source: Record<string, string | undefined>,\n path: string,\n ctx: { id: string; envPath: string }\n): string {\n return value.replace(VAR_RE, (_m, name: string) => {\n const resolved = source[name]\n if (resolved === undefined) {\n throw new Error(`agent \"${ctx.id}\": unresolved \\${${name}} in ${path} (.env: ${ctx.envPath})`)\n }\n return resolved\n })\n}\n\nfunction walk(\n node: unknown,\n source: Record<string, string | undefined>,\n path: string,\n ctx: { id: string; envPath: string }\n): unknown {\n if (typeof node === 'string') return interpolateString(node, source, path, ctx)\n if (Array.isArray(node)) {\n for (let i = 0; i < node.length; i++) node[i] = walk(node[i], source, `${path}[${i}]`, ctx)\n return node\n }\n if (node && typeof node === 'object') {\n const obj = node as Record<string, unknown>\n for (const key of Object.keys(obj)) {\n obj[key] = walk(obj[key], source, path ? `${path}.${key}` : key, ctx)\n }\n return node\n }\n return node\n}\n\n// Rewrite every ${VAR} in every string of `agent` (in place). Throws on the\n// first unresolved variable.\nexport function interpolateAgent(\n agent: Agent,\n source: Record<string, string | undefined>,\n ctx: { id: string; envPath: string }\n): void {\n walk(agent, source, '', ctx)\n}\n\n// Per-agent env overlay applied on top of runtime.env when spawning the ACP\n// child. .env values, then runtimeOverrides.env (which wins).\nexport function agentChildEnv(agent: {\n env: Record<string, string>\n runtimeOverrides?: { env?: { name: string; value: string }[] }\n}): Record<string, string> {\n return {\n ...agent.env,\n ...Object.fromEntries((agent.runtimeOverrides?.env ?? []).map((e) => [e.name, e.value]))\n }\n}\n","import { readFileSync, readdirSync, existsSync } from 'node:fs'\nimport { join, resolve, isAbsolute, dirname } from 'node:path'\nimport { AgentSchema, type Agent } from './agent-schema.js'\nimport { loadDotenv, interpolateAgent } from './agent-env.js'\n\nconst IGNORED_DIRS = new Set(['node_modules', '.git'])\nconst MAX_DEPTH = 4\n\n// Agent plus loader-derived data: its directory and parsed .env.\nexport type LoadedAgent = Agent & { dir: string; env: Record<string, string> }\n\n// Parse one agent.json, interpolate ${VAR} from the sibling .env (+ process.env),\n// then resolve workspace.path relative to the agent dir.\nfunction parseAgentFile(file: string): LoadedAgent {\n const dir = dirname(file)\n const env = loadDotenv(dir)\n const agent = AgentSchema.parse(JSON.parse(readFileSync(file, 'utf8')))\n interpolateAgent(agent, { ...process.env, ...env }, { id: agent.id, envPath: join(dir, '.env') })\n if (!isAbsolute(agent.workspace.path)) {\n agent.workspace.path = resolve(dir, agent.workspace.path)\n }\n return { ...agent, dir, env }\n}\n\n// Bounded recursive walk: collect every agent.json under `dir`. A directory that\n// contains an agent.json is treated as a leaf (we do not recurse into it), so an\n// agent's own workspace checkout can't masquerade as nested agents.\nexport function findAgentFiles(dir: string, depth = 0): string[] {\n if (depth > MAX_DEPTH || !existsSync(dir)) return []\n let entries\n try {\n entries = readdirSync(dir, { withFileTypes: true })\n } catch {\n return []\n }\n if (entries.some((e) => e.isFile() && e.name === 'agent.json')) {\n return [join(dir, 'agent.json')]\n }\n const out: string[] = []\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n if (IGNORED_DIRS.has(entry.name) || entry.name.startsWith('.')) continue\n out.push(...findAgentFiles(join(dir, entry.name), depth + 1))\n }\n return out\n}\n\n// All parsed agents under `agentsDir`, no status filter. `dir` is the directory\n// holding each agent.json.\nexport function discoverAgents(agentsDir: string): { agent: LoadedAgent; dir: string }[] {\n return findAgentFiles(agentsDir).map((file) => {\n try {\n return { agent: parseAgentFile(file), dir: dirname(file) }\n } catch (err) {\n throw new Error(`invalid agent.json at ${file}: ${(err as Error).message}`)\n }\n })\n}\n\n// Active agents only — the daemon's multi-agent path.\nexport function loadAgents(agentsDir: string): LoadedAgent[] {\n return discoverAgents(agentsDir)\n .map((d) => d.agent)\n .filter((a) => a.status === 'active')\n}\n\n// Resolve a single agent from `agentsDir`. With `name`, matches the agent `id`.\n// Without `name`: requires exactly one discovered agent. Used by `chat` and by\n// `run --agent`. Does NOT filter by status.\nexport function selectAgent(agentsDir: string, name?: string): LoadedAgent {\n const agents = discoverAgents(agentsDir).map((d) => d.agent)\n if (name) {\n const match = agents.find((a) => a.id === name)\n if (!match) {\n const available =\n agents\n .map((a) => a.id)\n .sort()\n .join(', ') || '(none)'\n throw new Error(`agent \"${name}\" not found in ${agentsDir}. Available: ${available}`)\n }\n return match\n }\n if (agents.length === 0) throw new Error(`no agent.json found in ${agentsDir}`)\n if (agents.length > 1) {\n const ids = agents\n .map((a) => a.id)\n .sort()\n .join(', ')\n throw new Error(`multiple agents found in ${agentsDir}: ${ids}; use --agent <name> to specify one`)\n }\n return agents[0]!\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as DAEMON_VERSION } from "./version-DGaRXjBK.js";
|
|
2
|
-
import { d as encode, l as buildEnvelope, t as isFrame, u as decodeEnvelope } from "./dist-
|
|
3
|
-
import { i as ClientTransport, t as runForeground } from "./run-foreground-
|
|
4
|
-
import { l as ConfigSchema } from "./load-agents-
|
|
2
|
+
import { d as encode, l as buildEnvelope, t as isFrame, u as decodeEnvelope } from "./dist-D8I6ZfGL.js";
|
|
3
|
+
import { i as ClientTransport, t as runForeground } from "./run-foreground-B5Kfk1KO.js";
|
|
4
|
+
import { l as ConfigSchema } from "./load-agents-C2pFahVp.js";
|
|
5
5
|
import { l as resolveRoot, t as configPath } from "./paths-DEcUO8lC.js";
|
|
6
6
|
import { n as resolveController } from "./service-BQVw7wbz.js";
|
|
7
7
|
import { dirname } from "node:path";
|
|
@@ -229,4 +229,4 @@ async function runLogin(opts, partial = {}) {
|
|
|
229
229
|
//#endregion
|
|
230
230
|
export { buildInstallOpts, persistCredentials, runLogin };
|
|
231
231
|
|
|
232
|
-
//# sourceMappingURL=login-
|
|
232
|
+
//# sourceMappingURL=login-D6xoGjQX.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-DYBS48no.js","names":["realRunForeground"],"sources":["../src/cp/auth-probe.ts","../src/cli/login.ts"],"sourcesContent":["/**\n * `probeAuth` — a one-shot CP auth check used by `agentconnect login`. It dials\n * the CP, sends ONLY the `auth` frame (no `register`), and resolves with the\n * authoritative daemonId from `auth/ok`, or a human-readable failure reason on\n * a `4401` close, a correlated `error`, a dial failure, or a timeout. It always\n * closes the socket and never leaves a connection open.\n */\nimport { buildEnvelope, decodeEnvelope, encode, isFrame } from '@agentconnect.md/protocol'\nimport { ClientTransport, type Transport } from '@agentconnect.md/connection'\nimport { CP_SUBPROTOCOL, CP_WS_PATH } from './client.js'\n\nexport interface ProbeResult {\n ok: boolean\n daemonId?: string\n reason?: string\n}\n\nexport interface ProbeOpts {\n url: string\n token: string\n daemonId?: string\n agentVersion?: string\n timeoutMs?: number\n /** Injectable for tests; defaults to dialing the real WebSocket. */\n connect?: () => Promise<Transport>\n}\n\nexport async function probeAuth(opts: ProbeOpts): Promise<ProbeResult> {\n const timeoutMs = opts.timeoutMs ?? 10_000\n const dial = opts.connect ?? (() => ClientTransport.dial(opts.url, { subprotocol: CP_SUBPROTOCOL, path: CP_WS_PATH }))\n\n let transport: Transport\n try {\n transport = await dial()\n } catch (err) {\n return { ok: false, reason: `cannot reach ${opts.url}: ${(err as Error).message}` }\n }\n\n const authPayload: Record<string, unknown> = {\n apiKey: opts.token,\n agentVersion: opts.agentVersion ?? 'probe'\n }\n if (opts.daemonId) authPayload.daemonId = opts.daemonId\n const frame = buildEnvelope('auth', authPayload)\n\n return await new Promise<ProbeResult>((resolve) => {\n let settled = false\n const done = (r: ProbeResult): void => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n try {\n transport.close(1000, 'probe done')\n } catch {\n // socket may already be closed\n }\n resolve(r)\n }\n\n const timer = setTimeout(\n () => done({ ok: false, reason: `timed out after ${timeoutMs}ms waiting for auth/ok` }),\n timeoutMs\n )\n\n transport.onMessage((text) => {\n const decoded = decodeEnvelope(text)\n if (!decoded.ok) return\n const f = decoded.frame\n if (f.corr !== frame.id) return\n if (f.type === 'error') {\n const e = f.payload as { code: string; message: string }\n done({ ok: false, reason: `${e.code}: ${e.message}` })\n return\n }\n if (isFrame('auth/ok')(f)) {\n const ok = f.payload as { daemonId: string }\n done({ ok: true, daemonId: ok.daemonId })\n }\n })\n\n transport.onClose((code) => {\n if (code === 4401) done({ ok: false, reason: 'authentication failed (4401) — check the daemon token' })\n else done({ ok: false, reason: `connection closed (${code}) before auth/ok` })\n })\n\n transport.send(encode(frame))\n })\n}\n","/**\n * `agentconnect login` — interactive onboarding. It tests the Control Plane auth\n * flow, persists credentials only on success, then offers to install the OS\n * service. If installed it starts the service (`up`) and returns; otherwise it\n * runs the daemon in the foreground (the same path as `agentconnect run`).\n *\n * `persistCredentials` is the pure config-merge half (token-only onboarding:\n * `daemonId` is omitted unless given — the first connect adopts it from the\n * token's `sub`). `runLogin` is the orchestrator, with injectable deps for tests.\n */\nimport { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport { fileURLToPath } from 'node:url'\nimport { ConfigSchema } from '../config/config-schema.js'\nimport { resolveRoot, configPath } from '../paths.js'\nimport { DAEMON_VERSION } from '../version.js'\nimport { probeAuth, type ProbeResult } from '../cp/auth-probe.js'\nimport { resolveController, type InstallOpts } from '../service/index.js'\nimport { runForeground as realRunForeground } from './run-foreground.js'\n\nexport interface PersistCredsOpts {\n cpUrl: string\n cpKey: string\n daemonId?: string\n root?: string\n configPath?: string\n}\n\n/** Merge url/token into config.json (validated), returning the written path. */\nexport function persistCredentials(opts: PersistCredsOpts): string {\n const root = resolveRoot(opts.root)\n const file = opts.configPath ?? configPath(root)\n const raw: Record<string, unknown> = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : { version: 1 }\n\n const cp = (raw.controlPlane as Record<string, unknown> | undefined) ?? {}\n raw.controlPlane = { ...cp, url: opts.cpUrl, key: opts.cpKey, enabled: true }\n if (opts.daemonId) raw.daemonId = opts.daemonId\n\n ConfigSchema.parse(raw) // fail before writing if the merge is invalid\n\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(raw, null, 2) + '\\n')\n return file\n}\n\nexport interface RunLoginOpts {\n cpUrl?: string\n cpKey?: string\n daemonId?: string\n root?: string\n configPath?: string\n /** The resolved CLI entry path. Must be supplied by the CLI entry point\n * (index.ts) so the correct bundle path is used rather than inferring it\n * from login.ts's own import.meta.url (which is the same bundle but the\n * `../` path calculation would be wrong). Falls back to import.meta.url\n * for the pure test path where login.ts is loaded standalone. */\n cliEntry?: string\n}\n\n/**\n * Build the InstallOpts for the OS service installer.\n *\n * Exported as a pure helper so it can be unit-tested independently of the\n * full `runLogin` orchestration. The caller (index.ts) passes `cliEntry` so\n * the correct bundle path always flows through; if omitted we fall back to\n * `import.meta.url` (correct only when login.ts is the entry, e.g. in tests).\n */\nexport function buildInstallOpts(opts: { root?: string; cliEntry?: string }): InstallOpts {\n return {\n execPath: process.execPath,\n cliEntry: opts.cliEntry ?? fileURLToPath(import.meta.url),\n includeRootEnv: resolveRoot(opts.root) !== resolveRoot(undefined)\n }\n}\n\nexport interface LoginDeps {\n probe: (o: { url: string; token: string }) => Promise<ProbeResult>\n installService: () => Promise<void>\n runForeground: () => Promise<void>\n out: NodeJS.WritableStream\n input: NodeJS.ReadableStream\n isTTY: boolean\n}\n\nfunction realDeps(opts: RunLoginOpts): LoginDeps {\n return {\n probe: (o) => probeAuth({ url: o.url, token: o.token, agentVersion: DAEMON_VERSION }),\n installService: async () => {\n const controller = resolveController({ root: opts.root })\n await controller.install(buildInstallOpts(opts))\n await controller.up()\n },\n runForeground: () =>\n realRunForeground({\n root: opts.root,\n overrides: { cpUrl: opts.cpUrl, cpKey: opts.cpKey, daemonId: opts.daemonId }\n }),\n out: process.stdout,\n input: process.stdin,\n isTTY: Boolean(process.stdin.isTTY)\n }\n}\n\n/** Read the next line from a readline interface via async iterator. */\nasync function nextLine(\n iter: AsyncIterableIterator<string>,\n prompt: string,\n out: NodeJS.WritableStream\n): Promise<string> {\n out.write(prompt)\n const { value, done } = await iter.next()\n if (done) throw new Error(`unexpected end of input waiting for: ${prompt}`)\n return (value as string).trim()\n}\n\nexport async function runLogin(opts: RunLoginOpts, partial: Partial<LoginDeps> = {}): Promise<void> {\n const deps: LoginDeps = { ...realDeps(opts), ...partial }\n const { out } = deps\n\n // ── Non-interactive: flags required; probe; persist. No prompts, no handoff. ──\n if (!deps.isTTY) {\n if (!opts.cpUrl) throw new Error('login requires --cp-url <url>')\n if (!opts.cpKey) throw new Error('login requires --cp-key <key>')\n const r = await deps.probe({ url: opts.cpUrl, token: opts.cpKey })\n if (!r.ok) throw new Error(`authentication failed: ${r.reason}`)\n persistCredentials({ ...opts, cpUrl: opts.cpUrl, cpKey: opts.cpKey })\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''} — credentials saved\\n`)\n return\n }\n\n // Both interactive handoffs read `<root>/config.json`: the installed service\n // runs `… run` (no `--config`), and the foreground `Daemon` loads by root. A\n // custom `--config` would persist credentials the daemon never reads, so the\n // install/run branch would look successful but start without them. Reject it;\n // `--root` is the knob that keeps persist + service + foreground in sync.\n if (opts.configPath) {\n throw new Error(\n 'interactive login does not support --config — use --root <dir> so the daemon and the installed service read the same config'\n )\n }\n\n // ── Interactive ──\n const rl = createInterface({ input: deps.input, output: out })\n const iter = rl[Symbol.asyncIterator]()\n let url = opts.cpUrl\n let token = opts.cpKey\n try {\n if (!url) url = await nextLine(iter, 'Control Plane URL: ', out)\n\n for (let attempt = 0; ; attempt++) {\n if (!token) token = await nextLine(iter, 'Daemon API key: ', out)\n out.write(`Testing connection to ${url}…\\n`)\n const r = await deps.probe({ url: url!, token: token! })\n if (r.ok) {\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''}\\n`)\n break\n }\n out.write(`✗ ${r.reason}\\n`)\n if (attempt >= 1) throw new Error('authentication failed')\n token = undefined // re-prompt on retry\n }\n\n persistCredentials({ ...opts, cpUrl: url!, cpKey: token! })\n\n const ans = (await nextLine(iter, 'Install AgentConnect as a background service? (y/N) ', out)).toLowerCase()\n if (ans === 'y' || ans === 'yes') {\n await deps.installService()\n out.write('Service installed and started. Manage it with `agentconnect up` / `down` / `status`.\\n')\n return\n }\n out.write('Starting in the foreground (Ctrl-C to stop)…\\n')\n } finally {\n rl.close()\n }\n\n await deps.runForeground()\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"login-D6xoGjQX.js","names":["realRunForeground"],"sources":["../src/cp/auth-probe.ts","../src/cli/login.ts"],"sourcesContent":["/**\n * `probeAuth` — a one-shot CP auth check used by `agentconnect login`. It dials\n * the CP, sends ONLY the `auth` frame (no `register`), and resolves with the\n * authoritative daemonId from `auth/ok`, or a human-readable failure reason on\n * a `4401` close, a correlated `error`, a dial failure, or a timeout. It always\n * closes the socket and never leaves a connection open.\n */\nimport { buildEnvelope, decodeEnvelope, encode, isFrame } from '@agentconnect.md/protocol'\nimport { ClientTransport, type Transport } from '@agentconnect.md/connection'\nimport { CP_SUBPROTOCOL, CP_WS_PATH } from './client.js'\n\nexport interface ProbeResult {\n ok: boolean\n daemonId?: string\n reason?: string\n}\n\nexport interface ProbeOpts {\n url: string\n token: string\n daemonId?: string\n agentVersion?: string\n timeoutMs?: number\n /** Injectable for tests; defaults to dialing the real WebSocket. */\n connect?: () => Promise<Transport>\n}\n\nexport async function probeAuth(opts: ProbeOpts): Promise<ProbeResult> {\n const timeoutMs = opts.timeoutMs ?? 10_000\n const dial = opts.connect ?? (() => ClientTransport.dial(opts.url, { subprotocol: CP_SUBPROTOCOL, path: CP_WS_PATH }))\n\n let transport: Transport\n try {\n transport = await dial()\n } catch (err) {\n return { ok: false, reason: `cannot reach ${opts.url}: ${(err as Error).message}` }\n }\n\n const authPayload: Record<string, unknown> = {\n apiKey: opts.token,\n agentVersion: opts.agentVersion ?? 'probe'\n }\n if (opts.daemonId) authPayload.daemonId = opts.daemonId\n const frame = buildEnvelope('auth', authPayload)\n\n return await new Promise<ProbeResult>((resolve) => {\n let settled = false\n const done = (r: ProbeResult): void => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n try {\n transport.close(1000, 'probe done')\n } catch {\n // socket may already be closed\n }\n resolve(r)\n }\n\n const timer = setTimeout(\n () => done({ ok: false, reason: `timed out after ${timeoutMs}ms waiting for auth/ok` }),\n timeoutMs\n )\n\n transport.onMessage((text) => {\n const decoded = decodeEnvelope(text)\n if (!decoded.ok) return\n const f = decoded.frame\n if (f.corr !== frame.id) return\n if (f.type === 'error') {\n const e = f.payload as { code: string; message: string }\n done({ ok: false, reason: `${e.code}: ${e.message}` })\n return\n }\n if (isFrame('auth/ok')(f)) {\n const ok = f.payload as { daemonId: string }\n done({ ok: true, daemonId: ok.daemonId })\n }\n })\n\n transport.onClose((code) => {\n if (code === 4401) done({ ok: false, reason: 'authentication failed (4401) — check the daemon token' })\n else done({ ok: false, reason: `connection closed (${code}) before auth/ok` })\n })\n\n transport.send(encode(frame))\n })\n}\n","/**\n * `agentconnect login` — interactive onboarding. It tests the Control Plane auth\n * flow, persists credentials only on success, then offers to install the OS\n * service. If installed it starts the service (`up`) and returns; otherwise it\n * runs the daemon in the foreground (the same path as `agentconnect run`).\n *\n * `persistCredentials` is the pure config-merge half (token-only onboarding:\n * `daemonId` is omitted unless given — the first connect adopts it from the\n * token's `sub`). `runLogin` is the orchestrator, with injectable deps for tests.\n */\nimport { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport { fileURLToPath } from 'node:url'\nimport { ConfigSchema } from '../config/config-schema.js'\nimport { resolveRoot, configPath } from '../paths.js'\nimport { DAEMON_VERSION } from '../version.js'\nimport { probeAuth, type ProbeResult } from '../cp/auth-probe.js'\nimport { resolveController, type InstallOpts } from '../service/index.js'\nimport { runForeground as realRunForeground } from './run-foreground.js'\n\nexport interface PersistCredsOpts {\n cpUrl: string\n cpKey: string\n daemonId?: string\n root?: string\n configPath?: string\n}\n\n/** Merge url/token into config.json (validated), returning the written path. */\nexport function persistCredentials(opts: PersistCredsOpts): string {\n const root = resolveRoot(opts.root)\n const file = opts.configPath ?? configPath(root)\n const raw: Record<string, unknown> = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : { version: 1 }\n\n const cp = (raw.controlPlane as Record<string, unknown> | undefined) ?? {}\n raw.controlPlane = { ...cp, url: opts.cpUrl, key: opts.cpKey, enabled: true }\n if (opts.daemonId) raw.daemonId = opts.daemonId\n\n ConfigSchema.parse(raw) // fail before writing if the merge is invalid\n\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(raw, null, 2) + '\\n')\n return file\n}\n\nexport interface RunLoginOpts {\n cpUrl?: string\n cpKey?: string\n daemonId?: string\n root?: string\n configPath?: string\n /** The resolved CLI entry path. Must be supplied by the CLI entry point\n * (index.ts) so the correct bundle path is used rather than inferring it\n * from login.ts's own import.meta.url (which is the same bundle but the\n * `../` path calculation would be wrong). Falls back to import.meta.url\n * for the pure test path where login.ts is loaded standalone. */\n cliEntry?: string\n}\n\n/**\n * Build the InstallOpts for the OS service installer.\n *\n * Exported as a pure helper so it can be unit-tested independently of the\n * full `runLogin` orchestration. The caller (index.ts) passes `cliEntry` so\n * the correct bundle path always flows through; if omitted we fall back to\n * `import.meta.url` (correct only when login.ts is the entry, e.g. in tests).\n */\nexport function buildInstallOpts(opts: { root?: string; cliEntry?: string }): InstallOpts {\n return {\n execPath: process.execPath,\n cliEntry: opts.cliEntry ?? fileURLToPath(import.meta.url),\n includeRootEnv: resolveRoot(opts.root) !== resolveRoot(undefined)\n }\n}\n\nexport interface LoginDeps {\n probe: (o: { url: string; token: string }) => Promise<ProbeResult>\n installService: () => Promise<void>\n runForeground: () => Promise<void>\n out: NodeJS.WritableStream\n input: NodeJS.ReadableStream\n isTTY: boolean\n}\n\nfunction realDeps(opts: RunLoginOpts): LoginDeps {\n return {\n probe: (o) => probeAuth({ url: o.url, token: o.token, agentVersion: DAEMON_VERSION }),\n installService: async () => {\n const controller = resolveController({ root: opts.root })\n await controller.install(buildInstallOpts(opts))\n await controller.up()\n },\n runForeground: () =>\n realRunForeground({\n root: opts.root,\n overrides: { cpUrl: opts.cpUrl, cpKey: opts.cpKey, daemonId: opts.daemonId }\n }),\n out: process.stdout,\n input: process.stdin,\n isTTY: Boolean(process.stdin.isTTY)\n }\n}\n\n/** Read the next line from a readline interface via async iterator. */\nasync function nextLine(\n iter: AsyncIterableIterator<string>,\n prompt: string,\n out: NodeJS.WritableStream\n): Promise<string> {\n out.write(prompt)\n const { value, done } = await iter.next()\n if (done) throw new Error(`unexpected end of input waiting for: ${prompt}`)\n return (value as string).trim()\n}\n\nexport async function runLogin(opts: RunLoginOpts, partial: Partial<LoginDeps> = {}): Promise<void> {\n const deps: LoginDeps = { ...realDeps(opts), ...partial }\n const { out } = deps\n\n // ── Non-interactive: flags required; probe; persist. No prompts, no handoff. ──\n if (!deps.isTTY) {\n if (!opts.cpUrl) throw new Error('login requires --cp-url <url>')\n if (!opts.cpKey) throw new Error('login requires --cp-key <key>')\n const r = await deps.probe({ url: opts.cpUrl, token: opts.cpKey })\n if (!r.ok) throw new Error(`authentication failed: ${r.reason}`)\n persistCredentials({ ...opts, cpUrl: opts.cpUrl, cpKey: opts.cpKey })\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''} — credentials saved\\n`)\n return\n }\n\n // Both interactive handoffs read `<root>/config.json`: the installed service\n // runs `… run` (no `--config`), and the foreground `Daemon` loads by root. A\n // custom `--config` would persist credentials the daemon never reads, so the\n // install/run branch would look successful but start without them. Reject it;\n // `--root` is the knob that keeps persist + service + foreground in sync.\n if (opts.configPath) {\n throw new Error(\n 'interactive login does not support --config — use --root <dir> so the daemon and the installed service read the same config'\n )\n }\n\n // ── Interactive ──\n const rl = createInterface({ input: deps.input, output: out })\n const iter = rl[Symbol.asyncIterator]()\n let url = opts.cpUrl\n let token = opts.cpKey\n try {\n if (!url) url = await nextLine(iter, 'Control Plane URL: ', out)\n\n for (let attempt = 0; ; attempt++) {\n if (!token) token = await nextLine(iter, 'Daemon API key: ', out)\n out.write(`Testing connection to ${url}…\\n`)\n const r = await deps.probe({ url: url!, token: token! })\n if (r.ok) {\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''}\\n`)\n break\n }\n out.write(`✗ ${r.reason}\\n`)\n if (attempt >= 1) throw new Error('authentication failed')\n token = undefined // re-prompt on retry\n }\n\n persistCredentials({ ...opts, cpUrl: url!, cpKey: token! })\n\n const ans = (await nextLine(iter, 'Install AgentConnect as a background service? (y/N) ', out)).toLowerCase()\n if (ans === 'y' || ans === 'yes') {\n await deps.installService()\n out.write('Service installed and started. Manage it with `agentconnect up` / `down` / `status`.\\n')\n return\n }\n out.write('Starting in the foreground (Ctrl-C to stop)…\\n')\n } finally {\n rl.close()\n }\n\n await deps.runForeground()\n}\n"],"mappings":"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"}
|
|
@@ -3,10 +3,10 @@ import { n as require_ms, t as require_src$3 } from "./src-DDtlVutj.js";
|
|
|
3
3
|
import { n as init_esm_shims, t as __dirname } from "./esm-shims-DxGcm4Hw.js";
|
|
4
4
|
import { t as DAEMON_VERSION } from "./version-DGaRXjBK.js";
|
|
5
5
|
import { C as _coercedNumber, T as NEVER, a as array, d as literal, f as looseObject, i as any, m as object, o as boolean, p as number$1, t as ZodNumber, v as string, x as url$4 } from "./schemas-CQkO9hC5.js";
|
|
6
|
-
import { a as RELAY_DAEMON_WS_PATH, c as SHARED_CONFIG_ACTION_ID, d as encode, f as MAX_FRAME_BYTES, g as ZodIssueCode, h as RESERVED_MCP_SERVER_NAME, i as RELAY_DAEMON_SUBPROTOCOL, l as buildEnvelope, n as gitRepoLabel, o as buildRelayDaemonFrame, r as normalizeGitUrl, s as decodeRelayDaemonFrame, u as decodeEnvelope } from "./dist-
|
|
7
|
-
import { a as agentChildEnv, c as persistRelays, i as selectAgent, n as findAgentFiles, o as loadConfig, r as loadAgents, s as persistDaemonId } from "./load-agents-
|
|
6
|
+
import { a as RELAY_DAEMON_WS_PATH, c as SHARED_CONFIG_ACTION_ID, d as encode, f as MAX_FRAME_BYTES, g as ZodIssueCode, h as RESERVED_MCP_SERVER_NAME, i as RELAY_DAEMON_SUBPROTOCOL, l as buildEnvelope, n as gitRepoLabel, o as buildRelayDaemonFrame, r as normalizeGitUrl, s as decodeRelayDaemonFrame, u as decodeEnvelope } from "./dist-D8I6ZfGL.js";
|
|
7
|
+
import { a as agentChildEnv, c as persistRelays, i as selectAgent, n as findAgentFiles, o as loadConfig, r as loadAgents, s as persistDaemonId } from "./load-agents-C2pFahVp.js";
|
|
8
8
|
import { l as resolveRoot, o as mcpSocketPath, u as statePath } from "./paths-DEcUO8lC.js";
|
|
9
|
-
import { C as cachedRuntimeVersions, S as cachedRuntimeNames, _ as resolveInMemoryDir, a as prepareWorkspace, b as MEMORY_TOOL_NAMES, c as sessionGitEnv, d as memoryKindOf, f as memoryProviderFor, g as listMemory, h as MemoryTooLargeError, i as prefetchWorkspace, l as simpleGit, m as MemoryPathError, n as turnFailureReason, o as initGitInjection, p as MemoryConflictError, r as installedRuntimes, s as probeGitVersion, t as AcpHost, u as createMemoryProvider, v as writeMemoryFile, w as resolveRuntimes, x as toolsForIntegrations, y as ALL_TOOL_NAMES } from "./acp-host-
|
|
9
|
+
import { C as cachedRuntimeVersions, S as cachedRuntimeNames, _ as resolveInMemoryDir, a as prepareWorkspace, b as MEMORY_TOOL_NAMES, c as sessionGitEnv, d as memoryKindOf, f as memoryProviderFor, g as listMemory, h as MemoryTooLargeError, i as prefetchWorkspace, l as simpleGit, m as MemoryPathError, n as turnFailureReason, o as initGitInjection, p as MemoryConflictError, r as installedRuntimes, s as probeGitVersion, t as AcpHost, u as createMemoryProvider, v as writeMemoryFile, w as resolveRuntimes, x as toolsForIntegrations, y as ALL_TOOL_NAMES } from "./acp-host-BEjpql6l.js";
|
|
10
10
|
import { i as GitCredentialCache, n as gitcredSocketPath, r as writeGitcredShim, t as GitCredServer } from "./gitcred-server-Bu-Svl7L.js";
|
|
11
11
|
import { B as ToolListChangedNotificationSchema, D as ListPromptsResultSchema, E as ListChangedOptionsBaseSchema, F as PromptListChangedNotificationSchema, G as isZ4Schema, H as isJSONRPCRequest, I as ReadResourceResultSchema, J as encodeFrame, K as safeParse, L as ResourceListChangedNotificationSchema, M as ListToolsResultSchema, O as ListResourceTemplatesResultSchema, P as McpError, R as SUPPORTED_PROTOCOL_VERSIONS, S as InitializeResultSchema, T as LATEST_PROTOCOL_VERSION, U as isJSONRPCResultResponse, V as isInitializedNotification, W as getObjectShape, _ as ElicitResultSchema, a as AjvJsonSchemaValidator, b as GetPromptResultSchema, c as mergeCapabilities, d as CompleteResultSchema, f as CreateMessageRequestSchema, g as ElicitRequestSchema, h as CreateTaskResultSchema, i as assertToolsCallTaskCapability, k as ListResourcesResultSchema, m as CreateMessageResultWithToolsSchema, n as serializeMessage, o as require_fast_deep_equal, p as CreateMessageResultSchema, q as decodeFrames, r as assertClientRequestTaskCapability, s as Protocol, t as ReadBuffer, u as CallToolResultSchema, v as EmptyResultSchema, w as JSONRPCMessageSchema, y as ErrorCode } from "./stdio-DnS8WcXi.js";
|
|
12
12
|
import { createRequire } from "node:module";
|
|
@@ -6364,7 +6364,7 @@ var LocalStore = class {
|
|
|
6364
6364
|
acpSessionId TEXT, state TEXT, lastDeliveredTs TEXT, updatedAt INTEGER,
|
|
6365
6365
|
usage TEXT, muted INTEGER, triggeredBy TEXT, title TEXT, modelOverride TEXT,
|
|
6366
6366
|
effortOverride TEXT, permissionModeOverride TEXT, fastModeOverride INTEGER,
|
|
6367
|
-
outputModeOverride TEXT
|
|
6367
|
+
outputModeOverride TEXT, statusBarTs TEXT
|
|
6368
6368
|
);
|
|
6369
6369
|
-- A !stop can arrive while a cold session is still materializing, before the
|
|
6370
6370
|
-- sessions row exists. Keep the mute independently keyed so that stop survives a
|
|
@@ -6515,13 +6515,10 @@ var LocalStore = class {
|
|
|
6515
6515
|
this.db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS transcript_tool_call
|
|
6516
6516
|
ON transcript (channel, thread, tool_call_id) WHERE tool_call_id IS NOT NULL`);
|
|
6517
6517
|
}
|
|
6518
|
-
/** Add the
|
|
6519
|
-
*
|
|
6520
|
-
*
|
|
6521
|
-
*
|
|
6522
|
-
* permission-mode / fast-mode choices) columns to a pre-existing DB. CREATE TABLE IF
|
|
6523
|
-
* NOT EXISTS above adds them for fresh DBs but never alters an existing table, so
|
|
6524
|
-
* upgrade in place. No-op once the columns are present. */
|
|
6518
|
+
/** Add session columns introduced after the initial schema: usage JSON, mute/title,
|
|
6519
|
+
* sticky runtime controls, and Slack chrome pointers. CREATE TABLE IF NOT EXISTS
|
|
6520
|
+
* above adds them for fresh DBs but never alters an existing table, so upgrade in
|
|
6521
|
+
* place. No-op once the columns are present. */
|
|
6525
6522
|
migrateSessionUsage() {
|
|
6526
6523
|
const cols = this.db.prepare("PRAGMA table_info(sessions)").all();
|
|
6527
6524
|
if (!cols.some((c) => c.name === "usage")) this.db.exec("ALTER TABLE sessions ADD COLUMN usage TEXT");
|
|
@@ -6533,6 +6530,7 @@ var LocalStore = class {
|
|
|
6533
6530
|
if (!cols.some((c) => c.name === "permissionModeOverride")) this.db.exec("ALTER TABLE sessions ADD COLUMN permissionModeOverride TEXT");
|
|
6534
6531
|
if (!cols.some((c) => c.name === "fastModeOverride")) this.db.exec("ALTER TABLE sessions ADD COLUMN fastModeOverride INTEGER");
|
|
6535
6532
|
if (!cols.some((c) => c.name === "outputModeOverride")) this.db.exec("ALTER TABLE sessions ADD COLUMN outputModeOverride TEXT");
|
|
6533
|
+
if (!cols.some((c) => c.name === "statusBarTs")) this.db.exec("ALTER TABLE sessions ADD COLUMN statusBarTs TEXT");
|
|
6536
6534
|
}
|
|
6537
6535
|
/**
|
|
6538
6536
|
* Pre-`kind` transcript tables keyed on (channel, thread, ts) hold only conversational
|
|
@@ -6666,6 +6664,14 @@ var LocalStore = class {
|
|
|
6666
6664
|
setSessionTitle(key, title) {
|
|
6667
6665
|
this.db.prepare("UPDATE sessions SET title = ? WHERE key = ?").run(title, key);
|
|
6668
6666
|
}
|
|
6667
|
+
/** Slack status-bar message ts for this session, if one has been posted already. */
|
|
6668
|
+
getStatusBarTs(key) {
|
|
6669
|
+
return this.db.prepare("SELECT statusBarTs FROM sessions WHERE key = ?").get(key)?.statusBarTs ?? void 0;
|
|
6670
|
+
}
|
|
6671
|
+
/** Remember the first Slack status-bar message so later turns edit it in place. */
|
|
6672
|
+
setStatusBarTs(key, ts) {
|
|
6673
|
+
this.db.prepare("UPDATE sessions SET statusBarTs = COALESCE(statusBarTs, ?) WHERE key = ?").run(ts, key);
|
|
6674
|
+
}
|
|
6669
6675
|
isSessionMuted(key) {
|
|
6670
6676
|
return this.db.prepare(`SELECT CASE
|
|
6671
6677
|
WHEN EXISTS (SELECT 1 FROM session_mutes WHERE key = ?)
|
|
@@ -68089,7 +68095,7 @@ function compactCount$2(n) {
|
|
|
68089
68095
|
}
|
|
68090
68096
|
/** Render the compact in-thread status line as one `markdown` block — e.g.
|
|
68091
68097
|
* `:bar_chart: *opus-4.8* · fast · ctx 120k/200k (60%) · 45.2k tok`. Deliberately narrow:
|
|
68092
|
-
* effort and cost are omitted here (they live in the
|
|
68098
|
+
* effort and cost are omitted here (they live in the Configure modal) to keep the line short in a
|
|
68093
68099
|
* Slack thread. Pure: unknown fields are dropped so a partial snapshot still yields a clean
|
|
68094
68100
|
* line, and an empty snapshot degrades to a bare `:bar_chart: —` placeholder. */
|
|
68095
68101
|
function renderStatusBar(info) {
|
|
@@ -68301,31 +68307,47 @@ function buildElicitationResolvedCard(params, decision) {
|
|
|
68301
68307
|
}
|
|
68302
68308
|
}];
|
|
68303
68309
|
}
|
|
68304
|
-
/**
|
|
68305
|
-
*
|
|
68306
|
-
*
|
|
68307
|
-
* `View session` link appended when a `link` is known, and whose `accessory` is the ⚙
|
|
68308
|
-
* Manage button (opens the controls modal). Section text + one accessory share a row, so
|
|
68309
|
-
* status + View link + Manage all sit on one line. The session key rides the button
|
|
68310
|
-
* `value`. Pure — safe to unit-test.
|
|
68311
|
-
*/
|
|
68310
|
+
/** Build the compact in-thread status message: a live status `section`, followed by a
|
|
68311
|
+
* small controls row. Slack mrkdwn links can only open URLs, not dispatch block_actions,
|
|
68312
|
+
* so Configure/Cancel are real Block Kit buttons; View Session remains a URL button. */
|
|
68312
68313
|
function buildStatusBlocks(info, sessionKey, link) {
|
|
68314
|
+
const elements = [];
|
|
68315
|
+
if (link) elements.push({
|
|
68316
|
+
type: "button",
|
|
68317
|
+
action_id: STATUS_ACTION.view,
|
|
68318
|
+
text: {
|
|
68319
|
+
type: "plain_text",
|
|
68320
|
+
text: "View Session"
|
|
68321
|
+
},
|
|
68322
|
+
url: link
|
|
68323
|
+
});
|
|
68324
|
+
elements.push({
|
|
68325
|
+
type: "button",
|
|
68326
|
+
action_id: STATUS_ACTION.manage,
|
|
68327
|
+
text: {
|
|
68328
|
+
type: "plain_text",
|
|
68329
|
+
text: "Configure"
|
|
68330
|
+
},
|
|
68331
|
+
value: sessionKey
|
|
68332
|
+
}, {
|
|
68333
|
+
type: "button",
|
|
68334
|
+
action_id: STATUS_ACTION.cancel,
|
|
68335
|
+
text: {
|
|
68336
|
+
type: "plain_text",
|
|
68337
|
+
text: "Cancel"
|
|
68338
|
+
},
|
|
68339
|
+
style: "danger",
|
|
68340
|
+
value: sessionKey
|
|
68341
|
+
});
|
|
68313
68342
|
return [{
|
|
68314
68343
|
type: "section",
|
|
68315
68344
|
text: {
|
|
68316
68345
|
type: "mrkdwn",
|
|
68317
|
-
text:
|
|
68318
|
-
},
|
|
68319
|
-
accessory: {
|
|
68320
|
-
type: "button",
|
|
68321
|
-
action_id: STATUS_ACTION.manage,
|
|
68322
|
-
text: {
|
|
68323
|
-
type: "plain_text",
|
|
68324
|
-
text: ":gear:",
|
|
68325
|
-
emoji: true
|
|
68326
|
-
},
|
|
68327
|
-
value: sessionKey
|
|
68346
|
+
text: renderStatusBar(info)
|
|
68328
68347
|
}
|
|
68348
|
+
}, {
|
|
68349
|
+
type: "actions",
|
|
68350
|
+
elements
|
|
68329
68351
|
}];
|
|
68330
68352
|
}
|
|
68331
68353
|
/** Format a token count with thousands separators for the modal's usage grid. */
|
|
@@ -68333,7 +68355,7 @@ function fmtCount(n) {
|
|
|
68333
68355
|
return n === void 0 ? void 0 : n.toLocaleString("en-US");
|
|
68334
68356
|
}
|
|
68335
68357
|
/**
|
|
68336
|
-
* Build the controls modal opened from
|
|
68358
|
+
* Build the controls modal opened from Configure (a snapshot — Slack modals don't
|
|
68337
68359
|
* stream). `private_metadata` carries the session key so the modal's `block_actions`
|
|
68338
68360
|
* (model/effort/fast/output selects, Cancel) resolve the session. Blocks: the model /
|
|
68339
68361
|
* effort / fast / output selectors (each omitted when not applicable), a labeled usage
|
|
@@ -199138,6 +199160,8 @@ function findAgentFileById(agentsDir, agentId) {
|
|
|
199138
199160
|
*/
|
|
199139
199161
|
function applySpecFields(raw, spec, opts) {
|
|
199140
199162
|
raw.name = spec.name;
|
|
199163
|
+
if (spec.displayName !== void 0) if (spec.displayName === null) delete raw.displayName;
|
|
199164
|
+
else raw.displayName = spec.displayName;
|
|
199141
199165
|
if (spec.description !== void 0) raw.description = spec.description;
|
|
199142
199166
|
if (spec.runtime !== void 0) raw.runtime = spec.runtime;
|
|
199143
199167
|
if (spec.reasoningEffort !== void 0) if (spec.reasoningEffort === null) delete raw.reasoningEffort;
|
|
@@ -215586,6 +215610,9 @@ function usesLoopGuard(msg) {
|
|
|
215586
215610
|
function isMalformedPlatformTurn(msg) {
|
|
215587
215611
|
return msg.platform === "slack" && msg.source === "user" && !msg.sender.isBot && msg.sender.id === "unknown" && msg.text.trim() === "" && (msg.attachments?.length ?? 0) === 0;
|
|
215588
215612
|
}
|
|
215613
|
+
function isSlackStatusBarText(text) {
|
|
215614
|
+
return text.startsWith(":bar_chart:") || text.startsWith("📊");
|
|
215615
|
+
}
|
|
215589
215616
|
/** Build the wire SessionKey (protocol §5) for a pending turn — what `drain/done`
|
|
215590
215617
|
* reports as released so the CP may reassign it. Uses the real `thread` (absent for
|
|
215591
215618
|
* a channel-root message), NOT `statusThread` (which falls back to msgId): the CP
|
|
@@ -218434,6 +218461,7 @@ var Daemon = class Daemon {
|
|
|
218434
218461
|
}
|
|
218435
218462
|
if (callMeta?.correlationId !== void 0) this.recordWorkerReport(key, callMeta, msg.text);
|
|
218436
218463
|
const agent = this.agents.get(agentId);
|
|
218464
|
+
const agentName = agent.displayName?.trim() || agent.name;
|
|
218437
218465
|
const rec = new TranscriptRecorder();
|
|
218438
218466
|
const replyConn = msg.headless || webchat ? void 0 : this.replyConnFor(agentId, integrationId);
|
|
218439
218467
|
const wasRunning = this.hostStarts.has(agentId);
|
|
@@ -218470,7 +218498,7 @@ var Daemon = class Daemon {
|
|
|
218470
218498
|
this.showActivity(replyConn, msg.channel, statusThread, "");
|
|
218471
218499
|
} else this.surfaceTurnFailure(err, {
|
|
218472
218500
|
agentId,
|
|
218473
|
-
agentName
|
|
218501
|
+
agentName,
|
|
218474
218502
|
platform: msg.platform,
|
|
218475
218503
|
isDm: msg.isDm,
|
|
218476
218504
|
webchat,
|
|
@@ -218524,7 +218552,7 @@ var Daemon = class Daemon {
|
|
|
218524
218552
|
conv,
|
|
218525
218553
|
rec,
|
|
218526
218554
|
agentId,
|
|
218527
|
-
agentName
|
|
218555
|
+
agentName,
|
|
218528
218556
|
platform: msg.platform,
|
|
218529
218557
|
isDm: msg.isDm,
|
|
218530
218558
|
loopGuardScope: loopGuardScope(msg),
|
|
@@ -218636,7 +218664,7 @@ var Daemon = class Daemon {
|
|
|
218636
218664
|
if (p.webchat) {
|
|
218637
218665
|
this.surfaceTurnFailure(err, {
|
|
218638
218666
|
agentId,
|
|
218639
|
-
agentName
|
|
218667
|
+
agentName,
|
|
218640
218668
|
platform: msg.platform,
|
|
218641
218669
|
isDm: msg.isDm,
|
|
218642
218670
|
webchat,
|
|
@@ -218903,15 +218931,39 @@ var Daemon = class Daemon {
|
|
|
218903
218931
|
p.reasoningTs = await conn.postMessage(p.channel, action.text, p.thread, postOptions);
|
|
218904
218932
|
}
|
|
218905
218933
|
return;
|
|
218906
|
-
case "status-bar":
|
|
218907
|
-
|
|
218908
|
-
|
|
218934
|
+
case "status-bar": {
|
|
218935
|
+
let ts = p.statusBarTs ?? this.store.getStatusBarTs(p.sessionKey);
|
|
218936
|
+
if (!ts && !p.statusBarAttempted) {
|
|
218937
|
+
ts = await this.findExistingSlackStatusBarTs(conn, p);
|
|
218938
|
+
if (ts) {
|
|
218939
|
+
p.statusBarTs = ts;
|
|
218940
|
+
this.store.setStatusBarTs(p.sessionKey, ts);
|
|
218941
|
+
}
|
|
218942
|
+
}
|
|
218943
|
+
if (ts) {
|
|
218944
|
+
p.statusBarTs = ts;
|
|
218945
|
+
await conn.updateBlocks(p.channel, ts, action.blocks, action.text);
|
|
218946
|
+
} else if (!p.statusBarAttempted) {
|
|
218909
218947
|
p.statusBarAttempted = true;
|
|
218910
|
-
|
|
218948
|
+
const posted = await conn.postBlocks(p.channel, action.blocks, action.text, p.thread, postOptions);
|
|
218949
|
+
if (posted) {
|
|
218950
|
+
p.statusBarTs = posted;
|
|
218951
|
+
this.store.setStatusBarTs(p.sessionKey, posted);
|
|
218952
|
+
}
|
|
218911
218953
|
}
|
|
218912
218954
|
return;
|
|
218955
|
+
}
|
|
218913
218956
|
}
|
|
218914
218957
|
}
|
|
218958
|
+
/** Best-effort adoption path for sessions that already have an older status bar in
|
|
218959
|
+
* Slack before `statusBarTs` was persisted locally. We scan in Slack's thread order
|
|
218960
|
+
* and pick the first bot-authored status fallback, so future turns update the topmost
|
|
218961
|
+
* existing line instead of adding one more duplicate. */
|
|
218962
|
+
async findExistingSlackStatusBarTs(conn, p) {
|
|
218963
|
+
const getThreadReplies = conn.getThreadReplies;
|
|
218964
|
+
if (!getThreadReplies) return void 0;
|
|
218965
|
+
return (await getThreadReplies.call(conn, p.channel, p.statusThread)).find((m) => m.isBot && isSlackStatusBarText(m.text))?.ts;
|
|
218966
|
+
}
|
|
218915
218967
|
/**
|
|
218916
218968
|
* Apply one converger action against the session's Telegram connection — the
|
|
218917
218969
|
* Telegram analog of applySlackAction. Reuses the Pending's `*Ts` fields as
|
|
@@ -219219,7 +219271,7 @@ var Daemon = class Daemon {
|
|
|
219219
219271
|
* turn start (model + carried-over usage), on each `usage_update` (live context/cost),
|
|
219220
219272
|
* and at turn end (token totals). Deduped against the last snapshot so an unchanged
|
|
219221
219273
|
* update is a no-op. Webchat rides a status-only `webchat/output` frame; Slack gets
|
|
219222
|
-
* the in-place `status-bar` action. Telegram has NO status bar — state is queried on
|
|
219274
|
+
* the session-scoped in-place `status-bar` action. Telegram has NO status bar — state is queried on
|
|
219223
219275
|
* demand via `/status` (see handleCommand) — so it's skipped here. Headless no-ops in
|
|
219224
219276
|
* applyAction (no connection), which is fine. */
|
|
219225
219277
|
emitStatusBar(p) {
|
|
@@ -220720,4 +220772,4 @@ async function runForeground(opts, deps = {}) {
|
|
|
220720
220772
|
//#endregion
|
|
220721
220773
|
export { ClientTransport as i, CP_SUBPROTOCOL as n, CP_WS_PATH as r, runForeground as t };
|
|
220722
220774
|
|
|
220723
|
-
//# sourceMappingURL=run-foreground-
|
|
220775
|
+
//# sourceMappingURL=run-foreground-B5Kfk1KO.js.map
|