@dam-agents/cli 0.2.7 → 0.2.9-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 +602 -172
  2. package/package.json +1 -1
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
  ]);
@@ -30935,7 +31119,7 @@ function describeApprovalPayload(payload) {
30935
31119
  if (payload.kind === "ext_authz") {
30936
31120
  return {
30937
31121
  title: `${payload.method} ${payload.host}`,
30938
- subtitle: payload.path
31122
+ subtitle: payload.viaAgentId ? `${payload.path} \xB7 via ${payload.viaAgentId}` : payload.path
30939
31123
  };
30940
31124
  }
30941
31125
  return { title: payload.toolName ?? "tool call", subtitle: "" };
@@ -30984,7 +31168,7 @@ var platformTurnEndedNotificationSchema = external_exports.object({
30984
31168
  var brandSchema = external_exports.object({
30985
31169
  name: external_exports.string(),
30986
31170
  short: external_exports.string(),
30987
- tagline: external_exports.string(),
31171
+ title: external_exports.string(),
30988
31172
  theme: external_exports.object({
30989
31173
  light: external_exports.object({
30990
31174
  accent: external_exports.string(),
@@ -31073,12 +31257,24 @@ var e2ePerformFetchInputSchema = external_exports.object({
31073
31257
  url: external_exports.string().url(),
31074
31258
  headers: external_exports.record(external_exports.string(), external_exports.string()).optional()
31075
31259
  }).strict();
31260
+ var e2eSpawnInvocationInputSchema = external_exports.object({
31261
+ agentId: external_exports.string().min(1),
31262
+ prompt: external_exports.string().min(1),
31263
+ schema: external_exports.record(external_exports.string(), external_exports.unknown()),
31264
+ templateId: external_exports.string().min(1).optional(),
31265
+ image: external_exports.string().min(1).optional(),
31266
+ connections: external_exports.array(external_exports.string()).optional(),
31267
+ ttlMs: external_exports.number().int().positive().optional()
31268
+ }).strict();
31269
+ var spawnInvocationResultSchema = external_exports.object({ id: external_exports.string().min(1) }).strict();
31076
31270
  var slackFireMentionInputSchema = external_exports.object({
31077
31271
  user: external_exports.string().min(1),
31078
31272
  channel: external_exports.string().min(1),
31079
31273
  ts: external_exports.string().min(1),
31080
31274
  threadTs: external_exports.string().optional(),
31081
- text: external_exports.string()
31275
+ text: external_exports.string(),
31276
+ /** Workspace id; present so the fake exercises the live-streaming path. */
31277
+ teamId: external_exports.string().optional()
31082
31278
  }).strict();
31083
31279
  var slackFireCommandInputSchema = external_exports.object({
31084
31280
  text: external_exports.string(),
@@ -31110,6 +31306,33 @@ var slackOutboundRecordSchema = external_exports.discriminatedUnion("kind", [
31110
31306
  kind: external_exports.literal("upload"),
31111
31307
  channelId: external_exports.string(),
31112
31308
  filename: external_exports.string()
31309
+ }).strict(),
31310
+ external_exports.object({
31311
+ kind: external_exports.literal("stream_start"),
31312
+ channel: external_exports.string(),
31313
+ threadTs: external_exports.string(),
31314
+ ts: external_exports.string(),
31315
+ text: external_exports.string(),
31316
+ recipientTeamId: external_exports.string().optional(),
31317
+ recipientUserId: external_exports.string().optional()
31318
+ }).strict(),
31319
+ external_exports.object({
31320
+ kind: external_exports.literal("stream_append"),
31321
+ channel: external_exports.string(),
31322
+ ts: external_exports.string(),
31323
+ text: external_exports.string()
31324
+ }).strict(),
31325
+ external_exports.object({
31326
+ kind: external_exports.literal("stream_stop"),
31327
+ channel: external_exports.string(),
31328
+ ts: external_exports.string(),
31329
+ text: external_exports.string().optional()
31330
+ }).strict(),
31331
+ external_exports.object({
31332
+ kind: external_exports.literal("status"),
31333
+ channel: external_exports.string(),
31334
+ threadTs: external_exports.string(),
31335
+ status: external_exports.string()
31113
31336
  }).strict()
31114
31337
  ]);
31115
31338
  var slackReadOutboundResultSchema = external_exports.object({ records: external_exports.array(slackOutboundRecordSchema) }).strict();
@@ -31436,6 +31659,7 @@ var EXIT_RULE_NOT_FOUND = 6;
31436
31659
  var EXIT_AGENT_NOT_REACHABLE = 7;
31437
31660
  var EXIT_APPROVAL_NOT_ACTIONABLE = 8;
31438
31661
  var EXIT_SCHEDULE_NOT_FOUND = 9;
31662
+ var EXIT_TERMS_NOT_ACCEPTED = 10;
31439
31663
  var EXIT_SIGINT = 130;
31440
31664
 
31441
31665
  // src/modules/agent/commands/create.ts
@@ -37247,11 +37471,16 @@ function buildDisallowCommand(deps) {
37247
37471
  // src/modules/channel/commands/list.ts
37248
37472
  function renderHuman(channels, allowedUserEmails) {
37249
37473
  const channelsBlock = channels.length === 0 ? "No channels connected.\n" : renderTable([
37250
- ["TYPE", "IDENTIFIER"],
37474
+ ["TYPE", "IDENTIFIER", "MODE"],
37251
37475
  ...channels.map(
37252
37476
  (c2) => (
37253
37477
  // Telegram has no readable identifier — the bot token is write-only.
37254
- c2.type === "slack" /* Slack */ ? ["slack", c2.slackChannelId] : ["telegram", "\u2014"]
37478
+ // Telegram is structurally shared-only.
37479
+ c2.type === "slack" /* Slack */ ? [
37480
+ "slack",
37481
+ c2.slackChannelId,
37482
+ c2.ambient ? "shared (ambient)" : c2.mode ?? "person-scoped"
37483
+ ] : ["telegram", "\u2014", "shared"]
37255
37484
  )
37256
37485
  )
37257
37486
  ]);
@@ -37305,7 +37534,7 @@ function buildListCommand3(deps) {
37305
37534
  // src/modules/channel/commands/precheck.ts
37306
37535
  var UNAVAILABLE_MESSAGE = {
37307
37536
  ["slack" /* Slack */]: "Slack app token not configured",
37308
- ["telegram" /* Telegram */]: "Telegram channel not enabled"
37537
+ ["telegram" /* Telegram */]: "Telegram bot not configured"
37309
37538
  };
37310
37539
  async function ensureProviderAvailable(svc, provider, host) {
37311
37540
  const res = await svc.available();
@@ -37323,11 +37552,17 @@ async function ensureProviderAvailable(svc, provider, host) {
37323
37552
  // src/modules/channel/commands/slack-connect.ts
37324
37553
  function buildSlackConnectCommand(deps) {
37325
37554
  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(
37555
+ "--mode <mode>",
37556
+ "access mode: 'person-scoped' (default) or 'shared'"
37557
+ ).option(
37558
+ "--ambient",
37559
+ "ambient mode (shared only): the agent reads along and may chime in without being mentioned"
37560
+ ).option(
37326
37561
  "--server <url>",
37327
37562
  "override the configured server URL for this call"
37328
37563
  ).option("--json", "emit the updated ChannelConfig[] as JSON").addHelpText(
37329
37564
  "after",
37330
- "\nExamples:\n dam channel slack connect my-agent --channel-id C0123ABCD\n"
37565
+ "\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
37566
  ).action(
37332
37567
  async (ref, opts) => {
37333
37568
  const channelId = opts.channelId.trim();
@@ -37335,6 +37570,17 @@ function buildSlackConnectCommand(deps) {
37335
37570
  process.stderr.write("error: --channel-id must not be empty\n");
37336
37571
  process.exit(EXIT_INVALID_INPUT);
37337
37572
  }
37573
+ if (opts.mode !== void 0 && opts.mode !== "shared" && opts.mode !== "person-scoped") {
37574
+ process.stderr.write(
37575
+ "error: --mode must be 'shared' or 'person-scoped'\n"
37576
+ );
37577
+ process.exit(EXIT_INVALID_INPUT);
37578
+ }
37579
+ const mode = opts.mode;
37580
+ if (opts.ambient && mode !== "shared") {
37581
+ process.stderr.write("error: --ambient requires --mode shared\n");
37582
+ process.exit(EXIT_INVALID_INPUT);
37583
+ }
37338
37584
  const host = await resolveActiveHost(deps, {
37339
37585
  flag: opts.server ? { server: opts.server } : void 0,
37340
37586
  exitCodes: {
@@ -37352,7 +37598,12 @@ function buildSlackConnectCommand(deps) {
37352
37598
  }
37353
37599
  const svc = deps.createChannelService(host);
37354
37600
  await ensureProviderAvailable(svc, "slack" /* Slack */, host);
37355
- const res = await svc.connectSlack(resolved.value.id, channelId);
37601
+ const res = await svc.connectSlack(
37602
+ resolved.value.id,
37603
+ channelId,
37604
+ mode,
37605
+ opts.ambient
37606
+ );
37356
37607
  if (!res.ok) {
37357
37608
  if (res.error.kind === "channel-conflict") {
37358
37609
  process.stderr.write(
@@ -37368,17 +37619,54 @@ function buildSlackConnectCommand(deps) {
37368
37619
  printServiceError(res.error, host);
37369
37620
  process.exit(EXIT_RUNTIME_FAILURE);
37370
37621
  }
37622
+ if (mode === "shared") {
37623
+ const slackCh = res.value.find((c2) => c2.type === "slack" /* Slack */);
37624
+ if (!slackCh || slackCh.mode !== "shared") {
37625
+ const rolledBack = await svc.disconnectSlack(resolved.value.id);
37626
+ if (rolledBack.ok) {
37627
+ process.stderr.write(
37628
+ "error: this server does not support shared access mode \u2014 binding rolled back\n"
37629
+ );
37630
+ } else {
37631
+ process.stderr.write(
37632
+ "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"
37633
+ );
37634
+ printServiceError(rolledBack.error, host);
37635
+ }
37636
+ process.exit(EXIT_RUNTIME_FAILURE);
37637
+ }
37638
+ }
37639
+ if (opts.ambient) {
37640
+ const slackCh = res.value.find((c2) => c2.type === "slack" /* Slack */);
37641
+ if (slackCh?.type === "slack" /* Slack */ && !slackCh.ambient) {
37642
+ process.stderr.write(
37643
+ "error: this server does not support ambient mode \u2014 the channel is connected in shared mode without it\n"
37644
+ );
37645
+ process.exit(EXIT_RUNTIME_FAILURE);
37646
+ }
37647
+ }
37371
37648
  if (opts.json) {
37372
37649
  process.stdout.write(`${JSON.stringify(res.value)}
37373
37650
  `);
37374
37651
  } else {
37375
37652
  process.stdout.write(
37376
- `\u2713 Slack channel ${channelId} connected to ${resolved.value.name}.
37653
+ `\u2713 Slack channel ${channelId} connected to ${resolved.value.name}${mode === "shared" ? opts.ambient ? " in shared mode with ambient on" : " in shared mode" : ""}.
37377
37654
  `
37378
37655
  );
37379
- process.stderr.write(
37380
- "hint: users must run `/platform login` inside Slack before they can drive this agent\n"
37381
- );
37656
+ if (opts.ambient) {
37657
+ process.stderr.write(
37658
+ "note: the agent reads along in this channel and may chime in without being mentioned; the channel gets a visible notice\n"
37659
+ );
37660
+ }
37661
+ if (mode === "shared") {
37662
+ process.stderr.write(
37663
+ "note: everyone in the channel drives this agent under its credentials; your Terms-of-Use acceptance covers every turn\n"
37664
+ );
37665
+ } else {
37666
+ process.stderr.write(
37667
+ "hint: users must run `/platform login` inside Slack before they can drive this agent\n"
37668
+ );
37669
+ }
37382
37670
  }
37383
37671
  process.exit(EXIT_SUCCESS);
37384
37672
  }
@@ -37430,131 +37718,19 @@ function buildSlackDisconnectCommand(deps) {
37430
37718
  });
37431
37719
  }
37432
37720
 
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
37721
  // src/modules/channel/services/channel-service.ts
37548
37722
  function createChannelService(deps) {
37549
37723
  return {
37550
37724
  async available() {
37551
37725
  return trpcCall(() => deps.trpc.channels.available.query());
37552
37726
  },
37553
- async connectSlack(id, slackChannelId) {
37727
+ async connectSlack(id, slackChannelId, mode, ambient) {
37554
37728
  try {
37555
37729
  const agent = await deps.trpc.agents.connectSlack.mutate({
37556
37730
  id,
37557
- slackChannelId
37731
+ slackChannelId,
37732
+ ...mode ? { mode } : {},
37733
+ ...ambient ? { ambient: true } : {}
37558
37734
  });
37559
37735
  return ok2(agent.channels);
37560
37736
  } catch (e2) {
@@ -37574,29 +37750,6 @@ function createChannelService(deps) {
37574
37750
  const agent = await deps.trpc.agents.disconnectSlack.mutate({ id });
37575
37751
  return agent.channels;
37576
37752
  });
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
37753
  }
37601
37754
  };
37602
37755
  }
@@ -37612,7 +37765,7 @@ function composeChannelModule(opts) {
37612
37765
  createChannelService: createService
37613
37766
  };
37614
37767
  const parent = new Command("channel").description(
37615
- "Manage messenger channel bindings (Slack, Telegram)"
37768
+ "Manage messenger channel bindings (Telegram chats bind in-chat via /login)"
37616
37769
  );
37617
37770
  parent.addCommand(
37618
37771
  buildAvailableCommand({
@@ -37634,12 +37787,6 @@ function composeChannelModule(opts) {
37634
37787
  slack.addCommand(buildSlackConnectCommand(agentScoped));
37635
37788
  slack.addCommand(buildSlackDisconnectCommand(agentScoped));
37636
37789
  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
37790
  const allowScoped = {
37644
37791
  compatService: opts.compatService,
37645
37792
  configService: opts.configService,
@@ -39647,6 +39794,7 @@ function createAcpSessionClient(opts) {
39647
39794
  }
39648
39795
 
39649
39796
  // src/modules/chat/infrastructure/terminal-bridge.ts
39797
+ var TERMINAL_MODE_RESET = "\x1B[<u\x1B[?1049l\x1B[?1000;1002;1003;1005;1006;1015;1016l\x1B[?1004l\x1B[?2004l\x1B[?25h\x1B[0m";
39650
39798
  function connectTerminalBridge({
39651
39799
  host,
39652
39800
  token,
@@ -39680,6 +39828,7 @@ function connectTerminalBridge({
39680
39828
  } catch {
39681
39829
  }
39682
39830
  stdin.pause();
39831
+ stdout.write(TERMINAL_MODE_RESET);
39683
39832
  if (ws.readyState === wrapper_default.OPEN || ws.readyState === wrapper_default.CONNECTING)
39684
39833
  ws.close();
39685
39834
  resolve4(result);
@@ -40174,7 +40323,7 @@ function createProcessEnvReader() {
40174
40323
 
40175
40324
  // src/modules/cli/infrastructure/package-version.ts
40176
40325
  function readPackageVersion() {
40177
- return true ? "0.2.7" : DEV_VERSION;
40326
+ return true ? "0.2.9-rc1" : DEV_VERSION;
40178
40327
  }
40179
40328
 
40180
40329
  // src/modules/cli/infrastructure/version-probe.ts
@@ -40410,7 +40559,13 @@ function buildConnectCommand(deps) {
40410
40559
  ).option(
40411
40560
  "--audience <audience>",
40412
40561
  "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(
40562
+ ).option("--app-slug <slug>", "input: GitHub App slug").option("--app-id <id>", "input: GitHub App ID (GitHub App installation)").option(
40563
+ "--installation-id <id>",
40564
+ "input: GitHub App installation ID (GitHub App installation)"
40565
+ ).option(
40566
+ "--private-key <pem>",
40567
+ 'input: GitHub App private key \u2014 PEM or base64; use --private-key "$(cat app.pem)" (GitHub App installation)'
40568
+ ).option("--header-name <name>", "input: header name").option("--value-format <format>", "input: header value format").option(
40414
40569
  "--env-name <name>",
40415
40570
  "input: expose the credential to the agent as this env var (custom header credentials)"
40416
40571
  ).option("--value <value>", "input: header secret value").option(
@@ -40433,7 +40588,7 @@ function buildConnectCommand(deps) {
40433
40588
  String(DEFAULT_TIMEOUT_SECONDS3)
40434
40589
  ).addHelpText(
40435
40590
  "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"
40591
+ '\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
40592
  ).action(async (providerOrUrl, opts) => {
40438
40593
  const json2 = opts.json ?? false;
40439
40594
  const timeoutSeconds = parseTimeout2(opts.timeout);
@@ -40655,8 +40810,9 @@ async function collectInputs(template, opts, json2) {
40655
40810
  }
40656
40811
  for (const input of missing) {
40657
40812
  const prompt = input.secret ? Ce : Re;
40813
+ const label = input.label ?? labelFor(input.name);
40658
40814
  const answer = await prompt({
40659
- message: labelFor(input.name),
40815
+ message: input.hint ? `${label} \u2014 ${input.hint}` : label,
40660
40816
  validate: (v2) => v2 && v2.trim().length > 0 ? void 0 : "Required"
40661
40817
  });
40662
40818
  if (q(answer)) exitCancelled({ json: json2 });
@@ -40695,6 +40851,25 @@ function buildPayload(template, name, values) {
40695
40851
  ...v2("valueFormat") ? { valueFormat: v2("valueFormat") } : {},
40696
40852
  ...v2("envName") ? { envName: v2("envName") } : {}
40697
40853
  };
40854
+ case "github-app": {
40855
+ const appId = v2("appId");
40856
+ const installationId = v2("installationId");
40857
+ const privateKey = v2("privateKey");
40858
+ if (!appId) return { error: "the App ID is required (--app-id)" };
40859
+ if (!installationId) {
40860
+ return { error: "the installation ID is required (--installation-id)" };
40861
+ }
40862
+ if (!privateKey) {
40863
+ return { error: "the private key is required (--private-key)" };
40864
+ }
40865
+ return {
40866
+ ...common2,
40867
+ authKind: "github-app",
40868
+ appId,
40869
+ installationId,
40870
+ privateKey
40871
+ };
40872
+ }
40698
40873
  case "header": {
40699
40874
  const value = v2("value");
40700
40875
  if (!value) return { error: "the secret value is required (--value)" };
@@ -40817,6 +40992,9 @@ var FIELD_LABELS = {
40817
40992
  scopes: "Scopes (space-separated)",
40818
40993
  audience: "Audience",
40819
40994
  appSlug: "GitHub App slug",
40995
+ appId: "GitHub App ID",
40996
+ installationId: "GitHub App installation ID",
40997
+ privateKey: "GitHub App private key (PEM)",
40820
40998
  envName: "Env var name",
40821
40999
  caData: "Cluster CA certificate"
40822
41000
  };
@@ -44230,6 +44408,12 @@ function createSkillsService(deps) {
44230
44408
  const message = e2.message.replace(/\nplatform-cta:\S+/, "").trim();
44231
44409
  return err2({ kind: "source-needs-connection", message, cta });
44232
44410
  }
44411
+ if (e2?.data?.code === "FORBIDDEN") {
44412
+ return err2({
44413
+ kind: "source-needs-connection",
44414
+ message: e2 instanceof Error ? e2.message : String(e2)
44415
+ });
44416
+ }
44233
44417
  return classifyWakeError(e2);
44234
44418
  }
44235
44419
  },
@@ -45159,6 +45343,248 @@ function composeTemplateModule(opts) {
45159
45343
  };
45160
45344
  }
45161
45345
 
45346
+ // src/modules/terms/commands/accept.ts
45347
+ function buildAcceptCommand(deps) {
45348
+ return new Command("accept").description(
45349
+ "Accept the current Terms of Use \u2014 interactive on a TTY, use --yes for CI/headless"
45350
+ ).option(
45351
+ "--yes",
45352
+ "accept without the interactive confirmation (required on a non-TTY)"
45353
+ ).option(
45354
+ "--expect-version <version>",
45355
+ "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)"
45356
+ ).option(
45357
+ "--server <url>",
45358
+ "override the configured server URL for this call"
45359
+ ).option("--json", "emit the acceptance result as JSON").action(
45360
+ async (opts) => {
45361
+ const host = await resolveActiveHost(deps, {
45362
+ flag: opts.server ? { server: opts.server } : void 0,
45363
+ exitCodes: {
45364
+ runtimeFailure: EXIT_RUNTIME_FAILURE,
45365
+ belowFloor: EXIT_BELOW_FLOOR
45366
+ }
45367
+ });
45368
+ if (!opts.yes && !process.stdin.isTTY) {
45369
+ process.stderr.write(
45370
+ "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"
45371
+ );
45372
+ process.exit(EXIT_RUNTIME_FAILURE);
45373
+ }
45374
+ const service = deps.createTermsService(host);
45375
+ const doc = await service.document();
45376
+ if (!doc.ok) {
45377
+ printServiceError(doc.error, host);
45378
+ process.exit(EXIT_RUNTIME_FAILURE);
45379
+ }
45380
+ const currentVersion = doc.value.version;
45381
+ if (opts.expectVersion && opts.expectVersion !== currentVersion) {
45382
+ process.stderr.write(
45383
+ `error: reviewed version ${opts.expectVersion} does not match the server's current version ${currentVersion}; re-review before accepting
45384
+ `
45385
+ );
45386
+ process.exit(EXIT_RUNTIME_FAILURE);
45387
+ }
45388
+ if (!opts.yes) {
45389
+ process.stderr.write(
45390
+ `Terms of Use \u2014 version ${currentVersion}
45391
+
45392
+ ${doc.value.text}
45393
+
45394
+ `
45395
+ );
45396
+ if (!await confirm("Accept the Terms of Use?", {
45397
+ timeoutMs: 15 * 6e4
45398
+ }))
45399
+ exitCancelled({ json: opts.json });
45400
+ }
45401
+ const accepted = await service.accept(currentVersion);
45402
+ if (!accepted.ok) {
45403
+ if (accepted.error.kind === "transport" && accepted.error.serverCode === "PRECONDITION_FAILED") {
45404
+ process.stderr.write(
45405
+ "error: the Terms of Use changed while you were reviewing; re-run `dam terms accept` to review the current version\n"
45406
+ );
45407
+ } else {
45408
+ printServiceError(accepted.error, host);
45409
+ }
45410
+ process.exit(EXIT_RUNTIME_FAILURE);
45411
+ }
45412
+ if (opts.json) {
45413
+ return writeStdoutAndExit(
45414
+ `${JSON.stringify({ accepted: true, version: currentVersion })}
45415
+ `,
45416
+ EXIT_SUCCESS
45417
+ );
45418
+ }
45419
+ return writeStdoutAndExit(
45420
+ `Accepted Terms of Use ${currentVersion}.
45421
+ `,
45422
+ EXIT_SUCCESS
45423
+ );
45424
+ }
45425
+ );
45426
+ }
45427
+
45428
+ // src/modules/terms/commands/show.ts
45429
+ function buildShowCommand(deps) {
45430
+ return new Command("show").description(
45431
+ "Print the current Terms of Use text, version, and your acceptance state"
45432
+ ).option(
45433
+ "--server <url>",
45434
+ "override the configured server URL for this call"
45435
+ ).option("--json", "emit the raw Terms of Use document as JSON").action(async (opts) => {
45436
+ const host = await resolveActiveHost(deps, {
45437
+ flag: opts.server ? { server: opts.server } : void 0,
45438
+ exitCodes: {
45439
+ runtimeFailure: EXIT_RUNTIME_FAILURE,
45440
+ belowFloor: EXIT_BELOW_FLOOR
45441
+ }
45442
+ });
45443
+ const service = deps.createTermsService(host);
45444
+ const [doc, latest] = await Promise.all([
45445
+ service.document(),
45446
+ service.latestAcceptance()
45447
+ ]);
45448
+ if (!doc.ok) {
45449
+ printServiceError(doc.error, host);
45450
+ process.exit(EXIT_RUNTIME_FAILURE);
45451
+ }
45452
+ if (!latest.ok) {
45453
+ printServiceError(latest.error, host);
45454
+ process.exit(EXIT_RUNTIME_FAILURE);
45455
+ }
45456
+ if (opts.json) {
45457
+ return writeStdoutAndExit(
45458
+ `${JSON.stringify(doc.value)}
45459
+ `,
45460
+ EXIT_SUCCESS
45461
+ );
45462
+ }
45463
+ 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.`;
45464
+ return writeStdoutAndExit(
45465
+ `Terms of Use \u2014 version ${doc.value.version}
45466
+ ${acceptState}
45467
+
45468
+ ${doc.value.text}
45469
+ `,
45470
+ EXIT_SUCCESS
45471
+ );
45472
+ });
45473
+ }
45474
+
45475
+ // src/modules/terms/commands/status.ts
45476
+ function buildStatusCommand2(deps) {
45477
+ return new Command("status").description(
45478
+ "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"
45479
+ ).option(
45480
+ "--server <url>",
45481
+ "override the configured server URL for this call"
45482
+ ).option("--json", "emit the acceptance state as JSON").action(async (opts) => {
45483
+ const host = await resolveActiveHost(deps, {
45484
+ flag: opts.server ? { server: opts.server } : void 0,
45485
+ exitCodes: {
45486
+ runtimeFailure: EXIT_RUNTIME_FAILURE,
45487
+ belowFloor: EXIT_BELOW_FLOOR
45488
+ }
45489
+ });
45490
+ const service = deps.createTermsService(host);
45491
+ const [current, latest] = await Promise.all([
45492
+ service.current(),
45493
+ service.latestAcceptance()
45494
+ ]);
45495
+ if (!current.ok) {
45496
+ printServiceError(current.error, host);
45497
+ process.exit(EXIT_RUNTIME_FAILURE);
45498
+ }
45499
+ if (!latest.ok) {
45500
+ printServiceError(latest.error, host);
45501
+ process.exit(EXIT_RUNTIME_FAILURE);
45502
+ }
45503
+ const acceptedVersion = latest.value?.version ?? null;
45504
+ const accepted = acceptedVersion === current.value.version;
45505
+ const exitCode = accepted ? EXIT_SUCCESS : EXIT_TERMS_NOT_ACCEPTED;
45506
+ if (opts.json) {
45507
+ return writeStdoutAndExit(
45508
+ `${JSON.stringify({ currentVersion: current.value.version, acceptedVersion, accepted })}
45509
+ `,
45510
+ exitCode
45511
+ );
45512
+ }
45513
+ 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).`;
45514
+ return writeStdoutAndExit(`${line}
45515
+ `, exitCode);
45516
+ });
45517
+ }
45518
+
45519
+ // src/modules/terms/services/terms-service.ts
45520
+ function createTermsService(deps) {
45521
+ const doFetch = deps.fetch ?? fetch;
45522
+ const base = deps.host.replace(/\/+$/, "");
45523
+ return {
45524
+ async document() {
45525
+ let res;
45526
+ try {
45527
+ res = await doFetch(`${base}/api/terms`);
45528
+ } catch (e2) {
45529
+ return err2({
45530
+ kind: "transport",
45531
+ reason: e2 instanceof Error ? e2.message : "cannot reach server"
45532
+ });
45533
+ }
45534
+ if (!res.ok)
45535
+ return err2({
45536
+ kind: "transport",
45537
+ reason: `GET /api/terms returned ${res.status}`
45538
+ });
45539
+ let body;
45540
+ try {
45541
+ body = await res.json();
45542
+ } catch {
45543
+ return err2({
45544
+ kind: "transport",
45545
+ reason: "malformed Terms of Use document from server"
45546
+ });
45547
+ }
45548
+ const parsed = termsDocumentSchema.safeParse(body);
45549
+ if (!parsed.success)
45550
+ return err2({
45551
+ kind: "transport",
45552
+ reason: "malformed Terms of Use document from server"
45553
+ });
45554
+ return ok2(parsed.data);
45555
+ },
45556
+ async current() {
45557
+ return trpcCall(() => deps.trpc.terms.current.query());
45558
+ },
45559
+ async latestAcceptance() {
45560
+ return trpcCall(() => deps.trpc.terms.latestAcceptance.query());
45561
+ },
45562
+ async accept(version2) {
45563
+ return trpcCall(() => deps.trpc.terms.accept.mutate({ version: version2 }));
45564
+ }
45565
+ };
45566
+ }
45567
+
45568
+ // src/modules/terms/compose.ts
45569
+ function composeTermsModule(opts) {
45570
+ const createService = (host) => createTermsService({
45571
+ trpc: createTrpcClient({ host, tokenProvider: opts.tokenProvider }),
45572
+ host
45573
+ });
45574
+ const scoped = {
45575
+ compatService: opts.compatService,
45576
+ configService: opts.configService,
45577
+ createTermsService: createService
45578
+ };
45579
+ const parent = new Command("terms").description(
45580
+ "View and accept the deployment's Terms of Use"
45581
+ );
45582
+ parent.addCommand(buildShowCommand(scoped));
45583
+ parent.addCommand(buildStatusCommand2(scoped));
45584
+ parent.addCommand(buildAcceptCommand(scoped));
45585
+ return { commands: [parent] };
45586
+ }
45587
+
45162
45588
  // src/compose.ts
45163
45589
  function compose(opts = {}) {
45164
45590
  const cli = composeCliModule({ configPath: opts.configPath });
@@ -45251,6 +45677,11 @@ function compose(opts = {}) {
45251
45677
  compatService: cli.services.compatService,
45252
45678
  createAgentService: agent.exports.createService
45253
45679
  });
45680
+ const terms = composeTermsModule({
45681
+ tokenProvider: auth.exports.tokenProvider,
45682
+ configService: cli.services.configService,
45683
+ compatService: cli.services.compatService
45684
+ });
45254
45685
  const program3 = new Command();
45255
45686
  program3.name("dam").description("Command-line client for a Platform deployment").version(cli.cliVersion);
45256
45687
  for (const command of cli.commands) program3.addCommand(command);
@@ -45268,6 +45699,7 @@ function compose(opts = {}) {
45268
45699
  for (const command of ssh.commands) program3.addCommand(command);
45269
45700
  for (const command of channel.commands) program3.addCommand(command);
45270
45701
  for (const command of metrics.commands) program3.addCommand(command);
45702
+ for (const command of terms.commands) program3.addCommand(command);
45271
45703
  return program3;
45272
45704
  }
45273
45705
 
@@ -45278,9 +45710,7 @@ try {
45278
45710
  } catch (err3) {
45279
45711
  if (err3 instanceof TermsStaleAtTransportError) {
45280
45712
  process.stderr.write(
45281
- `error: Terms of Use acceptance required
45282
- hint: open ${err3.host} to accept
45283
- `
45713
+ "error: Terms of Use acceptance required\nhint: run `dam terms accept` to review and accept\n"
45284
45714
  );
45285
45715
  process.exit(1);
45286
45716
  }
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",
4
+ "version": "0.2.9-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",