oasis_test 0.1.52 → 0.1.54
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 +1389 -890
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -46,6 +46,7 @@ function emptyReadModel() {
|
|
|
46
46
|
annotations: /* @__PURE__ */ new Map(),
|
|
47
47
|
annotationSeq: /* @__PURE__ */ new Map(),
|
|
48
48
|
seals: /* @__PURE__ */ new Map(),
|
|
49
|
+
holds: /* @__PURE__ */ new Map(),
|
|
49
50
|
lastOpAt: /* @__PURE__ */ new Map(),
|
|
50
51
|
pinMeta: /* @__PURE__ */ new Map(),
|
|
51
52
|
gaps: /* @__PURE__ */ new Map(),
|
|
@@ -316,13 +317,28 @@ function fold(model, op) {
|
|
|
316
317
|
gap.resolved = true;
|
|
317
318
|
gap.resolvedAtSeq = op.seq;
|
|
318
319
|
if (p2.note !== void 0) gap.note = p2.note;
|
|
319
|
-
for (const e of model.escalations.get(op.artifactId) ?? [])
|
|
320
|
+
for (const e of model.escalations.get(op.artifactId) ?? []) {
|
|
321
|
+
if (e.resolved) continue;
|
|
322
|
+
if (e.gapId !== void 0) {
|
|
323
|
+
if (e.gapId === p2.gapId) e.resolved = true;
|
|
324
|
+
} else if (e.part === gap.part) {
|
|
325
|
+
e.resolved = true;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
320
328
|
break;
|
|
321
329
|
}
|
|
322
330
|
case "escalate": {
|
|
323
331
|
const p2 = op.payload;
|
|
324
332
|
const list = model.escalations.get(op.artifactId) ?? [];
|
|
325
|
-
list.push({
|
|
333
|
+
list.push({
|
|
334
|
+
part: p2.part ?? null,
|
|
335
|
+
reason: p2.reason,
|
|
336
|
+
by: op.actor,
|
|
337
|
+
...op.hand !== void 0 ? { hand: op.hand } : {},
|
|
338
|
+
at: op.timestamp,
|
|
339
|
+
resolved: false,
|
|
340
|
+
...p2.gapId !== void 0 ? { gapId: p2.gapId } : {}
|
|
341
|
+
});
|
|
326
342
|
model.escalations.set(op.artifactId, list);
|
|
327
343
|
break;
|
|
328
344
|
}
|
|
@@ -339,7 +355,7 @@ function fold(model, op) {
|
|
|
339
355
|
nonMonotonicOps: p2.nonMonotonicOps,
|
|
340
356
|
proposedBy: p2.proposedBy,
|
|
341
357
|
origin: p2.origin ?? null,
|
|
342
|
-
workspace: p2.plan.workspace ?? null,
|
|
358
|
+
workspace: p2.plan.workspace ?? model.artifacts.get(op.artifactId)?.workspace ?? null,
|
|
343
359
|
advisory: p2.advisory ?? [],
|
|
344
360
|
downstreamClosure: p2.downstreamClosure ?? [],
|
|
345
361
|
at: op.timestamp,
|
|
@@ -418,6 +434,21 @@ function fold(model, op) {
|
|
|
418
434
|
model.seals.delete(op.artifactId);
|
|
419
435
|
break;
|
|
420
436
|
}
|
|
437
|
+
case "hold": {
|
|
438
|
+
const p2 = op.payload;
|
|
439
|
+
if (!model.artifacts.has(op.artifactId)) throw new CorruptLogError(op, "hold on missing artifact");
|
|
440
|
+
model.holds.set(op.artifactId, {
|
|
441
|
+
source: p2.source,
|
|
442
|
+
by: op.actor,
|
|
443
|
+
at: op.timestamp,
|
|
444
|
+
...p2.reason !== void 0 ? { reason: p2.reason } : {}
|
|
445
|
+
});
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
case "release": {
|
|
449
|
+
model.holds.delete(op.artifactId);
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
421
452
|
case "unlink_input": {
|
|
422
453
|
const p2 = op.payload;
|
|
423
454
|
const artifact = model.artifacts.get(op.artifactId);
|
|
@@ -499,6 +530,12 @@ function lifecycleOf(model, id) {
|
|
|
499
530
|
const seal = model.seals.get(id);
|
|
500
531
|
return seal ? `sealed:${seal.reason}` : "active";
|
|
501
532
|
}
|
|
533
|
+
function isHeld(model, id) {
|
|
534
|
+
return model.holds.has(id);
|
|
535
|
+
}
|
|
536
|
+
function holdOf(model, id) {
|
|
537
|
+
return model.holds.get(id) ?? null;
|
|
538
|
+
}
|
|
502
539
|
function annotationsOf(model, id) {
|
|
503
540
|
const revisionIds = new Set(revisionsOf(model, id).map((r) => r.id));
|
|
504
541
|
return [...model.annotations.values()].filter(
|
|
@@ -565,7 +602,14 @@ function findGap(model, gapId) {
|
|
|
565
602
|
return null;
|
|
566
603
|
}
|
|
567
604
|
function unresolvedEscalationsOf(model, id) {
|
|
568
|
-
return (model.escalations.get(id) ?? []).filter((e) => !e.resolved).map((e) => ({
|
|
605
|
+
return (model.escalations.get(id) ?? []).filter((e) => !e.resolved).map((e) => ({
|
|
606
|
+
part: e.part,
|
|
607
|
+
reason: e.reason,
|
|
608
|
+
by: e.by,
|
|
609
|
+
...e.hand !== void 0 ? { hand: e.hand } : {},
|
|
610
|
+
at: e.at,
|
|
611
|
+
...e.gapId !== void 0 ? { gapId: e.gapId } : {}
|
|
612
|
+
}));
|
|
569
613
|
}
|
|
570
614
|
function listPendingReviews(model, workspace) {
|
|
571
615
|
const all = [...model.pendingReviews.values()];
|
|
@@ -606,6 +650,16 @@ function stuckDiagnosis(model, id, opts) {
|
|
|
606
650
|
if (!artifact || lifecycle !== "active") {
|
|
607
651
|
return { id, lifecycle, staleMs, causes, needsCoordinator: false };
|
|
608
652
|
}
|
|
653
|
+
if (isHeld(model, id)) {
|
|
654
|
+
const h = holdOf(model, id);
|
|
655
|
+
causes.push({
|
|
656
|
+
category: "held",
|
|
657
|
+
source: "oplog",
|
|
658
|
+
detail: `\u8282\u70B9\u5DF2\u6682\u505C\u6D3E\u53D1\uFF08${h?.source ?? "?"}${h?.reason ? `\uFF1A${h.reason}` : ""}\uFF09\u2014\u2014\u5F85\u89E3\u963B`,
|
|
659
|
+
handler: "human"
|
|
660
|
+
});
|
|
661
|
+
return { id, lifecycle, staleMs, causes, needsCoordinator: false };
|
|
662
|
+
}
|
|
609
663
|
if (artifact.owner.startsWith("pending:role:")) {
|
|
610
664
|
causes.push({
|
|
611
665
|
category: "owner-pending",
|
|
@@ -2004,9 +2058,9 @@ var init_kernel = __esm({
|
|
|
2004
2058
|
throw new KernelError(`revision not in queue: ${args.revisionId}\uFF08state=${target?.state ?? "missing"}\uFF09`);
|
|
2005
2059
|
}
|
|
2006
2060
|
if (isStale(this.model, head)) {
|
|
2007
|
-
const
|
|
2061
|
+
const path19 = this.pathForStaleHead(artifact, queue);
|
|
2008
2062
|
throw new KernelError(
|
|
2009
|
-
`stale base: ${head.id} based on ${head.base ?? "\u2205"}, head is ${artifact.currentRev ?? "\u2205"} \u2014 ` + (
|
|
2063
|
+
`stale base: ${head.id} based on ${head.base ?? "\u2205"}, head is ${artifact.currentRev ?? "\u2205"} \u2014 ` + (path19 === "rebase" ? `\u9700\u8981 rebase\uFF1A\u4F5C\u8005\u5728\u65B0 head \u4E0A\u91CD\u5199\u540E propose --rebase-of ${head.id}\uFF08\xA75.5\uFF09` : `\u591A\u6761\u7ADE\u4E89\u4E14\u68C0\u67E5\u8D35 \u2192 group-commit\uFF1Aowner \u4E00\u6B21\u96C6\u6210\u6574\u6279\uFF08\xA75.5\uFF09`)
|
|
2010
2064
|
);
|
|
2011
2065
|
}
|
|
2012
2066
|
await this.landHead(args.artifactId, args.actor, head, args.hand);
|
|
@@ -2023,13 +2077,13 @@ var init_kernel = __esm({
|
|
|
2023
2077
|
const head = queue[0];
|
|
2024
2078
|
if (!head) return { landed };
|
|
2025
2079
|
if (isStale(this.model, head)) {
|
|
2026
|
-
const
|
|
2080
|
+
const path19 = this.pathForStaleHead(artifact, queue);
|
|
2027
2081
|
return {
|
|
2028
2082
|
landed,
|
|
2029
2083
|
blockedHead: {
|
|
2030
2084
|
revision: head,
|
|
2031
|
-
path:
|
|
2032
|
-
hint:
|
|
2085
|
+
path: path19,
|
|
2086
|
+
hint: path19 === "rebase" ? `\u4F5C\u8005\u91CD\u5199\u540E propose --rebase-of ${head.id}` : `owner \u96C6\u6210\u6574\u6279\uFF08integrate\uFF09\uFF0CR*.parents = [head, \u2026batch]`
|
|
2033
2087
|
}
|
|
2034
2088
|
};
|
|
2035
2089
|
}
|
|
@@ -2454,19 +2508,28 @@ var init_kernel = __esm({
|
|
|
2454
2508
|
*/
|
|
2455
2509
|
async escalate(args) {
|
|
2456
2510
|
this.artifactOrThrow(args.artifactId);
|
|
2457
|
-
if (!isHumanActor(args.actor)) {
|
|
2511
|
+
if (!isHumanActor(args.actor) && args.actor !== SYSTEM_ACTOR) {
|
|
2458
2512
|
const ws = this.model.artifacts.get(args.artifactId)?.workspace;
|
|
2459
2513
|
const brief = [...this.model.artifacts.values()].find((a) => a.type === "brief" && a.workspace === ws);
|
|
2460
2514
|
const manager = brief?.fields?.["manager"];
|
|
2461
2515
|
if (manager !== args.actor) {
|
|
2462
2516
|
throw new KernelError(
|
|
2463
|
-
`escalate \u4EC5\u9650\u672C\u5DE5\u5355\u7BA1\u7406\u8005\uFF08brief.fields.manager\uFF09\
|
|
2517
|
+
`escalate \u4EC5\u9650\u672C\u5DE5\u5355\u7BA1\u7406\u8005\uFF08brief.fields.manager\uFF09\u3001\u4EBA\u7C7B\u6216\u7CFB\u7EDF\u8C03\u7528\u2014\u2014${args.actor} \u4E0D\u662F\u3002\u4F60\u82E5\u662F producer/owner \u5361\u4F4F\u4E86\uFF1A\u7528 \`oasis gap\` \u4E0A\u62A5\u9700\u8981\uFF0C\u7531\u7BA1\u7406\u8005\u5206\u8BCA\uFF08\u51B3\u7B56 0030 D4\uFF09\u3002`
|
|
2464
2518
|
);
|
|
2465
2519
|
}
|
|
2466
2520
|
}
|
|
2521
|
+
let part = args.part;
|
|
2522
|
+
let gapId = args.gapId;
|
|
2523
|
+
if (gapId) {
|
|
2524
|
+
const found = findGap(this.model, gapId);
|
|
2525
|
+
if (found && found.artifactId === args.artifactId) {
|
|
2526
|
+
if (part === void 0 && found.gap.part) part = found.gap.part;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2467
2529
|
await this.commit(args.artifactId, args.actor, "escalate", args.artifactId, {
|
|
2468
2530
|
reason: args.reason,
|
|
2469
|
-
...
|
|
2531
|
+
...part !== void 0 ? { part } : {},
|
|
2532
|
+
...gapId !== void 0 ? { gapId } : {}
|
|
2470
2533
|
}, void 0, args.hand);
|
|
2471
2534
|
}
|
|
2472
2535
|
/** 决策 0027 D1:人显式关闭本 artifact 上的 escalate(off-graph 解决兜底)。 */
|
|
@@ -2619,6 +2682,43 @@ var init_kernel = __esm({
|
|
|
2619
2682
|
const payload = { persistence: "persistent" };
|
|
2620
2683
|
await this.commit(args.artifactId, args.actor, "promote", args.artifactId, payload);
|
|
2621
2684
|
}
|
|
2685
|
+
/**
|
|
2686
|
+
* 节点级派发暂停(node-hold,本批):落一条 hold op——**只挡 agent 派发、不锁内容**(≠ seal/freeze,
|
|
2687
|
+
* 见 docs/notes/artifact-seal-lifecycle.md)。用于自动升级冻结(source:"sla")与人/管理者显式暂停。
|
|
2688
|
+
* 不校验 lifecycle:sealed 节点本就不派、held 于它无意义但无害;主用途是冻 active 节点。幂等——重复 hold 覆盖来源。
|
|
2689
|
+
*/
|
|
2690
|
+
async hold(args) {
|
|
2691
|
+
this.artifactOrThrow(args.artifactId);
|
|
2692
|
+
this.assertHoldAdmission(args.artifactId, args.actor);
|
|
2693
|
+
const payload = {
|
|
2694
|
+
source: args.source,
|
|
2695
|
+
...args.reason !== void 0 ? { reason: args.reason } : {}
|
|
2696
|
+
};
|
|
2697
|
+
await this.commit(args.artifactId, args.actor, "hold", args.artifactId, payload);
|
|
2698
|
+
}
|
|
2699
|
+
/** 解阻(node-hold):撤销 hold,节点恢复可派(投影下一拍重推当下欠的活)。空解(未 held)拦下,避免日志噪声。 */
|
|
2700
|
+
async release(args) {
|
|
2701
|
+
this.artifactOrThrow(args.artifactId);
|
|
2702
|
+
this.assertHoldAdmission(args.artifactId, args.actor);
|
|
2703
|
+
if (!isHeld(this.model, args.artifactId)) throw new KernelError(`not held: ${args.artifactId}`);
|
|
2704
|
+
const payload = { ...args.reason !== void 0 ? { reason: args.reason } : {} };
|
|
2705
|
+
await this.commit(args.artifactId, args.actor, "release", args.artifactId, payload);
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* hold/release 准入闸(红线 6:管控在服务端收紧):人类 / 本工单管理者 / `actor:system`(自动升级)可调;
|
|
2709
|
+
* 普通 producer/owner agent 不可——防 agent 把自己节点暂停了逃避派发(同 escalate 的机制闸精神,kernel.escalate)。
|
|
2710
|
+
*/
|
|
2711
|
+
assertHoldAdmission(artifactId, actor) {
|
|
2712
|
+
if (isHumanActor(actor)) return;
|
|
2713
|
+
if (actor === SYSTEM_ACTOR) return;
|
|
2714
|
+
const ws = this.model.artifacts.get(artifactId)?.workspace;
|
|
2715
|
+
const brief = [...this.model.artifacts.values()].find((a) => a.type === "brief" && a.workspace === ws);
|
|
2716
|
+
const manager = brief?.fields?.["manager"];
|
|
2717
|
+
if (manager === actor) return;
|
|
2718
|
+
throw new KernelError(
|
|
2719
|
+
`hold/release \u4EC5\u9650\u4EBA\u7C7B\u3001\u672C\u5DE5\u5355\u7BA1\u7406\u8005\uFF08brief.fields.manager\uFF09\u6216\u7CFB\u7EDF\u8C03\u7528\u2014\u2014${actor} \u4E0D\u662F\u3002`
|
|
2720
|
+
);
|
|
2721
|
+
}
|
|
2622
2722
|
// ── 治理 op(§11.3):force/override 单点豁免可直发;结构 op 以 Intervention 为载体 ──────
|
|
2623
2723
|
/** 治理解封:sealed → active(lifecycle = 最后一条 seal/reopen,§4.4)。frozen 解冻、cancelled 复活都走它 */
|
|
2624
2724
|
async reopen(args) {
|
|
@@ -4269,6 +4369,7 @@ var init_dispatcher = __esm({
|
|
|
4269
4369
|
const { kernel } = this.opts;
|
|
4270
4370
|
const jobWorkspace = kernel.model.artifacts.get(spec.artifactId)?.workspace;
|
|
4271
4371
|
if (jobWorkspace && await this.opts.resolveDispatchHold?.(jobWorkspace)) return 0;
|
|
4372
|
+
if (isHeld(kernel.model, spec.artifactId)) return 0;
|
|
4272
4373
|
this.assertSpawnAttemptCurrent(jobKey, attempt);
|
|
4273
4374
|
const seqNow = kernel.model.lastSeq.get(spec.artifactId) ?? 0;
|
|
4274
4375
|
const strike = this.strikes.get(jobKey);
|
|
@@ -4808,6 +4909,172 @@ var init_src2 = __esm({
|
|
|
4808
4909
|
}
|
|
4809
4910
|
});
|
|
4810
4911
|
|
|
4912
|
+
// ../server/src/git/ci-provider.ts
|
|
4913
|
+
function normalizeRemote(remote) {
|
|
4914
|
+
let s2 = remote.trim();
|
|
4915
|
+
s2 = s2.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, "");
|
|
4916
|
+
s2 = s2.replace(/^git@/, "").replace(/^([^/:]+):/, "$1/");
|
|
4917
|
+
s2 = s2.replace(/\/+$/, "").replace(/\.git$/i, "");
|
|
4918
|
+
const slash = s2.indexOf("/");
|
|
4919
|
+
if (slash > 0) s2 = s2.slice(0, slash).toLowerCase() + s2.slice(slash);
|
|
4920
|
+
return s2;
|
|
4921
|
+
}
|
|
4922
|
+
function isGitHubRemote(remote) {
|
|
4923
|
+
return !!remote && /(?:^|\/\/|@)github\.com[/:]/i.test(remote);
|
|
4924
|
+
}
|
|
4925
|
+
function ciBranchFor(artifactId, sha2) {
|
|
4926
|
+
const s2 = slug3(artifactId.replace(/^artifact:/, "")).slice(0, 48);
|
|
4927
|
+
return `oasis-ci/${s2}/${sha2.slice(0, 8)}`;
|
|
4928
|
+
}
|
|
4929
|
+
function mirrorDirFor(mirrorRoot, repo) {
|
|
4930
|
+
if (repo.repoDir) return repo.repoDir;
|
|
4931
|
+
const key = repo.remote ? `${slug3(repo.id)}-${crypto.createHash("sha1").update(normalizeRemote(repo.remote)).digest("hex").slice(0, 8)}` : slug3(repo.id);
|
|
4932
|
+
return `${mirrorRoot}/${key}`;
|
|
4933
|
+
}
|
|
4934
|
+
var import_node_child_process, crypto, fs, path, import_node_util, execFile, slug3, GhCiProvider;
|
|
4935
|
+
var init_ci_provider = __esm({
|
|
4936
|
+
"../server/src/git/ci-provider.ts"() {
|
|
4937
|
+
"use strict";
|
|
4938
|
+
import_node_child_process = require("node:child_process");
|
|
4939
|
+
crypto = __toESM(require("node:crypto"), 1);
|
|
4940
|
+
fs = __toESM(require("node:fs"), 1);
|
|
4941
|
+
path = __toESM(require("node:path"), 1);
|
|
4942
|
+
import_node_util = require("node:util");
|
|
4943
|
+
execFile = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
4944
|
+
slug3 = (id) => id.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
4945
|
+
GhCiProvider = class {
|
|
4946
|
+
constructor(opts) {
|
|
4947
|
+
this.opts = opts;
|
|
4948
|
+
}
|
|
4949
|
+
repoDir(repo) {
|
|
4950
|
+
return mirrorDirFor(this.opts.mirrorRoot, repo);
|
|
4951
|
+
}
|
|
4952
|
+
/** 缺则 clone(首次使用/进程重启后本地镜像不存在)——否则 git fetch 会"not a git repo"报错。同 GitRemoteIntegrator。 */
|
|
4953
|
+
ensureClone(repo) {
|
|
4954
|
+
const dir = this.repoDir(repo);
|
|
4955
|
+
if (fs.existsSync(path.join(dir, ".git"))) {
|
|
4956
|
+
if (repo.remote) {
|
|
4957
|
+
let origin = "";
|
|
4958
|
+
try {
|
|
4959
|
+
origin = (0, import_node_child_process.execFileSync)("git", ["remote", "get-url", "origin"], { cwd: dir, env: process.env }).toString().trim();
|
|
4960
|
+
} catch {
|
|
4961
|
+
}
|
|
4962
|
+
if (origin !== repo.remote) {
|
|
4963
|
+
(0, import_node_child_process.execFileSync)("git", ["remote", "set-url", "origin", repo.remote], { cwd: dir, env: process.env });
|
|
4964
|
+
this.opts.log?.(`[ci] ${repo.id} \u955C\u50CF origin \u7EA0\u504F\uFF1A${origin || "(\u65E0)"} \u2192 ${repo.remote}`);
|
|
4965
|
+
}
|
|
4966
|
+
}
|
|
4967
|
+
return;
|
|
4968
|
+
}
|
|
4969
|
+
if (!repo.remote) throw new Error(`repo ${repo.id} \u65E0 remote\uFF0C\u65E0\u6CD5 clone`);
|
|
4970
|
+
fs.mkdirSync(path.dirname(dir), { recursive: true });
|
|
4971
|
+
(0, import_node_child_process.execFileSync)("git", ["clone", "--quiet", repo.remote, dir], { env: process.env });
|
|
4972
|
+
this.opts.log?.(`[ci] cloned ${repo.id} \u2192 ${dir}`);
|
|
4973
|
+
}
|
|
4974
|
+
async gh(repo, args) {
|
|
4975
|
+
this.ensureClone(repo);
|
|
4976
|
+
const { stdout } = await execFile("gh", args, { cwd: this.repoDir(repo), env: process.env });
|
|
4977
|
+
return stdout.trim();
|
|
4978
|
+
}
|
|
4979
|
+
async git(repo, args) {
|
|
4980
|
+
this.ensureClone(repo);
|
|
4981
|
+
const { stdout } = await execFile("git", args, { cwd: this.repoDir(repo), env: process.env });
|
|
4982
|
+
return stdout.trim();
|
|
4983
|
+
}
|
|
4984
|
+
async ensurePr(ref, meta) {
|
|
4985
|
+
if (!ref.repo.remote) throw new Error(`repo ${ref.repo.id} \u65E0 remote\uFF0C\u65E0\u6CD5\u5F00 PR`);
|
|
4986
|
+
await this.git(ref.repo, ["fetch", "--quiet", "--prune", "origin"]);
|
|
4987
|
+
await this.git(ref.repo, ["push", "--quiet", "--force-with-lease", "origin", `${ref.sha}:refs/heads/${ref.branch}`]);
|
|
4988
|
+
const existing = await this.gh(ref.repo, ["pr", "list", "--head", ref.branch, "--base", ref.repo.develop, "--state", "open", "--json", "number,url"]);
|
|
4989
|
+
const list = JSON.parse(existing || "[]");
|
|
4990
|
+
if (list[0]) return list[0];
|
|
4991
|
+
const url = await this.gh(ref.repo, ["pr", "create", "--base", ref.repo.develop, "--head", ref.branch, "--title", meta.title, "--body", meta.body]);
|
|
4992
|
+
const created = JSON.parse(await this.gh(ref.repo, ["pr", "view", ref.branch, "--json", "number,url"]));
|
|
4993
|
+
this.opts.log?.(`[ci] opened PR ${created.url} (${ref.branch} \u2192 ${ref.repo.develop})`);
|
|
4994
|
+
return created.url ? created : { number: created.number, url };
|
|
4995
|
+
}
|
|
4996
|
+
async status(ref) {
|
|
4997
|
+
let raw;
|
|
4998
|
+
try {
|
|
4999
|
+
raw = await this.gh(ref.repo, ["pr", "view", ref.branch, "--json", "statusCheckRollup,createdAt"]);
|
|
5000
|
+
} catch (e) {
|
|
5001
|
+
return { state: "pending", summary: `\u8BFB PR \u72B6\u6001\u5931\u8D25\uFF08\u4E0B\u8F6E\u91CD\u8BD5\uFF09\uFF1A${String(e)}` };
|
|
5002
|
+
}
|
|
5003
|
+
const parsed = JSON.parse(raw || "{}");
|
|
5004
|
+
const rollup = parsed.statusCheckRollup ?? [];
|
|
5005
|
+
if (rollup.length === 0) {
|
|
5006
|
+
const grace = this.opts.noCheckGraceMs ?? 12e4;
|
|
5007
|
+
const ageMs = parsed.createdAt ? Date.now() - Date.parse(parsed.createdAt) : Infinity;
|
|
5008
|
+
if (ageMs < grace) return { state: "pending" };
|
|
5009
|
+
if (this.opts.failClosedWhenNoChecks) return { state: "failure", summary: "\u8BE5\u4ED3\u672A\u914D\u4EFB\u4F55 Actions check\uFF08fail-closed\uFF09\u2014\u2014\u8BF7\u8865 CI \u6216\u6539\u914D\u7F6E" };
|
|
5010
|
+
this.opts.log?.(`[ci] ${ref.repo.id} PR ${ref.branch} \u5F00\u4E86 ${Math.round(ageMs / 1e3)}s \u4ECD\u65E0 check\uFF0C\u5224\u65E0 CI\u3001\u95F8\u7A7A\u8FC7\uFF08\u544A\u8B66\uFF09`);
|
|
5011
|
+
return { state: "success", summary: "\u65E0 CI check\uFF0C\u95F8\u7A7A\u8FC7" };
|
|
5012
|
+
}
|
|
5013
|
+
const failed = [];
|
|
5014
|
+
let anyPending = false;
|
|
5015
|
+
for (const c of rollup) {
|
|
5016
|
+
const concl = (c.conclusion ?? "").toUpperCase();
|
|
5017
|
+
const st = (c.status ?? "").toUpperCase();
|
|
5018
|
+
if (st && st !== "COMPLETED") {
|
|
5019
|
+
anyPending = true;
|
|
5020
|
+
continue;
|
|
5021
|
+
}
|
|
5022
|
+
if (concl && !["SUCCESS", "NEUTRAL", "SKIPPED"].includes(concl)) failed.push(c);
|
|
5023
|
+
}
|
|
5024
|
+
if (failed.length > 0) {
|
|
5025
|
+
const blocks = [];
|
|
5026
|
+
for (const c of failed) {
|
|
5027
|
+
const name = c.name ?? c.context ?? "check";
|
|
5028
|
+
const concl = (c.conclusion ?? "").toUpperCase();
|
|
5029
|
+
const url = c.detailsUrl ?? c.targetUrl ?? "";
|
|
5030
|
+
let block = ` - ${name} \u2192 ${concl}${url ? `
|
|
5031
|
+
${url}` : ""}`;
|
|
5032
|
+
const tail = await this.failedLogTail(ref.repo, url);
|
|
5033
|
+
if (tail) block += `
|
|
5034
|
+
\u5931\u8D25\u65E5\u5FD7\uFF08\u672B\u5C3E\uFF09\uFF1A
|
|
5035
|
+
${tail.split("\n").map((l) => " " + l).join("\n")}`;
|
|
5036
|
+
blocks.push(block);
|
|
5037
|
+
}
|
|
5038
|
+
return { state: "failure", summary: `CI \u672A\u8FC7\uFF08${failed.length} \u4E2A check\uFF09\uFF1A
|
|
5039
|
+
${blocks.join("\n")}` };
|
|
5040
|
+
}
|
|
5041
|
+
if (anyPending) return { state: "pending" };
|
|
5042
|
+
return { state: "success", summary: `CI \u901A\u8FC7\uFF08${rollup.length} \u4E2A check\uFF09` };
|
|
5043
|
+
}
|
|
5044
|
+
/** best-effort:从 check 的 detailsUrl 提 Actions run id,取失败步骤日志末尾(截断)。取不到返回空。 */
|
|
5045
|
+
async failedLogTail(repo, detailsUrl) {
|
|
5046
|
+
const m2 = /\/runs\/(\d+)/.exec(detailsUrl);
|
|
5047
|
+
if (!m2) return "";
|
|
5048
|
+
try {
|
|
5049
|
+
const log3 = await this.gh(repo, ["run", "view", m2[1], "--log-failed"]);
|
|
5050
|
+
return log3.split("\n").filter(Boolean).slice(-40).join("\n").slice(-2e3);
|
|
5051
|
+
} catch {
|
|
5052
|
+
return "";
|
|
5053
|
+
}
|
|
5054
|
+
}
|
|
5055
|
+
async merge(ref) {
|
|
5056
|
+
try {
|
|
5057
|
+
await this.gh(ref.repo, ["pr", "merge", ref.branch, "--merge", "--delete-branch"]);
|
|
5058
|
+
this.opts.log?.(`[ci] merged PR ${ref.branch} \u2192 ${ref.repo.develop}`);
|
|
5059
|
+
return { merged: true };
|
|
5060
|
+
} catch (e) {
|
|
5061
|
+
return { merged: false, reason: String(e) };
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
async isMerged(ref) {
|
|
5065
|
+
if (!ref.repo.remote) return false;
|
|
5066
|
+
try {
|
|
5067
|
+
await this.git(ref.repo, ["fetch", "--quiet", "origin", ref.repo.develop]);
|
|
5068
|
+
await this.git(ref.repo, ["merge-base", "--is-ancestor", ref.sha, `origin/${ref.repo.develop}`]);
|
|
5069
|
+
return true;
|
|
5070
|
+
} catch {
|
|
5071
|
+
return false;
|
|
5072
|
+
}
|
|
5073
|
+
}
|
|
5074
|
+
};
|
|
5075
|
+
}
|
|
5076
|
+
});
|
|
5077
|
+
|
|
4811
5078
|
// ../server/src/git/collect.ts
|
|
4812
5079
|
function collectUmbrellaCommits(repos, featBranch, env) {
|
|
4813
5080
|
const out = {};
|
|
@@ -4815,7 +5082,7 @@ function collectUmbrellaCommits(repos, featBranch, env) {
|
|
|
4815
5082
|
if (!r.remote) continue;
|
|
4816
5083
|
let ls;
|
|
4817
5084
|
try {
|
|
4818
|
-
ls = (0,
|
|
5085
|
+
ls = (0, import_node_child_process2.execFileSync)("git", ["ls-remote", r.remote, `refs/heads/${featBranch}`, `refs/heads/${r.develop}`], {
|
|
4819
5086
|
encoding: "utf8",
|
|
4820
5087
|
...env ? { env } : {}
|
|
4821
5088
|
});
|
|
@@ -4830,12 +5097,16 @@ function collectUmbrellaCommits(repos, featBranch, env) {
|
|
|
4830
5097
|
}
|
|
4831
5098
|
const featSha = refs.get(`refs/heads/${featBranch}`);
|
|
4832
5099
|
const devSha = refs.get(`refs/heads/${r.develop}`);
|
|
4833
|
-
if (featSha && featSha !== devSha) out[r.id] = featSha;
|
|
5100
|
+
if (featSha && featSha !== devSha) out[r.remote ? normalizeRemote(r.remote) : r.id] = featSha;
|
|
4834
5101
|
}
|
|
4835
5102
|
return out;
|
|
4836
5103
|
}
|
|
4837
5104
|
function parseUmbrellaCommits(contentRef, repos) {
|
|
4838
|
-
const
|
|
5105
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
5106
|
+
for (const r of repos) {
|
|
5107
|
+
byKey.set(r.id, r);
|
|
5108
|
+
if (r.remote) byKey.set(normalizeRemote(r.remote), r);
|
|
5109
|
+
}
|
|
4839
5110
|
if (contentRef.trim().startsWith("{")) {
|
|
4840
5111
|
let manifest = null;
|
|
4841
5112
|
try {
|
|
@@ -4845,8 +5116,8 @@ function parseUmbrellaCommits(contentRef, repos) {
|
|
|
4845
5116
|
}
|
|
4846
5117
|
if (manifest) {
|
|
4847
5118
|
const out = [];
|
|
4848
|
-
for (const [
|
|
4849
|
-
const repo =
|
|
5119
|
+
for (const [key, sha2] of Object.entries(manifest)) {
|
|
5120
|
+
const repo = byKey.get(key);
|
|
4850
5121
|
if (repo && typeof sha2 === "string") out.push({ repo, sha: sha2 });
|
|
4851
5122
|
}
|
|
4852
5123
|
return out;
|
|
@@ -4854,11 +5125,12 @@ function parseUmbrellaCommits(contentRef, repos) {
|
|
|
4854
5125
|
}
|
|
4855
5126
|
return repos[0] ? [{ repo: repos[0], sha: contentRef }] : [];
|
|
4856
5127
|
}
|
|
4857
|
-
var
|
|
5128
|
+
var import_node_child_process2;
|
|
4858
5129
|
var init_collect = __esm({
|
|
4859
5130
|
"../server/src/git/collect.ts"() {
|
|
4860
5131
|
"use strict";
|
|
4861
|
-
|
|
5132
|
+
import_node_child_process2 = require("node:child_process");
|
|
5133
|
+
init_ci_provider();
|
|
4862
5134
|
}
|
|
4863
5135
|
});
|
|
4864
5136
|
|
|
@@ -4869,17 +5141,16 @@ function autoMergeWorkIntoFeat(args) {
|
|
|
4869
5141
|
for (const repo of args.repos) {
|
|
4870
5142
|
if (!repo.remote) continue;
|
|
4871
5143
|
const remote = repo.remote;
|
|
4872
|
-
const
|
|
4873
|
-
const
|
|
4874
|
-
const git = (a) => (0, import_node_child_process2.execFileSync)("git", ["-C", dir, ...a], { encoding: "utf8", env, maxBuffer: 256 * 1024 * 1024 }).trim();
|
|
5144
|
+
const dir = mirrorDirFor(args.mirrorRoot, repo);
|
|
5145
|
+
const git = (a) => (0, import_node_child_process3.execFileSync)("git", ["-C", dir, ...a], { encoding: "utf8", env, maxBuffer: 256 * 1024 * 1024 }).trim();
|
|
4875
5146
|
const clone2 = () => {
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
(0,
|
|
5147
|
+
fs2.rmSync(dir, { recursive: true, force: true });
|
|
5148
|
+
fs2.mkdirSync(path2.dirname(dir), { recursive: true });
|
|
5149
|
+
(0, import_node_child_process3.execFileSync)("git", ["clone", "--quiet", remote, dir], { env, maxBuffer: 256 * 1024 * 1024 });
|
|
4879
5150
|
git(["config", "user.email", "oasis@local"]);
|
|
4880
5151
|
git(["config", "user.name", "oasis"]);
|
|
4881
5152
|
};
|
|
4882
|
-
if (!
|
|
5153
|
+
if (!fs2.existsSync(path2.join(dir, ".git"))) {
|
|
4883
5154
|
clone2();
|
|
4884
5155
|
} else {
|
|
4885
5156
|
let originOk = false;
|
|
@@ -4934,18 +5205,18 @@ function autoMergeWorkIntoFeat(args) {
|
|
|
4934
5205
|
staged.push(dir);
|
|
4935
5206
|
}
|
|
4936
5207
|
for (const dir of staged) {
|
|
4937
|
-
(0,
|
|
5208
|
+
(0, import_node_child_process3.execFileSync)("git", ["-C", dir, "push", "--quiet", "origin", args.featBranch], { env, maxBuffer: 256 * 1024 * 1024 });
|
|
4938
5209
|
}
|
|
4939
5210
|
return { ok: true };
|
|
4940
5211
|
}
|
|
4941
|
-
var
|
|
5212
|
+
var import_node_child_process3, fs2, path2;
|
|
4942
5213
|
var init_auto_merge = __esm({
|
|
4943
5214
|
"../server/src/git/auto-merge.ts"() {
|
|
4944
5215
|
"use strict";
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
5216
|
+
import_node_child_process3 = require("node:child_process");
|
|
5217
|
+
fs2 = __toESM(require("node:fs"), 1);
|
|
5218
|
+
path2 = __toESM(require("node:path"), 1);
|
|
5219
|
+
init_ci_provider();
|
|
4949
5220
|
}
|
|
4950
5221
|
});
|
|
4951
5222
|
|
|
@@ -5361,8 +5632,8 @@ var init_parseUtil = __esm({
|
|
|
5361
5632
|
init_errors2();
|
|
5362
5633
|
init_en();
|
|
5363
5634
|
makeIssue = (params) => {
|
|
5364
|
-
const { data, path:
|
|
5365
|
-
const fullPath = [...
|
|
5635
|
+
const { data, path: path19, errorMaps, issueData } = params;
|
|
5636
|
+
const fullPath = [...path19, ...issueData.path || []];
|
|
5366
5637
|
const fullIssue = {
|
|
5367
5638
|
...issueData,
|
|
5368
5639
|
path: fullPath
|
|
@@ -5670,11 +5941,11 @@ var init_types = __esm({
|
|
|
5670
5941
|
init_parseUtil();
|
|
5671
5942
|
init_util();
|
|
5672
5943
|
ParseInputLazyPath = class {
|
|
5673
|
-
constructor(parent, value,
|
|
5944
|
+
constructor(parent, value, path19, key) {
|
|
5674
5945
|
this._cachedPath = [];
|
|
5675
5946
|
this.parent = parent;
|
|
5676
5947
|
this.data = value;
|
|
5677
|
-
this._path =
|
|
5948
|
+
this._path = path19;
|
|
5678
5949
|
this._key = key;
|
|
5679
5950
|
}
|
|
5680
5951
|
get path() {
|
|
@@ -9248,10 +9519,10 @@ function assignProp(target, prop, value) {
|
|
|
9248
9519
|
configurable: true
|
|
9249
9520
|
});
|
|
9250
9521
|
}
|
|
9251
|
-
function getElementAtPath(obj,
|
|
9252
|
-
if (!
|
|
9522
|
+
function getElementAtPath(obj, path19) {
|
|
9523
|
+
if (!path19)
|
|
9253
9524
|
return obj;
|
|
9254
|
-
return
|
|
9525
|
+
return path19.reduce((acc, key) => acc?.[key], obj);
|
|
9255
9526
|
}
|
|
9256
9527
|
function promiseAllObject(promisesObj) {
|
|
9257
9528
|
const keys = Object.keys(promisesObj);
|
|
@@ -9500,11 +9771,11 @@ function aborted(x2, startIndex = 0) {
|
|
|
9500
9771
|
}
|
|
9501
9772
|
return false;
|
|
9502
9773
|
}
|
|
9503
|
-
function prefixIssues(
|
|
9774
|
+
function prefixIssues(path19, issues) {
|
|
9504
9775
|
return issues.map((iss) => {
|
|
9505
9776
|
var _a;
|
|
9506
9777
|
(_a = iss).path ?? (_a.path = []);
|
|
9507
|
-
iss.path.unshift(
|
|
9778
|
+
iss.path.unshift(path19);
|
|
9508
9779
|
return iss;
|
|
9509
9780
|
});
|
|
9510
9781
|
}
|
|
@@ -18044,8 +18315,8 @@ var require_utils = __commonJS({
|
|
|
18044
18315
|
}
|
|
18045
18316
|
return ind;
|
|
18046
18317
|
}
|
|
18047
|
-
function removeDotSegments(
|
|
18048
|
-
let input =
|
|
18318
|
+
function removeDotSegments(path19) {
|
|
18319
|
+
let input = path19;
|
|
18049
18320
|
const output = [];
|
|
18050
18321
|
let nextSlash = -1;
|
|
18051
18322
|
let len = 0;
|
|
@@ -18297,8 +18568,8 @@ var require_schemes = __commonJS({
|
|
|
18297
18568
|
wsComponent.secure = void 0;
|
|
18298
18569
|
}
|
|
18299
18570
|
if (wsComponent.resourceName) {
|
|
18300
|
-
const [
|
|
18301
|
-
wsComponent.path =
|
|
18571
|
+
const [path19, query] = wsComponent.resourceName.split("?");
|
|
18572
|
+
wsComponent.path = path19 && path19 !== "/" ? path19 : void 0;
|
|
18302
18573
|
wsComponent.query = query;
|
|
18303
18574
|
wsComponent.resourceName = void 0;
|
|
18304
18575
|
}
|
|
@@ -21811,23 +22082,23 @@ var init_router = __esm({
|
|
|
21811
22082
|
};
|
|
21812
22083
|
Router = class {
|
|
21813
22084
|
routes = [];
|
|
21814
|
-
on(method,
|
|
21815
|
-
this.routes.push({ method, segments:
|
|
22085
|
+
on(method, path19, handler) {
|
|
22086
|
+
this.routes.push({ method, segments: path19.split("/").filter(Boolean), handler });
|
|
21816
22087
|
}
|
|
21817
|
-
get(
|
|
21818
|
-
this.on("GET",
|
|
22088
|
+
get(path19, h) {
|
|
22089
|
+
this.on("GET", path19, h);
|
|
21819
22090
|
}
|
|
21820
|
-
post(
|
|
21821
|
-
this.on("POST",
|
|
22091
|
+
post(path19, h) {
|
|
22092
|
+
this.on("POST", path19, h);
|
|
21822
22093
|
}
|
|
21823
|
-
patch(
|
|
21824
|
-
this.on("PATCH",
|
|
22094
|
+
patch(path19, h) {
|
|
22095
|
+
this.on("PATCH", path19, h);
|
|
21825
22096
|
}
|
|
21826
|
-
put(
|
|
21827
|
-
this.on("PUT",
|
|
22097
|
+
put(path19, h) {
|
|
22098
|
+
this.on("PUT", path19, h);
|
|
21828
22099
|
}
|
|
21829
|
-
delete(
|
|
21830
|
-
this.on("DELETE",
|
|
22100
|
+
delete(path19, h) {
|
|
22101
|
+
this.on("DELETE", path19, h);
|
|
21831
22102
|
}
|
|
21832
22103
|
match(method, pathname) {
|
|
21833
22104
|
const parts = pathname.split("/").filter(Boolean);
|
|
@@ -22296,10 +22567,10 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
22296
22567
|
const schema = input.schema;
|
|
22297
22568
|
const schemaByName = new Map(schema.map((def) => [def.name, def]));
|
|
22298
22569
|
const displayTitle = input.title?.trim() || input.description;
|
|
22299
|
-
const
|
|
22300
|
-
const briefId = `artifact:brief:${
|
|
22570
|
+
const slug4 = artifactSlug(input.requestedArtifactId ?? input.workspace, displayTitle);
|
|
22571
|
+
const briefId = `artifact:brief:${slug4}`;
|
|
22301
22572
|
const primaryType = schemaByName.has(input.requestedType) && input.requestedType !== "brief" ? input.requestedType : "prd";
|
|
22302
|
-
const primaryId = input.requestedArtifactId && input.requestedType === primaryType ? input.requestedArtifactId : `artifact:${primaryType}:${
|
|
22573
|
+
const primaryId = input.requestedArtifactId && input.requestedType === primaryType ? input.requestedArtifactId : `artifact:${primaryType}:${slug4}`;
|
|
22303
22574
|
const issues = [...input.agentIssues ?? []];
|
|
22304
22575
|
const enforceStaffing = Boolean(input.registry);
|
|
22305
22576
|
const staffableTypes = new Set((await plannerArtifactTypes(schema, input.registry)).map((def) => def.name));
|
|
@@ -22378,7 +22649,7 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
22378
22649
|
const owner = await resolveRole(def.ownerRole, type);
|
|
22379
22650
|
const ownerActor = owner.actor ?? (type === primaryType ? input.actor : `pending:role:${def.ownerRole}`);
|
|
22380
22651
|
if (enforceStaffing && !owner.actor && type !== primaryType) continue;
|
|
22381
|
-
const id = type === primaryType ? primaryId : `artifact:${type}:${
|
|
22652
|
+
const id = type === primaryType ? primaryId : `artifact:${type}:${slug4}`;
|
|
22382
22653
|
availableNodes.push({ type, id, role: def.ownerRole, owner: ownerActor });
|
|
22383
22654
|
}
|
|
22384
22655
|
}
|
|
@@ -23859,11 +24130,11 @@ async function resolveProposeContent(kernel, blobs, args, artifactId, ctx) {
|
|
|
23859
24130
|
const ws = art?.workspace;
|
|
23860
24131
|
const repos = ws ? (await ctx.resolveCodeRepo(ws))?.repos : void 0;
|
|
23861
24132
|
if (repos?.length) {
|
|
23862
|
-
const
|
|
23863
|
-
const feat = `feat/${
|
|
24133
|
+
const slug4 = (id) => id.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
24134
|
+
const feat = `feat/${slug4(artifactId)}`;
|
|
23864
24135
|
const proposingPart = optStr(args, "part");
|
|
23865
24136
|
if (proposingPart !== void 0 && ctx.gitMirrorRoot) {
|
|
23866
|
-
const work = `feat/${
|
|
24137
|
+
const work = `feat/${slug4(artifactId)}__${slug4(proposingPart)}`;
|
|
23867
24138
|
const r = autoMergeWorkIntoFeat({
|
|
23868
24139
|
repos,
|
|
23869
24140
|
workBranch: work,
|
|
@@ -24384,7 +24655,8 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
24384
24655
|
artifactId: str(args, "artifactId"),
|
|
24385
24656
|
actor,
|
|
24386
24657
|
reason: str(args, "reason"),
|
|
24387
|
-
...optStr(args, "part") !== void 0 ? { part: optStr(args, "part") } : {}
|
|
24658
|
+
...optStr(args, "part") !== void 0 ? { part: optStr(args, "part") } : {},
|
|
24659
|
+
...optStr(args, "gapId") !== void 0 ? { gapId: optStr(args, "gapId") } : {}
|
|
24388
24660
|
});
|
|
24389
24661
|
return { message: '\u5DF2\u4E0A\u62A5\uFF08escalate\uFF09\uFF1A\u7CFB\u7EDF\u5C06\u5347\u7EA7\u5230\u8D1F\u8D23\u4EBA\u6536\u4EF6\u7BB1\u3002\u4F60\u53EF\u4EE5\u6536\u5DE5\u4E86\uFF1B\u4E4B\u540E\u53EF\u80FD\u5728\u5BF9\u8BDD\u91CC\u88AB ta \u95EE\u5230\u3002\u26A0\uFE0F \u82E5\u8FD9\u6B21\u4E0A\u62A5\u5173\u8054\u67D0\u6761 gap\uFF0C**\u4E0D\u8981\u5BF9\u5B83\u8C03 resolve-gap**\u2014\u2014\u90A3\u8868\u793A"\u9700\u6C42\u5DF2\u6EE1\u8DB3\u3001\u8BA9 owner \u590D\u5DE5"\uFF0C\u73B0\u5728\u8FD8\u6CA1\u6EE1\u8DB3\uFF1Bgap \u4FDD\u6301 open \u8282\u70B9\u624D\u4F1A\u7EE7\u7EED\u7B49\u5F85\u3002' };
|
|
24390
24662
|
}
|
|
@@ -24440,6 +24712,23 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
24440
24712
|
await kernel.reopen({ artifactId: str(args, "artifactId"), actor, note: optStr(args, "note") });
|
|
24441
24713
|
return { message: `reopened ${str(args, "artifactId")}\uFF08lifecycle \u2192 active\uFF09` };
|
|
24442
24714
|
}
|
|
24715
|
+
case "hold": {
|
|
24716
|
+
await kernel.hold({
|
|
24717
|
+
artifactId: str(args, "artifactId"),
|
|
24718
|
+
actor,
|
|
24719
|
+
source: optStr(args, "source") ?? "human",
|
|
24720
|
+
...optStr(args, "reason") !== void 0 ? { reason: optStr(args, "reason") } : {}
|
|
24721
|
+
});
|
|
24722
|
+
return { message: `\u8282\u70B9\u5DF2\u6682\u505C\u6D3E\u53D1\uFF08hold\uFF09\uFF1A${str(args, "artifactId")}\u3002\u6062\u590D\u7528 release\u3002` };
|
|
24723
|
+
}
|
|
24724
|
+
case "release": {
|
|
24725
|
+
await kernel.release({
|
|
24726
|
+
artifactId: str(args, "artifactId"),
|
|
24727
|
+
actor,
|
|
24728
|
+
...optStr(args, "reason") !== void 0 ? { reason: optStr(args, "reason") } : {}
|
|
24729
|
+
});
|
|
24730
|
+
return { message: `\u8282\u70B9\u5DF2\u89E3\u963B\uFF08release\uFF09\uFF1A${str(args, "artifactId")}\u2014\u2014\u6295\u5F71\u4E0B\u4E00\u62CD\u4F1A\u91CD\u63A8\u8BE5\u8282\u70B9\u5F53\u4E0B\u6B20\u7684\u6D3B\u3002` };
|
|
24731
|
+
}
|
|
24443
24732
|
case "forceConclude": {
|
|
24444
24733
|
await kernel.forceConclude({
|
|
24445
24734
|
artifactId: str(args, "artifactId"),
|
|
@@ -24582,7 +24871,7 @@ async function runView(kernel, oplog, blobs, name, artifactId, params, ciGateSta
|
|
|
24582
24871
|
}
|
|
24583
24872
|
case "content": {
|
|
24584
24873
|
const id = need2();
|
|
24585
|
-
const
|
|
24874
|
+
const path19 = params?.get("path") ?? null;
|
|
24586
24875
|
const artifact = model.artifacts.get(id);
|
|
24587
24876
|
if (!artifact) throw new Error(`artifact \u4E0D\u5B58\u5728: ${id}`);
|
|
24588
24877
|
const head = artifact.currentRev;
|
|
@@ -24594,11 +24883,11 @@ async function runView(kernel, oplog, blobs, name, artifactId, params, ciGateSta
|
|
|
24594
24883
|
const dec2 = new TextDecoder();
|
|
24595
24884
|
if (headRev.contentKind === "manifest") {
|
|
24596
24885
|
const entries = await readManifest(blobs, headRev.contentRef);
|
|
24597
|
-
if (
|
|
24598
|
-
const e = entries.find((x2) => x2.path ===
|
|
24599
|
-
if (!e) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${
|
|
24886
|
+
if (path19 !== null) {
|
|
24887
|
+
const e = entries.find((x2) => x2.path === path19);
|
|
24888
|
+
if (!e) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${path19}\uFF08\u672C\u4EA7\u7269\u6709\uFF1A${entries.map((x2) => x2.path).join("\u3001")}\uFF09`);
|
|
24600
24889
|
const isText = !e.bytes.includes(0);
|
|
24601
|
-
return { kind: "file", path:
|
|
24890
|
+
return { kind: "file", path: path19, bytes: e.bytes.length, text: isText ? dec2.decode(e.bytes) : null };
|
|
24602
24891
|
}
|
|
24603
24892
|
return {
|
|
24604
24893
|
kind: "manifest",
|
|
@@ -24870,7 +25159,9 @@ async function startOasisServer(opts) {
|
|
|
24870
25159
|
if (url.pathname === "/api/intervention/draft" && req.method === "POST") {
|
|
24871
25160
|
try {
|
|
24872
25161
|
const plan = JSON.parse(rawBody);
|
|
24873
|
-
const
|
|
25162
|
+
const chatSessionId = req.headers["x-chat-session-id"];
|
|
25163
|
+
const origin = typeof chatSessionId === "string" && chatSessionId ? chatSessionId : void 0;
|
|
25164
|
+
const { draftId, changeClass, nonMonotonicOps } = await engine.kernel.proposeIntervention(plan, actor, origin ? { origin } : void 0);
|
|
24874
25165
|
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
24875
25166
|
JSON.stringify({ draftId, changeClass, nonMonotonicOps })
|
|
24876
25167
|
);
|
|
@@ -25236,7 +25527,10 @@ async function startOasisServer(opts) {
|
|
|
25236
25527
|
try {
|
|
25237
25528
|
const viewName = url.searchParams.get("name") ?? "";
|
|
25238
25529
|
if (viewName === "intervention-drafts") {
|
|
25239
|
-
const
|
|
25530
|
+
const origin = url.searchParams.get("origin");
|
|
25531
|
+
const workspace = url.searchParams.get("workspace") ?? void 0;
|
|
25532
|
+
const reviews = listPendingReviews(engine.kernel.model, origin === null ? workspace : void 0).filter((r) => origin === null ? true : r.origin === origin);
|
|
25533
|
+
const data2 = reviews.map((r) => ({
|
|
25240
25534
|
id: r.draftId,
|
|
25241
25535
|
plan: r.plan,
|
|
25242
25536
|
draftedBy: r.proposedBy,
|
|
@@ -25247,6 +25541,7 @@ async function startOasisServer(opts) {
|
|
|
25247
25541
|
downstreamClosure: r.downstreamClosure,
|
|
25248
25542
|
at: r.at,
|
|
25249
25543
|
origin: r.origin,
|
|
25544
|
+
workspace: r.workspace,
|
|
25250
25545
|
anchor: r.anchor
|
|
25251
25546
|
}));
|
|
25252
25547
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: data2 }));
|
|
@@ -25559,12 +25854,12 @@ async function startOasisServer(opts) {
|
|
|
25559
25854
|
const actorCtx = await opts.resolveActorContext(body.actorId).catch(() => null);
|
|
25560
25855
|
if (actorCtx) {
|
|
25561
25856
|
const { mkdtempSync: mkdtempSync9, mkdirSync: mkdirSync17, writeFileSync: writeFileSync15 } = await import("node:fs");
|
|
25562
|
-
const { join:
|
|
25563
|
-
const { tmpdir:
|
|
25564
|
-
const dir = mkdtempSync9(
|
|
25857
|
+
const { join: join23, dirname: dirname19 } = await import("node:path");
|
|
25858
|
+
const { tmpdir: tmpdir12 } = await import("node:os");
|
|
25859
|
+
const dir = mkdtempSync9(join23(tmpdir12(), "oasis-chat-"));
|
|
25565
25860
|
if (actorCtx.config?.prompt) {
|
|
25566
25861
|
for (const [rel, content] of Object.entries(splitIdentityFiles2(actorCtx.config.prompt))) {
|
|
25567
|
-
const file =
|
|
25862
|
+
const file = join23(dir, rel);
|
|
25568
25863
|
mkdirSync17(dirname19(file), { recursive: true });
|
|
25569
25864
|
writeFileSync15(file, content);
|
|
25570
25865
|
}
|
|
@@ -25576,12 +25871,12 @@ async function startOasisServer(opts) {
|
|
|
25576
25871
|
const s2 = byId.get(id);
|
|
25577
25872
|
return s2 ? `- **${s2.name}** (\`${s2.id}\`): ${s2.description}` : `- \`${id}\`\uFF08\u672A\u5728\u6280\u80FD\u5E93\u4E2D\uFF0C\u53EF\u80FD\u5DF2\u5378\u8F7D\uFF09`;
|
|
25578
25873
|
});
|
|
25579
|
-
writeFileSync15(
|
|
25874
|
+
writeFileSync15(join23(dir, "SKILLS.md"), ["# \u53EF\u7528\u6280\u80FD", "", "\u4EE5\u4E0B\u6280\u80FD\u5DF2\u4E3A\u4F60\u542F\u7528\uFF0C\u53EF\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u76F4\u63A5\u4F7F\u7528\uFF1A", "", ...lines].join("\n"));
|
|
25580
25875
|
}
|
|
25581
25876
|
if (opts.materializeSkills) {
|
|
25582
25877
|
const skillFiles = await opts.materializeSkills(body.actorId, "claude").catch(() => ({}));
|
|
25583
25878
|
for (const [rel, content] of Object.entries(skillFiles)) {
|
|
25584
|
-
const file =
|
|
25879
|
+
const file = join23(dir, rel);
|
|
25585
25880
|
mkdirSync17(dirname19(file), { recursive: true });
|
|
25586
25881
|
writeFileSync15(file, content);
|
|
25587
25882
|
}
|
|
@@ -25595,7 +25890,7 @@ async function startOasisServer(opts) {
|
|
|
25595
25890
|
const modeNote = c.mode === "oauth" ? "OAuth \xB7 \u51ED\u8BC1\u7531\u5E73\u53F0\u7BA1\u7406\uFF0C\u901A\u8FC7\u5BF9\u5E94 CLI wrapper \u8C03\u7528" : "\u76F4\u63A5\u5199\u5165 \xB7 \u51ED\u8BC1\u5DF2\u6CE8\u5165\u73AF\u5883\u53D8\u91CF";
|
|
25596
25891
|
return `- **${c.name}** (\`${c.id}\`): ${statusNote} \xB7 ${modeNote}`;
|
|
25597
25892
|
});
|
|
25598
|
-
writeFileSync15(
|
|
25893
|
+
writeFileSync15(join23(dir, "CONNECTORS.md"), ["# \u53EF\u7528\u8FDE\u63A5\u5668", "", "\u4EE5\u4E0B\u8FDE\u63A5\u5668\u5DF2\u4E3A\u672C\u6B21\u4F1A\u8BDD\u914D\u7F6E\uFF0C\u51ED\u8BC1\u5DF2\u901A\u8FC7\u73AF\u5883\u53D8\u91CF\u6216 CLI wrapper \u6CE8\u5165\uFF0C\u65E0\u9700\u624B\u52A8\u914D\u7F6E\uFF1A", "", ...lines].join("\n"));
|
|
25599
25894
|
}
|
|
25600
25895
|
spawnCwd = dir;
|
|
25601
25896
|
}
|
|
@@ -26037,16 +26332,16 @@ function createTokenIssuer(opts = {}) {
|
|
|
26037
26332
|
function createNodeTokenStore(file) {
|
|
26038
26333
|
const read = () => {
|
|
26039
26334
|
try {
|
|
26040
|
-
return JSON.parse(
|
|
26335
|
+
return JSON.parse(fs4.readFileSync(file, "utf8"));
|
|
26041
26336
|
} catch {
|
|
26042
26337
|
return {};
|
|
26043
26338
|
}
|
|
26044
26339
|
};
|
|
26045
|
-
const write = (table) =>
|
|
26340
|
+
const write = (table) => fs4.writeFileSync(file, JSON.stringify(table, null, 2), { mode: 384 });
|
|
26046
26341
|
return {
|
|
26047
26342
|
issue(nodeId) {
|
|
26048
26343
|
const table = read();
|
|
26049
|
-
const token = `ont_${(0,
|
|
26344
|
+
const token = `ont_${(0, import_node_crypto3.randomBytes)(24).toString("base64url")}`;
|
|
26050
26345
|
table[token] = nodeId;
|
|
26051
26346
|
write(table);
|
|
26052
26347
|
return token;
|
|
@@ -26068,14 +26363,14 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
26068
26363
|
const read = () => {
|
|
26069
26364
|
if (!file) return table;
|
|
26070
26365
|
try {
|
|
26071
|
-
return new Map(Object.entries(JSON.parse(
|
|
26366
|
+
return new Map(Object.entries(JSON.parse(fs4.readFileSync(file, "utf8"))));
|
|
26072
26367
|
} catch {
|
|
26073
26368
|
return /* @__PURE__ */ new Map();
|
|
26074
26369
|
}
|
|
26075
26370
|
};
|
|
26076
26371
|
const write = (m2) => {
|
|
26077
26372
|
if (!file) return;
|
|
26078
|
-
|
|
26373
|
+
fs4.writeFileSync(file, JSON.stringify(Object.fromEntries(m2), null, 2), { mode: 384 });
|
|
26079
26374
|
};
|
|
26080
26375
|
const table = /* @__PURE__ */ new Map();
|
|
26081
26376
|
const prune = () => {
|
|
@@ -26088,7 +26383,7 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
26088
26383
|
return {
|
|
26089
26384
|
issue(name, ttlMs = defaultTtlMs) {
|
|
26090
26385
|
const m2 = prune();
|
|
26091
|
-
const token = `ent_${(0,
|
|
26386
|
+
const token = `ent_${(0, import_node_crypto3.randomBytes)(24).toString("base64url")}`;
|
|
26092
26387
|
const expiresAt = Date.now() + ttlMs;
|
|
26093
26388
|
m2.set(token, { name, expiresAt });
|
|
26094
26389
|
write(m2);
|
|
@@ -26118,33 +26413,33 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
26118
26413
|
}
|
|
26119
26414
|
};
|
|
26120
26415
|
}
|
|
26121
|
-
var
|
|
26416
|
+
var import_node_crypto3, fs4, path3, SESSION_TOKEN_PREFIX, b64url, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
|
|
26122
26417
|
var init_tokens = __esm({
|
|
26123
26418
|
"../server/src/tokens.ts"() {
|
|
26124
26419
|
"use strict";
|
|
26125
|
-
|
|
26126
|
-
|
|
26127
|
-
|
|
26420
|
+
import_node_crypto3 = require("node:crypto");
|
|
26421
|
+
fs4 = __toESM(require("node:fs"), 1);
|
|
26422
|
+
path3 = __toESM(require("node:path"), 1);
|
|
26128
26423
|
SESSION_TOKEN_PREFIX = "oat_v2_";
|
|
26129
26424
|
b64url = (value) => Buffer.from(value).toString("base64url");
|
|
26130
26425
|
fromB64url = (value) => Buffer.from(value, "base64url").toString("utf8");
|
|
26131
26426
|
readOrCreateSecret = (file) => {
|
|
26132
|
-
if (!file) return (0,
|
|
26427
|
+
if (!file) return (0, import_node_crypto3.randomBytes)(32);
|
|
26133
26428
|
try {
|
|
26134
|
-
const raw =
|
|
26429
|
+
const raw = fs4.readFileSync(file, "utf8").trim();
|
|
26135
26430
|
if (raw) return Buffer.from(raw, "base64url");
|
|
26136
26431
|
} catch {
|
|
26137
26432
|
}
|
|
26138
|
-
const secret = (0,
|
|
26139
|
-
|
|
26140
|
-
|
|
26433
|
+
const secret = (0, import_node_crypto3.randomBytes)(32);
|
|
26434
|
+
fs4.mkdirSync(path3.dirname(file), { recursive: true });
|
|
26435
|
+
fs4.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
|
|
26141
26436
|
return secret;
|
|
26142
26437
|
};
|
|
26143
|
-
sign = (secret, payload) => (0,
|
|
26438
|
+
sign = (secret, payload) => (0, import_node_crypto3.createHmac)("sha256", secret).update(payload).digest("base64url");
|
|
26144
26439
|
signatureMatches = (actual, expected) => {
|
|
26145
26440
|
const a = Buffer.from(actual);
|
|
26146
26441
|
const b2 = Buffer.from(expected);
|
|
26147
|
-
return a.length === b2.length && (0,
|
|
26442
|
+
return a.length === b2.length && (0, import_node_crypto3.timingSafeEqual)(a, b2);
|
|
26148
26443
|
};
|
|
26149
26444
|
isTokenClaims = (value) => {
|
|
26150
26445
|
if (value === null || typeof value !== "object") return false;
|
|
@@ -26499,6 +26794,15 @@ var init_memory_trace_store = __esm({
|
|
|
26499
26794
|
const nextCursor = items.length > limit ? page[page.length - 1]?.id ?? null : null;
|
|
26500
26795
|
return { items: page.map((r) => this.decorate(structuredClone(r))), nextCursor };
|
|
26501
26796
|
}
|
|
26797
|
+
async listUsageRuns(filter) {
|
|
26798
|
+
return [...this.runs.values()].filter((run) => run.usage !== null && run.startedAt >= filter.from && run.startedAt <= filter.to).map((run) => ({
|
|
26799
|
+
actorId: run.actorId,
|
|
26800
|
+
artifactId: run.artifactId,
|
|
26801
|
+
startedAt: run.startedAt,
|
|
26802
|
+
effectiveModel: run.effectiveModel,
|
|
26803
|
+
usage: structuredClone(run.usage)
|
|
26804
|
+
}));
|
|
26805
|
+
}
|
|
26502
26806
|
async appendEvent(runId, event) {
|
|
26503
26807
|
return (await this.appendEvents(runId, [event]))[0];
|
|
26504
26808
|
}
|
|
@@ -26992,9 +27296,9 @@ var init_memory_control_plane_store = __esm({
|
|
|
26992
27296
|
const c = this.companies.get(id);
|
|
26993
27297
|
return c ? { ...c } : null;
|
|
26994
27298
|
}
|
|
26995
|
-
async getCompanyBySlug(
|
|
27299
|
+
async getCompanyBySlug(slug4) {
|
|
26996
27300
|
for (const c of this.companies.values()) {
|
|
26997
|
-
if (c.slug ===
|
|
27301
|
+
if (c.slug === slug4) return { ...c };
|
|
26998
27302
|
}
|
|
26999
27303
|
return null;
|
|
27000
27304
|
}
|
|
@@ -27489,8 +27793,8 @@ function namedError(name, message) {
|
|
|
27489
27793
|
return err;
|
|
27490
27794
|
}
|
|
27491
27795
|
function slugProjectId(name) {
|
|
27492
|
-
const
|
|
27493
|
-
return `proj_${
|
|
27796
|
+
const slug4 = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
27797
|
+
return `proj_${slug4 || Date.now()}`;
|
|
27494
27798
|
}
|
|
27495
27799
|
function runStateKey(workOrderId, nodeId) {
|
|
27496
27800
|
return `${workOrderId ?? ""}::${nodeId ?? ""}`;
|
|
@@ -27920,13 +28224,13 @@ var init_service = __esm({
|
|
|
27920
28224
|
async readDocument(contentRef) {
|
|
27921
28225
|
for (const [projectId, space] of this.spaces) {
|
|
27922
28226
|
if (!contentRef.startsWith(`${space.rootRef}/`)) continue;
|
|
27923
|
-
const
|
|
27924
|
-
const content = space.files.get(
|
|
28227
|
+
const path19 = contentRef.slice(space.rootRef.length + 1);
|
|
28228
|
+
const content = space.files.get(path19);
|
|
27925
28229
|
if (content === void 0) return null;
|
|
27926
28230
|
return {
|
|
27927
28231
|
contentRef,
|
|
27928
28232
|
content,
|
|
27929
|
-
url: `https://outline.local/${encodeURIComponent(projectId)}/${
|
|
28233
|
+
url: `https://outline.local/${encodeURIComponent(projectId)}/${path19.split("/").map(encodeURIComponent).join("/")}`
|
|
27930
28234
|
};
|
|
27931
28235
|
}
|
|
27932
28236
|
return null;
|
|
@@ -28010,8 +28314,8 @@ var init_service = __esm({
|
|
|
28010
28314
|
const space = this.requireSpace(state.projectId);
|
|
28011
28315
|
const stored = { ...state, payload: { ...state.payload }, updatedAt: state.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString() };
|
|
28012
28316
|
space.runStates.set(runStateKey(state.workOrderId, state.nodeId), stored);
|
|
28013
|
-
const
|
|
28014
|
-
space.files.set(
|
|
28317
|
+
const path19 = `run_state/${state.workOrderId ?? "project"}/${state.nodeId ?? "default"}.json`;
|
|
28318
|
+
space.files.set(path19, JSON.stringify(stored, null, 2));
|
|
28015
28319
|
return { ...stored, payload: { ...stored.payload } };
|
|
28016
28320
|
}
|
|
28017
28321
|
requireSpace(projectId) {
|
|
@@ -28711,13 +29015,13 @@ var init_service = __esm({
|
|
|
28711
29015
|
});
|
|
28712
29016
|
|
|
28713
29017
|
// ../server/src/dev-store.ts
|
|
28714
|
-
var
|
|
29018
|
+
var import_node_crypto4, fs5, path4, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRegistryStore, FileProjectStateStore, FileProjectDocumentStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore;
|
|
28715
29019
|
var init_dev_store = __esm({
|
|
28716
29020
|
"../server/src/dev-store.ts"() {
|
|
28717
29021
|
"use strict";
|
|
28718
|
-
|
|
28719
|
-
|
|
28720
|
-
|
|
29022
|
+
import_node_crypto4 = require("node:crypto");
|
|
29023
|
+
fs5 = __toESM(require("node:fs"), 1);
|
|
29024
|
+
path4 = __toESM(require("node:path"), 1);
|
|
28721
29025
|
init_src();
|
|
28722
29026
|
init_src3();
|
|
28723
29027
|
init_src3();
|
|
@@ -28729,8 +29033,8 @@ var init_dev_store = __esm({
|
|
|
28729
29033
|
}
|
|
28730
29034
|
static async open(file) {
|
|
28731
29035
|
const memory = new MemoryOplogStore();
|
|
28732
|
-
if (
|
|
28733
|
-
for (const line of
|
|
29036
|
+
if (fs5.existsSync(file)) {
|
|
29037
|
+
for (const line of fs5.readFileSync(file, "utf8").split("\n")) {
|
|
28734
29038
|
if (!line.trim()) continue;
|
|
28735
29039
|
const stored = JSON.parse(line);
|
|
28736
29040
|
const { artifactId, seq, ...op } = stored;
|
|
@@ -28738,15 +29042,15 @@ var init_dev_store = __esm({
|
|
|
28738
29042
|
if (assigned !== seq) throw new Error(`corrupt ndjson log: seq mismatch at ${line}`);
|
|
28739
29043
|
}
|
|
28740
29044
|
} else {
|
|
28741
|
-
|
|
28742
|
-
|
|
29045
|
+
fs5.mkdirSync(path4.dirname(file), { recursive: true });
|
|
29046
|
+
fs5.writeFileSync(file, "");
|
|
28743
29047
|
}
|
|
28744
29048
|
return new _NdjsonOplogStore(file, memory);
|
|
28745
29049
|
}
|
|
28746
29050
|
async append(artifactId, op, expectedSeq) {
|
|
28747
29051
|
const seq = await this.memory.append(artifactId, op, expectedSeq);
|
|
28748
29052
|
const stored = { ...op, artifactId, seq };
|
|
28749
|
-
|
|
29053
|
+
fs5.appendFileSync(this.file, JSON.stringify(stored) + "\n");
|
|
28750
29054
|
return seq;
|
|
28751
29055
|
}
|
|
28752
29056
|
read(artifactId, fromSeq) {
|
|
@@ -28762,39 +29066,39 @@ var init_dev_store = __esm({
|
|
|
28762
29066
|
DirBlobStore = class {
|
|
28763
29067
|
constructor(dir) {
|
|
28764
29068
|
this.dir = dir;
|
|
28765
|
-
|
|
29069
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
28766
29070
|
}
|
|
28767
29071
|
fileOf(hash) {
|
|
28768
|
-
return
|
|
29072
|
+
return path4.join(this.dir, hash);
|
|
28769
29073
|
}
|
|
28770
29074
|
async put(bytes) {
|
|
28771
|
-
const hash = (0,
|
|
29075
|
+
const hash = (0, import_node_crypto4.createHash)("sha256").update(bytes).digest("hex");
|
|
28772
29076
|
const file = this.fileOf(hash);
|
|
28773
|
-
if (!
|
|
29077
|
+
if (!fs5.existsSync(file)) fs5.writeFileSync(file, bytes);
|
|
28774
29078
|
return hash;
|
|
28775
29079
|
}
|
|
28776
29080
|
async meta(hash) {
|
|
28777
29081
|
const file = this.fileOf(hash);
|
|
28778
|
-
if (!
|
|
28779
|
-
const size =
|
|
28780
|
-
const fd =
|
|
29082
|
+
if (!fs5.existsSync(file)) return null;
|
|
29083
|
+
const size = fs5.statSync(file).size;
|
|
29084
|
+
const fd = fs5.openSync(file, "r");
|
|
28781
29085
|
const head = Buffer.alloc(16);
|
|
28782
|
-
|
|
28783
|
-
|
|
29086
|
+
fs5.readSync(fd, head, 0, 16, 0);
|
|
29087
|
+
fs5.closeSync(fd);
|
|
28784
29088
|
const contentType = sniffContentType(new Uint8Array(head));
|
|
28785
29089
|
return { size, ...contentType ? { contentType } : {} };
|
|
28786
29090
|
}
|
|
28787
29091
|
async get(hash) {
|
|
28788
29092
|
const file = this.fileOf(hash);
|
|
28789
|
-
if (!
|
|
28790
|
-
return new Uint8Array(
|
|
29093
|
+
if (!fs5.existsSync(file)) throw new BlobNotFoundError(hash);
|
|
29094
|
+
return new Uint8Array(fs5.readFileSync(file));
|
|
28791
29095
|
}
|
|
28792
29096
|
async has(hash) {
|
|
28793
|
-
return
|
|
29097
|
+
return fs5.existsSync(this.fileOf(hash));
|
|
28794
29098
|
}
|
|
28795
29099
|
async sweep(reachable) {
|
|
28796
|
-
for (const name of
|
|
28797
|
-
if (!reachable.has(name))
|
|
29100
|
+
for (const name of fs5.readdirSync(this.dir)) {
|
|
29101
|
+
if (!reachable.has(name)) fs5.unlinkSync(this.fileOf(name));
|
|
28798
29102
|
}
|
|
28799
29103
|
}
|
|
28800
29104
|
};
|
|
@@ -28822,7 +29126,7 @@ var init_dev_store = __esm({
|
|
|
28822
29126
|
this.file = file;
|
|
28823
29127
|
}
|
|
28824
29128
|
static async open(file) {
|
|
28825
|
-
const seed =
|
|
29129
|
+
const seed = fs5.existsSync(file) ? JSON.parse(fs5.readFileSync(file, "utf8")) : [];
|
|
28826
29130
|
return new _FileTypeRegistryStore(file, seed);
|
|
28827
29131
|
}
|
|
28828
29132
|
async put(def) {
|
|
@@ -28835,8 +29139,8 @@ var init_dev_store = __esm({
|
|
|
28835
29139
|
}
|
|
28836
29140
|
async save() {
|
|
28837
29141
|
const tmp = `${this.file}.tmp`;
|
|
28838
|
-
|
|
28839
|
-
|
|
29142
|
+
fs5.writeFileSync(tmp, JSON.stringify(await this.list(), null, 2));
|
|
29143
|
+
fs5.renameSync(tmp, this.file);
|
|
28840
29144
|
}
|
|
28841
29145
|
};
|
|
28842
29146
|
FileRegistryStore = class _FileRegistryStore extends MemoryRegistryStore {
|
|
@@ -28854,8 +29158,8 @@ var init_dev_store = __esm({
|
|
|
28854
29158
|
}
|
|
28855
29159
|
static async open(file) {
|
|
28856
29160
|
const store = new _FileRegistryStore(file);
|
|
28857
|
-
if (
|
|
28858
|
-
const snap = JSON.parse(
|
|
29161
|
+
if (fs5.existsSync(file)) {
|
|
29162
|
+
const snap = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
28859
29163
|
for (const a of snap.actors) await MemoryRegistryStore.prototype.upsertActor.call(store, a);
|
|
28860
29164
|
for (const c of snap.configs) await MemoryRegistryStore.prototype.appendConfig.call(store, c);
|
|
28861
29165
|
for (const t of snap.teams) await MemoryRegistryStore.prototype.upsertTeam.call(store, t);
|
|
@@ -28896,7 +29200,7 @@ var init_dev_store = __esm({
|
|
|
28896
29200
|
skillFiles: skillFilesEntries,
|
|
28897
29201
|
connections
|
|
28898
29202
|
};
|
|
28899
|
-
|
|
29203
|
+
fs5.writeFileSync(this.file, JSON.stringify(snap, null, 2));
|
|
28900
29204
|
}
|
|
28901
29205
|
};
|
|
28902
29206
|
FileProjectStateStore = class _FileProjectStateStore extends MemoryProjectStateStore {
|
|
@@ -28906,8 +29210,8 @@ var init_dev_store = __esm({
|
|
|
28906
29210
|
}
|
|
28907
29211
|
static async open(file) {
|
|
28908
29212
|
const store = new _FileProjectStateStore(file);
|
|
28909
|
-
if (
|
|
28910
|
-
const snap = JSON.parse(
|
|
29213
|
+
if (fs5.existsSync(file)) {
|
|
29214
|
+
const snap = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
28911
29215
|
for (const project of snap.projects ?? []) await MemoryProjectStateStore.prototype.upsertProject.call(store, project);
|
|
28912
29216
|
const membersByProject = /* @__PURE__ */ new Map();
|
|
28913
29217
|
for (const member of snap.members ?? []) {
|
|
@@ -28919,8 +29223,8 @@ var init_dev_store = __esm({
|
|
|
28919
29223
|
await MemoryProjectStateStore.prototype.replaceProjectMembers.call(store, projectId, members);
|
|
28920
29224
|
}
|
|
28921
29225
|
} else {
|
|
28922
|
-
|
|
28923
|
-
|
|
29226
|
+
fs5.mkdirSync(path4.dirname(file), { recursive: true });
|
|
29227
|
+
fs5.writeFileSync(file, JSON.stringify({ projects: [], members: [] }, null, 2));
|
|
28924
29228
|
}
|
|
28925
29229
|
return store;
|
|
28926
29230
|
}
|
|
@@ -28939,7 +29243,7 @@ var init_dev_store = __esm({
|
|
|
28939
29243
|
async save() {
|
|
28940
29244
|
const projects = await this.listProjects();
|
|
28941
29245
|
const members = (await Promise.all(projects.map((project) => this.listProjectMembers(project.id)))).flat();
|
|
28942
|
-
|
|
29246
|
+
fs5.writeFileSync(this.file, JSON.stringify({ projects, members }, null, 2));
|
|
28943
29247
|
}
|
|
28944
29248
|
};
|
|
28945
29249
|
FileProjectDocumentStore = class _FileProjectDocumentStore extends MemoryProjectDocumentStore {
|
|
@@ -28949,8 +29253,8 @@ var init_dev_store = __esm({
|
|
|
28949
29253
|
}
|
|
28950
29254
|
static async open(file) {
|
|
28951
29255
|
const store = new _FileProjectDocumentStore(file);
|
|
28952
|
-
if (
|
|
28953
|
-
const snap = JSON.parse(
|
|
29256
|
+
if (fs5.existsSync(file)) {
|
|
29257
|
+
const snap = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
28954
29258
|
for (const [projectId, space] of Object.entries(snap.spaces ?? {})) {
|
|
28955
29259
|
store.spaces.set(projectId, {
|
|
28956
29260
|
rootRef: space.rootRef,
|
|
@@ -28964,8 +29268,8 @@ var init_dev_store = __esm({
|
|
|
28964
29268
|
});
|
|
28965
29269
|
}
|
|
28966
29270
|
} else {
|
|
28967
|
-
|
|
28968
|
-
|
|
29271
|
+
fs5.mkdirSync(path4.dirname(file), { recursive: true });
|
|
29272
|
+
fs5.writeFileSync(file, JSON.stringify({ spaces: {} }, null, 2));
|
|
28969
29273
|
}
|
|
28970
29274
|
return store;
|
|
28971
29275
|
}
|
|
@@ -28998,7 +29302,7 @@ var init_dev_store = __esm({
|
|
|
28998
29302
|
runStates: [...space.runStates.values()]
|
|
28999
29303
|
};
|
|
29000
29304
|
}
|
|
29001
|
-
|
|
29305
|
+
fs5.writeFileSync(this.file, JSON.stringify({ spaces }, null, 2));
|
|
29002
29306
|
}
|
|
29003
29307
|
};
|
|
29004
29308
|
FileArtifactStateStore = class _FileArtifactStateStore extends MemoryArtifactStateStore {
|
|
@@ -29008,8 +29312,8 @@ var init_dev_store = __esm({
|
|
|
29008
29312
|
}
|
|
29009
29313
|
static async open(file) {
|
|
29010
29314
|
const store = new _FileArtifactStateStore(file);
|
|
29011
|
-
if (
|
|
29012
|
-
const snap = JSON.parse(
|
|
29315
|
+
if (fs5.existsSync(file)) {
|
|
29316
|
+
const snap = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
29013
29317
|
for (const artifact of snap.artifacts ?? []) await MemoryArtifactStateStore.prototype.upsertArtifact.call(store, artifact);
|
|
29014
29318
|
for (const revision of snap.revisions ?? []) await MemoryArtifactStateStore.prototype.upsertRevision.call(store, revision);
|
|
29015
29319
|
for (const event of snap.events ?? []) await MemoryArtifactStateStore.prototype.appendEvent.call(store, event);
|
|
@@ -29018,8 +29322,8 @@ var init_dev_store = __esm({
|
|
|
29018
29322
|
for (const binding of snap.workspaceBindings ?? []) await MemoryArtifactStateStore.prototype.upsertWorkspaceBinding.call(store, binding);
|
|
29019
29323
|
for (const ws of snap.workspaceDispatchHeld ?? []) await MemoryArtifactStateStore.prototype.setWorkspaceDispatchHold.call(store, ws, true);
|
|
29020
29324
|
} else {
|
|
29021
|
-
|
|
29022
|
-
|
|
29325
|
+
fs5.mkdirSync(path4.dirname(file), { recursive: true });
|
|
29326
|
+
fs5.writeFileSync(file, JSON.stringify({ artifacts: [], revisions: [], events: [], annotations: [], nodeOutputs: [], workspaceBindings: [], workspaceDispatchHeld: [] }, null, 2));
|
|
29023
29327
|
}
|
|
29024
29328
|
return store;
|
|
29025
29329
|
}
|
|
@@ -29052,7 +29356,7 @@ var init_dev_store = __esm({
|
|
|
29052
29356
|
await this.save();
|
|
29053
29357
|
}
|
|
29054
29358
|
async save() {
|
|
29055
|
-
|
|
29359
|
+
fs5.writeFileSync(this.file, JSON.stringify(await this.listAll(), null, 2));
|
|
29056
29360
|
}
|
|
29057
29361
|
};
|
|
29058
29362
|
FileTraceStore = class _FileTraceStore extends MemoryTraceStore {
|
|
@@ -29061,39 +29365,39 @@ var init_dev_store = __esm({
|
|
|
29061
29365
|
this.dir = dir;
|
|
29062
29366
|
}
|
|
29063
29367
|
get runsFile() {
|
|
29064
|
-
return
|
|
29368
|
+
return path4.join(this.dir, "runs.ndjson");
|
|
29065
29369
|
}
|
|
29066
29370
|
get toolCallsFile() {
|
|
29067
|
-
return
|
|
29371
|
+
return path4.join(this.dir, "tool-calls.ndjson");
|
|
29068
29372
|
}
|
|
29069
29373
|
get linksFile() {
|
|
29070
|
-
return
|
|
29374
|
+
return path4.join(this.dir, "links.ndjson");
|
|
29071
29375
|
}
|
|
29072
29376
|
get artifactsFile() {
|
|
29073
|
-
return
|
|
29377
|
+
return path4.join(this.dir, "artifacts.ndjson");
|
|
29074
29378
|
}
|
|
29075
29379
|
get eventsDir() {
|
|
29076
|
-
return
|
|
29380
|
+
return path4.join(this.dir, "events");
|
|
29077
29381
|
}
|
|
29078
29382
|
eventsFile(runId) {
|
|
29079
|
-
return
|
|
29383
|
+
return path4.join(this.eventsDir, `${encodeURIComponent(runId)}.ndjson`);
|
|
29080
29384
|
}
|
|
29081
29385
|
appendLine(file, obj) {
|
|
29082
|
-
|
|
29386
|
+
fs5.appendFileSync(file, JSON.stringify(obj) + "\n");
|
|
29083
29387
|
}
|
|
29084
29388
|
static replay(file, fn) {
|
|
29085
|
-
if (!
|
|
29086
|
-
for (const line of
|
|
29389
|
+
if (!fs5.existsSync(file)) return;
|
|
29390
|
+
for (const line of fs5.readFileSync(file, "utf8").split("\n")) {
|
|
29087
29391
|
if (!line.trim()) continue;
|
|
29088
29392
|
fn(JSON.parse(line));
|
|
29089
29393
|
}
|
|
29090
29394
|
}
|
|
29091
29395
|
static async open(dir) {
|
|
29092
29396
|
const store = new _FileTraceStore(dir);
|
|
29093
|
-
|
|
29397
|
+
fs5.mkdirSync(store.eventsDir, { recursive: true });
|
|
29094
29398
|
_FileTraceStore.replay(store.runsFile, (r) => store.restoreRun(r));
|
|
29095
|
-
for (const name of
|
|
29096
|
-
_FileTraceStore.replay(
|
|
29399
|
+
for (const name of fs5.readdirSync(store.eventsDir)) {
|
|
29400
|
+
_FileTraceStore.replay(path4.join(store.eventsDir, name), (e) => store.restoreEvent(e));
|
|
29097
29401
|
}
|
|
29098
29402
|
_FileTraceStore.replay(store.toolCallsFile, (c) => store.restoreToolCall(c));
|
|
29099
29403
|
_FileTraceStore.replay(store.linksFile, (l) => store.restoreLink(l));
|
|
@@ -29196,14 +29500,14 @@ var init_dev_store = __esm({
|
|
|
29196
29500
|
}
|
|
29197
29501
|
static async open(file) {
|
|
29198
29502
|
const store = new _FileChatSessionStore(file);
|
|
29199
|
-
if (
|
|
29200
|
-
const snap = JSON.parse(
|
|
29503
|
+
if (fs5.existsSync(file)) {
|
|
29504
|
+
const snap = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
29201
29505
|
for (const s2 of snap.sessions ?? []) store.sessions.set(s2.id, s2);
|
|
29202
29506
|
for (const [sid, list] of Object.entries(snap.messages ?? {})) store.messages.set(sid, list);
|
|
29203
29507
|
for (const [sid, ids] of Object.entries(snap.sessionWorkOrders ?? {})) store.sessionWorkOrders.set(sid, new Set(ids));
|
|
29204
29508
|
} else {
|
|
29205
|
-
|
|
29206
|
-
|
|
29509
|
+
fs5.mkdirSync(path4.dirname(file), { recursive: true });
|
|
29510
|
+
fs5.writeFileSync(file, JSON.stringify({ sessions: [], messages: {}, sessionWorkOrders: {} }, null, 2));
|
|
29207
29511
|
}
|
|
29208
29512
|
return store;
|
|
29209
29513
|
}
|
|
@@ -29238,19 +29542,19 @@ var init_dev_store = __esm({
|
|
|
29238
29542
|
messages: Object.fromEntries(this.messages),
|
|
29239
29543
|
sessionWorkOrders: Object.fromEntries([...this.sessionWorkOrders].map(([k2, v2]) => [k2, [...v2]]))
|
|
29240
29544
|
};
|
|
29241
|
-
|
|
29545
|
+
fs5.writeFileSync(this.file, JSON.stringify(snap, null, 2));
|
|
29242
29546
|
}
|
|
29243
29547
|
};
|
|
29244
29548
|
}
|
|
29245
29549
|
});
|
|
29246
29550
|
|
|
29247
29551
|
// ../server/src/control-plane-file-store.ts
|
|
29248
|
-
var
|
|
29552
|
+
var fs6, path5, MUTATORS2, FileControlPlaneStore;
|
|
29249
29553
|
var init_control_plane_file_store = __esm({
|
|
29250
29554
|
"../server/src/control-plane-file-store.ts"() {
|
|
29251
29555
|
"use strict";
|
|
29252
|
-
|
|
29253
|
-
|
|
29556
|
+
fs6 = __toESM(require("node:fs"), 1);
|
|
29557
|
+
path5 = __toESM(require("node:path"), 1);
|
|
29254
29558
|
init_src3();
|
|
29255
29559
|
MUTATORS2 = [
|
|
29256
29560
|
"createCompany",
|
|
@@ -29278,15 +29582,15 @@ var init_control_plane_file_store = __esm({
|
|
|
29278
29582
|
}
|
|
29279
29583
|
static async open(file) {
|
|
29280
29584
|
const store = new _FileControlPlaneStore(file);
|
|
29281
|
-
if (
|
|
29282
|
-
const snap = JSON.parse(
|
|
29585
|
+
if (fs6.existsSync(file)) {
|
|
29586
|
+
const snap = JSON.parse(fs6.readFileSync(file, "utf8"));
|
|
29283
29587
|
for (const c of snap.companies ?? []) await MemoryControlPlaneStore.prototype.createCompany.call(store, c);
|
|
29284
29588
|
for (const m2 of snap.members ?? []) await MemoryControlPlaneStore.prototype.upsertMember.call(store, m2);
|
|
29285
29589
|
for (const inv of snap.invitations ?? []) await MemoryControlPlaneStore.prototype.createInvitation.call(store, inv);
|
|
29286
29590
|
for (const a of snap.accounts ?? []) await MemoryControlPlaneStore.prototype.upsertAccount.call(store, a);
|
|
29287
29591
|
for (const e of snap.accountSecrets ?? []) store.accountSecrets.set(e.accountId, { ...e.secret });
|
|
29288
29592
|
} else {
|
|
29289
|
-
|
|
29593
|
+
fs6.mkdirSync(path5.dirname(file), { recursive: true });
|
|
29290
29594
|
}
|
|
29291
29595
|
return store;
|
|
29292
29596
|
}
|
|
@@ -29307,7 +29611,7 @@ var init_control_plane_file_store = __esm({
|
|
|
29307
29611
|
accounts,
|
|
29308
29612
|
accountSecrets
|
|
29309
29613
|
};
|
|
29310
|
-
|
|
29614
|
+
fs6.writeFileSync(this.file, JSON.stringify(snap, null, 2));
|
|
29311
29615
|
}
|
|
29312
29616
|
};
|
|
29313
29617
|
}
|
|
@@ -29666,8 +29970,8 @@ var init_recovery_plan = __esm({
|
|
|
29666
29970
|
|
|
29667
29971
|
// ../server/src/auth/crypto.ts
|
|
29668
29972
|
function hashPassword(password) {
|
|
29669
|
-
const salt = (0,
|
|
29670
|
-
const dk = (0,
|
|
29973
|
+
const salt = (0, import_node_crypto5.randomBytes)(16);
|
|
29974
|
+
const dk = (0, import_node_crypto5.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
|
|
29671
29975
|
return `scrypt$${SCRYPT_N}$${salt.toString("base64url")}$${dk.toString("base64url")}`;
|
|
29672
29976
|
}
|
|
29673
29977
|
function verifyPassword(password, stored) {
|
|
@@ -29684,8 +29988,8 @@ function verifyPassword(password, stored) {
|
|
|
29684
29988
|
return false;
|
|
29685
29989
|
}
|
|
29686
29990
|
if (expected.length === 0) return false;
|
|
29687
|
-
const dk = (0,
|
|
29688
|
-
return dk.length === expected.length && (0,
|
|
29991
|
+
const dk = (0, import_node_crypto5.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
|
|
29992
|
+
return dk.length === expected.length && (0, import_node_crypto5.timingSafeEqual)(dk, expected);
|
|
29689
29993
|
}
|
|
29690
29994
|
function b64urlJson(value) {
|
|
29691
29995
|
return Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
@@ -29693,17 +29997,17 @@ function b64urlJson(value) {
|
|
|
29693
29997
|
function signSession(claims, secret) {
|
|
29694
29998
|
const head = b64urlJson({ alg: "HS256", typ: "JWT" });
|
|
29695
29999
|
const body = b64urlJson(claims);
|
|
29696
|
-
const sig = (0,
|
|
30000
|
+
const sig = (0, import_node_crypto5.createHmac)("sha256", secret).update(`${head}.${body}`).digest("base64url");
|
|
29697
30001
|
return `${head}.${body}.${sig}`;
|
|
29698
30002
|
}
|
|
29699
30003
|
function verifySession(token, secret, nowMs) {
|
|
29700
30004
|
const parts = token.split(".");
|
|
29701
30005
|
if (parts.length !== 3) return null;
|
|
29702
30006
|
const [head, body, sig] = parts;
|
|
29703
|
-
const expected = (0,
|
|
30007
|
+
const expected = (0, import_node_crypto5.createHmac)("sha256", secret).update(`${head}.${body}`).digest("base64url");
|
|
29704
30008
|
const got = Buffer.from(sig);
|
|
29705
30009
|
const exp = Buffer.from(expected);
|
|
29706
|
-
if (got.length !== exp.length || !(0,
|
|
30010
|
+
if (got.length !== exp.length || !(0, import_node_crypto5.timingSafeEqual)(got, exp)) return null;
|
|
29707
30011
|
let claims;
|
|
29708
30012
|
try {
|
|
29709
30013
|
claims = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
@@ -29715,21 +30019,21 @@ function verifySession(token, secret, nowMs) {
|
|
|
29715
30019
|
return claims;
|
|
29716
30020
|
}
|
|
29717
30021
|
function generateCode() {
|
|
29718
|
-
return String((0,
|
|
30022
|
+
return String((0, import_node_crypto5.randomInt)(0, 1e6)).padStart(6, "0");
|
|
29719
30023
|
}
|
|
29720
30024
|
function hashCode(code, secret) {
|
|
29721
|
-
return (0,
|
|
30025
|
+
return (0, import_node_crypto5.createHmac)("sha256", secret).update(`code:${code}`).digest("base64url");
|
|
29722
30026
|
}
|
|
29723
30027
|
function safeEqualHash(a, b2) {
|
|
29724
30028
|
const ba = Buffer.from(a);
|
|
29725
30029
|
const bb = Buffer.from(b2);
|
|
29726
|
-
return ba.length === bb.length && (0,
|
|
30030
|
+
return ba.length === bb.length && (0, import_node_crypto5.timingSafeEqual)(ba, bb);
|
|
29727
30031
|
}
|
|
29728
|
-
var
|
|
30032
|
+
var import_node_crypto5, SCRYPT_N, SCRYPT_KEYLEN;
|
|
29729
30033
|
var init_crypto = __esm({
|
|
29730
30034
|
"../server/src/auth/crypto.ts"() {
|
|
29731
30035
|
"use strict";
|
|
29732
|
-
|
|
30036
|
+
import_node_crypto5 = require("node:crypto");
|
|
29733
30037
|
SCRYPT_N = 16384;
|
|
29734
30038
|
SCRYPT_KEYLEN = 32;
|
|
29735
30039
|
}
|
|
@@ -29737,11 +30041,11 @@ var init_crypto = __esm({
|
|
|
29737
30041
|
|
|
29738
30042
|
// ../server/src/file-engine.ts
|
|
29739
30043
|
async function openFileEngine(dir, seed = {}) {
|
|
29740
|
-
|
|
29741
|
-
const oplog = await NdjsonOplogStore.open(
|
|
29742
|
-
const blobs = new DirBlobStore(
|
|
29743
|
-
const assets = new DirBlobStore(
|
|
29744
|
-
const registry2 = await FileRegistryStore.open(
|
|
30044
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
30045
|
+
const oplog = await NdjsonOplogStore.open(path6.join(dir, "oplog.ndjson"));
|
|
30046
|
+
const blobs = new DirBlobStore(path6.join(dir, "blobs"));
|
|
30047
|
+
const assets = new DirBlobStore(path6.join(dir, "assets"));
|
|
30048
|
+
const registry2 = await FileRegistryStore.open(path6.join(dir, "registry-store.json"));
|
|
29745
30049
|
const kernel = await Kernel.open(oplog, {
|
|
29746
30050
|
schema: seed.schema ?? [],
|
|
29747
30051
|
registry: seed.registry ?? [],
|
|
@@ -29754,12 +30058,12 @@ async function openFileEngine(dir, seed = {}) {
|
|
|
29754
30058
|
function companyEngineDirName(companyId) {
|
|
29755
30059
|
return companyId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
29756
30060
|
}
|
|
29757
|
-
var
|
|
30061
|
+
var fs7, path6;
|
|
29758
30062
|
var init_file_engine = __esm({
|
|
29759
30063
|
"../server/src/file-engine.ts"() {
|
|
29760
30064
|
"use strict";
|
|
29761
|
-
|
|
29762
|
-
|
|
30065
|
+
fs7 = __toESM(require("node:fs"), 1);
|
|
30066
|
+
path6 = __toESM(require("node:path"), 1);
|
|
29763
30067
|
init_src2();
|
|
29764
30068
|
init_dev_store();
|
|
29765
30069
|
}
|
|
@@ -32013,7 +32317,7 @@ var require_websocket = __commonJS({
|
|
|
32013
32317
|
var http2 = require("http");
|
|
32014
32318
|
var net = require("net");
|
|
32015
32319
|
var tls = require("tls");
|
|
32016
|
-
var { randomBytes: randomBytes5, createHash:
|
|
32320
|
+
var { randomBytes: randomBytes5, createHash: createHash11 } = require("crypto");
|
|
32017
32321
|
var { Duplex, Readable } = require("stream");
|
|
32018
32322
|
var { URL: URL2 } = require("url");
|
|
32019
32323
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -32681,7 +32985,7 @@ var require_websocket = __commonJS({
|
|
|
32681
32985
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
32682
32986
|
return;
|
|
32683
32987
|
}
|
|
32684
|
-
const digest =
|
|
32988
|
+
const digest = createHash11("sha1").update(key + GUID).digest("base64");
|
|
32685
32989
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
32686
32990
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
32687
32991
|
return;
|
|
@@ -33050,7 +33354,7 @@ var require_websocket_server = __commonJS({
|
|
|
33050
33354
|
var EventEmitter = require("events");
|
|
33051
33355
|
var http2 = require("http");
|
|
33052
33356
|
var { Duplex } = require("stream");
|
|
33053
|
-
var { createHash:
|
|
33357
|
+
var { createHash: createHash11 } = require("crypto");
|
|
33054
33358
|
var extension2 = require_extension();
|
|
33055
33359
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
33056
33360
|
var subprotocol2 = require_subprotocol();
|
|
@@ -33357,7 +33661,7 @@ var require_websocket_server = __commonJS({
|
|
|
33357
33661
|
);
|
|
33358
33662
|
}
|
|
33359
33663
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
33360
|
-
const digest =
|
|
33664
|
+
const digest = createHash11("sha1").update(key + GUID).digest("base64");
|
|
33361
33665
|
const headers = [
|
|
33362
33666
|
"HTTP/1.1 101 Switching Protocols",
|
|
33363
33667
|
"Upgrade: websocket",
|
|
@@ -33501,12 +33805,12 @@ var init_daemon_hub = __esm({
|
|
|
33501
33805
|
this.disconnectListeners.add(cb);
|
|
33502
33806
|
return () => this.disconnectListeners.delete(cb);
|
|
33503
33807
|
}
|
|
33504
|
-
constructor(server,
|
|
33808
|
+
constructor(server, path19 = "/daemon/ws", resolveNode, opts = {}) {
|
|
33505
33809
|
this.pingIntervalMs = opts.pingIntervalMs ?? 2e4;
|
|
33506
33810
|
this.silenceTimeoutMs = opts.silenceTimeoutMs ?? 6e4;
|
|
33507
33811
|
this.wss = new import_websocket_server.default({
|
|
33508
33812
|
server,
|
|
33509
|
-
path:
|
|
33813
|
+
path: path19,
|
|
33510
33814
|
...resolveNode ? {
|
|
33511
33815
|
verifyClient: (info, cb) => {
|
|
33512
33816
|
const authHeader = info.req.headers.authorization ?? "";
|
|
@@ -33688,11 +33992,11 @@ async function fetchGitHubApi(url) {
|
|
|
33688
33992
|
async function fetchFromClawHub(sourceUrl) {
|
|
33689
33993
|
const parsed = new URL(sourceUrl);
|
|
33690
33994
|
const parts = parsed.pathname.replace(/^\//, "").split("/").filter(Boolean);
|
|
33691
|
-
const
|
|
33692
|
-
if (!
|
|
33995
|
+
const slug4 = parts[parts.length - 1];
|
|
33996
|
+
if (!slug4) throw new Error(`Cannot extract ClawHub slug from URL: ${sourceUrl}`);
|
|
33693
33997
|
const apiBase = "https://clawhub.ai/api/v1";
|
|
33694
|
-
const meta = await fetch(`${apiBase}/skills/${encodeURIComponent(
|
|
33695
|
-
if (r.status === 404) throw new Error(`Skill not found on ClawHub: ${
|
|
33998
|
+
const meta = await fetch(`${apiBase}/skills/${encodeURIComponent(slug4)}`).then((r) => {
|
|
33999
|
+
if (r.status === 404) throw new Error(`Skill not found on ClawHub: ${slug4}`);
|
|
33696
34000
|
if (!r.ok) throw new Error(`ClawHub returned ${r.status}`);
|
|
33697
34001
|
return r.json();
|
|
33698
34002
|
});
|
|
@@ -33700,13 +34004,13 @@ async function fetchFromClawHub(sourceUrl) {
|
|
|
33700
34004
|
let latestVersion = (chSkill.tags?.["latest"] ?? meta.latestVersion?.version) || "";
|
|
33701
34005
|
let filePaths = [];
|
|
33702
34006
|
if (latestVersion) {
|
|
33703
|
-
const vUrl = `${apiBase}/skills/${encodeURIComponent(
|
|
34007
|
+
const vUrl = `${apiBase}/skills/${encodeURIComponent(slug4)}/versions/${encodeURIComponent(latestVersion)}`;
|
|
33704
34008
|
const vMeta = await fetch(vUrl).then((r) => r.ok ? r.json() : null);
|
|
33705
34009
|
if (vMeta) filePaths = vMeta.version.files.map((f2) => f2.path);
|
|
33706
34010
|
}
|
|
33707
34011
|
const bundle = new Bundle();
|
|
33708
34012
|
for (const fp of filePaths) {
|
|
33709
|
-
let fileUrl = `${apiBase}/skills/${encodeURIComponent(
|
|
34013
|
+
let fileUrl = `${apiBase}/skills/${encodeURIComponent(slug4)}/file?path=${encodeURIComponent(fp)}`;
|
|
33710
34014
|
if (latestVersion) fileUrl += `&version=${encodeURIComponent(latestVersion)}`;
|
|
33711
34015
|
try {
|
|
33712
34016
|
const content = await fetchText(fileUrl);
|
|
@@ -33717,11 +34021,11 @@ async function fetchFromClawHub(sourceUrl) {
|
|
|
33717
34021
|
console.warn(`[skill-fetcher] ClawHub: skipping ${fp}: ${err}`);
|
|
33718
34022
|
}
|
|
33719
34023
|
}
|
|
33720
|
-
if (!bundle.files["SKILL.md"]) throw new Error(`ClawHub: SKILL.md missing for ${
|
|
34024
|
+
if (!bundle.files["SKILL.md"]) throw new Error(`ClawHub: SKILL.md missing for ${slug4}`);
|
|
33721
34025
|
const { name, description } = parseSkillFrontmatter(bundle.files["SKILL.md"]);
|
|
33722
34026
|
return {
|
|
33723
|
-
id: `claw-${
|
|
33724
|
-
name: name || chSkill.displayName ||
|
|
34027
|
+
id: `claw-${slug4.replace(/[^a-z0-9-]/g, "-")}`,
|
|
34028
|
+
name: name || chSkill.displayName || slug4,
|
|
33725
34029
|
description: description || chSkill.summary || "",
|
|
33726
34030
|
files: bundle.files
|
|
33727
34031
|
};
|
|
@@ -33861,13 +34165,13 @@ var init_skill_fetcher = __esm({
|
|
|
33861
34165
|
files = {};
|
|
33862
34166
|
totalBytes = 0;
|
|
33863
34167
|
fileCount = 0;
|
|
33864
|
-
add(
|
|
34168
|
+
add(path19, content) {
|
|
33865
34169
|
const bytes = new TextEncoder().encode(content).length;
|
|
33866
34170
|
if (this.fileCount >= MAX_FILE_COUNT)
|
|
33867
34171
|
throw new Error(`Import bundle exceeds ${MAX_FILE_COUNT} file limit`);
|
|
33868
34172
|
if (this.totalBytes + bytes > MAX_TOTAL_BYTES)
|
|
33869
34173
|
throw new Error(`Import bundle exceeds ${MAX_TOTAL_BYTES} byte limit`);
|
|
33870
|
-
this.files[
|
|
34174
|
+
this.files[path19] = content;
|
|
33871
34175
|
this.totalBytes += bytes;
|
|
33872
34176
|
this.fileCount++;
|
|
33873
34177
|
}
|
|
@@ -33884,13 +34188,13 @@ function variableKeyFromEnv(env = process.env) {
|
|
|
33884
34188
|
return buf;
|
|
33885
34189
|
}
|
|
33886
34190
|
if (env.NODE_ENV === "production") throw new Error("OASIS_VAR_KEY is required in production");
|
|
33887
|
-
return (0,
|
|
34191
|
+
return (0, import_node_crypto6.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
|
|
33888
34192
|
}
|
|
33889
|
-
var
|
|
34193
|
+
var import_node_crypto6, REDACTED_MARKER, ActorsService, mask, changedKeys;
|
|
33890
34194
|
var init_service2 = __esm({
|
|
33891
34195
|
"../server/src/domains/actors/service.ts"() {
|
|
33892
34196
|
"use strict";
|
|
33893
|
-
|
|
34197
|
+
import_node_crypto6 = require("node:crypto");
|
|
33894
34198
|
init_skill_fetcher();
|
|
33895
34199
|
init_identity();
|
|
33896
34200
|
init_feishu_oauth();
|
|
@@ -34136,7 +34440,7 @@ ${input.description}
|
|
|
34136
34440
|
};
|
|
34137
34441
|
await this.opts.store.putInstalledSkill(installed);
|
|
34138
34442
|
const skillFiles = Object.entries(fetched.files).map(
|
|
34139
|
-
([
|
|
34443
|
+
([path19, content]) => this.buildSkillFile(id, path19, content, now)
|
|
34140
34444
|
);
|
|
34141
34445
|
await this.opts.store.putSkillFiles(id, skillFiles);
|
|
34142
34446
|
await this.audit({ kind: "skill_install", actorId: by, by, at: now, detail: { skillId: id, source: input.sourceKind, sourceUrl } });
|
|
@@ -34171,7 +34475,7 @@ ${input.description}
|
|
|
34171
34475
|
connectorId
|
|
34172
34476
|
});
|
|
34173
34477
|
const skillFiles = Object.entries(entry.files).map(
|
|
34174
|
-
([
|
|
34478
|
+
([path19, content]) => this.buildSkillFile(id, path19, content, now)
|
|
34175
34479
|
);
|
|
34176
34480
|
await this.opts.store.putSkillFiles(id, skillFiles);
|
|
34177
34481
|
}
|
|
@@ -34189,25 +34493,25 @@ ${input.description}
|
|
|
34189
34493
|
* 构造 SkillFile:正文内联进 content(决策 0036);blobHash/size 计算为 content 的 sha256
|
|
34190
34494
|
* 与字节数,仅作元数据/审计(不再有读取方,正文直接从 content 出)。
|
|
34191
34495
|
*/
|
|
34192
|
-
buildSkillFile(skillId,
|
|
34496
|
+
buildSkillFile(skillId, path19, content, now) {
|
|
34193
34497
|
const bytes = new TextEncoder().encode(content);
|
|
34194
|
-
const blobHash = (0,
|
|
34195
|
-
return { skillId, path:
|
|
34498
|
+
const blobHash = (0, import_node_crypto6.createHash)("sha256").update(bytes).digest("hex");
|
|
34499
|
+
return { skillId, path: path19, content, blobHash, size: bytes.length, updatedAt: now };
|
|
34196
34500
|
}
|
|
34197
34501
|
/**
|
|
34198
34502
|
* 更新或新建技能的单个文件。
|
|
34199
34503
|
* 正文内联进 skill_files(决策 0036),更新文件索引,但**不**改写技能的 name/description——
|
|
34200
34504
|
* 如果修改的是 SKILL.md,调用方应另外调用 updateInstalledSkill 同步 frontmatter。
|
|
34201
34505
|
*/
|
|
34202
|
-
async putSkillFile(skillId,
|
|
34506
|
+
async putSkillFile(skillId, path19, content) {
|
|
34203
34507
|
const cur = (await this.opts.store.listInstalledSkills()).find((x2) => x2.id === skillId);
|
|
34204
34508
|
if (!cur) throw new Error(`\u672A\u5B89\u88C5\u8BE5\u6280\u80FD\uFF1A${skillId}`);
|
|
34205
34509
|
const now = this.now();
|
|
34206
|
-
const file = this.buildSkillFile(skillId,
|
|
34510
|
+
const file = this.buildSkillFile(skillId, path19, content, now);
|
|
34207
34511
|
const existing = await this.opts.store.listSkillFiles(skillId);
|
|
34208
|
-
const next = [...existing.filter((f2) => f2.path !==
|
|
34512
|
+
const next = [...existing.filter((f2) => f2.path !== path19), file];
|
|
34209
34513
|
await this.opts.store.putSkillFiles(skillId, next);
|
|
34210
|
-
if (
|
|
34514
|
+
if (path19 === "SKILL.md") {
|
|
34211
34515
|
const { name, description } = parseSkillFrontmatter(content);
|
|
34212
34516
|
if (name || description) {
|
|
34213
34517
|
await this.opts.store.putInstalledSkill({
|
|
@@ -34223,11 +34527,11 @@ ${input.description}
|
|
|
34223
34527
|
* 删除技能的单个支撑文件(SKILL.md 主文档受保护,调用方应先拦截)。
|
|
34224
34528
|
* 单文件删除=读现有清单→剔除该 path→整体回写(putSkillFiles 是 replace-all 语义);blob 残留由 GC 回收。
|
|
34225
34529
|
*/
|
|
34226
|
-
async deleteSkillFile(skillId,
|
|
34530
|
+
async deleteSkillFile(skillId, path19) {
|
|
34227
34531
|
const cur = (await this.opts.store.listInstalledSkills()).find((x2) => x2.id === skillId);
|
|
34228
34532
|
if (!cur) throw new Error(`\u672A\u5B89\u88C5\u8BE5\u6280\u80FD\uFF1A${skillId}`);
|
|
34229
34533
|
const existing = await this.opts.store.listSkillFiles(skillId);
|
|
34230
|
-
const next = existing.filter((f2) => f2.path !==
|
|
34534
|
+
const next = existing.filter((f2) => f2.path !== path19);
|
|
34231
34535
|
if (next.length === existing.length) return;
|
|
34232
34536
|
await this.opts.store.putSkillFiles(skillId, next);
|
|
34233
34537
|
}
|
|
@@ -34525,15 +34829,15 @@ ${input.description}
|
|
|
34525
34829
|
return env;
|
|
34526
34830
|
}
|
|
34527
34831
|
encrypt(plain) {
|
|
34528
|
-
const iv = (0,
|
|
34529
|
-
const cipher = (0,
|
|
34832
|
+
const iv = (0, import_node_crypto6.randomBytes)(12);
|
|
34833
|
+
const cipher = (0, import_node_crypto6.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
|
|
34530
34834
|
const enc4 = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
|
|
34531
34835
|
return [iv.toString("base64"), cipher.getAuthTag().toString("base64"), enc4.toString("base64")].join(".");
|
|
34532
34836
|
}
|
|
34533
34837
|
decrypt(packed) {
|
|
34534
34838
|
const [iv, tag, data] = packed.split(".");
|
|
34535
34839
|
if (!iv || !tag || typeof data !== "string") throw new Error("malformed ciphertext");
|
|
34536
|
-
const decipher = (0,
|
|
34840
|
+
const decipher = (0, import_node_crypto6.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
|
|
34537
34841
|
decipher.setAuthTag(Buffer.from(tag, "base64"));
|
|
34538
34842
|
return Buffer.concat([decipher.update(Buffer.from(data, "base64")), decipher.final()]).toString("utf8");
|
|
34539
34843
|
}
|
|
@@ -35067,7 +35371,7 @@ async function listSkillMetadataForActor(args) {
|
|
|
35067
35371
|
usedDirs.add(dir);
|
|
35068
35372
|
const sorted = [...metas].sort((a, b2) => a.path.localeCompare(b2.path));
|
|
35069
35373
|
let latest = "1970-01-01T00:00:00.000Z";
|
|
35070
|
-
const hasher = (0,
|
|
35374
|
+
const hasher = (0, import_node_crypto7.createHash)("sha256");
|
|
35071
35375
|
for (const m2 of sorted) {
|
|
35072
35376
|
if (m2.updatedAt > latest) latest = m2.updatedAt;
|
|
35073
35377
|
hasher.update(m2.path);
|
|
@@ -35090,11 +35394,11 @@ async function listSkillMetadataForActor(args) {
|
|
|
35090
35394
|
const dir = sanitizeSkillDir(b2.name || b2.id);
|
|
35091
35395
|
if (usedDirs.has(dir)) continue;
|
|
35092
35396
|
usedDirs.add(dir);
|
|
35093
|
-
const hasher = (0,
|
|
35397
|
+
const hasher = (0, import_node_crypto7.createHash)("sha256");
|
|
35094
35398
|
for (const p2 of paths) {
|
|
35095
35399
|
hasher.update(p2);
|
|
35096
35400
|
hasher.update("\0");
|
|
35097
|
-
hasher.update((0,
|
|
35401
|
+
hasher.update((0, import_node_crypto7.createHash)("sha256").update(b2.files[p2]).digest("hex"));
|
|
35098
35402
|
hasher.update("\0");
|
|
35099
35403
|
}
|
|
35100
35404
|
out.push({
|
|
@@ -35110,11 +35414,11 @@ async function listSkillMetadataForActor(args) {
|
|
|
35110
35414
|
return out;
|
|
35111
35415
|
}
|
|
35112
35416
|
function builtinSkillToFiles(b2) {
|
|
35113
|
-
return Object.entries(b2.files ?? {}).map(([
|
|
35417
|
+
return Object.entries(b2.files ?? {}).map(([path19, content]) => ({
|
|
35114
35418
|
skillId: `builtin:${b2.id}`,
|
|
35115
|
-
path:
|
|
35419
|
+
path: path19,
|
|
35116
35420
|
content,
|
|
35117
|
-
blobHash: (0,
|
|
35421
|
+
blobHash: (0, import_node_crypto7.createHash)("sha256").update(content).digest("hex"),
|
|
35118
35422
|
size: Buffer.byteLength(content, "utf8"),
|
|
35119
35423
|
updatedAt: "1970-01-01T00:00:00.000Z"
|
|
35120
35424
|
}));
|
|
@@ -35124,6 +35428,10 @@ function skillsDirForRuntime(runtimeKind) {
|
|
|
35124
35428
|
case "claude":
|
|
35125
35429
|
case "claude-code":
|
|
35126
35430
|
return ".claude/skills";
|
|
35431
|
+
case "codex":
|
|
35432
|
+
return ".codex/skills";
|
|
35433
|
+
case "cursor":
|
|
35434
|
+
return ".cursor/skills";
|
|
35127
35435
|
default:
|
|
35128
35436
|
return ".agent_context/skills";
|
|
35129
35437
|
}
|
|
@@ -35158,11 +35466,11 @@ async function materializeSkillFiles(args) {
|
|
|
35158
35466
|
}
|
|
35159
35467
|
return out;
|
|
35160
35468
|
}
|
|
35161
|
-
var
|
|
35469
|
+
var import_node_crypto7;
|
|
35162
35470
|
var init_skill_materializer = __esm({
|
|
35163
35471
|
"../server/src/domains/actors/skill-materializer.ts"() {
|
|
35164
35472
|
"use strict";
|
|
35165
|
-
|
|
35473
|
+
import_node_crypto7 = require("node:crypto");
|
|
35166
35474
|
}
|
|
35167
35475
|
});
|
|
35168
35476
|
|
|
@@ -35652,7 +35960,7 @@ function createActorsDomain(opts) {
|
|
|
35652
35960
|
...kernel !== void 0 ? { onRolesChanged: (a, r) => kernel.setActorRoles(a, r) } : {},
|
|
35653
35961
|
audit: async (entry) => {
|
|
35654
35962
|
await opts.audit?.({
|
|
35655
|
-
id: `reg_${(0,
|
|
35963
|
+
id: `reg_${(0, import_node_crypto8.randomUUID)()}`,
|
|
35656
35964
|
actor: entry.by,
|
|
35657
35965
|
kind: "registry_change",
|
|
35658
35966
|
target: entry.actorId,
|
|
@@ -35687,11 +35995,11 @@ function createActorsDomain(opts) {
|
|
|
35687
35995
|
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {} })
|
|
35688
35996
|
};
|
|
35689
35997
|
}
|
|
35690
|
-
var
|
|
35998
|
+
var import_node_crypto8;
|
|
35691
35999
|
var init_actors = __esm({
|
|
35692
36000
|
"../server/src/domains/actors/index.ts"() {
|
|
35693
36001
|
"use strict";
|
|
35694
|
-
|
|
36002
|
+
import_node_crypto8 = require("node:crypto");
|
|
35695
36003
|
init_service2();
|
|
35696
36004
|
init_routes();
|
|
35697
36005
|
init_service2();
|
|
@@ -36524,11 +36832,11 @@ var init_projects = __esm({
|
|
|
36524
36832
|
});
|
|
36525
36833
|
|
|
36526
36834
|
// ../server/src/domains/companies/service.ts
|
|
36527
|
-
var
|
|
36835
|
+
var import_node_crypto9, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, SLUG_RE, CompanyError, CompaniesService;
|
|
36528
36836
|
var init_service3 = __esm({
|
|
36529
36837
|
"../server/src/domains/companies/service.ts"() {
|
|
36530
36838
|
"use strict";
|
|
36531
|
-
|
|
36839
|
+
import_node_crypto9 = require("node:crypto");
|
|
36532
36840
|
ROLES = ["owner", "admin", "member"];
|
|
36533
36841
|
INVITABLE_ROLES = ["admin", "member"];
|
|
36534
36842
|
INVITATION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -36580,7 +36888,7 @@ var init_service3 = __esm({
|
|
|
36580
36888
|
const existing = await this.store.getAccountByEmail(normalized);
|
|
36581
36889
|
if (existing) return existing;
|
|
36582
36890
|
const account = {
|
|
36583
|
-
id: `actor:human:${(0,
|
|
36891
|
+
id: `actor:human:${(0, import_node_crypto9.randomUUID)()}`,
|
|
36584
36892
|
email: normalized,
|
|
36585
36893
|
name: name ?? normalized,
|
|
36586
36894
|
status: "active"
|
|
@@ -36625,14 +36933,14 @@ var init_service3 = __esm({
|
|
|
36625
36933
|
}
|
|
36626
36934
|
/** 新建公司:校验 slug、生成 id=company:<slug>、把创建者设为 owner。 */
|
|
36627
36935
|
async create(input, creatorAccountId) {
|
|
36628
|
-
const
|
|
36629
|
-
if (!SLUG_RE.test(
|
|
36936
|
+
const slug4 = input.slug?.trim() ?? "";
|
|
36937
|
+
if (!SLUG_RE.test(slug4)) throw new CompanyError(400, "BAD_SLUG", "slug \u987B\u4E3A URL \u5B89\u5168\u7684\u5C0F\u5199\u4E32\uFF08^[a-z][a-z0-9-]*$\uFF09");
|
|
36630
36938
|
const name = input.name?.trim() ?? "";
|
|
36631
36939
|
if (!name) throw new CompanyError(400, "BAD_REQUEST", "\u7F3A\u5C11 name");
|
|
36632
|
-
if (await this.store.getCompanyBySlug(
|
|
36940
|
+
if (await this.store.getCompanyBySlug(slug4)) throw new CompanyError(409, "SLUG_TAKEN", `slug \u5DF2\u88AB\u5360\u7528\uFF1A${slug4}`);
|
|
36633
36941
|
const company = {
|
|
36634
|
-
id: `company:${
|
|
36635
|
-
slug:
|
|
36942
|
+
id: `company:${slug4}`,
|
|
36943
|
+
slug: slug4,
|
|
36636
36944
|
name,
|
|
36637
36945
|
status: "active",
|
|
36638
36946
|
createdAt: this.now(),
|
|
@@ -36704,7 +37012,7 @@ var init_service3 = __esm({
|
|
|
36704
37012
|
}
|
|
36705
37013
|
const nowMs = Date.parse(this.now());
|
|
36706
37014
|
const invitation = {
|
|
36707
|
-
id: `invitation:${(0,
|
|
37015
|
+
id: `invitation:${(0, import_node_crypto9.randomUUID)()}`,
|
|
36708
37016
|
companyId,
|
|
36709
37017
|
email: mail,
|
|
36710
37018
|
role,
|
|
@@ -36862,9 +37170,9 @@ var init_routes3 = __esm({
|
|
|
36862
37170
|
|
|
36863
37171
|
// ../server/src/domains/companies/migrate.ts
|
|
36864
37172
|
function resolveDefaultCompanyConfig(env = process.env) {
|
|
36865
|
-
const
|
|
37173
|
+
const slug4 = env["OASIS_DEFAULT_COMPANY_SLUG"]?.trim() || "oasis";
|
|
36866
37174
|
const name = env["OASIS_DEFAULT_COMPANY_NAME"]?.trim() || "Oasis \u603B\u90E8";
|
|
36867
|
-
return { id: `company:${
|
|
37175
|
+
return { id: `company:${slug4}`, slug: slug4, name };
|
|
36868
37176
|
}
|
|
36869
37177
|
async function migrateDefaultCompany(store, actors, opts = {}) {
|
|
36870
37178
|
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -37188,7 +37496,7 @@ var init_sender = __esm({
|
|
|
37188
37496
|
function deriveBoardKey(artifactId) {
|
|
37189
37497
|
const tail = artifactId.split(":").pop() ?? artifactId;
|
|
37190
37498
|
const ascii = tail.replace(/[^a-zA-Z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 24);
|
|
37191
|
-
const sha8 = (0,
|
|
37499
|
+
const sha8 = (0, import_node_crypto10.createHash)("sha256").update(artifactId).digest("hex").slice(0, 8);
|
|
37192
37500
|
return ascii ? `od-${ascii}-${sha8}` : `od-${sha8}`;
|
|
37193
37501
|
}
|
|
37194
37502
|
async function odApi(base, fetchImpl, method, p2, body) {
|
|
@@ -37221,13 +37529,13 @@ async function ensureProject(base, fetchImpl, boardKey, title) {
|
|
|
37221
37529
|
}
|
|
37222
37530
|
function materializeContext(resolvedDir, files) {
|
|
37223
37531
|
if (!resolvedDir) return;
|
|
37224
|
-
|
|
37225
|
-
const root =
|
|
37226
|
-
|
|
37532
|
+
fs8.mkdirSync(resolvedDir, { recursive: true });
|
|
37533
|
+
const root = path7.join(resolvedDir, "task-context");
|
|
37534
|
+
fs8.rmSync(root, { recursive: true, force: true });
|
|
37227
37535
|
for (const [rel, content] of Object.entries(files)) {
|
|
37228
|
-
const f2 =
|
|
37229
|
-
|
|
37230
|
-
|
|
37536
|
+
const f2 = path7.join(root, rel);
|
|
37537
|
+
fs8.mkdirSync(path7.dirname(f2), { recursive: true });
|
|
37538
|
+
fs8.writeFileSync(f2, content);
|
|
37231
37539
|
}
|
|
37232
37540
|
}
|
|
37233
37541
|
async function startDesignRun(base, fetchImpl, args) {
|
|
@@ -37235,8 +37543,8 @@ async function startDesignRun(base, fetchImpl, args) {
|
|
|
37235
37543
|
agentId: args.agentId,
|
|
37236
37544
|
projectId: args.boardKey,
|
|
37237
37545
|
conversationId: args.conversationId,
|
|
37238
|
-
assistantMessageId: (0,
|
|
37239
|
-
clientRequestId: (0,
|
|
37546
|
+
assistantMessageId: (0, import_node_crypto10.randomUUID)(),
|
|
37547
|
+
clientRequestId: (0, import_node_crypto10.randomUUID)(),
|
|
37240
37548
|
message: args.message,
|
|
37241
37549
|
systemPrompt: args.systemPrompt
|
|
37242
37550
|
});
|
|
@@ -37289,23 +37597,23 @@ async function harvestBoard(base, fetchImpl, publicBase, workRoot, boardKey, run
|
|
|
37289
37597
|
const htmls = (listing.files ?? []).filter((f2) => f2.kind === "html" && !f2.path.startsWith("task-context/")).filter((f2) => (f2.artifactManifest?.status ?? "complete") === "complete").sort((a, b2) => (b2.mtime ?? 0) - (a.mtime ?? 0));
|
|
37290
37598
|
const entry = htmls[0]?.path;
|
|
37291
37599
|
if (!entry) throw new Error("\u753B\u677F\u9879\u76EE\u65E0\u5DF2\u5B8C\u6210\u7684 HTML \u5165\u53E3\u7A3F\u2014\u2014\u8BBE\u8BA1\u4F1A\u8BDD\u672A\u4EA7\u51FA\u53EF\u4EA4\u4ED8\u754C\u9762");
|
|
37292
|
-
const dir =
|
|
37600
|
+
const dir = fs8.mkdtempSync(path7.join(workRoot, "od-deliverable-"));
|
|
37293
37601
|
const fetchInline = async (rel) => {
|
|
37294
37602
|
const res = await fetchImpl(`${base}/api/projects/${encodeURIComponent(boardKey)}/export/${encodeURIComponent(rel)}?inline=1`);
|
|
37295
37603
|
if (!res.ok) throw new Error(`od export ${rel} \u2192 ${res.status}`);
|
|
37296
37604
|
return await res.text();
|
|
37297
37605
|
};
|
|
37298
|
-
|
|
37606
|
+
fs8.writeFileSync(path7.join(dir, "index.html"), await fetchInline(entry));
|
|
37299
37607
|
for (const f2 of listing.files ?? []) {
|
|
37300
37608
|
if (f2.path.endsWith(".md") && !f2.path.includes("/")) {
|
|
37301
37609
|
try {
|
|
37302
|
-
|
|
37610
|
+
fs8.writeFileSync(path7.join(dir, f2.path), await fetchInline(f2.path));
|
|
37303
37611
|
} catch {
|
|
37304
37612
|
}
|
|
37305
37613
|
}
|
|
37306
37614
|
}
|
|
37307
|
-
|
|
37308
|
-
|
|
37615
|
+
fs8.writeFileSync(
|
|
37616
|
+
path7.join(dir, "board.link"),
|
|
37309
37617
|
`${publicBase}/projects/${boardKey}
|
|
37310
37618
|
\u5165\u53E3\u7A3F\uFF1A${entry}
|
|
37311
37619
|
\u5FEB\u7167\u6765\u6E90 run\uFF1A${runId}
|
|
@@ -37338,14 +37646,14 @@ async function runOpenDesignSession(opts) {
|
|
|
37338
37646
|
const { dir, entry } = await harvestBoard(opts.base, fetchImpl, opts.publicBase, workRoot, boardKey, runId);
|
|
37339
37647
|
return { dir, entry, boardKey, runId };
|
|
37340
37648
|
}
|
|
37341
|
-
var
|
|
37649
|
+
var import_node_crypto10, fs8, os, path7;
|
|
37342
37650
|
var init_driver = __esm({
|
|
37343
37651
|
"../adapters/src/open-design/driver.ts"() {
|
|
37344
37652
|
"use strict";
|
|
37345
|
-
|
|
37346
|
-
|
|
37653
|
+
import_node_crypto10 = require("node:crypto");
|
|
37654
|
+
fs8 = __toESM(require("node:fs"), 1);
|
|
37347
37655
|
os = __toESM(require("node:os"), 1);
|
|
37348
|
-
|
|
37656
|
+
path7 = __toESM(require("node:path"), 1);
|
|
37349
37657
|
}
|
|
37350
37658
|
});
|
|
37351
37659
|
|
|
@@ -37471,26 +37779,26 @@ function classifyExit(result) {
|
|
|
37471
37779
|
return void 0;
|
|
37472
37780
|
}
|
|
37473
37781
|
function conversationExists(sessionId) {
|
|
37474
|
-
const configDir = process.env["CLAUDE_CONFIG_DIR"] ||
|
|
37475
|
-
const projectsDir =
|
|
37782
|
+
const configDir = process.env["CLAUDE_CONFIG_DIR"] || path8.join(os2.homedir(), ".claude");
|
|
37783
|
+
const projectsDir = path8.join(configDir, "projects");
|
|
37476
37784
|
const target = `${sessionId}.jsonl`;
|
|
37477
37785
|
try {
|
|
37478
|
-
for (const proj of
|
|
37479
|
-
if (
|
|
37786
|
+
for (const proj of fs9.readdirSync(projectsDir)) {
|
|
37787
|
+
if (fs9.existsSync(path8.join(projectsDir, proj, target))) return true;
|
|
37480
37788
|
}
|
|
37481
37789
|
} catch {
|
|
37482
37790
|
}
|
|
37483
37791
|
return false;
|
|
37484
37792
|
}
|
|
37485
|
-
var
|
|
37793
|
+
var import_node_child_process4, import_node_crypto11, fs9, os2, path8, readline, ClaudeCodeAdapter;
|
|
37486
37794
|
var init_claude_code = __esm({
|
|
37487
37795
|
"../adapters/src/claude-code/index.ts"() {
|
|
37488
37796
|
"use strict";
|
|
37489
|
-
|
|
37490
|
-
|
|
37491
|
-
|
|
37797
|
+
import_node_child_process4 = require("node:child_process");
|
|
37798
|
+
import_node_crypto11 = require("node:crypto");
|
|
37799
|
+
fs9 = __toESM(require("node:fs"), 1);
|
|
37492
37800
|
os2 = __toESM(require("node:os"), 1);
|
|
37493
|
-
|
|
37801
|
+
path8 = __toESM(require("node:path"), 1);
|
|
37494
37802
|
readline = __toESM(require("node:readline"), 1);
|
|
37495
37803
|
init_sync_skills();
|
|
37496
37804
|
ClaudeCodeAdapter = class {
|
|
@@ -37519,10 +37827,10 @@ var init_claude_code = __esm({
|
|
|
37519
37827
|
};
|
|
37520
37828
|
}
|
|
37521
37829
|
async spawn(job) {
|
|
37522
|
-
const
|
|
37523
|
-
const id = `claude-${
|
|
37524
|
-
const dir = job.runtimeSessionId ?
|
|
37525
|
-
|
|
37830
|
+
const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
37831
|
+
const id = `claude-${slug4}-${(0, import_node_crypto11.randomUUID)().slice(0, 8)}`;
|
|
37832
|
+
const dir = job.runtimeSessionId ? path8.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-chat-sessions", job.runtimeSessionId.replace(/[^a-zA-Z0-9_-]+/g, "_")) : fs9.mkdtempSync(path8.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-session-"));
|
|
37833
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
37526
37834
|
await runSyncSkills(this.opts.syncSkills, {
|
|
37527
37835
|
actorId: job.actor,
|
|
37528
37836
|
actorToken: job.actorToken,
|
|
@@ -37531,14 +37839,14 @@ var init_claude_code = __esm({
|
|
|
37531
37839
|
sessionDir: dir
|
|
37532
37840
|
});
|
|
37533
37841
|
for (const [rel, content] of Object.entries(job.bundle.files)) {
|
|
37534
|
-
const file =
|
|
37535
|
-
|
|
37536
|
-
|
|
37842
|
+
const file = path8.join(dir, rel);
|
|
37843
|
+
fs9.mkdirSync(path8.dirname(file), { recursive: true });
|
|
37844
|
+
fs9.writeFileSync(file, content);
|
|
37537
37845
|
}
|
|
37538
37846
|
for (const [rel, b64] of Object.entries(job.bundle.binaryFiles ?? {})) {
|
|
37539
|
-
const file =
|
|
37540
|
-
|
|
37541
|
-
|
|
37847
|
+
const file = path8.join(dir, rel);
|
|
37848
|
+
fs9.mkdirSync(path8.dirname(file), { recursive: true });
|
|
37849
|
+
fs9.writeFileSync(file, Buffer.from(b64, "base64"));
|
|
37542
37850
|
}
|
|
37543
37851
|
const prompt = job.bundle.files["TASK.md"];
|
|
37544
37852
|
if (!prompt) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
@@ -37559,7 +37867,7 @@ var init_claude_code = __esm({
|
|
|
37559
37867
|
...this.opts.extraArgs ?? []
|
|
37560
37868
|
];
|
|
37561
37869
|
const otelEnv = this.resolveOtelEnv(job, id);
|
|
37562
|
-
const child = (0,
|
|
37870
|
+
const child = (0, import_node_child_process4.spawn)(this.opts.claudeBin ?? "claude", args, {
|
|
37563
37871
|
cwd: dir,
|
|
37564
37872
|
env: {
|
|
37565
37873
|
// 凭证泄漏防护:剔除 pnpm/npm 脚本注入的变量——npm_lifecycle_script 等会把 serve 的启动命令
|
|
@@ -37570,7 +37878,7 @@ var init_claude_code = __esm({
|
|
|
37570
37878
|
...job.env,
|
|
37571
37879
|
// BUG-4 缓冲:抬高单响应输出 token 上限(临时缓冲,根治靠分解)
|
|
37572
37880
|
...this.opts.maxOutputTokens ? { CLAUDE_CODE_MAX_OUTPUT_TOKENS: String(this.opts.maxOutputTokens) } : {},
|
|
37573
|
-
...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) =>
|
|
37881
|
+
...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path8.dirname(p2)).join(path8.delimiter)}${path8.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
|
|
37574
37882
|
OASIS_SERVER: job.server.url,
|
|
37575
37883
|
OASIS_TOKEN: job.actorToken,
|
|
37576
37884
|
// OTel 遥测(默认关;resolveOtelEnv 决定开不开,runId 进 resource 属性自然归位到本次 run)。
|
|
@@ -37582,8 +37890,8 @@ var init_claude_code = __esm({
|
|
|
37582
37890
|
});
|
|
37583
37891
|
if (promptViaStdin) child.stdin.write(prompt);
|
|
37584
37892
|
child.stdin.end();
|
|
37585
|
-
let transcriptRef =
|
|
37586
|
-
const out =
|
|
37893
|
+
let transcriptRef = path8.join(dir, "transcript.txt");
|
|
37894
|
+
const out = fs9.createWriteStream(transcriptRef);
|
|
37587
37895
|
const telemetryCbs = [];
|
|
37588
37896
|
let eventSeq = 0;
|
|
37589
37897
|
let lastResult;
|
|
@@ -37669,14 +37977,14 @@ var init_claude_code = __esm({
|
|
|
37669
37977
|
const reason = killedByUs ? "timeout" : classifyExit(lastResult);
|
|
37670
37978
|
if (this.opts.cleanupWorkdir && !job.runtimeSessionId) {
|
|
37671
37979
|
try {
|
|
37672
|
-
const keepDir =
|
|
37673
|
-
|
|
37674
|
-
const kept =
|
|
37980
|
+
const keepDir = path8.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-transcripts");
|
|
37981
|
+
fs9.mkdirSync(keepDir, { recursive: true });
|
|
37982
|
+
const kept = path8.join(keepDir, `${id}.txt`);
|
|
37675
37983
|
const src = transcriptRef;
|
|
37676
37984
|
out.end(() => {
|
|
37677
37985
|
try {
|
|
37678
|
-
|
|
37679
|
-
|
|
37986
|
+
fs9.renameSync(src, kept);
|
|
37987
|
+
fs9.rmSync(dir, { recursive: true, force: true });
|
|
37680
37988
|
} catch {
|
|
37681
37989
|
}
|
|
37682
37990
|
});
|
|
@@ -37872,10 +38180,20 @@ var init_resilient = __esm({
|
|
|
37872
38180
|
|
|
37873
38181
|
// ../adapters/src/_core/skill-cache.ts
|
|
37874
38182
|
function defaultCacheRoot() {
|
|
37875
|
-
return
|
|
38183
|
+
return path9.join(os3.homedir(), ".oasis", "skill-cache");
|
|
37876
38184
|
}
|
|
37877
38185
|
function sessionSkillsSubdir(runtimeKind) {
|
|
37878
|
-
|
|
38186
|
+
switch (runtimeKind) {
|
|
38187
|
+
case "claude":
|
|
38188
|
+
case "claude-code":
|
|
38189
|
+
return ".claude/skills";
|
|
38190
|
+
case "codex":
|
|
38191
|
+
return ".codex/skills";
|
|
38192
|
+
case "cursor":
|
|
38193
|
+
return ".cursor/skills";
|
|
38194
|
+
default:
|
|
38195
|
+
return ".agent_context/skills";
|
|
38196
|
+
}
|
|
37879
38197
|
}
|
|
37880
38198
|
async function syncActorSkills(opts) {
|
|
37881
38199
|
const cacheRoot = opts.cacheRoot ?? defaultCacheRoot();
|
|
@@ -37883,7 +38201,7 @@ async function syncActorSkills(opts) {
|
|
|
37883
38201
|
});
|
|
37884
38202
|
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
37885
38203
|
const actorDir = pathForActor(cacheRoot, opts.actorId);
|
|
37886
|
-
const manifestPath =
|
|
38204
|
+
const manifestPath = path9.join(actorDir, "manifest.json");
|
|
37887
38205
|
await fsp.mkdir(actorDir, { recursive: true, mode: 493 });
|
|
37888
38206
|
const release = await acquireLock(cacheRoot, opts.actorId, now, log3);
|
|
37889
38207
|
try {
|
|
@@ -37901,7 +38219,7 @@ async function syncActorSkills(opts) {
|
|
|
37901
38219
|
const cloudIds = new Set(cloudMetadata.map((s2) => s2.id));
|
|
37902
38220
|
for (const cloud of cloudMetadata) {
|
|
37903
38221
|
const cached2 = manifest.skills[cloud.id];
|
|
37904
|
-
const skillDir =
|
|
38222
|
+
const skillDir = path9.join(actorDir, cloud.dir);
|
|
37905
38223
|
const inSync = cached2 && cached2.dir === cloud.dir && cached2.contentHash === cloud.contentHash && cached2.cloudUpdatedAt === cloud.updatedAt && await dirLooksValid(skillDir, cached2.fileList);
|
|
37906
38224
|
if (inSync) {
|
|
37907
38225
|
skipped += 1;
|
|
@@ -37929,7 +38247,7 @@ async function syncActorSkills(opts) {
|
|
|
37929
38247
|
for (const [id, entry] of Object.entries(manifest.skills)) {
|
|
37930
38248
|
if (cloudIds.has(id)) continue;
|
|
37931
38249
|
try {
|
|
37932
|
-
await fsp.rm(
|
|
38250
|
+
await fsp.rm(path9.join(actorDir, entry.dir), { recursive: true, force: true });
|
|
37933
38251
|
} catch (err) {
|
|
37934
38252
|
log3("warn", `\u5220\u9664\u5B64\u513F\u6280\u80FD\u76EE\u5F55\u5931\u8D25 ${entry.dir}`, err);
|
|
37935
38253
|
}
|
|
@@ -37946,14 +38264,14 @@ async function syncActorSkills(opts) {
|
|
|
37946
38264
|
}
|
|
37947
38265
|
}
|
|
37948
38266
|
function pathForActor(cacheRoot, actorId) {
|
|
37949
|
-
return
|
|
38267
|
+
return path9.join(cacheRoot, "actors", encodeURIComponent(actorId));
|
|
37950
38268
|
}
|
|
37951
38269
|
function lockPathFor(cacheRoot, actorId) {
|
|
37952
|
-
return
|
|
38270
|
+
return path9.join(cacheRoot, "locks", `${encodeURIComponent(actorId)}.lock`);
|
|
37953
38271
|
}
|
|
37954
38272
|
async function acquireLock(cacheRoot, actorId, now, log3) {
|
|
37955
38273
|
const lockPath = lockPathFor(cacheRoot, actorId);
|
|
37956
|
-
await fsp.mkdir(
|
|
38274
|
+
await fsp.mkdir(path9.dirname(lockPath), { recursive: true, mode: 493 });
|
|
37957
38275
|
const started = now().getTime();
|
|
37958
38276
|
while (true) {
|
|
37959
38277
|
try {
|
|
@@ -38019,26 +38337,26 @@ async function readManifest2(manifestPath, actorId, log3) {
|
|
|
38019
38337
|
};
|
|
38020
38338
|
}
|
|
38021
38339
|
async function writeManifest(manifestPath, manifest) {
|
|
38022
|
-
const dir =
|
|
38340
|
+
const dir = path9.dirname(manifestPath);
|
|
38023
38341
|
await fsp.mkdir(dir, { recursive: true });
|
|
38024
38342
|
const tmp = `${manifestPath}.${process.pid}.${Date.now()}.tmp`;
|
|
38025
38343
|
await fsp.writeFile(tmp, JSON.stringify(manifest, null, 2), { mode: 420 });
|
|
38026
38344
|
await fsp.rename(tmp, manifestPath);
|
|
38027
38345
|
}
|
|
38028
38346
|
async function materializeSkillDir(actorDir, targetDir, files, previousDir) {
|
|
38029
|
-
const targetPath =
|
|
38030
|
-
const tmpPath =
|
|
38031
|
-
const oldBackup =
|
|
38347
|
+
const targetPath = path9.join(actorDir, targetDir);
|
|
38348
|
+
const tmpPath = path9.join(actorDir, `${targetDir}.new.${process.pid}.${Date.now()}`);
|
|
38349
|
+
const oldBackup = path9.join(actorDir, `${targetDir}.old.${process.pid}.${Date.now()}`);
|
|
38032
38350
|
await fsp.rm(tmpPath, { recursive: true, force: true });
|
|
38033
38351
|
await fsp.mkdir(tmpPath, { recursive: true, mode: 493 });
|
|
38034
38352
|
for (const f2 of files) {
|
|
38035
38353
|
if (!f2.content) continue;
|
|
38036
|
-
const dest =
|
|
38037
|
-
const rel =
|
|
38038
|
-
if (rel.startsWith("..") ||
|
|
38354
|
+
const dest = path9.join(tmpPath, f2.path);
|
|
38355
|
+
const rel = path9.relative(tmpPath, dest);
|
|
38356
|
+
if (rel.startsWith("..") || path9.isAbsolute(rel)) {
|
|
38039
38357
|
throw new Error(`SkillFile.path \u4E0D\u5408\u6CD5: ${f2.path}`);
|
|
38040
38358
|
}
|
|
38041
|
-
await fsp.mkdir(
|
|
38359
|
+
await fsp.mkdir(path9.dirname(dest), { recursive: true });
|
|
38042
38360
|
await fsp.writeFile(dest, f2.content, { mode: 420 });
|
|
38043
38361
|
}
|
|
38044
38362
|
let hadOld = false;
|
|
@@ -38052,14 +38370,14 @@ async function materializeSkillDir(actorDir, targetDir, files, previousDir) {
|
|
|
38052
38370
|
await fsp.rename(tmpPath, targetPath);
|
|
38053
38371
|
if (hadOld) await fsp.rm(oldBackup, { recursive: true, force: true });
|
|
38054
38372
|
if (previousDir && previousDir !== targetDir) {
|
|
38055
|
-
await fsp.rm(
|
|
38373
|
+
await fsp.rm(path9.join(actorDir, previousDir), { recursive: true, force: true });
|
|
38056
38374
|
}
|
|
38057
38375
|
}
|
|
38058
38376
|
async function dirLooksValid(dir, expectFileList) {
|
|
38059
38377
|
if (expectFileList.length === 0) return false;
|
|
38060
38378
|
try {
|
|
38061
38379
|
for (const rel of expectFileList) {
|
|
38062
|
-
await fsp.stat(
|
|
38380
|
+
await fsp.stat(path9.join(dir, rel));
|
|
38063
38381
|
}
|
|
38064
38382
|
return true;
|
|
38065
38383
|
} catch {
|
|
@@ -38068,18 +38386,18 @@ async function dirLooksValid(dir, expectFileList) {
|
|
|
38068
38386
|
}
|
|
38069
38387
|
async function syncSessionSymlinks(sessionDir, runtimeKind, actorDir, manifest, log3) {
|
|
38070
38388
|
const subdir = sessionSkillsSubdir(runtimeKind);
|
|
38071
|
-
const skillsDir =
|
|
38389
|
+
const skillsDir = path9.join(sessionDir, subdir);
|
|
38072
38390
|
await fsp.mkdir(skillsDir, { recursive: true, mode: 493 });
|
|
38073
38391
|
const desired = /* @__PURE__ */ new Map();
|
|
38074
38392
|
for (const entry of Object.values(manifest.skills)) {
|
|
38075
|
-
desired.set(entry.dir,
|
|
38393
|
+
desired.set(entry.dir, path9.join(actorDir, entry.dir));
|
|
38076
38394
|
}
|
|
38077
38395
|
for (const [name, target] of desired) {
|
|
38078
|
-
const link =
|
|
38396
|
+
const link = path9.join(skillsDir, name);
|
|
38079
38397
|
let ok = false;
|
|
38080
38398
|
try {
|
|
38081
38399
|
const current = await fsp.readlink(link);
|
|
38082
|
-
ok =
|
|
38400
|
+
ok = path9.resolve(skillsDir, current) === target;
|
|
38083
38401
|
} catch {
|
|
38084
38402
|
}
|
|
38085
38403
|
if (!ok) {
|
|
@@ -38099,19 +38417,19 @@ async function syncSessionSymlinks(sessionDir, runtimeKind, actorDir, manifest,
|
|
|
38099
38417
|
for (const name of entries) {
|
|
38100
38418
|
if (desired.has(name)) continue;
|
|
38101
38419
|
try {
|
|
38102
|
-
await fsp.rm(
|
|
38420
|
+
await fsp.rm(path9.join(skillsDir, name), { recursive: true, force: true });
|
|
38103
38421
|
} catch (err) {
|
|
38104
38422
|
log3("warn", `\u6E05\u5B64\u513F\u5931\u8D25 ${name}`, err);
|
|
38105
38423
|
}
|
|
38106
38424
|
}
|
|
38107
38425
|
}
|
|
38108
|
-
var fsp, os3,
|
|
38426
|
+
var fsp, os3, path9, MANIFEST_SCHEMA_VERSION, STALE_LOCK_MS, LOCK_ACQUIRE_TIMEOUT_MS, LOCK_POLL_MS;
|
|
38109
38427
|
var init_skill_cache = __esm({
|
|
38110
38428
|
"../adapters/src/_core/skill-cache.ts"() {
|
|
38111
38429
|
"use strict";
|
|
38112
38430
|
fsp = __toESM(require("node:fs/promises"), 1);
|
|
38113
38431
|
os3 = __toESM(require("node:os"), 1);
|
|
38114
|
-
|
|
38432
|
+
path9 = __toESM(require("node:path"), 1);
|
|
38115
38433
|
MANIFEST_SCHEMA_VERSION = 1;
|
|
38116
38434
|
STALE_LOCK_MS = 6e4;
|
|
38117
38435
|
LOCK_ACQUIRE_TIMEOUT_MS = 3e4;
|
|
@@ -38295,7 +38613,7 @@ async function discoverAntigravityModels(bin) {
|
|
|
38295
38613
|
async function discoverACPModels(bin, provider, args = ["acp"]) {
|
|
38296
38614
|
const child = (() => {
|
|
38297
38615
|
try {
|
|
38298
|
-
return (0,
|
|
38616
|
+
return (0, import_node_child_process5.spawn)(bin, args, { stdio: ["pipe", "pipe", "ignore"], env: { ...process.env } });
|
|
38299
38617
|
} catch {
|
|
38300
38618
|
return null;
|
|
38301
38619
|
}
|
|
@@ -38401,11 +38719,11 @@ function parseACPSessionNewModels(result) {
|
|
|
38401
38719
|
async function discoverOpenclawModels(bin) {
|
|
38402
38720
|
throw new Error("discoverOpenclawModels: TODO");
|
|
38403
38721
|
}
|
|
38404
|
-
var
|
|
38722
|
+
var import_node_child_process5, import_node_fs2, import_node_os, import_node_path, modelCache, CACHE_TTL_MS2;
|
|
38405
38723
|
var init_models = __esm({
|
|
38406
38724
|
"../adapters/src/_core/models.ts"() {
|
|
38407
38725
|
"use strict";
|
|
38408
|
-
|
|
38726
|
+
import_node_child_process5 = require("node:child_process");
|
|
38409
38727
|
import_node_fs2 = require("node:fs");
|
|
38410
38728
|
import_node_os = require("node:os");
|
|
38411
38729
|
import_node_path = require("node:path");
|
|
@@ -38480,12 +38798,12 @@ function parseCodexModel(lines) {
|
|
|
38480
38798
|
function codexSessionsRoot() {
|
|
38481
38799
|
const candidates = [];
|
|
38482
38800
|
const ch = process.env["CODEX_HOME"];
|
|
38483
|
-
if (ch) candidates.push(
|
|
38801
|
+
if (ch) candidates.push(path10.join(ch, "sessions"));
|
|
38484
38802
|
const home = process.env["HOME"] || os4.homedir();
|
|
38485
|
-
if (home) candidates.push(
|
|
38803
|
+
if (home) candidates.push(path10.join(home, ".codex", "sessions"));
|
|
38486
38804
|
for (const c of candidates) {
|
|
38487
38805
|
try {
|
|
38488
|
-
if (
|
|
38806
|
+
if (fs10.statSync(c).isDirectory()) return c;
|
|
38489
38807
|
} catch {
|
|
38490
38808
|
}
|
|
38491
38809
|
}
|
|
@@ -38499,13 +38817,13 @@ function codexRolloutExists(sessionId, root = codexSessionsRoot()) {
|
|
|
38499
38817
|
if (hit) return;
|
|
38500
38818
|
let ents;
|
|
38501
38819
|
try {
|
|
38502
|
-
ents =
|
|
38820
|
+
ents = fs10.readdirSync(d, { withFileTypes: true });
|
|
38503
38821
|
} catch {
|
|
38504
38822
|
return;
|
|
38505
38823
|
}
|
|
38506
38824
|
for (const ent of ents) {
|
|
38507
38825
|
if (hit) return;
|
|
38508
|
-
const p2 =
|
|
38826
|
+
const p2 = path10.join(d, ent.name);
|
|
38509
38827
|
if (ent.isDirectory()) walk(p2);
|
|
38510
38828
|
else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(suffix)) {
|
|
38511
38829
|
hit = true;
|
|
@@ -38522,17 +38840,17 @@ function listRecentRollouts(root, sinceMs) {
|
|
|
38522
38840
|
const walk = (d) => {
|
|
38523
38841
|
let ents;
|
|
38524
38842
|
try {
|
|
38525
|
-
ents =
|
|
38843
|
+
ents = fs10.readdirSync(d, { withFileTypes: true });
|
|
38526
38844
|
} catch {
|
|
38527
38845
|
return;
|
|
38528
38846
|
}
|
|
38529
38847
|
for (const ent of ents) {
|
|
38530
|
-
const p2 =
|
|
38848
|
+
const p2 = path10.join(d, ent.name);
|
|
38531
38849
|
if (ent.isDirectory()) walk(p2);
|
|
38532
38850
|
else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(".jsonl")) {
|
|
38533
38851
|
let m2 = 0;
|
|
38534
38852
|
try {
|
|
38535
|
-
m2 =
|
|
38853
|
+
m2 = fs10.statSync(p2).mtimeMs;
|
|
38536
38854
|
} catch {
|
|
38537
38855
|
}
|
|
38538
38856
|
if (m2 >= margin) found.push({ f: p2, m: m2 });
|
|
@@ -38557,7 +38875,7 @@ function scanCodexTelemetry(workdir, sinceMs, sessionsRoot = codexSessionsRoot()
|
|
|
38557
38875
|
const files = listRecentRollouts(sessionsRoot, sinceMs);
|
|
38558
38876
|
const readLines = (f2) => {
|
|
38559
38877
|
try {
|
|
38560
|
-
return
|
|
38878
|
+
return fs10.readFileSync(f2, "utf8").split("\n");
|
|
38561
38879
|
} catch {
|
|
38562
38880
|
return null;
|
|
38563
38881
|
}
|
|
@@ -38757,15 +39075,15 @@ function normalizeCodexConsoleLine(line, state = createCodexNormalizeState()) {
|
|
|
38757
39075
|
tool.output.push(line);
|
|
38758
39076
|
return [];
|
|
38759
39077
|
}
|
|
38760
|
-
var
|
|
39078
|
+
var import_node_child_process6, import_node_crypto12, fs10, os4, path10, readline2, CodexAdapter;
|
|
38761
39079
|
var init_codex = __esm({
|
|
38762
39080
|
"../adapters/src/codex/index.ts"() {
|
|
38763
39081
|
"use strict";
|
|
38764
|
-
|
|
38765
|
-
|
|
38766
|
-
|
|
39082
|
+
import_node_child_process6 = require("node:child_process");
|
|
39083
|
+
import_node_crypto12 = require("node:crypto");
|
|
39084
|
+
fs10 = __toESM(require("node:fs"), 1);
|
|
38767
39085
|
os4 = __toESM(require("node:os"), 1);
|
|
38768
|
-
|
|
39086
|
+
path10 = __toESM(require("node:path"), 1);
|
|
38769
39087
|
readline2 = __toESM(require("node:readline"), 1);
|
|
38770
39088
|
init_sync_skills();
|
|
38771
39089
|
init_claude_code();
|
|
@@ -38774,10 +39092,10 @@ var init_codex = __esm({
|
|
|
38774
39092
|
this.opts = opts;
|
|
38775
39093
|
}
|
|
38776
39094
|
async spawn(job) {
|
|
38777
|
-
const
|
|
38778
|
-
const id = `codex-${
|
|
39095
|
+
const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
39096
|
+
const id = `codex-${slug4}-${(0, import_node_crypto12.randomUUID)().slice(0, 8)}`;
|
|
38779
39097
|
const startedAtMs = Date.now();
|
|
38780
|
-
const dir =
|
|
39098
|
+
const dir = fs10.mkdtempSync(path10.join(this.opts.workRoot ?? os4.tmpdir(), "oasis-session-"));
|
|
38781
39099
|
await runSyncSkills(this.opts.syncSkills, {
|
|
38782
39100
|
actorId: job.actor,
|
|
38783
39101
|
actorToken: job.actorToken,
|
|
@@ -38786,9 +39104,9 @@ var init_codex = __esm({
|
|
|
38786
39104
|
sessionDir: dir
|
|
38787
39105
|
});
|
|
38788
39106
|
for (const [rel, content] of Object.entries(job.bundle.files)) {
|
|
38789
|
-
const file =
|
|
38790
|
-
|
|
38791
|
-
|
|
39107
|
+
const file = path10.join(dir, rel);
|
|
39108
|
+
fs10.mkdirSync(path10.dirname(file), { recursive: true });
|
|
39109
|
+
fs10.writeFileSync(file, content);
|
|
38792
39110
|
}
|
|
38793
39111
|
const task = job.bundle.files["TASK.md"];
|
|
38794
39112
|
if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
@@ -38811,20 +39129,20 @@ var init_codex = __esm({
|
|
|
38811
39129
|
...this.opts.extraArgs ?? [],
|
|
38812
39130
|
prompt
|
|
38813
39131
|
];
|
|
38814
|
-
const child = (0,
|
|
39132
|
+
const child = (0, import_node_child_process6.spawn)(this.opts.codexBin ?? "codex", args, {
|
|
38815
39133
|
cwd: dir,
|
|
38816
39134
|
env: {
|
|
38817
39135
|
...scrubLeakyEnv(process.env),
|
|
38818
39136
|
...this.opts.env,
|
|
38819
39137
|
...job.env,
|
|
38820
|
-
...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) =>
|
|
39138
|
+
...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path10.dirname(p2)).join(path10.delimiter)}${path10.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
|
|
38821
39139
|
OASIS_SERVER: job.server.url,
|
|
38822
39140
|
OASIS_TOKEN: job.actorToken
|
|
38823
39141
|
},
|
|
38824
39142
|
stdio: ["ignore", "pipe", "pipe"]
|
|
38825
39143
|
});
|
|
38826
|
-
let transcriptRef =
|
|
38827
|
-
const out =
|
|
39144
|
+
let transcriptRef = path10.join(dir, "transcript.txt");
|
|
39145
|
+
const out = fs10.createWriteStream(transcriptRef);
|
|
38828
39146
|
const outputCbs = [];
|
|
38829
39147
|
const telemetryCbs = [];
|
|
38830
39148
|
const normalizeState = createCodexNormalizeState();
|
|
@@ -38890,14 +39208,14 @@ var init_codex = __esm({
|
|
|
38890
39208
|
if (exited) return;
|
|
38891
39209
|
if (this.opts.cleanupWorkdir) {
|
|
38892
39210
|
try {
|
|
38893
|
-
const keepDir =
|
|
38894
|
-
|
|
38895
|
-
const kept =
|
|
39211
|
+
const keepDir = path10.join(this.opts.workRoot ?? os4.tmpdir(), "oasis-transcripts");
|
|
39212
|
+
fs10.mkdirSync(keepDir, { recursive: true });
|
|
39213
|
+
const kept = path10.join(keepDir, `${id}.txt`);
|
|
38896
39214
|
const src = transcriptRef;
|
|
38897
39215
|
out.end(() => {
|
|
38898
39216
|
try {
|
|
38899
|
-
|
|
38900
|
-
|
|
39217
|
+
fs10.renameSync(src, kept);
|
|
39218
|
+
fs10.rmSync(dir, { recursive: true, force: true });
|
|
38901
39219
|
} catch {
|
|
38902
39220
|
}
|
|
38903
39221
|
});
|
|
@@ -39486,10 +39804,10 @@ function buildReportedUsage(acc, model, inputTokensIncludeCacheRead) {
|
|
|
39486
39804
|
return buildUsage(normalized, cost);
|
|
39487
39805
|
}
|
|
39488
39806
|
async function runACPSession(job, cfg) {
|
|
39489
|
-
const
|
|
39490
|
-
const binTag =
|
|
39491
|
-
const id = `${binTag}-${
|
|
39492
|
-
const dir =
|
|
39807
|
+
const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
39808
|
+
const binTag = path11.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
|
|
39809
|
+
const id = `${binTag}-${slug4}-${(0, import_node_crypto13.randomUUID)().slice(0, 8)}`;
|
|
39810
|
+
const dir = fs11.mkdtempSync(path11.join(cfg.workRoot ?? os5.tmpdir(), "oasis-session-"));
|
|
39493
39811
|
if (cfg.runtimeKind) {
|
|
39494
39812
|
await runSyncSkills(cfg.syncSkills, {
|
|
39495
39813
|
actorId: job.actor,
|
|
@@ -39500,27 +39818,29 @@ async function runACPSession(job, cfg) {
|
|
|
39500
39818
|
});
|
|
39501
39819
|
}
|
|
39502
39820
|
for (const [rel, content] of Object.entries(job.bundle.files)) {
|
|
39503
|
-
const file =
|
|
39504
|
-
|
|
39505
|
-
|
|
39821
|
+
const file = path11.join(dir, rel);
|
|
39822
|
+
fs11.mkdirSync(path11.dirname(file), { recursive: true });
|
|
39823
|
+
fs11.writeFileSync(file, content);
|
|
39506
39824
|
}
|
|
39507
39825
|
const task = job.bundle.files["TASK.md"];
|
|
39508
39826
|
if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
39509
|
-
const child = (0,
|
|
39827
|
+
const child = (0, import_node_child_process7.spawn)(cfg.bin, [...cfg.args ?? [], ...cfg.extraArgs ?? []], {
|
|
39510
39828
|
cwd: dir,
|
|
39511
39829
|
env: {
|
|
39512
39830
|
...scrubLeakyEnv(process.env),
|
|
39513
39831
|
...cfg.env,
|
|
39514
39832
|
...job.env,
|
|
39515
39833
|
...cfg.yoloEnv ?? {},
|
|
39516
|
-
...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) =>
|
|
39834
|
+
...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path11.dirname(p2)).join(path11.delimiter)}${path11.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
|
|
39835
|
+
// 每会话技能目录绝对路径 → env(供不扫 cwd 的 runtime 经 config external_dirs 发现,见 injectSkillsDirEnv)。
|
|
39836
|
+
...cfg.injectSkillsDirEnv ? { [cfg.injectSkillsDirEnv]: path11.join(dir, sessionSkillsSubdir(cfg.runtimeKind ?? "")) } : {},
|
|
39517
39837
|
OASIS_SERVER: job.server.url,
|
|
39518
39838
|
OASIS_TOKEN: job.actorToken
|
|
39519
39839
|
},
|
|
39520
39840
|
stdio: ["pipe", "pipe", "pipe"]
|
|
39521
39841
|
});
|
|
39522
|
-
let transcriptRef =
|
|
39523
|
-
const transcriptOut =
|
|
39842
|
+
let transcriptRef = path11.join(dir, "transcript.txt");
|
|
39843
|
+
const transcriptOut = fs11.createWriteStream(transcriptRef);
|
|
39524
39844
|
const outputCbs = [];
|
|
39525
39845
|
const telemetryCbs = [];
|
|
39526
39846
|
const exitCbs = [];
|
|
@@ -39537,14 +39857,14 @@ async function runACPSession(job, cfg) {
|
|
|
39537
39857
|
if (capturedSessionId && !info.runtimeSessionId) info = { ...info, runtimeSessionId: capturedSessionId };
|
|
39538
39858
|
if (cfg.cleanupWorkdir) {
|
|
39539
39859
|
try {
|
|
39540
|
-
const keepDir =
|
|
39541
|
-
|
|
39542
|
-
const kept =
|
|
39860
|
+
const keepDir = path11.join(cfg.workRoot ?? os5.tmpdir(), "oasis-transcripts");
|
|
39861
|
+
fs11.mkdirSync(keepDir, { recursive: true });
|
|
39862
|
+
const kept = path11.join(keepDir, `${id}.txt`);
|
|
39543
39863
|
const src = transcriptRef;
|
|
39544
39864
|
transcriptOut.end(() => {
|
|
39545
39865
|
try {
|
|
39546
|
-
|
|
39547
|
-
|
|
39866
|
+
fs11.renameSync(src, kept);
|
|
39867
|
+
fs11.rmSync(dir, { recursive: true, force: true });
|
|
39548
39868
|
} catch {
|
|
39549
39869
|
}
|
|
39550
39870
|
});
|
|
@@ -39745,17 +40065,18 @@ ${task}` : task;
|
|
|
39745
40065
|
}
|
|
39746
40066
|
};
|
|
39747
40067
|
}
|
|
39748
|
-
var
|
|
40068
|
+
var import_node_child_process7, import_node_crypto13, fs11, os5, path11, readline3, ACPClient;
|
|
39749
40069
|
var init_acp = __esm({
|
|
39750
40070
|
"../adapters/src/_core/acp.ts"() {
|
|
39751
40071
|
"use strict";
|
|
39752
|
-
|
|
39753
|
-
|
|
39754
|
-
|
|
40072
|
+
import_node_child_process7 = require("node:child_process");
|
|
40073
|
+
import_node_crypto13 = require("node:crypto");
|
|
40074
|
+
fs11 = __toESM(require("node:fs"), 1);
|
|
39755
40075
|
os5 = __toESM(require("node:os"), 1);
|
|
39756
|
-
|
|
40076
|
+
path11 = __toESM(require("node:path"), 1);
|
|
39757
40077
|
readline3 = __toESM(require("node:readline"), 1);
|
|
39758
40078
|
init_sync_skills();
|
|
40079
|
+
init_skill_cache();
|
|
39759
40080
|
init_claude_code();
|
|
39760
40081
|
init_usage();
|
|
39761
40082
|
init_pricing();
|
|
@@ -40028,6 +40349,11 @@ var init_hermes = __esm({
|
|
|
40028
40349
|
},
|
|
40029
40350
|
extraArgs: this.opts.extraArgs,
|
|
40030
40351
|
runtimeKind: "hermes",
|
|
40352
|
+
// hermes 不扫 cwd 技能目录(只认 $HERMES_HOME/skills + config external_dirs)。把本会话
|
|
40353
|
+
// 技能目录绝对路径喂到 OASIS_HERMES_SKILLS_DIR;node enroll 已把 config.yaml 的
|
|
40354
|
+
// skills.external_dirs 配成该 env → hermes 每 session 发现各自技能。见
|
|
40355
|
+
// packages/adapters/docs/modules/07-skills-delivery.md 与 connect-script.ts。
|
|
40356
|
+
injectSkillsDirEnv: "OASIS_HERMES_SKILLS_DIR",
|
|
40031
40357
|
...this.opts.syncSkills ? { syncSkills: this.opts.syncSkills } : {}
|
|
40032
40358
|
});
|
|
40033
40359
|
}
|
|
@@ -40087,9 +40413,9 @@ var init_kiro = __esm({
|
|
|
40087
40413
|
|
|
40088
40414
|
// ../adapters/src/_core/subprocess.ts
|
|
40089
40415
|
async function materialize(job, cfg) {
|
|
40090
|
-
const
|
|
40091
|
-
const id = `${
|
|
40092
|
-
const dir =
|
|
40416
|
+
const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
40417
|
+
const id = `${slug4}-${(0, import_node_crypto14.randomUUID)().slice(0, 8)}`;
|
|
40418
|
+
const dir = fs12.mkdtempSync(path12.join(cfg.workRoot ?? os6.tmpdir(), "oasis-session-"));
|
|
40093
40419
|
if (cfg.runtimeKind) {
|
|
40094
40420
|
await runSyncSkills(cfg.syncSkills, {
|
|
40095
40421
|
actorId: job.actor,
|
|
@@ -40100,9 +40426,9 @@ async function materialize(job, cfg) {
|
|
|
40100
40426
|
});
|
|
40101
40427
|
}
|
|
40102
40428
|
for (const [rel, content] of Object.entries(job.bundle.files)) {
|
|
40103
|
-
const file =
|
|
40104
|
-
|
|
40105
|
-
|
|
40429
|
+
const file = path12.join(dir, rel);
|
|
40430
|
+
fs12.mkdirSync(path12.dirname(file), { recursive: true });
|
|
40431
|
+
fs12.writeFileSync(file, content);
|
|
40106
40432
|
}
|
|
40107
40433
|
const task = job.bundle.files["TASK.md"];
|
|
40108
40434
|
if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
@@ -40113,7 +40439,7 @@ function buildEnv(job, extra) {
|
|
|
40113
40439
|
...scrubLeakyEnv(process.env),
|
|
40114
40440
|
...extra,
|
|
40115
40441
|
...job.env,
|
|
40116
|
-
...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) =>
|
|
40442
|
+
...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path12.dirname(p2)).join(path12.delimiter)}${path12.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
|
|
40117
40443
|
OASIS_SERVER: job.server.url,
|
|
40118
40444
|
OASIS_TOKEN: job.actorToken
|
|
40119
40445
|
};
|
|
@@ -40125,14 +40451,14 @@ function makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs) {
|
|
|
40125
40451
|
if (cfg.model && !info.model) info = { ...info, model: cfg.model };
|
|
40126
40452
|
if (cfg.cleanupWorkdir) {
|
|
40127
40453
|
try {
|
|
40128
|
-
const keepDir =
|
|
40129
|
-
|
|
40130
|
-
const kept =
|
|
40454
|
+
const keepDir = path12.join(cfg.workRoot ?? os6.tmpdir(), "oasis-transcripts");
|
|
40455
|
+
fs12.mkdirSync(keepDir, { recursive: true });
|
|
40456
|
+
const kept = path12.join(keepDir, `${id}.txt`);
|
|
40131
40457
|
const src = transcriptRefPtr.value;
|
|
40132
40458
|
transcriptOut.end(() => {
|
|
40133
40459
|
try {
|
|
40134
|
-
|
|
40135
|
-
|
|
40460
|
+
fs12.renameSync(src, kept);
|
|
40461
|
+
fs12.rmSync(dir, { recursive: true, force: true });
|
|
40136
40462
|
} catch {
|
|
40137
40463
|
}
|
|
40138
40464
|
});
|
|
@@ -40149,13 +40475,13 @@ function makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs) {
|
|
|
40149
40475
|
async function runStreamJson(job, cfg) {
|
|
40150
40476
|
const { id, dir, task: _task } = await materialize(job, cfg);
|
|
40151
40477
|
const args = [...cfg.buildArgs(job, dir), ...cfg.extraArgs ?? []];
|
|
40152
|
-
const child = (0,
|
|
40478
|
+
const child = (0, import_node_child_process8.spawn)(cfg.bin, args, {
|
|
40153
40479
|
cwd: dir,
|
|
40154
40480
|
env: buildEnv(job, cfg.env),
|
|
40155
40481
|
stdio: ["ignore", "pipe", "pipe"]
|
|
40156
40482
|
});
|
|
40157
|
-
const transcriptRefPtr = { value:
|
|
40158
|
-
const transcriptOut =
|
|
40483
|
+
const transcriptRefPtr = { value: path12.join(dir, "transcript.txt") };
|
|
40484
|
+
const transcriptOut = fs12.createWriteStream(transcriptRefPtr.value);
|
|
40159
40485
|
const outputCbs = [];
|
|
40160
40486
|
const telemetryCbs = [];
|
|
40161
40487
|
const exitCbs = [];
|
|
@@ -40225,13 +40551,13 @@ async function runStreamJson(job, cfg) {
|
|
|
40225
40551
|
async function runOneShotText(job, cfg) {
|
|
40226
40552
|
const { id, dir } = await materialize(job, cfg);
|
|
40227
40553
|
const args = [...cfg.buildArgs(job, dir), ...cfg.extraArgs ?? []];
|
|
40228
|
-
const child = (0,
|
|
40554
|
+
const child = (0, import_node_child_process8.spawn)(cfg.bin, args, {
|
|
40229
40555
|
cwd: dir,
|
|
40230
40556
|
env: buildEnv(job, cfg.env),
|
|
40231
40557
|
stdio: ["ignore", "pipe", "pipe"]
|
|
40232
40558
|
});
|
|
40233
|
-
const transcriptRefPtr = { value:
|
|
40234
|
-
const transcriptOut =
|
|
40559
|
+
const transcriptRefPtr = { value: path12.join(dir, "transcript.txt") };
|
|
40560
|
+
const transcriptOut = fs12.createWriteStream(transcriptRefPtr.value);
|
|
40235
40561
|
const outputCbs = [];
|
|
40236
40562
|
const exitCbs = [];
|
|
40237
40563
|
const { finish, exited } = makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs);
|
|
@@ -40288,15 +40614,15 @@ async function runOneShotText(job, cfg) {
|
|
|
40288
40614
|
}
|
|
40289
40615
|
});
|
|
40290
40616
|
}
|
|
40291
|
-
var
|
|
40617
|
+
var import_node_child_process8, import_node_crypto14, fs12, os6, path12, readline4;
|
|
40292
40618
|
var init_subprocess = __esm({
|
|
40293
40619
|
"../adapters/src/_core/subprocess.ts"() {
|
|
40294
40620
|
"use strict";
|
|
40295
|
-
|
|
40296
|
-
|
|
40297
|
-
|
|
40621
|
+
import_node_child_process8 = require("node:child_process");
|
|
40622
|
+
import_node_crypto14 = require("node:crypto");
|
|
40623
|
+
fs12 = __toESM(require("node:fs"), 1);
|
|
40298
40624
|
os6 = __toESM(require("node:os"), 1);
|
|
40299
|
-
|
|
40625
|
+
path12 = __toESM(require("node:path"), 1);
|
|
40300
40626
|
readline4 = __toESM(require("node:readline"), 1);
|
|
40301
40627
|
init_claude_code();
|
|
40302
40628
|
init_sync_skills();
|
|
@@ -40703,11 +41029,11 @@ function normalizeOpenClaw(line) {
|
|
|
40703
41029
|
}
|
|
40704
41030
|
return [];
|
|
40705
41031
|
}
|
|
40706
|
-
var
|
|
41032
|
+
var import_node_crypto15, OpenClawAdapter;
|
|
40707
41033
|
var init_openclaw = __esm({
|
|
40708
41034
|
"../adapters/src/openclaw/index.ts"() {
|
|
40709
41035
|
"use strict";
|
|
40710
|
-
|
|
41036
|
+
import_node_crypto15 = require("node:crypto");
|
|
40711
41037
|
init_subprocess();
|
|
40712
41038
|
OpenClawAdapter = class {
|
|
40713
41039
|
constructor(opts = {}) {
|
|
@@ -40725,7 +41051,7 @@ var init_openclaw = __esm({
|
|
|
40725
41051
|
---
|
|
40726
41052
|
|
|
40727
41053
|
${task}` : task;
|
|
40728
|
-
const sessionId = job2.runtimeSessionId ?? `oasis-${(0,
|
|
41054
|
+
const sessionId = job2.runtimeSessionId ?? `oasis-${(0, import_node_crypto15.randomUUID)().slice(0, 8)}`;
|
|
40729
41055
|
return [
|
|
40730
41056
|
"agent",
|
|
40731
41057
|
...opts.mode !== "gateway" ? ["--local"] : [],
|
|
@@ -40846,6 +41172,45 @@ if command -v hermes &>/dev/null; then
|
|
|
40846
41172
|
"$HERMES_PIP" install 'agent-client-protocol>=0.9.0,<1.0'
|
|
40847
41173
|
fi
|
|
40848
41174
|
fi
|
|
41175
|
+
# Oasis \u6BCF\u4F1A\u8BDD\u6280\u80FD\uFF1Ahermes \u4E0D\u626B cwd\uFF0C\u53EA\u8BA4 $HERMES_HOME/skills + config skills.external_dirs\u3002
|
|
41176
|
+
# \u5E42\u7B49\u5730\u628A config.yaml \u7684 external_dirs \u914D\u6210\u5B57\u9762\u91CF $OASIS_HERMES_SKILLS_DIR\uFF08\u7531 hermes \u8FD0\u884C\u65F6
|
|
41177
|
+
# \u6309 env \u5C55\u5F00\uFF1Badapter \u6BCF\u4F1A\u8BDD\u628A\u8BE5 env \u6307\u5411\u672C workdir \u7684\u6280\u80FD\u76EE\u5F55 \u2192 \u6BCF session \u5404\u81EA\u53D1\u73B0\uFF0C\u8BFB\u4FA7\u9694\u79BB\uFF09\u3002
|
|
41178
|
+
HERMES_HOME_DIR="$HERMES_HOME"
|
|
41179
|
+
if [ -z "$HERMES_HOME_DIR" ]; then HERMES_HOME_DIR="$HOME/.hermes"; fi
|
|
41180
|
+
"$HERMES_PY" - "$HERMES_HOME_DIR/config.yaml" <<'PYEOF' || true
|
|
41181
|
+
import sys, os
|
|
41182
|
+
try:
|
|
41183
|
+
import yaml
|
|
41184
|
+
except Exception:
|
|
41185
|
+
sys.exit(0)
|
|
41186
|
+
p = sys.argv[1]
|
|
41187
|
+
data = {}
|
|
41188
|
+
if os.path.exists(p):
|
|
41189
|
+
try:
|
|
41190
|
+
with open(p) as f:
|
|
41191
|
+
data = yaml.safe_load(f) or {}
|
|
41192
|
+
except Exception:
|
|
41193
|
+
sys.exit(0)
|
|
41194
|
+
if not isinstance(data, dict):
|
|
41195
|
+
sys.exit(0)
|
|
41196
|
+
skills = data.get("skills")
|
|
41197
|
+
if not isinstance(skills, dict):
|
|
41198
|
+
skills = {}
|
|
41199
|
+
data["skills"] = skills
|
|
41200
|
+
ext = skills.get("external_dirs")
|
|
41201
|
+
if not isinstance(ext, list):
|
|
41202
|
+
ext = [] if not ext else [ext]
|
|
41203
|
+
skills["external_dirs"] = ext
|
|
41204
|
+
token = "$OASIS_HERMES_SKILLS_DIR"
|
|
41205
|
+
if token not in ext:
|
|
41206
|
+
ext.append(token)
|
|
41207
|
+
os.makedirs(os.path.dirname(p), exist_ok=True)
|
|
41208
|
+
tmp = p + ".oasis.tmp"
|
|
41209
|
+
with open(tmp, "w") as f:
|
|
41210
|
+
yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
|
|
41211
|
+
os.replace(tmp, p)
|
|
41212
|
+
print("-> hermes config.yaml: skills.external_dirs += $OASIS_HERMES_SKILLS_DIR")
|
|
41213
|
+
PYEOF
|
|
40849
41214
|
fi
|
|
40850
41215
|
|
|
40851
41216
|
OASIS_DIR="$(pwd)"
|
|
@@ -41197,7 +41562,7 @@ function nodesDomain(deps) {
|
|
|
41197
41562
|
nodeId = incomingNodeId;
|
|
41198
41563
|
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
41199
41564
|
} else {
|
|
41200
|
-
nodeId = `node-${(0,
|
|
41565
|
+
nodeId = `node-${(0, import_node_crypto16.randomUUID)().slice(0, 8)}`;
|
|
41201
41566
|
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
41202
41567
|
}
|
|
41203
41568
|
const existingNode = await deps.nodeStore.getNode(nodeId);
|
|
@@ -41337,11 +41702,11 @@ function nodesDomain(deps) {
|
|
|
41337
41702
|
});
|
|
41338
41703
|
};
|
|
41339
41704
|
}
|
|
41340
|
-
var
|
|
41705
|
+
var import_node_crypto16, import_node_fs3, import_node_url, import_node_path2;
|
|
41341
41706
|
var init_routes4 = __esm({
|
|
41342
41707
|
"../server/src/domains/nodes/routes.ts"() {
|
|
41343
41708
|
"use strict";
|
|
41344
|
-
|
|
41709
|
+
import_node_crypto16 = require("node:crypto");
|
|
41345
41710
|
import_node_fs3 = require("node:fs");
|
|
41346
41711
|
import_node_url = require("node:url");
|
|
41347
41712
|
import_node_path2 = require("node:path");
|
|
@@ -41403,11 +41768,11 @@ var init_types3 = __esm({
|
|
|
41403
41768
|
});
|
|
41404
41769
|
|
|
41405
41770
|
// ../server/src/node-store.ts
|
|
41406
|
-
var
|
|
41771
|
+
var fs13, MemoryNodeStore, FileNodeStore;
|
|
41407
41772
|
var init_node_store = __esm({
|
|
41408
41773
|
"../server/src/node-store.ts"() {
|
|
41409
41774
|
"use strict";
|
|
41410
|
-
|
|
41775
|
+
fs13 = __toESM(require("node:fs"), 1);
|
|
41411
41776
|
MemoryNodeStore = class {
|
|
41412
41777
|
nodes = /* @__PURE__ */ new Map();
|
|
41413
41778
|
runtimes = /* @__PURE__ */ new Map();
|
|
@@ -41492,16 +41857,16 @@ var init_node_store = __esm({
|
|
|
41492
41857
|
static async open(file) {
|
|
41493
41858
|
const s2 = new _FileNodeStore(file);
|
|
41494
41859
|
try {
|
|
41495
|
-
const snap = JSON.parse(
|
|
41860
|
+
const snap = JSON.parse(fs13.readFileSync(file, "utf8"));
|
|
41496
41861
|
for (const n of snap.nodes ?? []) s2.nodes.set(n.id, n);
|
|
41497
41862
|
for (const r of snap.runtimes ?? []) s2.runtimes.set(r.id, r);
|
|
41498
41863
|
} catch {
|
|
41499
|
-
|
|
41864
|
+
fs13.writeFileSync(file, JSON.stringify({ nodes: [], runtimes: [] }, null, 2));
|
|
41500
41865
|
}
|
|
41501
41866
|
return s2;
|
|
41502
41867
|
}
|
|
41503
41868
|
flush() {
|
|
41504
|
-
|
|
41869
|
+
fs13.writeFileSync(this.file, JSON.stringify({
|
|
41505
41870
|
nodes: [...this.nodes.values()],
|
|
41506
41871
|
runtimes: [...this.runtimes.values()]
|
|
41507
41872
|
}, null, 2));
|
|
@@ -41553,8 +41918,18 @@ var init_node_store = __esm({
|
|
|
41553
41918
|
});
|
|
41554
41919
|
|
|
41555
41920
|
// ../server/src/domains/collab/inbox.ts
|
|
41556
|
-
function buildInbox(model, me, leadOf) {
|
|
41921
|
+
function buildInbox(model, me, leadOf, resolveActor) {
|
|
41557
41922
|
const out = [];
|
|
41923
|
+
const gapItems = [];
|
|
41924
|
+
const actorRef = (id) => {
|
|
41925
|
+
const extra = resolveActor?.(id);
|
|
41926
|
+
return {
|
|
41927
|
+
id,
|
|
41928
|
+
...extra?.name ? { name: extra.name } : {},
|
|
41929
|
+
...extra?.role ? { role: extra.role } : {},
|
|
41930
|
+
...extra?.avatar ? { avatar: extra.avatar } : {}
|
|
41931
|
+
};
|
|
41932
|
+
};
|
|
41558
41933
|
const creatorOf = /* @__PURE__ */ new Map();
|
|
41559
41934
|
for (const a of model.artifacts.values()) {
|
|
41560
41935
|
if (a.type === "brief") creatorOf.set(a.workspace, a.owner);
|
|
@@ -41575,48 +41950,61 @@ function buildInbox(model, me, leadOf) {
|
|
|
41575
41950
|
summary: `\u5DE5\u5355\u7F3A\u4EBA\u624B\uFF1A${pendingRole[1]} \u89D2\u8272\u65E0\u5458\u5DE5\uFF0C\u300C${nodeLabel(a)}\u300D\u5F85\u6D3E\u2014\u2014\u8BF7\u8865\u4EBA\u6216 assign`
|
|
41576
41951
|
});
|
|
41577
41952
|
}
|
|
41578
|
-
const gapEscalationSeen = /* @__PURE__ */ new Set();
|
|
41579
41953
|
for (const a of model.artifacts.values()) {
|
|
41580
|
-
const escs = unresolvedEscalationsOf(model, a.id);
|
|
41581
|
-
if (escs.length === 0) continue;
|
|
41582
41954
|
const isOwnerRecipient = creatorOf.get(a.workspace) === me;
|
|
41583
41955
|
const isLeadRecipient = !!leadOf && leadOf(a.owner) === me;
|
|
41584
41956
|
if (!isOwnerRecipient && !isLeadRecipient) continue;
|
|
41585
|
-
const key = `${a.workspace}:${a.id}`;
|
|
41586
|
-
if (gapEscalationSeen.has(key)) continue;
|
|
41587
|
-
gapEscalationSeen.add(key);
|
|
41588
|
-
const who = isOwnerRecipient ? "\uFF08\u4F60\u662F\u5DE5\u5355\u5F52\u5C5E\u4EBA\uFF09" : "\uFF08\u4F60\u662F\u5361\u4F4F\u8282\u70B9 owner \u7684\u4E0A\u7EA7\uFF09";
|
|
41589
|
-
out.push({
|
|
41590
|
-
kind: "gap-escalation",
|
|
41591
|
-
artifactId: a.id,
|
|
41592
|
-
workspace: a.workspace,
|
|
41593
|
-
since: escs.at(-1).at,
|
|
41594
|
-
actionRequired: true,
|
|
41595
|
-
summary: `\u300C${nodeLabel(a)}\u300D\u88AB\u4E0A\u62A5\u9700\u4F60\u4ECB\u5165${who}\uFF1A${escs.at(-1).reason.slice(0, 100)}${escs.length > 1 ? `\uFF08\u5171 ${escs.length} \u6761\uFF09` : ""}`
|
|
41596
|
-
});
|
|
41597
|
-
}
|
|
41598
|
-
for (const a of model.artifacts.values()) {
|
|
41599
41957
|
const gaps = unresolvedGapsOf(model, a.id);
|
|
41600
|
-
|
|
41601
|
-
const isOwnerRecipient = creatorOf.get(a.workspace) === me;
|
|
41602
|
-
const isLeadRecipient = !!leadOf && leadOf(a.owner) === me;
|
|
41603
|
-
if (!isOwnerRecipient && !isLeadRecipient) continue;
|
|
41604
|
-
const key = `${a.workspace}:${a.id}`;
|
|
41605
|
-
if (gapEscalationSeen.has(key)) continue;
|
|
41606
|
-
gapEscalationSeen.add(key);
|
|
41958
|
+
const escalations = unresolvedEscalationsOf(model, a.id);
|
|
41607
41959
|
const who = isOwnerRecipient ? "\uFF08\u4F60\u662F\u5DE5\u5355\u5F52\u5C5E\u4EBA\uFF09" : "\uFF08\u4F60\u662F\u5361\u4F4F\u8282\u70B9 owner \u7684\u4E0A\u7EA7\uFF09";
|
|
41608
|
-
|
|
41609
|
-
|
|
41610
|
-
|
|
41611
|
-
|
|
41612
|
-
|
|
41613
|
-
|
|
41614
|
-
|
|
41615
|
-
|
|
41960
|
+
for (const gap of gaps) {
|
|
41961
|
+
const byGapId = escalations.filter((entry) => entry.gapId === gap.gapId);
|
|
41962
|
+
const samePartGaps = gaps.filter((candidate) => candidate.part === gap.part);
|
|
41963
|
+
const samePartEscalations = escalations.filter(
|
|
41964
|
+
(entry) => entry.gapId === void 0 && entry.part === gap.part
|
|
41965
|
+
);
|
|
41966
|
+
const partlessEscalations = escalations.filter(
|
|
41967
|
+
(entry) => entry.gapId === void 0 && entry.part === null
|
|
41968
|
+
);
|
|
41969
|
+
const exactEscalation = byGapId.at(-1) ?? (samePartGaps.length === 1 ? samePartEscalations.at(-1) : void 0) ?? (gaps.length === 1 ? partlessEscalations.at(-1) : void 0);
|
|
41970
|
+
gapItems.push({
|
|
41971
|
+
kind: "gap-escalation",
|
|
41972
|
+
artifactId: a.id,
|
|
41973
|
+
workspace: a.workspace,
|
|
41974
|
+
since: exactEscalation?.at ?? gap.lastReportedAt ?? model.lastOpAt.get(a.id) ?? "",
|
|
41975
|
+
actionRequired: true,
|
|
41976
|
+
summary: exactEscalation?.reason ?? gap.description,
|
|
41977
|
+
gap: {
|
|
41978
|
+
gapId: gap.gapId,
|
|
41979
|
+
// 契约:reporter 永远是 GapView.by(原始上报人);诊断者另见 diagnosis 文案,不覆盖署名。
|
|
41980
|
+
reporter: actorRef(gap.by),
|
|
41981
|
+
artifactLabel: nodeLabel(a),
|
|
41982
|
+
problem: gap.description,
|
|
41983
|
+
...exactEscalation ? { diagnosis: exactEscalation.reason } : {},
|
|
41984
|
+
priority: exactEscalation ? "decision_required" : "blocked",
|
|
41985
|
+
reportCount: gap.reportCount ?? 1
|
|
41986
|
+
}
|
|
41987
|
+
});
|
|
41988
|
+
}
|
|
41989
|
+
if (gaps.length === 0 && escalations.length > 0) {
|
|
41990
|
+
const last = escalations.at(-1);
|
|
41991
|
+
gapItems.push({
|
|
41992
|
+
kind: "gap-escalation",
|
|
41993
|
+
artifactId: a.id,
|
|
41994
|
+
workspace: a.workspace,
|
|
41995
|
+
since: last.at,
|
|
41996
|
+
actionRequired: true,
|
|
41997
|
+
summary: `\u300C${nodeLabel(a)}\u300D\u88AB\u4E0A\u62A5\u9700\u4F60\u4ECB\u5165${who}\uFF1A${last.reason.slice(0, 100)}${escalations.length > 1 ? `\uFF08\u5171 ${escalations.length} \u6761\uFF09` : ""}`
|
|
41998
|
+
});
|
|
41999
|
+
}
|
|
41616
42000
|
}
|
|
41617
42001
|
for (const r of listPendingReviews(model)) {
|
|
41618
42002
|
if (r.origin) continue;
|
|
41619
|
-
if (!r.workspace
|
|
42003
|
+
if (!r.workspace) continue;
|
|
42004
|
+
const anchorOwner = model.artifacts.get(r.anchor)?.owner;
|
|
42005
|
+
const isOwnerRecipient = creatorOf.get(r.workspace) === me;
|
|
42006
|
+
const isLeadRecipient = !!anchorOwner && !!leadOf && leadOf(anchorOwner) === me;
|
|
42007
|
+
if (!isOwnerRecipient && !isLeadRecipient) continue;
|
|
41620
42008
|
out.push({
|
|
41621
42009
|
kind: "change-confirm",
|
|
41622
42010
|
artifactId: r.anchor,
|
|
@@ -41716,7 +42104,10 @@ function buildInbox(model, me, leadOf) {
|
|
|
41716
42104
|
if (gate) continue;
|
|
41717
42105
|
out.push({ kind: "assigned", artifactId: a.id, workspace: ws, since: model.lastOpAt.get(a.id) ?? "", actionRequired: true, summary: `\u5728\u9014\u5F85\u529E\uFF1A${label}` });
|
|
41718
42106
|
}
|
|
41719
|
-
|
|
42107
|
+
const rank = (item) => item.gap?.priority === "decision_required" ? 0 : 1;
|
|
42108
|
+
gapItems.sort((a, b2) => rank(a) - rank(b2) || a.since.localeCompare(b2.since));
|
|
42109
|
+
out.sort((x2, y) => x2.since < y.since ? 1 : x2.since > y.since ? -1 : 0);
|
|
42110
|
+
return [...gapItems, ...out];
|
|
41720
42111
|
}
|
|
41721
42112
|
var init_inbox = __esm({
|
|
41722
42113
|
"../server/src/domains/collab/inbox.ts"() {
|
|
@@ -41776,7 +42167,9 @@ var init_audit = __esm({
|
|
|
41776
42167
|
reopen: "\u91CD\u5F00",
|
|
41777
42168
|
seal: "\u5C01\u5B58",
|
|
41778
42169
|
link_input: "\u63A5\u4F9D\u8D56",
|
|
41779
|
-
report_gap: "\u7559\u7F3A\u53E3"
|
|
42170
|
+
report_gap: "\u7559\u7F3A\u53E3",
|
|
42171
|
+
hold: "\u6682\u505C\u6D3E\u53D1",
|
|
42172
|
+
release: "\u89E3\u963B"
|
|
41780
42173
|
};
|
|
41781
42174
|
}
|
|
41782
42175
|
});
|
|
@@ -41827,6 +42220,139 @@ var init_dashboard = __esm({
|
|
|
41827
42220
|
}
|
|
41828
42221
|
});
|
|
41829
42222
|
|
|
42223
|
+
// ../server/src/domains/collab/organization-usage.ts
|
|
42224
|
+
function emptyUsage() {
|
|
42225
|
+
return {
|
|
42226
|
+
input: 0,
|
|
42227
|
+
cache: 0,
|
|
42228
|
+
output: 0,
|
|
42229
|
+
tokens: 0,
|
|
42230
|
+
costUsdMicros: 0,
|
|
42231
|
+
costParts: { input: 0, cache: 0, output: 0 }
|
|
42232
|
+
};
|
|
42233
|
+
}
|
|
42234
|
+
function usageOf(run) {
|
|
42235
|
+
if (!run.effectiveModel?.trim()) return null;
|
|
42236
|
+
const input = Math.max(run.usage.inputTokens ?? 0, 0);
|
|
42237
|
+
const cache = Math.max(run.usage.cacheReadTokens ?? 0, 0) + Math.max(run.usage.cacheCreationTokens ?? 0, 0);
|
|
42238
|
+
const output = Math.max(run.usage.outputTokens ?? 0, 0);
|
|
42239
|
+
const tokens = input + cache + output;
|
|
42240
|
+
const costUsdMicros = Math.max(run.usage.costUsdMicros ?? 0, 0);
|
|
42241
|
+
const denominator = tokens || 1;
|
|
42242
|
+
const inputCost = Math.round(costUsdMicros * input / denominator);
|
|
42243
|
+
const cacheCost = Math.round(costUsdMicros * cache / denominator);
|
|
42244
|
+
return {
|
|
42245
|
+
input,
|
|
42246
|
+
cache,
|
|
42247
|
+
output,
|
|
42248
|
+
tokens,
|
|
42249
|
+
costUsdMicros,
|
|
42250
|
+
costParts: {
|
|
42251
|
+
input: inputCost,
|
|
42252
|
+
cache: cacheCost,
|
|
42253
|
+
output: costUsdMicros - inputCost - cacheCost
|
|
42254
|
+
}
|
|
42255
|
+
};
|
|
42256
|
+
}
|
|
42257
|
+
function addUsage(target, addition) {
|
|
42258
|
+
target.input += addition.input;
|
|
42259
|
+
target.cache += addition.cache;
|
|
42260
|
+
target.output += addition.output;
|
|
42261
|
+
target.tokens += addition.tokens;
|
|
42262
|
+
target.costUsdMicros += addition.costUsdMicros;
|
|
42263
|
+
target.costParts.input += addition.costParts.input;
|
|
42264
|
+
target.costParts.cache += addition.costParts.cache;
|
|
42265
|
+
target.costParts.output += addition.costParts.output;
|
|
42266
|
+
}
|
|
42267
|
+
function bucketFormatter(timeZone, kind) {
|
|
42268
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
42269
|
+
timeZone,
|
|
42270
|
+
year: "numeric",
|
|
42271
|
+
month: "2-digit",
|
|
42272
|
+
day: "2-digit",
|
|
42273
|
+
...kind === "hour" ? { hour: "2-digit", hourCycle: "h23" } : {}
|
|
42274
|
+
});
|
|
42275
|
+
}
|
|
42276
|
+
function bucketKey(formatter, value, kind) {
|
|
42277
|
+
const date3 = new Date(value);
|
|
42278
|
+
if (!Number.isFinite(date3.getTime())) return null;
|
|
42279
|
+
const parts = new Map(formatter.formatToParts(date3).map((part) => [part.type, part.value]));
|
|
42280
|
+
const day = `${parts.get("year")}-${parts.get("month")}-${parts.get("day")}`;
|
|
42281
|
+
return kind === "hour" ? `${day}T${parts.get("hour")}` : day;
|
|
42282
|
+
}
|
|
42283
|
+
function buildOrganizationUsageSummary(input) {
|
|
42284
|
+
const formatter = bucketFormatter(input.timeZone, input.bucketKind);
|
|
42285
|
+
const workorders = buildWorkorderSummaries(input.model);
|
|
42286
|
+
const workorderById = new Map(workorders.map((workorder) => [workorder.id, workorder]));
|
|
42287
|
+
const workspaceByArtifact = new Map([...input.model.artifacts.values()].map((artifact) => [artifact.id, artifact.workspace]));
|
|
42288
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
42289
|
+
const rows = /* @__PURE__ */ new Map();
|
|
42290
|
+
const total = emptyUsage();
|
|
42291
|
+
let unpricedRunCount = 0;
|
|
42292
|
+
for (const run of input.runs) {
|
|
42293
|
+
const usage = usageOf(run);
|
|
42294
|
+
if (!usage) {
|
|
42295
|
+
unpricedRunCount += 1;
|
|
42296
|
+
continue;
|
|
42297
|
+
}
|
|
42298
|
+
addUsage(total, usage);
|
|
42299
|
+
const key = bucketKey(formatter, run.startedAt, input.bucketKind);
|
|
42300
|
+
if (key) {
|
|
42301
|
+
const bucket = buckets.get(key) ?? { key, ...emptyUsage() };
|
|
42302
|
+
addUsage(bucket, usage);
|
|
42303
|
+
buckets.set(key, bucket);
|
|
42304
|
+
}
|
|
42305
|
+
const workspace = run.artifactId ? workspaceByArtifact.get(run.artifactId) : void 0;
|
|
42306
|
+
const workorder = workspace ? workorderById.get(workspace) : void 0;
|
|
42307
|
+
const rowId = workorder?.id ?? UNASSIGNED_ID;
|
|
42308
|
+
let row = rows.get(rowId);
|
|
42309
|
+
if (!row) {
|
|
42310
|
+
row = {
|
|
42311
|
+
id: rowId,
|
|
42312
|
+
title: workorder?.title ?? "\u672A\u5173\u8054\u5DE5\u5355",
|
|
42313
|
+
stage: workorder?.stage ?? "unassigned",
|
|
42314
|
+
usage: emptyUsage(),
|
|
42315
|
+
employees: [],
|
|
42316
|
+
share: 0,
|
|
42317
|
+
...!workorder ? { unassigned: true } : {}
|
|
42318
|
+
};
|
|
42319
|
+
rows.set(rowId, row);
|
|
42320
|
+
}
|
|
42321
|
+
addUsage(row.usage, usage);
|
|
42322
|
+
let employee = row.employees.find((item) => item.actorId === run.actorId);
|
|
42323
|
+
if (!employee) {
|
|
42324
|
+
employee = { actorId: run.actorId, usage: emptyUsage(), share: 0 };
|
|
42325
|
+
row.employees.push(employee);
|
|
42326
|
+
}
|
|
42327
|
+
addUsage(employee.usage, usage);
|
|
42328
|
+
}
|
|
42329
|
+
const workorderRows = [...rows.values()].map((row) => {
|
|
42330
|
+
row.share = total.tokens > 0 ? row.usage.tokens / total.tokens : 0;
|
|
42331
|
+
row.employees = row.employees.map((employee) => ({
|
|
42332
|
+
...employee,
|
|
42333
|
+
share: row.usage.tokens > 0 ? employee.usage.tokens / row.usage.tokens : 0
|
|
42334
|
+
})).sort((a, b2) => b2.usage.tokens - a.usage.tokens);
|
|
42335
|
+
return row;
|
|
42336
|
+
}).sort((a, b2) => {
|
|
42337
|
+
if (a.unassigned !== b2.unassigned) return a.unassigned ? 1 : -1;
|
|
42338
|
+
return b2.usage.tokens - a.usage.tokens;
|
|
42339
|
+
});
|
|
42340
|
+
return {
|
|
42341
|
+
total,
|
|
42342
|
+
buckets: [...buckets.values()].sort((a, b2) => a.key.localeCompare(b2.key)),
|
|
42343
|
+
workorders: workorderRows,
|
|
42344
|
+
unpricedRunCount
|
|
42345
|
+
};
|
|
42346
|
+
}
|
|
42347
|
+
var UNASSIGNED_ID;
|
|
42348
|
+
var init_organization_usage = __esm({
|
|
42349
|
+
"../server/src/domains/collab/organization-usage.ts"() {
|
|
42350
|
+
"use strict";
|
|
42351
|
+
init_workorders();
|
|
42352
|
+
UNASSIGNED_ID = "__unassigned__";
|
|
42353
|
+
}
|
|
42354
|
+
});
|
|
42355
|
+
|
|
41830
42356
|
// ../server/src/domains/collab/index.ts
|
|
41831
42357
|
async function resolveLead(registry2, me) {
|
|
41832
42358
|
const rec = await registry2.getActor(me);
|
|
@@ -41904,6 +42430,34 @@ function collabDomain(opts) {
|
|
|
41904
42430
|
return ctx;
|
|
41905
42431
|
}
|
|
41906
42432
|
return (router) => {
|
|
42433
|
+
router.get("/api/organization/usage-summary", async (req) => {
|
|
42434
|
+
if (!opts.trace) {
|
|
42435
|
+
return { status: 503, body: { error: { code: "no_trace_store", message: "\u8FD0\u884C\u7528\u91CF\u6570\u636E\u6E90\u672A\u63A5\u5165" } } };
|
|
42436
|
+
}
|
|
42437
|
+
const from = req.query.get("from");
|
|
42438
|
+
const to = req.query.get("to");
|
|
42439
|
+
const bucket = req.query.get("bucket");
|
|
42440
|
+
const timeZone = req.query.get("timeZone") ?? "UTC";
|
|
42441
|
+
if (!from || !to || bucket !== "hour" && bucket !== "day") {
|
|
42442
|
+
return { status: 400, body: { error: { code: "bad_request", message: "from / to / bucket(hour|day) \u5FC5\u586B" } } };
|
|
42443
|
+
}
|
|
42444
|
+
const fromMs = Date.parse(from);
|
|
42445
|
+
const toMs = Date.parse(to);
|
|
42446
|
+
if (!Number.isFinite(fromMs) || !Number.isFinite(toMs) || fromMs > toMs) {
|
|
42447
|
+
return { status: 400, body: { error: { code: "bad_request", message: "from / to \u65F6\u95F4\u8303\u56F4\u65E0\u6548" } } };
|
|
42448
|
+
}
|
|
42449
|
+
try {
|
|
42450
|
+
new Intl.DateTimeFormat("en", { timeZone }).format();
|
|
42451
|
+
} catch {
|
|
42452
|
+
return { status: 400, body: { error: { code: "bad_request", message: "timeZone \u65E0\u6548" } } };
|
|
42453
|
+
}
|
|
42454
|
+
const { kernel } = await resolveCtx(req.auth.companyId);
|
|
42455
|
+
const runs = await opts.trace.listUsageRuns({ from, to });
|
|
42456
|
+
return {
|
|
42457
|
+
status: 200,
|
|
42458
|
+
body: buildOrganizationUsageSummary({ model: kernel.model, runs, bucketKind: bucket, timeZone })
|
|
42459
|
+
};
|
|
42460
|
+
});
|
|
41907
42461
|
router.get("/api/workorders", async (req) => {
|
|
41908
42462
|
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
41909
42463
|
const resolve5 = await buildResolver(registry2);
|
|
@@ -41971,8 +42525,13 @@ function collabDomain(opts) {
|
|
|
41971
42525
|
});
|
|
41972
42526
|
router.get("/api/inbox", async (req) => {
|
|
41973
42527
|
const { kernel, registry: registry2 } = await resolveCtx(req.auth.companyId);
|
|
41974
|
-
const leadOf = await
|
|
41975
|
-
|
|
42528
|
+
const [leadOf, resolveActor] = await Promise.all([
|
|
42529
|
+
buildLeadResolver(registry2),
|
|
42530
|
+
// 决策 0026 (c):gap 升级也发卡住节点 owner 的人类上级
|
|
42531
|
+
buildResolver(registry2)
|
|
42532
|
+
// 决策 0042:gap reporter 名册 join
|
|
42533
|
+
]);
|
|
42534
|
+
return { status: 200, body: { items: buildInbox(kernel.model, req.auth.actor, leadOf, resolveActor) } };
|
|
41976
42535
|
});
|
|
41977
42536
|
router.get("/api/audit", async (req) => {
|
|
41978
42537
|
const { oplog } = await resolveCtx(req.auth.companyId);
|
|
@@ -42033,19 +42592,21 @@ var init_collab = __esm({
|
|
|
42033
42592
|
init_inbox();
|
|
42034
42593
|
init_audit();
|
|
42035
42594
|
init_dashboard();
|
|
42595
|
+
init_organization_usage();
|
|
42036
42596
|
init_node_state();
|
|
42037
42597
|
init_workorders();
|
|
42038
42598
|
init_workorder_detail();
|
|
42039
42599
|
init_inbox();
|
|
42040
42600
|
init_audit();
|
|
42041
42601
|
init_dashboard();
|
|
42602
|
+
init_organization_usage();
|
|
42042
42603
|
init_planner();
|
|
42043
42604
|
}
|
|
42044
42605
|
});
|
|
42045
42606
|
|
|
42046
42607
|
// ../server/src/domains/collab/create-seeded-workorder.ts
|
|
42047
42608
|
function makeSeededWorkorderCreator(deps) {
|
|
42048
|
-
const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0,
|
|
42609
|
+
const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto17.randomUUID)().slice(0, 8)}`);
|
|
42049
42610
|
return async (input) => {
|
|
42050
42611
|
const workspace = genWorkspace();
|
|
42051
42612
|
if (input.projectId && deps.artifactState) {
|
|
@@ -42082,11 +42643,11 @@ function makeSeededWorkorderCreator(deps) {
|
|
|
42082
42643
|
return { workspace, rootArtifactId: briefId, spawned: planned.spawned };
|
|
42083
42644
|
};
|
|
42084
42645
|
}
|
|
42085
|
-
var
|
|
42646
|
+
var import_node_crypto17, enc2;
|
|
42086
42647
|
var init_create_seeded_workorder = __esm({
|
|
42087
42648
|
"../server/src/domains/collab/create-seeded-workorder.ts"() {
|
|
42088
42649
|
"use strict";
|
|
42089
|
-
|
|
42650
|
+
import_node_crypto17 = require("node:crypto");
|
|
42090
42651
|
init_planner();
|
|
42091
42652
|
enc2 = (s2) => new TextEncoder().encode(s2);
|
|
42092
42653
|
}
|
|
@@ -42559,6 +43120,17 @@ var init_service4 = __esm({
|
|
|
42559
43120
|
const page = await this.store.listRuns(filter);
|
|
42560
43121
|
return { ...page, items: page.items.map((run) => this.withDisplayCost(run)) };
|
|
42561
43122
|
}
|
|
43123
|
+
/** 组织汇总专用瘦读:只取归属、时间和 usage,并统一补齐展示成本。 */
|
|
43124
|
+
async listUsageRuns(filter) {
|
|
43125
|
+
const rows = await this.store.listUsageRuns(filter);
|
|
43126
|
+
return rows.map((row) => ({
|
|
43127
|
+
...row,
|
|
43128
|
+
usage: {
|
|
43129
|
+
...row.usage,
|
|
43130
|
+
costUsdMicros: displayCostUsdMicros(row.effectiveModel, row.usage)
|
|
43131
|
+
}
|
|
43132
|
+
}));
|
|
43133
|
+
}
|
|
42562
43134
|
/** 按维度查聚合用量(per-session/per-agent plan §4)。store 未实现聚合时返回空。
|
|
42563
43135
|
* 读侧补全展示成本:adapter 未上报 cost 时按价格快照估算(与员工周统计同口径)。 */
|
|
42564
43136
|
async queryUsageStats(filter) {
|
|
@@ -43164,7 +43736,7 @@ function createChatSessionsDomain(opts) {
|
|
|
43164
43736
|
const runtimeId = await currentRuntimeId(body.aiActorId) ?? "unknown";
|
|
43165
43737
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
43166
43738
|
const session = {
|
|
43167
|
-
id: (0,
|
|
43739
|
+
id: (0, import_node_crypto18.randomUUID)(),
|
|
43168
43740
|
humanActorId: req.auth.actor,
|
|
43169
43741
|
aiActorId: body.aiActorId,
|
|
43170
43742
|
runtimeId,
|
|
@@ -43215,7 +43787,7 @@ function createChatSessionsDomain(opts) {
|
|
|
43215
43787
|
if (!body?.role || !body?.content) throw new ApiError(400, "BAD_REQUEST", "role and content required");
|
|
43216
43788
|
if (body.role !== "user" && body.role !== "assistant") throw new ApiError(400, "BAD_REQUEST", "role must be user or assistant");
|
|
43217
43789
|
const msg = await store.appendMessage({
|
|
43218
|
-
id: (0,
|
|
43790
|
+
id: (0, import_node_crypto18.randomUUID)(),
|
|
43219
43791
|
sessionId: req.params.id,
|
|
43220
43792
|
role: body.role,
|
|
43221
43793
|
content: body.content,
|
|
@@ -43252,11 +43824,11 @@ function createChatSessionsDomain(opts) {
|
|
|
43252
43824
|
});
|
|
43253
43825
|
};
|
|
43254
43826
|
}
|
|
43255
|
-
var
|
|
43827
|
+
var import_node_crypto18;
|
|
43256
43828
|
var init_chat_sessions = __esm({
|
|
43257
43829
|
"../server/src/domains/chat-sessions/index.ts"() {
|
|
43258
43830
|
"use strict";
|
|
43259
|
-
|
|
43831
|
+
import_node_crypto18 = require("node:crypto");
|
|
43260
43832
|
init_router();
|
|
43261
43833
|
init_workorders();
|
|
43262
43834
|
init_chat_parts();
|
|
@@ -44622,11 +45194,11 @@ function ackMsFromEnv(fallback) {
|
|
|
44622
45194
|
const n = Number(v2);
|
|
44623
45195
|
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
44624
45196
|
}
|
|
44625
|
-
var
|
|
45197
|
+
var import_node_crypto19, DaemonHubAdapter, BindingRouterAdapter;
|
|
44626
45198
|
var init_daemon_adapter = __esm({
|
|
44627
45199
|
"../server/src/daemon-adapter.ts"() {
|
|
44628
45200
|
"use strict";
|
|
44629
|
-
|
|
45201
|
+
import_node_crypto19 = require("node:crypto");
|
|
44630
45202
|
DaemonHubAdapter = class {
|
|
44631
45203
|
constructor(hub, opts = {}) {
|
|
44632
45204
|
this.hub = hub;
|
|
@@ -44713,7 +45285,7 @@ var init_daemon_adapter = __esm({
|
|
|
44713
45285
|
async spawn(job) {
|
|
44714
45286
|
const nodeId = job.binding?.nodeId;
|
|
44715
45287
|
if (!nodeId) throw new Error("DaemonHubAdapter \u9700\u8981 job.binding.nodeId\uFF08\u8DEF\u7531\u9519\u8BEF\uFF09");
|
|
44716
|
-
const dispatchId = `dispatch:${(0,
|
|
45288
|
+
const dispatchId = `dispatch:${(0, import_node_crypto19.randomUUID)()}`;
|
|
44717
45289
|
const entry = {
|
|
44718
45290
|
nodeId,
|
|
44719
45291
|
exitCbs: [],
|
|
@@ -44857,13 +45429,13 @@ function classifyPage(page, lastPushedHash, serviceAccount) {
|
|
|
44857
45429
|
if (sha(body) === lastPushedHash) return { kind: "echo" };
|
|
44858
45430
|
return { kind: "human-edit", content: body, updatedBy: page.updatedBy };
|
|
44859
45431
|
}
|
|
44860
|
-
var
|
|
45432
|
+
var import_node_crypto20, sha, enc3, dec, MirrorEngine, MapMirrorIdentities;
|
|
44861
45433
|
var init_engine = __esm({
|
|
44862
45434
|
"../server/src/mirror/engine.ts"() {
|
|
44863
45435
|
"use strict";
|
|
44864
|
-
|
|
45436
|
+
import_node_crypto20 = require("node:crypto");
|
|
44865
45437
|
init_src();
|
|
44866
|
-
sha = (s2) => (0,
|
|
45438
|
+
sha = (s2) => (0, import_node_crypto20.createHash)("sha256").update(s2, "utf8").digest("hex");
|
|
44867
45439
|
enc3 = (s2) => new TextEncoder().encode(s2);
|
|
44868
45440
|
dec = (b2) => new TextDecoder().decode(b2);
|
|
44869
45441
|
MirrorEngine = class {
|
|
@@ -44976,22 +45548,22 @@ var init_engine = __esm({
|
|
|
44976
45548
|
});
|
|
44977
45549
|
|
|
44978
45550
|
// ../server/src/mirror/fs-state.ts
|
|
44979
|
-
var
|
|
45551
|
+
var fs15, FsMirrorStateStore;
|
|
44980
45552
|
var init_fs_state = __esm({
|
|
44981
45553
|
"../server/src/mirror/fs-state.ts"() {
|
|
44982
45554
|
"use strict";
|
|
44983
|
-
|
|
45555
|
+
fs15 = __toESM(require("node:fs"), 1);
|
|
44984
45556
|
FsMirrorStateStore = class {
|
|
44985
45557
|
constructor(file) {
|
|
44986
45558
|
this.file = file;
|
|
44987
|
-
if (
|
|
44988
|
-
const obj = JSON.parse(
|
|
45559
|
+
if (fs15.existsSync(file)) {
|
|
45560
|
+
const obj = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
44989
45561
|
for (const [k2, v2] of Object.entries(obj)) this.map.set(k2, v2);
|
|
44990
45562
|
}
|
|
44991
45563
|
}
|
|
44992
45564
|
map = /* @__PURE__ */ new Map();
|
|
44993
45565
|
flush() {
|
|
44994
|
-
|
|
45566
|
+
fs15.writeFileSync(this.file, JSON.stringify(Object.fromEntries(this.map), null, 2));
|
|
44995
45567
|
}
|
|
44996
45568
|
async get(artifactId) {
|
|
44997
45569
|
return this.map.get(artifactId) ?? null;
|
|
@@ -45008,13 +45580,13 @@ var init_fs_state = __esm({
|
|
|
45008
45580
|
});
|
|
45009
45581
|
|
|
45010
45582
|
// ../server/src/git/integrate.ts
|
|
45011
|
-
var
|
|
45583
|
+
var import_node_child_process9, fs16, path14, GitRemoteIntegrator;
|
|
45012
45584
|
var init_integrate = __esm({
|
|
45013
45585
|
"../server/src/git/integrate.ts"() {
|
|
45014
45586
|
"use strict";
|
|
45015
|
-
|
|
45016
|
-
|
|
45017
|
-
|
|
45587
|
+
import_node_child_process9 = require("node:child_process");
|
|
45588
|
+
fs16 = __toESM(require("node:fs"), 1);
|
|
45589
|
+
path14 = __toESM(require("node:path"), 1);
|
|
45018
45590
|
GitRemoteIntegrator = class {
|
|
45019
45591
|
constructor(remote, cloneDir, develop = "develop") {
|
|
45020
45592
|
this.remote = remote;
|
|
@@ -45023,7 +45595,7 @@ var init_integrate = __esm({
|
|
|
45023
45595
|
}
|
|
45024
45596
|
env = { ...process.env, LC_ALL: "C", LANG: "C", GIT_TERMINAL_PROMPT: "0" };
|
|
45025
45597
|
git(args) {
|
|
45026
|
-
return (0,
|
|
45598
|
+
return (0, import_node_child_process9.execFileSync)("git", ["-C", this.cloneDir, ...args], {
|
|
45027
45599
|
encoding: "utf8",
|
|
45028
45600
|
env: this.env,
|
|
45029
45601
|
maxBuffer: 256 * 1024 * 1024
|
|
@@ -45031,9 +45603,9 @@ var init_integrate = __esm({
|
|
|
45031
45603
|
}
|
|
45032
45604
|
/** 确保本地工作克隆存在并拉到最新远程态。幂等。 */
|
|
45033
45605
|
sync() {
|
|
45034
|
-
if (!
|
|
45035
|
-
|
|
45036
|
-
(0,
|
|
45606
|
+
if (!fs16.existsSync(path14.join(this.cloneDir, ".git"))) {
|
|
45607
|
+
fs16.mkdirSync(path14.dirname(this.cloneDir), { recursive: true });
|
|
45608
|
+
(0, import_node_child_process9.execFileSync)("git", ["clone", "--quiet", this.remote, this.cloneDir], { env: this.env, maxBuffer: 256 * 1024 * 1024 });
|
|
45037
45609
|
this.git(["config", "user.email", "oasis@local"]);
|
|
45038
45610
|
this.git(["config", "user.name", "oasis"]);
|
|
45039
45611
|
} else {
|
|
@@ -45083,224 +45655,26 @@ var init_integrate = __esm({
|
|
|
45083
45655
|
});
|
|
45084
45656
|
|
|
45085
45657
|
// ../server/src/git/sync.ts
|
|
45086
|
-
var os7, path14, slug3, GitSyncWorker;
|
|
45087
45658
|
var init_sync = __esm({
|
|
45088
45659
|
"../server/src/git/sync.ts"() {
|
|
45089
45660
|
"use strict";
|
|
45090
|
-
os7 = __toESM(require("node:os"), 1);
|
|
45091
|
-
path14 = __toESM(require("node:path"), 1);
|
|
45092
|
-
init_src2();
|
|
45093
|
-
init_integrate();
|
|
45094
|
-
init_collect();
|
|
45095
|
-
slug3 = (id) => id.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
45096
|
-
GitSyncWorker = class {
|
|
45097
|
-
// 仓库键(remote||id) → 集成器(缓存)
|
|
45098
|
-
constructor(deps) {
|
|
45099
|
-
this.deps = deps;
|
|
45100
|
-
}
|
|
45101
|
-
integrators = /* @__PURE__ */ new Map();
|
|
45102
|
-
integratorFor(repo) {
|
|
45103
|
-
if (!repo.remote) return null;
|
|
45104
|
-
const key = repo.remote;
|
|
45105
|
-
let g2 = this.integrators.get(key);
|
|
45106
|
-
if (!g2) {
|
|
45107
|
-
const root = this.deps.mirrorRoot ?? path14.join(os7.tmpdir(), "oasis-git-mirrors");
|
|
45108
|
-
const cloneDir = repo.repoDir ?? path14.join(root, slug3(repo.id));
|
|
45109
|
-
g2 = new GitRemoteIntegrator(repo.remote, cloneDir, repo.develop);
|
|
45110
|
-
this.integrators.set(key, g2);
|
|
45111
|
-
}
|
|
45112
|
-
return g2;
|
|
45113
|
-
}
|
|
45114
|
-
/** 一轮:把每个已 conclude 的代码 artifact 的 commit **逐仓**集成进各自 develop。幂等(git ancestor 判断)。 */
|
|
45115
|
-
async tick() {
|
|
45116
|
-
const { kernel, log: log3 } = this.deps;
|
|
45117
|
-
const synced = /* @__PURE__ */ new Set();
|
|
45118
|
-
for (const artifact of kernel.model.artifacts.values()) {
|
|
45119
|
-
const p2 = await this.deps.resolveProject(artifact.workspace);
|
|
45120
|
-
if (!p2 || !p2.repos?.length) continue;
|
|
45121
|
-
if (!isConcluded(kernel.model, artifact.id)) continue;
|
|
45122
|
-
const head = artifact.currentRev;
|
|
45123
|
-
if (!head) continue;
|
|
45124
|
-
const rev = kernel.model.revisions.get(head);
|
|
45125
|
-
if (!rev || rev.contentKind !== "external-pin") continue;
|
|
45126
|
-
for (const { repo, sha: sha2 } of parseUmbrellaCommits(rev.contentRef, p2.repos)) {
|
|
45127
|
-
const integ = this.integratorFor(repo);
|
|
45128
|
-
if (!integ) continue;
|
|
45129
|
-
const tag = `${artifact.id}/${repo.id}@${sha2.slice(0, 12)}`;
|
|
45130
|
-
try {
|
|
45131
|
-
if (!synced.has(repo.remote)) {
|
|
45132
|
-
integ.sync();
|
|
45133
|
-
synced.add(repo.remote);
|
|
45134
|
-
}
|
|
45135
|
-
const r = integ.integrate(sha2, `integrate ${tag} \u2192 ${repo.develop}`);
|
|
45136
|
-
if (r.merged) log3?.(`[git] merged ${tag} \u2192 ${repo.develop}`);
|
|
45137
|
-
else if (r.conflicts?.length) log3?.(`[git] ${tag} \u96C6\u6210\u51B2\u7A81: ${r.conflicts.join(", ")}\uFF08\u5F85\u56DE\u704C blocking annotation + rebase\uFF09`);
|
|
45138
|
-
} catch (e) {
|
|
45139
|
-
log3?.(`[git] ${tag} \u96C6\u6210\u5931\u8D25\uFF08\u4E0B\u4E00\u8F6E\u91CD\u8BD5\uFF09: ${String(e)}`);
|
|
45140
|
-
}
|
|
45141
|
-
}
|
|
45142
|
-
}
|
|
45143
|
-
}
|
|
45144
|
-
};
|
|
45145
|
-
}
|
|
45146
|
-
});
|
|
45147
|
-
|
|
45148
|
-
// ../server/src/git/ci-provider.ts
|
|
45149
|
-
function mirrorDirFor(mirrorRoot, repo) {
|
|
45150
|
-
if (repo.repoDir) return repo.repoDir;
|
|
45151
|
-
const key = repo.remote ? `${slug4(repo.id)}-${crypto.createHash("sha1").update(repo.remote).digest("hex").slice(0, 8)}` : slug4(repo.id);
|
|
45152
|
-
return `${mirrorRoot}/${key}`;
|
|
45153
|
-
}
|
|
45154
|
-
var import_node_child_process9, crypto, fs16, path15, import_node_util, execFile, slug4, GhCiProvider;
|
|
45155
|
-
var init_ci_provider = __esm({
|
|
45156
|
-
"../server/src/git/ci-provider.ts"() {
|
|
45157
|
-
"use strict";
|
|
45158
|
-
import_node_child_process9 = require("node:child_process");
|
|
45159
|
-
crypto = __toESM(require("node:crypto"), 1);
|
|
45160
|
-
fs16 = __toESM(require("node:fs"), 1);
|
|
45161
|
-
path15 = __toESM(require("node:path"), 1);
|
|
45162
|
-
import_node_util = require("node:util");
|
|
45163
|
-
execFile = (0, import_node_util.promisify)(import_node_child_process9.execFile);
|
|
45164
|
-
slug4 = (id) => id.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
45165
|
-
GhCiProvider = class {
|
|
45166
|
-
constructor(opts) {
|
|
45167
|
-
this.opts = opts;
|
|
45168
|
-
}
|
|
45169
|
-
repoDir(repo) {
|
|
45170
|
-
return mirrorDirFor(this.opts.mirrorRoot, repo);
|
|
45171
|
-
}
|
|
45172
|
-
/** 缺则 clone(首次使用/进程重启后本地镜像不存在)——否则 git fetch 会"not a git repo"报错。同 GitRemoteIntegrator。 */
|
|
45173
|
-
ensureClone(repo) {
|
|
45174
|
-
const dir = this.repoDir(repo);
|
|
45175
|
-
if (fs16.existsSync(path15.join(dir, ".git"))) {
|
|
45176
|
-
if (repo.remote) {
|
|
45177
|
-
let origin = "";
|
|
45178
|
-
try {
|
|
45179
|
-
origin = (0, import_node_child_process9.execFileSync)("git", ["remote", "get-url", "origin"], { cwd: dir, env: process.env }).toString().trim();
|
|
45180
|
-
} catch {
|
|
45181
|
-
}
|
|
45182
|
-
if (origin !== repo.remote) {
|
|
45183
|
-
(0, import_node_child_process9.execFileSync)("git", ["remote", "set-url", "origin", repo.remote], { cwd: dir, env: process.env });
|
|
45184
|
-
this.opts.log?.(`[ci] ${repo.id} \u955C\u50CF origin \u7EA0\u504F\uFF1A${origin || "(\u65E0)"} \u2192 ${repo.remote}`);
|
|
45185
|
-
}
|
|
45186
|
-
}
|
|
45187
|
-
return;
|
|
45188
|
-
}
|
|
45189
|
-
if (!repo.remote) throw new Error(`repo ${repo.id} \u65E0 remote\uFF0C\u65E0\u6CD5 clone`);
|
|
45190
|
-
fs16.mkdirSync(path15.dirname(dir), { recursive: true });
|
|
45191
|
-
(0, import_node_child_process9.execFileSync)("git", ["clone", "--quiet", repo.remote, dir], { env: process.env });
|
|
45192
|
-
this.opts.log?.(`[ci] cloned ${repo.id} \u2192 ${dir}`);
|
|
45193
|
-
}
|
|
45194
|
-
async gh(repo, args) {
|
|
45195
|
-
this.ensureClone(repo);
|
|
45196
|
-
const { stdout } = await execFile("gh", args, { cwd: this.repoDir(repo), env: process.env });
|
|
45197
|
-
return stdout.trim();
|
|
45198
|
-
}
|
|
45199
|
-
async git(repo, args) {
|
|
45200
|
-
this.ensureClone(repo);
|
|
45201
|
-
const { stdout } = await execFile("git", args, { cwd: this.repoDir(repo), env: process.env });
|
|
45202
|
-
return stdout.trim();
|
|
45203
|
-
}
|
|
45204
|
-
async ensurePr(ref, meta) {
|
|
45205
|
-
if (!ref.repo.remote) throw new Error(`repo ${ref.repo.id} \u65E0 remote\uFF0C\u65E0\u6CD5\u5F00 PR`);
|
|
45206
|
-
await this.git(ref.repo, ["fetch", "--quiet", "--prune", "origin"]);
|
|
45207
|
-
await this.git(ref.repo, ["push", "--quiet", "--force-with-lease", "origin", `${ref.sha}:refs/heads/${ref.branch}`]);
|
|
45208
|
-
const existing = await this.gh(ref.repo, ["pr", "list", "--head", ref.branch, "--base", ref.repo.develop, "--state", "open", "--json", "number,url"]);
|
|
45209
|
-
const list = JSON.parse(existing || "[]");
|
|
45210
|
-
if (list[0]) return list[0];
|
|
45211
|
-
const url = await this.gh(ref.repo, ["pr", "create", "--base", ref.repo.develop, "--head", ref.branch, "--title", meta.title, "--body", meta.body]);
|
|
45212
|
-
const created = JSON.parse(await this.gh(ref.repo, ["pr", "view", ref.branch, "--json", "number,url"]));
|
|
45213
|
-
this.opts.log?.(`[ci] opened PR ${created.url} (${ref.branch} \u2192 ${ref.repo.develop})`);
|
|
45214
|
-
return created.url ? created : { number: created.number, url };
|
|
45215
|
-
}
|
|
45216
|
-
async status(ref) {
|
|
45217
|
-
let raw;
|
|
45218
|
-
try {
|
|
45219
|
-
raw = await this.gh(ref.repo, ["pr", "view", ref.branch, "--json", "statusCheckRollup,createdAt"]);
|
|
45220
|
-
} catch (e) {
|
|
45221
|
-
return { state: "pending", summary: `\u8BFB PR \u72B6\u6001\u5931\u8D25\uFF08\u4E0B\u8F6E\u91CD\u8BD5\uFF09\uFF1A${String(e)}` };
|
|
45222
|
-
}
|
|
45223
|
-
const parsed = JSON.parse(raw || "{}");
|
|
45224
|
-
const rollup = parsed.statusCheckRollup ?? [];
|
|
45225
|
-
if (rollup.length === 0) {
|
|
45226
|
-
const grace = this.opts.noCheckGraceMs ?? 12e4;
|
|
45227
|
-
const ageMs = parsed.createdAt ? Date.now() - Date.parse(parsed.createdAt) : Infinity;
|
|
45228
|
-
if (ageMs < grace) return { state: "pending" };
|
|
45229
|
-
if (this.opts.failClosedWhenNoChecks) return { state: "failure", summary: "\u8BE5\u4ED3\u672A\u914D\u4EFB\u4F55 Actions check\uFF08fail-closed\uFF09\u2014\u2014\u8BF7\u8865 CI \u6216\u6539\u914D\u7F6E" };
|
|
45230
|
-
this.opts.log?.(`[ci] ${ref.repo.id} PR ${ref.branch} \u5F00\u4E86 ${Math.round(ageMs / 1e3)}s \u4ECD\u65E0 check\uFF0C\u5224\u65E0 CI\u3001\u95F8\u7A7A\u8FC7\uFF08\u544A\u8B66\uFF09`);
|
|
45231
|
-
return { state: "success", summary: "\u65E0 CI check\uFF0C\u95F8\u7A7A\u8FC7" };
|
|
45232
|
-
}
|
|
45233
|
-
const failed = [];
|
|
45234
|
-
let anyPending = false;
|
|
45235
|
-
for (const c of rollup) {
|
|
45236
|
-
const concl = (c.conclusion ?? "").toUpperCase();
|
|
45237
|
-
const st = (c.status ?? "").toUpperCase();
|
|
45238
|
-
if (st && st !== "COMPLETED") {
|
|
45239
|
-
anyPending = true;
|
|
45240
|
-
continue;
|
|
45241
|
-
}
|
|
45242
|
-
if (concl && !["SUCCESS", "NEUTRAL", "SKIPPED"].includes(concl)) failed.push(c);
|
|
45243
|
-
}
|
|
45244
|
-
if (failed.length > 0) {
|
|
45245
|
-
const blocks = [];
|
|
45246
|
-
for (const c of failed) {
|
|
45247
|
-
const name = c.name ?? c.context ?? "check";
|
|
45248
|
-
const concl = (c.conclusion ?? "").toUpperCase();
|
|
45249
|
-
const url = c.detailsUrl ?? c.targetUrl ?? "";
|
|
45250
|
-
let block = ` - ${name} \u2192 ${concl}${url ? `
|
|
45251
|
-
${url}` : ""}`;
|
|
45252
|
-
const tail = await this.failedLogTail(ref.repo, url);
|
|
45253
|
-
if (tail) block += `
|
|
45254
|
-
\u5931\u8D25\u65E5\u5FD7\uFF08\u672B\u5C3E\uFF09\uFF1A
|
|
45255
|
-
${tail.split("\n").map((l) => " " + l).join("\n")}`;
|
|
45256
|
-
blocks.push(block);
|
|
45257
|
-
}
|
|
45258
|
-
return { state: "failure", summary: `CI \u672A\u8FC7\uFF08${failed.length} \u4E2A check\uFF09\uFF1A
|
|
45259
|
-
${blocks.join("\n")}` };
|
|
45260
|
-
}
|
|
45261
|
-
if (anyPending) return { state: "pending" };
|
|
45262
|
-
return { state: "success", summary: `CI \u901A\u8FC7\uFF08${rollup.length} \u4E2A check\uFF09` };
|
|
45263
|
-
}
|
|
45264
|
-
/** best-effort:从 check 的 detailsUrl 提 Actions run id,取失败步骤日志末尾(截断)。取不到返回空。 */
|
|
45265
|
-
async failedLogTail(repo, detailsUrl) {
|
|
45266
|
-
const m2 = /\/runs\/(\d+)/.exec(detailsUrl);
|
|
45267
|
-
if (!m2) return "";
|
|
45268
|
-
try {
|
|
45269
|
-
const log3 = await this.gh(repo, ["run", "view", m2[1], "--log-failed"]);
|
|
45270
|
-
return log3.split("\n").filter(Boolean).slice(-40).join("\n").slice(-2e3);
|
|
45271
|
-
} catch {
|
|
45272
|
-
return "";
|
|
45273
|
-
}
|
|
45274
|
-
}
|
|
45275
|
-
async merge(ref) {
|
|
45276
|
-
try {
|
|
45277
|
-
await this.gh(ref.repo, ["pr", "merge", ref.branch, "--merge", "--delete-branch"]);
|
|
45278
|
-
this.opts.log?.(`[ci] merged PR ${ref.branch} \u2192 ${ref.repo.develop}`);
|
|
45279
|
-
return { merged: true };
|
|
45280
|
-
} catch (e) {
|
|
45281
|
-
return { merged: false, reason: String(e) };
|
|
45282
|
-
}
|
|
45283
|
-
}
|
|
45284
|
-
};
|
|
45285
45661
|
}
|
|
45286
45662
|
});
|
|
45287
45663
|
|
|
45288
45664
|
// ../server/src/git/ci-gate.ts
|
|
45289
|
-
var
|
|
45665
|
+
var CiGateWorker;
|
|
45290
45666
|
var init_ci_gate = __esm({
|
|
45291
45667
|
"../server/src/git/ci-gate.ts"() {
|
|
45292
45668
|
"use strict";
|
|
45293
45669
|
init_src2();
|
|
45294
45670
|
init_collect();
|
|
45295
|
-
|
|
45671
|
+
init_ci_provider();
|
|
45296
45672
|
CiGateWorker = class {
|
|
45297
45673
|
constructor(deps) {
|
|
45298
45674
|
this.deps = deps;
|
|
45299
45675
|
this.ciActor = deps.ciActor ?? "actor:ci";
|
|
45300
45676
|
}
|
|
45301
45677
|
ciActor;
|
|
45302
|
-
/** 进程内"已尝试合并"去重(避免每轮重复 gh pr merge;重启后至多多试一次,gh 报已合并即无害)。 */
|
|
45303
|
-
mergedOnce = /* @__PURE__ */ new Set();
|
|
45304
45678
|
/** 逐产物失败退避:连续失败指数拉长重试间隔,超过阈值响亮升级一次(不再静默每轮刷日志)。 */
|
|
45305
45679
|
failures = /* @__PURE__ */ new Map();
|
|
45306
45680
|
inBackoff(artifactId) {
|
|
@@ -45336,9 +45710,8 @@ var init_ci_gate = __esm({
|
|
|
45336
45710
|
if (!p2 || !p2.repos?.length) return null;
|
|
45337
45711
|
const units = [];
|
|
45338
45712
|
for (const { repo, sha: sha2 } of parseUmbrellaCommits(contentRef, p2.repos)) {
|
|
45339
|
-
if (!repo.remote) continue;
|
|
45340
|
-
|
|
45341
|
-
units.push({ repo, sha: sha2, branch: `oasis-ci/${artifactSlug2}/${sha2.slice(0, 8)}` });
|
|
45713
|
+
if (!isGitHubRemote(repo.remote)) continue;
|
|
45714
|
+
units.push({ repo, sha: sha2, branch: ciBranchFor(artifactId, sha2) });
|
|
45342
45715
|
}
|
|
45343
45716
|
return units;
|
|
45344
45717
|
}
|
|
@@ -45386,14 +45759,6 @@ var init_ci_gate = __esm({
|
|
|
45386
45759
|
log3?.(`[ci] vote ${artifact.id} \u5931\u8D25\uFF08\u9000\u907F\u91CD\u8BD5\uFF09\uFF1A${String(e)}`);
|
|
45387
45760
|
}
|
|
45388
45761
|
);
|
|
45389
|
-
} else if (isConcluded(kernel.model, artifact.id) && rev.contentKind === "external-pin") {
|
|
45390
|
-
await this.mergeMilestone(artifact.id, artifact.workspace, rev.contentRef, head).then(
|
|
45391
|
-
() => this.recordOk(artifact.id),
|
|
45392
|
-
(e) => {
|
|
45393
|
-
this.recordFail(artifact.id);
|
|
45394
|
-
log3?.(`[ci] merge ${artifact.id} \u5931\u8D25\uFF08\u9000\u907F\u91CD\u8BD5\uFF09\uFF1A${String(e)}`);
|
|
45395
|
-
}
|
|
45396
|
-
);
|
|
45397
45762
|
} else {
|
|
45398
45763
|
this.clearStatus(artifact.id);
|
|
45399
45764
|
}
|
|
@@ -45430,32 +45795,122 @@ var init_ci_gate = __esm({
|
|
|
45430
45795
|
this.clearStatus(artifactId);
|
|
45431
45796
|
log3?.(`[ci] ${artifactId} CI \u5168\u7EFF \u2192 approve`);
|
|
45432
45797
|
}
|
|
45433
|
-
|
|
45434
|
-
|
|
45435
|
-
|
|
45436
|
-
|
|
45437
|
-
|
|
45438
|
-
|
|
45798
|
+
};
|
|
45799
|
+
}
|
|
45800
|
+
});
|
|
45801
|
+
|
|
45802
|
+
// ../server/src/git/merge-worker.ts
|
|
45803
|
+
var MergeWorker;
|
|
45804
|
+
var init_merge_worker = __esm({
|
|
45805
|
+
"../server/src/git/merge-worker.ts"() {
|
|
45806
|
+
"use strict";
|
|
45807
|
+
init_src2();
|
|
45808
|
+
init_collect();
|
|
45809
|
+
init_integrate();
|
|
45810
|
+
init_ci_provider();
|
|
45811
|
+
MergeWorker = class {
|
|
45812
|
+
constructor(deps) {
|
|
45813
|
+
this.deps = deps;
|
|
45814
|
+
}
|
|
45815
|
+
integrators = /* @__PURE__ */ new Map();
|
|
45816
|
+
mergedOnce = /* @__PURE__ */ new Set();
|
|
45817
|
+
failures = /* @__PURE__ */ new Map();
|
|
45818
|
+
inBackoff(id) {
|
|
45819
|
+
const f2 = this.failures.get(id);
|
|
45820
|
+
return !!f2 && Date.now() < f2.nextAt;
|
|
45821
|
+
}
|
|
45822
|
+
recordFail(id) {
|
|
45823
|
+
const base = this.deps.backoff?.baseMs ?? 3e4;
|
|
45824
|
+
const cap = this.deps.backoff?.capMs ?? 6e5;
|
|
45825
|
+
const escalateAfter = this.deps.backoff?.escalateAfter ?? 5;
|
|
45826
|
+
const f2 = this.failures.get(id) ?? { count: 0, nextAt: 0, escalated: false };
|
|
45827
|
+
f2.count += 1;
|
|
45828
|
+
f2.nextAt = Date.now() + Math.min(base * 2 ** (f2.count - 1), cap);
|
|
45829
|
+
if (f2.count >= escalateAfter && !f2.escalated) {
|
|
45830
|
+
f2.escalated = true;
|
|
45831
|
+
this.deps.log?.(`[merge] \u26A0\uFE0F ${id} \u8FDE\u7EED\u5931\u8D25 ${f2.count} \u6B21\uFF0C\u5DF2\u8FDB\u5165\u957F\u9000\u907F\uFF08\u4E0A\u9650 ${Math.round(cap / 1e3)}s\uFF09\u2014\u2014\u8BF7\u4EBA\u5DE5\u68C0\u67E5\u8FDC\u7AEF\u4ED3 / \u5206\u652F\u4FDD\u62A4\u72B6\u6001`);
|
|
45439
45832
|
}
|
|
45440
|
-
|
|
45441
|
-
|
|
45442
|
-
|
|
45443
|
-
|
|
45833
|
+
this.failures.set(id, f2);
|
|
45834
|
+
}
|
|
45835
|
+
recordOk(id) {
|
|
45836
|
+
this.failures.delete(id);
|
|
45837
|
+
}
|
|
45838
|
+
setStatus(id, s2) {
|
|
45839
|
+
this.deps.statusSink?.set(id, { ...s2, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
45840
|
+
}
|
|
45841
|
+
clearStatus(id) {
|
|
45842
|
+
this.deps.statusSink?.delete(id);
|
|
45843
|
+
}
|
|
45844
|
+
/** 非 GitHub 仓的本地集成器(按归一化 remote 缓存;克隆目录与 CI 闸同套 mirrorDirFor,逻辑 id 重名不撞)。 */
|
|
45845
|
+
integratorFor(repo) {
|
|
45846
|
+
if (!repo.remote) return null;
|
|
45847
|
+
const key = normalizeRemote(repo.remote);
|
|
45848
|
+
let g2 = this.integrators.get(key);
|
|
45849
|
+
if (!g2) {
|
|
45850
|
+
g2 = new GitRemoteIntegrator(repo.remote, mirrorDirFor(this.deps.mirrorRoot, repo), repo.develop);
|
|
45851
|
+
this.integrators.set(key, g2);
|
|
45444
45852
|
}
|
|
45445
|
-
|
|
45446
|
-
|
|
45447
|
-
|
|
45448
|
-
|
|
45449
|
-
|
|
45450
|
-
|
|
45451
|
-
|
|
45452
|
-
|
|
45453
|
-
|
|
45454
|
-
|
|
45455
|
-
|
|
45853
|
+
return g2;
|
|
45854
|
+
}
|
|
45855
|
+
async tick() {
|
|
45856
|
+
const { kernel, log: log3 } = this.deps;
|
|
45857
|
+
for (const artifact of kernel.model.artifacts.values()) {
|
|
45858
|
+
const head = artifact.currentRev;
|
|
45859
|
+
if (!head) continue;
|
|
45860
|
+
const rev = kernel.model.revisions.get(head);
|
|
45861
|
+
if (!rev || rev.contentKind !== "external-pin") continue;
|
|
45862
|
+
if (!isConcluded(kernel.model, artifact.id)) continue;
|
|
45863
|
+
if (this.inBackoff(artifact.id)) continue;
|
|
45864
|
+
const p2 = await this.deps.resolveProject(artifact.workspace);
|
|
45865
|
+
if (!p2 || !p2.repos?.length) {
|
|
45866
|
+
this.clearStatus(artifact.id);
|
|
45867
|
+
continue;
|
|
45868
|
+
}
|
|
45869
|
+
const units = parseUmbrellaCommits(rev.contentRef, p2.repos).filter((u) => u.repo.remote);
|
|
45870
|
+
const keyOf = (u) => `${artifact.id}::${head}::${normalizeRemote(u.repo.remote)}`;
|
|
45871
|
+
const pending = units.filter((u) => !this.mergedOnce.has(keyOf(u)));
|
|
45872
|
+
if (pending.length === 0) {
|
|
45873
|
+
this.clearStatus(artifact.id);
|
|
45874
|
+
continue;
|
|
45875
|
+
}
|
|
45876
|
+
this.setStatus(artifact.id, { phase: "merging", repos: pending.map((u) => ({ repoId: u.repo.id, sha: u.sha.slice(0, 8), state: "success" })) });
|
|
45877
|
+
try {
|
|
45878
|
+
for (const { repo, sha: sha2 } of pending) {
|
|
45879
|
+
if (isGitHubRemote(repo.remote)) {
|
|
45880
|
+
if (await this.deps.ci.isMerged({ repo, sha: sha2 })) {
|
|
45881
|
+
this.mergedOnce.add(keyOf({ repo }));
|
|
45882
|
+
continue;
|
|
45883
|
+
}
|
|
45884
|
+
const branch = ciBranchFor(artifact.id, sha2);
|
|
45885
|
+
await this.deps.ci.ensurePr(
|
|
45886
|
+
{ repo, branch, sha: sha2 },
|
|
45887
|
+
{ title: `[oasis-ci] ${artifact.id} \u2192 ${repo.develop}`, body: `merge artifact ${artifact.id} @ ${sha2}` }
|
|
45888
|
+
);
|
|
45889
|
+
const r = await this.deps.ci.merge({ repo, branch });
|
|
45890
|
+
this.mergedOnce.add(keyOf({ repo }));
|
|
45891
|
+
log3?.(r.merged ? `[merge] merged ${artifact.id}/${repo.id} \u2192 ${repo.develop}\uFF08PR\uFF09` : `[merge] ${artifact.id}/${repo.id} \u672A\u5408\uFF08${r.reason ?? "not mergeable"}\uFF09\u2014\u2014\u82E5\u5DF2\u5408\u53EF\u5FFD\u7565`);
|
|
45892
|
+
} else {
|
|
45893
|
+
const integ = this.integratorFor(repo);
|
|
45894
|
+
if (!integ) continue;
|
|
45895
|
+
integ.sync();
|
|
45896
|
+
const r = integ.integrate(sha2, `integrate ${artifact.id}/${repo.id}@${sha2.slice(0, 12)} \u2192 ${repo.develop}`);
|
|
45897
|
+
if (r.merged || r.already) {
|
|
45898
|
+
this.mergedOnce.add(keyOf({ repo }));
|
|
45899
|
+
if (r.merged) log3?.(`[merge] merged ${artifact.id}/${repo.id} \u2192 ${repo.develop}\uFF08\u672C\u5730\uFF09`);
|
|
45900
|
+
} else if (r.conflicts?.length) {
|
|
45901
|
+
log3?.(`[merge] ${artifact.id}/${repo.id} \u96C6\u6210\u51B2\u7A81: ${r.conflicts.join(", ")}\uFF08\u5F85\u56DE\u704C blocking annotation + rebase\uFF09`);
|
|
45902
|
+
}
|
|
45903
|
+
}
|
|
45904
|
+
}
|
|
45905
|
+
this.recordOk(artifact.id);
|
|
45906
|
+
this.clearStatus(artifact.id);
|
|
45907
|
+
} catch (e) {
|
|
45908
|
+
this.recordFail(artifact.id);
|
|
45909
|
+
const f2 = this.failures.get(artifact.id);
|
|
45910
|
+
this.setStatus(artifact.id, { phase: "stuck", repos: [], reason: String(e).slice(0, 500), ...f2 ? { retryAt: new Date(f2.nextAt).toISOString() } : {} });
|
|
45911
|
+
log3?.(`[merge] ${artifact.id} \u5408\u5E76\u5931\u8D25\uFF08\u9000\u907F\u91CD\u8BD5\uFF09\uFF1A${String(e)}`);
|
|
45456
45912
|
}
|
|
45457
45913
|
}
|
|
45458
|
-
this.clearStatus(artifactId);
|
|
45459
45914
|
}
|
|
45460
45915
|
};
|
|
45461
45916
|
}
|
|
@@ -45492,11 +45947,11 @@ var init_resolve = __esm({
|
|
|
45492
45947
|
});
|
|
45493
45948
|
|
|
45494
45949
|
// ../server/src/coordinator/worker.ts
|
|
45495
|
-
var
|
|
45950
|
+
var import_node_crypto21, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_MANAGER_BODY, CoordinatorWorker;
|
|
45496
45951
|
var init_worker = __esm({
|
|
45497
45952
|
"../server/src/coordinator/worker.ts"() {
|
|
45498
45953
|
"use strict";
|
|
45499
|
-
|
|
45954
|
+
import_node_crypto21 = require("node:crypto");
|
|
45500
45955
|
init_src2();
|
|
45501
45956
|
init_identity();
|
|
45502
45957
|
AUTONOMOUS_ETHOS = `\u4F60\u662F\u8FD9\u4E2A\u5DE5\u5355\u7684**\u7BA1\u7406\u8005**\u2014\u2014\u804C\u8D23\u662F\u8BA9\u5B83\u987A\u7545\u8DD1\u5B8C\u3002\u7CFB\u7EDF\u5728\u67D0\u4E2A\u8282\u70B9\u5361\u4F4F\u3001\u673A\u68B0\u5206\u8BCA\u786E\u8BA4"\u9700\u8981\u4F60"\u65F6\u5524\u8D77\u4F60\uFF08\u65E0\u4EBA\u5728\u573A\uFF0C\u4F60\u8FD9\u4E00\u8F6E\u628A\u80FD\u505A\u7684\u505A\u6389\uFF09\u3002\u4F60\u7684\u624B\u6BB5\u5F88\u5BBD\uFF1A
|
|
@@ -45509,7 +45964,7 @@ var init_worker = __esm({
|
|
|
45509
45964
|
\u3010\u600E\u4E48\u5E72\u3011\u963B\u585E\u662F**\u75C7\u72B6**\u4E0D\u662F\u75C5\u3002\u7CFB\u7EDF\u5DF2\u5148\u673A\u68B0\u5206\u8BCA\u3001\u628A**\u80FD\u5B9A\u4F4D\u7684**\uFF08\u8D44\u6E90\u6392\u961F / \u8FD0\u884C\u65F6\u7194\u65AD / \u4EBA\u6CA1\u63A5 / \u7B49\u4E00\u4E2A\u5065\u5EB7\u5728\u4EA7\u7684\u4E0A\u6E38\uFF09\u8DEF\u7531\u7ED9\u4E86\u5BF9\u53E3\u5904\u7F6E\u8005\uFF1B\u8D70\u5230\u4F60\u8FD9\u513F\u7684\u662F\u9700\u8981\u4F60\u5224\u65AD\u7684\u90A3\u7C7B\u3002
|
|
45510
45965
|
\u2460 \u5148\u8BFB\u4E0B\u65B9"\u75C5\u5386"\u6BB5 + \u5FC5\u8981\u65F6 \`oasis status/content <id>\` \u6CBF\u4F9D\u8D56\u94FE\u6478\u6E05\u8BED\u4E49\u6839\uFF1B
|
|
45511
45966
|
\u2461 \u51FA**\u6700\u7701\u4E8B\u3001\u6700\u6CBB\u672C**\u7684\u4E00\u624B\uFF1A\u80FD\u8865\u4E0A\u4E0B\u6587\u5C31\u522B\u6539\u56FE\u3001\u80FD\u8FDE\u65E2\u6709\u5C31\u522B\u65B0\u5EFA\u3001\u80FD\u81EA\u5DF1\u89E3\u51B3\u5C31\u522B\u60CA\u52A8\u4EBA\uFF1B\u9500\u6BC1\u6027\u6539\u56FE\uFF08\u65AD\u8FB9/\u5E9F\u8282\u70B9/\u6539\u6D3E\uFF09apply \u65F6\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u8F6C\u6210\u5F85\u4EBA\u786E\u8BA4\u7684\u63D0\u6848\uFF0C\u4F60 stage \u597D\u8BB2\u6E05\u5F71\u54CD\u5373\u53EF\u3002
|
|
45512
|
-
\u2462 **\u6536\u5C3E\u5FC5\u987B\u663E\u5F0F**\u2014\u2014\u628A\u8FD9\u6761\u7EBF\u63A8\u52A8\u4E86\u3001\u6216\u5224\u65AD\u8BE5\u7531 owner \u7EE7\u7EED \u2192 \`oasis resolve-gap --gap <id> --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u8BE5\u600E\u4E48\u63A5>"\`\uFF08**\u4E0D\u53D1\u8FD9\u53E5 gap \u4E0D\u89E3\u3001owner \u4E0D\u9192**\uFF09\uFF1B\u591F\u4E0D\u7740 / \u8981\u4EBA\u5B9A \u2192 \`oasis escalate <\u8282\u70B9> --reason "<\u6839\u56E0 + \u8981\u4EBA\u505A\u4EC0\u4E48>"\` \u4E0A\u62A5\u3002**\u7EDD\u4E0D\u7A7A\u624B\u9000\u51FA**\uFF1A\u4EC0\u4E48\u90FD\u4E0D\u505A\u5730\u6536\u5DE5 = \u628A\u8282\u70B9\u6C38\u4E45\u667E\u6B7B\uFF0C\u6700\u7CDF\u3002`;
|
|
45967
|
+
\u2462 **\u6536\u5C3E\u5FC5\u987B\u663E\u5F0F**\u2014\u2014\u628A\u8FD9\u6761\u7EBF\u63A8\u52A8\u4E86\u3001\u6216\u5224\u65AD\u8BE5\u7531 owner \u7EE7\u7EED \u2192 \`oasis resolve-gap --gap <id> --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u8BE5\u600E\u4E48\u63A5>"\`\uFF08**\u4E0D\u53D1\u8FD9\u53E5 gap \u4E0D\u89E3\u3001owner \u4E0D\u9192**\uFF09\uFF1B\u591F\u4E0D\u7740 / \u8981\u4EBA\u5B9A \u2192 \`oasis escalate <\u8282\u70B9> --reason "<\u6839\u56E0 + \u8981\u4EBA\u505A\u4EC0\u4E48>" --gap <gapId> [--part <part>]\` \u4E0A\u62A5\uFF08**\u5FC5\u987B\u5E26 --gap**\uFF0C\u6709 part \u65F6\u4E00\u5E76\u5E26\u4E0A\uFF09\u3002**\u7EDD\u4E0D\u7A7A\u624B\u9000\u51FA**\uFF1A\u4EC0\u4E48\u90FD\u4E0D\u505A\u5730\u6536\u5DE5 = \u628A\u8282\u70B9\u6C38\u4E45\u667E\u6B7B\uFF0C\u6700\u7CDF\u3002`;
|
|
45513
45968
|
CONVERSATIONAL_ETHOS = `\u4F60\u662F\u8FD9\u4E2A\u5DE5\u5355\u7684**\u7BA1\u7406\u8005**\uFF0C\u6B63\u5728\u548C\u5DE5\u5355\u53D1\u8D77\u4EBA\u5BF9\u8BDD\u3001\u4E00\u8D77\u628A\u5B83\u5F80\u524D\u63A8\u3002\u4F60\u7684\u624B\u6BB5\u5F88\u5BBD\uFF1A\u5E2E\u5404\u8282\u70B9 owner \u626B\u6E05\u969C\u788D\u3001\u7B54\u7591\u3001\u8865\u4E0A\u4E0B\u6587\u3001**\u81EA\u5DF1\u8DD1\u5206\u6790 / \u62C9\u6570\u636E / \u5199\u8BF4\u660E**\u3001\u534F\u8C03\u8DE8\u8282\u70B9\u3001\u6309\u9700\u6539\u56FE\uFF08\u8FDE\u8FB9 / \u65B0\u5EFA / \u53BB\u91CD / \u6539\u6D3E / \u6539\u6807\u9898\u4E0E brief\uFF09\u3001\u4E3A\u53D1\u8D77\u4EBA\u89E3\u7B54\u6216\u4EE3\u529E\u5DE5\u5355\u8303\u56F4\u5185\u7684\u4E8B\u52A1\u3002**\u4F60\u6709\u4F1A\u8BDD\u5185\u7684\u5168\u90E8\u80FD\u529B\uFF08Bash / \u6587\u4EF6 / \u4EFB\u610F CLI\uFF09\uFF0C\u653E\u5F00\u7528\u3002\u552F\u4E00\u786C\u8FB9\u754C\uFF1A\u4E0D\u66FF owner \u5199\u8282\u70B9\u7684\u4EA7\u7269\u6B63\u6587**\uFF08\u8865\u4E0A\u4E0B\u6587\u3001\u7ED9\u53C2\u8003\u90FD\u884C\uFF0C\u522B\u4EE3\u7B14\uFF09\u3002
|
|
45514
45969
|
- **\u5148\u8BCA\u65AD\u3001\u7ED9\u65B9\u6848\u3001\u95EE ta**\uFF1A\u770B\u6E05\u5361\u5728\u54EA\uFF0C\u7ED9 1\u20132 \u4E2A\u53EF\u884C\u505A\u6CD5\u3001\u8BB2\u6E05\u5404\u81EA\u5F71\u54CD\uFF0C\u95EE ta \u91C7\u7528\u54EA\u4E2A\u2014\u2014\u522B\u81EA\u4F5C\u4E3B\u5F20\u95F7\u5934\u6539\u3002\u660E\u663E\u4E14\u4F4E\u98CE\u9669\u7684\u53EF\u4EE5\u5148\u505A\u4E00\u7248\u518D\u8BF4\u3002
|
|
45515
45970
|
- **\u6539\u52A8\u5206\u4E24\u7C7B**\uFF08\u6388\u6743\u5355\u4F4D\u662F diff\uFF09\uFF1A\u52A0\u8FB9 / \u65B0\u589E\u8282\u70B9 / \u6539\u6807\u9898 brief \u8FD9\u7C7B**\u53EF\u9006\u7684**\uFF08B\uFF09\uFF0C\`stage\`\u2192\`apply\` \u76F4\u63A5\u843D\u3001\u505A\u5B8C\u628A\u52A8\u4E86\u4EC0\u4E48\u544A\u8BC9 ta\uFF1B\u65AD\u8FB9 / \u5E9F\u8282\u70B9 / \u6539\u6D3E / \u64A4\u90E8\u4EF6\u8FD9\u7C7B**\u9500\u6BC1\u6027\u7684**\uFF08C\uFF09\uFF0C\u4F60 \`apply\` \u65F6\u7CFB\u7EDF\u4F1A**\u81EA\u52A8\u8F6C\u6210\u4E00\u4EFD\u5F85\u786E\u8BA4\u63D0\u6848**\u653E\u5230 ta \u9762\u524D\u7684\u300C\u5F85\u786E\u8BA4\u53D8\u66F4\u300D\u5361\u2014\u2014\u4F60 **stage \u597D\u3001\u628A\u5F71\u54CD\u8BB2\u6E05\u695A\u5C31\u884C\uFF0C\u522B\u81EA\u5DF1\u786C\u843D**\uFF0C\u7531 ta \u5728\u5361\u4E0A\u786E\u8BA4\u6216\u9A73\u56DE\u3002
|
|
@@ -45536,7 +45991,7 @@ var init_worker = __esm({
|
|
|
45536
45991
|
|
|
45537
45992
|
\u3010\u6536\u5C3E gap / \u4E0A\u62A5\u5361\u70B9\u3011\uFF08\u4E0D\u8D70\u6682\u5B58\u7F13\u51B2\uFF0C\u76F4\u63A5\u751F\u6548\uFF09
|
|
45538
45993
|
- \`oasis resolve-gap --gap <gapId> --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u8BE5\u600E\u4E48\u7EE7\u7EED>"\` \u2014\u2014 **\u4FEE\u5B8C\u4E00\u6761 gap \u5FC5\u987B\u663E\u5F0F\u6536\u5C3E**\uFF1A\u8FDE\u8FB9 / \u5EFA\u8282\u70B9**\u4E0D\u4F1A\u81EA\u52A8\u89E3 gap**\uFF0C\u4E0D\u53D1\u8FD9\u53E5\u62A5 gap \u7684 owner \u9192\u4E0D\u8FC7\u6765\u3002\`--gap\` = \u90A3\u6761 gap \u7684 id\uFF08reconcile \u4EFB\u52A1\u91CC\u4F1A\u7ED9\u4F60\uFF09\uFF1B\`--note\` \u539F\u6837\u7ED9 owner \u770B\uFF0C\u5199\u4EBA\u8BDD\u3002
|
|
45539
|
-
- \`oasis escalate <id> --reason "<\u7F3A\u4EC0\u4E48\u80FD\u529B / \u8981\u4EBA\u66FF\u4F60\u5B9A\u6216\u505A\u4EC0\u4E48>"\` \u2014\u2014 **\u641E\u4E0D\u5B9A / \u62FF\u4E0D\u51C6 / \u6839\u56E0\u4E0D\u5728\u56FE\u4E0A\u5C31\u4E0A\u62A5\u53D1\u8D77\u4EBA**\uFF08\u522B\u6C89\u9ED8\u9000\u51FA\uFF09\u3002\`<id>\` = \u5361\u4F4F\u7684\u8282\u70B9\uFF1B\`--reason\` \u8BF4\u6E05\u4F60\u5224\u65AD\u7684\u6839\u56E0\u548C\u9700\u8981\u4EBA\u505A\u4EC0\u4E48\u3002\u7CFB\u7EDF\u4F1A\u767B\u8BB0\u672C\u6B21\u4F1A\u8BDD\uFF0C\u4EBA\u56DE\u8BDD\u65F6\u4F60\u88AB**\u63A5\u7740\u5524\u8D77\u3001\u5E26\u4E0A\u4E0B\u6587\u7EE7\u7EED\u89E3**\u3002
|
|
45994
|
+
- \`oasis escalate <id> --reason "<\u7F3A\u4EC0\u4E48\u80FD\u529B / \u8981\u4EBA\u66FF\u4F60\u5B9A\u6216\u505A\u4EC0\u4E48>" --gap <gapId> [--part <part>]\` \u2014\u2014 **\u641E\u4E0D\u5B9A / \u62FF\u4E0D\u51C6 / \u6839\u56E0\u4E0D\u5728\u56FE\u4E0A\u5C31\u4E0A\u62A5\u53D1\u8D77\u4EBA**\uFF08\u522B\u6C89\u9ED8\u9000\u51FA\uFF09\u3002\`<id>\` = \u5361\u4F4F\u7684\u8282\u70B9\uFF1B\`--reason\` \u8BF4\u6E05\u4F60\u5224\u65AD\u7684\u6839\u56E0\u548C\u9700\u8981\u4EBA\u505A\u4EC0\u4E48\u3002\u7CFB\u7EDF\u4F1A\u767B\u8BB0\u672C\u6B21\u4F1A\u8BDD\uFF0C\u4EBA\u56DE\u8BDD\u65F6\u4F60\u88AB**\u63A5\u7740\u5524\u8D77\u3001\u5E26\u4E0A\u4E0B\u6587\u7EE7\u7EED\u89E3**\u3002
|
|
45540
45995
|
|
|
45541
45996
|
\u3010\u8BFB\u56FE\u3011\uFF08\u90FD\u8BFB**\u771F\u56FE**\uFF0C\u4E0E\u522B\u7684 agent \u540C\u8BED\u4E49\uFF0C\u4E0D\u53D7\u4F60\u6682\u5B58\u5F71\u54CD\uFF09
|
|
45542
45997
|
- \`oasis graph\` \u2014\u2014 \u5168\u56FE\u4F9D\u8D56\u8FB9\uFF08\u8C01\u8FDE\u8C01\uFF09\uFF1B\`oasis ls\` \u2014\u2014 \u5168\u4EA7\u7269\u5217\u8868\uFF08id / type / owner / head / \u961F\u5217 / \u72B6\u6001\u6807\uFF09\u3002\u5148\u7528\u5B83\u4EEC\u5EFA\u7ACB\u5168\u5C40\u56FE\u666F\u3002
|
|
@@ -45672,7 +46127,7 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45672
46127
|
return this.deps.kernel.model.lastSeq.get(artifactId) ?? 0;
|
|
45673
46128
|
}
|
|
45674
46129
|
evaluationKey(kind, artifactId, evidence) {
|
|
45675
|
-
const fingerprint = (0,
|
|
46130
|
+
const fingerprint = (0, import_node_crypto21.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
|
|
45676
46131
|
return `${kind}:${artifactId}:${fingerprint}`;
|
|
45677
46132
|
}
|
|
45678
46133
|
artifactEvidence(id) {
|
|
@@ -45702,7 +46157,7 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45702
46157
|
const out = [];
|
|
45703
46158
|
for (const artifact of this.deps.kernel.model.artifacts.values()) {
|
|
45704
46159
|
for (const g2 of unresolvedGapsOf(this.deps.kernel.model, artifact.id)) {
|
|
45705
|
-
out.push({ artifactId: artifact.id, gapId: g2.gapId, description: g2.description });
|
|
46160
|
+
out.push({ artifactId: artifact.id, gapId: g2.gapId, description: g2.description, part: g2.part });
|
|
45706
46161
|
}
|
|
45707
46162
|
}
|
|
45708
46163
|
return out;
|
|
@@ -45737,6 +46192,10 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45737
46192
|
}
|
|
45738
46193
|
return lines.length > 0 ? lines.join("\n") : "\uFF08\u56FE\u91CC\u6682\u65E0\u5176\u5B83\u4EA7\u7269\uFF09";
|
|
45739
46194
|
}
|
|
46195
|
+
escalateCmd(gap, reasonPlaceholder) {
|
|
46196
|
+
const partFlag = gap.part ? ` --part ${gap.part}` : "";
|
|
46197
|
+
return `oasis escalate ${gap.artifactId} --reason "${reasonPlaceholder}" --gap ${gap.gapId}${partFlag}`;
|
|
46198
|
+
}
|
|
45740
46199
|
async reconcile(gap) {
|
|
45741
46200
|
const gapper = this.deps.kernel.model.artifacts.get(gap.artifactId);
|
|
45742
46201
|
const brief = gapper?.title ?? gapper?.description ?? "";
|
|
@@ -45749,7 +46208,7 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45749
46208
|
`## \u8981\u4F60\u505A\u4EC0\u4E48\uFF08\u5148\u5206\u8BCA\uFF09`,
|
|
45750
46209
|
`\u8FD9\u4E2A\u9700\u6C42**\u53EF\u80FD\u662F\u4EFB\u4F55\u963B\u585E**\u2014\u2014\u7F3A\u4E0A\u6E38\u8F93\u5165 / \u7F3A\u4FE1\u606F / \u8981\u4EBA\u51B3\u7B56 / \u7F3A\u80FD\u529B\uFF08\u5982\u67D0\u4E2A\u5DE5\u5177\u88C5\u4E0D\u4E0A\uFF09\u3002\u4F60\u5206\u8BCA\uFF1A`,
|
|
45751
46210
|
`- **\u56FE\u4E0A\u80FD\u4FEE\u7684**\uFF08\u7F3A\u4E0A\u6E38/\u8BE5\u8FDE\u6CA1\u8FDE\uFF09\u2192 \u5224\u65AD\u8FDE\u65E2\u6709\uFF08\u53BB\u91CD\uFF0C\u4F18\u5148\uFF09\u8FD8\u662F\u65B0\u5EFA\u2014\u2014\u6BD4\u5BF9\u4E0B\u9762\u73B0\u6709\u4EA7\u7269\u7684 brief\uFF1B\u62FF\u4E0D\u51C6\u5C31 \`oasis content <\u5019\u9009id>\` \u8BFB\u6B63\u6587\u3002`,
|
|
45752
|
-
`- **\u56FE\u4E0A\u4FEE\u4E0D\u4E86\u7684**\uFF08\u8981\u4EBA\u62CD\u677F / \u7F3A\u73AF\u5883\u80FD\u529B\uFF09\u2192
|
|
46211
|
+
`- **\u56FE\u4E0A\u4FEE\u4E0D\u4E86\u7684**\uFF08\u8981\u4EBA\u62CD\u677F / \u7F3A\u73AF\u5883\u80FD\u529B\uFF09\u2192 \`${this.escalateCmd(gap, "<\u8981\u4EBA\u5B9A/\u505A\u4EC0\u4E48>")}\`\uFF0C\u7CFB\u7EDF\u4F1A\u5347\u7EA7\u7ED9\u53D1\u8D77\u4EBA\u3001\u4F60\u4E4B\u540E\u53EF\u80FD\u5728\u5BF9\u8BDD\u91CC\u88AB ta \u95EE\u5230\uFF0C\u7136\u540E\u6536\u5DE5\u3002`,
|
|
45753
46212
|
``,
|
|
45754
46213
|
`## \u73B0\u6709\u4EA7\u7269\uFF08id / type / \u662F\u5426\u5DF2\u4EA4\u4ED8 / brief / \u4F9D\u8D56\uFF09`,
|
|
45755
46214
|
this.structDigest(gap.artifactId),
|
|
@@ -45763,11 +46222,11 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45763
46222
|
`- \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`,
|
|
45764
46223
|
`- **\u4FEE\u5B8C\u624D\u6536\u5C3E**\uFF1A\`oasis resolve-gap --gap ${gap.gapId} --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u8BE5\u600E\u4E48\u7EE7\u7EED>"\`\u2014\u2014**\u8FDE\u8FB9\u4E0D\u4F1A\u81EA\u52A8\u89E3 gap**\uFF0C\u4E0D\u53D1\u8FD9\u53E5 owner \u9192\u4E0D\u8FC7\u6765\uFF1Bnote \u4F1A\u539F\u6837\u7ED9 owner \u770B\uFF0C\u5199\u4EBA\u8BDD\u3002`,
|
|
45765
46224
|
`**B. \u8981\u4EBA**\uFF08\u51B3\u7B56/\u80FD\u529B\u95EE\u9898\uFF0C\u56FE\u4E0A\u4FEE\u4E0D\u4E86\uFF09\uFF1A`,
|
|
45766
|
-
`-
|
|
46225
|
+
`- \`${this.escalateCmd(gap, "<\u8981\u4EBA\u5B9A/\u505A\u4EC0\u4E48>")}\` \u540E**\u76F4\u63A5\u6536\u5DE5**\u3002`,
|
|
45767
46226
|
`- \u26A0\uFE0F **\u7EDD\u4E0D\u8981\u5BF9\u8FD9\u6761 gap \u8C03 resolve-gap**\u2014\u2014resolve-gap \u7684\u542B\u4E49\u662F"**\u9700\u6C42\u5DF2\u6EE1\u8DB3\u3001\u8BA9 owner \u590D\u5DE5**"\uFF0C\u4E0D\u662F"\u6211\u5206\u8BCA\u5B8C\u4E86"\u7684\u767B\u8BB0\u3002\u73B0\u5728\u9700\u6C42\u8FD8\u6CA1\u6EE1\u8DB3\uFF1Agap \u4FDD\u6301 open \u624D\u662F"\u8282\u70B9\u7EE7\u7EED\u7B49\u5F85"\u7684\u6B63\u786E\u72B6\u6001\uFF1B\u63D0\u524D resolve \u4F1A\u8BA9 owner \u5728\u5565\u90FD\u6CA1\u53D8\u7684\u60C5\u51B5\u4E0B\u88AB\u53EB\u9192\u767D\u8DD1\u3001\u8FD8\u4F1A\u628A\u4F60\u7684\u4E0A\u62A5\u4ECE\u4EBA\u7684\u6536\u4EF6\u7BB1\u91CC\u62B9\u6389\u3002\u4EBA\u7B54\u590D\u540E\uFF08\u591A\u534A\u5728\u5BF9\u8BDD\u91CC\u627E\u4F60\uFF09\u4F60\u518D\u4FEE\u56FE\u3001\u90A3\u65F6\u624D resolve-gap\u3002`,
|
|
45768
46227
|
`**\u5176\u5B83**\uFF1A`,
|
|
45769
46228
|
`- \u987A\u624B\u53D1\u73B0\u4E24\u4E2A\u4EA7\u7269\u91CD\u590D \u2192 **\u5148\u628A\u89E3 gap \u90A3\u7B14\uFF08\u542B resolve-gap\uFF09\u5355\u72EC\u6536\u6389**\uFF0C\u518D\u53E6\u8D77\u4E00\u7B14\uFF1A\`oasis edit\` \u6269\u4FDD\u7559\u4EF6 brief + \`oasis link\` \u6539\u8FDE\u6D88\u8D39\u8005 + \`oasis cancel\` \u5E9F\u5F03\u4EF6\uFF0C\u4E00\u8D77\u6682\u5B58\u518D apply\u3002`,
|
|
45770
|
-
`- **\u62FF\u4E0D\u51C6\u4FEE\u6CD5 \u2192 \u4E00\u5F8B\u627E\u4EBA\uFF0C\u7EDD\u4E0D\u6C89\u9ED8\u9000\u51FA**\uFF1A
|
|
46229
|
+
`- **\u62FF\u4E0D\u51C6\u4FEE\u6CD5 \u2192 \u4E00\u5F8B\u627E\u4EBA\uFF0C\u7EDD\u4E0D\u6C89\u9ED8\u9000\u51FA**\uFF1A\`${this.escalateCmd(gap, "<\u4F60\u5361\u5728\u54EA\u3001\u9700\u8981\u4EBA\u66FF\u4F60\u5B9A/\u505A\u4EC0\u4E48>")}\` \u628A\u7591\u8651\u4E0A\u62A5\u7ED9\u53D1\u8D77\u4EBA\u2014\u2014\u7CFB\u7EDF\u4F1A\u767B\u8BB0\u672C\u6B21\u4F1A\u8BDD\uFF0C\u4EBA\u56DE\u8BDD\u65F6\u4F60\u88AB\u63A5\u7740\u5524\u8D77\u3001\u5E26\u7740\u4E0A\u4E0B\u6587\u7EE7\u7EED\u89E3\u3002**\u6CA1\u6709"\u8FD8\u4E0D\u5230\u8981\u4EBA\u7684\u7A0B\u5EA6"\u8FD9\u4E00\u6863\uFF1A\u6539\u4E0D\u52A8\u53C8\u62FF\u4E0D\u51C6\uFF0C\u5C31\u662F\u8981\u4EBA\u3002\u4EC0\u4E48\u90FD\u4E0D\u505A\u5730\u6536\u5DE5 = \u628A\u8282\u70B9\u6C38\u4E45\u667E\u6B7B\uFF0C\u6700\u7CDF\u3002**`
|
|
45771
46230
|
].join("\n");
|
|
45772
46231
|
const subjectKey = `gap:${gap.gapId}`;
|
|
45773
46232
|
const emptyCount = this.emptyHandedGap.get(subjectKey) ?? 0;
|
|
@@ -45777,7 +46236,7 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45777
46236
|
`## \u26A0\uFE0F \u63A5\u4E0A\u4E00\u8F6E\u2014\u2014\u4F60\u4E0A\u6B21\u88AB\u5524\u8D77\u5374**\u7A7A\u624B\u9000\u51FA\u4E86**\uFF08\u65E2\u6CA1 resolve-gap \u4E5F\u6CA1 escalate\uFF09`,
|
|
45778
46237
|
`\u8FD9\u6761\u7EBF\u8FD8\u5361\u7740\u3002\u8FD9\u4E00\u8F6E\u4F60**\u5FC5\u987B**\u4E8C\u9009\u4E00\u6536\u53E3\uFF0C\u4E0D\u8BB8\u518D\u7A7A\u624B\u9000\u51FA\uFF1A`,
|
|
45779
46238
|
`- \u5DF2\u628A\u8FD9\u6761\u7EBF\u63A8\u52A8 / \u5224\u65AD\u8BE5 owner \u63A5\u624B \u2192 \`oasis resolve-gap --gap ${gap.gapId} --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u600E\u4E48\u63A5>"\``,
|
|
45780
|
-
`- \u591F\u4E0D\u7740 / \u8981\u4EBA\u62CD\u677F \u2192
|
|
46239
|
+
`- \u591F\u4E0D\u7740 / \u8981\u4EBA\u62CD\u677F \u2192 \`${this.escalateCmd(gap, "<\u6839\u56E0 + \u8981\u4EBA\u505A\u4EC0\u4E48>")}\``,
|
|
45781
46240
|
`\u4E0D\u53D1\u8FD9\u4E24\u53E5\u4E4B\u4E00\uFF0Cgap \u6C38\u8FDC\u89E3\u4E0D\u5F00\u3001owner \u6C38\u8FDC\u9192\u4E0D\u8FC7\u6765\u3002**\u8FD9\u662F\u6700\u540E\u4E00\u6B21\u673A\u4F1A\u2014\u2014\u518D\u7A7A\u624B\uFF0C\u7CFB\u7EDF\u4F1A\u66FF\u4F60\u4E0A\u62A5\u7ED9\u4EBA\u3002**`
|
|
45782
46241
|
].join("\n");
|
|
45783
46242
|
const resumeId = emptyCount > 0 ? this.emptyHandedSession.get(subjectKey) : void 0;
|
|
@@ -45800,7 +46259,9 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45800
46259
|
await this.deps.kernel.escalate({
|
|
45801
46260
|
artifactId: gap.artifactId,
|
|
45802
46261
|
actor: run.managerActorId,
|
|
45803
|
-
reason: `\u7BA1\u7406\u8005\u88AB\u7CFB\u7EDF\u5524\u8D77 ${n} \u8F6E\u4ECD\u672A\u6536\u53E3\uFF08\u65E2\u672A resolve-gap \u4E5F\u672A escalate\uFF09\u2014\u2014\u7CFB\u7EDF\u4EE3\u4E3A\u4E0A\u62A5\uFF0C\u8BF7\u4F60\u5904\u7406\u8FD9\u6761\u5361\u70B9\uFF1A${gap.description}
|
|
46262
|
+
reason: `\u7BA1\u7406\u8005\u88AB\u7CFB\u7EDF\u5524\u8D77 ${n} \u8F6E\u4ECD\u672A\u6536\u53E3\uFF08\u65E2\u672A resolve-gap \u4E5F\u672A escalate\uFF09\u2014\u2014\u7CFB\u7EDF\u4EE3\u4E3A\u4E0A\u62A5\uFF0C\u8BF7\u4F60\u5904\u7406\u8FD9\u6761\u5361\u70B9\uFF1A${gap.description}`,
|
|
46263
|
+
gapId: gap.gapId,
|
|
46264
|
+
...gap.part ? { part: gap.part } : {}
|
|
45804
46265
|
}).catch((e) => this.deps.log?.(`[coordinator] ${gap.artifactId} \u81EA\u52A8 escalate \u5931\u8D25\uFF1A${String(e)}`));
|
|
45805
46266
|
this.deps.log?.(`[coordinator] ${gap.artifactId} \u7BA1\u7406\u8005\u8FDE\u7EED ${n} \u8F6E\u7A7A\u624B \u2192 \u7CFB\u7EDF\u81EA\u52A8 escalate \u4EA4\u53D1\u8D77\u4EBA`);
|
|
45806
46267
|
this.emptyHandedGap.delete(subjectKey);
|
|
@@ -45912,7 +46373,7 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45912
46373
|
const token = this.deps.tokenFor(actorId, ctx);
|
|
45913
46374
|
const serverUrl = this.deps.serverUrlFor ? await this.deps.serverUrlFor(ctx) : this.deps.serverUrl;
|
|
45914
46375
|
const workspace = this.deps.kernel.model.artifacts.get(artifactId)?.workspace;
|
|
45915
|
-
const runtimeSessionId = opts?.resumeSessionId ?? (0,
|
|
46376
|
+
const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto21.randomUUID)();
|
|
45916
46377
|
const job = {
|
|
45917
46378
|
actor: actorId,
|
|
45918
46379
|
actorToken: token,
|
|
@@ -46008,22 +46469,22 @@ async function loadBuiltinSkills(skillsDir) {
|
|
|
46008
46469
|
const skills = [];
|
|
46009
46470
|
for (const d of dirents) {
|
|
46010
46471
|
if (!d.isDirectory()) continue;
|
|
46011
|
-
const
|
|
46012
|
-
const skillDir = (0, import_node_path4.join)(skillsDir,
|
|
46472
|
+
const slug4 = d.name;
|
|
46473
|
+
const skillDir = (0, import_node_path4.join)(skillsDir, slug4);
|
|
46013
46474
|
const files = {};
|
|
46014
46475
|
try {
|
|
46015
46476
|
await collectDir(skillDir, skillDir, files);
|
|
46016
46477
|
} catch (err) {
|
|
46017
|
-
console.warn(`[builtin-skills] failed reading ${
|
|
46478
|
+
console.warn(`[builtin-skills] failed reading ${slug4}: ${String(err)}`);
|
|
46018
46479
|
continue;
|
|
46019
46480
|
}
|
|
46020
46481
|
const skillMd = files["SKILL.md"];
|
|
46021
46482
|
if (!skillMd) {
|
|
46022
|
-
console.warn(`[builtin-skills] ${
|
|
46483
|
+
console.warn(`[builtin-skills] ${slug4} has no SKILL.md, skipping`);
|
|
46023
46484
|
continue;
|
|
46024
46485
|
}
|
|
46025
46486
|
const { name, description } = parseSkillFrontmatter(skillMd);
|
|
46026
|
-
skills.push({ id:
|
|
46487
|
+
skills.push({ id: slug4, name: name || slug4, description, files });
|
|
46027
46488
|
}
|
|
46028
46489
|
return skills;
|
|
46029
46490
|
}
|
|
@@ -46105,6 +46566,7 @@ var init_src5 = __esm({
|
|
|
46105
46566
|
init_sync();
|
|
46106
46567
|
init_ci_provider();
|
|
46107
46568
|
init_ci_gate();
|
|
46569
|
+
init_merge_worker();
|
|
46108
46570
|
init_resolve();
|
|
46109
46571
|
init_identity();
|
|
46110
46572
|
init_worker();
|
|
@@ -49475,7 +49937,7 @@ var require_split2 = __commonJS({
|
|
|
49475
49937
|
var require_helper = __commonJS({
|
|
49476
49938
|
"../../node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js"(exports2, module2) {
|
|
49477
49939
|
"use strict";
|
|
49478
|
-
var
|
|
49940
|
+
var path19 = require("path");
|
|
49479
49941
|
var Stream = require("stream").Stream;
|
|
49480
49942
|
var split = require_split2();
|
|
49481
49943
|
var util2 = require("util");
|
|
@@ -49514,7 +49976,7 @@ var require_helper = __commonJS({
|
|
|
49514
49976
|
};
|
|
49515
49977
|
module2.exports.getFileName = function(rawEnv) {
|
|
49516
49978
|
var env = rawEnv || process.env;
|
|
49517
|
-
var file = env.PGPASSFILE || (isWin ?
|
|
49979
|
+
var file = env.PGPASSFILE || (isWin ? path19.join(env.APPDATA || "./", "postgresql", "pgpass.conf") : path19.join(env.HOME || "./", ".pgpass"));
|
|
49518
49980
|
return file;
|
|
49519
49981
|
};
|
|
49520
49982
|
module2.exports.usePgPass = function(stats, fname) {
|
|
@@ -49646,7 +50108,7 @@ var require_helper = __commonJS({
|
|
|
49646
50108
|
var require_lib = __commonJS({
|
|
49647
50109
|
"../../node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js"(exports2, module2) {
|
|
49648
50110
|
"use strict";
|
|
49649
|
-
var
|
|
50111
|
+
var path19 = require("path");
|
|
49650
50112
|
var fs21 = require("fs");
|
|
49651
50113
|
var helper = require_helper();
|
|
49652
50114
|
module2.exports = function(connInfo, cb) {
|
|
@@ -51208,33 +51670,33 @@ __export(trajectory_exports, {
|
|
|
51208
51670
|
exportTrajectories: () => exportTrajectories
|
|
51209
51671
|
});
|
|
51210
51672
|
async function exportTrajectories(dataDir) {
|
|
51211
|
-
const root =
|
|
51673
|
+
const root = path15.join(dataDir, "trajectories");
|
|
51212
51674
|
if (!fs17.existsSync(root)) return [];
|
|
51213
51675
|
const model = emptyReadModel();
|
|
51214
|
-
const oplogFile =
|
|
51676
|
+
const oplogFile = path15.join(dataDir, "oplog.ndjson");
|
|
51215
51677
|
if (fs17.existsSync(oplogFile)) {
|
|
51216
51678
|
const oplog = await NdjsonOplogStore.open(oplogFile);
|
|
51217
51679
|
for await (const entry of oplog.readAll()) fold(model, entry.op);
|
|
51218
51680
|
}
|
|
51219
51681
|
const samples = [];
|
|
51220
51682
|
for (const sessionId of fs17.readdirSync(root).sort()) {
|
|
51221
|
-
const dir =
|
|
51222
|
-
const sessionFile =
|
|
51683
|
+
const dir = path15.join(root, sessionId);
|
|
51684
|
+
const sessionFile = path15.join(dir, "session.json");
|
|
51223
51685
|
if (!fs17.existsSync(sessionFile)) continue;
|
|
51224
51686
|
const session = JSON.parse(fs17.readFileSync(sessionFile, "utf8"));
|
|
51225
51687
|
const events = [];
|
|
51226
|
-
const eventsFile =
|
|
51688
|
+
const eventsFile = path15.join(dir, "events.ndjson");
|
|
51227
51689
|
if (fs17.existsSync(eventsFile)) {
|
|
51228
51690
|
for (const line of fs17.readFileSync(eventsFile, "utf8").split("\n")) {
|
|
51229
51691
|
if (line.trim()) events.push(JSON.parse(line));
|
|
51230
51692
|
}
|
|
51231
51693
|
}
|
|
51232
51694
|
const bundle = {};
|
|
51233
|
-
const bundleDir =
|
|
51695
|
+
const bundleDir = path15.join(dir, "bundle");
|
|
51234
51696
|
const walk = (sub) => {
|
|
51235
|
-
for (const name of fs17.readdirSync(
|
|
51697
|
+
for (const name of fs17.readdirSync(path15.join(bundleDir, sub))) {
|
|
51236
51698
|
const rel = sub ? `${sub}/${name}` : name;
|
|
51237
|
-
const full =
|
|
51699
|
+
const full = path15.join(bundleDir, rel);
|
|
51238
51700
|
if (fs17.statSync(full).isDirectory()) walk(rel);
|
|
51239
51701
|
else bundle[rel] = fs17.readFileSync(full, "utf8");
|
|
51240
51702
|
}
|
|
@@ -51251,47 +51713,47 @@ async function exportTrajectories(dataDir) {
|
|
|
51251
51713
|
}
|
|
51252
51714
|
return samples;
|
|
51253
51715
|
}
|
|
51254
|
-
var fs17,
|
|
51716
|
+
var fs17, path15, FsTrajectorySink;
|
|
51255
51717
|
var init_trajectory = __esm({
|
|
51256
51718
|
"../cli/src/trajectory.ts"() {
|
|
51257
51719
|
"use strict";
|
|
51258
51720
|
fs17 = __toESM(require("node:fs"), 1);
|
|
51259
|
-
|
|
51721
|
+
path15 = __toESM(require("node:path"), 1);
|
|
51260
51722
|
init_src2();
|
|
51261
51723
|
init_src5();
|
|
51262
51724
|
FsTrajectorySink = class {
|
|
51263
51725
|
constructor(dataDir, broadcast) {
|
|
51264
51726
|
this.broadcast = broadcast;
|
|
51265
|
-
this.root =
|
|
51727
|
+
this.root = path15.join(dataDir, "trajectories");
|
|
51266
51728
|
}
|
|
51267
51729
|
root;
|
|
51268
51730
|
dir(sessionId) {
|
|
51269
|
-
return
|
|
51731
|
+
return path15.join(this.root, sessionId);
|
|
51270
51732
|
}
|
|
51271
51733
|
begin(session, bundleFiles) {
|
|
51272
51734
|
const dir = this.dir(session.sessionId);
|
|
51273
|
-
fs17.mkdirSync(
|
|
51274
|
-
fs17.writeFileSync(
|
|
51735
|
+
fs17.mkdirSync(path15.join(dir, "bundle"), { recursive: true });
|
|
51736
|
+
fs17.writeFileSync(path15.join(dir, "session.json"), JSON.stringify(session, null, 2));
|
|
51275
51737
|
for (const [rel, content] of Object.entries(bundleFiles)) {
|
|
51276
|
-
const file =
|
|
51277
|
-
fs17.mkdirSync(
|
|
51738
|
+
const file = path15.join(dir, "bundle", rel);
|
|
51739
|
+
fs17.mkdirSync(path15.dirname(file), { recursive: true });
|
|
51278
51740
|
fs17.writeFileSync(file, content);
|
|
51279
51741
|
}
|
|
51280
51742
|
this.broadcast?.({ type: "begin", sessionId: session.sessionId, data: session });
|
|
51281
51743
|
}
|
|
51282
51744
|
recover(session) {
|
|
51283
51745
|
fs17.mkdirSync(this.dir(session.sessionId), { recursive: true });
|
|
51284
|
-
const file =
|
|
51746
|
+
const file = path15.join(this.dir(session.sessionId), "session.json");
|
|
51285
51747
|
if (!fs17.existsSync(file)) fs17.writeFileSync(file, JSON.stringify(session, null, 2));
|
|
51286
51748
|
this.broadcast?.({ type: "begin", sessionId: session.sessionId, data: { ...session, recovered: true } });
|
|
51287
51749
|
}
|
|
51288
51750
|
event(sessionId, event) {
|
|
51289
51751
|
fs17.mkdirSync(this.dir(sessionId), { recursive: true });
|
|
51290
|
-
fs17.appendFileSync(
|
|
51752
|
+
fs17.appendFileSync(path15.join(this.dir(sessionId), "events.ndjson"), JSON.stringify(event) + "\n");
|
|
51291
51753
|
this.broadcast?.({ type: "event", sessionId, data: event });
|
|
51292
51754
|
}
|
|
51293
51755
|
end(sessionId, update) {
|
|
51294
|
-
const file =
|
|
51756
|
+
const file = path15.join(this.dir(sessionId), "session.json");
|
|
51295
51757
|
const session = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
51296
51758
|
session.exit = update.exit;
|
|
51297
51759
|
session.opRefs = update.opRefs;
|
|
@@ -51302,23 +51764,23 @@ var init_trajectory = __esm({
|
|
|
51302
51764
|
* 路径逻辑复用写入侧 `this.dir()`(单一真相,随 --dir 迁移同步);带路径穿越防护。 */
|
|
51303
51765
|
readBundleFile(sessionId, rel) {
|
|
51304
51766
|
if (!sessionId || sessionId.includes("/") || sessionId.includes("..")) return null;
|
|
51305
|
-
const bundleRoot =
|
|
51306
|
-
const target =
|
|
51307
|
-
if (target !== bundleRoot && !target.startsWith(bundleRoot +
|
|
51767
|
+
const bundleRoot = path15.join(this.dir(sessionId), "bundle");
|
|
51768
|
+
const target = path15.resolve(bundleRoot, rel);
|
|
51769
|
+
if (target !== bundleRoot && !target.startsWith(bundleRoot + path15.sep)) return null;
|
|
51308
51770
|
if (!fs17.existsSync(target) || !fs17.statSync(target).isFile()) return null;
|
|
51309
51771
|
return fs17.readFileSync(target, "utf8");
|
|
51310
51772
|
}
|
|
51311
51773
|
/** 列出某会话 bundle 下所有文件的相对路径(供前端「本次输入」点开清单)。 */
|
|
51312
51774
|
listBundle(sessionId) {
|
|
51313
51775
|
if (!sessionId || sessionId.includes("/") || sessionId.includes("..")) return [];
|
|
51314
|
-
const bundleRoot =
|
|
51776
|
+
const bundleRoot = path15.join(this.dir(sessionId), "bundle");
|
|
51315
51777
|
if (!fs17.existsSync(bundleRoot)) return [];
|
|
51316
51778
|
const out = [];
|
|
51317
51779
|
const walk = (d) => {
|
|
51318
51780
|
for (const e of fs17.readdirSync(d, { withFileTypes: true })) {
|
|
51319
|
-
const full =
|
|
51781
|
+
const full = path15.join(d, e.name);
|
|
51320
51782
|
if (e.isDirectory()) walk(full);
|
|
51321
|
-
else out.push(
|
|
51783
|
+
else out.push(path15.relative(bundleRoot, full));
|
|
51322
51784
|
}
|
|
51323
51785
|
};
|
|
51324
51786
|
walk(bundleRoot);
|
|
@@ -51330,21 +51792,21 @@ var init_trajectory = __esm({
|
|
|
51330
51792
|
|
|
51331
51793
|
// src/index.ts
|
|
51332
51794
|
var fs20 = __toESM(require("node:fs"));
|
|
51333
|
-
var
|
|
51334
|
-
var
|
|
51795
|
+
var os9 = __toESM(require("node:os"));
|
|
51796
|
+
var path18 = __toESM(require("node:path"));
|
|
51335
51797
|
var import_node_child_process15 = require("node:child_process");
|
|
51336
51798
|
|
|
51337
51799
|
// ../cli/src/cli.ts
|
|
51338
51800
|
var fs19 = __toESM(require("node:fs"), 1);
|
|
51339
|
-
var
|
|
51340
|
-
var
|
|
51341
|
-
var
|
|
51801
|
+
var os8 = __toESM(require("node:os"), 1);
|
|
51802
|
+
var path17 = __toESM(require("node:path"), 1);
|
|
51803
|
+
var import_node_crypto26 = require("node:crypto");
|
|
51342
51804
|
|
|
51343
51805
|
// ../cli/src/serve.ts
|
|
51344
51806
|
var fs18 = __toESM(require("node:fs"), 1);
|
|
51345
|
-
var
|
|
51346
|
-
var
|
|
51347
|
-
var
|
|
51807
|
+
var os7 = __toESM(require("node:os"), 1);
|
|
51808
|
+
var path16 = __toESM(require("node:path"), 1);
|
|
51809
|
+
var import_node_crypto24 = require("node:crypto");
|
|
51348
51810
|
var import_node_url4 = require("node:url");
|
|
51349
51811
|
init_src2();
|
|
51350
51812
|
init_src5();
|
|
@@ -52261,7 +52723,7 @@ function defaultDocumentPath2(input) {
|
|
|
52261
52723
|
init_src4();
|
|
52262
52724
|
|
|
52263
52725
|
// ../storage/src/postgres.ts
|
|
52264
|
-
var
|
|
52726
|
+
var import_node_crypto22 = require("node:crypto");
|
|
52265
52727
|
|
|
52266
52728
|
// ../../node_modules/.pnpm/pg@8.21.0/node_modules/pg/esm/index.mjs
|
|
52267
52729
|
var import_lib = __toESM(require_lib2(), 1);
|
|
@@ -52396,7 +52858,7 @@ var PostgresBlobStore = class _PostgresBlobStore {
|
|
|
52396
52858
|
return new _PostgresBlobStore(pool, schema);
|
|
52397
52859
|
}
|
|
52398
52860
|
async put(bytes) {
|
|
52399
|
-
const hash = (0,
|
|
52861
|
+
const hash = (0, import_node_crypto22.createHash)("sha256").update(bytes).digest("hex");
|
|
52400
52862
|
await this.pool.query(
|
|
52401
52863
|
`INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
|
|
52402
52864
|
[hash, Buffer.from(bytes), bytes.byteLength, sniffContentType(bytes) ?? null]
|
|
@@ -53861,8 +54323,8 @@ var PostgresControlPlaneStore = class _PostgresControlPlaneStore {
|
|
|
53861
54323
|
const r = await this.pool.query(`SELECT * FROM ${this.s}.companies WHERE id = $1`, [id]);
|
|
53862
54324
|
return r.rows[0] ? rowToCompany(r.rows[0]) : null;
|
|
53863
54325
|
}
|
|
53864
|
-
async getCompanyBySlug(
|
|
53865
|
-
const r = await this.pool.query(`SELECT * FROM ${this.s}.companies WHERE slug = $1`, [
|
|
54326
|
+
async getCompanyBySlug(slug4) {
|
|
54327
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.companies WHERE slug = $1`, [slug4]);
|
|
53866
54328
|
return r.rows[0] ? rowToCompany(r.rows[0]) : null;
|
|
53867
54329
|
}
|
|
53868
54330
|
async listCompanies() {
|
|
@@ -54306,7 +54768,7 @@ var PostgresTraceStore = class _PostgresTraceStore {
|
|
|
54306
54768
|
run.exitReason,
|
|
54307
54769
|
run.errorCode,
|
|
54308
54770
|
run.errorMessage,
|
|
54309
|
-
JSON.stringify(run.usage),
|
|
54771
|
+
run.usage === null ? null : JSON.stringify(run.usage),
|
|
54310
54772
|
run.effectiveModel,
|
|
54311
54773
|
run.modelSource,
|
|
54312
54774
|
run.transcriptRef,
|
|
@@ -54353,7 +54815,7 @@ var PostgresTraceStore = class _PostgresTraceStore {
|
|
|
54353
54815
|
for (const [key, spec] of Object.entries(_PostgresTraceStore.RUN_COLS)) {
|
|
54354
54816
|
if (!(key in patch)) continue;
|
|
54355
54817
|
const v2 = patch[key];
|
|
54356
|
-
args.push(spec.json ? JSON.stringify(v2
|
|
54818
|
+
args.push(spec.json ? v2 == null ? null : JSON.stringify(v2) : v2 ?? null);
|
|
54357
54819
|
sets.push(spec.json ? `${spec.col}=$${args.length}::jsonb` : `${spec.col}=$${args.length}`);
|
|
54358
54820
|
}
|
|
54359
54821
|
args.push(this.now());
|
|
@@ -54418,6 +54880,22 @@ var PostgresTraceStore = class _PostgresTraceStore {
|
|
|
54418
54880
|
const nextCursor = hasMore ? page[page.length - 1]?.id ?? null : null;
|
|
54419
54881
|
return { items: page, nextCursor };
|
|
54420
54882
|
}
|
|
54883
|
+
async listUsageRuns(filter) {
|
|
54884
|
+
const r = await this.pool.query(
|
|
54885
|
+
`SELECT actor_id, artifact_id, started_at, effective_model, usage
|
|
54886
|
+
FROM ${this.s}.agent_runs
|
|
54887
|
+
WHERE usage IS NOT NULL AND started_at >= $1 AND started_at <= $2
|
|
54888
|
+
ORDER BY started_at`,
|
|
54889
|
+
[filter.from, filter.to]
|
|
54890
|
+
);
|
|
54891
|
+
return r.rows.map((row) => ({
|
|
54892
|
+
actorId: row.actor_id,
|
|
54893
|
+
artifactId: row.artifact_id ?? null,
|
|
54894
|
+
startedAt: row.started_at,
|
|
54895
|
+
effectiveModel: row.effective_model ?? null,
|
|
54896
|
+
usage: row.usage
|
|
54897
|
+
}));
|
|
54898
|
+
}
|
|
54421
54899
|
/* ---------- 事件流(单写者 seq + 幂等吸收)---------- */
|
|
54422
54900
|
async appendEvent(runId, event) {
|
|
54423
54901
|
return (await this.appendEvents(runId, [event]))[0];
|
|
@@ -54706,7 +55184,7 @@ var PostgresTraceStore = class _PostgresTraceStore {
|
|
|
54706
55184
|
COALESCE(SUM(COALESCE((usage->>'cacheCreationTokens')::bigint, 0)), 0),
|
|
54707
55185
|
COALESCE(SUM(COALESCE((usage->>'costUsdMicros')::bigint, 0)), 0)
|
|
54708
55186
|
FROM ${this.s}.agent_runs
|
|
54709
|
-
WHERE usage
|
|
55187
|
+
WHERE jsonb_typeof(usage) = 'object'
|
|
54710
55188
|
AND started_at >= $2 AND started_at < $3
|
|
54711
55189
|
GROUP BY actor_id, runtime_instance_id, runtime_kind, effective_model, hour`,
|
|
54712
55190
|
[cid, from, to]
|
|
@@ -55412,7 +55890,7 @@ var rowToMessage = (row) => ({
|
|
|
55412
55890
|
});
|
|
55413
55891
|
|
|
55414
55892
|
// ../storage/src/postgres-nodes.ts
|
|
55415
|
-
var
|
|
55893
|
+
var import_node_crypto23 = require("node:crypto");
|
|
55416
55894
|
var ident8 = (s2) => {
|
|
55417
55895
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
55418
55896
|
return s2;
|
|
@@ -55550,7 +56028,7 @@ var PostgresNodeTokenStore = class _PostgresNodeTokenStore {
|
|
|
55550
56028
|
return store;
|
|
55551
56029
|
}
|
|
55552
56030
|
issue(nodeId) {
|
|
55553
|
-
const token = `ont_${(0,
|
|
56031
|
+
const token = `ont_${(0, import_node_crypto23.randomBytes)(24).toString("base64url")}`;
|
|
55554
56032
|
this.cache.set(token, nodeId);
|
|
55555
56033
|
void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token, nodeId]);
|
|
55556
56034
|
return token;
|
|
@@ -55697,7 +56175,7 @@ function builtinSkillsDir() {
|
|
|
55697
56175
|
const override = process.env["OASIS_BUILTIN_SKILLS_DIR"]?.trim();
|
|
55698
56176
|
if (override) return override;
|
|
55699
56177
|
const repoRoot = (0, import_node_url4.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
|
|
55700
|
-
return
|
|
56178
|
+
return path16.join(repoRoot, "skills");
|
|
55701
56179
|
}
|
|
55702
56180
|
function materializeBuiltins(builtins, runtimeKind) {
|
|
55703
56181
|
if (builtins.length === 0) return {};
|
|
@@ -55731,10 +56209,10 @@ var oasisWrapperCache;
|
|
|
55731
56209
|
function oasisWrapperScript() {
|
|
55732
56210
|
if (oasisWrapperCache && fs18.existsSync(oasisWrapperCache)) return oasisWrapperCache;
|
|
55733
56211
|
const repoRoot = (0, import_node_url4.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
|
|
55734
|
-
const tsx =
|
|
55735
|
-
const main =
|
|
55736
|
-
const dir = fs18.mkdtempSync(
|
|
55737
|
-
const wrapper =
|
|
56212
|
+
const tsx = path16.join(repoRoot, "node_modules/.bin/tsx");
|
|
56213
|
+
const main = path16.join(repoRoot, "packages/cli/src/main.ts");
|
|
56214
|
+
const dir = fs18.mkdtempSync(path16.join(os7.tmpdir(), "oasis-cli-"));
|
|
56215
|
+
const wrapper = path16.join(dir, "oasis");
|
|
55738
56216
|
fs18.writeFileSync(wrapper, `#!/usr/bin/env bash
|
|
55739
56217
|
exec ${JSON.stringify(tsx)} ${JSON.stringify(main)} "$@"
|
|
55740
56218
|
`);
|
|
@@ -55826,7 +56304,7 @@ function traceRuntimeKind(runtimeKind) {
|
|
|
55826
56304
|
async function startServe(opts) {
|
|
55827
56305
|
const serverStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
55828
56306
|
fs18.mkdirSync(opts.dir, { recursive: true });
|
|
55829
|
-
const serverHeartbeatFile =
|
|
56307
|
+
const serverHeartbeatFile = path16.join(opts.dir, "serve-heartbeat.json");
|
|
55830
56308
|
let previousServerHeartbeatAt;
|
|
55831
56309
|
if (fs18.existsSync(serverHeartbeatFile)) {
|
|
55832
56310
|
try {
|
|
@@ -55842,7 +56320,7 @@ async function startServe(opts) {
|
|
|
55842
56320
|
writeServerHeartbeat();
|
|
55843
56321
|
const serverHeartbeatTimer = setInterval(writeServerHeartbeat, 5e3);
|
|
55844
56322
|
serverHeartbeatTimer.unref?.();
|
|
55845
|
-
const lock =
|
|
56323
|
+
const lock = path16.join(opts.dir, "serve.lock");
|
|
55846
56324
|
if (fs18.existsSync(lock)) {
|
|
55847
56325
|
const pid = Number(fs18.readFileSync(lock, "utf8"));
|
|
55848
56326
|
let alive = false;
|
|
@@ -55856,15 +56334,15 @@ async function startServe(opts) {
|
|
|
55856
56334
|
}
|
|
55857
56335
|
fs18.writeFileSync(lock, String(process.pid));
|
|
55858
56336
|
const loadJson = (name) => {
|
|
55859
|
-
const file =
|
|
56337
|
+
const file = path16.join(opts.dir, name);
|
|
55860
56338
|
return fs18.existsSync(file) ? JSON.parse(fs18.readFileSync(file, "utf8")) : void 0;
|
|
55861
56339
|
};
|
|
55862
56340
|
const pgDsn = process.env["OASIS_PG_DSN"] ?? process.env["DATABASE_URL"];
|
|
55863
56341
|
const pgSchema = process.env["OASIS_PG_SCHEMA"] ?? "public";
|
|
55864
56342
|
const pgPool = pgDsn ? createPgPool(pgDsn) : void 0;
|
|
55865
|
-
const oplog = pgDsn && pgPool ? await PostgresOplogStore.open(pgPool, pgSchema) : await NdjsonOplogStore.open(
|
|
55866
|
-
const blobs = pgDsn && pgPool ? await PostgresBlobStore.open(pgPool, pgSchema) : new DirBlobStore(
|
|
55867
|
-
const assets = new DirBlobStore(
|
|
56343
|
+
const oplog = pgDsn && pgPool ? await PostgresOplogStore.open(pgPool, pgSchema) : await NdjsonOplogStore.open(path16.join(opts.dir, "oplog.ndjson"));
|
|
56344
|
+
const blobs = pgDsn && pgPool ? await PostgresBlobStore.open(pgPool, pgSchema) : new DirBlobStore(path16.join(opts.dir, "blobs"));
|
|
56345
|
+
const assets = new DirBlobStore(path16.join(opts.dir, "assets"));
|
|
55868
56346
|
const defaultRootSchema = [
|
|
55869
56347
|
{ name: "brief", description: "\u5DE5\u5355\u603B\u7EB2\uFF1A\u53D1\u8D77\u65B9\u5BF9\u9F50\u7684\u603B\u76EE\u6807\u4E0E\u8303\u56F4\uFF0C\u6574\u5F20\u534F\u4F5C\u56FE\u7684\u6839\u3002", contentType: "text", ownerRole: "founder" },
|
|
55870
56348
|
{
|
|
@@ -55882,7 +56360,7 @@ async function startServe(opts) {
|
|
|
55882
56360
|
if (!schema.some((d) => d.name === "document")) {
|
|
55883
56361
|
schema.push({ name: "document", description: "\u901A\u7528\u6587\u6863\uFF1A\u672A\u7EC6\u5206\u7684\u6587\u5B57\u4EA7\u7269\uFF08\u5177\u4F53\u7C7B\u578B\u89C1 docType\uFF09\u3002", contentType: "text", ownerRole: "writer" });
|
|
55884
56362
|
}
|
|
55885
|
-
const typeStore = await FileTypeRegistryStore.open(
|
|
56363
|
+
const typeStore = await FileTypeRegistryStore.open(path16.join(opts.dir, "type-registry.json"));
|
|
55886
56364
|
if ((await typeStore.list()).length === 0) for (const d of schema) await typeStore.put(d);
|
|
55887
56365
|
{
|
|
55888
56366
|
const live = await typeStore.list();
|
|
@@ -55890,7 +56368,7 @@ async function startServe(opts) {
|
|
|
55890
56368
|
schema.length = 0;
|
|
55891
56369
|
schema.push(...live);
|
|
55892
56370
|
}
|
|
55893
|
-
const registryStore = pgDsn && pgPool ? await PostgresRegistryStore.open(pgPool, pgSchema) : await FileRegistryStore.open(
|
|
56371
|
+
const registryStore = pgDsn && pgPool ? await PostgresRegistryStore.open(pgPool, pgSchema) : await FileRegistryStore.open(path16.join(opts.dir, "registry-store.json"));
|
|
55894
56372
|
try {
|
|
55895
56373
|
await backfillSkillContentFromAssets(registryStore, assets);
|
|
55896
56374
|
} catch (err) {
|
|
@@ -55914,14 +56392,14 @@ async function startServe(opts) {
|
|
|
55914
56392
|
// per-contentType merge facet(§5.6):text=expensive→group-commit、code/structured=cheap→顺序 rebase
|
|
55915
56393
|
handlers: defaultHandlersByContentType()
|
|
55916
56394
|
});
|
|
55917
|
-
const issuer = createTokenIssuer({ secretFile:
|
|
55918
|
-
const operatorActor = `actor:human:${
|
|
56395
|
+
const issuer = createTokenIssuer({ secretFile: path16.join(opts.dir, "session-token-secret") });
|
|
56396
|
+
const operatorActor = `actor:human:${os7.userInfo().username}`;
|
|
55919
56397
|
const operatorToken = issuer.issue(operatorActor);
|
|
55920
|
-
fs18.writeFileSync(
|
|
55921
|
-
let nodeTokens = createNodeTokenStore(
|
|
55922
|
-
const enrollTokens = createEnrollTokenStore(30 * 60 * 1e3,
|
|
55923
|
-
let nodeStore = await FileNodeStore.open(
|
|
55924
|
-
const registryAuditFile =
|
|
56398
|
+
fs18.writeFileSync(path16.join(opts.dir, "operator-token"), operatorToken, { mode: 384 });
|
|
56399
|
+
let nodeTokens = createNodeTokenStore(path16.join(opts.dir, "node-tokens.json"));
|
|
56400
|
+
const enrollTokens = createEnrollTokenStore(30 * 60 * 1e3, path16.join(opts.dir, "enroll-tokens.json"));
|
|
56401
|
+
let nodeStore = await FileNodeStore.open(path16.join(opts.dir, "nodes.json"));
|
|
56402
|
+
const registryAuditFile = path16.join(opts.dir, "registry-audit.ndjson");
|
|
55925
56403
|
const registryListeners = /* @__PURE__ */ new Set();
|
|
55926
56404
|
const registryAudit = (record5) => {
|
|
55927
56405
|
fs18.appendFile(registryAuditFile, JSON.stringify(record5) + "\n", () => {
|
|
@@ -55963,9 +56441,9 @@ async function startServe(opts) {
|
|
|
55963
56441
|
nodeTokens = await PostgresNodeTokenStore.open(pgPool, pgSchema);
|
|
55964
56442
|
console.log(`[serve] \u4E8B\u5B9E/\u72B6\u6001\u4F53\u7CFB\uFF1APostgres schema=${pgSchema}\uFF08oplog / registry / blobs / artifacts / chat\uFF09`);
|
|
55965
56443
|
} else {
|
|
55966
|
-
projectStateStore = await FileProjectStateStore.open(
|
|
55967
|
-
artifactStateStore = await FileArtifactStateStore.open(
|
|
55968
|
-
chatSessionStore = await FileChatSessionStore.open(
|
|
56444
|
+
projectStateStore = await FileProjectStateStore.open(path16.join(opts.dir, "artifact-document-projects.json"));
|
|
56445
|
+
artifactStateStore = await FileArtifactStateStore.open(path16.join(opts.dir, "artifact-document-state.json"));
|
|
56446
|
+
chatSessionStore = await FileChatSessionStore.open(path16.join(opts.dir, "chat-sessions.json"));
|
|
55969
56447
|
console.log("[serve] \u4EA4\u4ED8\u7269\u72B6\u6001\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
|
|
55970
56448
|
console.log("[serve] \u8282\u70B9/\u8FD0\u884C\u65F6\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
|
|
55971
56449
|
}
|
|
@@ -55984,7 +56462,7 @@ async function startServe(opts) {
|
|
|
55984
56462
|
});
|
|
55985
56463
|
console.log(`[serve] \u9879\u76EE\u6587\u6863\u4F53\u7CFB\uFF1AOutline ${outlineUrl}\uFF08\u6302\u5728 Team Workspace\u300C\u9879\u76EE\u300D\u4E0B\uFF09`);
|
|
55986
56464
|
} else {
|
|
55987
|
-
projectDocumentStore = await FileProjectDocumentStore.open(
|
|
56465
|
+
projectDocumentStore = await FileProjectDocumentStore.open(path16.join(opts.dir, "project-document-spaces.json"));
|
|
55988
56466
|
console.log("[serve] \u9879\u76EE\u6587\u6863\u4F53\u7CFB\uFF1A\u672C\u5730\u5185\u5B58/\u6587\u4EF6 dev store");
|
|
55989
56467
|
}
|
|
55990
56468
|
const registryActorNames = /* @__PURE__ */ new Map();
|
|
@@ -56018,7 +56496,7 @@ async function startServe(opts) {
|
|
|
56018
56496
|
traceStore = await PostgresTraceStore.open(tracePool, pgSchema);
|
|
56019
56497
|
console.log(`[serve] \u8FD0\u884C\u8F68\u8FF9\u4F53\u7CFB\uFF1APostgres schema=${pgSchema}${traceDsn === pgDsn ? "" : "\uFF08\u72EC\u7ACB trace \u5E93\uFF09"}`);
|
|
56020
56498
|
} else {
|
|
56021
|
-
traceStore = await FileTraceStore.open(
|
|
56499
|
+
traceStore = await FileTraceStore.open(path16.join(opts.dir, "trace"));
|
|
56022
56500
|
console.log("[serve] \u8FD0\u884C\u8F68\u8FF9\u4F53\u7CFB\uFF1A\u672C\u5730 ndjson dev store");
|
|
56023
56501
|
}
|
|
56024
56502
|
traceStoreForActors = traceStore;
|
|
@@ -56039,7 +56517,7 @@ async function startServe(opts) {
|
|
|
56039
56517
|
create: createSeededWorkorder,
|
|
56040
56518
|
projectExists: async (id) => Boolean(await projectStateStore.getProject(id))
|
|
56041
56519
|
});
|
|
56042
|
-
const controlPlaneStore = pgDsn && pgPool ? await PostgresControlPlaneStore.open(pgPool, pgSchema) : await FileControlPlaneStore.open(
|
|
56520
|
+
const controlPlaneStore = pgDsn && pgPool ? await PostgresControlPlaneStore.open(pgPool, pgSchema) : await FileControlPlaneStore.open(path16.join(opts.dir, "control-plane.json"));
|
|
56043
56521
|
if (pgDsn && pgPool) {
|
|
56044
56522
|
const priceStore = await PostgresModelPriceStore.open(pgPool, pgSchema);
|
|
56045
56523
|
setModelPriceCacheLoader(async () => {
|
|
@@ -56128,8 +56606,8 @@ async function startServe(opts) {
|
|
|
56128
56606
|
}
|
|
56129
56607
|
return auth.sessionAccountId(headers);
|
|
56130
56608
|
};
|
|
56131
|
-
const workorderDrafts = new WorkorderDraftStore(
|
|
56132
|
-
const journalFile =
|
|
56609
|
+
const workorderDrafts = new WorkorderDraftStore(path16.join(opts.dir, "workorder-drafts.json"));
|
|
56610
|
+
const journalFile = path16.join(opts.dir, "dispatch-journal.ndjson");
|
|
56133
56611
|
const journalRing = [];
|
|
56134
56612
|
if (fs18.existsSync(journalFile)) {
|
|
56135
56613
|
try {
|
|
@@ -56173,7 +56651,7 @@ async function startServe(opts) {
|
|
|
56173
56651
|
defaultEngine: { kernel, oplog, blobs, registry: registryStore, assets },
|
|
56174
56652
|
defaultCompanyId,
|
|
56175
56653
|
buildEngine: async (companyId) => {
|
|
56176
|
-
const engine2 = await openFileEngine(
|
|
56654
|
+
const engine2 = await openFileEngine(path16.join(opts.dir, "companies", companyEngineDirName(companyId)), { schema });
|
|
56177
56655
|
await ensureCompanyActors(controlPlaneStore, companyId, engine2.registry);
|
|
56178
56656
|
await syncRegistryRolesToKernel(engine2.registry, engine2.kernel);
|
|
56179
56657
|
return engine2;
|
|
@@ -56290,7 +56768,7 @@ async function startServe(opts) {
|
|
|
56290
56768
|
}));
|
|
56291
56769
|
}
|
|
56292
56770
|
const limits = { wallClockMs: SESSION_WALL_CLOCK_MS.planner };
|
|
56293
|
-
const artifactId = `artifact:planner:${(0,
|
|
56771
|
+
const artifactId = `artifact:planner:${(0, import_node_crypto24.randomUUID)()}`;
|
|
56294
56772
|
const handle = await chatRemoteAdapter.spawn({
|
|
56295
56773
|
actor: planner.id,
|
|
56296
56774
|
actorToken: issueSessionToken(planner.id, { artifactId, action: "plan-workorder", limits, binding }),
|
|
@@ -56318,7 +56796,7 @@ async function startServe(opts) {
|
|
|
56318
56796
|
resolveCodeRepo: (ws) => resolveCodeRepoRef?.(ws) ?? null,
|
|
56319
56797
|
// B3 auto-merge(§9.7):part propose 时把工作分支合进伞分支的兜底 mirror 根——与 GitSyncWorker/CiGate
|
|
56320
56798
|
// 的 git-mirrors **分开**(各自 checkout 不同分支,共享 clone 会竞态)。
|
|
56321
|
-
gitMirrorRoot:
|
|
56799
|
+
gitMirrorRoot: path16.join(opts.dir, "propose-mirrors"),
|
|
56322
56800
|
domains: [
|
|
56323
56801
|
actors.register,
|
|
56324
56802
|
companies.register,
|
|
@@ -56336,6 +56814,7 @@ async function startServe(opts) {
|
|
|
56336
56814
|
collabDomain({
|
|
56337
56815
|
kernel,
|
|
56338
56816
|
oplog,
|
|
56817
|
+
trace: trace.service,
|
|
56339
56818
|
registry: registryStore,
|
|
56340
56819
|
artifacts: artifactStateStore,
|
|
56341
56820
|
dispatchJournal: () => journalRing.slice(-200),
|
|
@@ -56401,8 +56880,8 @@ async function startServe(opts) {
|
|
|
56401
56880
|
},
|
|
56402
56881
|
retainedSession: (key) => key.annotationId && key.artifactId ? allDispatchers().map((d) => d.retainedProduceSessionFor(key.artifactId)).find(Boolean) : coordinatorRef?.retainedSessionFor(key),
|
|
56403
56882
|
// 0030 D3/D7 落盘:discuss 按项表写文件,serve 重启后同一卡点续同一段、不再新开。
|
|
56404
|
-
discussSessions: new FileSideMap(
|
|
56405
|
-
escalateDiscussRetain: new FileSideMap(
|
|
56883
|
+
discussSessions: new FileSideMap(path16.join(opts.dir, "discuss-sessions.json")),
|
|
56884
|
+
escalateDiscussRetain: new FileSideMap(path16.join(opts.dir, "escalate-discuss-retain.json")),
|
|
56406
56885
|
// 切片 2b:代署 escalate 发起会话保留(落盘)
|
|
56407
56886
|
dispatchChat: async ({ actorId, message, sessionId, chatSessionId, attachments, workspace, companyId }) => {
|
|
56408
56887
|
const runCompanyId = companyId ?? defaultCompanyId;
|
|
@@ -56425,10 +56904,10 @@ async function startServe(opts) {
|
|
|
56425
56904
|
if (!localUrl) {
|
|
56426
56905
|
throw new ApiError(503, "SERVER_URL_NOT_READY", "server URL \u5C1A\u672A\u5C31\u7EEA");
|
|
56427
56906
|
}
|
|
56428
|
-
const runtimeSessionId = sessionId ?? (0,
|
|
56907
|
+
const runtimeSessionId = sessionId ?? (0, import_node_crypto24.randomUUID)();
|
|
56429
56908
|
const resumeRuntimeSession = Boolean(sessionId);
|
|
56430
|
-
const traceRunId = `chat-run:${(0,
|
|
56431
|
-
const artifactId = `artifact:chat:${(0,
|
|
56909
|
+
const traceRunId = `chat-run:${(0, import_node_crypto24.randomUUID)()}`;
|
|
56910
|
+
const artifactId = `artifact:chat:${(0, import_node_crypto24.randomUUID)()}`;
|
|
56432
56911
|
const traceStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
56433
56912
|
let traceSeq = 0;
|
|
56434
56913
|
let lastProgressTouchMs = 0;
|
|
@@ -56707,7 +57186,7 @@ ${detail}` : genericLine));
|
|
|
56707
57186
|
console.log(`[serve] \u8FDC\u7A0B agent API URL\uFF1A${remoteApiUrl ?? "\u672A\u914D\u7F6E\uFF08\u4EC5\u652F\u6301\u672C\u673A runtime\uFF09"}`);
|
|
56708
57187
|
const projectsCfg = loadJson("projects.json");
|
|
56709
57188
|
const seedProjects = (projectsCfg?.projects ?? []).map(
|
|
56710
|
-
(p2) => p2.repos?.length ? { ...p2, repos: p2.repos.map((r) => r.repoDir ? { ...r, repoDir:
|
|
57189
|
+
(p2) => p2.repos?.length ? { ...p2, repos: p2.repos.map((r) => r.repoDir ? { ...r, repoDir: path16.resolve(opts.dir, r.repoDir) } : r) } : p2
|
|
56711
57190
|
);
|
|
56712
57191
|
for (const sp of seedProjects) {
|
|
56713
57192
|
const existing = await projectStateStore.getProject(sp.id);
|
|
@@ -56837,8 +57316,8 @@ ${detail}` : genericLine));
|
|
|
56837
57316
|
// 每 agent 并发产出上限默认 5(工单数 / 全局总量默认不限);--max-produce-per-agent N 覆盖。
|
|
56838
57317
|
maxConcurrentProducePerActor: opts.maxConcurrentProducePerActor ?? 5,
|
|
56839
57318
|
// 提案 uniform-annotation-resolve ③/④ 落盘:容错计数 + produce 会话号(重启不丢;丢了也只是降级/多容错一轮)
|
|
56840
|
-
annotationRescue: new FileSideMap(
|
|
56841
|
-
produceSessions: new FileSideMap(
|
|
57319
|
+
annotationRescue: new FileSideMap(path16.join(opts.dir, `annotation-rescue.${companyId}.json`)),
|
|
57320
|
+
produceSessions: new FileSideMap(path16.join(opts.dir, `produce-sessions.${companyId}.json`))
|
|
56842
57321
|
});
|
|
56843
57322
|
const slot = { dispatcher: d, logged: 0, ticking: false };
|
|
56844
57323
|
dispatchers.set(companyId, slot);
|
|
@@ -57010,8 +57489,17 @@ ${detail}` : genericLine));
|
|
|
57010
57489
|
if (f2.action !== "produce") continue;
|
|
57011
57490
|
const cur = kernel.model.artifacts.get(f2.artifactId);
|
|
57012
57491
|
if (!cur) continue;
|
|
57013
|
-
if (now - Date.parse(f2.fusedAt)
|
|
57014
|
-
|
|
57492
|
+
if (now - Date.parse(f2.fusedAt) <= agentMs) continue;
|
|
57493
|
+
if (unresolvedEscalationsOf(kernel.model, f2.artifactId).length > 0) continue;
|
|
57494
|
+
if (isHeld(kernel.model, f2.artifactId)) continue;
|
|
57495
|
+
const reason = `agent \u8FDE\u8D25\u7194\u65AD\u540E ${Math.round(agentMs / 6e4)} \u5206\u949F\u65E0\u4EBA\u5904\u7406\uFF08\u8FDE\u8D25 ${f2.consecutiveFailures} \u6B21\uFF09`;
|
|
57496
|
+
try {
|
|
57497
|
+
await kernel.escalate({ artifactId: f2.artifactId, actor: SYSTEM_ACTOR, reason });
|
|
57498
|
+
await kernel.hold({ artifactId: f2.artifactId, actor: SYSTEM_ACTOR, source: "sla", reason });
|
|
57499
|
+
journal({ kind: "escalated", at: (/* @__PURE__ */ new Date()).toISOString(), artifactId: f2.artifactId, from: cur.owner, to: cur.owner, reason });
|
|
57500
|
+
console.log(`[sla] ${f2.artifactId} \u7194\u65AD\u5347\u7EA7\uFF1A\u843D escalate + hold\uFF08owner \u4FDD\u6301 ${cur.owner}\uFF0C\u4E0D\u593A\uFF09`);
|
|
57501
|
+
} catch (err) {
|
|
57502
|
+
console.error(`[sla] \u7194\u65AD\u5347\u7EA7\u5931\u8D25 ${f2.artifactId}: ${String(err)}`);
|
|
57015
57503
|
}
|
|
57016
57504
|
}
|
|
57017
57505
|
})().catch((err) => console.error(`[sla] \u626B\u63CF\u5931\u8D25: ${String(err)}`));
|
|
@@ -57024,7 +57512,7 @@ ${detail}` : genericLine));
|
|
|
57024
57512
|
schema: schemaMap,
|
|
57025
57513
|
mirrors: new Map(opts.mirror.connectors.map((c) => [c.platform, c])),
|
|
57026
57514
|
identities: opts.mirror.identities ?? new MapMirrorIdentities(),
|
|
57027
|
-
state: new FsMirrorStateStore(
|
|
57515
|
+
state: new FsMirrorStateStore(path16.join(opts.dir, "mirror-state.json")),
|
|
57028
57516
|
...opts.mirror.debounceMs !== void 0 ? { debounceMs: opts.mirror.debounceMs } : {},
|
|
57029
57517
|
log: (m2) => console.log(m2)
|
|
57030
57518
|
});
|
|
@@ -57034,27 +57522,20 @@ ${detail}` : genericLine));
|
|
|
57034
57522
|
}
|
|
57035
57523
|
let gitTimer;
|
|
57036
57524
|
if (opts.dispatch) {
|
|
57037
|
-
|
|
57038
|
-
|
|
57039
|
-
|
|
57040
|
-
|
|
57041
|
-
|
|
57042
|
-
|
|
57043
|
-
|
|
57044
|
-
|
|
57045
|
-
|
|
57046
|
-
|
|
57047
|
-
|
|
57048
|
-
|
|
57049
|
-
|
|
57050
|
-
|
|
57051
|
-
mirrorRoot: path17.join(opts.dir, "git-mirrors"),
|
|
57052
|
-
log: (m2) => console.log(m2)
|
|
57053
|
-
});
|
|
57054
|
-
gitTimer = setInterval(() => {
|
|
57055
|
-
void gitSync.tick().catch((err) => console.error(`[git] \u540C\u6B65\u5931\u8D25: ${String(err)}`));
|
|
57056
|
-
}, 3e4);
|
|
57057
|
-
}
|
|
57525
|
+
const gitMirrorRoot = path16.join(opts.dir, "git-mirrors");
|
|
57526
|
+
const ci = new GhCiProvider({
|
|
57527
|
+
mirrorRoot: gitMirrorRoot,
|
|
57528
|
+
failClosedWhenNoChecks: process.env["OASIS_CI_FAIL_CLOSED"] === "1",
|
|
57529
|
+
log: (m2) => console.log(m2)
|
|
57530
|
+
});
|
|
57531
|
+
const mergeWorker = new MergeWorker({ kernel, resolveProject, ci, mirrorRoot: gitMirrorRoot, statusSink: ciGateStatus, log: (m2) => console.log(m2) });
|
|
57532
|
+
const ciGate = ciGateEnabled ? new CiGateWorker({ kernel, resolveProject, ci, statusSink: ciGateStatus, log: (m2) => console.log(m2) }) : void 0;
|
|
57533
|
+
gitTimer = setInterval(() => {
|
|
57534
|
+
void (async () => {
|
|
57535
|
+
if (ciGate) await ciGate.tick().catch((err) => console.error(`[ci] tick \u5931\u8D25: ${String(err)}`));
|
|
57536
|
+
await mergeWorker.tick().catch((err) => console.error(`[merge] tick \u5931\u8D25: ${String(err)}`));
|
|
57537
|
+
})();
|
|
57538
|
+
}, 3e4);
|
|
57058
57539
|
}
|
|
57059
57540
|
let coordTimer;
|
|
57060
57541
|
let livenessTimer;
|
|
@@ -57109,7 +57590,7 @@ ${detail}` : genericLine));
|
|
|
57109
57590
|
// 病历里展示节点的"在跑会话情况"(runId + 跑了多久):从派发器在途会话按 artifactId 过滤。
|
|
57110
57591
|
sessionsOf: (id) => allDispatchers().flatMap((d) => d.inFlightSessions()).filter((s2) => s2.artifactId === id).map((s2) => ({ runId: s2.sessionId, action: s2.action, startedAt: s2.startedAt })),
|
|
57111
57592
|
log: (m2) => console.log(m2),
|
|
57112
|
-
retention: new FileSideMap(
|
|
57593
|
+
retention: new FileSideMap(path16.join(opts.dir, "retained-sessions.json"))
|
|
57113
57594
|
// 0030 D7 落盘
|
|
57114
57595
|
});
|
|
57115
57596
|
coordinatorRef = coordinator;
|
|
@@ -57653,7 +58134,7 @@ async function startNode(opts) {
|
|
|
57653
58134
|
// ../cli/src/daemon/machine-id.ts
|
|
57654
58135
|
var import_node_child_process14 = require("node:child_process");
|
|
57655
58136
|
var import_node_fs6 = require("node:fs");
|
|
57656
|
-
var
|
|
58137
|
+
var import_node_crypto25 = require("node:crypto");
|
|
57657
58138
|
var import_node_os7 = require("node:os");
|
|
57658
58139
|
function linuxMachineId() {
|
|
57659
58140
|
for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
@@ -57719,7 +58200,7 @@ var defaultSources = {
|
|
|
57719
58200
|
function resolveNodeId(sources = {}) {
|
|
57720
58201
|
const s2 = { ...defaultSources, ...sources };
|
|
57721
58202
|
const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
|
|
57722
|
-
const digest = (0,
|
|
58203
|
+
const digest = (0, import_node_crypto25.createHash)("sha256").update(material).digest("hex").slice(0, 12);
|
|
57723
58204
|
return `node-${digest}`;
|
|
57724
58205
|
}
|
|
57725
58206
|
|
|
@@ -57889,8 +58370,8 @@ function makeClient(base, token) {
|
|
|
57889
58370
|
if (!res.ok) await fail(res);
|
|
57890
58371
|
return (await res.json()).data;
|
|
57891
58372
|
},
|
|
57892
|
-
async request(method,
|
|
57893
|
-
const res = await fetch(`${base}${
|
|
58373
|
+
async request(method, path19, body) {
|
|
58374
|
+
const res = await fetch(`${base}${path19}`, {
|
|
57894
58375
|
method,
|
|
57895
58376
|
headers,
|
|
57896
58377
|
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
@@ -57898,8 +58379,8 @@ function makeClient(base, token) {
|
|
|
57898
58379
|
if (!res.ok) await fail(res);
|
|
57899
58380
|
return await res.json();
|
|
57900
58381
|
},
|
|
57901
|
-
async post(
|
|
57902
|
-
const res = await fetch(`${base}${
|
|
58382
|
+
async post(path19, body) {
|
|
58383
|
+
const res = await fetch(`${base}${path19}`, { method: "POST", headers, body: JSON.stringify(body) });
|
|
57903
58384
|
if (!res.ok) await fail(res);
|
|
57904
58385
|
return await res.json();
|
|
57905
58386
|
}
|
|
@@ -57912,14 +58393,14 @@ async function runCli(argv, println = console.log) {
|
|
|
57912
58393
|
return;
|
|
57913
58394
|
}
|
|
57914
58395
|
const { positional, flags } = parseArgs(rest);
|
|
57915
|
-
const dir = flags.get("dir") ?? process.env["OASIS_DIR"] ??
|
|
58396
|
+
const dir = flags.get("dir") ?? process.env["OASIS_DIR"] ?? path17.resolve(".oasis");
|
|
57916
58397
|
if (command === "node" && positional[0] !== "output") {
|
|
57917
58398
|
const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? "ws://127.0.0.1:7320/node-gateway";
|
|
57918
58399
|
const nodeName = flags.get("name") ?? process.env["OASIS_NODE_NAME"];
|
|
57919
|
-
const nodeIdFile =
|
|
57920
|
-
const nodeTokenFile =
|
|
57921
|
-
const enrollTokenFile =
|
|
57922
|
-
const reportMarkerFile =
|
|
58400
|
+
const nodeIdFile = path17.join(dir, "node-id");
|
|
58401
|
+
const nodeTokenFile = path17.join(dir, "node-token");
|
|
58402
|
+
const enrollTokenFile = path17.join(dir, "enroll-token");
|
|
58403
|
+
const reportMarkerFile = path17.join(dir, "report-runtimes");
|
|
57923
58404
|
const persistedNodeId = fs19.existsSync(nodeIdFile) ? fs19.readFileSync(nodeIdFile, "utf8").trim() : null;
|
|
57924
58405
|
let nodeId = persistedNodeId || resolveNodeId();
|
|
57925
58406
|
let token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs19.existsSync(nodeTokenFile) ? fs19.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
|
|
@@ -57930,7 +58411,7 @@ async function runCli(argv, println = console.log) {
|
|
|
57930
58411
|
const res = await fetch(`${httpBase2}/api/nodes/exchange`, {
|
|
57931
58412
|
method: "POST",
|
|
57932
58413
|
headers: { "content-type": "application/json" },
|
|
57933
|
-
body: JSON.stringify({ enrollToken, nodeId, hostname:
|
|
58414
|
+
body: JSON.stringify({ enrollToken, nodeId, hostname: os8.hostname() })
|
|
57934
58415
|
});
|
|
57935
58416
|
if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
|
|
57936
58417
|
const body = await res.json();
|
|
@@ -57960,11 +58441,11 @@ async function runCli(argv, println = console.log) {
|
|
|
57960
58441
|
println("\u2192 Installing oasis_test globally...");
|
|
57961
58442
|
spawnSync("npm", ["install", "-g", "oasis_test@latest"], { stdio: "inherit" });
|
|
57962
58443
|
const prefix = execSync2("npm prefix -g").toString().trim();
|
|
57963
|
-
const binPath =
|
|
58444
|
+
const binPath = path17.join(prefix, "bin", "oasis_test");
|
|
57964
58445
|
const svcArgs = `node --server-ws ${serverUrl} --dir ${dir}${nodeName ? ` --name ${nodeName}` : ""}`;
|
|
57965
58446
|
if (process.platform === "linux") {
|
|
57966
|
-
const svcFile =
|
|
57967
|
-
fs19.mkdirSync(
|
|
58447
|
+
const svcFile = path17.join(os8.homedir(), ".config/systemd/user/oasis-node.service");
|
|
58448
|
+
fs19.mkdirSync(path17.dirname(svcFile), { recursive: true });
|
|
57968
58449
|
if (spawnSync("systemctl", ["--user", "is-active", "--quiet", "oasis-node"], {}).status === 0)
|
|
57969
58450
|
spawnSync("systemctl", ["--user", "stop", "oasis-node"], { stdio: "inherit" });
|
|
57970
58451
|
fs19.writeFileSync(svcFile, [
|
|
@@ -57976,7 +58457,7 @@ async function runCli(argv, println = console.log) {
|
|
|
57976
58457
|
`ExecStart=${binPath} ${svcArgs}`,
|
|
57977
58458
|
// Carry the install-time PATH so the detached daemon detects CLIs in user dirs
|
|
57978
58459
|
// (~/.npm-global/bin, ~/.local/bin) — systemd --user otherwise gives a minimal PATH.
|
|
57979
|
-
`Environment=PATH=${process.env["PATH"] ?? ""}:${
|
|
58460
|
+
`Environment=PATH=${process.env["PATH"] ?? ""}:${path17.join(os8.homedir(), ".npm-global/bin")}:${path17.join(os8.homedir(), ".local/bin")}:/usr/local/bin`,
|
|
57980
58461
|
"Restart=on-failure",
|
|
57981
58462
|
"RestartSec=5",
|
|
57982
58463
|
"StandardOutput=journal",
|
|
@@ -57988,23 +58469,23 @@ async function runCli(argv, println = console.log) {
|
|
|
57988
58469
|
].join("\n"));
|
|
57989
58470
|
spawnSync("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
|
|
57990
58471
|
spawnSync("systemctl", ["--user", "enable", "--now", "oasis-node"], { stdio: "inherit" });
|
|
57991
|
-
spawnSync("loginctl", ["enable-linger",
|
|
58472
|
+
spawnSync("loginctl", ["enable-linger", os8.userInfo().username], { stdio: "ignore" });
|
|
57992
58473
|
println("\u2713 Oasis node service started (systemd --user oasis-node)");
|
|
57993
58474
|
println(" Status : systemctl --user status oasis-node");
|
|
57994
58475
|
println(" Logs : journalctl --user -u oasis-node -f");
|
|
57995
58476
|
println(" Stop : systemctl --user stop oasis-node");
|
|
57996
58477
|
} else if (process.platform === "darwin") {
|
|
57997
|
-
const plist =
|
|
57998
|
-
fs19.mkdirSync(
|
|
58478
|
+
const plist = path17.join(os8.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
|
|
58479
|
+
fs19.mkdirSync(path17.dirname(plist), { recursive: true });
|
|
57999
58480
|
spawnSync("launchctl", ["unload", plist], { stdio: "ignore" });
|
|
58000
58481
|
const argXml = [binPath, "node", "--server-ws", serverUrl, "--dir", dir, ...nodeName ? ["--name", nodeName] : []].map((a) => `<string>${a}</string>`).join("");
|
|
58001
|
-
fs19.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>${
|
|
58482
|
+
fs19.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>${os8.homedir()}/.oasis-node.log</string><key>StandardErrorPath</key><string>${os8.homedir()}/.oasis-node.err</string></dict></plist>`);
|
|
58002
58483
|
spawnSync("launchctl", ["load", plist], { stdio: "inherit" });
|
|
58003
58484
|
println("\u2713 Oasis node service started (macOS LaunchAgent com.oasis.node)");
|
|
58004
|
-
println(` Logs : tail -f ${
|
|
58485
|
+
println(` Logs : tail -f ${os8.homedir()}/.oasis-node.log`);
|
|
58005
58486
|
println(` Stop : launchctl unload ${plist}`);
|
|
58006
58487
|
} else {
|
|
58007
|
-
spawnSync("sh", ["-c", `nohup ${binPath} ${svcArgs} > ${
|
|
58488
|
+
spawnSync("sh", ["-c", `nohup ${binPath} ${svcArgs} > ${path17.join(os8.homedir(), ".oasis-node.log")} 2>&1 &`], { stdio: "inherit" });
|
|
58008
58489
|
println(`\u2713 Oasis node started in background (nohup). Logs: ~/.oasis-node.log`);
|
|
58009
58490
|
println(" Note: no automatic restart on reboot in this environment.");
|
|
58010
58491
|
}
|
|
@@ -58020,14 +58501,14 @@ async function runCli(argv, println = console.log) {
|
|
|
58020
58501
|
return;
|
|
58021
58502
|
}
|
|
58022
58503
|
if (command === "node-token") {
|
|
58023
|
-
const store = createNodeTokenStore(
|
|
58504
|
+
const store = createNodeTokenStore(path17.join(dir, "node-tokens.json"));
|
|
58024
58505
|
const sub = positional[0];
|
|
58025
58506
|
if (sub === "issue") {
|
|
58026
|
-
const id = flags.get("id") ?? `node-${(0,
|
|
58507
|
+
const id = flags.get("id") ?? `node-${(0, import_node_crypto26.randomUUID)()}`;
|
|
58027
58508
|
const token2 = store.issue(id);
|
|
58028
58509
|
println(token2);
|
|
58029
58510
|
process.stderr.write(
|
|
58030
|
-
`\u5DF2\u4E3A ${id} \u7B7E\u53D1 node-token\uFF08\u5199\u5165 ${
|
|
58511
|
+
`\u5DF2\u4E3A ${id} \u7B7E\u53D1 node-token\uFF08\u5199\u5165 ${path17.join(dir, "node-tokens.json")}\uFF09\u3002
|
|
58031
58512
|
\u628A\u4E0A\u9762\u8FD9\u4E32\u653E\u5230\u8282\u70B9\u673A\uFF1A--token <t> / $OASIS_NODE_TOKEN / <\u8282\u70B9dir>/node-token
|
|
58032
58513
|
`
|
|
58033
58514
|
);
|
|
@@ -58048,7 +58529,7 @@ async function runCli(argv, println = console.log) {
|
|
|
58048
58529
|
}
|
|
58049
58530
|
if (command === "admin" && positional[0] === "grant-owner") {
|
|
58050
58531
|
const email2 = need(flags, "email");
|
|
58051
|
-
const store = await FileControlPlaneStore.open(
|
|
58532
|
+
const store = await FileControlPlaneStore.open(path17.join(dir, "control-plane.json"));
|
|
58052
58533
|
const activeCompanies = (await store.listCompanies()).filter((c) => c.status === "active");
|
|
58053
58534
|
const companyId = flags.get("company") ?? activeCompanies[0]?.id ?? resolveDefaultCompanyConfig().id;
|
|
58054
58535
|
const name = flags.get("name");
|
|
@@ -58061,7 +58542,7 @@ async function runCli(argv, println = console.log) {
|
|
|
58061
58542
|
}
|
|
58062
58543
|
if (command === "export-trajectories") {
|
|
58063
58544
|
const { exportTrajectories: exportTrajectories2 } = await Promise.resolve().then(() => (init_trajectory(), trajectory_exports));
|
|
58064
|
-
const samples = await exportTrajectories2(
|
|
58545
|
+
const samples = await exportTrajectories2(path17.resolve(dir));
|
|
58065
58546
|
const outFile = flags.get("out");
|
|
58066
58547
|
const lines = samples.map((sample) => JSON.stringify(sample)).join("\n");
|
|
58067
58548
|
if (outFile !== void 0) {
|
|
@@ -58073,10 +58554,10 @@ async function runCli(argv, println = console.log) {
|
|
|
58073
58554
|
return;
|
|
58074
58555
|
}
|
|
58075
58556
|
if (command === "serve" && positional[0] === "install") {
|
|
58076
|
-
const absDir =
|
|
58077
|
-
const name = `oasis-serve-${
|
|
58078
|
-
const unitDir = flags.get("unit-dir") ??
|
|
58079
|
-
const bin = flags.get("bin") ??
|
|
58557
|
+
const absDir = path17.resolve(dir);
|
|
58558
|
+
const name = `oasis-serve-${path17.basename(absDir)}`;
|
|
58559
|
+
const unitDir = flags.get("unit-dir") ?? path17.join(os8.homedir(), ".config", "systemd", "user");
|
|
58560
|
+
const bin = flags.get("bin") ?? path17.join(os8.homedir(), ".local", "bin", "oasis");
|
|
58080
58561
|
const port = flags.get("port") ?? "7320";
|
|
58081
58562
|
const extra = (flags.get("dispatch") === "true" ? ` --dispatch true` : "") + (flags.has("model") ? ` --model ${flags.get("model")}` : "");
|
|
58082
58563
|
const unit = [
|
|
@@ -58094,7 +58575,7 @@ async function runCli(argv, println = console.log) {
|
|
|
58094
58575
|
``
|
|
58095
58576
|
].join("\n");
|
|
58096
58577
|
fs19.mkdirSync(unitDir, { recursive: true });
|
|
58097
|
-
const unitPath =
|
|
58578
|
+
const unitPath = path17.join(unitDir, `${name}.service`);
|
|
58098
58579
|
fs19.writeFileSync(unitPath, unit);
|
|
58099
58580
|
println(`\u5DF2\u5199\u5165 ${unitPath}`);
|
|
58100
58581
|
println(`\u542F\u7528\uFF1Asystemctl --user daemon-reload && systemctl --user enable --now ${name}`);
|
|
@@ -58117,14 +58598,14 @@ async function runCli(argv, println = console.log) {
|
|
|
58117
58598
|
});
|
|
58118
58599
|
const port2 = new URL(handle.baseUrl).port;
|
|
58119
58600
|
println(`oasis serve \u5C31\u7EEA\uFF1Ahttp://0.0.0.0:${port2}\uFF08\u672C\u673A\u8BBF\u95EE\u7528 http://127.0.0.1:${port2}\uFF09`);
|
|
58120
|
-
println(`operator token \u5DF2\u5199\u5165 ${
|
|
58601
|
+
println(`operator token \u5DF2\u5199\u5165 ${path17.join(dir, "operator-token")}\uFF08\u672C\u673A CLI \u81EA\u52A8\u8BFB\u53D6\uFF09`);
|
|
58121
58602
|
if (flags.get("dispatch") === "true") println(`\u8C03\u5EA6\u5FAA\u73AF\u5DF2\u542F\u52A8\uFF08claude-code adapter\uFF09`);
|
|
58122
58603
|
await new Promise(() => {
|
|
58123
58604
|
});
|
|
58124
58605
|
return;
|
|
58125
58606
|
}
|
|
58126
58607
|
const base = flags.get("server") ?? process.env["OASIS_SERVER"] ?? "http://127.0.0.1:7320";
|
|
58127
|
-
const tokenFile =
|
|
58608
|
+
const tokenFile = path17.join(dir, "operator-token");
|
|
58128
58609
|
const token = flags.get("token") ?? process.env["OASIS_TOKEN"] ?? (fs19.existsSync(tokenFile) ? fs19.readFileSync(tokenFile, "utf8").trim() : void 0);
|
|
58129
58610
|
if (!token) {
|
|
58130
58611
|
throw new Error(`\u65E0 token\uFF1A\u5148 oasis serve\uFF08\u4F1A\u751F\u6210 ${tokenFile}\uFF09\uFF0C\u6216\u663E\u5F0F --token / $OASIS_TOKEN`);
|
|
@@ -58149,7 +58630,7 @@ async function runCli(argv, println = console.log) {
|
|
|
58149
58630
|
const out = [];
|
|
58150
58631
|
const walk = (abs, rel) => {
|
|
58151
58632
|
for (const name of fs19.readdirSync(abs)) {
|
|
58152
|
-
const childAbs =
|
|
58633
|
+
const childAbs = path17.join(abs, name);
|
|
58153
58634
|
const childRel = rel ? `${rel}/${name}` : name;
|
|
58154
58635
|
if (fs19.statSync(childAbs).isDirectory()) walk(childAbs, childRel);
|
|
58155
58636
|
else out.push({ path: childRel, contentBase64: fs19.readFileSync(childAbs).toString("base64") });
|
|
@@ -58501,7 +58982,7 @@ async function runCli(argv, println = console.log) {
|
|
|
58501
58982
|
if (file !== void 0) {
|
|
58502
58983
|
const bytes = fs19.readFileSync(file);
|
|
58503
58984
|
const res = await api.request("POST", `/api/projects/${encodeURIComponent(projectId)}/files`, {
|
|
58504
|
-
name: flags.get("name") ??
|
|
58985
|
+
name: flags.get("name") ?? path17.basename(file),
|
|
58505
58986
|
...flags.has("path") ? { path: flags.get("path") } : {},
|
|
58506
58987
|
content_base64: bytes.toString("base64"),
|
|
58507
58988
|
...flags.has("content-type") ? { content_type: flags.get("content-type") } : {}
|
|
@@ -58790,9 +59271,10 @@ ${res.warning}`);
|
|
|
58790
59271
|
}
|
|
58791
59272
|
case "escalate": {
|
|
58792
59273
|
const { message } = await api.cmd("escalate", {
|
|
58793
|
-
artifactId: needPos(positional, 0, "oasis escalate <artifactId> --reason t [--part p]"),
|
|
59274
|
+
artifactId: needPos(positional, 0, "oasis escalate <artifactId> --reason t [--gap id] [--part p]"),
|
|
58794
59275
|
reason: need(flags, "reason"),
|
|
58795
|
-
...flags.get("part") !== void 0 ? { part: flags.get("part") } : {}
|
|
59276
|
+
...flags.get("part") !== void 0 ? { part: flags.get("part") } : {},
|
|
59277
|
+
...flags.get("gap") !== void 0 ? { gapId: flags.get("gap") } : {}
|
|
58796
59278
|
});
|
|
58797
59279
|
println(message);
|
|
58798
59280
|
break;
|
|
@@ -58805,6 +59287,23 @@ ${res.warning}`);
|
|
|
58805
59287
|
println(message);
|
|
58806
59288
|
break;
|
|
58807
59289
|
}
|
|
59290
|
+
case "hold": {
|
|
59291
|
+
const { message } = await api.cmd("hold", {
|
|
59292
|
+
artifactId: needPos(positional, 0, "oasis hold <artifactId> [--reason t]"),
|
|
59293
|
+
...flags.get("reason") !== void 0 ? { reason: flags.get("reason") } : {},
|
|
59294
|
+
...flags.get("source") !== void 0 ? { source: flags.get("source") } : {}
|
|
59295
|
+
});
|
|
59296
|
+
println(message);
|
|
59297
|
+
break;
|
|
59298
|
+
}
|
|
59299
|
+
case "release": {
|
|
59300
|
+
const { message } = await api.cmd("release", {
|
|
59301
|
+
artifactId: needPos(positional, 0, "oasis release <artifactId> [--reason t]"),
|
|
59302
|
+
...flags.get("reason") !== void 0 ? { reason: flags.get("reason") } : {}
|
|
59303
|
+
});
|
|
59304
|
+
println(message);
|
|
59305
|
+
break;
|
|
59306
|
+
}
|
|
58808
59307
|
case "add-revision": {
|
|
58809
59308
|
const { message } = await api.cmd("addRevision", {
|
|
58810
59309
|
artifactId: needPos(positional, 0, "oasis add-revision <artifactId> --desc <\u8981\u5B83\u5B8C\u6210\u4EC0\u4E48> [--role <\u8C01\u6765\u505A>]"),
|
|
@@ -59083,8 +59582,8 @@ ${res.warning}`);
|
|
|
59083
59582
|
}
|
|
59084
59583
|
case "content": {
|
|
59085
59584
|
const id = needPos(positional, 0, "oasis content <artifactId> [path]");
|
|
59086
|
-
const
|
|
59087
|
-
const c = await api.view("content", id,
|
|
59585
|
+
const path19 = positional[1];
|
|
59586
|
+
const c = await api.view("content", id, path19 ? { path: path19 } : void 0);
|
|
59088
59587
|
if (c.kind === "empty") println("\uFF08\u672C\u4EA7\u7269\u5C1A\u65E0\u5185\u5BB9\uFF09");
|
|
59089
59588
|
else if (c.kind === "external-pin") println(`\u4EE3\u7801/\u5916\u90E8\u4EA4\u4ED8\u7269\uFF1A\u5185\u5BB9\u5728 git/\u5916\u90E8 \u2192 ${c.ref}\uFF08\u6309\u300C\u600E\u4E48\u63D0\u4EA4\u300D\u91CC\u7684\u4ED3\u5E93\u6E05\u5355 clone \u53D6\uFF09`);
|
|
59090
59589
|
else if (c.kind === "text") println(c.text);
|
|
@@ -59257,14 +59756,14 @@ ${res.warning}`);
|
|
|
59257
59756
|
}
|
|
59258
59757
|
|
|
59259
59758
|
// src/index.ts
|
|
59260
|
-
var PKG_VERSION = true ? "0.1.
|
|
59261
|
-
var OASIS_DIR =
|
|
59262
|
-
var CONFIG_FILE =
|
|
59263
|
-
var PID_FILE =
|
|
59264
|
-
var BIN_FILE =
|
|
59265
|
-
var LOCAL_BIN =
|
|
59266
|
-
var LOG_FILE =
|
|
59267
|
-
var NPM_PREFIX =
|
|
59759
|
+
var PKG_VERSION = true ? "0.1.54" : "dev";
|
|
59760
|
+
var OASIS_DIR = path18.join(os9.homedir(), ".oasis");
|
|
59761
|
+
var CONFIG_FILE = path18.join(OASIS_DIR, "node-config.json");
|
|
59762
|
+
var PID_FILE = path18.join(OASIS_DIR, "node.pid");
|
|
59763
|
+
var BIN_FILE = path18.join(OASIS_DIR, "bin", "oasis.js");
|
|
59764
|
+
var LOCAL_BIN = path18.join(os9.homedir(), ".local", "bin", "oasis");
|
|
59765
|
+
var LOG_FILE = path18.join(OASIS_DIR, "node.log");
|
|
59766
|
+
var NPM_PREFIX = path18.join(OASIS_DIR, "npm-global");
|
|
59268
59767
|
var PKG_NAME = "oasis_test";
|
|
59269
59768
|
var parseFlags = (argv) => {
|
|
59270
59769
|
const flags = /* @__PURE__ */ new Map();
|
|
@@ -59303,26 +59802,26 @@ var isRunning = (pid) => {
|
|
|
59303
59802
|
};
|
|
59304
59803
|
var httpBase = (wsUrl) => wsUrl.replace(/^ws(s?):\/\//, "http$1://").replace(/\/node-gateway.*$/, "");
|
|
59305
59804
|
function installBinary() {
|
|
59306
|
-
fs20.mkdirSync(
|
|
59805
|
+
fs20.mkdirSync(path18.dirname(BIN_FILE), { recursive: true });
|
|
59307
59806
|
fs20.copyFileSync(process.argv[1], BIN_FILE);
|
|
59308
|
-
fs20.mkdirSync(
|
|
59807
|
+
fs20.mkdirSync(path18.dirname(LOCAL_BIN), { recursive: true });
|
|
59309
59808
|
fs20.writeFileSync(LOCAL_BIN, `#!/bin/sh
|
|
59310
59809
|
exec node "${BIN_FILE}" "$@"
|
|
59311
59810
|
`, { mode: 493 });
|
|
59312
59811
|
}
|
|
59313
59812
|
function setupAutostart() {
|
|
59314
59813
|
const cmd = `${process.execPath} "${BIN_FILE}" --_daemon`;
|
|
59315
|
-
const home =
|
|
59814
|
+
const home = os9.homedir();
|
|
59316
59815
|
const extraBins = [
|
|
59317
|
-
|
|
59318
|
-
|
|
59816
|
+
path18.join(home, ".npm-global", "bin"),
|
|
59817
|
+
path18.join(home, ".local", "bin"),
|
|
59319
59818
|
"/usr/local/bin"
|
|
59320
59819
|
];
|
|
59321
59820
|
const daemonPath = [process.env["PATH"] ?? "", ...extraBins].filter(Boolean).join(":");
|
|
59322
59821
|
if (process.platform === "linux") {
|
|
59323
|
-
const unitDir =
|
|
59822
|
+
const unitDir = path18.join(os9.homedir(), ".config", "systemd", "user");
|
|
59324
59823
|
fs20.mkdirSync(unitDir, { recursive: true });
|
|
59325
|
-
fs20.writeFileSync(
|
|
59824
|
+
fs20.writeFileSync(path18.join(unitDir, "oasis-node.service"), [
|
|
59326
59825
|
"[Unit]",
|
|
59327
59826
|
"Description=Oasis Node Daemon",
|
|
59328
59827
|
"After=network.target",
|
|
@@ -59344,8 +59843,8 @@ function setupAutostart() {
|
|
|
59344
59843
|
} catch {
|
|
59345
59844
|
}
|
|
59346
59845
|
} else if (process.platform === "darwin") {
|
|
59347
|
-
const plist =
|
|
59348
|
-
fs20.mkdirSync(
|
|
59846
|
+
const plist = path18.join(os9.homedir(), "Library", "LaunchAgents", "com.oasis.node.plist");
|
|
59847
|
+
fs20.mkdirSync(path18.dirname(plist), { recursive: true });
|
|
59349
59848
|
fs20.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?>
|
|
59350
59849
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
59351
59850
|
<plist version="1.0"><dict>
|
|
@@ -59398,7 +59897,7 @@ function selfUpdate() {
|
|
|
59398
59897
|
const cfg = readConfig();
|
|
59399
59898
|
if (!cfg?.serverUrl) throw new Error("no config \u2014 run `oasis start` first");
|
|
59400
59899
|
const nameArg = cfg.name ? ` --name '${cfg.name.replace(/'/g, "'\\''")}'` : "";
|
|
59401
|
-
const oasisBin =
|
|
59900
|
+
const oasisBin = path18.join(NPM_PREFIX, "bin", "oasis");
|
|
59402
59901
|
const startCmd = `"${oasisBin}" start --server-ws '${cfg.serverUrl}'${nameArg}`;
|
|
59403
59902
|
const inner = `{ echo "[oasis] self-update \u2192 npm install --prefix ${NPM_PREFIX} ${PKG_NAME}@latest"; sleep 2; npm install -g --prefer-online --prefix '${NPM_PREFIX}' '${PKG_NAME}@latest' && ${startCmd}; } >> '${LOG_FILE}' 2>&1`;
|
|
59404
59903
|
const hasSystemd = process.platform === "linux" && (() => {
|
|
@@ -59498,7 +59997,7 @@ void (async () => {
|
|
|
59498
59997
|
const res = await fetch(`${httpBase(serverUrl)}/api/nodes/exchange`, {
|
|
59499
59998
|
method: "POST",
|
|
59500
59999
|
headers: { "content-type": "application/json" },
|
|
59501
|
-
body: JSON.stringify({ enrollToken, nodeId: derivedNodeId, hostname:
|
|
60000
|
+
body: JSON.stringify({ enrollToken, nodeId: derivedNodeId, hostname: os9.hostname() })
|
|
59502
60001
|
});
|
|
59503
60002
|
if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
|
|
59504
60003
|
const body = await res.json();
|
|
@@ -59521,7 +60020,7 @@ void (async () => {
|
|
|
59521
60020
|
const pid = spawnDaemon();
|
|
59522
60021
|
console.log(`\u2713 daemon started (PID ${pid})${nameChanged ? ` [name updated: ${prev.name} \u2192 ${name}]` : ""}`);
|
|
59523
60022
|
console.log(` logs: tail -f ${LOG_FILE}`);
|
|
59524
|
-
if (!process.env["PATH"]?.includes(
|
|
60023
|
+
if (!process.env["PATH"]?.includes(path18.dirname(LOCAL_BIN)))
|
|
59525
60024
|
console.log(` add to PATH: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc`);
|
|
59526
60025
|
return;
|
|
59527
60026
|
}
|