@kubitnodes/stellaris 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +108 -18
- package/dist/server/index.js +179 -36
- package/package.json +1 -1
- package/web/dist/assets/{BackLink-Cr1RWb4K.js → BackLink-DrYyD7II.js} +1 -1
- package/web/dist/assets/{ChannelView-iK3ug1lV.js → ChannelView-B2sufhRs.js} +1 -1
- package/web/dist/assets/CitizenView-B9u0X7ZY.js +2 -0
- package/web/dist/assets/{CitizensView-CFrD6f7m.js → CitizensView-B60Z7gmH.js} +1 -1
- package/web/dist/assets/KnowledgeView-Cppc2D7_.js +1 -0
- package/web/dist/assets/{Markdown-CYcTTxcR.js → Markdown-B390nfRS.js} +1 -1
- package/web/dist/assets/{MessageItem-DDC_1Jqn.js → MessageItem-D54BS9OS.js} +1 -1
- package/web/dist/assets/MetricsView-CsoRuj4g.js +1 -0
- package/web/dist/assets/{NeedsYouView-I7j_cnda.js → NeedsYouView-BPjjyZRx.js} +1 -1
- package/web/dist/assets/{Overview-DI7LPNEY.js → Overview-BWEXLQaq.js} +1 -1
- package/web/dist/assets/{Pane-BNjb76U4.js → Pane-DvocjNie.js} +1 -1
- package/web/dist/assets/ProjectView-BIC7kE4L.js +1 -0
- package/web/dist/assets/{ProposalView-DrP3Cv9y.js → ProposalView-cPi-EUXR.js} +1 -1
- package/web/dist/assets/{ProposalsView-90rufsQN.js → ProposalsView-Bq7iCMtk.js} +1 -1
- package/web/dist/assets/{RunnersView-iMbemuCe.js → RunnersView-BiJmbsoG.js} +1 -1
- package/web/dist/assets/{SocietyView-C9FdIFZY.js → SocietyView-6iPyxKze.js} +1 -1
- package/web/dist/assets/{SubjectThread-Bw6wEJpH.js → SubjectThread-D2BHnecl.js} +1 -1
- package/web/dist/assets/{TaskFileView-D0n7kzt2.js → TaskFileView-DDs6OHWY.js} +1 -1
- package/web/dist/assets/{TaskView-BZ6U_XpQ.js → TaskView-3_2vs2fy.js} +1 -1
- package/web/dist/assets/{TasksView-CtGF3i-n.js → TasksView-B7a0sWKu.js} +1 -1
- package/web/dist/assets/{ThreadCard-FPtAzvNM.js → ThreadCard-9h03aKCB.js} +1 -1
- package/web/dist/assets/{ThreadForms-LFw2EF6r.js → ThreadForms-uORhDqAo.js} +1 -1
- package/web/dist/assets/{ThreadView-DnDx2SCv.js → ThreadView-DVSe1HGK.js} +1 -1
- package/web/dist/assets/{citizen-Bm_r8vBY.js → citizen-CDTy3g7f.js} +1 -1
- package/web/dist/assets/format-C6tBTRKZ.js +55 -0
- package/web/dist/assets/index-CugUsWCm.js +23 -0
- package/web/dist/assets/{tasks-tFehkpRz.js → tasks-Bh3GaY7V.js} +1 -1
- package/web/dist/assets/{useStickyScroll-XrG6M-hT.js → useStickyScroll-C6bu36lV.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/dist/assets/CitizenView-BiaYYmCh.js +0 -2
- package/web/dist/assets/KnowledgeView-CR3cXdj0.js +0 -1
- package/web/dist/assets/MetricsView-xdvE7pFA.js +0 -1
- package/web/dist/assets/ProjectView-DYDcDsWg.js +0 -1
- package/web/dist/assets/format-Cj7NW9Y9.js +0 -55
- package/web/dist/assets/index-CthEgIzf.js +0 -23
package/dist/cli/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import path7 from "node:path";
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// packages/board-core/dist/board.js
|
|
9
|
-
import { readdir as readdir3, readFile as readFile5, rename as rename3, stat as stat2 } from "node:fs/promises";
|
|
9
|
+
import { mkdir as mkdir4, readdir as readdir3, readFile as readFile5, rename as rename3, stat as stat2 } from "node:fs/promises";
|
|
10
10
|
import path6 from "node:path";
|
|
11
11
|
import { decodeTime, monotonicFactory } from "ulid";
|
|
12
12
|
import { z as z12 } from "zod";
|
|
@@ -56,7 +56,8 @@ var VERB_NAMES = [
|
|
|
56
56
|
"plan_task",
|
|
57
57
|
"advance_task",
|
|
58
58
|
"configure_project",
|
|
59
|
-
"archive_project"
|
|
59
|
+
"archive_project",
|
|
60
|
+
"remove_knowledge"
|
|
60
61
|
];
|
|
61
62
|
var VerbNameSchema = z2.enum(VERB_NAMES);
|
|
62
63
|
var CharterTriggerSchema = z2.enum(["user_post", "ops_event", "heartbeat"]);
|
|
@@ -100,6 +101,7 @@ var MEMBER_VERBS = [
|
|
|
100
101
|
"join_project",
|
|
101
102
|
"leave_project",
|
|
102
103
|
"write_knowledge",
|
|
104
|
+
"remove_knowledge",
|
|
103
105
|
"plan_task",
|
|
104
106
|
"advance_task"
|
|
105
107
|
];
|
|
@@ -314,6 +316,11 @@ var ModelOptionSchema = z3.object({
|
|
|
314
316
|
efforts: z3.array(EffortOptionSchema).default([]),
|
|
315
317
|
defaultEffort: EffortSchema.optional()
|
|
316
318
|
});
|
|
319
|
+
var RunnerModelsSchema = z3.object({
|
|
320
|
+
runner: NameSchema,
|
|
321
|
+
cli: CliKindSchema,
|
|
322
|
+
models: z3.array(ModelOptionSchema)
|
|
323
|
+
});
|
|
317
324
|
var AgentStatusSchema = z3.enum(["active", "retired"]);
|
|
318
325
|
var AgentSchema = z3.object({
|
|
319
326
|
name: NameSchema,
|
|
@@ -534,6 +541,12 @@ var KnowledgeSchema = z3.object({
|
|
|
534
541
|
updatedBy: NameSchema,
|
|
535
542
|
updatedAt: IsoDateTimeSchema
|
|
536
543
|
});
|
|
544
|
+
var RemovedKnowledgeSchema = z3.object({
|
|
545
|
+
topic: NameSchema,
|
|
546
|
+
project: NameSchema.nullable(),
|
|
547
|
+
removedBy: NameSchema,
|
|
548
|
+
removedAt: IsoDateTimeSchema
|
|
549
|
+
});
|
|
537
550
|
var ProposalFrontmatterSchema = z3.object({
|
|
538
551
|
id: UlidSchema,
|
|
539
552
|
kind: ProposalKindSchema,
|
|
@@ -630,6 +643,11 @@ var VerbInputs = {
|
|
|
630
643
|
topic: NameSchema,
|
|
631
644
|
body: z4.string().min(1)
|
|
632
645
|
}),
|
|
646
|
+
remove_knowledge: z4.object({
|
|
647
|
+
/** As for write_knowledge: a project you belong to, or null for society knowledge. */
|
|
648
|
+
project: NameSchema.nullable().default(null),
|
|
649
|
+
topic: NameSchema
|
|
650
|
+
}),
|
|
633
651
|
plan_task: z4.object({
|
|
634
652
|
task_id: UlidSchema,
|
|
635
653
|
stages: z4.array(PlanEditStageSchema),
|
|
@@ -639,7 +657,9 @@ var VerbInputs = {
|
|
|
639
657
|
advance_task: z4.object({ task_id: UlidSchema, note: z4.string().min(1).optional() }),
|
|
640
658
|
configure_project: z4.object({
|
|
641
659
|
project: NameSchema,
|
|
642
|
-
on_done: CompletionEffectSchema
|
|
660
|
+
on_done: CompletionEffectSchema.optional(),
|
|
661
|
+
/** A new display name; the slug, which everything refers to the project by, never changes. */
|
|
662
|
+
name: z4.string().trim().min(1).max(120).optional()
|
|
643
663
|
}),
|
|
644
664
|
archive_project: z4.object({ project: NameSchema, reason: z4.string().min(1) })
|
|
645
665
|
};
|
|
@@ -679,6 +699,7 @@ var BOARD_EVENT_TYPES = [
|
|
|
679
699
|
"agent.joined",
|
|
680
700
|
"agent.left",
|
|
681
701
|
"knowledge.written",
|
|
702
|
+
"knowledge.removed",
|
|
682
703
|
"skill.promoted",
|
|
683
704
|
"runner.added",
|
|
684
705
|
"user.signed_in",
|
|
@@ -1276,7 +1297,7 @@ var RunnerConfigSchema = z10.object({
|
|
|
1276
1297
|
});
|
|
1277
1298
|
|
|
1278
1299
|
// packages/shared/dist/version.js
|
|
1279
|
-
var STELLARIS_VERSION = "0.3.
|
|
1300
|
+
var STELLARIS_VERSION = "0.3.8";
|
|
1280
1301
|
|
|
1281
1302
|
// packages/board-core/dist/errors.js
|
|
1282
1303
|
var BoardError = class extends Error {
|
|
@@ -2309,6 +2330,10 @@ var BoardPaths = class {
|
|
|
2309
2330
|
pausedFile() {
|
|
2310
2331
|
return path5.join(this.state(), "paused.json");
|
|
2311
2332
|
}
|
|
2333
|
+
/** Removed knowledge topics, set aside outside the projection, by scope. */
|
|
2334
|
+
removedKnowledge(scope) {
|
|
2335
|
+
return path5.join(this.state(), "removed-knowledge", scope);
|
|
2336
|
+
}
|
|
2312
2337
|
};
|
|
2313
2338
|
|
|
2314
2339
|
// packages/board-core/dist/tokens.js
|
|
@@ -2402,6 +2427,7 @@ var OPS_CHANNEL_RETIRED = "ops-channel-retired";
|
|
|
2402
2427
|
var DECISIONS_CHANNEL_RETIRED = "decisions-channel-retired";
|
|
2403
2428
|
var THREAD_CONVERSATIONS = "thread-conversations";
|
|
2404
2429
|
var ASKS_CHANNEL_OPENED = "asks-channel-opened";
|
|
2430
|
+
var KNOWLEDGE_REMOVAL = "knowledge-removal-granted";
|
|
2405
2431
|
var PLANNING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
2406
2432
|
var REALLOCATING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
2407
2433
|
var PROFILE_TEMPLATE = "# Profile\n\nOne short paragraph, kept current: what I do well, what I am working on, and what to send my way. The board projects this into the roster the front desk reads.\n";
|
|
@@ -2591,7 +2617,8 @@ var Board = class _Board {
|
|
|
2591
2617
|
OPS_CHANNEL_RETIRED,
|
|
2592
2618
|
DECISIONS_CHANNEL_RETIRED,
|
|
2593
2619
|
THREAD_CONVERSATIONS,
|
|
2594
|
-
ASKS_CHANNEL_OPENED
|
|
2620
|
+
ASKS_CHANNEL_OPENED,
|
|
2621
|
+
KNOWLEDGE_REMOVAL
|
|
2595
2622
|
].filter((name) => !applied.includes(name));
|
|
2596
2623
|
if (pending.length === 0) {
|
|
2597
2624
|
return;
|
|
@@ -2620,8 +2647,22 @@ var Board = class _Board {
|
|
|
2620
2647
|
if (pending.includes(ASKS_CHANNEL_OPENED)) {
|
|
2621
2648
|
await this.openAsksChannel();
|
|
2622
2649
|
}
|
|
2650
|
+
if (pending.includes(KNOWLEDGE_REMOVAL)) {
|
|
2651
|
+
await this.grantKnowledgeRemoval();
|
|
2652
|
+
}
|
|
2623
2653
|
await writeJson(file, { applied: [...applied, ...pending] });
|
|
2624
2654
|
}
|
|
2655
|
+
/**
|
|
2656
|
+
* Whoever may write a scope's knowledge may remove it, so every charter granting
|
|
2657
|
+
* `write_knowledge` is granted `remove_knowledge` once; seed roles have it from their seed.
|
|
2658
|
+
*/
|
|
2659
|
+
async grantKnowledgeRemoval() {
|
|
2660
|
+
for (const charter of await this.listRoles()) {
|
|
2661
|
+
if (charter.verbs.includes("write_knowledge") && !charter.verbs.includes("remove_knowledge")) {
|
|
2662
|
+
await this.writeRoleUnlocked(USER_NAME, { ...charter, verbs: [...charter.verbs, "remove_knowledge"] }, { verbsAdded: ["remove_knowledge"] });
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2625
2666
|
/**
|
|
2626
2667
|
* A task sent back before tasks recorded it kept no trace outside the event log. A task still
|
|
2627
2668
|
* short of the stage its latest `task.moved` came from is in that rework, and records it.
|
|
@@ -3853,16 +3894,7 @@ ${task2.body}`;
|
|
|
3853
3894
|
const args = VerbInputs.write_knowledge.parse(input);
|
|
3854
3895
|
await this.authorize(actor, "write_knowledge");
|
|
3855
3896
|
return this.mutex.run(async () => {
|
|
3856
|
-
|
|
3857
|
-
if (!CURATING_ROLES.includes(actor.role)) {
|
|
3858
|
-
throw new BoardError("FORBIDDEN", "society knowledge is curated by the steward and the user");
|
|
3859
|
-
}
|
|
3860
|
-
} else {
|
|
3861
|
-
const project2 = await this.readActiveProject(args.project);
|
|
3862
|
-
if (!CURATING_ROLES.includes(actor.role) && !project2.members.includes(actor.name)) {
|
|
3863
|
-
throw new BoardError("FORBIDDEN", `${actor.name} is not a member of ${args.project}`);
|
|
3864
|
-
}
|
|
3865
|
-
}
|
|
3897
|
+
await this.assertMayCurate(actor, args.project);
|
|
3866
3898
|
const data = KnowledgeSchema.parse({
|
|
3867
3899
|
topic: args.topic,
|
|
3868
3900
|
project: args.project,
|
|
@@ -3882,6 +3914,52 @@ ${task2.body}`;
|
|
|
3882
3914
|
return { ...data, body: args.body };
|
|
3883
3915
|
});
|
|
3884
3916
|
}
|
|
3917
|
+
/**
|
|
3918
|
+
* Retires a topic, by whoever may write it. Its file leaves the knowledge directory, so no turn,
|
|
3919
|
+
* search, or digest finds it again, and is set aside under `state/removed-knowledge`, outside
|
|
3920
|
+
* the projection, for the user to restore by hand. The note in the matching general channel
|
|
3921
|
+
* carries no mention, as a write's does.
|
|
3922
|
+
*/
|
|
3923
|
+
async removeKnowledge(actor, input) {
|
|
3924
|
+
const args = VerbInputs.remove_knowledge.parse(input);
|
|
3925
|
+
await this.authorize(actor, "remove_knowledge");
|
|
3926
|
+
return this.mutex.run(async () => {
|
|
3927
|
+
await this.assertMayCurate(actor, args.project);
|
|
3928
|
+
const file = args.project === null ? this.paths.societyKnowledgeFile(args.topic) : this.paths.projectKnowledgeFile(args.project, args.topic);
|
|
3929
|
+
if (!await exists(file)) {
|
|
3930
|
+
throw new BoardError("NOT_FOUND", `${args.project ?? "the society"} has no knowledge topic ${args.topic}`);
|
|
3931
|
+
}
|
|
3932
|
+
const removed = RemovedKnowledgeSchema.parse({
|
|
3933
|
+
topic: args.topic,
|
|
3934
|
+
project: args.project,
|
|
3935
|
+
removedBy: actor.name,
|
|
3936
|
+
removedAt: this.now().toISOString()
|
|
3937
|
+
});
|
|
3938
|
+
const aside = this.paths.removedKnowledge(args.project ?? SOCIETY_SCOPE);
|
|
3939
|
+
await mkdir4(aside, { recursive: true });
|
|
3940
|
+
await rename3(file, path6.join(aside, `${args.topic}.${removed.removedAt.replaceAll(":", "-")}.md`));
|
|
3941
|
+
await this.events.append("knowledge.removed", actor.name, {
|
|
3942
|
+
topic: args.topic,
|
|
3943
|
+
project: args.project
|
|
3944
|
+
});
|
|
3945
|
+
const where = args.project === null ? "general" : channelRef(args.project, "general");
|
|
3946
|
+
await this.appendMessage(actor.name, where, `Knowledge removed: ${args.topic}. It is no longer under knowledge/${args.topic}.md${args.project === null ? " of the society" : ""}.`);
|
|
3947
|
+
return removed;
|
|
3948
|
+
});
|
|
3949
|
+
}
|
|
3950
|
+
/** Society knowledge is the steward's and the user's; a project's, its members' too. */
|
|
3951
|
+
async assertMayCurate(actor, project2) {
|
|
3952
|
+
if (project2 === null) {
|
|
3953
|
+
if (!CURATING_ROLES.includes(actor.role)) {
|
|
3954
|
+
throw new BoardError("FORBIDDEN", "society knowledge is curated by the steward and the user");
|
|
3955
|
+
}
|
|
3956
|
+
return;
|
|
3957
|
+
}
|
|
3958
|
+
const active = await this.readActiveProject(project2);
|
|
3959
|
+
if (!CURATING_ROLES.includes(actor.role) && !active.members.includes(actor.name)) {
|
|
3960
|
+
throw new BoardError("FORBIDDEN", `${actor.name} is not a member of ${project2}`);
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3885
3963
|
async openThread(actor, input) {
|
|
3886
3964
|
const args = VerbInputs.open_thread.parse(input);
|
|
3887
3965
|
await this.authorize(actor, "open_thread");
|
|
@@ -4551,18 +4629,28 @@ ${rationale}`}`);
|
|
|
4551
4629
|
}));
|
|
4552
4630
|
}
|
|
4553
4631
|
/** Sets a project's completion effect. */
|
|
4632
|
+
/**
|
|
4633
|
+
* Sets a project's completion effect, its display name, or both. The slug stays: addresses,
|
|
4634
|
+
* channel references, branches, and runners' directories all carry it.
|
|
4635
|
+
*/
|
|
4554
4636
|
async configureProject(actor, input) {
|
|
4555
4637
|
const args = VerbInputs.configure_project.parse(input);
|
|
4556
4638
|
await this.authorize(actor, "configure_project");
|
|
4639
|
+
if (args.on_done === void 0 && args.name === void 0) {
|
|
4640
|
+
throw new BoardError("VALIDATION", "configure_project needs on_done, name, or both");
|
|
4641
|
+
}
|
|
4557
4642
|
return this.mutex.run(async () => {
|
|
4558
|
-
await this.readActiveProject(args.project);
|
|
4643
|
+
const previous = await this.readActiveProject(args.project);
|
|
4559
4644
|
const project2 = await this.updateProject(args.project, (current) => ({
|
|
4560
4645
|
...current,
|
|
4561
|
-
onDone: args.on_done
|
|
4646
|
+
...args.on_done === void 0 ? {} : { onDone: args.on_done },
|
|
4647
|
+
...args.name === void 0 ? {} : { name: args.name }
|
|
4562
4648
|
}));
|
|
4563
4649
|
await this.events.append("project.configured", actor.name, {
|
|
4564
4650
|
slug: project2.slug,
|
|
4565
|
-
onDone: project2.onDone
|
|
4651
|
+
onDone: project2.onDone,
|
|
4652
|
+
name: project2.name,
|
|
4653
|
+
...project2.name === previous.name ? {} : { previousName: previous.name }
|
|
4566
4654
|
});
|
|
4567
4655
|
return project2;
|
|
4568
4656
|
});
|
|
@@ -4708,6 +4796,8 @@ ${rationale}`}`);
|
|
|
4708
4796
|
return this.leaveProject(actor, VerbInputs.leave_project.parse(input));
|
|
4709
4797
|
case "write_knowledge":
|
|
4710
4798
|
return this.writeKnowledge(actor, VerbInputs.write_knowledge.parse(input));
|
|
4799
|
+
case "remove_knowledge":
|
|
4800
|
+
return this.removeKnowledge(actor, VerbInputs.remove_knowledge.parse(input));
|
|
4711
4801
|
case "plan_task":
|
|
4712
4802
|
return this.planTask(actor, VerbInputs.plan_task.parse(input));
|
|
4713
4803
|
case "advance_task":
|
package/dist/server/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import path9 from "node:path";
|
|
|
6
6
|
import { serve } from "@hono/node-server";
|
|
7
7
|
|
|
8
8
|
// packages/board-core/dist/board.js
|
|
9
|
-
import { readdir as readdir3, readFile as readFile5, rename as rename3, stat as stat2 } from "node:fs/promises";
|
|
9
|
+
import { mkdir as mkdir4, readdir as readdir3, readFile as readFile5, rename as rename3, stat as stat2 } from "node:fs/promises";
|
|
10
10
|
import path6 from "node:path";
|
|
11
11
|
import { decodeTime, monotonicFactory } from "ulid";
|
|
12
12
|
import { z as z12 } from "zod";
|
|
@@ -56,7 +56,8 @@ var VERB_NAMES = [
|
|
|
56
56
|
"plan_task",
|
|
57
57
|
"advance_task",
|
|
58
58
|
"configure_project",
|
|
59
|
-
"archive_project"
|
|
59
|
+
"archive_project",
|
|
60
|
+
"remove_knowledge"
|
|
60
61
|
];
|
|
61
62
|
var VerbNameSchema = z2.enum(VERB_NAMES);
|
|
62
63
|
var CharterTriggerSchema = z2.enum(["user_post", "ops_event", "heartbeat"]);
|
|
@@ -100,6 +101,7 @@ var MEMBER_VERBS = [
|
|
|
100
101
|
"join_project",
|
|
101
102
|
"leave_project",
|
|
102
103
|
"write_knowledge",
|
|
104
|
+
"remove_knowledge",
|
|
103
105
|
"plan_task",
|
|
104
106
|
"advance_task"
|
|
105
107
|
];
|
|
@@ -314,6 +316,11 @@ var ModelOptionSchema = z3.object({
|
|
|
314
316
|
efforts: z3.array(EffortOptionSchema).default([]),
|
|
315
317
|
defaultEffort: EffortSchema.optional()
|
|
316
318
|
});
|
|
319
|
+
var RunnerModelsSchema = z3.object({
|
|
320
|
+
runner: NameSchema,
|
|
321
|
+
cli: CliKindSchema,
|
|
322
|
+
models: z3.array(ModelOptionSchema)
|
|
323
|
+
});
|
|
317
324
|
var AgentStatusSchema = z3.enum(["active", "retired"]);
|
|
318
325
|
var AgentSchema = z3.object({
|
|
319
326
|
name: NameSchema,
|
|
@@ -534,6 +541,12 @@ var KnowledgeSchema = z3.object({
|
|
|
534
541
|
updatedBy: NameSchema,
|
|
535
542
|
updatedAt: IsoDateTimeSchema
|
|
536
543
|
});
|
|
544
|
+
var RemovedKnowledgeSchema = z3.object({
|
|
545
|
+
topic: NameSchema,
|
|
546
|
+
project: NameSchema.nullable(),
|
|
547
|
+
removedBy: NameSchema,
|
|
548
|
+
removedAt: IsoDateTimeSchema
|
|
549
|
+
});
|
|
537
550
|
var ProposalFrontmatterSchema = z3.object({
|
|
538
551
|
id: UlidSchema,
|
|
539
552
|
kind: ProposalKindSchema,
|
|
@@ -630,6 +643,11 @@ var VerbInputs = {
|
|
|
630
643
|
topic: NameSchema,
|
|
631
644
|
body: z4.string().min(1)
|
|
632
645
|
}),
|
|
646
|
+
remove_knowledge: z4.object({
|
|
647
|
+
/** As for write_knowledge: a project you belong to, or null for society knowledge. */
|
|
648
|
+
project: NameSchema.nullable().default(null),
|
|
649
|
+
topic: NameSchema
|
|
650
|
+
}),
|
|
633
651
|
plan_task: z4.object({
|
|
634
652
|
task_id: UlidSchema,
|
|
635
653
|
stages: z4.array(PlanEditStageSchema),
|
|
@@ -639,7 +657,9 @@ var VerbInputs = {
|
|
|
639
657
|
advance_task: z4.object({ task_id: UlidSchema, note: z4.string().min(1).optional() }),
|
|
640
658
|
configure_project: z4.object({
|
|
641
659
|
project: NameSchema,
|
|
642
|
-
on_done: CompletionEffectSchema
|
|
660
|
+
on_done: CompletionEffectSchema.optional(),
|
|
661
|
+
/** A new display name; the slug, which everything refers to the project by, never changes. */
|
|
662
|
+
name: z4.string().trim().min(1).max(120).optional()
|
|
643
663
|
}),
|
|
644
664
|
archive_project: z4.object({ project: NameSchema, reason: z4.string().min(1) })
|
|
645
665
|
};
|
|
@@ -663,9 +683,10 @@ var VERB_DESCRIPTIONS = {
|
|
|
663
683
|
join_project: "Join a project, or add another citizen to one when your role allows it. Membership gives the pair a worktree and an onboarding turn.",
|
|
664
684
|
leave_project: "Leave a project, or remove another citizen from one when your role allows it.",
|
|
665
685
|
write_knowledge: "Write or replace a knowledge topic: durable facts every member of a project should know, or with project null the society's shared knowledge (steward and user). Not a message; use post_message for those.",
|
|
686
|
+
remove_knowledge: "Remove a knowledge topic that is wrong, superseded, or folded into another, so no turn or search finds it again. Whoever may write the topic may remove it; the board keeps its text aside for the user to restore.",
|
|
666
687
|
plan_task: "Reshape a task's plan from the current stage onward, or after it while someone holds it: keep a stage by passing its id, drop it by leaving it out, add one without an id. Gated stages and on_done: user, steward, concierge.",
|
|
667
688
|
advance_task: "Finish the stage you hold, with a note for the task's thread on what you did. The next stage becomes current; past the last one the task is done.",
|
|
668
|
-
configure_project: "Set a project's completion effect
|
|
689
|
+
configure_project: "Set a project's completion effect (none, or merge to land each finished task's branch on the default branch), its display name, or both. The slug never changes.",
|
|
669
690
|
archive_project: "Archive a project whose work is finished or has moved: its members leave, its open threads close, and nothing more is posted, filed, or joined there, while its files and history stay. Refused while a task there is in play."
|
|
670
691
|
};
|
|
671
692
|
|
|
@@ -704,6 +725,7 @@ var BOARD_EVENT_TYPES = [
|
|
|
704
725
|
"agent.joined",
|
|
705
726
|
"agent.left",
|
|
706
727
|
"knowledge.written",
|
|
728
|
+
"knowledge.removed",
|
|
707
729
|
"skill.promoted",
|
|
708
730
|
"runner.added",
|
|
709
731
|
"user.signed_in",
|
|
@@ -1356,7 +1378,7 @@ var RunnerConfigSchema = z10.object({
|
|
|
1356
1378
|
});
|
|
1357
1379
|
|
|
1358
1380
|
// packages/shared/dist/version.js
|
|
1359
|
-
var STELLARIS_VERSION = "0.3.
|
|
1381
|
+
var STELLARIS_VERSION = "0.3.8";
|
|
1360
1382
|
|
|
1361
1383
|
// packages/board-core/dist/errors.js
|
|
1362
1384
|
var BoardError = class extends Error {
|
|
@@ -2392,6 +2414,10 @@ var BoardPaths = class {
|
|
|
2392
2414
|
pausedFile() {
|
|
2393
2415
|
return path5.join(this.state(), "paused.json");
|
|
2394
2416
|
}
|
|
2417
|
+
/** Removed knowledge topics, set aside outside the projection, by scope. */
|
|
2418
|
+
removedKnowledge(scope) {
|
|
2419
|
+
return path5.join(this.state(), "removed-knowledge", scope);
|
|
2420
|
+
}
|
|
2395
2421
|
};
|
|
2396
2422
|
|
|
2397
2423
|
// packages/board-core/dist/tokens.js
|
|
@@ -2485,6 +2511,7 @@ var OPS_CHANNEL_RETIRED = "ops-channel-retired";
|
|
|
2485
2511
|
var DECISIONS_CHANNEL_RETIRED = "decisions-channel-retired";
|
|
2486
2512
|
var THREAD_CONVERSATIONS = "thread-conversations";
|
|
2487
2513
|
var ASKS_CHANNEL_OPENED = "asks-channel-opened";
|
|
2514
|
+
var KNOWLEDGE_REMOVAL = "knowledge-removal-granted";
|
|
2488
2515
|
var PLANNING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
2489
2516
|
var REALLOCATING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
2490
2517
|
var PROFILE_TEMPLATE = "# Profile\n\nOne short paragraph, kept current: what I do well, what I am working on, and what to send my way. The board projects this into the roster the front desk reads.\n";
|
|
@@ -2674,7 +2701,8 @@ var Board = class _Board {
|
|
|
2674
2701
|
OPS_CHANNEL_RETIRED,
|
|
2675
2702
|
DECISIONS_CHANNEL_RETIRED,
|
|
2676
2703
|
THREAD_CONVERSATIONS,
|
|
2677
|
-
ASKS_CHANNEL_OPENED
|
|
2704
|
+
ASKS_CHANNEL_OPENED,
|
|
2705
|
+
KNOWLEDGE_REMOVAL
|
|
2678
2706
|
].filter((name) => !applied.includes(name));
|
|
2679
2707
|
if (pending.length === 0) {
|
|
2680
2708
|
return;
|
|
@@ -2703,8 +2731,22 @@ var Board = class _Board {
|
|
|
2703
2731
|
if (pending.includes(ASKS_CHANNEL_OPENED)) {
|
|
2704
2732
|
await this.openAsksChannel();
|
|
2705
2733
|
}
|
|
2734
|
+
if (pending.includes(KNOWLEDGE_REMOVAL)) {
|
|
2735
|
+
await this.grantKnowledgeRemoval();
|
|
2736
|
+
}
|
|
2706
2737
|
await writeJson(file, { applied: [...applied, ...pending] });
|
|
2707
2738
|
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Whoever may write a scope's knowledge may remove it, so every charter granting
|
|
2741
|
+
* `write_knowledge` is granted `remove_knowledge` once; seed roles have it from their seed.
|
|
2742
|
+
*/
|
|
2743
|
+
async grantKnowledgeRemoval() {
|
|
2744
|
+
for (const charter of await this.listRoles()) {
|
|
2745
|
+
if (charter.verbs.includes("write_knowledge") && !charter.verbs.includes("remove_knowledge")) {
|
|
2746
|
+
await this.writeRoleUnlocked(USER_NAME, { ...charter, verbs: [...charter.verbs, "remove_knowledge"] }, { verbsAdded: ["remove_knowledge"] });
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2708
2750
|
/**
|
|
2709
2751
|
* A task sent back before tasks recorded it kept no trace outside the event log. A task still
|
|
2710
2752
|
* short of the stage its latest `task.moved` came from is in that rework, and records it.
|
|
@@ -3936,16 +3978,7 @@ ${task.body}`;
|
|
|
3936
3978
|
const args = VerbInputs.write_knowledge.parse(input);
|
|
3937
3979
|
await this.authorize(actor, "write_knowledge");
|
|
3938
3980
|
return this.mutex.run(async () => {
|
|
3939
|
-
|
|
3940
|
-
if (!CURATING_ROLES.includes(actor.role)) {
|
|
3941
|
-
throw new BoardError("FORBIDDEN", "society knowledge is curated by the steward and the user");
|
|
3942
|
-
}
|
|
3943
|
-
} else {
|
|
3944
|
-
const project = await this.readActiveProject(args.project);
|
|
3945
|
-
if (!CURATING_ROLES.includes(actor.role) && !project.members.includes(actor.name)) {
|
|
3946
|
-
throw new BoardError("FORBIDDEN", `${actor.name} is not a member of ${args.project}`);
|
|
3947
|
-
}
|
|
3948
|
-
}
|
|
3981
|
+
await this.assertMayCurate(actor, args.project);
|
|
3949
3982
|
const data = KnowledgeSchema.parse({
|
|
3950
3983
|
topic: args.topic,
|
|
3951
3984
|
project: args.project,
|
|
@@ -3965,6 +3998,52 @@ ${task.body}`;
|
|
|
3965
3998
|
return { ...data, body: args.body };
|
|
3966
3999
|
});
|
|
3967
4000
|
}
|
|
4001
|
+
/**
|
|
4002
|
+
* Retires a topic, by whoever may write it. Its file leaves the knowledge directory, so no turn,
|
|
4003
|
+
* search, or digest finds it again, and is set aside under `state/removed-knowledge`, outside
|
|
4004
|
+
* the projection, for the user to restore by hand. The note in the matching general channel
|
|
4005
|
+
* carries no mention, as a write's does.
|
|
4006
|
+
*/
|
|
4007
|
+
async removeKnowledge(actor, input) {
|
|
4008
|
+
const args = VerbInputs.remove_knowledge.parse(input);
|
|
4009
|
+
await this.authorize(actor, "remove_knowledge");
|
|
4010
|
+
return this.mutex.run(async () => {
|
|
4011
|
+
await this.assertMayCurate(actor, args.project);
|
|
4012
|
+
const file = args.project === null ? this.paths.societyKnowledgeFile(args.topic) : this.paths.projectKnowledgeFile(args.project, args.topic);
|
|
4013
|
+
if (!await exists(file)) {
|
|
4014
|
+
throw new BoardError("NOT_FOUND", `${args.project ?? "the society"} has no knowledge topic ${args.topic}`);
|
|
4015
|
+
}
|
|
4016
|
+
const removed = RemovedKnowledgeSchema.parse({
|
|
4017
|
+
topic: args.topic,
|
|
4018
|
+
project: args.project,
|
|
4019
|
+
removedBy: actor.name,
|
|
4020
|
+
removedAt: this.now().toISOString()
|
|
4021
|
+
});
|
|
4022
|
+
const aside = this.paths.removedKnowledge(args.project ?? SOCIETY_SCOPE);
|
|
4023
|
+
await mkdir4(aside, { recursive: true });
|
|
4024
|
+
await rename3(file, path6.join(aside, `${args.topic}.${removed.removedAt.replaceAll(":", "-")}.md`));
|
|
4025
|
+
await this.events.append("knowledge.removed", actor.name, {
|
|
4026
|
+
topic: args.topic,
|
|
4027
|
+
project: args.project
|
|
4028
|
+
});
|
|
4029
|
+
const where = args.project === null ? "general" : channelRef(args.project, "general");
|
|
4030
|
+
await this.appendMessage(actor.name, where, `Knowledge removed: ${args.topic}. It is no longer under knowledge/${args.topic}.md${args.project === null ? " of the society" : ""}.`);
|
|
4031
|
+
return removed;
|
|
4032
|
+
});
|
|
4033
|
+
}
|
|
4034
|
+
/** Society knowledge is the steward's and the user's; a project's, its members' too. */
|
|
4035
|
+
async assertMayCurate(actor, project) {
|
|
4036
|
+
if (project === null) {
|
|
4037
|
+
if (!CURATING_ROLES.includes(actor.role)) {
|
|
4038
|
+
throw new BoardError("FORBIDDEN", "society knowledge is curated by the steward and the user");
|
|
4039
|
+
}
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
const active = await this.readActiveProject(project);
|
|
4043
|
+
if (!CURATING_ROLES.includes(actor.role) && !active.members.includes(actor.name)) {
|
|
4044
|
+
throw new BoardError("FORBIDDEN", `${actor.name} is not a member of ${project}`);
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
3968
4047
|
async openThread(actor, input) {
|
|
3969
4048
|
const args = VerbInputs.open_thread.parse(input);
|
|
3970
4049
|
await this.authorize(actor, "open_thread");
|
|
@@ -4634,18 +4713,28 @@ ${rationale}`}`);
|
|
|
4634
4713
|
}));
|
|
4635
4714
|
}
|
|
4636
4715
|
/** Sets a project's completion effect. */
|
|
4716
|
+
/**
|
|
4717
|
+
* Sets a project's completion effect, its display name, or both. The slug stays: addresses,
|
|
4718
|
+
* channel references, branches, and runners' directories all carry it.
|
|
4719
|
+
*/
|
|
4637
4720
|
async configureProject(actor, input) {
|
|
4638
4721
|
const args = VerbInputs.configure_project.parse(input);
|
|
4639
4722
|
await this.authorize(actor, "configure_project");
|
|
4723
|
+
if (args.on_done === void 0 && args.name === void 0) {
|
|
4724
|
+
throw new BoardError("VALIDATION", "configure_project needs on_done, name, or both");
|
|
4725
|
+
}
|
|
4640
4726
|
return this.mutex.run(async () => {
|
|
4641
|
-
await this.readActiveProject(args.project);
|
|
4727
|
+
const previous = await this.readActiveProject(args.project);
|
|
4642
4728
|
const project = await this.updateProject(args.project, (current) => ({
|
|
4643
4729
|
...current,
|
|
4644
|
-
onDone: args.on_done
|
|
4730
|
+
...args.on_done === void 0 ? {} : { onDone: args.on_done },
|
|
4731
|
+
...args.name === void 0 ? {} : { name: args.name }
|
|
4645
4732
|
}));
|
|
4646
4733
|
await this.events.append("project.configured", actor.name, {
|
|
4647
4734
|
slug: project.slug,
|
|
4648
|
-
onDone: project.onDone
|
|
4735
|
+
onDone: project.onDone,
|
|
4736
|
+
name: project.name,
|
|
4737
|
+
...project.name === previous.name ? {} : { previousName: previous.name }
|
|
4649
4738
|
});
|
|
4650
4739
|
return project;
|
|
4651
4740
|
});
|
|
@@ -4791,6 +4880,8 @@ ${rationale}`}`);
|
|
|
4791
4880
|
return this.leaveProject(actor, VerbInputs.leave_project.parse(input));
|
|
4792
4881
|
case "write_knowledge":
|
|
4793
4882
|
return this.writeKnowledge(actor, VerbInputs.write_knowledge.parse(input));
|
|
4883
|
+
case "remove_knowledge":
|
|
4884
|
+
return this.removeKnowledge(actor, VerbInputs.remove_knowledge.parse(input));
|
|
4794
4885
|
case "plan_task":
|
|
4795
4886
|
return this.planTask(actor, VerbInputs.plan_task.parse(input));
|
|
4796
4887
|
case "advance_task":
|
|
@@ -7601,7 +7692,7 @@ var GOVERNANCE = [
|
|
|
7601
7692
|
' - archive: {"project", "reason"}: approval archives the project: its members leave, its open threads close, and nothing more is posted, filed, or joined there, while its files and history stay; refused while a task there is in play',
|
|
7602
7693
|
' - reallocation: {"description"}',
|
|
7603
7694
|
` - skill: {"name", "summary", "body"}: the SKILL.md text; approval publishes it under society/skills, where every citizen's skills index lists it`,
|
|
7604
|
-
"- Projects and membership: a project is a lasting area of work, not one request; `create_project` opens one with its general channel (front desk and user), and `configure_project` sets its completion effect (user, steward, concierge). A project whose work is finished or has moved to another project is archived by proposal, once no task there is in play. `join_project` and `leave_project` move yourself, or another citizen when you are the concierge, the steward, or the user; joining gives the pair a worktree and an onboarding turn. A society role, one that keeps watch over the whole society as the concierge and the steward do, is never in a project: it moves others, never itself.",
|
|
7695
|
+
"- Projects and membership: a project is a lasting area of work, not one request; `create_project` opens one with its general channel (front desk and user), and `configure_project` sets its completion effect or its display name, never its slug (user, steward, concierge). A project whose work is finished or has moved to another project is archived by proposal, once no task there is in play. `join_project` and `leave_project` move yourself, or another citizen when you are the concierge, the steward, or the user; joining gives the pair a worktree and an onboarding turn. A society role, one that keeps watch over the whole society as the concierge and the steward do, is never in a project: it moves others, never itself.",
|
|
7605
7696
|
"- Prefer scaling an existing role over inventing one; a new role is justified by work a project needs that no existing role covers. A role that needs a tool the board lacks is an engineering task, not a hiring request.",
|
|
7606
7697
|
"- Operations signals are counters and timers the board logs, listed in the prompt of a role that reads them: stages waiting for a holder, backlog per role, stages waiting on a role nobody fills, churn, stale threads, idle members, missing capabilities, replicas added, spend. They are not posts in any channel; interpreting them is your judgment."
|
|
7607
7698
|
].join("\n");
|
|
@@ -8473,6 +8564,7 @@ var RunnerHub = class {
|
|
|
8473
8564
|
pending = /* @__PURE__ */ new Map();
|
|
8474
8565
|
/** Disconnects being recorded, which `close` waits for. */
|
|
8475
8566
|
detaching = /* @__PURE__ */ new Set();
|
|
8567
|
+
runnerListeners = /* @__PURE__ */ new Set();
|
|
8476
8568
|
constructor(options) {
|
|
8477
8569
|
this.board = options.board;
|
|
8478
8570
|
this.host = options.host;
|
|
@@ -8528,6 +8620,7 @@ var RunnerHub = class {
|
|
|
8528
8620
|
clis: hello.clis,
|
|
8529
8621
|
capabilities: hello.capabilities
|
|
8530
8622
|
});
|
|
8623
|
+
this.runnerChanged(name);
|
|
8531
8624
|
this.log.info({ runner: name, clis: hello.clis, slots: hello.slots }, "runner registered");
|
|
8532
8625
|
return { name, version: this.version };
|
|
8533
8626
|
}
|
|
@@ -8544,6 +8637,7 @@ var RunnerHub = class {
|
|
|
8544
8637
|
seat.grace = null;
|
|
8545
8638
|
}
|
|
8546
8639
|
await this.board.markRunner(name, { status: "connected" });
|
|
8640
|
+
this.runnerChanged(name);
|
|
8547
8641
|
return async () => {
|
|
8548
8642
|
if (seat.send !== send) {
|
|
8549
8643
|
return;
|
|
@@ -8816,16 +8910,37 @@ var RunnerHub = class {
|
|
|
8816
8910
|
return changes;
|
|
8817
8911
|
}
|
|
8818
8912
|
/** The models a CLI offers, asked of a connected runner that has it; none when no runner has it. */
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8913
|
+
/**
|
|
8914
|
+
* The connected runner a model list for `cli` comes from: the first of `prefer` that has the CLI,
|
|
8915
|
+
* else the least busy that does; null when no connected runner has it.
|
|
8916
|
+
*/
|
|
8917
|
+
modelRunner(cli, prefer = []) {
|
|
8918
|
+
const able = (seat) => seat !== void 0 && seat.send !== null && seat.hello.clis.includes(cli);
|
|
8919
|
+
const preferred = prefer.map((name) => this.seats.get(name)).find(able);
|
|
8920
|
+
return (preferred ?? this.byLoad().find(able))?.name ?? null;
|
|
8921
|
+
}
|
|
8922
|
+
/** The models `cli` lists on a runner, `modelRunner`'s choice when none is named. */
|
|
8923
|
+
async models(cli, runner) {
|
|
8924
|
+
const name = runner ?? this.modelRunner(cli);
|
|
8925
|
+
const seat = name === null ? void 0 : this.seats.get(name);
|
|
8926
|
+
if (seat === void 0 || seat.send === null || !seat.hello.clis.includes(cli)) {
|
|
8927
|
+
throw new RunnerAwayError(runner === void 0 ? `no connected runner has ${cli}` : `runner ${runner} is not connected with ${cli}`);
|
|
8823
8928
|
}
|
|
8824
8929
|
return ModelListSchema.parse(await this.request(seat, (request) => ({ type: "models", request, cli })));
|
|
8825
8930
|
}
|
|
8931
|
+
/** Calls `listener` whenever a runner registers, connects, or goes away. */
|
|
8932
|
+
onRunnerChange(listener) {
|
|
8933
|
+
this.runnerListeners.add(listener);
|
|
8934
|
+
}
|
|
8935
|
+
runnerChanged(runner) {
|
|
8936
|
+
for (const listener of this.runnerListeners) {
|
|
8937
|
+
listener(runner);
|
|
8938
|
+
}
|
|
8939
|
+
}
|
|
8826
8940
|
// -------------------------------------------------------------------------------------------
|
|
8827
8941
|
async detach(seat) {
|
|
8828
8942
|
seat.send = null;
|
|
8943
|
+
this.runnerChanged(seat.name);
|
|
8829
8944
|
seat.disconnectedAt = this.now().getTime();
|
|
8830
8945
|
for (const [id, pending] of this.pending) {
|
|
8831
8946
|
if (pending.runner === seat.name) {
|
|
@@ -9632,7 +9747,19 @@ function createApp(deps) {
|
|
|
9632
9747
|
});
|
|
9633
9748
|
api.get("/models/:cli", async (c) => {
|
|
9634
9749
|
const cli = CliKindSchema.parse(c.req.param("cli"));
|
|
9635
|
-
return c.json(models === void 0 ? [] : await models.list(cli).
|
|
9750
|
+
return c.json(models === void 0 ? [] : await models.list(cli).then((listed) => listed.models, () => []));
|
|
9751
|
+
});
|
|
9752
|
+
api.get("/agents/:name/models", async (c) => {
|
|
9753
|
+
const agent = await board2.readAgent(c.req.param("name"));
|
|
9754
|
+
if (agent.cli === null) {
|
|
9755
|
+
throw new BoardError("INVALID_STATE", `${agent.name} takes no turns, so it has no models`);
|
|
9756
|
+
}
|
|
9757
|
+
if (models === void 0) {
|
|
9758
|
+
throw new RunnerAwayError("this server asks no runner for models");
|
|
9759
|
+
}
|
|
9760
|
+
const placed = new Map((await board2.listProjects()).map((project) => [project.slug, project.runner]));
|
|
9761
|
+
const prefer = [agent.homeRunner, ...agent.memberships.map((slug) => placed.get(slug))].filter((runner) => runner !== void 0);
|
|
9762
|
+
return c.json(await models.list(agent.cli, prefer));
|
|
9636
9763
|
});
|
|
9637
9764
|
api.put("/roles/:name", async (c) => {
|
|
9638
9765
|
const charter = RoleCharterSchema.parse({
|
|
@@ -9915,26 +10042,42 @@ var EnrollmentDesk = class {
|
|
|
9915
10042
|
// apps/server/dist/models.js
|
|
9916
10043
|
var HOUR = 60 * 6e4;
|
|
9917
10044
|
var ModelCatalog = class {
|
|
9918
|
-
|
|
10045
|
+
runners;
|
|
9919
10046
|
ttlMs;
|
|
9920
10047
|
now;
|
|
9921
10048
|
kept = /* @__PURE__ */ new Map();
|
|
9922
|
-
constructor(
|
|
9923
|
-
this.
|
|
10049
|
+
constructor(runners2, ttlMs = HOUR, now = Date.now) {
|
|
10050
|
+
this.runners = runners2;
|
|
9924
10051
|
this.ttlMs = ttlMs;
|
|
9925
10052
|
this.now = now;
|
|
10053
|
+
runners2.onRunnerChange((runner) => this.forget(runner));
|
|
9926
10054
|
}
|
|
9927
|
-
list(cli) {
|
|
9928
|
-
const
|
|
10055
|
+
async list(cli, prefer = []) {
|
|
10056
|
+
const runner = this.runners.modelRunner(cli, prefer);
|
|
10057
|
+
if (runner === null) {
|
|
10058
|
+
throw new RunnerAwayError(`no connected runner has ${cli}`);
|
|
10059
|
+
}
|
|
10060
|
+
const key = `${runner}/${cli}`;
|
|
10061
|
+
const kept = this.kept.get(key);
|
|
9929
10062
|
if (kept !== void 0 && this.now() - kept.at < this.ttlMs) {
|
|
9930
|
-
return kept.models;
|
|
10063
|
+
return { runner, cli, models: await kept.models };
|
|
9931
10064
|
}
|
|
9932
|
-
const models = this.
|
|
9933
|
-
this.kept.set(
|
|
10065
|
+
const models = this.runners.models(cli, runner);
|
|
10066
|
+
this.kept.set(key, { at: this.now(), models });
|
|
9934
10067
|
models.catch(() => {
|
|
9935
|
-
this.kept.
|
|
10068
|
+
if (this.kept.get(key)?.models === models) {
|
|
10069
|
+
this.kept.delete(key);
|
|
10070
|
+
}
|
|
9936
10071
|
});
|
|
9937
|
-
return models;
|
|
10072
|
+
return { runner, cli, models: await models };
|
|
10073
|
+
}
|
|
10074
|
+
/** Drops every list a runner gave. */
|
|
10075
|
+
forget(runner) {
|
|
10076
|
+
for (const key of this.kept.keys()) {
|
|
10077
|
+
if (key.startsWith(`${runner}/`)) {
|
|
10078
|
+
this.kept.delete(key);
|
|
10079
|
+
}
|
|
10080
|
+
}
|
|
9938
10081
|
}
|
|
9939
10082
|
};
|
|
9940
10083
|
|
|
@@ -10042,7 +10185,7 @@ var app = createApp({
|
|
|
10042
10185
|
version: VERSION,
|
|
10043
10186
|
turns,
|
|
10044
10187
|
scheduler: view,
|
|
10045
|
-
models: new ModelCatalog(
|
|
10188
|
+
models: new ModelCatalog(runners),
|
|
10046
10189
|
runners,
|
|
10047
10190
|
webDir,
|
|
10048
10191
|
signIn: { github: config.github, log },
|