@kaiba-cloud/orbital 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/index.js +749 -425
- package/dist/mod.json +1 -1
- package/dist/orbital.js +777 -445
- package/dist/runner.js +79 -13
- package/package.json +6 -7
- package/schemas/telemetry-event.schema.json +7 -6
- package/schemas/telemetry-snapshot.schema.json +7 -6
- package/skills/orbital-plugin-builder/SKILL.md +8 -5
- package/skills/orbital-plugin-builder/references/host-api.md +10 -10
- package/skills/orbital-plugin-builder/references/manifest.md +7 -2
- package/skills/orbital-plugin-builder/references/ports.md +72 -19
- package/skills/orbital-plugin-builder/references/recipes.md +4 -4
package/dist/index.js
CHANGED
|
@@ -10421,7 +10421,7 @@ var require_compile = __commonJS({
|
|
|
10421
10421
|
const schOrFunc = root.refs[ref];
|
|
10422
10422
|
if (schOrFunc)
|
|
10423
10423
|
return schOrFunc;
|
|
10424
|
-
let _sch =
|
|
10424
|
+
let _sch = resolve26.call(this, root, ref);
|
|
10425
10425
|
if (_sch === void 0) {
|
|
10426
10426
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
10427
10427
|
const { schemaId } = this.opts;
|
|
@@ -10448,7 +10448,7 @@ var require_compile = __commonJS({
|
|
|
10448
10448
|
function sameSchemaEnv(s1, s2) {
|
|
10449
10449
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
10450
10450
|
}
|
|
10451
|
-
function
|
|
10451
|
+
function resolve26(root, ref) {
|
|
10452
10452
|
let sch;
|
|
10453
10453
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
10454
10454
|
ref = sch;
|
|
@@ -11312,7 +11312,7 @@ var require_fast_uri = __commonJS({
|
|
|
11312
11312
|
}
|
|
11313
11313
|
return uri;
|
|
11314
11314
|
}
|
|
11315
|
-
function
|
|
11315
|
+
function resolve26(baseURI, relativeURI, options) {
|
|
11316
11316
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
11317
11317
|
const {
|
|
11318
11318
|
parsed: baseParsed,
|
|
@@ -11703,7 +11703,7 @@ var require_fast_uri = __commonJS({
|
|
|
11703
11703
|
var fastUri = {
|
|
11704
11704
|
SCHEMES,
|
|
11705
11705
|
normalize,
|
|
11706
|
-
resolve:
|
|
11706
|
+
resolve: resolve26,
|
|
11707
11707
|
resolveComponent,
|
|
11708
11708
|
equal,
|
|
11709
11709
|
serialize,
|
|
@@ -14786,12 +14786,12 @@ var require_isexe = __commonJS({
|
|
|
14786
14786
|
if (typeof Promise !== "function") {
|
|
14787
14787
|
throw new TypeError("callback not provided");
|
|
14788
14788
|
}
|
|
14789
|
-
return new Promise(function(
|
|
14789
|
+
return new Promise(function(resolve26, reject) {
|
|
14790
14790
|
isexe(path3, options || {}, function(er, is) {
|
|
14791
14791
|
if (er) {
|
|
14792
14792
|
reject(er);
|
|
14793
14793
|
} else {
|
|
14794
|
-
|
|
14794
|
+
resolve26(is);
|
|
14795
14795
|
}
|
|
14796
14796
|
});
|
|
14797
14797
|
});
|
|
@@ -14857,27 +14857,27 @@ var require_which = __commonJS({
|
|
|
14857
14857
|
opt = {};
|
|
14858
14858
|
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
14859
14859
|
const found = [];
|
|
14860
|
-
const step = (i) => new Promise((
|
|
14860
|
+
const step = (i) => new Promise((resolve26, reject) => {
|
|
14861
14861
|
if (i === pathEnv.length)
|
|
14862
|
-
return opt.all && found.length ?
|
|
14862
|
+
return opt.all && found.length ? resolve26(found) : reject(getNotFoundError(cmd));
|
|
14863
14863
|
const ppRaw = pathEnv[i];
|
|
14864
14864
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
14865
14865
|
const pCmd = path3.join(pathPart, cmd);
|
|
14866
14866
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
14867
|
-
|
|
14867
|
+
resolve26(subStep(p, i, 0));
|
|
14868
14868
|
});
|
|
14869
|
-
const subStep = (p, i, ii) => new Promise((
|
|
14869
|
+
const subStep = (p, i, ii) => new Promise((resolve26, reject) => {
|
|
14870
14870
|
if (ii === pathExt.length)
|
|
14871
|
-
return
|
|
14871
|
+
return resolve26(step(i + 1));
|
|
14872
14872
|
const ext = pathExt[ii];
|
|
14873
14873
|
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
|
|
14874
14874
|
if (!er && is) {
|
|
14875
14875
|
if (opt.all)
|
|
14876
14876
|
found.push(p + ext);
|
|
14877
14877
|
else
|
|
14878
|
-
return
|
|
14878
|
+
return resolve26(p + ext);
|
|
14879
14879
|
}
|
|
14880
|
-
return
|
|
14880
|
+
return resolve26(subStep(p, i, ii + 1));
|
|
14881
14881
|
});
|
|
14882
14882
|
});
|
|
14883
14883
|
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
@@ -15190,7 +15190,7 @@ var require_cross_spawn = __commonJS({
|
|
|
15190
15190
|
});
|
|
15191
15191
|
|
|
15192
15192
|
// src/kit/command/brand.ts
|
|
15193
|
-
var VERSION = true ? "0.0.
|
|
15193
|
+
var VERSION = true ? "0.0.6" : "0.0.0-dev";
|
|
15194
15194
|
function defineBrand(overrides) {
|
|
15195
15195
|
const { program } = overrides;
|
|
15196
15196
|
return {
|
|
@@ -20652,7 +20652,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
20652
20652
|
var NONE = 0;
|
|
20653
20653
|
var ASSUMED = 1;
|
|
20654
20654
|
var PROVEN = 2;
|
|
20655
|
-
function isRecursive(inst, stack,
|
|
20655
|
+
function isRecursive(inst, stack, resolve26) {
|
|
20656
20656
|
const cached2 = recursive.get(inst);
|
|
20657
20657
|
if (cached2 !== void 0)
|
|
20658
20658
|
return cached2 ? PROVEN : NONE;
|
|
@@ -20662,7 +20662,7 @@ function isRecursive(inst, stack, resolve25) {
|
|
|
20662
20662
|
let result2 = NONE;
|
|
20663
20663
|
const check3 = (child) => {
|
|
20664
20664
|
if (result2 !== PROVEN && child?._zod) {
|
|
20665
|
-
const answer2 = isRecursive(child, stack,
|
|
20665
|
+
const answer2 = isRecursive(child, stack, resolve26);
|
|
20666
20666
|
if (answer2 > result2)
|
|
20667
20667
|
result2 = answer2;
|
|
20668
20668
|
}
|
|
@@ -20673,7 +20673,7 @@ function isRecursive(inst, stack, resolve25) {
|
|
|
20673
20673
|
const desc = Object.getOwnPropertyDescriptor(sh, key2);
|
|
20674
20674
|
if (spread && !desc.enumerable)
|
|
20675
20675
|
continue;
|
|
20676
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
20676
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve26) : NONE;
|
|
20677
20677
|
if (child > answer2)
|
|
20678
20678
|
answer2 = child;
|
|
20679
20679
|
}
|
|
@@ -20737,7 +20737,7 @@ function isRecursive(inst, stack, resolve25) {
|
|
|
20737
20737
|
break;
|
|
20738
20738
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
20739
20739
|
case "lazy": {
|
|
20740
|
-
const inner = def._cachedInner ?? (
|
|
20740
|
+
const inner = def._cachedInner ?? (resolve26 ? inst._zod.innerType : void 0);
|
|
20741
20741
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
20742
20742
|
break;
|
|
20743
20743
|
}
|
|
@@ -35891,7 +35891,7 @@ var MissionAskedSchema = external_exports.object({
|
|
|
35891
35891
|
question: external_exports.string().min(1),
|
|
35892
35892
|
at: external_exports.string()
|
|
35893
35893
|
});
|
|
35894
|
-
function
|
|
35894
|
+
function waitsOnDecision(mission) {
|
|
35895
35895
|
return mission.state === "waiting" || mission.state === "active" && mission.questions.length > 0;
|
|
35896
35896
|
}
|
|
35897
35897
|
var MISSION_QUESTION_META = "orbital/question";
|
|
@@ -35929,7 +35929,7 @@ var MissionStateFileSchema = external_exports.object({
|
|
|
35929
35929
|
source: external_exports.string().nullable(),
|
|
35930
35930
|
result: external_exports.string().nullable(),
|
|
35931
35931
|
question: external_exports.string().nullable(),
|
|
35932
|
-
|
|
35932
|
+
receivers: external_exports.array(external_exports.string().min(1)).default([]),
|
|
35933
35933
|
lead: external_exports.string().nullable(),
|
|
35934
35934
|
claim: external_exports.string().nullable(),
|
|
35935
35935
|
claimedAt: external_exports.string().nullable(),
|
|
@@ -35956,7 +35956,7 @@ var MissionRecordSchema = external_exports.object({
|
|
|
35956
35956
|
result: external_exports.string().nullable(),
|
|
35957
35957
|
question: external_exports.string().nullable(),
|
|
35958
35958
|
questions: external_exports.array(MissionAskedSchema).default([]),
|
|
35959
|
-
|
|
35959
|
+
receivers: external_exports.array(external_exports.string().min(1)).default([]),
|
|
35960
35960
|
lead: external_exports.string().nullable(),
|
|
35961
35961
|
lastActivity: external_exports.string().nullable().default(null),
|
|
35962
35962
|
claim: external_exports.string().nullable().default(null),
|
|
@@ -35998,9 +35998,9 @@ var MissionReplySchema = external_exports.object({
|
|
|
35998
35998
|
text: external_exports.string().min(1),
|
|
35999
35999
|
questionId: external_exports.string().min(1).optional()
|
|
36000
36000
|
});
|
|
36001
|
-
var
|
|
36001
|
+
var RemoteReceiverSchema = external_exports.object({ node: external_exports.string().min(1), sessionId: external_exports.string().min(1) });
|
|
36002
36002
|
var SENDER_FIELDS = { team: external_exports.string().min(1).nullable().default(null), agent: external_exports.string().min(1).nullable().default(null) };
|
|
36003
|
-
var RelayJoinSchema = external_exports.object({ missionId: MissionIdSchema,
|
|
36003
|
+
var RelayJoinSchema = external_exports.object({ missionId: MissionIdSchema, receiver: RemoteReceiverSchema, ...SENDER_FIELDS });
|
|
36004
36004
|
var RelayReplySchema = external_exports.object({ missionId: MissionIdSchema, text: external_exports.string().min(1), questionId: external_exports.string().min(1).optional(), by: external_exports.string().min(1), ...SENDER_FIELDS });
|
|
36005
36005
|
var MissionControlSchema = external_exports.object({ missionId: MissionIdSchema });
|
|
36006
36006
|
var STEER_MARK = "[orbital] message during your turn";
|
|
@@ -36034,18 +36034,27 @@ var MissionQuestionSchema = external_exports.object({
|
|
|
36034
36034
|
leadSessionId: external_exports.string().min(1),
|
|
36035
36035
|
question: external_exports.string(),
|
|
36036
36036
|
questionId: external_exports.string().min(1).optional(),
|
|
36037
|
-
|
|
36037
|
+
receiver: RemoteReceiverSchema,
|
|
36038
36038
|
team: external_exports.string().min(1).nullable().default(null)
|
|
36039
36039
|
});
|
|
36040
|
-
|
|
36041
|
-
|
|
36040
|
+
var EXTERNAL_RECEIVER = "external:";
|
|
36041
|
+
function externalReceiver(externalId) {
|
|
36042
|
+
return `${EXTERNAL_RECEIVER}${externalId}`;
|
|
36042
36043
|
}
|
|
36043
|
-
function
|
|
36044
|
-
|
|
36045
|
-
|
|
36044
|
+
function externalIdOf(receiver) {
|
|
36045
|
+
return receiver.startsWith(EXTERNAL_RECEIVER) ? receiver.slice(EXTERNAL_RECEIVER.length) : null;
|
|
36046
|
+
}
|
|
36047
|
+
function receiverRef(receiver) {
|
|
36048
|
+
return `${receiver.sessionId}@${receiver.node}`;
|
|
36049
|
+
}
|
|
36050
|
+
function remoteReceiverOf(receiver) {
|
|
36051
|
+
if (externalIdOf(receiver) !== null) return null;
|
|
36052
|
+
const at = receiver.lastIndexOf("@");
|
|
36053
|
+
return at > 0 && at < receiver.length - 1 ? { sessionId: receiver.slice(0, at), node: receiver.slice(at + 1) } : null;
|
|
36046
36054
|
}
|
|
36047
36055
|
var MissionJoinSchema = external_exports.object({
|
|
36048
|
-
missionId: MissionIdSchema
|
|
36056
|
+
missionId: MissionIdSchema,
|
|
36057
|
+
externalId: external_exports.string().min(1).optional()
|
|
36049
36058
|
});
|
|
36050
36059
|
|
|
36051
36060
|
// src/protocol/charter.ts
|
|
@@ -36183,7 +36192,7 @@ var MissionStateEventSchema = external_exports.object({
|
|
|
36183
36192
|
transition: MissionTransitionSchema,
|
|
36184
36193
|
reason: external_exports.string().nullable(),
|
|
36185
36194
|
by: external_exports.string(),
|
|
36186
|
-
|
|
36195
|
+
receivers: external_exports.array(external_exports.string()).default([]),
|
|
36187
36196
|
sessionId: external_exports.string().nullable(),
|
|
36188
36197
|
lead: external_exports.string().nullable(),
|
|
36189
36198
|
question: external_exports.string().nullable(),
|
|
@@ -36237,7 +36246,7 @@ var TelemetrySnapshotSchema = external_exports.object({
|
|
|
36237
36246
|
state: MissionStateSchema,
|
|
36238
36247
|
sessionId: external_exports.string().nullable(),
|
|
36239
36248
|
lead: external_exports.string().nullable(),
|
|
36240
|
-
|
|
36249
|
+
receivers: external_exports.array(external_exports.string()).default([]),
|
|
36241
36250
|
givenBy: external_exports.string().nullable(),
|
|
36242
36251
|
question: external_exports.string().nullable(),
|
|
36243
36252
|
questions: external_exports.array(MissionAskedSchema).default([]),
|
|
@@ -36831,6 +36840,9 @@ var NodeClient = class {
|
|
|
36831
36840
|
async joinMission(missionId) {
|
|
36832
36841
|
return MissionRecordSchema.parse(await this.request("/orbital/missions/join", { method: "POST", body: { missionId } }));
|
|
36833
36842
|
}
|
|
36843
|
+
async leaveMission(missionId) {
|
|
36844
|
+
return MissionRecordSchema.parse(await this.request("/orbital/missions/leave", { method: "POST", body: { missionId } }));
|
|
36845
|
+
}
|
|
36834
36846
|
async handoff(handoff2) {
|
|
36835
36847
|
return MissionRecordSchema.parse(await this.request("/orbital/missions/handoff", { method: "POST", body: handoff2 }));
|
|
36836
36848
|
}
|
|
@@ -37472,6 +37484,13 @@ var DeliverySchema = external_exports.object({
|
|
|
37472
37484
|
question: external_exports.string().nullable().default(null),
|
|
37473
37485
|
text: external_exports.string()
|
|
37474
37486
|
});
|
|
37487
|
+
var AnsweredSchema = external_exports.object({
|
|
37488
|
+
kind: external_exports.literal("answered"),
|
|
37489
|
+
mission: external_exports.string().min(1),
|
|
37490
|
+
question: external_exports.string().nullable(),
|
|
37491
|
+
by: external_exports.string().min(1),
|
|
37492
|
+
text: external_exports.string()
|
|
37493
|
+
});
|
|
37475
37494
|
var SettleSchema = external_exports.object({
|
|
37476
37495
|
taskId: external_exports.string().min(1).optional(),
|
|
37477
37496
|
text: external_exports.string()
|
|
@@ -37717,6 +37736,60 @@ var PORT_METHODS = {
|
|
|
37717
37736
|
reasoner: ["extractModule", "classify", "align", "checkConsistency"],
|
|
37718
37737
|
missions: ["join", "reply", "pause", "resume", "ask", "say"]
|
|
37719
37738
|
};
|
|
37739
|
+
var PLUGIN_COMMAND_RESERVED_OPTIONS = ["output", "quiet", "non-interactive", "confirm", "timeout", "help", "version", "workspace", "node"];
|
|
37740
|
+
var PluginCommandOptionSchema = external_exports.object({
|
|
37741
|
+
name: external_exports.string().regex(NAME_SHAPE).refine((name) => !PLUGIN_COMMAND_RESERVED_OPTIONS.includes(name), { error: `an option cannot take a name orbital gives every command: ${PLUGIN_COMMAND_RESERVED_OPTIONS.join(", ")}` }),
|
|
37742
|
+
type: external_exports.enum(["string", "boolean", "integer", "enum", "path"]),
|
|
37743
|
+
summary: external_exports.string().min(1),
|
|
37744
|
+
values: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
37745
|
+
required: external_exports.boolean().default(false),
|
|
37746
|
+
default: external_exports.union([external_exports.string(), external_exports.number().int(), external_exports.boolean()]).optional()
|
|
37747
|
+
}).strict().superRefine((option, issues2) => {
|
|
37748
|
+
if (option.type === "enum" !== (option.values !== void 0)) issues2.addIssue({ code: "custom", message: `the option ${option.name} names values only when its type is enum` });
|
|
37749
|
+
if (option.type === "boolean" && option.required) issues2.addIssue({ code: "custom", message: `the boolean option ${option.name} cannot be required` });
|
|
37750
|
+
if (option.default === void 0) return;
|
|
37751
|
+
const fits = option.type === "boolean" ? typeof option.default === "boolean" : option.type === "integer" ? typeof option.default === "number" : typeof option.default === "string" && (option.values?.includes(option.default) ?? true);
|
|
37752
|
+
if (!fits) issues2.addIssue({ code: "custom", message: `the default of the option ${option.name} is not a ${option.type === "enum" ? `value of ${option.values.join(", ")}` : option.type}` });
|
|
37753
|
+
});
|
|
37754
|
+
var PluginCommandArgSchema = external_exports.object({
|
|
37755
|
+
name: external_exports.string().regex(NAME_SHAPE),
|
|
37756
|
+
summary: external_exports.string().min(1),
|
|
37757
|
+
required: external_exports.boolean().default(false),
|
|
37758
|
+
variadic: external_exports.boolean().default(false)
|
|
37759
|
+
}).strict();
|
|
37760
|
+
var PluginCommandSchema = external_exports.object({
|
|
37761
|
+
name: external_exports.string().regex(NAME_SHAPE),
|
|
37762
|
+
summary: external_exports.string().min(1),
|
|
37763
|
+
args: external_exports.array(PluginCommandArgSchema).default([]),
|
|
37764
|
+
options: external_exports.array(PluginCommandOptionSchema).default([]),
|
|
37765
|
+
interactive: external_exports.boolean().default(false),
|
|
37766
|
+
node: external_exports.boolean().default(false)
|
|
37767
|
+
}).strict().superRefine((command, issues2) => {
|
|
37768
|
+
const twice = (names) => names.find((name, index) => names.indexOf(name) !== index);
|
|
37769
|
+
const arg = twice(command.args.map((one) => one.name));
|
|
37770
|
+
if (arg) issues2.addIssue({ code: "custom", message: `the command ${command.name} names the argument ${arg} twice` });
|
|
37771
|
+
const option = twice(command.options.map((one) => one.name));
|
|
37772
|
+
if (option) issues2.addIssue({ code: "custom", message: `the command ${command.name} names the option ${option} twice` });
|
|
37773
|
+
if (command.args.slice(0, -1).some((one) => one.variadic)) issues2.addIssue({ code: "custom", message: `only the last argument of the command ${command.name} can be variadic` });
|
|
37774
|
+
if (command.args.some((one, index) => one.required && command.args.slice(0, index).some((before) => !before.required))) issues2.addIssue({ code: "custom", message: `a required argument of the command ${command.name} cannot come after an optional one` });
|
|
37775
|
+
if (command.interactive && command.node) issues2.addIssue({ code: "custom", message: `the command ${command.name} cannot be interactive and need the node: the node has no terminal to give it` });
|
|
37776
|
+
});
|
|
37777
|
+
var PluginCommandsSchema = external_exports.array(PluginCommandSchema).default([]).superRefine((commands, issues2) => {
|
|
37778
|
+
const names = commands.map((one) => one.name);
|
|
37779
|
+
const twice = names.find((name, index) => names.indexOf(name) !== index);
|
|
37780
|
+
if (twice) issues2.addIssue({ code: "custom", message: `the command ${twice} is declared twice` });
|
|
37781
|
+
});
|
|
37782
|
+
var PluginCommandRequestSchema = external_exports.object({
|
|
37783
|
+
name: external_exports.string().regex(NAME_SHAPE),
|
|
37784
|
+
args: external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.array(external_exports.string())])).default({}),
|
|
37785
|
+
options: external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])).default({}),
|
|
37786
|
+
terminal: external_exports.object({ tty: external_exports.boolean() }).nullable().default(null)
|
|
37787
|
+
}).strict();
|
|
37788
|
+
var PluginCommandResultSchema = external_exports.object({
|
|
37789
|
+
exitCode: external_exports.union([external_exports.literal(0), external_exports.literal(1), external_exports.literal(2), external_exports.literal(3), external_exports.literal(5)]).default(0),
|
|
37790
|
+
text: external_exports.string().nullable().default(null),
|
|
37791
|
+
data: external_exports.record(external_exports.string(), external_exports.unknown()).default({})
|
|
37792
|
+
}).strict();
|
|
37720
37793
|
var PluginManifestSchema = external_exports.object({
|
|
37721
37794
|
name: external_exports.string().regex(NAME_SHAPE),
|
|
37722
37795
|
version: external_exports.string().min(1),
|
|
@@ -37746,7 +37819,7 @@ var PluginManifestSchema = external_exports.object({
|
|
|
37746
37819
|
secrets: external_exports.array(external_exports.string().min(1)).default([]),
|
|
37747
37820
|
models: external_exports.array(external_exports.string().regex(/^[a-z0-9-]+:.+$/, "a model pattern is <provider>:<model>, such as anthropic:*")).default([])
|
|
37748
37821
|
}).prefault({}),
|
|
37749
|
-
commands:
|
|
37822
|
+
commands: PluginCommandsSchema,
|
|
37750
37823
|
panels: external_exports.array(external_exports.object({ id: external_exports.string().min(1), title: external_exports.string(), kind: external_exports.enum(["list", "tree", "transcript", "form"]) })).default([])
|
|
37751
37824
|
}).strict();
|
|
37752
37825
|
var PluginInitSchema = external_exports.object({
|
|
@@ -37769,7 +37842,8 @@ var PluginPackageSchema = external_exports.object({
|
|
|
37769
37842
|
dir: external_exports.string().min(1).optional(),
|
|
37770
37843
|
env: external_exports.array(external_exports.string().regex(PLUGIN_ENV_NAME, "is not the name of an environment variable")).default([]),
|
|
37771
37844
|
config: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
|
|
37772
|
-
harnesses: external_exports.array(HarnessAdapterDeclarationSchema).default([])
|
|
37845
|
+
harnesses: external_exports.array(HarnessAdapterDeclarationSchema).default([]),
|
|
37846
|
+
commands: PluginCommandsSchema
|
|
37773
37847
|
}).strict();
|
|
37774
37848
|
var PLUGIN_PACKAGE_FILE = "orbital-plugin.json";
|
|
37775
37849
|
var MarketplaceSchema = external_exports.object({
|
|
@@ -37815,12 +37889,12 @@ var RpcPeer = class {
|
|
|
37815
37889
|
request(method, params, timeoutMs = this.timeoutMs) {
|
|
37816
37890
|
if (this.closedWith) return Promise.reject(this.closedWith);
|
|
37817
37891
|
const id = this.nextId++;
|
|
37818
|
-
return new Promise((
|
|
37892
|
+
return new Promise((resolve26, reject) => {
|
|
37819
37893
|
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
37820
37894
|
this.pending.delete(id);
|
|
37821
37895
|
reject(new RpcError(RPC_ERRORS.internal, `${method} got no answer in ${timeoutMs} ms`));
|
|
37822
37896
|
}, timeoutMs) : null;
|
|
37823
|
-
this.pending.set(id, { resolve:
|
|
37897
|
+
this.pending.set(id, { resolve: resolve26, reject, timer });
|
|
37824
37898
|
this.write({ jsonrpc: "2.0", id, method, ...params !== void 0 && { params } });
|
|
37825
37899
|
});
|
|
37826
37900
|
}
|
|
@@ -38878,12 +38952,9 @@ function readDescriptorName(dir) {
|
|
|
38878
38952
|
if (!existsSync6(file2)) throw refuse("invalid-input", `${dir} has no ${PLUGIN_PACKAGE_FILE}, so it is not a plugin.`);
|
|
38879
38953
|
return PluginPackageSchema.parse(JSON.parse(readFileSync7(file2, "utf8"))).name;
|
|
38880
38954
|
}
|
|
38881
|
-
function lastPart(value2) {
|
|
38882
|
-
return value2.replace(/\.git$/, "").split(/[/:]/).filter(Boolean).pop() ?? value2;
|
|
38883
|
-
}
|
|
38884
38955
|
function resolvePluginRef(home, ref) {
|
|
38885
|
-
if (ref.startsWith("npm:")) return { kind: "npm", name:
|
|
38886
|
-
if (ref.startsWith("git:")) return { kind: "git", name:
|
|
38956
|
+
if (ref.startsWith("npm:")) return { kind: "npm", name: null, pkg: ref.slice(4) };
|
|
38957
|
+
if (ref.startsWith("git:")) return { kind: "git", name: null, url: ref.slice(4) };
|
|
38887
38958
|
if (isFolder(ref)) return { kind: "folder", name: readDescriptorName(resolve6(ref)), dir: resolve6(ref) };
|
|
38888
38959
|
const at = ref.lastIndexOf("@");
|
|
38889
38960
|
if (at <= 0) throw refuse("invalid-input", `"${ref}" is not a plugin reference.`, "Use <name>@<marketplace>, npm:<package>, git:<url>, or a folder such as ./plugins/mine.");
|
|
@@ -38915,17 +38986,17 @@ function installPlugin(home, ref, exec2 = run) {
|
|
|
38915
38986
|
const installed = Object.keys(JSON.parse(readFileSync7(join10(scratch, "package.json"), "utf8")).dependencies ?? {});
|
|
38916
38987
|
const pkgDir = join10("node_modules", ...(installed[0] ?? ref.pkg.replace(/(.)@[^/]*$/, "$1")).split("/"));
|
|
38917
38988
|
const inner = PluginPackageSchema.parse(JSON.parse(readFileSync7(join10(scratch, pkgDir, PLUGIN_PACKAGE_FILE), "utf8")));
|
|
38918
|
-
writeFileSync5(join10(scratch, PLUGIN_PACKAGE_FILE), `${JSON.stringify({ name:
|
|
38989
|
+
writeFileSync5(join10(scratch, PLUGIN_PACKAGE_FILE), `${JSON.stringify({ name: inner.name, command: inner.command, dir: inner.dir ? join10(pkgDir, inner.dir) : pkgDir, env: inner.env, ...Object.keys(inner.config).length > 0 && { config: inner.config }, ...inner.harnesses.length > 0 && { harnesses: inner.harnesses }, ...inner.commands.length > 0 && { commands: inner.commands } }, null, 2)}
|
|
38919
38990
|
`);
|
|
38920
38991
|
break;
|
|
38921
38992
|
}
|
|
38922
38993
|
}
|
|
38923
38994
|
const named2 = readDescriptorName(scratch);
|
|
38924
|
-
if (named2 !== ref.name) throw refuse("invalid-input", `The plugin calls itself ${named2}, and its reference names it ${ref.name}.`);
|
|
38925
|
-
const dir = join10(parent,
|
|
38995
|
+
if (ref.name !== null && named2 !== ref.name) throw refuse("invalid-input", `The plugin calls itself ${named2}, and its reference names it ${ref.name}.`);
|
|
38996
|
+
const dir = join10(parent, named2);
|
|
38926
38997
|
rmSync3(dir, { recursive: true, force: true });
|
|
38927
38998
|
renameSync(scratch, dir);
|
|
38928
|
-
return dir;
|
|
38999
|
+
return { name: named2, dir };
|
|
38929
39000
|
} finally {
|
|
38930
39001
|
rmSync3(scratch, { recursive: true, force: true });
|
|
38931
39002
|
}
|
|
@@ -38972,8 +39043,8 @@ function isPackageSource(source) {
|
|
|
38972
39043
|
function ensurePluginInstalled(home, name, source, exec2 = run) {
|
|
38973
39044
|
const dir = join10(pluginsDir(home), name);
|
|
38974
39045
|
if (existsSync6(join10(dir, PLUGIN_PACKAGE_FILE))) return dir;
|
|
38975
|
-
if (source.startsWith("npm:")) return installPlugin(home, { kind: "npm", name, pkg: source.slice(4) }, exec2);
|
|
38976
|
-
if (source.startsWith("git:")) return installPlugin(home, { kind: "git", name, url: source.slice(4) }, exec2);
|
|
39046
|
+
if (source.startsWith("npm:")) return installPlugin(home, { kind: "npm", name, pkg: source.slice(4) }, exec2).dir;
|
|
39047
|
+
if (source.startsWith("git:")) return installPlugin(home, { kind: "git", name, url: source.slice(4) }, exec2).dir;
|
|
38977
39048
|
throw refuse("invalid-input", `The plugin ${name} has the source ${source}, and only an npm: or git: source installs into the plugin cache.`);
|
|
38978
39049
|
}
|
|
38979
39050
|
|
|
@@ -38981,15 +39052,22 @@ function ensurePluginInstalled(home, name, source, exec2 = run) {
|
|
|
38981
39052
|
function pluginsDir(home) {
|
|
38982
39053
|
return join11(home, "plugins");
|
|
38983
39054
|
}
|
|
39055
|
+
function isLocalSource(source) {
|
|
39056
|
+
return source.startsWith(".") || isAbsolute5(source);
|
|
39057
|
+
}
|
|
39058
|
+
function pluginDir(name, source, roots) {
|
|
39059
|
+
if (!isLocalSource(source)) return join11(pluginsDir(roots.home), name);
|
|
39060
|
+
return isAbsolute5(source) ? source : resolve7(roots.harnessRoot ?? process.cwd(), source);
|
|
39061
|
+
}
|
|
38984
39062
|
function commandOf(name, source, roots, entryEnv = []) {
|
|
38985
39063
|
if (source.startsWith("exec:")) {
|
|
38986
39064
|
const [command2, ...args2] = source.slice("exec:".length).trim().split(/\s+/);
|
|
38987
39065
|
if (!command2) throw new Error(`The plugin ${name}'s source "${source}" names no command.`);
|
|
38988
|
-
return { command: command2, args: args2, cwd: roots.harnessRoot ?? process.cwd(), env: [...entryEnv], defaults: {}, harnesses: null };
|
|
39066
|
+
return { command: command2, args: args2, cwd: roots.harnessRoot ?? process.cwd(), env: [...entryEnv], defaults: {}, harnesses: null, commands: null };
|
|
38989
39067
|
}
|
|
38990
39068
|
if (entryEnv.length > 0) throw new Error(`The plugin ${name}'s ${ORBITAL_FILE} entry names env:, and only an exec: source does: its ${PLUGIN_PACKAGE_FILE} declares the variables it reads.`);
|
|
38991
|
-
const local =
|
|
38992
|
-
const dir =
|
|
39069
|
+
const local = isLocalSource(source);
|
|
39070
|
+
const dir = pluginDir(name, source, roots);
|
|
38993
39071
|
const descriptor = join11(dir, PLUGIN_PACKAGE_FILE);
|
|
38994
39072
|
if (!local && !existsSync7(descriptor) && /^(npm|git):/.test(source)) ensurePluginInstalled(roots.home, name, source);
|
|
38995
39073
|
if (!existsSync7(descriptor)) {
|
|
@@ -38997,7 +39075,7 @@ function commandOf(name, source, roots, entryEnv = []) {
|
|
|
38997
39075
|
}
|
|
38998
39076
|
const plugin = PluginPackageSchema.parse(JSON.parse(readFileSync8(descriptor, "utf8")));
|
|
38999
39077
|
const [command, ...args] = plugin.command;
|
|
39000
|
-
return { command: command === "node" ? process.execPath : command, args, cwd: plugin.dir ? resolve7(dir, plugin.dir) : dir, env: plugin.env, defaults: plugin.config, harnesses: plugin.harnesses };
|
|
39078
|
+
return { command: command === "node" ? process.execPath : command, args, cwd: plugin.dir ? resolve7(dir, plugin.dir) : dir, env: plugin.env, defaults: plugin.config, harnesses: plugin.harnesses, commands: plugin.commands };
|
|
39001
39079
|
}
|
|
39002
39080
|
var PLUGIN_ENV = [
|
|
39003
39081
|
"PATH",
|
|
@@ -39042,6 +39120,15 @@ function folderMismatch(manifest, folders) {
|
|
|
39042
39120
|
const extra = folders.find((one) => !manifest.provides.harnesses.some((harness) => harness.id === one.id && harness.folder !== null));
|
|
39043
39121
|
return extra ? `declares the harness ${extra.id} in its ${PLUGIN_PACKAGE_FILE}, and its harnesses port serves no folder for it` : null;
|
|
39044
39122
|
}
|
|
39123
|
+
function commandMismatch(manifest, declared) {
|
|
39124
|
+
for (const command of manifest.commands) {
|
|
39125
|
+
const same2 = declared.find((one) => one.name === command.name);
|
|
39126
|
+
if (!same2) return `serves the command ${command.name}, and its ${PLUGIN_PACKAGE_FILE} declares no command ${command.name}`;
|
|
39127
|
+
if (JSON.stringify(same2) !== JSON.stringify(command)) return `declares the command ${command.name} one way in its ${PLUGIN_PACKAGE_FILE} and another way in its code`;
|
|
39128
|
+
}
|
|
39129
|
+
const extra = declared.find((one) => !manifest.commands.some((command) => command.name === one.name));
|
|
39130
|
+
return extra ? `declares the command ${extra.name} in its ${PLUGIN_PACKAGE_FILE}, and serves no command ${extra.name}` : null;
|
|
39131
|
+
}
|
|
39045
39132
|
function manifestEnv(manifest) {
|
|
39046
39133
|
return manifest.permissions.secrets.filter((name) => PLUGIN_ENV_NAME.test(name));
|
|
39047
39134
|
}
|
|
@@ -39070,15 +39157,16 @@ async function startRemotePlugin(options) {
|
|
|
39070
39157
|
let reconnecting = false;
|
|
39071
39158
|
const connect = async () => {
|
|
39072
39159
|
const start = commandOf(options.name, options.source, options.roots, options.entryEnv);
|
|
39073
|
-
const
|
|
39160
|
+
const terminal = options.terminal ? [options.terminal.input, options.terminal.output, options.terminal.error] : [];
|
|
39161
|
+
const child = spawn2(start.command, start.args, { cwd: start.cwd, env: pluginProcessEnv(options.env, start.env), stdio: ["pipe", "pipe", "pipe", ...terminal] });
|
|
39074
39162
|
try {
|
|
39075
|
-
return await initialize(child, start.env, start.defaults, start.harnesses);
|
|
39163
|
+
return await initialize(child, start.env, start.defaults, start.harnesses, start.commands);
|
|
39076
39164
|
} catch (error62) {
|
|
39077
39165
|
child.kill("SIGKILL");
|
|
39078
39166
|
throw error62;
|
|
39079
39167
|
}
|
|
39080
39168
|
};
|
|
39081
|
-
const initialize = async (child, env, defaults, folders
|
|
39169
|
+
const initialize = async (child, env, defaults, folders, commands) => {
|
|
39082
39170
|
const peer = new RpcPeer({ send: (line) => child.stdin?.write(`${line}
|
|
39083
39171
|
`) });
|
|
39084
39172
|
createInterface2({ input: child.stdout }).on("line", (line) => peer.receive(line));
|
|
@@ -39113,12 +39201,16 @@ async function startRemotePlugin(options) {
|
|
|
39113
39201
|
const where2 = options.source.startsWith("exec:") ? `its ${ORBITAL_FILE} entry` : `its ${PLUGIN_PACKAGE_FILE}`;
|
|
39114
39202
|
throw new Error(`The plugin ${options.name} declares the variables [${declared.join(", ")}] as secrets, and ${where2} declares env: [${env.join(", ")}]. Make them the same.`);
|
|
39115
39203
|
}
|
|
39204
|
+
if (commands) {
|
|
39205
|
+
const mismatch = commandMismatch(manifest2, commands);
|
|
39206
|
+
if (mismatch) throw new Error(`The plugin ${options.name} ${mismatch}. The commands of its ${PLUGIN_PACKAGE_FILE} and the commands of its code must be the same.`);
|
|
39207
|
+
}
|
|
39116
39208
|
if (options.offline && manifest2.permissions.network.length > 0) {
|
|
39117
39209
|
throw new Error(`The plugin ${options.name} reaches ${manifest2.permissions.network.join(", ")}, and ${ORBITAL_FILE} says offline: true.`);
|
|
39118
39210
|
}
|
|
39119
39211
|
await options.policy.check({ kind: "plugin.load", plugin: options.name, network: manifest2.permissions.network, secrets: manifest2.permissions.secrets, models: manifest2.permissions.models });
|
|
39120
|
-
child.once("exit", () => {
|
|
39121
|
-
peer.close(new Error(`The plugin ${options.name} stopped.`));
|
|
39212
|
+
child.once("exit", (code, signal) => {
|
|
39213
|
+
peer.close(new Error(`The plugin ${options.name} stopped (${signal ? `signal ${signal}` : `exit code ${code}`}).`));
|
|
39122
39214
|
if (stopped || reconnecting) return;
|
|
39123
39215
|
current2 = reconnect();
|
|
39124
39216
|
current2.catch((error62) => options.log("error", error62 instanceof Error ? error62.message : String(error62)));
|
|
@@ -39298,7 +39390,7 @@ async function startRemotePlugin(options) {
|
|
|
39298
39390
|
endpoint = given;
|
|
39299
39391
|
void current2.then((connection) => connection.peer.notify(PLUGIN_METHODS.hostEndpoint, given));
|
|
39300
39392
|
},
|
|
39301
|
-
command: async (
|
|
39393
|
+
command: async (request) => PluginCommandResultSchema.parse(await (await current2).peer.request(PLUGIN_METHODS.commandRun, request, 0)),
|
|
39302
39394
|
close: async () => {
|
|
39303
39395
|
stopped = true;
|
|
39304
39396
|
const connection = await current2.catch(() => null);
|
|
@@ -40653,7 +40745,7 @@ var readBodyDirect = (request) => {
|
|
|
40653
40745
|
request[bodyBufferKey] = buffered;
|
|
40654
40746
|
return Promise.resolve(buffered);
|
|
40655
40747
|
}
|
|
40656
|
-
const promise2 = new Promise((
|
|
40748
|
+
const promise2 = new Promise((resolve26, reject) => {
|
|
40657
40749
|
const chunks = [];
|
|
40658
40750
|
let settled = false;
|
|
40659
40751
|
const finish = (callback) => {
|
|
@@ -40671,7 +40763,7 @@ var readBodyDirect = (request) => {
|
|
|
40671
40763
|
else if (recovered === void 0) reject(error62 ?? normalizeAbortError(request, incoming));
|
|
40672
40764
|
else {
|
|
40673
40765
|
request[bodyBufferKey] = recovered;
|
|
40674
|
-
|
|
40766
|
+
resolve26(recovered);
|
|
40675
40767
|
}
|
|
40676
40768
|
});
|
|
40677
40769
|
return true;
|
|
@@ -40683,7 +40775,7 @@ var readBodyDirect = (request) => {
|
|
|
40683
40775
|
finish(() => {
|
|
40684
40776
|
const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
|
|
40685
40777
|
request[bodyBufferKey] = buffer;
|
|
40686
|
-
|
|
40778
|
+
resolve26(buffer);
|
|
40687
40779
|
});
|
|
40688
40780
|
};
|
|
40689
40781
|
const onError = (error62) => {
|
|
@@ -41230,7 +41322,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
41230
41322
|
});
|
|
41231
41323
|
if (!chunk) {
|
|
41232
41324
|
if (i === 1) {
|
|
41233
|
-
await new Promise((
|
|
41325
|
+
await new Promise((resolve26) => setTimeout(resolve26));
|
|
41234
41326
|
maxReadCount = 3;
|
|
41235
41327
|
continue;
|
|
41236
41328
|
}
|
|
@@ -43499,9 +43591,9 @@ data:
|
|
|
43499
43591
|
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
43500
43592
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
43501
43593
|
if (this._enableJsonResponse) {
|
|
43502
|
-
return new Promise((
|
|
43594
|
+
return new Promise((resolve26) => {
|
|
43503
43595
|
this._streamMapping.set(streamId, {
|
|
43504
|
-
resolveJson:
|
|
43596
|
+
resolveJson: resolve26,
|
|
43505
43597
|
cleanup: () => {
|
|
43506
43598
|
this._streamMapping.delete(streamId);
|
|
43507
43599
|
}
|
|
@@ -49864,7 +49956,7 @@ var Protocol = class {
|
|
|
49864
49956
|
return;
|
|
49865
49957
|
}
|
|
49866
49958
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
49867
|
-
await new Promise((
|
|
49959
|
+
await new Promise((resolve26) => setTimeout(resolve26, pollInterval));
|
|
49868
49960
|
options?.signal?.throwIfAborted();
|
|
49869
49961
|
}
|
|
49870
49962
|
} catch (error62) {
|
|
@@ -49881,7 +49973,7 @@ var Protocol = class {
|
|
|
49881
49973
|
*/
|
|
49882
49974
|
request(request, resultSchema, options) {
|
|
49883
49975
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
49884
|
-
return new Promise((
|
|
49976
|
+
return new Promise((resolve26, reject) => {
|
|
49885
49977
|
const earlyReject = (error62) => {
|
|
49886
49978
|
reject(error62);
|
|
49887
49979
|
};
|
|
@@ -49959,7 +50051,7 @@ var Protocol = class {
|
|
|
49959
50051
|
if (!parseResult.success) {
|
|
49960
50052
|
reject(parseResult.error);
|
|
49961
50053
|
} else {
|
|
49962
|
-
|
|
50054
|
+
resolve26(parseResult.data);
|
|
49963
50055
|
}
|
|
49964
50056
|
} catch (error62) {
|
|
49965
50057
|
reject(error62);
|
|
@@ -50221,12 +50313,12 @@ var Protocol = class {
|
|
|
50221
50313
|
}
|
|
50222
50314
|
} catch {
|
|
50223
50315
|
}
|
|
50224
|
-
return new Promise((
|
|
50316
|
+
return new Promise((resolve26, reject) => {
|
|
50225
50317
|
if (signal.aborted) {
|
|
50226
50318
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
50227
50319
|
return;
|
|
50228
50320
|
}
|
|
50229
|
-
const timeoutId = setTimeout(
|
|
50321
|
+
const timeoutId = setTimeout(resolve26, interval);
|
|
50230
50322
|
signal.addEventListener("abort", () => {
|
|
50231
50323
|
clearTimeout(timeoutId);
|
|
50232
50324
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -51366,7 +51458,7 @@ var McpServer = class {
|
|
|
51366
51458
|
let task = createTaskResult.task;
|
|
51367
51459
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
51368
51460
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
51369
|
-
await new Promise((
|
|
51461
|
+
await new Promise((resolve26) => setTimeout(resolve26, pollInterval));
|
|
51370
51462
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
51371
51463
|
if (!updatedTask) {
|
|
51372
51464
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -52091,14 +52183,14 @@ function claudeService(env) {
|
|
|
52091
52183
|
if (!dir) return SERVICE;
|
|
52092
52184
|
return `${SERVICE}-${createHash4("sha256").update(dir.normalize("NFC")).digest("hex").slice(0, 8)}`;
|
|
52093
52185
|
}
|
|
52094
|
-
var findWithSecurity = (args) => new Promise((
|
|
52186
|
+
var findWithSecurity = (args) => new Promise((resolve26) => {
|
|
52095
52187
|
const child = spawn3(SECURITY, [...args], { stdio: ["ignore", "pipe", "ignore"], timeout: SECURITY_TIMEOUT_MS });
|
|
52096
52188
|
let stdout = "";
|
|
52097
52189
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
52098
52190
|
stdout += chunk;
|
|
52099
52191
|
});
|
|
52100
|
-
child.once("error", () =>
|
|
52101
|
-
child.once("close", (code) =>
|
|
52192
|
+
child.once("error", () => resolve26({ status: 1, stdout: "" }));
|
|
52193
|
+
child.once("close", (code) => resolve26({ status: code ?? 1, stdout }));
|
|
52102
52194
|
});
|
|
52103
52195
|
function keychainRead(account, service, find = findWithSecurity) {
|
|
52104
52196
|
return async () => {
|
|
@@ -52119,7 +52211,7 @@ function renewEnv(env) {
|
|
|
52119
52211
|
return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== void 0 && !KEPT_OUT.includes(entry[0])));
|
|
52120
52212
|
}
|
|
52121
52213
|
function claudeRenewal(binary, env) {
|
|
52122
|
-
return () => new Promise((
|
|
52214
|
+
return () => new Promise((resolve26, reject) => {
|
|
52123
52215
|
const cwd = mkdtempSync(join16(tmpdir(), "orbital-claude-renew-"));
|
|
52124
52216
|
const child = spawn3(binary, RENEW_ARGS, { cwd, env: renewEnv(env), stdio: ["ignore", "ignore", "pipe"], timeout: RENEW_TIMEOUT_MS });
|
|
52125
52217
|
let stderr = "";
|
|
@@ -52129,7 +52221,7 @@ function claudeRenewal(binary, env) {
|
|
|
52129
52221
|
const done = (error62) => {
|
|
52130
52222
|
rmSync5(cwd, { recursive: true, force: true });
|
|
52131
52223
|
if (error62) reject(error62);
|
|
52132
|
-
else
|
|
52224
|
+
else resolve26();
|
|
52133
52225
|
};
|
|
52134
52226
|
child.once("error", done);
|
|
52135
52227
|
child.once("close", (code) => done(code === 0 ? null : new Error(`claude exited ${code ?? "on a signal"}${stderr.trim() ? `: ${stderr.trim().split("\n")[0]}` : ""}`)));
|
|
@@ -53396,7 +53488,7 @@ function missionOfRow(row2) {
|
|
|
53396
53488
|
result: textOf(row2, "result"),
|
|
53397
53489
|
question: textOf(row2, "question"),
|
|
53398
53490
|
questions: questionsOf(row2),
|
|
53399
|
-
|
|
53491
|
+
receivers: [],
|
|
53400
53492
|
lead: textOf(row2, "lead"),
|
|
53401
53493
|
lastActivity: textOf(row2, "activity"),
|
|
53402
53494
|
claim: textOf(row2, "claim"),
|
|
@@ -53549,7 +53641,7 @@ var MissionStore = class {
|
|
|
53549
53641
|
source: request.source !== void 0 ? request.source : known?.source ?? null,
|
|
53550
53642
|
result: request.result !== void 0 ? request.result : known?.result ?? null,
|
|
53551
53643
|
question: request.question !== void 0 ? request.question : request.state === "waiting" ? known?.question ?? null : null,
|
|
53552
|
-
|
|
53644
|
+
receivers: request.receivers !== void 0 ? request.receivers : known?.receivers ?? [],
|
|
53553
53645
|
lead: request.lead !== void 0 ? request.lead : known?.lead ?? null,
|
|
53554
53646
|
claim: request.claim !== void 0 ? request.claim : known?.claim ?? null,
|
|
53555
53647
|
claimedAt: request.claimedAt !== void 0 ? request.claimedAt : known?.claimedAt ?? null,
|
|
@@ -53604,7 +53696,7 @@ function missionRecord(node2, mission, declared, stored) {
|
|
|
53604
53696
|
result: stored?.result ?? null,
|
|
53605
53697
|
question: stored?.question ?? null,
|
|
53606
53698
|
questions: stored?.questions ?? [],
|
|
53607
|
-
|
|
53699
|
+
receivers: stored?.receivers ?? [],
|
|
53608
53700
|
lead: stored?.lead ?? null,
|
|
53609
53701
|
lastActivity: null,
|
|
53610
53702
|
claim: stored?.claim ?? null,
|
|
@@ -53711,9 +53803,9 @@ var Telemetry = class {
|
|
|
53711
53803
|
async drain(timeoutMs) {
|
|
53712
53804
|
if (this.queues.every((queue) => !queue.running && queue.items.length === 0)) return true;
|
|
53713
53805
|
let done = null;
|
|
53714
|
-
const empty = new Promise((
|
|
53806
|
+
const empty = new Promise((resolve26) => {
|
|
53715
53807
|
done = () => {
|
|
53716
|
-
if (this.queues.every((queue) => !queue.running && queue.items.length === 0))
|
|
53808
|
+
if (this.queues.every((queue) => !queue.running && queue.items.length === 0)) resolve26(true);
|
|
53717
53809
|
};
|
|
53718
53810
|
this.drained.add(done);
|
|
53719
53811
|
});
|
|
@@ -53966,7 +54058,7 @@ var SessionHost = class {
|
|
|
53966
54058
|
transition: missionTransition(before?.state ?? "open", after.state, after.question),
|
|
53967
54059
|
reason: change?.note ?? null,
|
|
53968
54060
|
by: change?.by ?? "node",
|
|
53969
|
-
|
|
54061
|
+
receivers: after.receivers,
|
|
53970
54062
|
sessionId: after.sessionId,
|
|
53971
54063
|
lead: after.lead,
|
|
53972
54064
|
question: after.question,
|
|
@@ -53974,7 +54066,7 @@ var SessionHost = class {
|
|
|
53974
54066
|
});
|
|
53975
54067
|
}
|
|
53976
54068
|
missionWritten(mission, team, by) {
|
|
53977
|
-
this.telemetry.emit({ kind: "mission.state", mission, team, from: null, to: "open", transition: "new", reason: "written", by,
|
|
54069
|
+
this.telemetry.emit({ kind: "mission.state", mission, team, from: null, to: "open", transition: "new", reason: "written", by, receivers: [], sessionId: null, lead: null, question: null, result: null });
|
|
53978
54070
|
}
|
|
53979
54071
|
async snapshot() {
|
|
53980
54072
|
const definition = await this.definitionOf(this.options.defaultWorkspace);
|
|
@@ -54003,7 +54095,7 @@ var SessionHost = class {
|
|
|
54003
54095
|
retiring: note?.retiring ?? null
|
|
54004
54096
|
};
|
|
54005
54097
|
}), ...[...notes].filter(([name]) => !definition.teams.some((team) => team.name === name)).map(([name, note]) => ({ name, node: this.node, purpose: null, lead: null, members: [], problems: [...note.problems], retiring: note.retiring }))],
|
|
54006
|
-
missions: missions.map((record2) => ({ missionId: record2.missionId, team: record2.team, state: record2.state, sessionId: record2.sessionId, lead: record2.lead,
|
|
54098
|
+
missions: missions.map((record2) => ({ missionId: record2.missionId, team: record2.team, state: record2.state, sessionId: record2.sessionId, lead: record2.lead, receivers: record2.receivers, givenBy: record2.givenBy, question: record2.question, questions: record2.questions, result: record2.result, updatedAt: record2.updatedAt })),
|
|
54007
54099
|
sessions: this.liveHosted().map((session) => ({ ...this.contextOf(session), role: this.roleOf(session), placement: session.placement, running: session.running !== null, since: this.turnSince.get(session.id) ?? null })),
|
|
54008
54100
|
jobs: [...this.openJobs].map(([jobId, open2]) => ({ ...open2.context, jobId, since: new Date(open2.since).toISOString(), via: open2.via })),
|
|
54009
54101
|
tokens: this.telemetry.tokens(),
|
|
@@ -54057,19 +54149,48 @@ var SessionHost = class {
|
|
|
54057
54149
|
this.redeliverQuestions(id);
|
|
54058
54150
|
return info;
|
|
54059
54151
|
}
|
|
54060
|
-
redeliverQuestions(
|
|
54152
|
+
redeliverQuestions(sessionId) {
|
|
54153
|
+
const session = this.sessions.get(sessionId);
|
|
54154
|
+
if (!session) return;
|
|
54061
54155
|
for (const stored of this.missions.all()) {
|
|
54062
|
-
if (
|
|
54063
|
-
|
|
54064
|
-
for (const asked2 of stored.questions) {
|
|
54065
|
-
if (!this.questionOpen(giverId, stored.mission, asked2.id)) this.deliverQuestion(giverId, { team: stored.team, mission: stored.mission, lead: stored.lead, question: asked2.question, questionId: asked2.id }, stored.sessionId);
|
|
54066
|
-
}
|
|
54156
|
+
if (!stored.receivers.some((key2) => this.receives(session, key2))) continue;
|
|
54157
|
+
for (const open2 of this.openQuestions(stored)) if (!this.questionOpen(sessionId, open2.mission, open2.questionId)) this.deliverQuestion(sessionId, open2, stored.sessionId);
|
|
54067
54158
|
}
|
|
54068
54159
|
}
|
|
54069
|
-
|
|
54070
|
-
if (
|
|
54160
|
+
openQuestions(stored) {
|
|
54161
|
+
if (!stored.sessionId || stored.state === "paused" || !hasLead(stored.state)) return [];
|
|
54162
|
+
const base2 = { team: stored.team, mission: stored.mission, lead: stored.lead };
|
|
54163
|
+
return [
|
|
54164
|
+
...stored.state === "waiting" ? [{ ...base2, question: stored.question ?? NO_HANDOFF_TELL, questionId: null }] : [],
|
|
54165
|
+
...stored.questions.map((asked2) => ({ ...base2, question: asked2.question, questionId: asked2.id }))
|
|
54166
|
+
];
|
|
54167
|
+
}
|
|
54168
|
+
receiverKey(session) {
|
|
54169
|
+
return session.external?.externalId ? externalReceiver(session.external.externalId) : session.id;
|
|
54170
|
+
}
|
|
54171
|
+
receives(session, key2) {
|
|
54172
|
+
return key2 === session.id || key2 === this.receiverKey(session);
|
|
54173
|
+
}
|
|
54174
|
+
liveReceivers(receivers, except = null) {
|
|
54175
|
+
return [...this.sessions.values()].filter((session) => session.id !== except && !this.ended.has(session.id) && (session.external ? session.external.attached : true) && receivers.some((key2) => this.receives(session, key2)));
|
|
54176
|
+
}
|
|
54177
|
+
keyOfRemote(receiver) {
|
|
54178
|
+
if (receiver.node !== this.node) return receiverRef(receiver);
|
|
54179
|
+
const session = this.sessions.get(receiver.sessionId);
|
|
54180
|
+
return session ? this.receiverKey(session) : receiver.sessionId;
|
|
54181
|
+
}
|
|
54182
|
+
starterKey(by) {
|
|
54183
|
+
if (!by.startsWith("session:")) return null;
|
|
54184
|
+
const named2 = by.slice("session:".length);
|
|
54185
|
+
const remote = remoteReceiverOf(named2);
|
|
54186
|
+
if (remote) return this.keyOfRemote(remote);
|
|
54187
|
+
const session = this.sessions.get(named2);
|
|
54188
|
+
return session ? this.receiverKey(session) : named2;
|
|
54189
|
+
}
|
|
54190
|
+
questionOpen(sessionId, mission, questionId) {
|
|
54191
|
+
if (this.questionsInFlight.has(`${sessionId}:${mission}:${questionId ?? ""}`)) return true;
|
|
54071
54192
|
for (const [taskId, asked2] of [...this.questionDeliveries]) {
|
|
54072
|
-
if (asked2.mission !== mission || asked2.questionId !== questionId || this.taskSession.get(taskId) !==
|
|
54193
|
+
if (asked2.mission !== mission || asked2.questionId !== questionId || this.taskSession.get(taskId) !== sessionId) continue;
|
|
54073
54194
|
const task = this.tasks.get(taskId);
|
|
54074
54195
|
if (task && !(task.status && isTerminal(task.status.state))) return true;
|
|
54075
54196
|
this.questionDeliveries.delete(taskId);
|
|
@@ -54101,7 +54222,7 @@ var SessionHost = class {
|
|
|
54101
54222
|
async settleExternal(sessionId, settle2) {
|
|
54102
54223
|
const session = this.sessions.get(sessionId);
|
|
54103
54224
|
if (!session?.external) throw new SessionHostError("not-found", `There is no attached session ${sessionId} on this node.`);
|
|
54104
|
-
const taskId = settle2.taskId ?? session.running;
|
|
54225
|
+
const taskId = settle2.taskId ?? session.running ?? session.external.open.keys().next().value ?? null;
|
|
54105
54226
|
const task = taskId ? this.tasks.get(taskId) : void 0;
|
|
54106
54227
|
if (!task || this.taskSession.get(task.id) !== sessionId || task.status && isTerminal(task.status.state)) throw new SessionHostError("invalid", `Session ${sessionId} has no open delivery${settle2.taskId ? ` ${settle2.taskId}` : ""} to settle.`);
|
|
54107
54228
|
await this.finish(sessionId, task, "TASK_STATE_COMPLETED", settle2.text);
|
|
@@ -54135,23 +54256,21 @@ var SessionHost = class {
|
|
|
54135
54256
|
await this.finish(session.id, task, "TASK_STATE_FAILED", `The attached session ${session.id} is detached.`);
|
|
54136
54257
|
return;
|
|
54137
54258
|
}
|
|
54138
|
-
|
|
54259
|
+
const asked2 = this.questionOf(message);
|
|
54260
|
+
if (asked2 === null) session.running = task.id;
|
|
54139
54261
|
await this.update(session.id, task, "TASK_STATE_WORKING");
|
|
54140
54262
|
const fromAgent = message.metadata?.[ORBITAL_META.fromAgent];
|
|
54141
54263
|
const fromSession = message.metadata?.[ORBITAL_META.fromSession];
|
|
54142
|
-
const asked2 = this.questionOf(message);
|
|
54143
54264
|
const delivery = { kind: "delivery", taskId: task.id, fromAgent: typeof fromAgent === "string" ? fromAgent : null, fromSession: typeof fromSession === "string" ? fromSession : null, mission: asked2, question: asked2 === null ? null : this.questionIdOf(message), text: messageText(message) };
|
|
54144
|
-
const settled = new Promise((
|
|
54265
|
+
const settled = new Promise((resolve26) => this.externalWaits.set(task.id, resolve26)).finally(() => {
|
|
54266
|
+
this.externalWaits.delete(task.id);
|
|
54267
|
+
external.open.delete(task.id);
|
|
54268
|
+
if (session.running === task.id) session.running = null;
|
|
54269
|
+
});
|
|
54145
54270
|
if (asked2 !== null) external.open.set(task.id, delivery);
|
|
54146
54271
|
if (external.listeners.size === 0) external.waiting.push(delivery);
|
|
54147
54272
|
for (const listener of external.listeners) listener(delivery);
|
|
54148
|
-
|
|
54149
|
-
await settled;
|
|
54150
|
-
} finally {
|
|
54151
|
-
this.externalWaits.delete(task.id);
|
|
54152
|
-
external.open.delete(task.id);
|
|
54153
|
-
session.running = null;
|
|
54154
|
-
}
|
|
54273
|
+
if (asked2 === null) await settled;
|
|
54155
54274
|
}
|
|
54156
54275
|
stopFollowing() {
|
|
54157
54276
|
this.following.abort();
|
|
@@ -54166,9 +54285,9 @@ var SessionHost = class {
|
|
|
54166
54285
|
if (this.followingHub) return;
|
|
54167
54286
|
let listener = null;
|
|
54168
54287
|
try {
|
|
54169
|
-
await Promise.race([this.settled(), new Promise((
|
|
54170
|
-
listener =
|
|
54171
|
-
this.followListeners.add(
|
|
54288
|
+
await Promise.race([this.settled(), new Promise((resolve26) => {
|
|
54289
|
+
listener = resolve26;
|
|
54290
|
+
this.followListeners.add(resolve26);
|
|
54172
54291
|
})]);
|
|
54173
54292
|
} finally {
|
|
54174
54293
|
if (listener) this.followListeners.delete(listener);
|
|
@@ -54259,7 +54378,7 @@ var SessionHost = class {
|
|
|
54259
54378
|
const leadLives = known?.sessionId ? this.sessions.has(known.sessionId) : false;
|
|
54260
54379
|
if (known?.state === "paused" && this.leadKept(known.sessionId)) throw new SessionHostError("invalid", `The mission ${missionId} is paused, in session ${known.sessionId}. Resume it with mission resume ${missionId}.`);
|
|
54261
54380
|
if (known?.state === "active" && leadLives) throw new SessionHostError("invalid", `The mission ${missionId} is active already, in session ${known.sessionId}. Its lead hands it off first.`);
|
|
54262
|
-
if (known?.state === "waiting" && leadLives) throw new SessionHostError("invalid", `The mission ${missionId} waits for
|
|
54381
|
+
if (known?.state === "waiting" && leadLives) throw new SessionHostError("invalid", `The mission ${missionId} waits for a decision, in session ${known.sessionId}. Answer it with mission reply.`);
|
|
54263
54382
|
const ended = known && hasLead(known.state) && !leadLives ? `the lead session ${known.sessionId ?? "(none)"} ended with its node` : null;
|
|
54264
54383
|
if (known?.state === "done") throw new SessionHostError("invalid", `The mission ${missionId} is done. Give the team a new mission for new work.`);
|
|
54265
54384
|
const claim2 = declared?.source ?? missionId;
|
|
@@ -54270,12 +54389,13 @@ var SessionHost = class {
|
|
|
54270
54389
|
const resumable = ended && known?.sessionId && readSessionRecord(this.paths.sessionsDir, known.sessionId) !== null ? known.sessionId : null;
|
|
54271
54390
|
if (resumable) {
|
|
54272
54391
|
const task2 = await this.submit({ sessionId: resumable, message });
|
|
54273
|
-
|
|
54392
|
+
const starter = this.starterKey(by);
|
|
54393
|
+
this.missions.change(mission, { state: "active", by, team, note: `${source.event}; ${ended}, so it goes on in the same session`, taskId: task2.id, source: key2, question: null, receivers: [.../* @__PURE__ */ new Set([...known?.receivers ?? [], ...starter ? [starter] : []])] });
|
|
54274
54394
|
return { task: task2, duplicate: false };
|
|
54275
54395
|
}
|
|
54276
54396
|
const task = await this.submit({ message, start: { agent: team, workspaceDir, team, mission } });
|
|
54277
54397
|
const takenOver = held2.map(claimHolder);
|
|
54278
|
-
this.missions.change(mission, { state: "active", by, team, note: [source.event, ended, takenOver.length > 0 ? `took over the ${live2.length > 0 ? "claim" : "stale claim"} of ${takenOver.join(", ")}` : null].filter((part) => part !== null).join("; "), claim: claim2, claimedAt: new Date(this.options.context.now()).toISOString(), sessionId: task.contextId ?? null, taskId: task.id, source: key2, result: null, question: null, questions: [],
|
|
54398
|
+
this.missions.change(mission, { state: "active", by, team, note: [source.event, ended, takenOver.length > 0 ? `took over the ${live2.length > 0 ? "claim" : "stale claim"} of ${takenOver.join(", ")}` : null].filter((part) => part !== null).join("; "), claim: claim2, claimedAt: new Date(this.options.context.now()).toISOString(), sessionId: task.contextId ?? null, taskId: task.id, source: key2, result: null, question: null, questions: [], receivers: [this.starterKey(by)].filter((one) => one !== null), lead: here.members[0]?.agent ?? null });
|
|
54279
54399
|
return { task, duplicate: false };
|
|
54280
54400
|
}
|
|
54281
54401
|
pauseInterrupted() {
|
|
@@ -54378,7 +54498,7 @@ var SessionHost = class {
|
|
|
54378
54498
|
for (const { relay: port } of this.options.ports.missionRelays()) {
|
|
54379
54499
|
const answered = await port.reply({ missionId, text: text3, ...questionId && { questionId }, by, ...this.claimOfBy(by) });
|
|
54380
54500
|
if (!answered) continue;
|
|
54381
|
-
await this.closeQuestions(missionId, questionId ?? void 0,
|
|
54501
|
+
await this.closeQuestions(missionId, questionId ?? void 0, by, text3);
|
|
54382
54502
|
return answered;
|
|
54383
54503
|
}
|
|
54384
54504
|
}
|
|
@@ -54389,14 +54509,14 @@ var SessionHost = class {
|
|
|
54389
54509
|
if (known?.state !== "waiting" || !known.sessionId) throw new SessionHostError("invalid", `The mission ${missionId} is ${known?.state ?? "open"}, not waiting, and has no open question, so it takes no reply.`);
|
|
54390
54510
|
const lead = known.sessionId;
|
|
54391
54511
|
this.missions.change(missionId, { state: "active", by, note: "reply" });
|
|
54392
|
-
await this.closeQuestions(missionId, null,
|
|
54512
|
+
await this.closeQuestions(missionId, null, by, text3);
|
|
54393
54513
|
await this.submit({ sessionId: lead, message: { messageId: randomUUID4(), role: "ROLE_USER", parts: [textPart(`[reply to your question on the mission ${missionId}, from ${by}]
|
|
54394
54514
|
${text3}`)] } });
|
|
54395
54515
|
return this.missionOf(missionId);
|
|
54396
54516
|
}
|
|
54397
54517
|
async answerAsked(by, missionId, lead, asked2, text3) {
|
|
54398
54518
|
this.missions.answer(missionId, asked2.id);
|
|
54399
|
-
await this.closeQuestions(missionId, asked2.id,
|
|
54519
|
+
await this.closeQuestions(missionId, asked2.id, by, text3);
|
|
54400
54520
|
const session = this.sessions.get(lead);
|
|
54401
54521
|
if (session?.running && !session.external) {
|
|
54402
54522
|
this.steering.set(lead, [...this.steering.get(lead) ?? [], { from: by, missionId, text: `reply to your question ${asked2.id}: ${asked2.question}
|
|
@@ -54413,7 +54533,7 @@ ${text3}`)] } });
|
|
|
54413
54533
|
const missionId = given.mission;
|
|
54414
54534
|
const known = this.missions.get(missionId);
|
|
54415
54535
|
if (!known || !hasLead(known.state) || known.state === "paused" || !known.sessionId) throw new SessionHostError("invalid", `The mission ${missionId} is ${known?.state ?? "open"}, so its lead asks nothing.`);
|
|
54416
|
-
if (given.sessionId !== null && known.sessionId !== given.sessionId) throw new SessionHostError("invalid", `Only the lead session of the mission ${missionId} asks its
|
|
54536
|
+
if (given.sessionId !== null && known.sessionId !== given.sessionId) throw new SessionHostError("invalid", `Only the lead session of the mission ${missionId} asks its decision receivers: session ${known.sessionId}.`);
|
|
54417
54537
|
this.askedInTurn.add(known.sessionId);
|
|
54418
54538
|
this.recordQuestion(missionId, known.sessionId, given.question);
|
|
54419
54539
|
return this.missionOf(missionId);
|
|
@@ -54421,8 +54541,8 @@ ${text3}`)] } });
|
|
|
54421
54541
|
recordQuestion(missionId, sessionId, question) {
|
|
54422
54542
|
const asked2 = { id: `q-${randomUUID4().slice(0, 8)}`, question, at: new Date(this.options.context.now()).toISOString() };
|
|
54423
54543
|
const after = this.missions.ask(missionId, asked2);
|
|
54424
|
-
this.telemetry.emit({ kind: "mission.state", mission: missionId, team: after.team, from: after.state, to: after.state, transition: "question", reason: `asked ${asked2.id}`, by: `session:${sessionId}`,
|
|
54425
|
-
this.
|
|
54544
|
+
this.telemetry.emit({ kind: "mission.state", mission: missionId, team: after.team, from: after.state, to: after.state, transition: "question", reason: `asked ${asked2.id}`, by: `session:${sessionId}`, receivers: after.receivers, sessionId: after.sessionId, lead: after.lead, question: asked2.question, result: after.result });
|
|
54545
|
+
this.tellReceivers(after.receivers, { team: after.team, mission: missionId, lead: after.lead, question: asked2.question, questionId: asked2.id }, sessionId);
|
|
54426
54546
|
}
|
|
54427
54547
|
async say(by, target, text3, reader, relay = true) {
|
|
54428
54548
|
const named2 = this.missions.get(target);
|
|
@@ -54478,16 +54598,28 @@ ${text3}`)] } });
|
|
|
54478
54598
|
const mission = message.metadata?.[ORBITAL_META.mission];
|
|
54479
54599
|
return typeof mission === "string" ? mission : null;
|
|
54480
54600
|
}
|
|
54481
|
-
async closeQuestions(mission, questionId, text3) {
|
|
54601
|
+
async closeQuestions(mission, questionId, by, text3) {
|
|
54602
|
+
const answerer = by.startsWith("session:") ? by.slice("session:".length) : null;
|
|
54603
|
+
const told = /* @__PURE__ */ new Set();
|
|
54482
54604
|
for (const [taskId, asked2] of [...this.questionDeliveries]) {
|
|
54483
54605
|
if (asked2.mission !== mission || questionId !== void 0 && asked2.questionId !== questionId) continue;
|
|
54484
54606
|
this.questionDeliveries.delete(taskId);
|
|
54485
54607
|
const task = this.tasks.get(taskId);
|
|
54486
|
-
const
|
|
54487
|
-
if (!task || !
|
|
54488
|
-
await this.finish(
|
|
54608
|
+
const receiver = this.taskSession.get(taskId);
|
|
54609
|
+
if (!task || !receiver || task.status && isTerminal(task.status.state)) continue;
|
|
54610
|
+
await this.finish(receiver, task, "TASK_STATE_COMPLETED", `answered by ${by}: ${text3}`);
|
|
54489
54611
|
this.externalWaits.get(taskId)?.();
|
|
54612
|
+
if (receiver !== answerer) this.tellAnswered(receiver, { kind: "answered", mission, question: asked2.questionId, by, text: text3 }, told);
|
|
54490
54613
|
}
|
|
54614
|
+
for (const session of this.liveReceivers(this.missions.get(mission)?.receivers ?? [], answerer)) this.tellAnswered(session.id, { kind: "answered", mission, question: questionId ?? null, by, text: text3 }, told);
|
|
54615
|
+
}
|
|
54616
|
+
tellAnswered(sessionId, answered, told) {
|
|
54617
|
+
const external = this.sessions.get(sessionId)?.external;
|
|
54618
|
+
if (!external?.attached || told.has(sessionId)) return;
|
|
54619
|
+
told.add(sessionId);
|
|
54620
|
+
const answers = (delivery) => delivery.mission === answered.mission && (answered.question === null || delivery.question === answered.question);
|
|
54621
|
+
for (let index = external.waiting.length - 1; index >= 0; index -= 1) if (answers(external.waiting[index])) external.waiting.splice(index, 1);
|
|
54622
|
+
for (const listener of external.listeners) listener(answered);
|
|
54491
54623
|
}
|
|
54492
54624
|
busyBelow(sessionId) {
|
|
54493
54625
|
return (this.pendingWakes.get(sessionId) ?? []).some((taskId) => !this.delivered.has(taskId)) || [...this.sessions.values(), ...this.remoteSessions.values()].some((one) => one.parentSessionId === sessionId && one.running !== null);
|
|
@@ -54514,32 +54646,46 @@ ${text3}`)] } });
|
|
|
54514
54646
|
}
|
|
54515
54647
|
this.nudged.delete(sessionId);
|
|
54516
54648
|
this.missions.change(known.mission, { state: "waiting", by: `session:${sessionId}`, note: question === "" ? NO_HANDOFF_NOTE : stateWord(state), question: question === "" ? null : question });
|
|
54517
|
-
this.
|
|
54649
|
+
this.tellReceivers(known.receivers, { team: known.team, mission: known.mission, lead: known.lead, question: question === "" ? NO_HANDOFF_TELL : question, questionId: null }, sessionId);
|
|
54518
54650
|
}
|
|
54519
|
-
async joinMission(
|
|
54520
|
-
const session = this.sessions.get(sessionId);
|
|
54521
|
-
if (!session
|
|
54651
|
+
async joinMission(receiver, missionId) {
|
|
54652
|
+
const session = "sessionId" in receiver ? this.sessions.get(receiver.sessionId) : void 0;
|
|
54653
|
+
if ("sessionId" in receiver && !session) throw new SessionHostError("not-found", `There is no session ${receiver.sessionId} on this node.`);
|
|
54522
54654
|
const known = this.missions.get(missionId);
|
|
54523
|
-
if (!known) {
|
|
54655
|
+
if (!known && session) {
|
|
54524
54656
|
for (const { relay } of this.options.ports.missionRelays()) {
|
|
54525
|
-
const joined = await relay.join({ missionId,
|
|
54657
|
+
const joined = await relay.join({ missionId, receiver: { node: this.node, sessionId: session.id }, ...this.claimOfSession(session.id) });
|
|
54526
54658
|
if (joined) return joined;
|
|
54527
54659
|
}
|
|
54528
54660
|
}
|
|
54529
|
-
|
|
54661
|
+
if (session && known?.sessionId === session.id) throw new SessionHostError("invalid", `The session ${session.id} leads the mission ${missionId}, so it asks its questions and receives none.`);
|
|
54662
|
+
if (!session) return this.joinHere(externalReceiver("externalId" in receiver ? receiver.externalId : ""), "cli", missionId, null);
|
|
54663
|
+
return this.joinHere(this.receiverKey(session), `session:${session.id}`, missionId, [session.id]);
|
|
54530
54664
|
}
|
|
54531
54665
|
async joinRemote(request) {
|
|
54532
|
-
|
|
54666
|
+
const key2 = this.keyOfRemote(request.receiver);
|
|
54667
|
+
const local = remoteReceiverOf(key2) === null ? [request.receiver.sessionId] : null;
|
|
54668
|
+
return this.joinHere(key2, `session:${receiverRef(request.receiver)}`, request.missionId, local);
|
|
54533
54669
|
}
|
|
54534
|
-
async
|
|
54535
|
-
const
|
|
54536
|
-
|
|
54670
|
+
async leaveMission(receiver, missionId) {
|
|
54671
|
+
const known = this.missions.get(missionId);
|
|
54672
|
+
if (!known || !hasLead(known.state)) throw new SessionHostError("invalid", `The mission ${missionId} is ${known?.state ?? "open"} on this node, so it has no decision receivers to leave.`);
|
|
54673
|
+
const session = "sessionId" in receiver ? this.sessions.get(receiver.sessionId) : void 0;
|
|
54674
|
+
const leaves = (key2) => session ? this.receives(session, key2) : "sessionId" in receiver ? key2 === receiver.sessionId : externalIdOf(key2) === receiver.externalId;
|
|
54675
|
+
const kept = known.receivers.filter((key2) => !leaves(key2));
|
|
54676
|
+
if (kept.length === known.receivers.length) return this.missionOf(missionId);
|
|
54677
|
+
this.missions.change(missionId, { state: known.state, by: "sessionId" in receiver ? `session:${receiver.sessionId}` : "cli", note: `${known.receivers.filter(leaves).join(", ")} left the decision receivers`, question: known.question, receivers: kept });
|
|
54678
|
+
return this.missionOf(missionId);
|
|
54679
|
+
}
|
|
54680
|
+
async joinHere(key2, by, missionId, deliverTo) {
|
|
54537
54681
|
const known = this.missions.get(missionId);
|
|
54538
54682
|
if (!known || !hasLead(known.state) || !known.sessionId) throw new SessionHostError("invalid", `The mission ${missionId} is ${known?.state ?? "open"}, so it has no lead to join.`);
|
|
54539
|
-
|
|
54540
|
-
|
|
54541
|
-
|
|
54542
|
-
|
|
54683
|
+
const stored = known.receivers.includes(key2) ? known : this.missions.change(missionId, { state: known.state, by, note: `${key2} joined the decision receivers`, question: known.question, receivers: [...known.receivers, key2] });
|
|
54684
|
+
const remote = remoteReceiverOf(key2);
|
|
54685
|
+
const live2 = this.liveReceivers([key2]).filter((session) => deliverTo === null || deliverTo.includes(session.id));
|
|
54686
|
+
for (const open2 of this.openQuestions(stored)) {
|
|
54687
|
+
if (remote && remote.node !== this.node) this.askRemote(remote, open2, stored.sessionId);
|
|
54688
|
+
for (const session of live2) if (!this.questionOpen(session.id, open2.mission, open2.questionId)) this.deliverQuestion(session.id, open2, stored.sessionId);
|
|
54543
54689
|
}
|
|
54544
54690
|
return this.missionOf(missionId);
|
|
54545
54691
|
}
|
|
@@ -54549,36 +54695,34 @@ ${text3}`)] } });
|
|
|
54549
54695
|
missionTeam(missionId) {
|
|
54550
54696
|
return this.missions.get(missionId)?.team ?? null;
|
|
54551
54697
|
}
|
|
54552
|
-
async
|
|
54553
|
-
const
|
|
54554
|
-
if (!
|
|
54555
|
-
this.deliverQuestion(
|
|
54698
|
+
async askReceiver(question) {
|
|
54699
|
+
const session = this.sessions.get(question.receiver.sessionId);
|
|
54700
|
+
if (!session || question.receiver.node !== this.node || this.liveReceivers([session.id]).length === 0) return false;
|
|
54701
|
+
this.deliverQuestion(session.id, { team: null, mission: question.missionId, lead: question.lead ? `${question.lead} on ${question.node}` : null, question: question.question, questionId: question.questionId ?? null }, question.leadSessionId);
|
|
54556
54702
|
return true;
|
|
54557
54703
|
}
|
|
54558
|
-
|
|
54559
|
-
const
|
|
54560
|
-
|
|
54561
|
-
|
|
54562
|
-
if (remote && remote.node !== self) {
|
|
54563
|
-
const question = { missionId, node: self, lead: known.lead, leadSessionId: sessionId, question: known.question, ...known.questionId && { questionId: known.questionId }, giver: remote, team: known.team };
|
|
54564
|
-
void (async () => {
|
|
54565
|
-
for (const { relay } of this.options.ports.missionRelays()) if (await relay.ask(question)) return;
|
|
54566
|
-
this.options.context.output.warn(`no plugin carried the question of ${missionId} to ${known.giver}`);
|
|
54567
|
-
})().catch((error62) => this.options.context.output.warn(`could not tell the giver of ${missionId}: ${error62 instanceof Error ? error62.message : String(error62)}`));
|
|
54568
|
-
return;
|
|
54704
|
+
tellReceivers(receivers, open2, leadSessionId) {
|
|
54705
|
+
for (const key2 of receivers) {
|
|
54706
|
+
const remote = remoteReceiverOf(key2);
|
|
54707
|
+
if (remote && remote.node !== this.node) this.askRemote(remote, open2, leadSessionId);
|
|
54569
54708
|
}
|
|
54570
|
-
const
|
|
54571
|
-
if (!giver) return;
|
|
54572
|
-
this.deliverQuestion(giver.id, known, sessionId);
|
|
54709
|
+
for (const session of this.liveReceivers(receivers)) this.deliverQuestion(session.id, open2, leadSessionId);
|
|
54573
54710
|
}
|
|
54574
|
-
|
|
54711
|
+
askRemote(receiver, open2, leadSessionId) {
|
|
54712
|
+
const question = { missionId: open2.mission, node: this.node, lead: open2.lead, leadSessionId, question: open2.question, ...open2.questionId && { questionId: open2.questionId }, receiver, team: open2.team };
|
|
54713
|
+
void (async () => {
|
|
54714
|
+
for (const { relay } of this.options.ports.missionRelays()) if (await relay.ask(question)) return;
|
|
54715
|
+
this.options.context.output.warn(`no plugin carried the question of ${open2.mission} to ${receiverRef(receiver)}`);
|
|
54716
|
+
})().catch((error62) => this.options.context.output.warn(`could not tell ${receiverRef(receiver)} the question of ${open2.mission}: ${error62 instanceof Error ? error62.message : String(error62)}`));
|
|
54717
|
+
}
|
|
54718
|
+
deliverQuestion(receiverId, known, sessionId) {
|
|
54575
54719
|
const missionId = known.mission;
|
|
54576
54720
|
const question = known.question;
|
|
54577
54721
|
const questionId = known.questionId;
|
|
54578
|
-
const key2 = `${
|
|
54722
|
+
const key2 = `${receiverId}:${missionId}:${questionId ?? ""}`;
|
|
54579
54723
|
this.questionsInFlight.add(key2);
|
|
54580
54724
|
void this.submit({
|
|
54581
|
-
sessionId:
|
|
54725
|
+
sessionId: receiverId,
|
|
54582
54726
|
message: {
|
|
54583
54727
|
messageId: randomUUID4(),
|
|
54584
54728
|
role: "ROLE_USER",
|
|
@@ -54593,7 +54737,7 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
54593
54737
|
}
|
|
54594
54738
|
}).then((task) => {
|
|
54595
54739
|
this.questionDeliveries.set(task.id, { mission: known.mission, questionId });
|
|
54596
|
-
}).catch((error62) => this.options.context.output.warn(`could not tell the
|
|
54740
|
+
}).catch((error62) => this.options.context.output.warn(`could not tell ${receiverId} the question of ${missionId}: ${error62 instanceof Error ? error62.message : String(error62)}`)).finally(() => this.questionsInFlight.delete(key2));
|
|
54597
54741
|
}
|
|
54598
54742
|
async missionActivity(missionId, limit) {
|
|
54599
54743
|
const sessionId = this.missions.get(missionId)?.sessionId;
|
|
@@ -54811,8 +54955,8 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
54811
54955
|
}
|
|
54812
54956
|
}
|
|
54813
54957
|
if (signal?.aborted) return;
|
|
54814
|
-
await new Promise((
|
|
54815
|
-
wake =
|
|
54958
|
+
await new Promise((resolve26) => {
|
|
54959
|
+
wake = resolve26;
|
|
54816
54960
|
});
|
|
54817
54961
|
wake = null;
|
|
54818
54962
|
}
|
|
@@ -54888,9 +55032,13 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
54888
55032
|
}
|
|
54889
55033
|
missionRoots(readerSessionId) {
|
|
54890
55034
|
const outside = this.sessions.get(readerSessionId)?.external !== void 0;
|
|
54891
|
-
const missionRoots = this.missions.all().filter((stored) => stored.sessionId !== null && (outside || stored.
|
|
55035
|
+
const missionRoots = this.missions.all().filter((stored) => stored.sessionId !== null && (outside || this.receivesFor(readerSessionId, stored.receivers)) && (this.sessions.has(stored.sessionId) || this.remoteSessions.has(stored.sessionId))).map((stored) => this.rootOf(stored.sessionId));
|
|
54892
55036
|
return new Set(missionRoots);
|
|
54893
55037
|
}
|
|
55038
|
+
receivesFor(sessionId, receivers) {
|
|
55039
|
+
const session = this.sessions.get(sessionId);
|
|
55040
|
+
return session !== void 0 && receivers.some((key2) => this.receives(session, key2));
|
|
55041
|
+
}
|
|
54894
55042
|
reachRoots(readerSessionId) {
|
|
54895
55043
|
const outside = this.sessions.get(readerSessionId)?.external !== void 0;
|
|
54896
55044
|
const attachedRoots = outside ? [...this.sessions.values()].filter((one) => one.external?.attached === true).map((one) => this.rootOf(one.id)) : [];
|
|
@@ -56457,7 +56605,7 @@ var StdioClientTransport = class {
|
|
|
56457
56605
|
if (this._process) {
|
|
56458
56606
|
throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
|
56459
56607
|
}
|
|
56460
|
-
return new Promise((
|
|
56608
|
+
return new Promise((resolve26, reject) => {
|
|
56461
56609
|
this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], {
|
|
56462
56610
|
// merge default env with server env because mcp server needs some env vars
|
|
56463
56611
|
env: {
|
|
@@ -56474,7 +56622,7 @@ var StdioClientTransport = class {
|
|
|
56474
56622
|
this.onerror?.(error62);
|
|
56475
56623
|
});
|
|
56476
56624
|
this._process.on("spawn", () => {
|
|
56477
|
-
|
|
56625
|
+
resolve26();
|
|
56478
56626
|
});
|
|
56479
56627
|
this._process.on("close", (_code) => {
|
|
56480
56628
|
this._process = void 0;
|
|
@@ -56539,22 +56687,22 @@ var StdioClientTransport = class {
|
|
|
56539
56687
|
if (this._process) {
|
|
56540
56688
|
const processToClose = this._process;
|
|
56541
56689
|
this._process = void 0;
|
|
56542
|
-
const closePromise = new Promise((
|
|
56690
|
+
const closePromise = new Promise((resolve26) => {
|
|
56543
56691
|
processToClose.once("close", () => {
|
|
56544
|
-
|
|
56692
|
+
resolve26();
|
|
56545
56693
|
});
|
|
56546
56694
|
});
|
|
56547
56695
|
try {
|
|
56548
56696
|
processToClose.stdin?.end();
|
|
56549
56697
|
} catch {
|
|
56550
56698
|
}
|
|
56551
|
-
await Promise.race([closePromise, new Promise((
|
|
56699
|
+
await Promise.race([closePromise, new Promise((resolve26) => setTimeout(resolve26, 2e3).unref())]);
|
|
56552
56700
|
if (processToClose.exitCode === null) {
|
|
56553
56701
|
try {
|
|
56554
56702
|
processToClose.kill("SIGTERM");
|
|
56555
56703
|
} catch {
|
|
56556
56704
|
}
|
|
56557
|
-
await Promise.race([closePromise, new Promise((
|
|
56705
|
+
await Promise.race([closePromise, new Promise((resolve26) => setTimeout(resolve26, 2e3).unref())]);
|
|
56558
56706
|
}
|
|
56559
56707
|
if (processToClose.exitCode === null) {
|
|
56560
56708
|
try {
|
|
@@ -56566,15 +56714,15 @@ var StdioClientTransport = class {
|
|
|
56566
56714
|
this._readBuffer.clear();
|
|
56567
56715
|
}
|
|
56568
56716
|
send(message) {
|
|
56569
|
-
return new Promise((
|
|
56717
|
+
return new Promise((resolve26) => {
|
|
56570
56718
|
if (!this._process?.stdin) {
|
|
56571
56719
|
throw new Error("Not connected");
|
|
56572
56720
|
}
|
|
56573
56721
|
const json4 = serializeMessage(message);
|
|
56574
56722
|
if (this._process.stdin.write(json4)) {
|
|
56575
|
-
|
|
56723
|
+
resolve26();
|
|
56576
56724
|
} else {
|
|
56577
|
-
this._process.stdin.once("drain",
|
|
56725
|
+
this._process.stdin.once("drain", resolve26);
|
|
56578
56726
|
}
|
|
56579
56727
|
});
|
|
56580
56728
|
}
|
|
@@ -56667,12 +56815,12 @@ var StdioServerTransport = class {
|
|
|
56667
56815
|
this.onclose?.();
|
|
56668
56816
|
}
|
|
56669
56817
|
send(message) {
|
|
56670
|
-
return new Promise((
|
|
56818
|
+
return new Promise((resolve26) => {
|
|
56671
56819
|
const json4 = serializeMessage(message);
|
|
56672
56820
|
if (this._stdout.write(json4)) {
|
|
56673
|
-
|
|
56821
|
+
resolve26();
|
|
56674
56822
|
} else {
|
|
56675
|
-
this._stdout.once("drain",
|
|
56823
|
+
this._stdout.once("drain", resolve26);
|
|
56676
56824
|
}
|
|
56677
56825
|
});
|
|
56678
56826
|
}
|
|
@@ -56781,6 +56929,7 @@ function agentTools(options) {
|
|
|
56781
56929
|
};
|
|
56782
56930
|
const send = (message, wait) => node2.send(message, wait);
|
|
56783
56931
|
const a2a = () => node2.a2a();
|
|
56932
|
+
const own2 = async () => options.attached ? await options.attached() : options.sessionId;
|
|
56784
56933
|
return [
|
|
56785
56934
|
defineMcpTool({
|
|
56786
56935
|
name: "agentList",
|
|
@@ -56824,7 +56973,7 @@ function agentTools(options) {
|
|
|
56824
56973
|
description: "List the sessions of your team's work: the lead and every agent it or another member started. Each has its agent, its session id (a jobId for agentMessage), its role (lead or member), its status and its harness.",
|
|
56825
56974
|
inputSchema: {},
|
|
56826
56975
|
run: async () => {
|
|
56827
|
-
if (!
|
|
56976
|
+
if (!await own2()) throw new Error("agentPeers needs your own session: only an agent the node runs has peers.");
|
|
56828
56977
|
return await node2.peers();
|
|
56829
56978
|
}
|
|
56830
56979
|
}),
|
|
@@ -56904,13 +57053,13 @@ function agentTools(options) {
|
|
|
56904
57053
|
}),
|
|
56905
57054
|
defineMcpTool({
|
|
56906
57055
|
name: "missionList",
|
|
56907
|
-
description: "List every mission and its state (open, active, waiting, paused, done or failed): the missions of this node and of the other nodes it knows, such as the nodes a lan plugin finds. Each has its id, its team, its node, its state, its open questions (questions: each lead question that waits on
|
|
57056
|
+
description: "List every mission and its state (open, active, waiting, paused, done or failed): the missions of this node and of the other nodes it knows, such as the nodes a lan plugin finds. Each has its id, its team, its node, its state, its open questions (questions: each lead question that waits on a decision, with its id and text; answer one with missionReply), its decision receivers and, when it ended, its result.",
|
|
56908
57057
|
inputSchema: {},
|
|
56909
57058
|
run: async () => node2.missions()
|
|
56910
57059
|
}),
|
|
56911
57060
|
defineMcpTool({
|
|
56912
57061
|
name: "missionShow",
|
|
56913
|
-
description: "Show one mission by its id: its brief, its state, its open questions (questions: each lead question that waits on
|
|
57062
|
+
description: "Show one mission by its id: its brief, its state, its open questions (questions: each lead question that waits on a decision, with its id and text; answer one with missionReply), its decision receivers, its session, its result and the history of its states.",
|
|
56914
57063
|
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
56915
57064
|
run: async (params) => {
|
|
56916
57065
|
const found = (await node2.missions()).filter((record3) => record3.missionId === params.mission);
|
|
@@ -56962,7 +57111,7 @@ function agentTools(options) {
|
|
|
56962
57111
|
}),
|
|
56963
57112
|
defineMcpTool({
|
|
56964
57113
|
name: "missionReply",
|
|
56965
|
-
description: "Answer a question of a mission, by its id: the question its waiting lead ended its turn with, or a question its lead asked with missionAsk, by the question id. The answer reaches the lead and closes that question. With no question id, the reply answers the question of a waiting mission, or else the oldest open question.",
|
|
57114
|
+
description: "Answer a question of a mission, by its id: the question its waiting lead ended its turn with, or a question its lead asked with missionAsk, by the question id. The answer reaches the lead and closes that question for every decision receiver, and the node tells the other receivers that it is answered. With no question id, the reply answers the question of a waiting mission, or else the oldest open question.",
|
|
56966
57115
|
inputSchema: {
|
|
56967
57116
|
mission: MissionIdSchema.describe("The mission id."),
|
|
56968
57117
|
text: external_exports.string().min(1).describe("The answer, in full: the lead has none of your context."),
|
|
@@ -56996,13 +57145,22 @@ function agentTools(options) {
|
|
|
56996
57145
|
}),
|
|
56997
57146
|
defineMcpTool({
|
|
56998
57147
|
name: "missionJoin",
|
|
56999
|
-
description: "
|
|
57148
|
+
description: "Join an active, waiting or paused mission, by its id, as one of its decision receivers: any session can join, also a Claude Code session that the orbital mod attached. Each question of its lead goes to every live receiver, and the questions that are open now come to you at once. Answer a question with missionReply and its question id, or the question of a waiting mission with missionSay: the first answer from any receiver closes it for all. An attached session stays a receiver after it or the node restarts, so join once.",
|
|
57000
57149
|
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
57001
57150
|
run: async (params) => {
|
|
57002
|
-
if (!
|
|
57151
|
+
if (!await own2()) throw new Error("missionJoin needs your own session: run it in a session of the node, or in a session that the orbital mod attached.");
|
|
57003
57152
|
return node2.joinMission(params.mission);
|
|
57004
57153
|
}
|
|
57005
57154
|
}),
|
|
57155
|
+
defineMcpTool({
|
|
57156
|
+
name: "missionLeave",
|
|
57157
|
+
description: "Leave the decision receivers of a mission, by its id: the questions of its lead stop coming to this session. The other receivers still get them.",
|
|
57158
|
+
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
57159
|
+
run: async (params) => {
|
|
57160
|
+
if (!await own2()) throw new Error("missionLeave needs your own session: only a decision receiver leaves a mission.");
|
|
57161
|
+
return node2.leaveMission(params.mission);
|
|
57162
|
+
}
|
|
57163
|
+
}),
|
|
57006
57164
|
defineMcpTool({
|
|
57007
57165
|
name: "missionNew",
|
|
57008
57166
|
description: "Write a new mission for a team of this project: missions/<id>/MISSION.md in the folder that holds the files of orbital, with the team in its frontmatter. One id names one mission. It is open until missionStart starts it.",
|
|
@@ -57022,7 +57180,7 @@ function agentTools(options) {
|
|
|
57022
57180
|
description: "Read the mission of your session: its id, its team, its brief with the intent, the end state and the limits, its state (open, active, waiting, paused, done or failed), and its history.",
|
|
57023
57181
|
inputSchema: {},
|
|
57024
57182
|
run: async () => {
|
|
57025
|
-
if (!
|
|
57183
|
+
if (!await own2()) throw new Error("missionGet needs your own session: only an agent the node runs has a mission.");
|
|
57026
57184
|
return await node2.mission();
|
|
57027
57185
|
}
|
|
57028
57186
|
}),
|
|
@@ -57034,7 +57192,7 @@ function agentTools(options) {
|
|
|
57034
57192
|
state: external_exports.enum(["done", "failed"]).optional().describe("done when the end state is reached, failed when it cannot be. Default: done.")
|
|
57035
57193
|
},
|
|
57036
57194
|
run: async (params) => {
|
|
57037
|
-
if (!
|
|
57195
|
+
if (!await own2()) throw new Error("missionHandoff needs your own session: only the lead session of a mission hands it off.");
|
|
57038
57196
|
return await node2.handoff({ result: params.result, state: params.state ?? "done" });
|
|
57039
57197
|
}
|
|
57040
57198
|
}),
|
|
@@ -57047,18 +57205,18 @@ function agentTools(options) {
|
|
|
57047
57205
|
state: external_exports.string().min(1).optional().describe("The state of the linked thing, such as open, merged or closed.")
|
|
57048
57206
|
},
|
|
57049
57207
|
run: async (params) => {
|
|
57050
|
-
if (!
|
|
57208
|
+
if (!await own2()) throw new Error("missionLink needs your own session: only the lead session of a mission adds a link to it.");
|
|
57051
57209
|
return await node2.link({ label: params.label, url: params.url, state: params.state ?? null });
|
|
57052
57210
|
}
|
|
57053
57211
|
}),
|
|
57054
57212
|
defineMcpTool({
|
|
57055
57213
|
name: "missionAsk",
|
|
57056
|
-
description: "Ask the
|
|
57214
|
+
description: "Ask the decision receivers of the mission of your session a question, as its lead, at any moment, also while your workers run. The node keeps each question open with the mission until a receiver answers it, shows it to the person as waiting on them, and pushes it at once to every live receiver. The answer comes back to you as a message that names the question. Use it for every decision you need: a question only in your reply text reaches no one. Keep working on what does not depend on the answer.",
|
|
57057
57215
|
inputSchema: {
|
|
57058
|
-
question: external_exports.string().min(1).describe("The question, in full, with the options you see and what each one means: the
|
|
57216
|
+
question: external_exports.string().min(1).describe("The question, in full, with the options you see and what each one means: the receivers have none of your context.")
|
|
57059
57217
|
},
|
|
57060
57218
|
run: async (params) => {
|
|
57061
|
-
if (!
|
|
57219
|
+
if (!await own2()) throw new Error("missionAsk needs your own session: only the lead session of a mission asks its decision receivers.");
|
|
57062
57220
|
return await node2.ask({ question: params.question });
|
|
57063
57221
|
}
|
|
57064
57222
|
})
|
|
@@ -57620,8 +57778,8 @@ function relaySender(plugin, by) {
|
|
|
57620
57778
|
function pluginSender(plugin) {
|
|
57621
57779
|
return { channel: plugin, peer: plugin, from: { kind: "node", name: plugin } };
|
|
57622
57780
|
}
|
|
57623
|
-
function remoteSessionSender(plugin,
|
|
57624
|
-
return { channel: plugin, peer:
|
|
57781
|
+
function remoteSessionSender(plugin, session) {
|
|
57782
|
+
return { channel: plugin, peer: session.node, from: { kind: "session", name: session.sessionId, ade: session.node } };
|
|
57625
57783
|
}
|
|
57626
57784
|
function missionTarget(node2, team) {
|
|
57627
57785
|
return team ? { kind: "team", name: team, ade: node2 } : { kind: "node", name: node2 };
|
|
@@ -57760,10 +57918,10 @@ function a2aHandlerFor(host, reader, confined = false, network = null) {
|
|
|
57760
57918
|
};
|
|
57761
57919
|
}
|
|
57762
57920
|
function readBody(request) {
|
|
57763
|
-
return new Promise((
|
|
57921
|
+
return new Promise((resolve26, reject) => {
|
|
57764
57922
|
const chunks = [];
|
|
57765
57923
|
request.on("data", (chunk) => chunks.push(chunk));
|
|
57766
|
-
request.on("end", () =>
|
|
57924
|
+
request.on("end", () => resolve26(Buffer.concat(chunks).toString("utf8")));
|
|
57767
57925
|
request.on("error", reject);
|
|
57768
57926
|
});
|
|
57769
57927
|
}
|
|
@@ -57827,7 +57985,7 @@ async function serveHostApi(request, response, url2, api) {
|
|
|
57827
57985
|
if (request.method === "POST" && url2.pathname === "/orbital/host/missions/ask") {
|
|
57828
57986
|
const asked2 = MissionQuestionSchema.safeParse(JSON.parse(await readBody(request) || "{}"));
|
|
57829
57987
|
if (!asked2.success) return json3(response, 400, { error: asked2.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ") });
|
|
57830
|
-
return json3(response, 200, { delivered: await api.
|
|
57988
|
+
return json3(response, 200, { delivered: await api.askReceiver(asked2.data) });
|
|
57831
57989
|
}
|
|
57832
57990
|
if (request.method === "POST" && url2.pathname === "/orbital/host/settled") {
|
|
57833
57991
|
const { orFollowing } = JSON.parse(await readBody(request) || "{}");
|
|
@@ -57946,11 +58104,16 @@ async function startNodeServer(options) {
|
|
|
57946
58104
|
const by = caller.bound ? `session:${caller.bound}` : "cli";
|
|
57947
58105
|
return json3(response, 200, control[1] === "pause" ? await options.host.pauseMission(by, asked2.data.missionId) : await options.host.resumeMission(by, asked2.data.missionId));
|
|
57948
58106
|
}
|
|
57949
|
-
|
|
57950
|
-
|
|
58107
|
+
const membership = /^\/orbital\/missions\/(join|leave)$/.exec(url2.pathname);
|
|
58108
|
+
if (request.method === "POST" && membership) {
|
|
57951
58109
|
const asked2 = MissionJoinSchema.safeParse(JSON.parse(await readBody(request) || "{}"));
|
|
57952
58110
|
if (!asked2.success) return json3(response, 400, { error: asked2.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ") });
|
|
57953
|
-
|
|
58111
|
+
const session = caller.bound ?? caller.reader;
|
|
58112
|
+
const holder = caller.presented === caller.token;
|
|
58113
|
+
if (asked2.data.externalId && !holder) return json3(response, 403, { error: "Only a holder of the node secret names an external id: a session joins as itself." });
|
|
58114
|
+
if (!session && !asked2.data.externalId) return json3(response, 403, { error: `Only a session ${membership[1]}s the decision receivers of a mission: present its session token, or name the external id of an attached session.` });
|
|
58115
|
+
const receiver = asked2.data.externalId ? { externalId: asked2.data.externalId } : { sessionId: session };
|
|
58116
|
+
return json3(response, 200, membership[1] === "join" ? await options.host.joinMission(receiver, asked2.data.missionId) : await options.host.leaveMission(receiver, asked2.data.missionId));
|
|
57954
58117
|
}
|
|
57955
58118
|
if (request.method === "POST" && url2.pathname === "/orbital/missions/new") {
|
|
57956
58119
|
if (caller.presented !== caller.token && !caller.bound) return json3(response, 403, { error: "Only a holder of the node secret or a session of the node writes a mission here." });
|
|
@@ -57988,7 +58151,7 @@ async function startNodeServer(options) {
|
|
|
57988
58151
|
const named2 = asked2.data.missionId ?? own2?.missionId ?? null;
|
|
57989
58152
|
if (!named2) return json3(response, 400, { error: "Name the mission by its id." });
|
|
57990
58153
|
if (own2 && own2.missionId !== named2) return json3(response, 403, { error: `The session ${session} works on the mission ${own2.missionId}, not on ${named2}.` });
|
|
57991
|
-
if (!session && caller.presented !== caller.token) return json3(response, 403, { error: "Only the lead session or a holder of the node secret asks the
|
|
58154
|
+
if (!session && caller.presented !== caller.token) return json3(response, 403, { error: "Only the lead session or a holder of the node secret asks the decision receivers of a mission." });
|
|
57992
58155
|
return json3(response, 200, await options.host.askMission({ mission: named2, question: asked2.data.question, sessionId: session }));
|
|
57993
58156
|
}
|
|
57994
58157
|
const activity = /^\/orbital\/missions\/([^/]+)\/activity$/.exec(url2.pathname);
|
|
@@ -58029,6 +58192,20 @@ async function startNodeServer(options) {
|
|
|
58029
58192
|
return json3(response, 502, { error: `The panel ${plugin2}/${id} did not answer: ${error62 instanceof Error ? error62.message : String(error62)}` });
|
|
58030
58193
|
}
|
|
58031
58194
|
}
|
|
58195
|
+
const commandPath = /^\/orbital\/plugins\/([^/]+)\/commands\/([^/]+)$/.exec(url2.pathname);
|
|
58196
|
+
if (request.method === "POST" && commandPath) {
|
|
58197
|
+
if (presented !== token) return json3(response, 403, { error: "Only a holder of the node secret runs a command of a plugin." });
|
|
58198
|
+
const [name, command] = [decodeURIComponent(commandPath[1]), decodeURIComponent(commandPath[2])];
|
|
58199
|
+
const plugin2 = options.ports.plugins.find((one) => one.name === name);
|
|
58200
|
+
if (!plugin2?.command) return json3(response, 404, { error: `No plugin ${name} with commands runs on this node.` });
|
|
58201
|
+
const asked2 = PluginCommandRequestSchema.safeParse(await readBody(request).then((raw) => JSON.parse(raw)).catch(() => null));
|
|
58202
|
+
if (!asked2.success || asked2.data.name !== command || asked2.data.terminal !== null) return json3(response, 400, { error: `The request does not run the command ${command}: it names another command or asks for a terminal.` });
|
|
58203
|
+
try {
|
|
58204
|
+
return json3(response, 200, await plugin2.command(asked2.data));
|
|
58205
|
+
} catch (error62) {
|
|
58206
|
+
return json3(response, 502, { error: `The command ${name} ${command} failed: ${error62 instanceof Error ? error62.message : String(error62)}` });
|
|
58207
|
+
}
|
|
58208
|
+
}
|
|
58032
58209
|
if (url2.pathname === "/orbital/telemetry/snapshot" || url2.pathname === "/orbital/telemetry/events") {
|
|
58033
58210
|
if (request.method === "GET" && url2.pathname === "/orbital/telemetry/snapshot" && (presented === token || bound)) return json3(response, 200, await options.host.snapshot());
|
|
58034
58211
|
if (presented !== token) return json3(response, 403, { error: "Only a holder of the node secret reads the telemetry stream of the node." });
|
|
@@ -58188,7 +58365,7 @@ async function startNodeServer(options) {
|
|
|
58188
58365
|
const result2 = await dispatchA2a(await readBody(request), a2aHandlerFor(options.host, reader, bound !== null, httpSender(request.socket.remoteAddress)));
|
|
58189
58366
|
if (result2.kind === "json") return json3(response, 200, result2.body);
|
|
58190
58367
|
response.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
|
|
58191
|
-
const closed = new Promise((
|
|
58368
|
+
const closed = new Promise((resolve26) => response.on("close", resolve26));
|
|
58192
58369
|
const iterator = result2.events[Symbol.asyncIterator]();
|
|
58193
58370
|
for (; ; ) {
|
|
58194
58371
|
const next = await Promise.race([iterator.next(), closed.then(() => null)]);
|
|
@@ -58201,9 +58378,9 @@ async function startNodeServer(options) {
|
|
|
58201
58378
|
}
|
|
58202
58379
|
response.end();
|
|
58203
58380
|
}
|
|
58204
|
-
await new Promise((
|
|
58381
|
+
await new Promise((resolve26, reject) => {
|
|
58205
58382
|
server.once("error", reject);
|
|
58206
|
-
server.listen(options.port, address, () =>
|
|
58383
|
+
server.listen(options.port, address, () => resolve26());
|
|
58207
58384
|
});
|
|
58208
58385
|
const info = { name: options.name, workspaceDir: options.workspaceDir, url: nodeUrl(), token, pid: process.pid, startedAt: new Date(options.now()).toISOString(), restart: restartOf(process.env[NODE_RESTART_ENV]), code: pinnedCodeKey() };
|
|
58209
58386
|
mkdirSync17(nodeDir(options.home, options.name), { recursive: true, mode: 448 });
|
|
@@ -58214,7 +58391,7 @@ async function startNodeServer(options) {
|
|
|
58214
58391
|
close: async () => {
|
|
58215
58392
|
if (readNodeInfo(options.home, options.name)?.pid === process.pid) rmSync9(nodeInfoPath(options.home, options.name), { force: true });
|
|
58216
58393
|
server.closeAllConnections();
|
|
58217
|
-
await new Promise((
|
|
58394
|
+
await new Promise((resolve26) => server.close(() => resolve26()));
|
|
58218
58395
|
}
|
|
58219
58396
|
};
|
|
58220
58397
|
}
|
|
@@ -59220,7 +59397,7 @@ function hostApiOf(host, plugin, catalog, channels, models) {
|
|
|
59220
59397
|
startMission: (start) => host.startMission(plugin, start),
|
|
59221
59398
|
missions: () => host.missionList(),
|
|
59222
59399
|
joinMission: async (request) => {
|
|
59223
|
-
await host.receive(networkReceive(remoteSessionSender(plugin, request.
|
|
59400
|
+
await host.receive(networkReceive(remoteSessionSender(plugin, request.receiver), missionTarget(host.node, host.missionTeam(request.missionId)), `join the mission ${request.missionId} as a decision receiver`, relayClaim(request)));
|
|
59224
59401
|
return host.joinRemote(request);
|
|
59225
59402
|
},
|
|
59226
59403
|
replyMission: async (request) => {
|
|
@@ -59235,9 +59412,9 @@ function hostApiOf(host, plugin, catalog, channels, models) {
|
|
|
59235
59412
|
await relayed(request, `resume the mission ${request.missionId}`);
|
|
59236
59413
|
return host.resumeMission(request.by, request.missionId, false);
|
|
59237
59414
|
},
|
|
59238
|
-
|
|
59239
|
-
await host.receive(networkReceive(remoteSessionSender(plugin, { node: question.node, sessionId: question.leadSessionId }), { kind: "session", name: question.
|
|
59240
|
-
return host.
|
|
59415
|
+
askReceiver: async (question) => {
|
|
59416
|
+
await host.receive(networkReceive(remoteSessionSender(plugin, { node: question.node, sessionId: question.leadSessionId }), { kind: "session", name: question.receiver.sessionId, ade: host.node }, question.question, relayClaim({ team: question.team, agent: question.lead })));
|
|
59417
|
+
return host.askReceiver(question);
|
|
59241
59418
|
},
|
|
59242
59419
|
sayMission: async (request) => {
|
|
59243
59420
|
await relayed(request, request.text);
|
|
@@ -59451,8 +59628,8 @@ var ChannelHub = class {
|
|
|
59451
59628
|
if (state && isTerminal(state)) return;
|
|
59452
59629
|
}
|
|
59453
59630
|
if (signal?.aborted) return;
|
|
59454
|
-
await new Promise((
|
|
59455
|
-
wake =
|
|
59631
|
+
await new Promise((resolve26) => {
|
|
59632
|
+
wake = resolve26;
|
|
59456
59633
|
});
|
|
59457
59634
|
wake = null;
|
|
59458
59635
|
}
|
|
@@ -59626,8 +59803,8 @@ Answer yes or no in this thread.`)], metadata: { [ORBITAL_META.channel]: binding
|
|
|
59626
59803
|
thread: null
|
|
59627
59804
|
});
|
|
59628
59805
|
const threadKey = `${binding.channel}|${binding.id}|${sent.thread ?? sent.ref}`;
|
|
59629
|
-
const answer2 = await new Promise((
|
|
59630
|
-
this.asks.set(threadKey, { resolve:
|
|
59806
|
+
const answer2 = await new Promise((resolve26, reject) => {
|
|
59807
|
+
this.asks.set(threadKey, { resolve: resolve26, question, channel: binding.channel, since: new Date(this.options.context.now()).toISOString() });
|
|
59631
59808
|
signal?.addEventListener("abort", () => {
|
|
59632
59809
|
this.asks.delete(threadKey);
|
|
59633
59810
|
reject(new Error("The question was withdrawn."));
|
|
@@ -60301,8 +60478,9 @@ var panel = {
|
|
|
60301
60478
|
var panelCommands = [panel];
|
|
60302
60479
|
|
|
60303
60480
|
// src/core/cli/plugin.ts
|
|
60304
|
-
|
|
60305
|
-
import {
|
|
60481
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
60482
|
+
import { readFileSync as readFileSync29 } from "node:fs";
|
|
60483
|
+
import { basename as basename9, resolve as resolve15 } from "node:path";
|
|
60306
60484
|
|
|
60307
60485
|
// src/sdk/conformance.ts
|
|
60308
60486
|
import { existsSync as existsSync29, mkdirSync as mkdirSync21, mkdtempSync as mkdtempSync2, readFileSync as readFileSync27, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "node:fs";
|
|
@@ -60673,13 +60851,144 @@ async function runConformance(ports, fixtures = {}) {
|
|
|
60673
60851
|
return results;
|
|
60674
60852
|
}
|
|
60675
60853
|
|
|
60854
|
+
// src/core/cli/plugin-commands.ts
|
|
60855
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28 } from "node:fs";
|
|
60856
|
+
import { join as join37, resolve as resolve14 } from "node:path";
|
|
60857
|
+
var INTROSPECTION_WORDS = ["describe", "schema", "help"];
|
|
60858
|
+
function commandsOf(dir) {
|
|
60859
|
+
const descriptor = join37(dir, PLUGIN_PACKAGE_FILE);
|
|
60860
|
+
if (!existsSync30(descriptor)) return [];
|
|
60861
|
+
try {
|
|
60862
|
+
return PluginPackageSchema.parse(JSON.parse(readFileSync28(descriptor, "utf8"))).commands;
|
|
60863
|
+
} catch {
|
|
60864
|
+
return [];
|
|
60865
|
+
}
|
|
60866
|
+
}
|
|
60867
|
+
function declaredPluginCommands(workspaceDir, env) {
|
|
60868
|
+
try {
|
|
60869
|
+
const source = definitionSource(workspaceDir, env);
|
|
60870
|
+
const config2 = source ? workspaceConfig(workspaceDir, env) : null;
|
|
60871
|
+
if (!source || !config2) return [];
|
|
60872
|
+
return Object.entries(config2.plugins).filter(([, entry]) => entry.enabled && entry.source).flatMap(([plugin, entry]) => {
|
|
60873
|
+
const from = pluginSource(plugin, entry);
|
|
60874
|
+
const commands = from.startsWith("exec:") ? [] : commandsOf(pluginDir(plugin, from, { harnessRoot: source.root, home: orbitalHome(env) }));
|
|
60875
|
+
return commands.length > 0 ? [{ plugin, commands }] : [];
|
|
60876
|
+
});
|
|
60877
|
+
} catch {
|
|
60878
|
+
return [];
|
|
60879
|
+
}
|
|
60880
|
+
}
|
|
60881
|
+
function workspaceOf(input2, fallback = ".") {
|
|
60882
|
+
return resolve14(typeof input2.flags.workspace === "string" ? input2.flags.workspace : fallback);
|
|
60883
|
+
}
|
|
60884
|
+
async function startWorkspacePlugin(context, workspaceDir, name, terminal) {
|
|
60885
|
+
const root = definitionSource(workspaceDir, context.env)?.root ?? null;
|
|
60886
|
+
const config2 = workspaceConfig(workspaceDir, context.env);
|
|
60887
|
+
const entry = config2?.plugins[name];
|
|
60888
|
+
if (!config2 || !entry) throw new CliError({ code: "not-found", message: `No plugin ${name} is named in ${ORBITAL_FILE} or found in ${PROJECT_PLUGINS_DIR}.`, exitCode: ExitCode.Validation, nextCommand: `${commandPrefix(context.env)} plugin add ${name}@<marketplace>` });
|
|
60889
|
+
const secrets = new PluginSecrets(context.env);
|
|
60890
|
+
return startRemotePlugin({
|
|
60891
|
+
name,
|
|
60892
|
+
source: pluginSource(name, entry),
|
|
60893
|
+
config: entry.config,
|
|
60894
|
+
entryEnv: entry.env,
|
|
60895
|
+
node: { name: `plugin-${name}`, workspaceDir, harness: defaultHarness(context.env), harnesses: [...RUNNER_HARNESSES], once: true, idleExitMs: null, project: null },
|
|
60896
|
+
roots: { harnessRoot: root, home: orbitalHome(context.env) },
|
|
60897
|
+
offline: config2.offline,
|
|
60898
|
+
policy: new PolicyChain([]),
|
|
60899
|
+
env: context.env,
|
|
60900
|
+
log: (level, message) => level === "info" ? context.output.step(message) : context.output.warn(message),
|
|
60901
|
+
secret: async (secret) => secrets.get(name, secret),
|
|
60902
|
+
restarts: 0,
|
|
60903
|
+
...terminal && { terminal }
|
|
60904
|
+
});
|
|
60905
|
+
}
|
|
60906
|
+
function flagOf(option) {
|
|
60907
|
+
return {
|
|
60908
|
+
name: option.name,
|
|
60909
|
+
type: option.type,
|
|
60910
|
+
summary: option.summary,
|
|
60911
|
+
...option.required && { required: true },
|
|
60912
|
+
...option.default !== void 0 && { default: option.default },
|
|
60913
|
+
...option.values && { enumValues: option.values }
|
|
60914
|
+
};
|
|
60915
|
+
}
|
|
60916
|
+
function requestOf(command, input2, terminal) {
|
|
60917
|
+
const args = Object.fromEntries(command.args.flatMap((arg) => {
|
|
60918
|
+
const value2 = input2.positionals[arg.name];
|
|
60919
|
+
return value2 === void 0 ? [] : [[arg.name, value2]];
|
|
60920
|
+
}));
|
|
60921
|
+
const options = Object.fromEntries(command.options.flatMap((option) => {
|
|
60922
|
+
const value2 = input2.flags[option.name];
|
|
60923
|
+
if (option.type === "boolean") return [[option.name, value2 === true]];
|
|
60924
|
+
return typeof value2 === "string" || typeof value2 === "number" ? [[option.name, value2]] : [];
|
|
60925
|
+
}));
|
|
60926
|
+
return { name: command.name, args, options, terminal };
|
|
60927
|
+
}
|
|
60928
|
+
async function runOnNode(context, workspaceDir, input2, plugin, request) {
|
|
60929
|
+
const configured = workspaceConfig(workspaceDir, context.env)?.node ?? null;
|
|
60930
|
+
const name = chooseNode({ ...typeof input2.flags.node === "string" && { flag: input2.flags.node }, env: context.env, configured, workspaceDir }).name;
|
|
60931
|
+
const node2 = await liveNode(orbitalHome(context.env), name, context.fetch);
|
|
60932
|
+
if (!node2) throw new CliError({ code: "not-found", message: `The command ${plugin} ${request.name} needs the node ${name}, and it does not run.`, exitCode: ExitCode.Validation, nextCommand: `${commandPrefix(context.env)} up` });
|
|
60933
|
+
const response = await context.fetch(`${node2.url}/orbital/plugins/${encodeURIComponent(plugin)}/commands/${encodeURIComponent(request.name)}`, {
|
|
60934
|
+
method: "POST",
|
|
60935
|
+
headers: { authorization: `Bearer ${node2.token}`, "content-type": "application/json" },
|
|
60936
|
+
body: JSON.stringify(request)
|
|
60937
|
+
});
|
|
60938
|
+
const body2 = await response.json();
|
|
60939
|
+
if (!response.ok) throw new CliError({ code: response.status === 404 ? "not-found" : "transport", message: body2.error ?? `The node answered ${response.status}.`, exitCode: response.status === 404 ? ExitCode.Validation : ExitCode.Error });
|
|
60940
|
+
return PluginCommandResultSchema.parse(body2);
|
|
60941
|
+
}
|
|
60942
|
+
async function runInPlugin(context, workspaceDir, input2, plugin, command, terminal) {
|
|
60943
|
+
if (command.interactive && context.nonInteractive) {
|
|
60944
|
+
throw new CliError({ code: "invalid-input", message: `The command ${plugin} ${command.name} is interactive: it needs a terminal.`, exitCode: ExitCode.Validation, hint: "Run it in a terminal, without --non-interactive." });
|
|
60945
|
+
}
|
|
60946
|
+
const given = command.interactive ? { input: terminal.input, output: context.outputMode === "text" ? terminal.output : terminal.error, error: terminal.error } : void 0;
|
|
60947
|
+
const started = await startWorkspacePlugin(context, workspaceDir, plugin, given);
|
|
60948
|
+
try {
|
|
60949
|
+
return await started.command(requestOf(command, input2, given ? { tty: context.isTty } : null));
|
|
60950
|
+
} catch (error62) {
|
|
60951
|
+
if (error62 instanceof CliError) throw error62;
|
|
60952
|
+
const message = error62 instanceof Error ? error62.message : String(error62);
|
|
60953
|
+
throw new CliError({ code: "internal", message: error62 instanceof RpcError ? `The command ${plugin} ${command.name} failed: ${message}` : `The command ${plugin} ${command.name} got no answer: ${message}`, exitCode: ExitCode.Error });
|
|
60954
|
+
} finally {
|
|
60955
|
+
await started.close?.();
|
|
60956
|
+
}
|
|
60957
|
+
}
|
|
60958
|
+
function specOf(workspaceDir, plugin, command, terminal) {
|
|
60959
|
+
return {
|
|
60960
|
+
path: [plugin, command.name],
|
|
60961
|
+
summary: command.summary,
|
|
60962
|
+
group: CommandGroup.Packs,
|
|
60963
|
+
credential: "none",
|
|
60964
|
+
flags: [
|
|
60965
|
+
{ name: "workspace", type: "path", summary: `The project whose ${ORBITAL_FILE} names the plugin. Default: the current directory.` },
|
|
60966
|
+
...command.node ? [{ name: "node", type: "string", identifier: true, summary: "The node that runs the plugin. Default: the node of this workspace." }] : [],
|
|
60967
|
+
...command.options.map(flagOf)
|
|
60968
|
+
],
|
|
60969
|
+
positionals: command.args.map((arg) => ({ name: arg.name, type: "string", summary: arg.summary, ...arg.required && { required: true }, ...arg.variadic && { variadic: true } })),
|
|
60970
|
+
outputShape: { kind: "object", ref: "PluginCommandResult", fields: [{ name: "data", type: "object", summary: `What the command of the plugin ${plugin} answered.` }] },
|
|
60971
|
+
exitCodes: [0, 1, 2, 3, 5],
|
|
60972
|
+
routes: [],
|
|
60973
|
+
examples: [{ command: `orbital ${plugin} ${command.name}`, why: command.summary }],
|
|
60974
|
+
run: async (input2, context) => {
|
|
60975
|
+
const workspace = workspaceOf(input2, workspaceDir);
|
|
60976
|
+
const result2 = command.node ? await runOnNode(context, workspace, input2, plugin, requestOf(command, input2, null)) : await runInPlugin(context, workspace, input2, plugin, command, terminal);
|
|
60977
|
+
if (result2.text !== null) context.output.line(result2.text);
|
|
60978
|
+
if (result2.text === null && Object.keys(result2.data).length > 0) context.output.line(JSON.stringify(result2.data, null, 2));
|
|
60979
|
+
return { data: result2.data, ...result2.exitCode !== 0 && { exitCode: result2.exitCode } };
|
|
60980
|
+
}
|
|
60981
|
+
};
|
|
60982
|
+
}
|
|
60983
|
+
function pluginCommandSpecs(options) {
|
|
60984
|
+
const terminal = options.terminal ?? { input: 0, output: 1, error: 2 };
|
|
60985
|
+
return declaredPluginCommands(options.workspaceDir, options.env).filter(({ plugin }) => !options.reserved.includes(plugin)).flatMap(({ plugin, commands }) => commands.map((command) => specOf(options.workspaceDir, plugin, command, terminal)));
|
|
60986
|
+
}
|
|
60987
|
+
|
|
60676
60988
|
// src/core/cli/plugin.ts
|
|
60677
60989
|
function workspaceFlag() {
|
|
60678
60990
|
return { name: "workspace", type: "path", summary: `The project whose ${ORBITAL_FILE} names the plugin. Default: the current directory.` };
|
|
60679
60991
|
}
|
|
60680
|
-
function workspaceOf(input2) {
|
|
60681
|
-
return resolve14(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
60682
|
-
}
|
|
60683
60992
|
function sourceOrRefuse(context, input2) {
|
|
60684
60993
|
const workspaceDir = workspaceOf(input2);
|
|
60685
60994
|
const source = definitionSource(workspaceDir, context.env);
|
|
@@ -60703,6 +61012,25 @@ async function manifestOf(context, workspaceDir, name, source, root, config2) {
|
|
|
60703
61012
|
await plugin.close?.();
|
|
60704
61013
|
return plugin.manifest;
|
|
60705
61014
|
}
|
|
61015
|
+
function configOf(input2, known) {
|
|
61016
|
+
const file2 = typeof input2.flags["config-file"] === "string" ? resolve15(input2.flags["config-file"]) : null;
|
|
61017
|
+
const fromFile = file2 ? (0, import_yaml8.parse)(readFileSync29(file2, "utf8")) : {};
|
|
61018
|
+
if (fromFile === null || typeof fromFile !== "object" || Array.isArray(fromFile)) throw new CliError({ code: "invalid-input", message: `${file2} holds no object of config keys.`, exitCode: ExitCode.Validation, hint: "Write the config as YAML or JSON keys, such as channel: general." });
|
|
61019
|
+
const pairs = Array.isArray(input2.flags.config) ? input2.flags.config.map(String) : typeof input2.flags.config === "string" ? [input2.flags.config] : [];
|
|
61020
|
+
const fromPairs = Object.fromEntries(pairs.map((pair) => {
|
|
61021
|
+
const at = pair.indexOf("=");
|
|
61022
|
+
if (at <= 0) throw new CliError({ code: "invalid-input", message: `--config ${pair} is not key=value.`, exitCode: ExitCode.Validation, hint: "Write one key and its value, such as --config channel=general." });
|
|
61023
|
+
return [pair.slice(0, at), (0, import_yaml8.parse)(pair.slice(at + 1)) ?? ""];
|
|
61024
|
+
}));
|
|
61025
|
+
return { ...known, ...fromFile, ...fromPairs };
|
|
61026
|
+
}
|
|
61027
|
+
async function checkedManifest(context, workspaceDir, name, source, root, config2) {
|
|
61028
|
+
try {
|
|
61029
|
+
return await manifestOf(context, workspaceDir, name, source, root, config2);
|
|
61030
|
+
} catch (error62) {
|
|
61031
|
+
throw new CliError({ code: "invalid-input", message: `${name} did not start with the config ${JSON.stringify(config2)}, so no config names it: ${error62 instanceof Error ? error62.message : String(error62)}`, exitCode: ExitCode.Validation, hint: "Give the plugin its config with --config key=value or --config-file <file>." });
|
|
61032
|
+
}
|
|
61033
|
+
}
|
|
60706
61034
|
function permissionLines(manifest) {
|
|
60707
61035
|
const { network, secrets, models } = manifest.permissions;
|
|
60708
61036
|
return [
|
|
@@ -60718,7 +61046,9 @@ var add = () => ({
|
|
|
60718
61046
|
flags: [
|
|
60719
61047
|
workspaceFlag(),
|
|
60720
61048
|
{ name: "install-only", type: "boolean", summary: `Install the plugin on this machine only: do not name it in ${ORBITAL_FILE}.` },
|
|
60721
|
-
{ name: "user", type: "boolean", summary: `Add the plugin for you, in your user config, not in ${ORBITAL_FILE}: every project of yours loads it, after the plugins its ${ORBITAL_FILE} names.` }
|
|
61049
|
+
{ name: "user", type: "boolean", summary: `Add the plugin for you, in your user config, not in ${ORBITAL_FILE}: every project of yours loads it, after the plugins its ${ORBITAL_FILE} names.` },
|
|
61050
|
+
{ name: "config", type: "string", repeatable: true, summary: `One config key of the plugin, as key=value; the value reads as YAML, so 3 is a number and true a boolean. The plugin starts with it before ${ORBITAL_FILE} names it.` },
|
|
61051
|
+
{ name: "config-file", type: "path", summary: "A YAML or JSON file of config keys for the plugin. A --config key wins over the same key of the file." }
|
|
60722
61052
|
],
|
|
60723
61053
|
positionals: [
|
|
60724
61054
|
{ name: "plugin", type: "string", required: true, summary: "<name>@<marketplace>, npm:<package>, git:<url>, or a folder such as ./plugins/mine." }
|
|
@@ -60739,34 +61069,39 @@ var add = () => ({
|
|
|
60739
61069
|
examples: [
|
|
60740
61070
|
{ command: "orbital plugin add slack@community", why: "Install the slack plugin from the community marketplace, and use it in this workspace." },
|
|
60741
61071
|
{ command: "orbital plugin add ./plugins/mine", why: `Use a plugin you write in this project: ${ORBITAL_FILE} names it by its path.` },
|
|
60742
|
-
{ command: "orbital plugin add npm:@acme/orbital-discord --install-only", why: "Install a plugin from npm on this machine, for a workspace to name later." }
|
|
61072
|
+
{ command: "orbital plugin add npm:@acme/orbital-discord --install-only", why: "Install a plugin from npm on this machine, for a workspace to name later." },
|
|
61073
|
+
{ command: "orbital plugin add ./plugins/notify --config channel=general --config retries=3", why: "Add a plugin that checks its config when it starts." }
|
|
60743
61074
|
],
|
|
60744
61075
|
run: async (input2, context) => {
|
|
60745
61076
|
const home = orbitalHome(context.env);
|
|
60746
61077
|
const given = String(input2.positionals.plugin);
|
|
60747
61078
|
const ref = resolvePluginRef(home, given);
|
|
60748
61079
|
const copied = ref.kind !== "folder" || given.includes("@");
|
|
60749
|
-
const
|
|
60750
|
-
|
|
60751
|
-
|
|
61080
|
+
const install2 = copied ? installPlugin(home, ref) : null;
|
|
61081
|
+
const installed = install2?.dir ?? null;
|
|
61082
|
+
const name = install2?.name ?? ref.name ?? given;
|
|
61083
|
+
if (install2) context.output.step(`\u2713 installed ${name} in ${install2.dir}`);
|
|
61084
|
+
if (input2.flags["install-only"] === true) return { data: { name, source: given, installed, enabled: false, manifest: null } };
|
|
60752
61085
|
if (input2.flags.user === true) {
|
|
60753
61086
|
const source2 = ref.kind === "folder" && !given.includes("@") ? ref.dir : ref.kind === "npm" ? `npm:${ref.pkg}` : ref.kind === "git" ? `git:${ref.url}` : given;
|
|
60754
|
-
const
|
|
60755
|
-
context
|
|
61087
|
+
const config3 = configOf(input2, userPlugins(context.env)[name]?.config ?? {});
|
|
61088
|
+
const manifest2 = await checkedManifest(context, workspaceOf(input2), name, source2, null, config3);
|
|
61089
|
+
context.output.step(`${name} ${manifest2.version} reaches:`);
|
|
60756
61090
|
for (const line of permissionLines(manifest2)) context.output.step(line);
|
|
60757
|
-
setUserPlugin(context.env,
|
|
60758
|
-
context.output.step(`\u2713 your user config names ${
|
|
60759
|
-
return { data: { name
|
|
61091
|
+
setUserPlugin(context.env, name, { source: source2, enabled: true, config: config3, env: [] });
|
|
61092
|
+
context.output.step(`\u2713 your user config names ${name}: source ${source2}`);
|
|
61093
|
+
return { data: { name, source: source2, installed, enabled: true, manifest: manifest2 } };
|
|
60760
61094
|
}
|
|
60761
61095
|
const definition = sourceOrRefuse(context, input2);
|
|
60762
61096
|
const root = definition.root;
|
|
60763
61097
|
const source = sourceOf(ref, given, root);
|
|
60764
|
-
const
|
|
60765
|
-
context
|
|
61098
|
+
const config2 = configOf(input2, sourceConfig(definition).plugins[name]?.config ?? {});
|
|
61099
|
+
const manifest = await checkedManifest(context, workspaceOf(input2), name, source, root, config2);
|
|
61100
|
+
context.output.step(`${name} ${manifest.version} reaches:`);
|
|
60766
61101
|
for (const line of permissionLines(manifest)) context.output.step(line);
|
|
60767
|
-
setPluginEntry(definition.configFile,
|
|
60768
|
-
context.output.step(`\u2713 ${basename9(definition.configFile)} names ${
|
|
60769
|
-
return { data: { name
|
|
61102
|
+
setPluginEntry(definition.configFile, name, { source, enabled: true, config: config2, env: [] });
|
|
61103
|
+
context.output.step(`\u2713 ${basename9(definition.configFile)} names ${name}: source ${source}`);
|
|
61104
|
+
return { data: { name, source, installed, enabled: true, manifest } };
|
|
60770
61105
|
}
|
|
60771
61106
|
});
|
|
60772
61107
|
var list3 = () => ({
|
|
@@ -60842,57 +61177,6 @@ var remove = () => ({
|
|
|
60842
61177
|
return { data: { name, unnamed, uninstalled } };
|
|
60843
61178
|
}
|
|
60844
61179
|
});
|
|
60845
|
-
var runCommand = () => ({
|
|
60846
|
-
path: ["plugin", "run"],
|
|
60847
|
-
summary: "Run one of a plugin's own commands, such as a login, and print what it answers.",
|
|
60848
|
-
credential: "none",
|
|
60849
|
-
flags: [workspaceFlag()],
|
|
60850
|
-
positionals: [
|
|
60851
|
-
{ name: "plugin", type: "string", required: true, identifier: true, summary: `A plugin ${ORBITAL_FILE} names.` },
|
|
60852
|
-
{ name: "command", type: "string", required: true, summary: "The plugin's command." },
|
|
60853
|
-
{ name: "args", type: "string", summary: "Arguments for the command, after `--`.", passthrough: true }
|
|
60854
|
-
],
|
|
60855
|
-
outputShape: { kind: "object", ref: "PluginCommand", fields: [{ name: "output", type: "string", summary: "What the command answered." }] },
|
|
60856
|
-
exitCodes: [0, 1, 3],
|
|
60857
|
-
routes: [],
|
|
60858
|
-
examples: [{ command: "orbital plugin run slack login", why: "Run the slack plugin's login command." }],
|
|
60859
|
-
run: async (input2, context) => {
|
|
60860
|
-
const name = String(input2.positionals.plugin);
|
|
60861
|
-
const plugin = await startNamed(context, input2, name);
|
|
60862
|
-
try {
|
|
60863
|
-
const command = String(input2.positionals.command);
|
|
60864
|
-
if (!plugin.manifest.commands.some((one) => one.name === command)) {
|
|
60865
|
-
throw new CliError({ code: "not-found", message: `The plugin ${name} has no command ${command}.`, exitCode: ExitCode.Validation, hint: plugin.manifest.commands.length > 0 ? `Its commands: ${plugin.manifest.commands.map((one) => one.name).join(", ")}.` : "It has no commands." });
|
|
60866
|
-
}
|
|
60867
|
-
const output2 = await plugin.command(command, [...input2.passthrough]);
|
|
60868
|
-
context.output.line(output2);
|
|
60869
|
-
return { data: { output: output2 } };
|
|
60870
|
-
} finally {
|
|
60871
|
-
await plugin.close?.();
|
|
60872
|
-
}
|
|
60873
|
-
}
|
|
60874
|
-
});
|
|
60875
|
-
async function startNamed(context, input2, name) {
|
|
60876
|
-
const root = definitionSource(workspaceOf(input2), context.env)?.root ?? null;
|
|
60877
|
-
const config2 = workspaceConfig(workspaceOf(input2), context.env);
|
|
60878
|
-
const entry = config2?.plugins[name];
|
|
60879
|
-
if (!config2 || !entry) throw new CliError({ code: "not-found", message: `No plugin ${name} is named in ${ORBITAL_FILE} or found in ${PROJECT_PLUGINS_DIR}.`, exitCode: ExitCode.Validation, nextCommand: `${commandPrefix(context.env)} plugin add ${name}@<marketplace>` });
|
|
60880
|
-
const secrets = new PluginSecrets(context.env);
|
|
60881
|
-
return startRemotePlugin({
|
|
60882
|
-
name,
|
|
60883
|
-
source: pluginSource(name, entry),
|
|
60884
|
-
config: entry.config,
|
|
60885
|
-
entryEnv: entry.env,
|
|
60886
|
-
node: { name: `plugin-${name}`, workspaceDir: workspaceOf(input2), harness: defaultHarness(context.env), harnesses: [...RUNNER_HARNESSES], once: true, idleExitMs: null, project: null },
|
|
60887
|
-
roots: { harnessRoot: root, home: orbitalHome(context.env) },
|
|
60888
|
-
offline: config2.offline,
|
|
60889
|
-
policy: new PolicyChain([]),
|
|
60890
|
-
env: context.env,
|
|
60891
|
-
log: (level, message) => level === "info" ? context.output.step(message) : context.output.warn(message),
|
|
60892
|
-
secret: async (secret) => secrets.get(name, secret),
|
|
60893
|
-
restarts: 0
|
|
60894
|
-
});
|
|
60895
|
-
}
|
|
60896
61180
|
var check2 = () => ({
|
|
60897
61181
|
path: ["plugin", "check"],
|
|
60898
61182
|
summary: "Run the conformance checks of each port a plugin provides, so any orbital host can use it.",
|
|
@@ -60918,8 +61202,8 @@ var check2 = () => ({
|
|
|
60918
61202
|
],
|
|
60919
61203
|
run: async (input2, context) => {
|
|
60920
61204
|
const name = String(input2.positionals.plugin);
|
|
60921
|
-
const fixtures = typeof input2.flags.fixtures === "string" ? JSON.parse(
|
|
60922
|
-
const plugin = await
|
|
61205
|
+
const fixtures = typeof input2.flags.fixtures === "string" ? JSON.parse(readFileSync29(input2.flags.fixtures, "utf8")) : {};
|
|
61206
|
+
const plugin = await startWorkspacePlugin(context, workspaceOf(input2), name);
|
|
60923
61207
|
try {
|
|
60924
61208
|
const results = await runConformance(plugin.ports, fixtures);
|
|
60925
61209
|
const mark = { pass: "\u2713", fail: "\u2717", skip: "\u25CB" };
|
|
@@ -61047,13 +61331,13 @@ var secretRemove = {
|
|
|
61047
61331
|
return { data: { plugin, name } };
|
|
61048
61332
|
}
|
|
61049
61333
|
};
|
|
61050
|
-
var pluginCommands = () => [add(), list3(), remove(),
|
|
61334
|
+
var pluginCommands = () => [add(), list3(), remove(), check2(), marketplaceAdd, marketplaceList, marketplaceUpdate, marketplaceRemove, secretSet, secretRemove];
|
|
61051
61335
|
|
|
61052
61336
|
// src/core/cli/plugin-new.ts
|
|
61053
|
-
import { existsSync as
|
|
61054
|
-
import { dirname as dirname16, join as
|
|
61337
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync22, readdirSync as readdirSync20, writeFileSync as writeFileSync20 } from "node:fs";
|
|
61338
|
+
import { dirname as dirname16, join as join38, relative as relative7, resolve as resolve16 } from "node:path";
|
|
61055
61339
|
var PLUGIN_PORTS = ["channel", "store", "workspace", "harness", "panel", "policy", "telemetry", "actions", "workflows", "command"];
|
|
61056
|
-
var SDK_VERSION = true ? `^${"0.0.
|
|
61340
|
+
var SDK_VERSION = true ? `^${"0.0.4"}` : "*";
|
|
61057
61341
|
var ESBUILD_VERSION = "^0.25.0";
|
|
61058
61342
|
var TYPES_NODE_VERSION = "^20.19.43";
|
|
61059
61343
|
var HARNESS_ID = /^[a-z][a-z0-9-]{0,31}$/;
|
|
@@ -61432,7 +61716,7 @@ export function plugin(context: PluginContext): PluginImplementation {
|
|
|
61432
61716
|
switch (event.kind) {
|
|
61433
61717
|
case 'mission.state': {
|
|
61434
61718
|
const known = graph.missions.find((mission) => mission.missionId === event.mission);
|
|
61435
|
-
const state = { missionId: event.mission, team: event.team, state: event.to, sessionId: event.sessionId, lead: event.lead,
|
|
61719
|
+
const state = { missionId: event.mission, team: event.team, state: event.to, sessionId: event.sessionId, lead: event.lead, receivers: event.receivers, givenBy: known?.givenBy ?? null, question: event.question, result: event.result, updatedAt: event.at };
|
|
61436
61720
|
graph.missions = [...graph.missions.filter((mission) => mission.missionId !== event.mission), state];
|
|
61437
61721
|
return;
|
|
61438
61722
|
}
|
|
@@ -61573,7 +61857,7 @@ export function plugin(_context: PluginContext): PluginImplementation {
|
|
|
61573
61857
|
case "command":
|
|
61574
61858
|
return {
|
|
61575
61859
|
summary: `An orbital plugin with a command: ${name}.`,
|
|
61576
|
-
plugin: `${header("one command. A person runs it with `orbital
|
|
61860
|
+
plugin: `${header("one command. A person runs it with `orbital " + name + " hello [name]`.\n * orbital-plugin.json declares the same command, so orbital lists it without starting the plugin.")}import { defineCommand, type PluginContext, type PluginImplementation } from '@kaiba-cloud/orbital-sdk';
|
|
61577
61861
|
|
|
61578
61862
|
export const NAME = '${name}';
|
|
61579
61863
|
|
|
@@ -61583,17 +61867,19 @@ export function plugin(context: PluginContext): PluginImplementation {
|
|
|
61583
61867
|
version: '0.1.0',
|
|
61584
61868
|
permissions: { network: [], secrets: [], models: [] },
|
|
61585
61869
|
ports: {},
|
|
61586
|
-
commands:
|
|
61587
|
-
|
|
61588
|
-
|
|
61589
|
-
|
|
61590
|
-
|
|
61591
|
-
|
|
61870
|
+
commands: [
|
|
61871
|
+
defineCommand({
|
|
61872
|
+
name: 'hello',
|
|
61873
|
+
summary: 'Greet the name given, or the workspace.',
|
|
61874
|
+
args: [{ name: 'name', summary: 'The name to greet.' }],
|
|
61875
|
+
run: async ({ args }) => \`Hello, \${args.name ?? context.init.node.workspaceDir}.\`,
|
|
61876
|
+
}),
|
|
61877
|
+
],
|
|
61592
61878
|
};
|
|
61593
61879
|
}
|
|
61594
61880
|
`,
|
|
61595
61881
|
fixtures: {},
|
|
61596
|
-
test: ` assert.equal(await implementation.commands?.
|
|
61882
|
+
test: ` assert.equal(await implementation.commands?.[0]?.run({ args: { name: 'Ada' }, options: {}, terminal: null }), 'Hello, Ada.');`
|
|
61597
61883
|
};
|
|
61598
61884
|
}
|
|
61599
61885
|
}
|
|
@@ -61627,10 +61913,11 @@ ${template.test ? `${template.test}
|
|
|
61627
61913
|
` : ""}});
|
|
61628
61914
|
`;
|
|
61629
61915
|
}
|
|
61916
|
+
var COMMAND_DECLARATION = { name: "hello", summary: "Greet the name given, or the workspace.", args: [{ name: "name", summary: "The name to greet." }] };
|
|
61630
61917
|
function pluginFiles(name, port) {
|
|
61631
61918
|
const template = templateOf(name, port);
|
|
61632
61919
|
const files = {
|
|
61633
|
-
"orbital-plugin.json": `${JSON.stringify({ name, command: ["node", "dist/plugin.js"], ...port === "harness" && { harnesses: [{ id: name, folder: `.${name}`, instructions: `${name.toUpperCase()}.md`, agents: "agents", skills: "skills" }] } }, null, 2)}
|
|
61920
|
+
"orbital-plugin.json": `${JSON.stringify({ name, command: ["node", "dist/plugin.js"], ...port === "harness" && { harnesses: [{ id: name, folder: `.${name}`, instructions: `${name.toUpperCase()}.md`, agents: "agents", skills: "skills" }] }, ...port === "command" && { commands: [COMMAND_DECLARATION] } }, null, 2)}
|
|
61634
61921
|
`,
|
|
61635
61922
|
"package.json": `${JSON.stringify({
|
|
61636
61923
|
name,
|
|
@@ -61688,14 +61975,14 @@ var pluginNewCommand = {
|
|
|
61688
61975
|
if (port === "harness" && !HARNESS_ID.test(name)) {
|
|
61689
61976
|
throw new CliError({ code: "invalid-input", message: `"${name}" is not a harness id.`, exitCode: ExitCode.Validation, hint: "A harness plugin's name starts with a letter and has at most 32 characters." });
|
|
61690
61977
|
}
|
|
61691
|
-
const dir =
|
|
61692
|
-
if (
|
|
61978
|
+
const dir = resolve16(typeof input2.flags.dir === "string" ? input2.flags.dir : name);
|
|
61979
|
+
if (existsSync31(dir) && readdirSync20(dir).length > 0) {
|
|
61693
61980
|
throw new CliError({ code: "conflict", message: `${dir} is not empty.`, exitCode: ExitCode.Validation, hint: "Name a new folder with --dir." });
|
|
61694
61981
|
}
|
|
61695
61982
|
const files = pluginFiles(name, port);
|
|
61696
61983
|
for (const [path3, content] of Object.entries(files)) {
|
|
61697
|
-
mkdirSync22(dirname16(
|
|
61698
|
-
writeFileSync20(
|
|
61984
|
+
mkdirSync22(dirname16(join38(dir, path3)), { recursive: true });
|
|
61985
|
+
writeFileSync20(join38(dir, path3), content);
|
|
61699
61986
|
}
|
|
61700
61987
|
const program = commandPrefix(context.env);
|
|
61701
61988
|
const shown = relative7(process.cwd(), dir) || ".";
|
|
@@ -61838,8 +62125,8 @@ If the browser does not return here in ${CALLBACK_WAIT_MS / 6e4} minutes, paste
|
|
|
61838
62125
|
${url2}
|
|
61839
62126
|
Then paste the code, or the full URL the browser ends on.`);
|
|
61840
62127
|
},
|
|
61841
|
-
onManualCodeInput: () => new Promise((
|
|
61842
|
-
timer = setTimeout(() =>
|
|
62128
|
+
onManualCodeInput: () => new Promise((resolve26) => {
|
|
62129
|
+
timer = setTimeout(() => resolve26(""), opened2 ? CALLBACK_WAIT_MS : 0);
|
|
61843
62130
|
timer.unref();
|
|
61844
62131
|
}),
|
|
61845
62132
|
onPrompt: async (prompt) => (await context.ask(`${prompt.message} `))?.trim() ?? "",
|
|
@@ -61932,12 +62219,12 @@ function createProviderCommands(signIn = loginAnthropic) {
|
|
|
61932
62219
|
}
|
|
61933
62220
|
|
|
61934
62221
|
// src/core/cli/run.ts
|
|
61935
|
-
import { resolve as
|
|
62222
|
+
import { resolve as resolve18 } from "node:path";
|
|
61936
62223
|
|
|
61937
62224
|
// src/core/local/local-run.ts
|
|
61938
62225
|
import { randomBytes as randomBytes4, randomUUID as randomUUID8 } from "node:crypto";
|
|
61939
62226
|
import { mkdirSync as mkdirSync23, rmSync as rmSync12 } from "node:fs";
|
|
61940
|
-
import { join as
|
|
62227
|
+
import { join as join39, resolve as resolve17 } from "node:path";
|
|
61941
62228
|
|
|
61942
62229
|
// src/core/local/interrupt.ts
|
|
61943
62230
|
var EXIT_CODE = { SIGINT: 130, SIGTERM: 143 };
|
|
@@ -61960,7 +62247,7 @@ function cleanupOnInterrupt(cleanup, options = {}) {
|
|
|
61960
62247
|
async function runLocal(context, request, plugins) {
|
|
61961
62248
|
const { workspaceDir } = request;
|
|
61962
62249
|
const home = orbitalHome(context.env);
|
|
61963
|
-
const sessionsDir =
|
|
62250
|
+
const sessionsDir = join39(home, "sessions");
|
|
61964
62251
|
mkdirSync23(sessionsDir, { recursive: true, mode: 448 });
|
|
61965
62252
|
const resumed = request.resumeSessionId ? readSessionRecord(sessionsDir, request.resumeSessionId) : null;
|
|
61966
62253
|
if (request.resumeSessionId && !resumed) {
|
|
@@ -61990,9 +62277,9 @@ async function runLocal(context, request, plugins) {
|
|
|
61990
62277
|
let channels = null;
|
|
61991
62278
|
const asks = new PersonDesk(async (question, name) => await channels?.askPerson(`${name} asks: ${question}`, null) ?? null);
|
|
61992
62279
|
const policy = new PolicyChain(ports.policies(), asks.ask);
|
|
61993
|
-
channels = new ChannelHub({ context, ports, policy, self: nodeName, stateDir:
|
|
62280
|
+
channels = new ChannelHub({ context, ports, policy, self: nodeName, stateDir: join39(home, "channels", nodeName), plugins: () => definition.config.plugins });
|
|
61994
62281
|
const models = modelGatewayOf(context, policy, remote);
|
|
61995
|
-
const actions = nodeActions({ ports: () => ports, policy: () => policy, definition: { workspaceDir:
|
|
62282
|
+
const actions = nodeActions({ ports: () => ports, policy: () => policy, definition: { workspaceDir: resolve17(workspaceDir), definition }, span: (span2) => host.telemetry.span(span2), now: context.now });
|
|
61996
62283
|
const agentName = request.agent ?? resumed?.agent ?? null;
|
|
61997
62284
|
const target = await resolveTarget(definition, agentName, ports);
|
|
61998
62285
|
const harness = request.harness ?? resumed?.harness ?? defaultHarness(context.env);
|
|
@@ -62053,7 +62340,7 @@ async function runLocal(context, request, plugins) {
|
|
|
62053
62340
|
const result2 = chat ? await chatTurns(chat, host, { host, asks, panels: () => panelsOf(ownPanels, ports.plugins), sessionId, agent: target.local?.name ?? target.ref.name, harness, model: target.local?.model ?? null, workspaceDir, node: nodeName }) : request.interactive ? await interactiveTurn(context, request, { host, ports, policy, definition, target, sessionId, harness, harnessArgs, credentials, home, sessionsDir, node: { url: `${node2.info.url}/a2a`, token: node2.sessionToken(sessionId) } }) : await headlessTurn(context, request, host, { sessionId, agent: target.local?.name ?? target.ref.name, harness });
|
|
62054
62341
|
if (result2 !== null) context.output.line(result2);
|
|
62055
62342
|
const cli = commandPrefix(context.env);
|
|
62056
|
-
context.output.step(`\u2713 transcript: ${
|
|
62343
|
+
context.output.step(`\u2713 transcript: ${join39(sessionsDir, sessionId, "conversation.jsonl")}`);
|
|
62057
62344
|
context.output.step(`\u2713 resume with: ${cli} run --resume ${sessionId}`);
|
|
62058
62345
|
return {
|
|
62059
62346
|
sessionId,
|
|
@@ -62061,7 +62348,7 @@ async function runLocal(context, request, plugins) {
|
|
|
62061
62348
|
done: true,
|
|
62062
62349
|
harness,
|
|
62063
62350
|
result: result2,
|
|
62064
|
-
transcript:
|
|
62351
|
+
transcript: join39(sessionsDir, sessionId, "conversation.jsonl"),
|
|
62065
62352
|
interactive: request.interactive
|
|
62066
62353
|
};
|
|
62067
62354
|
} finally {
|
|
@@ -62121,7 +62408,7 @@ async function interactiveTurn(context, request, run2) {
|
|
|
62121
62408
|
const info = sessionInfoOf(run2.sessionId, run2.target, { node: run2.host.node, agent: run2.target.local?.name ?? run2.target.ref.name, parentSessionId: null, workspaceDir: request.workspaceDir, harness: run2.harness, team: null, mission: null, definition: request.useDefinition ? run2.definition : null });
|
|
62122
62409
|
const opened2 = await openSession(run2.ports, run2.policy, info);
|
|
62123
62410
|
const record2 = run2.host.attach(info, opened2.sinks, { agent: info.agent, harness: run2.harness, workspaceDir: request.workspaceDir });
|
|
62124
|
-
const runDir =
|
|
62411
|
+
const runDir = join39(run2.home, "runs", randomUUID8());
|
|
62125
62412
|
mkdirSync23(runDir, { recursive: true, mode: 448 });
|
|
62126
62413
|
let usage = [];
|
|
62127
62414
|
try {
|
|
@@ -62228,7 +62515,7 @@ function createRunCommand(options) {
|
|
|
62228
62515
|
const terminal = log ? await options.terminal() : null;
|
|
62229
62516
|
const data = await runLocal(log ? { ...context, output: log.output(context.output) } : context, {
|
|
62230
62517
|
agent,
|
|
62231
|
-
workspaceDir:
|
|
62518
|
+
workspaceDir: resolve18(typeof input2.flags.workspace === "string" ? input2.flags.workspace : "."),
|
|
62232
62519
|
prompt,
|
|
62233
62520
|
useDefinition: true,
|
|
62234
62521
|
follow: false,
|
|
@@ -62244,24 +62531,24 @@ function createRunCommand(options) {
|
|
|
62244
62531
|
}
|
|
62245
62532
|
|
|
62246
62533
|
// src/core/cli/session.ts
|
|
62247
|
-
import { resolve as
|
|
62534
|
+
import { resolve as resolve19 } from "node:path";
|
|
62248
62535
|
|
|
62249
62536
|
// src/core/local/attachments.ts
|
|
62250
|
-
import { existsSync as
|
|
62251
|
-
import { join as
|
|
62537
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync30, writeFileSync as writeFileSync21 } from "node:fs";
|
|
62538
|
+
import { join as join40 } from "node:path";
|
|
62252
62539
|
function fileOf(home, externalId) {
|
|
62253
|
-
return
|
|
62540
|
+
return join40(home, "attachments", `${encodeURIComponent(externalId)}.json`);
|
|
62254
62541
|
}
|
|
62255
62542
|
function writeAttachment(home, externalId, node2) {
|
|
62256
|
-
mkdirSync24(
|
|
62543
|
+
mkdirSync24(join40(home, "attachments"), { recursive: true, mode: 448 });
|
|
62257
62544
|
writeFileSync21(fileOf(home, externalId), `${JSON.stringify({ node: node2 })}
|
|
62258
62545
|
`, { mode: 384 });
|
|
62259
62546
|
}
|
|
62260
62547
|
function readAttachment(home, externalId) {
|
|
62261
62548
|
const file2 = fileOf(home, externalId);
|
|
62262
|
-
if (!
|
|
62549
|
+
if (!existsSync33(file2)) return null;
|
|
62263
62550
|
try {
|
|
62264
|
-
const parsed = JSON.parse(
|
|
62551
|
+
const parsed = JSON.parse(readFileSync30(file2, "utf8"));
|
|
62265
62552
|
return typeof parsed === "object" && parsed !== null && "node" in parsed && typeof parsed.node === "string" ? parsed.node : null;
|
|
62266
62553
|
} catch {
|
|
62267
62554
|
return null;
|
|
@@ -62329,7 +62616,7 @@ async function joinMission(context, attached, missionId) {
|
|
|
62329
62616
|
const body2 = await response.json();
|
|
62330
62617
|
if (!response.ok) {
|
|
62331
62618
|
const error62 = typeof body2 === "object" && body2 !== null && "error" in body2 ? String(body2.error) : `The node answered ${response.status}.`;
|
|
62332
|
-
throw new CliError({ code: "invalid-input", message: `The node refused to make this session
|
|
62619
|
+
throw new CliError({ code: "invalid-input", message: `The node refused to make this session a decision receiver of ${missionId}: ${error62}`, exitCode: ExitCode.Validation });
|
|
62333
62620
|
}
|
|
62334
62621
|
return MissionRecordSchema.parse(body2);
|
|
62335
62622
|
}
|
|
@@ -62409,7 +62696,7 @@ var sessionAttachCommand = {
|
|
|
62409
62696
|
{ name: "node", type: "string", summary: "The node to join. Default: ORBITAL_NODE, then the node key of the definition, then the node of the folder." },
|
|
62410
62697
|
{ name: "team", type: "string", summary: "The team the session joins. Sessions of one team are peers." },
|
|
62411
62698
|
{ name: "external-id", type: "string", summary: "The id the outside program gives the session. With it, the command attaches again after a node restarts." },
|
|
62412
|
-
{ name: "mission", type: "string", summary: "A mission id: join the node that runs it, from any folder, as
|
|
62699
|
+
{ name: "mission", type: "string", summary: "A mission id: join the node that runs it, from any folder, as a decision receiver of the mission, so its questions come to this session." }
|
|
62413
62700
|
],
|
|
62414
62701
|
positionals: [],
|
|
62415
62702
|
outputShape: {
|
|
@@ -62426,7 +62713,7 @@ var sessionAttachCommand = {
|
|
|
62426
62713
|
examples: [
|
|
62427
62714
|
{ command: "orbital session attach --harness claude-code --output ndjson", why: "Join the node of this folder, and print each delivery as a record." },
|
|
62428
62715
|
{ command: "orbital session attach --node shared --team review --output ndjson", why: "Join the node shared, as a member of the team review." },
|
|
62429
|
-
{ command: "orbital session attach --mission build/pr-7 --output ndjson", why: "Join the node that runs the mission build/pr-7 as
|
|
62716
|
+
{ command: "orbital session attach --mission build/pr-7 --output ndjson", why: "Join the node that runs the mission build/pr-7 as a decision receiver, and print each question of its lead as a record." }
|
|
62430
62717
|
],
|
|
62431
62718
|
run: async (input2, context) => {
|
|
62432
62719
|
const harness = typeof input2.flags.harness === "string" ? input2.flags.harness : "external";
|
|
@@ -62438,7 +62725,7 @@ var sessionAttachCommand = {
|
|
|
62438
62725
|
const found = await keepAttached(context, {
|
|
62439
62726
|
harness,
|
|
62440
62727
|
agent: typeof input2.flags.agent === "string" ? input2.flags.agent : harness,
|
|
62441
|
-
workspaceDir:
|
|
62728
|
+
workspaceDir: resolve19(typeof input2.flags.workspace === "string" ? input2.flags.workspace : "."),
|
|
62442
62729
|
...typeof input2.flags.node === "string" && { node: input2.flags.node },
|
|
62443
62730
|
team: typeof input2.flags.team === "string" ? input2.flags.team : null,
|
|
62444
62731
|
externalId: typeof input2.flags["external-id"] === "string" ? input2.flags["external-id"] : null,
|
|
@@ -62451,6 +62738,8 @@ var sessionAttachCommand = {
|
|
|
62451
62738
|
delivery: (event) => {
|
|
62452
62739
|
const delivery = DeliverySchema.safeParse(event);
|
|
62453
62740
|
if (delivery.success) context.output.record({ type: StreamRecordType.Event, ...delivery.data });
|
|
62741
|
+
const answered = AnsweredSchema.safeParse(event);
|
|
62742
|
+
if (answered.success) context.output.record({ type: StreamRecordType.Event, ...answered.data });
|
|
62454
62743
|
},
|
|
62455
62744
|
detached: (reason) => {
|
|
62456
62745
|
context.output.record({ type: StreamRecordType.Event, kind: "detached", reason });
|
|
@@ -62538,8 +62827,8 @@ var remove3 = {
|
|
|
62538
62827
|
var workspaceCommands = [list5, show, remove3];
|
|
62539
62828
|
|
|
62540
62829
|
// src/core/cli/up.ts
|
|
62541
|
-
import { existsSync as
|
|
62542
|
-
import { resolve as
|
|
62830
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
62831
|
+
import { resolve as resolve20 } from "node:path";
|
|
62543
62832
|
|
|
62544
62833
|
// src/core/cli/node-control.ts
|
|
62545
62834
|
var STOP_WAIT_MS = 3e4;
|
|
@@ -62613,7 +62902,7 @@ function groupOf(state) {
|
|
|
62613
62902
|
}
|
|
62614
62903
|
}
|
|
62615
62904
|
function missionGroup(mission) {
|
|
62616
|
-
return
|
|
62905
|
+
return waitsOnDecision(mission) ? "waiting" : groupOf(mission.state);
|
|
62617
62906
|
}
|
|
62618
62907
|
var SESSIONS_PER_MEMBER = 3;
|
|
62619
62908
|
var RUNNING_SHOWN = 5;
|
|
@@ -63092,7 +63381,7 @@ async function liveView(context, nodes, signal, terminal, home) {
|
|
|
63092
63381
|
for (const line of events(viewed, states.get(one.node), now)) context.output.line(line);
|
|
63093
63382
|
states.set(one.node, now);
|
|
63094
63383
|
}
|
|
63095
|
-
await Promise.race([context.sleep(REFRESH_MS), new Promise((
|
|
63384
|
+
await Promise.race([context.sleep(REFRESH_MS), new Promise((resolve26) => signal.addEventListener("abort", () => resolve26(), { once: true }))]);
|
|
63096
63385
|
}
|
|
63097
63386
|
context.output.line(closing);
|
|
63098
63387
|
}
|
|
@@ -63101,7 +63390,7 @@ async function liveView(context, nodes, signal, terminal, home) {
|
|
|
63101
63390
|
var WORKSPACE_FLAG = { name: "workspace", type: "path", summary: "The project. Default: the current directory." };
|
|
63102
63391
|
var TEAMS = { name: "teams", type: "string", variadic: true, summary: "Only the nodes of these teams. Default: every node of the project." };
|
|
63103
63392
|
async function planOf(context, input2) {
|
|
63104
|
-
const workspaceDir =
|
|
63393
|
+
const workspaceDir = resolve20(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
63105
63394
|
const { definition, refusals } = await loadDefinitionPartial(workspaceDir, context.env);
|
|
63106
63395
|
for (const refusal of refusals) context.output.warn(refusal.message);
|
|
63107
63396
|
const project = projectNodeOf(definition, workspaceDir);
|
|
@@ -63248,7 +63537,7 @@ var statusCommand = {
|
|
|
63248
63537
|
for (const { charters: _charters, named: _named, ...planned } of nodes) {
|
|
63249
63538
|
const log = nodeLogPath(home, planned.node);
|
|
63250
63539
|
const supervisor = readSupervisor(home, planned.node);
|
|
63251
|
-
if (
|
|
63540
|
+
if (existsSync34(log)) logs.push({ node: planned.node, log, lastCrash: supervisor?.crashes.at(-1) ?? null, lastExit: supervisor?.lastExit ?? null, gaveUp: supervisor?.gaveUp ?? null });
|
|
63252
63541
|
const running = await liveNode(home, planned.node, context.fetch);
|
|
63253
63542
|
const served = running ? await servedTeams(context, running) : null;
|
|
63254
63543
|
for (const note of served ?? []) if (note.problems.length > 0 || note.retiring) notes.push({ node: planned.node, ...note });
|
|
@@ -63281,7 +63570,7 @@ var downCommand = {
|
|
|
63281
63570
|
const { workspaceDir, nodes } = await planOf(context, input2);
|
|
63282
63571
|
const home = orbitalHome(context.env);
|
|
63283
63572
|
const rows = [];
|
|
63284
|
-
const separate2 = (await liveNodes(home, context.fetch)).filter((info) =>
|
|
63573
|
+
const separate2 = (await liveNodes(home, context.fetch)).filter((info) => resolve20(info.workspaceDir) === resolve20(workspaceDir) && nodes.some((planned) => new RegExp(`^${planned.node}-[0-9]+$`).test(info.name)));
|
|
63285
63574
|
const targets = [...nodes.map(({ charters: _charters, named: _named, ...planned }) => planned), ...separate2.map((info) => ({ node: info.name, teams: nodes.find((planned) => info.name.startsWith(`${planned.node}-`))?.teams ?? [] }))];
|
|
63286
63575
|
for (const planned of targets) {
|
|
63287
63576
|
const running = await liveNode(home, planned.node, context.fetch);
|
|
@@ -63340,7 +63629,7 @@ var createJoinCommand = (options) => ({
|
|
|
63340
63629
|
}
|
|
63341
63630
|
let charters = [];
|
|
63342
63631
|
try {
|
|
63343
|
-
const workspaceDir =
|
|
63632
|
+
const workspaceDir = resolve20(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
63344
63633
|
charters = (await loadDefinition(workspaceDir, context.env)).teams;
|
|
63345
63634
|
} catch {
|
|
63346
63635
|
charters = [];
|
|
@@ -63365,9 +63654,9 @@ function createUpCommands(options) {
|
|
|
63365
63654
|
// src/core/cli/service.ts
|
|
63366
63655
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
63367
63656
|
import { createHash as createHash10 } from "node:crypto";
|
|
63368
|
-
import { existsSync as
|
|
63657
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync25, rmSync as rmSync13, writeFileSync as writeFileSync22 } from "node:fs";
|
|
63369
63658
|
import { homedir as homedir8 } from "node:os";
|
|
63370
|
-
import { join as
|
|
63659
|
+
import { join as join41, resolve as resolve21 } from "node:path";
|
|
63371
63660
|
var exec = (command, args) => {
|
|
63372
63661
|
const result2 = spawnSync5(command, args, { encoding: "utf8" });
|
|
63373
63662
|
return { status: result2.status, output: `${result2.stdout ?? ""}${result2.stderr ?? ""}${result2.error?.message ?? ""}` };
|
|
@@ -63385,7 +63674,7 @@ function serviceSpec(env, workspaceDir) {
|
|
|
63385
63674
|
workspaceDir,
|
|
63386
63675
|
program: [process.execPath, installedProgram(), ...mountPath(), "up", "--detach", "--workspace", workspaceDir],
|
|
63387
63676
|
env: Object.fromEntries(KEPT_ENV.flatMap((name) => env[name] ? [[name, env[name]]] : [])),
|
|
63388
|
-
log:
|
|
63677
|
+
log: join41(orbitalHome(env), "logs", `service-${hash2}.log`)
|
|
63389
63678
|
};
|
|
63390
63679
|
}
|
|
63391
63680
|
function xml(text3) {
|
|
@@ -63448,7 +63737,7 @@ function userHome(env) {
|
|
|
63448
63737
|
return env.HOME ?? homedir8();
|
|
63449
63738
|
}
|
|
63450
63739
|
function fileOf2(platform, env, label) {
|
|
63451
|
-
return platform === "launchd" ?
|
|
63740
|
+
return platform === "launchd" ? join41(userHome(env), "Library", "LaunchAgents", `${label}.plist`) : join41(env.XDG_CONFIG_HOME ?? join41(userHome(env), ".config"), "systemd", "user", `${label}.service`);
|
|
63452
63741
|
}
|
|
63453
63742
|
function domain2() {
|
|
63454
63743
|
return `gui/${process.getuid?.() ?? 0}`;
|
|
@@ -63461,13 +63750,13 @@ function serviceState(platform, env, workspaceDir, run2 = exec) {
|
|
|
63461
63750
|
const { label } = serviceSpec(env, workspaceDir);
|
|
63462
63751
|
const file2 = fileOf2(platform, env, label);
|
|
63463
63752
|
const loaded = platform === "launchd" ? run2("launchctl", ["print", `${domain2()}/${label}`]).status === 0 : run2("systemctl", ["--user", "is-enabled", `${label}.service`]).status === 0;
|
|
63464
|
-
return { platform, label, file: file2, installed:
|
|
63753
|
+
return { platform, label, file: file2, installed: existsSync35(file2), loaded };
|
|
63465
63754
|
}
|
|
63466
63755
|
function installService(platform, env, workspaceDir, run2 = exec) {
|
|
63467
63756
|
const spec = serviceSpec(env, workspaceDir);
|
|
63468
63757
|
const file2 = fileOf2(platform, env, spec.label);
|
|
63469
|
-
mkdirSync25(
|
|
63470
|
-
mkdirSync25(
|
|
63758
|
+
mkdirSync25(join41(file2, ".."), { recursive: true });
|
|
63759
|
+
mkdirSync25(join41(spec.log, ".."), { recursive: true, mode: 448 });
|
|
63471
63760
|
if (platform === "launchd") {
|
|
63472
63761
|
writeFileSync22(file2, launchdPlist(spec));
|
|
63473
63762
|
run2("launchctl", ["bootout", `${domain2()}/${spec.label}`]);
|
|
@@ -63489,7 +63778,7 @@ function uninstallService(platform, env, workspaceDir, run2 = exec) {
|
|
|
63489
63778
|
return serviceState(platform, env, workspaceDir, run2);
|
|
63490
63779
|
}
|
|
63491
63780
|
function workspaceOf2(input2) {
|
|
63492
|
-
return
|
|
63781
|
+
return resolve21(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
63493
63782
|
}
|
|
63494
63783
|
function platformOrRefuse() {
|
|
63495
63784
|
const platform = platformOf();
|
|
@@ -63564,9 +63853,9 @@ var serviceCommands = [
|
|
|
63564
63853
|
];
|
|
63565
63854
|
|
|
63566
63855
|
// src/core/cli/browser.ts
|
|
63567
|
-
import { existsSync as
|
|
63856
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync31, writeFileSync as writeFileSync23 } from "node:fs";
|
|
63568
63857
|
import { homedir as homedir9 } from "node:os";
|
|
63569
|
-
import { dirname as dirname17, join as
|
|
63858
|
+
import { dirname as dirname17, join as join42 } from "node:path";
|
|
63570
63859
|
|
|
63571
63860
|
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.2_supports-color@7.2.0_zod@4.6.7/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/transport.js
|
|
63572
63861
|
function normalizeHeaders(headers) {
|
|
@@ -65179,7 +65468,7 @@ function isChromeDevtools(entry) {
|
|
|
65179
65468
|
return isChromeDevtoolsMcp(command, args);
|
|
65180
65469
|
}
|
|
65181
65470
|
function claudeJsonPath(env) {
|
|
65182
|
-
return
|
|
65471
|
+
return join42(env.CLAUDE_CONFIG_DIR ?? env.HOME ?? homedir9(), ".claude.json");
|
|
65183
65472
|
}
|
|
65184
65473
|
function chromeServersOf(config2) {
|
|
65185
65474
|
return Object.entries(config2.mcpServers ?? {}).filter(([, entry]) => isChromeDevtools(entry)).map(([name]) => name);
|
|
@@ -65191,21 +65480,21 @@ function sharedBrowserEntry() {
|
|
|
65191
65480
|
return { type: "stdio", ...sharedBrowserCommand(), env: {} };
|
|
65192
65481
|
}
|
|
65193
65482
|
function backupPath(env) {
|
|
65194
|
-
return
|
|
65483
|
+
return join42(browserDir(orbitalHome(env)), "claude-mcp-backup.json");
|
|
65195
65484
|
}
|
|
65196
65485
|
function codexBackupPath(env) {
|
|
65197
|
-
return
|
|
65486
|
+
return join42(browserDir(orbitalHome(env)), "codex-config-backup.json");
|
|
65198
65487
|
}
|
|
65199
65488
|
function codexConfigPath(env) {
|
|
65200
|
-
return
|
|
65489
|
+
return join42(env.CODEX_HOME ?? join42(env.HOME ?? homedir9(), ".codex"), "config.toml");
|
|
65201
65490
|
}
|
|
65202
65491
|
function pointClaudeAtSharedBrowser(env, entry = sharedBrowserEntry()) {
|
|
65203
65492
|
const file2 = claudeJsonPath(env);
|
|
65204
|
-
const config2 =
|
|
65493
|
+
const config2 = existsSync36(file2) ? JSON.parse(readFileSync31(file2, "utf8")) : {};
|
|
65205
65494
|
const found = chromeServersOf(config2);
|
|
65206
65495
|
const names = found.length > 0 ? found : [SHARED_BROWSER_SERVER];
|
|
65207
65496
|
const backup = backupPath(env);
|
|
65208
|
-
const previous =
|
|
65497
|
+
const previous = existsSync36(backup) ? JSON.parse(readFileSync31(backup, "utf8")) : {};
|
|
65209
65498
|
const saved = { ...Object.fromEntries(names.filter((name) => config2.mcpServers?.[name] !== void 0).map((name) => [name, config2.mcpServers[name]])), ...previous };
|
|
65210
65499
|
mkdirSync26(browserDir(orbitalHome(env)), { recursive: true, mode: 448 });
|
|
65211
65500
|
writeFileSync23(backup, `${JSON.stringify(saved, null, 2)}
|
|
@@ -65218,9 +65507,9 @@ function pointClaudeAtSharedBrowser(env, entry = sharedBrowserEntry()) {
|
|
|
65218
65507
|
function revertClaudeBrowser(env) {
|
|
65219
65508
|
const file2 = claudeJsonPath(env);
|
|
65220
65509
|
const backup = backupPath(env);
|
|
65221
|
-
if (!
|
|
65222
|
-
const saved = JSON.parse(
|
|
65223
|
-
const config2 =
|
|
65510
|
+
if (!existsSync36(backup)) return null;
|
|
65511
|
+
const saved = JSON.parse(readFileSync31(backup, "utf8"));
|
|
65512
|
+
const config2 = existsSync36(file2) ? JSON.parse(readFileSync31(file2, "utf8")) : {};
|
|
65224
65513
|
const shared = Object.entries(config2.mcpServers ?? {}).filter(([, entry]) => Array.isArray(entry.args) && entry.args.join(" ").endsWith("browser connect")).map(([name]) => name);
|
|
65225
65514
|
const servers = { ...config2.mcpServers };
|
|
65226
65515
|
for (const name of shared) delete servers[name];
|
|
@@ -65231,18 +65520,18 @@ function revertClaudeBrowser(env) {
|
|
|
65231
65520
|
}
|
|
65232
65521
|
function pointCodexAtSharedBrowser(env, shared = sharedBrowserCommand()) {
|
|
65233
65522
|
const file2 = codexConfigPath(env);
|
|
65234
|
-
if (!
|
|
65235
|
-
const text3 =
|
|
65523
|
+
if (!existsSync36(file2) && !existsSync36(dirname17(file2))) return null;
|
|
65524
|
+
const text3 = existsSync36(file2) ? readFileSync31(file2, "utf8") : "";
|
|
65236
65525
|
const tables = mcpServerTables(text3);
|
|
65237
65526
|
const found = tables.filter((table2) => isChromeDevtoolsMcp(table2.command, table2.args)).map((table2) => table2.name);
|
|
65238
65527
|
const connected = tables.filter((table2) => isSharedBrowserConnect(table2.args)).map((table2) => table2.name);
|
|
65239
65528
|
const names = found.length > 0 ? found : connected.length > 0 ? [] : [SHARED_BROWSER_SERVER];
|
|
65240
65529
|
const backup = codexBackupPath(env);
|
|
65241
|
-
if (names.length === 0) return { file: file2, servers: connected, backup:
|
|
65530
|
+
if (names.length === 0) return { file: file2, servers: connected, backup: existsSync36(backup) ? backup : null };
|
|
65242
65531
|
const block = (name) => stdioServerBlock(name, shared.command, shared.args);
|
|
65243
65532
|
const replaced = replaceMcpServers(text3, (table2) => names.includes(table2.name) ? block(table2.name) : null);
|
|
65244
65533
|
const next = names.filter((name) => !replaced.replaced.some((one) => one.name === name)).reduce((current2, name) => appendMcpServer(current2, block(name)), replaced.text);
|
|
65245
|
-
const previous =
|
|
65534
|
+
const previous = existsSync36(backup) ? JSON.parse(readFileSync31(backup, "utf8")) : {};
|
|
65246
65535
|
const saved = { ...Object.fromEntries(replaced.replaced.map((one) => [one.name, one.original])), ...previous };
|
|
65247
65536
|
mkdirSync26(browserDir(orbitalHome(env)), { recursive: true, mode: 448 });
|
|
65248
65537
|
writeFileSync23(backup, `${JSON.stringify(saved, null, 2)}
|
|
@@ -65254,9 +65543,9 @@ function pointCodexAtSharedBrowser(env, shared = sharedBrowserCommand()) {
|
|
|
65254
65543
|
function revertCodexBrowser(env) {
|
|
65255
65544
|
const file2 = codexConfigPath(env);
|
|
65256
65545
|
const backup = codexBackupPath(env);
|
|
65257
|
-
if (!
|
|
65258
|
-
const saved = JSON.parse(
|
|
65259
|
-
const text3 =
|
|
65546
|
+
if (!existsSync36(backup)) return null;
|
|
65547
|
+
const saved = JSON.parse(readFileSync31(backup, "utf8"));
|
|
65548
|
+
const text3 = existsSync36(file2) ? readFileSync31(file2, "utf8") : "";
|
|
65260
65549
|
const replaced = replaceMcpServers(text3, (table2) => isSharedBrowserConnect(table2.args) ? saved[table2.name] ?? "" : null);
|
|
65261
65550
|
const next = Object.entries(saved).filter(([name]) => !replaced.replaced.some((one) => one.name === name)).reduce((current2, [, original]) => appendMcpServer(current2, original), replaced.text);
|
|
65262
65551
|
mkdirSync26(dirname17(file2), { recursive: true });
|
|
@@ -65441,12 +65730,12 @@ var browserCommands = [
|
|
|
65441
65730
|
];
|
|
65442
65731
|
|
|
65443
65732
|
// src/core/cli/mission.ts
|
|
65444
|
-
import { existsSync as
|
|
65445
|
-
import { join as
|
|
65733
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
65734
|
+
import { join as join44, relative as relative8, resolve as resolve22 } from "node:path";
|
|
65446
65735
|
|
|
65447
65736
|
// src/core/local/mission-migrate.ts
|
|
65448
|
-
import { existsSync as
|
|
65449
|
-
import { join as
|
|
65737
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync27, readFileSync as readFileSync32, readdirSync as readdirSync21, renameSync as renameSync8, rmSync as rmSync14, rmdirSync as rmdirSync2, writeFileSync as writeFileSync24 } from "node:fs";
|
|
65738
|
+
import { join as join43 } from "node:path";
|
|
65450
65739
|
function withTeam(content, team) {
|
|
65451
65740
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(\r?\n[\s\S]*)?$/);
|
|
65452
65741
|
if (!match) return content;
|
|
@@ -65458,26 +65747,26 @@ ${head.join("\n")}
|
|
|
65458
65747
|
---${match[2] ?? "\n"}`;
|
|
65459
65748
|
}
|
|
65460
65749
|
function emptyDir(dir) {
|
|
65461
|
-
if (
|
|
65750
|
+
if (existsSync37(dir) && readdirSync21(dir).length === 0) rmdirSync2(dir);
|
|
65462
65751
|
}
|
|
65463
65752
|
function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
65464
65753
|
const moved = [];
|
|
65465
65754
|
const refused3 = [];
|
|
65466
|
-
const teamsDir =
|
|
65467
|
-
const target =
|
|
65755
|
+
const teamsDir = join43(root, TEAMS_DIR);
|
|
65756
|
+
const target = join43(root, MISSIONS_DIR);
|
|
65468
65757
|
const seen = /* @__PURE__ */ new Map();
|
|
65469
|
-
for (const team of
|
|
65470
|
-
const missionsDir =
|
|
65471
|
-
if (!
|
|
65758
|
+
for (const team of existsSync37(teamsDir) ? readdirSync21(teamsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()) : []) {
|
|
65759
|
+
const missionsDir = join43(teamsDir, team.name, MISSIONS_DIR);
|
|
65760
|
+
if (!existsSync37(missionsDir)) continue;
|
|
65472
65761
|
for (const mission of readdirSync21(missionsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory())) {
|
|
65473
|
-
const from =
|
|
65474
|
-
const to =
|
|
65762
|
+
const from = join43(missionsDir, mission.name);
|
|
65763
|
+
const to = join43(target, mission.name);
|
|
65475
65764
|
const other = seen.get(mission.name);
|
|
65476
65765
|
if (other) {
|
|
65477
65766
|
refused3.push(`${from}: the team ${other} has a mission ${mission.name} too. Rename one folder and its mission: key, then run the migration again.`);
|
|
65478
65767
|
continue;
|
|
65479
65768
|
}
|
|
65480
|
-
if (
|
|
65769
|
+
if (existsSync37(to)) {
|
|
65481
65770
|
refused3.push(`${from}: ${to} exists already.`);
|
|
65482
65771
|
continue;
|
|
65483
65772
|
}
|
|
@@ -65486,22 +65775,22 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
65486
65775
|
if (dryRun) continue;
|
|
65487
65776
|
mkdirSync27(target, { recursive: true });
|
|
65488
65777
|
renameSync8(from, to);
|
|
65489
|
-
const file2 =
|
|
65490
|
-
if (
|
|
65778
|
+
const file2 = join43(to, MISSION_FILE);
|
|
65779
|
+
if (existsSync37(file2)) writeFileSync24(file2, withTeam(readFileSync32(file2, "utf8"), team.name));
|
|
65491
65780
|
}
|
|
65492
65781
|
if (!dryRun) emptyDir(missionsDir);
|
|
65493
65782
|
}
|
|
65494
65783
|
for (const node2 of nodes) {
|
|
65495
|
-
const nodeDir2 =
|
|
65496
|
-
if (!
|
|
65784
|
+
const nodeDir2 = join43(nodesDir, node2);
|
|
65785
|
+
if (!existsSync37(nodeDir2)) continue;
|
|
65497
65786
|
const files = [];
|
|
65498
65787
|
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) {
|
|
65499
|
-
if (entry.isFile() && entry.name.endsWith(".json")) files.push({ from:
|
|
65500
|
-
if (entry.isFile() && entry.name.endsWith(".json.migrated")) files.push({ from:
|
|
65788
|
+
if (entry.isFile() && entry.name.endsWith(".json")) files.push({ from: join43(nodeDir2, entry.name), mission: entry.name.slice(0, -".json".length), team: null, backup: false });
|
|
65789
|
+
if (entry.isFile() && entry.name.endsWith(".json.migrated")) files.push({ from: join43(nodeDir2, entry.name), mission: entry.name.slice(0, -".json.migrated".length), team: null, backup: true });
|
|
65501
65790
|
if (!entry.isDirectory()) continue;
|
|
65502
|
-
for (const name of readdirSync21(
|
|
65503
|
-
if (name.endsWith(".json")) files.push({ from:
|
|
65504
|
-
if (name.endsWith(".json.migrated")) files.push({ from:
|
|
65791
|
+
for (const name of readdirSync21(join43(nodeDir2, entry.name))) {
|
|
65792
|
+
if (name.endsWith(".json")) files.push({ from: join43(nodeDir2, entry.name, name), mission: name.slice(0, -".json".length), team: entry.name, backup: false });
|
|
65793
|
+
if (name.endsWith(".json.migrated")) files.push({ from: join43(nodeDir2, entry.name, name), mission: name.slice(0, -".json.migrated".length), team: entry.name, backup: true });
|
|
65505
65794
|
}
|
|
65506
65795
|
}
|
|
65507
65796
|
files.sort((a, b) => Number(a.backup) - Number(b.backup) || Number(a.team !== null) - Number(b.team !== null));
|
|
@@ -65513,13 +65802,13 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
65513
65802
|
if (!dryRun) rmSync14(file2.from);
|
|
65514
65803
|
continue;
|
|
65515
65804
|
}
|
|
65516
|
-
if (taken.has(file2.mission) ||
|
|
65805
|
+
if (taken.has(file2.mission) || existsSync37(to)) {
|
|
65517
65806
|
refused3.push(`${file2.from}: ${to} exists already.`);
|
|
65518
65807
|
continue;
|
|
65519
65808
|
}
|
|
65520
|
-
const raw = JSON.parse(
|
|
65809
|
+
const raw = JSON.parse(readFileSync32(file2.from, "utf8"));
|
|
65521
65810
|
const { file: _file2, ...rest } = raw;
|
|
65522
|
-
const state = MissionStateFileSchema.safeParse({ question: null,
|
|
65811
|
+
const state = MissionStateFileSchema.safeParse({ question: null, lead: null, claim: null, claimedAt: null, ...rest, mission: file2.mission, node: node2, team: rest.team ?? file2.team });
|
|
65523
65812
|
if (!state.success) {
|
|
65524
65813
|
refused3.push(`${file2.from}: it is not the state of a mission: ${state.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ")}`);
|
|
65525
65814
|
continue;
|
|
@@ -65531,7 +65820,7 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
65531
65820
|
rmSync14(file2.from);
|
|
65532
65821
|
}
|
|
65533
65822
|
if (dryRun) continue;
|
|
65534
|
-
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) if (entry.isDirectory()) emptyDir(
|
|
65823
|
+
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) if (entry.isDirectory()) emptyDir(join43(nodeDir2, entry.name));
|
|
65535
65824
|
emptyDir(nodeDir2);
|
|
65536
65825
|
}
|
|
65537
65826
|
emptyDir(nodesDir);
|
|
@@ -65553,13 +65842,13 @@ var MISSION_FIELDS = {
|
|
|
65553
65842
|
{ name: "result", type: "string", nullable: true, summary: "The hand-off of the mission, when it ended." },
|
|
65554
65843
|
{ name: "summary", type: "string", nullable: true, summary: "The first sentence of the Intent of the brief." },
|
|
65555
65844
|
{ name: "question", type: "string", nullable: true, summary: "The question a waiting lead ended its turn with." },
|
|
65556
|
-
{ name: "questions", type: "array", summary: "The open questions the lead asked
|
|
65845
|
+
{ name: "questions", type: "array", summary: "The open questions the lead asked with missionAsk, each with an id, the question and the time: the mission waits on a decision until each is answered." },
|
|
65557
65846
|
{ name: "links", type: "array", summary: "The links of the mission, each with a label, a URL and a state: the issue of its source, and the links its lead adds, such as its pull request." },
|
|
65558
65847
|
{ name: "history", type: "array", summary: "Each change of state, with its time and who made it." }
|
|
65559
65848
|
]
|
|
65560
65849
|
};
|
|
65561
65850
|
function workspaceOf3(input2) {
|
|
65562
|
-
return
|
|
65851
|
+
return resolve22(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
65563
65852
|
}
|
|
65564
65853
|
function refused2(message, hint) {
|
|
65565
65854
|
return new CliError({ code: "invalid-input", message, exitCode: ExitCode.Validation, ...hint && { hint } });
|
|
@@ -65641,7 +65930,7 @@ function missionNote(context, record2) {
|
|
|
65641
65930
|
}
|
|
65642
65931
|
}
|
|
65643
65932
|
function shownState(record2) {
|
|
65644
|
-
return
|
|
65933
|
+
return waitsOnDecision(record2) && record2.state !== "waiting" ? `${record2.state}, waits on you` : record2.state;
|
|
65645
65934
|
}
|
|
65646
65935
|
function missionLines2(context, records) {
|
|
65647
65936
|
const widths = ["mission", "node", "state"].map((key2) => Math.max(key2.length, ...records.map((record2) => (key2 === "mission" ? record2.missionId : key2 === "node" ? record2.node : shownState(record2)).length)));
|
|
@@ -65693,7 +65982,7 @@ var missionNew = {
|
|
|
65693
65982
|
if (!id.success) throw refused2(`"${String(input2.positionals.id)}" is not a mission id: use lowercase letters, digits and hyphens.`);
|
|
65694
65983
|
const file2 = missionFile(teamRoot(team), id.data);
|
|
65695
65984
|
if (definition.missions.some((one) => one.id === id.data && one.team !== team.name)) throw refused2(`The mission ${id.data} belongs to another team. A mission id names one mission: choose another id.`);
|
|
65696
|
-
if (
|
|
65985
|
+
if (existsSync38(file2) && input2.flags.force !== true) throw refused2(`${relative8(workspaceDir, file2)} exists.`, "Pass --force to write over it.");
|
|
65697
65986
|
const intent = await asked(context, input2, "intent", "Intent: why does the team do this mission? ");
|
|
65698
65987
|
const endState = await asked(context, input2, "end-state", "End state: what is true when the mission is done? ");
|
|
65699
65988
|
const limits = await asked(context, input2, "limits", "Limits: what must the team not do or change? ");
|
|
@@ -65772,7 +66061,7 @@ var missionShow = {
|
|
|
65772
66061
|
if (record2.sessionId) context.output.line(`session: ${record2.sessionId}`);
|
|
65773
66062
|
if (record2.brief) context.output.line(["", record2.brief].join("\n"));
|
|
65774
66063
|
if (record2.lead) context.output.line(`lead: ${record2.lead}`);
|
|
65775
|
-
if (record2.
|
|
66064
|
+
if (record2.receivers.length > 0) context.output.line(`decision receivers: ${record2.receivers.join(", ")} (each question goes to every live one)`);
|
|
65776
66065
|
if (record2.claim) context.output.line(`claim: ${record2.claim}${record2.claimedAt ? ` since ${record2.claimedAt}` : ""}${record2.stale ? " (stale)" : ""}`);
|
|
65777
66066
|
if (!record2.question && record2.state === "waiting") context.output.line(["", "The lead stopped without a hand-off.", `Tell it how to go on with: ${commandPrefix(context.env)} mission reply ${record2.missionId} "<message>"`].join("\n"));
|
|
65778
66067
|
if (record2.question && record2.state === "waiting") context.output.line(["", "question:", record2.question, "", `Answer with: ${commandPrefix(context.env)} mission reply ${record2.missionId} "<answer>"`].join("\n"));
|
|
@@ -65847,6 +66136,31 @@ var missionSay = {
|
|
|
65847
66136
|
return { data: { ...said2, node: node2.name } };
|
|
65848
66137
|
}
|
|
65849
66138
|
};
|
|
66139
|
+
var CLAUDE_SESSION_ENV = "CLAUDE_CODE_SESSION_ID";
|
|
66140
|
+
function missionMembership(verb, summary, why) {
|
|
66141
|
+
return {
|
|
66142
|
+
path: ["mission", verb],
|
|
66143
|
+
summary,
|
|
66144
|
+
credential: "none",
|
|
66145
|
+
flags: [WORKSPACE2, { name: "external-id", type: "string", summary: `The external id of the attached session, such as the Claude Code session id. Default: ${CLAUDE_SESSION_ENV}, so a Claude Code session names itself.` }],
|
|
66146
|
+
positionals: [MISSION_ID2],
|
|
66147
|
+
outputShape: MISSION_FIELDS,
|
|
66148
|
+
exitCodes: [0, 3],
|
|
66149
|
+
routes: [],
|
|
66150
|
+
examples: [{ command: `orbital mission ${verb} pr-42`, why }],
|
|
66151
|
+
run: async (input2, context) => {
|
|
66152
|
+
const mission = idOf(input2);
|
|
66153
|
+
const externalId = typeof input2.flags["external-id"] === "string" ? input2.flags["external-id"] : context.env[CLAUDE_SESSION_ENV] || null;
|
|
66154
|
+
if (!externalId) throw refused2(`Name the session that ${verb}s the decision receivers of ${mission}.`, `Pass --external-id <id>, or run the command in a Claude Code session, which sets ${CLAUDE_SESSION_ENV}.`);
|
|
66155
|
+
const node2 = await nodeOfMission(context, mission, () => nodeOfProject(context, workspaceOf3(input2)));
|
|
66156
|
+
const record2 = MissionRecordSchema.parse(await nodeCall(context, node2, `/orbital/missions/${verb}`, { missionId: mission, externalId }));
|
|
66157
|
+
context.output.line(`${record2.missionId} on ${record2.node}: ${record2.state}; decision receivers: ${record2.receivers.join(", ") || "none"}`);
|
|
66158
|
+
return { data: record2 };
|
|
66159
|
+
}
|
|
66160
|
+
};
|
|
66161
|
+
}
|
|
66162
|
+
var missionJoin = missionMembership("join", "Make an attached session, such as this Claude Code session, a decision receiver of a running mission: each question of its lead goes to every live receiver, and the open questions come at once. The node keys the receiver by its external id, so the session stays a receiver after it or the node restarts.", "Get the questions of the lead of pr-42 in this Claude Code session.");
|
|
66163
|
+
var missionLeave = missionMembership("leave", "Remove an attached session from the decision receivers of a mission: its lead's questions stop coming to it.", "Stop getting the questions of the lead of pr-42 in this Claude Code session.");
|
|
65850
66164
|
var missionMigrate = {
|
|
65851
66165
|
path: ["mission", "migrate"],
|
|
65852
66166
|
summary: `Move the missions of the old layout, teams/<team>/missions/<id>/, to missions/<id>/${MISSION_FILE} with team: <team> in the frontmatter, and the state of each mission of the nodes of this project from the node home, missions/<node>/<id>.json, to missions/<id>/${MISSION_STATE_FILE} beside its ${MISSION_FILE}. Stop the nodes of the project first.`,
|
|
@@ -65861,11 +66175,11 @@ var missionMigrate = {
|
|
|
65861
66175
|
const workspaceDir = workspaceOf3(input2);
|
|
65862
66176
|
const source = definitionSource(workspaceDir, context.env);
|
|
65863
66177
|
if (!source) throw refused2(`${workspaceDir} has no definition of orbital.`);
|
|
65864
|
-
const running = (await liveNodes(orbitalHome(context.env), context.fetch)).filter((node2) =>
|
|
66178
|
+
const running = (await liveNodes(orbitalHome(context.env), context.fetch)).filter((node2) => resolve22(node2.workspaceDir) === resolve22(source.projectDir));
|
|
65865
66179
|
if (running.length > 0 && input2.flags["dry-run"] !== true) throw refused2(`The node ${running.map((node2) => node2.name).join(", ")} of this project runs.`, `Stop it first, with ${commandPrefix(context.env)} down, then run the migration.`);
|
|
65866
66180
|
const project = sourceConfig(source).node ?? defaultNodeName(workspaceDir);
|
|
65867
66181
|
const nodes = teamNodes(readTeams(source.root).teams, project);
|
|
65868
|
-
const migration = migrateMissions(source.root,
|
|
66182
|
+
const migration = migrateMissions(source.root, join44(orbitalHome(context.env), "missions"), nodes, input2.flags["dry-run"] === true);
|
|
65869
66183
|
for (const move of migration.moved) context.output.line(`${input2.flags["dry-run"] === true ? "would move" : "moved"} ${move.from} -> ${move.to}`);
|
|
65870
66184
|
for (const reason of migration.refused) context.output.warn(reason);
|
|
65871
66185
|
if (migration.moved.length === 0 && migration.refused.length === 0) context.output.line("Nothing to move: the missions are in the new layout.");
|
|
@@ -65890,10 +66204,10 @@ var missionReply = {
|
|
|
65890
66204
|
return { data: record2 };
|
|
65891
66205
|
}
|
|
65892
66206
|
};
|
|
65893
|
-
var missionCommands = [missionNew, missionStart, missionList, missionShow, missionPause, missionResume, missionSay, missionReply, missionDone, missionMigrate].map((spec) => ({ ...spec, group: CommandGroup.OperatingModel }));
|
|
66207
|
+
var missionCommands = [missionNew, missionStart, missionList, missionShow, missionPause, missionResume, missionSay, missionReply, missionJoin, missionLeave, missionDone, missionMigrate].map((spec) => ({ ...spec, group: CommandGroup.OperatingModel }));
|
|
65894
66208
|
|
|
65895
66209
|
// src/core/cli/check.ts
|
|
65896
|
-
import { resolve as
|
|
66210
|
+
import { resolve as resolve23 } from "node:path";
|
|
65897
66211
|
var LOAD_RULE = "orbital.load";
|
|
65898
66212
|
async function loadRefusals(workspaceDir, env) {
|
|
65899
66213
|
try {
|
|
@@ -65931,7 +66245,7 @@ var checkCommand = {
|
|
|
65931
66245
|
{ command: "orbital check --annotations", why: "Show the findings on a pull request." }
|
|
65932
66246
|
],
|
|
65933
66247
|
run: async (input2, context) => {
|
|
65934
|
-
const workspaceDir =
|
|
66248
|
+
const workspaceDir = resolve23(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
65935
66249
|
const source = definitionSource(workspaceDir, context.env);
|
|
65936
66250
|
const findings = await loadRefusals(workspaceDir, context.env);
|
|
65937
66251
|
for (const finding of findings) context.output.line(`${finding.finding} ${finding.rule} ${finding.path} ${finding.message}`);
|
|
@@ -66241,18 +66555,18 @@ function createOrbitalCommands(options) {
|
|
|
66241
66555
|
}
|
|
66242
66556
|
|
|
66243
66557
|
// src/core/cli/shipped-skills.ts
|
|
66244
|
-
var
|
|
66245
|
-
import { cpSync as cpSync4, existsSync as
|
|
66558
|
+
var import_yaml9 = __toESM(require_dist(), 1);
|
|
66559
|
+
import { cpSync as cpSync4, existsSync as existsSync39, mkdirSync as mkdirSync28, readFileSync as readFileSync33, readdirSync as readdirSync22, rmSync as rmSync15, writeFileSync as writeFileSync25 } from "node:fs";
|
|
66246
66560
|
import { createHash as createHash11 } from "node:crypto";
|
|
66247
66561
|
import { homedir as homedir11 } from "node:os";
|
|
66248
|
-
import { dirname as dirname18, join as
|
|
66562
|
+
import { dirname as dirname18, join as join45, relative as relative9, resolve as resolve24, sep as sep7 } from "node:path";
|
|
66249
66563
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
66250
66564
|
var HASH_FILE = ".installed-hash";
|
|
66251
66565
|
function skillsRoot(shipped) {
|
|
66252
66566
|
let directory = dirname18(fileURLToPath3(shipped.anchor));
|
|
66253
66567
|
for (let depth = 0; depth < 5; depth += 1) {
|
|
66254
|
-
const candidate =
|
|
66255
|
-
if (
|
|
66568
|
+
const candidate = join45(directory, "skills");
|
|
66569
|
+
if (existsSync39(join45(candidate, shipped.names[0], "SKILL.md"))) return candidate;
|
|
66256
66570
|
directory = dirname18(directory);
|
|
66257
66571
|
}
|
|
66258
66572
|
throw new CliError({
|
|
@@ -66264,33 +66578,33 @@ function skillsRoot(shipped) {
|
|
|
66264
66578
|
}
|
|
66265
66579
|
function filesOf2(dir) {
|
|
66266
66580
|
return readdirSync22(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
66267
|
-
const path3 =
|
|
66581
|
+
const path3 = join45(dir, entry.name);
|
|
66268
66582
|
if (entry.isDirectory()) return filesOf2(path3);
|
|
66269
66583
|
return entry.name === HASH_FILE ? [] : [path3];
|
|
66270
66584
|
});
|
|
66271
66585
|
}
|
|
66272
66586
|
function descriptionOf(skillFile) {
|
|
66273
|
-
const match = /^---\n([\s\S]*?)\n---/.exec(
|
|
66587
|
+
const match = /^---\n([\s\S]*?)\n---/.exec(readFileSync33(skillFile, "utf8"));
|
|
66274
66588
|
if (!match?.[1]) return "";
|
|
66275
|
-
const frontmatter = (0,
|
|
66589
|
+
const frontmatter = (0, import_yaml9.parse)(match[1]);
|
|
66276
66590
|
return typeof frontmatter?.description === "string" ? frontmatter.description : "";
|
|
66277
66591
|
}
|
|
66278
66592
|
function shippedSkills(shipped) {
|
|
66279
66593
|
const root = skillsRoot(shipped);
|
|
66280
66594
|
return shipped.names.map((name) => {
|
|
66281
|
-
const dir =
|
|
66282
|
-
if (!
|
|
66595
|
+
const dir = join45(root, name);
|
|
66596
|
+
if (!existsSync39(join45(dir, "SKILL.md"))) {
|
|
66283
66597
|
throw new CliError({ code: "internal", message: `This build is missing its skill ${name}.`, exitCode: ExitCode.Error, hint: "Reinstall the CLI. The skills ship inside the package." });
|
|
66284
66598
|
}
|
|
66285
66599
|
const files = filesOf2(dir).map((file2) => relative9(dir, file2).split(sep7).join("/")).sort();
|
|
66286
|
-
return { name, description: descriptionOf(
|
|
66600
|
+
return { name, description: descriptionOf(join45(dir, "SKILL.md")), dir, files };
|
|
66287
66601
|
});
|
|
66288
66602
|
}
|
|
66289
66603
|
function folderHash(dir) {
|
|
66290
66604
|
const hash2 = createHash11("sha256");
|
|
66291
66605
|
for (const file2 of filesOf2(dir).map((path3) => relative9(dir, path3).split(sep7).join("/")).sort()) {
|
|
66292
66606
|
hash2.update(`${file2}\0`);
|
|
66293
|
-
hash2.update(
|
|
66607
|
+
hash2.update(readFileSync33(join45(dir, file2)));
|
|
66294
66608
|
hash2.update("\0");
|
|
66295
66609
|
}
|
|
66296
66610
|
return hash2.digest("hex");
|
|
@@ -66310,18 +66624,18 @@ function pick2(all, names) {
|
|
|
66310
66624
|
});
|
|
66311
66625
|
}
|
|
66312
66626
|
function installBase(input2) {
|
|
66313
|
-
if (typeof input2.flags.dir === "string") return
|
|
66314
|
-
if (input2.flags.global === true) return
|
|
66315
|
-
return
|
|
66627
|
+
if (typeof input2.flags.dir === "string") return resolve24(input2.flags.dir);
|
|
66628
|
+
if (input2.flags.global === true) return join45(homedir11(), ".claude", "skills");
|
|
66629
|
+
return resolve24(".claude", "skills");
|
|
66316
66630
|
}
|
|
66317
66631
|
function planOne(skill, base2) {
|
|
66318
|
-
const target =
|
|
66632
|
+
const target = join45(base2, skill.name);
|
|
66319
66633
|
const hash2 = folderHash(skill.dir);
|
|
66320
|
-
if (!
|
|
66634
|
+
if (!existsSync39(join45(target, "SKILL.md"))) return { skill, target, hash: hash2, action: "installed", edited: false };
|
|
66321
66635
|
const onDisk = folderHash(target);
|
|
66322
66636
|
if (onDisk === hash2) return { skill, target, hash: hash2, action: "unchanged", edited: false };
|
|
66323
|
-
const hashFile =
|
|
66324
|
-
const recorded =
|
|
66637
|
+
const hashFile = join45(target, HASH_FILE);
|
|
66638
|
+
const recorded = existsSync39(hashFile) ? readFileSync33(hashFile, "utf8").trim() : "";
|
|
66325
66639
|
return { skill, target, hash: hash2, action: "updated", edited: onDisk !== recorded };
|
|
66326
66640
|
}
|
|
66327
66641
|
function writeOne(plan) {
|
|
@@ -66329,7 +66643,7 @@ function writeOne(plan) {
|
|
|
66329
66643
|
rmSync15(plan.target, { recursive: true, force: true });
|
|
66330
66644
|
mkdirSync28(plan.target, { recursive: true });
|
|
66331
66645
|
cpSync4(plan.skill.dir, plan.target, { recursive: true });
|
|
66332
|
-
writeFileSync25(
|
|
66646
|
+
writeFileSync25(join45(plan.target, HASH_FILE), `${plan.hash}
|
|
66333
66647
|
`);
|
|
66334
66648
|
} catch (error62) {
|
|
66335
66649
|
throw new CliError({
|
|
@@ -66374,7 +66688,7 @@ function createSkillCommands(shipped) {
|
|
|
66374
66688
|
context.output.line(skill.dir);
|
|
66375
66689
|
return { data: { name: skill.name, path: skill.dir, content: null } };
|
|
66376
66690
|
}
|
|
66377
|
-
const content =
|
|
66691
|
+
const content = readFileSync33(join45(skill.dir, "SKILL.md"), "utf8");
|
|
66378
66692
|
context.output.line(content);
|
|
66379
66693
|
return { data: { name: skill.name, path: skill.dir, content } };
|
|
66380
66694
|
}
|
|
@@ -66454,21 +66768,21 @@ function createSkillCommands(shipped) {
|
|
|
66454
66768
|
}
|
|
66455
66769
|
|
|
66456
66770
|
// src/init.ts
|
|
66457
|
-
import { existsSync as
|
|
66458
|
-
import { dirname as dirname19, isAbsolute as isAbsolute8, relative as relative10, resolve as
|
|
66459
|
-
var
|
|
66771
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync29, writeFileSync as writeFileSync26 } from "node:fs";
|
|
66772
|
+
import { dirname as dirname19, isAbsolute as isAbsolute8, relative as relative10, resolve as resolve25 } from "node:path";
|
|
66773
|
+
var import_yaml10 = __toESM(require_dist(), 1);
|
|
66460
66774
|
function initHarnessFolder(workspaceDir, adapter = CLAUDE_ADAPTER) {
|
|
66461
66775
|
const file2 = harnessRootAt(workspaceDir, adapter).file;
|
|
66462
|
-
if (
|
|
66776
|
+
if (existsSync40(file2)) {
|
|
66463
66777
|
throw new CliError({ code: "conflict", message: `This project has ${file2} already.`, exitCode: ExitCode.Validation, hint: "A harness folder holds one orbital.yaml." });
|
|
66464
66778
|
}
|
|
66465
66779
|
mkdirSync29(dirname19(file2), { recursive: true });
|
|
66466
|
-
writeFileSync26(file2, (0,
|
|
66780
|
+
writeFileSync26(file2, (0, import_yaml10.stringify)({ apiVersion: ORBITAL_API_VERSION }));
|
|
66467
66781
|
return file2;
|
|
66468
66782
|
}
|
|
66469
66783
|
function workspaceOf4(flag) {
|
|
66470
|
-
const workspaceDir =
|
|
66471
|
-
if (!
|
|
66784
|
+
const workspaceDir = resolve25(typeof flag === "string" ? flag : ".");
|
|
66785
|
+
if (!existsSync40(workspaceDir)) throw new CliError({ code: "not-found", message: `${workspaceDir} does not exist.`, exitCode: ExitCode.Validation });
|
|
66472
66786
|
return workspaceDir;
|
|
66473
66787
|
}
|
|
66474
66788
|
var WORKSPACE3 = { name: "workspace", type: "path", summary: "The project. Default: the current directory." };
|
|
@@ -66506,9 +66820,9 @@ function pluginRefOf(home, given) {
|
|
|
66506
66820
|
return path3.startsWith(".") ? path3 : `./${path3}`;
|
|
66507
66821
|
} };
|
|
66508
66822
|
}
|
|
66509
|
-
installPlugin(home, resolved);
|
|
66823
|
+
const { name } = installPlugin(home, resolved);
|
|
66510
66824
|
const source = resolved.kind === "npm" ? `npm:${resolved.pkg}` : resolved.kind === "git" ? `git:${resolved.url}` : spec;
|
|
66511
|
-
return { ref: { name
|
|
66825
|
+
return { ref: { name, source, base: home }, recorded: () => source };
|
|
66512
66826
|
}
|
|
66513
66827
|
function knownRef(context, known) {
|
|
66514
66828
|
const home = orbitalHome(context.env);
|
|
@@ -66544,7 +66858,7 @@ function targetOf2(context, workspaceDir, named2, given, adapters) {
|
|
|
66544
66858
|
});
|
|
66545
66859
|
}
|
|
66546
66860
|
async function chooseTargets(context, workspaceDir, adapters) {
|
|
66547
|
-
const present = adapters.filter((adapter) =>
|
|
66861
|
+
const present = adapters.filter((adapter) => existsSync40(resolve25(workspaceDir, adapter.folder.folder)));
|
|
66548
66862
|
if (present.length === 1) {
|
|
66549
66863
|
context.output.step(`installing ${present[0].id}: ${present[0].folder.folder} is the harness folder of this project`);
|
|
66550
66864
|
return [{ adapter: present[0], given: null }];
|
|
@@ -66578,9 +66892,9 @@ async function adapterInstall(context, adapter, workspaceDir, force) {
|
|
|
66578
66892
|
async function installTarget(context, workspaceDir, target, user, force) {
|
|
66579
66893
|
const { adapter, given } = target;
|
|
66580
66894
|
const root = harnessRootAt(workspaceDir, adapter);
|
|
66581
|
-
const file2 =
|
|
66895
|
+
const file2 = existsSync40(root.file) ? null : initHarnessFolder(workspaceDir, adapter);
|
|
66582
66896
|
if (file2) context.output.line(`wrote ${file2}`);
|
|
66583
|
-
const plugin = given ? { name: given.ref.name, source: user ? isPackageSource(given.ref.source) ? given.ref.source :
|
|
66897
|
+
const plugin = given ? { name: given.ref.name, source: user ? isPackageSource(given.ref.source) ? given.ref.source : resolve25(given.ref.source) : given.recorded(root.root), user } : null;
|
|
66584
66898
|
const named2 = plugin ? user ? setUserPlugin(context.env, plugin.name, { source: plugin.source, enabled: true, config: {}, env: [] }) : setPluginEntry(root.file, plugin.name, { source: plugin.source, enabled: true, config: {}, env: [] }) : false;
|
|
66585
66899
|
if (plugin && named2) context.output.line(user ? `your user config names the plugin ${plugin.name}: source ${plugin.source}` : `${ORBITAL_FILE} names the plugin ${plugin.name}: source ${plugin.source}`);
|
|
66586
66900
|
const install2 = await adapterInstall(context, adapter, workspaceDir, force);
|
|
@@ -66665,6 +66979,12 @@ function followAttach(context, options, signal, inbox, timing = {}) {
|
|
|
66665
66979
|
delivery: (event) => {
|
|
66666
66980
|
const delivery = DeliverySchema.safeParse(event);
|
|
66667
66981
|
if (delivery.success) inbox.push(delivery.data);
|
|
66982
|
+
const answered = AnsweredSchema.safeParse(event);
|
|
66983
|
+
if (!answered.success) return;
|
|
66984
|
+
for (let index = inbox.length - 1; index >= 0; index -= 1) {
|
|
66985
|
+
const held2 = inbox[index];
|
|
66986
|
+
if (held2.mission === answered.data.mission && (answered.data.question === null || held2.question === answered.data.question)) inbox.splice(index, 1);
|
|
66987
|
+
}
|
|
66668
66988
|
},
|
|
66669
66989
|
detached: (reason) => {
|
|
66670
66990
|
if (kept) kept.ended = true;
|
|
@@ -66794,9 +67114,8 @@ function mcpCommand(toolsOf) {
|
|
|
66794
67114
|
const joined = await attachment.current();
|
|
66795
67115
|
return { url: `${joined.nodeUrl}/a2a`, token: joined.token };
|
|
66796
67116
|
},
|
|
66797
|
-
|
|
66798
|
-
|
|
66799
|
-
},
|
|
67117
|
+
sessionId: null,
|
|
67118
|
+
attached: async () => (await attachment.current()).sessionId,
|
|
66800
67119
|
harness,
|
|
66801
67120
|
follow: true,
|
|
66802
67121
|
tools: inboxTools(context, attachment.current, inbox)
|
|
@@ -66805,8 +67124,8 @@ function mcpCommand(toolsOf) {
|
|
|
66805
67124
|
const server = await mcpServer(options, input2.flags.actions === true, toolsOf);
|
|
66806
67125
|
const transport = new StdioServerTransport();
|
|
66807
67126
|
await server.connect(transport);
|
|
66808
|
-
await new Promise((
|
|
66809
|
-
server.server.onclose =
|
|
67127
|
+
await new Promise((resolve26) => {
|
|
67128
|
+
server.server.onclose = resolve26;
|
|
66810
67129
|
process.stdin.once("end", () => void server.close());
|
|
66811
67130
|
});
|
|
66812
67131
|
stop.abort();
|
|
@@ -66832,6 +67151,10 @@ function orbitalCommands(options) {
|
|
|
66832
67151
|
...options.commands ?? []
|
|
66833
67152
|
];
|
|
66834
67153
|
}
|
|
67154
|
+
function workspacePluginCommands(options) {
|
|
67155
|
+
const reserved = [.../* @__PURE__ */ new Set([...options.builtIn.map((spec) => spec.path[0]), ...INTROSPECTION_WORDS])];
|
|
67156
|
+
return pluginCommandSpecs({ workspaceDir: options.workspaceDir, env: options.env, reserved, ...options.terminal && { terminal: options.terminal } });
|
|
67157
|
+
}
|
|
66835
67158
|
|
|
66836
67159
|
// src/launch.ts
|
|
66837
67160
|
import { spawn as spawn9 } from "node:child_process";
|
|
@@ -66869,7 +67192,8 @@ export {
|
|
|
66869
67192
|
nodeLaunch,
|
|
66870
67193
|
nodeStartArgv,
|
|
66871
67194
|
orbitalCommands,
|
|
66872
|
-
skillCommands
|
|
67195
|
+
skillCommands,
|
|
67196
|
+
workspacePluginCommands
|
|
66873
67197
|
};
|
|
66874
67198
|
/*! Bundled license information:
|
|
66875
67199
|
|