@dam-agents/cli 0.2.7-rc1 → 0.2.8-rc1

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/bin.js +588 -170
  2. package/package.json +3 -3
package/dist/bin.js CHANGED
@@ -26799,6 +26799,18 @@ var skillListInputSchema = external_exports.object({
26799
26799
  sourceId: external_exports.string().min(1),
26800
26800
  agentId: external_exports.string().min(1).optional()
26801
26801
  });
26802
+ var skillGetContentInputSchema = external_exports.object({
26803
+ sourceId: external_exports.string().min(1),
26804
+ name: external_exports.string().min(1),
26805
+ agentId: external_exports.string().min(1).optional()
26806
+ });
26807
+ var skillContentSchema = external_exports.object({
26808
+ content: external_exports.string(),
26809
+ /** Source-relative directory the SKILL.md was found in, when resolvable —
26810
+ * lets the UI build an accurate blob link instead of guessing the dir from
26811
+ * the (frontmatter) skill name. */
26812
+ dir: external_exports.string().optional()
26813
+ });
26802
26814
  var skillInstallInputSchema = external_exports.object({
26803
26815
  agentId: external_exports.string().min(1),
26804
26816
  source: external_exports.string().url(),
@@ -26892,6 +26904,38 @@ var repoSchema = external_exports.object({
26892
26904
  compatibleTemplates: external_exports.array(external_exports.string()).default([])
26893
26905
  });
26894
26906
 
26907
+ // ../api-server-api/src/modules/invocations/schemas.ts
26908
+ var DEFAULT_INVOCATION_TTL_MS = 60 * 60 * 1e3;
26909
+ var MIN_INVOCATION_TTL_MS = 60 * 1e3;
26910
+ var MAX_INVOCATION_TTL_MS = 6 * 60 * 60 * 1e3;
26911
+ var spawnInvocationRequestSchema = external_exports.object({
26912
+ image: external_exports.string().min(1).optional(),
26913
+ templateId: external_exports.string().min(1).optional(),
26914
+ connections: external_exports.array(external_exports.string().min(1)).optional(),
26915
+ prompt: external_exports.string().min(1),
26916
+ // The result JSON Schema — arbitrary JSON, validated structurally by ajv
26917
+ // when the target reports (a malformed schema is rejected at spawn).
26918
+ schema: external_exports.unknown(),
26919
+ // Optional liveness deadline for this target, in ms. Bounded server-side; a
26920
+ // shorter value fails a wedged/misconfigured target fast, a longer one lets
26921
+ // a heavy target run past the default hour.
26922
+ ttlMs: external_exports.number().int().min(MIN_INVOCATION_TTL_MS).max(MAX_INVOCATION_TTL_MS).optional(),
26923
+ // Optional resource limits. A heavy Make (clone + install + build)
26924
+ // OOM-kills at the template's small default memory, so let the driver raise
26925
+ // it. Same grammar and floors as the agent size knob.
26926
+ memory: external_exports.string().regex(/^\d+(Mi|Gi)$/, "memory must look like '512Mi' or '4Gi'").optional(),
26927
+ cpu: external_exports.string().regex(/^\d+(\.\d+)?m?$/, "cpu must look like '2', '0.5' or '500m'").optional()
26928
+ }).refine((d2) => d2.image !== void 0 || d2.templateId !== void 0, {
26929
+ message: "either image or templateId is required"
26930
+ });
26931
+ var spawnInvocationResponseSchema = external_exports.object({
26932
+ id: external_exports.string().min(1)
26933
+ });
26934
+ var invocationViewSchema = external_exports.object({
26935
+ status: external_exports.enum(["running", "done", "failed"]),
26936
+ result: external_exports.unknown()
26937
+ });
26938
+
26895
26939
  // ../agent-runtime-api/src/modules/files/schemas.ts
26896
26940
  var pathSchema = external_exports.string().min(1);
26897
26941
  var fileListDirsInputSchema = external_exports.object({
@@ -27209,6 +27253,10 @@ var harnessConfigSettledSchema = external_exports.object({
27209
27253
 
27210
27254
  // ../api-server-api/src/modules/egress-rules/schemas.ts
27211
27255
  var ruleVerdictSchema = external_exports.enum(["allow", "deny"]);
27256
+ var HOSTNAME = /^(\*\.)?[a-zA-Z0-9]([-a-zA-Z0-9]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([-a-zA-Z0-9]{0,61}[a-zA-Z0-9])?)*$/;
27257
+ var egressHostSchema = external_exports.string().min(1).max(253).refine((h2) => h2 === "*" || HOSTNAME.test(h2), {
27258
+ message: "host must be a DNS hostname, a *.wildcard, or bare *"
27259
+ });
27212
27260
  var egressPresetSchema = external_exports.enum(["none", "trusted", "all"]);
27213
27261
  var egressRuleListForAgentInputSchema = external_exports.object({
27214
27262
  agentId: external_exports.string().min(1)
@@ -27218,7 +27266,7 @@ var egressRuleCurrentPresetInputSchema = external_exports.object({
27218
27266
  });
27219
27267
  var egressRuleCreateInputSchema = external_exports.object({
27220
27268
  agentId: external_exports.string().min(1),
27221
- host: external_exports.string().min(1),
27269
+ host: egressHostSchema,
27222
27270
  // Upstream port; omit for 443. Promotes the host onto the L7 chain.
27223
27271
  port: external_exports.number().int().min(1).max(65535).optional(),
27224
27272
  method: external_exports.string().min(1).default("*"),
@@ -27257,6 +27305,9 @@ var agentSizeSchema = external_exports.object({
27257
27305
  cpu: cpuQuantitySchema.optional(),
27258
27306
  memory: memoryQuantitySchema.optional()
27259
27307
  });
27308
+ var agentUpgradeInputSchema = idSchema.extend({
27309
+ expectedToImage: external_exports.string().min(1).optional()
27310
+ });
27260
27311
  var agentCreateInputSchema = external_exports.object({
27261
27312
  name: external_exports.string().min(1).refine((n) => !n.startsWith("agent-"), {
27262
27313
  message: "agent name cannot start with 'agent-' (reserved for IDs)"
@@ -27290,7 +27341,15 @@ var agentCreateInputSchema = external_exports.object({
27290
27341
  // dimensions inherit the template's limits, else the chart default.
27291
27342
  // Requests are derived server-side — never a user concept. Floors match
27292
27343
  // the chart's derivation floors; the budget gate is the only ceiling.
27293
- size: agentSizeSchema.optional()
27344
+ size: agentSizeSchema.optional(),
27345
+ // Sweepable (#2816): mark this agent for automatic deletion by the Agent
27346
+ // Sweep once it hibernates. Set on ephemeral agents (Invocation targets);
27347
+ // durable owned agents omit it. Stamped as an annotation at create.
27348
+ sweepable: external_exports.boolean().optional(),
27349
+ // Agent Lifetime (#2816): optional grace in ms a Sweepable agent may stay
27350
+ // hibernated before the Sweep deletes it. Default zero — deleted as soon
27351
+ // as it hibernates. Ignored unless `sweepable`.
27352
+ lifetimeMs: external_exports.number().int().min(0).optional()
27294
27353
  }).refine((d2) => d2.templateId !== void 0 || d2.image !== void 0, {
27295
27354
  message: "Either templateId or image is required"
27296
27355
  });
@@ -27310,11 +27369,32 @@ var agentUpdateInputSchema = external_exports.object({
27310
27369
  });
27311
27370
  var agentConnectSlackInputSchema = external_exports.object({
27312
27371
  id: external_exports.string().min(1),
27313
- slackChannelId: external_exports.string().min(1)
27372
+ slackChannelId: external_exports.string().min(1),
27373
+ // Access mode; absent = person-scoped.
27374
+ mode: external_exports.enum(["shared", "person-scoped"]).optional(),
27375
+ // Ambient mode; absent = off. Unlike mode it is mutable: re-connecting
27376
+ // with the same mode updates it in place.
27377
+ ambient: external_exports.boolean().optional()
27378
+ }).refine((v2) => !v2.ambient || v2.mode === "shared", {
27379
+ message: "Ambient mode requires a shared binding",
27380
+ path: ["ambient"]
27381
+ });
27382
+ var agentListTelegramChatsInputSchema = external_exports.object({
27383
+ agentId: external_exports.string().min(1)
27314
27384
  });
27315
- var agentConnectTelegramInputSchema = external_exports.object({
27316
- id: external_exports.string().min(1),
27317
- botToken: external_exports.string().min(1)
27385
+ var agentUnbindTelegramChatInputSchema = external_exports.object({
27386
+ agentId: external_exports.string().min(1),
27387
+ conversationId: external_exports.string().min(1)
27388
+ });
27389
+ var agentBindTelegramChatInputSchema = external_exports.object({
27390
+ agentId: external_exports.string().min(1),
27391
+ // Opaque bind-flow id minted by the Telegram OAuth callback.
27392
+ flowId: external_exports.string().min(1)
27393
+ });
27394
+ var agentBindSlackChannelInputSchema = external_exports.object({
27395
+ agentId: external_exports.string().min(1),
27396
+ // Opaque bind-flow id minted by the Slack in-chat bind OAuth callback.
27397
+ flowId: external_exports.string().min(1)
27318
27398
  });
27319
27399
 
27320
27400
  // ../api-server-api/src/modules/schedules/schemas.ts
@@ -27417,6 +27497,83 @@ var experimentAddArmInputSchema = external_exports.object({
27417
27497
  armVariation: armVariationSchema.default("")
27418
27498
  });
27419
27499
 
27500
+ // ../api-server-api/src/modules/artifact-library/schemas.ts
27501
+ var artifactKindSchema = external_exports.enum([
27502
+ "html",
27503
+ "jsx",
27504
+ "markdown",
27505
+ "code",
27506
+ "text",
27507
+ "binary"
27508
+ ]);
27509
+ var artifactVisibilitySchema = external_exports.enum(["private", "public"]);
27510
+ var titleSchema = external_exports.string().trim().min(1, "title is required").max(300);
27511
+ var fileNameSchema = external_exports.string().trim().min(1).max(255);
27512
+ var expiresInHoursSchema = external_exports.number().int().positive().max(24 * 365 * 10);
27513
+ var artifactIdInputSchema = external_exports.object({ id: external_exports.string().min(1) });
27514
+ var artifactListInputSchema = external_exports.object({
27515
+ folderId: external_exports.string().min(1).nullish(),
27516
+ agentId: external_exports.string().min(1).optional(),
27517
+ search: external_exports.string().trim().max(200).optional()
27518
+ });
27519
+ var artifactContentInputSchema = external_exports.object({
27520
+ id: external_exports.string().min(1),
27521
+ version: external_exports.number().int().positive().optional()
27522
+ });
27523
+ var INLINE_CONTENT_MAX_BYTES = 2 * 1024 * 1024;
27524
+ var contentOrUploadRef = {
27525
+ content: external_exports.string().max(INLINE_CONTENT_MAX_BYTES).optional(),
27526
+ uploadRef: external_exports.string().min(1).max(1024).optional()
27527
+ };
27528
+ var artifactCreateInputSchema = external_exports.object({
27529
+ title: titleSchema,
27530
+ ...contentOrUploadRef,
27531
+ fileName: fileNameSchema.optional(),
27532
+ kind: artifactKindSchema.optional(),
27533
+ contentType: external_exports.string().trim().min(1).max(200).optional(),
27534
+ folderId: external_exports.string().min(1).optional(),
27535
+ visibility: artifactVisibilitySchema.optional(),
27536
+ expiresInHours: expiresInHoursSchema.nullish()
27537
+ }).refine((v2) => v2.content == null !== (v2.uploadRef == null), {
27538
+ message: "provide exactly one of content or uploadRef",
27539
+ path: ["content"]
27540
+ });
27541
+ var artifactUpdateInputSchema = external_exports.object({
27542
+ id: external_exports.string().min(1),
27543
+ title: titleSchema.optional(),
27544
+ folderId: external_exports.string().min(1).nullish(),
27545
+ ...contentOrUploadRef,
27546
+ fileName: fileNameSchema.optional(),
27547
+ kind: artifactKindSchema.optional(),
27548
+ contentType: external_exports.string().trim().min(1).max(200).optional()
27549
+ }).refine((v2) => !(v2.content != null && v2.uploadRef != null), {
27550
+ message: "provide at most one of content or uploadRef",
27551
+ path: ["content"]
27552
+ });
27553
+ var artifactSharingInputSchema = external_exports.object({
27554
+ id: external_exports.string().min(1),
27555
+ visibility: artifactVisibilitySchema.optional(),
27556
+ expiresInHours: expiresInHoursSchema.nullish()
27557
+ });
27558
+ var artifactUploadUrlInputSchema = external_exports.object({
27559
+ fileName: fileNameSchema
27560
+ });
27561
+ var folderCreateInputSchema = external_exports.object({
27562
+ name: external_exports.string().trim().min(1, "name is required").max(200)
27563
+ });
27564
+ var folderUpdateInputSchema = external_exports.object({
27565
+ id: external_exports.string().min(1),
27566
+ name: external_exports.string().trim().min(1).max(200).optional()
27567
+ });
27568
+ var folderIdInputSchema = external_exports.object({ id: external_exports.string().min(1) });
27569
+
27570
+ // ../api-server-api/src/modules/features/schemas.ts
27571
+ var featureIdSchema = external_exports.enum(["advanced-connections", "experiments"]);
27572
+ var featureSetFlagInputSchema = external_exports.object({
27573
+ feature: featureIdSchema,
27574
+ enabled: external_exports.boolean()
27575
+ });
27576
+
27420
27577
  // ../../node_modules/.pnpm/rrule@2.8.1/node_modules/rrule/dist/esm/index.js
27421
27578
  var esm_exports = {};
27422
27579
  __export(esm_exports, {
@@ -30648,6 +30805,17 @@ var clientCredentialsAuth = external_exports.object({
30648
30805
  tokenEndpointAcceptJson: external_exports.boolean().optional(),
30649
30806
  host: external_exports.string().min(1).optional()
30650
30807
  });
30808
+ var githubAppAuth = external_exports.object({
30809
+ kind: external_exports.literal("github-app"),
30810
+ appId: external_exports.string().min(1),
30811
+ installationId: external_exports.string().min(1),
30812
+ privateKeyRef: secretRef,
30813
+ accessTokenRef: secretRef,
30814
+ apiBaseUrl: external_exports.string().url(),
30815
+ expiresAt: external_exports.number().int().optional(),
30816
+ connectedAt: external_exports.number().int().optional(),
30817
+ host: external_exports.string().min(1).optional()
30818
+ });
30651
30819
  var headerAuth = external_exports.object({
30652
30820
  kind: external_exports.literal("header"),
30653
30821
  valueRef: secretRef,
@@ -30660,6 +30828,7 @@ var noneAuth = external_exports.object({
30660
30828
  var authConfig = external_exports.discriminatedUnion("kind", [
30661
30829
  oauthAuth,
30662
30830
  clientCredentialsAuth,
30831
+ githubAppAuth,
30663
30832
  headerAuth,
30664
30833
  noneAuth
30665
30834
  ]);
@@ -30681,6 +30850,7 @@ var connectionStatus = external_exports.enum([
30681
30850
  var authKind = external_exports.enum([
30682
30851
  "oauth",
30683
30852
  "client-credentials",
30853
+ "github-app",
30684
30854
  "header",
30685
30855
  "none"
30686
30856
  ]);
@@ -30708,6 +30878,8 @@ var templateInput = external_exports.object({
30708
30878
  state: templateInputState,
30709
30879
  presetValue: external_exports.string().optional(),
30710
30880
  secret: external_exports.boolean().optional(),
30881
+ // Renders as a multi-line textarea rather than a single-line input (e.g. a PEM private key).
30882
+ multiline: external_exports.boolean().optional(),
30711
30883
  // Marks a config input the form packs into `configInputs` rather than the typed auth fields.
30712
30884
  configInput: external_exports.boolean().optional(),
30713
30885
  label: external_exports.string().optional(),
@@ -30823,14 +30995,26 @@ var clientCredentialsCreateInput = external_exports.object({
30823
30995
  "env var name must be letters, digits, and underscores (not starting with a digit)"
30824
30996
  ).optional()
30825
30997
  });
30998
+ var githubAppCreateInput = external_exports.object({
30999
+ ...commonFields,
31000
+ authKind: external_exports.literal("github-app"),
31001
+ appId: external_exports.string().min(1),
31002
+ installationId: external_exports.string().min(1),
31003
+ privateKey: external_exports.string().min(1)
31004
+ });
30826
31005
  var noneCreateInput = external_exports.object({
30827
31006
  ...commonFields,
30828
31007
  authKind: external_exports.literal("none"),
30829
- url: external_exports.string().url().optional()
31008
+ url: external_exports.string().url().optional(),
31009
+ // Optional header credential (API key) for MCP servers guarded by a
31010
+ // static header — injected at the gateway, never written to harness config.
31011
+ headerName: external_exports.string().min(1).optional(),
31012
+ value: external_exports.string().min(1).optional()
30830
31013
  });
30831
31014
  var connectionCreateInputSchema = external_exports.discriminatedUnion("authKind", [
30832
31015
  oauthCreateInput,
30833
31016
  clientCredentialsCreateInput,
31017
+ githubAppCreateInput,
30834
31018
  headerCreateInput,
30835
31019
  noneCreateInput
30836
31020
  ]);
@@ -31078,7 +31262,9 @@ var slackFireMentionInputSchema = external_exports.object({
31078
31262
  channel: external_exports.string().min(1),
31079
31263
  ts: external_exports.string().min(1),
31080
31264
  threadTs: external_exports.string().optional(),
31081
- text: external_exports.string()
31265
+ text: external_exports.string(),
31266
+ /** Workspace id; present so the fake exercises the live-streaming path. */
31267
+ teamId: external_exports.string().optional()
31082
31268
  }).strict();
31083
31269
  var slackFireCommandInputSchema = external_exports.object({
31084
31270
  text: external_exports.string(),
@@ -31110,6 +31296,33 @@ var slackOutboundRecordSchema = external_exports.discriminatedUnion("kind", [
31110
31296
  kind: external_exports.literal("upload"),
31111
31297
  channelId: external_exports.string(),
31112
31298
  filename: external_exports.string()
31299
+ }).strict(),
31300
+ external_exports.object({
31301
+ kind: external_exports.literal("stream_start"),
31302
+ channel: external_exports.string(),
31303
+ threadTs: external_exports.string(),
31304
+ ts: external_exports.string(),
31305
+ text: external_exports.string(),
31306
+ recipientTeamId: external_exports.string().optional(),
31307
+ recipientUserId: external_exports.string().optional()
31308
+ }).strict(),
31309
+ external_exports.object({
31310
+ kind: external_exports.literal("stream_append"),
31311
+ channel: external_exports.string(),
31312
+ ts: external_exports.string(),
31313
+ text: external_exports.string()
31314
+ }).strict(),
31315
+ external_exports.object({
31316
+ kind: external_exports.literal("stream_stop"),
31317
+ channel: external_exports.string(),
31318
+ ts: external_exports.string(),
31319
+ text: external_exports.string().optional()
31320
+ }).strict(),
31321
+ external_exports.object({
31322
+ kind: external_exports.literal("status"),
31323
+ channel: external_exports.string(),
31324
+ threadTs: external_exports.string(),
31325
+ status: external_exports.string()
31113
31326
  }).strict()
31114
31327
  ]);
31115
31328
  var slackReadOutboundResultSchema = external_exports.object({ records: external_exports.array(slackOutboundRecordSchema) }).strict();
@@ -31436,6 +31649,7 @@ var EXIT_RULE_NOT_FOUND = 6;
31436
31649
  var EXIT_AGENT_NOT_REACHABLE = 7;
31437
31650
  var EXIT_APPROVAL_NOT_ACTIONABLE = 8;
31438
31651
  var EXIT_SCHEDULE_NOT_FOUND = 9;
31652
+ var EXIT_TERMS_NOT_ACCEPTED = 10;
31439
31653
  var EXIT_SIGINT = 130;
31440
31654
 
31441
31655
  // src/modules/agent/commands/create.ts
@@ -37247,11 +37461,16 @@ function buildDisallowCommand(deps) {
37247
37461
  // src/modules/channel/commands/list.ts
37248
37462
  function renderHuman(channels, allowedUserEmails) {
37249
37463
  const channelsBlock = channels.length === 0 ? "No channels connected.\n" : renderTable([
37250
- ["TYPE", "IDENTIFIER"],
37464
+ ["TYPE", "IDENTIFIER", "MODE"],
37251
37465
  ...channels.map(
37252
37466
  (c2) => (
37253
37467
  // Telegram has no readable identifier — the bot token is write-only.
37254
- c2.type === "slack" /* Slack */ ? ["slack", c2.slackChannelId] : ["telegram", "\u2014"]
37468
+ // Telegram is structurally shared-only.
37469
+ c2.type === "slack" /* Slack */ ? [
37470
+ "slack",
37471
+ c2.slackChannelId,
37472
+ c2.ambient ? "shared (ambient)" : c2.mode ?? "person-scoped"
37473
+ ] : ["telegram", "\u2014", "shared"]
37255
37474
  )
37256
37475
  )
37257
37476
  ]);
@@ -37305,7 +37524,7 @@ function buildListCommand3(deps) {
37305
37524
  // src/modules/channel/commands/precheck.ts
37306
37525
  var UNAVAILABLE_MESSAGE = {
37307
37526
  ["slack" /* Slack */]: "Slack app token not configured",
37308
- ["telegram" /* Telegram */]: "Telegram channel not enabled"
37527
+ ["telegram" /* Telegram */]: "Telegram bot not configured"
37309
37528
  };
37310
37529
  async function ensureProviderAvailable(svc, provider, host) {
37311
37530
  const res = await svc.available();
@@ -37323,11 +37542,17 @@ async function ensureProviderAvailable(svc, provider, host) {
37323
37542
  // src/modules/channel/commands/slack-connect.ts
37324
37543
  function buildSlackConnectCommand(deps) {
37325
37544
  return new Command("connect").description("Bind a Slack channel to an Agent").argument("<agent>", "Agent Ref \u2014 name or 'agent-\u2026' ID").requiredOption("--channel-id <id>", "Slack channel id (e.g. C0123ABCD)").option(
37545
+ "--mode <mode>",
37546
+ "access mode: 'person-scoped' (default) or 'shared'"
37547
+ ).option(
37548
+ "--ambient",
37549
+ "ambient mode (shared only): the agent reads along and may chime in without being mentioned"
37550
+ ).option(
37326
37551
  "--server <url>",
37327
37552
  "override the configured server URL for this call"
37328
37553
  ).option("--json", "emit the updated ChannelConfig[] as JSON").addHelpText(
37329
37554
  "after",
37330
- "\nExamples:\n dam channel slack connect my-agent --channel-id C0123ABCD\n"
37555
+ "\nExamples:\n dam channel slack connect my-agent --channel-id C0123ABCD\n dam channel slack connect my-agent --channel-id C0123ABCD --mode shared\n dam channel slack connect my-agent --channel-id C0123ABCD --mode shared --ambient\n"
37331
37556
  ).action(
37332
37557
  async (ref, opts) => {
37333
37558
  const channelId = opts.channelId.trim();
@@ -37335,6 +37560,17 @@ function buildSlackConnectCommand(deps) {
37335
37560
  process.stderr.write("error: --channel-id must not be empty\n");
37336
37561
  process.exit(EXIT_INVALID_INPUT);
37337
37562
  }
37563
+ if (opts.mode !== void 0 && opts.mode !== "shared" && opts.mode !== "person-scoped") {
37564
+ process.stderr.write(
37565
+ "error: --mode must be 'shared' or 'person-scoped'\n"
37566
+ );
37567
+ process.exit(EXIT_INVALID_INPUT);
37568
+ }
37569
+ const mode = opts.mode;
37570
+ if (opts.ambient && mode !== "shared") {
37571
+ process.stderr.write("error: --ambient requires --mode shared\n");
37572
+ process.exit(EXIT_INVALID_INPUT);
37573
+ }
37338
37574
  const host = await resolveActiveHost(deps, {
37339
37575
  flag: opts.server ? { server: opts.server } : void 0,
37340
37576
  exitCodes: {
@@ -37352,7 +37588,12 @@ function buildSlackConnectCommand(deps) {
37352
37588
  }
37353
37589
  const svc = deps.createChannelService(host);
37354
37590
  await ensureProviderAvailable(svc, "slack" /* Slack */, host);
37355
- const res = await svc.connectSlack(resolved.value.id, channelId);
37591
+ const res = await svc.connectSlack(
37592
+ resolved.value.id,
37593
+ channelId,
37594
+ mode,
37595
+ opts.ambient
37596
+ );
37356
37597
  if (!res.ok) {
37357
37598
  if (res.error.kind === "channel-conflict") {
37358
37599
  process.stderr.write(
@@ -37368,17 +37609,54 @@ function buildSlackConnectCommand(deps) {
37368
37609
  printServiceError(res.error, host);
37369
37610
  process.exit(EXIT_RUNTIME_FAILURE);
37370
37611
  }
37612
+ if (mode === "shared") {
37613
+ const slackCh = res.value.find((c2) => c2.type === "slack" /* Slack */);
37614
+ if (!slackCh || slackCh.mode !== "shared") {
37615
+ const rolledBack = await svc.disconnectSlack(resolved.value.id);
37616
+ if (rolledBack.ok) {
37617
+ process.stderr.write(
37618
+ "error: this server does not support shared access mode \u2014 binding rolled back\n"
37619
+ );
37620
+ } else {
37621
+ process.stderr.write(
37622
+ "error: this server does not support shared access mode, and rolling the binding back failed \u2014 the channel is still bound in person-scoped mode; disconnect it manually\n"
37623
+ );
37624
+ printServiceError(rolledBack.error, host);
37625
+ }
37626
+ process.exit(EXIT_RUNTIME_FAILURE);
37627
+ }
37628
+ }
37629
+ if (opts.ambient) {
37630
+ const slackCh = res.value.find((c2) => c2.type === "slack" /* Slack */);
37631
+ if (slackCh?.type === "slack" /* Slack */ && !slackCh.ambient) {
37632
+ process.stderr.write(
37633
+ "error: this server does not support ambient mode \u2014 the channel is connected in shared mode without it\n"
37634
+ );
37635
+ process.exit(EXIT_RUNTIME_FAILURE);
37636
+ }
37637
+ }
37371
37638
  if (opts.json) {
37372
37639
  process.stdout.write(`${JSON.stringify(res.value)}
37373
37640
  `);
37374
37641
  } else {
37375
37642
  process.stdout.write(
37376
- `\u2713 Slack channel ${channelId} connected to ${resolved.value.name}.
37643
+ `\u2713 Slack channel ${channelId} connected to ${resolved.value.name}${mode === "shared" ? opts.ambient ? " in shared mode with ambient on" : " in shared mode" : ""}.
37377
37644
  `
37378
37645
  );
37379
- process.stderr.write(
37380
- "hint: users must run `/platform login` inside Slack before they can drive this agent\n"
37381
- );
37646
+ if (opts.ambient) {
37647
+ process.stderr.write(
37648
+ "note: the agent reads along in this channel and may chime in without being mentioned; the channel gets a visible notice\n"
37649
+ );
37650
+ }
37651
+ if (mode === "shared") {
37652
+ process.stderr.write(
37653
+ "note: everyone in the channel drives this agent under its credentials; your Terms-of-Use acceptance covers every turn\n"
37654
+ );
37655
+ } else {
37656
+ process.stderr.write(
37657
+ "hint: users must run `/platform login` inside Slack before they can drive this agent\n"
37658
+ );
37659
+ }
37382
37660
  }
37383
37661
  process.exit(EXIT_SUCCESS);
37384
37662
  }
@@ -37430,131 +37708,19 @@ function buildSlackDisconnectCommand(deps) {
37430
37708
  });
37431
37709
  }
37432
37710
 
37433
- // src/modules/channel/commands/telegram-connect.ts
37434
- function buildTelegramConnectCommand(deps) {
37435
- return new Command("connect").description("Bind a Telegram bot to an Agent").argument("<agent>", "Agent Ref \u2014 name or 'agent-\u2026' ID").option(
37436
- "--bot-token <token>",
37437
- "bot token from @BotFather (prompted, masked, if omitted on a TTY)"
37438
- ).option(
37439
- "--server <url>",
37440
- "override the configured server URL for this call"
37441
- ).option("--json", "emit the updated ChannelConfig[] as JSON").addHelpText(
37442
- "after",
37443
- "\nExamples:\n dam channel telegram connect my-agent\n dam channel telegram connect my-agent --bot-token 123:ABC\n"
37444
- ).action(
37445
- async (ref, opts) => {
37446
- const host = await resolveActiveHost(deps, {
37447
- flag: opts.server ? { server: opts.server } : void 0,
37448
- exitCodes: {
37449
- runtimeFailure: EXIT_RUNTIME_FAILURE,
37450
- belowFloor: EXIT_BELOW_FLOOR
37451
- }
37452
- });
37453
- const resolver = createAgentResolver({
37454
- agentService: deps.createAgentService(host)
37455
- });
37456
- const resolved = await resolver.resolve(ref);
37457
- if (!resolved.ok) {
37458
- printResolveError(resolved.error, host);
37459
- process.exit(exitCodeForResolveError(resolved.error));
37460
- }
37461
- const token = await resolveToken(opts);
37462
- const svc = deps.createChannelService(host);
37463
- await ensureProviderAvailable(svc, "telegram" /* Telegram */, host);
37464
- const res = await svc.connectTelegram(resolved.value.id, token);
37465
- if (!res.ok) {
37466
- if (res.error.kind === "channel-precondition" || res.error.kind === "invalid-input") {
37467
- process.stderr.write(`error: ${res.error.message}
37468
- `);
37469
- process.exit(EXIT_INVALID_INPUT);
37470
- }
37471
- printServiceError(res.error, host);
37472
- process.exit(EXIT_RUNTIME_FAILURE);
37473
- }
37474
- if (opts.json) {
37475
- process.stdout.write(`${JSON.stringify(res.value)}
37476
- `);
37477
- } else {
37478
- process.stdout.write(
37479
- `\u2713 Telegram bot connected to ${resolved.value.name}.
37480
- `
37481
- );
37482
- }
37483
- process.exit(EXIT_SUCCESS);
37484
- }
37485
- );
37486
- }
37487
- async function resolveToken(opts) {
37488
- const flag = opts.botToken?.trim();
37489
- if (flag) return flag;
37490
- if (!process.stdin.isTTY) {
37491
- process.stderr.write("error: --bot-token is required on a non-TTY\n");
37492
- process.exit(EXIT_INVALID_INPUT);
37493
- }
37494
- const answer = await Ce({
37495
- message: "Telegram bot token (from @BotFather)",
37496
- validate: (v2) => v2 && v2.trim().length > 0 ? void 0 : "Required"
37497
- });
37498
- if (q(answer)) exitCancelled({ json: opts.json });
37499
- return String(answer).trim();
37500
- }
37501
-
37502
- // src/modules/channel/commands/telegram-disconnect.ts
37503
- function buildTelegramDisconnectCommand(deps) {
37504
- return new Command("disconnect").description(
37505
- "Unbind an Agent's Telegram bot (deletes its k8s Secret). Idempotent \u2014 a not-connected agent exits 0."
37506
- ).argument("<agent>", "Agent Ref \u2014 name or 'agent-\u2026' ID").option(
37507
- "--server <url>",
37508
- "override the configured server URL for this call"
37509
- ).option("--json", "emit the updated ChannelConfig[] as JSON").addHelpText(
37510
- "after",
37511
- "\nExamples:\n dam channel telegram disconnect my-agent\n"
37512
- ).action(async (ref, opts) => {
37513
- const host = await resolveActiveHost(deps, {
37514
- flag: opts.server ? { server: opts.server } : void 0,
37515
- exitCodes: {
37516
- runtimeFailure: EXIT_RUNTIME_FAILURE,
37517
- belowFloor: EXIT_BELOW_FLOOR
37518
- }
37519
- });
37520
- const resolver = createAgentResolver({
37521
- agentService: deps.createAgentService(host)
37522
- });
37523
- const resolved = await resolver.resolve(ref);
37524
- if (!resolved.ok) {
37525
- printResolveError(resolved.error, host);
37526
- process.exit(exitCodeForResolveError(resolved.error));
37527
- }
37528
- const svc = deps.createChannelService(host);
37529
- const res = await svc.disconnectTelegram(resolved.value.id);
37530
- if (!res.ok) {
37531
- printServiceError(res.error, host);
37532
- process.exit(EXIT_RUNTIME_FAILURE);
37533
- }
37534
- if (opts.json) {
37535
- process.stdout.write(`${JSON.stringify(res.value)}
37536
- `);
37537
- } else {
37538
- process.stdout.write(
37539
- `\u2713 Telegram disconnected from ${resolved.value.name}.
37540
- `
37541
- );
37542
- }
37543
- process.exit(EXIT_SUCCESS);
37544
- });
37545
- }
37546
-
37547
37711
  // src/modules/channel/services/channel-service.ts
37548
37712
  function createChannelService(deps) {
37549
37713
  return {
37550
37714
  async available() {
37551
37715
  return trpcCall(() => deps.trpc.channels.available.query());
37552
37716
  },
37553
- async connectSlack(id, slackChannelId) {
37717
+ async connectSlack(id, slackChannelId, mode, ambient) {
37554
37718
  try {
37555
37719
  const agent = await deps.trpc.agents.connectSlack.mutate({
37556
37720
  id,
37557
- slackChannelId
37721
+ slackChannelId,
37722
+ ...mode ? { mode } : {},
37723
+ ...ambient ? { ambient: true } : {}
37558
37724
  });
37559
37725
  return ok2(agent.channels);
37560
37726
  } catch (e2) {
@@ -37574,29 +37740,6 @@ function createChannelService(deps) {
37574
37740
  const agent = await deps.trpc.agents.disconnectSlack.mutate({ id });
37575
37741
  return agent.channels;
37576
37742
  });
37577
- },
37578
- async connectTelegram(id, botToken) {
37579
- try {
37580
- const agent = await deps.trpc.agents.connectTelegram.mutate({
37581
- id,
37582
- botToken
37583
- });
37584
- return ok2(agent.channels);
37585
- } catch (e2) {
37586
- const code = e2?.data?.code;
37587
- const message = e2 instanceof Error ? e2.message : String(e2);
37588
- if (code === "PRECONDITION_FAILED")
37589
- return err2({ kind: "channel-precondition", message });
37590
- if (code === "BAD_REQUEST")
37591
- return err2({ kind: "invalid-input", message });
37592
- return classifyTrpcError(e2);
37593
- }
37594
- },
37595
- async disconnectTelegram(id) {
37596
- return trpcCall(async () => {
37597
- const agent = await deps.trpc.agents.disconnectTelegram.mutate({ id });
37598
- return agent.channels;
37599
- });
37600
37743
  }
37601
37744
  };
37602
37745
  }
@@ -37612,7 +37755,7 @@ function composeChannelModule(opts) {
37612
37755
  createChannelService: createService
37613
37756
  };
37614
37757
  const parent = new Command("channel").description(
37615
- "Manage messenger channel bindings (Slack, Telegram)"
37758
+ "Manage messenger channel bindings (Telegram chats bind in-chat via /login)"
37616
37759
  );
37617
37760
  parent.addCommand(
37618
37761
  buildAvailableCommand({
@@ -37634,12 +37777,6 @@ function composeChannelModule(opts) {
37634
37777
  slack.addCommand(buildSlackConnectCommand(agentScoped));
37635
37778
  slack.addCommand(buildSlackDisconnectCommand(agentScoped));
37636
37779
  parent.addCommand(slack);
37637
- const telegram = new Command("telegram").description(
37638
- "Bind or unbind an Agent's Telegram bot"
37639
- );
37640
- telegram.addCommand(buildTelegramConnectCommand(agentScoped));
37641
- telegram.addCommand(buildTelegramDisconnectCommand(agentScoped));
37642
- parent.addCommand(telegram);
37643
37780
  const allowScoped = {
37644
37781
  compatService: opts.compatService,
37645
37782
  configService: opts.configService,
@@ -40174,7 +40311,7 @@ function createProcessEnvReader() {
40174
40311
 
40175
40312
  // src/modules/cli/infrastructure/package-version.ts
40176
40313
  function readPackageVersion() {
40177
- return true ? "0.2.7-rc1" : DEV_VERSION;
40314
+ return true ? "0.2.8-rc1" : DEV_VERSION;
40178
40315
  }
40179
40316
 
40180
40317
  // src/modules/cli/infrastructure/version-probe.ts
@@ -40410,7 +40547,13 @@ function buildConnectCommand(deps) {
40410
40547
  ).option(
40411
40548
  "--audience <audience>",
40412
40549
  "input: OAuth audience (client credentials)"
40413
- ).option("--app-slug <slug>", "input: GitHub App slug").option("--header-name <name>", "input: header name").option("--value-format <format>", "input: header value format").option(
40550
+ ).option("--app-slug <slug>", "input: GitHub App slug").option("--app-id <id>", "input: GitHub App ID (GitHub App installation)").option(
40551
+ "--installation-id <id>",
40552
+ "input: GitHub App installation ID (GitHub App installation)"
40553
+ ).option(
40554
+ "--private-key <pem>",
40555
+ 'input: GitHub App private key \u2014 PEM or base64; use --private-key "$(cat app.pem)" (GitHub App installation)'
40556
+ ).option("--header-name <name>", "input: header name").option("--value-format <format>", "input: header value format").option(
40414
40557
  "--env-name <name>",
40415
40558
  "input: expose the credential to the agent as this env var (custom header credentials)"
40416
40559
  ).option("--value <value>", "input: header secret value").option(
@@ -40433,7 +40576,7 @@ function buildConnectCommand(deps) {
40433
40576
  String(DEFAULT_TIMEOUT_SECONDS3)
40434
40577
  ).addHelpText(
40435
40578
  "after",
40436
- "\nExamples:\n dam connection connect github\n dam connection connect github --client-id Iv1.\u2026 --client-secret \u2026 # use your own OAuth app\n dam connection connect github --no-browser\n dam connection connect my-api --header-name X-API-Key --value sk-\u2026\n dam connection connect custom-client-credentials --host api.example.com \\\n --issuer-url https://auth.example.com/realms/main --client-id \u2026 --client-secret \u2026\n dam connection connect bob --value sk-\u2026 --config model=premium-shell --config chatMode=code\n dam connection connect https://mcp.example.com\n dam connection connect https://mcp.example.com --auth none\n"
40579
+ '\nExamples:\n dam connection connect github\n dam connection connect github --client-id Iv1.\u2026 --client-secret \u2026 # use your own OAuth app\n dam connection connect github --no-browser\n dam connection connect my-api --header-name X-API-Key --value sk-\u2026\n dam connection connect custom-client-credentials --host api.example.com \\\n --issuer-url https://auth.example.com/realms/main --client-id \u2026 --client-secret \u2026\n dam connection connect github-app --app-id 123456 --installation-id 987654 \\\n --private-key "$(cat app.pem)"\n dam connection connect bob --value sk-\u2026 --config model=premium-shell --config chatMode=code\n dam connection connect https://mcp.example.com\n dam connection connect https://mcp.example.com --auth none\n'
40437
40580
  ).action(async (providerOrUrl, opts) => {
40438
40581
  const json2 = opts.json ?? false;
40439
40582
  const timeoutSeconds = parseTimeout2(opts.timeout);
@@ -40655,8 +40798,9 @@ async function collectInputs(template, opts, json2) {
40655
40798
  }
40656
40799
  for (const input of missing) {
40657
40800
  const prompt = input.secret ? Ce : Re;
40801
+ const label = input.label ?? labelFor(input.name);
40658
40802
  const answer = await prompt({
40659
- message: labelFor(input.name),
40803
+ message: input.hint ? `${label} \u2014 ${input.hint}` : label,
40660
40804
  validate: (v2) => v2 && v2.trim().length > 0 ? void 0 : "Required"
40661
40805
  });
40662
40806
  if (q(answer)) exitCancelled({ json: json2 });
@@ -40695,6 +40839,25 @@ function buildPayload(template, name, values) {
40695
40839
  ...v2("valueFormat") ? { valueFormat: v2("valueFormat") } : {},
40696
40840
  ...v2("envName") ? { envName: v2("envName") } : {}
40697
40841
  };
40842
+ case "github-app": {
40843
+ const appId = v2("appId");
40844
+ const installationId = v2("installationId");
40845
+ const privateKey = v2("privateKey");
40846
+ if (!appId) return { error: "the App ID is required (--app-id)" };
40847
+ if (!installationId) {
40848
+ return { error: "the installation ID is required (--installation-id)" };
40849
+ }
40850
+ if (!privateKey) {
40851
+ return { error: "the private key is required (--private-key)" };
40852
+ }
40853
+ return {
40854
+ ...common2,
40855
+ authKind: "github-app",
40856
+ appId,
40857
+ installationId,
40858
+ privateKey
40859
+ };
40860
+ }
40698
40861
  case "header": {
40699
40862
  const value = v2("value");
40700
40863
  if (!value) return { error: "the secret value is required (--value)" };
@@ -40817,6 +40980,9 @@ var FIELD_LABELS = {
40817
40980
  scopes: "Scopes (space-separated)",
40818
40981
  audience: "Audience",
40819
40982
  appSlug: "GitHub App slug",
40983
+ appId: "GitHub App ID",
40984
+ installationId: "GitHub App installation ID",
40985
+ privateKey: "GitHub App private key (PEM)",
40820
40986
  envName: "Env var name",
40821
40987
  caData: "Cluster CA certificate"
40822
40988
  };
@@ -44230,6 +44396,12 @@ function createSkillsService(deps) {
44230
44396
  const message = e2.message.replace(/\nplatform-cta:\S+/, "").trim();
44231
44397
  return err2({ kind: "source-needs-connection", message, cta });
44232
44398
  }
44399
+ if (e2?.data?.code === "FORBIDDEN") {
44400
+ return err2({
44401
+ kind: "source-needs-connection",
44402
+ message: e2 instanceof Error ? e2.message : String(e2)
44403
+ });
44404
+ }
44233
44405
  return classifyWakeError(e2);
44234
44406
  }
44235
44407
  },
@@ -45159,6 +45331,248 @@ function composeTemplateModule(opts) {
45159
45331
  };
45160
45332
  }
45161
45333
 
45334
+ // src/modules/terms/commands/accept.ts
45335
+ function buildAcceptCommand(deps) {
45336
+ return new Command("accept").description(
45337
+ "Accept the current Terms of Use \u2014 interactive on a TTY, use --yes for CI/headless"
45338
+ ).option(
45339
+ "--yes",
45340
+ "accept without the interactive confirmation (required on a non-TTY)"
45341
+ ).option(
45342
+ "--expect-version <version>",
45343
+ "assert the version you reviewed; fails if it differs from the server's current version (not named --version, which is reserved for the CLI's own version)"
45344
+ ).option(
45345
+ "--server <url>",
45346
+ "override the configured server URL for this call"
45347
+ ).option("--json", "emit the acceptance result as JSON").action(
45348
+ async (opts) => {
45349
+ const host = await resolveActiveHost(deps, {
45350
+ flag: opts.server ? { server: opts.server } : void 0,
45351
+ exitCodes: {
45352
+ runtimeFailure: EXIT_RUNTIME_FAILURE,
45353
+ belowFloor: EXIT_BELOW_FLOOR
45354
+ }
45355
+ });
45356
+ if (!opts.yes && !process.stdin.isTTY) {
45357
+ process.stderr.write(
45358
+ "error: refusing to accept the Terms of Use non-interactively without --yes\nhint: re-run with --yes to accept in a script or CI\n"
45359
+ );
45360
+ process.exit(EXIT_RUNTIME_FAILURE);
45361
+ }
45362
+ const service = deps.createTermsService(host);
45363
+ const doc = await service.document();
45364
+ if (!doc.ok) {
45365
+ printServiceError(doc.error, host);
45366
+ process.exit(EXIT_RUNTIME_FAILURE);
45367
+ }
45368
+ const currentVersion = doc.value.version;
45369
+ if (opts.expectVersion && opts.expectVersion !== currentVersion) {
45370
+ process.stderr.write(
45371
+ `error: reviewed version ${opts.expectVersion} does not match the server's current version ${currentVersion}; re-review before accepting
45372
+ `
45373
+ );
45374
+ process.exit(EXIT_RUNTIME_FAILURE);
45375
+ }
45376
+ if (!opts.yes) {
45377
+ process.stderr.write(
45378
+ `Terms of Use \u2014 version ${currentVersion}
45379
+
45380
+ ${doc.value.text}
45381
+
45382
+ `
45383
+ );
45384
+ if (!await confirm("Accept the Terms of Use?", {
45385
+ timeoutMs: 15 * 6e4
45386
+ }))
45387
+ exitCancelled({ json: opts.json });
45388
+ }
45389
+ const accepted = await service.accept(currentVersion);
45390
+ if (!accepted.ok) {
45391
+ if (accepted.error.kind === "transport" && accepted.error.serverCode === "PRECONDITION_FAILED") {
45392
+ process.stderr.write(
45393
+ "error: the Terms of Use changed while you were reviewing; re-run `dam terms accept` to review the current version\n"
45394
+ );
45395
+ } else {
45396
+ printServiceError(accepted.error, host);
45397
+ }
45398
+ process.exit(EXIT_RUNTIME_FAILURE);
45399
+ }
45400
+ if (opts.json) {
45401
+ return writeStdoutAndExit(
45402
+ `${JSON.stringify({ accepted: true, version: currentVersion })}
45403
+ `,
45404
+ EXIT_SUCCESS
45405
+ );
45406
+ }
45407
+ return writeStdoutAndExit(
45408
+ `Accepted Terms of Use ${currentVersion}.
45409
+ `,
45410
+ EXIT_SUCCESS
45411
+ );
45412
+ }
45413
+ );
45414
+ }
45415
+
45416
+ // src/modules/terms/commands/show.ts
45417
+ function buildShowCommand(deps) {
45418
+ return new Command("show").description(
45419
+ "Print the current Terms of Use text, version, and your acceptance state"
45420
+ ).option(
45421
+ "--server <url>",
45422
+ "override the configured server URL for this call"
45423
+ ).option("--json", "emit the raw Terms of Use document as JSON").action(async (opts) => {
45424
+ const host = await resolveActiveHost(deps, {
45425
+ flag: opts.server ? { server: opts.server } : void 0,
45426
+ exitCodes: {
45427
+ runtimeFailure: EXIT_RUNTIME_FAILURE,
45428
+ belowFloor: EXIT_BELOW_FLOOR
45429
+ }
45430
+ });
45431
+ const service = deps.createTermsService(host);
45432
+ const [doc, latest] = await Promise.all([
45433
+ service.document(),
45434
+ service.latestAcceptance()
45435
+ ]);
45436
+ if (!doc.ok) {
45437
+ printServiceError(doc.error, host);
45438
+ process.exit(EXIT_RUNTIME_FAILURE);
45439
+ }
45440
+ if (!latest.ok) {
45441
+ printServiceError(latest.error, host);
45442
+ process.exit(EXIT_RUNTIME_FAILURE);
45443
+ }
45444
+ if (opts.json) {
45445
+ return writeStdoutAndExit(
45446
+ `${JSON.stringify(doc.value)}
45447
+ `,
45448
+ EXIT_SUCCESS
45449
+ );
45450
+ }
45451
+ const acceptState = !latest.value ? "Not accepted." : latest.value.version === doc.value.version ? `Accepted on ${new Date(latest.value.acceptedAt).toISOString()}.` : `You accepted version ${latest.value.version}; version ${doc.value.version} is current and not yet accepted.`;
45452
+ return writeStdoutAndExit(
45453
+ `Terms of Use \u2014 version ${doc.value.version}
45454
+ ${acceptState}
45455
+
45456
+ ${doc.value.text}
45457
+ `,
45458
+ EXIT_SUCCESS
45459
+ );
45460
+ });
45461
+ }
45462
+
45463
+ // src/modules/terms/commands/status.ts
45464
+ function buildStatusCommand2(deps) {
45465
+ return new Command("status").description(
45466
+ "Check whether you've accepted the current Terms of Use \u2014 no text dump; exits non-zero when not accepted, so it works as a CI gate"
45467
+ ).option(
45468
+ "--server <url>",
45469
+ "override the configured server URL for this call"
45470
+ ).option("--json", "emit the acceptance state as JSON").action(async (opts) => {
45471
+ const host = await resolveActiveHost(deps, {
45472
+ flag: opts.server ? { server: opts.server } : void 0,
45473
+ exitCodes: {
45474
+ runtimeFailure: EXIT_RUNTIME_FAILURE,
45475
+ belowFloor: EXIT_BELOW_FLOOR
45476
+ }
45477
+ });
45478
+ const service = deps.createTermsService(host);
45479
+ const [current, latest] = await Promise.all([
45480
+ service.current(),
45481
+ service.latestAcceptance()
45482
+ ]);
45483
+ if (!current.ok) {
45484
+ printServiceError(current.error, host);
45485
+ process.exit(EXIT_RUNTIME_FAILURE);
45486
+ }
45487
+ if (!latest.ok) {
45488
+ printServiceError(latest.error, host);
45489
+ process.exit(EXIT_RUNTIME_FAILURE);
45490
+ }
45491
+ const acceptedVersion = latest.value?.version ?? null;
45492
+ const accepted = acceptedVersion === current.value.version;
45493
+ const exitCode = accepted ? EXIT_SUCCESS : EXIT_TERMS_NOT_ACCEPTED;
45494
+ if (opts.json) {
45495
+ return writeStdoutAndExit(
45496
+ `${JSON.stringify({ currentVersion: current.value.version, acceptedVersion, accepted })}
45497
+ `,
45498
+ exitCode
45499
+ );
45500
+ }
45501
+ const line = accepted ? `Terms of Use ${current.value.version}: accepted.` : acceptedVersion ? `Terms of Use ${current.value.version}: NOT accepted (you accepted ${acceptedVersion}).` : `Terms of Use ${current.value.version}: NOT accepted (no acceptance on record).`;
45502
+ return writeStdoutAndExit(`${line}
45503
+ `, exitCode);
45504
+ });
45505
+ }
45506
+
45507
+ // src/modules/terms/services/terms-service.ts
45508
+ function createTermsService(deps) {
45509
+ const doFetch = deps.fetch ?? fetch;
45510
+ const base = deps.host.replace(/\/+$/, "");
45511
+ return {
45512
+ async document() {
45513
+ let res;
45514
+ try {
45515
+ res = await doFetch(`${base}/api/terms`);
45516
+ } catch (e2) {
45517
+ return err2({
45518
+ kind: "transport",
45519
+ reason: e2 instanceof Error ? e2.message : "cannot reach server"
45520
+ });
45521
+ }
45522
+ if (!res.ok)
45523
+ return err2({
45524
+ kind: "transport",
45525
+ reason: `GET /api/terms returned ${res.status}`
45526
+ });
45527
+ let body;
45528
+ try {
45529
+ body = await res.json();
45530
+ } catch {
45531
+ return err2({
45532
+ kind: "transport",
45533
+ reason: "malformed Terms of Use document from server"
45534
+ });
45535
+ }
45536
+ const parsed = termsDocumentSchema.safeParse(body);
45537
+ if (!parsed.success)
45538
+ return err2({
45539
+ kind: "transport",
45540
+ reason: "malformed Terms of Use document from server"
45541
+ });
45542
+ return ok2(parsed.data);
45543
+ },
45544
+ async current() {
45545
+ return trpcCall(() => deps.trpc.terms.current.query());
45546
+ },
45547
+ async latestAcceptance() {
45548
+ return trpcCall(() => deps.trpc.terms.latestAcceptance.query());
45549
+ },
45550
+ async accept(version2) {
45551
+ return trpcCall(() => deps.trpc.terms.accept.mutate({ version: version2 }));
45552
+ }
45553
+ };
45554
+ }
45555
+
45556
+ // src/modules/terms/compose.ts
45557
+ function composeTermsModule(opts) {
45558
+ const createService = (host) => createTermsService({
45559
+ trpc: createTrpcClient({ host, tokenProvider: opts.tokenProvider }),
45560
+ host
45561
+ });
45562
+ const scoped = {
45563
+ compatService: opts.compatService,
45564
+ configService: opts.configService,
45565
+ createTermsService: createService
45566
+ };
45567
+ const parent = new Command("terms").description(
45568
+ "View and accept the deployment's Terms of Use"
45569
+ );
45570
+ parent.addCommand(buildShowCommand(scoped));
45571
+ parent.addCommand(buildStatusCommand2(scoped));
45572
+ parent.addCommand(buildAcceptCommand(scoped));
45573
+ return { commands: [parent] };
45574
+ }
45575
+
45162
45576
  // src/compose.ts
45163
45577
  function compose(opts = {}) {
45164
45578
  const cli = composeCliModule({ configPath: opts.configPath });
@@ -45251,6 +45665,11 @@ function compose(opts = {}) {
45251
45665
  compatService: cli.services.compatService,
45252
45666
  createAgentService: agent.exports.createService
45253
45667
  });
45668
+ const terms = composeTermsModule({
45669
+ tokenProvider: auth.exports.tokenProvider,
45670
+ configService: cli.services.configService,
45671
+ compatService: cli.services.compatService
45672
+ });
45254
45673
  const program3 = new Command();
45255
45674
  program3.name("dam").description("Command-line client for a Platform deployment").version(cli.cliVersion);
45256
45675
  for (const command of cli.commands) program3.addCommand(command);
@@ -45268,6 +45687,7 @@ function compose(opts = {}) {
45268
45687
  for (const command of ssh.commands) program3.addCommand(command);
45269
45688
  for (const command of channel.commands) program3.addCommand(command);
45270
45689
  for (const command of metrics.commands) program3.addCommand(command);
45690
+ for (const command of terms.commands) program3.addCommand(command);
45271
45691
  return program3;
45272
45692
  }
45273
45693
 
@@ -45278,9 +45698,7 @@ try {
45278
45698
  } catch (err3) {
45279
45699
  if (err3 instanceof TermsStaleAtTransportError) {
45280
45700
  process.stderr.write(
45281
- `error: Terms of Use acceptance required
45282
- hint: open ${err3.host} to accept
45283
- `
45701
+ "error: Terms of Use acceptance required\nhint: run `dam terms accept` to review and accept\n"
45284
45702
  );
45285
45703
  process.exit(1);
45286
45704
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dam-agents/cli",
3
3
  "author": "IBM Corp.",
4
- "version": "0.2.7-rc1",
4
+ "version": "0.2.8-rc1",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "homepage": "https://github.com/dam-agents/dam/tree/main/packages/cli#readme",
@@ -43,8 +43,8 @@
43
43
  "vitest": "^4.1.3",
44
44
  "ws": "^8.20.0",
45
45
  "zod": "^4.4.3",
46
- "agent-runtime-api": "0.0.1",
46
+ "api-server-api": "0.0.1",
47
47
  "dev-config": "0.0.1",
48
- "api-server-api": "0.0.1"
48
+ "agent-runtime-api": "0.0.1"
49
49
  }
50
50
  }