@sema-agent/server 1.314.0 → 1.316.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/dist/approval-hmac.d.ts +17 -0
- package/dist/approval-hmac.js +27 -0
- package/dist/config-center/apply-effective.d.ts +22 -0
- package/dist/config-center/apply-effective.js +283 -0
- package/dist/config-center/http-client.d.ts +23 -0
- package/dist/config-center/http-client.js +109 -0
- package/dist/config-center/restart-signal.d.ts +12 -0
- package/dist/config-center/restart-signal.js +70 -0
- package/dist/config-center/skills-mcp.d.ts +13 -0
- package/dist/config-center/skills-mcp.js +113 -0
- package/dist/config-center/types.d.ts +143 -0
- package/dist/config-center/types.js +2 -0
- package/dist/fleet/fleet-bus.js +92 -83
- package/dist/hooks/hook-runner.js +18 -9
- package/dist/http/idempotency.d.ts +1 -0
- package/dist/http/idempotency.js +3 -0
- package/dist/http/principal-gate.d.ts +12 -0
- package/dist/http/principal-gate.js +52 -0
- package/dist/http/route-ctx.d.ts +67 -0
- package/dist/http/route-ctx.js +2 -0
- package/dist/http/routes/attachments.d.ts +4 -0
- package/dist/http/routes/attachments.js +106 -0
- package/dist/http/routes/capabilities.d.ts +4 -0
- package/dist/http/routes/capabilities.js +111 -0
- package/dist/http/routes/images.d.ts +20 -0
- package/dist/http/routes/images.js +572 -0
- package/dist/http/routes/memory-policy.d.ts +4 -0
- package/dist/http/routes/memory-policy.js +105 -0
- package/dist/http/routes/observability.d.ts +4 -0
- package/dist/http/routes/observability.js +63 -0
- package/dist/http/routes/session-sync.d.ts +36 -0
- package/dist/http/routes/session-sync.js +513 -0
- package/dist/http/routes/sessions-list.d.ts +4 -0
- package/dist/http/routes/sessions-list.js +117 -0
- package/dist/http/routes/sessions.d.ts +17 -0
- package/dist/http/routes/sessions.js +746 -0
- package/dist/http/send.d.ts +6 -0
- package/dist/http/send.js +19 -0
- package/dist/http/server.d.ts +101 -76
- package/dist/http/server.js +129 -2410
- package/dist/http/sse-log.d.ts +25 -0
- package/dist/http/sse-log.js +59 -0
- package/dist/http/workspace-content.d.ts +3 -0
- package/dist/http/workspace-content.js +20 -0
- package/dist/index.d.ts +1 -1
- package/dist/leader/leader.js +5 -2
- package/dist/leader/wire.js +169 -165
- package/dist/main.js +462 -451
- package/dist/plugins/approval-store-sql.d.ts +52 -0
- package/dist/plugins/approval-store-sql.js +119 -0
- package/dist/plugins/breaker-state-sql.d.ts +29 -0
- package/dist/plugins/breaker-state-sql.js +104 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +67 -0
- package/dist/plugins/checkpoint-store-sql.js +224 -0
- package/dist/plugins/file-snapshot-store-sql.d.ts +38 -0
- package/dist/plugins/file-snapshot-store-sql.js +210 -0
- package/dist/plugins/image-bake-store-sql.d.ts +53 -0
- package/dist/plugins/image-bake-store-sql.js +463 -0
- package/dist/plugins/image-index-sql.d.ts +33 -0
- package/dist/plugins/image-index-sql.js +328 -0
- package/dist/plugins/k8s-bg-scripts.d.ts +19 -0
- package/dist/plugins/k8s-bg-scripts.js +129 -0
- package/dist/plugins/k8s-exec-protocol.d.ts +20 -0
- package/dist/plugins/k8s-exec-protocol.js +87 -0
- package/dist/plugins/outcome-ledger-sql.d.ts +70 -0
- package/dist/plugins/outcome-ledger-sql.js +158 -0
- package/dist/plugins/pg-approval-store.d.ts +1 -27
- package/dist/plugins/pg-approval-store.js +1 -83
- package/dist/plugins/pg-breaker-state.d.ts +1 -19
- package/dist/plugins/pg-breaker-state.js +1 -88
- package/dist/plugins/pg-checkpoint-store.d.ts +1 -33
- package/dist/plugins/pg-checkpoint-store.js +1 -189
- package/dist/plugins/pg-cost-quota.js +3 -143
- package/dist/plugins/pg-file-snapshot-store.d.ts +1 -27
- package/dist/plugins/pg-file-snapshot-store.js +1 -190
- package/dist/plugins/pg-image-bake.d.ts +1 -40
- package/dist/plugins/pg-image-bake.js +1 -420
- package/dist/plugins/pg-image-index.d.ts +1 -21
- package/dist/plugins/pg-image-index.js +1 -217
- package/dist/plugins/pg-outcome-ledger.d.ts +2 -22
- package/dist/plugins/pg-outcome-ledger.js +1 -104
- package/dist/plugins/pg-rate-limiter.d.ts +2 -32
- package/dist/plugins/pg-rate-limiter.js +4 -147
- package/dist/plugins/pg-resume-anchor-store.d.ts +1 -13
- package/dist/plugins/pg-resume-anchor-store.js +1 -22
- package/dist/plugins/pg-run-store.d.ts +1 -75
- package/dist/plugins/pg-run-store.js +1 -287
- package/dist/plugins/pg-session-policy-store.d.ts +1 -11
- package/dist/plugins/pg-session-policy-store.js +1 -90
- package/dist/plugins/pg-tool-result-store.d.ts +1 -15
- package/dist/plugins/pg-tool-result-store.js +1 -52
- package/dist/plugins/pg-workflow-journal-store.d.ts +1 -20
- package/dist/plugins/pg-workflow-journal-store.js +1 -34
- package/dist/plugins/pg-workflow-run-store.d.ts +1 -55
- package/dist/plugins/pg-workflow-run-store.js +1 -220
- package/dist/plugins/remote-env-k8s.d.ts +5 -38
- package/dist/plugins/remote-env-k8s.js +7 -213
- package/dist/plugins/resume-anchor-store-sql.d.ts +23 -0
- package/dist/plugins/resume-anchor-store-sql.js +38 -0
- package/dist/plugins/run-store-sql.d.ts +89 -0
- package/dist/plugins/run-store-sql.js +397 -0
- package/dist/plugins/session-policy-store-sql.d.ts +22 -0
- package/dist/plugins/session-policy-store-sql.js +109 -0
- package/dist/plugins/sql-driver.d.ts +25 -0
- package/dist/plugins/sql-driver.js +59 -0
- package/dist/plugins/tidb-approval-store.d.ts +1 -39
- package/dist/plugins/tidb-approval-store.js +1 -71
- package/dist/plugins/tidb-breaker-state.d.ts +1 -17
- package/dist/plugins/tidb-breaker-state.js +1 -74
- package/dist/plugins/tidb-checkpoint-store.d.ts +1 -49
- package/dist/plugins/tidb-checkpoint-store.js +1 -191
- package/dist/plugins/tidb-file-snapshot-store.d.ts +1 -27
- package/dist/plugins/tidb-file-snapshot-store.js +1 -192
- package/dist/plugins/tidb-image-bake.d.ts +1 -38
- package/dist/plugins/tidb-image-bake.js +1 -348
- package/dist/plugins/tidb-image-index.d.ts +1 -19
- package/dist/plugins/tidb-image-index.js +1 -149
- package/dist/plugins/tidb-outcome-ledger.d.ts +1 -57
- package/dist/plugins/tidb-outcome-ledger.js +1 -97
- package/dist/plugins/tidb-resume-anchor-store.d.ts +1 -13
- package/dist/plugins/tidb-resume-anchor-store.js +1 -22
- package/dist/plugins/tidb-run-store.d.ts +1 -75
- package/dist/plugins/tidb-run-store.js +1 -306
- package/dist/plugins/tidb-session-policy-store.d.ts +1 -11
- package/dist/plugins/tidb-session-policy-store.js +1 -86
- package/dist/plugins/tidb-tool-result-store.d.ts +1 -13
- package/dist/plugins/tidb-tool-result-store.js +1 -38
- package/dist/plugins/tidb-workflow-journal-store.d.ts +1 -20
- package/dist/plugins/tidb-workflow-journal-store.js +1 -34
- package/dist/plugins/tidb-workflow-run-store.d.ts +1 -61
- package/dist/plugins/tidb-workflow-run-store.js +1 -231
- package/dist/plugins/tool-result-store-sql.d.ts +25 -0
- package/dist/plugins/tool-result-store-sql.js +69 -0
- package/dist/plugins/workflow-journal-store-sql.d.ts +30 -0
- package/dist/plugins/workflow-journal-store-sql.js +53 -0
- package/dist/plugins/workflow-run-store-sql.d.ts +85 -0
- package/dist/plugins/workflow-run-store-sql.js +299 -0
- package/dist/plugins/write-behind-counter.d.ts +14 -3
- package/dist/plugins/write-behind-counter.js +39 -12
- package/dist/principal-jwt.d.ts +44 -0
- package/dist/principal-jwt.js +95 -0
- package/dist/security.d.ts +2 -58
- package/dist/security.js +3 -118
- package/dist/sema-registry.d.ts +5 -200
- package/dist/sema-registry.js +4 -567
- package/package.json +1 -1
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { pgSafeJsonStringify, pgSanitizeText, pgHasUnstorable, PgUnstorableError } from "./pg-safe-json.js";
|
|
3
|
+
import { mapImageRow as mapRow, } from "./store-contracts.js";
|
|
4
|
+
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
5
|
+
export { mapImageRow as mapRow } from "./store-contracts.js";
|
|
6
|
+
export const PG_IMAGE_INDEX_SCHEMA = [
|
|
7
|
+
`CREATE TABLE IF NOT EXISTS sandbox_image_index (
|
|
8
|
+
id VARCHAR(64) NOT NULL,
|
|
9
|
+
profile VARCHAR(64) NOT NULL,
|
|
10
|
+
bands JSONB,
|
|
11
|
+
repo VARCHAR(255) NOT NULL,
|
|
12
|
+
tag VARCHAR(128) NOT NULL,
|
|
13
|
+
digest VARCHAR(80) NOT NULL,
|
|
14
|
+
toolchain_versions JSONB,
|
|
15
|
+
capabilities JSONB,
|
|
16
|
+
pod_contract JSONB,
|
|
17
|
+
size_bytes BIGINT,
|
|
18
|
+
status VARCHAR(16) NOT NULL DEFAULT 'building',
|
|
19
|
+
visibility VARCHAR(16) NOT NULL DEFAULT 'public',
|
|
20
|
+
tenant_id VARCHAR(64),
|
|
21
|
+
manifest_sha VARCHAR(64),
|
|
22
|
+
recipe_git_sha VARCHAR(64),
|
|
23
|
+
generator_version VARCHAR(32),
|
|
24
|
+
build_date TIMESTAMPTZ(3),
|
|
25
|
+
supersedes VARCHAR(64),
|
|
26
|
+
signed SMALLINT NOT NULL DEFAULT 0,
|
|
27
|
+
created_at TIMESTAMPTZ(3) NOT NULL,
|
|
28
|
+
updated_at TIMESTAMPTZ(3) NOT NULL,
|
|
29
|
+
PRIMARY KEY (id)
|
|
30
|
+
)`,
|
|
31
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS uq_repo_digest ON sandbox_image_index (repo, digest)`,
|
|
32
|
+
`CREATE INDEX IF NOT EXISTS idx_profile_status ON sandbox_image_index (profile, status, visibility)`,
|
|
33
|
+
`CREATE INDEX IF NOT EXISTS idx_digest ON sandbox_image_index (digest)`,
|
|
34
|
+
`CREATE INDEX IF NOT EXISTS idx_tenant ON sandbox_image_index (tenant_id, status)`,
|
|
35
|
+
];
|
|
36
|
+
export async function ensureSchema(pool) {
|
|
37
|
+
for (const stmt of PG_IMAGE_INDEX_SCHEMA)
|
|
38
|
+
await pool.query(stmt);
|
|
39
|
+
}
|
|
40
|
+
const MAX_LIMIT = 200;
|
|
41
|
+
const DEFAULT_LIMIT = 50;
|
|
42
|
+
function visibilityClauseTidb(viewer) {
|
|
43
|
+
if (viewer.operator)
|
|
44
|
+
return { sql: "1=1", params: [] };
|
|
45
|
+
if (viewer.tenantId)
|
|
46
|
+
return { sql: "(visibility='public' OR tenant_id=?)", params: [viewer.tenantId] };
|
|
47
|
+
return { sql: "visibility='public'", params: [] };
|
|
48
|
+
}
|
|
49
|
+
function visibilityClausePg(viewer, startIdx) {
|
|
50
|
+
if (viewer.operator)
|
|
51
|
+
return { sql: "1=1", params: [], nextIdx: startIdx };
|
|
52
|
+
if (viewer.tenantId)
|
|
53
|
+
return {
|
|
54
|
+
sql: `(visibility = 'public' OR tenant_id = $${startIdx})`,
|
|
55
|
+
params: [viewer.tenantId],
|
|
56
|
+
nextIdx: startIdx + 1,
|
|
57
|
+
};
|
|
58
|
+
return { sql: "visibility = 'public'", params: [], nextIdx: startIdx };
|
|
59
|
+
}
|
|
60
|
+
function encodeCursor(e) {
|
|
61
|
+
return Buffer.from(`${e.createdAt}|${e.id}`, "utf8").toString("base64url");
|
|
62
|
+
}
|
|
63
|
+
function decodeCursor(c) {
|
|
64
|
+
try {
|
|
65
|
+
const [createdAt, id] = Buffer.from(c, "base64url").toString("utf8").split("|");
|
|
66
|
+
if (!createdAt || !id)
|
|
67
|
+
return null;
|
|
68
|
+
return { createdAt, id };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function tidbUpsertParams(e, id, now) {
|
|
75
|
+
return [
|
|
76
|
+
id, e.profile, JSON.stringify(e.bands ?? []), e.repo, e.tag, e.digest,
|
|
77
|
+
JSON.stringify(e.toolchainVersions ?? {}), JSON.stringify(e.capabilities ?? {}),
|
|
78
|
+
JSON.stringify(e.podContract ?? {}), e.sizeBytes,
|
|
79
|
+
e.status, e.visibility, e.tenantId ?? null, e.manifestSha ?? null, e.recipeGitSha ?? null,
|
|
80
|
+
e.generatorVersion ?? null, e.buildDate ? new Date(e.buildDate) : null, e.supersedes ?? null,
|
|
81
|
+
e.signed ? 1 : 0, now, now,
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
const PG_UPSERT_COLS = "(id, profile, bands, repo, tag, digest, toolchain_versions, capabilities, pod_contract, size_bytes, " +
|
|
85
|
+
"status, visibility, tenant_id, manifest_sha, recipe_git_sha, generator_version, build_date, supersedes, " +
|
|
86
|
+
"signed, created_at, updated_at)";
|
|
87
|
+
const PG_UPSERT_VALUES = "($1,$2,$3::jsonb,$4,$5,$6,$7::jsonb,$8::jsonb,$9::jsonb,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21)";
|
|
88
|
+
function pgUpsertParams(e, id, now) {
|
|
89
|
+
return [
|
|
90
|
+
id,
|
|
91
|
+
pgSanitizeText(e.profile),
|
|
92
|
+
pgSafeJsonStringify(e.bands ?? []),
|
|
93
|
+
e.repo,
|
|
94
|
+
pgSanitizeText(e.tag),
|
|
95
|
+
e.digest,
|
|
96
|
+
pgSafeJsonStringify(e.toolchainVersions ?? {}),
|
|
97
|
+
pgSafeJsonStringify(e.capabilities ?? {}),
|
|
98
|
+
pgSafeJsonStringify(e.podContract ?? {}),
|
|
99
|
+
e.sizeBytes,
|
|
100
|
+
e.status,
|
|
101
|
+
e.visibility,
|
|
102
|
+
e.tenantId ?? null,
|
|
103
|
+
e.manifestSha ?? null,
|
|
104
|
+
e.recipeGitSha == null ? null : pgSanitizeText(e.recipeGitSha),
|
|
105
|
+
e.generatorVersion == null ? null : pgSanitizeText(e.generatorVersion),
|
|
106
|
+
e.buildDate ? new Date(e.buildDate) : null,
|
|
107
|
+
e.supersedes == null ? null : pgSanitizeText(e.supersedes),
|
|
108
|
+
e.signed ? 1 : 0,
|
|
109
|
+
now,
|
|
110
|
+
now,
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
export class SqlImageIndex {
|
|
114
|
+
db;
|
|
115
|
+
constructor(db) {
|
|
116
|
+
this.db = db;
|
|
117
|
+
}
|
|
118
|
+
q(tidb, pg) {
|
|
119
|
+
return this.db.dialect === "tidb" ? tidb : pg;
|
|
120
|
+
}
|
|
121
|
+
static idFor(repo, digest) {
|
|
122
|
+
return createHash("sha256").update(`${repo}@${digest}`).digest("hex").slice(0, 64);
|
|
123
|
+
}
|
|
124
|
+
async upsert(e) {
|
|
125
|
+
if (this.db.dialect === "pg") {
|
|
126
|
+
if (pgHasUnstorable(e.repo))
|
|
127
|
+
throw new PgUnstorableError("image repo (identity)");
|
|
128
|
+
if (e.tenantId != null && pgHasUnstorable(e.tenantId))
|
|
129
|
+
throw new PgUnstorableError("image tenantId (identity)");
|
|
130
|
+
}
|
|
131
|
+
const id = SqlImageIndex.idFor(e.repo, e.digest);
|
|
132
|
+
const now = new Date();
|
|
133
|
+
await this.db.query(this.q(`INSERT INTO sandbox_image_index
|
|
134
|
+
(id, profile, bands, repo, tag, digest, toolchain_versions, capabilities, pod_contract, size_bytes,
|
|
135
|
+
status, visibility, tenant_id, manifest_sha, recipe_git_sha, generator_version, build_date, supersedes,
|
|
136
|
+
signed, created_at, updated_at)
|
|
137
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
138
|
+
ON DUPLICATE KEY UPDATE
|
|
139
|
+
profile=VALUES(profile), bands=VALUES(bands), tag=VALUES(tag), toolchain_versions=VALUES(toolchain_versions),
|
|
140
|
+
capabilities=VALUES(capabilities), pod_contract=VALUES(pod_contract), size_bytes=VALUES(size_bytes),
|
|
141
|
+
status=VALUES(status), visibility=VALUES(visibility), tenant_id=VALUES(tenant_id),
|
|
142
|
+
manifest_sha=VALUES(manifest_sha), recipe_git_sha=VALUES(recipe_git_sha),
|
|
143
|
+
generator_version=VALUES(generator_version), build_date=VALUES(build_date), supersedes=VALUES(supersedes),
|
|
144
|
+
signed=VALUES(signed), updated_at=VALUES(updated_at)`, `INSERT INTO sandbox_image_index ${PG_UPSERT_COLS}
|
|
145
|
+
VALUES ${PG_UPSERT_VALUES}
|
|
146
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
147
|
+
profile = EXCLUDED.profile, bands = EXCLUDED.bands, tag = EXCLUDED.tag,
|
|
148
|
+
toolchain_versions = EXCLUDED.toolchain_versions, capabilities = EXCLUDED.capabilities,
|
|
149
|
+
pod_contract = EXCLUDED.pod_contract, size_bytes = EXCLUDED.size_bytes,
|
|
150
|
+
status = EXCLUDED.status, visibility = EXCLUDED.visibility, tenant_id = EXCLUDED.tenant_id,
|
|
151
|
+
manifest_sha = EXCLUDED.manifest_sha, recipe_git_sha = EXCLUDED.recipe_git_sha,
|
|
152
|
+
generator_version = EXCLUDED.generator_version, build_date = EXCLUDED.build_date,
|
|
153
|
+
supersedes = EXCLUDED.supersedes, signed = EXCLUDED.signed, updated_at = EXCLUDED.updated_at`), this.db.dialect === "tidb" ? tidbUpsertParams(e, id, now) : pgUpsertParams(e, id, now));
|
|
154
|
+
return id;
|
|
155
|
+
}
|
|
156
|
+
async registerBuilding(e) {
|
|
157
|
+
if (this.db.dialect === "pg") {
|
|
158
|
+
if (pgHasUnstorable(e.repo))
|
|
159
|
+
throw new PgUnstorableError("image repo (identity)");
|
|
160
|
+
if (e.tenantId != null && pgHasUnstorable(e.tenantId))
|
|
161
|
+
throw new PgUnstorableError("image tenantId (identity)");
|
|
162
|
+
}
|
|
163
|
+
const id = SqlImageIndex.idFor(e.repo, e.digest);
|
|
164
|
+
const now = new Date();
|
|
165
|
+
await this.db.query(this.q(`INSERT INTO sandbox_image_index
|
|
166
|
+
(id, profile, bands, repo, tag, digest, toolchain_versions, capabilities, pod_contract, size_bytes,
|
|
167
|
+
status, visibility, tenant_id, manifest_sha, recipe_git_sha, generator_version, build_date, supersedes,
|
|
168
|
+
signed, created_at, updated_at)
|
|
169
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
170
|
+
ON DUPLICATE KEY UPDATE
|
|
171
|
+
profile=VALUES(profile), bands=VALUES(bands), tag=VALUES(tag), toolchain_versions=VALUES(toolchain_versions),
|
|
172
|
+
capabilities=VALUES(capabilities), pod_contract=VALUES(pod_contract), size_bytes=VALUES(size_bytes),
|
|
173
|
+
status=IF(sandbox_image_index.status='published', sandbox_image_index.status, VALUES(status)),
|
|
174
|
+
visibility=IF(sandbox_image_index.status='published', sandbox_image_index.visibility, VALUES(visibility)),
|
|
175
|
+
tenant_id=IF(sandbox_image_index.status='published', sandbox_image_index.tenant_id, VALUES(tenant_id)),
|
|
176
|
+
manifest_sha=VALUES(manifest_sha), recipe_git_sha=VALUES(recipe_git_sha),
|
|
177
|
+
generator_version=VALUES(generator_version), build_date=VALUES(build_date), supersedes=VALUES(supersedes),
|
|
178
|
+
signed=IF(sandbox_image_index.status='published', sandbox_image_index.signed, VALUES(signed)), updated_at=VALUES(updated_at)`, `INSERT INTO sandbox_image_index ${PG_UPSERT_COLS}
|
|
179
|
+
VALUES ${PG_UPSERT_VALUES}
|
|
180
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
181
|
+
profile = EXCLUDED.profile, bands = EXCLUDED.bands, tag = EXCLUDED.tag,
|
|
182
|
+
toolchain_versions = EXCLUDED.toolchain_versions, capabilities = EXCLUDED.capabilities,
|
|
183
|
+
pod_contract = EXCLUDED.pod_contract, size_bytes = EXCLUDED.size_bytes,
|
|
184
|
+
status = CASE WHEN sandbox_image_index.status = 'published'
|
|
185
|
+
THEN sandbox_image_index.status ELSE EXCLUDED.status END,
|
|
186
|
+
visibility = CASE WHEN sandbox_image_index.status = 'published'
|
|
187
|
+
THEN sandbox_image_index.visibility ELSE EXCLUDED.visibility END,
|
|
188
|
+
tenant_id = CASE WHEN sandbox_image_index.status = 'published'
|
|
189
|
+
THEN sandbox_image_index.tenant_id ELSE EXCLUDED.tenant_id END,
|
|
190
|
+
manifest_sha = EXCLUDED.manifest_sha, recipe_git_sha = EXCLUDED.recipe_git_sha,
|
|
191
|
+
generator_version = EXCLUDED.generator_version, build_date = EXCLUDED.build_date,
|
|
192
|
+
supersedes = EXCLUDED.supersedes,
|
|
193
|
+
signed = CASE WHEN sandbox_image_index.status = 'published'
|
|
194
|
+
THEN sandbox_image_index.signed ELSE EXCLUDED.signed END,
|
|
195
|
+
updated_at = EXCLUDED.updated_at`), this.db.dialect === "tidb" ? tidbUpsertParams(e, id, now) : pgUpsertParams(e, id, now));
|
|
196
|
+
return id;
|
|
197
|
+
}
|
|
198
|
+
async getById(id) {
|
|
199
|
+
const { rows } = await this.db.query(this.q("SELECT * FROM sandbox_image_index WHERE id=? LIMIT 1", "SELECT * FROM sandbox_image_index WHERE id = $1 LIMIT 1"), [id]);
|
|
200
|
+
return rows[0] ? mapRow(rows[0]) : null;
|
|
201
|
+
}
|
|
202
|
+
async getByDigest(digest) {
|
|
203
|
+
const { rows } = await this.db.query(this.q("SELECT * FROM sandbox_image_index WHERE digest=? ORDER BY updated_at DESC LIMIT 1", "SELECT * FROM sandbox_image_index WHERE digest = $1 ORDER BY updated_at DESC LIMIT 1"), [digest]);
|
|
204
|
+
return rows[0] ? mapRow(rows[0]) : null;
|
|
205
|
+
}
|
|
206
|
+
async latestPublished(profile, viewer) {
|
|
207
|
+
if (this.db.dialect === "tidb") {
|
|
208
|
+
const vis = visibilityClauseTidb(viewer);
|
|
209
|
+
const { rows } = await this.db.query(`SELECT * FROM sandbox_image_index
|
|
210
|
+
WHERE profile=? AND status='published' AND ${vis.sql}
|
|
211
|
+
ORDER BY (build_date IS NULL), build_date DESC, created_at DESC LIMIT 1`, [profile, ...vis.params]);
|
|
212
|
+
return rows[0] ? mapRow(rows[0]) : null;
|
|
213
|
+
}
|
|
214
|
+
const vis = visibilityClausePg(viewer, 2);
|
|
215
|
+
const { rows } = await this.db.query(`SELECT * FROM sandbox_image_index
|
|
216
|
+
WHERE profile = $1 AND status = 'published' AND ${vis.sql}
|
|
217
|
+
ORDER BY build_date DESC NULLS LAST, created_at DESC LIMIT 1`, [profile, ...vis.params]);
|
|
218
|
+
return rows[0] ? mapRow(rows[0]) : null;
|
|
219
|
+
}
|
|
220
|
+
async list(filter) {
|
|
221
|
+
const limit = Math.min(Math.max(1, filter.limit ?? DEFAULT_LIMIT), MAX_LIMIT);
|
|
222
|
+
let rawRows;
|
|
223
|
+
if (this.db.dialect === "tidb") {
|
|
224
|
+
const vis = visibilityClauseTidb(filter);
|
|
225
|
+
const where = [vis.sql];
|
|
226
|
+
const params = [...vis.params];
|
|
227
|
+
if (filter.profile) {
|
|
228
|
+
where.push("profile=?");
|
|
229
|
+
params.push(filter.profile);
|
|
230
|
+
}
|
|
231
|
+
if (filter.status) {
|
|
232
|
+
where.push("status=?");
|
|
233
|
+
params.push(filter.status);
|
|
234
|
+
}
|
|
235
|
+
if (filter.capability) {
|
|
236
|
+
where.push("JSON_EXTRACT(capabilities, ?) = true");
|
|
237
|
+
params.push(`$.${filter.capability}`);
|
|
238
|
+
}
|
|
239
|
+
if (filter.cursor) {
|
|
240
|
+
const c = decodeCursor(filter.cursor);
|
|
241
|
+
if (c) {
|
|
242
|
+
where.push("(created_at < ? OR (created_at = ? AND id < ?))");
|
|
243
|
+
params.push(new Date(c.createdAt), new Date(c.createdAt), c.id);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const sql = `SELECT * FROM sandbox_image_index WHERE ${where.join(" AND ")} ` +
|
|
247
|
+
`ORDER BY created_at DESC, id DESC LIMIT ?`;
|
|
248
|
+
const { rows } = await this.db.query(sql, [...params, limit + 1]);
|
|
249
|
+
rawRows = rows;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
const vis = visibilityClausePg(filter, 1);
|
|
253
|
+
const where = [vis.sql];
|
|
254
|
+
const params = [...vis.params];
|
|
255
|
+
let idx = vis.nextIdx;
|
|
256
|
+
if (filter.profile) {
|
|
257
|
+
where.push(`profile = $${idx++}`);
|
|
258
|
+
params.push(filter.profile);
|
|
259
|
+
}
|
|
260
|
+
if (filter.status) {
|
|
261
|
+
where.push(`status = $${idx++}`);
|
|
262
|
+
params.push(filter.status);
|
|
263
|
+
}
|
|
264
|
+
if (filter.capability) {
|
|
265
|
+
where.push(`capabilities -> $${idx++} = 'true'::jsonb`);
|
|
266
|
+
params.push(filter.capability);
|
|
267
|
+
}
|
|
268
|
+
if (filter.cursor) {
|
|
269
|
+
const c = decodeCursor(filter.cursor);
|
|
270
|
+
if (c) {
|
|
271
|
+
where.push(`(created_at < $${idx} OR (created_at = $${idx + 1} AND id < $${idx + 2}))`);
|
|
272
|
+
params.push(new Date(c.createdAt), new Date(c.createdAt), c.id);
|
|
273
|
+
idx += 3;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const sql = `SELECT * FROM sandbox_image_index WHERE ${where.join(" AND ")} ` +
|
|
277
|
+
`ORDER BY created_at DESC, id DESC LIMIT $${idx}`;
|
|
278
|
+
params.push(limit + 1);
|
|
279
|
+
const { rows } = await this.db.query(sql, params);
|
|
280
|
+
rawRows = rows;
|
|
281
|
+
}
|
|
282
|
+
let entries = rawRows.map(mapRow);
|
|
283
|
+
let nextCursor = null;
|
|
284
|
+
if (entries.length > limit) {
|
|
285
|
+
entries = entries.slice(0, limit);
|
|
286
|
+
const last = entries[entries.length - 1];
|
|
287
|
+
nextCursor = encodeCursor({ createdAt: last.createdAt, id: last.id });
|
|
288
|
+
}
|
|
289
|
+
if (filter.latestOnly) {
|
|
290
|
+
const seen = new Set();
|
|
291
|
+
entries = entries.filter((e) => (seen.has(e.profile) ? false : (seen.add(e.profile), true)));
|
|
292
|
+
}
|
|
293
|
+
return { entries, nextCursor };
|
|
294
|
+
}
|
|
295
|
+
async setStatus(id, status, opts) {
|
|
296
|
+
if (this.db.dialect === "tidb") {
|
|
297
|
+
const sets = ["status=?", "updated_at=?"];
|
|
298
|
+
const params = [status, new Date()];
|
|
299
|
+
if (opts && "supersedes" in opts) {
|
|
300
|
+
sets.push("supersedes=?");
|
|
301
|
+
params.push(opts.supersedes ?? null);
|
|
302
|
+
}
|
|
303
|
+
params.push(id);
|
|
304
|
+
await this.db.query(`UPDATE sandbox_image_index SET ${sets.join(", ")} WHERE id=?`, params);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const sets = ["status = $1", "updated_at = $2"];
|
|
308
|
+
const params = [status, new Date()];
|
|
309
|
+
let idx = 3;
|
|
310
|
+
if (opts && "supersedes" in opts) {
|
|
311
|
+
sets.push(`supersedes = $${idx++}`);
|
|
312
|
+
params.push(opts.supersedes ?? null);
|
|
313
|
+
}
|
|
314
|
+
params.push(id);
|
|
315
|
+
await this.db.query(`UPDATE sandbox_image_index SET ${sets.join(", ")} WHERE id = $${idx}`, params);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
export class TiDBImageIndex extends SqlImageIndex {
|
|
319
|
+
constructor(pool) {
|
|
320
|
+
super(mysqlDriver(pool));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
export class PgImageIndex extends SqlImageIndex {
|
|
324
|
+
constructor(pool) {
|
|
325
|
+
super(pgDriver(pool));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
//# sourceMappingURL=image-index-sql.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare function buildBgRunnerScript(dir: string): string;
|
|
2
|
+
export declare function buildBgLauncherScript(dir: string, cmdShB64: string, runnerShB64: string): string;
|
|
3
|
+
export declare function buildBgPollScript(dir: string, stdoutCursor: number, stderrCursor: number, readCap: number): string;
|
|
4
|
+
export declare function buildBgKillScript(dir: string): string;
|
|
5
|
+
export declare function buildBgDisposeScript(dir: string): string;
|
|
6
|
+
export declare function buildDetachCapableExec(dir: string, command: string): string;
|
|
7
|
+
export interface ParsedBgPoll {
|
|
8
|
+
gone: boolean;
|
|
9
|
+
outTotal: number;
|
|
10
|
+
errTotal: number;
|
|
11
|
+
exitCode?: number;
|
|
12
|
+
alive: boolean;
|
|
13
|
+
stdout: string;
|
|
14
|
+
stdoutBytes: number;
|
|
15
|
+
stderr: string;
|
|
16
|
+
stderrBytes: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function parseBgPollOutput(raw: string): ParsedBgPoll | undefined;
|
|
19
|
+
//# sourceMappingURL=k8s-bg-scripts.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { shellQuote } from "./remote-shell.js";
|
|
2
|
+
export function buildBgRunnerScript(dir) {
|
|
3
|
+
const q = (name) => shellQuote(`${dir}/${name}`);
|
|
4
|
+
return [
|
|
5
|
+
`echo $$ > ${q("pgid.tmp")} 2>/dev/null && mv ${q("pgid.tmp")} ${q("pgid")} 2>/dev/null`,
|
|
6
|
+
`sh ${q("cmd.sh")} > ${q("out")} 2> ${q("err")} < /dev/null`,
|
|
7
|
+
`__RC=$?`,
|
|
8
|
+
`echo $__RC > ${q("exit.tmp")} 2>/dev/null && mv ${q("exit.tmp")} ${q("exit")} 2>/dev/null`,
|
|
9
|
+
].join("\n");
|
|
10
|
+
}
|
|
11
|
+
export function buildBgLauncherScript(dir, cmdShB64, runnerShB64) {
|
|
12
|
+
const d = shellQuote(dir);
|
|
13
|
+
const q = (name) => shellQuote(`${dir}/${name}`);
|
|
14
|
+
return [
|
|
15
|
+
`mkdir -p ${d} && chmod 700 ${d}`,
|
|
16
|
+
`printf %s '${cmdShB64}' | base64 -d > ${q("cmd.sh")}`,
|
|
17
|
+
`printf %s '${runnerShB64}' | base64 -d > ${q("runner.sh")}`,
|
|
18
|
+
`setsid sh ${q("runner.sh")} >/dev/null 2>&1 &`,
|
|
19
|
+
`__i=0; while [ ! -s ${q("pgid")} ] && [ $__i -lt 40 ]; do __i=$((__i+1)); sleep 0.05; done`,
|
|
20
|
+
`if [ -s ${q("pgid")} ]; then echo SEMABG_LAUNCHED; else echo SEMABG_NOPGID; fi`,
|
|
21
|
+
].join("\n");
|
|
22
|
+
}
|
|
23
|
+
export function buildBgPollScript(dir, stdoutCursor, stderrCursor, readCap) {
|
|
24
|
+
const d = shellQuote(dir);
|
|
25
|
+
const slice = (file, cursor) => [
|
|
26
|
+
`__N=$((__T - ${cursor}))`,
|
|
27
|
+
`if [ "$__N" -gt 0 ]; then`,
|
|
28
|
+
` if [ "$__N" -le ${readCap} ]; then tail -c +$(( ${cursor} + 1 )) ${d}/${file} 2>/dev/null | head -c "$__N" | base64;`,
|
|
29
|
+
` else tail -c +$(( __T - ${readCap} + 1 )) ${d}/${file} 2>/dev/null | head -c ${readCap} | base64; fi`,
|
|
30
|
+
`fi`,
|
|
31
|
+
].join("\n");
|
|
32
|
+
return [
|
|
33
|
+
`if [ ! -d ${d} ]; then echo SEMABG_GONE; exit 0; fi`,
|
|
34
|
+
`__OT=$(wc -c < ${d}/out 2>/dev/null); __OT=$(( \${__OT:-0} + 0 ))`,
|
|
35
|
+
`__ET=$(wc -c < ${d}/err 2>/dev/null); __ET=$(( \${__ET:-0} + 0 ))`,
|
|
36
|
+
`__EX=$( [ -f ${d}/exit ] && cat ${d}/exit 2>/dev/null || echo - )`,
|
|
37
|
+
`__PG=$(cat ${d}/pgid 2>/dev/null); __PG=\${__PG:--}`,
|
|
38
|
+
`if [ "$__PG" = "-" ]; then __AL=1; elif kill -0 -"$__PG" 2>/dev/null; then __AL=1; else __AL=0; fi`,
|
|
39
|
+
`echo "SEMABG_META o=$__OT e=$__ET x=$__EX a=$__AL"`,
|
|
40
|
+
`echo SEMABG_O`,
|
|
41
|
+
`__T=$__OT`,
|
|
42
|
+
slice("out", stdoutCursor),
|
|
43
|
+
`echo SEMABG_E`,
|
|
44
|
+
`__T=$__ET`,
|
|
45
|
+
slice("err", stderrCursor),
|
|
46
|
+
`echo SEMABG_X`,
|
|
47
|
+
].join("\n");
|
|
48
|
+
}
|
|
49
|
+
export function buildBgKillScript(dir) {
|
|
50
|
+
const d = shellQuote(dir);
|
|
51
|
+
return [
|
|
52
|
+
`__i=0; while [ ! -s ${d}/pgid ] && [ $__i -lt 40 ]; do __i=$((__i+1)); sleep 0.05; done`,
|
|
53
|
+
`__PG=$(cat ${d}/pgid 2>/dev/null); __PG=\${__PG:--}`,
|
|
54
|
+
`if [ "$__PG" != "-" ]; then kill -TERM -"$__PG" 2>/dev/null || kill -TERM "$__PG" 2>/dev/null; sleep 1; kill -KILL -"$__PG" 2>/dev/null || kill -KILL "$__PG" 2>/dev/null; fi`,
|
|
55
|
+
`echo SEMABG_KILLED`,
|
|
56
|
+
].join("\n");
|
|
57
|
+
}
|
|
58
|
+
export function buildBgDisposeScript(dir) {
|
|
59
|
+
const d = shellQuote(dir);
|
|
60
|
+
return [
|
|
61
|
+
`__PG=$(cat ${d}/pgid 2>/dev/null); __PG=\${__PG:--}`,
|
|
62
|
+
`if [ "$__PG" != "-" ]; then kill -KILL -"$__PG" 2>/dev/null || kill -KILL "$__PG" 2>/dev/null; fi`,
|
|
63
|
+
`rm -rf ${d} 2>/dev/null`,
|
|
64
|
+
`echo SEMABG_DISPOSED`,
|
|
65
|
+
].join("\n");
|
|
66
|
+
}
|
|
67
|
+
export function buildDetachCapableExec(dir, command) {
|
|
68
|
+
const d = shellQuote(dir);
|
|
69
|
+
const q = (name) => shellQuote(`${dir}/${name}`);
|
|
70
|
+
const cmdB64 = Buffer.from(command, "utf8").toString("base64");
|
|
71
|
+
const writePgid = (val) => `printf %s ${val} > ${q("pgid.tmp")} 2>/dev/null && mv ${q("pgid.tmp")} ${q("pgid")} 2>/dev/null`;
|
|
72
|
+
return [
|
|
73
|
+
`__cmd=$(printf %s '${cmdB64}' | base64 -d)`,
|
|
74
|
+
`mkdir -p ${d} 2>/dev/null`,
|
|
75
|
+
`trap 'rm -rf ${d} 2>/dev/null' EXIT`,
|
|
76
|
+
`if command -v setsid >/dev/null 2>&1; then`,
|
|
77
|
+
` setsid sh -c "$__cmd" &`,
|
|
78
|
+
` __pg=$!`,
|
|
79
|
+
` ${writePgid('"$__pg"')}`,
|
|
80
|
+
` wait "$__pg"; __rc=$?`,
|
|
81
|
+
`else`,
|
|
82
|
+
` ${writePgid('"$$"')}`,
|
|
83
|
+
` sh -c "$__cmd"; __rc=$?`,
|
|
84
|
+
`fi`,
|
|
85
|
+
`exit $__rc`,
|
|
86
|
+
].join("\n");
|
|
87
|
+
}
|
|
88
|
+
export function parseBgPollOutput(raw) {
|
|
89
|
+
if (raw.includes("SEMABG_GONE")) {
|
|
90
|
+
return { gone: true, outTotal: 0, errTotal: 0, alive: false, stdout: "", stdoutBytes: 0, stderr: "", stderrBytes: 0 };
|
|
91
|
+
}
|
|
92
|
+
const lines = raw.split("\n");
|
|
93
|
+
const metaIdx = lines.findIndex((l) => l.startsWith("SEMABG_META "));
|
|
94
|
+
if (metaIdx < 0)
|
|
95
|
+
return undefined;
|
|
96
|
+
const oIdx = lines.indexOf("SEMABG_O", metaIdx + 1);
|
|
97
|
+
const eIdx = oIdx >= 0 ? lines.indexOf("SEMABG_E", oIdx + 1) : -1;
|
|
98
|
+
const xIdx = eIdx >= 0 ? lines.indexOf("SEMABG_X", eIdx + 1) : -1;
|
|
99
|
+
if (oIdx < 0 || eIdx < 0 || xIdx < 0)
|
|
100
|
+
return undefined;
|
|
101
|
+
const m = /o=(\d+) e=(\d+) x=(\S+) a=([01])/.exec(lines[metaIdx].slice("SEMABG_META ".length));
|
|
102
|
+
if (!m)
|
|
103
|
+
return undefined;
|
|
104
|
+
const outTotal = parseInt(m[1], 10);
|
|
105
|
+
const errTotal = parseInt(m[2], 10);
|
|
106
|
+
const exTok = m[3];
|
|
107
|
+
const alive = m[4] === "1";
|
|
108
|
+
const exitParsed = exTok === "-" ? NaN : parseInt(exTok, 10);
|
|
109
|
+
const exitCode = Number.isFinite(exitParsed) ? exitParsed : undefined;
|
|
110
|
+
const decode = (from, to) => {
|
|
111
|
+
const b64 = lines.slice(from, to).join("").replace(/\s+/g, "");
|
|
112
|
+
const buf = Buffer.from(b64, "base64");
|
|
113
|
+
return { text: buf.toString("utf8"), bytes: buf.length };
|
|
114
|
+
};
|
|
115
|
+
const o = decode(oIdx + 1, eIdx);
|
|
116
|
+
const e = decode(eIdx + 1, xIdx);
|
|
117
|
+
return {
|
|
118
|
+
gone: false,
|
|
119
|
+
outTotal,
|
|
120
|
+
errTotal,
|
|
121
|
+
...(exitCode !== undefined ? { exitCode } : {}),
|
|
122
|
+
alive,
|
|
123
|
+
stdout: o.text,
|
|
124
|
+
stdoutBytes: o.bytes,
|
|
125
|
+
stderr: e.text,
|
|
126
|
+
stderrBytes: e.bytes,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=k8s-bg-scripts.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface PodSpecPatch {
|
|
2
|
+
volumes?: unknown[];
|
|
3
|
+
volumeMounts?: unknown[];
|
|
4
|
+
podSecurityContext?: Record<string, unknown>;
|
|
5
|
+
containerSecurityContext?: Record<string, unknown>;
|
|
6
|
+
labels?: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
export declare function applyPodSpecPatch(pod: {
|
|
9
|
+
metadata: {
|
|
10
|
+
labels?: Record<string, string>;
|
|
11
|
+
};
|
|
12
|
+
spec: Record<string, unknown>;
|
|
13
|
+
}, patch: PodSpecPatch): void;
|
|
14
|
+
export declare function execSocketTlsOptions(ca: string | undefined, insecureTls: boolean): Record<string, unknown>;
|
|
15
|
+
export declare function exitCodeFromStatus(statusJson: string): {
|
|
16
|
+
exitCode: number;
|
|
17
|
+
} | {
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=k8s-exec-protocol.d.ts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export function applyPodSpecPatch(pod, patch) {
|
|
2
|
+
assertHardeningOnly(patch.podSecurityContext, "podSecurityContext");
|
|
3
|
+
assertHardeningOnly(patch.containerSecurityContext, "containerSecurityContext");
|
|
4
|
+
if (patch.labels) {
|
|
5
|
+
const identity = {};
|
|
6
|
+
for (const k of ["app", "managed-by"]) {
|
|
7
|
+
const v = pod.metadata.labels?.[k];
|
|
8
|
+
if (v !== undefined)
|
|
9
|
+
identity[k] = v;
|
|
10
|
+
}
|
|
11
|
+
pod.metadata.labels = { ...pod.metadata.labels, ...patch.labels, ...identity };
|
|
12
|
+
}
|
|
13
|
+
const spec = pod.spec;
|
|
14
|
+
assertVolumesPodLocal(patch.volumes, "volumes");
|
|
15
|
+
if (patch.volumes?.length)
|
|
16
|
+
spec.volumes = [...(spec.volumes ?? []), ...patch.volumes];
|
|
17
|
+
if (patch.podSecurityContext)
|
|
18
|
+
spec.securityContext = { ...(spec.securityContext ?? {}), ...patch.podSecurityContext };
|
|
19
|
+
const container = spec.containers?.[0];
|
|
20
|
+
if (container) {
|
|
21
|
+
if (patch.volumeMounts?.length)
|
|
22
|
+
container.volumeMounts = [...(container.volumeMounts ?? []), ...patch.volumeMounts];
|
|
23
|
+
if (patch.containerSecurityContext)
|
|
24
|
+
container.securityContext = { ...(container.securityContext ?? {}), ...patch.containerSecurityContext };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function assertHardeningOnly(sc, where) {
|
|
28
|
+
if (!sc)
|
|
29
|
+
return;
|
|
30
|
+
const bad = (msg) => {
|
|
31
|
+
throw new Error(`podSpecPatch.${where} rejected — it may only HARDEN, never weaken isolation: ${msg}`);
|
|
32
|
+
};
|
|
33
|
+
if (sc.privileged === true)
|
|
34
|
+
bad("privileged:true");
|
|
35
|
+
if (sc.allowPrivilegeEscalation === true)
|
|
36
|
+
bad("allowPrivilegeEscalation:true");
|
|
37
|
+
if (sc.readOnlyRootFilesystem === false)
|
|
38
|
+
bad("readOnlyRootFilesystem:false");
|
|
39
|
+
if (sc.runAsNonRoot === false)
|
|
40
|
+
bad("runAsNonRoot:false");
|
|
41
|
+
if (sc.runAsUser === 0)
|
|
42
|
+
bad("runAsUser:0 (root)");
|
|
43
|
+
const caps = sc.capabilities;
|
|
44
|
+
if (caps && Array.isArray(caps.add) && caps.add.length > 0)
|
|
45
|
+
bad(`capabilities.add ${JSON.stringify(caps.add)} (a hardening patch may only .drop)`);
|
|
46
|
+
if (sc.procMount === "Unmasked")
|
|
47
|
+
bad('procMount:"Unmasked" (re-exposes masked /proc)');
|
|
48
|
+
const seccomp = sc.seccompProfile;
|
|
49
|
+
if (seccomp && seccomp.type === "Unconfined")
|
|
50
|
+
bad('seccompProfile.type:"Unconfined" (disables syscall filtering)');
|
|
51
|
+
}
|
|
52
|
+
function assertVolumesPodLocal(volumes, where) {
|
|
53
|
+
if (!volumes?.length)
|
|
54
|
+
return;
|
|
55
|
+
const POD_LOCAL = new Set(["emptyDir", "configMap", "secret", "projected", "downwardAPI", "ephemeral"]);
|
|
56
|
+
for (const v of volumes) {
|
|
57
|
+
const vol = v;
|
|
58
|
+
const sources = Object.keys(vol).filter((k) => k !== "name");
|
|
59
|
+
for (const src of sources) {
|
|
60
|
+
if (!POD_LOCAL.has(src)) {
|
|
61
|
+
throw new Error(`podSpecPatch.${where} rejected — volume source ${JSON.stringify(src)} is not pod-local (host-backed sources like hostPath/csi/flexVolume cross the Kata VM boundary into the node FS). Allowed: ${[...POD_LOCAL].join("/")}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function execSocketTlsOptions(ca, insecureTls) {
|
|
67
|
+
const caList = ca ? [ca] : undefined;
|
|
68
|
+
return { ca: caList, rejectUnauthorized: !insecureTls, tls: { ca: caList, rejectUnauthorized: !insecureTls } };
|
|
69
|
+
}
|
|
70
|
+
export function exitCodeFromStatus(statusJson) {
|
|
71
|
+
try {
|
|
72
|
+
const s = JSON.parse(statusJson);
|
|
73
|
+
if (s.status === "Success")
|
|
74
|
+
return { exitCode: 0 };
|
|
75
|
+
const cause = s.details?.causes?.find((c) => c.reason === "ExitCode");
|
|
76
|
+
if (cause?.message?.trim()) {
|
|
77
|
+
const code = Number(cause.message);
|
|
78
|
+
if (Number.isFinite(code))
|
|
79
|
+
return { exitCode: code };
|
|
80
|
+
}
|
|
81
|
+
return { error: s.message || s.reason || "exec failed without an exit code" };
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return { error: `unparseable exec status: ${statusJson.slice(0, 200)}` };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=k8s-exec-protocol.js.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
2
|
+
import type { Pool as PgPool, PoolClient } from "pg";
|
|
3
|
+
import type { TaskOutcome as CoreTaskOutcome } from "@sema-agent/core";
|
|
4
|
+
import { type SqlDriver } from "./sql-driver.js";
|
|
5
|
+
export interface MechanicalOutcome {
|
|
6
|
+
route?: "single" | "fanout";
|
|
7
|
+
workerCount?: number;
|
|
8
|
+
oraclePassed?: number;
|
|
9
|
+
oracleTotal?: number;
|
|
10
|
+
assembleExit?: number;
|
|
11
|
+
lintExit?: number;
|
|
12
|
+
mergeOk?: boolean;
|
|
13
|
+
replanTriggers?: string[];
|
|
14
|
+
costMicroUsd?: number;
|
|
15
|
+
turns?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface LlmAssistedOutcome {
|
|
18
|
+
stuckEvents?: number;
|
|
19
|
+
verifier?: {
|
|
20
|
+
verdict: string;
|
|
21
|
+
confidence: number;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface SignatureInputs {
|
|
25
|
+
taskType?: string;
|
|
26
|
+
toolset?: string[];
|
|
27
|
+
targetLang?: string;
|
|
28
|
+
moduleCount?: number;
|
|
29
|
+
tag?: string;
|
|
30
|
+
}
|
|
31
|
+
export type RunStatus = "completed-graded" | "infra-aborted";
|
|
32
|
+
export interface TaskOutcome {
|
|
33
|
+
taskId?: string;
|
|
34
|
+
model: string;
|
|
35
|
+
runStatus?: RunStatus;
|
|
36
|
+
signatureInputs: SignatureInputs;
|
|
37
|
+
mechanical: MechanicalOutcome;
|
|
38
|
+
llmAssisted?: LlmAssistedOutcome;
|
|
39
|
+
core?: unknown;
|
|
40
|
+
}
|
|
41
|
+
export declare function taskSignature(s: SignatureInputs): string;
|
|
42
|
+
export declare function deriveOutcome(m: MechanicalOutcome, runStatus?: RunStatus): "pass" | "partial" | "fail" | "aborted";
|
|
43
|
+
export declare function coreOutcomeToLedgerRow(o: CoreTaskOutcome): TaskOutcome;
|
|
44
|
+
export declare const PG_OUTCOME_LEDGER_SCHEMA: string[];
|
|
45
|
+
export declare function ensurePgOutcomeLedgerSchema(pool: PgPool | PoolClient): Promise<void>;
|
|
46
|
+
export declare class SqlOutcomeLedger {
|
|
47
|
+
private readonly db;
|
|
48
|
+
constructor(db: SqlDriver);
|
|
49
|
+
private q;
|
|
50
|
+
private json;
|
|
51
|
+
record(o: TaskOutcome): Promise<string>;
|
|
52
|
+
recordCore(o: CoreTaskOutcome): Promise<void>;
|
|
53
|
+
summary(opts?: {
|
|
54
|
+
taskSignature?: string;
|
|
55
|
+
includeAborted?: boolean;
|
|
56
|
+
}): Promise<Array<{
|
|
57
|
+
taskSignature: string;
|
|
58
|
+
model: string;
|
|
59
|
+
n: number;
|
|
60
|
+
passRate: number;
|
|
61
|
+
meanCostMicroUsd: number;
|
|
62
|
+
}>>;
|
|
63
|
+
}
|
|
64
|
+
export declare class TiDBOutcomeLedger extends SqlOutcomeLedger {
|
|
65
|
+
constructor(pool: MySqlPool);
|
|
66
|
+
}
|
|
67
|
+
export declare class PgOutcomeLedger extends SqlOutcomeLedger {
|
|
68
|
+
constructor(pool: PgPool);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=outcome-ledger-sql.d.ts.map
|