oasis_test 0.1.13 → 0.1.14
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/index.js +1776 -404
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -161,6 +161,7 @@ function fold(model, op) {
|
|
|
161
161
|
author: op.actor,
|
|
162
162
|
body: p2.body,
|
|
163
163
|
state: "open",
|
|
164
|
+
createdAt: op.timestamp,
|
|
164
165
|
thread: [],
|
|
165
166
|
raisedFrom: p2.raisedFrom ?? null,
|
|
166
167
|
raisedFromRev: p2.raisedFromRev ?? null,
|
|
@@ -1744,14 +1745,18 @@ var init_kernel = __esm({
|
|
|
1744
1745
|
const override = await this.resolveReviewerOverride?.(artifact.owner, artifact);
|
|
1745
1746
|
const overrideApplied = !!override && override.length > 0;
|
|
1746
1747
|
const configured = overrideApplied ? override : gateSpec.reviewers.flatMap((role) => this.roleIndex.get(role) ?? []);
|
|
1747
|
-
const
|
|
1748
|
+
const configuredUnique = [...new Set(configured)];
|
|
1749
|
+
const eligible = configuredUnique.filter((a) => a !== head.author);
|
|
1748
1750
|
const quorum = overrideApplied ? Math.min(gateSpec.quorum, Math.max(1, eligible.length)) : gateSpec.quorum;
|
|
1749
1751
|
if (eligible.length < quorum) {
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1752
|
+
if (!(configuredUnique.includes(head.author) && configuredUnique.length >= quorum)) {
|
|
1753
|
+
throw new KernelError(
|
|
1754
|
+
`\u6536\u5C3E gate \u65E0\u6CD5\u6EE1\u8DB3\uFF1A\u5408\u683C reviewer ${eligible.length} \u4EBA < quorum ${quorum}\uFF08reviewers=${eligible.join(",") || "none"}\uFF1Broles=${gateSpec.reviewers.join(",")}\uFF0C\u5DF2\u5254\u9664 head \u4F5C\u8005 ${head.author}\uFF09\u2014\u2014\u914D\u7F6E\u95EE\u9898\u8981\u54CD\u4EAE\uFF0C\u4E0D\u9759\u9ED8\u653E\u884C`
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
} else {
|
|
1758
|
+
gate = { quorum, eligible };
|
|
1753
1759
|
}
|
|
1754
|
-
gate = { quorum, eligible };
|
|
1755
1760
|
}
|
|
1756
1761
|
const payload = {
|
|
1757
1762
|
...args.note !== void 0 ? { note: args.note } : {},
|
|
@@ -3303,14 +3308,26 @@ var init_dispatcher = __esm({
|
|
|
3303
3308
|
...this.opts.resolveActorContext !== void 0 ? { resolveActorContext: this.opts.resolveActorContext } : {},
|
|
3304
3309
|
...materializeSkills ? { materializeSkills } : {}
|
|
3305
3310
|
});
|
|
3311
|
+
const limits = { wallClockMs: this.wallClockFor(spec.artifactId) };
|
|
3312
|
+
const credentialCtx = {
|
|
3313
|
+
actor: spec.actor,
|
|
3314
|
+
artifactId: spec.artifactId,
|
|
3315
|
+
action: spec.action,
|
|
3316
|
+
...spec.part !== void 0 ? { part: spec.part } : {},
|
|
3317
|
+
...binding !== void 0 ? { binding } : {},
|
|
3318
|
+
limits
|
|
3319
|
+
};
|
|
3320
|
+
const serverUrl = this.opts.serverUrlFor ? await this.opts.serverUrlFor(credentialCtx) : this.opts.serverUrl;
|
|
3321
|
+
const tokenFor = this.opts.tokenFor ?? ((a, _ctx) => `token:${a}`);
|
|
3322
|
+
const actorToken = tokenFor(spec.actor, credentialCtx);
|
|
3306
3323
|
const provisioned = this.opts.provision ? await this.opts.provision(spec.actor) : void 0;
|
|
3307
3324
|
const job = {
|
|
3308
3325
|
actor: spec.actor,
|
|
3309
|
-
actorToken
|
|
3326
|
+
actorToken,
|
|
3310
3327
|
artifactId: spec.artifactId,
|
|
3311
3328
|
bundle: { files: bundle.files },
|
|
3312
|
-
server: { url:
|
|
3313
|
-
limits
|
|
3329
|
+
server: { url: serverUrl },
|
|
3330
|
+
limits,
|
|
3314
3331
|
...binding !== void 0 ? { binding } : {},
|
|
3315
3332
|
...provisioned?.env && Object.keys(provisioned.env).length > 0 ? { env: provisioned.env } : {},
|
|
3316
3333
|
...provisioned?.wrapperPaths && provisioned.wrapperPaths.length > 0 ? { wrapperPaths: provisioned.wrapperPaths } : {}
|
|
@@ -3333,6 +3350,7 @@ var init_dispatcher = __esm({
|
|
|
3333
3350
|
artifactId: spec.artifactId,
|
|
3334
3351
|
actor: spec.actor,
|
|
3335
3352
|
action: spec.action,
|
|
3353
|
+
...binding?.runtimeKind ? { runtimeKind: binding.runtimeKind } : {},
|
|
3336
3354
|
bundleManifest: manifest,
|
|
3337
3355
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3338
3356
|
};
|
|
@@ -3481,7 +3499,7 @@ var init_dispatcher = __esm({
|
|
|
3481
3499
|
if (!artifact) return false;
|
|
3482
3500
|
if (!artifact.owner.startsWith("actor:agent:")) return false;
|
|
3483
3501
|
if (lifecycleOf(model, spec.artifactId) !== "active") return false;
|
|
3484
|
-
if (isConcluded(model, spec.artifactId)) return false;
|
|
3502
|
+
if (isConcluded(model, spec.artifactId) && concludedHead(model, spec.artifactId) === artifact.currentRev) return false;
|
|
3485
3503
|
if (pendingConcludeAttempt(model, spec.artifactId)) return false;
|
|
3486
3504
|
if (!artifact.currentRev) return false;
|
|
3487
3505
|
if (queueOf(model, spec.artifactId).length > 0) return false;
|
|
@@ -16436,7 +16454,7 @@ var require_compile = __commonJS({
|
|
|
16436
16454
|
}
|
|
16437
16455
|
}
|
|
16438
16456
|
exports2.compileSchema = compileSchema;
|
|
16439
|
-
function
|
|
16457
|
+
function resolveRef2(root, baseId, ref) {
|
|
16440
16458
|
var _a;
|
|
16441
16459
|
ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
|
|
16442
16460
|
const schOrFunc = root.refs[ref];
|
|
@@ -16453,7 +16471,7 @@ var require_compile = __commonJS({
|
|
|
16453
16471
|
return;
|
|
16454
16472
|
return root.refs[ref] = inlineOrCompile.call(this, _sch);
|
|
16455
16473
|
}
|
|
16456
|
-
exports2.resolveRef =
|
|
16474
|
+
exports2.resolveRef = resolveRef2;
|
|
16457
16475
|
function inlineOrCompile(sch) {
|
|
16458
16476
|
if ((0, resolve_1.inlineRef)(sch.schema, this.opts.inlineRefs))
|
|
16459
16477
|
return sch.schema;
|
|
@@ -20334,12 +20352,12 @@ var require_dist = __commonJS({
|
|
|
20334
20352
|
throw new Error(`Unknown format "${name}"`);
|
|
20335
20353
|
return f2;
|
|
20336
20354
|
};
|
|
20337
|
-
function addFormats(ajv, list,
|
|
20355
|
+
function addFormats(ajv, list, fs17, exportName) {
|
|
20338
20356
|
var _a;
|
|
20339
20357
|
var _b;
|
|
20340
20358
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
20341
20359
|
for (const f2 of list)
|
|
20342
|
-
ajv.addFormat(f2,
|
|
20360
|
+
ajv.addFormat(f2, fs17[f2]);
|
|
20343
20361
|
}
|
|
20344
20362
|
module2.exports = exports2 = formatsPlugin;
|
|
20345
20363
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -20527,16 +20545,35 @@ var init_drafts = __esm({
|
|
|
20527
20545
|
});
|
|
20528
20546
|
|
|
20529
20547
|
// ../server/src/governance/workorder-drafts.ts
|
|
20530
|
-
var WorkorderDraftStore;
|
|
20548
|
+
var import_node_fs, WorkorderDraftStore;
|
|
20531
20549
|
var init_workorder_drafts = __esm({
|
|
20532
20550
|
"../server/src/governance/workorder-drafts.ts"() {
|
|
20533
20551
|
"use strict";
|
|
20552
|
+
import_node_fs = __toESM(require("node:fs"), 1);
|
|
20534
20553
|
WorkorderDraftStore = class {
|
|
20554
|
+
constructor(file) {
|
|
20555
|
+
this.file = file;
|
|
20556
|
+
if (file && import_node_fs.default.existsSync(file)) {
|
|
20557
|
+
try {
|
|
20558
|
+
const data = JSON.parse(import_node_fs.default.readFileSync(file, "utf8"));
|
|
20559
|
+
this.seq = data.seq ?? 0;
|
|
20560
|
+
for (const d of data.drafts ?? []) this.drafts.set(d.id, d);
|
|
20561
|
+
} catch {
|
|
20562
|
+
}
|
|
20563
|
+
}
|
|
20564
|
+
}
|
|
20535
20565
|
drafts = /* @__PURE__ */ new Map();
|
|
20536
20566
|
seq = 0;
|
|
20567
|
+
persist() {
|
|
20568
|
+
if (!this.file) return;
|
|
20569
|
+
const tmp = `${this.file}.tmp`;
|
|
20570
|
+
import_node_fs.default.writeFileSync(tmp, JSON.stringify({ seq: this.seq, drafts: [...this.drafts.values()] }));
|
|
20571
|
+
import_node_fs.default.renameSync(tmp, this.file);
|
|
20572
|
+
}
|
|
20537
20573
|
submit(d) {
|
|
20538
20574
|
const draft = { ...d, id: `wodraft:${++this.seq}`, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
20539
20575
|
this.drafts.set(draft.id, draft);
|
|
20576
|
+
this.persist();
|
|
20540
20577
|
return draft;
|
|
20541
20578
|
}
|
|
20542
20579
|
list() {
|
|
@@ -20546,16 +20583,159 @@ var init_workorder_drafts = __esm({
|
|
|
20546
20583
|
forWorkspace(workspace) {
|
|
20547
20584
|
return [...this.drafts.values()].reverse().find((d) => d.workspace === workspace);
|
|
20548
20585
|
}
|
|
20586
|
+
/** 按 chat 会话取最新一份:agent 不自带 ws(后端生成)时,friday 据本会话 id 取回它刚建的草案。 */
|
|
20587
|
+
forSession(chatSessionId) {
|
|
20588
|
+
return [...this.drafts.values()].reverse().find((d) => d.chatSessionId === chatSessionId);
|
|
20589
|
+
}
|
|
20590
|
+
/** 给草案打上 chat 会话标(建单经 chat 路时,/api/cmd 拿到 X-Chat-Session-Id 后调)。 */
|
|
20591
|
+
tagSession(draftId, chatSessionId) {
|
|
20592
|
+
const d = this.drafts.get(draftId);
|
|
20593
|
+
if (d) {
|
|
20594
|
+
d.chatSessionId = chatSessionId;
|
|
20595
|
+
this.persist();
|
|
20596
|
+
}
|
|
20597
|
+
}
|
|
20549
20598
|
get(id) {
|
|
20550
20599
|
return this.drafts.get(id);
|
|
20551
20600
|
}
|
|
20552
20601
|
remove(id) {
|
|
20553
|
-
|
|
20602
|
+
const ok = this.drafts.delete(id);
|
|
20603
|
+
if (ok) this.persist();
|
|
20604
|
+
return ok;
|
|
20554
20605
|
}
|
|
20555
20606
|
};
|
|
20556
20607
|
}
|
|
20557
20608
|
});
|
|
20558
20609
|
|
|
20610
|
+
// ../server/src/governance/draft-edit.ts
|
|
20611
|
+
function resolveRef(ref, draft) {
|
|
20612
|
+
const res = resolveNodeRef(ref, candidatesOf(draft));
|
|
20613
|
+
if (res.ok) return res.id;
|
|
20614
|
+
if (res.reason === "ambiguous")
|
|
20615
|
+
throw new Error(`\u300C${ref}\u300D\u5728\u672C\u8349\u6848\u5339\u914D\u5230\u591A\u4E2A\u8282\u70B9\uFF0C\u8BF7\u66F4\u5177\u4F53\uFF08type:title \u6216 id\uFF09\uFF1A${res.candidates.map((c) => `${c.type}:${c.title ?? "?"}`).join("\u3001")}`);
|
|
20616
|
+
throw new Error(`\u300C${ref}\u300D\u5728\u672C\u8349\u6848\u91CC\u627E\u4E0D\u5230\u5BF9\u5E94\u8282\u70B9\uFF08\u53EF\u7528 type / type:title / id\uFF09`);
|
|
20617
|
+
}
|
|
20618
|
+
function editDraftPlan(draft, op) {
|
|
20619
|
+
switch (op.action) {
|
|
20620
|
+
case "spawn": {
|
|
20621
|
+
const title = op.title?.trim();
|
|
20622
|
+
if (!title) throw new Error("\u7ED9\u8349\u6848\u52A0\u8282\u70B9\u8981 --title\uFF08\u636E (type,title) \u6D3E\u751F id\uFF09");
|
|
20623
|
+
if (candidatesOf(draft).some((c) => c.type === op.type && (c.title ?? "").toLowerCase() === title.toLowerCase()))
|
|
20624
|
+
throw new Error(`\u8282\u70B9 title \u649E\u540D\uFF1A${op.type}\u300C${title}\u300D\u8349\u6848\u91CC\u5DF2\u6709\uFF0C\u6362\u4E2A\u533A\u5206\u7684\u540D`);
|
|
20625
|
+
const used = new Set(spawnOps(draft).map((o) => o.id));
|
|
20626
|
+
const id = deriveArtifactId(op.type, draft.workspace, title, used);
|
|
20627
|
+
const inputs = op.inputs?.map((e) => ({ to: resolveRef(e.to, draft), ...e.required !== void 0 ? { required: e.required } : {} }));
|
|
20628
|
+
draft.plan.ops.push({
|
|
20629
|
+
action: "spawn",
|
|
20630
|
+
id,
|
|
20631
|
+
type: op.type,
|
|
20632
|
+
workspace: draft.workspace,
|
|
20633
|
+
title,
|
|
20634
|
+
...op.description !== void 0 ? { description: op.description } : {},
|
|
20635
|
+
...op.fields ? { fields: op.fields } : {},
|
|
20636
|
+
...inputs && inputs.length > 0 ? { inputs } : {}
|
|
20637
|
+
});
|
|
20638
|
+
if (op.description) draft.plan.ops.push({ action: "annotate", artifactId: id, body: op.description, blocks: false });
|
|
20639
|
+
break;
|
|
20640
|
+
}
|
|
20641
|
+
case "edit": {
|
|
20642
|
+
const id = resolveRef(op.artifactId, draft);
|
|
20643
|
+
const target = spawnOps(draft).find((o) => o.id === id);
|
|
20644
|
+
if (!target) throw new Error(`\u8349\u6848\u91CC\u6CA1\u6709\u8282\u70B9 ${id} \u7684 spawn`);
|
|
20645
|
+
if (op.title !== void 0) target.title = op.title;
|
|
20646
|
+
if (op.fields) target.fields = { ...target.fields ?? {}, ...op.fields };
|
|
20647
|
+
if (op.description !== void 0) {
|
|
20648
|
+
target.description = op.description;
|
|
20649
|
+
const ann = draft.plan.ops.find((o) => o.action === "annotate" && o.artifactId === id);
|
|
20650
|
+
if (ann && ann.action === "annotate") ann.body = op.description;
|
|
20651
|
+
else draft.plan.ops.push({ action: "annotate", artifactId: id, body: op.description, blocks: false });
|
|
20652
|
+
}
|
|
20653
|
+
break;
|
|
20654
|
+
}
|
|
20655
|
+
case "link": {
|
|
20656
|
+
const consumer = resolveRef(op.artifactId, draft);
|
|
20657
|
+
const upstream = resolveRef(op.to, draft);
|
|
20658
|
+
draft.plan.ops.push({ action: "link", artifactId: consumer, to: upstream, ...op.required !== void 0 ? { required: op.required } : {} });
|
|
20659
|
+
break;
|
|
20660
|
+
}
|
|
20661
|
+
case "unlink": {
|
|
20662
|
+
const consumer = resolveRef(op.artifactId, draft);
|
|
20663
|
+
const upstream = resolveRef(op.to, draft);
|
|
20664
|
+
draft.plan.ops = draft.plan.ops.filter((o) => !(o.action === "link" && o.artifactId === consumer && o.to === upstream));
|
|
20665
|
+
for (const o of draft.plan.ops) if (o.action === "spawn" && o.id === consumer && o.inputs) o.inputs = o.inputs.filter((e) => e.to !== upstream);
|
|
20666
|
+
break;
|
|
20667
|
+
}
|
|
20668
|
+
case "seal": {
|
|
20669
|
+
if (op.reason !== "cancelled") throw new Error(`\u8349\u6848\u8282\u70B9\u53EA\u652F\u6301 cancel\uFF08\u5220\u9664\uFF09\uFF0C\u4E0D\u652F\u6301 ${op.reason}\u2014\u2014\u8282\u70B9\u8FD8\u6CA1\u843D\u5E93`);
|
|
20670
|
+
const id = resolveRef(op.artifactId, draft);
|
|
20671
|
+
draft.plan.ops = draft.plan.ops.filter((o) => {
|
|
20672
|
+
if (o.action === "spawn" && o.id === id) return false;
|
|
20673
|
+
if (o.action === "annotate" && o.artifactId === id) return false;
|
|
20674
|
+
if (o.action === "link" && (o.artifactId === id || o.to === id)) return false;
|
|
20675
|
+
return true;
|
|
20676
|
+
});
|
|
20677
|
+
for (const o of draft.plan.ops) if (o.action === "spawn" && o.inputs) o.inputs = o.inputs.filter((e) => e.to !== id);
|
|
20678
|
+
break;
|
|
20679
|
+
}
|
|
20680
|
+
default:
|
|
20681
|
+
throw new Error(`\u8349\u6848\u7F16\u8F91\u6682\u4E0D\u652F\u6301 ${op.action}\uFF08\u8349\u6848\u9636\u6BB5\u53EA\u652F\u6301 spawn / edit / link / unlink / cancel\uFF09`);
|
|
20682
|
+
}
|
|
20683
|
+
}
|
|
20684
|
+
var spawnOps, candidatesOf;
|
|
20685
|
+
var init_draft_edit = __esm({
|
|
20686
|
+
"../server/src/governance/draft-edit.ts"() {
|
|
20687
|
+
"use strict";
|
|
20688
|
+
init_src2();
|
|
20689
|
+
spawnOps = (draft) => draft.plan.ops.filter((o) => o.action === "spawn" && !!o.id);
|
|
20690
|
+
candidatesOf = (draft) => spawnOps(draft).map((o) => ({ id: o.id, type: o.type, ...o.title ? { title: o.title } : {} }));
|
|
20691
|
+
}
|
|
20692
|
+
});
|
|
20693
|
+
|
|
20694
|
+
// ../server/src/coordinator/identity.ts
|
|
20695
|
+
function isCoordinatorAgentName(name) {
|
|
20696
|
+
return COORDINATOR_NAME_RE.test(name);
|
|
20697
|
+
}
|
|
20698
|
+
function isActiveCoordinatorAgent(actor) {
|
|
20699
|
+
return actor.kind === "agent" && actor.status === "active" && isCoordinatorAgentName(actor.name);
|
|
20700
|
+
}
|
|
20701
|
+
async function listActiveCoordinatorAgents(registry2) {
|
|
20702
|
+
if (!registry2) return [];
|
|
20703
|
+
return (await registry2.listActors({ kind: "agent", status: "active" })).filter(isActiveCoordinatorAgent);
|
|
20704
|
+
}
|
|
20705
|
+
async function resolveCoordinatorRequirement(registry2) {
|
|
20706
|
+
const candidates = await listActiveCoordinatorAgents(registry2);
|
|
20707
|
+
const actor = candidates[0];
|
|
20708
|
+
if (!actor) {
|
|
20709
|
+
return {
|
|
20710
|
+
ok: false,
|
|
20711
|
+
code: "coordinator_missing",
|
|
20712
|
+
message: "\u7CFB\u7EDF\u7F3A\u5C11\u534F\u8C03\u8005\u667A\u80FD\u4F53\u3002\u8BF7\u5148\u521B\u5EFA\u4E00\u4E2A\u540D\u79F0\u5305\u542B\u201C\u534F\u8C03\u8005\u201D\u6216\u201Ccoordinator\u201D\u7684\u6D3B\u8DC3\u667A\u80FD\u4F53\uFF0C\u5E76\u7ED1\u5B9A\u53EF\u7528\u8FD0\u884C\u65F6\u3002"
|
|
20713
|
+
};
|
|
20714
|
+
}
|
|
20715
|
+
const binding = await registry2.getBinding(actor.id);
|
|
20716
|
+
if (!binding || binding.status !== "active") {
|
|
20717
|
+
return {
|
|
20718
|
+
ok: false,
|
|
20719
|
+
actor,
|
|
20720
|
+
code: "coordinator_binding_missing",
|
|
20721
|
+
message: `${actor.name}\uFF08${actor.id}\uFF09\u7F3A\u5C11 active runtime \u7ED1\u5B9A\u3002\u534F\u8C03\u8005\u65E0\u6CD5\u5904\u7406\u540E\u7EED gap / \u6B7B\u9501 / \u6539\u56FE\u6D41\u7A0B\u3002`
|
|
20722
|
+
};
|
|
20723
|
+
}
|
|
20724
|
+
return { ok: true, actor, binding };
|
|
20725
|
+
}
|
|
20726
|
+
async function resolveCoordinator(registry2) {
|
|
20727
|
+
const resolved = await resolveCoordinatorRequirement(registry2);
|
|
20728
|
+
return resolved.ok && resolved.actor && resolved.binding ? { actor: resolved.actor, binding: resolved.binding } : null;
|
|
20729
|
+
}
|
|
20730
|
+
var LEGACY_COORDINATOR_ACTOR, COORDINATOR_NAME_RE;
|
|
20731
|
+
var init_identity = __esm({
|
|
20732
|
+
"../server/src/coordinator/identity.ts"() {
|
|
20733
|
+
"use strict";
|
|
20734
|
+
LEGACY_COORDINATOR_ACTOR = "actor:agent:coordinator";
|
|
20735
|
+
COORDINATOR_NAME_RE = /协调者|coordinator/i;
|
|
20736
|
+
}
|
|
20737
|
+
});
|
|
20738
|
+
|
|
20559
20739
|
// ../server/src/domains/collab/planner.ts
|
|
20560
20740
|
function ensureDefaultWorkorderTypes(schema) {
|
|
20561
20741
|
const out = [...schema];
|
|
@@ -20576,6 +20756,46 @@ function upgradeDefaultFieldDefs(schema) {
|
|
|
20576
20756
|
}
|
|
20577
20757
|
return changed;
|
|
20578
20758
|
}
|
|
20759
|
+
function isPlannerCoordinator(actor) {
|
|
20760
|
+
return actor.kind === "agent" && (actor.roles.includes("coordinator") || isCoordinatorAgentName(actor.name));
|
|
20761
|
+
}
|
|
20762
|
+
async function plannerAvailableActors(registry2) {
|
|
20763
|
+
if (!registry2) return [];
|
|
20764
|
+
const active = await registry2.listActors({ status: "active" });
|
|
20765
|
+
const out = [];
|
|
20766
|
+
for (const actor of active) {
|
|
20767
|
+
if (isPlannerCoordinator(actor)) continue;
|
|
20768
|
+
const roles = actor.roles.filter((role) => role !== "coordinator");
|
|
20769
|
+
if (roles.length === 0) continue;
|
|
20770
|
+
if (actor.kind === "agent") {
|
|
20771
|
+
const binding = await registry2.getBinding(actor.id);
|
|
20772
|
+
if (!binding || binding.status !== "active") continue;
|
|
20773
|
+
}
|
|
20774
|
+
out.push({
|
|
20775
|
+
id: actor.id,
|
|
20776
|
+
name: actor.name,
|
|
20777
|
+
roles,
|
|
20778
|
+
kind: actor.kind
|
|
20779
|
+
});
|
|
20780
|
+
}
|
|
20781
|
+
return out;
|
|
20782
|
+
}
|
|
20783
|
+
async function plannerArtifactTypes(schema, registry2) {
|
|
20784
|
+
if (!registry2) {
|
|
20785
|
+
return schema.filter((def) => def.name !== "brief").map((def) => ({
|
|
20786
|
+
name: def.name,
|
|
20787
|
+
ownerRole: def.ownerRole,
|
|
20788
|
+
contentType: def.contentType
|
|
20789
|
+
}));
|
|
20790
|
+
}
|
|
20791
|
+
const actors = await plannerAvailableActors(registry2);
|
|
20792
|
+
const staffableRoles = new Set(actors.flatMap((actor) => actor.roles));
|
|
20793
|
+
return schema.filter((def) => def.name !== "brief" && staffableRoles.has(def.ownerRole)).map((def) => ({
|
|
20794
|
+
name: def.name,
|
|
20795
|
+
ownerRole: def.ownerRole,
|
|
20796
|
+
contentType: def.contentType
|
|
20797
|
+
}));
|
|
20798
|
+
}
|
|
20579
20799
|
function parseWorkorderSpec(body) {
|
|
20580
20800
|
const first = body.split("\n", 1)[0] ?? "";
|
|
20581
20801
|
if (!first.startsWith(WORKORDER_SPEC_PREFIX)) return null;
|
|
@@ -20602,6 +20822,8 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
20602
20822
|
const primaryType = schemaByName.has(input.requestedType) && input.requestedType !== "brief" ? input.requestedType : "prd";
|
|
20603
20823
|
const primaryId = input.requestedArtifactId && input.requestedType === primaryType ? input.requestedArtifactId : `artifact:${primaryType}:${slug4}`;
|
|
20604
20824
|
const issues = [...input.agentIssues ?? []];
|
|
20825
|
+
const enforceStaffing = Boolean(input.registry);
|
|
20826
|
+
const staffableTypes = new Set((await plannerArtifactTypes(schema, input.registry)).map((def) => def.name));
|
|
20605
20827
|
const roleCache = /* @__PURE__ */ new Map();
|
|
20606
20828
|
const resolveRole = async (role, artifactType) => {
|
|
20607
20829
|
const hit = roleCache.get(role);
|
|
@@ -20653,6 +20875,7 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
20653
20875
|
continue;
|
|
20654
20876
|
}
|
|
20655
20877
|
const owner = await resolveRole(def.ownerRole, node.type);
|
|
20878
|
+
if (enforceStaffing && (!owner.actor || !staffableTypes.has(node.type))) continue;
|
|
20656
20879
|
const ownerActor = owner.actor ?? `pending:role:${def.ownerRole}`;
|
|
20657
20880
|
availableNodes.push({
|
|
20658
20881
|
type: node.type,
|
|
@@ -20674,6 +20897,7 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
20674
20897
|
}
|
|
20675
20898
|
const owner = await resolveRole(def.ownerRole, type);
|
|
20676
20899
|
const ownerActor = owner.actor ?? (type === primaryType ? input.actor : `pending:role:${def.ownerRole}`);
|
|
20900
|
+
if (enforceStaffing && !owner.actor && type !== primaryType) continue;
|
|
20677
20901
|
const id = type === primaryType ? primaryId : `artifact:${type}:${slug4}`;
|
|
20678
20902
|
availableNodes.push({ type, id, role: def.ownerRole, owner: ownerActor });
|
|
20679
20903
|
}
|
|
@@ -20980,6 +21204,7 @@ var init_planner = __esm({
|
|
|
20980
21204
|
"../server/src/domains/collab/planner.ts"() {
|
|
20981
21205
|
"use strict";
|
|
20982
21206
|
init_src2();
|
|
21207
|
+
init_identity();
|
|
20983
21208
|
DEFAULT_TYPES = [
|
|
20984
21209
|
// brief 的 goal/acceptanceCriteria 现在是【自定义字段】(Phase 3,替代旧 OASIS_WORKORDER_SPEC 批注,铲 #1 死锁根)
|
|
20985
21210
|
{ name: "brief", contentType: "text", ownerRole: "pm", fieldDefs: [
|
|
@@ -21265,8 +21490,19 @@ async function runCommand(kernel, blobs, oplog, actor, command, args, ctx = {})
|
|
|
21265
21490
|
if (createdViaArg !== void 0 && createdViaArg !== "seed" && createdViaArg !== "manual-spawn") {
|
|
21266
21491
|
throw new Error(`createdVia \u4EC5\u63A5\u53D7 seed\uFF08\u53D1\u8D77\u5DE5\u5355\uFF09\u6216 manual-spawn\uFF1Bauto-trigger/prereq \u7531\u5185\u6838\u5185\u90E8\u8BBE\u7F6E`);
|
|
21267
21492
|
}
|
|
21268
|
-
|
|
21493
|
+
let workspace = optStr(args, "workspace");
|
|
21494
|
+
if (workspace === void 0) {
|
|
21495
|
+
workspace = createdViaArg === "seed" ? `ws:wo-${(await import("node:crypto")).randomUUID().slice(0, 8)}` : "ws:default";
|
|
21496
|
+
}
|
|
21269
21497
|
const projectId = optStr(args, "project_id") ?? optStr(args, "projectId");
|
|
21498
|
+
const schemaForSeed = (createdViaArg ?? "manual-spawn") === "seed" ? ctx.schema : void 0;
|
|
21499
|
+
let coordinatorRequirement;
|
|
21500
|
+
if (schemaForSeed) {
|
|
21501
|
+
coordinatorRequirement = await resolveCoordinatorRequirement(ctx.registry);
|
|
21502
|
+
if (!coordinatorRequirement.ok && args["allowMissingCoordinator"] !== true) {
|
|
21503
|
+
throw new CoordinatorRequiredError(coordinatorRequirement);
|
|
21504
|
+
}
|
|
21505
|
+
}
|
|
21270
21506
|
if (projectId !== void 0) {
|
|
21271
21507
|
if (!ctx.projectState || !ctx.artifactState) {
|
|
21272
21508
|
throw new Error("spawn \u5E26 project_id \u9700\u8981\u670D\u52A1\u7AEF\u6CE8\u5165\u9879\u76EE\u5B58\u50A8\u4E0E\u5DE5\u5355\u7ED1\u5B9A\u5B58\u50A8");
|
|
@@ -21275,7 +21511,7 @@ async function runCommand(kernel, blobs, oplog, actor, command, args, ctx = {})
|
|
|
21275
21511
|
if (!project) throw new Error(`project not found: ${projectId}`);
|
|
21276
21512
|
await ctx.artifactState.upsertWorkspaceBinding({ workOrderId: workspace, projectId });
|
|
21277
21513
|
}
|
|
21278
|
-
if (
|
|
21514
|
+
if (schemaForSeed) {
|
|
21279
21515
|
const title = optStr(args, "title") ?? optStr(args, "description") ?? workspace;
|
|
21280
21516
|
const brief = optStr(args, "brief") ?? optStr(args, "body") ?? optStr(args, "description") ?? workspace;
|
|
21281
21517
|
const goal = optStr(args, "goal");
|
|
@@ -21286,6 +21522,14 @@ async function runCommand(kernel, blobs, oplog, actor, command, args, ctx = {})
|
|
|
21286
21522
|
\u9A8C\u6536\u6807\u51C6\uFF1A
|
|
21287
21523
|
${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
21288
21524
|
const agentIssues = [];
|
|
21525
|
+
if (coordinatorRequirement && !coordinatorRequirement.ok) {
|
|
21526
|
+
agentIssues.push({
|
|
21527
|
+
code: "missing_coordinator",
|
|
21528
|
+
severity: "warning",
|
|
21529
|
+
role: "coordinator",
|
|
21530
|
+
message: `${coordinatorRequirement.message} \u7528\u6237\u5DF2\u786E\u8BA4\u7EE7\u7EED\u521B\u5EFA\uFF1B\u540E\u7EED gap / \u6B7B\u9501 / \u6539\u56FE\u6D41\u7A0B\u53EF\u80FD\u65E0\u6CD5\u81EA\u52A8\u5904\u7406\u3002`
|
|
21531
|
+
});
|
|
21532
|
+
}
|
|
21289
21533
|
let agentPlan = null;
|
|
21290
21534
|
const inlinePlan = args["plan"];
|
|
21291
21535
|
if (inlinePlan !== void 0 && inlinePlan !== null) {
|
|
@@ -21296,22 +21540,14 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
21296
21540
|
}
|
|
21297
21541
|
if (agentPlan === null && ctx.planWorkorderWithAgent) {
|
|
21298
21542
|
try {
|
|
21299
|
-
const availableActors =
|
|
21300
|
-
|
|
21301
|
-
name: a.name,
|
|
21302
|
-
roles: a.roles,
|
|
21303
|
-
kind: a.kind
|
|
21304
|
-
})) : [];
|
|
21543
|
+
const availableActors = await plannerAvailableActors(ctx.registry);
|
|
21544
|
+
const artifactTypes = await plannerArtifactTypes(schemaForSeed, ctx.registry);
|
|
21305
21545
|
const attempt = await ctx.planWorkorderWithAgent({
|
|
21306
21546
|
requestedType: type,
|
|
21307
21547
|
workspace,
|
|
21308
21548
|
title,
|
|
21309
21549
|
brief: briefForPlanner,
|
|
21310
|
-
artifactTypes
|
|
21311
|
-
name: def.name,
|
|
21312
|
-
ownerRole: def.ownerRole,
|
|
21313
|
-
contentType: def.contentType
|
|
21314
|
-
})),
|
|
21550
|
+
artifactTypes,
|
|
21315
21551
|
availableActors
|
|
21316
21552
|
});
|
|
21317
21553
|
if (attempt) {
|
|
@@ -21342,7 +21578,7 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
21342
21578
|
...goal !== void 0 ? { goal } : {},
|
|
21343
21579
|
...acceptanceCriteria && acceptanceCriteria.length > 0 ? { acceptanceCriteria } : {},
|
|
21344
21580
|
actor,
|
|
21345
|
-
schema:
|
|
21581
|
+
schema: schemaForSeed,
|
|
21346
21582
|
...optStr(args, "id") !== void 0 ? { requestedArtifactId: optStr(args, "id") } : {},
|
|
21347
21583
|
...ctx.registry ? { registry: ctx.registry } : {},
|
|
21348
21584
|
...agentPlan ? { agentPlan } : {},
|
|
@@ -21739,8 +21975,9 @@ async function runView(kernel, oplog, blobs, name, artifactId, params) {
|
|
|
21739
21975
|
case "ls":
|
|
21740
21976
|
return [...model.artifacts.values()].map((a) => {
|
|
21741
21977
|
const lifecycle = lifecycleOf(model, a.id);
|
|
21742
|
-
const
|
|
21743
|
-
|
|
21978
|
+
const queue = queueOf(model, a.id).length;
|
|
21979
|
+
const mark = lifecycle !== "active" ? lifecycle : pendingConcludeAttempt(model, a.id) ? "gate-pending" : blockedOf(model, a.id).blocked ? "blocked" : isConcluded(model, a.id) ? "concluded" : a.currentRev || queue > 0 ? "active" : "embryo";
|
|
21980
|
+
return { ...a, queue, mark };
|
|
21744
21981
|
});
|
|
21745
21982
|
case "show": {
|
|
21746
21983
|
const id = need2();
|
|
@@ -21842,6 +22079,7 @@ async function runView(kernel, oplog, blobs, name, artifactId, params) {
|
|
|
21842
22079
|
async function startOasisServer(opts) {
|
|
21843
22080
|
const resolveActor = opts.resolveActor ?? defaultResolveActor;
|
|
21844
22081
|
const stagingBuffers = /* @__PURE__ */ new Map();
|
|
22082
|
+
const stagingWorkspace = /* @__PURE__ */ new Map();
|
|
21845
22083
|
const resolveEngine = opts.resolveEngine ?? (async () => ({ kernel: opts.kernel, oplog: opts.oplog, blobs: opts.blobs, ...opts.registry ? { registry: opts.registry } : {} }));
|
|
21846
22084
|
const domainRouter = new Router();
|
|
21847
22085
|
for (const register of opts.domains ?? []) register(domainRouter);
|
|
@@ -21924,6 +22162,10 @@ async function startOasisServer(opts) {
|
|
|
21924
22162
|
}));
|
|
21925
22163
|
return;
|
|
21926
22164
|
}
|
|
22165
|
+
if (url.pathname === "/api/whoami" && req.method === "GET") {
|
|
22166
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ actor }));
|
|
22167
|
+
return;
|
|
22168
|
+
}
|
|
21927
22169
|
const sessionKey = (req.headers.authorization ?? "").startsWith("Bearer ") ? req.headers.authorization.slice("Bearer ".length) : actor;
|
|
21928
22170
|
const chunks = [];
|
|
21929
22171
|
for await (const chunk of req) chunks.push(chunk);
|
|
@@ -21945,8 +22187,38 @@ async function startOasisServer(opts) {
|
|
|
21945
22187
|
...opts.workorderDrafts ? { workorderDrafts: opts.workorderDrafts } : {},
|
|
21946
22188
|
...opts.gitEnv ? { gitEnv: opts.gitEnv } : {}
|
|
21947
22189
|
});
|
|
22190
|
+
if (body.command === "spawn" && opts.chatSession) {
|
|
22191
|
+
const workspace = body.args?.["workspace"];
|
|
22192
|
+
if (workspace && workspace !== "ws:default") {
|
|
22193
|
+
const chatSid = req.headers["x-chat-session-id"];
|
|
22194
|
+
if (typeof chatSid === "string") {
|
|
22195
|
+
await opts.chatSession.linkWorkOrder(chatSid, workspace).catch((err) => {
|
|
22196
|
+
console.error(`[chat-session] Failed to link work order ${workspace} to session ${chatSid}:`, err);
|
|
22197
|
+
});
|
|
22198
|
+
}
|
|
22199
|
+
}
|
|
22200
|
+
}
|
|
22201
|
+
if (body.command === "spawn" && opts.workorderDrafts) {
|
|
22202
|
+
const draftId = result?.data?.draftId;
|
|
22203
|
+
const chatSid = req.headers["x-chat-session-id"];
|
|
22204
|
+
if (draftId && typeof chatSid === "string") opts.workorderDrafts.tagSession(draftId, chatSid);
|
|
22205
|
+
}
|
|
21948
22206
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(result));
|
|
21949
22207
|
} catch (err) {
|
|
22208
|
+
if (err instanceof CoordinatorRequiredError) {
|
|
22209
|
+
res.writeHead(err.status, { "content-type": "application/json" }).end(JSON.stringify({
|
|
22210
|
+
error: {
|
|
22211
|
+
code: err.code,
|
|
22212
|
+
message: err.message,
|
|
22213
|
+
canContinueWithOverride: true,
|
|
22214
|
+
overrideField: "allowMissingCoordinator",
|
|
22215
|
+
overrideValue: true,
|
|
22216
|
+
reason: err.requirement.code,
|
|
22217
|
+
coordinatorActorId: err.requirement.actor?.id
|
|
22218
|
+
}
|
|
22219
|
+
}));
|
|
22220
|
+
return;
|
|
22221
|
+
}
|
|
21950
22222
|
const status = err instanceof FrozenError ? 409 : 400;
|
|
21951
22223
|
res.writeHead(status, { "content-type": "application/json" }).end(
|
|
21952
22224
|
JSON.stringify({
|
|
@@ -21983,7 +22255,8 @@ async function startOasisServer(opts) {
|
|
|
21983
22255
|
}
|
|
21984
22256
|
if (url.pathname === "/api/workorder/draft" && req.method === "GET" && opts.workorderDrafts) {
|
|
21985
22257
|
const workspace = url.searchParams.get("workspace");
|
|
21986
|
-
const
|
|
22258
|
+
const session = url.searchParams.get("session");
|
|
22259
|
+
const draft = workspace ? opts.workorderDrafts.forWorkspace(workspace) : session ? opts.workorderDrafts.forSession(session) : void 0;
|
|
21987
22260
|
if (!draft) {
|
|
21988
22261
|
res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: "no staged workorder draft" }));
|
|
21989
22262
|
return;
|
|
@@ -22005,6 +22278,11 @@ async function startOasisServer(opts) {
|
|
|
22005
22278
|
if (body.dispatch === false && brief?.workspace) {
|
|
22006
22279
|
await opts.artifactState?.setWorkspaceDispatchHold(brief.workspace, true);
|
|
22007
22280
|
}
|
|
22281
|
+
if (opts.chatSession && body.chatSessionId && brief?.workspace && brief.workspace !== "ws:default") {
|
|
22282
|
+
await opts.chatSession.linkWorkOrder(body.chatSessionId, brief.workspace).catch((err) => {
|
|
22283
|
+
console.error(`[chat-session] Failed to link work order ${brief.workspace} to session ${body.chatSessionId}:`, err);
|
|
22284
|
+
});
|
|
22285
|
+
}
|
|
22008
22286
|
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
22009
22287
|
JSON.stringify({ ...result, briefArtifactId: body.briefArtifactId, workspace: brief?.workspace ?? null })
|
|
22010
22288
|
);
|
|
@@ -22089,11 +22367,29 @@ async function startOasisServer(opts) {
|
|
|
22089
22367
|
}
|
|
22090
22368
|
if (url.pathname === "/api/stage" && req.method === "POST") {
|
|
22091
22369
|
try {
|
|
22092
|
-
const { op } = JSON.parse(rawBody);
|
|
22370
|
+
const { op, workspace } = JSON.parse(rawBody);
|
|
22093
22371
|
if (!op || typeof op.action !== "string") throw new Error("\u7F3A\u5C11 op\uFF08{action,...}\uFF09");
|
|
22372
|
+
if (opts.workorderDrafts) {
|
|
22373
|
+
let ws = typeof workspace === "string" && workspace.trim() ? workspace.trim() : void 0;
|
|
22374
|
+
if (!ws) {
|
|
22375
|
+
const ref = op.artifactId;
|
|
22376
|
+
if (typeof ref === "string" && ref.startsWith("artifact:"))
|
|
22377
|
+
ws = opts.workorderDrafts.list().find((d) => d.plan.ops.some((o) => o.action === "spawn" && o.id === ref))?.workspace;
|
|
22378
|
+
}
|
|
22379
|
+
const draft = ws ? opts.workorderDrafts.forWorkspace(ws) : void 0;
|
|
22380
|
+
if (draft) {
|
|
22381
|
+
editDraftPlan(draft, op);
|
|
22382
|
+
draft.at = (/* @__PURE__ */ new Date()).toISOString();
|
|
22383
|
+
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
22384
|
+
JSON.stringify({ draft: true, workspace: draft.workspace, nodes: draft.plan.ops.filter((o) => o.action === "spawn").length, staged: op })
|
|
22385
|
+
);
|
|
22386
|
+
return;
|
|
22387
|
+
}
|
|
22388
|
+
}
|
|
22094
22389
|
const buf = stagingBuffers.get(sessionKey) ?? [];
|
|
22095
22390
|
buf.push(op);
|
|
22096
22391
|
stagingBuffers.set(sessionKey, buf);
|
|
22392
|
+
if (typeof workspace === "string" && workspace.trim()) stagingWorkspace.set(sessionKey, workspace.trim());
|
|
22097
22393
|
const cascade = op.action === "seal" && op.reason === "cancelled" ? engine.kernel.cancelCascadeEdges(op.artifactId) : void 0;
|
|
22098
22394
|
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
22099
22395
|
JSON.stringify({ count: buf.length, staged: op, ...cascade && cascade.length > 0 ? { cascade } : {} })
|
|
@@ -22107,10 +22403,11 @@ async function startOasisServer(opts) {
|
|
|
22107
22403
|
}
|
|
22108
22404
|
if (url.pathname === "/api/stage" && req.method === "GET") {
|
|
22109
22405
|
const ops = stagingBuffers.get(sessionKey) ?? [];
|
|
22406
|
+
const ws = stagingWorkspace.get(sessionKey);
|
|
22110
22407
|
let preview2 = null;
|
|
22111
22408
|
if (ops.length > 0) {
|
|
22112
22409
|
try {
|
|
22113
|
-
preview2 = await engine.kernel.previewIntervention({ reason: "(staged)", ops });
|
|
22410
|
+
preview2 = await engine.kernel.previewIntervention({ reason: "(staged)", ops, ...ws ? { workspace: ws } : {} });
|
|
22114
22411
|
} catch (err) {
|
|
22115
22412
|
preview2 = { error: err instanceof Error ? err.message : String(err) };
|
|
22116
22413
|
}
|
|
@@ -22120,6 +22417,7 @@ async function startOasisServer(opts) {
|
|
|
22120
22417
|
}
|
|
22121
22418
|
if (url.pathname === "/api/stage" && req.method === "DELETE") {
|
|
22122
22419
|
stagingBuffers.delete(sessionKey);
|
|
22420
|
+
stagingWorkspace.delete(sessionKey);
|
|
22123
22421
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ cleared: true }));
|
|
22124
22422
|
return;
|
|
22125
22423
|
}
|
|
@@ -22131,7 +22429,8 @@ async function startOasisServer(opts) {
|
|
|
22131
22429
|
return;
|
|
22132
22430
|
}
|
|
22133
22431
|
const { reason } = JSON.parse(rawBody || "{}");
|
|
22134
|
-
const
|
|
22432
|
+
const ws = stagingWorkspace.get(sessionKey);
|
|
22433
|
+
const plan = { reason: reason?.trim() || "(\u534F\u8C03\u8005 staged \u6539\u56FE)", ops, ...ws ? { workspace: ws } : {} };
|
|
22135
22434
|
const preview2 = await engine.kernel.previewIntervention(plan);
|
|
22136
22435
|
if (!preview2.endState.ok) {
|
|
22137
22436
|
throw new Error(`\u7EC8\u6001\u6821\u9A8C\u4E0D\u8FC7\uFF08\u673A\u68B0\u786C\u62E6\uFF09\uFF1A${preview2.endState.violations.join("\uFF1B")}`);
|
|
@@ -22139,6 +22438,7 @@ async function startOasisServer(opts) {
|
|
|
22139
22438
|
if (preview2.changeClass === "B") {
|
|
22140
22439
|
const result = await engine.kernel.applyIntervention(plan, preview2.baseSeqs, actor);
|
|
22141
22440
|
stagingBuffers.delete(sessionKey);
|
|
22441
|
+
stagingWorkspace.delete(sessionKey);
|
|
22142
22442
|
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
22143
22443
|
JSON.stringify({ outcome: "applied", applied: result.applied, interventionId: result.interventionId })
|
|
22144
22444
|
);
|
|
@@ -22154,6 +22454,7 @@ async function startOasisServer(opts) {
|
|
|
22154
22454
|
downstreamClosure: preview2.blastRadius.downstreamClosure
|
|
22155
22455
|
});
|
|
22156
22456
|
stagingBuffers.delete(sessionKey);
|
|
22457
|
+
stagingWorkspace.delete(sessionKey);
|
|
22157
22458
|
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
22158
22459
|
JSON.stringify({ outcome: "drafted", draftId: draft.id, changeClass: draft.changeClass, nonMonotonicOps: draft.nonMonotonicOps })
|
|
22159
22460
|
);
|
|
@@ -22249,10 +22550,33 @@ async function startOasisServer(opts) {
|
|
|
22249
22550
|
}
|
|
22250
22551
|
return;
|
|
22251
22552
|
}
|
|
22553
|
+
if (url.pathname === "/api/chat/attachments" && req.method === "POST" && opts.uploadChatAttachment) {
|
|
22554
|
+
try {
|
|
22555
|
+
const body = JSON.parse(rawBody);
|
|
22556
|
+
if (typeof body.contentBase64 !== "string" || !body.contentBase64) throw new Error("\u7F3A\u5C11 contentBase64");
|
|
22557
|
+
const out = await opts.uploadChatAttachment({
|
|
22558
|
+
contentBase64: body.contentBase64,
|
|
22559
|
+
...typeof body.contentType === "string" ? { contentType: body.contentType } : {},
|
|
22560
|
+
...typeof body.name === "string" ? { name: body.name } : {}
|
|
22561
|
+
});
|
|
22562
|
+
res.writeHead(201, { "content-type": "application/json" });
|
|
22563
|
+
res.end(JSON.stringify(out));
|
|
22564
|
+
} catch (err) {
|
|
22565
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
22566
|
+
res.end(JSON.stringify({ error: { code: "ATTACHMENT_UPLOAD_FAILED", message: err instanceof Error ? err.message : String(err) } }));
|
|
22567
|
+
}
|
|
22568
|
+
return;
|
|
22569
|
+
}
|
|
22252
22570
|
if (url.pathname === "/api/chat" && req.method === "POST") {
|
|
22253
22571
|
try {
|
|
22254
22572
|
const body = JSON.parse(rawBody);
|
|
22255
22573
|
if (!body.message) throw new Error("\u7F3A\u5C11 message");
|
|
22574
|
+
const attachments = Array.isArray(body.attachments) ? body.attachments.filter((a) => typeof a?.name === "string" && (typeof a?.text === "string" || typeof a?.blobRef === "string")).map((a) => ({
|
|
22575
|
+
name: a.name,
|
|
22576
|
+
...typeof a.text === "string" ? { text: a.text } : {},
|
|
22577
|
+
...typeof a.blobRef === "string" ? { blobRef: a.blobRef } : {},
|
|
22578
|
+
...typeof a.contentType === "string" ? { contentType: a.contentType } : {}
|
|
22579
|
+
})) : [];
|
|
22256
22580
|
const typedParts = wantsChatParts(req);
|
|
22257
22581
|
if (opts.dispatchChat) {
|
|
22258
22582
|
if (!body.actorId) {
|
|
@@ -22261,7 +22585,9 @@ async function startOasisServer(opts) {
|
|
|
22261
22585
|
const session = await opts.dispatchChat({
|
|
22262
22586
|
actorId: body.actorId,
|
|
22263
22587
|
message: body.message,
|
|
22264
|
-
...body.sessionId ? { sessionId: body.sessionId } : {}
|
|
22588
|
+
...body.sessionId ? { sessionId: body.sessionId } : {},
|
|
22589
|
+
...body.chatSessionId ? { chatSessionId: body.chatSessionId } : {},
|
|
22590
|
+
...attachments.length ? { attachments } : {}
|
|
22265
22591
|
});
|
|
22266
22592
|
let finished = false;
|
|
22267
22593
|
res.on("close", () => {
|
|
@@ -22277,8 +22603,9 @@ async function startOasisServer(opts) {
|
|
|
22277
22603
|
res.writeHead(200, {
|
|
22278
22604
|
"content-type": "application/x-ndjson; charset=utf-8",
|
|
22279
22605
|
"transfer-encoding": "chunked",
|
|
22280
|
-
"access-control-expose-headers": "X-Session-Id",
|
|
22281
|
-
"x-session-id": session.id
|
|
22606
|
+
"access-control-expose-headers": "X-Session-Id, X-Run-Id",
|
|
22607
|
+
"x-session-id": session.id,
|
|
22608
|
+
...session.runId ? { "x-run-id": session.runId } : {}
|
|
22282
22609
|
});
|
|
22283
22610
|
let sawTextOutput = false;
|
|
22284
22611
|
session.onOutput((chunk) => {
|
|
@@ -22382,7 +22709,7 @@ async function startOasisServer(opts) {
|
|
|
22382
22709
|
];
|
|
22383
22710
|
const child = spawn6(opts.claudeBin ?? "claude", args, {
|
|
22384
22711
|
...spawnCwd ? { cwd: spawnCwd } : {},
|
|
22385
|
-
env: { ...process.env, ...env },
|
|
22712
|
+
env: { ...process.env, ...env, ...body.chatSessionId ? { OASIS_CHAT_SESSION_ID: body.chatSessionId } : {} },
|
|
22386
22713
|
stdio: ["ignore", "pipe", "pipe"]
|
|
22387
22714
|
});
|
|
22388
22715
|
if (typedParts) {
|
|
@@ -22663,7 +22990,7 @@ async function startOasisServer(opts) {
|
|
|
22663
22990
|
})
|
|
22664
22991
|
};
|
|
22665
22992
|
}
|
|
22666
|
-
var http, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, GOVERNANCE_COMMANDS, isAgent;
|
|
22993
|
+
var http, CoordinatorRequiredError, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, GOVERNANCE_COMMANDS, isAgent;
|
|
22667
22994
|
var init_server3 = __esm({
|
|
22668
22995
|
"../server/src/server.ts"() {
|
|
22669
22996
|
"use strict";
|
|
@@ -22675,7 +23002,17 @@ var init_server3 = __esm({
|
|
|
22675
23002
|
init_router();
|
|
22676
23003
|
init_drafts();
|
|
22677
23004
|
init_workorder_drafts();
|
|
23005
|
+
init_draft_edit();
|
|
22678
23006
|
init_planner();
|
|
23007
|
+
init_identity();
|
|
23008
|
+
CoordinatorRequiredError = class extends Error {
|
|
23009
|
+
constructor(requirement) {
|
|
23010
|
+
super(requirement.message ?? "\u7CFB\u7EDF\u7F3A\u5C11\u534F\u8C03\u8005\u667A\u80FD\u4F53\u3002");
|
|
23011
|
+
this.requirement = requirement;
|
|
23012
|
+
}
|
|
23013
|
+
status = 409;
|
|
23014
|
+
code = "COORDINATOR_REQUIRED";
|
|
23015
|
+
};
|
|
22679
23016
|
defaultResolveActor = (token) => token.startsWith("token:") ? token.slice("token:".length) : null;
|
|
22680
23017
|
enc = (s2) => new TextEncoder().encode(s2);
|
|
22681
23018
|
CHAT_OUTPUT_LIMIT = 16e3;
|
|
@@ -22687,29 +23024,53 @@ var init_server3 = __esm({
|
|
|
22687
23024
|
// ../server/src/tokens.ts
|
|
22688
23025
|
function createTokenIssuer() {
|
|
22689
23026
|
const table = /* @__PURE__ */ new Map();
|
|
23027
|
+
const prune = () => {
|
|
23028
|
+
const now = Date.now();
|
|
23029
|
+
for (const [token, entry] of table) {
|
|
23030
|
+
if (entry.expiresAt !== void 0 && entry.expiresAt <= now) table.delete(token);
|
|
23031
|
+
}
|
|
23032
|
+
};
|
|
22690
23033
|
return {
|
|
22691
|
-
issue(actor) {
|
|
23034
|
+
issue(actor, opts = {}) {
|
|
23035
|
+
prune();
|
|
22692
23036
|
const token = `oat_${(0, import_node_crypto3.randomBytes)(24).toString("base64url")}`;
|
|
22693
|
-
|
|
23037
|
+
const issuedAt = Date.now();
|
|
23038
|
+
table.set(token, {
|
|
23039
|
+
actor,
|
|
23040
|
+
issuedAt,
|
|
23041
|
+
...opts.ttlMs !== void 0 ? { expiresAt: issuedAt + opts.ttlMs } : {},
|
|
23042
|
+
...opts.sessionId !== void 0 ? { sessionId: opts.sessionId } : {},
|
|
23043
|
+
...opts.artifactId !== void 0 ? { artifactId: opts.artifactId } : {},
|
|
23044
|
+
...opts.action !== void 0 ? { action: opts.action } : {},
|
|
23045
|
+
...opts.nodeId !== void 0 ? { nodeId: opts.nodeId } : {},
|
|
23046
|
+
...opts.runtimeKind !== void 0 ? { runtimeKind: opts.runtimeKind } : {},
|
|
23047
|
+
...opts.scopes !== void 0 ? { scopes: opts.scopes } : {}
|
|
23048
|
+
});
|
|
22694
23049
|
return token;
|
|
22695
23050
|
},
|
|
22696
23051
|
revoke(token) {
|
|
22697
23052
|
table.delete(token);
|
|
22698
23053
|
},
|
|
22699
23054
|
resolve(token) {
|
|
22700
|
-
|
|
23055
|
+
const entry = table.get(token);
|
|
23056
|
+
if (!entry) return null;
|
|
23057
|
+
if (entry.expiresAt !== void 0 && entry.expiresAt <= Date.now()) {
|
|
23058
|
+
table.delete(token);
|
|
23059
|
+
return null;
|
|
23060
|
+
}
|
|
23061
|
+
return entry.actor;
|
|
22701
23062
|
}
|
|
22702
23063
|
};
|
|
22703
23064
|
}
|
|
22704
23065
|
function createNodeTokenStore(file) {
|
|
22705
23066
|
const read = () => {
|
|
22706
23067
|
try {
|
|
22707
|
-
return JSON.parse(
|
|
23068
|
+
return JSON.parse(fs2.readFileSync(file, "utf8"));
|
|
22708
23069
|
} catch {
|
|
22709
23070
|
return {};
|
|
22710
23071
|
}
|
|
22711
23072
|
};
|
|
22712
|
-
const write = (table) =>
|
|
23073
|
+
const write = (table) => fs2.writeFileSync(file, JSON.stringify(table, null, 2), { mode: 384 });
|
|
22713
23074
|
return {
|
|
22714
23075
|
issue(nodeId) {
|
|
22715
23076
|
const table = read();
|
|
@@ -22735,14 +23096,14 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
22735
23096
|
const read = () => {
|
|
22736
23097
|
if (!file) return table;
|
|
22737
23098
|
try {
|
|
22738
|
-
return new Map(Object.entries(JSON.parse(
|
|
23099
|
+
return new Map(Object.entries(JSON.parse(fs2.readFileSync(file, "utf8"))));
|
|
22739
23100
|
} catch {
|
|
22740
23101
|
return /* @__PURE__ */ new Map();
|
|
22741
23102
|
}
|
|
22742
23103
|
};
|
|
22743
23104
|
const write = (m2) => {
|
|
22744
23105
|
if (!file) return;
|
|
22745
|
-
|
|
23106
|
+
fs2.writeFileSync(file, JSON.stringify(Object.fromEntries(m2), null, 2), { mode: 384 });
|
|
22746
23107
|
};
|
|
22747
23108
|
const table = /* @__PURE__ */ new Map();
|
|
22748
23109
|
const prune = () => {
|
|
@@ -22785,12 +23146,12 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
22785
23146
|
}
|
|
22786
23147
|
};
|
|
22787
23148
|
}
|
|
22788
|
-
var import_node_crypto3,
|
|
23149
|
+
var import_node_crypto3, fs2;
|
|
22789
23150
|
var init_tokens = __esm({
|
|
22790
23151
|
"../server/src/tokens.ts"() {
|
|
22791
23152
|
"use strict";
|
|
22792
23153
|
import_node_crypto3 = require("node:crypto");
|
|
22793
|
-
|
|
23154
|
+
fs2 = __toESM(require("node:fs"), 1);
|
|
22794
23155
|
}
|
|
22795
23156
|
});
|
|
22796
23157
|
|
|
@@ -23904,17 +24265,25 @@ function nodeInfo(model, a) {
|
|
|
23904
24265
|
const lifecycle = lifecycleOf(model, a.id);
|
|
23905
24266
|
const sealed = lifecycle !== "active";
|
|
23906
24267
|
const concluded = isConcluded(model, a.id);
|
|
23907
|
-
const
|
|
23908
|
-
const
|
|
23909
|
-
|
|
24268
|
+
const queueLen = queueOf(model, a.id).length;
|
|
24269
|
+
const mark = sealed ? lifecycle : pendingConcludeAttempt(model, a.id) ? "gate-pending" : blockedOf(model, a.id).blocked ? "blocked" : concluded ? "concluded" : (
|
|
24270
|
+
// embryo 只判"还没动工":无定稿版**且**队列里也没有待审版。智能体已交首版排队待审
|
|
24271
|
+
// (queueLen>0)即已产出,按 active 走,由 deriveNodeStage 落到 review,别误判成 embryo。
|
|
24272
|
+
a.currentRev || queueLen > 0 ? "active" : "embryo"
|
|
24273
|
+
);
|
|
24274
|
+
const revisions = revisionsOf(model, a.id);
|
|
24275
|
+
const hasWorking = revisions.some((r) => r.state === "working");
|
|
24276
|
+
const producedParts = new Set(revisions.filter((r) => r.part !== void 0 && r.state !== "rejected").map((r) => r.part));
|
|
24277
|
+
const hasUnproducedParts = (a.parts ?? []).some((p2) => !producedParts.has(p2.name));
|
|
24278
|
+
return { mark, queueLen, concluded, sealed, hasWorking, hasUnproducedParts };
|
|
23910
24279
|
}
|
|
23911
24280
|
function deriveStage(members) {
|
|
23912
24281
|
if (members.length > 0 && members.every((m2) => m2.sealed)) return "sealed";
|
|
23913
24282
|
const live = members.filter((m2) => !m2.sealed);
|
|
23914
24283
|
if (live.length > 0 && live.every((m2) => m2.concluded)) return "concluded";
|
|
23915
|
-
const reviewing = live.some((m2) => m2.mark === "gate-pending" || m2.mark === "active" && m2.queueLen > 0);
|
|
24284
|
+
const reviewing = live.some((m2) => m2.mark === "gate-pending" || m2.mark === "active" && m2.queueLen > 0 && !m2.hasUnproducedParts);
|
|
23916
24285
|
if (reviewing) return "review";
|
|
23917
|
-
const progressing = live.some((m2) => m2.mark === "active" || m2.mark === "embryo");
|
|
24286
|
+
const progressing = live.some((m2) => m2.mark === "active" || m2.mark === "embryo" || m2.hasUnproducedParts);
|
|
23918
24287
|
const blocked = live.some((m2) => m2.mark === "blocked");
|
|
23919
24288
|
if (blocked && !progressing) return "blocked";
|
|
23920
24289
|
return "executing";
|
|
@@ -23931,7 +24300,7 @@ function deriveNodeStage(info) {
|
|
|
23931
24300
|
case "embryo":
|
|
23932
24301
|
return "embryo";
|
|
23933
24302
|
default:
|
|
23934
|
-
if (info.hasWorking) return "editing";
|
|
24303
|
+
if (info.hasWorking || info.hasUnproducedParts) return "editing";
|
|
23935
24304
|
if (info.queueLen > 0) return "review";
|
|
23936
24305
|
return "merged";
|
|
23937
24306
|
}
|
|
@@ -23969,6 +24338,8 @@ function buildWorkorderSummaries(model, resolveActor, resolveProject, dispatchPa
|
|
|
23969
24338
|
const lastOps = arts.map((a) => model.lastOpAt.get(a.id)).filter((t) => Boolean(t)).sort();
|
|
23970
24339
|
const participantIds = [...new Set(arts.map((a) => a.owner))];
|
|
23971
24340
|
const woTitle = seed.title ?? seed.description ?? workspace;
|
|
24341
|
+
const stage = deriveStage(members);
|
|
24342
|
+
const dispatchPaused = dispatchPausedOf?.(workspace) ?? false;
|
|
23972
24343
|
out.push({
|
|
23973
24344
|
id: workspace,
|
|
23974
24345
|
title: woTitle,
|
|
@@ -23976,7 +24347,7 @@ function buildWorkorderSummaries(model, resolveActor, resolveProject, dispatchPa
|
|
|
23976
24347
|
...seed.description && seed.description !== woTitle ? { description: seed.description } : {},
|
|
23977
24348
|
// 一句话目标:优先取结构化 spec.goal;缺省时前端回退 description 首段(投影不替前端做回退,只给真值)
|
|
23978
24349
|
...spec?.goal ? { goal: spec.goal } : {},
|
|
23979
|
-
stage
|
|
24350
|
+
stage,
|
|
23980
24351
|
owner: ref(seed.owner),
|
|
23981
24352
|
participants: participantIds.map(ref),
|
|
23982
24353
|
artifactCount: arts.length,
|
|
@@ -23984,7 +24355,7 @@ function buildWorkorderSummaries(model, resolveActor, resolveProject, dispatchPa
|
|
|
23984
24355
|
updatedAt: lastOps[lastOps.length - 1] ?? "",
|
|
23985
24356
|
projectId: resolveProject?.(workspace) ?? null,
|
|
23986
24357
|
planning: planningStatusOf(model, arts),
|
|
23987
|
-
...
|
|
24358
|
+
...dispatchPaused ? { dispatchPaused: true } : {}
|
|
23988
24359
|
});
|
|
23989
24360
|
}
|
|
23990
24361
|
return out.sort((a, b2) => a.updatedAt < b2.updatedAt ? 1 : a.updatedAt > b2.updatedAt ? -1 : 0);
|
|
@@ -24858,9 +25229,9 @@ var init_service = __esm({
|
|
|
24858
25229
|
reason: input.revision.summary ?? input.revision.version
|
|
24859
25230
|
});
|
|
24860
25231
|
await this.syncProjection();
|
|
24861
|
-
const
|
|
24862
|
-
if (!
|
|
24863
|
-
return
|
|
25232
|
+
const record4 = await this.artifacts.getRevision(kernelRevision.id);
|
|
25233
|
+
if (!record4) throw new Error(`revision projection missing after register: ${kernelRevision.id}`);
|
|
25234
|
+
return record4;
|
|
24864
25235
|
}
|
|
24865
25236
|
async approveArtifactRevision(input) {
|
|
24866
25237
|
if (input.actorKind !== "human") throw namedError("AgentReviewForbidden", "agents cannot approve artifact revisions");
|
|
@@ -24955,9 +25326,9 @@ var init_service = __esm({
|
|
|
24955
25326
|
...input.revisionId ? { revisionId: input.revisionId } : {}
|
|
24956
25327
|
});
|
|
24957
25328
|
await this.syncProjection();
|
|
24958
|
-
const
|
|
24959
|
-
if (!
|
|
24960
|
-
return
|
|
25329
|
+
const record4 = await this.artifacts.getAnnotation(kernelAnnotation.id);
|
|
25330
|
+
if (!record4) throw new Error(`annotation projection missing after create: ${kernelAnnotation.id}`);
|
|
25331
|
+
return record4;
|
|
24961
25332
|
}
|
|
24962
25333
|
async resolveArtifactAnnotation(input) {
|
|
24963
25334
|
const artifact = await this.artifacts.getArtifact(input.artifactId);
|
|
@@ -24973,9 +25344,9 @@ var init_service = __esm({
|
|
|
24973
25344
|
...input.comment?.trim() ? { note: input.comment.trim() } : {}
|
|
24974
25345
|
});
|
|
24975
25346
|
await this.syncProjection();
|
|
24976
|
-
const
|
|
24977
|
-
if (!
|
|
24978
|
-
return
|
|
25347
|
+
const record4 = await this.artifacts.getAnnotation(input.annotationId);
|
|
25348
|
+
if (!record4) throw new Error(`annotation projection missing after resolve: ${input.annotationId}`);
|
|
25349
|
+
return record4;
|
|
24979
25350
|
}
|
|
24980
25351
|
async registerNodeOutput(input) {
|
|
24981
25352
|
await this.assertProjectExecutable(input.projectId);
|
|
@@ -25204,12 +25575,12 @@ var init_service = __esm({
|
|
|
25204
25575
|
});
|
|
25205
25576
|
|
|
25206
25577
|
// ../server/src/dev-store.ts
|
|
25207
|
-
var import_node_crypto4,
|
|
25578
|
+
var import_node_crypto4, fs3, path, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRegistryStore, FileProjectStateStore, FileProjectDocumentStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore;
|
|
25208
25579
|
var init_dev_store = __esm({
|
|
25209
25580
|
"../server/src/dev-store.ts"() {
|
|
25210
25581
|
"use strict";
|
|
25211
25582
|
import_node_crypto4 = require("node:crypto");
|
|
25212
|
-
|
|
25583
|
+
fs3 = __toESM(require("node:fs"), 1);
|
|
25213
25584
|
path = __toESM(require("node:path"), 1);
|
|
25214
25585
|
init_src();
|
|
25215
25586
|
init_src3();
|
|
@@ -25222,8 +25593,8 @@ var init_dev_store = __esm({
|
|
|
25222
25593
|
}
|
|
25223
25594
|
static async open(file) {
|
|
25224
25595
|
const memory = new MemoryOplogStore();
|
|
25225
|
-
if (
|
|
25226
|
-
for (const line of
|
|
25596
|
+
if (fs3.existsSync(file)) {
|
|
25597
|
+
for (const line of fs3.readFileSync(file, "utf8").split("\n")) {
|
|
25227
25598
|
if (!line.trim()) continue;
|
|
25228
25599
|
const stored = JSON.parse(line);
|
|
25229
25600
|
const { artifactId, seq, ...op } = stored;
|
|
@@ -25231,15 +25602,15 @@ var init_dev_store = __esm({
|
|
|
25231
25602
|
if (assigned !== seq) throw new Error(`corrupt ndjson log: seq mismatch at ${line}`);
|
|
25232
25603
|
}
|
|
25233
25604
|
} else {
|
|
25234
|
-
|
|
25235
|
-
|
|
25605
|
+
fs3.mkdirSync(path.dirname(file), { recursive: true });
|
|
25606
|
+
fs3.writeFileSync(file, "");
|
|
25236
25607
|
}
|
|
25237
25608
|
return new _NdjsonOplogStore(file, memory);
|
|
25238
25609
|
}
|
|
25239
25610
|
async append(artifactId, op, expectedSeq) {
|
|
25240
25611
|
const seq = await this.memory.append(artifactId, op, expectedSeq);
|
|
25241
25612
|
const stored = { ...op, artifactId, seq };
|
|
25242
|
-
|
|
25613
|
+
fs3.appendFileSync(this.file, JSON.stringify(stored) + "\n");
|
|
25243
25614
|
return seq;
|
|
25244
25615
|
}
|
|
25245
25616
|
read(artifactId, fromSeq) {
|
|
@@ -25255,7 +25626,7 @@ var init_dev_store = __esm({
|
|
|
25255
25626
|
DirBlobStore = class {
|
|
25256
25627
|
constructor(dir) {
|
|
25257
25628
|
this.dir = dir;
|
|
25258
|
-
|
|
25629
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
25259
25630
|
}
|
|
25260
25631
|
fileOf(hash) {
|
|
25261
25632
|
return path.join(this.dir, hash);
|
|
@@ -25263,31 +25634,31 @@ var init_dev_store = __esm({
|
|
|
25263
25634
|
async put(bytes) {
|
|
25264
25635
|
const hash = (0, import_node_crypto4.createHash)("sha256").update(bytes).digest("hex");
|
|
25265
25636
|
const file = this.fileOf(hash);
|
|
25266
|
-
if (!
|
|
25637
|
+
if (!fs3.existsSync(file)) fs3.writeFileSync(file, bytes);
|
|
25267
25638
|
return hash;
|
|
25268
25639
|
}
|
|
25269
25640
|
async meta(hash) {
|
|
25270
25641
|
const file = this.fileOf(hash);
|
|
25271
|
-
if (!
|
|
25272
|
-
const size =
|
|
25273
|
-
const fd =
|
|
25642
|
+
if (!fs3.existsSync(file)) return null;
|
|
25643
|
+
const size = fs3.statSync(file).size;
|
|
25644
|
+
const fd = fs3.openSync(file, "r");
|
|
25274
25645
|
const head = Buffer.alloc(16);
|
|
25275
|
-
|
|
25276
|
-
|
|
25646
|
+
fs3.readSync(fd, head, 0, 16, 0);
|
|
25647
|
+
fs3.closeSync(fd);
|
|
25277
25648
|
const contentType = sniffContentType(new Uint8Array(head));
|
|
25278
25649
|
return { size, ...contentType ? { contentType } : {} };
|
|
25279
25650
|
}
|
|
25280
25651
|
async get(hash) {
|
|
25281
25652
|
const file = this.fileOf(hash);
|
|
25282
|
-
if (!
|
|
25283
|
-
return new Uint8Array(
|
|
25653
|
+
if (!fs3.existsSync(file)) throw new BlobNotFoundError(hash);
|
|
25654
|
+
return new Uint8Array(fs3.readFileSync(file));
|
|
25284
25655
|
}
|
|
25285
25656
|
async has(hash) {
|
|
25286
|
-
return
|
|
25657
|
+
return fs3.existsSync(this.fileOf(hash));
|
|
25287
25658
|
}
|
|
25288
25659
|
async sweep(reachable) {
|
|
25289
|
-
for (const name of
|
|
25290
|
-
if (!reachable.has(name))
|
|
25660
|
+
for (const name of fs3.readdirSync(this.dir)) {
|
|
25661
|
+
if (!reachable.has(name)) fs3.unlinkSync(this.fileOf(name));
|
|
25291
25662
|
}
|
|
25292
25663
|
}
|
|
25293
25664
|
};
|
|
@@ -25313,7 +25684,7 @@ var init_dev_store = __esm({
|
|
|
25313
25684
|
this.file = file;
|
|
25314
25685
|
}
|
|
25315
25686
|
static async open(file) {
|
|
25316
|
-
const seed =
|
|
25687
|
+
const seed = fs3.existsSync(file) ? JSON.parse(fs3.readFileSync(file, "utf8")) : [];
|
|
25317
25688
|
return new _FileTypeRegistryStore(file, seed);
|
|
25318
25689
|
}
|
|
25319
25690
|
async put(def) {
|
|
@@ -25326,8 +25697,8 @@ var init_dev_store = __esm({
|
|
|
25326
25697
|
}
|
|
25327
25698
|
async save() {
|
|
25328
25699
|
const tmp = `${this.file}.tmp`;
|
|
25329
|
-
|
|
25330
|
-
|
|
25700
|
+
fs3.writeFileSync(tmp, JSON.stringify(await this.list(), null, 2));
|
|
25701
|
+
fs3.renameSync(tmp, this.file);
|
|
25331
25702
|
}
|
|
25332
25703
|
};
|
|
25333
25704
|
FileRegistryStore = class _FileRegistryStore extends MemoryRegistryStore {
|
|
@@ -25345,8 +25716,8 @@ var init_dev_store = __esm({
|
|
|
25345
25716
|
}
|
|
25346
25717
|
static async open(file) {
|
|
25347
25718
|
const store = new _FileRegistryStore(file);
|
|
25348
|
-
if (
|
|
25349
|
-
const snap = JSON.parse(
|
|
25719
|
+
if (fs3.existsSync(file)) {
|
|
25720
|
+
const snap = JSON.parse(fs3.readFileSync(file, "utf8"));
|
|
25350
25721
|
for (const a of snap.actors) await MemoryRegistryStore.prototype.upsertActor.call(store, a);
|
|
25351
25722
|
for (const c of snap.configs) await MemoryRegistryStore.prototype.appendConfig.call(store, c);
|
|
25352
25723
|
for (const t of snap.teams) await MemoryRegistryStore.prototype.upsertTeam.call(store, t);
|
|
@@ -25383,7 +25754,7 @@ var init_dev_store = __esm({
|
|
|
25383
25754
|
installedSkills,
|
|
25384
25755
|
skillFiles: skillFilesEntries
|
|
25385
25756
|
};
|
|
25386
|
-
|
|
25757
|
+
fs3.writeFileSync(this.file, JSON.stringify(snap, null, 2));
|
|
25387
25758
|
}
|
|
25388
25759
|
};
|
|
25389
25760
|
FileProjectStateStore = class _FileProjectStateStore extends MemoryProjectStateStore {
|
|
@@ -25393,8 +25764,8 @@ var init_dev_store = __esm({
|
|
|
25393
25764
|
}
|
|
25394
25765
|
static async open(file) {
|
|
25395
25766
|
const store = new _FileProjectStateStore(file);
|
|
25396
|
-
if (
|
|
25397
|
-
const snap = JSON.parse(
|
|
25767
|
+
if (fs3.existsSync(file)) {
|
|
25768
|
+
const snap = JSON.parse(fs3.readFileSync(file, "utf8"));
|
|
25398
25769
|
for (const project of snap.projects ?? []) await MemoryProjectStateStore.prototype.upsertProject.call(store, project);
|
|
25399
25770
|
const membersByProject = /* @__PURE__ */ new Map();
|
|
25400
25771
|
for (const member of snap.members ?? []) {
|
|
@@ -25406,8 +25777,8 @@ var init_dev_store = __esm({
|
|
|
25406
25777
|
await MemoryProjectStateStore.prototype.replaceProjectMembers.call(store, projectId, members);
|
|
25407
25778
|
}
|
|
25408
25779
|
} else {
|
|
25409
|
-
|
|
25410
|
-
|
|
25780
|
+
fs3.mkdirSync(path.dirname(file), { recursive: true });
|
|
25781
|
+
fs3.writeFileSync(file, JSON.stringify({ projects: [], members: [] }, null, 2));
|
|
25411
25782
|
}
|
|
25412
25783
|
return store;
|
|
25413
25784
|
}
|
|
@@ -25426,7 +25797,7 @@ var init_dev_store = __esm({
|
|
|
25426
25797
|
async save() {
|
|
25427
25798
|
const projects = await this.listProjects();
|
|
25428
25799
|
const members = (await Promise.all(projects.map((project) => this.listProjectMembers(project.id)))).flat();
|
|
25429
|
-
|
|
25800
|
+
fs3.writeFileSync(this.file, JSON.stringify({ projects, members }, null, 2));
|
|
25430
25801
|
}
|
|
25431
25802
|
};
|
|
25432
25803
|
FileProjectDocumentStore = class _FileProjectDocumentStore extends MemoryProjectDocumentStore {
|
|
@@ -25436,8 +25807,8 @@ var init_dev_store = __esm({
|
|
|
25436
25807
|
}
|
|
25437
25808
|
static async open(file) {
|
|
25438
25809
|
const store = new _FileProjectDocumentStore(file);
|
|
25439
|
-
if (
|
|
25440
|
-
const snap = JSON.parse(
|
|
25810
|
+
if (fs3.existsSync(file)) {
|
|
25811
|
+
const snap = JSON.parse(fs3.readFileSync(file, "utf8"));
|
|
25441
25812
|
for (const [projectId, space] of Object.entries(snap.spaces ?? {})) {
|
|
25442
25813
|
store.spaces.set(projectId, {
|
|
25443
25814
|
rootRef: space.rootRef,
|
|
@@ -25451,8 +25822,8 @@ var init_dev_store = __esm({
|
|
|
25451
25822
|
});
|
|
25452
25823
|
}
|
|
25453
25824
|
} else {
|
|
25454
|
-
|
|
25455
|
-
|
|
25825
|
+
fs3.mkdirSync(path.dirname(file), { recursive: true });
|
|
25826
|
+
fs3.writeFileSync(file, JSON.stringify({ spaces: {} }, null, 2));
|
|
25456
25827
|
}
|
|
25457
25828
|
return store;
|
|
25458
25829
|
}
|
|
@@ -25485,7 +25856,7 @@ var init_dev_store = __esm({
|
|
|
25485
25856
|
runStates: [...space.runStates.values()]
|
|
25486
25857
|
};
|
|
25487
25858
|
}
|
|
25488
|
-
|
|
25859
|
+
fs3.writeFileSync(this.file, JSON.stringify({ spaces }, null, 2));
|
|
25489
25860
|
}
|
|
25490
25861
|
};
|
|
25491
25862
|
FileArtifactStateStore = class _FileArtifactStateStore extends MemoryArtifactStateStore {
|
|
@@ -25495,8 +25866,8 @@ var init_dev_store = __esm({
|
|
|
25495
25866
|
}
|
|
25496
25867
|
static async open(file) {
|
|
25497
25868
|
const store = new _FileArtifactStateStore(file);
|
|
25498
|
-
if (
|
|
25499
|
-
const snap = JSON.parse(
|
|
25869
|
+
if (fs3.existsSync(file)) {
|
|
25870
|
+
const snap = JSON.parse(fs3.readFileSync(file, "utf8"));
|
|
25500
25871
|
for (const artifact of snap.artifacts ?? []) await MemoryArtifactStateStore.prototype.upsertArtifact.call(store, artifact);
|
|
25501
25872
|
for (const revision of snap.revisions ?? []) await MemoryArtifactStateStore.prototype.upsertRevision.call(store, revision);
|
|
25502
25873
|
for (const event of snap.events ?? []) await MemoryArtifactStateStore.prototype.appendEvent.call(store, event);
|
|
@@ -25505,8 +25876,8 @@ var init_dev_store = __esm({
|
|
|
25505
25876
|
for (const binding of snap.workspaceBindings ?? []) await MemoryArtifactStateStore.prototype.upsertWorkspaceBinding.call(store, binding);
|
|
25506
25877
|
for (const ws of snap.workspaceDispatchHeld ?? []) await MemoryArtifactStateStore.prototype.setWorkspaceDispatchHold.call(store, ws, true);
|
|
25507
25878
|
} else {
|
|
25508
|
-
|
|
25509
|
-
|
|
25879
|
+
fs3.mkdirSync(path.dirname(file), { recursive: true });
|
|
25880
|
+
fs3.writeFileSync(file, JSON.stringify({ artifacts: [], revisions: [], events: [], annotations: [], nodeOutputs: [], workspaceBindings: [], workspaceDispatchHeld: [] }, null, 2));
|
|
25510
25881
|
}
|
|
25511
25882
|
return store;
|
|
25512
25883
|
}
|
|
@@ -25539,7 +25910,7 @@ var init_dev_store = __esm({
|
|
|
25539
25910
|
await this.save();
|
|
25540
25911
|
}
|
|
25541
25912
|
async save() {
|
|
25542
|
-
|
|
25913
|
+
fs3.writeFileSync(this.file, JSON.stringify(await this.listAll(), null, 2));
|
|
25543
25914
|
}
|
|
25544
25915
|
};
|
|
25545
25916
|
FileTraceStore = class _FileTraceStore extends MemoryTraceStore {
|
|
@@ -25566,20 +25937,20 @@ var init_dev_store = __esm({
|
|
|
25566
25937
|
return path.join(this.eventsDir, `${encodeURIComponent(runId)}.ndjson`);
|
|
25567
25938
|
}
|
|
25568
25939
|
appendLine(file, obj) {
|
|
25569
|
-
|
|
25940
|
+
fs3.appendFileSync(file, JSON.stringify(obj) + "\n");
|
|
25570
25941
|
}
|
|
25571
25942
|
static replay(file, fn) {
|
|
25572
|
-
if (!
|
|
25573
|
-
for (const line of
|
|
25943
|
+
if (!fs3.existsSync(file)) return;
|
|
25944
|
+
for (const line of fs3.readFileSync(file, "utf8").split("\n")) {
|
|
25574
25945
|
if (!line.trim()) continue;
|
|
25575
25946
|
fn(JSON.parse(line));
|
|
25576
25947
|
}
|
|
25577
25948
|
}
|
|
25578
25949
|
static async open(dir) {
|
|
25579
25950
|
const store = new _FileTraceStore(dir);
|
|
25580
|
-
|
|
25951
|
+
fs3.mkdirSync(store.eventsDir, { recursive: true });
|
|
25581
25952
|
_FileTraceStore.replay(store.runsFile, (r) => store.restoreRun(r));
|
|
25582
|
-
for (const name of
|
|
25953
|
+
for (const name of fs3.readdirSync(store.eventsDir)) {
|
|
25583
25954
|
_FileTraceStore.replay(path.join(store.eventsDir, name), (e) => store.restoreEvent(e));
|
|
25584
25955
|
}
|
|
25585
25956
|
_FileTraceStore.replay(store.toolCallsFile, (c) => store.restoreToolCall(c));
|
|
@@ -25624,6 +25995,7 @@ var init_dev_store = __esm({
|
|
|
25624
25995
|
MemoryChatSessionStore = class {
|
|
25625
25996
|
sessions = /* @__PURE__ */ new Map();
|
|
25626
25997
|
messages = /* @__PURE__ */ new Map();
|
|
25998
|
+
sessionWorkOrders = /* @__PURE__ */ new Map();
|
|
25627
25999
|
async createSession(s2) {
|
|
25628
26000
|
this.sessions.set(s2.id, s2);
|
|
25629
26001
|
}
|
|
@@ -25640,6 +26012,7 @@ var init_dev_store = __esm({
|
|
|
25640
26012
|
async deleteSession(id) {
|
|
25641
26013
|
this.sessions.delete(id);
|
|
25642
26014
|
this.messages.delete(id);
|
|
26015
|
+
this.sessionWorkOrders.delete(id);
|
|
25643
26016
|
}
|
|
25644
26017
|
async appendMessage(m2) {
|
|
25645
26018
|
const list = this.messages.get(m2.sessionId) ?? [];
|
|
@@ -25650,16 +26023,24 @@ var init_dev_store = __esm({
|
|
|
25650
26023
|
const list = (this.messages.get(sessionId) ?? []).sort((a, b2) => a.seq - b2.seq);
|
|
25651
26024
|
return limit ? list.slice(-limit) : list;
|
|
25652
26025
|
}
|
|
26026
|
+
async linkWorkOrder(sessionId, workOrderId) {
|
|
26027
|
+
const set = this.sessionWorkOrders.get(sessionId) ?? /* @__PURE__ */ new Set();
|
|
26028
|
+
set.add(workOrderId);
|
|
26029
|
+
this.sessionWorkOrders.set(sessionId, set);
|
|
26030
|
+
}
|
|
26031
|
+
async listWorkOrdersForSession(sessionId) {
|
|
26032
|
+
return [...this.sessionWorkOrders.get(sessionId) ?? []];
|
|
26033
|
+
}
|
|
25653
26034
|
};
|
|
25654
26035
|
}
|
|
25655
26036
|
});
|
|
25656
26037
|
|
|
25657
26038
|
// ../server/src/control-plane-file-store.ts
|
|
25658
|
-
var
|
|
26039
|
+
var fs4, path2, MUTATORS2, FileControlPlaneStore;
|
|
25659
26040
|
var init_control_plane_file_store = __esm({
|
|
25660
26041
|
"../server/src/control-plane-file-store.ts"() {
|
|
25661
26042
|
"use strict";
|
|
25662
|
-
|
|
26043
|
+
fs4 = __toESM(require("node:fs"), 1);
|
|
25663
26044
|
path2 = __toESM(require("node:path"), 1);
|
|
25664
26045
|
init_src3();
|
|
25665
26046
|
MUTATORS2 = [
|
|
@@ -25688,15 +26069,15 @@ var init_control_plane_file_store = __esm({
|
|
|
25688
26069
|
}
|
|
25689
26070
|
static async open(file) {
|
|
25690
26071
|
const store = new _FileControlPlaneStore(file);
|
|
25691
|
-
if (
|
|
25692
|
-
const snap = JSON.parse(
|
|
26072
|
+
if (fs4.existsSync(file)) {
|
|
26073
|
+
const snap = JSON.parse(fs4.readFileSync(file, "utf8"));
|
|
25693
26074
|
for (const c of snap.companies ?? []) await MemoryControlPlaneStore.prototype.createCompany.call(store, c);
|
|
25694
26075
|
for (const m2 of snap.members ?? []) await MemoryControlPlaneStore.prototype.upsertMember.call(store, m2);
|
|
25695
26076
|
for (const inv of snap.invitations ?? []) await MemoryControlPlaneStore.prototype.createInvitation.call(store, inv);
|
|
25696
26077
|
for (const a of snap.accounts ?? []) await MemoryControlPlaneStore.prototype.upsertAccount.call(store, a);
|
|
25697
26078
|
for (const e of snap.accountSecrets ?? []) store.accountSecrets.set(e.accountId, { ...e.secret });
|
|
25698
26079
|
} else {
|
|
25699
|
-
|
|
26080
|
+
fs4.mkdirSync(path2.dirname(file), { recursive: true });
|
|
25700
26081
|
}
|
|
25701
26082
|
return store;
|
|
25702
26083
|
}
|
|
@@ -25717,7 +26098,7 @@ var init_control_plane_file_store = __esm({
|
|
|
25717
26098
|
accounts,
|
|
25718
26099
|
accountSecrets
|
|
25719
26100
|
};
|
|
25720
|
-
|
|
26101
|
+
fs4.writeFileSync(this.file, JSON.stringify(snap, null, 2));
|
|
25721
26102
|
}
|
|
25722
26103
|
};
|
|
25723
26104
|
}
|
|
@@ -25991,7 +26372,7 @@ var init_liveness = __esm({
|
|
|
25991
26372
|
|
|
25992
26373
|
// ../server/src/file-engine.ts
|
|
25993
26374
|
async function openFileEngine(dir, seed = {}) {
|
|
25994
|
-
|
|
26375
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
25995
26376
|
const oplog = await NdjsonOplogStore.open(path3.join(dir, "oplog.ndjson"));
|
|
25996
26377
|
const blobs = new DirBlobStore(path3.join(dir, "blobs"));
|
|
25997
26378
|
const assets = new DirBlobStore(path3.join(dir, "assets"));
|
|
@@ -26012,11 +26393,11 @@ async function openFileEngine(dir, seed = {}) {
|
|
|
26012
26393
|
function companyEngineDirName(companyId) {
|
|
26013
26394
|
return companyId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
26014
26395
|
}
|
|
26015
|
-
var
|
|
26396
|
+
var fs5, path3;
|
|
26016
26397
|
var init_file_engine = __esm({
|
|
26017
26398
|
"../server/src/file-engine.ts"() {
|
|
26018
26399
|
"use strict";
|
|
26019
|
-
|
|
26400
|
+
fs5 = __toESM(require("node:fs"), 1);
|
|
26020
26401
|
path3 = __toESM(require("node:path"), 1);
|
|
26021
26402
|
init_src2();
|
|
26022
26403
|
init_dev_store();
|
|
@@ -30069,6 +30450,7 @@ var init_service2 = __esm({
|
|
|
30069
30450
|
"use strict";
|
|
30070
30451
|
import_node_crypto5 = require("node:crypto");
|
|
30071
30452
|
init_skill_fetcher();
|
|
30453
|
+
init_identity();
|
|
30072
30454
|
REDACTED_MARKER = "[REDACTED]";
|
|
30073
30455
|
ActorsService = class {
|
|
30074
30456
|
constructor(opts) {
|
|
@@ -30084,11 +30466,19 @@ var init_service2 = __esm({
|
|
|
30084
30466
|
async upsertActor(a, by) {
|
|
30085
30467
|
const existing = await this.opts.store.getActor(a.id);
|
|
30086
30468
|
const roles = a.roles ?? existing?.roles ?? [];
|
|
30087
|
-
const
|
|
30088
|
-
|
|
30089
|
-
|
|
30469
|
+
const record4 = { ...a, roles, createdAt: existing?.createdAt ?? a.createdAt ?? this.now() };
|
|
30470
|
+
if (isActiveCoordinatorAgent(record4)) {
|
|
30471
|
+
const existingCoordinator = (await this.opts.store.listActors({ kind: "agent", status: "active" })).find((actor) => actor.id !== record4.id && isActiveCoordinatorAgent(actor));
|
|
30472
|
+
if (existingCoordinator) {
|
|
30473
|
+
throw new Error(
|
|
30474
|
+
`\u7CFB\u7EDF\u53EA\u80FD\u6709\u4E00\u4E2A\u534F\u8C03\u8005\u667A\u80FD\u4F53\uFF1A\u5DF2\u5B58\u5728 ${existingCoordinator.name}\uFF08${existingCoordinator.id}\uFF09\uFF0C\u4E0D\u80FD\u518D\u521B\u5EFA\u6216\u542F\u7528 ${record4.name}\uFF08${record4.id}\uFF09\u3002`
|
|
30475
|
+
);
|
|
30476
|
+
}
|
|
30477
|
+
}
|
|
30478
|
+
await this.opts.store.upsertActor(record4);
|
|
30479
|
+
this.opts.onRolesChanged?.(record4.id, record4.status === "active" ? roles : []);
|
|
30090
30480
|
await this.audit({ kind: "actor_upsert", actorId: a.id, by, at: this.now(), detail: { status: a.status, roles } });
|
|
30091
|
-
return
|
|
30481
|
+
return record4;
|
|
30092
30482
|
}
|
|
30093
30483
|
/** 删除即停用(2.1 完成标准) */
|
|
30094
30484
|
async disableActor(id, by) {
|
|
@@ -30104,10 +30494,10 @@ var init_service2 = __esm({
|
|
|
30104
30494
|
const a = await this.opts.store.getActor(id);
|
|
30105
30495
|
if (!a) throw new Error(`actor not found: ${id}`);
|
|
30106
30496
|
const { avatar: _drop, ...rest } = a;
|
|
30107
|
-
const
|
|
30108
|
-
await this.opts.store.upsertActor(
|
|
30497
|
+
const record4 = ref === null ? rest : { ...rest, avatar: ref };
|
|
30498
|
+
await this.opts.store.upsertActor(record4);
|
|
30109
30499
|
await this.audit({ kind: "actor_upsert", actorId: id, by, at: this.now(), detail: { avatar: ref ?? "cleared" } });
|
|
30110
|
-
return
|
|
30500
|
+
return record4;
|
|
30111
30501
|
}
|
|
30112
30502
|
/** 设置/清除团队图标引用(同头像约定;不接受 emoji,只接受 blob 引用) */
|
|
30113
30503
|
async setTeamIcon(teamId, ref) {
|
|
@@ -31881,8 +32271,8 @@ var DEFAULT_COMPANY_ID, DEFAULT_COMPANY_SLUG, DEFAULT_COMPANY_NAME;
|
|
|
31881
32271
|
var init_migrate = __esm({
|
|
31882
32272
|
"../server/src/domains/companies/migrate.ts"() {
|
|
31883
32273
|
"use strict";
|
|
31884
|
-
DEFAULT_COMPANY_ID = "company:
|
|
31885
|
-
DEFAULT_COMPANY_SLUG = "
|
|
32274
|
+
DEFAULT_COMPANY_ID = "company:oasis";
|
|
32275
|
+
DEFAULT_COMPANY_SLUG = "oasis";
|
|
31886
32276
|
DEFAULT_COMPANY_NAME = "Oasis \u603B\u90E8";
|
|
31887
32277
|
}
|
|
31888
32278
|
});
|
|
@@ -31908,9 +32298,13 @@ var init_service3 = __esm({
|
|
|
31908
32298
|
CompaniesService = class {
|
|
31909
32299
|
store;
|
|
31910
32300
|
now;
|
|
32301
|
+
sender;
|
|
32302
|
+
consoleUrl;
|
|
31911
32303
|
constructor(opts) {
|
|
31912
32304
|
this.store = opts.store;
|
|
31913
32305
|
this.now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
32306
|
+
this.sender = opts.sender;
|
|
32307
|
+
this.consoleUrl = opts.consoleUrl ?? "/";
|
|
31914
32308
|
}
|
|
31915
32309
|
/** 当前用户所属的公司(按 membership 聚合)。 */
|
|
31916
32310
|
async listForUser(accountId) {
|
|
@@ -32001,6 +32395,16 @@ var init_service3 = __esm({
|
|
|
32001
32395
|
await this.requireCompany(companyId);
|
|
32002
32396
|
return this.store.listMembers(companyId);
|
|
32003
32397
|
}
|
|
32398
|
+
/** 成员视图:每条成员关系附上其账号的 email/name(无账号成员如 agent 为 null)。 */
|
|
32399
|
+
async listMemberViews(companyId) {
|
|
32400
|
+
const members = await this.listMembers(companyId);
|
|
32401
|
+
const out = [];
|
|
32402
|
+
for (const m2 of members) {
|
|
32403
|
+
const acc = await this.store.getAccount(m2.accountId);
|
|
32404
|
+
out.push({ ...m2, email: acc?.email ?? null, name: acc?.name ?? null });
|
|
32405
|
+
}
|
|
32406
|
+
return out;
|
|
32407
|
+
}
|
|
32004
32408
|
/**
|
|
32005
32409
|
* 改成员角色。不变量「至少留 1 个 owner」:把唯一的 owner 降级会被拒。
|
|
32006
32410
|
* 公司/成员不存在则抛 404。
|
|
@@ -32036,7 +32440,7 @@ var init_service3 = __esm({
|
|
|
32036
32440
|
* 公司不存在则抛 404。
|
|
32037
32441
|
*/
|
|
32038
32442
|
async createInvitation(companyId, email2, role) {
|
|
32039
|
-
const mail = email2?.trim() ?? "";
|
|
32443
|
+
const mail = email2?.trim().toLowerCase() ?? "";
|
|
32040
32444
|
if (!mail) throw new CompanyError(400, "BAD_REQUEST", "\u7F3A\u5C11 email");
|
|
32041
32445
|
if (!INVITABLE_ROLES.includes(role)) throw new CompanyError(400, "BAD_ROLE", `role \u987B\u4E3A ${INVITABLE_ROLES.join(" | ")}\uFF08owner \u4E0D\u7ECF\u9080\u8BF7\u4EA7\u751F\uFF09`);
|
|
32042
32446
|
await this.requireCompany(companyId);
|
|
@@ -32054,11 +32458,16 @@ var init_service3 = __esm({
|
|
|
32054
32458
|
expiresAt: new Date(nowMs + INVITATION_TTL_MS).toISOString()
|
|
32055
32459
|
};
|
|
32056
32460
|
await this.store.createInvitation(invitation);
|
|
32461
|
+
if (this.sender) {
|
|
32462
|
+
const company = await this.store.getCompany(companyId);
|
|
32463
|
+
const companyName = company?.name ?? companyId;
|
|
32464
|
+
void this.sender.sendInvitation(mail, { companyName, consoleUrl: this.consoleUrl }).catch((e) => console.error(`[companies] \u9080\u8BF7\u90AE\u4EF6\u53D1\u9001\u5931\u8D25 ${mail}: ${e instanceof Error ? e.message : String(e)}`));
|
|
32465
|
+
}
|
|
32057
32466
|
return invitation;
|
|
32058
32467
|
}
|
|
32059
32468
|
/** 列出发给某邮箱的有效(pending 未过期)邀请;顺带把过期的 pending 落为 expired。 */
|
|
32060
32469
|
async listInvitationsForEmail(email2) {
|
|
32061
|
-
const all = await this.store.listInvitations({ email: email2 });
|
|
32470
|
+
const all = await this.store.listInvitations({ email: email2.trim().toLowerCase() });
|
|
32062
32471
|
const out = [];
|
|
32063
32472
|
for (const inv of all) {
|
|
32064
32473
|
if (inv.status !== "pending") continue;
|
|
@@ -32152,7 +32561,7 @@ function companiesDomain(opts) {
|
|
|
32152
32561
|
return { status: 200, body: { ok: true } };
|
|
32153
32562
|
});
|
|
32154
32563
|
router.get("/api/companies/:id/members", async (req) => {
|
|
32155
|
-
const items = await service.
|
|
32564
|
+
const items = await service.listMemberViews(req.params.id).catch(mapErr);
|
|
32156
32565
|
return { status: 200, body: { items } };
|
|
32157
32566
|
});
|
|
32158
32567
|
router.patch("/api/companies/:id/members/:accountId", async (req) => {
|
|
@@ -32199,7 +32608,12 @@ var init_routes3 = __esm({
|
|
|
32199
32608
|
|
|
32200
32609
|
// ../server/src/domains/companies/index.ts
|
|
32201
32610
|
function createCompaniesDomain(opts) {
|
|
32202
|
-
const service = new CompaniesService({
|
|
32611
|
+
const service = new CompaniesService({
|
|
32612
|
+
store: opts.store,
|
|
32613
|
+
...opts.now !== void 0 ? { now: opts.now } : {},
|
|
32614
|
+
...opts.sender !== void 0 ? { sender: opts.sender } : {},
|
|
32615
|
+
...opts.consoleUrl !== void 0 ? { consoleUrl: opts.consoleUrl } : {}
|
|
32616
|
+
});
|
|
32203
32617
|
const currentUser = opts.currentUser ?? makeCurrentUser(opts.store);
|
|
32204
32618
|
return { service, register: companiesDomain({ service, currentUser, ...opts.blobs !== void 0 ? { blobs: opts.blobs } : {} }), currentUser };
|
|
32205
32619
|
}
|
|
@@ -32302,6 +32716,14 @@ function parseCookies(header) {
|
|
|
32302
32716
|
}
|
|
32303
32717
|
return out;
|
|
32304
32718
|
}
|
|
32719
|
+
function looksLikeEmail(s2) {
|
|
32720
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s2);
|
|
32721
|
+
}
|
|
32722
|
+
function maskEmail(email2) {
|
|
32723
|
+
const at = email2.indexOf("@");
|
|
32724
|
+
if (at <= 0) return "***";
|
|
32725
|
+
return `${email2[0]}***${email2.slice(at)}`;
|
|
32726
|
+
}
|
|
32305
32727
|
function createAuth(opts) {
|
|
32306
32728
|
const now = opts.now ?? (() => Date.now());
|
|
32307
32729
|
const cookieName = opts.cookieName ?? "oasis_auth";
|
|
@@ -32310,7 +32732,6 @@ function createAuth(opts) {
|
|
|
32310
32732
|
const maxAttempts = opts.maxCodeAttempts ?? 5;
|
|
32311
32733
|
const resendThrottleMs = opts.resendThrottleMs ?? 30 * 1e3;
|
|
32312
32734
|
const { store, sender, jwtSecret } = opts;
|
|
32313
|
-
const hasPendingInvite = async (email2) => (await store.listInvitations({ email: email2, status: "pending" })).some((inv) => Date.parse(inv.expiresAt) > now());
|
|
32314
32735
|
const sessionCookie = (accountId, tokenVersion) => {
|
|
32315
32736
|
const t = now();
|
|
32316
32737
|
const jwt = signSession({ sub: accountId, tokenVersion, iat: t, exp: t + sessionTtlMs }, jwtSecret);
|
|
@@ -32367,26 +32788,23 @@ function createAuth(opts) {
|
|
|
32367
32788
|
if (method !== "POST") return { status: 405, body: { error: { code: "METHOD_NOT_ALLOWED", message: method } } };
|
|
32368
32789
|
if (route === "request-code") {
|
|
32369
32790
|
const email2 = typeof parsed.email === "string" ? parsed.email.trim().toLowerCase() : "";
|
|
32370
|
-
if (email2) {
|
|
32371
|
-
const
|
|
32372
|
-
const
|
|
32373
|
-
if (
|
|
32374
|
-
const
|
|
32375
|
-
const
|
|
32376
|
-
|
|
32377
|
-
|
|
32378
|
-
|
|
32379
|
-
|
|
32380
|
-
|
|
32381
|
-
|
|
32382
|
-
|
|
32383
|
-
|
|
32384
|
-
|
|
32385
|
-
|
|
32386
|
-
|
|
32387
|
-
await sender.sendCode(email2, code);
|
|
32388
|
-
} catch {
|
|
32389
|
-
}
|
|
32791
|
+
if (looksLikeEmail(email2)) {
|
|
32792
|
+
const existing = await store.getVerificationCode(email2);
|
|
32793
|
+
const fresh = existing && Date.parse(existing.createdAt) > now() - resendThrottleMs;
|
|
32794
|
+
if (!fresh) {
|
|
32795
|
+
const code = generateCode();
|
|
32796
|
+
const t = now();
|
|
32797
|
+
await store.putVerificationCode({
|
|
32798
|
+
email: email2,
|
|
32799
|
+
codeHash: hashCode(code, jwtSecret),
|
|
32800
|
+
createdAt: new Date(t).toISOString(),
|
|
32801
|
+
expiresAt: new Date(t + codeTtlMs).toISOString(),
|
|
32802
|
+
attempts: 0
|
|
32803
|
+
});
|
|
32804
|
+
try {
|
|
32805
|
+
await sender.sendCode(email2, code);
|
|
32806
|
+
} catch (e) {
|
|
32807
|
+
console.error(`[auth] \u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25 ${maskEmail(email2)}: ${e instanceof Error ? e.message : String(e)}`);
|
|
32390
32808
|
}
|
|
32391
32809
|
}
|
|
32392
32810
|
}
|
|
@@ -32409,7 +32827,7 @@ function createAuth(opts) {
|
|
|
32409
32827
|
}
|
|
32410
32828
|
await store.deleteVerificationCode(email2);
|
|
32411
32829
|
let account = await store.getAccountByEmail(email2);
|
|
32412
|
-
if (!account && opts.provisionAccount
|
|
32830
|
+
if (!account && opts.provisionAccount) {
|
|
32413
32831
|
account = await opts.provisionAccount(email2);
|
|
32414
32832
|
}
|
|
32415
32833
|
if (!account) return fail();
|
|
@@ -32456,6 +32874,9 @@ function consoleSender() {
|
|
|
32456
32874
|
return {
|
|
32457
32875
|
async sendCode(email2, code) {
|
|
32458
32876
|
console.log(`[auth] \u9A8C\u8BC1\u7801 \u2192 ${email2}: ${code}`);
|
|
32877
|
+
},
|
|
32878
|
+
async sendInvitation(email2, opts) {
|
|
32879
|
+
console.log(`[auth] \u9080\u8BF7 \u2192 ${email2}: \u52A0\u5165\u300C${opts.companyName}\u300D\uFF0C\u767B\u5F55 ${opts.consoleUrl}`);
|
|
32459
32880
|
}
|
|
32460
32881
|
};
|
|
32461
32882
|
}
|
|
@@ -32477,6 +32898,22 @@ function resendSender(opts) {
|
|
|
32477
32898
|
const detail = await res.text().catch(() => "");
|
|
32478
32899
|
throw new Error(`Resend \u53D1\u9001\u5931\u8D25 ${res.status}: ${detail.slice(0, 200)}`);
|
|
32479
32900
|
}
|
|
32901
|
+
},
|
|
32902
|
+
async sendInvitation(email2, { companyName, consoleUrl }) {
|
|
32903
|
+
const res = await doFetch("https://api.resend.com/emails", {
|
|
32904
|
+
method: "POST",
|
|
32905
|
+
headers: { authorization: `Bearer ${opts.apiKey}`, "content-type": "application/json" },
|
|
32906
|
+
body: JSON.stringify({
|
|
32907
|
+
from: opts.from,
|
|
32908
|
+
to: [email2],
|
|
32909
|
+
subject: `\u4F60\u88AB\u9080\u8BF7\u52A0\u5165\u300C${companyName}\u300D`,
|
|
32910
|
+
html: `<div style="font-family:sans-serif;max-width:440px;margin:0 auto"><h2>\u52A0\u5165\u300C${companyName}\u300D</h2><p>\u4F60\u88AB\u9080\u8BF7\u52A0\u5165 Oasis \u4E0A\u7684\u300C${companyName}\u300D\u3002</p><p style="margin:24px 0"><a href="${consoleUrl}" style="background:#111;color:#fff;padding:10px 20px;border-radius:8px;text-decoration:none">\u767B\u5F55\u5E76\u63A5\u53D7\u9080\u8BF7</a></p><p style="color:#666;font-size:14px">\u7528<strong>\u672C\u90AE\u7BB1</strong>\u767B\u5F55\uFF08\u90AE\u7BB1\u9A8C\u8BC1\u7801\uFF09\uFF0C\u767B\u5F55\u540E\u5728\u516C\u53F8\u5207\u6362\u5904\u5373\u53EF\u770B\u5230\u5E76\u63A5\u53D7\u8FD9\u6761\u9080\u8BF7\u3002\u9080\u8BF7 7 \u5929\u5185\u6709\u6548\u3002</p><p style="color:#666;font-size:13px">\u94FE\u63A5\u6253\u4E0D\u5F00\u5C31\u76F4\u63A5\u8BBF\u95EE\uFF1A${consoleUrl}</p></div>`
|
|
32911
|
+
})
|
|
32912
|
+
});
|
|
32913
|
+
if (!res.ok) {
|
|
32914
|
+
const detail = await res.text().catch(() => "");
|
|
32915
|
+
throw new Error(`Resend \u9080\u8BF7\u53D1\u9001\u5931\u8D25 ${res.status}: ${detail.slice(0, 200)}`);
|
|
32916
|
+
}
|
|
32480
32917
|
}
|
|
32481
32918
|
};
|
|
32482
32919
|
}
|
|
@@ -32952,11 +33389,11 @@ var init_types3 = __esm({
|
|
|
32952
33389
|
});
|
|
32953
33390
|
|
|
32954
33391
|
// ../server/src/node-store.ts
|
|
32955
|
-
var
|
|
33392
|
+
var fs6, MemoryNodeStore, FileNodeStore;
|
|
32956
33393
|
var init_node_store = __esm({
|
|
32957
33394
|
"../server/src/node-store.ts"() {
|
|
32958
33395
|
"use strict";
|
|
32959
|
-
|
|
33396
|
+
fs6 = __toESM(require("node:fs"), 1);
|
|
32960
33397
|
MemoryNodeStore = class {
|
|
32961
33398
|
nodes = /* @__PURE__ */ new Map();
|
|
32962
33399
|
runtimes = /* @__PURE__ */ new Map();
|
|
@@ -33037,16 +33474,16 @@ var init_node_store = __esm({
|
|
|
33037
33474
|
static async open(file) {
|
|
33038
33475
|
const s2 = new _FileNodeStore(file);
|
|
33039
33476
|
try {
|
|
33040
|
-
const snap = JSON.parse(
|
|
33477
|
+
const snap = JSON.parse(fs6.readFileSync(file, "utf8"));
|
|
33041
33478
|
for (const n of snap.nodes ?? []) s2.nodes.set(n.id, n);
|
|
33042
33479
|
for (const r of snap.runtimes ?? []) s2.runtimes.set(r.id, r);
|
|
33043
33480
|
} catch {
|
|
33044
|
-
|
|
33481
|
+
fs6.writeFileSync(file, JSON.stringify({ nodes: [], runtimes: [] }, null, 2));
|
|
33045
33482
|
}
|
|
33046
33483
|
return s2;
|
|
33047
33484
|
}
|
|
33048
33485
|
flush() {
|
|
33049
|
-
|
|
33486
|
+
fs6.writeFileSync(this.file, JSON.stringify({
|
|
33050
33487
|
nodes: [...this.nodes.values()],
|
|
33051
33488
|
runtimes: [...this.runtimes.values()]
|
|
33052
33489
|
}, null, 2));
|
|
@@ -33094,7 +33531,7 @@ var init_node_store = __esm({
|
|
|
33094
33531
|
});
|
|
33095
33532
|
|
|
33096
33533
|
// ../server/src/domains/collab/workorder-detail.ts
|
|
33097
|
-
function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject, dispatchJournal = [], dispatchPausedOf) {
|
|
33534
|
+
function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject, dispatchJournal = [], dispatchPausedOf, ops = []) {
|
|
33098
33535
|
const arts = [...model.artifacts.values()].filter((a) => a.workspace === workspaceId);
|
|
33099
33536
|
if (arts.length === 0) return null;
|
|
33100
33537
|
const summary = buildWorkorderSummaries(model, resolveActor, resolveProject, dispatchPausedOf).find((w2) => w2.id === workspaceId);
|
|
@@ -33111,13 +33548,16 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
|
|
|
33111
33548
|
const runtimeByArtifact = buildRuntimeByArtifact(dispatchJournal, ref);
|
|
33112
33549
|
const nodes = arts.map((a) => {
|
|
33113
33550
|
const info = nodeInfo(model, a);
|
|
33114
|
-
const
|
|
33551
|
+
const observedRuntime = runtimeByArtifact.get(a.id);
|
|
33552
|
+
const runtime = observedRuntime && shouldExposeRuntime(model, a.id, info, observedRuntime) ? observedRuntime : void 0;
|
|
33553
|
+
const baseStage = deriveNodeStage(info);
|
|
33554
|
+
const stage = runtime?.status === "running" && (runtime.action === "produce" || runtime.action === "integrate") && (baseStage === "embryo" || baseStage === "merged" || baseStage === "review") ? "editing" : baseStage;
|
|
33115
33555
|
const lifecycle = lifecycleOf(model, a.id);
|
|
33116
33556
|
return {
|
|
33117
33557
|
id: a.id,
|
|
33118
33558
|
type: a.type,
|
|
33119
33559
|
label: nodeLabel(a),
|
|
33120
|
-
stage
|
|
33560
|
+
stage,
|
|
33121
33561
|
currentRev: a.currentRev,
|
|
33122
33562
|
queue: info.queueLen,
|
|
33123
33563
|
blocked: blockedOf(model, a.id).blocked,
|
|
@@ -33131,6 +33571,7 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
|
|
|
33131
33571
|
(a) => a.inputs.map((e) => ({ from: a.id, to: e.to, pinned: e.pinned, required: e.required }))
|
|
33132
33572
|
);
|
|
33133
33573
|
const activity = buildActivity(model, arts, ref);
|
|
33574
|
+
const coordinatorActivity = buildCoordinatorActivity(arts, ops, ref);
|
|
33134
33575
|
const acceptance = buildAcceptance(model, arts);
|
|
33135
33576
|
const planning = planningStatusOf(model, arts);
|
|
33136
33577
|
const spec = workorderSpecOf(model, arts);
|
|
@@ -33138,11 +33579,72 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
|
|
|
33138
33579
|
summary,
|
|
33139
33580
|
graph: { nodes, edges },
|
|
33140
33581
|
activity,
|
|
33582
|
+
coordinatorActivity,
|
|
33141
33583
|
acceptance,
|
|
33142
33584
|
...spec?.acceptanceCriteria ? { acceptanceCriteria: spec.acceptanceCriteria } : {},
|
|
33143
33585
|
planning
|
|
33144
33586
|
};
|
|
33145
33587
|
}
|
|
33588
|
+
function buildWorkorderDraftDetail(draft, resolveActor) {
|
|
33589
|
+
const ref = (id) => {
|
|
33590
|
+
const extra = resolveActor?.(id);
|
|
33591
|
+
return { id, ...extra?.name ? { name: extra.name } : {}, ...extra?.role ? { role: extra.role } : {}, ...extra?.avatar ? { avatar: extra.avatar } : {} };
|
|
33592
|
+
};
|
|
33593
|
+
const spawns = draft.plan.ops.filter((op) => op.action === "spawn" && !!op.id);
|
|
33594
|
+
const nodes = spawns.map((op) => ({
|
|
33595
|
+
id: op.id,
|
|
33596
|
+
type: op.type,
|
|
33597
|
+
label: op.title ?? op.description ?? op.id,
|
|
33598
|
+
stage: "embryo",
|
|
33599
|
+
currentRev: null,
|
|
33600
|
+
queue: 0,
|
|
33601
|
+
blocked: false,
|
|
33602
|
+
owner: ref(op.owner ?? draft.draftedBy)
|
|
33603
|
+
}));
|
|
33604
|
+
const edges = [
|
|
33605
|
+
...spawns.flatMap((op) => (op.inputs ?? []).map((inp) => ({ from: op.id, to: inp.to, pinned: null, required: inp.required ?? true }))),
|
|
33606
|
+
...draft.plan.ops.filter((op) => op.action === "link").map((op) => ({ from: op.artifactId, to: op.to, pinned: null, required: op.required ?? true }))
|
|
33607
|
+
];
|
|
33608
|
+
const rootSpawn = spawns.find((op) => op.id === draft.rootArtifactId);
|
|
33609
|
+
const title = rootSpawn?.title ?? rootSpawn?.description ?? draft.workspace;
|
|
33610
|
+
const participantIds = [...new Set(nodes.map((n) => n.owner.id))];
|
|
33611
|
+
const summary = {
|
|
33612
|
+
id: draft.workspace,
|
|
33613
|
+
title,
|
|
33614
|
+
...draft.seed.brief && draft.seed.brief !== title ? { description: draft.seed.brief } : {},
|
|
33615
|
+
...draft.seed.goal ? { goal: draft.seed.goal } : {},
|
|
33616
|
+
stage: "draft",
|
|
33617
|
+
owner: ref(draft.draftedBy),
|
|
33618
|
+
participants: participantIds.map((id) => ref(id)),
|
|
33619
|
+
artifactCount: nodes.length,
|
|
33620
|
+
progress: { concluded: 0, total: nodes.length },
|
|
33621
|
+
updatedAt: draft.at,
|
|
33622
|
+
projectId: null,
|
|
33623
|
+
...draft.seed.dispatch === false ? { dispatchPaused: true } : {}
|
|
33624
|
+
};
|
|
33625
|
+
return {
|
|
33626
|
+
summary,
|
|
33627
|
+
graph: { nodes, edges },
|
|
33628
|
+
activity: [],
|
|
33629
|
+
coordinatorActivity: [],
|
|
33630
|
+
acceptance: [],
|
|
33631
|
+
...draft.seed.acceptanceCriteria ? { acceptanceCriteria: draft.seed.acceptanceCriteria } : {}
|
|
33632
|
+
};
|
|
33633
|
+
}
|
|
33634
|
+
function isRuntimeFailure(runtime) {
|
|
33635
|
+
return runtime.status === "failed" || runtime.status === "fused" || runtime.status === "escalated";
|
|
33636
|
+
}
|
|
33637
|
+
function shouldExposeRuntime(model, artifactId, info, runtime) {
|
|
33638
|
+
if (!isRuntimeFailure(runtime)) return true;
|
|
33639
|
+
if (info.concluded) return false;
|
|
33640
|
+
const lastOpAt = model.lastOpAt.get(artifactId);
|
|
33641
|
+
const runtimeAt = runtime.finishedAt ?? runtime.startedAt;
|
|
33642
|
+
if (!lastOpAt || !runtimeAt) return true;
|
|
33643
|
+
const lastOpTime = Date.parse(lastOpAt);
|
|
33644
|
+
const runtimeTime = Date.parse(runtimeAt);
|
|
33645
|
+
if (!Number.isFinite(lastOpTime) || !Number.isFinite(runtimeTime)) return true;
|
|
33646
|
+
return lastOpTime <= runtimeTime;
|
|
33647
|
+
}
|
|
33146
33648
|
function buildRuntimeByArtifact(entries, ref) {
|
|
33147
33649
|
const byJob = /* @__PURE__ */ new Map();
|
|
33148
33650
|
for (const entry of entries) {
|
|
@@ -33191,7 +33693,16 @@ function buildRuntimeByArtifact(entries, ref) {
|
|
|
33191
33693
|
const byArtifact = /* @__PURE__ */ new Map();
|
|
33192
33694
|
for (const value of byJob.values()) {
|
|
33193
33695
|
const old = byArtifact.get(value.artifactId);
|
|
33194
|
-
if (!old
|
|
33696
|
+
if (!old) {
|
|
33697
|
+
byArtifact.set(value.artifactId, value);
|
|
33698
|
+
continue;
|
|
33699
|
+
}
|
|
33700
|
+
if (old.status === "running" && value.status !== "running") continue;
|
|
33701
|
+
if (old.status !== "running" && value.status === "running") {
|
|
33702
|
+
byArtifact.set(value.artifactId, value);
|
|
33703
|
+
continue;
|
|
33704
|
+
}
|
|
33705
|
+
if (old.at < value.at) byArtifact.set(value.artifactId, value);
|
|
33195
33706
|
}
|
|
33196
33707
|
return new Map([...byArtifact].map(([id, value]) => {
|
|
33197
33708
|
const { at: _at, artifactId: _artifactId, ...runtime } = value;
|
|
@@ -33249,6 +33760,65 @@ function buildActivity(model, arts, ref) {
|
|
|
33249
33760
|
}
|
|
33250
33761
|
return events.sort((x2, y) => x2.at < y.at ? -1 : x2.at > y.at ? 1 : x2.seq - y.seq);
|
|
33251
33762
|
}
|
|
33763
|
+
function buildCoordinatorActivity(arts, ops, ref) {
|
|
33764
|
+
const inWorkspace = new Set(arts.map((a) => a.id));
|
|
33765
|
+
const firstRevisionAt = ops.filter((op) => op.kind === "propose_revision" && inWorkspace.has(op.artifactId)).map((op) => op.timestamp).sort()[0];
|
|
33766
|
+
const edits = [];
|
|
33767
|
+
for (const op of ops) {
|
|
33768
|
+
if (!inWorkspace.has(op.artifactId)) continue;
|
|
33769
|
+
if (op.actor !== COORDINATOR_ACTOR) continue;
|
|
33770
|
+
if (!COORDINATOR_EDIT_KINDS.has(op.kind)) continue;
|
|
33771
|
+
const kind = op.kind;
|
|
33772
|
+
if ((kind === "spawn_artifact" || kind === "link_input") && (!firstRevisionAt || op.timestamp < firstRevisionAt)) {
|
|
33773
|
+
continue;
|
|
33774
|
+
}
|
|
33775
|
+
const detail = coordinatorEditDetail(kind, op.payload, ref);
|
|
33776
|
+
edits.push({
|
|
33777
|
+
seq: op.seq,
|
|
33778
|
+
at: op.timestamp,
|
|
33779
|
+
actor: ref(op.actor),
|
|
33780
|
+
kind,
|
|
33781
|
+
artifactId: op.artifactId,
|
|
33782
|
+
label: COORDINATOR_EDIT_LABEL[kind],
|
|
33783
|
+
...detail ? { detail } : {}
|
|
33784
|
+
});
|
|
33785
|
+
}
|
|
33786
|
+
return edits.sort((x2, y) => x2.at < y.at ? -1 : x2.at > y.at ? 1 : x2.seq - y.seq);
|
|
33787
|
+
}
|
|
33788
|
+
function coordinatorEditDetail(kind, payload, ref) {
|
|
33789
|
+
const str2 = (v2) => typeof v2 === "string" && v2.trim() ? v2 : void 0;
|
|
33790
|
+
switch (kind) {
|
|
33791
|
+
case "link_input":
|
|
33792
|
+
case "unlink_input":
|
|
33793
|
+
return str2(payload.to);
|
|
33794
|
+
case "spawn_artifact":
|
|
33795
|
+
return str2(payload.title) ?? str2(payload.type);
|
|
33796
|
+
case "assign_owner": {
|
|
33797
|
+
const owner = str2(payload.owner);
|
|
33798
|
+
return owner ? ref(owner).name ?? owner : void 0;
|
|
33799
|
+
}
|
|
33800
|
+
case "bump_pin": {
|
|
33801
|
+
const to = str2(payload.to);
|
|
33802
|
+
const pinned = str2(payload.pinned);
|
|
33803
|
+
return to && pinned ? `${to} \u2192 ${pinned}` : to ?? pinned;
|
|
33804
|
+
}
|
|
33805
|
+
case "report_gap":
|
|
33806
|
+
return str2(payload.description);
|
|
33807
|
+
case "edit_artifact": {
|
|
33808
|
+
const changed = ["title", "description", "docType", "fields"].filter((k2) => payload[k2] !== void 0);
|
|
33809
|
+
return changed.length ? changed.join("\u3001") : void 0;
|
|
33810
|
+
}
|
|
33811
|
+
case "seal": {
|
|
33812
|
+
const reason = str2(payload.reason);
|
|
33813
|
+
const note = str2(payload.note);
|
|
33814
|
+
return [reason, note].filter(Boolean).join("\uFF1A") || void 0;
|
|
33815
|
+
}
|
|
33816
|
+
case "reopen":
|
|
33817
|
+
return str2(payload.note);
|
|
33818
|
+
default:
|
|
33819
|
+
return void 0;
|
|
33820
|
+
}
|
|
33821
|
+
}
|
|
33252
33822
|
function buildAcceptance(model, arts) {
|
|
33253
33823
|
return arts.map((a) => ({
|
|
33254
33824
|
artifactId: a.id,
|
|
@@ -33256,12 +33826,36 @@ function buildAcceptance(model, arts) {
|
|
|
33256
33826
|
state: isConcluded(model, a.id) ? "done" : a.currentRev ? "active" : "pending"
|
|
33257
33827
|
}));
|
|
33258
33828
|
}
|
|
33829
|
+
var COORDINATOR_EDIT_KINDS, COORDINATOR_EDIT_LABEL, COORDINATOR_ACTOR;
|
|
33259
33830
|
var init_workorder_detail = __esm({
|
|
33260
33831
|
"../server/src/domains/collab/workorder-detail.ts"() {
|
|
33261
33832
|
"use strict";
|
|
33262
33833
|
init_src2();
|
|
33263
33834
|
init_node_state();
|
|
33264
33835
|
init_workorders();
|
|
33836
|
+
COORDINATOR_EDIT_KINDS = /* @__PURE__ */ new Set([
|
|
33837
|
+
"link_input",
|
|
33838
|
+
"unlink_input",
|
|
33839
|
+
"spawn_artifact",
|
|
33840
|
+
"assign_owner",
|
|
33841
|
+
"bump_pin",
|
|
33842
|
+
"report_gap",
|
|
33843
|
+
"edit_artifact",
|
|
33844
|
+
"seal",
|
|
33845
|
+
"reopen"
|
|
33846
|
+
]);
|
|
33847
|
+
COORDINATOR_EDIT_LABEL = {
|
|
33848
|
+
link_input: "\u63A5\u5165\u4F9D\u8D56",
|
|
33849
|
+
unlink_input: "\u79FB\u9664\u4F9D\u8D56",
|
|
33850
|
+
spawn_artifact: "\u65B0\u5EFA\u4EA4\u4ED8\u7269",
|
|
33851
|
+
assign_owner: "\u6539\u6D3E",
|
|
33852
|
+
bump_pin: "\u89E6\u53D1\u8FD4\u5DE5",
|
|
33853
|
+
report_gap: "\u7559\u7F3A\u53E3",
|
|
33854
|
+
edit_artifact: "\u6539\u4EA4\u4ED8\u7269\u4FE1\u606F",
|
|
33855
|
+
seal: "\u5C01\u5B58",
|
|
33856
|
+
reopen: "\u91CD\u542F"
|
|
33857
|
+
};
|
|
33858
|
+
COORDINATOR_ACTOR = "actor:agent:coordinator";
|
|
33265
33859
|
}
|
|
33266
33860
|
});
|
|
33267
33861
|
|
|
@@ -33311,7 +33905,7 @@ function buildInbox(model, me) {
|
|
|
33311
33905
|
if (gate?.gate && gate.gate.eligible.includes(me)) {
|
|
33312
33906
|
const voted = (model.reviews.get(gate.head) ?? []).some((v2) => v2.author === me);
|
|
33313
33907
|
if (!voted) {
|
|
33314
|
-
out.push({ kind: "review-request", artifactId: a.id, workspace: ws,
|
|
33908
|
+
out.push({ kind: "review-request", artifactId: a.id, workspace: ws, since: gate.at, actionRequired: true, summary: `\u5F85\u4F60\u5BA1\u6279\uFF1A${label}\uFF08quorum=${gate.gate.quorum}\uFF09` });
|
|
33315
33909
|
continue;
|
|
33316
33910
|
}
|
|
33317
33911
|
}
|
|
@@ -33518,15 +34112,17 @@ function collabDomain(opts) {
|
|
|
33518
34112
|
return { status: 200, body: { items: buildWorkorderSummaries(kernel.model, resolve3, resolveProject, dispatchPausedOf) } };
|
|
33519
34113
|
});
|
|
33520
34114
|
router.get("/api/workorders/:id", async (req) => {
|
|
33521
|
-
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
34115
|
+
const { kernel, oplog, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
33522
34116
|
const resolve3 = await buildResolver(registry2);
|
|
33523
34117
|
const resolveProject = await buildProjectResolver(artifacts);
|
|
33524
34118
|
const dispatchPausedOf = await buildDispatchPausedResolver(artifacts);
|
|
33525
|
-
const
|
|
33526
|
-
|
|
33527
|
-
|
|
33528
|
-
}
|
|
33529
|
-
|
|
34119
|
+
const wsArts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === req.params.id);
|
|
34120
|
+
const ops = (await Promise.all(wsArts.map((a) => oplog.read(a.id)))).flat();
|
|
34121
|
+
const detail = buildWorkorderDetail(kernel.model, req.params.id, resolve3, resolveProject, opts.dispatchJournal?.() ?? [], dispatchPausedOf, ops);
|
|
34122
|
+
if (detail) return { status: 200, body: detail };
|
|
34123
|
+
const draft = opts.workorderDrafts?.forWorkspace(req.params.id);
|
|
34124
|
+
if (draft) return { status: 200, body: buildWorkorderDraftDetail(draft, resolve3) };
|
|
34125
|
+
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${req.params.id}` } } };
|
|
33530
34126
|
});
|
|
33531
34127
|
router.post("/api/workorders/:id/dispatch", async (req) => {
|
|
33532
34128
|
const { kernel, artifacts } = await resolveCtx(req.auth.companyId);
|
|
@@ -33549,7 +34145,7 @@ function collabDomain(opts) {
|
|
|
33549
34145
|
const q = req.query;
|
|
33550
34146
|
const audit = {};
|
|
33551
34147
|
const actor = q.get("actor");
|
|
33552
|
-
|
|
34148
|
+
audit.actor = actor && actor !== "me" ? actor : req.auth.actor;
|
|
33553
34149
|
const kind = q.get("kind");
|
|
33554
34150
|
if (kind) audit.kind = kind;
|
|
33555
34151
|
const from = q.get("from");
|
|
@@ -33613,12 +34209,75 @@ var init_collab = __esm({
|
|
|
33613
34209
|
});
|
|
33614
34210
|
|
|
33615
34211
|
// ../server/src/domains/trace/service.ts
|
|
33616
|
-
|
|
34212
|
+
function rawObj(event) {
|
|
34213
|
+
const payload = event.payload;
|
|
34214
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return null;
|
|
34215
|
+
const raw = payload["raw"];
|
|
34216
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
34217
|
+
}
|
|
34218
|
+
function payloadObj(event) {
|
|
34219
|
+
const payload = event.payload;
|
|
34220
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
34221
|
+
}
|
|
34222
|
+
function modelChunkKind(event) {
|
|
34223
|
+
if (event.stream !== "model") return null;
|
|
34224
|
+
const payload = payloadObj(event);
|
|
34225
|
+
const kind = payload?.["kind"] ?? rawObj(event)?.["kind"];
|
|
34226
|
+
if (kind === "message" || kind === "thought") return kind;
|
|
34227
|
+
return null;
|
|
34228
|
+
}
|
|
34229
|
+
function modelText(event) {
|
|
34230
|
+
const raw = rawObj(event);
|
|
34231
|
+
const text = raw?.["text"];
|
|
34232
|
+
if (typeof text === "string") return text;
|
|
34233
|
+
return typeof event.message === "string" ? event.message : null;
|
|
34234
|
+
}
|
|
34235
|
+
function eventTimeMs(event) {
|
|
34236
|
+
const t = Date.parse(event.startedAt ?? event.createdAt);
|
|
34237
|
+
return Number.isFinite(t) ? t : null;
|
|
34238
|
+
}
|
|
34239
|
+
function eventEndTimeMs(event) {
|
|
34240
|
+
const t = Date.parse(event.endedAt ?? event.startedAt ?? event.createdAt);
|
|
34241
|
+
return Number.isFinite(t) ? t : null;
|
|
34242
|
+
}
|
|
34243
|
+
function canCompactModelChunk(prev, next) {
|
|
34244
|
+
const kind = modelChunkKind(prev);
|
|
34245
|
+
if (!kind || kind !== modelChunkKind(next)) return false;
|
|
34246
|
+
const a = eventEndTimeMs(prev);
|
|
34247
|
+
const b2 = eventTimeMs(next);
|
|
34248
|
+
if (a === null || b2 === null) return false;
|
|
34249
|
+
return Math.abs(b2 - a) <= MODEL_CHUNK_COMPACT_WINDOW_MS;
|
|
34250
|
+
}
|
|
34251
|
+
function compactPair(prev, next) {
|
|
34252
|
+
const text = `${modelText(prev) ?? ""}${modelText(next) ?? ""}`;
|
|
34253
|
+
const prevPayload = payloadObj(prev) ?? {};
|
|
34254
|
+
const prevRaw = rawObj(prev) ?? {};
|
|
34255
|
+
return {
|
|
34256
|
+
...prev,
|
|
34257
|
+
id: next.id,
|
|
34258
|
+
seq: next.seq,
|
|
34259
|
+
message: text.slice(0, 500),
|
|
34260
|
+
payload: { ...prevPayload, raw: { ...prevRaw, text } },
|
|
34261
|
+
endedAt: next.endedAt ?? next.startedAt ?? prev.endedAt
|
|
34262
|
+
};
|
|
34263
|
+
}
|
|
34264
|
+
function compactModelChunks(rows) {
|
|
34265
|
+
const out = [];
|
|
34266
|
+
for (const row of rows) {
|
|
34267
|
+
const prev = out[out.length - 1];
|
|
34268
|
+
if (prev && canCompactModelChunk(prev, row)) out[out.length - 1] = compactPair(prev, row);
|
|
34269
|
+
else out.push(row);
|
|
34270
|
+
}
|
|
34271
|
+
return out;
|
|
34272
|
+
}
|
|
34273
|
+
var EVENT_PAGE_LIMIT, MODEL_CHUNK_COMPACT_WINDOW_MS, MODEL_CHUNK_RAW_MULTIPLIER, TraceService;
|
|
33617
34274
|
var init_service4 = __esm({
|
|
33618
34275
|
"../server/src/domains/trace/service.ts"() {
|
|
33619
34276
|
"use strict";
|
|
33620
34277
|
init_src();
|
|
33621
34278
|
EVENT_PAGE_LIMIT = 200;
|
|
34279
|
+
MODEL_CHUNK_COMPACT_WINDOW_MS = 1500;
|
|
34280
|
+
MODEL_CHUNK_RAW_MULTIPLIER = 20;
|
|
33622
34281
|
TraceService = class {
|
|
33623
34282
|
store;
|
|
33624
34283
|
constructor(opts) {
|
|
@@ -33695,12 +34354,14 @@ var init_service4 = __esm({
|
|
|
33695
34354
|
}
|
|
33696
34355
|
async listEvents(runId, opts) {
|
|
33697
34356
|
const limit = Math.min(opts.limit ?? EVENT_PAGE_LIMIT, EVENT_PAGE_LIMIT);
|
|
34357
|
+
const rawLimit = Math.min(limit * MODEL_CHUNK_RAW_MULTIPLIER + 1, 5e3);
|
|
33698
34358
|
const rows = await this.store.listEvents(runId, {
|
|
33699
34359
|
...opts.afterSeq !== void 0 ? { afterSeq: opts.afterSeq } : {},
|
|
33700
|
-
limit:
|
|
34360
|
+
limit: rawLimit
|
|
33701
34361
|
});
|
|
33702
|
-
const
|
|
33703
|
-
const
|
|
34362
|
+
const compacted = compactModelChunks(rows);
|
|
34363
|
+
const hasMore = rows.length > rawLimit - 1 || compacted.length > limit;
|
|
34364
|
+
const items = compacted.length > limit ? compacted.slice(0, limit) : compacted;
|
|
33704
34365
|
const nextCursor = hasMore ? String(items[items.length - 1].seq) : null;
|
|
33705
34366
|
return { items, nextCursor };
|
|
33706
34367
|
}
|
|
@@ -34266,6 +34927,18 @@ var init_sink = __esm({
|
|
|
34266
34927
|
this.onError = opts.onError ?? (() => {
|
|
34267
34928
|
});
|
|
34268
34929
|
}
|
|
34930
|
+
runtimeKindFor(session) {
|
|
34931
|
+
if (this.runtimeKind !== "auto") return this.runtimeKind;
|
|
34932
|
+
const runtimeKind = session.runtimeKind?.toLowerCase();
|
|
34933
|
+
if (runtimeKind === "codex") return "codex";
|
|
34934
|
+
if (runtimeKind === "hermes") return "hermes";
|
|
34935
|
+
if (runtimeKind === "claude" || runtimeKind === "claude-code") return "claude-code";
|
|
34936
|
+
const id = session.sessionId.toLowerCase();
|
|
34937
|
+
if (id.startsWith("codex-")) return "codex";
|
|
34938
|
+
if (id.startsWith("hermes-")) return "hermes";
|
|
34939
|
+
if (id.startsWith("claude-") || id.startsWith("claudecode-")) return "claude-code";
|
|
34940
|
+
return "custom";
|
|
34941
|
+
}
|
|
34269
34942
|
enqueue(sessionId, step) {
|
|
34270
34943
|
const st = this.sessions.get(sessionId);
|
|
34271
34944
|
if (!st) return;
|
|
@@ -34278,7 +34951,7 @@ var init_sink = __esm({
|
|
|
34278
34951
|
await this.store.createRun({
|
|
34279
34952
|
id: session.sessionId,
|
|
34280
34953
|
actorId: session.actor,
|
|
34281
|
-
runtimeKind: this.
|
|
34954
|
+
runtimeKind: this.runtimeKindFor(session),
|
|
34282
34955
|
triggerKind: "dispatch",
|
|
34283
34956
|
triggerRef: session.jobKey,
|
|
34284
34957
|
jobKey: session.jobKey,
|
|
@@ -34395,6 +35068,69 @@ var init_sink = __esm({
|
|
|
34395
35068
|
}
|
|
34396
35069
|
});
|
|
34397
35070
|
|
|
35071
|
+
// ../server/src/domains/trace/chat-parts.ts
|
|
35072
|
+
function asObj2(v2) {
|
|
35073
|
+
return v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
|
|
35074
|
+
}
|
|
35075
|
+
function textOf2(v2) {
|
|
35076
|
+
if (typeof v2 === "string") return v2;
|
|
35077
|
+
if (Array.isArray(v2)) return v2.map(textOf2).filter(Boolean).join("\n") || void 0;
|
|
35078
|
+
const o = asObj2(v2);
|
|
35079
|
+
if (o) {
|
|
35080
|
+
if (typeof o.text === "string") return o.text;
|
|
35081
|
+
if (typeof o.content === "string") return o.content;
|
|
35082
|
+
if (typeof o.thinking === "string") return o.thinking;
|
|
35083
|
+
}
|
|
35084
|
+
return void 0;
|
|
35085
|
+
}
|
|
35086
|
+
function truncate(s2, limit = OUTPUT_PREVIEW_LIMIT) {
|
|
35087
|
+
return s2.length > limit ? `${s2.slice(0, limit)}\u2026` : s2;
|
|
35088
|
+
}
|
|
35089
|
+
function deriveChatPartsFromEvents(events) {
|
|
35090
|
+
const parts = [];
|
|
35091
|
+
for (const ev of [...events].sort((a, b2) => a.seq - b2.seq)) {
|
|
35092
|
+
const p2 = asObj2(ev.payload);
|
|
35093
|
+
const kind = p2 && typeof p2.kind === "string" ? p2.kind : void 0;
|
|
35094
|
+
const raw = p2?.raw;
|
|
35095
|
+
if (kind === "thought") {
|
|
35096
|
+
const text = textOf2(raw);
|
|
35097
|
+
if (text) parts.push({ type: "thinking", seq: ev.seq, text });
|
|
35098
|
+
continue;
|
|
35099
|
+
}
|
|
35100
|
+
if (kind === "tool_use") {
|
|
35101
|
+
const ro = asObj2(raw);
|
|
35102
|
+
parts.push({
|
|
35103
|
+
type: "tool_use",
|
|
35104
|
+
seq: ev.seq,
|
|
35105
|
+
toolName: ro && typeof ro.name === "string" ? ro.name : "tool",
|
|
35106
|
+
...ro && ro.input !== void 0 ? { input: ro.input } : {},
|
|
35107
|
+
...ro && typeof ro.id === "string" ? { toolCallId: ro.id } : {}
|
|
35108
|
+
});
|
|
35109
|
+
continue;
|
|
35110
|
+
}
|
|
35111
|
+
if (kind === "tool_result") {
|
|
35112
|
+
const ro = asObj2(raw);
|
|
35113
|
+
const output = textOf2(raw) ?? "";
|
|
35114
|
+
parts.push({
|
|
35115
|
+
type: "tool_result",
|
|
35116
|
+
seq: ev.seq,
|
|
35117
|
+
output: truncate(output),
|
|
35118
|
+
isError: ro?.is_error === true || ro?.isError === true,
|
|
35119
|
+
...ro && typeof ro.tool_use_id === "string" ? { toolCallId: ro.tool_use_id } : {}
|
|
35120
|
+
});
|
|
35121
|
+
continue;
|
|
35122
|
+
}
|
|
35123
|
+
}
|
|
35124
|
+
return parts;
|
|
35125
|
+
}
|
|
35126
|
+
var OUTPUT_PREVIEW_LIMIT;
|
|
35127
|
+
var init_chat_parts = __esm({
|
|
35128
|
+
"../server/src/domains/trace/chat-parts.ts"() {
|
|
35129
|
+
"use strict";
|
|
35130
|
+
OUTPUT_PREVIEW_LIMIT = 4e3;
|
|
35131
|
+
}
|
|
35132
|
+
});
|
|
35133
|
+
|
|
34398
35134
|
// ../server/src/domains/trace/index.ts
|
|
34399
35135
|
function createTraceDomain(opts) {
|
|
34400
35136
|
const service = new TraceService({ store: opts.store });
|
|
@@ -34408,6 +35144,7 @@ var init_trace2 = __esm({
|
|
|
34408
35144
|
init_service4();
|
|
34409
35145
|
init_routes5();
|
|
34410
35146
|
init_sink();
|
|
35147
|
+
init_chat_parts();
|
|
34411
35148
|
}
|
|
34412
35149
|
});
|
|
34413
35150
|
|
|
@@ -34418,6 +35155,15 @@ function createChatSessionsDomain(opts) {
|
|
|
34418
35155
|
const binding = await registry2.getBinding(aiActorId).catch(() => null);
|
|
34419
35156
|
return binding?.nodeId ?? null;
|
|
34420
35157
|
}
|
|
35158
|
+
async function enrichMessages(messages) {
|
|
35159
|
+
if (!opts.trace) return messages;
|
|
35160
|
+
return Promise.all(messages.map(async (m2) => {
|
|
35161
|
+
if (!m2.runId) return m2;
|
|
35162
|
+
const events = await opts.trace.listEvents(m2.runId, {}).catch(() => []);
|
|
35163
|
+
const parts = deriveChatPartsFromEvents(events);
|
|
35164
|
+
return parts.length ? { ...m2, parts } : m2;
|
|
35165
|
+
}));
|
|
35166
|
+
}
|
|
34421
35167
|
return (router) => {
|
|
34422
35168
|
router.get("/api/chat-sessions", async (req) => {
|
|
34423
35169
|
const sessions = await store.listSessions(req.auth.actor);
|
|
@@ -34444,10 +35190,11 @@ function createChatSessionsDomain(opts) {
|
|
|
34444
35190
|
router.get("/api/chat-sessions/:id", async (req) => {
|
|
34445
35191
|
const session = await store.getSession(req.params.id);
|
|
34446
35192
|
if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
34447
|
-
const [
|
|
35193
|
+
const [rawMessages, curRuntimeId] = await Promise.all([
|
|
34448
35194
|
store.listMessages(session.id),
|
|
34449
35195
|
currentRuntimeId(session.aiActorId)
|
|
34450
35196
|
]);
|
|
35197
|
+
const messages = await enrichMessages(rawMessages);
|
|
34451
35198
|
const detail = { ...session, currentRuntimeId: curRuntimeId, messages };
|
|
34452
35199
|
return { status: 200, body: detail };
|
|
34453
35200
|
});
|
|
@@ -34482,12 +35229,30 @@ function createChatSessionsDomain(opts) {
|
|
|
34482
35229
|
role: body.role,
|
|
34483
35230
|
content: body.content,
|
|
34484
35231
|
seq: existing.length + 1,
|
|
34485
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
35232
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35233
|
+
...body.runId ? { runId: body.runId } : {}
|
|
34486
35234
|
};
|
|
34487
35235
|
await store.appendMessage(msg);
|
|
34488
35236
|
await store.updateSession(req.params.id, { touchedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
34489
35237
|
return { status: 201, body: msg };
|
|
34490
35238
|
});
|
|
35239
|
+
router.get("/api/chat-sessions/:id/work-orders", async (req) => {
|
|
35240
|
+
const session = await store.getSession(req.params.id);
|
|
35241
|
+
if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
35242
|
+
const workOrderIds = await store.listWorkOrdersForSession(req.params.id);
|
|
35243
|
+
if (!opts.kernel || workOrderIds.length === 0) return { status: 200, body: { items: [] } };
|
|
35244
|
+
const bindings = opts.artifactState ? await opts.artifactState.listWorkspaceBindings() : [];
|
|
35245
|
+
const byWo = new Map(bindings.map((b2) => [b2.workOrderId, b2.projectId]));
|
|
35246
|
+
const actors = opts.kernel ? await registry2.listActors().catch(() => []) : [];
|
|
35247
|
+
const byId = new Map(actors.map((a) => [a.id, a]));
|
|
35248
|
+
const resolver = (id) => {
|
|
35249
|
+
const a = byId.get(id);
|
|
35250
|
+
return a ? { name: a.name } : void 0;
|
|
35251
|
+
};
|
|
35252
|
+
const allSummaries = buildWorkorderSummaries(opts.kernel.model, resolver, (wo) => byWo.get(wo) ?? null);
|
|
35253
|
+
const items = allSummaries.filter((s2) => workOrderIds.includes(s2.id));
|
|
35254
|
+
return { status: 200, body: { items } };
|
|
35255
|
+
});
|
|
34491
35256
|
};
|
|
34492
35257
|
}
|
|
34493
35258
|
var import_node_crypto10;
|
|
@@ -34496,6 +35261,8 @@ var init_chat_sessions = __esm({
|
|
|
34496
35261
|
"use strict";
|
|
34497
35262
|
import_node_crypto10 = require("node:crypto");
|
|
34498
35263
|
init_router();
|
|
35264
|
+
init_workorders();
|
|
35265
|
+
init_chat_parts();
|
|
34499
35266
|
}
|
|
34500
35267
|
});
|
|
34501
35268
|
|
|
@@ -35826,22 +36593,22 @@ var init_engine = __esm({
|
|
|
35826
36593
|
});
|
|
35827
36594
|
|
|
35828
36595
|
// ../server/src/mirror/fs-state.ts
|
|
35829
|
-
var
|
|
36596
|
+
var fs7, FsMirrorStateStore;
|
|
35830
36597
|
var init_fs_state = __esm({
|
|
35831
36598
|
"../server/src/mirror/fs-state.ts"() {
|
|
35832
36599
|
"use strict";
|
|
35833
|
-
|
|
36600
|
+
fs7 = __toESM(require("node:fs"), 1);
|
|
35834
36601
|
FsMirrorStateStore = class {
|
|
35835
36602
|
constructor(file) {
|
|
35836
36603
|
this.file = file;
|
|
35837
|
-
if (
|
|
35838
|
-
const obj = JSON.parse(
|
|
36604
|
+
if (fs7.existsSync(file)) {
|
|
36605
|
+
const obj = JSON.parse(fs7.readFileSync(file, "utf8"));
|
|
35839
36606
|
for (const [k2, v2] of Object.entries(obj)) this.map.set(k2, v2);
|
|
35840
36607
|
}
|
|
35841
36608
|
}
|
|
35842
36609
|
map = /* @__PURE__ */ new Map();
|
|
35843
36610
|
flush() {
|
|
35844
|
-
|
|
36611
|
+
fs7.writeFileSync(this.file, JSON.stringify(Object.fromEntries(this.map), null, 2));
|
|
35845
36612
|
}
|
|
35846
36613
|
async get(artifactId) {
|
|
35847
36614
|
return this.map.get(artifactId) ?? null;
|
|
@@ -35858,12 +36625,12 @@ var init_fs_state = __esm({
|
|
|
35858
36625
|
});
|
|
35859
36626
|
|
|
35860
36627
|
// ../server/src/git/integrate.ts
|
|
35861
|
-
var import_node_child_process2,
|
|
36628
|
+
var import_node_child_process2, fs8, path4, GitRemoteIntegrator;
|
|
35862
36629
|
var init_integrate = __esm({
|
|
35863
36630
|
"../server/src/git/integrate.ts"() {
|
|
35864
36631
|
"use strict";
|
|
35865
36632
|
import_node_child_process2 = require("node:child_process");
|
|
35866
|
-
|
|
36633
|
+
fs8 = __toESM(require("node:fs"), 1);
|
|
35867
36634
|
path4 = __toESM(require("node:path"), 1);
|
|
35868
36635
|
GitRemoteIntegrator = class {
|
|
35869
36636
|
constructor(remote, cloneDir, develop = "develop") {
|
|
@@ -35881,8 +36648,8 @@ var init_integrate = __esm({
|
|
|
35881
36648
|
}
|
|
35882
36649
|
/** 确保本地工作克隆存在并拉到最新远程态。幂等。 */
|
|
35883
36650
|
sync() {
|
|
35884
|
-
if (!
|
|
35885
|
-
|
|
36651
|
+
if (!fs8.existsSync(path4.join(this.cloneDir, ".git"))) {
|
|
36652
|
+
fs8.mkdirSync(path4.dirname(this.cloneDir), { recursive: true });
|
|
35886
36653
|
(0, import_node_child_process2.execFileSync)("git", ["clone", "--quiet", this.remote, this.cloneDir], { env: this.env, maxBuffer: 256 * 1024 * 1024 });
|
|
35887
36654
|
this.git(["config", "user.email", "oasis@local"]);
|
|
35888
36655
|
this.git(["config", "user.name", "oasis"]);
|
|
@@ -36021,13 +36788,14 @@ var init_resolve = __esm({
|
|
|
36021
36788
|
});
|
|
36022
36789
|
|
|
36023
36790
|
// ../server/src/coordinator/worker.ts
|
|
36024
|
-
var import_node_crypto13,
|
|
36791
|
+
var import_node_crypto13, COORDINATOR_ACTOR2, COORDINATOR_SYSTEM_PROMPT, CoordinatorWorker;
|
|
36025
36792
|
var init_worker = __esm({
|
|
36026
36793
|
"../server/src/coordinator/worker.ts"() {
|
|
36027
36794
|
"use strict";
|
|
36028
36795
|
import_node_crypto13 = require("node:crypto");
|
|
36029
36796
|
init_src2();
|
|
36030
|
-
|
|
36797
|
+
init_identity();
|
|
36798
|
+
COORDINATOR_ACTOR2 = LEGACY_COORDINATOR_ACTOR;
|
|
36031
36799
|
COORDINATOR_SYSTEM_PROMPT = `\u4F60\u662F Oasis \u534F\u4F5C\u56FE\u7684**\u534F\u8C03\u8005**\u3002\u4F60\u53EA\u7BA1**\u56FE\u7684\u7ED3\u6784**\uFF08\u8FDE\u8FB9 / \u65B0\u589E / \u53BB\u91CD / \u91CD\u6784 / \u6539\u8282\u70B9\u7684\u6807\u9898\u4E0E brief\uFF09\uFF0C**\u7EDD\u4E0D\u78B0\u5185\u5BB9**\uFF08\u4EA7\u7269\u6B63\u6587\u600E\u4E48\u5199\u662F\u5404 owner \u7684\u6D3B\uFF09\u3002
|
|
36032
36800
|
\u539F\u5219\uFF1A\u2460 **\u53BB\u91CD\u4F18\u5148**\uFF0C\u80FD\u8FDE\u65E2\u6709\u5C31\u522B\u65B0\u5EFA\uFF1B\u2461 \u4F60\u505A\u7ED3\u6784 wiring\u3001\u4E0D\u505A\u5185\u5BB9\u8BC4\u5BA1\uFF1B\u2462 **\u62FF\u4E0D\u51C6\u5C31\u522B\u52A8\u3001\u522B\u786C\u8FDE**\uFF0C\u7ED3\u675F\u65F6\u4E00\u53E5\u8BDD\u8BF4\u660E\u7591\u8651\u3002
|
|
36033
36801
|
|
|
@@ -36037,8 +36805,12 @@ var init_worker = __esm({
|
|
|
36037
36805
|
\u2462 **\u62FF\u4E0D\u51C6\u3001\u6216\u6839\u56E0\u4E0D\u5728\u56FE\u4E0A**\uFF08\u50CF\u8FD0\u884C\u65F6\u5361\u6B7B\u3001\u5185\u5BB9\u5199\u4E0D\u51FA\u3001\u9700\u6C42\u672C\u8EAB\u77DB\u76FE\uFF09\u2192 **\u522B\u52A8\u56FE**\uFF0C\u4E00\u53E5\u8BDD\u8BF4\u6E05\u4F60\u5224\u65AD\u7684\u6839\u56E0 + \u5EFA\u8BAE\u8C01\u5904\u7F6E\uFF0C\u6536\u5DE5\u3002**\u8BEF\u5220\u4E00\u6761\u672C\u8BE5\u7B49\u7684\u6D41\u7A0B\uFF0C\u6BD4\u665A\u6551\u4E00\u4F1A\u513F\u66F4\u7CDF\u3002**
|
|
36038
36806
|
|
|
36039
36807
|
\u3010\u6539\u56FE\uFF1A\u6682\u5B58 \u2192 \u63D0\u4EA4\u3011\u4F60\u80FD\u7528\u7684\u6539\u56FE\u547D\u4EE4\uFF08**\u90FD\u5148\u8FDB\u7F16\u8F91\u7F13\u51B2\u3001\u4E0D\u7ACB\u5373\u6539\u771F\u56FE**\uFF1B\u6539\u5B8C\u7528 \`oasis apply\` \u4E00\u6B21\u6027\u63D0\u4EA4\uFF09\uFF1A
|
|
36040
|
-
|
|
36041
|
-
|
|
36808
|
+
|
|
36809
|
+
> **\u600E\u4E48\u6307\u4E00\u4E2A\u8282\u70B9\uFF08\u5F15\u7528\u5199\u6CD5\uFF09**\uFF1A\u4E0B\u9762\u547D\u4EE4\u91CC\u51E1\u662F\u6307\u8282\u70B9\u7684\u53C2\u6570\uFF08\`<consumer>\`/\`<up>\`/\`<id>\`\uFF09\uFF0C\u4E09\u79CD\u5199\u6CD5\u90FD\u884C\u2014\u2014
|
|
36810
|
+
> **\u88F8 type**\uFF08\u5982 \`dev\`,\u8BE5 type \u53EA\u6709\u4E00\u4E2A\u8282\u70B9\u65F6\u6700\u7701\u4E8B\uFF09/ **\`type:title\`**\uFF08\u5982 \`prd:\u540E\u7AEF\u9700\u6C42\`,\u4E00\u4E2A type \u6709\u591A\u4E2A\u8282\u70B9\u65F6\u7528 title \u533A\u5206\uFF09/ **\u5B8C\u6574 id**\u3002\u7CFB\u7EDF\u81EA\u52A8\u89E3\u6790;**\u5199\u5F97\u592A\u6CDB\u3001\u649E\u5230\u591A\u4E2A**\u4F1A\u628A\u5019\u9009\u5217\u7ED9\u4F60,\u6311\u4E2A\u66F4\u5177\u4F53\u7684\u91CD\u53D1\u5373\u53EF\u3002\u5DE5\u5355\u6839\u7528 \`brief\`\u3002**\u4F60\u4E0D\u7528\u81EA\u5DF1\u7F16 id\u3002**
|
|
36811
|
+
|
|
36812
|
+
- \`oasis link <consumer> --to <up> [--required true]\` \u2014\u2014 \u7ED9 consumer \u52A0\u4E00\u6761\u6307\u5411\u4E0A\u6E38 up \u7684\u4F9D\u8D56\u8FB9\u3002\`<consumer>\` = \u8981\u52A0\u4F9D\u8D56\u7684\u4E0B\u6E38\u8282\u70B9\uFF08\u5F15\u7528,\u89C1\u4E0A\uFF09\uFF1B\`--to <up>\` = \u88AB\u4F9D\u8D56\u7684\u4E0A\u6E38\uFF1B\`--required true\` = \u8FD9\u6761\u662F**\u963B\u585E\u6027**\u4F9D\u8D56\uFF08up \u6CA1 conclude\uFF0Cconsumer \u5C31\u52A8\u4E0D\u4E86\uFF09\uFF0C**\u7701\u7565\u5219\u662F\u8F6F\u4F9D\u8D56**\uFF08\u53EA\u8BB0\u5173\u7CFB\u3001\u4E0D\u963B\u585E\uFF09\u3002\u53BB\u91CD\u65F6\u7528\u5B83\u628A"\u8BE5\u8FDE\u65E2\u6709"\u7684\u8FB9\u8865\u4E0A\u3002
|
|
36813
|
+
- \`oasis spawn --type <t> --title "<\u77ED\u540D>" [--brief "<\u89C4\u683C>"] [--input <ref>[,\u2026]]\` \u2014\u2014 \u65B0\u5EFA\u4E00\u4E2A\u8282\u70B9\u3002**id \u4E0D\u7528\u4F60\u7ED9,\u7CFB\u7EDF\u636E (type,title) \u81EA\u52A8\u6D3E\u751F**\u3002\`--type <t>\` = \u7C7B\u578B\u540D\uFF08**\u5FC5\u586B**\uFF0C\u5148 \`oasis artifact-types\` \u770B\u53EF\u9009\u7C7B\u578B\u4E0E\u7528\u9014\uFF09\uFF1B\`--title\` = \u77ED\u540D\uFF08**\u5FC5\u586B\u3001\u540C type \u4E0B\u552F\u4E00**,\u649E\u540D\u4F1A\u8BA9\u4F60\u6539\uFF09\uFF1B\`--brief\` = \u7ED9\u5B83\u7684\u4EA7\u51FA\u89C4\u683C\uFF08\u5199\u6E05\u8981\u505A\u6210\u4EC0\u4E48\uFF0C\u4F1A\u6210\u4E3A\u8BE5\u8282\u70B9 owner \u7684\u4EFB\u52A1\u4E66\uFF09\uFF1B\`--input\` = \u5B83\u4F9D\u8D56\u54EA\u4E9B\u4E0A\u6E38\uFF08**\u9017\u53F7\u5206\u9694**\u591A\u4E2A,\u6BCF\u4E2A\u6309\u4E0A\u9762\u5F15\u7528\u5199\u6CD5\uFF09\u3002**\u786E\u65E0\u65E2\u6709\u53EF\u8FDE\u3001\u662F\u771F\u65B0\u9700\u6C42\u624D\u7528**\u3002
|
|
36042
36814
|
- \`oasis edit <id> [--title "<\u65B0\u77ED\u540D>"] [--brief "<\u65B0\u89C4\u683C>"]\` \u2014\u2014 \u6539\u4E00\u4E2A**\u5DF2\u5B58\u5728**\u8282\u70B9\u7684\u6807\u9898 / brief\uFF08**\u53EA\u6539\u8FD9\u4E24\u6837\u5143\u6570\u636E\uFF0C\u7EDD\u4E0D\u78B0\u6B63\u6587**\uFF09\u3002\`<id>\` = \u88AB\u6539\u8282\u70B9\uFF1B\`--title\` / \`--brief\` \u7ED9\u54EA\u4E2A\u4F20\u54EA\u4E2A\u3002\u5408\u5E76\u53BB\u91CD\u65F6\u7528\u5B83\u628A\u4FDD\u7559\u4EF6\u7684 brief \u6269\u5199\u3002
|
|
36043
36815
|
- \`oasis unlink <consumer> --to <up>\` \u2014\u2014 \u5220\u6389 consumer \u6307\u5411 up \u7684\u90A3\u6761\u4F9D\u8D56\u8FB9\u3002\`<consumer>\` = \u4E0B\u6E38\uFF1B\`--to <up>\` = \u4E0D\u518D\u4F9D\u8D56\u7684\u4E0A\u6E38\u3002\u6539\u8FDE\uFF08\u65AD\u9519\u8FB9 / \u6539\u8FDE\u5230\u6B63\u786E\u4E0A\u6E38\uFF09\u65F6\u7528\u3002
|
|
36044
36816
|
- \`oasis cancel <id> [--note "<\u539F\u56E0>"]\` \u2014\u2014 **\u5F7B\u5E95\u5E9F\u5F03**\u4E00\u4E2A\u8282\u70B9\uFF1A\u5B83\u4E0D\u518D\u7B97\u6570\uFF0C\u7CFB\u7EDF**\u81EA\u52A8\u7EA7\u8054**\u65AD\u6389\u5B83\u6240\u6709\u4F9D\u8D56\u8FB9\u3001\u5E76\u91CD\u5524\u4E0B\u6E38\u8BA9\u5B83\u4EEC\u53BB\u6389\u5BF9\u5B83\u7684\u9002\u914D\u3002\`<id>\`\uFF08\u4F4D\u7F6E\u53C2\u6570\uFF09= \u88AB\u5E9F\u8282\u70B9\uFF1B\`--note\` = \u4E3A\u4EC0\u4E48\u5E9F\uFF08\u7559\u75D5\uFF09\u3002\u5EFA\u5728\u5DF2\u5E9F\u5730\u57FA\u4E0A\u3001\u6216\u8FD9\u8282\u70B9\u6839\u672C\u4E0D\u8BE5\u5B58\u5728\u65F6\u7528\u3002\u5E9F\u5F03\u540E\u8BE5\u8282\u70B9\u5728 \`oasis status\` \u91CC\u663E\u793A lifecycle=sealed:cancelled\u3002**\u9500\u6BC1\u6027**\u2014\u2014apply \u65F6\u6253\u5305\u6210\u63D0\u6848\u4EA4\u4EBA\u786E\u8BA4\u3002
|
|
@@ -36237,7 +37009,7 @@ var init_worker = __esm({
|
|
|
36237
37009
|
``,
|
|
36238
37010
|
`## \u4F60\u7684\u52A8\u4F5C\uFF08\u6682\u5B58 \u2192 apply\uFF09`,
|
|
36239
37011
|
`- \u67D0\u65E2\u6709\u6EE1\u8DB3\u8FD9\u9700\u6C42 \u2192 \`oasis link ${gap.artifactId} --to <\u65E2\u6709id>\`\uFF0C\`oasis apply --reason "\u2026"\`\uFF08apply \u540E\u81EA\u52A8\u89E3\u8FD9\u6761 gap\uFF09\u3002`,
|
|
36240
|
-
`- \u786E\u65E0\u5339\u914D\u3001\u65B0\u9700\u6C42 \u2192 \`oasis spawn
|
|
37012
|
+
`- \u786E\u65E0\u5339\u914D\u3001\u65B0\u9700\u6C42 \u2192 \`oasis spawn --type <\u7C7B\u578B> --title "<\u77ED\u540D>" --brief "${gap.description}"\`\uFF08\u4E0D\u7528\u7ED9 id\uFF09+ \`oasis link ${gap.artifactId} --to <\u7C7B\u578B>:<\u77ED\u540D>\`\uFF08\u6309 type:title \u5F15\u521A\u5EFA\u7684\uFF09\u2192 apply\u3002`,
|
|
36241
37013
|
`- \u987A\u624B\u53D1\u73B0\u4E24\u4E2A\u4EA7\u7269\u91CD\u590D \u2192 **\u5148\u628A\u4E0A\u9762\u89E3 gap \u90A3\u7B14\u5355\u72EC apply \u6389**\uFF0C\u518D\u53E6\u8D77\u4E00\u7B14\uFF1A\`oasis edit\` \u6269\u4FDD\u7559\u4EF6 brief + \`oasis link\` \u628A\u6D88\u8D39\u8005\u6539\u8FDE\u5230\u4FDD\u7559\u4EF6 + \`oasis cancel\` \u5E9F\u5F03\u4EF6\uFF08\u81EA\u52A8\u65AD\u5B83\u7684\u8FB9\u3001\u91CD\u5524\u88AB\u6539\u8FDE\u7684\u6D88\u8D39\u8005\uFF09\uFF0C\u4E00\u8D77\u6682\u5B58\u518D apply\u3002`,
|
|
36242
37014
|
`- \u62FF\u4E0D\u51C6 \u2192 \u522B\u6682\u5B58\uFF0C\u4E00\u53E5\u8BDD\u8BF4\u660E\u7591\u8651\u6536\u5DE5\u3002`
|
|
36243
37015
|
].join("\n");
|
|
@@ -36297,7 +37069,7 @@ var init_worker = __esm({
|
|
|
36297
37069
|
``,
|
|
36298
37070
|
`## \u4F60\u7684\u52A8\u4F5C\uFF08\u6682\u5B58 \u2192 apply\uFF09`,
|
|
36299
37071
|
`- \u5728**\u7B49\u4E00\u4E2A active\u3001\u6709 head/\u5728\u4EA7\u7684\u4E0A\u6E38** \u2192 **\u4E0D\u662F\u6B7B\u9501\uFF0C\u522B\u52A8**\uFF0C\u6536\u5DE5\uFF08\u53EF \`oasis status <\u4E0A\u6E38>\` \u786E\u8BA4\uFF09\u3002`,
|
|
36300
|
-
`- **\u5B83\u81EA\u5DF1\u7684 gap \u6CA1\u89E3**\u3001\u4F60\u80FD\u5224 \u2192 \`oasis link ${p2.id} --to <\u65E2\u6709>\`\uFF0C\u6216 \`oasis spawn
|
|
37072
|
+
`- **\u5B83\u81EA\u5DF1\u7684 gap \u6CA1\u89E3**\u3001\u4F60\u80FD\u5224 \u2192 \`oasis link ${p2.id} --to <\u65E2\u6709>\`\uFF0C\u6216 \`oasis spawn --type <\u7C7B\u578B> --title "<\u77ED\u540D>" --brief \u2026\`\uFF08\u4E0D\u7ED9 id\uFF09+ \`oasis link ${p2.id} --to <\u7C7B\u578B>:<\u77ED\u540D>\` \u2192 apply\u3002`,
|
|
36301
37073
|
`- **required \u4E0A\u6E38\u5DF2\u5E9F\u5F03 / \u51BB\u7ED3\u3001\u7B49\u4E0D\u5230\u4E86** \u2192 \`oasis link ${p2.id} --to <\u5B58\u6D3B\u4E0A\u6E38>\` \u6539\u8FDE\uFF0C\u6216 \`oasis cancel ${p2.id}\`\uFF08\u8F6C\u4EBA\u5BA1\uFF09\u3002`,
|
|
36302
37074
|
`- **\u67D0 part \u6C38\u4E45\u4EA4\u4ED8\u4E0D\u4E86\u3001\u5361\u6B7B\u6574\u6761\u96C6\u6210** \u2192 \`oasis cancel-part ${p2.id} --part <\u5361\u4F4F\u7684part>\` \u2192 apply\uFF08\u9500\u6BC1\u6027\uFF0C\u8F6C\u4EBA\u5BA1\uFF09\u3002`,
|
|
36303
37075
|
`- \u62FF\u4E0D\u51C6\u3001\u6216\u6839\u56E0\u4E0D\u5728\u56FE\u4E0A \u2192 \u522B\u52A8\uFF0C\u4E00\u53E5\u8BDD\u8BF4\u6E05\u6839\u56E0 + \u5EFA\u8BAE\u8C01\u5904\u7F6E\uFF0C\u6536\u5DE5\u3002`
|
|
@@ -36306,30 +37078,49 @@ var init_worker = __esm({
|
|
|
36306
37078
|
}
|
|
36307
37079
|
/** 唤起一个 bounded 协调者会话跑给定任务(reconcile / 死锁评估共用)。 */
|
|
36308
37080
|
async runCoordinator(artifactId, task, logMsg) {
|
|
36309
|
-
const
|
|
36310
|
-
const
|
|
37081
|
+
const resolved = this.deps.resolveCoordinator ? await this.deps.resolveCoordinator() : null;
|
|
37082
|
+
const actorId = this.deps.resolveCoordinator ? resolved?.actor.id : COORDINATOR_ACTOR2;
|
|
37083
|
+
const binding = resolved?.binding;
|
|
37084
|
+
if (!actorId) {
|
|
37085
|
+
this.deps.log?.(`[coordinator] ${logMsg} \u8DF3\u8FC7\uFF1A\u7CFB\u7EDF\u7F3A\u5C11\u540D\u79F0\u5305\u542B\u201C\u534F\u8C03\u8005\u201D\u6216\u201Ccoordinator\u201D\u7684 active \u667A\u80FD\u4F53\uFF0C\u6216\u7F3A\u5C11 active runtime \u7ED1\u5B9A`);
|
|
37086
|
+
return;
|
|
37087
|
+
}
|
|
37088
|
+
const prov = this.deps.provision ? await this.deps.provision(actorId) : void 0;
|
|
37089
|
+
const limits = { wallClockMs: 5 * 6e4 };
|
|
37090
|
+
const ctx = {
|
|
37091
|
+
actor: actorId,
|
|
37092
|
+
artifactId,
|
|
37093
|
+
action: "coordinate",
|
|
37094
|
+
...binding !== void 0 ? { binding } : {},
|
|
37095
|
+
limits
|
|
37096
|
+
};
|
|
37097
|
+
const token = this.deps.tokenFor(actorId, ctx);
|
|
37098
|
+
const serverUrl = this.deps.serverUrlFor ? await this.deps.serverUrlFor(ctx) : this.deps.serverUrl;
|
|
37099
|
+
const workspace = this.deps.kernel.model.artifacts.get(artifactId)?.workspace;
|
|
36311
37100
|
const job = {
|
|
36312
|
-
actor:
|
|
37101
|
+
actor: actorId,
|
|
36313
37102
|
actorToken: token,
|
|
36314
37103
|
artifactId,
|
|
36315
37104
|
bundle: { files: { "TASK.md": task } },
|
|
36316
37105
|
systemPrompt: COORDINATOR_SYSTEM_PROMPT,
|
|
36317
|
-
server: { url:
|
|
36318
|
-
|
|
37106
|
+
server: { url: serverUrl },
|
|
37107
|
+
...binding ? { binding } : {},
|
|
37108
|
+
limits,
|
|
36319
37109
|
// OASIS_STAGE=1:本会话里 link/spawn/unlink/seal/cancel-part 进编辑缓冲、apply 才提交(react 攒事务)。
|
|
36320
|
-
env: { ...prov?.env ?? {}, OASIS_STAGE: "1" },
|
|
37110
|
+
env: { ...prov?.env ?? {}, OASIS_STAGE: "1", ...workspace ? { OASIS_WORKSPACE: workspace } : {} },
|
|
36321
37111
|
...prov?.wrapperPaths && prov.wrapperPaths.length > 0 ? { wrapperPaths: prov.wrapperPaths } : {}
|
|
36322
37112
|
};
|
|
36323
37113
|
this.deps.log?.(`[coordinator] ${logMsg}`);
|
|
36324
37114
|
const handle = await this.deps.adapter.spawn(job);
|
|
36325
37115
|
await new Promise((resolve3) => handle.onExit(() => resolve3()));
|
|
36326
|
-
|
|
37116
|
+
void this.finalizeStage(token);
|
|
36327
37117
|
}
|
|
36328
37118
|
/** 会话结束兜底:flush 该会话(按 token 隔离)的编辑缓冲;空则 no-op。 */
|
|
36329
37119
|
async finalizeStage(token) {
|
|
36330
37120
|
try {
|
|
36331
37121
|
const res = await fetch(`${this.deps.serverUrl}/api/stage/apply`, {
|
|
36332
37122
|
method: "POST",
|
|
37123
|
+
signal: AbortSignal.timeout(2e3),
|
|
36333
37124
|
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
36334
37125
|
body: JSON.stringify({ reason: "\uFF08\u534F\u8C03\u8005\u4F1A\u8BDD\u6536\u5DE5\u81EA\u52A8\u63D0\u4EA4\uFF09" })
|
|
36335
37126
|
});
|
|
@@ -36344,6 +37135,115 @@ var init_worker = __esm({
|
|
|
36344
37135
|
}
|
|
36345
37136
|
});
|
|
36346
37137
|
|
|
37138
|
+
// ../server/src/governance/build-workorder-skill.ts
|
|
37139
|
+
async function bootstrapBuildWorkorderSkill(service) {
|
|
37140
|
+
const SYSTEM = "actor:system:bootstrap";
|
|
37141
|
+
const installed = await service.listInstalledSkills();
|
|
37142
|
+
if (!installed.some((s2) => s2.id === BUILD_WORKORDER_SKILL_ID)) {
|
|
37143
|
+
await service.installCustomSkill(
|
|
37144
|
+
{ id: BUILD_WORKORDER_SKILL_ID, name: BUILD_WORKORDER_SKILL_ID, description: DESCRIPTION, source: "custom" },
|
|
37145
|
+
SYSTEM
|
|
37146
|
+
);
|
|
37147
|
+
}
|
|
37148
|
+
await service.putSkillFile(BUILD_WORKORDER_SKILL_ID, "SKILL.md", BUILD_WORKORDER_SKILL_MD);
|
|
37149
|
+
}
|
|
37150
|
+
var BUILD_WORKORDER_SKILL_ID, DESCRIPTION, BUILD_WORKORDER_SKILL_MD;
|
|
37151
|
+
var init_build_workorder_skill = __esm({
|
|
37152
|
+
"../server/src/governance/build-workorder-skill.ts"() {
|
|
37153
|
+
"use strict";
|
|
37154
|
+
BUILD_WORKORDER_SKILL_ID = "build-workorder";
|
|
37155
|
+
DESCRIPTION = "\u628A\u804A\u6E05\u695A\u7684\u9700\u6C42\u843D\u6210\u4E00\u5F20\u5DE5\u5355\uFF08\u534F\u4F5C\u56FE\uFF09\uFF0C\u4EE5\u53CA\u5728\u4EBA\u786E\u8BA4\u524D\u7EE7\u7EED\u6539\u8FD9\u4EFD\u8349\u6848\u3002\u5F53\u7528\u6237\u8981\u5EFA\u5355/\u7ACB\u9879\uFF0C\u6216\u8981\u8C03\u6574\u521A\u751F\u6210\u7684\u5DE5\u5355\u8349\u6848\u65F6\u7528\u672C\u6280\u80FD\uFF1A\u67E5\u91CD oasis workorders \u2192 \u5EFA\u5355 oasis create-workorder \u2192 \u6539\u8349\u6848 oasis spawn/edit/link/cancel\u3002";
|
|
37156
|
+
BUILD_WORKORDER_SKILL_MD = String.raw`---
|
|
37157
|
+
name: build-workorder
|
|
37158
|
+
description: ${DESCRIPTION}
|
|
37159
|
+
---
|
|
37160
|
+
|
|
37161
|
+
# 建单:把对话落成一张工单
|
|
37162
|
+
|
|
37163
|
+
工单 = 一张以产物为中心的协作图:一个根 ´brief´(工单总规格)+ 若干分解节点(prd/research/design/dev/qa…),节点间连依赖边。你把对话敲定的内容**结构化暂存**成一份草案,人在编辑器里看、确认才真创建。
|
|
37164
|
+
|
|
37165
|
+
## 流程
|
|
37166
|
+
1. **查重** ´oasis workorders --search <词>´:有没有类似工单在做/做过,别重复立项。
|
|
37167
|
+
2. **建单** ´oasis create-workorder …´:暂存**草案**(stage=draft,人确认才真创建),命令**直接打印** workspace(工单 id)+ 各节点 id。
|
|
37168
|
+
3. **改草案** ´oasis spawn/edit/link/cancel …´:人确认前,随时调这份草案(加/删/改节点、连边)。
|
|
37169
|
+
4. 人在编辑器确认 → 工单创建。**你不用 apply**,确认是人的事。
|
|
37170
|
+
|
|
37171
|
+
## 一、查重(建单前必做)
|
|
37172
|
+
- ´oasis workorders --search <关键词>´:列/搜工单。命中类似的 → ´oasis workorder <工单id>´ 看详情,已覆盖就别再建、跟用户说明。
|
|
37173
|
+
|
|
37174
|
+
## 二、建单
|
|
37175
|
+
信息不足、关键决策没定**别硬建**,用文字问用户;一个工单一个目标,多件独立的事拆成多张。
|
|
37176
|
+
|
|
37177
|
+
´´´bash
|
|
37178
|
+
oasis create-workorder \
|
|
37179
|
+
--type <工单主产物类型,如 prd;根 brief 自动建> \
|
|
37180
|
+
--title "工单名,名词短语,≤20字" \
|
|
37181
|
+
--goal "一句话目标,≤50字" \
|
|
37182
|
+
--brief "完整规格(markdown):背景、关键决策、约束、范围、不做什么" \
|
|
37183
|
+
--acceptance '["逐条可判定的验收条件","没聊到给 []"]' \
|
|
37184
|
+
--plan '<节点分解 JSON,见下>'
|
|
37185
|
+
´´´
|
|
37186
|
+
**不用填 workspace——系统自动生成工单 id。** 命令**直接打印 workspace(工单 id)和每个节点的 id**,记住它们、改草案要用——**不必再去 query**。它产出的是**草案**(stage=draft):人在编辑器确认才真创建,你别拿它当活工单使。
|
|
37187
|
+
|
|
37188
|
+
### --plan 结构
|
|
37189
|
+
´´´json
|
|
37190
|
+
{
|
|
37191
|
+
"nodes": [
|
|
37192
|
+
{ "type": "节点类型", "title": "≤20字,同 type 内唯一",
|
|
37193
|
+
"dependsOn": ["上游节点写 type:title,如 prd:需求;无上游写 []"],
|
|
37194
|
+
"brief": "这个节点具体要产出什么的规格,别套模板、别只写类型名" }
|
|
37195
|
+
],
|
|
37196
|
+
"notes": ["可选:分解理由/缺口;没有给 []"]
|
|
37197
|
+
}
|
|
37198
|
+
´´´
|
|
37199
|
+
|
|
37200
|
+
## 三、改草案(人确认前随时改)
|
|
37201
|
+
建单后草案在编辑器里等人确认。用户要调整就用下面的命令改这份草案(**改完即时生效、人在编辑器里能看到**,你不用 apply):
|
|
37202
|
+
|
|
37203
|
+
**节点引用 = id 或 type:title,分清:**
|
|
37204
|
+
- **填完整 id**(´oasis workorder´ / create 返回的,如 ´artifact:prd:x7k2-需求´)→ **不用带 workspace**。推荐。
|
|
37205
|
+
- **填 type:title**(简写,如 ´prd:需求´)→ **必须加 ´--workspace <工单id>´**(create 返回的那个)。
|
|
37206
|
+
- ´spawn´ 是新建节点、没有现成 id → **一律带 ´--workspace´**。
|
|
37207
|
+
|
|
37208
|
+
´´´bash
|
|
37209
|
+
# 加节点(带 workspace;--input 上游可用 type:title)
|
|
37210
|
+
oasis spawn --type qa --title 验收用例 --brief "覆盖登录成功/失败" --input dev:实现 --workspace ws:wo-x7k2
|
|
37211
|
+
# 改节点标题/规格(用 id,免 workspace)
|
|
37212
|
+
oasis edit artifact:prd:x7k2-需求 --title "需求与范围"
|
|
37213
|
+
# 连边:消费者依赖上游
|
|
37214
|
+
oasis link artifact:qa:x7k2-验收 --to artifact:dev:x7k2-实现
|
|
37215
|
+
# 删节点(从草案删掉,连它的边一起摘)
|
|
37216
|
+
oasis cancel prd:需求 --workspace ws:wo-x7k2
|
|
37217
|
+
´´´
|
|
37218
|
+
|
|
37219
|
+
# 命令参考
|
|
37220
|
+
|
|
37221
|
+
## 查询
|
|
37222
|
+
- ´oasis workorders [--search <词>]´ —— 列/搜工单(id/名/阶段/进度)。无参列全部;´--search´ 端侧过滤。
|
|
37223
|
+
- ´oasis workorder <工单id>´ —— 工单详情:**stage**(draft 草案 / executing 进行中 / review 评审 / blocked 卡住 / concluded 已完成 / sealed 已封存)+ 目标/验收 + 节点(id/type)+ 依赖边。**刚建的草案这里 ´stage=draft´、也查得到(含各节点 id)**;真查不到才报不存在。
|
|
37224
|
+
- ´oasis artifact-types list | search <词> | show <type>´ —— 看可用节点类型(´show´ 看该类型可填的自定义字段)。
|
|
37225
|
+
- ´oasis projects [--search <词>]´ —— 列/搜项目(建单 ´--project´ 用)。
|
|
37226
|
+
|
|
37227
|
+
## 建单
|
|
37228
|
+
- ´oasis create-workorder --type <t> --title <s> [--goal <s>] [--brief <s>] [--acceptance '<json string[]>'] [--plan '<json>'] [--project <id>]´
|
|
37229
|
+
暂存建单草案。´--type´=主产物类型(必填,根 brief 自动建);´--title´ 必填。**不填 workspace(系统生成)**。返回 ´workspace´ + 各节点 ´id´。
|
|
37230
|
+
|
|
37231
|
+
## 改草案(引用 id 不带 workspace;引用 type:title 或 spawn 带 ´--workspace <工单id>´)
|
|
37232
|
+
- ´oasis spawn --type <t> --title <s> [--brief <s>] [--input <ref>,<ref>…] --workspace <工单id>´ —— 加节点,id 据 (type,title) 自动派生。´--type´/´--title´ 必填,title 同 type 唯一;´--input´=上游依赖(逗号分隔)。
|
|
37233
|
+
- ´oasis edit <ref> [--title <s>] [--brief <s>]´ —— 改节点标题/规格。
|
|
37234
|
+
- ´oasis link <consumer-ref> --to <upstream-ref> [--required true]´ —— 加依赖边。´--required true´=阻塞性(上游没完成下游动不了),省=软依赖。
|
|
37235
|
+
- ´oasis unlink <consumer-ref> --to <upstream-ref>´ —— 删依赖边。
|
|
37236
|
+
- ´oasis cancel <ref> [--note <s>]´ —— 从草案删节点(连它的边一起摘)。
|
|
37237
|
+
|
|
37238
|
+
## 规则
|
|
37239
|
+
- **节点类型**:先 ´oasis artifact-types list/search/show´ 挑最贴合的;都不贴合用 ´oasis artifact-types add <name> --content-type <text|code|…> --owner-role <角色>´ 新建再用。常见:prd、research、design、adr、dev、qa。
|
|
37240
|
+
- **title**:≤20字、同 type 内唯一、别用半角冒号 ´:´(与 type:title 分隔冲突,要冒号用全角 ´:´)。一个工单可多个同 type 节点,靠 title 区分。
|
|
37241
|
+
- **项目**:归某项目就 ´oasis projects --search <词>´ 找 id 再加 ´--project´。
|
|
37242
|
+
- **边界**:你只管图的结构与规格,不替用户拍产品决策——拿不准就问。改**已建好的活工单**是协调者的活,不在本技能范围。
|
|
37243
|
+
`.replace(/´/g, "`");
|
|
37244
|
+
}
|
|
37245
|
+
});
|
|
37246
|
+
|
|
36347
37247
|
// ../server/src/index.ts
|
|
36348
37248
|
var init_src4 = __esm({
|
|
36349
37249
|
"../server/src/index.ts"() {
|
|
@@ -36379,9 +37279,11 @@ var init_src4 = __esm({
|
|
|
36379
37279
|
init_integrate();
|
|
36380
37280
|
init_sync();
|
|
36381
37281
|
init_resolve();
|
|
37282
|
+
init_identity();
|
|
36382
37283
|
init_worker();
|
|
36383
37284
|
init_drafts();
|
|
36384
37285
|
init_workorder_drafts();
|
|
37286
|
+
init_build_workorder_skill();
|
|
36385
37287
|
}
|
|
36386
37288
|
});
|
|
36387
37289
|
|
|
@@ -37973,15 +38875,15 @@ var require_pg_connection_string = __commonJS({
|
|
|
37973
38875
|
if (config2.sslcert || config2.sslkey || config2.sslrootcert || config2.sslmode) {
|
|
37974
38876
|
config2.ssl = {};
|
|
37975
38877
|
}
|
|
37976
|
-
const
|
|
38878
|
+
const fs17 = config2.sslcert || config2.sslkey || config2.sslrootcert ? require("fs") : null;
|
|
37977
38879
|
if (config2.sslcert) {
|
|
37978
|
-
config2.ssl.cert =
|
|
38880
|
+
config2.ssl.cert = fs17.readFileSync(config2.sslcert).toString();
|
|
37979
38881
|
}
|
|
37980
38882
|
if (config2.sslkey) {
|
|
37981
|
-
config2.ssl.key =
|
|
38883
|
+
config2.ssl.key = fs17.readFileSync(config2.sslkey).toString();
|
|
37982
38884
|
}
|
|
37983
38885
|
if (config2.sslrootcert) {
|
|
37984
|
-
config2.ssl.ca =
|
|
38886
|
+
config2.ssl.ca = fs17.readFileSync(config2.sslrootcert).toString();
|
|
37985
38887
|
}
|
|
37986
38888
|
if (options.useLibpqCompat && config2.uselibpqcompat) {
|
|
37987
38889
|
throw new Error("Both useLibpqCompat and uselibpqcompat are set. Please use only one of them.");
|
|
@@ -39918,15 +40820,15 @@ var require_lib = __commonJS({
|
|
|
39918
40820
|
"../../node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js"(exports2, module2) {
|
|
39919
40821
|
"use strict";
|
|
39920
40822
|
var path14 = require("path");
|
|
39921
|
-
var
|
|
40823
|
+
var fs17 = require("fs");
|
|
39922
40824
|
var helper = require_helper();
|
|
39923
40825
|
module2.exports = function(connInfo, cb) {
|
|
39924
40826
|
var file = helper.getFileName();
|
|
39925
|
-
|
|
40827
|
+
fs17.stat(file, function(err, stat) {
|
|
39926
40828
|
if (err || !helper.usePgPass(stat, file)) {
|
|
39927
40829
|
return cb(void 0);
|
|
39928
40830
|
}
|
|
39929
|
-
var st =
|
|
40831
|
+
var st = fs17.createReadStream(file);
|
|
39930
40832
|
helper.getPassword(connInfo, st, cb);
|
|
39931
40833
|
});
|
|
39932
40834
|
};
|
|
@@ -41480,37 +42382,37 @@ __export(trajectory_exports, {
|
|
|
41480
42382
|
});
|
|
41481
42383
|
async function exportTrajectories(dataDir) {
|
|
41482
42384
|
const root = path10.join(dataDir, "trajectories");
|
|
41483
|
-
if (!
|
|
42385
|
+
if (!fs13.existsSync(root)) return [];
|
|
41484
42386
|
const model = emptyReadModel();
|
|
41485
42387
|
const oplogFile = path10.join(dataDir, "oplog.ndjson");
|
|
41486
|
-
if (
|
|
42388
|
+
if (fs13.existsSync(oplogFile)) {
|
|
41487
42389
|
const oplog = await NdjsonOplogStore.open(oplogFile);
|
|
41488
42390
|
for await (const entry of oplog.readAll()) fold(model, entry.op);
|
|
41489
42391
|
}
|
|
41490
42392
|
const samples = [];
|
|
41491
|
-
for (const sessionId of
|
|
42393
|
+
for (const sessionId of fs13.readdirSync(root).sort()) {
|
|
41492
42394
|
const dir = path10.join(root, sessionId);
|
|
41493
42395
|
const sessionFile = path10.join(dir, "session.json");
|
|
41494
|
-
if (!
|
|
41495
|
-
const session = JSON.parse(
|
|
42396
|
+
if (!fs13.existsSync(sessionFile)) continue;
|
|
42397
|
+
const session = JSON.parse(fs13.readFileSync(sessionFile, "utf8"));
|
|
41496
42398
|
const events = [];
|
|
41497
42399
|
const eventsFile = path10.join(dir, "events.ndjson");
|
|
41498
|
-
if (
|
|
41499
|
-
for (const line of
|
|
42400
|
+
if (fs13.existsSync(eventsFile)) {
|
|
42401
|
+
for (const line of fs13.readFileSync(eventsFile, "utf8").split("\n")) {
|
|
41500
42402
|
if (line.trim()) events.push(JSON.parse(line));
|
|
41501
42403
|
}
|
|
41502
42404
|
}
|
|
41503
42405
|
const bundle = {};
|
|
41504
42406
|
const bundleDir = path10.join(dir, "bundle");
|
|
41505
42407
|
const walk = (sub) => {
|
|
41506
|
-
for (const name of
|
|
42408
|
+
for (const name of fs13.readdirSync(path10.join(bundleDir, sub))) {
|
|
41507
42409
|
const rel = sub ? `${sub}/${name}` : name;
|
|
41508
42410
|
const full = path10.join(bundleDir, rel);
|
|
41509
|
-
if (
|
|
41510
|
-
else bundle[rel] =
|
|
42411
|
+
if (fs13.statSync(full).isDirectory()) walk(rel);
|
|
42412
|
+
else bundle[rel] = fs13.readFileSync(full, "utf8");
|
|
41511
42413
|
}
|
|
41512
42414
|
};
|
|
41513
|
-
if (
|
|
42415
|
+
if (fs13.existsSync(bundleDir)) walk("");
|
|
41514
42416
|
const proposed = (session.opRefs ?? []).filter((r) => r.kind === "propose_revision").map((r) => r.target);
|
|
41515
42417
|
const verdicts = proposed.flatMap(
|
|
41516
42418
|
(rev) => (model.reviews.get(rev) ?? []).map((v2) => ({ author: v2.author, verdict: v2.verdict, ...v2.note !== void 0 ? { note: v2.note } : {} }))
|
|
@@ -41522,11 +42424,11 @@ async function exportTrajectories(dataDir) {
|
|
|
41522
42424
|
}
|
|
41523
42425
|
return samples;
|
|
41524
42426
|
}
|
|
41525
|
-
var
|
|
42427
|
+
var fs13, path10, FsTrajectorySink;
|
|
41526
42428
|
var init_trajectory = __esm({
|
|
41527
42429
|
"../cli/src/trajectory.ts"() {
|
|
41528
42430
|
"use strict";
|
|
41529
|
-
|
|
42431
|
+
fs13 = __toESM(require("node:fs"), 1);
|
|
41530
42432
|
path10 = __toESM(require("node:path"), 1);
|
|
41531
42433
|
init_src2();
|
|
41532
42434
|
init_src4();
|
|
@@ -41541,25 +42443,25 @@ var init_trajectory = __esm({
|
|
|
41541
42443
|
}
|
|
41542
42444
|
begin(session, bundleFiles) {
|
|
41543
42445
|
const dir = this.dir(session.sessionId);
|
|
41544
|
-
|
|
41545
|
-
|
|
42446
|
+
fs13.mkdirSync(path10.join(dir, "bundle"), { recursive: true });
|
|
42447
|
+
fs13.writeFileSync(path10.join(dir, "session.json"), JSON.stringify(session, null, 2));
|
|
41546
42448
|
for (const [rel, content] of Object.entries(bundleFiles)) {
|
|
41547
42449
|
const file = path10.join(dir, "bundle", rel);
|
|
41548
|
-
|
|
41549
|
-
|
|
42450
|
+
fs13.mkdirSync(path10.dirname(file), { recursive: true });
|
|
42451
|
+
fs13.writeFileSync(file, content);
|
|
41550
42452
|
}
|
|
41551
42453
|
this.broadcast?.({ type: "begin", sessionId: session.sessionId, data: session });
|
|
41552
42454
|
}
|
|
41553
42455
|
event(sessionId, event) {
|
|
41554
|
-
|
|
42456
|
+
fs13.appendFileSync(path10.join(this.dir(sessionId), "events.ndjson"), JSON.stringify(event) + "\n");
|
|
41555
42457
|
this.broadcast?.({ type: "event", sessionId, data: event });
|
|
41556
42458
|
}
|
|
41557
42459
|
end(sessionId, update) {
|
|
41558
42460
|
const file = path10.join(this.dir(sessionId), "session.json");
|
|
41559
|
-
const session = JSON.parse(
|
|
42461
|
+
const session = JSON.parse(fs13.readFileSync(file, "utf8"));
|
|
41560
42462
|
session.exit = update.exit;
|
|
41561
42463
|
session.opRefs = update.opRefs;
|
|
41562
|
-
|
|
42464
|
+
fs13.writeFileSync(file, JSON.stringify(session, null, 2));
|
|
41563
42465
|
this.broadcast?.({ type: "end", sessionId, data: session });
|
|
41564
42466
|
}
|
|
41565
42467
|
};
|
|
@@ -41567,19 +42469,19 @@ var init_trajectory = __esm({
|
|
|
41567
42469
|
});
|
|
41568
42470
|
|
|
41569
42471
|
// src/index.ts
|
|
41570
|
-
var
|
|
42472
|
+
var fs16 = __toESM(require("node:fs"));
|
|
41571
42473
|
var os8 = __toESM(require("node:os"));
|
|
41572
42474
|
var path13 = __toESM(require("node:path"));
|
|
41573
42475
|
var import_node_child_process10 = require("node:child_process");
|
|
41574
42476
|
|
|
41575
42477
|
// ../cli/src/cli.ts
|
|
41576
|
-
var
|
|
42478
|
+
var fs15 = __toESM(require("node:fs"), 1);
|
|
41577
42479
|
var os7 = __toESM(require("node:os"), 1);
|
|
41578
42480
|
var path12 = __toESM(require("node:path"), 1);
|
|
41579
42481
|
var import_node_crypto21 = require("node:crypto");
|
|
41580
42482
|
|
|
41581
42483
|
// ../cli/src/serve.ts
|
|
41582
|
-
var
|
|
42484
|
+
var fs14 = __toESM(require("node:fs"), 1);
|
|
41583
42485
|
var os6 = __toESM(require("node:os"), 1);
|
|
41584
42486
|
var path11 = __toESM(require("node:path"), 1);
|
|
41585
42487
|
var import_node_crypto20 = require("node:crypto");
|
|
@@ -41788,7 +42690,7 @@ var import_node_path3 = require("node:path");
|
|
|
41788
42690
|
// ../adapters/src/claude-code/index.ts
|
|
41789
42691
|
var import_node_child_process5 = require("node:child_process");
|
|
41790
42692
|
var import_node_crypto14 = require("node:crypto");
|
|
41791
|
-
var
|
|
42693
|
+
var fs9 = __toESM(require("node:fs"), 1);
|
|
41792
42694
|
var os2 = __toESM(require("node:os"), 1);
|
|
41793
42695
|
var path6 = __toESM(require("node:path"), 1);
|
|
41794
42696
|
var readline = __toESM(require("node:readline"), 1);
|
|
@@ -41909,12 +42811,17 @@ var ClaudeCodeAdapter = class {
|
|
|
41909
42811
|
async spawn(job) {
|
|
41910
42812
|
const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
41911
42813
|
const id = `claude-${slug4}-${(0, import_node_crypto14.randomUUID)().slice(0, 8)}`;
|
|
41912
|
-
const dir = job.runtimeSessionId ? path6.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-chat-sessions", job.runtimeSessionId.replace(/[^a-zA-Z0-9_-]+/g, "_")) :
|
|
41913
|
-
|
|
42814
|
+
const dir = job.runtimeSessionId ? path6.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-chat-sessions", job.runtimeSessionId.replace(/[^a-zA-Z0-9_-]+/g, "_")) : fs9.mkdtempSync(path6.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-session-"));
|
|
42815
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
41914
42816
|
for (const [rel, content] of Object.entries(job.bundle.files)) {
|
|
41915
42817
|
const file = path6.join(dir, rel);
|
|
41916
|
-
|
|
41917
|
-
|
|
42818
|
+
fs9.mkdirSync(path6.dirname(file), { recursive: true });
|
|
42819
|
+
fs9.writeFileSync(file, content);
|
|
42820
|
+
}
|
|
42821
|
+
for (const [rel, b64] of Object.entries(job.bundle.binaryFiles ?? {})) {
|
|
42822
|
+
const file = path6.join(dir, rel);
|
|
42823
|
+
fs9.mkdirSync(path6.dirname(file), { recursive: true });
|
|
42824
|
+
fs9.writeFileSync(file, Buffer.from(b64, "base64"));
|
|
41918
42825
|
}
|
|
41919
42826
|
const prompt = job.bundle.files["TASK.md"];
|
|
41920
42827
|
if (!prompt) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
@@ -41953,7 +42860,7 @@ var ClaudeCodeAdapter = class {
|
|
|
41953
42860
|
stdio: ["ignore", "pipe", "pipe"]
|
|
41954
42861
|
});
|
|
41955
42862
|
let transcriptRef = path6.join(dir, "transcript.txt");
|
|
41956
|
-
const out =
|
|
42863
|
+
const out = fs9.createWriteStream(transcriptRef);
|
|
41957
42864
|
const telemetryCbs = [];
|
|
41958
42865
|
let eventSeq = 0;
|
|
41959
42866
|
let lastResult;
|
|
@@ -42016,13 +42923,13 @@ var ClaudeCodeAdapter = class {
|
|
|
42016
42923
|
if (this.opts.cleanupWorkdir && !job.runtimeSessionId) {
|
|
42017
42924
|
try {
|
|
42018
42925
|
const keepDir = path6.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-transcripts");
|
|
42019
|
-
|
|
42926
|
+
fs9.mkdirSync(keepDir, { recursive: true });
|
|
42020
42927
|
const kept = path6.join(keepDir, `${id}.txt`);
|
|
42021
42928
|
const src = transcriptRef;
|
|
42022
42929
|
out.end(() => {
|
|
42023
42930
|
try {
|
|
42024
|
-
|
|
42025
|
-
|
|
42931
|
+
fs9.renameSync(src, kept);
|
|
42932
|
+
fs9.rmSync(dir, { recursive: true, force: true });
|
|
42026
42933
|
} catch {
|
|
42027
42934
|
}
|
|
42028
42935
|
});
|
|
@@ -42057,7 +42964,7 @@ var ClaudeCodeAdapter = class {
|
|
|
42057
42964
|
// ../adapters/src/codex/index.ts
|
|
42058
42965
|
var import_node_child_process6 = require("node:child_process");
|
|
42059
42966
|
var import_node_crypto15 = require("node:crypto");
|
|
42060
|
-
var
|
|
42967
|
+
var fs10 = __toESM(require("node:fs"), 1);
|
|
42061
42968
|
var os3 = __toESM(require("node:os"), 1);
|
|
42062
42969
|
var path7 = __toESM(require("node:path"), 1);
|
|
42063
42970
|
var readline2 = __toESM(require("node:readline"), 1);
|
|
@@ -42102,7 +43009,7 @@ function codexSessionsRoot() {
|
|
|
42102
43009
|
if (home) candidates.push(path7.join(home, ".codex", "sessions"));
|
|
42103
43010
|
for (const c of candidates) {
|
|
42104
43011
|
try {
|
|
42105
|
-
if (
|
|
43012
|
+
if (fs10.statSync(c).isDirectory()) return c;
|
|
42106
43013
|
} catch {
|
|
42107
43014
|
}
|
|
42108
43015
|
}
|
|
@@ -42114,7 +43021,7 @@ function listRecentRollouts(root, sinceMs) {
|
|
|
42114
43021
|
const walk = (d) => {
|
|
42115
43022
|
let ents;
|
|
42116
43023
|
try {
|
|
42117
|
-
ents =
|
|
43024
|
+
ents = fs10.readdirSync(d, { withFileTypes: true });
|
|
42118
43025
|
} catch {
|
|
42119
43026
|
return;
|
|
42120
43027
|
}
|
|
@@ -42124,7 +43031,7 @@ function listRecentRollouts(root, sinceMs) {
|
|
|
42124
43031
|
else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(".jsonl")) {
|
|
42125
43032
|
let m2 = 0;
|
|
42126
43033
|
try {
|
|
42127
|
-
m2 =
|
|
43034
|
+
m2 = fs10.statSync(p2).mtimeMs;
|
|
42128
43035
|
} catch {
|
|
42129
43036
|
}
|
|
42130
43037
|
if (m2 >= margin) found.push({ f: p2, m: m2 });
|
|
@@ -42139,7 +43046,7 @@ function scanCodexUsage(workdir, sinceMs, sessionsRoot = codexSessionsRoot()) {
|
|
|
42139
43046
|
for (const f2 of listRecentRollouts(sessionsRoot, sinceMs)) {
|
|
42140
43047
|
let lines;
|
|
42141
43048
|
try {
|
|
42142
|
-
lines =
|
|
43049
|
+
lines = fs10.readFileSync(f2, "utf8").split("\n");
|
|
42143
43050
|
} catch {
|
|
42144
43051
|
continue;
|
|
42145
43052
|
}
|
|
@@ -42163,9 +43070,9 @@ function createCodexNormalizeState() {
|
|
|
42163
43070
|
function record2(value) {
|
|
42164
43071
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
42165
43072
|
}
|
|
42166
|
-
function
|
|
43073
|
+
function textOf3(value) {
|
|
42167
43074
|
if (typeof value === "string") return value;
|
|
42168
|
-
if (Array.isArray(value)) return value.map(
|
|
43075
|
+
if (Array.isArray(value)) return value.map(textOf3).filter(Boolean).join("\n");
|
|
42169
43076
|
const obj = record2(value);
|
|
42170
43077
|
if (!obj) return void 0;
|
|
42171
43078
|
if (typeof obj.text === "string") return obj.text;
|
|
@@ -42174,7 +43081,7 @@ function textOf2(value) {
|
|
|
42174
43081
|
return void 0;
|
|
42175
43082
|
}
|
|
42176
43083
|
function stringifyContent(value) {
|
|
42177
|
-
const text =
|
|
43084
|
+
const text = textOf3(value);
|
|
42178
43085
|
if (text !== void 0) return text;
|
|
42179
43086
|
if (value === void 0 || value === null) return "";
|
|
42180
43087
|
try {
|
|
@@ -42205,7 +43112,7 @@ ${text}` : text;
|
|
|
42205
43112
|
return [{ kind: "message", payload: { text: delta }, outputText: delta }];
|
|
42206
43113
|
}
|
|
42207
43114
|
if (type === "reasoning") {
|
|
42208
|
-
const text =
|
|
43115
|
+
const text = textOf3(item.text ?? item.summary ?? item.content ?? item);
|
|
42209
43116
|
return text ? [{ kind: "thought", payload: { text } }] : [];
|
|
42210
43117
|
}
|
|
42211
43118
|
if (type === "command_execution" && typeof item.id === "string") {
|
|
@@ -42331,11 +43238,11 @@ var CodexAdapter = class {
|
|
|
42331
43238
|
const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
42332
43239
|
const id = `codex-${slug4}-${(0, import_node_crypto15.randomUUID)().slice(0, 8)}`;
|
|
42333
43240
|
const startedAtMs = Date.now();
|
|
42334
|
-
const dir =
|
|
43241
|
+
const dir = fs10.mkdtempSync(path7.join(this.opts.workRoot ?? os3.tmpdir(), "oasis-session-"));
|
|
42335
43242
|
for (const [rel, content] of Object.entries(job.bundle.files)) {
|
|
42336
43243
|
const file = path7.join(dir, rel);
|
|
42337
|
-
|
|
42338
|
-
|
|
43244
|
+
fs10.mkdirSync(path7.dirname(file), { recursive: true });
|
|
43245
|
+
fs10.writeFileSync(file, content);
|
|
42339
43246
|
}
|
|
42340
43247
|
const task = job.bundle.files["TASK.md"];
|
|
42341
43248
|
if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
@@ -42367,7 +43274,7 @@ var CodexAdapter = class {
|
|
|
42367
43274
|
stdio: ["ignore", "pipe", "pipe"]
|
|
42368
43275
|
});
|
|
42369
43276
|
let transcriptRef = path7.join(dir, "transcript.txt");
|
|
42370
|
-
const out =
|
|
43277
|
+
const out = fs10.createWriteStream(transcriptRef);
|
|
42371
43278
|
const outputCbs = [];
|
|
42372
43279
|
const telemetryCbs = [];
|
|
42373
43280
|
const normalizeState = createCodexNormalizeState();
|
|
@@ -42398,13 +43305,13 @@ var CodexAdapter = class {
|
|
|
42398
43305
|
if (this.opts.cleanupWorkdir) {
|
|
42399
43306
|
try {
|
|
42400
43307
|
const keepDir = path7.join(this.opts.workRoot ?? os3.tmpdir(), "oasis-transcripts");
|
|
42401
|
-
|
|
43308
|
+
fs10.mkdirSync(keepDir, { recursive: true });
|
|
42402
43309
|
const kept = path7.join(keepDir, `${id}.txt`);
|
|
42403
43310
|
const src = transcriptRef;
|
|
42404
43311
|
out.end(() => {
|
|
42405
43312
|
try {
|
|
42406
|
-
|
|
42407
|
-
|
|
43313
|
+
fs10.renameSync(src, kept);
|
|
43314
|
+
fs10.rmSync(dir, { recursive: true, force: true });
|
|
42408
43315
|
} catch {
|
|
42409
43316
|
}
|
|
42410
43317
|
});
|
|
@@ -42457,7 +43364,7 @@ var CodexAdapter = class {
|
|
|
42457
43364
|
// ../adapters/src/_core/acp.ts
|
|
42458
43365
|
var import_node_child_process7 = require("node:child_process");
|
|
42459
43366
|
var import_node_crypto16 = require("node:crypto");
|
|
42460
|
-
var
|
|
43367
|
+
var fs11 = __toESM(require("node:fs"), 1);
|
|
42461
43368
|
var os4 = __toESM(require("node:os"), 1);
|
|
42462
43369
|
var path8 = __toESM(require("node:path"), 1);
|
|
42463
43370
|
var readline3 = __toESM(require("node:readline"), 1);
|
|
@@ -42472,6 +43379,7 @@ var ACPClient = class {
|
|
|
42472
43379
|
nextId = 0;
|
|
42473
43380
|
pending = /* @__PURE__ */ new Map();
|
|
42474
43381
|
pendingTools = /* @__PURE__ */ new Map();
|
|
43382
|
+
textBuffers = /* @__PURE__ */ new Map();
|
|
42475
43383
|
seq = 0;
|
|
42476
43384
|
request(method, params) {
|
|
42477
43385
|
const id = this.nextId++;
|
|
@@ -42481,6 +43389,7 @@ var ACPClient = class {
|
|
|
42481
43389
|
});
|
|
42482
43390
|
}
|
|
42483
43391
|
closeAllPending(err) {
|
|
43392
|
+
this.flushTextBuffers();
|
|
42484
43393
|
for (const p2 of this.pending.values()) p2.reject(err);
|
|
42485
43394
|
this.pending.clear();
|
|
42486
43395
|
}
|
|
@@ -42539,17 +43448,22 @@ var ACPClient = class {
|
|
|
42539
43448
|
this.handleAgentThought(updateData);
|
|
42540
43449
|
break;
|
|
42541
43450
|
case "tool_call":
|
|
43451
|
+
this.flushTextBuffers();
|
|
42542
43452
|
this.handleToolCallStart(updateData);
|
|
43453
|
+
this.handleToolCallUpdate(updateData);
|
|
42543
43454
|
break;
|
|
42544
43455
|
case "tool_call_update":
|
|
43456
|
+
this.flushTextBuffers();
|
|
42545
43457
|
this.handleToolCallUpdate(updateData);
|
|
42546
43458
|
break;
|
|
42547
43459
|
case "turn_end":
|
|
43460
|
+
this.flushTextBuffers();
|
|
42548
43461
|
this.extractStopReason(updateData);
|
|
42549
43462
|
break;
|
|
42550
43463
|
}
|
|
42551
43464
|
}
|
|
42552
43465
|
extractStopReason(data) {
|
|
43466
|
+
this.flushTextBuffers();
|
|
42553
43467
|
const r = data;
|
|
42554
43468
|
if (r?.stopReason) this.onStop(r.stopReason);
|
|
42555
43469
|
}
|
|
@@ -42557,45 +43471,235 @@ var ACPClient = class {
|
|
|
42557
43471
|
if (outputText) this.onOutput(outputText);
|
|
42558
43472
|
this.onTelemetry({ kind, payload, seq: ++this.seq, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
42559
43473
|
}
|
|
43474
|
+
appendText(kind, text) {
|
|
43475
|
+
if (kind === "message") this.onOutput(text);
|
|
43476
|
+
const other = kind === "message" ? "thought" : "message";
|
|
43477
|
+
this.flushTextBuffer(other);
|
|
43478
|
+
const existing = this.textBuffers.get(kind);
|
|
43479
|
+
if (existing) existing.text += text;
|
|
43480
|
+
else this.textBuffers.set(kind, { text, startedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
43481
|
+
}
|
|
43482
|
+
flushTextBuffer(kind) {
|
|
43483
|
+
const buffered = this.textBuffers.get(kind);
|
|
43484
|
+
if (!buffered) return;
|
|
43485
|
+
this.textBuffers.delete(kind);
|
|
43486
|
+
const text = buffered.text;
|
|
43487
|
+
if (!text) return;
|
|
43488
|
+
this.onTelemetry({ kind, payload: { text }, seq: ++this.seq, ts: buffered.startedAt });
|
|
43489
|
+
}
|
|
43490
|
+
flushTextBuffers() {
|
|
43491
|
+
this.flushTextBuffer("thought");
|
|
43492
|
+
this.flushTextBuffer("message");
|
|
43493
|
+
}
|
|
42560
43494
|
handleAgentMessage(data) {
|
|
42561
|
-
const text = data?.content
|
|
42562
|
-
if (text) this.
|
|
43495
|
+
const text = textFromUnknown(record3(data)?.["content"] ?? data);
|
|
43496
|
+
if (text) this.appendText("message", text);
|
|
42563
43497
|
}
|
|
42564
43498
|
handleAgentThought(data) {
|
|
42565
|
-
const text = data?.content
|
|
42566
|
-
if (text) this.
|
|
43499
|
+
const text = textFromUnknown(record3(data)?.["content"] ?? data);
|
|
43500
|
+
if (text) this.appendText("thought", text);
|
|
42567
43501
|
}
|
|
42568
43502
|
handleToolCallStart(data) {
|
|
42569
|
-
const d = data;
|
|
42570
|
-
|
|
42571
|
-
|
|
42572
|
-
const
|
|
42573
|
-
|
|
42574
|
-
|
|
42575
|
-
this.
|
|
43503
|
+
const d = toolPayload(data);
|
|
43504
|
+
const toolCallId = toolCallIdOf(d);
|
|
43505
|
+
if (!toolCallId) return;
|
|
43506
|
+
const toolName = toolNameOf(d);
|
|
43507
|
+
const input = toolInputOf(d);
|
|
43508
|
+
if (input !== void 0) {
|
|
43509
|
+
this.pendingTools.set(toolCallId, { toolName, input, argsText: "", outputParts: [], emitted: true });
|
|
43510
|
+
this.emit("tool_use", { id: toolCallId, name: toolName, input });
|
|
42576
43511
|
} else {
|
|
42577
|
-
this.pendingTools.set(
|
|
43512
|
+
this.pendingTools.set(toolCallId, {
|
|
43513
|
+
toolName,
|
|
43514
|
+
argsText: argsTextOf(d) ?? "",
|
|
43515
|
+
outputParts: [],
|
|
43516
|
+
emitted: false
|
|
43517
|
+
});
|
|
42578
43518
|
}
|
|
42579
43519
|
}
|
|
42580
43520
|
handleToolCallUpdate(data) {
|
|
42581
|
-
const d = data;
|
|
42582
|
-
|
|
42583
|
-
|
|
42584
|
-
|
|
42585
|
-
|
|
42586
|
-
|
|
43521
|
+
const d = toolPayload(data);
|
|
43522
|
+
const toolCallId = toolCallIdOf(d);
|
|
43523
|
+
if (!toolCallId) return;
|
|
43524
|
+
let pending = this.pendingTools.get(toolCallId);
|
|
43525
|
+
if (!pending) {
|
|
43526
|
+
pending = {
|
|
43527
|
+
toolName: toolNameOf(d),
|
|
43528
|
+
input: toolInputOf(d),
|
|
43529
|
+
argsText: argsTextOf(d) ?? "",
|
|
43530
|
+
outputParts: [],
|
|
43531
|
+
emitted: false
|
|
43532
|
+
};
|
|
43533
|
+
this.pendingTools.set(toolCallId, pending);
|
|
43534
|
+
}
|
|
43535
|
+
const input = toolInputOf(d);
|
|
43536
|
+
if (input !== void 0) pending.input = input;
|
|
43537
|
+
const argsText = argsTextOf(d);
|
|
43538
|
+
if (argsText) pending.argsText += argsText;
|
|
43539
|
+
const output = toolOutputOf(d);
|
|
43540
|
+
if (output !== void 0) pending.outputParts.push(output);
|
|
43541
|
+
if (!pending.emitted && pending.input !== void 0) {
|
|
43542
|
+
pending.emitted = true;
|
|
43543
|
+
this.emit("tool_use", { id: toolCallId, name: pending.toolName, input: pending.input });
|
|
43544
|
+
}
|
|
43545
|
+
const status = statusOf(d);
|
|
43546
|
+
if (!isFinalToolStatus(status, d)) return;
|
|
43547
|
+
this.pendingTools.delete(toolCallId);
|
|
42587
43548
|
if (!pending?.emitted) {
|
|
42588
|
-
const
|
|
42589
|
-
|
|
42590
|
-
this.emit("tool_use", { id: d.toolCallId, name: toolName, input });
|
|
43549
|
+
const input2 = pending.input ?? parseArgsJSON(pending.argsText);
|
|
43550
|
+
this.emit("tool_use", { id: toolCallId, name: pending.toolName, input: input2 ?? {} });
|
|
42591
43551
|
}
|
|
42592
43552
|
this.emit("tool_result", {
|
|
42593
|
-
tool_use_id:
|
|
42594
|
-
content:
|
|
42595
|
-
is_error: status
|
|
43553
|
+
tool_use_id: toolCallId,
|
|
43554
|
+
content: output ?? joinOutputParts(pending.outputParts),
|
|
43555
|
+
is_error: isErrorToolStatus(status, d)
|
|
42596
43556
|
});
|
|
42597
43557
|
}
|
|
42598
43558
|
};
|
|
43559
|
+
function record3(value) {
|
|
43560
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
43561
|
+
}
|
|
43562
|
+
function firstString(...values) {
|
|
43563
|
+
for (const value of values) {
|
|
43564
|
+
if (typeof value === "string" && value.trim()) return value;
|
|
43565
|
+
}
|
|
43566
|
+
return void 0;
|
|
43567
|
+
}
|
|
43568
|
+
function textFromUnknown(value) {
|
|
43569
|
+
if (typeof value === "string") return value;
|
|
43570
|
+
if (Array.isArray(value)) {
|
|
43571
|
+
const parts = value.map(textFromUnknown).filter((x2) => Boolean(x2));
|
|
43572
|
+
return parts.length ? parts.join("") : void 0;
|
|
43573
|
+
}
|
|
43574
|
+
const obj = record3(value);
|
|
43575
|
+
if (!obj) return void 0;
|
|
43576
|
+
const direct = firstString(obj["text"], obj["delta"], obj["message"], record3(obj["error"])?.["message"]);
|
|
43577
|
+
if (direct) return direct;
|
|
43578
|
+
return textFromUnknown(obj["content"]) ?? textFromUnknown(record3(obj["result"])?.["content"]) ?? textFromUnknown(obj["result"]);
|
|
43579
|
+
}
|
|
43580
|
+
function jsonLike(value) {
|
|
43581
|
+
if (typeof value !== "string") return value;
|
|
43582
|
+
const t = value.trim();
|
|
43583
|
+
if (!t) return void 0;
|
|
43584
|
+
if (t.startsWith("{") && t.endsWith("}") || t.startsWith("[") && t.endsWith("]")) {
|
|
43585
|
+
try {
|
|
43586
|
+
return JSON.parse(t);
|
|
43587
|
+
} catch {
|
|
43588
|
+
}
|
|
43589
|
+
}
|
|
43590
|
+
return { text: value };
|
|
43591
|
+
}
|
|
43592
|
+
function toolPayload(data) {
|
|
43593
|
+
const d = record3(data) ?? {};
|
|
43594
|
+
const nested = record3(d["toolCall"]) ?? record3(d["tool_call"]) ?? record3(d["call"]) ?? record3(d["tool"]) ?? record3(d["data"]) ?? {};
|
|
43595
|
+
return { ...nested, ...d };
|
|
43596
|
+
}
|
|
43597
|
+
function toolCallIdOf(d) {
|
|
43598
|
+
return firstString(d["toolCallId"], d["tool_call_id"], d["callId"], d["call_id"], d["id"]);
|
|
43599
|
+
}
|
|
43600
|
+
function toolNameOf(d) {
|
|
43601
|
+
return firstString(d["name"], d["toolName"], d["tool_name"], record3(d["tool"])?.["name"]) ?? toolNameFromTitle(firstString(d["title"]) ?? "", firstString(d["kind"]) ?? "");
|
|
43602
|
+
}
|
|
43603
|
+
function inputFromTitle(title, toolName) {
|
|
43604
|
+
const colon = title.indexOf(":");
|
|
43605
|
+
if (colon < 0) return void 0;
|
|
43606
|
+
const tail = title.slice(colon + 1).trim();
|
|
43607
|
+
if (!tail) return void 0;
|
|
43608
|
+
if (toolName === "terminal" || toolName === "execute_code" || toolName === "python") return { command: tail };
|
|
43609
|
+
if (toolName.includes("read") || toolName.includes("write")) return { path: tail };
|
|
43610
|
+
if (toolName.includes("search")) return { query: tail };
|
|
43611
|
+
return { text: tail };
|
|
43612
|
+
}
|
|
43613
|
+
function toolInputOf(d) {
|
|
43614
|
+
const direct = d["rawInput"] ?? d["raw_input"] ?? d["input"] ?? d["arguments"] ?? d["args"] ?? d["parameters"] ?? d["params"];
|
|
43615
|
+
const parsed = jsonLike(direct);
|
|
43616
|
+
if (parsed !== void 0) return parsed;
|
|
43617
|
+
const command = firstString(d["command"], d["cmd"]);
|
|
43618
|
+
if (command) return { command };
|
|
43619
|
+
const common = compactRecord({
|
|
43620
|
+
query: d["query"] ?? d["q"] ?? d["searchQuery"] ?? d["search_query"],
|
|
43621
|
+
queries: d["queries"],
|
|
43622
|
+
url: d["url"] ?? d["uri"],
|
|
43623
|
+
urls: d["urls"],
|
|
43624
|
+
path: d["path"] ?? d["filePath"] ?? d["file_path"],
|
|
43625
|
+
paths: d["paths"],
|
|
43626
|
+
pattern: d["pattern"] ?? d["regex"],
|
|
43627
|
+
prompt: d["prompt"],
|
|
43628
|
+
description: d["description"]
|
|
43629
|
+
});
|
|
43630
|
+
if (common) return common;
|
|
43631
|
+
const fromTitle = inputFromTitle(firstString(d["title"]) ?? "", toolNameOf(d));
|
|
43632
|
+
const locations = locationsOf(d);
|
|
43633
|
+
if (fromTitle && locations) return { ...fromTitle, locations };
|
|
43634
|
+
if (fromTitle) return fromTitle;
|
|
43635
|
+
if (locations) return { locations };
|
|
43636
|
+
return void 0;
|
|
43637
|
+
}
|
|
43638
|
+
function argsTextOf(d) {
|
|
43639
|
+
const value = d["argsText"] ?? d["args_text"] ?? d["argumentsText"] ?? d["arguments_text"];
|
|
43640
|
+
return typeof value === "string" ? value : void 0;
|
|
43641
|
+
}
|
|
43642
|
+
function toolOutputOf(d) {
|
|
43643
|
+
const error2 = record3(d["error"]);
|
|
43644
|
+
if (error2) return error2["message"] ?? error2;
|
|
43645
|
+
const raw = d["rawOutput"] ?? d["raw_output"] ?? d["output"] ?? d["result"] ?? d["results"] ?? d["response"] ?? d["data"] ?? d["content"];
|
|
43646
|
+
const stream = compactRecord({
|
|
43647
|
+
stdout: d["stdout"],
|
|
43648
|
+
stderr: d["stderr"],
|
|
43649
|
+
exit_code: d["exit_code"] ?? d["exitCode"]
|
|
43650
|
+
});
|
|
43651
|
+
if (raw === void 0) return stream;
|
|
43652
|
+
const text = textFromUnknown(raw);
|
|
43653
|
+
const primary = text ?? raw;
|
|
43654
|
+
if (stream && typeof primary === "string" && primary.trim()) return { output: primary, ...stream };
|
|
43655
|
+
return stream ?? primary;
|
|
43656
|
+
}
|
|
43657
|
+
function compactRecord(input) {
|
|
43658
|
+
const out = {};
|
|
43659
|
+
for (const [key, value] of Object.entries(input)) {
|
|
43660
|
+
if (value === void 0 || value === null) continue;
|
|
43661
|
+
if (typeof value === "string" && !value.trim()) continue;
|
|
43662
|
+
if (Array.isArray(value) && value.length === 0) continue;
|
|
43663
|
+
out[key] = parseJsonString(value);
|
|
43664
|
+
}
|
|
43665
|
+
return Object.keys(out).length ? out : void 0;
|
|
43666
|
+
}
|
|
43667
|
+
function parseJsonString(value) {
|
|
43668
|
+
if (typeof value !== "string") return value;
|
|
43669
|
+
const t = value.trim();
|
|
43670
|
+
if (t.startsWith("{") && t.endsWith("}") || t.startsWith("[") && t.endsWith("]")) {
|
|
43671
|
+
try {
|
|
43672
|
+
return JSON.parse(t);
|
|
43673
|
+
} catch {
|
|
43674
|
+
}
|
|
43675
|
+
}
|
|
43676
|
+
return value;
|
|
43677
|
+
}
|
|
43678
|
+
function locationsOf(d) {
|
|
43679
|
+
const locations = d["locations"];
|
|
43680
|
+
if (!Array.isArray(locations)) return void 0;
|
|
43681
|
+
const out = locations.filter((item) => Boolean(record3(item)));
|
|
43682
|
+
return out.length ? out : void 0;
|
|
43683
|
+
}
|
|
43684
|
+
function statusOf(d) {
|
|
43685
|
+
const status = firstString(d["status"], d["state"]);
|
|
43686
|
+
if (status) return status.toLowerCase();
|
|
43687
|
+
if (d["success"] === true) return "succeeded";
|
|
43688
|
+
if (d["success"] === false || d["isError"] === true || d["is_error"] === true || d["error"]) return "failed";
|
|
43689
|
+
return void 0;
|
|
43690
|
+
}
|
|
43691
|
+
function isFinalToolStatus(status, d) {
|
|
43692
|
+
if (d["done"] === true || d["final"] === true) return true;
|
|
43693
|
+
return status === "completed" || status === "complete" || status === "succeeded" || status === "success" || status === "failed" || status === "error" || status === "cancelled" || status === "canceled" || status === "done" || status === "finished";
|
|
43694
|
+
}
|
|
43695
|
+
function isErrorToolStatus(status, d) {
|
|
43696
|
+
return status === "failed" || status === "error" || status === "cancelled" || status === "canceled" || d["success"] === false || d["isError"] === true || d["is_error"] === true || Boolean(d["error"]);
|
|
43697
|
+
}
|
|
43698
|
+
function joinOutputParts(parts) {
|
|
43699
|
+
if (parts.length === 0) return "";
|
|
43700
|
+
if (parts.every((p2) => typeof p2 === "string")) return parts.join("");
|
|
43701
|
+
return parts;
|
|
43702
|
+
}
|
|
42599
43703
|
function normalizeACPUpdate(data) {
|
|
42600
43704
|
const d = data;
|
|
42601
43705
|
if (!d) return ["", data];
|
|
@@ -42613,7 +43717,17 @@ function normalizeUpdateType(t) {
|
|
|
42613
43717
|
agentmessagechunk: "agent_message_chunk",
|
|
42614
43718
|
agentthoughtchunk: "agent_thought_chunk",
|
|
42615
43719
|
toolcall: "tool_call",
|
|
43720
|
+
toolcallstart: "tool_call",
|
|
43721
|
+
toolcallstarted: "tool_call",
|
|
43722
|
+
toolcallbegin: "tool_call",
|
|
43723
|
+
toolcallcreated: "tool_call",
|
|
42616
43724
|
toolcallupdate: "tool_call_update",
|
|
43725
|
+
toolcallupdated: "tool_call_update",
|
|
43726
|
+
toolcallprogress: "tool_call_update",
|
|
43727
|
+
toolcallstatus: "tool_call_update",
|
|
43728
|
+
toolcalldelta: "tool_call_update",
|
|
43729
|
+
toolcallfinished: "tool_call_update",
|
|
43730
|
+
toolcallcompleted: "tool_call_update",
|
|
42617
43731
|
usageupdate: "usage_update",
|
|
42618
43732
|
turnend: "turn_end",
|
|
42619
43733
|
endturn: "turn_end"
|
|
@@ -42663,11 +43777,11 @@ async function runACPSession(job, cfg) {
|
|
|
42663
43777
|
const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
42664
43778
|
const binTag = path8.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
|
|
42665
43779
|
const id = `${binTag}-${slug4}-${(0, import_node_crypto16.randomUUID)().slice(0, 8)}`;
|
|
42666
|
-
const dir =
|
|
43780
|
+
const dir = fs11.mkdtempSync(path8.join(cfg.workRoot ?? os4.tmpdir(), "oasis-session-"));
|
|
42667
43781
|
for (const [rel, content] of Object.entries(job.bundle.files)) {
|
|
42668
43782
|
const file = path8.join(dir, rel);
|
|
42669
|
-
|
|
42670
|
-
|
|
43783
|
+
fs11.mkdirSync(path8.dirname(file), { recursive: true });
|
|
43784
|
+
fs11.writeFileSync(file, content);
|
|
42671
43785
|
}
|
|
42672
43786
|
const task = job.bundle.files["TASK.md"];
|
|
42673
43787
|
if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
@@ -42685,7 +43799,7 @@ async function runACPSession(job, cfg) {
|
|
|
42685
43799
|
stdio: ["pipe", "pipe", "pipe"]
|
|
42686
43800
|
});
|
|
42687
43801
|
let transcriptRef = path8.join(dir, "transcript.txt");
|
|
42688
|
-
const transcriptOut =
|
|
43802
|
+
const transcriptOut = fs11.createWriteStream(transcriptRef);
|
|
42689
43803
|
const outputCbs = [];
|
|
42690
43804
|
const telemetryCbs = [];
|
|
42691
43805
|
const exitCbs = [];
|
|
@@ -42696,13 +43810,13 @@ async function runACPSession(job, cfg) {
|
|
|
42696
43810
|
if (cfg.cleanupWorkdir) {
|
|
42697
43811
|
try {
|
|
42698
43812
|
const keepDir = path8.join(cfg.workRoot ?? os4.tmpdir(), "oasis-transcripts");
|
|
42699
|
-
|
|
43813
|
+
fs11.mkdirSync(keepDir, { recursive: true });
|
|
42700
43814
|
const kept = path8.join(keepDir, `${id}.txt`);
|
|
42701
43815
|
const src = transcriptRef;
|
|
42702
43816
|
transcriptOut.end(() => {
|
|
42703
43817
|
try {
|
|
42704
|
-
|
|
42705
|
-
|
|
43818
|
+
fs11.renameSync(src, kept);
|
|
43819
|
+
fs11.rmSync(dir, { recursive: true, force: true });
|
|
42706
43820
|
} catch {
|
|
42707
43821
|
}
|
|
42708
43822
|
});
|
|
@@ -42879,18 +43993,18 @@ var KiroAdapter = class {
|
|
|
42879
43993
|
// ../adapters/src/_core/subprocess.ts
|
|
42880
43994
|
var import_node_child_process8 = require("node:child_process");
|
|
42881
43995
|
var import_node_crypto17 = require("node:crypto");
|
|
42882
|
-
var
|
|
43996
|
+
var fs12 = __toESM(require("node:fs"), 1);
|
|
42883
43997
|
var os5 = __toESM(require("node:os"), 1);
|
|
42884
43998
|
var path9 = __toESM(require("node:path"), 1);
|
|
42885
43999
|
var readline4 = __toESM(require("node:readline"), 1);
|
|
42886
44000
|
function materialize(job, workRoot) {
|
|
42887
44001
|
const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
42888
44002
|
const id = `${slug4}-${(0, import_node_crypto17.randomUUID)().slice(0, 8)}`;
|
|
42889
|
-
const dir =
|
|
44003
|
+
const dir = fs12.mkdtempSync(path9.join(workRoot ?? os5.tmpdir(), "oasis-session-"));
|
|
42890
44004
|
for (const [rel, content] of Object.entries(job.bundle.files)) {
|
|
42891
44005
|
const file = path9.join(dir, rel);
|
|
42892
|
-
|
|
42893
|
-
|
|
44006
|
+
fs12.mkdirSync(path9.dirname(file), { recursive: true });
|
|
44007
|
+
fs12.writeFileSync(file, content);
|
|
42894
44008
|
}
|
|
42895
44009
|
const task = job.bundle.files["TASK.md"];
|
|
42896
44010
|
if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
@@ -42913,13 +44027,13 @@ function makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs) {
|
|
|
42913
44027
|
if (cfg.cleanupWorkdir) {
|
|
42914
44028
|
try {
|
|
42915
44029
|
const keepDir = path9.join(cfg.workRoot ?? os5.tmpdir(), "oasis-transcripts");
|
|
42916
|
-
|
|
44030
|
+
fs12.mkdirSync(keepDir, { recursive: true });
|
|
42917
44031
|
const kept = path9.join(keepDir, `${id}.txt`);
|
|
42918
44032
|
const src = transcriptRefPtr.value;
|
|
42919
44033
|
transcriptOut.end(() => {
|
|
42920
44034
|
try {
|
|
42921
|
-
|
|
42922
|
-
|
|
44035
|
+
fs12.renameSync(src, kept);
|
|
44036
|
+
fs12.rmSync(dir, { recursive: true, force: true });
|
|
42923
44037
|
} catch {
|
|
42924
44038
|
}
|
|
42925
44039
|
});
|
|
@@ -42942,7 +44056,7 @@ function runStreamJson(job, cfg) {
|
|
|
42942
44056
|
stdio: ["ignore", "pipe", "pipe"]
|
|
42943
44057
|
});
|
|
42944
44058
|
const transcriptRefPtr = { value: path9.join(dir, "transcript.txt") };
|
|
42945
|
-
const transcriptOut =
|
|
44059
|
+
const transcriptOut = fs12.createWriteStream(transcriptRefPtr.value);
|
|
42946
44060
|
const outputCbs = [];
|
|
42947
44061
|
const telemetryCbs = [];
|
|
42948
44062
|
const exitCbs = [];
|
|
@@ -43000,7 +44114,7 @@ function runOneShotText(job, cfg) {
|
|
|
43000
44114
|
stdio: ["ignore", "pipe", "pipe"]
|
|
43001
44115
|
});
|
|
43002
44116
|
const transcriptRefPtr = { value: path9.join(dir, "transcript.txt") };
|
|
43003
|
-
const transcriptOut =
|
|
44117
|
+
const transcriptOut = fs12.createWriteStream(transcriptRefPtr.value);
|
|
43004
44118
|
const outputCbs = [];
|
|
43005
44119
|
const exitCbs = [];
|
|
43006
44120
|
const { finish, exited } = makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs);
|
|
@@ -45743,9 +46857,18 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
45743
46857
|
role text NOT NULL,
|
|
45744
46858
|
content text NOT NULL,
|
|
45745
46859
|
seq integer NOT NULL,
|
|
45746
|
-
created_at timestamptz NOT NULL
|
|
46860
|
+
created_at timestamptz NOT NULL,
|
|
46861
|
+
run_id text
|
|
45747
46862
|
)`);
|
|
45748
46863
|
await pool.query(`CREATE INDEX IF NOT EXISTS chat_messages_session_idx ON "${s2}".chat_messages (session_id, seq)`);
|
|
46864
|
+
await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS run_id text`);
|
|
46865
|
+
await pool.query(`
|
|
46866
|
+
CREATE TABLE IF NOT EXISTS "${s2}".chat_session_work_orders (
|
|
46867
|
+
session_id text NOT NULL,
|
|
46868
|
+
work_order_id text NOT NULL,
|
|
46869
|
+
linked_at timestamptz NOT NULL DEFAULT now(),
|
|
46870
|
+
PRIMARY KEY (session_id, work_order_id)
|
|
46871
|
+
)`);
|
|
45749
46872
|
return new _PostgresChatSessionStore(pool, schema);
|
|
45750
46873
|
}
|
|
45751
46874
|
async createSession(s2) {
|
|
@@ -45795,9 +46918,9 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
45795
46918
|
}
|
|
45796
46919
|
async appendMessage(m2) {
|
|
45797
46920
|
await this.pool.query(
|
|
45798
|
-
`INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at)
|
|
45799
|
-
VALUES ($1,$2,$3,$4,$5,$6)`,
|
|
45800
|
-
[m2.id, m2.sessionId, m2.role, m2.content, m2.seq, m2.createdAt]
|
|
46921
|
+
`INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id)
|
|
46922
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)`,
|
|
46923
|
+
[m2.id, m2.sessionId, m2.role, m2.content, m2.seq, m2.createdAt, m2.runId ?? null]
|
|
45801
46924
|
);
|
|
45802
46925
|
}
|
|
45803
46926
|
async listMessages(sessionId, limit) {
|
|
@@ -45809,6 +46932,19 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
45809
46932
|
const rows = r.rows.map(rowToMessage);
|
|
45810
46933
|
return limit ? rows.reverse() : rows;
|
|
45811
46934
|
}
|
|
46935
|
+
async linkWorkOrder(sessionId, workOrderId) {
|
|
46936
|
+
await this.pool.query(
|
|
46937
|
+
`INSERT INTO ${this.s}.chat_session_work_orders (session_id, work_order_id) VALUES ($1,$2) ON CONFLICT DO NOTHING`,
|
|
46938
|
+
[sessionId, workOrderId]
|
|
46939
|
+
);
|
|
46940
|
+
}
|
|
46941
|
+
async listWorkOrdersForSession(sessionId) {
|
|
46942
|
+
const r = await this.pool.query(
|
|
46943
|
+
`SELECT work_order_id FROM ${this.s}.chat_session_work_orders WHERE session_id = $1 ORDER BY linked_at`,
|
|
46944
|
+
[sessionId]
|
|
46945
|
+
);
|
|
46946
|
+
return r.rows.map((row) => row.work_order_id);
|
|
46947
|
+
}
|
|
45812
46948
|
};
|
|
45813
46949
|
var rowToSession = (row) => ({
|
|
45814
46950
|
id: row.id,
|
|
@@ -45826,7 +46962,8 @@ var rowToMessage = (row) => ({
|
|
|
45826
46962
|
role: row.role,
|
|
45827
46963
|
content: row.content,
|
|
45828
46964
|
seq: Number(row.seq),
|
|
45829
|
-
createdAt: new Date(row.created_at).toISOString()
|
|
46965
|
+
createdAt: new Date(row.created_at).toISOString(),
|
|
46966
|
+
...row.run_id != null ? { runId: row.run_id } : {}
|
|
45830
46967
|
});
|
|
45831
46968
|
|
|
45832
46969
|
// ../storage/src/postgres-nodes.ts
|
|
@@ -46003,18 +47140,42 @@ var rowToRuntime = (row) => ({
|
|
|
46003
47140
|
init_src3();
|
|
46004
47141
|
init_trajectory();
|
|
46005
47142
|
var DEFAULT_CHAT_SKILLS = ["build-workorder"];
|
|
47143
|
+
var SESSION_TOKEN_GRACE_MS = 15 * 6e4;
|
|
47144
|
+
function normalizeHttpBaseUrl(raw) {
|
|
47145
|
+
const url = new URL(raw);
|
|
47146
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
47147
|
+
throw new Error(`public URL \u5FC5\u987B\u662F http(s): ${raw}`);
|
|
47148
|
+
}
|
|
47149
|
+
url.hash = "";
|
|
47150
|
+
url.search = "";
|
|
47151
|
+
const out = url.toString();
|
|
47152
|
+
return out.endsWith("/") ? out.slice(0, -1) : out;
|
|
47153
|
+
}
|
|
47154
|
+
function apiUrlFromGatewayUrl(raw) {
|
|
47155
|
+
if (!raw) return void 0;
|
|
47156
|
+
try {
|
|
47157
|
+
const url = new URL(raw);
|
|
47158
|
+
if (url.protocol === "wss:") url.protocol = "https:";
|
|
47159
|
+
else if (url.protocol === "ws:") url.protocol = "http:";
|
|
47160
|
+
else if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
|
|
47161
|
+
url.pathname = url.pathname.replace(/\/node-gateway\/?$/, "") || "/";
|
|
47162
|
+
return normalizeHttpBaseUrl(url.toString());
|
|
47163
|
+
} catch {
|
|
47164
|
+
return void 0;
|
|
47165
|
+
}
|
|
47166
|
+
}
|
|
46006
47167
|
var oasisWrapperCache;
|
|
46007
47168
|
function oasisWrapperScript() {
|
|
46008
|
-
if (oasisWrapperCache &&
|
|
47169
|
+
if (oasisWrapperCache && fs14.existsSync(oasisWrapperCache)) return oasisWrapperCache;
|
|
46009
47170
|
const repoRoot = (0, import_node_url3.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
|
|
46010
47171
|
const tsx = path11.join(repoRoot, "node_modules/.bin/tsx");
|
|
46011
47172
|
const main = path11.join(repoRoot, "packages/cli/src/main.ts");
|
|
46012
|
-
const dir =
|
|
47173
|
+
const dir = fs14.mkdtempSync(path11.join(os6.tmpdir(), "oasis-cli-"));
|
|
46013
47174
|
const wrapper = path11.join(dir, "oasis");
|
|
46014
|
-
|
|
47175
|
+
fs14.writeFileSync(wrapper, `#!/usr/bin/env bash
|
|
46015
47176
|
exec ${JSON.stringify(tsx)} ${JSON.stringify(main)} "$@"
|
|
46016
47177
|
`);
|
|
46017
|
-
|
|
47178
|
+
fs14.chmodSync(wrapper, 493);
|
|
46018
47179
|
oasisWrapperCache = wrapper;
|
|
46019
47180
|
return wrapper;
|
|
46020
47181
|
}
|
|
@@ -46071,13 +47232,14 @@ async function buildActorContext(service, actorId) {
|
|
|
46071
47232
|
function traceRuntimeKind(runtimeKind) {
|
|
46072
47233
|
if (runtimeKind === "claude" || runtimeKind === "claude-code") return "claude-code";
|
|
46073
47234
|
if (runtimeKind === "codex") return "codex";
|
|
47235
|
+
if (runtimeKind === "hermes") return "hermes";
|
|
46074
47236
|
return "custom";
|
|
46075
47237
|
}
|
|
46076
47238
|
async function startServe(opts) {
|
|
46077
|
-
|
|
47239
|
+
fs14.mkdirSync(opts.dir, { recursive: true });
|
|
46078
47240
|
const lock = path11.join(opts.dir, "serve.lock");
|
|
46079
|
-
if (
|
|
46080
|
-
const pid = Number(
|
|
47241
|
+
if (fs14.existsSync(lock)) {
|
|
47242
|
+
const pid = Number(fs14.readFileSync(lock, "utf8"));
|
|
46081
47243
|
let alive = false;
|
|
46082
47244
|
try {
|
|
46083
47245
|
process.kill(pid, 0);
|
|
@@ -46085,12 +47247,12 @@ async function startServe(opts) {
|
|
|
46085
47247
|
} catch {
|
|
46086
47248
|
}
|
|
46087
47249
|
if (alive) throw new Error(`\u8BE5\u6570\u636E\u76EE\u5F55\u5DF2\u6709 serve \u5728\u8DD1\uFF08pid=${pid}\uFF09\u2014\u2014\u5355\u5199\u8005\u8FDB\u7A0B\uFF0C\u4E0D\u80FD\u53CC\u5F00`);
|
|
46088
|
-
|
|
47250
|
+
fs14.unlinkSync(lock);
|
|
46089
47251
|
}
|
|
46090
|
-
|
|
47252
|
+
fs14.writeFileSync(lock, String(process.pid));
|
|
46091
47253
|
const loadJson = (name) => {
|
|
46092
47254
|
const file = path11.join(opts.dir, name);
|
|
46093
|
-
return
|
|
47255
|
+
return fs14.existsSync(file) ? JSON.parse(fs14.readFileSync(file, "utf8")) : void 0;
|
|
46094
47256
|
};
|
|
46095
47257
|
const oplog = await NdjsonOplogStore.open(path11.join(opts.dir, "oplog.ndjson"));
|
|
46096
47258
|
const blobs = new DirBlobStore(path11.join(opts.dir, "blobs"));
|
|
@@ -46135,16 +47297,16 @@ async function startServe(opts) {
|
|
|
46135
47297
|
const issuer = createTokenIssuer();
|
|
46136
47298
|
const operatorActor = `actor:human:${os6.userInfo().username}`;
|
|
46137
47299
|
const operatorToken = issuer.issue(operatorActor);
|
|
46138
|
-
|
|
47300
|
+
fs14.writeFileSync(path11.join(opts.dir, "operator-token"), operatorToken, { mode: 384 });
|
|
46139
47301
|
let nodeTokens = createNodeTokenStore(path11.join(opts.dir, "node-tokens.json"));
|
|
46140
47302
|
const enrollTokens = createEnrollTokenStore(30 * 60 * 1e3, path11.join(opts.dir, "enroll-tokens.json"));
|
|
46141
47303
|
let nodeStore = await FileNodeStore.open(path11.join(opts.dir, "nodes.json"));
|
|
46142
47304
|
const registryAuditFile = path11.join(opts.dir, "registry-audit.ndjson");
|
|
46143
47305
|
const registryListeners = /* @__PURE__ */ new Set();
|
|
46144
|
-
const registryAudit = (
|
|
46145
|
-
|
|
47306
|
+
const registryAudit = (record4) => {
|
|
47307
|
+
fs14.appendFile(registryAuditFile, JSON.stringify(record4) + "\n", () => {
|
|
46146
47308
|
});
|
|
46147
|
-
for (const l of registryListeners) l({ kind:
|
|
47309
|
+
for (const l of registryListeners) l({ kind: record4.kind, actor: record4.actor, target: record4.target, timestamp: record4.timestamp });
|
|
46148
47310
|
};
|
|
46149
47311
|
await syncRegistryRolesToKernel(registryStore, kernel);
|
|
46150
47312
|
const actors = createActorsDomain({
|
|
@@ -46226,20 +47388,22 @@ async function startServe(opts) {
|
|
|
46226
47388
|
console.log("[serve] \u8FD0\u884C\u8F68\u8FF9\u4F53\u7CFB\uFF1A\u672C\u5730 ndjson dev store");
|
|
46227
47389
|
}
|
|
46228
47390
|
const trace = createTraceDomain({ store: traceStore });
|
|
46229
|
-
const traceStoreSink = new TraceStoreSink({ store: traceStore, runtimeKind: "
|
|
47391
|
+
const traceStoreSink = new TraceStoreSink({ store: traceStore, runtimeKind: "auto" });
|
|
46230
47392
|
const automationStore = pgPool ? await PostgresAutomationStore.open(pgPool, pgSchema) : new MemoryAutomationStore();
|
|
46231
47393
|
console.log(`[serve] \u81EA\u52A8\u5316\u4F53\u7CFB\uFF1A${pgPool ? `Postgres schema=${pgSchema}` : "\u5185\u5B58 dev store"}`);
|
|
46232
47394
|
const automations = createAutomationsDomain({ store: automationStore, kernel });
|
|
46233
47395
|
const controlPlaneStore = await FileControlPlaneStore.open(path11.join(opts.dir, "control-plane.json"));
|
|
46234
|
-
const companies = createCompaniesDomain({ store: controlPlaneStore, blobs });
|
|
46235
|
-
const jwtSecret = process.env["OASIS_JWT_SECRET"];
|
|
46236
|
-
if (!jwtSecret) console.warn("[serve] OASIS_JWT_SECRET \u672A\u8BBE\u2014\u2014\u7528 dev \u9ED8\u8BA4\u5BC6\u94A5\uFF0C\u751F\u4EA7\u52A1\u5FC5\u914D\u7F6E\uFF08\u5426\u5219\u4F1A\u8BDD\u53EF\u4F2A\u9020\uFF09");
|
|
46237
47396
|
const resendKey = process.env["RESEND_API_KEY"];
|
|
46238
47397
|
const mailFrom = process.env["RESEND_FROM_EMAIL"] ?? process.env["OASIS_MAIL_FROM"] ?? "noreply@open-friday.com";
|
|
46239
|
-
if (!resendKey) console.warn("[serve] RESEND_API_KEY \u672A\u8BBE\u2014\u2014\u9A8C\u8BC1\u7801\u6253\u5370\u5230\u65E5\u5FD7\uFF08dev\uFF09\uFF1B\u751F\u4EA7\u8BF7\u914D RESEND_API_KEY + RESEND_FROM_EMAIL\uFF08Resend \u5DF2\u9A8C\u8BC1\u57DF\u540D\uFF09");
|
|
47398
|
+
if (!resendKey) console.warn("[serve] RESEND_API_KEY \u672A\u8BBE\u2014\u2014\u9A8C\u8BC1\u7801/\u9080\u8BF7\u6253\u5370\u5230\u65E5\u5FD7\uFF08dev\uFF09\uFF1B\u751F\u4EA7\u8BF7\u914D RESEND_API_KEY + RESEND_FROM_EMAIL\uFF08Resend \u5DF2\u9A8C\u8BC1\u57DF\u540D\uFF09");
|
|
47399
|
+
const mailSender = resendKey ? resendSender({ apiKey: resendKey, from: mailFrom }) : consoleSender();
|
|
47400
|
+
const consoleUrl = process.env["OASIS_CONSOLE_URL"] ?? "https://design.open-friday.com/oasis-preview/";
|
|
47401
|
+
const companies = createCompaniesDomain({ store: controlPlaneStore, blobs, sender: mailSender, consoleUrl });
|
|
47402
|
+
const jwtSecret = process.env["OASIS_JWT_SECRET"];
|
|
47403
|
+
if (!jwtSecret) console.warn("[serve] OASIS_JWT_SECRET \u672A\u8BBE\u2014\u2014\u7528 dev \u9ED8\u8BA4\u5BC6\u94A5\uFF0C\u751F\u4EA7\u52A1\u5FC5\u914D\u7F6E\uFF08\u5426\u5219\u4F1A\u8BDD\u53EF\u4F2A\u9020\uFF09");
|
|
46240
47404
|
const auth = createAuth({
|
|
46241
47405
|
store: controlPlaneStore,
|
|
46242
|
-
sender:
|
|
47406
|
+
sender: mailSender,
|
|
46243
47407
|
jwtSecret: jwtSecret ?? "oasis-dev-secret-change-me",
|
|
46244
47408
|
// dev 经 http/vite 反代,置 Secure 会丢 cookie;生产经 https 应由部署设 OASIS_SECURE_COOKIE=1
|
|
46245
47409
|
secureCookie: process.env["OASIS_SECURE_COOKIE"] === "1",
|
|
@@ -46247,11 +47411,7 @@ async function startServe(opts) {
|
|
|
46247
47411
|
provisionAccount: (email2) => companies.service.ensureAccount(email2)
|
|
46248
47412
|
});
|
|
46249
47413
|
await migrateDefaultCompany(controlPlaneStore, await actors.service.listActors());
|
|
46250
|
-
|
|
46251
|
-
if (!await controlPlaneStore.getMember(DEFAULT_COMPANY_ID, devActor)) {
|
|
46252
|
-
await controlPlaneStore.upsertMember({ companyId: DEFAULT_COMPANY_ID, accountId: devActor, actorId: devActor, role: "member" });
|
|
46253
|
-
}
|
|
46254
|
-
}
|
|
47414
|
+
await bootstrapBuildWorkorderSkill(actors.service).catch((err) => console.warn(`[serve] build-workorder skill bootstrap failed: ${String(err)}`));
|
|
46255
47415
|
if (!await controlPlaneStore.getMember(DEFAULT_COMPANY_ID, operatorActor)) {
|
|
46256
47416
|
await controlPlaneStore.upsertMember({ companyId: DEFAULT_COMPANY_ID, accountId: operatorActor, actorId: operatorActor, role: "owner" });
|
|
46257
47417
|
}
|
|
@@ -46283,12 +47443,12 @@ async function startServe(opts) {
|
|
|
46283
47443
|
return auth.sessionAccountId(headers);
|
|
46284
47444
|
};
|
|
46285
47445
|
const interventionDrafts = new InterventionDraftStore();
|
|
46286
|
-
const workorderDrafts = new WorkorderDraftStore();
|
|
47446
|
+
const workorderDrafts = new WorkorderDraftStore(path11.join(opts.dir, "workorder-drafts.json"));
|
|
46287
47447
|
const journalFile = path11.join(opts.dir, "dispatch-journal.ndjson");
|
|
46288
47448
|
const journalRing = [];
|
|
46289
|
-
if (
|
|
47449
|
+
if (fs14.existsSync(journalFile)) {
|
|
46290
47450
|
try {
|
|
46291
|
-
const lines =
|
|
47451
|
+
const lines = fs14.readFileSync(journalFile, "utf8").split(/\r?\n/).filter(Boolean);
|
|
46292
47452
|
for (const line of lines.slice(-500)) {
|
|
46293
47453
|
try {
|
|
46294
47454
|
journalRing.push(JSON.parse(line));
|
|
@@ -46301,9 +47461,43 @@ async function startServe(opts) {
|
|
|
46301
47461
|
const journal = (entry) => {
|
|
46302
47462
|
journalRing.push(entry);
|
|
46303
47463
|
if (journalRing.length > 500) journalRing.shift();
|
|
46304
|
-
|
|
47464
|
+
fs14.appendFile(journalFile, JSON.stringify(entry) + "\n", () => {
|
|
46305
47465
|
});
|
|
46306
47466
|
};
|
|
47467
|
+
const closeOrphanedDispatches = () => {
|
|
47468
|
+
const open = /* @__PURE__ */ new Map();
|
|
47469
|
+
for (const entry of journalRing) {
|
|
47470
|
+
if (entry.kind === "dispatched") {
|
|
47471
|
+
open.set(entry.jobKey, entry);
|
|
47472
|
+
} else if (entry.kind === "exited" || entry.kind === "fused") {
|
|
47473
|
+
open.delete(entry.jobKey);
|
|
47474
|
+
}
|
|
47475
|
+
}
|
|
47476
|
+
if (open.size === 0) return;
|
|
47477
|
+
const now = Date.now();
|
|
47478
|
+
const at = new Date(now).toISOString();
|
|
47479
|
+
for (const entry of open.values()) {
|
|
47480
|
+
const startedAt = Date.parse(entry.at);
|
|
47481
|
+
const lastOpAt = kernel.model.lastOpAt.get(entry.artifactId);
|
|
47482
|
+
const lastOpTime = lastOpAt ? Date.parse(lastOpAt) : NaN;
|
|
47483
|
+
const progressed = Number.isFinite(startedAt) && Number.isFinite(lastOpTime) && lastOpTime > startedAt;
|
|
47484
|
+
journal({
|
|
47485
|
+
kind: "exited",
|
|
47486
|
+
at,
|
|
47487
|
+
jobKey: entry.jobKey,
|
|
47488
|
+
artifactId: entry.artifactId,
|
|
47489
|
+
actor: entry.actor,
|
|
47490
|
+
action: entry.action,
|
|
47491
|
+
code: null,
|
|
47492
|
+
reason: "timeout",
|
|
47493
|
+
durationMs: Number.isFinite(startedAt) ? Math.max(0, now - startedAt) : 0,
|
|
47494
|
+
progressed,
|
|
47495
|
+
consecutiveFailures: progressed ? 0 : 1
|
|
47496
|
+
});
|
|
47497
|
+
}
|
|
47498
|
+
console.warn(`[dispatch] closed ${open.size} orphaned dispatch journal entr${open.size === 1 ? "y" : "ies"} from a previous server process`);
|
|
47499
|
+
};
|
|
47500
|
+
if (opts.dispatch) closeOrphanedDispatches();
|
|
46307
47501
|
let dispatcher;
|
|
46308
47502
|
const trajListeners = /* @__PURE__ */ new Set();
|
|
46309
47503
|
const trajectorySink = new FsTrajectorySink(opts.dir, (msg) => {
|
|
@@ -46326,6 +47520,27 @@ async function startServe(opts) {
|
|
|
46326
47520
|
let hub = null;
|
|
46327
47521
|
let chatRemoteAdapter = null;
|
|
46328
47522
|
let localUrl = "";
|
|
47523
|
+
const remoteApiUrl = opts.publicUrl ? normalizeHttpBaseUrl(opts.publicUrl) : apiUrlFromGatewayUrl(opts.gatewayUrl);
|
|
47524
|
+
const serverUrlForBinding = (binding) => {
|
|
47525
|
+
if (!binding?.nodeId) return localUrl;
|
|
47526
|
+
if (remoteApiUrl) return remoteApiUrl;
|
|
47527
|
+
throw new Error(`\u8FDC\u7A0B\u8282\u70B9 ${binding.nodeId} \u9700\u8981\u53EF\u8FBE\u7684 Oasis API \u5730\u5740\uFF1A\u914D\u7F6E --public-url / $OASIS_PUBLIC_URL\uFF0C\u6216\u4F7F\u7528\u53EF\u63A8\u5BFC\u7684 --gateway-url`);
|
|
47528
|
+
};
|
|
47529
|
+
const issueSessionToken = (actorId, ctx) => {
|
|
47530
|
+
const token = issuer.issue(actorId, {
|
|
47531
|
+
ttlMs: ctx.limits.wallClockMs + SESSION_TOKEN_GRACE_MS,
|
|
47532
|
+
artifactId: ctx.artifactId,
|
|
47533
|
+
action: ctx.action,
|
|
47534
|
+
...ctx.sessionId !== void 0 ? { sessionId: ctx.sessionId } : {},
|
|
47535
|
+
...ctx.binding?.nodeId !== void 0 ? { nodeId: ctx.binding.nodeId } : {},
|
|
47536
|
+
...ctx.binding?.runtimeKind !== void 0 ? { runtimeKind: ctx.binding.runtimeKind } : {},
|
|
47537
|
+
scopes: ctx.scopes ?? ["oasis:api"]
|
|
47538
|
+
});
|
|
47539
|
+
if (issuer.resolve(token) !== actorId) {
|
|
47540
|
+
throw new Error(`\u521A\u7B7E\u53D1\u7684 OASIS_TOKEN \u65E0\u6CD5\u89E3\u6790\u4E3A ${actorId}`);
|
|
47541
|
+
}
|
|
47542
|
+
return token;
|
|
47543
|
+
};
|
|
46329
47544
|
let resolveCodeRepoRef;
|
|
46330
47545
|
const server = await startOasisServer({
|
|
46331
47546
|
kernel: engine.kernel,
|
|
@@ -46373,15 +47588,17 @@ async function startServe(opts) {
|
|
|
46373
47588
|
console.warn(`[planner] skill materialize failed for ${planner.id}: ${String(err)}`);
|
|
46374
47589
|
return {};
|
|
46375
47590
|
}));
|
|
47591
|
+
const limits = { wallClockMs: 5 * 6e4 };
|
|
47592
|
+
const artifactId = `artifact:planner:${(0, import_node_crypto20.randomUUID)()}`;
|
|
46376
47593
|
const handle = await chatRemoteAdapter.spawn({
|
|
46377
47594
|
actor: planner.id,
|
|
46378
|
-
actorToken:
|
|
46379
|
-
artifactId
|
|
47595
|
+
actorToken: issueSessionToken(planner.id, { artifactId, action: "plan-workorder", limits, binding }),
|
|
47596
|
+
artifactId,
|
|
46380
47597
|
bundle: { files },
|
|
46381
47598
|
...actorCtx?.config?.prompt ? { systemPrompt: actorCtx.config.prompt } : {},
|
|
46382
|
-
server: { url:
|
|
47599
|
+
server: { url: serverUrlForBinding(binding) },
|
|
46383
47600
|
binding,
|
|
46384
|
-
limits
|
|
47601
|
+
limits,
|
|
46385
47602
|
env: provision.env,
|
|
46386
47603
|
wrapperPaths: provision.wrapperPaths
|
|
46387
47604
|
});
|
|
@@ -46415,15 +47632,18 @@ async function startServe(opts) {
|
|
|
46415
47632
|
registry: registryStore,
|
|
46416
47633
|
artifacts: artifactStateStore,
|
|
46417
47634
|
dispatchJournal: () => journalRing.slice(-200),
|
|
47635
|
+
workorderDrafts,
|
|
47636
|
+
// 活工单查不到时回退查建单草案(status="draft")
|
|
46418
47637
|
...opts.sla ? { sla: opts.sla } : {},
|
|
46419
47638
|
resolveEngine: (cid) => engineRouter.getEngine(cid ?? DEFAULT_COMPANY_ID)
|
|
46420
47639
|
}),
|
|
46421
47640
|
trace.register,
|
|
46422
47641
|
automations.register,
|
|
46423
|
-
...chatSessionStore ? [createChatSessionsDomain({ store: chatSessionStore, registry: registryStore })] : []
|
|
47642
|
+
...chatSessionStore ? [createChatSessionsDomain({ store: chatSessionStore, registry: registryStore, trace: traceStore, kernel, artifactState: artifactStateStore })] : []
|
|
46424
47643
|
],
|
|
46425
47644
|
drafts: interventionDrafts,
|
|
46426
47645
|
workorderDrafts,
|
|
47646
|
+
...chatSessionStore ? { chatSession: chatSessionStore } : {},
|
|
46427
47647
|
// 暴露注册的 artifact 类型,给前端「发起工单」动态拉取根产物可选类型——schema.json 增改即自动扩展,
|
|
46428
47648
|
// 前端不再写死枚举。走通用 /api/view(generic data),非域路由契约,不进 gen:api。
|
|
46429
47649
|
views: {
|
|
@@ -46443,6 +47663,7 @@ async function startServe(opts) {
|
|
|
46443
47663
|
resolveActorEnv: async (actorId) => (await buildActorProvision(actors.service, actorId)).env,
|
|
46444
47664
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
46445
47665
|
materializeSkills: (actorId, runtimeKind) => materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind }),
|
|
47666
|
+
uploadChatAttachment: async ({ contentBase64 }) => ({ blobRef: await assets.put(new Uint8Array(Buffer.from(contentBase64, "base64"))) }),
|
|
46446
47667
|
dispatchProduce: async ({ artifactId, actorId, part }) => {
|
|
46447
47668
|
if (!dispatcher) {
|
|
46448
47669
|
throw new ApiError(503, "DISPATCH_NOT_ENABLED", "\u670D\u52A1\u7AEF\u672A\u542F\u7528 --dispatch true\uFF0C\u4E0D\u80FD\u62C9\u8D77\u771F\u5B9E\u667A\u80FD\u4F53\u4FEE\u6539");
|
|
@@ -46453,7 +47674,7 @@ async function startServe(opts) {
|
|
|
46453
47674
|
...part !== void 0 ? { part } : {}
|
|
46454
47675
|
});
|
|
46455
47676
|
},
|
|
46456
|
-
dispatchChat: async ({ actorId, message, sessionId }) => {
|
|
47677
|
+
dispatchChat: async ({ actorId, message, sessionId, chatSessionId, attachments }) => {
|
|
46457
47678
|
const binding = await actors.service.resolveBinding()(actorId);
|
|
46458
47679
|
if (!binding) {
|
|
46459
47680
|
throw new ApiError(409, "ACTOR_BINDING_MISSING", `\u6570\u5B57\u5458\u5DE5 ${actorId} \u6CA1\u6709 active runtime \u7ED1\u5B9A`);
|
|
@@ -46526,6 +47747,29 @@ async function startServe(opts) {
|
|
|
46526
47747
|
};
|
|
46527
47748
|
const provision = await buildActorProvision(actors.service, actorId);
|
|
46528
47749
|
const actorCtx = await buildActorContext(actors.service, actorId).catch(() => null);
|
|
47750
|
+
const attachmentFiles = {};
|
|
47751
|
+
const attachmentBinary = {};
|
|
47752
|
+
const attachmentPaths = [];
|
|
47753
|
+
const usedRel = /* @__PURE__ */ new Set();
|
|
47754
|
+
for (const att of attachments ?? []) {
|
|
47755
|
+
const safe = (att.name || "attachment").replace(/[^\w.\-]+/g, "_").replace(/^\.+/, "").slice(0, 80) || "attachment";
|
|
47756
|
+
let rel = `inputs/attachments/${safe}`;
|
|
47757
|
+
let n = 1;
|
|
47758
|
+
while (usedRel.has(rel)) rel = `inputs/attachments/${n++}-${safe}`;
|
|
47759
|
+
usedRel.add(rel);
|
|
47760
|
+
if (typeof att.text === "string") {
|
|
47761
|
+
attachmentFiles[rel] = att.text;
|
|
47762
|
+
attachmentPaths.push(rel);
|
|
47763
|
+
} else if (att.blobRef) {
|
|
47764
|
+
try {
|
|
47765
|
+
const bytes = await assets.get(att.blobRef);
|
|
47766
|
+
attachmentBinary[rel] = Buffer.from(bytes).toString("base64");
|
|
47767
|
+
attachmentPaths.push(rel);
|
|
47768
|
+
} catch (err) {
|
|
47769
|
+
console.warn(`[chat] \u9644\u4EF6 blob ${att.blobRef} \u8BFB\u53D6\u5931\u8D25: ${String(err)}`);
|
|
47770
|
+
}
|
|
47771
|
+
}
|
|
47772
|
+
}
|
|
46529
47773
|
const files = {
|
|
46530
47774
|
"TASK.md": [
|
|
46531
47775
|
"# \u5373\u65F6\u8111\u66B4\u5BF9\u8BDD",
|
|
@@ -46534,12 +47778,14 @@ async function startServe(opts) {
|
|
|
46534
47778
|
`actor_id: ${actorId}`,
|
|
46535
47779
|
"",
|
|
46536
47780
|
"\u8BF7\u76F4\u63A5\u56DE\u7B54\u7528\u6237\u95EE\u9898\u3002\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u5199\u5165\u7CFB\u7EDF\uFF0C\u5426\u5219\u4E0D\u8981\u521B\u5EFA\u6216\u4FEE\u6539 artifact\u3002",
|
|
47781
|
+
...attachmentPaths.length ? ["", "## \u7528\u6237\u9644\u4EF6", "", "\u7528\u6237\u968F\u6D88\u606F\u9644\u5E26\u4E86\u4EE5\u4E0B\u6587\u4EF6\uFF08\u5728\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0B\uFF0C\u53EF\u76F4\u63A5\u8BFB\u53D6\uFF09\uFF1A", ...attachmentPaths.map((p2) => `- ${p2}`)] : [],
|
|
46537
47782
|
"",
|
|
46538
47783
|
"## \u7528\u6237\u6D88\u606F",
|
|
46539
47784
|
"",
|
|
46540
47785
|
message
|
|
46541
47786
|
].join("\n")
|
|
46542
47787
|
};
|
|
47788
|
+
Object.assign(files, attachmentFiles);
|
|
46543
47789
|
if (actorCtx?.config?.prompt) {
|
|
46544
47790
|
Object.assign(files, splitIdentityFiles3(actorCtx.config.prompt));
|
|
46545
47791
|
}
|
|
@@ -46553,18 +47799,23 @@ async function startServe(opts) {
|
|
|
46553
47799
|
console.warn(`[chat] skill materialize failed for ${actorId}: ${String(err)}`);
|
|
46554
47800
|
return {};
|
|
46555
47801
|
}));
|
|
47802
|
+
const limits = { wallClockMs: 10 * 6e4 };
|
|
47803
|
+
const artifactId = `artifact:chat:${(0, import_node_crypto20.randomUUID)()}`;
|
|
46556
47804
|
const handle = await chatRemoteAdapter.spawn({
|
|
46557
47805
|
actor: actorId,
|
|
46558
|
-
actorToken:
|
|
46559
|
-
artifactId
|
|
46560
|
-
bundle: { files },
|
|
47806
|
+
actorToken: issueSessionToken(actorId, { artifactId, action: "chat", limits, binding, sessionId: runtimeSessionId }),
|
|
47807
|
+
artifactId,
|
|
47808
|
+
bundle: { files, ...Object.keys(attachmentBinary).length ? { binaryFiles: attachmentBinary } : {} },
|
|
46561
47809
|
...actorCtx?.config?.prompt ? { systemPrompt: actorCtx.config.prompt } : {},
|
|
46562
47810
|
runtimeSessionId,
|
|
46563
47811
|
resumeRuntimeSession,
|
|
46564
|
-
server: { url:
|
|
47812
|
+
server: { url: serverUrlForBinding(binding) },
|
|
46565
47813
|
binding,
|
|
46566
|
-
limits
|
|
46567
|
-
|
|
47814
|
+
limits,
|
|
47815
|
+
// OASIS_STAGE=1:chat agent 的改图命令(edit/link/spawn/cancel)经 /api/stage → 草案路由
|
|
47816
|
+
// (建单/改图暂存统一,见 docs/proposals/draft-edit-unification.md)。不设 OASIS_WORKSPACE——
|
|
47817
|
+
// chat agent 不绑单一工单,按命令 --workspace / id 定位是哪份草案。
|
|
47818
|
+
env: { ...provision.env, OASIS_STAGE: "1", ...chatSessionId ? { OASIS_CHAT_SESSION_ID: chatSessionId } : {} },
|
|
46568
47819
|
wrapperPaths: provision.wrapperPaths
|
|
46569
47820
|
});
|
|
46570
47821
|
const buffered = [];
|
|
@@ -46607,6 +47858,7 @@ async function startServe(opts) {
|
|
|
46607
47858
|
});
|
|
46608
47859
|
return {
|
|
46609
47860
|
id: runtimeSessionId,
|
|
47861
|
+
runId: traceRunId,
|
|
46610
47862
|
onOutput(cb) {
|
|
46611
47863
|
for (const chunk of buffered.splice(0)) cb(chunk);
|
|
46612
47864
|
outputCbs.push(cb);
|
|
@@ -46637,6 +47889,7 @@ async function startServe(opts) {
|
|
|
46637
47889
|
chatRemoteAdapter = new DaemonHubAdapter(hub);
|
|
46638
47890
|
const port = new URL(server.baseUrl).port;
|
|
46639
47891
|
localUrl = `http://127.0.0.1:${port}`;
|
|
47892
|
+
console.log(`[serve] \u8FDC\u7A0B agent API URL\uFF1A${remoteApiUrl ?? "\u672A\u914D\u7F6E\uFF08\u4EC5\u652F\u6301\u672C\u673A runtime\uFF09"}`);
|
|
46640
47893
|
const projectsCfg = loadJson("projects.json");
|
|
46641
47894
|
const seedProjects = (projectsCfg?.projects ?? []).map(
|
|
46642
47895
|
(p2) => p2.repos?.length ? { ...p2, repos: p2.repos.map((r) => r.repoDir ? { ...r, repoDir: path11.resolve(opts.dir, r.repoDir) } : r) } : p2
|
|
@@ -46684,6 +47937,7 @@ async function startServe(opts) {
|
|
|
46684
47937
|
remote: new DaemonHubAdapter(hub)
|
|
46685
47938
|
}),
|
|
46686
47939
|
serverUrl: localUrl,
|
|
47940
|
+
serverUrlFor: (ctx) => serverUrlForBinding(ctx.binding),
|
|
46687
47941
|
resolveBinding: actors.service.resolveBinding(),
|
|
46688
47942
|
provision: (actorId) => buildActorProvision(actors.service, actorId),
|
|
46689
47943
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
@@ -46693,7 +47947,13 @@ async function startServe(opts) {
|
|
|
46693
47947
|
// §4/C:异步从权威源(store)解析;dispatcher 装配前 await
|
|
46694
47948
|
// #2 派单开关:工单"待派发"(控制面旁存标 held)时不派其活。held 集通常空、表极小,每次查可接受。
|
|
46695
47949
|
resolveDispatchHold: async (ws) => (await artifactStateStore.listWorkspaceDispatchHeld()).includes(ws),
|
|
46696
|
-
tokenFor: (actor) =>
|
|
47950
|
+
tokenFor: (actor, ctx) => issueSessionToken(actor, {
|
|
47951
|
+
artifactId: ctx.artifactId,
|
|
47952
|
+
action: ctx.action,
|
|
47953
|
+
limits: ctx.limits,
|
|
47954
|
+
...ctx.binding !== void 0 ? { binding: ctx.binding } : {},
|
|
47955
|
+
...ctx.part !== void 0 ? { sessionId: `${ctx.action}:${ctx.artifactId}:${ctx.part}` } : {}
|
|
47956
|
+
}),
|
|
46697
47957
|
journal,
|
|
46698
47958
|
// TrajectoryEvent 是唯一事件模型;trace 旁账与 M7 蒸馏库并行消费同一流(§16.D 收口)。
|
|
46699
47959
|
trajectory: combineTrajectorySinks([trajectorySink, traceStoreSink]),
|
|
@@ -46834,14 +48094,24 @@ async function startServe(opts) {
|
|
|
46834
48094
|
const coordinator = new CoordinatorWorker({
|
|
46835
48095
|
kernel,
|
|
46836
48096
|
oplog,
|
|
46837
|
-
adapter: new
|
|
46838
|
-
|
|
46839
|
-
|
|
46840
|
-
|
|
46841
|
-
|
|
48097
|
+
adapter: new BindingRouterAdapter({
|
|
48098
|
+
local: new ConnectorAwareAdapter({
|
|
48099
|
+
streamJson: true,
|
|
48100
|
+
maxOutputTokens: opts.maxOutputTokens ?? 64e3,
|
|
48101
|
+
cleanupWorkdir: true,
|
|
48102
|
+
...opts.model ? { model: opts.model } : {}
|
|
48103
|
+
}),
|
|
48104
|
+
remote: new DaemonHubAdapter(hub)
|
|
46842
48105
|
}),
|
|
46843
48106
|
serverUrl: localUrl,
|
|
46844
|
-
|
|
48107
|
+
serverUrlFor: (ctx) => serverUrlForBinding(ctx.binding),
|
|
48108
|
+
tokenFor: (actor, ctx) => issueSessionToken(actor, {
|
|
48109
|
+
artifactId: ctx?.artifactId ?? "artifact:coordinator",
|
|
48110
|
+
action: ctx?.action ?? "coordinate",
|
|
48111
|
+
limits: ctx?.limits ?? { wallClockMs: 5 * 6e4 },
|
|
48112
|
+
...ctx?.binding !== void 0 ? { binding: ctx.binding } : {}
|
|
48113
|
+
}),
|
|
48114
|
+
resolveCoordinator: () => resolveCoordinator(registryStore),
|
|
46845
48115
|
schemaTypes: schema.map((d) => d.name),
|
|
46846
48116
|
schemaTypeDescriptions: Object.fromEntries(schema.filter((d) => d.description).map((d) => [d.name, d.description])),
|
|
46847
48117
|
provision: (actorId) => buildActorProvision(actors.service, actorId),
|
|
@@ -46897,7 +48167,7 @@ async function startServe(opts) {
|
|
|
46897
48167
|
await server.close();
|
|
46898
48168
|
if (pgPool) await pgPool.end().catch(() => {
|
|
46899
48169
|
});
|
|
46900
|
-
|
|
48170
|
+
fs14.rmSync(lock, { force: true });
|
|
46901
48171
|
}
|
|
46902
48172
|
};
|
|
46903
48173
|
}
|
|
@@ -46945,8 +48215,22 @@ function log2(tag, msg, extra) {
|
|
|
46945
48215
|
// ../cli/src/daemon/ws-client.ts
|
|
46946
48216
|
var RECONNECT_DELAY_MS = 3e3;
|
|
46947
48217
|
var PING_INTERVAL_MS = 2e4;
|
|
48218
|
+
async function preflightOasisAccess(job) {
|
|
48219
|
+
const base = job.server.url.replace(/\/+$/, "");
|
|
48220
|
+
try {
|
|
48221
|
+
const res = await fetch(`${base}/api/whoami`, {
|
|
48222
|
+
headers: { authorization: `Bearer ${job.actorToken}` },
|
|
48223
|
+
signal: AbortSignal.timeout(5e3)
|
|
48224
|
+
});
|
|
48225
|
+
if (res.status === 401 || res.status === 403) return { code: null, reason: "auth-error" };
|
|
48226
|
+
if (!res.ok) return { code: null, reason: "server-unreachable" };
|
|
48227
|
+
return null;
|
|
48228
|
+
} catch {
|
|
48229
|
+
return { code: null, reason: "server-unreachable" };
|
|
48230
|
+
}
|
|
48231
|
+
}
|
|
46948
48232
|
var DaemonWsClient = class {
|
|
46949
|
-
constructor(serverUrl, daemonId, sessions, adapters = ["claude-code"], token, runtimes = adapters.map((kind) => ({ kind })), nodeName, reportRuntimes =
|
|
48233
|
+
constructor(serverUrl, daemonId, sessions, adapters = ["claude-code"], token, runtimes = adapters.map((kind) => ({ kind })), nodeName, reportRuntimes = true, preflight = true) {
|
|
46950
48234
|
this.serverUrl = serverUrl;
|
|
46951
48235
|
this.daemonId = daemonId;
|
|
46952
48236
|
this.sessions = sessions;
|
|
@@ -46955,6 +48239,7 @@ var DaemonWsClient = class {
|
|
|
46955
48239
|
this.runtimes = runtimes;
|
|
46956
48240
|
this.nodeName = nodeName;
|
|
46957
48241
|
this.reportRuntimes = reportRuntimes;
|
|
48242
|
+
this.preflight = preflight;
|
|
46958
48243
|
}
|
|
46959
48244
|
ws = null;
|
|
46960
48245
|
pingTimer = null;
|
|
@@ -47011,6 +48296,14 @@ var DaemonWsClient = class {
|
|
|
47011
48296
|
const t0 = Date.now();
|
|
47012
48297
|
log2("[node-cli]", `\u2190 dispatch ${dispatchId} artifact=${job.artifactId}`);
|
|
47013
48298
|
try {
|
|
48299
|
+
if (this.preflight) {
|
|
48300
|
+
const preflightFailure = await preflightOasisAccess(job);
|
|
48301
|
+
if (preflightFailure) {
|
|
48302
|
+
log2("[node-cli]", ` preflight failed: ${preflightFailure.reason}`);
|
|
48303
|
+
this.send({ type: "session_exited", dispatchId, sessionId: "preflight-failed", info: preflightFailure });
|
|
48304
|
+
return;
|
|
48305
|
+
}
|
|
48306
|
+
}
|
|
47014
48307
|
const handle = await this.sessions.getAdapter().spawn(job);
|
|
47015
48308
|
this.sessions.register(dispatchId, handle);
|
|
47016
48309
|
log2("[node-cli]", ` \u2192 session_started id=${handle.id} setup=${Date.now() - t0}ms`);
|
|
@@ -47030,7 +48323,10 @@ var DaemonWsClient = class {
|
|
|
47030
48323
|
}
|
|
47031
48324
|
case "kill": {
|
|
47032
48325
|
log2("[node-cli]", `\u2190 kill dispatch=${msg.dispatchId}`);
|
|
47033
|
-
await this.sessions.kill(msg.dispatchId);
|
|
48326
|
+
const killed = await this.sessions.kill(msg.dispatchId);
|
|
48327
|
+
if (!killed) {
|
|
48328
|
+
this.send({ type: "session_exited", dispatchId: msg.dispatchId, sessionId: "missing-session", info: { code: null, reason: "timeout" } });
|
|
48329
|
+
}
|
|
47034
48330
|
break;
|
|
47035
48331
|
}
|
|
47036
48332
|
case "ping":
|
|
@@ -47163,8 +48459,9 @@ async function startNode(opts) {
|
|
|
47163
48459
|
init_src4();
|
|
47164
48460
|
var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uFF08\u670D\u52A1\u7AEF\u7626\u5BA2\u6237\u7AEF\uFF09
|
|
47165
48461
|
|
|
47166
|
-
oasis serve [--dir .oasis] [--port 7320] [--dispatch true [--model haiku] [--max-produce-per-agent 5] [--max-concurrent-produce N]] [--allow-simple-tokens true] [--gateway-url wss://host/oasis-api/node-gateway]
|
|
48462
|
+
oasis serve [--dir .oasis] [--port 7320] [--dispatch true [--model haiku] [--max-produce-per-agent 5] [--max-concurrent-produce N]] [--allow-simple-tokens true] [--public-url https://host/oasis-api] [--gateway-url wss://host/oasis-api/node-gateway]
|
|
47167
48463
|
# --gateway-url\uFF08\u6216 $OASIS_GATEWAY_URL\uFF09\uFF1A\u8282\u70B9\u5165\u7F51\u811A\u672C\u91CC\u5B88\u62A4\u8FDB\u7A0B\u8981\u8FDE\u7684\u7F51\u5173 URL\uFF1B\u516C\u7F51\u7ECF nginx \u5FC5\u8BBE\uFF0C\u5426\u5219\u9ED8\u8BA4\u672C\u673A ws://127.0.0.1:<port>\u3002
|
|
48464
|
+
# --public-url\uFF08\u6216 $OASIS_PUBLIC_URL\uFF09\uFF1A\u8FDC\u7A0B node \u4E0A agent \u56DE\u8FDE\u7684 HTTP API \u5730\u5740\uFF1B\u7F3A\u7701\u4ECE gateway-url \u63A8\u5BFC\u3002
|
|
47168
48465
|
oasis export-trajectories [--dir d] [--out samples.jsonl] \u5BFC\u51FA\u84B8\u998F\u6837\u672C\uFF08\u8F93\u5165\u5305+\u4E8B\u4EF6\u6D41+\u7ED3\u679C\u5F15\u7528+gate \u6807\u7B7E\uFF09
|
|
47169
48466
|
oasis serve install [--dir d] [--port p] [--dispatch true --model m] [--bin ~/.local/bin/oasis] [--unit-dir ~/.config/systemd/user] \u751F\u6210 systemd \u7528\u6237\u5355\u5143\uFF08D2 \u5B88\u62A4\u5316\uFF09
|
|
47170
48467
|
\u5E38\u9A7B\u670D\u52A1\uFF08\u552F\u4E00\u5199\u8005\u8FDB\u7A0B\uFF09\uFF1A/api \u7ED9 CLI\u3001\u524D\u7AEF\u4E0E agent\uFF08agent \u901A\u8FC7 oasis CLI \u8C03\u7528\uFF09\u3002
|
|
@@ -47280,7 +48577,10 @@ function projectSlug(value) {
|
|
|
47280
48577
|
return value.trim().toLowerCase().replace(/[^\w\u4e00-\u9fa5\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 32);
|
|
47281
48578
|
}
|
|
47282
48579
|
function makeClient(base, token) {
|
|
47283
|
-
const
|
|
48580
|
+
const baseHeaders = { authorization: `Bearer ${token}`, "content-type": "application/json" };
|
|
48581
|
+
const chatSessionId = process.env["OASIS_CHAT_SESSION_ID"];
|
|
48582
|
+
if (chatSessionId) baseHeaders["x-chat-session-id"] = chatSessionId;
|
|
48583
|
+
const headers = baseHeaders;
|
|
47284
48584
|
const fail = async (res) => {
|
|
47285
48585
|
const body = await res.json().catch(() => ({}));
|
|
47286
48586
|
const err = body.error;
|
|
@@ -47336,9 +48636,9 @@ async function runCli(argv, println = console.log) {
|
|
|
47336
48636
|
const nodeTokenFile = path12.join(dir, "node-token");
|
|
47337
48637
|
const enrollTokenFile = path12.join(dir, "enroll-token");
|
|
47338
48638
|
const reportMarkerFile = path12.join(dir, "report-runtimes");
|
|
47339
|
-
let nodeId =
|
|
47340
|
-
let token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (
|
|
47341
|
-
const enrollToken = flags.get("enroll-token") ?? process.env["OASIS_ENROLL_TOKEN"] ?? (
|
|
48639
|
+
let nodeId = fs15.existsSync(nodeIdFile) ? fs15.readFileSync(nodeIdFile, "utf8").trim() : null;
|
|
48640
|
+
let token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs15.existsSync(nodeTokenFile) ? fs15.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
|
|
48641
|
+
const enrollToken = flags.get("enroll-token") ?? process.env["OASIS_ENROLL_TOKEN"] ?? (fs15.existsSync(enrollTokenFile) ? fs15.readFileSync(enrollTokenFile, "utf8").trim() : void 0);
|
|
47342
48642
|
let freshEnrollment = false;
|
|
47343
48643
|
if ((!nodeId || !token2) && enrollToken) {
|
|
47344
48644
|
const httpBase2 = serverUrl.replace(/^ws(s?):\/\//, "http$1://").replace(/\/node-gateway.*$/, "");
|
|
@@ -47351,26 +48651,26 @@ async function runCli(argv, println = console.log) {
|
|
|
47351
48651
|
const body = await res.json();
|
|
47352
48652
|
nodeId = body.nodeId;
|
|
47353
48653
|
token2 = body.token;
|
|
47354
|
-
|
|
47355
|
-
|
|
47356
|
-
|
|
48654
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
48655
|
+
fs15.writeFileSync(nodeIdFile, nodeId);
|
|
48656
|
+
fs15.writeFileSync(nodeTokenFile, token2, { mode: 384 });
|
|
47357
48657
|
try {
|
|
47358
|
-
|
|
48658
|
+
fs15.unlinkSync(enrollTokenFile);
|
|
47359
48659
|
} catch {
|
|
47360
48660
|
}
|
|
47361
48661
|
freshEnrollment = true;
|
|
47362
48662
|
}
|
|
47363
48663
|
if (!nodeId) throw new Error("no nodeId \u2014 provide --enroll-token <ent_...> on first run");
|
|
47364
|
-
const scriptRun = freshEnrollment ||
|
|
48664
|
+
const scriptRun = freshEnrollment || fs15.existsSync(reportMarkerFile);
|
|
47365
48665
|
if (flags.has("service")) {
|
|
47366
48666
|
const { spawnSync, execSync: execSync2 } = await import("node:child_process");
|
|
47367
48667
|
if (token2) {
|
|
47368
|
-
|
|
47369
|
-
|
|
48668
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
48669
|
+
fs15.writeFileSync(nodeTokenFile, token2, { mode: 384 });
|
|
47370
48670
|
}
|
|
47371
48671
|
if (scriptRun) {
|
|
47372
|
-
|
|
47373
|
-
|
|
48672
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
48673
|
+
fs15.writeFileSync(reportMarkerFile, "");
|
|
47374
48674
|
}
|
|
47375
48675
|
println("\u2192 Installing oasis_test globally...");
|
|
47376
48676
|
spawnSync("npm", ["install", "-g", "oasis_test@latest"], { stdio: "inherit" });
|
|
@@ -47379,10 +48679,10 @@ async function runCli(argv, println = console.log) {
|
|
|
47379
48679
|
const svcArgs = `node --server-ws ${serverUrl} --dir ${dir}${nodeName ? ` --name ${nodeName}` : ""}`;
|
|
47380
48680
|
if (process.platform === "linux") {
|
|
47381
48681
|
const svcFile = path12.join(os7.homedir(), ".config/systemd/user/oasis-node.service");
|
|
47382
|
-
|
|
48682
|
+
fs15.mkdirSync(path12.dirname(svcFile), { recursive: true });
|
|
47383
48683
|
if (spawnSync("systemctl", ["--user", "is-active", "--quiet", "oasis-node"], {}).status === 0)
|
|
47384
48684
|
spawnSync("systemctl", ["--user", "stop", "oasis-node"], { stdio: "inherit" });
|
|
47385
|
-
|
|
48685
|
+
fs15.writeFileSync(svcFile, [
|
|
47386
48686
|
"[Unit]",
|
|
47387
48687
|
"Description=Oasis Node Daemon",
|
|
47388
48688
|
"After=network.target",
|
|
@@ -47410,10 +48710,10 @@ async function runCli(argv, println = console.log) {
|
|
|
47410
48710
|
println(" Stop : systemctl --user stop oasis-node");
|
|
47411
48711
|
} else if (process.platform === "darwin") {
|
|
47412
48712
|
const plist = path12.join(os7.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
|
|
47413
|
-
|
|
48713
|
+
fs15.mkdirSync(path12.dirname(plist), { recursive: true });
|
|
47414
48714
|
spawnSync("launchctl", ["unload", plist], { stdio: "ignore" });
|
|
47415
48715
|
const argXml = [binPath, "node", "--server-ws", serverUrl, "--dir", dir, ...nodeName ? ["--name", nodeName] : []].map((a) => `<string>${a}</string>`).join("");
|
|
47416
|
-
|
|
48716
|
+
fs15.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>com.oasis.node</string><key>ProgramArguments</key><array>${argXml}</array><key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>StandardOutPath</key><string>${os7.homedir()}/.oasis-node.log</string><key>StandardErrorPath</key><string>${os7.homedir()}/.oasis-node.err</string></dict></plist>`);
|
|
47417
48717
|
spawnSync("launchctl", ["load", plist], { stdio: "inherit" });
|
|
47418
48718
|
println("\u2713 Oasis node service started (macOS LaunchAgent com.oasis.node)");
|
|
47419
48719
|
println(` Logs : tail -f ${os7.homedir()}/.oasis-node.log`);
|
|
@@ -47425,9 +48725,9 @@ async function runCli(argv, println = console.log) {
|
|
|
47425
48725
|
}
|
|
47426
48726
|
return;
|
|
47427
48727
|
}
|
|
47428
|
-
if (
|
|
48728
|
+
if (fs15.existsSync(reportMarkerFile)) {
|
|
47429
48729
|
try {
|
|
47430
|
-
|
|
48730
|
+
fs15.unlinkSync(reportMarkerFile);
|
|
47431
48731
|
} catch {
|
|
47432
48732
|
}
|
|
47433
48733
|
}
|
|
@@ -47479,7 +48779,7 @@ async function runCli(argv, println = console.log) {
|
|
|
47479
48779
|
const outFile = flags.get("out");
|
|
47480
48780
|
const lines = samples.map((sample) => JSON.stringify(sample)).join("\n");
|
|
47481
48781
|
if (outFile !== void 0) {
|
|
47482
|
-
|
|
48782
|
+
fs15.writeFileSync(outFile, lines + (lines ? "\n" : ""));
|
|
47483
48783
|
println(`\u5DF2\u5BFC\u51FA ${samples.length} \u6761\u8F68\u8FF9\u6837\u672C \u2192 ${outFile}\uFF08approved/rejected \u6210\u5BF9\u5373\u504F\u597D\u5BF9\uFF09`);
|
|
47484
48784
|
} else {
|
|
47485
48785
|
println(lines);
|
|
@@ -47507,9 +48807,9 @@ async function runCli(argv, println = console.log) {
|
|
|
47507
48807
|
`WantedBy=default.target`,
|
|
47508
48808
|
``
|
|
47509
48809
|
].join("\n");
|
|
47510
|
-
|
|
48810
|
+
fs15.mkdirSync(unitDir, { recursive: true });
|
|
47511
48811
|
const unitPath = path12.join(unitDir, `${name}.service`);
|
|
47512
|
-
|
|
48812
|
+
fs15.writeFileSync(unitPath, unit);
|
|
47513
48813
|
println(`\u5DF2\u5199\u5165 ${unitPath}`);
|
|
47514
48814
|
println(`\u542F\u7528\uFF1Asystemctl --user daemon-reload && systemctl --user enable --now ${name}`);
|
|
47515
48815
|
println(`\u5F00\u673A\u81EA\u542F\uFF08\u542B\u672A\u767B\u5F55\uFF09\uFF1Aloginctl enable-linger $USER`);
|
|
@@ -47526,6 +48826,7 @@ async function runCli(argv, println = console.log) {
|
|
|
47526
48826
|
...flags.has("max-produce-per-agent") ? { maxConcurrentProducePerActor: Number(flags.get("max-produce-per-agent")) } : {},
|
|
47527
48827
|
...flags.has("model") ? { model: flags.get("model") } : {},
|
|
47528
48828
|
allowSimpleTokens: flags.get("allow-simple-tokens") === "true",
|
|
48829
|
+
...flags.get("public-url") ?? process.env["OASIS_PUBLIC_URL"] ? { publicUrl: flags.get("public-url") ?? process.env["OASIS_PUBLIC_URL"] } : {},
|
|
47529
48830
|
...flags.get("gateway-url") ?? process.env["OASIS_GATEWAY_URL"] ? { gatewayUrl: flags.get("gateway-url") ?? process.env["OASIS_GATEWAY_URL"] } : {}
|
|
47530
48831
|
});
|
|
47531
48832
|
const port2 = new URL(handle.baseUrl).port;
|
|
@@ -47538,12 +48839,12 @@ async function runCli(argv, println = console.log) {
|
|
|
47538
48839
|
}
|
|
47539
48840
|
const base = flags.get("server") ?? process.env["OASIS_SERVER"] ?? "http://127.0.0.1:7320";
|
|
47540
48841
|
const tokenFile = path12.join(dir, "operator-token");
|
|
47541
|
-
const token = flags.get("token") ?? process.env["OASIS_TOKEN"] ?? (
|
|
48842
|
+
const token = flags.get("token") ?? process.env["OASIS_TOKEN"] ?? (fs15.existsSync(tokenFile) ? fs15.readFileSync(tokenFile, "utf8").trim() : void 0);
|
|
47542
48843
|
if (!token) {
|
|
47543
48844
|
throw new Error(`\u65E0 token\uFF1A\u5148 oasis serve\uFF08\u4F1A\u751F\u6210 ${tokenFile}\uFF09\uFF0C\u6216\u663E\u5F0F --token / $OASIS_TOKEN`);
|
|
47544
48845
|
}
|
|
47545
48846
|
const api = makeClient(base, token);
|
|
47546
|
-
const readFileArg = (file) =>
|
|
48847
|
+
const readFileArg = (file) => fs15.readFileSync(file, "utf8");
|
|
47547
48848
|
const parseJsonFlag = (value, name) => {
|
|
47548
48849
|
const parsed = JSON.parse(value);
|
|
47549
48850
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -47561,11 +48862,11 @@ async function runCli(argv, println = console.log) {
|
|
|
47561
48862
|
const readDirFiles = (root) => {
|
|
47562
48863
|
const out = [];
|
|
47563
48864
|
const walk = (abs, rel) => {
|
|
47564
|
-
for (const name of
|
|
48865
|
+
for (const name of fs15.readdirSync(abs)) {
|
|
47565
48866
|
const childAbs = path12.join(abs, name);
|
|
47566
48867
|
const childRel = rel ? `${rel}/${name}` : name;
|
|
47567
|
-
if (
|
|
47568
|
-
else out.push({ path: childRel, contentBase64:
|
|
48868
|
+
if (fs15.statSync(childAbs).isDirectory()) walk(childAbs, childRel);
|
|
48869
|
+
else out.push({ path: childRel, contentBase64: fs15.readFileSync(childAbs).toString("base64") });
|
|
47569
48870
|
}
|
|
47570
48871
|
};
|
|
47571
48872
|
walk(root, "");
|
|
@@ -47608,9 +48909,10 @@ async function runCli(argv, println = console.log) {
|
|
|
47608
48909
|
};
|
|
47609
48910
|
case "spawn": {
|
|
47610
48911
|
const fields = fieldsFromFlags(SPAWN_OWN_FLAGS);
|
|
48912
|
+
const spawnId = positional[0];
|
|
47611
48913
|
return {
|
|
47612
48914
|
action: "spawn",
|
|
47613
|
-
|
|
48915
|
+
...spawnId ? { id: spawnId } : {},
|
|
47614
48916
|
type: need(flags, "type"),
|
|
47615
48917
|
...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
|
|
47616
48918
|
...(flags.get("brief") ?? flags.get("description")) !== void 0 ? { description: flags.get("brief") ?? flags.get("description") } : {},
|
|
@@ -47664,7 +48966,12 @@ async function runCli(argv, println = console.log) {
|
|
|
47664
48966
|
};
|
|
47665
48967
|
if (process.env["OASIS_STAGE"] === "1" && STAGE_COMMANDS.has(command)) {
|
|
47666
48968
|
const op = buildStageOp(command);
|
|
47667
|
-
const
|
|
48969
|
+
const editWs = flags.get("workspace") ?? process.env["OASIS_WORKSPACE"];
|
|
48970
|
+
const r = await api.post("/api/stage", { op, ...editWs ? { workspace: editWs } : {} });
|
|
48971
|
+
if (r.draft) {
|
|
48972
|
+
println(`\u5DF2\u6539\u8349\u6848\uFF1A${describeStageOp(op)}\uFF08\u5DE5\u5355 ${r.workspace}\uFF0C\u73B0 ${r.nodes} \u4E2A\u8282\u70B9\uFF1B\u4EBA\u5728\u7F16\u8F91\u5668\u786E\u8BA4\u5373\u521B\u5EFA\uFF09`);
|
|
48973
|
+
return;
|
|
48974
|
+
}
|
|
47668
48975
|
let msg = `\u5DF2\u6682\u5B58\uFF1A${describeStageOp(op)}\uFF08\u7F13\u51B2 ${r.count} \u9879\uFF1B\u6539\u5B8C \`oasis apply --reason "\u2026"\` \u63D0\u4EA4\u3001\`oasis staged\` \u770B\u5168\u90E8\uFF09`;
|
|
47669
48976
|
if (r.cascade && r.cascade.length > 0)
|
|
47670
48977
|
msg += `
|
|
@@ -47674,29 +48981,71 @@ async function runCli(argv, println = console.log) {
|
|
|
47674
48981
|
}
|
|
47675
48982
|
switch (command) {
|
|
47676
48983
|
case "create-workorder": {
|
|
47677
|
-
const planRaw = flags.get("plan-file") !== void 0 ?
|
|
48984
|
+
const planRaw = flags.get("plan-file") !== void 0 ? fs15.readFileSync(flags.get("plan-file"), "utf8") : flags.get("plan");
|
|
47678
48985
|
const acceptanceFlag = flags.get("acceptance");
|
|
47679
48986
|
const args = {
|
|
47680
48987
|
createdVia: "seed",
|
|
47681
48988
|
stage: true,
|
|
47682
48989
|
type: need(flags, "type"),
|
|
47683
|
-
workspace
|
|
48990
|
+
// workspace 可选:不给则服务端自动生成唯一工单 id(见 build-workorder skill:建单不必自带 ws)。
|
|
48991
|
+
...flags.get("workspace") !== void 0 ? { workspace: flags.get("workspace") } : {},
|
|
47684
48992
|
title: need(flags, "title"),
|
|
47685
48993
|
...flags.get("brief") !== void 0 ? { brief: flags.get("brief") } : {},
|
|
47686
48994
|
...flags.get("goal") !== void 0 ? { goal: flags.get("goal") } : {},
|
|
47687
48995
|
...acceptanceFlag !== void 0 ? { acceptance_criteria: parseStringArrayFlag(acceptanceFlag, "acceptance") } : {},
|
|
47688
48996
|
...planRaw !== void 0 ? { plan: parseJsonFlag(planRaw, "plan") } : {},
|
|
47689
48997
|
...flags.get("project") !== void 0 ? { project_id: flags.get("project") } : {},
|
|
47690
|
-
...flags.get("dispatch") === "false" ? { dispatch: false } : {}
|
|
48998
|
+
...flags.get("dispatch") === "false" ? { dispatch: false } : {},
|
|
48999
|
+
...flags.get("allow-missing-coordinator") === "true" ? { allowMissingCoordinator: true } : {}
|
|
47691
49000
|
};
|
|
47692
49001
|
const res = await api.cmd("spawn", args);
|
|
47693
49002
|
println(res.message);
|
|
47694
49003
|
const data = res.data;
|
|
47695
49004
|
if (data?.draftId) {
|
|
47696
|
-
println(`draftId=${data.draftId} workspace=${data.workspace} \u8282\u70B9=${data.spawned?.length ?? 0} \u89C4\u5212=${data.planning?.status ?? "?"}`);
|
|
49005
|
+
println(`draftId=${data.draftId} workspace=${data.workspace} \u72B6\u6001=draft\uFF08\u8349\u6848\uFF0C\u5F85\u4EBA\u786E\u8BA4\uFF09 \u8282\u70B9=${data.spawned?.length ?? 0} \u89C4\u5212=${data.planning?.status ?? "?"}`);
|
|
49006
|
+
for (const n of data.spawned ?? []) println(` ${n.id} (${n.type})`);
|
|
47697
49007
|
}
|
|
47698
49008
|
break;
|
|
47699
49009
|
}
|
|
49010
|
+
case "workorders": {
|
|
49011
|
+
const sessionId = flags.get("session");
|
|
49012
|
+
if (sessionId) {
|
|
49013
|
+
const res = await api.request(
|
|
49014
|
+
"GET",
|
|
49015
|
+
`/api/chat-sessions/${encodeURIComponent(sessionId)}/work-orders`
|
|
49016
|
+
);
|
|
49017
|
+
println(`\u4F1A\u8BDD ${sessionId} \u7684\u5DE5\u5355\uFF08${res.items.length}\uFF09\uFF1A`);
|
|
49018
|
+
for (const w2 of res.items) {
|
|
49019
|
+
const p2 = w2.progress ? ` [${w2.progress.concluded}/${w2.progress.total}]` : "";
|
|
49020
|
+
println(` ${w2.id} ${w2.title} [${w2.stage}]${p2}`);
|
|
49021
|
+
}
|
|
49022
|
+
} else {
|
|
49023
|
+
const res = await api.request("GET", "/api/workorders");
|
|
49024
|
+
const q = flags.get("search")?.trim().toLowerCase();
|
|
49025
|
+
const items = q ? res.items.filter((w2) => `${w2.id} ${w2.title} ${w2.goal ?? ""} ${w2.description ?? ""}`.toLowerCase().includes(q)) : res.items;
|
|
49026
|
+
println(`\u5DE5\u5355(${items.length}${q ? ` / \u641C\u300C${q}\u300D` : ""})\u2014\u2014\u8BE6\u60C5/\u6539\u56FE\u524D\u5148 \`oasis workorder <id>\`:`);
|
|
49027
|
+
for (const w2 of items) {
|
|
49028
|
+
println(`- ${w2.id} ${w2.title}${w2.dispatchPaused ? " [\u5F85\u6D3E\u53D1]" : ""}`);
|
|
49029
|
+
println(` \u9636\u6BB5=${w2.stage} \u8FDB\u5EA6=${w2.progress.concluded}/${w2.progress.total} \u8282\u70B9=${w2.artifactCount} owner=${w2.owner.name ?? w2.owner.id}${w2.goal ? ` \u76EE\u6807: ${w2.goal}` : ""}`);
|
|
49030
|
+
}
|
|
49031
|
+
if (items.length === 0) println("\uFF08\u65E0\u5339\u914D\u5DE5\u5355\uFF09");
|
|
49032
|
+
}
|
|
49033
|
+
break;
|
|
49034
|
+
}
|
|
49035
|
+
case "artifacts": {
|
|
49036
|
+
const workOrderId = positional[0];
|
|
49037
|
+
if (!workOrderId) {
|
|
49038
|
+
println("\u7528\u6CD5: oasis artifacts <workOrderId>");
|
|
49039
|
+
break;
|
|
49040
|
+
}
|
|
49041
|
+
const res = await api.request(
|
|
49042
|
+
"GET",
|
|
49043
|
+
`/api/workorders/${encodeURIComponent(workOrderId)}`
|
|
49044
|
+
);
|
|
49045
|
+
println(`\u5DE5\u5355 ${res.summary.title} \u7684\u4EA7\u7269\uFF08${res.graph.nodes.length}\uFF09\uFF1A`);
|
|
49046
|
+
for (const n of res.graph.nodes) println(` ${n.id} [${n.type}] ${n.label} stage=${n.stage}`);
|
|
49047
|
+
break;
|
|
49048
|
+
}
|
|
47700
49049
|
case "projects": {
|
|
47701
49050
|
const res = await api.request("GET", "/api/projects");
|
|
47702
49051
|
const q = flags.get("search")?.trim().toLowerCase();
|
|
@@ -47707,6 +49056,29 @@ async function runCli(argv, println = console.log) {
|
|
|
47707
49056
|
if (res.next_cursor) println("\uFF08\u8FD8\u6709\u66F4\u591A\u9879\u76EE\u672A\u5217\u5168\uFF0C\u7528 --search \u7F29\u5C0F\u8303\u56F4\uFF09");
|
|
47708
49057
|
break;
|
|
47709
49058
|
}
|
|
49059
|
+
case "workorder": {
|
|
49060
|
+
const woId = needPos(positional, 0, "oasis workorder <\u5DE5\u5355id\uFF0C\u5982 ws:wo-abc>");
|
|
49061
|
+
const d = await api.request("GET", `/api/workorders/${encodeURIComponent(woId)}`);
|
|
49062
|
+
const s2 = d.summary;
|
|
49063
|
+
const draftNote = s2.stage === "draft" ? "\uFF08\u8349\u6848\u2014\u2014\u5F85\u4EBA\u5728\u7F16\u8F91\u5668\u786E\u8BA4\u624D\u771F\u521B\u5EFA\uFF1B\u4E0B\u9762\u7684\u8282\u70B9 id \u5DF2\u53EF\u7528\u4E8E\u6539\u8349\u6848\uFF09" : "";
|
|
49064
|
+
println(`${s2.id} ${s2.title} \u72B6\u6001=${s2.stage}${draftNote}${s2.dispatchPaused ? " [\u672A\u6D3E\u53D1]" : ""}`);
|
|
49065
|
+
if (s2.goal) println(`\u76EE\u6807: ${s2.goal}`);
|
|
49066
|
+
println(`\u8FDB\u5EA6=${s2.progress.concluded}/${s2.progress.total} owner=${s2.owner.name ?? s2.owner.id}`);
|
|
49067
|
+
if (d.acceptanceCriteria && d.acceptanceCriteria.length > 0) {
|
|
49068
|
+
println("\u9A8C\u6536\u6807\u51C6:");
|
|
49069
|
+
for (const a of d.acceptanceCriteria) println(` - ${a}`);
|
|
49070
|
+
}
|
|
49071
|
+
println("\n\u8282\u70B9\uFF08\u6539\u56FE\u5F15\u7528\u7528\u8FD9\u4E9B id\uFF09:");
|
|
49072
|
+
for (const n of d.graph.nodes) {
|
|
49073
|
+
println(` ${n.id}`);
|
|
49074
|
+
println(` type=${n.type} \u9636\u6BB5=${n.stage}${n.blocked ? " [\u5361\u4F4F]" : ""} owner=${n.owner.name ?? n.owner.id}${n.label ? ` ${n.label}` : ""}`);
|
|
49075
|
+
}
|
|
49076
|
+
if (d.graph.edges.length > 0) {
|
|
49077
|
+
println("\n\u4F9D\u8D56\u8FB9\uFF08\u6D88\u8D39\u8005 \u2192 \u4E0A\u6E38\uFF09:");
|
|
49078
|
+
for (const e of d.graph.edges) println(` ${e.from} \u2192 ${e.to}${e.required ? "" : "\uFF08\u53EF\u9009\uFF09"}`);
|
|
49079
|
+
}
|
|
49080
|
+
break;
|
|
49081
|
+
}
|
|
47710
49082
|
case "project": {
|
|
47711
49083
|
const projSub = positional[0];
|
|
47712
49084
|
if (projSub && !["create", "docs", "progress", "run-state"].includes(projSub)) {
|
|
@@ -48460,18 +49832,18 @@ var parseFlags = (argv) => {
|
|
|
48460
49832
|
};
|
|
48461
49833
|
var readConfig = () => {
|
|
48462
49834
|
try {
|
|
48463
|
-
return JSON.parse(
|
|
49835
|
+
return JSON.parse(fs16.readFileSync(CONFIG_FILE, "utf8"));
|
|
48464
49836
|
} catch {
|
|
48465
49837
|
return null;
|
|
48466
49838
|
}
|
|
48467
49839
|
};
|
|
48468
49840
|
var saveConfig = (c) => {
|
|
48469
|
-
|
|
48470
|
-
|
|
49841
|
+
fs16.mkdirSync(OASIS_DIR, { recursive: true });
|
|
49842
|
+
fs16.writeFileSync(CONFIG_FILE, JSON.stringify(c), { mode: 384 });
|
|
48471
49843
|
};
|
|
48472
49844
|
var readPid = () => {
|
|
48473
49845
|
try {
|
|
48474
|
-
const p2 = parseInt(
|
|
49846
|
+
const p2 = parseInt(fs16.readFileSync(PID_FILE, "utf8").trim(), 10);
|
|
48475
49847
|
return isNaN(p2) ? null : p2;
|
|
48476
49848
|
} catch {
|
|
48477
49849
|
return null;
|
|
@@ -48487,10 +49859,10 @@ var isRunning = (pid) => {
|
|
|
48487
49859
|
};
|
|
48488
49860
|
var httpBase = (wsUrl) => wsUrl.replace(/^ws(s?):\/\//, "http$1://").replace(/\/node-gateway.*$/, "");
|
|
48489
49861
|
function installBinary() {
|
|
48490
|
-
|
|
48491
|
-
|
|
48492
|
-
|
|
48493
|
-
|
|
49862
|
+
fs16.mkdirSync(path13.dirname(BIN_FILE), { recursive: true });
|
|
49863
|
+
fs16.copyFileSync(process.argv[1], BIN_FILE);
|
|
49864
|
+
fs16.mkdirSync(path13.dirname(LOCAL_BIN), { recursive: true });
|
|
49865
|
+
fs16.writeFileSync(LOCAL_BIN, `#!/bin/sh
|
|
48494
49866
|
exec node "${BIN_FILE}" "$@"
|
|
48495
49867
|
`, { mode: 493 });
|
|
48496
49868
|
}
|
|
@@ -48505,8 +49877,8 @@ function setupAutostart() {
|
|
|
48505
49877
|
const daemonPath = [process.env["PATH"] ?? "", ...extraBins].filter(Boolean).join(":");
|
|
48506
49878
|
if (process.platform === "linux") {
|
|
48507
49879
|
const unitDir = path13.join(os8.homedir(), ".config", "systemd", "user");
|
|
48508
|
-
|
|
48509
|
-
|
|
49880
|
+
fs16.mkdirSync(unitDir, { recursive: true });
|
|
49881
|
+
fs16.writeFileSync(path13.join(unitDir, "oasis-node.service"), [
|
|
48510
49882
|
"[Unit]",
|
|
48511
49883
|
"Description=Oasis Node Daemon",
|
|
48512
49884
|
"After=network.target",
|
|
@@ -48529,8 +49901,8 @@ function setupAutostart() {
|
|
|
48529
49901
|
}
|
|
48530
49902
|
} else if (process.platform === "darwin") {
|
|
48531
49903
|
const plist = path13.join(os8.homedir(), "Library", "LaunchAgents", "com.oasis.node.plist");
|
|
48532
|
-
|
|
48533
|
-
|
|
49904
|
+
fs16.mkdirSync(path13.dirname(plist), { recursive: true });
|
|
49905
|
+
fs16.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?>
|
|
48534
49906
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
48535
49907
|
<plist version="1.0"><dict>
|
|
48536
49908
|
<key>Label</key><string>com.oasis.node</string>
|
|
@@ -48547,14 +49919,14 @@ function setupAutostart() {
|
|
|
48547
49919
|
}
|
|
48548
49920
|
}
|
|
48549
49921
|
function spawnDaemon() {
|
|
48550
|
-
const log3 =
|
|
49922
|
+
const log3 = fs16.openSync(LOG_FILE, "a");
|
|
48551
49923
|
const child = (0, import_node_child_process10.spawn)(process.execPath, [BIN_FILE, "--_daemon"], {
|
|
48552
49924
|
detached: true,
|
|
48553
49925
|
stdio: ["ignore", log3, log3]
|
|
48554
49926
|
});
|
|
48555
49927
|
child.unref();
|
|
48556
49928
|
const pid = child.pid ?? 0;
|
|
48557
|
-
|
|
49929
|
+
fs16.writeFileSync(PID_FILE, String(pid));
|
|
48558
49930
|
return pid;
|
|
48559
49931
|
}
|
|
48560
49932
|
function stopDaemon() {
|
|
@@ -48566,7 +49938,7 @@ function stopDaemon() {
|
|
|
48566
49938
|
}
|
|
48567
49939
|
}
|
|
48568
49940
|
try {
|
|
48569
|
-
|
|
49941
|
+
fs16.unlinkSync(PID_FILE);
|
|
48570
49942
|
} catch {
|
|
48571
49943
|
}
|
|
48572
49944
|
try {
|
|
@@ -48587,10 +49959,10 @@ void (async () => {
|
|
|
48587
49959
|
console.error("[oasis] no config \u2014 run `oasis start` first");
|
|
48588
49960
|
process.exit(1);
|
|
48589
49961
|
}
|
|
48590
|
-
|
|
49962
|
+
fs16.writeFileSync(PID_FILE, String(process.pid));
|
|
48591
49963
|
const cleanup = () => {
|
|
48592
49964
|
try {
|
|
48593
|
-
|
|
49965
|
+
fs16.unlinkSync(PID_FILE);
|
|
48594
49966
|
} catch {
|
|
48595
49967
|
}
|
|
48596
49968
|
};
|