@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/orbital.js
CHANGED
|
@@ -71,7 +71,7 @@ var VERSION, current;
|
|
|
71
71
|
var init_brand = __esm({
|
|
72
72
|
"src/kit/command/brand.ts"() {
|
|
73
73
|
"use strict";
|
|
74
|
-
VERSION = true ? "0.0.
|
|
74
|
+
VERSION = true ? "0.0.5" : "0.0.0-dev";
|
|
75
75
|
current = defineBrand({ program: "orbital" });
|
|
76
76
|
}
|
|
77
77
|
});
|
|
@@ -1214,19 +1214,19 @@ async function readSecretTerminal(prompt) {
|
|
|
1214
1214
|
process.stdin.resume();
|
|
1215
1215
|
process.stdin.setEncoding("utf8");
|
|
1216
1216
|
try {
|
|
1217
|
-
return await new Promise((
|
|
1217
|
+
return await new Promise((resolve27) => {
|
|
1218
1218
|
let typed = "";
|
|
1219
1219
|
const onData = (chunk) => {
|
|
1220
1220
|
for (const char of chunk) {
|
|
1221
1221
|
if (char === "") {
|
|
1222
1222
|
process.stdin.off("data", onData);
|
|
1223
|
-
|
|
1223
|
+
resolve27(null);
|
|
1224
1224
|
return;
|
|
1225
1225
|
}
|
|
1226
1226
|
if (char === "\r" || char === "\n") {
|
|
1227
1227
|
process.stdin.off("data", onData);
|
|
1228
1228
|
process.stderr.write("\n");
|
|
1229
|
-
|
|
1229
|
+
resolve27(typed || null);
|
|
1230
1230
|
return;
|
|
1231
1231
|
}
|
|
1232
1232
|
if (char === "\x7F" || char === "\b") {
|
|
@@ -1270,8 +1270,10 @@ function toCliError(error62, message) {
|
|
|
1270
1270
|
});
|
|
1271
1271
|
}
|
|
1272
1272
|
async function runProgram(program, options) {
|
|
1273
|
-
const {
|
|
1273
|
+
const { streams, env: env3, isTty } = options;
|
|
1274
1274
|
const { registry: registry2 } = program;
|
|
1275
|
+
const asksHelp = options.argv[0] === "help" && !registry2.some((spec) => spec.path[0] === "help");
|
|
1276
|
+
const argv = asksHelp ? [...options.argv.slice(1), "--help"] : options.argv;
|
|
1275
1277
|
const guessed = resolveCommand(argv, registry2);
|
|
1276
1278
|
const mode = resolveMode(
|
|
1277
1279
|
typeof peekFlag(argv, "output") === "string" ? String(peekFlag(argv, "output")) : guessed?.defaultOutput,
|
|
@@ -1308,7 +1310,7 @@ async function runProgram(program, options) {
|
|
|
1308
1310
|
output: commandOutput,
|
|
1309
1311
|
env: env3,
|
|
1310
1312
|
now: options.now ?? (() => Date.now()),
|
|
1311
|
-
sleep: options.sleep ?? ((ms) => new Promise((
|
|
1313
|
+
sleep: options.sleep ?? ((ms) => new Promise((resolve27) => setTimeout(resolve27, ms))),
|
|
1312
1314
|
fetch: options.fetch ?? globalThis.fetch,
|
|
1313
1315
|
quiet,
|
|
1314
1316
|
nonInteractive: peekFlag(argv, "non-interactive") === true || !isTty,
|
|
@@ -6046,7 +6048,7 @@ function isRef(value2) {
|
|
|
6046
6048
|
function cloneIssues(issues2) {
|
|
6047
6049
|
return issues2.map((iss) => iss.path ? { ...iss, path: iss.path.slice() } : { ...iss });
|
|
6048
6050
|
}
|
|
6049
|
-
function isRecursive(inst, stack,
|
|
6051
|
+
function isRecursive(inst, stack, resolve27) {
|
|
6050
6052
|
const cached2 = recursive.get(inst);
|
|
6051
6053
|
if (cached2 !== void 0)
|
|
6052
6054
|
return cached2 ? PROVEN : NONE;
|
|
@@ -6056,7 +6058,7 @@ function isRecursive(inst, stack, resolve25) {
|
|
|
6056
6058
|
let result2 = NONE;
|
|
6057
6059
|
const check4 = (child) => {
|
|
6058
6060
|
if (result2 !== PROVEN && child?._zod) {
|
|
6059
|
-
const answer2 = isRecursive(child, stack,
|
|
6061
|
+
const answer2 = isRecursive(child, stack, resolve27);
|
|
6060
6062
|
if (answer2 > result2)
|
|
6061
6063
|
result2 = answer2;
|
|
6062
6064
|
}
|
|
@@ -6067,7 +6069,7 @@ function isRecursive(inst, stack, resolve25) {
|
|
|
6067
6069
|
const desc = Object.getOwnPropertyDescriptor(sh, key2);
|
|
6068
6070
|
if (spread && !desc.enumerable)
|
|
6069
6071
|
continue;
|
|
6070
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
6072
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve27) : NONE;
|
|
6071
6073
|
if (child > answer2)
|
|
6072
6074
|
answer2 = child;
|
|
6073
6075
|
}
|
|
@@ -6131,7 +6133,7 @@ function isRecursive(inst, stack, resolve25) {
|
|
|
6131
6133
|
break;
|
|
6132
6134
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
6133
6135
|
case "lazy": {
|
|
6134
|
-
const inner = def._cachedInner ?? (
|
|
6136
|
+
const inner = def._cachedInner ?? (resolve27 ? inst._zod.innerType : void 0);
|
|
6135
6137
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
6136
6138
|
break;
|
|
6137
6139
|
}
|
|
@@ -29932,7 +29934,7 @@ function leadFailure(mission) {
|
|
|
29932
29934
|
const last = mission.history.at(-1);
|
|
29933
29935
|
return mission.state === "paused" && last?.state === "paused" && last.note?.startsWith(LEAD_FAILED) ? last.note : null;
|
|
29934
29936
|
}
|
|
29935
|
-
function
|
|
29937
|
+
function waitsOnDecision(mission) {
|
|
29936
29938
|
return mission.state === "waiting" || mission.state === "active" && mission.questions.length > 0;
|
|
29937
29939
|
}
|
|
29938
29940
|
function sourceLink(source) {
|
|
@@ -29969,14 +29971,21 @@ function steerParts(text3) {
|
|
|
29969
29971
|
const match = /^\[orbital\] message during your turn, from (.+?)(?: on the mission [^:\n]+)?:\n([\s\S]*)$/.exec(text3);
|
|
29970
29972
|
return match ? { from: match[1], text: match[2] } : { from: "the person", text: text3 };
|
|
29971
29973
|
}
|
|
29972
|
-
function
|
|
29973
|
-
return `${
|
|
29974
|
+
function externalReceiver(externalId) {
|
|
29975
|
+
return `${EXTERNAL_RECEIVER}${externalId}`;
|
|
29974
29976
|
}
|
|
29975
|
-
function
|
|
29976
|
-
|
|
29977
|
-
return at > 0 && at < giver.length - 1 ? { sessionId: giver.slice(0, at), node: giver.slice(at + 1) } : null;
|
|
29977
|
+
function externalIdOf(receiver) {
|
|
29978
|
+
return receiver.startsWith(EXTERNAL_RECEIVER) ? receiver.slice(EXTERNAL_RECEIVER.length) : null;
|
|
29978
29979
|
}
|
|
29979
|
-
|
|
29980
|
+
function receiverRef(receiver) {
|
|
29981
|
+
return `${receiver.sessionId}@${receiver.node}`;
|
|
29982
|
+
}
|
|
29983
|
+
function remoteReceiverOf(receiver) {
|
|
29984
|
+
if (externalIdOf(receiver) !== null) return null;
|
|
29985
|
+
const at = receiver.lastIndexOf("@");
|
|
29986
|
+
return at > 0 && at < receiver.length - 1 ? { sessionId: receiver.slice(0, at), node: receiver.slice(at + 1) } : null;
|
|
29987
|
+
}
|
|
29988
|
+
var MISSION_STATES, MissionStateSchema, MissionChangeSchema, LEAD_FAILED, MISSION_STATE_FILE, MissionLinkSchema, MissionAskedSchema, MISSION_QUESTION_META, GITHUB_SOURCE, HEADING, MissionStateFileSchema, MISSION_ID, MissionIdSchema, MissionRecordSchema, MissionListSchema, MissionOutcomeSchema, MissionHandoffSchema, MissionLinkRequestSchema, MissionAskRequestSchema, MissionStartRequestSchema, MissionNewRequestSchema, MissionWrittenSchema, MissionReplySchema, RemoteReceiverSchema, SENDER_FIELDS, RelayJoinSchema, RelayReplySchema, MissionControlSchema, STEER_MARK, SayRequestSchema, SaidSchema, RelayMessageSchema, RelayControlSchema, MissionQuestionSchema, EXTERNAL_RECEIVER, MissionJoinSchema;
|
|
29980
29989
|
var init_missions = __esm({
|
|
29981
29990
|
"src/protocol/missions.ts"() {
|
|
29982
29991
|
"use strict";
|
|
@@ -30014,7 +30023,7 @@ var init_missions = __esm({
|
|
|
30014
30023
|
source: external_exports.string().nullable(),
|
|
30015
30024
|
result: external_exports.string().nullable(),
|
|
30016
30025
|
question: external_exports.string().nullable(),
|
|
30017
|
-
|
|
30026
|
+
receivers: external_exports.array(external_exports.string().min(1)).default([]),
|
|
30018
30027
|
lead: external_exports.string().nullable(),
|
|
30019
30028
|
claim: external_exports.string().nullable(),
|
|
30020
30029
|
claimedAt: external_exports.string().nullable(),
|
|
@@ -30041,7 +30050,7 @@ var init_missions = __esm({
|
|
|
30041
30050
|
result: external_exports.string().nullable(),
|
|
30042
30051
|
question: external_exports.string().nullable(),
|
|
30043
30052
|
questions: external_exports.array(MissionAskedSchema).default([]),
|
|
30044
|
-
|
|
30053
|
+
receivers: external_exports.array(external_exports.string().min(1)).default([]),
|
|
30045
30054
|
lead: external_exports.string().nullable(),
|
|
30046
30055
|
lastActivity: external_exports.string().nullable().default(null),
|
|
30047
30056
|
claim: external_exports.string().nullable().default(null),
|
|
@@ -30083,9 +30092,9 @@ var init_missions = __esm({
|
|
|
30083
30092
|
text: external_exports.string().min(1),
|
|
30084
30093
|
questionId: external_exports.string().min(1).optional()
|
|
30085
30094
|
});
|
|
30086
|
-
|
|
30095
|
+
RemoteReceiverSchema = external_exports.object({ node: external_exports.string().min(1), sessionId: external_exports.string().min(1) });
|
|
30087
30096
|
SENDER_FIELDS = { team: external_exports.string().min(1).nullable().default(null), agent: external_exports.string().min(1).nullable().default(null) };
|
|
30088
|
-
RelayJoinSchema = external_exports.object({ missionId: MissionIdSchema,
|
|
30097
|
+
RelayJoinSchema = external_exports.object({ missionId: MissionIdSchema, receiver: RemoteReceiverSchema, ...SENDER_FIELDS });
|
|
30089
30098
|
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 });
|
|
30090
30099
|
MissionControlSchema = external_exports.object({ missionId: MissionIdSchema });
|
|
30091
30100
|
STEER_MARK = "[orbital] message during your turn";
|
|
@@ -30107,11 +30116,13 @@ var init_missions = __esm({
|
|
|
30107
30116
|
leadSessionId: external_exports.string().min(1),
|
|
30108
30117
|
question: external_exports.string(),
|
|
30109
30118
|
questionId: external_exports.string().min(1).optional(),
|
|
30110
|
-
|
|
30119
|
+
receiver: RemoteReceiverSchema,
|
|
30111
30120
|
team: external_exports.string().min(1).nullable().default(null)
|
|
30112
30121
|
});
|
|
30122
|
+
EXTERNAL_RECEIVER = "external:";
|
|
30113
30123
|
MissionJoinSchema = external_exports.object({
|
|
30114
|
-
missionId: MissionIdSchema
|
|
30124
|
+
missionId: MissionIdSchema,
|
|
30125
|
+
externalId: external_exports.string().min(1).optional()
|
|
30115
30126
|
});
|
|
30116
30127
|
}
|
|
30117
30128
|
});
|
|
@@ -30266,7 +30277,7 @@ var init_telemetry = __esm({
|
|
|
30266
30277
|
transition: MissionTransitionSchema,
|
|
30267
30278
|
reason: external_exports.string().nullable(),
|
|
30268
30279
|
by: external_exports.string(),
|
|
30269
|
-
|
|
30280
|
+
receivers: external_exports.array(external_exports.string()).default([]),
|
|
30270
30281
|
sessionId: external_exports.string().nullable(),
|
|
30271
30282
|
lead: external_exports.string().nullable(),
|
|
30272
30283
|
question: external_exports.string().nullable(),
|
|
@@ -30320,7 +30331,7 @@ var init_telemetry = __esm({
|
|
|
30320
30331
|
state: MissionStateSchema,
|
|
30321
30332
|
sessionId: external_exports.string().nullable(),
|
|
30322
30333
|
lead: external_exports.string().nullable(),
|
|
30323
|
-
|
|
30334
|
+
receivers: external_exports.array(external_exports.string()).default([]),
|
|
30324
30335
|
givenBy: external_exports.string().nullable(),
|
|
30325
30336
|
question: external_exports.string().nullable(),
|
|
30326
30337
|
questions: external_exports.array(MissionAskedSchema).default([]),
|
|
@@ -30960,6 +30971,9 @@ var init_node_client = __esm({
|
|
|
30960
30971
|
async joinMission(missionId) {
|
|
30961
30972
|
return MissionRecordSchema.parse(await this.request("/orbital/missions/join", { method: "POST", body: { missionId } }));
|
|
30962
30973
|
}
|
|
30974
|
+
async leaveMission(missionId) {
|
|
30975
|
+
return MissionRecordSchema.parse(await this.request("/orbital/missions/leave", { method: "POST", body: { missionId } }));
|
|
30976
|
+
}
|
|
30963
30977
|
async handoff(handoff2) {
|
|
30964
30978
|
return MissionRecordSchema.parse(await this.request("/orbital/missions/handoff", { method: "POST", body: handoff2 }));
|
|
30965
30979
|
}
|
|
@@ -31620,7 +31634,7 @@ var init_definition_files = __esm({
|
|
|
31620
31634
|
});
|
|
31621
31635
|
|
|
31622
31636
|
// src/protocol/attach.ts
|
|
31623
|
-
var AttachRequestSchema, AttachAnswerSchema, DeliverySchema, SettleSchema, ToolCheckRequestSchema, ToolDecisionSchema, ToolCheckAnswerSchema;
|
|
31637
|
+
var AttachRequestSchema, AttachAnswerSchema, DeliverySchema, AnsweredSchema, SettleSchema, ToolCheckRequestSchema, ToolDecisionSchema, ToolCheckAnswerSchema;
|
|
31624
31638
|
var init_attach = __esm({
|
|
31625
31639
|
"src/protocol/attach.ts"() {
|
|
31626
31640
|
"use strict";
|
|
@@ -31648,6 +31662,13 @@ var init_attach = __esm({
|
|
|
31648
31662
|
question: external_exports.string().nullable().default(null),
|
|
31649
31663
|
text: external_exports.string()
|
|
31650
31664
|
});
|
|
31665
|
+
AnsweredSchema = external_exports.object({
|
|
31666
|
+
kind: external_exports.literal("answered"),
|
|
31667
|
+
mission: external_exports.string().min(1),
|
|
31668
|
+
question: external_exports.string().nullable(),
|
|
31669
|
+
by: external_exports.string().min(1),
|
|
31670
|
+
text: external_exports.string()
|
|
31671
|
+
});
|
|
31651
31672
|
SettleSchema = external_exports.object({
|
|
31652
31673
|
taskId: external_exports.string().min(1).optional(),
|
|
31653
31674
|
text: external_exports.string()
|
|
@@ -31900,7 +31921,7 @@ var init_history = __esm({
|
|
|
31900
31921
|
});
|
|
31901
31922
|
|
|
31902
31923
|
// src/protocol/plugin-protocol.ts
|
|
31903
|
-
var PORTS_VERSION, PLUGIN_METHODS, PORT_METHODS, PluginManifestSchema, PluginInitSchema, HostEndpointSchema, PluginPackageSchema, PLUGIN_PACKAGE_FILE, MarketplaceSchema, MARKETPLACE_FILE, RPC_ERRORS;
|
|
31924
|
+
var PORTS_VERSION, PLUGIN_METHODS, PORT_METHODS, PLUGIN_COMMAND_RESERVED_OPTIONS, PluginCommandOptionSchema, PluginCommandArgSchema, PluginCommandSchema, PluginCommandsSchema, PluginCommandRequestSchema, PluginCommandResultSchema, PluginManifestSchema, PluginInitSchema, HostEndpointSchema, PluginPackageSchema, PLUGIN_PACKAGE_FILE, MarketplaceSchema, MARKETPLACE_FILE, RPC_ERRORS;
|
|
31904
31925
|
var init_plugin_protocol = __esm({
|
|
31905
31926
|
"src/protocol/plugin-protocol.ts"() {
|
|
31906
31927
|
"use strict";
|
|
@@ -31936,6 +31957,60 @@ var init_plugin_protocol = __esm({
|
|
|
31936
31957
|
reasoner: ["extractModule", "classify", "align", "checkConsistency"],
|
|
31937
31958
|
missions: ["join", "reply", "pause", "resume", "ask", "say"]
|
|
31938
31959
|
};
|
|
31960
|
+
PLUGIN_COMMAND_RESERVED_OPTIONS = ["output", "quiet", "non-interactive", "confirm", "timeout", "help", "version", "workspace", "node"];
|
|
31961
|
+
PluginCommandOptionSchema = external_exports.object({
|
|
31962
|
+
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(", ")}` }),
|
|
31963
|
+
type: external_exports.enum(["string", "boolean", "integer", "enum", "path"]),
|
|
31964
|
+
summary: external_exports.string().min(1),
|
|
31965
|
+
values: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
31966
|
+
required: external_exports.boolean().default(false),
|
|
31967
|
+
default: external_exports.union([external_exports.string(), external_exports.number().int(), external_exports.boolean()]).optional()
|
|
31968
|
+
}).strict().superRefine((option, issues2) => {
|
|
31969
|
+
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` });
|
|
31970
|
+
if (option.type === "boolean" && option.required) issues2.addIssue({ code: "custom", message: `the boolean option ${option.name} cannot be required` });
|
|
31971
|
+
if (option.default === void 0) return;
|
|
31972
|
+
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);
|
|
31973
|
+
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}` });
|
|
31974
|
+
});
|
|
31975
|
+
PluginCommandArgSchema = external_exports.object({
|
|
31976
|
+
name: external_exports.string().regex(NAME_SHAPE),
|
|
31977
|
+
summary: external_exports.string().min(1),
|
|
31978
|
+
required: external_exports.boolean().default(false),
|
|
31979
|
+
variadic: external_exports.boolean().default(false)
|
|
31980
|
+
}).strict();
|
|
31981
|
+
PluginCommandSchema = external_exports.object({
|
|
31982
|
+
name: external_exports.string().regex(NAME_SHAPE),
|
|
31983
|
+
summary: external_exports.string().min(1),
|
|
31984
|
+
args: external_exports.array(PluginCommandArgSchema).default([]),
|
|
31985
|
+
options: external_exports.array(PluginCommandOptionSchema).default([]),
|
|
31986
|
+
interactive: external_exports.boolean().default(false),
|
|
31987
|
+
node: external_exports.boolean().default(false)
|
|
31988
|
+
}).strict().superRefine((command, issues2) => {
|
|
31989
|
+
const twice = (names) => names.find((name, index) => names.indexOf(name) !== index);
|
|
31990
|
+
const arg = twice(command.args.map((one) => one.name));
|
|
31991
|
+
if (arg) issues2.addIssue({ code: "custom", message: `the command ${command.name} names the argument ${arg} twice` });
|
|
31992
|
+
const option = twice(command.options.map((one) => one.name));
|
|
31993
|
+
if (option) issues2.addIssue({ code: "custom", message: `the command ${command.name} names the option ${option} twice` });
|
|
31994
|
+
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` });
|
|
31995
|
+
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` });
|
|
31996
|
+
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` });
|
|
31997
|
+
});
|
|
31998
|
+
PluginCommandsSchema = external_exports.array(PluginCommandSchema).default([]).superRefine((commands, issues2) => {
|
|
31999
|
+
const names = commands.map((one) => one.name);
|
|
32000
|
+
const twice = names.find((name, index) => names.indexOf(name) !== index);
|
|
32001
|
+
if (twice) issues2.addIssue({ code: "custom", message: `the command ${twice} is declared twice` });
|
|
32002
|
+
});
|
|
32003
|
+
PluginCommandRequestSchema = external_exports.object({
|
|
32004
|
+
name: external_exports.string().regex(NAME_SHAPE),
|
|
32005
|
+
args: external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.array(external_exports.string())])).default({}),
|
|
32006
|
+
options: external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])).default({}),
|
|
32007
|
+
terminal: external_exports.object({ tty: external_exports.boolean() }).nullable().default(null)
|
|
32008
|
+
}).strict();
|
|
32009
|
+
PluginCommandResultSchema = external_exports.object({
|
|
32010
|
+
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),
|
|
32011
|
+
text: external_exports.string().nullable().default(null),
|
|
32012
|
+
data: external_exports.record(external_exports.string(), external_exports.unknown()).default({})
|
|
32013
|
+
}).strict();
|
|
31939
32014
|
PluginManifestSchema = external_exports.object({
|
|
31940
32015
|
name: external_exports.string().regex(NAME_SHAPE),
|
|
31941
32016
|
version: external_exports.string().min(1),
|
|
@@ -31965,7 +32040,7 @@ var init_plugin_protocol = __esm({
|
|
|
31965
32040
|
secrets: external_exports.array(external_exports.string().min(1)).default([]),
|
|
31966
32041
|
models: external_exports.array(external_exports.string().regex(/^[a-z0-9-]+:.+$/, "a model pattern is <provider>:<model>, such as anthropic:*")).default([])
|
|
31967
32042
|
}).prefault({}),
|
|
31968
|
-
commands:
|
|
32043
|
+
commands: PluginCommandsSchema,
|
|
31969
32044
|
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([])
|
|
31970
32045
|
}).strict();
|
|
31971
32046
|
PluginInitSchema = external_exports.object({
|
|
@@ -31988,7 +32063,8 @@ var init_plugin_protocol = __esm({
|
|
|
31988
32063
|
dir: external_exports.string().min(1).optional(),
|
|
31989
32064
|
env: external_exports.array(external_exports.string().regex(PLUGIN_ENV_NAME, "is not the name of an environment variable")).default([]),
|
|
31990
32065
|
config: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
|
|
31991
|
-
harnesses: external_exports.array(HarnessAdapterDeclarationSchema).default([])
|
|
32066
|
+
harnesses: external_exports.array(HarnessAdapterDeclarationSchema).default([]),
|
|
32067
|
+
commands: PluginCommandsSchema
|
|
31992
32068
|
}).strict();
|
|
31993
32069
|
PLUGIN_PACKAGE_FILE = "orbital-plugin.json";
|
|
31994
32070
|
MarketplaceSchema = external_exports.object({
|
|
@@ -32041,12 +32117,12 @@ var init_rpc_peer = __esm({
|
|
|
32041
32117
|
request(method, params, timeoutMs = this.timeoutMs) {
|
|
32042
32118
|
if (this.closedWith) return Promise.reject(this.closedWith);
|
|
32043
32119
|
const id = this.nextId++;
|
|
32044
|
-
return new Promise((
|
|
32120
|
+
return new Promise((resolve27, reject) => {
|
|
32045
32121
|
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
32046
32122
|
this.pending.delete(id);
|
|
32047
32123
|
reject(new RpcError(RPC_ERRORS.internal, `${method} got no answer in ${timeoutMs} ms`));
|
|
32048
32124
|
}, timeoutMs) : null;
|
|
32049
|
-
this.pending.set(id, { resolve:
|
|
32125
|
+
this.pending.set(id, { resolve: resolve27, reject, timer });
|
|
32050
32126
|
this.write({ jsonrpc: "2.0", id, method, ...params !== void 0 && { params } });
|
|
32051
32127
|
});
|
|
32052
32128
|
}
|
|
@@ -35234,7 +35310,7 @@ var require_compile = __commonJS({
|
|
|
35234
35310
|
const schOrFunc = root.refs[ref];
|
|
35235
35311
|
if (schOrFunc)
|
|
35236
35312
|
return schOrFunc;
|
|
35237
|
-
let _sch =
|
|
35313
|
+
let _sch = resolve27.call(this, root, ref);
|
|
35238
35314
|
if (_sch === void 0) {
|
|
35239
35315
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
35240
35316
|
const { schemaId } = this.opts;
|
|
@@ -35261,7 +35337,7 @@ var require_compile = __commonJS({
|
|
|
35261
35337
|
function sameSchemaEnv(s1, s2) {
|
|
35262
35338
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
35263
35339
|
}
|
|
35264
|
-
function
|
|
35340
|
+
function resolve27(root, ref) {
|
|
35265
35341
|
let sch;
|
|
35266
35342
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
35267
35343
|
ref = sch;
|
|
@@ -36125,7 +36201,7 @@ var require_fast_uri = __commonJS({
|
|
|
36125
36201
|
}
|
|
36126
36202
|
return uri;
|
|
36127
36203
|
}
|
|
36128
|
-
function
|
|
36204
|
+
function resolve27(baseURI, relativeURI, options) {
|
|
36129
36205
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
36130
36206
|
const {
|
|
36131
36207
|
parsed: baseParsed,
|
|
@@ -36516,7 +36592,7 @@ var require_fast_uri = __commonJS({
|
|
|
36516
36592
|
var fastUri = {
|
|
36517
36593
|
SCHEMES,
|
|
36518
36594
|
normalize,
|
|
36519
|
-
resolve:
|
|
36595
|
+
resolve: resolve27,
|
|
36520
36596
|
resolveComponent,
|
|
36521
36597
|
equal,
|
|
36522
36598
|
serialize,
|
|
@@ -39611,12 +39687,12 @@ var require_isexe = __commonJS({
|
|
|
39611
39687
|
if (typeof Promise !== "function") {
|
|
39612
39688
|
throw new TypeError("callback not provided");
|
|
39613
39689
|
}
|
|
39614
|
-
return new Promise(function(
|
|
39690
|
+
return new Promise(function(resolve27, reject) {
|
|
39615
39691
|
isexe(path3, options || {}, function(er, is) {
|
|
39616
39692
|
if (er) {
|
|
39617
39693
|
reject(er);
|
|
39618
39694
|
} else {
|
|
39619
|
-
|
|
39695
|
+
resolve27(is);
|
|
39620
39696
|
}
|
|
39621
39697
|
});
|
|
39622
39698
|
});
|
|
@@ -39682,27 +39758,27 @@ var require_which = __commonJS({
|
|
|
39682
39758
|
opt = {};
|
|
39683
39759
|
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
39684
39760
|
const found = [];
|
|
39685
|
-
const step = (i) => new Promise((
|
|
39761
|
+
const step = (i) => new Promise((resolve27, reject) => {
|
|
39686
39762
|
if (i === pathEnv.length)
|
|
39687
|
-
return opt.all && found.length ?
|
|
39763
|
+
return opt.all && found.length ? resolve27(found) : reject(getNotFoundError(cmd));
|
|
39688
39764
|
const ppRaw = pathEnv[i];
|
|
39689
39765
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
39690
39766
|
const pCmd = path3.join(pathPart, cmd);
|
|
39691
39767
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
39692
|
-
|
|
39768
|
+
resolve27(subStep(p, i, 0));
|
|
39693
39769
|
});
|
|
39694
|
-
const subStep = (p, i, ii) => new Promise((
|
|
39770
|
+
const subStep = (p, i, ii) => new Promise((resolve27, reject) => {
|
|
39695
39771
|
if (ii === pathExt.length)
|
|
39696
|
-
return
|
|
39772
|
+
return resolve27(step(i + 1));
|
|
39697
39773
|
const ext = pathExt[ii];
|
|
39698
39774
|
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
|
|
39699
39775
|
if (!er && is) {
|
|
39700
39776
|
if (opt.all)
|
|
39701
39777
|
found.push(p + ext);
|
|
39702
39778
|
else
|
|
39703
|
-
return
|
|
39779
|
+
return resolve27(p + ext);
|
|
39704
39780
|
}
|
|
39705
|
-
return
|
|
39781
|
+
return resolve27(subStep(p, i, ii + 1));
|
|
39706
39782
|
});
|
|
39707
39783
|
});
|
|
39708
39784
|
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
@@ -40029,7 +40105,7 @@ function groupOf(state) {
|
|
|
40029
40105
|
}
|
|
40030
40106
|
}
|
|
40031
40107
|
function missionGroup(mission) {
|
|
40032
|
-
return
|
|
40108
|
+
return waitsOnDecision(mission) ? "waiting" : groupOf(mission.state);
|
|
40033
40109
|
}
|
|
40034
40110
|
function isSelectable(row2) {
|
|
40035
40111
|
return row2.kind === "node" || row2.kind === "mission" || row2.kind === "member" || row2.kind === "session" || row2.kind === "tasks" || row2.kind === "subagent" || row2.kind === "task" && row2.task.transcript;
|
|
@@ -45193,8 +45269,8 @@ var require_react_reconciler_production = __commonJS({
|
|
|
45193
45269
|
currentEntangledActionThenable = {
|
|
45194
45270
|
status: "pending",
|
|
45195
45271
|
value: void 0,
|
|
45196
|
-
then: function(
|
|
45197
|
-
entangledListeners.push(
|
|
45272
|
+
then: function(resolve27) {
|
|
45273
|
+
entangledListeners.push(resolve27);
|
|
45198
45274
|
}
|
|
45199
45275
|
};
|
|
45200
45276
|
}
|
|
@@ -45217,8 +45293,8 @@ var require_react_reconciler_production = __commonJS({
|
|
|
45217
45293
|
status: "pending",
|
|
45218
45294
|
value: null,
|
|
45219
45295
|
reason: null,
|
|
45220
|
-
then: function(
|
|
45221
|
-
listeners.push(
|
|
45296
|
+
then: function(resolve27) {
|
|
45297
|
+
listeners.push(resolve27);
|
|
45222
45298
|
}
|
|
45223
45299
|
};
|
|
45224
45300
|
thenable.then(
|
|
@@ -61894,8 +61970,8 @@ var init_ink = __esm({
|
|
|
61894
61970
|
}
|
|
61895
61971
|
}
|
|
61896
61972
|
async waitUntilExit() {
|
|
61897
|
-
this.exitPromise ||= new Promise((
|
|
61898
|
-
this.resolveExitPromise =
|
|
61973
|
+
this.exitPromise ||= new Promise((resolve27, reject) => {
|
|
61974
|
+
this.resolveExitPromise = resolve27;
|
|
61899
61975
|
this.rejectExitPromise = reject;
|
|
61900
61976
|
});
|
|
61901
61977
|
if (!this.beforeExitHandler) {
|
|
@@ -63219,8 +63295,8 @@ var init_chat = __esm({
|
|
|
63219
63295
|
ask(question, policy) {
|
|
63220
63296
|
if (this.closed) return Promise.resolve(null);
|
|
63221
63297
|
const id = this.nextAsk++;
|
|
63222
|
-
return new Promise((
|
|
63223
|
-
this.answers.set(id,
|
|
63298
|
+
return new Promise((resolve27) => {
|
|
63299
|
+
this.answers.set(id, resolve27);
|
|
63224
63300
|
this.set({ asks: [...this.state.asks, { id, question, policy }] });
|
|
63225
63301
|
});
|
|
63226
63302
|
}
|
|
@@ -64688,6 +64764,7 @@ var init_tui = __esm({
|
|
|
64688
64764
|
|
|
64689
64765
|
// src/cli.ts
|
|
64690
64766
|
init_command();
|
|
64767
|
+
import { resolve as resolve26 } from "node:path";
|
|
64691
64768
|
|
|
64692
64769
|
// src/core/config/env.ts
|
|
64693
64770
|
init_command();
|
|
@@ -65791,7 +65868,7 @@ function installPlugin(home, ref, exec4 = run) {
|
|
|
65791
65868
|
const installed = Object.keys(JSON.parse(readFileSync7(join10(scratch, "package.json"), "utf8")).dependencies ?? {});
|
|
65792
65869
|
const pkgDir = join10("node_modules", ...(installed[0] ?? ref.pkg.replace(/(.)@[^/]*$/, "$1")).split("/"));
|
|
65793
65870
|
const inner = PluginPackageSchema.parse(JSON.parse(readFileSync7(join10(scratch, pkgDir, PLUGIN_PACKAGE_FILE), "utf8")));
|
|
65794
|
-
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)}
|
|
65871
|
+
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)}
|
|
65795
65872
|
`);
|
|
65796
65873
|
break;
|
|
65797
65874
|
}
|
|
@@ -65857,15 +65934,22 @@ function ensurePluginInstalled(home, name, source, exec4 = run) {
|
|
|
65857
65934
|
function pluginsDir(home) {
|
|
65858
65935
|
return join11(home, "plugins");
|
|
65859
65936
|
}
|
|
65937
|
+
function isLocalSource(source) {
|
|
65938
|
+
return source.startsWith(".") || isAbsolute5(source);
|
|
65939
|
+
}
|
|
65940
|
+
function pluginDir(name, source, roots) {
|
|
65941
|
+
if (!isLocalSource(source)) return join11(pluginsDir(roots.home), name);
|
|
65942
|
+
return isAbsolute5(source) ? source : resolve7(roots.harnessRoot ?? process.cwd(), source);
|
|
65943
|
+
}
|
|
65860
65944
|
function commandOf(name, source, roots, entryEnv = []) {
|
|
65861
65945
|
if (source.startsWith("exec:")) {
|
|
65862
65946
|
const [command2, ...args2] = source.slice("exec:".length).trim().split(/\s+/);
|
|
65863
65947
|
if (!command2) throw new Error(`The plugin ${name}'s source "${source}" names no command.`);
|
|
65864
|
-
return { command: command2, args: args2, cwd: roots.harnessRoot ?? process.cwd(), env: [...entryEnv], defaults: {}, harnesses: null };
|
|
65948
|
+
return { command: command2, args: args2, cwd: roots.harnessRoot ?? process.cwd(), env: [...entryEnv], defaults: {}, harnesses: null, commands: null };
|
|
65865
65949
|
}
|
|
65866
65950
|
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.`);
|
|
65867
|
-
const local =
|
|
65868
|
-
const dir =
|
|
65951
|
+
const local = isLocalSource(source);
|
|
65952
|
+
const dir = pluginDir(name, source, roots);
|
|
65869
65953
|
const descriptor = join11(dir, PLUGIN_PACKAGE_FILE);
|
|
65870
65954
|
if (!local && !existsSync7(descriptor) && /^(npm|git):/.test(source)) ensurePluginInstalled(roots.home, name, source);
|
|
65871
65955
|
if (!existsSync7(descriptor)) {
|
|
@@ -65873,7 +65957,7 @@ function commandOf(name, source, roots, entryEnv = []) {
|
|
|
65873
65957
|
}
|
|
65874
65958
|
const plugin = PluginPackageSchema.parse(JSON.parse(readFileSync8(descriptor, "utf8")));
|
|
65875
65959
|
const [command, ...args] = plugin.command;
|
|
65876
|
-
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 };
|
|
65960
|
+
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 };
|
|
65877
65961
|
}
|
|
65878
65962
|
var PLUGIN_ENV = [
|
|
65879
65963
|
"PATH",
|
|
@@ -65918,6 +66002,15 @@ function folderMismatch(manifest, folders) {
|
|
|
65918
66002
|
const extra = folders.find((one) => !manifest.provides.harnesses.some((harness) => harness.id === one.id && harness.folder !== null));
|
|
65919
66003
|
return extra ? `declares the harness ${extra.id} in its ${PLUGIN_PACKAGE_FILE}, and its harnesses port serves no folder for it` : null;
|
|
65920
66004
|
}
|
|
66005
|
+
function commandMismatch(manifest, declared) {
|
|
66006
|
+
for (const command of manifest.commands) {
|
|
66007
|
+
const same2 = declared.find((one) => one.name === command.name);
|
|
66008
|
+
if (!same2) return `serves the command ${command.name}, and its ${PLUGIN_PACKAGE_FILE} declares no command ${command.name}`;
|
|
66009
|
+
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`;
|
|
66010
|
+
}
|
|
66011
|
+
const extra = declared.find((one) => !manifest.commands.some((command) => command.name === one.name));
|
|
66012
|
+
return extra ? `declares the command ${extra.name} in its ${PLUGIN_PACKAGE_FILE}, and serves no command ${extra.name}` : null;
|
|
66013
|
+
}
|
|
65921
66014
|
function manifestEnv(manifest) {
|
|
65922
66015
|
return manifest.permissions.secrets.filter((name) => PLUGIN_ENV_NAME.test(name));
|
|
65923
66016
|
}
|
|
@@ -65946,15 +66039,16 @@ async function startRemotePlugin(options) {
|
|
|
65946
66039
|
let reconnecting = false;
|
|
65947
66040
|
const connect = async () => {
|
|
65948
66041
|
const start = commandOf(options.name, options.source, options.roots, options.entryEnv);
|
|
65949
|
-
const
|
|
66042
|
+
const terminal2 = options.terminal ? [options.terminal.input, options.terminal.output, options.terminal.error] : [];
|
|
66043
|
+
const child = spawn2(start.command, start.args, { cwd: start.cwd, env: pluginProcessEnv(options.env, start.env), stdio: ["pipe", "pipe", "pipe", ...terminal2] });
|
|
65950
66044
|
try {
|
|
65951
|
-
return await initialize(child, start.env, start.defaults, start.harnesses);
|
|
66045
|
+
return await initialize(child, start.env, start.defaults, start.harnesses, start.commands);
|
|
65952
66046
|
} catch (error62) {
|
|
65953
66047
|
child.kill("SIGKILL");
|
|
65954
66048
|
throw error62;
|
|
65955
66049
|
}
|
|
65956
66050
|
};
|
|
65957
|
-
const initialize = async (child, env3, defaults, folders
|
|
66051
|
+
const initialize = async (child, env3, defaults, folders, commands) => {
|
|
65958
66052
|
const peer = new RpcPeer({ send: (line) => child.stdin?.write(`${line}
|
|
65959
66053
|
`) });
|
|
65960
66054
|
createInterface2({ input: child.stdout }).on("line", (line) => peer.receive(line));
|
|
@@ -65989,6 +66083,10 @@ async function startRemotePlugin(options) {
|
|
|
65989
66083
|
const where2 = options.source.startsWith("exec:") ? `its ${ORBITAL_FILE} entry` : `its ${PLUGIN_PACKAGE_FILE}`;
|
|
65990
66084
|
throw new Error(`The plugin ${options.name} declares the variables [${declared.join(", ")}] as secrets, and ${where2} declares env: [${env3.join(", ")}]. Make them the same.`);
|
|
65991
66085
|
}
|
|
66086
|
+
if (commands) {
|
|
66087
|
+
const mismatch = commandMismatch(manifest2, commands);
|
|
66088
|
+
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.`);
|
|
66089
|
+
}
|
|
65992
66090
|
if (options.offline && manifest2.permissions.network.length > 0) {
|
|
65993
66091
|
throw new Error(`The plugin ${options.name} reaches ${manifest2.permissions.network.join(", ")}, and ${ORBITAL_FILE} says offline: true.`);
|
|
65994
66092
|
}
|
|
@@ -66174,7 +66272,7 @@ async function startRemotePlugin(options) {
|
|
|
66174
66272
|
endpoint = given;
|
|
66175
66273
|
void current2.then((connection) => connection.peer.notify(PLUGIN_METHODS.hostEndpoint, given));
|
|
66176
66274
|
},
|
|
66177
|
-
command: async (
|
|
66275
|
+
command: async (request) => PluginCommandResultSchema.parse(await call(PLUGIN_METHODS.commandRun, request)),
|
|
66178
66276
|
close: async () => {
|
|
66179
66277
|
stopped = true;
|
|
66180
66278
|
const connection = await current2.catch(() => null);
|
|
@@ -67547,7 +67645,7 @@ var readBodyDirect = (request) => {
|
|
|
67547
67645
|
request[bodyBufferKey] = buffered;
|
|
67548
67646
|
return Promise.resolve(buffered);
|
|
67549
67647
|
}
|
|
67550
|
-
const promise2 = new Promise((
|
|
67648
|
+
const promise2 = new Promise((resolve27, reject) => {
|
|
67551
67649
|
const chunks = [];
|
|
67552
67650
|
let settled = false;
|
|
67553
67651
|
const finish = (callback) => {
|
|
@@ -67565,7 +67663,7 @@ var readBodyDirect = (request) => {
|
|
|
67565
67663
|
else if (recovered === void 0) reject(error62 ?? normalizeAbortError(request, incoming));
|
|
67566
67664
|
else {
|
|
67567
67665
|
request[bodyBufferKey] = recovered;
|
|
67568
|
-
|
|
67666
|
+
resolve27(recovered);
|
|
67569
67667
|
}
|
|
67570
67668
|
});
|
|
67571
67669
|
return true;
|
|
@@ -67577,7 +67675,7 @@ var readBodyDirect = (request) => {
|
|
|
67577
67675
|
finish(() => {
|
|
67578
67676
|
const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
|
|
67579
67677
|
request[bodyBufferKey] = buffer;
|
|
67580
|
-
|
|
67678
|
+
resolve27(buffer);
|
|
67581
67679
|
});
|
|
67582
67680
|
};
|
|
67583
67681
|
const onError = (error62) => {
|
|
@@ -68124,7 +68222,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
68124
68222
|
});
|
|
68125
68223
|
if (!chunk) {
|
|
68126
68224
|
if (i === 1) {
|
|
68127
|
-
await new Promise((
|
|
68225
|
+
await new Promise((resolve27) => setTimeout(resolve27));
|
|
68128
68226
|
maxReadCount = 3;
|
|
68129
68227
|
continue;
|
|
68130
68228
|
}
|
|
@@ -70393,9 +70491,9 @@ data:
|
|
|
70393
70491
|
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
70394
70492
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
70395
70493
|
if (this._enableJsonResponse) {
|
|
70396
|
-
return new Promise((
|
|
70494
|
+
return new Promise((resolve27) => {
|
|
70397
70495
|
this._streamMapping.set(streamId, {
|
|
70398
|
-
resolveJson:
|
|
70496
|
+
resolveJson: resolve27,
|
|
70399
70497
|
cleanup: () => {
|
|
70400
70498
|
this._streamMapping.delete(streamId);
|
|
70401
70499
|
}
|
|
@@ -76770,7 +76868,7 @@ var Protocol = class {
|
|
|
76770
76868
|
return;
|
|
76771
76869
|
}
|
|
76772
76870
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
76773
|
-
await new Promise((
|
|
76871
|
+
await new Promise((resolve27) => setTimeout(resolve27, pollInterval));
|
|
76774
76872
|
options?.signal?.throwIfAborted();
|
|
76775
76873
|
}
|
|
76776
76874
|
} catch (error62) {
|
|
@@ -76787,7 +76885,7 @@ var Protocol = class {
|
|
|
76787
76885
|
*/
|
|
76788
76886
|
request(request, resultSchema, options) {
|
|
76789
76887
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
76790
|
-
return new Promise((
|
|
76888
|
+
return new Promise((resolve27, reject) => {
|
|
76791
76889
|
const earlyReject = (error62) => {
|
|
76792
76890
|
reject(error62);
|
|
76793
76891
|
};
|
|
@@ -76865,7 +76963,7 @@ var Protocol = class {
|
|
|
76865
76963
|
if (!parseResult.success) {
|
|
76866
76964
|
reject(parseResult.error);
|
|
76867
76965
|
} else {
|
|
76868
|
-
|
|
76966
|
+
resolve27(parseResult.data);
|
|
76869
76967
|
}
|
|
76870
76968
|
} catch (error62) {
|
|
76871
76969
|
reject(error62);
|
|
@@ -77127,12 +77225,12 @@ var Protocol = class {
|
|
|
77127
77225
|
}
|
|
77128
77226
|
} catch {
|
|
77129
77227
|
}
|
|
77130
|
-
return new Promise((
|
|
77228
|
+
return new Promise((resolve27, reject) => {
|
|
77131
77229
|
if (signal.aborted) {
|
|
77132
77230
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
77133
77231
|
return;
|
|
77134
77232
|
}
|
|
77135
|
-
const timeoutId = setTimeout(
|
|
77233
|
+
const timeoutId = setTimeout(resolve27, interval);
|
|
77136
77234
|
signal.addEventListener("abort", () => {
|
|
77137
77235
|
clearTimeout(timeoutId);
|
|
77138
77236
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -78273,7 +78371,7 @@ var McpServer = class {
|
|
|
78273
78371
|
let task = createTaskResult.task;
|
|
78274
78372
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
78275
78373
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
78276
|
-
await new Promise((
|
|
78374
|
+
await new Promise((resolve27) => setTimeout(resolve27, pollInterval));
|
|
78277
78375
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
78278
78376
|
if (!updatedTask) {
|
|
78279
78377
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -78998,14 +79096,14 @@ function claudeService(env3) {
|
|
|
78998
79096
|
if (!dir) return SERVICE;
|
|
78999
79097
|
return `${SERVICE}-${createHash4("sha256").update(dir.normalize("NFC")).digest("hex").slice(0, 8)}`;
|
|
79000
79098
|
}
|
|
79001
|
-
var findWithSecurity = (args) => new Promise((
|
|
79099
|
+
var findWithSecurity = (args) => new Promise((resolve27) => {
|
|
79002
79100
|
const child = spawn3(SECURITY, [...args], { stdio: ["ignore", "pipe", "ignore"], timeout: SECURITY_TIMEOUT_MS });
|
|
79003
79101
|
let stdout = "";
|
|
79004
79102
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
79005
79103
|
stdout += chunk;
|
|
79006
79104
|
});
|
|
79007
|
-
child.once("error", () =>
|
|
79008
|
-
child.once("close", (code) =>
|
|
79105
|
+
child.once("error", () => resolve27({ status: 1, stdout: "" }));
|
|
79106
|
+
child.once("close", (code) => resolve27({ status: code ?? 1, stdout }));
|
|
79009
79107
|
});
|
|
79010
79108
|
function keychainRead(account, service, find = findWithSecurity) {
|
|
79011
79109
|
return async () => {
|
|
@@ -79026,7 +79124,7 @@ function renewEnv(env3) {
|
|
|
79026
79124
|
return Object.fromEntries(Object.entries(env3).filter((entry) => entry[1] !== void 0 && !KEPT_OUT.includes(entry[0])));
|
|
79027
79125
|
}
|
|
79028
79126
|
function claudeRenewal(binary, env3) {
|
|
79029
|
-
return () => new Promise((
|
|
79127
|
+
return () => new Promise((resolve27, reject) => {
|
|
79030
79128
|
const cwd2 = mkdtempSync(join16(tmpdir(), "orbital-claude-renew-"));
|
|
79031
79129
|
const child = spawn3(binary, RENEW_ARGS, { cwd: cwd2, env: renewEnv(env3), stdio: ["ignore", "ignore", "pipe"], timeout: RENEW_TIMEOUT_MS });
|
|
79032
79130
|
let stderr = "";
|
|
@@ -79036,7 +79134,7 @@ function claudeRenewal(binary, env3) {
|
|
|
79036
79134
|
const done = (error62) => {
|
|
79037
79135
|
rmSync5(cwd2, { recursive: true, force: true });
|
|
79038
79136
|
if (error62) reject(error62);
|
|
79039
|
-
else
|
|
79137
|
+
else resolve27();
|
|
79040
79138
|
};
|
|
79041
79139
|
child.once("error", done);
|
|
79042
79140
|
child.once("close", (code) => done(code === 0 ? null : new Error(`claude exited ${code ?? "on a signal"}${stderr.trim() ? `: ${stderr.trim().split("\n")[0]}` : ""}`)));
|
|
@@ -80325,7 +80423,7 @@ function missionOfRow(row2) {
|
|
|
80325
80423
|
result: textOf(row2, "result"),
|
|
80326
80424
|
question: textOf(row2, "question"),
|
|
80327
80425
|
questions: questionsOf(row2),
|
|
80328
|
-
|
|
80426
|
+
receivers: [],
|
|
80329
80427
|
lead: textOf(row2, "lead"),
|
|
80330
80428
|
lastActivity: textOf(row2, "activity"),
|
|
80331
80429
|
claim: textOf(row2, "claim"),
|
|
@@ -80479,7 +80577,7 @@ var MissionStore = class {
|
|
|
80479
80577
|
source: request.source !== void 0 ? request.source : known?.source ?? null,
|
|
80480
80578
|
result: request.result !== void 0 ? request.result : known?.result ?? null,
|
|
80481
80579
|
question: request.question !== void 0 ? request.question : request.state === "waiting" ? known?.question ?? null : null,
|
|
80482
|
-
|
|
80580
|
+
receivers: request.receivers !== void 0 ? request.receivers : known?.receivers ?? [],
|
|
80483
80581
|
lead: request.lead !== void 0 ? request.lead : known?.lead ?? null,
|
|
80484
80582
|
claim: request.claim !== void 0 ? request.claim : known?.claim ?? null,
|
|
80485
80583
|
claimedAt: request.claimedAt !== void 0 ? request.claimedAt : known?.claimedAt ?? null,
|
|
@@ -80534,7 +80632,7 @@ function missionRecord(node2, mission, declared, stored) {
|
|
|
80534
80632
|
result: stored?.result ?? null,
|
|
80535
80633
|
question: stored?.question ?? null,
|
|
80536
80634
|
questions: stored?.questions ?? [],
|
|
80537
|
-
|
|
80635
|
+
receivers: stored?.receivers ?? [],
|
|
80538
80636
|
lead: stored?.lead ?? null,
|
|
80539
80637
|
lastActivity: null,
|
|
80540
80638
|
claim: stored?.claim ?? null,
|
|
@@ -80642,9 +80740,9 @@ var Telemetry = class {
|
|
|
80642
80740
|
async drain(timeoutMs) {
|
|
80643
80741
|
if (this.queues.every((queue) => !queue.running && queue.items.length === 0)) return true;
|
|
80644
80742
|
let done = null;
|
|
80645
|
-
const empty = new Promise((
|
|
80743
|
+
const empty = new Promise((resolve27) => {
|
|
80646
80744
|
done = () => {
|
|
80647
|
-
if (this.queues.every((queue) => !queue.running && queue.items.length === 0))
|
|
80745
|
+
if (this.queues.every((queue) => !queue.running && queue.items.length === 0)) resolve27(true);
|
|
80648
80746
|
};
|
|
80649
80747
|
this.drained.add(done);
|
|
80650
80748
|
});
|
|
@@ -80897,7 +80995,7 @@ var SessionHost = class {
|
|
|
80897
80995
|
transition: missionTransition(before?.state ?? "open", after.state, after.question),
|
|
80898
80996
|
reason: change?.note ?? null,
|
|
80899
80997
|
by: change?.by ?? "node",
|
|
80900
|
-
|
|
80998
|
+
receivers: after.receivers,
|
|
80901
80999
|
sessionId: after.sessionId,
|
|
80902
81000
|
lead: after.lead,
|
|
80903
81001
|
question: after.question,
|
|
@@ -80905,7 +81003,7 @@ var SessionHost = class {
|
|
|
80905
81003
|
});
|
|
80906
81004
|
}
|
|
80907
81005
|
missionWritten(mission, team, by) {
|
|
80908
|
-
this.telemetry.emit({ kind: "mission.state", mission, team, from: null, to: "open", transition: "new", reason: "written", by,
|
|
81006
|
+
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 });
|
|
80909
81007
|
}
|
|
80910
81008
|
async snapshot() {
|
|
80911
81009
|
const definition = await this.definitionOf(this.options.defaultWorkspace);
|
|
@@ -80934,7 +81032,7 @@ var SessionHost = class {
|
|
|
80934
81032
|
retiring: note?.retiring ?? null
|
|
80935
81033
|
};
|
|
80936
81034
|
}), ...[...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 }))],
|
|
80937
|
-
missions: missions.map((record2) => ({ missionId: record2.missionId, team: record2.team, state: record2.state, sessionId: record2.sessionId, lead: record2.lead,
|
|
81035
|
+
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 })),
|
|
80938
81036
|
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 })),
|
|
80939
81037
|
jobs: [...this.openJobs].map(([jobId, open2]) => ({ ...open2.context, jobId, since: new Date(open2.since).toISOString(), via: open2.via })),
|
|
80940
81038
|
tokens: this.telemetry.tokens(),
|
|
@@ -80988,19 +81086,48 @@ var SessionHost = class {
|
|
|
80988
81086
|
this.redeliverQuestions(id);
|
|
80989
81087
|
return info;
|
|
80990
81088
|
}
|
|
80991
|
-
redeliverQuestions(
|
|
81089
|
+
redeliverQuestions(sessionId) {
|
|
81090
|
+
const session = this.sessions.get(sessionId);
|
|
81091
|
+
if (!session) return;
|
|
80992
81092
|
for (const stored of this.missions.all()) {
|
|
80993
|
-
if (
|
|
80994
|
-
|
|
80995
|
-
for (const asked2 of stored.questions) {
|
|
80996
|
-
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);
|
|
80997
|
-
}
|
|
81093
|
+
if (!stored.receivers.some((key2) => this.receives(session, key2))) continue;
|
|
81094
|
+
for (const open2 of this.openQuestions(stored)) if (!this.questionOpen(sessionId, open2.mission, open2.questionId)) this.deliverQuestion(sessionId, open2, stored.sessionId);
|
|
80998
81095
|
}
|
|
80999
81096
|
}
|
|
81000
|
-
|
|
81001
|
-
if (
|
|
81097
|
+
openQuestions(stored) {
|
|
81098
|
+
if (!stored.sessionId || stored.state === "paused" || !hasLead(stored.state)) return [];
|
|
81099
|
+
const base2 = { team: stored.team, mission: stored.mission, lead: stored.lead };
|
|
81100
|
+
return [
|
|
81101
|
+
...stored.state === "waiting" ? [{ ...base2, question: stored.question ?? NO_HANDOFF_TELL, questionId: null }] : [],
|
|
81102
|
+
...stored.questions.map((asked2) => ({ ...base2, question: asked2.question, questionId: asked2.id }))
|
|
81103
|
+
];
|
|
81104
|
+
}
|
|
81105
|
+
receiverKey(session) {
|
|
81106
|
+
return session.external?.externalId ? externalReceiver(session.external.externalId) : session.id;
|
|
81107
|
+
}
|
|
81108
|
+
receives(session, key2) {
|
|
81109
|
+
return key2 === session.id || key2 === this.receiverKey(session);
|
|
81110
|
+
}
|
|
81111
|
+
liveReceivers(receivers, except = null) {
|
|
81112
|
+
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)));
|
|
81113
|
+
}
|
|
81114
|
+
keyOfRemote(receiver) {
|
|
81115
|
+
if (receiver.node !== this.node) return receiverRef(receiver);
|
|
81116
|
+
const session = this.sessions.get(receiver.sessionId);
|
|
81117
|
+
return session ? this.receiverKey(session) : receiver.sessionId;
|
|
81118
|
+
}
|
|
81119
|
+
starterKey(by) {
|
|
81120
|
+
if (!by.startsWith("session:")) return null;
|
|
81121
|
+
const named2 = by.slice("session:".length);
|
|
81122
|
+
const remote = remoteReceiverOf(named2);
|
|
81123
|
+
if (remote) return this.keyOfRemote(remote);
|
|
81124
|
+
const session = this.sessions.get(named2);
|
|
81125
|
+
return session ? this.receiverKey(session) : named2;
|
|
81126
|
+
}
|
|
81127
|
+
questionOpen(sessionId, mission, questionId) {
|
|
81128
|
+
if (this.questionsInFlight.has(`${sessionId}:${mission}:${questionId ?? ""}`)) return true;
|
|
81002
81129
|
for (const [taskId, asked2] of [...this.questionDeliveries]) {
|
|
81003
|
-
if (asked2.mission !== mission || asked2.questionId !== questionId || this.taskSession.get(taskId) !==
|
|
81130
|
+
if (asked2.mission !== mission || asked2.questionId !== questionId || this.taskSession.get(taskId) !== sessionId) continue;
|
|
81004
81131
|
const task = this.tasks.get(taskId);
|
|
81005
81132
|
if (task && !(task.status && isTerminal(task.status.state))) return true;
|
|
81006
81133
|
this.questionDeliveries.delete(taskId);
|
|
@@ -81032,7 +81159,7 @@ var SessionHost = class {
|
|
|
81032
81159
|
async settleExternal(sessionId, settle2) {
|
|
81033
81160
|
const session = this.sessions.get(sessionId);
|
|
81034
81161
|
if (!session?.external) throw new SessionHostError("not-found", `There is no attached session ${sessionId} on this node.`);
|
|
81035
|
-
const taskId = settle2.taskId ?? session.running;
|
|
81162
|
+
const taskId = settle2.taskId ?? session.running ?? session.external.open.keys().next().value ?? null;
|
|
81036
81163
|
const task = taskId ? this.tasks.get(taskId) : void 0;
|
|
81037
81164
|
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.`);
|
|
81038
81165
|
await this.finish(sessionId, task, "TASK_STATE_COMPLETED", settle2.text);
|
|
@@ -81066,23 +81193,21 @@ var SessionHost = class {
|
|
|
81066
81193
|
await this.finish(session.id, task, "TASK_STATE_FAILED", `The attached session ${session.id} is detached.`);
|
|
81067
81194
|
return;
|
|
81068
81195
|
}
|
|
81069
|
-
|
|
81196
|
+
const asked2 = this.questionOf(message);
|
|
81197
|
+
if (asked2 === null) session.running = task.id;
|
|
81070
81198
|
await this.update(session.id, task, "TASK_STATE_WORKING");
|
|
81071
81199
|
const fromAgent = message.metadata?.[ORBITAL_META.fromAgent];
|
|
81072
81200
|
const fromSession = message.metadata?.[ORBITAL_META.fromSession];
|
|
81073
|
-
const asked2 = this.questionOf(message);
|
|
81074
81201
|
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) };
|
|
81075
|
-
const settled = new Promise((
|
|
81202
|
+
const settled = new Promise((resolve27) => this.externalWaits.set(task.id, resolve27)).finally(() => {
|
|
81203
|
+
this.externalWaits.delete(task.id);
|
|
81204
|
+
external.open.delete(task.id);
|
|
81205
|
+
if (session.running === task.id) session.running = null;
|
|
81206
|
+
});
|
|
81076
81207
|
if (asked2 !== null) external.open.set(task.id, delivery);
|
|
81077
81208
|
if (external.listeners.size === 0) external.waiting.push(delivery);
|
|
81078
81209
|
for (const listener of external.listeners) listener(delivery);
|
|
81079
|
-
|
|
81080
|
-
await settled;
|
|
81081
|
-
} finally {
|
|
81082
|
-
this.externalWaits.delete(task.id);
|
|
81083
|
-
external.open.delete(task.id);
|
|
81084
|
-
session.running = null;
|
|
81085
|
-
}
|
|
81210
|
+
if (asked2 === null) await settled;
|
|
81086
81211
|
}
|
|
81087
81212
|
stopFollowing() {
|
|
81088
81213
|
this.following.abort();
|
|
@@ -81097,9 +81222,9 @@ var SessionHost = class {
|
|
|
81097
81222
|
if (this.followingHub) return;
|
|
81098
81223
|
let listener = null;
|
|
81099
81224
|
try {
|
|
81100
|
-
await Promise.race([this.settled(), new Promise((
|
|
81101
|
-
listener =
|
|
81102
|
-
this.followListeners.add(
|
|
81225
|
+
await Promise.race([this.settled(), new Promise((resolve27) => {
|
|
81226
|
+
listener = resolve27;
|
|
81227
|
+
this.followListeners.add(resolve27);
|
|
81103
81228
|
})]);
|
|
81104
81229
|
} finally {
|
|
81105
81230
|
if (listener) this.followListeners.delete(listener);
|
|
@@ -81190,7 +81315,7 @@ var SessionHost = class {
|
|
|
81190
81315
|
const leadLives = known?.sessionId ? this.sessions.has(known.sessionId) : false;
|
|
81191
81316
|
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}.`);
|
|
81192
81317
|
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.`);
|
|
81193
|
-
if (known?.state === "waiting" && leadLives) throw new SessionHostError("invalid", `The mission ${missionId} waits for
|
|
81318
|
+
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.`);
|
|
81194
81319
|
const ended = known && hasLead(known.state) && !leadLives ? `the lead session ${known.sessionId ?? "(none)"} ended with its node` : null;
|
|
81195
81320
|
if (known?.state === "done") throw new SessionHostError("invalid", `The mission ${missionId} is done. Give the team a new mission for new work.`);
|
|
81196
81321
|
const claim2 = declared?.source ?? missionId;
|
|
@@ -81201,12 +81326,13 @@ var SessionHost = class {
|
|
|
81201
81326
|
const resumable = ended && known?.sessionId && readSessionRecord(this.paths.sessionsDir, known.sessionId) !== null ? known.sessionId : null;
|
|
81202
81327
|
if (resumable) {
|
|
81203
81328
|
const task2 = await this.submit({ sessionId: resumable, message });
|
|
81204
|
-
|
|
81329
|
+
const starter = this.starterKey(by);
|
|
81330
|
+
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] : []])] });
|
|
81205
81331
|
return { task: task2, duplicate: false };
|
|
81206
81332
|
}
|
|
81207
81333
|
const task = await this.submit({ message, start: { agent: team, workspaceDir, team, mission } });
|
|
81208
81334
|
const takenOver = held2.map(claimHolder);
|
|
81209
|
-
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: [],
|
|
81335
|
+
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 });
|
|
81210
81336
|
return { task, duplicate: false };
|
|
81211
81337
|
}
|
|
81212
81338
|
pauseInterrupted() {
|
|
@@ -81309,7 +81435,7 @@ var SessionHost = class {
|
|
|
81309
81435
|
for (const { relay: port } of this.options.ports.missionRelays()) {
|
|
81310
81436
|
const answered = await port.reply({ missionId, text: text3, ...questionId && { questionId }, by, ...this.claimOfBy(by) });
|
|
81311
81437
|
if (!answered) continue;
|
|
81312
|
-
await this.closeQuestions(missionId, questionId ?? void 0,
|
|
81438
|
+
await this.closeQuestions(missionId, questionId ?? void 0, by, text3);
|
|
81313
81439
|
return answered;
|
|
81314
81440
|
}
|
|
81315
81441
|
}
|
|
@@ -81320,14 +81446,14 @@ var SessionHost = class {
|
|
|
81320
81446
|
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.`);
|
|
81321
81447
|
const lead = known.sessionId;
|
|
81322
81448
|
this.missions.change(missionId, { state: "active", by, note: "reply" });
|
|
81323
|
-
await this.closeQuestions(missionId, null,
|
|
81449
|
+
await this.closeQuestions(missionId, null, by, text3);
|
|
81324
81450
|
await this.submit({ sessionId: lead, message: { messageId: randomUUID4(), role: "ROLE_USER", parts: [textPart(`[reply to your question on the mission ${missionId}, from ${by}]
|
|
81325
81451
|
${text3}`)] } });
|
|
81326
81452
|
return this.missionOf(missionId);
|
|
81327
81453
|
}
|
|
81328
81454
|
async answerAsked(by, missionId, lead, asked2, text3) {
|
|
81329
81455
|
this.missions.answer(missionId, asked2.id);
|
|
81330
|
-
await this.closeQuestions(missionId, asked2.id,
|
|
81456
|
+
await this.closeQuestions(missionId, asked2.id, by, text3);
|
|
81331
81457
|
const session = this.sessions.get(lead);
|
|
81332
81458
|
if (session?.running && !session.external) {
|
|
81333
81459
|
this.steering.set(lead, [...this.steering.get(lead) ?? [], { from: by, missionId, text: `reply to your question ${asked2.id}: ${asked2.question}
|
|
@@ -81344,7 +81470,7 @@ ${text3}`)] } });
|
|
|
81344
81470
|
const missionId = given.mission;
|
|
81345
81471
|
const known = this.missions.get(missionId);
|
|
81346
81472
|
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.`);
|
|
81347
|
-
if (given.sessionId !== null && known.sessionId !== given.sessionId) throw new SessionHostError("invalid", `Only the lead session of the mission ${missionId} asks its
|
|
81473
|
+
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}.`);
|
|
81348
81474
|
this.askedInTurn.add(known.sessionId);
|
|
81349
81475
|
this.recordQuestion(missionId, known.sessionId, given.question);
|
|
81350
81476
|
return this.missionOf(missionId);
|
|
@@ -81352,8 +81478,8 @@ ${text3}`)] } });
|
|
|
81352
81478
|
recordQuestion(missionId, sessionId, question) {
|
|
81353
81479
|
const asked2 = { id: `q-${randomUUID4().slice(0, 8)}`, question, at: new Date(this.options.context.now()).toISOString() };
|
|
81354
81480
|
const after = this.missions.ask(missionId, asked2);
|
|
81355
|
-
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}`,
|
|
81356
|
-
this.
|
|
81481
|
+
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 });
|
|
81482
|
+
this.tellReceivers(after.receivers, { team: after.team, mission: missionId, lead: after.lead, question: asked2.question, questionId: asked2.id }, sessionId);
|
|
81357
81483
|
}
|
|
81358
81484
|
async say(by, target, text3, reader, relay = true) {
|
|
81359
81485
|
const named2 = this.missions.get(target);
|
|
@@ -81409,16 +81535,28 @@ ${text3}`)] } });
|
|
|
81409
81535
|
const mission = message.metadata?.[ORBITAL_META.mission];
|
|
81410
81536
|
return typeof mission === "string" ? mission : null;
|
|
81411
81537
|
}
|
|
81412
|
-
async closeQuestions(mission, questionId, text3) {
|
|
81538
|
+
async closeQuestions(mission, questionId, by, text3) {
|
|
81539
|
+
const answerer = by.startsWith("session:") ? by.slice("session:".length) : null;
|
|
81540
|
+
const told = /* @__PURE__ */ new Set();
|
|
81413
81541
|
for (const [taskId, asked2] of [...this.questionDeliveries]) {
|
|
81414
81542
|
if (asked2.mission !== mission || questionId !== void 0 && asked2.questionId !== questionId) continue;
|
|
81415
81543
|
this.questionDeliveries.delete(taskId);
|
|
81416
81544
|
const task = this.tasks.get(taskId);
|
|
81417
|
-
const
|
|
81418
|
-
if (!task || !
|
|
81419
|
-
await this.finish(
|
|
81545
|
+
const receiver = this.taskSession.get(taskId);
|
|
81546
|
+
if (!task || !receiver || task.status && isTerminal(task.status.state)) continue;
|
|
81547
|
+
await this.finish(receiver, task, "TASK_STATE_COMPLETED", `answered by ${by}: ${text3}`);
|
|
81420
81548
|
this.externalWaits.get(taskId)?.();
|
|
81549
|
+
if (receiver !== answerer) this.tellAnswered(receiver, { kind: "answered", mission, question: asked2.questionId, by, text: text3 }, told);
|
|
81421
81550
|
}
|
|
81551
|
+
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);
|
|
81552
|
+
}
|
|
81553
|
+
tellAnswered(sessionId, answered, told) {
|
|
81554
|
+
const external = this.sessions.get(sessionId)?.external;
|
|
81555
|
+
if (!external?.attached || told.has(sessionId)) return;
|
|
81556
|
+
told.add(sessionId);
|
|
81557
|
+
const answers = (delivery) => delivery.mission === answered.mission && (answered.question === null || delivery.question === answered.question);
|
|
81558
|
+
for (let index = external.waiting.length - 1; index >= 0; index -= 1) if (answers(external.waiting[index])) external.waiting.splice(index, 1);
|
|
81559
|
+
for (const listener of external.listeners) listener(answered);
|
|
81422
81560
|
}
|
|
81423
81561
|
busyBelow(sessionId) {
|
|
81424
81562
|
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);
|
|
@@ -81445,32 +81583,46 @@ ${text3}`)] } });
|
|
|
81445
81583
|
}
|
|
81446
81584
|
this.nudged.delete(sessionId);
|
|
81447
81585
|
this.missions.change(known.mission, { state: "waiting", by: `session:${sessionId}`, note: question === "" ? NO_HANDOFF_NOTE : stateWord(state), question: question === "" ? null : question });
|
|
81448
|
-
this.
|
|
81586
|
+
this.tellReceivers(known.receivers, { team: known.team, mission: known.mission, lead: known.lead, question: question === "" ? NO_HANDOFF_TELL : question, questionId: null }, sessionId);
|
|
81449
81587
|
}
|
|
81450
|
-
async joinMission(
|
|
81451
|
-
const session = this.sessions.get(sessionId);
|
|
81452
|
-
if (!session
|
|
81588
|
+
async joinMission(receiver, missionId) {
|
|
81589
|
+
const session = "sessionId" in receiver ? this.sessions.get(receiver.sessionId) : void 0;
|
|
81590
|
+
if ("sessionId" in receiver && !session) throw new SessionHostError("not-found", `There is no session ${receiver.sessionId} on this node.`);
|
|
81453
81591
|
const known = this.missions.get(missionId);
|
|
81454
|
-
if (!known) {
|
|
81592
|
+
if (!known && session) {
|
|
81455
81593
|
for (const { relay } of this.options.ports.missionRelays()) {
|
|
81456
|
-
const joined = await relay.join({ missionId,
|
|
81594
|
+
const joined = await relay.join({ missionId, receiver: { node: this.node, sessionId: session.id }, ...this.claimOfSession(session.id) });
|
|
81457
81595
|
if (joined) return joined;
|
|
81458
81596
|
}
|
|
81459
81597
|
}
|
|
81460
|
-
|
|
81598
|
+
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.`);
|
|
81599
|
+
if (!session) return this.joinHere(externalReceiver("externalId" in receiver ? receiver.externalId : ""), "cli", missionId, null);
|
|
81600
|
+
return this.joinHere(this.receiverKey(session), `session:${session.id}`, missionId, [session.id]);
|
|
81461
81601
|
}
|
|
81462
81602
|
async joinRemote(request) {
|
|
81463
|
-
|
|
81603
|
+
const key2 = this.keyOfRemote(request.receiver);
|
|
81604
|
+
const local = remoteReceiverOf(key2) === null ? [request.receiver.sessionId] : null;
|
|
81605
|
+
return this.joinHere(key2, `session:${receiverRef(request.receiver)}`, request.missionId, local);
|
|
81464
81606
|
}
|
|
81465
|
-
async
|
|
81466
|
-
const
|
|
81467
|
-
|
|
81607
|
+
async leaveMission(receiver, missionId) {
|
|
81608
|
+
const known = this.missions.get(missionId);
|
|
81609
|
+
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.`);
|
|
81610
|
+
const session = "sessionId" in receiver ? this.sessions.get(receiver.sessionId) : void 0;
|
|
81611
|
+
const leaves = (key2) => session ? this.receives(session, key2) : "sessionId" in receiver ? key2 === receiver.sessionId : externalIdOf(key2) === receiver.externalId;
|
|
81612
|
+
const kept = known.receivers.filter((key2) => !leaves(key2));
|
|
81613
|
+
if (kept.length === known.receivers.length) return this.missionOf(missionId);
|
|
81614
|
+
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 });
|
|
81615
|
+
return this.missionOf(missionId);
|
|
81616
|
+
}
|
|
81617
|
+
async joinHere(key2, by, missionId, deliverTo) {
|
|
81468
81618
|
const known = this.missions.get(missionId);
|
|
81469
81619
|
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.`);
|
|
81470
|
-
|
|
81471
|
-
|
|
81472
|
-
|
|
81473
|
-
|
|
81620
|
+
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] });
|
|
81621
|
+
const remote = remoteReceiverOf(key2);
|
|
81622
|
+
const live2 = this.liveReceivers([key2]).filter((session) => deliverTo === null || deliverTo.includes(session.id));
|
|
81623
|
+
for (const open2 of this.openQuestions(stored)) {
|
|
81624
|
+
if (remote && remote.node !== this.node) this.askRemote(remote, open2, stored.sessionId);
|
|
81625
|
+
for (const session of live2) if (!this.questionOpen(session.id, open2.mission, open2.questionId)) this.deliverQuestion(session.id, open2, stored.sessionId);
|
|
81474
81626
|
}
|
|
81475
81627
|
return this.missionOf(missionId);
|
|
81476
81628
|
}
|
|
@@ -81480,36 +81632,34 @@ ${text3}`)] } });
|
|
|
81480
81632
|
missionTeam(missionId) {
|
|
81481
81633
|
return this.missions.get(missionId)?.team ?? null;
|
|
81482
81634
|
}
|
|
81483
|
-
async
|
|
81484
|
-
const
|
|
81485
|
-
if (!
|
|
81486
|
-
this.deliverQuestion(
|
|
81635
|
+
async askReceiver(question) {
|
|
81636
|
+
const session = this.sessions.get(question.receiver.sessionId);
|
|
81637
|
+
if (!session || question.receiver.node !== this.node || this.liveReceivers([session.id]).length === 0) return false;
|
|
81638
|
+
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);
|
|
81487
81639
|
return true;
|
|
81488
81640
|
}
|
|
81489
|
-
|
|
81490
|
-
const
|
|
81491
|
-
|
|
81492
|
-
|
|
81493
|
-
if (remote && remote.node !== self) {
|
|
81494
|
-
const question = { missionId, node: self, lead: known.lead, leadSessionId: sessionId, question: known.question, ...known.questionId && { questionId: known.questionId }, giver: remote, team: known.team };
|
|
81495
|
-
void (async () => {
|
|
81496
|
-
for (const { relay } of this.options.ports.missionRelays()) if (await relay.ask(question)) return;
|
|
81497
|
-
this.options.context.output.warn(`no plugin carried the question of ${missionId} to ${known.giver}`);
|
|
81498
|
-
})().catch((error62) => this.options.context.output.warn(`could not tell the giver of ${missionId}: ${error62 instanceof Error ? error62.message : String(error62)}`));
|
|
81499
|
-
return;
|
|
81641
|
+
tellReceivers(receivers, open2, leadSessionId) {
|
|
81642
|
+
for (const key2 of receivers) {
|
|
81643
|
+
const remote = remoteReceiverOf(key2);
|
|
81644
|
+
if (remote && remote.node !== this.node) this.askRemote(remote, open2, leadSessionId);
|
|
81500
81645
|
}
|
|
81501
|
-
const
|
|
81502
|
-
|
|
81503
|
-
|
|
81646
|
+
for (const session of this.liveReceivers(receivers)) this.deliverQuestion(session.id, open2, leadSessionId);
|
|
81647
|
+
}
|
|
81648
|
+
askRemote(receiver, open2, leadSessionId) {
|
|
81649
|
+
const question = { missionId: open2.mission, node: this.node, lead: open2.lead, leadSessionId, question: open2.question, ...open2.questionId && { questionId: open2.questionId }, receiver, team: open2.team };
|
|
81650
|
+
void (async () => {
|
|
81651
|
+
for (const { relay } of this.options.ports.missionRelays()) if (await relay.ask(question)) return;
|
|
81652
|
+
this.options.context.output.warn(`no plugin carried the question of ${open2.mission} to ${receiverRef(receiver)}`);
|
|
81653
|
+
})().catch((error62) => this.options.context.output.warn(`could not tell ${receiverRef(receiver)} the question of ${open2.mission}: ${error62 instanceof Error ? error62.message : String(error62)}`));
|
|
81504
81654
|
}
|
|
81505
|
-
deliverQuestion(
|
|
81655
|
+
deliverQuestion(receiverId, known, sessionId) {
|
|
81506
81656
|
const missionId = known.mission;
|
|
81507
81657
|
const question = known.question;
|
|
81508
81658
|
const questionId = known.questionId;
|
|
81509
|
-
const key2 = `${
|
|
81659
|
+
const key2 = `${receiverId}:${missionId}:${questionId ?? ""}`;
|
|
81510
81660
|
this.questionsInFlight.add(key2);
|
|
81511
81661
|
void this.submit({
|
|
81512
|
-
sessionId:
|
|
81662
|
+
sessionId: receiverId,
|
|
81513
81663
|
message: {
|
|
81514
81664
|
messageId: randomUUID4(),
|
|
81515
81665
|
role: "ROLE_USER",
|
|
@@ -81524,7 +81674,7 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
81524
81674
|
}
|
|
81525
81675
|
}).then((task) => {
|
|
81526
81676
|
this.questionDeliveries.set(task.id, { mission: known.mission, questionId });
|
|
81527
|
-
}).catch((error62) => this.options.context.output.warn(`could not tell the
|
|
81677
|
+
}).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));
|
|
81528
81678
|
}
|
|
81529
81679
|
async missionActivity(missionId, limit) {
|
|
81530
81680
|
const sessionId = this.missions.get(missionId)?.sessionId;
|
|
@@ -81742,8 +81892,8 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
81742
81892
|
}
|
|
81743
81893
|
}
|
|
81744
81894
|
if (signal?.aborted) return;
|
|
81745
|
-
await new Promise((
|
|
81746
|
-
wake =
|
|
81895
|
+
await new Promise((resolve27) => {
|
|
81896
|
+
wake = resolve27;
|
|
81747
81897
|
});
|
|
81748
81898
|
wake = null;
|
|
81749
81899
|
}
|
|
@@ -81819,9 +81969,13 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
81819
81969
|
}
|
|
81820
81970
|
missionRoots(readerSessionId) {
|
|
81821
81971
|
const outside = this.sessions.get(readerSessionId)?.external !== void 0;
|
|
81822
|
-
const missionRoots = this.missions.all().filter((stored) => stored.sessionId !== null && (outside || stored.
|
|
81972
|
+
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));
|
|
81823
81973
|
return new Set(missionRoots);
|
|
81824
81974
|
}
|
|
81975
|
+
receivesFor(sessionId, receivers) {
|
|
81976
|
+
const session = this.sessions.get(sessionId);
|
|
81977
|
+
return session !== void 0 && receivers.some((key2) => this.receives(session, key2));
|
|
81978
|
+
}
|
|
81825
81979
|
reachRoots(readerSessionId) {
|
|
81826
81980
|
const outside = this.sessions.get(readerSessionId)?.external !== void 0;
|
|
81827
81981
|
const attachedRoots = outside ? [...this.sessions.values()].filter((one) => one.external?.attached === true).map((one) => this.rootOf(one.id)) : [];
|
|
@@ -83390,7 +83544,7 @@ var StdioClientTransport = class {
|
|
|
83390
83544
|
if (this._process) {
|
|
83391
83545
|
throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
|
83392
83546
|
}
|
|
83393
|
-
return new Promise((
|
|
83547
|
+
return new Promise((resolve27, reject) => {
|
|
83394
83548
|
this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], {
|
|
83395
83549
|
// merge default env with server env because mcp server needs some env vars
|
|
83396
83550
|
env: {
|
|
@@ -83407,7 +83561,7 @@ var StdioClientTransport = class {
|
|
|
83407
83561
|
this.onerror?.(error62);
|
|
83408
83562
|
});
|
|
83409
83563
|
this._process.on("spawn", () => {
|
|
83410
|
-
|
|
83564
|
+
resolve27();
|
|
83411
83565
|
});
|
|
83412
83566
|
this._process.on("close", (_code) => {
|
|
83413
83567
|
this._process = void 0;
|
|
@@ -83472,22 +83626,22 @@ var StdioClientTransport = class {
|
|
|
83472
83626
|
if (this._process) {
|
|
83473
83627
|
const processToClose = this._process;
|
|
83474
83628
|
this._process = void 0;
|
|
83475
|
-
const closePromise = new Promise((
|
|
83629
|
+
const closePromise = new Promise((resolve27) => {
|
|
83476
83630
|
processToClose.once("close", () => {
|
|
83477
|
-
|
|
83631
|
+
resolve27();
|
|
83478
83632
|
});
|
|
83479
83633
|
});
|
|
83480
83634
|
try {
|
|
83481
83635
|
processToClose.stdin?.end();
|
|
83482
83636
|
} catch {
|
|
83483
83637
|
}
|
|
83484
|
-
await Promise.race([closePromise, new Promise((
|
|
83638
|
+
await Promise.race([closePromise, new Promise((resolve27) => setTimeout(resolve27, 2e3).unref())]);
|
|
83485
83639
|
if (processToClose.exitCode === null) {
|
|
83486
83640
|
try {
|
|
83487
83641
|
processToClose.kill("SIGTERM");
|
|
83488
83642
|
} catch {
|
|
83489
83643
|
}
|
|
83490
|
-
await Promise.race([closePromise, new Promise((
|
|
83644
|
+
await Promise.race([closePromise, new Promise((resolve27) => setTimeout(resolve27, 2e3).unref())]);
|
|
83491
83645
|
}
|
|
83492
83646
|
if (processToClose.exitCode === null) {
|
|
83493
83647
|
try {
|
|
@@ -83499,15 +83653,15 @@ var StdioClientTransport = class {
|
|
|
83499
83653
|
this._readBuffer.clear();
|
|
83500
83654
|
}
|
|
83501
83655
|
send(message) {
|
|
83502
|
-
return new Promise((
|
|
83656
|
+
return new Promise((resolve27) => {
|
|
83503
83657
|
if (!this._process?.stdin) {
|
|
83504
83658
|
throw new Error("Not connected");
|
|
83505
83659
|
}
|
|
83506
83660
|
const json4 = serializeMessage(message);
|
|
83507
83661
|
if (this._process.stdin.write(json4)) {
|
|
83508
|
-
|
|
83662
|
+
resolve27();
|
|
83509
83663
|
} else {
|
|
83510
|
-
this._process.stdin.once("drain",
|
|
83664
|
+
this._process.stdin.once("drain", resolve27);
|
|
83511
83665
|
}
|
|
83512
83666
|
});
|
|
83513
83667
|
}
|
|
@@ -83601,12 +83755,12 @@ var StdioServerTransport = class {
|
|
|
83601
83755
|
this.onclose?.();
|
|
83602
83756
|
}
|
|
83603
83757
|
send(message) {
|
|
83604
|
-
return new Promise((
|
|
83758
|
+
return new Promise((resolve27) => {
|
|
83605
83759
|
const json4 = serializeMessage(message);
|
|
83606
83760
|
if (this._stdout.write(json4)) {
|
|
83607
|
-
|
|
83761
|
+
resolve27();
|
|
83608
83762
|
} else {
|
|
83609
|
-
this._stdout.once("drain",
|
|
83763
|
+
this._stdout.once("drain", resolve27);
|
|
83610
83764
|
}
|
|
83611
83765
|
});
|
|
83612
83766
|
}
|
|
@@ -83718,6 +83872,7 @@ function agentTools(options) {
|
|
|
83718
83872
|
};
|
|
83719
83873
|
const send = (message, wait) => node2.send(message, wait);
|
|
83720
83874
|
const a2a = () => node2.a2a();
|
|
83875
|
+
const own2 = async () => options.attached ? await options.attached() : options.sessionId;
|
|
83721
83876
|
return [
|
|
83722
83877
|
defineMcpTool({
|
|
83723
83878
|
name: "agentList",
|
|
@@ -83761,7 +83916,7 @@ function agentTools(options) {
|
|
|
83761
83916
|
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.",
|
|
83762
83917
|
inputSchema: {},
|
|
83763
83918
|
run: async () => {
|
|
83764
|
-
if (!
|
|
83919
|
+
if (!await own2()) throw new Error("agentPeers needs your own session: only an agent the node runs has peers.");
|
|
83765
83920
|
return await node2.peers();
|
|
83766
83921
|
}
|
|
83767
83922
|
}),
|
|
@@ -83841,13 +83996,13 @@ function agentTools(options) {
|
|
|
83841
83996
|
}),
|
|
83842
83997
|
defineMcpTool({
|
|
83843
83998
|
name: "missionList",
|
|
83844
|
-
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
|
|
83999
|
+
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.",
|
|
83845
84000
|
inputSchema: {},
|
|
83846
84001
|
run: async () => node2.missions()
|
|
83847
84002
|
}),
|
|
83848
84003
|
defineMcpTool({
|
|
83849
84004
|
name: "missionShow",
|
|
83850
|
-
description: "Show one mission by its id: its brief, its state, its open questions (questions: each lead question that waits on
|
|
84005
|
+
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.",
|
|
83851
84006
|
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
83852
84007
|
run: async (params) => {
|
|
83853
84008
|
const found = (await node2.missions()).filter((record3) => record3.missionId === params.mission);
|
|
@@ -83899,7 +84054,7 @@ function agentTools(options) {
|
|
|
83899
84054
|
}),
|
|
83900
84055
|
defineMcpTool({
|
|
83901
84056
|
name: "missionReply",
|
|
83902
|
-
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.",
|
|
84057
|
+
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.",
|
|
83903
84058
|
inputSchema: {
|
|
83904
84059
|
mission: MissionIdSchema.describe("The mission id."),
|
|
83905
84060
|
text: external_exports.string().min(1).describe("The answer, in full: the lead has none of your context."),
|
|
@@ -83933,13 +84088,22 @@ function agentTools(options) {
|
|
|
83933
84088
|
}),
|
|
83934
84089
|
defineMcpTool({
|
|
83935
84090
|
name: "missionJoin",
|
|
83936
|
-
description: "
|
|
84091
|
+
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.",
|
|
83937
84092
|
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
83938
84093
|
run: async (params) => {
|
|
83939
|
-
if (!
|
|
84094
|
+
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.");
|
|
83940
84095
|
return node2.joinMission(params.mission);
|
|
83941
84096
|
}
|
|
83942
84097
|
}),
|
|
84098
|
+
defineMcpTool({
|
|
84099
|
+
name: "missionLeave",
|
|
84100
|
+
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.",
|
|
84101
|
+
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
84102
|
+
run: async (params) => {
|
|
84103
|
+
if (!await own2()) throw new Error("missionLeave needs your own session: only a decision receiver leaves a mission.");
|
|
84104
|
+
return node2.leaveMission(params.mission);
|
|
84105
|
+
}
|
|
84106
|
+
}),
|
|
83943
84107
|
defineMcpTool({
|
|
83944
84108
|
name: "missionNew",
|
|
83945
84109
|
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.",
|
|
@@ -83959,7 +84123,7 @@ function agentTools(options) {
|
|
|
83959
84123
|
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.",
|
|
83960
84124
|
inputSchema: {},
|
|
83961
84125
|
run: async () => {
|
|
83962
|
-
if (!
|
|
84126
|
+
if (!await own2()) throw new Error("missionGet needs your own session: only an agent the node runs has a mission.");
|
|
83963
84127
|
return await node2.mission();
|
|
83964
84128
|
}
|
|
83965
84129
|
}),
|
|
@@ -83971,7 +84135,7 @@ function agentTools(options) {
|
|
|
83971
84135
|
state: external_exports.enum(["done", "failed"]).optional().describe("done when the end state is reached, failed when it cannot be. Default: done.")
|
|
83972
84136
|
},
|
|
83973
84137
|
run: async (params) => {
|
|
83974
|
-
if (!
|
|
84138
|
+
if (!await own2()) throw new Error("missionHandoff needs your own session: only the lead session of a mission hands it off.");
|
|
83975
84139
|
return await node2.handoff({ result: params.result, state: params.state ?? "done" });
|
|
83976
84140
|
}
|
|
83977
84141
|
}),
|
|
@@ -83984,18 +84148,18 @@ function agentTools(options) {
|
|
|
83984
84148
|
state: external_exports.string().min(1).optional().describe("The state of the linked thing, such as open, merged or closed.")
|
|
83985
84149
|
},
|
|
83986
84150
|
run: async (params) => {
|
|
83987
|
-
if (!
|
|
84151
|
+
if (!await own2()) throw new Error("missionLink needs your own session: only the lead session of a mission adds a link to it.");
|
|
83988
84152
|
return await node2.link({ label: params.label, url: params.url, state: params.state ?? null });
|
|
83989
84153
|
}
|
|
83990
84154
|
}),
|
|
83991
84155
|
defineMcpTool({
|
|
83992
84156
|
name: "missionAsk",
|
|
83993
|
-
description: "Ask the
|
|
84157
|
+
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.",
|
|
83994
84158
|
inputSchema: {
|
|
83995
|
-
question: external_exports.string().min(1).describe("The question, in full, with the options you see and what each one means: the
|
|
84159
|
+
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.")
|
|
83996
84160
|
},
|
|
83997
84161
|
run: async (params) => {
|
|
83998
|
-
if (!
|
|
84162
|
+
if (!await own2()) throw new Error("missionAsk needs your own session: only the lead session of a mission asks its decision receivers.");
|
|
83999
84163
|
return await node2.ask({ question: params.question });
|
|
84000
84164
|
}
|
|
84001
84165
|
})
|
|
@@ -84562,8 +84726,8 @@ function relaySender(plugin, by) {
|
|
|
84562
84726
|
function pluginSender(plugin) {
|
|
84563
84727
|
return { channel: plugin, peer: plugin, from: { kind: "node", name: plugin } };
|
|
84564
84728
|
}
|
|
84565
|
-
function remoteSessionSender(plugin,
|
|
84566
|
-
return { channel: plugin, peer:
|
|
84729
|
+
function remoteSessionSender(plugin, session) {
|
|
84730
|
+
return { channel: plugin, peer: session.node, from: { kind: "session", name: session.sessionId, ade: session.node } };
|
|
84567
84731
|
}
|
|
84568
84732
|
function missionTarget(node2, team) {
|
|
84569
84733
|
return team ? { kind: "team", name: team, ade: node2 } : { kind: "node", name: node2 };
|
|
@@ -84702,10 +84866,10 @@ function a2aHandlerFor(host, reader, confined = false, network = null) {
|
|
|
84702
84866
|
};
|
|
84703
84867
|
}
|
|
84704
84868
|
function readBody(request) {
|
|
84705
|
-
return new Promise((
|
|
84869
|
+
return new Promise((resolve27, reject) => {
|
|
84706
84870
|
const chunks = [];
|
|
84707
84871
|
request.on("data", (chunk) => chunks.push(chunk));
|
|
84708
|
-
request.on("end", () =>
|
|
84872
|
+
request.on("end", () => resolve27(Buffer.concat(chunks).toString("utf8")));
|
|
84709
84873
|
request.on("error", reject);
|
|
84710
84874
|
});
|
|
84711
84875
|
}
|
|
@@ -84769,7 +84933,7 @@ async function serveHostApi(request, response, url2, api) {
|
|
|
84769
84933
|
if (request.method === "POST" && url2.pathname === "/orbital/host/missions/ask") {
|
|
84770
84934
|
const asked2 = MissionQuestionSchema.safeParse(JSON.parse(await readBody(request) || "{}"));
|
|
84771
84935
|
if (!asked2.success) return json3(response, 400, { error: asked2.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ") });
|
|
84772
|
-
return json3(response, 200, { delivered: await api.
|
|
84936
|
+
return json3(response, 200, { delivered: await api.askReceiver(asked2.data) });
|
|
84773
84937
|
}
|
|
84774
84938
|
if (request.method === "POST" && url2.pathname === "/orbital/host/settled") {
|
|
84775
84939
|
const { orFollowing } = JSON.parse(await readBody(request) || "{}");
|
|
@@ -84888,11 +85052,16 @@ async function startNodeServer(options) {
|
|
|
84888
85052
|
const by = caller.bound ? `session:${caller.bound}` : "cli";
|
|
84889
85053
|
return json3(response, 200, control[1] === "pause" ? await options.host.pauseMission(by, asked2.data.missionId) : await options.host.resumeMission(by, asked2.data.missionId));
|
|
84890
85054
|
}
|
|
84891
|
-
|
|
84892
|
-
|
|
85055
|
+
const membership = /^\/orbital\/missions\/(join|leave)$/.exec(url2.pathname);
|
|
85056
|
+
if (request.method === "POST" && membership) {
|
|
84893
85057
|
const asked2 = MissionJoinSchema.safeParse(JSON.parse(await readBody(request) || "{}"));
|
|
84894
85058
|
if (!asked2.success) return json3(response, 400, { error: asked2.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ") });
|
|
84895
|
-
|
|
85059
|
+
const session = caller.bound ?? caller.reader;
|
|
85060
|
+
const holder = caller.presented === caller.token;
|
|
85061
|
+
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." });
|
|
85062
|
+
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.` });
|
|
85063
|
+
const receiver = asked2.data.externalId ? { externalId: asked2.data.externalId } : { sessionId: session };
|
|
85064
|
+
return json3(response, 200, membership[1] === "join" ? await options.host.joinMission(receiver, asked2.data.missionId) : await options.host.leaveMission(receiver, asked2.data.missionId));
|
|
84896
85065
|
}
|
|
84897
85066
|
if (request.method === "POST" && url2.pathname === "/orbital/missions/new") {
|
|
84898
85067
|
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." });
|
|
@@ -84930,7 +85099,7 @@ async function startNodeServer(options) {
|
|
|
84930
85099
|
const named2 = asked2.data.missionId ?? own2?.missionId ?? null;
|
|
84931
85100
|
if (!named2) return json3(response, 400, { error: "Name the mission by its id." });
|
|
84932
85101
|
if (own2 && own2.missionId !== named2) return json3(response, 403, { error: `The session ${session} works on the mission ${own2.missionId}, not on ${named2}.` });
|
|
84933
|
-
if (!session && caller.presented !== caller.token) return json3(response, 403, { error: "Only the lead session or a holder of the node secret asks the
|
|
85102
|
+
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." });
|
|
84934
85103
|
return json3(response, 200, await options.host.askMission({ mission: named2, question: asked2.data.question, sessionId: session }));
|
|
84935
85104
|
}
|
|
84936
85105
|
const activity = /^\/orbital\/missions\/([^/]+)\/activity$/.exec(url2.pathname);
|
|
@@ -84971,6 +85140,20 @@ async function startNodeServer(options) {
|
|
|
84971
85140
|
return json3(response, 502, { error: `The panel ${plugin2}/${id} did not answer: ${error62 instanceof Error ? error62.message : String(error62)}` });
|
|
84972
85141
|
}
|
|
84973
85142
|
}
|
|
85143
|
+
const commandPath = /^\/orbital\/plugins\/([^/]+)\/commands\/([^/]+)$/.exec(url2.pathname);
|
|
85144
|
+
if (request.method === "POST" && commandPath) {
|
|
85145
|
+
if (presented !== token) return json3(response, 403, { error: "Only a holder of the node secret runs a command of a plugin." });
|
|
85146
|
+
const [name, command] = [decodeURIComponent(commandPath[1]), decodeURIComponent(commandPath[2])];
|
|
85147
|
+
const plugin2 = options.ports.plugins.find((one) => one.name === name);
|
|
85148
|
+
if (!plugin2?.command) return json3(response, 404, { error: `No plugin ${name} with commands runs on this node.` });
|
|
85149
|
+
const asked2 = PluginCommandRequestSchema.safeParse(await readBody(request).then((raw) => JSON.parse(raw)).catch(() => null));
|
|
85150
|
+
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.` });
|
|
85151
|
+
try {
|
|
85152
|
+
return json3(response, 200, await plugin2.command(asked2.data));
|
|
85153
|
+
} catch (error62) {
|
|
85154
|
+
return json3(response, 502, { error: `The command ${name} ${command} failed: ${error62 instanceof Error ? error62.message : String(error62)}` });
|
|
85155
|
+
}
|
|
85156
|
+
}
|
|
84974
85157
|
if (url2.pathname === "/orbital/telemetry/snapshot" || url2.pathname === "/orbital/telemetry/events") {
|
|
84975
85158
|
if (request.method === "GET" && url2.pathname === "/orbital/telemetry/snapshot" && (presented === token || bound)) return json3(response, 200, await options.host.snapshot());
|
|
84976
85159
|
if (presented !== token) return json3(response, 403, { error: "Only a holder of the node secret reads the telemetry stream of the node." });
|
|
@@ -85130,7 +85313,7 @@ async function startNodeServer(options) {
|
|
|
85130
85313
|
const result2 = await dispatchA2a(await readBody(request), a2aHandlerFor(options.host, reader, bound !== null, httpSender(request.socket.remoteAddress)));
|
|
85131
85314
|
if (result2.kind === "json") return json3(response, 200, result2.body);
|
|
85132
85315
|
response.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
|
|
85133
|
-
const closed = new Promise((
|
|
85316
|
+
const closed = new Promise((resolve27) => response.on("close", resolve27));
|
|
85134
85317
|
const iterator = result2.events[Symbol.asyncIterator]();
|
|
85135
85318
|
for (; ; ) {
|
|
85136
85319
|
const next = await Promise.race([iterator.next(), closed.then(() => null)]);
|
|
@@ -85143,9 +85326,9 @@ async function startNodeServer(options) {
|
|
|
85143
85326
|
}
|
|
85144
85327
|
response.end();
|
|
85145
85328
|
}
|
|
85146
|
-
await new Promise((
|
|
85329
|
+
await new Promise((resolve27, reject) => {
|
|
85147
85330
|
server.once("error", reject);
|
|
85148
|
-
server.listen(options.port, address, () =>
|
|
85331
|
+
server.listen(options.port, address, () => resolve27());
|
|
85149
85332
|
});
|
|
85150
85333
|
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() };
|
|
85151
85334
|
mkdirSync17(nodeDir(options.home, options.name), { recursive: true, mode: 448 });
|
|
@@ -85156,7 +85339,7 @@ async function startNodeServer(options) {
|
|
|
85156
85339
|
close: async () => {
|
|
85157
85340
|
if (readNodeInfo(options.home, options.name)?.pid === process.pid) rmSync9(nodeInfoPath(options.home, options.name), { force: true });
|
|
85158
85341
|
server.closeAllConnections();
|
|
85159
|
-
await new Promise((
|
|
85342
|
+
await new Promise((resolve27) => server.close(() => resolve27()));
|
|
85160
85343
|
}
|
|
85161
85344
|
};
|
|
85162
85345
|
}
|
|
@@ -86166,7 +86349,7 @@ function hostApiOf(host, plugin, catalog, channels, models) {
|
|
|
86166
86349
|
startMission: (start) => host.startMission(plugin, start),
|
|
86167
86350
|
missions: () => host.missionList(),
|
|
86168
86351
|
joinMission: async (request) => {
|
|
86169
|
-
await host.receive(networkReceive(remoteSessionSender(plugin, request.
|
|
86352
|
+
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)));
|
|
86170
86353
|
return host.joinRemote(request);
|
|
86171
86354
|
},
|
|
86172
86355
|
replyMission: async (request) => {
|
|
@@ -86181,9 +86364,9 @@ function hostApiOf(host, plugin, catalog, channels, models) {
|
|
|
86181
86364
|
await relayed(request, `resume the mission ${request.missionId}`);
|
|
86182
86365
|
return host.resumeMission(request.by, request.missionId, false);
|
|
86183
86366
|
},
|
|
86184
|
-
|
|
86185
|
-
await host.receive(networkReceive(remoteSessionSender(plugin, { node: question.node, sessionId: question.leadSessionId }), { kind: "session", name: question.
|
|
86186
|
-
return host.
|
|
86367
|
+
askReceiver: async (question) => {
|
|
86368
|
+
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 })));
|
|
86369
|
+
return host.askReceiver(question);
|
|
86187
86370
|
},
|
|
86188
86371
|
sayMission: async (request) => {
|
|
86189
86372
|
await relayed(request, request.text);
|
|
@@ -86399,8 +86582,8 @@ var ChannelHub = class {
|
|
|
86399
86582
|
if (state && isTerminal(state)) return;
|
|
86400
86583
|
}
|
|
86401
86584
|
if (signal?.aborted) return;
|
|
86402
|
-
await new Promise((
|
|
86403
|
-
wake =
|
|
86585
|
+
await new Promise((resolve27) => {
|
|
86586
|
+
wake = resolve27;
|
|
86404
86587
|
});
|
|
86405
86588
|
wake = null;
|
|
86406
86589
|
}
|
|
@@ -86574,8 +86757,8 @@ Answer yes or no in this thread.`)], metadata: { [ORBITAL_META.channel]: binding
|
|
|
86574
86757
|
thread: null
|
|
86575
86758
|
});
|
|
86576
86759
|
const threadKey = `${binding.channel}|${binding.id}|${sent.thread ?? sent.ref}`;
|
|
86577
|
-
const answer2 = await new Promise((
|
|
86578
|
-
this.asks.set(threadKey, { resolve:
|
|
86760
|
+
const answer2 = await new Promise((resolve27, reject) => {
|
|
86761
|
+
this.asks.set(threadKey, { resolve: resolve27, question, channel: binding.channel, since: new Date(this.options.context.now()).toISOString() });
|
|
86579
86762
|
signal?.addEventListener("abort", () => {
|
|
86580
86763
|
this.asks.delete(threadKey);
|
|
86581
86764
|
reject(new Error("The question was withdrawn."));
|
|
@@ -87250,8 +87433,9 @@ var panel = {
|
|
|
87250
87433
|
var panelCommands = [panel];
|
|
87251
87434
|
|
|
87252
87435
|
// src/core/cli/plugin.ts
|
|
87253
|
-
|
|
87254
|
-
import {
|
|
87436
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
87437
|
+
import { readFileSync as readFileSync29 } from "node:fs";
|
|
87438
|
+
import { basename as basename9, resolve as resolve15 } from "node:path";
|
|
87255
87439
|
|
|
87256
87440
|
// src/sdk/index.ts
|
|
87257
87441
|
init_protocol();
|
|
@@ -87632,13 +87816,144 @@ init_protocol();
|
|
|
87632
87816
|
|
|
87633
87817
|
// src/core/cli/plugin.ts
|
|
87634
87818
|
init_protocol();
|
|
87819
|
+
|
|
87820
|
+
// src/core/cli/plugin-commands.ts
|
|
87821
|
+
init_protocol();
|
|
87822
|
+
init_command();
|
|
87823
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28 } from "node:fs";
|
|
87824
|
+
import { join as join37, resolve as resolve14 } from "node:path";
|
|
87825
|
+
var INTROSPECTION_WORDS = ["describe", "schema", "help"];
|
|
87826
|
+
function commandsOf(dir) {
|
|
87827
|
+
const descriptor = join37(dir, PLUGIN_PACKAGE_FILE);
|
|
87828
|
+
if (!existsSync30(descriptor)) return [];
|
|
87829
|
+
try {
|
|
87830
|
+
return PluginPackageSchema.parse(JSON.parse(readFileSync28(descriptor, "utf8"))).commands;
|
|
87831
|
+
} catch {
|
|
87832
|
+
return [];
|
|
87833
|
+
}
|
|
87834
|
+
}
|
|
87835
|
+
function declaredPluginCommands(workspaceDir, env3) {
|
|
87836
|
+
try {
|
|
87837
|
+
const source = definitionSource(workspaceDir, env3);
|
|
87838
|
+
const config2 = source ? workspaceConfig(workspaceDir, env3) : null;
|
|
87839
|
+
if (!source || !config2) return [];
|
|
87840
|
+
return Object.entries(config2.plugins).filter(([, entry]) => entry.enabled && entry.source).flatMap(([plugin, entry]) => {
|
|
87841
|
+
const from = pluginSource(plugin, entry);
|
|
87842
|
+
const commands = from.startsWith("exec:") ? [] : commandsOf(pluginDir(plugin, from, { harnessRoot: source.root, home: orbitalHome(env3) }));
|
|
87843
|
+
return commands.length > 0 ? [{ plugin, commands }] : [];
|
|
87844
|
+
});
|
|
87845
|
+
} catch {
|
|
87846
|
+
return [];
|
|
87847
|
+
}
|
|
87848
|
+
}
|
|
87849
|
+
function workspaceOf(input2, fallback = ".") {
|
|
87850
|
+
return resolve14(typeof input2.flags.workspace === "string" ? input2.flags.workspace : fallback);
|
|
87851
|
+
}
|
|
87852
|
+
async function startWorkspacePlugin(context, workspaceDir, name, terminal2) {
|
|
87853
|
+
const root = definitionSource(workspaceDir, context.env)?.root ?? null;
|
|
87854
|
+
const config2 = workspaceConfig(workspaceDir, context.env);
|
|
87855
|
+
const entry = config2?.plugins[name];
|
|
87856
|
+
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>` });
|
|
87857
|
+
const secrets = new PluginSecrets(context.env);
|
|
87858
|
+
return startRemotePlugin({
|
|
87859
|
+
name,
|
|
87860
|
+
source: pluginSource(name, entry),
|
|
87861
|
+
config: entry.config,
|
|
87862
|
+
entryEnv: entry.env,
|
|
87863
|
+
node: { name: `plugin-${name}`, workspaceDir, harness: defaultHarness(context.env), harnesses: [...RUNNER_HARNESSES], once: true, idleExitMs: null, project: null },
|
|
87864
|
+
roots: { harnessRoot: root, home: orbitalHome(context.env) },
|
|
87865
|
+
offline: config2.offline,
|
|
87866
|
+
policy: new PolicyChain([]),
|
|
87867
|
+
env: context.env,
|
|
87868
|
+
log: (level, message) => level === "info" ? context.output.step(message) : context.output.warn(message),
|
|
87869
|
+
secret: async (secret) => secrets.get(name, secret),
|
|
87870
|
+
restarts: 0,
|
|
87871
|
+
...terminal2 && { terminal: terminal2 }
|
|
87872
|
+
});
|
|
87873
|
+
}
|
|
87874
|
+
function flagOf(option) {
|
|
87875
|
+
return {
|
|
87876
|
+
name: option.name,
|
|
87877
|
+
type: option.type,
|
|
87878
|
+
summary: option.summary,
|
|
87879
|
+
...option.required && { required: true },
|
|
87880
|
+
...option.default !== void 0 && { default: option.default },
|
|
87881
|
+
...option.values && { enumValues: option.values }
|
|
87882
|
+
};
|
|
87883
|
+
}
|
|
87884
|
+
function requestOf(command, input2, terminal2) {
|
|
87885
|
+
const args = Object.fromEntries(command.args.flatMap((arg) => {
|
|
87886
|
+
const value2 = input2.positionals[arg.name];
|
|
87887
|
+
return value2 === void 0 ? [] : [[arg.name, value2]];
|
|
87888
|
+
}));
|
|
87889
|
+
const options = Object.fromEntries(command.options.flatMap((option) => {
|
|
87890
|
+
const value2 = input2.flags[option.name];
|
|
87891
|
+
if (option.type === "boolean") return [[option.name, value2 === true]];
|
|
87892
|
+
return typeof value2 === "string" || typeof value2 === "number" ? [[option.name, value2]] : [];
|
|
87893
|
+
}));
|
|
87894
|
+
return { name: command.name, args, options, terminal: terminal2 };
|
|
87895
|
+
}
|
|
87896
|
+
async function runOnNode(context, workspaceDir, input2, plugin, request) {
|
|
87897
|
+
const configured = workspaceConfig(workspaceDir, context.env)?.node ?? null;
|
|
87898
|
+
const name = chooseNode({ ...typeof input2.flags.node === "string" && { flag: input2.flags.node }, env: context.env, configured, workspaceDir }).name;
|
|
87899
|
+
const node2 = await liveNode(orbitalHome(context.env), name, context.fetch);
|
|
87900
|
+
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` });
|
|
87901
|
+
const response = await context.fetch(`${node2.url}/orbital/plugins/${encodeURIComponent(plugin)}/commands/${encodeURIComponent(request.name)}`, {
|
|
87902
|
+
method: "POST",
|
|
87903
|
+
headers: { authorization: `Bearer ${node2.token}`, "content-type": "application/json" },
|
|
87904
|
+
body: JSON.stringify(request)
|
|
87905
|
+
});
|
|
87906
|
+
const body2 = await response.json();
|
|
87907
|
+
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 });
|
|
87908
|
+
return PluginCommandResultSchema.parse(body2);
|
|
87909
|
+
}
|
|
87910
|
+
async function runInPlugin(context, workspaceDir, input2, plugin, command, terminal2) {
|
|
87911
|
+
if (command.interactive && context.nonInteractive) {
|
|
87912
|
+
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." });
|
|
87913
|
+
}
|
|
87914
|
+
const given = command.interactive ? { input: terminal2.input, output: context.outputMode === "text" ? terminal2.output : terminal2.error, error: terminal2.error } : void 0;
|
|
87915
|
+
const started = await startWorkspacePlugin(context, workspaceDir, plugin, given);
|
|
87916
|
+
try {
|
|
87917
|
+
return await started.command(requestOf(command, input2, given ? { tty: context.isTty } : null));
|
|
87918
|
+
} finally {
|
|
87919
|
+
await started.close?.();
|
|
87920
|
+
}
|
|
87921
|
+
}
|
|
87922
|
+
function specOf(workspaceDir, plugin, command, terminal2) {
|
|
87923
|
+
return {
|
|
87924
|
+
path: [plugin, command.name],
|
|
87925
|
+
summary: command.summary,
|
|
87926
|
+
group: CommandGroup.Packs,
|
|
87927
|
+
credential: "none",
|
|
87928
|
+
flags: [
|
|
87929
|
+
{ name: "workspace", type: "path", summary: `The project whose ${ORBITAL_FILE} names the plugin. Default: the current directory.` },
|
|
87930
|
+
...command.node ? [{ name: "node", type: "string", identifier: true, summary: "The node that runs the plugin. Default: the node of this workspace." }] : [],
|
|
87931
|
+
...command.options.map(flagOf)
|
|
87932
|
+
],
|
|
87933
|
+
positionals: command.args.map((arg) => ({ name: arg.name, type: "string", summary: arg.summary, ...arg.required && { required: true }, ...arg.variadic && { variadic: true } })),
|
|
87934
|
+
outputShape: { kind: "object", ref: "PluginCommandResult", fields: [{ name: "data", type: "object", summary: `What the command of the plugin ${plugin} answered.` }] },
|
|
87935
|
+
exitCodes: [0, 1, 2, 3, 5],
|
|
87936
|
+
routes: [],
|
|
87937
|
+
examples: [{ command: `orbital ${plugin} ${command.name}`, why: command.summary }],
|
|
87938
|
+
run: async (input2, context) => {
|
|
87939
|
+
const workspace = workspaceOf(input2, workspaceDir);
|
|
87940
|
+
const result2 = command.node ? await runOnNode(context, workspace, input2, plugin, requestOf(command, input2, null)) : await runInPlugin(context, workspace, input2, plugin, command, terminal2);
|
|
87941
|
+
if (result2.text !== null) context.output.line(result2.text);
|
|
87942
|
+
if (result2.text === null && Object.keys(result2.data).length > 0) context.output.line(JSON.stringify(result2.data, null, 2));
|
|
87943
|
+
return { data: result2.data, ...result2.exitCode !== 0 && { exitCode: result2.exitCode } };
|
|
87944
|
+
}
|
|
87945
|
+
};
|
|
87946
|
+
}
|
|
87947
|
+
function pluginCommandSpecs(options) {
|
|
87948
|
+
const terminal2 = options.terminal ?? { input: 0, output: 1, error: 2 };
|
|
87949
|
+
return declaredPluginCommands(options.workspaceDir, options.env).filter(({ plugin }) => !options.reserved.includes(plugin)).flatMap(({ plugin, commands }) => commands.map((command) => specOf(options.workspaceDir, plugin, command, terminal2)));
|
|
87950
|
+
}
|
|
87951
|
+
|
|
87952
|
+
// src/core/cli/plugin.ts
|
|
87635
87953
|
init_command();
|
|
87636
87954
|
function workspaceFlag() {
|
|
87637
87955
|
return { name: "workspace", type: "path", summary: `The project whose ${ORBITAL_FILE} names the plugin. Default: the current directory.` };
|
|
87638
87956
|
}
|
|
87639
|
-
function workspaceOf(input2) {
|
|
87640
|
-
return resolve14(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
87641
|
-
}
|
|
87642
87957
|
function sourceOrRefuse(context, input2) {
|
|
87643
87958
|
const workspaceDir = workspaceOf(input2);
|
|
87644
87959
|
const source = definitionSource(workspaceDir, context.env);
|
|
@@ -87662,6 +87977,25 @@ async function manifestOf(context, workspaceDir, name, source, root, config2) {
|
|
|
87662
87977
|
await plugin.close?.();
|
|
87663
87978
|
return plugin.manifest;
|
|
87664
87979
|
}
|
|
87980
|
+
function configOf(input2, known) {
|
|
87981
|
+
const file2 = typeof input2.flags["config-file"] === "string" ? resolve15(input2.flags["config-file"]) : null;
|
|
87982
|
+
const fromFile = file2 ? (0, import_yaml8.parse)(readFileSync29(file2, "utf8")) : {};
|
|
87983
|
+
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." });
|
|
87984
|
+
const pairs = Array.isArray(input2.flags.config) ? input2.flags.config.map(String) : typeof input2.flags.config === "string" ? [input2.flags.config] : [];
|
|
87985
|
+
const fromPairs = Object.fromEntries(pairs.map((pair) => {
|
|
87986
|
+
const at = pair.indexOf("=");
|
|
87987
|
+
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." });
|
|
87988
|
+
return [pair.slice(0, at), (0, import_yaml8.parse)(pair.slice(at + 1)) ?? ""];
|
|
87989
|
+
}));
|
|
87990
|
+
return { ...known, ...fromFile, ...fromPairs };
|
|
87991
|
+
}
|
|
87992
|
+
async function checkedManifest(context, workspaceDir, name, source, root, config2) {
|
|
87993
|
+
try {
|
|
87994
|
+
return await manifestOf(context, workspaceDir, name, source, root, config2);
|
|
87995
|
+
} catch (error62) {
|
|
87996
|
+
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>." });
|
|
87997
|
+
}
|
|
87998
|
+
}
|
|
87665
87999
|
function permissionLines(manifest) {
|
|
87666
88000
|
const { network, secrets, models } = manifest.permissions;
|
|
87667
88001
|
return [
|
|
@@ -87677,7 +88011,9 @@ var add = () => ({
|
|
|
87677
88011
|
flags: [
|
|
87678
88012
|
workspaceFlag(),
|
|
87679
88013
|
{ name: "install-only", type: "boolean", summary: `Install the plugin on this machine only: do not name it in ${ORBITAL_FILE}.` },
|
|
87680
|
-
{ 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.` }
|
|
88014
|
+
{ 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.` },
|
|
88015
|
+
{ 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.` },
|
|
88016
|
+
{ 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." }
|
|
87681
88017
|
],
|
|
87682
88018
|
positionals: [
|
|
87683
88019
|
{ name: "plugin", type: "string", required: true, summary: "<name>@<marketplace>, npm:<package>, git:<url>, or a folder such as ./plugins/mine." }
|
|
@@ -87698,7 +88034,8 @@ var add = () => ({
|
|
|
87698
88034
|
examples: [
|
|
87699
88035
|
{ command: "orbital plugin add slack@community", why: "Install the slack plugin from the community marketplace, and use it in this workspace." },
|
|
87700
88036
|
{ command: "orbital plugin add ./plugins/mine", why: `Use a plugin you write in this project: ${ORBITAL_FILE} names it by its path.` },
|
|
87701
|
-
{ 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." }
|
|
88037
|
+
{ 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." },
|
|
88038
|
+
{ command: "orbital plugin add ./plugins/notify --config channel=general --config retries=3", why: "Add a plugin that checks its config when it starts." }
|
|
87702
88039
|
],
|
|
87703
88040
|
run: async (input2, context) => {
|
|
87704
88041
|
const home = orbitalHome(context.env);
|
|
@@ -87710,20 +88047,22 @@ var add = () => ({
|
|
|
87710
88047
|
if (input2.flags["install-only"] === true) return { data: { name: ref.name, source: given, installed, enabled: false, manifest: null } };
|
|
87711
88048
|
if (input2.flags.user === true) {
|
|
87712
88049
|
const source2 = ref.kind === "folder" && !given.includes("@") ? ref.dir : ref.kind === "npm" ? `npm:${ref.pkg}` : ref.kind === "git" ? `git:${ref.url}` : given;
|
|
87713
|
-
const
|
|
88050
|
+
const config3 = configOf(input2, userPlugins(context.env)[ref.name]?.config ?? {});
|
|
88051
|
+
const manifest2 = await checkedManifest(context, workspaceOf(input2), ref.name, source2, null, config3);
|
|
87714
88052
|
context.output.step(`${ref.name} ${manifest2.version} reaches:`);
|
|
87715
88053
|
for (const line of permissionLines(manifest2)) context.output.step(line);
|
|
87716
|
-
setUserPlugin(context.env, ref.name, { source: source2, enabled: true, config:
|
|
88054
|
+
setUserPlugin(context.env, ref.name, { source: source2, enabled: true, config: config3, env: [] });
|
|
87717
88055
|
context.output.step(`\u2713 your user config names ${ref.name}: source ${source2}`);
|
|
87718
88056
|
return { data: { name: ref.name, source: source2, installed, enabled: true, manifest: manifest2 } };
|
|
87719
88057
|
}
|
|
87720
88058
|
const definition = sourceOrRefuse(context, input2);
|
|
87721
88059
|
const root = definition.root;
|
|
87722
88060
|
const source = sourceOf(ref, given, root);
|
|
87723
|
-
const
|
|
88061
|
+
const config2 = configOf(input2, sourceConfig(definition).plugins[ref.name]?.config ?? {});
|
|
88062
|
+
const manifest = await checkedManifest(context, workspaceOf(input2), ref.name, source, root, config2);
|
|
87724
88063
|
context.output.step(`${ref.name} ${manifest.version} reaches:`);
|
|
87725
88064
|
for (const line of permissionLines(manifest)) context.output.step(line);
|
|
87726
|
-
setPluginEntry(definition.configFile, ref.name, { source, enabled: true, config:
|
|
88065
|
+
setPluginEntry(definition.configFile, ref.name, { source, enabled: true, config: config2, env: [] });
|
|
87727
88066
|
context.output.step(`\u2713 ${basename9(definition.configFile)} names ${ref.name}: source ${source}`);
|
|
87728
88067
|
return { data: { name: ref.name, source, installed, enabled: true, manifest } };
|
|
87729
88068
|
}
|
|
@@ -87801,57 +88140,6 @@ var remove = () => ({
|
|
|
87801
88140
|
return { data: { name, unnamed, uninstalled } };
|
|
87802
88141
|
}
|
|
87803
88142
|
});
|
|
87804
|
-
var runCommand = () => ({
|
|
87805
|
-
path: ["plugin", "run"],
|
|
87806
|
-
summary: "Run one of a plugin's own commands, such as a login, and print what it answers.",
|
|
87807
|
-
credential: "none",
|
|
87808
|
-
flags: [workspaceFlag()],
|
|
87809
|
-
positionals: [
|
|
87810
|
-
{ name: "plugin", type: "string", required: true, identifier: true, summary: `A plugin ${ORBITAL_FILE} names.` },
|
|
87811
|
-
{ name: "command", type: "string", required: true, summary: "The plugin's command." },
|
|
87812
|
-
{ name: "args", type: "string", summary: "Arguments for the command, after `--`.", passthrough: true }
|
|
87813
|
-
],
|
|
87814
|
-
outputShape: { kind: "object", ref: "PluginCommand", fields: [{ name: "output", type: "string", summary: "What the command answered." }] },
|
|
87815
|
-
exitCodes: [0, 1, 3],
|
|
87816
|
-
routes: [],
|
|
87817
|
-
examples: [{ command: "orbital plugin run slack login", why: "Run the slack plugin's login command." }],
|
|
87818
|
-
run: async (input2, context) => {
|
|
87819
|
-
const name = String(input2.positionals.plugin);
|
|
87820
|
-
const plugin = await startNamed(context, input2, name);
|
|
87821
|
-
try {
|
|
87822
|
-
const command = String(input2.positionals.command);
|
|
87823
|
-
if (!plugin.manifest.commands.some((one) => one.name === command)) {
|
|
87824
|
-
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." });
|
|
87825
|
-
}
|
|
87826
|
-
const output2 = await plugin.command(command, [...input2.passthrough]);
|
|
87827
|
-
context.output.line(output2);
|
|
87828
|
-
return { data: { output: output2 } };
|
|
87829
|
-
} finally {
|
|
87830
|
-
await plugin.close?.();
|
|
87831
|
-
}
|
|
87832
|
-
}
|
|
87833
|
-
});
|
|
87834
|
-
async function startNamed(context, input2, name) {
|
|
87835
|
-
const root = definitionSource(workspaceOf(input2), context.env)?.root ?? null;
|
|
87836
|
-
const config2 = workspaceConfig(workspaceOf(input2), context.env);
|
|
87837
|
-
const entry = config2?.plugins[name];
|
|
87838
|
-
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>` });
|
|
87839
|
-
const secrets = new PluginSecrets(context.env);
|
|
87840
|
-
return startRemotePlugin({
|
|
87841
|
-
name,
|
|
87842
|
-
source: pluginSource(name, entry),
|
|
87843
|
-
config: entry.config,
|
|
87844
|
-
entryEnv: entry.env,
|
|
87845
|
-
node: { name: `plugin-${name}`, workspaceDir: workspaceOf(input2), harness: defaultHarness(context.env), harnesses: [...RUNNER_HARNESSES], once: true, idleExitMs: null, project: null },
|
|
87846
|
-
roots: { harnessRoot: root, home: orbitalHome(context.env) },
|
|
87847
|
-
offline: config2.offline,
|
|
87848
|
-
policy: new PolicyChain([]),
|
|
87849
|
-
env: context.env,
|
|
87850
|
-
log: (level, message) => level === "info" ? context.output.step(message) : context.output.warn(message),
|
|
87851
|
-
secret: async (secret) => secrets.get(name, secret),
|
|
87852
|
-
restarts: 0
|
|
87853
|
-
});
|
|
87854
|
-
}
|
|
87855
88143
|
var check2 = () => ({
|
|
87856
88144
|
path: ["plugin", "check"],
|
|
87857
88145
|
summary: "Run the conformance checks of each port a plugin provides, so any orbital host can use it.",
|
|
@@ -87877,8 +88165,8 @@ var check2 = () => ({
|
|
|
87877
88165
|
],
|
|
87878
88166
|
run: async (input2, context) => {
|
|
87879
88167
|
const name = String(input2.positionals.plugin);
|
|
87880
|
-
const fixtures = typeof input2.flags.fixtures === "string" ? JSON.parse(
|
|
87881
|
-
const plugin = await
|
|
88168
|
+
const fixtures = typeof input2.flags.fixtures === "string" ? JSON.parse(readFileSync29(input2.flags.fixtures, "utf8")) : {};
|
|
88169
|
+
const plugin = await startWorkspacePlugin(context, workspaceOf(input2), name);
|
|
87882
88170
|
try {
|
|
87883
88171
|
const results = await runConformance(plugin.ports, fixtures);
|
|
87884
88172
|
const mark = { pass: "\u2713", fail: "\u2717", skip: "\u25CB" };
|
|
@@ -88006,16 +88294,16 @@ var secretRemove = {
|
|
|
88006
88294
|
return { data: { plugin, name } };
|
|
88007
88295
|
}
|
|
88008
88296
|
};
|
|
88009
|
-
var pluginCommands = () => [add(), list3(), remove(),
|
|
88297
|
+
var pluginCommands = () => [add(), list3(), remove(), check2(), marketplaceAdd, marketplaceList, marketplaceUpdate, marketplaceRemove, secretSet, secretRemove];
|
|
88010
88298
|
|
|
88011
88299
|
// src/core/cli/plugin-new.ts
|
|
88012
88300
|
init_structure();
|
|
88013
88301
|
init_protocol();
|
|
88014
88302
|
init_command();
|
|
88015
|
-
import { existsSync as
|
|
88016
|
-
import { dirname as dirname16, join as
|
|
88303
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync22, readdirSync as readdirSync20, writeFileSync as writeFileSync20 } from "node:fs";
|
|
88304
|
+
import { dirname as dirname16, join as join38, relative as relative7, resolve as resolve16 } from "node:path";
|
|
88017
88305
|
var PLUGIN_PORTS = ["channel", "store", "workspace", "harness", "panel", "policy", "telemetry", "actions", "workflows", "command"];
|
|
88018
|
-
var SDK_VERSION = true ? `^${"0.0.
|
|
88306
|
+
var SDK_VERSION = true ? `^${"0.0.4"}` : "*";
|
|
88019
88307
|
var ESBUILD_VERSION = "^0.25.0";
|
|
88020
88308
|
var TYPES_NODE_VERSION = "^20.19.43";
|
|
88021
88309
|
var HARNESS_ID = /^[a-z][a-z0-9-]{0,31}$/;
|
|
@@ -88394,7 +88682,7 @@ export function plugin(context: PluginContext): PluginImplementation {
|
|
|
88394
88682
|
switch (event.kind) {
|
|
88395
88683
|
case 'mission.state': {
|
|
88396
88684
|
const known = graph.missions.find((mission) => mission.missionId === event.mission);
|
|
88397
|
-
const state = { missionId: event.mission, team: event.team, state: event.to, sessionId: event.sessionId, lead: event.lead,
|
|
88685
|
+
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 };
|
|
88398
88686
|
graph.missions = [...graph.missions.filter((mission) => mission.missionId !== event.mission), state];
|
|
88399
88687
|
return;
|
|
88400
88688
|
}
|
|
@@ -88535,7 +88823,7 @@ export function plugin(_context: PluginContext): PluginImplementation {
|
|
|
88535
88823
|
case "command":
|
|
88536
88824
|
return {
|
|
88537
88825
|
summary: `An orbital plugin with a command: ${name}.`,
|
|
88538
|
-
plugin: `${header("one command. A person runs it with `orbital
|
|
88826
|
+
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';
|
|
88539
88827
|
|
|
88540
88828
|
export const NAME = '${name}';
|
|
88541
88829
|
|
|
@@ -88545,17 +88833,19 @@ export function plugin(context: PluginContext): PluginImplementation {
|
|
|
88545
88833
|
version: '0.1.0',
|
|
88546
88834
|
permissions: { network: [], secrets: [], models: [] },
|
|
88547
88835
|
ports: {},
|
|
88548
|
-
commands:
|
|
88549
|
-
|
|
88550
|
-
|
|
88551
|
-
|
|
88552
|
-
|
|
88553
|
-
|
|
88836
|
+
commands: [
|
|
88837
|
+
defineCommand({
|
|
88838
|
+
name: 'hello',
|
|
88839
|
+
summary: 'Greet the name given, or the workspace.',
|
|
88840
|
+
args: [{ name: 'name', summary: 'The name to greet.' }],
|
|
88841
|
+
run: async ({ args }) => \`Hello, \${args.name ?? context.init.node.workspaceDir}.\`,
|
|
88842
|
+
}),
|
|
88843
|
+
],
|
|
88554
88844
|
};
|
|
88555
88845
|
}
|
|
88556
88846
|
`,
|
|
88557
88847
|
fixtures: {},
|
|
88558
|
-
test: ` assert.equal(await implementation.commands?.
|
|
88848
|
+
test: ` assert.equal(await implementation.commands?.[0]?.run({ args: { name: 'Ada' }, options: {}, terminal: null }), 'Hello, Ada.');`
|
|
88559
88849
|
};
|
|
88560
88850
|
}
|
|
88561
88851
|
}
|
|
@@ -88589,10 +88879,11 @@ ${template.test ? `${template.test}
|
|
|
88589
88879
|
` : ""}});
|
|
88590
88880
|
`;
|
|
88591
88881
|
}
|
|
88882
|
+
var COMMAND_DECLARATION = { name: "hello", summary: "Greet the name given, or the workspace.", args: [{ name: "name", summary: "The name to greet." }] };
|
|
88592
88883
|
function pluginFiles(name, port) {
|
|
88593
88884
|
const template = templateOf(name, port);
|
|
88594
88885
|
const files = {
|
|
88595
|
-
"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)}
|
|
88886
|
+
"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)}
|
|
88596
88887
|
`,
|
|
88597
88888
|
"package.json": `${JSON.stringify({
|
|
88598
88889
|
name,
|
|
@@ -88650,14 +88941,14 @@ var pluginNewCommand = {
|
|
|
88650
88941
|
if (port === "harness" && !HARNESS_ID.test(name)) {
|
|
88651
88942
|
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." });
|
|
88652
88943
|
}
|
|
88653
|
-
const dir =
|
|
88654
|
-
if (
|
|
88944
|
+
const dir = resolve16(typeof input2.flags.dir === "string" ? input2.flags.dir : name);
|
|
88945
|
+
if (existsSync31(dir) && readdirSync20(dir).length > 0) {
|
|
88655
88946
|
throw new CliError({ code: "conflict", message: `${dir} is not empty.`, exitCode: ExitCode.Validation, hint: "Name a new folder with --dir." });
|
|
88656
88947
|
}
|
|
88657
88948
|
const files = pluginFiles(name, port);
|
|
88658
88949
|
for (const [path3, content] of Object.entries(files)) {
|
|
88659
|
-
mkdirSync22(dirname16(
|
|
88660
|
-
writeFileSync20(
|
|
88950
|
+
mkdirSync22(dirname16(join38(dir, path3)), { recursive: true });
|
|
88951
|
+
writeFileSync20(join38(dir, path3), content);
|
|
88661
88952
|
}
|
|
88662
88953
|
const program = commandPrefix(context.env);
|
|
88663
88954
|
const shown = relative7(process.cwd(), dir) || ".";
|
|
@@ -88802,8 +89093,8 @@ If the browser does not return here in ${CALLBACK_WAIT_MS / 6e4} minutes, paste
|
|
|
88802
89093
|
${url2}
|
|
88803
89094
|
Then paste the code, or the full URL the browser ends on.`);
|
|
88804
89095
|
},
|
|
88805
|
-
onManualCodeInput: () => new Promise((
|
|
88806
|
-
timer = setTimeout(() =>
|
|
89096
|
+
onManualCodeInput: () => new Promise((resolve27) => {
|
|
89097
|
+
timer = setTimeout(() => resolve27(""), opened2 ? CALLBACK_WAIT_MS : 0);
|
|
88807
89098
|
timer.unref();
|
|
88808
89099
|
}),
|
|
88809
89100
|
onPrompt: async (prompt) => (await context.ask(`${prompt.message} `))?.trim() ?? "",
|
|
@@ -88897,14 +89188,14 @@ function createProviderCommands(signIn = loginAnthropic) {
|
|
|
88897
89188
|
|
|
88898
89189
|
// src/core/cli/run.ts
|
|
88899
89190
|
init_command();
|
|
88900
|
-
import { resolve as
|
|
89191
|
+
import { resolve as resolve18 } from "node:path";
|
|
88901
89192
|
|
|
88902
89193
|
// src/core/local/local-run.ts
|
|
88903
89194
|
init_protocol();
|
|
88904
89195
|
init_command();
|
|
88905
89196
|
import { randomBytes as randomBytes4, randomUUID as randomUUID8 } from "node:crypto";
|
|
88906
89197
|
import { mkdirSync as mkdirSync23, rmSync as rmSync12 } from "node:fs";
|
|
88907
|
-
import { join as
|
|
89198
|
+
import { join as join39, resolve as resolve17 } from "node:path";
|
|
88908
89199
|
|
|
88909
89200
|
// src/core/local/interrupt.ts
|
|
88910
89201
|
var EXIT_CODE = { SIGINT: 130, SIGTERM: 143 };
|
|
@@ -88927,7 +89218,7 @@ function cleanupOnInterrupt(cleanup, options = {}) {
|
|
|
88927
89218
|
async function runLocal(context, request, plugins) {
|
|
88928
89219
|
const { workspaceDir } = request;
|
|
88929
89220
|
const home = orbitalHome(context.env);
|
|
88930
|
-
const sessionsDir =
|
|
89221
|
+
const sessionsDir = join39(home, "sessions");
|
|
88931
89222
|
mkdirSync23(sessionsDir, { recursive: true, mode: 448 });
|
|
88932
89223
|
const resumed = request.resumeSessionId ? readSessionRecord(sessionsDir, request.resumeSessionId) : null;
|
|
88933
89224
|
if (request.resumeSessionId && !resumed) {
|
|
@@ -88957,9 +89248,9 @@ async function runLocal(context, request, plugins) {
|
|
|
88957
89248
|
let channels = null;
|
|
88958
89249
|
const asks = new PersonDesk(async (question, name) => await channels?.askPerson(`${name} asks: ${question}`, null) ?? null);
|
|
88959
89250
|
const policy = new PolicyChain(ports.policies(), asks.ask);
|
|
88960
|
-
channels = new ChannelHub({ context, ports, policy, self: nodeName, stateDir:
|
|
89251
|
+
channels = new ChannelHub({ context, ports, policy, self: nodeName, stateDir: join39(home, "channels", nodeName), plugins: () => definition.config.plugins });
|
|
88961
89252
|
const models = modelGatewayOf(context, policy, remote);
|
|
88962
|
-
const actions = nodeActions({ ports: () => ports, policy: () => policy, definition: { workspaceDir:
|
|
89253
|
+
const actions = nodeActions({ ports: () => ports, policy: () => policy, definition: { workspaceDir: resolve17(workspaceDir), definition }, span: (span2) => host.telemetry.span(span2), now: context.now });
|
|
88963
89254
|
const agentName = request.agent ?? resumed?.agent ?? null;
|
|
88964
89255
|
const target = await resolveTarget(definition, agentName, ports);
|
|
88965
89256
|
const harness = request.harness ?? resumed?.harness ?? defaultHarness(context.env);
|
|
@@ -89020,7 +89311,7 @@ async function runLocal(context, request, plugins) {
|
|
|
89020
89311
|
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 });
|
|
89021
89312
|
if (result2 !== null) context.output.line(result2);
|
|
89022
89313
|
const cli = commandPrefix(context.env);
|
|
89023
|
-
context.output.step(`\u2713 transcript: ${
|
|
89314
|
+
context.output.step(`\u2713 transcript: ${join39(sessionsDir, sessionId, "conversation.jsonl")}`);
|
|
89024
89315
|
context.output.step(`\u2713 resume with: ${cli} run --resume ${sessionId}`);
|
|
89025
89316
|
return {
|
|
89026
89317
|
sessionId,
|
|
@@ -89028,7 +89319,7 @@ async function runLocal(context, request, plugins) {
|
|
|
89028
89319
|
done: true,
|
|
89029
89320
|
harness,
|
|
89030
89321
|
result: result2,
|
|
89031
|
-
transcript:
|
|
89322
|
+
transcript: join39(sessionsDir, sessionId, "conversation.jsonl"),
|
|
89032
89323
|
interactive: request.interactive
|
|
89033
89324
|
};
|
|
89034
89325
|
} finally {
|
|
@@ -89088,7 +89379,7 @@ async function interactiveTurn(context, request, run3) {
|
|
|
89088
89379
|
const info = sessionInfoOf(run3.sessionId, run3.target, { node: run3.host.node, agent: run3.target.local?.name ?? run3.target.ref.name, parentSessionId: null, workspaceDir: request.workspaceDir, harness: run3.harness, team: null, mission: null, definition: request.useDefinition ? run3.definition : null });
|
|
89089
89380
|
const opened2 = await openSession(run3.ports, run3.policy, info);
|
|
89090
89381
|
const record2 = run3.host.attach(info, opened2.sinks, { agent: info.agent, harness: run3.harness, workspaceDir: request.workspaceDir });
|
|
89091
|
-
const runDir =
|
|
89382
|
+
const runDir = join39(run3.home, "runs", randomUUID8());
|
|
89092
89383
|
mkdirSync23(runDir, { recursive: true, mode: 448 });
|
|
89093
89384
|
let usage = [];
|
|
89094
89385
|
try {
|
|
@@ -89195,7 +89486,7 @@ function createRunCommand(options) {
|
|
|
89195
89486
|
const terminal2 = log ? await options.terminal() : null;
|
|
89196
89487
|
const data = await runLocal(log ? { ...context, output: log.output(context.output) } : context, {
|
|
89197
89488
|
agent,
|
|
89198
|
-
workspaceDir:
|
|
89489
|
+
workspaceDir: resolve18(typeof input2.flags.workspace === "string" ? input2.flags.workspace : "."),
|
|
89199
89490
|
prompt,
|
|
89200
89491
|
useDefinition: true,
|
|
89201
89492
|
follow: false,
|
|
@@ -89213,24 +89504,24 @@ function createRunCommand(options) {
|
|
|
89213
89504
|
// src/core/cli/session.ts
|
|
89214
89505
|
init_command();
|
|
89215
89506
|
init_protocol();
|
|
89216
|
-
import { resolve as
|
|
89507
|
+
import { resolve as resolve19 } from "node:path";
|
|
89217
89508
|
|
|
89218
89509
|
// src/core/local/attachments.ts
|
|
89219
|
-
import { existsSync as
|
|
89220
|
-
import { join as
|
|
89510
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync30, writeFileSync as writeFileSync21 } from "node:fs";
|
|
89511
|
+
import { join as join40 } from "node:path";
|
|
89221
89512
|
function fileOf(home, externalId) {
|
|
89222
|
-
return
|
|
89513
|
+
return join40(home, "attachments", `${encodeURIComponent(externalId)}.json`);
|
|
89223
89514
|
}
|
|
89224
89515
|
function writeAttachment(home, externalId, node2) {
|
|
89225
|
-
mkdirSync24(
|
|
89516
|
+
mkdirSync24(join40(home, "attachments"), { recursive: true, mode: 448 });
|
|
89226
89517
|
writeFileSync21(fileOf(home, externalId), `${JSON.stringify({ node: node2 })}
|
|
89227
89518
|
`, { mode: 384 });
|
|
89228
89519
|
}
|
|
89229
89520
|
function readAttachment(home, externalId) {
|
|
89230
89521
|
const file2 = fileOf(home, externalId);
|
|
89231
|
-
if (!
|
|
89522
|
+
if (!existsSync33(file2)) return null;
|
|
89232
89523
|
try {
|
|
89233
|
-
const parsed = JSON.parse(
|
|
89524
|
+
const parsed = JSON.parse(readFileSync30(file2, "utf8"));
|
|
89234
89525
|
return typeof parsed === "object" && parsed !== null && "node" in parsed && typeof parsed.node === "string" ? parsed.node : null;
|
|
89235
89526
|
} catch {
|
|
89236
89527
|
return null;
|
|
@@ -89298,7 +89589,7 @@ async function joinMission(context, attached, missionId) {
|
|
|
89298
89589
|
const body2 = await response.json();
|
|
89299
89590
|
if (!response.ok) {
|
|
89300
89591
|
const error62 = typeof body2 === "object" && body2 !== null && "error" in body2 ? String(body2.error) : `The node answered ${response.status}.`;
|
|
89301
|
-
throw new CliError({ code: "invalid-input", message: `The node refused to make this session
|
|
89592
|
+
throw new CliError({ code: "invalid-input", message: `The node refused to make this session a decision receiver of ${missionId}: ${error62}`, exitCode: ExitCode.Validation });
|
|
89302
89593
|
}
|
|
89303
89594
|
return MissionRecordSchema.parse(body2);
|
|
89304
89595
|
}
|
|
@@ -89378,7 +89669,7 @@ var sessionAttachCommand = {
|
|
|
89378
89669
|
{ 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." },
|
|
89379
89670
|
{ name: "team", type: "string", summary: "The team the session joins. Sessions of one team are peers." },
|
|
89380
89671
|
{ name: "external-id", type: "string", summary: "The id the outside program gives the session. With it, the command attaches again after a node restarts." },
|
|
89381
|
-
{ name: "mission", type: "string", summary: "A mission id: join the node that runs it, from any folder, as
|
|
89672
|
+
{ 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." }
|
|
89382
89673
|
],
|
|
89383
89674
|
positionals: [],
|
|
89384
89675
|
outputShape: {
|
|
@@ -89395,7 +89686,7 @@ var sessionAttachCommand = {
|
|
|
89395
89686
|
examples: [
|
|
89396
89687
|
{ command: "orbital session attach --harness claude-code --output ndjson", why: "Join the node of this folder, and print each delivery as a record." },
|
|
89397
89688
|
{ command: "orbital session attach --node shared --team review --output ndjson", why: "Join the node shared, as a member of the team review." },
|
|
89398
|
-
{ command: "orbital session attach --mission build/pr-7 --output ndjson", why: "Join the node that runs the mission build/pr-7 as
|
|
89689
|
+
{ 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." }
|
|
89399
89690
|
],
|
|
89400
89691
|
run: async (input2, context) => {
|
|
89401
89692
|
const harness = typeof input2.flags.harness === "string" ? input2.flags.harness : "external";
|
|
@@ -89407,7 +89698,7 @@ var sessionAttachCommand = {
|
|
|
89407
89698
|
const found = await keepAttached(context, {
|
|
89408
89699
|
harness,
|
|
89409
89700
|
agent: typeof input2.flags.agent === "string" ? input2.flags.agent : harness,
|
|
89410
|
-
workspaceDir:
|
|
89701
|
+
workspaceDir: resolve19(typeof input2.flags.workspace === "string" ? input2.flags.workspace : "."),
|
|
89411
89702
|
...typeof input2.flags.node === "string" && { node: input2.flags.node },
|
|
89412
89703
|
team: typeof input2.flags.team === "string" ? input2.flags.team : null,
|
|
89413
89704
|
externalId: typeof input2.flags["external-id"] === "string" ? input2.flags["external-id"] : null,
|
|
@@ -89420,6 +89711,8 @@ var sessionAttachCommand = {
|
|
|
89420
89711
|
delivery: (event) => {
|
|
89421
89712
|
const delivery = DeliverySchema.safeParse(event);
|
|
89422
89713
|
if (delivery.success) context.output.record({ type: StreamRecordType.Event, ...delivery.data });
|
|
89714
|
+
const answered = AnsweredSchema.safeParse(event);
|
|
89715
|
+
if (answered.success) context.output.record({ type: StreamRecordType.Event, ...answered.data });
|
|
89423
89716
|
},
|
|
89424
89717
|
detached: (reason) => {
|
|
89425
89718
|
context.output.record({ type: StreamRecordType.Event, kind: "detached", reason });
|
|
@@ -89509,8 +89802,8 @@ var workspaceCommands = [list5, show, remove3];
|
|
|
89509
89802
|
|
|
89510
89803
|
// src/core/cli/up.ts
|
|
89511
89804
|
init_command();
|
|
89512
|
-
import { existsSync as
|
|
89513
|
-
import { resolve as
|
|
89805
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
89806
|
+
import { resolve as resolve20 } from "node:path";
|
|
89514
89807
|
init_protocol();
|
|
89515
89808
|
|
|
89516
89809
|
// src/core/cli/up-view.ts
|
|
@@ -89692,7 +89985,7 @@ async function liveView(context, nodes, signal, terminal2, home) {
|
|
|
89692
89985
|
for (const line of events(viewed, states.get(one.node), now)) context.output.line(line);
|
|
89693
89986
|
states.set(one.node, now);
|
|
89694
89987
|
}
|
|
89695
|
-
await Promise.race([context.sleep(REFRESH_MS), new Promise((
|
|
89988
|
+
await Promise.race([context.sleep(REFRESH_MS), new Promise((resolve27) => signal.addEventListener("abort", () => resolve27(), { once: true }))]);
|
|
89696
89989
|
}
|
|
89697
89990
|
context.output.line(closing);
|
|
89698
89991
|
}
|
|
@@ -89701,7 +89994,7 @@ async function liveView(context, nodes, signal, terminal2, home) {
|
|
|
89701
89994
|
var WORKSPACE_FLAG = { name: "workspace", type: "path", summary: "The project. Default: the current directory." };
|
|
89702
89995
|
var TEAMS = { name: "teams", type: "string", variadic: true, summary: "Only the nodes of these teams. Default: every node of the project." };
|
|
89703
89996
|
async function planOf(context, input2) {
|
|
89704
|
-
const workspaceDir =
|
|
89997
|
+
const workspaceDir = resolve20(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
89705
89998
|
const { definition, refusals } = await loadDefinitionPartial(workspaceDir, context.env);
|
|
89706
89999
|
for (const refusal of refusals) context.output.warn(refusal.message);
|
|
89707
90000
|
const project = projectNodeOf(definition, workspaceDir);
|
|
@@ -89848,7 +90141,7 @@ var statusCommand = {
|
|
|
89848
90141
|
for (const { charters: _charters, named: _named, ...planned } of nodes) {
|
|
89849
90142
|
const log = nodeLogPath(home, planned.node);
|
|
89850
90143
|
const supervisor = readSupervisor(home, planned.node);
|
|
89851
|
-
if (
|
|
90144
|
+
if (existsSync34(log)) logs.push({ node: planned.node, log, lastCrash: supervisor?.crashes.at(-1) ?? null, lastExit: supervisor?.lastExit ?? null, gaveUp: supervisor?.gaveUp ?? null });
|
|
89852
90145
|
const running = await liveNode(home, planned.node, context.fetch);
|
|
89853
90146
|
const served = running ? await servedTeams(context, running) : null;
|
|
89854
90147
|
for (const note of served ?? []) if (note.problems.length > 0 || note.retiring) notes.push({ node: planned.node, ...note });
|
|
@@ -89881,7 +90174,7 @@ var downCommand = {
|
|
|
89881
90174
|
const { workspaceDir, nodes } = await planOf(context, input2);
|
|
89882
90175
|
const home = orbitalHome(context.env);
|
|
89883
90176
|
const rows = [];
|
|
89884
|
-
const separate2 = (await liveNodes(home, context.fetch)).filter((info) =>
|
|
90177
|
+
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)));
|
|
89885
90178
|
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 ?? [] }))];
|
|
89886
90179
|
for (const planned of targets) {
|
|
89887
90180
|
const running = await liveNode(home, planned.node, context.fetch);
|
|
@@ -89940,7 +90233,7 @@ var createJoinCommand = (options) => ({
|
|
|
89940
90233
|
}
|
|
89941
90234
|
let charters = [];
|
|
89942
90235
|
try {
|
|
89943
|
-
const workspaceDir =
|
|
90236
|
+
const workspaceDir = resolve20(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
89944
90237
|
charters = (await loadDefinition(workspaceDir, context.env)).teams;
|
|
89945
90238
|
} catch {
|
|
89946
90239
|
charters = [];
|
|
@@ -89966,9 +90259,9 @@ function createUpCommands(options) {
|
|
|
89966
90259
|
init_command();
|
|
89967
90260
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
89968
90261
|
import { createHash as createHash10 } from "node:crypto";
|
|
89969
|
-
import { existsSync as
|
|
90262
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync25, rmSync as rmSync13, writeFileSync as writeFileSync22 } from "node:fs";
|
|
89970
90263
|
import { homedir as homedir8 } from "node:os";
|
|
89971
|
-
import { join as
|
|
90264
|
+
import { join as join41, resolve as resolve21 } from "node:path";
|
|
89972
90265
|
var exec = (command, args) => {
|
|
89973
90266
|
const result2 = spawnSync5(command, args, { encoding: "utf8" });
|
|
89974
90267
|
return { status: result2.status, output: `${result2.stdout ?? ""}${result2.stderr ?? ""}${result2.error?.message ?? ""}` };
|
|
@@ -89986,7 +90279,7 @@ function serviceSpec(env3, workspaceDir) {
|
|
|
89986
90279
|
workspaceDir,
|
|
89987
90280
|
program: [process.execPath, installedProgram(), ...mountPath(), "up", "--detach", "--workspace", workspaceDir],
|
|
89988
90281
|
env: Object.fromEntries(KEPT_ENV.flatMap((name) => env3[name] ? [[name, env3[name]]] : [])),
|
|
89989
|
-
log:
|
|
90282
|
+
log: join41(orbitalHome(env3), "logs", `service-${hash2}.log`)
|
|
89990
90283
|
};
|
|
89991
90284
|
}
|
|
89992
90285
|
function xml(text3) {
|
|
@@ -90049,7 +90342,7 @@ function userHome(env3) {
|
|
|
90049
90342
|
return env3.HOME ?? homedir8();
|
|
90050
90343
|
}
|
|
90051
90344
|
function fileOf2(platform2, env3, label) {
|
|
90052
|
-
return platform2 === "launchd" ?
|
|
90345
|
+
return platform2 === "launchd" ? join41(userHome(env3), "Library", "LaunchAgents", `${label}.plist`) : join41(env3.XDG_CONFIG_HOME ?? join41(userHome(env3), ".config"), "systemd", "user", `${label}.service`);
|
|
90053
90346
|
}
|
|
90054
90347
|
function domain2() {
|
|
90055
90348
|
return `gui/${process.getuid?.() ?? 0}`;
|
|
@@ -90062,13 +90355,13 @@ function serviceState(platform2, env3, workspaceDir, run3 = exec) {
|
|
|
90062
90355
|
const { label } = serviceSpec(env3, workspaceDir);
|
|
90063
90356
|
const file2 = fileOf2(platform2, env3, label);
|
|
90064
90357
|
const loaded = platform2 === "launchd" ? run3("launchctl", ["print", `${domain2()}/${label}`]).status === 0 : run3("systemctl", ["--user", "is-enabled", `${label}.service`]).status === 0;
|
|
90065
|
-
return { platform: platform2, label, file: file2, installed:
|
|
90358
|
+
return { platform: platform2, label, file: file2, installed: existsSync35(file2), loaded };
|
|
90066
90359
|
}
|
|
90067
90360
|
function installService(platform2, env3, workspaceDir, run3 = exec) {
|
|
90068
90361
|
const spec = serviceSpec(env3, workspaceDir);
|
|
90069
90362
|
const file2 = fileOf2(platform2, env3, spec.label);
|
|
90070
|
-
mkdirSync25(
|
|
90071
|
-
mkdirSync25(
|
|
90363
|
+
mkdirSync25(join41(file2, ".."), { recursive: true });
|
|
90364
|
+
mkdirSync25(join41(spec.log, ".."), { recursive: true, mode: 448 });
|
|
90072
90365
|
if (platform2 === "launchd") {
|
|
90073
90366
|
writeFileSync22(file2, launchdPlist(spec));
|
|
90074
90367
|
run3("launchctl", ["bootout", `${domain2()}/${spec.label}`]);
|
|
@@ -90090,7 +90383,7 @@ function uninstallService(platform2, env3, workspaceDir, run3 = exec) {
|
|
|
90090
90383
|
return serviceState(platform2, env3, workspaceDir, run3);
|
|
90091
90384
|
}
|
|
90092
90385
|
function workspaceOf2(input2) {
|
|
90093
|
-
return
|
|
90386
|
+
return resolve21(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
90094
90387
|
}
|
|
90095
90388
|
function platformOrRefuse() {
|
|
90096
90389
|
const platform2 = platformOf();
|
|
@@ -90165,9 +90458,9 @@ var serviceCommands = [
|
|
|
90165
90458
|
];
|
|
90166
90459
|
|
|
90167
90460
|
// src/core/cli/browser.ts
|
|
90168
|
-
import { existsSync as
|
|
90461
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync31, writeFileSync as writeFileSync23 } from "node:fs";
|
|
90169
90462
|
import { homedir as homedir9 } from "node:os";
|
|
90170
|
-
import { dirname as dirname17, join as
|
|
90463
|
+
import { dirname as dirname17, join as join42 } from "node:path";
|
|
90171
90464
|
|
|
90172
90465
|
// ../../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
|
|
90173
90466
|
function normalizeHeaders(headers) {
|
|
@@ -91782,7 +92075,7 @@ function isChromeDevtools(entry) {
|
|
|
91782
92075
|
return isChromeDevtoolsMcp(command, args);
|
|
91783
92076
|
}
|
|
91784
92077
|
function claudeJsonPath(env3) {
|
|
91785
|
-
return
|
|
92078
|
+
return join42(env3.CLAUDE_CONFIG_DIR ?? env3.HOME ?? homedir9(), ".claude.json");
|
|
91786
92079
|
}
|
|
91787
92080
|
function chromeServersOf(config2) {
|
|
91788
92081
|
return Object.entries(config2.mcpServers ?? {}).filter(([, entry]) => isChromeDevtools(entry)).map(([name]) => name);
|
|
@@ -91794,21 +92087,21 @@ function sharedBrowserEntry() {
|
|
|
91794
92087
|
return { type: "stdio", ...sharedBrowserCommand(), env: {} };
|
|
91795
92088
|
}
|
|
91796
92089
|
function backupPath(env3) {
|
|
91797
|
-
return
|
|
92090
|
+
return join42(browserDir(orbitalHome(env3)), "claude-mcp-backup.json");
|
|
91798
92091
|
}
|
|
91799
92092
|
function codexBackupPath(env3) {
|
|
91800
|
-
return
|
|
92093
|
+
return join42(browserDir(orbitalHome(env3)), "codex-config-backup.json");
|
|
91801
92094
|
}
|
|
91802
92095
|
function codexConfigPath(env3) {
|
|
91803
|
-
return
|
|
92096
|
+
return join42(env3.CODEX_HOME ?? join42(env3.HOME ?? homedir9(), ".codex"), "config.toml");
|
|
91804
92097
|
}
|
|
91805
92098
|
function pointClaudeAtSharedBrowser(env3, entry = sharedBrowserEntry()) {
|
|
91806
92099
|
const file2 = claudeJsonPath(env3);
|
|
91807
|
-
const config2 =
|
|
92100
|
+
const config2 = existsSync36(file2) ? JSON.parse(readFileSync31(file2, "utf8")) : {};
|
|
91808
92101
|
const found = chromeServersOf(config2);
|
|
91809
92102
|
const names = found.length > 0 ? found : [SHARED_BROWSER_SERVER];
|
|
91810
92103
|
const backup = backupPath(env3);
|
|
91811
|
-
const previous =
|
|
92104
|
+
const previous = existsSync36(backup) ? JSON.parse(readFileSync31(backup, "utf8")) : {};
|
|
91812
92105
|
const saved = { ...Object.fromEntries(names.filter((name) => config2.mcpServers?.[name] !== void 0).map((name) => [name, config2.mcpServers[name]])), ...previous };
|
|
91813
92106
|
mkdirSync26(browserDir(orbitalHome(env3)), { recursive: true, mode: 448 });
|
|
91814
92107
|
writeFileSync23(backup, `${JSON.stringify(saved, null, 2)}
|
|
@@ -91821,9 +92114,9 @@ function pointClaudeAtSharedBrowser(env3, entry = sharedBrowserEntry()) {
|
|
|
91821
92114
|
function revertClaudeBrowser(env3) {
|
|
91822
92115
|
const file2 = claudeJsonPath(env3);
|
|
91823
92116
|
const backup = backupPath(env3);
|
|
91824
|
-
if (!
|
|
91825
|
-
const saved = JSON.parse(
|
|
91826
|
-
const config2 =
|
|
92117
|
+
if (!existsSync36(backup)) return null;
|
|
92118
|
+
const saved = JSON.parse(readFileSync31(backup, "utf8"));
|
|
92119
|
+
const config2 = existsSync36(file2) ? JSON.parse(readFileSync31(file2, "utf8")) : {};
|
|
91827
92120
|
const shared = Object.entries(config2.mcpServers ?? {}).filter(([, entry]) => Array.isArray(entry.args) && entry.args.join(" ").endsWith("browser connect")).map(([name]) => name);
|
|
91828
92121
|
const servers = { ...config2.mcpServers };
|
|
91829
92122
|
for (const name of shared) delete servers[name];
|
|
@@ -91834,18 +92127,18 @@ function revertClaudeBrowser(env3) {
|
|
|
91834
92127
|
}
|
|
91835
92128
|
function pointCodexAtSharedBrowser(env3, shared = sharedBrowserCommand()) {
|
|
91836
92129
|
const file2 = codexConfigPath(env3);
|
|
91837
|
-
if (!
|
|
91838
|
-
const text3 =
|
|
92130
|
+
if (!existsSync36(file2) && !existsSync36(dirname17(file2))) return null;
|
|
92131
|
+
const text3 = existsSync36(file2) ? readFileSync31(file2, "utf8") : "";
|
|
91839
92132
|
const tables = mcpServerTables(text3);
|
|
91840
92133
|
const found = tables.filter((table2) => isChromeDevtoolsMcp(table2.command, table2.args)).map((table2) => table2.name);
|
|
91841
92134
|
const connected = tables.filter((table2) => isSharedBrowserConnect(table2.args)).map((table2) => table2.name);
|
|
91842
92135
|
const names = found.length > 0 ? found : connected.length > 0 ? [] : [SHARED_BROWSER_SERVER];
|
|
91843
92136
|
const backup = codexBackupPath(env3);
|
|
91844
|
-
if (names.length === 0) return { file: file2, servers: connected, backup:
|
|
92137
|
+
if (names.length === 0) return { file: file2, servers: connected, backup: existsSync36(backup) ? backup : null };
|
|
91845
92138
|
const block = (name) => stdioServerBlock(name, shared.command, shared.args);
|
|
91846
92139
|
const replaced = replaceMcpServers(text3, (table2) => names.includes(table2.name) ? block(table2.name) : null);
|
|
91847
92140
|
const next = names.filter((name) => !replaced.replaced.some((one) => one.name === name)).reduce((current2, name) => appendMcpServer(current2, block(name)), replaced.text);
|
|
91848
|
-
const previous =
|
|
92141
|
+
const previous = existsSync36(backup) ? JSON.parse(readFileSync31(backup, "utf8")) : {};
|
|
91849
92142
|
const saved = { ...Object.fromEntries(replaced.replaced.map((one) => [one.name, one.original])), ...previous };
|
|
91850
92143
|
mkdirSync26(browserDir(orbitalHome(env3)), { recursive: true, mode: 448 });
|
|
91851
92144
|
writeFileSync23(backup, `${JSON.stringify(saved, null, 2)}
|
|
@@ -91857,9 +92150,9 @@ function pointCodexAtSharedBrowser(env3, shared = sharedBrowserCommand()) {
|
|
|
91857
92150
|
function revertCodexBrowser(env3) {
|
|
91858
92151
|
const file2 = codexConfigPath(env3);
|
|
91859
92152
|
const backup = codexBackupPath(env3);
|
|
91860
|
-
if (!
|
|
91861
|
-
const saved = JSON.parse(
|
|
91862
|
-
const text3 =
|
|
92153
|
+
if (!existsSync36(backup)) return null;
|
|
92154
|
+
const saved = JSON.parse(readFileSync31(backup, "utf8"));
|
|
92155
|
+
const text3 = existsSync36(file2) ? readFileSync31(file2, "utf8") : "";
|
|
91863
92156
|
const replaced = replaceMcpServers(text3, (table2) => isSharedBrowserConnect(table2.args) ? saved[table2.name] ?? "" : null);
|
|
91864
92157
|
const next = Object.entries(saved).filter(([name]) => !replaced.replaced.some((one) => one.name === name)).reduce((current2, [, original]) => appendMcpServer(current2, original), replaced.text);
|
|
91865
92158
|
mkdirSync26(dirname17(file2), { recursive: true });
|
|
@@ -92047,13 +92340,13 @@ var browserCommands = [
|
|
|
92047
92340
|
init_command();
|
|
92048
92341
|
init_structure();
|
|
92049
92342
|
init_protocol();
|
|
92050
|
-
import { existsSync as
|
|
92051
|
-
import { join as
|
|
92343
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
92344
|
+
import { join as join44, relative as relative8, resolve as resolve22 } from "node:path";
|
|
92052
92345
|
|
|
92053
92346
|
// src/core/local/mission-migrate.ts
|
|
92054
92347
|
init_protocol();
|
|
92055
|
-
import { existsSync as
|
|
92056
|
-
import { join as
|
|
92348
|
+
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";
|
|
92349
|
+
import { join as join43 } from "node:path";
|
|
92057
92350
|
function withTeam(content, team) {
|
|
92058
92351
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(\r?\n[\s\S]*)?$/);
|
|
92059
92352
|
if (!match) return content;
|
|
@@ -92065,26 +92358,26 @@ ${head.join("\n")}
|
|
|
92065
92358
|
---${match[2] ?? "\n"}`;
|
|
92066
92359
|
}
|
|
92067
92360
|
function emptyDir(dir) {
|
|
92068
|
-
if (
|
|
92361
|
+
if (existsSync37(dir) && readdirSync21(dir).length === 0) rmdirSync2(dir);
|
|
92069
92362
|
}
|
|
92070
92363
|
function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
92071
92364
|
const moved = [];
|
|
92072
92365
|
const refused3 = [];
|
|
92073
|
-
const teamsDir =
|
|
92074
|
-
const target =
|
|
92366
|
+
const teamsDir = join43(root, TEAMS_DIR);
|
|
92367
|
+
const target = join43(root, MISSIONS_DIR);
|
|
92075
92368
|
const seen = /* @__PURE__ */ new Map();
|
|
92076
|
-
for (const team of
|
|
92077
|
-
const missionsDir =
|
|
92078
|
-
if (!
|
|
92369
|
+
for (const team of existsSync37(teamsDir) ? readdirSync21(teamsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()) : []) {
|
|
92370
|
+
const missionsDir = join43(teamsDir, team.name, MISSIONS_DIR);
|
|
92371
|
+
if (!existsSync37(missionsDir)) continue;
|
|
92079
92372
|
for (const mission of readdirSync21(missionsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory())) {
|
|
92080
|
-
const from =
|
|
92081
|
-
const to =
|
|
92373
|
+
const from = join43(missionsDir, mission.name);
|
|
92374
|
+
const to = join43(target, mission.name);
|
|
92082
92375
|
const other = seen.get(mission.name);
|
|
92083
92376
|
if (other) {
|
|
92084
92377
|
refused3.push(`${from}: the team ${other} has a mission ${mission.name} too. Rename one folder and its mission: key, then run the migration again.`);
|
|
92085
92378
|
continue;
|
|
92086
92379
|
}
|
|
92087
|
-
if (
|
|
92380
|
+
if (existsSync37(to)) {
|
|
92088
92381
|
refused3.push(`${from}: ${to} exists already.`);
|
|
92089
92382
|
continue;
|
|
92090
92383
|
}
|
|
@@ -92093,22 +92386,22 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
92093
92386
|
if (dryRun) continue;
|
|
92094
92387
|
mkdirSync27(target, { recursive: true });
|
|
92095
92388
|
renameSync8(from, to);
|
|
92096
|
-
const file2 =
|
|
92097
|
-
if (
|
|
92389
|
+
const file2 = join43(to, MISSION_FILE);
|
|
92390
|
+
if (existsSync37(file2)) writeFileSync24(file2, withTeam(readFileSync32(file2, "utf8"), team.name));
|
|
92098
92391
|
}
|
|
92099
92392
|
if (!dryRun) emptyDir(missionsDir);
|
|
92100
92393
|
}
|
|
92101
92394
|
for (const node2 of nodes) {
|
|
92102
|
-
const nodeDir2 =
|
|
92103
|
-
if (!
|
|
92395
|
+
const nodeDir2 = join43(nodesDir, node2);
|
|
92396
|
+
if (!existsSync37(nodeDir2)) continue;
|
|
92104
92397
|
const files = [];
|
|
92105
92398
|
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) {
|
|
92106
|
-
if (entry.isFile() && entry.name.endsWith(".json")) files.push({ from:
|
|
92107
|
-
if (entry.isFile() && entry.name.endsWith(".json.migrated")) files.push({ from:
|
|
92399
|
+
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 });
|
|
92400
|
+
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 });
|
|
92108
92401
|
if (!entry.isDirectory()) continue;
|
|
92109
|
-
for (const name of readdirSync21(
|
|
92110
|
-
if (name.endsWith(".json")) files.push({ from:
|
|
92111
|
-
if (name.endsWith(".json.migrated")) files.push({ from:
|
|
92402
|
+
for (const name of readdirSync21(join43(nodeDir2, entry.name))) {
|
|
92403
|
+
if (name.endsWith(".json")) files.push({ from: join43(nodeDir2, entry.name, name), mission: name.slice(0, -".json".length), team: entry.name, backup: false });
|
|
92404
|
+
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 });
|
|
92112
92405
|
}
|
|
92113
92406
|
}
|
|
92114
92407
|
files.sort((a, b) => Number(a.backup) - Number(b.backup) || Number(a.team !== null) - Number(b.team !== null));
|
|
@@ -92120,13 +92413,13 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
92120
92413
|
if (!dryRun) rmSync14(file2.from);
|
|
92121
92414
|
continue;
|
|
92122
92415
|
}
|
|
92123
|
-
if (taken.has(file2.mission) ||
|
|
92416
|
+
if (taken.has(file2.mission) || existsSync37(to)) {
|
|
92124
92417
|
refused3.push(`${file2.from}: ${to} exists already.`);
|
|
92125
92418
|
continue;
|
|
92126
92419
|
}
|
|
92127
|
-
const raw = JSON.parse(
|
|
92420
|
+
const raw = JSON.parse(readFileSync32(file2.from, "utf8"));
|
|
92128
92421
|
const { file: _file2, ...rest } = raw;
|
|
92129
|
-
const state = MissionStateFileSchema.safeParse({ question: null,
|
|
92422
|
+
const state = MissionStateFileSchema.safeParse({ question: null, lead: null, claim: null, claimedAt: null, ...rest, mission: file2.mission, node: node2, team: rest.team ?? file2.team });
|
|
92130
92423
|
if (!state.success) {
|
|
92131
92424
|
refused3.push(`${file2.from}: it is not the state of a mission: ${state.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ")}`);
|
|
92132
92425
|
continue;
|
|
@@ -92138,7 +92431,7 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
92138
92431
|
rmSync14(file2.from);
|
|
92139
92432
|
}
|
|
92140
92433
|
if (dryRun) continue;
|
|
92141
|
-
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) if (entry.isDirectory()) emptyDir(
|
|
92434
|
+
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) if (entry.isDirectory()) emptyDir(join43(nodeDir2, entry.name));
|
|
92142
92435
|
emptyDir(nodeDir2);
|
|
92143
92436
|
}
|
|
92144
92437
|
emptyDir(nodesDir);
|
|
@@ -92160,13 +92453,13 @@ var MISSION_FIELDS = {
|
|
|
92160
92453
|
{ name: "result", type: "string", nullable: true, summary: "The hand-off of the mission, when it ended." },
|
|
92161
92454
|
{ name: "summary", type: "string", nullable: true, summary: "The first sentence of the Intent of the brief." },
|
|
92162
92455
|
{ name: "question", type: "string", nullable: true, summary: "The question a waiting lead ended its turn with." },
|
|
92163
|
-
{ name: "questions", type: "array", summary: "The open questions the lead asked
|
|
92456
|
+
{ 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." },
|
|
92164
92457
|
{ 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." },
|
|
92165
92458
|
{ name: "history", type: "array", summary: "Each change of state, with its time and who made it." }
|
|
92166
92459
|
]
|
|
92167
92460
|
};
|
|
92168
92461
|
function workspaceOf3(input2) {
|
|
92169
|
-
return
|
|
92462
|
+
return resolve22(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
92170
92463
|
}
|
|
92171
92464
|
function refused2(message, hint) {
|
|
92172
92465
|
return new CliError({ code: "invalid-input", message, exitCode: ExitCode.Validation, ...hint && { hint } });
|
|
@@ -92248,7 +92541,7 @@ function missionNote(context, record2) {
|
|
|
92248
92541
|
}
|
|
92249
92542
|
}
|
|
92250
92543
|
function shownState(record2) {
|
|
92251
|
-
return
|
|
92544
|
+
return waitsOnDecision(record2) && record2.state !== "waiting" ? `${record2.state}, waits on you` : record2.state;
|
|
92252
92545
|
}
|
|
92253
92546
|
function missionLines2(context, records) {
|
|
92254
92547
|
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)));
|
|
@@ -92300,7 +92593,7 @@ var missionNew = {
|
|
|
92300
92593
|
if (!id.success) throw refused2(`"${String(input2.positionals.id)}" is not a mission id: use lowercase letters, digits and hyphens.`);
|
|
92301
92594
|
const file2 = missionFile(teamRoot(team), id.data);
|
|
92302
92595
|
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.`);
|
|
92303
|
-
if (
|
|
92596
|
+
if (existsSync38(file2) && input2.flags.force !== true) throw refused2(`${relative8(workspaceDir, file2)} exists.`, "Pass --force to write over it.");
|
|
92304
92597
|
const intent = await asked(context, input2, "intent", "Intent: why does the team do this mission? ");
|
|
92305
92598
|
const endState = await asked(context, input2, "end-state", "End state: what is true when the mission is done? ");
|
|
92306
92599
|
const limits = await asked(context, input2, "limits", "Limits: what must the team not do or change? ");
|
|
@@ -92379,7 +92672,7 @@ var missionShow = {
|
|
|
92379
92672
|
if (record2.sessionId) context.output.line(`session: ${record2.sessionId}`);
|
|
92380
92673
|
if (record2.brief) context.output.line(["", record2.brief].join("\n"));
|
|
92381
92674
|
if (record2.lead) context.output.line(`lead: ${record2.lead}`);
|
|
92382
|
-
if (record2.
|
|
92675
|
+
if (record2.receivers.length > 0) context.output.line(`decision receivers: ${record2.receivers.join(", ")} (each question goes to every live one)`);
|
|
92383
92676
|
if (record2.claim) context.output.line(`claim: ${record2.claim}${record2.claimedAt ? ` since ${record2.claimedAt}` : ""}${record2.stale ? " (stale)" : ""}`);
|
|
92384
92677
|
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"));
|
|
92385
92678
|
if (record2.question && record2.state === "waiting") context.output.line(["", "question:", record2.question, "", `Answer with: ${commandPrefix(context.env)} mission reply ${record2.missionId} "<answer>"`].join("\n"));
|
|
@@ -92454,6 +92747,31 @@ var missionSay = {
|
|
|
92454
92747
|
return { data: { ...said2, node: node2.name } };
|
|
92455
92748
|
}
|
|
92456
92749
|
};
|
|
92750
|
+
var CLAUDE_SESSION_ENV = "CLAUDE_CODE_SESSION_ID";
|
|
92751
|
+
function missionMembership(verb, summary, why) {
|
|
92752
|
+
return {
|
|
92753
|
+
path: ["mission", verb],
|
|
92754
|
+
summary,
|
|
92755
|
+
credential: "none",
|
|
92756
|
+
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.` }],
|
|
92757
|
+
positionals: [MISSION_ID2],
|
|
92758
|
+
outputShape: MISSION_FIELDS,
|
|
92759
|
+
exitCodes: [0, 3],
|
|
92760
|
+
routes: [],
|
|
92761
|
+
examples: [{ command: `orbital mission ${verb} pr-42`, why }],
|
|
92762
|
+
run: async (input2, context) => {
|
|
92763
|
+
const mission = idOf(input2);
|
|
92764
|
+
const externalId = typeof input2.flags["external-id"] === "string" ? input2.flags["external-id"] : context.env[CLAUDE_SESSION_ENV] || null;
|
|
92765
|
+
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}.`);
|
|
92766
|
+
const node2 = await nodeOfMission(context, mission, () => nodeOfProject(context, workspaceOf3(input2)));
|
|
92767
|
+
const record2 = MissionRecordSchema.parse(await nodeCall(context, node2, `/orbital/missions/${verb}`, { missionId: mission, externalId }));
|
|
92768
|
+
context.output.line(`${record2.missionId} on ${record2.node}: ${record2.state}; decision receivers: ${record2.receivers.join(", ") || "none"}`);
|
|
92769
|
+
return { data: record2 };
|
|
92770
|
+
}
|
|
92771
|
+
};
|
|
92772
|
+
}
|
|
92773
|
+
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.");
|
|
92774
|
+
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.");
|
|
92457
92775
|
var missionMigrate = {
|
|
92458
92776
|
path: ["mission", "migrate"],
|
|
92459
92777
|
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.`,
|
|
@@ -92468,11 +92786,11 @@ var missionMigrate = {
|
|
|
92468
92786
|
const workspaceDir = workspaceOf3(input2);
|
|
92469
92787
|
const source = definitionSource(workspaceDir, context.env);
|
|
92470
92788
|
if (!source) throw refused2(`${workspaceDir} has no definition of orbital.`);
|
|
92471
|
-
const running = (await liveNodes(orbitalHome(context.env), context.fetch)).filter((node2) =>
|
|
92789
|
+
const running = (await liveNodes(orbitalHome(context.env), context.fetch)).filter((node2) => resolve22(node2.workspaceDir) === resolve22(source.projectDir));
|
|
92472
92790
|
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.`);
|
|
92473
92791
|
const project = sourceConfig(source).node ?? defaultNodeName(workspaceDir);
|
|
92474
92792
|
const nodes = teamNodes(readTeams(source.root).teams, project);
|
|
92475
|
-
const migration = migrateMissions(source.root,
|
|
92793
|
+
const migration = migrateMissions(source.root, join44(orbitalHome(context.env), "missions"), nodes, input2.flags["dry-run"] === true);
|
|
92476
92794
|
for (const move of migration.moved) context.output.line(`${input2.flags["dry-run"] === true ? "would move" : "moved"} ${move.from} -> ${move.to}`);
|
|
92477
92795
|
for (const reason of migration.refused) context.output.warn(reason);
|
|
92478
92796
|
if (migration.moved.length === 0 && migration.refused.length === 0) context.output.line("Nothing to move: the missions are in the new layout.");
|
|
@@ -92497,12 +92815,12 @@ var missionReply = {
|
|
|
92497
92815
|
return { data: record2 };
|
|
92498
92816
|
}
|
|
92499
92817
|
};
|
|
92500
|
-
var missionCommands = [missionNew, missionStart, missionList, missionShow, missionPause, missionResume, missionSay, missionReply, missionDone, missionMigrate].map((spec) => ({ ...spec, group: CommandGroup.OperatingModel }));
|
|
92818
|
+
var missionCommands = [missionNew, missionStart, missionList, missionShow, missionPause, missionResume, missionSay, missionReply, missionJoin, missionLeave, missionDone, missionMigrate].map((spec) => ({ ...spec, group: CommandGroup.OperatingModel }));
|
|
92501
92819
|
|
|
92502
92820
|
// src/core/cli/check.ts
|
|
92503
92821
|
init_command();
|
|
92504
92822
|
init_structure();
|
|
92505
|
-
import { resolve as
|
|
92823
|
+
import { resolve as resolve23 } from "node:path";
|
|
92506
92824
|
var LOAD_RULE = "orbital.load";
|
|
92507
92825
|
async function loadRefusals(workspaceDir, env3) {
|
|
92508
92826
|
try {
|
|
@@ -92540,7 +92858,7 @@ var checkCommand = {
|
|
|
92540
92858
|
{ command: "orbital check --annotations", why: "Show the findings on a pull request." }
|
|
92541
92859
|
],
|
|
92542
92860
|
run: async (input2, context) => {
|
|
92543
|
-
const workspaceDir =
|
|
92861
|
+
const workspaceDir = resolve23(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
92544
92862
|
const source = definitionSource(workspaceDir, context.env);
|
|
92545
92863
|
const findings = await loadRefusals(workspaceDir, context.env);
|
|
92546
92864
|
for (const finding of findings) context.output.line(`${finding.finding} ${finding.rule} ${finding.path} ${finding.message}`);
|
|
@@ -92809,19 +93127,19 @@ function createOrbitalCommands(options) {
|
|
|
92809
93127
|
}
|
|
92810
93128
|
|
|
92811
93129
|
// src/core/cli/shipped-skills.ts
|
|
92812
|
-
var
|
|
93130
|
+
var import_yaml9 = __toESM(require_dist(), 1);
|
|
92813
93131
|
init_command();
|
|
92814
|
-
import { cpSync as cpSync4, existsSync as
|
|
93132
|
+
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";
|
|
92815
93133
|
import { createHash as createHash11 } from "node:crypto";
|
|
92816
93134
|
import { homedir as homedir11 } from "node:os";
|
|
92817
|
-
import { dirname as dirname18, join as
|
|
93135
|
+
import { dirname as dirname18, join as join45, relative as relative9, resolve as resolve24, sep as sep7 } from "node:path";
|
|
92818
93136
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
92819
93137
|
var HASH_FILE = ".installed-hash";
|
|
92820
93138
|
function skillsRoot(shipped) {
|
|
92821
93139
|
let directory = dirname18(fileURLToPath3(shipped.anchor));
|
|
92822
93140
|
for (let depth = 0; depth < 5; depth += 1) {
|
|
92823
|
-
const candidate =
|
|
92824
|
-
if (
|
|
93141
|
+
const candidate = join45(directory, "skills");
|
|
93142
|
+
if (existsSync39(join45(candidate, shipped.names[0], "SKILL.md"))) return candidate;
|
|
92825
93143
|
directory = dirname18(directory);
|
|
92826
93144
|
}
|
|
92827
93145
|
throw new CliError({
|
|
@@ -92833,33 +93151,33 @@ function skillsRoot(shipped) {
|
|
|
92833
93151
|
}
|
|
92834
93152
|
function filesOf2(dir) {
|
|
92835
93153
|
return readdirSync22(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
92836
|
-
const path3 =
|
|
93154
|
+
const path3 = join45(dir, entry.name);
|
|
92837
93155
|
if (entry.isDirectory()) return filesOf2(path3);
|
|
92838
93156
|
return entry.name === HASH_FILE ? [] : [path3];
|
|
92839
93157
|
});
|
|
92840
93158
|
}
|
|
92841
93159
|
function descriptionOf(skillFile) {
|
|
92842
|
-
const match = /^---\n([\s\S]*?)\n---/.exec(
|
|
93160
|
+
const match = /^---\n([\s\S]*?)\n---/.exec(readFileSync33(skillFile, "utf8"));
|
|
92843
93161
|
if (!match?.[1]) return "";
|
|
92844
|
-
const frontmatter = (0,
|
|
93162
|
+
const frontmatter = (0, import_yaml9.parse)(match[1]);
|
|
92845
93163
|
return typeof frontmatter?.description === "string" ? frontmatter.description : "";
|
|
92846
93164
|
}
|
|
92847
93165
|
function shippedSkills(shipped) {
|
|
92848
93166
|
const root = skillsRoot(shipped);
|
|
92849
93167
|
return shipped.names.map((name) => {
|
|
92850
|
-
const dir =
|
|
92851
|
-
if (!
|
|
93168
|
+
const dir = join45(root, name);
|
|
93169
|
+
if (!existsSync39(join45(dir, "SKILL.md"))) {
|
|
92852
93170
|
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." });
|
|
92853
93171
|
}
|
|
92854
93172
|
const files = filesOf2(dir).map((file2) => relative9(dir, file2).split(sep7).join("/")).sort();
|
|
92855
|
-
return { name, description: descriptionOf(
|
|
93173
|
+
return { name, description: descriptionOf(join45(dir, "SKILL.md")), dir, files };
|
|
92856
93174
|
});
|
|
92857
93175
|
}
|
|
92858
93176
|
function folderHash(dir) {
|
|
92859
93177
|
const hash2 = createHash11("sha256");
|
|
92860
93178
|
for (const file2 of filesOf2(dir).map((path3) => relative9(dir, path3).split(sep7).join("/")).sort()) {
|
|
92861
93179
|
hash2.update(`${file2}\0`);
|
|
92862
|
-
hash2.update(
|
|
93180
|
+
hash2.update(readFileSync33(join45(dir, file2)));
|
|
92863
93181
|
hash2.update("\0");
|
|
92864
93182
|
}
|
|
92865
93183
|
return hash2.digest("hex");
|
|
@@ -92879,18 +93197,18 @@ function pick2(all, names) {
|
|
|
92879
93197
|
});
|
|
92880
93198
|
}
|
|
92881
93199
|
function installBase(input2) {
|
|
92882
|
-
if (typeof input2.flags.dir === "string") return
|
|
92883
|
-
if (input2.flags.global === true) return
|
|
92884
|
-
return
|
|
93200
|
+
if (typeof input2.flags.dir === "string") return resolve24(input2.flags.dir);
|
|
93201
|
+
if (input2.flags.global === true) return join45(homedir11(), ".claude", "skills");
|
|
93202
|
+
return resolve24(".claude", "skills");
|
|
92885
93203
|
}
|
|
92886
93204
|
function planOne(skill, base2) {
|
|
92887
|
-
const target =
|
|
93205
|
+
const target = join45(base2, skill.name);
|
|
92888
93206
|
const hash2 = folderHash(skill.dir);
|
|
92889
|
-
if (!
|
|
93207
|
+
if (!existsSync39(join45(target, "SKILL.md"))) return { skill, target, hash: hash2, action: "installed", edited: false };
|
|
92890
93208
|
const onDisk = folderHash(target);
|
|
92891
93209
|
if (onDisk === hash2) return { skill, target, hash: hash2, action: "unchanged", edited: false };
|
|
92892
|
-
const hashFile =
|
|
92893
|
-
const recorded =
|
|
93210
|
+
const hashFile = join45(target, HASH_FILE);
|
|
93211
|
+
const recorded = existsSync39(hashFile) ? readFileSync33(hashFile, "utf8").trim() : "";
|
|
92894
93212
|
return { skill, target, hash: hash2, action: "updated", edited: onDisk !== recorded };
|
|
92895
93213
|
}
|
|
92896
93214
|
function writeOne(plan) {
|
|
@@ -92898,7 +93216,7 @@ function writeOne(plan) {
|
|
|
92898
93216
|
rmSync15(plan.target, { recursive: true, force: true });
|
|
92899
93217
|
mkdirSync28(plan.target, { recursive: true });
|
|
92900
93218
|
cpSync4(plan.skill.dir, plan.target, { recursive: true });
|
|
92901
|
-
writeFileSync25(
|
|
93219
|
+
writeFileSync25(join45(plan.target, HASH_FILE), `${plan.hash}
|
|
92902
93220
|
`);
|
|
92903
93221
|
} catch (error62) {
|
|
92904
93222
|
throw new CliError({
|
|
@@ -92943,7 +93261,7 @@ function createSkillCommands(shipped) {
|
|
|
92943
93261
|
context.output.line(skill.dir);
|
|
92944
93262
|
return { data: { name: skill.name, path: skill.dir, content: null } };
|
|
92945
93263
|
}
|
|
92946
|
-
const content =
|
|
93264
|
+
const content = readFileSync33(join45(skill.dir, "SKILL.md"), "utf8");
|
|
92947
93265
|
context.output.line(content);
|
|
92948
93266
|
return { data: { name: skill.name, path: skill.dir, content } };
|
|
92949
93267
|
}
|
|
@@ -93025,21 +93343,21 @@ function createSkillCommands(shipped) {
|
|
|
93025
93343
|
// src/init.ts
|
|
93026
93344
|
init_command();
|
|
93027
93345
|
init_protocol();
|
|
93028
|
-
import { existsSync as
|
|
93029
|
-
import { dirname as dirname19, isAbsolute as isAbsolute8, relative as relative10, resolve as
|
|
93030
|
-
var
|
|
93346
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync29, writeFileSync as writeFileSync26 } from "node:fs";
|
|
93347
|
+
import { dirname as dirname19, isAbsolute as isAbsolute8, relative as relative10, resolve as resolve25 } from "node:path";
|
|
93348
|
+
var import_yaml10 = __toESM(require_dist(), 1);
|
|
93031
93349
|
function initHarnessFolder(workspaceDir, adapter = CLAUDE_ADAPTER) {
|
|
93032
93350
|
const file2 = harnessRootAt(workspaceDir, adapter).file;
|
|
93033
|
-
if (
|
|
93351
|
+
if (existsSync40(file2)) {
|
|
93034
93352
|
throw new CliError({ code: "conflict", message: `This project has ${file2} already.`, exitCode: ExitCode.Validation, hint: "A harness folder holds one orbital.yaml." });
|
|
93035
93353
|
}
|
|
93036
93354
|
mkdirSync29(dirname19(file2), { recursive: true });
|
|
93037
|
-
writeFileSync26(file2, (0,
|
|
93355
|
+
writeFileSync26(file2, (0, import_yaml10.stringify)({ apiVersion: ORBITAL_API_VERSION }));
|
|
93038
93356
|
return file2;
|
|
93039
93357
|
}
|
|
93040
93358
|
function workspaceOf4(flag) {
|
|
93041
|
-
const workspaceDir =
|
|
93042
|
-
if (!
|
|
93359
|
+
const workspaceDir = resolve25(typeof flag === "string" ? flag : ".");
|
|
93360
|
+
if (!existsSync40(workspaceDir)) throw new CliError({ code: "not-found", message: `${workspaceDir} does not exist.`, exitCode: ExitCode.Validation });
|
|
93043
93361
|
return workspaceDir;
|
|
93044
93362
|
}
|
|
93045
93363
|
var WORKSPACE3 = { name: "workspace", type: "path", summary: "The project. Default: the current directory." };
|
|
@@ -93115,7 +93433,7 @@ function targetOf2(context, workspaceDir, named2, given, adapters) {
|
|
|
93115
93433
|
});
|
|
93116
93434
|
}
|
|
93117
93435
|
async function chooseTargets(context, workspaceDir, adapters) {
|
|
93118
|
-
const present = adapters.filter((adapter) =>
|
|
93436
|
+
const present = adapters.filter((adapter) => existsSync40(resolve25(workspaceDir, adapter.folder.folder)));
|
|
93119
93437
|
if (present.length === 1) {
|
|
93120
93438
|
context.output.step(`installing ${present[0].id}: ${present[0].folder.folder} is the harness folder of this project`);
|
|
93121
93439
|
return [{ adapter: present[0], given: null }];
|
|
@@ -93149,9 +93467,9 @@ async function adapterInstall(context, adapter, workspaceDir, force) {
|
|
|
93149
93467
|
async function installTarget(context, workspaceDir, target, user, force) {
|
|
93150
93468
|
const { adapter, given } = target;
|
|
93151
93469
|
const root = harnessRootAt(workspaceDir, adapter);
|
|
93152
|
-
const file2 =
|
|
93470
|
+
const file2 = existsSync40(root.file) ? null : initHarnessFolder(workspaceDir, adapter);
|
|
93153
93471
|
if (file2) context.output.line(`wrote ${file2}`);
|
|
93154
|
-
const plugin = given ? { name: given.ref.name, source: user ? isPackageSource(given.ref.source) ? given.ref.source :
|
|
93472
|
+
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;
|
|
93155
93473
|
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;
|
|
93156
93474
|
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}`);
|
|
93157
93475
|
const install2 = await adapterInstall(context, adapter, workspaceDir, force);
|
|
@@ -93238,6 +93556,12 @@ function followAttach(context, options, signal, inbox, timing = {}) {
|
|
|
93238
93556
|
delivery: (event) => {
|
|
93239
93557
|
const delivery = DeliverySchema.safeParse(event);
|
|
93240
93558
|
if (delivery.success) inbox.push(delivery.data);
|
|
93559
|
+
const answered = AnsweredSchema.safeParse(event);
|
|
93560
|
+
if (!answered.success) return;
|
|
93561
|
+
for (let index = inbox.length - 1; index >= 0; index -= 1) {
|
|
93562
|
+
const held2 = inbox[index];
|
|
93563
|
+
if (held2.mission === answered.data.mission && (answered.data.question === null || held2.question === answered.data.question)) inbox.splice(index, 1);
|
|
93564
|
+
}
|
|
93241
93565
|
},
|
|
93242
93566
|
detached: (reason) => {
|
|
93243
93567
|
if (kept) kept.ended = true;
|
|
@@ -93367,9 +93691,8 @@ function mcpCommand(toolsOf) {
|
|
|
93367
93691
|
const joined = await attachment.current();
|
|
93368
93692
|
return { url: `${joined.nodeUrl}/a2a`, token: joined.token };
|
|
93369
93693
|
},
|
|
93370
|
-
|
|
93371
|
-
|
|
93372
|
-
},
|
|
93694
|
+
sessionId: null,
|
|
93695
|
+
attached: async () => (await attachment.current()).sessionId,
|
|
93373
93696
|
harness,
|
|
93374
93697
|
follow: true,
|
|
93375
93698
|
tools: inboxTools(context, attachment.current, inbox)
|
|
@@ -93378,8 +93701,8 @@ function mcpCommand(toolsOf) {
|
|
|
93378
93701
|
const server = await mcpServer(options, input2.flags.actions === true, toolsOf);
|
|
93379
93702
|
const transport = new StdioServerTransport();
|
|
93380
93703
|
await server.connect(transport);
|
|
93381
|
-
await new Promise((
|
|
93382
|
-
server.server.onclose =
|
|
93704
|
+
await new Promise((resolve27) => {
|
|
93705
|
+
server.server.onclose = resolve27;
|
|
93383
93706
|
process.stdin.once("end", () => void server.close());
|
|
93384
93707
|
});
|
|
93385
93708
|
stop.abort();
|
|
@@ -93405,6 +93728,10 @@ function orbitalCommands(options) {
|
|
|
93405
93728
|
...options.commands ?? []
|
|
93406
93729
|
];
|
|
93407
93730
|
}
|
|
93731
|
+
function workspacePluginCommands(options) {
|
|
93732
|
+
const reserved = [.../* @__PURE__ */ new Set([...options.builtIn.map((spec) => spec.path[0]), ...INTROSPECTION_WORDS])];
|
|
93733
|
+
return pluginCommandSpecs({ workspaceDir: options.workspaceDir, env: options.env, reserved, ...options.terminal && { terminal: options.terminal } });
|
|
93734
|
+
}
|
|
93408
93735
|
|
|
93409
93736
|
// src/launch.ts
|
|
93410
93737
|
init_zod();
|
|
@@ -93437,13 +93764,15 @@ async function nodeLaunch(env3, argv, prefix, run3) {
|
|
|
93437
93764
|
}
|
|
93438
93765
|
|
|
93439
93766
|
// src/cli.ts
|
|
93440
|
-
var orbital = () =>
|
|
93441
|
-
|
|
93442
|
-
|
|
93443
|
-
});
|
|
93767
|
+
var orbital = (options) => {
|
|
93768
|
+
const own2 = orbitalCommands({ terminal: async () => (await init_tui().then(() => tui_exports)).terminal });
|
|
93769
|
+
const workspace = peekFlag(options.argv, "workspace");
|
|
93770
|
+
const plugins = workspacePluginCommands({ builtIn: own2, workspaceDir: resolve26(typeof workspace === "string" ? workspace : "."), env: options.env });
|
|
93771
|
+
return { registry: composeRegistry([...own2, ...plugins]), context: (base2) => base2 };
|
|
93772
|
+
};
|
|
93444
93773
|
async function run2(options) {
|
|
93445
93774
|
setBrand(ORBITAL_BRAND);
|
|
93446
|
-
return runProgram(orbital(), options);
|
|
93775
|
+
return runProgram(orbital(options), options);
|
|
93447
93776
|
}
|
|
93448
93777
|
function processRun(argv) {
|
|
93449
93778
|
return run2({
|