@ghostystudio/cli 0.83.0 → 0.85.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.
- package/README.md +8 -0
- package/dist/index.js +439 -298
- 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
|
|
977
|
-
await mkdir(dirname(
|
|
978
|
-
await writeFile(
|
|
979
|
-
await chmod(
|
|
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(
|
|
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}${
|
|
1175
|
+
const req = httpsRequest(`${base}${path2}`, {
|
|
1176
1176
|
method: "POST",
|
|
1177
1177
|
timeout: timeoutMs,
|
|
1178
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
1178
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.85.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(
|
|
1204
|
-
const res = await request(
|
|
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(
|
|
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}${
|
|
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.
|
|
1220
|
+
"user-agent": `ghosty-cli/${"0.85.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(
|
|
1230
|
+
if (cfg) return request(path2, opts, true);
|
|
1231
1231
|
}
|
|
1232
1232
|
if (!res.ok) throw await toError(res);
|
|
1233
1233
|
return res;
|
|
@@ -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
|
|
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(
|
|
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(
|
|
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,7 +1303,7 @@ ${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(`${
|
|
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
1309
|
await writeFile8(file, bytes);
|
|
@@ -1312,27 +1312,27 @@ ${dim(`${t("Next", "Sigue")}: ghosty channels mteams zip ${id} ${b?.id ?? "<bot-
|
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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,
|
|
1418
|
-
const ctrl = callVisitor(key, node, visitor,
|
|
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,
|
|
1421
|
-
return visit_(key, ctrl, visitor,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
1439
|
-
const ck = visit_("key", node.key, visitor,
|
|
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,
|
|
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,
|
|
1466
|
-
const ctrl = await callVisitor(key, node, visitor,
|
|
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,
|
|
1469
|
-
return visitAsync_(key, ctrl, visitor,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
1487
|
-
const ck = await visitAsync_("key", node.key, visitor,
|
|
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,
|
|
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,
|
|
1519
|
+
function callVisitor(key, node, visitor, path2) {
|
|
1520
1520
|
if (typeof visitor === "function")
|
|
1521
|
-
return visitor(key, node,
|
|
1521
|
+
return visitor(key, node, path2);
|
|
1522
1522
|
if (identity.isMap(node))
|
|
1523
|
-
return visitor.Map?.(key, node,
|
|
1523
|
+
return visitor.Map?.(key, node, path2);
|
|
1524
1524
|
if (identity.isSeq(node))
|
|
1525
|
-
return visitor.Seq?.(key, node,
|
|
1525
|
+
return visitor.Seq?.(key, node, path2);
|
|
1526
1526
|
if (identity.isPair(node))
|
|
1527
|
-
return visitor.Pair?.(key, node,
|
|
1527
|
+
return visitor.Pair?.(key, node, path2);
|
|
1528
1528
|
if (identity.isScalar(node))
|
|
1529
|
-
return visitor.Scalar?.(key, node,
|
|
1529
|
+
return visitor.Scalar?.(key, node, path2);
|
|
1530
1530
|
if (identity.isAlias(node))
|
|
1531
|
-
return visitor.Alias?.(key, node,
|
|
1531
|
+
return visitor.Alias?.(key, node, path2);
|
|
1532
1532
|
return void 0;
|
|
1533
1533
|
}
|
|
1534
|
-
function replaceNode(key,
|
|
1535
|
-
const parent =
|
|
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,
|
|
2147
|
+
function collectionFromPath(schema, path2, value) {
|
|
2148
2148
|
let v = value;
|
|
2149
|
-
for (let i =
|
|
2150
|
-
const k =
|
|
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 = (
|
|
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(
|
|
2200
|
-
if (isEmptyPath(
|
|
2199
|
+
addIn(path2, value) {
|
|
2200
|
+
if (isEmptyPath(path2))
|
|
2201
2201
|
this.add(value);
|
|
2202
2202
|
else {
|
|
2203
|
-
const [key, ...rest] =
|
|
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(
|
|
2218
|
-
const [key, ...rest] =
|
|
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(
|
|
2233
|
-
const [key, ...rest] =
|
|
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(
|
|
2252
|
-
const [key, ...rest] =
|
|
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(
|
|
2263
|
-
const [key, ...rest] =
|
|
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(
|
|
4778
|
+
addIn(path2, value) {
|
|
4779
4779
|
if (assertCollection(this.contents))
|
|
4780
|
-
this.contents.addIn(
|
|
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(
|
|
4856
|
-
if (Collection.isEmptyPath(
|
|
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(
|
|
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(
|
|
4878
|
-
if (Collection.isEmptyPath(
|
|
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(
|
|
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(
|
|
4892
|
-
if (Collection.isEmptyPath(
|
|
4891
|
+
hasIn(path2) {
|
|
4892
|
+
if (Collection.isEmptyPath(path2))
|
|
4893
4893
|
return this.contents !== void 0;
|
|
4894
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
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(
|
|
4912
|
-
if (Collection.isEmptyPath(
|
|
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(
|
|
4915
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path2), value);
|
|
4916
4916
|
} else if (assertCollection(this.contents)) {
|
|
4917
|
-
this.contents.setIn(
|
|
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,
|
|
6878
|
+
visit.itemAtPath = (cst, path2) => {
|
|
6879
6879
|
let item = cst;
|
|
6880
|
-
for (const [field, index] of
|
|
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,
|
|
6890
|
-
const parent = visit.itemAtPath(cst,
|
|
6891
|
-
const field =
|
|
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(
|
|
6898
|
-
let ctrl = visitor(item,
|
|
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(
|
|
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,
|
|
6916
|
+
ctrl = ctrl(item, path2);
|
|
6917
6917
|
}
|
|
6918
6918
|
}
|
|
6919
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
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
|
|
9078
|
-
const plan = await api(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
9110
|
-
const plan = await api(
|
|
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(
|
|
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
|
|
9125
|
-
const plan = await api(
|
|
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(
|
|
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
|
|
9143
|
-
const plan = await api(
|
|
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(
|
|
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,
|
|
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 = (
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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:
|
|
10131
|
-
...(r.house ?? []).map((s2) => ({ slug: s2.slug, source: "ghosty", on: s2.enabled ? "on" : "off", 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
|
|
10421
|
-
const exists = await readFile3(
|
|
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(
|
|
10440
|
-
await writeFile3(
|
|
10441
|
-
return relative(process.cwd(),
|
|
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
|
|
10836
|
-
const prev = await stat(
|
|
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(
|
|
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
|
|
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(`${
|
|
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(
|
|
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(
|
|
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\
|
|
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(
|
|
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()}${
|
|
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.
|
|
11240
|
+
"user-agent": `ghosty-cli/${"0.85.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(
|
|
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(
|
|
11254
|
-
const res = await call(
|
|
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
|
|
11339
|
-
if (sub === "get") return out(await json(
|
|
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(
|
|
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(
|
|
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,6 +11571,85 @@ async function imageCmd(sub, a1, a2, v) {
|
|
|
11571
11571
|
);
|
|
11572
11572
|
}
|
|
11573
11573
|
|
|
11574
|
+
// src/commands/watch.ts
|
|
11575
|
+
init_api();
|
|
11576
|
+
init_util();
|
|
11577
|
+
init_i18n();
|
|
11578
|
+
var enc8 = encodeURIComponent;
|
|
11579
|
+
var path = (agent, rest = "") => `/api/v2/me/agents/${enc8(agent)}/watches${rest}`;
|
|
11580
|
+
var short2 = (s2, n = 50) => s2.length > n ? s2.slice(0, n - 1) + "\u2026" : s2;
|
|
11581
|
+
async function watchCmd(sub, agent, arg, v) {
|
|
11582
|
+
if (!agent) throw usage(t("Missing agent id.", "Falta el id del agente."), "ghosty watch ls <agent-id>");
|
|
11583
|
+
if (sub === "ls" || sub === "list" || !sub) {
|
|
11584
|
+
const r = await api(path(agent));
|
|
11585
|
+
const rows = r.watches.map((w) => ({
|
|
11586
|
+
id: w.id,
|
|
11587
|
+
page: short2(w.label ?? w.url),
|
|
11588
|
+
every: `${w.everyHours}h`,
|
|
11589
|
+
state: w.state + (w.failures ? ` (${w.failures}\u2717)` : ""),
|
|
11590
|
+
changed: w.lastChangedAt ?? "",
|
|
11591
|
+
checked: w.lastCheckedAt ?? "",
|
|
11592
|
+
conversation: w.sessionId
|
|
11593
|
+
}));
|
|
11594
|
+
return out(r, () => rows.length ? table(rows, ["id", "page", "every", "state", "changed", "checked", "conversation"]) : dim(t("No watched pages.", "No hay p\xE1ginas vigiladas.")));
|
|
11595
|
+
}
|
|
11596
|
+
if (sub === "add") {
|
|
11597
|
+
if (!arg) throw usage(t("Missing the URL.", "Falta la URL."), 'ghosty watch add <agent-id> https://competencia.mx/precios --every 24h --focus "Precios"');
|
|
11598
|
+
const body = {
|
|
11599
|
+
url: arg,
|
|
11600
|
+
...v.every ? { everyHours: minutesOf(v.every, "--every") / 60 } : {},
|
|
11601
|
+
...v.focus ? { focus: v.focus } : {},
|
|
11602
|
+
...v.instruction ? { instruction: v.instruction } : {},
|
|
11603
|
+
...v.label ? { label: v.label } : {},
|
|
11604
|
+
...v.days ? { days: Number(v.days) } : {}
|
|
11605
|
+
};
|
|
11606
|
+
let sid = v.conversation;
|
|
11607
|
+
const created = !sid;
|
|
11608
|
+
if (!sid) {
|
|
11609
|
+
sid = (await api(convPath(agent), { method: "POST", body: {} })).id;
|
|
11610
|
+
if (!globals.json) info(dim(t(`new conversation ${sid}`, `conversaci\xF3n nueva ${sid}`)));
|
|
11611
|
+
}
|
|
11612
|
+
let r;
|
|
11613
|
+
try {
|
|
11614
|
+
r = await api(path(agent), { body: { ...body, sessionId: sid } });
|
|
11615
|
+
} catch (e) {
|
|
11616
|
+
if (created) await api(convPath(agent, `/${enc8(sid)}`), { method: "DELETE" }).catch(() => {
|
|
11617
|
+
});
|
|
11618
|
+
throw e;
|
|
11619
|
+
}
|
|
11620
|
+
if (v.title && created) await api(convPath(agent, `/${enc8(sid)}`), { method: "PATCH", body: { title: v.title } });
|
|
11621
|
+
const w = r.watch;
|
|
11622
|
+
return out(
|
|
11623
|
+
r,
|
|
11624
|
+
() => `${lavender("\u2713")} ${t(`Watching ${w.url} every ${w.everyHours} h until ${w.expiresAt}`, `Vigilando ${w.url} cada ${w.everyHours} h hasta ${w.expiresAt}`)}
|
|
11625
|
+
${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`)}`
|
|
11626
|
+
);
|
|
11627
|
+
}
|
|
11628
|
+
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}`);
|
|
11629
|
+
if (sub === "rm" || sub === "remove") {
|
|
11630
|
+
const r = await api(path(agent, `/${enc8(arg)}`), { method: "DELETE" });
|
|
11631
|
+
return out(r, () => `${lavender("\u2713")} ${t("Stopped watching", "Ya no se vigila")} ${arg}`);
|
|
11632
|
+
}
|
|
11633
|
+
if (sub === "check") {
|
|
11634
|
+
const dry = !!v["dry-run"];
|
|
11635
|
+
const r = await api(path(agent, `/${enc8(arg)}`), { body: { action: "check", ...dry ? { dryRun: true } : {} } });
|
|
11636
|
+
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.")}`);
|
|
11637
|
+
const label = {
|
|
11638
|
+
baseline: t("no snapshot yet: the first check takes it", "a\xFAn sin foto: la primera revisi\xF3n la toma"),
|
|
11639
|
+
same: t("unchanged", "sin cambios"),
|
|
11640
|
+
changed: t("CHANGED (in dry-run nothing is saved or sent)", "CAMBI\xD3 (en dry-run no se guarda ni se avisa)"),
|
|
11641
|
+
failed: t("could not read the page", "no se pudo leer la p\xE1gina"),
|
|
11642
|
+
skipped: t("skipped (daily web cap)", "saltada (tope diario de web)")
|
|
11643
|
+
};
|
|
11644
|
+
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"));
|
|
11645
|
+
}
|
|
11646
|
+
if (sub === "resume") {
|
|
11647
|
+
const r = await api(path(agent, `/${enc8(arg)}`), { body: { action: "resume", ...v.days ? { days: Number(v.days) } : {} } });
|
|
11648
|
+
return out(r, () => `${lavender("\u2713")} ${t("Resumed", "Reanudada")} ${arg}`);
|
|
11649
|
+
}
|
|
11650
|
+
throw usage(t(`Unknown watch subcommand: ${sub}.`, `Subcomando de watch desconocido: ${sub}.`), "ghosty watch ls|add|rm|check|resume <agent-id> \u2026");
|
|
11651
|
+
}
|
|
11652
|
+
|
|
11574
11653
|
// src/commands/data.ts
|
|
11575
11654
|
init_api();
|
|
11576
11655
|
import { readdir as readdir2, readFile as readFile6, writeFile as writeFile7 } from "node:fs/promises";
|
|
@@ -11597,8 +11676,8 @@ var describeRowScope = (scope) => Object.entries(scope ?? {}).map(([t2, c]) => `
|
|
|
11597
11676
|
// src/commands/data.ts
|
|
11598
11677
|
init_util();
|
|
11599
11678
|
init_i18n();
|
|
11600
|
-
var
|
|
11601
|
-
var agentPath2 = (id, rest = "") => `/api/v2/agents/${
|
|
11679
|
+
var enc9 = encodeURIComponent;
|
|
11680
|
+
var agentPath2 = (id, rest = "") => `/api/v2/agents/${enc9(id)}${rest}`;
|
|
11602
11681
|
var need2 = (v, what, example) => {
|
|
11603
11682
|
if (!v) throw usage(t(`Missing ${what}.`, `Falta ${what}.`), `${t("Example", "Ejemplo")}: ${example}`);
|
|
11604
11683
|
return v;
|
|
@@ -11653,15 +11732,15 @@ async function dbsImport(id, db, v) {
|
|
|
11653
11732
|
const easybitsKey = process.env.EASYBITS_API_KEY;
|
|
11654
11733
|
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
11734
|
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
|
|
11657
|
-
const dry = await api(
|
|
11735
|
+
const path2 = agentPath2(id, `/dbs/${enc9(db)}/import`);
|
|
11736
|
+
const dry = await api(path2, { body: { ...body, dryRun: true } });
|
|
11658
11737
|
if (v["dry-run"]) return out({ ...dry, shrinking: shrinking(dry.plan).map((p) => p.table) }, () => `${dry.db} \u2190 easybits:${m[1]}
|
|
11659
11738
|
${planText(dry.plan)}${shrinkWarnings(dry.plan)}${importWarnings(dry.warnings)}${gsOnlyWarnings(dry.gsOnly)}
|
|
11660
11739
|
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
11661
11740
|
const destructive = dry.plan.some((p) => p.action === "replace" && p.targetRows > 0);
|
|
11662
11741
|
if (destructive) await confirm(`${planText(dry.plan)}${shrinkWarnings(dry.plan)}${importWarnings(dry.warnings)}${gsOnlyWarnings(dry.gsOnly)}
|
|
11663
11742
|
${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(
|
|
11743
|
+
const r = await api(path2, { body });
|
|
11665
11744
|
const img = r.rehosted ? `
|
|
11666
11745
|
${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
11746
|
${r.rehosted.failed.map((f) => ` ${f.url} ${dim(f.error)}`).join("\n")}` : ""}` : "";
|
|
@@ -11672,8 +11751,8 @@ async function dbsRehost(id, db, v) {
|
|
|
11672
11751
|
const ex = "ghosty dbs rehost <agent-id> <db> --table catalogo --column imagen_url [--dry-run]";
|
|
11673
11752
|
const table2 = need2(v.table, "--table", ex);
|
|
11674
11753
|
const column = need2(v.column, "--column", ex);
|
|
11675
|
-
const
|
|
11676
|
-
const dry = await api(
|
|
11754
|
+
const path2 = agentPath2(id, `/dbs/${enc9(db)}/rehost`);
|
|
11755
|
+
const dry = await api(path2, { body: { table: table2, column, dryRun: true } });
|
|
11677
11756
|
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
11757
|
if (v["dry-run"] || !dry.toCopy) return out(dry, () => `${head}
|
|
11679
11758
|
${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 +11760,7 @@ ${dim(v["dry-run"] ? t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3
|
|
|
11681
11760
|
const total = { copied: 0, reused: 0, failed: [], warnings: [] };
|
|
11682
11761
|
let after;
|
|
11683
11762
|
for (; ; ) {
|
|
11684
|
-
const r = await api(
|
|
11763
|
+
const r = await api(path2, { body: { table: table2, column, ...after ? { after } : {} } });
|
|
11685
11764
|
total.copied += r.copied ?? 0;
|
|
11686
11765
|
total.reused += r.reused ?? 0;
|
|
11687
11766
|
total.failed.push(...r.failed ?? []);
|
|
@@ -11702,7 +11781,7 @@ ${total.failed.map((f) => ` ${f.url} ${dim(f.error)}`).join("\n")}` : ""
|
|
|
11702
11781
|
);
|
|
11703
11782
|
}
|
|
11704
11783
|
async function dbsRename(id, db, name, dryRun) {
|
|
11705
|
-
const r = await api(agentPath2(id, `/dbs/${
|
|
11784
|
+
const r = await api(agentPath2(id, `/dbs/${enc9(db)}/rename`), { body: { name, dryRun } });
|
|
11706
11785
|
const refs = r.references.flatMap((x) => [
|
|
11707
11786
|
` ${bold(x.agent)} ${x.where}${x.derived ? dim(t(" (derived: follows its base)", " (derivado: sigue a su base)")) : ""}`,
|
|
11708
11787
|
...x.refs.slice(0, 3).map((l) => dim(` ${x.where === "prompt" || x.where.startsWith("skill:") ? `${l.line}: ` : ""}${l.text}`)),
|
|
@@ -11719,8 +11798,8 @@ ${dim(t("Dry run: nothing changed. Without --dry-run those mentions are rewritte
|
|
|
11719
11798
|
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
11799
|
}
|
|
11721
11800
|
async function dbsRm(id, db, v) {
|
|
11722
|
-
const
|
|
11723
|
-
const dry = await api(
|
|
11801
|
+
const path2 = agentPath2(id, `/dbs/${enc9(db)}/delete`);
|
|
11802
|
+
const dry = await api(path2, { body: { dryRun: true } });
|
|
11724
11803
|
const rows = dry.tables.reduce((n, x) => n + x.rows, 0);
|
|
11725
11804
|
const plan = [
|
|
11726
11805
|
`${bold(dry.name)}: ${t(`${dry.tables.length} table(s), ${rows} row(s)`, `${dry.tables.length} tabla(s), ${rows} fila(s)`)}`,
|
|
@@ -11730,7 +11809,7 @@ async function dbsRm(id, db, v) {
|
|
|
11730
11809
|
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
11731
11810
|
await confirm(`${plan}
|
|
11732
11811
|
${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(
|
|
11812
|
+
const r = await api(path2, { body: {} });
|
|
11734
11813
|
return out(r, () => `${lavender("\u2713")} ${t(`${r.name} deleted.`, `${r.name} borrada.`)}`);
|
|
11735
11814
|
}
|
|
11736
11815
|
async function dbsExport(id, db, v) {
|
|
@@ -11738,7 +11817,7 @@ async function dbsExport(id, db, v) {
|
|
|
11738
11817
|
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
11818
|
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
11819
|
const q = new URLSearchParams({ format, ...v.table ? { table: v.table } : {} });
|
|
11741
|
-
const res = await request(agentPath2(id, `/dbs/${
|
|
11820
|
+
const res = await request(agentPath2(id, `/dbs/${enc9(db)}/export?${q}`));
|
|
11742
11821
|
const suggested = /filename="([^"]+)"/.exec(res.headers.get("content-disposition") ?? "")?.[1] ?? (format === "csv" ? `${db}-${v.table}.csv` : `${db}.sqlite`);
|
|
11743
11822
|
const file = v.out ?? suggested;
|
|
11744
11823
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
@@ -11768,7 +11847,7 @@ ${dim(`${t("Delete one", "Borrar una")}: ghosty dbs rm ${id} <db> --dry-run`)}`
|
|
|
11768
11847
|
}
|
|
11769
11848
|
case "tables": {
|
|
11770
11849
|
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/${
|
|
11850
|
+
const r = await api(agentPath2(id, `/dbs/${enc9(db)}`));
|
|
11772
11851
|
const ext = new Set(r.externalWriteTables);
|
|
11773
11852
|
const own = r.externalRowScope ?? {};
|
|
11774
11853
|
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 +11866,7 @@ ${table(rows, ["table", "rows", "customer writes", "customer sees", "columns"])}
|
|
|
11787
11866
|
case "sql": {
|
|
11788
11867
|
db = need2(db, t("database (id or name)", "la base (id o nombre)"), 'ghosty dbs query <agent-id> <db> "SELECT \u2026"');
|
|
11789
11868
|
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/${
|
|
11869
|
+
const r = await api(agentPath2(id, `/dbs/${enc9(db)}/query`), { body: { sql, args: v.arg ?? [], write: v.write === true } }).catch((e) => {
|
|
11791
11870
|
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
11871
|
throw e;
|
|
11793
11872
|
});
|
|
@@ -11804,13 +11883,13 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
|
|
|
11804
11883
|
if (v.table) q.set("table", v.table);
|
|
11805
11884
|
if (v.since) q.set("since", v.since);
|
|
11806
11885
|
if (v.limit) q.set("limit", v.limit);
|
|
11807
|
-
const r = await api(agentPath2(id, `/dbs/${
|
|
11886
|
+
const r = await api(agentPath2(id, `/dbs/${enc9(db)}/origin${q.size ? `?${q}` : ""}`));
|
|
11808
11887
|
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
11888
|
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
11889
|
}
|
|
11811
11890
|
case "checkpoints": {
|
|
11812
11891
|
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/${
|
|
11892
|
+
const r = await api(agentPath2(id, `/dbs/${enc9(db)}/checkpoints`));
|
|
11814
11893
|
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
11894
|
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
11895
|
}
|
|
@@ -11818,7 +11897,7 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
|
|
|
11818
11897
|
db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs restore <agent-id> <db> <checkpoint-id> [--yes]");
|
|
11819
11898
|
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
11899
|
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/${
|
|
11900
|
+
const r = await api(agentPath2(id, `/dbs/${enc9(db)}/checkpoints`), { method: "POST", body: { checkpointId: ckpt } });
|
|
11822
11901
|
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
11902
|
}
|
|
11824
11903
|
case "grant": {
|
|
@@ -11833,7 +11912,7 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
|
|
|
11833
11912
|
...v["external-write"] !== void 0 ? { externalWriteTables: csv(v["external-write"]) } : {},
|
|
11834
11913
|
...rowScope !== void 0 ? { externalRowScope: rowScope } : {}
|
|
11835
11914
|
};
|
|
11836
|
-
const r = await api(agentPath2(id, `/dbs/${
|
|
11915
|
+
const r = await api(agentPath2(id, `/dbs/${enc9(db)}`), { method: "PUT", body });
|
|
11837
11916
|
const own = describeRowScope(r.externalRowScope);
|
|
11838
11917
|
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
11918
|
}
|
|
@@ -11862,7 +11941,7 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
|
|
|
11862
11941
|
}
|
|
11863
11942
|
case "revoke": {
|
|
11864
11943
|
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/${
|
|
11944
|
+
const r = await api(agentPath2(id, `/dbs/${enc9(db)}`), { method: "DELETE" });
|
|
11866
11945
|
return out(r, () => `${lavender("\u2713")} ${r.name} ${t("revoked.", "revocada.")} ${dim(`${t("Undo", "Deshacer")}: ghosty dbs grant ${id} ${r.name}`)}`);
|
|
11867
11946
|
}
|
|
11868
11947
|
default:
|
|
@@ -11870,9 +11949,9 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
|
|
|
11870
11949
|
}
|
|
11871
11950
|
}
|
|
11872
11951
|
async function docsDedupe(v) {
|
|
11873
|
-
const
|
|
11952
|
+
const path2 = "/api/v2/me/documents/dedupe";
|
|
11874
11953
|
const pair = v.keep || v.drop ? { keep: v.keep, drop: v.drop } : {};
|
|
11875
|
-
const plan = await api(
|
|
11954
|
+
const plan = await api(path2, { method: "POST", body: { ...pair, dryRun: true } });
|
|
11876
11955
|
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
11956
|
const text = [
|
|
11878
11957
|
...plan.merges.map((m) => `${t("keep", "queda")} ${name(m.keep)}
|
|
@@ -11886,7 +11965,7 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
|
11886
11965
|
requireYesIfHeadless(v.yes, retry);
|
|
11887
11966
|
await confirm(`${text}
|
|
11888
11967
|
${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(
|
|
11968
|
+
const r = await api(path2, { method: "POST", body: pair });
|
|
11890
11969
|
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
11970
|
}
|
|
11892
11971
|
async function agentDocsCmd(sub, id, docs, v) {
|
|
@@ -11916,7 +11995,7 @@ async function agentDocsCmd(sub, id, docs, v) {
|
|
|
11916
11995
|
wanted = docs.map((ref) => ({ ref, body }));
|
|
11917
11996
|
}
|
|
11918
11997
|
const done = [];
|
|
11919
|
-
for (const w of wanted) done.push(await api(agentPath2(id, `/documents/${
|
|
11998
|
+
for (const w of wanted) done.push(await api(agentPath2(id, `/documents/${enc9(w.ref)}`), { method: "PUT", body: w.body }));
|
|
11920
11999
|
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
12000
|
}
|
|
11922
12001
|
case "add": {
|
|
@@ -11934,7 +12013,7 @@ async function agentDocsCmd(sub, id, docs, v) {
|
|
|
11934
12013
|
}
|
|
11935
12014
|
case "revoke": {
|
|
11936
12015
|
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/${
|
|
12016
|
+
const r = await api(agentPath2(id, `/documents/${enc9(doc)}`), { method: "DELETE" });
|
|
11938
12017
|
return out(r, () => `${lavender("\u2713")} ${r.name} ${t("revoked.", "revocado.")} ${dim(`${t("Undo", "Deshacer")}: ghosty agents docs grant ${id} "${r.name}"`)}`);
|
|
11939
12018
|
}
|
|
11940
12019
|
case "replace": {
|
|
@@ -11945,13 +12024,13 @@ async function agentDocsCmd(sub, id, docs, v) {
|
|
|
11945
12024
|
...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64"), ...mimeField(v.file) },
|
|
11946
12025
|
...v.note ? { note: v.note } : {}
|
|
11947
12026
|
};
|
|
11948
|
-
const r = await api(agentPath2(id, `/documents/${
|
|
12027
|
+
const r = await api(agentPath2(id, `/documents/${enc9(doc)}/versions`), { method: "POST", body });
|
|
11949
12028
|
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
12029
|
}
|
|
11951
12030
|
case "history":
|
|
11952
12031
|
case "versions": {
|
|
11953
12032
|
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/${
|
|
12033
|
+
const r = await api(agentPath2(id, `/documents/${enc9(doc)}/versions`));
|
|
11955
12034
|
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
12035
|
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
12036
|
return out(r, () => `${r.document.name}
|
|
@@ -11963,7 +12042,7 @@ ${dim(t("Used by", "Lo usan"))}: ${used}` : ""}`);
|
|
|
11963
12042
|
doc = need2(doc, t("document (id or exact name)", "el documento (id o nombre exacto)"), ex);
|
|
11964
12043
|
const n = Number(need2(docs[1], t("version number", "el n\xFAmero de versi\xF3n"), ex));
|
|
11965
12044
|
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/${
|
|
12045
|
+
const r = await api(agentPath2(id, `/documents/${enc9(doc)}/versions/${n}/restore`), { method: "POST", body: v.note ? { note: v.note } : {} });
|
|
11967
12046
|
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
12047
|
}
|
|
11969
12048
|
default:
|
|
@@ -12001,7 +12080,7 @@ ${dim(tally)}` : dim(t(`Every customer message${v.since ? ` since ${v.since}` :
|
|
|
12001
12080
|
}
|
|
12002
12081
|
async function turnsJobs(id) {
|
|
12003
12082
|
id = need2(id, t("agent id", "el id del agente"), "ghosty turns jobs <agent-id>");
|
|
12004
|
-
const r = await api(`/api/v2/me/agents/${
|
|
12083
|
+
const r = await api(`/api/v2/me/agents/${enc9(id)}/tasks`);
|
|
12005
12084
|
const rows = jobRows(r.tasks);
|
|
12006
12085
|
out(r, () => rows.length ? `${table(rows, ["id", "kind", "what", "state", "minutes", "delivered", "conversation", "note"])}
|
|
12007
12086
|
${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 +12102,7 @@ async function turnsCancel(id, sid) {
|
|
|
12023
12102
|
const ex = "ghosty turns cancel <agent-id> <conversation-id>";
|
|
12024
12103
|
id = need2(id, t("agent id", "el id del agente"), ex);
|
|
12025
12104
|
sid = need2(sid, t("conversation id", "el id de la conversaci\xF3n"), ex);
|
|
12026
|
-
const r = await api(`/api/v2/me/agents/${
|
|
12105
|
+
const r = await api(`/api/v2/me/agents/${enc9(id)}/conversations/${enc9(sid)}/cancel`, { method: "POST", body: {} });
|
|
12027
12106
|
const b = r.background ?? { videos: 0, subagents: 0, wakes: 0 };
|
|
12028
12107
|
const bg = b.videos + b.subagents + b.wakes;
|
|
12029
12108
|
out(
|
|
@@ -12082,7 +12161,7 @@ async function turnsGrep(id, text, v) {
|
|
|
12082
12161
|
out(r, () => rows.length ? `${table(rows, ["at", "who", "turn", "state", "session", "text"])}
|
|
12083
12162
|
${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
12163
|
}
|
|
12085
|
-
function mimeField(
|
|
12164
|
+
function mimeField(path2) {
|
|
12086
12165
|
const types = {
|
|
12087
12166
|
pdf: "application/pdf",
|
|
12088
12167
|
png: "image/png",
|
|
@@ -12106,12 +12185,12 @@ function mimeField(path) {
|
|
|
12106
12185
|
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
12107
12186
|
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
12108
12187
|
};
|
|
12109
|
-
const mime = types[
|
|
12188
|
+
const mime = types[path2.split(".").pop()?.toLowerCase() ?? ""];
|
|
12110
12189
|
return mime ? { mime } : {};
|
|
12111
12190
|
}
|
|
12112
12191
|
async function turnShow(id, turnId, full = false) {
|
|
12113
12192
|
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/${
|
|
12193
|
+
const r = await api(agentPath2(id, `/turns/${enc9(turnId ?? "last")}`));
|
|
12115
12194
|
out(r, () => {
|
|
12116
12195
|
const tu = r.turn;
|
|
12117
12196
|
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 +12257,7 @@ async function dbsPhotosCmd(verb, id, db, v) {
|
|
|
12178
12257
|
const dir = need2(v.dir, "--dir", ex);
|
|
12179
12258
|
let column = v.column;
|
|
12180
12259
|
if (!column) {
|
|
12181
|
-
const r = await api(agentPath2(id, `/dbs/${
|
|
12260
|
+
const r = await api(agentPath2(id, `/dbs/${enc9(db)}`));
|
|
12182
12261
|
const tb = r.tables.find((x) => x.name === tableName);
|
|
12183
12262
|
if (!tb) throw usage(t(`${db} has no table ${tableName}.`, `${db} no tiene la tabla ${tableName}.`));
|
|
12184
12263
|
column = tb.columns.find((c) => c.name === "imagen_url")?.name;
|
|
@@ -12186,12 +12265,12 @@ async function dbsPhotosCmd(verb, id, db, v) {
|
|
|
12186
12265
|
}
|
|
12187
12266
|
const files = photoFiles(await readdir2(dir));
|
|
12188
12267
|
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
|
|
12268
|
+
const path2 = agentPath2(id, `/dbs/${enc9(db)}/photos`);
|
|
12190
12269
|
const results = [];
|
|
12191
12270
|
for (let i = 0; i < files.length; i += 8) {
|
|
12192
12271
|
const batch = files.slice(i, i + 8);
|
|
12193
12272
|
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(
|
|
12273
|
+
const r = await api(path2, { body: { table: tableName, keyColumn, column, items, ...v["dry-run"] ? { dryRun: true } : {} } });
|
|
12195
12274
|
results.push(...r.items);
|
|
12196
12275
|
if (!globals.json && files.length > 8) info(dim(` ${Math.min(i + 8, files.length)}/${files.length}`));
|
|
12197
12276
|
}
|
|
@@ -12463,7 +12542,7 @@ async function readMultiFileAuth(dir) {
|
|
|
12463
12542
|
init_api();
|
|
12464
12543
|
init_util();
|
|
12465
12544
|
init_i18n();
|
|
12466
|
-
var
|
|
12545
|
+
var enc11 = encodeURIComponent;
|
|
12467
12546
|
var digits = (s2) => s2.replace(/\D/g, "");
|
|
12468
12547
|
function find2(nums, ref) {
|
|
12469
12548
|
const d = digits(ref);
|
|
@@ -12489,9 +12568,9 @@ async function channelsCmd(kind, sub, id, ref, v) {
|
|
|
12489
12568
|
if (sub === "integrations") return integrationsCmd(id, v.email);
|
|
12490
12569
|
if (sub === "import" || sub === "status" || sub === "pause" || sub === "pair") return waCmd2(sub, id, v);
|
|
12491
12570
|
if (sub === "enable" && !v["dry-run"]) requireYesIfHeadless(v.yes, `ghosty channels whatsapp enable ${id} ${ref ?? "<number>"}`);
|
|
12492
|
-
const
|
|
12571
|
+
const path2 = `/api/v2/agents/${enc11(id)}/whatsapp`;
|
|
12493
12572
|
const listing = !sub || sub === "ls" || sub === "list";
|
|
12494
|
-
const cur = await api(listing ? `${
|
|
12573
|
+
const cur = await api(listing ? `${path2}?formmy=1` : path2);
|
|
12495
12574
|
if (listing) {
|
|
12496
12575
|
const fwd = (n2) => !n2.formmy ? dim("\u2014") : "error" in n2.formmy ? dim("?") : n2.formmy.forwards ? t("yes", "s\xED") : lavender("NO");
|
|
12497
12576
|
const rows = cur.numbers.map((n2) => ({
|
|
@@ -12515,7 +12594,7 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
|
12515
12594
|
if (on) await confirm(`${bold(plan)}
|
|
12516
12595
|
${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
12596
|
const wanted = cur.numbers.filter((x) => x.id === n.id ? on : x.mine).map((x) => x.id);
|
|
12518
|
-
const r = await api(
|
|
12597
|
+
const r = await api(path2, { method: "PUT", body: { numbers: wanted } });
|
|
12519
12598
|
return out(r, () => `${lavender("\u2713")} ${plan.slice(2)}`);
|
|
12520
12599
|
}
|
|
12521
12600
|
function findGroup(groups, ref) {
|
|
@@ -12533,18 +12612,18 @@ async function groupsCmd(args, v) {
|
|
|
12533
12612
|
if (rawSub === "disable") v = { ...v, off: true };
|
|
12534
12613
|
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
12614
|
if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), `${t("Example", "Ejemplo")}: ${ex}`);
|
|
12536
|
-
const
|
|
12615
|
+
const path2 = `/api/v2/agents/${enc11(id)}/wa/groups`;
|
|
12537
12616
|
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
12617
|
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
12618
|
if (!sub || sub === "ls" || sub === "list") {
|
|
12540
|
-
const r2 = await api(`${
|
|
12619
|
+
const r2 = await api(`${path2}${v.cached ? "?live=0" : ""}`);
|
|
12541
12620
|
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
12621
|
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
12622
|
}
|
|
12544
12623
|
if (sub === "create") {
|
|
12545
12624
|
const name2 = rest.join(" ").trim();
|
|
12546
12625
|
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(
|
|
12626
|
+
const r2 = await api(path2, { method: "POST", body: { create: name2, role: v.role ?? "equipo" } });
|
|
12548
12627
|
return out(r2, () => `${lavender("\u2713")} ${t("Created", "Creado")} ${bold(name2)} (${v.role ?? "equipo"}, on)
|
|
12549
12628
|
${t("invite", "invitaci\xF3n")}: ${r2.inviteUrl}`);
|
|
12550
12629
|
}
|
|
@@ -12552,15 +12631,15 @@ async function groupsCmd(args, v) {
|
|
|
12552
12631
|
const ref2 = rest.join(" ").trim();
|
|
12553
12632
|
const text = v.text?.trim();
|
|
12554
12633
|
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(`${
|
|
12556
|
-
const r2 = await api(`/api/v2/agents/${
|
|
12634
|
+
const g2 = findGroup((await api(`${path2}?live=0`)).groups, ref2);
|
|
12635
|
+
const r2 = await api(`/api/v2/agents/${enc11(id)}/wa/inject`, { method: "POST", body: { jid: g2.jid, text, ...v.sender ? { senderName: v.sender } : {} } });
|
|
12557
12636
|
return out(r2, () => `${lavender("\u2713")} ${t(`Injected into ${g2.name}: the agent answers there.`, `Reinyectado en ${g2.name}: el agente contesta ah\xED.`)}`);
|
|
12558
12637
|
}
|
|
12559
12638
|
if (sub !== "set") throw usage(t(`Unknown groups subcommand: ${sub}.`, `Subcomando de groups desconocido: ${sub}.`), ex);
|
|
12560
12639
|
const ref = rest.join(" ").trim();
|
|
12561
12640
|
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
12641
|
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(`${
|
|
12642
|
+
const cur = await api(`${path2}?live=0`);
|
|
12564
12643
|
const g = findGroup(cur.groups, ref);
|
|
12565
12644
|
const enabled = v.on ? true : v.off ? false : g.enabled;
|
|
12566
12645
|
const role = v.role ?? g.role;
|
|
@@ -12573,11 +12652,11 @@ async function groupsCmd(args, v) {
|
|
|
12573
12652
|
${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
12574
12653
|
if (enabled && !g.enabled) await confirm(`${bold(plan)}
|
|
12575
12654
|
${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(
|
|
12655
|
+
const r = await api(path2, { method: "POST", body: { jid: g.jid, enabled, role, ...wake ? { wake } : {}, ...name ? { name } : {} } });
|
|
12577
12656
|
const now = r.groups.find((x) => x.jid === g.jid);
|
|
12578
12657
|
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
12658
|
}
|
|
12580
|
-
var EB_EXPORT = (agentId) => `${(process.env.EASYBITS_URL ?? "https://www.easybits.cloud").replace(/\/+$/, "")}/api/v2/fleet-agents/${
|
|
12659
|
+
var EB_EXPORT = (agentId) => `${(process.env.EASYBITS_URL ?? "https://www.easybits.cloud").replace(/\/+$/, "")}/api/v2/fleet-agents/${enc11(agentId)}/baileys/export`;
|
|
12581
12660
|
async function easybitsSession(agentId, keys) {
|
|
12582
12661
|
const key = process.env.EASYBITS_API_KEY;
|
|
12583
12662
|
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 +12683,7 @@ function originText(o) {
|
|
|
12604
12683
|
}
|
|
12605
12684
|
async function waCmd2(sub, id, v) {
|
|
12606
12685
|
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/${
|
|
12686
|
+
const base = `/api/v2/agents/${enc11(id)}/wa`;
|
|
12608
12687
|
if (sub === "status") {
|
|
12609
12688
|
const s2 = await api(base);
|
|
12610
12689
|
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 +12747,19 @@ async function linkCmd(sub, id, v) {
|
|
|
12668
12747
|
const board = v.board === void 0 ? void 0 : /^(agent|agente)$/i.test(v.board) ? null : v.board;
|
|
12669
12748
|
const boardFlag = v.board !== void 0 ? ` --board ${JSON.stringify(v.board)}` : "";
|
|
12670
12749
|
if (sub === "link" && (adsOnly !== void 0 || board !== void 0) && v.answer === void 0) {
|
|
12671
|
-
const
|
|
12672
|
-
const r2 = await api(
|
|
12750
|
+
const path3 = `/api/v2/agents/${enc11(id)}/whatsapp/link`;
|
|
12751
|
+
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
12752
|
return out(r2, () => `${r2.plan.map((x) => serverText(x)).join("\n")}${v["dry-run"] ? `
|
|
12674
12753
|
${dim(applyHint(`ghosty channels whatsapp link ${id} --integration ${v.integration}${v["ads-only"] ? ` --ads-only ${v["ads-only"]}` : ""}${boardFlag}`))}` : ""}`);
|
|
12675
12754
|
}
|
|
12676
12755
|
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
|
|
12756
|
+
const path2 = `/api/v2/agents/${enc11(id)}/whatsapp/link`;
|
|
12678
12757
|
if (v["partner-agent"] && !v["partner-tenant"]) throw usage(t("--partner-agent needs --partner-tenant <externalId>.", "--partner-agent necesita --partner-tenant <externalId>."));
|
|
12679
12758
|
const partner = v["partner-tenant"] ? { partnerExternalId: v["partner-tenant"], ...v["partner-agent"] ? { partnerAgentSlug: v["partner-agent"] } : {} } : {};
|
|
12680
12759
|
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
12760
|
const partnerFlags = v["partner-tenant"] ? ` --partner-tenant ${v["partner-tenant"]}${v["partner-agent"] ? ` --partner-agent ${v["partner-agent"]}` : ""}` : "";
|
|
12682
12761
|
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(
|
|
12762
|
+
const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
|
|
12684
12763
|
const text = `${plan.plan.map((x) => serverText(x)).join("\n")}
|
|
12685
12764
|
${dim(`Formmy: ${plan.formmy.before.url ?? "null"} \u2192 ${plan.formmy.after.url ?? "null"} (${t("as", "como")} ${plan.formmy.by})`)}`;
|
|
12686
12765
|
if (v["dry-run"]) return out(plan, () => `${text}
|
|
@@ -12689,7 +12768,7 @@ ${dim(applyHint(cmd))}`);
|
|
|
12689
12768
|
const digits4 = (plan.number.phoneNumberId ?? v.integration).slice(-4);
|
|
12690
12769
|
await confirm(`${bold(text)}
|
|
12691
12770
|
${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(
|
|
12771
|
+
const r = await api(path2, { method: "POST", body });
|
|
12693
12772
|
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
12773
|
}
|
|
12695
12774
|
function bareMediaFlag(argv) {
|
|
@@ -12721,7 +12800,7 @@ async function pageWithRetry(fetchPage, where, waitMs = (n) => 5e3 * n, tries =
|
|
|
12721
12800
|
}
|
|
12722
12801
|
var lastCell = (l) => l ? `${l.agentName ?? l.agentId} ${dim(l.at.slice(0, 16).replace("T", " "))}` : dim("\u2014");
|
|
12723
12802
|
async function linksCmd(id, kind, orphans) {
|
|
12724
|
-
const r = await api(`/api/v2/agents/${
|
|
12803
|
+
const r = await api(`/api/v2/agents/${enc11(id)}/channels/links?kind=${kind}${orphans ? "&orphans=1" : ""}`);
|
|
12725
12804
|
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
12805
|
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
12806
|
const first = r.links.find((l) => l.nobody && l.lastAnswer);
|
|
@@ -12733,7 +12812,7 @@ ${dim(`${t("Give it back", "Regresarlo")}: ${kind === "waba" ? `ghosty channels
|
|
|
12733
12812
|
);
|
|
12734
12813
|
}
|
|
12735
12814
|
async function integrationsCmd(id, email) {
|
|
12736
|
-
const r = await api(`/api/v2/agents/${
|
|
12815
|
+
const r = await api(`/api/v2/agents/${enc11(id)}/whatsapp/integrations${email ? `?email=${enc11(email)}` : ""}`);
|
|
12737
12816
|
const who2 = { gs: "gs", easybits: "EasyBits", droplet: "droplet", formmy: "Formmy", other: t("other", "otro") };
|
|
12738
12817
|
const rows = r.integrations.map((i) => ({
|
|
12739
12818
|
number: i.phoneNumber ? `+${i.phoneNumber}` : dim("?"),
|
|
@@ -12754,11 +12833,11 @@ async function historyCmd(id, v) {
|
|
|
12754
12833
|
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
12834
|
const days = Number(v.days ?? 7);
|
|
12756
12835
|
if (!Number.isInteger(days) || days < 1 || days > 30) throw usage(t("--days takes 1 to 30.", "--days acepta de 1 a 30."));
|
|
12757
|
-
const
|
|
12836
|
+
const path2 = `/api/v2/agents/${enc11(id)}/whatsapp/history`;
|
|
12758
12837
|
const media = v.media !== void 0;
|
|
12759
12838
|
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
12839
|
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(
|
|
12840
|
+
const c = await api(path2, { method: "POST", body: { integrationId: v.integration, days, dryRun: true, media, ...v.list ? { list: true } : {}, ...filters } });
|
|
12762
12841
|
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
12842
|
if (media) {
|
|
12764
12843
|
const missing = c.mediaMissing ?? 0;
|
|
@@ -12773,7 +12852,7 @@ ${dim(v["dry-run"] ? t("Dry run: nothing copied.", "Simulacro: no se copi\xF3 na
|
|
|
12773
12852
|
let done2 = 0;
|
|
12774
12853
|
while (cursor2 !== null) {
|
|
12775
12854
|
const at = cursor2;
|
|
12776
|
-
const p = await pageWithRetry(() => api(
|
|
12855
|
+
const p = await pageWithRetry(() => api(path2, { method: "POST", body: { integrationId: v.integration, days, cursor: at, media: true } }), `${done2}/${c.conversations}`);
|
|
12777
12856
|
sum2.mediaCopied += p.mediaCopied ?? 0;
|
|
12778
12857
|
sum2.mediaFailed += p.mediaFailed ?? 0;
|
|
12779
12858
|
done2 += p.page;
|
|
@@ -12805,7 +12884,7 @@ ${t(`Cards appear on the team's board (no "new lead" alerts).`, "Las tarjetas ap
|
|
|
12805
12884
|
let done = 0;
|
|
12806
12885
|
while (cursor !== null) {
|
|
12807
12886
|
const at = cursor;
|
|
12808
|
-
const p = await pageWithRetry(() => api(
|
|
12887
|
+
const p = await pageWithRetry(() => api(path2, { method: "POST", body: { integrationId: v.integration, days, cursor: at, ...filters } }), `${done}/${c.conversations}`);
|
|
12809
12888
|
for (const k of Object.keys(sum)) sum[k] += p[k] ?? 0;
|
|
12810
12889
|
done += p.page;
|
|
12811
12890
|
if (!globals.json) info(dim(` ${done}/${p.total} ${t("conversations", "conversaciones")}`));
|
|
@@ -12955,8 +13034,8 @@ async function boardCmd(sub, id, v, ref) {
|
|
|
12955
13034
|
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
13035
|
const retry = `ghosty board archive ${id} ${Object.entries(filter).map(([k, x]) => `--${k} ${JSON.stringify(x)}`).join(" ")}`;
|
|
12957
13036
|
if (!v["dry-run"]) requireYesIfHeadless(v.yes, retry);
|
|
12958
|
-
const
|
|
12959
|
-
const plan = await api(
|
|
13037
|
+
const path2 = `/api/v2/agents/${encodeURIComponent(id)}/board/archive`;
|
|
13038
|
+
const plan = await api(path2, { body: { ...filter, dryRun: true } });
|
|
12960
13039
|
const sample = () => table(plan.sample.map((c) => ({ name: c.name ?? "", phone: c.phone, column: c.estado_label ?? "" })), ["name", "phone", "column"]);
|
|
12961
13040
|
if (v["dry-run"] || plan.matched === 0) {
|
|
12962
13041
|
return out(plan, () => plan.matched ? `${sample()}
|
|
@@ -12964,7 +13043,7 @@ ${dim(t(`Would archive ${plan.matched} card(s) on \xAB${plan.board}\xBB. Dry run
|
|
|
12964
13043
|
}
|
|
12965
13044
|
await confirm(`${sample()}
|
|
12966
13045
|
${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(
|
|
13046
|
+
const r = await api(path2, { body: filter });
|
|
12968
13047
|
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
13048
|
}
|
|
12970
13049
|
|
|
@@ -12972,7 +13051,7 @@ ${t(`Archive ${plan.matched} card(s) on \xAB${plan.board}\xBB? (reversible: a ne
|
|
|
12972
13051
|
init_api();
|
|
12973
13052
|
init_util();
|
|
12974
13053
|
init_i18n();
|
|
12975
|
-
var
|
|
13054
|
+
var enc12 = encodeURIComponent;
|
|
12976
13055
|
async function boardsCmd(sub, a1, a2, v, context) {
|
|
12977
13056
|
if (context && sub !== "assign") {
|
|
12978
13057
|
if ((sub === "ls" || sub === "list" || !sub || sub === "move-cards") && !a1) a1 = context;
|
|
@@ -12983,13 +13062,13 @@ async function boardsCmd(sub, a1, a2, v, context) {
|
|
|
12983
13062
|
case "ls":
|
|
12984
13063
|
case "list": {
|
|
12985
13064
|
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/${
|
|
13065
|
+
const r = await api(`/api/v2/workspaces/${enc12(a1)}/boards`);
|
|
12987
13066
|
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
13067
|
return out(r, () => table(rows, ["name", "cards", "agents", "columns", "id"]));
|
|
12989
13068
|
}
|
|
12990
13069
|
case "create": {
|
|
12991
13070
|
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/${
|
|
13071
|
+
const r = await api(`/api/v2/workspaces/${enc12(a1)}/boards`, { method: "POST", body: { name: a2, ...v["columns-from"] ? { columnsFrom: v["columns-from"] } : {} } });
|
|
12993
13072
|
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
13073
|
${lavender(`\u26A0\uFE0F ${serverText(r.warning)}`)}` : ""}`);
|
|
12995
13074
|
}
|
|
@@ -12997,22 +13076,22 @@ ${lavender(`\u26A0\uFE0F ${serverText(r.warning)}`)}` : ""}`);
|
|
|
12997
13076
|
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
13077
|
requireYesIfHeadless(v.yes, `ghosty boards rm ${a1} ${JSON.stringify(a2)}`);
|
|
12999
13078
|
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/${
|
|
13079
|
+
const r = await api(`/api/v2/workspaces/${enc12(a1)}/boards`, { method: "DELETE", body: { board: a2 } });
|
|
13001
13080
|
return out(r, () => `${lavender("\u2713")} ${t(`Board \xAB${r.deleted}\xBB deleted.`, `Tablero \xAB${r.deleted}\xBB borrado.`)}`);
|
|
13002
13081
|
}
|
|
13003
13082
|
case "assign": {
|
|
13004
13083
|
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/${
|
|
13084
|
+
const r = await api(`/api/v2/agents/${enc12(a1)}`, { method: "PATCH", body: { board: a2 } });
|
|
13006
13085
|
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
13086
|
}
|
|
13008
13087
|
case "move-cards": {
|
|
13009
13088
|
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
13089
|
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
|
|
13090
|
+
const path2 = `/api/v2/workspaces/${enc12(a1)}/boards/move-cards`;
|
|
13012
13091
|
const body = { from: v.from, to: v.to, ...v.integration ? { integration: v.integration } : {}, ...v.phone ? { phone: v.phone } : {}, ...v.all ? { all: true } : {} };
|
|
13013
13092
|
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
13093
|
if (!v["dry-run"]) requireYesIfHeadless(v.yes, retry);
|
|
13015
|
-
const plan = await api(
|
|
13094
|
+
const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
|
|
13016
13095
|
const sample = table(plan.sample.map((c) => ({ folio: c.folio ? `#${c.folio}` : "", name: c.name ?? "", phone: c.phone })), ["folio", "name", "phone"]);
|
|
13017
13096
|
const text = `${sample}
|
|
13018
13097
|
${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 +13099,7 @@ ${t(`${plan.moved} of ${plan.matched} card(s)`, `${plan.moved} de ${plan.matched
|
|
|
13020
13099
|
${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
13021
13100
|
await confirm(`${text}
|
|
13022
13101
|
${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(
|
|
13102
|
+
const r = await api(path2, { method: "POST", body });
|
|
13024
13103
|
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
13104
|
}
|
|
13026
13105
|
// Quién ve un tablero: lista «sólo estas personas» o todo el espacio (feedback del revisor de Meta).
|
|
@@ -13031,7 +13110,7 @@ ${t("Move them with their threads and orders? (they get a new folio there)", "\x
|
|
|
13031
13110
|
const add = split(v.add);
|
|
13032
13111
|
const remove = split(v.remove);
|
|
13033
13112
|
const edit = add.length || remove.length || v.everyone;
|
|
13034
|
-
const r = edit ? await api(`/api/v2/workspaces/${
|
|
13113
|
+
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
13114
|
return out(r, () => {
|
|
13036
13115
|
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
13116
|
const people = r.everyone ? "" : `
|
|
@@ -13332,6 +13411,18 @@ var HELPS_ES = {
|
|
|
13332
13411
|
replace: [['"what to do"', '"qu\xE9 hacer"'], ["# answers in that thread as if mentioned", "# contesta en ese hilo como si lo mencionaran"]],
|
|
13333
13412
|
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
13413
|
},
|
|
13414
|
+
watch: {
|
|
13415
|
+
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.",
|
|
13416
|
+
replace: [['"heading or line"', '"encabezado o l\xEDnea"'], ['"what matters"', '"qu\xE9 importa"']],
|
|
13417
|
+
flags: [
|
|
13418
|
+
["--every 24h", "cada cu\xE1nto revisarla: 1h m\xEDnimo, 7d m\xE1ximo (default 24h)"],
|
|
13419
|
+
["--focus TEXT", "compara s\xF3lo esa parte: el texto de un encabezado (hasta el siguiente de su nivel) o de una l\xEDnea"],
|
|
13420
|
+
["--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"],
|
|
13421
|
+
["--days N", "add: vence a los N d\xEDas (default 90, m\xE1x. 365) \xB7 resume: renueva una vencida por N d\xEDas"],
|
|
13422
|
+
["--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)"]
|
|
13423
|
+
],
|
|
13424
|
+
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."]
|
|
13425
|
+
},
|
|
13335
13426
|
teams: {
|
|
13336
13427
|
summary: "Escribe en un room o hilo de Teams a tu nombre; el @agente que menciones contesta, como en el composer.",
|
|
13337
13428
|
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?"]],
|
|
@@ -13367,7 +13458,7 @@ var HELPS_ES = {
|
|
|
13367
13458
|
notes: [
|
|
13368
13459
|
"Para cambiar una imagen que ya existe usa edit (no generate): sale la misma imagen con s\xF3lo ese cambio.",
|
|
13369
13460
|
`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\
|
|
13461
|
+
"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
13462
|
]
|
|
13372
13463
|
},
|
|
13373
13464
|
me: {
|
|
@@ -14142,6 +14233,20 @@ var HELPS = {
|
|
|
14142
14233
|
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
14234
|
learn: `${REF}#programar-turnos`
|
|
14144
14235
|
},
|
|
14236
|
+
watch: {
|
|
14237
|
+
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.",
|
|
14238
|
+
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]"],
|
|
14239
|
+
flags: [
|
|
14240
|
+
["--every 24h", "how often to check it: 1h minimum, 7d maximum (default 24h)"],
|
|
14241
|
+
["--focus TEXT", "compare only that part: the text of a heading (until the next one of its level) or of a line"],
|
|
14242
|
+
["--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"],
|
|
14243
|
+
["--days N", "add: expires after N days (default 90, max 365) \xB7 resume: renew an expired one for N days"],
|
|
14244
|
+
["--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)"]
|
|
14245
|
+
],
|
|
14246
|
+
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"],
|
|
14247
|
+
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."],
|
|
14248
|
+
learn: `${REF}#vigilar-paginas`
|
|
14249
|
+
},
|
|
14145
14250
|
teams: {
|
|
14146
14251
|
summary: "Write in a Teams room or thread as yourself; any @agent you mention answers, like in the composer.",
|
|
14147
14252
|
usage: ['ghosty teams post <workspace>#<room> "message"', 'ghosty teams post <workspace>#<room>/<thread-id> "message" [--wait [--timeout 600]]'],
|
|
@@ -14842,6 +14947,7 @@ function agentArgIndex(pos) {
|
|
|
14842
14947
|
case "board":
|
|
14843
14948
|
case "conversations":
|
|
14844
14949
|
case "schedule":
|
|
14950
|
+
case "watch":
|
|
14845
14951
|
return 2;
|
|
14846
14952
|
case "try":
|
|
14847
14953
|
case "chat":
|
|
@@ -15004,10 +15110,10 @@ import { mkdir as mkdir7, readdir as readdir4, readFile as readFile11, stat as s
|
|
|
15004
15110
|
import { join as join9, resolve as resolve2 } from "node:path";
|
|
15005
15111
|
init_util();
|
|
15006
15112
|
init_i18n();
|
|
15007
|
-
var
|
|
15113
|
+
var enc13 = encodeURIComponent;
|
|
15008
15114
|
async function agentsExport(id, v) {
|
|
15009
15115
|
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/${
|
|
15116
|
+
const yaml = await (await request(`/api/v2/agents/${enc13(id)}/export`)).text();
|
|
15011
15117
|
if (!v.out) {
|
|
15012
15118
|
if (globals.json) return out((0, import_yaml.parse)(yaml));
|
|
15013
15119
|
return void process.stdout.write(yaml);
|
|
@@ -15017,7 +15123,7 @@ async function agentsExport(id, v) {
|
|
|
15017
15123
|
await writeFile11(join9(dir, "agent.yaml"), yaml);
|
|
15018
15124
|
const own = (0, import_yaml.parse)(yaml)["x-ghosty"]?.skills?.own ?? [];
|
|
15019
15125
|
for (const slug of own) {
|
|
15020
|
-
const sk = await api(`/api/v2/agents/${
|
|
15126
|
+
const sk = await api(`/api/v2/agents/${enc13(id)}/skills/${enc13(slug)}`);
|
|
15021
15127
|
await writeSkill(join9(dir, "skills", slug), sk);
|
|
15022
15128
|
}
|
|
15023
15129
|
out({ saved: dir, skills: own }, () => `${lavender("\u2713")} ${dir}/agent.yaml${own.length ? ` + skills/ (${own.join(", ")})` : ""}
|
|
@@ -15076,8 +15182,8 @@ ${dim(t("Then every setting of the file is applied. Dry run: nothing created.",
|
|
|
15076
15182
|
}
|
|
15077
15183
|
const spec = JSON.stringify(v.create ? { ...file, title: void 0 } : file);
|
|
15078
15184
|
const body = { spec, ...files ? { files } : {}, ...v.prune ? { prune: true } : {}, ...v["no-notify"] ? { notify: false } : {} };
|
|
15079
|
-
const
|
|
15080
|
-
const plan = await api(
|
|
15185
|
+
const path2 = `/api/v2/agents/${enc13(id)}/apply`;
|
|
15186
|
+
const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
|
|
15081
15187
|
const actionable = plan.plan.filter((c) => c.op !== "!");
|
|
15082
15188
|
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
15189
|
if (v["dry-run"] || !actionable.length) return out(plan, () => `${bold(id)}
|
|
@@ -15087,7 +15193,7 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}` : "
|
|
|
15087
15193
|
if (!v.create) requireYesIfHeadless(v.yes, retry);
|
|
15088
15194
|
if (!v.create) await confirm(`${text2}
|
|
15089
15195
|
${t(`Apply ${actionable.length} change(s) to ${id}?`, `\xBFAplicar ${actionable.length} cambio(s) a ${id}?`)}`, { yes: v.yes, retry });
|
|
15090
|
-
const r = await api(
|
|
15196
|
+
const r = await api(path2, { method: "POST", body });
|
|
15091
15197
|
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
15198
|
${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
15199
|
}
|
|
@@ -15100,8 +15206,8 @@ async function planCmd(sub, slug, v, context) {
|
|
|
15100
15206
|
if (sub !== "apply") throw usage(t(`Unknown plan subcommand: ${sub ?? "(none)"}.`, `Subcomando desconocido de plan: ${sub ?? "(ninguno)"}.`), "ghosty plan apply <workspace> [--dry-run]");
|
|
15101
15207
|
slug ??= context;
|
|
15102
15208
|
if (!slug) throw usage(t("Missing workspace.", "Falta el espacio."), "ghosty plan apply <workspace> [--dry-run] (or ghosty use <workspace>)");
|
|
15103
|
-
const
|
|
15104
|
-
const plan = await api(
|
|
15209
|
+
const path2 = `/api/v2/workspaces/${encodeURIComponent(slug)}/plan/apply`;
|
|
15210
|
+
const plan = await api(path2, { method: "POST", body: { dryRun: true } });
|
|
15105
15211
|
const money2 = (n) => `$${n.toLocaleString("es-MX")} MXN`;
|
|
15106
15212
|
const text = [
|
|
15107
15213
|
`${bold(plan.workspace)} ${dim(plan.scheduledFor ? t(`scheduled for ${plan.scheduledFor.slice(0, 10)}`, `programado para el ${plan.scheduledFor.slice(0, 10)}`) : "")}`,
|
|
@@ -15113,7 +15219,7 @@ ${dim(t("Dry run: nothing applied.", "Simulaci\xF3n: no se aplic\xF3 nada."))}`)
|
|
|
15113
15219
|
requireYesIfHeadless(v.yes, `ghosty plan apply ${slug}`);
|
|
15114
15220
|
await confirm(`${text}
|
|
15115
15221
|
${t("Apply this plan now?", "\xBFAplicar este plan ya?")}`, { yes: v.yes, retry: `ghosty plan apply ${slug}` });
|
|
15116
|
-
const r = await api(
|
|
15222
|
+
const r = await api(path2, { method: "POST", body: {} });
|
|
15117
15223
|
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
15224
|
}
|
|
15119
15225
|
async function boxesUsage(slug) {
|
|
@@ -15142,12 +15248,12 @@ import { homedir as homedir5 } from "node:os";
|
|
|
15142
15248
|
import { dirname as dirname4, join as join10 } from "node:path";
|
|
15143
15249
|
init_i18n();
|
|
15144
15250
|
init_util();
|
|
15145
|
-
var
|
|
15251
|
+
var enc14 = encodeURIComponent;
|
|
15146
15252
|
var hora = (iso) => new Date(iso).toLocaleString("es-MX", { timeZone: "America/Mexico_City", hour12: false, dateStyle: "short", timeStyle: "short" });
|
|
15147
15253
|
async function history(agent, v) {
|
|
15148
15254
|
if (!agent) throw usage(t("Missing agent.", "Falta el agente."), "ghosty demos history <agent> [--since 2d] [--session web:\u2026] [--timeline] [--full]");
|
|
15149
15255
|
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/${
|
|
15256
|
+
const r = await api(`/api/v2/agents/${enc14(agent)}/demo/conversations?${q}`);
|
|
15151
15257
|
out(r, () => {
|
|
15152
15258
|
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
15259
|
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,7 +15282,7 @@ function anonCookieOf(setCookie) {
|
|
|
15176
15282
|
async function visitorCookie(base, slug, fresh) {
|
|
15177
15283
|
const jar = await readJar();
|
|
15178
15284
|
if (jar[slug] && !fresh) return jar[slug];
|
|
15179
|
-
const res = await fetch(`${base}/p/${
|
|
15285
|
+
const res = await fetch(`${base}/p/${enc14(slug)}`, { redirect: "manual" });
|
|
15180
15286
|
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
15287
|
const cookie = anonCookieOf(res.headers.get("set-cookie"));
|
|
15182
15288
|
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);
|
|
@@ -15188,9 +15294,9 @@ async function visitorCookie(base, slug, fresh) {
|
|
|
15188
15294
|
async function visitorTurn(base, slug, cookie, text) {
|
|
15189
15295
|
const turnId = randomUUID2();
|
|
15190
15296
|
const ctrl = new AbortController();
|
|
15191
|
-
const ev = await fetch(`${base}/p/${
|
|
15297
|
+
const ev = await fetch(`${base}/p/${enc14(slug)}/events`, { headers: { cookie, accept: "text/event-stream" }, signal: ctrl.signal });
|
|
15192
15298
|
if (!ev.ok) throw await toError(ev);
|
|
15193
|
-
const sent = await fetch(`${base}/p/${
|
|
15299
|
+
const sent = await fetch(`${base}/p/${enc14(slug)}/messages`, { method: "POST", headers: { cookie, "content-type": "application/json" }, body: JSON.stringify({ content: text, turnId }) });
|
|
15194
15300
|
if (!sent.ok) {
|
|
15195
15301
|
ctrl.abort();
|
|
15196
15302
|
const body = await sent.text();
|
|
@@ -15220,7 +15326,7 @@ async function visitorTurn(base, slug, cookie, text) {
|
|
|
15220
15326
|
async function recycle(agent) {
|
|
15221
15327
|
for (let i = 0; i < 10; i++) {
|
|
15222
15328
|
try {
|
|
15223
|
-
await api(`/api/v2/agents/${
|
|
15329
|
+
await api(`/api/v2/agents/${enc14(agent)}/box`, { method: "POST", body: { recycle: true } });
|
|
15224
15330
|
return;
|
|
15225
15331
|
} catch (e) {
|
|
15226
15332
|
if (!(e instanceof CliError) || !/turno|turn/i.test(e.message)) throw e;
|
|
@@ -15276,10 +15382,10 @@ async function collectSiteFiles(target) {
|
|
|
15276
15382
|
const full = join11(dir, e.name);
|
|
15277
15383
|
if (e.isDirectory()) await walk(full);
|
|
15278
15384
|
else if (e.isFile()) {
|
|
15279
|
-
const
|
|
15280
|
-
const ext =
|
|
15385
|
+
const path2 = relative2(target, full).split(sep).join("/");
|
|
15386
|
+
const ext = path2.split(".").pop()?.toLowerCase() ?? "";
|
|
15281
15387
|
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" });
|
|
15388
|
+
files.push(TEXT_EXT.has(ext) ? { path: path2, content: buf.toString("utf8"), encoding: "text" } : { path: path2, content: buf.toString("base64"), encoding: "base64" });
|
|
15283
15389
|
}
|
|
15284
15390
|
}
|
|
15285
15391
|
};
|
|
@@ -15400,9 +15506,9 @@ async function domainCmd(action, a2, a3) {
|
|
|
15400
15506
|
const acts = /* @__PURE__ */ new Set(["ls", "list", "add", "verify", "rm", "remove"]);
|
|
15401
15507
|
const [act, slug, host] = action && !acts.has(action) ? ["ls", action, a2] : [action ?? "ls", a2, a3];
|
|
15402
15508
|
if (!slug) throw usage(t("Missing the app (slug, id or URL).", "Falta la app (slug, id o URL)."), ex);
|
|
15403
|
-
const
|
|
15509
|
+
const path2 = `/api/v2/sites/${encodeURIComponent(slug)}/domain`;
|
|
15404
15510
|
if (act === "ls" || act === "list") {
|
|
15405
|
-
const r2 = await api(
|
|
15511
|
+
const r2 = await api(path2);
|
|
15406
15512
|
return out(
|
|
15407
15513
|
r2,
|
|
15408
15514
|
() => r2.domains.length ? `${bold(r2.url)}
|
|
@@ -15411,11 +15517,11 @@ ${table(r2.domains.map((d) => ({ domain: d.host, state: d.verified ? t("verified
|
|
|
15411
15517
|
}
|
|
15412
15518
|
if (!host) throw usage(t("Missing the domain.", "Falta el dominio."), ex);
|
|
15413
15519
|
if (act === "rm" || act === "remove") {
|
|
15414
|
-
const r2 = await api(
|
|
15520
|
+
const r2 = await api(path2, { method: "DELETE", body: { domain: host } });
|
|
15415
15521
|
return out(r2, () => `${lavender("\u2713")} ${t("Removed", "Quitado")} ${bold(r2.host)}`);
|
|
15416
15522
|
}
|
|
15417
15523
|
if (act !== "add" && act !== "verify") throw usage(t(`Unknown domain action: ${act}.`, `Acci\xF3n de dominio desconocida: ${act}.`), ex);
|
|
15418
|
-
const r = await api(
|
|
15524
|
+
const r = await api(path2, { method: "POST", body: { domain: host, ...act === "verify" ? { verify: true } : {} } });
|
|
15419
15525
|
return out(r, () => `${r.verified ? lavender("\u2713") : dim("\u2026")} ${bold(r.host)}
|
|
15420
15526
|
${domainLine(r, t("en", "es") === "es" ? "es" : "en").replace("<slug>", slug)}`);
|
|
15421
15527
|
}
|
|
@@ -15498,8 +15604,8 @@ function blockTextMapped(b) {
|
|
|
15498
15604
|
// src/commands/docs.ts
|
|
15499
15605
|
init_util();
|
|
15500
15606
|
init_i18n();
|
|
15501
|
-
var
|
|
15502
|
-
var docPath = (id, rest = "") => `/api/v2/me/docs/${
|
|
15607
|
+
var enc15 = encodeURIComponent;
|
|
15608
|
+
var docPath = (id, rest = "") => `/api/v2/me/docs/${enc15(id)}${rest}`;
|
|
15503
15609
|
function docRef(ref) {
|
|
15504
15610
|
const m = /\/docs\/([^/?#\s]+)/.exec(ref);
|
|
15505
15611
|
return m && /^https?:\/\//.test(ref) ? decodeURIComponent(m[1]) : ref.trim();
|
|
@@ -15630,8 +15736,8 @@ init_api();
|
|
|
15630
15736
|
init_auth();
|
|
15631
15737
|
init_util();
|
|
15632
15738
|
init_i18n();
|
|
15633
|
-
var
|
|
15634
|
-
var agentPath3 = (id, rest = "") => `/api/v2/agents/${
|
|
15739
|
+
var enc16 = encodeURIComponent;
|
|
15740
|
+
var agentPath3 = (id, rest = "") => `/api/v2/agents/${enc16(id)}${rest}`;
|
|
15635
15741
|
async function agentsToken(sub, id, v) {
|
|
15636
15742
|
const ex = "ghosty agents token rotate|revoke <agent-id>";
|
|
15637
15743
|
if (sub !== "rotate" && sub !== "revoke") throw usage(t(`Unknown agents token subcommand: ${sub ?? "(none)"}.`, `Subcomando de agents token desconocido: ${sub ?? "(ninguno)"}.`), ex);
|
|
@@ -15653,21 +15759,21 @@ async function agentsToken(sub, id, v) {
|
|
|
15653
15759
|
const r = await api(agentPath3(id, "/token/revoke"), { method: "POST", body: {} });
|
|
15654
15760
|
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
15761
|
}
|
|
15656
|
-
var
|
|
15762
|
+
var short3 = (s2, n = 70) => s2.length > n ? s2.slice(0, n - 1) + "\u2026" : s2;
|
|
15657
15763
|
function memoryRows(r) {
|
|
15658
15764
|
return {
|
|
15659
|
-
notes: r.notes.map((n) => ({ id: n.id, scope: n.scope || t("all", "todo"), rule:
|
|
15660
|
-
promos: r.promos.map((p) => ({ id: p.id, title:
|
|
15765
|
+
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") })),
|
|
15766
|
+
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
15767
|
};
|
|
15662
15768
|
}
|
|
15663
15769
|
async function agentsMemory(sub, id, args, v) {
|
|
15664
15770
|
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
15771
|
if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), ex);
|
|
15666
|
-
const
|
|
15772
|
+
const path2 = agentPath3(id, "/memory");
|
|
15667
15773
|
switch (sub ?? "ls") {
|
|
15668
15774
|
case "ls":
|
|
15669
15775
|
case "list": {
|
|
15670
|
-
const r = await api(
|
|
15776
|
+
const r = await api(path2);
|
|
15671
15777
|
const rows = memoryRows(r);
|
|
15672
15778
|
return out(
|
|
15673
15779
|
r,
|
|
@@ -15680,33 +15786,33 @@ async function agentsMemory(sub, id, args, v) {
|
|
|
15680
15786
|
case "add": {
|
|
15681
15787
|
if (v.promo) {
|
|
15682
15788
|
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(
|
|
15789
|
+
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
15790
|
return out(r2, () => `${lavender("\u2713")} ${t("Promo saved", "Promo guardada")} ${r2.promo.id}${r2.warning ? dim(` (${serverText(r2.warning)})`) : ""}`);
|
|
15685
15791
|
}
|
|
15686
15792
|
const content = args.join(" ").trim();
|
|
15687
15793
|
if (!content) throw usage(t("Missing the rule.", "Falta la regla."), ex);
|
|
15688
|
-
const r = await api(
|
|
15794
|
+
const r = await api(path2, { method: "POST", body: { content, ...v.scope ? { scope: v.scope } : {} } });
|
|
15689
15795
|
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
15796
|
}
|
|
15691
15797
|
case "edit": {
|
|
15692
15798
|
const [ref, ...rest] = args;
|
|
15693
15799
|
const content = rest.join(" ").trim();
|
|
15694
15800
|
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(
|
|
15801
|
+
const r = await api(path2, { method: "POST", body: { content, replaces: ref, ...v.scope !== void 0 ? { scope: v.scope } : {} } });
|
|
15696
15802
|
return out(r, () => `${lavender("\u2713")} ${t("Updated", "Actualizada")} ${r.note.id}`);
|
|
15697
15803
|
}
|
|
15698
15804
|
case "rm":
|
|
15699
15805
|
case "forget": {
|
|
15700
15806
|
const ref = args[0];
|
|
15701
15807
|
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(
|
|
15808
|
+
const r = await api(path2, { method: "DELETE", body: { id: ref } });
|
|
15703
15809
|
return out(r, () => `${lavender("\u2713")} ${r.kind === "promo" ? t("Promo deleted", "Promo borrada") : t("Rule forgotten", "Regla olvidada")} ${r.deleted}`);
|
|
15704
15810
|
}
|
|
15705
15811
|
case "enable":
|
|
15706
15812
|
case "disable": {
|
|
15707
15813
|
const ref = args[0];
|
|
15708
15814
|
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(
|
|
15815
|
+
const r = await api(path2, { method: "POST", body: { promo: { id: ref, active: sub === "enable" } } });
|
|
15710
15816
|
return out(r, () => `${lavender("\u2713")} ${r.promo.title}: ${r.promo.active ? t("on", "prendida") : t("off (kept)", "apagada (se conserva)")}`);
|
|
15711
15817
|
}
|
|
15712
15818
|
default:
|
|
@@ -15722,8 +15828,8 @@ async function agentsTransfer(id, v) {
|
|
|
15722
15828
|
if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), ex);
|
|
15723
15829
|
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
15830
|
const body = { ...v.to ? { to: v.to } : {}, ...v.workspace ? { workspace: v.workspace } : {}, ...v.force ? { force: true } : {} };
|
|
15725
|
-
const
|
|
15726
|
-
const plan = await api(
|
|
15831
|
+
const path2 = agentPath3(id, "/transfer");
|
|
15832
|
+
const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
|
|
15727
15833
|
const text = transferText(plan);
|
|
15728
15834
|
const retry = `ghosty agents transfer ${id}${v.to ? ` --to ${v.to}` : ""}${v.workspace ? ` --workspace ${v.workspace}` : ""}${v.force ? " --force" : ""}`;
|
|
15729
15835
|
if (v["dry-run"]) return out(plan, () => `${text}
|
|
@@ -15732,13 +15838,13 @@ ${dim(applyHint(retry))}`);
|
|
|
15732
15838
|
requireYesIfHeadless(v.yes, retry);
|
|
15733
15839
|
await confirm(`${text}
|
|
15734
15840
|
${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(
|
|
15841
|
+
const r = await api(path2, { method: "POST", body });
|
|
15736
15842
|
if (r.kind === "move" && r.failed.length) return out(r, () => `${transferText(r)}
|
|
15737
15843
|
${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
15844
|
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
15845
|
}
|
|
15740
|
-
async function openPage(
|
|
15741
|
-
const url = `${await baseUrl()}${
|
|
15846
|
+
async function openPage(path2, what, v) {
|
|
15847
|
+
const url = `${await baseUrl()}${path2}`;
|
|
15742
15848
|
if (globals.json) return out({ url });
|
|
15743
15849
|
info(`${lavender("\u203A")} ${what}: ${url}`);
|
|
15744
15850
|
if (!v["no-open"]) await openUrl(url);
|
|
@@ -15785,15 +15891,15 @@ import { randomBytes as randomBytes2 } from "node:crypto";
|
|
|
15785
15891
|
import { existsSync, readFileSync } from "node:fs";
|
|
15786
15892
|
init_util();
|
|
15787
15893
|
init_i18n();
|
|
15788
|
-
var
|
|
15894
|
+
var enc17 = encodeURIComponent;
|
|
15789
15895
|
var money = (n) => n === null || n === void 0 ? "\u2014" : `$${n.toFixed(2)}`;
|
|
15790
15896
|
var mins = (n) => n === null || n === void 0 ? "\u2014" : n >= 120 ? `${(n / 60).toFixed(1)} h` : `${n} min`;
|
|
15791
15897
|
var hhmm = (iso) => new Date(iso).toLocaleTimeString("es-MX", { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
15792
|
-
var
|
|
15898
|
+
var short4 = (s2) => (s2 ?? "").slice(0, 16).replace("T", " ");
|
|
15793
15899
|
async function roomsCmd(ws, verb, repo, v) {
|
|
15794
|
-
const
|
|
15900
|
+
const path2 = `/api/v2/workspaces/${enc17(ws)}/factory/rooms`;
|
|
15795
15901
|
if (!verb || verb === "ls" || verb === "list") {
|
|
15796
|
-
const r2 = await api(
|
|
15902
|
+
const r2 = await api(path2);
|
|
15797
15903
|
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
15904
|
return out(
|
|
15799
15905
|
r2,
|
|
@@ -15805,7 +15911,7 @@ async function roomsCmd(ws, verb, repo, v) {
|
|
|
15805
15911
|
);
|
|
15806
15912
|
}
|
|
15807
15913
|
if (verb === "repos") {
|
|
15808
|
-
const r2 = await api(`${
|
|
15914
|
+
const r2 = await api(`${path2}?repos=1`);
|
|
15809
15915
|
return out(r2, () => {
|
|
15810
15916
|
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
15917
|
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 +15929,10 @@ async function roomsCmd(ws, verb, repo, v) {
|
|
|
15823
15929
|
const retry = `ghosty factory rooms disconnect ${repo} --room ${v.room} --workspace ${ws}`;
|
|
15824
15930
|
requireYesIfHeadless(v.yes, retry);
|
|
15825
15931
|
await confirm(t(`Disconnect ${repo} from #${v.room}?`, `\xBFDesconectar ${repo} de #${v.room}?`), { yes: v.yes, retry });
|
|
15826
|
-
const r2 = await api(
|
|
15932
|
+
const r2 = await api(path2, { body: { action: "disconnect", repo, room: v.room } });
|
|
15827
15933
|
return out(r2, () => `${lavender("\u2713")} ${t(`${repo} is no longer connected to #${r2.room}.`, `${repo} ya no est\xE1 conectado a #${r2.room}.`)}`);
|
|
15828
15934
|
}
|
|
15829
|
-
const r = await api(
|
|
15935
|
+
const r = await api(path2, { body: { action: "connect", repo, room: v.room, ...v.create ? { create: true } : {}, ...v.private ? { private: true } : {} } });
|
|
15830
15936
|
const rd = r.readiness;
|
|
15831
15937
|
return out(
|
|
15832
15938
|
r,
|
|
@@ -15841,9 +15947,9 @@ async function roomsCmd(ws, verb, repo, v) {
|
|
|
15841
15947
|
);
|
|
15842
15948
|
}
|
|
15843
15949
|
async function sprintsCmd(ws, verb, ref, v) {
|
|
15844
|
-
const
|
|
15950
|
+
const path2 = `/api/v2/workspaces/${enc17(ws)}/factory/sprints`;
|
|
15845
15951
|
if (!verb || verb === "ls" || verb === "list") {
|
|
15846
|
-
const r2 = await api(
|
|
15952
|
+
const r2 = await api(path2);
|
|
15847
15953
|
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
15954
|
return out(r2, () => rows.length ? table(rows, ["sprint", "title", "status", "room", "merged"]) : dim(t("No sprints yet.", "Todav\xEDa no hay sprints.")));
|
|
15849
15955
|
}
|
|
@@ -15855,7 +15961,7 @@ async function sprintsCmd(ws, verb, ref, v) {
|
|
|
15855
15961
|
requireYesIfHeadless(v.yes, retry);
|
|
15856
15962
|
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
15963
|
}
|
|
15858
|
-
const r = verb === "approve" ? await api(`${
|
|
15964
|
+
const r = verb === "approve" ? await api(`${path2}/${id}`, { body: { action: "approve" } }) : await api(`${path2}/${id}`);
|
|
15859
15965
|
return out(
|
|
15860
15966
|
r,
|
|
15861
15967
|
() => [
|
|
@@ -15881,12 +15987,12 @@ function dotenvKeys(text) {
|
|
|
15881
15987
|
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
15988
|
}
|
|
15883
15989
|
async function envCmd(ws, verb, ref, file, v) {
|
|
15884
|
-
const
|
|
15990
|
+
const path2 = `/api/v2/workspaces/${enc17(ws)}/factory/env`;
|
|
15885
15991
|
const repo = ref && ref.includes("/") ? ref : v.repo ?? repoHere();
|
|
15886
15992
|
if (ref && !ref.includes("/") && !file) file = ref;
|
|
15887
15993
|
if (!repo) throw usage(t("Which repo?", "\xBFQu\xE9 repo?"), `ghosty factory env ${verb ?? "ls"} OWNER/REPO --workspace ${ws}`);
|
|
15888
15994
|
if (!verb || verb === "ls" || verb === "list") {
|
|
15889
|
-
const r2 = await api(`${
|
|
15995
|
+
const r2 = await api(`${path2}?repo=${enc17(repo)}`);
|
|
15890
15996
|
const risk = new Map(r2.risks.map((x) => [x.key, x.risk]));
|
|
15891
15997
|
return out(
|
|
15892
15998
|
r2,
|
|
@@ -15925,7 +16031,7 @@ ${k}=${randomBytes2(32).toString("hex")}`;
|
|
|
15925
16031
|
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
16032
|
{ yes: v.yes, retry }
|
|
15927
16033
|
);
|
|
15928
|
-
const r = await api(
|
|
16034
|
+
const r = await api(path2, {
|
|
15929
16035
|
body: { repo, dotenv, ...v.replace ? { replace: true } : {} }
|
|
15930
16036
|
});
|
|
15931
16037
|
return out(
|
|
@@ -16026,7 +16132,7 @@ async function factoryCmd(sub, verb, ref, title, v, context) {
|
|
|
16026
16132
|
if (sub === "rooms") return roomsCmd(ws, verb, ref, v);
|
|
16027
16133
|
if (sub === "sprints") return sprintsCmd(ws, verb, ref, v);
|
|
16028
16134
|
if (sub === "env") return envCmd(ws, verb, ref, title, v);
|
|
16029
|
-
const base = `/api/v2/workspaces/${
|
|
16135
|
+
const base = `/api/v2/workspaces/${enc17(ws)}/factory/runs`;
|
|
16030
16136
|
if (sub === "run") {
|
|
16031
16137
|
const text = verb?.trim();
|
|
16032
16138
|
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 +16149,13 @@ ${dim(t(`Wait for the plan: ghosty factory runs watch --thread ${r.threadId} --w
|
|
|
16043
16149
|
if (!verb || verb === "ls" || verb === "list") {
|
|
16044
16150
|
const qs = new URLSearchParams({ ...v.room ? { room: v.room } : {}, ...v.status ? { status: v.status } : {}, ...v.limit ? { limit: v.limit } : {} });
|
|
16045
16151
|
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:
|
|
16152
|
+
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
16153
|
return out(r, () => rows.length ? `${table(rows, ["run", "title", "status", "room", "pr", "time", "usd", "updated"])}
|
|
16048
16154
|
${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
16155
|
}
|
|
16050
16156
|
if (verb === "stats") {
|
|
16051
16157
|
const qs = new URLSearchParams({ since: v.since ?? "30d", ...v.repo ? { repo: v.repo } : {} });
|
|
16052
|
-
const { stats: s2, workspace } = await api(`/api/v2/workspaces/${
|
|
16158
|
+
const { stats: s2, workspace } = await api(`/api/v2/workspaces/${enc17(ws)}/factory/stats?${qs}`);
|
|
16053
16159
|
return out({ workspace, stats: s2 }, () => {
|
|
16054
16160
|
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
16161
|
return [
|
|
@@ -16069,9 +16175,9 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
|
|
|
16069
16175
|
return waitResult(ws, await waitRun(base, ws, ref ? { runId: Number(ref.replace(/^#/, "")) } : { thread }, timeoutS), timeoutS);
|
|
16070
16176
|
}
|
|
16071
16177
|
if (!ref) throw usage(t("Missing run.", "Falta el pedido."), `ghosty factory runs ${verb} <run> --workspace ${ws}`);
|
|
16072
|
-
const
|
|
16178
|
+
const path2 = `${base}/${enc17(ref.replace(/^#/, ""))}`;
|
|
16073
16179
|
if (verb === "show" && v.timing) {
|
|
16074
|
-
const { timing: tm, workspace } = await api(`${
|
|
16180
|
+
const { timing: tm, workspace } = await api(`${path2}/timing`);
|
|
16075
16181
|
return out({ workspace, timing: tm }, () => {
|
|
16076
16182
|
const c = tm.clock;
|
|
16077
16183
|
const head = [
|
|
@@ -16102,14 +16208,14 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
|
|
|
16102
16208
|
});
|
|
16103
16209
|
}
|
|
16104
16210
|
if (verb === "why") {
|
|
16105
|
-
const { why: w, workspace } = await api(`${
|
|
16211
|
+
const { why: w, workspace } = await api(`${path2}/why`, v.refresh ? { body: { refresh: true } } : void 0);
|
|
16106
16212
|
return out({ workspace, why: w }, () => {
|
|
16107
16213
|
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
16214
|
if (gap) return `${bold(`#${w.runId}`)} ${gap}`;
|
|
16109
16215
|
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
16216
|
const impact = { high: t("high", "alto"), medium: t("medium", "medio"), low: t("low", "bajo") }[w.impact] ?? w.impact;
|
|
16111
16217
|
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 ${
|
|
16218
|
+
`${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
16219
|
...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
16220
|
${dim(g.evidence)}`), ""] : [],
|
|
16115
16221
|
w.summary,
|
|
@@ -16122,7 +16228,7 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
|
|
|
16122
16228
|
}
|
|
16123
16229
|
if (verb === "advise") {
|
|
16124
16230
|
const id = Number(ref.replace(/^#/, ""));
|
|
16125
|
-
const { advice: a, workspace } = await api(`${
|
|
16231
|
+
const { advice: a, workspace } = await api(`${path2}/advise`, { body: {} });
|
|
16126
16232
|
const note = a.buildNote.map((p, i) => `${i + 1}. ${p.point}`).join(" ");
|
|
16127
16233
|
const sh = (x) => `'${x.replace(/'/g, `'\\''`)}'`;
|
|
16128
16234
|
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 +16254,21 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
|
|
|
16148
16254
|
});
|
|
16149
16255
|
}
|
|
16150
16256
|
if (verb === "show") {
|
|
16151
|
-
const { run: r } = await api(
|
|
16257
|
+
const { run: r } = await api(path2);
|
|
16152
16258
|
const lines = [
|
|
16153
16259
|
`${bold(`#${r.id} ${r.title}`)} ${dim(`\xB7 ${r.status}${r.kind ? ` (${r.kind})` : ""} \xB7 ${r.room ? `#${r.room}` : ""}`)}`,
|
|
16154
16260
|
`${t("repo", "repo")}: ${r.repo ?? "\u2014"}${r.branch ? ` (${r.branch})` : ""} PR: ${r.pr ?? "\u2014"}`,
|
|
16155
16261
|
`preview: ${r.preview ?? "\u2014"}${r.previewState ? dim(` (${r.previewState})`) : ""}`,
|
|
16156
16262
|
`${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
16263
|
...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}, ${
|
|
16264
|
+
`${t("running", "trabajando")}: ${r.running.length ? r.running.map((x) => `@${x.role} ${dim(`(${x.turn}, ${short4(x.since)})`)}`).join(", ") : dim(t("nobody", "nadie"))}`,
|
|
16159
16265
|
...r.escalation ? [
|
|
16160
16266
|
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
16267
|
r.escalation.findings,
|
|
16162
16268
|
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
16269
|
] : [],
|
|
16164
16270
|
bold(t("Latest events", "\xDAltimos eventos")),
|
|
16165
|
-
...r.events.map((e) => ` ${dim(
|
|
16271
|
+
...r.events.map((e) => ` ${dim(short4(e.at))} ${e.type}${e.actor ? ` ${dim(e.actor)}` : ""}`),
|
|
16166
16272
|
...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
16273
|
];
|
|
16168
16274
|
return out(r, () => lines.join("\n"));
|
|
@@ -16171,12 +16277,12 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
|
|
|
16171
16277
|
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
16278
|
const retry = `ghosty factory runs rename ${ref} ${JSON.stringify(title)} --workspace ${ws}`;
|
|
16173
16279
|
if (!v["dry-run"]) requireYesIfHeadless(v.yes, retry);
|
|
16174
|
-
const plan = await api(
|
|
16280
|
+
const plan = await api(path2, { body: { title, dryRun: true } });
|
|
16175
16281
|
const line2 = `#${ref.replace(/^#/, "")}: \xAB${plan.from}\xBB \u2192 \xAB${plan.to}\xBB`;
|
|
16176
16282
|
if (v["dry-run"]) return out({ ...plan, dryRun: true }, () => `${line2}
|
|
16177
16283
|
${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
16178
16284
|
await confirm(t(`Rename ${line2}?`, `\xBFRenombrar ${line2}?`), { yes: v.yes, retry });
|
|
16179
|
-
const r = await api(
|
|
16285
|
+
const r = await api(path2, { body: { title } });
|
|
16180
16286
|
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
16287
|
}
|
|
16182
16288
|
if (verb === "approve" || verb === "changes" || verb === "recheck" || verb === "merge" || verb === "card") {
|
|
@@ -16281,7 +16387,10 @@ function couponRow(c) {
|
|
|
16281
16387
|
code: c.code,
|
|
16282
16388
|
credit: `$${c.creditMxn}`,
|
|
16283
16389
|
plan: c.plan ?? "",
|
|
16390
|
+
from: c.planFrom ? c.planFrom.slice(0, 10) : "",
|
|
16284
16391
|
until: c.until ? c.until.slice(0, 10) : "",
|
|
16392
|
+
redeem: c.redeemBy ? c.redeemBy.slice(0, 10) : "",
|
|
16393
|
+
promo: c.convertPromo ?? "",
|
|
16285
16394
|
used: `${c.redemptions}/${c.maxRedemptions ?? "\u221E"}`,
|
|
16286
16395
|
state: c.active ? t("active", "activo") : t("draft", "borrador"),
|
|
16287
16396
|
only: c.eligibility ?? "",
|
|
@@ -16289,11 +16398,17 @@ function couponRow(c) {
|
|
|
16289
16398
|
note: c.note ?? ""
|
|
16290
16399
|
};
|
|
16291
16400
|
}
|
|
16401
|
+
function redemptionRow(x) {
|
|
16402
|
+
return { email: x.email, redeemed: x.redeemedAt.slice(0, 10), plan: x.plan ?? "", from: x.from?.slice(0, 10) ?? "", until: x.until?.slice(0, 10) ?? "", status: x.status ?? "", converted: x.converted ?? "\u2014" };
|
|
16403
|
+
}
|
|
16292
16404
|
function bodyOf(v) {
|
|
16293
16405
|
const b = {};
|
|
16294
16406
|
if (v.credit !== void 0) b.creditMxn = Number(v.credit);
|
|
16295
16407
|
if (v.plan !== void 0) b.plan = v.plan === "none" ? null : v.plan;
|
|
16296
16408
|
if (v.until !== void 0) b.until = v.until;
|
|
16409
|
+
if (v.from !== void 0) b.planFrom = v.from;
|
|
16410
|
+
if (v["redeem-by"] !== void 0) b.redeemBy = v["redeem-by"];
|
|
16411
|
+
if (v["convert-promo"] !== void 0) b.convertPromo = v["convert-promo"];
|
|
16297
16412
|
if (v.max !== void 0) b.maxRedemptions = v.max === "none" ? null : Number(v.max);
|
|
16298
16413
|
if (v.on) b.active = true;
|
|
16299
16414
|
if (v.off) b.active = false;
|
|
@@ -16306,7 +16421,7 @@ async function couponsCmd(sub, code, v) {
|
|
|
16306
16421
|
const one = (c) => `${lavender("\u2713")} ${c.code} $${c.creditMxn}${c.plan ? ` + ${c.plan} \u2192 ${c.until?.slice(0, 10)}` : ""} ${c.active ? t("active", "activo") : dim(t("draft", "borrador"))}`;
|
|
16307
16422
|
if (!sub || sub === "ls" || sub === "list") {
|
|
16308
16423
|
const r = await api("/api/v2/support/coupons");
|
|
16309
|
-
return out(r, () => r.coupons.length ? table(r.coupons.map(couponRow), ["code", "credit", "plan", "until", "used", "state", "only", "agent", "note"]) : dim(t("No coupons.", "Sin cupones.")));
|
|
16424
|
+
return out(r, () => r.coupons.length ? table(r.coupons.map(couponRow), ["code", "credit", "plan", "from", "until", "redeem", "promo", "used", "state", "only", "agent", "note"]) : dim(t("No coupons.", "Sin cupones.")));
|
|
16310
16425
|
}
|
|
16311
16426
|
if (!code) throw usage(t("Missing the coupon code.", "Falta el c\xF3digo del cup\xF3n."), `ghosty coupons ${sub} <CODE>`);
|
|
16312
16427
|
if (sub === "create") {
|
|
@@ -16317,11 +16432,17 @@ async function couponsCmd(sub, code, v) {
|
|
|
16317
16432
|
const r = await api(`/api/v2/support/coupons/${encodeURIComponent(code)}`, { method: "PATCH", body: bodyOf(v) });
|
|
16318
16433
|
return out(r, () => one(r.coupon));
|
|
16319
16434
|
}
|
|
16435
|
+
if (sub === "show") {
|
|
16436
|
+
const r = await api(`/api/v2/support/coupons/${encodeURIComponent(code)}`);
|
|
16437
|
+
return out(r, () => `${one(r.coupon)}
|
|
16438
|
+
${r.redemptions.length ? table(r.redemptions.map(redemptionRow), ["email", "redeemed", "plan", "from", "until", "status", "converted"]) : dim(t("Nobody redeemed it yet.", "Nadie lo ha canjeado."))}
|
|
16439
|
+
${dim(t(`${r.converted}/${r.redemptions.length} converted to a paid plan`, `${r.converted}/${r.redemptions.length} ya pagan un plan`))}`);
|
|
16440
|
+
}
|
|
16320
16441
|
if (sub === "redeem") {
|
|
16321
16442
|
const r = await api("/api/v2/me/coupons", { method: "POST", body: { code } });
|
|
16322
16443
|
return out(r, () => `${lavender("\u2713")} ${r.message}`);
|
|
16323
16444
|
}
|
|
16324
|
-
throw usage(t(`Unknown coupons subcommand: ${sub}.`, `Subcomando de coupons desconocido: ${sub}.`), "ghosty coupons ls|create|set|redeem <CODE>");
|
|
16445
|
+
throw usage(t(`Unknown coupons subcommand: ${sub}.`, `Subcomando de coupons desconocido: ${sub}.`), "ghosty coupons ls|create|set|show|redeem <CODE>");
|
|
16325
16446
|
}
|
|
16326
16447
|
|
|
16327
16448
|
// src/commands/ads.ts
|
|
@@ -16363,7 +16484,7 @@ init_api();
|
|
|
16363
16484
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
16364
16485
|
init_i18n();
|
|
16365
16486
|
init_util();
|
|
16366
|
-
var
|
|
16487
|
+
var enc18 = encodeURIComponent;
|
|
16367
16488
|
var cdmx = (iso) => new Date(Date.parse(iso) - 6 * 36e5).toISOString().slice(5, 16).replace("T", " ");
|
|
16368
16489
|
async function supportThreads(email, v) {
|
|
16369
16490
|
const q = new URLSearchParams({ user: email, since: v.since ?? "30d", ...v.surface ? { surface: v.surface } : {} });
|
|
@@ -16383,7 +16504,7 @@ ${table(rows, ["last", "agent", "conversation", "title", "kind", "listed", "turn
|
|
|
16383
16504
|
${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"))}`);
|
|
16384
16505
|
}
|
|
16385
16506
|
async function supportThread(agent, sid, v) {
|
|
16386
|
-
const r = await api(`/api/v2/support/threads/${
|
|
16507
|
+
const r = await api(`/api/v2/support/threads/${enc18(agent)}/${enc18(sid)}?tail=${enc18(v.limit ?? "40")}${v.tools ? "&tools=1" : ""}`);
|
|
16387
16508
|
out(r, () => {
|
|
16388
16509
|
if (v.tools && r.tools) {
|
|
16389
16510
|
const x = r.tools;
|
|
@@ -16416,9 +16537,9 @@ async function cleanupConversations(action, agent, v) {
|
|
|
16416
16537
|
if (!agent) throw usage(t("Missing agent.", "Falta el agente."), ex);
|
|
16417
16538
|
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);
|
|
16418
16539
|
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);
|
|
16419
|
-
const
|
|
16540
|
+
const path2 = `/api/v2/agents/${enc18(agent)}/conversations/cleanup`;
|
|
16420
16541
|
const body = { kind: v.kind, action, ...v.before ? { before: v.before } : {} };
|
|
16421
|
-
const plan = await api(
|
|
16542
|
+
const plan = await api(path2, { method: "POST", body: { ...body, dryRun: true } });
|
|
16422
16543
|
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)");
|
|
16423
16544
|
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}`);
|
|
16424
16545
|
if (v["dry-run"] || !plan.threads) return out(plan, () => `${line2}${plan.sample.length ? `
|
|
@@ -16427,11 +16548,11 @@ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
|
16427
16548
|
requireYesIfHeadless(v.yes, `ghosty conversations ${action} ${agent} --kind ${v.kind}${v.before ? ` --before ${v.before}` : ""}`);
|
|
16428
16549
|
await confirm(`${line2}
|
|
16429
16550
|
${t("Go ahead?", "\xBFSigo?")}`, { yes: v.yes, retry: `ghosty conversations ${action} ${agent} --kind ${v.kind}` });
|
|
16430
|
-
const r = await api(
|
|
16551
|
+
const r = await api(path2, { method: "POST", body });
|
|
16431
16552
|
out(r, () => `${lavender("\u2713")} ${t(`${r.threads} conversation(s) ${action === "rm" ? "deleted" : "archived"}.`, `${r.threads} conversaci\xF3n(es) ${action === "rm" ? "borradas" : "archivadas"}.`)}`);
|
|
16432
16553
|
}
|
|
16433
16554
|
async function turnStats(v) {
|
|
16434
|
-
const r = await api(`/api/v2/support/turn-stats?since=${
|
|
16555
|
+
const r = await api(`/api/v2/support/turn-stats?since=${enc18(v.since ?? "14d")}${v.all ? "&all=1" : ""}`);
|
|
16435
16556
|
out(
|
|
16436
16557
|
r,
|
|
16437
16558
|
() => [
|
|
@@ -16451,7 +16572,7 @@ async function fleetBoxes() {
|
|
|
16451
16572
|
${table(rows, ["template", "boxes", "on", "asleep", "ram", "vcpu", "ram on"])}`);
|
|
16452
16573
|
}
|
|
16453
16574
|
async function appAgentsOf(email, client) {
|
|
16454
|
-
const r = await api(`/api/v2/support/app-agents?user=${
|
|
16575
|
+
const r = await api(`/api/v2/support/app-agents?user=${enc18(email)}${client && client !== "all" ? `&client=${enc18(client)}` : ""}`);
|
|
16455
16576
|
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) : "" }));
|
|
16456
16577
|
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, " "));
|
|
16457
16578
|
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"))}
|
|
@@ -16462,7 +16583,7 @@ ${dim(t("No phones registered for push.", "Sin tel\xE9fonos registrados para avi
|
|
|
16462
16583
|
}
|
|
16463
16584
|
async function pushLog(email, v) {
|
|
16464
16585
|
if (!email) throw usage(t("Missing --user.", "Falta --user."), "ghosty push log --user ana@empresa.com [--since 2d]");
|
|
16465
|
-
const r = await api(`/api/v2/support/push-log?user=${
|
|
16586
|
+
const r = await api(`/api/v2/support/push-log?user=${enc18(email)}&since=${enc18(v.since ?? "2d")}${v.limit ? `&limit=${enc18(v.limit)}` : ""}`);
|
|
16466
16587
|
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 ?? "" }));
|
|
16467
16588
|
out(r, () => rows.length ? `${bold(r.user)}
|
|
16468
16589
|
${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).`)));
|
|
@@ -16500,24 +16621,24 @@ ${dim(t(`Try it on your phone: ghosty novedades test ${r.id}`, `Pru\xE9bala en t
|
|
|
16500
16621
|
if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
|
|
16501
16622
|
const b = await fields();
|
|
16502
16623
|
if (!Object.keys(b).length) throw usage(t("Nothing to change.", "Nada que cambiar."), ex);
|
|
16503
|
-
const r = await api(`${base}/${
|
|
16624
|
+
const r = await api(`${base}/${enc18(id)}`, { method: "PATCH", body: b });
|
|
16504
16625
|
return out(r, () => `${lavender("\u2713")} ${line2(r)}`);
|
|
16505
16626
|
}
|
|
16506
16627
|
case "test": {
|
|
16507
16628
|
if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
|
|
16508
|
-
const r = await api(`${base}/${
|
|
16629
|
+
const r = await api(`${base}/${enc18(id)}`, { method: "POST", body: { push: "test" } });
|
|
16509
16630
|
return out(r, () => `${lavender("\u2713")} ${t(`Sent to your phone (${r.enviados} device(s)).`, `Enviada a tu tel\xE9fono (${r.enviados} dispositivo(s)).`)}`);
|
|
16510
16631
|
}
|
|
16511
16632
|
case "send":
|
|
16512
16633
|
case "schedule": {
|
|
16513
16634
|
if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
|
|
16514
16635
|
if (v.at !== void 0) {
|
|
16515
|
-
const r2 = await api(`${base}/${
|
|
16636
|
+
const r2 = await api(`${base}/${enc18(id)}`, { method: "PATCH", body: { schedule: v.at || null } });
|
|
16516
16637
|
return out(r2, () => `${lavender("\u2713")} ${line2(r2)}`);
|
|
16517
16638
|
}
|
|
16518
16639
|
requireYesIfHeadless(v.yes, `ghosty novedades send ${id}`);
|
|
16519
16640
|
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}` });
|
|
16520
|
-
const r = await api(`${base}/${
|
|
16641
|
+
const r = await api(`${base}/${enc18(id)}`, { method: "POST", body: { push: "all" } });
|
|
16521
16642
|
return out(r, () => `${lavender("\u2713")} ${t(`Pushed to ${r.enviados} device(s).`, `Enviada a ${r.enviados} dispositivo(s).`)}`);
|
|
16522
16643
|
}
|
|
16523
16644
|
case "rm":
|
|
@@ -16525,7 +16646,7 @@ ${dim(t(`Try it on your phone: ghosty novedades test ${r.id}`, `Pru\xE9bala en t
|
|
|
16525
16646
|
if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
|
|
16526
16647
|
requireYesIfHeadless(v.yes, `ghosty novedades rm ${id}`);
|
|
16527
16648
|
await confirm(t(`Delete announcement ${id}?`, `\xBFBorrar la novedad ${id}?`), { yes: v.yes, retry: `ghosty novedades rm ${id}` });
|
|
16528
|
-
return out(await api(`${base}/${
|
|
16649
|
+
return out(await api(`${base}/${enc18(id)}`, { method: "DELETE" }), () => `${lavender("\u2713")} ${t("Deleted.", "Borrada.")}`);
|
|
16529
16650
|
}
|
|
16530
16651
|
default:
|
|
16531
16652
|
throw usage(t(`Unknown novedades subcommand: ${sub}.`, `Subcomando de novedades desconocido: ${sub}.`), ex);
|
|
@@ -16594,7 +16715,7 @@ ${dim(r.case.id)}`);
|
|
|
16594
16715
|
}
|
|
16595
16716
|
if (!id) throw usage(t("Missing the case id.", "Falta el id del caso."), "ghosty support ls");
|
|
16596
16717
|
if (sub === "show") {
|
|
16597
|
-
const r = v.all ? await api(`/api/v2/support/cases/${
|
|
16718
|
+
const r = v.all ? await api(`/api/v2/support/cases/${enc18(id)}`) : await api(`/api/v2/me/support/${enc18(id)}`);
|
|
16598
16719
|
const c = r.case;
|
|
16599
16720
|
return out(r, () => {
|
|
16600
16721
|
const who2 = (a) => a === "staff" ? "Ghosty" : a === "agent" ? t("agent", "agente") : a === "customer" ? t("customer", "cliente") : a;
|
|
@@ -16615,18 +16736,18 @@ ${m.body}${m.attachments.length ? dim(`
|
|
|
16615
16736
|
}
|
|
16616
16737
|
if (sub === "reply") {
|
|
16617
16738
|
if (!v.body && !v.resolve) throw usage(t("Missing --body.", "Falta --body."), 'ghosty support reply <id> --body "\u2026" [--resolve]');
|
|
16618
|
-
const r = v.all || v.resolve ? await api(`/api/v2/support/cases/${
|
|
16739
|
+
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 } });
|
|
16619
16740
|
return out(r, () => `${lavender("\u2713")} ${r.case.code} \xB7 ${STATUS(r.case.status)}`);
|
|
16620
16741
|
}
|
|
16621
16742
|
if (sub === "link") {
|
|
16622
16743
|
const ref = v.ref;
|
|
16623
16744
|
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)");
|
|
16624
|
-
const r = await api(`/api/v2/support/cases/${
|
|
16745
|
+
const r = await api(`/api/v2/support/cases/${enc18(id)}`, { method: "POST", body: { intent: "link", ref } });
|
|
16625
16746
|
return out(r, () => `${lavender("\u2713")} ${r.case.code} \u2192 ${bold(r.ref)} ${dim(`(${r.github.title})`)}${r.alsoWaiting ? `
|
|
16626
16747
|
${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.`))}` : ""}`);
|
|
16627
16748
|
}
|
|
16628
16749
|
if (sub === "resolve" || sub === "reopen") {
|
|
16629
|
-
const r = await api(`/api/v2/support/cases/${
|
|
16750
|
+
const r = await api(`/api/v2/support/cases/${enc18(id)}`, { method: "POST", body: { intent: sub } });
|
|
16630
16751
|
return out(r, () => `${lavender("\u2713")} ${r.case.code} \xB7 ${STATUS(r.case.status)}`);
|
|
16631
16752
|
}
|
|
16632
16753
|
throw usage(t(`Unknown support subcommand: ${sub}.`, `Subcomando de support desconocido: ${sub}.`), "ghosty support ls | open | show | reply | resolve | reopen | link");
|
|
@@ -16667,7 +16788,8 @@ ${bold("Workspaces")} ${dim("(changing them: Ghosty staff)")}
|
|
|
16667
16788
|
staff ls [staff-email] [client-email] Ghosty staff assigned to client accounts (staff) [--all: also revoked]
|
|
16668
16789
|
staff assign|unassign <staff> <client> Staff sees and runs that account's agents, documents, databases and boards as themselves (no keys, billing or transfers; logged) [--reason]
|
|
16669
16790
|
settings ls|set|unset <KEY> [value] Platform switches (staff): who answers /c without an account, native subagents\u2026 [+id/-id edits a list]
|
|
16670
|
-
coupons ls|create|set <CODE> Redeemable coupons (staff): balance in cost pesos and an optional lent plan until a date; created as a draft [--credit 50] [--plan pro|power|max --until 2026-11-13] [--max 100] [--eligibility hackathon:<event>: accepted people only] [--agent ID: each account gets its own copy] [--on|--off] [--note]
|
|
16791
|
+
coupons ls|create|set <CODE> Redeemable coupons (staff): balance in cost pesos and an optional lent plan until a date; created as a draft [--credit 50] [--plan pro|power|max --until 2026-11-13] [--max 100] [--eligibility hackathon:<event>: accepted people only] [--agent ID: each account gets its own copy] [--from DATE: the lent plan starts then] [--redeem-by DATE: last day to redeem] [--convert-promo STRIPE_CODE: offered when the plan ends] [--on|--off] [--note]
|
|
16792
|
+
coupons show <CODE> Who redeemed it: plan window (pending/active/ended) and who converted to a paid plan (staff)
|
|
16671
16793
|
coupons redeem <CODE> Redeem a coupon for your account (also /canjear?codigo=CODE)
|
|
16672
16794
|
ads watch [ls|run] Campaign watch (staff): Meta campaigns that stop delivering and ads-only WhatsApp numbers with no leads for 24 h [run --dry-run: what it would alert]
|
|
16673
16795
|
audit ls The audit log: who changed what (agent page, channels, pauses, replies, transfers, "log in as") [--workspace SLUG] [--agent ID] [--actor EMAIL] [--action A] [--since 24h] [--limit N]
|
|
@@ -16807,6 +16929,12 @@ ${bold("Schedule turns")} ${dim("(each run lands in a conversation and notifies
|
|
|
16807
16929
|
schedule rm <id> <schedule-id> Cancel a pending turn
|
|
16808
16930
|
schedule add|ls|rm <id> \u2026 --teams WS#room[/thread] The same in a Teams room or thread: it answers there as if mentioned
|
|
16809
16931
|
|
|
16932
|
+
${bold("Watch pages")} ${dim("(checked without a model turn; the agent only gets a turn when the page changed)")}
|
|
16933
|
+
watch ls <id> Watched pages: state, last change, conversation
|
|
16934
|
+
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]
|
|
16935
|
+
watch check <id> <watch-id> Check it in the next sweep [--dry-run: fetch and compare now, save nothing, notify nobody]
|
|
16936
|
+
watch rm|resume <id> <watch-id> Stop watching \xB7 resume a paused one (or renew an expired one) [--days N]
|
|
16937
|
+
|
|
16810
16938
|
${bold("Teams")}
|
|
16811
16939
|
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]
|
|
16812
16940
|
|
|
@@ -16875,7 +17003,8 @@ ${bold("Espacios")} ${dim("(cambiarlos: staff de Ghosty)")}
|
|
|
16875
17003
|
staff ls [correo-staff] [correo-cliente] Staff de Ghosty asignado a cuentas cliente (staff) [--all: tambi\xE9n revocadas]
|
|
16876
17004
|
staff assign|unassign <staff> <cliente> El staff ve y opera agentes, documentos, bases y tableros de esa cuenta como s\xED mismo (sin llaves, cobro ni traspasos; queda en bit\xE1cora) [--reason]
|
|
16877
17005
|
settings ls|set|unset <CLAVE> [valor] Perillas de la plataforma (staff): qui\xE9n atiende /c sin cuenta, subagentes nativos\u2026 [+id/-id edita una lista]
|
|
16878
|
-
coupons ls|create|set <C\xD3DIGO> Cupones canjeables (staff): saldo en pesos de costo y un plan prestado opcional hasta una fecha; nacen como borrador [--credit 50] [--plan pro|power|max --until 2026-11-13] [--max 100] [--eligibility hackathon:<evento>: s\xF3lo aceptados] [--agent ID: cada cuenta recibe su copia] [--on|--off] [--note]
|
|
17006
|
+
coupons ls|create|set <C\xD3DIGO> Cupones canjeables (staff): saldo en pesos de costo y un plan prestado opcional hasta una fecha; nacen como borrador [--credit 50] [--plan pro|power|max --until 2026-11-13] [--max 100] [--eligibility hackathon:<evento>: s\xF3lo aceptados] [--agent ID: cada cuenta recibe su copia] [--from FECHA: el plan prestado arranca ah\xED] [--redeem-by FECHA: \xFAltimo d\xEDa para canjear] [--convert-promo C\xD3DIGO_STRIPE: se ofrece al terminar el plan] [--on|--off] [--note]
|
|
17007
|
+
coupons show <C\xD3DIGO> Qui\xE9n lo canje\xF3: ventana del plan (por empezar/activo/termin\xF3) y qui\xE9n ya paga un plan (staff)
|
|
16879
17008
|
coupons redeem <C\xD3DIGO> Canjea un cup\xF3n para tu cuenta (tambi\xE9n /canjear?codigo=C\xD3DIGO)
|
|
16880
17009
|
ads watch [ls|run] Vig\xEDa de campa\xF1as (staff): campa\xF1as de Meta que dejan de entregar y n\xFAmeros \xABs\xF3lo anuncios\xBB sin leads en 24 h [run --dry-run: lo que avisar\xEDa]
|
|
16881
17010
|
audit ls La bit\xE1cora: qui\xE9n cambi\xF3 qu\xE9 (ficha, canales, pausas, respuestas, traspasos, \xABentrar como\xBB) [--workspace SLUG] [--agent ID] [--actor CORREO] [--action A] [--since 24h] [--limit N]
|
|
@@ -17014,6 +17143,12 @@ ${bold("Turnos programados")} ${dim("(cada corrida cae en una conversaci\xF3n y
|
|
|
17014
17143
|
schedule rm <id> <schedule-id> Cancela un turno pendiente
|
|
17015
17144
|
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
|
|
17016
17145
|
|
|
17146
|
+
${bold("Vigilar p\xE1ginas")} ${dim("(se revisan sin turno del modelo; el agente s\xF3lo recibe turno cuando la p\xE1gina cambi\xF3)")}
|
|
17147
|
+
watch ls <id> P\xE1ginas vigiladas: estado, \xFAltimo cambio, conversaci\xF3n
|
|
17148
|
+
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]
|
|
17149
|
+
watch check <id> <watch-id> Rev\xEDsala en el siguiente barrido [--dry-run: baja y compara ya, sin guardar ni avisar]
|
|
17150
|
+
watch rm|resume <id> <watch-id> Deja de vigilarla \xB7 reanuda una pausada (o renueva una vencida) [--days N]
|
|
17151
|
+
|
|
17017
17152
|
${bold("Teams")}
|
|
17018
17153
|
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]
|
|
17019
17154
|
|
|
@@ -17049,7 +17184,7 @@ ${bold("Opciones globales")}
|
|
|
17049
17184
|
${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
|
|
17050
17185
|
${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.")}
|
|
17051
17186
|
${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
|
|
17052
|
-
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"];
|
|
17187
|
+
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", "me", "channels", "board", "boards", "access", "credentials", "connectors", "browser", "support", "docs", "help"];
|
|
17053
17188
|
var AGENTS_SUBS = ["ls", "create", "get", "show", "set", "export", "doctor", "docs", "share", "unshare", "shares", "teams", "rm", "restart", "demo", "embed", "box", "token", "memory", "transfer"];
|
|
17054
17189
|
function unknown(what, input, known, helpCmd) {
|
|
17055
17190
|
const s2 = suggest(input, known);
|
|
@@ -17114,9 +17249,13 @@ async function main() {
|
|
|
17114
17249
|
at: { type: "string" },
|
|
17115
17250
|
in: { type: "string" },
|
|
17116
17251
|
every: { type: "string" },
|
|
17252
|
+
focus: { type: "string" },
|
|
17253
|
+
instruction: { type: "string" },
|
|
17117
17254
|
until: { type: "string" },
|
|
17118
17255
|
credit: { type: "string" },
|
|
17119
17256
|
eligibility: { type: "string" },
|
|
17257
|
+
"redeem-by": { type: "string" },
|
|
17258
|
+
"convert-promo": { type: "string" },
|
|
17120
17259
|
title: { type: "string" },
|
|
17121
17260
|
body: { type: "string" },
|
|
17122
17261
|
profile: { type: "string" },
|
|
@@ -17362,7 +17501,7 @@ async function main() {
|
|
|
17362
17501
|
const tokens = splitTokens(pos, v);
|
|
17363
17502
|
globals.token = tokens.session;
|
|
17364
17503
|
globals.profile = v.profile;
|
|
17365
|
-
if (v.version) return out({ version: "0.
|
|
17504
|
+
if (v.version) return out({ version: "0.85.0" }, () => "0.85.0");
|
|
17366
17505
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
17367
17506
|
const context = (await readConfig()).workspace;
|
|
17368
17507
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -17497,6 +17636,8 @@ async function main() {
|
|
|
17497
17636
|
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>"));
|
|
17498
17637
|
case "schedule":
|
|
17499
17638
|
return scheduleCmd(sub, a1, a2, v);
|
|
17639
|
+
case "watch":
|
|
17640
|
+
return watchCmd(sub, a1, a2, v);
|
|
17500
17641
|
case "teams":
|
|
17501
17642
|
return teamsCmd(sub, a1, a2, v);
|
|
17502
17643
|
case "browser":
|
|
@@ -17561,7 +17702,7 @@ async function main() {
|
|
|
17561
17702
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
17562
17703
|
}
|
|
17563
17704
|
}
|
|
17564
|
-
var notice = updateNotice("0.
|
|
17705
|
+
var notice = updateNotice("0.85.0").catch(() => null);
|
|
17565
17706
|
main().then(
|
|
17566
17707
|
async () => {
|
|
17567
17708
|
const n = await notice;
|