@kaiba-cloud/orbital 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/index.js +749 -425
- package/dist/mod.json +1 -1
- package/dist/orbital.js +777 -445
- package/dist/runner.js +79 -13
- package/package.json +6 -7
- package/schemas/telemetry-event.schema.json +7 -6
- package/schemas/telemetry-snapshot.schema.json +7 -6
- package/skills/orbital-plugin-builder/SKILL.md +8 -5
- package/skills/orbital-plugin-builder/references/host-api.md +10 -10
- package/skills/orbital-plugin-builder/references/manifest.md +7 -2
- package/skills/orbital-plugin-builder/references/ports.md +72 -19
- package/skills/orbital-plugin-builder/references/recipes.md +4 -4
package/dist/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.6" : "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();
|
|
@@ -65754,12 +65831,9 @@ function readDescriptorName(dir) {
|
|
|
65754
65831
|
if (!existsSync6(file2)) throw refuse("invalid-input", `${dir} has no ${PLUGIN_PACKAGE_FILE}, so it is not a plugin.`);
|
|
65755
65832
|
return PluginPackageSchema.parse(JSON.parse(readFileSync7(file2, "utf8"))).name;
|
|
65756
65833
|
}
|
|
65757
|
-
function lastPart(value2) {
|
|
65758
|
-
return value2.replace(/\.git$/, "").split(/[/:]/).filter(Boolean).pop() ?? value2;
|
|
65759
|
-
}
|
|
65760
65834
|
function resolvePluginRef(home, ref) {
|
|
65761
|
-
if (ref.startsWith("npm:")) return { kind: "npm", name:
|
|
65762
|
-
if (ref.startsWith("git:")) return { kind: "git", name:
|
|
65835
|
+
if (ref.startsWith("npm:")) return { kind: "npm", name: null, pkg: ref.slice(4) };
|
|
65836
|
+
if (ref.startsWith("git:")) return { kind: "git", name: null, url: ref.slice(4) };
|
|
65763
65837
|
if (isFolder(ref)) return { kind: "folder", name: readDescriptorName(resolve6(ref)), dir: resolve6(ref) };
|
|
65764
65838
|
const at = ref.lastIndexOf("@");
|
|
65765
65839
|
if (at <= 0) throw refuse("invalid-input", `"${ref}" is not a plugin reference.`, "Use <name>@<marketplace>, npm:<package>, git:<url>, or a folder such as ./plugins/mine.");
|
|
@@ -65791,17 +65865,17 @@ function installPlugin(home, ref, exec4 = run) {
|
|
|
65791
65865
|
const installed = Object.keys(JSON.parse(readFileSync7(join10(scratch, "package.json"), "utf8")).dependencies ?? {});
|
|
65792
65866
|
const pkgDir = join10("node_modules", ...(installed[0] ?? ref.pkg.replace(/(.)@[^/]*$/, "$1")).split("/"));
|
|
65793
65867
|
const inner = PluginPackageSchema.parse(JSON.parse(readFileSync7(join10(scratch, pkgDir, PLUGIN_PACKAGE_FILE), "utf8")));
|
|
65794
|
-
writeFileSync5(join10(scratch, PLUGIN_PACKAGE_FILE), `${JSON.stringify({ name:
|
|
65868
|
+
writeFileSync5(join10(scratch, PLUGIN_PACKAGE_FILE), `${JSON.stringify({ name: inner.name, command: inner.command, dir: inner.dir ? join10(pkgDir, inner.dir) : pkgDir, env: inner.env, ...Object.keys(inner.config).length > 0 && { config: inner.config }, ...inner.harnesses.length > 0 && { harnesses: inner.harnesses }, ...inner.commands.length > 0 && { commands: inner.commands } }, null, 2)}
|
|
65795
65869
|
`);
|
|
65796
65870
|
break;
|
|
65797
65871
|
}
|
|
65798
65872
|
}
|
|
65799
65873
|
const named2 = readDescriptorName(scratch);
|
|
65800
|
-
if (named2 !== ref.name) throw refuse("invalid-input", `The plugin calls itself ${named2}, and its reference names it ${ref.name}.`);
|
|
65801
|
-
const dir = join10(parent,
|
|
65874
|
+
if (ref.name !== null && named2 !== ref.name) throw refuse("invalid-input", `The plugin calls itself ${named2}, and its reference names it ${ref.name}.`);
|
|
65875
|
+
const dir = join10(parent, named2);
|
|
65802
65876
|
rmSync3(dir, { recursive: true, force: true });
|
|
65803
65877
|
renameSync(scratch, dir);
|
|
65804
|
-
return dir;
|
|
65878
|
+
return { name: named2, dir };
|
|
65805
65879
|
} finally {
|
|
65806
65880
|
rmSync3(scratch, { recursive: true, force: true });
|
|
65807
65881
|
}
|
|
@@ -65848,8 +65922,8 @@ function isPackageSource(source) {
|
|
|
65848
65922
|
function ensurePluginInstalled(home, name, source, exec4 = run) {
|
|
65849
65923
|
const dir = join10(pluginsDir(home), name);
|
|
65850
65924
|
if (existsSync6(join10(dir, PLUGIN_PACKAGE_FILE))) return dir;
|
|
65851
|
-
if (source.startsWith("npm:")) return installPlugin(home, { kind: "npm", name, pkg: source.slice(4) }, exec4);
|
|
65852
|
-
if (source.startsWith("git:")) return installPlugin(home, { kind: "git", name, url: source.slice(4) }, exec4);
|
|
65925
|
+
if (source.startsWith("npm:")) return installPlugin(home, { kind: "npm", name, pkg: source.slice(4) }, exec4).dir;
|
|
65926
|
+
if (source.startsWith("git:")) return installPlugin(home, { kind: "git", name, url: source.slice(4) }, exec4).dir;
|
|
65853
65927
|
throw refuse("invalid-input", `The plugin ${name} has the source ${source}, and only an npm: or git: source installs into the plugin cache.`);
|
|
65854
65928
|
}
|
|
65855
65929
|
|
|
@@ -65857,15 +65931,22 @@ function ensurePluginInstalled(home, name, source, exec4 = run) {
|
|
|
65857
65931
|
function pluginsDir(home) {
|
|
65858
65932
|
return join11(home, "plugins");
|
|
65859
65933
|
}
|
|
65934
|
+
function isLocalSource(source) {
|
|
65935
|
+
return source.startsWith(".") || isAbsolute5(source);
|
|
65936
|
+
}
|
|
65937
|
+
function pluginDir(name, source, roots) {
|
|
65938
|
+
if (!isLocalSource(source)) return join11(pluginsDir(roots.home), name);
|
|
65939
|
+
return isAbsolute5(source) ? source : resolve7(roots.harnessRoot ?? process.cwd(), source);
|
|
65940
|
+
}
|
|
65860
65941
|
function commandOf(name, source, roots, entryEnv = []) {
|
|
65861
65942
|
if (source.startsWith("exec:")) {
|
|
65862
65943
|
const [command2, ...args2] = source.slice("exec:".length).trim().split(/\s+/);
|
|
65863
65944
|
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 };
|
|
65945
|
+
return { command: command2, args: args2, cwd: roots.harnessRoot ?? process.cwd(), env: [...entryEnv], defaults: {}, harnesses: null, commands: null };
|
|
65865
65946
|
}
|
|
65866
65947
|
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 =
|
|
65948
|
+
const local = isLocalSource(source);
|
|
65949
|
+
const dir = pluginDir(name, source, roots);
|
|
65869
65950
|
const descriptor = join11(dir, PLUGIN_PACKAGE_FILE);
|
|
65870
65951
|
if (!local && !existsSync7(descriptor) && /^(npm|git):/.test(source)) ensurePluginInstalled(roots.home, name, source);
|
|
65871
65952
|
if (!existsSync7(descriptor)) {
|
|
@@ -65873,7 +65954,7 @@ function commandOf(name, source, roots, entryEnv = []) {
|
|
|
65873
65954
|
}
|
|
65874
65955
|
const plugin = PluginPackageSchema.parse(JSON.parse(readFileSync8(descriptor, "utf8")));
|
|
65875
65956
|
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 };
|
|
65957
|
+
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
65958
|
}
|
|
65878
65959
|
var PLUGIN_ENV = [
|
|
65879
65960
|
"PATH",
|
|
@@ -65918,6 +65999,15 @@ function folderMismatch(manifest, folders) {
|
|
|
65918
65999
|
const extra = folders.find((one) => !manifest.provides.harnesses.some((harness) => harness.id === one.id && harness.folder !== null));
|
|
65919
66000
|
return extra ? `declares the harness ${extra.id} in its ${PLUGIN_PACKAGE_FILE}, and its harnesses port serves no folder for it` : null;
|
|
65920
66001
|
}
|
|
66002
|
+
function commandMismatch(manifest, declared) {
|
|
66003
|
+
for (const command of manifest.commands) {
|
|
66004
|
+
const same2 = declared.find((one) => one.name === command.name);
|
|
66005
|
+
if (!same2) return `serves the command ${command.name}, and its ${PLUGIN_PACKAGE_FILE} declares no command ${command.name}`;
|
|
66006
|
+
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`;
|
|
66007
|
+
}
|
|
66008
|
+
const extra = declared.find((one) => !manifest.commands.some((command) => command.name === one.name));
|
|
66009
|
+
return extra ? `declares the command ${extra.name} in its ${PLUGIN_PACKAGE_FILE}, and serves no command ${extra.name}` : null;
|
|
66010
|
+
}
|
|
65921
66011
|
function manifestEnv(manifest) {
|
|
65922
66012
|
return manifest.permissions.secrets.filter((name) => PLUGIN_ENV_NAME.test(name));
|
|
65923
66013
|
}
|
|
@@ -65946,15 +66036,16 @@ async function startRemotePlugin(options) {
|
|
|
65946
66036
|
let reconnecting = false;
|
|
65947
66037
|
const connect = async () => {
|
|
65948
66038
|
const start = commandOf(options.name, options.source, options.roots, options.entryEnv);
|
|
65949
|
-
const
|
|
66039
|
+
const terminal2 = options.terminal ? [options.terminal.input, options.terminal.output, options.terminal.error] : [];
|
|
66040
|
+
const child = spawn2(start.command, start.args, { cwd: start.cwd, env: pluginProcessEnv(options.env, start.env), stdio: ["pipe", "pipe", "pipe", ...terminal2] });
|
|
65950
66041
|
try {
|
|
65951
|
-
return await initialize(child, start.env, start.defaults, start.harnesses);
|
|
66042
|
+
return await initialize(child, start.env, start.defaults, start.harnesses, start.commands);
|
|
65952
66043
|
} catch (error62) {
|
|
65953
66044
|
child.kill("SIGKILL");
|
|
65954
66045
|
throw error62;
|
|
65955
66046
|
}
|
|
65956
66047
|
};
|
|
65957
|
-
const initialize = async (child, env3, defaults, folders
|
|
66048
|
+
const initialize = async (child, env3, defaults, folders, commands) => {
|
|
65958
66049
|
const peer = new RpcPeer({ send: (line) => child.stdin?.write(`${line}
|
|
65959
66050
|
`) });
|
|
65960
66051
|
createInterface2({ input: child.stdout }).on("line", (line) => peer.receive(line));
|
|
@@ -65989,12 +66080,16 @@ async function startRemotePlugin(options) {
|
|
|
65989
66080
|
const where2 = options.source.startsWith("exec:") ? `its ${ORBITAL_FILE} entry` : `its ${PLUGIN_PACKAGE_FILE}`;
|
|
65990
66081
|
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
66082
|
}
|
|
66083
|
+
if (commands) {
|
|
66084
|
+
const mismatch = commandMismatch(manifest2, commands);
|
|
66085
|
+
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.`);
|
|
66086
|
+
}
|
|
65992
66087
|
if (options.offline && manifest2.permissions.network.length > 0) {
|
|
65993
66088
|
throw new Error(`The plugin ${options.name} reaches ${manifest2.permissions.network.join(", ")}, and ${ORBITAL_FILE} says offline: true.`);
|
|
65994
66089
|
}
|
|
65995
66090
|
await options.policy.check({ kind: "plugin.load", plugin: options.name, network: manifest2.permissions.network, secrets: manifest2.permissions.secrets, models: manifest2.permissions.models });
|
|
65996
|
-
child.once("exit", () => {
|
|
65997
|
-
peer.close(new Error(`The plugin ${options.name} stopped.`));
|
|
66091
|
+
child.once("exit", (code, signal) => {
|
|
66092
|
+
peer.close(new Error(`The plugin ${options.name} stopped (${signal ? `signal ${signal}` : `exit code ${code}`}).`));
|
|
65998
66093
|
if (stopped || reconnecting) return;
|
|
65999
66094
|
current2 = reconnect();
|
|
66000
66095
|
current2.catch((error62) => options.log("error", error62 instanceof Error ? error62.message : String(error62)));
|
|
@@ -66174,7 +66269,7 @@ async function startRemotePlugin(options) {
|
|
|
66174
66269
|
endpoint = given;
|
|
66175
66270
|
void current2.then((connection) => connection.peer.notify(PLUGIN_METHODS.hostEndpoint, given));
|
|
66176
66271
|
},
|
|
66177
|
-
command: async (
|
|
66272
|
+
command: async (request) => PluginCommandResultSchema.parse(await (await current2).peer.request(PLUGIN_METHODS.commandRun, request, 0)),
|
|
66178
66273
|
close: async () => {
|
|
66179
66274
|
stopped = true;
|
|
66180
66275
|
const connection = await current2.catch(() => null);
|
|
@@ -67547,7 +67642,7 @@ var readBodyDirect = (request) => {
|
|
|
67547
67642
|
request[bodyBufferKey] = buffered;
|
|
67548
67643
|
return Promise.resolve(buffered);
|
|
67549
67644
|
}
|
|
67550
|
-
const promise2 = new Promise((
|
|
67645
|
+
const promise2 = new Promise((resolve27, reject) => {
|
|
67551
67646
|
const chunks = [];
|
|
67552
67647
|
let settled = false;
|
|
67553
67648
|
const finish = (callback) => {
|
|
@@ -67565,7 +67660,7 @@ var readBodyDirect = (request) => {
|
|
|
67565
67660
|
else if (recovered === void 0) reject(error62 ?? normalizeAbortError(request, incoming));
|
|
67566
67661
|
else {
|
|
67567
67662
|
request[bodyBufferKey] = recovered;
|
|
67568
|
-
|
|
67663
|
+
resolve27(recovered);
|
|
67569
67664
|
}
|
|
67570
67665
|
});
|
|
67571
67666
|
return true;
|
|
@@ -67577,7 +67672,7 @@ var readBodyDirect = (request) => {
|
|
|
67577
67672
|
finish(() => {
|
|
67578
67673
|
const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
|
|
67579
67674
|
request[bodyBufferKey] = buffer;
|
|
67580
|
-
|
|
67675
|
+
resolve27(buffer);
|
|
67581
67676
|
});
|
|
67582
67677
|
};
|
|
67583
67678
|
const onError = (error62) => {
|
|
@@ -68124,7 +68219,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
68124
68219
|
});
|
|
68125
68220
|
if (!chunk) {
|
|
68126
68221
|
if (i === 1) {
|
|
68127
|
-
await new Promise((
|
|
68222
|
+
await new Promise((resolve27) => setTimeout(resolve27));
|
|
68128
68223
|
maxReadCount = 3;
|
|
68129
68224
|
continue;
|
|
68130
68225
|
}
|
|
@@ -70393,9 +70488,9 @@ data:
|
|
|
70393
70488
|
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
70394
70489
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
70395
70490
|
if (this._enableJsonResponse) {
|
|
70396
|
-
return new Promise((
|
|
70491
|
+
return new Promise((resolve27) => {
|
|
70397
70492
|
this._streamMapping.set(streamId, {
|
|
70398
|
-
resolveJson:
|
|
70493
|
+
resolveJson: resolve27,
|
|
70399
70494
|
cleanup: () => {
|
|
70400
70495
|
this._streamMapping.delete(streamId);
|
|
70401
70496
|
}
|
|
@@ -76770,7 +76865,7 @@ var Protocol = class {
|
|
|
76770
76865
|
return;
|
|
76771
76866
|
}
|
|
76772
76867
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
76773
|
-
await new Promise((
|
|
76868
|
+
await new Promise((resolve27) => setTimeout(resolve27, pollInterval));
|
|
76774
76869
|
options?.signal?.throwIfAborted();
|
|
76775
76870
|
}
|
|
76776
76871
|
} catch (error62) {
|
|
@@ -76787,7 +76882,7 @@ var Protocol = class {
|
|
|
76787
76882
|
*/
|
|
76788
76883
|
request(request, resultSchema, options) {
|
|
76789
76884
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
76790
|
-
return new Promise((
|
|
76885
|
+
return new Promise((resolve27, reject) => {
|
|
76791
76886
|
const earlyReject = (error62) => {
|
|
76792
76887
|
reject(error62);
|
|
76793
76888
|
};
|
|
@@ -76865,7 +76960,7 @@ var Protocol = class {
|
|
|
76865
76960
|
if (!parseResult.success) {
|
|
76866
76961
|
reject(parseResult.error);
|
|
76867
76962
|
} else {
|
|
76868
|
-
|
|
76963
|
+
resolve27(parseResult.data);
|
|
76869
76964
|
}
|
|
76870
76965
|
} catch (error62) {
|
|
76871
76966
|
reject(error62);
|
|
@@ -77127,12 +77222,12 @@ var Protocol = class {
|
|
|
77127
77222
|
}
|
|
77128
77223
|
} catch {
|
|
77129
77224
|
}
|
|
77130
|
-
return new Promise((
|
|
77225
|
+
return new Promise((resolve27, reject) => {
|
|
77131
77226
|
if (signal.aborted) {
|
|
77132
77227
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
77133
77228
|
return;
|
|
77134
77229
|
}
|
|
77135
|
-
const timeoutId = setTimeout(
|
|
77230
|
+
const timeoutId = setTimeout(resolve27, interval);
|
|
77136
77231
|
signal.addEventListener("abort", () => {
|
|
77137
77232
|
clearTimeout(timeoutId);
|
|
77138
77233
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -78273,7 +78368,7 @@ var McpServer = class {
|
|
|
78273
78368
|
let task = createTaskResult.task;
|
|
78274
78369
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
78275
78370
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
78276
|
-
await new Promise((
|
|
78371
|
+
await new Promise((resolve27) => setTimeout(resolve27, pollInterval));
|
|
78277
78372
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
78278
78373
|
if (!updatedTask) {
|
|
78279
78374
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -78998,14 +79093,14 @@ function claudeService(env3) {
|
|
|
78998
79093
|
if (!dir) return SERVICE;
|
|
78999
79094
|
return `${SERVICE}-${createHash4("sha256").update(dir.normalize("NFC")).digest("hex").slice(0, 8)}`;
|
|
79000
79095
|
}
|
|
79001
|
-
var findWithSecurity = (args) => new Promise((
|
|
79096
|
+
var findWithSecurity = (args) => new Promise((resolve27) => {
|
|
79002
79097
|
const child = spawn3(SECURITY, [...args], { stdio: ["ignore", "pipe", "ignore"], timeout: SECURITY_TIMEOUT_MS });
|
|
79003
79098
|
let stdout = "";
|
|
79004
79099
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
79005
79100
|
stdout += chunk;
|
|
79006
79101
|
});
|
|
79007
|
-
child.once("error", () =>
|
|
79008
|
-
child.once("close", (code) =>
|
|
79102
|
+
child.once("error", () => resolve27({ status: 1, stdout: "" }));
|
|
79103
|
+
child.once("close", (code) => resolve27({ status: code ?? 1, stdout }));
|
|
79009
79104
|
});
|
|
79010
79105
|
function keychainRead(account, service, find = findWithSecurity) {
|
|
79011
79106
|
return async () => {
|
|
@@ -79026,7 +79121,7 @@ function renewEnv(env3) {
|
|
|
79026
79121
|
return Object.fromEntries(Object.entries(env3).filter((entry) => entry[1] !== void 0 && !KEPT_OUT.includes(entry[0])));
|
|
79027
79122
|
}
|
|
79028
79123
|
function claudeRenewal(binary, env3) {
|
|
79029
|
-
return () => new Promise((
|
|
79124
|
+
return () => new Promise((resolve27, reject) => {
|
|
79030
79125
|
const cwd2 = mkdtempSync(join16(tmpdir(), "orbital-claude-renew-"));
|
|
79031
79126
|
const child = spawn3(binary, RENEW_ARGS, { cwd: cwd2, env: renewEnv(env3), stdio: ["ignore", "ignore", "pipe"], timeout: RENEW_TIMEOUT_MS });
|
|
79032
79127
|
let stderr = "";
|
|
@@ -79036,7 +79131,7 @@ function claudeRenewal(binary, env3) {
|
|
|
79036
79131
|
const done = (error62) => {
|
|
79037
79132
|
rmSync5(cwd2, { recursive: true, force: true });
|
|
79038
79133
|
if (error62) reject(error62);
|
|
79039
|
-
else
|
|
79134
|
+
else resolve27();
|
|
79040
79135
|
};
|
|
79041
79136
|
child.once("error", done);
|
|
79042
79137
|
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 +80420,7 @@ function missionOfRow(row2) {
|
|
|
80325
80420
|
result: textOf(row2, "result"),
|
|
80326
80421
|
question: textOf(row2, "question"),
|
|
80327
80422
|
questions: questionsOf(row2),
|
|
80328
|
-
|
|
80423
|
+
receivers: [],
|
|
80329
80424
|
lead: textOf(row2, "lead"),
|
|
80330
80425
|
lastActivity: textOf(row2, "activity"),
|
|
80331
80426
|
claim: textOf(row2, "claim"),
|
|
@@ -80479,7 +80574,7 @@ var MissionStore = class {
|
|
|
80479
80574
|
source: request.source !== void 0 ? request.source : known?.source ?? null,
|
|
80480
80575
|
result: request.result !== void 0 ? request.result : known?.result ?? null,
|
|
80481
80576
|
question: request.question !== void 0 ? request.question : request.state === "waiting" ? known?.question ?? null : null,
|
|
80482
|
-
|
|
80577
|
+
receivers: request.receivers !== void 0 ? request.receivers : known?.receivers ?? [],
|
|
80483
80578
|
lead: request.lead !== void 0 ? request.lead : known?.lead ?? null,
|
|
80484
80579
|
claim: request.claim !== void 0 ? request.claim : known?.claim ?? null,
|
|
80485
80580
|
claimedAt: request.claimedAt !== void 0 ? request.claimedAt : known?.claimedAt ?? null,
|
|
@@ -80534,7 +80629,7 @@ function missionRecord(node2, mission, declared, stored) {
|
|
|
80534
80629
|
result: stored?.result ?? null,
|
|
80535
80630
|
question: stored?.question ?? null,
|
|
80536
80631
|
questions: stored?.questions ?? [],
|
|
80537
|
-
|
|
80632
|
+
receivers: stored?.receivers ?? [],
|
|
80538
80633
|
lead: stored?.lead ?? null,
|
|
80539
80634
|
lastActivity: null,
|
|
80540
80635
|
claim: stored?.claim ?? null,
|
|
@@ -80642,9 +80737,9 @@ var Telemetry = class {
|
|
|
80642
80737
|
async drain(timeoutMs) {
|
|
80643
80738
|
if (this.queues.every((queue) => !queue.running && queue.items.length === 0)) return true;
|
|
80644
80739
|
let done = null;
|
|
80645
|
-
const empty = new Promise((
|
|
80740
|
+
const empty = new Promise((resolve27) => {
|
|
80646
80741
|
done = () => {
|
|
80647
|
-
if (this.queues.every((queue) => !queue.running && queue.items.length === 0))
|
|
80742
|
+
if (this.queues.every((queue) => !queue.running && queue.items.length === 0)) resolve27(true);
|
|
80648
80743
|
};
|
|
80649
80744
|
this.drained.add(done);
|
|
80650
80745
|
});
|
|
@@ -80897,7 +80992,7 @@ var SessionHost = class {
|
|
|
80897
80992
|
transition: missionTransition(before?.state ?? "open", after.state, after.question),
|
|
80898
80993
|
reason: change?.note ?? null,
|
|
80899
80994
|
by: change?.by ?? "node",
|
|
80900
|
-
|
|
80995
|
+
receivers: after.receivers,
|
|
80901
80996
|
sessionId: after.sessionId,
|
|
80902
80997
|
lead: after.lead,
|
|
80903
80998
|
question: after.question,
|
|
@@ -80905,7 +81000,7 @@ var SessionHost = class {
|
|
|
80905
81000
|
});
|
|
80906
81001
|
}
|
|
80907
81002
|
missionWritten(mission, team, by) {
|
|
80908
|
-
this.telemetry.emit({ kind: "mission.state", mission, team, from: null, to: "open", transition: "new", reason: "written", by,
|
|
81003
|
+
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
81004
|
}
|
|
80910
81005
|
async snapshot() {
|
|
80911
81006
|
const definition = await this.definitionOf(this.options.defaultWorkspace);
|
|
@@ -80934,7 +81029,7 @@ var SessionHost = class {
|
|
|
80934
81029
|
retiring: note?.retiring ?? null
|
|
80935
81030
|
};
|
|
80936
81031
|
}), ...[...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,
|
|
81032
|
+
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
81033
|
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
81034
|
jobs: [...this.openJobs].map(([jobId, open2]) => ({ ...open2.context, jobId, since: new Date(open2.since).toISOString(), via: open2.via })),
|
|
80940
81035
|
tokens: this.telemetry.tokens(),
|
|
@@ -80988,19 +81083,48 @@ var SessionHost = class {
|
|
|
80988
81083
|
this.redeliverQuestions(id);
|
|
80989
81084
|
return info;
|
|
80990
81085
|
}
|
|
80991
|
-
redeliverQuestions(
|
|
81086
|
+
redeliverQuestions(sessionId) {
|
|
81087
|
+
const session = this.sessions.get(sessionId);
|
|
81088
|
+
if (!session) return;
|
|
80992
81089
|
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
|
-
}
|
|
81090
|
+
if (!stored.receivers.some((key2) => this.receives(session, key2))) continue;
|
|
81091
|
+
for (const open2 of this.openQuestions(stored)) if (!this.questionOpen(sessionId, open2.mission, open2.questionId)) this.deliverQuestion(sessionId, open2, stored.sessionId);
|
|
80998
81092
|
}
|
|
80999
81093
|
}
|
|
81000
|
-
|
|
81001
|
-
if (
|
|
81094
|
+
openQuestions(stored) {
|
|
81095
|
+
if (!stored.sessionId || stored.state === "paused" || !hasLead(stored.state)) return [];
|
|
81096
|
+
const base2 = { team: stored.team, mission: stored.mission, lead: stored.lead };
|
|
81097
|
+
return [
|
|
81098
|
+
...stored.state === "waiting" ? [{ ...base2, question: stored.question ?? NO_HANDOFF_TELL, questionId: null }] : [],
|
|
81099
|
+
...stored.questions.map((asked2) => ({ ...base2, question: asked2.question, questionId: asked2.id }))
|
|
81100
|
+
];
|
|
81101
|
+
}
|
|
81102
|
+
receiverKey(session) {
|
|
81103
|
+
return session.external?.externalId ? externalReceiver(session.external.externalId) : session.id;
|
|
81104
|
+
}
|
|
81105
|
+
receives(session, key2) {
|
|
81106
|
+
return key2 === session.id || key2 === this.receiverKey(session);
|
|
81107
|
+
}
|
|
81108
|
+
liveReceivers(receivers, except = null) {
|
|
81109
|
+
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)));
|
|
81110
|
+
}
|
|
81111
|
+
keyOfRemote(receiver) {
|
|
81112
|
+
if (receiver.node !== this.node) return receiverRef(receiver);
|
|
81113
|
+
const session = this.sessions.get(receiver.sessionId);
|
|
81114
|
+
return session ? this.receiverKey(session) : receiver.sessionId;
|
|
81115
|
+
}
|
|
81116
|
+
starterKey(by) {
|
|
81117
|
+
if (!by.startsWith("session:")) return null;
|
|
81118
|
+
const named2 = by.slice("session:".length);
|
|
81119
|
+
const remote = remoteReceiverOf(named2);
|
|
81120
|
+
if (remote) return this.keyOfRemote(remote);
|
|
81121
|
+
const session = this.sessions.get(named2);
|
|
81122
|
+
return session ? this.receiverKey(session) : named2;
|
|
81123
|
+
}
|
|
81124
|
+
questionOpen(sessionId, mission, questionId) {
|
|
81125
|
+
if (this.questionsInFlight.has(`${sessionId}:${mission}:${questionId ?? ""}`)) return true;
|
|
81002
81126
|
for (const [taskId, asked2] of [...this.questionDeliveries]) {
|
|
81003
|
-
if (asked2.mission !== mission || asked2.questionId !== questionId || this.taskSession.get(taskId) !==
|
|
81127
|
+
if (asked2.mission !== mission || asked2.questionId !== questionId || this.taskSession.get(taskId) !== sessionId) continue;
|
|
81004
81128
|
const task = this.tasks.get(taskId);
|
|
81005
81129
|
if (task && !(task.status && isTerminal(task.status.state))) return true;
|
|
81006
81130
|
this.questionDeliveries.delete(taskId);
|
|
@@ -81032,7 +81156,7 @@ var SessionHost = class {
|
|
|
81032
81156
|
async settleExternal(sessionId, settle2) {
|
|
81033
81157
|
const session = this.sessions.get(sessionId);
|
|
81034
81158
|
if (!session?.external) throw new SessionHostError("not-found", `There is no attached session ${sessionId} on this node.`);
|
|
81035
|
-
const taskId = settle2.taskId ?? session.running;
|
|
81159
|
+
const taskId = settle2.taskId ?? session.running ?? session.external.open.keys().next().value ?? null;
|
|
81036
81160
|
const task = taskId ? this.tasks.get(taskId) : void 0;
|
|
81037
81161
|
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
81162
|
await this.finish(sessionId, task, "TASK_STATE_COMPLETED", settle2.text);
|
|
@@ -81066,23 +81190,21 @@ var SessionHost = class {
|
|
|
81066
81190
|
await this.finish(session.id, task, "TASK_STATE_FAILED", `The attached session ${session.id} is detached.`);
|
|
81067
81191
|
return;
|
|
81068
81192
|
}
|
|
81069
|
-
|
|
81193
|
+
const asked2 = this.questionOf(message);
|
|
81194
|
+
if (asked2 === null) session.running = task.id;
|
|
81070
81195
|
await this.update(session.id, task, "TASK_STATE_WORKING");
|
|
81071
81196
|
const fromAgent = message.metadata?.[ORBITAL_META.fromAgent];
|
|
81072
81197
|
const fromSession = message.metadata?.[ORBITAL_META.fromSession];
|
|
81073
|
-
const asked2 = this.questionOf(message);
|
|
81074
81198
|
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((
|
|
81199
|
+
const settled = new Promise((resolve27) => this.externalWaits.set(task.id, resolve27)).finally(() => {
|
|
81200
|
+
this.externalWaits.delete(task.id);
|
|
81201
|
+
external.open.delete(task.id);
|
|
81202
|
+
if (session.running === task.id) session.running = null;
|
|
81203
|
+
});
|
|
81076
81204
|
if (asked2 !== null) external.open.set(task.id, delivery);
|
|
81077
81205
|
if (external.listeners.size === 0) external.waiting.push(delivery);
|
|
81078
81206
|
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
|
-
}
|
|
81207
|
+
if (asked2 === null) await settled;
|
|
81086
81208
|
}
|
|
81087
81209
|
stopFollowing() {
|
|
81088
81210
|
this.following.abort();
|
|
@@ -81097,9 +81219,9 @@ var SessionHost = class {
|
|
|
81097
81219
|
if (this.followingHub) return;
|
|
81098
81220
|
let listener = null;
|
|
81099
81221
|
try {
|
|
81100
|
-
await Promise.race([this.settled(), new Promise((
|
|
81101
|
-
listener =
|
|
81102
|
-
this.followListeners.add(
|
|
81222
|
+
await Promise.race([this.settled(), new Promise((resolve27) => {
|
|
81223
|
+
listener = resolve27;
|
|
81224
|
+
this.followListeners.add(resolve27);
|
|
81103
81225
|
})]);
|
|
81104
81226
|
} finally {
|
|
81105
81227
|
if (listener) this.followListeners.delete(listener);
|
|
@@ -81190,7 +81312,7 @@ var SessionHost = class {
|
|
|
81190
81312
|
const leadLives = known?.sessionId ? this.sessions.has(known.sessionId) : false;
|
|
81191
81313
|
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
81314
|
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
|
|
81315
|
+
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
81316
|
const ended = known && hasLead(known.state) && !leadLives ? `the lead session ${known.sessionId ?? "(none)"} ended with its node` : null;
|
|
81195
81317
|
if (known?.state === "done") throw new SessionHostError("invalid", `The mission ${missionId} is done. Give the team a new mission for new work.`);
|
|
81196
81318
|
const claim2 = declared?.source ?? missionId;
|
|
@@ -81201,12 +81323,13 @@ var SessionHost = class {
|
|
|
81201
81323
|
const resumable = ended && known?.sessionId && readSessionRecord(this.paths.sessionsDir, known.sessionId) !== null ? known.sessionId : null;
|
|
81202
81324
|
if (resumable) {
|
|
81203
81325
|
const task2 = await this.submit({ sessionId: resumable, message });
|
|
81204
|
-
|
|
81326
|
+
const starter = this.starterKey(by);
|
|
81327
|
+
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
81328
|
return { task: task2, duplicate: false };
|
|
81206
81329
|
}
|
|
81207
81330
|
const task = await this.submit({ message, start: { agent: team, workspaceDir, team, mission } });
|
|
81208
81331
|
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: [],
|
|
81332
|
+
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
81333
|
return { task, duplicate: false };
|
|
81211
81334
|
}
|
|
81212
81335
|
pauseInterrupted() {
|
|
@@ -81309,7 +81432,7 @@ var SessionHost = class {
|
|
|
81309
81432
|
for (const { relay: port } of this.options.ports.missionRelays()) {
|
|
81310
81433
|
const answered = await port.reply({ missionId, text: text3, ...questionId && { questionId }, by, ...this.claimOfBy(by) });
|
|
81311
81434
|
if (!answered) continue;
|
|
81312
|
-
await this.closeQuestions(missionId, questionId ?? void 0,
|
|
81435
|
+
await this.closeQuestions(missionId, questionId ?? void 0, by, text3);
|
|
81313
81436
|
return answered;
|
|
81314
81437
|
}
|
|
81315
81438
|
}
|
|
@@ -81320,14 +81443,14 @@ var SessionHost = class {
|
|
|
81320
81443
|
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
81444
|
const lead = known.sessionId;
|
|
81322
81445
|
this.missions.change(missionId, { state: "active", by, note: "reply" });
|
|
81323
|
-
await this.closeQuestions(missionId, null,
|
|
81446
|
+
await this.closeQuestions(missionId, null, by, text3);
|
|
81324
81447
|
await this.submit({ sessionId: lead, message: { messageId: randomUUID4(), role: "ROLE_USER", parts: [textPart(`[reply to your question on the mission ${missionId}, from ${by}]
|
|
81325
81448
|
${text3}`)] } });
|
|
81326
81449
|
return this.missionOf(missionId);
|
|
81327
81450
|
}
|
|
81328
81451
|
async answerAsked(by, missionId, lead, asked2, text3) {
|
|
81329
81452
|
this.missions.answer(missionId, asked2.id);
|
|
81330
|
-
await this.closeQuestions(missionId, asked2.id,
|
|
81453
|
+
await this.closeQuestions(missionId, asked2.id, by, text3);
|
|
81331
81454
|
const session = this.sessions.get(lead);
|
|
81332
81455
|
if (session?.running && !session.external) {
|
|
81333
81456
|
this.steering.set(lead, [...this.steering.get(lead) ?? [], { from: by, missionId, text: `reply to your question ${asked2.id}: ${asked2.question}
|
|
@@ -81344,7 +81467,7 @@ ${text3}`)] } });
|
|
|
81344
81467
|
const missionId = given.mission;
|
|
81345
81468
|
const known = this.missions.get(missionId);
|
|
81346
81469
|
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
|
|
81470
|
+
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
81471
|
this.askedInTurn.add(known.sessionId);
|
|
81349
81472
|
this.recordQuestion(missionId, known.sessionId, given.question);
|
|
81350
81473
|
return this.missionOf(missionId);
|
|
@@ -81352,8 +81475,8 @@ ${text3}`)] } });
|
|
|
81352
81475
|
recordQuestion(missionId, sessionId, question) {
|
|
81353
81476
|
const asked2 = { id: `q-${randomUUID4().slice(0, 8)}`, question, at: new Date(this.options.context.now()).toISOString() };
|
|
81354
81477
|
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.
|
|
81478
|
+
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 });
|
|
81479
|
+
this.tellReceivers(after.receivers, { team: after.team, mission: missionId, lead: after.lead, question: asked2.question, questionId: asked2.id }, sessionId);
|
|
81357
81480
|
}
|
|
81358
81481
|
async say(by, target, text3, reader, relay = true) {
|
|
81359
81482
|
const named2 = this.missions.get(target);
|
|
@@ -81409,16 +81532,28 @@ ${text3}`)] } });
|
|
|
81409
81532
|
const mission = message.metadata?.[ORBITAL_META.mission];
|
|
81410
81533
|
return typeof mission === "string" ? mission : null;
|
|
81411
81534
|
}
|
|
81412
|
-
async closeQuestions(mission, questionId, text3) {
|
|
81535
|
+
async closeQuestions(mission, questionId, by, text3) {
|
|
81536
|
+
const answerer = by.startsWith("session:") ? by.slice("session:".length) : null;
|
|
81537
|
+
const told = /* @__PURE__ */ new Set();
|
|
81413
81538
|
for (const [taskId, asked2] of [...this.questionDeliveries]) {
|
|
81414
81539
|
if (asked2.mission !== mission || questionId !== void 0 && asked2.questionId !== questionId) continue;
|
|
81415
81540
|
this.questionDeliveries.delete(taskId);
|
|
81416
81541
|
const task = this.tasks.get(taskId);
|
|
81417
|
-
const
|
|
81418
|
-
if (!task || !
|
|
81419
|
-
await this.finish(
|
|
81542
|
+
const receiver = this.taskSession.get(taskId);
|
|
81543
|
+
if (!task || !receiver || task.status && isTerminal(task.status.state)) continue;
|
|
81544
|
+
await this.finish(receiver, task, "TASK_STATE_COMPLETED", `answered by ${by}: ${text3}`);
|
|
81420
81545
|
this.externalWaits.get(taskId)?.();
|
|
81546
|
+
if (receiver !== answerer) this.tellAnswered(receiver, { kind: "answered", mission, question: asked2.questionId, by, text: text3 }, told);
|
|
81421
81547
|
}
|
|
81548
|
+
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);
|
|
81549
|
+
}
|
|
81550
|
+
tellAnswered(sessionId, answered, told) {
|
|
81551
|
+
const external = this.sessions.get(sessionId)?.external;
|
|
81552
|
+
if (!external?.attached || told.has(sessionId)) return;
|
|
81553
|
+
told.add(sessionId);
|
|
81554
|
+
const answers = (delivery) => delivery.mission === answered.mission && (answered.question === null || delivery.question === answered.question);
|
|
81555
|
+
for (let index = external.waiting.length - 1; index >= 0; index -= 1) if (answers(external.waiting[index])) external.waiting.splice(index, 1);
|
|
81556
|
+
for (const listener of external.listeners) listener(answered);
|
|
81422
81557
|
}
|
|
81423
81558
|
busyBelow(sessionId) {
|
|
81424
81559
|
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 +81580,46 @@ ${text3}`)] } });
|
|
|
81445
81580
|
}
|
|
81446
81581
|
this.nudged.delete(sessionId);
|
|
81447
81582
|
this.missions.change(known.mission, { state: "waiting", by: `session:${sessionId}`, note: question === "" ? NO_HANDOFF_NOTE : stateWord(state), question: question === "" ? null : question });
|
|
81448
|
-
this.
|
|
81583
|
+
this.tellReceivers(known.receivers, { team: known.team, mission: known.mission, lead: known.lead, question: question === "" ? NO_HANDOFF_TELL : question, questionId: null }, sessionId);
|
|
81449
81584
|
}
|
|
81450
|
-
async joinMission(
|
|
81451
|
-
const session = this.sessions.get(sessionId);
|
|
81452
|
-
if (!session
|
|
81585
|
+
async joinMission(receiver, missionId) {
|
|
81586
|
+
const session = "sessionId" in receiver ? this.sessions.get(receiver.sessionId) : void 0;
|
|
81587
|
+
if ("sessionId" in receiver && !session) throw new SessionHostError("not-found", `There is no session ${receiver.sessionId} on this node.`);
|
|
81453
81588
|
const known = this.missions.get(missionId);
|
|
81454
|
-
if (!known) {
|
|
81589
|
+
if (!known && session) {
|
|
81455
81590
|
for (const { relay } of this.options.ports.missionRelays()) {
|
|
81456
|
-
const joined = await relay.join({ missionId,
|
|
81591
|
+
const joined = await relay.join({ missionId, receiver: { node: this.node, sessionId: session.id }, ...this.claimOfSession(session.id) });
|
|
81457
81592
|
if (joined) return joined;
|
|
81458
81593
|
}
|
|
81459
81594
|
}
|
|
81460
|
-
|
|
81595
|
+
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.`);
|
|
81596
|
+
if (!session) return this.joinHere(externalReceiver("externalId" in receiver ? receiver.externalId : ""), "cli", missionId, null);
|
|
81597
|
+
return this.joinHere(this.receiverKey(session), `session:${session.id}`, missionId, [session.id]);
|
|
81461
81598
|
}
|
|
81462
81599
|
async joinRemote(request) {
|
|
81463
|
-
|
|
81600
|
+
const key2 = this.keyOfRemote(request.receiver);
|
|
81601
|
+
const local = remoteReceiverOf(key2) === null ? [request.receiver.sessionId] : null;
|
|
81602
|
+
return this.joinHere(key2, `session:${receiverRef(request.receiver)}`, request.missionId, local);
|
|
81464
81603
|
}
|
|
81465
|
-
async
|
|
81466
|
-
const
|
|
81467
|
-
|
|
81604
|
+
async leaveMission(receiver, missionId) {
|
|
81605
|
+
const known = this.missions.get(missionId);
|
|
81606
|
+
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.`);
|
|
81607
|
+
const session = "sessionId" in receiver ? this.sessions.get(receiver.sessionId) : void 0;
|
|
81608
|
+
const leaves = (key2) => session ? this.receives(session, key2) : "sessionId" in receiver ? key2 === receiver.sessionId : externalIdOf(key2) === receiver.externalId;
|
|
81609
|
+
const kept = known.receivers.filter((key2) => !leaves(key2));
|
|
81610
|
+
if (kept.length === known.receivers.length) return this.missionOf(missionId);
|
|
81611
|
+
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 });
|
|
81612
|
+
return this.missionOf(missionId);
|
|
81613
|
+
}
|
|
81614
|
+
async joinHere(key2, by, missionId, deliverTo) {
|
|
81468
81615
|
const known = this.missions.get(missionId);
|
|
81469
81616
|
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
|
-
|
|
81617
|
+
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] });
|
|
81618
|
+
const remote = remoteReceiverOf(key2);
|
|
81619
|
+
const live2 = this.liveReceivers([key2]).filter((session) => deliverTo === null || deliverTo.includes(session.id));
|
|
81620
|
+
for (const open2 of this.openQuestions(stored)) {
|
|
81621
|
+
if (remote && remote.node !== this.node) this.askRemote(remote, open2, stored.sessionId);
|
|
81622
|
+
for (const session of live2) if (!this.questionOpen(session.id, open2.mission, open2.questionId)) this.deliverQuestion(session.id, open2, stored.sessionId);
|
|
81474
81623
|
}
|
|
81475
81624
|
return this.missionOf(missionId);
|
|
81476
81625
|
}
|
|
@@ -81480,36 +81629,34 @@ ${text3}`)] } });
|
|
|
81480
81629
|
missionTeam(missionId) {
|
|
81481
81630
|
return this.missions.get(missionId)?.team ?? null;
|
|
81482
81631
|
}
|
|
81483
|
-
async
|
|
81484
|
-
const
|
|
81485
|
-
if (!
|
|
81486
|
-
this.deliverQuestion(
|
|
81632
|
+
async askReceiver(question) {
|
|
81633
|
+
const session = this.sessions.get(question.receiver.sessionId);
|
|
81634
|
+
if (!session || question.receiver.node !== this.node || this.liveReceivers([session.id]).length === 0) return false;
|
|
81635
|
+
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
81636
|
return true;
|
|
81488
81637
|
}
|
|
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;
|
|
81638
|
+
tellReceivers(receivers, open2, leadSessionId) {
|
|
81639
|
+
for (const key2 of receivers) {
|
|
81640
|
+
const remote = remoteReceiverOf(key2);
|
|
81641
|
+
if (remote && remote.node !== this.node) this.askRemote(remote, open2, leadSessionId);
|
|
81500
81642
|
}
|
|
81501
|
-
const
|
|
81502
|
-
|
|
81503
|
-
|
|
81643
|
+
for (const session of this.liveReceivers(receivers)) this.deliverQuestion(session.id, open2, leadSessionId);
|
|
81644
|
+
}
|
|
81645
|
+
askRemote(receiver, open2, leadSessionId) {
|
|
81646
|
+
const question = { missionId: open2.mission, node: this.node, lead: open2.lead, leadSessionId, question: open2.question, ...open2.questionId && { questionId: open2.questionId }, receiver, team: open2.team };
|
|
81647
|
+
void (async () => {
|
|
81648
|
+
for (const { relay } of this.options.ports.missionRelays()) if (await relay.ask(question)) return;
|
|
81649
|
+
this.options.context.output.warn(`no plugin carried the question of ${open2.mission} to ${receiverRef(receiver)}`);
|
|
81650
|
+
})().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
81651
|
}
|
|
81505
|
-
deliverQuestion(
|
|
81652
|
+
deliverQuestion(receiverId, known, sessionId) {
|
|
81506
81653
|
const missionId = known.mission;
|
|
81507
81654
|
const question = known.question;
|
|
81508
81655
|
const questionId = known.questionId;
|
|
81509
|
-
const key2 = `${
|
|
81656
|
+
const key2 = `${receiverId}:${missionId}:${questionId ?? ""}`;
|
|
81510
81657
|
this.questionsInFlight.add(key2);
|
|
81511
81658
|
void this.submit({
|
|
81512
|
-
sessionId:
|
|
81659
|
+
sessionId: receiverId,
|
|
81513
81660
|
message: {
|
|
81514
81661
|
messageId: randomUUID4(),
|
|
81515
81662
|
role: "ROLE_USER",
|
|
@@ -81524,7 +81671,7 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
81524
81671
|
}
|
|
81525
81672
|
}).then((task) => {
|
|
81526
81673
|
this.questionDeliveries.set(task.id, { mission: known.mission, questionId });
|
|
81527
|
-
}).catch((error62) => this.options.context.output.warn(`could not tell the
|
|
81674
|
+
}).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
81675
|
}
|
|
81529
81676
|
async missionActivity(missionId, limit) {
|
|
81530
81677
|
const sessionId = this.missions.get(missionId)?.sessionId;
|
|
@@ -81742,8 +81889,8 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
81742
81889
|
}
|
|
81743
81890
|
}
|
|
81744
81891
|
if (signal?.aborted) return;
|
|
81745
|
-
await new Promise((
|
|
81746
|
-
wake =
|
|
81892
|
+
await new Promise((resolve27) => {
|
|
81893
|
+
wake = resolve27;
|
|
81747
81894
|
});
|
|
81748
81895
|
wake = null;
|
|
81749
81896
|
}
|
|
@@ -81819,9 +81966,13 @@ Answer with missionSay on ${missionId}.`)],
|
|
|
81819
81966
|
}
|
|
81820
81967
|
missionRoots(readerSessionId) {
|
|
81821
81968
|
const outside = this.sessions.get(readerSessionId)?.external !== void 0;
|
|
81822
|
-
const missionRoots = this.missions.all().filter((stored) => stored.sessionId !== null && (outside || stored.
|
|
81969
|
+
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
81970
|
return new Set(missionRoots);
|
|
81824
81971
|
}
|
|
81972
|
+
receivesFor(sessionId, receivers) {
|
|
81973
|
+
const session = this.sessions.get(sessionId);
|
|
81974
|
+
return session !== void 0 && receivers.some((key2) => this.receives(session, key2));
|
|
81975
|
+
}
|
|
81825
81976
|
reachRoots(readerSessionId) {
|
|
81826
81977
|
const outside = this.sessions.get(readerSessionId)?.external !== void 0;
|
|
81827
81978
|
const attachedRoots = outside ? [...this.sessions.values()].filter((one) => one.external?.attached === true).map((one) => this.rootOf(one.id)) : [];
|
|
@@ -83390,7 +83541,7 @@ var StdioClientTransport = class {
|
|
|
83390
83541
|
if (this._process) {
|
|
83391
83542
|
throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
|
83392
83543
|
}
|
|
83393
|
-
return new Promise((
|
|
83544
|
+
return new Promise((resolve27, reject) => {
|
|
83394
83545
|
this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], {
|
|
83395
83546
|
// merge default env with server env because mcp server needs some env vars
|
|
83396
83547
|
env: {
|
|
@@ -83407,7 +83558,7 @@ var StdioClientTransport = class {
|
|
|
83407
83558
|
this.onerror?.(error62);
|
|
83408
83559
|
});
|
|
83409
83560
|
this._process.on("spawn", () => {
|
|
83410
|
-
|
|
83561
|
+
resolve27();
|
|
83411
83562
|
});
|
|
83412
83563
|
this._process.on("close", (_code) => {
|
|
83413
83564
|
this._process = void 0;
|
|
@@ -83472,22 +83623,22 @@ var StdioClientTransport = class {
|
|
|
83472
83623
|
if (this._process) {
|
|
83473
83624
|
const processToClose = this._process;
|
|
83474
83625
|
this._process = void 0;
|
|
83475
|
-
const closePromise = new Promise((
|
|
83626
|
+
const closePromise = new Promise((resolve27) => {
|
|
83476
83627
|
processToClose.once("close", () => {
|
|
83477
|
-
|
|
83628
|
+
resolve27();
|
|
83478
83629
|
});
|
|
83479
83630
|
});
|
|
83480
83631
|
try {
|
|
83481
83632
|
processToClose.stdin?.end();
|
|
83482
83633
|
} catch {
|
|
83483
83634
|
}
|
|
83484
|
-
await Promise.race([closePromise, new Promise((
|
|
83635
|
+
await Promise.race([closePromise, new Promise((resolve27) => setTimeout(resolve27, 2e3).unref())]);
|
|
83485
83636
|
if (processToClose.exitCode === null) {
|
|
83486
83637
|
try {
|
|
83487
83638
|
processToClose.kill("SIGTERM");
|
|
83488
83639
|
} catch {
|
|
83489
83640
|
}
|
|
83490
|
-
await Promise.race([closePromise, new Promise((
|
|
83641
|
+
await Promise.race([closePromise, new Promise((resolve27) => setTimeout(resolve27, 2e3).unref())]);
|
|
83491
83642
|
}
|
|
83492
83643
|
if (processToClose.exitCode === null) {
|
|
83493
83644
|
try {
|
|
@@ -83499,15 +83650,15 @@ var StdioClientTransport = class {
|
|
|
83499
83650
|
this._readBuffer.clear();
|
|
83500
83651
|
}
|
|
83501
83652
|
send(message) {
|
|
83502
|
-
return new Promise((
|
|
83653
|
+
return new Promise((resolve27) => {
|
|
83503
83654
|
if (!this._process?.stdin) {
|
|
83504
83655
|
throw new Error("Not connected");
|
|
83505
83656
|
}
|
|
83506
83657
|
const json4 = serializeMessage(message);
|
|
83507
83658
|
if (this._process.stdin.write(json4)) {
|
|
83508
|
-
|
|
83659
|
+
resolve27();
|
|
83509
83660
|
} else {
|
|
83510
|
-
this._process.stdin.once("drain",
|
|
83661
|
+
this._process.stdin.once("drain", resolve27);
|
|
83511
83662
|
}
|
|
83512
83663
|
});
|
|
83513
83664
|
}
|
|
@@ -83601,12 +83752,12 @@ var StdioServerTransport = class {
|
|
|
83601
83752
|
this.onclose?.();
|
|
83602
83753
|
}
|
|
83603
83754
|
send(message) {
|
|
83604
|
-
return new Promise((
|
|
83755
|
+
return new Promise((resolve27) => {
|
|
83605
83756
|
const json4 = serializeMessage(message);
|
|
83606
83757
|
if (this._stdout.write(json4)) {
|
|
83607
|
-
|
|
83758
|
+
resolve27();
|
|
83608
83759
|
} else {
|
|
83609
|
-
this._stdout.once("drain",
|
|
83760
|
+
this._stdout.once("drain", resolve27);
|
|
83610
83761
|
}
|
|
83611
83762
|
});
|
|
83612
83763
|
}
|
|
@@ -83718,6 +83869,7 @@ function agentTools(options) {
|
|
|
83718
83869
|
};
|
|
83719
83870
|
const send = (message, wait) => node2.send(message, wait);
|
|
83720
83871
|
const a2a = () => node2.a2a();
|
|
83872
|
+
const own2 = async () => options.attached ? await options.attached() : options.sessionId;
|
|
83721
83873
|
return [
|
|
83722
83874
|
defineMcpTool({
|
|
83723
83875
|
name: "agentList",
|
|
@@ -83761,7 +83913,7 @@ function agentTools(options) {
|
|
|
83761
83913
|
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
83914
|
inputSchema: {},
|
|
83763
83915
|
run: async () => {
|
|
83764
|
-
if (!
|
|
83916
|
+
if (!await own2()) throw new Error("agentPeers needs your own session: only an agent the node runs has peers.");
|
|
83765
83917
|
return await node2.peers();
|
|
83766
83918
|
}
|
|
83767
83919
|
}),
|
|
@@ -83841,13 +83993,13 @@ function agentTools(options) {
|
|
|
83841
83993
|
}),
|
|
83842
83994
|
defineMcpTool({
|
|
83843
83995
|
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
|
|
83996
|
+
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
83997
|
inputSchema: {},
|
|
83846
83998
|
run: async () => node2.missions()
|
|
83847
83999
|
}),
|
|
83848
84000
|
defineMcpTool({
|
|
83849
84001
|
name: "missionShow",
|
|
83850
|
-
description: "Show one mission by its id: its brief, its state, its open questions (questions: each lead question that waits on
|
|
84002
|
+
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
84003
|
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
83852
84004
|
run: async (params) => {
|
|
83853
84005
|
const found = (await node2.missions()).filter((record3) => record3.missionId === params.mission);
|
|
@@ -83899,7 +84051,7 @@ function agentTools(options) {
|
|
|
83899
84051
|
}),
|
|
83900
84052
|
defineMcpTool({
|
|
83901
84053
|
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.",
|
|
84054
|
+
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
84055
|
inputSchema: {
|
|
83904
84056
|
mission: MissionIdSchema.describe("The mission id."),
|
|
83905
84057
|
text: external_exports.string().min(1).describe("The answer, in full: the lead has none of your context."),
|
|
@@ -83933,13 +84085,22 @@ function agentTools(options) {
|
|
|
83933
84085
|
}),
|
|
83934
84086
|
defineMcpTool({
|
|
83935
84087
|
name: "missionJoin",
|
|
83936
|
-
description: "
|
|
84088
|
+
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
84089
|
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
83938
84090
|
run: async (params) => {
|
|
83939
|
-
if (!
|
|
84091
|
+
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
84092
|
return node2.joinMission(params.mission);
|
|
83941
84093
|
}
|
|
83942
84094
|
}),
|
|
84095
|
+
defineMcpTool({
|
|
84096
|
+
name: "missionLeave",
|
|
84097
|
+
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.",
|
|
84098
|
+
inputSchema: { mission: MissionIdSchema.describe("The mission id.") },
|
|
84099
|
+
run: async (params) => {
|
|
84100
|
+
if (!await own2()) throw new Error("missionLeave needs your own session: only a decision receiver leaves a mission.");
|
|
84101
|
+
return node2.leaveMission(params.mission);
|
|
84102
|
+
}
|
|
84103
|
+
}),
|
|
83943
84104
|
defineMcpTool({
|
|
83944
84105
|
name: "missionNew",
|
|
83945
84106
|
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 +84120,7 @@ function agentTools(options) {
|
|
|
83959
84120
|
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
84121
|
inputSchema: {},
|
|
83961
84122
|
run: async () => {
|
|
83962
|
-
if (!
|
|
84123
|
+
if (!await own2()) throw new Error("missionGet needs your own session: only an agent the node runs has a mission.");
|
|
83963
84124
|
return await node2.mission();
|
|
83964
84125
|
}
|
|
83965
84126
|
}),
|
|
@@ -83971,7 +84132,7 @@ function agentTools(options) {
|
|
|
83971
84132
|
state: external_exports.enum(["done", "failed"]).optional().describe("done when the end state is reached, failed when it cannot be. Default: done.")
|
|
83972
84133
|
},
|
|
83973
84134
|
run: async (params) => {
|
|
83974
|
-
if (!
|
|
84135
|
+
if (!await own2()) throw new Error("missionHandoff needs your own session: only the lead session of a mission hands it off.");
|
|
83975
84136
|
return await node2.handoff({ result: params.result, state: params.state ?? "done" });
|
|
83976
84137
|
}
|
|
83977
84138
|
}),
|
|
@@ -83984,18 +84145,18 @@ function agentTools(options) {
|
|
|
83984
84145
|
state: external_exports.string().min(1).optional().describe("The state of the linked thing, such as open, merged or closed.")
|
|
83985
84146
|
},
|
|
83986
84147
|
run: async (params) => {
|
|
83987
|
-
if (!
|
|
84148
|
+
if (!await own2()) throw new Error("missionLink needs your own session: only the lead session of a mission adds a link to it.");
|
|
83988
84149
|
return await node2.link({ label: params.label, url: params.url, state: params.state ?? null });
|
|
83989
84150
|
}
|
|
83990
84151
|
}),
|
|
83991
84152
|
defineMcpTool({
|
|
83992
84153
|
name: "missionAsk",
|
|
83993
|
-
description: "Ask the
|
|
84154
|
+
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
84155
|
inputSchema: {
|
|
83995
|
-
question: external_exports.string().min(1).describe("The question, in full, with the options you see and what each one means: the
|
|
84156
|
+
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
84157
|
},
|
|
83997
84158
|
run: async (params) => {
|
|
83998
|
-
if (!
|
|
84159
|
+
if (!await own2()) throw new Error("missionAsk needs your own session: only the lead session of a mission asks its decision receivers.");
|
|
83999
84160
|
return await node2.ask({ question: params.question });
|
|
84000
84161
|
}
|
|
84001
84162
|
})
|
|
@@ -84562,8 +84723,8 @@ function relaySender(plugin, by) {
|
|
|
84562
84723
|
function pluginSender(plugin) {
|
|
84563
84724
|
return { channel: plugin, peer: plugin, from: { kind: "node", name: plugin } };
|
|
84564
84725
|
}
|
|
84565
|
-
function remoteSessionSender(plugin,
|
|
84566
|
-
return { channel: plugin, peer:
|
|
84726
|
+
function remoteSessionSender(plugin, session) {
|
|
84727
|
+
return { channel: plugin, peer: session.node, from: { kind: "session", name: session.sessionId, ade: session.node } };
|
|
84567
84728
|
}
|
|
84568
84729
|
function missionTarget(node2, team) {
|
|
84569
84730
|
return team ? { kind: "team", name: team, ade: node2 } : { kind: "node", name: node2 };
|
|
@@ -84702,10 +84863,10 @@ function a2aHandlerFor(host, reader, confined = false, network = null) {
|
|
|
84702
84863
|
};
|
|
84703
84864
|
}
|
|
84704
84865
|
function readBody(request) {
|
|
84705
|
-
return new Promise((
|
|
84866
|
+
return new Promise((resolve27, reject) => {
|
|
84706
84867
|
const chunks = [];
|
|
84707
84868
|
request.on("data", (chunk) => chunks.push(chunk));
|
|
84708
|
-
request.on("end", () =>
|
|
84869
|
+
request.on("end", () => resolve27(Buffer.concat(chunks).toString("utf8")));
|
|
84709
84870
|
request.on("error", reject);
|
|
84710
84871
|
});
|
|
84711
84872
|
}
|
|
@@ -84769,7 +84930,7 @@ async function serveHostApi(request, response, url2, api) {
|
|
|
84769
84930
|
if (request.method === "POST" && url2.pathname === "/orbital/host/missions/ask") {
|
|
84770
84931
|
const asked2 = MissionQuestionSchema.safeParse(JSON.parse(await readBody(request) || "{}"));
|
|
84771
84932
|
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.
|
|
84933
|
+
return json3(response, 200, { delivered: await api.askReceiver(asked2.data) });
|
|
84773
84934
|
}
|
|
84774
84935
|
if (request.method === "POST" && url2.pathname === "/orbital/host/settled") {
|
|
84775
84936
|
const { orFollowing } = JSON.parse(await readBody(request) || "{}");
|
|
@@ -84888,11 +85049,16 @@ async function startNodeServer(options) {
|
|
|
84888
85049
|
const by = caller.bound ? `session:${caller.bound}` : "cli";
|
|
84889
85050
|
return json3(response, 200, control[1] === "pause" ? await options.host.pauseMission(by, asked2.data.missionId) : await options.host.resumeMission(by, asked2.data.missionId));
|
|
84890
85051
|
}
|
|
84891
|
-
|
|
84892
|
-
|
|
85052
|
+
const membership = /^\/orbital\/missions\/(join|leave)$/.exec(url2.pathname);
|
|
85053
|
+
if (request.method === "POST" && membership) {
|
|
84893
85054
|
const asked2 = MissionJoinSchema.safeParse(JSON.parse(await readBody(request) || "{}"));
|
|
84894
85055
|
if (!asked2.success) return json3(response, 400, { error: asked2.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ") });
|
|
84895
|
-
|
|
85056
|
+
const session = caller.bound ?? caller.reader;
|
|
85057
|
+
const holder = caller.presented === caller.token;
|
|
85058
|
+
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." });
|
|
85059
|
+
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.` });
|
|
85060
|
+
const receiver = asked2.data.externalId ? { externalId: asked2.data.externalId } : { sessionId: session };
|
|
85061
|
+
return json3(response, 200, membership[1] === "join" ? await options.host.joinMission(receiver, asked2.data.missionId) : await options.host.leaveMission(receiver, asked2.data.missionId));
|
|
84896
85062
|
}
|
|
84897
85063
|
if (request.method === "POST" && url2.pathname === "/orbital/missions/new") {
|
|
84898
85064
|
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 +85096,7 @@ async function startNodeServer(options) {
|
|
|
84930
85096
|
const named2 = asked2.data.missionId ?? own2?.missionId ?? null;
|
|
84931
85097
|
if (!named2) return json3(response, 400, { error: "Name the mission by its id." });
|
|
84932
85098
|
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
|
|
85099
|
+
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
85100
|
return json3(response, 200, await options.host.askMission({ mission: named2, question: asked2.data.question, sessionId: session }));
|
|
84935
85101
|
}
|
|
84936
85102
|
const activity = /^\/orbital\/missions\/([^/]+)\/activity$/.exec(url2.pathname);
|
|
@@ -84971,6 +85137,20 @@ async function startNodeServer(options) {
|
|
|
84971
85137
|
return json3(response, 502, { error: `The panel ${plugin2}/${id} did not answer: ${error62 instanceof Error ? error62.message : String(error62)}` });
|
|
84972
85138
|
}
|
|
84973
85139
|
}
|
|
85140
|
+
const commandPath = /^\/orbital\/plugins\/([^/]+)\/commands\/([^/]+)$/.exec(url2.pathname);
|
|
85141
|
+
if (request.method === "POST" && commandPath) {
|
|
85142
|
+
if (presented !== token) return json3(response, 403, { error: "Only a holder of the node secret runs a command of a plugin." });
|
|
85143
|
+
const [name, command] = [decodeURIComponent(commandPath[1]), decodeURIComponent(commandPath[2])];
|
|
85144
|
+
const plugin2 = options.ports.plugins.find((one) => one.name === name);
|
|
85145
|
+
if (!plugin2?.command) return json3(response, 404, { error: `No plugin ${name} with commands runs on this node.` });
|
|
85146
|
+
const asked2 = PluginCommandRequestSchema.safeParse(await readBody(request).then((raw) => JSON.parse(raw)).catch(() => null));
|
|
85147
|
+
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.` });
|
|
85148
|
+
try {
|
|
85149
|
+
return json3(response, 200, await plugin2.command(asked2.data));
|
|
85150
|
+
} catch (error62) {
|
|
85151
|
+
return json3(response, 502, { error: `The command ${name} ${command} failed: ${error62 instanceof Error ? error62.message : String(error62)}` });
|
|
85152
|
+
}
|
|
85153
|
+
}
|
|
84974
85154
|
if (url2.pathname === "/orbital/telemetry/snapshot" || url2.pathname === "/orbital/telemetry/events") {
|
|
84975
85155
|
if (request.method === "GET" && url2.pathname === "/orbital/telemetry/snapshot" && (presented === token || bound)) return json3(response, 200, await options.host.snapshot());
|
|
84976
85156
|
if (presented !== token) return json3(response, 403, { error: "Only a holder of the node secret reads the telemetry stream of the node." });
|
|
@@ -85130,7 +85310,7 @@ async function startNodeServer(options) {
|
|
|
85130
85310
|
const result2 = await dispatchA2a(await readBody(request), a2aHandlerFor(options.host, reader, bound !== null, httpSender(request.socket.remoteAddress)));
|
|
85131
85311
|
if (result2.kind === "json") return json3(response, 200, result2.body);
|
|
85132
85312
|
response.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
|
|
85133
|
-
const closed = new Promise((
|
|
85313
|
+
const closed = new Promise((resolve27) => response.on("close", resolve27));
|
|
85134
85314
|
const iterator = result2.events[Symbol.asyncIterator]();
|
|
85135
85315
|
for (; ; ) {
|
|
85136
85316
|
const next = await Promise.race([iterator.next(), closed.then(() => null)]);
|
|
@@ -85143,9 +85323,9 @@ async function startNodeServer(options) {
|
|
|
85143
85323
|
}
|
|
85144
85324
|
response.end();
|
|
85145
85325
|
}
|
|
85146
|
-
await new Promise((
|
|
85326
|
+
await new Promise((resolve27, reject) => {
|
|
85147
85327
|
server.once("error", reject);
|
|
85148
|
-
server.listen(options.port, address, () =>
|
|
85328
|
+
server.listen(options.port, address, () => resolve27());
|
|
85149
85329
|
});
|
|
85150
85330
|
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
85331
|
mkdirSync17(nodeDir(options.home, options.name), { recursive: true, mode: 448 });
|
|
@@ -85156,7 +85336,7 @@ async function startNodeServer(options) {
|
|
|
85156
85336
|
close: async () => {
|
|
85157
85337
|
if (readNodeInfo(options.home, options.name)?.pid === process.pid) rmSync9(nodeInfoPath(options.home, options.name), { force: true });
|
|
85158
85338
|
server.closeAllConnections();
|
|
85159
|
-
await new Promise((
|
|
85339
|
+
await new Promise((resolve27) => server.close(() => resolve27()));
|
|
85160
85340
|
}
|
|
85161
85341
|
};
|
|
85162
85342
|
}
|
|
@@ -86166,7 +86346,7 @@ function hostApiOf(host, plugin, catalog, channels, models) {
|
|
|
86166
86346
|
startMission: (start) => host.startMission(plugin, start),
|
|
86167
86347
|
missions: () => host.missionList(),
|
|
86168
86348
|
joinMission: async (request) => {
|
|
86169
|
-
await host.receive(networkReceive(remoteSessionSender(plugin, request.
|
|
86349
|
+
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
86350
|
return host.joinRemote(request);
|
|
86171
86351
|
},
|
|
86172
86352
|
replyMission: async (request) => {
|
|
@@ -86181,9 +86361,9 @@ function hostApiOf(host, plugin, catalog, channels, models) {
|
|
|
86181
86361
|
await relayed(request, `resume the mission ${request.missionId}`);
|
|
86182
86362
|
return host.resumeMission(request.by, request.missionId, false);
|
|
86183
86363
|
},
|
|
86184
|
-
|
|
86185
|
-
await host.receive(networkReceive(remoteSessionSender(plugin, { node: question.node, sessionId: question.leadSessionId }), { kind: "session", name: question.
|
|
86186
|
-
return host.
|
|
86364
|
+
askReceiver: async (question) => {
|
|
86365
|
+
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 })));
|
|
86366
|
+
return host.askReceiver(question);
|
|
86187
86367
|
},
|
|
86188
86368
|
sayMission: async (request) => {
|
|
86189
86369
|
await relayed(request, request.text);
|
|
@@ -86399,8 +86579,8 @@ var ChannelHub = class {
|
|
|
86399
86579
|
if (state && isTerminal(state)) return;
|
|
86400
86580
|
}
|
|
86401
86581
|
if (signal?.aborted) return;
|
|
86402
|
-
await new Promise((
|
|
86403
|
-
wake =
|
|
86582
|
+
await new Promise((resolve27) => {
|
|
86583
|
+
wake = resolve27;
|
|
86404
86584
|
});
|
|
86405
86585
|
wake = null;
|
|
86406
86586
|
}
|
|
@@ -86574,8 +86754,8 @@ Answer yes or no in this thread.`)], metadata: { [ORBITAL_META.channel]: binding
|
|
|
86574
86754
|
thread: null
|
|
86575
86755
|
});
|
|
86576
86756
|
const threadKey = `${binding.channel}|${binding.id}|${sent.thread ?? sent.ref}`;
|
|
86577
|
-
const answer2 = await new Promise((
|
|
86578
|
-
this.asks.set(threadKey, { resolve:
|
|
86757
|
+
const answer2 = await new Promise((resolve27, reject) => {
|
|
86758
|
+
this.asks.set(threadKey, { resolve: resolve27, question, channel: binding.channel, since: new Date(this.options.context.now()).toISOString() });
|
|
86579
86759
|
signal?.addEventListener("abort", () => {
|
|
86580
86760
|
this.asks.delete(threadKey);
|
|
86581
86761
|
reject(new Error("The question was withdrawn."));
|
|
@@ -87250,8 +87430,9 @@ var panel = {
|
|
|
87250
87430
|
var panelCommands = [panel];
|
|
87251
87431
|
|
|
87252
87432
|
// src/core/cli/plugin.ts
|
|
87253
|
-
|
|
87254
|
-
import {
|
|
87433
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
87434
|
+
import { readFileSync as readFileSync29 } from "node:fs";
|
|
87435
|
+
import { basename as basename9, resolve as resolve15 } from "node:path";
|
|
87255
87436
|
|
|
87256
87437
|
// src/sdk/index.ts
|
|
87257
87438
|
init_protocol();
|
|
@@ -87632,13 +87813,148 @@ init_protocol();
|
|
|
87632
87813
|
|
|
87633
87814
|
// src/core/cli/plugin.ts
|
|
87634
87815
|
init_protocol();
|
|
87816
|
+
|
|
87817
|
+
// src/core/cli/plugin-commands.ts
|
|
87818
|
+
init_protocol();
|
|
87819
|
+
init_command();
|
|
87820
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28 } from "node:fs";
|
|
87821
|
+
import { join as join37, resolve as resolve14 } from "node:path";
|
|
87822
|
+
var INTROSPECTION_WORDS = ["describe", "schema", "help"];
|
|
87823
|
+
function commandsOf(dir) {
|
|
87824
|
+
const descriptor = join37(dir, PLUGIN_PACKAGE_FILE);
|
|
87825
|
+
if (!existsSync30(descriptor)) return [];
|
|
87826
|
+
try {
|
|
87827
|
+
return PluginPackageSchema.parse(JSON.parse(readFileSync28(descriptor, "utf8"))).commands;
|
|
87828
|
+
} catch {
|
|
87829
|
+
return [];
|
|
87830
|
+
}
|
|
87831
|
+
}
|
|
87832
|
+
function declaredPluginCommands(workspaceDir, env3) {
|
|
87833
|
+
try {
|
|
87834
|
+
const source = definitionSource(workspaceDir, env3);
|
|
87835
|
+
const config2 = source ? workspaceConfig(workspaceDir, env3) : null;
|
|
87836
|
+
if (!source || !config2) return [];
|
|
87837
|
+
return Object.entries(config2.plugins).filter(([, entry]) => entry.enabled && entry.source).flatMap(([plugin, entry]) => {
|
|
87838
|
+
const from = pluginSource(plugin, entry);
|
|
87839
|
+
const commands = from.startsWith("exec:") ? [] : commandsOf(pluginDir(plugin, from, { harnessRoot: source.root, home: orbitalHome(env3) }));
|
|
87840
|
+
return commands.length > 0 ? [{ plugin, commands }] : [];
|
|
87841
|
+
});
|
|
87842
|
+
} catch {
|
|
87843
|
+
return [];
|
|
87844
|
+
}
|
|
87845
|
+
}
|
|
87846
|
+
function workspaceOf(input2, fallback = ".") {
|
|
87847
|
+
return resolve14(typeof input2.flags.workspace === "string" ? input2.flags.workspace : fallback);
|
|
87848
|
+
}
|
|
87849
|
+
async function startWorkspacePlugin(context, workspaceDir, name, terminal2) {
|
|
87850
|
+
const root = definitionSource(workspaceDir, context.env)?.root ?? null;
|
|
87851
|
+
const config2 = workspaceConfig(workspaceDir, context.env);
|
|
87852
|
+
const entry = config2?.plugins[name];
|
|
87853
|
+
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>` });
|
|
87854
|
+
const secrets = new PluginSecrets(context.env);
|
|
87855
|
+
return startRemotePlugin({
|
|
87856
|
+
name,
|
|
87857
|
+
source: pluginSource(name, entry),
|
|
87858
|
+
config: entry.config,
|
|
87859
|
+
entryEnv: entry.env,
|
|
87860
|
+
node: { name: `plugin-${name}`, workspaceDir, harness: defaultHarness(context.env), harnesses: [...RUNNER_HARNESSES], once: true, idleExitMs: null, project: null },
|
|
87861
|
+
roots: { harnessRoot: root, home: orbitalHome(context.env) },
|
|
87862
|
+
offline: config2.offline,
|
|
87863
|
+
policy: new PolicyChain([]),
|
|
87864
|
+
env: context.env,
|
|
87865
|
+
log: (level, message) => level === "info" ? context.output.step(message) : context.output.warn(message),
|
|
87866
|
+
secret: async (secret) => secrets.get(name, secret),
|
|
87867
|
+
restarts: 0,
|
|
87868
|
+
...terminal2 && { terminal: terminal2 }
|
|
87869
|
+
});
|
|
87870
|
+
}
|
|
87871
|
+
function flagOf(option) {
|
|
87872
|
+
return {
|
|
87873
|
+
name: option.name,
|
|
87874
|
+
type: option.type,
|
|
87875
|
+
summary: option.summary,
|
|
87876
|
+
...option.required && { required: true },
|
|
87877
|
+
...option.default !== void 0 && { default: option.default },
|
|
87878
|
+
...option.values && { enumValues: option.values }
|
|
87879
|
+
};
|
|
87880
|
+
}
|
|
87881
|
+
function requestOf(command, input2, terminal2) {
|
|
87882
|
+
const args = Object.fromEntries(command.args.flatMap((arg) => {
|
|
87883
|
+
const value2 = input2.positionals[arg.name];
|
|
87884
|
+
return value2 === void 0 ? [] : [[arg.name, value2]];
|
|
87885
|
+
}));
|
|
87886
|
+
const options = Object.fromEntries(command.options.flatMap((option) => {
|
|
87887
|
+
const value2 = input2.flags[option.name];
|
|
87888
|
+
if (option.type === "boolean") return [[option.name, value2 === true]];
|
|
87889
|
+
return typeof value2 === "string" || typeof value2 === "number" ? [[option.name, value2]] : [];
|
|
87890
|
+
}));
|
|
87891
|
+
return { name: command.name, args, options, terminal: terminal2 };
|
|
87892
|
+
}
|
|
87893
|
+
async function runOnNode(context, workspaceDir, input2, plugin, request) {
|
|
87894
|
+
const configured = workspaceConfig(workspaceDir, context.env)?.node ?? null;
|
|
87895
|
+
const name = chooseNode({ ...typeof input2.flags.node === "string" && { flag: input2.flags.node }, env: context.env, configured, workspaceDir }).name;
|
|
87896
|
+
const node2 = await liveNode(orbitalHome(context.env), name, context.fetch);
|
|
87897
|
+
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` });
|
|
87898
|
+
const response = await context.fetch(`${node2.url}/orbital/plugins/${encodeURIComponent(plugin)}/commands/${encodeURIComponent(request.name)}`, {
|
|
87899
|
+
method: "POST",
|
|
87900
|
+
headers: { authorization: `Bearer ${node2.token}`, "content-type": "application/json" },
|
|
87901
|
+
body: JSON.stringify(request)
|
|
87902
|
+
});
|
|
87903
|
+
const body2 = await response.json();
|
|
87904
|
+
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 });
|
|
87905
|
+
return PluginCommandResultSchema.parse(body2);
|
|
87906
|
+
}
|
|
87907
|
+
async function runInPlugin(context, workspaceDir, input2, plugin, command, terminal2) {
|
|
87908
|
+
if (command.interactive && context.nonInteractive) {
|
|
87909
|
+
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." });
|
|
87910
|
+
}
|
|
87911
|
+
const given = command.interactive ? { input: terminal2.input, output: context.outputMode === "text" ? terminal2.output : terminal2.error, error: terminal2.error } : void 0;
|
|
87912
|
+
const started = await startWorkspacePlugin(context, workspaceDir, plugin, given);
|
|
87913
|
+
try {
|
|
87914
|
+
return await started.command(requestOf(command, input2, given ? { tty: context.isTty } : null));
|
|
87915
|
+
} catch (error62) {
|
|
87916
|
+
if (error62 instanceof CliError) throw error62;
|
|
87917
|
+
const message = error62 instanceof Error ? error62.message : String(error62);
|
|
87918
|
+
throw new CliError({ code: "internal", message: error62 instanceof RpcError ? `The command ${plugin} ${command.name} failed: ${message}` : `The command ${plugin} ${command.name} got no answer: ${message}`, exitCode: ExitCode.Error });
|
|
87919
|
+
} finally {
|
|
87920
|
+
await started.close?.();
|
|
87921
|
+
}
|
|
87922
|
+
}
|
|
87923
|
+
function specOf(workspaceDir, plugin, command, terminal2) {
|
|
87924
|
+
return {
|
|
87925
|
+
path: [plugin, command.name],
|
|
87926
|
+
summary: command.summary,
|
|
87927
|
+
group: CommandGroup.Packs,
|
|
87928
|
+
credential: "none",
|
|
87929
|
+
flags: [
|
|
87930
|
+
{ name: "workspace", type: "path", summary: `The project whose ${ORBITAL_FILE} names the plugin. Default: the current directory.` },
|
|
87931
|
+
...command.node ? [{ name: "node", type: "string", identifier: true, summary: "The node that runs the plugin. Default: the node of this workspace." }] : [],
|
|
87932
|
+
...command.options.map(flagOf)
|
|
87933
|
+
],
|
|
87934
|
+
positionals: command.args.map((arg) => ({ name: arg.name, type: "string", summary: arg.summary, ...arg.required && { required: true }, ...arg.variadic && { variadic: true } })),
|
|
87935
|
+
outputShape: { kind: "object", ref: "PluginCommandResult", fields: [{ name: "data", type: "object", summary: `What the command of the plugin ${plugin} answered.` }] },
|
|
87936
|
+
exitCodes: [0, 1, 2, 3, 5],
|
|
87937
|
+
routes: [],
|
|
87938
|
+
examples: [{ command: `orbital ${plugin} ${command.name}`, why: command.summary }],
|
|
87939
|
+
run: async (input2, context) => {
|
|
87940
|
+
const workspace = workspaceOf(input2, workspaceDir);
|
|
87941
|
+
const result2 = command.node ? await runOnNode(context, workspace, input2, plugin, requestOf(command, input2, null)) : await runInPlugin(context, workspace, input2, plugin, command, terminal2);
|
|
87942
|
+
if (result2.text !== null) context.output.line(result2.text);
|
|
87943
|
+
if (result2.text === null && Object.keys(result2.data).length > 0) context.output.line(JSON.stringify(result2.data, null, 2));
|
|
87944
|
+
return { data: result2.data, ...result2.exitCode !== 0 && { exitCode: result2.exitCode } };
|
|
87945
|
+
}
|
|
87946
|
+
};
|
|
87947
|
+
}
|
|
87948
|
+
function pluginCommandSpecs(options) {
|
|
87949
|
+
const terminal2 = options.terminal ?? { input: 0, output: 1, error: 2 };
|
|
87950
|
+
return declaredPluginCommands(options.workspaceDir, options.env).filter(({ plugin }) => !options.reserved.includes(plugin)).flatMap(({ plugin, commands }) => commands.map((command) => specOf(options.workspaceDir, plugin, command, terminal2)));
|
|
87951
|
+
}
|
|
87952
|
+
|
|
87953
|
+
// src/core/cli/plugin.ts
|
|
87635
87954
|
init_command();
|
|
87636
87955
|
function workspaceFlag() {
|
|
87637
87956
|
return { name: "workspace", type: "path", summary: `The project whose ${ORBITAL_FILE} names the plugin. Default: the current directory.` };
|
|
87638
87957
|
}
|
|
87639
|
-
function workspaceOf(input2) {
|
|
87640
|
-
return resolve14(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
87641
|
-
}
|
|
87642
87958
|
function sourceOrRefuse(context, input2) {
|
|
87643
87959
|
const workspaceDir = workspaceOf(input2);
|
|
87644
87960
|
const source = definitionSource(workspaceDir, context.env);
|
|
@@ -87662,6 +87978,25 @@ async function manifestOf(context, workspaceDir, name, source, root, config2) {
|
|
|
87662
87978
|
await plugin.close?.();
|
|
87663
87979
|
return plugin.manifest;
|
|
87664
87980
|
}
|
|
87981
|
+
function configOf(input2, known) {
|
|
87982
|
+
const file2 = typeof input2.flags["config-file"] === "string" ? resolve15(input2.flags["config-file"]) : null;
|
|
87983
|
+
const fromFile = file2 ? (0, import_yaml8.parse)(readFileSync29(file2, "utf8")) : {};
|
|
87984
|
+
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." });
|
|
87985
|
+
const pairs = Array.isArray(input2.flags.config) ? input2.flags.config.map(String) : typeof input2.flags.config === "string" ? [input2.flags.config] : [];
|
|
87986
|
+
const fromPairs = Object.fromEntries(pairs.map((pair) => {
|
|
87987
|
+
const at = pair.indexOf("=");
|
|
87988
|
+
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." });
|
|
87989
|
+
return [pair.slice(0, at), (0, import_yaml8.parse)(pair.slice(at + 1)) ?? ""];
|
|
87990
|
+
}));
|
|
87991
|
+
return { ...known, ...fromFile, ...fromPairs };
|
|
87992
|
+
}
|
|
87993
|
+
async function checkedManifest(context, workspaceDir, name, source, root, config2) {
|
|
87994
|
+
try {
|
|
87995
|
+
return await manifestOf(context, workspaceDir, name, source, root, config2);
|
|
87996
|
+
} catch (error62) {
|
|
87997
|
+
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>." });
|
|
87998
|
+
}
|
|
87999
|
+
}
|
|
87665
88000
|
function permissionLines(manifest) {
|
|
87666
88001
|
const { network, secrets, models } = manifest.permissions;
|
|
87667
88002
|
return [
|
|
@@ -87677,7 +88012,9 @@ var add = () => ({
|
|
|
87677
88012
|
flags: [
|
|
87678
88013
|
workspaceFlag(),
|
|
87679
88014
|
{ 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.` }
|
|
88015
|
+
{ 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.` },
|
|
88016
|
+
{ 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.` },
|
|
88017
|
+
{ 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
88018
|
],
|
|
87682
88019
|
positionals: [
|
|
87683
88020
|
{ name: "plugin", type: "string", required: true, summary: "<name>@<marketplace>, npm:<package>, git:<url>, or a folder such as ./plugins/mine." }
|
|
@@ -87698,34 +88035,39 @@ var add = () => ({
|
|
|
87698
88035
|
examples: [
|
|
87699
88036
|
{ command: "orbital plugin add slack@community", why: "Install the slack plugin from the community marketplace, and use it in this workspace." },
|
|
87700
88037
|
{ 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." }
|
|
88038
|
+
{ 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." },
|
|
88039
|
+
{ command: "orbital plugin add ./plugins/notify --config channel=general --config retries=3", why: "Add a plugin that checks its config when it starts." }
|
|
87702
88040
|
],
|
|
87703
88041
|
run: async (input2, context) => {
|
|
87704
88042
|
const home = orbitalHome(context.env);
|
|
87705
88043
|
const given = String(input2.positionals.plugin);
|
|
87706
88044
|
const ref = resolvePluginRef(home, given);
|
|
87707
88045
|
const copied = ref.kind !== "folder" || given.includes("@");
|
|
87708
|
-
const
|
|
87709
|
-
|
|
87710
|
-
|
|
88046
|
+
const install2 = copied ? installPlugin(home, ref) : null;
|
|
88047
|
+
const installed = install2?.dir ?? null;
|
|
88048
|
+
const name = install2?.name ?? ref.name ?? given;
|
|
88049
|
+
if (install2) context.output.step(`\u2713 installed ${name} in ${install2.dir}`);
|
|
88050
|
+
if (input2.flags["install-only"] === true) return { data: { name, source: given, installed, enabled: false, manifest: null } };
|
|
87711
88051
|
if (input2.flags.user === true) {
|
|
87712
88052
|
const source2 = ref.kind === "folder" && !given.includes("@") ? ref.dir : ref.kind === "npm" ? `npm:${ref.pkg}` : ref.kind === "git" ? `git:${ref.url}` : given;
|
|
87713
|
-
const
|
|
87714
|
-
context
|
|
88053
|
+
const config3 = configOf(input2, userPlugins(context.env)[name]?.config ?? {});
|
|
88054
|
+
const manifest2 = await checkedManifest(context, workspaceOf(input2), name, source2, null, config3);
|
|
88055
|
+
context.output.step(`${name} ${manifest2.version} reaches:`);
|
|
87715
88056
|
for (const line of permissionLines(manifest2)) context.output.step(line);
|
|
87716
|
-
setUserPlugin(context.env,
|
|
87717
|
-
context.output.step(`\u2713 your user config names ${
|
|
87718
|
-
return { data: { name
|
|
88057
|
+
setUserPlugin(context.env, name, { source: source2, enabled: true, config: config3, env: [] });
|
|
88058
|
+
context.output.step(`\u2713 your user config names ${name}: source ${source2}`);
|
|
88059
|
+
return { data: { name, source: source2, installed, enabled: true, manifest: manifest2 } };
|
|
87719
88060
|
}
|
|
87720
88061
|
const definition = sourceOrRefuse(context, input2);
|
|
87721
88062
|
const root = definition.root;
|
|
87722
88063
|
const source = sourceOf(ref, given, root);
|
|
87723
|
-
const
|
|
87724
|
-
context
|
|
88064
|
+
const config2 = configOf(input2, sourceConfig(definition).plugins[name]?.config ?? {});
|
|
88065
|
+
const manifest = await checkedManifest(context, workspaceOf(input2), name, source, root, config2);
|
|
88066
|
+
context.output.step(`${name} ${manifest.version} reaches:`);
|
|
87725
88067
|
for (const line of permissionLines(manifest)) context.output.step(line);
|
|
87726
|
-
setPluginEntry(definition.configFile,
|
|
87727
|
-
context.output.step(`\u2713 ${basename9(definition.configFile)} names ${
|
|
87728
|
-
return { data: { name
|
|
88068
|
+
setPluginEntry(definition.configFile, name, { source, enabled: true, config: config2, env: [] });
|
|
88069
|
+
context.output.step(`\u2713 ${basename9(definition.configFile)} names ${name}: source ${source}`);
|
|
88070
|
+
return { data: { name, source, installed, enabled: true, manifest } };
|
|
87729
88071
|
}
|
|
87730
88072
|
});
|
|
87731
88073
|
var list3 = () => ({
|
|
@@ -87801,57 +88143,6 @@ var remove = () => ({
|
|
|
87801
88143
|
return { data: { name, unnamed, uninstalled } };
|
|
87802
88144
|
}
|
|
87803
88145
|
});
|
|
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
88146
|
var check2 = () => ({
|
|
87856
88147
|
path: ["plugin", "check"],
|
|
87857
88148
|
summary: "Run the conformance checks of each port a plugin provides, so any orbital host can use it.",
|
|
@@ -87877,8 +88168,8 @@ var check2 = () => ({
|
|
|
87877
88168
|
],
|
|
87878
88169
|
run: async (input2, context) => {
|
|
87879
88170
|
const name = String(input2.positionals.plugin);
|
|
87880
|
-
const fixtures = typeof input2.flags.fixtures === "string" ? JSON.parse(
|
|
87881
|
-
const plugin = await
|
|
88171
|
+
const fixtures = typeof input2.flags.fixtures === "string" ? JSON.parse(readFileSync29(input2.flags.fixtures, "utf8")) : {};
|
|
88172
|
+
const plugin = await startWorkspacePlugin(context, workspaceOf(input2), name);
|
|
87882
88173
|
try {
|
|
87883
88174
|
const results = await runConformance(plugin.ports, fixtures);
|
|
87884
88175
|
const mark = { pass: "\u2713", fail: "\u2717", skip: "\u25CB" };
|
|
@@ -88006,16 +88297,16 @@ var secretRemove = {
|
|
|
88006
88297
|
return { data: { plugin, name } };
|
|
88007
88298
|
}
|
|
88008
88299
|
};
|
|
88009
|
-
var pluginCommands = () => [add(), list3(), remove(),
|
|
88300
|
+
var pluginCommands = () => [add(), list3(), remove(), check2(), marketplaceAdd, marketplaceList, marketplaceUpdate, marketplaceRemove, secretSet, secretRemove];
|
|
88010
88301
|
|
|
88011
88302
|
// src/core/cli/plugin-new.ts
|
|
88012
88303
|
init_structure();
|
|
88013
88304
|
init_protocol();
|
|
88014
88305
|
init_command();
|
|
88015
|
-
import { existsSync as
|
|
88016
|
-
import { dirname as dirname16, join as
|
|
88306
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync22, readdirSync as readdirSync20, writeFileSync as writeFileSync20 } from "node:fs";
|
|
88307
|
+
import { dirname as dirname16, join as join38, relative as relative7, resolve as resolve16 } from "node:path";
|
|
88017
88308
|
var PLUGIN_PORTS = ["channel", "store", "workspace", "harness", "panel", "policy", "telemetry", "actions", "workflows", "command"];
|
|
88018
|
-
var SDK_VERSION = true ? `^${"0.0.
|
|
88309
|
+
var SDK_VERSION = true ? `^${"0.0.4"}` : "*";
|
|
88019
88310
|
var ESBUILD_VERSION = "^0.25.0";
|
|
88020
88311
|
var TYPES_NODE_VERSION = "^20.19.43";
|
|
88021
88312
|
var HARNESS_ID = /^[a-z][a-z0-9-]{0,31}$/;
|
|
@@ -88394,7 +88685,7 @@ export function plugin(context: PluginContext): PluginImplementation {
|
|
|
88394
88685
|
switch (event.kind) {
|
|
88395
88686
|
case 'mission.state': {
|
|
88396
88687
|
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,
|
|
88688
|
+
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
88689
|
graph.missions = [...graph.missions.filter((mission) => mission.missionId !== event.mission), state];
|
|
88399
88690
|
return;
|
|
88400
88691
|
}
|
|
@@ -88535,7 +88826,7 @@ export function plugin(_context: PluginContext): PluginImplementation {
|
|
|
88535
88826
|
case "command":
|
|
88536
88827
|
return {
|
|
88537
88828
|
summary: `An orbital plugin with a command: ${name}.`,
|
|
88538
|
-
plugin: `${header("one command. A person runs it with `orbital
|
|
88829
|
+
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
88830
|
|
|
88540
88831
|
export const NAME = '${name}';
|
|
88541
88832
|
|
|
@@ -88545,17 +88836,19 @@ export function plugin(context: PluginContext): PluginImplementation {
|
|
|
88545
88836
|
version: '0.1.0',
|
|
88546
88837
|
permissions: { network: [], secrets: [], models: [] },
|
|
88547
88838
|
ports: {},
|
|
88548
|
-
commands:
|
|
88549
|
-
|
|
88550
|
-
|
|
88551
|
-
|
|
88552
|
-
|
|
88553
|
-
|
|
88839
|
+
commands: [
|
|
88840
|
+
defineCommand({
|
|
88841
|
+
name: 'hello',
|
|
88842
|
+
summary: 'Greet the name given, or the workspace.',
|
|
88843
|
+
args: [{ name: 'name', summary: 'The name to greet.' }],
|
|
88844
|
+
run: async ({ args }) => \`Hello, \${args.name ?? context.init.node.workspaceDir}.\`,
|
|
88845
|
+
}),
|
|
88846
|
+
],
|
|
88554
88847
|
};
|
|
88555
88848
|
}
|
|
88556
88849
|
`,
|
|
88557
88850
|
fixtures: {},
|
|
88558
|
-
test: ` assert.equal(await implementation.commands?.
|
|
88851
|
+
test: ` assert.equal(await implementation.commands?.[0]?.run({ args: { name: 'Ada' }, options: {}, terminal: null }), 'Hello, Ada.');`
|
|
88559
88852
|
};
|
|
88560
88853
|
}
|
|
88561
88854
|
}
|
|
@@ -88589,10 +88882,11 @@ ${template.test ? `${template.test}
|
|
|
88589
88882
|
` : ""}});
|
|
88590
88883
|
`;
|
|
88591
88884
|
}
|
|
88885
|
+
var COMMAND_DECLARATION = { name: "hello", summary: "Greet the name given, or the workspace.", args: [{ name: "name", summary: "The name to greet." }] };
|
|
88592
88886
|
function pluginFiles(name, port) {
|
|
88593
88887
|
const template = templateOf(name, port);
|
|
88594
88888
|
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)}
|
|
88889
|
+
"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
88890
|
`,
|
|
88597
88891
|
"package.json": `${JSON.stringify({
|
|
88598
88892
|
name,
|
|
@@ -88650,14 +88944,14 @@ var pluginNewCommand = {
|
|
|
88650
88944
|
if (port === "harness" && !HARNESS_ID.test(name)) {
|
|
88651
88945
|
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
88946
|
}
|
|
88653
|
-
const dir =
|
|
88654
|
-
if (
|
|
88947
|
+
const dir = resolve16(typeof input2.flags.dir === "string" ? input2.flags.dir : name);
|
|
88948
|
+
if (existsSync31(dir) && readdirSync20(dir).length > 0) {
|
|
88655
88949
|
throw new CliError({ code: "conflict", message: `${dir} is not empty.`, exitCode: ExitCode.Validation, hint: "Name a new folder with --dir." });
|
|
88656
88950
|
}
|
|
88657
88951
|
const files = pluginFiles(name, port);
|
|
88658
88952
|
for (const [path3, content] of Object.entries(files)) {
|
|
88659
|
-
mkdirSync22(dirname16(
|
|
88660
|
-
writeFileSync20(
|
|
88953
|
+
mkdirSync22(dirname16(join38(dir, path3)), { recursive: true });
|
|
88954
|
+
writeFileSync20(join38(dir, path3), content);
|
|
88661
88955
|
}
|
|
88662
88956
|
const program = commandPrefix(context.env);
|
|
88663
88957
|
const shown = relative7(process.cwd(), dir) || ".";
|
|
@@ -88802,8 +89096,8 @@ If the browser does not return here in ${CALLBACK_WAIT_MS / 6e4} minutes, paste
|
|
|
88802
89096
|
${url2}
|
|
88803
89097
|
Then paste the code, or the full URL the browser ends on.`);
|
|
88804
89098
|
},
|
|
88805
|
-
onManualCodeInput: () => new Promise((
|
|
88806
|
-
timer = setTimeout(() =>
|
|
89099
|
+
onManualCodeInput: () => new Promise((resolve27) => {
|
|
89100
|
+
timer = setTimeout(() => resolve27(""), opened2 ? CALLBACK_WAIT_MS : 0);
|
|
88807
89101
|
timer.unref();
|
|
88808
89102
|
}),
|
|
88809
89103
|
onPrompt: async (prompt) => (await context.ask(`${prompt.message} `))?.trim() ?? "",
|
|
@@ -88897,14 +89191,14 @@ function createProviderCommands(signIn = loginAnthropic) {
|
|
|
88897
89191
|
|
|
88898
89192
|
// src/core/cli/run.ts
|
|
88899
89193
|
init_command();
|
|
88900
|
-
import { resolve as
|
|
89194
|
+
import { resolve as resolve18 } from "node:path";
|
|
88901
89195
|
|
|
88902
89196
|
// src/core/local/local-run.ts
|
|
88903
89197
|
init_protocol();
|
|
88904
89198
|
init_command();
|
|
88905
89199
|
import { randomBytes as randomBytes4, randomUUID as randomUUID8 } from "node:crypto";
|
|
88906
89200
|
import { mkdirSync as mkdirSync23, rmSync as rmSync12 } from "node:fs";
|
|
88907
|
-
import { join as
|
|
89201
|
+
import { join as join39, resolve as resolve17 } from "node:path";
|
|
88908
89202
|
|
|
88909
89203
|
// src/core/local/interrupt.ts
|
|
88910
89204
|
var EXIT_CODE = { SIGINT: 130, SIGTERM: 143 };
|
|
@@ -88927,7 +89221,7 @@ function cleanupOnInterrupt(cleanup, options = {}) {
|
|
|
88927
89221
|
async function runLocal(context, request, plugins) {
|
|
88928
89222
|
const { workspaceDir } = request;
|
|
88929
89223
|
const home = orbitalHome(context.env);
|
|
88930
|
-
const sessionsDir =
|
|
89224
|
+
const sessionsDir = join39(home, "sessions");
|
|
88931
89225
|
mkdirSync23(sessionsDir, { recursive: true, mode: 448 });
|
|
88932
89226
|
const resumed = request.resumeSessionId ? readSessionRecord(sessionsDir, request.resumeSessionId) : null;
|
|
88933
89227
|
if (request.resumeSessionId && !resumed) {
|
|
@@ -88957,9 +89251,9 @@ async function runLocal(context, request, plugins) {
|
|
|
88957
89251
|
let channels = null;
|
|
88958
89252
|
const asks = new PersonDesk(async (question, name) => await channels?.askPerson(`${name} asks: ${question}`, null) ?? null);
|
|
88959
89253
|
const policy = new PolicyChain(ports.policies(), asks.ask);
|
|
88960
|
-
channels = new ChannelHub({ context, ports, policy, self: nodeName, stateDir:
|
|
89254
|
+
channels = new ChannelHub({ context, ports, policy, self: nodeName, stateDir: join39(home, "channels", nodeName), plugins: () => definition.config.plugins });
|
|
88961
89255
|
const models = modelGatewayOf(context, policy, remote);
|
|
88962
|
-
const actions = nodeActions({ ports: () => ports, policy: () => policy, definition: { workspaceDir:
|
|
89256
|
+
const actions = nodeActions({ ports: () => ports, policy: () => policy, definition: { workspaceDir: resolve17(workspaceDir), definition }, span: (span2) => host.telemetry.span(span2), now: context.now });
|
|
88963
89257
|
const agentName = request.agent ?? resumed?.agent ?? null;
|
|
88964
89258
|
const target = await resolveTarget(definition, agentName, ports);
|
|
88965
89259
|
const harness = request.harness ?? resumed?.harness ?? defaultHarness(context.env);
|
|
@@ -89020,7 +89314,7 @@ async function runLocal(context, request, plugins) {
|
|
|
89020
89314
|
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
89315
|
if (result2 !== null) context.output.line(result2);
|
|
89022
89316
|
const cli = commandPrefix(context.env);
|
|
89023
|
-
context.output.step(`\u2713 transcript: ${
|
|
89317
|
+
context.output.step(`\u2713 transcript: ${join39(sessionsDir, sessionId, "conversation.jsonl")}`);
|
|
89024
89318
|
context.output.step(`\u2713 resume with: ${cli} run --resume ${sessionId}`);
|
|
89025
89319
|
return {
|
|
89026
89320
|
sessionId,
|
|
@@ -89028,7 +89322,7 @@ async function runLocal(context, request, plugins) {
|
|
|
89028
89322
|
done: true,
|
|
89029
89323
|
harness,
|
|
89030
89324
|
result: result2,
|
|
89031
|
-
transcript:
|
|
89325
|
+
transcript: join39(sessionsDir, sessionId, "conversation.jsonl"),
|
|
89032
89326
|
interactive: request.interactive
|
|
89033
89327
|
};
|
|
89034
89328
|
} finally {
|
|
@@ -89088,7 +89382,7 @@ async function interactiveTurn(context, request, run3) {
|
|
|
89088
89382
|
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
89383
|
const opened2 = await openSession(run3.ports, run3.policy, info);
|
|
89090
89384
|
const record2 = run3.host.attach(info, opened2.sinks, { agent: info.agent, harness: run3.harness, workspaceDir: request.workspaceDir });
|
|
89091
|
-
const runDir =
|
|
89385
|
+
const runDir = join39(run3.home, "runs", randomUUID8());
|
|
89092
89386
|
mkdirSync23(runDir, { recursive: true, mode: 448 });
|
|
89093
89387
|
let usage = [];
|
|
89094
89388
|
try {
|
|
@@ -89195,7 +89489,7 @@ function createRunCommand(options) {
|
|
|
89195
89489
|
const terminal2 = log ? await options.terminal() : null;
|
|
89196
89490
|
const data = await runLocal(log ? { ...context, output: log.output(context.output) } : context, {
|
|
89197
89491
|
agent,
|
|
89198
|
-
workspaceDir:
|
|
89492
|
+
workspaceDir: resolve18(typeof input2.flags.workspace === "string" ? input2.flags.workspace : "."),
|
|
89199
89493
|
prompt,
|
|
89200
89494
|
useDefinition: true,
|
|
89201
89495
|
follow: false,
|
|
@@ -89213,24 +89507,24 @@ function createRunCommand(options) {
|
|
|
89213
89507
|
// src/core/cli/session.ts
|
|
89214
89508
|
init_command();
|
|
89215
89509
|
init_protocol();
|
|
89216
|
-
import { resolve as
|
|
89510
|
+
import { resolve as resolve19 } from "node:path";
|
|
89217
89511
|
|
|
89218
89512
|
// src/core/local/attachments.ts
|
|
89219
|
-
import { existsSync as
|
|
89220
|
-
import { join as
|
|
89513
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync30, writeFileSync as writeFileSync21 } from "node:fs";
|
|
89514
|
+
import { join as join40 } from "node:path";
|
|
89221
89515
|
function fileOf(home, externalId) {
|
|
89222
|
-
return
|
|
89516
|
+
return join40(home, "attachments", `${encodeURIComponent(externalId)}.json`);
|
|
89223
89517
|
}
|
|
89224
89518
|
function writeAttachment(home, externalId, node2) {
|
|
89225
|
-
mkdirSync24(
|
|
89519
|
+
mkdirSync24(join40(home, "attachments"), { recursive: true, mode: 448 });
|
|
89226
89520
|
writeFileSync21(fileOf(home, externalId), `${JSON.stringify({ node: node2 })}
|
|
89227
89521
|
`, { mode: 384 });
|
|
89228
89522
|
}
|
|
89229
89523
|
function readAttachment(home, externalId) {
|
|
89230
89524
|
const file2 = fileOf(home, externalId);
|
|
89231
|
-
if (!
|
|
89525
|
+
if (!existsSync33(file2)) return null;
|
|
89232
89526
|
try {
|
|
89233
|
-
const parsed = JSON.parse(
|
|
89527
|
+
const parsed = JSON.parse(readFileSync30(file2, "utf8"));
|
|
89234
89528
|
return typeof parsed === "object" && parsed !== null && "node" in parsed && typeof parsed.node === "string" ? parsed.node : null;
|
|
89235
89529
|
} catch {
|
|
89236
89530
|
return null;
|
|
@@ -89298,7 +89592,7 @@ async function joinMission(context, attached, missionId) {
|
|
|
89298
89592
|
const body2 = await response.json();
|
|
89299
89593
|
if (!response.ok) {
|
|
89300
89594
|
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
|
|
89595
|
+
throw new CliError({ code: "invalid-input", message: `The node refused to make this session a decision receiver of ${missionId}: ${error62}`, exitCode: ExitCode.Validation });
|
|
89302
89596
|
}
|
|
89303
89597
|
return MissionRecordSchema.parse(body2);
|
|
89304
89598
|
}
|
|
@@ -89378,7 +89672,7 @@ var sessionAttachCommand = {
|
|
|
89378
89672
|
{ 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
89673
|
{ name: "team", type: "string", summary: "The team the session joins. Sessions of one team are peers." },
|
|
89380
89674
|
{ 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
|
|
89675
|
+
{ 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
89676
|
],
|
|
89383
89677
|
positionals: [],
|
|
89384
89678
|
outputShape: {
|
|
@@ -89395,7 +89689,7 @@ var sessionAttachCommand = {
|
|
|
89395
89689
|
examples: [
|
|
89396
89690
|
{ command: "orbital session attach --harness claude-code --output ndjson", why: "Join the node of this folder, and print each delivery as a record." },
|
|
89397
89691
|
{ 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
|
|
89692
|
+
{ 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
89693
|
],
|
|
89400
89694
|
run: async (input2, context) => {
|
|
89401
89695
|
const harness = typeof input2.flags.harness === "string" ? input2.flags.harness : "external";
|
|
@@ -89407,7 +89701,7 @@ var sessionAttachCommand = {
|
|
|
89407
89701
|
const found = await keepAttached(context, {
|
|
89408
89702
|
harness,
|
|
89409
89703
|
agent: typeof input2.flags.agent === "string" ? input2.flags.agent : harness,
|
|
89410
|
-
workspaceDir:
|
|
89704
|
+
workspaceDir: resolve19(typeof input2.flags.workspace === "string" ? input2.flags.workspace : "."),
|
|
89411
89705
|
...typeof input2.flags.node === "string" && { node: input2.flags.node },
|
|
89412
89706
|
team: typeof input2.flags.team === "string" ? input2.flags.team : null,
|
|
89413
89707
|
externalId: typeof input2.flags["external-id"] === "string" ? input2.flags["external-id"] : null,
|
|
@@ -89420,6 +89714,8 @@ var sessionAttachCommand = {
|
|
|
89420
89714
|
delivery: (event) => {
|
|
89421
89715
|
const delivery = DeliverySchema.safeParse(event);
|
|
89422
89716
|
if (delivery.success) context.output.record({ type: StreamRecordType.Event, ...delivery.data });
|
|
89717
|
+
const answered = AnsweredSchema.safeParse(event);
|
|
89718
|
+
if (answered.success) context.output.record({ type: StreamRecordType.Event, ...answered.data });
|
|
89423
89719
|
},
|
|
89424
89720
|
detached: (reason) => {
|
|
89425
89721
|
context.output.record({ type: StreamRecordType.Event, kind: "detached", reason });
|
|
@@ -89509,8 +89805,8 @@ var workspaceCommands = [list5, show, remove3];
|
|
|
89509
89805
|
|
|
89510
89806
|
// src/core/cli/up.ts
|
|
89511
89807
|
init_command();
|
|
89512
|
-
import { existsSync as
|
|
89513
|
-
import { resolve as
|
|
89808
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
89809
|
+
import { resolve as resolve20 } from "node:path";
|
|
89514
89810
|
init_protocol();
|
|
89515
89811
|
|
|
89516
89812
|
// src/core/cli/up-view.ts
|
|
@@ -89692,7 +89988,7 @@ async function liveView(context, nodes, signal, terminal2, home) {
|
|
|
89692
89988
|
for (const line of events(viewed, states.get(one.node), now)) context.output.line(line);
|
|
89693
89989
|
states.set(one.node, now);
|
|
89694
89990
|
}
|
|
89695
|
-
await Promise.race([context.sleep(REFRESH_MS), new Promise((
|
|
89991
|
+
await Promise.race([context.sleep(REFRESH_MS), new Promise((resolve27) => signal.addEventListener("abort", () => resolve27(), { once: true }))]);
|
|
89696
89992
|
}
|
|
89697
89993
|
context.output.line(closing);
|
|
89698
89994
|
}
|
|
@@ -89701,7 +89997,7 @@ async function liveView(context, nodes, signal, terminal2, home) {
|
|
|
89701
89997
|
var WORKSPACE_FLAG = { name: "workspace", type: "path", summary: "The project. Default: the current directory." };
|
|
89702
89998
|
var TEAMS = { name: "teams", type: "string", variadic: true, summary: "Only the nodes of these teams. Default: every node of the project." };
|
|
89703
89999
|
async function planOf(context, input2) {
|
|
89704
|
-
const workspaceDir =
|
|
90000
|
+
const workspaceDir = resolve20(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
89705
90001
|
const { definition, refusals } = await loadDefinitionPartial(workspaceDir, context.env);
|
|
89706
90002
|
for (const refusal of refusals) context.output.warn(refusal.message);
|
|
89707
90003
|
const project = projectNodeOf(definition, workspaceDir);
|
|
@@ -89848,7 +90144,7 @@ var statusCommand = {
|
|
|
89848
90144
|
for (const { charters: _charters, named: _named, ...planned } of nodes) {
|
|
89849
90145
|
const log = nodeLogPath(home, planned.node);
|
|
89850
90146
|
const supervisor = readSupervisor(home, planned.node);
|
|
89851
|
-
if (
|
|
90147
|
+
if (existsSync34(log)) logs.push({ node: planned.node, log, lastCrash: supervisor?.crashes.at(-1) ?? null, lastExit: supervisor?.lastExit ?? null, gaveUp: supervisor?.gaveUp ?? null });
|
|
89852
90148
|
const running = await liveNode(home, planned.node, context.fetch);
|
|
89853
90149
|
const served = running ? await servedTeams(context, running) : null;
|
|
89854
90150
|
for (const note of served ?? []) if (note.problems.length > 0 || note.retiring) notes.push({ node: planned.node, ...note });
|
|
@@ -89881,7 +90177,7 @@ var downCommand = {
|
|
|
89881
90177
|
const { workspaceDir, nodes } = await planOf(context, input2);
|
|
89882
90178
|
const home = orbitalHome(context.env);
|
|
89883
90179
|
const rows = [];
|
|
89884
|
-
const separate2 = (await liveNodes(home, context.fetch)).filter((info) =>
|
|
90180
|
+
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
90181
|
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
90182
|
for (const planned of targets) {
|
|
89887
90183
|
const running = await liveNode(home, planned.node, context.fetch);
|
|
@@ -89940,7 +90236,7 @@ var createJoinCommand = (options) => ({
|
|
|
89940
90236
|
}
|
|
89941
90237
|
let charters = [];
|
|
89942
90238
|
try {
|
|
89943
|
-
const workspaceDir =
|
|
90239
|
+
const workspaceDir = resolve20(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
89944
90240
|
charters = (await loadDefinition(workspaceDir, context.env)).teams;
|
|
89945
90241
|
} catch {
|
|
89946
90242
|
charters = [];
|
|
@@ -89966,9 +90262,9 @@ function createUpCommands(options) {
|
|
|
89966
90262
|
init_command();
|
|
89967
90263
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
89968
90264
|
import { createHash as createHash10 } from "node:crypto";
|
|
89969
|
-
import { existsSync as
|
|
90265
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync25, rmSync as rmSync13, writeFileSync as writeFileSync22 } from "node:fs";
|
|
89970
90266
|
import { homedir as homedir8 } from "node:os";
|
|
89971
|
-
import { join as
|
|
90267
|
+
import { join as join41, resolve as resolve21 } from "node:path";
|
|
89972
90268
|
var exec = (command, args) => {
|
|
89973
90269
|
const result2 = spawnSync5(command, args, { encoding: "utf8" });
|
|
89974
90270
|
return { status: result2.status, output: `${result2.stdout ?? ""}${result2.stderr ?? ""}${result2.error?.message ?? ""}` };
|
|
@@ -89986,7 +90282,7 @@ function serviceSpec(env3, workspaceDir) {
|
|
|
89986
90282
|
workspaceDir,
|
|
89987
90283
|
program: [process.execPath, installedProgram(), ...mountPath(), "up", "--detach", "--workspace", workspaceDir],
|
|
89988
90284
|
env: Object.fromEntries(KEPT_ENV.flatMap((name) => env3[name] ? [[name, env3[name]]] : [])),
|
|
89989
|
-
log:
|
|
90285
|
+
log: join41(orbitalHome(env3), "logs", `service-${hash2}.log`)
|
|
89990
90286
|
};
|
|
89991
90287
|
}
|
|
89992
90288
|
function xml(text3) {
|
|
@@ -90049,7 +90345,7 @@ function userHome(env3) {
|
|
|
90049
90345
|
return env3.HOME ?? homedir8();
|
|
90050
90346
|
}
|
|
90051
90347
|
function fileOf2(platform2, env3, label) {
|
|
90052
|
-
return platform2 === "launchd" ?
|
|
90348
|
+
return platform2 === "launchd" ? join41(userHome(env3), "Library", "LaunchAgents", `${label}.plist`) : join41(env3.XDG_CONFIG_HOME ?? join41(userHome(env3), ".config"), "systemd", "user", `${label}.service`);
|
|
90053
90349
|
}
|
|
90054
90350
|
function domain2() {
|
|
90055
90351
|
return `gui/${process.getuid?.() ?? 0}`;
|
|
@@ -90062,13 +90358,13 @@ function serviceState(platform2, env3, workspaceDir, run3 = exec) {
|
|
|
90062
90358
|
const { label } = serviceSpec(env3, workspaceDir);
|
|
90063
90359
|
const file2 = fileOf2(platform2, env3, label);
|
|
90064
90360
|
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:
|
|
90361
|
+
return { platform: platform2, label, file: file2, installed: existsSync35(file2), loaded };
|
|
90066
90362
|
}
|
|
90067
90363
|
function installService(platform2, env3, workspaceDir, run3 = exec) {
|
|
90068
90364
|
const spec = serviceSpec(env3, workspaceDir);
|
|
90069
90365
|
const file2 = fileOf2(platform2, env3, spec.label);
|
|
90070
|
-
mkdirSync25(
|
|
90071
|
-
mkdirSync25(
|
|
90366
|
+
mkdirSync25(join41(file2, ".."), { recursive: true });
|
|
90367
|
+
mkdirSync25(join41(spec.log, ".."), { recursive: true, mode: 448 });
|
|
90072
90368
|
if (platform2 === "launchd") {
|
|
90073
90369
|
writeFileSync22(file2, launchdPlist(spec));
|
|
90074
90370
|
run3("launchctl", ["bootout", `${domain2()}/${spec.label}`]);
|
|
@@ -90090,7 +90386,7 @@ function uninstallService(platform2, env3, workspaceDir, run3 = exec) {
|
|
|
90090
90386
|
return serviceState(platform2, env3, workspaceDir, run3);
|
|
90091
90387
|
}
|
|
90092
90388
|
function workspaceOf2(input2) {
|
|
90093
|
-
return
|
|
90389
|
+
return resolve21(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
90094
90390
|
}
|
|
90095
90391
|
function platformOrRefuse() {
|
|
90096
90392
|
const platform2 = platformOf();
|
|
@@ -90165,9 +90461,9 @@ var serviceCommands = [
|
|
|
90165
90461
|
];
|
|
90166
90462
|
|
|
90167
90463
|
// src/core/cli/browser.ts
|
|
90168
|
-
import { existsSync as
|
|
90464
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync31, writeFileSync as writeFileSync23 } from "node:fs";
|
|
90169
90465
|
import { homedir as homedir9 } from "node:os";
|
|
90170
|
-
import { dirname as dirname17, join as
|
|
90466
|
+
import { dirname as dirname17, join as join42 } from "node:path";
|
|
90171
90467
|
|
|
90172
90468
|
// ../../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
90469
|
function normalizeHeaders(headers) {
|
|
@@ -91782,7 +92078,7 @@ function isChromeDevtools(entry) {
|
|
|
91782
92078
|
return isChromeDevtoolsMcp(command, args);
|
|
91783
92079
|
}
|
|
91784
92080
|
function claudeJsonPath(env3) {
|
|
91785
|
-
return
|
|
92081
|
+
return join42(env3.CLAUDE_CONFIG_DIR ?? env3.HOME ?? homedir9(), ".claude.json");
|
|
91786
92082
|
}
|
|
91787
92083
|
function chromeServersOf(config2) {
|
|
91788
92084
|
return Object.entries(config2.mcpServers ?? {}).filter(([, entry]) => isChromeDevtools(entry)).map(([name]) => name);
|
|
@@ -91794,21 +92090,21 @@ function sharedBrowserEntry() {
|
|
|
91794
92090
|
return { type: "stdio", ...sharedBrowserCommand(), env: {} };
|
|
91795
92091
|
}
|
|
91796
92092
|
function backupPath(env3) {
|
|
91797
|
-
return
|
|
92093
|
+
return join42(browserDir(orbitalHome(env3)), "claude-mcp-backup.json");
|
|
91798
92094
|
}
|
|
91799
92095
|
function codexBackupPath(env3) {
|
|
91800
|
-
return
|
|
92096
|
+
return join42(browserDir(orbitalHome(env3)), "codex-config-backup.json");
|
|
91801
92097
|
}
|
|
91802
92098
|
function codexConfigPath(env3) {
|
|
91803
|
-
return
|
|
92099
|
+
return join42(env3.CODEX_HOME ?? join42(env3.HOME ?? homedir9(), ".codex"), "config.toml");
|
|
91804
92100
|
}
|
|
91805
92101
|
function pointClaudeAtSharedBrowser(env3, entry = sharedBrowserEntry()) {
|
|
91806
92102
|
const file2 = claudeJsonPath(env3);
|
|
91807
|
-
const config2 =
|
|
92103
|
+
const config2 = existsSync36(file2) ? JSON.parse(readFileSync31(file2, "utf8")) : {};
|
|
91808
92104
|
const found = chromeServersOf(config2);
|
|
91809
92105
|
const names = found.length > 0 ? found : [SHARED_BROWSER_SERVER];
|
|
91810
92106
|
const backup = backupPath(env3);
|
|
91811
|
-
const previous =
|
|
92107
|
+
const previous = existsSync36(backup) ? JSON.parse(readFileSync31(backup, "utf8")) : {};
|
|
91812
92108
|
const saved = { ...Object.fromEntries(names.filter((name) => config2.mcpServers?.[name] !== void 0).map((name) => [name, config2.mcpServers[name]])), ...previous };
|
|
91813
92109
|
mkdirSync26(browserDir(orbitalHome(env3)), { recursive: true, mode: 448 });
|
|
91814
92110
|
writeFileSync23(backup, `${JSON.stringify(saved, null, 2)}
|
|
@@ -91821,9 +92117,9 @@ function pointClaudeAtSharedBrowser(env3, entry = sharedBrowserEntry()) {
|
|
|
91821
92117
|
function revertClaudeBrowser(env3) {
|
|
91822
92118
|
const file2 = claudeJsonPath(env3);
|
|
91823
92119
|
const backup = backupPath(env3);
|
|
91824
|
-
if (!
|
|
91825
|
-
const saved = JSON.parse(
|
|
91826
|
-
const config2 =
|
|
92120
|
+
if (!existsSync36(backup)) return null;
|
|
92121
|
+
const saved = JSON.parse(readFileSync31(backup, "utf8"));
|
|
92122
|
+
const config2 = existsSync36(file2) ? JSON.parse(readFileSync31(file2, "utf8")) : {};
|
|
91827
92123
|
const shared = Object.entries(config2.mcpServers ?? {}).filter(([, entry]) => Array.isArray(entry.args) && entry.args.join(" ").endsWith("browser connect")).map(([name]) => name);
|
|
91828
92124
|
const servers = { ...config2.mcpServers };
|
|
91829
92125
|
for (const name of shared) delete servers[name];
|
|
@@ -91834,18 +92130,18 @@ function revertClaudeBrowser(env3) {
|
|
|
91834
92130
|
}
|
|
91835
92131
|
function pointCodexAtSharedBrowser(env3, shared = sharedBrowserCommand()) {
|
|
91836
92132
|
const file2 = codexConfigPath(env3);
|
|
91837
|
-
if (!
|
|
91838
|
-
const text3 =
|
|
92133
|
+
if (!existsSync36(file2) && !existsSync36(dirname17(file2))) return null;
|
|
92134
|
+
const text3 = existsSync36(file2) ? readFileSync31(file2, "utf8") : "";
|
|
91839
92135
|
const tables = mcpServerTables(text3);
|
|
91840
92136
|
const found = tables.filter((table2) => isChromeDevtoolsMcp(table2.command, table2.args)).map((table2) => table2.name);
|
|
91841
92137
|
const connected = tables.filter((table2) => isSharedBrowserConnect(table2.args)).map((table2) => table2.name);
|
|
91842
92138
|
const names = found.length > 0 ? found : connected.length > 0 ? [] : [SHARED_BROWSER_SERVER];
|
|
91843
92139
|
const backup = codexBackupPath(env3);
|
|
91844
|
-
if (names.length === 0) return { file: file2, servers: connected, backup:
|
|
92140
|
+
if (names.length === 0) return { file: file2, servers: connected, backup: existsSync36(backup) ? backup : null };
|
|
91845
92141
|
const block = (name) => stdioServerBlock(name, shared.command, shared.args);
|
|
91846
92142
|
const replaced = replaceMcpServers(text3, (table2) => names.includes(table2.name) ? block(table2.name) : null);
|
|
91847
92143
|
const next = names.filter((name) => !replaced.replaced.some((one) => one.name === name)).reduce((current2, name) => appendMcpServer(current2, block(name)), replaced.text);
|
|
91848
|
-
const previous =
|
|
92144
|
+
const previous = existsSync36(backup) ? JSON.parse(readFileSync31(backup, "utf8")) : {};
|
|
91849
92145
|
const saved = { ...Object.fromEntries(replaced.replaced.map((one) => [one.name, one.original])), ...previous };
|
|
91850
92146
|
mkdirSync26(browserDir(orbitalHome(env3)), { recursive: true, mode: 448 });
|
|
91851
92147
|
writeFileSync23(backup, `${JSON.stringify(saved, null, 2)}
|
|
@@ -91857,9 +92153,9 @@ function pointCodexAtSharedBrowser(env3, shared = sharedBrowserCommand()) {
|
|
|
91857
92153
|
function revertCodexBrowser(env3) {
|
|
91858
92154
|
const file2 = codexConfigPath(env3);
|
|
91859
92155
|
const backup = codexBackupPath(env3);
|
|
91860
|
-
if (!
|
|
91861
|
-
const saved = JSON.parse(
|
|
91862
|
-
const text3 =
|
|
92156
|
+
if (!existsSync36(backup)) return null;
|
|
92157
|
+
const saved = JSON.parse(readFileSync31(backup, "utf8"));
|
|
92158
|
+
const text3 = existsSync36(file2) ? readFileSync31(file2, "utf8") : "";
|
|
91863
92159
|
const replaced = replaceMcpServers(text3, (table2) => isSharedBrowserConnect(table2.args) ? saved[table2.name] ?? "" : null);
|
|
91864
92160
|
const next = Object.entries(saved).filter(([name]) => !replaced.replaced.some((one) => one.name === name)).reduce((current2, [, original]) => appendMcpServer(current2, original), replaced.text);
|
|
91865
92161
|
mkdirSync26(dirname17(file2), { recursive: true });
|
|
@@ -92047,13 +92343,13 @@ var browserCommands = [
|
|
|
92047
92343
|
init_command();
|
|
92048
92344
|
init_structure();
|
|
92049
92345
|
init_protocol();
|
|
92050
|
-
import { existsSync as
|
|
92051
|
-
import { join as
|
|
92346
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
92347
|
+
import { join as join44, relative as relative8, resolve as resolve22 } from "node:path";
|
|
92052
92348
|
|
|
92053
92349
|
// src/core/local/mission-migrate.ts
|
|
92054
92350
|
init_protocol();
|
|
92055
|
-
import { existsSync as
|
|
92056
|
-
import { join as
|
|
92351
|
+
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";
|
|
92352
|
+
import { join as join43 } from "node:path";
|
|
92057
92353
|
function withTeam(content, team) {
|
|
92058
92354
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(\r?\n[\s\S]*)?$/);
|
|
92059
92355
|
if (!match) return content;
|
|
@@ -92065,26 +92361,26 @@ ${head.join("\n")}
|
|
|
92065
92361
|
---${match[2] ?? "\n"}`;
|
|
92066
92362
|
}
|
|
92067
92363
|
function emptyDir(dir) {
|
|
92068
|
-
if (
|
|
92364
|
+
if (existsSync37(dir) && readdirSync21(dir).length === 0) rmdirSync2(dir);
|
|
92069
92365
|
}
|
|
92070
92366
|
function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
92071
92367
|
const moved = [];
|
|
92072
92368
|
const refused3 = [];
|
|
92073
|
-
const teamsDir =
|
|
92074
|
-
const target =
|
|
92369
|
+
const teamsDir = join43(root, TEAMS_DIR);
|
|
92370
|
+
const target = join43(root, MISSIONS_DIR);
|
|
92075
92371
|
const seen = /* @__PURE__ */ new Map();
|
|
92076
|
-
for (const team of
|
|
92077
|
-
const missionsDir =
|
|
92078
|
-
if (!
|
|
92372
|
+
for (const team of existsSync37(teamsDir) ? readdirSync21(teamsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()) : []) {
|
|
92373
|
+
const missionsDir = join43(teamsDir, team.name, MISSIONS_DIR);
|
|
92374
|
+
if (!existsSync37(missionsDir)) continue;
|
|
92079
92375
|
for (const mission of readdirSync21(missionsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory())) {
|
|
92080
|
-
const from =
|
|
92081
|
-
const to =
|
|
92376
|
+
const from = join43(missionsDir, mission.name);
|
|
92377
|
+
const to = join43(target, mission.name);
|
|
92082
92378
|
const other = seen.get(mission.name);
|
|
92083
92379
|
if (other) {
|
|
92084
92380
|
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
92381
|
continue;
|
|
92086
92382
|
}
|
|
92087
|
-
if (
|
|
92383
|
+
if (existsSync37(to)) {
|
|
92088
92384
|
refused3.push(`${from}: ${to} exists already.`);
|
|
92089
92385
|
continue;
|
|
92090
92386
|
}
|
|
@@ -92093,22 +92389,22 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
92093
92389
|
if (dryRun) continue;
|
|
92094
92390
|
mkdirSync27(target, { recursive: true });
|
|
92095
92391
|
renameSync8(from, to);
|
|
92096
|
-
const file2 =
|
|
92097
|
-
if (
|
|
92392
|
+
const file2 = join43(to, MISSION_FILE);
|
|
92393
|
+
if (existsSync37(file2)) writeFileSync24(file2, withTeam(readFileSync32(file2, "utf8"), team.name));
|
|
92098
92394
|
}
|
|
92099
92395
|
if (!dryRun) emptyDir(missionsDir);
|
|
92100
92396
|
}
|
|
92101
92397
|
for (const node2 of nodes) {
|
|
92102
|
-
const nodeDir2 =
|
|
92103
|
-
if (!
|
|
92398
|
+
const nodeDir2 = join43(nodesDir, node2);
|
|
92399
|
+
if (!existsSync37(nodeDir2)) continue;
|
|
92104
92400
|
const files = [];
|
|
92105
92401
|
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:
|
|
92402
|
+
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 });
|
|
92403
|
+
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
92404
|
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:
|
|
92405
|
+
for (const name of readdirSync21(join43(nodeDir2, entry.name))) {
|
|
92406
|
+
if (name.endsWith(".json")) files.push({ from: join43(nodeDir2, entry.name, name), mission: name.slice(0, -".json".length), team: entry.name, backup: false });
|
|
92407
|
+
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
92408
|
}
|
|
92113
92409
|
}
|
|
92114
92410
|
files.sort((a, b) => Number(a.backup) - Number(b.backup) || Number(a.team !== null) - Number(b.team !== null));
|
|
@@ -92120,13 +92416,13 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
92120
92416
|
if (!dryRun) rmSync14(file2.from);
|
|
92121
92417
|
continue;
|
|
92122
92418
|
}
|
|
92123
|
-
if (taken.has(file2.mission) ||
|
|
92419
|
+
if (taken.has(file2.mission) || existsSync37(to)) {
|
|
92124
92420
|
refused3.push(`${file2.from}: ${to} exists already.`);
|
|
92125
92421
|
continue;
|
|
92126
92422
|
}
|
|
92127
|
-
const raw = JSON.parse(
|
|
92423
|
+
const raw = JSON.parse(readFileSync32(file2.from, "utf8"));
|
|
92128
92424
|
const { file: _file2, ...rest } = raw;
|
|
92129
|
-
const state = MissionStateFileSchema.safeParse({ question: null,
|
|
92425
|
+
const state = MissionStateFileSchema.safeParse({ question: null, lead: null, claim: null, claimedAt: null, ...rest, mission: file2.mission, node: node2, team: rest.team ?? file2.team });
|
|
92130
92426
|
if (!state.success) {
|
|
92131
92427
|
refused3.push(`${file2.from}: it is not the state of a mission: ${state.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`).join("; ")}`);
|
|
92132
92428
|
continue;
|
|
@@ -92138,7 +92434,7 @@ function migrateMissions(root, nodesDir, nodes, dryRun) {
|
|
|
92138
92434
|
rmSync14(file2.from);
|
|
92139
92435
|
}
|
|
92140
92436
|
if (dryRun) continue;
|
|
92141
|
-
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) if (entry.isDirectory()) emptyDir(
|
|
92437
|
+
for (const entry of readdirSync21(nodeDir2, { withFileTypes: true })) if (entry.isDirectory()) emptyDir(join43(nodeDir2, entry.name));
|
|
92142
92438
|
emptyDir(nodeDir2);
|
|
92143
92439
|
}
|
|
92144
92440
|
emptyDir(nodesDir);
|
|
@@ -92160,13 +92456,13 @@ var MISSION_FIELDS = {
|
|
|
92160
92456
|
{ name: "result", type: "string", nullable: true, summary: "The hand-off of the mission, when it ended." },
|
|
92161
92457
|
{ name: "summary", type: "string", nullable: true, summary: "The first sentence of the Intent of the brief." },
|
|
92162
92458
|
{ 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
|
|
92459
|
+
{ 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
92460
|
{ 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
92461
|
{ name: "history", type: "array", summary: "Each change of state, with its time and who made it." }
|
|
92166
92462
|
]
|
|
92167
92463
|
};
|
|
92168
92464
|
function workspaceOf3(input2) {
|
|
92169
|
-
return
|
|
92465
|
+
return resolve22(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
92170
92466
|
}
|
|
92171
92467
|
function refused2(message, hint) {
|
|
92172
92468
|
return new CliError({ code: "invalid-input", message, exitCode: ExitCode.Validation, ...hint && { hint } });
|
|
@@ -92248,7 +92544,7 @@ function missionNote(context, record2) {
|
|
|
92248
92544
|
}
|
|
92249
92545
|
}
|
|
92250
92546
|
function shownState(record2) {
|
|
92251
|
-
return
|
|
92547
|
+
return waitsOnDecision(record2) && record2.state !== "waiting" ? `${record2.state}, waits on you` : record2.state;
|
|
92252
92548
|
}
|
|
92253
92549
|
function missionLines2(context, records) {
|
|
92254
92550
|
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 +92596,7 @@ var missionNew = {
|
|
|
92300
92596
|
if (!id.success) throw refused2(`"${String(input2.positionals.id)}" is not a mission id: use lowercase letters, digits and hyphens.`);
|
|
92301
92597
|
const file2 = missionFile(teamRoot(team), id.data);
|
|
92302
92598
|
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 (
|
|
92599
|
+
if (existsSync38(file2) && input2.flags.force !== true) throw refused2(`${relative8(workspaceDir, file2)} exists.`, "Pass --force to write over it.");
|
|
92304
92600
|
const intent = await asked(context, input2, "intent", "Intent: why does the team do this mission? ");
|
|
92305
92601
|
const endState = await asked(context, input2, "end-state", "End state: what is true when the mission is done? ");
|
|
92306
92602
|
const limits = await asked(context, input2, "limits", "Limits: what must the team not do or change? ");
|
|
@@ -92379,7 +92675,7 @@ var missionShow = {
|
|
|
92379
92675
|
if (record2.sessionId) context.output.line(`session: ${record2.sessionId}`);
|
|
92380
92676
|
if (record2.brief) context.output.line(["", record2.brief].join("\n"));
|
|
92381
92677
|
if (record2.lead) context.output.line(`lead: ${record2.lead}`);
|
|
92382
|
-
if (record2.
|
|
92678
|
+
if (record2.receivers.length > 0) context.output.line(`decision receivers: ${record2.receivers.join(", ")} (each question goes to every live one)`);
|
|
92383
92679
|
if (record2.claim) context.output.line(`claim: ${record2.claim}${record2.claimedAt ? ` since ${record2.claimedAt}` : ""}${record2.stale ? " (stale)" : ""}`);
|
|
92384
92680
|
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
92681
|
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 +92750,31 @@ var missionSay = {
|
|
|
92454
92750
|
return { data: { ...said2, node: node2.name } };
|
|
92455
92751
|
}
|
|
92456
92752
|
};
|
|
92753
|
+
var CLAUDE_SESSION_ENV = "CLAUDE_CODE_SESSION_ID";
|
|
92754
|
+
function missionMembership(verb, summary, why) {
|
|
92755
|
+
return {
|
|
92756
|
+
path: ["mission", verb],
|
|
92757
|
+
summary,
|
|
92758
|
+
credential: "none",
|
|
92759
|
+
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.` }],
|
|
92760
|
+
positionals: [MISSION_ID2],
|
|
92761
|
+
outputShape: MISSION_FIELDS,
|
|
92762
|
+
exitCodes: [0, 3],
|
|
92763
|
+
routes: [],
|
|
92764
|
+
examples: [{ command: `orbital mission ${verb} pr-42`, why }],
|
|
92765
|
+
run: async (input2, context) => {
|
|
92766
|
+
const mission = idOf(input2);
|
|
92767
|
+
const externalId = typeof input2.flags["external-id"] === "string" ? input2.flags["external-id"] : context.env[CLAUDE_SESSION_ENV] || null;
|
|
92768
|
+
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}.`);
|
|
92769
|
+
const node2 = await nodeOfMission(context, mission, () => nodeOfProject(context, workspaceOf3(input2)));
|
|
92770
|
+
const record2 = MissionRecordSchema.parse(await nodeCall(context, node2, `/orbital/missions/${verb}`, { missionId: mission, externalId }));
|
|
92771
|
+
context.output.line(`${record2.missionId} on ${record2.node}: ${record2.state}; decision receivers: ${record2.receivers.join(", ") || "none"}`);
|
|
92772
|
+
return { data: record2 };
|
|
92773
|
+
}
|
|
92774
|
+
};
|
|
92775
|
+
}
|
|
92776
|
+
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.");
|
|
92777
|
+
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
92778
|
var missionMigrate = {
|
|
92458
92779
|
path: ["mission", "migrate"],
|
|
92459
92780
|
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 +92789,11 @@ var missionMigrate = {
|
|
|
92468
92789
|
const workspaceDir = workspaceOf3(input2);
|
|
92469
92790
|
const source = definitionSource(workspaceDir, context.env);
|
|
92470
92791
|
if (!source) throw refused2(`${workspaceDir} has no definition of orbital.`);
|
|
92471
|
-
const running = (await liveNodes(orbitalHome(context.env), context.fetch)).filter((node2) =>
|
|
92792
|
+
const running = (await liveNodes(orbitalHome(context.env), context.fetch)).filter((node2) => resolve22(node2.workspaceDir) === resolve22(source.projectDir));
|
|
92472
92793
|
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
92794
|
const project = sourceConfig(source).node ?? defaultNodeName(workspaceDir);
|
|
92474
92795
|
const nodes = teamNodes(readTeams(source.root).teams, project);
|
|
92475
|
-
const migration = migrateMissions(source.root,
|
|
92796
|
+
const migration = migrateMissions(source.root, join44(orbitalHome(context.env), "missions"), nodes, input2.flags["dry-run"] === true);
|
|
92476
92797
|
for (const move of migration.moved) context.output.line(`${input2.flags["dry-run"] === true ? "would move" : "moved"} ${move.from} -> ${move.to}`);
|
|
92477
92798
|
for (const reason of migration.refused) context.output.warn(reason);
|
|
92478
92799
|
if (migration.moved.length === 0 && migration.refused.length === 0) context.output.line("Nothing to move: the missions are in the new layout.");
|
|
@@ -92497,12 +92818,12 @@ var missionReply = {
|
|
|
92497
92818
|
return { data: record2 };
|
|
92498
92819
|
}
|
|
92499
92820
|
};
|
|
92500
|
-
var missionCommands = [missionNew, missionStart, missionList, missionShow, missionPause, missionResume, missionSay, missionReply, missionDone, missionMigrate].map((spec) => ({ ...spec, group: CommandGroup.OperatingModel }));
|
|
92821
|
+
var missionCommands = [missionNew, missionStart, missionList, missionShow, missionPause, missionResume, missionSay, missionReply, missionJoin, missionLeave, missionDone, missionMigrate].map((spec) => ({ ...spec, group: CommandGroup.OperatingModel }));
|
|
92501
92822
|
|
|
92502
92823
|
// src/core/cli/check.ts
|
|
92503
92824
|
init_command();
|
|
92504
92825
|
init_structure();
|
|
92505
|
-
import { resolve as
|
|
92826
|
+
import { resolve as resolve23 } from "node:path";
|
|
92506
92827
|
var LOAD_RULE = "orbital.load";
|
|
92507
92828
|
async function loadRefusals(workspaceDir, env3) {
|
|
92508
92829
|
try {
|
|
@@ -92540,7 +92861,7 @@ var checkCommand = {
|
|
|
92540
92861
|
{ command: "orbital check --annotations", why: "Show the findings on a pull request." }
|
|
92541
92862
|
],
|
|
92542
92863
|
run: async (input2, context) => {
|
|
92543
|
-
const workspaceDir =
|
|
92864
|
+
const workspaceDir = resolve23(typeof input2.flags.workspace === "string" ? input2.flags.workspace : ".");
|
|
92544
92865
|
const source = definitionSource(workspaceDir, context.env);
|
|
92545
92866
|
const findings = await loadRefusals(workspaceDir, context.env);
|
|
92546
92867
|
for (const finding of findings) context.output.line(`${finding.finding} ${finding.rule} ${finding.path} ${finding.message}`);
|
|
@@ -92809,19 +93130,19 @@ function createOrbitalCommands(options) {
|
|
|
92809
93130
|
}
|
|
92810
93131
|
|
|
92811
93132
|
// src/core/cli/shipped-skills.ts
|
|
92812
|
-
var
|
|
93133
|
+
var import_yaml9 = __toESM(require_dist(), 1);
|
|
92813
93134
|
init_command();
|
|
92814
|
-
import { cpSync as cpSync4, existsSync as
|
|
93135
|
+
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
93136
|
import { createHash as createHash11 } from "node:crypto";
|
|
92816
93137
|
import { homedir as homedir11 } from "node:os";
|
|
92817
|
-
import { dirname as dirname18, join as
|
|
93138
|
+
import { dirname as dirname18, join as join45, relative as relative9, resolve as resolve24, sep as sep7 } from "node:path";
|
|
92818
93139
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
92819
93140
|
var HASH_FILE = ".installed-hash";
|
|
92820
93141
|
function skillsRoot(shipped) {
|
|
92821
93142
|
let directory = dirname18(fileURLToPath3(shipped.anchor));
|
|
92822
93143
|
for (let depth = 0; depth < 5; depth += 1) {
|
|
92823
|
-
const candidate =
|
|
92824
|
-
if (
|
|
93144
|
+
const candidate = join45(directory, "skills");
|
|
93145
|
+
if (existsSync39(join45(candidate, shipped.names[0], "SKILL.md"))) return candidate;
|
|
92825
93146
|
directory = dirname18(directory);
|
|
92826
93147
|
}
|
|
92827
93148
|
throw new CliError({
|
|
@@ -92833,33 +93154,33 @@ function skillsRoot(shipped) {
|
|
|
92833
93154
|
}
|
|
92834
93155
|
function filesOf2(dir) {
|
|
92835
93156
|
return readdirSync22(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
92836
|
-
const path3 =
|
|
93157
|
+
const path3 = join45(dir, entry.name);
|
|
92837
93158
|
if (entry.isDirectory()) return filesOf2(path3);
|
|
92838
93159
|
return entry.name === HASH_FILE ? [] : [path3];
|
|
92839
93160
|
});
|
|
92840
93161
|
}
|
|
92841
93162
|
function descriptionOf(skillFile) {
|
|
92842
|
-
const match = /^---\n([\s\S]*?)\n---/.exec(
|
|
93163
|
+
const match = /^---\n([\s\S]*?)\n---/.exec(readFileSync33(skillFile, "utf8"));
|
|
92843
93164
|
if (!match?.[1]) return "";
|
|
92844
|
-
const frontmatter = (0,
|
|
93165
|
+
const frontmatter = (0, import_yaml9.parse)(match[1]);
|
|
92845
93166
|
return typeof frontmatter?.description === "string" ? frontmatter.description : "";
|
|
92846
93167
|
}
|
|
92847
93168
|
function shippedSkills(shipped) {
|
|
92848
93169
|
const root = skillsRoot(shipped);
|
|
92849
93170
|
return shipped.names.map((name) => {
|
|
92850
|
-
const dir =
|
|
92851
|
-
if (!
|
|
93171
|
+
const dir = join45(root, name);
|
|
93172
|
+
if (!existsSync39(join45(dir, "SKILL.md"))) {
|
|
92852
93173
|
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
93174
|
}
|
|
92854
93175
|
const files = filesOf2(dir).map((file2) => relative9(dir, file2).split(sep7).join("/")).sort();
|
|
92855
|
-
return { name, description: descriptionOf(
|
|
93176
|
+
return { name, description: descriptionOf(join45(dir, "SKILL.md")), dir, files };
|
|
92856
93177
|
});
|
|
92857
93178
|
}
|
|
92858
93179
|
function folderHash(dir) {
|
|
92859
93180
|
const hash2 = createHash11("sha256");
|
|
92860
93181
|
for (const file2 of filesOf2(dir).map((path3) => relative9(dir, path3).split(sep7).join("/")).sort()) {
|
|
92861
93182
|
hash2.update(`${file2}\0`);
|
|
92862
|
-
hash2.update(
|
|
93183
|
+
hash2.update(readFileSync33(join45(dir, file2)));
|
|
92863
93184
|
hash2.update("\0");
|
|
92864
93185
|
}
|
|
92865
93186
|
return hash2.digest("hex");
|
|
@@ -92879,18 +93200,18 @@ function pick2(all, names) {
|
|
|
92879
93200
|
});
|
|
92880
93201
|
}
|
|
92881
93202
|
function installBase(input2) {
|
|
92882
|
-
if (typeof input2.flags.dir === "string") return
|
|
92883
|
-
if (input2.flags.global === true) return
|
|
92884
|
-
return
|
|
93203
|
+
if (typeof input2.flags.dir === "string") return resolve24(input2.flags.dir);
|
|
93204
|
+
if (input2.flags.global === true) return join45(homedir11(), ".claude", "skills");
|
|
93205
|
+
return resolve24(".claude", "skills");
|
|
92885
93206
|
}
|
|
92886
93207
|
function planOne(skill, base2) {
|
|
92887
|
-
const target =
|
|
93208
|
+
const target = join45(base2, skill.name);
|
|
92888
93209
|
const hash2 = folderHash(skill.dir);
|
|
92889
|
-
if (!
|
|
93210
|
+
if (!existsSync39(join45(target, "SKILL.md"))) return { skill, target, hash: hash2, action: "installed", edited: false };
|
|
92890
93211
|
const onDisk = folderHash(target);
|
|
92891
93212
|
if (onDisk === hash2) return { skill, target, hash: hash2, action: "unchanged", edited: false };
|
|
92892
|
-
const hashFile =
|
|
92893
|
-
const recorded =
|
|
93213
|
+
const hashFile = join45(target, HASH_FILE);
|
|
93214
|
+
const recorded = existsSync39(hashFile) ? readFileSync33(hashFile, "utf8").trim() : "";
|
|
92894
93215
|
return { skill, target, hash: hash2, action: "updated", edited: onDisk !== recorded };
|
|
92895
93216
|
}
|
|
92896
93217
|
function writeOne(plan) {
|
|
@@ -92898,7 +93219,7 @@ function writeOne(plan) {
|
|
|
92898
93219
|
rmSync15(plan.target, { recursive: true, force: true });
|
|
92899
93220
|
mkdirSync28(plan.target, { recursive: true });
|
|
92900
93221
|
cpSync4(plan.skill.dir, plan.target, { recursive: true });
|
|
92901
|
-
writeFileSync25(
|
|
93222
|
+
writeFileSync25(join45(plan.target, HASH_FILE), `${plan.hash}
|
|
92902
93223
|
`);
|
|
92903
93224
|
} catch (error62) {
|
|
92904
93225
|
throw new CliError({
|
|
@@ -92943,7 +93264,7 @@ function createSkillCommands(shipped) {
|
|
|
92943
93264
|
context.output.line(skill.dir);
|
|
92944
93265
|
return { data: { name: skill.name, path: skill.dir, content: null } };
|
|
92945
93266
|
}
|
|
92946
|
-
const content =
|
|
93267
|
+
const content = readFileSync33(join45(skill.dir, "SKILL.md"), "utf8");
|
|
92947
93268
|
context.output.line(content);
|
|
92948
93269
|
return { data: { name: skill.name, path: skill.dir, content } };
|
|
92949
93270
|
}
|
|
@@ -93025,21 +93346,21 @@ function createSkillCommands(shipped) {
|
|
|
93025
93346
|
// src/init.ts
|
|
93026
93347
|
init_command();
|
|
93027
93348
|
init_protocol();
|
|
93028
|
-
import { existsSync as
|
|
93029
|
-
import { dirname as dirname19, isAbsolute as isAbsolute8, relative as relative10, resolve as
|
|
93030
|
-
var
|
|
93349
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync29, writeFileSync as writeFileSync26 } from "node:fs";
|
|
93350
|
+
import { dirname as dirname19, isAbsolute as isAbsolute8, relative as relative10, resolve as resolve25 } from "node:path";
|
|
93351
|
+
var import_yaml10 = __toESM(require_dist(), 1);
|
|
93031
93352
|
function initHarnessFolder(workspaceDir, adapter = CLAUDE_ADAPTER) {
|
|
93032
93353
|
const file2 = harnessRootAt(workspaceDir, adapter).file;
|
|
93033
|
-
if (
|
|
93354
|
+
if (existsSync40(file2)) {
|
|
93034
93355
|
throw new CliError({ code: "conflict", message: `This project has ${file2} already.`, exitCode: ExitCode.Validation, hint: "A harness folder holds one orbital.yaml." });
|
|
93035
93356
|
}
|
|
93036
93357
|
mkdirSync29(dirname19(file2), { recursive: true });
|
|
93037
|
-
writeFileSync26(file2, (0,
|
|
93358
|
+
writeFileSync26(file2, (0, import_yaml10.stringify)({ apiVersion: ORBITAL_API_VERSION }));
|
|
93038
93359
|
return file2;
|
|
93039
93360
|
}
|
|
93040
93361
|
function workspaceOf4(flag) {
|
|
93041
|
-
const workspaceDir =
|
|
93042
|
-
if (!
|
|
93362
|
+
const workspaceDir = resolve25(typeof flag === "string" ? flag : ".");
|
|
93363
|
+
if (!existsSync40(workspaceDir)) throw new CliError({ code: "not-found", message: `${workspaceDir} does not exist.`, exitCode: ExitCode.Validation });
|
|
93043
93364
|
return workspaceDir;
|
|
93044
93365
|
}
|
|
93045
93366
|
var WORKSPACE3 = { name: "workspace", type: "path", summary: "The project. Default: the current directory." };
|
|
@@ -93077,9 +93398,9 @@ function pluginRefOf(home, given) {
|
|
|
93077
93398
|
return path3.startsWith(".") ? path3 : `./${path3}`;
|
|
93078
93399
|
} };
|
|
93079
93400
|
}
|
|
93080
|
-
installPlugin(home, resolved);
|
|
93401
|
+
const { name } = installPlugin(home, resolved);
|
|
93081
93402
|
const source = resolved.kind === "npm" ? `npm:${resolved.pkg}` : resolved.kind === "git" ? `git:${resolved.url}` : spec;
|
|
93082
|
-
return { ref: { name
|
|
93403
|
+
return { ref: { name, source, base: home }, recorded: () => source };
|
|
93083
93404
|
}
|
|
93084
93405
|
function knownRef(context, known) {
|
|
93085
93406
|
const home = orbitalHome(context.env);
|
|
@@ -93115,7 +93436,7 @@ function targetOf2(context, workspaceDir, named2, given, adapters) {
|
|
|
93115
93436
|
});
|
|
93116
93437
|
}
|
|
93117
93438
|
async function chooseTargets(context, workspaceDir, adapters) {
|
|
93118
|
-
const present = adapters.filter((adapter) =>
|
|
93439
|
+
const present = adapters.filter((adapter) => existsSync40(resolve25(workspaceDir, adapter.folder.folder)));
|
|
93119
93440
|
if (present.length === 1) {
|
|
93120
93441
|
context.output.step(`installing ${present[0].id}: ${present[0].folder.folder} is the harness folder of this project`);
|
|
93121
93442
|
return [{ adapter: present[0], given: null }];
|
|
@@ -93149,9 +93470,9 @@ async function adapterInstall(context, adapter, workspaceDir, force) {
|
|
|
93149
93470
|
async function installTarget(context, workspaceDir, target, user, force) {
|
|
93150
93471
|
const { adapter, given } = target;
|
|
93151
93472
|
const root = harnessRootAt(workspaceDir, adapter);
|
|
93152
|
-
const file2 =
|
|
93473
|
+
const file2 = existsSync40(root.file) ? null : initHarnessFolder(workspaceDir, adapter);
|
|
93153
93474
|
if (file2) context.output.line(`wrote ${file2}`);
|
|
93154
|
-
const plugin = given ? { name: given.ref.name, source: user ? isPackageSource(given.ref.source) ? given.ref.source :
|
|
93475
|
+
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
93476
|
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
93477
|
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
93478
|
const install2 = await adapterInstall(context, adapter, workspaceDir, force);
|
|
@@ -93238,6 +93559,12 @@ function followAttach(context, options, signal, inbox, timing = {}) {
|
|
|
93238
93559
|
delivery: (event) => {
|
|
93239
93560
|
const delivery = DeliverySchema.safeParse(event);
|
|
93240
93561
|
if (delivery.success) inbox.push(delivery.data);
|
|
93562
|
+
const answered = AnsweredSchema.safeParse(event);
|
|
93563
|
+
if (!answered.success) return;
|
|
93564
|
+
for (let index = inbox.length - 1; index >= 0; index -= 1) {
|
|
93565
|
+
const held2 = inbox[index];
|
|
93566
|
+
if (held2.mission === answered.data.mission && (answered.data.question === null || held2.question === answered.data.question)) inbox.splice(index, 1);
|
|
93567
|
+
}
|
|
93241
93568
|
},
|
|
93242
93569
|
detached: (reason) => {
|
|
93243
93570
|
if (kept) kept.ended = true;
|
|
@@ -93367,9 +93694,8 @@ function mcpCommand(toolsOf) {
|
|
|
93367
93694
|
const joined = await attachment.current();
|
|
93368
93695
|
return { url: `${joined.nodeUrl}/a2a`, token: joined.token };
|
|
93369
93696
|
},
|
|
93370
|
-
|
|
93371
|
-
|
|
93372
|
-
},
|
|
93697
|
+
sessionId: null,
|
|
93698
|
+
attached: async () => (await attachment.current()).sessionId,
|
|
93373
93699
|
harness,
|
|
93374
93700
|
follow: true,
|
|
93375
93701
|
tools: inboxTools(context, attachment.current, inbox)
|
|
@@ -93378,8 +93704,8 @@ function mcpCommand(toolsOf) {
|
|
|
93378
93704
|
const server = await mcpServer(options, input2.flags.actions === true, toolsOf);
|
|
93379
93705
|
const transport = new StdioServerTransport();
|
|
93380
93706
|
await server.connect(transport);
|
|
93381
|
-
await new Promise((
|
|
93382
|
-
server.server.onclose =
|
|
93707
|
+
await new Promise((resolve27) => {
|
|
93708
|
+
server.server.onclose = resolve27;
|
|
93383
93709
|
process.stdin.once("end", () => void server.close());
|
|
93384
93710
|
});
|
|
93385
93711
|
stop.abort();
|
|
@@ -93405,6 +93731,10 @@ function orbitalCommands(options) {
|
|
|
93405
93731
|
...options.commands ?? []
|
|
93406
93732
|
];
|
|
93407
93733
|
}
|
|
93734
|
+
function workspacePluginCommands(options) {
|
|
93735
|
+
const reserved = [.../* @__PURE__ */ new Set([...options.builtIn.map((spec) => spec.path[0]), ...INTROSPECTION_WORDS])];
|
|
93736
|
+
return pluginCommandSpecs({ workspaceDir: options.workspaceDir, env: options.env, reserved, ...options.terminal && { terminal: options.terminal } });
|
|
93737
|
+
}
|
|
93408
93738
|
|
|
93409
93739
|
// src/launch.ts
|
|
93410
93740
|
init_zod();
|
|
@@ -93437,13 +93767,15 @@ async function nodeLaunch(env3, argv, prefix, run3) {
|
|
|
93437
93767
|
}
|
|
93438
93768
|
|
|
93439
93769
|
// src/cli.ts
|
|
93440
|
-
var orbital = () =>
|
|
93441
|
-
|
|
93442
|
-
|
|
93443
|
-
});
|
|
93770
|
+
var orbital = (options) => {
|
|
93771
|
+
const own2 = orbitalCommands({ terminal: async () => (await init_tui().then(() => tui_exports)).terminal });
|
|
93772
|
+
const workspace = peekFlag(options.argv, "workspace");
|
|
93773
|
+
const plugins = workspacePluginCommands({ builtIn: own2, workspaceDir: resolve26(typeof workspace === "string" ? workspace : "."), env: options.env });
|
|
93774
|
+
return { registry: composeRegistry([...own2, ...plugins]), context: (base2) => base2 };
|
|
93775
|
+
};
|
|
93444
93776
|
async function run2(options) {
|
|
93445
93777
|
setBrand(ORBITAL_BRAND);
|
|
93446
|
-
return runProgram(orbital(), options);
|
|
93778
|
+
return runProgram(orbital(options), options);
|
|
93447
93779
|
}
|
|
93448
93780
|
function processRun(argv) {
|
|
93449
93781
|
return run2({
|