cruo-agent 0.1.2 → 0.1.4
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/VERSION +1 -1
- package/dist/cli.js +701 -167
- package/dist/index.js +14 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -45,64 +45,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
45
45
|
));
|
|
46
46
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
47
47
|
|
|
48
|
-
// src/options.ts
|
|
49
|
-
function stripTokenFlag(argv2) {
|
|
50
|
-
const at = argv2.indexOf("--token");
|
|
51
|
-
if (at < 0) return [...argv2];
|
|
52
|
-
return [...argv2.slice(0, at), ...argv2.slice(at + 2)];
|
|
53
|
-
}
|
|
54
|
-
function numericIn(argv2, name, spec = {}) {
|
|
55
|
-
const i = argv2.indexOf(`--${name}`);
|
|
56
|
-
if (i < 0) return spec.fallback;
|
|
57
|
-
const raw = argv2[i + 1];
|
|
58
|
-
if (raw === void 0 || raw.startsWith("--")) {
|
|
59
|
-
throw new OptionError(
|
|
60
|
-
`--${name} expects a ${spec.integer ? "whole " : ""}number${spec.unit ? ` of ${spec.unit}` : ""}, but no value followed it.`
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
const value = raw.trim() === "" ? NaN : Number(raw);
|
|
64
|
-
const what = `--${name} ${JSON.stringify(raw)}`;
|
|
65
|
-
if (!Number.isFinite(value)) {
|
|
66
|
-
throw new OptionError(
|
|
67
|
-
`${what} is not a number.` + // Reached only by something like `-abc`. A plain `-5` is a finite
|
|
68
|
-
// negative and falls to the range check below, which is the accurate
|
|
69
|
-
// refusal for it — this hint is for the case where the leading dash is
|
|
70
|
-
// the reason it did not parse at all.
|
|
71
|
-
(raw.startsWith("-") ? " A leading `-` reads as a flag, not a sign." : "")
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
if (value <= 0) {
|
|
75
|
-
throw new OptionError(`${what} must be greater than zero${spec.unit ? ` ${spec.unit}` : ""}.`);
|
|
76
|
-
}
|
|
77
|
-
if (spec.integer && !Number.isInteger(value)) {
|
|
78
|
-
throw new OptionError(`${what} must be a whole number.`);
|
|
79
|
-
}
|
|
80
|
-
return value;
|
|
81
|
-
}
|
|
82
|
-
function requiredNumericIn(argv2, name, fallback, spec = {}) {
|
|
83
|
-
return numericIn(argv2, name, { ...spec, fallback });
|
|
84
|
-
}
|
|
85
|
-
function secondsIn(argv2, name, fallbackSeconds) {
|
|
86
|
-
return requiredNumericIn(argv2, name, fallbackSeconds, { unit: "seconds" }) * 1e3;
|
|
87
|
-
}
|
|
88
|
-
var OptionError, flagIn, optIn;
|
|
89
|
-
var init_options = __esm({
|
|
90
|
-
"src/options.ts"() {
|
|
91
|
-
"use strict";
|
|
92
|
-
OptionError = class extends Error {
|
|
93
|
-
constructor(message) {
|
|
94
|
-
super(message);
|
|
95
|
-
this.name = "OptionError";
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
flagIn = (argv2, name) => argv2.includes(`--${name}`);
|
|
99
|
-
optIn = (argv2, name, fallback) => {
|
|
100
|
-
const i = argv2.indexOf(`--${name}`);
|
|
101
|
-
return i >= 0 && argv2[i + 1] && !argv2[i + 1].startsWith("--") ? argv2[i + 1] : fallback;
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
|
|
106
48
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/core.js
|
|
107
49
|
// @__NO_SIDE_EFFECTS__
|
|
108
50
|
function $constructor(name, initializer3, params) {
|
|
@@ -14032,17 +13974,17 @@ var init_schemas2 = __esm({
|
|
|
14032
13974
|
inst._zod.processJSONSchema = (ctx, json2, params) => arrayProcessor(inst, ctx, json2, params);
|
|
14033
13975
|
inst.element = def.element;
|
|
14034
13976
|
_installLazyMethods(inst, "ZodArray", {
|
|
14035
|
-
min(
|
|
14036
|
-
return this.check(_minLength(
|
|
13977
|
+
min(n2, params) {
|
|
13978
|
+
return this.check(_minLength(n2, params));
|
|
14037
13979
|
},
|
|
14038
13980
|
nonempty(params) {
|
|
14039
13981
|
return this.check(_minLength(1, params));
|
|
14040
13982
|
},
|
|
14041
|
-
max(
|
|
14042
|
-
return this.check(_maxLength(
|
|
13983
|
+
max(n2, params) {
|
|
13984
|
+
return this.check(_maxLength(n2, params));
|
|
14043
13985
|
},
|
|
14044
|
-
length(
|
|
14045
|
-
return this.check(_length(
|
|
13986
|
+
length(n2, params) {
|
|
13987
|
+
return this.check(_length(n2, params));
|
|
14046
13988
|
},
|
|
14047
13989
|
unwrap() {
|
|
14048
13990
|
return this.element;
|
|
@@ -15185,7 +15127,7 @@ var init_zod = __esm({
|
|
|
15185
15127
|
});
|
|
15186
15128
|
|
|
15187
15129
|
// ../../packages/core/dist/schemas.js
|
|
15188
|
-
var stateCategory, issuePriority, memberRole, projectKey, createProjectInput, createIssueInput, updateIssueInput, moveIssueInput, workflowStateInput, listIssuesFilter;
|
|
15130
|
+
var stateCategory, issuePriority, memberRole, projectKey, createProjectInput, estimateHours, createIssueInput, updateIssueInput, moveIssueInput, workflowStateInput, listIssuesFilter;
|
|
15189
15131
|
var init_schemas3 = __esm({
|
|
15190
15132
|
"../../packages/core/dist/schemas.js"() {
|
|
15191
15133
|
"use strict";
|
|
@@ -15213,6 +15155,7 @@ var init_schemas3 = __esm({
|
|
|
15213
15155
|
description: external_exports.string().trim().max(2e3).optional(),
|
|
15214
15156
|
leadId: external_exports.string().uuid().optional()
|
|
15215
15157
|
});
|
|
15158
|
+
estimateHours = external_exports.number().positive().max(9999).multipleOf(0.01).nullish();
|
|
15216
15159
|
createIssueInput = external_exports.object({
|
|
15217
15160
|
projectId: external_exports.string().uuid(),
|
|
15218
15161
|
title: external_exports.string().trim().min(1).max(300),
|
|
@@ -15223,6 +15166,7 @@ var init_schemas3 = __esm({
|
|
|
15223
15166
|
parentId: external_exports.string().uuid().nullish(),
|
|
15224
15167
|
dueDate: external_exports.string().date().nullish(),
|
|
15225
15168
|
// YYYY-MM-DD
|
|
15169
|
+
estimateHours,
|
|
15226
15170
|
labelIds: external_exports.array(external_exports.string().uuid()).optional()
|
|
15227
15171
|
});
|
|
15228
15172
|
updateIssueInput = external_exports.object({
|
|
@@ -15230,7 +15174,8 @@ var init_schemas3 = __esm({
|
|
|
15230
15174
|
description: external_exports.string().trim().max(2e4).nullish(),
|
|
15231
15175
|
priority: issuePriority.optional(),
|
|
15232
15176
|
assigneeId: external_exports.string().uuid().nullish(),
|
|
15233
|
-
dueDate: external_exports.string().date().nullish()
|
|
15177
|
+
dueDate: external_exports.string().date().nullish(),
|
|
15178
|
+
estimateHours
|
|
15234
15179
|
});
|
|
15235
15180
|
moveIssueInput = external_exports.object({
|
|
15236
15181
|
issueId: external_exports.string().uuid(),
|
|
@@ -15288,6 +15233,13 @@ var init_identity = __esm({
|
|
|
15288
15233
|
}
|
|
15289
15234
|
});
|
|
15290
15235
|
|
|
15236
|
+
// ../../packages/core/dist/effort.js
|
|
15237
|
+
var init_effort = __esm({
|
|
15238
|
+
"../../packages/core/dist/effort.js"() {
|
|
15239
|
+
"use strict";
|
|
15240
|
+
}
|
|
15241
|
+
});
|
|
15242
|
+
|
|
15291
15243
|
// ../../packages/core/dist/plans.js
|
|
15292
15244
|
var init_plans = __esm({
|
|
15293
15245
|
"../../packages/core/dist/plans.js"() {
|
|
@@ -15339,6 +15291,7 @@ var init_dist = __esm({
|
|
|
15339
15291
|
init_schemas3();
|
|
15340
15292
|
init_rank();
|
|
15341
15293
|
init_identity();
|
|
15294
|
+
init_effort();
|
|
15342
15295
|
init_plans();
|
|
15343
15296
|
init_mcp_connect();
|
|
15344
15297
|
init_tree();
|
|
@@ -15349,8 +15302,8 @@ var init_dist = __esm({
|
|
|
15349
15302
|
|
|
15350
15303
|
// ../../packages/mcp-kit/dist/env.js
|
|
15351
15304
|
function pick2(...names) {
|
|
15352
|
-
for (const
|
|
15353
|
-
const v = process.env[
|
|
15305
|
+
for (const n2 of names) {
|
|
15306
|
+
const v = process.env[n2];
|
|
15354
15307
|
if (v && v.trim())
|
|
15355
15308
|
return v.trim();
|
|
15356
15309
|
}
|
|
@@ -15570,9 +15523,9 @@ function __generator(thisArg, body) {
|
|
|
15570
15523
|
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
|
|
15571
15524
|
return this;
|
|
15572
15525
|
}), g;
|
|
15573
|
-
function verb(
|
|
15526
|
+
function verb(n2) {
|
|
15574
15527
|
return function(v) {
|
|
15575
|
-
return step([
|
|
15528
|
+
return step([n2, v]);
|
|
15576
15529
|
};
|
|
15577
15530
|
}
|
|
15578
15531
|
function step(op) {
|
|
@@ -15645,12 +15598,12 @@ function __values(o) {
|
|
|
15645
15598
|
};
|
|
15646
15599
|
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
15647
15600
|
}
|
|
15648
|
-
function __read(o,
|
|
15601
|
+
function __read(o, n2) {
|
|
15649
15602
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
15650
15603
|
if (!m) return o;
|
|
15651
15604
|
var i = m.call(o), r, ar = [], e;
|
|
15652
15605
|
try {
|
|
15653
|
-
while ((
|
|
15606
|
+
while ((n2 === void 0 || n2-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
15654
15607
|
} catch (error51) {
|
|
15655
15608
|
e = { error: error51 };
|
|
15656
15609
|
} finally {
|
|
@@ -15697,19 +15650,19 @@ function __asyncGenerator(thisArg, _arguments, generator) {
|
|
|
15697
15650
|
return Promise.resolve(v).then(f, reject);
|
|
15698
15651
|
};
|
|
15699
15652
|
}
|
|
15700
|
-
function verb(
|
|
15701
|
-
if (g[
|
|
15702
|
-
i[
|
|
15653
|
+
function verb(n2, f) {
|
|
15654
|
+
if (g[n2]) {
|
|
15655
|
+
i[n2] = function(v) {
|
|
15703
15656
|
return new Promise(function(a, b) {
|
|
15704
|
-
q.push([
|
|
15657
|
+
q.push([n2, v, a, b]) > 1 || resume(n2, v);
|
|
15705
15658
|
});
|
|
15706
15659
|
};
|
|
15707
|
-
if (f) i[
|
|
15660
|
+
if (f) i[n2] = f(i[n2]);
|
|
15708
15661
|
}
|
|
15709
15662
|
}
|
|
15710
|
-
function resume(
|
|
15663
|
+
function resume(n2, v) {
|
|
15711
15664
|
try {
|
|
15712
|
-
step(g[
|
|
15665
|
+
step(g[n2](v));
|
|
15713
15666
|
} catch (e) {
|
|
15714
15667
|
settle(q[0][3], e);
|
|
15715
15668
|
}
|
|
@@ -15734,9 +15687,9 @@ function __asyncDelegator(o) {
|
|
|
15734
15687
|
}), verb("return"), i[Symbol.iterator] = function() {
|
|
15735
15688
|
return this;
|
|
15736
15689
|
}, i;
|
|
15737
|
-
function verb(
|
|
15738
|
-
i[
|
|
15739
|
-
return (p = !p) ? { value: __await(o[
|
|
15690
|
+
function verb(n2, f) {
|
|
15691
|
+
i[n2] = o[n2] ? function(v) {
|
|
15692
|
+
return (p = !p) ? { value: __await(o[n2](v)), done: false } : f ? f(v) : v;
|
|
15740
15693
|
} : f;
|
|
15741
15694
|
}
|
|
15742
15695
|
}
|
|
@@ -15746,10 +15699,10 @@ function __asyncValues(o) {
|
|
|
15746
15699
|
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
|
|
15747
15700
|
return this;
|
|
15748
15701
|
}, i);
|
|
15749
|
-
function verb(
|
|
15750
|
-
i[
|
|
15702
|
+
function verb(n2) {
|
|
15703
|
+
i[n2] = o[n2] && function(v) {
|
|
15751
15704
|
return new Promise(function(resolve2, reject) {
|
|
15752
|
-
v = o[
|
|
15705
|
+
v = o[n2](v), settle(resolve2, reject, v.done, v.value);
|
|
15753
15706
|
});
|
|
15754
15707
|
};
|
|
15755
15708
|
}
|
|
@@ -15868,7 +15821,7 @@ var init_tslib_es6 = __esm({
|
|
|
15868
15821
|
};
|
|
15869
15822
|
__assign = function() {
|
|
15870
15823
|
__assign = Object.assign || function __assign2(t) {
|
|
15871
|
-
for (var s, i = 1,
|
|
15824
|
+
for (var s, i = 1, n2 = arguments.length; i < n2; i++) {
|
|
15872
15825
|
s = arguments[i];
|
|
15873
15826
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
15874
15827
|
}
|
|
@@ -31053,8 +31006,8 @@ var require_GoTrueClient = __commonJS({
|
|
|
31053
31006
|
const result = await this.initializePromise;
|
|
31054
31007
|
const queue = (_a3 = this._pendingInitNotifications) !== null && _a3 !== void 0 ? _a3 : [];
|
|
31055
31008
|
this._pendingInitNotifications = null;
|
|
31056
|
-
for (const
|
|
31057
|
-
await this._notifyAllSubscribers(
|
|
31009
|
+
for (const n2 of queue) {
|
|
31010
|
+
await this._notifyAllSubscribers(n2.event, n2.session, n2.broadcast);
|
|
31058
31011
|
}
|
|
31059
31012
|
return result;
|
|
31060
31013
|
}
|
|
@@ -39404,17 +39357,17 @@ var require_codegen = __commonJS({
|
|
|
39404
39357
|
this.nodes = nodes;
|
|
39405
39358
|
}
|
|
39406
39359
|
render(opts) {
|
|
39407
|
-
return this.nodes.reduce((code,
|
|
39360
|
+
return this.nodes.reduce((code, n2) => code + n2.render(opts), "");
|
|
39408
39361
|
}
|
|
39409
39362
|
optimizeNodes() {
|
|
39410
39363
|
const { nodes } = this;
|
|
39411
39364
|
let i = nodes.length;
|
|
39412
39365
|
while (i--) {
|
|
39413
|
-
const
|
|
39414
|
-
if (Array.isArray(
|
|
39415
|
-
nodes.splice(i, 1, ...
|
|
39416
|
-
else if (
|
|
39417
|
-
nodes[i] =
|
|
39366
|
+
const n2 = nodes[i].optimizeNodes();
|
|
39367
|
+
if (Array.isArray(n2))
|
|
39368
|
+
nodes.splice(i, 1, ...n2);
|
|
39369
|
+
else if (n2)
|
|
39370
|
+
nodes[i] = n2;
|
|
39418
39371
|
else
|
|
39419
39372
|
nodes.splice(i, 1);
|
|
39420
39373
|
}
|
|
@@ -39424,16 +39377,16 @@ var require_codegen = __commonJS({
|
|
|
39424
39377
|
const { nodes } = this;
|
|
39425
39378
|
let i = nodes.length;
|
|
39426
39379
|
while (i--) {
|
|
39427
|
-
const
|
|
39428
|
-
if (
|
|
39380
|
+
const n2 = nodes[i];
|
|
39381
|
+
if (n2.optimizeNames(names, constants))
|
|
39429
39382
|
continue;
|
|
39430
|
-
subtractNames(names,
|
|
39383
|
+
subtractNames(names, n2.names);
|
|
39431
39384
|
nodes.splice(i, 1);
|
|
39432
39385
|
}
|
|
39433
39386
|
return nodes.length > 0 ? this : void 0;
|
|
39434
39387
|
}
|
|
39435
39388
|
get names() {
|
|
39436
|
-
return this.nodes.reduce((names,
|
|
39389
|
+
return this.nodes.reduce((names, n2) => addNames(names, n2.names), {});
|
|
39437
39390
|
}
|
|
39438
39391
|
};
|
|
39439
39392
|
var BlockNode = class extends ParentNode {
|
|
@@ -39844,8 +39797,8 @@ var require_codegen = __commonJS({
|
|
|
39844
39797
|
endFunc() {
|
|
39845
39798
|
return this._endBlockNode(Func);
|
|
39846
39799
|
}
|
|
39847
|
-
optimize(
|
|
39848
|
-
while (
|
|
39800
|
+
optimize(n2 = 1) {
|
|
39801
|
+
while (n2-- > 0) {
|
|
39849
39802
|
this._root.optimizeNodes();
|
|
39850
39803
|
this._root.optimizeNames(this._root.names, this._constants);
|
|
39851
39804
|
}
|
|
@@ -39859,19 +39812,19 @@ var require_codegen = __commonJS({
|
|
|
39859
39812
|
this._nodes.push(node);
|
|
39860
39813
|
}
|
|
39861
39814
|
_endBlockNode(N1, N2) {
|
|
39862
|
-
const
|
|
39863
|
-
if (
|
|
39815
|
+
const n2 = this._currNode;
|
|
39816
|
+
if (n2 instanceof N1 || N2 && n2 instanceof N2) {
|
|
39864
39817
|
this._nodes.pop();
|
|
39865
39818
|
return this;
|
|
39866
39819
|
}
|
|
39867
39820
|
throw new Error(`CodeGen: not in block "${N2 ? `${N1.kind}/${N2.kind}` : N1.kind}"`);
|
|
39868
39821
|
}
|
|
39869
39822
|
_elseNode(node) {
|
|
39870
|
-
const
|
|
39871
|
-
if (!(
|
|
39823
|
+
const n2 = this._currNode;
|
|
39824
|
+
if (!(n2 instanceof If)) {
|
|
39872
39825
|
throw new Error('CodeGen: "else" without "if"');
|
|
39873
39826
|
}
|
|
39874
|
-
this._currNode =
|
|
39827
|
+
this._currNode = n2.else = node;
|
|
39875
39828
|
return this;
|
|
39876
39829
|
}
|
|
39877
39830
|
get _root() {
|
|
@@ -39888,8 +39841,8 @@ var require_codegen = __commonJS({
|
|
|
39888
39841
|
};
|
|
39889
39842
|
exports.CodeGen = CodeGen;
|
|
39890
39843
|
function addNames(names, from) {
|
|
39891
|
-
for (const
|
|
39892
|
-
names[
|
|
39844
|
+
for (const n2 in from)
|
|
39845
|
+
names[n2] = (names[n2] || 0) + (from[n2] || 0);
|
|
39893
39846
|
return names;
|
|
39894
39847
|
}
|
|
39895
39848
|
function addExprNames(names, from) {
|
|
@@ -39909,11 +39862,11 @@ var require_codegen = __commonJS({
|
|
|
39909
39862
|
items.push(c);
|
|
39910
39863
|
return items;
|
|
39911
39864
|
}, []));
|
|
39912
|
-
function replaceName(
|
|
39913
|
-
const c = constants[
|
|
39914
|
-
if (c === void 0 || names[
|
|
39915
|
-
return
|
|
39916
|
-
delete names[
|
|
39865
|
+
function replaceName(n2) {
|
|
39866
|
+
const c = constants[n2.str];
|
|
39867
|
+
if (c === void 0 || names[n2.str] !== 1)
|
|
39868
|
+
return n2;
|
|
39869
|
+
delete names[n2.str];
|
|
39917
39870
|
return c;
|
|
39918
39871
|
}
|
|
39919
39872
|
function canOptimize(e) {
|
|
@@ -39921,8 +39874,8 @@ var require_codegen = __commonJS({
|
|
|
39921
39874
|
}
|
|
39922
39875
|
}
|
|
39923
39876
|
function subtractNames(names, from) {
|
|
39924
|
-
for (const
|
|
39925
|
-
names[
|
|
39877
|
+
for (const n2 in from)
|
|
39878
|
+
names[n2] = (names[n2] || 0) - (from[n2] || 0);
|
|
39926
39879
|
}
|
|
39927
39880
|
function not(x) {
|
|
39928
39881
|
return typeof x == "boolean" || typeof x == "number" || x === null ? !x : (0, code_1._)`!${par(x)}`;
|
|
@@ -45938,6 +45891,64 @@ var init_dist7 = __esm({
|
|
|
45938
45891
|
}
|
|
45939
45892
|
});
|
|
45940
45893
|
|
|
45894
|
+
// src/options.ts
|
|
45895
|
+
function stripTokenFlag(argv2) {
|
|
45896
|
+
const at = argv2.indexOf("--token");
|
|
45897
|
+
if (at < 0) return [...argv2];
|
|
45898
|
+
return [...argv2.slice(0, at), ...argv2.slice(at + 2)];
|
|
45899
|
+
}
|
|
45900
|
+
function numericIn(argv2, name, spec = {}) {
|
|
45901
|
+
const i = argv2.indexOf(`--${name}`);
|
|
45902
|
+
if (i < 0) return spec.fallback;
|
|
45903
|
+
const raw = argv2[i + 1];
|
|
45904
|
+
if (raw === void 0 || raw.startsWith("--")) {
|
|
45905
|
+
throw new OptionError(
|
|
45906
|
+
`--${name} expects a ${spec.integer ? "whole " : ""}number${spec.unit ? ` of ${spec.unit}` : ""}, but no value followed it.`
|
|
45907
|
+
);
|
|
45908
|
+
}
|
|
45909
|
+
const value = raw.trim() === "" ? NaN : Number(raw);
|
|
45910
|
+
const what = `--${name} ${JSON.stringify(raw)}`;
|
|
45911
|
+
if (!Number.isFinite(value)) {
|
|
45912
|
+
throw new OptionError(
|
|
45913
|
+
`${what} is not a number.` + // Reached only by something like `-abc`. A plain `-5` is a finite
|
|
45914
|
+
// negative and falls to the range check below, which is the accurate
|
|
45915
|
+
// refusal for it — this hint is for the case where the leading dash is
|
|
45916
|
+
// the reason it did not parse at all.
|
|
45917
|
+
(raw.startsWith("-") ? " A leading `-` reads as a flag, not a sign." : "")
|
|
45918
|
+
);
|
|
45919
|
+
}
|
|
45920
|
+
if (value <= 0) {
|
|
45921
|
+
throw new OptionError(`${what} must be greater than zero${spec.unit ? ` ${spec.unit}` : ""}.`);
|
|
45922
|
+
}
|
|
45923
|
+
if (spec.integer && !Number.isInteger(value)) {
|
|
45924
|
+
throw new OptionError(`${what} must be a whole number.`);
|
|
45925
|
+
}
|
|
45926
|
+
return value;
|
|
45927
|
+
}
|
|
45928
|
+
function requiredNumericIn(argv2, name, fallback, spec = {}) {
|
|
45929
|
+
return numericIn(argv2, name, { ...spec, fallback });
|
|
45930
|
+
}
|
|
45931
|
+
function secondsIn(argv2, name, fallbackSeconds) {
|
|
45932
|
+
return requiredNumericIn(argv2, name, fallbackSeconds, { unit: "seconds" }) * 1e3;
|
|
45933
|
+
}
|
|
45934
|
+
var OptionError, flagIn, optIn;
|
|
45935
|
+
var init_options = __esm({
|
|
45936
|
+
"src/options.ts"() {
|
|
45937
|
+
"use strict";
|
|
45938
|
+
OptionError = class extends Error {
|
|
45939
|
+
constructor(message) {
|
|
45940
|
+
super(message);
|
|
45941
|
+
this.name = "OptionError";
|
|
45942
|
+
}
|
|
45943
|
+
};
|
|
45944
|
+
flagIn = (argv2, name) => argv2.includes(`--${name}`);
|
|
45945
|
+
optIn = (argv2, name, fallback) => {
|
|
45946
|
+
const i = argv2.indexOf(`--${name}`);
|
|
45947
|
+
return i >= 0 && argv2[i + 1] && !argv2[i + 1].startsWith("--") ? argv2[i + 1] : fallback;
|
|
45948
|
+
};
|
|
45949
|
+
}
|
|
45950
|
+
});
|
|
45951
|
+
|
|
45941
45952
|
// src/env.ts
|
|
45942
45953
|
function applyCloudDefaults() {
|
|
45943
45954
|
const url3 = process.env.SUPABASE_URL ?? process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
@@ -45990,6 +46001,21 @@ function childSession() {
|
|
|
45990
46001
|
const endpoint = sessionEndpoint();
|
|
45991
46002
|
return endpoint ? fetchDelegatedSession(endpoint, token) : mintDelegatedSession({ config: config2, token, product: "projects", mintedAt: MINTED_AT });
|
|
45992
46003
|
}
|
|
46004
|
+
async function contextOrExplain() {
|
|
46005
|
+
try {
|
|
46006
|
+
return await getContext();
|
|
46007
|
+
} catch (error51) {
|
|
46008
|
+
const label = process.env.CRUO_IDENTITY?.trim();
|
|
46009
|
+
if (!label) throw error51;
|
|
46010
|
+
throw new Error(
|
|
46011
|
+
`${error51 instanceof Error ? error51.message : String(error51)}
|
|
46012
|
+
|
|
46013
|
+
That is the token stored for \`${label}\`.
|
|
46014
|
+
Replace it: Cruo \u2192 Settings \u2192 Members \u2192 ${label} \u2192 new token,
|
|
46015
|
+
then: cruo login <token> --as ${label}`
|
|
46016
|
+
);
|
|
46017
|
+
}
|
|
46018
|
+
}
|
|
45993
46019
|
var stdioResolver;
|
|
45994
46020
|
var init_auth = __esm({
|
|
45995
46021
|
"src/auth.ts"() {
|
|
@@ -46001,6 +46027,176 @@ var init_auth = __esm({
|
|
|
46001
46027
|
}
|
|
46002
46028
|
});
|
|
46003
46029
|
|
|
46030
|
+
// src/usage.ts
|
|
46031
|
+
var usage_exports = {};
|
|
46032
|
+
__export(usage_exports, {
|
|
46033
|
+
reportUsage: () => reportUsage
|
|
46034
|
+
});
|
|
46035
|
+
function compact(value) {
|
|
46036
|
+
if (value === 0) return "\u2014";
|
|
46037
|
+
if (value >= 1e6) return `${(value / 1e6).toFixed(2)}M`;
|
|
46038
|
+
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}k`;
|
|
46039
|
+
return String(value);
|
|
46040
|
+
}
|
|
46041
|
+
async function reportUsage(opts) {
|
|
46042
|
+
const ctx = await contextOrExplain();
|
|
46043
|
+
const since = new Date(Date.now() - opts.days * 864e5).toISOString();
|
|
46044
|
+
let query = ctx.client.from("harness_runs").select("*").gte("started_at", since).order("started_at", { ascending: false });
|
|
46045
|
+
let issueTitle = null;
|
|
46046
|
+
if (opts.issue) {
|
|
46047
|
+
const [key, num3] = opts.issue.split("-");
|
|
46048
|
+
const { data: project } = await ctx.client.from("projects").select("id").eq("key", (key ?? "").toUpperCase()).maybeSingle();
|
|
46049
|
+
const { data: issue2 } = project ? await ctx.client.from("issues").select("id, title").eq("project_id", project.id).eq("number", Number(num3)).maybeSingle() : { data: null };
|
|
46050
|
+
if (!issue2) {
|
|
46051
|
+
console.error(`
|
|
46052
|
+
No issue ${opts.issue} on this board.
|
|
46053
|
+
`);
|
|
46054
|
+
process.exit(2);
|
|
46055
|
+
}
|
|
46056
|
+
issueTitle = issue2.title;
|
|
46057
|
+
query = query.eq("issue_id", issue2.id);
|
|
46058
|
+
}
|
|
46059
|
+
const { data, error: error51 } = await query;
|
|
46060
|
+
if (error51) {
|
|
46061
|
+
console.error(`
|
|
46062
|
+
Could not read run costs: ${error51.message}
|
|
46063
|
+
`);
|
|
46064
|
+
process.exit(1);
|
|
46065
|
+
}
|
|
46066
|
+
const rows = data ?? [];
|
|
46067
|
+
console.log(
|
|
46068
|
+
`
|
|
46069
|
+
${opts.issue ? `${opts.issue} \u2014 ${issueTitle}` : ctx.workspaceName} \xB7 last ${opts.days} day${opts.days === 1 ? "" : "s"}`
|
|
46070
|
+
);
|
|
46071
|
+
if (rows.length === 0) {
|
|
46072
|
+
console.log(
|
|
46073
|
+
`
|
|
46074
|
+
No runs recorded. Either no agent has run in that window, or the runs
|
|
46075
|
+
predate cost recording (\`CRA-89\`, 2026-08-29).
|
|
46076
|
+
`
|
|
46077
|
+
);
|
|
46078
|
+
return;
|
|
46079
|
+
}
|
|
46080
|
+
const ids = [...new Set(rows.map((r) => r.agent_id))];
|
|
46081
|
+
const { data: people } = await ctx.client.schema("public").from("profiles").select("id, display_name, email").in("id", ids);
|
|
46082
|
+
const nameOf = (id) => {
|
|
46083
|
+
const p = (people ?? []).find((x) => x.id === id);
|
|
46084
|
+
return p?.display_name || p?.email || id.slice(0, 8);
|
|
46085
|
+
};
|
|
46086
|
+
const blank = () => ({
|
|
46087
|
+
runs: 0,
|
|
46088
|
+
productive: 0,
|
|
46089
|
+
neverRan: 0,
|
|
46090
|
+
ms: 0,
|
|
46091
|
+
bytes: 0,
|
|
46092
|
+
input: 0,
|
|
46093
|
+
output: 0,
|
|
46094
|
+
cacheRead: 0,
|
|
46095
|
+
cacheWrite: 0,
|
|
46096
|
+
cost: 0,
|
|
46097
|
+
measured: 0
|
|
46098
|
+
});
|
|
46099
|
+
const fold = (a, r) => {
|
|
46100
|
+
a.runs += 1;
|
|
46101
|
+
if (r.outcome === "productive") a.productive += 1;
|
|
46102
|
+
if (r.outcome === "never-ran") a.neverRan += 1;
|
|
46103
|
+
a.ms += r.duration_ms;
|
|
46104
|
+
a.bytes += n(r.stdout_bytes);
|
|
46105
|
+
a.input += n(r.input_tokens);
|
|
46106
|
+
a.output += n(r.output_tokens);
|
|
46107
|
+
a.cacheRead += n(r.cache_read_tokens);
|
|
46108
|
+
a.cacheWrite += n(r.cache_write_tokens);
|
|
46109
|
+
a.cost += n(r.cost_usd);
|
|
46110
|
+
if (r.input_tokens !== null || r.cost_usd !== null) a.measured += 1;
|
|
46111
|
+
};
|
|
46112
|
+
const byAgent = /* @__PURE__ */ new Map();
|
|
46113
|
+
const total = blank();
|
|
46114
|
+
for (const r of rows) {
|
|
46115
|
+
if (!byAgent.has(r.agent_id)) byAgent.set(r.agent_id, blank());
|
|
46116
|
+
fold(byAgent.get(r.agent_id), r);
|
|
46117
|
+
fold(total, r);
|
|
46118
|
+
}
|
|
46119
|
+
const w = Math.max(12, ...[...byAgent.keys()].map((id) => nameOf(id).length));
|
|
46120
|
+
console.log(
|
|
46121
|
+
`
|
|
46122
|
+
${pad("agent", w)} ${rpad("runs", 5)} ${rpad("useful", 7)} ${rpad("dead", 5)} ${rpad("time", 7)} ${rpad("in", 8)} ${rpad("out", 8)} ${rpad("cost", 9)}`
|
|
46123
|
+
);
|
|
46124
|
+
for (const [id, a] of [...byAgent].sort((x, y) => y[1].runs - x[1].runs)) {
|
|
46125
|
+
console.log(
|
|
46126
|
+
` ${pad(nameOf(id), w)} ${rpad(String(a.runs), 5)} ${rpad(String(a.productive), 7)} ${rpad(String(a.neverRan), 5)} ${rpad(duration3(a.ms), 7)} ${rpad(compact(a.input + a.cacheRead), 8)} ${rpad(compact(a.output), 8)} ${rpad(a.cost > 0 ? `$${a.cost.toFixed(2)}` : "\u2014", 9)}`
|
|
46127
|
+
);
|
|
46128
|
+
}
|
|
46129
|
+
if (!opts.issue) {
|
|
46130
|
+
const byIssue = /* @__PURE__ */ new Map();
|
|
46131
|
+
for (const r of rows) {
|
|
46132
|
+
if (!r.issue_id) continue;
|
|
46133
|
+
if (!byIssue.has(r.issue_id)) byIssue.set(r.issue_id, blank());
|
|
46134
|
+
fold(byIssue.get(r.issue_id), r);
|
|
46135
|
+
}
|
|
46136
|
+
const worst = [...byIssue].sort((x, y) => y[1].runs - x[1].runs).slice(0, 8);
|
|
46137
|
+
if (worst.length > 0) {
|
|
46138
|
+
const { data: issues } = await ctx.client.from("issues").select("id, number, title, project_id").in("id", worst.map(([id]) => id));
|
|
46139
|
+
const { data: projects } = await ctx.client.from("projects").select("id, key");
|
|
46140
|
+
const refOf = (id) => {
|
|
46141
|
+
const i = (issues ?? []).find((x) => x.id === id);
|
|
46142
|
+
if (!i) return id.slice(0, 8);
|
|
46143
|
+
const key = (projects ?? []).find((p) => p.id === i.project_id)?.key ?? "?";
|
|
46144
|
+
return `${key}-${i.number}`;
|
|
46145
|
+
};
|
|
46146
|
+
const titleOf = (id) => (issues ?? []).find((x) => x.id === id)?.title ?? "";
|
|
46147
|
+
console.log(`
|
|
46148
|
+
most-worked cards`);
|
|
46149
|
+
for (const [id, a] of worst) {
|
|
46150
|
+
console.log(
|
|
46151
|
+
` ${rpad(String(a.runs), 3)} run${a.runs === 1 ? " " : "s"} ${pad(refOf(id), 8)} ${rpad(duration3(a.ms), 6)} ${titleOf(id).slice(0, 52)}`
|
|
46152
|
+
);
|
|
46153
|
+
}
|
|
46154
|
+
}
|
|
46155
|
+
} else {
|
|
46156
|
+
console.log(`
|
|
46157
|
+
run by run`);
|
|
46158
|
+
for (const r of rows) {
|
|
46159
|
+
console.log(
|
|
46160
|
+
` ${r.started_at.slice(0, 16).replace("T", " ")} ${pad(r.outcome, 13)} ${rpad(duration3(r.duration_ms), 6)} ${rpad(compact(n(r.output_tokens)), 7)} out ${nameOf(r.agent_id)}`
|
|
46161
|
+
);
|
|
46162
|
+
}
|
|
46163
|
+
}
|
|
46164
|
+
console.log(
|
|
46165
|
+
`
|
|
46166
|
+
${total.runs} run(s), ${total.productive} productive, ${total.neverRan} never reached a model.
|
|
46167
|
+
${duration3(total.ms)} of wall clock, ${compact(total.bytes)} bytes of output.`
|
|
46168
|
+
);
|
|
46169
|
+
if (total.measured === 0) {
|
|
46170
|
+
console.log(
|
|
46171
|
+
`
|
|
46172
|
+
No token or cost figures: this harness does not report them. Runs, time and
|
|
46173
|
+
output are measured for any harness; tokens need one that says what it spent \u2014
|
|
46174
|
+
e.g. --harness "claude -p --output-format json".
|
|
46175
|
+
`
|
|
46176
|
+
);
|
|
46177
|
+
} else if (total.measured < total.runs) {
|
|
46178
|
+
console.log(
|
|
46179
|
+
`
|
|
46180
|
+
${total.measured} of ${total.runs} runs reported tokens; the rest did not, so the
|
|
46181
|
+
figures above are a floor rather than a total.
|
|
46182
|
+
`
|
|
46183
|
+
);
|
|
46184
|
+
} else {
|
|
46185
|
+
console.log("");
|
|
46186
|
+
}
|
|
46187
|
+
}
|
|
46188
|
+
var n, pad, rpad, duration3;
|
|
46189
|
+
var init_usage = __esm({
|
|
46190
|
+
"src/usage.ts"() {
|
|
46191
|
+
"use strict";
|
|
46192
|
+
init_auth();
|
|
46193
|
+
n = (v) => v === null || v === void 0 ? 0 : Number(v);
|
|
46194
|
+
pad = (s, w) => s.padEnd(w);
|
|
46195
|
+
rpad = (s, w) => s.padStart(w);
|
|
46196
|
+
duration3 = (ms2) => ms2 >= 36e5 ? `${(ms2 / 36e5).toFixed(1)}h` : ms2 >= 6e4 ? `${(ms2 / 6e4).toFixed(1)}m` : `${Math.round(ms2 / 1e3)}s`;
|
|
46197
|
+
}
|
|
46198
|
+
});
|
|
46199
|
+
|
|
46004
46200
|
// src/harness-signal.ts
|
|
46005
46201
|
function refusalIn(stdout) {
|
|
46006
46202
|
if (!stdout.trim()) return null;
|
|
@@ -46012,7 +46208,36 @@ function refusalIn(stdout) {
|
|
|
46012
46208
|
function neverRan(run) {
|
|
46013
46209
|
return run.refusal !== null || run.stdoutBytes === 0;
|
|
46014
46210
|
}
|
|
46015
|
-
|
|
46211
|
+
function usageIn(text) {
|
|
46212
|
+
const trimmed = text.trimEnd();
|
|
46213
|
+
if (!trimmed.endsWith("}")) return null;
|
|
46214
|
+
let parsed = null;
|
|
46215
|
+
for (let i = trimmed.length - 1; i >= 0; i -= 1) {
|
|
46216
|
+
if (trimmed[i] !== "{") continue;
|
|
46217
|
+
try {
|
|
46218
|
+
parsed = JSON.parse(trimmed.slice(i));
|
|
46219
|
+
break;
|
|
46220
|
+
} catch {
|
|
46221
|
+
}
|
|
46222
|
+
}
|
|
46223
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
46224
|
+
const root = parsed;
|
|
46225
|
+
const usage = root.usage ?? root;
|
|
46226
|
+
const out = {
|
|
46227
|
+
model: typeof root.model === "string" ? root.model : void 0,
|
|
46228
|
+
// Both spellings. The snake_case ones are the API's; a harness that passes
|
|
46229
|
+
// its response through unchanged emits those, and one that tidies them
|
|
46230
|
+
// emits camelCase.
|
|
46231
|
+
inputTokens: num(usage.input_tokens) ?? num(usage.inputTokens),
|
|
46232
|
+
outputTokens: num(usage.output_tokens) ?? num(usage.outputTokens),
|
|
46233
|
+
cacheReadTokens: num(usage.cache_read_input_tokens) ?? num(usage.cacheReadInputTokens),
|
|
46234
|
+
cacheWriteTokens: num(usage.cache_creation_input_tokens) ?? num(usage.cacheCreationInputTokens),
|
|
46235
|
+
costUsd: num(root.total_cost_usd) ?? num(root.totalCostUsd) ?? num(root.cost_usd)
|
|
46236
|
+
};
|
|
46237
|
+
const measured = Object.values(out).some((v) => v !== void 0);
|
|
46238
|
+
return measured ? out : null;
|
|
46239
|
+
}
|
|
46240
|
+
var REFUSALS, num;
|
|
46016
46241
|
var init_harness_signal = __esm({
|
|
46017
46242
|
"src/harness-signal.ts"() {
|
|
46018
46243
|
"use strict";
|
|
@@ -46023,6 +46248,47 @@ var init_harness_signal = __esm({
|
|
|
46023
46248
|
[/invalid api key|authentication[_ ]error|please run \/login/i, "the harness is not signed in"],
|
|
46024
46249
|
[/rate limit(ed| exceeded|s? reached)\b.*\btry again/i, "the account is rate limited"]
|
|
46025
46250
|
];
|
|
46251
|
+
num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
46252
|
+
}
|
|
46253
|
+
});
|
|
46254
|
+
|
|
46255
|
+
// src/pacing.ts
|
|
46256
|
+
function afterAttempt(p, attempt, limits, now) {
|
|
46257
|
+
const allDead = attempt.invoked > 0 && attempt.failedToRun === attempt.invoked;
|
|
46258
|
+
const deadTicks = allDead ? p.deadTicks + 1 : 0;
|
|
46259
|
+
if (deadTicks < 2) {
|
|
46260
|
+
return {
|
|
46261
|
+
next: { deadTicks, invokeAfter: 0, backedOff: false },
|
|
46262
|
+
event: p.backedOff ? "recovered" : null,
|
|
46263
|
+
cooldownMs: 0
|
|
46264
|
+
};
|
|
46265
|
+
}
|
|
46266
|
+
const cooldownMs = Math.min(
|
|
46267
|
+
limits.intervalMs * 2 ** (deadTicks - 1),
|
|
46268
|
+
limits.maxIntervalMs
|
|
46269
|
+
);
|
|
46270
|
+
return {
|
|
46271
|
+
next: { deadTicks, invokeAfter: now + cooldownMs, backedOff: true },
|
|
46272
|
+
// Said once per descent, not once per tick. The ceiling is reached in a few
|
|
46273
|
+
// ticks and held for hours; repeating the line there would bury the
|
|
46274
|
+
// `recovered` that matters.
|
|
46275
|
+
event: p.backedOff ? null : "backing-off",
|
|
46276
|
+
cooldownMs
|
|
46277
|
+
};
|
|
46278
|
+
}
|
|
46279
|
+
var freshPacing, allowanceAt;
|
|
46280
|
+
var init_pacing = __esm({
|
|
46281
|
+
"src/pacing.ts"() {
|
|
46282
|
+
"use strict";
|
|
46283
|
+
freshPacing = () => ({
|
|
46284
|
+
deadTicks: 0,
|
|
46285
|
+
invokeAfter: 0,
|
|
46286
|
+
backedOff: false
|
|
46287
|
+
});
|
|
46288
|
+
allowanceAt = (p, now) => {
|
|
46289
|
+
if (!p.backedOff) return "free";
|
|
46290
|
+
return now >= p.invokeAfter ? "probe" : "held";
|
|
46291
|
+
};
|
|
46026
46292
|
}
|
|
46027
46293
|
});
|
|
46028
46294
|
|
|
@@ -46253,10 +46519,10 @@ async function pollMentions(ctx, identity) {
|
|
|
46253
46519
|
if (error51) throw new Error(`Mention poll failed: ${error51.message}`);
|
|
46254
46520
|
if (!notes || notes.length === 0) return [];
|
|
46255
46521
|
const byIssue = /* @__PURE__ */ new Map();
|
|
46256
|
-
for (const
|
|
46257
|
-
const list = byIssue.get(
|
|
46258
|
-
list.push(
|
|
46259
|
-
byIssue.set(
|
|
46522
|
+
for (const n2 of notes) {
|
|
46523
|
+
const list = byIssue.get(n2.issue_id) ?? [];
|
|
46524
|
+
list.push(n2.id);
|
|
46525
|
+
byIssue.set(n2.issue_id, list);
|
|
46260
46526
|
}
|
|
46261
46527
|
const { data: issues, error: issueErr } = await ctx.client.from("issues").select("*").in("id", [...byIssue.keys()]);
|
|
46262
46528
|
if (issueErr) throw new Error(`Mention issue lookup failed: ${issueErr.message}`);
|
|
@@ -46450,8 +46716,18 @@ function systemPrompt(ctx, identity, worktree) {
|
|
|
46450
46716
|
`survives. A run that ends with uncommitted changes has produced nothing,`,
|
|
46451
46717
|
`however good the changes were.`,
|
|
46452
46718
|
``,
|
|
46453
|
-
`Do not push, and do not merge into any other branch.
|
|
46454
|
-
`
|
|
46719
|
+
`Do not push, and do not merge into any other branch. That is not a`,
|
|
46720
|
+
`matter of trust \u2014 you have no push tool, and nothing in this loop`,
|
|
46721
|
+
`merges anything (\`CRA-88\`). Releasing is a person's job.`,
|
|
46722
|
+
...options.push ? [
|
|
46723
|
+
`Your branch is published for you after you exit, by the process`,
|
|
46724
|
+
`that started you. It is pushed, not merged: somebody still has`,
|
|
46725
|
+
`to review and land it.`
|
|
46726
|
+
] : [
|
|
46727
|
+
`Your branch stays on this machine. Nothing publishes it, so say`,
|
|
46728
|
+
`on the card which branch holds the work \u2014 that name is the only`,
|
|
46729
|
+
`way anyone finds it.`
|
|
46730
|
+
]
|
|
46455
46731
|
] : [],
|
|
46456
46732
|
``,
|
|
46457
46733
|
`You act on the board through the Cruo MCP tools. Start with get_issue to read the`,
|
|
@@ -46530,11 +46806,14 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
46530
46806
|
cwd,
|
|
46531
46807
|
env: harnessEnv()
|
|
46532
46808
|
});
|
|
46809
|
+
const startedAtMs = Date.now();
|
|
46533
46810
|
let stdoutBytes = 0;
|
|
46811
|
+
let tail = "";
|
|
46534
46812
|
let head2 = "";
|
|
46535
46813
|
child.stdout?.on("data", (c) => {
|
|
46536
46814
|
stdoutBytes += c.length;
|
|
46537
46815
|
if (head2.length < 2048) head2 += c.toString("utf8").slice(0, 2048 - head2.length);
|
|
46816
|
+
tail = (tail + c.toString("utf8")).slice(-4096);
|
|
46538
46817
|
process.stdout.write(c);
|
|
46539
46818
|
});
|
|
46540
46819
|
child.stderr?.on("data", (c) => process.stderr.write(c));
|
|
@@ -46546,11 +46825,23 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
46546
46825
|
child.on("error", (e) => {
|
|
46547
46826
|
clearTimeout(timer);
|
|
46548
46827
|
log(` harness failed to start: ${e.message}`);
|
|
46549
|
-
resolve2({
|
|
46828
|
+
resolve2({
|
|
46829
|
+
code: 127,
|
|
46830
|
+
stdoutBytes,
|
|
46831
|
+
refusal: refusalIn(head2),
|
|
46832
|
+
durationMs: Date.now() - startedAtMs,
|
|
46833
|
+
usage: null
|
|
46834
|
+
});
|
|
46550
46835
|
});
|
|
46551
46836
|
child.on("close", (code) => {
|
|
46552
46837
|
clearTimeout(timer);
|
|
46553
|
-
resolve2({
|
|
46838
|
+
resolve2({
|
|
46839
|
+
code: code ?? 1,
|
|
46840
|
+
stdoutBytes,
|
|
46841
|
+
refusal: refusalIn(head2),
|
|
46842
|
+
durationMs: Date.now() - startedAtMs,
|
|
46843
|
+
usage: usageIn(tail)
|
|
46844
|
+
});
|
|
46554
46845
|
});
|
|
46555
46846
|
});
|
|
46556
46847
|
} finally {
|
|
@@ -46633,7 +46924,29 @@ async function beat(ctx, fields) {
|
|
|
46633
46924
|
);
|
|
46634
46925
|
if (error51) log(` could not record a heartbeat: ${error51.message}`);
|
|
46635
46926
|
}
|
|
46636
|
-
async function
|
|
46927
|
+
async function recordRun(ctx, hit, run, outcome) {
|
|
46928
|
+
const { error: error51 } = await ctx.client.from("harness_runs").insert({
|
|
46929
|
+
agent_id: ctx.userId,
|
|
46930
|
+
workspace_id: ctx.workspaceId,
|
|
46931
|
+
issue_id: hit.issue.id,
|
|
46932
|
+
reason: hit.reason,
|
|
46933
|
+
duration_ms: run.durationMs,
|
|
46934
|
+
exit_code: run.code,
|
|
46935
|
+
stdout_bytes: run.stdoutBytes,
|
|
46936
|
+
outcome,
|
|
46937
|
+
model: run.usage?.model ?? null,
|
|
46938
|
+
input_tokens: run.usage?.inputTokens ?? null,
|
|
46939
|
+
output_tokens: run.usage?.outputTokens ?? null,
|
|
46940
|
+
cache_read_tokens: run.usage?.cacheReadTokens ?? null,
|
|
46941
|
+
cache_write_tokens: run.usage?.cacheWriteTokens ?? null,
|
|
46942
|
+
cost_usd: run.usage?.costUsd ?? null
|
|
46943
|
+
});
|
|
46944
|
+
if (error51 && !warnedAboutSpendTable) {
|
|
46945
|
+
warnedAboutSpendTable = true;
|
|
46946
|
+
log(` (not recording run costs: ${error51.message})`);
|
|
46947
|
+
}
|
|
46948
|
+
}
|
|
46949
|
+
async function tick(ctx, identity, deadTicks, allowance) {
|
|
46637
46950
|
const mentions = await pollMentions(ctx, identity);
|
|
46638
46951
|
const [assigned, stateWork] = await Promise.all([pollAssigned(ctx), poll(ctx, identity)]);
|
|
46639
46952
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -46654,7 +46967,20 @@ async function tick(ctx, identity, deadTicks) {
|
|
|
46654
46967
|
if (free.length > 0) log(`${free.length} issue(s) waiting, all set aside`);
|
|
46655
46968
|
else if (heldByOthers.size > 0) log(`nothing free \u2014 everything waiting is claimed`);
|
|
46656
46969
|
await beat(ctx, { holding: 0, deadTicks, intervalMs: options.intervalMs });
|
|
46657
|
-
return { invoked: 0, failedToRun: 0 };
|
|
46970
|
+
return { invoked: 0, failedToRun: 0, suppressed: false, waiting: 0 };
|
|
46971
|
+
}
|
|
46972
|
+
if (allowance === "held") {
|
|
46973
|
+
await beat(ctx, {
|
|
46974
|
+
holding: actionable.length,
|
|
46975
|
+
deadTicks,
|
|
46976
|
+
intervalMs: options.intervalMs
|
|
46977
|
+
});
|
|
46978
|
+
return {
|
|
46979
|
+
invoked: 0,
|
|
46980
|
+
failedToRun: 0,
|
|
46981
|
+
suppressed: true,
|
|
46982
|
+
waiting: actionable.length
|
|
46983
|
+
};
|
|
46658
46984
|
}
|
|
46659
46985
|
await beat(ctx, {
|
|
46660
46986
|
holding: actionable.length,
|
|
@@ -46667,9 +46993,12 @@ async function tick(ctx, identity, deadTicks) {
|
|
|
46667
46993
|
log(
|
|
46668
46994
|
`${actionable.length} issue(s) for "${identity.jobFunction}"` + (mentionCount > 0 ? ` \u2014 ${mentionCount} from unread mentions` : "")
|
|
46669
46995
|
);
|
|
46670
|
-
const
|
|
46996
|
+
const cap = allowance === "probe" ? 1 : options.limit;
|
|
46997
|
+
const taking = cap ? actionable.slice(0, cap) : actionable;
|
|
46671
46998
|
if (taking.length < actionable.length) {
|
|
46672
|
-
log(
|
|
46999
|
+
log(
|
|
47000
|
+
allowance === "probe" ? ` probing with ${taking.length} of ${actionable.length} \u2014 one card is enough to learn whether the harness works again` : ` taking ${taking.length} this tick (--limit ${options.limit})`
|
|
47001
|
+
);
|
|
46673
47002
|
}
|
|
46674
47003
|
for (const hit of taking) {
|
|
46675
47004
|
const why = hit.reason === "mention" ? "mentioned" : hit.reason === "assigned" ? `assigned \xB7 ${hit.state.name}` : hit.state.name;
|
|
@@ -46709,6 +47038,7 @@ async function tick(ctx, identity, deadTicks) {
|
|
|
46709
47038
|
const code = run.code;
|
|
46710
47039
|
if (neverRan(run)) {
|
|
46711
47040
|
failedToRun += 1;
|
|
47041
|
+
await recordRun(ctx, hit, run, "never-ran");
|
|
46712
47042
|
if (run.refusal) {
|
|
46713
47043
|
if (run.refusal !== lastRefusal) {
|
|
46714
47044
|
log(`! the harness is refusing to work: ${run.refusal}`);
|
|
@@ -46725,6 +47055,7 @@ async function tick(ctx, identity, deadTicks) {
|
|
|
46725
47055
|
const { data: after } = await ctx.client.from("issues").select("status_id, assignee_id").eq("id", hit.issue.id).maybeSingle();
|
|
46726
47056
|
const moved = !after || after.status_id !== before.status || after.assignee_id !== before.assignee;
|
|
46727
47057
|
const productive = hit.reason === "mention" ? await repliedSince(ctx, hit, startedAt) || moved : moved || committed > 0;
|
|
47058
|
+
await recordRun(ctx, hit, run, productive ? "productive" : "unproductive");
|
|
46728
47059
|
if (productive) {
|
|
46729
47060
|
await clearAttempts(ctx, hit);
|
|
46730
47061
|
if (hit.reason === "mention") {
|
|
@@ -46740,9 +47071,9 @@ async function tick(ctx, identity, deadTicks) {
|
|
|
46740
47071
|
}
|
|
46741
47072
|
} else {
|
|
46742
47073
|
const what = hit.reason === "mention" ? "no reply written" : "unchanged";
|
|
46743
|
-
const
|
|
47074
|
+
const n2 = await recordAttempt(ctx, hit, `${what} (exit ${code})`, attemptsFor(hit));
|
|
46744
47075
|
log(
|
|
46745
|
-
` ${hit.ref} ${what} after the run (exit ${code}) \u2014 attempt ${
|
|
47076
|
+
` ${hit.ref} ${what} after the run (exit ${code}) \u2014 attempt ${n2}/${options.maxAttempts}` + (n2 >= options.maxAttempts ? "; setting it aside, a human should look" : "")
|
|
46746
47077
|
);
|
|
46747
47078
|
}
|
|
46748
47079
|
if (worktree) {
|
|
@@ -46767,7 +47098,7 @@ async function tick(ctx, identity, deadTicks) {
|
|
|
46767
47098
|
deadTicks: allDead ? deadTicks + 1 : 0,
|
|
46768
47099
|
intervalMs: options.intervalMs
|
|
46769
47100
|
});
|
|
46770
|
-
return { invoked, failedToRun };
|
|
47101
|
+
return { invoked, failedToRun, suppressed: false, waiting: actionable.length };
|
|
46771
47102
|
}
|
|
46772
47103
|
async function main() {
|
|
46773
47104
|
log(
|
|
@@ -46780,7 +47111,7 @@ async function main() {
|
|
|
46780
47111
|
if (staleConfigs > 0) {
|
|
46781
47112
|
log(` cleared ${staleConfigs} temp config(s) left by a killed run`);
|
|
46782
47113
|
}
|
|
46783
|
-
const ctx = await
|
|
47114
|
+
const ctx = await contextOrExplain();
|
|
46784
47115
|
const identity = await identify(ctx);
|
|
46785
47116
|
try {
|
|
46786
47117
|
await preflight();
|
|
@@ -46811,6 +47142,7 @@ async function main() {
|
|
|
46811
47142
|
const swept = await sweep(worktreeConfig, options.harnessTimeoutMs + options.prepareTimeout);
|
|
46812
47143
|
if (swept.length) log(` swept ${swept.length} worktree(s) left by an earlier run: ${swept.join(", ")}`);
|
|
46813
47144
|
if (options.push) log(` publishing to ${options.pushRemote} after any run that commits`);
|
|
47145
|
+
else log(` --push is off: branches stay on this machine and reach no remote`);
|
|
46814
47146
|
if (options.allow === "mcp__cruo") {
|
|
46815
47147
|
log(`! --worktree with the default --allow gives the harness a checkout it cannot read.`);
|
|
46816
47148
|
log(` Widen it, e.g. --allow "mcp__cruo,Read,Glob,Grep,Edit,Write,Bash(git:*),Bash(pnpm:*)"`);
|
|
@@ -46826,24 +47158,45 @@ async function main() {
|
|
|
46826
47158
|
log(
|
|
46827
47159
|
options.once ? "single pass" : `polling every ${options.intervalMs / 1e3}s \xB7 ctrl-c to stop`
|
|
46828
47160
|
);
|
|
46829
|
-
let
|
|
47161
|
+
let pacing = freshPacing();
|
|
47162
|
+
let lastHeldLog = 0;
|
|
46830
47163
|
for (; ; ) {
|
|
46831
47164
|
try {
|
|
46832
|
-
const result = await tick(
|
|
46833
|
-
|
|
46834
|
-
|
|
47165
|
+
const result = await tick(
|
|
47166
|
+
await getContext(),
|
|
47167
|
+
identity,
|
|
47168
|
+
pacing.deadTicks,
|
|
47169
|
+
allowanceAt(pacing, Date.now())
|
|
47170
|
+
);
|
|
47171
|
+
if (result.suppressed) {
|
|
47172
|
+
const now = Date.now();
|
|
47173
|
+
if (now - lastHeldLog >= 6e4) {
|
|
47174
|
+
lastHeldLog = now;
|
|
47175
|
+
const left = Math.max(0, Math.round((pacing.invokeAfter - now) / 1e3));
|
|
47176
|
+
log(
|
|
47177
|
+
`! ${result.waiting} issue(s) waiting \u2014 not invoking after ${pacing.deadTicks} dead ticks; next attempt in ${left}s`
|
|
47178
|
+
);
|
|
47179
|
+
}
|
|
47180
|
+
} else {
|
|
47181
|
+
const step = afterAttempt(pacing, result, options, Date.now());
|
|
47182
|
+
pacing = step.next;
|
|
47183
|
+
if (step.event === "backing-off") {
|
|
47184
|
+
lastHeldLog = Date.now();
|
|
47185
|
+
log(
|
|
47186
|
+
` backing off \u2014 ${pacing.deadTicks} ticks with nothing reaching a model; still polling every ${options.intervalMs / 1e3}s, next invocation in ${step.cooldownMs / 1e3}s`
|
|
47187
|
+
);
|
|
47188
|
+
} else if (step.event === "recovered") {
|
|
47189
|
+
log(` recovered \u2014 a run reached a model; invoking normally again`);
|
|
47190
|
+
}
|
|
47191
|
+
}
|
|
46835
47192
|
} catch (e) {
|
|
46836
47193
|
log(`poll error: ${e instanceof Error ? e.message : String(e)}`);
|
|
46837
47194
|
}
|
|
46838
47195
|
if (options.once) return;
|
|
46839
|
-
|
|
46840
|
-
if (wait !== options.intervalMs) {
|
|
46841
|
-
log(` backing off \u2014 ${deadTicks} ticks with nothing reaching a model; next in ${wait / 1e3}s`);
|
|
46842
|
-
}
|
|
46843
|
-
await new Promise((r) => setTimeout(r, wait));
|
|
47196
|
+
await new Promise((r) => setTimeout(r, options.intervalMs));
|
|
46844
47197
|
}
|
|
46845
47198
|
}
|
|
46846
|
-
var argv, flag, opt,
|
|
47199
|
+
var argv, flag, opt, num2, ms, readOptions, options, log, worktreeConfig, lastRefusal, PRIORITY_RANK, CONFIG_DIR_PREFIX, CRUO_OWNED_ENV, warnedAboutSpendTable;
|
|
46847
47200
|
var init_supervisor = __esm({
|
|
46848
47201
|
"src/supervisor.ts"() {
|
|
46849
47202
|
"use strict";
|
|
@@ -46853,11 +47206,12 @@ var init_supervisor = __esm({
|
|
|
46853
47206
|
init_auth();
|
|
46854
47207
|
init_options();
|
|
46855
47208
|
init_harness_signal();
|
|
47209
|
+
init_pacing();
|
|
46856
47210
|
init_worktree();
|
|
46857
47211
|
argv = process.argv.slice(2);
|
|
46858
47212
|
flag = (name) => flagIn(argv, name);
|
|
46859
47213
|
opt = (name, fallback) => optIn(argv, name, fallback);
|
|
46860
|
-
|
|
47214
|
+
num2 = (name, spec) => numericIn(argv, name, spec);
|
|
46861
47215
|
ms = (name, fallbackSeconds) => secondsIn(argv, name, fallbackSeconds);
|
|
46862
47216
|
readOptions = () => ({
|
|
46863
47217
|
/** Override the function to poll for. Defaults to this agent's own. */
|
|
@@ -46873,7 +47227,7 @@ var init_supervisor = __esm({
|
|
|
46873
47227
|
* moment they have the least reason to trust three. Unset means all of them,
|
|
46874
47228
|
* which is the right steady-state behaviour.
|
|
46875
47229
|
*/
|
|
46876
|
-
limit:
|
|
47230
|
+
limit: num2("limit", { integer: true }),
|
|
46877
47231
|
intervalMs: ms("interval", 20),
|
|
46878
47232
|
once: flag("once"),
|
|
46879
47233
|
dryRun: flag("dry-run"),
|
|
@@ -46903,11 +47257,15 @@ var init_supervisor = __esm({
|
|
|
46903
47257
|
* `pm.issue_attempts` since CRA-7 — it was in-memory, which meant a restart
|
|
46904
47258
|
* forgot every set-aside and nothing outside the process could see one.
|
|
46905
47259
|
*/
|
|
46906
|
-
maxAttempts:
|
|
47260
|
+
maxAttempts: num2("max-attempts", { fallback: 3, integer: true }),
|
|
46907
47261
|
/**
|
|
46908
|
-
* Ceiling
|
|
46909
|
-
*
|
|
46910
|
-
* the cause is noticed without restarting
|
|
47262
|
+
* Ceiling on how long invocation is held back after a run of dead ticks.
|
|
47263
|
+
* Ten minutes: long enough that a broken harness stops being spawned over and
|
|
47264
|
+
* over, short enough that fixing the cause is noticed without restarting
|
|
47265
|
+
* anything.
|
|
47266
|
+
*
|
|
47267
|
+
* It is not a poll ceiling. Polling stays at `--interval` throughout, so this
|
|
47268
|
+
* bounds spend, not how quickly a recovery is seen (`CRA-87`).
|
|
46911
47269
|
*/
|
|
46912
47270
|
maxIntervalMs: ms("max-interval", 600),
|
|
46913
47271
|
/**
|
|
@@ -46992,6 +47350,7 @@ cruo-supervisor: ${error51.message}
|
|
|
46992
47350
|
// stands in for. Cheap to list, and wrong to discover the hard way.
|
|
46993
47351
|
"CRUO_SESSION"
|
|
46994
47352
|
];
|
|
47353
|
+
warnedAboutSpendTable = false;
|
|
46995
47354
|
main().catch((e) => {
|
|
46996
47355
|
console.error(e instanceof Error ? e.message : e);
|
|
46997
47356
|
process.exit(1);
|
|
@@ -47000,17 +47359,26 @@ cruo-supervisor: ${error51.message}
|
|
|
47000
47359
|
});
|
|
47001
47360
|
|
|
47002
47361
|
// src/cli.ts
|
|
47362
|
+
init_dist();
|
|
47363
|
+
init_dist7();
|
|
47003
47364
|
init_options();
|
|
47004
47365
|
import { chmod, mkdir as mkdir2, readFile as readFile2, rm as rm3, writeFile as writeFile2 } from "node:fs/promises";
|
|
47005
47366
|
import { homedir as homedir2 } from "node:os";
|
|
47006
47367
|
import { join as join3 } from "node:path";
|
|
47007
47368
|
var CONFIG_DIR = join3(homedir2(), ".cruo");
|
|
47008
47369
|
var CONFIG_PATH = join3(CONFIG_DIR, "config.json");
|
|
47370
|
+
function nameFromEmail(email3) {
|
|
47371
|
+
const local = email3.split("@")[0]?.toLowerCase().replace(/[^a-z0-9._-]/g, "");
|
|
47372
|
+
return local && local.length > 0 ? local : null;
|
|
47373
|
+
}
|
|
47009
47374
|
var USAGE = `cruo \u2014 run a Cruo agent
|
|
47010
47375
|
|
|
47011
47376
|
cruo login <token> remember an agent's token on this machine
|
|
47012
|
-
cruo
|
|
47377
|
+
cruo agents list what is remembered (never the tokens)
|
|
47378
|
+
cruo usage [--days 7] what the agents have been spending
|
|
47379
|
+
cruo logout [name] forget one, or --all
|
|
47013
47380
|
cruo [options] run the agent's supervisor
|
|
47381
|
+
cruo --as <name> \u2026 run a particular one
|
|
47014
47382
|
|
|
47015
47383
|
Getting a token
|
|
47016
47384
|
Cruo \u2192 Settings \u2192 Members \u2192 Add an agent. It is shown once.
|
|
@@ -47026,52 +47394,188 @@ Common options
|
|
|
47026
47394
|
|
|
47027
47395
|
Full list and what each one is for: https://cruo.space/docs#agents
|
|
47028
47396
|
|
|
47397
|
+
More than one agent on one machine: \`login\` files each token under the name the
|
|
47398
|
+
board shows, so the chain is \`cruo --as jarvis\` and \`cruo --as luna\` rather than
|
|
47399
|
+
a CRUO_TOKEN export per terminal.
|
|
47400
|
+
|
|
47029
47401
|
The token comes from CRUO_TOKEN, then --token, then whatever \`login\` stored.
|
|
47030
47402
|
`;
|
|
47031
|
-
|
|
47403
|
+
var EMPTY = { version: 2, default: null, agents: {} };
|
|
47404
|
+
async function readConfig2() {
|
|
47405
|
+
let parsed;
|
|
47406
|
+
try {
|
|
47407
|
+
parsed = JSON.parse(await readFile2(CONFIG_PATH, "utf8"));
|
|
47408
|
+
} catch {
|
|
47409
|
+
return { ...EMPTY };
|
|
47410
|
+
}
|
|
47411
|
+
const raw = parsed;
|
|
47412
|
+
if (raw.agents && typeof raw.agents === "object") {
|
|
47413
|
+
return {
|
|
47414
|
+
version: 2,
|
|
47415
|
+
default: typeof raw.default === "string" ? raw.default : null,
|
|
47416
|
+
agents: raw.agents
|
|
47417
|
+
};
|
|
47418
|
+
}
|
|
47419
|
+
const legacy = raw.token?.trim();
|
|
47420
|
+
if (legacy) {
|
|
47421
|
+
return { version: 2, default: "default", agents: { default: { token: legacy } } };
|
|
47422
|
+
}
|
|
47423
|
+
return { ...EMPTY };
|
|
47424
|
+
}
|
|
47425
|
+
async function writeConfig(config3) {
|
|
47426
|
+
await mkdir2(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
47427
|
+
await writeFile2(CONFIG_PATH, JSON.stringify(config3, null, 2) + "\n", { mode: 384 });
|
|
47428
|
+
await chmod(CONFIG_PATH, 384);
|
|
47429
|
+
}
|
|
47430
|
+
async function identify2(token) {
|
|
47431
|
+
const endpoint = process.env.CRUO_SESSION_URL?.trim() || CRUO_CLOUD.sessionUrl;
|
|
47032
47432
|
try {
|
|
47033
|
-
const
|
|
47034
|
-
return
|
|
47433
|
+
const session = await fetchDelegatedSession(endpoint, token);
|
|
47434
|
+
return { email: session.email, workspace: session.workspaceName };
|
|
47035
47435
|
} catch {
|
|
47036
47436
|
return null;
|
|
47037
47437
|
}
|
|
47038
47438
|
}
|
|
47039
|
-
async function login(token) {
|
|
47439
|
+
async function login(token, as) {
|
|
47040
47440
|
if (!token.startsWith("cruo_pat_")) {
|
|
47041
47441
|
console.error(`
|
|
47042
47442
|
That does not look like a Cruo token \u2014 they begin "cruo_pat_".
|
|
47043
47443
|
`);
|
|
47044
47444
|
process.exit(2);
|
|
47045
47445
|
}
|
|
47046
|
-
|
|
47047
|
-
|
|
47048
|
-
|
|
47049
|
-
|
|
47050
|
-
|
|
47446
|
+
const who = as ? null : await identify2(token);
|
|
47447
|
+
const name = as ?? (who ? nameFromEmail(who.email) : null);
|
|
47448
|
+
if (!name) {
|
|
47449
|
+
console.error(
|
|
47450
|
+
`
|
|
47451
|
+
Could not work out whose token that is, so it needs a name:
|
|
47051
47452
|
|
|
47052
|
-
|
|
47453
|
+
cruo login <token> --as jarvis
|
|
47454
|
+
|
|
47455
|
+
That happens when the board cannot be reached \u2014 a local stack, or no
|
|
47456
|
+
network. The token is fine; only the lookup failed.
|
|
47457
|
+
`
|
|
47458
|
+
);
|
|
47459
|
+
process.exit(2);
|
|
47460
|
+
}
|
|
47461
|
+
const config3 = await readConfig2();
|
|
47462
|
+
const replacing = config3.agents[name];
|
|
47463
|
+
config3.agents[name] = {
|
|
47464
|
+
token,
|
|
47465
|
+
email: who?.email,
|
|
47466
|
+
workspace: who?.workspace,
|
|
47467
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
47468
|
+
};
|
|
47469
|
+
config3.default ??= name;
|
|
47470
|
+
await writeConfig(config3);
|
|
47471
|
+
const where = who ? ` (${who.email} in ${who.workspace})` : "";
|
|
47472
|
+
console.log(
|
|
47473
|
+
`
|
|
47474
|
+
${replacing ? "Replaced" : "Saved"} the token for \`${name}\`${where}.
|
|
47475
|
+
${CONFIG_PATH} is readable only by you.
|
|
47476
|
+
|
|
47477
|
+
` + (config3.default === name ? `Run \`cruo --dry-run\` to see what it would pick up.
|
|
47478
|
+
` : `Run \`cruo --as ${name} --dry-run\` to see what it would pick up.
|
|
47479
|
+
A bare \`cruo\` still runs \`${config3.default}\`.
|
|
47480
|
+
`)
|
|
47481
|
+
);
|
|
47482
|
+
}
|
|
47483
|
+
async function logout(which, all) {
|
|
47484
|
+
const config3 = await readConfig2();
|
|
47485
|
+
const names = Object.keys(config3.agents);
|
|
47486
|
+
if (all) {
|
|
47487
|
+
await rm3(CONFIG_PATH, { force: true });
|
|
47488
|
+
console.log(
|
|
47489
|
+
names.length === 0 ? `
|
|
47490
|
+
Nothing was stored.
|
|
47491
|
+
` : `
|
|
47492
|
+
Forgot ${names.length} token(s): ${names.join(", ")}. ${CONFIG_PATH} is gone.
|
|
47493
|
+
`
|
|
47494
|
+
);
|
|
47495
|
+
return;
|
|
47496
|
+
}
|
|
47497
|
+
if (!which) {
|
|
47498
|
+
if (names.length <= 1) {
|
|
47499
|
+
await rm3(CONFIG_PATH, { force: true });
|
|
47500
|
+
console.log(
|
|
47501
|
+
names.length === 0 ? `
|
|
47502
|
+
Nothing was stored.
|
|
47503
|
+
` : `
|
|
47504
|
+
Forgot the token for \`${names[0]}\`. ${CONFIG_PATH} is gone.
|
|
47505
|
+
`
|
|
47506
|
+
);
|
|
47507
|
+
return;
|
|
47508
|
+
}
|
|
47509
|
+
console.error(
|
|
47510
|
+
`
|
|
47511
|
+
${names.length} tokens are stored: ${names.join(", ")}
|
|
47512
|
+
|
|
47513
|
+
Say which: cruo logout <name>
|
|
47514
|
+
Or all: cruo logout --all
|
|
47515
|
+
`
|
|
47516
|
+
);
|
|
47517
|
+
process.exit(2);
|
|
47518
|
+
}
|
|
47519
|
+
if (!config3.agents[which]) {
|
|
47520
|
+
console.error(`
|
|
47521
|
+
Nothing stored for \`${which}\`. Stored: ${names.join(", ") || "(none)"}
|
|
47053
47522
|
`);
|
|
47523
|
+
process.exit(2);
|
|
47524
|
+
}
|
|
47525
|
+
delete config3.agents[which];
|
|
47526
|
+
if (config3.default === which) config3.default = Object.keys(config3.agents)[0] ?? null;
|
|
47527
|
+
await writeConfig(config3);
|
|
47528
|
+
console.log(
|
|
47529
|
+
`
|
|
47530
|
+
Forgot the token for \`${which}\`.` + (config3.default ? ` A bare \`cruo\` now runs \`${config3.default}\`.
|
|
47531
|
+
` : ` Nothing is stored now.
|
|
47532
|
+
`)
|
|
47533
|
+
);
|
|
47054
47534
|
}
|
|
47055
|
-
async function
|
|
47056
|
-
|
|
47535
|
+
async function listAgents() {
|
|
47536
|
+
const config3 = await readConfig2();
|
|
47537
|
+
const names = Object.keys(config3.agents);
|
|
47538
|
+
if (names.length === 0) {
|
|
47539
|
+
console.log(`
|
|
47540
|
+
No tokens stored. \`cruo login <token>\` to add one.
|
|
47541
|
+
`);
|
|
47542
|
+
return;
|
|
47543
|
+
}
|
|
47544
|
+
console.log("");
|
|
47545
|
+
for (const name of names) {
|
|
47546
|
+
const a = config3.agents[name];
|
|
47547
|
+
const mark = config3.default === name ? "*" : " ";
|
|
47548
|
+
const who = a.email ? ` \u2014 ${a.email}${a.workspace ? ` in ${a.workspace}` : ""}` : "";
|
|
47549
|
+
console.log(` ${mark} ${name}${who}`);
|
|
47550
|
+
}
|
|
47057
47551
|
console.log(`
|
|
47058
|
-
|
|
47552
|
+
* is what a bare \`cruo\` runs. Use \`--as <name>\` for another.
|
|
47059
47553
|
`);
|
|
47060
47554
|
}
|
|
47061
47555
|
async function main2() {
|
|
47062
47556
|
const argv2 = process.argv.slice(2);
|
|
47063
47557
|
const [first] = argv2;
|
|
47558
|
+
const flagValue = (name) => {
|
|
47559
|
+
const i = argv2.indexOf(name);
|
|
47560
|
+
return i >= 0 ? argv2[i + 1] ?? null : null;
|
|
47561
|
+
};
|
|
47064
47562
|
if (first === "login") {
|
|
47065
47563
|
const token2 = argv2[1];
|
|
47066
|
-
if (!token2) {
|
|
47564
|
+
if (!token2 || token2.startsWith("--")) {
|
|
47067
47565
|
console.error(`
|
|
47068
|
-
Usage: cruo login <token>
|
|
47566
|
+
Usage: cruo login <token> [--as <name>]
|
|
47069
47567
|
`);
|
|
47070
47568
|
process.exit(2);
|
|
47071
47569
|
}
|
|
47072
|
-
return login(token2);
|
|
47570
|
+
return login(token2, flagValue("--as"));
|
|
47571
|
+
}
|
|
47572
|
+
if (first === "logout") {
|
|
47573
|
+
const named = argv2[1] && !argv2[1].startsWith("--") ? argv2[1] : null;
|
|
47574
|
+
return logout(named, argv2.includes("--all"));
|
|
47575
|
+
}
|
|
47576
|
+
if (first === "agents" || first === "whoami") return listAgents();
|
|
47577
|
+
if (first === "usage") {
|
|
47073
47578
|
}
|
|
47074
|
-
if (first === "logout") return logout();
|
|
47075
47579
|
if (first === "help" || argv2.includes("--help") || argv2.includes("-h")) {
|
|
47076
47580
|
console.log(USAGE);
|
|
47077
47581
|
return;
|
|
@@ -47084,7 +47588,22 @@ Usage: cruo login <token>
|
|
|
47084
47588
|
Prefer CRUO_TOKEN in the environment, or \`cruo login\` once.`
|
|
47085
47589
|
);
|
|
47086
47590
|
}
|
|
47087
|
-
const
|
|
47591
|
+
const asName = flagValue("--as");
|
|
47592
|
+
const config3 = await readConfig2();
|
|
47593
|
+
const stored = asName ? config3.agents[asName] : config3.default ? config3.agents[config3.default] : void 0;
|
|
47594
|
+
if (asName && !stored) {
|
|
47595
|
+
const names = Object.keys(config3.agents);
|
|
47596
|
+
console.error(
|
|
47597
|
+
`
|
|
47598
|
+
Nothing stored for \`${asName}\`.
|
|
47599
|
+
|
|
47600
|
+
` + (names.length > 0 ? `Stored: ${names.join(", ")}
|
|
47601
|
+
` : `Nothing is stored at all \u2014 \`cruo login <token>\` first.
|
|
47602
|
+
`)
|
|
47603
|
+
);
|
|
47604
|
+
process.exit(2);
|
|
47605
|
+
}
|
|
47606
|
+
const token = process.env.CRUO_TOKEN?.trim() || fromFlag || stored?.token;
|
|
47088
47607
|
if (!token) {
|
|
47089
47608
|
console.error(
|
|
47090
47609
|
`
|
|
@@ -47095,9 +47614,24 @@ Get one from Cruo \u2192 Settings \u2192 Members \u2192 Add an agent.
|
|
|
47095
47614
|
);
|
|
47096
47615
|
process.exit(2);
|
|
47097
47616
|
}
|
|
47617
|
+
const usingStored = !process.env.CRUO_TOKEN?.trim() && !fromFlag && stored;
|
|
47618
|
+
if (usingStored) {
|
|
47619
|
+
process.env.CRUO_IDENTITY = asName ?? config3.default ?? "";
|
|
47620
|
+
}
|
|
47098
47621
|
process.env.CRUO_TOKEN = token;
|
|
47099
|
-
const passthrough = stripTokenFlag(argv2);
|
|
47622
|
+
const passthrough = stripTokenFlag(argv2).filter((a, i, all) => a !== "--as" && all[i - 1] !== "--as");
|
|
47100
47623
|
process.argv = [process.argv[0], process.argv[1], ...passthrough];
|
|
47624
|
+
if (first === "usage") {
|
|
47625
|
+
const { reportUsage: reportUsage2 } = await Promise.resolve().then(() => (init_usage(), usage_exports));
|
|
47626
|
+
const days = Number(flagValue("--days") ?? 7);
|
|
47627
|
+
if (!Number.isFinite(days) || days <= 0) {
|
|
47628
|
+
console.error(`
|
|
47629
|
+
--days must be a positive number.
|
|
47630
|
+
`);
|
|
47631
|
+
process.exit(2);
|
|
47632
|
+
}
|
|
47633
|
+
return reportUsage2({ days, issue: flagValue("--issue") });
|
|
47634
|
+
}
|
|
47101
47635
|
await Promise.resolve().then(() => (init_supervisor(), supervisor_exports));
|
|
47102
47636
|
}
|
|
47103
47637
|
main2().catch((error51) => {
|