replicas-cli 0.3.129 → 0.3.131
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/dist/index.cjs +754 -190
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -111,17 +111,17 @@ var require_visit = __commonJS({
|
|
|
111
111
|
visit.BREAK = BREAK;
|
|
112
112
|
visit.SKIP = SKIP;
|
|
113
113
|
visit.REMOVE = REMOVE;
|
|
114
|
-
function visit_(key, node, visitor,
|
|
115
|
-
const ctrl = callVisitor(key, node, visitor,
|
|
114
|
+
function visit_(key, node, visitor, path10) {
|
|
115
|
+
const ctrl = callVisitor(key, node, visitor, path10);
|
|
116
116
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
117
|
-
replaceNode(key,
|
|
118
|
-
return visit_(key, ctrl, visitor,
|
|
117
|
+
replaceNode(key, path10, ctrl);
|
|
118
|
+
return visit_(key, ctrl, visitor, path10);
|
|
119
119
|
}
|
|
120
120
|
if (typeof ctrl !== "symbol") {
|
|
121
121
|
if (identity.isCollection(node)) {
|
|
122
|
-
|
|
122
|
+
path10 = Object.freeze(path10.concat(node));
|
|
123
123
|
for (let i = 0; i < node.items.length; ++i) {
|
|
124
|
-
const ci = visit_(i, node.items[i], visitor,
|
|
124
|
+
const ci = visit_(i, node.items[i], visitor, path10);
|
|
125
125
|
if (typeof ci === "number")
|
|
126
126
|
i = ci - 1;
|
|
127
127
|
else if (ci === BREAK)
|
|
@@ -132,13 +132,13 @@ var require_visit = __commonJS({
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
} else if (identity.isPair(node)) {
|
|
135
|
-
|
|
136
|
-
const ck = visit_("key", node.key, visitor,
|
|
135
|
+
path10 = Object.freeze(path10.concat(node));
|
|
136
|
+
const ck = visit_("key", node.key, visitor, path10);
|
|
137
137
|
if (ck === BREAK)
|
|
138
138
|
return BREAK;
|
|
139
139
|
else if (ck === REMOVE)
|
|
140
140
|
node.key = null;
|
|
141
|
-
const cv = visit_("value", node.value, visitor,
|
|
141
|
+
const cv = visit_("value", node.value, visitor, path10);
|
|
142
142
|
if (cv === BREAK)
|
|
143
143
|
return BREAK;
|
|
144
144
|
else if (cv === REMOVE)
|
|
@@ -159,17 +159,17 @@ var require_visit = __commonJS({
|
|
|
159
159
|
visitAsync.BREAK = BREAK;
|
|
160
160
|
visitAsync.SKIP = SKIP;
|
|
161
161
|
visitAsync.REMOVE = REMOVE;
|
|
162
|
-
async function visitAsync_(key, node, visitor,
|
|
163
|
-
const ctrl = await callVisitor(key, node, visitor,
|
|
162
|
+
async function visitAsync_(key, node, visitor, path10) {
|
|
163
|
+
const ctrl = await callVisitor(key, node, visitor, path10);
|
|
164
164
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
165
|
-
replaceNode(key,
|
|
166
|
-
return visitAsync_(key, ctrl, visitor,
|
|
165
|
+
replaceNode(key, path10, ctrl);
|
|
166
|
+
return visitAsync_(key, ctrl, visitor, path10);
|
|
167
167
|
}
|
|
168
168
|
if (typeof ctrl !== "symbol") {
|
|
169
169
|
if (identity.isCollection(node)) {
|
|
170
|
-
|
|
170
|
+
path10 = Object.freeze(path10.concat(node));
|
|
171
171
|
for (let i = 0; i < node.items.length; ++i) {
|
|
172
|
-
const ci = await visitAsync_(i, node.items[i], visitor,
|
|
172
|
+
const ci = await visitAsync_(i, node.items[i], visitor, path10);
|
|
173
173
|
if (typeof ci === "number")
|
|
174
174
|
i = ci - 1;
|
|
175
175
|
else if (ci === BREAK)
|
|
@@ -180,13 +180,13 @@ var require_visit = __commonJS({
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
} else if (identity.isPair(node)) {
|
|
183
|
-
|
|
184
|
-
const ck = await visitAsync_("key", node.key, visitor,
|
|
183
|
+
path10 = Object.freeze(path10.concat(node));
|
|
184
|
+
const ck = await visitAsync_("key", node.key, visitor, path10);
|
|
185
185
|
if (ck === BREAK)
|
|
186
186
|
return BREAK;
|
|
187
187
|
else if (ck === REMOVE)
|
|
188
188
|
node.key = null;
|
|
189
|
-
const cv = await visitAsync_("value", node.value, visitor,
|
|
189
|
+
const cv = await visitAsync_("value", node.value, visitor, path10);
|
|
190
190
|
if (cv === BREAK)
|
|
191
191
|
return BREAK;
|
|
192
192
|
else if (cv === REMOVE)
|
|
@@ -213,23 +213,23 @@ var require_visit = __commonJS({
|
|
|
213
213
|
}
|
|
214
214
|
return visitor;
|
|
215
215
|
}
|
|
216
|
-
function callVisitor(key, node, visitor,
|
|
216
|
+
function callVisitor(key, node, visitor, path10) {
|
|
217
217
|
if (typeof visitor === "function")
|
|
218
|
-
return visitor(key, node,
|
|
218
|
+
return visitor(key, node, path10);
|
|
219
219
|
if (identity.isMap(node))
|
|
220
|
-
return visitor.Map?.(key, node,
|
|
220
|
+
return visitor.Map?.(key, node, path10);
|
|
221
221
|
if (identity.isSeq(node))
|
|
222
|
-
return visitor.Seq?.(key, node,
|
|
222
|
+
return visitor.Seq?.(key, node, path10);
|
|
223
223
|
if (identity.isPair(node))
|
|
224
|
-
return visitor.Pair?.(key, node,
|
|
224
|
+
return visitor.Pair?.(key, node, path10);
|
|
225
225
|
if (identity.isScalar(node))
|
|
226
|
-
return visitor.Scalar?.(key, node,
|
|
226
|
+
return visitor.Scalar?.(key, node, path10);
|
|
227
227
|
if (identity.isAlias(node))
|
|
228
|
-
return visitor.Alias?.(key, node,
|
|
228
|
+
return visitor.Alias?.(key, node, path10);
|
|
229
229
|
return void 0;
|
|
230
230
|
}
|
|
231
|
-
function replaceNode(key,
|
|
232
|
-
const parent =
|
|
231
|
+
function replaceNode(key, path10, node) {
|
|
232
|
+
const parent = path10[path10.length - 1];
|
|
233
233
|
if (identity.isCollection(parent)) {
|
|
234
234
|
parent.items[key] = node;
|
|
235
235
|
} else if (identity.isPair(parent)) {
|
|
@@ -839,10 +839,10 @@ var require_Collection = __commonJS({
|
|
|
839
839
|
var createNode = require_createNode();
|
|
840
840
|
var identity = require_identity();
|
|
841
841
|
var Node = require_Node();
|
|
842
|
-
function collectionFromPath(schema,
|
|
842
|
+
function collectionFromPath(schema, path10, value) {
|
|
843
843
|
let v = value;
|
|
844
|
-
for (let i =
|
|
845
|
-
const k =
|
|
844
|
+
for (let i = path10.length - 1; i >= 0; --i) {
|
|
845
|
+
const k = path10[i];
|
|
846
846
|
if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
|
|
847
847
|
const a = [];
|
|
848
848
|
a[k] = v;
|
|
@@ -861,7 +861,7 @@ var require_Collection = __commonJS({
|
|
|
861
861
|
sourceObjects: /* @__PURE__ */ new Map()
|
|
862
862
|
});
|
|
863
863
|
}
|
|
864
|
-
var isEmptyPath = (
|
|
864
|
+
var isEmptyPath = (path10) => path10 == null || typeof path10 === "object" && !!path10[Symbol.iterator]().next().done;
|
|
865
865
|
var Collection = class extends Node.NodeBase {
|
|
866
866
|
constructor(type, schema) {
|
|
867
867
|
super(type);
|
|
@@ -891,11 +891,11 @@ var require_Collection = __commonJS({
|
|
|
891
891
|
* be a Pair instance or a `{ key, value }` object, which may not have a key
|
|
892
892
|
* that already exists in the map.
|
|
893
893
|
*/
|
|
894
|
-
addIn(
|
|
895
|
-
if (isEmptyPath(
|
|
894
|
+
addIn(path10, value) {
|
|
895
|
+
if (isEmptyPath(path10))
|
|
896
896
|
this.add(value);
|
|
897
897
|
else {
|
|
898
|
-
const [key, ...rest] =
|
|
898
|
+
const [key, ...rest] = path10;
|
|
899
899
|
const node = this.get(key, true);
|
|
900
900
|
if (identity.isCollection(node))
|
|
901
901
|
node.addIn(rest, value);
|
|
@@ -909,8 +909,8 @@ var require_Collection = __commonJS({
|
|
|
909
909
|
* Removes a value from the collection.
|
|
910
910
|
* @returns `true` if the item was found and removed.
|
|
911
911
|
*/
|
|
912
|
-
deleteIn(
|
|
913
|
-
const [key, ...rest] =
|
|
912
|
+
deleteIn(path10) {
|
|
913
|
+
const [key, ...rest] = path10;
|
|
914
914
|
if (rest.length === 0)
|
|
915
915
|
return this.delete(key);
|
|
916
916
|
const node = this.get(key, true);
|
|
@@ -924,8 +924,8 @@ var require_Collection = __commonJS({
|
|
|
924
924
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
925
925
|
* `true` (collections are always returned intact).
|
|
926
926
|
*/
|
|
927
|
-
getIn(
|
|
928
|
-
const [key, ...rest] =
|
|
927
|
+
getIn(path10, keepScalar) {
|
|
928
|
+
const [key, ...rest] = path10;
|
|
929
929
|
const node = this.get(key, true);
|
|
930
930
|
if (rest.length === 0)
|
|
931
931
|
return !keepScalar && identity.isScalar(node) ? node.value : node;
|
|
@@ -943,8 +943,8 @@ var require_Collection = __commonJS({
|
|
|
943
943
|
/**
|
|
944
944
|
* Checks if the collection includes a value with the key `key`.
|
|
945
945
|
*/
|
|
946
|
-
hasIn(
|
|
947
|
-
const [key, ...rest] =
|
|
946
|
+
hasIn(path10) {
|
|
947
|
+
const [key, ...rest] = path10;
|
|
948
948
|
if (rest.length === 0)
|
|
949
949
|
return this.has(key);
|
|
950
950
|
const node = this.get(key, true);
|
|
@@ -954,8 +954,8 @@ var require_Collection = __commonJS({
|
|
|
954
954
|
* Sets a value in this collection. For `!!set`, `value` needs to be a
|
|
955
955
|
* boolean to add/remove the item from the set.
|
|
956
956
|
*/
|
|
957
|
-
setIn(
|
|
958
|
-
const [key, ...rest] =
|
|
957
|
+
setIn(path10, value) {
|
|
958
|
+
const [key, ...rest] = path10;
|
|
959
959
|
if (rest.length === 0) {
|
|
960
960
|
this.set(key, value);
|
|
961
961
|
} else {
|
|
@@ -3470,9 +3470,9 @@ var require_Document = __commonJS({
|
|
|
3470
3470
|
this.contents.add(value);
|
|
3471
3471
|
}
|
|
3472
3472
|
/** Adds a value to the document. */
|
|
3473
|
-
addIn(
|
|
3473
|
+
addIn(path10, value) {
|
|
3474
3474
|
if (assertCollection(this.contents))
|
|
3475
|
-
this.contents.addIn(
|
|
3475
|
+
this.contents.addIn(path10, value);
|
|
3476
3476
|
}
|
|
3477
3477
|
/**
|
|
3478
3478
|
* Create a new `Alias` node, ensuring that the target `node` has the required anchor.
|
|
@@ -3547,14 +3547,14 @@ var require_Document = __commonJS({
|
|
|
3547
3547
|
* Removes a value from the document.
|
|
3548
3548
|
* @returns `true` if the item was found and removed.
|
|
3549
3549
|
*/
|
|
3550
|
-
deleteIn(
|
|
3551
|
-
if (Collection.isEmptyPath(
|
|
3550
|
+
deleteIn(path10) {
|
|
3551
|
+
if (Collection.isEmptyPath(path10)) {
|
|
3552
3552
|
if (this.contents == null)
|
|
3553
3553
|
return false;
|
|
3554
3554
|
this.contents = null;
|
|
3555
3555
|
return true;
|
|
3556
3556
|
}
|
|
3557
|
-
return assertCollection(this.contents) ? this.contents.deleteIn(
|
|
3557
|
+
return assertCollection(this.contents) ? this.contents.deleteIn(path10) : false;
|
|
3558
3558
|
}
|
|
3559
3559
|
/**
|
|
3560
3560
|
* Returns item at `key`, or `undefined` if not found. By default unwraps
|
|
@@ -3569,10 +3569,10 @@ var require_Document = __commonJS({
|
|
|
3569
3569
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
3570
3570
|
* `true` (collections are always returned intact).
|
|
3571
3571
|
*/
|
|
3572
|
-
getIn(
|
|
3573
|
-
if (Collection.isEmptyPath(
|
|
3572
|
+
getIn(path10, keepScalar) {
|
|
3573
|
+
if (Collection.isEmptyPath(path10))
|
|
3574
3574
|
return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
|
|
3575
|
-
return identity.isCollection(this.contents) ? this.contents.getIn(
|
|
3575
|
+
return identity.isCollection(this.contents) ? this.contents.getIn(path10, keepScalar) : void 0;
|
|
3576
3576
|
}
|
|
3577
3577
|
/**
|
|
3578
3578
|
* Checks if the document includes a value with the key `key`.
|
|
@@ -3583,10 +3583,10 @@ var require_Document = __commonJS({
|
|
|
3583
3583
|
/**
|
|
3584
3584
|
* Checks if the document includes a value at `path`.
|
|
3585
3585
|
*/
|
|
3586
|
-
hasIn(
|
|
3587
|
-
if (Collection.isEmptyPath(
|
|
3586
|
+
hasIn(path10) {
|
|
3587
|
+
if (Collection.isEmptyPath(path10))
|
|
3588
3588
|
return this.contents !== void 0;
|
|
3589
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
3589
|
+
return identity.isCollection(this.contents) ? this.contents.hasIn(path10) : false;
|
|
3590
3590
|
}
|
|
3591
3591
|
/**
|
|
3592
3592
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
@@ -3603,13 +3603,13 @@ var require_Document = __commonJS({
|
|
|
3603
3603
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
3604
3604
|
* boolean to add/remove the item from the set.
|
|
3605
3605
|
*/
|
|
3606
|
-
setIn(
|
|
3607
|
-
if (Collection.isEmptyPath(
|
|
3606
|
+
setIn(path10, value) {
|
|
3607
|
+
if (Collection.isEmptyPath(path10)) {
|
|
3608
3608
|
this.contents = value;
|
|
3609
3609
|
} else if (this.contents == null) {
|
|
3610
|
-
this.contents = Collection.collectionFromPath(this.schema, Array.from(
|
|
3610
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path10), value);
|
|
3611
3611
|
} else if (assertCollection(this.contents)) {
|
|
3612
|
-
this.contents.setIn(
|
|
3612
|
+
this.contents.setIn(path10, value);
|
|
3613
3613
|
}
|
|
3614
3614
|
}
|
|
3615
3615
|
/**
|
|
@@ -5569,9 +5569,9 @@ var require_cst_visit = __commonJS({
|
|
|
5569
5569
|
visit.BREAK = BREAK;
|
|
5570
5570
|
visit.SKIP = SKIP;
|
|
5571
5571
|
visit.REMOVE = REMOVE;
|
|
5572
|
-
visit.itemAtPath = (cst,
|
|
5572
|
+
visit.itemAtPath = (cst, path10) => {
|
|
5573
5573
|
let item = cst;
|
|
5574
|
-
for (const [field2, index] of
|
|
5574
|
+
for (const [field2, index] of path10) {
|
|
5575
5575
|
const tok = item?.[field2];
|
|
5576
5576
|
if (tok && "items" in tok) {
|
|
5577
5577
|
item = tok.items[index];
|
|
@@ -5580,23 +5580,23 @@ var require_cst_visit = __commonJS({
|
|
|
5580
5580
|
}
|
|
5581
5581
|
return item;
|
|
5582
5582
|
};
|
|
5583
|
-
visit.parentCollection = (cst,
|
|
5584
|
-
const parent = visit.itemAtPath(cst,
|
|
5585
|
-
const field2 =
|
|
5583
|
+
visit.parentCollection = (cst, path10) => {
|
|
5584
|
+
const parent = visit.itemAtPath(cst, path10.slice(0, -1));
|
|
5585
|
+
const field2 = path10[path10.length - 1][0];
|
|
5586
5586
|
const coll = parent?.[field2];
|
|
5587
5587
|
if (coll && "items" in coll)
|
|
5588
5588
|
return coll;
|
|
5589
5589
|
throw new Error("Parent collection not found");
|
|
5590
5590
|
};
|
|
5591
|
-
function _visit(
|
|
5592
|
-
let ctrl = visitor(item,
|
|
5591
|
+
function _visit(path10, item, visitor) {
|
|
5592
|
+
let ctrl = visitor(item, path10);
|
|
5593
5593
|
if (typeof ctrl === "symbol")
|
|
5594
5594
|
return ctrl;
|
|
5595
5595
|
for (const field2 of ["key", "value"]) {
|
|
5596
5596
|
const token = item[field2];
|
|
5597
5597
|
if (token && "items" in token) {
|
|
5598
5598
|
for (let i = 0; i < token.items.length; ++i) {
|
|
5599
|
-
const ci = _visit(Object.freeze(
|
|
5599
|
+
const ci = _visit(Object.freeze(path10.concat([[field2, i]])), token.items[i], visitor);
|
|
5600
5600
|
if (typeof ci === "number")
|
|
5601
5601
|
i = ci - 1;
|
|
5602
5602
|
else if (ci === BREAK)
|
|
@@ -5607,10 +5607,10 @@ var require_cst_visit = __commonJS({
|
|
|
5607
5607
|
}
|
|
5608
5608
|
}
|
|
5609
5609
|
if (typeof ctrl === "function" && field2 === "key")
|
|
5610
|
-
ctrl = ctrl(item,
|
|
5610
|
+
ctrl = ctrl(item, path10);
|
|
5611
5611
|
}
|
|
5612
5612
|
}
|
|
5613
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
5613
|
+
return typeof ctrl === "function" ? ctrl(item, path10) : ctrl;
|
|
5614
5614
|
}
|
|
5615
5615
|
exports2.visit = visit;
|
|
5616
5616
|
}
|
|
@@ -6912,14 +6912,14 @@ var require_parser = __commonJS({
|
|
|
6912
6912
|
case "scalar":
|
|
6913
6913
|
case "single-quoted-scalar":
|
|
6914
6914
|
case "double-quoted-scalar": {
|
|
6915
|
-
const
|
|
6915
|
+
const fs11 = this.flowScalar(this.type);
|
|
6916
6916
|
if (atNextItem || it.value) {
|
|
6917
|
-
map2.items.push({ start, key:
|
|
6917
|
+
map2.items.push({ start, key: fs11, sep: [] });
|
|
6918
6918
|
this.onKeyLine = true;
|
|
6919
6919
|
} else if (it.sep) {
|
|
6920
|
-
this.stack.push(
|
|
6920
|
+
this.stack.push(fs11);
|
|
6921
6921
|
} else {
|
|
6922
|
-
Object.assign(it, { key:
|
|
6922
|
+
Object.assign(it, { key: fs11, sep: [] });
|
|
6923
6923
|
this.onKeyLine = true;
|
|
6924
6924
|
}
|
|
6925
6925
|
return;
|
|
@@ -7047,13 +7047,13 @@ var require_parser = __commonJS({
|
|
|
7047
7047
|
case "scalar":
|
|
7048
7048
|
case "single-quoted-scalar":
|
|
7049
7049
|
case "double-quoted-scalar": {
|
|
7050
|
-
const
|
|
7050
|
+
const fs11 = this.flowScalar(this.type);
|
|
7051
7051
|
if (!it || it.value)
|
|
7052
|
-
fc.items.push({ start: [], key:
|
|
7052
|
+
fc.items.push({ start: [], key: fs11, sep: [] });
|
|
7053
7053
|
else if (it.sep)
|
|
7054
|
-
this.stack.push(
|
|
7054
|
+
this.stack.push(fs11);
|
|
7055
7055
|
else
|
|
7056
|
-
Object.assign(it, { key:
|
|
7056
|
+
Object.assign(it, { key: fs11, sep: [] });
|
|
7057
7057
|
return;
|
|
7058
7058
|
}
|
|
7059
7059
|
case "flow-map-end":
|
|
@@ -7405,19 +7405,19 @@ var LEGACY_WEB_APP_PATHS = {
|
|
|
7405
7405
|
dashboardWorkspaces: `${DASHBOARD_PATH}/workspaces`
|
|
7406
7406
|
};
|
|
7407
7407
|
function getWorkspaceDashboardUrl(workspaceId, options = {}) {
|
|
7408
|
-
const { encodeWorkspaceId = true, mode, mediaId, webAppUrl = DEFAULT_WEB_APP_URL } = options;
|
|
7408
|
+
const { encodeWorkspaceId = true, mode, mediaId, webAppUrl: webAppUrl2 = DEFAULT_WEB_APP_URL } = options;
|
|
7409
7409
|
const workspaceIdValue = encodeWorkspaceId ? encodeURIComponent(workspaceId) : workspaceId;
|
|
7410
7410
|
const params = [];
|
|
7411
7411
|
if (mode) params.push(`mode=${encodeURIComponent(mode)}`);
|
|
7412
7412
|
if (mediaId) params.push(`media=${encodeURIComponent(mediaId)}`);
|
|
7413
7413
|
const query = params.length > 0 ? `?${params.join("&")}` : "";
|
|
7414
|
-
return `${
|
|
7414
|
+
return `${webAppUrl2}${WEB_APP_PATHS.fullscreenWorkspaces}/${workspaceIdValue}${query}`;
|
|
7415
7415
|
}
|
|
7416
7416
|
function getOrganizationMediaPath(organizationId, mediaId) {
|
|
7417
7417
|
return `/organizations/${encodeURIComponent(organizationId)}/media/${encodeURIComponent(mediaId)}`;
|
|
7418
7418
|
}
|
|
7419
|
-
function getOrganizationMediaUrl(organizationId, mediaId,
|
|
7420
|
-
return `${
|
|
7419
|
+
function getOrganizationMediaUrl(organizationId, mediaId, webAppUrl2 = DEFAULT_WEB_APP_URL) {
|
|
7420
|
+
return `${webAppUrl2}${getOrganizationMediaPath(organizationId, mediaId)}`;
|
|
7421
7421
|
}
|
|
7422
7422
|
|
|
7423
7423
|
// src/commands/login.ts
|
|
@@ -8580,10 +8580,10 @@ function mergeDefs(...defs) {
|
|
|
8580
8580
|
function cloneDef(schema) {
|
|
8581
8581
|
return mergeDefs(schema._zod.def);
|
|
8582
8582
|
}
|
|
8583
|
-
function getElementAtPath(obj,
|
|
8584
|
-
if (!
|
|
8583
|
+
function getElementAtPath(obj, path10) {
|
|
8584
|
+
if (!path10)
|
|
8585
8585
|
return obj;
|
|
8586
|
-
return
|
|
8586
|
+
return path10.reduce((acc, key) => acc?.[key], obj);
|
|
8587
8587
|
}
|
|
8588
8588
|
function promiseAllObject(promisesObj) {
|
|
8589
8589
|
const keys = Object.keys(promisesObj);
|
|
@@ -8992,11 +8992,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
8992
8992
|
}
|
|
8993
8993
|
return false;
|
|
8994
8994
|
}
|
|
8995
|
-
function prefixIssues(
|
|
8995
|
+
function prefixIssues(path10, issues) {
|
|
8996
8996
|
return issues.map((iss) => {
|
|
8997
8997
|
var _a3;
|
|
8998
8998
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
8999
|
-
iss.path.unshift(
|
|
8999
|
+
iss.path.unshift(path10);
|
|
9000
9000
|
return iss;
|
|
9001
9001
|
});
|
|
9002
9002
|
}
|
|
@@ -9143,16 +9143,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
9143
9143
|
}
|
|
9144
9144
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
9145
9145
|
const fieldErrors = { _errors: [] };
|
|
9146
|
-
const processError = (error52,
|
|
9146
|
+
const processError = (error52, path10 = []) => {
|
|
9147
9147
|
for (const issue2 of error52.issues) {
|
|
9148
9148
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
9149
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
9149
|
+
issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
|
|
9150
9150
|
} else if (issue2.code === "invalid_key") {
|
|
9151
|
-
processError({ issues: issue2.issues }, [...
|
|
9151
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
9152
9152
|
} else if (issue2.code === "invalid_element") {
|
|
9153
|
-
processError({ issues: issue2.issues }, [...
|
|
9153
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
9154
9154
|
} else {
|
|
9155
|
-
const fullpath = [...
|
|
9155
|
+
const fullpath = [...path10, ...issue2.path];
|
|
9156
9156
|
if (fullpath.length === 0) {
|
|
9157
9157
|
fieldErrors._errors.push(mapper(issue2));
|
|
9158
9158
|
} else {
|
|
@@ -9179,17 +9179,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
9179
9179
|
}
|
|
9180
9180
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
9181
9181
|
const result = { errors: [] };
|
|
9182
|
-
const processError = (error52,
|
|
9182
|
+
const processError = (error52, path10 = []) => {
|
|
9183
9183
|
var _a3, _b;
|
|
9184
9184
|
for (const issue2 of error52.issues) {
|
|
9185
9185
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
9186
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
9186
|
+
issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
|
|
9187
9187
|
} else if (issue2.code === "invalid_key") {
|
|
9188
|
-
processError({ issues: issue2.issues }, [...
|
|
9188
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
9189
9189
|
} else if (issue2.code === "invalid_element") {
|
|
9190
|
-
processError({ issues: issue2.issues }, [...
|
|
9190
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
9191
9191
|
} else {
|
|
9192
|
-
const fullpath = [...
|
|
9192
|
+
const fullpath = [...path10, ...issue2.path];
|
|
9193
9193
|
if (fullpath.length === 0) {
|
|
9194
9194
|
result.errors.push(mapper(issue2));
|
|
9195
9195
|
continue;
|
|
@@ -9221,8 +9221,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
9221
9221
|
}
|
|
9222
9222
|
function toDotPath(_path) {
|
|
9223
9223
|
const segs = [];
|
|
9224
|
-
const
|
|
9225
|
-
for (const seg of
|
|
9224
|
+
const path10 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
9225
|
+
for (const seg of path10) {
|
|
9226
9226
|
if (typeof seg === "number")
|
|
9227
9227
|
segs.push(`[${seg}]`);
|
|
9228
9228
|
else if (typeof seg === "symbol")
|
|
@@ -21914,13 +21914,13 @@ function resolveRef(ref, ctx) {
|
|
|
21914
21914
|
if (!ref.startsWith("#")) {
|
|
21915
21915
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
21916
21916
|
}
|
|
21917
|
-
const
|
|
21918
|
-
if (
|
|
21917
|
+
const path10 = ref.slice(1).split("/").filter(Boolean);
|
|
21918
|
+
if (path10.length === 0) {
|
|
21919
21919
|
return ctx.rootSchema;
|
|
21920
21920
|
}
|
|
21921
21921
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
21922
|
-
if (
|
|
21923
|
-
const key =
|
|
21922
|
+
if (path10[0] === defsKey) {
|
|
21923
|
+
const key = path10[1];
|
|
21924
21924
|
if (!key || !ctx.defs[key]) {
|
|
21925
21925
|
throw new Error(`Reference not found: ${ref}`);
|
|
21926
21926
|
}
|
|
@@ -23287,11 +23287,11 @@ function parseShellScript(script, cwd) {
|
|
|
23287
23287
|
parseUntil(scope, 0, false);
|
|
23288
23288
|
return scope.commands;
|
|
23289
23289
|
}
|
|
23290
|
-
function resolveShellPath(dir,
|
|
23291
|
-
if (
|
|
23292
|
-
if (!
|
|
23290
|
+
function resolveShellPath(dir, path10) {
|
|
23291
|
+
if (path10.includes(SHELL_OPAQUE) || path10.startsWith("~")) return null;
|
|
23292
|
+
if (!path10.startsWith("/") && dir === null) return null;
|
|
23293
23293
|
const parts = [];
|
|
23294
|
-
for (const part of (
|
|
23294
|
+
for (const part of (path10.startsWith("/") ? path10 : `${dir}/${path10}`).split("/")) {
|
|
23295
23295
|
if (part === "..") parts.pop();
|
|
23296
23296
|
else if (part && part !== ".") parts.push(part);
|
|
23297
23297
|
}
|
|
@@ -23645,8 +23645,8 @@ function parseSkillName(tool, input) {
|
|
|
23645
23645
|
const skillName = parsedInput.skill ?? parsedInput.name;
|
|
23646
23646
|
return typeof skillName === "string" && skillName ? skillName : null;
|
|
23647
23647
|
}
|
|
23648
|
-
function skillNameFromFilePath(
|
|
23649
|
-
const parts =
|
|
23648
|
+
function skillNameFromFilePath(path10) {
|
|
23649
|
+
const parts = path10.split(/[\\/]+/).filter(Boolean);
|
|
23650
23650
|
if (parts.length < 2 || parts[parts.length - 1].toLowerCase() !== "skill.md") return null;
|
|
23651
23651
|
const skillName = parts[parts.length - 2];
|
|
23652
23652
|
return /[*?[\]{}…$]/.test(skillName) || ["skills", ".system"].includes(skillName.toLowerCase()) ? null : skillName;
|
|
@@ -23656,19 +23656,19 @@ function skillFilePath(tool, input) {
|
|
|
23656
23656
|
if (!READ_TOOL_NAMES.has(tool.toLowerCase())) return null;
|
|
23657
23657
|
const parsedInput = typeof input === "string" ? safeJsonParse(input, null) : input;
|
|
23658
23658
|
if (!isRecord(parsedInput)) return null;
|
|
23659
|
-
const
|
|
23660
|
-
return
|
|
23659
|
+
const path10 = [parsedInput.path, parsedInput.filePath, parsedInput.file_path].find((value) => typeof value === "string" && value.length > 0);
|
|
23660
|
+
return path10 && skillNameFromFilePath(path10) ? path10 : null;
|
|
23661
23661
|
}
|
|
23662
23662
|
function createCallDisplayMessage(message2) {
|
|
23663
23663
|
if (message2.inputRequest) return { ...message2, type: "tool_call" };
|
|
23664
|
-
const
|
|
23665
|
-
const skillName = message2.server.toLowerCase() === "skills" ? message2.tool :
|
|
23664
|
+
const path10 = skillFilePath(message2.tool, message2.input);
|
|
23665
|
+
const skillName = message2.server.toLowerCase() === "skills" ? message2.tool : path10 ? skillNameFromFilePath(path10) : parseSkillName(message2.tool, message2.input);
|
|
23666
23666
|
if (skillName === null) return { ...message2, type: "tool_call" };
|
|
23667
23667
|
return {
|
|
23668
23668
|
id: message2.id,
|
|
23669
23669
|
type: "skill",
|
|
23670
23670
|
skillName,
|
|
23671
|
-
...
|
|
23671
|
+
...path10 ? { path: path10 } : {},
|
|
23672
23672
|
status: message2.status,
|
|
23673
23673
|
timestamp: message2.timestamp
|
|
23674
23674
|
};
|
|
@@ -23681,11 +23681,11 @@ function insertDisplayMessageByTimestamp(messages, message2) {
|
|
|
23681
23681
|
}
|
|
23682
23682
|
|
|
23683
23683
|
// ../shared/src/display-message/format.ts
|
|
23684
|
-
function fileContentDiff(
|
|
23684
|
+
function fileContentDiff(path10, content, action) {
|
|
23685
23685
|
const lines = content.replace(/\n$/, "").split("\n");
|
|
23686
23686
|
return [
|
|
23687
|
-
`--- ${action === "add" ? "/dev/null" : `a/${
|
|
23688
|
-
`+++ ${action === "add" ? `b/${
|
|
23687
|
+
`--- ${action === "add" ? "/dev/null" : `a/${path10}`}`,
|
|
23688
|
+
`+++ ${action === "add" ? `b/${path10}` : "/dev/null"}`,
|
|
23689
23689
|
action === "add" ? `@@ -0,0 +1,${lines.length} @@` : `@@ -1,${lines.length} +0,0 @@`,
|
|
23690
23690
|
...lines.map((line) => `${action === "add" ? "+" : "-"}${line}`)
|
|
23691
23691
|
].join("\n");
|
|
@@ -23712,7 +23712,7 @@ var LINE_COUNT_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
23712
23712
|
]);
|
|
23713
23713
|
var SCRIPT_ROOT = "/";
|
|
23714
23714
|
var commandName = (argv) => argv[0].split("/").pop() ?? argv[0];
|
|
23715
|
-
var fileWrites = ({ writes }) => writes.filter(({ path:
|
|
23715
|
+
var fileWrites = ({ writes }) => writes.filter(({ path: path10 }) => path10 !== "/dev/null");
|
|
23716
23716
|
function shellCommands(command) {
|
|
23717
23717
|
return parseShellScript(command, SCRIPT_ROOT).filter(({ wrapper }) => !wrapper);
|
|
23718
23718
|
}
|
|
@@ -23724,8 +23724,8 @@ function skillNamesFromCommand(command) {
|
|
|
23724
23724
|
if (!SKILL_READ_COMMANDS.has(name) || writesFiles(name, argv) || fileWrites(shellCommand).length > 0) return [];
|
|
23725
23725
|
const paths = readCommandPaths(name, argv);
|
|
23726
23726
|
const stdinTarget = shellCommand.stdinTarget?.replaceAll(SHELL_OPAQUE, "\u2026");
|
|
23727
|
-
return (paths.length === 0 && stdinTarget ? [stdinTarget] : paths).flatMap((
|
|
23728
|
-
const resolved =
|
|
23727
|
+
return (paths.length === 0 && stdinTarget ? [stdinTarget] : paths).flatMap((path10) => {
|
|
23728
|
+
const resolved = path10.startsWith("~") ? path10 : resolveShellPath(shellCommand.dir, path10)?.replace(SCRIPT_ROOT, "");
|
|
23729
23729
|
const skillName = resolved && skillNameFromFilePath(resolved);
|
|
23730
23730
|
return skillName ? [skillName] : [];
|
|
23731
23731
|
});
|
|
@@ -25188,7 +25188,7 @@ var LINEAR_ABILITY = {
|
|
|
25188
25188
|
|
|
25189
25189
|
// ../shared/src/default-skills/replicas-agent/abilities/media.ts
|
|
25190
25190
|
var SECTION8 = `### Media
|
|
25191
|
-
Deliver screenshots, GIFs, screen recordings, generated diagrams, audio clips
|
|
25191
|
+
Deliver screenshots, GIFs, screen recordings, generated diagrams, and audio clips to the Replicas dashboard and, when applicable, inline in the destination workflow.
|
|
25192
25192
|
|
|
25193
25193
|
**Reference:** \`references/MEDIA.md\`
|
|
25194
25194
|
|
|
@@ -25196,16 +25196,15 @@ Use this when:
|
|
|
25196
25196
|
- You are about to run \`replicas media upload\`; this reference owns the complete Replicas-first, destination-second workflow
|
|
25197
25197
|
- You produce a screenshot, recording, generated image, or audio clip the user should see
|
|
25198
25198
|
- You need to show motion or an interaction (an animation, a popup that moves, a flow) in a PR or message: make a short looping GIF with \`replicas media gif\`, never a grid of frames
|
|
25199
|
-
- You produce a self-contained HTML report that should be linked from a pull request
|
|
25200
25199
|
- You record video output (browser automation, screen capture) \u2014 including the recommended aspect ratio and FPS
|
|
25201
25200
|
- You need to embed media in a Slack/Linear/GitHub/GitLab message AND keep a referenceable copy in the Replicas dashboard`;
|
|
25202
|
-
var REFERENCE8 = `# Media (Screenshots, GIFs, Recordings, Audio
|
|
25201
|
+
var REFERENCE8 = `# Media (Screenshots, GIFs, Recordings, Audio)
|
|
25203
25202
|
|
|
25204
|
-
This is the single source of truth for delivering screenshots, GIFs, screen recordings, generated diagrams, audio clips
|
|
25203
|
+
This is the single source of truth for delivering screenshots, GIFs, screen recordings, generated diagrams, and audio clips through Replicas and external surfaces (Slack, Linear, GitHub, GitLab). HTML pages are visuals: publish them with \`replicas visualize\` (see \`replicas visualize --help\`), not \`media upload\`.
|
|
25205
25204
|
|
|
25206
25205
|
## Always
|
|
25207
25206
|
|
|
25208
|
-
- Every picture, GIF, recording, or
|
|
25207
|
+
- Every picture, GIF, recording, or visual you mention in your reply to the user shows up in that reply: paste its printed \`\` line on its own line. Do this even when you also posted it to a PR, Slack, Linear, or GitLab, and even when the task came from one of them.
|
|
25209
25208
|
- Never send the user somewhere else to see it ("see the Canvas", "check the media tab"). Uploads also appear in the Canvas's media folder, so they stay there for later.
|
|
25210
25209
|
- Keep the whole \`replicas media upload\` output. Don't pipe it through \`tail\`, \`grep\`, or \`head\`; you need every line it prints.
|
|
25211
25210
|
|
|
@@ -25257,8 +25256,8 @@ Make comparisons easy to see at a glance, whether it's before and after or optio
|
|
|
25257
25256
|
|
|
25258
25257
|
Upload to Replicas in these cases \u2014 and **only** these cases:
|
|
25259
25258
|
|
|
25260
|
-
1. **Media you show the user.** A screenshot, screen recording, generated diagram, audio clip,
|
|
25261
|
-
2. **Files the user explicitly asks you to upload.** If the user sends or points at a file (image, video, audio
|
|
25259
|
+
1. **Media you show the user.** A screenshot, screen recording, generated diagram, or audio clip, once you decide to show it to the user or share it. Screenshots you take only to check your own work stay local. Upload before you delete the file or send it anywhere else. This applies even when you're also sending the file to Slack, Linear, GitHub, or GitLab.
|
|
25260
|
+
2. **Files the user explicitly asks you to upload.** If the user sends or points at a file (image, video, audio) and asks you to upload it, run \`replicas media upload\`. Otherwise leave it alone \u2014 files in the workspace the user did not ask about should not be auto-uploaded as media.
|
|
25262
25261
|
3. **Anything you plan to share externally (Slack, Linear, GitHub, GitLab, etc.).** Upload to Replicas first, then follow the destination's delivery path below.
|
|
25263
25262
|
|
|
25264
25263
|
If none of these apply, don't upload.
|
|
@@ -25268,7 +25267,7 @@ If none of these apply, don't upload.
|
|
|
25268
25267
|
\`replicas media upload\` is always the first delivery action. When the current task has an external destination, every other delivery step follows only after that command succeeds. For every file:
|
|
25269
25268
|
|
|
25270
25269
|
1. First, run \`replicas media upload\` with the original file. Do not begin any destination upload until the command succeeds. This creates the canonical dashboard copy and prints an embed plus a per-file dashboard link. Every image gets a public link, so its printed embed works in chat and on GitHub.
|
|
25271
|
-
2. Include the printed embed line in the Replicas chat reply.
|
|
25270
|
+
2. Include the printed embed line in the Replicas chat reply.
|
|
25272
25271
|
3. Identify every destination in the task and use its inline representation from the table below. Slack, Linear, and GitLab require an upload of the same original bytes; GitHub uses the image's printed public embed.
|
|
25273
25272
|
4. On Slack and Linear, put the **View in Replicas** dashboard link next to the destination embed as a secondary link. GitHub and GitLab need only the embed.
|
|
25274
25273
|
5. Check each destination response. If its upload or public link fails, say so explicitly and keep the dashboard link; never claim that a dashboard link is an inline embed.
|
|
@@ -25315,15 +25314,13 @@ For each media file, the CLI prints an embed line and a dashboard link:
|
|
|
25315
25314
|
|
|
25316
25315
|
Match each "View in Replicas" line to the embed line directly above it \u2014 that's the deep link for that file.
|
|
25317
25316
|
|
|
25318
|
-
HTML uploads print an \`HTML page\` Markdown link instead of an image embed. It previews the page inside Replicas chat only. They also print a \`Forge link (organization only)\` to use everywhere else: it opens a dedicated page, where signed-in organization members go straight to the report and signed-out members sign in first.
|
|
25319
|
-
|
|
25320
25317
|
Video and audio stay private unless they must play without Replicas sign-in; then pass \`--access public\` (alias \`--share\`):
|
|
25321
25318
|
|
|
25322
25319
|
\`\`\`bash
|
|
25323
25320
|
replicas media upload demo.mp4 --name "Checkout demo" --access public
|
|
25324
25321
|
\`\`\`
|
|
25325
25322
|
|
|
25326
|
-
Public links are unguessable bearer URLs: anyone with the link can view the file until it is rotated or revoked.
|
|
25323
|
+
Public links are unguessable bearer URLs: anyone with the link can view the file until it is rotated or revoked. Reuse the link printed at upload. \`replicas media share <media-id>\` issues a new link and breaks the old one everywhere it is embedded, and \`replicas media revoke <media-id>\` removes public access.
|
|
25327
25324
|
|
|
25328
25325
|
Permanently delete an upload with \`replicas media delete <media-id>\`. The backend only permits deletion when the media belongs to the current workspace; media from another workspace returns not found.
|
|
25329
25326
|
|
|
@@ -25331,8 +25328,8 @@ Permanently delete an upload with \`replicas media delete <media-id>\`. The back
|
|
|
25331
25328
|
|
|
25332
25329
|
| URL | What it is | Where it's allowed |
|
|
25333
25330
|
|---|---|---|
|
|
25334
|
-
| \`https://api.replicas.dev/v1/media/<id>\` (inside \`\`) | Authenticated API redirect printed for private video, audio, and
|
|
25335
|
-
| \`https://app.replicas.dev/organizations/<organization-id>/media/<media-id>\` | Organization-only page (\`Forge link\`) printed for every
|
|
25331
|
+
| \`https://api.replicas.dev/v1/media/<id>\` (inside \`\`) | Authenticated API redirect printed for private video, audio, and visuals \u2014 requires the Replicas chat session | **Only** in the Replicas chat reply. Nowhere else. |
|
|
25332
|
+
| \`https://app.replicas.dev/organizations/<organization-id>/media/<media-id>\` | Organization-only page (\`Forge link\`) printed for every visual, and for video or audio with \`--access organization\` | Pull requests and other places where every viewer belongs to the Replicas organization. |
|
|
25336
25333
|
| \`https://api.replicas.dev/v1/media/public/<id>/<token>\` | Public bearer URL printed for every image, and for video or audio with \`--access public\` | The Replicas chat reply and GitHub embeds. |
|
|
25337
25334
|
| \`https://app.replicas.dev/workspaces/<workspace-id>?mode=media&media=<media-id>\` | Authenticated dashboard deep link that opens the media file in the workspace | Everywhere external: Slack, GitHub, GitLab, Linear, PR bodies, comments, emails. |
|
|
25338
25335
|
|
|
@@ -25350,7 +25347,7 @@ This rule has no exceptions:
|
|
|
25350
25347
|
|
|
25351
25348
|
### ALWAYS link a Replicas web URL \u2014 never the authenticated API URL \u2014 when referencing media externally
|
|
25352
25349
|
|
|
25353
|
-
For
|
|
25350
|
+
For a visual, use its printed \`Forge link\`: it opens a dedicated page, where signed-in organization members go straight to it and signed-out members sign in first. For other authenticated media references, use the dashboard form:
|
|
25354
25351
|
|
|
25355
25352
|
\`\`\`
|
|
25356
25353
|
https://app.replicas.dev/workspaces/<workspace-id>?mode=media&media=<media-id>
|
|
@@ -25364,7 +25361,7 @@ Always render as a markdown / mrkdwn hyperlink labeled **View in Replicas** \u20
|
|
|
25364
25361
|
|
|
25365
25362
|
## In your Replicas chat reply
|
|
25366
25363
|
|
|
25367
|
-
Include each markdown embed line **verbatim** where you want that file to render inline. The chat substitutes each one with an embedded image, video, or audio player.
|
|
25364
|
+
Include each markdown embed line **verbatim** where you want that file to render inline. The chat substitutes each one with an embedded image, video, or audio player. Multiple embeds can appear in a single reply.
|
|
25368
25365
|
|
|
25369
25366
|
Never reply with only dashboard links (for example a "Links:" list of \`?mode=media&media=\` URLs) in place of the embeds: a dashboard link renders as plain text, so the user sees nothing inline. This applies even when the task came from Slack, Linear, GitHub, or GitLab; the chat reply still gets the embeds.
|
|
25370
25367
|
|
|
@@ -25507,7 +25504,7 @@ GitHub does not play external videos inline, so show motion with a GIF (see *Mak
|
|
|
25507
25504
|
|
|
25508
25505
|
Public links are unauthenticated bearer capabilities. Revoke with \`replicas media revoke <media-id>\` **only** when the user asks or the media should intentionally stop rendering; never as routine post-task cleanup, since revoking breaks every embed already posted on the PR.
|
|
25509
25506
|
|
|
25510
|
-
For a self-contained HTML report,
|
|
25507
|
+
For a self-contained HTML report, publish it with \`replicas visualize\` and put the printed \`Forge link\` Markdown in the PR body or comment. GitHub cannot render the page inline, so use the normal link rather than image syntax. Visuals cannot be shared publicly.
|
|
25511
25508
|
|
|
25512
25509
|
Do **not** commit screenshots to the repo, use placeholder URLs, expose the authenticated \`/v1/media/<id>\` URL, or automate GitHub's undocumented browser upload flow.
|
|
25513
25510
|
|
|
@@ -25545,9 +25542,8 @@ Auto-detected from the filename extension:
|
|
|
25545
25542
|
| \`png\`, \`jpg\`, \`jpeg\`, \`webp\`, \`gif\` | image |
|
|
25546
25543
|
| \`mp4\`, \`webm\` | video |
|
|
25547
25544
|
| \`mp3\`, \`wav\` | audio |
|
|
25548
|
-
| \`html\`, \`htm\` | html |
|
|
25549
25545
|
|
|
25550
|
-
For other extensions, pass \`--kind image|video|audio
|
|
25546
|
+
For other extensions, pass \`--kind image|video|audio\` explicitly. The kind applies to every file in that invocation, so group files of the same kind together:
|
|
25551
25547
|
|
|
25552
25548
|
\`\`\`bash
|
|
25553
25549
|
replicas media upload diagram.svg --name "Architecture diagram" --kind image
|
|
@@ -25556,16 +25552,16 @@ replicas media upload chart-a.svg chart-b.svg --name "Latency chart" --name "Err
|
|
|
25556
25552
|
|
|
25557
25553
|
## Options
|
|
25558
25554
|
|
|
25559
|
-
- \`--kind <image|video|audio
|
|
25555
|
+
- \`--kind <image|video|audio>\` \u2014 override auto-detection
|
|
25560
25556
|
- \`--name <name>\`: required display name shown in the dashboard, one per file in order
|
|
25561
25557
|
- \`--session-id <id>\` \u2014 associate the upload with a specific session
|
|
25562
|
-
- \`--access organization|public\`: for video and audio, print an organization-only link or create a public bearer URL. Images are always public
|
|
25563
|
-
- \`--share\`: alias for \`--access public
|
|
25558
|
+
- \`--access organization|public\`: for video and audio, print an organization-only link or create a public bearer URL. Images are always public
|
|
25559
|
+
- \`--share\`: alias for \`--access public\`
|
|
25564
25560
|
`;
|
|
25565
25561
|
var MEDIA_ABILITY = {
|
|
25566
25562
|
label: "Media",
|
|
25567
25563
|
description: "Deliver media to Replicas and destination workflows with inline uploads.",
|
|
25568
|
-
bullet: "- Producing or showing the user any media \u2014 screenshots, screen recordings, generated images or diagrams, audio clips
|
|
25564
|
+
bullet: "- Producing or showing the user any media \u2014 screenshots, screen recordings, generated images or diagrams, or audio clips \u2014 including in your Replicas chat reply, PR descriptions/comments, and other external platforms. Paste the printed `` embeds in your chat reply, never only dashboard links, and upload the file itself to Slack, Linear, or GitLab. To show motion, turn the recording into a short GIF with `replicas media gif` instead of a grid of frames",
|
|
25569
25565
|
section: SECTION8,
|
|
25570
25566
|
referenceFile: { name: "MEDIA.md", content: REFERENCE8 }
|
|
25571
25567
|
};
|
|
@@ -26384,6 +26380,245 @@ var SLACK_ABILITY = {
|
|
|
26384
26380
|
referenceFile: { name: "SLACK.md", content: REFERENCE14 }
|
|
26385
26381
|
};
|
|
26386
26382
|
|
|
26383
|
+
// ../shared/src/design-tokens.ts
|
|
26384
|
+
var DESIGN_COLORS = {
|
|
26385
|
+
dark: {
|
|
26386
|
+
background: "#111111",
|
|
26387
|
+
foreground: "#ededed",
|
|
26388
|
+
muted: "#1f1f1f",
|
|
26389
|
+
mutedForeground: "#a3a3a3",
|
|
26390
|
+
accent: "#2b2b2b",
|
|
26391
|
+
primary: "#3eeba3",
|
|
26392
|
+
primaryForeground: "#111111",
|
|
26393
|
+
border: "#2a2a2a",
|
|
26394
|
+
destructive: "#ef4444",
|
|
26395
|
+
warning: "#f59e0b",
|
|
26396
|
+
sidebar: "#181818",
|
|
26397
|
+
sidebarAccent: "#1f1f1f",
|
|
26398
|
+
chatCodeBackground: "#111111",
|
|
26399
|
+
chatCodeHeader: "#151515",
|
|
26400
|
+
chatCodeBorder: "#333333",
|
|
26401
|
+
chatCodeForeground: "#e5e7eb",
|
|
26402
|
+
chatCodeMuted: "#9ca3af"
|
|
26403
|
+
},
|
|
26404
|
+
light: {
|
|
26405
|
+
background: "#ffffff",
|
|
26406
|
+
foreground: "#162118",
|
|
26407
|
+
muted: "#f2f3f1",
|
|
26408
|
+
mutedForeground: "#67706a",
|
|
26409
|
+
accent: "#e8ece7",
|
|
26410
|
+
primary: "#2f9d57",
|
|
26411
|
+
primaryForeground: "#ffffff",
|
|
26412
|
+
border: "#e2e6e0",
|
|
26413
|
+
destructive: "#dc2626",
|
|
26414
|
+
warning: "#92400e",
|
|
26415
|
+
sidebar: "#f7f8f6",
|
|
26416
|
+
sidebarAccent: "#e8ece7",
|
|
26417
|
+
chatCodeBackground: "#f7f8f6",
|
|
26418
|
+
chatCodeHeader: "#eef1ed",
|
|
26419
|
+
chatCodeBorder: "#d9ded8",
|
|
26420
|
+
chatCodeForeground: "#162118",
|
|
26421
|
+
chatCodeMuted: "#67706a"
|
|
26422
|
+
}
|
|
26423
|
+
};
|
|
26424
|
+
|
|
26425
|
+
// ../shared/src/visuals.ts
|
|
26426
|
+
var VISUAL_MEASURE_WIDTHS = [320, 375, 430, 520, 640, 760, 880, 952];
|
|
26427
|
+
var VISUAL_COLUMN_WIDTH = 760;
|
|
26428
|
+
var VISUAL_PHONE_WIDTH = 390;
|
|
26429
|
+
var VISUAL_MIN_HEIGHT = 24;
|
|
26430
|
+
var VISUAL_MAX_HEIGHT = 4e3;
|
|
26431
|
+
var VISUAL_FONT_PATH = "/fonts/GeneralSans-Variable.woff2";
|
|
26432
|
+
var FONT_SANS = '"General Sans", system-ui, -apple-system, "Segoe UI", sans-serif';
|
|
26433
|
+
var FONT_MONO = '"Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace';
|
|
26434
|
+
var CHART_COLORS = {
|
|
26435
|
+
dark: ["#199e70", "#3987e5", "#d95926", "#9085e9", "#c98500", "#d55181"],
|
|
26436
|
+
light: ["#2f9d57", "#2a78d6", "#eb6834", "#4a3aa7", "#eda100", "#e87ba4"]
|
|
26437
|
+
};
|
|
26438
|
+
function visualTheme(appearance, fontUrl) {
|
|
26439
|
+
const colors = DESIGN_COLORS[appearance];
|
|
26440
|
+
return {
|
|
26441
|
+
appearance,
|
|
26442
|
+
variables: {
|
|
26443
|
+
"--background": colors.background,
|
|
26444
|
+
"--foreground": colors.foreground,
|
|
26445
|
+
"--muted": colors.muted,
|
|
26446
|
+
"--muted-foreground": colors.mutedForeground,
|
|
26447
|
+
"--accent": colors.accent,
|
|
26448
|
+
"--primary": colors.primary,
|
|
26449
|
+
"--primary-foreground": colors.primaryForeground,
|
|
26450
|
+
"--border": colors.border,
|
|
26451
|
+
"--ring": colors.primary,
|
|
26452
|
+
"--destructive": colors.destructive,
|
|
26453
|
+
"--warning": colors.warning,
|
|
26454
|
+
"--code-background": colors.chatCodeBackground,
|
|
26455
|
+
"--code-foreground": colors.chatCodeForeground,
|
|
26456
|
+
...Object.fromEntries(CHART_COLORS[appearance].map((color, index) => [`--chart-${index + 1}`, color])),
|
|
26457
|
+
"--radius": "6px",
|
|
26458
|
+
"--font-sans": FONT_SANS,
|
|
26459
|
+
"--font-mono": FONT_MONO
|
|
26460
|
+
},
|
|
26461
|
+
...fontUrl ? { fontUrl } : {}
|
|
26462
|
+
};
|
|
26463
|
+
}
|
|
26464
|
+
var VISUAL_PAGE_RULES = `- Write one self-contained HTML file with inline <style> and <script>; a fragment without <html> or <head> is fine. Put data inline instead of fetching it. Libraries may load from public CDNs such as cdn.jsdelivr.net. Local images written as absolute paths (src="/abs/shot.png", url(/abs/bg.webp)) are inlined when you publish.
|
|
26465
|
+
- The page sits directly on the chat background, as wide as your reply (about ${VISUAL_COLUMN_WIDTH}px on desktop, ${VISUAL_PHONE_WIDTH - 30}px on phones), with its left edge aligned to your text. Leave html and body transparent, use a fluid width with no outer padding, and add no outer card, border, or title banner: the page is part of your reply.
|
|
26466
|
+
- Let content set the height. Never use 100vh, height:100%, or min-height:100vh on html, body, or the outer element; publishing refuses a page whose height follows the frame. Give charts fixed pixel heights.
|
|
26467
|
+
- Color everything with the theme variables so the page follows light and dark mode live: --foreground, --muted-foreground (secondary text), --background, --muted (subtle fills), --accent, --border, --primary and --primary-foreground (Replicas green), --destructive, --warning, --code-background, --code-foreground, and --chart-1 to --chart-6 for series (use them in order and label series directly). Never hardcode a light or dark palette. A canvas or chart library that needs literal colors can read them with getComputedStyle(document.documentElement) and redraw on matchMedia("(prefers-color-scheme: dark)") changes.
|
|
26468
|
+
- Text is 13px General Sans, like the chat; --font-sans and --font-mono are set. Keep Replicas' flat look: separate sections with 1px var(--border) lines or var(--muted) fills, without shadows or rounded cards. Use var(--radius) only on small controls.
|
|
26469
|
+
- Interaction is welcome: hover details, tabs, toggles, sliders. Links open in a new browser tab.
|
|
26470
|
+
- Keep the top-right corner (about 110\xD740px) free of anything essential: the chat shows the visual's actions there on hover.`;
|
|
26471
|
+
var THEME_FRAGMENT_KEY = "replicas-theme";
|
|
26472
|
+
var HOST_CONTEXT_CHANGED_METHOD = "ui/notifications/host-context-changed";
|
|
26473
|
+
var SIZE_CHANGED_METHOD = "ui/notifications/size-changed";
|
|
26474
|
+
var OPEN_LINK_METHOD = "ui/open-link";
|
|
26475
|
+
function visualThemeFragment(theme) {
|
|
26476
|
+
return `#${THEME_FRAGMENT_KEY}=${encodeURIComponent(JSON.stringify(theme))}`;
|
|
26477
|
+
}
|
|
26478
|
+
function clampVisualHeight(height) {
|
|
26479
|
+
return Math.min(VISUAL_MAX_HEIGHT, Math.max(VISUAL_MIN_HEIGHT, Math.round(height)));
|
|
26480
|
+
}
|
|
26481
|
+
var VISUAL_CONTENT_HEIGHT_EXPRESSION = "(function(){var r=document.documentElement;return Math.ceil(r.scrollHeight>r.clientHeight?r.scrollHeight:r.getBoundingClientRect().height)})()";
|
|
26482
|
+
var BASE_CSS = "html{color:var(--foreground);font-family:var(--font-sans);font-size:13px;line-height:1.5;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%;scrollbar-width:none}html::-webkit-scrollbar{display:none}body{margin:0}:where(*,*::before,*::after){box-sizing:border-box}code,kbd,pre,samp{font-family:var(--font-mono)}:where(:focus-visible){outline:2px solid var(--ring);outline-offset:-2px}";
|
|
26483
|
+
function rootRule(theme) {
|
|
26484
|
+
const declarations = Object.entries(theme.variables).map(([name, value]) => `${name}:${value};`).join("");
|
|
26485
|
+
return `:root{color-scheme:${theme.appearance};${declarations}}`;
|
|
26486
|
+
}
|
|
26487
|
+
var THEME_STYLE_ID = "replicas-visual-theme";
|
|
26488
|
+
var BOOTSTRAP_SCRIPT = `
|
|
26489
|
+
(() => {
|
|
26490
|
+
const style = document.getElementById('${THEME_STYLE_ID}');
|
|
26491
|
+
const nativeView = window.ReactNativeWebView;
|
|
26492
|
+
const framed = window.parent !== window;
|
|
26493
|
+
const post = (message) => nativeView ? nativeView.postMessage(JSON.stringify(message)) : window.parent.postMessage(message, '*');
|
|
26494
|
+
const applyTheme = (theme) => {
|
|
26495
|
+
const variables = Object.entries(theme.variables).map(([name, value]) => name + ':' + value + ';').join('');
|
|
26496
|
+
const font = theme.fontUrl ? '@font-face{font-family:"General Sans";src:url("' + theme.fontUrl + '") format("woff2");font-weight:200 700;font-display:swap}' : '';
|
|
26497
|
+
style.textContent = ':root{color-scheme:' + theme.appearance + ';' + variables + '}' + font + ${JSON.stringify(BASE_CSS)};
|
|
26498
|
+
};
|
|
26499
|
+
try {
|
|
26500
|
+
const fragment = /[#&]${THEME_FRAGMENT_KEY}=([^&]*)/.exec(location.hash);
|
|
26501
|
+
if (fragment) {
|
|
26502
|
+
applyTheme(JSON.parse(decodeURIComponent(fragment[1])));
|
|
26503
|
+
history.replaceState(history.state, '', location.pathname + location.search);
|
|
26504
|
+
}
|
|
26505
|
+
} catch {}
|
|
26506
|
+
if (!nativeView) {
|
|
26507
|
+
document.addEventListener('click', (event) => {
|
|
26508
|
+
const anchor = event.isTrusted && event.composedPath().find((target) => target instanceof Element && target.matches('a[href]'));
|
|
26509
|
+
if (!anchor) return;
|
|
26510
|
+
const url = new URL(anchor.getAttribute('href'), document.baseURI);
|
|
26511
|
+
if (!/^https?:$/.test(url.protocol) || url.href.split('#')[0] === location.href.split('#')[0]) return;
|
|
26512
|
+
if (framed) {
|
|
26513
|
+
event.preventDefault();
|
|
26514
|
+
post({ jsonrpc: '2.0', method: '${OPEN_LINK_METHOD}', params: { url: url.href } });
|
|
26515
|
+
} else {
|
|
26516
|
+
anchor.setAttribute('target', '_blank');
|
|
26517
|
+
anchor.setAttribute('rel', 'noopener');
|
|
26518
|
+
}
|
|
26519
|
+
}, true);
|
|
26520
|
+
}
|
|
26521
|
+
if (!framed && !nativeView) return;
|
|
26522
|
+
let reportedHeight;
|
|
26523
|
+
const reportHeight = () => {
|
|
26524
|
+
const height = ${VISUAL_CONTENT_HEIGHT_EXPRESSION};
|
|
26525
|
+
if (height === reportedHeight) return;
|
|
26526
|
+
reportedHeight = height;
|
|
26527
|
+
post({ jsonrpc: '2.0', method: '${SIZE_CHANGED_METHOD}', params: { height } });
|
|
26528
|
+
};
|
|
26529
|
+
window.addEventListener('message', (event) => {
|
|
26530
|
+
if (event.source !== window.parent || event.data?.method !== '${HOST_CONTEXT_CHANGED_METHOD}') return;
|
|
26531
|
+
const { theme, styles, fontUrl } = event.data.params;
|
|
26532
|
+
applyTheme({ appearance: theme, variables: styles.variables, fontUrl });
|
|
26533
|
+
reportedHeight = undefined;
|
|
26534
|
+
if (document.readyState !== 'loading') reportHeight();
|
|
26535
|
+
});
|
|
26536
|
+
const resizeObserver = new ResizeObserver(reportHeight);
|
|
26537
|
+
resizeObserver.observe(document.documentElement);
|
|
26538
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
26539
|
+
resizeObserver.observe(document.body);
|
|
26540
|
+
reportHeight();
|
|
26541
|
+
});
|
|
26542
|
+
window.addEventListener('load', reportHeight);
|
|
26543
|
+
})();
|
|
26544
|
+
`.trim().replace(/\s*\n\s*/g, " ");
|
|
26545
|
+
function bootstrapMarkup(scan) {
|
|
26546
|
+
const css = `${rootRule(visualTheme("dark"))}@media (prefers-color-scheme: light){${rootRule(visualTheme("light"))}}${BASE_CSS}`;
|
|
26547
|
+
return [
|
|
26548
|
+
/<meta\s[^>]*charset/i.test(scan.slice(0, 4096)) ? "" : '<meta charset="utf-8">',
|
|
26549
|
+
/<meta\s[^>]*name\s*=\s*["']?viewport/i.test(scan) ? "" : '<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
26550
|
+
`<style id="${THEME_STYLE_ID}">${css}</style>`,
|
|
26551
|
+
`<script>${BOOTSTRAP_SCRIPT}</script>`
|
|
26552
|
+
].join("");
|
|
26553
|
+
}
|
|
26554
|
+
var blankNonMarkup = (html) => html.replace(
|
|
26555
|
+
/<!--[\s\S]*?(?:-->|$)|<(script|style|textarea|title|xmp|iframe|noembed|noframes|noscript|template)\b[\s\S]*?(?:<\/\1\s*>|$)/gi,
|
|
26556
|
+
(match) => " ".repeat(match.length)
|
|
26557
|
+
);
|
|
26558
|
+
function injectVisualBootstrap(html) {
|
|
26559
|
+
const scan = blankNonMarkup(html);
|
|
26560
|
+
const markup = bootstrapMarkup(scan);
|
|
26561
|
+
const doctype = /^\s*<!doctype[^>]*>/i.exec(scan);
|
|
26562
|
+
const prefix = doctype ? "" : "<!doctype html>";
|
|
26563
|
+
const headOpen = /<head(?:\s[^>]*)?>/i.exec(scan);
|
|
26564
|
+
if (headOpen) {
|
|
26565
|
+
const at2 = headOpen.index + headOpen[0].length;
|
|
26566
|
+
return `${prefix}${html.slice(0, at2)}${markup}${html.slice(at2)}`;
|
|
26567
|
+
}
|
|
26568
|
+
const htmlOpen = /<html(?:\s[^>]*)?>/i.exec(scan);
|
|
26569
|
+
const at = htmlOpen ? htmlOpen.index + htmlOpen[0].length : doctype ? doctype[0].length : 0;
|
|
26570
|
+
return `${prefix}${html.slice(0, at)}<head>${markup}</head>${html.slice(at)}`;
|
|
26571
|
+
}
|
|
26572
|
+
|
|
26573
|
+
// ../shared/src/default-skills/replicas-agent/abilities/visuals.ts
|
|
26574
|
+
var SECTION15 = `### Visuals
|
|
26575
|
+
Show a chart, diagram, comparison, timeline, or mockup inline in your Replicas chat reply, in the chat's own font and colors.
|
|
26576
|
+
|
|
26577
|
+
**Reference:** \`references/VISUALS.md\`
|
|
26578
|
+
|
|
26579
|
+
Use this when:
|
|
26580
|
+
- Seeing it would explain faster than reading it: numbers over time, a flow or architecture, a before and after, options side by side
|
|
26581
|
+
- The user asks to see, chart, plot, compare, or visualize something`;
|
|
26582
|
+
var REFERENCE15 = `# Visuals
|
|
26583
|
+
|
|
26584
|
+
A visual is a self-contained HTML page shown inline in your Replicas chat reply. It sits on the chat background in the chat's font and colors, grows to fit its content, and follows the user's light or dark theme live.
|
|
26585
|
+
|
|
26586
|
+
## When to make one
|
|
26587
|
+
|
|
26588
|
+
Make one when seeing beats reading: how parts of a system connect, how a request or data flows, numbers or options compared, a timeline, a before and after, or a UI mockup. Make one when the user asks to see, chart, compare, sketch, or visualize something. Never draw these as ASCII art or text diagrams.
|
|
26589
|
+
|
|
26590
|
+
Don't make one when:
|
|
26591
|
+
- A short answer or list covers it.
|
|
26592
|
+
- It is a plain table: write a Markdown table.
|
|
26593
|
+
- It is screenshots, or a before and after of them: upload them as images, which readers can open full screen and zoom. See "Before and after" in \`references/MEDIA.md\`.
|
|
26594
|
+
- The user wants a page, site, or component in their project: build that in the repo.
|
|
26595
|
+
|
|
26596
|
+
## Make it
|
|
26597
|
+
|
|
26598
|
+
1. Write the page, for example \`/tmp/visuals/<name>.html\`, following the page rules below.
|
|
26599
|
+
2. Run \`replicas visualize <page.html> --title "<short title>"\`. It checks the page in a headless browser, publishes it, and prints two screenshots (dark at desktop width, light at phone width) with any console errors and layout warnings.
|
|
26600
|
+
3. Open both screenshots and look. If anything is wrong, fix the page and publish again, then use the newest line. While iterating, \`replicas visualize <page.html> --check\` renders without publishing.
|
|
26601
|
+
4. Paste the printed \`\` line on its own line where the visual belongs in your final reply.
|
|
26602
|
+
|
|
26603
|
+
## Page rules
|
|
26604
|
+
|
|
26605
|
+
${VISUAL_PAGE_RULES}
|
|
26606
|
+
|
|
26607
|
+
## In your reply
|
|
26608
|
+
|
|
26609
|
+
- The user sees the visual itself. Don't introduce it ("Here's a chart", "Here's the diagram"), say where it is, or restate its data. Add only what it doesn't say, such as the takeaway or a caveat.
|
|
26610
|
+
- Paste the line in your final reply, not in a progress update. To change a visual, edit the page, publish it again, and paste the new line.
|
|
26611
|
+
- The line only renders in Replicas chat. When you post to pull requests, Slack, Linear, or anywhere else, link the printed Forge link instead. Leave the Forge link out of your chat reply.
|
|
26612
|
+
`;
|
|
26613
|
+
var VISUALS_ABILITY = {
|
|
26614
|
+
label: "Visuals",
|
|
26615
|
+
description: "Show charts, diagrams, comparisons, and mockups inline in chat replies.",
|
|
26616
|
+
// The visuals_instructions system prompt block is its trigger.
|
|
26617
|
+
bullet: "",
|
|
26618
|
+
section: SECTION15,
|
|
26619
|
+
referenceFile: { name: "VISUALS.md", content: REFERENCE15 }
|
|
26620
|
+
};
|
|
26621
|
+
|
|
26387
26622
|
// ../shared/src/default-skills/replicas-agent/registry.ts
|
|
26388
26623
|
var REPLICAS_AGENT_ABILITY_REGISTRY = {
|
|
26389
26624
|
replicas: REPLICAS_ABILITY,
|
|
@@ -26399,7 +26634,8 @@ var REPLICAS_AGENT_ABILITY_REGISTRY = {
|
|
|
26399
26634
|
plugins: PLUGINS_ABILITY,
|
|
26400
26635
|
previews: PREVIEWS_ABILITY,
|
|
26401
26636
|
sentry: SENTRY_ABILITY,
|
|
26402
|
-
slack: SLACK_ABILITY
|
|
26637
|
+
slack: SLACK_ABILITY,
|
|
26638
|
+
visuals: VISUALS_ABILITY
|
|
26403
26639
|
};
|
|
26404
26640
|
var REPLICAS_AGENT_ABILITIES = Object.keys(
|
|
26405
26641
|
REPLICAS_AGENT_ABILITY_REGISTRY
|
|
@@ -26604,7 +26840,7 @@ function stripAttachmentNote(text3) {
|
|
|
26604
26840
|
}
|
|
26605
26841
|
return {
|
|
26606
26842
|
content,
|
|
26607
|
-
files: match ? [...match[1].matchAll(/^- (.+)$/gm)].map(([,
|
|
26843
|
+
files: match ? [...match[1].matchAll(/^- (.+)$/gm)].map(([, path10]) => path10) : [],
|
|
26608
26844
|
walkthroughs
|
|
26609
26845
|
};
|
|
26610
26846
|
}
|
|
@@ -27493,7 +27729,7 @@ function parseOpencodeEvents(events) {
|
|
|
27493
27729
|
upsertDisplayMessage(messages, {
|
|
27494
27730
|
id,
|
|
27495
27731
|
type: "file_change",
|
|
27496
|
-
changes: files.map((
|
|
27732
|
+
changes: files.map((path10) => ({ path: path10, kind: "update" })),
|
|
27497
27733
|
status: "completed",
|
|
27498
27734
|
timestamp
|
|
27499
27735
|
});
|
|
@@ -28781,7 +29017,7 @@ var headlessAgentRequestSchema = external_exports.object({
|
|
|
28781
29017
|
});
|
|
28782
29018
|
|
|
28783
29019
|
// ../shared/src/cli-version.ts
|
|
28784
|
-
var CLI_VERSION = "0.3.
|
|
29020
|
+
var CLI_VERSION = "0.3.131";
|
|
28785
29021
|
|
|
28786
29022
|
// ../shared/src/version.ts
|
|
28787
29023
|
function compareVersions(v1, v2) {
|
|
@@ -29506,9 +29742,6 @@ var MEDIA_KIND = {
|
|
|
29506
29742
|
};
|
|
29507
29743
|
var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO, MEDIA_KIND.HTML];
|
|
29508
29744
|
var PUBLIC_MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
|
|
29509
|
-
function isPublicMediaKind(value) {
|
|
29510
|
-
return PUBLIC_MEDIA_KINDS.some((kind) => kind === value);
|
|
29511
|
-
}
|
|
29512
29745
|
var MAX_ENGINE_LOG_BYTES = 50 * 1024 * 1024;
|
|
29513
29746
|
|
|
29514
29747
|
// ../shared/src/sse.ts
|
|
@@ -31311,8 +31544,8 @@ async function claudeAuthCommand(options = {}) {
|
|
|
31311
31544
|
var CLIENT_ID = "1031625952748946";
|
|
31312
31545
|
var AUTH_URL = "https://auth.meta.com";
|
|
31313
31546
|
var API_KEY_URL = "https://api.meta.ai/muse-code/key";
|
|
31314
|
-
async function post(
|
|
31315
|
-
return fetch(`${AUTH_URL}${
|
|
31547
|
+
async function post(path10, body) {
|
|
31548
|
+
return fetch(`${AUTH_URL}${path10}`, {
|
|
31316
31549
|
method: "POST",
|
|
31317
31550
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
31318
31551
|
body
|
|
@@ -31508,7 +31741,7 @@ async function credentialsGetCommand(service, options) {
|
|
|
31508
31741
|
});
|
|
31509
31742
|
}
|
|
31510
31743
|
async function credentialsSetCommand(service, options) {
|
|
31511
|
-
const
|
|
31744
|
+
const path10 = credentialsPath(service, options, true);
|
|
31512
31745
|
const { org: org2, models, ...fields } = options;
|
|
31513
31746
|
const secretField = service === "bedrock" ? "aws_secret_access_key" : "api_key";
|
|
31514
31747
|
const body = {
|
|
@@ -31516,7 +31749,7 @@ async function credentialsSetCommand(service, options) {
|
|
|
31516
31749
|
...models ? { models: models.split(",").map((model2) => model2.trim()).filter(Boolean) } : {},
|
|
31517
31750
|
[secretField]: await readSecret(service, secretField === "api_key" ? "API key" : "AWS secret access key")
|
|
31518
31751
|
};
|
|
31519
|
-
const response = await orgAuthenticatedFetch(
|
|
31752
|
+
const response = await orgAuthenticatedFetch(path10, { method: "POST", body });
|
|
31520
31753
|
output(response, () => console.log(import_chalk17.default.green(`Saved ${scopeLabel(options)} ${service} credentials.`)));
|
|
31521
31754
|
}
|
|
31522
31755
|
async function credentialsRemoveCommand(service, options) {
|
|
@@ -32788,8 +33021,8 @@ ${url2}`));
|
|
|
32788
33021
|
});
|
|
32789
33022
|
}
|
|
32790
33023
|
async function prMergeCommand(id, prUrl) {
|
|
32791
|
-
await orgAuthenticatedFetch(`/v1/replica/${id}/pull-requests/merge`, { method: "POST", body: { prUrl } });
|
|
32792
|
-
outputSuccess(
|
|
33024
|
+
const { queued } = await orgAuthenticatedFetch(`/v1/replica/${id}/pull-requests/merge`, { method: "POST", body: { prUrl } });
|
|
33025
|
+
outputSuccess(`${queued ? "Added to the merge queue:" : "Merged"} ${prUrl}`);
|
|
32793
33026
|
}
|
|
32794
33027
|
function prUpdateCommand(action) {
|
|
32795
33028
|
return async (id, prUrl) => {
|
|
@@ -32829,7 +33062,7 @@ async function canvasPutCommand(id, filename, options) {
|
|
|
32829
33062
|
|
|
32830
33063
|
// src/commands/chat.ts
|
|
32831
33064
|
var import_chalk23 = __toESM(require("chalk"));
|
|
32832
|
-
var chatPath2 = (id, chatId,
|
|
33065
|
+
var chatPath2 = (id, chatId, path10 = "") => `/v1/replica/${id}/chats/${chatId}${path10}`;
|
|
32833
33066
|
function printQueue(queue) {
|
|
32834
33067
|
if (queue.length === 0) {
|
|
32835
33068
|
console.log("Queue is empty.");
|
|
@@ -32914,8 +33147,8 @@ async function chatQueueListCommand(id, chatId) {
|
|
|
32914
33147
|
printQueue(queue);
|
|
32915
33148
|
});
|
|
32916
33149
|
}
|
|
32917
|
-
async function mutateQueue(
|
|
32918
|
-
output(await orgAuthenticatedFetch(
|
|
33150
|
+
async function mutateQueue(path10, method, body) {
|
|
33151
|
+
output(await orgAuthenticatedFetch(path10, { method, body }), ({ queue }) => {
|
|
32919
33152
|
if (queue) printQueue(queue);
|
|
32920
33153
|
else console.log("Done.");
|
|
32921
33154
|
});
|
|
@@ -32928,7 +33161,7 @@ var chatQueueActionCommand = (action) => (id, chatId, messageId) => mutateQueue(
|
|
|
32928
33161
|
|
|
32929
33162
|
// src/commands/plugins.ts
|
|
32930
33163
|
var import_chalk24 = __toESM(require("chalk"));
|
|
32931
|
-
var pluginsPath = async ({ env },
|
|
33164
|
+
var pluginsPath = async ({ env }, path10) => env ? `/v1/environments/${await resolveEnvironmentId(env)}/plugins${path10}` : `/v1/plugins${path10}`;
|
|
32932
33165
|
async function pluginsListCommand(options) {
|
|
32933
33166
|
output(await orgAuthenticatedFetch(await pluginsPath(options, "/connections")), ({ plugins: plugins2, environment: environment2 }) => {
|
|
32934
33167
|
if (!options.all && environment2.length === 0) console.log("No plugins connected. Pass --all to list available plugins.");
|
|
@@ -33901,10 +34134,10 @@ var AgentRequestError = class extends Error {
|
|
|
33901
34134
|
this.status = status;
|
|
33902
34135
|
}
|
|
33903
34136
|
};
|
|
33904
|
-
async function agentFetch(
|
|
33905
|
-
return (await agentRequest(
|
|
34137
|
+
async function agentFetch(path10, options) {
|
|
34138
|
+
return (await agentRequest(path10, options)).json();
|
|
33906
34139
|
}
|
|
33907
|
-
async function agentRequest(
|
|
34140
|
+
async function agentRequest(path10, options) {
|
|
33908
34141
|
const config3 = readAgentConfig();
|
|
33909
34142
|
if (!config3) {
|
|
33910
34143
|
throw new Error("Agent mode config not found");
|
|
@@ -33920,7 +34153,7 @@ async function agentRequest(path8, options) {
|
|
|
33920
34153
|
...options?.headers || {}
|
|
33921
34154
|
};
|
|
33922
34155
|
const body = rawBody === void 0 ? void 0 : rawBody instanceof FormData || rawBody instanceof ReadableStream ? rawBody : JSON.stringify(rawBody);
|
|
33923
|
-
const response = await fetch(`${MONOLITH_URL}${
|
|
34156
|
+
const response = await fetch(`${MONOLITH_URL}${path10}`, {
|
|
33924
34157
|
...options,
|
|
33925
34158
|
headers,
|
|
33926
34159
|
body,
|
|
@@ -33932,7 +34165,7 @@ async function agentRequest(path8, options) {
|
|
|
33932
34165
|
}
|
|
33933
34166
|
return response;
|
|
33934
34167
|
}
|
|
33935
|
-
async function engineFetch(
|
|
34168
|
+
async function engineFetch(path10, options) {
|
|
33936
34169
|
const config3 = readAgentConfig();
|
|
33937
34170
|
if (!config3) {
|
|
33938
34171
|
throw new Error("Agent mode config not found");
|
|
@@ -33942,7 +34175,7 @@ async function engineFetch(path8, options) {
|
|
|
33942
34175
|
"Content-Type": "application/json",
|
|
33943
34176
|
...options?.headers || {}
|
|
33944
34177
|
};
|
|
33945
|
-
const response = await fetch(`http://localhost:${ENGINE_PORT}${
|
|
34178
|
+
const response = await fetch(`http://localhost:${ENGINE_PORT}${path10}`, {
|
|
33946
34179
|
...options,
|
|
33947
34180
|
headers,
|
|
33948
34181
|
body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0
|
|
@@ -34033,7 +34266,7 @@ var MEDIA_ACCESS_VALUES = ["organization", "public"];
|
|
|
34033
34266
|
function publicMediaUrl(mediaId, token) {
|
|
34034
34267
|
return `${MONOLITH_URL}/v1/media/public/${mediaId}/${token}`;
|
|
34035
34268
|
}
|
|
34036
|
-
var MEDIA_DELIVERY_HINT = "Next: paste each media line above (`
|
|
34269
|
+
var MEDIA_DELIVERY_HINT = "Next: paste each media line above (`` or `[\u2026](\u2026)`) verbatim into your Replicas chat reply so it renders inline; View in Replicas links are secondary and never replace it. Slack, Linear, and GitLab also need a native upload of the file bytes; see the replicas-agent skill's references/MEDIA.md.";
|
|
34037
34270
|
var markdownLabel = (name) => name.replace(/[[\]]/g, "");
|
|
34038
34271
|
function formatMediaEmbed(name, media2, publicUrl) {
|
|
34039
34272
|
if (publicUrl) return `${media2.kind === MEDIA_KIND.IMAGE ? "!" : ""}[${markdownLabel(name)}](${publicUrl})`;
|
|
@@ -34069,6 +34302,13 @@ function resolveKind(ext, override) {
|
|
|
34069
34302
|
}
|
|
34070
34303
|
return inferred;
|
|
34071
34304
|
}
|
|
34305
|
+
async function uploadMedia(file2, fileName, fields) {
|
|
34306
|
+
const form = new FormData();
|
|
34307
|
+
form.append("file", file2, fileName);
|
|
34308
|
+
for (const [name, value] of Object.entries(fields)) if (value !== void 0) form.append(name, value);
|
|
34309
|
+
const { media: media2 } = await agentFetch("/v1/engine/media", { method: "POST", body: form });
|
|
34310
|
+
return media2;
|
|
34311
|
+
}
|
|
34072
34312
|
async function uploadOne(filePath, displayName, kind, options) {
|
|
34073
34313
|
const absPath = import_path6.default.resolve(filePath);
|
|
34074
34314
|
if (!import_fs7.default.existsSync(absPath)) {
|
|
@@ -34084,16 +34324,11 @@ async function uploadOne(filePath, displayName, kind, options) {
|
|
|
34084
34324
|
const fileName = import_path6.default.basename(absPath);
|
|
34085
34325
|
const ext = import_path6.default.extname(absPath).slice(1).toLowerCase();
|
|
34086
34326
|
const contentType = mediaInfo(ext)?.contentType ?? "application/octet-stream";
|
|
34087
|
-
|
|
34088
|
-
|
|
34089
|
-
|
|
34090
|
-
|
|
34091
|
-
form.append("display_name", displayName);
|
|
34092
|
-
const { media: media2 } = await agentFetch("/v1/engine/media", {
|
|
34093
|
-
method: "POST",
|
|
34094
|
-
body: form
|
|
34327
|
+
return uploadMedia(new Blob([import_fs7.default.readFileSync(absPath)], { type: contentType }), fileName, {
|
|
34328
|
+
kind,
|
|
34329
|
+
display_name: displayName,
|
|
34330
|
+
session_id: options.sessionId || void 0
|
|
34095
34331
|
});
|
|
34096
|
-
return media2;
|
|
34097
34332
|
}
|
|
34098
34333
|
async function mediaUploadCommand(filePaths, options) {
|
|
34099
34334
|
if (filePaths.length === 0) {
|
|
@@ -34122,8 +34357,8 @@ async function mediaUploadCommand(filePaths, options) {
|
|
|
34122
34357
|
const ext = import_path6.default.extname(filePath).slice(1).toLowerCase();
|
|
34123
34358
|
return resolveKind(ext, options.kind);
|
|
34124
34359
|
});
|
|
34125
|
-
if (
|
|
34126
|
-
throw new Error(
|
|
34360
|
+
if (kinds.includes(MEDIA_KIND.HTML)) {
|
|
34361
|
+
throw new Error('Publish HTML pages with `replicas visualize <page.html> --title "<title>"`. It shows them inline in chat, styled like the chat, and prints a Forge link for everywhere else.');
|
|
34127
34362
|
}
|
|
34128
34363
|
const shared = kinds.map((kind) => publicAccess || kind === MEDIA_KIND.IMAGE);
|
|
34129
34364
|
const results = await Promise.allSettled(
|
|
@@ -34160,9 +34395,8 @@ async function mediaUploadCommand(filePaths, options) {
|
|
|
34160
34395
|
name: names[i],
|
|
34161
34396
|
media: media2,
|
|
34162
34397
|
public_url: publicUrl,
|
|
34163
|
-
embed:
|
|
34164
|
-
|
|
34165
|
-
organization_url: media2.kind === MEDIA_KIND.HTML || options.access === "organization" && !shared[i] ? getOrganizationMediaUrl(media2.organization_id, media2.id) : void 0,
|
|
34398
|
+
embed: !shared[i] || publicUrl ? formatMediaEmbed(names[i], media2, publicUrl) : void 0,
|
|
34399
|
+
organization_url: options.access === "organization" && !shared[i] ? getOrganizationMediaUrl(media2.organization_id, media2.id) : void 0,
|
|
34166
34400
|
dashboard_url: config3?.workspace_id ? getWorkspaceDashboardUrl(config3.workspace_id, { mode: "media", mediaId: media2.id }) : void 0
|
|
34167
34401
|
});
|
|
34168
34402
|
} else {
|
|
@@ -34302,6 +34536,330 @@ async function mediaGifCommand(input, options) {
|
|
|
34302
34536
|
);
|
|
34303
34537
|
}
|
|
34304
34538
|
|
|
34539
|
+
// src/commands/visualize.ts
|
|
34540
|
+
var import_fs10 = __toESM(require("fs"));
|
|
34541
|
+
var import_http3 = __toESM(require("http"));
|
|
34542
|
+
var import_path9 = __toESM(require("path"));
|
|
34543
|
+
|
|
34544
|
+
// src/lib/headless-chrome.ts
|
|
34545
|
+
var import_child_process5 = require("child_process");
|
|
34546
|
+
var import_fs9 = __toESM(require("fs"));
|
|
34547
|
+
var import_os4 = __toESM(require("os"));
|
|
34548
|
+
var import_path8 = __toESM(require("path"));
|
|
34549
|
+
var import_stream = require("stream");
|
|
34550
|
+
var CHROME_NAMES = ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"];
|
|
34551
|
+
var COMMAND_TIMEOUT_MS = 15e3;
|
|
34552
|
+
var CLOSE_TIMEOUT_MS = 2e3;
|
|
34553
|
+
function findChrome() {
|
|
34554
|
+
const dirs = (process.env.PATH ?? "").split(import_path8.default.delimiter).filter(Boolean);
|
|
34555
|
+
return dirs.flatMap((dir) => CHROME_NAMES.map((name) => import_path8.default.join(dir, name))).find((file2) => import_fs9.default.existsSync(file2)) ?? null;
|
|
34556
|
+
}
|
|
34557
|
+
function launchChrome() {
|
|
34558
|
+
const executable = findChrome();
|
|
34559
|
+
if (!executable) throw new Error(`Chrome is not installed (looked for ${CHROME_NAMES.join(", ")} on PATH)`);
|
|
34560
|
+
const userDataDir = import_fs9.default.mkdtempSync(import_path8.default.join(import_os4.default.tmpdir(), "replicas-chrome-"));
|
|
34561
|
+
const child = (0, import_child_process5.spawn)(executable, [
|
|
34562
|
+
"--headless=new",
|
|
34563
|
+
"--remote-debugging-pipe",
|
|
34564
|
+
"--no-first-run",
|
|
34565
|
+
"--no-default-browser-check",
|
|
34566
|
+
"--disable-gpu",
|
|
34567
|
+
"--disable-extensions",
|
|
34568
|
+
"--disable-dev-shm-usage",
|
|
34569
|
+
"--hide-scrollbars",
|
|
34570
|
+
"--mute-audio",
|
|
34571
|
+
`--user-data-dir=${userDataDir}`,
|
|
34572
|
+
...process.getuid?.() === 0 ? ["--no-sandbox"] : [],
|
|
34573
|
+
"about:blank"
|
|
34574
|
+
], { stdio: ["ignore", "ignore", "pipe", "pipe", "pipe"] });
|
|
34575
|
+
const [, , , input, outputStream] = child.stdio;
|
|
34576
|
+
if (!(input instanceof import_stream.Writable) || !(outputStream instanceof import_stream.Readable)) throw new Error("Chrome did not open its DevTools pipes");
|
|
34577
|
+
let stderr = "";
|
|
34578
|
+
child.stderr?.on("data", (chunk) => {
|
|
34579
|
+
stderr = (stderr + chunk.toString()).slice(-2e3);
|
|
34580
|
+
});
|
|
34581
|
+
let nextId = 1;
|
|
34582
|
+
let exitError = null;
|
|
34583
|
+
const pending = /* @__PURE__ */ new Map();
|
|
34584
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
34585
|
+
const failAll = (error51) => {
|
|
34586
|
+
exitError = error51;
|
|
34587
|
+
for (const request of pending.values()) {
|
|
34588
|
+
clearTimeout(request.timer);
|
|
34589
|
+
request.reject(error51);
|
|
34590
|
+
}
|
|
34591
|
+
pending.clear();
|
|
34592
|
+
};
|
|
34593
|
+
child.on("error", (error51) => failAll(error51));
|
|
34594
|
+
input.on("error", (error51) => failAll(error51));
|
|
34595
|
+
child.on("exit", (code, signal) => failAll(new Error(`Chrome exited (${signal ?? code})${stderr.trim() ? `: ${stderr.trim().split("\n").pop()}` : ""}`)));
|
|
34596
|
+
let buffer = "";
|
|
34597
|
+
outputStream.setEncoding("utf8");
|
|
34598
|
+
outputStream.on("data", (chunk) => {
|
|
34599
|
+
buffer += chunk;
|
|
34600
|
+
let end;
|
|
34601
|
+
while ((end = buffer.indexOf("\0")) !== -1) {
|
|
34602
|
+
const message2 = JSON.parse(buffer.slice(0, end));
|
|
34603
|
+
buffer = buffer.slice(end + 1);
|
|
34604
|
+
if (!isRecord(message2)) continue;
|
|
34605
|
+
if (typeof message2.id === "number") {
|
|
34606
|
+
const request = pending.get(message2.id);
|
|
34607
|
+
if (!request) continue;
|
|
34608
|
+
pending.delete(message2.id);
|
|
34609
|
+
clearTimeout(request.timer);
|
|
34610
|
+
if (isRecord(message2.error)) request.reject(new Error(String(message2.error.message)));
|
|
34611
|
+
else request.resolve(isRecord(message2.result) ? message2.result : {});
|
|
34612
|
+
} else if (typeof message2.method === "string") {
|
|
34613
|
+
const sessionId = typeof message2.sessionId === "string" ? message2.sessionId : "";
|
|
34614
|
+
for (const handler of handlers.get(`${sessionId}:${message2.method}`) ?? []) handler.handle(isRecord(message2.params) ? message2.params : {});
|
|
34615
|
+
}
|
|
34616
|
+
}
|
|
34617
|
+
});
|
|
34618
|
+
const send = (method, params = {}, sessionId) => new Promise((resolve3, reject) => {
|
|
34619
|
+
if (exitError) return reject(exitError);
|
|
34620
|
+
const id = nextId++;
|
|
34621
|
+
const timer = setTimeout(() => {
|
|
34622
|
+
pending.delete(id);
|
|
34623
|
+
reject(new Error(`Chrome did not answer ${method} within ${COMMAND_TIMEOUT_MS / 1e3}s`));
|
|
34624
|
+
}, COMMAND_TIMEOUT_MS);
|
|
34625
|
+
pending.set(id, { resolve: resolve3, reject, timer });
|
|
34626
|
+
input.write(`${JSON.stringify({ id, method, params, ...sessionId ? { sessionId } : {} })}\0`);
|
|
34627
|
+
});
|
|
34628
|
+
return {
|
|
34629
|
+
async newPage() {
|
|
34630
|
+
const { targetId } = await send("Target.createTarget", { url: "about:blank" });
|
|
34631
|
+
const { sessionId } = await send("Target.attachToTarget", { targetId, flatten: true });
|
|
34632
|
+
const page = {
|
|
34633
|
+
send: (method, params) => send(method, params, sessionId),
|
|
34634
|
+
on: (method, handler) => {
|
|
34635
|
+
const key = `${sessionId}:${method}`;
|
|
34636
|
+
const entry = { handle: handler };
|
|
34637
|
+
handlers.set(key, [...handlers.get(key) ?? [], entry]);
|
|
34638
|
+
return () => handlers.set(key, (handlers.get(key) ?? []).filter((existing) => existing !== entry));
|
|
34639
|
+
},
|
|
34640
|
+
async evaluate(expression) {
|
|
34641
|
+
const { result, exceptionDetails } = await page.send("Runtime.evaluate", { expression, awaitPromise: true, returnByValue: true });
|
|
34642
|
+
if (exceptionDetails) throw new Error(exceptionDetails.text);
|
|
34643
|
+
return result.value;
|
|
34644
|
+
}
|
|
34645
|
+
};
|
|
34646
|
+
await Promise.all(["Page.enable", "Runtime.enable", "Log.enable"].map((method) => page.send(method)));
|
|
34647
|
+
return page;
|
|
34648
|
+
},
|
|
34649
|
+
// Callers exit the process right after, and Chrome's helper processes write into the profile until Chrome exits.
|
|
34650
|
+
async close() {
|
|
34651
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
34652
|
+
const exited = new Promise((resolve3) => child.once("exit", resolve3));
|
|
34653
|
+
const kill = setTimeout(() => child.kill("SIGKILL"), CLOSE_TIMEOUT_MS);
|
|
34654
|
+
send("Browser.close").catch(() => child.kill("SIGKILL"));
|
|
34655
|
+
await exited;
|
|
34656
|
+
clearTimeout(kill);
|
|
34657
|
+
}
|
|
34658
|
+
import_fs9.default.rmSync(userDataDir, { recursive: true, force: true });
|
|
34659
|
+
}
|
|
34660
|
+
};
|
|
34661
|
+
}
|
|
34662
|
+
|
|
34663
|
+
// src/commands/visualize.ts
|
|
34664
|
+
var MIB = 1024 * 1024;
|
|
34665
|
+
var MAX_IMAGE_BYTES = 10 * MIB;
|
|
34666
|
+
var MAX_PAGE_BYTES = 25 * MIB;
|
|
34667
|
+
var LOAD_TIMEOUT_MS = 1e4;
|
|
34668
|
+
var SCREENSHOT_DIR = "/tmp/replicas-visualize";
|
|
34669
|
+
var LOCAL_IMAGE = /(["'`])(\/(?!\/)[^"'`\r\n]*?\.(?:png|jpe?g|gif|webp|avif|svg))\1|url\(\s*(\/(?!\/)[^\s"'`()]*?\.(?:png|jpe?g|gif|webp|avif|svg))\s*\)/gi;
|
|
34670
|
+
function inlineLocalImages(html) {
|
|
34671
|
+
const dataUris = /* @__PURE__ */ new Map();
|
|
34672
|
+
const dataUri = (file2) => {
|
|
34673
|
+
if (!dataUris.has(file2)) {
|
|
34674
|
+
const stat = import_fs10.default.statSync(file2, { throwIfNoEntry: false });
|
|
34675
|
+
if (stat && stat.size > MAX_IMAGE_BYTES) throw new Error(`${file2} is ${(stat.size / MIB).toFixed(1)} MiB; each image must be at most ${MAX_IMAGE_BYTES / MIB} MiB.`);
|
|
34676
|
+
dataUris.set(file2, stat?.isFile() ? `data:${classifyCanvasFilename(file2).mimeType};base64,${import_fs10.default.readFileSync(file2).toString("base64")}` : null);
|
|
34677
|
+
}
|
|
34678
|
+
return dataUris.get(file2) ?? null;
|
|
34679
|
+
};
|
|
34680
|
+
const inlined = html.replace(LOCAL_IMAGE, (match, quote, quoted, bare) => {
|
|
34681
|
+
const uri = dataUri(quoted ?? bare ?? "");
|
|
34682
|
+
if (!uri) return match;
|
|
34683
|
+
return quoted ? `${quote}${uri}${quote}` : `url(${uri})`;
|
|
34684
|
+
});
|
|
34685
|
+
if (Buffer.byteLength(inlined) > MAX_PAGE_BYTES) throw new Error(`With its images inlined the page is over ${MAX_PAGE_BYTES / MIB} MiB. Use smaller images.`);
|
|
34686
|
+
return { html: inlined, missing: [...dataUris].filter(([, uri]) => uri === null).map(([file2]) => file2) };
|
|
34687
|
+
}
|
|
34688
|
+
var webAppUrl = () => process.env.REPLICAS_WEB_URL || DEFAULT_WEB_APP_URL;
|
|
34689
|
+
async function servePage(html) {
|
|
34690
|
+
const font = await fetch(new URL(VISUAL_FONT_PATH, webAppUrl()), { signal: AbortSignal.timeout(5e3) }).then((response) => response.ok ? response.arrayBuffer() : null).catch(() => null);
|
|
34691
|
+
const server = import_http3.default.createServer((request, response) => {
|
|
34692
|
+
if (request.url === VISUAL_FONT_PATH && font) {
|
|
34693
|
+
response.writeHead(200, { "Content-Type": "font/woff2", "Access-Control-Allow-Origin": "*" });
|
|
34694
|
+
response.end(Buffer.from(font));
|
|
34695
|
+
return;
|
|
34696
|
+
}
|
|
34697
|
+
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Content-Security-Policy": "sandbox allow-scripts" });
|
|
34698
|
+
response.end(html);
|
|
34699
|
+
});
|
|
34700
|
+
await new Promise((resolve3) => server.listen(0, "127.0.0.1", resolve3));
|
|
34701
|
+
const address = server.address();
|
|
34702
|
+
const origin = `http://127.0.0.1:${typeof address === "object" && address ? address.port : 0}`;
|
|
34703
|
+
return { server, pageUrl: `${origin}/page.html`, fontUrl: font ? `${origin}${VISUAL_FONT_PATH}` : void 0 };
|
|
34704
|
+
}
|
|
34705
|
+
function waitFor(page, method, timeoutMs) {
|
|
34706
|
+
return new Promise((resolve3) => {
|
|
34707
|
+
const stop = page.on(method, () => {
|
|
34708
|
+
clearTimeout(timer);
|
|
34709
|
+
stop();
|
|
34710
|
+
resolve3(true);
|
|
34711
|
+
});
|
|
34712
|
+
const timer = setTimeout(() => {
|
|
34713
|
+
stop();
|
|
34714
|
+
resolve3(false);
|
|
34715
|
+
}, timeoutMs);
|
|
34716
|
+
});
|
|
34717
|
+
}
|
|
34718
|
+
async function settledHeight(page) {
|
|
34719
|
+
let last = -1;
|
|
34720
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
34721
|
+
const height = Number(await page.evaluate(
|
|
34722
|
+
`new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve(${VISUAL_CONTENT_HEIGHT_EXPRESSION}))))`
|
|
34723
|
+
));
|
|
34724
|
+
if (height === last) return height;
|
|
34725
|
+
last = height;
|
|
34726
|
+
}
|
|
34727
|
+
return last;
|
|
34728
|
+
}
|
|
34729
|
+
async function inspect(html, source) {
|
|
34730
|
+
const chrome = launchChrome();
|
|
34731
|
+
let server;
|
|
34732
|
+
try {
|
|
34733
|
+
const served = await servePage(html);
|
|
34734
|
+
server = served.server;
|
|
34735
|
+
const { pageUrl, fontUrl } = served;
|
|
34736
|
+
const page = await chrome.newPage();
|
|
34737
|
+
const messages = [];
|
|
34738
|
+
page.on("Runtime.consoleAPICalled", ({ type, args }) => {
|
|
34739
|
+
const text3 = args.map((arg) => arg.value === void 0 ? arg.description ?? "" : typeof arg.value === "string" ? arg.value : JSON.stringify(arg.value)).join(" ");
|
|
34740
|
+
messages.push({ level: type === "error" || type === "assert" ? "error" : type === "warning" ? "warning" : "log", text: text3 });
|
|
34741
|
+
});
|
|
34742
|
+
page.on("Runtime.exceptionThrown", ({ exceptionDetails }) => {
|
|
34743
|
+
messages.push({ level: "error", text: exceptionDetails.exception?.description ?? exceptionDetails.text });
|
|
34744
|
+
});
|
|
34745
|
+
page.on("Log.entryAdded", ({ entry }) => {
|
|
34746
|
+
if (entry.level === "error") messages.push({ level: "error", text: `${entry.text}${entry.url ? ` (${entry.url})` : ""}` });
|
|
34747
|
+
});
|
|
34748
|
+
let loads = 0;
|
|
34749
|
+
const load = async (appearance, width, height) => {
|
|
34750
|
+
const { r, g, b } = hexToRgb(DESIGN_COLORS[appearance].background);
|
|
34751
|
+
await page.send("Emulation.setDeviceMetricsOverride", { width, height, deviceScaleFactor: 1, mobile: false });
|
|
34752
|
+
await page.send("Emulation.setEmulatedMedia", { features: [{ name: "prefers-color-scheme", value: appearance }] });
|
|
34753
|
+
await page.send("Emulation.setDefaultBackgroundColorOverride", { color: { r, g, b, a: 1 } });
|
|
34754
|
+
const loaded = waitFor(page, "Page.loadEventFired", LOAD_TIMEOUT_MS);
|
|
34755
|
+
await page.send("Page.navigate", { url: `${pageUrl}?load=${++loads}${visualThemeFragment(visualTheme(appearance, fontUrl))}` });
|
|
34756
|
+
if (!await loaded) throw new Error(`The page did not finish loading within ${LOAD_TIMEOUT_MS / 1e3}s. Check for scripts, images, or fonts that never finish downloading.`);
|
|
34757
|
+
await page.evaluate("document.fonts.ready.then(() => true)");
|
|
34758
|
+
};
|
|
34759
|
+
const resize = (width, height) => page.send("Emulation.setDeviceMetricsOverride", { width, height, deviceScaleFactor: 1, mobile: false });
|
|
34760
|
+
const screenshot = async (appearance, width) => {
|
|
34761
|
+
await load(appearance, width, 600);
|
|
34762
|
+
await resize(width, clampVisualHeight(await settledHeight(page)));
|
|
34763
|
+
const { data } = await page.send("Page.captureScreenshot", { format: "png" });
|
|
34764
|
+
import_fs10.default.mkdirSync(SCREENSHOT_DIR, { recursive: true });
|
|
34765
|
+
const file2 = import_path9.default.join(SCREENSHOT_DIR, `${import_path9.default.basename(source, import_path9.default.extname(source))}-${appearance}-${width}.png`);
|
|
34766
|
+
import_fs10.default.writeFileSync(file2, Buffer.from(data, "base64"));
|
|
34767
|
+
return file2;
|
|
34768
|
+
};
|
|
34769
|
+
await load("dark", VISUAL_COLUMN_WIDTH, 600);
|
|
34770
|
+
const heights = [];
|
|
34771
|
+
const overflowWidths = [];
|
|
34772
|
+
for (const width of VISUAL_MEASURE_WIDTHS) {
|
|
34773
|
+
await resize(width, 600);
|
|
34774
|
+
heights.push([width, await settledHeight(page)]);
|
|
34775
|
+
if (await page.evaluate("document.documentElement.scrollWidth > document.documentElement.clientWidth + 1") === true) overflowWidths.push(width);
|
|
34776
|
+
}
|
|
34777
|
+
await resize(VISUAL_COLUMN_WIDTH, 300);
|
|
34778
|
+
const short = await settledHeight(page);
|
|
34779
|
+
await resize(VISUAL_COLUMN_WIDTH, 1500);
|
|
34780
|
+
const tall = await settledHeight(page);
|
|
34781
|
+
const backgroundColor = await page.evaluate(
|
|
34782
|
+
"[document.documentElement, document.body].map((element) => element && getComputedStyle(element).backgroundColor).find((color) => color && !/^(transparent|rgba\\(.*, 0\\))$/.test(color)) ?? null"
|
|
34783
|
+
);
|
|
34784
|
+
const background = typeof backgroundColor === "string" ? backgroundColor : null;
|
|
34785
|
+
const screenshots = [await screenshot("dark", VISUAL_COLUMN_WIDTH), await screenshot("light", VISUAL_PHONE_WIDTH)];
|
|
34786
|
+
const traced = messages.map((message2) => ({ ...message2, text: message2.text.replaceAll(new RegExp(`${pageUrl}\\?load=\\d+`, "g"), source) }));
|
|
34787
|
+
const consoleMessages = traced.filter((message2, index) => traced.findIndex((other) => other.text === message2.text) === index);
|
|
34788
|
+
return { heights, viewportDependent: Math.abs(short - tall) > 1, overflowWidths, background, consoleMessages, screenshots };
|
|
34789
|
+
} finally {
|
|
34790
|
+
server?.close();
|
|
34791
|
+
await chrome.close();
|
|
34792
|
+
}
|
|
34793
|
+
}
|
|
34794
|
+
function hexToRgb(hex3) {
|
|
34795
|
+
const value = parseInt(hex3.slice(1), 16);
|
|
34796
|
+
return { r: value >> 16, g: value >> 8 & 255, b: value & 255 };
|
|
34797
|
+
}
|
|
34798
|
+
var VIEWPORT_DEPENDENT_FIX = "Its height follows the frame (100vh, height:100%, or min-height:100vh on html, body, or the outer element), so in chat it would grow without end. Let content set the height.";
|
|
34799
|
+
function warnings(inspection) {
|
|
34800
|
+
return [
|
|
34801
|
+
...inspection.viewportDependent ? [VIEWPORT_DEPENDENT_FIX] : [],
|
|
34802
|
+
...inspection.overflowWidths.length > 0 ? [`Content overflows horizontally at ${inspection.overflowWidths.join("px, ")}px. Let it wrap or stack at narrow widths.`] : [],
|
|
34803
|
+
...inspection.heights.some(([, height]) => height > VISUAL_MAX_HEIGHT) ? [`The page is taller than ${VISUAL_MAX_HEIGHT}px at some widths, so chat cuts off the bottom. Make it shorter or split it into several visuals.`] : [],
|
|
34804
|
+
...inspection.background ? [`The page paints its own background (${inspection.background}) on html or body. Leave them transparent so it sits on the chat background.`] : []
|
|
34805
|
+
];
|
|
34806
|
+
}
|
|
34807
|
+
var consoleErrors = (inspection) => inspection.consoleMessages.filter((message2) => message2.level === "error").map((message2) => `Console error: ${message2.text}`);
|
|
34808
|
+
async function visualizeCommand(file2, options) {
|
|
34809
|
+
const source = import_path9.default.resolve(file2);
|
|
34810
|
+
if (!import_fs10.default.statSync(source, { throwIfNoEntry: false })?.isFile()) throw new Error(`File not found: ${source}`);
|
|
34811
|
+
const { html: inlined, missing } = inlineLocalImages(import_fs10.default.readFileSync(source, "utf8"));
|
|
34812
|
+
const html = injectVisualBootstrap(inlined);
|
|
34813
|
+
const missingImages = missing.length > 0 ? [`These local images could not be read, so they won't show: ${missing.join(", ")}. Use absolute paths to existing image files.`] : [];
|
|
34814
|
+
if (options.check) {
|
|
34815
|
+
const inspection2 = await inspect(html, source);
|
|
34816
|
+
output({ file: source, ...inspection2, warnings: [...missingImages, ...warnings(inspection2)] }, (result) => {
|
|
34817
|
+
console.log(`Checked ${source} (dark at ${VISUAL_COLUMN_WIDTH}px, light at ${VISUAL_PHONE_WIDTH}px).
|
|
34818
|
+
`);
|
|
34819
|
+
console.log("Screenshots (open both and look before publishing):");
|
|
34820
|
+
for (const screenshot of result.screenshots) console.log(` ${screenshot}`);
|
|
34821
|
+
console.log(`
|
|
34822
|
+
Height: ${result.heights.filter(([width]) => width === VISUAL_COLUMN_WIDTH || width === VISUAL_MEASURE_WIDTHS[0]).map(([width, height]) => `${height}px at ${width}px`).join(", ")}`);
|
|
34823
|
+
if (result.consoleMessages.length > 0) {
|
|
34824
|
+
console.log("\nConsole:");
|
|
34825
|
+
for (const message2 of result.consoleMessages) console.log(` ${message2.level.padEnd(7)} ${message2.text}`);
|
|
34826
|
+
}
|
|
34827
|
+
const errors = result.consoleMessages.some((message2) => message2.level === "error") ? ["The console errors above."] : [];
|
|
34828
|
+
if (result.warnings.length + errors.length > 0) {
|
|
34829
|
+
console.log("\nFix before publishing:");
|
|
34830
|
+
for (const warning of [...result.warnings, ...errors]) console.log(` - ${warning}`);
|
|
34831
|
+
}
|
|
34832
|
+
console.log(`
|
|
34833
|
+
Next: publish with \`replicas visualize ${file2} --title "<short title>"\`.`);
|
|
34834
|
+
});
|
|
34835
|
+
return;
|
|
34836
|
+
}
|
|
34837
|
+
const title2 = options.title?.trim();
|
|
34838
|
+
if (!title2) throw new Error('Pass --title "<short title>" to publish, or --check to preview without publishing.');
|
|
34839
|
+
const inspection = await inspect(html, source);
|
|
34840
|
+
if (inspection.viewportDependent) throw new Error(`Not published. ${VIEWPORT_DEPENDENT_FIX}`);
|
|
34841
|
+
const media2 = await uploadMedia(new Blob([html], { type: "text/html; charset=utf-8" }), "visual.html", {
|
|
34842
|
+
kind: "html",
|
|
34843
|
+
display_name: title2,
|
|
34844
|
+
visual_heights: JSON.stringify(inspection.heights.map(([width, height]) => [width, clampVisualHeight(height)]))
|
|
34845
|
+
});
|
|
34846
|
+
const embed = `![${title2.replace(/[[\]]/g, "")}](${MONOLITH_URL}/v1/media/${media2.id})`;
|
|
34847
|
+
const forgeUrl = getOrganizationMediaUrl(media2.organization_id, media2.id);
|
|
34848
|
+
const notes = [...missingImages, ...warnings(inspection), ...consoleErrors(inspection)];
|
|
34849
|
+
const { screenshots } = inspection;
|
|
34850
|
+
output({ media: media2, embed, forgeUrl, screenshots, warnings: notes }, () => {
|
|
34851
|
+
console.log(embed);
|
|
34852
|
+
console.log(`Forge link (organization only): [View ${title2}](${forgeUrl})`);
|
|
34853
|
+
console.log("\nScreenshots (open both; if something looks wrong, fix the page and publish again):");
|
|
34854
|
+
for (const screenshot of screenshots) console.log(` ${screenshot}`);
|
|
34855
|
+
if (notes.length > 0) {
|
|
34856
|
+
console.log("\nPublished, but fix these and publish again:");
|
|
34857
|
+
for (const note of notes) console.log(` - ${note}`);
|
|
34858
|
+
}
|
|
34859
|
+
console.log("\nNext: paste the  line on its own line where the visual belongs in your final reply. The user sees the visual itself, so don't introduce or restate it; add only what it doesn't say. The Forge link is for posting outside Replicas chat (PRs, Slack, Linear); leave it out of your chat reply.");
|
|
34860
|
+
});
|
|
34861
|
+
}
|
|
34862
|
+
|
|
34305
34863
|
// src/commands/gpu.ts
|
|
34306
34864
|
var import_chalk28 = __toESM(require("chalk"));
|
|
34307
34865
|
var import_node_child_process2 = require("child_process");
|
|
@@ -34449,21 +35007,21 @@ function assertTransferSize(bytes, localPath) {
|
|
|
34449
35007
|
}
|
|
34450
35008
|
}
|
|
34451
35009
|
function archive(localPath) {
|
|
34452
|
-
const
|
|
34453
|
-
if (!(0, import_node_fs2.existsSync)(
|
|
34454
|
-
if (!(0, import_node_fs2.statSync)(
|
|
34455
|
-
assertTransferSize((0, import_node_fs2.statSync)(
|
|
34456
|
-
return (0, import_node_child_process2.spawn)("tar", ["-czf", "-", "-C", (0, import_node_path2.dirname)(
|
|
35010
|
+
const path10 = (0, import_node_path2.resolve)(localPath);
|
|
35011
|
+
if (!(0, import_node_fs2.existsSync)(path10)) throw new Error(`${path10} does not exist`);
|
|
35012
|
+
if (!(0, import_node_fs2.statSync)(path10).isDirectory()) {
|
|
35013
|
+
assertTransferSize((0, import_node_fs2.statSync)(path10).size, localPath);
|
|
35014
|
+
return (0, import_node_child_process2.spawn)("tar", ["-czf", "-", "-C", (0, import_node_path2.dirname)(path10), (0, import_node_path2.basename)(path10)], { stdio: ["ignore", "pipe", "pipe"] });
|
|
34457
35015
|
}
|
|
34458
|
-
const listed = (0, import_node_child_process2.spawnSync)("git", ["-C",
|
|
35016
|
+
const listed = (0, import_node_child_process2.spawnSync)("git", ["-C", path10, "ls-files", "-co", "--exclude-standard", "-z"], { maxBuffer: 256 * 1024 * 1024 });
|
|
34459
35017
|
if (listed.status !== 0) {
|
|
34460
|
-
const du = (0, import_node_child_process2.spawnSync)("du", ["-sk", "--exclude=.git", "--exclude=node_modules",
|
|
35018
|
+
const du = (0, import_node_child_process2.spawnSync)("du", ["-sk", "--exclude=.git", "--exclude=node_modules", path10]);
|
|
34461
35019
|
assertTransferSize(Number(du.stdout.toString().split(" ")[0]) * 1024, localPath);
|
|
34462
|
-
return (0, import_node_child_process2.spawn)("tar", ["-czf", "-", "-C",
|
|
35020
|
+
return (0, import_node_child_process2.spawn)("tar", ["-czf", "-", "-C", path10, "--exclude=.git", "--exclude=node_modules", "."], { stdio: ["ignore", "pipe", "pipe"] });
|
|
34463
35021
|
}
|
|
34464
|
-
const files = listed.stdout.toString().split("\0").filter((file2) => file2 && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(
|
|
34465
|
-
assertTransferSize(files.reduce((total, file2) => total + (0, import_node_fs2.statSync)((0, import_node_path2.join)(
|
|
34466
|
-
const child = (0, import_node_child_process2.spawn)("tar", ["-czf", "-", "-C",
|
|
35022
|
+
const files = listed.stdout.toString().split("\0").filter((file2) => file2 && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(path10, file2)));
|
|
35023
|
+
assertTransferSize(files.reduce((total, file2) => total + (0, import_node_fs2.statSync)((0, import_node_path2.join)(path10, file2)).size, 0), localPath);
|
|
35024
|
+
const child = (0, import_node_child_process2.spawn)("tar", ["-czf", "-", "-C", path10, "--null", "-T", "-"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
34467
35025
|
child.stdin.end(files.map((file2) => `${file2}\0`).join(""));
|
|
34468
35026
|
return child;
|
|
34469
35027
|
}
|
|
@@ -35089,12 +35647,17 @@ if (isAgentMode()) {
|
|
|
35089
35647
|
service.command("list").description("List registered services and whether they are running").action(run(serviceListCommand));
|
|
35090
35648
|
service.command("logs <name>").description("Print the last lines of a service log").option("-n, --lines <n>", "Number of lines to print (default 50)").action(run(serviceLogsCommand));
|
|
35091
35649
|
media.command("gif <recording>").description("Turn a screen recording into a short looping GIF under GitHub's 5 MiB image limit, lowering fps and width until it fits").option("-o, --output <path>", "Output .gif path (default: the recording path with .gif)").option("--start <seconds>", "Where the GIF starts in the recording (default 0)").option("--duration <seconds>", "Length in seconds, 1-30 (default 10)").option("--width <px>", "Maximum width, 200-1280 (default 960)").option("--fps <n>", "Maximum frame rate, 5-20 (default 12)").option("--crop <w:h:x:y>", "Crop to the region that changes, in recording pixels, before scaling").action(run(mediaGifCommand));
|
|
35092
|
-
media.command("upload <files...>").description("Upload one or more image, video,
|
|
35650
|
+
media.command("upload <files...>").description("Upload one or more image, video, or audio files. Multiple files are uploaded concurrently. Publish HTML pages with `replicas visualize`.").option("-k, --kind <kind>", "Media kind: image, video, or audio (inferred from extension if omitted)").option("-s, --session-id <id>", "Session ID to associate the asset with").option(
|
|
35093
35651
|
"-n, --name <name>",
|
|
35094
35652
|
"Required human-facing display name shown in the dashboard. Pass one --name per file, in order.",
|
|
35095
35653
|
(value, previous) => [...previous, value],
|
|
35096
35654
|
[]
|
|
35097
|
-
).option("--access <access>", "Link access for video
|
|
35655
|
+
).option("--access <access>", "Link access for video and audio: organization (sign-in required) or public. Images always get a public link").option("--share", "Alias for --access public").action(run(mediaUploadCommand));
|
|
35656
|
+
program.command("visualize <file>").description("Show a self-contained HTML page (chart, diagram, comparison, mockup) inline in your Replicas chat reply, styled like the chat").option("-t, --title <title>", "Short name for the visual, used as its label (required to publish)").option("--check", "Render without publishing: screenshots in dark and light, heights, console output, and layout warnings").addHelpText("after", `
|
|
35657
|
+
Publishing checks the page too and prints screenshots: look at them, and if something is wrong, fix the page and publish again. Paste the printed  line where the visual belongs in your reply.
|
|
35658
|
+
|
|
35659
|
+
Page rules:
|
|
35660
|
+
${VISUAL_PAGE_RULES}`).action(run(visualizeCommand));
|
|
35098
35661
|
media.command("share <mediaId>").description("Rotate an image's public forge URL, or create one for video or audio media").action(run(mediaShareCommand));
|
|
35099
35662
|
media.command("revoke <mediaId>").description("Revoke the public forge URL for uploaded media").action(run(mediaRevokeCommand));
|
|
35100
35663
|
media.command("delete <mediaId>").description("Permanently delete media uploaded by this workspace").action(run(mediaDeleteCommand));
|
|
@@ -35120,6 +35683,7 @@ if (isAgentMode()) {
|
|
|
35120
35683
|
"preview",
|
|
35121
35684
|
"service",
|
|
35122
35685
|
"media",
|
|
35686
|
+
"visualize",
|
|
35123
35687
|
"gpu",
|
|
35124
35688
|
"history",
|
|
35125
35689
|
"skills",
|