oasis_test 0.1.118 → 0.1.119
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 +178 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4801,6 +4801,11 @@ function createPolicy(cfg) {
|
|
|
4801
4801
|
}
|
|
4802
4802
|
};
|
|
4803
4803
|
}
|
|
4804
|
+
function policyGetter(source, fallback = CODE_DEFAULT_POLICY) {
|
|
4805
|
+
if (typeof source === "function") return source;
|
|
4806
|
+
if (source) return () => source;
|
|
4807
|
+
return () => fallback;
|
|
4808
|
+
}
|
|
4804
4809
|
var MINUTE, HOUR, DAY, AGENT_WORK_DEADLINE_MS, AGENT_REVIEW_DEADLINE_MS, AGENT_REVIEW_MAX_RETRIES, AGENT_WORK_MAX_RETRIES, RETRY_BASE_MS, RUNTIME_MAX_RETRIES, DEFAULT_IDLE_BY_RUNTIME, DEFAULT_IDLE_MS, DEFAULT_WALL_CLOCK_BY_RUNTIME, DEFAULT_WALL_CLOCK_MS, DEFAULT_HUMAN_CADENCE, CODE_DEFAULT_POLICY;
|
|
4805
4810
|
var init_policy = __esm({
|
|
4806
4811
|
"../timeout-policy/src/policy.ts"() {
|
|
@@ -7571,6 +7576,10 @@ function inputsMatch(state, node, latestWork) {
|
|
|
7571
7576
|
const latestInputs = new Set(state.workInputsOf(latestWork.id).map((i) => i.upstreamWorkId));
|
|
7572
7577
|
return currInputs.size === latestInputs.size && [...currInputs].every((x2) => latestInputs.has(x2));
|
|
7573
7578
|
}
|
|
7579
|
+
function reviewMaxRetriesOf(policy) {
|
|
7580
|
+
const act = policy.resolve({ executor: "agent", scenario: "review" }).timeoutAction;
|
|
7581
|
+
return act.kind === "kill-rebuild" ? act.maxRetries : AGENT_REVIEW_MAX_RETRIES;
|
|
7582
|
+
}
|
|
7574
7583
|
function processReviews(state, nodeId, workId, ctx, events) {
|
|
7575
7584
|
const policy = ctx.policy ?? CODE_DEFAULT_POLICY;
|
|
7576
7585
|
const reqs = state.requirementsOf(nodeId);
|
|
@@ -7620,7 +7629,7 @@ function processReviews(state, nodeId, workId, ctx, events) {
|
|
|
7620
7629
|
}
|
|
7621
7630
|
} else if (info.status === "failed") {
|
|
7622
7631
|
const req = reqs.find((r) => r.reviewGroup === g2);
|
|
7623
|
-
const overLimit = countFailedForReviewGroup(g2, reviews) >
|
|
7632
|
+
const overLimit = countFailedForReviewGroup(g2, reviews) > reviewMaxRetriesOf(policy);
|
|
7624
7633
|
if (overLimit && classify(req.reviewerActorId) === "agent") {
|
|
7625
7634
|
const lrId = req.latestReviewId;
|
|
7626
7635
|
if (lrId) events.push({
|
|
@@ -15427,7 +15436,8 @@ var init_bus = __esm({
|
|
|
15427
15436
|
io;
|
|
15428
15437
|
clock;
|
|
15429
15438
|
config;
|
|
15430
|
-
policy
|
|
15439
|
+
/** 取当前策略(不是策略本身)——见 {@link EngineBusOptions.policy} 的热载不变量。 */
|
|
15440
|
+
policyOf;
|
|
15431
15441
|
registry;
|
|
15432
15442
|
maxEffectAttempts;
|
|
15433
15443
|
stuckAppliedRecoveryMs;
|
|
@@ -15439,7 +15449,7 @@ var init_bus = __esm({
|
|
|
15439
15449
|
this.io = opts.io ?? NOOP_IO;
|
|
15440
15450
|
this.clock = opts.clock ?? SYSTEM_CLOCK;
|
|
15441
15451
|
this.config = opts.config ?? DEFAULT_CONFIG;
|
|
15442
|
-
this.
|
|
15452
|
+
this.policyOf = policyGetter(opts.policy);
|
|
15443
15453
|
this.registry = new HandlerRegistry(opts.handlers);
|
|
15444
15454
|
this.maxEffectAttempts = opts.maxEffectAttempts ?? 3;
|
|
15445
15455
|
this.stuckAppliedRecoveryMs = opts.stuckAppliedRecoveryMs ?? DEFAULT_STUCK_APPLIED_RECOVERY_MS;
|
|
@@ -15828,7 +15838,7 @@ var init_bus = __esm({
|
|
|
15828
15838
|
if (!snapshot) return { emitted: 0, terminated: false };
|
|
15829
15839
|
if (snapshot.workorder.lifecycle !== "active") return { emitted: 0, terminated: false };
|
|
15830
15840
|
const state = new WorkorderState(snapshot);
|
|
15831
|
-
const scanCtx = { at: this.clock.now(), actorId: SYSTEM_ACTOR2, config: this.config, policy: this.
|
|
15841
|
+
const scanCtx = { at: this.clock.now(), actorId: SYSTEM_ACTOR2, config: this.config, policy: this.policyOf() };
|
|
15832
15842
|
const { events, terminated } = scan(state, scanCtx);
|
|
15833
15843
|
await tx.applyMutations(workorderId, state.mutations);
|
|
15834
15844
|
const allEvents = [...state.emitted, ...events];
|
|
@@ -128738,13 +128748,13 @@ ${block}
|
|
|
128738
128748
|
throw err;
|
|
128739
128749
|
}
|
|
128740
128750
|
const delay = base * Math.pow(3, attempt - 1);
|
|
128741
|
-
yield
|
|
128751
|
+
yield sleep5(delay);
|
|
128742
128752
|
}
|
|
128743
128753
|
}
|
|
128744
128754
|
throw lastErr;
|
|
128745
128755
|
});
|
|
128746
128756
|
}
|
|
128747
|
-
function
|
|
128757
|
+
function sleep5(ms) {
|
|
128748
128758
|
return new Promise((r) => setTimeout(r, ms));
|
|
128749
128759
|
}
|
|
128750
128760
|
var DEFAULT_CHUNK_LIMIT = 3500;
|
|
@@ -173922,7 +173932,9 @@ async function openNewEngine(opts) {
|
|
|
173922
173932
|
const bus = new EngineBus({
|
|
173923
173933
|
store,
|
|
173924
173934
|
io: NOOP_IO,
|
|
173925
|
-
clock: SYSTEM_CLOCK
|
|
173935
|
+
clock: SYSTEM_CLOCK,
|
|
173936
|
+
// 原样交给 bus(bus 内部归一成取值函数)——这里绝不能 call 一次存下来,那就把热载掐死了。
|
|
173937
|
+
policy: opts.policy
|
|
173926
173938
|
});
|
|
173927
173939
|
const kernel = await EngineKernel.open(store, bus, {
|
|
173928
173940
|
schema: opts.schemaDefs ?? [],
|
|
@@ -198389,7 +198401,10 @@ async function startServe(opts) {
|
|
|
198389
198401
|
schemaDefs: schema,
|
|
198390
198402
|
registry: kernelRegistry,
|
|
198391
198403
|
reworkWavefront,
|
|
198392
|
-
convergenceUnified
|
|
198404
|
+
convergenceUnified,
|
|
198405
|
+
// ★ ADR 0147:引擎 scan 的 work/review 超时与重试上限走策略中心(与 dispatcher 墙钟、
|
|
198406
|
+
// waiting-inbox 催办同源)。thunk 而非实例——随 SIGHUP / POST /api/policy/reload 热载而变。
|
|
198407
|
+
policy: () => activeTimeoutPolicy
|
|
198393
198408
|
});
|
|
198394
198409
|
const kernel = newEngine.kernel;
|
|
198395
198410
|
const dispatchLedger = await PostgresDispatchStore.open(pgPool, pgSchema);
|
|
@@ -198998,7 +199013,9 @@ async function startServe(opts) {
|
|
|
198998
199013
|
schemaDefs: schema,
|
|
198999
199014
|
registry: kernelRegistry,
|
|
199000
199015
|
reworkWavefront,
|
|
199001
|
-
convergenceUnified
|
|
199016
|
+
convergenceUnified,
|
|
199017
|
+
// ★ 同默认公司:漏了这里,非默认公司的引擎照旧按代码默认 30min 判超时(策略文件形同虚设)。
|
|
199018
|
+
policy: () => activeTimeoutPolicy
|
|
199002
199019
|
});
|
|
199003
199020
|
await ensureCompanyActors(controlPlaneStore, companyId, registryStore);
|
|
199004
199021
|
await syncRegistryRolesToKernel(registryStore, engine2.kernel);
|
|
@@ -201950,6 +201967,9 @@ var PENDING_STREAM_MAX = 2e3;
|
|
|
201950
201967
|
var AUTH_REJECT_BACKOFF_MAX_MS = 15 * 6e4;
|
|
201951
201968
|
var AUTH_REJECT_GIVE_UP_AFTER = 3;
|
|
201952
201969
|
var AUTH_REJECT_RE = /Unexpected server response:\s*(401|403)/;
|
|
201970
|
+
var DRAIN_TIMEOUT_MS = 15e3;
|
|
201971
|
+
var DRAIN_POLL_MS = 50;
|
|
201972
|
+
var sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
201953
201973
|
async function preflightOasisAccess(job) {
|
|
201954
201974
|
const base = job.server.url.replace(/\/+$/, "");
|
|
201955
201975
|
try {
|
|
@@ -201994,6 +202014,8 @@ var DaemonWsClient = class {
|
|
|
201994
202014
|
stopped = false;
|
|
201995
202015
|
/** 收到 update 但有活跃会话时置真:延后到所有会话结束再自更新,避免打断执行中的 agent。 */
|
|
201996
202016
|
pendingUpdate = false;
|
|
202017
|
+
/** 停机排空中(见 {@link drain}):不再接新派发、不再触发延后的自更新。 */
|
|
202018
|
+
draining = false;
|
|
201997
202019
|
/** 已回 dispatch_received、尚未完成 runtime spawn 的派发;也随 hello 上报供重连对账。 */
|
|
201998
202020
|
pendingDispatchIds = /* @__PURE__ */ new Set();
|
|
201999
202021
|
/** setup 期间收到 kill 的派发。spawn 返回后先杀句柄,不得再宣告 session_started。 */
|
|
@@ -202050,6 +202072,73 @@ var DaemonWsClient = class {
|
|
|
202050
202072
|
if (this.pingTimer) clearInterval(this.pingTimer);
|
|
202051
202073
|
this.ws?.close();
|
|
202052
202074
|
}
|
|
202075
|
+
/** 排空中?(观测/测试用) */
|
|
202076
|
+
isDraining() {
|
|
202077
|
+
return this.draining;
|
|
202078
|
+
}
|
|
202079
|
+
/**
|
|
202080
|
+
* 停机排空——在进程退出前,把「在途会话已经死了」这件事告诉服务端。
|
|
202081
|
+
*
|
|
202082
|
+
* ## 为什么必须有这一步(2026-08-25 生产实证)
|
|
202083
|
+
*
|
|
202084
|
+
* 引擎里一条 work 从 `running` 变成别的状态,全后端只有三个入口:收到 `session_exited` 帧走主路
|
|
202085
|
+
* (`serve.ts` 的 work.response 桥)、同一帧走 journal 反查的兜底、以及 scan 里 **30 分钟**的
|
|
202086
|
+
* `work.timeout`。前两个都要求**这一帧真的被发出来**。
|
|
202087
|
+
*
|
|
202088
|
+
* 而这一帧的生成者,是本进程 dispatch 分支里注册的那个 `handle.onExit` 回调——**它住在 daemon
|
|
202089
|
+
* 进程内部**。子进程被杀不会自动产生任何帧;进程一死,回调就再没机会跑。
|
|
202090
|
+
*
|
|
202091
|
+
* 旧行为:SIGTERM handler 只删 pid 文件就 `process.exit(0)`;systemd unit 又没设 `KillMode`,
|
|
202092
|
+
* 默认 `control-group` 把 daemon 和它拉起的所有会话**同时** SIGTERM——父子同死,一帧都发不出去。
|
|
202093
|
+
* 实测 node-6745008c1fc7:5389 次会话起跑里 48 个没有配对退出帧,其中 43 个(90%)是重启带走的;
|
|
202094
|
+
* 每一个都让对应的 work 空转到 30 分钟超时才被判死,控制台全程显示"运行中"。
|
|
202095
|
+
*
|
|
202096
|
+
* ## 做法:让真实退出帧走正路,只有两类才合成
|
|
202097
|
+
*
|
|
202098
|
+
* 不图省事直接合成假帧——`kill()` 掉在途会话,adapter 的 `killReason` 归因成 `cancelled`,既有
|
|
202099
|
+
* `onExit` 正路会带着 `transcriptRef` 把**真实**帧发出去。只有两类兜底才合成:还没 spawn 的派发
|
|
202100
|
+
* (没有子进程可 kill,不可能有真实帧),以及超时后仍未退出的会话(宁可给服务端一帧不精确的死讯,
|
|
202101
|
+
* 也好过让它等满 30 分钟)。
|
|
202102
|
+
*
|
|
202103
|
+
* 排空期间 `draining` 置真:不再接新派发(见 dispatch 分支),也不再触发延后的自更新。
|
|
202104
|
+
*/
|
|
202105
|
+
async drain(reason, timeoutMs = DRAIN_TIMEOUT_MS) {
|
|
202106
|
+
if (this.draining) return;
|
|
202107
|
+
this.draining = true;
|
|
202108
|
+
const active = this.sessions.activeSessions();
|
|
202109
|
+
const pending = [...this.pendingDispatchIds];
|
|
202110
|
+
if (active.length === 0 && pending.length === 0) {
|
|
202111
|
+
this.stop();
|
|
202112
|
+
return;
|
|
202113
|
+
}
|
|
202114
|
+
log2("[node-cli]", `\u505C\u673A\u6392\u7A7A\uFF08${reason}\uFF09\uFF1A\u5728\u9014\u4F1A\u8BDD ${active.length} \u4E2A\u3001\u5F85\u8D77\u6D3E\u53D1 ${pending.length} \u4E2A\u2014\u2014\u53D1\u5B8C\u9000\u51FA\u5E27\u518D\u9000\u51FA`);
|
|
202115
|
+
for (const dispatchId of pending) {
|
|
202116
|
+
this.pendingDispatchIds.delete(dispatchId);
|
|
202117
|
+
this.sendKeyFrame(dispatchId, {
|
|
202118
|
+
type: "session_exited",
|
|
202119
|
+
dispatchId,
|
|
202120
|
+
sessionId: "node-shutdown",
|
|
202121
|
+
info: { code: null, reason: "cancelled", errorMessage: `\u8282\u70B9 daemon \u505C\u673A\uFF08${reason}\uFF09\uFF1A\u6D3E\u53D1\u5C1A\u672A\u8D77\u4F1A\u8BDD` }
|
|
202122
|
+
});
|
|
202123
|
+
}
|
|
202124
|
+
await Promise.all(active.map((s2) => this.sessions.kill(s2.dispatchId).catch(() => void 0)));
|
|
202125
|
+
const deadline = Date.now() + timeoutMs;
|
|
202126
|
+
while (Date.now() < deadline && (this.sessions.activeCount() > 0 || this.outbox.size > 0)) await sleep4(DRAIN_POLL_MS);
|
|
202127
|
+
const stuck = this.sessions.activeSessions();
|
|
202128
|
+
for (const s2 of stuck) {
|
|
202129
|
+
log2("[node-cli]", ` \u26A0 \u4F1A\u8BDD ${s2.sessionId} \u5728 ${timeoutMs}ms \u5185\u6CA1\u9000\u51FA\uFF0C\u6309\u505C\u673A\u5408\u6210\u9000\u51FA\u5E27`);
|
|
202130
|
+
this.sendKeyFrame(s2.dispatchId, {
|
|
202131
|
+
type: "session_exited",
|
|
202132
|
+
dispatchId: s2.dispatchId,
|
|
202133
|
+
sessionId: s2.sessionId,
|
|
202134
|
+
info: { code: null, reason: "cancelled", errorMessage: `\u8282\u70B9 daemon \u505C\u673A\uFF08${reason}\uFF09\uFF1A\u4F1A\u8BDD\u672A\u5728\u6392\u7A7A\u7A97\u53E3\u5185\u9000\u51FA` }
|
|
202135
|
+
});
|
|
202136
|
+
}
|
|
202137
|
+
if (stuck.length > 0) await sleep4(DRAIN_POLL_MS * 4);
|
|
202138
|
+
if (this.outbox.size > 0) log2("[node-cli]", `\u26A0 \u505C\u673A\u6392\u7A7A\u7ED3\u675F\uFF1A\u4ECD\u6709 ${this.outbox.size} \u4E2A\u9000\u51FA\u5E27\u672A\u786E\u8BA4\u9001\u8FBE\uFF08\u8FD9\u4E9B work \u4F1A\u9000\u56DE 30 \u5206\u949F\u8D85\u65F6\u515C\u5E95\uFF09`);
|
|
202139
|
+
else log2("[node-cli]", "\u505C\u673A\u6392\u7A7A\u5B8C\u6210\uFF1A\u5728\u9014\u4F1A\u8BDD\u7684\u9000\u51FA\u5E27\u5747\u5DF2\u786E\u8BA4\u9001\u8FBE");
|
|
202140
|
+
this.stop();
|
|
202141
|
+
}
|
|
202053
202142
|
connect() {
|
|
202054
202143
|
if (this.stopped) return;
|
|
202055
202144
|
const ws = new import_websocket.default(this.serverUrl, {
|
|
@@ -202126,6 +202215,16 @@ var DaemonWsClient = class {
|
|
|
202126
202215
|
const { dispatchId, job } = msg;
|
|
202127
202216
|
const t0 = Date.now();
|
|
202128
202217
|
log2("[node-cli]", `\u2190 dispatch ${dispatchId} artifact=${job.artifactId}`);
|
|
202218
|
+
if (this.draining) {
|
|
202219
|
+
log2("[node-cli]", ` \u62D2\u6536\uFF1A\u8282\u70B9\u6B63\u5728\u505C\u673A\u6392\u7A7A \u2192 \u56DE\u9000\u51FA\u5E27\uFF0C\u670D\u52A1\u7AEF\u53EF\u7ACB\u5373\u6539\u6D3E`);
|
|
202220
|
+
this.sendKeyFrame(dispatchId, {
|
|
202221
|
+
type: "session_exited",
|
|
202222
|
+
dispatchId,
|
|
202223
|
+
sessionId: "node-draining",
|
|
202224
|
+
info: { code: null, reason: "cancelled", errorMessage: "\u8282\u70B9\u6B63\u5728\u505C\u673A\u6392\u7A7A\uFF0C\u672C\u6B21\u6D3E\u53D1\u672A\u8D77\u4F1A\u8BDD" }
|
|
202225
|
+
});
|
|
202226
|
+
break;
|
|
202227
|
+
}
|
|
202129
202228
|
if (this.pendingDispatchIds.has(dispatchId) || this.sessions.get(dispatchId)) {
|
|
202130
202229
|
this.send({ type: "dispatch_received", dispatchId });
|
|
202131
202230
|
break;
|
|
@@ -202243,6 +202342,10 @@ var DaemonWsClient = class {
|
|
|
202243
202342
|
log2("[node-cli]", "\u2190 update \u5FFD\u7565\uFF1A\u672C\u8FDB\u7A0B\u672A\u6CE8\u5165\u81EA\u66F4\u65B0\u903B\u8F91\uFF08\u975E node-daemon \u6258\u7BA1\uFF1F\uFF09");
|
|
202244
202343
|
break;
|
|
202245
202344
|
}
|
|
202345
|
+
if (this.draining) {
|
|
202346
|
+
log2("[node-cli]", "\u2190 update \u5FFD\u7565\uFF1A\u672C\u8FDB\u7A0B\u6B63\u5728\u505C\u673A\u6392\u7A7A");
|
|
202347
|
+
break;
|
|
202348
|
+
}
|
|
202246
202349
|
const active = this.sessions.activeCount();
|
|
202247
202350
|
if (active > 0) {
|
|
202248
202351
|
this.pendingUpdate = true;
|
|
@@ -202330,6 +202433,7 @@ var DaemonWsClient = class {
|
|
|
202330
202433
|
}
|
|
202331
202434
|
/** 会话结束后调用:若之前收到过 update 但因忙延后了,且现已空闲,则触发自更新。 */
|
|
202332
202435
|
maybeRunPendingUpdate() {
|
|
202436
|
+
if (this.draining) return;
|
|
202333
202437
|
if (this.pendingUpdate && this.sessions.activeCount() === 0) {
|
|
202334
202438
|
this.pendingUpdate = false;
|
|
202335
202439
|
log2("[node-cli]", "\u6240\u6709\u4F1A\u8BDD\u5DF2\u7ED3\u675F,\u6267\u884C\u5EF6\u540E\u7684\u66F4\u65B0\uFF1A\u62C9\u53D6\u6700\u65B0\u7248\u672C\u5E76\u91CD\u542F");
|
|
@@ -202493,6 +202597,7 @@ async function startNode(opts) {
|
|
|
202493
202597
|
});
|
|
202494
202598
|
console.log(`[oasis node] ${opts.nodeId} \u2192 ${opts.serverUrl}`);
|
|
202495
202599
|
client.start();
|
|
202600
|
+
opts.onReady?.({ drain: (reason) => client.drain(reason) });
|
|
202496
202601
|
const { workRoot, legacyRoot } = resolveWorkRoots(opts.workRoot);
|
|
202497
202602
|
const workRootIsEphemeral = workRoot.startsWith(import_node_os11.default.tmpdir());
|
|
202498
202603
|
if (opts.gcEnabled === false && !workRootIsEphemeral) {
|
|
@@ -207460,6 +207565,37 @@ function gcVersions(versionsDirPath, opts) {
|
|
|
207460
207565
|
}
|
|
207461
207566
|
return deleted;
|
|
207462
207567
|
}
|
|
207568
|
+
function systemdUnitText(o) {
|
|
207569
|
+
return [
|
|
207570
|
+
"[Unit]",
|
|
207571
|
+
`Description=Oasis Node Daemon (${o.name})`,
|
|
207572
|
+
"After=network.target",
|
|
207573
|
+
"",
|
|
207574
|
+
// OASIS_CLI_HOME 由 daemon 继承给它 spawn 的 agent 会话——非 default 实例的会话因此
|
|
207575
|
+
// 用的是**本实例**的 CLI 版本,而不是 PATH 上那个默认指向 default 的 shim。
|
|
207576
|
+
// (台阶 3 的 D4 后半截会把它细到「每个会话钉 spawn 那一刻的版本」,这里先钉到实例。)
|
|
207577
|
+
"[Service]",
|
|
207578
|
+
`ExecStart=${o.cmd}`,
|
|
207579
|
+
`Environment=PATH=${o.daemonPath}`,
|
|
207580
|
+
`Environment=OASIS_CLI_HOME=${o.cliHome}`,
|
|
207581
|
+
`StandardOutput=append:${o.logFile}`,
|
|
207582
|
+
`StandardError=append:${o.logFile}`,
|
|
207583
|
+
"Restart=on-failure",
|
|
207584
|
+
"RestartSec=5",
|
|
207585
|
+
// 🚨 停机时只 SIGTERM 主进程,子会话留给 daemon 自己收——它要先给每个在途会话发 session_exited,
|
|
207586
|
+
// 那是服务端唯一的死亡通知渠道(生成者是 daemon 进程内的 onExit 回调)。
|
|
207587
|
+
// systemd 默认的 KillMode=control-group 会把 daemon 和它拉起的所有 claude 会话**同时**杀掉:
|
|
207588
|
+
// 父子同死,一帧都发不出去,对应的 work 只能空转到 scan 的 30 分钟超时才被判死。
|
|
207589
|
+
// 实测占本机孤儿会话的 90%(43/48)。详见 change-record 20260825-daemon-restart-swallows-exit-frames。
|
|
207590
|
+
// TimeoutStopSec 必须明显大于排空预算 DRAIN_TIMEOUT_MS(15s),否则排空没做完就被 SIGKILL。
|
|
207591
|
+
"KillMode=mixed",
|
|
207592
|
+
"TimeoutStopSec=30",
|
|
207593
|
+
"",
|
|
207594
|
+
"[Install]",
|
|
207595
|
+
"WantedBy=default.target",
|
|
207596
|
+
""
|
|
207597
|
+
].join("\n");
|
|
207598
|
+
}
|
|
207463
207599
|
function shimScript() {
|
|
207464
207600
|
return [
|
|
207465
207601
|
"#!/bin/sh",
|
|
@@ -207472,7 +207608,7 @@ function shimScript() {
|
|
|
207472
207608
|
}
|
|
207473
207609
|
|
|
207474
207610
|
// src/index.ts
|
|
207475
|
-
var PKG_VERSION = true ? "0.1.
|
|
207611
|
+
var PKG_VERSION = true ? "0.1.119" : "dev";
|
|
207476
207612
|
var LOCAL_BIN = localBin();
|
|
207477
207613
|
var NPM_PREFIX = npmPrefix();
|
|
207478
207614
|
var INSTANCE = DEFAULT_INSTANCE;
|
|
@@ -207586,27 +207722,13 @@ function setupAutostart(name) {
|
|
|
207586
207722
|
fs38.mkdirSync(unitDir, { recursive: true });
|
|
207587
207723
|
const unit = systemdUnitName(name);
|
|
207588
207724
|
fs38.mkdirSync(path32.dirname(inst.logFile), { recursive: true });
|
|
207589
|
-
fs38.writeFileSync(path32.join(unitDir, unit),
|
|
207590
|
-
|
|
207591
|
-
|
|
207592
|
-
|
|
207593
|
-
|
|
207594
|
-
|
|
207595
|
-
|
|
207596
|
-
// (台阶 3 的 D4 后半截会把它细到「每个会话钉 spawn 那一刻的版本」,这里先钉到实例。)
|
|
207597
|
-
"[Service]",
|
|
207598
|
-
`ExecStart=${cmd}`,
|
|
207599
|
-
`Environment=PATH=${daemonPath}`,
|
|
207600
|
-
`Environment=OASIS_CLI_HOME=${inst.currentLink}`,
|
|
207601
|
-
`StandardOutput=append:${inst.logFile}`,
|
|
207602
|
-
`StandardError=append:${inst.logFile}`,
|
|
207603
|
-
"Restart=on-failure",
|
|
207604
|
-
"RestartSec=5",
|
|
207605
|
-
"",
|
|
207606
|
-
"[Install]",
|
|
207607
|
-
"WantedBy=default.target",
|
|
207608
|
-
""
|
|
207609
|
-
].join("\n"));
|
|
207725
|
+
fs38.writeFileSync(path32.join(unitDir, unit), systemdUnitText({
|
|
207726
|
+
name,
|
|
207727
|
+
cmd,
|
|
207728
|
+
daemonPath,
|
|
207729
|
+
cliHome: inst.currentLink,
|
|
207730
|
+
logFile: inst.logFile
|
|
207731
|
+
}));
|
|
207610
207732
|
if (name === DEFAULT_INSTANCE) {
|
|
207611
207733
|
try {
|
|
207612
207734
|
(0, import_node_child_process19.execSync)(`systemctl --user disable --now ${LEGACY_SYSTEMD_UNIT} 2>/dev/null`, { stdio: "ignore" });
|
|
@@ -207876,19 +207998,32 @@ void (async () => {
|
|
|
207876
207998
|
}
|
|
207877
207999
|
const cleanup = () => {
|
|
207878
208000
|
try {
|
|
207879
|
-
fs38.unlinkSync(PID_FILE);
|
|
208001
|
+
if (fs38.readFileSync(PID_FILE, "utf8").trim() === String(process.pid)) fs38.unlinkSync(PID_FILE);
|
|
207880
208002
|
} catch {
|
|
207881
208003
|
}
|
|
207882
208004
|
};
|
|
207883
208005
|
process.on("exit", cleanup);
|
|
207884
|
-
|
|
207885
|
-
|
|
207886
|
-
|
|
207887
|
-
|
|
207888
|
-
|
|
207889
|
-
|
|
207890
|
-
|
|
207891
|
-
|
|
208006
|
+
let drainNode = null;
|
|
208007
|
+
let shuttingDown = false;
|
|
208008
|
+
const shutdown = (signal) => {
|
|
208009
|
+
if (shuttingDown) {
|
|
208010
|
+
console.error(`[oasis] \u6536\u5230\u7B2C\u4E8C\u6B21 ${signal}\uFF1A\u653E\u5F03\u6392\u7A7A\uFF0C\u7ACB\u5373\u9000\u51FA`);
|
|
208011
|
+
cleanup();
|
|
208012
|
+
process.exit(0);
|
|
208013
|
+
}
|
|
208014
|
+
shuttingDown = true;
|
|
208015
|
+
void (async () => {
|
|
208016
|
+
try {
|
|
208017
|
+
await drainNode?.(signal);
|
|
208018
|
+
} catch (e) {
|
|
208019
|
+
console.error("[oasis] \u505C\u673A\u6392\u7A7A\u5931\u8D25\uFF08\u5728\u9014\u4F1A\u8BDD\u7684\u9000\u51FA\u5E27\u53EF\u80FD\u6CA1\u53D1\u51FA\u53BB\uFF09:", String(e));
|
|
208020
|
+
}
|
|
208021
|
+
cleanup();
|
|
208022
|
+
process.exit(0);
|
|
208023
|
+
})();
|
|
208024
|
+
};
|
|
208025
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
208026
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
207892
208027
|
const cleanPath = sanitizeDaemonPath(process.env["PATH"]);
|
|
207893
208028
|
if (cleanPath !== process.env["PATH"]) {
|
|
207894
208029
|
console.error(`[oasis] daemon PATH \u5DF2\u6E05\u6D17\uFF1A\u5254\u9664 agent \u4F1A\u8BDD\u4E34\u65F6 wrapper \u76EE\u5F55 ${(process.env["PATH"] ?? "").split(":").length - cleanPath.split(":").length} \u6761`);
|
|
@@ -207928,6 +208063,9 @@ void (async () => {
|
|
|
207928
208063
|
} catch (e) {
|
|
207929
208064
|
console.error("[oasis] self-update failed:", String(e));
|
|
207930
208065
|
}
|
|
208066
|
+
},
|
|
208067
|
+
onReady: (ctl) => {
|
|
208068
|
+
drainNode = ctl.drain;
|
|
207931
208069
|
}
|
|
207932
208070
|
});
|
|
207933
208071
|
return;
|