@sema-agent/server 7.131.0 → 7.132.0
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/MIGRATION.md +309 -0
- package/README.md +5 -1
- package/README.zh-CN.md +3 -1
- package/USAGE.md +163 -54
- package/dist/approval-card.d.ts +3 -1
- package/dist/approval-card.js +2 -3
- package/dist/audit.d.ts +7 -10
- package/dist/auth-bridge.d.ts +4 -0
- package/dist/auth-bridge.js +10 -11
- package/dist/boot/deployment-mods.d.ts +31 -0
- package/dist/boot/deployment-mods.js +26 -0
- package/dist/boot/execution-env.d.ts +1 -1
- package/dist/boot/execution-env.js +19 -6
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
- package/dist/boot/read-deny-anchor-audit.js +36 -0
- package/dist/boot/resolve-spec.d.ts +1 -5
- package/dist/boot/resolve-spec.js +19 -47
- package/dist/boot/runner-deps.d.ts +19 -8
- package/dist/boot/runner-deps.js +14 -2
- package/dist/boot/session-faces.d.ts +1 -7
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +4 -4
- package/dist/boot/stage-05-execution-env.js +7 -10
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +5 -4
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +3 -3
- package/dist/boot/stage-08-reapers.d.ts +2 -2
- package/dist/boot/stage-09-leader.d.ts +3 -9
- package/dist/boot/stage-10-http-server.d.ts +3 -9
- package/dist/boot/stage-10-http-server.js +6 -7
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +5 -6
- package/dist/boot/web-admin.d.ts +0 -3
- package/dist/boot/web-admin.js +35 -35
- package/dist/brain.js +17 -22
- package/dist/budget.js +5 -4
- package/dist/caller-text-echo.d.ts +20 -0
- package/dist/caller-text-echo.js +24 -0
- package/dist/capabilities/brand-identity.d.ts +26 -0
- package/dist/capabilities/brand-identity.js +27 -0
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/center-prompts.d.ts +32 -59
- package/dist/capabilities/center-prompts.js +18 -32
- package/dist/capabilities/engine-capabilities.js +2 -1
- package/dist/capabilities/hands-lane.d.ts +9 -6
- package/dist/capabilities/hands-lane.js +1 -1
- package/dist/capabilities/mcp-probe.d.ts +14 -1
- package/dist/capabilities/mcp-probe.js +22 -133
- package/dist/capabilities/scenarios.d.ts +34 -40
- package/dist/capabilities/scenarios.js +16 -33
- package/dist/capabilities/skills.d.ts +19 -4
- package/dist/capabilities/skills.js +30 -10
- package/dist/config-catalog.js +6 -6
- package/dist/config-lkg.d.ts +9 -9
- package/dist/config-lkg.js +20 -4
- package/dist/config-provider.d.ts +3 -1
- package/dist/config-provider.js +12 -4
- package/dist/config-types.d.ts +7 -9
- package/dist/config.js +17 -4
- package/dist/device-ws-protocol.d.ts +1 -1
- package/dist/device-ws-protocol.js +1 -0
- package/dist/env-name-allowlist-knobs.d.ts +12 -2
- package/dist/env-name-allowlist-knobs.js +5 -4
- package/dist/exec-bounds.d.ts +15 -3
- package/dist/exec-bounds.js +4 -0
- package/dist/execution-lane-caps.d.ts +9 -2
- package/dist/execution-lane-caps.js +11 -0
- package/dist/hooks/classic-stations.d.ts +86 -0
- package/dist/hooks/classic-stations.js +27 -0
- package/dist/hooks/hook-llm.d.ts +8 -5
- package/dist/hooks/hook-llm.js +78 -35
- package/dist/hooks/hook-runner.d.ts +232 -80
- package/dist/hooks/hook-runner.js +218 -312
- package/dist/http/admission.d.ts +8 -2
- package/dist/http/admission.js +101 -57
- package/dist/http/approval-sweeps.js +57 -16
- package/dist/http/checkpoint-ctx.d.ts +25 -8
- package/dist/http/checkpoint-ctx.js +11 -7
- package/dist/http/claimed-run.d.ts +13 -0
- package/dist/http/claimed-run.js +15 -0
- package/dist/http/device-placement.js +5 -10
- package/dist/http/dispatch.d.ts +0 -4
- package/dist/http/dispatch.js +5 -9
- package/dist/http/durable-run-leg.d.ts +2 -1
- package/dist/http/durable-run-leg.js +5 -3
- package/dist/http/health-self-report.d.ts +7 -5
- package/dist/http/health-self-report.js +4 -18
- package/dist/http/principal-gate.d.ts +23 -6
- package/dist/http/principal-gate.js +24 -5
- package/dist/http/refusal-body.d.ts +21 -0
- package/dist/http/refusal-body.js +4 -0
- package/dist/http/request-guards.d.ts +27 -0
- package/dist/http/request-guards.js +24 -3
- package/dist/http/resume-legs.d.ts +25 -0
- package/dist/http/resume-legs.js +43 -22
- package/dist/http/routes/capabilities.js +1 -1
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/rules.js +2 -2
- package/dist/http/routes/runs.js +7 -7
- package/dist/http/routes/session-sync.js +4 -2
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/shared-memory.js +2 -13
- package/dist/http/routes/side-query.js +2 -3
- package/dist/http/routes/tasks.js +19 -9
- package/dist/http/send.d.ts +9 -7
- package/dist/http/send.js +7 -2
- package/dist/http/server.d.ts +21 -11
- package/dist/http/server.js +28 -2
- package/dist/http/submit-ctx.d.ts +25 -0
- package/dist/http/submit-ctx.js +39 -0
- package/dist/http/wire-types.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lane-home-probe.d.ts +43 -0
- package/dist/lane-home-probe.js +35 -0
- package/dist/limit-refusal.d.ts +47 -0
- package/dist/limit-refusal.js +12 -0
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +1 -9
- package/dist/mcp-assertion-header.js +0 -6
- package/dist/mcp-tool-faces.d.ts +5 -0
- package/dist/mcp-tool-faces.js +5 -1
- package/dist/memory-scope.d.ts +1 -1
- package/dist/model-provider.d.ts +39 -12
- package/dist/model-provider.js +19 -2
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-route-endpoint.js +5 -5
- package/dist/observability/cost-taxonomy.d.ts +7 -7
- package/dist/observability/fail-open.d.ts +42 -6
- package/dist/observability/fail-open.js +42 -6
- package/dist/observability/metrics.js +3 -1
- package/dist/observability/prompt-manifest.d.ts +47 -82
- package/dist/observability/prompt-manifest.js +119 -17
- package/dist/observability/run-terminal.js +14 -6
- package/dist/observability/secret-env-scrub.d.ts +13 -1
- package/dist/observability/secret-env-scrub.js +13 -1
- package/dist/observability/tool-trace.d.ts +16 -21
- package/dist/observability/tool-trace.js +51 -42
- package/dist/per-task-image.d.ts +7 -1
- package/dist/per-task-image.js +3 -3
- package/dist/plain-string-record.d.ts +25 -0
- package/dist/plain-string-record.js +32 -0
- package/dist/plugins/blob-backend.d.ts +68 -13
- package/dist/plugins/blob-backend.js +53 -10
- package/dist/plugins/checkpoint-store-sql.d.ts +14 -4
- package/dist/plugins/checkpoint-store-sql.js +39 -26
- package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
- package/dist/plugins/k8s-bg-scripts.js +22 -0
- package/dist/plugins/local-checkpoint-store.js +5 -2
- package/dist/plugins/local-session-store.js +1 -1
- package/dist/plugins/one-time-migrations.d.ts +137 -90
- package/dist/plugins/one-time-migrations.js +356 -207
- package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
- package/dist/plugins/permission-rule-store-sql.js +4 -18
- package/dist/plugins/pg-session-storage.d.ts +4 -5
- package/dist/plugins/pg-session-storage.js +7 -17
- package/dist/plugins/remote-env-adb.d.ts +6 -0
- package/dist/plugins/remote-env-adb.js +9 -4
- package/dist/plugins/remote-env-device.js +3 -0
- package/dist/plugins/remote-env-e2b.d.ts +1 -1
- package/dist/plugins/remote-env-e2b.js +21 -9
- package/dist/plugins/remote-env-host.d.ts +9 -9
- package/dist/plugins/remote-env-host.js +10 -32
- package/dist/plugins/remote-env-k8s.d.ts +95 -26
- package/dist/plugins/remote-env-k8s.js +265 -90
- package/dist/plugins/remote-env-local-docker.d.ts +20 -1
- package/dist/plugins/remote-env-local-docker.js +77 -9
- package/dist/plugins/remote-env-ssh.d.ts +15 -0
- package/dist/plugins/remote-env-ssh.js +109 -3
- package/dist/plugins/remote-shell.d.ts +0 -1
- package/dist/plugins/remote-shell.js +1 -2
- package/dist/plugins/retention-store-sql.js +6 -2
- package/dist/plugins/schema-consistency.d.ts +54 -0
- package/dist/plugins/schema-consistency.js +52 -0
- package/dist/plugins/session-epoch-anchor.d.ts +13 -0
- package/dist/plugins/session-epoch-anchor.js +23 -0
- package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
- package/dist/plugins/shared-memory-store-sql.js +5 -16
- package/dist/plugins/sql-driver.d.ts +141 -0
- package/dist/plugins/sql-driver.js +115 -1
- package/dist/plugins/sql-errors.d.ts +41 -0
- package/dist/plugins/sql-errors.js +36 -0
- package/dist/plugins/store-backend.js +2 -1
- package/dist/plugins/tidb-session-storage.d.ts +7 -11
- package/dist/plugins/tidb-session-storage.js +6 -16
- package/dist/plugins/tidb-session-store.js +1 -1
- package/dist/prompts-domain-validate.d.ts +17 -61
- package/dist/prompts-domain-validate.js +9 -119
- package/dist/request-key-closure.d.ts +2 -2
- package/dist/request-key-closure.js +2 -4
- package/dist/rule-entry-list.d.ts +12 -9
- package/dist/rule-entry-list.js +37 -21
- package/dist/run-local.js +19 -19
- package/dist/runs.d.ts +6 -0
- package/dist/runs.js +16 -0
- package/dist/server-secret-env.d.ts +44 -18
- package/dist/server-secret-env.js +49 -8
- package/dist/session-incarnation.d.ts +15 -5
- package/dist/session-incarnation.js +16 -9
- package/dist/session-titler.js +12 -2
- package/dist/spec-fields.d.ts +28 -16
- package/dist/spec-fields.js +46 -53
- package/dist/sql-engine-posture.d.ts +29 -4
- package/dist/sql-engine-posture.js +36 -1
- package/dist/stored-request-tool-axes-m19.d.ts +19 -4
- package/dist/stored-request-tool-axes-m19.js +30 -2
- package/dist/task-a2a.d.ts +9 -1
- package/dist/task-a2a.js +73 -47
- package/dist/task-mcp.d.ts +46 -58
- package/dist/task-mcp.js +99 -65
- package/dist/task-settings.d.ts +7 -21
- package/dist/task-settings.js +7 -36
- package/dist/text-bidi.d.ts +3 -46
- package/dist/text-bidi.js +3 -3
- package/dist/tool-approval.d.ts +1 -1
- package/dist/trace/core-keyset-guard.d.ts +24 -4
- package/dist/trace/engine-notice-wire.d.ts +11 -3
- package/dist/trace/engine-notice-wire.js +8 -1
- package/dist/trace/ledger-events.d.ts +3 -1
- package/dist/trace/ledger-sink.js +4 -1
- package/dist/trace/model-segment.js +1 -1
- package/dist/trace/project.d.ts +19 -6
- package/dist/trace/project.js +57 -22
- package/dist/trace/wire-projection-faces.d.ts +79 -1
- package/dist/trace/wire-projection-faces.js +22 -0
- package/dist/web-admin-client/candidate-gate.d.ts +6 -6
- package/dist/web-admin-client/candidate-gate.js +3 -3
- package/dist/web-admin-client/http-client.js +0 -6
- package/dist/web-admin-client/read-warnings.d.ts +13 -0
- package/dist/web-admin-client/read-warnings.js +10 -0
- package/dist/web-admin-client/skills-mcp.d.ts +3 -0
- package/dist/web-admin-client/skills-mcp.js +9 -6
- package/dist/web-admin-client/types.d.ts +2 -3
- package/dist/workspace-cache.d.ts +35 -0
- package/dist/workspace-cache.js +36 -0
- package/dist/workspace-space.d.ts +3 -2
- package/dist/workspace-space.js +2 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +8 -1
- package/package.json +4 -4
- package/scripts/drain-check-local.mjs +34 -1
- package/scripts/drain-check.mysql.sql +30 -2
- package/scripts/drain-check.pg.sql +29 -2
- package/dist/capabilities/memory-notice.d.ts +0 -138
- package/dist/capabilities/memory-notice.js +0 -64
- package/dist/capabilities/prompt.d.ts +0 -17
- package/dist/capabilities/prompt.js +0 -6
- package/dist/mcp-content-origin.d.ts +0 -34
- package/dist/mcp-content-origin.js +0 -10
- package/dist/tool-axis-words.d.ts +0 -27
- package/dist/tool-axis-words.js +0 -9
- package/dist/wire-name-bound.d.ts +0 -14
- package/dist/wire-name-bound.js +0 -13
|
@@ -437,13 +437,17 @@ export interface UnreadableRuleBucket {
|
|
|
437
437
|
readonly ownerKey: string;
|
|
438
438
|
readonly why: string;
|
|
439
439
|
}
|
|
440
|
-
/**
|
|
441
|
-
|
|
442
|
-
|
|
440
|
+
/**
|
|
441
|
+
* M11 写进的读数账 —— 三格与一次性迁移段那一只收据账(`one-time-migrations.ts` 的 `MigrationReceipts`)上的同名三格**同名同形**,迁移段把它的账直接递进来
|
|
442
|
+
* (S-1594:一次起服一只收据账,M11 不另起第二套):删掉的行在那只桶的回写**提交的那一刻**追加,之后任何一步失败,拒因随账带走它们。
|
|
443
|
+
*/
|
|
444
|
+
export interface UndurableScopePurgeReadings {
|
|
445
|
+
/** 删掉的行(一行一条读数;每只桶的回写提交之后才追加)。 */
|
|
446
|
+
readonly undurableScopeRuleRows: UndurableScopeRuleRow[];
|
|
443
447
|
/** 连续争不过在线写者(OCC 每次都输)而留到下一次起服的桶数。那些桶里的坏行这期间照旧由引擎读侧丢掉并告知。 */
|
|
444
|
-
|
|
448
|
+
undurableScopeRuleBucketsDeferred: number;
|
|
445
449
|
/** 读腿整桶拒的桶(形 / 指纹 / behavior 闭集 / 墓碑规范文本 / 单一验证器):一个字节不动,逐桶交出拒因,等人处置。 */
|
|
446
|
-
readonly
|
|
450
|
+
readonly undurableScopeRuleBucketsUnreadable: UnreadableRuleBucket[];
|
|
447
451
|
}
|
|
448
452
|
/** {@link purgeUndurableScopeRows} 要的最小查询口(一次性迁移段的 `MigrationDb` 同形;占位符按方言由本函数写)。 */
|
|
449
453
|
export interface UndurableScopePurgeDb {
|
|
@@ -458,13 +462,15 @@ export interface UndurableScopePurgeDb {
|
|
|
458
462
|
* 而撤销口删不动它(引擎的写臂对这种 scope 的墓碑同样拒)。⇒ 一次性迁移,不留「撤销口对坏 scope 另开一条读法」的长期双读。
|
|
459
463
|
*
|
|
460
464
|
* 读法与写法都是**店的那一只**:每只桶先过唯一判官 {@link decodeBucketRow}(读腿用的同一只),判官整桶拒 ⇒ 这只桶**一个字节不动**,
|
|
461
|
-
* 进 `
|
|
465
|
+
* 进 `undurableScopeRuleBucketsUnreadable`(桶键 + 不带行内容的拒因;读腿对它的整桶拒仍是它的属主,出路是人);判官放行 ⇒ 在那份合法状态上筛 scope,
|
|
462
466
|
* 经写臂的同一只 {@link writeBucket} 整桶 OCC 回写(`WHERE owner_key = ? AND rev = ?`,rev+1,落盘字节与指纹同写臂)—— 不碰别的桶。
|
|
463
467
|
* **硬删、不铸墓碑**:墓碑的作用是挡同步回灌,而引擎的 sync-join 对 scope 不合法的入站行本来就丢(`applySyncJoin`),
|
|
464
468
|
* 本店也不实现同步(见 `apply` 的 sync-join 臂)。
|
|
465
469
|
* 幂等:第二跑零命中零写(读不出的桶每一跑照报,直到有人处置)。
|
|
470
|
+
* 读数写进调用方递进来的账(`into`,{@link UndurableScopePurgeReadings}):删掉的行在那只桶的回写提交之后追加 ⇒ 本函数半途抛出时,已删的行已经在账上;
|
|
471
|
+
* 错误**原样**上抛(不加工、不自铸拒因句 —— 收据由迁移段那一只收据账随拒因带走,S-1594)。
|
|
466
472
|
*/
|
|
467
|
-
export declare function purgeUndurableScopeRows(db: UndurableScopePurgeDb, dialect: "tidb" | "pg", now?: () => number): Promise<
|
|
473
|
+
export declare function purgeUndurableScopeRows(db: UndurableScopePurgeDb, dialect: "tidb" | "pg", into: UndurableScopePurgeReadings, now?: () => number): Promise<void>;
|
|
468
474
|
/** 三个 SQL 面的一次性装配束(one driver, three faces)。`durable` = [ref] 的 durable 分区后端;
|
|
469
475
|
* 引擎接缝上的统一店由 `main.ts` 用 `createPermissionRuleStoreProvider({ durable })` 合成一次。 */
|
|
470
476
|
export interface PermissionRuleStores {
|
|
@@ -783,21 +783,7 @@ export const RULE_PENDING_APPROVAL_RETENTION_MS = 24 * 60 * 60_000;
|
|
|
783
783
|
export const RULE_REAP_BATCH = 500;
|
|
784
784
|
const UNDURABLE_PURGE_PAGE = 200;
|
|
785
785
|
const UNDURABLE_PURGE_OCC_ATTEMPTS = 5;
|
|
786
|
-
export async function purgeUndurableScopeRows(db, dialect, now = Date.now) {
|
|
787
|
-
const acc = { removed: [], bucketsDeferred: 0, bucketsUnreadable: [] };
|
|
788
|
-
try {
|
|
789
|
-
await purgeAllBuckets(db, dialect, now, acc);
|
|
790
|
-
}
|
|
791
|
-
catch (e) {
|
|
792
|
-
if (acc.removed.length === 0)
|
|
793
|
-
throw e;
|
|
794
|
-
throw new Error(`the one-time removal of persisted permission rules whose scope the engine refuses failed AFTER removing ${acc.removed.length} row(s) — record them before retrying: ` +
|
|
795
|
-
acc.removed.map((r) => `${r.kind} in bucket ${r.ownerKey} (${r.behavior} ${r.tool}, scope root ${JSON.stringify(r.scopeRoot)})`).join("; ") +
|
|
796
|
-
`. Underlying failure: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
797
|
-
}
|
|
798
|
-
return acc;
|
|
799
|
-
}
|
|
800
|
-
async function purgeAllBuckets(db, dialect, now, acc) {
|
|
786
|
+
export async function purgeUndurableScopeRows(db, dialect, into, now = Date.now) {
|
|
801
787
|
const cols = `owner_key, ${bucketRowColumns(dialect)}`;
|
|
802
788
|
let after = "";
|
|
803
789
|
for (;;) {
|
|
@@ -813,11 +799,11 @@ async function purgeAllBuckets(db, dialect, now, acc) {
|
|
|
813
799
|
const outcome = await purgeOneBucket(db, dialect, ownerKey, row, now);
|
|
814
800
|
switch (outcome.kind) {
|
|
815
801
|
case "purged":
|
|
816
|
-
|
|
802
|
+
into.undurableScopeRuleRows.push(...outcome.rows);
|
|
817
803
|
settled = true;
|
|
818
804
|
break;
|
|
819
805
|
case "unreadable":
|
|
820
|
-
|
|
806
|
+
into.undurableScopeRuleBucketsUnreadable.push({ ownerKey, why: outcome.why });
|
|
821
807
|
settled = true;
|
|
822
808
|
break;
|
|
823
809
|
case "clean":
|
|
@@ -835,7 +821,7 @@ async function purgeAllBuckets(db, dialect, now, acc) {
|
|
|
835
821
|
}
|
|
836
822
|
}
|
|
837
823
|
if (!settled)
|
|
838
|
-
|
|
824
|
+
into.undurableScopeRuleBucketsDeferred += 1;
|
|
839
825
|
}
|
|
840
826
|
if (page.length < UNDURABLE_PURGE_PAGE)
|
|
841
827
|
break;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseSessionStorage, type
|
|
1
|
+
import { BaseSessionStorage, type AcquiredSession, type PlacedSessionRow, type SessionMetadata, type SessionPlacement, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry, type SessionWriteOptions } from "@sema-agent/core";
|
|
2
2
|
import type { Pool, PoolClient } from "pg";
|
|
3
3
|
import type { SessionSummary } from "./store-contracts.js";
|
|
4
4
|
import { type SessionRowPatch } from "../session-row.js";
|
|
@@ -30,10 +30,9 @@ export declare class PgSessionStorage extends BaseSessionStorage {
|
|
|
30
30
|
private readonly sessionId;
|
|
31
31
|
private readonly floorSeq;
|
|
32
32
|
constructor(pool: Pool, sessionId: string, metadata: SessionMetadata, entries: SessionTreeEntry[], leafId: string | null, floorEntryId?: string | null, floorSeq?: number);
|
|
33
|
-
/** @see TiDBSessionStorage.getEpochAnchor — [ref] S3 [ref] pull seam, PG dialect twin
|
|
34
|
-
* (
|
|
35
|
-
|
|
36
|
-
getEpochAnchor(): Promise<PromptEpochDescriptor | undefined>;
|
|
33
|
+
/** @see TiDBSessionStorage.getEpochAnchor — [ref] S3 [ref] pull seam, PG dialect twin: the same reading
|
|
34
|
+
* ({@link scanEpochAnchor}, the record as stored), this backend's page query. */
|
|
35
|
+
getEpochAnchor(): Promise<unknown>;
|
|
37
36
|
/** @see TiDBSessionStorage.wake — F3 bounded `[floor..leaf]` rebuild; null if the session does not exist. */
|
|
38
37
|
static wake(pool: Pool, sessionId: string): Promise<PgSessionStorage | null>;
|
|
39
38
|
/** The bounded-window floor = last compaction's firstKeptEntryId (and its seq). @see TiDBSessionStorage.computeFloor */
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { emitLeafAdvance } from "../session-leaf-bus.js";
|
|
2
|
-
import { BaseSessionStorage, StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry,
|
|
2
|
+
import { BaseSessionStorage, StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry, } from "@sema-agent/core";
|
|
3
3
|
import { SESSION_TITLE_MAX_CHARS } from "../session-row.js";
|
|
4
4
|
import { escapeLike } from "./sql-escape.js";
|
|
5
5
|
import { decodeSessionEventPayload } from "./session-event-payload.js";
|
|
6
|
+
import { scanEpochAnchor } from "./session-epoch-anchor.js";
|
|
6
7
|
import { jsonColumnText } from "./sql-json-column.js";
|
|
7
8
|
import { pgSafeJsonStringify } from "./pg-safe-json.js";
|
|
8
9
|
import { toIso } from "./sql-row-helpers.js";
|
|
@@ -82,21 +83,10 @@ export class PgSessionStorage extends BaseSessionStorage {
|
|
|
82
83
|
this.floorSeq = floorSeq;
|
|
83
84
|
}
|
|
84
85
|
async getEpochAnchor() {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const res = await this.pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND seq < $2 AND type IN ('prompt_epoch','compaction') ORDER BY seq DESC LIMIT 100`, [this.sessionId, cursor]);
|
|
90
|
-
if (res.rows.length === 0)
|
|
91
|
-
return undefined;
|
|
92
|
-
for (const r of res.rows) {
|
|
93
|
-
const e = decodeSessionEventPayload(r.payload, this.sessionId, r.seq);
|
|
94
|
-
const d = e.type === "prompt_epoch" ? normalizePromptEpoch(e) : normalizePromptEpoch(e.details?.promptEpoch);
|
|
95
|
-
if (d)
|
|
96
|
-
return d;
|
|
97
|
-
}
|
|
98
|
-
cursor = Number(res.rows[res.rows.length - 1].seq);
|
|
99
|
-
}
|
|
86
|
+
return scanEpochAnchor(this.sessionId, this.floorSeq, async (beforeSeq) => {
|
|
87
|
+
const res = await this.pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND seq < $2 AND type IN ('prompt_epoch','compaction') ORDER BY seq DESC LIMIT 100`, [this.sessionId, beforeSeq]);
|
|
88
|
+
return res.rows;
|
|
89
|
+
});
|
|
100
90
|
}
|
|
101
91
|
static async wake(pool, sessionId) {
|
|
102
92
|
const metaRes = await pool.query(`SELECT created_at, leaf_id, leaf_seq, incarnation, ${PLACEMENT_SELECT_COLUMNS} FROM session_meta WHERE session_id = $1`, [sessionId]);
|
|
@@ -516,7 +506,7 @@ export class PgSessionStore {
|
|
|
516
506
|
}
|
|
517
507
|
await conn.query("DELETE FROM session_event WHERE session_id = $1", [sessionId]);
|
|
518
508
|
await conn.query("DELETE FROM session_meta WHERE session_id = $1 AND owner IS NOT DISTINCT FROM $2 AND placement_kind IS NULL", [sessionId, owner]);
|
|
519
|
-
await this.insertValidatedLog(conn, sessionId, owner, validated, carriedIncarnation(placedRes.rows[0], owner));
|
|
509
|
+
await this.insertValidatedLog(conn, sessionId, owner, validated, carriedIncarnation(sessionId, placedRes.rows[0], owner));
|
|
520
510
|
await conn.query("COMMIT");
|
|
521
511
|
}
|
|
522
512
|
catch (err) {
|
|
@@ -204,6 +204,12 @@ export declare class RemoteAdbExecutionEnv implements RemoteExecutionEnv {
|
|
|
204
204
|
suffix?: string;
|
|
205
205
|
abortSignal?: AbortSignal;
|
|
206
206
|
}): Promise<Result<string, FileError>>;
|
|
207
|
+
/**
|
|
208
|
+
* 交给宿主 `adb` CLI 的 env —— 宿主子进程 env 的唯一组合(`server-secret-env.ts` `hostSubprocessEnv`),继承词 `"all"`:本服务自有凭据(起服声明的名与值)
|
|
209
|
+
* 任何词都不交,adb 自己要读的变量(`ADB_VENDOR_KEYS` / `ANDROID_*` …)照交 —— 形状剥会剥 `ADB_VENDOR_KEYS`(名形),设备鉴权因此断。首次调用拉起的常驻 adb server
|
|
210
|
+
* 继承的就是这一份。命令本身跑在设备的 shell 里,本 env 到不了设备。每次起进程现算一次(与 host shell 同)。
|
|
211
|
+
*/
|
|
212
|
+
private cliEnv;
|
|
207
213
|
/** Spawn the adb binary with the given args; collect stdout/stderr (+ raw bytes when binary), bound by timeout/abort. */
|
|
208
214
|
private adb;
|
|
209
215
|
/**
|
|
@@ -2,9 +2,10 @@ import path from "node:path";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
|
+
import { hostSubprocessEnv } from "../server-secret-env.js";
|
|
5
6
|
import { buildCwdEnvCommand, createCwdGuardedCall, shellQuote, armPipeDestroyGrace } from "./remote-shell.js";
|
|
6
7
|
import { signalNumber } from "./host-platform.js";
|
|
7
|
-
import {
|
|
8
|
+
import { buildLivenessCut, commandBoundsOf } from "../exec-bounds.js";
|
|
8
9
|
import { FileError, ExecutionError, RemoteExecutionError, withRetry, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
|
|
9
10
|
import { classifyFsStderr, fileErrorFromExec } from "./remote-env-file-error.js";
|
|
10
11
|
import { createPosixShellFs } from "./posix-shell-fs.js";
|
|
@@ -180,7 +181,7 @@ export class RemoteAdbExecutionEnv {
|
|
|
180
181
|
if (this.destroyed)
|
|
181
182
|
throw new RemoteExecutionError("connect_failed", "execution env already destroyed");
|
|
182
183
|
const wrapped = buildCwdEnvCommand(command, this.workingDirOf(options?.cwd), options?.env, "raw").command;
|
|
183
|
-
const child = this.spawnFn(this.cfg.adbPath, this.deviceShellArgs(wrapped), { stdio: ["ignore", "pipe", "pipe"] });
|
|
184
|
+
const child = this.spawnFn(this.cfg.adbPath, this.deviceShellArgs(wrapped), { stdio: ["ignore", "pipe", "pipe"], env: this.cliEnv() });
|
|
184
185
|
const queue = [];
|
|
185
186
|
let finished = false;
|
|
186
187
|
let failure;
|
|
@@ -276,7 +277,8 @@ export class RemoteAdbExecutionEnv {
|
|
|
276
277
|
clearTimeout(waitTimer);
|
|
277
278
|
}
|
|
278
279
|
if (liveness !== undefined && !finished && Date.now() - lastChunk >= liveness.ms) {
|
|
279
|
-
|
|
280
|
+
const cut = buildLivenessCut(liveness);
|
|
281
|
+
throw new RemoteExecutionError(cut.code, cut.message);
|
|
280
282
|
}
|
|
281
283
|
}
|
|
282
284
|
while (queue.length)
|
|
@@ -417,13 +419,16 @@ export class RemoteAdbExecutionEnv {
|
|
|
417
419
|
async createTempFile(options) {
|
|
418
420
|
return this.posixFs.createTempFile(options);
|
|
419
421
|
}
|
|
422
|
+
cliEnv() {
|
|
423
|
+
return hostSubprocessEnv({ site: "server.remote-env-adb.spawn-env", policy: "all", allow: [] });
|
|
424
|
+
}
|
|
420
425
|
adb(args, opts) {
|
|
421
426
|
if (this.destroyed)
|
|
422
427
|
return Promise.resolve({ ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") });
|
|
423
428
|
return new Promise((resolve) => {
|
|
424
429
|
let settled = false;
|
|
425
430
|
let forceSettleTimer;
|
|
426
|
-
const child = this.spawnFn(this.cfg.adbPath, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
431
|
+
const child = this.spawnFn(this.cfg.adbPath, args, { stdio: ["ignore", "pipe", "pipe"], env: this.cliEnv() });
|
|
427
432
|
const killGrace = () => {
|
|
428
433
|
try {
|
|
429
434
|
child.kill("SIGKILL");
|
|
@@ -12,6 +12,7 @@ export const DEVICE_OUTCOME_TO_FILE_ERROR = {
|
|
|
12
12
|
callback_error: "unknown",
|
|
13
13
|
invalid: "invalid",
|
|
14
14
|
is_directory: "is_directory",
|
|
15
|
+
liveness_no_output: "unknown",
|
|
15
16
|
not_directory: "not_directory",
|
|
16
17
|
not_found: "not_found",
|
|
17
18
|
not_supported: "not_supported",
|
|
@@ -33,6 +34,7 @@ export const DEVICE_OUTCOME_TO_EXEC_ERROR = {
|
|
|
33
34
|
callback_error: "callback_error",
|
|
34
35
|
invalid: "unknown",
|
|
35
36
|
is_directory: "unknown",
|
|
37
|
+
liveness_no_output: "liveness_no_output",
|
|
36
38
|
not_directory: "unknown",
|
|
37
39
|
not_found: "unknown",
|
|
38
40
|
not_supported: "shell_unavailable",
|
|
@@ -763,6 +765,7 @@ const OUTCOME_TO_REMOTE_ERROR = {
|
|
|
763
765
|
callback_error: "unknown",
|
|
764
766
|
invalid: "unknown",
|
|
765
767
|
is_directory: "unknown",
|
|
768
|
+
liveness_no_output: "liveness_no_output",
|
|
766
769
|
not_directory: "unknown",
|
|
767
770
|
not_found: "unknown",
|
|
768
771
|
not_supported: "unsupported",
|
|
@@ -321,7 +321,7 @@ export declare class RemoteContainerExecutionEnv implements RemoteExecutionEnv,
|
|
|
321
321
|
* a command that hangs DURING execution (not creation) would otherwise wait the E2B command timeout (= the
|
|
322
322
|
* lifetime when none is set, the original hang bug). With a `liveness` bound (the caller stated no `timeout` —
|
|
323
323
|
* `exec-bounds.ts`) it races wait() against an idle check: no stdout/stderr for `liveness.ms` ⇒ hung → kill + typed
|
|
324
|
-
* retryable
|
|
324
|
+
* retryable `liveness_no_output` (the shared cut: code, sentence and the window in whole seconds — `buildLivenessCut`). `lastProgress()` is bumped by the stdout/stderr callbacks, so a
|
|
325
325
|
* slow-but-PROGRESSING command (long `npm ci`) is never killed. Both cuts reject with an `ExecutionError` carrying
|
|
326
326
|
* its real code (exec returns it as-is) — a `RemoteExecutionError` here would reach {@link execError}'s text match
|
|
327
327
|
* and land as `unknown`. The rejection is armed BEFORE the kill: the kill rejects `wait()` too, and the race must
|
|
@@ -3,7 +3,7 @@ import { Sandbox, CommandExitError, FileType } from "e2b";
|
|
|
3
3
|
import { FileError, ExecutionError, RemoteExecutionError, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
|
|
4
4
|
import { fileErrorFromExec } from "./remote-env-file-error.js";
|
|
5
5
|
import { numEnvOr, runSymlinkChain, shellQuote } from "./remote-shell.js";
|
|
6
|
-
import { commandBoundsOf, firstBoundOf, laneLivenessOf,
|
|
6
|
+
import { buildLivenessCut, commandBoundsOf, firstBoundOf, laneLivenessOf, LIVENESS_MS_DEFAULT } from "../exec-bounds.js";
|
|
7
7
|
import { BackgroundShellManager } from "./background-shell-support.js";
|
|
8
8
|
const PROVIDER = "e2b";
|
|
9
9
|
const DEFAULT_MOUNT_PATH = "/home/user";
|
|
@@ -297,7 +297,10 @@ export class RemoteContainerExecutionEnv {
|
|
|
297
297
|
startCut = true;
|
|
298
298
|
this.settleAbandonedStart(starting);
|
|
299
299
|
const cut = e instanceof RemoteExecutionError && e.code === "timeout" && !options?.abortSignal?.aborted;
|
|
300
|
-
|
|
300
|
+
if (!cut)
|
|
301
|
+
return { ok: false, error: this.execError(e, options?.abortSignal) };
|
|
302
|
+
const bc = boundCut(first);
|
|
303
|
+
return { ok: false, error: new ExecutionError(bc.code, bc.message, undefined, undefined, bc.noOutputSec) };
|
|
301
304
|
}
|
|
302
305
|
const onAbort = () => { if (!detached)
|
|
303
306
|
void handle.kill().catch(() => { }); };
|
|
@@ -425,8 +428,10 @@ export class RemoteContainerExecutionEnv {
|
|
|
425
428
|
catch (e) {
|
|
426
429
|
finished = true;
|
|
427
430
|
this.settleAbandonedStart(starting);
|
|
428
|
-
if (e instanceof RemoteExecutionError && e.code === "timeout" && !options?.signal?.aborted)
|
|
429
|
-
|
|
431
|
+
if (e instanceof RemoteExecutionError && e.code === "timeout" && !options?.signal?.aborted) {
|
|
432
|
+
const bc = boundCut(first);
|
|
433
|
+
throw new RemoteExecutionError(bc.code, bc.message, e);
|
|
434
|
+
}
|
|
430
435
|
throw this.remoteError(e, "connect_failed");
|
|
431
436
|
}
|
|
432
437
|
handle
|
|
@@ -490,7 +495,10 @@ export class RemoteContainerExecutionEnv {
|
|
|
490
495
|
const idle = liveness === undefined
|
|
491
496
|
? undefined
|
|
492
497
|
: new Promise((_, reject) => {
|
|
493
|
-
timer = setTimeout(() =>
|
|
498
|
+
timer = setTimeout(() => {
|
|
499
|
+
const cut = buildLivenessCut(liveness);
|
|
500
|
+
reject(new RemoteExecutionError(cut.code, cut.message));
|
|
501
|
+
}, liveness.ms);
|
|
494
502
|
});
|
|
495
503
|
try {
|
|
496
504
|
await (idle === undefined ? next : Promise.race([next, idle]));
|
|
@@ -850,7 +858,8 @@ export class RemoteContainerExecutionEnv {
|
|
|
850
858
|
return;
|
|
851
859
|
const idleFor = Date.now() - lastProgress();
|
|
852
860
|
if (idleFor >= bound.ms) {
|
|
853
|
-
|
|
861
|
+
const cut = buildLivenessCut(bound);
|
|
862
|
+
reject(new ExecutionError(cut.code, cut.message, undefined, undefined, cut.noOutputSec));
|
|
854
863
|
void handle.kill().catch(() => { });
|
|
855
864
|
this.cfg.metrics?.inc("remote_env_exec_idle_timeout_total", { provider: PROVIDER });
|
|
856
865
|
}
|
|
@@ -866,7 +875,7 @@ export class RemoteContainerExecutionEnv {
|
|
|
866
875
|
wallTimer = setTimeout(() => {
|
|
867
876
|
if (opts.disarmed?.())
|
|
868
877
|
return;
|
|
869
|
-
reject(new ExecutionError("timeout",
|
|
878
|
+
reject(new ExecutionError("timeout", wallCutMessage(wall.totalMs)));
|
|
870
879
|
void handle.kill().catch(() => { });
|
|
871
880
|
}, wall.leftMs);
|
|
872
881
|
}
|
|
@@ -969,8 +978,11 @@ export function e2bExecutionEnvFactory(config, deps) {
|
|
|
969
978
|
function errMsg(e) {
|
|
970
979
|
return e instanceof Error ? e.message : String(e);
|
|
971
980
|
}
|
|
972
|
-
function
|
|
973
|
-
return
|
|
981
|
+
function wallCutMessage(ms) {
|
|
982
|
+
return `exec exceeded ${ms}ms`;
|
|
983
|
+
}
|
|
984
|
+
function boundCut(first) {
|
|
985
|
+
return first.cut === "wall" ? { code: "timeout", message: wallCutMessage(first.ms) } : buildLivenessCut(first.cut);
|
|
974
986
|
}
|
|
975
987
|
function providerTimedOut(e) {
|
|
976
988
|
return e?.name === "TimeoutError" || /timeout|timed out/i.test(errMsg(e));
|
|
@@ -314,17 +314,17 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
314
314
|
canonicalPath(p: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
|
|
315
315
|
readLink(p: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
|
|
316
316
|
/**
|
|
317
|
-
* S-362 / core 7.21.0 [ref] —— **实现**(表态:本腿实现 `canonicalChain`)。host 腿是 node:fs
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* (UNC)时**记下该跳并停止探测**(不碰网络,CC `vr` 对网络拼法只给拼法)。
|
|
317
|
+
* S-362 / core 7.21.0 [ref] —— **实现**(表态:本腿实现 `canonicalChain`)。host 腿是 node:fs 腿:走法(对整条拼法 `readlink`、再对每一跳
|
|
318
|
+
* `readlink`,**上限**、**只锚不折**、**遇环即止**、win32 宿主上跳到共享(UNC)时**记下该跳并停止探测** —— 不碰网络)是 core 导出的
|
|
319
|
+
* `walkSymlinkChain`,本腿只供它**逐跳的读口**;上限、播种、锚法、UNC 判都在那一只里,本仓不再重写第二遍。
|
|
321
320
|
*
|
|
322
|
-
* 🔴
|
|
323
|
-
*
|
|
324
|
-
*
|
|
321
|
+
* 🔴 为什么本腿仍自备读口而不是整体复用 core 的 `NodeExecutionEnv.canonicalChain`:本腿的每一次读都必须先过 {@link fsReady}
|
|
322
|
+
* (2026-07-25 全族统一:拆除之后只读面不得与写面自相矛盾),那道门在 core 的实现里不存在 —— 门在**起点**过一次,读口不再逐跳过
|
|
323
|
+
* (中途 destroy 不改变「这一次链读」的读数:每跳是同一次往返里对本机盘的读,与旧循环逐字同姿势)。
|
|
325
324
|
*
|
|
326
|
-
* 链尽的三种 errno(`EINVAL` 不是软链 / `ENOENT` 不存在 / `ENOTDIR`)
|
|
327
|
-
* (`EACCES` …)⇒ `ok:false`(core 读作「这一次没有跳」,**永不是拒绝**)
|
|
325
|
+
* 链尽的三种 errno(`EINVAL` 不是软链 / `ENOENT` 不存在 / `ENOTDIR`)由本腿的 `fileError` 归成 `invalid` / `not_found` / `not_directory`,
|
|
326
|
+
* core 的走法读作「到此为止」⇒ `ok` + 到此为止的跳;其余 errno(`EACCES` …)⇒ `ok:false`(core 读作「这一次没有跳」,**永不是拒绝**)。
|
|
327
|
+
* 非链接的叶答 `[]`。同形的判据钉在 `test/remote-env-canonical-chain.test.ts`,「读 core 导出」的钉在 `test/core-export-single-source-*.test.ts`。
|
|
328
328
|
*/
|
|
329
329
|
canonicalChain(p: string, abortSignal?: AbortSignal): Promise<Result<SymlinkChain, FileError>>;
|
|
330
330
|
exists(p: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;
|
|
@@ -5,12 +5,12 @@ import fs from "node:fs/promises";
|
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import { openSync, closeSync, readSync, statSync, truncateSync, unlinkSync, mkdirSync, rmdirSync, mkdtempSync, existsSync, realpathSync, accessSync, constants as FS_CONST } from "node:fs";
|
|
7
7
|
import { StringDecoder } from "node:string_decoder";
|
|
8
|
-
import { armPipeDestroyGrace, numEnvOr
|
|
9
|
-
import {
|
|
8
|
+
import { armPipeDestroyGrace, numEnvOr } from "./remote-shell.js";
|
|
9
|
+
import { buildLivenessCut, commandBoundsOf } from "../exec-bounds.js";
|
|
10
10
|
import { hostBackgroundShellEnabled, hostExecSpoolEnabled } from "../config.js";
|
|
11
11
|
import { IS_WIN32, resolveHostShell, hostShell, pinHostShell, spawnGroupOptions, killTreeHard, killTreeSoft, hardKillGroupNow, collapseWin32EnvKeys, adoptHostForegroundChild } from "./host-platform.js";
|
|
12
12
|
import { BackgroundShellManager, seedMemStream, feedMemStream, drainMemStream } from "./background-shell-support.js";
|
|
13
|
-
import { FileError, NodeExecutionEnv, inheritEnvPolicyProblem, ExecutionError, RemoteExecutionError, RollingTailBuffer, markTruncated, SchedulerError, BackgroundShellError, } from "@sema-agent/core";
|
|
13
|
+
import { FileError, NodeExecutionEnv, inheritEnvPolicyProblem, ExecutionError, RemoteExecutionError, RollingTailBuffer, walkSymlinkChain, markTruncated, SchedulerError, BackgroundShellError, } from "@sema-agent/core";
|
|
14
14
|
import { composeHostSubprocessEnv, hostSubprocessEnv } from "../server-secret-env.js";
|
|
15
15
|
import { trackEphemeralHostWorkspace, untrackEphemeralHostWorkspace } from "./host-workspace-registry.js";
|
|
16
16
|
const PROVIDER = "host";
|
|
@@ -24,7 +24,6 @@ const HOST_BG_FILE_CAP = numEnvOr("HOST_BG_FILE_CAP", 64 * 1024 * 1024, HOST_BG_
|
|
|
24
24
|
export const HOST_BG_KILL_GRACE_MS = numEnvOr("HOST_BG_KILL_GRACE_MS", 1000, 0);
|
|
25
25
|
const HOST_BG_MEM_CAP = numEnvOr("HOST_BG_MEM_CAP", 8 * 1024 * 1024, 64 * 1024);
|
|
26
26
|
const ok = (value) => ({ ok: true, value });
|
|
27
|
-
const UNC_SPELLING = /^[\\/]{2}[^\\/]+[\\/]+[^\\/]/;
|
|
28
27
|
const unsupported = (op) => ({
|
|
29
28
|
ok: false,
|
|
30
29
|
error: new RemoteExecutionError("unsupported", `${op} is not supported on a host target (the operator's own machine — no container boundary, not snapshotable; capabilities.suspendable=false)`),
|
|
@@ -919,7 +918,8 @@ export class RemoteHostExecutionEnv {
|
|
|
919
918
|
if (liveness !== undefined && !finished && Date.now() - lastChunk >= liveness.ms) {
|
|
920
919
|
finished = true;
|
|
921
920
|
kill();
|
|
922
|
-
|
|
921
|
+
const cut = buildLivenessCut(liveness);
|
|
922
|
+
throw new RemoteExecutionError(cut.code, cut.message);
|
|
923
923
|
}
|
|
924
924
|
}
|
|
925
925
|
while (queue.length)
|
|
@@ -1099,36 +1099,14 @@ export class RemoteHostExecutionEnv {
|
|
|
1099
1099
|
const ready = await this.fsReady(p);
|
|
1100
1100
|
if (!ready.ok)
|
|
1101
1101
|
return ready;
|
|
1102
|
-
|
|
1103
|
-
let cur = this.resolve(p);
|
|
1104
|
-
const seen = new Set([cur]);
|
|
1105
|
-
for (let i = 0; i < SYMLINK_CHAIN_MAX_HOPS; i += 1) {
|
|
1106
|
-
let target;
|
|
1102
|
+
return walkSymlinkChain(this.resolve(p), async (hop) => {
|
|
1107
1103
|
try {
|
|
1108
|
-
|
|
1104
|
+
return ok(await fs.readlink(hop));
|
|
1109
1105
|
}
|
|
1110
1106
|
catch (e) {
|
|
1111
|
-
|
|
1112
|
-
if (fe.code === "invalid" || fe.code === "not_found" || fe.code === "not_directory")
|
|
1113
|
-
break;
|
|
1114
|
-
return { ok: false, error: fe };
|
|
1107
|
+
return { ok: false, error: this.fileError(e, hop, abortSignal) };
|
|
1115
1108
|
}
|
|
1116
|
-
|
|
1117
|
-
if (path.isAbsolute(target))
|
|
1118
|
-
next = target;
|
|
1119
|
-
else {
|
|
1120
|
-
const dir = path.dirname(cur);
|
|
1121
|
-
next = dir.endsWith(path.sep) ? `${dir}${target}` : `${dir}${path.sep}${target}`;
|
|
1122
|
-
}
|
|
1123
|
-
if (seen.has(next))
|
|
1124
|
-
break;
|
|
1125
|
-
seen.add(next);
|
|
1126
|
-
hops.push(next);
|
|
1127
|
-
if (IS_WIN32 && UNC_SPELLING.test(next))
|
|
1128
|
-
break;
|
|
1129
|
-
cur = next;
|
|
1130
|
-
}
|
|
1131
|
-
return ok({ hops });
|
|
1109
|
+
});
|
|
1132
1110
|
}
|
|
1133
1111
|
async exists(p, abortSignal) {
|
|
1134
1112
|
const ready = await this.fsReady(p);
|
|
@@ -1325,7 +1303,7 @@ export class RemoteHostExecutionEnv {
|
|
|
1325
1303
|
}
|
|
1326
1304
|
childEnv(perCommand, spawnShell) {
|
|
1327
1305
|
const base = spawnShell !== undefined
|
|
1328
|
-
? hostSubprocessEnv({ site: "server.remote-env-host.
|
|
1306
|
+
? hostSubprocessEnv({ site: "server.remote-env-host.spawn-env", policy: this.cfg.inheritEnv, allow: [] })
|
|
1329
1307
|
: composeHostSubprocessEnv({ policy: this.cfg.inheritEnv, allow: [] });
|
|
1330
1308
|
if (this.cfg.env)
|
|
1331
1309
|
Object.assign(base, this.cfg.env);
|