@ghostystudio/cli 0.39.0 → 0.41.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 +10 -0
- package/dist/index.js +254 -74
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ ghosty apply <file|dir> [--agent X] [--create] [--prune] [--dry-run] # make an
|
|
|
30
30
|
ghosty agents set <id> [--name] [--engine] [--model] [--prompt | --prompt-file FILE] [--env K=V,…] [--connect ID,…] [--disconnect ID,…] [--toolsets +crm]
|
|
31
31
|
ghosty agents set <id> --prompt-base <base-id> --prompt-overlay overlay.json [--dry-run] # prompt = another agent's + edits, kept in sync
|
|
32
32
|
ghosty agents share|unshare <id> <email> [--role editor|admin] # share without handing it over · agents shares <id> lists them
|
|
33
|
+
ghosty agents teams ls|on|off <id> [workspace] [--handle x] # which Teams workspaces it answers in, and its @handle there
|
|
33
34
|
ghosty agents rm <id> [--from-workspace] [--yes] # --from-workspace: turn it off in Teams first
|
|
34
35
|
ghosty agents set <id> [--tool wa:tool=off,…] [--bag NAME|none] [--avatar URL|FILE]
|
|
35
36
|
ghosty agents restart <id>
|
|
@@ -83,6 +84,7 @@ ghosty conversations ls --user EMAIL [--since 30d] · conversations show <id> <c
|
|
|
83
84
|
ghosty turns stats [--since 14d] [--all] · push log --user EMAIL [--since 2d]
|
|
84
85
|
ghosty agents doctor --user EMAIL [agent] [--turns 8] · usage --images [--plan free] [--by user]
|
|
85
86
|
ghosty novedades ls|create|set|test|send|rm [--title] [--body] [--image FILE] [--link] [--to] [--publish] [--at]
|
|
87
|
+
ghosty sites ls [--house] · sites publish <file.html|folder> [--title] [--slug S] [--site <slug|id>] · sites slug <slug|id> <new-slug> # house sites on EasyBits
|
|
86
88
|
ghosty credentials ls|set|rm [provider] [--from-env VAR] # never as an argument
|
|
87
89
|
ghosty docs
|
|
88
90
|
|
|
@@ -99,6 +101,14 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
|
|
|
99
101
|
|
|
100
102
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
101
103
|
|
|
104
|
+
### Changes in 0.41.0
|
|
105
|
+
|
|
106
|
+
- `agents teams ls <id>`: the Teams workspaces you administer, with the `@handle` it answers with in each (or the suggested one). `agents teams on <id> <workspace> [--handle x]` turns it on there, or changes its handle if it was already on; `agents teams off <id> <workspace>` turns it off (the agent stays). Same rules as the agent page (Channels → Ghosty Teams): `@ghosty` is reserved, a taken handle is rejected.
|
|
107
|
+
|
|
108
|
+
### Changes in 0.40.0
|
|
109
|
+
|
|
110
|
+
- `sites ls [--house]`: the sites your agents published (or, staff, Ghosty's house sites). `sites publish <file.html|folder> [--title] [--slug S] [--site <slug|id>]` (staff) publishes a house site on EasyBits — no owner, no expiry, no banner; `--site` updates one. `sites slug <slug|id> <new-slug>` (staff) changes a site's slug. Slug errors come with a code: `slug_invalid`, `slug_taken`, `slug_unsupported` (EasyBits doesn't accept a slug yet; nothing changes, exit 1).
|
|
111
|
+
|
|
102
112
|
### Changes in 0.39.0
|
|
103
113
|
|
|
104
114
|
- `spaces transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys]` (staff): hands a workspace to a new owner in one step. Its agents go with it and spend the new owner's key, so `--copy-keys` copies the ones they lack (the dry run lists what's missing); the previous owner keeps the role you pick, and the agents' boxes are recycled so the new owner shows up everywhere.
|
package/dist/index.js
CHANGED
|
@@ -95,7 +95,7 @@ var require_visit = __commonJS({
|
|
|
95
95
|
"use strict";
|
|
96
96
|
var identity = require_identity();
|
|
97
97
|
var BREAK = /* @__PURE__ */ Symbol("break visit");
|
|
98
|
-
var
|
|
98
|
+
var SKIP2 = /* @__PURE__ */ Symbol("skip children");
|
|
99
99
|
var REMOVE = /* @__PURE__ */ Symbol("remove node");
|
|
100
100
|
function visit(node, visitor) {
|
|
101
101
|
const visitor_ = initVisitor(visitor);
|
|
@@ -107,7 +107,7 @@ var require_visit = __commonJS({
|
|
|
107
107
|
visit_(null, node, visitor_, Object.freeze([]));
|
|
108
108
|
}
|
|
109
109
|
visit.BREAK = BREAK;
|
|
110
|
-
visit.SKIP =
|
|
110
|
+
visit.SKIP = SKIP2;
|
|
111
111
|
visit.REMOVE = REMOVE;
|
|
112
112
|
function visit_(key, node, visitor, path) {
|
|
113
113
|
const ctrl = callVisitor(key, node, visitor, path);
|
|
@@ -155,7 +155,7 @@ var require_visit = __commonJS({
|
|
|
155
155
|
await visitAsync_(null, node, visitor_, Object.freeze([]));
|
|
156
156
|
}
|
|
157
157
|
visitAsync.BREAK = BREAK;
|
|
158
|
-
visitAsync.SKIP =
|
|
158
|
+
visitAsync.SKIP = SKIP2;
|
|
159
159
|
visitAsync.REMOVE = REMOVE;
|
|
160
160
|
async function visitAsync_(key, node, visitor, path) {
|
|
161
161
|
const ctrl = await callVisitor(key, node, visitor, path);
|
|
@@ -3122,9 +3122,9 @@ var require_timestamp = __commonJS({
|
|
|
3122
3122
|
const match = str.match(timestamp.test);
|
|
3123
3123
|
if (!match)
|
|
3124
3124
|
throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd");
|
|
3125
|
-
const [, year, month,
|
|
3125
|
+
const [, year, month, day2, hour, minute, second] = match.map(Number);
|
|
3126
3126
|
const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0;
|
|
3127
|
-
let date = Date.UTC(year, month - 1,
|
|
3127
|
+
let date = Date.UTC(year, month - 1, day2, hour || 0, minute || 0, second || 0, millisec);
|
|
3128
3128
|
const tz = match[8];
|
|
3129
3129
|
if (tz && tz !== "Z") {
|
|
3130
3130
|
let d = parseSexagesimal(tz, false);
|
|
@@ -3995,10 +3995,10 @@ var require_resolve_block_map = __commonJS({
|
|
|
3995
3995
|
let offset = bm.offset;
|
|
3996
3996
|
let commentEnd = null;
|
|
3997
3997
|
for (const collItem of bm.items) {
|
|
3998
|
-
const { start, key, sep, value } = collItem;
|
|
3998
|
+
const { start, key, sep: sep2, value } = collItem;
|
|
3999
3999
|
const keyProps = resolveProps.resolveProps(start, {
|
|
4000
4000
|
indicator: "explicit-key-ind",
|
|
4001
|
-
next: key ??
|
|
4001
|
+
next: key ?? sep2?.[0],
|
|
4002
4002
|
offset,
|
|
4003
4003
|
onError,
|
|
4004
4004
|
parentIndent: bm.indent,
|
|
@@ -4012,7 +4012,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4012
4012
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
4013
4013
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
4014
4014
|
}
|
|
4015
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
4015
|
+
if (!keyProps.anchor && !keyProps.tag && !sep2) {
|
|
4016
4016
|
commentEnd = keyProps.end;
|
|
4017
4017
|
if (keyProps.comment) {
|
|
4018
4018
|
if (map.comment)
|
|
@@ -4036,7 +4036,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4036
4036
|
ctx.atKey = false;
|
|
4037
4037
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
4038
4038
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
4039
|
-
const valueProps = resolveProps.resolveProps(
|
|
4039
|
+
const valueProps = resolveProps.resolveProps(sep2 ?? [], {
|
|
4040
4040
|
indicator: "map-value-ind",
|
|
4041
4041
|
next: value,
|
|
4042
4042
|
offset: keyNode.range[2],
|
|
@@ -4052,7 +4052,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4052
4052
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
4053
4053
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
4054
4054
|
}
|
|
4055
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
4055
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep2, null, valueProps, onError);
|
|
4056
4056
|
if (ctx.schema.compat)
|
|
4057
4057
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
4058
4058
|
offset = valueNode.range[2];
|
|
@@ -4143,7 +4143,7 @@ var require_resolve_end = __commonJS({
|
|
|
4143
4143
|
let comment = "";
|
|
4144
4144
|
if (end) {
|
|
4145
4145
|
let hasSpace = false;
|
|
4146
|
-
let
|
|
4146
|
+
let sep2 = "";
|
|
4147
4147
|
for (const token of end) {
|
|
4148
4148
|
const { source, type } = token;
|
|
4149
4149
|
switch (type) {
|
|
@@ -4157,13 +4157,13 @@ var require_resolve_end = __commonJS({
|
|
|
4157
4157
|
if (!comment)
|
|
4158
4158
|
comment = cb;
|
|
4159
4159
|
else
|
|
4160
|
-
comment +=
|
|
4161
|
-
|
|
4160
|
+
comment += sep2 + cb;
|
|
4161
|
+
sep2 = "";
|
|
4162
4162
|
break;
|
|
4163
4163
|
}
|
|
4164
4164
|
case "newline":
|
|
4165
4165
|
if (comment)
|
|
4166
|
-
|
|
4166
|
+
sep2 += source;
|
|
4167
4167
|
hasSpace = true;
|
|
4168
4168
|
break;
|
|
4169
4169
|
default:
|
|
@@ -4206,18 +4206,18 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4206
4206
|
let offset = fc.offset + fc.start.source.length;
|
|
4207
4207
|
for (let i = 0; i < fc.items.length; ++i) {
|
|
4208
4208
|
const collItem = fc.items[i];
|
|
4209
|
-
const { start, key, sep, value } = collItem;
|
|
4209
|
+
const { start, key, sep: sep2, value } = collItem;
|
|
4210
4210
|
const props = resolveProps.resolveProps(start, {
|
|
4211
4211
|
flow: fcName,
|
|
4212
4212
|
indicator: "explicit-key-ind",
|
|
4213
|
-
next: key ??
|
|
4213
|
+
next: key ?? sep2?.[0],
|
|
4214
4214
|
offset,
|
|
4215
4215
|
onError,
|
|
4216
4216
|
parentIndent: fc.indent,
|
|
4217
4217
|
startOnNewline: false
|
|
4218
4218
|
});
|
|
4219
4219
|
if (!props.found) {
|
|
4220
|
-
if (!props.anchor && !props.tag && !
|
|
4220
|
+
if (!props.anchor && !props.tag && !sep2 && !value) {
|
|
4221
4221
|
if (i === 0 && props.comma)
|
|
4222
4222
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
4223
4223
|
else if (i < fc.items.length - 1)
|
|
@@ -4271,8 +4271,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4271
4271
|
}
|
|
4272
4272
|
}
|
|
4273
4273
|
}
|
|
4274
|
-
if (!isMap && !
|
|
4275
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
4274
|
+
if (!isMap && !sep2 && !props.found) {
|
|
4275
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep2, null, props, onError);
|
|
4276
4276
|
coll.items.push(valueNode);
|
|
4277
4277
|
offset = valueNode.range[2];
|
|
4278
4278
|
if (isBlock(value))
|
|
@@ -4284,7 +4284,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4284
4284
|
if (isBlock(key))
|
|
4285
4285
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
4286
4286
|
ctx.atKey = false;
|
|
4287
|
-
const valueProps = resolveProps.resolveProps(
|
|
4287
|
+
const valueProps = resolveProps.resolveProps(sep2 ?? [], {
|
|
4288
4288
|
flow: fcName,
|
|
4289
4289
|
indicator: "map-value-ind",
|
|
4290
4290
|
next: value,
|
|
@@ -4295,8 +4295,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4295
4295
|
});
|
|
4296
4296
|
if (valueProps.found) {
|
|
4297
4297
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
4298
|
-
if (
|
|
4299
|
-
for (const st of
|
|
4298
|
+
if (sep2)
|
|
4299
|
+
for (const st of sep2) {
|
|
4300
4300
|
if (st === valueProps.found)
|
|
4301
4301
|
break;
|
|
4302
4302
|
if (st.type === "newline") {
|
|
@@ -4313,7 +4313,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4313
4313
|
else
|
|
4314
4314
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
4315
4315
|
}
|
|
4316
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
4316
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep2, null, valueProps, onError) : null;
|
|
4317
4317
|
if (valueNode) {
|
|
4318
4318
|
if (isBlock(value))
|
|
4319
4319
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -4493,7 +4493,7 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4493
4493
|
chompStart = i + 1;
|
|
4494
4494
|
}
|
|
4495
4495
|
let value = "";
|
|
4496
|
-
let
|
|
4496
|
+
let sep2 = "";
|
|
4497
4497
|
let prevMoreIndented = false;
|
|
4498
4498
|
for (let i = 0; i < contentStart; ++i)
|
|
4499
4499
|
value += lines[i][0].slice(trimIndent) + "\n";
|
|
@@ -4510,24 +4510,24 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4510
4510
|
indent = "";
|
|
4511
4511
|
}
|
|
4512
4512
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
4513
|
-
value +=
|
|
4514
|
-
|
|
4513
|
+
value += sep2 + indent.slice(trimIndent) + content;
|
|
4514
|
+
sep2 = "\n";
|
|
4515
4515
|
} else if (indent.length > trimIndent || content[0] === " ") {
|
|
4516
|
-
if (
|
|
4517
|
-
|
|
4518
|
-
else if (!prevMoreIndented &&
|
|
4519
|
-
|
|
4520
|
-
value +=
|
|
4521
|
-
|
|
4516
|
+
if (sep2 === " ")
|
|
4517
|
+
sep2 = "\n";
|
|
4518
|
+
else if (!prevMoreIndented && sep2 === "\n")
|
|
4519
|
+
sep2 = "\n\n";
|
|
4520
|
+
value += sep2 + indent.slice(trimIndent) + content;
|
|
4521
|
+
sep2 = "\n";
|
|
4522
4522
|
prevMoreIndented = true;
|
|
4523
4523
|
} else if (content === "") {
|
|
4524
|
-
if (
|
|
4524
|
+
if (sep2 === "\n")
|
|
4525
4525
|
value += "\n";
|
|
4526
4526
|
else
|
|
4527
|
-
|
|
4527
|
+
sep2 = "\n";
|
|
4528
4528
|
} else {
|
|
4529
|
-
value +=
|
|
4530
|
-
|
|
4529
|
+
value += sep2 + content;
|
|
4530
|
+
sep2 = " ";
|
|
4531
4531
|
prevMoreIndented = false;
|
|
4532
4532
|
}
|
|
4533
4533
|
}
|
|
@@ -4710,25 +4710,25 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4710
4710
|
trimBoth = /^[ \t]+|[ \t]+$/g;
|
|
4711
4711
|
}
|
|
4712
4712
|
let res = match[1].replace(trimEnd, "");
|
|
4713
|
-
let
|
|
4713
|
+
let sep2 = " ";
|
|
4714
4714
|
let pos = line2.lastIndex;
|
|
4715
4715
|
while (match = line2.exec(source)) {
|
|
4716
4716
|
const lm = match[1].replace(trimBoth, "");
|
|
4717
4717
|
if (lm === "") {
|
|
4718
|
-
if (
|
|
4719
|
-
res +=
|
|
4718
|
+
if (sep2 === "\n")
|
|
4719
|
+
res += sep2;
|
|
4720
4720
|
else
|
|
4721
|
-
|
|
4721
|
+
sep2 = "\n";
|
|
4722
4722
|
} else {
|
|
4723
|
-
res +=
|
|
4724
|
-
|
|
4723
|
+
res += sep2 + lm;
|
|
4724
|
+
sep2 = " ";
|
|
4725
4725
|
}
|
|
4726
4726
|
pos = line2.lastIndex;
|
|
4727
4727
|
}
|
|
4728
4728
|
const last = /[ \t]*(.*)/sy;
|
|
4729
4729
|
last.lastIndex = pos;
|
|
4730
4730
|
match = last.exec(source);
|
|
4731
|
-
return res +
|
|
4731
|
+
return res + sep2 + (match?.[1] ?? "");
|
|
4732
4732
|
}
|
|
4733
4733
|
function doubleQuotedValue(source, onError) {
|
|
4734
4734
|
let res = "";
|
|
@@ -5538,14 +5538,14 @@ var require_cst_stringify = __commonJS({
|
|
|
5538
5538
|
}
|
|
5539
5539
|
}
|
|
5540
5540
|
}
|
|
5541
|
-
function stringifyItem({ start, key, sep, value }) {
|
|
5541
|
+
function stringifyItem({ start, key, sep: sep2, value }) {
|
|
5542
5542
|
let res = "";
|
|
5543
5543
|
for (const st of start)
|
|
5544
5544
|
res += st.source;
|
|
5545
5545
|
if (key)
|
|
5546
5546
|
res += stringifyToken(key);
|
|
5547
|
-
if (
|
|
5548
|
-
for (const st of
|
|
5547
|
+
if (sep2)
|
|
5548
|
+
for (const st of sep2)
|
|
5549
5549
|
res += st.source;
|
|
5550
5550
|
if (value)
|
|
5551
5551
|
res += stringifyToken(value);
|
|
@@ -5560,7 +5560,7 @@ var require_cst_visit = __commonJS({
|
|
|
5560
5560
|
"../../node_modules/yaml/dist/parse/cst-visit.js"(exports) {
|
|
5561
5561
|
"use strict";
|
|
5562
5562
|
var BREAK = /* @__PURE__ */ Symbol("break visit");
|
|
5563
|
-
var
|
|
5563
|
+
var SKIP2 = /* @__PURE__ */ Symbol("skip children");
|
|
5564
5564
|
var REMOVE = /* @__PURE__ */ Symbol("remove item");
|
|
5565
5565
|
function visit(cst, visitor) {
|
|
5566
5566
|
if ("type" in cst && cst.type === "document")
|
|
@@ -5568,7 +5568,7 @@ var require_cst_visit = __commonJS({
|
|
|
5568
5568
|
_visit(Object.freeze([]), cst, visitor);
|
|
5569
5569
|
}
|
|
5570
5570
|
visit.BREAK = BREAK;
|
|
5571
|
-
visit.SKIP =
|
|
5571
|
+
visit.SKIP = SKIP2;
|
|
5572
5572
|
visit.REMOVE = REMOVE;
|
|
5573
5573
|
visit.itemAtPath = (cst, path) => {
|
|
5574
5574
|
let item = cst;
|
|
@@ -6712,18 +6712,18 @@ var require_parser = __commonJS({
|
|
|
6712
6712
|
if (this.type === "map-value-ind") {
|
|
6713
6713
|
const prev = getPrevProps(this.peek(2));
|
|
6714
6714
|
const start = getFirstKeyStartProps(prev);
|
|
6715
|
-
let
|
|
6715
|
+
let sep2;
|
|
6716
6716
|
if (scalar.end) {
|
|
6717
|
-
|
|
6718
|
-
|
|
6717
|
+
sep2 = scalar.end;
|
|
6718
|
+
sep2.push(this.sourceToken);
|
|
6719
6719
|
delete scalar.end;
|
|
6720
6720
|
} else
|
|
6721
|
-
|
|
6721
|
+
sep2 = [this.sourceToken];
|
|
6722
6722
|
const map = {
|
|
6723
6723
|
type: "block-map",
|
|
6724
6724
|
offset: scalar.offset,
|
|
6725
6725
|
indent: scalar.indent,
|
|
6726
|
-
items: [{ start, key: scalar, sep }]
|
|
6726
|
+
items: [{ start, key: scalar, sep: sep2 }]
|
|
6727
6727
|
};
|
|
6728
6728
|
this.onKeyLine = true;
|
|
6729
6729
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -6876,15 +6876,15 @@ var require_parser = __commonJS({
|
|
|
6876
6876
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
6877
6877
|
const start2 = getFirstKeyStartProps(it.start);
|
|
6878
6878
|
const key = it.key;
|
|
6879
|
-
const
|
|
6880
|
-
|
|
6879
|
+
const sep2 = it.sep;
|
|
6880
|
+
sep2.push(this.sourceToken);
|
|
6881
6881
|
delete it.key;
|
|
6882
6882
|
delete it.sep;
|
|
6883
6883
|
this.stack.push({
|
|
6884
6884
|
type: "block-map",
|
|
6885
6885
|
offset: this.offset,
|
|
6886
6886
|
indent: this.indent,
|
|
6887
|
-
items: [{ start: start2, key, sep }]
|
|
6887
|
+
items: [{ start: start2, key, sep: sep2 }]
|
|
6888
6888
|
});
|
|
6889
6889
|
} else if (start.length > 0) {
|
|
6890
6890
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -7078,13 +7078,13 @@ var require_parser = __commonJS({
|
|
|
7078
7078
|
const prev = getPrevProps(parent);
|
|
7079
7079
|
const start = getFirstKeyStartProps(prev);
|
|
7080
7080
|
fixFlowSeqItems(fc);
|
|
7081
|
-
const
|
|
7082
|
-
|
|
7081
|
+
const sep2 = fc.end.splice(1, fc.end.length);
|
|
7082
|
+
sep2.push(this.sourceToken);
|
|
7083
7083
|
const map = {
|
|
7084
7084
|
type: "block-map",
|
|
7085
7085
|
offset: fc.offset,
|
|
7086
7086
|
indent: fc.indent,
|
|
7087
|
-
items: [{ start, key: fc, sep }]
|
|
7087
|
+
items: [{ start, key: fc, sep: sep2 }]
|
|
7088
7088
|
};
|
|
7089
7089
|
this.onKeyLine = true;
|
|
7090
7090
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -7458,9 +7458,18 @@ var SERVER_MESSAGES = [
|
|
|
7458
7458
|
["s\xF3lo el due\xF1o da acceso a sus conectores", "only the owner grants access to their connectors"],
|
|
7459
7459
|
["s\xF3lo el due\xF1o del agente maneja su demo", "only the agent's owner manages its demo"],
|
|
7460
7460
|
["s\xF3lo el due\xF1o puede borrar el agente", "only the owner can delete the agent"],
|
|
7461
|
-
["Ese agente est\xE1 activo en {}. Qu\xEDtalo de ah\xED (Ajustes \u2192 Agentes) antes de borrarlo.", "That agent is active in {}. Remove it from there (Settings \u2192 Agents) before deleting it."],
|
|
7462
7461
|
["Ese espacio no es tuyo.", "That workspace isn't yours."],
|
|
7463
7462
|
["@{} ya est\xE1 ocupado por otro agente en este espacio: dale un handle propio", "@{} is already taken by another agent in this workspace: give it its own handle"],
|
|
7463
|
+
// — Activar en equipos de Teams (`agents teams`)
|
|
7464
|
+
["@{} ya est\xE1 ocupado en {}", "@{} is already taken in {}"],
|
|
7465
|
+
["@{} est\xE1 reservado", "@{} is reserved"],
|
|
7466
|
+
["los @handle {} a {}5 ya est\xE1n ocupados en {}", "handles @{} to @{}5 are already taken in {}"],
|
|
7467
|
+
["no administras el equipo {}", "you don't administer the workspace {}"],
|
|
7468
|
+
["no pude leer el equipo {}; int\xE9ntalo en un momento", "couldn't read the workspace {}; try again in a moment"],
|
|
7469
|
+
["s\xF3lo el due\xF1o del agente lo activa en sus equipos", "only the agent's owner turns it on in their workspaces"],
|
|
7470
|
+
["s\xF3lo el due\xF1o del agente o staff lo activan en Teams", "only the agent's owner or staff turn it on in Teams"],
|
|
7471
|
+
["falta workspace (el slug del equipo)", "missing workspace (the workspace slug)"],
|
|
7472
|
+
["Ese agente est\xE1 activo en {}. Ap\xE1galo ah\xED (Canales \u2192 Ghosty Teams) antes de borrarlo.", "That agent is active in {}. Turn it off there (Channels \u2192 Ghosty Teams) before deleting it."],
|
|
7464
7473
|
["sin cupo para otro agente ahora", "no room for another agent right now"],
|
|
7465
7474
|
["Tu plan incluye 1 agente.", "Your plan includes 1 agent."],
|
|
7466
7475
|
["Tu plan incluye {} agentes.", "Your plan includes {} agents."],
|
|
@@ -8088,6 +8097,29 @@ var SERVER_MESSAGES = [
|
|
|
8088
8097
|
["turnToken excede {} caracteres", "turnToken exceeds {} characters"],
|
|
8089
8098
|
["turnToken no puede llevar espacios", "turnToken can't contain whitespace"],
|
|
8090
8099
|
["turnToken requerido: los MCP de este agente usan ${turn.token}", "turnToken required: this agent's MCP servers use ${turn.token}"],
|
|
8100
|
+
// — Sitios de la casa (`ghosty sites`, 3-oct)
|
|
8101
|
+
["los sitios de la casa: s\xF3lo el staff de Ghosty", "house sites: Ghosty staff only"],
|
|
8102
|
+
["publicar sitios de la casa: s\xF3lo el staff de Ghosty", "publishing house sites: Ghosty staff only"],
|
|
8103
|
+
["cambiar el slug de un sitio: s\xF3lo el staff de Ghosty", "changing a site's slug: Ghosty staff only"],
|
|
8104
|
+
["scope acepta mine o house", "scope takes mine or house"],
|
|
8105
|
+
["falta html o files", "missing html or files"],
|
|
8106
|
+
["falta slug", "missing slug"],
|
|
8107
|
+
["EasyBits todav\xEDa no permite elegir ni cambiar el slug", "EasyBits doesn't allow choosing or changing the slug yet"],
|
|
8108
|
+
["el slug \xAB{}\xBB ya est\xE1 ocupado en EasyBits", "slug \xAB{}\xBB is already taken on EasyBits"],
|
|
8109
|
+
["el slug \xAB{}\xBB ya est\xE1 ocupado", "slug \xAB{}\xBB is already taken"],
|
|
8110
|
+
["EasyBits rechaz\xF3 el slug \xAB{}\xBB por inv\xE1lido", "EasyBits rejected slug \xAB{}\xBB as invalid"],
|
|
8111
|
+
["el slug \xAB{}\xBB est\xE1 reservado", "slug \xAB{}\xBB is reserved"],
|
|
8112
|
+
["el slug debe tener entre 3 y 60 caracteres", "the slug must be 3 to 60 characters"],
|
|
8113
|
+
["el slug s\xF3lo admite min\xFAsculas (a-z), n\xFAmeros y guiones", "the slug only allows lowercase letters (a-z), digits and hyphens"],
|
|
8114
|
+
["el slug no puede empezar ni terminar en guion", "the slug can't start or end with a hyphen"],
|
|
8115
|
+
["el slug no puede llevar dos guiones seguidos", "the slug can't have two hyphens in a row"],
|
|
8116
|
+
["no encontr\xE9 un sitio vivo \xAB{}\xBB; publica sin `site` para crear uno nuevo", "no live site \xAB{}\xBB; publish without `site` to create a new one"],
|
|
8117
|
+
["no encontr\xE9 un sitio vivo \xAB{}\xBB", "no live site \xAB{}\xBB"],
|
|
8118
|
+
["ese sitio no es de la casa (s\xF3lo se actualizan con `--site` los publicados como casa)", "that site isn't a house site (--site only updates sites published as house)"],
|
|
8119
|
+
["la publicaci\xF3n de sitios no est\xE1 disponible en este momento", "site publishing isn't available right now"],
|
|
8120
|
+
["no pude conectar con el servicio de sitios ({})", "couldn't reach the sites service ({})"],
|
|
8121
|
+
["no se pudo crear el sitio; int\xE9ntalo en un momento", "couldn't create the site; try again in a moment"],
|
|
8122
|
+
["no se pudo subir {}: {}", "couldn't upload {}: {}"],
|
|
8091
8123
|
// — Infra interna (casi nunca llega al CLI, pero por si acaso)
|
|
8092
8124
|
["SESSION_SECRET ausente en producci\xF3n \u2014 las cookies quedar\xEDan firmadas con un valor p\xFAblico. Inyectarlo con ./scripts/set_gs_secret.sh SESSION_SECRET (openssl rand -hex 32).", "SESSION_SECRET missing in production \u2014 cookies would be signed with a public value. Inject it with ./scripts/set_gs_secret.sh SESSION_SECRET (openssl rand -hex 32)."],
|
|
8093
8125
|
["sqld: fetch sin namespace", "sqld: fetch without namespace"],
|
|
@@ -8390,7 +8422,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8390
8422
|
const req = httpsRequest(`${base}${path}`, {
|
|
8391
8423
|
method: "POST",
|
|
8392
8424
|
timeout: timeoutMs,
|
|
8393
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8425
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.41.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8394
8426
|
}, (res) => {
|
|
8395
8427
|
let buf = "";
|
|
8396
8428
|
res.on("data", (c) => buf += c);
|
|
@@ -8432,7 +8464,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8432
8464
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8433
8465
|
headers: {
|
|
8434
8466
|
authorization: `Bearer ${token}`,
|
|
8435
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8467
|
+
"user-agent": `ghosty-cli/${"0.41.0"}`,
|
|
8436
8468
|
"accept-language": lang(),
|
|
8437
8469
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8438
8470
|
...opts.headers
|
|
@@ -8514,12 +8546,12 @@ var enc = encodeURIComponent;
|
|
|
8514
8546
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
8515
8547
|
var MIME = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", webp: "image/webp", gif: "image/gif", pdf: "application/pdf", ogg: "audio/ogg", opus: "audio/ogg", mp3: "audio/mpeg", m4a: "audio/mp4", wav: "audio/wav", mp4: "video/mp4" };
|
|
8516
8548
|
async function localFiles(paths) {
|
|
8517
|
-
const { readFile:
|
|
8518
|
-
const { basename:
|
|
8549
|
+
const { readFile: readFile15 } = await import("node:fs/promises");
|
|
8550
|
+
const { basename: basename5 } = await import("node:path");
|
|
8519
8551
|
return Promise.all(
|
|
8520
8552
|
paths.map(async (p) => {
|
|
8521
8553
|
const ext = p.toLowerCase().split(".").pop() ?? "";
|
|
8522
|
-
return { name:
|
|
8554
|
+
return { name: basename5(p), mimeType: MIME[ext] ?? "application/octet-stream", data: (await readFile15(p)).toString("base64") };
|
|
8523
8555
|
})
|
|
8524
8556
|
);
|
|
8525
8557
|
}
|
|
@@ -8877,6 +8909,27 @@ ${rows.length ? table(rows, ["email", "role", "accepted"]) : dim(t("Not shared w
|
|
|
8877
8909
|
const r = await api(agentPath(id, "/shares"), { method: "POST", body: { email, ...v.role ? { role: v.role } : {} } });
|
|
8878
8910
|
out(r, () => `${lavender("\u2713")} ${r.already ? t(`Already shared with ${r.email}`, `Ya estaba compartido con ${r.email}`) : t(`Shared with ${r.email}; they got an email`, `Compartido con ${r.email}; le lleg\xF3 un correo`)} ${dim(`(${r.role})`)}.`);
|
|
8879
8911
|
}
|
|
8912
|
+
async function agentsTeams(sub, id, ws, v) {
|
|
8913
|
+
const ex = "ghosty agents teams ls <agent-id> \xB7 on <agent-id> <workspace> [--handle x] \xB7 off <agent-id> <workspace>";
|
|
8914
|
+
if (sub !== "ls" && sub !== "on" && sub !== "off") throw usage(t("Use ls, on or off.", "Usa ls, on u off."), ex);
|
|
8915
|
+
id = need(id, "agent id", ex);
|
|
8916
|
+
if (sub === "ls") {
|
|
8917
|
+
const r2 = await api(agentPath(id, "/teams"));
|
|
8918
|
+
const rows = r2.teams.map((x) => ({
|
|
8919
|
+
workspace: x.slug,
|
|
8920
|
+
handle: x.active ? `@${x.handle}` : dim(`(@${x.suggested})`),
|
|
8921
|
+
state: !x.confirmed ? t("unconfirmed", "sin confirmar") : x.active ? t("on", "prendido") : t("off", "apagado")
|
|
8922
|
+
}));
|
|
8923
|
+
return out(r2, () => rows.length ? table(rows, ["workspace", "handle", "state"]) : dim(t("You don't administer any Teams workspace yet.", "A\xFAn no administras ning\xFAn equipo de Teams.")));
|
|
8924
|
+
}
|
|
8925
|
+
const workspace = need(ws ?? v.workspace, "workspace", ex);
|
|
8926
|
+
if (sub === "off") {
|
|
8927
|
+
const r2 = await api(agentPath(id, `/teams?workspace=${encodeURIComponent(workspace)}`), { method: "DELETE", body: { workspace } });
|
|
8928
|
+
return out(r2, () => `${lavender("\u2713")} ${r2.wasActive ? t(`It no longer answers in ${workspace}.`, `Ya no contesta en ${workspace}.`) : t(`It wasn't active in ${workspace}.`, `No estaba activo en ${workspace}.`)}`);
|
|
8929
|
+
}
|
|
8930
|
+
const r = await api(agentPath(id, "/teams"), { method: "POST", body: { workspace, ...v.handle ? { handle: v.handle } : {} } });
|
|
8931
|
+
out(r, () => `${lavender("\u2713")} ${r.changed ? t(`Answers in ${workspace} as @${r.handle}.`, `Contesta en ${workspace} como @${r.handle}.`) : t(`Already answering in ${workspace} as @${r.handle}.`, `Ya contestaba en ${workspace} como @${r.handle}.`)}`);
|
|
8932
|
+
}
|
|
8880
8933
|
async function agentsRestart(id) {
|
|
8881
8934
|
id = need(id, "agent id", "ghosty agents restart <agent-id>");
|
|
8882
8935
|
out(await api(agentPath(id, "/restart"), { method: "POST", body: {} }), () => `${lavender("\u2713")} ${t("Restarting \u2014 the next turn uses the new config.", "Reiniciando: el siguiente turno usa la config nueva.")}`);
|
|
@@ -9618,7 +9671,7 @@ async function call(path, v, init = {}) {
|
|
|
9618
9671
|
"x-ghosty-ts": ts,
|
|
9619
9672
|
"x-ghosty-sig": sig,
|
|
9620
9673
|
"content-type": "application/json",
|
|
9621
|
-
"user-agent": `ghosty-cli/${"0.
|
|
9674
|
+
"user-agent": `ghosty-cli/${"0.41.0"}`
|
|
9622
9675
|
},
|
|
9623
9676
|
body: method === "GET" ? void 0 : raw
|
|
9624
9677
|
});
|
|
@@ -10926,7 +10979,10 @@ var HELPS_ES = {
|
|
|
10926
10979
|
["# the agent as a file; see ghosty apply --help", "# el agente como archivo; ver ghosty apply --help"],
|
|
10927
10980
|
["# your account: merge copies with the same content", "# tu cuenta: une las copias con el mismo contenido"],
|
|
10928
10981
|
["# staff: their agents, latest turns, plan and boxes", "# staff: sus agentes, \xFAltimos turnos, plan y cajas"],
|
|
10929
|
-
["# what it really has: effective toolsets, connectors connected + granted", "# lo que de verdad tiene: toolsets efectivos, conectores conectados + concedidos"]
|
|
10982
|
+
["# what it really has: effective toolsets, connectors connected + granted", "# lo que de verdad tiene: toolsets efectivos, conectores conectados + concedidos"],
|
|
10983
|
+
["# Teams workspaces you administer: where it answers and with which @handle", "# los equipos de Teams que administras: d\xF3nde contesta y con qu\xE9 @handle"],
|
|
10984
|
+
["# turn it on there (or change its @handle)", "# lo prende ah\xED (o le cambia el @handle)"],
|
|
10985
|
+
["# turn it off there; the agent stays", "# lo apaga ah\xED; el agente sigue"]
|
|
10930
10986
|
],
|
|
10931
10987
|
flags: [
|
|
10932
10988
|
["--fields a,b", "get: regresa s\xF3lo estos campos"],
|
|
@@ -10953,6 +11009,7 @@ var HELPS_ES = {
|
|
|
10953
11009
|
["--grant read|write|no", "docs add: acceso de este agente (default read; no = s\xF3lo subirlo a la cuenta)"],
|
|
10954
11010
|
["--note TEXT", "docs replace/restore: por qu\xE9 cambi\xF3 (sale en docs history)"],
|
|
10955
11011
|
["--role R", "share: editor (lo configura y lo usa) o admin (adem\xE1s lo comparte); repetirlo sobre un correo ya compartido cambia el rol"],
|
|
11012
|
+
["--handle NAME", "teams on: su @handle en ese equipo (default: sacado de su nombre, con sufijo si est\xE1 ocupado; @ghosty est\xE1 reservado). Sobre un equipo donde ya est\xE1, le cambia el handle"],
|
|
10956
11013
|
["--yes", "rm: se salta la confirmaci\xF3n (obligatorio sin terminal)"],
|
|
10957
11014
|
["--slug NAME", "demo: nombre de la liga; se le agrega un c\xF3digo de 6 caracteres (/p/NAME-CODE)"],
|
|
10958
11015
|
["--expires DATE|no", 'demo: caducidad fija en la hora de tu m\xE1quina ("2026-10-15T20:00"); no = nunca'],
|
|
@@ -11272,6 +11329,16 @@ var HELPS_ES = {
|
|
|
11272
11329
|
summary: "Staff de Ghosty: las novedades de la app Android. Cada una con su ilustraci\xF3n; mismos frenos que el admin: pru\xE9bala en tu tel\xE9fono primero, push de 9 a 21 h CDMX, 72 h entre env\xEDos a todos.",
|
|
11273
11330
|
notes: ["ls dice, de una ya enviada, cu\xE1ntos entreg\xF3 y rechaz\xF3 FCM (del push log, desde el 1-oct).", "--image se publica en img.ghosty.studio (WebP 1200 px). --to vac\xEDo = todos. send sin --at la manda ya (pregunta); --at '' quita la programaci\xF3n.", "--min-version N (versionCode de Android): si la app del tester es m\xE1s vieja, el bot\xF3n dice \xABActualizar\xBB y lanza la actualizaci\xF3n de Play desde la app; '' la quita."]
|
|
11274
11331
|
},
|
|
11332
|
+
sites: {
|
|
11333
|
+
summary: "Sitios est\xE1ticos publicados en EasyBits con la cuenta de casa de Ghosty. ls lista los que publicaron tus agentes; el staff publica los artefactos de Ghosty (demos, landings) como sitios de la casa \u2014 sin due\xF1o, sin caducidad, sin la franja \xABHecha con Ghosty\xBB \u2014 y cambia el slug de cualquier sitio.",
|
|
11334
|
+
flags: [
|
|
11335
|
+
["--house", "ls (staff): los sitios de la casa en vez de los tuyos"],
|
|
11336
|
+
["--title T", "publish: el nombre del sitio (default: el <title> del HTML o el nombre de la carpeta)"],
|
|
11337
|
+
["--slug S", "publish: el slug de un sitio NUEVO (a-z, 0-9 y guiones, 3\u201360, no reservado)"],
|
|
11338
|
+
["--site X", "publish: actualiza ese sitio de la casa (slug, id o URL) en vez de crear uno"]
|
|
11339
|
+
],
|
|
11340
|
+
notes: ["Una carpeta necesita index.html en la ra\xEDz; se saltan ocultos y node_modules. Topes: 1 MB por archivo, 8 MB y 40 archivos por sitio.", "Sin --site, publish siempre crea un sitio nuevo (los de la casa nunca se buscan por t\xEDtulo).", "Errores de slug: slug_invalid, slug_taken, y slug_unsupported mientras EasyBits no acepte slug (exit 1, no cambia nada)."]
|
|
11341
|
+
},
|
|
11275
11342
|
spaces: {
|
|
11276
11343
|
summary: "Espacios: crea uno a tu nombre (prueba), lista los tuyos (o, staff, los de cualquiera) y operaciones del staff de Ghosty \u2014 entrar/salir del espacio de un cliente, cambiar su combo, mover \xABpagado hasta\xBB, renombrar su slug, cambiar de due\xF1o. Cada cambio queda en la bit\xE1cora del espacio.",
|
|
11277
11344
|
flags: [
|
|
@@ -11316,6 +11383,9 @@ var HELPS = {
|
|
|
11316
11383
|
"ghosty agents share <agent-id> <email> [--role editor|admin]",
|
|
11317
11384
|
"ghosty agents unshare <agent-id> <email> [--yes]",
|
|
11318
11385
|
"ghosty agents shares <agent-id>",
|
|
11386
|
+
"ghosty agents teams ls <agent-id> # Teams workspaces you administer: where it answers and with which @handle",
|
|
11387
|
+
"ghosty agents teams on <agent-id> <workspace> [--handle NAME] # turn it on there (or change its @handle)",
|
|
11388
|
+
"ghosty agents teams off <agent-id> <workspace> # turn it off there; the agent stays",
|
|
11319
11389
|
"ghosty agents restart <agent-id>",
|
|
11320
11390
|
"ghosty agents box <agent-id> [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle]",
|
|
11321
11391
|
"ghosty agents demo enable|disable <agent-id>",
|
|
@@ -11347,6 +11417,7 @@ var HELPS = {
|
|
|
11347
11417
|
["--grant read|write|no", "docs add: access for this agent (default read; no = only upload to the account)"],
|
|
11348
11418
|
["--note TEXT", "docs replace/restore: why it changed (shown in docs history)"],
|
|
11349
11419
|
["--role R", "share: editor (configures and uses it) or admin (also shares it); again on a shared email changes the role"],
|
|
11420
|
+
["--handle NAME", "teams on: its @handle in that workspace (default: from its name, with a suffix if taken; @ghosty is reserved). On an active workspace it changes the handle"],
|
|
11350
11421
|
["--yes", "rm: skip the confirmation (required without a terminal)"],
|
|
11351
11422
|
["--slug NAME", "demo: link name; a 6-char code is appended (/p/NAME-CODE)"],
|
|
11352
11423
|
["--expires DATE|no", `demo: fixed expiry in your machine's time ("2026-10-15T20:00"); no = never`],
|
|
@@ -11383,6 +11454,7 @@ var HELPS = {
|
|
|
11383
11454
|
"ghosty agents create --name Pia --engine claude --workspace ventas --channels teams=off",
|
|
11384
11455
|
"ghosty agents set <id> --toolsets +crm",
|
|
11385
11456
|
"ghosty agents share <id> ana@empresa.com --role admin",
|
|
11457
|
+
"ghosty agents teams on <id> abogados --handle licenciado",
|
|
11386
11458
|
"ghosty agents get <id> --prompt-out PROMPT.md",
|
|
11387
11459
|
"ghosty agents get <id> --fields toolsets,connectors # what it really has: effective toolsets, connectors connected + granted",
|
|
11388
11460
|
"ghosty agents create --name tania-mkt --like <pia-id> --board new --no-notify --dry-run",
|
|
@@ -11871,6 +11943,19 @@ HELPS.spaces = {
|
|
|
11871
11943
|
learn: `${REF}#espacios`
|
|
11872
11944
|
};
|
|
11873
11945
|
HELPS.workspaces = HELPS.spaces;
|
|
11946
|
+
HELPS.sites = {
|
|
11947
|
+
summary: "Static sites published on EasyBits with Ghosty's house account. ls lists the ones your agents published; staff publish Ghosty's own artifacts (demos, landings) as house sites \u2014 no owner, no expiry, no \xABMade with Ghosty\xBB banner \u2014 and change any site's slug.",
|
|
11948
|
+
usage: ["ghosty sites ls [--house]", "ghosty sites publish <file.html|folder> [--title T] [--slug S] [--site <slug|id>]", "ghosty sites slug <slug|id> <new-slug>"],
|
|
11949
|
+
flags: [
|
|
11950
|
+
["--house", "ls (staff): Ghosty's house sites instead of yours"],
|
|
11951
|
+
["--title T", "publish: the site's name (default: the HTML <title> or the folder name)"],
|
|
11952
|
+
["--slug S", "publish: the slug for a NEW site (a-z, 0-9 and hyphens, 3\u201360, not reserved)"],
|
|
11953
|
+
["--site X", "publish: update that house site (slug, id or URL) instead of creating one"]
|
|
11954
|
+
],
|
|
11955
|
+
examples: ["ghosty sites ls --house", 'ghosty sites publish ./melon.html --title "Gelatina de sand\xEDa" --slug gelatina-de-sandia', "ghosty sites publish ./landing/ --site gelatina-de-sandia", "ghosty sites slug arbol-palpitante gelatina-de-sandia"],
|
|
11956
|
+
notes: ["A folder needs index.html at its root; hidden files and node_modules are skipped. Limits: 1 MB per file, 8 MB and 40 files per site.", "Without --site, publish always creates a new site (house sites are never matched by title).", "Slug errors: slug_invalid, slug_taken, and slug_unsupported while EasyBits does not accept a slug yet (exit 1, nothing changes)."],
|
|
11957
|
+
learn: `${REF}#sitios`
|
|
11958
|
+
};
|
|
11874
11959
|
function helpText(cmd) {
|
|
11875
11960
|
const base = HELPS[cmd];
|
|
11876
11961
|
if (!base) return void 0;
|
|
@@ -12476,7 +12561,7 @@ ${dim(t(`${total2} message(s) unanswered for lack of balance in ${r.days} day(s)
|
|
|
12476
12561
|
d.noQuota += x.noQuota;
|
|
12477
12562
|
days.set(x.day, d);
|
|
12478
12563
|
}
|
|
12479
|
-
rows = [...days].map(([
|
|
12564
|
+
rows = [...days].map(([day2, x]) => ({ day: day2, ...fmt2(x) }));
|
|
12480
12565
|
} else rows = r.agents.map((x) => ({ agent: x.agent, ...fmt2(x) }));
|
|
12481
12566
|
const cols = [...by.has("day") ? ["day"] : [], ...by.has("agent") ? ["agent"] : [], "turns", "failed", "tokens", "no balance"];
|
|
12482
12567
|
const total = r.agents.reduce((n, a) => n + a.billable, 0);
|
|
@@ -12601,8 +12686,84 @@ async function demosCmd(sub, a1, rest, v) {
|
|
|
12601
12686
|
throw usage(t(`Unknown demos subcommand: ${sub ?? "(none)"}.`, `Subcomando de demos desconocido: ${sub ?? "(ninguno)"}.`), 'ghosty demos history <agent> \xB7 ghosty demos try <slug> "message"');
|
|
12602
12687
|
}
|
|
12603
12688
|
|
|
12689
|
+
// src/commands/sites.ts
|
|
12690
|
+
import { readdir as readdir5, readFile as readFile13, stat as stat2 } from "node:fs/promises";
|
|
12691
|
+
import { basename as basename4, join as join10, relative as relative2, sep } from "node:path";
|
|
12692
|
+
var TEXT_EXT = /* @__PURE__ */ new Set(["html", "htm", "css", "js", "mjs", "json", "txt", "xml", "svg", "webmanifest", "md"]);
|
|
12693
|
+
var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".DS_Store"]);
|
|
12694
|
+
async function collectSiteFiles(target) {
|
|
12695
|
+
const st = await stat2(target).catch(() => null);
|
|
12696
|
+
if (!st) throw usage(t(`Not found: ${target}`, `No existe: ${target}`));
|
|
12697
|
+
if (st.isFile()) {
|
|
12698
|
+
if (!/\.html?$/i.test(target)) throw usage(t("A single file must be .html (or pass a folder).", "Un archivo suelto debe ser .html (o pasa una carpeta)."));
|
|
12699
|
+
const html = await readFile13(target, "utf8");
|
|
12700
|
+
return { files: [{ path: "index.html", content: html, encoding: "text" }], title: titleOf(html) };
|
|
12701
|
+
}
|
|
12702
|
+
const files = [];
|
|
12703
|
+
const walk = async (dir) => {
|
|
12704
|
+
for (const e of await readdir5(dir, { withFileTypes: true })) {
|
|
12705
|
+
if (e.name.startsWith(".") || SKIP.has(e.name)) continue;
|
|
12706
|
+
const full = join10(dir, e.name);
|
|
12707
|
+
if (e.isDirectory()) await walk(full);
|
|
12708
|
+
else if (e.isFile()) {
|
|
12709
|
+
const path = relative2(target, full).split(sep).join("/");
|
|
12710
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
12711
|
+
const buf = await readFile13(full);
|
|
12712
|
+
files.push(TEXT_EXT.has(ext) ? { path, content: buf.toString("utf8"), encoding: "text" } : { path, content: buf.toString("base64"), encoding: "base64" });
|
|
12713
|
+
}
|
|
12714
|
+
}
|
|
12715
|
+
};
|
|
12716
|
+
await walk(target);
|
|
12717
|
+
const index = files.find((f) => f.path === "index.html");
|
|
12718
|
+
if (!index) throw usage(t(`${target} has no index.html at its root.`, `${target} no tiene index.html en la ra\xEDz.`));
|
|
12719
|
+
return { files: files.sort((a, b) => a.path.localeCompare(b.path)), title: titleOf(index.content) ?? basename4(target.replace(/\/+$/, "")) };
|
|
12720
|
+
}
|
|
12721
|
+
function titleOf(html) {
|
|
12722
|
+
const m = html.match(/<title[^>]*>([^<]*)<\/title>/i);
|
|
12723
|
+
const s = m?.[1]?.replace(/\s+/g, " ").trim();
|
|
12724
|
+
return s || null;
|
|
12725
|
+
}
|
|
12726
|
+
var kb = (n) => n ? `${Math.max(1, Math.round(n / 1024))} KB` : "";
|
|
12727
|
+
var day = (iso) => iso ? iso.slice(0, 10) : "";
|
|
12728
|
+
async function sitesCmd(sub, a1, a2, v) {
|
|
12729
|
+
const ex = "ghosty sites publish <file.html|folder> [--title T] [--slug S] [--site <slug|id>] \xB7 ls [--house] \xB7 slug <slug|id> <new-slug>";
|
|
12730
|
+
switch (sub ?? "ls") {
|
|
12731
|
+
case "ls":
|
|
12732
|
+
case "list": {
|
|
12733
|
+
const r = await api(`/api/v2/sites?scope=${v.house ? "house" : "mine"}`);
|
|
12734
|
+
const rows = r.sites.map((s) => ({ slug: s.slug, title: (s.title ?? "").slice(0, 40), url: s.url, files: s.files ?? "", size: kb(s.bytes), published: day(s.publishedAt), expires: s.expiresAt ? day(s.expiresAt) : t("never", "nunca") }));
|
|
12735
|
+
return out(r, () => `${bold(v.house ? t("House sites", "Sitios de la casa") : t("Your sites", "Tus sitios"))}
|
|
12736
|
+
${table(rows, ["slug", "title", "url", "files", "size", "published", "expires"])}`);
|
|
12737
|
+
}
|
|
12738
|
+
case "publish":
|
|
12739
|
+
case "deploy": {
|
|
12740
|
+
if (!a1) throw usage(t("Missing the file or folder.", "Falta el archivo o la carpeta."), ex);
|
|
12741
|
+
const { files, title } = await collectSiteFiles(a1);
|
|
12742
|
+
const body = {
|
|
12743
|
+
title: v.title ?? title ?? "Sitio de Ghosty",
|
|
12744
|
+
...files.length === 1 && files[0].path === "index.html" && files[0].encoding === "text" ? { html: files[0].content } : { files },
|
|
12745
|
+
...v.site ? { site: v.site } : {},
|
|
12746
|
+
...v.slug ? { slug: v.slug } : {}
|
|
12747
|
+
};
|
|
12748
|
+
const r = await api("/api/v2/sites", { method: "POST", body });
|
|
12749
|
+
return out(r, () => `${lavender("\u2713")} ${r.created ? t("Published", "Publicado") : t("Updated", "Actualizado")} ${bold(r.url)}
|
|
12750
|
+
${dim(t(`slug ${r.slug} \xB7 ${files.length} file(s) \xB7 update it: ghosty sites publish ${a1} --site ${r.slug}`, `slug ${r.slug} \xB7 ${files.length} archivo(s) \xB7 actual\xEDzalo: ghosty sites publish ${a1} --site ${r.slug}`))}`);
|
|
12751
|
+
}
|
|
12752
|
+
case "slug":
|
|
12753
|
+
case "rename": {
|
|
12754
|
+
if (!a1 || !a2) throw usage(t("Missing the site and the new slug.", "Faltan el sitio y el slug nuevo."), "ghosty sites slug <slug|id> <new-slug>");
|
|
12755
|
+
const r = await api(`/api/v2/sites/${encodeURIComponent(a1)}`, { method: "PATCH", body: { slug: a2 } });
|
|
12756
|
+
return out(r, () => `${lavender("\u2713")} ${r.oldSlug} \u2192 ${bold(r.slug)}
|
|
12757
|
+
${r.url}
|
|
12758
|
+
${dim(t("Links with the old slug may stop working.", "Las ligas con el slug viejo pueden dejar de funcionar."))}`);
|
|
12759
|
+
}
|
|
12760
|
+
default:
|
|
12761
|
+
throw usage(t(`Unknown sites subcommand: ${sub}.`, `Subcomando de sites desconocido: ${sub}.`), ex);
|
|
12762
|
+
}
|
|
12763
|
+
}
|
|
12764
|
+
|
|
12604
12765
|
// src/commands/support.ts
|
|
12605
|
-
import { readFile as
|
|
12766
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
12606
12767
|
var enc12 = encodeURIComponent;
|
|
12607
12768
|
var cdmx = (iso) => new Date(Date.parse(iso) - 6 * 36e5).toISOString().slice(5, 16).replace("T", " ");
|
|
12608
12769
|
async function supportThreads(email, v) {
|
|
@@ -12712,7 +12873,7 @@ async function novedadesCmd(sub, id, v) {
|
|
|
12712
12873
|
const fields = async () => ({
|
|
12713
12874
|
...v.title !== void 0 ? { title: v.title } : {},
|
|
12714
12875
|
...v.body !== void 0 ? { body: v.body } : {},
|
|
12715
|
-
...v.image ? { image: (await
|
|
12876
|
+
...v.image ? { image: (await readFile14(v.image)).toString("base64") } : {},
|
|
12716
12877
|
...v.link !== void 0 ? { enlace: v.link || null } : {},
|
|
12717
12878
|
...v.button !== void 0 ? { boton: v.button || null } : {},
|
|
12718
12879
|
...v.to !== void 0 ? { destinatarios: v.to || null } : {},
|
|
@@ -12868,6 +13029,7 @@ ${bold("Agents")}
|
|
|
12868
13029
|
agents docs replace <id> <doc> New version (keeps name and access) --url URL | --file PATH [--note]
|
|
12869
13030
|
agents docs history|restore <id> <doc> [n] Its versions; restore n as a new version
|
|
12870
13031
|
agents share|unshare <id> <email> Share it without handing it over [--role editor|admin] \xB7 agents shares <id> lists them
|
|
13032
|
+
agents teams ls|on|off <id> [ws] Which Teams workspaces it answers in, and its @handle there [--handle x]
|
|
12871
13033
|
agents rm <id> Delete an agent (owner only; asks you to type its name, or --yes) [--from-workspace: turn it off in Teams first]
|
|
12872
13034
|
agents restart <id> Restart an ACP agent (needsRestart) so it loads new skills
|
|
12873
13035
|
agents box <id> The machine serving it: is it on the new image? [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle [--reenv] [--force]]
|
|
@@ -12904,6 +13066,11 @@ ${bold("Support")} ${dim("(Ghosty staff)")}
|
|
|
12904
13066
|
push log --user EMAIL Pushes sent to someone and what Apple/Google answered [--since 2d]
|
|
12905
13067
|
novedades ls|create|set|test|send|rm Android app announcements [--title] [--body] [--image FILE] [--link] [--to] [--min-version N] [--publish] [--at]
|
|
12906
13068
|
|
|
13069
|
+
${bold("Sites")} ${dim("(static sites on EasyBits with Ghosty's house account)")}
|
|
13070
|
+
sites ls Sites your agents published [--house: Ghosty's own demos and landings (staff)]
|
|
13071
|
+
sites publish <file.html|folder> Publish a house site (staff): no owner, no expiry, no banner [--title] [--slug S] [--site <slug|id>: update that one]
|
|
13072
|
+
sites slug <slug|id> <new-slug> Change a site's slug (staff)
|
|
13073
|
+
|
|
12907
13074
|
${bold("Demos")}
|
|
12908
13075
|
demos try <slug> "msg" ["msg2"\u2026] Try a public demo as a visitor (same thread across runs) [--new] [--recycle-between --agent X]
|
|
12909
13076
|
demos history <id> What visitors did: turns, messages, files (Ghosty staff) [--since 2d] [--session web:\u2026] [--timeline] [--full]
|
|
@@ -12998,6 +13165,7 @@ ${bold("Agentes")}
|
|
|
12998
13165
|
agents docs replace <id> <doc> Versi\xF3n nueva (conserva nombre y accesos) --url URL | --file RUTA [--note]
|
|
12999
13166
|
agents docs history|restore <id> <doc> [n] Sus versiones; restaura la n como versi\xF3n nueva
|
|
13000
13167
|
agents share|unshare <id> <correo> Comp\xE1rtelo sin cederlo [--role editor|admin] \xB7 agents shares <id> los lista
|
|
13168
|
+
agents teams ls|on|off <id> [equipo] En qu\xE9 equipos de Teams contesta y con qu\xE9 @handle [--handle x]
|
|
13001
13169
|
agents rm <id> Borra un agente (s\xF3lo el due\xF1o; te pide escribir su nombre, o --yes) [--from-workspace: antes lo desactiva en Teams]
|
|
13002
13170
|
agents restart <id> Reinicia un agente ACP (needsRestart) para que cargue skills nuevas
|
|
13003
13171
|
agents box <id> La m\xE1quina que lo atiende: \xBFya trae la imagen nueva? [--check RUTA]\u2026 [--contains RUTA=TEXTO]\u2026 [--recycle [--reenv] [--force]]
|
|
@@ -13034,6 +13202,11 @@ ${bold("Soporte")} ${dim("(staff de Ghosty)")}
|
|
|
13034
13202
|
push log --user CORREO Avisos que se le mandaron a alguien y qu\xE9 contest\xF3 Apple/Google [--since 2d]
|
|
13035
13203
|
novedades ls|create|set|test|send|rm Novedades de la app Android [--title] [--body] [--image ARCHIVO] [--link] [--to] [--min-version N] [--publish] [--at]
|
|
13036
13204
|
|
|
13205
|
+
${bold("Sitios")} ${dim("(sitios est\xE1ticos en EasyBits con la cuenta de casa de Ghosty)")}
|
|
13206
|
+
sites ls Sitios que publicaron tus agentes [--house: demos y landings de Ghosty (staff)]
|
|
13207
|
+
sites publish <archivo.html|carpeta> Publica un sitio de la casa (staff): sin due\xF1o, sin caducidad, sin franja [--title] [--slug S] [--site <slug|id>: actualiza \xE9se]
|
|
13208
|
+
sites slug <slug|id> <slug-nuevo> Cambia el slug de un sitio (staff)
|
|
13209
|
+
|
|
13037
13210
|
${bold("Demos")}
|
|
13038
13211
|
demos try <slug> "msg" ["msg2"\u2026] Prueba un demo p\xFAblico como visitante (mismo hilo entre corridas) [--new] [--recycle-between --agent X]
|
|
13039
13212
|
demos history <id> Qu\xE9 hicieron los visitantes: turnos, mensajes, archivos (staff de Ghosty) [--since 2d] [--session web:\u2026] [--timeline] [--full]
|
|
@@ -13079,8 +13252,8 @@ ${bold("Opciones globales")}
|
|
|
13079
13252
|
${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
|
|
13080
13253
|
${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.")}
|
|
13081
13254
|
${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
|
|
13082
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "demos", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
13083
|
-
var AGENTS_SUBS = ["ls", "create", "get", "set", "export", "doctor", "docs", "share", "unshare", "shares", "rm", "restart", "demo", "box"];
|
|
13255
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
13256
|
+
var AGENTS_SUBS = ["ls", "create", "get", "set", "export", "doctor", "docs", "share", "unshare", "shares", "teams", "rm", "restart", "demo", "box"];
|
|
13084
13257
|
function unknown(what, input, known, helpCmd) {
|
|
13085
13258
|
const s = suggest(input, known);
|
|
13086
13259
|
return usage(t(`Unknown command: ${what}.`, `Comando desconocido: ${what}.`), s ? t(`Did you mean "${s}"? Run: ${helpCmd}`, `\xBFQuisiste decir "${s}"? Corre: ${helpCmd}`) : t(`Run: ${helpCmd}`, `Corre: ${helpCmd}`));
|
|
@@ -13155,6 +13328,8 @@ async function main() {
|
|
|
13155
13328
|
config: { type: "string" },
|
|
13156
13329
|
"turn-token": { type: "string" },
|
|
13157
13330
|
slug: { type: "string" },
|
|
13331
|
+
site: { type: "string" },
|
|
13332
|
+
house: { type: "boolean" },
|
|
13158
13333
|
on: { type: "boolean" },
|
|
13159
13334
|
off: { type: "boolean" },
|
|
13160
13335
|
turns: { type: "string" },
|
|
@@ -13230,6 +13405,7 @@ async function main() {
|
|
|
13230
13405
|
before: { type: "string" },
|
|
13231
13406
|
recycle: { type: "boolean" },
|
|
13232
13407
|
role: { type: "string" },
|
|
13408
|
+
handle: { type: "string" },
|
|
13233
13409
|
wake: { type: "string" },
|
|
13234
13410
|
text: { type: "string" },
|
|
13235
13411
|
sender: { type: "string" },
|
|
@@ -13302,7 +13478,7 @@ async function main() {
|
|
|
13302
13478
|
const tokens = splitTokens(pos, v);
|
|
13303
13479
|
globals.token = tokens.session;
|
|
13304
13480
|
globals.profile = v.profile;
|
|
13305
|
-
if (v.version) return out({ version: "0.
|
|
13481
|
+
if (v.version) return out({ version: "0.41.0" }, () => "0.41.0");
|
|
13306
13482
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
13307
13483
|
const context = (await readConfig()).workspace;
|
|
13308
13484
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -13361,6 +13537,7 @@ async function main() {
|
|
|
13361
13537
|
}
|
|
13362
13538
|
if (sub === "get") return agentsGet(a1, v.fields, v["prompt-out"]);
|
|
13363
13539
|
if (sub === "share" || sub === "unshare" || sub === "shares") return agentsShare(sub, a1, a2, v);
|
|
13540
|
+
if (sub === "teams") return agentsTeams(a1, a2, pos[4], v);
|
|
13364
13541
|
if (sub === "set") return agentsSet(a1, v);
|
|
13365
13542
|
if (sub === "rm" || sub === "delete") return agentsRm(a1, v);
|
|
13366
13543
|
if (sub === "doctor" && v.user) return userDoctor(v.user, a1, v);
|
|
@@ -13391,6 +13568,9 @@ async function main() {
|
|
|
13391
13568
|
case "novedades":
|
|
13392
13569
|
case "announcements":
|
|
13393
13570
|
return novedadesCmd(sub, a1, v);
|
|
13571
|
+
case "sites":
|
|
13572
|
+
case "site":
|
|
13573
|
+
return sitesCmd(sub, a1, a2, v);
|
|
13394
13574
|
case "board":
|
|
13395
13575
|
return boardCmd(sub, a1, v, a2);
|
|
13396
13576
|
case "boards":
|
|
@@ -13451,7 +13631,7 @@ async function main() {
|
|
|
13451
13631
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
13452
13632
|
}
|
|
13453
13633
|
}
|
|
13454
|
-
var notice = updateNotice("0.
|
|
13634
|
+
var notice = updateNotice("0.41.0").catch(() => null);
|
|
13455
13635
|
main().then(
|
|
13456
13636
|
async () => {
|
|
13457
13637
|
const n = await notice;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostystudio/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "Official Ghosty Studio CLI — configure, test and chat with your agents from the terminal. Built for humans and coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node22 --outfile=dist/index.js --banner:js='#!/usr/bin/env node\nimport { createRequire as __cr } from \"node:module\"; const require = __cr(import.meta.url);' --define:__VERSION__=\\\"$npm_package_version\\\"",
|
|
18
18
|
"prepack": "npm run build",
|
|
19
|
-
"test": "for t in flags prompt api rows wa-auth resolve aliases i18n help completion photos channels-retry spaces; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
|
|
19
|
+
"test": "for t in flags prompt api rows wa-auth resolve aliases i18n help completion photos channels-retry spaces sites; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
22
22
|
"ghosty",
|