@inneranimalmedia/agentsam-sdk 2.4.1 → 2.5.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/README.md +7 -6
- package/docs/AGENTSAM_WORKBENCH.md +30 -0
- package/docs/AUTH_IDENTITY_CONTRACT.md +52 -0
- package/docs/CAPABILITIES.md +5 -3
- package/docs/CLI_SHELL.md +57 -80
- package/docs/CMS_STUDIO.md +101 -0
- package/docs/CONTEXT.md +170 -0
- package/docs/LOCAL_OLLAMA.md +58 -0
- package/docs/MERKLE.md +19 -19
- package/docs/PORTABLE_CONTEXT.md +4 -3
- package/docs/PROJECT_CONFIG.md +72 -0
- package/docs/RELEASES.md +26 -2
- package/docs/REPOSITORY_INTELLIGENCE.md +1 -1
- package/docs/REPOSITORY_KNOWLEDGE.md +114 -0
- package/docs/SDK_WORKER.md +86 -0
- package/docs/SECURITY.md +59 -22
- package/docs/client-cms-editor.md +15 -0
- package/docs/local-studio/WORKMODE_DONOR_NOTES.md +485 -0
- package/package.json +17 -4
- package/packages/identity/package.json +11 -2
- package/packages/identity/src/contracts/auth-config.js +98 -0
- package/packages/identity/src/index.js +1 -0
- package/packages/identity/src/oauth/README.md +2 -2
- package/packages/identity/src/oauth/credentials.js +11 -4
- package/packages/identity/src/oauth/iam-platform.js +3 -3
- package/packages/identity/src/providers/iam/index.js +7 -7
- package/packages/identity/src/providers/iam/oauth.js +6 -4
- package/packages/identity/src/providers/iam/profile.js +5 -5
- package/packages/identity/tests/auth-config.test.mjs +57 -0
- package/packages/identity/tests/oauth-credentials.test.mjs +13 -2
- package/protocol/FILEMETA_V1.md +95 -0
- package/protocol/INSPECT_VIEWS_V1.md +40 -0
- package/protocol/MERKLE_PERSISTENCE_V1.md +50 -0
- package/protocol/MERKLE_V1.md +3 -1
- package/protocol/capabilities/manifest.json +32 -3
- package/protocol/capabilities/repository-snapshot.schema.json +1 -0
- package/protocol/context/context-budget.schema.json +41 -0
- package/protocol/context/context-item.schema.json +20 -0
- package/protocol/context/resolved-context-pack.schema.json +42 -0
- package/protocol/context/result-policy.schema.json +17 -0
- package/protocol/knowledge/context-pack.schema.json +33 -0
- package/protocol/knowledge/retrieval-query.schema.json +68 -13
- package/python/README.md +15 -7
- package/python/agentsam_sdk/cli.py +0 -21
- package/python/agentsam_sdk/tui/README.md +17 -12
- package/python/agentsam_sdk/tui/bootstrap.py +2 -2
- package/python/agentsam_sdk/tui/demo.py +25 -10
- package/python/agentsam_sdk/tui/onboarding.py +208 -0
- package/python/tests/test_tui_cli.py +7 -6
- package/skills/README.md +21 -0
- package/skills/agentsam-app-fundamentals/SKILL.md +165 -0
- package/skills/agentsam-app-fundamentals/references/graphs-contracts-ast-merkle.md +89 -0
- package/skills/agentsam-app-fundamentals/references/trust-credentials-and-destinations.md +99 -0
- package/skills/agentsam-jr-dev/SKILL.md +232 -0
- package/skills/agentsam-jr-dev/references/real-application-logic.md +156 -0
- package/skills/agentsam-jr-dev/references/web-application-fundamentals.md +240 -0
- package/skills/agentsam-progression-guard/SKILL.md +197 -0
- package/skills/agentsam-progression-guard/references/checkpoint-chain.md +111 -0
- package/skills/agentsam-progression-guard/references/hooks-operational-io.md +96 -0
- package/skills/catalog.json +53 -0
- package/src/capabilities/index.js +7 -0
- package/src/capabilities/repository-snapshot-view.js +238 -0
- package/src/capabilities/repository-snapshot.js +28 -14
- package/src/cli.js +108 -56
- package/src/commands/context.js +14 -2
- package/src/commands/db.js +4 -7
- package/src/commands/deploy.js +17 -31
- package/src/commands/interactive.js +21 -0
- package/src/commands/knowledge.js +27 -4
- package/src/commands/merkle-persist.js +118 -0
- package/src/commands/merkle.js +32 -17
- package/src/commands/models.js +107 -0
- package/src/commands/ollama.js +259 -0
- package/src/commands/preferences.js +102 -0
- package/src/commands/product.js +86 -16
- package/src/commands/security.js +3 -3
- package/src/commands/shell.js +71 -27
- package/src/commands/skills.js +66 -0
- package/src/commands/start-local.js +1 -1
- package/src/commands/tunnel.js +5 -4
- package/src/context/budget.js +58 -0
- package/src/context/compact.js +28 -0
- package/src/context/index.js +5 -0
- package/src/context/resolve.js +84 -0
- package/src/context/result-policy.js +66 -0
- package/src/index.js +16 -0
- package/src/indexing/execution-boundary.js +144 -0
- package/src/indexing/index.js +8 -0
- package/src/indexing/provider.js +41 -0
- package/src/knowledge/config.js +2 -2
- package/src/knowledge/context-pack.js +12 -1
- package/src/knowledge/contracts.js +9 -4
- package/src/knowledge/engine.js +7 -3
- package/src/knowledge/service/server.js +1 -1
- package/src/lib/bridge-client.js +7 -5
- package/src/lib/cli-preferences.js +74 -0
- package/src/lib/core-client.js +8 -8
- package/src/lib/deploy-receipt/index.js +5 -2
- package/src/lib/detect-context.js +6 -5
- package/src/lib/identity-scaffold.js +1 -1
- package/src/lib/local-scaffold.js +35 -33
- package/src/lib/local-status.js +9 -17
- package/src/lib/merkle/cloudflare-persistence.js +321 -0
- package/src/lib/merkle/filemeta.js +43 -0
- package/src/lib/merkle/git-ignore.js +24 -0
- package/src/lib/merkle/hash.js +1 -0
- package/src/lib/merkle/index.js +22 -0
- package/src/lib/merkle/persistence.js +72 -0
- package/src/lib/merkle/semantic.js +359 -0
- package/src/lib/merkle/snapshot.js +9 -3
- package/src/lib/merkle/tree.js +11 -6
- package/src/lib/project-config.js +227 -0
- package/src/lib/project-rules.js +68 -0
- package/src/lib/save-sdk-token.js +1 -1
- package/src/lib/slash-commands.js +2 -1
- package/src/lib/tools.js +11 -5
- package/src/security/index.js +1 -0
- package/src/security/inventory.js +4 -1
- package/src/security/render.js +27 -5
- package/src/security/scan.js +24 -9
- package/src/security/trust-boundary.js +24 -0
- package/src/skills/index.js +64 -0
- package/src/tools/index.js +1 -0
- package/src/tools/search.js +70 -0
- package/src/ui/ansi.js +1 -1
- package/src/ui/boot.js +56 -0
- package/src/ui/merkle/render.js +1 -0
- package/src/ui/runtime-activity.js +192 -0
- package/src/ui/theme.js +19 -18
- package/test/app-building-skills.test.mjs +61 -0
- package/test/apps-scaffold-contract.test.mjs +56 -0
- package/test/capabilities.test.mjs +53 -4
- package/test/cli-preferences.test.mjs +25 -0
- package/test/context.test.mjs +95 -0
- package/test/indexing-provider.test.mjs +29 -0
- package/test/jr-dev-skill.test.mjs +26 -0
- package/test/knowledge-context-pack.test.mjs +19 -0
- package/test/knowledge.test.mjs +1 -1
- package/test/merkle-persistence.test.mjs +91 -0
- package/test/merkle.test.mjs +77 -3
- package/test/models.test.mjs +37 -0
- package/test/ollama.test.mjs +94 -0
- package/test/project-config.test.mjs +81 -0
- package/test/project-rules.test.mjs +44 -0
- package/test/release-hygiene.test.mjs +34 -0
- package/test/repository-snapshot-view.test.mjs +112 -0
- package/test/runtime-activity.test.mjs +98 -0
- package/test/sdk-worker-contract.test.mjs +68 -0
- package/test/security.test.mjs +46 -0
- package/test/shell.test.mjs +8 -1
- package/test/skills.test.mjs +22 -0
- package/test/smoke.mjs +2 -2
- package/test/theme-portability.test.mjs +14 -0
- package/test/tools-search.test.mjs +27 -0
- package/examples/agentsam-tui-ansi.mjs +0 -149
- package/src/commands/tui.js +0 -120
- package/src/ui/splash-xterm.js +0 -290
- package/src/ui/splash.js +0 -426
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { execFile } from 'child_process';
|
|
6
6
|
import { promisify } from 'util';
|
|
7
7
|
import { coreBaseUrl } from './core-client.js';
|
|
8
|
+
import { resolveSdkKey } from '../../packages/identity/src/contracts/auth-config.js';
|
|
8
9
|
|
|
9
10
|
const execFileAsync = promisify(execFile);
|
|
10
11
|
|
|
@@ -220,22 +221,22 @@ async function detectIam(explicitToken = '') {
|
|
|
220
221
|
});
|
|
221
222
|
}
|
|
222
223
|
|
|
223
|
-
const sdkToken =
|
|
224
|
+
const sdkToken = resolveSdkKey(process.env, explicitToken);
|
|
224
225
|
if (sdkToken.trim()) {
|
|
225
226
|
const probe = await probeSdkBearer(sdkToken);
|
|
226
227
|
if (probe.valid) {
|
|
227
228
|
return {
|
|
228
|
-
source: 'sdk-
|
|
229
|
+
source: 'sdk-key',
|
|
229
230
|
ready: true,
|
|
230
|
-
detail: `
|
|
231
|
+
detail: `AGENTSAM_SDK_KEY · user ${probe.user_id || '?'}`,
|
|
231
232
|
probe,
|
|
232
233
|
aux,
|
|
233
234
|
};
|
|
234
235
|
}
|
|
235
236
|
return {
|
|
236
|
-
source: 'sdk-
|
|
237
|
+
source: 'sdk-key',
|
|
237
238
|
ready: false,
|
|
238
|
-
detail: `
|
|
239
|
+
detail: `AGENTSAM_SDK_KEY invalid (${probe.error}) → will open browser`,
|
|
239
240
|
probe,
|
|
240
241
|
aux,
|
|
241
242
|
};
|
|
@@ -75,7 +75,7 @@ migrations_dir = "migrations"
|
|
|
75
75
|
files['.env.example'] = `# Minted at install/build — secrets via wrangler secret put (never commit)
|
|
76
76
|
# IAM_CLIENT_ID= # plaintext var (public by OAuth design)
|
|
77
77
|
# IAM_CLIENT_SECRET= # wrangler secret put only
|
|
78
|
-
#
|
|
78
|
+
# IAM_ORIGIN=https://inneranimalmedia.com
|
|
79
79
|
|
|
80
80
|
# Developer BYOK — takes /api/oauth/{google|github}/start when set
|
|
81
81
|
# GOOGLE_CLIENT_ID=
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import pkg from '../../package.json' with { type: 'json' };
|
|
7
|
+
import { createProjectManifest } from './project-config.js';
|
|
8
|
+
import { defaultProjectRules } from './project-rules.js';
|
|
7
9
|
|
|
8
10
|
const LANE_KEYS = {
|
|
9
11
|
'1': 'fullstack',
|
|
@@ -139,37 +141,24 @@ export function buildLocalScaffoldFiles({
|
|
|
139
141
|
const migration = migrationSql(laneKey);
|
|
140
142
|
|
|
141
143
|
return [
|
|
142
|
-
{
|
|
143
|
-
path: 'agentsam.config.js',
|
|
144
|
-
content: `export default {
|
|
145
|
-
project: '${projectName}',
|
|
146
|
-
lane: '${laneKey}',
|
|
147
|
-
agent: '${agent}',
|
|
148
|
-
runTarget: '${runTarget}',
|
|
149
|
-
api: { baseUrl: '/api/agentsam' },
|
|
150
|
-
};
|
|
151
|
-
`,
|
|
152
|
-
},
|
|
153
144
|
{
|
|
154
145
|
path: '.agentsam/config.json',
|
|
155
146
|
content: `${JSON.stringify(
|
|
156
|
-
{
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
agent,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
dev_port: 8787,
|
|
164
|
-
db_path: '.agentsam/data/agentsam.sqlite',
|
|
165
|
-
db_schema: 'db/schema.sql',
|
|
166
|
-
ui: 'terminal',
|
|
167
|
-
scaffold_version: sdkVersion,
|
|
168
|
-
},
|
|
147
|
+
createProjectManifest({
|
|
148
|
+
projectName,
|
|
149
|
+
preset: laneKey,
|
|
150
|
+
profile: agent,
|
|
151
|
+
runTarget,
|
|
152
|
+
sdkVersion,
|
|
153
|
+
}),
|
|
169
154
|
null,
|
|
170
155
|
2,
|
|
171
156
|
)}\n`,
|
|
172
157
|
},
|
|
158
|
+
{
|
|
159
|
+
path: '.agentsamrules',
|
|
160
|
+
content: defaultProjectRules(projectName),
|
|
161
|
+
},
|
|
173
162
|
{
|
|
174
163
|
path: '.agentsam/start-local.md',
|
|
175
164
|
content: `# Agent Sam local project
|
|
@@ -187,9 +176,10 @@ Useful local surfaces:
|
|
|
187
176
|
|
|
188
177
|
\`\`\`bash
|
|
189
178
|
npm run db:status
|
|
190
|
-
|
|
191
|
-
npm run tui:rich -- --install # optional Rich UI in isolated .agentsam venv
|
|
179
|
+
npx agentsam # enter Agent Sam
|
|
192
180
|
npx agentsam start-local # PTY on ws://127.0.0.1:3099
|
|
181
|
+
npm run ollama:setup # optional local/free model config
|
|
182
|
+
npm run ollama:status # probe local Ollama + models
|
|
193
183
|
\`\`\`
|
|
194
184
|
|
|
195
185
|
The local SQLite database lives at \`.agentsam/data/agentsam.sqlite\` and is initialized from \`db/schema.sql\`.
|
|
@@ -209,7 +199,12 @@ npx agentsam deploy
|
|
|
209
199
|
dist/
|
|
210
200
|
.wrangler/
|
|
211
201
|
.agentsam/data/
|
|
202
|
+
.agentsam/cli.json
|
|
212
203
|
.agentsam/tui-venv/
|
|
204
|
+
.agentsam/knowledge/
|
|
205
|
+
.agentsam/cache/
|
|
206
|
+
.agentsam/merkle/
|
|
207
|
+
.agentsam/merkle.json
|
|
213
208
|
*.db
|
|
214
209
|
*.sqlite
|
|
215
210
|
*.sqlite-*
|
|
@@ -231,8 +226,8 @@ dist/
|
|
|
231
226
|
'db:init': 'agentsam db init',
|
|
232
227
|
'db:status': 'agentsam db status',
|
|
233
228
|
pty: 'agentsam start-local',
|
|
234
|
-
|
|
235
|
-
'
|
|
229
|
+
'ollama:setup': 'agentsam ollama setup',
|
|
230
|
+
'ollama:status': 'agentsam ollama status',
|
|
236
231
|
deploy: 'agentsam deploy',
|
|
237
232
|
},
|
|
238
233
|
engines: {
|
|
@@ -262,6 +257,11 @@ AGENTSAM_LANE=${laneKey}
|
|
|
262
257
|
AGENTSAM_AGENT=${agent}
|
|
263
258
|
AGENTSAM_DB=.agentsam/data/agentsam.sqlite
|
|
264
259
|
PORT=8787
|
|
260
|
+
|
|
261
|
+
# Optional free/local model runtime. Enable with: agentsam ollama setup
|
|
262
|
+
# OLLAMA_BASE_URL=http://127.0.0.1:11434
|
|
263
|
+
# OLLAMA_MODEL=qwen2.5-coder
|
|
264
|
+
# OLLAMA_EMBED_MODEL=mxbai-embed-large
|
|
265
265
|
`,
|
|
266
266
|
},
|
|
267
267
|
{
|
|
@@ -399,13 +399,15 @@ Local state:
|
|
|
399
399
|
## Agent Sam terminal experience
|
|
400
400
|
|
|
401
401
|
\`\`\`bash
|
|
402
|
-
|
|
403
|
-
npm run tui:rich # Python Rich dashboard if installed
|
|
404
|
-
npm run tui:rich -- --install
|
|
402
|
+
npx agentsam # enter the interactive Agent Sam experience
|
|
405
403
|
npm run pty # local PTY on ws://127.0.0.1:3099
|
|
406
404
|
npm run db:status
|
|
405
|
+
npm run ollama:setup # optional local/free model config
|
|
406
|
+
npm run ollama:status # local Ollama + model readiness
|
|
407
407
|
\`\`\`
|
|
408
408
|
|
|
409
|
+
Ollama is optional and local-only; it is not deployed as an edge model. Remote AgentSam sessions can reach it by executing the CLI through the existing user-hosted terminal tunnel.
|
|
410
|
+
|
|
409
411
|
No Worker or cloud account is required for local development.
|
|
410
412
|
|
|
411
413
|
## Graduate intentionally
|
|
@@ -443,9 +445,9 @@ export function buildLocalScaffoldMeta(body, sdkVersion = pkg.version) {
|
|
|
443
445
|
'npm run smoke',
|
|
444
446
|
'npm run status',
|
|
445
447
|
'npm run dev',
|
|
446
|
-
'
|
|
448
|
+
'npx agentsam',
|
|
447
449
|
'npm run db:status',
|
|
448
|
-
'Optional: npm run
|
|
450
|
+
'Optional free/local models: npm run ollama:setup',
|
|
449
451
|
'Optional: npm run pty',
|
|
450
452
|
'When ready for cloud: npm run deploy',
|
|
451
453
|
],
|
package/src/lib/local-status.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { tryResolveGitContext } from './git-context.js';
|
|
4
4
|
import { inspectLocalSqlite } from '../local/sqlite.js';
|
|
5
|
+
import { getCreatedWithVersion, getDefaultProfile, getDeployTarget, getLocalDatabasePath, getProjectName, getProjectPreset, getRepositoryId, tryReadProjectConfig } from './project-config.js';
|
|
5
6
|
|
|
6
7
|
export function findAgentSamProjectRoot(startDir = process.cwd()) {
|
|
7
8
|
let dir = path.resolve(startDir);
|
|
@@ -14,16 +15,6 @@ export function findAgentSamProjectRoot(startDir = process.cwd()) {
|
|
|
14
15
|
return path.resolve(startDir);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
function readProjectConfig(root) {
|
|
18
|
-
const configPath = path.join(root, '.agentsam', 'config.json');
|
|
19
|
-
if (!fs.existsSync(configPath)) return null;
|
|
20
|
-
try {
|
|
21
|
-
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
22
|
-
} catch {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
18
|
async function probe(url) {
|
|
28
19
|
try {
|
|
29
20
|
const response = await fetch(url, { signal: AbortSignal.timeout(500) });
|
|
@@ -35,12 +26,12 @@ async function probe(url) {
|
|
|
35
26
|
|
|
36
27
|
export async function collectLocalStatus(cwd = process.cwd()) {
|
|
37
28
|
const root = findAgentSamProjectRoot(cwd);
|
|
38
|
-
const config =
|
|
29
|
+
const config = tryReadProjectConfig(root);
|
|
39
30
|
const git = tryResolveGitContext({ cwd: root });
|
|
40
31
|
|
|
41
32
|
let db = { ok: false, exists: false, tables: [] };
|
|
42
33
|
if (config) {
|
|
43
|
-
db = await inspectLocalSqlite(path.join(root, config
|
|
34
|
+
db = await inspectLocalSqlite(path.join(root, getLocalDatabasePath(config)));
|
|
44
35
|
}
|
|
45
36
|
|
|
46
37
|
const devPort = config?.dev_port ?? 8787;
|
|
@@ -54,11 +45,12 @@ export async function collectLocalStatus(cwd = process.cwd()) {
|
|
|
54
45
|
schemaVersion: 'agentsam-local-status-v1',
|
|
55
46
|
configured: Boolean(config),
|
|
56
47
|
root,
|
|
57
|
-
project: config
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
project: getProjectName(config, path.basename(root)),
|
|
49
|
+
repositoryId: getRepositoryId(config),
|
|
50
|
+
lane: getProjectPreset(config),
|
|
51
|
+
agent: getDefaultProfile(config),
|
|
52
|
+
deployTarget: getDeployTarget(config),
|
|
53
|
+
scaffoldVersion: getCreatedWithVersion(config),
|
|
62
54
|
git: git
|
|
63
55
|
? {
|
|
64
56
|
repo: git.repoFullName || null,
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { execFileSync } from 'node:child_process';
|
|
6
|
+
import { resolveGitContext } from '../git-context.js';
|
|
7
|
+
import {
|
|
8
|
+
MERKLE_SNAPSHOT_STORAGE_PREFIX,
|
|
9
|
+
MERKLE_SNAPSHOT_TABLE,
|
|
10
|
+
MERKLE_WEBSITE_ASSETS_BINDING,
|
|
11
|
+
merkleSnapshotStorageKey,
|
|
12
|
+
normalizeMerkleStoragePrefix,
|
|
13
|
+
} from './persistence.js';
|
|
14
|
+
|
|
15
|
+
const D1_BINDING = 'DB';
|
|
16
|
+
const CAPTURE_KINDS = new Set(['deploy', 'manual', 'agent', 'index']);
|
|
17
|
+
const SOURCES = new Set(['github', 'gitlab', 'bitbucket', 'local', 'upload']);
|
|
18
|
+
|
|
19
|
+
function clean(value) {
|
|
20
|
+
return value == null ? '' : String(value).trim();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function stripJsonComments(input) {
|
|
24
|
+
let out = '';
|
|
25
|
+
let string = false;
|
|
26
|
+
let quote = '';
|
|
27
|
+
let escaped = false;
|
|
28
|
+
let line = false;
|
|
29
|
+
let block = false;
|
|
30
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
31
|
+
const char = input[i];
|
|
32
|
+
const next = input[i + 1];
|
|
33
|
+
if (line) {
|
|
34
|
+
if (char === '\n') { line = false; out += char; }
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (block) {
|
|
38
|
+
if (char === '*' && next === '/') { block = false; i += 1; }
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (string) {
|
|
42
|
+
out += char;
|
|
43
|
+
if (escaped) escaped = false;
|
|
44
|
+
else if (char === '\\') escaped = true;
|
|
45
|
+
else if (char === quote) string = false;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (char === '"' || char === "'") { string = true; quote = char; out += char; continue; }
|
|
49
|
+
if (char === '/' && next === '/') { line = true; i += 1; continue; }
|
|
50
|
+
if (char === '/' && next === '*') { block = true; i += 1; continue; }
|
|
51
|
+
out += char;
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function parseTomlBindings(text) {
|
|
57
|
+
const rows = { r2_buckets: [], d1_databases: [] };
|
|
58
|
+
let current = null;
|
|
59
|
+
let currentType = null;
|
|
60
|
+
const flush = () => {
|
|
61
|
+
if (current && currentType) rows[currentType].push(current);
|
|
62
|
+
current = null;
|
|
63
|
+
currentType = null;
|
|
64
|
+
};
|
|
65
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
66
|
+
const line = raw.trim();
|
|
67
|
+
const header = line.match(/^\[\[(?:env\.([^.\]]+)\.)?(r2_buckets|d1_databases)\]\]$/);
|
|
68
|
+
if (header) {
|
|
69
|
+
flush();
|
|
70
|
+
currentType = header[2];
|
|
71
|
+
current = { environment: header[1] || null };
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (/^\[/.test(line)) { flush(); continue; }
|
|
75
|
+
if (!current || !line || line.startsWith('#')) continue;
|
|
76
|
+
const match = line.match(/^([A-Za-z0-9_]+)\s*=\s*["']([^"']*)["']/);
|
|
77
|
+
if (match) current[match[1]] = match[2];
|
|
78
|
+
}
|
|
79
|
+
flush();
|
|
80
|
+
return rows;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function rowsFromJson(parsed, environment) {
|
|
84
|
+
const base = parsed && typeof parsed === 'object' ? parsed : {};
|
|
85
|
+
const env = environment && base.env?.[environment] && typeof base.env[environment] === 'object'
|
|
86
|
+
? base.env[environment]
|
|
87
|
+
: {};
|
|
88
|
+
return {
|
|
89
|
+
r2_buckets: [...(env.r2_buckets || []), ...(base.r2_buckets || [])],
|
|
90
|
+
d1_databases: [...(env.d1_databases || []), ...(base.d1_databases || [])],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function readWranglerPersistenceBindings(configPath, { environment = null } = {}) {
|
|
95
|
+
const filename = path.resolve(configPath);
|
|
96
|
+
const text = fs.readFileSync(filename, 'utf8');
|
|
97
|
+
const ext = path.extname(filename).toLowerCase();
|
|
98
|
+
let rows;
|
|
99
|
+
if (ext === '.json' || ext === '.jsonc') rows = rowsFromJson(JSON.parse(stripJsonComments(text)), environment);
|
|
100
|
+
else rows = parseTomlBindings(text);
|
|
101
|
+
const scoped = (items) => {
|
|
102
|
+
if (!environment) return items.filter((row) => !row.environment);
|
|
103
|
+
const envRows = items.filter((row) => row.environment === environment);
|
|
104
|
+
return envRows.length ? envRows : items.filter((row) => !row.environment);
|
|
105
|
+
};
|
|
106
|
+
return { configPath: filename, r2_buckets: scoped(rows.r2_buckets), d1_databases: scoped(rows.d1_databases) };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function resolveWranglerMerklePersistence({
|
|
110
|
+
configPath,
|
|
111
|
+
environment = null,
|
|
112
|
+
r2Binding = MERKLE_WEBSITE_ASSETS_BINDING,
|
|
113
|
+
d1Binding = D1_BINDING,
|
|
114
|
+
r2Only = false,
|
|
115
|
+
} = {}) {
|
|
116
|
+
if (!clean(configPath)) throw new Error('wrangler_config_required');
|
|
117
|
+
const rows = readWranglerPersistenceBindings(configPath, { environment });
|
|
118
|
+
const r2 = rows.r2_buckets.find((row) => clean(row.binding) === clean(r2Binding));
|
|
119
|
+
if (!r2?.bucket_name) throw new Error(`wrangler_r2_binding_not_found:${r2Binding}`);
|
|
120
|
+
const d1 = rows.d1_databases.find((row) => clean(row.binding) === clean(d1Binding));
|
|
121
|
+
if (!r2Only && !d1?.database_name) throw new Error(`wrangler_d1_binding_not_found:${d1Binding}`);
|
|
122
|
+
return {
|
|
123
|
+
config_path: rows.configPath,
|
|
124
|
+
environment: environment || null,
|
|
125
|
+
r2_binding: r2Binding,
|
|
126
|
+
storage_bucket: String(r2.bucket_name),
|
|
127
|
+
d1_binding: r2Only ? null : d1Binding,
|
|
128
|
+
database_name: r2Only ? null : String(d1.database_name),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function providerRepoId(git) {
|
|
133
|
+
const host = clean(git?.remoteHost).toLowerCase();
|
|
134
|
+
const fullName = clean(git?.repoFullName);
|
|
135
|
+
if (!fullName) return null;
|
|
136
|
+
if (host === 'github.com') return `github:${fullName}`;
|
|
137
|
+
if (host === 'gitlab.com') return `gitlab:${fullName}`;
|
|
138
|
+
if (host === 'bitbucket.org') return `bitbucket:${fullName}`;
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function safeId(value) {
|
|
143
|
+
return clean(value).replace(/[^A-Za-z0-9._-]+/g, '').slice(0, 96);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function snapshotIdFor({ snapshot, repoId, captureKind, deploymentId }) {
|
|
147
|
+
const deployment = safeId(deploymentId);
|
|
148
|
+
if (captureKind === 'deploy' && deployment) return `mrs_dep_${deployment.toLowerCase()}`;
|
|
149
|
+
const digest = createHash('sha256')
|
|
150
|
+
.update([repoId, snapshot.rootHash, snapshot.semantic?.rootHash || '', captureKind].join('\0'))
|
|
151
|
+
.digest('hex');
|
|
152
|
+
return `mrs_${digest.slice(0, 24)}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function sqlEsc(value) {
|
|
156
|
+
return String(value ?? '').replaceAll("'", "''");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function sqlText(value) {
|
|
160
|
+
return value == null || value === '' ? 'NULL' : `'${sqlEsc(value)}'`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function sqlInt(value) {
|
|
164
|
+
return value == null || value === '' || !Number.isFinite(Number(value)) ? 'NULL' : String(Math.trunc(Number(value)));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function buildMerklePersistencePlan({
|
|
168
|
+
snapshot,
|
|
169
|
+
root = process.cwd(),
|
|
170
|
+
ownerUserId,
|
|
171
|
+
repoId,
|
|
172
|
+
source = 'local',
|
|
173
|
+
captureKind = 'manual',
|
|
174
|
+
connectionId = null,
|
|
175
|
+
runtimeLeaseId = null,
|
|
176
|
+
deploymentId = null,
|
|
177
|
+
workerVersionId = null,
|
|
178
|
+
referenceLabel = null,
|
|
179
|
+
storagePrefix = MERKLE_SNAPSHOT_STORAGE_PREFIX,
|
|
180
|
+
wrangler,
|
|
181
|
+
} = {}) {
|
|
182
|
+
if (!snapshot?.rootHash || !Array.isArray(snapshot?.entries)) throw new Error('merkle_snapshot_required');
|
|
183
|
+
const owner = clean(ownerUserId);
|
|
184
|
+
if (!owner) throw new Error('owner_user_id_required');
|
|
185
|
+
if (!CAPTURE_KINDS.has(captureKind)) throw new Error(`capture_kind_invalid:${captureKind}`);
|
|
186
|
+
if (!SOURCES.has(source)) throw new Error(`source_invalid:${source}`);
|
|
187
|
+
if (captureKind !== 'deploy' && !clean(connectionId) && !clean(runtimeLeaseId)) throw new Error('execution_provenance_required');
|
|
188
|
+
let git = null;
|
|
189
|
+
try { git = resolveGitContext({ cwd: root }); } catch { git = null; }
|
|
190
|
+
const resolvedRepoId = clean(repoId) || providerRepoId(git);
|
|
191
|
+
if (!resolvedRepoId) throw new Error('repo_id_required');
|
|
192
|
+
const snapshotId = snapshotIdFor({ snapshot, repoId: resolvedRepoId, captureKind, deploymentId });
|
|
193
|
+
const prefix = normalizeMerkleStoragePrefix(storagePrefix);
|
|
194
|
+
const storageKey = merkleSnapshotStorageKey({ ownerUserId: owner, repoId: resolvedRepoId, snapshotId, prefix });
|
|
195
|
+
const createdAt = Math.floor(Date.now() / 1000);
|
|
196
|
+
const classifier = snapshot.semantic?.classifier || null;
|
|
197
|
+
const row = {
|
|
198
|
+
snapshot_id: snapshotId,
|
|
199
|
+
owner_user_id: owner,
|
|
200
|
+
repo_id: resolvedRepoId,
|
|
201
|
+
repository: git?.remoteUrl || null,
|
|
202
|
+
source,
|
|
203
|
+
manifest_format: snapshot.format || 'agentsam-merkle',
|
|
204
|
+
manifest_version: snapshot.version || 1,
|
|
205
|
+
hash_algorithm: snapshot.algorithm || 'sha256',
|
|
206
|
+
root_hash: snapshot.rootHash,
|
|
207
|
+
policy_hash: snapshot.policyHash || null,
|
|
208
|
+
resolved_commit_sha: git?.revisionSha || null,
|
|
209
|
+
resolved_tree_sha: null,
|
|
210
|
+
git_branch: git?.branch || null,
|
|
211
|
+
working_tree_dirty: git?.dirty ? 1 : 0,
|
|
212
|
+
connection_id: clean(connectionId) || null,
|
|
213
|
+
runtime_lease_id: clean(runtimeLeaseId) || null,
|
|
214
|
+
storage_backend: 'r2',
|
|
215
|
+
storage_bucket: wrangler.storage_bucket,
|
|
216
|
+
storage_key: storageKey,
|
|
217
|
+
entry_count: snapshot.entries.length,
|
|
218
|
+
file_count: snapshot.stats?.files ?? null,
|
|
219
|
+
directory_count: snapshot.stats?.directories ?? null,
|
|
220
|
+
symlink_count: snapshot.stats?.symlinks ?? 0,
|
|
221
|
+
total_bytes: snapshot.stats?.bytes ?? null,
|
|
222
|
+
capture_kind: captureKind,
|
|
223
|
+
deployment_id: clean(deploymentId) || null,
|
|
224
|
+
worker_version_id: clean(workerVersionId) || null,
|
|
225
|
+
reference_label: clean(referenceLabel) || `${captureKind}:${snapshotId}`,
|
|
226
|
+
created_at: createdAt,
|
|
227
|
+
persisted_at: createdAt,
|
|
228
|
+
metadata_root: snapshot.semantic?.rootHash || null,
|
|
229
|
+
classifier_format: classifier?.format || null,
|
|
230
|
+
classifier_version: classifier?.version ?? null,
|
|
231
|
+
classifier_source: classifier?.source || null,
|
|
232
|
+
};
|
|
233
|
+
return { snapshot_id: snapshotId, storage_key: storageKey, row, wrangler, prefix };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function merklePersistenceUpsertSql(row) {
|
|
237
|
+
return `INSERT INTO ${MERKLE_SNAPSHOT_TABLE} (
|
|
238
|
+
snapshot_id, owner_user_id, repo_id, repository, source,
|
|
239
|
+
manifest_format, manifest_version, hash_algorithm, root_hash, policy_hash,
|
|
240
|
+
resolved_commit_sha, resolved_tree_sha, git_branch, working_tree_dirty,
|
|
241
|
+
connection_id, runtime_lease_id, storage_backend, storage_bucket, storage_key,
|
|
242
|
+
entry_count, file_count, directory_count, symlink_count, total_bytes,
|
|
243
|
+
capture_kind, deployment_id, worker_version_id, reference_label,
|
|
244
|
+
created_at, persisted_at, metadata_root, classifier_format, classifier_version, classifier_source
|
|
245
|
+
) VALUES (
|
|
246
|
+
${sqlText(row.snapshot_id)}, ${sqlText(row.owner_user_id)}, ${sqlText(row.repo_id)}, ${sqlText(row.repository)}, ${sqlText(row.source)},
|
|
247
|
+
${sqlText(row.manifest_format)}, ${sqlInt(row.manifest_version)}, ${sqlText(row.hash_algorithm)}, ${sqlText(row.root_hash)}, ${sqlText(row.policy_hash)},
|
|
248
|
+
${sqlText(row.resolved_commit_sha)}, ${sqlText(row.resolved_tree_sha)}, ${sqlText(row.git_branch)}, ${sqlInt(row.working_tree_dirty)},
|
|
249
|
+
${sqlText(row.connection_id)}, ${sqlText(row.runtime_lease_id)}, ${sqlText(row.storage_backend)}, ${sqlText(row.storage_bucket)}, ${sqlText(row.storage_key)},
|
|
250
|
+
${sqlInt(row.entry_count)}, ${sqlInt(row.file_count)}, ${sqlInt(row.directory_count)}, ${sqlInt(row.symlink_count)}, ${sqlInt(row.total_bytes)},
|
|
251
|
+
${sqlText(row.capture_kind)}, ${sqlText(row.deployment_id)}, ${sqlText(row.worker_version_id)}, ${sqlText(row.reference_label)},
|
|
252
|
+
${sqlInt(row.created_at)}, ${sqlInt(row.persisted_at)}, ${sqlText(row.metadata_root)}, ${sqlText(row.classifier_format)}, ${sqlInt(row.classifier_version)}, ${sqlText(row.classifier_source)}
|
|
253
|
+
)
|
|
254
|
+
ON CONFLICT(snapshot_id) DO UPDATE SET
|
|
255
|
+
storage_backend=excluded.storage_backend,
|
|
256
|
+
storage_bucket=excluded.storage_bucket,
|
|
257
|
+
storage_key=excluded.storage_key,
|
|
258
|
+
persisted_at=excluded.persisted_at,
|
|
259
|
+
deployment_id=excluded.deployment_id,
|
|
260
|
+
worker_version_id=excluded.worker_version_id,
|
|
261
|
+
reference_label=excluded.reference_label,
|
|
262
|
+
metadata_root=excluded.metadata_root,
|
|
263
|
+
classifier_format=excluded.classifier_format,
|
|
264
|
+
classifier_version=excluded.classifier_version,
|
|
265
|
+
classifier_source=excluded.classifier_source;`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function wranglerArgs(base, wrangler) {
|
|
269
|
+
const args = [...base, '--config', wrangler.config_path];
|
|
270
|
+
if (wrangler.environment) args.push('--env', wrangler.environment);
|
|
271
|
+
return args;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function persistMerkleSnapshotCloudflare({
|
|
275
|
+
snapshotPath,
|
|
276
|
+
snapshot,
|
|
277
|
+
plan,
|
|
278
|
+
wranglerBin = process.env.WRANGLER_BIN || 'wrangler',
|
|
279
|
+
r2Only = false,
|
|
280
|
+
dryRun = false,
|
|
281
|
+
} = {}) {
|
|
282
|
+
const filename = path.resolve(snapshotPath);
|
|
283
|
+
if (!fs.existsSync(filename)) throw new Error('snapshot_file_not_found');
|
|
284
|
+
const result = {
|
|
285
|
+
provider: 'cloudflare',
|
|
286
|
+
binding: plan.wrangler.r2_binding,
|
|
287
|
+
storage_bucket: plan.row.storage_bucket,
|
|
288
|
+
storage_key: plan.row.storage_key,
|
|
289
|
+
database_binding: plan.wrangler.d1_binding,
|
|
290
|
+
database_name: plan.wrangler.database_name,
|
|
291
|
+
table: r2Only ? null : MERKLE_SNAPSHOT_TABLE,
|
|
292
|
+
snapshot_id: plan.row.snapshot_id,
|
|
293
|
+
row: plan.row,
|
|
294
|
+
dry_run: Boolean(dryRun),
|
|
295
|
+
};
|
|
296
|
+
if (dryRun) return result;
|
|
297
|
+
|
|
298
|
+
const object = `${plan.row.storage_bucket}/${plan.row.storage_key}`;
|
|
299
|
+
execFileSync(wranglerBin, wranglerArgs(['r2', 'object', 'put', object, '--file', filename, '--remote'], plan.wrangler), {
|
|
300
|
+
stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8',
|
|
301
|
+
});
|
|
302
|
+
if (r2Only) return result;
|
|
303
|
+
|
|
304
|
+
const tmp = path.join(os.tmpdir(), `agentsam-merkle-persist-${process.pid}-${Date.now()}.sql`);
|
|
305
|
+
fs.writeFileSync(tmp, `${merklePersistenceUpsertSql(plan.row)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
306
|
+
try {
|
|
307
|
+
execFileSync(wranglerBin, wranglerArgs(['d1', 'execute', plan.wrangler.database_name, '--remote', '--yes', `--file=${tmp}`], plan.wrangler), {
|
|
308
|
+
stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8',
|
|
309
|
+
});
|
|
310
|
+
} catch (error) {
|
|
311
|
+
try {
|
|
312
|
+
execFileSync(wranglerBin, wranglerArgs(['r2', 'object', 'delete', object, '--remote'], plan.wrangler), {
|
|
313
|
+
stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8',
|
|
314
|
+
});
|
|
315
|
+
} catch {}
|
|
316
|
+
throw error;
|
|
317
|
+
} finally {
|
|
318
|
+
fs.rmSync(tmp, { force: true });
|
|
319
|
+
}
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { comparePaths } from './hash.js';
|
|
3
|
+
|
|
4
|
+
export const FILEMETA_FORMAT = 'agentsam-filemeta';
|
|
5
|
+
export const FILEMETA_VERSION = 1;
|
|
6
|
+
|
|
7
|
+
function canonical(value) {
|
|
8
|
+
if (Array.isArray(value)) return value.map(canonical);
|
|
9
|
+
if (value && typeof value === 'object') return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])]));
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function semanticDigest(type, value) {
|
|
14
|
+
return 'sha256:' + createHash('sha256')
|
|
15
|
+
.update(`agentsam-filemeta:${type}:v1\0`)
|
|
16
|
+
.update(JSON.stringify(canonical(value)))
|
|
17
|
+
.digest('hex');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function metadataRoot(entries, classifier) {
|
|
21
|
+
return semanticDigest('root', { classifier, entries: [...entries].sort((a, b) => comparePaths(a.path, b.path)) });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function validateSemanticMetadata(value, contentEntries = []) {
|
|
25
|
+
const fail = (message) => { throw new Error(`Invalid semantic metadata: ${message}`); };
|
|
26
|
+
if (!value || value.format !== FILEMETA_FORMAT || value.version !== FILEMETA_VERSION || value.algorithm !== 'sha256') fail('unsupported format/version');
|
|
27
|
+
if (!value.classifier || !Array.isArray(value.entries)) fail('missing classifier/entries');
|
|
28
|
+
const content = new Map(contentEntries.filter((entry) => entry.type !== 'directory').map((entry) => [entry.path, entry]));
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
for (const entry of value.entries) {
|
|
31
|
+
if (!entry || typeof entry.path !== 'string' || seen.has(entry.path)) fail('invalid/duplicate path');
|
|
32
|
+
seen.add(entry.path);
|
|
33
|
+
if (!Number.isInteger(entry.mode) || entry.mode < 0 || entry.mode > 0o7777) fail(`invalid mode: ${entry.path}`);
|
|
34
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(entry.hash || '')) fail(`invalid hash: ${entry.path}`);
|
|
35
|
+
const source = content.get(entry.path);
|
|
36
|
+
if (source && source.hash !== entry.hash) fail(`content hash mismatch: ${entry.path}`);
|
|
37
|
+
if (source?.type === 'file' && source.size !== entry.size) fail(`file size mismatch: ${entry.path}`);
|
|
38
|
+
}
|
|
39
|
+
if (content.size && seen.size !== content.size) fail('entry count does not match content tree');
|
|
40
|
+
const rootHash = metadataRoot(value.entries, value.classifier);
|
|
41
|
+
if (rootHash !== value.rootHash) fail('metadata root mismatch');
|
|
42
|
+
return { ...value, rootHash, entries: [...value.entries].sort((a, b) => comparePaths(a.path, b.path)) };
|
|
43
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
|
|
4
|
+
const execute = promisify(execFile);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Resolve Git-ignored paths using Git itself so every Merkle-backed consumer can
|
|
8
|
+
* build evidence from the same checkout policy. Non-Git directories simply
|
|
9
|
+
* return an empty list and still receive the protocol's built-in excludes.
|
|
10
|
+
*/
|
|
11
|
+
export async function gitIgnoredPaths(root) {
|
|
12
|
+
try {
|
|
13
|
+
const result = await execute('git', ['status', '--porcelain=v1', '--ignored=matching'], {
|
|
14
|
+
cwd: root,
|
|
15
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
16
|
+
});
|
|
17
|
+
return [...new Set(result.stdout.split('\n')
|
|
18
|
+
.filter((line) => line.startsWith('!! '))
|
|
19
|
+
.map((line) => line.slice(3).trim().replace(/\/$/, ''))
|
|
20
|
+
.filter((value) => value && !value.includes('\\') && !value.split('/').includes('..')))];
|
|
21
|
+
} catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/lib/merkle/hash.js
CHANGED
|
@@ -4,6 +4,7 @@ export const comparePaths = (a, b) => Buffer.compare(Buffer.from(a), Buffer.from
|
|
|
4
4
|
export const digest = (type, value) => 'sha256:' + createHash('sha256')
|
|
5
5
|
.update(`agentsam-merkle:${type}:v1\0`).update(value).digest('hex');
|
|
6
6
|
export const linkHash = (target) => digest('symlink', target);
|
|
7
|
+
export const policyHash = (policy) => digest('policy', JSON.stringify(policy));
|
|
7
8
|
export const directoryHash = (children) => digest('directory', JSON.stringify(
|
|
8
9
|
[...children].sort((a, b) => comparePaths(a.name, b.name)).map(({ name, type, hash }) => [name, type, hash]),
|
|
9
10
|
));
|
package/src/lib/merkle/index.js
CHANGED
|
@@ -2,3 +2,25 @@ export { buildMerkleTree } from './tree.js';
|
|
|
2
2
|
export { saveSnapshot, readSnapshot, validateSnapshot } from './snapshot.js';
|
|
3
3
|
export { diffTrees } from './diff.js';
|
|
4
4
|
export { DEFAULT_IGNORES, normalizePolicy } from './policy.js';
|
|
5
|
+
export { policyHash } from './hash.js';
|
|
6
|
+
export async function buildSemanticMetadata(...args) {
|
|
7
|
+
const semantic = await import('./semantic.js');
|
|
8
|
+
return semantic.buildSemanticMetadata(...args);
|
|
9
|
+
}
|
|
10
|
+
export { validateSemanticMetadata, metadataRoot, FILEMETA_FORMAT, FILEMETA_VERSION } from './filemeta.js';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
MERKLE_SNAPSHOT_SCHEMA_SQL,
|
|
14
|
+
MERKLE_SNAPSHOT_STORAGE_PREFIX,
|
|
15
|
+
MERKLE_SNAPSHOT_TABLE,
|
|
16
|
+
MERKLE_WEBSITE_ASSETS_BINDING,
|
|
17
|
+
merkleSnapshotStorageKey,
|
|
18
|
+
normalizeMerkleStoragePrefix,
|
|
19
|
+
} from './persistence.js';
|
|
20
|
+
export {
|
|
21
|
+
buildMerklePersistencePlan,
|
|
22
|
+
merklePersistenceUpsertSql,
|
|
23
|
+
persistMerkleSnapshotCloudflare,
|
|
24
|
+
readWranglerPersistenceBindings,
|
|
25
|
+
resolveWranglerMerklePersistence,
|
|
26
|
+
} from './cloudflare-persistence.js';
|