@robbeverhelst/do 1.151.0 → 1.153.0

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/do.js +301 -3
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -2571,6 +2571,7 @@ var tenants = pgTable("tenants", {
2571
2571
  id: text("id").primaryKey(),
2572
2572
  name: text("name").notNull(),
2573
2573
  backdrop: text("backdrop"),
2574
+ gaardHouseUrl: text("gaard_house_url"),
2574
2575
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
2575
2576
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
2576
2577
  deletedAt: timestamp("deleted_at", { withTimezone: true })
@@ -18024,7 +18025,7 @@ var spaceSchema = createSelectSchema(tenants, {
18024
18025
  createdAt: exports_external.coerce.date(),
18025
18026
  updatedAt: exports_external.coerce.date(),
18026
18027
  deletedAt: exports_external.coerce.date().nullable()
18027
- });
18028
+ }).omit({ gaardHouseUrl: true });
18028
18029
  var backdropIdSchema = exports_external.string().trim().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "a backdrop id is lowercase words joined by dashes").max(40);
18029
18030
  var updateSpaceInputSchema = exports_external.object({
18030
18031
  name: exports_external.string().trim().min(1).max(120).optional(),
@@ -18050,6 +18051,60 @@ var linkGoogleInputSchema = exports_external.object({
18050
18051
  state: exports_external.string().min(1)
18051
18052
  });
18052
18053
 
18054
+ // ../../packages/schema/src/contracts/changes.ts
18055
+ var changeOpSchema = exports_external.enum(["created", "updated", "deleted"]);
18056
+ var CHANGE_ENTITY_SCOPES = {
18057
+ task: "tasks:read",
18058
+ project: "projects:read",
18059
+ area: "areas:read",
18060
+ routine: "routines:read",
18061
+ habit: "habits:read",
18062
+ occurrence: "occurrences:read",
18063
+ note: "notes:read",
18064
+ contact: "contacts:read",
18065
+ collection_item: "collections:read",
18066
+ inventory_item: "inventory:read",
18067
+ shopping_list: "shopping:read",
18068
+ recipe: "meals:read",
18069
+ meal_plan: "meals:read",
18070
+ event: "calendar:read",
18071
+ time_block: "calendar:read",
18072
+ place: "places:read"
18073
+ };
18074
+ var CHANGE_ENTITIES = Object.keys(CHANGE_ENTITY_SCOPES);
18075
+ var changeEntitySchema = exports_external.enum(CHANGE_ENTITIES);
18076
+ var changeSchema = exports_external.object({
18077
+ entity: changeEntitySchema,
18078
+ id: exports_external.string(),
18079
+ op: changeOpSchema,
18080
+ changedAt: exports_external.coerce.date()
18081
+ });
18082
+ var MAX_CHANGES_LIMIT = 500;
18083
+ var CHANGES_SINCE_NOW = "now";
18084
+ var CURSOR_RE = /^(\d+)-(\d+)$/;
18085
+ function parseChangeCursor(raw) {
18086
+ const m = CURSOR_RE.exec(raw);
18087
+ if (!m)
18088
+ return null;
18089
+ const txid = Number(m[1]);
18090
+ const seq = Number(m[2]);
18091
+ if (!Number.isSafeInteger(txid) || !Number.isSafeInteger(seq))
18092
+ return null;
18093
+ return { txid, seq };
18094
+ }
18095
+ var changesQuerySchema = exports_external.object({
18096
+ since: exports_external.string().refine((s) => s === CHANGES_SINCE_NOW || parseChangeCursor(s) !== null, {
18097
+ message: "since must be a cursor from a prior page's nextCursor, or 'now'"
18098
+ }).optional(),
18099
+ limit: exports_external.coerce.number().int().positive().transform((n) => Math.min(n, MAX_CHANGES_LIMIT)).optional(),
18100
+ entity: exports_external.string().transform((s) => s.split(",").map((e) => e.trim()).filter((e) => e.length > 0)).pipe(exports_external.array(changeEntitySchema).min(1)).optional()
18101
+ });
18102
+ var changesPageSchema = exports_external.object({
18103
+ data: exports_external.array(changeSchema),
18104
+ nextCursor: exports_external.string(),
18105
+ hasMore: exports_external.boolean()
18106
+ });
18107
+
18053
18108
  // ../../packages/schema/src/contracts/value-sets.ts
18054
18109
  var areaColorSchema = exports_external.enum(areaColor.enumValues);
18055
18110
  var reviewCadenceSchema = exports_external.enum(reviewCadence.enumValues);
@@ -19071,6 +19126,22 @@ var importPlacesResultSchema = exports_external.object({
19071
19126
  skipped: exports_external.number().int().min(0),
19072
19127
  places: exports_external.array(placeViewSchema)
19073
19128
  });
19129
+ function asUrl(value) {
19130
+ try {
19131
+ return new URL(value);
19132
+ } catch {
19133
+ return null;
19134
+ }
19135
+ }
19136
+ var gaardHouseUrlSchema = exports_external.string().trim().max(2048).url("that isn't a URL").refine((value) => asUrl(value)?.protocol === "https:", "the URL must start with https://").refine((value) => {
19137
+ const url2 = asUrl(value);
19138
+ return !url2 || url2.username === "" && url2.password === "";
19139
+ }, "the URL can't hold a username or password");
19140
+ var placesSourceSchema = exports_external.object({ gaardHouseUrl: exports_external.string().nullable() });
19141
+ var updatePlacesSourceInputSchema = exports_external.object({
19142
+ gaardHouseUrl: exports_external.union([exports_external.literal("").transform(() => null), gaardHouseUrlSchema]).nullable()
19143
+ }).strict();
19144
+ var importLinkedHouseInputSchema = exports_external.object({ houseIndex: exports_external.number().int().min(0).default(0) }).strict();
19074
19145
 
19075
19146
  // ../../packages/schema/src/contracts/modules.ts
19076
19147
  var MODULE_KEYS = [
@@ -19532,6 +19603,110 @@ var timeBlockRangeQuerySchema = exports_external.object({
19532
19603
  path: ["to"]
19533
19604
  });
19534
19605
 
19606
+ // ../../packages/schema/src/contracts/agent-chat.ts
19607
+ var agentConnectionSourceSchema = exports_external.enum(["space", "env"]);
19608
+ var agentConnectionSchema = exports_external.object({
19609
+ connected: exports_external.boolean(),
19610
+ source: agentConnectionSourceSchema.nullable(),
19611
+ endpoint: exports_external.string().nullable(),
19612
+ model: exports_external.string().nullable(),
19613
+ hasKey: exports_external.boolean(),
19614
+ agentId: exports_external.string().nullable(),
19615
+ autoFile: exports_external.boolean(),
19616
+ updatedAt: exports_external.coerce.date().nullable()
19617
+ });
19618
+ var agentEndpointSchema = exports_external.string().trim().max(2000).url().refine((value) => /^https?:\/\//i.test(value), "endpoint must be an http(s) URL");
19619
+ var saveAgentConnectionInputSchema = exports_external.object({
19620
+ endpoint: agentEndpointSchema,
19621
+ apiKey: exports_external.string().trim().min(1).max(4000).nullable().optional(),
19622
+ model: exports_external.string().trim().min(1).max(200).nullable().optional(),
19623
+ agentId: exports_external.string().trim().min(1).max(64).nullable().optional(),
19624
+ autoFile: exports_external.boolean().optional()
19625
+ }).strict();
19626
+ var agentPageContextSchema = exports_external.object({
19627
+ route: exports_external.string().trim().min(1).max(500).startsWith("/"),
19628
+ nodeId: exports_external.string().trim().min(1).max(64).nullable()
19629
+ }).strict();
19630
+ var agentChatInputSchema = exports_external.object({
19631
+ message: exports_external.string().trim().min(1).max(8000),
19632
+ context: agentPageContextSchema
19633
+ }).strict();
19634
+ var agentChatEventSchema = exports_external.discriminatedUnion("type", [
19635
+ exports_external.object({ type: exports_external.literal("delta"), text: exports_external.string() }),
19636
+ exports_external.object({ type: exports_external.literal("done") }),
19637
+ exports_external.object({ type: exports_external.literal("error"), message: exports_external.string() })
19638
+ ]);
19639
+ var agentChangeValueSchema = exports_external.union([
19640
+ exports_external.string(),
19641
+ exports_external.number(),
19642
+ exports_external.boolean(),
19643
+ exports_external.null(),
19644
+ exports_external.array(exports_external.string())
19645
+ ]);
19646
+ var agentChangeFieldSchema = exports_external.object({
19647
+ field: exports_external.string(),
19648
+ before: agentChangeValueSchema,
19649
+ after: agentChangeValueSchema,
19650
+ beforeLabel: exports_external.string().nullable().optional(),
19651
+ afterLabel: exports_external.string().nullable().optional()
19652
+ });
19653
+ var agentChangeSchema = exports_external.object({
19654
+ entity: changeEntitySchema,
19655
+ id: exports_external.string(),
19656
+ op: changeOpSchema,
19657
+ changedAt: exports_external.coerce.date(),
19658
+ title: exports_external.string().nullable(),
19659
+ actor: actorRefSchema,
19660
+ fields: exports_external.array(agentChangeFieldSchema).optional()
19661
+ });
19662
+ var agentChangesPageSchema = exports_external.object({
19663
+ data: exports_external.array(agentChangeSchema),
19664
+ nextCursor: exports_external.string()
19665
+ });
19666
+ var agentChangesQuerySchema = exports_external.object({
19667
+ since: exports_external.string().refine((s) => s === "now" || /^\d+-\d+$/.test(s), {
19668
+ message: "since must be a cursor from a prior page's nextCursor, or 'now'"
19669
+ })
19670
+ });
19671
+ var agentConfirmationKindSchema = exports_external.enum(["delete", "cancel", "google", "orca", "bulk"]);
19672
+ var agentConfirmationStatusSchema = exports_external.enum([
19673
+ "pending",
19674
+ "confirmed",
19675
+ "done",
19676
+ "failed",
19677
+ "declined"
19678
+ ]);
19679
+ var agentConfirmationSchema = exports_external.object({
19680
+ id: exports_external.string(),
19681
+ kind: agentConfirmationKindSchema,
19682
+ summary: exports_external.string(),
19683
+ items: exports_external.array(exports_external.object({ id: exports_external.string(), title: exports_external.string().nullable() })),
19684
+ method: exports_external.string(),
19685
+ path: exports_external.string(),
19686
+ status: agentConfirmationStatusSchema,
19687
+ agent: exports_external.object({ id: exports_external.string(), name: exports_external.string().nullable() }),
19688
+ createdAt: exports_external.coerce.date(),
19689
+ decidedAt: exports_external.coerce.date().nullable()
19690
+ });
19691
+ var agentUndoInputSchema = exports_external.object({
19692
+ id: exports_external.string().trim().min(1).max(64),
19693
+ since: exports_external.string().regex(/^\d+-\d+$/, "since must be a change-feed cursor")
19694
+ }).strict();
19695
+ var agentUndoResultSchema = exports_external.object({
19696
+ id: exports_external.string(),
19697
+ undone: exports_external.enum(["removed", "restored"])
19698
+ });
19699
+ var agentRedoResultSchema = exports_external.object({
19700
+ id: exports_external.string(),
19701
+ redone: exports_external.literal(true)
19702
+ });
19703
+ var agentFilingSchema = exports_external.object({
19704
+ confident: exports_external.boolean().default(false),
19705
+ areaId: exports_external.string().nullable().optional(),
19706
+ owner: exports_external.string().nullable().optional(),
19707
+ date: exports_external.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional()
19708
+ });
19709
+
19535
19710
  // ../../packages/schema/src/contracts/roles.ts
19536
19711
  var permissionSetSchema = createSelectSchema(permissionSets, {
19537
19712
  createdAt: exports_external.coerce.date(),
@@ -20631,7 +20806,7 @@ function parseCapture2(input, now) {
20631
20806
 
20632
20807
  // src/meta.ts
20633
20808
  var CLI_NAME = "do";
20634
- var VERSION = "1.151.0";
20809
+ var VERSION = "1.153.0";
20635
20810
  var API_PREFIX = "/api/v1";
20636
20811
  var DEFAULT_API_URL = "http://localhost:3001";
20637
20812
 
@@ -21530,6 +21705,68 @@ var COMMAND_SPECS = [
21530
21705
  returns: "None",
21531
21706
  notes: "Gated behind --allow-destructive + an orca:write key. Idempotent."
21532
21707
  },
21708
+ {
21709
+ path: ["agents", "list"],
21710
+ summary: "List the Space's Agents (id + name).",
21711
+ args: [],
21712
+ flags: [],
21713
+ scopes: ["agents:read"],
21714
+ returns: "AgentSummary",
21715
+ notes: "Creating an Agent and minting its keys stays a member's job in the app."
21716
+ },
21717
+ {
21718
+ path: ["agent", "connection", "get"],
21719
+ summary: "Show the agent runtime the Echo panel's chat goes to (never its key).",
21720
+ args: [],
21721
+ flags: [],
21722
+ scopes: ["agents:read"],
21723
+ returns: "AgentConnection",
21724
+ notes: "hasKey says whether a key is set; the key itself is write-only."
21725
+ },
21726
+ {
21727
+ path: ["agent", "connection", "set"],
21728
+ summary: "Point the Echo panel at an agent runtime (replaces the connection).",
21729
+ args: [],
21730
+ flags: [
21731
+ { name: "endpoint", type: "string", description: "The runtime's base URL (required)." },
21732
+ {
21733
+ name: "model",
21734
+ type: "string",
21735
+ description: "The model to ask for, if the runtime needs one."
21736
+ },
21737
+ {
21738
+ name: "agent",
21739
+ type: "string",
21740
+ description: "The Agent the runtime acts as: its id or name (see `agents list`)."
21741
+ },
21742
+ {
21743
+ name: "auto-file",
21744
+ type: "boolean",
21745
+ description: "Let Echo file new captures (needs --agent)."
21746
+ },
21747
+ { name: "no-auto-file", type: "boolean", description: "Stop Echo filing new captures." },
21748
+ {
21749
+ name: "key-stdin",
21750
+ type: "boolean",
21751
+ description: "Read the runtime's API key from stdin's first line instead of DO_AGENT_API_KEY."
21752
+ },
21753
+ { name: "clear-key", type: "boolean", description: "Remove the stored key." }
21754
+ ],
21755
+ scopes: ["agents:write"],
21756
+ returns: "AgentConnection",
21757
+ notes: "The runtime's key is read from DO_AGENT_API_KEY or, with --key-stdin, from stdin \u2014 never a flag. With neither, the stored key is kept. --model and --agent are replaced as sent (left out clears them). Owner/admin only (canManageSpace)."
21758
+ },
21759
+ {
21760
+ path: ["agent", "connection", "remove"],
21761
+ summary: "Forget the Space's agent connection (destructive).",
21762
+ args: [],
21763
+ flags: [
21764
+ { name: "allow-destructive", type: "boolean", description: "Required to actually remove." }
21765
+ ],
21766
+ scopes: ["agents:write"],
21767
+ returns: "None",
21768
+ notes: "Owner/admin only. An Agent's key gets a 428 confirmation_required until the member confirms in the Echo panel."
21769
+ },
21533
21770
  {
21534
21771
  path: ["settings", "get"],
21535
21772
  summary: "Show the caller's settings for the Space.",
@@ -22051,7 +22288,7 @@ var COMMAND_SPECS = [
22051
22288
  }
22052
22289
  ];
22053
22290
  function matchCommandSpec(positionals) {
22054
- for (const len of [2, 1]) {
22291
+ for (const len of [3, 2, 1]) {
22055
22292
  const candidate = positionals.slice(0, len);
22056
22293
  if (candidate.length < len)
22057
22294
  continue;
@@ -23217,6 +23454,35 @@ var HANDLERS = {
23217
23454
  const input = parseInput(logMetricInputSchema, { value, ...at ? { occurredAt: at } : {} });
23218
23455
  return { data: await requireClient().post(`/metrics/${key}`, input), render: renderRecord };
23219
23456
  },
23457
+ "agents list": async ({ requireClient }) => ({
23458
+ data: await requireClient().getPage("/agents"),
23459
+ render: renderList
23460
+ }),
23461
+ "agent connection get": async ({ requireClient }) => ({
23462
+ data: await requireClient().get("/agent/connection"),
23463
+ render: renderRecord
23464
+ }),
23465
+ "agent connection set": async ({ io, flags, requireClient }) => {
23466
+ const endpoint = flagString(flags, "endpoint");
23467
+ if (endpoint === undefined)
23468
+ throw usageError("agent connection set needs --endpoint <url>");
23469
+ const client = requireClient();
23470
+ const agent = flagString(flags, "agent");
23471
+ const autoFile = flagBool(flags, "auto-file") ? true : flagBool(flags, "no-auto-file") ? false : undefined;
23472
+ const body = parseInput(saveAgentConnectionInputSchema, {
23473
+ endpoint,
23474
+ model: flagString(flags, "model"),
23475
+ agentId: agent === undefined ? undefined : await resolveAgentId(client, agent),
23476
+ autoFile,
23477
+ apiKey: await readAgentKey(io, flags)
23478
+ });
23479
+ return { data: await client.put("/agent/connection", body), render: renderRecord };
23480
+ },
23481
+ "agent connection remove": async ({ flags, requireClient }) => {
23482
+ assertDestructiveAllowed(flags, "remove the agent connection");
23483
+ await requireClient().delete("/agent/connection");
23484
+ return { data: { ok: true, removed: "agent connection" }, render: renderRecord };
23485
+ },
23220
23486
  "settings get": async ({ requireClient }) => ({
23221
23487
  data: await requireClient().get("/settings"),
23222
23488
  render: renderRecord
@@ -23635,6 +23901,38 @@ function redact(secret) {
23635
23901
  return "********";
23636
23902
  return `${secret.slice(0, 6)}\u2026${secret.slice(-2)}`;
23637
23903
  }
23904
+ var AGENT_KEY_ENV = "DO_AGENT_API_KEY";
23905
+ async function readAgentKey(io, flags) {
23906
+ if (flagBool(flags, "clear-key"))
23907
+ return null;
23908
+ if (flagBool(flags, "key-stdin")) {
23909
+ let first;
23910
+ if (io.stdin) {
23911
+ for await (const line2 of io.stdin) {
23912
+ first = line2;
23913
+ break;
23914
+ }
23915
+ }
23916
+ const key = first?.trim();
23917
+ if (!key)
23918
+ throw usageError("--key-stdin read no key: pipe the key in on stdin");
23919
+ return key;
23920
+ }
23921
+ const fromEnv = io.env[AGENT_KEY_ENV]?.trim();
23922
+ return fromEnv ? fromEnv : undefined;
23923
+ }
23924
+ var ULID = /^[0-9A-HJKMNP-TV-Z]{26}$/i;
23925
+ async function resolveAgentId(client, agent) {
23926
+ if (ULID.test(agent))
23927
+ return agent;
23928
+ const { data } = await client.getPage("/agents");
23929
+ const wanted = agent.trim().toLowerCase();
23930
+ const match = data.find((a) => a.name.toLowerCase() === wanted);
23931
+ if (!match) {
23932
+ throw new CliError("not_found", `no Agent named ${agent} \u2014 see \`do agents list\``, EXIT.NOT_FOUND);
23933
+ }
23934
+ return match.id;
23935
+ }
23638
23936
  function requireWrite(entity, op) {
23639
23937
  if (!entity.write) {
23640
23938
  throw usageError(`\`${entity.name}\` is read-only \u2014 \`${op}\` is not available`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robbeverhelst/do",
3
- "version": "1.151.0",
3
+ "version": "1.153.0",
4
4
  "private": false,
5
5
  "description": "do — the agent-drivable CLI over the public /api/v1 surface",
6
6
  "type": "module",