oasis_test 0.1.14 → 0.1.15
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 +1671 -519
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2806,13 +2806,10 @@ async function assembleContext(args) {
|
|
|
2806
2806
|
for (const [p2, c] of Object.entries(identityFiles)) files[p2] = c;
|
|
2807
2807
|
identityNote = `\`identity/\` \u2014\u2014 \u4F60\u7684\u4EBA\u683C\u4E0E\u884C\u4E3A\u51C6\u5219\uFF08${Object.keys(identityFiles).join(", ")}\uFF09\uFF0C\u5F00\u5DE5\u524D\u52A1\u5FC5\u8BFB`;
|
|
2808
2808
|
}
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
const s2 = byId.get(id);
|
|
2814
|
-
return s2 ? `- **${s2.name}** (\`${s2.id}\`)\uFF1A${s2.description}` : `- \`${id}\`\uFF08\u672A\u5728\u6280\u80FD\u5E93\u4E2D\uFF0C\u53EF\u80FD\u5DF2\u5378\u8F7D\uFF09`;
|
|
2815
|
-
});
|
|
2809
|
+
if (actorCtx.installedSkills.length > 0) {
|
|
2810
|
+
skillsLines = actorCtx.installedSkills.map(
|
|
2811
|
+
(s2) => `- **${s2.name}** (\`${s2.id}\`)\uFF1A${s2.description}`
|
|
2812
|
+
);
|
|
2816
2813
|
}
|
|
2817
2814
|
const activeConnectors = actorCtx.connectors.filter((c) => c.status === "connected" || c.status === "verifying");
|
|
2818
2815
|
if (activeConnectors.length > 0) {
|
|
@@ -3330,7 +3327,8 @@ var init_dispatcher = __esm({
|
|
|
3330
3327
|
limits,
|
|
3331
3328
|
...binding !== void 0 ? { binding } : {},
|
|
3332
3329
|
...provisioned?.env && Object.keys(provisioned.env).length > 0 ? { env: provisioned.env } : {},
|
|
3333
|
-
...provisioned?.wrapperPaths && provisioned.wrapperPaths.length > 0 ? { wrapperPaths: provisioned.wrapperPaths } : {}
|
|
3330
|
+
...provisioned?.wrapperPaths && provisioned.wrapperPaths.length > 0 ? { wrapperPaths: provisioned.wrapperPaths } : {},
|
|
3331
|
+
...provisioned?.requiredTools && provisioned.requiredTools.length > 0 ? { requiredTools: provisioned.requiredTools } : {}
|
|
3334
3332
|
};
|
|
3335
3333
|
const session = await this.opts.adapter.spawn(job);
|
|
3336
3334
|
this.inFlight.set(jobKey, session);
|
|
@@ -17124,49 +17122,49 @@ var require_fast_uri = __commonJS({
|
|
|
17124
17122
|
schemelessOptions.skipEscape = true;
|
|
17125
17123
|
return serialize(resolved, schemelessOptions);
|
|
17126
17124
|
}
|
|
17127
|
-
function resolveComponent(base,
|
|
17125
|
+
function resolveComponent(base, relative2, options, skipNormalization) {
|
|
17128
17126
|
const target = {};
|
|
17129
17127
|
if (!skipNormalization) {
|
|
17130
17128
|
base = parse2(serialize(base, options), options);
|
|
17131
|
-
|
|
17129
|
+
relative2 = parse2(serialize(relative2, options), options);
|
|
17132
17130
|
}
|
|
17133
17131
|
options = options || {};
|
|
17134
|
-
if (!options.tolerant &&
|
|
17135
|
-
target.scheme =
|
|
17136
|
-
target.userinfo =
|
|
17137
|
-
target.host =
|
|
17138
|
-
target.port =
|
|
17139
|
-
target.path = removeDotSegments(
|
|
17140
|
-
target.query =
|
|
17132
|
+
if (!options.tolerant && relative2.scheme) {
|
|
17133
|
+
target.scheme = relative2.scheme;
|
|
17134
|
+
target.userinfo = relative2.userinfo;
|
|
17135
|
+
target.host = relative2.host;
|
|
17136
|
+
target.port = relative2.port;
|
|
17137
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
17138
|
+
target.query = relative2.query;
|
|
17141
17139
|
} else {
|
|
17142
|
-
if (
|
|
17143
|
-
target.userinfo =
|
|
17144
|
-
target.host =
|
|
17145
|
-
target.port =
|
|
17146
|
-
target.path = removeDotSegments(
|
|
17147
|
-
target.query =
|
|
17140
|
+
if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
|
|
17141
|
+
target.userinfo = relative2.userinfo;
|
|
17142
|
+
target.host = relative2.host;
|
|
17143
|
+
target.port = relative2.port;
|
|
17144
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
17145
|
+
target.query = relative2.query;
|
|
17148
17146
|
} else {
|
|
17149
|
-
if (!
|
|
17147
|
+
if (!relative2.path) {
|
|
17150
17148
|
target.path = base.path;
|
|
17151
|
-
if (
|
|
17152
|
-
target.query =
|
|
17149
|
+
if (relative2.query !== void 0) {
|
|
17150
|
+
target.query = relative2.query;
|
|
17153
17151
|
} else {
|
|
17154
17152
|
target.query = base.query;
|
|
17155
17153
|
}
|
|
17156
17154
|
} else {
|
|
17157
|
-
if (
|
|
17158
|
-
target.path = removeDotSegments(
|
|
17155
|
+
if (relative2.path[0] === "/") {
|
|
17156
|
+
target.path = removeDotSegments(relative2.path);
|
|
17159
17157
|
} else {
|
|
17160
17158
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
17161
|
-
target.path = "/" +
|
|
17159
|
+
target.path = "/" + relative2.path;
|
|
17162
17160
|
} else if (!base.path) {
|
|
17163
|
-
target.path =
|
|
17161
|
+
target.path = relative2.path;
|
|
17164
17162
|
} else {
|
|
17165
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
17163
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
|
|
17166
17164
|
}
|
|
17167
17165
|
target.path = removeDotSegments(target.path);
|
|
17168
17166
|
}
|
|
17169
|
-
target.query =
|
|
17167
|
+
target.query = relative2.query;
|
|
17170
17168
|
}
|
|
17171
17169
|
target.userinfo = base.userinfo;
|
|
17172
17170
|
target.host = base.host;
|
|
@@ -17174,7 +17172,7 @@ var require_fast_uri = __commonJS({
|
|
|
17174
17172
|
}
|
|
17175
17173
|
target.scheme = base.scheme;
|
|
17176
17174
|
}
|
|
17177
|
-
target.fragment =
|
|
17175
|
+
target.fragment = relative2.fragment;
|
|
17178
17176
|
return target;
|
|
17179
17177
|
}
|
|
17180
17178
|
function equal(uriA, uriB, options) {
|
|
@@ -21222,12 +21220,83 @@ var init_planner = __esm({
|
|
|
21222
21220
|
}
|
|
21223
21221
|
});
|
|
21224
21222
|
|
|
21223
|
+
// ../server/src/domains/trace/chat-parts.ts
|
|
21224
|
+
function asObj(v2) {
|
|
21225
|
+
return v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
|
|
21226
|
+
}
|
|
21227
|
+
function textOf(v2) {
|
|
21228
|
+
if (typeof v2 === "string") return v2;
|
|
21229
|
+
if (Array.isArray(v2)) return v2.map(textOf).filter(Boolean).join("\n") || void 0;
|
|
21230
|
+
const o = asObj(v2);
|
|
21231
|
+
if (o) {
|
|
21232
|
+
if (typeof o.text === "string") return o.text;
|
|
21233
|
+
if (typeof o.content === "string") return o.content;
|
|
21234
|
+
if (typeof o.thinking === "string") return o.thinking;
|
|
21235
|
+
}
|
|
21236
|
+
return void 0;
|
|
21237
|
+
}
|
|
21238
|
+
function truncate(s2, limit = OUTPUT_PREVIEW_LIMIT) {
|
|
21239
|
+
return s2.length > limit ? `${s2.slice(0, limit)}\u2026` : s2;
|
|
21240
|
+
}
|
|
21241
|
+
function isSkillBootstrapText(text) {
|
|
21242
|
+
return typeof text === "string" && text.trimStart().startsWith("Base directory for this skill:");
|
|
21243
|
+
}
|
|
21244
|
+
function deriveChatPartsFromEvents(events) {
|
|
21245
|
+
const parts = [];
|
|
21246
|
+
for (const ev of [...events].sort((a, b2) => a.seq - b2.seq)) {
|
|
21247
|
+
const p2 = asObj(ev.payload);
|
|
21248
|
+
const kind = p2 && typeof p2.kind === "string" ? p2.kind : void 0;
|
|
21249
|
+
const raw = p2?.raw;
|
|
21250
|
+
if (kind === "thought") {
|
|
21251
|
+
const text = textOf(raw);
|
|
21252
|
+
if (text) parts.push({ type: "thinking", seq: ev.seq, text });
|
|
21253
|
+
continue;
|
|
21254
|
+
}
|
|
21255
|
+
if (kind === "tool_use") {
|
|
21256
|
+
const ro = asObj(raw);
|
|
21257
|
+
parts.push({
|
|
21258
|
+
type: "tool_use",
|
|
21259
|
+
seq: ev.seq,
|
|
21260
|
+
toolName: ro && typeof ro.name === "string" ? ro.name : "tool",
|
|
21261
|
+
...ro && ro.input !== void 0 ? { input: ro.input } : {},
|
|
21262
|
+
...ro && typeof ro.id === "string" ? { toolCallId: ro.id } : {}
|
|
21263
|
+
});
|
|
21264
|
+
continue;
|
|
21265
|
+
}
|
|
21266
|
+
if (kind === "tool_result") {
|
|
21267
|
+
const ro = asObj(raw);
|
|
21268
|
+
const output = textOf(raw) ?? "";
|
|
21269
|
+
parts.push({
|
|
21270
|
+
type: "tool_result",
|
|
21271
|
+
seq: ev.seq,
|
|
21272
|
+
output: truncate(output),
|
|
21273
|
+
isError: ro?.is_error === true || ro?.isError === true,
|
|
21274
|
+
...ro && typeof ro.tool_use_id === "string" ? { toolCallId: ro.tool_use_id } : {}
|
|
21275
|
+
});
|
|
21276
|
+
continue;
|
|
21277
|
+
}
|
|
21278
|
+
if (kind === "message") {
|
|
21279
|
+
const text = textOf(raw);
|
|
21280
|
+
if (text && !isSkillBootstrapText(text)) parts.push({ type: "text", seq: ev.seq, text });
|
|
21281
|
+
continue;
|
|
21282
|
+
}
|
|
21283
|
+
}
|
|
21284
|
+
return parts;
|
|
21285
|
+
}
|
|
21286
|
+
var OUTPUT_PREVIEW_LIMIT;
|
|
21287
|
+
var init_chat_parts = __esm({
|
|
21288
|
+
"../server/src/domains/trace/chat-parts.ts"() {
|
|
21289
|
+
"use strict";
|
|
21290
|
+
OUTPUT_PREVIEW_LIMIT = 4e3;
|
|
21291
|
+
}
|
|
21292
|
+
});
|
|
21293
|
+
|
|
21225
21294
|
// ../server/src/server.ts
|
|
21226
21295
|
function wantsChatParts(req) {
|
|
21227
21296
|
const accept = req.headers.accept;
|
|
21228
21297
|
return typeof accept === "string" && accept.includes("application/x-ndjson");
|
|
21229
21298
|
}
|
|
21230
|
-
function
|
|
21299
|
+
function asObj2(value) {
|
|
21231
21300
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
21232
21301
|
}
|
|
21233
21302
|
function asJsonValue(value) {
|
|
@@ -21240,9 +21309,9 @@ function asJsonValue(value) {
|
|
|
21240
21309
|
}
|
|
21241
21310
|
return null;
|
|
21242
21311
|
}
|
|
21243
|
-
function
|
|
21312
|
+
function textOf2(value) {
|
|
21244
21313
|
if (typeof value === "string") return value;
|
|
21245
|
-
if (Array.isArray(value)) return value.map(
|
|
21314
|
+
if (Array.isArray(value)) return value.map(textOf2).filter(Boolean).join("\n");
|
|
21246
21315
|
if (value && typeof value === "object") {
|
|
21247
21316
|
const obj = value;
|
|
21248
21317
|
if (typeof obj.text === "string") return obj.text;
|
|
@@ -21255,13 +21324,14 @@ function truncateOutput(text) {
|
|
|
21255
21324
|
...[truncated]` : text;
|
|
21256
21325
|
}
|
|
21257
21326
|
function chatPartFromTrajectoryEvent(event) {
|
|
21258
|
-
const payload =
|
|
21327
|
+
const payload = asObj2(event.payload);
|
|
21259
21328
|
if (event.kind === "message") {
|
|
21260
|
-
const text =
|
|
21261
|
-
|
|
21329
|
+
const text = textOf2(payload?.text ?? payload?.content ?? event.payload);
|
|
21330
|
+
if (!text || isSkillBootstrapText(text)) return null;
|
|
21331
|
+
return { type: "text", text };
|
|
21262
21332
|
}
|
|
21263
21333
|
if (event.kind === "thought") {
|
|
21264
|
-
const text =
|
|
21334
|
+
const text = textOf2(payload?.text ?? payload?.thinking ?? event.payload);
|
|
21265
21335
|
return text ? { type: "thinking", text } : null;
|
|
21266
21336
|
}
|
|
21267
21337
|
if (event.kind === "tool_use") {
|
|
@@ -21273,7 +21343,7 @@ function chatPartFromTrajectoryEvent(event) {
|
|
|
21273
21343
|
};
|
|
21274
21344
|
}
|
|
21275
21345
|
if (event.kind === "tool_result") {
|
|
21276
|
-
const output =
|
|
21346
|
+
const output = textOf2(payload?.content ?? payload?.output ?? event.payload) ?? "";
|
|
21277
21347
|
return {
|
|
21278
21348
|
type: "tool_result",
|
|
21279
21349
|
toolCallId: typeof payload?.tool_use_id === "string" ? payload.tool_use_id : typeof payload?.id === "string" ? payload.id : void 0,
|
|
@@ -21292,9 +21362,9 @@ function chatPartsFromClaudeStreamLine(line, state) {
|
|
|
21292
21362
|
return line ? [{ type: "text", text: line }] : [];
|
|
21293
21363
|
}
|
|
21294
21364
|
if (parsed.type === "stream_event") {
|
|
21295
|
-
const event =
|
|
21365
|
+
const event = asObj2(parsed.event);
|
|
21296
21366
|
if (event?.type === "content_block_delta") {
|
|
21297
|
-
const delta =
|
|
21367
|
+
const delta = asObj2(event.delta);
|
|
21298
21368
|
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
21299
21369
|
state.sawTextDelta = true;
|
|
21300
21370
|
return [{ type: "text", text: delta.text }];
|
|
@@ -21304,7 +21374,7 @@ function chatPartsFromClaudeStreamLine(line, state) {
|
|
|
21304
21374
|
}
|
|
21305
21375
|
}
|
|
21306
21376
|
if (event?.type === "content_block_start") {
|
|
21307
|
-
const block =
|
|
21377
|
+
const block = asObj2(event.content_block);
|
|
21308
21378
|
if (block?.type === "tool_use") {
|
|
21309
21379
|
return [{
|
|
21310
21380
|
type: "tool_use",
|
|
@@ -21317,12 +21387,12 @@ function chatPartsFromClaudeStreamLine(line, state) {
|
|
|
21317
21387
|
return [];
|
|
21318
21388
|
}
|
|
21319
21389
|
if (parsed.type === "assistant" || parsed.type === "user") {
|
|
21320
|
-
const message =
|
|
21390
|
+
const message = asObj2(parsed.message);
|
|
21321
21391
|
const content = Array.isArray(message?.content) ? message.content : [];
|
|
21322
21392
|
const parts = [];
|
|
21323
21393
|
for (const block of content) {
|
|
21324
21394
|
if (block.type === "text" && !state.sawTextDelta && typeof block.text === "string") {
|
|
21325
|
-
parts.push({ type: "text", text: block.text });
|
|
21395
|
+
if (!isSkillBootstrapText(block.text)) parts.push({ type: "text", text: block.text });
|
|
21326
21396
|
} else if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
21327
21397
|
parts.push({ type: "thinking", text: block.thinking });
|
|
21328
21398
|
} else if (block.type === "tool_use") {
|
|
@@ -21336,7 +21406,7 @@ function chatPartsFromClaudeStreamLine(line, state) {
|
|
|
21336
21406
|
parts.push({
|
|
21337
21407
|
type: "tool_result",
|
|
21338
21408
|
toolCallId: typeof block.tool_use_id === "string" ? block.tool_use_id : void 0,
|
|
21339
|
-
output: truncateOutput(
|
|
21409
|
+
output: truncateOutput(textOf2(block.content) ?? ""),
|
|
21340
21410
|
isError: block.is_error === true
|
|
21341
21411
|
});
|
|
21342
21412
|
}
|
|
@@ -22589,15 +22659,55 @@ async function startOasisServer(opts) {
|
|
|
22589
22659
|
...body.chatSessionId ? { chatSessionId: body.chatSessionId } : {},
|
|
22590
22660
|
...attachments.length ? { attachments } : {}
|
|
22591
22661
|
});
|
|
22662
|
+
const chatStore = opts.chatSession;
|
|
22663
|
+
const persistSession = chatStore && body.chatSessionId ? await chatStore.getSession(body.chatSessionId).catch(() => null) : null;
|
|
22664
|
+
const persistTarget = persistSession && persistSession.humanActorId === actor ? persistSession : null;
|
|
22665
|
+
if (chatStore && persistTarget) {
|
|
22666
|
+
const { randomUUID: randomUUID15 } = await import("node:crypto");
|
|
22667
|
+
await chatStore.appendMessage({
|
|
22668
|
+
id: randomUUID15(),
|
|
22669
|
+
sessionId: persistTarget.id,
|
|
22670
|
+
role: "user",
|
|
22671
|
+
content: body.message,
|
|
22672
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22673
|
+
}).catch(() => void 0);
|
|
22674
|
+
}
|
|
22675
|
+
let assistantText = "";
|
|
22676
|
+
let persisted = false;
|
|
22677
|
+
const streamedParts = [];
|
|
22678
|
+
const finalizeAssistant = async () => {
|
|
22679
|
+
if (persisted || !chatStore || !persistTarget) return;
|
|
22680
|
+
persisted = true;
|
|
22681
|
+
if (!assistantText && !session.runId && streamedParts.length === 0) return;
|
|
22682
|
+
const { randomUUID: randomUUID15 } = await import("node:crypto");
|
|
22683
|
+
await chatStore.appendMessage({
|
|
22684
|
+
id: randomUUID15(),
|
|
22685
|
+
sessionId: persistTarget.id,
|
|
22686
|
+
role: "assistant",
|
|
22687
|
+
content: assistantText,
|
|
22688
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22689
|
+
...session.runId ? { runId: session.runId } : {},
|
|
22690
|
+
...streamedParts.length ? { parts: streamedParts } : {}
|
|
22691
|
+
}).catch(() => void 0);
|
|
22692
|
+
await chatStore.updateSession(persistTarget.id, {
|
|
22693
|
+
runtimeSessionId: session.id,
|
|
22694
|
+
touchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22695
|
+
}).catch(() => void 0);
|
|
22696
|
+
};
|
|
22592
22697
|
let finished = false;
|
|
22593
22698
|
res.on("close", () => {
|
|
22594
|
-
if (!finished)
|
|
22699
|
+
if (!finished) {
|
|
22700
|
+
void session.kill?.();
|
|
22701
|
+
void finalizeAssistant();
|
|
22702
|
+
}
|
|
22595
22703
|
});
|
|
22596
22704
|
if (typedParts) {
|
|
22597
22705
|
let seq = 0;
|
|
22598
22706
|
const writePart = (part) => {
|
|
22707
|
+
const withSeq = { ...part, seq: ++seq };
|
|
22708
|
+
if (part.type !== "done") streamedParts.push(withSeq);
|
|
22599
22709
|
if (res.destroyed) return;
|
|
22600
|
-
res.write(`${JSON.stringify(
|
|
22710
|
+
res.write(`${JSON.stringify(withSeq)}
|
|
22601
22711
|
`);
|
|
22602
22712
|
};
|
|
22603
22713
|
res.writeHead(200, {
|
|
@@ -22610,6 +22720,7 @@ async function startOasisServer(opts) {
|
|
|
22610
22720
|
let sawTextOutput = false;
|
|
22611
22721
|
session.onOutput((chunk) => {
|
|
22612
22722
|
sawTextOutput = true;
|
|
22723
|
+
assistantText += chunk;
|
|
22613
22724
|
writePart({ type: "text", text: chunk });
|
|
22614
22725
|
});
|
|
22615
22726
|
session.onTelemetry?.((event) => {
|
|
@@ -22623,6 +22734,7 @@ async function startOasisServer(opts) {
|
|
|
22623
22734
|
writePart({ type: "error", text: err instanceof Error ? err.message : String(err) });
|
|
22624
22735
|
}
|
|
22625
22736
|
finished = true;
|
|
22737
|
+
await finalizeAssistant();
|
|
22626
22738
|
writePart({ type: "done", sessionId: session.id });
|
|
22627
22739
|
res.end();
|
|
22628
22740
|
return;
|
|
@@ -22633,7 +22745,10 @@ async function startOasisServer(opts) {
|
|
|
22633
22745
|
"access-control-expose-headers": "X-Session-Id",
|
|
22634
22746
|
"x-session-id": session.id
|
|
22635
22747
|
});
|
|
22636
|
-
session.onOutput((chunk) =>
|
|
22748
|
+
session.onOutput((chunk) => {
|
|
22749
|
+
assistantText += chunk;
|
|
22750
|
+
res.write(chunk);
|
|
22751
|
+
});
|
|
22637
22752
|
try {
|
|
22638
22753
|
await session.done;
|
|
22639
22754
|
} catch (err) {
|
|
@@ -22643,6 +22758,7 @@ async function startOasisServer(opts) {
|
|
|
22643
22758
|
`);
|
|
22644
22759
|
}
|
|
22645
22760
|
finished = true;
|
|
22761
|
+
await finalizeAssistant();
|
|
22646
22762
|
res.end();
|
|
22647
22763
|
return;
|
|
22648
22764
|
}
|
|
@@ -22654,13 +22770,13 @@ async function startOasisServer(opts) {
|
|
|
22654
22770
|
if (body.actorId && opts.resolveActorContext) {
|
|
22655
22771
|
const actorCtx = await opts.resolveActorContext(body.actorId).catch(() => null);
|
|
22656
22772
|
if (actorCtx) {
|
|
22657
|
-
const { mkdtempSync:
|
|
22658
|
-
const { join:
|
|
22659
|
-
const { tmpdir:
|
|
22660
|
-
const dir =
|
|
22773
|
+
const { mkdtempSync: mkdtempSync7, mkdirSync: mkdirSync13, writeFileSync: writeFileSync14 } = await import("node:fs");
|
|
22774
|
+
const { join: join18, dirname: dirname13 } = await import("node:path");
|
|
22775
|
+
const { tmpdir: tmpdir9 } = await import("node:os");
|
|
22776
|
+
const dir = mkdtempSync7(join18(tmpdir9(), "oasis-chat-"));
|
|
22661
22777
|
if (actorCtx.config?.prompt) {
|
|
22662
22778
|
for (const [rel, content] of Object.entries(splitIdentityFiles2(actorCtx.config.prompt))) {
|
|
22663
|
-
const file =
|
|
22779
|
+
const file = join18(dir, rel);
|
|
22664
22780
|
mkdirSync13(dirname13(file), { recursive: true });
|
|
22665
22781
|
writeFileSync14(file, content);
|
|
22666
22782
|
}
|
|
@@ -22672,12 +22788,12 @@ async function startOasisServer(opts) {
|
|
|
22672
22788
|
const s2 = byId.get(id);
|
|
22673
22789
|
return s2 ? `- **${s2.name}** (\`${s2.id}\`): ${s2.description}` : `- \`${id}\`\uFF08\u672A\u5728\u6280\u80FD\u5E93\u4E2D\uFF0C\u53EF\u80FD\u5DF2\u5378\u8F7D\uFF09`;
|
|
22674
22790
|
});
|
|
22675
|
-
writeFileSync14(
|
|
22791
|
+
writeFileSync14(join18(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"));
|
|
22676
22792
|
}
|
|
22677
22793
|
if (opts.materializeSkills) {
|
|
22678
22794
|
const skillFiles = await opts.materializeSkills(body.actorId, "claude").catch(() => ({}));
|
|
22679
22795
|
for (const [rel, content] of Object.entries(skillFiles)) {
|
|
22680
|
-
const file =
|
|
22796
|
+
const file = join18(dir, rel);
|
|
22681
22797
|
mkdirSync13(dirname13(file), { recursive: true });
|
|
22682
22798
|
writeFileSync14(file, content);
|
|
22683
22799
|
}
|
|
@@ -22691,7 +22807,7 @@ async function startOasisServer(opts) {
|
|
|
22691
22807
|
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";
|
|
22692
22808
|
return `- **${c.name}** (\`${c.id}\`): ${statusNote} \xB7 ${modeNote}`;
|
|
22693
22809
|
});
|
|
22694
|
-
writeFileSync14(
|
|
22810
|
+
writeFileSync14(join18(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"));
|
|
22695
22811
|
}
|
|
22696
22812
|
spawnCwd = dir;
|
|
22697
22813
|
}
|
|
@@ -22808,9 +22924,9 @@ async function startOasisServer(opts) {
|
|
|
22808
22924
|
return;
|
|
22809
22925
|
}
|
|
22810
22926
|
try {
|
|
22811
|
-
const { execFile:
|
|
22812
|
-
const { promisify:
|
|
22813
|
-
const { stdout } = await
|
|
22927
|
+
const { execFile: execFile4 } = await import("node:child_process");
|
|
22928
|
+
const { promisify: promisify4 } = await import("node:util");
|
|
22929
|
+
const { stdout } = await promisify4(execFile4)(larkBin, ["config", "show", "--json"]);
|
|
22814
22930
|
const cfg = JSON.parse(stdout);
|
|
22815
22931
|
send(cfg.appId ? { type: "done", appId: cfg.appId } : { type: "error", message: "could not determine appId" });
|
|
22816
22932
|
} catch (e) {
|
|
@@ -22833,9 +22949,9 @@ async function startOasisServer(opts) {
|
|
|
22833
22949
|
}
|
|
22834
22950
|
const larkBin = process.env["LARK_CLI_BIN"] ?? "lark-cli";
|
|
22835
22951
|
try {
|
|
22836
|
-
const { execFile:
|
|
22837
|
-
const { promisify:
|
|
22838
|
-
const { stdout } = await
|
|
22952
|
+
const { execFile: execFile4 } = await import("node:child_process");
|
|
22953
|
+
const { promisify: promisify4 } = await import("node:util");
|
|
22954
|
+
const { stdout } = await promisify4(execFile4)(larkBin, ["--profile", appId, "auth", "login", "--no-wait", "--json", "--recommend"]);
|
|
22839
22955
|
const data = JSON.parse(stdout.trim());
|
|
22840
22956
|
if (!data.verification_url) throw new Error("no verification_url in response");
|
|
22841
22957
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ verificationUrl: data.verification_url, deviceCode: data.device_code }));
|
|
@@ -22852,14 +22968,14 @@ async function startOasisServer(opts) {
|
|
|
22852
22968
|
}
|
|
22853
22969
|
const larkBin = process.env["LARK_CLI_BIN"] ?? "lark-cli";
|
|
22854
22970
|
try {
|
|
22855
|
-
const { execFile:
|
|
22856
|
-
const { promisify:
|
|
22857
|
-
const { stdout } = await
|
|
22971
|
+
const { execFile: execFile4 } = await import("node:child_process");
|
|
22972
|
+
const { promisify: promisify4 } = await import("node:util");
|
|
22973
|
+
const { stdout } = await promisify4(execFile4)(larkBin, ["--profile", appId, "auth", "login", "--device-code", deviceCode, "--json"]);
|
|
22858
22974
|
const data = JSON.parse(stdout.trim());
|
|
22859
22975
|
if (data.ok === false) throw new Error(data.error?.message ?? "login failed");
|
|
22860
22976
|
let appSecret;
|
|
22861
22977
|
try {
|
|
22862
|
-
const { stdout: cfgOut } = await
|
|
22978
|
+
const { stdout: cfgOut } = await promisify4(execFile4)(larkBin, ["--profile", appId, "config", "show", "--json"]);
|
|
22863
22979
|
const cfg = JSON.parse(cfgOut.trim());
|
|
22864
22980
|
appSecret = cfg.appSecret ?? cfg.app_secret;
|
|
22865
22981
|
} catch {
|
|
@@ -23005,6 +23121,7 @@ var init_server3 = __esm({
|
|
|
23005
23121
|
init_draft_edit();
|
|
23006
23122
|
init_planner();
|
|
23007
23123
|
init_identity();
|
|
23124
|
+
init_chat_parts();
|
|
23008
23125
|
CoordinatorRequiredError = class extends Error {
|
|
23009
23126
|
constructor(requirement) {
|
|
23010
23127
|
super(requirement.message ?? "\u7CFB\u7EDF\u7F3A\u5C11\u534F\u8C03\u8005\u667A\u80FD\u4F53\u3002");
|
|
@@ -25575,7 +25692,7 @@ var init_service = __esm({
|
|
|
25575
25692
|
});
|
|
25576
25693
|
|
|
25577
25694
|
// ../server/src/dev-store.ts
|
|
25578
|
-
var import_node_crypto4, fs3, path, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRegistryStore, FileProjectStateStore, FileProjectDocumentStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore;
|
|
25695
|
+
var import_node_crypto4, fs3, path, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRegistryStore, FileProjectStateStore, FileProjectDocumentStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore;
|
|
25579
25696
|
var init_dev_store = __esm({
|
|
25580
25697
|
"../server/src/dev-store.ts"() {
|
|
25581
25698
|
"use strict";
|
|
@@ -26016,8 +26133,11 @@ var init_dev_store = __esm({
|
|
|
26016
26133
|
}
|
|
26017
26134
|
async appendMessage(m2) {
|
|
26018
26135
|
const list = this.messages.get(m2.sessionId) ?? [];
|
|
26019
|
-
list.
|
|
26136
|
+
const seq = (list.length ? Math.max(...list.map((x2) => x2.seq)) : 0) + 1;
|
|
26137
|
+
const record4 = { ...m2, seq };
|
|
26138
|
+
list.push(record4);
|
|
26020
26139
|
this.messages.set(m2.sessionId, list);
|
|
26140
|
+
return record4;
|
|
26021
26141
|
}
|
|
26022
26142
|
async listMessages(sessionId, limit) {
|
|
26023
26143
|
const list = (this.messages.get(sessionId) ?? []).sort((a, b2) => a.seq - b2.seq);
|
|
@@ -26032,6 +26152,54 @@ var init_dev_store = __esm({
|
|
|
26032
26152
|
return [...this.sessionWorkOrders.get(sessionId) ?? []];
|
|
26033
26153
|
}
|
|
26034
26154
|
};
|
|
26155
|
+
FileChatSessionStore = class _FileChatSessionStore extends MemoryChatSessionStore {
|
|
26156
|
+
constructor(file) {
|
|
26157
|
+
super();
|
|
26158
|
+
this.file = file;
|
|
26159
|
+
}
|
|
26160
|
+
static async open(file) {
|
|
26161
|
+
const store = new _FileChatSessionStore(file);
|
|
26162
|
+
if (fs3.existsSync(file)) {
|
|
26163
|
+
const snap = JSON.parse(fs3.readFileSync(file, "utf8"));
|
|
26164
|
+
for (const s2 of snap.sessions ?? []) store.sessions.set(s2.id, s2);
|
|
26165
|
+
for (const [sid, list] of Object.entries(snap.messages ?? {})) store.messages.set(sid, list);
|
|
26166
|
+
for (const [sid, ids] of Object.entries(snap.sessionWorkOrders ?? {})) store.sessionWorkOrders.set(sid, new Set(ids));
|
|
26167
|
+
} else {
|
|
26168
|
+
fs3.mkdirSync(path.dirname(file), { recursive: true });
|
|
26169
|
+
fs3.writeFileSync(file, JSON.stringify({ sessions: [], messages: {}, sessionWorkOrders: {} }, null, 2));
|
|
26170
|
+
}
|
|
26171
|
+
return store;
|
|
26172
|
+
}
|
|
26173
|
+
async createSession(s2) {
|
|
26174
|
+
await super.createSession(s2);
|
|
26175
|
+
this.save();
|
|
26176
|
+
}
|
|
26177
|
+
async updateSession(id, patch) {
|
|
26178
|
+
await super.updateSession(id, patch);
|
|
26179
|
+
this.save();
|
|
26180
|
+
}
|
|
26181
|
+
async deleteSession(id) {
|
|
26182
|
+
await super.deleteSession(id);
|
|
26183
|
+
this.save();
|
|
26184
|
+
}
|
|
26185
|
+
async appendMessage(m2) {
|
|
26186
|
+
const record4 = await super.appendMessage(m2);
|
|
26187
|
+
this.save();
|
|
26188
|
+
return record4;
|
|
26189
|
+
}
|
|
26190
|
+
async linkWorkOrder(sessionId, workOrderId) {
|
|
26191
|
+
await super.linkWorkOrder(sessionId, workOrderId);
|
|
26192
|
+
this.save();
|
|
26193
|
+
}
|
|
26194
|
+
save() {
|
|
26195
|
+
const snap = {
|
|
26196
|
+
sessions: [...this.sessions.values()],
|
|
26197
|
+
messages: Object.fromEntries(this.messages),
|
|
26198
|
+
sessionWorkOrders: Object.fromEntries([...this.sessionWorkOrders].map(([k2, v2]) => [k2, [...v2]]))
|
|
26199
|
+
};
|
|
26200
|
+
fs3.writeFileSync(this.file, JSON.stringify(snap, null, 2));
|
|
26201
|
+
}
|
|
26202
|
+
};
|
|
26035
26203
|
}
|
|
26036
26204
|
});
|
|
26037
26205
|
|
|
@@ -28652,7 +28820,7 @@ var require_websocket = __commonJS({
|
|
|
28652
28820
|
var http2 = require("http");
|
|
28653
28821
|
var net = require("net");
|
|
28654
28822
|
var tls = require("tls");
|
|
28655
|
-
var { randomBytes: randomBytes5, createHash:
|
|
28823
|
+
var { randomBytes: randomBytes5, createHash: createHash8 } = require("crypto");
|
|
28656
28824
|
var { Duplex, Readable } = require("stream");
|
|
28657
28825
|
var { URL: URL2 } = require("url");
|
|
28658
28826
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -29320,7 +29488,7 @@ var require_websocket = __commonJS({
|
|
|
29320
29488
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
29321
29489
|
return;
|
|
29322
29490
|
}
|
|
29323
|
-
const digest =
|
|
29491
|
+
const digest = createHash8("sha1").update(key + GUID).digest("base64");
|
|
29324
29492
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
29325
29493
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
29326
29494
|
return;
|
|
@@ -29689,7 +29857,7 @@ var require_websocket_server = __commonJS({
|
|
|
29689
29857
|
var EventEmitter = require("events");
|
|
29690
29858
|
var http2 = require("http");
|
|
29691
29859
|
var { Duplex } = require("stream");
|
|
29692
|
-
var { createHash:
|
|
29860
|
+
var { createHash: createHash8 } = require("crypto");
|
|
29693
29861
|
var extension2 = require_extension();
|
|
29694
29862
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
29695
29863
|
var subprotocol2 = require_subprotocol();
|
|
@@ -29996,7 +30164,7 @@ var require_websocket_server = __commonJS({
|
|
|
29996
30164
|
);
|
|
29997
30165
|
}
|
|
29998
30166
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
29999
|
-
const digest =
|
|
30167
|
+
const digest = createHash8("sha1").update(key + GUID).digest("base64");
|
|
30000
30168
|
const headers = [
|
|
30001
30169
|
"HTTP/1.1 101 Switching Protocols",
|
|
30002
30170
|
"Upgrade: websocket",
|
|
@@ -30748,6 +30916,17 @@ ${input.description}
|
|
|
30748
30916
|
await this.opts.store.putInstalledSkill(updated);
|
|
30749
30917
|
return updated;
|
|
30750
30918
|
}
|
|
30919
|
+
/**
|
|
30920
|
+
* 设置组织技能的「全员生效」标记:true = 挂给本组织所有 agent(无需各 agent 启用);
|
|
30921
|
+
* false = 回落到「技能库」,由各 agent 在 config.skills 里按需挑选。
|
|
30922
|
+
*/
|
|
30923
|
+
async setSkillAppliedToAll(skillId, appliedToAll) {
|
|
30924
|
+
const cur = (await this.opts.store.listInstalledSkills()).find((x2) => x2.id === skillId);
|
|
30925
|
+
if (!cur) throw new Error(`\u672A\u5B89\u88C5\u8BE5\u6280\u80FD\uFF1A${skillId}`);
|
|
30926
|
+
const updated = { ...cur, appliedToAll };
|
|
30927
|
+
await this.opts.store.putInstalledSkill(updated);
|
|
30928
|
+
return updated;
|
|
30929
|
+
}
|
|
30751
30930
|
/**
|
|
30752
30931
|
* 已安装技能 + 分配派生:员工←→技能的挂载事实在 actor_configs.skills 版本链里
|
|
30753
30932
|
* (决策:不另建 assignment 表),这里按各 AI 员工最新配置聚合出"谁挂了它"。
|
|
@@ -30779,9 +30958,10 @@ ${input.description}
|
|
|
30779
30958
|
return this.opts.store.listConnectors();
|
|
30780
30959
|
}
|
|
30781
30960
|
/**
|
|
30782
|
-
* 为指定 AI
|
|
30783
|
-
*
|
|
30784
|
-
*
|
|
30961
|
+
* 为指定 AI 员工筛出**生效**的组织技能 = 该员工 config.skills 显式启用集 ∪ 全组织 appliedToAll 技能,
|
|
30962
|
+
* 与公司已安装技能库做交叉、去重,返回带元数据的完整 InstalledSkill 记录。
|
|
30963
|
+
* (内置技能不在 DB、不走这里,由 filesystem 加载后无条件挂给所有 agent。)
|
|
30964
|
+
* 供派单装配器在每次会话启动时注入技能正文与 TASK.md 索引。
|
|
30785
30965
|
*/
|
|
30786
30966
|
async listInstalledSkillsForActor(actorId) {
|
|
30787
30967
|
const [config2, all] = await Promise.all([
|
|
@@ -30789,8 +30969,9 @@ ${input.description}
|
|
|
30789
30969
|
this.opts.store.listInstalledSkills()
|
|
30790
30970
|
]);
|
|
30791
30971
|
const enabledKeys = new Set(config2?.skills ?? []);
|
|
30792
|
-
|
|
30793
|
-
|
|
30972
|
+
return all.filter(
|
|
30973
|
+
(s2) => s2.appliedToAll === true || enabledKeys.has(s2.id) || enabledKeys.has(s2.name)
|
|
30974
|
+
);
|
|
30794
30975
|
}
|
|
30795
30976
|
/** 全局已装技能平铺列表(「默认挂」materialize 用;installedSkillsWithAssignments 带分配关系太重)。 */
|
|
30796
30977
|
async listInstalledSkills() {
|
|
@@ -31235,6 +31416,16 @@ function actorsDomain(opts) {
|
|
|
31235
31416
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
31236
31417
|
return { status: 200, body: { items: await service.installedSkillsWithAssignments() } };
|
|
31237
31418
|
});
|
|
31419
|
+
router.get("/api/skills/builtin", async () => {
|
|
31420
|
+
const items = (opts.listBuiltinSkills?.() ?? []).map((s2) => ({
|
|
31421
|
+
...s2,
|
|
31422
|
+
source: "builtin",
|
|
31423
|
+
enabled: true,
|
|
31424
|
+
appliedToAll: true,
|
|
31425
|
+
builtin: true
|
|
31426
|
+
}));
|
|
31427
|
+
return { status: 200, body: { items } };
|
|
31428
|
+
});
|
|
31238
31429
|
router.post("/api/skills/install", async (req) => {
|
|
31239
31430
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
31240
31431
|
const b2 = req.body;
|
|
@@ -31259,6 +31450,10 @@ function actorsDomain(opts) {
|
|
|
31259
31450
|
router.patch("/api/skills/:id", async (req) => {
|
|
31260
31451
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
31261
31452
|
const b2 = req.body;
|
|
31453
|
+
if (typeof b2?.appliedToAll === "boolean") {
|
|
31454
|
+
const updated = await service.setSkillAppliedToAll(req.params.id, b2.appliedToAll);
|
|
31455
|
+
return { status: 200, body: updated };
|
|
31456
|
+
}
|
|
31262
31457
|
if (typeof b2?.enabled === "boolean") {
|
|
31263
31458
|
await service.setSkillEnabled(req.params.id, b2.enabled);
|
|
31264
31459
|
return { status: 200, body: { ok: true } };
|
|
@@ -31270,7 +31465,7 @@ function actorsDomain(opts) {
|
|
|
31270
31465
|
});
|
|
31271
31466
|
return { status: 200, body: updated };
|
|
31272
31467
|
}
|
|
31273
|
-
throw new ApiError(400, "BAD_REQUEST", "\u4F20 enabled\uFF08\u5F00\u5173\uFF09\
|
|
31468
|
+
throw new ApiError(400, "BAD_REQUEST", "\u4F20 enabled\uFF08\u5F00\u5173\uFF09/ appliedToAll\uFF08\u5168\u5458\u751F\u6548\uFF09/ name\xB7description\uFF08\u66F4\u65B0\u5143\u6570\u636E\uFF09");
|
|
31274
31469
|
});
|
|
31275
31470
|
router.get("/api/actors/:id/stats", async (req) => {
|
|
31276
31471
|
const { oplog } = await resolveCtx(req.auth.companyId);
|
|
@@ -31329,14 +31524,7 @@ function sanitizeSkillDir(name) {
|
|
|
31329
31524
|
return s2 || "skill";
|
|
31330
31525
|
}
|
|
31331
31526
|
async function materializeSkillFiles(args) {
|
|
31332
|
-
const
|
|
31333
|
-
let extras = [];
|
|
31334
|
-
if (args.extraSkillIds && args.extraSkillIds.length > 0) {
|
|
31335
|
-
const ownIds = new Set(own.map((s2) => s2.id));
|
|
31336
|
-
const wanted = new Set(args.extraSkillIds);
|
|
31337
|
-
extras = (await args.service.listInstalledSkills()).filter((s2) => wanted.has(s2.id) && !ownIds.has(s2.id));
|
|
31338
|
-
}
|
|
31339
|
-
const skills = [...own, ...extras];
|
|
31527
|
+
const skills = await args.service.listInstalledSkillsForActor(args.actorId);
|
|
31340
31528
|
if (skills.length === 0) return {};
|
|
31341
31529
|
const prefix = skillsDirForRuntime(args.runtimeKind);
|
|
31342
31530
|
const out = {};
|
|
@@ -31414,7 +31602,7 @@ function createActorsDomain(opts) {
|
|
|
31414
31602
|
cache.set(companyId, ctx);
|
|
31415
31603
|
return ctx;
|
|
31416
31604
|
}
|
|
31417
|
-
return { service: defaultCtx.service, register: actorsDomain({ resolveCtx }) };
|
|
31605
|
+
return { service: defaultCtx.service, register: actorsDomain({ resolveCtx, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {} }) };
|
|
31418
31606
|
}
|
|
31419
31607
|
var import_node_crypto6;
|
|
31420
31608
|
var init_actors = __esm({
|
|
@@ -32206,84 +32394,12 @@ var init_projects = __esm({
|
|
|
32206
32394
|
}
|
|
32207
32395
|
});
|
|
32208
32396
|
|
|
32209
|
-
// ../server/src/domains/companies/migrate.ts
|
|
32210
|
-
async function migrateDefaultCompany(store, actors, opts = {}) {
|
|
32211
|
-
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
32212
|
-
let company = await store.getCompany(DEFAULT_COMPANY_ID);
|
|
32213
|
-
let createdCompany = false;
|
|
32214
|
-
if (!company) {
|
|
32215
|
-
company = {
|
|
32216
|
-
id: DEFAULT_COMPANY_ID,
|
|
32217
|
-
slug: DEFAULT_COMPANY_SLUG,
|
|
32218
|
-
name: DEFAULT_COMPANY_NAME,
|
|
32219
|
-
status: "active",
|
|
32220
|
-
createdAt: now()
|
|
32221
|
-
};
|
|
32222
|
-
await store.createCompany(company);
|
|
32223
|
-
createdCompany = true;
|
|
32224
|
-
}
|
|
32225
|
-
let addedMembers = 0;
|
|
32226
|
-
for (const a of actors) {
|
|
32227
|
-
if (!await store.getMember(DEFAULT_COMPANY_ID, a.id)) {
|
|
32228
|
-
await store.upsertMember({ companyId: DEFAULT_COMPANY_ID, accountId: a.id, actorId: a.id, role: "member" });
|
|
32229
|
-
addedMembers++;
|
|
32230
|
-
}
|
|
32231
|
-
}
|
|
32232
|
-
let designatedOwner;
|
|
32233
|
-
const members = await store.listMembers(DEFAULT_COMPANY_ID);
|
|
32234
|
-
if (members.length > 0 && !members.some((m2) => m2.role === "owner")) {
|
|
32235
|
-
const sorted = [...actors].sort((x2, y) => x2.id.localeCompare(y.id));
|
|
32236
|
-
const pick2 = sorted.find((a) => a.kind === "human") ?? sorted[0];
|
|
32237
|
-
if (pick2) {
|
|
32238
|
-
await store.upsertMember({ companyId: DEFAULT_COMPANY_ID, accountId: pick2.id, actorId: pick2.id, role: "owner" });
|
|
32239
|
-
designatedOwner = pick2.id;
|
|
32240
|
-
}
|
|
32241
|
-
}
|
|
32242
|
-
return {
|
|
32243
|
-
company,
|
|
32244
|
-
createdCompany,
|
|
32245
|
-
addedMembers,
|
|
32246
|
-
...designatedOwner !== void 0 ? { designatedOwner } : {}
|
|
32247
|
-
};
|
|
32248
|
-
}
|
|
32249
|
-
async function ensureCompanyActors(store, companyId, registry2, opts = {}) {
|
|
32250
|
-
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
32251
|
-
const members = await store.listMembers(companyId);
|
|
32252
|
-
let created = 0;
|
|
32253
|
-
for (const m2 of members) {
|
|
32254
|
-
if (await registry2.getActor(m2.actorId)) continue;
|
|
32255
|
-
const account = await store.getAccount(m2.accountId);
|
|
32256
|
-
const rec = {
|
|
32257
|
-
id: m2.actorId,
|
|
32258
|
-
kind: "human",
|
|
32259
|
-
name: account?.name ?? m2.accountId,
|
|
32260
|
-
...account?.email ? { email: account.email } : {},
|
|
32261
|
-
roles: [],
|
|
32262
|
-
status: "active",
|
|
32263
|
-
createdAt: now()
|
|
32264
|
-
};
|
|
32265
|
-
await registry2.upsertActor(rec);
|
|
32266
|
-
created++;
|
|
32267
|
-
}
|
|
32268
|
-
return created;
|
|
32269
|
-
}
|
|
32270
|
-
var DEFAULT_COMPANY_ID, DEFAULT_COMPANY_SLUG, DEFAULT_COMPANY_NAME;
|
|
32271
|
-
var init_migrate = __esm({
|
|
32272
|
-
"../server/src/domains/companies/migrate.ts"() {
|
|
32273
|
-
"use strict";
|
|
32274
|
-
DEFAULT_COMPANY_ID = "company:oasis";
|
|
32275
|
-
DEFAULT_COMPANY_SLUG = "oasis";
|
|
32276
|
-
DEFAULT_COMPANY_NAME = "Oasis \u603B\u90E8";
|
|
32277
|
-
}
|
|
32278
|
-
});
|
|
32279
|
-
|
|
32280
32397
|
// ../server/src/domains/companies/service.ts
|
|
32281
32398
|
var import_node_crypto7, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, SLUG_RE, CompanyError, CompaniesService;
|
|
32282
32399
|
var init_service3 = __esm({
|
|
32283
32400
|
"../server/src/domains/companies/service.ts"() {
|
|
32284
32401
|
"use strict";
|
|
32285
32402
|
import_node_crypto7 = require("node:crypto");
|
|
32286
|
-
init_migrate();
|
|
32287
32403
|
ROLES = ["owner", "admin", "member"];
|
|
32288
32404
|
INVITABLE_ROLES = ["admin", "member"];
|
|
32289
32405
|
INVITATION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -32354,16 +32470,25 @@ var init_service3 = __esm({
|
|
|
32354
32470
|
return membership;
|
|
32355
32471
|
}
|
|
32356
32472
|
/**
|
|
32357
|
-
*
|
|
32358
|
-
*
|
|
32359
|
-
*
|
|
32473
|
+
* 当前公司解析(动态、随登录用户走):
|
|
32474
|
+
* - 带 selector(公司 id 或 slug)→ 按它取公司(CO-301 路由层据此取/懒建对应引擎)。
|
|
32475
|
+
* - 不带 selector → 回退到**该用户自己的**第一个 active 公司(按 membership),而非全局硬编默认公司。
|
|
32476
|
+
* 前端 CompanyProvider 也是这个语义(存的当前公司不在列表时回退列表首个)。
|
|
32477
|
+
* 返回 company + 该用户是否其成员;找不到(selector 不存在,或用户无任何 active 公司)返回 null。
|
|
32360
32478
|
*/
|
|
32361
32479
|
async resolveCurrentCompany(accountId, selector) {
|
|
32362
32480
|
let company;
|
|
32363
32481
|
if (selector) {
|
|
32364
32482
|
company = await this.store.getCompany(selector) ?? await this.store.getCompanyBySlug(selector);
|
|
32365
32483
|
} else {
|
|
32366
|
-
company =
|
|
32484
|
+
company = null;
|
|
32485
|
+
for (const m2 of await this.store.listMembershipsForAccount(accountId)) {
|
|
32486
|
+
const c = await this.store.getCompany(m2.companyId);
|
|
32487
|
+
if (c?.status === "active") {
|
|
32488
|
+
company = c;
|
|
32489
|
+
break;
|
|
32490
|
+
}
|
|
32491
|
+
}
|
|
32367
32492
|
}
|
|
32368
32493
|
if (!company) return null;
|
|
32369
32494
|
const member = await this.store.getMember(company.id, accountId);
|
|
@@ -32606,6 +32731,84 @@ var init_routes3 = __esm({
|
|
|
32606
32731
|
}
|
|
32607
32732
|
});
|
|
32608
32733
|
|
|
32734
|
+
// ../server/src/domains/companies/migrate.ts
|
|
32735
|
+
function resolveDefaultCompanyConfig(env = process.env) {
|
|
32736
|
+
const slug4 = env["OASIS_DEFAULT_COMPANY_SLUG"]?.trim() || "oasis";
|
|
32737
|
+
const name = env["OASIS_DEFAULT_COMPANY_NAME"]?.trim() || "Oasis \u603B\u90E8";
|
|
32738
|
+
return { id: `company:${slug4}`, slug: slug4, name };
|
|
32739
|
+
}
|
|
32740
|
+
async function migrateDefaultCompany(store, actors, opts = {}) {
|
|
32741
|
+
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
32742
|
+
const cfg = resolveDefaultCompanyConfig(opts.env);
|
|
32743
|
+
let company = await store.getCompany(cfg.id);
|
|
32744
|
+
let createdCompany = false;
|
|
32745
|
+
if (!company) {
|
|
32746
|
+
company = {
|
|
32747
|
+
id: cfg.id,
|
|
32748
|
+
slug: cfg.slug,
|
|
32749
|
+
name: cfg.name,
|
|
32750
|
+
status: "active",
|
|
32751
|
+
createdAt: now()
|
|
32752
|
+
};
|
|
32753
|
+
await store.createCompany(company);
|
|
32754
|
+
createdCompany = true;
|
|
32755
|
+
}
|
|
32756
|
+
let addedMembers = 0;
|
|
32757
|
+
for (const a of actors) {
|
|
32758
|
+
if (!await store.getMember(cfg.id, a.id)) {
|
|
32759
|
+
await store.upsertMember({ companyId: cfg.id, accountId: a.id, actorId: a.id, role: "member" });
|
|
32760
|
+
addedMembers++;
|
|
32761
|
+
}
|
|
32762
|
+
}
|
|
32763
|
+
let designatedOwner;
|
|
32764
|
+
const members = await store.listMembers(cfg.id);
|
|
32765
|
+
if (members.length > 0 && !members.some((m2) => m2.role === "owner")) {
|
|
32766
|
+
const sorted = [...actors].sort((x2, y) => x2.id.localeCompare(y.id));
|
|
32767
|
+
const pick2 = sorted.find((a) => a.kind === "human") ?? sorted[0];
|
|
32768
|
+
if (pick2) {
|
|
32769
|
+
await store.upsertMember({ companyId: cfg.id, accountId: pick2.id, actorId: pick2.id, role: "owner" });
|
|
32770
|
+
designatedOwner = pick2.id;
|
|
32771
|
+
}
|
|
32772
|
+
}
|
|
32773
|
+
return {
|
|
32774
|
+
company,
|
|
32775
|
+
createdCompany,
|
|
32776
|
+
addedMembers,
|
|
32777
|
+
...designatedOwner !== void 0 ? { designatedOwner } : {}
|
|
32778
|
+
};
|
|
32779
|
+
}
|
|
32780
|
+
async function ensureCompanyActors(store, companyId, registry2, opts = {}) {
|
|
32781
|
+
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
32782
|
+
const members = await store.listMembers(companyId);
|
|
32783
|
+
let created = 0;
|
|
32784
|
+
for (const m2 of members) {
|
|
32785
|
+
if (await registry2.getActor(m2.actorId)) continue;
|
|
32786
|
+
const account = await store.getAccount(m2.accountId);
|
|
32787
|
+
const rec = {
|
|
32788
|
+
id: m2.actorId,
|
|
32789
|
+
kind: "human",
|
|
32790
|
+
name: account?.name ?? m2.accountId,
|
|
32791
|
+
...account?.email ? { email: account.email } : {},
|
|
32792
|
+
roles: [],
|
|
32793
|
+
status: "active",
|
|
32794
|
+
createdAt: now()
|
|
32795
|
+
};
|
|
32796
|
+
await registry2.upsertActor(rec);
|
|
32797
|
+
created++;
|
|
32798
|
+
}
|
|
32799
|
+
return created;
|
|
32800
|
+
}
|
|
32801
|
+
var _defaultCompany, DEFAULT_COMPANY_ID, DEFAULT_COMPANY_SLUG, DEFAULT_COMPANY_NAME;
|
|
32802
|
+
var init_migrate = __esm({
|
|
32803
|
+
"../server/src/domains/companies/migrate.ts"() {
|
|
32804
|
+
"use strict";
|
|
32805
|
+
_defaultCompany = resolveDefaultCompanyConfig();
|
|
32806
|
+
DEFAULT_COMPANY_ID = _defaultCompany.id;
|
|
32807
|
+
DEFAULT_COMPANY_SLUG = _defaultCompany.slug;
|
|
32808
|
+
DEFAULT_COMPANY_NAME = _defaultCompany.name;
|
|
32809
|
+
}
|
|
32810
|
+
});
|
|
32811
|
+
|
|
32609
32812
|
// ../server/src/domains/companies/index.ts
|
|
32610
32813
|
function createCompaniesDomain(opts) {
|
|
32611
32814
|
const service = new CompaniesService({
|
|
@@ -33282,19 +33485,29 @@ function nodesDomain(deps) {
|
|
|
33282
33485
|
const entry = deps.enrollTokens.peek(enrollToken);
|
|
33283
33486
|
if (!entry) return { status: 401, body: { error: "invalid or expired enroll token" } };
|
|
33284
33487
|
const incomingNodeId = body.nodeId?.trim();
|
|
33285
|
-
const ownsIncoming = !!incomingNodeId && (deps.nodeTokens.list().some((e) => e.nodeId === incomingNodeId) || await deps.nodeStore.getNode(incomingNodeId) !== null);
|
|
33286
33488
|
let nodeId;
|
|
33287
33489
|
if (entry.issuedNodeId) {
|
|
33288
|
-
if (incomingNodeId && incomingNodeId !== entry.issuedNodeId && !ownsIncoming)
|
|
33289
|
-
return { status: 409, body: { error: "nodeId mismatch" } };
|
|
33290
33490
|
nodeId = entry.issuedNodeId;
|
|
33291
|
-
} else if (
|
|
33491
|
+
} else if (incomingNodeId) {
|
|
33292
33492
|
nodeId = incomingNodeId;
|
|
33293
33493
|
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
33294
33494
|
} else {
|
|
33295
33495
|
nodeId = `node-${(0, import_node_crypto9.randomUUID)().slice(0, 8)}`;
|
|
33296
33496
|
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
33297
33497
|
}
|
|
33498
|
+
const existingNode = await deps.nodeStore.getNode(nodeId);
|
|
33499
|
+
if (!existingNode || existingNode.status === "deleted") {
|
|
33500
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
33501
|
+
await deps.nodeStore.upsertNode({
|
|
33502
|
+
id: nodeId,
|
|
33503
|
+
hostname: body.hostname ?? existingNode?.hostname ?? "unknown",
|
|
33504
|
+
nodeVersion: existingNode?.nodeVersion ?? "unknown",
|
|
33505
|
+
status: "online",
|
|
33506
|
+
lastSeenAt: nowIso,
|
|
33507
|
+
enrolledAt: existingNode?.enrolledAt ?? nowIso,
|
|
33508
|
+
...entry.name ? { name: entry.name } : existingNode?.name ? { name: existingNode.name } : {}
|
|
33509
|
+
});
|
|
33510
|
+
}
|
|
33298
33511
|
deps.enrollTokens.consume(enrollToken);
|
|
33299
33512
|
if (entry.name) await deps.nodeStore.updateNodeName(nodeId, entry.name);
|
|
33300
33513
|
const existing = deps.nodeTokens.list().find((e) => e.nodeId === nodeId);
|
|
@@ -35068,69 +35281,6 @@ var init_sink = __esm({
|
|
|
35068
35281
|
}
|
|
35069
35282
|
});
|
|
35070
35283
|
|
|
35071
|
-
// ../server/src/domains/trace/chat-parts.ts
|
|
35072
|
-
function asObj2(v2) {
|
|
35073
|
-
return v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
|
|
35074
|
-
}
|
|
35075
|
-
function textOf2(v2) {
|
|
35076
|
-
if (typeof v2 === "string") return v2;
|
|
35077
|
-
if (Array.isArray(v2)) return v2.map(textOf2).filter(Boolean).join("\n") || void 0;
|
|
35078
|
-
const o = asObj2(v2);
|
|
35079
|
-
if (o) {
|
|
35080
|
-
if (typeof o.text === "string") return o.text;
|
|
35081
|
-
if (typeof o.content === "string") return o.content;
|
|
35082
|
-
if (typeof o.thinking === "string") return o.thinking;
|
|
35083
|
-
}
|
|
35084
|
-
return void 0;
|
|
35085
|
-
}
|
|
35086
|
-
function truncate(s2, limit = OUTPUT_PREVIEW_LIMIT) {
|
|
35087
|
-
return s2.length > limit ? `${s2.slice(0, limit)}\u2026` : s2;
|
|
35088
|
-
}
|
|
35089
|
-
function deriveChatPartsFromEvents(events) {
|
|
35090
|
-
const parts = [];
|
|
35091
|
-
for (const ev of [...events].sort((a, b2) => a.seq - b2.seq)) {
|
|
35092
|
-
const p2 = asObj2(ev.payload);
|
|
35093
|
-
const kind = p2 && typeof p2.kind === "string" ? p2.kind : void 0;
|
|
35094
|
-
const raw = p2?.raw;
|
|
35095
|
-
if (kind === "thought") {
|
|
35096
|
-
const text = textOf2(raw);
|
|
35097
|
-
if (text) parts.push({ type: "thinking", seq: ev.seq, text });
|
|
35098
|
-
continue;
|
|
35099
|
-
}
|
|
35100
|
-
if (kind === "tool_use") {
|
|
35101
|
-
const ro = asObj2(raw);
|
|
35102
|
-
parts.push({
|
|
35103
|
-
type: "tool_use",
|
|
35104
|
-
seq: ev.seq,
|
|
35105
|
-
toolName: ro && typeof ro.name === "string" ? ro.name : "tool",
|
|
35106
|
-
...ro && ro.input !== void 0 ? { input: ro.input } : {},
|
|
35107
|
-
...ro && typeof ro.id === "string" ? { toolCallId: ro.id } : {}
|
|
35108
|
-
});
|
|
35109
|
-
continue;
|
|
35110
|
-
}
|
|
35111
|
-
if (kind === "tool_result") {
|
|
35112
|
-
const ro = asObj2(raw);
|
|
35113
|
-
const output = textOf2(raw) ?? "";
|
|
35114
|
-
parts.push({
|
|
35115
|
-
type: "tool_result",
|
|
35116
|
-
seq: ev.seq,
|
|
35117
|
-
output: truncate(output),
|
|
35118
|
-
isError: ro?.is_error === true || ro?.isError === true,
|
|
35119
|
-
...ro && typeof ro.tool_use_id === "string" ? { toolCallId: ro.tool_use_id } : {}
|
|
35120
|
-
});
|
|
35121
|
-
continue;
|
|
35122
|
-
}
|
|
35123
|
-
}
|
|
35124
|
-
return parts;
|
|
35125
|
-
}
|
|
35126
|
-
var OUTPUT_PREVIEW_LIMIT;
|
|
35127
|
-
var init_chat_parts = __esm({
|
|
35128
|
-
"../server/src/domains/trace/chat-parts.ts"() {
|
|
35129
|
-
"use strict";
|
|
35130
|
-
OUTPUT_PREVIEW_LIMIT = 4e3;
|
|
35131
|
-
}
|
|
35132
|
-
});
|
|
35133
|
-
|
|
35134
35284
|
// ../server/src/domains/trace/index.ts
|
|
35135
35285
|
function createTraceDomain(opts) {
|
|
35136
35286
|
const service = new TraceService({ store: opts.store });
|
|
@@ -35156,9 +35306,9 @@ function createChatSessionsDomain(opts) {
|
|
|
35156
35306
|
return binding?.nodeId ?? null;
|
|
35157
35307
|
}
|
|
35158
35308
|
async function enrichMessages(messages) {
|
|
35159
|
-
if (!opts.trace) return messages;
|
|
35160
35309
|
return Promise.all(messages.map(async (m2) => {
|
|
35161
|
-
if (
|
|
35310
|
+
if (Array.isArray(m2.parts) && m2.parts.length) return m2;
|
|
35311
|
+
if (!opts.trace || !m2.runId) return m2;
|
|
35162
35312
|
const events = await opts.trace.listEvents(m2.runId, {}).catch(() => []);
|
|
35163
35313
|
const parts = deriveChatPartsFromEvents(events);
|
|
35164
35314
|
return parts.length ? { ...m2, parts } : m2;
|
|
@@ -35222,17 +35372,14 @@ function createChatSessionsDomain(opts) {
|
|
|
35222
35372
|
const body = req.body;
|
|
35223
35373
|
if (!body?.role || !body?.content) throw new ApiError(400, "BAD_REQUEST", "role and content required");
|
|
35224
35374
|
if (body.role !== "user" && body.role !== "assistant") throw new ApiError(400, "BAD_REQUEST", "role must be user or assistant");
|
|
35225
|
-
const
|
|
35226
|
-
const msg = {
|
|
35375
|
+
const msg = await store.appendMessage({
|
|
35227
35376
|
id: (0, import_node_crypto10.randomUUID)(),
|
|
35228
35377
|
sessionId: req.params.id,
|
|
35229
35378
|
role: body.role,
|
|
35230
35379
|
content: body.content,
|
|
35231
|
-
seq: existing.length + 1,
|
|
35232
35380
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35233
35381
|
...body.runId ? { runId: body.runId } : {}
|
|
35234
|
-
};
|
|
35235
|
-
await store.appendMessage(msg);
|
|
35382
|
+
});
|
|
35236
35383
|
await store.updateSession(req.params.id, { touchedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
35237
35384
|
return { status: 201, body: msg };
|
|
35238
35385
|
});
|
|
@@ -37108,7 +37255,8 @@ var init_worker = __esm({
|
|
|
37108
37255
|
limits,
|
|
37109
37256
|
// OASIS_STAGE=1:本会话里 link/spawn/unlink/seal/cancel-part 进编辑缓冲、apply 才提交(react 攒事务)。
|
|
37110
37257
|
env: { ...prov?.env ?? {}, OASIS_STAGE: "1", ...workspace ? { OASIS_WORKSPACE: workspace } : {} },
|
|
37111
|
-
...prov?.wrapperPaths && prov.wrapperPaths.length > 0 ? { wrapperPaths: prov.wrapperPaths } : {}
|
|
37258
|
+
...prov?.wrapperPaths && prov.wrapperPaths.length > 0 ? { wrapperPaths: prov.wrapperPaths } : {},
|
|
37259
|
+
...prov?.requiredTools && prov.requiredTools.length > 0 ? { requiredTools: prov.requiredTools } : {}
|
|
37112
37260
|
};
|
|
37113
37261
|
this.deps.log?.(`[coordinator] ${logMsg}`);
|
|
37114
37262
|
const handle = await this.deps.adapter.spawn(job);
|
|
@@ -37135,112 +37283,75 @@ var init_worker = __esm({
|
|
|
37135
37283
|
}
|
|
37136
37284
|
});
|
|
37137
37285
|
|
|
37138
|
-
// ../server/src/governance/
|
|
37139
|
-
async function
|
|
37140
|
-
const
|
|
37141
|
-
const
|
|
37142
|
-
|
|
37143
|
-
|
|
37144
|
-
|
|
37145
|
-
|
|
37146
|
-
|
|
37286
|
+
// ../server/src/governance/builtin-skills.ts
|
|
37287
|
+
async function collectDir(root, dir, out) {
|
|
37288
|
+
const entries = await (0, import_promises.readdir)(dir, { withFileTypes: true });
|
|
37289
|
+
for (const e of entries) {
|
|
37290
|
+
const abs = (0, import_node_path.join)(dir, e.name);
|
|
37291
|
+
if (e.isDirectory()) {
|
|
37292
|
+
await collectDir(root, abs, out);
|
|
37293
|
+
} else if (e.isFile()) {
|
|
37294
|
+
const info = await (0, import_promises.stat)(abs);
|
|
37295
|
+
if (info.size > MAX_FILE_BYTES2) {
|
|
37296
|
+
console.warn(`[builtin-skills] skip oversized file (${info.size}B): ${abs}`);
|
|
37297
|
+
continue;
|
|
37298
|
+
}
|
|
37299
|
+
const rel = (0, import_node_path.relative)(root, abs).split(/[\\/]/).join("/");
|
|
37300
|
+
out[rel] = await (0, import_promises.readFile)(abs, "utf8");
|
|
37301
|
+
}
|
|
37302
|
+
}
|
|
37303
|
+
}
|
|
37304
|
+
async function loadBuiltinSkills(skillsDir) {
|
|
37305
|
+
let dirents;
|
|
37306
|
+
try {
|
|
37307
|
+
dirents = await (0, import_promises.readdir)(skillsDir, { withFileTypes: true });
|
|
37308
|
+
} catch {
|
|
37309
|
+
return [];
|
|
37310
|
+
}
|
|
37311
|
+
const skills = [];
|
|
37312
|
+
for (const d of dirents) {
|
|
37313
|
+
if (!d.isDirectory()) continue;
|
|
37314
|
+
const slug4 = d.name;
|
|
37315
|
+
const skillDir = (0, import_node_path.join)(skillsDir, slug4);
|
|
37316
|
+
const files = {};
|
|
37317
|
+
try {
|
|
37318
|
+
await collectDir(skillDir, skillDir, files);
|
|
37319
|
+
} catch (err) {
|
|
37320
|
+
console.warn(`[builtin-skills] failed reading ${slug4}: ${String(err)}`);
|
|
37321
|
+
continue;
|
|
37322
|
+
}
|
|
37323
|
+
const skillMd = files["SKILL.md"];
|
|
37324
|
+
if (!skillMd) {
|
|
37325
|
+
console.warn(`[builtin-skills] ${slug4} has no SKILL.md, skipping`);
|
|
37326
|
+
continue;
|
|
37327
|
+
}
|
|
37328
|
+
const { name, description } = parseSkillFrontmatter(skillMd);
|
|
37329
|
+
skills.push({ id: slug4, name: name || slug4, description, files });
|
|
37330
|
+
}
|
|
37331
|
+
return skills;
|
|
37332
|
+
}
|
|
37333
|
+
function materializeBuiltinSkills(builtins, skillsDirPrefix) {
|
|
37334
|
+
const out = {};
|
|
37335
|
+
for (const skill of builtins) {
|
|
37336
|
+
const dir = sanitize(skill.name || skill.id);
|
|
37337
|
+
for (const [rel, content] of Object.entries(skill.files)) {
|
|
37338
|
+
out[`${skillsDirPrefix}/${dir}/${rel}`] = content;
|
|
37339
|
+
}
|
|
37147
37340
|
}
|
|
37148
|
-
|
|
37341
|
+
return out;
|
|
37149
37342
|
}
|
|
37150
|
-
|
|
37151
|
-
|
|
37152
|
-
"
|
|
37343
|
+
function sanitize(name) {
|
|
37344
|
+
const s2 = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
37345
|
+
return s2 || "skill";
|
|
37346
|
+
}
|
|
37347
|
+
var import_promises, import_node_path, MAX_FILE_BYTES2;
|
|
37348
|
+
var init_builtin_skills = __esm({
|
|
37349
|
+
"../server/src/governance/builtin-skills.ts"() {
|
|
37153
37350
|
"use strict";
|
|
37154
|
-
|
|
37155
|
-
|
|
37156
|
-
|
|
37157
|
-
|
|
37158
|
-
description: ${DESCRIPTION}
|
|
37159
|
-
---
|
|
37160
|
-
|
|
37161
|
-
# 建单:把对话落成一张工单
|
|
37162
|
-
|
|
37163
|
-
工单 = 一张以产物为中心的协作图:一个根 ´brief´(工单总规格)+ 若干分解节点(prd/research/design/dev/qa…),节点间连依赖边。你把对话敲定的内容**结构化暂存**成一份草案,人在编辑器里看、确认才真创建。
|
|
37164
|
-
|
|
37165
|
-
## 流程
|
|
37166
|
-
1. **查重** ´oasis workorders --search <词>´:有没有类似工单在做/做过,别重复立项。
|
|
37167
|
-
2. **建单** ´oasis create-workorder …´:暂存**草案**(stage=draft,人确认才真创建),命令**直接打印** workspace(工单 id)+ 各节点 id。
|
|
37168
|
-
3. **改草案** ´oasis spawn/edit/link/cancel …´:人确认前,随时调这份草案(加/删/改节点、连边)。
|
|
37169
|
-
4. 人在编辑器确认 → 工单创建。**你不用 apply**,确认是人的事。
|
|
37170
|
-
|
|
37171
|
-
## 一、查重(建单前必做)
|
|
37172
|
-
- ´oasis workorders --search <关键词>´:列/搜工单。命中类似的 → ´oasis workorder <工单id>´ 看详情,已覆盖就别再建、跟用户说明。
|
|
37173
|
-
|
|
37174
|
-
## 二、建单
|
|
37175
|
-
信息不足、关键决策没定**别硬建**,用文字问用户;一个工单一个目标,多件独立的事拆成多张。
|
|
37176
|
-
|
|
37177
|
-
´´´bash
|
|
37178
|
-
oasis create-workorder \
|
|
37179
|
-
--type <工单主产物类型,如 prd;根 brief 自动建> \
|
|
37180
|
-
--title "工单名,名词短语,≤20字" \
|
|
37181
|
-
--goal "一句话目标,≤50字" \
|
|
37182
|
-
--brief "完整规格(markdown):背景、关键决策、约束、范围、不做什么" \
|
|
37183
|
-
--acceptance '["逐条可判定的验收条件","没聊到给 []"]' \
|
|
37184
|
-
--plan '<节点分解 JSON,见下>'
|
|
37185
|
-
´´´
|
|
37186
|
-
**不用填 workspace——系统自动生成工单 id。** 命令**直接打印 workspace(工单 id)和每个节点的 id**,记住它们、改草案要用——**不必再去 query**。它产出的是**草案**(stage=draft):人在编辑器确认才真创建,你别拿它当活工单使。
|
|
37187
|
-
|
|
37188
|
-
### --plan 结构
|
|
37189
|
-
´´´json
|
|
37190
|
-
{
|
|
37191
|
-
"nodes": [
|
|
37192
|
-
{ "type": "节点类型", "title": "≤20字,同 type 内唯一",
|
|
37193
|
-
"dependsOn": ["上游节点写 type:title,如 prd:需求;无上游写 []"],
|
|
37194
|
-
"brief": "这个节点具体要产出什么的规格,别套模板、别只写类型名" }
|
|
37195
|
-
],
|
|
37196
|
-
"notes": ["可选:分解理由/缺口;没有给 []"]
|
|
37197
|
-
}
|
|
37198
|
-
´´´
|
|
37199
|
-
|
|
37200
|
-
## 三、改草案(人确认前随时改)
|
|
37201
|
-
建单后草案在编辑器里等人确认。用户要调整就用下面的命令改这份草案(**改完即时生效、人在编辑器里能看到**,你不用 apply):
|
|
37202
|
-
|
|
37203
|
-
**节点引用 = id 或 type:title,分清:**
|
|
37204
|
-
- **填完整 id**(´oasis workorder´ / create 返回的,如 ´artifact:prd:x7k2-需求´)→ **不用带 workspace**。推荐。
|
|
37205
|
-
- **填 type:title**(简写,如 ´prd:需求´)→ **必须加 ´--workspace <工单id>´**(create 返回的那个)。
|
|
37206
|
-
- ´spawn´ 是新建节点、没有现成 id → **一律带 ´--workspace´**。
|
|
37207
|
-
|
|
37208
|
-
´´´bash
|
|
37209
|
-
# 加节点(带 workspace;--input 上游可用 type:title)
|
|
37210
|
-
oasis spawn --type qa --title 验收用例 --brief "覆盖登录成功/失败" --input dev:实现 --workspace ws:wo-x7k2
|
|
37211
|
-
# 改节点标题/规格(用 id,免 workspace)
|
|
37212
|
-
oasis edit artifact:prd:x7k2-需求 --title "需求与范围"
|
|
37213
|
-
# 连边:消费者依赖上游
|
|
37214
|
-
oasis link artifact:qa:x7k2-验收 --to artifact:dev:x7k2-实现
|
|
37215
|
-
# 删节点(从草案删掉,连它的边一起摘)
|
|
37216
|
-
oasis cancel prd:需求 --workspace ws:wo-x7k2
|
|
37217
|
-
´´´
|
|
37218
|
-
|
|
37219
|
-
# 命令参考
|
|
37220
|
-
|
|
37221
|
-
## 查询
|
|
37222
|
-
- ´oasis workorders [--search <词>]´ —— 列/搜工单(id/名/阶段/进度)。无参列全部;´--search´ 端侧过滤。
|
|
37223
|
-
- ´oasis workorder <工单id>´ —— 工单详情:**stage**(draft 草案 / executing 进行中 / review 评审 / blocked 卡住 / concluded 已完成 / sealed 已封存)+ 目标/验收 + 节点(id/type)+ 依赖边。**刚建的草案这里 ´stage=draft´、也查得到(含各节点 id)**;真查不到才报不存在。
|
|
37224
|
-
- ´oasis artifact-types list | search <词> | show <type>´ —— 看可用节点类型(´show´ 看该类型可填的自定义字段)。
|
|
37225
|
-
- ´oasis projects [--search <词>]´ —— 列/搜项目(建单 ´--project´ 用)。
|
|
37226
|
-
|
|
37227
|
-
## 建单
|
|
37228
|
-
- ´oasis create-workorder --type <t> --title <s> [--goal <s>] [--brief <s>] [--acceptance '<json string[]>'] [--plan '<json>'] [--project <id>]´
|
|
37229
|
-
暂存建单草案。´--type´=主产物类型(必填,根 brief 自动建);´--title´ 必填。**不填 workspace(系统生成)**。返回 ´workspace´ + 各节点 ´id´。
|
|
37230
|
-
|
|
37231
|
-
## 改草案(引用 id 不带 workspace;引用 type:title 或 spawn 带 ´--workspace <工单id>´)
|
|
37232
|
-
- ´oasis spawn --type <t> --title <s> [--brief <s>] [--input <ref>,<ref>…] --workspace <工单id>´ —— 加节点,id 据 (type,title) 自动派生。´--type´/´--title´ 必填,title 同 type 唯一;´--input´=上游依赖(逗号分隔)。
|
|
37233
|
-
- ´oasis edit <ref> [--title <s>] [--brief <s>]´ —— 改节点标题/规格。
|
|
37234
|
-
- ´oasis link <consumer-ref> --to <upstream-ref> [--required true]´ —— 加依赖边。´--required true´=阻塞性(上游没完成下游动不了),省=软依赖。
|
|
37235
|
-
- ´oasis unlink <consumer-ref> --to <upstream-ref>´ —— 删依赖边。
|
|
37236
|
-
- ´oasis cancel <ref> [--note <s>]´ —— 从草案删节点(连它的边一起摘)。
|
|
37237
|
-
|
|
37238
|
-
## 规则
|
|
37239
|
-
- **节点类型**:先 ´oasis artifact-types list/search/show´ 挑最贴合的;都不贴合用 ´oasis artifact-types add <name> --content-type <text|code|…> --owner-role <角色>´ 新建再用。常见:prd、research、design、adr、dev、qa。
|
|
37240
|
-
- **title**:≤20字、同 type 内唯一、别用半角冒号 ´:´(与 type:title 分隔冲突,要冒号用全角 ´:´)。一个工单可多个同 type 节点,靠 title 区分。
|
|
37241
|
-
- **项目**:归某项目就 ´oasis projects --search <词>´ 找 id 再加 ´--project´。
|
|
37242
|
-
- **边界**:你只管图的结构与规格,不替用户拍产品决策——拿不准就问。改**已建好的活工单**是协调者的活,不在本技能范围。
|
|
37243
|
-
`.replace(/´/g, "`");
|
|
37351
|
+
import_promises = require("node:fs/promises");
|
|
37352
|
+
import_node_path = require("node:path");
|
|
37353
|
+
init_skill_fetcher();
|
|
37354
|
+
MAX_FILE_BYTES2 = 1 << 20;
|
|
37244
37355
|
}
|
|
37245
37356
|
});
|
|
37246
37357
|
|
|
@@ -37283,7 +37394,7 @@ var init_src4 = __esm({
|
|
|
37283
37394
|
init_worker();
|
|
37284
37395
|
init_drafts();
|
|
37285
37396
|
init_workorder_drafts();
|
|
37286
|
-
|
|
37397
|
+
init_builtin_skills();
|
|
37287
37398
|
}
|
|
37288
37399
|
});
|
|
37289
37400
|
|
|
@@ -38855,11 +38966,11 @@ var require_pg_connection_string = __commonJS({
|
|
|
38855
38966
|
config2.client_encoding = result.searchParams.get("encoding");
|
|
38856
38967
|
return config2;
|
|
38857
38968
|
}
|
|
38858
|
-
const
|
|
38969
|
+
const hostname6 = dummyHost ? "" : result.hostname;
|
|
38859
38970
|
if (!config2.host) {
|
|
38860
|
-
config2.host = decodeURIComponent(
|
|
38861
|
-
} else if (
|
|
38862
|
-
result.pathname =
|
|
38971
|
+
config2.host = decodeURIComponent(hostname6);
|
|
38972
|
+
} else if (hostname6 && /^%2f/i.test(hostname6)) {
|
|
38973
|
+
result.pathname = hostname6 + result.pathname;
|
|
38863
38974
|
}
|
|
38864
38975
|
if (!config2.port) {
|
|
38865
38976
|
config2.port = result.port;
|
|
@@ -40824,8 +40935,8 @@ var require_lib = __commonJS({
|
|
|
40824
40935
|
var helper = require_helper();
|
|
40825
40936
|
module2.exports = function(connInfo, cb) {
|
|
40826
40937
|
var file = helper.getFileName();
|
|
40827
|
-
fs17.stat(file, function(err,
|
|
40828
|
-
if (err || !helper.usePgPass(
|
|
40938
|
+
fs17.stat(file, function(err, stat2) {
|
|
40939
|
+
if (err || !helper.usePgPass(stat2, file)) {
|
|
40829
40940
|
return cb(void 0);
|
|
40830
40941
|
}
|
|
40831
40942
|
var st = fs17.createReadStream(file);
|
|
@@ -41500,7 +41611,7 @@ var require_pg_pool = __commonJS({
|
|
|
41500
41611
|
function throwOnDoubleRelease() {
|
|
41501
41612
|
throw new Error("Release called on client which has already been released to the pool.");
|
|
41502
41613
|
}
|
|
41503
|
-
function
|
|
41614
|
+
function promisify4(Promise2, callback) {
|
|
41504
41615
|
if (callback) {
|
|
41505
41616
|
return { callback, result: void 0 };
|
|
41506
41617
|
}
|
|
@@ -41638,7 +41749,7 @@ var require_pg_pool = __commonJS({
|
|
|
41638
41749
|
const err = new Error("Cannot use a pool after calling end on the pool");
|
|
41639
41750
|
return cb ? cb(err) : this.Promise.reject(err);
|
|
41640
41751
|
}
|
|
41641
|
-
const response =
|
|
41752
|
+
const response = promisify4(this.Promise, cb);
|
|
41642
41753
|
const result = response.result;
|
|
41643
41754
|
if (this._isFull() || this._idle.length) {
|
|
41644
41755
|
if (this._idle.length) {
|
|
@@ -41823,7 +41934,7 @@ var require_pg_pool = __commonJS({
|
|
|
41823
41934
|
}
|
|
41824
41935
|
query(text, values, cb) {
|
|
41825
41936
|
if (typeof text === "function") {
|
|
41826
|
-
const response2 =
|
|
41937
|
+
const response2 = promisify4(this.Promise, text);
|
|
41827
41938
|
setImmediate(function() {
|
|
41828
41939
|
return response2.callback(new Error("Passing a function as the first parameter to pool.query is not supported"));
|
|
41829
41940
|
});
|
|
@@ -41833,7 +41944,7 @@ var require_pg_pool = __commonJS({
|
|
|
41833
41944
|
cb = values;
|
|
41834
41945
|
values = void 0;
|
|
41835
41946
|
}
|
|
41836
|
-
const response =
|
|
41947
|
+
const response = promisify4(this.Promise, cb);
|
|
41837
41948
|
cb = response.callback;
|
|
41838
41949
|
this.connect((err, client) => {
|
|
41839
41950
|
if (err) {
|
|
@@ -41878,7 +41989,7 @@ var require_pg_pool = __commonJS({
|
|
|
41878
41989
|
return cb ? cb(err) : this.Promise.reject(err);
|
|
41879
41990
|
}
|
|
41880
41991
|
this.ending = true;
|
|
41881
|
-
const promised =
|
|
41992
|
+
const promised = promisify4(this.Promise, cb);
|
|
41882
41993
|
this._endCallback = promised.callback;
|
|
41883
41994
|
this._pulseQueue();
|
|
41884
41995
|
return promised.result;
|
|
@@ -42472,30 +42583,138 @@ var init_trajectory = __esm({
|
|
|
42472
42583
|
var fs16 = __toESM(require("node:fs"));
|
|
42473
42584
|
var os8 = __toESM(require("node:os"));
|
|
42474
42585
|
var path13 = __toESM(require("node:path"));
|
|
42475
|
-
var
|
|
42586
|
+
var import_node_child_process12 = require("node:child_process");
|
|
42476
42587
|
|
|
42477
42588
|
// ../cli/src/cli.ts
|
|
42478
42589
|
var fs15 = __toESM(require("node:fs"), 1);
|
|
42479
42590
|
var os7 = __toESM(require("node:os"), 1);
|
|
42480
42591
|
var path12 = __toESM(require("node:path"), 1);
|
|
42481
|
-
var
|
|
42592
|
+
var import_node_crypto23 = require("node:crypto");
|
|
42482
42593
|
|
|
42483
42594
|
// ../cli/src/serve.ts
|
|
42484
42595
|
var fs14 = __toESM(require("node:fs"), 1);
|
|
42485
42596
|
var os6 = __toESM(require("node:os"), 1);
|
|
42486
42597
|
var path11 = __toESM(require("node:path"), 1);
|
|
42487
|
-
var
|
|
42598
|
+
var import_node_crypto21 = require("node:crypto");
|
|
42488
42599
|
var import_node_url3 = require("node:url");
|
|
42489
42600
|
init_src2();
|
|
42490
42601
|
init_src4();
|
|
42491
42602
|
|
|
42492
|
-
// ../connectors/src/
|
|
42603
|
+
// ../connectors/src/_base/stage.ts
|
|
42604
|
+
var import_node_fs2 = require("node:fs");
|
|
42605
|
+
var import_node_os = require("node:os");
|
|
42606
|
+
var import_node_path2 = require("node:path");
|
|
42607
|
+
var stagedBinDirs = /* @__PURE__ */ new Map();
|
|
42608
|
+
function stageConnectorWrapper(connector) {
|
|
42609
|
+
const scriptPath = connector.wrapperScriptPath();
|
|
42610
|
+
if (scriptPath === null) return null;
|
|
42611
|
+
const commandName = connector.commandName;
|
|
42612
|
+
if (!commandName) {
|
|
42613
|
+
throw new Error(`connector ${connector.config.slug}: commandName \u4E3A\u7A7A\uFF0CCLI \u7C7B\u5FC5\u987B\u58F0\u660E\u8981\u906E\u853D\u7684\u547D\u4EE4\u540D`);
|
|
42614
|
+
}
|
|
42615
|
+
const cacheKey = `${commandName}\0${scriptPath}`;
|
|
42616
|
+
const cached2 = stagedBinDirs.get(cacheKey);
|
|
42617
|
+
if (cached2 && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(cached2, commandName))) return (0, import_node_path2.join)(cached2, commandName);
|
|
42618
|
+
const binDir = (0, import_node_fs2.mkdtempSync)((0, import_node_path2.join)((0, import_node_os.tmpdir)(), `oasis-conn-${connector.config.slug}-`));
|
|
42619
|
+
const link = (0, import_node_path2.join)(binDir, commandName);
|
|
42620
|
+
(0, import_node_fs2.symlinkSync)(scriptPath, link);
|
|
42621
|
+
stagedBinDirs.set(cacheKey, binDir);
|
|
42622
|
+
return link;
|
|
42623
|
+
}
|
|
42624
|
+
|
|
42625
|
+
// ../connectors/src/_base/install.ts
|
|
42493
42626
|
var import_node_child_process3 = require("node:child_process");
|
|
42494
42627
|
var import_node_util = require("node:util");
|
|
42495
|
-
var import_node_path = require("node:path");
|
|
42496
|
-
var import_node_url = require("node:url");
|
|
42497
42628
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process3.execFile);
|
|
42498
|
-
|
|
42629
|
+
function installPlan(cmd, platform) {
|
|
42630
|
+
if (cmd === "lark-cli") {
|
|
42631
|
+
const npm = platform === "win32" ? "npm.cmd" : "npm";
|
|
42632
|
+
return [{ file: npm, args: ["install", "-g", "@larksuite/cli"] }];
|
|
42633
|
+
}
|
|
42634
|
+
const pkg = cmd === "gh" ? { brew: "gh", apt: "gh", dnf: "gh", pacman: "github-cli", apk: "github-cli", winget: "GitHub.cli", choco: "gh", scoop: "gh" } : { brew: "git", apt: "git", dnf: "git", pacman: "git", apk: "git", winget: "Git.Git", choco: "git", scoop: "git" };
|
|
42635
|
+
if (platform === "darwin") {
|
|
42636
|
+
return [{ file: "brew", args: ["install", pkg.brew], requires: "brew" }];
|
|
42637
|
+
}
|
|
42638
|
+
if (platform === "win32") {
|
|
42639
|
+
return [
|
|
42640
|
+
{ file: "winget", args: ["install", "--silent", "--accept-package-agreements", "--accept-source-agreements", "-e", "--id", pkg.winget], requires: "winget" },
|
|
42641
|
+
{ file: "choco", args: ["install", "-y", pkg.choco], requires: "choco" },
|
|
42642
|
+
{ file: "scoop", args: ["install", pkg.scoop], requires: "scoop" }
|
|
42643
|
+
];
|
|
42644
|
+
}
|
|
42645
|
+
return [
|
|
42646
|
+
{ file: "sudo", args: ["apt-get", "install", "-y", "--no-install-recommends", pkg.apt], requires: "apt-get" },
|
|
42647
|
+
{ file: "sudo", args: ["dnf", "install", "-y", pkg.dnf], requires: "dnf" },
|
|
42648
|
+
{ file: "sudo", args: ["pacman", "-S", "--noconfirm", pkg.pacman], requires: "pacman" },
|
|
42649
|
+
{ file: "sudo", args: ["apk", "add", pkg.apk], requires: "apk" }
|
|
42650
|
+
];
|
|
42651
|
+
}
|
|
42652
|
+
var confirmed = /* @__PURE__ */ new Set();
|
|
42653
|
+
function defaultDeps() {
|
|
42654
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
42655
|
+
const probe = async (cmd) => {
|
|
42656
|
+
try {
|
|
42657
|
+
await execFileAsync(whichCmd, [cmd]);
|
|
42658
|
+
return true;
|
|
42659
|
+
} catch {
|
|
42660
|
+
return false;
|
|
42661
|
+
}
|
|
42662
|
+
};
|
|
42663
|
+
return {
|
|
42664
|
+
isPresent: probe,
|
|
42665
|
+
hasCommand: probe,
|
|
42666
|
+
run: async (file, args) => {
|
|
42667
|
+
await execFileAsync(file, args, { timeout: 18e4 });
|
|
42668
|
+
},
|
|
42669
|
+
platform: process.platform,
|
|
42670
|
+
log: (msg) => console.warn(msg)
|
|
42671
|
+
};
|
|
42672
|
+
}
|
|
42673
|
+
async function ensureToolInstalled(cmd, deps = {}) {
|
|
42674
|
+
const d = { ...defaultDeps(), ...deps };
|
|
42675
|
+
if (confirmed.has(cmd)) return true;
|
|
42676
|
+
if (await d.isPresent(cmd)) {
|
|
42677
|
+
confirmed.add(cmd);
|
|
42678
|
+
return true;
|
|
42679
|
+
}
|
|
42680
|
+
const steps = installPlan(cmd, d.platform);
|
|
42681
|
+
if (steps.length === 0) {
|
|
42682
|
+
d.log(`[connector-install] ${cmd} \u7F3A\u5931\uFF0C\u4E14 ${d.platform} \u4E0B\u65E0\u5DF2\u77E5\u81EA\u52A8\u5B89\u88C5\u6CD5\u2014\u2014\u8BF7\u624B\u52A8\u5B89\u88C5`);
|
|
42683
|
+
return false;
|
|
42684
|
+
}
|
|
42685
|
+
d.log(`[connector-install] ${cmd} \u7F3A\u5931\uFF0C\u5C1D\u8BD5\u81EA\u52A8\u5B89\u88C5\u2026`);
|
|
42686
|
+
for (const step of steps) {
|
|
42687
|
+
if (step.requires && !await d.hasCommand(step.requires)) continue;
|
|
42688
|
+
try {
|
|
42689
|
+
await d.run(step.file, step.args);
|
|
42690
|
+
if (await d.isPresent(cmd)) {
|
|
42691
|
+
confirmed.add(cmd);
|
|
42692
|
+
d.log(`[connector-install] ${cmd} \u5B89\u88C5\u6210\u529F\uFF08${step.file} ${step.args.join(" ")}\uFF09`);
|
|
42693
|
+
return true;
|
|
42694
|
+
}
|
|
42695
|
+
} catch (err) {
|
|
42696
|
+
d.log(`[connector-install] ${cmd} \u5B89\u88C5\u5C1D\u8BD5\u5931\u8D25\uFF08${step.file}\uFF09\uFF1A${String(err)}`);
|
|
42697
|
+
}
|
|
42698
|
+
}
|
|
42699
|
+
d.log(`[connector-install] ${cmd} \u81EA\u52A8\u5B89\u88C5\u672A\u6210\u529F\u2014\u2014\u5C06\u56DE\u9000\u5230 wrapper/\u547D\u4EE4\u81EA\u8EAB\u62A5\u9519`);
|
|
42700
|
+
return false;
|
|
42701
|
+
}
|
|
42702
|
+
async function ensureConnectorTools(tools, deps = {}) {
|
|
42703
|
+
if (!tools || tools.length === 0) return [];
|
|
42704
|
+
const missing = [];
|
|
42705
|
+
for (const cmd of tools) {
|
|
42706
|
+
if (!await ensureToolInstalled(cmd, deps)) missing.push(cmd);
|
|
42707
|
+
}
|
|
42708
|
+
return missing;
|
|
42709
|
+
}
|
|
42710
|
+
|
|
42711
|
+
// ../connectors/src/feishu/index.ts
|
|
42712
|
+
var import_node_child_process4 = require("node:child_process");
|
|
42713
|
+
var import_node_util2 = require("node:util");
|
|
42714
|
+
var import_node_path3 = require("node:path");
|
|
42715
|
+
var import_node_url = require("node:url");
|
|
42716
|
+
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process4.execFile);
|
|
42717
|
+
var __dirname = (0, import_node_path3.dirname)((0, import_node_url.fileURLToPath)(__esm_import_meta_url));
|
|
42499
42718
|
var FeishuConnector = class {
|
|
42500
42719
|
config = {
|
|
42501
42720
|
slug: "feishu",
|
|
@@ -42504,6 +42723,10 @@ var FeishuConnector = class {
|
|
|
42504
42723
|
{ key: "app_id", description: "\u98DE\u4E66 App ID\uFF08\u7559\u7A7A\u5219\u81EA\u52A8\u521B\u5EFA\u65B0\u5E94\u7528\uFF09", secret: false, required: false }
|
|
42505
42724
|
]
|
|
42506
42725
|
};
|
|
42726
|
+
/** wrapper 在 agent PATH 上冒名的命令——必须与真实 CLI 同名,否则遮蔽不生效。 */
|
|
42727
|
+
commandName = "lark-cli";
|
|
42728
|
+
/** node 侧需存在的真实 CLI(缺失则自动装:npm i -g @larksuite/cli,见 _base/install.ts)。 */
|
|
42729
|
+
requiredCommands = ["lark-cli"];
|
|
42507
42730
|
larkBin;
|
|
42508
42731
|
constructor(larkBin) {
|
|
42509
42732
|
this.larkBin = larkBin ?? this.resolveLarkBin();
|
|
@@ -42520,17 +42743,17 @@ var FeishuConnector = class {
|
|
|
42520
42743
|
const profileName = credentials["app_id"];
|
|
42521
42744
|
if (!profileName) throw new Error("feishu inject: missing app_id in credentials");
|
|
42522
42745
|
sessionEnv.set("LARK_PROFILE", profileName);
|
|
42523
|
-
sessionEnv.set("LARK_BIN", this.
|
|
42746
|
+
sessionEnv.set("LARK_BIN", await this.resolveRealBin());
|
|
42524
42747
|
}
|
|
42525
42748
|
wrapperScriptPath() {
|
|
42526
|
-
return (0,
|
|
42749
|
+
return (0, import_node_path3.join)(__dirname, "wrapper.sh");
|
|
42527
42750
|
}
|
|
42528
42751
|
async cleanup(sessionEnv) {
|
|
42529
42752
|
const profile = sessionEnv.get("LARK_PROFILE");
|
|
42530
42753
|
if (!profile) return;
|
|
42531
42754
|
if (!process.env["FEISHU_SIMULATE"]) {
|
|
42532
42755
|
try {
|
|
42533
|
-
await
|
|
42756
|
+
await execFileAsync2(this.larkBin, ["profile", "remove", profile, "--confirm"]);
|
|
42534
42757
|
} catch {
|
|
42535
42758
|
}
|
|
42536
42759
|
}
|
|
@@ -42539,18 +42762,18 @@ var FeishuConnector = class {
|
|
|
42539
42762
|
}
|
|
42540
42763
|
async initNewApp() {
|
|
42541
42764
|
await new Promise((resolve3, reject) => {
|
|
42542
|
-
const proc = (0,
|
|
42765
|
+
const proc = (0, import_node_child_process4.spawn)(this.larkBin, ["config", "init", "--new"], { stdio: "inherit" });
|
|
42543
42766
|
proc.on("close", (code) => code === 0 ? resolve3() : reject(new Error(`lark-cli config init exited ${code}`)));
|
|
42544
42767
|
proc.on("error", reject);
|
|
42545
42768
|
});
|
|
42546
|
-
const { stdout } = await
|
|
42769
|
+
const { stdout } = await execFileAsync2(this.larkBin, ["config", "show", "--json"]);
|
|
42547
42770
|
const cfg = JSON.parse(stdout);
|
|
42548
42771
|
if (!cfg.appId) throw new Error("feishu connector: could not determine app_id after config init");
|
|
42549
42772
|
return cfg.appId;
|
|
42550
42773
|
}
|
|
42551
42774
|
async runAuthLogin(app_id) {
|
|
42552
42775
|
await new Promise((resolve3, reject) => {
|
|
42553
|
-
const proc = (0,
|
|
42776
|
+
const proc = (0, import_node_child_process4.spawn)(this.larkBin, ["--profile", app_id, "auth", "login"], { stdio: "inherit" });
|
|
42554
42777
|
proc.on("close", (code) => code === 0 ? resolve3() : reject(new Error(`lark-cli auth login exited ${code}`)));
|
|
42555
42778
|
proc.on("error", reject);
|
|
42556
42779
|
});
|
|
@@ -42558,15 +42781,31 @@ var FeishuConnector = class {
|
|
|
42558
42781
|
resolveLarkBin() {
|
|
42559
42782
|
return process.env["LARK_CLI_BIN"] ?? "lark-cli";
|
|
42560
42783
|
}
|
|
42784
|
+
/**
|
|
42785
|
+
* 把 larkBin 解析成真实二进制的绝对路径,供 wrapper 的 LARK_BIN 使用。
|
|
42786
|
+
* 已是绝对路径直接返回;裸名("lark-cli")走 `which` 定位——绝不能把裸名交给 wrapper,
|
|
42787
|
+
* 否则 wrapper 的 exec 会命中 PATH 上的同名 wrapper 软链造成无限递归。which 失败时兜底原值,
|
|
42788
|
+
* 交由 wrapper 内 PATH 扫描逻辑(跳过自身)处理。
|
|
42789
|
+
*/
|
|
42790
|
+
async resolveRealBin() {
|
|
42791
|
+
if (this.larkBin.includes("/")) return this.larkBin;
|
|
42792
|
+
try {
|
|
42793
|
+
const { stdout } = await execFileAsync2("which", [this.larkBin]);
|
|
42794
|
+
const resolved = stdout.trim();
|
|
42795
|
+
if (resolved) return resolved;
|
|
42796
|
+
} catch {
|
|
42797
|
+
}
|
|
42798
|
+
return this.larkBin;
|
|
42799
|
+
}
|
|
42561
42800
|
};
|
|
42562
42801
|
|
|
42563
42802
|
// ../connectors/src/github/index.ts
|
|
42564
|
-
var
|
|
42565
|
-
var
|
|
42566
|
-
var
|
|
42803
|
+
var import_node_child_process5 = require("node:child_process");
|
|
42804
|
+
var import_node_util3 = require("node:util");
|
|
42805
|
+
var import_node_path4 = require("node:path");
|
|
42567
42806
|
var import_node_url2 = require("node:url");
|
|
42568
|
-
var
|
|
42569
|
-
var __dirname2 = (0,
|
|
42807
|
+
var execFile3 = (0, import_node_util3.promisify)(import_node_child_process5.execFile);
|
|
42808
|
+
var __dirname2 = (0, import_node_path4.dirname)((0, import_node_url2.fileURLToPath)(__esm_import_meta_url));
|
|
42570
42809
|
var GithubConnector = class {
|
|
42571
42810
|
config = {
|
|
42572
42811
|
slug: "github",
|
|
@@ -42576,6 +42815,10 @@ var GithubConnector = class {
|
|
|
42576
42815
|
{ key: "actor_name", description: "AI \u5458\u5DE5\u540D\u79F0\uFF08\u7528\u4E8E git \u63D0\u4EA4\u7F72\u540D\uFF09", secret: false, required: false }
|
|
42577
42816
|
]
|
|
42578
42817
|
};
|
|
42818
|
+
/** wrapper 在 agent PATH 上冒名的命令——必须与真实 CLI 同名,否则遮蔽不生效。 */
|
|
42819
|
+
commandName = "gh";
|
|
42820
|
+
/** node 侧需存在的真实 CLI(缺失则按 OS 自动装,见 _base/install.ts)。gh + git 都要。 */
|
|
42821
|
+
requiredCommands = ["gh", "git"];
|
|
42579
42822
|
async connect(options = {}) {
|
|
42580
42823
|
if (process.env["GITHUB_SIMULATE"] && options["access_token"]) {
|
|
42581
42824
|
return { credentials: { access_token: options["access_token"] ?? "ghp_simulated", actor_name: options["actor_name"] ?? "oasis-agent" } };
|
|
@@ -42605,7 +42848,7 @@ var GithubConnector = class {
|
|
|
42605
42848
|
sessionEnv.set("EMAIL", "");
|
|
42606
42849
|
}
|
|
42607
42850
|
wrapperScriptPath() {
|
|
42608
|
-
return (0,
|
|
42851
|
+
return (0, import_node_path4.join)(__dirname2, "wrapper.sh");
|
|
42609
42852
|
}
|
|
42610
42853
|
async cleanup(sessionEnv) {
|
|
42611
42854
|
for (const k2 of [
|
|
@@ -42622,59 +42865,19 @@ var GithubConnector = class {
|
|
|
42622
42865
|
}
|
|
42623
42866
|
}
|
|
42624
42867
|
// ── tool availability ────────────────────────────────────────────────────────
|
|
42868
|
+
/**
|
|
42869
|
+
* 确保 gh + git 存在(缺失则按 OS 装)。委托给共享 installer(_base/install.ts)——
|
|
42870
|
+
* 与 node 侧 pre-spawn 钩子同一套跨平台安装逻辑,不再各写一份。装不上则抛(connect() 语义要求可用)。
|
|
42871
|
+
*/
|
|
42625
42872
|
async ensureTools() {
|
|
42626
|
-
const missing =
|
|
42627
|
-
|
|
42628
|
-
|
|
42629
|
-
}
|
|
42630
|
-
if (missing.length === 0) return;
|
|
42631
|
-
console.warn(`[github connector] missing tools: ${missing.join(", ")} \u2014 installing`);
|
|
42632
|
-
await this.installTools(missing);
|
|
42633
|
-
for (const tool of missing) {
|
|
42634
|
-
if (!await this.isAvailable(tool))
|
|
42635
|
-
throw new Error(`github connector: ${tool} could not be installed \u2014 install it manually`);
|
|
42636
|
-
}
|
|
42637
|
-
}
|
|
42638
|
-
async isAvailable(tool) {
|
|
42639
|
-
try {
|
|
42640
|
-
await execFile2("which", [tool]);
|
|
42641
|
-
return true;
|
|
42642
|
-
} catch {
|
|
42643
|
-
return false;
|
|
42644
|
-
}
|
|
42645
|
-
}
|
|
42646
|
-
async installTools(tools) {
|
|
42647
|
-
for (const tool of tools) {
|
|
42648
|
-
try {
|
|
42649
|
-
if (process.platform === "darwin") {
|
|
42650
|
-
await execFile2("brew", ["install", tool === "gh" ? "gh" : "git"]);
|
|
42651
|
-
} else {
|
|
42652
|
-
if (tool === "git") {
|
|
42653
|
-
await execFile2("sudo", ["apt-get", "install", "-y", "--no-install-recommends", "git"]);
|
|
42654
|
-
} else {
|
|
42655
|
-
try {
|
|
42656
|
-
await execFile2("snap", ["install", "gh"]);
|
|
42657
|
-
} catch {
|
|
42658
|
-
await execFile2("bash", ["-c", [
|
|
42659
|
-
"curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg",
|
|
42660
|
-
"| sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg",
|
|
42661
|
-
"&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg",
|
|
42662
|
-
'&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main"',
|
|
42663
|
-
"| sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null",
|
|
42664
|
-
"&& sudo apt-get update && sudo apt-get install -y gh"
|
|
42665
|
-
].join(" ")]);
|
|
42666
|
-
}
|
|
42667
|
-
}
|
|
42668
|
-
}
|
|
42669
|
-
console.warn(`[github connector] installed ${tool}`);
|
|
42670
|
-
} catch (err) {
|
|
42671
|
-
console.error(`[github connector] failed to install ${tool}:`, String(err));
|
|
42672
|
-
}
|
|
42873
|
+
const missing = await ensureConnectorTools(this.requiredCommands);
|
|
42874
|
+
if (missing.length > 0) {
|
|
42875
|
+
throw new Error(`github connector: ${missing.join(", ")} could not be installed \u2014 install it manually`);
|
|
42673
42876
|
}
|
|
42674
42877
|
}
|
|
42675
42878
|
async resolveRealBin(name) {
|
|
42676
42879
|
try {
|
|
42677
|
-
const { stdout } = await
|
|
42880
|
+
const { stdout } = await execFile3("which", [name]);
|
|
42678
42881
|
return stdout.trim();
|
|
42679
42882
|
} catch {
|
|
42680
42883
|
return name;
|
|
@@ -42683,12 +42886,12 @@ var GithubConnector = class {
|
|
|
42683
42886
|
};
|
|
42684
42887
|
|
|
42685
42888
|
// ../connectors/src/connector-adapter.ts
|
|
42686
|
-
var
|
|
42687
|
-
var
|
|
42688
|
-
var
|
|
42889
|
+
var import_promises2 = require("node:fs/promises");
|
|
42890
|
+
var import_node_os2 = require("node:os");
|
|
42891
|
+
var import_node_path5 = require("node:path");
|
|
42689
42892
|
|
|
42690
42893
|
// ../adapters/src/claude-code/index.ts
|
|
42691
|
-
var
|
|
42894
|
+
var import_node_child_process6 = require("node:child_process");
|
|
42692
42895
|
var import_node_crypto14 = require("node:crypto");
|
|
42693
42896
|
var fs9 = __toESM(require("node:fs"), 1);
|
|
42694
42897
|
var os2 = __toESM(require("node:os"), 1);
|
|
@@ -42840,7 +43043,7 @@ var ClaudeCodeAdapter = class {
|
|
|
42840
43043
|
...this.opts.extraArgs ?? []
|
|
42841
43044
|
];
|
|
42842
43045
|
const otelEnv = this.resolveOtelEnv(job, id);
|
|
42843
|
-
const child = (0,
|
|
43046
|
+
const child = (0, import_node_child_process6.spawn)(this.opts.claudeBin ?? "claude", args, {
|
|
42844
43047
|
cwd: dir,
|
|
42845
43048
|
env: {
|
|
42846
43049
|
// 凭证泄漏防护:剔除 pnpm/npm 脚本注入的变量——npm_lifecycle_script 等会把 serve 的启动命令
|
|
@@ -42962,7 +43165,7 @@ var ClaudeCodeAdapter = class {
|
|
|
42962
43165
|
};
|
|
42963
43166
|
|
|
42964
43167
|
// ../adapters/src/codex/index.ts
|
|
42965
|
-
var
|
|
43168
|
+
var import_node_child_process7 = require("node:child_process");
|
|
42966
43169
|
var import_node_crypto15 = require("node:crypto");
|
|
42967
43170
|
var fs10 = __toESM(require("node:fs"), 1);
|
|
42968
43171
|
var os3 = __toESM(require("node:os"), 1);
|
|
@@ -43261,7 +43464,7 @@ var CodexAdapter = class {
|
|
|
43261
43464
|
...this.opts.extraArgs ?? [],
|
|
43262
43465
|
prompt
|
|
43263
43466
|
];
|
|
43264
|
-
const child = (0,
|
|
43467
|
+
const child = (0, import_node_child_process7.spawn)(this.opts.codexBin ?? "codex", args, {
|
|
43265
43468
|
cwd: dir,
|
|
43266
43469
|
env: {
|
|
43267
43470
|
...scrubLeakyEnv(process.env),
|
|
@@ -43362,7 +43565,7 @@ var CodexAdapter = class {
|
|
|
43362
43565
|
};
|
|
43363
43566
|
|
|
43364
43567
|
// ../adapters/src/_core/acp.ts
|
|
43365
|
-
var
|
|
43568
|
+
var import_node_child_process8 = require("node:child_process");
|
|
43366
43569
|
var import_node_crypto16 = require("node:crypto");
|
|
43367
43570
|
var fs11 = __toESM(require("node:fs"), 1);
|
|
43368
43571
|
var os4 = __toESM(require("node:os"), 1);
|
|
@@ -43785,7 +43988,7 @@ async function runACPSession(job, cfg) {
|
|
|
43785
43988
|
}
|
|
43786
43989
|
const task = job.bundle.files["TASK.md"];
|
|
43787
43990
|
if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
43788
|
-
const child = (0,
|
|
43991
|
+
const child = (0, import_node_child_process8.spawn)(cfg.bin, [...cfg.args ?? [], ...cfg.extraArgs ?? []], {
|
|
43789
43992
|
cwd: dir,
|
|
43790
43993
|
env: {
|
|
43791
43994
|
...scrubLeakyEnv(process.env),
|
|
@@ -43991,7 +44194,7 @@ var KiroAdapter = class {
|
|
|
43991
44194
|
};
|
|
43992
44195
|
|
|
43993
44196
|
// ../adapters/src/_core/subprocess.ts
|
|
43994
|
-
var
|
|
44197
|
+
var import_node_child_process9 = require("node:child_process");
|
|
43995
44198
|
var import_node_crypto17 = require("node:crypto");
|
|
43996
44199
|
var fs12 = __toESM(require("node:fs"), 1);
|
|
43997
44200
|
var os5 = __toESM(require("node:os"), 1);
|
|
@@ -44050,7 +44253,7 @@ function makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs) {
|
|
|
44050
44253
|
function runStreamJson(job, cfg) {
|
|
44051
44254
|
const { id, dir, task: _task } = materialize(job, cfg.workRoot);
|
|
44052
44255
|
const args = [...cfg.buildArgs(job, dir), ...cfg.extraArgs ?? []];
|
|
44053
|
-
const child = (0,
|
|
44256
|
+
const child = (0, import_node_child_process9.spawn)(cfg.bin, args, {
|
|
44054
44257
|
cwd: dir,
|
|
44055
44258
|
env: buildEnv(job, cfg.env),
|
|
44056
44259
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -44108,7 +44311,7 @@ function runStreamJson(job, cfg) {
|
|
|
44108
44311
|
function runOneShotText(job, cfg) {
|
|
44109
44312
|
const { id, dir } = materialize(job, cfg.workRoot);
|
|
44110
44313
|
const args = [...cfg.buildArgs(job, dir), ...cfg.extraArgs ?? []];
|
|
44111
|
-
const child = (0,
|
|
44314
|
+
const child = (0, import_node_child_process9.spawn)(cfg.bin, args, {
|
|
44112
44315
|
cwd: dir,
|
|
44113
44316
|
env: buildEnv(job, cfg.env),
|
|
44114
44317
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -44616,6 +44819,10 @@ var ConnectorAwareAdapter = class {
|
|
|
44616
44819
|
}
|
|
44617
44820
|
async spawn(job) {
|
|
44618
44821
|
const t0 = Date.now();
|
|
44822
|
+
if (job.requiredTools?.length) {
|
|
44823
|
+
const missing = await ensureConnectorTools(job.requiredTools);
|
|
44824
|
+
if (missing.length) log("[adapter]", ` connector tools still missing (agent may fail): ${missing.join(", ")}`);
|
|
44825
|
+
}
|
|
44619
44826
|
const sentinelWrappers = Object.entries(job.env ?? {}).filter(([k2]) => k2.endsWith("_WRAPPER")).map(([, v2]) => v2);
|
|
44620
44827
|
const wrapperPaths = [...job.wrapperPaths ?? [], ...sentinelWrappers];
|
|
44621
44828
|
const strippedEnv = Object.fromEntries(
|
|
@@ -44623,11 +44830,11 @@ var ConnectorAwareAdapter = class {
|
|
|
44623
44830
|
);
|
|
44624
44831
|
let extraEnv = { ...strippedEnv };
|
|
44625
44832
|
if (wrapperPaths.length > 0) {
|
|
44626
|
-
const wrapperBinDir = await (0,
|
|
44627
|
-
await (0,
|
|
44833
|
+
const wrapperBinDir = await (0, import_promises2.mkdtemp)((0, import_node_path5.join)((0, import_node_os2.tmpdir)(), "oasis-wrappers-"));
|
|
44834
|
+
await (0, import_promises2.mkdir)(wrapperBinDir, { recursive: true });
|
|
44628
44835
|
for (const wp of wrapperPaths) {
|
|
44629
|
-
const linkName = (0,
|
|
44630
|
-
await (0,
|
|
44836
|
+
const linkName = (0, import_node_path5.basename)(wp).replace(/\.sh$/, "");
|
|
44837
|
+
await (0, import_promises2.symlink)(wp, (0, import_node_path5.join)(wrapperBinDir, linkName));
|
|
44631
44838
|
}
|
|
44632
44839
|
extraEnv = { ...extraEnv, PATH: `${wrapperBinDir}:${process.env["PATH"] ?? ""}` };
|
|
44633
44840
|
log("[adapter]", ` connectors injected +${Date.now() - t0}ms wrappers=${wrapperPaths.length} env_keys=${Object.keys(job.env ?? {}).join(",")}`);
|
|
@@ -45156,6 +45363,9 @@ function defaultDocumentPath2(input) {
|
|
|
45156
45363
|
return `${domain}/${workOrder}/${input.artifactId}.md`;
|
|
45157
45364
|
}
|
|
45158
45365
|
|
|
45366
|
+
// ../storage/src/postgres.ts
|
|
45367
|
+
var import_node_crypto19 = require("node:crypto");
|
|
45368
|
+
|
|
45159
45369
|
// ../../node_modules/.pnpm/pg@8.21.0/node_modules/pg/esm/index.mjs
|
|
45160
45370
|
var import_lib = __toESM(require_lib2(), 1);
|
|
45161
45371
|
var Client = import_lib.default.Client;
|
|
@@ -45173,9 +45383,551 @@ var esm_default = import_lib.default;
|
|
|
45173
45383
|
|
|
45174
45384
|
// ../storage/src/postgres.ts
|
|
45175
45385
|
init_src();
|
|
45386
|
+
var ident = (s2) => {
|
|
45387
|
+
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema identifier: ${s2}`);
|
|
45388
|
+
return s2;
|
|
45389
|
+
};
|
|
45390
|
+
var isUniqueViolation = (err) => typeof err === "object" && err !== null && err.code === "23505";
|
|
45391
|
+
var PostgresOplogStore = class _PostgresOplogStore {
|
|
45392
|
+
constructor(pool, schema) {
|
|
45393
|
+
this.pool = pool;
|
|
45394
|
+
this.t = `"${ident(schema)}".oplog`;
|
|
45395
|
+
}
|
|
45396
|
+
t;
|
|
45397
|
+
static async open(pool, schema = "public") {
|
|
45398
|
+
const s2 = ident(schema);
|
|
45399
|
+
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
45400
|
+
await pool.query(`
|
|
45401
|
+
CREATE TABLE IF NOT EXISTS "${s2}".oplog (
|
|
45402
|
+
artifact_id text NOT NULL,
|
|
45403
|
+
seq integer NOT NULL,
|
|
45404
|
+
op jsonb NOT NULL,
|
|
45405
|
+
global_seq bigserial,
|
|
45406
|
+
appended_at timestamptz NOT NULL DEFAULT now(),
|
|
45407
|
+
PRIMARY KEY (artifact_id, seq)
|
|
45408
|
+
)`);
|
|
45409
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS oplog_global_idx ON "${s2}".oplog (global_seq)`);
|
|
45410
|
+
return new _PostgresOplogStore(pool, schema);
|
|
45411
|
+
}
|
|
45412
|
+
async append(artifactId, op, expectedSeq) {
|
|
45413
|
+
for (let attempt = 0; ; attempt++) {
|
|
45414
|
+
const target = expectedSeq ?? await this.nextSeq(artifactId);
|
|
45415
|
+
try {
|
|
45416
|
+
const res = await this.pool.query(
|
|
45417
|
+
`INSERT INTO ${this.t} (artifact_id, seq, op)
|
|
45418
|
+
SELECT $1, $2::int, $3::jsonb
|
|
45419
|
+
WHERE (SELECT COALESCE(MAX(seq), 0) + 1 FROM ${this.t} WHERE artifact_id = $1) = $2::int`,
|
|
45420
|
+
[artifactId, target, JSON.stringify(op)]
|
|
45421
|
+
);
|
|
45422
|
+
if (res.rowCount === 1) return target;
|
|
45423
|
+
} catch (err) {
|
|
45424
|
+
if (!isUniqueViolation(err)) throw err;
|
|
45425
|
+
}
|
|
45426
|
+
if (expectedSeq !== void 0) {
|
|
45427
|
+
throw new SeqConflictError(artifactId, expectedSeq, await this.nextSeq(artifactId));
|
|
45428
|
+
}
|
|
45429
|
+
if (attempt >= 5) throw new Error(`oplog append contention on ${artifactId}`);
|
|
45430
|
+
}
|
|
45431
|
+
}
|
|
45432
|
+
async nextSeq(artifactId) {
|
|
45433
|
+
const res = await this.pool.query(
|
|
45434
|
+
`SELECT COALESCE(MAX(seq), 0) + 1 AS next FROM ${this.t} WHERE artifact_id = $1`,
|
|
45435
|
+
[artifactId]
|
|
45436
|
+
);
|
|
45437
|
+
return Number(res.rows[0].next);
|
|
45438
|
+
}
|
|
45439
|
+
async read(artifactId, fromSeq = 1) {
|
|
45440
|
+
const res = await this.pool.query(
|
|
45441
|
+
`SELECT seq, op FROM ${this.t} WHERE artifact_id = $1 AND seq >= $2 ORDER BY seq`,
|
|
45442
|
+
[artifactId, fromSeq]
|
|
45443
|
+
);
|
|
45444
|
+
return res.rows.map((row) => ({ ...row.op, artifactId, seq: Number(row.seq) }));
|
|
45445
|
+
}
|
|
45446
|
+
async *readAll(from) {
|
|
45447
|
+
yield* this.scan(from, false);
|
|
45448
|
+
}
|
|
45449
|
+
async *subscribe(from) {
|
|
45450
|
+
yield* this.scan(from, true);
|
|
45451
|
+
}
|
|
45452
|
+
async *scan(from, follow) {
|
|
45453
|
+
let cursor = from ?? "0";
|
|
45454
|
+
try {
|
|
45455
|
+
for (; ; ) {
|
|
45456
|
+
const res = await this.pool.query(
|
|
45457
|
+
`SELECT artifact_id, seq, op, global_seq FROM ${this.t}
|
|
45458
|
+
WHERE global_seq > $1::bigint ORDER BY global_seq LIMIT 500`,
|
|
45459
|
+
[cursor]
|
|
45460
|
+
);
|
|
45461
|
+
if (res.rowCount === 0) {
|
|
45462
|
+
if (!follow) return;
|
|
45463
|
+
await new Promise((resolve3) => setTimeout(resolve3, 120));
|
|
45464
|
+
continue;
|
|
45465
|
+
}
|
|
45466
|
+
for (const row of res.rows) {
|
|
45467
|
+
cursor = String(row.global_seq);
|
|
45468
|
+
yield {
|
|
45469
|
+
op: { ...row.op, artifactId: row.artifact_id, seq: Number(row.seq) },
|
|
45470
|
+
cursor
|
|
45471
|
+
};
|
|
45472
|
+
}
|
|
45473
|
+
}
|
|
45474
|
+
} catch (err) {
|
|
45475
|
+
if (follow) return;
|
|
45476
|
+
throw err;
|
|
45477
|
+
}
|
|
45478
|
+
}
|
|
45479
|
+
};
|
|
45480
|
+
var PostgresBlobStore = class _PostgresBlobStore {
|
|
45481
|
+
constructor(pool, schema) {
|
|
45482
|
+
this.pool = pool;
|
|
45483
|
+
this.t = `"${ident(schema)}".blobs`;
|
|
45484
|
+
}
|
|
45485
|
+
t;
|
|
45486
|
+
static async open(pool, schema = "public") {
|
|
45487
|
+
const s2 = ident(schema);
|
|
45488
|
+
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
45489
|
+
await pool.query(`
|
|
45490
|
+
CREATE TABLE IF NOT EXISTS "${s2}".blobs (
|
|
45491
|
+
hash text PRIMARY KEY,
|
|
45492
|
+
bytes bytea NOT NULL,
|
|
45493
|
+
size bigint NOT NULL,
|
|
45494
|
+
content_type text, -- \u5199\u5165\u65F6\u6309\u5B57\u8282\u55C5\u63A2\uFF1B\u55C5\u4E0D\u51FA\u4E3A NULL
|
|
45495
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
45496
|
+
)`);
|
|
45497
|
+
await pool.query(`ALTER TABLE "${s2}".blobs ADD COLUMN IF NOT EXISTS content_type text`);
|
|
45498
|
+
await pool.query(`ALTER TABLE "${s2}".blobs ADD COLUMN IF NOT EXISTS created_at timestamptz NOT NULL DEFAULT now()`);
|
|
45499
|
+
return new _PostgresBlobStore(pool, schema);
|
|
45500
|
+
}
|
|
45501
|
+
async put(bytes) {
|
|
45502
|
+
const hash = (0, import_node_crypto19.createHash)("sha256").update(bytes).digest("hex");
|
|
45503
|
+
await this.pool.query(
|
|
45504
|
+
`INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
|
|
45505
|
+
[hash, Buffer.from(bytes), bytes.byteLength, sniffContentType(bytes) ?? null]
|
|
45506
|
+
);
|
|
45507
|
+
return hash;
|
|
45508
|
+
}
|
|
45509
|
+
async meta(hash) {
|
|
45510
|
+
const res = await this.pool.query(`SELECT size, content_type FROM ${this.t} WHERE hash = $1`, [hash]);
|
|
45511
|
+
const row = res.rows[0];
|
|
45512
|
+
if (!row) return null;
|
|
45513
|
+
return { size: Number(row.size), ...row.content_type ? { contentType: row.content_type } : {} };
|
|
45514
|
+
}
|
|
45515
|
+
async get(hash) {
|
|
45516
|
+
const res = await this.pool.query(`SELECT bytes FROM ${this.t} WHERE hash = $1`, [hash]);
|
|
45517
|
+
if (res.rowCount === 0) throw new BlobNotFoundError(hash);
|
|
45518
|
+
return new Uint8Array(res.rows[0].bytes);
|
|
45519
|
+
}
|
|
45520
|
+
async has(hash) {
|
|
45521
|
+
const res = await this.pool.query(`SELECT 1 FROM ${this.t} WHERE hash = $1`, [hash]);
|
|
45522
|
+
return res.rowCount === 1;
|
|
45523
|
+
}
|
|
45524
|
+
async sweep(reachable) {
|
|
45525
|
+
await this.pool.query(`DELETE FROM ${this.t} WHERE NOT (hash = ANY($1::text[]))`, [
|
|
45526
|
+
[...reachable]
|
|
45527
|
+
]);
|
|
45528
|
+
}
|
|
45529
|
+
};
|
|
45530
|
+
|
|
45531
|
+
// ../storage/src/postgres-registry.ts
|
|
45532
|
+
var ident2 = (s2) => {
|
|
45533
|
+
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
45534
|
+
return s2;
|
|
45535
|
+
};
|
|
45536
|
+
var PostgresRegistryStore = class _PostgresRegistryStore {
|
|
45537
|
+
constructor(pool, schema) {
|
|
45538
|
+
this.pool = pool;
|
|
45539
|
+
this.s = `"${ident2(schema)}"`;
|
|
45540
|
+
}
|
|
45541
|
+
s;
|
|
45542
|
+
static async open(pool, schema = "public") {
|
|
45543
|
+
const s2 = ident2(schema);
|
|
45544
|
+
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
45545
|
+
await pool.query(`
|
|
45546
|
+
CREATE TABLE IF NOT EXISTS "${s2}".actors (
|
|
45547
|
+
id text PRIMARY KEY, -- "actor:human:<slug>" / "actor:agent:<slug>"
|
|
45548
|
+
kind text NOT NULL, -- human | agent
|
|
45549
|
+
name text NOT NULL,
|
|
45550
|
+
email text,
|
|
45551
|
+
team_id text,
|
|
45552
|
+
status text NOT NULL, -- active | disabled\uFF08\u5220\u9664\u5373\u505C\u7528\uFF09
|
|
45553
|
+
roles jsonb NOT NULL DEFAULT '[]'::jsonb, -- spec \xA76.2 \u540D\u518C\u89D2\u8272\uFF08\u4EFB\u52A1 2.0\uFF09
|
|
45554
|
+
avatar text, -- "blob:<hash>"\uFF0C\u7A7A=\u9ED8\u8BA4\u751F\u6210
|
|
45555
|
+
created_at timestamptz NOT NULL
|
|
45556
|
+
)`);
|
|
45557
|
+
await pool.query(`
|
|
45558
|
+
CREATE TABLE IF NOT EXISTS "${s2}".actor_configs (
|
|
45559
|
+
actor_id text NOT NULL,
|
|
45560
|
+
version integer NOT NULL, -- 1 \u8D77\u9012\u589E\uFF1B\u53EA\u589E\u4E0D\u6539\uFF0C\u56DE\u6EDA=\u590D\u5236\u65E7\u7248\u4E3A\u65B0\u7248
|
|
45561
|
+
prompt text NOT NULL,
|
|
45562
|
+
model text NOT NULL,
|
|
45563
|
+
skills jsonb NOT NULL,
|
|
45564
|
+
connector_ids jsonb NOT NULL,
|
|
45565
|
+
reviewer_ids jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
45566
|
+
max_concurrent integer NOT NULL DEFAULT 4,
|
|
45567
|
+
created_by text NOT NULL,
|
|
45568
|
+
created_at timestamptz NOT NULL,
|
|
45569
|
+
source text NOT NULL, -- patch | rollback
|
|
45570
|
+
rolled_back_from integer,
|
|
45571
|
+
PRIMARY KEY (actor_id, version)
|
|
45572
|
+
)`);
|
|
45573
|
+
await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS max_concurrent integer NOT NULL DEFAULT 4`);
|
|
45574
|
+
await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS reviewer_ids jsonb NOT NULL DEFAULT '[]'::jsonb`);
|
|
45575
|
+
await pool.query(`
|
|
45576
|
+
CREATE TABLE IF NOT EXISTS "${s2}".teams (
|
|
45577
|
+
id text PRIMARY KEY,
|
|
45578
|
+
name text NOT NULL,
|
|
45579
|
+
parent_id text, -- \u7EC4\u7EC7\u6811
|
|
45580
|
+
lead_actor_id text,
|
|
45581
|
+
icon text -- "blob:<hash>"\uFF0C\u7A7A=\u9ED8\u8BA4\u751F\u6210\uFF08\u4E0D\u7528 emoji\uFF09
|
|
45582
|
+
)`);
|
|
45583
|
+
await pool.query(`
|
|
45584
|
+
CREATE TABLE IF NOT EXISTS "${s2}".actor_bindings (
|
|
45585
|
+
actor_id text PRIMARY KEY, -- \u4E00\u5458\u5DE5\u4E00\u7ED1\u5B9A\uFF08\u51B3\u7B56 0001\uFF09
|
|
45586
|
+
node_id text NOT NULL,
|
|
45587
|
+
runtime_kind text NOT NULL,
|
|
45588
|
+
status text NOT NULL -- active | paused
|
|
45589
|
+
)`);
|
|
45590
|
+
await pool.query(`
|
|
45591
|
+
CREATE TABLE IF NOT EXISTS "${s2}".connectors (
|
|
45592
|
+
id text PRIMARY KEY,
|
|
45593
|
+
name text NOT NULL,
|
|
45594
|
+
mode text NOT NULL, -- oauth | direct
|
|
45595
|
+
status text NOT NULL,
|
|
45596
|
+
account text,
|
|
45597
|
+
verified_at timestamptz
|
|
45598
|
+
)`);
|
|
45599
|
+
await pool.query(`
|
|
45600
|
+
CREATE TABLE IF NOT EXISTS "${s2}".skill_catalog (
|
|
45601
|
+
id text PRIMARY KEY, -- slug
|
|
45602
|
+
name text NOT NULL,
|
|
45603
|
+
description text NOT NULL,
|
|
45604
|
+
category text NOT NULL,
|
|
45605
|
+
maker text NOT NULL,
|
|
45606
|
+
tags jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
45607
|
+
installs integer NOT NULL DEFAULT 0, -- \u5E02\u573A\u70ED\u5EA6(\u5C55\u793A)
|
|
45608
|
+
created_at timestamptz NOT NULL
|
|
45609
|
+
)`);
|
|
45610
|
+
await pool.query(`
|
|
45611
|
+
CREATE TABLE IF NOT EXISTS "${s2}".installed_skills (
|
|
45612
|
+
id text PRIMARY KEY, -- \u4E0E\u76EE\u5F55\u540C slug;\u81EA\u5B9A\u4E49\u4E3A\u81EA\u8D77 slug
|
|
45613
|
+
name text NOT NULL,
|
|
45614
|
+
description text NOT NULL,
|
|
45615
|
+
source text NOT NULL, -- market | custom | github
|
|
45616
|
+
enabled boolean NOT NULL DEFAULT true,
|
|
45617
|
+
installed_by text NOT NULL,
|
|
45618
|
+
installed_at timestamptz NOT NULL,
|
|
45619
|
+
source_url text -- \u5916\u90E8\u6765\u6E90\u539F\u59CB URL\uFF08\u5BFC\u5165\u65F6\u8BBE\u7F6E\uFF09
|
|
45620
|
+
)`);
|
|
45621
|
+
await pool.query(`ALTER TABLE "${s2}".installed_skills ADD COLUMN IF NOT EXISTS applied_to_all boolean NOT NULL DEFAULT false`);
|
|
45622
|
+
await pool.query(`
|
|
45623
|
+
CREATE TABLE IF NOT EXISTS "${s2}".skill_files (
|
|
45624
|
+
skill_id text NOT NULL,
|
|
45625
|
+
path text NOT NULL, -- \u76F8\u5BF9\u8DEF\u5F84\uFF0C\u5982 "SKILL.md"
|
|
45626
|
+
blob_hash text NOT NULL, -- BlobStore \u54C8\u5E0C\uFF08\u4E0D\u542B "blob:" \u524D\u7F00\uFF09
|
|
45627
|
+
size integer NOT NULL,
|
|
45628
|
+
updated_at timestamptz NOT NULL,
|
|
45629
|
+
PRIMARY KEY (skill_id, path)
|
|
45630
|
+
)`);
|
|
45631
|
+
await pool.query(`
|
|
45632
|
+
CREATE TABLE IF NOT EXISTS "${s2}".variables (
|
|
45633
|
+
key text NOT NULL,
|
|
45634
|
+
scope text NOT NULL,
|
|
45635
|
+
actor_id text,
|
|
45636
|
+
connector_id text,
|
|
45637
|
+
overrides text,
|
|
45638
|
+
value_encrypted text NOT NULL,
|
|
45639
|
+
updated_at timestamptz NOT NULL
|
|
45640
|
+
)`);
|
|
45641
|
+
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS "${s2}_variables_key_actor_uq" ON "${s2}".variables (key, COALESCE(actor_id, ''))`);
|
|
45642
|
+
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS overrides text`);
|
|
45643
|
+
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS actor_id text`);
|
|
45644
|
+
return new _PostgresRegistryStore(pool, schema);
|
|
45645
|
+
}
|
|
45646
|
+
/* ---------- 员工 ---------- */
|
|
45647
|
+
async upsertActor(a) {
|
|
45648
|
+
await this.pool.query(
|
|
45649
|
+
`INSERT INTO ${this.s}.actors (id, kind, name, email, team_id, status, roles, avatar, created_at)
|
|
45650
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8,$9)
|
|
45651
|
+
ON CONFLICT (id) DO UPDATE SET kind=$2, name=$3, email=$4, team_id=$5, status=$6, roles=$7::jsonb, avatar=$8`,
|
|
45652
|
+
[a.id, a.kind, a.name, a.email ?? null, a.teamId ?? null, a.status, JSON.stringify(a.roles), a.avatar ?? null, a.createdAt]
|
|
45653
|
+
);
|
|
45654
|
+
}
|
|
45655
|
+
async getActor(id) {
|
|
45656
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.actors WHERE id = $1`, [id]);
|
|
45657
|
+
return r.rows[0] ? rowToActor(r.rows[0]) : null;
|
|
45658
|
+
}
|
|
45659
|
+
async listActors(filter) {
|
|
45660
|
+
const conds = [];
|
|
45661
|
+
const args = [];
|
|
45662
|
+
if (filter?.kind) {
|
|
45663
|
+
args.push(filter.kind);
|
|
45664
|
+
conds.push(`kind = $${args.length}`);
|
|
45665
|
+
}
|
|
45666
|
+
if (filter?.teamId) {
|
|
45667
|
+
args.push(filter.teamId);
|
|
45668
|
+
conds.push(`team_id = $${args.length}`);
|
|
45669
|
+
}
|
|
45670
|
+
if (filter?.status) {
|
|
45671
|
+
args.push(filter.status);
|
|
45672
|
+
conds.push(`status = $${args.length}`);
|
|
45673
|
+
}
|
|
45674
|
+
const where = conds.length ? `WHERE ${conds.join(" AND ")}` : "";
|
|
45675
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.actors ${where} ORDER BY created_at`, args);
|
|
45676
|
+
return r.rows.map(rowToActor);
|
|
45677
|
+
}
|
|
45678
|
+
/* ---------- 配置版本 ---------- */
|
|
45679
|
+
async appendConfig(c) {
|
|
45680
|
+
const r = await this.pool.query(
|
|
45681
|
+
`INSERT INTO ${this.s}.actor_configs
|
|
45682
|
+
(actor_id, version, prompt, model, skills, connector_ids, reviewer_ids, max_concurrent, created_by, created_at, source, rolled_back_from)
|
|
45683
|
+
SELECT $1,$2::int,$3,$4,$5::jsonb,$6::jsonb,$7::jsonb,$8::int,$9,$10,$11,$12
|
|
45684
|
+
WHERE (SELECT COALESCE(MAX(version), 0) + 1 FROM ${this.s}.actor_configs WHERE actor_id = $1) = $2::int`,
|
|
45685
|
+
[
|
|
45686
|
+
c.actorId,
|
|
45687
|
+
c.version,
|
|
45688
|
+
c.prompt,
|
|
45689
|
+
c.model,
|
|
45690
|
+
JSON.stringify(c.skills),
|
|
45691
|
+
JSON.stringify(c.connectorIds),
|
|
45692
|
+
JSON.stringify(c.reviewerIds ?? []),
|
|
45693
|
+
c.maxConcurrent,
|
|
45694
|
+
c.createdBy,
|
|
45695
|
+
c.createdAt,
|
|
45696
|
+
c.source,
|
|
45697
|
+
c.rolledBackFrom ?? null
|
|
45698
|
+
]
|
|
45699
|
+
);
|
|
45700
|
+
if (r.rowCount !== 1) throw new Error(`config version conflict for ${c.actorId}: got ${c.version}`);
|
|
45701
|
+
}
|
|
45702
|
+
async latestConfig(actorId) {
|
|
45703
|
+
const r = await this.pool.query(
|
|
45704
|
+
`SELECT * FROM ${this.s}.actor_configs WHERE actor_id = $1 ORDER BY version DESC LIMIT 1`,
|
|
45705
|
+
[actorId]
|
|
45706
|
+
);
|
|
45707
|
+
return r.rows[0] ? rowToConfig(r.rows[0]) : null;
|
|
45708
|
+
}
|
|
45709
|
+
async listConfigVersions(actorId) {
|
|
45710
|
+
const r = await this.pool.query(
|
|
45711
|
+
`SELECT * FROM ${this.s}.actor_configs WHERE actor_id = $1 ORDER BY version`,
|
|
45712
|
+
[actorId]
|
|
45713
|
+
);
|
|
45714
|
+
return r.rows.map(rowToConfig);
|
|
45715
|
+
}
|
|
45716
|
+
/* ---------- 团队 ---------- */
|
|
45717
|
+
async upsertTeam(t) {
|
|
45718
|
+
await this.pool.query(
|
|
45719
|
+
`INSERT INTO ${this.s}.teams (id, name, parent_id, lead_actor_id, icon) VALUES ($1,$2,$3,$4,$5)
|
|
45720
|
+
ON CONFLICT (id) DO UPDATE SET name=$2, parent_id=$3, lead_actor_id=$4, icon=$5`,
|
|
45721
|
+
[t.id, t.name, t.parentId ?? null, t.leadActorId ?? null, t.icon ?? null]
|
|
45722
|
+
);
|
|
45723
|
+
}
|
|
45724
|
+
async listTeams() {
|
|
45725
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.teams ORDER BY id`);
|
|
45726
|
+
return r.rows.map((row) => ({
|
|
45727
|
+
id: row.id,
|
|
45728
|
+
name: row.name,
|
|
45729
|
+
...row.parent_id !== null ? { parentId: row.parent_id } : {},
|
|
45730
|
+
...row.lead_actor_id !== null ? { leadActorId: row.lead_actor_id } : {},
|
|
45731
|
+
...row.icon !== null ? { icon: row.icon } : {}
|
|
45732
|
+
}));
|
|
45733
|
+
}
|
|
45734
|
+
/* ---------- 绑定 ---------- */
|
|
45735
|
+
async upsertBinding(b2) {
|
|
45736
|
+
await this.pool.query(
|
|
45737
|
+
`INSERT INTO ${this.s}.actor_bindings (actor_id, node_id, runtime_kind, status) VALUES ($1,$2,$3,$4)
|
|
45738
|
+
ON CONFLICT (actor_id) DO UPDATE SET node_id=$2, runtime_kind=$3, status=$4`,
|
|
45739
|
+
[b2.actorId, b2.nodeId, b2.runtimeKind, b2.status]
|
|
45740
|
+
);
|
|
45741
|
+
}
|
|
45742
|
+
async removeBinding(actorId) {
|
|
45743
|
+
await this.pool.query(`DELETE FROM ${this.s}.actor_bindings WHERE actor_id = $1`, [actorId]);
|
|
45744
|
+
}
|
|
45745
|
+
async getBinding(actorId) {
|
|
45746
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.actor_bindings WHERE actor_id = $1`, [actorId]);
|
|
45747
|
+
const row = r.rows[0];
|
|
45748
|
+
return row ? { actorId: row.actor_id, nodeId: row.node_id, runtimeKind: row.runtime_kind, status: row.status } : null;
|
|
45749
|
+
}
|
|
45750
|
+
async listBindings() {
|
|
45751
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.actor_bindings ORDER BY actor_id`);
|
|
45752
|
+
return r.rows.map((row) => ({ actorId: row.actor_id, nodeId: row.node_id, runtimeKind: row.runtime_kind, status: row.status }));
|
|
45753
|
+
}
|
|
45754
|
+
/* ---------- 连接器 ---------- */
|
|
45755
|
+
async upsertConnector(c) {
|
|
45756
|
+
await this.pool.query(
|
|
45757
|
+
`INSERT INTO ${this.s}.connectors (id, name, mode, status, account, verified_at) VALUES ($1,$2,$3,$4,$5,$6)
|
|
45758
|
+
ON CONFLICT (id) DO UPDATE SET name=$2, mode=$3, status=$4, account=$5, verified_at=$6`,
|
|
45759
|
+
[c.id, c.name, c.mode, c.status, c.account ?? null, c.verifiedAt ?? null]
|
|
45760
|
+
);
|
|
45761
|
+
}
|
|
45762
|
+
async listConnectors() {
|
|
45763
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.connectors ORDER BY id`);
|
|
45764
|
+
return r.rows.map((row) => ({
|
|
45765
|
+
id: row.id,
|
|
45766
|
+
name: row.name,
|
|
45767
|
+
mode: row.mode,
|
|
45768
|
+
status: row.status,
|
|
45769
|
+
...row.account !== null ? { account: row.account } : {},
|
|
45770
|
+
...row.verified_at !== null ? { verifiedAt: new Date(row.verified_at).toISOString() } : {}
|
|
45771
|
+
}));
|
|
45772
|
+
}
|
|
45773
|
+
/* ---------- 技能 ---------- */
|
|
45774
|
+
async upsertSkillCatalogItem(item) {
|
|
45775
|
+
await this.pool.query(
|
|
45776
|
+
`INSERT INTO ${this.s}.skill_catalog (id, name, description, category, maker, tags, installs, created_at)
|
|
45777
|
+
VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7,$8)
|
|
45778
|
+
ON CONFLICT (id) DO UPDATE SET name=$2, description=$3, category=$4, maker=$5, tags=$6::jsonb, installs=$7`,
|
|
45779
|
+
[item.id, item.name, item.description, item.category, item.maker, JSON.stringify(item.tags), item.installs, item.createdAt]
|
|
45780
|
+
);
|
|
45781
|
+
}
|
|
45782
|
+
async listSkillCatalog() {
|
|
45783
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.skill_catalog ORDER BY installs DESC`);
|
|
45784
|
+
return r.rows.map((row) => ({
|
|
45785
|
+
id: row.id,
|
|
45786
|
+
name: row.name,
|
|
45787
|
+
description: row.description,
|
|
45788
|
+
category: row.category,
|
|
45789
|
+
maker: row.maker,
|
|
45790
|
+
tags: row.tags,
|
|
45791
|
+
installs: row.installs,
|
|
45792
|
+
createdAt: new Date(row.created_at).toISOString()
|
|
45793
|
+
}));
|
|
45794
|
+
}
|
|
45795
|
+
async putInstalledSkill(x2) {
|
|
45796
|
+
await this.pool.query(
|
|
45797
|
+
`INSERT INTO ${this.s}.installed_skills (id, name, description, source, enabled, installed_by, installed_at, source_url, applied_to_all)
|
|
45798
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
|
|
45799
|
+
ON CONFLICT (id) DO UPDATE SET name=$2, description=$3, source=$4, enabled=$5, source_url=$8, applied_to_all=$9`,
|
|
45800
|
+
[x2.id, x2.name, x2.description, x2.source, x2.enabled, x2.installedBy, x2.installedAt, x2.sourceUrl ?? null, x2.appliedToAll ?? false]
|
|
45801
|
+
);
|
|
45802
|
+
}
|
|
45803
|
+
async removeInstalledSkill(id) {
|
|
45804
|
+
await this.pool.query(`DELETE FROM ${this.s}.installed_skills WHERE id = $1`, [id]);
|
|
45805
|
+
await this.pool.query(`DELETE FROM ${this.s}.skill_files WHERE skill_id = $1`, [id]);
|
|
45806
|
+
}
|
|
45807
|
+
async listInstalledSkills() {
|
|
45808
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.installed_skills ORDER BY installed_at`);
|
|
45809
|
+
return r.rows.map((row) => ({
|
|
45810
|
+
id: row.id,
|
|
45811
|
+
name: row.name,
|
|
45812
|
+
description: row.description,
|
|
45813
|
+
source: row.source,
|
|
45814
|
+
enabled: row.enabled,
|
|
45815
|
+
installedBy: row.installed_by,
|
|
45816
|
+
installedAt: new Date(row.installed_at).toISOString(),
|
|
45817
|
+
...row.source_url !== null ? { sourceUrl: row.source_url } : {},
|
|
45818
|
+
appliedToAll: !!row.applied_to_all
|
|
45819
|
+
}));
|
|
45820
|
+
}
|
|
45821
|
+
async putSkillFiles(skillId, files) {
|
|
45822
|
+
await this.pool.query(`DELETE FROM ${this.s}.skill_files WHERE skill_id = $1`, [skillId]);
|
|
45823
|
+
for (const f2 of files) {
|
|
45824
|
+
await this.pool.query(
|
|
45825
|
+
`INSERT INTO ${this.s}.skill_files (skill_id, path, blob_hash, size, updated_at)
|
|
45826
|
+
VALUES ($1,$2,$3,$4,$5)
|
|
45827
|
+
ON CONFLICT (skill_id, path) DO UPDATE SET blob_hash=$3, size=$4, updated_at=$5`,
|
|
45828
|
+
[skillId, f2.path, f2.blobHash, f2.size, f2.updatedAt]
|
|
45829
|
+
);
|
|
45830
|
+
}
|
|
45831
|
+
}
|
|
45832
|
+
async listSkillFiles(skillId) {
|
|
45833
|
+
const r = await this.pool.query(
|
|
45834
|
+
`SELECT * FROM ${this.s}.skill_files WHERE skill_id = $1 ORDER BY path`,
|
|
45835
|
+
[skillId]
|
|
45836
|
+
);
|
|
45837
|
+
return r.rows.map((row) => ({
|
|
45838
|
+
skillId: row.skill_id,
|
|
45839
|
+
path: row.path,
|
|
45840
|
+
blobHash: row.blob_hash,
|
|
45841
|
+
size: row.size,
|
|
45842
|
+
updatedAt: new Date(row.updated_at).toISOString()
|
|
45843
|
+
}));
|
|
45844
|
+
}
|
|
45845
|
+
async deleteSkillFiles(skillId) {
|
|
45846
|
+
await this.pool.query(`DELETE FROM ${this.s}.skill_files WHERE skill_id = $1`, [skillId]);
|
|
45847
|
+
}
|
|
45848
|
+
/* ---------- 变量 ---------- */
|
|
45849
|
+
async putVariable(v2) {
|
|
45850
|
+
const aid = v2.actorId ?? null;
|
|
45851
|
+
await this.pool.query(
|
|
45852
|
+
`INSERT INTO ${this.s}.variables (key, scope, actor_id, connector_id, overrides, value_encrypted, updated_at)
|
|
45853
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)
|
|
45854
|
+
ON CONFLICT (key, COALESCE(actor_id, '')) DO UPDATE
|
|
45855
|
+
SET scope=$2, actor_id=$3, connector_id=$4, overrides=$5, value_encrypted=$6, updated_at=$7`,
|
|
45856
|
+
[v2.key, v2.scope, aid, v2.connectorId ?? null, v2.overrides ?? null, v2.valueEncrypted, v2.updatedAt]
|
|
45857
|
+
);
|
|
45858
|
+
}
|
|
45859
|
+
async patchVariableValue(key, actorId, valueEncrypted, updatedAt) {
|
|
45860
|
+
const aid = actorId ?? null;
|
|
45861
|
+
await this.pool.query(
|
|
45862
|
+
`UPDATE ${this.s}.variables SET value_encrypted=$3, updated_at=$4
|
|
45863
|
+
WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')`,
|
|
45864
|
+
[key, aid, valueEncrypted, updatedAt]
|
|
45865
|
+
);
|
|
45866
|
+
}
|
|
45867
|
+
async getVariableCiphertext(key, actorId) {
|
|
45868
|
+
const aid = actorId ?? null;
|
|
45869
|
+
const r = await this.pool.query(
|
|
45870
|
+
`SELECT value_encrypted FROM ${this.s}.variables WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')`,
|
|
45871
|
+
[key, aid]
|
|
45872
|
+
);
|
|
45873
|
+
return r.rows[0]?.value_encrypted ?? null;
|
|
45874
|
+
}
|
|
45875
|
+
async listVariables(actorId) {
|
|
45876
|
+
let r;
|
|
45877
|
+
if (actorId !== void 0) {
|
|
45878
|
+
r = await this.pool.query(
|
|
45879
|
+
`SELECT * FROM ${this.s}.variables WHERE scope='global' OR actor_id=$1 ORDER BY key`,
|
|
45880
|
+
[actorId]
|
|
45881
|
+
);
|
|
45882
|
+
} else {
|
|
45883
|
+
r = await this.pool.query(`SELECT * FROM ${this.s}.variables ORDER BY key`);
|
|
45884
|
+
}
|
|
45885
|
+
return r.rows.map((row) => ({
|
|
45886
|
+
key: row.key,
|
|
45887
|
+
scope: row.scope === "connector" ? "global" : row.scope,
|
|
45888
|
+
...row.actor_id !== null && row.actor_id !== void 0 ? { actorId: row.actor_id } : {},
|
|
45889
|
+
...row.connector_id !== null ? { connectorId: row.connector_id } : {},
|
|
45890
|
+
...row.overrides !== null ? { overrides: row.overrides } : {},
|
|
45891
|
+
valueEncrypted: row.value_encrypted,
|
|
45892
|
+
updatedAt: new Date(row.updated_at).toISOString()
|
|
45893
|
+
}));
|
|
45894
|
+
}
|
|
45895
|
+
async deleteVariable(key, actorId) {
|
|
45896
|
+
const aid = actorId ?? null;
|
|
45897
|
+
await this.pool.query(
|
|
45898
|
+
`DELETE FROM ${this.s}.variables WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')`,
|
|
45899
|
+
[key, aid]
|
|
45900
|
+
);
|
|
45901
|
+
}
|
|
45902
|
+
};
|
|
45903
|
+
var rowToActor = (row) => ({
|
|
45904
|
+
id: row.id,
|
|
45905
|
+
kind: row.kind,
|
|
45906
|
+
name: row.name,
|
|
45907
|
+
...row.email !== null ? { email: row.email } : {},
|
|
45908
|
+
...row.team_id !== null ? { teamId: row.team_id } : {},
|
|
45909
|
+
status: row.status,
|
|
45910
|
+
roles: row.roles ?? [],
|
|
45911
|
+
...row.avatar !== null && row.avatar !== void 0 ? { avatar: row.avatar } : {},
|
|
45912
|
+
createdAt: new Date(row.created_at).toISOString()
|
|
45913
|
+
});
|
|
45914
|
+
var rowToConfig = (row) => ({
|
|
45915
|
+
actorId: row.actor_id,
|
|
45916
|
+
version: row.version,
|
|
45917
|
+
prompt: row.prompt,
|
|
45918
|
+
model: row.model,
|
|
45919
|
+
skills: row.skills,
|
|
45920
|
+
connectorIds: row.connector_ids,
|
|
45921
|
+
reviewerIds: row.reviewer_ids ?? [],
|
|
45922
|
+
maxConcurrent: row.max_concurrent ?? 4,
|
|
45923
|
+
createdBy: row.created_by,
|
|
45924
|
+
createdAt: new Date(row.created_at).toISOString(),
|
|
45925
|
+
source: row.source,
|
|
45926
|
+
...row.rolled_back_from !== null ? { rolledBackFrom: row.rolled_back_from } : {}
|
|
45927
|
+
});
|
|
45176
45928
|
|
|
45177
45929
|
// ../storage/src/postgres-projects.ts
|
|
45178
|
-
var
|
|
45930
|
+
var ident3 = (s2) => {
|
|
45179
45931
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
45180
45932
|
return s2;
|
|
45181
45933
|
};
|
|
@@ -45183,11 +45935,11 @@ var iso = (v2) => new Date(v2).toISOString();
|
|
|
45183
45935
|
var PostgresProjectStateStore = class _PostgresProjectStateStore {
|
|
45184
45936
|
constructor(pool, schema) {
|
|
45185
45937
|
this.pool = pool;
|
|
45186
|
-
this.s = `"${
|
|
45938
|
+
this.s = `"${ident3(schema)}"`;
|
|
45187
45939
|
}
|
|
45188
45940
|
s;
|
|
45189
45941
|
static async open(pool, schema = "public") {
|
|
45190
|
-
const s2 =
|
|
45942
|
+
const s2 = ident3(schema);
|
|
45191
45943
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
45192
45944
|
await pool.query(`
|
|
45193
45945
|
CREATE TABLE IF NOT EXISTS "${s2}".projects (
|
|
@@ -45373,11 +46125,11 @@ var rowToProjectMember = (row) => ({
|
|
|
45373
46125
|
var PostgresArtifactStateStore = class _PostgresArtifactStateStore {
|
|
45374
46126
|
constructor(pool, schema) {
|
|
45375
46127
|
this.pool = pool;
|
|
45376
|
-
this.s = `"${
|
|
46128
|
+
this.s = `"${ident3(schema)}"`;
|
|
45377
46129
|
}
|
|
45378
46130
|
s;
|
|
45379
46131
|
static async open(pool, schema = "public") {
|
|
45380
|
-
const s2 =
|
|
46132
|
+
const s2 = ident3(schema);
|
|
45381
46133
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
45382
46134
|
await pool.query(`
|
|
45383
46135
|
CREATE TABLE IF NOT EXISTS "${s2}".artifacts (
|
|
@@ -45953,20 +46705,285 @@ function createPgPool(dsn, opts) {
|
|
|
45953
46705
|
});
|
|
45954
46706
|
}
|
|
45955
46707
|
|
|
46708
|
+
// ../storage/src/postgres-control-plane.ts
|
|
46709
|
+
var ident4 = (s2) => {
|
|
46710
|
+
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
46711
|
+
return s2;
|
|
46712
|
+
};
|
|
46713
|
+
var PostgresControlPlaneStore = class _PostgresControlPlaneStore {
|
|
46714
|
+
constructor(pool, schema) {
|
|
46715
|
+
this.pool = pool;
|
|
46716
|
+
this.s = `"${ident4(schema)}"`;
|
|
46717
|
+
}
|
|
46718
|
+
s;
|
|
46719
|
+
static async open(pool, schema = "public") {
|
|
46720
|
+
const s2 = ident4(schema);
|
|
46721
|
+
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
46722
|
+
await pool.query(`
|
|
46723
|
+
CREATE TABLE IF NOT EXISTS "${s2}".companies (
|
|
46724
|
+
id text PRIMARY KEY,
|
|
46725
|
+
slug text NOT NULL UNIQUE, -- \u4E0D\u53EF\u6539\uFF0C\u8DEF\u7531\u952E
|
|
46726
|
+
name text NOT NULL,
|
|
46727
|
+
avatar text, -- "blob:<hash>"\uFF0C\u7A7A=\u9ED8\u8BA4
|
|
46728
|
+
status text NOT NULL, -- active | suspended
|
|
46729
|
+
created_at timestamptz NOT NULL
|
|
46730
|
+
)`);
|
|
46731
|
+
await pool.query(`
|
|
46732
|
+
CREATE TABLE IF NOT EXISTS "${s2}".company_members (
|
|
46733
|
+
company_id text NOT NULL,
|
|
46734
|
+
account_id text NOT NULL,
|
|
46735
|
+
actor_id text NOT NULL, -- \u8BE5\u8D26\u53F7\u5728\u672C\u516C\u53F8\u7684\u5458\u5DE5\uFF08\u8FDB oplog \u7F72\u540D\uFF09
|
|
46736
|
+
role text NOT NULL, -- owner | admin | member
|
|
46737
|
+
PRIMARY KEY (company_id, account_id)
|
|
46738
|
+
)`);
|
|
46739
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS company_members_account_idx ON "${s2}".company_members (account_id)`);
|
|
46740
|
+
await pool.query(`
|
|
46741
|
+
CREATE TABLE IF NOT EXISTS "${s2}".company_invitations (
|
|
46742
|
+
id text PRIMARY KEY,
|
|
46743
|
+
company_id text NOT NULL,
|
|
46744
|
+
email text NOT NULL,
|
|
46745
|
+
role text NOT NULL, -- admin | member\uFF08\u4E0D\u542B owner\uFF09
|
|
46746
|
+
status text NOT NULL, -- pending | accepted | declined | expired
|
|
46747
|
+
expires_at timestamptz NOT NULL
|
|
46748
|
+
)`);
|
|
46749
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS company_invitations_email_idx ON "${s2}".company_invitations (email)`);
|
|
46750
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS company_invitations_company_idx ON "${s2}".company_invitations (company_id)`);
|
|
46751
|
+
await pool.query(`
|
|
46752
|
+
CREATE TABLE IF NOT EXISTS "${s2}".accounts (
|
|
46753
|
+
id text PRIMARY KEY,
|
|
46754
|
+
email text NOT NULL,
|
|
46755
|
+
name text NOT NULL,
|
|
46756
|
+
avatar text,
|
|
46757
|
+
status text NOT NULL, -- active | disabled
|
|
46758
|
+
password_hash text, -- scrypt \u4E32\uFF1BNULL=\u672A\u8BBE\u5BC6\u7801\uFF08\u4EC5\u9A8C\u8BC1\u7801\u767B\u5F55\uFF09\u3002\u7EDD\u4E0D\u7ECF HTTP \u8FD4\u56DE
|
|
46759
|
+
token_version int NOT NULL DEFAULT 0 -- \u4F1A\u8BDD\u603B\u95F8\uFF1Abump \u5373\u4F5C\u5E9F\u8BE5\u8D26\u53F7\u6240\u6709 oasis_auth
|
|
46760
|
+
)`);
|
|
46761
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS accounts_email_idx ON "${s2}".accounts (email)`);
|
|
46762
|
+
await pool.query(`
|
|
46763
|
+
CREATE TABLE IF NOT EXISTS "${s2}".verification_codes (
|
|
46764
|
+
email text PRIMARY KEY, -- \u5355\u90AE\u7BB1\u4E00\u6761 active
|
|
46765
|
+
code_hash text NOT NULL,
|
|
46766
|
+
created_at timestamptz NOT NULL,
|
|
46767
|
+
expires_at timestamptz NOT NULL,
|
|
46768
|
+
attempts int NOT NULL DEFAULT 0
|
|
46769
|
+
)`);
|
|
46770
|
+
return new _PostgresControlPlaneStore(pool, schema);
|
|
46771
|
+
}
|
|
46772
|
+
// —— 公司 ——
|
|
46773
|
+
async createCompany(c) {
|
|
46774
|
+
await this.pool.query(
|
|
46775
|
+
`INSERT INTO ${this.s}.companies (id, slug, name, avatar, status, created_at) VALUES ($1,$2,$3,$4,$5,$6)`,
|
|
46776
|
+
[c.id, c.slug, c.name, c.avatar ?? null, c.status, c.createdAt]
|
|
46777
|
+
);
|
|
46778
|
+
}
|
|
46779
|
+
async getCompany(id) {
|
|
46780
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.companies WHERE id = $1`, [id]);
|
|
46781
|
+
return r.rows[0] ? rowToCompany(r.rows[0]) : null;
|
|
46782
|
+
}
|
|
46783
|
+
async getCompanyBySlug(slug4) {
|
|
46784
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.companies WHERE slug = $1`, [slug4]);
|
|
46785
|
+
return r.rows[0] ? rowToCompany(r.rows[0]) : null;
|
|
46786
|
+
}
|
|
46787
|
+
async listCompanies() {
|
|
46788
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.companies ORDER BY created_at`);
|
|
46789
|
+
return r.rows.map(rowToCompany);
|
|
46790
|
+
}
|
|
46791
|
+
async updateCompany(id, patch) {
|
|
46792
|
+
const sets = [];
|
|
46793
|
+
const args = [];
|
|
46794
|
+
if (patch.name !== void 0) {
|
|
46795
|
+
args.push(patch.name);
|
|
46796
|
+
sets.push(`name = $${args.length}`);
|
|
46797
|
+
}
|
|
46798
|
+
if (patch.avatar !== void 0) {
|
|
46799
|
+
args.push(patch.avatar);
|
|
46800
|
+
sets.push(`avatar = $${args.length}`);
|
|
46801
|
+
}
|
|
46802
|
+
if (patch.status !== void 0) {
|
|
46803
|
+
args.push(patch.status);
|
|
46804
|
+
sets.push(`status = $${args.length}`);
|
|
46805
|
+
}
|
|
46806
|
+
args.push(id);
|
|
46807
|
+
const sql = sets.length ? `UPDATE ${this.s}.companies SET ${sets.join(", ")} WHERE id = $${args.length}` : `SELECT 1 FROM ${this.s}.companies WHERE id = $${args.length}`;
|
|
46808
|
+
const r = await this.pool.query(sql, args);
|
|
46809
|
+
if (r.rowCount === 0) throw new Error(`company not found: ${id}`);
|
|
46810
|
+
}
|
|
46811
|
+
// —— 成员 ——
|
|
46812
|
+
async upsertMember(m2) {
|
|
46813
|
+
await this.pool.query(
|
|
46814
|
+
`INSERT INTO ${this.s}.company_members (company_id, account_id, actor_id, role) VALUES ($1,$2,$3,$4)
|
|
46815
|
+
ON CONFLICT (company_id, account_id) DO UPDATE SET actor_id = $3, role = $4`,
|
|
46816
|
+
[m2.companyId, m2.accountId, m2.actorId, m2.role]
|
|
46817
|
+
);
|
|
46818
|
+
}
|
|
46819
|
+
async getMember(companyId, accountId) {
|
|
46820
|
+
const r = await this.pool.query(
|
|
46821
|
+
`SELECT * FROM ${this.s}.company_members WHERE company_id = $1 AND account_id = $2`,
|
|
46822
|
+
[companyId, accountId]
|
|
46823
|
+
);
|
|
46824
|
+
return r.rows[0] ? rowToMember(r.rows[0]) : null;
|
|
46825
|
+
}
|
|
46826
|
+
async listMembers(companyId) {
|
|
46827
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.company_members WHERE company_id = $1`, [companyId]);
|
|
46828
|
+
return r.rows.map(rowToMember);
|
|
46829
|
+
}
|
|
46830
|
+
async listMembershipsForAccount(accountId) {
|
|
46831
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.company_members WHERE account_id = $1`, [accountId]);
|
|
46832
|
+
return r.rows.map(rowToMember);
|
|
46833
|
+
}
|
|
46834
|
+
async removeMember(companyId, accountId) {
|
|
46835
|
+
await this.pool.query(`DELETE FROM ${this.s}.company_members WHERE company_id = $1 AND account_id = $2`, [companyId, accountId]);
|
|
46836
|
+
}
|
|
46837
|
+
// —— 邀请 ——
|
|
46838
|
+
async createInvitation(inv) {
|
|
46839
|
+
await this.pool.query(
|
|
46840
|
+
`INSERT INTO ${this.s}.company_invitations (id, company_id, email, role, status, expires_at) VALUES ($1,$2,$3,$4,$5,$6)`,
|
|
46841
|
+
[inv.id, inv.companyId, inv.email, inv.role, inv.status, inv.expiresAt]
|
|
46842
|
+
);
|
|
46843
|
+
}
|
|
46844
|
+
async getInvitation(id) {
|
|
46845
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.company_invitations WHERE id = $1`, [id]);
|
|
46846
|
+
return r.rows[0] ? rowToInvitation(r.rows[0]) : null;
|
|
46847
|
+
}
|
|
46848
|
+
async listInvitations(filter) {
|
|
46849
|
+
const conds = [];
|
|
46850
|
+
const args = [];
|
|
46851
|
+
if (filter?.companyId !== void 0) {
|
|
46852
|
+
args.push(filter.companyId);
|
|
46853
|
+
conds.push(`company_id = $${args.length}`);
|
|
46854
|
+
}
|
|
46855
|
+
if (filter?.email !== void 0) {
|
|
46856
|
+
args.push(filter.email);
|
|
46857
|
+
conds.push(`email = $${args.length}`);
|
|
46858
|
+
}
|
|
46859
|
+
if (filter?.status !== void 0) {
|
|
46860
|
+
args.push(filter.status);
|
|
46861
|
+
conds.push(`status = $${args.length}`);
|
|
46862
|
+
}
|
|
46863
|
+
const where = conds.length ? `WHERE ${conds.join(" AND ")}` : "";
|
|
46864
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.company_invitations ${where} ORDER BY id`, args);
|
|
46865
|
+
return r.rows.map(rowToInvitation);
|
|
46866
|
+
}
|
|
46867
|
+
async updateInvitationStatus(id, status) {
|
|
46868
|
+
const r = await this.pool.query(`UPDATE ${this.s}.company_invitations SET status = $2 WHERE id = $1`, [id, status]);
|
|
46869
|
+
if (r.rowCount === 0) throw new Error(`invitation not found: ${id}`);
|
|
46870
|
+
}
|
|
46871
|
+
// —— 账号 ——
|
|
46872
|
+
async upsertAccount(a) {
|
|
46873
|
+
await this.pool.query(
|
|
46874
|
+
`INSERT INTO ${this.s}.accounts (id, email, name, avatar, status) VALUES ($1,$2,$3,$4,$5)
|
|
46875
|
+
ON CONFLICT (id) DO UPDATE SET email = $2, name = $3, avatar = $4, status = $5`,
|
|
46876
|
+
[a.id, a.email, a.name, a.avatar ?? null, a.status]
|
|
46877
|
+
);
|
|
46878
|
+
}
|
|
46879
|
+
async getAccount(id) {
|
|
46880
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.accounts WHERE id = $1`, [id]);
|
|
46881
|
+
return r.rows[0] ? rowToAccount(r.rows[0]) : null;
|
|
46882
|
+
}
|
|
46883
|
+
async getAccountByEmail(email2) {
|
|
46884
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.accounts WHERE email = $1 LIMIT 1`, [email2]);
|
|
46885
|
+
return r.rows[0] ? rowToAccount(r.rows[0]) : null;
|
|
46886
|
+
}
|
|
46887
|
+
async listAccounts() {
|
|
46888
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.accounts ORDER BY id`);
|
|
46889
|
+
return r.rows.map(rowToAccount);
|
|
46890
|
+
}
|
|
46891
|
+
// —— 账号密钥(绝不经 HTTP 返回)——
|
|
46892
|
+
async setAccountPassword(accountId, passwordHash) {
|
|
46893
|
+
const r = await this.pool.query(`UPDATE ${this.s}.accounts SET password_hash = $2 WHERE id = $1`, [accountId, passwordHash]);
|
|
46894
|
+
if (r.rowCount === 0) throw new Error(`account not found: ${accountId}`);
|
|
46895
|
+
}
|
|
46896
|
+
async getAccountSecret(accountId) {
|
|
46897
|
+
const r = await this.pool.query(`SELECT password_hash, token_version FROM ${this.s}.accounts WHERE id = $1`, [accountId]);
|
|
46898
|
+
const row = r.rows[0];
|
|
46899
|
+
if (!row) return null;
|
|
46900
|
+
return {
|
|
46901
|
+
tokenVersion: Number(row.token_version),
|
|
46902
|
+
...row.password_hash !== null && row.password_hash !== void 0 ? { passwordHash: row.password_hash } : {}
|
|
46903
|
+
};
|
|
46904
|
+
}
|
|
46905
|
+
async bumpAccountTokenVersion(accountId) {
|
|
46906
|
+
const r = await this.pool.query(
|
|
46907
|
+
`UPDATE ${this.s}.accounts SET token_version = token_version + 1 WHERE id = $1 RETURNING token_version`,
|
|
46908
|
+
[accountId]
|
|
46909
|
+
);
|
|
46910
|
+
if (r.rowCount === 0) throw new Error(`account not found: ${accountId}`);
|
|
46911
|
+
return Number(r.rows[0].token_version);
|
|
46912
|
+
}
|
|
46913
|
+
// —— 邮箱验证码(单邮箱一条 active)——
|
|
46914
|
+
async putVerificationCode(rec) {
|
|
46915
|
+
await this.pool.query(
|
|
46916
|
+
`INSERT INTO ${this.s}.verification_codes (email, code_hash, created_at, expires_at, attempts) VALUES ($1,$2,$3,$4,$5)
|
|
46917
|
+
ON CONFLICT (email) DO UPDATE SET code_hash = $2, created_at = $3, expires_at = $4, attempts = $5`,
|
|
46918
|
+
[rec.email, rec.codeHash, rec.createdAt, rec.expiresAt, rec.attempts]
|
|
46919
|
+
);
|
|
46920
|
+
}
|
|
46921
|
+
async getVerificationCode(email2) {
|
|
46922
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.verification_codes WHERE email = $1`, [email2]);
|
|
46923
|
+
return r.rows[0] ? rowToVerificationCode(r.rows[0]) : null;
|
|
46924
|
+
}
|
|
46925
|
+
async incrementCodeAttempts(email2) {
|
|
46926
|
+
const r = await this.pool.query(
|
|
46927
|
+
`UPDATE ${this.s}.verification_codes SET attempts = attempts + 1 WHERE email = $1 RETURNING attempts`,
|
|
46928
|
+
[email2]
|
|
46929
|
+
);
|
|
46930
|
+
return r.rows[0] ? Number(r.rows[0].attempts) : 0;
|
|
46931
|
+
}
|
|
46932
|
+
async deleteVerificationCode(email2) {
|
|
46933
|
+
await this.pool.query(`DELETE FROM ${this.s}.verification_codes WHERE email = $1`, [email2]);
|
|
46934
|
+
}
|
|
46935
|
+
};
|
|
46936
|
+
var rowToCompany = (row) => ({
|
|
46937
|
+
id: row.id,
|
|
46938
|
+
slug: row.slug,
|
|
46939
|
+
name: row.name,
|
|
46940
|
+
...row.avatar !== null && row.avatar !== void 0 ? { avatar: row.avatar } : {},
|
|
46941
|
+
status: row.status,
|
|
46942
|
+
createdAt: new Date(row.created_at).toISOString()
|
|
46943
|
+
});
|
|
46944
|
+
var rowToMember = (row) => ({
|
|
46945
|
+
companyId: row.company_id,
|
|
46946
|
+
accountId: row.account_id,
|
|
46947
|
+
actorId: row.actor_id,
|
|
46948
|
+
role: row.role
|
|
46949
|
+
});
|
|
46950
|
+
var rowToInvitation = (row) => ({
|
|
46951
|
+
id: row.id,
|
|
46952
|
+
companyId: row.company_id,
|
|
46953
|
+
email: row.email,
|
|
46954
|
+
role: row.role,
|
|
46955
|
+
status: row.status,
|
|
46956
|
+
expiresAt: new Date(row.expires_at).toISOString()
|
|
46957
|
+
});
|
|
46958
|
+
var rowToAccount = (row) => ({
|
|
46959
|
+
id: row.id,
|
|
46960
|
+
email: row.email,
|
|
46961
|
+
name: row.name,
|
|
46962
|
+
...row.avatar !== null && row.avatar !== void 0 ? { avatar: row.avatar } : {},
|
|
46963
|
+
status: row.status
|
|
46964
|
+
});
|
|
46965
|
+
var rowToVerificationCode = (row) => ({
|
|
46966
|
+
email: row.email,
|
|
46967
|
+
codeHash: row.code_hash,
|
|
46968
|
+
createdAt: new Date(row.created_at).toISOString(),
|
|
46969
|
+
expiresAt: new Date(row.expires_at).toISOString(),
|
|
46970
|
+
attempts: Number(row.attempts)
|
|
46971
|
+
});
|
|
46972
|
+
|
|
45956
46973
|
// ../storage/src/postgres-trace.ts
|
|
45957
46974
|
init_src();
|
|
45958
|
-
var
|
|
46975
|
+
var ident5 = (s2) => {
|
|
45959
46976
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
45960
46977
|
return s2;
|
|
45961
46978
|
};
|
|
45962
46979
|
var PostgresTraceStore = class _PostgresTraceStore {
|
|
45963
46980
|
constructor(pool, schema) {
|
|
45964
46981
|
this.pool = pool;
|
|
45965
|
-
this.s = `"${
|
|
46982
|
+
this.s = `"${ident5(schema)}"`;
|
|
45966
46983
|
}
|
|
45967
46984
|
s;
|
|
45968
46985
|
static async open(pool, schema = "public") {
|
|
45969
|
-
const s2 =
|
|
46986
|
+
const s2 = ident5(schema);
|
|
45970
46987
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
45971
46988
|
await pool.query(`
|
|
45972
46989
|
CREATE TABLE IF NOT EXISTS "${s2}".agent_runs (
|
|
@@ -46518,7 +47535,7 @@ var rowToArtifact2 = (row) => ({
|
|
|
46518
47535
|
|
|
46519
47536
|
// ../storage/src/postgres-automations.ts
|
|
46520
47537
|
init_src();
|
|
46521
|
-
var
|
|
47538
|
+
var ident6 = (s2) => {
|
|
46522
47539
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
46523
47540
|
return s2;
|
|
46524
47541
|
};
|
|
@@ -46526,11 +47543,11 @@ var genId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 12)}`;
|
|
|
46526
47543
|
var PostgresAutomationStore = class _PostgresAutomationStore {
|
|
46527
47544
|
constructor(pool, schema) {
|
|
46528
47545
|
this.pool = pool;
|
|
46529
|
-
this.s = `"${
|
|
47546
|
+
this.s = `"${ident6(schema)}"`;
|
|
46530
47547
|
}
|
|
46531
47548
|
s;
|
|
46532
47549
|
static async open(pool, schema = "public") {
|
|
46533
|
-
const s2 =
|
|
47550
|
+
const s2 = ident6(schema);
|
|
46534
47551
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
46535
47552
|
await pool.query(`
|
|
46536
47553
|
CREATE TABLE IF NOT EXISTS "${s2}".automations (
|
|
@@ -46825,18 +47842,18 @@ var rowToRun2 = (row) => ({
|
|
|
46825
47842
|
});
|
|
46826
47843
|
|
|
46827
47844
|
// ../storage/src/postgres-chat-sessions.ts
|
|
46828
|
-
var
|
|
47845
|
+
var ident7 = (s2) => {
|
|
46829
47846
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
46830
47847
|
return s2;
|
|
46831
47848
|
};
|
|
46832
47849
|
var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
46833
47850
|
constructor(pool, schema) {
|
|
46834
47851
|
this.pool = pool;
|
|
46835
|
-
this.s = `"${
|
|
47852
|
+
this.s = `"${ident7(schema)}"`;
|
|
46836
47853
|
}
|
|
46837
47854
|
s;
|
|
46838
47855
|
static async open(pool, schema = "public") {
|
|
46839
|
-
const s2 =
|
|
47856
|
+
const s2 = ident7(schema);
|
|
46840
47857
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
46841
47858
|
await pool.query(`
|
|
46842
47859
|
CREATE TABLE IF NOT EXISTS "${s2}".chat_sessions (
|
|
@@ -46862,6 +47879,14 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
46862
47879
|
)`);
|
|
46863
47880
|
await pool.query(`CREATE INDEX IF NOT EXISTS chat_messages_session_idx ON "${s2}".chat_messages (session_id, seq)`);
|
|
46864
47881
|
await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS run_id text`);
|
|
47882
|
+
await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS parts jsonb`);
|
|
47883
|
+
await pool.query(`
|
|
47884
|
+
WITH ranked AS (
|
|
47885
|
+
SELECT id, row_number() OVER (PARTITION BY session_id ORDER BY seq, created_at, id) AS rn
|
|
47886
|
+
FROM "${s2}".chat_messages
|
|
47887
|
+
)
|
|
47888
|
+
UPDATE "${s2}".chat_messages m SET seq = ranked.rn FROM ranked WHERE m.id = ranked.id AND m.seq <> ranked.rn`);
|
|
47889
|
+
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS chat_messages_session_seq_uq ON "${s2}".chat_messages (session_id, seq)`);
|
|
46865
47890
|
await pool.query(`
|
|
46866
47891
|
CREATE TABLE IF NOT EXISTS "${s2}".chat_session_work_orders (
|
|
46867
47892
|
session_id text NOT NULL,
|
|
@@ -46917,11 +47942,25 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
46917
47942
|
await this.pool.query(`DELETE FROM ${this.s}.chat_sessions WHERE id = $1`, [id]);
|
|
46918
47943
|
}
|
|
46919
47944
|
async appendMessage(m2) {
|
|
46920
|
-
|
|
46921
|
-
|
|
46922
|
-
|
|
46923
|
-
|
|
46924
|
-
|
|
47945
|
+
const insert = async () => {
|
|
47946
|
+
const r = await this.pool.query(
|
|
47947
|
+
`INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id, parts)
|
|
47948
|
+
SELECT $1,$2,$3,$4, COALESCE(MAX(seq),0)+1, $5,$6,$7
|
|
47949
|
+
FROM ${this.s}.chat_messages WHERE session_id = $2
|
|
47950
|
+
RETURNING seq`,
|
|
47951
|
+
[m2.id, m2.sessionId, m2.role, m2.content, m2.createdAt, m2.runId ?? null, m2.parts !== void 0 ? JSON.stringify(m2.parts) : null]
|
|
47952
|
+
);
|
|
47953
|
+
return Number(r.rows[0].seq);
|
|
47954
|
+
};
|
|
47955
|
+
for (let attempt = 0; ; attempt++) {
|
|
47956
|
+
try {
|
|
47957
|
+
return { ...m2, seq: await insert() };
|
|
47958
|
+
} catch (err) {
|
|
47959
|
+
const isUniqueViolation2 = err && typeof err === "object" && err.code === "23505";
|
|
47960
|
+
if (isUniqueViolation2 && attempt < 10) continue;
|
|
47961
|
+
throw err;
|
|
47962
|
+
}
|
|
47963
|
+
}
|
|
46925
47964
|
}
|
|
46926
47965
|
async listMessages(sessionId, limit) {
|
|
46927
47966
|
const limitClause = limit ? `LIMIT ${limit}` : "";
|
|
@@ -46963,23 +48002,24 @@ var rowToMessage = (row) => ({
|
|
|
46963
48002
|
content: row.content,
|
|
46964
48003
|
seq: Number(row.seq),
|
|
46965
48004
|
createdAt: new Date(row.created_at).toISOString(),
|
|
46966
|
-
...row.run_id != null ? { runId: row.run_id } : {}
|
|
48005
|
+
...row.run_id != null ? { runId: row.run_id } : {},
|
|
48006
|
+
...row.parts != null ? { parts: row.parts } : {}
|
|
46967
48007
|
});
|
|
46968
48008
|
|
|
46969
48009
|
// ../storage/src/postgres-nodes.ts
|
|
46970
|
-
var
|
|
46971
|
-
var
|
|
48010
|
+
var import_node_crypto20 = require("node:crypto");
|
|
48011
|
+
var ident8 = (s2) => {
|
|
46972
48012
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
46973
48013
|
return s2;
|
|
46974
48014
|
};
|
|
46975
48015
|
var PostgresNodeStore = class _PostgresNodeStore {
|
|
46976
48016
|
constructor(pool, schema) {
|
|
46977
48017
|
this.pool = pool;
|
|
46978
|
-
this.s = `"${
|
|
48018
|
+
this.s = `"${ident8(schema)}"`;
|
|
46979
48019
|
}
|
|
46980
48020
|
s;
|
|
46981
48021
|
static async open(pool, schema = "public") {
|
|
46982
|
-
const s2 =
|
|
48022
|
+
const s2 = ident8(schema);
|
|
46983
48023
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
46984
48024
|
await pool.query(`
|
|
46985
48025
|
CREATE TABLE IF NOT EXISTS "${s2}".nodes (
|
|
@@ -47086,12 +48126,12 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
47086
48126
|
var PostgresNodeTokenStore = class _PostgresNodeTokenStore {
|
|
47087
48127
|
constructor(pool, schema) {
|
|
47088
48128
|
this.pool = pool;
|
|
47089
|
-
this.s = `"${
|
|
48129
|
+
this.s = `"${ident8(schema)}"`;
|
|
47090
48130
|
}
|
|
47091
48131
|
s;
|
|
47092
48132
|
cache = /* @__PURE__ */ new Map();
|
|
47093
48133
|
static async open(pool, schema = "public") {
|
|
47094
|
-
const s2 =
|
|
48134
|
+
const s2 = ident8(schema);
|
|
47095
48135
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
47096
48136
|
await pool.query(`CREATE TABLE IF NOT EXISTS "${s2}".node_tokens (token text PRIMARY KEY, node_id text NOT NULL)`);
|
|
47097
48137
|
const store = new _PostgresNodeTokenStore(pool, schema);
|
|
@@ -47100,7 +48140,7 @@ var PostgresNodeTokenStore = class _PostgresNodeTokenStore {
|
|
|
47100
48140
|
return store;
|
|
47101
48141
|
}
|
|
47102
48142
|
issue(nodeId) {
|
|
47103
|
-
const token = `ont_${(0,
|
|
48143
|
+
const token = `ont_${(0, import_node_crypto20.randomBytes)(24).toString("base64url")}`;
|
|
47104
48144
|
this.cache.set(token, nodeId);
|
|
47105
48145
|
void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token, nodeId]);
|
|
47106
48146
|
return token;
|
|
@@ -47139,7 +48179,16 @@ var rowToRuntime = (row) => ({
|
|
|
47139
48179
|
// ../cli/src/serve.ts
|
|
47140
48180
|
init_src3();
|
|
47141
48181
|
init_trajectory();
|
|
47142
|
-
|
|
48182
|
+
function builtinSkillsDir() {
|
|
48183
|
+
const override = process.env["OASIS_BUILTIN_SKILLS_DIR"]?.trim();
|
|
48184
|
+
if (override) return override;
|
|
48185
|
+
const repoRoot = (0, import_node_url3.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
|
|
48186
|
+
return path11.join(repoRoot, "skills");
|
|
48187
|
+
}
|
|
48188
|
+
function materializeBuiltins(builtins, runtimeKind) {
|
|
48189
|
+
if (builtins.length === 0) return {};
|
|
48190
|
+
return materializeBuiltinSkills(builtins, skillsDirForRuntime(runtimeKind));
|
|
48191
|
+
}
|
|
47143
48192
|
var SESSION_TOKEN_GRACE_MS = 15 * 6e4;
|
|
47144
48193
|
function normalizeHttpBaseUrl(raw) {
|
|
47145
48194
|
const url = new URL(raw);
|
|
@@ -47195,13 +48244,16 @@ async function buildActorProvision(service, actorId) {
|
|
|
47195
48244
|
const raw = await service.resolveActorEnv(actorId);
|
|
47196
48245
|
const env = { ...raw };
|
|
47197
48246
|
const wrapperPaths = [oasisWrapperScript()];
|
|
48247
|
+
const requiredTools = /* @__PURE__ */ new Set();
|
|
47198
48248
|
if (raw["FEISHU_APP_ID"] && raw["FEISHU_APP_SECRET"]) {
|
|
47199
48249
|
try {
|
|
47200
48250
|
const feishu = new FeishuConnector();
|
|
47201
48251
|
const sessionEnv = /* @__PURE__ */ new Map();
|
|
47202
48252
|
await feishu.inject({ app_id: raw["FEISHU_APP_ID"], app_secret: raw["FEISHU_APP_SECRET"] }, sessionEnv);
|
|
47203
48253
|
for (const [k2, v2] of sessionEnv) env[k2] = v2;
|
|
47204
|
-
|
|
48254
|
+
const staged = stageConnectorWrapper(feishu);
|
|
48255
|
+
if (staged) wrapperPaths.push(staged);
|
|
48256
|
+
for (const c of feishu.requiredCommands) requiredTools.add(c);
|
|
47205
48257
|
} catch (err) {
|
|
47206
48258
|
console.warn(`[serve] feishu connector setup failed for ${actorId}:`, String(err));
|
|
47207
48259
|
}
|
|
@@ -47213,13 +48265,14 @@ async function buildActorProvision(service, actorId) {
|
|
|
47213
48265
|
await github.inject({ access_token: raw["GITHUB_TOKEN"], actor_name: raw["GIT_AUTHOR_NAME"] ?? actorId.split(":").pop() ?? "oasis-agent" }, sessionEnv);
|
|
47214
48266
|
for (const [k2, v2] of sessionEnv) env[k2] = v2;
|
|
47215
48267
|
delete env["GITHUB_TOKEN"];
|
|
47216
|
-
const
|
|
47217
|
-
if (
|
|
48268
|
+
const staged = stageConnectorWrapper(github);
|
|
48269
|
+
if (staged) wrapperPaths.push(staged);
|
|
48270
|
+
for (const c of github.requiredCommands) requiredTools.add(c);
|
|
47218
48271
|
} catch (err) {
|
|
47219
48272
|
console.warn(`[serve] github connector setup failed for ${actorId}:`, String(err));
|
|
47220
48273
|
}
|
|
47221
48274
|
}
|
|
47222
|
-
return { env, wrapperPaths };
|
|
48275
|
+
return { env, wrapperPaths, requiredTools: [...requiredTools] };
|
|
47223
48276
|
}
|
|
47224
48277
|
async function buildActorContext(service, actorId) {
|
|
47225
48278
|
const [config2, connectors, installedSkills] = await Promise.all([
|
|
@@ -47254,8 +48307,11 @@ async function startServe(opts) {
|
|
|
47254
48307
|
const file = path11.join(opts.dir, name);
|
|
47255
48308
|
return fs14.existsSync(file) ? JSON.parse(fs14.readFileSync(file, "utf8")) : void 0;
|
|
47256
48309
|
};
|
|
47257
|
-
const
|
|
47258
|
-
const
|
|
48310
|
+
const pgDsn = process.env["OASIS_PG_DSN"] ?? process.env["DATABASE_URL"];
|
|
48311
|
+
const pgSchema = process.env["OASIS_PG_SCHEMA"] ?? "public";
|
|
48312
|
+
const pgPool = pgDsn ? createPgPool(pgDsn) : void 0;
|
|
48313
|
+
const oplog = pgDsn && pgPool ? await PostgresOplogStore.open(pgPool, pgSchema) : await NdjsonOplogStore.open(path11.join(opts.dir, "oplog.ndjson"));
|
|
48314
|
+
const blobs = pgDsn && pgPool ? await PostgresBlobStore.open(pgPool, pgSchema) : new DirBlobStore(path11.join(opts.dir, "blobs"));
|
|
47259
48315
|
const assets = new DirBlobStore(path11.join(opts.dir, "assets"));
|
|
47260
48316
|
const defaultRootSchema = [
|
|
47261
48317
|
{ 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" },
|
|
@@ -47282,7 +48338,7 @@ async function startServe(opts) {
|
|
|
47282
48338
|
schema.length = 0;
|
|
47283
48339
|
schema.push(...live);
|
|
47284
48340
|
}
|
|
47285
|
-
const registryStore = await FileRegistryStore.open(path11.join(opts.dir, "registry-store.json"));
|
|
48341
|
+
const registryStore = pgDsn && pgPool ? await PostgresRegistryStore.open(pgPool, pgSchema) : await FileRegistryStore.open(path11.join(opts.dir, "registry-store.json"));
|
|
47286
48342
|
const kernel = await Kernel.open(oplog, {
|
|
47287
48343
|
schema,
|
|
47288
48344
|
registry: loadJson("registry.json") ?? [],
|
|
@@ -47309,6 +48365,8 @@ async function startServe(opts) {
|
|
|
47309
48365
|
for (const l of registryListeners) l({ kind: record4.kind, actor: record4.actor, target: record4.target, timestamp: record4.timestamp });
|
|
47310
48366
|
};
|
|
47311
48367
|
await syncRegistryRolesToKernel(registryStore, kernel);
|
|
48368
|
+
let builtinSkills = [];
|
|
48369
|
+
let defaultCompanyId = resolveDefaultCompanyConfig().id;
|
|
47312
48370
|
const actors = createActorsDomain({
|
|
47313
48371
|
store: registryStore,
|
|
47314
48372
|
oplog,
|
|
@@ -47319,27 +48377,24 @@ async function startServe(opts) {
|
|
|
47319
48377
|
// engineRouter 在下方(行 ~411)以 const 声明;此箭头只在请求时调用,那时已初始化,闭包引用合法
|
|
47320
48378
|
// (TDZ 只在构造时访问才报错,这里不访问)。默认公司命中现有单实例(registry===registryStore),
|
|
47321
48379
|
// resolveCtx 会复用默认 ctx,行为不变。collab 域仍共用同一 registry(行 ~443),本试点只做 actors。
|
|
47322
|
-
resolveEngine: (cid) => engineRouter.getEngine(cid ??
|
|
48380
|
+
resolveEngine: (cid) => engineRouter.getEngine(cid ?? defaultCompanyId),
|
|
48381
|
+
// 内置技能只读列表:/api/skills/builtin 展示用(getter 闭包,加载完成后即返回实际集)。
|
|
48382
|
+
listBuiltinSkills: () => builtinSkills.map((s2) => ({ id: s2.id, name: s2.name, description: s2.description }))
|
|
47323
48383
|
});
|
|
47324
|
-
const pgDsn = process.env["OASIS_PG_DSN"] ?? process.env["DATABASE_URL"];
|
|
47325
|
-
const pgSchema = process.env["OASIS_PG_SCHEMA"] ?? "public";
|
|
47326
|
-
let pgPool;
|
|
47327
48384
|
let projectStateStore;
|
|
47328
48385
|
let artifactStateStore;
|
|
47329
48386
|
let chatSessionStore;
|
|
47330
|
-
if (pgDsn) {
|
|
47331
|
-
pgPool = createPgPool(pgDsn);
|
|
48387
|
+
if (pgDsn && pgPool) {
|
|
47332
48388
|
projectStateStore = await PostgresProjectStateStore.open(pgPool, pgSchema);
|
|
47333
48389
|
artifactStateStore = await PostgresArtifactStateStore.open(pgPool, pgSchema);
|
|
47334
48390
|
chatSessionStore = await PostgresChatSessionStore.open(pgPool, pgSchema);
|
|
47335
48391
|
nodeStore = await PostgresNodeStore.open(pgPool, pgSchema);
|
|
47336
48392
|
nodeTokens = await PostgresNodeTokenStore.open(pgPool, pgSchema);
|
|
47337
|
-
console.log(`[serve] \
|
|
47338
|
-
console.log(`[serve] \u8282\u70B9/\u8FD0\u884C\u65F6\u4F53\u7CFB\uFF1APostgres schema=${pgSchema}`);
|
|
48393
|
+
console.log(`[serve] \u4E8B\u5B9E/\u72B6\u6001\u4F53\u7CFB\uFF1APostgres schema=${pgSchema}\uFF08oplog / registry / blobs / artifacts / chat\uFF09`);
|
|
47339
48394
|
} else {
|
|
47340
48395
|
projectStateStore = await FileProjectStateStore.open(path11.join(opts.dir, "artifact-document-projects.json"));
|
|
47341
48396
|
artifactStateStore = await FileArtifactStateStore.open(path11.join(opts.dir, "artifact-document-state.json"));
|
|
47342
|
-
chatSessionStore =
|
|
48397
|
+
chatSessionStore = await FileChatSessionStore.open(path11.join(opts.dir, "chat-sessions.json"));
|
|
47343
48398
|
console.log("[serve] \u4EA4\u4ED8\u7269\u72B6\u6001\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
|
|
47344
48399
|
console.log("[serve] \u8282\u70B9/\u8FD0\u884C\u65F6\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
|
|
47345
48400
|
}
|
|
@@ -47392,7 +48447,7 @@ async function startServe(opts) {
|
|
|
47392
48447
|
const automationStore = pgPool ? await PostgresAutomationStore.open(pgPool, pgSchema) : new MemoryAutomationStore();
|
|
47393
48448
|
console.log(`[serve] \u81EA\u52A8\u5316\u4F53\u7CFB\uFF1A${pgPool ? `Postgres schema=${pgSchema}` : "\u5185\u5B58 dev store"}`);
|
|
47394
48449
|
const automations = createAutomationsDomain({ store: automationStore, kernel });
|
|
47395
|
-
const controlPlaneStore = await FileControlPlaneStore.open(path11.join(opts.dir, "control-plane.json"));
|
|
48450
|
+
const controlPlaneStore = pgDsn && pgPool ? await PostgresControlPlaneStore.open(pgPool, pgSchema) : await FileControlPlaneStore.open(path11.join(opts.dir, "control-plane.json"));
|
|
47396
48451
|
const resendKey = process.env["RESEND_API_KEY"];
|
|
47397
48452
|
const mailFrom = process.env["RESEND_FROM_EMAIL"] ?? process.env["OASIS_MAIL_FROM"] ?? "noreply@open-friday.com";
|
|
47398
48453
|
if (!resendKey) console.warn("[serve] RESEND_API_KEY \u672A\u8BBE\u2014\u2014\u9A8C\u8BC1\u7801/\u9080\u8BF7\u6253\u5370\u5230\u65E5\u5FD7\uFF08dev\uFF09\uFF1B\u751F\u4EA7\u8BF7\u914D RESEND_API_KEY + RESEND_FROM_EMAIL\uFF08Resend \u5DF2\u9A8C\u8BC1\u57DF\u540D\uFF09");
|
|
@@ -47410,17 +48465,27 @@ async function startServe(opts) {
|
|
|
47410
48465
|
// 邀请上手(切片5):被邀请但无账号的邮箱,验证码登录成功时建账号。
|
|
47411
48466
|
provisionAccount: (email2) => companies.service.ensureAccount(email2)
|
|
47412
48467
|
});
|
|
47413
|
-
await migrateDefaultCompany(controlPlaneStore, await actors.service.listActors());
|
|
47414
|
-
|
|
47415
|
-
|
|
47416
|
-
|
|
48468
|
+
const defaultCompanyMigration = await migrateDefaultCompany(controlPlaneStore, await actors.service.listActors());
|
|
48469
|
+
defaultCompanyId = defaultCompanyMigration.company.id;
|
|
48470
|
+
builtinSkills = await loadBuiltinSkills(builtinSkillsDir()).catch((err) => {
|
|
48471
|
+
console.warn(`[serve] builtin skills load failed: ${String(err)}`);
|
|
48472
|
+
return [];
|
|
48473
|
+
});
|
|
48474
|
+
console.log(`[serve] loaded ${builtinSkills.length} builtin skill(s): ${builtinSkills.map((s2) => s2.id).join(", ") || "(none)"}`);
|
|
48475
|
+
for (const devActor of ["actor:human:yx"]) {
|
|
48476
|
+
if (!await controlPlaneStore.getMember(defaultCompanyId, devActor)) {
|
|
48477
|
+
await controlPlaneStore.upsertMember({ companyId: defaultCompanyId, accountId: devActor, actorId: devActor, role: "member" });
|
|
48478
|
+
}
|
|
48479
|
+
}
|
|
48480
|
+
if (!await controlPlaneStore.getMember(defaultCompanyId, operatorActor)) {
|
|
48481
|
+
await controlPlaneStore.upsertMember({ companyId: defaultCompanyId, accountId: operatorActor, actorId: operatorActor, role: "owner" });
|
|
47417
48482
|
}
|
|
47418
48483
|
const bootstrapEmail = process.env["OASIS_BOOTSTRAP_OWNER_EMAIL"];
|
|
47419
48484
|
if (bootstrapEmail) {
|
|
47420
|
-
const m2 = await companies.service.grantOwner(
|
|
48485
|
+
const m2 = await companies.service.grantOwner(defaultCompanyId, bootstrapEmail);
|
|
47421
48486
|
console.log(`[serve] bootstrap owner\uFF1A${bootstrapEmail} \u2192 ${m2.accountId}\uFF08\u9ED8\u8BA4\u516C\u53F8 owner\uFF09`);
|
|
47422
48487
|
}
|
|
47423
|
-
await ensureCompanyActors(controlPlaneStore,
|
|
48488
|
+
await ensureCompanyActors(controlPlaneStore, defaultCompanyId, registryStore);
|
|
47424
48489
|
const CONTROL_PLANE_PREFIXES = ["/api/companies", "/api/invitations"];
|
|
47425
48490
|
const resolveCompanyContext = async (actor, headers, pathname) => {
|
|
47426
48491
|
if (CONTROL_PLANE_PREFIXES.some((p2) => pathname === p2 || pathname.startsWith(`${p2}/`))) {
|
|
@@ -47507,7 +48572,7 @@ async function startServe(opts) {
|
|
|
47507
48572
|
});
|
|
47508
48573
|
const engineRouter = new CompanyEngineRouter({
|
|
47509
48574
|
defaultEngine: { kernel, oplog, blobs, registry: registryStore, assets },
|
|
47510
|
-
defaultCompanyId
|
|
48575
|
+
defaultCompanyId,
|
|
47511
48576
|
buildEngine: async (companyId) => {
|
|
47512
48577
|
const engine2 = await openFileEngine(path11.join(opts.dir, "companies", companyEngineDirName(companyId)), { schema });
|
|
47513
48578
|
await ensureCompanyActors(controlPlaneStore, companyId, engine2.registry);
|
|
@@ -47516,7 +48581,7 @@ async function startServe(opts) {
|
|
|
47516
48581
|
}
|
|
47517
48582
|
});
|
|
47518
48583
|
const typesAdmin = new TypesAdminService(typeStore, () => engineRouter.liveEngines().map((e) => e.kernel));
|
|
47519
|
-
const engine = await engineRouter.getEngine(
|
|
48584
|
+
const engine = await engineRouter.getEngine(defaultCompanyId);
|
|
47520
48585
|
let hub = null;
|
|
47521
48586
|
let chatRemoteAdapter = null;
|
|
47522
48587
|
let localUrl = "";
|
|
@@ -47552,7 +48617,7 @@ async function startServe(opts) {
|
|
|
47552
48617
|
resolveCompanyContext,
|
|
47553
48618
|
auth,
|
|
47554
48619
|
// CO-301 阶段1:请求热路径按当前公司经路由层取引擎(恒等到默认公司;per-company 懒建是后续阶段)。
|
|
47555
|
-
resolveEngine: (companyId) => engineRouter.getEngine(companyId ??
|
|
48620
|
+
resolveEngine: (companyId) => engineRouter.getEngine(companyId ?? defaultCompanyId),
|
|
47556
48621
|
actors,
|
|
47557
48622
|
port: opts.port ?? 7320,
|
|
47558
48623
|
projectState: projectStateStore,
|
|
@@ -47589,7 +48654,7 @@ async function startServe(opts) {
|
|
|
47589
48654
|
return {};
|
|
47590
48655
|
}));
|
|
47591
48656
|
const limits = { wallClockMs: 5 * 6e4 };
|
|
47592
|
-
const artifactId = `artifact:planner:${(0,
|
|
48657
|
+
const artifactId = `artifact:planner:${(0, import_node_crypto21.randomUUID)()}`;
|
|
47593
48658
|
const handle = await chatRemoteAdapter.spawn({
|
|
47594
48659
|
actor: planner.id,
|
|
47595
48660
|
actorToken: issueSessionToken(planner.id, { artifactId, action: "plan-workorder", limits, binding }),
|
|
@@ -47600,7 +48665,8 @@ async function startServe(opts) {
|
|
|
47600
48665
|
binding,
|
|
47601
48666
|
limits,
|
|
47602
48667
|
env: provision.env,
|
|
47603
|
-
wrapperPaths: provision.wrapperPaths
|
|
48668
|
+
wrapperPaths: provision.wrapperPaths,
|
|
48669
|
+
...provision.requiredTools.length > 0 ? { requiredTools: provision.requiredTools } : {}
|
|
47604
48670
|
});
|
|
47605
48671
|
const chunks = [];
|
|
47606
48672
|
handle.onOutput?.((chunk) => chunks.push(chunk));
|
|
@@ -47635,7 +48701,7 @@ async function startServe(opts) {
|
|
|
47635
48701
|
workorderDrafts,
|
|
47636
48702
|
// 活工单查不到时回退查建单草案(status="draft")
|
|
47637
48703
|
...opts.sla ? { sla: opts.sla } : {},
|
|
47638
|
-
resolveEngine: (cid) => engineRouter.getEngine(cid ??
|
|
48704
|
+
resolveEngine: (cid) => engineRouter.getEngine(cid ?? defaultCompanyId)
|
|
47639
48705
|
}),
|
|
47640
48706
|
trace.register,
|
|
47641
48707
|
automations.register,
|
|
@@ -47662,7 +48728,10 @@ async function startServe(opts) {
|
|
|
47662
48728
|
},
|
|
47663
48729
|
resolveActorEnv: async (actorId) => (await buildActorProvision(actors.service, actorId)).env,
|
|
47664
48730
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
47665
|
-
materializeSkills: (actorId, runtimeKind) =>
|
|
48731
|
+
materializeSkills: async (actorId, runtimeKind) => ({
|
|
48732
|
+
...materializeBuiltins(builtinSkills, runtimeKind),
|
|
48733
|
+
...await materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind })
|
|
48734
|
+
}),
|
|
47666
48735
|
uploadChatAttachment: async ({ contentBase64 }) => ({ blobRef: await assets.put(new Uint8Array(Buffer.from(contentBase64, "base64"))) }),
|
|
47667
48736
|
dispatchProduce: async ({ artifactId, actorId, part }) => {
|
|
47668
48737
|
if (!dispatcher) {
|
|
@@ -47692,9 +48761,9 @@ async function startServe(opts) {
|
|
|
47692
48761
|
if (!localUrl) {
|
|
47693
48762
|
throw new ApiError(503, "SERVER_URL_NOT_READY", "server URL \u5C1A\u672A\u5C31\u7EEA");
|
|
47694
48763
|
}
|
|
47695
|
-
const runtimeSessionId = sessionId ?? (0,
|
|
48764
|
+
const runtimeSessionId = sessionId ?? (0, import_node_crypto21.randomUUID)();
|
|
47696
48765
|
const resumeRuntimeSession = Boolean(sessionId);
|
|
47697
|
-
const traceRunId = `chat-run:${(0,
|
|
48766
|
+
const traceRunId = `chat-run:${(0, import_node_crypto21.randomUUID)()}`;
|
|
47698
48767
|
const traceStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
47699
48768
|
let traceSeq = 0;
|
|
47700
48769
|
let traceEnabled = true;
|
|
@@ -47789,18 +48858,18 @@ async function startServe(opts) {
|
|
|
47789
48858
|
if (actorCtx?.config?.prompt) {
|
|
47790
48859
|
Object.assign(files, splitIdentityFiles3(actorCtx.config.prompt));
|
|
47791
48860
|
}
|
|
48861
|
+
Object.assign(files, materializeBuiltins(builtinSkills, binding.runtimeKind));
|
|
47792
48862
|
Object.assign(files, await materializeSkillFiles({
|
|
47793
48863
|
service: actors.service,
|
|
47794
48864
|
blobs: assets,
|
|
47795
48865
|
actorId,
|
|
47796
|
-
runtimeKind: binding.runtimeKind
|
|
47797
|
-
extraSkillIds: DEFAULT_CHAT_SKILLS
|
|
48866
|
+
runtimeKind: binding.runtimeKind
|
|
47798
48867
|
}).catch((err) => {
|
|
47799
48868
|
console.warn(`[chat] skill materialize failed for ${actorId}: ${String(err)}`);
|
|
47800
48869
|
return {};
|
|
47801
48870
|
}));
|
|
47802
48871
|
const limits = { wallClockMs: 10 * 6e4 };
|
|
47803
|
-
const artifactId = `artifact:chat:${(0,
|
|
48872
|
+
const artifactId = `artifact:chat:${(0, import_node_crypto21.randomUUID)()}`;
|
|
47804
48873
|
const handle = await chatRemoteAdapter.spawn({
|
|
47805
48874
|
actor: actorId,
|
|
47806
48875
|
actorToken: issueSessionToken(actorId, { artifactId, action: "chat", limits, binding, sessionId: runtimeSessionId }),
|
|
@@ -47816,7 +48885,8 @@ async function startServe(opts) {
|
|
|
47816
48885
|
// (建单/改图暂存统一,见 docs/proposals/draft-edit-unification.md)。不设 OASIS_WORKSPACE——
|
|
47817
48886
|
// chat agent 不绑单一工单,按命令 --workspace / id 定位是哪份草案。
|
|
47818
48887
|
env: { ...provision.env, OASIS_STAGE: "1", ...chatSessionId ? { OASIS_CHAT_SESSION_ID: chatSessionId } : {} },
|
|
47819
|
-
wrapperPaths: provision.wrapperPaths
|
|
48888
|
+
wrapperPaths: provision.wrapperPaths,
|
|
48889
|
+
...provision.requiredTools.length > 0 ? { requiredTools: provision.requiredTools } : {}
|
|
47820
48890
|
});
|
|
47821
48891
|
const buffered = [];
|
|
47822
48892
|
const bufferedTelemetry = [];
|
|
@@ -47941,7 +49011,10 @@ async function startServe(opts) {
|
|
|
47941
49011
|
resolveBinding: actors.service.resolveBinding(),
|
|
47942
49012
|
provision: (actorId) => buildActorProvision(actors.service, actorId),
|
|
47943
49013
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
47944
|
-
materializeSkills: (actorId, runtimeKind) =>
|
|
49014
|
+
materializeSkills: async (actorId, runtimeKind) => ({
|
|
49015
|
+
...materializeBuiltins(builtinSkills, runtimeKind),
|
|
49016
|
+
...await materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind })
|
|
49017
|
+
}),
|
|
47945
49018
|
schema: new Map(schema.map((d2) => [d2.name, d2])),
|
|
47946
49019
|
resolveCodeRepo,
|
|
47947
49020
|
// §4/C:异步从权威源(store)解析;dispatcher 装配前 await
|
|
@@ -48201,7 +49274,7 @@ var SessionManager = class {
|
|
|
48201
49274
|
|
|
48202
49275
|
// ../cli/src/daemon/ws-client.ts
|
|
48203
49276
|
init_wrapper();
|
|
48204
|
-
var
|
|
49277
|
+
var import_node_os3 = require("node:os");
|
|
48205
49278
|
|
|
48206
49279
|
// ../cli/src/daemon/logger.ts
|
|
48207
49280
|
function ts2() {
|
|
@@ -48304,6 +49377,10 @@ var DaemonWsClient = class {
|
|
|
48304
49377
|
return;
|
|
48305
49378
|
}
|
|
48306
49379
|
}
|
|
49380
|
+
if (job.requiredTools?.length) {
|
|
49381
|
+
const missing = await ensureConnectorTools(job.requiredTools);
|
|
49382
|
+
if (missing.length) log2("[node-cli]", ` connector tools still missing (agent may fail): ${missing.join(", ")}`);
|
|
49383
|
+
}
|
|
48307
49384
|
const handle = await this.sessions.getAdapter().spawn(job);
|
|
48308
49385
|
this.sessions.register(dispatchId, handle);
|
|
48309
49386
|
log2("[node-cli]", ` \u2192 session_started id=${handle.id} setup=${Date.now() - t0}ms`);
|
|
@@ -48338,12 +49415,12 @@ var DaemonWsClient = class {
|
|
|
48338
49415
|
if (this.ws?.readyState === import_websocket.default.OPEN) this.ws.send(JSON.stringify(msg));
|
|
48339
49416
|
}
|
|
48340
49417
|
buildMeta() {
|
|
48341
|
-
return { hostname: (0,
|
|
49418
|
+
return { hostname: (0, import_node_os3.hostname)(), adapters: this.adapters, ...this.reportRuntimes ? { runtimes: this.runtimes } : {}, nodeVersion: process.version, ...this.nodeName ? { nodeName: this.nodeName } : {} };
|
|
48342
49419
|
}
|
|
48343
49420
|
};
|
|
48344
49421
|
|
|
48345
49422
|
// ../cli/src/daemon/detect-adapters.ts
|
|
48346
|
-
var
|
|
49423
|
+
var import_node_child_process10 = require("node:child_process");
|
|
48347
49424
|
var KNOWN = [
|
|
48348
49425
|
{ adapter: "claude-code", binary: "claude" },
|
|
48349
49426
|
{ adapter: "codex", binary: "codex" },
|
|
@@ -48362,7 +49439,7 @@ var KNOWN = [
|
|
|
48362
49439
|
function onPath(binary) {
|
|
48363
49440
|
try {
|
|
48364
49441
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
48365
|
-
(0,
|
|
49442
|
+
(0, import_node_child_process10.execFileSync)(cmd, [binary], { stdio: "ignore" });
|
|
48366
49443
|
return true;
|
|
48367
49444
|
} catch {
|
|
48368
49445
|
return false;
|
|
@@ -48370,7 +49447,7 @@ function onPath(binary) {
|
|
|
48370
49447
|
}
|
|
48371
49448
|
function versionOf(binary) {
|
|
48372
49449
|
try {
|
|
48373
|
-
return (0,
|
|
49450
|
+
return (0, import_node_child_process10.execFileSync)(binary, ["--version"], {
|
|
48374
49451
|
encoding: "utf8",
|
|
48375
49452
|
stdio: ["ignore", "pipe", "ignore"],
|
|
48376
49453
|
timeout: 1500
|
|
@@ -48455,6 +49532,79 @@ async function startNode(opts) {
|
|
|
48455
49532
|
});
|
|
48456
49533
|
}
|
|
48457
49534
|
|
|
49535
|
+
// ../cli/src/daemon/machine-id.ts
|
|
49536
|
+
var import_node_child_process11 = require("node:child_process");
|
|
49537
|
+
var import_node_fs3 = require("node:fs");
|
|
49538
|
+
var import_node_crypto22 = require("node:crypto");
|
|
49539
|
+
var import_node_os4 = require("node:os");
|
|
49540
|
+
function linuxMachineId() {
|
|
49541
|
+
for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
49542
|
+
try {
|
|
49543
|
+
const v2 = (0, import_node_fs3.readFileSync)(p2, "utf8").trim();
|
|
49544
|
+
if (v2) return v2;
|
|
49545
|
+
} catch {
|
|
49546
|
+
}
|
|
49547
|
+
}
|
|
49548
|
+
return null;
|
|
49549
|
+
}
|
|
49550
|
+
function macMachineId() {
|
|
49551
|
+
try {
|
|
49552
|
+
const out = (0, import_node_child_process11.execFileSync)("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
|
|
49553
|
+
encoding: "utf8",
|
|
49554
|
+
timeout: 2e3,
|
|
49555
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
49556
|
+
});
|
|
49557
|
+
const m2 = out.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
|
|
49558
|
+
return m2?.[1] ?? null;
|
|
49559
|
+
} catch {
|
|
49560
|
+
return null;
|
|
49561
|
+
}
|
|
49562
|
+
}
|
|
49563
|
+
function windowsMachineId() {
|
|
49564
|
+
try {
|
|
49565
|
+
const out = (0, import_node_child_process11.execFileSync)("reg", [
|
|
49566
|
+
"query",
|
|
49567
|
+
"HKLM\\SOFTWARE\\Microsoft\\Cryptography",
|
|
49568
|
+
"/v",
|
|
49569
|
+
"MachineGuid"
|
|
49570
|
+
], { encoding: "utf8", timeout: 2e3, stdio: ["ignore", "pipe", "ignore"] });
|
|
49571
|
+
const m2 = out.match(/MachineGuid\s+REG_SZ\s+([A-Za-z0-9-]+)/);
|
|
49572
|
+
return m2?.[1] ?? null;
|
|
49573
|
+
} catch {
|
|
49574
|
+
return null;
|
|
49575
|
+
}
|
|
49576
|
+
}
|
|
49577
|
+
function fallbackFingerprint() {
|
|
49578
|
+
const macs = [];
|
|
49579
|
+
const ifaces = (0, import_node_os4.networkInterfaces)();
|
|
49580
|
+
for (const name of Object.keys(ifaces).sort()) {
|
|
49581
|
+
for (const ni of ifaces[name] ?? []) {
|
|
49582
|
+
if (!ni.internal && ni.mac && ni.mac !== "00:00:00:00:00:00") macs.push(ni.mac);
|
|
49583
|
+
}
|
|
49584
|
+
}
|
|
49585
|
+
return `fallback:${(0, import_node_os4.hostname)()}:${macs.sort()[0] ?? "no-mac"}`;
|
|
49586
|
+
}
|
|
49587
|
+
function machineFingerprint() {
|
|
49588
|
+
const byOs = process.platform === "darwin" ? macMachineId() : process.platform === "win32" ? windowsMachineId() : linuxMachineId();
|
|
49589
|
+
return byOs ?? fallbackFingerprint();
|
|
49590
|
+
}
|
|
49591
|
+
var defaultSources = {
|
|
49592
|
+
machineFingerprint,
|
|
49593
|
+
osUser: () => {
|
|
49594
|
+
try {
|
|
49595
|
+
return (0, import_node_os4.userInfo)().username;
|
|
49596
|
+
} catch {
|
|
49597
|
+
return process.env["USER"] ?? process.env["USERNAME"] ?? "unknown";
|
|
49598
|
+
}
|
|
49599
|
+
}
|
|
49600
|
+
};
|
|
49601
|
+
function resolveNodeId(sources = {}) {
|
|
49602
|
+
const s2 = { ...defaultSources, ...sources };
|
|
49603
|
+
const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
|
|
49604
|
+
const digest = (0, import_node_crypto22.createHash)("sha256").update(material).digest("hex").slice(0, 12);
|
|
49605
|
+
return `node-${digest}`;
|
|
49606
|
+
}
|
|
49607
|
+
|
|
48458
49608
|
// ../cli/src/cli.ts
|
|
48459
49609
|
init_src4();
|
|
48460
49610
|
var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uFF08\u670D\u52A1\u7AEF\u7626\u5BA2\u6237\u7AEF\uFF09
|
|
@@ -48636,16 +49786,17 @@ async function runCli(argv, println = console.log) {
|
|
|
48636
49786
|
const nodeTokenFile = path12.join(dir, "node-token");
|
|
48637
49787
|
const enrollTokenFile = path12.join(dir, "enroll-token");
|
|
48638
49788
|
const reportMarkerFile = path12.join(dir, "report-runtimes");
|
|
48639
|
-
|
|
49789
|
+
const persistedNodeId = fs15.existsSync(nodeIdFile) ? fs15.readFileSync(nodeIdFile, "utf8").trim() : null;
|
|
49790
|
+
let nodeId = persistedNodeId || resolveNodeId();
|
|
48640
49791
|
let token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs15.existsSync(nodeTokenFile) ? fs15.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
|
|
48641
49792
|
const enrollToken = flags.get("enroll-token") ?? process.env["OASIS_ENROLL_TOKEN"] ?? (fs15.existsSync(enrollTokenFile) ? fs15.readFileSync(enrollTokenFile, "utf8").trim() : void 0);
|
|
48642
49793
|
let freshEnrollment = false;
|
|
48643
|
-
if (
|
|
49794
|
+
if (!token2 && enrollToken) {
|
|
48644
49795
|
const httpBase2 = serverUrl.replace(/^ws(s?):\/\//, "http$1://").replace(/\/node-gateway.*$/, "");
|
|
48645
49796
|
const res = await fetch(`${httpBase2}/api/nodes/exchange`, {
|
|
48646
49797
|
method: "POST",
|
|
48647
49798
|
headers: { "content-type": "application/json" },
|
|
48648
|
-
body: JSON.stringify({ enrollToken,
|
|
49799
|
+
body: JSON.stringify({ enrollToken, nodeId, hostname: os7.hostname() })
|
|
48649
49800
|
});
|
|
48650
49801
|
if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
|
|
48651
49802
|
const body = await res.json();
|
|
@@ -48738,7 +49889,7 @@ async function runCli(argv, println = console.log) {
|
|
|
48738
49889
|
const store = createNodeTokenStore(path12.join(dir, "node-tokens.json"));
|
|
48739
49890
|
const sub = positional[0];
|
|
48740
49891
|
if (sub === "issue") {
|
|
48741
|
-
const id = flags.get("id") ?? `node-${(0,
|
|
49892
|
+
const id = flags.get("id") ?? `node-${(0, import_node_crypto23.randomUUID)()}`;
|
|
48742
49893
|
const token2 = store.issue(id);
|
|
48743
49894
|
println(token2);
|
|
48744
49895
|
process.stderr.write(
|
|
@@ -48763,9 +49914,10 @@ async function runCli(argv, println = console.log) {
|
|
|
48763
49914
|
}
|
|
48764
49915
|
if (command === "admin" && positional[0] === "grant-owner") {
|
|
48765
49916
|
const email2 = need(flags, "email");
|
|
48766
|
-
const companyId = flags.get("company") ?? DEFAULT_COMPANY_ID;
|
|
48767
|
-
const name = flags.get("name");
|
|
48768
49917
|
const store = await FileControlPlaneStore.open(path12.join(dir, "control-plane.json"));
|
|
49918
|
+
const activeCompanies = (await store.listCompanies()).filter((c) => c.status === "active");
|
|
49919
|
+
const companyId = flags.get("company") ?? activeCompanies[0]?.id ?? resolveDefaultCompanyConfig().id;
|
|
49920
|
+
const name = flags.get("name");
|
|
48769
49921
|
if (!await store.getCompany(companyId)) throw new Error(`\u516C\u53F8\u4E0D\u5B58\u5728\uFF1A${companyId}\uFF08\u5148\u8BA9 serve \u8DD1\u4E00\u6B21\u4EE5\u5EFA\u9ED8\u8BA4\u516C\u53F8\uFF0C\u6216\u7528 --company \u6307\u5B9A\u5DF2\u5B58\u5728\u516C\u53F8\uFF09`);
|
|
48770
49922
|
const svc = new CompaniesService({ store });
|
|
48771
49923
|
const m2 = await svc.grantOwner(companyId, email2, name);
|
|
@@ -49896,7 +51048,7 @@ function setupAutostart() {
|
|
|
49896
51048
|
""
|
|
49897
51049
|
].join("\n"));
|
|
49898
51050
|
try {
|
|
49899
|
-
(0,
|
|
51051
|
+
(0, import_node_child_process12.execSync)("systemctl --user daemon-reload && systemctl --user enable --now oasis-node", { stdio: "ignore" });
|
|
49900
51052
|
} catch {
|
|
49901
51053
|
}
|
|
49902
51054
|
} else if (process.platform === "darwin") {
|
|
@@ -49913,14 +51065,14 @@ function setupAutostart() {
|
|
|
49913
51065
|
<key>StandardErrorPath</key><string>${LOG_FILE}</string>
|
|
49914
51066
|
</dict></plist>`);
|
|
49915
51067
|
try {
|
|
49916
|
-
(0,
|
|
51068
|
+
(0, import_node_child_process12.execSync)(`launchctl load "${plist}"`, { stdio: "ignore" });
|
|
49917
51069
|
} catch {
|
|
49918
51070
|
}
|
|
49919
51071
|
}
|
|
49920
51072
|
}
|
|
49921
51073
|
function spawnDaemon() {
|
|
49922
51074
|
const log3 = fs16.openSync(LOG_FILE, "a");
|
|
49923
|
-
const child = (0,
|
|
51075
|
+
const child = (0, import_node_child_process12.spawn)(process.execPath, [BIN_FILE, "--_daemon"], {
|
|
49924
51076
|
detached: true,
|
|
49925
51077
|
stdio: ["ignore", log3, log3]
|
|
49926
51078
|
});
|
|
@@ -49942,11 +51094,11 @@ function stopDaemon() {
|
|
|
49942
51094
|
} catch {
|
|
49943
51095
|
}
|
|
49944
51096
|
try {
|
|
49945
|
-
(0,
|
|
51097
|
+
(0, import_node_child_process12.execSync)('pkill -f "oasis.js --_daemon"', { stdio: "ignore" });
|
|
49946
51098
|
} catch {
|
|
49947
51099
|
}
|
|
49948
51100
|
try {
|
|
49949
|
-
(0,
|
|
51101
|
+
(0, import_node_child_process12.execSync)("systemctl --user stop oasis-node 2>/dev/null", { stdio: "ignore" });
|
|
49950
51102
|
} catch {
|
|
49951
51103
|
}
|
|
49952
51104
|
}
|