@kaiba-cloud/orbital 0.0.4 → 0.0.5
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 +723 -402
- package/dist/mod.json +1 -1
- package/dist/orbital.js +751 -422
- 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 +5 -2
- 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.5" : "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
|
}
|
|
@@ -38915,7 +38989,7 @@ function installPlugin(home, ref, exec2 = run) {
|
|
|
38915
38989
|
const installed = Object.keys(JSON.parse(readFileSync7(join10(scratch, "package.json"), "utf8")).dependencies ?? {});
|
|
38916
38990
|
const pkgDir = join10("node_modules", ...(installed[0] ?? ref.pkg.replace(/(.)@[^/]*$/, "$1")).split("/"));
|
|
38917
38991
|
const inner = PluginPackageSchema.parse(JSON.parse(readFileSync7(join10(scratch, pkgDir, PLUGIN_PACKAGE_FILE), "utf8")));
|
|
38918
|
-
writeFileSync5(join10(scratch, PLUGIN_PACKAGE_FILE), `${JSON.stringify({ name: ref.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 } }, null, 2)}
|
|
38992
|
+
writeFileSync5(join10(scratch, PLUGIN_PACKAGE_FILE), `${JSON.stringify({ name: ref.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
38993
|
`);
|
|
38920
38994
|
break;
|
|
38921
38995
|
}
|
|
@@ -38981,15 +39055,22 @@ function ensurePluginInstalled(home, name, source, exec2 = run) {
|
|
|
38981
39055
|
function pluginsDir(home) {
|
|
38982
39056
|
return join11(home, "plugins");
|
|
38983
39057
|
}
|
|
39058
|
+
function isLocalSource(source) {
|
|
39059
|
+
return source.startsWith(".") || isAbsolute5(source);
|
|
39060
|
+
}
|
|
39061
|
+
function pluginDir(name, source, roots) {
|
|
39062
|
+
if (!isLocalSource(source)) return join11(pluginsDir(roots.home), name);
|
|
39063
|
+
return isAbsolute5(source) ? source : resolve7(roots.harnessRoot ?? process.cwd(), source);
|
|
39064
|
+
}
|
|
38984
39065
|
function commandOf(name, source, roots, entryEnv = []) {
|
|
38985
39066
|
if (source.startsWith("exec:")) {
|
|
38986
39067
|
const [command2, ...args2] = source.slice("exec:".length).trim().split(/\s+/);
|
|
38987
39068
|
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 };
|
|
39069
|
+
return { command: command2, args: args2, cwd: roots.harnessRoot ?? process.cwd(), env: [...entryEnv], defaults: {}, harnesses: null, commands: null };
|
|
38989
39070
|
}
|
|
38990
39071
|
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 =
|
|
39072
|
+
const local = isLocalSource(source);
|
|
39073
|
+
const dir = pluginDir(name, source, roots);
|
|
38993
39074
|
const descriptor = join11(dir, PLUGIN_PACKAGE_FILE);
|
|
38994
39075
|
if (!local && !existsSync7(descriptor) && /^(npm|git):/.test(source)) ensurePluginInstalled(roots.home, name, source);
|
|
38995
39076
|
if (!existsSync7(descriptor)) {
|
|
@@ -38997,7 +39078,7 @@ function commandOf(name, source, roots, entryEnv = []) {
|
|
|
38997
39078
|
}
|
|
38998
39079
|
const plugin = PluginPackageSchema.parse(JSON.parse(readFileSync8(descriptor, "utf8")));
|
|
38999
39080
|
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 };
|
|
39081
|
+
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
39082
|
}
|
|
39002
39083
|
var PLUGIN_ENV = [
|
|
39003
39084
|
"PATH",
|
|
@@ -39042,6 +39123,15 @@ function folderMismatch(manifest, folders) {
|
|
|
39042
39123
|
const extra = folders.find((one) => !manifest.provides.harnesses.some((harness) => harness.id === one.id && harness.folder !== null));
|
|
39043
39124
|
return extra ? `declares the harness ${extra.id} in its ${PLUGIN_PACKAGE_FILE}, and its harnesses port serves no folder for it` : null;
|
|
39044
39125
|
}
|
|
39126
|
+
function commandMismatch(manifest, declared) {
|
|
39127
|
+
for (const command of manifest.commands) {
|
|
39128
|
+
const same2 = declared.find((one) => one.name === command.name);
|
|
39129
|
+
if (!same2) return `serves the command ${command.name}, and its ${PLUGIN_PACKAGE_FILE} declares no command ${command.name}`;
|
|
39130
|
+
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`;
|
|
39131
|
+
}
|
|
39132
|
+
const extra = declared.find((one) => !manifest.commands.some((command) => command.name === one.name));
|
|
39133
|
+
return extra ? `declares the command ${extra.name} in its ${PLUGIN_PACKAGE_FILE}, and serves no command ${extra.name}` : null;
|
|
39134
|
+
}
|
|
39045
39135
|
function manifestEnv(manifest) {
|
|
39046
39136
|
return manifest.permissions.secrets.filter((name) => PLUGIN_ENV_NAME.test(name));
|
|
39047
39137
|
}
|
|
@@ -39070,15 +39160,16 @@ async function startRemotePlugin(options) {
|
|
|
39070
39160
|
let reconnecting = false;
|
|
39071
39161
|
const connect = async () => {
|
|
39072
39162
|
const start = commandOf(options.name, options.source, options.roots, options.entryEnv);
|
|
39073
|
-
const
|
|
39163
|
+
const terminal = options.terminal ? [options.terminal.input, options.terminal.output, options.terminal.error] : [];
|
|
39164
|
+
const child = spawn2(start.command, start.args, { cwd: start.cwd, env: pluginProcessEnv(options.env, start.env), stdio: ["pipe", "pipe", "pipe", ...terminal] });
|
|
39074
39165
|
try {
|
|
39075
|
-
return await initialize(child, start.env, start.defaults, start.harnesses);
|
|
39166
|
+
return await initialize(child, start.env, start.defaults, start.harnesses, start.commands);
|
|
39076
39167
|
} catch (error62) {
|
|
39077
39168
|
child.kill("SIGKILL");
|
|
39078
39169
|
throw error62;
|
|
39079
39170
|
}
|
|
39080
39171
|
};
|
|
39081
|
-
const initialize = async (child, env, defaults, folders
|
|
39172
|
+
const initialize = async (child, env, defaults, folders, commands) => {
|
|
39082
39173
|
const peer = new RpcPeer({ send: (line) => child.stdin?.write(`${line}
|
|
39083
39174
|
`) });
|
|
39084
39175
|
createInterface2({ input: child.stdout }).on("line", (line) => peer.receive(line));
|
|
@@ -39113,6 +39204,10 @@ async function startRemotePlugin(options) {
|
|
|
39113
39204
|
const where2 = options.source.startsWith("exec:") ? `its ${ORBITAL_FILE} entry` : `its ${PLUGIN_PACKAGE_FILE}`;
|
|
39114
39205
|
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
39206
|
}
|
|
39207
|
+
if (commands) {
|
|
39208
|
+
const mismatch = commandMismatch(manifest2, commands);
|
|
39209
|
+
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.`);
|
|
39210
|
+
}
|
|
39116
39211
|
if (options.offline && manifest2.permissions.network.length > 0) {
|
|
39117
39212
|
throw new Error(`The plugin ${options.name} reaches ${manifest2.permissions.network.join(", ")}, and ${ORBITAL_FILE} says offline: true.`);
|
|
39118
39213
|
}
|
|
@@ -39298,7 +39393,7 @@ async function startRemotePlugin(options) {
|
|
|
39298
39393
|
endpoint = given;
|
|
39299
39394
|
void current2.then((connection) => connection.peer.notify(PLUGIN_METHODS.hostEndpoint, given));
|
|
39300
39395
|
},
|
|
39301
|
-
command: async (
|
|
39396
|
+
command: async (request) => PluginCommandResultSchema.parse(await call(PLUGIN_METHODS.commandRun, request)),
|
|
39302
39397
|
close: async () => {
|
|
39303
39398
|
stopped = true;
|
|
39304
39399
|
const connection = await current2.catch(() => null);
|
|
@@ -40653,7 +40748,7 @@ var readBodyDirect = (request) => {
|
|
|
40653
40748
|
request[bodyBufferKey] = buffered;
|
|
40654
40749
|
return Promise.resolve(buffered);
|
|
40655
40750
|
}
|
|
40656
|
-
const promise2 = new Promise((
|
|
40751
|
+
const promise2 = new Promise((resolve26, reject) => {
|
|
40657
40752
|
const chunks = [];
|
|
40658
40753
|
let settled = false;
|
|
40659
40754
|
const finish = (callback) => {
|
|
@@ -40671,7 +40766,7 @@ var readBodyDirect = (request) => {
|
|
|
40671
40766
|
else if (recovered === void 0) reject(error62 ?? normalizeAbortError(request, incoming));
|
|
40672
40767
|
else {
|
|
40673
40768
|
request[bodyBufferKey] = recovered;
|
|
40674
|
-
|
|
40769
|
+
resolve26(recovered);
|
|
40675
40770
|
}
|
|
40676
40771
|
});
|
|
40677
40772
|
return true;
|
|
@@ -40683,7 +40778,7 @@ var readBodyDirect = (request) => {
|
|
|
40683
40778
|
finish(() => {
|
|
40684
40779
|
const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
|
|
40685
40780
|
request[bodyBufferKey] = buffer;
|
|
40686
|
-
|
|
40781
|
+
resolve26(buffer);
|
|
40687
40782
|
});
|
|
40688
40783
|
};
|
|
40689
40784
|
const onError = (error62) => {
|
|
@@ -41230,7 +41325,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
41230
41325
|
});
|
|
41231
41326
|
if (!chunk) {
|
|
41232
41327
|
if (i === 1) {
|
|
41233
|
-
await new Promise((
|
|
41328
|
+
await new Promise((resolve26) => setTimeout(resolve26));
|
|
41234
41329
|
maxReadCount = 3;
|
|
41235
41330
|
continue;
|
|
41236
41331
|
}
|
|
@@ -43499,9 +43594,9 @@ data:
|
|
|
43499
43594
|
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
43500
43595
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
43501
43596
|
if (this._enableJsonResponse) {
|
|
43502
|
-
return new Promise((
|
|
43597
|
+
return new Promise((resolve26) => {
|
|
43503
43598
|
this._streamMapping.set(streamId, {
|
|
43504
|
-
resolveJson:
|
|
43599
|
+
resolveJson: resolve26,
|
|
43505
43600
|
cleanup: () => {
|
|
43506
43601
|
this._streamMapping.delete(streamId);
|
|
43507
43602
|
}
|
|
@@ -49864,7 +49959,7 @@ var Protocol = class {
|
|
|
49864
49959
|
return;
|
|
49865
49960
|
}
|
|
49866
49961
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
49867
|
-
await new Promise((
|
|
49962
|
+
await new Promise((resolve26) => setTimeout(resolve26, pollInterval));
|
|
49868
49963
|
options?.signal?.throwIfAborted();
|
|
49869
49964
|
}
|
|
49870
49965
|
} catch (error62) {
|
|
@@ -49881,7 +49976,7 @@ var Protocol = class {
|
|
|
49881
49976
|
*/
|
|
49882
49977
|
request(request, resultSchema, options) {
|
|
49883
49978
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
49884
|
-
return new Promise((
|
|
49979
|
+
return new Promise((resolve26, reject) => {
|
|
49885
49980
|
const earlyReject = (error62) => {
|
|
49886
49981
|
reject(error62);
|
|
49887
49982
|
};
|
|
@@ -49959,7 +50054,7 @@ var Protocol = class {
|
|
|
49959
50054
|
if (!parseResult.success) {
|
|
49960
50055
|
reject(parseResult.error);
|
|
49961
50056
|
} else {
|
|
49962
|
-
|
|
50057
|
+
resolve26(parseResult.data);
|
|
49963
50058
|
}
|
|
49964
50059
|
} catch (error62) {
|
|
49965
50060
|
reject(error62);
|
|
@@ -50221,12 +50316,12 @@ var Protocol = class {
|
|
|
50221
50316
|
}
|
|
50222
50317
|
} catch {
|
|
50223
50318
|
}
|
|
50224
|
-
return new Promise((
|
|
50319
|
+
return new Promise((resolve26, reject) => {
|
|
50225
50320
|
if (signal.aborted) {
|
|
50226
50321
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
50227
50322
|
return;
|
|
50228
50323
|
}
|
|
50229
|
-
const timeoutId = setTimeout(
|
|
50324
|
+
const timeoutId = setTimeout(resolve26, interval);
|
|
50230
50325
|
signal.addEventListener("abort", () => {
|
|
50231
50326
|
clearTimeout(timeoutId);
|
|
50232
50327
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -51366,7 +51461,7 @@ var McpServer = class {
|
|
|
51366
51461
|
let task = createTaskResult.task;
|
|
51367
51462
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
51368
51463
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
51369
|
-
await new Promise((
|
|
51464
|
+
await new Promise((resolve26) => setTimeout(resolve26, pollInterval));
|
|
51370
51465
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
51371
51466
|
if (!updatedTask) {
|
|
51372
51467
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -52091,14 +52186,14 @@ function claudeService(env) {
|
|
|
52091
52186
|
if (!dir) return SERVICE;
|
|
52092
52187
|
return `${SERVICE}-${createHash4("sha256").update(dir.normalize("NFC")).digest("hex").slice(0, 8)}`;
|
|
52093
52188
|
}
|
|
52094
|
-
var findWithSecurity = (args) => new Promise((
|
|
52189
|
+
var findWithSecurity = (args) => new Promise((resolve26) => {
|
|
52095
52190
|
const child = spawn3(SECURITY, [...args], { stdio: ["ignore", "pipe", "ignore"], timeout: SECURITY_TIMEOUT_MS });
|
|
52096
52191
|
let stdout = "";
|
|
52097
52192
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
52098
52193
|
stdout += chunk;
|
|
52099
52194
|
});
|
|
52100
|
-
child.once("error", () =>
|
|
52101
|
-
child.once("close", (code) =>
|
|
52195
|
+
child.once("error", () => resolve26({ status: 1, stdout: "" }));
|
|
52196
|
+
child.once("close", (code) => resolve26({ status: code ?? 1, stdout }));
|
|
52102
52197
|
});
|
|
52103
52198
|
function keychainRead(account, service, find = findWithSecurity) {
|
|
52104
52199
|
return async () => {
|
|
@@ -52119,7 +52214,7 @@ function renewEnv(env) {
|
|
|
52119
52214
|
return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== void 0 && !KEPT_OUT.includes(entry[0])));
|
|
52120
52215
|
}
|
|
52121
52216
|
function claudeRenewal(binary, env) {
|
|
52122
|
-
return () => new Promise((
|
|
52217
|
+
return () => new Promise((resolve26, reject) => {
|
|
52123
52218
|
const cwd = mkdtempSync(join16(tmpdir(), "orbital-claude-renew-"));
|
|
52124
52219
|
const child = spawn3(binary, RENEW_ARGS, { cwd, env: renewEnv(env), stdio: ["ignore", "ignore", "pipe"], timeout: RENEW_TIMEOUT_MS });
|
|
52125
52220
|
let stderr = "";
|
|
@@ -52129,7 +52224,7 @@ function claudeRenewal(binary, env) {
|
|
|
52129
52224
|
const done = (error62) => {
|
|
52130
52225
|
rmSync5(cwd, { recursive: true, force: true });
|
|
52131
52226
|
if (error62) reject(error62);
|
|
52132
|
-
else
|
|
52227
|
+
else resolve26();
|
|
52133
52228
|
};
|
|
52134
52229
|
child.once("error", done);
|
|
52135
52230
|
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 +53491,7 @@ function missionOfRow(row2) {
|
|
|
53396
53491
|
result: textOf(row2, "result"),
|
|
53397
53492
|
question: textOf(row2, "question"),
|
|
53398
53493
|
questions: questionsOf(row2),
|
|
53399
|
-
|
|
53494
|
+
receivers: [],
|
|
53400
53495
|
lead: textOf(row2, "lead"),
|
|
53401
53496
|
lastActivity: textOf(row2, "activity"),
|
|
53402
53497
|
claim: textOf(row2, "claim"),
|
|
@@ -53549,7 +53644,7 @@ var MissionStore = class {
|
|
|
53549
53644
|
source: request.source !== void 0 ? request.source : known?.source ?? null,
|
|
53550
53645
|
result: request.result !== void 0 ? request.result : known?.result ?? null,
|
|
53551
53646
|
question: request.question !== void 0 ? request.question : request.state === "waiting" ? known?.question ?? null : null,
|
|
53552
|
-
|
|
53647
|
+
receivers: request.receivers !== void 0 ? request.receivers : known?.receivers ?? [],
|
|
53553
53648
|
lead: request.lead !== void 0 ? request.lead : known?.lead ?? null,
|
|
53554
53649
|
claim: request.claim !== void 0 ? request.claim : known?.claim ?? null,
|
|
53555
53650
|
claimedAt: request.claimedAt !== void 0 ? request.claimedAt : known?.claimedAt ?? null,
|
|
@@ -53604,7 +53699,7 @@ function missionRecord(node2, mission, declared, stored) {
|
|
|
53604
53699
|
result: stored?.result ?? null,
|
|
53605
53700
|
question: stored?.question ?? null,
|
|
53606
53701
|
questions: stored?.questions ?? [],
|
|
53607
|
-
|
|
53702
|
+
receivers: stored?.receivers ?? [],
|
|
53608
53703
|
lead: stored?.lead ?? null,
|
|
53609
53704
|
lastActivity: null,
|
|
53610
53705
|
claim: stored?.claim ?? null,
|
|
@@ -53711,9 +53806,9 @@ var Telemetry = class {
|
|
|
53711
53806
|
async drain(timeoutMs) {
|
|
53712
53807
|
if (this.queues.every((queue) => !queue.running && queue.items.length === 0)) return true;
|
|
53713
53808
|
let done = null;
|
|
53714
|
-
const empty = new Promise((
|
|
53809
|
+
const empty = new Promise((resolve26) => {
|
|
53715
53810
|
done = () => {
|
|
53716
|
-
if (this.queues.every((queue) => !queue.running && queue.items.length === 0))
|
|
53811
|
+
if (this.queues.every((queue) => !queue.running && queue.items.length === 0)) resolve26(true);
|
|
53717
53812
|
};
|
|
53718
53813
|
this.drained.add(done);
|
|
53719
53814
|
});
|
|
@@ -53966,7 +54061,7 @@ var SessionHost = class {
|
|
|
53966
54061
|
transition: missionTransition(before?.state ?? "open", after.state, after.question),
|
|
53967
54062
|
reason: change?.note ?? null,
|
|
53968
54063
|
by: change?.by ?? "node",
|
|
53969
|
-
|
|
54064
|
+
receivers: after.receivers,
|
|
53970
54065
|
sessionId: after.sessionId,
|
|
53971
54066
|
lead: after.lead,
|
|
53972
54067
|
question: after.question,
|
|
@@ -53974,7 +54069,7 @@ var SessionHost = class {
|
|
|
53974
54069
|
});
|
|
53975
54070
|
}
|
|
53976
54071
|
missionWritten(mission, team, by) {
|
|
53977
|
-
this.telemetry.emit({ kind: "mission.state", mission, team, from: null, to: "open", transition: "new", reason: "written", by,
|
|
54072
|
+
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
54073
|
}
|
|
53979
54074
|
async snapshot() {
|
|
53980
54075
|
const definition = await this.definitionOf(this.options.defaultWorkspace);
|
|
@@ -54003,7 +54098,7 @@ var SessionHost = class {
|
|
|
54003
54098
|
retiring: note?.retiring ?? null
|
|
54004
54099
|
};
|
|
54005
54100
|
}), ...[...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,
|
|
54101
|
+
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
54102
|
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
54103
|
jobs: [...this.openJobs].map(([jobId, open2]) => ({ ...open2.context, jobId, since: new Date(open2.since).toISOString(), via: open2.via })),
|
|
54009
54104
|
tokens: this.telemetry.tokens(),
|
|
@@ -54057,19 +54152,48 @@ var SessionHost = class {
|
|
|
54057
54152
|
this.redeliverQuestions(id);
|
|
54058
54153
|
return info;
|
|
54059
54154
|
}
|
|
54060
|
-
redeliverQuestions(
|
|
54155
|
+
redeliverQuestions(sessionId) {
|
|
54156
|
+
const session = this.sessions.get(sessionId);
|
|
54157
|
+
if (!session) return;
|
|
54061
54158
|
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
|
-
}
|
|
54159
|
+
if (!stored.receivers.some((key2) => this.receives(session, key2))) continue;
|
|
54160
|
+
for (const open2 of this.openQuestions(stored)) if (!this.questionOpen(sessionId, open2.mission, open2.questionId)) this.deliverQuestion(sessionId, open2, stored.sessionId);
|
|
54067
54161
|
}
|
|
54068
54162
|
}
|
|
54069
|
-
|
|
54070
|
-
if (
|
|
54163
|
+
openQuestions(stored) {
|
|
54164
|
+
if (!stored.sessionId || stored.state === "paused" || !hasLead(stored.state)) return [];
|
|
54165
|
+
const base2 = { team: stored.team, mission: stored.mission, lead: stored.lead };
|
|
54166
|
+
return [
|
|
54167
|
+
...stored.state === "waiting" ? [{ ...base2, question: stored.question ?? NO_HANDOFF_TELL, questionId: null }] : [],
|
|
54168
|
+
...stored.questions.map((asked2) => ({ ...base2, question: asked2.question, questionId: asked2.id }))
|
|
54169
|
+
];
|
|
54170
|
+
}
|
|
54171
|
+
receiverKey(session) {
|
|
54172
|
+
return session.external?.externalId ? externalReceiver(session.external.externalId) : session.id;
|
|
54173
|
+
}
|
|
54174
|
+
receives(session, key2) {
|
|
54175
|
+
return key2 === session.id || key2 === this.receiverKey(session);
|
|
54176
|
+
}
|
|
54177
|
+
liveReceivers(receivers, except = null) {
|
|
54178
|
+
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)));
|
|
54179
|
+
}
|
|
54180
|
+
keyOfRemote(receiver) {
|
|
54181
|
+
if (receiver.node !== this.node) return receiverRef(receiver);
|
|
54182
|
+
const session = this.sessions.get(receiver.sessionId);
|
|
54183
|
+
return session ? this.receiverKey(session) : receiver.sessionId;
|
|
54184
|
+
}
|
|
54185
|
+
starterKey(by) {
|
|
54186
|
+
if (!by.startsWith("session:")) return null;
|
|
54187
|
+
const named2 = by.slice("session:".length);
|
|
54188
|
+
const remote = remoteReceiverOf(named2);
|
|
54189
|
+
if (remote) return this.keyOfRemote(remote);
|
|
54190
|
+
const session = this.sessions.get(named2);
|
|
54191
|
+
return session ? this.receiverKey(session) : named2;
|
|
54192
|
+
}
|
|
54193
|
+
questionOpen(sessionId, mission, questionId) {
|
|
54194
|
+
if (this.questionsInFlight.has(`${sessionId}:${mission}:${questionId ?? ""}`)) return true;
|
|
54071
54195
|
for (const [taskId, asked2] of [...this.questionDeliveries]) {
|
|
54072
|
-
if (asked2.mission !== mission || asked2.questionId !== questionId || this.taskSession.get(taskId) !==
|
|
54196
|
+
if (asked2.mission !== mission || asked2.questionId !== questionId || this.taskSession.get(taskId) !== sessionId) continue;
|
|
54073
54197
|
const task = this.tasks.get(taskId);
|
|
54074
54198
|
if (task && !(task.status && isTerminal(task.status.state))) return true;
|
|
54075
54199
|
this.questionDeliveries.delete(taskId);
|
|
@@ -54101,7 +54225,7 @@ var SessionHost = class {
|
|
|
54101
54225
|
async settleExternal(sessionId, settle2) {
|
|
54102
54226
|
const session = this.sessions.get(sessionId);
|
|
54103
54227
|
if (!session?.external) throw new SessionHostError("not-found", `There is no attached session ${sessionId} on this node.`);
|
|
54104
|
-
const taskId = settle2.taskId ?? session.running;
|
|
54228
|
+
const taskId = settle2.taskId ?? session.running ?? session.external.open.keys().next().value ?? null;
|
|
54105
54229
|
const task = taskId ? this.tasks.get(taskId) : void 0;
|
|
54106
54230
|
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
54231
|
await this.finish(sessionId, task, "TASK_STATE_COMPLETED", settle2.text);
|
|
@@ -54135,23 +54259,21 @@ var SessionHost = class {
|
|
|
54135
54259
|
await this.finish(session.id, task, "TASK_STATE_FAILED", `The attached session ${session.id} is detached.`);
|
|
54136
54260
|
return;
|
|
54137
54261
|
}
|
|
54138
|
-
|
|
54262
|
+
const asked2 = this.questionOf(message);
|
|
54263
|
+
if (asked2 === null) session.running = task.id;
|
|
54139
54264
|
await this.update(session.id, task, "TASK_STATE_WORKING");
|
|
54140
54265
|
const fromAgent = message.metadata?.[ORBITAL_META.fromAgent];
|
|
54141
54266
|
const fromSession = message.metadata?.[ORBITAL_META.fromSession];
|
|
54142
|
-
const asked2 = this.questionOf(message);
|
|
54143
54267
|
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((
|
|
54268
|
+
const settled = new Promise((resolve26) => this.externalWaits.set(task.id, resolve26)).finally(() => {
|
|
54269
|
+
this.externalWaits.delete(task.id);
|
|
54270
|
+
external.open.delete(task.id);
|
|
54271
|
+
if (session.running === task.id) session.running = null;
|
|
54272
|
+
});
|
|
54145
54273
|
if (asked2 !== null) external.open.set(task.id, delivery);
|
|
54146
54274
|
if (external.listeners.size === 0) external.waiting.push(delivery);
|
|
54147
54275
|
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
|
-
}
|
|
54276
|
+
if (asked2 === null) await settled;
|
|
54155
54277
|
}
|
|
54156
54278
|
stopFollowing() {
|
|
54157
54279
|
this.following.abort();
|
|
@@ -54166,9 +54288,9 @@ var SessionHost = class {
|
|
|
54166
54288
|
if (this.followingHub) return;
|
|
54167
54289
|
let listener = null;
|
|
54168
54290
|
try {
|
|
54169
|
-
await Promise.race([this.settled(), new Promise((
|
|
54170
|
-
listener =
|
|
54171
|
-
this.followListeners.add(
|
|
54291
|
+
await Promise.race([this.settled(), new Promise((resolve26) => {
|
|
54292
|
+
listener = resolve26;
|
|
54293
|
+
this.followListeners.add(resolve26);
|
|
54172
54294
|
})]);
|
|
54173
54295
|
} finally {
|
|
54174
54296
|
if (listener) this.followListeners.delete(listener);
|
|
@@ -54259,7 +54381,7 @@ var SessionHost = class {
|
|
|
54259
54381
|
const leadLives = known?.sessionId ? this.sessions.has(known.sessionId) : false;
|
|
54260
54382
|
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
54383
|
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
|
|
54384
|
+
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
54385
|
const ended = known && hasLead(known.state) && !leadLives ? `the lead session ${known.sessionId ?? "(none)"} ended with its node` : null;
|
|
54264
54386
|
if (known?.state === "done") throw new SessionHostError("invalid", `The mission ${missionId} is done. Give the team a new mission for new work.`);
|
|
54265
54387
|
const claim2 = declared?.source ?? missionId;
|
|
@@ -54270,12 +54392,13 @@ var SessionHost = class {
|
|
|
54270
54392
|
const resumable = ended && known?.sessionId && readSessionRecord(this.paths.sessionsDir, known.sessionId) !== null ? known.sessionId : null;
|
|
54271
54393
|
if (resumable) {
|
|
54272
54394
|
const task2 = await this.submit({ sessionId: resumable, message });
|
|
54273
|
-
|
|
54395
|
+
const starter = this.starterKey(by);
|
|
54396
|
+
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
54397
|
return { task: task2, duplicate: false };
|
|
54275
54398
|
}
|
|
54276
54399
|
const task = await this.submit({ message, start: { agent: team, workspaceDir, team, mission } });
|
|
54277
54400
|
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: [],
|
|
54401
|
+
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
54402
|
return { task, duplicate: false };
|
|
54280
54403
|
}
|
|
54281
54404
|
pauseInterrupted() {
|
|
@@ -54378,7 +54501,7 @@ var SessionHost = class {
|
|
|
54378
54501
|
for (const { relay: port } of this.options.ports.missionRelays()) {
|
|
54379
54502
|
const answered = await port.reply({ missionId, text: text3, ...questionId && { questionId }, by, ...this.claimOfBy(by) });
|
|
54380
54503
|
if (!answered) continue;
|
|
54381
|
-
await this.closeQuestions(missionId, questionId ?? void 0,
|
|
54504
|
+
await this.closeQuestions(missionId, questionId ?? void 0, by, text3);
|
|
54382
54505
|
return answered;
|
|
54383
54506
|
}
|
|
54384
54507
|
}
|
|
@@ -54389,14 +54512,14 @@ var SessionHost = class {
|
|
|
54389
54512
|
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
54513
|
const lead = known.sessionId;
|
|
54391
54514
|
this.missions.change(missionId, { state: "active", by, note: "reply" });
|
|
54392
|
-
await this.closeQuestions(missionId, null,
|
|
54515
|
+
await this.closeQuestions(missionId, null, by, text3);
|
|
54393
54516
|
await this.submit({ sessionId: lead, message: { messageId: randomUUID4(), role: "ROLE_USER", parts: [textPart(`[reply to your question on the mission ${missionId}, from ${by}]
|
|
54394
54517
|
${text3}`)] } });
|
|
54395
54518
|
return this.missionOf(missionId);
|
|
54396
54519
|
}
|
|
54397
54520
|
async answerAsked(by, missionId, lead, asked2, text3) {
|
|
54398
54521
|
this.missions.answer(missionId, asked2.id);
|
|
54399
|
-
await this.closeQuestions(missionId, asked2.id,
|
|
54522
|
+
await this.closeQuestions(missionId, asked2.id, by, text3);
|
|
54400
54523
|
const session = this.sessions.get(lead);
|
|
54401
54524
|
if (session?.running && !session.external) {
|
|
54402
54525
|
this.steering.set(lead, [...this.steering.get(lead) ?? [], { from: by, missionId, text: `reply to your question ${asked2.id}: ${asked2.question}
|
|
@@ -54413,7 +54536,7 @@ ${text3}`)] } });
|
|
|
54413
54536
|
const missionId = given.mission;
|
|
54414
54537
|
const known = this.missions.get(missionId);
|
|
54415
54538
|
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
|
|
54539
|
+
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
54540
|
this.askedInTurn.add(known.sessionId);
|
|
54418
54541
|
this.recordQuestion(missionId, known.sessionId, given.question);
|
|
54419
54542
|
return this.missionOf(missionId);
|
|
@@ -54421,8 +54544,8 @@ ${text3}`)] } });
|
|
|
54421
54544
|
recordQuestion(missionId, sessionId, question) {
|
|
54422
54545
|
const asked2 = { id: `q-${randomUUID4().slice(0, 8)}`, question, at: new Date(this.options.context.now()).toISOString() };
|
|
54423
54546
|
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.
|
|
54547
|
+
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 });
|
|
54548
|
+
this.tellReceivers(after.receivers, { team: after.team, mission: missionId, lead: after.lead, question: asked2.question, questionId: asked2.id }, sessionId);
|
|
54426
54549
|
}
|
|
54427
54550
|
async say(by, target, text3, reader, relay = true) {
|
|
54428
54551
|
const named2 = this.missions.get(target);
|
|
@@ -54478,16 +54601,28 @@ ${text3}`)] } });
|
|
|
54478
54601
|
const mission = message.metadata?.[ORBITAL_META.mission];
|
|
54479
54602
|
return typeof mission === "string" ? mission : null;
|
|
54480
54603
|
}
|
|
54481
|
-
async closeQuestions(mission, questionId, text3) {
|
|
54604
|
+
async closeQuestions(mission, questionId, by, text3) {
|
|
54605
|
+
const answerer = by.startsWith("session:") ? by.slice("session:".length) : null;
|
|
54606
|
+
const told = /* @__PURE__ */ new Set();
|
|
54482
54607
|
for (const [taskId, asked2] of [...this.questionDeliveries]) {
|
|
54483
54608
|
if (asked2.mission !== mission || questionId !== void 0 && asked2.questionId !== questionId) continue;
|
|
54484
54609
|
this.questionDeliveries.delete(taskId);
|
|
54485
54610
|
const task = this.tasks.get(taskId);
|
|
54486
|
-
const
|
|
54487
|
-
if (!task || !
|
|
54488
|
-
await this.finish(
|
|
54611
|
+
const receiver = this.taskSession.get(taskId);
|
|
54612
|
+
if (!task || !receiver || task.status && isTerminal(task.status.state)) continue;
|
|
54613
|
+
await this.finish(receiver, task, "TASK_STATE_COMPLETED", `answered by ${by}: ${text3}`);
|
|
54489
54614
|
this.externalWaits.get(taskId)?.();
|
|
54615
|
+
if (receiver !== answerer) this.tellAnswered(receiver, { kind: "answered", mission, question: asked2.questionId, by, text: text3 }, told);
|
|
54490
54616
|
}
|
|
54617
|
+
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);
|
|
54618
|
+
}
|
|
54619
|
+
tellAnswered(sessionId, answered, told) {
|
|
54620
|
+
const external = this.sessions.get(sessionId)?.external;
|
|
54621
|
+
if (!external?.attached || told.has(sessionId)) return;
|
|
54622
|
+
told.add(sessionId);
|
|
54623
|
+
const answers = (delivery) => delivery.mission === answered.mission && (answered.question === null || delivery.question === answered.question);
|
|
54624
|
+
for (let index = external.waiting.length - 1; index >= 0; index -= 1) if (answers(external.waiting[index])) external.waiting.splice(index, 1);
|
|
54625
|
+
for (const listener of external.listeners) listener(answered);
|
|
54491
54626
|
}
|
|
54492
54627
|
busyBelow(sessionId) {
|
|
54493
54628
|
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 +54649,46 @@ ${text3}`)] } });
|
|
|
54514
54649
|
}
|
|
54515
54650
|
this.nudged.delete(sessionId);
|
|
54516
54651
|
this.missions.change(known.mission, { state: "waiting", by: `session:${sessionId}`, note: question === "" ? NO_HANDOFF_NOTE : stateWord(state), question: question === "" ? null : question });
|
|
54517
|
-
this.
|
|
54652
|
+
this.tellReceivers(known.receivers, { team: known.team, mission: known.mission, lead: known.lead, question: question === "" ? NO_HANDOFF_TELL : question, questionId: null }, sessionId);
|
|
54518
54653
|
}
|
|
54519
|
-
async joinMission(
|
|
54520
|
-
const session = this.sessions.get(sessionId);
|
|
54521
|
-
if (!session
|
|
54654
|
+
async joinMission(receiver, missionId) {
|
|
54655
|
+
const session = "sessionId" in receiver ? this.sessions.get(receiver.sessionId) : void 0;
|
|
54656
|
+
if ("sessionId" in receiver && !session) throw new SessionHostError("not-found", `There is no session ${receiver.sessionId} on this node.`);
|
|
54522
54657
|
const known = this.missions.get(missionId);
|
|
54523
|
-
if (!known) {
|
|
54658
|
+
if (!known && session) {
|
|
54524
54659
|
for (const { relay } of this.options.ports.missionRelays()) {
|
|
54525
|
-
const joined = await relay.join({ missionId,
|
|
54660
|
+
const joined = await relay.join({ missionId, receiver: { node: this.node, sessionId: session.id }, ...this.claimOfSession(session.id) });
|
|
54526
54661
|
if (joined) return joined;
|
|
54527
54662
|
}
|
|
54528
54663
|
}
|
|
54529
|
-
|
|
54664
|
+
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.`);
|
|
54665
|
+
if (!session) return this.joinHere(externalReceiver("externalId" in receiver ? receiver.externalId : ""), "cli", missionId, null);
|
|
54666
|
+
return this.joinHere(this.receiverKey(session), `session:${session.id}`, missionId, [session.id]);
|
|
54530
54667
|
}
|
|
54531
54668
|
async joinRemote(request) {
|
|
54532
|
-
|
|
54669
|
+
const key2 = this.keyOfRemote(request.receiver);
|
|
54670
|
+
const local = remoteReceiverOf(key2) === null ? [request.receiver.sessionId] : null;
|
|
54671
|
+
return this.joinHere(key2, `session:${receiverRef(request.receiver)}`, request.missionId, local);
|
|
54533
54672
|
}
|
|
54534
|
-
async
|
|
54535
|
-
const
|
|
54536
|
-
|
|
54673
|
+
async leaveMission(receiver, missionId) {
|
|
54674
|
+
const known = this.missions.get(missionId);
|
|
54675
|
+
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.`);
|
|
54676
|
+
const session = "sessionId" in receiver ? this.sessions.get(receiver.sessionId) : void 0;
|
|
54677
|
+
const leaves = (key2) => session ? this.receives(session, key2) : "sessionId" in receiver ? key2 === receiver.sessionId : externalIdOf(key2) === receiver.externalId;
|
|
54678
|
+
const kept = known.receivers.filter((key2) => !leaves(key2));
|
|
54679
|
+
if (kept.length === known.receivers.length) return this.missionOf(missionId);
|
|
54680
|
+
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 });
|
|
54681
|
+
return this.missionOf(missionId);
|
|
54682
|
+
}
|
|
54683
|
+
async joinHere(key2, by, missionId, deliverTo) {
|
|
54537
54684
|
const known = this.missions.get(missionId);
|
|
54538
54685
|
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
|
-
|
|
54686
|
+
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] });
|
|
54687
|
+
const remote = remoteReceiverOf(key2);
|
|
54688
|
+
const live2 = this.liveReceivers([key2]).filter((session) => deliverTo === null || deliverTo.includes(session.id));
|
|
54689
|
+
for (const open2 of this.openQuestions(stored)) {
|
|
54690
|
+
if (remote && remote.node !== this.node) this.askRemote(remote, open2, stored.sessionId);
|
|
54691
|
+
for (const session of live2) if (!this.questionOpen(session.id, open2.mission, open2.questionId)) this.deliverQuestion(session.id, open2, stored.sessionId);
|
|
54543
54692
|
}
|
|
54544
54693
|
return this.missionOf(missionId);
|
|
54545
54694
|
}
|
|
@@ -54549,36 +54698,34 @@ ${text3}`)] } });
|
|
|
54549
54698
|
missionTeam(missionId) {
|
|
54550
54699
|
return this.missions.get(missionId)?.team ?? null;
|
|
54551
54700
|
}
|
|
54552
|
-
async
|
|
54553
|
-
const
|
|
54554
|
-
if (!
|
|
54555
|
-
this.deliverQuestion(
|
|
54701
|
+
async askReceiver(question) {
|
|
54702
|
+
const session = this.sessions.get(question.receiver.sessionId);
|
|
54703
|
+
if (!session || question.receiver.node !== this.node || this.liveReceivers([session.id]).length === 0) return false;
|
|
54704
|
+
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
54705
|
return true;
|
|
54557
54706
|
}
|
|
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;
|
|
54707
|
+
tellReceivers(receivers, open2, leadSessionId) {
|
|
54708
|
+
for (const key2 of receivers) {
|
|
54709
|
+
const remote = remoteReceiverOf(key2);
|
|
54710
|
+
if (remote && remote.node !== this.node) this.askRemote(remote, open2, leadSessionId);
|
|
54569
54711
|
}
|
|
54570
|
-
const
|
|
54571
|
-
if (!giver) return;
|
|
54572
|
-
this.deliverQuestion(giver.id, known, sessionId);
|
|
54712
|
+
for (const session of this.liveReceivers(receivers)) this.deliverQuestion(session.id, open2, leadSessionId);
|
|
54573
54713
|
}
|
|
54574
|
-
|
|
54714
|
+
askRemote(receiver, open2, leadSessionId) {
|
|
54715
|
+
const question = { missionId: open2.mission, node: this.node, lead: open2.lead, leadSessionId, question: open2.question, ...open2.questionId && { questionId: open2.questionId }, receiver, team: open2.team };
|
|
54716
|
+
void (async () => {
|
|
54717
|
+
for (const { relay } of this.options.ports.missionRelays()) if (await relay.ask(question)) return;
|
|
54718
|
+
this.options.context.output.warn(`no plugin carried the question of ${open2.mission} to ${receiverRef(receiver)}`);
|
|
54719
|
+
})().catch((error62) => this.options.context.output.warn(`could not tell ${receiverRef(receiver)} the question of ${open2.mission}: ${error62 instanceof Error ? error62.message : String(error62)}`));
|
|
54720
|
+
}
|
|
54721
|
+
deliverQuestion(receiverId, known, sessionId) {
|
|
54575
54722
|
const missionId = known.mission;
|
|
54576
54723
|
const question = known.question;
|
|
54577
54724
|
const questionId = known.questionId;
|
|
54578
|
-
const key2 = `${
|
|
54725
|
+
const key2 = `${receiverId}:${missionId}:${questionId ?? ""}`;
|
|
54579
54726
|
this.questionsInFlight.add(key2);
|
|
54580
54727
|
void this.submit({
|
|
54581
|
-
sessionId:
|
|
54728
|
+
sessionId: receiverId,
|
|
54582
54729
|
message: {
|
|
54583
54730
|
messageId: randomUUID4(),
|
|
54584
54731
|
role: "ROLE_USER",
|
|
@@ -54593,7 +54740,7 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
54593
54740
|
}
|
|
54594
54741
|
}).then((task) => {
|
|
54595
54742
|
this.questionDeliveries.set(task.id, { mission: known.mission, questionId });
|
|
54596
|
-
}).catch((error62) => this.options.context.output.warn(`could not tell the
|
|
54743
|
+
}).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
54744
|
}
|
|
54598
54745
|
async missionActivity(missionId, limit) {
|
|
54599
54746
|
const sessionId = this.missions.get(missionId)?.sessionId;
|
|
@@ -54811,8 +54958,8 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
54811
54958
|
}
|
|
54812
54959
|
}
|
|
54813
54960
|
if (signal?.aborted) return;
|
|
54814
|
-
await new Promise((
|
|
54815
|
-
wake =
|
|
54961
|
+
await new Promise((resolve26) => {
|
|
54962
|
+
wake = resolve26;
|
|
54816
54963
|
});
|
|
54817
54964
|
wake = null;
|
|
54818
54965
|
}
|
|
@@ -54888,9 +55035,13 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
54888
55035
|
}
|
|
54889
55036
|
missionRoots(readerSessionId) {
|
|
54890
55037
|
const outside = this.sessions.get(readerSessionId)?.external !== void 0;
|
|
54891
|
-
const missionRoots = this.missions.all().filter((stored) => stored.sessionId !== null && (outside || stored.
|
|
55038
|
+
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
55039
|
return new Set(missionRoots);
|
|
54893
55040
|
}
|
|
55041
|
+
receivesFor(sessionId, receivers) {
|
|
55042
|
+
const session = this.sessions.get(sessionId);
|
|
55043
|
+
return session !== void 0 && receivers.some((key2) => this.receives(session, key2));
|
|
55044
|
+
}
|
|
54894
55045
|
reachRoots(readerSessionId) {
|
|
54895
55046
|
const outside = this.sessions.get(readerSessionId)?.external !== void 0;
|
|
54896
55047
|
const attachedRoots = outside ? [...this.sessions.values()].filter((one) => one.external?.attached === true).map((one) => this.rootOf(one.id)) : [];
|
|
@@ -56457,7 +56608,7 @@ var StdioClientTransport = class {
|
|
|
56457
56608
|
if (this._process) {
|
|
56458
56609
|
throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
|
56459
56610
|
}
|
|
56460
|
-
return new Promise((
|
|
56611
|
+
return new Promise((resolve26, reject) => {
|
|
56461
56612
|
this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], {
|
|
56462
56613
|
// merge default env with server env because mcp server needs some env vars
|
|
56463
56614
|
env: {
|
|
@@ -56474,7 +56625,7 @@ var StdioClientTransport = class {
|
|
|
56474
56625
|
this.onerror?.(error62);
|
|
56475
56626
|
});
|
|
56476
56627
|
this._process.on("spawn", () => {
|
|
56477
|
-
|
|
56628
|
+
resolve26();
|
|
56478
56629
|
});
|
|
56479
56630
|
this._process.on("close", (_code) => {
|
|
56480
56631
|
this._process = void 0;
|
|
@@ -56539,22 +56690,22 @@ var StdioClientTransport = class {
|
|
|
56539
56690
|
if (this._process) {
|
|
56540
56691
|
const processToClose = this._process;
|
|
56541
56692
|
this._process = void 0;
|
|
56542
|
-
const closePromise = new Promise((
|
|
56693
|
+
const closePromise = new Promise((resolve26) => {
|
|
56543
56694
|
processToClose.once("close", () => {
|
|
56544
|
-
|
|
56695
|
+
resolve26();
|
|
56545
56696
|
});
|
|
56546
56697
|
});
|
|
56547
56698
|
try {
|
|
56548
56699
|
processToClose.stdin?.end();
|
|
56549
56700
|
} catch {
|
|
56550
56701
|
}
|
|
56551
|
-
await Promise.race([closePromise, new Promise((
|
|
56702
|
+
await Promise.race([closePromise, new Promise((resolve26) => setTimeout(resolve26, 2e3).unref())]);
|
|
56552
56703
|
if (processToClose.exitCode === null) {
|
|
56553
56704
|
try {
|
|
56554
56705
|
processToClose.kill("SIGTERM");
|
|
56555
56706
|
} catch {
|
|
56556
56707
|
}
|
|
56557
|
-
await Promise.race([closePromise, new Promise((
|
|
56708
|
+
await Promise.race([closePromise, new Promise((resolve26) => setTimeout(resolve26, 2e3).unref())]);
|
|
56558
56709
|
}
|
|
56559
56710
|
if (processToClose.exitCode === null) {
|
|
56560
56711
|
try {
|
|
@@ -56566,15 +56717,15 @@ var StdioClientTransport = class {
|
|
|
56566
56717
|
this._readBuffer.clear();
|
|
56567
56718
|
}
|
|
56568
56719
|
send(message) {
|
|
56569
|
-
return new Promise((
|
|
56720
|
+
return new Promise((resolve26) => {
|
|
56570
56721
|
if (!this._process?.stdin) {
|
|
56571
56722
|
throw new Error("Not connected");
|
|
56572
56723
|
}
|
|
56573
56724
|
const json4 = serializeMessage(message);
|
|
56574
56725
|
if (this._process.stdin.write(json4)) {
|
|
56575
|
-
|
|
56726
|
+
resolve26();
|
|
56576
56727
|
} else {
|
|
56577
|
-
this._process.stdin.once("drain",
|
|
56728
|
+
this._process.stdin.once("drain", resolve26);
|
|
56578
56729
|
}
|
|
56579
56730
|
});
|
|
56580
56731
|
}
|
|
@@ -56667,12 +56818,12 @@ var StdioServerTransport = class {
|
|
|
56667
56818
|
this.onclose?.();
|
|
56668
56819
|
}
|
|
56669
56820
|
send(message) {
|
|
56670
|
-
return new Promise((
|
|
56821
|
+
return new Promise((resolve26) => {
|
|
56671
56822
|
const json4 = serializeMessage(message);
|
|
56672
56823
|
if (this._stdout.write(json4)) {
|
|
56673
|
-
|
|
56824
|
+
resolve26();
|
|
56674
56825
|
} else {
|
|
56675
|
-
this._stdout.once("drain",
|
|
56826
|
+
this._stdout.once("drain", resolve26);
|
|
56676
56827
|
}
|
|
56677
56828
|
});
|
|
56678
56829
|
}
|
|
@@ -56781,6 +56932,7 @@ function agentTools(options) {
|
|
|
56781
56932
|
};
|
|
56782
56933
|
const send = (message, wait) => node2.send(message, wait);
|
|
56783
56934
|
const a2a = () => node2.a2a();
|
|
56935
|
+
const own2 = async () => options.attached ? await options.attached() : options.sessionId;
|
|
56784
56936
|
return [
|
|
56785
56937
|
defineMcpTool({
|
|
56786
56938
|
name: "agentList",
|
|
@@ -56824,7 +56976,7 @@ function agentTools(options) {
|
|
|
56824
56976
|
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
56977
|
inputSchema: {},
|
|
56826
56978
|
run: async () => {
|
|
56827
|
-
if (!
|
|
56979
|
+
if (!await own2()) throw new Error("agentPeers needs your own session: only an agent the node runs has peers.");
|
|
56828
56980
|
return await node2.peers();
|
|
56829
56981
|
}
|
|
56830
56982
|
}),
|
|
@@ -56904,13 +57056,13 @@ function agentTools(options) {
|
|
|
56904
57056
|
}),
|
|
56905
57057
|
defineMcpTool({
|
|
56906
57058
|
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
|
|
57059
|
+
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
57060
|
inputSchema: {},
|
|
56909
57061
|
run: async () => node2.missions()
|
|
56910
57062
|
}),
|
|
56911
57063
|
defineMcpTool({
|
|
56912
57064
|
name: "missionShow",
|
|
56913
|
-
description: "Show one mission by its id: its brief, its state, its open questions (questions: each lead question that waits on
|
|
57065
|
+
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
57066
|
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
56915
57067
|
run: async (params) => {
|
|
56916
57068
|
const found = (await node2.missions()).filter((record3) => record3.missionId === params.mission);
|
|
@@ -56962,7 +57114,7 @@ function agentTools(options) {
|
|
|
56962
57114
|
}),
|
|
56963
57115
|
defineMcpTool({
|
|
56964
57116
|
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.",
|
|
57117
|
+
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
57118
|
inputSchema: {
|
|
56967
57119
|
mission: MissionIdSchema.describe("The mission id."),
|
|
56968
57120
|
text: external_exports.string().min(1).describe("The answer, in full: the lead has none of your context."),
|
|
@@ -56996,13 +57148,22 @@ function agentTools(options) {
|
|
|
56996
57148
|
}),
|
|
56997
57149
|
defineMcpTool({
|
|
56998
57150
|
name: "missionJoin",
|
|
56999
|
-
description: "
|
|
57151
|
+
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
57152
|
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
57001
57153
|
run: async (params) => {
|
|
57002
|
-
if (!
|
|
57154
|
+
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
57155
|
return node2.joinMission(params.mission);
|
|
57004
57156
|
}
|
|
57005
57157
|
}),
|
|
57158
|
+
defineMcpTool({
|
|
57159
|
+
name: "missionLeave",
|
|
57160
|
+
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.",
|
|
57161
|
+
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
57162
|
+
run: async (params) => {
|
|
57163
|
+
if (!await own2()) throw new Error("missionLeave needs your own session: only a decision receiver leaves a mission.");
|
|
57164
|
+
return node2.leaveMission(params.mission);
|
|
57165
|
+
}
|
|
57166
|
+
}),
|
|
57006
57167
|
defineMcpTool({
|
|
57007
57168
|
name: "missionNew",
|
|
57008
57169
|
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 +57183,7 @@ function agentTools(options) {
|
|
|
57022
57183
|
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
57184
|
inputSchema: {},
|
|
57024
57185
|
run: async () => {
|
|
57025
|
-
if (!
|
|
57186
|
+
if (!await own2()) throw new Error("missionGet needs your own session: only an agent the node runs has a mission.");
|
|
57026
57187
|
return await node2.mission();
|
|
57027
57188
|
}
|
|
57028
57189
|
}),
|
|
@@ -57034,7 +57195,7 @@ function agentTools(options) {
|
|
|
57034
57195
|
state: external_exports.enum(["done", "failed"]).optional().describe("done when the end state is reached, failed when it cannot be. Default: done.")
|
|
57035
57196
|
},
|
|
57036
57197
|
run: async (params) => {
|
|
57037
|
-
if (!
|
|
57198
|
+
if (!await own2()) throw new Error("missionHandoff needs your own session: only the lead session of a mission hands it off.");
|
|
57038
57199
|
return await node2.handoff({ result: params.result, state: params.state ?? "done" });
|
|
57039
57200
|
}
|
|
57040
57201
|
}),
|
|
@@ -57047,18 +57208,18 @@ function agentTools(options) {
|
|
|
57047
57208
|
state: external_exports.string().min(1).optional().describe("The state of the linked thing, such as open, merged or closed.")
|
|
57048
57209
|
},
|
|
57049
57210
|
run: async (params) => {
|
|
57050
|
-
if (!
|
|
57211
|
+
if (!await own2()) throw new Error("missionLink needs your own session: only the lead session of a mission adds a link to it.");
|
|
57051
57212
|
return await node2.link({ label: params.label, url: params.url, state: params.state ?? null });
|
|
57052
57213
|
}
|
|
57053
57214
|
}),
|
|
57054
57215
|
defineMcpTool({
|
|
57055
57216
|
name: "missionAsk",
|
|
57056
|
-
description: "Ask the
|
|
57217
|
+
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
57218
|
inputSchema: {
|
|
57058
|
-
question: external_exports.string().min(1).describe("The question, in full, with the options you see and what each one means: the
|
|
57219
|
+
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
57220
|
},
|
|
57060
57221
|
run: async (params) => {
|
|
57061
|
-
if (!
|
|
57222
|
+
if (!await own2()) throw new Error("missionAsk needs your own session: only the lead session of a mission asks its decision receivers.");
|
|
57062
57223
|
return await node2.ask({ question: params.question });
|
|
57063
57224
|
}
|
|
57064
57225
|
})
|
|
@@ -57620,8 +57781,8 @@ function relaySender(plugin, by) {
|
|
|
57620
57781
|
function pluginSender(plugin) {
|
|
57621
57782
|
return { channel: plugin, peer: plugin, from: { kind: "node", name: plugin } };
|
|
57622
57783
|
}
|
|
57623
|
-
function remoteSessionSender(plugin,
|
|
57624
|
-
return { channel: plugin, peer:
|
|
57784
|
+
function remoteSessionSender(plugin, session) {
|
|
57785
|
+
return { channel: plugin, peer: session.node, from: { kind: "session", name: session.sessionId, ade: session.node } };
|
|
57625
57786
|
}
|
|
57626
57787
|
function missionTarget(node2, team) {
|
|
57627
57788
|
return team ? { kind: "team", name: team, ade: node2 } : { kind: "node", name: node2 };
|
|
@@ -57760,10 +57921,10 @@ function a2aHandlerFor(host, reader, confined = false, network = null) {
|
|
|
57760
57921
|
};
|
|
57761
57922
|
}
|
|
57762
57923
|
function readBody(request) {
|
|
57763
|
-
return new Promise((
|
|
57924
|
+
return new Promise((resolve26, reject) => {
|
|
57764
57925
|
const chunks = [];
|
|
57765
57926
|
request.on("data", (chunk) => chunks.push(chunk));
|
|
57766
|
-
request.on("end", () =>
|
|
57927
|
+
request.on("end", () => resolve26(Buffer.concat(chunks).toString("utf8")));
|
|
57767
57928
|
request.on("error", reject);
|
|
57768
57929
|
});
|
|
57769
57930
|
}
|
|
@@ -57827,7 +57988,7 @@ async function serveHostApi(request, response, url2, api) {
|
|
|
57827
57988
|
if (request.method === "POST" && url2.pathname === "/orbital/host/missions/ask") {
|
|
57828
57989
|
const asked2 = MissionQuestionSchema.safeParse(JSON.parse(await readBody(request) || "{}"));
|
|
57829
57990
|
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.
|
|
57991
|
+
return json3(response, 200, { delivered: await api.askReceiver(asked2.data) });
|
|
57831
57992
|
}
|
|
57832
57993
|
if (request.method === "POST" && url2.pathname === "/orbital/host/settled") {
|
|
57833
57994
|
const { orFollowing } = JSON.parse(await readBody(request) || "{}");
|
|
@@ -57946,11 +58107,16 @@ async function startNodeServer(options) {
|
|
|
57946
58107
|
const by = caller.bound ? `session:${caller.bound}` : "cli";
|
|
57947
58108
|
return json3(response, 200, control[1] === "pause" ? await options.host.pauseMission(by, asked2.data.missionId) : await options.host.resumeMission(by, asked2.data.missionId));
|
|
57948
58109
|
}
|
|
57949
|
-
|
|
57950
|
-
|
|
58110
|
+
const membership = /^\/orbital\/missions\/(join|leave)$/.exec(url2.pathname);
|
|
58111
|
+
if (request.method === "POST" && membership) {
|
|
57951
58112
|
const asked2 = MissionJoinSchema.safeParse(JSON.parse(await readBody(request) || "{}"));
|
|
57952
58113
|
if (!asked2.success) return json3(response, 400, { error: asked2.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ") });
|
|
57953
|
-
|
|
58114
|
+
const session = caller.bound ?? caller.reader;
|
|
58115
|
+
const holder = caller.presented === caller.token;
|
|
58116
|
+
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." });
|
|
58117
|
+
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.` });
|
|
58118
|
+
const receiver = asked2.data.externalId ? { externalId: asked2.data.externalId } : { sessionId: session };
|
|
58119
|
+
return json3(response, 200, membership[1] === "join" ? await options.host.joinMission(receiver, asked2.data.missionId) : await options.host.leaveMission(receiver, asked2.data.missionId));
|
|
57954
58120
|
}
|
|
57955
58121
|
if (request.method === "POST" && url2.pathname === "/orbital/missions/new") {
|
|
57956
58122
|
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 +58154,7 @@ async function startNodeServer(options) {
|
|
|
57988
58154
|
const named2 = asked2.data.missionId ?? own2?.missionId ?? null;
|
|
57989
58155
|
if (!named2) return json3(response, 400, { error: "Name the mission by its id." });
|
|
57990
58156
|
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
|
|
58157
|
+
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
58158
|
return json3(response, 200, await options.host.askMission({ mission: named2, question: asked2.data.question, sessionId: session }));
|
|
57993
58159
|
}
|
|
57994
58160
|
const activity = /^\/orbital\/missions\/([^/]+)\/activity$/.exec(url2.pathname);
|
|
@@ -58029,6 +58195,20 @@ async function startNodeServer(options) {
|
|
|
58029
58195
|
return json3(response, 502, { error: `The panel ${plugin2}/${id} did not answer: ${error62 instanceof Error ? error62.message : String(error62)}` });
|
|
58030
58196
|
}
|
|
58031
58197
|
}
|
|
58198
|
+
const commandPath = /^\/orbital\/plugins\/([^/]+)\/commands\/([^/]+)$/.exec(url2.pathname);
|
|
58199
|
+
if (request.method === "POST" && commandPath) {
|
|
58200
|
+
if (presented !== token) return json3(response, 403, { error: "Only a holder of the node secret runs a command of a plugin." });
|
|
58201
|
+
const [name, command] = [decodeURIComponent(commandPath[1]), decodeURIComponent(commandPath[2])];
|
|
58202
|
+
const plugin2 = options.ports.plugins.find((one) => one.name === name);
|
|
58203
|
+
if (!plugin2?.command) return json3(response, 404, { error: `No plugin ${name} with commands runs on this node.` });
|
|
58204
|
+
const asked2 = PluginCommandRequestSchema.safeParse(await readBody(request).then((raw) => JSON.parse(raw)).catch(() => null));
|
|
58205
|
+
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.` });
|
|
58206
|
+
try {
|
|
58207
|
+
return json3(response, 200, await plugin2.command(asked2.data));
|
|
58208
|
+
} catch (error62) {
|
|
58209
|
+
return json3(response, 502, { error: `The command ${name} ${command} failed: ${error62 instanceof Error ? error62.message : String(error62)}` });
|
|
58210
|
+
}
|
|
58211
|
+
}
|
|
58032
58212
|
if (url2.pathname === "/orbital/telemetry/snapshot" || url2.pathname === "/orbital/telemetry/events") {
|
|
58033
58213
|
if (request.method === "GET" && url2.pathname === "/orbital/telemetry/snapshot" && (presented === token || bound)) return json3(response, 200, await options.host.snapshot());
|
|
58034
58214
|
if (presented !== token) return json3(response, 403, { error: "Only a holder of the node secret reads the telemetry stream of the node." });
|
|
@@ -58188,7 +58368,7 @@ async function startNodeServer(options) {
|
|
|
58188
58368
|
const result2 = await dispatchA2a(await readBody(request), a2aHandlerFor(options.host, reader, bound !== null, httpSender(request.socket.remoteAddress)));
|
|
58189
58369
|
if (result2.kind === "json") return json3(response, 200, result2.body);
|
|
58190
58370
|
response.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
|
|
58191
|
-
const closed = new Promise((
|
|
58371
|
+
const closed = new Promise((resolve26) => response.on("close", resolve26));
|
|
58192
58372
|
const iterator = result2.events[Symbol.asyncIterator]();
|
|
58193
58373
|
for (; ; ) {
|
|
58194
58374
|
const next = await Promise.race([iterator.next(), closed.then(() => null)]);
|
|
@@ -58201,9 +58381,9 @@ async function startNodeServer(options) {
|
|
|
58201
58381
|
}
|
|
58202
58382
|
response.end();
|
|
58203
58383
|
}
|
|
58204
|
-
await new Promise((
|
|
58384
|
+
await new Promise((resolve26, reject) => {
|
|
58205
58385
|
server.once("error", reject);
|
|
58206
|
-
server.listen(options.port, address, () =>
|
|
58386
|
+
server.listen(options.port, address, () => resolve26());
|
|
58207
58387
|
});
|
|
58208
58388
|
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
58389
|
mkdirSync17(nodeDir(options.home, options.name), { recursive: true, mode: 448 });
|
|
@@ -58214,7 +58394,7 @@ async function startNodeServer(options) {
|
|
|
58214
58394
|
close: async () => {
|
|
58215
58395
|
if (readNodeInfo(options.home, options.name)?.pid === process.pid) rmSync9(nodeInfoPath(options.home, options.name), { force: true });
|
|
58216
58396
|
server.closeAllConnections();
|
|
58217
|
-
await new Promise((
|
|
58397
|
+
await new Promise((resolve26) => server.close(() => resolve26()));
|
|
58218
58398
|
}
|
|
58219
58399
|
};
|
|
58220
58400
|
}
|
|
@@ -59220,7 +59400,7 @@ function hostApiOf(host, plugin, catalog, channels, models) {
|
|
|
59220
59400
|
startMission: (start) => host.startMission(plugin, start),
|
|
59221
59401
|
missions: () => host.missionList(),
|
|
59222
59402
|
joinMission: async (request) => {
|
|
59223
|
-
await host.receive(networkReceive(remoteSessionSender(plugin, request.
|
|
59403
|
+
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
59404
|
return host.joinRemote(request);
|
|
59225
59405
|
},
|
|
59226
59406
|
replyMission: async (request) => {
|
|
@@ -59235,9 +59415,9 @@ function hostApiOf(host, plugin, catalog, channels, models) {
|
|
|
59235
59415
|
await relayed(request, `resume the mission ${request.missionId}`);
|
|
59236
59416
|
return host.resumeMission(request.by, request.missionId, false);
|
|
59237
59417
|
},
|
|
59238
|
-
|
|
59239
|
-
await host.receive(networkReceive(remoteSessionSender(plugin, { node: question.node, sessionId: question.leadSessionId }), { kind: "session", name: question.
|
|
59240
|
-
return host.
|
|
59418
|
+
askReceiver: async (question) => {
|
|
59419
|
+
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 })));
|
|
59420
|
+
return host.askReceiver(question);
|
|
59241
59421
|
},
|
|
59242
59422
|
sayMission: async (request) => {
|
|
59243
59423
|
await relayed(request, request.text);
|
|
@@ -59451,8 +59631,8 @@ var ChannelHub = class {
|
|
|
59451
59631
|
if (state && isTerminal(state)) return;
|
|
59452
59632
|
}
|
|
59453
59633
|
if (signal?.aborted) return;
|
|
59454
|
-
await new Promise((
|
|
59455
|
-
wake =
|
|
59634
|
+
await new Promise((resolve26) => {
|
|
59635
|
+
wake = resolve26;
|
|
59456
59636
|
});
|
|
59457
59637
|
wake = null;
|
|
59458
59638
|
}
|
|
@@ -59626,8 +59806,8 @@ Answer yes or no in this thread.`)], metadata: { [ORBITAL_META.channel]: binding
|
|
|
59626
59806
|
thread: null
|
|
59627
59807
|
});
|
|
59628
59808
|
const threadKey = `${binding.channel}|${binding.id}|${sent.thread ?? sent.ref}`;
|
|
59629
|
-
const answer2 = await new Promise((
|
|
59630
|
-
this.asks.set(threadKey, { resolve:
|
|
59809
|
+
const answer2 = await new Promise((resolve26, reject) => {
|
|
59810
|
+
this.asks.set(threadKey, { resolve: resolve26, question, channel: binding.channel, since: new Date(this.options.context.now()).toISOString() });
|
|
59631
59811
|
signal?.addEventListener("abort", () => {
|
|
59632
59812
|
this.asks.delete(threadKey);
|
|
59633
59813
|
reject(new Error("The question was withdrawn."));
|
|
@@ -60301,8 +60481,9 @@ var panel = {
|
|
|
60301
60481
|
var panelCommands = [panel];
|
|
60302
60482
|
|
|
60303
60483
|
// src/core/cli/plugin.ts
|
|
60304
|
-
|
|
60305
|
-
import {
|
|
60484
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
60485
|
+
import { readFileSync as readFileSync29 } from "node:fs";
|
|
60486
|
+
import { basename as basename9, resolve as resolve15 } from "node:path";
|
|
60306
60487
|
|
|
60307
60488
|
// src/sdk/conformance.ts
|
|
60308
60489
|
import { existsSync as existsSync29, mkdirSync as mkdirSync21, mkdtempSync as mkdtempSync2, readFileSync as readFileSync27, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "node:fs";
|
|
@@ -60673,13 +60854,140 @@ async function runConformance(ports, fixtures = {}) {
|
|
|
60673
60854
|
return results;
|
|
60674
60855
|
}
|
|
60675
60856
|
|
|
60857
|
+
// src/core/cli/plugin-commands.ts
|
|
60858
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28 } from "node:fs";
|
|
60859
|
+
import { join as join37, resolve as resolve14 } from "node:path";
|
|
60860
|
+
var INTROSPECTION_WORDS = ["describe", "schema", "help"];
|
|
60861
|
+
function commandsOf(dir) {
|
|
60862
|
+
const descriptor = join37(dir, PLUGIN_PACKAGE_FILE);
|
|
60863
|
+
if (!existsSync30(descriptor)) return [];
|
|
60864
|
+
try {
|
|
60865
|
+
return PluginPackageSchema.parse(JSON.parse(readFileSync28(descriptor, "utf8"))).commands;
|
|
60866
|
+
} catch {
|
|
60867
|
+
return [];
|
|
60868
|
+
}
|
|
60869
|
+
}
|
|
60870
|
+
function declaredPluginCommands(workspaceDir, env) {
|
|
60871
|
+
try {
|
|
60872
|
+
const source = definitionSource(workspaceDir, env);
|
|
60873
|
+
const config2 = source ? workspaceConfig(workspaceDir, env) : null;
|
|
60874
|
+
if (!source || !config2) return [];
|
|
60875
|
+
return Object.entries(config2.plugins).filter(([, entry]) => entry.enabled && entry.source).flatMap(([plugin, entry]) => {
|
|
60876
|
+
const from = pluginSource(plugin, entry);
|
|
60877
|
+
const commands = from.startsWith("exec:") ? [] : commandsOf(pluginDir(plugin, from, { harnessRoot: source.root, home: orbitalHome(env) }));
|
|
60878
|
+
return commands.length > 0 ? [{ plugin, commands }] : [];
|
|
60879
|
+
});
|
|
60880
|
+
} catch {
|
|
60881
|
+
return [];
|
|
60882
|
+
}
|
|
60883
|
+
}
|
|
60884
|
+
function workspaceOf(input2, fallback = ".") {
|
|
60885
|
+
return resolve14(typeof input2.flags.workspace === "string" ? input2.flags.workspace : fallback);
|
|
60886
|
+
}
|
|
60887
|
+
async function startWorkspacePlugin(context, workspaceDir, name, terminal) {
|
|
60888
|
+
const root = definitionSource(workspaceDir, context.env)?.root ?? null;
|
|
60889
|
+
const config2 = workspaceConfig(workspaceDir, context.env);
|
|
60890
|
+
const entry = config2?.plugins[name];
|
|
60891
|
+
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>` });
|
|
60892
|
+
const secrets = new PluginSecrets(context.env);
|
|
60893
|
+
return startRemotePlugin({
|
|
60894
|
+
name,
|
|
60895
|
+
source: pluginSource(name, entry),
|
|
60896
|
+
config: entry.config,
|
|
60897
|
+
entryEnv: entry.env,
|
|
60898
|
+
node: { name: `plugin-${name}`, workspaceDir, harness: defaultHarness(context.env), harnesses: [...RUNNER_HARNESSES], once: true, idleExitMs: null, project: null },
|
|
60899
|
+
roots: { harnessRoot: root, home: orbitalHome(context.env) },
|
|
60900
|
+
offline: config2.offline,
|
|
60901
|
+
policy: new PolicyChain([]),
|
|
60902
|
+
env: context.env,
|
|
60903
|
+
log: (level, message) => level === "info" ? context.output.step(message) : context.output.warn(message),
|
|
60904
|
+
secret: async (secret) => secrets.get(name, secret),
|
|
60905
|
+
restarts: 0,
|
|
60906
|
+
...terminal && { terminal }
|
|
60907
|
+
});
|
|
60908
|
+
}
|
|
60909
|
+
function flagOf(option) {
|
|
60910
|
+
return {
|
|
60911
|
+
name: option.name,
|
|
60912
|
+
type: option.type,
|
|
60913
|
+
summary: option.summary,
|
|
60914
|
+
...option.required && { required: true },
|
|
60915
|
+
...option.default !== void 0 && { default: option.default },
|
|
60916
|
+
...option.values && { enumValues: option.values }
|
|
60917
|
+
};
|
|
60918
|
+
}
|
|
60919
|
+
function requestOf(command, input2, terminal) {
|
|
60920
|
+
const args = Object.fromEntries(command.args.flatMap((arg) => {
|
|
60921
|
+
const value2 = input2.positionals[arg.name];
|
|
60922
|
+
return value2 === void 0 ? [] : [[arg.name, value2]];
|
|
60923
|
+
}));
|
|
60924
|
+
const options = Object.fromEntries(command.options.flatMap((option) => {
|
|
60925
|
+
const value2 = input2.flags[option.name];
|
|
60926
|
+
if (option.type === "boolean") return [[option.name, value2 === true]];
|
|
60927
|
+
return typeof value2 === "string" || typeof value2 === "number" ? [[option.name, value2]] : [];
|
|
60928
|
+
}));
|
|
60929
|
+
return { name: command.name, args, options, terminal };
|
|
60930
|
+
}
|
|
60931
|
+
async function runOnNode(context, workspaceDir, input2, plugin, request) {
|
|
60932
|
+
const configured = workspaceConfig(workspaceDir, context.env)?.node ?? null;
|
|
60933
|
+
const name = chooseNode({ ...typeof input2.flags.node === "string" && { flag: input2.flags.node }, env: context.env, configured, workspaceDir }).name;
|
|
60934
|
+
const node2 = await liveNode(orbitalHome(context.env), name, context.fetch);
|
|
60935
|
+
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` });
|
|
60936
|
+
const response = await context.fetch(`${node2.url}/orbital/plugins/${encodeURIComponent(plugin)}/commands/${encodeURIComponent(request.name)}`, {
|
|
60937
|
+
method: "POST",
|
|
60938
|
+
headers: { authorization: `Bearer ${node2.token}`, "content-type": "application/json" },
|
|
60939
|
+
body: JSON.stringify(request)
|
|
60940
|
+
});
|
|
60941
|
+
const body2 = await response.json();
|
|
60942
|
+
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 });
|
|
60943
|
+
return PluginCommandResultSchema.parse(body2);
|
|
60944
|
+
}
|
|
60945
|
+
async function runInPlugin(context, workspaceDir, input2, plugin, command, terminal) {
|
|
60946
|
+
if (command.interactive && context.nonInteractive) {
|
|
60947
|
+
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." });
|
|
60948
|
+
}
|
|
60949
|
+
const given = command.interactive ? { input: terminal.input, output: context.outputMode === "text" ? terminal.output : terminal.error, error: terminal.error } : void 0;
|
|
60950
|
+
const started = await startWorkspacePlugin(context, workspaceDir, plugin, given);
|
|
60951
|
+
try {
|
|
60952
|
+
return await started.command(requestOf(command, input2, given ? { tty: context.isTty } : null));
|
|
60953
|
+
} finally {
|
|
60954
|
+
await started.close?.();
|
|
60955
|
+
}
|
|
60956
|
+
}
|
|
60957
|
+
function specOf(workspaceDir, plugin, command, terminal) {
|
|
60958
|
+
return {
|
|
60959
|
+
path: [plugin, command.name],
|
|
60960
|
+
summary: command.summary,
|
|
60961
|
+
group: CommandGroup.Packs,
|
|
60962
|
+
credential: "none",
|
|
60963
|
+
flags: [
|
|
60964
|
+
{ name: "workspace", type: "path", summary: `The project whose ${ORBITAL_FILE} names the plugin. Default: the current directory.` },
|
|
60965
|
+
...command.node ? [{ name: "node", type: "string", identifier: true, summary: "The node that runs the plugin. Default: the node of this workspace." }] : [],
|
|
60966
|
+
...command.options.map(flagOf)
|
|
60967
|
+
],
|
|
60968
|
+
positionals: command.args.map((arg) => ({ name: arg.name, type: "string", summary: arg.summary, ...arg.required && { required: true }, ...arg.variadic && { variadic: true } })),
|
|
60969
|
+
outputShape: { kind: "object", ref: "PluginCommandResult", fields: [{ name: "data", type: "object", summary: `What the command of the plugin ${plugin} answered.` }] },
|
|
60970
|
+
exitCodes: [0, 1, 2, 3, 5],
|
|
60971
|
+
routes: [],
|
|
60972
|
+
examples: [{ command: `orbital ${plugin} ${command.name}`, why: command.summary }],
|
|
60973
|
+
run: async (input2, context) => {
|
|
60974
|
+
const workspace = workspaceOf(input2, workspaceDir);
|
|
60975
|
+
const result2 = command.node ? await runOnNode(context, workspace, input2, plugin, requestOf(command, input2, null)) : await runInPlugin(context, workspace, input2, plugin, command, terminal);
|
|
60976
|
+
if (result2.text !== null) context.output.line(result2.text);
|
|
60977
|
+
if (result2.text === null && Object.keys(result2.data).length > 0) context.output.line(JSON.stringify(result2.data, null, 2));
|
|
60978
|
+
return { data: result2.data, ...result2.exitCode !== 0 && { exitCode: result2.exitCode } };
|
|
60979
|
+
}
|
|
60980
|
+
};
|
|
60981
|
+
}
|
|
60982
|
+
function pluginCommandSpecs(options) {
|
|
60983
|
+
const terminal = options.terminal ?? { input: 0, output: 1, error: 2 };
|
|
60984
|
+
return declaredPluginCommands(options.workspaceDir, options.env).filter(({ plugin }) => !options.reserved.includes(plugin)).flatMap(({ plugin, commands }) => commands.map((command) => specOf(options.workspaceDir, plugin, command, terminal)));
|
|
60985
|
+
}
|
|
60986
|
+
|
|
60676
60987
|
// src/core/cli/plugin.ts
|
|
60677
60988
|
function workspaceFlag() {
|
|
60678
60989
|
return { name: "workspace", type: "path", summary: `The project whose ${ORBITAL_FILE} names the plugin. Default: the current directory.` };
|
|
60679
60990
|
}
|
|
60680
|
-
function workspaceOf(input2) {
|
|
60681
|
-
return resolve14(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
60682
|
-
}
|
|
60683
60991
|
function sourceOrRefuse(context, input2) {
|
|
60684
60992
|
const workspaceDir = workspaceOf(input2);
|
|
60685
60993
|
const source = definitionSource(workspaceDir, context.env);
|
|
@@ -60703,6 +61011,25 @@ async function manifestOf(context, workspaceDir, name, source, root, config2) {
|
|
|
60703
61011
|
await plugin.close?.();
|
|
60704
61012
|
return plugin.manifest;
|
|
60705
61013
|
}
|
|
61014
|
+
function configOf(input2, known) {
|
|
61015
|
+
const file2 = typeof input2.flags["config-file"] === "string" ? resolve15(input2.flags["config-file"]) : null;
|
|
61016
|
+
const fromFile = file2 ? (0, import_yaml8.parse)(readFileSync29(file2, "utf8")) : {};
|
|
61017
|
+
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." });
|
|
61018
|
+
const pairs = Array.isArray(input2.flags.config) ? input2.flags.config.map(String) : typeof input2.flags.config === "string" ? [input2.flags.config] : [];
|
|
61019
|
+
const fromPairs = Object.fromEntries(pairs.map((pair) => {
|
|
61020
|
+
const at = pair.indexOf("=");
|
|
61021
|
+
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." });
|
|
61022
|
+
return [pair.slice(0, at), (0, import_yaml8.parse)(pair.slice(at + 1)) ?? ""];
|
|
61023
|
+
}));
|
|
61024
|
+
return { ...known, ...fromFile, ...fromPairs };
|
|
61025
|
+
}
|
|
61026
|
+
async function checkedManifest(context, workspaceDir, name, source, root, config2) {
|
|
61027
|
+
try {
|
|
61028
|
+
return await manifestOf(context, workspaceDir, name, source, root, config2);
|
|
61029
|
+
} catch (error62) {
|
|
61030
|
+
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>." });
|
|
61031
|
+
}
|
|
61032
|
+
}
|
|
60706
61033
|
function permissionLines(manifest) {
|
|
60707
61034
|
const { network, secrets, models } = manifest.permissions;
|
|
60708
61035
|
return [
|
|
@@ -60718,7 +61045,9 @@ var add = () => ({
|
|
|
60718
61045
|
flags: [
|
|
60719
61046
|
workspaceFlag(),
|
|
60720
61047
|
{ 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.` }
|
|
61048
|
+
{ 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: "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.` },
|
|
61050
|
+
{ 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
61051
|
],
|
|
60723
61052
|
positionals: [
|
|
60724
61053
|
{ name: "plugin", type: "string", required: true, summary: "<name>@<marketplace>, npm:<package>, git:<url>, or a folder such as ./plugins/mine." }
|
|
@@ -60739,7 +61068,8 @@ var add = () => ({
|
|
|
60739
61068
|
examples: [
|
|
60740
61069
|
{ command: "orbital plugin add slack@community", why: "Install the slack plugin from the community marketplace, and use it in this workspace." },
|
|
60741
61070
|
{ 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." }
|
|
61071
|
+
{ 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 ./plugins/notify --config channel=general --config retries=3", why: "Add a plugin that checks its config when it starts." }
|
|
60743
61073
|
],
|
|
60744
61074
|
run: async (input2, context) => {
|
|
60745
61075
|
const home = orbitalHome(context.env);
|
|
@@ -60751,20 +61081,22 @@ var add = () => ({
|
|
|
60751
61081
|
if (input2.flags["install-only"] === true) return { data: { name: ref.name, source: given, installed, enabled: false, manifest: null } };
|
|
60752
61082
|
if (input2.flags.user === true) {
|
|
60753
61083
|
const source2 = ref.kind === "folder" && !given.includes("@") ? ref.dir : ref.kind === "npm" ? `npm:${ref.pkg}` : ref.kind === "git" ? `git:${ref.url}` : given;
|
|
60754
|
-
const
|
|
61084
|
+
const config3 = configOf(input2, userPlugins(context.env)[ref.name]?.config ?? {});
|
|
61085
|
+
const manifest2 = await checkedManifest(context, workspaceOf(input2), ref.name, source2, null, config3);
|
|
60755
61086
|
context.output.step(`${ref.name} ${manifest2.version} reaches:`);
|
|
60756
61087
|
for (const line of permissionLines(manifest2)) context.output.step(line);
|
|
60757
|
-
setUserPlugin(context.env, ref.name, { source: source2, enabled: true, config:
|
|
61088
|
+
setUserPlugin(context.env, ref.name, { source: source2, enabled: true, config: config3, env: [] });
|
|
60758
61089
|
context.output.step(`\u2713 your user config names ${ref.name}: source ${source2}`);
|
|
60759
61090
|
return { data: { name: ref.name, source: source2, installed, enabled: true, manifest: manifest2 } };
|
|
60760
61091
|
}
|
|
60761
61092
|
const definition = sourceOrRefuse(context, input2);
|
|
60762
61093
|
const root = definition.root;
|
|
60763
61094
|
const source = sourceOf(ref, given, root);
|
|
60764
|
-
const
|
|
61095
|
+
const config2 = configOf(input2, sourceConfig(definition).plugins[ref.name]?.config ?? {});
|
|
61096
|
+
const manifest = await checkedManifest(context, workspaceOf(input2), ref.name, source, root, config2);
|
|
60765
61097
|
context.output.step(`${ref.name} ${manifest.version} reaches:`);
|
|
60766
61098
|
for (const line of permissionLines(manifest)) context.output.step(line);
|
|
60767
|
-
setPluginEntry(definition.configFile, ref.name, { source, enabled: true, config:
|
|
61099
|
+
setPluginEntry(definition.configFile, ref.name, { source, enabled: true, config: config2, env: [] });
|
|
60768
61100
|
context.output.step(`\u2713 ${basename9(definition.configFile)} names ${ref.name}: source ${source}`);
|
|
60769
61101
|
return { data: { name: ref.name, source, installed, enabled: true, manifest } };
|
|
60770
61102
|
}
|
|
@@ -60842,57 +61174,6 @@ var remove = () => ({
|
|
|
60842
61174
|
return { data: { name, unnamed, uninstalled } };
|
|
60843
61175
|
}
|
|
60844
61176
|
});
|
|
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
61177
|
var check2 = () => ({
|
|
60897
61178
|
path: ["plugin", "check"],
|
|
60898
61179
|
summary: "Run the conformance checks of each port a plugin provides, so any orbital host can use it.",
|
|
@@ -60918,8 +61199,8 @@ var check2 = () => ({
|
|
|
60918
61199
|
],
|
|
60919
61200
|
run: async (input2, context) => {
|
|
60920
61201
|
const name = String(input2.positionals.plugin);
|
|
60921
|
-
const fixtures = typeof input2.flags.fixtures === "string" ? JSON.parse(
|
|
60922
|
-
const plugin = await
|
|
61202
|
+
const fixtures = typeof input2.flags.fixtures === "string" ? JSON.parse(readFileSync29(input2.flags.fixtures, "utf8")) : {};
|
|
61203
|
+
const plugin = await startWorkspacePlugin(context, workspaceOf(input2), name);
|
|
60923
61204
|
try {
|
|
60924
61205
|
const results = await runConformance(plugin.ports, fixtures);
|
|
60925
61206
|
const mark = { pass: "\u2713", fail: "\u2717", skip: "\u25CB" };
|
|
@@ -61047,13 +61328,13 @@ var secretRemove = {
|
|
|
61047
61328
|
return { data: { plugin, name } };
|
|
61048
61329
|
}
|
|
61049
61330
|
};
|
|
61050
|
-
var pluginCommands = () => [add(), list3(), remove(),
|
|
61331
|
+
var pluginCommands = () => [add(), list3(), remove(), check2(), marketplaceAdd, marketplaceList, marketplaceUpdate, marketplaceRemove, secretSet, secretRemove];
|
|
61051
61332
|
|
|
61052
61333
|
// src/core/cli/plugin-new.ts
|
|
61053
|
-
import { existsSync as
|
|
61054
|
-
import { dirname as dirname16, join as
|
|
61334
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync22, readdirSync as readdirSync20, writeFileSync as writeFileSync20 } from "node:fs";
|
|
61335
|
+
import { dirname as dirname16, join as join38, relative as relative7, resolve as resolve16 } from "node:path";
|
|
61055
61336
|
var PLUGIN_PORTS = ["channel", "store", "workspace", "harness", "panel", "policy", "telemetry", "actions", "workflows", "command"];
|
|
61056
|
-
var SDK_VERSION = true ? `^${"0.0.
|
|
61337
|
+
var SDK_VERSION = true ? `^${"0.0.4"}` : "*";
|
|
61057
61338
|
var ESBUILD_VERSION = "^0.25.0";
|
|
61058
61339
|
var TYPES_NODE_VERSION = "^20.19.43";
|
|
61059
61340
|
var HARNESS_ID = /^[a-z][a-z0-9-]{0,31}$/;
|
|
@@ -61432,7 +61713,7 @@ export function plugin(context: PluginContext): PluginImplementation {
|
|
|
61432
61713
|
switch (event.kind) {
|
|
61433
61714
|
case 'mission.state': {
|
|
61434
61715
|
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,
|
|
61716
|
+
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
61717
|
graph.missions = [...graph.missions.filter((mission) => mission.missionId !== event.mission), state];
|
|
61437
61718
|
return;
|
|
61438
61719
|
}
|
|
@@ -61573,7 +61854,7 @@ export function plugin(_context: PluginContext): PluginImplementation {
|
|
|
61573
61854
|
case "command":
|
|
61574
61855
|
return {
|
|
61575
61856
|
summary: `An orbital plugin with a command: ${name}.`,
|
|
61576
|
-
plugin: `${header("one command. A person runs it with `orbital
|
|
61857
|
+
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
61858
|
|
|
61578
61859
|
export const NAME = '${name}';
|
|
61579
61860
|
|
|
@@ -61583,17 +61864,19 @@ export function plugin(context: PluginContext): PluginImplementation {
|
|
|
61583
61864
|
version: '0.1.0',
|
|
61584
61865
|
permissions: { network: [], secrets: [], models: [] },
|
|
61585
61866
|
ports: {},
|
|
61586
|
-
commands:
|
|
61587
|
-
|
|
61588
|
-
|
|
61589
|
-
|
|
61590
|
-
|
|
61591
|
-
|
|
61867
|
+
commands: [
|
|
61868
|
+
defineCommand({
|
|
61869
|
+
name: 'hello',
|
|
61870
|
+
summary: 'Greet the name given, or the workspace.',
|
|
61871
|
+
args: [{ name: 'name', summary: 'The name to greet.' }],
|
|
61872
|
+
run: async ({ args }) => \`Hello, \${args.name ?? context.init.node.workspaceDir}.\`,
|
|
61873
|
+
}),
|
|
61874
|
+
],
|
|
61592
61875
|
};
|
|
61593
61876
|
}
|
|
61594
61877
|
`,
|
|
61595
61878
|
fixtures: {},
|
|
61596
|
-
test: ` assert.equal(await implementation.commands?.
|
|
61879
|
+
test: ` assert.equal(await implementation.commands?.[0]?.run({ args: { name: 'Ada' }, options: {}, terminal: null }), 'Hello, Ada.');`
|
|
61597
61880
|
};
|
|
61598
61881
|
}
|
|
61599
61882
|
}
|
|
@@ -61627,10 +61910,11 @@ ${template.test ? `${template.test}
|
|
|
61627
61910
|
` : ""}});
|
|
61628
61911
|
`;
|
|
61629
61912
|
}
|
|
61913
|
+
var COMMAND_DECLARATION = { name: "hello", summary: "Greet the name given, or the workspace.", args: [{ name: "name", summary: "The name to greet." }] };
|
|
61630
61914
|
function pluginFiles(name, port) {
|
|
61631
61915
|
const template = templateOf(name, port);
|
|
61632
61916
|
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)}
|
|
61917
|
+
"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
61918
|
`,
|
|
61635
61919
|
"package.json": `${JSON.stringify({
|
|
61636
61920
|
name,
|
|
@@ -61688,14 +61972,14 @@ var pluginNewCommand = {
|
|
|
61688
61972
|
if (port === "harness" && !HARNESS_ID.test(name)) {
|
|
61689
61973
|
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
61974
|
}
|
|
61691
|
-
const dir =
|
|
61692
|
-
if (
|
|
61975
|
+
const dir = resolve16(typeof input2.flags.dir === "string" ? input2.flags.dir : name);
|
|
61976
|
+
if (existsSync31(dir) && readdirSync20(dir).length > 0) {
|
|
61693
61977
|
throw new CliError({ code: "conflict", message: `${dir} is not empty.`, exitCode: ExitCode.Validation, hint: "Name a new folder with --dir." });
|
|
61694
61978
|
}
|
|
61695
61979
|
const files = pluginFiles(name, port);
|
|
61696
61980
|
for (const [path3, content] of Object.entries(files)) {
|
|
61697
|
-
mkdirSync22(dirname16(
|
|
61698
|
-
writeFileSync20(
|
|
61981
|
+
mkdirSync22(dirname16(join38(dir, path3)), { recursive: true });
|
|
61982
|
+
writeFileSync20(join38(dir, path3), content);
|
|
61699
61983
|
}
|
|
61700
61984
|
const program = commandPrefix(context.env);
|
|
61701
61985
|
const shown = relative7(process.cwd(), dir) || ".";
|
|
@@ -61838,8 +62122,8 @@ If the browser does not return here in ${CALLBACK_WAIT_MS / 6e4} minutes, paste
|
|
|
61838
62122
|
${url2}
|
|
61839
62123
|
Then paste the code, or the full URL the browser ends on.`);
|
|
61840
62124
|
},
|
|
61841
|
-
onManualCodeInput: () => new Promise((
|
|
61842
|
-
timer = setTimeout(() =>
|
|
62125
|
+
onManualCodeInput: () => new Promise((resolve26) => {
|
|
62126
|
+
timer = setTimeout(() => resolve26(""), opened2 ? CALLBACK_WAIT_MS : 0);
|
|
61843
62127
|
timer.unref();
|
|
61844
62128
|
}),
|
|
61845
62129
|
onPrompt: async (prompt) => (await context.ask(`${prompt.message} `))?.trim() ?? "",
|
|
@@ -61932,12 +62216,12 @@ function createProviderCommands(signIn = loginAnthropic) {
|
|
|
61932
62216
|
}
|
|
61933
62217
|
|
|
61934
62218
|
// src/core/cli/run.ts
|
|
61935
|
-
import { resolve as
|
|
62219
|
+
import { resolve as resolve18 } from "node:path";
|
|
61936
62220
|
|
|
61937
62221
|
// src/core/local/local-run.ts
|
|
61938
62222
|
import { randomBytes as randomBytes4, randomUUID as randomUUID8 } from "node:crypto";
|
|
61939
62223
|
import { mkdirSync as mkdirSync23, rmSync as rmSync12 } from "node:fs";
|
|
61940
|
-
import { join as
|
|
62224
|
+
import { join as join39, resolve as resolve17 } from "node:path";
|
|
61941
62225
|
|
|
61942
62226
|
// src/core/local/interrupt.ts
|
|
61943
62227
|
var EXIT_CODE = { SIGINT: 130, SIGTERM: 143 };
|
|
@@ -61960,7 +62244,7 @@ function cleanupOnInterrupt(cleanup, options = {}) {
|
|
|
61960
62244
|
async function runLocal(context, request, plugins) {
|
|
61961
62245
|
const { workspaceDir } = request;
|
|
61962
62246
|
const home = orbitalHome(context.env);
|
|
61963
|
-
const sessionsDir =
|
|
62247
|
+
const sessionsDir = join39(home, "sessions");
|
|
61964
62248
|
mkdirSync23(sessionsDir, { recursive: true, mode: 448 });
|
|
61965
62249
|
const resumed = request.resumeSessionId ? readSessionRecord(sessionsDir, request.resumeSessionId) : null;
|
|
61966
62250
|
if (request.resumeSessionId && !resumed) {
|
|
@@ -61990,9 +62274,9 @@ async function runLocal(context, request, plugins) {
|
|
|
61990
62274
|
let channels = null;
|
|
61991
62275
|
const asks = new PersonDesk(async (question, name) => await channels?.askPerson(`${name} asks: ${question}`, null) ?? null);
|
|
61992
62276
|
const policy = new PolicyChain(ports.policies(), asks.ask);
|
|
61993
|
-
channels = new ChannelHub({ context, ports, policy, self: nodeName, stateDir:
|
|
62277
|
+
channels = new ChannelHub({ context, ports, policy, self: nodeName, stateDir: join39(home, "channels", nodeName), plugins: () => definition.config.plugins });
|
|
61994
62278
|
const models = modelGatewayOf(context, policy, remote);
|
|
61995
|
-
const actions = nodeActions({ ports: () => ports, policy: () => policy, definition: { workspaceDir:
|
|
62279
|
+
const actions = nodeActions({ ports: () => ports, policy: () => policy, definition: { workspaceDir: resolve17(workspaceDir), definition }, span: (span2) => host.telemetry.span(span2), now: context.now });
|
|
61996
62280
|
const agentName = request.agent ?? resumed?.agent ?? null;
|
|
61997
62281
|
const target = await resolveTarget(definition, agentName, ports);
|
|
61998
62282
|
const harness = request.harness ?? resumed?.harness ?? defaultHarness(context.env);
|
|
@@ -62053,7 +62337,7 @@ async function runLocal(context, request, plugins) {
|
|
|
62053
62337
|
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
62338
|
if (result2 !== null) context.output.line(result2);
|
|
62055
62339
|
const cli = commandPrefix(context.env);
|
|
62056
|
-
context.output.step(`\u2713 transcript: ${
|
|
62340
|
+
context.output.step(`\u2713 transcript: ${join39(sessionsDir, sessionId, "conversation.jsonl")}`);
|
|
62057
62341
|
context.output.step(`\u2713 resume with: ${cli} run --resume ${sessionId}`);
|
|
62058
62342
|
return {
|
|
62059
62343
|
sessionId,
|
|
@@ -62061,7 +62345,7 @@ async function runLocal(context, request, plugins) {
|
|
|
62061
62345
|
done: true,
|
|
62062
62346
|
harness,
|
|
62063
62347
|
result: result2,
|
|
62064
|
-
transcript:
|
|
62348
|
+
transcript: join39(sessionsDir, sessionId, "conversation.jsonl"),
|
|
62065
62349
|
interactive: request.interactive
|
|
62066
62350
|
};
|
|
62067
62351
|
} finally {
|
|
@@ -62121,7 +62405,7 @@ async function interactiveTurn(context, request, run2) {
|
|
|
62121
62405
|
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
62406
|
const opened2 = await openSession(run2.ports, run2.policy, info);
|
|
62123
62407
|
const record2 = run2.host.attach(info, opened2.sinks, { agent: info.agent, harness: run2.harness, workspaceDir: request.workspaceDir });
|
|
62124
|
-
const runDir =
|
|
62408
|
+
const runDir = join39(run2.home, "runs", randomUUID8());
|
|
62125
62409
|
mkdirSync23(runDir, { recursive: true, mode: 448 });
|
|
62126
62410
|
let usage = [];
|
|
62127
62411
|
try {
|
|
@@ -62228,7 +62512,7 @@ function createRunCommand(options) {
|
|
|
62228
62512
|
const terminal = log ? await options.terminal() : null;
|
|
62229
62513
|
const data = await runLocal(log ? { ...context, output: log.output(context.output) } : context, {
|
|
62230
62514
|
agent,
|
|
62231
|
-
workspaceDir:
|
|
62515
|
+
workspaceDir: resolve18(typeof input2.flags.workspace === "string" ? input2.flags.workspace : "."),
|
|
62232
62516
|
prompt,
|
|
62233
62517
|
useDefinition: true,
|
|
62234
62518
|
follow: false,
|
|
@@ -62244,24 +62528,24 @@ function createRunCommand(options) {
|
|
|
62244
62528
|
}
|
|
62245
62529
|
|
|
62246
62530
|
// src/core/cli/session.ts
|
|
62247
|
-
import { resolve as
|
|
62531
|
+
import { resolve as resolve19 } from "node:path";
|
|
62248
62532
|
|
|
62249
62533
|
// src/core/local/attachments.ts
|
|
62250
|
-
import { existsSync as
|
|
62251
|
-
import { join as
|
|
62534
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync30, writeFileSync as writeFileSync21 } from "node:fs";
|
|
62535
|
+
import { join as join40 } from "node:path";
|
|
62252
62536
|
function fileOf(home, externalId) {
|
|
62253
|
-
return
|
|
62537
|
+
return join40(home, "attachments", `${encodeURIComponent(externalId)}.json`);
|
|
62254
62538
|
}
|
|
62255
62539
|
function writeAttachment(home, externalId, node2) {
|
|
62256
|
-
mkdirSync24(
|
|
62540
|
+
mkdirSync24(join40(home, "attachments"), { recursive: true, mode: 448 });
|
|
62257
62541
|
writeFileSync21(fileOf(home, externalId), `${JSON.stringify({ node: node2 })}
|
|
62258
62542
|
`, { mode: 384 });
|
|
62259
62543
|
}
|
|
62260
62544
|
function readAttachment(home, externalId) {
|
|
62261
62545
|
const file2 = fileOf(home, externalId);
|
|
62262
|
-
if (!
|
|
62546
|
+
if (!existsSync33(file2)) return null;
|
|
62263
62547
|
try {
|
|
62264
|
-
const parsed = JSON.parse(
|
|
62548
|
+
const parsed = JSON.parse(readFileSync30(file2, "utf8"));
|
|
62265
62549
|
return typeof parsed === "object" && parsed !== null && "node" in parsed && typeof parsed.node === "string" ? parsed.node : null;
|
|
62266
62550
|
} catch {
|
|
62267
62551
|
return null;
|
|
@@ -62329,7 +62613,7 @@ async function joinMission(context, attached, missionId) {
|
|
|
62329
62613
|
const body2 = await response.json();
|
|
62330
62614
|
if (!response.ok) {
|
|
62331
62615
|
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
|
|
62616
|
+
throw new CliError({ code: "invalid-input", message: `The node refused to make this session a decision receiver of ${missionId}: ${error62}`, exitCode: ExitCode.Validation });
|
|
62333
62617
|
}
|
|
62334
62618
|
return MissionRecordSchema.parse(body2);
|
|
62335
62619
|
}
|
|
@@ -62409,7 +62693,7 @@ var sessionAttachCommand = {
|
|
|
62409
62693
|
{ 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
62694
|
{ name: "team", type: "string", summary: "The team the session joins. Sessions of one team are peers." },
|
|
62411
62695
|
{ 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
|
|
62696
|
+
{ 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
62697
|
],
|
|
62414
62698
|
positionals: [],
|
|
62415
62699
|
outputShape: {
|
|
@@ -62426,7 +62710,7 @@ var sessionAttachCommand = {
|
|
|
62426
62710
|
examples: [
|
|
62427
62711
|
{ command: "orbital session attach --harness claude-code --output ndjson", why: "Join the node of this folder, and print each delivery as a record." },
|
|
62428
62712
|
{ 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
|
|
62713
|
+
{ 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
62714
|
],
|
|
62431
62715
|
run: async (input2, context) => {
|
|
62432
62716
|
const harness = typeof input2.flags.harness === "string" ? input2.flags.harness : "external";
|
|
@@ -62438,7 +62722,7 @@ var sessionAttachCommand = {
|
|
|
62438
62722
|
const found = await keepAttached(context, {
|
|
62439
62723
|
harness,
|
|
62440
62724
|
agent: typeof input2.flags.agent === "string" ? input2.flags.agent : harness,
|
|
62441
|
-
workspaceDir:
|
|
62725
|
+
workspaceDir: resolve19(typeof input2.flags.workspace === "string" ? input2.flags.workspace : "."),
|
|
62442
62726
|
...typeof input2.flags.node === "string" && { node: input2.flags.node },
|
|
62443
62727
|
team: typeof input2.flags.team === "string" ? input2.flags.team : null,
|
|
62444
62728
|
externalId: typeof input2.flags["external-id"] === "string" ? input2.flags["external-id"] : null,
|
|
@@ -62451,6 +62735,8 @@ var sessionAttachCommand = {
|
|
|
62451
62735
|
delivery: (event) => {
|
|
62452
62736
|
const delivery = DeliverySchema.safeParse(event);
|
|
62453
62737
|
if (delivery.success) context.output.record({ type: StreamRecordType.Event, ...delivery.data });
|
|
62738
|
+
const answered = AnsweredSchema.safeParse(event);
|
|
62739
|
+
if (answered.success) context.output.record({ type: StreamRecordType.Event, ...answered.data });
|
|
62454
62740
|
},
|
|
62455
62741
|
detached: (reason) => {
|
|
62456
62742
|
context.output.record({ type: StreamRecordType.Event, kind: "detached", reason });
|
|
@@ -62538,8 +62824,8 @@ var remove3 = {
|
|
|
62538
62824
|
var workspaceCommands = [list5, show, remove3];
|
|
62539
62825
|
|
|
62540
62826
|
// src/core/cli/up.ts
|
|
62541
|
-
import { existsSync as
|
|
62542
|
-
import { resolve as
|
|
62827
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
62828
|
+
import { resolve as resolve20 } from "node:path";
|
|
62543
62829
|
|
|
62544
62830
|
// src/core/cli/node-control.ts
|
|
62545
62831
|
var STOP_WAIT_MS = 3e4;
|
|
@@ -62613,7 +62899,7 @@ function groupOf(state) {
|
|
|
62613
62899
|
}
|
|
62614
62900
|
}
|
|
62615
62901
|
function missionGroup(mission) {
|
|
62616
|
-
return
|
|
62902
|
+
return waitsOnDecision(mission) ? "waiting" : groupOf(mission.state);
|
|
62617
62903
|
}
|
|
62618
62904
|
var SESSIONS_PER_MEMBER = 3;
|
|
62619
62905
|
var RUNNING_SHOWN = 5;
|
|
@@ -63092,7 +63378,7 @@ async function liveView(context, nodes, signal, terminal, home) {
|
|
|
63092
63378
|
for (const line of events(viewed, states.get(one.node), now)) context.output.line(line);
|
|
63093
63379
|
states.set(one.node, now);
|
|
63094
63380
|
}
|
|
63095
|
-
await Promise.race([context.sleep(REFRESH_MS), new Promise((
|
|
63381
|
+
await Promise.race([context.sleep(REFRESH_MS), new Promise((resolve26) => signal.addEventListener("abort", () => resolve26(), { once: true }))]);
|
|
63096
63382
|
}
|
|
63097
63383
|
context.output.line(closing);
|
|
63098
63384
|
}
|
|
@@ -63101,7 +63387,7 @@ async function liveView(context, nodes, signal, terminal, home) {
|
|
|
63101
63387
|
var WORKSPACE_FLAG = { name: "workspace", type: "path", summary: "The project. Default: the current directory." };
|
|
63102
63388
|
var TEAMS = { name: "teams", type: "string", variadic: true, summary: "Only the nodes of these teams. Default: every node of the project." };
|
|
63103
63389
|
async function planOf(context, input2) {
|
|
63104
|
-
const workspaceDir =
|
|
63390
|
+
const workspaceDir = resolve20(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
63105
63391
|
const { definition, refusals } = await loadDefinitionPartial(workspaceDir, context.env);
|
|
63106
63392
|
for (const refusal of refusals) context.output.warn(refusal.message);
|
|
63107
63393
|
const project = projectNodeOf(definition, workspaceDir);
|
|
@@ -63248,7 +63534,7 @@ var statusCommand = {
|
|
|
63248
63534
|
for (const { charters: _charters, named: _named, ...planned } of nodes) {
|
|
63249
63535
|
const log = nodeLogPath(home, planned.node);
|
|
63250
63536
|
const supervisor = readSupervisor(home, planned.node);
|
|
63251
|
-
if (
|
|
63537
|
+
if (existsSync34(log)) logs.push({ node: planned.node, log, lastCrash: supervisor?.crashes.at(-1) ?? null, lastExit: supervisor?.lastExit ?? null, gaveUp: supervisor?.gaveUp ?? null });
|
|
63252
63538
|
const running = await liveNode(home, planned.node, context.fetch);
|
|
63253
63539
|
const served = running ? await servedTeams(context, running) : null;
|
|
63254
63540
|
for (const note of served ?? []) if (note.problems.length > 0 || note.retiring) notes.push({ node: planned.node, ...note });
|
|
@@ -63281,7 +63567,7 @@ var downCommand = {
|
|
|
63281
63567
|
const { workspaceDir, nodes } = await planOf(context, input2);
|
|
63282
63568
|
const home = orbitalHome(context.env);
|
|
63283
63569
|
const rows = [];
|
|
63284
|
-
const separate2 = (await liveNodes(home, context.fetch)).filter((info) =>
|
|
63570
|
+
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
63571
|
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
63572
|
for (const planned of targets) {
|
|
63287
63573
|
const running = await liveNode(home, planned.node, context.fetch);
|
|
@@ -63340,7 +63626,7 @@ var createJoinCommand = (options) => ({
|
|
|
63340
63626
|
}
|
|
63341
63627
|
let charters = [];
|
|
63342
63628
|
try {
|
|
63343
|
-
const workspaceDir =
|
|
63629
|
+
const workspaceDir = resolve20(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
63344
63630
|
charters = (await loadDefinition(workspaceDir, context.env)).teams;
|
|
63345
63631
|
} catch {
|
|
63346
63632
|
charters = [];
|
|
@@ -63365,9 +63651,9 @@ function createUpCommands(options) {
|
|
|
63365
63651
|
// src/core/cli/service.ts
|
|
63366
63652
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
63367
63653
|
import { createHash as createHash10 } from "node:crypto";
|
|
63368
|
-
import { existsSync as
|
|
63654
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync25, rmSync as rmSync13, writeFileSync as writeFileSync22 } from "node:fs";
|
|
63369
63655
|
import { homedir as homedir8 } from "node:os";
|
|
63370
|
-
import { join as
|
|
63656
|
+
import { join as join41, resolve as resolve21 } from "node:path";
|
|
63371
63657
|
var exec = (command, args) => {
|
|
63372
63658
|
const result2 = spawnSync5(command, args, { encoding: "utf8" });
|
|
63373
63659
|
return { status: result2.status, output: `${result2.stdout ?? ""}${result2.stderr ?? ""}${result2.error?.message ?? ""}` };
|
|
@@ -63385,7 +63671,7 @@ function serviceSpec(env, workspaceDir) {
|
|
|
63385
63671
|
workspaceDir,
|
|
63386
63672
|
program: [process.execPath, installedProgram(), ...mountPath(), "up", "--detach", "--workspace", workspaceDir],
|
|
63387
63673
|
env: Object.fromEntries(KEPT_ENV.flatMap((name) => env[name] ? [[name, env[name]]] : [])),
|
|
63388
|
-
log:
|
|
63674
|
+
log: join41(orbitalHome(env), "logs", `service-${hash2}.log`)
|
|
63389
63675
|
};
|
|
63390
63676
|
}
|
|
63391
63677
|
function xml(text3) {
|
|
@@ -63448,7 +63734,7 @@ function userHome(env) {
|
|
|
63448
63734
|
return env.HOME ?? homedir8();
|
|
63449
63735
|
}
|
|
63450
63736
|
function fileOf2(platform, env, label) {
|
|
63451
|
-
return platform === "launchd" ?
|
|
63737
|
+
return platform === "launchd" ? join41(userHome(env), "Library", "LaunchAgents", `${label}.plist`) : join41(env.XDG_CONFIG_HOME ?? join41(userHome(env), ".config"), "systemd", "user", `${label}.service`);
|
|
63452
63738
|
}
|
|
63453
63739
|
function domain2() {
|
|
63454
63740
|
return `gui/${process.getuid?.() ?? 0}`;
|
|
@@ -63461,13 +63747,13 @@ function serviceState(platform, env, workspaceDir, run2 = exec) {
|
|
|
63461
63747
|
const { label } = serviceSpec(env, workspaceDir);
|
|
63462
63748
|
const file2 = fileOf2(platform, env, label);
|
|
63463
63749
|
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:
|
|
63750
|
+
return { platform, label, file: file2, installed: existsSync35(file2), loaded };
|
|
63465
63751
|
}
|
|
63466
63752
|
function installService(platform, env, workspaceDir, run2 = exec) {
|
|
63467
63753
|
const spec = serviceSpec(env, workspaceDir);
|
|
63468
63754
|
const file2 = fileOf2(platform, env, spec.label);
|
|
63469
|
-
mkdirSync25(
|
|
63470
|
-
mkdirSync25(
|
|
63755
|
+
mkdirSync25(join41(file2, ".."), { recursive: true });
|
|
63756
|
+
mkdirSync25(join41(spec.log, ".."), { recursive: true, mode: 448 });
|
|
63471
63757
|
if (platform === "launchd") {
|
|
63472
63758
|
writeFileSync22(file2, launchdPlist(spec));
|
|
63473
63759
|
run2("launchctl", ["bootout", `${domain2()}/${spec.label}`]);
|
|
@@ -63489,7 +63775,7 @@ function uninstallService(platform, env, workspaceDir, run2 = exec) {
|
|
|
63489
63775
|
return serviceState(platform, env, workspaceDir, run2);
|
|
63490
63776
|
}
|
|
63491
63777
|
function workspaceOf2(input2) {
|
|
63492
|
-
return
|
|
63778
|
+
return resolve21(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
63493
63779
|
}
|
|
63494
63780
|
function platformOrRefuse() {
|
|
63495
63781
|
const platform = platformOf();
|
|
@@ -63564,9 +63850,9 @@ var serviceCommands = [
|
|
|
63564
63850
|
];
|
|
63565
63851
|
|
|
63566
63852
|
// src/core/cli/browser.ts
|
|
63567
|
-
import { existsSync as
|
|
63853
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync31, writeFileSync as writeFileSync23 } from "node:fs";
|
|
63568
63854
|
import { homedir as homedir9 } from "node:os";
|
|
63569
|
-
import { dirname as dirname17, join as
|
|
63855
|
+
import { dirname as dirname17, join as join42 } from "node:path";
|
|
63570
63856
|
|
|
63571
63857
|
// ../../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
63858
|
function normalizeHeaders(headers) {
|
|
@@ -65179,7 +65465,7 @@ function isChromeDevtools(entry) {
|
|
|
65179
65465
|
return isChromeDevtoolsMcp(command, args);
|
|
65180
65466
|
}
|
|
65181
65467
|
function claudeJsonPath(env) {
|
|
65182
|
-
return
|
|
65468
|
+
return join42(env.CLAUDE_CONFIG_DIR ?? env.HOME ?? homedir9(), ".claude.json");
|
|
65183
65469
|
}
|
|
65184
65470
|
function chromeServersOf(config2) {
|
|
65185
65471
|
return Object.entries(config2.mcpServers ?? {}).filter(([, entry]) => isChromeDevtools(entry)).map(([name]) => name);
|
|
@@ -65191,21 +65477,21 @@ function sharedBrowserEntry() {
|
|
|
65191
65477
|
return { type: "stdio", ...sharedBrowserCommand(), env: {} };
|
|
65192
65478
|
}
|
|
65193
65479
|
function backupPath(env) {
|
|
65194
|
-
return
|
|
65480
|
+
return join42(browserDir(orbitalHome(env)), "claude-mcp-backup.json");
|
|
65195
65481
|
}
|
|
65196
65482
|
function codexBackupPath(env) {
|
|
65197
|
-
return
|
|
65483
|
+
return join42(browserDir(orbitalHome(env)), "codex-config-backup.json");
|
|
65198
65484
|
}
|
|
65199
65485
|
function codexConfigPath(env) {
|
|
65200
|
-
return
|
|
65486
|
+
return join42(env.CODEX_HOME ?? join42(env.HOME ?? homedir9(), ".codex"), "config.toml");
|
|
65201
65487
|
}
|
|
65202
65488
|
function pointClaudeAtSharedBrowser(env, entry = sharedBrowserEntry()) {
|
|
65203
65489
|
const file2 = claudeJsonPath(env);
|
|
65204
|
-
const config2 =
|
|
65490
|
+
const config2 = existsSync36(file2) ? JSON.parse(readFileSync31(file2, "utf8")) : {};
|
|
65205
65491
|
const found = chromeServersOf(config2);
|
|
65206
65492
|
const names = found.length > 0 ? found : [SHARED_BROWSER_SERVER];
|
|
65207
65493
|
const backup = backupPath(env);
|
|
65208
|
-
const previous =
|
|
65494
|
+
const previous = existsSync36(backup) ? JSON.parse(readFileSync31(backup, "utf8")) : {};
|
|
65209
65495
|
const saved = { ...Object.fromEntries(names.filter((name) => config2.mcpServers?.[name] !== void 0).map((name) => [name, config2.mcpServers[name]])), ...previous };
|
|
65210
65496
|
mkdirSync26(browserDir(orbitalHome(env)), { recursive: true, mode: 448 });
|
|
65211
65497
|
writeFileSync23(backup, `${JSON.stringify(saved, null, 2)}
|
|
@@ -65218,9 +65504,9 @@ function pointClaudeAtSharedBrowser(env, entry = sharedBrowserEntry()) {
|
|
|
65218
65504
|
function revertClaudeBrowser(env) {
|
|
65219
65505
|
const file2 = claudeJsonPath(env);
|
|
65220
65506
|
const backup = backupPath(env);
|
|
65221
|
-
if (!
|
|
65222
|
-
const saved = JSON.parse(
|
|
65223
|
-
const config2 =
|
|
65507
|
+
if (!existsSync36(backup)) return null;
|
|
65508
|
+
const saved = JSON.parse(readFileSync31(backup, "utf8"));
|
|
65509
|
+
const config2 = existsSync36(file2) ? JSON.parse(readFileSync31(file2, "utf8")) : {};
|
|
65224
65510
|
const shared = Object.entries(config2.mcpServers ?? {}).filter(([, entry]) => Array.isArray(entry.args) && entry.args.join(" ").endsWith("browser connect")).map(([name]) => name);
|
|
65225
65511
|
const servers = { ...config2.mcpServers };
|
|
65226
65512
|
for (const name of shared) delete servers[name];
|
|
@@ -65231,18 +65517,18 @@ function revertClaudeBrowser(env) {
|
|
|
65231
65517
|
}
|
|
65232
65518
|
function pointCodexAtSharedBrowser(env, shared = sharedBrowserCommand()) {
|
|
65233
65519
|
const file2 = codexConfigPath(env);
|
|
65234
|
-
if (!
|
|
65235
|
-
const text3 =
|
|
65520
|
+
if (!existsSync36(file2) && !existsSync36(dirname17(file2))) return null;
|
|
65521
|
+
const text3 = existsSync36(file2) ? readFileSync31(file2, "utf8") : "";
|
|
65236
65522
|
const tables = mcpServerTables(text3);
|
|
65237
65523
|
const found = tables.filter((table2) => isChromeDevtoolsMcp(table2.command, table2.args)).map((table2) => table2.name);
|
|
65238
65524
|
const connected = tables.filter((table2) => isSharedBrowserConnect(table2.args)).map((table2) => table2.name);
|
|
65239
65525
|
const names = found.length > 0 ? found : connected.length > 0 ? [] : [SHARED_BROWSER_SERVER];
|
|
65240
65526
|
const backup = codexBackupPath(env);
|
|
65241
|
-
if (names.length === 0) return { file: file2, servers: connected, backup:
|
|
65527
|
+
if (names.length === 0) return { file: file2, servers: connected, backup: existsSync36(backup) ? backup : null };
|
|
65242
65528
|
const block = (name) => stdioServerBlock(name, shared.command, shared.args);
|
|
65243
65529
|
const replaced = replaceMcpServers(text3, (table2) => names.includes(table2.name) ? block(table2.name) : null);
|
|
65244
65530
|
const next = names.filter((name) => !replaced.replaced.some((one) => one.name === name)).reduce((current2, name) => appendMcpServer(current2, block(name)), replaced.text);
|
|
65245
|
-
const previous =
|
|
65531
|
+
const previous = existsSync36(backup) ? JSON.parse(readFileSync31(backup, "utf8")) : {};
|
|
65246
65532
|
const saved = { ...Object.fromEntries(replaced.replaced.map((one) => [one.name, one.original])), ...previous };
|
|
65247
65533
|
mkdirSync26(browserDir(orbitalHome(env)), { recursive: true, mode: 448 });
|
|
65248
65534
|
writeFileSync23(backup, `${JSON.stringify(saved, null, 2)}
|
|
@@ -65254,9 +65540,9 @@ function pointCodexAtSharedBrowser(env, shared = sharedBrowserCommand()) {
|
|
|
65254
65540
|
function revertCodexBrowser(env) {
|
|
65255
65541
|
const file2 = codexConfigPath(env);
|
|
65256
65542
|
const backup = codexBackupPath(env);
|
|
65257
|
-
if (!
|
|
65258
|
-
const saved = JSON.parse(
|
|
65259
|
-
const text3 =
|
|
65543
|
+
if (!existsSync36(backup)) return null;
|
|
65544
|
+
const saved = JSON.parse(readFileSync31(backup, "utf8"));
|
|
65545
|
+
const text3 = existsSync36(file2) ? readFileSync31(file2, "utf8") : "";
|
|
65260
65546
|
const replaced = replaceMcpServers(text3, (table2) => isSharedBrowserConnect(table2.args) ? saved[table2.name] ?? "" : null);
|
|
65261
65547
|
const next = Object.entries(saved).filter(([name]) => !replaced.replaced.some((one) => one.name === name)).reduce((current2, [, original]) => appendMcpServer(current2, original), replaced.text);
|
|
65262
65548
|
mkdirSync26(dirname17(file2), { recursive: true });
|
|
@@ -65441,12 +65727,12 @@ var browserCommands = [
|
|
|
65441
65727
|
];
|
|
65442
65728
|
|
|
65443
65729
|
// src/core/cli/mission.ts
|
|
65444
|
-
import { existsSync as
|
|
65445
|
-
import { join as
|
|
65730
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
65731
|
+
import { join as join44, relative as relative8, resolve as resolve22 } from "node:path";
|
|
65446
65732
|
|
|
65447
65733
|
// src/core/local/mission-migrate.ts
|
|
65448
|
-
import { existsSync as
|
|
65449
|
-
import { join as
|
|
65734
|
+
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";
|
|
65735
|
+
import { join as join43 } from "node:path";
|
|
65450
65736
|
function withTeam(content, team) {
|
|
65451
65737
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(\r?\n[\s\S]*)?$/);
|
|
65452
65738
|
if (!match) return content;
|
|
@@ -65458,26 +65744,26 @@ ${head.join("\n")}
|
|
|
65458
65744
|
---${match[2] ?? "\n"}`;
|
|
65459
65745
|
}
|
|
65460
65746
|
function emptyDir(dir) {
|
|
65461
|
-
if (
|
|
65747
|
+
if (existsSync37(dir) && readdirSync21(dir).length === 0) rmdirSync2(dir);
|
|
65462
65748
|
}
|
|
65463
65749
|
function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
65464
65750
|
const moved = [];
|
|
65465
65751
|
const refused3 = [];
|
|
65466
|
-
const teamsDir =
|
|
65467
|
-
const target =
|
|
65752
|
+
const teamsDir = join43(root, TEAMS_DIR);
|
|
65753
|
+
const target = join43(root, MISSIONS_DIR);
|
|
65468
65754
|
const seen = /* @__PURE__ */ new Map();
|
|
65469
|
-
for (const team of
|
|
65470
|
-
const missionsDir =
|
|
65471
|
-
if (!
|
|
65755
|
+
for (const team of existsSync37(teamsDir) ? readdirSync21(teamsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()) : []) {
|
|
65756
|
+
const missionsDir = join43(teamsDir, team.name, MISSIONS_DIR);
|
|
65757
|
+
if (!existsSync37(missionsDir)) continue;
|
|
65472
65758
|
for (const mission of readdirSync21(missionsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory())) {
|
|
65473
|
-
const from =
|
|
65474
|
-
const to =
|
|
65759
|
+
const from = join43(missionsDir, mission.name);
|
|
65760
|
+
const to = join43(target, mission.name);
|
|
65475
65761
|
const other = seen.get(mission.name);
|
|
65476
65762
|
if (other) {
|
|
65477
65763
|
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
65764
|
continue;
|
|
65479
65765
|
}
|
|
65480
|
-
if (
|
|
65766
|
+
if (existsSync37(to)) {
|
|
65481
65767
|
refused3.push(`${from}: ${to} exists already.`);
|
|
65482
65768
|
continue;
|
|
65483
65769
|
}
|
|
@@ -65486,22 +65772,22 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
65486
65772
|
if (dryRun) continue;
|
|
65487
65773
|
mkdirSync27(target, { recursive: true });
|
|
65488
65774
|
renameSync8(from, to);
|
|
65489
|
-
const file2 =
|
|
65490
|
-
if (
|
|
65775
|
+
const file2 = join43(to, MISSION_FILE);
|
|
65776
|
+
if (existsSync37(file2)) writeFileSync24(file2, withTeam(readFileSync32(file2, "utf8"), team.name));
|
|
65491
65777
|
}
|
|
65492
65778
|
if (!dryRun) emptyDir(missionsDir);
|
|
65493
65779
|
}
|
|
65494
65780
|
for (const node2 of nodes) {
|
|
65495
|
-
const nodeDir2 =
|
|
65496
|
-
if (!
|
|
65781
|
+
const nodeDir2 = join43(nodesDir, node2);
|
|
65782
|
+
if (!existsSync37(nodeDir2)) continue;
|
|
65497
65783
|
const files = [];
|
|
65498
65784
|
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:
|
|
65785
|
+
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 });
|
|
65786
|
+
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
65787
|
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:
|
|
65788
|
+
for (const name of readdirSync21(join43(nodeDir2, entry.name))) {
|
|
65789
|
+
if (name.endsWith(".json")) files.push({ from: join43(nodeDir2, entry.name, name), mission: name.slice(0, -".json".length), team: entry.name, backup: false });
|
|
65790
|
+
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
65791
|
}
|
|
65506
65792
|
}
|
|
65507
65793
|
files.sort((a, b) => Number(a.backup) - Number(b.backup) || Number(a.team !== null) - Number(b.team !== null));
|
|
@@ -65513,13 +65799,13 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
65513
65799
|
if (!dryRun) rmSync14(file2.from);
|
|
65514
65800
|
continue;
|
|
65515
65801
|
}
|
|
65516
|
-
if (taken.has(file2.mission) ||
|
|
65802
|
+
if (taken.has(file2.mission) || existsSync37(to)) {
|
|
65517
65803
|
refused3.push(`${file2.from}: ${to} exists already.`);
|
|
65518
65804
|
continue;
|
|
65519
65805
|
}
|
|
65520
|
-
const raw = JSON.parse(
|
|
65806
|
+
const raw = JSON.parse(readFileSync32(file2.from, "utf8"));
|
|
65521
65807
|
const { file: _file2, ...rest } = raw;
|
|
65522
|
-
const state = MissionStateFileSchema.safeParse({ question: null,
|
|
65808
|
+
const state = MissionStateFileSchema.safeParse({ question: null, lead: null, claim: null, claimedAt: null, ...rest, mission: file2.mission, node: node2, team: rest.team ?? file2.team });
|
|
65523
65809
|
if (!state.success) {
|
|
65524
65810
|
refused3.push(`${file2.from}: it is not the state of a mission: ${state.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ")}`);
|
|
65525
65811
|
continue;
|
|
@@ -65531,7 +65817,7 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
65531
65817
|
rmSync14(file2.from);
|
|
65532
65818
|
}
|
|
65533
65819
|
if (dryRun) continue;
|
|
65534
|
-
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) if (entry.isDirectory()) emptyDir(
|
|
65820
|
+
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) if (entry.isDirectory()) emptyDir(join43(nodeDir2, entry.name));
|
|
65535
65821
|
emptyDir(nodeDir2);
|
|
65536
65822
|
}
|
|
65537
65823
|
emptyDir(nodesDir);
|
|
@@ -65553,13 +65839,13 @@ var MISSION_FIELDS = {
|
|
|
65553
65839
|
{ name: "result", type: "string", nullable: true, summary: "The hand-off of the mission, when it ended." },
|
|
65554
65840
|
{ name: "summary", type: "string", nullable: true, summary: "The first sentence of the Intent of the brief." },
|
|
65555
65841
|
{ 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
|
|
65842
|
+
{ 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
65843
|
{ 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
65844
|
{ name: "history", type: "array", summary: "Each change of state, with its time and who made it." }
|
|
65559
65845
|
]
|
|
65560
65846
|
};
|
|
65561
65847
|
function workspaceOf3(input2) {
|
|
65562
|
-
return
|
|
65848
|
+
return resolve22(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
65563
65849
|
}
|
|
65564
65850
|
function refused2(message, hint) {
|
|
65565
65851
|
return new CliError({ code: "invalid-input", message, exitCode: ExitCode.Validation, ...hint && { hint } });
|
|
@@ -65641,7 +65927,7 @@ function missionNote(context, record2) {
|
|
|
65641
65927
|
}
|
|
65642
65928
|
}
|
|
65643
65929
|
function shownState(record2) {
|
|
65644
|
-
return
|
|
65930
|
+
return waitsOnDecision(record2) && record2.state !== "waiting" ? `${record2.state}, waits on you` : record2.state;
|
|
65645
65931
|
}
|
|
65646
65932
|
function missionLines2(context, records) {
|
|
65647
65933
|
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 +65979,7 @@ var missionNew = {
|
|
|
65693
65979
|
if (!id.success) throw refused2(`"${String(input2.positionals.id)}" is not a mission id: use lowercase letters, digits and hyphens.`);
|
|
65694
65980
|
const file2 = missionFile(teamRoot(team), id.data);
|
|
65695
65981
|
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 (
|
|
65982
|
+
if (existsSync38(file2) && input2.flags.force !== true) throw refused2(`${relative8(workspaceDir, file2)} exists.`, "Pass --force to write over it.");
|
|
65697
65983
|
const intent = await asked(context, input2, "intent", "Intent: why does the team do this mission? ");
|
|
65698
65984
|
const endState = await asked(context, input2, "end-state", "End state: what is true when the mission is done? ");
|
|
65699
65985
|
const limits = await asked(context, input2, "limits", "Limits: what must the team not do or change? ");
|
|
@@ -65772,7 +66058,7 @@ var missionShow = {
|
|
|
65772
66058
|
if (record2.sessionId) context.output.line(`session: ${record2.sessionId}`);
|
|
65773
66059
|
if (record2.brief) context.output.line(["", record2.brief].join("\n"));
|
|
65774
66060
|
if (record2.lead) context.output.line(`lead: ${record2.lead}`);
|
|
65775
|
-
if (record2.
|
|
66061
|
+
if (record2.receivers.length > 0) context.output.line(`decision receivers: ${record2.receivers.join(", ")} (each question goes to every live one)`);
|
|
65776
66062
|
if (record2.claim) context.output.line(`claim: ${record2.claim}${record2.claimedAt ? ` since ${record2.claimedAt}` : ""}${record2.stale ? " (stale)" : ""}`);
|
|
65777
66063
|
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
66064
|
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 +66133,31 @@ var missionSay = {
|
|
|
65847
66133
|
return { data: { ...said2, node: node2.name } };
|
|
65848
66134
|
}
|
|
65849
66135
|
};
|
|
66136
|
+
var CLAUDE_SESSION_ENV = "CLAUDE_CODE_SESSION_ID";
|
|
66137
|
+
function missionMembership(verb, summary, why) {
|
|
66138
|
+
return {
|
|
66139
|
+
path: ["mission", verb],
|
|
66140
|
+
summary,
|
|
66141
|
+
credential: "none",
|
|
66142
|
+
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.` }],
|
|
66143
|
+
positionals: [MISSION_ID2],
|
|
66144
|
+
outputShape: MISSION_FIELDS,
|
|
66145
|
+
exitCodes: [0, 3],
|
|
66146
|
+
routes: [],
|
|
66147
|
+
examples: [{ command: `orbital mission ${verb} pr-42`, why }],
|
|
66148
|
+
run: async (input2, context) => {
|
|
66149
|
+
const mission = idOf(input2);
|
|
66150
|
+
const externalId = typeof input2.flags["external-id"] === "string" ? input2.flags["external-id"] : context.env[CLAUDE_SESSION_ENV] || null;
|
|
66151
|
+
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}.`);
|
|
66152
|
+
const node2 = await nodeOfMission(context, mission, () => nodeOfProject(context, workspaceOf3(input2)));
|
|
66153
|
+
const record2 = MissionRecordSchema.parse(await nodeCall(context, node2, `/orbital/missions/${verb}`, { missionId: mission, externalId }));
|
|
66154
|
+
context.output.line(`${record2.missionId} on ${record2.node}: ${record2.state}; decision receivers: ${record2.receivers.join(", ") || "none"}`);
|
|
66155
|
+
return { data: record2 };
|
|
66156
|
+
}
|
|
66157
|
+
};
|
|
66158
|
+
}
|
|
66159
|
+
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.");
|
|
66160
|
+
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
66161
|
var missionMigrate = {
|
|
65851
66162
|
path: ["mission", "migrate"],
|
|
65852
66163
|
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 +66172,11 @@ var missionMigrate = {
|
|
|
65861
66172
|
const workspaceDir = workspaceOf3(input2);
|
|
65862
66173
|
const source = definitionSource(workspaceDir, context.env);
|
|
65863
66174
|
if (!source) throw refused2(`${workspaceDir} has no definition of orbital.`);
|
|
65864
|
-
const running = (await liveNodes(orbitalHome(context.env), context.fetch)).filter((node2) =>
|
|
66175
|
+
const running = (await liveNodes(orbitalHome(context.env), context.fetch)).filter((node2) => resolve22(node2.workspaceDir) === resolve22(source.projectDir));
|
|
65865
66176
|
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
66177
|
const project = sourceConfig(source).node ?? defaultNodeName(workspaceDir);
|
|
65867
66178
|
const nodes = teamNodes(readTeams(source.root).teams, project);
|
|
65868
|
-
const migration = migrateMissions(source.root,
|
|
66179
|
+
const migration = migrateMissions(source.root, join44(orbitalHome(context.env), "missions"), nodes, input2.flags["dry-run"] === true);
|
|
65869
66180
|
for (const move of migration.moved) context.output.line(`${input2.flags["dry-run"] === true ? "would move" : "moved"} ${move.from} -> ${move.to}`);
|
|
65870
66181
|
for (const reason of migration.refused) context.output.warn(reason);
|
|
65871
66182
|
if (migration.moved.length === 0 && migration.refused.length === 0) context.output.line("Nothing to move: the missions are in the new layout.");
|
|
@@ -65890,10 +66201,10 @@ var missionReply = {
|
|
|
65890
66201
|
return { data: record2 };
|
|
65891
66202
|
}
|
|
65892
66203
|
};
|
|
65893
|
-
var missionCommands = [missionNew, missionStart, missionList, missionShow, missionPause, missionResume, missionSay, missionReply, missionDone, missionMigrate].map((spec) => ({ ...spec, group: CommandGroup.OperatingModel }));
|
|
66204
|
+
var missionCommands = [missionNew, missionStart, missionList, missionShow, missionPause, missionResume, missionSay, missionReply, missionJoin, missionLeave, missionDone, missionMigrate].map((spec) => ({ ...spec, group: CommandGroup.OperatingModel }));
|
|
65894
66205
|
|
|
65895
66206
|
// src/core/cli/check.ts
|
|
65896
|
-
import { resolve as
|
|
66207
|
+
import { resolve as resolve23 } from "node:path";
|
|
65897
66208
|
var LOAD_RULE = "orbital.load";
|
|
65898
66209
|
async function loadRefusals(workspaceDir, env) {
|
|
65899
66210
|
try {
|
|
@@ -65931,7 +66242,7 @@ var checkCommand = {
|
|
|
65931
66242
|
{ command: "orbital check --annotations", why: "Show the findings on a pull request." }
|
|
65932
66243
|
],
|
|
65933
66244
|
run: async (input2, context) => {
|
|
65934
|
-
const workspaceDir =
|
|
66245
|
+
const workspaceDir = resolve23(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
65935
66246
|
const source = definitionSource(workspaceDir, context.env);
|
|
65936
66247
|
const findings = await loadRefusals(workspaceDir, context.env);
|
|
65937
66248
|
for (const finding of findings) context.output.line(`${finding.finding} ${finding.rule} ${finding.path} ${finding.message}`);
|
|
@@ -66241,18 +66552,18 @@ function createOrbitalCommands(options) {
|
|
|
66241
66552
|
}
|
|
66242
66553
|
|
|
66243
66554
|
// src/core/cli/shipped-skills.ts
|
|
66244
|
-
var
|
|
66245
|
-
import { cpSync as cpSync4, existsSync as
|
|
66555
|
+
var import_yaml9 = __toESM(require_dist(), 1);
|
|
66556
|
+
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
66557
|
import { createHash as createHash11 } from "node:crypto";
|
|
66247
66558
|
import { homedir as homedir11 } from "node:os";
|
|
66248
|
-
import { dirname as dirname18, join as
|
|
66559
|
+
import { dirname as dirname18, join as join45, relative as relative9, resolve as resolve24, sep as sep7 } from "node:path";
|
|
66249
66560
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
66250
66561
|
var HASH_FILE = ".installed-hash";
|
|
66251
66562
|
function skillsRoot(shipped) {
|
|
66252
66563
|
let directory = dirname18(fileURLToPath3(shipped.anchor));
|
|
66253
66564
|
for (let depth = 0; depth < 5; depth += 1) {
|
|
66254
|
-
const candidate =
|
|
66255
|
-
if (
|
|
66565
|
+
const candidate = join45(directory, "skills");
|
|
66566
|
+
if (existsSync39(join45(candidate, shipped.names[0], "SKILL.md"))) return candidate;
|
|
66256
66567
|
directory = dirname18(directory);
|
|
66257
66568
|
}
|
|
66258
66569
|
throw new CliError({
|
|
@@ -66264,33 +66575,33 @@ function skillsRoot(shipped) {
|
|
|
66264
66575
|
}
|
|
66265
66576
|
function filesOf2(dir) {
|
|
66266
66577
|
return readdirSync22(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
66267
|
-
const path3 =
|
|
66578
|
+
const path3 = join45(dir, entry.name);
|
|
66268
66579
|
if (entry.isDirectory()) return filesOf2(path3);
|
|
66269
66580
|
return entry.name === HASH_FILE ? [] : [path3];
|
|
66270
66581
|
});
|
|
66271
66582
|
}
|
|
66272
66583
|
function descriptionOf(skillFile) {
|
|
66273
|
-
const match = /^---\n([\s\S]*?)\n---/.exec(
|
|
66584
|
+
const match = /^---\n([\s\S]*?)\n---/.exec(readFileSync33(skillFile, "utf8"));
|
|
66274
66585
|
if (!match?.[1]) return "";
|
|
66275
|
-
const frontmatter = (0,
|
|
66586
|
+
const frontmatter = (0, import_yaml9.parse)(match[1]);
|
|
66276
66587
|
return typeof frontmatter?.description === "string" ? frontmatter.description : "";
|
|
66277
66588
|
}
|
|
66278
66589
|
function shippedSkills(shipped) {
|
|
66279
66590
|
const root = skillsRoot(shipped);
|
|
66280
66591
|
return shipped.names.map((name) => {
|
|
66281
|
-
const dir =
|
|
66282
|
-
if (!
|
|
66592
|
+
const dir = join45(root, name);
|
|
66593
|
+
if (!existsSync39(join45(dir, "SKILL.md"))) {
|
|
66283
66594
|
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
66595
|
}
|
|
66285
66596
|
const files = filesOf2(dir).map((file2) => relative9(dir, file2).split(sep7).join("/")).sort();
|
|
66286
|
-
return { name, description: descriptionOf(
|
|
66597
|
+
return { name, description: descriptionOf(join45(dir, "SKILL.md")), dir, files };
|
|
66287
66598
|
});
|
|
66288
66599
|
}
|
|
66289
66600
|
function folderHash(dir) {
|
|
66290
66601
|
const hash2 = createHash11("sha256");
|
|
66291
66602
|
for (const file2 of filesOf2(dir).map((path3) => relative9(dir, path3).split(sep7).join("/")).sort()) {
|
|
66292
66603
|
hash2.update(`${file2}\0`);
|
|
66293
|
-
hash2.update(
|
|
66604
|
+
hash2.update(readFileSync33(join45(dir, file2)));
|
|
66294
66605
|
hash2.update("\0");
|
|
66295
66606
|
}
|
|
66296
66607
|
return hash2.digest("hex");
|
|
@@ -66310,18 +66621,18 @@ function pick2(all, names) {
|
|
|
66310
66621
|
});
|
|
66311
66622
|
}
|
|
66312
66623
|
function installBase(input2) {
|
|
66313
|
-
if (typeof input2.flags.dir === "string") return
|
|
66314
|
-
if (input2.flags.global === true) return
|
|
66315
|
-
return
|
|
66624
|
+
if (typeof input2.flags.dir === "string") return resolve24(input2.flags.dir);
|
|
66625
|
+
if (input2.flags.global === true) return join45(homedir11(), ".claude", "skills");
|
|
66626
|
+
return resolve24(".claude", "skills");
|
|
66316
66627
|
}
|
|
66317
66628
|
function planOne(skill, base2) {
|
|
66318
|
-
const target =
|
|
66629
|
+
const target = join45(base2, skill.name);
|
|
66319
66630
|
const hash2 = folderHash(skill.dir);
|
|
66320
|
-
if (!
|
|
66631
|
+
if (!existsSync39(join45(target, "SKILL.md"))) return { skill, target, hash: hash2, action: "installed", edited: false };
|
|
66321
66632
|
const onDisk = folderHash(target);
|
|
66322
66633
|
if (onDisk === hash2) return { skill, target, hash: hash2, action: "unchanged", edited: false };
|
|
66323
|
-
const hashFile =
|
|
66324
|
-
const recorded =
|
|
66634
|
+
const hashFile = join45(target, HASH_FILE);
|
|
66635
|
+
const recorded = existsSync39(hashFile) ? readFileSync33(hashFile, "utf8").trim() : "";
|
|
66325
66636
|
return { skill, target, hash: hash2, action: "updated", edited: onDisk !== recorded };
|
|
66326
66637
|
}
|
|
66327
66638
|
function writeOne(plan) {
|
|
@@ -66329,7 +66640,7 @@ function writeOne(plan) {
|
|
|
66329
66640
|
rmSync15(plan.target, { recursive: true, force: true });
|
|
66330
66641
|
mkdirSync28(plan.target, { recursive: true });
|
|
66331
66642
|
cpSync4(plan.skill.dir, plan.target, { recursive: true });
|
|
66332
|
-
writeFileSync25(
|
|
66643
|
+
writeFileSync25(join45(plan.target, HASH_FILE), `${plan.hash}
|
|
66333
66644
|
`);
|
|
66334
66645
|
} catch (error62) {
|
|
66335
66646
|
throw new CliError({
|
|
@@ -66374,7 +66685,7 @@ function createSkillCommands(shipped) {
|
|
|
66374
66685
|
context.output.line(skill.dir);
|
|
66375
66686
|
return { data: { name: skill.name, path: skill.dir, content: null } };
|
|
66376
66687
|
}
|
|
66377
|
-
const content =
|
|
66688
|
+
const content = readFileSync33(join45(skill.dir, "SKILL.md"), "utf8");
|
|
66378
66689
|
context.output.line(content);
|
|
66379
66690
|
return { data: { name: skill.name, path: skill.dir, content } };
|
|
66380
66691
|
}
|
|
@@ -66454,21 +66765,21 @@ function createSkillCommands(shipped) {
|
|
|
66454
66765
|
}
|
|
66455
66766
|
|
|
66456
66767
|
// src/init.ts
|
|
66457
|
-
import { existsSync as
|
|
66458
|
-
import { dirname as dirname19, isAbsolute as isAbsolute8, relative as relative10, resolve as
|
|
66459
|
-
var
|
|
66768
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync29, writeFileSync as writeFileSync26 } from "node:fs";
|
|
66769
|
+
import { dirname as dirname19, isAbsolute as isAbsolute8, relative as relative10, resolve as resolve25 } from "node:path";
|
|
66770
|
+
var import_yaml10 = __toESM(require_dist(), 1);
|
|
66460
66771
|
function initHarnessFolder(workspaceDir, adapter = CLAUDE_ADAPTER) {
|
|
66461
66772
|
const file2 = harnessRootAt(workspaceDir, adapter).file;
|
|
66462
|
-
if (
|
|
66773
|
+
if (existsSync40(file2)) {
|
|
66463
66774
|
throw new CliError({ code: "conflict", message: `This project has ${file2} already.`, exitCode: ExitCode.Validation, hint: "A harness folder holds one orbital.yaml." });
|
|
66464
66775
|
}
|
|
66465
66776
|
mkdirSync29(dirname19(file2), { recursive: true });
|
|
66466
|
-
writeFileSync26(file2, (0,
|
|
66777
|
+
writeFileSync26(file2, (0, import_yaml10.stringify)({ apiVersion: ORBITAL_API_VERSION }));
|
|
66467
66778
|
return file2;
|
|
66468
66779
|
}
|
|
66469
66780
|
function workspaceOf4(flag) {
|
|
66470
|
-
const workspaceDir =
|
|
66471
|
-
if (!
|
|
66781
|
+
const workspaceDir = resolve25(typeof flag === "string" ? flag : ".");
|
|
66782
|
+
if (!existsSync40(workspaceDir)) throw new CliError({ code: "not-found", message: `${workspaceDir} does not exist.`, exitCode: ExitCode.Validation });
|
|
66472
66783
|
return workspaceDir;
|
|
66473
66784
|
}
|
|
66474
66785
|
var WORKSPACE3 = { name: "workspace", type: "path", summary: "The project. Default: the current directory." };
|
|
@@ -66544,7 +66855,7 @@ function targetOf2(context, workspaceDir, named2, given, adapters) {
|
|
|
66544
66855
|
});
|
|
66545
66856
|
}
|
|
66546
66857
|
async function chooseTargets(context, workspaceDir, adapters) {
|
|
66547
|
-
const present = adapters.filter((adapter) =>
|
|
66858
|
+
const present = adapters.filter((adapter) => existsSync40(resolve25(workspaceDir, adapter.folder.folder)));
|
|
66548
66859
|
if (present.length === 1) {
|
|
66549
66860
|
context.output.step(`installing ${present[0].id}: ${present[0].folder.folder} is the harness folder of this project`);
|
|
66550
66861
|
return [{ adapter: present[0], given: null }];
|
|
@@ -66578,9 +66889,9 @@ async function adapterInstall(context, adapter, workspaceDir, force) {
|
|
|
66578
66889
|
async function installTarget(context, workspaceDir, target, user, force) {
|
|
66579
66890
|
const { adapter, given } = target;
|
|
66580
66891
|
const root = harnessRootAt(workspaceDir, adapter);
|
|
66581
|
-
const file2 =
|
|
66892
|
+
const file2 = existsSync40(root.file) ? null : initHarnessFolder(workspaceDir, adapter);
|
|
66582
66893
|
if (file2) context.output.line(`wrote ${file2}`);
|
|
66583
|
-
const plugin = given ? { name: given.ref.name, source: user ? isPackageSource(given.ref.source) ? given.ref.source :
|
|
66894
|
+
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
66895
|
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
66896
|
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
66897
|
const install2 = await adapterInstall(context, adapter, workspaceDir, force);
|
|
@@ -66665,6 +66976,12 @@ function followAttach(context, options, signal, inbox, timing = {}) {
|
|
|
66665
66976
|
delivery: (event) => {
|
|
66666
66977
|
const delivery = DeliverySchema.safeParse(event);
|
|
66667
66978
|
if (delivery.success) inbox.push(delivery.data);
|
|
66979
|
+
const answered = AnsweredSchema.safeParse(event);
|
|
66980
|
+
if (!answered.success) return;
|
|
66981
|
+
for (let index = inbox.length - 1; index >= 0; index -= 1) {
|
|
66982
|
+
const held2 = inbox[index];
|
|
66983
|
+
if (held2.mission === answered.data.mission && (answered.data.question === null || held2.question === answered.data.question)) inbox.splice(index, 1);
|
|
66984
|
+
}
|
|
66668
66985
|
},
|
|
66669
66986
|
detached: (reason) => {
|
|
66670
66987
|
if (kept) kept.ended = true;
|
|
@@ -66794,9 +67111,8 @@ function mcpCommand(toolsOf) {
|
|
|
66794
67111
|
const joined = await attachment.current();
|
|
66795
67112
|
return { url: `${joined.nodeUrl}/a2a`, token: joined.token };
|
|
66796
67113
|
},
|
|
66797
|
-
|
|
66798
|
-
|
|
66799
|
-
},
|
|
67114
|
+
sessionId: null,
|
|
67115
|
+
attached: async () => (await attachment.current()).sessionId,
|
|
66800
67116
|
harness,
|
|
66801
67117
|
follow: true,
|
|
66802
67118
|
tools: inboxTools(context, attachment.current, inbox)
|
|
@@ -66805,8 +67121,8 @@ function mcpCommand(toolsOf) {
|
|
|
66805
67121
|
const server = await mcpServer(options, input2.flags.actions === true, toolsOf);
|
|
66806
67122
|
const transport = new StdioServerTransport();
|
|
66807
67123
|
await server.connect(transport);
|
|
66808
|
-
await new Promise((
|
|
66809
|
-
server.server.onclose =
|
|
67124
|
+
await new Promise((resolve26) => {
|
|
67125
|
+
server.server.onclose = resolve26;
|
|
66810
67126
|
process.stdin.once("end", () => void server.close());
|
|
66811
67127
|
});
|
|
66812
67128
|
stop.abort();
|
|
@@ -66832,6 +67148,10 @@ function orbitalCommands(options) {
|
|
|
66832
67148
|
...options.commands ?? []
|
|
66833
67149
|
];
|
|
66834
67150
|
}
|
|
67151
|
+
function workspacePluginCommands(options) {
|
|
67152
|
+
const reserved = [.../* @__PURE__ */ new Set([...options.builtIn.map((spec) => spec.path[0]), ...INTROSPECTION_WORDS])];
|
|
67153
|
+
return pluginCommandSpecs({ workspaceDir: options.workspaceDir, env: options.env, reserved, ...options.terminal && { terminal: options.terminal } });
|
|
67154
|
+
}
|
|
66835
67155
|
|
|
66836
67156
|
// src/launch.ts
|
|
66837
67157
|
import { spawn as spawn9 } from "node:child_process";
|
|
@@ -66869,7 +67189,8 @@ export {
|
|
|
66869
67189
|
nodeLaunch,
|
|
66870
67190
|
nodeStartArgv,
|
|
66871
67191
|
orbitalCommands,
|
|
66872
|
-
skillCommands
|
|
67192
|
+
skillCommands,
|
|
67193
|
+
workspacePluginCommands
|
|
66873
67194
|
};
|
|
66874
67195
|
/*! Bundled license information:
|
|
66875
67196
|
|