@ghostystudio/cli 0.84.0 → 0.86.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 (3) hide show
  1. package/README.md +9 -0
  2. package/dist/index.js +477 -309
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -973,10 +973,10 @@ async function readConfig() {
973
973
  }
974
974
  }
975
975
  async function writeConfig(c) {
976
- const path = configPath();
977
- await mkdir(dirname(path), { recursive: true });
978
- await writeFile(path, JSON.stringify(c, null, 2), { mode: 384 });
979
- await chmod(path, 384);
976
+ const path2 = configPath();
977
+ await mkdir(dirname(path2), { recursive: true });
978
+ await writeFile(path2, JSON.stringify(c, null, 2), { mode: 384 });
979
+ await chmod(path2, 384);
980
980
  }
981
981
  async function baseUrl() {
982
982
  const url = process.env.GHOSTY_URL || (await readConfig()).baseUrl || "https://www.ghosty.studio";
@@ -1166,16 +1166,16 @@ async function currentToken() {
1166
1166
  if (cfg.expiresAt !== void 0 && cfg.expiresAt - Date.now() < 6e4) cfg = await refresh(cfg) ?? cfg;
1167
1167
  return cfg.accessToken;
1168
1168
  }
1169
- async function postLong(path, body, timeoutMs = 9e5) {
1169
+ async function postLong(path2, body, timeoutMs = 9e5) {
1170
1170
  const base = await baseUrl();
1171
1171
  const token = await currentToken();
1172
1172
  const httpsRequest = base.startsWith("https:") ? (await import("node:https")).request : (await import("node:http")).request;
1173
1173
  const data = JSON.stringify(body);
1174
1174
  return new Promise((resolve3) => {
1175
- const req = httpsRequest(`${base}${path}`, {
1175
+ const req = httpsRequest(`${base}${path2}`, {
1176
1176
  method: "POST",
1177
1177
  timeout: timeoutMs,
1178
- headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.84.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
1178
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.86.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
1179
1179
  }, (res) => {
1180
1180
  let buf = "";
1181
1181
  res.on("data", (c) => buf += c);
@@ -1200,8 +1200,8 @@ function retryAfterSeconds(h, now = Date.now()) {
1200
1200
  const t2 = Date.parse(h);
1201
1201
  return Number.isNaN(t2) ? null : Math.max(0, Math.round((t2 - now) / 1e3));
1202
1202
  }
1203
- async function api(path, opts = {}) {
1204
- const res = await request(path, opts);
1203
+ async function api(path2, opts = {}) {
1204
+ const res = await request(path2, opts);
1205
1205
  const text = await res.text();
1206
1206
  if (!text) return void 0;
1207
1207
  try {
@@ -1210,14 +1210,14 @@ async function api(path, opts = {}) {
1210
1210
  return text;
1211
1211
  }
1212
1212
  }
1213
- async function request(path, opts = {}, retried = false) {
1213
+ async function request(path2, opts = {}, retried = false) {
1214
1214
  const base = await baseUrl();
1215
1215
  const token = await currentToken();
1216
- const res = await fetch(`${base}${path}`, {
1216
+ const res = await fetch(`${base}${path2}`, {
1217
1217
  method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
1218
1218
  headers: {
1219
1219
  authorization: `Bearer ${token}`,
1220
- "user-agent": `ghosty-cli/${"0.84.0"}`,
1220
+ "user-agent": `ghosty-cli/${"0.86.0"}`,
1221
1221
  "accept-language": lang(),
1222
1222
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
1223
1223
  ...opts.headers
@@ -1227,7 +1227,7 @@ async function request(path, opts = {}, retried = false) {
1227
1227
  });
1228
1228
  if (res.status === 401 && !retried && !explicitToken()) {
1229
1229
  const cfg = await refresh(await readConfig());
1230
- if (cfg) return request(path, opts, true);
1230
+ if (cfg) return request(path2, opts, true);
1231
1231
  }
1232
1232
  if (!res.ok) throw await toError(res);
1233
1233
  return res;
@@ -1264,7 +1264,7 @@ var mteams_exports = {};
1264
1264
  __export(mteams_exports, {
1265
1265
  mteamsCmd: () => mteamsCmd
1266
1266
  });
1267
- import { writeFile as writeFile8 } from "node:fs/promises";
1267
+ import { writeFile as writeFile9 } from "node:fs/promises";
1268
1268
  function list(bots) {
1269
1269
  return bots.length ? table(
1270
1270
  bots.map((b) => ({
@@ -1281,18 +1281,18 @@ function list(bots) {
1281
1281
  }
1282
1282
  async function mteamsCmd(sub, id, botId, v) {
1283
1283
  if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), EX);
1284
- const path = `/api/v2/agents/${enc9(id)}/mteams`;
1284
+ const path2 = `/api/v2/agents/${enc10(id)}/mteams`;
1285
1285
  switch (sub) {
1286
1286
  case void 0:
1287
1287
  case "ls":
1288
1288
  case "list": {
1289
- const r = await api(path);
1289
+ const r = await api(path2);
1290
1290
  return out(r, () => list(r.bots));
1291
1291
  }
1292
1292
  case "connect": {
1293
1293
  const secret = v.secret ?? process.env.GHOSTY_MTEAMS_SECRET;
1294
1294
  if (!v["app-id"] || !v.tenant || !secret) throw usage(t("Missing --app-id, --tenant or the secret (--secret or GHOSTY_MTEAMS_SECRET).", "Faltan --app-id, --tenant o el secreto (--secret o GHOSTY_MTEAMS_SECRET)."), EX);
1295
- const r = await api(path, { method: "POST", body: { appId: v["app-id"], tenantId: v.tenant, secret, botName: v.name } });
1295
+ const r = await api(path2, { method: "POST", body: { appId: v["app-id"], tenantId: v.tenant, secret, botName: v.name } });
1296
1296
  const b = r.bots.find((x) => x.appId === v["app-id"]);
1297
1297
  return out(
1298
1298
  r,
@@ -1303,36 +1303,36 @@ ${dim(`${t("Next", "Sigue")}: ghosty channels mteams zip ${id} ${b?.id ?? "<bot-
1303
1303
  }
1304
1304
  case "zip": {
1305
1305
  if (!botId) throw usage(t("Missing bot id.", "Falta el id del bot."), EX);
1306
- const res = await request(`${path}/${enc9(botId)}/zip`);
1306
+ const res = await request(`${path2}/${enc10(botId)}/zip`);
1307
1307
  const bytes = Buffer.from(await res.arrayBuffer());
1308
1308
  const file = v.out ?? "ghosty-teams.zip";
1309
- await writeFile8(file, bytes);
1309
+ await writeFile9(file, bytes);
1310
1310
  return out({ saved: file, bytes: bytes.length }, () => `${lavender("\u2713")} ${t(`Saved ${file}: upload it in Teams admin \u2192 Manage apps.`, `Se guard\xF3 ${file}: s\xFAbelo en Teams admin \u2192 Administrar apps.`)}`);
1311
1311
  }
1312
1312
  case "on":
1313
1313
  case "off": {
1314
1314
  if (!botId) throw usage(t("Missing bot id.", "Falta el id del bot."), EX);
1315
- const r = await api(path, { method: "PATCH", body: { botId, enabled: sub === "on" } });
1315
+ const r = await api(path2, { method: "PATCH", body: { botId, enabled: sub === "on" } });
1316
1316
  return out(r, () => list(r.bots));
1317
1317
  }
1318
1318
  case "rm":
1319
1319
  case "disconnect": {
1320
1320
  if (!botId) throw usage(t("Missing bot id.", "Falta el id del bot."), EX);
1321
- const r = await api(path, { method: "DELETE", body: { botId } });
1321
+ const r = await api(path2, { method: "DELETE", body: { botId } });
1322
1322
  return out(r, () => list(r.bots));
1323
1323
  }
1324
1324
  default:
1325
1325
  throw usage(t(`Unknown subcommand: ${sub}.`, `Subcomando desconocido: ${sub}.`), EX);
1326
1326
  }
1327
1327
  }
1328
- var enc9, EX;
1328
+ var enc10, EX;
1329
1329
  var init_mteams = __esm({
1330
1330
  "src/commands/mteams.ts"() {
1331
1331
  "use strict";
1332
1332
  init_api();
1333
1333
  init_util();
1334
1334
  init_i18n();
1335
- enc9 = encodeURIComponent;
1335
+ enc10 = encodeURIComponent;
1336
1336
  EX = "ghosty channels mteams ls <agent-id> | connect <agent-id> --app-id \u2026 --tenant \u2026 [--secret \u2026] [--name \u2026] | zip <agent-id> <bot-id> [--out F.zip] | on|off|rm <agent-id> <bot-id>";
1337
1337
  }
1338
1338
  });
@@ -1414,17 +1414,17 @@ var require_visit = __commonJS({
1414
1414
  visit.BREAK = BREAK;
1415
1415
  visit.SKIP = SKIP2;
1416
1416
  visit.REMOVE = REMOVE;
1417
- function visit_(key, node, visitor, path) {
1418
- const ctrl = callVisitor(key, node, visitor, path);
1417
+ function visit_(key, node, visitor, path2) {
1418
+ const ctrl = callVisitor(key, node, visitor, path2);
1419
1419
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1420
- replaceNode(key, path, ctrl);
1421
- return visit_(key, ctrl, visitor, path);
1420
+ replaceNode(key, path2, ctrl);
1421
+ return visit_(key, ctrl, visitor, path2);
1422
1422
  }
1423
1423
  if (typeof ctrl !== "symbol") {
1424
1424
  if (identity.isCollection(node)) {
1425
- path = Object.freeze(path.concat(node));
1425
+ path2 = Object.freeze(path2.concat(node));
1426
1426
  for (let i = 0; i < node.items.length; ++i) {
1427
- const ci = visit_(i, node.items[i], visitor, path);
1427
+ const ci = visit_(i, node.items[i], visitor, path2);
1428
1428
  if (typeof ci === "number")
1429
1429
  i = ci - 1;
1430
1430
  else if (ci === BREAK)
@@ -1435,13 +1435,13 @@ var require_visit = __commonJS({
1435
1435
  }
1436
1436
  }
1437
1437
  } else if (identity.isPair(node)) {
1438
- path = Object.freeze(path.concat(node));
1439
- const ck = visit_("key", node.key, visitor, path);
1438
+ path2 = Object.freeze(path2.concat(node));
1439
+ const ck = visit_("key", node.key, visitor, path2);
1440
1440
  if (ck === BREAK)
1441
1441
  return BREAK;
1442
1442
  else if (ck === REMOVE)
1443
1443
  node.key = null;
1444
- const cv = visit_("value", node.value, visitor, path);
1444
+ const cv = visit_("value", node.value, visitor, path2);
1445
1445
  if (cv === BREAK)
1446
1446
  return BREAK;
1447
1447
  else if (cv === REMOVE)
@@ -1462,17 +1462,17 @@ var require_visit = __commonJS({
1462
1462
  visitAsync.BREAK = BREAK;
1463
1463
  visitAsync.SKIP = SKIP2;
1464
1464
  visitAsync.REMOVE = REMOVE;
1465
- async function visitAsync_(key, node, visitor, path) {
1466
- const ctrl = await callVisitor(key, node, visitor, path);
1465
+ async function visitAsync_(key, node, visitor, path2) {
1466
+ const ctrl = await callVisitor(key, node, visitor, path2);
1467
1467
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1468
- replaceNode(key, path, ctrl);
1469
- return visitAsync_(key, ctrl, visitor, path);
1468
+ replaceNode(key, path2, ctrl);
1469
+ return visitAsync_(key, ctrl, visitor, path2);
1470
1470
  }
1471
1471
  if (typeof ctrl !== "symbol") {
1472
1472
  if (identity.isCollection(node)) {
1473
- path = Object.freeze(path.concat(node));
1473
+ path2 = Object.freeze(path2.concat(node));
1474
1474
  for (let i = 0; i < node.items.length; ++i) {
1475
- const ci = await visitAsync_(i, node.items[i], visitor, path);
1475
+ const ci = await visitAsync_(i, node.items[i], visitor, path2);
1476
1476
  if (typeof ci === "number")
1477
1477
  i = ci - 1;
1478
1478
  else if (ci === BREAK)
@@ -1483,13 +1483,13 @@ var require_visit = __commonJS({
1483
1483
  }
1484
1484
  }
1485
1485
  } else if (identity.isPair(node)) {
1486
- path = Object.freeze(path.concat(node));
1487
- const ck = await visitAsync_("key", node.key, visitor, path);
1486
+ path2 = Object.freeze(path2.concat(node));
1487
+ const ck = await visitAsync_("key", node.key, visitor, path2);
1488
1488
  if (ck === BREAK)
1489
1489
  return BREAK;
1490
1490
  else if (ck === REMOVE)
1491
1491
  node.key = null;
1492
- const cv = await visitAsync_("value", node.value, visitor, path);
1492
+ const cv = await visitAsync_("value", node.value, visitor, path2);
1493
1493
  if (cv === BREAK)
1494
1494
  return BREAK;
1495
1495
  else if (cv === REMOVE)
@@ -1516,23 +1516,23 @@ var require_visit = __commonJS({
1516
1516
  }
1517
1517
  return visitor;
1518
1518
  }
1519
- function callVisitor(key, node, visitor, path) {
1519
+ function callVisitor(key, node, visitor, path2) {
1520
1520
  if (typeof visitor === "function")
1521
- return visitor(key, node, path);
1521
+ return visitor(key, node, path2);
1522
1522
  if (identity.isMap(node))
1523
- return visitor.Map?.(key, node, path);
1523
+ return visitor.Map?.(key, node, path2);
1524
1524
  if (identity.isSeq(node))
1525
- return visitor.Seq?.(key, node, path);
1525
+ return visitor.Seq?.(key, node, path2);
1526
1526
  if (identity.isPair(node))
1527
- return visitor.Pair?.(key, node, path);
1527
+ return visitor.Pair?.(key, node, path2);
1528
1528
  if (identity.isScalar(node))
1529
- return visitor.Scalar?.(key, node, path);
1529
+ return visitor.Scalar?.(key, node, path2);
1530
1530
  if (identity.isAlias(node))
1531
- return visitor.Alias?.(key, node, path);
1531
+ return visitor.Alias?.(key, node, path2);
1532
1532
  return void 0;
1533
1533
  }
1534
- function replaceNode(key, path, node) {
1535
- const parent = path[path.length - 1];
1534
+ function replaceNode(key, path2, node) {
1535
+ const parent = path2[path2.length - 1];
1536
1536
  if (identity.isCollection(parent)) {
1537
1537
  parent.items[key] = node;
1538
1538
  } else if (identity.isPair(parent)) {
@@ -2144,10 +2144,10 @@ var require_Collection = __commonJS({
2144
2144
  var createNode = require_createNode();
2145
2145
  var identity = require_identity();
2146
2146
  var Node = require_Node();
2147
- function collectionFromPath(schema, path, value) {
2147
+ function collectionFromPath(schema, path2, value) {
2148
2148
  let v = value;
2149
- for (let i = path.length - 1; i >= 0; --i) {
2150
- const k = path[i];
2149
+ for (let i = path2.length - 1; i >= 0; --i) {
2150
+ const k = path2[i];
2151
2151
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
2152
2152
  const a = [];
2153
2153
  a[k] = v;
@@ -2166,7 +2166,7 @@ var require_Collection = __commonJS({
2166
2166
  sourceObjects: /* @__PURE__ */ new Map()
2167
2167
  });
2168
2168
  }
2169
- var isEmptyPath = (path) => path == null || typeof path === "object" && !!path[Symbol.iterator]().next().done;
2169
+ var isEmptyPath = (path2) => path2 == null || typeof path2 === "object" && !!path2[Symbol.iterator]().next().done;
2170
2170
  var Collection = class extends Node.NodeBase {
2171
2171
  constructor(type, schema) {
2172
2172
  super(type);
@@ -2196,11 +2196,11 @@ var require_Collection = __commonJS({
2196
2196
  * be a Pair instance or a `{ key, value }` object, which may not have a key
2197
2197
  * that already exists in the map.
2198
2198
  */
2199
- addIn(path, value) {
2200
- if (isEmptyPath(path))
2199
+ addIn(path2, value) {
2200
+ if (isEmptyPath(path2))
2201
2201
  this.add(value);
2202
2202
  else {
2203
- const [key, ...rest] = path;
2203
+ const [key, ...rest] = path2;
2204
2204
  const node = this.get(key, true);
2205
2205
  if (identity.isCollection(node))
2206
2206
  node.addIn(rest, value);
@@ -2214,8 +2214,8 @@ var require_Collection = __commonJS({
2214
2214
  * Removes a value from the collection.
2215
2215
  * @returns `true` if the item was found and removed.
2216
2216
  */
2217
- deleteIn(path) {
2218
- const [key, ...rest] = path;
2217
+ deleteIn(path2) {
2218
+ const [key, ...rest] = path2;
2219
2219
  if (rest.length === 0)
2220
2220
  return this.delete(key);
2221
2221
  const node = this.get(key, true);
@@ -2229,8 +2229,8 @@ var require_Collection = __commonJS({
2229
2229
  * scalar values from their surrounding node; to disable set `keepScalar` to
2230
2230
  * `true` (collections are always returned intact).
2231
2231
  */
2232
- getIn(path, keepScalar) {
2233
- const [key, ...rest] = path;
2232
+ getIn(path2, keepScalar) {
2233
+ const [key, ...rest] = path2;
2234
2234
  const node = this.get(key, true);
2235
2235
  if (rest.length === 0)
2236
2236
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -2248,8 +2248,8 @@ var require_Collection = __commonJS({
2248
2248
  /**
2249
2249
  * Checks if the collection includes a value with the key `key`.
2250
2250
  */
2251
- hasIn(path) {
2252
- const [key, ...rest] = path;
2251
+ hasIn(path2) {
2252
+ const [key, ...rest] = path2;
2253
2253
  if (rest.length === 0)
2254
2254
  return this.has(key);
2255
2255
  const node = this.get(key, true);
@@ -2259,8 +2259,8 @@ var require_Collection = __commonJS({
2259
2259
  * Sets a value in this collection. For `!!set`, `value` needs to be a
2260
2260
  * boolean to add/remove the item from the set.
2261
2261
  */
2262
- setIn(path, value) {
2263
- const [key, ...rest] = path;
2262
+ setIn(path2, value) {
2263
+ const [key, ...rest] = path2;
2264
2264
  if (rest.length === 0) {
2265
2265
  this.set(key, value);
2266
2266
  } else {
@@ -4775,9 +4775,9 @@ var require_Document = __commonJS({
4775
4775
  this.contents.add(value);
4776
4776
  }
4777
4777
  /** Adds a value to the document. */
4778
- addIn(path, value) {
4778
+ addIn(path2, value) {
4779
4779
  if (assertCollection(this.contents))
4780
- this.contents.addIn(path, value);
4780
+ this.contents.addIn(path2, value);
4781
4781
  }
4782
4782
  /**
4783
4783
  * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
@@ -4852,14 +4852,14 @@ var require_Document = __commonJS({
4852
4852
  * Removes a value from the document.
4853
4853
  * @returns `true` if the item was found and removed.
4854
4854
  */
4855
- deleteIn(path) {
4856
- if (Collection.isEmptyPath(path)) {
4855
+ deleteIn(path2) {
4856
+ if (Collection.isEmptyPath(path2)) {
4857
4857
  if (this.contents == null)
4858
4858
  return false;
4859
4859
  this.contents = null;
4860
4860
  return true;
4861
4861
  }
4862
- return assertCollection(this.contents) ? this.contents.deleteIn(path) : false;
4862
+ return assertCollection(this.contents) ? this.contents.deleteIn(path2) : false;
4863
4863
  }
4864
4864
  /**
4865
4865
  * Returns item at `key`, or `undefined` if not found. By default unwraps
@@ -4874,10 +4874,10 @@ var require_Document = __commonJS({
4874
4874
  * scalar values from their surrounding node; to disable set `keepScalar` to
4875
4875
  * `true` (collections are always returned intact).
4876
4876
  */
4877
- getIn(path, keepScalar) {
4878
- if (Collection.isEmptyPath(path))
4877
+ getIn(path2, keepScalar) {
4878
+ if (Collection.isEmptyPath(path2))
4879
4879
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
4880
- return identity.isCollection(this.contents) ? this.contents.getIn(path, keepScalar) : void 0;
4880
+ return identity.isCollection(this.contents) ? this.contents.getIn(path2, keepScalar) : void 0;
4881
4881
  }
4882
4882
  /**
4883
4883
  * Checks if the document includes a value with the key `key`.
@@ -4888,10 +4888,10 @@ var require_Document = __commonJS({
4888
4888
  /**
4889
4889
  * Checks if the document includes a value at `path`.
4890
4890
  */
4891
- hasIn(path) {
4892
- if (Collection.isEmptyPath(path))
4891
+ hasIn(path2) {
4892
+ if (Collection.isEmptyPath(path2))
4893
4893
  return this.contents !== void 0;
4894
- return identity.isCollection(this.contents) ? this.contents.hasIn(path) : false;
4894
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path2) : false;
4895
4895
  }
4896
4896
  /**
4897
4897
  * Sets a value in this document. For `!!set`, `value` needs to be a
@@ -4908,13 +4908,13 @@ var require_Document = __commonJS({
4908
4908
  * Sets a value in this document. For `!!set`, `value` needs to be a
4909
4909
  * boolean to add/remove the item from the set.
4910
4910
  */
4911
- setIn(path, value) {
4912
- if (Collection.isEmptyPath(path)) {
4911
+ setIn(path2, value) {
4912
+ if (Collection.isEmptyPath(path2)) {
4913
4913
  this.contents = value;
4914
4914
  } else if (this.contents == null) {
4915
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path), value);
4915
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path2), value);
4916
4916
  } else if (assertCollection(this.contents)) {
4917
- this.contents.setIn(path, value);
4917
+ this.contents.setIn(path2, value);
4918
4918
  }
4919
4919
  }
4920
4920
  /**
@@ -6875,9 +6875,9 @@ var require_cst_visit = __commonJS({
6875
6875
  visit.BREAK = BREAK;
6876
6876
  visit.SKIP = SKIP2;
6877
6877
  visit.REMOVE = REMOVE;
6878
- visit.itemAtPath = (cst, path) => {
6878
+ visit.itemAtPath = (cst, path2) => {
6879
6879
  let item = cst;
6880
- for (const [field, index] of path) {
6880
+ for (const [field, index] of path2) {
6881
6881
  const tok = item?.[field];
6882
6882
  if (tok && "items" in tok) {
6883
6883
  item = tok.items[index];
@@ -6886,23 +6886,23 @@ var require_cst_visit = __commonJS({
6886
6886
  }
6887
6887
  return item;
6888
6888
  };
6889
- visit.parentCollection = (cst, path) => {
6890
- const parent = visit.itemAtPath(cst, path.slice(0, -1));
6891
- const field = path[path.length - 1][0];
6889
+ visit.parentCollection = (cst, path2) => {
6890
+ const parent = visit.itemAtPath(cst, path2.slice(0, -1));
6891
+ const field = path2[path2.length - 1][0];
6892
6892
  const coll = parent?.[field];
6893
6893
  if (coll && "items" in coll)
6894
6894
  return coll;
6895
6895
  throw new Error("Parent collection not found");
6896
6896
  };
6897
- function _visit(path, item, visitor) {
6898
- let ctrl = visitor(item, path);
6897
+ function _visit(path2, item, visitor) {
6898
+ let ctrl = visitor(item, path2);
6899
6899
  if (typeof ctrl === "symbol")
6900
6900
  return ctrl;
6901
6901
  for (const field of ["key", "value"]) {
6902
6902
  const token = item[field];
6903
6903
  if (token && "items" in token) {
6904
6904
  for (let i = 0; i < token.items.length; ++i) {
6905
- const ci = _visit(Object.freeze(path.concat([[field, i]])), token.items[i], visitor);
6905
+ const ci = _visit(Object.freeze(path2.concat([[field, i]])), token.items[i], visitor);
6906
6906
  if (typeof ci === "number")
6907
6907
  i = ci - 1;
6908
6908
  else if (ci === BREAK)
@@ -6913,10 +6913,10 @@ var require_cst_visit = __commonJS({
6913
6913
  }
6914
6914
  }
6915
6915
  if (typeof ctrl === "function" && field === "key")
6916
- ctrl = ctrl(item, path);
6916
+ ctrl = ctrl(item, path2);
6917
6917
  }
6918
6918
  }
6919
- return typeof ctrl === "function" ? ctrl(item, path) : ctrl;
6919
+ return typeof ctrl === "function" ? ctrl(item, path2) : ctrl;
6920
6920
  }
6921
6921
  exports.visit = visit;
6922
6922
  }
@@ -9074,23 +9074,23 @@ ${t("Change it?", "\xBFCambiarlo?")}`, { yes: v.yes, retry });
9074
9074
  if (sub === "renew") {
9075
9075
  slug = needSpace(slug ?? context, ex);
9076
9076
  if (!v.until) throw usage(t("Missing --until YYYY-MM-DD.", "Falta --until AAAA-MM-DD."), `ghosty spaces renew ${slug} --until 2026-11-01`);
9077
- const path = wsPath(slug, "/renew");
9078
- const plan = await api(path, { method: "POST", body: { until: v.until, dryRun: true } });
9077
+ const path2 = wsPath(slug, "/renew");
9078
+ const plan = await api(path2, { method: "POST", body: { until: v.until, dryRun: true } });
9079
9079
  const line2 = t(`${plan.workspace} (${plan.plan}): paid until ${plan.from ?? "\u2014"} \u2192 ${plan.to}`, `${plan.workspace} (${plan.plan}): pagado hasta ${plan.from ?? "\u2014"} \u2192 ${plan.to}`);
9080
9080
  if (v["dry-run"]) return out(plan, () => `${line2}
9081
9081
  ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
9082
9082
  requireYesIfHeadless(v.yes, `ghosty spaces renew ${slug} --until ${v.until}`);
9083
9083
  await confirm(`${line2}
9084
9084
  ${t("Renew?", "\xBFRenovar?")}`, { yes: v.yes, retry: `ghosty spaces renew ${slug} --until ${v.until}` });
9085
- const r = await api(path, { method: "POST", body: { until: v.until, ...v.note ? { note: v.note } : {} } });
9085
+ const r = await api(path2, { method: "POST", body: { until: v.until, ...v.note ? { note: v.note } : {} } });
9086
9086
  return out(r, () => `${lavender("\u2713")} ${line2} ${dim(t("(logged in the workspace's audit)", "(queda en la bit\xE1cora del espacio)"))}`);
9087
9087
  }
9088
9088
  if (sub === "plan") {
9089
9089
  slug = needSpace(slug ?? context, "ghosty spaces plan <workspace> pro --courtesy --dry-run");
9090
9090
  if (!arg) throw usage(t("Which plan?", "\xBFQu\xE9 plan?"), `ghosty spaces plan ${slug} trial|pro|empresarial [--courtesy] [--until YYYY-MM-DD] --dry-run`);
9091
- const path = wsPath(slug, "/plan");
9091
+ const path2 = wsPath(slug, "/plan");
9092
9092
  const body = { plan: arg, ...v.courtesy ? { courtesy: true } : {}, ...v.until ? { until: v.until } : {}, ...v.note ? { note: v.note } : {} };
9093
- const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
9093
+ const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
9094
9094
  const text = planChangeText(plan);
9095
9095
  if (!plan.changed && !v.courtesy && !v.note) return out(plan, () => dim(t(`Nothing to do: ${plan.workspace} is already ${plan.before.plan}.`, `Nada que hacer: ${plan.workspace} ya es ${plan.before.plan}.`)));
9096
9096
  if (v["dry-run"]) return out(plan, () => `${text}
@@ -9099,15 +9099,15 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
9099
9099
  requireYesIfHeadless(v.yes, retry);
9100
9100
  await confirm(`${text}
9101
9101
  ${t("Change the plan?", "\xBFCambiar el plan?")}`, { yes: v.yes, retry });
9102
- const r = await api(path, { method: "POST", body });
9102
+ const r = await api(path2, { method: "POST", body });
9103
9103
  return out(r, () => `${lavender("\u2713")} ${planChangeText(r)}
9104
9104
  ${dim(t("(logged in the workspace's audit)", "(queda en la bit\xE1cora del espacio)"))}`);
9105
9105
  }
9106
9106
  if (sub === "rename") {
9107
9107
  slug = needSpace(slug, ex);
9108
9108
  if (!arg) throw usage(t("Missing the new name.", "Falta el nombre nuevo."), `ghosty spaces rename ${slug} <new> --dry-run`);
9109
- const path = wsPath(slug, "/rename");
9110
- const plan = await api(path, { method: "POST", body: { to: arg, dryRun: true } });
9109
+ const path2 = wsPath(slug, "/rename");
9110
+ const plan = await api(path2, { method: "POST", body: { to: arg, dryRun: true } });
9111
9111
  const text = `${plan.from} \u2192 ${bold(plan.to)}
9112
9112
  ${plan.warnings.map((w) => dim(` \xB7 ${serverText(w)}`)).join("\n")}`;
9113
9113
  if (v["dry-run"]) return out(plan, () => `${text}
@@ -9115,14 +9115,14 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
9115
9115
  requireYesIfHeadless(v.yes, `ghosty spaces rename ${slug} ${arg}`);
9116
9116
  await confirm(`${text}
9117
9117
  ${t("Rename?", "\xBFRenombrar?")}`, { yes: v.yes, expect: plan.from, retry: `ghosty spaces rename ${slug} ${arg}` });
9118
- const r = await api(path, { method: "POST", body: { to: arg } });
9118
+ const r = await api(path2, { method: "POST", body: { to: arg } });
9119
9119
  return out(r, () => `${lavender("\u2713")} ${t(`Renamed: ${r.from} \u2192 ${r.to}. Undo: ghosty spaces rename ${r.to} ${r.from}`, `Renombrado: ${r.from} \u2192 ${r.to}. Deshacer: ghosty spaces rename ${r.to} ${r.from}`)}`);
9120
9120
  }
9121
9121
  if (sub === "archive" || sub === "restore") {
9122
9122
  slug = needSpace(slug, `ghosty spaces ${sub} <workspace> --dry-run`);
9123
9123
  const archive = sub === "archive";
9124
- const path = wsPath(slug, "/archive");
9125
- const plan = await api(path, { method: "POST", body: { archive, dryRun: true } });
9124
+ const path2 = wsPath(slug, "/archive");
9125
+ const plan = await api(path2, { method: "POST", body: { archive, dryRun: true } });
9126
9126
  const line2 = `${plan.slug}: ${plan.from} \u2192 ${bold(plan.to)} ${dim(t(`(${plan.agents} agent(s); they and their data stay as they are)`, `(${plan.agents} agente(s); ellos y sus datos se quedan igual)`))}`;
9127
9127
  if (!plan.changed) return out(plan, () => dim(t(`Nothing to do: ${plan.slug} is already ${plan.from}.`, `Nada que hacer: ${plan.slug} ya est\xE1 ${plan.from}.`)));
9128
9128
  if (v["dry-run"]) return out(plan, () => `${line2}
@@ -9131,7 +9131,7 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
9131
9131
  requireYesIfHeadless(v.yes, retry);
9132
9132
  await confirm(`${line2}
9133
9133
  ${archive ? t("Archive it?", "\xBFArchivarlo?") : t("Restore it?", "\xBFRestaurarlo?")}`, { yes: v.yes, retry, ...archive ? { expect: plan.slug } : {} });
9134
- const r = await api(path, { method: "POST", body: { archive } });
9134
+ const r = await api(path2, { method: "POST", body: { archive } });
9135
9135
  return out(r, () => archive ? `${lavender("\u2713")} ${t(`Archived: ${r.slug}. Undo: ghosty spaces restore ${r.slug}`, `Archivado: ${r.slug}. Deshacer: ghosty spaces restore ${r.slug}`)}` : `${lavender("\u2713")} ${t(`Restored: ${r.slug}.`, `Restaurado: ${r.slug}.`)}`);
9136
9136
  }
9137
9137
  if (sub === "transfer") {
@@ -9139,8 +9139,8 @@ ${archive ? t("Archive it?", "\xBFArchivarlo?") : t("Restore it?", "\xBFRestaura
9139
9139
  const toEmail = v.to ?? arg;
9140
9140
  if (!toEmail) throw usage(t("Missing --to EMAIL.", "Falta --to CORREO."), `ghosty spaces transfer ${slug} --to new@owner.com --keep-as admin --copy-keys --dry-run`);
9141
9141
  const body = { to: toEmail, ...v["keep-as"] ? { keepAs: v["keep-as"] } : {}, ...v["copy-keys"] ? { copyKeys: true } : {} };
9142
- const path = wsPath(slug, "/transfer");
9143
- const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
9142
+ const path2 = wsPath(slug, "/transfer");
9143
+ const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
9144
9144
  const text = [
9145
9145
  `${plan.workspace}: ${plan.from ?? "\u2014"} \u2192 ${bold(plan.to)} ${dim(t(`(previous owner stays as ${plan.keepAs})`, `(el anterior queda como ${plan.keepAs})`))}`,
9146
9146
  `${t("agents", "agentes")}: ${plan.agents.join(", ") || "\u2014"}`,
@@ -9153,7 +9153,7 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
9153
9153
  requireYesIfHeadless(v.yes, retry);
9154
9154
  await confirm(`${text}
9155
9155
  ${t("Transfer it? Its agents' boxes are recycled (a turn in flight finishes first).", "\xBFTraspasarlo? Se reciclan las cajas de sus agentes (un turno en vuelo termina antes).")}`, { yes: v.yes, expect: plan.workspace, retry });
9156
- const r = await api(path, { method: "POST", body });
9156
+ const r = await api(path2, { method: "POST", body });
9157
9157
  return out(r, () => `${lavender("\u2713")} ${t(`${r.workspace} now belongs to ${r.to}; ${r.from ?? "\u2014"} stays as ${r.keepAs}. ${r.recycled} agent(s) recycled.`, `${r.workspace} ya es de ${r.to}; ${r.from ?? "\u2014"} queda como ${r.keepAs}. ${r.recycled} agente(s) reciclado(s).`)}${r.warnings.length ? `
9158
9158
  ${r.warnings.map((w) => red(` \xB7 ${serverText(w)}`)).join("\n")}` : ""}`);
9159
9159
  }
@@ -10046,10 +10046,10 @@ async function tryTurn(id, text, v) {
10046
10046
  out(r, () => (r.text ?? "") + (r.error ? `
10047
10047
  ${dim(`(${t("warning", "aviso")}: ${serverText(r.error)})`)}` : ""));
10048
10048
  }
10049
- async function filesCmd(sub, id, path, v) {
10049
+ async function filesCmd(sub, id, path2, v) {
10050
10050
  const ex = "ghosty files ls <agent-id> | get <agent-id> <path> | put <agent-id> <path> --file <local> | rm <agent-id> <path>";
10051
10051
  id = need(id, "agent id", ex);
10052
- const p = (path ?? "").replace(/^\/+/, "");
10052
+ const p = (path2 ?? "").replace(/^\/+/, "");
10053
10053
  switch (sub) {
10054
10054
  case "ls": {
10055
10055
  const r = await api(agentPath(id, `/files/${p}`));
@@ -10060,7 +10060,7 @@ ${dim(r.note)}` : ""}`);
10060
10060
  return;
10061
10061
  }
10062
10062
  case "get": {
10063
- need(path, "path", ex);
10063
+ need(path2, "path", ex);
10064
10064
  const res = await request(agentPath(id, `/files/${p}`));
10065
10065
  const bytes = Buffer.from(await res.arrayBuffer());
10066
10066
  if (v.out) {
@@ -10070,14 +10070,14 @@ ${dim(r.note)}` : ""}`);
10070
10070
  return;
10071
10071
  }
10072
10072
  case "put": {
10073
- need(path, "path", ex);
10073
+ need(path2, "path", ex);
10074
10074
  const data = v.file ? await readFile3(v.file) : await readStdin();
10075
10075
  const r = await api(agentPath(id, `/files/${p}`), { method: "PUT", raw: new Uint8Array(data), headers: { "content-type": "application/octet-stream" } });
10076
10076
  out(r ?? { path: p, bytes: data.length }, () => `${lavender("\u2713")} ${t("Wrote", "Escrito")} ${p} (${data.length} bytes).`);
10077
10077
  return;
10078
10078
  }
10079
10079
  case "rm": {
10080
- need(path, "path", ex);
10080
+ need(path2, "path", ex);
10081
10081
  await confirm(t(`Delete ${p} from the agent's machine?`, `\xBFBorrar ${p} de la m\xE1quina del agente?`), { yes: v.yes, retry: `ghosty files rm ${id} ${p}` });
10082
10082
  out(await api(agentPath(id, `/files/${p}`), { method: "DELETE" }) ?? { deleted: p }, () => `${lavender("\u2713")} ${t("Deleted", "Borrado")} ${p}`);
10083
10083
  return;
@@ -10125,10 +10125,10 @@ async function skillsCmd(sub, id, slug, v) {
10125
10125
  case "ls":
10126
10126
  case "list": {
10127
10127
  const r = await api(agentPath(id, "/skills"));
10128
- const short4 = (d) => (d ?? "").replace(/\s+/g, " ").slice(0, 70);
10128
+ const short5 = (d) => (d ?? "").replace(/\s+/g, " ").slice(0, 70);
10129
10129
  const rows = [
10130
- ...r.skills.map((s2) => ({ slug: s2.slug, source: "own", on: "on", description: short4(s2.description) })),
10131
- ...(r.house ?? []).map((s2) => ({ slug: s2.slug, source: "ghosty", on: s2.enabled ? "on" : "off", description: short4(s2.description) }))
10130
+ ...r.skills.map((s2) => ({ slug: s2.slug, source: "own", on: "on", description: short5(s2.description) })),
10131
+ ...(r.house ?? []).map((s2) => ({ slug: s2.slug, source: "ghosty", on: s2.enabled ? "on" : "off", description: short5(s2.description) }))
10132
10132
  ];
10133
10133
  const more = r.community.filter((c) => !c.installed).length;
10134
10134
  out(r, () => `${table(rows, ["slug", "source", "on", "description"])}
@@ -10417,8 +10417,8 @@ async function writeEmbedComponent(e, baseUrl2, file, force) {
10417
10417
  if (!e.token) throw usage(t("Turn the widget on first: ghosty agents embed enable <id> --domains yoursite.com", "Prende primero el widget: ghosty agents embed enable <id> --domains tusitio.com"));
10418
10418
  const ext = file.toLowerCase().match(/\.(tsx|jsx)$/)?.[1];
10419
10419
  if (!ext) throw usage(t("--out must end in .tsx or .jsx", "--out debe terminar en .tsx o .jsx"));
10420
- const path = resolve(file);
10421
- const exists = await readFile3(path).then(() => true, () => false);
10420
+ const path2 = resolve(file);
10421
+ const exists = await readFile3(path2).then(() => true, () => false);
10422
10422
  if (exists && !force) throw usage(t(`${file} already exists; add --force to overwrite it`, `${file} ya existe; agrega --force para pisarlo`));
10423
10423
  const src = reactComponentSource(
10424
10424
  {
@@ -10436,9 +10436,9 @@ async function writeEmbedComponent(e, baseUrl2, file, force) {
10436
10436
  },
10437
10437
  ext === "tsx"
10438
10438
  );
10439
- await mkdir3(dirname2(path), { recursive: true });
10440
- await writeFile3(path, src);
10441
- return relative(process.cwd(), path) || path;
10439
+ await mkdir3(dirname2(path2), { recursive: true });
10440
+ await writeFile3(path2, src);
10441
+ return relative(process.cwd(), path2) || path2;
10442
10442
  }
10443
10443
  async function agentsBox(id, v) {
10444
10444
  id = need(id, "agent id", "ghosty agents box <agent-id> [--check PATH] [--contains PATH=TEXT] [--recycle | --start]");
@@ -10832,8 +10832,8 @@ ${dim(t(`${files.length} file(s), ${(total / 1e6).toFixed(1)} MB \u2192 ${dir}/
10832
10832
  let name = safeName(f.name);
10833
10833
  if (used.has(join4(sub, name).toLowerCase())) name = `${basename2(name, extname(name))}-${f.id.slice(-6)}${extname(name)}`;
10834
10834
  used.add(join4(sub, name).toLowerCase());
10835
- const path = join4(sub, name);
10836
- const prev = await stat(path).catch(() => null);
10835
+ const path2 = join4(sub, name);
10836
+ const prev = await stat(path2).catch(() => null);
10837
10837
  if (prev && prev.size === f.size) {
10838
10838
  skipped++;
10839
10839
  continue;
@@ -10844,7 +10844,7 @@ ${dim(t(`${files.length} file(s), ${(total / 1e6).toFixed(1)} MB \u2192 ${dir}/
10844
10844
  const res = await fetch(url);
10845
10845
  if (!res.ok) throw new Error(`${res.status}`);
10846
10846
  await mkdir4(sub, { recursive: true });
10847
- await writeFile4(path, Buffer.from(await res.arrayBuffer()));
10847
+ await writeFile4(path2, Buffer.from(await res.arrayBuffer()));
10848
10848
  saved++;
10849
10849
  } catch (e) {
10850
10850
  failed.push({ id: f.id, name: f.name, error: e.message });
@@ -10962,16 +10962,16 @@ function parseTeamsTarget(raw) {
10962
10962
  }
10963
10963
  async function teamsSchedule(sub, agent, arg, v) {
10964
10964
  const target = parseTeamsTarget(v.teams);
10965
- const path = `/api/v2/agents/${enc5(agent)}/teams-schedule`;
10965
+ const path2 = `/api/v2/agents/${enc5(agent)}/teams-schedule`;
10966
10966
  const when = (epoch) => new Date(epoch * 1e3).toLocaleString("es-MX", { timeZone: "America/Mexico_City", dateStyle: "medium", timeStyle: "short" });
10967
10967
  if (sub === "ls" || sub === "list") {
10968
- const r2 = await api(`${path}?workspace=${enc5(target.workspace)}`);
10968
+ const r2 = await api(`${path2}?workspace=${enc5(target.workspace)}`);
10969
10969
  const rows = r2.scheduled.map((x) => ({ id: x.id, due: when(x.dueAt), where: `#${x.channel}${x.parentId ? `/${x.parentId}` : ""}`, by: x.by, text: short(x.text) }));
10970
10970
  return out(r2, () => rows.length ? table(rows, ["id", "due", "where", "by", "text"]) : dim(t("No scheduled turns in Teams.", "No hay turnos programados en Teams.")));
10971
10971
  }
10972
10972
  if (sub === "rm" || sub === "cancel") {
10973
10973
  if (!arg) throw usage(t("Missing schedule id.", "Falta el id del turno."), `ghosty schedule rm <agent-id> <id> --teams ${target.workspace}`);
10974
- const r2 = await api(path, { method: "DELETE", body: { workspace: target.workspace, id: arg } });
10974
+ const r2 = await api(path2, { method: "DELETE", body: { workspace: target.workspace, id: arg } });
10975
10975
  return out(r2, () => `${lavender("\u2713")} ${t("Canceled.", "Cancelado.")}`);
10976
10976
  }
10977
10977
  if (sub !== "add") throw usage(t(`Unknown schedule subcommand: ${sub ?? "(none)"}.`, `Subcomando de schedule desconocido: ${sub ?? "(ninguno)"}.`), "ghosty schedule add|ls|rm <agent-id> \u2026 --teams workspace#room[/thread]");
@@ -10981,7 +10981,7 @@ async function teamsSchedule(sub, agent, arg, v) {
10981
10981
  if (v.at && v.in) throw usage(t("Use --at or --in, not both.", "Usa --at o --in, no ambos."));
10982
10982
  if (v.every) throw usage(t("--every is not supported with --teams yet.", "--every todav\xEDa no va con --teams."));
10983
10983
  const dueAt = v.in ? new Date(Date.now() + minutesOf(v.in, "--in") * 6e4).toISOString() : isoOf(v.at, "--at");
10984
- const r = await api(path, { method: "POST", body: { workspace: target.workspace, channel: target.channel, ...target.parentId ? { parentId: target.parentId } : {}, text: arg, dueAt } });
10984
+ const r = await api(path2, { method: "POST", body: { workspace: target.workspace, channel: target.channel, ...target.parentId ? { parentId: target.parentId } : {}, text: arg, dueAt } });
10985
10985
  out(r, () => `${lavender("\u2713")} ${t(`@${r.handle} will answer in #${r.channel}${r.parentId ? ` (thread ${r.parentId})` : ""} on ${when(r.dueAt)}.`, `@${r.handle} contestar\xE1 en #${r.channel}${r.parentId ? ` (hilo ${r.parentId})` : ""} el ${when(r.dueAt)}.`)} ${dim(`id ${r.id} \xB7 ${t("cancel", "cancelar")}: ghosty schedule rm ${agent} ${r.id} --teams ${target.workspace}`)}`);
10986
10986
  }
10987
10987
  async function scheduleCmd(sub, agent, arg, v) {
@@ -11172,7 +11172,7 @@ ${dim("The same gpk_/gps_ pair signs POST /api/v2/fleet-agents/<id>/message-stre
11172
11172
  ${dim("Docs: https://www.ghosty.studio/docs/cli/partners")}`,
11173
11173
  `${bold("Uso:")} ghosty partner <comando> [opciones]
11174
11174
 
11175
- ${bold("Espacio de partner")} ${dim("(t\xFA, con sesi\xF3n como due\xF1a del espacio)")}
11175
+ ${bold("Espacio de partner")} ${dim("(t\xFA, con sesi\xF3n como due\xF1o del espacio)")}
11176
11176
  partner ls Tus espacios de partner
11177
11177
  partner enable <workspace> Convierte uno de tus espacios en espacio de partner
11178
11178
  partner credentials ls Credenciales de servidor (sin secretos)
@@ -11223,35 +11223,35 @@ function partnerCreds() {
11223
11223
  const secret = process.env.GHOSTY_PARTNER_KEY_SECRET || process.env.GHOSTY_PARTNER_SECRET;
11224
11224
  return key && secret ? { key, secret } : null;
11225
11225
  }
11226
- async function call(path, v, init = {}) {
11226
+ async function call(path2, v, init = {}) {
11227
11227
  const method = init.method ?? (init.body !== void 0 ? "POST" : "GET");
11228
11228
  const raw = init.body !== void 0 ? JSON.stringify(init.body) : "";
11229
11229
  const creds = init.ownerOnly ? null : partnerCreds();
11230
11230
  if (creds) {
11231
11231
  const ts = String(Math.floor(Date.now() / 1e3));
11232
11232
  const sig = createHmac("sha256", creds.secret).update(`${ts}.${creds.key}.${raw}`).digest("hex");
11233
- const res = await fetch(`${await baseUrl()}${path}`, {
11233
+ const res = await fetch(`${await baseUrl()}${path2}`, {
11234
11234
  method,
11235
11235
  headers: {
11236
11236
  "x-ghosty-key": creds.key,
11237
11237
  "x-ghosty-ts": ts,
11238
11238
  "x-ghosty-sig": sig,
11239
11239
  "content-type": "application/json",
11240
- "user-agent": `ghosty-cli/${"0.84.0"}`
11240
+ "user-agent": `ghosty-cli/${"0.86.0"}`
11241
11241
  },
11242
11242
  body: method === "GET" ? void 0 : raw
11243
11243
  });
11244
11244
  if (!res.ok) throw await toError(res);
11245
11245
  return res;
11246
11246
  }
11247
- return request(path, {
11247
+ return request(path2, {
11248
11248
  method,
11249
11249
  ...init.body !== void 0 ? { body: init.body } : {},
11250
11250
  headers: v.workspace ? { "x-ghosty-workspace": v.workspace } : {}
11251
11251
  });
11252
11252
  }
11253
- async function json(path, v, init = {}) {
11254
- const res = await call(path, v, init);
11253
+ async function json(path2, v, init = {}) {
11254
+ const res = await call(path2, v, init);
11255
11255
  const text = await res.text();
11256
11256
  return text ? JSON.parse(text) : void 0;
11257
11257
  }
@@ -11335,8 +11335,8 @@ async function tenantsCmd(sub, id, v) {
11335
11335
  return out(r, () => r.tenants.length ? table(r.tenants.map((tn) => ({ externalId: tn.externalId, config: JSON.stringify(tn.config ?? {}), updated: fmt(tn.updatedAt) })), ["externalId", "config", "updated"]) : dim(t("No tenants yet: each one appears on its first turn, or create it with ghosty partner tenants set <externalId> --config '{\u2026}'", "Todav\xEDa no hay tenants: cada uno aparece en su primer turno, o cr\xE9alo con ghosty partner tenants set <externalId> --config '{\u2026}'")));
11336
11336
  }
11337
11337
  if (!id) throw usage(t("Missing externalId.", "Falta el externalId."), `${t("Example", "Ejemplo")}: ghosty partner tenants ${sub} org_123`);
11338
- const path = `/api/v2/partner/tenants/${enc7(id)}`;
11339
- if (sub === "get") return out(await json(path, v));
11338
+ const path2 = `/api/v2/partner/tenants/${enc7(id)}`;
11339
+ if (sub === "get") return out(await json(path2, v));
11340
11340
  if (sub === "set" || sub === "apply") {
11341
11341
  if (!v.config) throw usage(t("Missing --config.", "Falta --config."), `${t("Example", "Ejemplo")}: --config '{"name":"Spa","timezone":"America/Mexico_City"}'`);
11342
11342
  let config;
@@ -11345,11 +11345,11 @@ async function tenantsCmd(sub, id, v) {
11345
11345
  } catch {
11346
11346
  throw usage(t("--config must be JSON.", "--config debe ser JSON."));
11347
11347
  }
11348
- const r = await json(path, v, { method: "PUT", body: { config } });
11348
+ const r = await json(path2, v, { method: "PUT", body: { config } });
11349
11349
  return out(r, () => `${lavender("\u2713")} ${r.created ? t("Created", "Creado") : r.changed ? t("Updated", "Actualizado") : t("No changes", "Sin cambios")} ${id}`);
11350
11350
  }
11351
11351
  if (sub === "rm" || sub === "delete") {
11352
- const r = await json(path, v, { method: "DELETE" });
11352
+ const r = await json(path2, v, { method: "DELETE" });
11353
11353
  return out(r, () => r.deleted ? `${lavender("\u2713")} ${t(`Deleted ${id} (${r.conversations} conversations)`, `${id} borrado (${r.conversations} conversaciones)`)}` : dim(t(`${id} did not exist.`, `${id} no exist\xEDa.`)));
11354
11354
  }
11355
11355
  throw usage(t(`Unknown tenants subcommand: ${sub}.`, `Subcomando de tenants desconocido: ${sub}.`), t("Try", "Prueba") + ": ghosty partner tenants ls | get | set | rm");
@@ -11571,9 +11571,110 @@ async function imageCmd(sub, a1, a2, v) {
11571
11571
  );
11572
11572
  }
11573
11573
 
11574
+ // src/commands/tts.ts
11575
+ init_api();
11576
+ init_util();
11577
+ init_i18n();
11578
+ import { writeFile as writeFile7 } from "node:fs/promises";
11579
+ var MAX_CHARS = 400;
11580
+ var VOICES = ["em_santa", "em_alex", "ef_dora"];
11581
+ async function ttsCmd(text, v) {
11582
+ const ex = 'ghosty tts "Hola, gracias por escribir." [--voice em_santa|em_alex|ef_dora] [--out voz.wav]';
11583
+ const said = text?.trim();
11584
+ if (!said) throw usage(t("Missing the text to say.", "Falta el texto a decir."), ex);
11585
+ if (said.length > MAX_CHARS) throw usage(t(`Too long: ${said.length} characters (max ${MAX_CHARS}). Split it into sentences.`, `Demasiado largo: ${said.length} caracteres (m\xE1x. ${MAX_CHARS}). P\xE1rtelo en frases.`), ex);
11586
+ const voice = v.voice ?? "em_santa";
11587
+ if (!VOICES.includes(voice)) throw usage(t(`Unknown voice: ${voice}.`, `Voz desconocida: ${voice}.`), `${t("Voices", "Voces")}: ${VOICES.join(", ")}`);
11588
+ const res = await request("/api/v2/me/tts", { body: { text: said, voice } });
11589
+ const bytes = Buffer.from(await res.arrayBuffer());
11590
+ const file = v.out ?? "voz.wav";
11591
+ await writeFile7(file, bytes);
11592
+ const durationMs = Number(res.headers.get("x-duration-ms") ?? 0);
11593
+ return out({ saved: file, voice, bytes: bytes.length, durationMs }, () => `${lavender("\u2713")} ${file} (${(durationMs / 1e3).toFixed(1)} s) ${dim(t(`\xB7 play it: afplay ${file}`, `\xB7 esc\xFAchalo: afplay ${file}`))}`);
11594
+ }
11595
+
11596
+ // src/commands/watch.ts
11597
+ init_api();
11598
+ init_util();
11599
+ init_i18n();
11600
+ var enc8 = encodeURIComponent;
11601
+ var path = (agent, rest = "") => `/api/v2/me/agents/${enc8(agent)}/watches${rest}`;
11602
+ var short2 = (s2, n = 50) => s2.length > n ? s2.slice(0, n - 1) + "\u2026" : s2;
11603
+ async function watchCmd(sub, agent, arg, v) {
11604
+ if (!agent) throw usage(t("Missing agent id.", "Falta el id del agente."), "ghosty watch ls <agent-id>");
11605
+ if (sub === "ls" || sub === "list" || !sub) {
11606
+ const r = await api(path(agent));
11607
+ const rows = r.watches.map((w) => ({
11608
+ id: w.id,
11609
+ page: short2(w.label ?? w.url),
11610
+ every: `${w.everyHours}h`,
11611
+ state: w.state + (w.failures ? ` (${w.failures}\u2717)` : ""),
11612
+ changed: w.lastChangedAt ?? "",
11613
+ checked: w.lastCheckedAt ?? "",
11614
+ conversation: w.sessionId
11615
+ }));
11616
+ return out(r, () => rows.length ? table(rows, ["id", "page", "every", "state", "changed", "checked", "conversation"]) : dim(t("No watched pages.", "No hay p\xE1ginas vigiladas.")));
11617
+ }
11618
+ if (sub === "add") {
11619
+ if (!arg) throw usage(t("Missing the URL.", "Falta la URL."), 'ghosty watch add <agent-id> https://competencia.mx/precios --every 24h --focus "Precios"');
11620
+ const body = {
11621
+ url: arg,
11622
+ ...v.every ? { everyHours: minutesOf(v.every, "--every") / 60 } : {},
11623
+ ...v.focus ? { focus: v.focus } : {},
11624
+ ...v.instruction ? { instruction: v.instruction } : {},
11625
+ ...v.label ? { label: v.label } : {},
11626
+ ...v.days ? { days: Number(v.days) } : {}
11627
+ };
11628
+ let sid = v.conversation;
11629
+ const created = !sid;
11630
+ if (!sid) {
11631
+ sid = (await api(convPath(agent), { method: "POST", body: {} })).id;
11632
+ if (!globals.json) info(dim(t(`new conversation ${sid}`, `conversaci\xF3n nueva ${sid}`)));
11633
+ }
11634
+ let r;
11635
+ try {
11636
+ r = await api(path(agent), { body: { ...body, sessionId: sid } });
11637
+ } catch (e) {
11638
+ if (created) await api(convPath(agent, `/${enc8(sid)}`), { method: "DELETE" }).catch(() => {
11639
+ });
11640
+ throw e;
11641
+ }
11642
+ if (v.title && created) await api(convPath(agent, `/${enc8(sid)}`), { method: "PATCH", body: { title: v.title } });
11643
+ const w = r.watch;
11644
+ return out(
11645
+ r,
11646
+ () => `${lavender("\u2713")} ${t(`Watching ${w.url} every ${w.everyHours} h until ${w.expiresAt}`, `Vigilando ${w.url} cada ${w.everyHours} h hasta ${w.expiresAt}`)}
11647
+ ${dim(`id ${w.id} \xB7 ${t("conversation", "conversaci\xF3n")} ${w.sessionId} \xB7 ${t("try it", "pru\xE9bala")}: ghosty watch check ${agent} ${w.id} --dry-run`)}`
11648
+ );
11649
+ }
11650
+ if (!arg) throw usage(t("Missing the watch id.", "Falta el id de la vigilancia."), t("See them with", "M\xEDralas con") + `: ghosty watch ls ${agent}`);
11651
+ if (sub === "rm" || sub === "remove") {
11652
+ const r = await api(path(agent, `/${enc8(arg)}`), { method: "DELETE" });
11653
+ return out(r, () => `${lavender("\u2713")} ${t("Stopped watching", "Ya no se vigila")} ${arg}`);
11654
+ }
11655
+ if (sub === "check") {
11656
+ const dry = !!v["dry-run"];
11657
+ const r = await api(path(agent, `/${enc8(arg)}`), { body: { action: "check", ...dry ? { dryRun: true } : {} } });
11658
+ if (!dry) return out(r, () => `${lavender("\u2713")} ${t("It will be checked in the next sweep (\u2264 30 s); if it changed, the agent gets a turn.", "Se revisa en el siguiente barrido (\u2264 30 s); si cambi\xF3, el agente recibe un turno.")}`);
11659
+ const label = {
11660
+ baseline: t("no snapshot yet: the first check takes it", "a\xFAn sin foto: la primera revisi\xF3n la toma"),
11661
+ same: t("unchanged", "sin cambios"),
11662
+ changed: t("CHANGED (in dry-run nothing is saved or sent)", "CAMBI\xD3 (en dry-run no se guarda ni se avisa)"),
11663
+ failed: t("could not read the page", "no se pudo leer la p\xE1gina"),
11664
+ skipped: t("skipped (daily web cap)", "saltada (tope diario de web)")
11665
+ };
11666
+ return out(r, () => [`${lavender("\u2022")} ${label[r.status ?? ""] ?? r.status}${r.chars != null ? dim(` \xB7 ${r.chars} ${t("chars", "caracteres")}`) : ""}`, r.error ? dim(r.error) : "", r.diff ?? ""].filter(Boolean).join("\n"));
11667
+ }
11668
+ if (sub === "resume") {
11669
+ const r = await api(path(agent, `/${enc8(arg)}`), { body: { action: "resume", ...v.days ? { days: Number(v.days) } : {} } });
11670
+ return out(r, () => `${lavender("\u2713")} ${t("Resumed", "Reanudada")} ${arg}`);
11671
+ }
11672
+ throw usage(t(`Unknown watch subcommand: ${sub}.`, `Subcomando de watch desconocido: ${sub}.`), "ghosty watch ls|add|rm|check|resume <agent-id> \u2026");
11673
+ }
11674
+
11574
11675
  // src/commands/data.ts
11575
11676
  init_api();
11576
- import { readdir as readdir2, readFile as readFile6, writeFile as writeFile7 } from "node:fs/promises";
11677
+ import { readdir as readdir2, readFile as readFile6, writeFile as writeFile8 } from "node:fs/promises";
11577
11678
  import { basename as basename3, join as join5 } from "node:path";
11578
11679
 
11579
11680
  // src/rows.ts
@@ -11597,8 +11698,8 @@ var describeRowScope = (scope) => Object.entries(scope ?? {}).map(([t2, c]) => `
11597
11698
  // src/commands/data.ts
11598
11699
  init_util();
11599
11700
  init_i18n();
11600
- var enc8 = encodeURIComponent;
11601
- var agentPath2 = (id, rest = "") => `/api/v2/agents/${enc8(id)}${rest}`;
11701
+ var enc9 = encodeURIComponent;
11702
+ var agentPath2 = (id, rest = "") => `/api/v2/agents/${enc9(id)}${rest}`;
11602
11703
  var need2 = (v, what, example) => {
11603
11704
  if (!v) throw usage(t(`Missing ${what}.`, `Falta ${what}.`), `${t("Example", "Ejemplo")}: ${example}`);
11604
11705
  return v;
@@ -11653,15 +11754,15 @@ async function dbsImport(id, db, v) {
11653
11754
  const easybitsKey = process.env.EASYBITS_API_KEY;
11654
11755
  if (!easybitsKey) throw usage(t("EASYBITS_API_KEY is not set.", "EASYBITS_API_KEY no est\xE1 definida."), t("Export your EasyBits API key (https://www.easybits.cloud/dash/developer) and run again.", "Exporta tu API key de EasyBits (https://www.easybits.cloud/dash/developer) y vuelve a correrlo."));
11655
11756
  const body = { from: "easybits", sourceDb: m[1], easybitsKey, mode: v.append ? "append" : "replace", ...v.tables ? { tables: csv(v.tables) } : {}, ...v["rehost-images"] ? { rehost: csv(v["rehost-images"]) } : {} };
11656
- const path = agentPath2(id, `/dbs/${enc8(db)}/import`);
11657
- const dry = await api(path, { body: { ...body, dryRun: true } });
11757
+ const path2 = agentPath2(id, `/dbs/${enc9(db)}/import`);
11758
+ const dry = await api(path2, { body: { ...body, dryRun: true } });
11658
11759
  if (v["dry-run"]) return out({ ...dry, shrinking: shrinking(dry.plan).map((p) => p.table) }, () => `${dry.db} \u2190 easybits:${m[1]}
11659
11760
  ${planText(dry.plan)}${shrinkWarnings(dry.plan)}${importWarnings(dry.warnings)}${gsOnlyWarnings(dry.gsOnly)}
11660
11761
  ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
11661
11762
  const destructive = dry.plan.some((p) => p.action === "replace" && p.targetRows > 0);
11662
11763
  if (destructive) await confirm(`${planText(dry.plan)}${shrinkWarnings(dry.plan)}${importWarnings(dry.warnings)}${gsOnlyWarnings(dry.gsOnly)}
11663
11764
  ${t(`Replace the rows above in ${dry.db}?`, `\xBFReemplazar las filas de arriba en ${dry.db}?`)}`, { yes: v.yes, ...shrinking(dry.plan).length ? { expect: dry.db } : {}, retry: `ghosty dbs import ${id} ${db} --from ${v.from}${v.tables ? ` --tables ${v.tables}` : ""}` });
11664
- const r = await api(path, { body });
11765
+ const r = await api(path2, { body });
11665
11766
  const img = r.rehosted ? `
11666
11767
  ${t(`${r.rehosted.copied} image link(s) now permanent`, `${r.rehosted.copied} liga(s) de imagen ya permanentes`)}${r.rehosted.failed.length ? `, ${red(String(r.rehosted.failed.length))} ${t("kept as they were", "se quedaron como estaban")}:
11667
11768
  ${r.rehosted.failed.map((f) => ` ${f.url} ${dim(f.error)}`).join("\n")}` : ""}` : "";
@@ -11672,8 +11773,8 @@ async function dbsRehost(id, db, v) {
11672
11773
  const ex = "ghosty dbs rehost <agent-id> <db> --table catalogo --column imagen_url [--dry-run]";
11673
11774
  const table2 = need2(v.table, "--table", ex);
11674
11775
  const column = need2(v.column, "--column", ex);
11675
- const path = agentPath2(id, `/dbs/${enc8(db)}/rehost`);
11676
- const dry = await api(path, { body: { table: table2, column, dryRun: true } });
11776
+ const path2 = agentPath2(id, `/dbs/${enc9(db)}/rehost`);
11777
+ const dry = await api(path2, { body: { table: table2, column, dryRun: true } });
11677
11778
  const head = `${dry.db} \xB7 ${table2}.${column}: ${t(`${dry.distinct} distinct link(s), ${dry.alreadyOurs} already permanent, ${dry.toCopy} to copy`, `${dry.distinct} liga(s) distintas, ${dry.alreadyOurs} ya permanentes, ${dry.toCopy} por copiar`)}`;
11678
11779
  if (v["dry-run"] || !dry.toCopy) return out(dry, () => `${head}
11679
11780
  ${dim(v["dry-run"] ? t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada.") : t("Nothing to do.", "No hay nada que hacer."))}`);
@@ -11681,7 +11782,7 @@ ${dim(v["dry-run"] ? t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3
11681
11782
  const total = { copied: 0, reused: 0, failed: [], warnings: [] };
11682
11783
  let after;
11683
11784
  for (; ; ) {
11684
- const r = await api(path, { body: { table: table2, column, ...after ? { after } : {} } });
11785
+ const r = await api(path2, { body: { table: table2, column, ...after ? { after } : {} } });
11685
11786
  total.copied += r.copied ?? 0;
11686
11787
  total.reused += r.reused ?? 0;
11687
11788
  total.failed.push(...r.failed ?? []);
@@ -11702,7 +11803,7 @@ ${total.failed.map((f) => ` ${f.url} ${dim(f.error)}`).join("\n")}` : ""
11702
11803
  );
11703
11804
  }
11704
11805
  async function dbsRename(id, db, name, dryRun) {
11705
- const r = await api(agentPath2(id, `/dbs/${enc8(db)}/rename`), { body: { name, dryRun } });
11806
+ const r = await api(agentPath2(id, `/dbs/${enc9(db)}/rename`), { body: { name, dryRun } });
11706
11807
  const refs = r.references.flatMap((x) => [
11707
11808
  ` ${bold(x.agent)} ${x.where}${x.derived ? dim(t(" (derived: follows its base)", " (derivado: sigue a su base)")) : ""}`,
11708
11809
  ...x.refs.slice(0, 3).map((l) => dim(` ${x.where === "prompt" || x.where.startsWith("skill:") ? `${l.line}: ` : ""}${l.text}`)),
@@ -11719,8 +11820,8 @@ ${dim(t("Dry run: nothing changed. Without --dry-run those mentions are rewritte
11719
11820
  return out(r, () => [`${lavender("\u2713")} ${head}. ${t(`${r.rewritten.length} mention(s) rewritten.`, `${r.rewritten.length} menci\xF3n(es) reescritas.`)}`, ...failed, dim(`${t("Undo", "Deshacer")}: ghosty dbs rename ${id} ${r.to} ${r.from}`)].join("\n"));
11720
11821
  }
11721
11822
  async function dbsRm(id, db, v) {
11722
- const path = agentPath2(id, `/dbs/${enc8(db)}/delete`);
11723
- const dry = await api(path, { body: { dryRun: true } });
11823
+ const path2 = agentPath2(id, `/dbs/${enc9(db)}/delete`);
11824
+ const dry = await api(path2, { body: { dryRun: true } });
11724
11825
  const rows = dry.tables.reduce((n, x) => n + x.rows, 0);
11725
11826
  const plan = [
11726
11827
  `${bold(dry.name)}: ${t(`${dry.tables.length} table(s), ${rows} row(s)`, `${dry.tables.length} tabla(s), ${rows} fila(s)`)}`,
@@ -11730,7 +11831,7 @@ async function dbsRm(id, db, v) {
11730
11831
  ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
11731
11832
  await confirm(`${plan}
11732
11833
  ${t(`Delete ${dry.name} for good? This cannot be undone.`, `\xBFBorrar ${dry.name} para siempre? No se puede deshacer.`)}`, { yes: v.yes, expect: dry.name, retry: `ghosty dbs rm ${id} ${db}` });
11733
- const r = await api(path, { body: {} });
11834
+ const r = await api(path2, { body: {} });
11734
11835
  return out(r, () => `${lavender("\u2713")} ${t(`${r.name} deleted.`, `${r.name} borrada.`)}`);
11735
11836
  }
11736
11837
  async function dbsExport(id, db, v) {
@@ -11738,11 +11839,11 @@ async function dbsExport(id, db, v) {
11738
11839
  if (format !== "sqlite" && format !== "csv") throw usage(t("--format takes sqlite or csv.", "--format acepta sqlite o csv."), `ghosty dbs export ${id} ${db} --format csv --table <table>`);
11739
11840
  if (format === "csv" && !v.table) throw usage(t("A CSV is one table: add --table.", "Un CSV es de una tabla: agrega --table."), `ghosty dbs export ${id} ${db} --table <table> (ghosty dbs tables ${id} ${db})`);
11740
11841
  const q = new URLSearchParams({ format, ...v.table ? { table: v.table } : {} });
11741
- const res = await request(agentPath2(id, `/dbs/${enc8(db)}/export?${q}`));
11842
+ const res = await request(agentPath2(id, `/dbs/${enc9(db)}/export?${q}`));
11742
11843
  const suggested = /filename="([^"]+)"/.exec(res.headers.get("content-disposition") ?? "")?.[1] ?? (format === "csv" ? `${db}-${v.table}.csv` : `${db}.sqlite`);
11743
11844
  const file = v.out ?? suggested;
11744
11845
  const bytes = Buffer.from(await res.arrayBuffer());
11745
- await writeFile7(file, bytes);
11846
+ await writeFile8(file, bytes);
11746
11847
  const countsHeader = res.headers.get("x-gs-row-counts");
11747
11848
  const counts = countsHeader ? JSON.parse(Buffer.from(countsHeader, "base64").toString("utf8")) : {};
11748
11849
  const rows = Object.entries(counts).map(([tb, n]) => ({ table: tb, rows: n }));
@@ -11768,7 +11869,7 @@ ${dim(`${t("Delete one", "Borrar una")}: ghosty dbs rm ${id} <db> --dry-run`)}`
11768
11869
  }
11769
11870
  case "tables": {
11770
11871
  db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs tables <agent-id> <db>");
11771
- const r = await api(agentPath2(id, `/dbs/${enc8(db)}`));
11872
+ const r = await api(agentPath2(id, `/dbs/${enc9(db)}`));
11772
11873
  const ext = new Set(r.externalWriteTables);
11773
11874
  const own = r.externalRowScope ?? {};
11774
11875
  const rows = r.tables.map((tb) => ({ table: tb.name, rows: tb.rows, "customer writes": ext.has(tb.name) ? "yes" : "", "customer sees": own[tb.name] ? `own rows (${own[tb.name].join(", ")})` : "", columns: tb.columns.map((c) => `${c.name}${c.pk ? "*" : ""}`).join(", ") }));
@@ -11787,7 +11888,7 @@ ${table(rows, ["table", "rows", "customer writes", "customer sees", "columns"])}
11787
11888
  case "sql": {
11788
11889
  db = need2(db, t("database (id or name)", "la base (id o nombre)"), 'ghosty dbs query <agent-id> <db> "SELECT \u2026"');
11789
11890
  const sql = need2(rest.join(" ").trim() || void 0, t("SQL", "el SQL"), 'ghosty dbs query <agent-id> <db> "SELECT * FROM t LIMIT 10"');
11790
- const r = await api(agentPath2(id, `/dbs/${enc8(db)}/query`), { body: { sql, args: v.arg ?? [], write: v.write === true } }).catch((e) => {
11891
+ const r = await api(agentPath2(id, `/dbs/${enc9(db)}/query`), { body: { sql, args: v.arg ?? [], write: v.write === true } }).catch((e) => {
11791
11892
  if (e instanceof CliError && !v.write && /lectura|SELECT|escribir/i.test(e.message)) throw new CliError(e.message, EXIT.API, t("To write on purpose, add --write.", "Para escribir a prop\xF3sito, agrega --write."));
11792
11893
  throw e;
11793
11894
  });
@@ -11804,13 +11905,13 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
11804
11905
  if (v.table) q.set("table", v.table);
11805
11906
  if (v.since) q.set("since", v.since);
11806
11907
  if (v.limit) q.set("limit", v.limit);
11807
- const r = await api(agentPath2(id, `/dbs/${enc8(db)}/origin${q.size ? `?${q}` : ""}`));
11908
+ const r = await api(agentPath2(id, `/dbs/${enc9(db)}/origin${q.size ? `?${q}` : ""}`));
11808
11909
  const rows = r.writes.map((w) => ({ when: w.created_at, agent: w.agent_id.slice(0, 10), channel: w.canal ?? "", what: `${w.op} ${w.table_name}${w.row_id != null ? ` #${w.row_id}` : ""}`, customer: w.phone10 ?? "", why: w.reason ?? "" }));
11809
11910
  return out(r, () => rows.length ? table(rows, ["when", "agent", "channel", "what", "customer", "why"]) : dim(t("No agent writes recorded yet in this database.", "Todav\xEDa no hay escrituras de agentes registradas en esta base.")));
11810
11911
  }
11811
11912
  case "checkpoints": {
11812
11913
  db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs checkpoints <agent-id> <db>");
11813
- const r = await api(agentPath2(id, `/dbs/${enc8(db)}/checkpoints`));
11914
+ const r = await api(agentPath2(id, `/dbs/${enc9(db)}/checkpoints`));
11814
11915
  const rows = r.checkpoints.map((c) => ({ id: c.id, when: c.at, rows: Object.values(c.counts).reduce((a, b) => a + b, 0), why: c.reason }));
11815
11916
  return out(r, () => rows.length ? table(rows, ["id", "when", "rows", "why"]) : dim(t("No checkpoints yet: one is taken before the first write of each agent turn.", "Todav\xEDa no hay checkpoints: se toma uno antes de la primera escritura de cada turno de un agente.")));
11816
11917
  }
@@ -11818,7 +11919,7 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
11818
11919
  db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs restore <agent-id> <db> <checkpoint-id> [--yes]");
11819
11920
  const ckpt = need2(rest[0], t("checkpoint id (from dbs checkpoints)", "el id del checkpoint (de dbs checkpoints)"), `ghosty dbs restore ${id} ${db} <checkpoint-id>`);
11820
11921
  await confirm(t(`Return every table of ${db} to checkpoint ${ckpt}? A new checkpoint is taken first, so you can undo it.`, `\xBFRegresar todas las tablas de ${db} al checkpoint ${ckpt}? Antes se toma otro, as\xED que se puede deshacer.`), { yes: v.yes, retry: `ghosty dbs restore ${id} ${db} ${ckpt}` });
11821
- const r = await api(agentPath2(id, `/dbs/${enc8(db)}/checkpoints`), { method: "POST", body: { checkpointId: ckpt } });
11922
+ const r = await api(agentPath2(id, `/dbs/${enc9(db)}/checkpoints`), { method: "POST", body: { checkpointId: ckpt } });
11822
11923
  return out(r, () => `${lavender("\u2713")} ${t(`${r.db} returned to ${r.restoredTo.at}.`, `${r.db} regresada a ${r.restoredTo.at}.`)} ${dim(r.undo ? t(`Undo: ghosty dbs restore ${id} ${db} ${r.undo.id}`, `Para deshacer: ghosty dbs restore ${id} ${db} ${r.undo.id}`) : r.warning ?? "")}`);
11823
11924
  }
11824
11925
  case "grant": {
@@ -11833,7 +11934,7 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
11833
11934
  ...v["external-write"] !== void 0 ? { externalWriteTables: csv(v["external-write"]) } : {},
11834
11935
  ...rowScope !== void 0 ? { externalRowScope: rowScope } : {}
11835
11936
  };
11836
- const r = await api(agentPath2(id, `/dbs/${enc8(db)}`), { method: "PUT", body });
11937
+ const r = await api(agentPath2(id, `/dbs/${enc9(db)}`), { method: "PUT", body });
11837
11938
  const own = describeRowScope(r.externalRowScope);
11838
11939
  return out(r, () => `${lavender("\u2713")} ${r.name} ${t("granted.", "concedida.")} ${dim(t(`From customer channels it writes to: ${r.externalWriteTables.join(", ") || "nothing (read only)"}.${own ? ` Each customer sees only their own rows of: ${own}.` : ""}`, `Desde canales de clientes escribe en: ${r.externalWriteTables.join(", ") || "nada (s\xF3lo lectura)"}.${own ? ` Cada cliente ve s\xF3lo sus propias filas de: ${own}.` : ""}`))}`);
11839
11940
  }
@@ -11862,7 +11963,7 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
11862
11963
  }
11863
11964
  case "revoke": {
11864
11965
  db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs revoke <agent-id> <db>");
11865
- const r = await api(agentPath2(id, `/dbs/${enc8(db)}`), { method: "DELETE" });
11966
+ const r = await api(agentPath2(id, `/dbs/${enc9(db)}`), { method: "DELETE" });
11866
11967
  return out(r, () => `${lavender("\u2713")} ${r.name} ${t("revoked.", "revocada.")} ${dim(`${t("Undo", "Deshacer")}: ghosty dbs grant ${id} ${r.name}`)}`);
11867
11968
  }
11868
11969
  default:
@@ -11870,9 +11971,9 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
11870
11971
  }
11871
11972
  }
11872
11973
  async function docsDedupe(v) {
11873
- const path = "/api/v2/me/documents/dedupe";
11974
+ const path2 = "/api/v2/me/documents/dedupe";
11874
11975
  const pair = v.keep || v.drop ? { keep: v.keep, drop: v.drop } : {};
11875
- const plan = await api(path, { method: "POST", body: { ...pair, dryRun: true } });
11976
+ const plan = await api(path2, { method: "POST", body: { ...pair, dryRun: true } });
11876
11977
  const name = (d) => typeof d === "string" ? d : `\xAB${d.name}\xBB ${dim(`${d.id.slice(0, 10)} \xB7 ${d.agents} ${t("agent(s)", "agente(s)")}`)}`;
11877
11978
  const text = [
11878
11979
  ...plan.merges.map((m) => `${t("keep", "queda")} ${name(m.keep)}
@@ -11886,7 +11987,7 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
11886
11987
  requireYesIfHeadless(v.yes, retry);
11887
11988
  await confirm(`${text}
11888
11989
  ${t("Merge? The extra copies are deleted and their agents keep access through the one that stays.", "\xBFUnir? Las copias sobrantes se borran y sus agentes siguen leyendo por la que queda.")}`, { yes: v.yes, retry });
11889
- const r = await api(path, { method: "POST", body: pair });
11990
+ const r = await api(path2, { method: "POST", body: pair });
11890
11991
  out(r, () => `${lavender("\u2713")} ${t(`${r.merged} duplicate(s) merged \xB7 ${r.grantsMoved} permission(s) moved.`, `${r.merged} duplicado(s) unido(s) \xB7 ${r.grantsMoved} permiso(s) movido(s).`)}`);
11891
11992
  }
11892
11993
  async function agentDocsCmd(sub, id, docs, v) {
@@ -11916,7 +12017,7 @@ async function agentDocsCmd(sub, id, docs, v) {
11916
12017
  wanted = docs.map((ref) => ({ ref, body }));
11917
12018
  }
11918
12019
  const done = [];
11919
- for (const w of wanted) done.push(await api(agentPath2(id, `/documents/${enc8(w.ref)}`), { method: "PUT", body: w.body }));
12020
+ for (const w of wanted) done.push(await api(agentPath2(id, `/documents/${enc9(w.ref)}`), { method: "PUT", body: w.body }));
11920
12021
  return out(done.length === 1 ? done[0] : { granted: done }, () => done.map((r) => `${lavender("\u2713")} ${r.name}: ${r.mode === "write" ? t("reads and replaces it", "lo lee y lo reemplaza") : t("reads it", "lo lee")}${r.deliverable ? t(", and can send it", ", y puede mandarlo") : t(", never sends it", ", nunca lo manda")}.`).join("\n"));
11921
12022
  }
11922
12023
  case "add": {
@@ -11934,7 +12035,7 @@ async function agentDocsCmd(sub, id, docs, v) {
11934
12035
  }
11935
12036
  case "revoke": {
11936
12037
  doc = need2(doc, t("document (id or exact name)", "el documento (id o nombre exacto)"), 'ghosty agents docs revoke <agent-id> "Cat\xE1logo.pdf"');
11937
- const r = await api(agentPath2(id, `/documents/${enc8(doc)}`), { method: "DELETE" });
12038
+ const r = await api(agentPath2(id, `/documents/${enc9(doc)}`), { method: "DELETE" });
11938
12039
  return out(r, () => `${lavender("\u2713")} ${r.name} ${t("revoked.", "revocado.")} ${dim(`${t("Undo", "Deshacer")}: ghosty agents docs grant ${id} "${r.name}"`)}`);
11939
12040
  }
11940
12041
  case "replace": {
@@ -11945,13 +12046,13 @@ async function agentDocsCmd(sub, id, docs, v) {
11945
12046
  ...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64"), ...mimeField(v.file) },
11946
12047
  ...v.note ? { note: v.note } : {}
11947
12048
  };
11948
- const r = await api(agentPath2(id, `/documents/${enc8(doc)}/versions`), { method: "POST", body });
12049
+ const r = await api(agentPath2(id, `/documents/${enc9(doc)}/versions`), { method: "POST", body });
11949
12050
  return out(r, () => `${lavender("\u2713")} ${r.name}: ${t("version", "versi\xF3n")} ${r.version?.n ?? "?"} ${dim(`(${r.size} bytes)`)}. ${dim(`${t("Undo", "Deshacer")}: ghosty agents docs restore ${id} "${r.name}" ${(r.version?.n ?? 2) - 1}`)}`);
11950
12051
  }
11951
12052
  case "history":
11952
12053
  case "versions": {
11953
12054
  doc = need2(doc, t("document (id or exact name)", "el documento (id o nombre exacto)"), 'ghosty agents docs history <agent-id> "Cat\xE1logo.pdf"');
11954
- const r = await api(agentPath2(id, `/documents/${enc8(doc)}/versions`));
12055
+ const r = await api(agentPath2(id, `/documents/${enc9(doc)}/versions`));
11955
12056
  const rows = r.versions.map((x) => ({ v: `${x.n}${x.current ? "*" : ""}`, when: x.createdAt.slice(0, 16).replace("T", " "), by: x.agent ?? x.via ?? x.source, size: String(x.size), note: x.note ?? "" }));
11956
12057
  const used = r.usedBy.map((u) => `${u.agent} (${u.mode === "write" ? t("updates it", "lo actualiza") + (u.writableFrom.length ? `: ${u.writableFrom.join(", ")}` : "") : t("reads it", "lo lee")})`).join(" \xB7 ");
11957
12058
  return out(r, () => `${r.document.name}
@@ -11963,7 +12064,7 @@ ${dim(t("Used by", "Lo usan"))}: ${used}` : ""}`);
11963
12064
  doc = need2(doc, t("document (id or exact name)", "el documento (id o nombre exacto)"), ex);
11964
12065
  const n = Number(need2(docs[1], t("version number", "el n\xFAmero de versi\xF3n"), ex));
11965
12066
  if (!Number.isInteger(n) || n < 1) throw usage(t("The version is a whole number (see docs history).", "La versi\xF3n es un n\xFAmero entero (mira docs history)."), ex);
11966
- const r = await api(agentPath2(id, `/documents/${enc8(doc)}/versions/${n}/restore`), { method: "POST", body: v.note ? { note: v.note } : {} });
12067
+ const r = await api(agentPath2(id, `/documents/${enc9(doc)}/versions/${n}/restore`), { method: "POST", body: v.note ? { note: v.note } : {} });
11967
12068
  return out(r, () => `${lavender("\u2713")} ${r.name}: ${t(`restored from v${n} as version`, `restaurado de la v${n} como versi\xF3n`)} ${r.version?.n ?? "?"}.`);
11968
12069
  }
11969
12070
  default:
@@ -12001,7 +12102,7 @@ ${dim(tally)}` : dim(t(`Every customer message${v.since ? ` since ${v.since}` :
12001
12102
  }
12002
12103
  async function turnsJobs(id) {
12003
12104
  id = need2(id, t("agent id", "el id del agente"), "ghosty turns jobs <agent-id>");
12004
- const r = await api(`/api/v2/me/agents/${enc8(id)}/tasks`);
12105
+ const r = await api(`/api/v2/me/agents/${enc9(id)}/tasks`);
12005
12106
  const rows = jobRows(r.tasks);
12006
12107
  out(r, () => rows.length ? `${table(rows, ["id", "kind", "what", "state", "minutes", "delivered", "conversation", "note"])}
12007
12108
  ${dim(t(`${r.running} running \xB7 stop them: ghosty turns cancel ${id} <conversation>`, `${r.running} corriendo \xB7 detenerlos: ghosty turns cancel ${id} <conversaci\xF3n>`))}` : dim(t("Nothing in the background (last 24 h).", "Nada en segundo plano (\xFAltimas 24 h).")));
@@ -12023,7 +12124,7 @@ async function turnsCancel(id, sid) {
12023
12124
  const ex = "ghosty turns cancel <agent-id> <conversation-id>";
12024
12125
  id = need2(id, t("agent id", "el id del agente"), ex);
12025
12126
  sid = need2(sid, t("conversation id", "el id de la conversaci\xF3n"), ex);
12026
- const r = await api(`/api/v2/me/agents/${enc8(id)}/conversations/${enc8(sid)}/cancel`, { method: "POST", body: {} });
12127
+ const r = await api(`/api/v2/me/agents/${enc9(id)}/conversations/${enc9(sid)}/cancel`, { method: "POST", body: {} });
12027
12128
  const b = r.background ?? { videos: 0, subagents: 0, wakes: 0 };
12028
12129
  const bg = b.videos + b.subagents + b.wakes;
12029
12130
  out(
@@ -12082,7 +12183,7 @@ async function turnsGrep(id, text, v) {
12082
12183
  out(r, () => rows.length ? `${table(rows, ["at", "who", "turn", "state", "session", "text"])}
12083
12184
  ${dim(`${t("open one", "abre uno")}: ghosty turns show ${id} <turn>`)}` : dim(t(`Nothing says \xAB${text}\xBB${v.since ? ` since ${v.since}` : ""}.`, `Nada dice \xAB${text}\xBB${v.since ? ` desde ${v.since}` : ""}.`)));
12084
12185
  }
12085
- function mimeField(path) {
12186
+ function mimeField(path2) {
12086
12187
  const types = {
12087
12188
  pdf: "application/pdf",
12088
12189
  png: "image/png",
@@ -12106,12 +12207,12 @@ function mimeField(path) {
12106
12207
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
12107
12208
  pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
12108
12209
  };
12109
- const mime = types[path.split(".").pop()?.toLowerCase() ?? ""];
12210
+ const mime = types[path2.split(".").pop()?.toLowerCase() ?? ""];
12110
12211
  return mime ? { mime } : {};
12111
12212
  }
12112
12213
  async function turnShow(id, turnId, full = false) {
12113
12214
  id = need2(id, t("agent id", "el id del agente"), "ghosty turns show <agent-id> [turn-id|last] [--full]");
12114
- const r = await api(agentPath2(id, `/turns/${enc8(turnId ?? "last")}`));
12215
+ const r = await api(agentPath2(id, `/turns/${enc9(turnId ?? "last")}`));
12115
12216
  out(r, () => {
12116
12217
  const tu = r.turn;
12117
12218
  const clip = (s2) => full || s2.length <= 600 ? s2 : `${s2.slice(0, 600)}\u2026 ${dim(t(`(+${s2.length - 600} chars, --full)`, `(+${s2.length - 600} caracteres, --full)`))}`;
@@ -12178,7 +12279,7 @@ async function dbsPhotosCmd(verb, id, db, v) {
12178
12279
  const dir = need2(v.dir, "--dir", ex);
12179
12280
  let column = v.column;
12180
12281
  if (!column) {
12181
- const r = await api(agentPath2(id, `/dbs/${enc8(db)}`));
12282
+ const r = await api(agentPath2(id, `/dbs/${enc9(db)}`));
12182
12283
  const tb = r.tables.find((x) => x.name === tableName);
12183
12284
  if (!tb) throw usage(t(`${db} has no table ${tableName}.`, `${db} no tiene la tabla ${tableName}.`));
12184
12285
  column = tb.columns.find((c) => c.name === "imagen_url")?.name;
@@ -12186,12 +12287,12 @@ async function dbsPhotosCmd(verb, id, db, v) {
12186
12287
  }
12187
12288
  const files = photoFiles(await readdir2(dir));
12188
12289
  if (!files.length) throw usage(t(`No .jpg/.png/.webp files in ${dir}.`, `No hay archivos .jpg/.png/.webp en ${dir}.`), t("Name each photo after its key: SKU-123.jpg", "Nombra cada foto con su clave: SKU-123.jpg"));
12189
- const path = agentPath2(id, `/dbs/${enc8(db)}/photos`);
12290
+ const path2 = agentPath2(id, `/dbs/${enc9(db)}/photos`);
12190
12291
  const results = [];
12191
12292
  for (let i = 0; i < files.length; i += 8) {
12192
12293
  const batch = files.slice(i, i + 8);
12193
12294
  const items = await Promise.all(batch.map(async (f) => ({ key: f.key, filename: f.file, contentBase64: (await readFile6(join5(dir, f.file))).toString("base64") })));
12194
- const r = await api(path, { body: { table: tableName, keyColumn, column, items, ...v["dry-run"] ? { dryRun: true } : {} } });
12295
+ const r = await api(path2, { body: { table: tableName, keyColumn, column, items, ...v["dry-run"] ? { dryRun: true } : {} } });
12195
12296
  results.push(...r.items);
12196
12297
  if (!globals.json && files.length > 8) info(dim(` ${Math.min(i + 8, files.length)}/${files.length}`));
12197
12298
  }
@@ -12463,7 +12564,7 @@ async function readMultiFileAuth(dir) {
12463
12564
  init_api();
12464
12565
  init_util();
12465
12566
  init_i18n();
12466
- var enc10 = encodeURIComponent;
12567
+ var enc11 = encodeURIComponent;
12467
12568
  var digits = (s2) => s2.replace(/\D/g, "");
12468
12569
  function find2(nums, ref) {
12469
12570
  const d = digits(ref);
@@ -12489,9 +12590,9 @@ async function channelsCmd(kind, sub, id, ref, v) {
12489
12590
  if (sub === "integrations") return integrationsCmd(id, v.email);
12490
12591
  if (sub === "import" || sub === "status" || sub === "pause" || sub === "pair") return waCmd2(sub, id, v);
12491
12592
  if (sub === "enable" && !v["dry-run"]) requireYesIfHeadless(v.yes, `ghosty channels whatsapp enable ${id} ${ref ?? "<number>"}`);
12492
- const path = `/api/v2/agents/${enc10(id)}/whatsapp`;
12593
+ const path2 = `/api/v2/agents/${enc11(id)}/whatsapp`;
12493
12594
  const listing = !sub || sub === "ls" || sub === "list";
12494
- const cur = await api(listing ? `${path}?formmy=1` : path);
12595
+ const cur = await api(listing ? `${path2}?formmy=1` : path2);
12495
12596
  if (listing) {
12496
12597
  const fwd = (n2) => !n2.formmy ? dim("\u2014") : "error" in n2.formmy ? dim("?") : n2.formmy.forwards ? t("yes", "s\xED") : lavender("NO");
12497
12598
  const rows = cur.numbers.map((n2) => ({
@@ -12515,7 +12616,7 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
12515
12616
  if (on) await confirm(`${bold(plan)}
12516
12617
  ${t("Real customers will get answers from this agent.", "Clientes reales recibir\xE1n respuestas de este agente.")}`, { yes: v.yes, expect: digits(n.label).slice(-4) || n.id, retry: `ghosty channels whatsapp enable ${id} ${ref}` });
12517
12618
  const wanted = cur.numbers.filter((x) => x.id === n.id ? on : x.mine).map((x) => x.id);
12518
- const r = await api(path, { method: "PUT", body: { numbers: wanted } });
12619
+ const r = await api(path2, { method: "PUT", body: { numbers: wanted } });
12519
12620
  return out(r, () => `${lavender("\u2713")} ${plan.slice(2)}`);
12520
12621
  }
12521
12622
  function findGroup(groups, ref) {
@@ -12533,18 +12634,18 @@ async function groupsCmd(args, v) {
12533
12634
  if (rawSub === "disable") v = { ...v, off: true };
12534
12635
  const ex = 'ghosty channels wa groups ls <agent-id> | set <agent-id> <group> [--role equipo|cliente] [--wake mention|all] [--on|--off] [--name "New name"] | create <agent-id> "Name" | inject <agent-id> <group> --text "\u2026" [--sender NAME]';
12535
12636
  if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), `${t("Example", "Ejemplo")}: ${ex}`);
12536
- const path = `/api/v2/agents/${enc10(id)}/wa/groups`;
12637
+ const path2 = `/api/v2/agents/${enc11(id)}/wa/groups`;
12537
12638
  if (v.role !== void 0 && v.role !== "equipo" && v.role !== "cliente") throw usage(t("--role takes equipo or cliente.", "--role acepta equipo o cliente."));
12538
12639
  if (v.wake !== void 0 && v.wake !== "mention" && v.wake !== "all") throw usage(t("--wake takes mention or all.", "--wake acepta mention o all."));
12539
12640
  if (!sub || sub === "ls" || sub === "list") {
12540
- const r2 = await api(`${path}${v.cached ? "?live=0" : ""}`);
12641
+ const r2 = await api(`${path2}${v.cached ? "?live=0" : ""}`);
12541
12642
  const rows = r2.groups.map((g2) => ({ name: g2.name, on: g2.enabled ? lavender("on") : dim("off"), role: g2.role, wake: g2.wake ?? "all", jid: g2.jid }));
12542
12643
  return out(r2, () => rows.length ? table(rows, ["name", "on", "role", "wake", "jid"]) : dim(t('No groups. Pair its personal WhatsApp in the web, or: ghosty channels wa groups create <agent-id> "Name"', 'Sin grupos. Parea su WhatsApp personal en la web, o: ghosty channels wa groups create <agent-id> "Nombre"')));
12543
12644
  }
12544
12645
  if (sub === "create") {
12545
12646
  const name2 = rest.join(" ").trim();
12546
12647
  if (!name2) throw usage(t("Missing group name.", "Falta el nombre del grupo."), t("Example", "Ejemplo") + `: ghosty channels wa groups create ${id} "Equipo ventas" --role equipo`);
12547
- const r2 = await api(path, { method: "POST", body: { create: name2, role: v.role ?? "equipo" } });
12648
+ const r2 = await api(path2, { method: "POST", body: { create: name2, role: v.role ?? "equipo" } });
12548
12649
  return out(r2, () => `${lavender("\u2713")} ${t("Created", "Creado")} ${bold(name2)} (${v.role ?? "equipo"}, on)
12549
12650
  ${t("invite", "invitaci\xF3n")}: ${r2.inviteUrl}`);
12550
12651
  }
@@ -12552,15 +12653,15 @@ async function groupsCmd(args, v) {
12552
12653
  const ref2 = rest.join(" ").trim();
12553
12654
  const text = v.text?.trim();
12554
12655
  if (!ref2 || !text) throw usage(t("Missing group or --text.", "Falta el grupo o --text."), t("Example", "Ejemplo") + `: ghosty channels wa groups inject ${id} "Equipo" --text "lo que dec\xEDa el audio" --sender Brenda`);
12555
- const g2 = findGroup((await api(`${path}?live=0`)).groups, ref2);
12556
- const r2 = await api(`/api/v2/agents/${enc10(id)}/wa/inject`, { method: "POST", body: { jid: g2.jid, text, ...v.sender ? { senderName: v.sender } : {} } });
12656
+ const g2 = findGroup((await api(`${path2}?live=0`)).groups, ref2);
12657
+ const r2 = await api(`/api/v2/agents/${enc11(id)}/wa/inject`, { method: "POST", body: { jid: g2.jid, text, ...v.sender ? { senderName: v.sender } : {} } });
12557
12658
  return out(r2, () => `${lavender("\u2713")} ${t(`Injected into ${g2.name}: the agent answers there.`, `Reinyectado en ${g2.name}: el agente contesta ah\xED.`)}`);
12558
12659
  }
12559
12660
  if (sub !== "set") throw usage(t(`Unknown groups subcommand: ${sub}.`, `Subcomando de groups desconocido: ${sub}.`), ex);
12560
12661
  const ref = rest.join(" ").trim();
12561
12662
  if (!ref) throw usage(t("Missing group (name or jid).", "Falta el grupo (nombre o jid)."), t("Example", "Ejemplo") + `: ghosty channels wa groups set ${id} "Pia-cotizadora" --role equipo --on`);
12562
12663
  if (v.on && v.off) throw usage(t("Use --on or --off, not both.", "Usa --on o --off, no los dos."));
12563
- const cur = await api(`${path}?live=0`);
12664
+ const cur = await api(`${path2}?live=0`);
12564
12665
  const g = findGroup(cur.groups, ref);
12565
12666
  const enabled = v.on ? true : v.off ? false : g.enabled;
12566
12667
  const role = v.role ?? g.role;
@@ -12573,11 +12674,11 @@ async function groupsCmd(args, v) {
12573
12674
  ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
12574
12675
  if (enabled && !g.enabled) await confirm(`${bold(plan)}
12575
12676
  ${t(`The agent will start answering in this group as ${role}.`, `El agente empezar\xE1 a contestar en este grupo como ${role}.`)}`, { yes: v.yes, retry: `ghosty channels wa groups set ${id} "${ref}"${v.role ? ` --role ${v.role}` : ""} --on` });
12576
- const r = await api(path, { method: "POST", body: { jid: g.jid, enabled, role, ...wake ? { wake } : {}, ...name ? { name } : {} } });
12677
+ const r = await api(path2, { method: "POST", body: { jid: g.jid, enabled, role, ...wake ? { wake } : {}, ...name ? { name } : {} } });
12577
12678
  const now = r.groups.find((x) => x.jid === g.jid);
12578
12679
  return out({ group: now ?? null, plan: [plan] }, () => `${lavender("\u2713")} ${now?.name ?? g.name}: ${now?.enabled ? "on" : "off"} ${t("as", "como")} ${now?.role ?? role} \xB7 wake ${now?.wake ?? wake ?? "all"}`);
12579
12680
  }
12580
- var EB_EXPORT = (agentId) => `${(process.env.EASYBITS_URL ?? "https://www.easybits.cloud").replace(/\/+$/, "")}/api/v2/fleet-agents/${enc10(agentId)}/baileys/export`;
12681
+ var EB_EXPORT = (agentId) => `${(process.env.EASYBITS_URL ?? "https://www.easybits.cloud").replace(/\/+$/, "")}/api/v2/fleet-agents/${enc11(agentId)}/baileys/export`;
12581
12682
  async function easybitsSession(agentId, keys) {
12582
12683
  const key = process.env.EASYBITS_API_KEY;
12583
12684
  if (!key) throw usage(t("EASYBITS_API_KEY is not set.", "EASYBITS_API_KEY no est\xE1 definida."), t("Export the EasyBits API key of the agent's owner and run again.", "Exporta la API key de EasyBits del due\xF1o del agente y vuelve a correrlo."));
@@ -12604,7 +12705,7 @@ function originText(o) {
12604
12705
  }
12605
12706
  async function waCmd2(sub, id, v) {
12606
12707
  if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), t("Example", "Ejemplo") + `: ghosty channels wa ${sub} <agent-id>`);
12607
- const base = `/api/v2/agents/${enc10(id)}/wa`;
12708
+ const base = `/api/v2/agents/${enc11(id)}/wa`;
12608
12709
  if (sub === "status") {
12609
12710
  const s2 = await api(base);
12610
12711
  return out(s2, () => `${s2.status === "connected" ? lavender(t("\u25CF connected", "\u25CF conectado")) : dim(`\u25CB ${s2.status}`)} ${s2.phone ? `+${s2.phone}` : dim(t("(no number)", "(sin n\xFAmero)"))}${s2.live ? "" : dim(t(" \xB7 no socket in gs", " \xB7 sin socket en gs"))}${s2.hasCreds ? "" : dim(t(" \xB7 no saved session", " \xB7 sin sesi\xF3n guardada"))}${s2.reason ? `
@@ -12668,19 +12769,19 @@ async function linkCmd(sub, id, v) {
12668
12769
  const board = v.board === void 0 ? void 0 : /^(agent|agente)$/i.test(v.board) ? null : v.board;
12669
12770
  const boardFlag = v.board !== void 0 ? ` --board ${JSON.stringify(v.board)}` : "";
12670
12771
  if (sub === "link" && (adsOnly !== void 0 || board !== void 0) && v.answer === void 0) {
12671
- const path2 = `/api/v2/agents/${enc10(id)}/whatsapp/link`;
12672
- const r2 = await api(path2, { method: "POST", body: { integrationId: v.integration, ...adsOnly !== void 0 ? { adsOnly } : {}, ...board !== void 0 ? { board } : {}, ...v["dry-run"] ? { dryRun: true } : {} } });
12772
+ const path3 = `/api/v2/agents/${enc11(id)}/whatsapp/link`;
12773
+ const r2 = await api(path3, { method: "POST", body: { integrationId: v.integration, ...adsOnly !== void 0 ? { adsOnly } : {}, ...board !== void 0 ? { board } : {}, ...v["dry-run"] ? { dryRun: true } : {} } });
12673
12774
  return out(r2, () => `${r2.plan.map((x) => serverText(x)).join("\n")}${v["dry-run"] ? `
12674
12775
  ${dim(applyHint(`ghosty channels whatsapp link ${id} --integration ${v.integration}${v["ads-only"] ? ` --ads-only ${v["ads-only"]}` : ""}${boardFlag}`))}` : ""}`);
12675
12776
  }
12676
12777
  if (sub === "link" && v.answer !== "on" && v.answer !== "off") throw usage(t("Missing --answer on|off.", "Falta --answer on|off."), t("on = this agent answers real customers \xB7 off = it only records (add --forward so gs still sees the conversations)", "on = este agente contesta a clientes reales \xB7 off = s\xF3lo registra (agrega --forward para que gs siga viendo las conversaciones)"));
12677
- const path = `/api/v2/agents/${enc10(id)}/whatsapp/link`;
12778
+ const path2 = `/api/v2/agents/${enc11(id)}/whatsapp/link`;
12678
12779
  if (v["partner-agent"] && !v["partner-tenant"]) throw usage(t("--partner-agent needs --partner-tenant <externalId>.", "--partner-agent necesita --partner-tenant <externalId>."));
12679
12780
  const partner = v["partner-tenant"] ? { partnerExternalId: v["partner-tenant"], ...v["partner-agent"] ? { partnerAgentSlug: v["partner-agent"] } : {} } : {};
12680
12781
  const body = { integrationId: v.integration, ...sub === "unlink" ? { unlink: true } : { answer: v.answer === "on", ...v.forward ? { forward: true } : {}, ...adsOnly !== void 0 ? { adsOnly } : {}, ...board !== void 0 ? { board } : {}, ...partner } };
12681
12782
  const partnerFlags = v["partner-tenant"] ? ` --partner-tenant ${v["partner-tenant"]}${v["partner-agent"] ? ` --partner-agent ${v["partner-agent"]}` : ""}` : "";
12682
12783
  const cmd = `ghosty channels whatsapp ${sub} ${id} --integration ${v.integration}${sub === "link" ? ` --answer ${v.answer}${v.forward ? " --forward" : ""}${v["ads-only"] ? ` --ads-only ${v["ads-only"]}` : ""}${boardFlag}${partnerFlags}` : ""}`;
12683
- const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
12784
+ const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
12684
12785
  const text = `${plan.plan.map((x) => serverText(x)).join("\n")}
12685
12786
  ${dim(`Formmy: ${plan.formmy.before.url ?? "null"} \u2192 ${plan.formmy.after.url ?? "null"} (${t("as", "como")} ${plan.formmy.by})`)}`;
12686
12787
  if (v["dry-run"]) return out(plan, () => `${text}
@@ -12689,7 +12790,7 @@ ${dim(applyHint(cmd))}`);
12689
12790
  const digits4 = (plan.number.phoneNumberId ?? v.integration).slice(-4);
12690
12791
  await confirm(`${bold(text)}
12691
12792
  ${sub === "link" ? t("From now on this number's messages reach this agent in gs.", "Desde ahora los mensajes de este n\xFAmero le llegan a este agente en gs.") : t("This number goes back to where it was before.", "Este n\xFAmero regresa a donde estaba antes.")}`, { yes: v.yes, expect: digits4, retry: cmd });
12692
- const r = await api(path, { method: "POST", body });
12793
+ const r = await api(path2, { method: "POST", body });
12693
12794
  return out(r, () => `${lavender("\u2713")} ${sub === "link" ? t(`Linked (link ${r.linkId}). Check: ghosty channels whatsapp ls ${id} \xB7 ghosty turns ls ${id} --since 10m`, `Enlazado (enlace ${r.linkId}). Revisa: ghosty channels whatsapp ls ${id} \xB7 ghosty turns ls ${id} --since 10m`) : t("Unlinked: Formmy points where it did before.", "Desenlazado: Formmy apunta a donde apuntaba antes.")}`);
12694
12795
  }
12695
12796
  function bareMediaFlag(argv) {
@@ -12721,7 +12822,7 @@ async function pageWithRetry(fetchPage, where, waitMs = (n) => 5e3 * n, tries =
12721
12822
  }
12722
12823
  var lastCell = (l) => l ? `${l.agentName ?? l.agentId} ${dim(l.at.slice(0, 16).replace("T", " "))}` : dim("\u2014");
12723
12824
  async function linksCmd(id, kind, orphans) {
12724
- const r = await api(`/api/v2/agents/${enc10(id)}/channels/links?kind=${kind}${orphans ? "&orphans=1" : ""}`);
12825
+ const r = await api(`/api/v2/agents/${enc11(id)}/channels/links?kind=${kind}${orphans ? "&orphans=1" : ""}`);
12725
12826
  const now = (l) => l.answersNow ? l.answersNow.agentName ?? l.answersNow.agentId : l.recordsOnly ? t("nobody (records only)", "nadie (s\xF3lo registra)") : l.via === "easybits" ? "EasyBits" : l.via === "partner" ? "partner" : lavender(t("nobody", "nadie"));
12726
12827
  const rows = r.links.map((l) => ({ [kind === "waba" ? "number" : "page"]: l.label, "answers now": now(l), "last answered by": lastCell(l.lastAnswer), turns: l.lastAnswer?.turns ?? 0, id: l.id }));
12727
12828
  const first = r.links.find((l) => l.nobody && l.lastAnswer);
@@ -12733,7 +12834,7 @@ ${dim(`${t("Give it back", "Regresarlo")}: ${kind === "waba" ? `ghosty channels
12733
12834
  );
12734
12835
  }
12735
12836
  async function integrationsCmd(id, email) {
12736
- const r = await api(`/api/v2/agents/${enc10(id)}/whatsapp/integrations${email ? `?email=${enc10(email)}` : ""}`);
12837
+ const r = await api(`/api/v2/agents/${enc11(id)}/whatsapp/integrations${email ? `?email=${enc11(email)}` : ""}`);
12737
12838
  const who2 = { gs: "gs", easybits: "EasyBits", droplet: "droplet", formmy: "Formmy", other: t("other", "otro") };
12738
12839
  const rows = r.integrations.map((i) => ({
12739
12840
  number: i.phoneNumber ? `+${i.phoneNumber}` : dim("?"),
@@ -12754,11 +12855,11 @@ async function historyCmd(id, v) {
12754
12855
  if (!v.integration) throw usage(t("Missing --integration.", "Falta --integration."), t("Example", "Ejemplo") + `: ghosty channels whatsapp history ${id} --integration <formmy-integration-id> --days 7 --dry-run`);
12755
12856
  const days = Number(v.days ?? 7);
12756
12857
  if (!Number.isInteger(days) || days < 1 || days > 30) throw usage(t("--days takes 1 to 30.", "--days acepta de 1 a 30."));
12757
- const path = `/api/v2/agents/${enc10(id)}/whatsapp/history`;
12858
+ const path2 = `/api/v2/agents/${enc11(id)}/whatsapp/history`;
12758
12859
  const media = v.media !== void 0;
12759
12860
  const filters = { ...v["only-ads"] ? { onlyAds: true } : {}, ...v.phones ? { phones: v.phones.split(",").map((x) => x.trim()).filter(Boolean) } : {}, ...v.column ? { column: v.column } : {} };
12760
12861
  const cmd = `ghosty channels whatsapp history ${id} --integration ${v.integration} --days ${days}${v["only-ads"] ? " --only-ads" : ""}${v.phones ? ` --phones ${v.phones}` : ""}${v.column ? ` --column "${v.column}"` : ""}`;
12761
- const c = await api(path, { method: "POST", body: { integrationId: v.integration, days, dryRun: true, media, ...v.list ? { list: true } : {}, ...filters } });
12862
+ const c = await api(path2, { method: "POST", body: { integrationId: v.integration, days, dryRun: true, media, ...v.list ? { list: true } : {}, ...filters } });
12762
12863
  const files = (p) => (p.mediaCopied ?? 0) + (p.mediaFailed ?? 0) ? ` \xB7 ${t(`${p.mediaCopied ?? 0} file(s) copied`, `${p.mediaCopied ?? 0} archivo(s) copiado(s)`)}${p.mediaFailed ? lavender(t(`, ${p.mediaFailed} failed (retry: --media)`, `, ${p.mediaFailed} fallaron (reintenta: --media)`)) : ""}` : "";
12763
12864
  if (media) {
12764
12865
  const missing = c.mediaMissing ?? 0;
@@ -12773,7 +12874,7 @@ ${dim(v["dry-run"] ? t("Dry run: nothing copied.", "Simulacro: no se copi\xF3 na
12773
12874
  let done2 = 0;
12774
12875
  while (cursor2 !== null) {
12775
12876
  const at = cursor2;
12776
- const p = await pageWithRetry(() => api(path, { method: "POST", body: { integrationId: v.integration, days, cursor: at, media: true } }), `${done2}/${c.conversations}`);
12877
+ const p = await pageWithRetry(() => api(path2, { method: "POST", body: { integrationId: v.integration, days, cursor: at, media: true } }), `${done2}/${c.conversations}`);
12777
12878
  sum2.mediaCopied += p.mediaCopied ?? 0;
12778
12879
  sum2.mediaFailed += p.mediaFailed ?? 0;
12779
12880
  done2 += p.page;
@@ -12805,7 +12906,7 @@ ${t(`Cards appear on the team's board (no "new lead" alerts).`, "Las tarjetas ap
12805
12906
  let done = 0;
12806
12907
  while (cursor !== null) {
12807
12908
  const at = cursor;
12808
- const p = await pageWithRetry(() => api(path, { method: "POST", body: { integrationId: v.integration, days, cursor: at, ...filters } }), `${done}/${c.conversations}`);
12909
+ const p = await pageWithRetry(() => api(path2, { method: "POST", body: { integrationId: v.integration, days, cursor: at, ...filters } }), `${done}/${c.conversations}`);
12809
12910
  for (const k of Object.keys(sum)) sum[k] += p[k] ?? 0;
12810
12911
  done += p.page;
12811
12912
  if (!globals.json) info(dim(` ${done}/${p.total} ${t("conversations", "conversaciones")}`));
@@ -12955,8 +13056,8 @@ async function boardCmd(sub, id, v, ref) {
12955
13056
  if (!Object.keys(filter).length) throw usage(t("Add at least one filter: --integration, --column or --before.", "Agrega al menos un filtro: --integration, --column o --before."), t("A filter-less archive would empty the board.", "Archivar sin filtro vaciar\xEDa el tablero."));
12956
13057
  const retry = `ghosty board archive ${id} ${Object.entries(filter).map(([k, x]) => `--${k} ${JSON.stringify(x)}`).join(" ")}`;
12957
13058
  if (!v["dry-run"]) requireYesIfHeadless(v.yes, retry);
12958
- const path = `/api/v2/agents/${encodeURIComponent(id)}/board/archive`;
12959
- const plan = await api(path, { body: { ...filter, dryRun: true } });
13059
+ const path2 = `/api/v2/agents/${encodeURIComponent(id)}/board/archive`;
13060
+ const plan = await api(path2, { body: { ...filter, dryRun: true } });
12960
13061
  const sample = () => table(plan.sample.map((c) => ({ name: c.name ?? "", phone: c.phone, column: c.estado_label ?? "" })), ["name", "phone", "column"]);
12961
13062
  if (v["dry-run"] || plan.matched === 0) {
12962
13063
  return out(plan, () => plan.matched ? `${sample()}
@@ -12964,7 +13065,7 @@ ${dim(t(`Would archive ${plan.matched} card(s) on \xAB${plan.board}\xBB. Dry run
12964
13065
  }
12965
13066
  await confirm(`${sample()}
12966
13067
  ${t(`Archive ${plan.matched} card(s) on \xAB${plan.board}\xBB? (reversible: a new message brings a card back)`, `\xBFArchivar ${plan.matched} tarjeta(s) en \xAB${plan.board}\xBB? (reversible: un mensaje nuevo regresa la tarjeta)`)}`, { yes: v.yes, retry });
12967
- const r = await api(path, { body: filter });
13068
+ const r = await api(path2, { body: filter });
12968
13069
  out(r, () => `${lavender("\u2713")} ${t(`Archived ${r.archived} card(s) on \xAB${r.board}\xBB.`, `Se archivaron ${r.archived} tarjeta(s) en \xAB${r.board}\xBB.`)}`);
12969
13070
  }
12970
13071
 
@@ -12972,7 +13073,7 @@ ${t(`Archive ${plan.matched} card(s) on \xAB${plan.board}\xBB? (reversible: a ne
12972
13073
  init_api();
12973
13074
  init_util();
12974
13075
  init_i18n();
12975
- var enc11 = encodeURIComponent;
13076
+ var enc12 = encodeURIComponent;
12976
13077
  async function boardsCmd(sub, a1, a2, v, context) {
12977
13078
  if (context && sub !== "assign") {
12978
13079
  if ((sub === "ls" || sub === "list" || !sub || sub === "move-cards") && !a1) a1 = context;
@@ -12983,13 +13084,13 @@ async function boardsCmd(sub, a1, a2, v, context) {
12983
13084
  case "ls":
12984
13085
  case "list": {
12985
13086
  if (!a1) throw usage(t("Missing workspace.", "Falta el espacio."), t("Example", "Ejemplo") + ": ghosty boards ls <workspace-slug>");
12986
- const r = await api(`/api/v2/workspaces/${enc11(a1)}/boards`);
13087
+ const r = await api(`/api/v2/workspaces/${enc12(a1)}/boards`);
12987
13088
  const rows = r.boards.map((b) => ({ name: b.main ? `${b.name} (${t("main", "principal")})` : b.name, cards: b.cards, agents: b.agents.map((x) => x.name ?? x.id).join(", "), columns: b.columns.length, id: b.id }));
12988
13089
  return out(r, () => table(rows, ["name", "cards", "agents", "columns", "id"]));
12989
13090
  }
12990
13091
  case "create": {
12991
13092
  if (!a1 || !a2) throw usage(t("Missing workspace or name.", "Falta el espacio o el nombre."), t("Example", "Ejemplo") + ': ghosty boards create <workspace> "Siiqtec - Ixmiquilpan" --columns-from main');
12992
- const r = await api(`/api/v2/workspaces/${enc11(a1)}/boards`, { method: "POST", body: { name: a2, ...v["columns-from"] ? { columnsFrom: v["columns-from"] } : {} } });
13093
+ const r = await api(`/api/v2/workspaces/${enc12(a1)}/boards`, { method: "POST", body: { name: a2, ...v["columns-from"] ? { columnsFrom: v["columns-from"] } : {} } });
12993
13094
  return out(r, () => `${lavender("\u2713")} ${t(`Board \xAB${r.name}\xBB created${r.columnsFrom ? ` with the ${r.columns} column(s) of \xAB${r.columnsFrom}\xBB` : ""}.`, `Tablero \xAB${r.name}\xBB creado${r.columnsFrom ? ` con las ${r.columns} columna(s) de \xAB${r.columnsFrom}\xBB` : ""}.`)} ${dim(`${t("Send an agent's cards there", "Manda ah\xED las tarjetas de un agente")}: ghosty boards assign <agent-id> "${r.name}"`)}${r.warning ? `
12994
13095
  ${lavender(`\u26A0\uFE0F ${serverText(r.warning)}`)}` : ""}`);
12995
13096
  }
@@ -12997,22 +13098,22 @@ ${lavender(`\u26A0\uFE0F ${serverText(r.warning)}`)}` : ""}`);
12997
13098
  if (!a1 || !a2) throw usage(t("Missing workspace or board.", "Falta el espacio o el tablero."), t("Example", "Ejemplo") + ': ghosty boards rm <workspace> "Prueba"');
12998
13099
  requireYesIfHeadless(v.yes, `ghosty boards rm ${a1} ${JSON.stringify(a2)}`);
12999
13100
  await confirm(t(`Delete the board \xAB${a2}\xBB of ${a1}? (only an empty one; its agents go back to the main board)`, `\xBFBorrar el tablero \xAB${a2}\xBB de ${a1}? (s\xF3lo si est\xE1 vac\xEDo; sus agentes regresan al tablero principal)`), { yes: v.yes, retry: `ghosty boards rm ${a1} ${JSON.stringify(a2)}` });
13000
- const r = await api(`/api/v2/workspaces/${enc11(a1)}/boards`, { method: "DELETE", body: { board: a2 } });
13101
+ const r = await api(`/api/v2/workspaces/${enc12(a1)}/boards`, { method: "DELETE", body: { board: a2 } });
13001
13102
  return out(r, () => `${lavender("\u2713")} ${t(`Board \xAB${r.deleted}\xBB deleted.`, `Tablero \xAB${r.deleted}\xBB borrado.`)}`);
13002
13103
  }
13003
13104
  case "assign": {
13004
13105
  if (!a1 || !a2) throw usage(t("Missing agent or board.", "Falta el agente o el tablero."), t("Example", "Ejemplo") + ': ghosty boards assign <agent-id> "Siiqtec - Ixmiquilpan" (' + t("or main", "o main") + ")");
13005
- const r = await api(`/api/v2/agents/${enc11(a1)}`, { method: "PATCH", body: { board: a2 } });
13106
+ const r = await api(`/api/v2/agents/${enc12(a1)}`, { method: "PATCH", body: { board: a2 } });
13006
13107
  return out(r, () => `${lavender("\u2713")} ${t(`New cards of ${a1} go to \xAB${r.board}\xBB.`, `Las tarjetas nuevas de ${a1} van a \xAB${r.board}\xBB.`)} ${dim(`${t("Existing ones stay where they are", "Las que ya existen se quedan donde est\xE1n")}: ghosty boards move-cards ${r.workspace} --from \u2026 --to "${r.board}" \u2026`)}`);
13007
13108
  }
13008
13109
  case "move-cards": {
13009
13110
  if (!a1 || !v.from || !v.to) throw usage(t("Missing workspace, --from or --to.", "Falta el espacio, --from o --to."), t("Example", "Ejemplo") + ': ghosty boards move-cards <workspace> --from main --to "Siiqtec - Ixmiquilpan" --integration 6a3b02ad\u2026 --dry-run');
13010
13111
  if (!v.integration && !v.phone && !v.all) throw usage(t("Add a filter: --integration ID, --phone N or --all.", "Agrega un filtro: --integration ID, --phone N o --all."), t("A filter-less move would empty the board.", "Mover sin filtro vaciar\xEDa el tablero."));
13011
- const path = `/api/v2/workspaces/${enc11(a1)}/boards/move-cards`;
13112
+ const path2 = `/api/v2/workspaces/${enc12(a1)}/boards/move-cards`;
13012
13113
  const body = { from: v.from, to: v.to, ...v.integration ? { integration: v.integration } : {}, ...v.phone ? { phone: v.phone } : {}, ...v.all ? { all: true } : {} };
13013
13114
  const retry = `ghosty boards move-cards ${a1} --from ${JSON.stringify(v.from)} --to ${JSON.stringify(v.to)}${v.integration ? ` --integration ${v.integration}` : ""}${v.phone ? ` --phone ${v.phone}` : ""}${v.all ? " --all" : ""}`;
13014
13115
  if (!v["dry-run"]) requireYesIfHeadless(v.yes, retry);
13015
- const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
13116
+ const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
13016
13117
  const sample = table(plan.sample.map((c) => ({ folio: c.folio ? `#${c.folio}` : "", name: c.name ?? "", phone: c.phone })), ["folio", "name", "phone"]);
13017
13118
  const text = `${sample}
13018
13119
  ${t(`${plan.moved} of ${plan.matched} card(s)`, `${plan.moved} de ${plan.matched} tarjeta(s)`)} \xAB${plan.from}\xBB \u2192 \xAB${plan.to}\xBB${plan.skipped.length ? dim(t(` \xB7 ${plan.skipped.length} already in \xAB${plan.to}\xBB, skipped`, ` \xB7 ${plan.skipped.length} ya en \xAB${plan.to}\xBB, se omiten`)) : ""}`;
@@ -13020,7 +13121,7 @@ ${t(`${plan.moved} of ${plan.matched} card(s)`, `${plan.moved} de ${plan.matched
13020
13121
  ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
13021
13122
  await confirm(`${text}
13022
13123
  ${t("Move them with their threads and orders? (they get a new folio there)", "\xBFMoverlas con sus hilos y pedidos? (all\xE1 reciben folio nuevo)")}`, { yes: v.yes, retry });
13023
- const r = await api(path, { method: "POST", body });
13124
+ const r = await api(path2, { method: "POST", body });
13024
13125
  return out(r, () => `${lavender("\u2713")} ${t(`Moved ${r.moved} card(s) (${r.messages} message(s), ${r.orders} order(s)) to \xAB${r.to}\xBB.`, `Se movieron ${r.moved} tarjeta(s) (${r.messages} mensaje(s), ${r.orders} pedido(s)) a \xAB${r.to}\xBB.`)}${r.skipped.length ? dim(t(` Skipped ${r.skipped.length}.`, ` Se omitieron ${r.skipped.length}.`)) : ""}`);
13025
13126
  }
13026
13127
  // Quién ve un tablero: lista «sólo estas personas» o todo el espacio (feedback del revisor de Meta).
@@ -13031,7 +13132,7 @@ ${t("Move them with their threads and orders? (they get a new folio there)", "\x
13031
13132
  const add = split(v.add);
13032
13133
  const remove = split(v.remove);
13033
13134
  const edit = add.length || remove.length || v.everyone;
13034
- const r = edit ? await api(`/api/v2/workspaces/${enc11(a1)}/boards/access`, { method: "POST", body: { board, add, remove, ...v.everyone ? { everyone: true } : {}, ...v.restrict ? { restrict: true } : {}, ...v.until ? { until: v.until } : {} } }) : await api(`/api/v2/workspaces/${enc11(a1)}/boards/access?board=${enc11(board)}`);
13135
+ const r = edit ? await api(`/api/v2/workspaces/${enc12(a1)}/boards/access`, { method: "POST", body: { board, add, remove, ...v.everyone ? { everyone: true } : {}, ...v.restrict ? { restrict: true } : {}, ...v.until ? { until: v.until } : {} } }) : await api(`/api/v2/workspaces/${enc12(a1)}/boards/access?board=${enc12(board)}`);
13035
13136
  return out(r, () => {
13036
13137
  const head = `\xAB${r.board}\xBB (${r.workspace}): ${r.everyone ? t("the whole workspace sees it", "lo ve todo el espacio") : t("only these people (plus owner and staff)", "s\xF3lo estas personas (m\xE1s due\xF1o y staff)")}`;
13037
13138
  const people = r.everyone ? "" : `
@@ -13332,6 +13433,18 @@ var HELPS_ES = {
13332
13433
  replace: [['"what to do"', '"qu\xE9 hacer"'], ["# answers in that thread as if mentioned", "# contesta en ese hilo como si lo mencionaran"]],
13333
13434
  notes: ["--teams: el agente tiene que estar activo en el Teams de ese espacio y t\xFA ser miembro. El id del hilo es el del mensaje con que empieza.", "--conversation waba:\u2026 | wa:<jid> | messenger:\u2026: la respuesta sale por ese canal (WABA s\xF3lo dentro de 24 h; WhatsApp personal si el n\xFAmero est\xE1 conectado). \xABOK\xBB nunca se manda."]
13334
13435
  },
13436
+ watch: {
13437
+ summary: "Vigila una p\xE1gina web y que el agente te diga algo S\xD3LO cuando cambie. La plataforma la baja sin turno del modelo (no gasta saldo); si cambi\xF3, el agente recibe un turno en esa conversaci\xF3n con lo que cambi\xF3 y te avisa por su canal.",
13438
+ replace: [['"heading or line"', '"encabezado o l\xEDnea"'], ['"what matters"', '"qu\xE9 importa"']],
13439
+ flags: [
13440
+ ["--every 24h", "cada cu\xE1nto revisarla: 1h m\xEDnimo, 7d m\xE1ximo (default 24h)"],
13441
+ ["--focus TEXT", "compara s\xF3lo esa parte: el texto de un encabezado (hasta el siguiente de su nivel) o de una l\xEDnea"],
13442
+ ["--instruction TEXT", "lo que te importa (\xABav\xEDsame si baja de $500\xBB); el agente lo lee cuando la p\xE1gina cambia y contesta OK (silencio) si el cambio no importa"],
13443
+ ["--days N", "add: vence a los N d\xEDas (default 90, m\xE1x. 365) \xB7 resume: renueva una vencida por N d\xEDas"],
13444
+ ["--dry-run", "check: baja y compara ya contra la \xFAltima foto y ense\xF1a el diff; no guarda ni avisa (cuesta una lectura web, no saldo)"]
13445
+ ],
13446
+ notes: ["Al comparar se ignoran fechas, horas, \xABhace 5 minutos\xBB, scripts, im\xE1genes y URLs de las ligas: una p\xE1gina que s\xF3lo cambia su reloj no avisa.", "Topes: 10 por agente; por cuenta seg\xFAn el plan (Gratis 2, Pro 10, Power 25, Max 50, espacio de equipo 25). Tras 5 lecturas fallidas seguidas se pausa y te llega un push, una vez.", "Sin --conversation abre una nueva, donde caen los avisos. --conversation wa:<jid> | waba:\u2026 tambi\xE9n vale: el aviso sale por ese canal, como un turno programado.", "El agente tiene lo mismo como tools: watch_add, watch_list, watch_remove."]
13447
+ },
13335
13448
  teams: {
13336
13449
  summary: "Escribe en un room o hilo de Teams a tu nombre; el @agente que menciones contesta, como en el composer.",
13337
13450
  replace: [['"message"', '"mensaje"'], ["# new thread", "# hilo nuevo"], ["make Deploy wait for ci with needs", "que el Deploy espere al ci con needs"], ["how is it going?", "\xBFen qu\xE9 va?"]],
@@ -13341,6 +13454,16 @@ var HELPS_ES = {
13341
13454
  summary: "Ghosty para Chrome: cerrar la sesi\xF3n del navegador.",
13342
13455
  notes: ["Revoca todos los tokens de Ghosty para Chrome que tengan tus agentes de terminal (Claude Code, CLI, @ghostystudio/browser-mcp) y desconecta la extensi\xF3n. Para volver, copia un comando nuevo del panel de la extensi\xF3n."]
13343
13456
  },
13457
+ tts: {
13458
+ summary: "Oye la voz de la casa decir un texto (Kokoro, espa\xF1ol de M\xE9xico, con el l\xE9xico de pronunciaci\xF3n de Ghosty): la misma de las notas de voz. Guarda un .wav.",
13459
+ usage: ['ghosty tts "texto" [--voice em_santa|em_alex|ef_dora] [--out ARCHIVO]'],
13460
+ flags: [
13461
+ ["--voice V", "em_santa (hombre, la de la casa, default), em_alex (hombre) o ef_dora (mujer)"],
13462
+ ["--out FILE", "d\xF3nde guardar el .wav (default voz.wav)"]
13463
+ ],
13464
+ examples: ['ghosty tts "Hola, gracias por escribir. Te aviso por WhatsApp."', 'ghosty tts "Tu cita qued\xF3 el jueves a las cinco." --voice ef_dora --out cita.wav && afplay cita.wav'],
13465
+ notes: ["Hasta 400 caracteres y 60 frases por minuto. Las marcas (WhatsApp, Claude, GitHub, ghosty.studio\u2026) se escriben normal: el l\xE9xico corrige c\xF3mo suenan."]
13466
+ },
13344
13467
  image: {
13345
13468
  summary: "Im\xE1genes con IA (Gemini \xABNano Banana Pro\xBB) para los negocios de tu espacio de partner: generar, editar una que ya existe o componer con referencias; cobradas del saldo del espacio.",
13346
13469
  replace: [
@@ -13367,7 +13490,7 @@ var HELPS_ES = {
13367
13490
  notes: [
13368
13491
  "Para cambiar una imagen que ya existe usa edit (no generate): sale la misma imagen con s\xF3lo ese cambio.",
13369
13492
  `Cada imagen se cobra aparte del saldo del espacio de partner: USD 0.25 a 1K/2K y USD 0.45 a 4K; las variantes se cobran una por una y las fallidas no se cobran. Sin saldo: 402. gs no pone topes propios; un negocio puede tener el suyo: tenants set <id> --config '{"imageDailyCap":20}'.`,
13370
- "Auth: tu login (due\xF1a del espacio; --workspace si tienes varios) o la credencial del servidor del partner en el env (GHOSTY_PARTNER_KEY + GHOSTY_PARTNER_KEY_SECRET), que entonces exige --tenant."
13493
+ "Auth: tu login (due\xF1o del espacio; --workspace si tienes varios) o la credencial del servidor del partner en el env (GHOSTY_PARTNER_KEY + GHOSTY_PARTNER_KEY_SECRET), que entonces exige --tenant."
13371
13494
  ]
13372
13495
  },
13373
13496
  me: {
@@ -14142,6 +14265,20 @@ var HELPS = {
14142
14265
  notes: ["--teams: the agent must be active in that workspace's Teams and you must be a member. The thread id is the id of the message that starts it.", "--conversation waba:\u2026 | wa:<jid> | messenger:\u2026: the answer goes out through that channel (WABA only within 24 h; personal WhatsApp if the number is connected). \xABOK\xBB is never sent."],
14143
14266
  learn: `${REF}#programar-turnos`
14144
14267
  },
14268
+ watch: {
14269
+ summary: "Watch a web page and hear from the agent ONLY when it changes. The platform fetches it without a model turn (no balance spent); on a change, the agent gets a turn in that conversation with what changed and tells you through its channel.",
14270
+ usage: ['ghosty watch add <agent-id> <url> [--every 24h] [--focus "heading or line"] [--instruction "what matters"] [--label L] [--days 90] [--conversation ID] [--title T]', "ghosty watch ls <agent-id>", "ghosty watch check <agent-id> <watch-id> [--dry-run]", "ghosty watch rm <agent-id> <watch-id>", "ghosty watch resume <agent-id> <watch-id> [--days N]"],
14271
+ flags: [
14272
+ ["--every 24h", "how often to check it: 1h minimum, 7d maximum (default 24h)"],
14273
+ ["--focus TEXT", "compare only that part: the text of a heading (until the next one of its level) or of a line"],
14274
+ ["--instruction TEXT", "what matters to you (\xABtell me if it drops below $500\xBB); the agent reads it when the page changes and answers OK (silence) if the change doesn't matter"],
14275
+ ["--days N", "add: expires after N days (default 90, max 365) \xB7 resume: renew an expired one for N days"],
14276
+ ["--dry-run", "check: fetch and compare now against the last snapshot, show the diff; saves nothing and notifies nobody (costs one web read, no balance)"]
14277
+ ],
14278
+ examples: ['ghosty watch add <id> https://competencia.mx/precios --every 12h --focus "Planes" --instruction "av\xEDsame si bajan el Pro"', "ghosty watch check <id> <watch-id> --dry-run"],
14279
+ notes: ["Dates, times, \xAB5 minutes ago\xBB, scripts, images and link URLs are ignored when comparing: a page that only changes its clock doesn't notify.", "Caps: 10 per agent; per account by plan (Free 2, Pro 10, Power 25, Max 50, team space 25). After 5 failed reads in a row it pauses and pushes you once.", "No --conversation = a new conversation, where the notices land. --conversation wa:<jid> | waba:\u2026 also works: the notice goes out through that channel, like a scheduled turn.", "The agent has the same as tools: watch_add, watch_list, watch_remove."],
14280
+ learn: `${REF}#vigilar-paginas`
14281
+ },
14145
14282
  teams: {
14146
14283
  summary: "Write in a Teams room or thread as yourself; any @agent you mention answers, like in the composer.",
14147
14284
  usage: ['ghosty teams post <workspace>#<room> "message"', 'ghosty teams post <workspace>#<room>/<thread-id> "message" [--wait [--timeout 600]]'],
@@ -14156,6 +14293,16 @@ var HELPS = {
14156
14293
  notes: ["Revokes every Ghosty for Chrome token your terminal agents hold (Claude Code, CLI, @ghostystudio/browser-mcp) and disconnects the extension. Copy a new command from the extension panel to reconnect."],
14157
14294
  learn: `${REF}#navegador`
14158
14295
  },
14296
+ tts: {
14297
+ summary: "Hear the house voice say a text (Kokoro, Mexican Spanish, with Ghosty's pronunciation lexicon): the same voice as the voice notes. Saves a .wav.",
14298
+ usage: ['ghosty tts "text" [--voice em_santa|em_alex|ef_dora] [--out FILE]'],
14299
+ flags: [
14300
+ ["--voice V", "em_santa (man, the house voice, default), em_alex (man) or ef_dora (woman)"],
14301
+ ["--out FILE", "where to save the .wav (default voz.wav)"]
14302
+ ],
14303
+ examples: ['ghosty tts "Hola, gracias por escribir. Te aviso por WhatsApp."', 'ghosty tts "Tu cita qued\xF3 el jueves a las cinco." --voice ef_dora --out cita.wav && afplay cita.wav'],
14304
+ notes: ["Up to 400 characters and 60 sentences per minute. Brand names (WhatsApp, Claude, GitHub, ghosty.studio\u2026) are written normally: the lexicon fixes how they sound."]
14305
+ },
14159
14306
  image: {
14160
14307
  summary: "AI images (Gemini \xABNano Banana Pro\xBB) for your partner businesses: generate, edit an existing one, or compose from references; charged to the space's balance.",
14161
14308
  usage: [
@@ -14755,7 +14902,7 @@ function helpText(cmd) {
14755
14902
 
14756
14903
  // src/update.ts
14757
14904
  init_i18n();
14758
- import { mkdir as mkdir5, readFile as readFile9, writeFile as writeFile9 } from "node:fs/promises";
14905
+ import { mkdir as mkdir5, readFile as readFile9, writeFile as writeFile10 } from "node:fs/promises";
14759
14906
  import { homedir as homedir3 } from "node:os";
14760
14907
  import { dirname as dirname3, join as join7 } from "node:path";
14761
14908
  var FILE = join7(process.env.XDG_CONFIG_HOME || join7(homedir3(), ".config"), "ghosty", "update.json");
@@ -14781,7 +14928,7 @@ async function latestVersion(current2) {
14781
14928
  const latest = (await r.json()).version;
14782
14929
  if (latest) cache2 = { latest, checkedAt: Date.now() };
14783
14930
  await mkdir5(dirname3(FILE), { recursive: true });
14784
- await writeFile9(FILE, JSON.stringify(cache2));
14931
+ await writeFile10(FILE, JSON.stringify(cache2));
14785
14932
  } catch {
14786
14933
  }
14787
14934
  }
@@ -14842,6 +14989,7 @@ function agentArgIndex(pos) {
14842
14989
  case "board":
14843
14990
  case "conversations":
14844
14991
  case "schedule":
14992
+ case "watch":
14845
14993
  return 2;
14846
14994
  case "try":
14847
14995
  case "chat":
@@ -14861,7 +15009,7 @@ function agentArgIndex(pos) {
14861
15009
  }
14862
15010
 
14863
15011
  // src/completion.ts
14864
- import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile10 } from "node:fs/promises";
15012
+ import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile11 } from "node:fs/promises";
14865
15013
  import { homedir as homedir4 } from "node:os";
14866
15014
  import { join as join8 } from "node:path";
14867
15015
  var GLOBAL_FLAGS = ["--json", "--token", "--profile", "--yes", "--help", "--version", "--lang", "--dry-run"];
@@ -14913,7 +15061,7 @@ async function names() {
14913
15061
  const workspaces = [...new Set(list2.map((a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : void 0).filter((x) => !!x))];
14914
15062
  await mkdir6(join8(cacheFile(), ".."), { recursive: true }).catch(() => {
14915
15063
  });
14916
- await writeFile10(cacheFile(), JSON.stringify({ at: Date.now(), agents, workspaces })).catch(() => {
15064
+ await writeFile11(cacheFile(), JSON.stringify({ at: Date.now(), agents, workspaces })).catch(() => {
14917
15065
  });
14918
15066
  return { agents, workspaces };
14919
15067
  }
@@ -15000,24 +15148,24 @@ init_i18n();
15000
15148
  // src/commands/spec.ts
15001
15149
  var import_yaml = __toESM(require_dist(), 1);
15002
15150
  init_api();
15003
- import { mkdir as mkdir7, readdir as readdir4, readFile as readFile11, stat as stat2, writeFile as writeFile11 } from "node:fs/promises";
15151
+ import { mkdir as mkdir7, readdir as readdir4, readFile as readFile11, stat as stat2, writeFile as writeFile12 } from "node:fs/promises";
15004
15152
  import { join as join9, resolve as resolve2 } from "node:path";
15005
15153
  init_util();
15006
15154
  init_i18n();
15007
- var enc12 = encodeURIComponent;
15155
+ var enc13 = encodeURIComponent;
15008
15156
  async function agentsExport(id, v) {
15009
15157
  if (!id) throw usage(t("Missing agent.", "Falta el agente."), t("Example", "Ejemplo") + ": ghosty agents export pia-0 > pia.yaml \xB7 ghosty agents export pia-0 --out ./pia");
15010
- const yaml = await (await request(`/api/v2/agents/${enc12(id)}/export`)).text();
15158
+ const yaml = await (await request(`/api/v2/agents/${enc13(id)}/export`)).text();
15011
15159
  if (!v.out) {
15012
15160
  if (globals.json) return out((0, import_yaml.parse)(yaml));
15013
15161
  return void process.stdout.write(yaml);
15014
15162
  }
15015
15163
  const dir = resolve2(v.out);
15016
15164
  await mkdir7(dir, { recursive: true });
15017
- await writeFile11(join9(dir, "agent.yaml"), yaml);
15165
+ await writeFile12(join9(dir, "agent.yaml"), yaml);
15018
15166
  const own = (0, import_yaml.parse)(yaml)["x-ghosty"]?.skills?.own ?? [];
15019
15167
  for (const slug of own) {
15020
- const sk = await api(`/api/v2/agents/${enc12(id)}/skills/${enc12(slug)}`);
15168
+ const sk = await api(`/api/v2/agents/${enc13(id)}/skills/${enc13(slug)}`);
15021
15169
  await writeSkill(join9(dir, "skills", slug), sk);
15022
15170
  }
15023
15171
  out({ saved: dir, skills: own }, () => `${lavender("\u2713")} ${dir}/agent.yaml${own.length ? ` + skills/ (${own.join(", ")})` : ""}
@@ -15076,8 +15224,8 @@ ${dim(t("Then every setting of the file is applied. Dry run: nothing created.",
15076
15224
  }
15077
15225
  const spec = JSON.stringify(v.create ? { ...file, title: void 0 } : file);
15078
15226
  const body = { spec, ...files ? { files } : {}, ...v.prune ? { prune: true } : {}, ...v["no-notify"] ? { notify: false } : {} };
15079
- const path = `/api/v2/agents/${enc12(id)}/apply`;
15080
- const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
15227
+ const path2 = `/api/v2/agents/${enc13(id)}/apply`;
15228
+ const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
15081
15229
  const actionable = plan.plan.filter((c) => c.op !== "!");
15082
15230
  const text2 = plan.plan.map(line).join("\n") || dim(t("No changes: the agent already matches the file.", "Sin cambios: el agente ya coincide con el archivo."));
15083
15231
  if (v["dry-run"] || !actionable.length) return out(plan, () => `${bold(id)}
@@ -15087,7 +15235,7 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}` : "
15087
15235
  if (!v.create) requireYesIfHeadless(v.yes, retry);
15088
15236
  if (!v.create) await confirm(`${text2}
15089
15237
  ${t(`Apply ${actionable.length} change(s) to ${id}?`, `\xBFAplicar ${actionable.length} cambio(s) a ${id}?`)}`, { yes: v.yes, retry });
15090
- const r = await api(path, { method: "POST", body });
15238
+ const r = await api(path2, { method: "POST", body });
15091
15239
  out(r, () => `${lavender("\u2713")} ${t(`${r.applied?.length ?? 0} change(s) applied to ${id}.`, `${r.applied?.length ?? 0} cambio(s) aplicados a ${id}.`)}${r.failed ? `
15092
15240
  ${red(t(`Stopped at \xAB${r.failed.what}\xBB: ${serverText(r.failed.error)}`, `Se detuvo en \xAB${r.failed.what}\xBB: ${r.failed.error}`))}` : ""}`);
15093
15241
  }
@@ -15100,8 +15248,8 @@ async function planCmd(sub, slug, v, context) {
15100
15248
  if (sub !== "apply") throw usage(t(`Unknown plan subcommand: ${sub ?? "(none)"}.`, `Subcomando desconocido de plan: ${sub ?? "(ninguno)"}.`), "ghosty plan apply <workspace> [--dry-run]");
15101
15249
  slug ??= context;
15102
15250
  if (!slug) throw usage(t("Missing workspace.", "Falta el espacio."), "ghosty plan apply <workspace> [--dry-run] (or ghosty use <workspace>)");
15103
- const path = `/api/v2/workspaces/${encodeURIComponent(slug)}/plan/apply`;
15104
- const plan = await api(path, { method: "POST", body: { dryRun: true } });
15251
+ const path2 = `/api/v2/workspaces/${encodeURIComponent(slug)}/plan/apply`;
15252
+ const plan = await api(path2, { method: "POST", body: { dryRun: true } });
15105
15253
  const money2 = (n) => `$${n.toLocaleString("es-MX")} MXN`;
15106
15254
  const text = [
15107
15255
  `${bold(plan.workspace)} ${dim(plan.scheduledFor ? t(`scheduled for ${plan.scheduledFor.slice(0, 10)}`, `programado para el ${plan.scheduledFor.slice(0, 10)}`) : "")}`,
@@ -15113,7 +15261,7 @@ ${dim(t("Dry run: nothing applied.", "Simulaci\xF3n: no se aplic\xF3 nada."))}`)
15113
15261
  requireYesIfHeadless(v.yes, `ghosty plan apply ${slug}`);
15114
15262
  await confirm(`${text}
15115
15263
  ${t("Apply this plan now?", "\xBFAplicar este plan ya?")}`, { yes: v.yes, retry: `ghosty plan apply ${slug}` });
15116
- const r = await api(path, { method: "POST", body: {} });
15264
+ const r = await api(path2, { method: "POST", body: {} });
15117
15265
  out(r, () => `${lavender("\u2713")} ${r.applied ? t(`Plan applied to ${r.workspace}.`, `Plan aplicado a ${r.workspace}.`) : t("Nothing applied.", "No se aplic\xF3 nada.")}`);
15118
15266
  }
15119
15267
  async function boxesUsage(slug) {
@@ -15137,17 +15285,17 @@ ${dim(t(`${r.totals.activeTurns} turn(s) running`, `${r.totals.activeTurns} turn
15137
15285
  init_api();
15138
15286
  init_auth();
15139
15287
  import { randomUUID as randomUUID2 } from "node:crypto";
15140
- import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile12 } from "node:fs/promises";
15288
+ import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile13 } from "node:fs/promises";
15141
15289
  import { homedir as homedir5 } from "node:os";
15142
15290
  import { dirname as dirname4, join as join10 } from "node:path";
15143
15291
  init_i18n();
15144
15292
  init_util();
15145
- var enc13 = encodeURIComponent;
15293
+ var enc14 = encodeURIComponent;
15146
15294
  var hora = (iso) => new Date(iso).toLocaleString("es-MX", { timeZone: "America/Mexico_City", hour12: false, dateStyle: "short", timeStyle: "short" });
15147
15295
  async function history(agent, v) {
15148
15296
  if (!agent) throw usage(t("Missing agent.", "Falta el agente."), "ghosty demos history <agent> [--since 2d] [--session web:\u2026] [--timeline] [--full]");
15149
15297
  const q = new URLSearchParams({ since: v.since ?? "7d", ...v.session ? { session: v.session } : {}, ...v.timeline ? { timeline: "1" } : {}, ...v.full ? { full: "1" } : {} });
15150
- const r = await api(`/api/v2/agents/${enc13(agent)}/demo/conversations?${q}`);
15298
+ const r = await api(`/api/v2/agents/${enc14(agent)}/demo/conversations?${q}`);
15151
15299
  out(r, () => {
15152
15300
  const lines = [`${bold(r.agent.name ?? r.agent.id)} ${dim(`/p/${r.agent.slug ?? "\u2014"} \xB7 ${t("since", "desde")} ${hora(r.since)} \xB7 ${t("clones", "clones")}: ${r.clones.length}`)}`];
15153
15301
  for (const c of r.clones) lines.push(dim(` ${t("clone", "clon")} ${c.id} (${hora(c.createdAt)})${c.sponsored ? t(" \xB7 on your key", " \xB7 con tu llave") : ""}`));
@@ -15176,21 +15324,21 @@ function anonCookieOf(setCookie) {
15176
15324
  async function visitorCookie(base, slug, fresh) {
15177
15325
  const jar = await readJar();
15178
15326
  if (jar[slug] && !fresh) return jar[slug];
15179
- const res = await fetch(`${base}/p/${enc13(slug)}`, { redirect: "manual" });
15327
+ const res = await fetch(`${base}/p/${enc14(slug)}`, { redirect: "manual" });
15180
15328
  if (res.status === 404) throw new CliError(t(`No demo \xAB${slug}\xBB (the link is name-code, e.g. guardias-k3x9q2).`, `No hay demo \xAB${slug}\xBB (la liga es nombre-c\xF3digo, p. ej. guardias-k3x9q2).`), EXIT.API);
15181
15329
  const cookie = anonCookieOf(res.headers.get("set-cookie"));
15182
15330
  if (!cookie) throw new CliError(t("The demo didn't hand out a visitor cookie.", "El demo no entreg\xF3 cookie de visitante."), EXIT.API);
15183
15331
  jar[slug] = cookie;
15184
15332
  await mkdir8(dirname4(jarPath()), { recursive: true });
15185
- await writeFile12(jarPath(), JSON.stringify(jar, null, 2), { mode: 384 });
15333
+ await writeFile13(jarPath(), JSON.stringify(jar, null, 2), { mode: 384 });
15186
15334
  return cookie;
15187
15335
  }
15188
15336
  async function visitorTurn(base, slug, cookie, text) {
15189
15337
  const turnId = randomUUID2();
15190
15338
  const ctrl = new AbortController();
15191
- const ev = await fetch(`${base}/p/${enc13(slug)}/events`, { headers: { cookie, accept: "text/event-stream" }, signal: ctrl.signal });
15339
+ const ev = await fetch(`${base}/p/${enc14(slug)}/events`, { headers: { cookie, accept: "text/event-stream" }, signal: ctrl.signal });
15192
15340
  if (!ev.ok) throw await toError(ev);
15193
- const sent = await fetch(`${base}/p/${enc13(slug)}/messages`, { method: "POST", headers: { cookie, "content-type": "application/json" }, body: JSON.stringify({ content: text, turnId }) });
15341
+ const sent = await fetch(`${base}/p/${enc14(slug)}/messages`, { method: "POST", headers: { cookie, "content-type": "application/json" }, body: JSON.stringify({ content: text, turnId }) });
15194
15342
  if (!sent.ok) {
15195
15343
  ctrl.abort();
15196
15344
  const body = await sent.text();
@@ -15220,7 +15368,7 @@ async function visitorTurn(base, slug, cookie, text) {
15220
15368
  async function recycle(agent) {
15221
15369
  for (let i = 0; i < 10; i++) {
15222
15370
  try {
15223
- await api(`/api/v2/agents/${enc13(agent)}/box`, { method: "POST", body: { recycle: true } });
15371
+ await api(`/api/v2/agents/${enc14(agent)}/box`, { method: "POST", body: { recycle: true } });
15224
15372
  return;
15225
15373
  } catch (e) {
15226
15374
  if (!(e instanceof CliError) || !/turno|turn/i.test(e.message)) throw e;
@@ -15276,10 +15424,10 @@ async function collectSiteFiles(target) {
15276
15424
  const full = join11(dir, e.name);
15277
15425
  if (e.isDirectory()) await walk(full);
15278
15426
  else if (e.isFile()) {
15279
- const path = relative2(target, full).split(sep).join("/");
15280
- const ext = path.split(".").pop()?.toLowerCase() ?? "";
15427
+ const path2 = relative2(target, full).split(sep).join("/");
15428
+ const ext = path2.split(".").pop()?.toLowerCase() ?? "";
15281
15429
  const buf = await readFile13(full);
15282
- files.push(TEXT_EXT.has(ext) ? { path, content: buf.toString("utf8"), encoding: "text" } : { path, content: buf.toString("base64"), encoding: "base64" });
15430
+ files.push(TEXT_EXT.has(ext) ? { path: path2, content: buf.toString("utf8"), encoding: "text" } : { path: path2, content: buf.toString("base64"), encoding: "base64" });
15283
15431
  }
15284
15432
  }
15285
15433
  };
@@ -15400,9 +15548,9 @@ async function domainCmd(action, a2, a3) {
15400
15548
  const acts = /* @__PURE__ */ new Set(["ls", "list", "add", "verify", "rm", "remove"]);
15401
15549
  const [act, slug, host] = action && !acts.has(action) ? ["ls", action, a2] : [action ?? "ls", a2, a3];
15402
15550
  if (!slug) throw usage(t("Missing the app (slug, id or URL).", "Falta la app (slug, id o URL)."), ex);
15403
- const path = `/api/v2/sites/${encodeURIComponent(slug)}/domain`;
15551
+ const path2 = `/api/v2/sites/${encodeURIComponent(slug)}/domain`;
15404
15552
  if (act === "ls" || act === "list") {
15405
- const r2 = await api(path);
15553
+ const r2 = await api(path2);
15406
15554
  return out(
15407
15555
  r2,
15408
15556
  () => r2.domains.length ? `${bold(r2.url)}
@@ -15411,18 +15559,18 @@ ${table(r2.domains.map((d) => ({ domain: d.host, state: d.verified ? t("verified
15411
15559
  }
15412
15560
  if (!host) throw usage(t("Missing the domain.", "Falta el dominio."), ex);
15413
15561
  if (act === "rm" || act === "remove") {
15414
- const r2 = await api(path, { method: "DELETE", body: { domain: host } });
15562
+ const r2 = await api(path2, { method: "DELETE", body: { domain: host } });
15415
15563
  return out(r2, () => `${lavender("\u2713")} ${t("Removed", "Quitado")} ${bold(r2.host)}`);
15416
15564
  }
15417
15565
  if (act !== "add" && act !== "verify") throw usage(t(`Unknown domain action: ${act}.`, `Acci\xF3n de dominio desconocida: ${act}.`), ex);
15418
- const r = await api(path, { method: "POST", body: { domain: host, ...act === "verify" ? { verify: true } : {} } });
15566
+ const r = await api(path2, { method: "POST", body: { domain: host, ...act === "verify" ? { verify: true } : {} } });
15419
15567
  return out(r, () => `${r.verified ? lavender("\u2713") : dim("\u2026")} ${bold(r.host)}
15420
15568
  ${domainLine(r, t("en", "es") === "es" ? "es" : "en").replace("<slug>", slug)}`);
15421
15569
  }
15422
15570
 
15423
15571
  // src/commands/docs.ts
15424
15572
  init_api();
15425
- import { writeFile as writeFile13 } from "node:fs/promises";
15573
+ import { writeFile as writeFile14 } from "node:fs/promises";
15426
15574
 
15427
15575
  // ../docs/src/core/doc-blocks.ts
15428
15576
  var DOC_ENVELOPE_VERSION = 1;
@@ -15498,8 +15646,8 @@ function blockTextMapped(b) {
15498
15646
  // src/commands/docs.ts
15499
15647
  init_util();
15500
15648
  init_i18n();
15501
- var enc14 = encodeURIComponent;
15502
- var docPath = (id, rest = "") => `/api/v2/me/docs/${enc14(id)}${rest}`;
15649
+ var enc15 = encodeURIComponent;
15650
+ var docPath = (id, rest = "") => `/api/v2/me/docs/${enc15(id)}${rest}`;
15503
15651
  function docRef(ref) {
15504
15652
  const m = /\/docs\/([^/?#\s]+)/.exec(ref);
15505
15653
  return m && /^https?:\/\//.test(ref) ? decodeURIComponent(m[1]) : ref.trim();
@@ -15585,7 +15733,7 @@ ${t("Ids: ghosty me docs ls", "Ids: ghosty me docs ls")}`);
15585
15733
  }
15586
15734
  const named = /filename\*=UTF-8''([^;]+)/i.exec(res.headers.get("content-disposition") ?? "")?.[1];
15587
15735
  const file = v.out ?? (named ? decodeURIComponent(named) : fileName(id, format));
15588
- await writeFile13(file, bytes);
15736
+ await writeFile14(file, bytes);
15589
15737
  return out({ id, format, saved: file, bytes: bytes.length }, () => `${lavender("\u2713")} ${file} (${bytes.length} bytes)`);
15590
15738
  }
15591
15739
  case "check": {
@@ -15611,7 +15759,7 @@ ${t("Ids: ghosty me docs ls", "Ids: ghosty me docs ls")}`);
15611
15759
  if (res.status === 204) return out({ id, block, segment, segments, saved: null }, () => dim(t("That block has no text to read.", "Ese bloque no tiene texto que leer.")));
15612
15760
  const bytes = Buffer.from(await res.arrayBuffer());
15613
15761
  const file = v.out ?? `${id}-${block}${segment !== null ? `-${segment}` : ""}.${audioExt(res.headers.get("content-type"))}`;
15614
- await writeFile13(file, bytes);
15762
+ await writeFile14(file, bytes);
15615
15763
  return out({ id, block, segment, segments, saved: file, bytes: bytes.length }, () => `${lavender("\u2713")} ${file} (${bytes.length} bytes) ${dim(t(`\xB7 the block has ${segments} sentence(s): --segment 0\u2026${Math.max(segments - 1, 0)}`, `\xB7 el bloque tiene ${segments} frase(s): --segment 0\u2026${Math.max(segments - 1, 0)}`))}`);
15616
15764
  }
15617
15765
  case "restore": {
@@ -15630,8 +15778,8 @@ init_api();
15630
15778
  init_auth();
15631
15779
  init_util();
15632
15780
  init_i18n();
15633
- var enc15 = encodeURIComponent;
15634
- var agentPath3 = (id, rest = "") => `/api/v2/agents/${enc15(id)}${rest}`;
15781
+ var enc16 = encodeURIComponent;
15782
+ var agentPath3 = (id, rest = "") => `/api/v2/agents/${enc16(id)}${rest}`;
15635
15783
  async function agentsToken(sub, id, v) {
15636
15784
  const ex = "ghosty agents token rotate|revoke <agent-id>";
15637
15785
  if (sub !== "rotate" && sub !== "revoke") throw usage(t(`Unknown agents token subcommand: ${sub ?? "(none)"}.`, `Subcomando de agents token desconocido: ${sub ?? "(ninguno)"}.`), ex);
@@ -15653,21 +15801,21 @@ async function agentsToken(sub, id, v) {
15653
15801
  const r = await api(agentPath3(id, "/token/revoke"), { method: "POST", body: {} });
15654
15802
  return out(r, () => `${lavender("\u2713")} ${r.hadToken ? t("Token revoked: nothing gets in with it.", "Token revocado: ya no entra nada con \xE9l.") : t("It had no token; the channel stays closed.", "No ten\xEDa token; el canal sigue cerrado.")} ${dim(t(`New one: ghosty agents token rotate ${id}`, `Uno nuevo: ghosty agents token rotate ${id}`))}`);
15655
15803
  }
15656
- var short2 = (s2, n = 70) => s2.length > n ? s2.slice(0, n - 1) + "\u2026" : s2;
15804
+ var short3 = (s2, n = 70) => s2.length > n ? s2.slice(0, n - 1) + "\u2026" : s2;
15657
15805
  function memoryRows(r) {
15658
15806
  return {
15659
- notes: r.notes.map((n) => ({ id: n.id, scope: n.scope || t("all", "todo"), rule: short2(n.content), by: n.source === "agent" ? t("agent", "agente") : t("team", "equipo") })),
15660
- promos: r.promos.map((p) => ({ id: p.id, title: short2(p.title, 40), when: p.validFrom || p.validUntil ? `${p.validFrom ?? "\u2026"} \u2192 ${p.validUntil ?? "\u2026"}` : t("always", "siempre"), state: !p.active ? t("off", "apagada") : p.live ? t("live today", "vigente hoy") : t("on, not today", "prendida, hoy no") }))
15807
+ notes: r.notes.map((n) => ({ id: n.id, scope: n.scope || t("all", "todo"), rule: short3(n.content), by: n.source === "agent" ? t("agent", "agente") : t("team", "equipo") })),
15808
+ promos: r.promos.map((p) => ({ id: p.id, title: short3(p.title, 40), when: p.validFrom || p.validUntil ? `${p.validFrom ?? "\u2026"} \u2192 ${p.validUntil ?? "\u2026"}` : t("always", "siempre"), state: !p.active ? t("off", "apagada") : p.live ? t("live today", "vigente hoy") : t("on, not today", "prendida, hoy no") }))
15661
15809
  };
15662
15810
  }
15663
15811
  async function agentsMemory(sub, id, args, v) {
15664
15812
  const ex = 'ghosty agents memory ls <agent-id> | add <agent-id> "rule" [--scope wa] | edit <agent-id> <rule-id> "rule" | add <agent-id> --promo --title T --details D [--from YYYY-MM-DD] [--until YYYY-MM-DD] | rm <agent-id> <id> | enable|disable <agent-id> <promo-id>';
15665
15813
  if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), ex);
15666
- const path = agentPath3(id, "/memory");
15814
+ const path2 = agentPath3(id, "/memory");
15667
15815
  switch (sub ?? "ls") {
15668
15816
  case "ls":
15669
15817
  case "list": {
15670
- const r = await api(path);
15818
+ const r = await api(path2);
15671
15819
  const rows = memoryRows(r);
15672
15820
  return out(
15673
15821
  r,
@@ -15680,33 +15828,33 @@ async function agentsMemory(sub, id, args, v) {
15680
15828
  case "add": {
15681
15829
  if (v.promo) {
15682
15830
  if (!v.title || !v.details) throw usage(t("A promo needs --title and --details.", "Una promo necesita --title y --details."), ex);
15683
- const r2 = await api(path, { method: "POST", body: { promo: { title: v.title, details: v.details, ...v.from ? { validFrom: v.from } : {}, ...v.until ? { validUntil: v.until } : {} } } });
15831
+ const r2 = await api(path2, { method: "POST", body: { promo: { title: v.title, details: v.details, ...v.from ? { validFrom: v.from } : {}, ...v.until ? { validUntil: v.until } : {} } } });
15684
15832
  return out(r2, () => `${lavender("\u2713")} ${t("Promo saved", "Promo guardada")} ${r2.promo.id}${r2.warning ? dim(` (${serverText(r2.warning)})`) : ""}`);
15685
15833
  }
15686
15834
  const content = args.join(" ").trim();
15687
15835
  if (!content) throw usage(t("Missing the rule.", "Falta la regla."), ex);
15688
- const r = await api(path, { method: "POST", body: { content, ...v.scope ? { scope: v.scope } : {} } });
15836
+ const r = await api(path2, { method: "POST", body: { content, ...v.scope ? { scope: v.scope } : {} } });
15689
15837
  return out(r, () => `${lavender("\u2713")} ${t("Saved", "Guardada")} ${r.note.id} ${dim(r.note.scope ? t(`(only ${r.note.scope})`, `(s\xF3lo ${r.note.scope})`) : t("(whole agent)", "(todo el agente)"))}`);
15690
15838
  }
15691
15839
  case "edit": {
15692
15840
  const [ref, ...rest] = args;
15693
15841
  const content = rest.join(" ").trim();
15694
15842
  if (!ref || !content) throw usage(t("Missing the rule id or its new text.", "Falta el id de la regla o su texto nuevo."), `ghosty agents memory edit ${id} <rule-id> "\u2026"`);
15695
- const r = await api(path, { method: "POST", body: { content, replaces: ref, ...v.scope !== void 0 ? { scope: v.scope } : {} } });
15843
+ const r = await api(path2, { method: "POST", body: { content, replaces: ref, ...v.scope !== void 0 ? { scope: v.scope } : {} } });
15696
15844
  return out(r, () => `${lavender("\u2713")} ${t("Updated", "Actualizada")} ${r.note.id}`);
15697
15845
  }
15698
15846
  case "rm":
15699
15847
  case "forget": {
15700
15848
  const ref = args[0];
15701
15849
  if (!ref) throw usage(t("Missing the rule or promo id.", "Falta el id de la regla o la promo."), `ghosty agents memory rm ${id} <id> (ids: ghosty agents memory ls ${id})`);
15702
- const r = await api(path, { method: "DELETE", body: { id: ref } });
15850
+ const r = await api(path2, { method: "DELETE", body: { id: ref } });
15703
15851
  return out(r, () => `${lavender("\u2713")} ${r.kind === "promo" ? t("Promo deleted", "Promo borrada") : t("Rule forgotten", "Regla olvidada")} ${r.deleted}`);
15704
15852
  }
15705
15853
  case "enable":
15706
15854
  case "disable": {
15707
15855
  const ref = args[0];
15708
15856
  if (!ref) throw usage(t("Missing the promo id.", "Falta el id de la promo."), `ghosty agents memory ${sub} ${id} <promo-id>`);
15709
- const r = await api(path, { method: "POST", body: { promo: { id: ref, active: sub === "enable" } } });
15857
+ const r = await api(path2, { method: "POST", body: { promo: { id: ref, active: sub === "enable" } } });
15710
15858
  return out(r, () => `${lavender("\u2713")} ${r.promo.title}: ${r.promo.active ? t("on", "prendida") : t("off (kept)", "apagada (se conserva)")}`);
15711
15859
  }
15712
15860
  default:
@@ -15722,8 +15870,8 @@ async function agentsTransfer(id, v) {
15722
15870
  if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), ex);
15723
15871
  if (!v.to && !v.workspace) throw usage(t("Say where: --to EMAIL (another person) or --workspace SLUG|none (one of your workspaces).", "Di a d\xF3nde: --to CORREO (otra persona) o --workspace SLUG|none (uno de tus espacios)."), ex);
15724
15872
  const body = { ...v.to ? { to: v.to } : {}, ...v.workspace ? { workspace: v.workspace } : {}, ...v.force ? { force: true } : {} };
15725
- const path = agentPath3(id, "/transfer");
15726
- const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
15873
+ const path2 = agentPath3(id, "/transfer");
15874
+ const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
15727
15875
  const text = transferText(plan);
15728
15876
  const retry = `ghosty agents transfer ${id}${v.to ? ` --to ${v.to}` : ""}${v.workspace ? ` --workspace ${v.workspace}` : ""}${v.force ? " --force" : ""}`;
15729
15877
  if (v["dry-run"]) return out(plan, () => `${text}
@@ -15732,13 +15880,13 @@ ${dim(applyHint(retry))}`);
15732
15880
  requireYesIfHeadless(v.yes, retry);
15733
15881
  await confirm(`${text}
15734
15882
  ${v.to ? t("The agent, its box and its memory go to that person. You lose it.", "El agente, su caja y su memoria pasan a esa persona. T\xFA lo pierdes.") : t("Move it?", "\xBFMoverlo?")}`, { yes: v.yes, ...v.to ? { expect: v.to } : {}, retry });
15735
- const r = await api(path, { method: "POST", body });
15883
+ const r = await api(path2, { method: "POST", body });
15736
15884
  if (r.kind === "move" && r.failed.length) return out(r, () => `${transferText(r)}
15737
15885
  ${t(`Moved, but these steps failed: ${r.failed.join(", ")}. Check the agent (ghosty agents doctor ${id}).`, `Se movi\xF3, pero fallaron estos pasos: ${r.failed.join(", ")}. Revisa el agente (ghosty agents doctor ${id}).`)}`);
15738
15886
  return out(r, () => `${lavender("\u2713")} ${r.kind === "transfer" ? t(`Handed over to ${r.to}.`, `Cedido a ${r.to}.`) : t(`Moved to ${r.to}.`, `Movido a ${r.to}.`)}`);
15739
15887
  }
15740
- async function openPage(path, what, v) {
15741
- const url = `${await baseUrl()}${path}`;
15888
+ async function openPage(path2, what, v) {
15889
+ const url = `${await baseUrl()}${path2}`;
15742
15890
  if (globals.json) return out({ url });
15743
15891
  info(`${lavender("\u203A")} ${what}: ${url}`);
15744
15892
  if (!v["no-open"]) await openUrl(url);
@@ -15785,15 +15933,15 @@ import { randomBytes as randomBytes2 } from "node:crypto";
15785
15933
  import { existsSync, readFileSync } from "node:fs";
15786
15934
  init_util();
15787
15935
  init_i18n();
15788
- var enc16 = encodeURIComponent;
15936
+ var enc17 = encodeURIComponent;
15789
15937
  var money = (n) => n === null || n === void 0 ? "\u2014" : `$${n.toFixed(2)}`;
15790
15938
  var mins = (n) => n === null || n === void 0 ? "\u2014" : n >= 120 ? `${(n / 60).toFixed(1)} h` : `${n} min`;
15791
15939
  var hhmm = (iso) => new Date(iso).toLocaleTimeString("es-MX", { hour: "2-digit", minute: "2-digit", hour12: false });
15792
- var short3 = (s2) => (s2 ?? "").slice(0, 16).replace("T", " ");
15940
+ var short4 = (s2) => (s2 ?? "").slice(0, 16).replace("T", " ");
15793
15941
  async function roomsCmd(ws, verb, repo, v) {
15794
- const path = `/api/v2/workspaces/${enc16(ws)}/factory/rooms`;
15942
+ const path2 = `/api/v2/workspaces/${enc17(ws)}/factory/rooms`;
15795
15943
  if (!verb || verb === "ls" || verb === "list") {
15796
- const r2 = await api(path);
15944
+ const r2 = await api(path2);
15797
15945
  const rows = r2.rooms.filter((x) => x.repos.length || v.full).map((x) => ({ room: `#${x.slug}`, repos: x.repos.join(", ") || dim("\u2014"), factory: x.factory ? lavender(t("yes", "s\xED")) : dim("\u2014"), private: x.private ? t("yes", "s\xED") : "" }));
15798
15946
  return out(
15799
15947
  r2,
@@ -15805,7 +15953,7 @@ async function roomsCmd(ws, verb, repo, v) {
15805
15953
  );
15806
15954
  }
15807
15955
  if (verb === "repos") {
15808
- const r2 = await api(`${path}?repos=1`);
15956
+ const r2 = await api(`${path2}?repos=1`);
15809
15957
  return out(r2, () => {
15810
15958
  if (!r2.connected) return red(t(`GitHub isn't connected: ${r2.detail ?? "connect it with ghosty connectors connect github"}`, `GitHub no est\xE1 conectado: ${r2.detail ?? "con\xE9ctalo con ghosty connectors connect github"}`));
15811
15959
  if (!r2.installed) return red(t(`The Ghosty GitHub App isn't installed: ${r2.installUrl ?? ""}`, `La GitHub App de Ghosty no est\xE1 instalada: ${r2.installUrl ?? ""}`));
@@ -15823,10 +15971,10 @@ async function roomsCmd(ws, verb, repo, v) {
15823
15971
  const retry = `ghosty factory rooms disconnect ${repo} --room ${v.room} --workspace ${ws}`;
15824
15972
  requireYesIfHeadless(v.yes, retry);
15825
15973
  await confirm(t(`Disconnect ${repo} from #${v.room}?`, `\xBFDesconectar ${repo} de #${v.room}?`), { yes: v.yes, retry });
15826
- const r2 = await api(path, { body: { action: "disconnect", repo, room: v.room } });
15974
+ const r2 = await api(path2, { body: { action: "disconnect", repo, room: v.room } });
15827
15975
  return out(r2, () => `${lavender("\u2713")} ${t(`${repo} is no longer connected to #${r2.room}.`, `${repo} ya no est\xE1 conectado a #${r2.room}.`)}`);
15828
15976
  }
15829
- const r = await api(path, { body: { action: "connect", repo, room: v.room, ...v.create ? { create: true } : {}, ...v.private ? { private: true } : {} } });
15977
+ const r = await api(path2, { body: { action: "connect", repo, room: v.room, ...v.create ? { create: true } : {}, ...v.private ? { private: true } : {} } });
15830
15978
  const rd = r.readiness;
15831
15979
  return out(
15832
15980
  r,
@@ -15841,9 +15989,9 @@ async function roomsCmd(ws, verb, repo, v) {
15841
15989
  );
15842
15990
  }
15843
15991
  async function sprintsCmd(ws, verb, ref, v) {
15844
- const path = `/api/v2/workspaces/${enc16(ws)}/factory/sprints`;
15992
+ const path2 = `/api/v2/workspaces/${enc17(ws)}/factory/sprints`;
15845
15993
  if (!verb || verb === "ls" || verb === "list") {
15846
- const r2 = await api(path);
15994
+ const r2 = await api(path2);
15847
15995
  const rows = r2.sprints.map((x) => ({ sprint: `#${x.id}`, title: x.title.slice(0, 60), status: x.status, room: x.room ? `#${x.room}` : "", merged: `${x.merged}/${x.total}` }));
15848
15996
  return out(r2, () => rows.length ? table(rows, ["sprint", "title", "status", "room", "merged"]) : dim(t("No sprints yet.", "Todav\xEDa no hay sprints.")));
15849
15997
  }
@@ -15855,7 +16003,7 @@ async function sprintsCmd(ws, verb, ref, v) {
15855
16003
  requireYesIfHeadless(v.yes, retry);
15856
16004
  await confirm(t(`Approve sprint #${id}? Every included ticket starts, one at a time.`, `\xBFAprobar el sprint #${id}? Arrancan todos los tickets incluidos, uno a la vez.`), { yes: v.yes, retry });
15857
16005
  }
15858
- const r = verb === "approve" ? await api(`${path}/${id}`, { body: { action: "approve" } }) : await api(`${path}/${id}`);
16006
+ const r = verb === "approve" ? await api(`${path2}/${id}`, { body: { action: "approve" } }) : await api(`${path2}/${id}`);
15859
16007
  return out(
15860
16008
  r,
15861
16009
  () => [
@@ -15881,12 +16029,12 @@ function dotenvKeys(text) {
15881
16029
  return text.split("\n").map((l) => l.trim().replace(/^export\s+/, "")).filter((l) => l && !l.startsWith("#") && l.indexOf("=") > 0).map((l) => l.slice(0, l.indexOf("=")).trim());
15882
16030
  }
15883
16031
  async function envCmd(ws, verb, ref, file, v) {
15884
- const path = `/api/v2/workspaces/${enc16(ws)}/factory/env`;
16032
+ const path2 = `/api/v2/workspaces/${enc17(ws)}/factory/env`;
15885
16033
  const repo = ref && ref.includes("/") ? ref : v.repo ?? repoHere();
15886
16034
  if (ref && !ref.includes("/") && !file) file = ref;
15887
16035
  if (!repo) throw usage(t("Which repo?", "\xBFQu\xE9 repo?"), `ghosty factory env ${verb ?? "ls"} OWNER/REPO --workspace ${ws}`);
15888
16036
  if (!verb || verb === "ls" || verb === "list") {
15889
- const r2 = await api(`${path}?repo=${enc16(repo)}`);
16037
+ const r2 = await api(`${path2}?repo=${enc17(repo)}`);
15890
16038
  const risk = new Map(r2.risks.map((x) => [x.key, x.risk]));
15891
16039
  return out(
15892
16040
  r2,
@@ -15925,7 +16073,7 @@ ${k}=${randomBytes2(32).toString("hex")}`;
15925
16073
  v.replace ? t(`REPLACE the whole preview .env of ${repo} with ${keys.length} variables (${keys.join(", ")})?`, `\xBFREEMPLAZAR todo el .env de la preview de ${repo} con ${keys.length} variables (${keys.join(", ")})?`) : t(`Save ${keys.length} variables for the preview of ${repo} (${keys.join(", ")})? The others stay.`, `\xBFGuardar ${keys.length} variables para la preview de ${repo} (${keys.join(", ")})? Las dem\xE1s se quedan.`),
15926
16074
  { yes: v.yes, retry }
15927
16075
  );
15928
- const r = await api(path, {
16076
+ const r = await api(path2, {
15929
16077
  body: { repo, dotenv, ...v.replace ? { replace: true } : {} }
15930
16078
  });
15931
16079
  return out(
@@ -16026,7 +16174,7 @@ async function factoryCmd(sub, verb, ref, title, v, context) {
16026
16174
  if (sub === "rooms") return roomsCmd(ws, verb, ref, v);
16027
16175
  if (sub === "sprints") return sprintsCmd(ws, verb, ref, v);
16028
16176
  if (sub === "env") return envCmd(ws, verb, ref, title, v);
16029
- const base = `/api/v2/workspaces/${enc16(ws)}/factory/runs`;
16177
+ const base = `/api/v2/workspaces/${enc17(ws)}/factory/runs`;
16030
16178
  if (sub === "run") {
16031
16179
  const text = verb?.trim();
16032
16180
  if (!text) throw usage(t("Missing the request.", "Falta el pedido."), `ghosty factory run "\u2026" --repo owner/name [--room R] [--wait] --workspace ${ws}`);
@@ -16043,13 +16191,13 @@ ${dim(t(`Wait for the plan: ghosty factory runs watch --thread ${r.threadId} --w
16043
16191
  if (!verb || verb === "ls" || verb === "list") {
16044
16192
  const qs = new URLSearchParams({ ...v.room ? { room: v.room } : {}, ...v.status ? { status: v.status } : {}, ...v.limit ? { limit: v.limit } : {} });
16045
16193
  const r = await api(`${base}?${qs}`);
16046
- const rows = r.runs.map((x) => ({ run: `#${x.id}`, title: x.title.slice(0, 60), status: x.status, room: x.room ? `#${x.room}` : "", pr: x.pr ? x.pr.replace(/^https:\/\/github\.com\//, "") : "", time: mins(x.durationMin), usd: money(x.usd), updated: short3(x.updatedAt) }));
16194
+ const rows = r.runs.map((x) => ({ run: `#${x.id}`, title: x.title.slice(0, 60), status: x.status, room: x.room ? `#${x.room}` : "", pr: x.pr ? x.pr.replace(/^https:\/\/github\.com\//, "") : "", time: mins(x.durationMin), usd: money(x.usd), updated: short4(x.updatedAt) }));
16047
16195
  return out(r, () => rows.length ? `${table(rows, ["run", "title", "status", "room", "pr", "time", "usd", "updated"])}
16048
16196
  ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r.workspace}`)}` : dim(t(`No factory runs in ${r.workspace}.`, `No hay pedidos de la f\xE1brica en ${r.workspace}.`)));
16049
16197
  }
16050
16198
  if (verb === "stats") {
16051
16199
  const qs = new URLSearchParams({ since: v.since ?? "30d", ...v.repo ? { repo: v.repo } : {} });
16052
- const { stats: s2, workspace } = await api(`/api/v2/workspaces/${enc16(ws)}/factory/stats?${qs}`);
16200
+ const { stats: s2, workspace } = await api(`/api/v2/workspaces/${enc17(ws)}/factory/stats?${qs}`);
16053
16201
  return out({ workspace, stats: s2 }, () => {
16054
16202
  if (!s2.runs) return dim(t(`No factory runs in ${workspace} in the last ${s2.sinceDays} days.`, `No hay pedidos de la f\xE1brica en ${workspace} en los \xFAltimos ${s2.sinceDays} d\xEDas.`));
16055
16203
  return [
@@ -16069,9 +16217,9 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
16069
16217
  return waitResult(ws, await waitRun(base, ws, ref ? { runId: Number(ref.replace(/^#/, "")) } : { thread }, timeoutS), timeoutS);
16070
16218
  }
16071
16219
  if (!ref) throw usage(t("Missing run.", "Falta el pedido."), `ghosty factory runs ${verb} <run> --workspace ${ws}`);
16072
- const path = `${base}/${enc16(ref.replace(/^#/, ""))}`;
16220
+ const path2 = `${base}/${enc17(ref.replace(/^#/, ""))}`;
16073
16221
  if (verb === "show" && v.timing) {
16074
- const { timing: tm, workspace } = await api(`${path}/timing`);
16222
+ const { timing: tm, workspace } = await api(`${path2}/timing`);
16075
16223
  return out({ workspace, timing: tm }, () => {
16076
16224
  const c = tm.clock;
16077
16225
  const head = [
@@ -16102,14 +16250,14 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
16102
16250
  });
16103
16251
  }
16104
16252
  if (verb === "why") {
16105
- const { why: w, workspace } = await api(`${path}/why`, v.refresh ? { body: { refresh: true } } : void 0);
16253
+ const { why: w, workspace } = await api(`${path2}/why`, v.refresh ? { body: { refresh: true } } : void 0);
16106
16254
  return out({ workspace, why: w }, () => {
16107
16255
  const gap = w.incomplete?.length ? red(t(`Incomplete: no recorded turn for @${w.incomplete.join(", @")} (lost, e.g. a deploy while it closed); numbers don't add up.`, `Incompleto: no qued\xF3 registrado el turno de @${w.incomplete.join(", @")} (se perdi\xF3, p. ej. un deploy al cerrarlo); los n\xFAmeros no cuadran.`)) : null;
16108
16256
  if (gap) return `${bold(`#${w.runId}`)} ${gap}`;
16109
16257
  if (w.healthy) return `${lavender("\u2713")} #${w.runId} ${t("was healthy: no stuck patterns, no long @build, no loops, no red CI.", "fue sano: sin atascos, sin @build largo, sin vueltas ni CI rojo.")}`;
16110
16258
  const impact = { high: t("high", "alto"), medium: t("medium", "medio"), low: t("low", "bajo") }[w.impact] ?? w.impact;
16111
16259
  return [
16112
- `${bold(`#${w.runId}`)} ${red(w.category)} ${dim(`\xB7 ${t("impact", "impacto")} ${impact} \xB7 ${w.source === "model" ? w.model ?? "model" : t("rules", "reglas")} \xB7 ${short3(w.createdAt)}`)}`,
16260
+ `${bold(`#${w.runId}`)} ${red(w.category)} ${dim(`\xB7 ${t("impact", "impacto")} ${impact} \xB7 ${w.source === "model" ? w.model ?? "model" : t("rules", "reglas")} \xB7 ${short4(w.createdAt)}`)}`,
16113
16261
  ...w.acceptance?.length ? [bold(red(t("Unmet criterion, approved anyway", "Criterio incumplido y aprobado"))), ...w.acceptance.map((g) => ` ${g.at ? hhmm(g.at) : "\u2014"} ${red(g.rule)} \xAB${g.criterion}\xBB
16114
16262
  ${dim(g.evidence)}`), ""] : [],
16115
16263
  w.summary,
@@ -16122,7 +16270,7 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
16122
16270
  }
16123
16271
  if (verb === "advise") {
16124
16272
  const id = Number(ref.replace(/^#/, ""));
16125
- const { advice: a, workspace } = await api(`${path}/advise`, { body: {} });
16273
+ const { advice: a, workspace } = await api(`${path2}/advise`, { body: {} });
16126
16274
  const note = a.buildNote.map((p, i) => `${i + 1}. ${p.point}`).join(" ");
16127
16275
  const sh = (x) => `'${x.replace(/'/g, `'\\''`)}'`;
16128
16276
  const apply = a.decision === "fix" ? `ghosty factory runs approve ${id} --note ${sh(note)}${a.checkScope ? ` --check-scope ${sh(a.checkScope)}` : ""} --workspace ${workspace}` : a.decision === "replan" ? `ghosty factory runs changes ${id} --note ${sh(note)} --workspace ${workspace}` : a.decision === "recheck" ? `ghosty factory runs recheck ${id} --workspace ${workspace}` : null;
@@ -16148,21 +16296,21 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
16148
16296
  });
16149
16297
  }
16150
16298
  if (verb === "show") {
16151
- const { run: r } = await api(path);
16299
+ const { run: r } = await api(path2);
16152
16300
  const lines = [
16153
16301
  `${bold(`#${r.id} ${r.title}`)} ${dim(`\xB7 ${r.status}${r.kind ? ` (${r.kind})` : ""} \xB7 ${r.room ? `#${r.room}` : ""}`)}`,
16154
16302
  `${t("repo", "repo")}: ${r.repo ?? "\u2014"}${r.branch ? ` (${r.branch})` : ""} PR: ${r.pr ?? "\u2014"}`,
16155
16303
  `preview: ${r.preview ?? "\u2014"}${r.previewState ? dim(` (${r.previewState})`) : ""}`,
16156
16304
  `${t("plan", "plan")}: v${r.planVersion}${r.plan ? ` ${r.plan.decision ?? t("pending", "pendiente")}${r.plan.decidedBy ? ` ${t("by", "por")} ${r.plan.decidedBy}` : ""}` : ""} \xB7 ${t("loops", "vueltas")}: ${r.loops} \xB7 ${t("requested by", "pidi\xF3")} ${r.requestedBy}`,
16157
16305
  ...r.plans.map((p) => dim(` v${p.version} ${p.decision ?? "\u2014"}: ${p.title ?? ""}`)),
16158
- `${t("running", "trabajando")}: ${r.running.length ? r.running.map((x) => `@${x.role} ${dim(`(${x.turn}, ${short3(x.since)})`)}`).join(", ") : dim(t("nobody", "nadie"))}`,
16306
+ `${t("running", "trabajando")}: ${r.running.length ? r.running.map((x) => `@${x.role} ${dim(`(${x.turn}, ${short4(x.since)})`)}`).join(", ") : dim(t("nobody", "nadie"))}`,
16159
16307
  ...r.escalation ? [
16160
16308
  bold(red(r.escalation.blocked ? t("@check stopped it: needs something @build can't do", "@check lo fren\xF3: falta algo que @build no puede dar") : t(`@check stopped it after ${r.escalation.loops} loops`, `@check lo fren\xF3 tras ${r.escalation.loops} vueltas`))),
16161
16309
  r.escalation.findings,
16162
16310
  dim(t(`What to do: ghosty factory runs advise ${r.id} --workspace ${ws}`, `Qu\xE9 hacer: ghosty factory runs advise ${r.id} --workspace ${ws}`))
16163
16311
  ] : [],
16164
16312
  bold(t("Latest events", "\xDAltimos eventos")),
16165
- ...r.events.map((e) => ` ${dim(short3(e.at))} ${e.type}${e.actor ? ` ${dim(e.actor)}` : ""}`),
16313
+ ...r.events.map((e) => ` ${dim(short4(e.at))} ${e.type}${e.actor ? ` ${dim(e.actor)}` : ""}`),
16166
16314
  ...v.full && r.plan ? ["", bold(t("Current plan", "Plan vigente")), r.plan.markdown] : [dim(t("--full prints the current plan.", "--full imprime el plan vigente."))]
16167
16315
  ];
16168
16316
  return out(r, () => lines.join("\n"));
@@ -16171,12 +16319,12 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
16171
16319
  if (!title?.trim()) throw usage(t("Missing the new title.", "Falta el t\xEDtulo nuevo."), `ghosty factory runs rename ${ref} "T\xEDtulo" --workspace ${ws}`);
16172
16320
  const retry = `ghosty factory runs rename ${ref} ${JSON.stringify(title)} --workspace ${ws}`;
16173
16321
  if (!v["dry-run"]) requireYesIfHeadless(v.yes, retry);
16174
- const plan = await api(path, { body: { title, dryRun: true } });
16322
+ const plan = await api(path2, { body: { title, dryRun: true } });
16175
16323
  const line2 = `#${ref.replace(/^#/, "")}: \xAB${plan.from}\xBB \u2192 \xAB${plan.to}\xBB`;
16176
16324
  if (v["dry-run"]) return out({ ...plan, dryRun: true }, () => `${line2}
16177
16325
  ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
16178
16326
  await confirm(t(`Rename ${line2}?`, `\xBFRenombrar ${line2}?`), { yes: v.yes, retry });
16179
- const r = await api(path, { body: { title } });
16327
+ const r = await api(path2, { body: { title } });
16180
16328
  return out(r, () => `${lavender("\u2713")} ${line2} ${dim(t("(logged; the room card picks it up on its next refresh)", "(queda en bit\xE1cora; la tarjeta del room lo toma en su siguiente refresco)"))}`);
16181
16329
  }
16182
16330
  if (verb === "approve" || verb === "changes" || verb === "recheck" || verb === "merge" || verb === "card") {
@@ -16378,7 +16526,7 @@ init_api();
16378
16526
  import { readFile as readFile14 } from "node:fs/promises";
16379
16527
  init_i18n();
16380
16528
  init_util();
16381
- var enc17 = encodeURIComponent;
16529
+ var enc18 = encodeURIComponent;
16382
16530
  var cdmx = (iso) => new Date(Date.parse(iso) - 6 * 36e5).toISOString().slice(5, 16).replace("T", " ");
16383
16531
  async function supportThreads(email, v) {
16384
16532
  const q = new URLSearchParams({ user: email, since: v.since ?? "30d", ...v.surface ? { surface: v.surface } : {} });
@@ -16398,7 +16546,7 @@ ${table(rows, ["last", "agent", "conversation", "title", "kind", "listed", "turn
16398
16546
  ${dim(t("Read one: ghosty conversations show <agent> <conversation> --why \xB7 times in CDMX \xB7 listed = in their app list", "Leer uno: ghosty conversations show <agente> <conversaci\xF3n> --why \xB7 horas CDMX \xB7 listed = sale en su lista de la app"))}`);
16399
16547
  }
16400
16548
  async function supportThread(agent, sid, v) {
16401
- const r = await api(`/api/v2/support/threads/${enc17(agent)}/${enc17(sid)}?tail=${enc17(v.limit ?? "40")}${v.tools ? "&tools=1" : ""}`);
16549
+ const r = await api(`/api/v2/support/threads/${enc18(agent)}/${enc18(sid)}?tail=${enc18(v.limit ?? "40")}${v.tools ? "&tools=1" : ""}`);
16402
16550
  out(r, () => {
16403
16551
  if (v.tools && r.tools) {
16404
16552
  const x = r.tools;
@@ -16431,9 +16579,9 @@ async function cleanupConversations(action, agent, v) {
16431
16579
  if (!agent) throw usage(t("Missing agent.", "Falta el agente."), ex);
16432
16580
  if (v.kind !== "test" && v.kind !== "channel" && v.kind !== "personal") throw usage(t("--kind takes test, channel or personal.", "--kind acepta test, channel o personal."), ex);
16433
16581
  if (action === "rm" && v.kind === "channel") throw usage(t("Customer conversations (channel) are archived, never deleted: their memory keeps serving them.", "Los hilos de clientes (channel) se archivan, nunca se borran: su memoria los sigue atendiendo."), ex);
16434
- const path = `/api/v2/agents/${enc17(agent)}/conversations/cleanup`;
16582
+ const path2 = `/api/v2/agents/${enc18(agent)}/conversations/cleanup`;
16435
16583
  const body = { kind: v.kind, action, ...v.before ? { before: v.before } : {} };
16436
- const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
16584
+ const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
16437
16585
  const verb = action === "rm" ? t("delete (rows + agent memory)", "borrar (filas + memoria del agente)") : t("archive (hidden; memory stays)", "archivar (se ocultan; la memoria se queda)");
16438
16586
  const line2 = t(`${plan.threads} ${v.kind} conversation(s), ${plan.messages} message(s) to ${verb}`, `${plan.threads} conversaci\xF3n(es) ${v.kind}, ${plan.messages} mensaje(s) para ${verb}`);
16439
16587
  if (v["dry-run"] || !plan.threads) return out(plan, () => `${line2}${plan.sample.length ? `
@@ -16442,11 +16590,11 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
16442
16590
  requireYesIfHeadless(v.yes, `ghosty conversations ${action} ${agent} --kind ${v.kind}${v.before ? ` --before ${v.before}` : ""}`);
16443
16591
  await confirm(`${line2}
16444
16592
  ${t("Go ahead?", "\xBFSigo?")}`, { yes: v.yes, retry: `ghosty conversations ${action} ${agent} --kind ${v.kind}` });
16445
- const r = await api(path, { method: "POST", body });
16593
+ const r = await api(path2, { method: "POST", body });
16446
16594
  out(r, () => `${lavender("\u2713")} ${t(`${r.threads} conversation(s) ${action === "rm" ? "deleted" : "archived"}.`, `${r.threads} conversaci\xF3n(es) ${action === "rm" ? "borradas" : "archivadas"}.`)}`);
16447
16595
  }
16448
16596
  async function turnStats(v) {
16449
- const r = await api(`/api/v2/support/turn-stats?since=${enc17(v.since ?? "14d")}${v.all ? "&all=1" : ""}`);
16597
+ const r = await api(`/api/v2/support/turn-stats?since=${enc18(v.since ?? "14d")}${v.all ? "&all=1" : ""}`);
16450
16598
  out(
16451
16599
  r,
16452
16600
  () => [
@@ -16466,7 +16614,7 @@ async function fleetBoxes() {
16466
16614
  ${table(rows, ["template", "boxes", "on", "asleep", "ram", "vcpu", "ram on"])}`);
16467
16615
  }
16468
16616
  async function appAgentsOf(email, client) {
16469
- const r = await api(`/api/v2/support/app-agents?user=${enc17(email)}${client && client !== "all" ? `&client=${enc17(client)}` : ""}`);
16617
+ const r = await api(`/api/v2/support/app-agents?user=${enc18(email)}${client && client !== "all" ? `&client=${enc18(client)}` : ""}`);
16470
16618
  const rows = r.agentes.map((a) => ({ id: a.id, name: a.nombre, engine: a.motor ?? "", model: a.modelo ?? "", tone: a.tono ?? t("(by position)", "(por posici\xF3n)"), space: a.espacio?.tipo === "workspace" ? a.espacio.nombre : a.espacio?.tipo ?? "", last: a.ultimaActividad ? cdmx(a.ultimaActividad) : "" }));
16471
16619
  const phones = (r.devices ?? []).map((d) => `${d.platform} ${d.appVersion ?? ""}${d.appBuild ? ` (build ${d.appBuild})` : " (build ?)"} \xB7 ${t("seen", "visto")} ${cdmx(d.lastSeenAt)}`.replace(/\s+/g, " "));
16472
16620
  out(r, () => `${bold(r.user)} ${dim(r.client ? t(`as the ${r.client} app sees it`, `como lo ve la app de ${r.client}`) : t("all apps", "todas las apps"))}
@@ -16477,7 +16625,7 @@ ${dim(t("No phones registered for push.", "Sin tel\xE9fonos registrados para avi
16477
16625
  }
16478
16626
  async function pushLog(email, v) {
16479
16627
  if (!email) throw usage(t("Missing --user.", "Falta --user."), "ghosty push log --user ana@empresa.com [--since 2d]");
16480
- const r = await api(`/api/v2/support/push-log?user=${enc17(email)}&since=${enc17(v.since ?? "2d")}${v.limit ? `&limit=${enc17(v.limit)}` : ""}`);
16628
+ const r = await api(`/api/v2/support/push-log?user=${enc18(email)}&since=${enc18(v.since ?? "2d")}${v.limit ? `&limit=${enc18(v.limit)}` : ""}`);
16481
16629
  const rows = r.pushes.map((p) => ({ at: cdmx(p.at), kind: p.kind, to: p.platform, status: p.status === "sent" ? p.status : red(p.status), thread: p.threadKind ? `${p.threadId?.slice(0, 18)} (${p.threadKind})` : (p.threadId ?? "").slice(0, 24), title: (p.title ?? "").slice(0, 40), detail: p.detail ?? "" }));
16482
16630
  out(r, () => rows.length ? `${bold(r.user)}
16483
16631
  ${table(rows, ["at", "kind", "to", "status", "thread", "title", "detail"])}` : dim(t(`No pushes for ${r.user} in that range (logged since 2026-10-01).`, `Sin avisos para ${r.user} en ese rango (se registran desde el 1-oct).`)));
@@ -16515,24 +16663,24 @@ ${dim(t(`Try it on your phone: ghosty novedades test ${r.id}`, `Pru\xE9bala en t
16515
16663
  if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
16516
16664
  const b = await fields();
16517
16665
  if (!Object.keys(b).length) throw usage(t("Nothing to change.", "Nada que cambiar."), ex);
16518
- const r = await api(`${base}/${enc17(id)}`, { method: "PATCH", body: b });
16666
+ const r = await api(`${base}/${enc18(id)}`, { method: "PATCH", body: b });
16519
16667
  return out(r, () => `${lavender("\u2713")} ${line2(r)}`);
16520
16668
  }
16521
16669
  case "test": {
16522
16670
  if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
16523
- const r = await api(`${base}/${enc17(id)}`, { method: "POST", body: { push: "test" } });
16671
+ const r = await api(`${base}/${enc18(id)}`, { method: "POST", body: { push: "test" } });
16524
16672
  return out(r, () => `${lavender("\u2713")} ${t(`Sent to your phone (${r.enviados} device(s)).`, `Enviada a tu tel\xE9fono (${r.enviados} dispositivo(s)).`)}`);
16525
16673
  }
16526
16674
  case "send":
16527
16675
  case "schedule": {
16528
16676
  if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
16529
16677
  if (v.at !== void 0) {
16530
- const r2 = await api(`${base}/${enc17(id)}`, { method: "PATCH", body: { schedule: v.at || null } });
16678
+ const r2 = await api(`${base}/${enc18(id)}`, { method: "PATCH", body: { schedule: v.at || null } });
16531
16679
  return out(r2, () => `${lavender("\u2713")} ${line2(r2)}`);
16532
16680
  }
16533
16681
  requireYesIfHeadless(v.yes, `ghosty novedades send ${id}`);
16534
16682
  await confirm(t("Push this announcement to every recipient now?", "\xBFMandar ya el push de esta novedad a todos sus destinatarios?"), { yes: v.yes, retry: `ghosty novedades send ${id}` });
16535
- const r = await api(`${base}/${enc17(id)}`, { method: "POST", body: { push: "all" } });
16683
+ const r = await api(`${base}/${enc18(id)}`, { method: "POST", body: { push: "all" } });
16536
16684
  return out(r, () => `${lavender("\u2713")} ${t(`Pushed to ${r.enviados} device(s).`, `Enviada a ${r.enviados} dispositivo(s).`)}`);
16537
16685
  }
16538
16686
  case "rm":
@@ -16540,7 +16688,7 @@ ${dim(t(`Try it on your phone: ghosty novedades test ${r.id}`, `Pru\xE9bala en t
16540
16688
  if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
16541
16689
  requireYesIfHeadless(v.yes, `ghosty novedades rm ${id}`);
16542
16690
  await confirm(t(`Delete announcement ${id}?`, `\xBFBorrar la novedad ${id}?`), { yes: v.yes, retry: `ghosty novedades rm ${id}` });
16543
- return out(await api(`${base}/${enc17(id)}`, { method: "DELETE" }), () => `${lavender("\u2713")} ${t("Deleted.", "Borrada.")}`);
16691
+ return out(await api(`${base}/${enc18(id)}`, { method: "DELETE" }), () => `${lavender("\u2713")} ${t("Deleted.", "Borrada.")}`);
16544
16692
  }
16545
16693
  default:
16546
16694
  throw usage(t(`Unknown novedades subcommand: ${sub}.`, `Subcomando de novedades desconocido: ${sub}.`), ex);
@@ -16609,7 +16757,7 @@ ${dim(r.case.id)}`);
16609
16757
  }
16610
16758
  if (!id) throw usage(t("Missing the case id.", "Falta el id del caso."), "ghosty support ls");
16611
16759
  if (sub === "show") {
16612
- const r = v.all ? await api(`/api/v2/support/cases/${enc17(id)}`) : await api(`/api/v2/me/support/${enc17(id)}`);
16760
+ const r = v.all ? await api(`/api/v2/support/cases/${enc18(id)}`) : await api(`/api/v2/me/support/${enc18(id)}`);
16613
16761
  const c = r.case;
16614
16762
  return out(r, () => {
16615
16763
  const who2 = (a) => a === "staff" ? "Ghosty" : a === "agent" ? t("agent", "agente") : a === "customer" ? t("customer", "cliente") : a;
@@ -16630,18 +16778,18 @@ ${m.body}${m.attachments.length ? dim(`
16630
16778
  }
16631
16779
  if (sub === "reply") {
16632
16780
  if (!v.body && !v.resolve) throw usage(t("Missing --body.", "Falta --body."), 'ghosty support reply <id> --body "\u2026" [--resolve]');
16633
- const r = v.all || v.resolve ? await api(`/api/v2/support/cases/${enc17(id)}`, { method: "POST", body: { intent: "reply", body: v.body ?? "", resolve: Boolean(v.resolve) } }) : await api(`/api/v2/me/support/${enc17(id)}`, { method: "POST", body: { body: v.body } });
16781
+ const r = v.all || v.resolve ? await api(`/api/v2/support/cases/${enc18(id)}`, { method: "POST", body: { intent: "reply", body: v.body ?? "", resolve: Boolean(v.resolve) } }) : await api(`/api/v2/me/support/${enc18(id)}`, { method: "POST", body: { body: v.body } });
16634
16782
  return out(r, () => `${lavender("\u2713")} ${r.case.code} \xB7 ${STATUS(r.case.status)}`);
16635
16783
  }
16636
16784
  if (sub === "link") {
16637
16785
  const ref = v.ref;
16638
16786
  if (!ref) throw usage(t("Missing the issue or PR.", "Falta el issue o PR."), "ghosty support link <id> owner/repo#12 (or the issue/PR URL)");
16639
- const r = await api(`/api/v2/support/cases/${enc17(id)}`, { method: "POST", body: { intent: "link", ref } });
16787
+ const r = await api(`/api/v2/support/cases/${enc18(id)}`, { method: "POST", body: { intent: "link", ref } });
16640
16788
  return out(r, () => `${lavender("\u2713")} ${r.case.code} \u2192 ${bold(r.ref)} ${dim(`(${r.github.title})`)}${r.alsoWaiting ? `
16641
16789
  ${dim(t(`${r.alsoWaiting} more cases wait for the same fix: all get "fixed" when it closes.`, `Otros ${r.alsoWaiting} casos esperan el mismo arreglo: a todos les llega \xABya qued\xF3\xBB al cerrarse.`))}` : ""}`);
16642
16790
  }
16643
16791
  if (sub === "resolve" || sub === "reopen") {
16644
- const r = await api(`/api/v2/support/cases/${enc17(id)}`, { method: "POST", body: { intent: sub } });
16792
+ const r = await api(`/api/v2/support/cases/${enc18(id)}`, { method: "POST", body: { intent: sub } });
16645
16793
  return out(r, () => `${lavender("\u2713")} ${r.case.code} \xB7 ${STATUS(r.case.status)}`);
16646
16794
  }
16647
16795
  throw usage(t(`Unknown support subcommand: ${sub}.`, `Subcomando de support desconocido: ${sub}.`), "ghosty support ls | open | show | reply | resolve | reopen | link");
@@ -16823,6 +16971,12 @@ ${bold("Schedule turns")} ${dim("(each run lands in a conversation and notifies
16823
16971
  schedule rm <id> <schedule-id> Cancel a pending turn
16824
16972
  schedule add|ls|rm <id> \u2026 --teams WS#room[/thread] The same in a Teams room or thread: it answers there as if mentioned
16825
16973
 
16974
+ ${bold("Watch pages")} ${dim("(checked without a model turn; the agent only gets a turn when the page changed)")}
16975
+ watch ls <id> Watched pages: state, last change, conversation
16976
+ watch add <id> <url> Watch a page [--every 24h (\u2265 1h)] [--focus "heading or line"] [--instruction "what matters"] [--label L] [--days 90] [--conversation ID] [--title T]
16977
+ watch check <id> <watch-id> Check it in the next sweep [--dry-run: fetch and compare now, save nothing, notify nobody]
16978
+ watch rm|resume <id> <watch-id> Stop watching \xB7 resume a paused one (or renew an expired one) [--days N]
16979
+
16826
16980
  ${bold("Teams")}
16827
16981
  teams post WS#room[/thread] "msg" Write there as you; any @agent it mentions answers (no thread = new thread) [--wait [--timeout 600]: print their answers]
16828
16982
 
@@ -16833,6 +16987,7 @@ ${bold("Partners")} ${dim("(give an agent to each of YOUR customers \u2014 see:
16833
16987
  partner ls | enable | credentials | agent | tenants | audit | try
16834
16988
  image generate "prompt" An AI image for your businesses, charged to the space's balance [--ref URL (repeat, keeps the character)] [--style-ref URL] [--scene URL] [--text "-50 %"] [--direction D]\u2026 [--variants N] [--size 1K|2K|4K] [--aspect 16:9] [--tenant ID] [--out FILE]
16835
16989
  image edit URL "change" Edit an existing image: only that change, the rest stays [--ref URL] [--text T] [--variants N] [--size] [--aspect] [--tenant ID] [--out FILE]
16990
+ tts "text" Hear the house voice (Mexican Spanish, em_santa) say it, to a .wav [--voice em_santa|em_alex|ef_dora] [--out FILE]
16836
16991
 
16837
16992
  ${bold("Your account")}
16838
16993
  me files ls|get|export|upload|rm Files in your account [--q TEXT] [--kind image|video|audio|document|other] [--cursor C] \xB7 get <id> [--out FILE]
@@ -17031,6 +17186,12 @@ ${bold("Turnos programados")} ${dim("(cada corrida cae en una conversaci\xF3n y
17031
17186
  schedule rm <id> <schedule-id> Cancela un turno pendiente
17032
17187
  schedule add|ls|rm <id> \u2026 --teams ESP#room[/hilo] Lo mismo en un room o hilo de Teams: contesta ah\xED como si lo mencionaran
17033
17188
 
17189
+ ${bold("Vigilar p\xE1ginas")} ${dim("(se revisan sin turno del modelo; el agente s\xF3lo recibe turno cuando la p\xE1gina cambi\xF3)")}
17190
+ watch ls <id> P\xE1ginas vigiladas: estado, \xFAltimo cambio, conversaci\xF3n
17191
+ watch add <id> <url> Vigila una p\xE1gina [--every 24h (\u2265 1h)] [--focus "encabezado o l\xEDnea"] [--instruction "qu\xE9 importa"] [--label L] [--days 90] [--conversation ID] [--title T]
17192
+ watch check <id> <watch-id> Rev\xEDsala en el siguiente barrido [--dry-run: baja y compara ya, sin guardar ni avisar]
17193
+ watch rm|resume <id> <watch-id> Deja de vigilarla \xB7 reanuda una pausada (o renueva una vencida) [--days N]
17194
+
17034
17195
  ${bold("Teams")}
17035
17196
  teams post ESP#room[/hilo] "texto" Escribe ah\xED a tu nombre; el @agente que menciones contesta (sin hilo = hilo nuevo) [--wait [--timeout 600]: imprime sus respuestas]
17036
17197
 
@@ -17041,6 +17202,7 @@ ${bold("Partners")} ${dim("(dale un agente a cada uno de TUS clientes \u2014 ve
17041
17202
  partner ls | enable | credentials | agent | tenants | audit | try
17042
17203
  image generate "prompt" Una imagen con IA para tus negocios, cobrada del saldo del espacio [--ref URL (se repite, conserva al personaje)] [--style-ref URL] [--scene URL] [--text "-50 %"] [--direction D]\u2026 [--variants N] [--size 1K|2K|4K] [--aspect 16:9] [--tenant ID] [--out ARCHIVO]
17043
17204
  image edit URL "cambio" Edita una imagen que ya existe: s\xF3lo ese cambio, lo dem\xE1s se queda [--ref URL] [--text T] [--variants N] [--size] [--aspect] [--tenant ID] [--out ARCHIVO]
17205
+ tts "texto" Oye la voz de la casa (espa\xF1ol de M\xE9xico, em_santa) decirlo, en un .wav [--voice em_santa|em_alex|ef_dora] [--out ARCHIVO]
17044
17206
 
17045
17207
  ${bold("Tu cuenta")}
17046
17208
  me files ls|get|export|upload|rm Archivos de tu cuenta [--q TEXTO] [--kind image|video|audio|document|other] [--cursor C] \xB7 get <id> [--out ARCHIVO]
@@ -17066,7 +17228,7 @@ ${bold("Opciones globales")}
17066
17228
  ${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
17067
17229
  ${dim("Nota: `ghosty` puede chocar con otro binario en tu PATH (p. ej. la TUI de Ghosty). Rev\xEDsalo con `which -a ghosty`; corre `npx @ghostystudio/cli` para ir a la segura.")}
17068
17230
  ${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
17069
- var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "staff", "settings", "coupons", "ads", "audit", "factory", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "teams", "partner", "image", "me", "channels", "board", "boards", "access", "credentials", "connectors", "browser", "support", "docs", "help"];
17231
+ var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "staff", "settings", "coupons", "ads", "audit", "factory", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "watch", "teams", "partner", "image", "tts", "me", "channels", "board", "boards", "access", "credentials", "connectors", "browser", "support", "docs", "help"];
17070
17232
  var AGENTS_SUBS = ["ls", "create", "get", "show", "set", "export", "doctor", "docs", "share", "unshare", "shares", "teams", "rm", "restart", "demo", "embed", "box", "token", "memory", "transfer"];
17071
17233
  function unknown(what, input, known, helpCmd) {
17072
17234
  const s2 = suggest(input, known);
@@ -17131,6 +17293,8 @@ async function main() {
17131
17293
  at: { type: "string" },
17132
17294
  in: { type: "string" },
17133
17295
  every: { type: "string" },
17296
+ focus: { type: "string" },
17297
+ instruction: { type: "string" },
17134
17298
  until: { type: "string" },
17135
17299
  credit: { type: "string" },
17136
17300
  eligibility: { type: "string" },
@@ -17381,7 +17545,7 @@ async function main() {
17381
17545
  const tokens = splitTokens(pos, v);
17382
17546
  globals.token = tokens.session;
17383
17547
  globals.profile = v.profile;
17384
- if (v.version) return out({ version: "0.84.0" }, () => "0.84.0");
17548
+ if (v.version) return out({ version: "0.86.0" }, () => "0.86.0");
17385
17549
  for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
17386
17550
  const context = (await readConfig()).workspace;
17387
17551
  if (!explicitToken()?.startsWith("gat_")) {
@@ -17516,6 +17680,8 @@ async function main() {
17516
17680
  throw usage(t(`Unknown conversations subcommand: ${sub ?? "(none)"}.`, `Subcomando de conversations desconocido: ${sub ?? "(ninguno)"}.`), t("Try: ghosty conversations ls <agent-id> \xB7 ghosty conversations show <agent-id> <conversation-id>", "Prueba: ghosty conversations ls <agent-id> \xB7 ghosty conversations show <agent-id> <conversation-id>"));
17517
17681
  case "schedule":
17518
17682
  return scheduleCmd(sub, a1, a2, v);
17683
+ case "watch":
17684
+ return watchCmd(sub, a1, a2, v);
17519
17685
  case "teams":
17520
17686
  return teamsCmd(sub, a1, a2, v);
17521
17687
  case "browser":
@@ -17524,6 +17690,8 @@ async function main() {
17524
17690
  return partnerCmd(sub, a1, a2, { ...v, token: tokens.turn }, pos[4]);
17525
17691
  case "image":
17526
17692
  return imageCmd(sub, a1, a2, v);
17693
+ case "tts":
17694
+ return ttsCmd(sub, v);
17527
17695
  case "usage":
17528
17696
  if (v.images) return imageUsage(v);
17529
17697
  if (v.boxes && v.by === "template") return fleetBoxes();
@@ -17580,7 +17748,7 @@ async function main() {
17580
17748
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
17581
17749
  }
17582
17750
  }
17583
- var notice = updateNotice("0.84.0").catch(() => null);
17751
+ var notice = updateNotice("0.86.0").catch(() => null);
17584
17752
  main().then(
17585
17753
  async () => {
17586
17754
  const n = await notice;