@ycodium-ai/plugin-team 0.2.3122 → 0.2.3130
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/assets/ui.js +30 -10
- package/dist/index.js +498 -252
- package/locales/en.json +62 -0
- package/locales/zh-CN.json +62 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { definePlugin } from "@ycodium-ai/plugin-api";
|
|
3
3
|
|
|
4
|
-
// src/board.ts
|
|
5
|
-
import * as NodeTimers from "node:timers";
|
|
6
|
-
|
|
7
4
|
// ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Pipeable.js
|
|
8
5
|
var pipeArguments = (self, args2) => {
|
|
9
6
|
switch (args2.length) {
|
|
@@ -3843,7 +3840,7 @@ function numberToJson(checks) {
|
|
|
3843
3840
|
return new Link(new Union([encodedFinite, nonFiniteLiterals], "anyOf"), new Transformation(Number3(), transform((n) => globalThis.Number.isFinite(n) ? n : globalThis.String(n))));
|
|
3844
3841
|
}
|
|
3845
3842
|
var number2 = /* @__PURE__ */ new Number4();
|
|
3846
|
-
var
|
|
3843
|
+
var Boolean2 = class extends Base2 {
|
|
3847
3844
|
_tag = "Boolean";
|
|
3848
3845
|
/** @internal */
|
|
3849
3846
|
getParser() {
|
|
@@ -3854,7 +3851,7 @@ var Boolean = class extends Base2 {
|
|
|
3854
3851
|
return "boolean";
|
|
3855
3852
|
}
|
|
3856
3853
|
};
|
|
3857
|
-
var boolean = /* @__PURE__ */ new
|
|
3854
|
+
var boolean = /* @__PURE__ */ new Boolean2();
|
|
3858
3855
|
var Arrays = class _Arrays extends Base2 {
|
|
3859
3856
|
_tag = "Arrays";
|
|
3860
3857
|
isMutable;
|
|
@@ -6265,7 +6262,7 @@ var Null2 = /* @__PURE__ */ make8(null_);
|
|
|
6265
6262
|
var Undefined2 = /* @__PURE__ */ make8(undefined_2);
|
|
6266
6263
|
var String4 = /* @__PURE__ */ make8(string2);
|
|
6267
6264
|
var Number5 = /* @__PURE__ */ make8(number2);
|
|
6268
|
-
var
|
|
6265
|
+
var Boolean3 = /* @__PURE__ */ make8(boolean);
|
|
6269
6266
|
function makeStruct(ast, fields) {
|
|
6270
6267
|
return make8(ast, {
|
|
6271
6268
|
fields,
|
|
@@ -7045,6 +7042,68 @@ var en_default = {
|
|
|
7045
7042
|
remoteAgentsUnavailable: "Could not read the remote agents on this machine. Try again later.",
|
|
7046
7043
|
remoteSource: "Remote agent: {{label}}"
|
|
7047
7044
|
},
|
|
7045
|
+
handbookPanel: {
|
|
7046
|
+
title: "Handbook",
|
|
7047
|
+
open: "View handbook",
|
|
7048
|
+
close: "Close handbook",
|
|
7049
|
+
current: "Current version {{version}}",
|
|
7050
|
+
required: "Version {{version}} currently requires acknowledgement",
|
|
7051
|
+
noCurrent: "No handbook has been published",
|
|
7052
|
+
noRequired: "No version has required acknowledgement",
|
|
7053
|
+
handbook_decisions: "Decisions",
|
|
7054
|
+
handbook_rules: "Rules",
|
|
7055
|
+
handbook_progress: "Progress",
|
|
7056
|
+
missingPage: "This page is absent from the current version",
|
|
7057
|
+
erased: "Erased",
|
|
7058
|
+
unavailable: "Content is unavailable",
|
|
7059
|
+
uneditable: "This page cannot be edited because its content is erased or unavailable",
|
|
7060
|
+
loading: "Loading handbook\u2026",
|
|
7061
|
+
loadFailed: "Could not read the handbook. Reload to try again.",
|
|
7062
|
+
reload: "Reload handbook",
|
|
7063
|
+
moreBody: "Read more",
|
|
7064
|
+
moreBodyFailed: "Couldn't load the rest of this page. Click Read more to try again.",
|
|
7065
|
+
releases: "Version history",
|
|
7066
|
+
noReleases: "No versions yet",
|
|
7067
|
+
version: "Version {{version}}",
|
|
7068
|
+
publishedBy: "Published by {{author}}",
|
|
7069
|
+
requiresAck: "Acknowledgement required",
|
|
7070
|
+
noAck: "Acknowledgement not required",
|
|
7071
|
+
restoredFrom: "Restored from version {{version}}",
|
|
7072
|
+
moreReleases: "Load older versions",
|
|
7073
|
+
members: "Member acknowledgements",
|
|
7074
|
+
noMembers: "No executing members yet. Start a goal to assign team seats.",
|
|
7075
|
+
member: "Member {{id}}",
|
|
7076
|
+
neverAck: "Has not acknowledged a version",
|
|
7077
|
+
acked: "Acknowledged version {{version}}",
|
|
7078
|
+
pendingAck: "Needs to acknowledge version {{version}}",
|
|
7079
|
+
edit: "Edit handbook",
|
|
7080
|
+
pageTitle: "{{page}} \u2014 title (optional)",
|
|
7081
|
+
pageBody: "{{page}} \u2014 body",
|
|
7082
|
+
pageSummary: "{{page}} \u2014 change summary (optional)",
|
|
7083
|
+
ackChoice: "Require acknowledgement for changes to Decisions",
|
|
7084
|
+
ackPolicy: "Rules changes always require acknowledgement; Progress-only changes don't; Decisions changes follow this choice.",
|
|
7085
|
+
cancel: "Cancel",
|
|
7086
|
+
publish: "Save and publish",
|
|
7087
|
+
saved: "Handbook published",
|
|
7088
|
+
conflict: "Someone else changed the handbook. Your edits are kept. Review the latest version shown below before saving again.",
|
|
7089
|
+
failed: "Publishing did not finish. Click Save and publish again; as long as you don't change the edits, it counts as the same publication and won't publish twice.",
|
|
7090
|
+
revert: "Revert to this version",
|
|
7091
|
+
revertVersion: "Revert to version {{version}}",
|
|
7092
|
+
revertTitle: "Revert handbook to version {{version}}?",
|
|
7093
|
+
revertDescription: "Publish the content of version {{version}} as a new version, under your name. The current version remains in history.",
|
|
7094
|
+
confirmRevert: "Revert and publish",
|
|
7095
|
+
historyFailed: "Couldn't load the version history. Try again.",
|
|
7096
|
+
emptyPages: "Once the handbook is published, its three pages show here.",
|
|
7097
|
+
fieldTitle: "Title (optional)",
|
|
7098
|
+
fieldBody: "Body",
|
|
7099
|
+
fieldSummary: "Change summary (optional)",
|
|
7100
|
+
currentBadge: "Current",
|
|
7101
|
+
currentReadOnly: "Current handbook (read-only)",
|
|
7102
|
+
discardTitle: "Discard your unsaved edits?",
|
|
7103
|
+
discardDescription: "Your edits to the handbook pages will be lost.",
|
|
7104
|
+
keepEditing: "Keep editing",
|
|
7105
|
+
discard: "Discard edits"
|
|
7106
|
+
},
|
|
7048
7107
|
remoteStatus: {
|
|
7049
7108
|
title: "Remote members",
|
|
7050
7109
|
none: "No remote members on the roster. Add one with \u201CAdd remote agent\u201D in the roster.",
|
|
@@ -7245,8 +7304,8 @@ function createGoalService(ctx, rosterService, options = {}) {
|
|
|
7245
7304
|
});
|
|
7246
7305
|
await goals.get({ throughSequence: receipt.sequence });
|
|
7247
7306
|
goal = readFact(await requests.get({ throughSequence: receipt.sequence }), key) ?? goal;
|
|
7248
|
-
const
|
|
7249
|
-
const selection = await ctx.providers.resolveSelection({ base:
|
|
7307
|
+
const project2 = await ctx.projects.get({ projectId: goal.projectId });
|
|
7308
|
+
const selection = await ctx.providers.resolveSelection({ base: project2.defaultModelSelection });
|
|
7250
7309
|
const orderedThreads = [
|
|
7251
7310
|
...goal.threads.filter((thread) => thread.memberId === goal.leadMemberId),
|
|
7252
7311
|
...goal.threads.filter((thread) => thread.memberId !== goal.leadMemberId)
|
|
@@ -7308,16 +7367,32 @@ function createGoalService(ctx, rosterService, options = {}) {
|
|
|
7308
7367
|
// src/commands.ts
|
|
7309
7368
|
var decodeReadRoster = decodeUnknownSync(ReadRosterInput);
|
|
7310
7369
|
var decodeReadGoal = decodeUnknownSync(ReadGoalInput);
|
|
7311
|
-
var
|
|
7312
|
-
if (Array.isArray(node)) return node.map(
|
|
7370
|
+
var commandSchema = (node) => {
|
|
7371
|
+
if (Array.isArray(node)) return node.map(commandSchema);
|
|
7313
7372
|
if (typeof node !== "object" || node === null) return node;
|
|
7314
7373
|
const { allOf, ...rest } = node;
|
|
7315
7374
|
const merged = Object.fromEntries(
|
|
7316
|
-
Object.entries(rest).map(([key, value]) => [key,
|
|
7375
|
+
Object.entries(rest).map(([key, value]) => [key, commandSchema(value)])
|
|
7317
7376
|
);
|
|
7318
|
-
|
|
7377
|
+
if (Array.isArray(allOf)) Object.assign(merged, ...allOf.map(commandSchema));
|
|
7378
|
+
if (!Array.isArray(merged.anyOf) || merged.anyOf.length !== 2) return merged;
|
|
7379
|
+
const branches = merged.anyOf.filter(
|
|
7380
|
+
(branch) => typeof branch === "object" && branch !== null
|
|
7381
|
+
);
|
|
7382
|
+
if (branches.length !== 2) return merged;
|
|
7383
|
+
const base = branches.find((branch) => branch.type !== "null");
|
|
7384
|
+
if (!branches.some((branch) => branch.type === "null") || base === void 0 || typeof base.type !== "string" || !["string", "number", "integer", "boolean"].includes(base.type))
|
|
7385
|
+
return merged;
|
|
7386
|
+
const { anyOf: _, ...common } = merged;
|
|
7387
|
+
const { const: literal, ...constraints } = base;
|
|
7388
|
+
return {
|
|
7389
|
+
...common,
|
|
7390
|
+
...constraints,
|
|
7391
|
+
type: [base.type, "null"],
|
|
7392
|
+
...Object.hasOwn(base, "const") ? { enum: [literal, null] } : Array.isArray(base.enum) ? { enum: [...base.enum, null] } : {}
|
|
7393
|
+
};
|
|
7319
7394
|
};
|
|
7320
|
-
var payloadSchemaOf = (schema) =>
|
|
7395
|
+
var payloadSchemaOf = (schema) => commandSchema(toJsonSchemaDocument2(schema).schema);
|
|
7321
7396
|
function registerRosterCommands(ctx, service2) {
|
|
7322
7397
|
ctx.command({
|
|
7323
7398
|
name: "roster",
|
|
@@ -7347,57 +7422,8 @@ function registerGoalCommands(ctx, service2) {
|
|
|
7347
7422
|
});
|
|
7348
7423
|
}
|
|
7349
7424
|
|
|
7350
|
-
// src/
|
|
7351
|
-
|
|
7352
|
-
async function readPages(maximum, read, keep = "oldest") {
|
|
7353
|
-
let items = [];
|
|
7354
|
-
let truncated = false;
|
|
7355
|
-
let cursor = null;
|
|
7356
|
-
do {
|
|
7357
|
-
const page = await read({ limit: PAGE_SIZE, ...cursor === null ? {} : { cursor } });
|
|
7358
|
-
items.push(...page.items);
|
|
7359
|
-
cursor = page.nextCursor;
|
|
7360
|
-
if (keep === "oldest" && items.length >= maximum) {
|
|
7361
|
-
return {
|
|
7362
|
-
items: items.slice(0, maximum),
|
|
7363
|
-
truncated: items.length > maximum || cursor !== null
|
|
7364
|
-
};
|
|
7365
|
-
}
|
|
7366
|
-
if (items.length > maximum) {
|
|
7367
|
-
truncated = true;
|
|
7368
|
-
items = items.slice(-maximum);
|
|
7369
|
-
}
|
|
7370
|
-
} while (cursor !== null);
|
|
7371
|
-
return { items, truncated };
|
|
7372
|
-
}
|
|
7373
|
-
|
|
7374
|
-
// src/workWrites.ts
|
|
7375
|
-
var decodeWorkRefusal = decodeUnknownOption2(
|
|
7376
|
-
Struct({
|
|
7377
|
-
rejection: Struct({
|
|
7378
|
-
code: String4,
|
|
7379
|
-
details: optionalKey2(Unknown2)
|
|
7380
|
-
})
|
|
7381
|
-
})
|
|
7382
|
-
);
|
|
7383
|
-
var rejectWork = (commandType, code) => ({ _tag: "TeamBoardRejection", commandType, code });
|
|
7384
|
-
async function writeWork(invocation, commandType, write) {
|
|
7385
|
-
if (invocation.asPerson === void 0) return rejectWork(commandType, "needs-person");
|
|
7386
|
-
try {
|
|
7387
|
-
return await write(invocation.asPerson.work);
|
|
7388
|
-
} catch (error) {
|
|
7389
|
-
const refusal = decodeWorkRefusal(error);
|
|
7390
|
-
if (isNone2(refusal)) throw error;
|
|
7391
|
-
const { code, details } = refusal.value.rejection;
|
|
7392
|
-
return {
|
|
7393
|
-
...rejectWork(
|
|
7394
|
-
commandType,
|
|
7395
|
-
commandType !== "postComment" && code === "version-conflict" ? "stale-version" : code
|
|
7396
|
-
),
|
|
7397
|
-
...details === void 0 ? {} : { details }
|
|
7398
|
-
};
|
|
7399
|
-
}
|
|
7400
|
-
}
|
|
7425
|
+
// src/watch.ts
|
|
7426
|
+
import * as NodeTimers from "node:timers";
|
|
7401
7427
|
|
|
7402
7428
|
// src/boardContracts.ts
|
|
7403
7429
|
var BoardState = Struct({
|
|
@@ -7435,7 +7461,7 @@ var BoardRemoteWork = Struct({
|
|
|
7435
7461
|
pending: NullOr(
|
|
7436
7462
|
Struct({
|
|
7437
7463
|
count: Number5,
|
|
7438
|
-
items: ArraySchema(Struct({ workItemId: String4, blocked:
|
|
7464
|
+
items: ArraySchema(Struct({ workItemId: String4, blocked: Boolean3 }))
|
|
7439
7465
|
})
|
|
7440
7466
|
)
|
|
7441
7467
|
});
|
|
@@ -7461,8 +7487,8 @@ var BoardSnapshot = Struct({
|
|
|
7461
7487
|
remoteWork: ArraySchema(BoardRemoteWork),
|
|
7462
7488
|
viewerKey: optionalKey2(String4),
|
|
7463
7489
|
// 负责人要用看板与项目记忆两组工具;开关由宿主控件拨,这里只读来提示。
|
|
7464
|
-
toolkits: Struct({ board:
|
|
7465
|
-
truncated:
|
|
7490
|
+
toolkits: Struct({ board: Boolean3, memory: Boolean3 }),
|
|
7491
|
+
truncated: Boolean3
|
|
7466
7492
|
});
|
|
7467
7493
|
var ReadBoardInput = Struct({ projectId: String4 });
|
|
7468
7494
|
var CreateItemInput = Struct({
|
|
@@ -7478,7 +7504,7 @@ var UpdateItemInput = Struct({
|
|
|
7478
7504
|
expectedVersion: Int.check(isGreaterThanOrEqualTo3(1)),
|
|
7479
7505
|
stateId: optionalKey2(String4),
|
|
7480
7506
|
assigneeThreadId: optionalKey2(String4),
|
|
7481
|
-
unassign: optionalKey2(
|
|
7507
|
+
unassign: optionalKey2(Boolean3)
|
|
7482
7508
|
});
|
|
7483
7509
|
var BoardRejection = Struct({
|
|
7484
7510
|
_tag: Literal2("TeamBoardRejection"),
|
|
@@ -7495,18 +7521,14 @@ var UpdateItemResult = Union2([
|
|
|
7495
7521
|
BoardRejection
|
|
7496
7522
|
]);
|
|
7497
7523
|
|
|
7498
|
-
// src/
|
|
7499
|
-
var
|
|
7500
|
-
var PEOPLE_LIMIT = 200;
|
|
7501
|
-
var REMOTE_AGENT_LIMIT = 1e3;
|
|
7524
|
+
// src/watch.ts
|
|
7525
|
+
var emptyChanges = () => ({ cursor: 0, handbookCursor: 0 });
|
|
7502
7526
|
var decodeRead = decodeUnknownSync(ReadBoardInput);
|
|
7503
|
-
var decodeCreate = decodeUnknownOption2(CreateItemInput);
|
|
7504
|
-
var decodeUpdate = decodeUnknownOption2(UpdateItemInput);
|
|
7505
7527
|
var decodeProject = decodeUnknownOption2(Struct({ projectId: String4 }));
|
|
7506
7528
|
var decodeFact = decodeUnknownOption2(
|
|
7507
7529
|
Struct({ pluginId: Literal2("team"), name: String4 })
|
|
7508
7530
|
);
|
|
7509
|
-
var WORK_EVENTS = [
|
|
7531
|
+
var WORK_EVENTS = /* @__PURE__ */ new Set([
|
|
7510
7532
|
"work.item-created",
|
|
7511
7533
|
"work.item-updated",
|
|
7512
7534
|
"work.item-claimed",
|
|
@@ -7514,15 +7536,159 @@ var WORK_EVENTS = [
|
|
|
7514
7536
|
"work.relation-removed",
|
|
7515
7537
|
"work.comment-posted",
|
|
7516
7538
|
"work.states-seeded",
|
|
7517
|
-
// 快照带看板与记忆两只开关的状态,拨一下就敲门。
|
|
7518
7539
|
"board.toolkit-set",
|
|
7519
7540
|
"memory.toolkit-set"
|
|
7520
|
-
];
|
|
7521
|
-
|
|
7541
|
+
]);
|
|
7542
|
+
var HANDBOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
7543
|
+
"wiki.handbook-released",
|
|
7544
|
+
"wiki.page-erased",
|
|
7545
|
+
"memory.toolkit-set"
|
|
7546
|
+
]);
|
|
7547
|
+
function registerTeamWatch(ctx, options) {
|
|
7522
7548
|
const after = options.after ?? ((delay2, notify) => {
|
|
7523
7549
|
const timer = NodeTimers.setTimeout(notify, delay2);
|
|
7524
7550
|
return () => NodeTimers.clearTimeout(timer);
|
|
7525
7551
|
});
|
|
7552
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
7553
|
+
const cursors = /* @__PURE__ */ new Map();
|
|
7554
|
+
const timers = /* @__PURE__ */ new Map();
|
|
7555
|
+
let disposed = false;
|
|
7556
|
+
const changed = (event, board, handbook) => {
|
|
7557
|
+
const payload = decodeProject(event.payload);
|
|
7558
|
+
const projectId = isSome2(payload) ? payload.value.projectId : event.aggregateKind === "project" ? event.aggregateId : null;
|
|
7559
|
+
if (projectId === null || disposed || !listeners.has(projectId)) return;
|
|
7560
|
+
const previous = cursors.get(projectId) ?? emptyChanges();
|
|
7561
|
+
cursors.set(projectId, {
|
|
7562
|
+
cursor: board ? Math.max(previous.cursor, event.sequence) : previous.cursor,
|
|
7563
|
+
handbookCursor: handbook ? Math.max(previous.handbookCursor, event.sequence) : previous.handbookCursor
|
|
7564
|
+
});
|
|
7565
|
+
if (timers.has(projectId)) return;
|
|
7566
|
+
timers.set(
|
|
7567
|
+
projectId,
|
|
7568
|
+
after(50, () => {
|
|
7569
|
+
timers.delete(projectId);
|
|
7570
|
+
for (const emit of [...listeners.get(projectId) ?? []])
|
|
7571
|
+
emit(cursors.get(projectId) ?? emptyChanges());
|
|
7572
|
+
})
|
|
7573
|
+
);
|
|
7574
|
+
};
|
|
7575
|
+
const stops = [.../* @__PURE__ */ new Set([...WORK_EVENTS, ...HANDBOOK_EVENTS])].map(
|
|
7576
|
+
(type) => ctx.events.onRaw(
|
|
7577
|
+
type,
|
|
7578
|
+
(event) => changed(event, WORK_EVENTS.has(type), HANDBOOK_EVENTS.has(type))
|
|
7579
|
+
)
|
|
7580
|
+
);
|
|
7581
|
+
stops.push(
|
|
7582
|
+
ctx.events.onRaw("plugin.event-appended", (event) => {
|
|
7583
|
+
const fact = decodeFact(event.payload);
|
|
7584
|
+
if (isNone2(fact)) return;
|
|
7585
|
+
const name = fact.value.name;
|
|
7586
|
+
const board = name === "team/goal-started" || name === "team/roster-set";
|
|
7587
|
+
const handbook = name === "team/goal-started" || name === "team/handbook-acknowledged" || name === "team/handbook-publication-completed";
|
|
7588
|
+
if (board || handbook) changed(event, board, handbook);
|
|
7589
|
+
})
|
|
7590
|
+
);
|
|
7591
|
+
ctx.stream({
|
|
7592
|
+
name: "watch",
|
|
7593
|
+
scope: "read",
|
|
7594
|
+
payloadSchema: payloadSchemaOf(ReadBoardInput),
|
|
7595
|
+
buffer: { latest: 1 },
|
|
7596
|
+
handle: async (payload, emit, signal) => {
|
|
7597
|
+
if (signal.aborted || disposed) return;
|
|
7598
|
+
const { projectId } = decodeRead(payload);
|
|
7599
|
+
const subscribers = listeners.get(projectId) ?? /* @__PURE__ */ new Set();
|
|
7600
|
+
const ring = (value) => {
|
|
7601
|
+
if (!signal.aborted && !disposed) emit(value);
|
|
7602
|
+
};
|
|
7603
|
+
listeners.set(projectId, subscribers);
|
|
7604
|
+
subscribers.add(ring);
|
|
7605
|
+
try {
|
|
7606
|
+
ring(cursors.get(projectId) ?? emptyChanges());
|
|
7607
|
+
await new Promise((resolve2) => {
|
|
7608
|
+
if (signal.aborted) resolve2();
|
|
7609
|
+
else signal.addEventListener("abort", () => resolve2(), { once: true });
|
|
7610
|
+
});
|
|
7611
|
+
} finally {
|
|
7612
|
+
subscribers.delete(ring);
|
|
7613
|
+
if (subscribers.size === 0) {
|
|
7614
|
+
listeners.delete(projectId);
|
|
7615
|
+
timers.get(projectId)?.();
|
|
7616
|
+
timers.delete(projectId);
|
|
7617
|
+
cursors.delete(projectId);
|
|
7618
|
+
}
|
|
7619
|
+
}
|
|
7620
|
+
}
|
|
7621
|
+
});
|
|
7622
|
+
ctx.onDispose(() => {
|
|
7623
|
+
disposed = true;
|
|
7624
|
+
for (const stop of stops) stop();
|
|
7625
|
+
for (const cancel of timers.values()) cancel();
|
|
7626
|
+
timers.clear();
|
|
7627
|
+
listeners.clear();
|
|
7628
|
+
cursors.clear();
|
|
7629
|
+
});
|
|
7630
|
+
}
|
|
7631
|
+
|
|
7632
|
+
// src/workReads.ts
|
|
7633
|
+
var PAGE_SIZE = 200;
|
|
7634
|
+
async function readPages(maximum, read, keep = "oldest") {
|
|
7635
|
+
let items = [];
|
|
7636
|
+
let truncated = false;
|
|
7637
|
+
let cursor = null;
|
|
7638
|
+
do {
|
|
7639
|
+
const page = await read({ limit: PAGE_SIZE, ...cursor === null ? {} : { cursor } });
|
|
7640
|
+
items.push(...page.items);
|
|
7641
|
+
cursor = page.nextCursor;
|
|
7642
|
+
if (keep === "oldest" && items.length >= maximum) {
|
|
7643
|
+
return {
|
|
7644
|
+
items: items.slice(0, maximum),
|
|
7645
|
+
truncated: items.length > maximum || cursor !== null
|
|
7646
|
+
};
|
|
7647
|
+
}
|
|
7648
|
+
if (items.length > maximum) {
|
|
7649
|
+
truncated = true;
|
|
7650
|
+
items = items.slice(-maximum);
|
|
7651
|
+
}
|
|
7652
|
+
} while (cursor !== null);
|
|
7653
|
+
return { items, truncated };
|
|
7654
|
+
}
|
|
7655
|
+
|
|
7656
|
+
// src/workWrites.ts
|
|
7657
|
+
var decodeHostRefusal = decodeUnknownOption2(
|
|
7658
|
+
Struct({
|
|
7659
|
+
rejection: Struct({
|
|
7660
|
+
code: String4,
|
|
7661
|
+
details: optionalKey2(Unknown2)
|
|
7662
|
+
})
|
|
7663
|
+
})
|
|
7664
|
+
);
|
|
7665
|
+
var rejectWork = (commandType, code) => ({ _tag: "TeamBoardRejection", commandType, code });
|
|
7666
|
+
async function writeWork(invocation, commandType, write) {
|
|
7667
|
+
if (invocation.asPerson === void 0) return rejectWork(commandType, "needs-person");
|
|
7668
|
+
try {
|
|
7669
|
+
return await write(invocation.asPerson.work);
|
|
7670
|
+
} catch (error) {
|
|
7671
|
+
const refusal = decodeHostRefusal(error);
|
|
7672
|
+
if (isNone2(refusal)) throw error;
|
|
7673
|
+
const { code, details } = refusal.value.rejection;
|
|
7674
|
+
return {
|
|
7675
|
+
...rejectWork(
|
|
7676
|
+
commandType,
|
|
7677
|
+
commandType !== "postComment" && code === "version-conflict" ? "stale-version" : code
|
|
7678
|
+
),
|
|
7679
|
+
...details === void 0 ? {} : { details }
|
|
7680
|
+
};
|
|
7681
|
+
}
|
|
7682
|
+
}
|
|
7683
|
+
|
|
7684
|
+
// src/board.ts
|
|
7685
|
+
var ITEM_LIMIT = 500;
|
|
7686
|
+
var PEOPLE_LIMIT = 200;
|
|
7687
|
+
var REMOTE_AGENT_LIMIT = 1e3;
|
|
7688
|
+
var decodeRead2 = decodeUnknownSync(ReadBoardInput);
|
|
7689
|
+
var decodeCreate = decodeUnknownOption2(CreateItemInput);
|
|
7690
|
+
var decodeUpdate = decodeUnknownOption2(UpdateItemInput);
|
|
7691
|
+
function registerBoardCommands(ctx, goals, rosters, options = {}) {
|
|
7526
7692
|
const isMember2 = async (projectId, threadId) => goalExecution(await goals.read(projectId)).members.some(
|
|
7527
7693
|
(member) => "threadId" in member && member.threadId === threadId
|
|
7528
7694
|
);
|
|
@@ -7531,7 +7697,7 @@ function registerBoardCommands(ctx, goals, rosters, options = {}) {
|
|
|
7531
7697
|
scope: "read",
|
|
7532
7698
|
payloadSchema: payloadSchemaOf(ReadBoardInput),
|
|
7533
7699
|
handle: async (payload, invocation) => {
|
|
7534
|
-
const { projectId } =
|
|
7700
|
+
const { projectId } = decodeRead2(payload);
|
|
7535
7701
|
const [stateList, goal, roster, toolkitStates, people, remoteAgents, viewer] = await Promise.all([
|
|
7536
7702
|
ctx.work.states.list({ projectId }),
|
|
7537
7703
|
goals.read(projectId),
|
|
@@ -7656,74 +7822,7 @@ function registerBoardCommands(ctx, goals, rosters, options = {}) {
|
|
|
7656
7822
|
return "code" in result2 ? result2 : { workItemId: result2.result.workItemId, version: result2.result.version };
|
|
7657
7823
|
}
|
|
7658
7824
|
});
|
|
7659
|
-
|
|
7660
|
-
const cursors = /* @__PURE__ */ new Map();
|
|
7661
|
-
const timers = /* @__PURE__ */ new Map();
|
|
7662
|
-
let disposed = false;
|
|
7663
|
-
const changed = (event) => {
|
|
7664
|
-
const payload = decodeProject(event.payload);
|
|
7665
|
-
const projectId = isSome2(payload) ? payload.value.projectId : event.aggregateKind === "project" ? event.aggregateId : null;
|
|
7666
|
-
if (projectId === null || disposed || !listeners.has(projectId)) return;
|
|
7667
|
-
cursors.set(projectId, Math.max(cursors.get(projectId) ?? 0, event.sequence));
|
|
7668
|
-
if (timers.has(projectId)) return;
|
|
7669
|
-
timers.set(
|
|
7670
|
-
projectId,
|
|
7671
|
-
after(50, () => {
|
|
7672
|
-
timers.delete(projectId);
|
|
7673
|
-
for (const emit of [...listeners.get(projectId) ?? []]) {
|
|
7674
|
-
emit({ cursor: cursors.get(projectId) ?? 0 });
|
|
7675
|
-
}
|
|
7676
|
-
})
|
|
7677
|
-
);
|
|
7678
|
-
};
|
|
7679
|
-
const stops = WORK_EVENTS.map((type) => ctx.events.onRaw(type, changed));
|
|
7680
|
-
stops.push(
|
|
7681
|
-
ctx.events.onRaw("plugin.event-appended", (event) => {
|
|
7682
|
-
const fact = decodeFact(event.payload);
|
|
7683
|
-
if (isSome2(fact) && ["team/goal-started", "team/roster-set"].includes(fact.value.name)) {
|
|
7684
|
-
changed(event);
|
|
7685
|
-
}
|
|
7686
|
-
})
|
|
7687
|
-
);
|
|
7688
|
-
ctx.stream({
|
|
7689
|
-
name: "watch",
|
|
7690
|
-
scope: "read",
|
|
7691
|
-
payloadSchema: payloadSchemaOf(ReadBoardInput),
|
|
7692
|
-
buffer: { latest: 1 },
|
|
7693
|
-
handle: async (payload, emit, signal) => {
|
|
7694
|
-
if (signal.aborted || disposed) return;
|
|
7695
|
-
const { projectId } = decodeRead(payload);
|
|
7696
|
-
const subscribers = listeners.get(projectId) ?? /* @__PURE__ */ new Set();
|
|
7697
|
-
const ring = (value) => {
|
|
7698
|
-
if (!signal.aborted && !disposed) emit(value);
|
|
7699
|
-
};
|
|
7700
|
-
listeners.set(projectId, subscribers);
|
|
7701
|
-
subscribers.add(ring);
|
|
7702
|
-
try {
|
|
7703
|
-
ring({ cursor: cursors.get(projectId) ?? 0 });
|
|
7704
|
-
await new Promise((resolve2) => {
|
|
7705
|
-
if (signal.aborted) resolve2();
|
|
7706
|
-
else signal.addEventListener("abort", () => resolve2(), { once: true });
|
|
7707
|
-
});
|
|
7708
|
-
} finally {
|
|
7709
|
-
subscribers.delete(ring);
|
|
7710
|
-
if (subscribers.size === 0) {
|
|
7711
|
-
listeners.delete(projectId);
|
|
7712
|
-
timers.get(projectId)?.();
|
|
7713
|
-
timers.delete(projectId);
|
|
7714
|
-
cursors.delete(projectId);
|
|
7715
|
-
}
|
|
7716
|
-
}
|
|
7717
|
-
}
|
|
7718
|
-
});
|
|
7719
|
-
ctx.onDispose(() => {
|
|
7720
|
-
disposed = true;
|
|
7721
|
-
for (const stop of stops) stop();
|
|
7722
|
-
for (const cancel of timers.values()) cancel();
|
|
7723
|
-
timers.clear();
|
|
7724
|
-
listeners.clear();
|
|
7725
|
-
cursors.clear();
|
|
7726
|
-
});
|
|
7825
|
+
registerTeamWatch(ctx, options);
|
|
7727
7826
|
}
|
|
7728
7827
|
|
|
7729
7828
|
// src/detailContracts.ts
|
|
@@ -7785,9 +7884,9 @@ var ItemDetailSnapshot = Struct({
|
|
|
7785
7884
|
blockedBy: ArraySchema(Dependency),
|
|
7786
7885
|
blocking: ArraySchema(Dependency),
|
|
7787
7886
|
comments: ArraySchema(DetailComment),
|
|
7788
|
-
commentsTruncated:
|
|
7887
|
+
commentsTruncated: Boolean3,
|
|
7789
7888
|
history: ArraySchema(DetailActivity),
|
|
7790
|
-
historyTruncated:
|
|
7889
|
+
historyTruncated: Boolean3
|
|
7791
7890
|
});
|
|
7792
7891
|
var ReadItemDetailResult = NullOr(ItemDetailSnapshot);
|
|
7793
7892
|
|
|
@@ -7796,7 +7895,7 @@ var RELATION_LIMIT = 100;
|
|
|
7796
7895
|
var COMMENT_LIMIT = 200;
|
|
7797
7896
|
var HISTORY_LIMIT = 200;
|
|
7798
7897
|
var GET_BATCH_SIZE = 50;
|
|
7799
|
-
var
|
|
7898
|
+
var decodeRead3 = decodeUnknownSync(ReadItemDetailInput);
|
|
7800
7899
|
var decodePost = decodeUnknownOption2(PostCommentInput);
|
|
7801
7900
|
function registerDetailCommands(ctx) {
|
|
7802
7901
|
ctx.command({
|
|
@@ -7804,7 +7903,7 @@ function registerDetailCommands(ctx) {
|
|
|
7804
7903
|
scope: "read",
|
|
7805
7904
|
payloadSchema: payloadSchemaOf(ReadItemDetailInput),
|
|
7806
7905
|
handle: async (payload) => {
|
|
7807
|
-
const { projectId, workItemId } =
|
|
7906
|
+
const { projectId, workItemId } = decodeRead3(payload);
|
|
7808
7907
|
const result2 = await ctx.work.items.get({ projectId, workItemIds: [workItemId] });
|
|
7809
7908
|
const item = result2.items.find((row) => row.workItemId === workItemId);
|
|
7810
7909
|
if (item === void 0) return null;
|
|
@@ -8045,14 +8144,14 @@ var emptyProject = () => ({
|
|
|
8045
8144
|
});
|
|
8046
8145
|
var itemWakeKey = (workItemId, threadId, generation) => generation === 0 ? `item:${workItemId}:${threadId}` : `item:${workItemId}:${threadId}#${generation}`;
|
|
8047
8146
|
var unblockedWakeKey = (workItemId, threadId, generation) => `unblocked:${workItemId}:${threadId}#${generation}`;
|
|
8048
|
-
var hasOpenBlocker = (
|
|
8049
|
-
(blocker) => readFact(
|
|
8147
|
+
var hasOpenBlocker = (project2, workItemId) => (readFact(project2.blockers, workItemId) ?? []).some(
|
|
8148
|
+
(blocker) => readFact(project2.items, blocker)?.closed !== true
|
|
8050
8149
|
);
|
|
8051
|
-
var markBlockedAfterClaim = (
|
|
8052
|
-
...
|
|
8150
|
+
var markBlockedAfterClaim = (project2, workItemId) => ({
|
|
8151
|
+
...project2,
|
|
8053
8152
|
blockedAfterClaim: {
|
|
8054
|
-
...
|
|
8055
|
-
[workItemId]: (readFact(
|
|
8153
|
+
...project2.blockedAfterClaim,
|
|
8154
|
+
[workItemId]: (readFact(project2.blockedAfterClaim, workItemId) ?? 0) + 1
|
|
8056
8155
|
}
|
|
8057
8156
|
});
|
|
8058
8157
|
var decodeEnvelope2 = decodeUnknownOption2(
|
|
@@ -8137,8 +8236,8 @@ var decodeWorkEvent = decodeUnknownOption2(
|
|
|
8137
8236
|
var agentThreads = (principals) => [
|
|
8138
8237
|
...new Set(principals.flatMap((p) => p.kind === "agent" ? [p.threadId] : []))
|
|
8139
8238
|
];
|
|
8140
|
-
var isMember = (
|
|
8141
|
-
var commentRecipients = (
|
|
8239
|
+
var isMember = (project2, recipient) => "threadId" in recipient ? project2.threads.includes(recipient.threadId) : project2.remoteMembers.includes(recipient.principalId);
|
|
8240
|
+
var commentRecipients = (project2, actor, people) => {
|
|
8142
8241
|
const recipients = /* @__PURE__ */ new Map();
|
|
8143
8242
|
for (const person of people) {
|
|
8144
8243
|
if (person.kind === "agent" && !(actor.kind === "agent" && actor.threadId === person.threadId))
|
|
@@ -8146,7 +8245,7 @@ var commentRecipients = (project, actor, people) => {
|
|
|
8146
8245
|
if (person.kind === "remote-agent" && !(actor.kind === "remote-agent" && actor.principalId === person.principalId))
|
|
8147
8246
|
recipients.set(`principal:${person.principalId}`, { principalId: person.principalId });
|
|
8148
8247
|
}
|
|
8149
|
-
return [...recipients.values()].filter((recipient) => isMember(
|
|
8248
|
+
return [...recipients.values()].filter((recipient) => isMember(project2, recipient));
|
|
8150
8249
|
};
|
|
8151
8250
|
var commentWakeKey = (commentId, recipient) => "threadId" in recipient ? `comment:${commentId}:${recipient.threadId}` : `comment:${commentId}:principal:${recipient.principalId}`;
|
|
8152
8251
|
function foldWakes(state, event) {
|
|
@@ -8156,7 +8255,7 @@ function foldWakes(state, event) {
|
|
|
8156
8255
|
const fact = decoded2.value;
|
|
8157
8256
|
if (fact.name === "team/goal-started") {
|
|
8158
8257
|
const { goal } = fact.data;
|
|
8159
|
-
const
|
|
8258
|
+
const project4 = readFact(state, goal.projectId) ?? emptyProject();
|
|
8160
8259
|
const { members } = goalExecution(goal);
|
|
8161
8260
|
const threads = [
|
|
8162
8261
|
...new Set(members.flatMap((member) => "threadId" in member ? [member.threadId] : []))
|
|
@@ -8167,42 +8266,42 @@ function foldWakes(state, event) {
|
|
|
8167
8266
|
)
|
|
8168
8267
|
];
|
|
8169
8268
|
const leadThreads = goal.threads.filter((thread) => thread.memberId === goal.leadMemberId).map((thread) => thread.threadId);
|
|
8170
|
-
const next2 = { ...
|
|
8269
|
+
const next2 = { ...project4, threads, leadThreads, remoteMembers };
|
|
8171
8270
|
return {
|
|
8172
8271
|
...state,
|
|
8173
8272
|
[goal.projectId]: {
|
|
8174
8273
|
...next2,
|
|
8175
|
-
pendingComments:
|
|
8274
|
+
pendingComments: project4.pendingComments.filter((comment) => isMember(next2, comment))
|
|
8176
8275
|
}
|
|
8177
8276
|
};
|
|
8178
8277
|
}
|
|
8179
8278
|
const { projectId: projectId2, key } = fact.data;
|
|
8180
|
-
const
|
|
8181
|
-
if (
|
|
8279
|
+
const project3 = readFact(state, projectId2) ?? emptyProject();
|
|
8280
|
+
if (project3.pendingComments.some((comment) => comment.key === key)) {
|
|
8182
8281
|
return {
|
|
8183
8282
|
...state,
|
|
8184
8283
|
[projectId2]: {
|
|
8185
|
-
...
|
|
8186
|
-
pendingComments:
|
|
8284
|
+
...project3,
|
|
8285
|
+
pendingComments: project3.pendingComments.filter((comment) => comment.key !== key)
|
|
8187
8286
|
}
|
|
8188
8287
|
};
|
|
8189
8288
|
}
|
|
8190
|
-
if (
|
|
8191
|
-
return { ...state, [projectId2]: { ...
|
|
8289
|
+
if (project3.woken.includes(key)) return state;
|
|
8290
|
+
return { ...state, [projectId2]: { ...project3, woken: [...project3.woken, key] } };
|
|
8192
8291
|
}
|
|
8193
8292
|
const decoded = decodeWorkEvent(event);
|
|
8194
8293
|
if (isNone2(decoded)) return state;
|
|
8195
8294
|
const work = decoded.value;
|
|
8196
8295
|
const { projectId, workItemId } = work.payload;
|
|
8197
|
-
const
|
|
8198
|
-
const currentItem = readFact(
|
|
8199
|
-
let next =
|
|
8296
|
+
const project2 = readFact(state, projectId) ?? emptyProject();
|
|
8297
|
+
const currentItem = readFact(project2.items, workItemId);
|
|
8298
|
+
let next = project2;
|
|
8200
8299
|
switch (work.type) {
|
|
8201
8300
|
case "work.item-created":
|
|
8202
8301
|
next = {
|
|
8203
|
-
...
|
|
8302
|
+
...project2,
|
|
8204
8303
|
items: {
|
|
8205
|
-
...
|
|
8304
|
+
...project2.items,
|
|
8206
8305
|
[workItemId]: {
|
|
8207
8306
|
sequenceId: work.payload.sequenceId,
|
|
8208
8307
|
assigneeThreads: agentThreads(work.payload.assignees),
|
|
@@ -8222,9 +8321,9 @@ function foldWakes(state, event) {
|
|
|
8222
8321
|
])
|
|
8223
8322
|
];
|
|
8224
8323
|
next = {
|
|
8225
|
-
...
|
|
8324
|
+
...project2,
|
|
8226
8325
|
items: {
|
|
8227
|
-
...
|
|
8326
|
+
...project2.items,
|
|
8228
8327
|
[workItemId]: {
|
|
8229
8328
|
...currentItem,
|
|
8230
8329
|
assigneeThreads,
|
|
@@ -8240,18 +8339,18 @@ function foldWakes(state, event) {
|
|
|
8240
8339
|
case "work.relation-added":
|
|
8241
8340
|
case "work.relation-removed": {
|
|
8242
8341
|
if (work.payload.relationKind !== "blocked_by") return state;
|
|
8243
|
-
const blockers = readFact(
|
|
8342
|
+
const blockers = readFact(project2.blockers, workItemId) ?? [];
|
|
8244
8343
|
const related = work.payload.relatedWorkItemId;
|
|
8245
8344
|
next = {
|
|
8246
|
-
...
|
|
8345
|
+
...project2,
|
|
8247
8346
|
blockers: {
|
|
8248
|
-
...
|
|
8347
|
+
...project2.blockers,
|
|
8249
8348
|
[workItemId]: work.type === "work.relation-added" ? [.../* @__PURE__ */ new Set([...blockers, related])] : blockers.filter((blocker) => blocker !== related)
|
|
8250
8349
|
}
|
|
8251
8350
|
};
|
|
8252
|
-
if (work.type === "work.relation-added" && currentItem !== null && readFact(
|
|
8253
|
-
const woken = new Set(
|
|
8254
|
-
const rearms = { ...
|
|
8351
|
+
if (work.type === "work.relation-added" && currentItem !== null && readFact(project2.items, related)?.closed !== true) {
|
|
8352
|
+
const woken = new Set(project2.woken);
|
|
8353
|
+
const rearms = { ...project2.rearms };
|
|
8255
8354
|
for (const threadId of currentItem.assigneeThreads) {
|
|
8256
8355
|
if (currentItem.claimedBy.includes(threadId)) continue;
|
|
8257
8356
|
const generation = rearms[`${workItemId}:${threadId}`] ?? 0;
|
|
@@ -8259,14 +8358,14 @@ function foldWakes(state, event) {
|
|
|
8259
8358
|
rearms[`${workItemId}:${threadId}`] = generation + 1;
|
|
8260
8359
|
}
|
|
8261
8360
|
next = { ...next, rearms };
|
|
8262
|
-
if (currentItem.claimedBy.length > 0 && !hasOpenBlocker(
|
|
8361
|
+
if (currentItem.claimedBy.length > 0 && !hasOpenBlocker(project2, workItemId))
|
|
8263
8362
|
next = markBlockedAfterClaim(next, workItemId);
|
|
8264
8363
|
}
|
|
8265
8364
|
break;
|
|
8266
8365
|
}
|
|
8267
8366
|
case "work.comment-posted": {
|
|
8268
8367
|
const { actor, parentAuthor, mentions, commentId } = work.payload;
|
|
8269
|
-
const comments = commentRecipients(
|
|
8368
|
+
const comments = commentRecipients(project2, actor, [
|
|
8270
8369
|
...parentAuthor === void 0 ? [] : [parentAuthor],
|
|
8271
8370
|
...mentions
|
|
8272
8371
|
]).map((recipient) => ({
|
|
@@ -8276,10 +8375,10 @@ function foldWakes(state, event) {
|
|
|
8276
8375
|
workItemId,
|
|
8277
8376
|
commentId
|
|
8278
8377
|
})).filter(
|
|
8279
|
-
(comment) => !
|
|
8378
|
+
(comment) => !project2.pendingComments.some((pending) => pending.key === comment.key)
|
|
8280
8379
|
);
|
|
8281
8380
|
if (comments.length === 0) return state;
|
|
8282
|
-
next = { ...
|
|
8381
|
+
next = { ...project2, pendingComments: [...project2.pendingComments, ...comments] };
|
|
8283
8382
|
break;
|
|
8284
8383
|
}
|
|
8285
8384
|
}
|
|
@@ -8288,13 +8387,13 @@ function foldWakes(state, event) {
|
|
|
8288
8387
|
var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
8289
8388
|
function dueWakes(state) {
|
|
8290
8389
|
const due = [];
|
|
8291
|
-
for (const [projectId,
|
|
8292
|
-
const woken = new Set(
|
|
8293
|
-
const threads = new Set(
|
|
8294
|
-
const leads = new Set(
|
|
8295
|
-
for (const [workItemId, item] of Object.entries(
|
|
8296
|
-
if (item.closed || hasOpenBlocker(
|
|
8297
|
-
const unblocked = readFact(
|
|
8390
|
+
for (const [projectId, project2] of Object.entries(state)) {
|
|
8391
|
+
const woken = new Set(project2.woken);
|
|
8392
|
+
const threads = new Set(project2.threads);
|
|
8393
|
+
const leads = new Set(project2.leadThreads ?? []);
|
|
8394
|
+
for (const [workItemId, item] of Object.entries(project2.items)) {
|
|
8395
|
+
if (item.closed || hasOpenBlocker(project2, workItemId)) continue;
|
|
8396
|
+
const unblocked = readFact(project2.blockedAfterClaim, workItemId) ?? 0;
|
|
8298
8397
|
const recipients = /* @__PURE__ */ new Set([
|
|
8299
8398
|
...item.assigneeThreads,
|
|
8300
8399
|
...unblocked > 0 ? item.claimedBy : []
|
|
@@ -8305,7 +8404,7 @@ function dueWakes(state) {
|
|
|
8305
8404
|
const key = finishing ? unblockedWakeKey(workItemId, threadId, unblocked) : itemWakeKey(
|
|
8306
8405
|
workItemId,
|
|
8307
8406
|
threadId,
|
|
8308
|
-
readFact(
|
|
8407
|
+
readFact(project2.rearms, `${workItemId}:${threadId}`) ?? 0
|
|
8309
8408
|
);
|
|
8310
8409
|
if (woken.has(key)) continue;
|
|
8311
8410
|
due.push({
|
|
@@ -8319,8 +8418,8 @@ function dueWakes(state) {
|
|
|
8319
8418
|
});
|
|
8320
8419
|
}
|
|
8321
8420
|
}
|
|
8322
|
-
for (const comment of
|
|
8323
|
-
const item = readFact(
|
|
8421
|
+
for (const comment of project2.pendingComments) {
|
|
8422
|
+
const item = readFact(project2.items, comment.workItemId);
|
|
8324
8423
|
due.push({
|
|
8325
8424
|
...comment,
|
|
8326
8425
|
kind: "comment",
|
|
@@ -8453,8 +8552,13 @@ async function publicationRequiresAck(memory, projectId, input) {
|
|
|
8453
8552
|
|
|
8454
8553
|
// src/handbook/contracts.ts
|
|
8455
8554
|
var Release = Int.check(isGreaterThanOrEqualTo3(1));
|
|
8555
|
+
var HandbookPageKind = Literals([
|
|
8556
|
+
"handbook_decisions",
|
|
8557
|
+
"handbook_rules",
|
|
8558
|
+
"handbook_progress"
|
|
8559
|
+
]);
|
|
8456
8560
|
var Page = Struct({
|
|
8457
|
-
kind:
|
|
8561
|
+
kind: HandbookPageKind,
|
|
8458
8562
|
baseRevision: NullOr(Finite),
|
|
8459
8563
|
title: optionalKey2(String4),
|
|
8460
8564
|
body: String4,
|
|
@@ -8463,7 +8567,7 @@ var Page = Struct({
|
|
|
8463
8567
|
var WriteFields = {
|
|
8464
8568
|
requestId: String4,
|
|
8465
8569
|
expectedRelease: Finite,
|
|
8466
|
-
requireAck: optionalKey2(
|
|
8570
|
+
requireAck: optionalKey2(Boolean3)
|
|
8467
8571
|
};
|
|
8468
8572
|
var PublishInput = Struct({
|
|
8469
8573
|
...WriteFields,
|
|
@@ -8481,13 +8585,14 @@ var PublicationIntent = Struct({
|
|
|
8481
8585
|
memberId: String4,
|
|
8482
8586
|
requestId: String4,
|
|
8483
8587
|
op: Literals(["publish", "revert"]),
|
|
8484
|
-
requireAckChoice: optionalKey2(
|
|
8485
|
-
requiresAck: optionalKey2(
|
|
8588
|
+
requireAckChoice: optionalKey2(Boolean3),
|
|
8589
|
+
requiresAck: optionalKey2(Boolean3)
|
|
8486
8590
|
});
|
|
8487
8591
|
var PublicationCompleted = Struct({
|
|
8488
8592
|
version: Literal2(1),
|
|
8489
8593
|
projectId: String4,
|
|
8490
8594
|
requestId: String4,
|
|
8595
|
+
producedRelease: optionalKey2(Boolean3),
|
|
8491
8596
|
outcome: Union2([
|
|
8492
8597
|
Struct({ status: Literal2("succeeded"), release: Release }),
|
|
8493
8598
|
Struct({ status: Literal2("rejected") }),
|
|
@@ -8505,11 +8610,12 @@ var ReminderSent = Struct({
|
|
|
8505
8610
|
});
|
|
8506
8611
|
|
|
8507
8612
|
// src/handbook/state.ts
|
|
8508
|
-
var
|
|
8613
|
+
var HANDBOOK_EVENTS2 = ["plugin.event-appended", "wiki.handbook-released"];
|
|
8509
8614
|
var emptyHandbookState = () => ({
|
|
8510
8615
|
projects: {},
|
|
8511
8616
|
intents: {},
|
|
8512
8617
|
completed: {},
|
|
8618
|
+
publishers: {},
|
|
8513
8619
|
pendingPublications: {},
|
|
8514
8620
|
pendingAcknowledgements: {},
|
|
8515
8621
|
pendingReminders: {}
|
|
@@ -8535,16 +8641,16 @@ var decodeRelease = decodeUnknownSync(
|
|
|
8535
8641
|
Struct({
|
|
8536
8642
|
projectId: String4,
|
|
8537
8643
|
release: Number5,
|
|
8538
|
-
requiresAck:
|
|
8644
|
+
requiresAck: Boolean3
|
|
8539
8645
|
})
|
|
8540
8646
|
);
|
|
8541
|
-
function callerMember(
|
|
8647
|
+
function callerMember(project2, caller) {
|
|
8542
8648
|
if (caller === void 0) return void 0;
|
|
8543
|
-
return goalExecution(
|
|
8649
|
+
return goalExecution(project2.goal).members.find(
|
|
8544
8650
|
(member) => caller.kind === "thread" ? "threadId" in member && member.threadId === caller.threadId : "principalId" in member && member.principalId === caller.principalId
|
|
8545
8651
|
);
|
|
8546
8652
|
}
|
|
8547
|
-
var hasAcknowledged = (
|
|
8653
|
+
var hasAcknowledged = (project2, memberId, release) => readFact(readFact(project2.acknowledgements, memberId)?.releases ?? {}, String(release)) === true;
|
|
8548
8654
|
var reminderKey = (projectId, release, memberId) => JSON.stringify([projectId, release, memberId]);
|
|
8549
8655
|
var without = (rows, key) => {
|
|
8550
8656
|
if (!Object.hasOwn(rows, key)) return rows;
|
|
@@ -8555,12 +8661,12 @@ var without = (rows, key) => {
|
|
|
8555
8661
|
function foldHandbook(state, event) {
|
|
8556
8662
|
if (event.type === "wiki.handbook-released") {
|
|
8557
8663
|
const fact2 = decodeRelease(event.payload);
|
|
8558
|
-
const
|
|
8664
|
+
const project3 = readFact(state.projects, fact2.projectId) ?? emptyHandbookProject();
|
|
8559
8665
|
const pendingReminders = { ...state.pendingReminders };
|
|
8560
8666
|
if (fact2.requiresAck)
|
|
8561
|
-
for (const { memberId, ...recipient } of goalExecution(
|
|
8667
|
+
for (const { memberId, ...recipient } of goalExecution(project3.goal).members) {
|
|
8562
8668
|
const key = reminderKey(fact2.projectId, fact2.release, memberId);
|
|
8563
|
-
if (readFact(
|
|
8669
|
+
if (readFact(project3.reminded, key) === null)
|
|
8564
8670
|
pendingReminders[key] = {
|
|
8565
8671
|
projectId: fact2.projectId,
|
|
8566
8672
|
release: fact2.release,
|
|
@@ -8573,9 +8679,9 @@ function foldHandbook(state, event) {
|
|
|
8573
8679
|
projects: {
|
|
8574
8680
|
...state.projects,
|
|
8575
8681
|
[fact2.projectId]: {
|
|
8576
|
-
...
|
|
8682
|
+
...project3,
|
|
8577
8683
|
currentRelease: fact2.release,
|
|
8578
|
-
requiredRelease: fact2.requiresAck ? fact2.release :
|
|
8684
|
+
requiredRelease: fact2.requiresAck ? fact2.release : project3.requiredRelease
|
|
8579
8685
|
}
|
|
8580
8686
|
}
|
|
8581
8687
|
};
|
|
@@ -8585,11 +8691,11 @@ function foldHandbook(state, event) {
|
|
|
8585
8691
|
if (isNone2(decoded)) return state;
|
|
8586
8692
|
const fact = decoded.value;
|
|
8587
8693
|
const projectId = fact.name === "team/goal-started" ? fact.data.goal.projectId : fact.data.projectId;
|
|
8588
|
-
const
|
|
8589
|
-
let next =
|
|
8694
|
+
const project2 = readFact(state.projects, projectId) ?? emptyHandbookProject();
|
|
8695
|
+
let next = project2;
|
|
8590
8696
|
switch (fact.name) {
|
|
8591
8697
|
case "team/goal-started":
|
|
8592
|
-
next = { ...
|
|
8698
|
+
next = { ...project2, goal: fact.data.goal };
|
|
8593
8699
|
break;
|
|
8594
8700
|
case "team/handbook-publication-intended": {
|
|
8595
8701
|
const intent = fact.data;
|
|
@@ -8604,13 +8710,23 @@ function foldHandbook(state, event) {
|
|
|
8604
8710
|
const completed = fact.data;
|
|
8605
8711
|
if (readFact(state.completed, completed.requestId) !== null) return state;
|
|
8606
8712
|
const intent = readFact(state.pendingPublications, completed.requestId);
|
|
8713
|
+
let publishers = state.publishers;
|
|
8714
|
+
if (intent !== null && completed.producedRelease === true && completed.outcome.status === "succeeded") {
|
|
8715
|
+
const releases = readFact(publishers ?? {}, projectId) ?? {};
|
|
8716
|
+
const release = String(completed.outcome.release);
|
|
8717
|
+
if (readFact(releases, release) === null)
|
|
8718
|
+
publishers = {
|
|
8719
|
+
...publishers,
|
|
8720
|
+
[projectId]: { ...releases, [release]: intent.memberId }
|
|
8721
|
+
};
|
|
8722
|
+
}
|
|
8607
8723
|
let pendingAcknowledgements = state.pendingAcknowledgements;
|
|
8608
8724
|
let pendingReminders = state.pendingReminders;
|
|
8609
8725
|
if (intent !== null && intent.requiresAck && completed.outcome.status === "succeeded") {
|
|
8610
8726
|
const { release } = completed.outcome;
|
|
8611
8727
|
const key = reminderKey(projectId, release, intent.memberId);
|
|
8612
8728
|
pendingReminders = without(pendingReminders, key);
|
|
8613
|
-
if (!hasAcknowledged(
|
|
8729
|
+
if (!hasAcknowledged(project2, intent.memberId, release))
|
|
8614
8730
|
pendingAcknowledgements = {
|
|
8615
8731
|
...pendingAcknowledgements,
|
|
8616
8732
|
[key]: { version: 1, projectId, release, memberId: intent.memberId }
|
|
@@ -8619,6 +8735,7 @@ function foldHandbook(state, event) {
|
|
|
8619
8735
|
return {
|
|
8620
8736
|
...state,
|
|
8621
8737
|
completed: { ...state.completed, [completed.requestId]: completed },
|
|
8738
|
+
...publishers === void 0 ? {} : { publishers },
|
|
8622
8739
|
pendingPublications: without(state.pendingPublications, completed.requestId),
|
|
8623
8740
|
pendingAcknowledgements,
|
|
8624
8741
|
pendingReminders
|
|
@@ -8626,12 +8743,12 @@ function foldHandbook(state, event) {
|
|
|
8626
8743
|
}
|
|
8627
8744
|
case "team/handbook-acknowledged": {
|
|
8628
8745
|
const { memberId, release } = fact.data;
|
|
8629
|
-
if (hasAcknowledged(
|
|
8630
|
-
const previous = readFact(
|
|
8746
|
+
if (hasAcknowledged(project2, memberId, release)) return state;
|
|
8747
|
+
const previous = readFact(project2.acknowledgements, memberId);
|
|
8631
8748
|
next = {
|
|
8632
|
-
...
|
|
8749
|
+
...project2,
|
|
8633
8750
|
acknowledgements: {
|
|
8634
|
-
...
|
|
8751
|
+
...project2.acknowledgements,
|
|
8635
8752
|
[memberId]: {
|
|
8636
8753
|
releases: { ...previous?.releases, [release]: true },
|
|
8637
8754
|
latestRelease: Math.max(previous?.latestRelease ?? 0, release)
|
|
@@ -8649,8 +8766,8 @@ function foldHandbook(state, event) {
|
|
|
8649
8766
|
}
|
|
8650
8767
|
case "team/handbook-reminder-sent": {
|
|
8651
8768
|
const key = reminderKey(projectId, fact.data.release, fact.data.memberId);
|
|
8652
|
-
if (readFact(
|
|
8653
|
-
next = { ...
|
|
8769
|
+
if (readFact(project2.reminded, key) !== null) return state;
|
|
8770
|
+
next = { ...project2, reminded: { ...project2.reminded, [key]: true } };
|
|
8654
8771
|
return {
|
|
8655
8772
|
...state,
|
|
8656
8773
|
pendingReminders: without(state.pendingReminders, key),
|
|
@@ -8675,8 +8792,8 @@ var terminalFailure = (error) => {
|
|
|
8675
8792
|
function startHandbookRuntime(ctx) {
|
|
8676
8793
|
const state = ctx.derivedState({
|
|
8677
8794
|
key: "handbook",
|
|
8678
|
-
version:
|
|
8679
|
-
events:
|
|
8795
|
+
version: 5,
|
|
8796
|
+
events: HANDBOOK_EVENTS2,
|
|
8680
8797
|
init: emptyHandbookState,
|
|
8681
8798
|
apply: foldHandbook
|
|
8682
8799
|
});
|
|
@@ -8703,8 +8820,8 @@ function startHandbookRuntime(ctx) {
|
|
|
8703
8820
|
return state.get({ throughSequence: receipt.sequence });
|
|
8704
8821
|
};
|
|
8705
8822
|
const acknowledge = async (projectId, release, memberId) => {
|
|
8706
|
-
const
|
|
8707
|
-
if (hasAcknowledged(
|
|
8823
|
+
const project2 = readFact((await state.get()).projects, projectId) ?? emptyHandbookProject();
|
|
8824
|
+
if (hasAcknowledged(project2, memberId, release)) return;
|
|
8708
8825
|
await append2(
|
|
8709
8826
|
projectId,
|
|
8710
8827
|
"handbook-acknowledged",
|
|
@@ -8717,12 +8834,13 @@ function startHandbookRuntime(ctx) {
|
|
|
8717
8834
|
}
|
|
8718
8835
|
);
|
|
8719
8836
|
};
|
|
8720
|
-
const complete = async (intent, outcome) => {
|
|
8837
|
+
const complete = async (intent, outcome, producedRelease) => {
|
|
8721
8838
|
await append2(intent.projectId, "handbook-publication-completed", intent.requestId, {
|
|
8722
8839
|
version: 1,
|
|
8723
8840
|
requestId: intent.requestId,
|
|
8724
8841
|
projectId: intent.projectId,
|
|
8725
|
-
outcome
|
|
8842
|
+
outcome,
|
|
8843
|
+
...producedRelease === void 0 ? {} : { producedRelease }
|
|
8726
8844
|
});
|
|
8727
8845
|
if (outcome.status === "succeeded" && intent.requiresAck) {
|
|
8728
8846
|
await acknowledge(intent.projectId, outcome.release, intent.memberId);
|
|
@@ -8742,7 +8860,11 @@ function startHandbookRuntime(ctx) {
|
|
|
8742
8860
|
if (typeof result2 !== "object" || result2 === null || !("release" in result2) || typeof result2.release !== "number" || !Number.isSafeInteger(result2.release) || result2.release < 1)
|
|
8743
8861
|
throw new Error("handbook-receipt-result-unavailable");
|
|
8744
8862
|
await state.get({ throughSequence: receipt.sequence });
|
|
8745
|
-
return complete(
|
|
8863
|
+
return complete(
|
|
8864
|
+
intent,
|
|
8865
|
+
{ status: "succeeded", release: result2.release },
|
|
8866
|
+
"changed" in result2 && result2.changed === true
|
|
8867
|
+
);
|
|
8746
8868
|
};
|
|
8747
8869
|
const remind = async () => {
|
|
8748
8870
|
const snapshot = await state.get();
|
|
@@ -8796,7 +8918,7 @@ function startHandbookRuntime(ctx) {
|
|
|
8796
8918
|
await remind();
|
|
8797
8919
|
};
|
|
8798
8920
|
const flush = () => serial(execute);
|
|
8799
|
-
ctx.reactor({ name: "team-handbook", events:
|
|
8921
|
+
ctx.reactor({ name: "team-handbook", events: HANDBOOK_EVENTS2, handle: () => flush() });
|
|
8800
8922
|
ctx.onDispose(async () => {
|
|
8801
8923
|
disposed = true;
|
|
8802
8924
|
await pending;
|
|
@@ -8806,28 +8928,36 @@ function startHandbookRuntime(ctx) {
|
|
|
8806
8928
|
return {
|
|
8807
8929
|
ready,
|
|
8808
8930
|
flush,
|
|
8931
|
+
async publisherMembers(projectId, releases) {
|
|
8932
|
+
const snapshot = await state.get();
|
|
8933
|
+
const publishers = readFact(snapshot.publishers ?? {}, projectId) ?? {};
|
|
8934
|
+
return releases.flatMap((release) => {
|
|
8935
|
+
const memberId = readFact(publishers, String(release));
|
|
8936
|
+
return memberId === null ? [] : [{ release, memberId }];
|
|
8937
|
+
});
|
|
8938
|
+
},
|
|
8809
8939
|
async read(projectId) {
|
|
8810
|
-
const
|
|
8940
|
+
const project2 = readFact((await state.get()).projects, projectId) ?? emptyHandbookProject();
|
|
8811
8941
|
return {
|
|
8812
|
-
currentRelease:
|
|
8813
|
-
requiredRelease:
|
|
8814
|
-
members: goalExecution(
|
|
8942
|
+
currentRelease: project2.currentRelease,
|
|
8943
|
+
requiredRelease: project2.requiredRelease,
|
|
8944
|
+
members: goalExecution(project2.goal).members.map(({ memberId }) => ({
|
|
8815
8945
|
memberId,
|
|
8816
|
-
acknowledgedRelease: readFact(
|
|
8817
|
-
acknowledgedRequiredRelease:
|
|
8946
|
+
acknowledgedRelease: readFact(project2.acknowledgements, memberId)?.latestRelease ?? null,
|
|
8947
|
+
acknowledgedRequiredRelease: project2.requiredRelease === null || hasAcknowledged(project2, memberId, project2.requiredRelease)
|
|
8818
8948
|
}))
|
|
8819
8949
|
};
|
|
8820
8950
|
},
|
|
8821
8951
|
ack(release, invocation) {
|
|
8822
8952
|
return serial(async () => {
|
|
8823
8953
|
const projectId = invocation.projectId;
|
|
8824
|
-
const
|
|
8825
|
-
const member = callerMember(
|
|
8954
|
+
const project2 = projectId === null ? emptyHandbookProject() : readFact((await state.get()).projects, projectId) ?? emptyHandbookProject();
|
|
8955
|
+
const member = callerMember(project2, invocation.caller);
|
|
8826
8956
|
if (member === void 0 || projectId === null)
|
|
8827
8957
|
throw new Error(handbookMessage("unknownMember"));
|
|
8828
|
-
if (
|
|
8829
|
-
if (release !==
|
|
8830
|
-
throw new Error(handbookMessage("wrongRelease",
|
|
8958
|
+
if (project2.requiredRelease === null) throw new Error(handbookMessage("noRequirement"));
|
|
8959
|
+
if (release !== project2.requiredRelease)
|
|
8960
|
+
throw new Error(handbookMessage("wrongRelease", project2.requiredRelease));
|
|
8831
8961
|
await acknowledge(projectId, release, member.memberId);
|
|
8832
8962
|
return { release, memberId: member.memberId };
|
|
8833
8963
|
});
|
|
@@ -8835,9 +8965,9 @@ function startHandbookRuntime(ctx) {
|
|
|
8835
8965
|
publish(input, invocation) {
|
|
8836
8966
|
return serial(async () => {
|
|
8837
8967
|
const projectId = invocation.projectId;
|
|
8838
|
-
const
|
|
8839
|
-
const member = callerMember(
|
|
8840
|
-
const execution = goalExecution(
|
|
8968
|
+
const project2 = projectId === null ? emptyHandbookProject() : readFact((await state.get()).projects, projectId) ?? emptyHandbookProject();
|
|
8969
|
+
const member = callerMember(project2, invocation.caller);
|
|
8970
|
+
const execution = goalExecution(project2.goal);
|
|
8841
8971
|
if (execution.leadMemberId === null) throw new Error(handbookMessage("noGoal"));
|
|
8842
8972
|
if (projectId === null || invocation.caller?.kind !== "thread" || member === void 0 || member.memberId !== execution.leadMemberId)
|
|
8843
8973
|
throw new Error(handbookMessage("leadOnly"));
|
|
@@ -8863,35 +8993,37 @@ function startHandbookRuntime(ctx) {
|
|
|
8863
8993
|
if (existing === null)
|
|
8864
8994
|
await append2(projectId, "handbook-publication-intended", requestId, intent);
|
|
8865
8995
|
let outcome;
|
|
8996
|
+
let producedRelease;
|
|
8866
8997
|
let detail = null;
|
|
8867
8998
|
try {
|
|
8868
8999
|
const command = { ...input, projectId, requestId, requireAck };
|
|
8869
9000
|
const result2 = command.operation === "publish" ? await ctx.memory.handbook.publish(command) : await ctx.memory.handbook.revert(command);
|
|
8870
9001
|
await state.get({ throughSequence: result2.sequence });
|
|
8871
9002
|
outcome = { status: "succeeded", release: result2.result.release };
|
|
9003
|
+
producedRelease = result2.result.changed;
|
|
8872
9004
|
} catch (error) {
|
|
8873
9005
|
detail = terminalFailure(error);
|
|
8874
9006
|
if (detail === null) throw error;
|
|
8875
9007
|
outcome = { status: "rejected" };
|
|
8876
9008
|
}
|
|
8877
|
-
if (done2 === null) await complete(intent, outcome);
|
|
9009
|
+
if (done2 === null) await complete(intent, outcome, producedRelease);
|
|
8878
9010
|
await remind();
|
|
8879
9011
|
return detail === null ? outcome : { ...outcome, detail };
|
|
8880
9012
|
});
|
|
8881
9013
|
},
|
|
8882
9014
|
async check(input) {
|
|
8883
|
-
const
|
|
9015
|
+
const project2 = readFact(
|
|
8884
9016
|
(await state.get({ throughSequence: input.throughSequence })).projects,
|
|
8885
9017
|
input.projectId
|
|
8886
9018
|
);
|
|
8887
|
-
if (
|
|
9019
|
+
if (project2 === null || project2.requiredRelease === null) return { allowed: true };
|
|
8888
9020
|
for (const actor of input.added) {
|
|
8889
9021
|
const caller = actor.kind === "human" ? void 0 : actor.kind === "agent" ? { kind: "thread", threadId: actor.threadId } : actor;
|
|
8890
|
-
const member = callerMember(
|
|
8891
|
-
if (member !== void 0 && !hasAcknowledged(
|
|
9022
|
+
const member = callerMember(project2, caller);
|
|
9023
|
+
if (member !== void 0 && !hasAcknowledged(project2, member.memberId, project2.requiredRelease)) {
|
|
8892
9024
|
return {
|
|
8893
9025
|
allowed: false,
|
|
8894
|
-
detail: handbookMessage("guard",
|
|
9026
|
+
detail: handbookMessage("guard", project2.requiredRelease)
|
|
8895
9027
|
};
|
|
8896
9028
|
}
|
|
8897
9029
|
}
|
|
@@ -8904,7 +9036,7 @@ function startHandbookRuntime(ctx) {
|
|
|
8904
9036
|
var decodeAcknowledgement = decodeUnknownSync(AcknowledgeInput);
|
|
8905
9037
|
var decodePublish = decodeUnknownSync(PublishInput);
|
|
8906
9038
|
var decodeRevert = decodeUnknownSync(RevertInput);
|
|
8907
|
-
var
|
|
9039
|
+
var decodeRead4 = decodeUnknownSync(ReadHandbookInput);
|
|
8908
9040
|
var toolSchema = (schema) => ({
|
|
8909
9041
|
...payloadSchemaOf(schema),
|
|
8910
9042
|
type: "object",
|
|
@@ -8948,15 +9080,127 @@ function registerHandbook(ctx, runtime) {
|
|
|
8948
9080
|
name: "handbook",
|
|
8949
9081
|
scope: "read",
|
|
8950
9082
|
payloadSchema: payloadSchemaOf(ReadHandbookInput),
|
|
8951
|
-
handle: (input) => runtime.read(
|
|
9083
|
+
handle: (input) => runtime.read(decodeRead4(input).projectId)
|
|
8952
9084
|
});
|
|
8953
9085
|
ctx.operations.guard({ operations: ["work.assign"], check: runtime.check });
|
|
8954
9086
|
}
|
|
8955
9087
|
|
|
9088
|
+
// src/handbook/panelContracts.ts
|
|
9089
|
+
var project = { projectId: String4 };
|
|
9090
|
+
var ReadPanelInput = Struct(project);
|
|
9091
|
+
var ReadReleasesInput = Struct({
|
|
9092
|
+
...project,
|
|
9093
|
+
cursor: optionalKey2(String4)
|
|
9094
|
+
});
|
|
9095
|
+
var ReadPageInput = Struct({
|
|
9096
|
+
...project,
|
|
9097
|
+
pageId: String4,
|
|
9098
|
+
revision: Int,
|
|
9099
|
+
textOffset: optionalKey2(Int)
|
|
9100
|
+
});
|
|
9101
|
+
var PublishPanelInput = Struct({ ...project, ...PublishInput.fields });
|
|
9102
|
+
var RevertPanelInput = Struct({ ...project, ...RevertInput.fields });
|
|
9103
|
+
var HandbookWriteResult = Union2([
|
|
9104
|
+
Struct({ status: Literal2("succeeded"), release: Int }),
|
|
9105
|
+
Struct({ status: Literal2("rejected"), code: String4, detail: String4 })
|
|
9106
|
+
]);
|
|
9107
|
+
|
|
9108
|
+
// src/handbook/panel.ts
|
|
9109
|
+
var decodePanel = decodeUnknownSync(ReadPanelInput);
|
|
9110
|
+
var decodeList = decodeUnknownSync(ReadReleasesInput);
|
|
9111
|
+
var decodePage = decodeUnknownSync(ReadPageInput);
|
|
9112
|
+
var decodePublish2 = decodeUnknownSync(PublishPanelInput);
|
|
9113
|
+
var decodeRevert2 = decodeUnknownSync(RevertPanelInput);
|
|
9114
|
+
function registerHandbookPanel(ctx, runtime) {
|
|
9115
|
+
ctx.command({
|
|
9116
|
+
name: "handbookPanel",
|
|
9117
|
+
scope: "read",
|
|
9118
|
+
payloadSchema: payloadSchemaOf(ReadPanelInput),
|
|
9119
|
+
handle: async (payload, invocation) => {
|
|
9120
|
+
const input = decodePanel(payload);
|
|
9121
|
+
const [result2, status] = await Promise.all([
|
|
9122
|
+
ctx.memory.handbook.get(input),
|
|
9123
|
+
runtime.read(input.projectId)
|
|
9124
|
+
]);
|
|
9125
|
+
return { head: result2.head, status, canOperate: invocation.scope === "operate" };
|
|
9126
|
+
}
|
|
9127
|
+
});
|
|
9128
|
+
ctx.command({
|
|
9129
|
+
name: "handbookReleases",
|
|
9130
|
+
scope: "read",
|
|
9131
|
+
payloadSchema: payloadSchemaOf(ReadReleasesInput),
|
|
9132
|
+
handle: async (payload) => {
|
|
9133
|
+
const input = decodeList(payload);
|
|
9134
|
+
const result2 = await ctx.memory.handbook.releases.list({ ...input, limit: 20 });
|
|
9135
|
+
const publishers = await runtime.publisherMembers(
|
|
9136
|
+
input.projectId,
|
|
9137
|
+
result2.items.map((row) => row.release)
|
|
9138
|
+
);
|
|
9139
|
+
return {
|
|
9140
|
+
...result2,
|
|
9141
|
+
items: result2.items.map((row) => ({
|
|
9142
|
+
...row,
|
|
9143
|
+
publisherMemberId: row.publishedBy.kind === "plugin" && row.publishedBy.pluginId === "team" ? publishers.find((publisher) => publisher.release === row.release)?.memberId ?? null : null
|
|
9144
|
+
}))
|
|
9145
|
+
};
|
|
9146
|
+
}
|
|
9147
|
+
});
|
|
9148
|
+
ctx.command({
|
|
9149
|
+
name: "handbookPage",
|
|
9150
|
+
scope: "read",
|
|
9151
|
+
payloadSchema: payloadSchemaOf(ReadPageInput),
|
|
9152
|
+
handle: (payload) => ctx.memory.pageRevisions.get(decodePage(payload))
|
|
9153
|
+
});
|
|
9154
|
+
const write = async (input, invocation) => {
|
|
9155
|
+
if (invocation.scope !== "operate" || invocation.asPerson === void 0)
|
|
9156
|
+
return { status: "rejected", code: "needs-person", detail: "needs-person" };
|
|
9157
|
+
try {
|
|
9158
|
+
const requireAck = await publicationRequiresAck(ctx.memory, input.projectId, input);
|
|
9159
|
+
const handbook = invocation.asPerson.memory.handbook;
|
|
9160
|
+
const command = {
|
|
9161
|
+
projectId: input.projectId,
|
|
9162
|
+
requestId: input.requestId,
|
|
9163
|
+
expectedRelease: input.expectedRelease,
|
|
9164
|
+
requireAck
|
|
9165
|
+
};
|
|
9166
|
+
const result2 = input.operation === "publish" ? await handbook.publish({
|
|
9167
|
+
...command,
|
|
9168
|
+
pages: input.pages,
|
|
9169
|
+
...input.sourceExperience === void 0 ? {} : { sourceExperience: input.sourceExperience }
|
|
9170
|
+
}) : await handbook.revert({ ...command, toRelease: input.toRelease });
|
|
9171
|
+
return { status: "succeeded", release: result2.result.release };
|
|
9172
|
+
} catch (error) {
|
|
9173
|
+
const refusal = decodeHostRefusal(error);
|
|
9174
|
+
if (isNone2(refusal)) throw error;
|
|
9175
|
+
const { code, details } = refusal.value.rejection;
|
|
9176
|
+
return {
|
|
9177
|
+
status: "rejected",
|
|
9178
|
+
code,
|
|
9179
|
+
detail: [
|
|
9180
|
+
error instanceof Error ? error.message : code,
|
|
9181
|
+
details === void 0 ? null : JSON.stringify(details)
|
|
9182
|
+
].filter(Boolean).join("\n")
|
|
9183
|
+
};
|
|
9184
|
+
}
|
|
9185
|
+
};
|
|
9186
|
+
ctx.command({
|
|
9187
|
+
name: "publishHandbook",
|
|
9188
|
+
scope: "operate",
|
|
9189
|
+
payloadSchema: payloadSchemaOf(PublishPanelInput),
|
|
9190
|
+
handle: (payload, invocation) => write({ ...decodePublish2(payload), operation: "publish" }, invocation)
|
|
9191
|
+
});
|
|
9192
|
+
ctx.command({
|
|
9193
|
+
name: "revertHandbook",
|
|
9194
|
+
scope: "operate",
|
|
9195
|
+
payloadSchema: payloadSchemaOf(RevertPanelInput),
|
|
9196
|
+
handle: (payload, invocation) => write({ ...decodeRevert2(payload), operation: "revert" }, invocation)
|
|
9197
|
+
});
|
|
9198
|
+
}
|
|
9199
|
+
|
|
8956
9200
|
// src/index.ts
|
|
8957
9201
|
var index_default = definePlugin({
|
|
8958
9202
|
id: "team",
|
|
8959
|
-
apiVersion:
|
|
9203
|
+
apiVersion: 14,
|
|
8960
9204
|
activate(ctx) {
|
|
8961
9205
|
const rosters = createRosterService(ctx);
|
|
8962
9206
|
registerRosterCommands(ctx, rosters);
|
|
@@ -8965,7 +9209,9 @@ var index_default = definePlugin({
|
|
|
8965
9209
|
registerBoardCommands(ctx, goals, rosters);
|
|
8966
9210
|
registerDetailCommands(ctx);
|
|
8967
9211
|
startWakeRuntime(ctx);
|
|
8968
|
-
|
|
9212
|
+
const handbook = startHandbookRuntime(ctx);
|
|
9213
|
+
registerHandbook(ctx, handbook);
|
|
9214
|
+
registerHandbookPanel(ctx, handbook);
|
|
8969
9215
|
}
|
|
8970
9216
|
});
|
|
8971
9217
|
export {
|